dirmngr: Lazily launch ldap reaper thread.
[gnupg.git] / sm / minip12.c
index f50fbd4..f066892 100644 (file)
@@ -1,5 +1,6 @@
 /* minip12.c - A minimal pkcs-12 implementation.
- * Copyright (C) 2002, 2003, 2004, 2006 Free Software Foundation, Inc.
+ * Copyright (C) 2002, 2003, 2004, 2006, 2011 Free Software Foundation, Inc.
+ * Copyright (C) 2014 Werner Koch
  *
  * This file is part of GnuPG.
  *
@@ -14,7 +15,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -104,6 +105,12 @@ static unsigned char const oid_pbeWithSHAAnd40BitRC2_CBC[10] = {
 static unsigned char const oid_x509Certificate_for_pkcs_12[10] = {
   0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x16, 0x01 };
 
+static unsigned char const oid_pkcs5PBKDF2[9] = {
+  0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x05, 0x0C };
+static unsigned char const oid_pkcs5PBES2[9] = {
+  0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x05, 0x0D };
+static unsigned char const oid_aes128_CBC[9] = {
+  0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x02 };
 
 static unsigned char const oid_rsaEncryption[9] = {
   0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 };
@@ -111,14 +118,14 @@ static unsigned char const oid_rsaEncryption[9] = {
 
 static unsigned char const data_3desiter2048[30] = {
   0x30, 0x1C, 0x06, 0x0A, 0x2A, 0x86, 0x48, 0x86,
-  0xF7, 0x0D, 0x01, 0x0C, 0x01, 0x03, 0x30, 0x0E, 
+  0xF7, 0x0D, 0x01, 0x0C, 0x01, 0x03, 0x30, 0x0E,
   0x04, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
   0xFF, 0xFF, 0x02, 0x02, 0x08, 0x00 };
 #define DATA_3DESITER2048_SALT_OFF  18
 
 static unsigned char const data_rc2iter2048[30] = {
   0x30, 0x1C, 0x06, 0x0A, 0x2A, 0x86, 0x48, 0x86,
-  0xF7, 0x0D, 0x01, 0x0C, 0x01, 0x06, 0x30, 0x0E, 
+  0xF7, 0x0D, 0x01, 0x0C, 0x01, 0x06, 0x30, 0x0E,
   0x04, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
   0xFF, 0xFF, 0x02, 0x02, 0x08, 0x00 };
 #define DATA_RC2ITER2048_SALT_OFF  18
@@ -130,7 +137,7 @@ static unsigned char const data_mactemplate[51] = {
   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
   0xff, 0xff, 0xff, 0xff, 0xff, 0x04, 0x08, 0xff,
   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x02,
-  0x02, 0x08, 0x00 }; 
+  0x02, 0x08, 0x00 };
 #define DATA_MACTEMPLATE_MAC_OFF 17
 #define DATA_MACTEMPLATE_SALT_OFF 39
 
@@ -151,14 +158,14 @@ static unsigned char const data_attrtemplate[106] = {
   0x04, 0x14 }; /* Need to append SHA-1 digest. */
 #define DATA_ATTRTEMPLATE_KEYID_OFF 73
 
-struct buffer_s 
+struct buffer_s
 {
   unsigned char *buffer;
   size_t length;
-};  
+};
 
 
-struct tag_info 
+struct tag_info
 {
   int class;
   int is_constructed;
@@ -173,7 +180,7 @@ struct tag_info
    the tag and the length part from the TLV triplet.  Update BUFFER
    and SIZE on success.  Checks that the encoded length does not
    exhaust the length of the provided buffer. */
-static int 
+static int
 parse_tag (unsigned char const **buffer, size_t *size, struct tag_info *ti)
 {
   int c;
@@ -239,13 +246,13 @@ parse_tag (unsigned char const **buffer, size_t *size, struct tag_info *ti)
         }
       ti->length = len;
     }
-  
+
   if (ti->class == UNIVERSAL && !ti->tag)
     ti->length = 0;
 
   if (ti->length > length)
     return -1; /* data larger than buffer. */
-  
+
   *buffer = buf;
   *size = length;
   return 0;
@@ -262,11 +269,11 @@ parse_tag (unsigned char const **buffer, size_t *size, struct tag_info *ti)
           [...]
      04    2:         OCTET STRING
             :           00 00
-            :         } -- This denotes a Null tag and are the last 
+            :         } -- This denotes a Null tag and are the last
                         -- two bytes in INPUT.
-   
+
    Create a new buffer with the content of that octet string.  INPUT
-   is the orginal buffer with a length as stored at LENGTH.  Returns
+   is the original buffer with a length as stored at LENGTH.  Returns
    NULL on error or a new malloced buffer with the length of this new
    buffer stored at LENGTH and the number of bytes parsed from input
    are added to the value stored at INPUT_CONSUMED.  INPUT_CONSUMED is
