Correct punctuation in the ChangeLog summary line.
[gnupg.git] / sm / minip12.c
index d340f98..7b53a81 100644 (file)
@@ -1,5 +1,5 @@
 /* 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.
  *
  * This file is part of GnuPG.
  *
@@ -104,6 +104,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 +117,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 +136,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 +157,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 +179,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 +245,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,9 +268,9 @@ 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
    NULL on error or a new malloced buffer with the length of this new
@@ -291,7 +297,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 +306,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 +326,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 +351,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 +387,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 +424,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 +453,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 +535,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 +549,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,7 +610,7 @@ 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;
@@ -566,7 +621,7 @@ decrypt_block (const void *ciphertext, unsigned char *plaintext, size_t length,
                     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 +646,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 +654,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 +673,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. */
   int is_3des = 0;
+  int is_pbes2 = 0;
   gcry_mpi_t *result = NULL;
   int result_count;
 
@@ -666,7 +723,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 +731,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 +740,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 +867,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 +877,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 +1027,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 +1063,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 +1073,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 +1086,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 +1101,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 +1110,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 +1169,7 @@ bag_data_p (const void *plaintext, size_t length)
       || ti.length != 1 || *p)
     return 0;
 
-  return 1; 
+  return 1;
 }
 
 
@@ -1049,6 +1185,7 @@ 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;
@@ -1056,6 +1193,7 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset,
   int result_count, i;
   unsigned char *cram_buffer = NULL;
   size_t consumed = 0; /* Number of bytes consumed from the orginal buffer. */
+  int is_pbes2 = 0;
 
   where = "start";
   if (parse_tag (&p, &n, &ti))
@@ -1081,7 +1219,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 +1257,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 +1385,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 +1449,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 +1523,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 +1571,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 +1590,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 +1655,7 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw,
             goto bailout;
         }
     }
-  
+
   gcry_free (cram_buffer);
   return result;
  bailout:
@@ -1458,7 +1677,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 +1696,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 +1795,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 +1803,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 +1881,7 @@ create_final (struct buffer_s *sequences, const char *pw, size_t *r_length)
        SEQUENCE {
          INTEGER 0
          INTEGER
-         INTEGER 
+         INTEGER
          INTEGER
          INTEGER
          INTEGER
@@ -1672,9 +1891,9 @@ create_final (struct buffer_s *sequences, const char *pw, size_t *r_length)
          }
        }
      }
-*/  
-  
-static unsigned char * 
+*/
+
+static unsigned char *
 build_key_sequence (gcry_mpi_t *kparms, size_t *r_length)
 {
   int rc, i;
@@ -1727,7 +1946,7 @@ build_key_sequence (gcry_mpi_t *kparms, size_t *r_length)
   if (!n)
     return NULL;
   needed += n;
-  
+
   /* allocate 8 extra bytes for padding */
   plain = gcry_malloc_secure (needed+8);
   if (!plain)
@@ -1735,7 +1954,7 @@ 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);
@@ -1746,8 +1965,8 @@ build_key_sequence (gcry_mpi_t *kparms, size_t *r_length)
   /* 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); 
+  memcpy (p, oid_rsaEncryption, DIM (oid_rsaEncryption));
+  p += DIM (oid_rsaEncryption);
   *p++ = TAG_NULL;
   *p++ = 0;
   /* Start with the octet string. */
@@ -1769,7 +1988,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)
@@ -1864,8 +2083,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 +2100,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 +2137,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 +2200,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 +2211,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 +2219,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 +2232,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 +2243,7 @@ build_cert_bag (unsigned char *buffer, size_t buflen, char *salt,
 
 
 static unsigned char *
-build_cert_sequence (const 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 +2308,8 @@ build_cert_sequence (const 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 +2321,8 @@ build_cert_sequence (const 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 +2331,7 @@ build_cert_sequence (const 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 +2352,7 @@ build_cert_sequence (const 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,7 +2362,7 @@ build_cert_sequence (const 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 * 
+unsigned char *
 p12_build (gcry_mpi_t *kparms, const void *cert, size_t certlen,
            const char *pw, const char *charset, size_t *r_length)
 {
@@ -2193,7 +2412,7 @@ p12_build (gcry_mpi_t *kparms, const void *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",
@@ -2223,9 +2442,9 @@ p12_build (gcry_mpi_t *kparms, const void *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;
@@ -2243,14 +2462,15 @@ p12_build (gcry_mpi_t *kparms, const void *cert, size_t certlen,
       buffer = build_key_sequence (kparms, &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,
@@ -2284,7 +2504,7 @@ p12_build (gcry_mpi_t *kparms, const void *cert, size_t certlen,
 
 #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);
@@ -2315,7 +2535,7 @@ main (int argc, char **argv)
       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));