gpgscm: Use boxed values for source locations.
[gnupg.git] / sm / minip12.c
index 10f7c99..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 };
@@ -266,7 +273,7 @@ parse_tag (unsigned char const **buffer, size_t *size, struct tag_info *ti)
                         -- 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
@@ -447,9 +454,54 @@ 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;
@@ -461,8 +513,11 @@ crypt_block (unsigned char *buffer, size_t length, char *salt, size_t saltlen,
       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;
@@ -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[] = {
@@ -562,11 +618,11 @@ decrypt_block (const void *ciphertext, unsigned char *plaintext, size_t length,
             }
           *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. */
@@ -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;
 
@@ -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;
+    }
+  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 = "rc2or3des-ciphertext";
+  where = "rc2or3desoraes-ciphertext";
   if (parse_tag (&p, &n, &ti))
     goto bailout;
 
@@ -735,7 +869,8 @@ 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;
@@ -950,7 +1087,7 @@ 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)
+      if (n <= (is_pbes2? 16:8))
         n = 0;
 
       /* Skip the optional SET with the pkcs12 cert attributes. */
@@ -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;
@@ -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))
@@ -1119,46 +1258,126 @@ 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)
     {
-      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;
+    }
+  else
+    {
+      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;
@@ -1672,10 +1892,15 @@ create_final (struct buffer_s *sequences, const char *pw, 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, size_t *r_length)
+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,23 +1935,27 @@ 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);
@@ -1738,20 +1967,24 @@ build_key_sequence (gcry_mpi_t *kparms, size_t *r_length)
 
   /* 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;
@@ -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;
@@ -2182,9 +2419,8 @@ p12_build (gcry_mpi_t *kparms, const void *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;
         }
 
@@ -2196,9 +2432,8 @@ p12_build (gcry_mpi_t *kparms, const void *cert, size_t certlen,
                       &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,7 +2458,7 @@ 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. */
@@ -2240,13 +2475,14 @@ p12_build (gcry_mpi_t *kparms, const void *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)
@@ -2271,6 +2507,8 @@ p12_build (gcry_mpi_t *kparms, const void *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,6 +2520,24 @@ p12_build (gcry_mpi_t *kparms, const void *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
@@ -2312,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;
     }
 
@@ -2326,7 +2582,7 @@ 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);