@@ -291,7 +298,7 @@ cram_octet_string (const unsigned char *input, size_t *length,
     {
       if (parse_tag (&s, &n, &ti))
         goto bailout;
-      if (ti.class == UNIVERSAL && ti.tag == TAG_OCTET_STRING 
+      if (ti.class == UNIVERSAL && ti.tag == TAG_OCTET_STRING
           && !ti.ndef && !ti.is_constructed)
         {
           memcpy (d, s, ti.length);
@@ -300,7 +307,7 @@ cram_octet_string (const unsigned char *input, size_t *length,
           n -= ti.length;
         }
       else if (ti.class == UNIVERSAL && !ti.tag && !ti.is_constructed)
-        break; /* Ready */ 
+        break; /* Ready */
       else
         goto bailout;
     }
@@ -320,7 +327,7 @@ cram_octet_string (const unsigned char *input, size_t *length,
 
 
 
-static int 
+static int
 string_to_key (int id, char *salt, size_t saltlen, int iter, const char *pw,
                int req_keylen, unsigned char *keybuf)
 {
@@ -345,7 +352,7 @@ string_to_key (int id, char *salt, size_t saltlen, int iter, const char *pw,
       log_error ("salt too short\n");
       return -1;
     }
-  
+
   /* Store salt and password in BUF_I */
   p = buf_i;
   for(i=0; i < 64; i++)
@@ -381,7 +388,7 @@ string_to_key (int id, char *salt, size_t saltlen, int iter, const char *pw,
           gcry_mpi_release (num_b1);
           return 0; /* ready */
         }
-      
+
       /* need more bytes. */
       for(i=0; i < 64; i++)
         buf_b[i] = hash[i % 20];
@@ -418,7 +425,7 @@ string_to_key (int id, char *salt, size_t saltlen, int iter, const char *pw,
 }
 
 
-static int 
+static int
 set_key_iv (gcry_cipher_hd_t chd, char *salt, size_t saltlen, int iter,
             const char *pw, int keybytes)
 {
@@ -447,22 +454,70 @@ set_key_iv (gcry_cipher_hd_t chd, char *salt, size_t saltlen, int iter,
 }
 
 
+static int
+set_key_iv_pbes2 (gcry_cipher_hd_t chd, char *salt, size_t saltlen, int iter,
+                  const void *iv, size_t ivlen, const char *pw, int algo)
+{
+  unsigned char *keybuf;
+  size_t keylen;
+  int rc;
+
+  keylen = gcry_cipher_get_algo_keylen (algo);
+  if (!keylen)
+    return -1;
+  keybuf = gcry_malloc_secure (keylen);
+  if (!keybuf)
+    return -1;
+
+  rc = gcry_kdf_derive (pw, strlen (pw),
+                        GCRY_KDF_PBKDF2, GCRY_MD_SHA1,
+                        salt, saltlen, iter, keylen, keybuf);
+  if (rc)
+    {
+      log_error ("gcry_kdf_derive failed: %s\n", gpg_strerror (rc));
+      gcry_free (keybuf);
+      return -1;
+    }
+
+  rc = gcry_cipher_setkey (chd, keybuf, keylen);
+  gcry_free (keybuf);
+  if (rc)
+    {
+      log_error ("gcry_cipher_setkey failed: %s\n", gpg_strerror (rc));
+      return -1;
+    }
+
+
+  rc = gcry_cipher_setiv (chd, iv, ivlen);
+  if (rc)
+    {
+      log_error ("gcry_cipher_setiv failed: %s\n", gpg_strerror (rc));
+      return -1;
+    }
+  return 0;
+}
+
+
 static void
 crypt_block (unsigned char *buffer, size_t length, char *salt, size_t saltlen,
-             int iter, const char *pw, int cipher_algo, int encrypt)
+             int iter, const void *iv, size_t ivlen,
+             const char *pw, int cipher_algo, int encrypt)
 {
   gcry_cipher_hd_t chd;
   int rc;
 
-  rc = gcry_cipher_open (&chd, cipher_algo, GCRY_CIPHER_MODE_CBC, 0); 
+  rc = gcry_cipher_open (&chd, cipher_algo, GCRY_CIPHER_MODE_CBC, 0);
   if (rc)
     {
       log_error ( "gcry_cipher_open failed: %s\n", gpg_strerror(rc));
       wipememory (buffer, length);
       return;
     }
-  if (set_key_iv (chd, salt, saltlen, iter, pw,
-                  cipher_algo == GCRY_CIPHER_RFC2268_40? 5:24))
+
+  if (cipher_algo == GCRY_CIPHER_AES128
+      ? set_key_iv_pbes2 (chd, salt, saltlen, iter, iv, ivlen, pw, cipher_algo)
+      : set_key_iv (chd, salt, saltlen, iter, pw,
+                    cipher_algo == GCRY_CIPHER_RFC2268_40? 5:24))
     {
       wipememory (buffer, length);
       goto leave;
@@ -481,7 +536,7 @@ crypt_block (unsigned char *buffer, size_t length, char *salt, size_t saltlen,
  leave:
   gcry_cipher_close (chd);
 }
-  
+
 
 /* Decrypt a block of data and try several encodings of the key.
    CIPHERTEXT is the encrypted data of size LENGTH bytes; PLAINTEXT is
@@ -495,7 +550,8 @@ crypt_block (unsigned char *buffer, size_t length, char *salt, size_t saltlen,
 static void
 decrypt_block (const void *ciphertext, unsigned char *plaintext, size_t length,
                char *salt, size_t saltlen,
-               int iter, const char *pw, int cipher_algo,
+               int iter, const void *iv, size_t ivlen,
+               const char *pw, int cipher_algo,
                int (*check_fnc) (const void *, size_t))
 {
   static const char * const charsets[] = {
@@ -555,18 +611,18 @@ decrypt_block (const void *ciphertext, unsigned char *plaintext, size_t length,
           outptr = convertedpw;
           outbytes = convertedpwsize - 1;
           if ( jnlib_iconv (cd, (const char **)&inptr, &inbytes,
-                      &outptr, &outbytes) == (size_t)-1) 
+                      &outptr, &outbytes) == (size_t)-1)
             {
               jnlib_iconv_close (cd);
               continue;
             }
           *outptr = 0;
           jnlib_iconv_close (cd);
-          log_info ("decryption failed; trying charset `%s'\n",
+          log_info ("decryption failed; trying charset '%s'\n",
                     charsets[charsetidx]);
         }
       memcpy (plaintext, ciphertext, length);
-      crypt_block (plaintext, length, salt, saltlen, iter,
+      crypt_block (plaintext, length, salt, saltlen, iter, iv, ivlen,
                    convertedpw? convertedpw:pw, cipher_algo, 0);
       if (check_fnc (plaintext, length))
         break; /* Decryption succeeded. */
@@ -591,7 +647,7 @@ bag_decrypted_data_p (const void *plaintext, size_t length)
   /*       exit (2); */
   /*     fclose (fp); */
   /*   } */
-  
+
   if (parse_tag (&p, &n, &ti))
     return 0;
   if (ti.class || ti.tag != TAG_SEQUENCE)
@@ -599,7 +655,7 @@ bag_decrypted_data_p (const void *plaintext, size_t length)
   if (parse_tag (&p, &n, &ti))
     return 0;
 
-  return 1; 
+  return 1;
 }
 
 /* Note: If R_RESULT is passed as NULL, a key object as already be
@@ -618,12 +674,14 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
   const char *where;
   char salt[20];
   size_t saltlen;
+  char iv[16];
   unsigned int iter;
   unsigned char *plain = NULL;
   int bad_pass = 0;
   unsigned char *cram_buffer = NULL;
-  size_t consumed = 0; /* Number of bytes consumed from the orginal buffer. */
+  size_t consumed = 0; /* Number of bytes consumed from the original buffer. */
   int is_3des = 0;
+  int is_pbes2 = 0;
   gcry_mpi_t *result = NULL;
   int result_count;
 
@@ -666,7 +724,7 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
     goto bailout;
   if (parse_tag (&p, &n, &ti))
     goto bailout;
-  if (!ti.class && ti.tag == TAG_OBJECT_ID 
+  if (!ti.class && ti.tag == TAG_OBJECT_ID
       && ti.length == DIM(oid_pbeWithSHAAnd40BitRC2_CBC)
       && !memcmp (p, oid_pbeWithSHAAnd40BitRC2_CBC,
                   DIM(oid_pbeWithSHAAnd40BitRC2_CBC)))
@@ -674,7 +732,7 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
       p += DIM(oid_pbeWithSHAAnd40BitRC2_CBC);
       n -= DIM(oid_pbeWithSHAAnd40BitRC2_CBC);
     }
-  else if (!ti.class && ti.tag == TAG_OBJECT_ID 
+  else if (!ti.class && ti.tag == TAG_OBJECT_ID
       && ti.length == DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC)
       && !memcmp (p, oid_pbeWithSHAAnd3_KeyTripleDES_CBC,
                   DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC)))
@@ -683,35 +741,111 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
       n -= DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC);
       is_3des = 1;
     }
+  else if (!ti.class && ti.tag == TAG_OBJECT_ID
+           && ti.length == DIM(oid_pkcs5PBES2)
+           && !memcmp (p, oid_pkcs5PBES2, ti.length))
+    {
+      p += ti.length;
+      n -= ti.length;
+      is_pbes2 = 1;
+    }
   else
     goto bailout;
 
-  where = "rc2or3des-params";
-  if (parse_tag (&p, &n, &ti))
-    goto bailout;
-  if (ti.class || ti.tag != TAG_SEQUENCE)
-    goto bailout;
-  if (parse_tag (&p, &n, &ti))
-    goto bailout;
-  if (ti.class || ti.tag != TAG_OCTET_STRING
-      || ti.length < 8 || ti.length > 20 )
-    goto bailout;
-  saltlen = ti.length;
-  memcpy (salt, p, saltlen);
-  p += saltlen;
-  n -= saltlen;
-  if (parse_tag (&p, &n, &ti))
-    goto bailout;
-  if (ti.class || ti.tag != TAG_INTEGER || !ti.length )
-    goto bailout;
-  for (iter=0; ti.length; ti.length--)
+  if (is_pbes2)
     {
-      iter <<= 8;
-      iter |= (*p++) & 0xff; 
-      n--;
+      where = "pkcs5PBES2-params";
+      if (parse_tag (&p, &n, &ti))
+        goto bailout;
+      if (ti.class || ti.tag != TAG_SEQUENCE)
+        goto bailout;
+      if (parse_tag (&p, &n, &ti))
+        goto bailout;
+      if (ti.class || ti.tag != TAG_SEQUENCE)
+        goto bailout;
+      if (parse_tag (&p, &n, &ti))
+        goto bailout;
+      if (!(!ti.class && ti.tag == TAG_OBJECT_ID
+            && ti.length == DIM(oid_pkcs5PBKDF2)
+            && !memcmp (p, oid_pkcs5PBKDF2, ti.length)))
+        goto bailout; /* Not PBKDF2.  */
+      p += ti.length;
+      n -= ti.length;
+      if (parse_tag (&p, &n, &ti))
+        goto bailout;
+      if (ti.class || ti.tag != TAG_SEQUENCE)
+        goto bailout;
+      if (parse_tag (&p, &n, &ti))
+        goto bailout;
+      if (!(!ti.class && ti.tag == TAG_OCTET_STRING
+            && ti.length >= 8 && ti.length < sizeof salt))
+        goto bailout;  /* No salt or unsupported length.  */
+      saltlen = ti.length;
+      memcpy (salt, p, saltlen);
+      p += saltlen;
+      n -= saltlen;
+
+      if (parse_tag (&p, &n, &ti))
+        goto bailout;
+      if (!(!ti.class && ti.tag == TAG_INTEGER && ti.length))
+        goto bailout;  /* No valid iteration count.  */
+      for (iter=0; ti.length; ti.length--)
+        {
+          iter <<= 8;
+          iter |= (*p++) & 0xff;
+          n--;
+        }
+      /* Note: We don't support the optional parameters but assume
+         that the algorithmIdentifier follows. */
+      if (parse_tag (&p, &n, &ti))
+        goto bailout;
+      if (ti.class || ti.tag != TAG_SEQUENCE)
+        goto bailout;
+      if (parse_tag (&p, &n, &ti))
+        goto bailout;
+      if (!(!ti.class && ti.tag == TAG_OBJECT_ID
+            && ti.length == DIM(oid_aes128_CBC)
+            && !memcmp (p, oid_aes128_CBC, ti.length)))
+        goto bailout; /* Not AES-128.  */
+      p += ti.length;
+      n -= ti.length;
+      if (parse_tag (&p, &n, &ti))
+        goto bailout;
+      if (!(!ti.class && ti.tag == TAG_OCTET_STRING && ti.length == sizeof iv))
+        goto bailout; /* Bad IV.  */
+      memcpy (iv, p, sizeof iv);
+      p += sizeof iv;
+      n -= sizeof iv;
     }
-  
-  where = "rc2or3des-ciphertext";
+  else
+    {
+      where = "rc2or3des-params";
+      if (parse_tag (&p, &n, &ti))
+        goto bailout;
+      if (ti.class || ti.tag != TAG_SEQUENCE)
+        goto bailout;
+      if (parse_tag (&p, &n, &ti))
+        goto bailout;
+      if (ti.class || ti.tag != TAG_OCTET_STRING
+          || ti.length < 8 || ti.length > 20 )
+        goto bailout;
+      saltlen = ti.length;
+      memcpy (salt, p, saltlen);
+      p += saltlen;
+      n -= saltlen;
+      if (parse_tag (&p, &n, &ti))
+        goto bailout;
+      if (ti.class || ti.tag != TAG_INTEGER || !ti.length )
+        goto bailout;
+      for (iter=0; ti.length; ti.length--)
+        {
+          iter <<= 8;
+          iter |= (*p++) & 0xff;
+          n--;
+        }
+    }
+
+  where = "rc2or3desoraes-ciphertext";
   if (parse_tag (&p, &n, &ti))
     goto bailout;
 
@@ -734,8 +868,9 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
     ;
   else
     goto bailout;
-  
-  log_info ("%lu bytes of %s encrypted text\n",ti.length,is_3des?"3DES":"RC2");
+
+  log_info ("%lu bytes of %s encrypted text\n",ti.length,
+            is_pbes2?"AES128":is_3des?"3DES":"RC2");
 
   plain = gcry_malloc_secure (ti.length);
   if (!plain)
@@ -743,8 +878,10 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
       log_error ("error allocating decryption buffer\n");
       goto bailout;
     }
-  decrypt_block (p, plain, ti.length, salt, saltlen, iter, pw, 
-                 is_3des? GCRY_CIPHER_3DES : GCRY_CIPHER_RFC2268_40, 
+  decrypt_block (p, plain, ti.length, salt, saltlen, iter,
+                 iv, is_pbes2?16:0, pw,
+                 is_pbes2 ? GCRY_CIPHER_AES128 :
+                 is_3des  ? GCRY_CIPHER_3DES : GCRY_CIPHER_RFC2268_40,
                  bag_decrypted_data_p);
   n = ti.length;
   startoffset = 0;
@@ -891,7 +1028,7 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
               len -= ti.length;
               if (!result_count && ti.length == 1 && !*p)
                 ; /* ignore the very first one if it is a 0 */
-              else 
+              else
                 {
                   int rc;
 
@@ -927,7 +1064,7 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
             goto bailout;
           p += DIM(oid_x509Certificate_for_pkcs_12);
           n -= DIM(oid_x509Certificate_for_pkcs_12);
-          
+
           where = "certbag.before.octetstring";
           if (parse_tag (&p, &n, &ti))
             goto bailout;
@@ -937,11 +1074,11 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
             goto bailout;
           if (ti.class || ti.tag != TAG_OCTET_STRING || ti.ndef)
             goto bailout;
-          
+
           /* Return the certificate. */
           if (certcb)
             certcb (certcbarg, p, ti.length);
-   
+
           p += ti.length;
           n -= ti.length;
         }
@@ -950,8 +1087,8 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
          that is less or equal to the cipher's block length.  We can
          reasonable assume that all valid data will be longer than
          just one block. */
-      if (n <= 8)
-        n = 0;  
+      if (n <= (is_pbes2? 16:8))
+        n = 0;
 
       /* Skip the optional SET with the pkcs12 cert attributes. */
       if (n)
@@ -965,7 +1102,7 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
             { /* The optional SET. */
               p += ti.length;
               n -= ti.length;
-              if (n <= 8)
+              if (n <= (is_pbes2?16:8))
                 n = 0;
               if (n && parse_tag (&p, &n, &ti))
                 goto bailout;
@@ -974,7 +1111,7 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
             goto bailout;
         }
     }
-  
+
   if (r_consumed)
     *r_consumed = consumed;
   gcry_free (plain);
@@ -1033,7 +1170,7 @@ bag_data_p (const void *plaintext, size_t length)
       || ti.length != 1 || *p)
     return 0;
 
-  return 1; 
+  return 1;
 }
 
 
