Repository: mina-sshd Updated Branches: refs/heads/master b0c5ca22d -> 02f1d57be
[SSHD-710] Cannot connect standard OpenSSH client/server using ed25519 keys Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/02f1d57b Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/02f1d57b Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/02f1d57b Branch: refs/heads/master Commit: 02f1d57be9ca20d2a8ae6a3e741a94770f7c63fb Parents: b0c5ca2 Author: Markus Woschank <markus.wosch...@gmail.com> Authored: Thu Jun 1 16:31:54 2017 +0300 Committer: Lyor Goldstein <lyor.goldst...@gmail.com> Committed: Thu Jun 1 19:59:13 2017 +0300 ---------------------------------------------------------------------- .../eddsa/EdDSASecurityProviderUtils.java | 2 +- .../OpenSSHEd25519PrivateKeyEntryDecoder.java | 77 +++++++++++++++----- 2 files changed, 60 insertions(+), 19 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/02f1d57b/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderUtils.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderUtils.java index 24e7e5c..2ba5272 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderUtils.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/EdDSASecurityProviderUtils.java @@ -99,7 +99,7 @@ public final class EdDSASecurityProviderUtils { } EdDSAPrivateKey prvKey = (EdDSAPrivateKey) key; - EdDSAPublicKeySpec keySpec = new EdDSAPublicKeySpec(prvKey.getSeed(), prvKey.getParams()); + EdDSAPublicKeySpec keySpec = new EdDSAPublicKeySpec(prvKey.getAbyte(), prvKey.getParams()); KeyFactory factory = SecurityUtils.getKeyFactory(SecurityUtils.EDDSA); return EdDSAPublicKey.class.cast(factory.generatePublic(keySpec)); } http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/02f1d57b/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java b/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java index e83a328..4c511d5 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java @@ -27,7 +27,9 @@ import java.security.InvalidKeyException; import java.security.KeyFactory; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; +import java.util.Arrays; import java.util.Collections; +import java.util.Locale; import java.util.Objects; import net.i2p.crypto.eddsa.EdDSAPrivateKey; @@ -48,6 +50,9 @@ import org.apache.sshd.common.util.security.SecurityUtils; */ public class OpenSSHEd25519PrivateKeyEntryDecoder extends AbstractPrivateKeyEntryDecoder<EdDSAPublicKey, EdDSAPrivateKey> { public static final OpenSSHEd25519PrivateKeyEntryDecoder INSTANCE = new OpenSSHEd25519PrivateKeyEntryDecoder(); + private static final int PK_SIZE = 32; + private static final int SK_SIZE = 32; + private static final int KEYPAIR_SIZE = PK_SIZE + SK_SIZE; public OpenSSHEd25519PrivateKeyEntryDecoder() { super(EdDSAPublicKey.class, EdDSAPrivateKey.class, Collections.unmodifiableList(Collections.singletonList(KeyPairProvider.SSH_ED25519))); @@ -63,32 +68,68 @@ public class OpenSSHEd25519PrivateKeyEntryDecoder extends AbstractPrivateKeyEntr if (!SecurityUtils.isEDDSACurveSupported()) { throw new NoSuchAlgorithmException(SecurityUtils.EDDSA + " provider not supported"); } - - byte[] seed = KeyEntryResolver.readRLEBytes(keyData); // a.k.a pk in OpenSSH C code - byte[] signature = KeyEntryResolver.readRLEBytes(keyData); // a.k.a sk in OpenSSH C code - if (signature.length != seed.length * 2) { - throw new InvalidKeyException("Mismatched signature (" + signature.length + ") vs. seed (" + seed.length + ") length"); + + // ed25519 bernstein naming: pk .. public key, sk .. secret key + // we expect to find two byte arrays with the following structure (type:size): + // [pk:32], [sk:32,pk:32] + + byte[] pk = KeyEntryResolver.readRLEBytes(keyData); + byte[] keypair = KeyEntryResolver.readRLEBytes(keyData); + + if (pk.length != PK_SIZE) { + throw new InvalidKeyException(String.format(Locale.ENGLISH, "Unexpected pk size: %s (expected %s)", pk.length, PK_SIZE)); } - + + if (keypair.length != KEYPAIR_SIZE) { + throw new InvalidKeyException(String.format(Locale.ENGLISH, "Unexpected keypair size: %s (expected %s)", keypair.length, KEYPAIR_SIZE)); + } + + byte[] sk = Arrays.copyOf(keypair, SK_SIZE); + + // verify that the keypair contains the expected pk + // yes, it's stored redundant, this seems to mimic the output structure of the keypair generation interface + if (!Arrays.equals(pk, Arrays.copyOfRange(keypair, SK_SIZE, KEYPAIR_SIZE))) { + throw new InvalidKeyException("Keypair did not contain the public key."); + } + + // create the private key EdDSAParameterSpec params = EdDSANamedCurveTable.getByName(EdDSASecurityProviderUtils.CURVE_ED25519_SHA512); - EdDSAPrivateKeySpec keySpec = new EdDSAPrivateKeySpec(seed, params); - return generatePrivateKey(keySpec); + EdDSAPrivateKey privateKey = generatePrivateKey(new EdDSAPrivateKeySpec(sk, params)); + + // the private key class contains the calculated public key (Abyte) + // pointers to the corresponding code: + // EdDSAPrivateKeySpec.EdDSAPrivateKeySpec(byte[], EdDSAParameterSpec): A = spec.getB().scalarMultiply(a); + // EdDSAPrivateKey.EdDSAPrivateKey(EdDSAPrivateKeySpec): this.Abyte = this.A.toByteArray(); + + // we can now verify the generated pk matches the one we read + if (!Arrays.equals(privateKey.getAbyte(), pk)) { + throw new InvalidKeyException("The provided pk does NOT match the computed pk for the given sk."); + } + + return privateKey; } @Override public String encodePrivateKey(OutputStream s, EdDSAPrivateKey key) throws IOException { Objects.requireNonNull(key, "No private key provided"); - - // how - byte[] seed = key.getSeed(); - Objects.requireNonNull(seed, "No ssed"); - /* TODO see https://svn.nmap.org/ncrack/opensshlib/ed25519.c - byte[] signature = signEd25519KeyPair(seed); - KeyEntryResolver.writeRLEBytes(s, seed); - KeyEntryResolver.writeRLEBytes(s, signature); + + // ed25519 bernstein naming: pk .. public key, sk .. secret key + // we are expected to write the following arrays (type:size): + // [pk:32], [sk:32,pk:32] + + byte[] sk = key.getSeed(); + byte[] pk = key.getAbyte(); + + Objects.requireNonNull(sk, "No seed"); + + byte[] keypair = new byte[KEYPAIR_SIZE]; + System.arraycopy(sk, 0, keypair, 0, SK_SIZE); + System.arraycopy(pk, 0, keypair, SK_SIZE, PK_SIZE); + + KeyEntryResolver.writeRLEBytes(s, pk); + KeyEntryResolver.writeRLEBytes(s, keypair); + return KeyPairProvider.SSH_ED25519; - */ - return null; } @Override