Restore PBE Cipher wrap and unwrap support from upstream

Bug: https://code.google.com/p/android/issues/detail?id=41405
Change-Id: I9bf90613c510f753032110724b074d31ae76a1fb
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEBlockCipher.java
index 2205a26..ca655cf 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEBlockCipher.java
@@ -5,9 +5,15 @@
 import java.security.InvalidKeyException;
 import java.security.InvalidParameterException;
 import java.security.Key;
+import java.security.KeyFactory;
 import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
 import java.security.SecureRandom;
 import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
 
 import javax.crypto.BadPaddingException;
 import javax.crypto.Cipher;
@@ -22,7 +28,9 @@
 // import javax.crypto.spec.RC2ParameterSpec;
 // import javax.crypto.spec.RC5ParameterSpec;
 // END android-removed
+import javax.crypto.spec.SecretKeySpec;
 
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.crypto.BlockCipher;
 import org.bouncycastle.crypto.BufferedBlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
@@ -779,6 +787,108 @@
         return "CCM".equals(modeName) || "EAX".equals(modeName) || "GCM".equals(modeName);
     }
 
+    protected byte[] engineWrap(
+        Key     key)
+    throws IllegalBlockSizeException, InvalidKeyException
+    {
+        byte[] encoded = key.getEncoded();
+        if (encoded == null)
+        {
+            throw new InvalidKeyException("Cannot wrap key, null encoding.");
+        }
+
+        try
+        {
+            return engineDoFinal(encoded, 0, encoded.length);
+        }
+        catch (BadPaddingException e)
+        {
+            throw new IllegalBlockSizeException(e.getMessage());
+        }
+    }
+
+    protected Key engineUnwrap(
+        byte[] wrappedKey,
+        String wrappedKeyAlgorithm,
+        int wrappedKeyType)
+        throws InvalidKeyException
+    {
+        byte[] encoded;
+        try
+        {
+            encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length);
+        }
+        catch (BadPaddingException e)
+        {
+            throw new InvalidKeyException(e.getMessage());
+        }
+        catch (IllegalBlockSizeException e2)
+        {
+            throw new InvalidKeyException(e2.getMessage());
+        }
+
+        if (wrappedKeyType == Cipher.SECRET_KEY)
+        {
+            return new SecretKeySpec(encoded, wrappedKeyAlgorithm);
+        }
+        else if (wrappedKeyAlgorithm.equals("") && wrappedKeyType == Cipher.PRIVATE_KEY)
+        {
+            /*
+             * The caller doesn't know the algorithm as it is part of
+             * the encrypted data.
+             */
+            try
+            {
+                PrivateKeyInfo       in = PrivateKeyInfo.getInstance(encoded);
+
+                PrivateKey privKey = BouncyCastleProvider.getPrivateKey(in);
+
+                if (privKey != null)
+                {
+                    return privKey;
+                }
+                else
+                {
+                    throw new InvalidKeyException("algorithm " + in.getPrivateKeyAlgorithm().getAlgorithm() + " not supported");
+                }
+            }
+            catch (Exception e)
+            {
+                throw new InvalidKeyException("Invalid key encoding.");
+            }
+        }
+        else
+        {
+            try
+            {
+                KeyFactory kf = KeyFactory.getInstance(wrappedKeyAlgorithm, BouncyCastleProvider.PROVIDER_NAME);
+
+                if (wrappedKeyType == Cipher.PUBLIC_KEY)
+                {
+                    return kf.generatePublic(new X509EncodedKeySpec(encoded));
+                }
+                else if (wrappedKeyType == Cipher.PRIVATE_KEY)
+                {
+                    return kf.generatePrivate(new PKCS8EncodedKeySpec(encoded));
+                }
+            }
+            catch (NoSuchProviderException e)
+            {
+                throw new InvalidKeyException("Unknown key type " + e.getMessage());
+            }
+            catch (NoSuchAlgorithmException e)
+            {
+                throw new InvalidKeyException("Unknown key type " + e.getMessage());
+            }
+            catch (InvalidKeySpecException e2)
+            {
+                throw new InvalidKeyException("Unknown key type " + e2.getMessage());
+            }
+
+            throw new InvalidKeyException("Unknown key type " + wrappedKeyType);
+        }
+    }
+
     /*
      * The ciphers that inherit from us.
      */
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEStreamCipher.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEStreamCipher.java
index 16a14ec..4600679 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEStreamCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEStreamCipher.java
@@ -4,11 +4,20 @@
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
 import java.security.Key;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
 import java.security.SecureRandom;
 import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
 