@@ -1049,13 +1186,15 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset,
   const char *where;
   char salt[20];
   size_t saltlen;
+  char iv[16];
   unsigned int iter;
   int len;
   unsigned char *plain = NULL;
   gcry_mpi_t *result = NULL;
   int result_count, i;
   unsigned char *cram_buffer = NULL;
-  size_t consumed = 0; /* Number of bytes consumed from the orginal buffer. */
+  size_t consumed = 0; /* Number of bytes consumed from the original buffer. */
+  int is_pbes2 = 0;
 
   where = "start";
   if (parse_tag (&p, &n, &ti))
@@ -1081,7 +1220,7 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset,
         *r_consumed = consumed;
       r_consumed = NULL; /* Ugly hack to not update that value any further. */
     }
-  
+
 
   where = "data.outerseqs";
   if (parse_tag (&p, &n, &ti))
@@ -1119,47 +1258,127 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset,
     goto bailout;
   if (parse_tag (&p, &n, &ti))
     goto bailout;
-  if (ti.class || ti.tag != TAG_OBJECT_ID
-      || ti.length != DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC)
-      || memcmp (p, oid_pbeWithSHAAnd3_KeyTripleDES_CBC,
-                 DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC)))
+  if (ti.class == 0 && ti.tag == TAG_OBJECT_ID
+      && ti.length == DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC)
+      && !memcmp (p, oid_pbeWithSHAAnd3_KeyTripleDES_CBC,
+                  DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC)))
+    {
+      p += DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC);
+      n -= DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC);
+    }
+  else if (ti.class == 0 && ti.tag == TAG_OBJECT_ID
+           && ti.length == DIM(oid_pkcs5PBES2)
+           && !memcmp (p, oid_pkcs5PBES2, DIM(oid_pkcs5PBES2)))
+    {
+      p += DIM(oid_pkcs5PBES2);
+      n -= DIM(oid_pkcs5PBES2);
+      is_pbes2 = 1;
+    }
+  else
     goto bailout;
