1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-12-22 10:19:57 +01:00

* minip12.c (parse_bag_encrypted_data): Finished implementation.

(p12_parse): Add callback args.
* protect-tool.c (import_p12_cert_cb): New.
(import_p12_file): Use it.
This commit is contained in:
Werner Koch 2004-02-10 19:26:55 +00:00
parent cfb33014ae
commit a1dd1cc223
4 changed files with 199 additions and 38 deletions

View File

@ -1,3 +1,15 @@
2004-02-10 Werner Koch <wk@gnupg.org>
* minip12.c (parse_bag_encrypted_data): Finished implementation.
(p12_parse): Add callback args.
* protect-tool.c (import_p12_cert_cb): New.
(import_p12_file): Use it.
2004-02-06 Werner Koch <wk@gnupg.org>
* minip12.c (crypt_block): Add arg CIPHER_ALGO; changed all callers.
(set_key_iv): Add arg KEYBYTES; changed caller.
2004-02-03 Werner Koch <wk@gnupg.org> 2004-02-03 Werner Koch <wk@gnupg.org>
* findkey.c (agent_key_from_file): Extra paranoid wipe. * findkey.c (agent_key_from_file): Extra paranoid wipe.

View File

@ -1,5 +1,5 @@
/* minip12.c - A minimal pkcs-12 implementation. /* minip12.c - A minimal pkcs-12 implementation.
* Copyright (C) 2002, 2003 Free Software Foundation, Inc. * Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -27,7 +27,12 @@
#include <assert.h> #include <assert.h>
#include <gcrypt.h> #include <gcrypt.h>
#undef TEST #ifdef __GCC__
#warning Remove this kludge and set the libgcrypt required version higher.
#endif
#ifndef GCRY_CIPHER_RFC2268_40
#define GCRY_CIPHER_RFC2268_40 307
#endif
#ifdef TEST #ifdef TEST
#include <sys/stat.h> #include <sys/stat.h>
@ -97,6 +102,9 @@ static unsigned char const oid_pbeWithSHAAnd3_KeyTripleDES_CBC[10] = {
0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x01, 0x03 }; 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x01, 0x03 };
static unsigned char const oid_pbeWithSHAAnd40BitRC2_CBC[10] = { static unsigned char const oid_pbeWithSHAAnd40BitRC2_CBC[10] = {
0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x01, 0x06 }; 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x01, 0x06 };
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_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 };
@ -303,14 +311,16 @@ string_to_key (int id, char *salt, int iter, const char *pw,
static int static int
set_key_iv (gcry_cipher_hd_t chd, char *salt, int iter, const char *pw) set_key_iv (gcry_cipher_hd_t chd, char *salt, int iter, const char *pw,
int keybytes)
{ {
unsigned char keybuf[24]; unsigned char keybuf[24];
int rc; int rc;
if (string_to_key (1, salt, iter, pw, 24, keybuf)) assert (keybytes == 5 || keybytes == 24);
if (string_to_key (1, salt, iter, pw, keybytes, keybuf))
return -1; return -1;
rc = gcry_cipher_setkey (chd, keybuf, 24); rc = gcry_cipher_setkey (chd, keybuf, keybytes);
if (rc) if (rc)
{ {
log_error ( "gcry_cipher_setkey failed: %s\n", gpg_strerror (rc)); log_error ( "gcry_cipher_setkey failed: %s\n", gpg_strerror (rc));
@ -331,18 +341,19 @@ set_key_iv (gcry_cipher_hd_t chd, char *salt, int iter, const char *pw)
static void static void
crypt_block (unsigned char *buffer, size_t length, char *salt, int iter, crypt_block (unsigned char *buffer, size_t length, char *salt, int iter,
const char *pw, int encrypt) const char *pw, int cipher_algo, int encrypt)
{ {
gcry_cipher_hd_t chd; gcry_cipher_hd_t chd;
int rc; int rc;
rc = gcry_cipher_open (&chd, GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CBC, 0); rc = gcry_cipher_open (&chd, cipher_algo, GCRY_CIPHER_MODE_CBC, 0);
if (rc) if (rc)
{ {
log_error ( "gcry_cipher_open failed: %s\n", gpg_strerror(-1)); log_error ( "gcry_cipher_open failed: %s\n", gpg_strerror(rc));
return; return;
} }
if (set_key_iv (chd, salt, iter, pw)) if (set_key_iv (chd, salt, iter, pw,
cipher_algo == GCRY_CIPHER_RFC2268_40? 5:24))
goto leave; goto leave;
rc = encrypt? gcry_cipher_encrypt (chd, buffer, length, NULL, 0) rc = encrypt? gcry_cipher_encrypt (chd, buffer, length, NULL, 0)
@ -354,12 +365,6 @@ crypt_block (unsigned char *buffer, size_t length, char *salt, int iter,
goto leave; goto leave;
} }
/* { */
/* FILE *fp = fopen("inner.der", "wb"); */
/* fwrite (buffer, 1, length, fp); */
/* fclose (fp); */
/* } */
leave: leave:
gcry_cipher_close (chd); gcry_cipher_close (chd);
} }
@ -369,12 +374,18 @@ crypt_block (unsigned char *buffer, size_t length, char *salt, int iter,
static int static int
parse_bag_encrypted_data (const unsigned char *buffer, size_t length, parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
int startoffset) int startoffset, const char *pw,
void (*certcb)(void*, const unsigned char*, size_t),
void *certcbarg)
{ {
struct tag_info ti; struct tag_info ti;
const unsigned char *p = buffer; const unsigned char *p = buffer;
size_t n = length; size_t n = length;
const char *where; const char *where;
char salt[8];
unsigned int iter;
unsigned char *plain = NULL;
where = "start"; where = "start";
if (parse_tag (&p, &n, &ti)) if (parse_tag (&p, &n, &ti))
@ -406,18 +417,17 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
p += DIM(oid_data); p += DIM(oid_data);
n -= DIM(oid_data); n -= DIM(oid_data);
#if 0 where = "bag.encryptedData.keyinfo";
where = "bag.encryptedData.keyinfo"
if (parse_tag (&p, &n, &ti)) if (parse_tag (&p, &n, &ti))
goto bailout; goto bailout;
if (ti.class || ti.tag != TAG_SEQUENCE) if (ti.class || ti.tag != TAG_SEQUENCE)
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 && ti.tag == TAG_OBJECT_ID
&& ti.length == DIM(oid_pbeWithSHAAnd40BitRC2_CBC) && ti.length == DIM(oid_pbeWithSHAAnd40BitRC2_CBC)
&& memcmp (p, oid_pbeWithSHAAnd40BitRC2_CBC, && !memcmp (p, oid_pbeWithSHAAnd40BitRC2_CBC,
DIM(oid_pbeWithSHAAnd40BitRC2_CBC))) DIM(oid_pbeWithSHAAnd40BitRC2_CBC)))
{ {
p += DIM(oid_pbeWithSHAAnd40BitRC2_CBC); p += DIM(oid_pbeWithSHAAnd40BitRC2_CBC);
n -= DIM(oid_pbeWithSHAAnd40BitRC2_CBC); n -= DIM(oid_pbeWithSHAAnd40BitRC2_CBC);
@ -451,16 +461,118 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length,
where = "rc2-ciphertext"; where = "rc2-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 != CONTEXT || ti.tag != 0 || !ti.length )
goto bailout; goto bailout;
log_info ("%lu bytes of RC2 encrypted text\n", ti.length); log_info ("%lu bytes of RC2 encrypted text\n", ti.length);
#endif
plain = gcry_malloc_secure (ti.length);
if (!plain)
{
log_error ("error allocating decryption buffer\n");
goto bailout;
}
memcpy (plain, p, ti.length);
crypt_block (plain, ti.length, salt, iter, pw, GCRY_CIPHER_RFC2268_40, 0);
n = ti.length;
startoffset = 0;
buffer = p = plain;
where = "outer.outer.seq";
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;
/* Loop over all certificates inside the bab. */
while (n)
{
where = "certbag.nextcert";
if (ti.class || ti.tag != TAG_SEQUENCE)
goto bailout;
where = "certbag.objectidentifier";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class || ti.tag != TAG_OBJECT_ID
|| ti.length != DIM(oid_pkcs_12_CertBag)
|| memcmp (p, oid_pkcs_12_CertBag,
DIM(oid_pkcs_12_CertBag)))
goto bailout;
p += DIM(oid_pkcs_12_CertBag);
n -= DIM(oid_pkcs_12_CertBag);
where = "certbag.before.certheader";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (ti.class != CONTEXT || ti.tag)
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_x509Certificate_for_pkcs_12)
|| memcmp (p, oid_x509Certificate_for_pkcs_12,
DIM(oid_x509Certificate_for_pkcs_12)))
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;
if (ti.class != CONTEXT || ti.tag)
goto bailout;
if (parse_tag (&p, &n, &ti))
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;
/* Ugly hack to cope with the padding: Forget about a rest of
sie les than the cipher's block length. */
if (n < 8)
n = 0;
/* Skip the optional SET with the pkcs12 cert attributes. */
if (n)
{
where = "certbag.attributes";
if (parse_tag (&p, &n, &ti))
goto bailout;
if (!ti.class && ti.tag == TAG_SEQUENCE)
; /* No attributes. */
else if (!ti.class && ti.tag == TAG_SET && !ti.ndef)
{ /* The optional SET. */
p += ti.length;
n -= ti.length;
if (n < 8)
n = 0;
if (n && parse_tag (&p, &n, &ti))
goto bailout;
}
else
goto bailout;
}
}
gcry_free (plain);
return 0; return 0;
bailout: bailout:
gcry_free (plain);
log_error ("encryptedData error at \"%s\", offset %u\n", log_error ("encryptedData error at \"%s\", offset %u\n",
where, (p - buffer)+startoffset); where, (p - buffer)+startoffset);
return -1; return -1;
@ -574,7 +686,7 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset,
goto bailout; goto bailout;
} }
memcpy (plain, p, ti.length); memcpy (plain, p, ti.length);
crypt_block (plain, ti.length, salt, iter, pw, 0); crypt_block (plain, ti.length, salt, iter, pw, GCRY_CIPHER_3DES, 0);
n = ti.length; n = ti.length;
startoffset = 0; startoffset = 0;
buffer = p = plain; buffer = p = plain;
@ -673,9 +785,12 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset,
secret key parameters. This is a very limited implementation in secret key parameters. This is a very limited implementation in
that it is only able to look for 3DES encoded encryptedData and that it is only able to look for 3DES encoded encryptedData and
tries to extract the first private key object it finds. In case of tries to extract the first private key object it finds. In case of
an error NULL is returned. */ an error NULL is returned. CERTCB and CERRTCBARG are used to pass
X.509 certificates back to the caller. */
gcry_mpi_t * gcry_mpi_t *
p12_parse (const unsigned char *buffer, size_t length, const char *pw) p12_parse (const unsigned char *buffer, size_t length, const char *pw,
void (*certcb)(void*, const unsigned char*, size_t),
void *certcbarg)
{ {
struct tag_info ti; struct tag_info ti;
const unsigned char *p = buffer; const unsigned char *p = buffer;
@ -751,7 +866,8 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw)
n -= DIM(oid_encryptedData); n -= DIM(oid_encryptedData);
len -= DIM(oid_encryptedData); len -= DIM(oid_encryptedData);
where = "bag.encryptedData"; where = "bag.encryptedData";
if (parse_bag_encrypted_data (p, n, (p - buffer))) if (parse_bag_encrypted_data (p, n, (p - buffer), pw,
certcb, certcbarg))
goto bailout; goto bailout;
} }
else if (ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_data) else if (ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_data)
@ -1034,7 +1150,7 @@ p12_build (gcry_mpi_t *kparms, const char *pw, size_t *r_length)
/* Encrypt it and prepend a lot of stupid things. */ /* Encrypt it and prepend a lot of stupid things. */
gcry_randomize (salt, 8, GCRY_STRONG_RANDOM); gcry_randomize (salt, 8, GCRY_STRONG_RANDOM);
crypt_block (plain, plainlen, salt, 1024, pw, 1); crypt_block (plain, plainlen, salt, 1024, pw, GCRY_CIPHER_3DES, 1);
/* the data goes into an octet string. */ /* the data goes into an octet string. */
needed = compute_tag_length (plainlen); needed = compute_tag_length (plainlen);
needed += plainlen; needed += plainlen;
@ -1128,14 +1244,21 @@ p12_build (gcry_mpi_t *kparms, const char *pw, size_t *r_length)
#ifdef TEST #ifdef TEST
static void
cert_cb (void *opaque, const unsigned char *cert, size_t certlen)
{
printf ("got a certificate of %u bytes length\n", certlen);
}
int int
main (int argc, char **argv) main (int argc, char **argv)
{ {
FILE *fp; FILE *fp;
struct stat st; struct stat st;
char *buf; unsigned char *buf;
size_t buflen; size_t buflen;
GcryMPI *result; gcry_mpi_t *result;
if (argc != 3) if (argc != 3)
{ {
@ -1168,23 +1291,23 @@ main (int argc, char **argv)
} }
fclose (fp); fclose (fp);
result = p12_parse (buf, buflen, argv[2]); result = p12_parse (buf, buflen, argv[2], cert_cb, NULL);
if (result) if (result)
{ {
int i, rc; int i, rc;
char *buf; unsigned char *tmpbuf;
for (i=0; result[i]; i++) for (i=0; result[i]; i++)
{ {
rc = gcry_mpi_aprint (GCRYMPI_FMT_HEX, (void**)&buf, rc = gcry_mpi_aprint (GCRYMPI_FMT_HEX, &tmpbuf,
NULL, result[i]); NULL, result[i]);
if (rc) if (rc)
printf ("%d: [error printing number: %s]\n", printf ("%d: [error printing number: %s]\n",
i, gpg_strerror (rc)); i, gpg_strerror (rc));
else else
{ {
printf ("%d: %s\n", i, buf); printf ("%d: %s\n", i, tmpbuf);
gcry_free (buf); gcry_free (tmpbuf);
} }
} }
} }
@ -1192,4 +1315,10 @@ main (int argc, char **argv)
return 0; return 0;
} }
/*
Local Variables:
compile-command: "gcc -Wall -O -g -DTEST=1 -o minip12 minip12.c ../jnlib/libjnlib.a -L /usr/local/lib -lgcrypt -lgpg-error"
End:
*/
#endif /* TEST */ #endif /* TEST */