+import javax.crypto.BadPaddingException;
 import javax.crypto.Cipher;
 import javax.crypto.CipherSpi;
+import javax.crypto.IllegalBlockSizeException;
 import javax.crypto.NoSuchPaddingException;
 import javax.crypto.SecretKey;
 import javax.crypto.ShortBufferException;
@@ -18,7 +27,9 @@
 // import javax.crypto.spec.RC2ParameterSpec;
 // import javax.crypto.spec.RC5ParameterSpec;
 // END android-removed
+import javax.crypto.spec.SecretKeySpec;
 
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.crypto.BlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.DataLengthException;
@@ -347,7 +358,8 @@
     protected byte[] engineDoFinal(
         byte[]  input,
         int     inputOffset,
-        int     inputLen) 
+        int     inputLen)
+        throws BadPaddingException, IllegalBlockSizeException
     {
         if (inputLen != 0)
         {
@@ -368,7 +380,8 @@
         int     inputOffset,
         int     inputLen,
         byte[]  output,
-        int     outputOffset) 
+        int     outputOffset)
+        throws BadPaddingException
     {
         if (inputLen != 0)
         {
@@ -380,6 +393,108 @@
         return inputLen;
     }
 
+    protected byte[] engineWrap(
+         Key     key)
+     throws IllegalBlockSizeException, InvalidKeyException
+     {
+         byte[] encoded = key.getEncoded();
+         if (encoded == null)
+         {
+             throw new InvalidKeyException("Cannot wrap key, null encoding.");
+         }
+
+         try
+         {
+             return engineDoFinal(encoded, 0, encoded.length);
+         }
+         catch (BadPaddingException e)
+         {
+             throw new IllegalBlockSizeException(e.getMessage());
+         }
+     }
+
+     protected Key engineUnwrap(
+         byte[] wrappedKey,
+         String wrappedKeyAlgorithm,
+         int wrappedKeyType)
+         throws InvalidKeyException
+     {
+         byte[] encoded;
+         try
+         {
+             encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length);
+         }
+         catch (BadPaddingException e)
+         {
+             throw new InvalidKeyException(e.getMessage());
+         }
+         catch (IllegalBlockSizeException e2)
+         {
+             throw new InvalidKeyException(e2.getMessage());
+         }
+
+         if (wrappedKeyType == Cipher.SECRET_KEY)
+         {
+             return new SecretKeySpec(encoded, wrappedKeyAlgorithm);
+         }
+         else if (wrappedKeyAlgorithm.equals("") && wrappedKeyType == Cipher.PRIVATE_KEY)
+         {
+             /*
+              * The caller doesn't know the algorithm as it is part of
+              * the encrypted data.
+              */
+             try
+             {
+                 PrivateKeyInfo in = PrivateKeyInfo.getInstance(encoded);
+
+                 PrivateKey privKey = BouncyCastleProvider.getPrivateKey(in);
+
+                 if (privKey != null)
+                 {
+                     return privKey;
+                 }
+                 else
+                 {
+                     throw new InvalidKeyException("algorithm " + in.getPrivateKeyAlgorithm().getAlgorithm() + " not supported");
+                 }
+             }
+             catch (Exception e)
+             {
+                 throw new InvalidKeyException("Invalid key encoding.");
+             }
+         }
+         else
+         {
+             try
+             {
+                 KeyFactory kf = KeyFactory.getInstance(wrappedKeyAlgorithm, BouncyCastleProvider.PROVIDER_NAME);
+
+                 if (wrappedKeyType == Cipher.PUBLIC_KEY)
+                 {
+                     return kf.generatePublic(new X509EncodedKeySpec(encoded));
+                 }
+                 else if (wrappedKeyType == Cipher.PRIVATE_KEY)
+                 {
+                     return kf.generatePrivate(new PKCS8EncodedKeySpec(encoded));
+                 }
+             }
+             catch (NoSuchProviderException e)
+             {
+                 throw new InvalidKeyException("Unknown key type " + e.getMessage());
+             }
+             catch (NoSuchAlgorithmException e)
+             {
+                 throw new InvalidKeyException("Unknown key type " + e.getMessage());
+             }
+             catch (InvalidKeySpecException e2)
+             {
+                 throw new InvalidKeyException("Unknown key type " + e2.getMessage());
+             }
+
+             throw new InvalidKeyException("Unknown key type " + wrappedKeyType);
+         }
+     }
+
     /*
      * The ciphers that inherit from us.
      */