-  p += DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC);
-  n -= DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC);
 
-  where = "3des-params";
-  if (parse_tag (&p, &n, &ti))
-    goto bailout;
-  if (ti.class || ti.tag != TAG_SEQUENCE)
-    goto bailout;
-  if (parse_tag (&p, &n, &ti))
-    goto bailout;
-  if (ti.class || ti.tag != TAG_OCTET_STRING
-      || ti.length < 8 || ti.length > 20)
-    goto bailout;
-  saltlen = ti.length;
-  memcpy (salt, p, saltlen);
-  p += saltlen;
-  n -= saltlen;
-  if (parse_tag (&p, &n, &ti))
-    goto bailout;
-  if (ti.class || ti.tag != TAG_INTEGER || !ti.length )
-    goto bailout;
-  for (iter=0; ti.length; ti.length--)
+  if (is_pbes2)
+    {
+      where = "pkcs5PBES2-params";
+      if (parse_tag (&p, &n, &ti))
+        goto bailout;
+      if (ti.class || ti.tag != TAG_SEQUENCE)
+        goto bailout;
+      if (parse_tag (&p, &n, &ti))
+        goto bailout;
+      if (ti.class || ti.tag != TAG_SEQUENCE)
+        goto bailout;
+      if (parse_tag (&p, &n, &ti))
+        goto bailout;
+      if (!(!ti.class && ti.tag == TAG_OBJECT_ID
+            && ti.length == DIM(oid_pkcs5PBKDF2)
+            && !memcmp (p, oid_pkcs5PBKDF2, ti.length)))
+        goto bailout; /* Not PBKDF2.  */
+      p += ti.length;
+      n -= ti.length;
+      if (parse_tag (&p, &n, &ti))
+        goto bailout;
+      if (ti.class || ti.tag != TAG_SEQUENCE)
+        goto bailout;
+      if (parse_tag (&p, &n, &ti))
+        goto bailout;
+      if (!(!ti.class && ti.tag == TAG_OCTET_STRING
+            && ti.length >= 8 && ti.length < sizeof salt))
+        goto bailout;  /* No salt or unsupported length.  */
+      saltlen = ti.length;
+      memcpy (salt, p, saltlen);
+      p += saltlen;
+      n -= saltlen;
+
+      if (parse_tag (&p, &n, &ti))
+        goto bailout;
+      if (!(!ti.class && ti.tag == TAG_INTEGER && ti.length))
+        goto bailout;  /* No valid iteration count.  */
+      for (iter=0; ti.length; ti.length--)
+        {
+          iter <<= 8;
+          iter |= (*p++) & 0xff;
+          n--;
+        }
+      /* Note: We don't support the optional parameters but assume
+         that the algorithmIdentifier follows. */
+      if (parse_tag (&p, &n, &ti))
+        goto bailout;
+      if (ti.class || ti.tag != TAG_SEQUENCE)
+        goto bailout;
+      if (parse_tag (&p, &n, &ti))
+        goto bailout;
+      if (!(!ti.class && ti.tag == TAG_OBJECT_ID
+            && ti.length == DIM(oid_aes128_CBC)
+            && !memcmp (p, oid_aes128_CBC, ti.length)))
+        goto bailout; /* Not AES-128.  */
+      p += ti.length;
+      n -= ti.length;
+      if (parse_tag (&p, &n, &ti))
+        goto bailout;
+      if (!(!ti.class && ti.tag == TAG_OCTET_STRING && ti.length == sizeof iv))
+        goto bailout; /* Bad IV.  */
+      memcpy (iv, p, sizeof iv);
+      p += sizeof iv;
+      n -= sizeof iv;
+    }
+  else
     {
-      iter <<= 8;
-      iter |= (*p++) & 0xff; 
-      n--;
+      where = "3des-params";
+      if (parse_tag (&p, &n, &ti))
+        goto bailout;
+      if (ti.class || ti.tag != TAG_SEQUENCE)
+        goto bailout;
+      if (parse_tag (&p, &n, &ti))
+        goto bailout;
+      if (ti.class || ti.tag != TAG_OCTET_STRING
+          || ti.length < 8 || ti.length > 20)
+        goto bailout;
+      saltlen = ti.length;
+      memcpy (salt, p, saltlen);
+      p += saltlen;
+      n -= saltlen;
+      if (parse_tag (&p, &n, &ti))
+        goto bailout;
+      if (ti.class || ti.tag != TAG_INTEGER || !ti.length )
+        goto bailout;
+      for (iter=0; ti.length; ti.length--)
+        {
+          iter <<= 8;
+          iter |= (*p++) & 0xff;
+          n--;
+        }
     }