View File

@ -24,7 +24,9 @@
#include <gcrypt.h> #include <gcrypt.h>
gcry_mpi_t *p12_parse (const unsigned char *buffer, size_t length, gcry_mpi_t *p12_parse (const unsigned char *buffer, size_t length,
const char *pw); const char *pw,
void (*certcb)(void*, const unsigned char*, size_t),
void *certcbarg);
unsigned char *p12_build (gcry_mpi_t *kparms, const char *pw, unsigned char *p12_build (gcry_mpi_t *kparms, const char *pw,
size_t *r_length); size_t *r_length);

View File

@ -563,6 +563,23 @@ rsa_key_check (struct rsa_secret_key_s *skey)
} }
/* A callback used by p12_parse to return a certificate. */
static void
import_p12_cert_cb (void *opaque, const unsigned char *cert, size_t certlen)
{
struct b64state state;
gpg_error_t err, err2;
err = b64enc_start (&state, stdout, "CERTIFICATE");
if (!err)
err = b64enc_write (&state, cert, certlen);
err2 = b64enc_finish (&state);
if (!err)
err = err2;
if (err)
log_error ("error writing armored certificate: %s\n", gpg_strerror (err));
}
static void static void
import_p12_file (const char *fname) import_p12_file (const char *fname)
{ {
@ -583,11 +600,12 @@ import_p12_file (const char *fname)
if (!buf) if (!buf)
return; return;
kparms = p12_parse (buf, buflen, get_passphrase ()); kparms = p12_parse (buf, buflen, get_passphrase (),
import_p12_cert_cb, NULL);
xfree (buf); xfree (buf);
if (!kparms) if (!kparms)
{ {
log_error ("error parsing or decrypting the PKCS-1 file\n"); log_error ("error parsing or decrypting the PKCS-12 file\n");
return; return;
} }
for (i=0; kparms[i]; i++) for (i=0; kparms[i]; i++)