diff --git a/bouncycastle.config b/bouncycastle.config
index 7ee682e..c31204f 100644
--- a/bouncycastle.config
+++ b/bouncycastle.config
@@ -592,5 +592,5 @@
 NEEDED_BCPKIX_SOURCES="org"
 
 # list of patch files to apply in the given order
-BOUNCYCASTLE_BCPROV_PATCHES="patches/bcprov.patch"
+BOUNCYCASTLE_BCPROV_PATCHES="patches/bcprov.patch patches/CipherSpi-engineWrap.patch"
 BOUNCYCASTLE_BCPKIX_PATCHES="patches/bcpkix.patch"
diff --git a/import_bouncycastle.sh b/import_bouncycastle.sh
index 5cbd504..a97b060 100755
--- a/import_bouncycastle.sh
+++ b/import_bouncycastle.sh
@@ -235,7 +235,7 @@
   # Apply appropriate patches
   for i in $bouncycastle_patches; do
     echo "Applying patch $i"
-    patch -p1 < ../$i || die "Could not apply patches/$i. Fix source and run: $0 regenerate patches/$i"
+    patch -p1 < ../$i || die "Could not apply patches/$i. Fix source and run: $0 regenerate $i"
 
     # make sure no unneeded sources got into the patch
     problem=0