-  
-  where = "3des-ciphertext";
+
+  where = "3desoraes-ciphertext";
   if (parse_tag (&p, &n, &ti))
     goto bailout;
   if (ti.class || ti.tag != TAG_OCTET_STRING || !ti.length )
     goto bailout;
-  
-  log_info ("%lu bytes of 3DES encrypted text\n", ti.length);
-  
+
+  log_info ("%lu bytes of %s encrypted text\n",
+            ti.length, is_pbes2? "AES128":"3DES");
+
   plain = gcry_malloc_secure (ti.length);
   if (!plain)
     {
@@ -1167,8 +1386,9 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset,
       goto bailout;
     }
   consumed += p - p_start + ti.length;
-  decrypt_block (p, plain, ti.length, salt, saltlen, iter, pw, 
-                 GCRY_CIPHER_3DES, 
+  decrypt_block (p, plain, ti.length, salt, saltlen, iter,
+                 iv, is_pbes2? 16:0, pw,
+                 is_pbes2? GCRY_CIPHER_AES128 : GCRY_CIPHER_3DES,
                  bag_data_p);
   n = ti.length;
   startoffset = 0;
@@ -1230,7 +1450,7 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset,
       len -= ti.length;
       if (!result_count && ti.length == 1 && !*p)
         ; /* ignore the very first one if it is a 0 */
-      else 
+      else
         {
           rc = gcry_mpi_scan (result+result_count, GCRYMPI_FMT_USG, p,
                               ti.length, NULL);
@@ -1304,7 +1524,7 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw,
   if (ti.tag != TAG_INTEGER || ti.length != 1 || *p != 3)
     goto bailout;
   p++; n--;
-  
+
   where = "authSave";
   if (parse_tag (&p, &n, &ti))
     goto bailout;
@@ -1352,7 +1572,7 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw,
       if (parse_tag (&p, &n, &ti))
         goto bailout;
       if (bagseqndef && ti.class == UNIVERSAL && !ti.tag && !ti.is_constructed)
-        break; /* Ready */ 
+        break; /* Ready */
       if (ti.class != UNIVERSAL || ti.tag != TAG_SEQUENCE)
         goto bailout;
 
@@ -1371,9 +1591,9 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw,
       if (parse_tag (&p, &n, &ti))
         goto bailout;
       if (lenndef)
-        len = ti.nhdr; 
+        len = ti.nhdr;
       else
-        len -= ti.nhdr; 
+        len -= ti.nhdr;
 
       if (ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_encryptedData)
           && !memcmp (p, oid_encryptedData, DIM(oid_encryptedData)))
@@ -1436,7 +1656,7 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw,
             goto bailout;
         }
     }
-  
+
   gcry_free (cram_buffer);
   return result;
  bailout:
