This is an automated email from the ASF dual-hosted git repository. lgoldstein pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/mina-sshd.git
commit 5b38e8124e6c59beb8c11a6939f57b5ec9b819a4 Author: Thomas Wolf <thomas.w...@paranor.ch> AuthorDate: Fri May 22 07:51:24 2020 +0300 [SSHD-997] Fixed OpenSSH ed25519 private key decoder --- .../OpenSSHEd25519PrivateKeyEntryDecoder.java | 11 +- .../openssh/OpenSSHKeyPairResourceWriterTest.java | 135 ++++++++++++++++++--- 2 files changed, 123 insertions(+), 23 deletions(-) diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java index 5e72064..b4cd0f6 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/eddsa/OpenSSHEd25519PrivateKeyEntryDecoder.java @@ -101,15 +101,8 @@ public class OpenSSHEd25519PrivateKeyEntryDecoder extends AbstractPrivateKeyEntr } byte[] sk = Arrays.copyOf(keypair, SK_SIZE); - EdDSAPrivateKey privateKey; - try { - // create the private key - EdDSAParameterSpec params = EdDSANamedCurveTable.getByName(EdDSASecurityProviderUtils.CURVE_ED25519_SHA512); - privateKey = generatePrivateKey(new EdDSAPrivateKeySpec(sk, params)); - } finally { - // get rid of sensitive data a.s.a.p - Arrays.fill(sk, (byte) 0); - } + EdDSAParameterSpec params = EdDSANamedCurveTable.getByName(EdDSASecurityProviderUtils.CURVE_ED25519_SHA512); + EdDSAPrivateKey privateKey = generatePrivateKey(new EdDSAPrivateKeySpec(sk, params)); // the private key class contains the calculated public key (Abyte) // pointers to the corresponding code: diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/writer/openssh/OpenSSHKeyPairResourceWriterTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/writer/openssh/OpenSSHKeyPairResourceWriterTest.java index 050615a..f3c03e2 100644 --- a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/writer/openssh/OpenSSHKeyPairResourceWriterTest.java +++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/writer/openssh/OpenSSHKeyPairResourceWriterTest.java @@ -38,9 +38,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; -import java.util.Objects; -import net.i2p.crypto.eddsa.EdDSAKey; import net.i2p.crypto.eddsa.spec.EdDSAGenParameterSpec; import org.apache.sshd.common.config.keys.AuthorizedKeyEntry; import org.apache.sshd.common.config.keys.FilePasswordProvider; @@ -130,14 +128,6 @@ public class OpenSSHKeyPairResourceWriterTest extends JUnitTestSupport { } private boolean compare(KeyPair a, KeyPair b) { - if ("EDDSA".equals(data.algorithm)) { - // Bug in net.i2p.crypto.eddsa and in sshd? Both also compare the - // seed of the private key, but for a generated key, this is some - // random value, while it is all zeroes for a key read from a file. - return KeyUtils.compareKeys(a.getPublic(), b.getPublic()) - && Objects.equals(((EdDSAKey) a.getPrivate()).getParams(), - ((EdDSAKey) b.getPrivate()).getParams()); - } // Compares both public and private keys. return KeyUtils.compareKeyPairs(a, b); } @@ -156,6 +146,115 @@ public class OpenSSHKeyPairResourceWriterTest extends JUnitTestSupport { } @Test + public void testFileRoundtripNoEncryption() throws Exception { + Path tmp = getTemporaryOutputFile(); + try (SecureByteArrayOutputStream out = new SecureByteArrayOutputStream()) { + OpenSSHKeyPairResourceWriter.INSTANCE.writePrivateKey(testKey, "a comment", null, out); + writeToFile(tmp, out.toByteArray()); + } + try (InputStream in = Files.newInputStream(tmp)) { + KeyPair key = SecurityUtils.loadKeyPairIdentities(null, + new PathResource(tmp), in, null).iterator().next(); + assertNotNull("No key pair parsed", key); + assertKeyPairEquals("Mismatched recovered keys", testKey, key); + assertTrue("Keys should be equal", compare(key, testKey)); + Path tmp2 = getTemporaryOutputFile("again"); + try (SecureByteArrayOutputStream out = new SecureByteArrayOutputStream()) { + OpenSSHKeyPairResourceWriter.INSTANCE.writePrivateKey(key, "a comment", null, out); + writeToFile(tmp2, out.toByteArray()); + } + try (InputStream in2 = Files.newInputStream(tmp2)) { + KeyPair key2 = SecurityUtils.loadKeyPairIdentities(null, + new PathResource(tmp2), in2, null).iterator().next(); + assertNotNull("No key pair parsed", key2); + assertKeyPairEquals("Mismatched recovered keys", testKey, key2); + assertTrue("Keys should be equal", compare(key2, testKey)); + + assertKeyPairEquals("Mismatched recovered keys", key, key2); + assertTrue("Keys should be equal", compare(key2, key)); + } + } + } + + @Test + public void testFileRoundtripWithEncryption() throws Exception { + Path tmp = getTemporaryOutputFile(); + try (SecureByteArrayOutputStream out = new SecureByteArrayOutputStream()) { + OpenSSHKeyEncryptionContext options = new OpenSSHKeyEncryptionContext(); + options.setPassword("nonsense"); + options.setCipherName("AES"); + options.setCipherMode("CTR"); + options.setCipherType("256"); + OpenSSHKeyPairResourceWriter.INSTANCE.writePrivateKey(testKey, "a comment", options, out); + writeToFile(tmp, out.toByteArray()); + } + try (InputStream in = Files.newInputStream(tmp)) { + KeyPair key = SecurityUtils.loadKeyPairIdentities(null, + new PathResource(tmp), in, FilePasswordProvider.of("nonsense")).iterator().next(); + assertNotNull("No key pair parsed", key); + assertKeyPairEquals("Mismatched recovered keys", testKey, key); + assertTrue("Keys should be equal", compare(key, testKey)); + Path tmp2 = getTemporaryOutputFile("again"); + try (SecureByteArrayOutputStream out = new SecureByteArrayOutputStream()) { + OpenSSHKeyEncryptionContext options = new OpenSSHKeyEncryptionContext(); + options.setPassword("nonsense"); + options.setCipherName("AES"); + options.setCipherMode("CTR"); + options.setCipherType("256"); + OpenSSHKeyPairResourceWriter.INSTANCE.writePrivateKey(key, "a comment", options, out); + writeToFile(tmp2, out.toByteArray()); + } + try (InputStream in2 = Files.newInputStream(tmp2)) { + KeyPair key2 = SecurityUtils.loadKeyPairIdentities(null, + new PathResource(tmp2), in2, FilePasswordProvider.of("nonsense")).iterator().next(); + assertNotNull("No key pair parsed", key2); + assertKeyPairEquals("Mismatched recovered keys", testKey, key2); + assertTrue("Keys should be equal", compare(key2, testKey)); + + assertKeyPairEquals("Mismatched recovered keys", key, key2); + assertTrue("Keys should be equal", compare(key2, key)); + } + } + } + + @Test + public void testFileRoundtripAsymmetric() throws Exception { + // Write first unencrypted, then encrypted. read both and compare. + Path tmp = getTemporaryOutputFile(); + try (SecureByteArrayOutputStream out = new SecureByteArrayOutputStream()) { + OpenSSHKeyPairResourceWriter.INSTANCE.writePrivateKey(testKey, "a comment", null, out); + writeToFile(tmp, out.toByteArray()); + } + try (InputStream in = Files.newInputStream(tmp)) { + KeyPair key = SecurityUtils.loadKeyPairIdentities(null, + new PathResource(tmp), in, null).iterator().next(); + assertNotNull("No key pair parsed", key); + assertKeyPairEquals("Mismatched recovered keys", testKey, key); + assertTrue("Keys should be equal", compare(key, testKey)); + Path tmp2 = getTemporaryOutputFile("again"); + try (SecureByteArrayOutputStream out = new SecureByteArrayOutputStream()) { + OpenSSHKeyEncryptionContext options = new OpenSSHKeyEncryptionContext(); + options.setPassword("nonsense"); + options.setCipherName("AES"); + options.setCipherMode("CTR"); + options.setCipherType("256"); + OpenSSHKeyPairResourceWriter.INSTANCE.writePrivateKey(key, "a comment", options, out); + writeToFile(tmp2, out.toByteArray()); + } + try (InputStream in2 = Files.newInputStream(tmp2)) { + KeyPair key2 = SecurityUtils.loadKeyPairIdentities(null, + new PathResource(tmp2), in2, FilePasswordProvider.of("nonsense")).iterator().next(); + assertNotNull("No key pair parsed", key2); + assertKeyPairEquals("Mismatched recovered keys", testKey, key2); + assertTrue("Keys should be equal", compare(key2, testKey)); + + assertKeyPairEquals("Mismatched recovered keys", key, key2); + assertTrue("Keys should be equal", compare(key2, key)); + } + } + } + + @Test public void testWritePrivateKeyNoEncryption() throws Exception { Path tmp = getTemporaryOutputFile(); try (SecureByteArrayOutputStream out = new SecureByteArrayOutputStream()) { @@ -330,22 +429,30 @@ public class OpenSSHKeyPairResourceWriterTest extends JUnitTestSupport { checkPublicKey(tmp, null); } - private Path getTemporaryOutputFile() throws IOException { + private Path getTemporaryOutputFile(String suffix) throws IOException { Path dir = createTempClassFolder(); String testName = getCurrentTestName(); int pos = testName.indexOf('['); - Path file; + String fileName; if (pos > 0) { String baseName = testName.substring(0, pos); String paramName = testName.substring(pos + 1, testName.length() - 1); - file = dir.resolve(baseName + "-" + paramName.replace('(', '-').replace(")", "").trim()); + fileName = baseName + "-" + paramName.replace('(', '-').replace(")", "").trim(); } else { - file = dir.resolve(testName); + fileName = testName; } + if (suffix != null) { + fileName += suffix; + } + Path file = dir.resolve(fileName); Files.deleteIfExists(file); return file; } + private Path getTemporaryOutputFile() throws IOException { + return getTemporaryOutputFile(null); + } + @SuppressWarnings("checkstyle:VisibilityModifier") private static class TestData { public final String algorithm;