From 1538c32d65152d1792e6d2404f2018c9db29ab19 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Sat, 4 Apr 2015 17:17:58 -0700 Subject: Add Ed25519 host key support --- sshlib/build.gradle | 2 +- .../src/main/java/com/trilead/ssh2/KnownHosts.java | 25 +++- .../trilead/ssh2/auth/AuthenticationManager.java | 36 ++++++ .../com/trilead/ssh2/crypto/key/Ed25519Key.java | 45 +++++++ .../trilead/ssh2/crypto/key/Ed25519PrivateKey.java | 35 ++++++ .../trilead/ssh2/crypto/key/Ed25519PublicKey.java | 35 ++++++ .../trilead/ssh2/signature/ECDSASHA2Verify.java | 13 ++ .../com/trilead/ssh2/signature/Ed25519Verify.java | 140 +++++++++++++++++++++ .../com/trilead/ssh2/transport/KexManager.java | 12 ++ .../trilead/ssh2/signature/Ed25519VerifyTest.java | 70 +++++++++++ 10 files changed, 409 insertions(+), 4 deletions(-) create mode 100644 sshlib/src/main/java/com/trilead/ssh2/crypto/key/Ed25519Key.java create mode 100644 sshlib/src/main/java/com/trilead/ssh2/crypto/key/Ed25519PrivateKey.java create mode 100644 sshlib/src/main/java/com/trilead/ssh2/crypto/key/Ed25519PublicKey.java create mode 100644 sshlib/src/main/java/com/trilead/ssh2/signature/Ed25519Verify.java create mode 100644 sshlib/src/test/java/com/trilead/ssh2/signature/Ed25519VerifyTest.java diff --git a/sshlib/build.gradle b/sshlib/build.gradle index fb266ff..5949330 100644 --- a/sshlib/build.gradle +++ b/sshlib/build.gradle @@ -15,7 +15,7 @@ dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.jcraft:jzlib:1.1.3' compile 'org.connectbot:simplesocks:1.0.1' - compile 'org.zeromq:curve25519-java:0.1.0' + compile 'net.vrallev.ecc:ecc-25519-java:1.0.1' testCompile 'junit:junit:4.12' } diff --git a/sshlib/src/main/java/com/trilead/ssh2/KnownHosts.java b/sshlib/src/main/java/com/trilead/ssh2/KnownHosts.java index 4e75de1..1a5f0a8 100644 --- a/sshlib/src/main/java/com/trilead/ssh2/KnownHosts.java +++ b/sshlib/src/main/java/com/trilead/ssh2/KnownHosts.java @@ -28,8 +28,10 @@ import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import com.trilead.ssh2.crypto.Base64; +import com.trilead.ssh2.crypto.key.Ed25519PublicKey; import com.trilead.ssh2.signature.DSASHA1Verify; import com.trilead.ssh2.signature.ECDSASHA2Verify; +import com.trilead.ssh2.signature.Ed25519Verify; import com.trilead.ssh2.signature.RSASHA1Verify; @@ -121,12 +123,22 @@ public class KnownHosts { ECPublicKey epk = ECDSASHA2Verify.decodeSSHECDSAPublicKey(serverHostKey); - synchronized (publicKeys) { + synchronized (publicKeys) + { publicKeys.add(new KnownHostsEntry(hostnames, epk)); } } + else if (Ed25519Verify.ED25519_ID.equals(serverHostKeyAlgorithm)) + { + Ed25519PublicKey edpk = Ed25519Verify.decodeSSHEd25519PublicKey(serverHostKey); + + synchronized (publicKeys) + { + publicKeys.add(new KnownHostsEntry(hostnames, edpk)); + } + } else - throw new IOException("Unknwon host key type (" + serverHostKeyAlgorithm + ")"); + throw new IOException("Unknown host key type (" + serverHostKeyAlgorithm + ")"); } /** @@ -604,6 +616,10 @@ public class KnownHosts { remoteKey = ECDSASHA2Verify.decodeSSHECDSAPublicKey(serverHostKey); } + else if (Ed25519Verify.ED25519_ID.equals(serverHostKeyAlgorithm)) + { + remoteKey = Ed25519Verify.decodeSSHEd25519PublicKey(serverHostKey); + } else throw new IllegalArgumentException("Unknown hostkey type " + serverHostKeyAlgorithm); @@ -719,7 +735,10 @@ public class KnownHosts throw new IllegalArgumentException("Unknown hash type " + type); } - if (keyType.startsWith("ecdsa-sha2-")) + if (Ed25519Verify.ED25519_ID.equals(keyType)) + { + } + else if (keyType.startsWith("ecdsa-sha2-")) { } else if ("ssh-rsa".equals(keyType)) diff --git a/sshlib/src/main/java/com/trilead/ssh2/auth/AuthenticationManager.java b/sshlib/src/main/java/com/trilead/ssh2/auth/AuthenticationManager.java index e551495..dfafcbd 100644 --- a/sshlib/src/main/java/com/trilead/ssh2/auth/AuthenticationManager.java +++ b/sshlib/src/main/java/com/trilead/ssh2/auth/AuthenticationManager.java @@ -15,6 +15,8 @@ import java.util.Vector; import com.trilead.ssh2.InteractiveCallback; import com.trilead.ssh2.crypto.PEMDecoder; +import com.trilead.ssh2.crypto.key.Ed25519PrivateKey; +import com.trilead.ssh2.crypto.key.Ed25519PublicKey; import com.trilead.ssh2.packets.PacketServiceAccept; import com.trilead.ssh2.packets.PacketServiceRequest; import com.trilead.ssh2.packets.PacketUserauthBanner; @@ -29,6 +31,7 @@ import com.trilead.ssh2.packets.Packets; import com.trilead.ssh2.packets.TypesWriter; import com.trilead.ssh2.signature.DSASHA1Verify; import com.trilead.ssh2.signature.ECDSASHA2Verify; +import com.trilead.ssh2.signature.Ed25519Verify; import com.trilead.ssh2.signature.RSASHA1Verify; import com.trilead.ssh2.transport.MessageHandler; import com.trilead.ssh2.transport.TransportManager; @@ -276,6 +279,39 @@ public class AuthenticationManager implements MessageHandler tm.sendMessage(ua.getPayload()); } + else if (key instanceof Ed25519PrivateKey) + { + Ed25519PrivateKey pk = (Ed25519PrivateKey) key; + + final String algo = Ed25519Verify.ED25519_ID; + + byte[] pk_enc = Ed25519Verify.encodeSSHEd25519PublicKey((Ed25519PublicKey) pair.getPublic()); + + TypesWriter tw = new TypesWriter(); + { + byte[] H = tm.getSessionIdentifier(); + + tw.writeString(H, 0, H.length); + tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST); + tw.writeString(user); + tw.writeString("ssh-connection"); + tw.writeString("publickey"); + tw.writeBoolean(true); + tw.writeString(algo); + tw.writeString(pk_enc, 0, pk_enc.length); + } + + byte[] msg = tw.getBytes(); + + byte[] ds = Ed25519Verify.generateSignature(msg, pk); + + byte[] ed_sig_enc = Ed25519Verify.encodeSSHEd25519Signature(ds); + + PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user, + algo, pk_enc, ed_sig_enc); + + tm.sendMessage(ua.getPayload()); + } else { throw new IOException("Unknown private key type returned by the PEM decoder."); diff --git a/sshlib/src/main/java/com/trilead/ssh2/crypto/key/Ed25519Key.java b/sshlib/src/main/java/com/trilead/ssh2/crypto/key/Ed25519Key.java new file mode 100644 index 0000000..5c9e549 --- /dev/null +++ b/sshlib/src/main/java/com/trilead/ssh2/crypto/key/Ed25519Key.java @@ -0,0 +1,45 @@ +/* + * ConnectBot: simple, powerful, open-source SSH client for Android + * Copyright 2015 Kenny Root + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.trilead.ssh2.crypto.key; + +import java.security.Key; + +/** + * Java representation of a native Ed25519 key. + * + * @author Kenny Root + */ +public class Ed25519Key implements Key { + private final byte[] keyBytes; + + protected Ed25519Key(byte[] keyBytes) { + this.keyBytes = keyBytes; + } + + public String getAlgorithm() { + return "Ed25519"; + } + + public String getFormat() { + return "RAW"; + } + + public byte[] getEncoded() { + return keyBytes; + } +} diff --git a/sshlib/src/main/java/com/trilead/ssh2/crypto/key/Ed25519PrivateKey.java b/sshlib/src/main/java/com/trilead/ssh2/crypto/key/Ed25519PrivateKey.java new file mode 100644 index 0000000..05ab4ee --- /dev/null +++ b/sshlib/src/main/java/com/trilead/ssh2/crypto/key/Ed25519PrivateKey.java @@ -0,0 +1,35 @@ +/* + * ConnectBot: simple, powerful, open-source SSH client for Android + * Copyright 2015 Kenny Root + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.trilead.ssh2.crypto.key; + +import java.security.PrivateKey; + +/** + * Java representation of a native Ed25519 key. + * + * @author Kenny Root + */ +public class Ed25519PrivateKey extends Ed25519Key implements PrivateKey { + private Ed25519PrivateKey(byte[] keyBytes) { + super(keyBytes); + } + + public static Ed25519PrivateKey getInstance(byte[] keyBytes) { + return new Ed25519PrivateKey(keyBytes); + } +} diff --git a/sshlib/src/main/java/com/trilead/ssh2/crypto/key/Ed25519PublicKey.java b/sshlib/src/main/java/com/trilead/ssh2/crypto/key/Ed25519PublicKey.java new file mode 100644 index 0000000..a4e2092 --- /dev/null +++ b/sshlib/src/main/java/com/trilead/ssh2/crypto/key/Ed25519PublicKey.java @@ -0,0 +1,35 @@ +/* + * ConnectBot: simple, powerful, open-source SSH client for Android + * Copyright 2015 Kenny Root, Jeffrey Sharkey + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.trilead.ssh2.crypto.key; + +import java.security.PublicKey; + +/** + * Java representation of a native Ed25519 key. + * + * @author Kenny Root + */ +public class Ed25519PublicKey extends Ed25519Key implements PublicKey { + private Ed25519PublicKey(byte[] keyBytes) { + super(keyBytes); + } + + public static Ed25519PublicKey getInstance(byte[] keyBytes) { + return new Ed25519PublicKey(keyBytes); + } +} diff --git a/sshlib/src/main/java/com/trilead/ssh2/signature/ECDSASHA2Verify.java b/sshlib/src/main/java/com/trilead/ssh2/signature/ECDSASHA2Verify.java index 89cacfb..638a7d3 100644 --- a/sshlib/src/main/java/com/trilead/ssh2/signature/ECDSASHA2Verify.java +++ b/sshlib/src/main/java/com/trilead/ssh2/signature/ECDSASHA2Verify.java @@ -1,5 +1,18 @@ /** + * ConnectBot: simple, powerful, open-source SSH client for Android + * Copyright 2014 Kenny Root * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package com.trilead.ssh2.signature; diff --git a/sshlib/src/main/java/com/trilead/ssh2/signature/Ed25519Verify.java b/sshlib/src/main/java/com/trilead/ssh2/signature/Ed25519Verify.java new file mode 100644 index 0000000..e6dd036 --- /dev/null +++ b/sshlib/src/main/java/com/trilead/ssh2/signature/Ed25519Verify.java @@ -0,0 +1,140 @@ +/* + * ConnectBot: simple, powerful, open-source SSH client for Android + * Copyright 2015 Kenny Root + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.trilead.ssh2.signature; + +import com.trilead.ssh2.crypto.key.Ed25519PrivateKey; +import com.trilead.ssh2.crypto.key.Ed25519PublicKey; +import com.trilead.ssh2.log.Logger; +import com.trilead.ssh2.packets.TypesReader; +import com.trilead.ssh2.packets.TypesWriter; +import net.i2p.crypto.eddsa.EdDSAEngine; +import net.i2p.crypto.eddsa.EdDSAPrivateKey; +import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec; +import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable; +import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec; +import net.vrallev.java.ecc.Ecc25519Helper; + +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SignatureException; + +/** + * @author Kenny Root + */ +public class Ed25519Verify { + private static final Logger log = Logger.getLogger(Ed25519Verify.class); + + /** Identifies this as an Ed25519 key in the protocol. */ + public static final String ED25519_ID = "ssh-ed25519"; + + private static final int ED25519_PK_SIZE_BYTES = 32; + private static final int ED25519_SIG_SIZE_BYTES = 64; + + public static byte[] encodeSSHEd25519PublicKey(Ed25519PublicKey key) { + TypesWriter tw = new TypesWriter(); + + tw.writeString(ED25519_ID); + tw.writeBytes(key.getEncoded()); + + return tw.getBytes(); + } + + public static Ed25519PublicKey decodeSSHEd25519PublicKey(byte[] key) throws IOException { + TypesReader tr = new TypesReader(key); + + String key_format = tr.readString(); + if (key_format.equals(ED25519_ID) == false) { + throw new IOException("This is not an Ed25519 key"); + } + + byte[] keyBytes = tr.readByteString(); + + if (tr.remain() != 0) { + throw new IOException("Padding in Ed25519 public key! " + tr.remain() + " bytes left."); + } + + if (keyBytes.length != ED25519_PK_SIZE_BYTES) { + throw new IOException("Ed25519 was not of correct length: " + keyBytes.length + " vs " + ED25519_PK_SIZE_BYTES); + } + + return Ed25519PublicKey.getInstance(keyBytes); + } + + public static byte[] generateSignature(byte[] msg, Ed25519PrivateKey privateKey) throws IOException { + byte[] privateKeyBytes = privateKey.getEncoded(); + + EdDSANamedCurveSpec spec = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.CURVE_ED25519_SHA512); + EdDSAPrivateKeySpec privKeySpec = new EdDSAPrivateKeySpec(privateKeyBytes, spec); + + try { + EdDSAEngine engine = new EdDSAEngine(MessageDigest.getInstance("SHA-512")); + engine.initSign(new EdDSAPrivateKey(privKeySpec)); + engine.update(msg); + return engine.sign(); + } catch (NoSuchAlgorithmException e) { + throw new IOException(e); + } catch (SignatureException e) { + throw new IOException(e); + } catch (InvalidKeyException e) { + throw new IOException(e); + } + } + + public static boolean verifySignature(byte[] msg, byte[] sig, Ed25519PublicKey publicKey) throws IOException { + byte[] publicKeyBytes = publicKey.getEncoded(); + if (publicKeyBytes.length != ED25519_PK_SIZE_BYTES) { + throw new IOException("Invalid Ed25519 key length " + publicKeyBytes.length); + } + Ecc25519Helper helper = new Ecc25519Helper(); + return helper.isValidSignature(msg, sig, publicKeyBytes); + } + + public static byte[] encodeSSHEd25519Signature(byte[] sig) { + TypesWriter tw = new TypesWriter(); + + tw.writeString(ED25519_ID); + tw.writeBytes(sig); + + return tw.getBytes(); + } + + public static byte[] decodeSSHEd25519Signature(byte[] sig) throws IOException { + byte[] rsArray; + + TypesReader tr = new TypesReader(sig); + + String sig_format = tr.readString(); + if (sig_format.equals(ED25519_ID) == false) { + throw new IOException("Peer sent wrong signature format"); + } + + rsArray = tr.readByteString(); + + if (tr.remain() != 0) { + throw new IOException("Padding in Ed25519 signature!"); + } + + if (rsArray.length > ED25519_SIG_SIZE_BYTES) { + throw new IOException("Ed25519 signature was " + rsArray.length + " bytes (" + ED25519_PK_SIZE_BYTES + " expected)"); + } + + return rsArray; + } +} diff --git a/sshlib/src/main/java/com/trilead/ssh2/transport/KexManager.java b/sshlib/src/main/java/com/trilead/ssh2/transport/KexManager.java index 741268b..ab6d0b6 100644 --- a/sshlib/src/main/java/com/trilead/ssh2/transport/KexManager.java +++ b/sshlib/src/main/java/com/trilead/ssh2/transport/KexManager.java @@ -25,6 +25,7 @@ import com.trilead.ssh2.crypto.dh.Curve25519Exchange; import com.trilead.ssh2.crypto.dh.DhGroupExchange; import com.trilead.ssh2.crypto.dh.GenericDhExchange; import com.trilead.ssh2.crypto.digest.MAC; +import com.trilead.ssh2.crypto.key.Ed25519PublicKey; import com.trilead.ssh2.log.Logger; import com.trilead.ssh2.packets.PacketKexDHInit; import com.trilead.ssh2.packets.PacketKexDHReply; @@ -38,6 +39,7 @@ import com.trilead.ssh2.packets.PacketNewKeys; import com.trilead.ssh2.packets.Packets; import com.trilead.ssh2.signature.DSASHA1Verify; import com.trilead.ssh2.signature.ECDSASHA2Verify; +import com.trilead.ssh2.signature.Ed25519Verify; import com.trilead.ssh2.signature.RSASHA1Verify; @@ -65,6 +67,7 @@ public class KexManager private static final Set HOSTKEY_ALGS = new LinkedHashSet(); static { + HOSTKEY_ALGS.add(Ed25519Verify.ED25519_ID); if (supportsEc) { HOSTKEY_ALGS.add("ecdsa-sha2-nistp256"); HOSTKEY_ALGS.add("ecdsa-sha2-nistp384"); @@ -380,6 +383,15 @@ public class KexManager private boolean verifySignature(byte[] sig, byte[] hostkey) throws IOException { + if (kxs.np.server_host_key_algo.equals(Ed25519Verify.ED25519_ID)) { + byte[] eds = Ed25519Verify.decodeSSHEd25519Signature(sig); + Ed25519PublicKey edpk = Ed25519Verify.decodeSSHEd25519PublicKey(hostkey); + + log.log(50, "Verifying ed25519 signature"); + + return Ed25519Verify.verifySignature(kxs.H, eds, edpk); + + } if (kxs.np.server_host_key_algo.startsWith("ecdsa-sha2-")) { byte[] rs = ECDSASHA2Verify.decodeSSHECDSASignature(sig); diff --git a/sshlib/src/test/java/com/trilead/ssh2/signature/Ed25519VerifyTest.java b/sshlib/src/test/java/com/trilead/ssh2/signature/Ed25519VerifyTest.java new file mode 100644 index 0000000..f256f91 --- /dev/null +++ b/sshlib/src/test/java/com/trilead/ssh2/signature/Ed25519VerifyTest.java @@ -0,0 +1,70 @@ +package com.trilead.ssh2.signature; + +import com.trilead.ssh2.crypto.key.Ed25519PrivateKey; +import com.trilead.ssh2.crypto.key.Ed25519PublicKey; +import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec; +import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable; +import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec; +import org.junit.Test; + +import javax.xml.bind.DatatypeConverter; +import java.security.MessageDigest; + +import static org.junit.Assert.*; + +/** + * Created by kenny on 1/24/16. + */ +public class Ed25519VerifyTest { + /* Test vectors from draft-josefsson-eddsa-ed25519-03 */ + private static final byte[] SECRET_KEY = toByteArray("833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42"); + private static final byte[] PUBLIC_KEY = toByteArray("ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf"); + private static final byte[] MESSAGE = toByteArray("616263"); + private static final byte[] SIGNATURE = toByteArray("dc2a4459e7369633a52b1bf277839a00201009a3efbf3ecb69bea2186c26b58909351fc9ac90b3ecfdfbc7c66431e0303dca179c138ac17ad9bef1177331a704"); + + private static byte[] toByteArray(String s) { + return DatatypeConverter.parseHexBinary(s); + } + + @Test + public void verifies() throws Exception { + Ed25519PublicKey pubKey = Ed25519PublicKey.getInstance(PUBLIC_KEY); + MessageDigest md = MessageDigest.getInstance("SHA-512"); + assertTrue(Ed25519Verify.verifySignature(md.digest(MESSAGE), SIGNATURE, pubKey)); + } + + @Test + public void noVerificationForInvalidData() throws Exception { + Ed25519PublicKey pubKey = Ed25519PublicKey.getInstance(PUBLIC_KEY); + MessageDigest md = MessageDigest.getInstance("SHA-512"); + assertFalse(Ed25519Verify.verifySignature(md.digest(new byte[1]), SIGNATURE, pubKey)); + } + + @Test + public void signs() throws Exception { + Ed25519PrivateKey privKey = Ed25519PrivateKey.getInstance(SECRET_KEY); + MessageDigest md = MessageDigest.getInstance("SHA-512"); + assertArrayEquals(SIGNATURE, Ed25519Verify.generateSignature(md.digest(MESSAGE), privKey)); + } + + @Test + public void publicKeyCalculatedCorrectly() throws Exception { + EdDSANamedCurveSpec spec = EdDSANamedCurveTable.getByName("ed25519-sha-512"); + EdDSAPrivateKeySpec privKeySpec = new EdDSAPrivateKeySpec(SECRET_KEY, spec); + byte[] pubKeyBytes = privKeySpec.getA().toByteArray(); + assertArrayEquals(PUBLIC_KEY, pubKeyBytes); + } + + @Test + public void loopbackSuccess() throws Exception { + Ed25519PrivateKey privKey = Ed25519PrivateKey.getInstance(SECRET_KEY); + Ed25519PublicKey pubKey = Ed25519PublicKey.getInstance(PUBLIC_KEY); + MessageDigest md = MessageDigest.getInstance("SHA-512"); + + byte[] message = new byte[] { (byte) 0xA5, (byte) 0x5A }; + byte[] digest = md.digest(message); + + byte[] sig = Ed25519Verify.generateSignature(digest, privKey); + assertTrue(Ed25519Verify.verifySignature(digest, sig, pubKey)); + } +} \ No newline at end of file -- cgit v1.2.3