@@ -1458,7 +1678,7 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw,
 \f
 static size_t
 compute_tag_length (size_t n)
-{     
+{
   int needed = 0;
 
   if (n < 128)
@@ -1477,7 +1697,7 @@ compute_tag_length (size_t n)
 
 static unsigned char *
 store_tag_length (unsigned char *p, int tag, size_t n)
-{     
+{
   if (tag == TAG_SEQUENCE)
     tag |= 0x20; /* constructed */
 
@@ -1576,7 +1796,7 @@ create_final (struct buffer_s *sequences, const char *pw, size_t *r_length)
 
   /* 1. Store the version integer 3. */
   *p++ = TAG_INTEGER;
-  *p++ = 1; 
+  *p++ = 1;
   *p++ = 3;
 
   /* 2. Store another sequence. */
@@ -1584,8 +1804,8 @@ create_final (struct buffer_s *sequences, const char *pw, size_t *r_length)
 
   /* 3. Store the data OID. */
   p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_data));
-  memcpy (p, oid_data, DIM (oid_data)); 
-  p += DIM (oid_data); 
+  memcpy (p, oid_data, DIM (oid_data));
+  p += DIM (oid_data);
 
   /* 4. Next comes a context tag. */
   p = store_tag_length (p, 0xa0, len[4]);
@@ -1662,7 +1882,7 @@ create_final (struct buffer_s *sequences, const char *pw, size_t *r_length)
        SEQUENCE {
          INTEGER 0
          INTEGER
-         INTEGER 
+         INTEGER
          INTEGER
          INTEGER
          INTEGER
@@ -1672,10 +1892,15 @@ create_final (struct buffer_s *sequences, const char *pw, size_t *r_length)
          }
        }
      }
-*/  
-  
-static unsigned char * 
-build_key_sequence (gcry_mpi_t *kparms, size_t *r_length)
+
+  MODE controls what is being generated:
+     0 - As described above
+     1 - Ditto but without the padding
+     2 - Only the inner part (pkcs#1)
+*/
+
+static unsigned char *
+build_key_sequence (gcry_mpi_t *kparms, int mode, size_t *r_length)
 {
   int rc, i;
   size_t needed, n;
@@ -1683,7 +1908,7 @@ build_key_sequence (gcry_mpi_t *kparms, size_t *r_length)
   size_t plainlen;
   size_t outseqlen, oidseqlen, octstrlen, inseqlen;
 
-  needed = 3; /* The version(?) integer of value 0. */
+  needed = 3; /* The version integer with value 0. */
   for (i=0; kparms[i]; i++)
     {
       n = 0;
@@ -1710,24 +1935,28 @@ build_key_sequence (gcry_mpi_t *kparms, size_t *r_length)
   if (!n)
     return NULL;
   needed += n;
-  /* Encapsulate all into an octet string. */
-  octstrlen = needed;
-  n = compute_tag_length (needed);
-  if (!n)
-    return NULL;
-  needed += n;
-  /* Prepend the object identifier sequence. */
-  oidseqlen = 2 + DIM (oid_rsaEncryption) + 2;
-  needed += 2 + oidseqlen;
-  /* The version number. */
-  needed += 3;
-  /* And finally put the whole thing into a sequence. */
-  outseqlen = needed;
-  n = compute_tag_length (needed);
-  if (!n)
-    return NULL;
-  needed += n;
-  
+
+  if (mode != 2)
+    {
+      /* Encapsulate all into an octet string. */
+      octstrlen = needed;
+      n = compute_tag_length (needed);
+      if (!n)
+        return NULL;
+      needed += n;
+      /* Prepend the object identifier sequence. */
+      oidseqlen = 2 + DIM (oid_rsaEncryption) + 2;
+      needed += 2 + oidseqlen;
+      /* The version number. */
+      needed += 3;
+      /* And finally put the whole thing into a sequence. */
+      outseqlen = needed;
+      n = compute_tag_length (needed);
+      if (!n)
+        return NULL;
+      needed += n;
+    }
+
   /* allocate 8 extra bytes for padding */
   plain = gcry_malloc_secure (needed+8);
   if (!plain)
@@ -1735,23 +1964,27 @@ build_key_sequence (gcry_mpi_t *kparms, size_t *r_length)
       log_error ("error allocating encryption buffer\n");
       return NULL;
     }
-  
+
   /* And now fill the plaintext buffer. */
   p = plain;
-  p = store_tag_length (p, TAG_SEQUENCE, outseqlen);
-  /* Store version. */
-  *p++ = TAG_INTEGER;
-  *p++ = 1;
-  *p++ = 0;
-  /* Store object identifier sequence. */
-  p = store_tag_length (p, TAG_SEQUENCE, oidseqlen);
-  p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_rsaEncryption));
-  memcpy (p, oid_rsaEncryption, DIM (oid_rsaEncryption)); 
-  p += DIM (oid_rsaEncryption); 
-  *p++ = TAG_NULL;
-  *p++ = 0;
-  /* Start with the octet string. */
-  p = store_tag_length (p, TAG_OCTET_STRING, octstrlen);
+  if (mode != 2)
+    {
+      p = store_tag_length (p, TAG_SEQUENCE, outseqlen);
+      /* Store version. */
+      *p++ = TAG_INTEGER;
+      *p++ = 1;
+      *p++ = 0;
+      /* Store object identifier sequence. */
+      p = store_tag_length (p, TAG_SEQUENCE, oidseqlen);
+      p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_rsaEncryption));
+      memcpy (p, oid_rsaEncryption, DIM (oid_rsaEncryption));
+      p += DIM (oid_rsaEncryption);
+      *p++ = TAG_NULL;
+      *p++ = 0;
+      /* Start with the octet string. */
+      p = store_tag_length (p, TAG_OCTET_STRING, octstrlen);
+    }
+
   p = store_tag_length (p, TAG_SEQUENCE, inseqlen);
   /* Store the key parameters. */
   *p++ = TAG_INTEGER;
@@ -1769,7 +2002,7 @@ build_key_sequence (gcry_mpi_t *kparms, size_t *r_length)
           return NULL;
         }
       p = store_tag_length (p, TAG_INTEGER, n);
-      
+
       n = plain + needed - p;
       rc = gcry_mpi_print (GCRYMPI_FMT_STD, p, n, &n, kparms[i]);
       if (rc)
