Support pkcs#12 import of PBES2 encoded data.

This is so that we read compatible with gnutls's certtool.  Only
AES-128 is supported.  The latest Libgcrypt from git is required.

Fixes bug#1321.
This commit is contained in:
Werner Koch 2011-03-10 15:27:10 +01:00
parent 87a6a1c3fe
commit 35c731d889
2 changed files with 299 additions and 70 deletions

View File

@ -1,3 +1,12 @@
2011-03-10 Werner Koch <wk@g10code.com>
* minip12.c (oid_pkcs5PBKDF2, oid_pkcs5PBES2, oid_aes128_CBC): New.
(set_key_iv_pbes2): New.
(crypt_block): Add args IV and IVLEN. Call set_key_iv_pbes2.
(decrypt_block): Add args IV and IVLEN.
(parse_bag_encrypted_data): Hack to support PBES2 data.
(parse_bag_data): Ditto.
2011-03-03 Werner Koch <wk@g10code.com> 2011-03-03 Werner Koch <wk@g10code.com>
* base64.c (base64_finish_write): Do not copy to radbuf to get rid * base64.c (base64_finish_write): Do not copy to radbuf to get rid

View File

@ -1,5 +1,5 @@
/* minip12.c - A minimal pkcs-12 implementation. /* 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. * 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] = { static unsigned char const oid_x509Certificate_for_pkcs_12[10] = {
0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x16, 0x01 }; 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] = { static unsigned char const oid_rsaEncryption[9] = {
0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 }; 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 };
@ -447,9 +453,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 static void
crypt_block (unsigned char *buffer, size_t length, char *salt, size_t saltlen, 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; gcry_cipher_hd_t chd;
int rc; int rc;
@ -461,8 +512,11 @@ crypt_block (unsigned char *buffer, size_t length, char *salt, size_t saltlen,
wipememory (buffer, length); wipememory (buffer, length);
return; 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); wipememory (buffer, length);
goto leave; goto leave;
@ -495,7 +549,8 @@ crypt_block (unsigned char *buffer, size_t length, char *salt, size_t saltlen,
static void static void
decrypt_block (const void *ciphertext, unsigned char *plaintext, size_t length, decrypt_block (const void *ciphertext, unsigned char *plaintext, size_t length,
char *salt, size_t saltlen, 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)) int (*check_fnc) (const void *, size_t))
{ {
static const char * const charsets[] = { static const char * const charsets[] = {
@ -566,7 +621,7 @@ decrypt_block (const void *ciphertext, unsigned char *plaintext, size_t length,
charsets[charsetidx]); charsets[charsetidx]);
} }
memcpy (plaintext, ciphertext, length); 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); convertedpw? convertedpw:pw, cipher_algo, 0);
if (check_fnc (plaintext, length)) if (check_fnc (plaintext, length))
break; /* Decryption succeeded. */ break; /* Decryption succeeded. */
@ -618,12 +673,14 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
const char *where; const char *where;
char salt[20]; char salt[20];
size_t saltlen; size_t saltlen;
char iv[16];
unsigned int iter; unsigned int iter;
unsigned char *plain = NULL; unsigned char *plain = NULL;
int bad_pass = 0; int bad_pass = 0;
unsigned char *cram_buffer = NULL; 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 orginal buffer. */
int is_3des = 0; int is_3des = 0;
int is_pbes2 = 0;
gcry_mpi_t *result = NULL; gcry_mpi_t *result = NULL;
int result_count; int result_count;
@ -683,35 +740,111 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
n -= DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC); n -= DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC);
is_3des = 1; 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 else
goto bailout; goto bailout;
where = "rc2or3des-params"; if (is_pbes2)
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; where = "pkcs5PBES2-params";
iter |= (*p++) & 0xff; if (parse_tag (&p, &n, &ti))
n--; 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)) if (parse_tag (&p, &n, &ti))
goto bailout; goto bailout;
@ -735,7 +868,8 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
else else
goto bailout; 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); plain = gcry_malloc_secure (ti.length);
if (!plain) if (!plain)
@ -743,8 +877,10 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
log_error ("error allocating decryption buffer\n"); log_error ("error allocating decryption buffer\n");
goto bailout; goto bailout;
} }
decrypt_block (p, plain, ti.length, salt, saltlen, iter, pw, decrypt_block (p, plain, ti.length, salt, saltlen, iter,
is_3des? GCRY_CIPHER_3DES : GCRY_CIPHER_RFC2268_40, iv, is_pbes2?16:0, pw,
is_pbes2 ? GCRY_CIPHER_AES128 :
is_3des ? GCRY_CIPHER_3DES : GCRY_CIPHER_RFC2268_40,
bag_decrypted_data_p); bag_decrypted_data_p);
n = ti.length; n = ti.length;
startoffset = 0; startoffset = 0;
@ -950,7 +1086,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 that is less or equal to the cipher's block length. We can
reasonable assume that all valid data will be longer than reasonable assume that all valid data will be longer than
just one block. */ just one block. */
if (n <= 8) if (n <= (is_pbes2? 16:8))
n = 0; n = 0;
/* Skip the optional SET with the pkcs12 cert attributes. */ /* Skip the optional SET with the pkcs12 cert attributes. */
@ -965,7 +1101,7 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
{ /* The optional SET. */ { /* The optional SET. */
p += ti.length; p += ti.length;
n -= ti.length; n -= ti.length;
if (n <= 8) if (n <= (is_pbes2?16:8))
n = 0; n = 0;
if (n && parse_tag (&p, &n, &ti)) if (n && parse_tag (&p, &n, &ti))
goto bailout; goto bailout;
@ -1049,6 +1185,7 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset,
const char *where; const char *where;
char salt[20]; char salt[20];
size_t saltlen; size_t saltlen;
char iv[16];
unsigned int iter; unsigned int iter;
int len; int len;
unsigned char *plain = NULL; unsigned char *plain = NULL;
@ -1056,6 +1193,7 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset,
int result_count, i; int result_count, i;
unsigned char *cram_buffer = NULL; 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 orginal buffer. */
int is_pbes2 = 0;
where = "start"; where = "start";
if (parse_tag (&p, &n, &ti)) if (parse_tag (&p, &n, &ti))
@ -1119,46 +1257,126 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset,
goto bailout; goto bailout;
if (parse_tag (&p, &n, &ti)) if (parse_tag (&p, &n, &ti))
goto bailout; goto bailout;
if (ti.class || ti.tag != TAG_OBJECT_ID if (ti.class == 0 && ti.tag == TAG_OBJECT_ID
|| ti.length != DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC) && ti.length == DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC)
|| memcmp (p, oid_pbeWithSHAAnd3_KeyTripleDES_CBC, && !memcmp (p, oid_pbeWithSHAAnd3_KeyTripleDES_CBC,
DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC))) DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC)))
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--)
{ {
iter <<= 8; p += DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC);
iter |= (*p++) & 0xff; n -= DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC);
n--; }
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;
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
{
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)) if (parse_tag (&p, &n, &ti))
goto bailout; goto bailout;
if (ti.class || ti.tag != TAG_OCTET_STRING || !ti.length ) if (ti.class || ti.tag != TAG_OCTET_STRING || !ti.length )
goto bailout; 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); plain = gcry_malloc_secure (ti.length);
if (!plain) if (!plain)
@ -1167,8 +1385,9 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset,
goto bailout; goto bailout;
} }
consumed += p - p_start + ti.length; consumed += p - p_start + ti.length;
decrypt_block (p, plain, ti.length, salt, saltlen, iter, pw, decrypt_block (p, plain, ti.length, salt, saltlen, iter,
GCRY_CIPHER_3DES, iv, is_pbes2? 16:0, pw,
is_pbes2? GCRY_CIPHER_AES128 : GCRY_CIPHER_3DES,
bag_data_p); bag_data_p);
n = ti.length; n = ti.length;
startoffset = 0; startoffset = 0;
@ -2223,7 +2442,7 @@ p12_build (gcry_mpi_t *kparms, const void *cert, size_t certlen,
/* Encrypt it. */ /* Encrypt it. */
gcry_randomize (salt, 8, GCRY_STRONG_RANDOM); 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); GCRY_CIPHER_RFC2268_40, 1);
/* Encode the encrypted stuff into a bag. */ /* Encode the encrypted stuff into a bag. */
@ -2246,7 +2465,8 @@ p12_build (gcry_mpi_t *kparms, const void *cert, size_t certlen,
/* Encrypt it. */ /* Encrypt it. */
gcry_randomize (salt, 8, GCRY_STRONG_RANDOM); 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. */ /* Encode the encrypted stuff into a bag. */
if (cert && certlen) if (cert && certlen)