CipherTest fixes
Bug: 9095447
Change-Id: Ieba76865c4da4260949391389611dfd09bc5e326
diff --git a/luni/src/test/java/libcore/javax/crypto/CipherTest.java b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
index 9f19eef..4054813 100644
--- a/luni/src/test/java/libcore/javax/crypto/CipherTest.java
+++ b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
@@ -78,7 +78,7 @@
String algorithm = "PBEWITHMD5ANDTRIPLEDES";
Cipher.getInstance(algorithm).init(getEncryptMode(algorithm),
getEncryptKey(algorithm),
- getAlgorithmParameterSpec(algorithm));
+ getEncryptAlgorithmParameterSpec(algorithm));
is_unlimited = true;
} catch (Exception e) {
is_unlimited = false;
@@ -91,7 +91,7 @@
IS_UNLIMITED = is_unlimited;
}
- private static boolean isUnsupported(String algorithm) {
+ private static boolean isUnsupported(String algorithm, String provider) {
if (algorithm.equals("RC2")) {
return true;
}
@@ -115,6 +115,30 @@
return true;
}
}
+ // stream modes CFB, CTR, CTS, OFB with PKCS5Padding don't really make sense
+ if (!provider.equals("AndroidOpenSSL") &&
+ (algorithm.equals("AES/CFB/PKCS5PADDING")
+ || algorithm.equals("AES/CTR/PKCS5PADDING")
+ || algorithm.equals("AES/CTS/PKCS5PADDING")
+ || algorithm.equals("AES/OFB/PKCS5PADDING"))) {
+ return true;
+ }
+ return false;
+ }
+
+ private static boolean isUnsupportedForWrapping(String algorithm) {
+ if (isOnlyWrappingAlgorithm(algorithm)) {
+ return false;
+ }
+ // http://b/9097343 RSA with NoPadding won't work since
+ // leading zeroes in the underlying key material are lost.
+ if (algorithm.equals("RSA/ECB/NOPADDING")) {
+ return true;
+ }
+ // AESWRAP should be used instead, fails with BC and SunJCE otherwise.
+ if (algorithm.startsWith("AES")) {
+ return true;
+ }
return false;
}
@@ -258,6 +282,18 @@
private static Map<String, Integer> EXPECTED_BLOCK_SIZE = new HashMap<String, Integer>();
static {
setExpectedBlockSize("AES", 16);
+ setExpectedBlockSize("AES/CBC/PKCS5PADDING", 16);
+ setExpectedBlockSize("AES/CBC/NOPADDING", 16);
+ setExpectedBlockSize("AES/CFB/PKCS5PADDING", 16);
+ setExpectedBlockSize("AES/CFB/NOPADDING", 16);
+ setExpectedBlockSize("AES/CTR/PKCS5PADDING", 16);
+ setExpectedBlockSize("AES/CTR/NOPADDING", 16);
+ setExpectedBlockSize("AES/CTS/PKCS5PADDING", 16);
+ setExpectedBlockSize("AES/CTS/NOPADDING", 16);
+ setExpectedBlockSize("AES/ECB/PKCS5PADDING", 16);
+ setExpectedBlockSize("AES/ECB/NOPADDING", 16);
+ setExpectedBlockSize("AES/OFB/PKCS5PADDING", 16);
+ setExpectedBlockSize("AES/OFB/NOPADDING", 16);
setExpectedBlockSize("PBEWITHMD5AND128BITAES-CBC-OPENSSL", 16);
setExpectedBlockSize("PBEWITHMD5AND192BITAES-CBC-OPENSSL", 16);
setExpectedBlockSize("PBEWITHMD5AND256BITAES-CBC-OPENSSL", 16);
@@ -342,6 +378,8 @@
}
private static int getExpectedSize(Map<String, Integer> map, String algorithm, int mode, String provider) {
+ algorithm = algorithm.toUpperCase(Locale.US);
+ provider = provider.toUpperCase(Locale.US);
Integer expected = map.get(modeProviderKey(algorithm, mode, provider));
if (expected != null) {
return expected;
@@ -351,7 +389,8 @@
return expected;
}
expected = map.get(algorithm);
- assertNotNull("Algorithm " + algorithm + " not found in " + map, expected);
+ assertNotNull("Algorithm " + algorithm + " with mode " + mode + " and provider " + provider
+ + " not found in " + map, expected);
return expected;
}
@@ -373,7 +412,20 @@
private static Map<String, Integer> EXPECTED_OUTPUT_SIZE = new HashMap<String, Integer>();
static {
+ setExpectedOutputSize("AES/CBC/NOPADDING", 0);
+ setExpectedOutputSize("AES/CFB/NOPADDING", 0);
+ setExpectedOutputSize("AES/CTR/NOPADDING", 0);
+ setExpectedOutputSize("AES/CTS/NOPADDING", 0);
+ setExpectedOutputSize("AES/ECB/NOPADDING", 0);
+ setExpectedOutputSize("AES/OFB/NOPADDING", 0);
+
setExpectedOutputSize("AES", Cipher.ENCRYPT_MODE, 16);
+ setExpectedOutputSize("AES/CBC/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
+ setExpectedOutputSize("AES/CFB/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
+ setExpectedOutputSize("AES/CTR/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
+ setExpectedOutputSize("AES/CTS/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
+ setExpectedOutputSize("AES/ECB/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
+ setExpectedOutputSize("AES/OFB/PKCS5PADDING", Cipher.ENCRYPT_MODE, 16);
setExpectedOutputSize("PBEWITHMD5AND128BITAES-CBC-OPENSSL", 16);
setExpectedOutputSize("PBEWITHMD5AND192BITAES-CBC-OPENSSL", 16);
setExpectedOutputSize("PBEWITHMD5AND256BITAES-CBC-OPENSSL", 16);
@@ -383,8 +435,19 @@
setExpectedOutputSize("PBEWITHSHAAND128BITAES-CBC-BC", 16);
setExpectedOutputSize("PBEWITHSHAAND192BITAES-CBC-BC", 16);
setExpectedOutputSize("PBEWITHSHAAND256BITAES-CBC-BC", 16);
+ // AndroidOpenSSL returns zero for the non-block ciphers
+ setExpectedOutputSize("AES/CFB/PKCS5PADDING", Cipher.ENCRYPT_MODE, "AndroidOpenSSL", 0);
+ setExpectedOutputSize("AES/CTR/PKCS5PADDING", Cipher.ENCRYPT_MODE, "AndroidOpenSSL", 0);
+ setExpectedOutputSize("AES/CTS/PKCS5PADDING", Cipher.ENCRYPT_MODE, "AndroidOpenSSL", 0);
+ setExpectedOutputSize("AES/OFB/PKCS5PADDING", Cipher.ENCRYPT_MODE, "AndroidOpenSSL", 0);
setExpectedOutputSize("AES", Cipher.DECRYPT_MODE, 0);
+ setExpectedOutputSize("AES/CBC/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+ setExpectedOutputSize("AES/CFB/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+ setExpectedOutputSize("AES/CTR/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+ setExpectedOutputSize("AES/CTS/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+ setExpectedOutputSize("AES/ECB/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
+ setExpectedOutputSize("AES/OFB/PKCS5PADDING", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("PBEWITHMD5AND128BITAES-CBC-OPENSSL", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("PBEWITHMD5AND192BITAES-CBC-OPENSSL", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("PBEWITHMD5AND256BITAES-CBC-OPENSSL", Cipher.DECRYPT_MODE, 0);
@@ -394,6 +457,9 @@
setExpectedOutputSize("PBEWITHSHAAND128BITAES-CBC-BC", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("PBEWITHSHAAND192BITAES-CBC-BC", Cipher.DECRYPT_MODE, 0);
setExpectedOutputSize("PBEWITHSHAAND256BITAES-CBC-BC", Cipher.DECRYPT_MODE, 0);
+ // AndroidOpenSSL returns the block size for the block ciphers
+ setExpectedOutputSize("AES/CBC/PKCS5PADDING", Cipher.DECRYPT_MODE, "AndroidOpenSSL", 16);
+ setExpectedOutputSize("AES/ECB/PKCS5PADDING", Cipher.DECRYPT_MODE, "AndroidOpenSSL", 16);
if (StandardNames.IS_RI) {
setExpectedOutputSize("AESWRAP", Cipher.WRAP_MODE, 8);
@@ -445,6 +511,9 @@
setExpectedOutputSize("RSA/ECB/NoPadding", Cipher.DECRYPT_MODE, 256);
setExpectedOutputSize("RSA/ECB/PKCS1Padding", Cipher.DECRYPT_MODE, 245);
+ // SunJCE returns the full for size even when PKCS1Padding is specified
+ setExpectedOutputSize("RSA/ECB/PKCS1Padding", Cipher.DECRYPT_MODE, "SunJCE", 256);
+
// BC strips the leading 0 for us even when NoPadding is specified
setExpectedOutputSize("RSA", Cipher.DECRYPT_MODE, "BC", 255);
setExpectedOutputSize("RSA/ECB/NoPadding", Cipher.DECRYPT_MODE, "BC", 255);
@@ -467,6 +536,10 @@
}
private static byte[] ORIGINAL_PLAIN_TEXT = new byte[] { 0x0a, 0x0b, 0x0c };
+ private static byte[] SIXTEEN_BYTE_BLOCK_PLAIN_TEXT = new byte[] { 0x0a, 0x0b, 0x0c, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00 };
private static byte[] PKCS1_BLOCK_TYPE_00_PADDED_PLAIN_TEXT = new byte[] {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -554,20 +627,66 @@
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x00, (byte) 0x0a, (byte) 0x0b, (byte) 0x0c
};
- private static byte[] getExpectedPlainText(String algorithm) {
- if (algorithm.equals("RSA/ECB/NOPADDING")) {
+
+ private static byte[] getActualPlainText(String algorithm) {
+ // Block mode AES with NoPadding needs to match underlying block size
+ if (algorithm.equals("AES")
+ || algorithm.equals("AES/CBC/NOPADDING")
+ || algorithm.equals("AES/CTS/NOPADDING")
+ || algorithm.equals("AES/ECB/NOPADDING")) {
+ return SIXTEEN_BYTE_BLOCK_PLAIN_TEXT;
+ }
+ return ORIGINAL_PLAIN_TEXT;
+ }
+
+ private static byte[] getExpectedPlainText(String algorithm, String provider) {
+ // Block mode AES with NoPadding needs to match underlying block size
+ if (algorithm.equals("AES")
+ || algorithm.equals("AES/CBC/NOPADDING")
+ || algorithm.equals("AES/CTS/NOPADDING")
+ || algorithm.equals("AES/ECB/NOPADDING")) {
+ return SIXTEEN_BYTE_BLOCK_PLAIN_TEXT;
+ }
+ // BC strips the leading 0 for us even when NoPadding is specified
+ if (!provider.equals("BC") && algorithm.equals("RSA/ECB/NOPADDING")) {
return PKCS1_BLOCK_TYPE_00_PADDED_PLAIN_TEXT;
}
return ORIGINAL_PLAIN_TEXT;
}
- private static AlgorithmParameterSpec getAlgorithmParameterSpec(String algorithm) {
- if (!isPBE(algorithm)) {
+ private static AlgorithmParameterSpec getEncryptAlgorithmParameterSpec(String algorithm) {
+ if (isPBE(algorithm)) {
+ final byte[] salt = new byte[8];
+ new SecureRandom().nextBytes(salt);
+ return new PBEParameterSpec(salt, 1024);
+ }
+ if (algorithm.equals("AES/CBC/NOPADDING")
+ || algorithm.equals("AES/CBC/PKCS5PADDING")
+ || algorithm.equals("AES/CFB/NOPADDING")
+ || algorithm.equals("AES/CTR/NOPADDING")
+ || algorithm.equals("AES/CTS/NOPADDING")
+ || algorithm.equals("AES/OFB/NOPADDING")) {
+ final byte[] iv = new byte[16];
+ new SecureRandom().nextBytes(iv);
+ return new IvParameterSpec(iv);
+ }
+ return null;
+ }
+
+ private static AlgorithmParameterSpec getDecryptAlgorithmParameterSpec(AlgorithmParameterSpec encryptSpec,
+ Cipher encryptCipher) {
+ String algorithm = encryptCipher.getAlgorithm().toUpperCase(Locale.US);
+ if (isPBE(algorithm)) {
+ return encryptSpec;
+ }
+ if (isOnlyWrappingAlgorithm(algorithm)) {
return null;
}
- final byte[] salt = new byte[8];
- new SecureRandom().nextBytes(salt);
- return new PBEParameterSpec(salt, 1024);
+ byte[] iv = encryptCipher.getIV();
+ if (iv != null) {
+ return new IvParameterSpec(iv);
+ }
+ return null;
}
public void test_getInstance() throws Exception {
@@ -645,6 +764,17 @@
}
private void test_Cipher_Algorithm(Provider provider, String algorithm) throws Exception {
+ if (algorithm.equals("RSA") && provider.getName().equals("BC")) {
+ // http://b/9097343 BC's Cipher.RSA defaults to NoPadding
+ // which makes it fail the key wrapping test if the
+ // generated AES key to wrap starts with a leading
+ // zero. For the purposes of the test, use the same
+ // default behavior as the RI. Real code really should
+ // specify the exact mode and padding they need and not
+ // rely on defaults. http://b/9097343
+ algorithm = "RSA/ECB/PKCS1Padding";
+ }
+
// Cipher.getInstance(String)
Cipher c1 = Cipher.getInstance(algorithm);
assertEquals(algorithm, c1.getAlgorithm());
@@ -656,7 +786,7 @@
assertEquals(provider, c2.getProvider());
test_Cipher(c2);
- // KeyGenerator.getInstance(String, String)
+ // Cipher.getInstance(String, String)
Cipher c3 = Cipher.getInstance(algorithm, provider.getName());
assertEquals(algorithm, c3.getAlgorithm());
assertEquals(provider, c3.getProvider());
@@ -665,10 +795,10 @@
private void test_Cipher(Cipher c) throws Exception {
String algorithm = c.getAlgorithm().toUpperCase(Locale.US);
- if (isUnsupported(algorithm)) {
+ String providerName = c.getProvider().getName();
+ if (isUnsupported(algorithm, providerName)) {
return;
}
- String providerName = c.getProvider().getName();
String cipherID = algorithm + ":" + providerName;
try {
@@ -679,19 +809,20 @@
// TODO: test keys from different factories (e.g. OpenSSLRSAPrivateKey vs JCERSAPrivateKey)
Key encryptKey = getEncryptKey(algorithm);
- final AlgorithmParameterSpec spec = getAlgorithmParameterSpec(algorithm);
-
+ final AlgorithmParameterSpec encryptSpec = getEncryptAlgorithmParameterSpec(algorithm);
int encryptMode = getEncryptMode(algorithm);
- c.init(encryptMode, encryptKey, spec);
- assertEquals(cipherID + " getBlockSize()",
+ c.init(encryptMode, encryptKey, encryptSpec);
+ assertEquals(cipherID + " getBlockSize() encryptMode",
getExpectedBlockSize(algorithm, encryptMode, providerName), c.getBlockSize());
- assertEquals(cipherID + " getOutputSize(0)",
+ assertEquals(cipherID + " getOutputSize(0) encryptMode",
getExpectedOutputSize(algorithm, encryptMode, providerName), c.getOutputSize(0));
+
+ final AlgorithmParameterSpec decryptSpec = getDecryptAlgorithmParameterSpec(encryptSpec, c);
int decryptMode = getDecryptMode(algorithm);
- c.init(decryptMode, encryptKey, spec);
- assertEquals(cipherID + " getBlockSize()",
+ c.init(decryptMode, encryptKey, decryptSpec);
+ assertEquals(cipherID + " getBlockSize() decryptMode",
getExpectedBlockSize(algorithm, decryptMode, providerName), c.getBlockSize());
- assertEquals(cipherID + " getOutputSize(0)",
+ assertEquals(cipherID + " getOutputSize(0) decryptMode",
getExpectedOutputSize(algorithm, decryptMode, providerName), c.getOutputSize(0));
// TODO: test Cipher.getIV()
@@ -700,19 +831,19 @@
assertNull(cipherID, c.getExemptionMechanism());
- // Test wrapping a key. Every cipher should be able to wrap.
- {
+ // Test wrapping a key. Every cipher should be able to wrap. Except those that can't.
+ if (!isUnsupportedForWrapping(algorithm)) {
// Generate a small SecretKey for AES.
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(128);
SecretKey sk = kg.generateKey();
// Wrap it
- c.init(Cipher.WRAP_MODE, encryptKey, spec);
+ c.init(Cipher.WRAP_MODE, encryptKey, encryptSpec);
byte[] cipherText = c.wrap(sk);
// Unwrap it
- c.init(Cipher.UNWRAP_MODE, getDecryptKey(algorithm), spec);
+ c.init(Cipher.UNWRAP_MODE, getDecryptKey(algorithm), decryptSpec);
Key decryptedKey = c.unwrap(cipherText, sk.getAlgorithm(), Cipher.SECRET_KEY);
assertEquals(cipherID
@@ -724,12 +855,13 @@
}
if (!isOnlyWrappingAlgorithm(algorithm)) {
- c.init(Cipher.ENCRYPT_MODE, encryptKey, spec);
- byte[] cipherText = c.doFinal(ORIGINAL_PLAIN_TEXT);
- c.init(Cipher.DECRYPT_MODE, getDecryptKey(algorithm), spec);
+ c.init(Cipher.ENCRYPT_MODE, encryptKey, encryptSpec);
+ byte[] cipherText = c.doFinal(getActualPlainText(algorithm));
+ c.init(Cipher.DECRYPT_MODE, getDecryptKey(algorithm), decryptSpec);
byte[] decryptedPlainText = c.doFinal(cipherText);
- assertEquals(cipherID, Arrays.toString(getExpectedPlainText(algorithm)),
- Arrays.toString(decryptedPlainText));
+ assertEquals(cipherID,
+ Arrays.toString(getExpectedPlainText(algorithm, providerName)),
+ Arrays.toString(decryptedPlainText));
}
}
diff --git a/support/src/test/java/libcore/java/security/StandardNames.java b/support/src/test/java/libcore/java/security/StandardNames.java
index e0ed6f5..ae25352 100644
--- a/support/src/test/java/libcore/java/security/StandardNames.java
+++ b/support/src/test/java/libcore/java/security/StandardNames.java
@@ -118,10 +118,10 @@
modes.addAll(Arrays.asList(newModes));
}
private static void provideCipherPaddings(String algorithm, String newPaddings[]) {
- Set<String> paddings = CIPHER_MODES.get(algorithm);
+ Set<String> paddings = CIPHER_PADDINGS.get(algorithm);
if (paddings == null) {
paddings = new HashSet<String>();
- CIPHER_MODES.put(algorithm, paddings);
+ CIPHER_PADDINGS.put(algorithm, paddings);
}
paddings.addAll(Arrays.asList(newPaddings));
}
@@ -161,6 +161,10 @@
provide("Cipher", "PBEWithSHA1AndRC2_40");
provide("Cipher", "RC2");
provide("Cipher", "RSA");
+ // TODO: None?
+ provideCipherModes("RSA", new String[] { "ECB" });
+ // TODO: OAEPPadding
+ provideCipherPaddings("RSA", new String[] { "NoPadding", "PKCS1Padding" });
provide("Configuration", "JavaLoginConfig");
provide("KeyAgreement", "DiffieHellman");
provide("KeyFactory", "DSA");