@@ -1784,10 +2017,14 @@ build_key_sequence (gcry_mpi_t *kparms, size_t *r_length)
 
   plainlen = p - plain;
   assert (needed == plainlen);
-  /* Append some pad characters; we already allocated extra space. */
-  n = 8 - plainlen % 8;
-  for (i=0; i < n; i++, plainlen++)
-    *p++ = n;
+
+  if (!mode)
+    {
+      /* Append some pad characters; we already allocated extra space. */
+      n = 8 - plainlen % 8;
+      for (i=0; i < n; i++, plainlen++)
+        *p++ = n;
+    }
 
   *r_length = plainlen;
   return plain;
@@ -1864,8 +2101,8 @@ build_key_bag (unsigned char *buffer, size_t buflen, char *salt,
 
   /* 1. Store the data OID. */
   p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_data));
-  memcpy (p, oid_data, DIM (oid_data)); 
-  p += DIM (oid_data); 
+  memcpy (p, oid_data, DIM (oid_data));
+  p += DIM (oid_data);
 
   /* 2. Store a [0] tag. */
   p = store_tag_length (p, 0xa0, len[2]);
@@ -1881,8 +2118,8 @@ build_key_bag (unsigned char *buffer, size_t buflen, char *salt,
   p = store_tag_length (p, TAG_OBJECT_ID,
                         DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag));
   memcpy (p, oid_pkcs_12_pkcs_8ShroudedKeyBag,
-          DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag)); 
-  p += DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag); 
+          DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag));
+  p += DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag);
 
   /* 7. Store a [0] tag. */
   p = store_tag_length (p, 0xa0, len[7]);
@@ -1918,7 +2155,7 @@ build_key_bag (unsigned char *buffer, size_t buflen, char *salt,
   if (needed != keybaglen)
     log_debug ("length mismatch: %lu, %lu\n",
                (unsigned long)needed, (unsigned long)keybaglen);
-  
+
   *r_length = keybaglen;
   return keybag;
 }
@@ -1981,8 +2218,8 @@ build_cert_bag (unsigned char *buffer, size_t buflen, char *salt,
 
   /* 1. Store the encryptedData OID. */
   p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_encryptedData));
-  memcpy (p, oid_encryptedData, DIM (oid_encryptedData)); 
-  p += DIM (oid_encryptedData); 
+  memcpy (p, oid_encryptedData, DIM (oid_encryptedData));
+  p += DIM (oid_encryptedData);
 
   /* 2. Store a [0] tag. */
   p = store_tag_length (p, 0xa0, len[2]);
@@ -1992,7 +2229,7 @@ build_cert_bag (unsigned char *buffer, size_t buflen, char *salt,
 
   /* 4. Store the integer 0. */
   *p++ = TAG_INTEGER;
-  *p++ = 1; 
+  *p++ = 1;
   *p++ = 0;
 
   /* 5. Store a sequence. */
@@ -2000,8 +2237,8 @@ build_cert_bag (unsigned char *buffer, size_t buflen, char *salt,
 
   /* 6. Store the data OID. */
   p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_data));
-  memcpy (p, oid_data, DIM (oid_data)); 
-  p += DIM (oid_data); 
+  memcpy (p, oid_data, DIM (oid_data));
+  p += DIM (oid_data);
 
   /* 7. Now for the pre-encoded algorithm identifier and the salt. */
   memcpy (p, data_rc2iter2048, DIM (data_rc2iter2048));
