aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--agent/ChangeLog12
-rw-r--r--agent/minip12.c199
-rw-r--r--agent/minip12.h4
-rw-r--r--agent/protect-tool.c22
4 files changed, 199 insertions, 38 deletions
diff --git a/agent/ChangeLog b/agent/ChangeLog
index 01a3c90f5..1589d4f9a 100644
--- a/agent/ChangeLog
+++ b/agent/ChangeLog
@@ -1,3 +1,15 @@
+2004-02-10 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
+
+ * minip12.c (crypt_block): Add arg CIPHER_ALGO; changed all callers.
+ (set_key_iv): Add arg KEYBYTES; changed caller.
+
2004-02-03 Werner Koch <[email protected]>
* findkey.c (agent_key_from_file): Extra paranoid wipe.
diff --git a/agent/minip12.c b/agent/minip12.c
index 753b5022f..af39ae0fd 100644
--- a/agent/minip12.c
+++ b/agent/minip12.c
@@ -1,5 +1,5 @@
/* 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.
*
@@ -27,7 +27,12 @@
#include <assert.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
#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 };
static unsigned char const oid_pbeWithSHAAnd40BitRC2_CBC[10] = {
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] = {
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
-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];
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;
- rc = gcry_cipher_setkey (chd, keybuf, 24);
+ rc = gcry_cipher_setkey (chd, keybuf, keybytes);
if (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
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;
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)
{
- log_error ( "gcry_cipher_open failed: %s\n", gpg_strerror(-1));
+ log_error ( "gcry_cipher_open failed: %s\n", gpg_strerror(rc));
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;
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;
}
-/* { */
-/* FILE *fp = fopen("inner.der", "wb"); */
-/* fwrite (buffer, 1, length, fp); */
-/* fclose (fp); */
-/* } */
-
leave:
gcry_cipher_close (chd);
}
@@ -369,12 +374,18 @@ crypt_block (unsigned char *buffer, size_t length, char *salt, int iter,
static int
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;
const unsigned char *p = buffer;
size_t n = length;
const char *where;
+ char salt[8];
+ unsigned int iter;
+ unsigned char *plain = NULL;
+
where = "start";
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);
n -= DIM(oid_data);
-#if 0
- where = "bag.encryptedData.keyinfo"
+ where = "bag.encryptedData.keyinfo";
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
+ if (!ti.class && ti.tag == TAG_OBJECT_ID
&& ti.length == DIM(oid_pbeWithSHAAnd40BitRC2_CBC)
- && memcmp (p, oid_pbeWithSHAAnd40BitRC2_CBC,
- DIM(oid_pbeWithSHAAnd40BitRC2_CBC)))
+ && !memcmp (p, oid_pbeWithSHAAnd40BitRC2_CBC,
+ DIM(oid_pbeWithSHAAnd40BitRC2_CBC)))
{
p += 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";
if (parse_tag (&p, &n, &ti))
goto bailout;
- if (ti.class || ti.tag != TAG_OCTET_STRING || !ti.length )
+ if (ti.class != CONTEXT || ti.tag != 0 || !ti.length )
goto bailout;
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;
bailout:
+ gcry_free (plain);
log_error ("encryptedData error at \"%s\", offset %u\n",
where, (p - buffer)+startoffset);
return -1;
@@ -574,7 +686,7 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset,
goto bailout;
}
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;
startoffset = 0;
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
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
- 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 *
-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;
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);
len -= DIM(oid_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;
}
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. */
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. */
needed = compute_tag_length (plainlen);
needed += plainlen;
@@ -1128,14 +1244,21 @@ p12_build (gcry_mpi_t *kparms, const char *pw, size_t *r_length)
#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
main (int argc, char **argv)
{
FILE *fp;
struct stat st;
- char *buf;
+ unsigned char *buf;
size_t buflen;
- GcryMPI *result;
+ gcry_mpi_t *result;
if (argc != 3)
{
@@ -1168,23 +1291,23 @@ main (int argc, char **argv)
}
fclose (fp);
- result = p12_parse (buf, buflen, argv[2]);
+ result = p12_parse (buf, buflen, argv[2], cert_cb, NULL);
if (result)
{
int i, rc;
- char *buf;
+ unsigned char *tmpbuf;
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]);
if (rc)
printf ("%d: [error printing number: %s]\n",
i, gpg_strerror (rc));
else
{
- printf ("%d: %s\n", i, buf);
- gcry_free (buf);
+ printf ("%d: %s\n", i, tmpbuf);
+ gcry_free (tmpbuf);
}
}
}
@@ -1192,4 +1315,10 @@ main (int argc, char **argv)
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 */
diff --git a/agent/minip12.h b/agent/minip12.h
index 122215549..61a597926 100644
--- a/agent/minip12.h
+++ b/agent/minip12.h
@@ -24,7 +24,9 @@
#include <gcrypt.h>
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,
size_t *r_length);
diff --git a/agent/protect-tool.c b/agent/protect-tool.c
index d4f945a94..2d1df4d7c 100644
--- a/agent/protect-tool.c
+++ b/agent/protect-tool.c
@@ -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
import_p12_file (const char *fname)
{
@@ -583,11 +600,12 @@ import_p12_file (const char *fname)
if (!buf)
return;
- kparms = p12_parse (buf, buflen, get_passphrase ());
+ kparms = p12_parse (buf, buflen, get_passphrase (),
+ import_p12_cert_cb, NULL);
xfree (buf);
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;
}
for (i=0; kparms[i]; i++)