You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by lg...@apache.org on 2020/05/01 06:05:59 UTC

[mina-sshd] branch master updated: [SSHD-987] Correctly generate IV for AES private key obfuscator

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 <lg...@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 + "]";
+    }
 }