@@ -2013,7 +2250,7 @@ build_cert_bag (unsigned char *buffer, size_t buflen, char *salt,
   memcpy (p, buffer, buflen);
   p += buflen;
   certbaglen = p - certbag;
-  
+
   if (needed != certbaglen)
     log_debug ("length mismatch: %lu, %lu\n",
                (unsigned long)needed, (unsigned long)certbaglen);
@@ -2024,7 +2261,7 @@ build_cert_bag (unsigned char *buffer, size_t buflen, char *salt,
 
 
 static unsigned char *
-build_cert_sequence (unsigned char *buffer, size_t buflen, 
+build_cert_sequence (const unsigned char *buffer, size_t buflen,
                      const unsigned char *sha1hash, const char *keyidstr,
                      size_t *r_length)
 {
@@ -2089,8 +2326,8 @@ build_cert_sequence (unsigned char *buffer, size_t buflen,
 
   /* 2. Store the pkcs12-cert-bag OID. */
   p = store_tag_length (p, TAG_OBJECT_ID, DIM (oid_pkcs_12_CertBag));
-  memcpy (p, oid_pkcs_12_CertBag, DIM (oid_pkcs_12_CertBag)); 
-  p += DIM (oid_pkcs_12_CertBag); 
+  memcpy (p, oid_pkcs_12_CertBag, DIM (oid_pkcs_12_CertBag));
+  p += DIM (oid_pkcs_12_CertBag);
 
   /* 3. Store a [0] tag. */
   p = store_tag_length (p, 0xa0, len[3]);
@@ -2102,8 +2339,8 @@ build_cert_sequence (unsigned char *buffer, size_t buflen,
   p = store_tag_length (p, TAG_OBJECT_ID,
                         DIM (oid_x509Certificate_for_pkcs_12));
   memcpy (p, oid_x509Certificate_for_pkcs_12,
-          DIM (oid_x509Certificate_for_pkcs_12)); 
-  p += DIM (oid_x509Certificate_for_pkcs_12); 
+          DIM (oid_x509Certificate_for_pkcs_12));
+  p += DIM (oid_x509Certificate_for_pkcs_12);
 
   /* 6. Store a [0] tag. */
   p = store_tag_length (p, 0xa0, len[6]);
@@ -2112,7 +2349,7 @@ build_cert_sequence (unsigned char *buffer, size_t buflen,
   p = store_tag_length (p, TAG_OCTET_STRING, buflen);
   memcpy (p, buffer, buflen);
   p += buflen;
-  
+
   /* Append the attributes whose length we calculated at step 2b. */
   if (sha1hash)
     {
@@ -2133,7 +2370,7 @@ build_cert_sequence (unsigned char *buffer, size_t buflen,
   n = 8 - certseqlen % 8;
   for (i=0; i < n; i++, certseqlen++)
     *p++ = n;
-  
+
   *r_length = certseqlen;
   return certseq;
 }
@@ -2143,8 +2380,8 @@ build_cert_sequence (unsigned char *buffer, size_t buflen,
    Create a PKCS structure from it and return it as well as the length
    in R_LENGTH; return NULL in case of an error.  If CHARSET is not
    NULL, re-encode PW to that character set. */
-unsigned char * 
-p12_build (gcry_mpi_t *kparms, unsigned char *cert, size_t certlen,
+unsigned char *
+p12_build (gcry_mpi_t *kparms, const void *cert, size_t certlen,
            const char *pw, const char *charset, size_t *r_length)
 {
   unsigned char *buffer = NULL;
@@ -2182,9 +2419,8 @@ p12_build (gcry_mpi_t *kparms, unsigned char *cert, size_t certlen,
       if (cd == (jnlib_iconv_t)(-1))
         {
           log_error ("can't convert passphrase to"
-                     " requested charset `%s': %s\n",
+                     " requested charset '%s': %s\n",
                      charset, strerror (errno));
-          gcry_free (pwbuf);
           goto failure;
         }
 
@@ -2193,12 +2429,11 @@ p12_build (gcry_mpi_t *kparms, unsigned char *cert, size_t certlen,
       outptr = pwbuf;
       outbytes = pwbufsize - 1;
       if ( jnlib_iconv (cd, (const char **)&inptr, &inbytes,
-                      &outptr, &outbytes) == (size_t)-1) 
+                      &outptr, &outbytes) == (size_t)-1)
         {
           log_error ("error converting passphrase to"
-                     " requested charset `%s': %s\n",
+                     " requested charset '%s': %s\n",
                      charset, strerror (errno));
-          gcry_free (pwbuf);
           jnlib_iconv_close (cd);
           goto failure;
         }
@@ -2223,9 +2458,9 @@ p12_build (gcry_mpi_t *kparms, unsigned char *cert, size_t certlen,
 
       /* Encrypt it. */
       gcry_randomize (salt, 8, GCRY_STRONG_RANDOM);
-      crypt_block (buffer, buflen, salt, 8, 2048, pw,
+      crypt_block (buffer, buflen, salt, 8, 2048, NULL, 0, pw,
                    GCRY_CIPHER_RFC2268_40, 1);
-      
+
       /* Encode the encrypted stuff into a bag. */
       seqlist[seqlistidx].buffer = build_cert_bag (buffer, buflen, salt, &n);
       seqlist[seqlistidx].length = n;
@@ -2240,17 +2475,18 @@ p12_build (gcry_mpi_t *kparms, unsigned char *cert, size_t certlen,
   if (kparms)
     {
       /* Encode the key. */
-      buffer = build_key_sequence (kparms, &buflen);
+      buffer = build_key_sequence (kparms, 0, &buflen);
       if (!buffer)
         goto failure;
-      
+
       /* Encrypt it. */
       gcry_randomize (salt, 8, GCRY_STRONG_RANDOM);
-      crypt_block (buffer, buflen, salt, 8, 2048, pw, GCRY_CIPHER_3DES, 1);
+      crypt_block (buffer, buflen, salt, 8, 2048, NULL, 0,
+                   pw, GCRY_CIPHER_3DES, 1);
 
       /* Encode the encrypted stuff into a bag. */
       if (cert && certlen)
-        seqlist[seqlistidx].buffer = build_key_bag (buffer, buflen, salt, 
+        seqlist[seqlistidx].buffer = build_key_bag (buffer, buflen, salt,
                                                     sha1hash, keyidstr, &n);
       else
         seqlist[seqlistidx].buffer = build_key_bag (buffer, buflen, salt,
@@ -2271,6 +2507,8 @@ p12_build (gcry_mpi_t *kparms, unsigned char *cert, size_t certlen,
  failure:
   if (pwbuf)
     {
+      /* Note that wipememory is not really needed due to the use of
+         gcry_malloc_secure.  */
       wipememory (pwbuf, pwbufsize);
       gcry_free (pwbuf);
     }
@@ -2282,9 +2520,27 @@ p12_build (gcry_mpi_t *kparms, unsigned char *cert, size_t certlen,
 }
 
 
+/* This is actually not a pkcs#12 function but one which creates an
+   unencrypted a pkcs#1 private key.  */
+unsigned char *
+p12_raw_build (gcry_mpi_t *kparms, int rawmode, size_t *r_length)
+{
+  unsigned char *buffer;
+  size_t buflen;
+
+  assert (rawmode == 1 || rawmode == 2);
+  buffer = build_key_sequence (kparms, rawmode, &buflen);
+  if (!buffer)
+    return NULL;
+
+  *r_length = buflen;
+  return buffer;
+}
+
+
 #ifdef TEST
 
-static void 
+static void
 cert_cb (void *opaque, const unsigned char *cert, size_t certlen)
 {
   printf ("got a certificate of %u bytes length\n", certlen);
@@ -2298,6 +2554,7 @@ main (int argc, char **argv)
   unsigned char *buf;
   size_t buflen;
   gcry_mpi_t *result;
+  int badpass;
 
   if (argc != 3)
     {
@@ -2311,13 +2568,13 @@ main (int argc, char **argv)
   fp = fopen (argv[1], "rb");
   if (!fp)
     {
-      fprintf (stderr, "can't open `%s': %s\n", argv[1], strerror (errno));
+      fprintf (stderr, "can't open '%s': %s\n", argv[1], strerror (errno));
       return 1;
     }
-  
+
   if (fstat (fileno(fp), &st))
     {
-      fprintf (stderr, "can't stat `%s': %s\n", argv[1], strerror (errno));
+      fprintf (stderr, "can't stat '%s': %s\n", argv[1], strerror (errno));
       return 1;
     }
 
@@ -2325,12 +2582,12 @@ main (int argc, char **argv)
   buf = gcry_malloc (buflen+1);
   if (!buf || fread (buf, buflen, 1, fp) != 1)
     {
-      fprintf (stderr, "error reading `%s': %s\n", argv[1], strerror (errno));
+      fprintf (stderr, "error reading '%s': %s\n", argv[1], strerror (errno));
       return 1;
     }
   fclose (fp);
 
-  result = p12_parse (buf, buflen, argv[2], cert_cb, NULL);
+  result = p12_parse (buf, buflen, argv[2], cert_cb, NULL, &badpass);
   if (result)
     {
       int i, rc;