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
The following commit(s) were added to refs/heads/master by this push: new 742963a [SSHD-987] Correctly generate IV for AES private key obfuscator 742963a is described below commit 742963a5cdff42361c7b372e3fd7ad11d7046f67 Author: Lyor Goldstein <lgoldst...@apache.org> AuthorDate: Wed Apr 29 19:09:37 2020 +0300 [SSHD-987] Correctly generate IV for AES private key obfuscator --- .../keys/loader/AESPrivateKeyObfuscator.java | 29 +++++++++++++++++ .../keys/loader/AbstractPrivateKeyObfuscator.java | 19 ++++------- .../keys/loader/DESPrivateKeyObfuscator.java | 4 +-- .../keys/loader/AESPrivateKeyObfuscatorTest.java | 38 ++++++++++++++++++---- 4 files changed, 69 insertions(+), 21 deletions(-) diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscator.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscator.java index d45391d..8ba47ce 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscator.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscator.java @@ -21,14 +21,18 @@ package org.apache.sshd.common.config.keys.loader; import java.io.IOException; import java.security.GeneralSecurityException; import java.security.Key; +import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.function.Predicate; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; +import org.apache.sshd.common.cipher.BuiltinCiphers; +import org.apache.sshd.common.cipher.CipherInformation; import org.apache.sshd.common.util.security.SecurityUtils; /** @@ -57,6 +61,24 @@ public class AESPrivateKeyObfuscator extends AbstractPrivateKeyObfuscator { } @Override + protected int resolveInitializationVectorLength(PrivateKeyEncryptionContext encContext) throws GeneralSecurityException { + int keyLength = resolveKeyLength(encContext); + CipherInformation ci = resolveCipherInformation(keyLength, encContext.getCipherMode()); + if (ci == null) { + throw new NoSuchAlgorithmException("No match found for " + encContext); + } + return ci.getIVSize(); + } + + protected CipherInformation resolveCipherInformation(int keyLength, String cipherMode) { + Predicate<CipherInformation> selector = createCipherSelector(keyLength, cipherMode); + return BuiltinCiphers.VALUES.stream() + .filter(selector) + .findFirst() + .orElse(null); + } + + @Override protected int resolveKeyLength(PrivateKeyEncryptionContext encContext) throws GeneralSecurityException { String cipherType = encContext.getCipherType(); try { @@ -87,6 +109,13 @@ public class AESPrivateKeyObfuscator extends AbstractPrivateKeyObfuscator { return LazyKeyLengthsHolder.KEY_LENGTHS; } + public static Predicate<CipherInformation> createCipherSelector(int keyLength, String cipherMode) { + String xformMode = "/" + cipherMode.toUpperCase() + "/"; + return c -> CIPHER_NAME.equalsIgnoreCase(c.getAlgorithm()) + && (keyLength == c.getKeySize()) + && c.getTransformation().contains(xformMode); + } + private static final class LazyKeyLengthsHolder { private static final List<Integer> KEY_LENGTHS = Collections.unmodifiableList(detectSupportedKeySizes()); diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractPrivateKeyObfuscator.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractPrivateKeyObfuscator.java index cc6d300..57ff3e3 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractPrivateKeyObfuscator.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/AbstractPrivateKeyObfuscator.java @@ -57,7 +57,11 @@ public abstract class AbstractPrivateKeyObfuscator implements PrivateKeyObfuscat @Override public byte[] generateInitializationVector(PrivateKeyEncryptionContext encContext) throws GeneralSecurityException { - return generateInitializationVector(resolveKeyLength(encContext)); + int ivSize = resolveInitializationVectorLength(encContext); + byte[] initVector = new byte[ivSize]; + Random randomizer = new SecureRandom(); // TODO consider using some pre-created singleton instance + randomizer.nextBytes(initVector); + return initVector; } @Override @@ -80,17 +84,8 @@ public abstract class AbstractPrivateKeyObfuscator implements PrivateKeyObfuscat return sb; } - protected byte[] generateInitializationVector(int keyLength) { - int keySize = keyLength / Byte.SIZE; - if ((keyLength % Byte.SIZE) != 0) { // e.g., if 36-bits then we need 5 bytes to hold - keySize++; - } - - byte[] initVector = new byte[keySize]; - Random randomizer = new SecureRandom(); // TODO consider using some pre-created singleton instance - randomizer.nextBytes(initVector); - return initVector; - } + protected abstract int resolveInitializationVectorLength(PrivateKeyEncryptionContext encContext) + throws GeneralSecurityException; protected abstract int resolveKeyLength(PrivateKeyEncryptionContext encContext) throws GeneralSecurityException; diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/DESPrivateKeyObfuscator.java b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/DESPrivateKeyObfuscator.java index e62882b..411ca85 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/DESPrivateKeyObfuscator.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/config/keys/loader/DESPrivateKeyObfuscator.java @@ -57,8 +57,8 @@ public class DESPrivateKeyObfuscator extends AbstractPrivateKeyObfuscator { } @Override - protected byte[] generateInitializationVector(int keyLength) { - return super.generateInitializationVector(8 * Byte.SIZE); + protected int resolveInitializationVectorLength(PrivateKeyEncryptionContext encContext) throws GeneralSecurityException { + return 8; } public static final PrivateKeyEncryptionContext resolveEffectiveContext(PrivateKeyEncryptionContext encContext) { diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscatorTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscatorTest.java index 46daf15..df0c7f7 100644 --- a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscatorTest.java +++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/loader/AESPrivateKeyObfuscatorTest.java @@ -20,12 +20,16 @@ package org.apache.sshd.common.config.keys.loader; import java.security.GeneralSecurityException; import java.security.Key; +import java.util.Collection; import java.util.List; -import java.util.Random; +import java.util.function.Predicate; +import java.util.stream.Collectors; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; +import org.apache.sshd.common.cipher.BuiltinCiphers; +import org.apache.sshd.common.cipher.CipherInformation; import org.apache.sshd.common.util.GenericUtils; import org.apache.sshd.common.util.security.SecurityUtils; import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory; @@ -48,8 +52,6 @@ import org.junit.runners.Parameterized.UseParametersRunnerFactory; @UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class) @Category({ NoIoTestCase.class }) public class AESPrivateKeyObfuscatorTest extends JUnitTestSupport { - private static final Random RANDOMIZER = new Random(System.currentTimeMillis()); - private final int keyLength; public AESPrivateKeyObfuscatorTest(int keyLength) { @@ -67,13 +69,35 @@ public class AESPrivateKeyObfuscatorTest extends JUnitTestSupport { public void testAvailableKeyLengthExists() throws GeneralSecurityException { assertEquals("Not a BYTE size multiple", 0, keyLength % Byte.SIZE); - byte[] iv = new byte[keyLength / Byte.SIZE]; - synchronized (RANDOMIZER) { - RANDOMIZER.nextBytes(iv); - } + PrivateKeyEncryptionContext encContext = new PrivateKeyEncryptionContext(); + encContext.setCipherName(AESPrivateKeyObfuscator.CIPHER_NAME); + encContext.setCipherMode(PrivateKeyEncryptionContext.DEFAULT_CIPHER_MODE); + encContext.setCipherType(Integer.toString(keyLength)); + + int actual = AESPrivateKeyObfuscator.INSTANCE.resolveKeyLength(encContext); + assertEquals("Mismatched resolved key length", keyLength, actual); + + // see SSHD-987 + byte[] iv = AESPrivateKeyObfuscator.INSTANCE.generateInitializationVector(encContext); + assertEquals("Mismatched IV size", 16 /* TODO change this if GCM allowed */, iv.length); Key key = new SecretKeySpec(iv, AESPrivateKeyObfuscator.CIPHER_NAME); Cipher c = SecurityUtils.getCipher(AESPrivateKeyObfuscator.CIPHER_NAME); c.init(Cipher.DECRYPT_MODE, key); } + + @Test + public void testSingleCipherMatch() { + Predicate<CipherInformation> selector = AESPrivateKeyObfuscator.createCipherSelector( + keyLength, PrivateKeyEncryptionContext.DEFAULT_CIPHER_MODE); + Collection<CipherInformation> matches = BuiltinCiphers.VALUES.stream() + .filter(selector) + .collect(Collectors.toList()); + assertEquals("Mismatched matching ciphers: " + matches, 1, GenericUtils.size(matches)); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[keyLength=" + keyLength + "]"; + } }