diff --git a/patches/CipherSpi-engineWrap.patch b/patches/CipherSpi-engineWrap.patch
new file mode 100644
index 0000000..1df8688
--- /dev/null
+++ b/patches/CipherSpi-engineWrap.patch
@@ -0,0 +1,303 @@
+--- bcprov-jdk15on-147.orig/org/bouncycastle/jce/provider/JCEBlockCipher.java	2012/04/05 10:57:52	1.31
++++ bcprov-jdk15on-147/org/bouncycastle/jce/provider/JCEBlockCipher.java	2013/01/02 08:01:03	1.32
+@@ -5,9 +5,15 @@
+ import java.security.InvalidKeyException;
+ import java.security.InvalidParameterException;
+ import java.security.Key;
++import java.security.KeyFactory;
+ import java.security.NoSuchAlgorithmException;
++import java.security.NoSuchProviderException;
++import java.security.PrivateKey;
+ import java.security.SecureRandom;
+ import java.security.spec.AlgorithmParameterSpec;
++import java.security.spec.InvalidKeySpecException;
++import java.security.spec.PKCS8EncodedKeySpec;
++import java.security.spec.X509EncodedKeySpec;
+ 
+ import javax.crypto.BadPaddingException;
+ import javax.crypto.Cipher;
+@@ -20,9 +26,11 @@
+ import javax.crypto.spec.PBEParameterSpec;
+ // BEGIN android-removed
+ // import javax.crypto.spec.RC2ParameterSpec;
+ // import javax.crypto.spec.RC5ParameterSpec;
+ // END android-removed
++import javax.crypto.spec.SecretKeySpec;
+ 
++import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+ import org.bouncycastle.crypto.BlockCipher;
+ import org.bouncycastle.crypto.BufferedBlockCipher;
+ import org.bouncycastle.crypto.CipherParameters;
+@@ -739,6 +747,108 @@
+         return "CCM".equals(modeName) || "EAX".equals(modeName) || "GCM".equals(modeName);
+     }
+ 
++    protected byte[] engineWrap(
++        Key     key)
++    throws IllegalBlockSizeException, InvalidKeyException
++    {
++        byte[] encoded = key.getEncoded();
++        if (encoded == null)
++        {
++            throw new InvalidKeyException("Cannot wrap key, null encoding.");
++        }
++
++        try
++        {
++            return engineDoFinal(encoded, 0, encoded.length);
++        }
++        catch (BadPaddingException e)
++        {
++            throw new IllegalBlockSizeException(e.getMessage());
++        }
++    }
++
++    protected Key engineUnwrap(
++        byte[] wrappedKey,
++        String wrappedKeyAlgorithm,
++        int wrappedKeyType)
++        throws InvalidKeyException
++    {
++        byte[] encoded;
++        try
++        {
++            encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length);
++        }
++        catch (BadPaddingException e)
++        {
++            throw new InvalidKeyException(e.getMessage());
++        }
++        catch (IllegalBlockSizeException e2)
++        {
++            throw new InvalidKeyException(e2.getMessage());
++        }
++
++        if (wrappedKeyType == Cipher.SECRET_KEY)
++        {
++            return new SecretKeySpec(encoded, wrappedKeyAlgorithm);
++        }
++        else if (wrappedKeyAlgorithm.equals("") && wrappedKeyType == Cipher.PRIVATE_KEY)
++        {
++            /*
++             * The caller doesn't know the algorithm as it is part of
++             * the encrypted data.
++             */
++            try
++            {
++                PrivateKeyInfo       in = PrivateKeyInfo.getInstance(encoded);
++
++                PrivateKey privKey = BouncyCastleProvider.getPrivateKey(in);
++
++                if (privKey != null)
++                {
++                    return privKey;
++                }
++                else
++                {
++                    throw new InvalidKeyException("algorithm " + in.getPrivateKeyAlgorithm().getAlgorithm() + " not supported");
++                }
++            }
++            catch (Exception e)
++            {
++                throw new InvalidKeyException("Invalid key encoding.");
++            }
++        }
++        else
++        {
++            try
++            {
++                KeyFactory kf = KeyFactory.getInstance(wrappedKeyAlgorithm, BouncyCastleProvider.PROVIDER_NAME);
++
++                if (wrappedKeyType == Cipher.PUBLIC_KEY)
++                {
++                    return kf.generatePublic(new X509EncodedKeySpec(encoded));
++                }
++                else if (wrappedKeyType == Cipher.PRIVATE_KEY)
++                {
++                    return kf.generatePrivate(new PKCS8EncodedKeySpec(encoded));
++                }
++            }
++            catch (NoSuchProviderException e)
++            {
++                throw new InvalidKeyException("Unknown key type " + e.getMessage());
++            }
++            catch (NoSuchAlgorithmException e)
++            {
++                throw new InvalidKeyException("Unknown key type " + e.getMessage());
++            }
++            catch (InvalidKeySpecException e2)
++            {
++                throw new InvalidKeyException("Unknown key type " + e2.getMessage());
++            }
++
++            throw new InvalidKeyException("Unknown key type " + wrappedKeyType);
++        }
++    }
++
+     /*
+      * The ciphers that inherit from us.
+      */
+--- bcprov-jdk15on-147.orig/org/bouncycastle/jce/provider/JCEStreamCipher.java	2011/08/25 06:17:08	1.15
++++ bcprov-jdk15on-147/org/bouncycastle/jce/provider/JCEStreamCipher.java	2013/01/29 05:42:31	1.16
+@@ -4,11 +4,20 @@
+ import java.security.InvalidAlgorithmParameterException;
+ import java.security.InvalidKeyException;
+ import java.security.Key;
++import java.security.KeyFactory;
++import java.security.NoSuchAlgorithmException;
++import java.security.NoSuchProviderException;
++import java.security.PrivateKey;
+ import java.security.SecureRandom;
+ import java.security.spec.AlgorithmParameterSpec;
++import java.security.spec.InvalidKeySpecException;
++import java.security.spec.PKCS8EncodedKeySpec;
++import java.security.spec.X509EncodedKeySpec;
+ 
++import javax.crypto.BadPaddingException;
+ import javax.crypto.Cipher;
+ import javax.crypto.CipherSpi;
++import javax.crypto.IllegalBlockSizeException;
+ import javax.crypto.NoSuchPaddingException;
+ import javax.crypto.SecretKey;
+ import javax.crypto.ShortBufferException;
+@@ -16,9 +25,11 @@
+ import javax.crypto.spec.PBEParameterSpec;
+ // BEGIN android-removed
+ // import javax.crypto.spec.RC2ParameterSpec;
+ // import javax.crypto.spec.RC5ParameterSpec;
+ // END android-removed
++import javax.crypto.spec.SecretKeySpec;
+ 
++import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+ import org.bouncycastle.crypto.BlockCipher;
+ import org.bouncycastle.crypto.CipherParameters;
+ import org.bouncycastle.crypto.DataLengthException;
+@@ -339,7 +350,8 @@
+     protected byte[] engineDoFinal(
+         byte[]  input,
+         int     inputOffset,
+-        int     inputLen) 
++        int     inputLen)
++        throws BadPaddingException, IllegalBlockSizeException
+     {
+         if (inputLen != 0)
+         {
+@@ -360,7 +372,8 @@
+         int     inputOffset,
+         int     inputLen,
+         byte[]  output,
+-        int     outputOffset) 
++        int     outputOffset)
++        throws BadPaddingException
+     {
+         if (inputLen != 0)
+         {
+@@ -372,6 +385,108 @@
+         return inputLen;
+     }
+ 
++    protected byte[] engineWrap(
++         Key     key)
++     throws IllegalBlockSizeException, InvalidKeyException
++     {
++         byte[] encoded = key.getEncoded();
++         if (encoded == null)
++         {
++             throw new InvalidKeyException("Cannot wrap key, null encoding.");
++         }
++
++         try
++         {
++             return engineDoFinal(encoded, 0, encoded.length);
++         }
++         catch (BadPaddingException e)
++         {
++             throw new IllegalBlockSizeException(e.getMessage());
++         }
++     }
++
++     protected Key engineUnwrap(
++         byte[] wrappedKey,
++         String wrappedKeyAlgorithm,
++         int wrappedKeyType)
++         throws InvalidKeyException
++     {
++         byte[] encoded;
++         try
++         {
++             encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length);
++         }
++         catch (BadPaddingException e)
++         {
++             throw new InvalidKeyException(e.getMessage());
++         }
++         catch (IllegalBlockSizeException e2)
++         {
++             throw new InvalidKeyException(e2.getMessage());
++         }
++
++         if (wrappedKeyType == Cipher.SECRET_KEY)
++         {
++             return new SecretKeySpec(encoded, wrappedKeyAlgorithm);
++         }
++         else if (wrappedKeyAlgorithm.equals("") && wrappedKeyType == Cipher.PRIVATE_KEY)
++         {
++             /*
++              * The caller doesn't know the algorithm as it is part of
++              * the encrypted data.
++              */
++             try
++             {
++                 PrivateKeyInfo in = PrivateKeyInfo.getInstance(encoded);
++
++                 PrivateKey privKey = BouncyCastleProvider.getPrivateKey(in);
++
++                 if (privKey != null)
++                 {
++                     return privKey;
++                 }
++                 else
++                 {
++                     throw new InvalidKeyException("algorithm " + in.getPrivateKeyAlgorithm().getAlgorithm() + " not supported");
++                 }
++             }
++             catch (Exception e)
++             {
++                 throw new InvalidKeyException("Invalid key encoding.");
++             }
++         }
++         else
++         {
++             try
++             {
++                 KeyFactory kf = KeyFactory.getInstance(wrappedKeyAlgorithm, BouncyCastleProvider.PROVIDER_NAME);
++
++                 if (wrappedKeyType == Cipher.PUBLIC_KEY)
++                 {
++                     return kf.generatePublic(new X509EncodedKeySpec(encoded));
++                 }
++                 else if (wrappedKeyType == Cipher.PRIVATE_KEY)
++                 {
++                     return kf.generatePrivate(new PKCS8EncodedKeySpec(encoded));
++                 }
++             }
++             catch (NoSuchProviderException e)
++             {
++                 throw new InvalidKeyException("Unknown key type " + e.getMessage());
++             }
++             catch (NoSuchAlgorithmException e)
++             {
++                 throw new InvalidKeyException("Unknown key type " + e.getMessage());
++             }
++             catch (InvalidKeySpecException e2)
++             {
++                 throw new InvalidKeyException("Unknown key type " + e2.getMessage());
++             }
++
++             throw new InvalidKeyException("Unknown key type " + wrappedKeyType);
++         }
++     }
++
+     /*
+      * The ciphers that inherit from us.
+      */
diff --git a/patches/README b/patches/README
index 0caaea9..b9c9181 100644
--- a/patches/README
+++ b/patches/README
@@ -48,3 +48,14 @@
 The main differences involve:
 - removing algorithms not in our bcprov (MD2, MD4, SHA224, RIPEMD, GOST)
 - using the singleton DERNull.INSTANCE
+
+
+CipherSpi-engineWrap.patch:
+
+Fixes from upstream BouncyCastle repository for:
+    https://code.google.com/p/android/issues/detail?id=41405
+
+"added wrap/unwrap support back in."
+    http://www.bouncycastle.org/viewcvs/viewcvs.cgi/java/crypto/src/org/bouncycastle/jce/provider/JCEBlockCipher.java?r1=1.31&r2=1.32&view=patch
+"fix for JCEStreamCipher PBE wrapping"
+    http://www.bouncycastle.org/viewcvs/viewcvs.cgi/java/crypto/src/org/bouncycastle/jce/provider/JCEStreamCipher.java?r1=1.15&r2=1.16&view=patch