diff options
author | Werner Koch <wk@gnupg.org> | 2004-02-19 17:26:32 +0100 |
---|---|---|
committer | Werner Koch <wk@gnupg.org> | 2004-02-19 17:26:32 +0100 |
commit | a1b487a17a084b3d8d652d4feabb7de2d67ad292 (patch) | |
tree | f0a4fbb66f6b7d89de99fc8e69c20dc96f7db0d4 /agent | |
parent | * acinclude.m4: Removed macros to detect gpg-error, libgcrypt, (diff) | |
download | gnupg2-a1b487a17a084b3d8d652d4feabb7de2d67ad292.tar.xz gnupg2-a1b487a17a084b3d8d652d4feabb7de2d67ad292.zip |
* protect-tool.c: New options --have-cert and --prompt.
(export_p12_file): Read a certificate from STDIN and pass it to
p12_build. Detect a keygrip and construct the filename in that
case. Unprotcet a key if needed. Print error messages for key
formats we can't handle.
(release_passphrase): New.
(get_passphrase): New arg PROMPTNO. Return the allocated
string. Changed all callers.
* minip12.c: Revamped the build part.
(p12_build): New args CERT and CERTLEN.
* simple-pwquery.c (agent_open): Don't mangle INFOSTR.
* export.c (export_p12, popen_protect_tool)
(gpgsm_p12_export): New.
* gpgsm.c (main): New command --export-secret-key-p12.
Diffstat (limited to 'agent')
-rw-r--r-- | agent/ChangeLog | 14 | ||||
-rw-r--r-- | agent/minip12.c | 559 | ||||
-rw-r--r-- | agent/minip12.h | 5 | ||||
-rw-r--r-- | agent/protect-tool.c | 218 |
4 files changed, 653 insertions, 143 deletions
diff --git a/agent/ChangeLog b/agent/ChangeLog index ffd48f59e..241a60964 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,17 @@ +2004-02-19 Werner Koch <wk@gnupg.org> + + * protect-tool.c: New options --have-cert and --prompt. + (export_p12_file): Read a certificate from STDIN and pass it to + p12_build. Detect a keygrip and construct the filename in that + case. Unprotcet a key if needed. Print error messages for key + formats we can't handle. + (release_passphrase): New. + (get_passphrase): New arg PROMPTNO. Return the allocated + string. Changed all callers. + + * minip12.c: Revamped the build part. + (p12_build): New args CERT and CERTLEN. + 2004-02-18 Werner Koch <wk@gnupg.org> * protect-tool.c (main): Setup the used character set. diff --git a/agent/minip12.c b/agent/minip12.c index 13b6aa3eb..e32a40de2 100644 --- a/agent/minip12.c +++ b/agent/minip12.c @@ -106,12 +106,19 @@ static unsigned char const oid_rsaEncryption[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 }; -static unsigned char const data_3desiter1024[30] = { +static unsigned char const data_3desiter2048[30] = { 0x30, 0x1C, 0x06, 0x0A, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0C, 0x01, 0x03, 0x30, 0x0E, 0x04, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0x02, 0x02, 0x04, 0x00 }; -#define DATA_3DESITER1024_SALT_OFF 18 + 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, + 0x04, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x02, 0x02, 0x08, 0x00 }; +#define DATA_RC2ITER2048_SALT_OFF 18 struct buffer_s @@ -346,17 +353,22 @@ crypt_block (unsigned char *buffer, size_t length, char *salt, int iter, if (rc) { log_error ( "gcry_cipher_open failed: %s\n", gpg_strerror(rc)); + wipememory (buffer, length); return; } if (set_key_iv (chd, salt, iter, pw, cipher_algo == GCRY_CIPHER_RFC2268_40? 5:24)) - goto leave; + { + wipememory (buffer, length); + goto leave; + } rc = encrypt? gcry_cipher_encrypt (chd, buffer, length, NULL, 0) : gcry_cipher_decrypt (chd, buffer, length, NULL, 0); if (rc) { + wipememory (buffer, length); log_error ( "en/de-crytion failed: %s\n", gpg_strerror (rc)); goto leave; } @@ -474,6 +486,13 @@ parse_bag_encrypted_data (const unsigned char *buffer, size_t length, startoffset = 0; buffer = p = plain; +/* { */ +/* FILE *fp = fopen ("tmp-rc2-plain.der", "wb"); */ +/* if (!fp || fwrite (p, n, 1, fp) != 1) */ +/* exit (2); */ +/* fclose (fp); */ +/* } */ + where = "outer.outer.seq"; if (parse_tag (&p, &n, &ti)) goto bailout; @@ -708,13 +727,6 @@ parse_bag_data (const unsigned char *buffer, size_t length, int startoffset, startoffset = 0; buffer = p = plain; -/* { */ -/* FILE *fp = fopen ("tmp-3des-plain.der", "wb"); */ -/* if (!fp || fwrite (p, n, 1, fp) != 1) */ -/* exit (2); */ -/* fclose (fp); */ -/* } */ - where = "decrypted-text"; if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE) goto bailout; @@ -970,37 +982,48 @@ create_final (struct buffer_s *sequences, size_t *r_length) { int i; size_t needed = 0; - size_t n, outseqlen, notsooutseqlen, out0taglen, octstrlen, inseqlen; + size_t len[8], n; unsigned char *result, *p; size_t resultlen; + /* 8 steps to create the pkcs#12 Krampf. */ + + /* 7. All the buffers. */ for (i=0; sequences[i].buffer; i++) needed += sequences[i].length; - /* This goes into a sequences. */ - inseqlen = needed; + + /* 6. This goes into a sequences. */ + len[6] = needed; n = compute_tag_length (needed); needed += n; - /* And encapsulate all in an octet string. */ - octstrlen = needed; + + /* 5. Encapsulate all in an octet string. */ + len[5] = needed; n = compute_tag_length (needed); needed += n; - /* And tag it with [0]. */ - out0taglen = needed; + + /* 4. And tag it with [0]. */ + len[4] = needed; n = compute_tag_length (needed); needed += n; - /* Prepend an data OID. */ + + /* 3. Prepend an data OID. */ needed += 2 + DIM (oid_data); - /* This all into a sequences. */ - notsooutseqlen = needed; + + /* 2. Put all into a sequences. */ + len[2] = needed; n = compute_tag_length (needed); needed += n; - /* Prepend the version integer 3. */ + + /* 1. Prepend the version integer 3. */ needed += 3; - /* And the final sequence. */ - outseqlen = needed; + + /* 0. And the final outer sequence. */ + len[0] = needed; n = compute_tag_length (needed); needed += n; + /* Allocate a buffer. */ result = gcry_malloc (needed); if (!result) { @@ -1009,25 +1032,32 @@ create_final (struct buffer_s *sequences, size_t *r_length) } p = result; - /* Store the very outer sequence. */ - p = store_tag_length (p, TAG_SEQUENCE, outseqlen); - /* Store the version integer 3. */ + /* 0. Store the very outer sequence. */ + p = store_tag_length (p, TAG_SEQUENCE, len[0]); + + /* 1. Store the version integer 3. */ *p++ = TAG_INTEGER; *p++ = 1; - *p++ = 3; - /* Store another sequence. */ - p = store_tag_length (p, TAG_SEQUENCE, notsooutseqlen); - /* Store the data OID. */ + *p++ = 3; + + /* 2. Store another sequence. */ + p = store_tag_length (p, TAG_SEQUENCE, len[2]); + + /* 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); - /* Next comes a context tag. */ - p = store_tag_length (p, 0xa0, out0taglen); - /* And an octet string. */ - p = store_tag_length (p, TAG_OCTET_STRING, octstrlen); - /* And the inner sequence. */ - p = store_tag_length (p, TAG_SEQUENCE, inseqlen); - /* And append all the buffers. */ + + /* 4. Next comes a context tag. */ + p = store_tag_length (p, 0xa0, len[4]); + + /* 5. And an octet string. */ + p = store_tag_length (p, TAG_OCTET_STRING, len[5]); + + /* 6. And the inner sequence. */ + p = store_tag_length (p, TAG_SEQUENCE, len[6]); + + /* 7. Append all the buffers. */ for (i=0; sequences[i].buffer; i++) { memcpy (p, sequences[i].buffer, sequences[i].length); @@ -1044,20 +1074,38 @@ create_final (struct buffer_s *sequences, size_t *r_length) } -/* Expect the RSA key parameters in KPARMS and a password in - PW. Create a PKCS structure from it and return it as well as the - length in R_LENGTH; return NULL in case of an error. */ -unsigned char * -p12_build (gcry_mpi_t *kparms, const char *pw, size_t *r_length) +/* Build a DER encoded SEQUENCE with the key: + + SEQUENCE { + INTEGER 0 + SEQUENCE { + OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1) + NULL + } + OCTET STRING, encapsulates { + SEQUENCE { + INTEGER 0 + INTEGER + INTEGER + INTEGER + INTEGER + INTEGER + INTEGER + INTEGER + INTEGER + } + } + } +*/ + +static unsigned char * +build_key_sequence (gcry_mpi_t *kparms, size_t *r_length) { int rc, i; size_t needed, n; - unsigned char *plain, *p, *cipher; - size_t plainlen, cipherlen; + unsigned char *plain, *p; + size_t plainlen; size_t outseqlen, oidseqlen, octstrlen, inseqlen; - size_t out0taglen, in0taglen, outoctstrlen; - size_t aseq1len, aseq2len, aseq3len; - char salt[8]; needed = 3; /* The version(?) integer of value 0. */ for (i=0; kparms[i]; i++) @@ -1165,105 +1213,380 @@ p12_build (gcry_mpi_t *kparms, const char *pw, size_t *r_length) for (;(plainlen % 8); plainlen++) *p++ = n; -/* { */ -/* FILE *fp = fopen("inner-out.der", "wb"); */ -/* fwrite (plain, 1, plainlen, fp); */ -/* fclose (fp); */ -/* } */ + *r_length = plainlen; + return plain; +} + + + +static unsigned char * +build_key_bag (unsigned char *buffer, size_t buflen, char *salt, + size_t *r_length) +{ + size_t len[11], needed; + unsigned char *p, *keybag; + size_t keybaglen; + + /* Walk 11 steps down to collect the info: */ + + /* 10. The data goes into an octet string. */ + needed = compute_tag_length (buflen); + needed += buflen; + /* 9. Prepend the algorithm identifier. */ + needed += DIM (data_3desiter2048); - /* Encrypt it and prepend a lot of stupid things. */ - gcry_randomize (salt, 8, GCRY_STRONG_RANDOM); - 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; - /* we prepend the the algorithm identifier (we use a pre-encoded one)*/ - needed += DIM (data_3desiter1024); - /* we put a sequence around. */ - aseq3len = needed; + /* 8. Put a sequence around. */ + len[8] = needed; needed += compute_tag_length (needed); - /* Prepend it with a [0] tag. */ - in0taglen = needed; + + /* 7. Prepend a [0] tag. */ + len[7] = needed; needed += compute_tag_length (needed); - /* Prepend that shroudedKeyBag OID. */ + + /* 6. Prepend the shroudedKeyBag OID. */ needed += 2 + DIM (oid_pkcs_12_pkcs_8ShroudedKeyBag); - /* Put it all into two sequence. */ - aseq2len = needed; + + /* 5+4. Put all into two sequences. */ + len[5] = needed; needed += compute_tag_length ( needed); - aseq1len = needed; + len[4] = needed; needed += compute_tag_length (needed); - /* This all goes into an octet string. */ - outoctstrlen = needed; + + /* 3. This all goes into an octet string. */ + len[3] = needed; needed += compute_tag_length (needed); - /* Prepend it with a [0] tag. */ - out0taglen = needed; + + /* 2. Prepend another [0] tag. */ + len[2] = needed; needed += compute_tag_length (needed); - /* Prepend the data OID. */ + + /* 1. Prepend the data OID. */ needed += 2 + DIM (oid_data); - /* And a sequence. */ - outseqlen = needed; + + /* 0. Prepend another sequence. */ + len[0] = needed; needed += compute_tag_length (needed); - cipher = gcry_malloc (needed); - if (!cipher) + /* Now that we have all length information, allocate a buffer. */ + p = keybag = gcry_malloc (needed); + if (!keybag) { log_error ("error allocating buffer\n"); - gcry_free (plain); return NULL; } - p = cipher; - /* Store the first sequence. */ - p = store_tag_length (p, TAG_SEQUENCE, outseqlen); - /* Store the data OID. */ + + /* Walk 11 steps up to store the data. */ + + /* 0. Store the first sequence. */ + p = store_tag_length (p, TAG_SEQUENCE, len[0]); + + /* 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); - /* Next comes a context tag. */ - p = store_tag_length (p, 0xa0, out0taglen); - /* And an octet string. */ - p = store_tag_length (p, TAG_OCTET_STRING, outoctstrlen); - /* Two sequences. */ - p = store_tag_length (p, TAG_SEQUENCE, aseq1len); - p = store_tag_length (p, TAG_SEQUENCE, aseq2len); - /* Store the shroudedKeyBag OID. */ + + /* 2. Store a [0] tag. */ + p = store_tag_length (p, 0xa0, len[2]); + + /* 3. And an octet string. */ + p = store_tag_length (p, TAG_OCTET_STRING, len[3]); + + /* 4+5. Two sequences. */ + p = store_tag_length (p, TAG_SEQUENCE, len[4]); + p = store_tag_length (p, TAG_SEQUENCE, len[5]); + + /* 6. Store the shroudedKeyBag OID. */ 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); - /* Next comes a context tag. */ - p = store_tag_length (p, 0xa0, in0taglen); - /* And a sequence. */ - p = store_tag_length (p, TAG_SEQUENCE, aseq3len); - /* Now for the pre-encoded algorithm indentifier and the salt. */ - memcpy (p, data_3desiter1024, DIM (data_3desiter1024)); - memcpy (p + DATA_3DESITER1024_SALT_OFF, salt, 8); - p += DIM (data_3desiter1024); - /* And finally the octet string with the encrypted data. */ - p = store_tag_length (p, TAG_OCTET_STRING, plainlen); - memcpy (p, plain, plainlen); - p += plainlen; - cipherlen = p - cipher; + + /* 7. Store a [0] tag. */ + p = store_tag_length (p, 0xa0, len[7]); + + /* 8. Store a sequence. */ + p = store_tag_length (p, TAG_SEQUENCE, len[8]); + + /* 9. Now for the pre-encoded algorithm identifier and the salt. */ + memcpy (p, data_3desiter2048, DIM (data_3desiter2048)); + memcpy (p + DATA_3DESITER2048_SALT_OFF, salt, 8); + p += DIM (data_3desiter2048); + + /* 10. And finally the octet string with the encrypted data. */ + p = store_tag_length (p, TAG_OCTET_STRING, buflen); + memcpy (p, buffer, buflen); + p += buflen; + keybaglen = p - keybag; - if (needed != cipherlen) - log_debug ("length mismatch: %u, %u\n", needed, cipherlen); - gcry_free (plain); + if (needed != keybaglen) + log_debug ("length mismatch: %u, %u\n", needed, keybaglen); + + *r_length = keybaglen; + return keybag; +} + + +static unsigned char * +build_cert_bag (unsigned char *buffer, size_t buflen, char *salt, + size_t *r_length) +{ + size_t len[9], needed; + unsigned char *p, *certbag; + size_t certbaglen; + + /* Walk 9 steps down to collect the info: */ + + /* 8. The data goes into an octet string. */ + needed = compute_tag_length (buflen); + needed += buflen; + + /* 7. The algorithm identifier. */ + needed += DIM (data_rc2iter2048); + + /* 6. The data OID. */ + needed += 2 + DIM (oid_data); + + /* 5. A sequence. */ + len[5] = needed; + needed += compute_tag_length ( needed); + + /* 4. An integer. */ + needed += 3; + + /* 3. A sequence. */ + len[3] = needed; + needed += compute_tag_length (needed); + + /* 2. A [0] tag. */ + len[2] = needed; + needed += compute_tag_length (needed); + + /* 1. The encryptedData OID. */ + needed += 2 + DIM (oid_encryptedData); + + /* 0. The first sequence. */ + len[0] = needed; + needed += compute_tag_length (needed); + + /* Now that we have all length information, allocate a buffer. */ + p = certbag = gcry_malloc (needed); + if (!certbag) + { + log_error ("error allocating buffer\n"); + return NULL; + } + + /* Walk 9 steps up to store the data. */ + + /* 0. Store the first sequence. */ + p = store_tag_length (p, TAG_SEQUENCE, len[0]); + + /* 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); + + /* 2. Store a [0] tag. */ + p = store_tag_length (p, 0xa0, len[2]); + + /* 3. Store a sequence. */ + p = store_tag_length (p, TAG_SEQUENCE, len[3]); + + /* 4. Store the integer 0. */ + *p++ = TAG_INTEGER; + *p++ = 1; + *p++ = 0; + + /* 5. Store a sequence. */ + p = store_tag_length (p, TAG_SEQUENCE, len[5]); + + /* 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); + + /* 7. Now for the pre-encoded algorithm identifier and the salt. */ + memcpy (p, data_rc2iter2048, DIM (data_rc2iter2048)); + memcpy (p + DATA_RC2ITER2048_SALT_OFF, salt, 8); + p += DIM (data_rc2iter2048); + + /* 8. And finally the [0] tag with the encrypted data. */ + p = store_tag_length (p, 0xa0, buflen); + memcpy (p, buffer, buflen); + p += buflen; + certbaglen = p - certbag; + + if (needed != certbaglen) + log_debug ("length mismatch: %u, %u\n", needed, certbaglen); + + *r_length = certbaglen; + return certbag; +} + + +static unsigned char * +build_cert_sequence (unsigned char *buffer, size_t buflen, size_t *r_length) +{ + size_t len[8], needed, n; + unsigned char *p, *certseq; + size_t certseqlen; + + /* Walk 8 steps down to collect the info: */ + + /* 7. The data goes into an octet string. */ + needed = compute_tag_length (buflen); + needed += buflen; + + /* 6. A [0] tag. */ + len[6] = needed; + needed += compute_tag_length (needed); + + /* 5. An OID. */ + needed += 2 + DIM (oid_x509Certificate_for_pkcs_12); + + /* 4. A sequence. */ + len[4] = needed; + needed += compute_tag_length (needed); + + /* 3. A [0] tag. */ + len[3] = needed; + needed += compute_tag_length (needed); + + /* 2. An OID. */ + needed += 2 + DIM (oid_pkcs_12_CertBag); + + /* 1. A sequence. */ + len[1] = needed; + needed += compute_tag_length (needed); + + /* 0. The first sequence. */ + len[0] = needed; + needed += compute_tag_length (needed); + + /* Now that we have all length information, allocate a buffer. */ + p = certseq = gcry_malloc (needed + 8 /*(for padding)*/); + if (!certseq) + { + log_error ("error allocating buffer\n"); + return NULL; + } + + /* Walk 8 steps up to store the data. */ + + /* 0. Store the first sequence. */ + p = store_tag_length (p, TAG_SEQUENCE, len[0]); + + /* 1. Store the second sequence. */ + p = store_tag_length (p, TAG_SEQUENCE, len[1]); + + /* 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); + + /* 3. Store a [0] tag. */ + p = store_tag_length (p, 0xa0, len[3]); + + /* 4. Store a sequence. */ + p = store_tag_length (p, TAG_SEQUENCE, len[4]); + + /* 5. Store the x509Certificate OID. */ + 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); + + /* 6. Store a [0] tag. */ + p = store_tag_length (p, 0xa0, len[6]); + + /* 7. And finally the octet string with the actual certificate. */ + p = store_tag_length (p, TAG_OCTET_STRING, buflen); + memcpy (p, buffer, buflen); + p += buflen; + certseqlen = p - certseq; + + if (needed != certseqlen) + log_debug ("length mismatch: %u, %u\n", needed, certseqlen); + + /* Append some pad characters; we already allocated extra space. */ + n = 8 - certseqlen % 8; + for (;(certseqlen % 8); certseqlen++) + *p++ = n; + + *r_length = certseqlen; + return certseq; +} + + +/* Expect the RSA key parameters in KPARMS and a password in + PW. Create a PKCS structure from it and return it as well as the + length in R_LENGTH; return NULL in case of an error. */ +unsigned char * +p12_build (gcry_mpi_t *kparms, unsigned char *cert, size_t certlen, + const char *pw, size_t *r_length) +{ + unsigned char *buffer; + size_t n, buflen; + char salt[8]; + struct buffer_s seqlist[2]; + int seqlistidx = 0; + + if (cert && certlen) + { + /* Encode the certificate. */ + buffer = build_cert_sequence (cert, certlen, &buflen); + if (!buffer) + goto failure; + + /* Encrypt it. */ + gcry_randomize (salt, 8, GCRY_STRONG_RANDOM); + crypt_block (buffer, buflen, salt, 2048, 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; + gcry_free (buffer); + buffer = NULL; + if (!seqlist[seqlistidx].buffer) + goto failure; + seqlistidx++; + } + + if (kparms) + { + /* Encode the key. */ + buffer = build_key_sequence (kparms, &buflen); + if (!buffer) + goto failure; + + /* Encrypt it. */ + gcry_randomize (salt, 8, GCRY_STRONG_RANDOM); + crypt_block (buffer, buflen, salt, 2048, pw, GCRY_CIPHER_3DES, 1); + + /* Encode the encrypted stuff into a bag. */ + seqlist[seqlistidx].buffer = build_key_bag (buffer, buflen, salt, &n); + seqlist[seqlistidx].length = n; + gcry_free (buffer); + buffer = NULL; + if (!seqlist[seqlistidx].buffer) + goto failure; + seqlistidx++; + } - { - struct buffer_s seqlist[2]; + seqlist[seqlistidx].buffer = NULL; + seqlist[seqlistidx].length = 0; - seqlist[0].buffer = cipher; - seqlist[0].length = cipherlen; - seqlist[1].buffer = NULL; - seqlist[1].length = 0; + buffer = create_final (seqlist, &buflen); - cipher = create_final (seqlist, &cipherlen); - gcry_free (seqlist[0].buffer); - } + failure: + for ( ; seqlistidx; seqlistidx--) + gcry_free (seqlist[seqlistidx].buffer); - *r_length = cipherlen; - return cipher; + *r_length = buffer? buflen : 0; + return buffer; } diff --git a/agent/minip12.h b/agent/minip12.h index 61a597926..2fbb490d7 100644 --- a/agent/minip12.h +++ b/agent/minip12.h @@ -28,8 +28,9 @@ gcry_mpi_t *p12_parse (const unsigned char *buffer, size_t length, 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); +unsigned char *p12_build (gcry_mpi_t *kparms, + unsigned char *cert, size_t certlen, + const char *pw, size_t *r_length); #endif /*MINIP12_H*/ diff --git a/agent/protect-tool.c b/agent/protect-tool.c index 59f6c6711..1bc7144be 100644 --- a/agent/protect-tool.c +++ b/agent/protect-tool.c @@ -1,5 +1,5 @@ /* protect-tool.c - A tool to test the secret key protection - * Copyright (C) 2002, 2003 Free Software Foundation, Inc. + * Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -54,8 +54,10 @@ enum cmd_and_opt_values oP12Export, oStore, oForce, + oHaveCert, oNoFailOnExist, oHomedir, + oPrompt, aTest }; @@ -75,9 +77,12 @@ static int opt_armor; static int opt_store; static int opt_force; static int opt_no_fail_on_exist; -static const char *passphrase; +static int opt_have_cert; +static const char *opt_passphrase; +static char *opt_prompt; -static const char *get_passphrase (void); +static char *get_passphrase (int promptno); +static void release_passphrase (char *pw); static int store_private_key (const unsigned char *grip, const void *buffer, size_t length, int force); @@ -97,10 +102,12 @@ static ARGPARSE_OPTS opts[] = { { oP12Import, "p12-import", 256, "import a PKCS-12 encoded private key"}, { oP12Export, "p12-export", 256, "export a private key PKCS-12 encoded"}, + { oHaveCert, "have-cert", 0, "certificate to export provided on STDIN"}, { oStore, "store", 0, "store the created key in the appropriate place"}, { oForce, "force", 0, "force overwriting"}, { oNoFailOnExist, "no-fail-on-exist", 0, "@" }, { oHomedir, "homedir", 2, "@" }, + { oPrompt, "prompt", 2, "|ESCSTRING|use ESCSTRING as prompt in pinentry"}, {0} }; @@ -328,12 +335,15 @@ read_and_protect (const char *fname) unsigned char *key; unsigned char *result; size_t resultlen; + char *pw; key = read_key (fname); if (!key) return; - rc = agent_protect (key, get_passphrase (), &result, &resultlen); + pw = get_passphrase (1); + rc = agent_protect (key, pw, &result, &resultlen); + release_passphrase (pw); xfree (key); if (rc) { @@ -363,12 +373,14 @@ read_and_unprotect (const char *fname) unsigned char *key; unsigned char *result; size_t resultlen; + char *pw; key = read_key (fname); if (!key) return; - rc = agent_unprotect (key, get_passphrase (), &result, &resultlen); + rc = agent_unprotect (key, (pw=get_passphrase (1)), &result, &resultlen); + release_passphrase (pw); xfree (key); if (rc) { @@ -632,6 +644,7 @@ import_p12_file (const char *fname) gcry_sexp_t s_key; unsigned char *key; unsigned char grip[20]; + char *pw; /* fixme: we should release some stuff on error */ @@ -639,8 +652,9 @@ import_p12_file (const char *fname) if (!buf) return; - kparms = p12_parse (buf, buflen, get_passphrase (), + kparms = p12_parse (buf, buflen, (pw=get_passphrase (0)), import_p12_cert_cb, NULL); + release_passphrase (pw); xfree (buf); if (!kparms) { @@ -714,7 +728,8 @@ import_p12_file (const char *fname) gcry_sexp_release (s_key); - rc = agent_protect (key, get_passphrase (), &result, &resultlen); + rc = agent_protect (key, (pw=get_passphrase (0)), &result, &resultlen); + release_passphrase (pw); xfree (key); if (rc) { @@ -797,27 +812,113 @@ sexp_to_kparms (gcry_sexp_t sexp) } +/* Check whether STRING is a KEYGRIP, i.e has the correct length and + does only consist of uppercase hex characters. */ +static int +is_keygrip (const char *string) +{ + int i; + + for(i=0; string[i] && i < 41; i++) + if (!strchr("01234567890ABCDEF", string[i])) + return 0; + return i == 40; +} static void export_p12_file (const char *fname) { + int rc; gcry_mpi_t kparms[9], *kp; unsigned char *key; size_t keylen; gcry_sexp_t private; struct rsa_secret_key_s sk; int i; + unsigned char *cert = NULL; + size_t certlen = 0; + int keytype; + size_t keylen_for_wipe = 0; + char *pw; + + if ( is_keygrip (fname) ) + { + char hexgrip[40+4+1]; + char *p; - key = read_key (fname); + assert (strlen(fname) == 40); + strcpy (stpcpy (hexgrip, fname), ".key"); + + p = make_filename (opt_homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL); + key = read_key (p); + xfree (p); + } + else + key = read_key (fname); + if (!key) return; + keytype = agent_private_key_type (key); + if (keytype == PRIVATE_KEY_PROTECTED) + { + unsigned char *tmpkey; + size_t tmplen; + + rc = agent_unprotect (key, (pw=get_passphrase (1)), &tmpkey, &tmplen); + release_passphrase (pw); + if (rc) + { + log_error ("unprotecting key `%s' failed: %s\n", + fname, gpg_strerror (rc)); + xfree (key); + return; + } + xfree (key); + key = tmpkey; + keylen_for_wipe = tmplen; + + keytype = agent_private_key_type (key); + } + + if (keytype == PRIVATE_KEY_SHADOWED) + { + log_error ("`%s' is a shadowed private key - can't export it\n", fname); + wipememory (key, keylen_for_wipe); + xfree (key); + return; + } + else if (keytype != PRIVATE_KEY_CLEAR) + { + log_error ("\%s' is not a private key\n", fname); + wipememory (key, keylen_for_wipe); + xfree (key); + return; + } + + + if (opt_have_cert) + { + cert = read_file ("-", &certlen); + if (!cert) + { + wipememory (key, keylen_for_wipe); + xfree (key); + return; + } + } + + if (gcry_sexp_new (&private, key, 0, 0)) { log_error ("gcry_sexp_new failed\n"); + wipememory (key, keylen_for_wipe); + xfree (key); + xfree (cert); return; } + wipememory (key, keylen_for_wipe); xfree (key); kp = sexp_to_kparms (private); @@ -825,6 +926,7 @@ export_p12_file (const char *fname) if (!kp) { log_error ("error converting key parameters\n"); + xfree (cert); return; } sk.n = kp[0]; @@ -850,7 +952,9 @@ export_p12_file (const char *fname) kparms[7] = sk.u; kparms[8] = NULL; - key = p12_build (kparms, get_passphrase (), &keylen); + key = p12_build (kparms, cert, certlen, (pw=get_passphrase (0)), &keylen); + release_passphrase (pw); + xfree (cert); for (i=0; i < 8; i++) gcry_mpi_release (kparms[i]); if (!key) @@ -861,6 +965,54 @@ export_p12_file (const char *fname) } + +/* Do the percent and plus/space unescaping in place and return the + length of the valid buffer. */ +static size_t +percent_plus_unescape (unsigned char *string) +{ + unsigned char *p = string; + size_t n = 0; + + while (*string) + { + if (*string == '%' && string[1] && string[2]) + { + string++; + *p++ = xtoi_2 (string); + n++; + string+= 2; + } + else if (*string == '+') + { + *p++ = ' '; + n++; + string++; + } + else + { + *p++ = *string++; + n++; + } + } + + return n; +} + +/* Remove percent and plus escaping and make sure that the reuslt is a + string. This is done in place. Returns STRING. */ +static char * +percent_plus_unescape_string (char *string) +{ + unsigned char *p = string; + size_t n; + + n = percent_plus_unescape (p); + p[n] = 0; + + return string; +} + int main (int argc, char **argv ) @@ -918,11 +1070,13 @@ main (int argc, char **argv ) case oP12Import: cmd = oP12Import; break; case oP12Export: cmd = oP12Export; break; - case oPassphrase: passphrase = pargs.r.ret_str; break; + case oPassphrase: opt_passphrase = pargs.r.ret_str; break; case oStore: opt_store = 1; break; case oForce: opt_force = 1; break; case oNoFailOnExist: opt_no_fail_on_exist = 1; break; - + case oHaveCert: opt_have_cert = 1; break; + case oPrompt: opt_prompt = pargs.r.ret_str; break; + default : pargs.err = 2; break; } } @@ -935,6 +1089,9 @@ main (int argc, char **argv ) else if (argc > 1) usage (1); + if (opt_prompt) + opt_prompt = percent_plus_unescape_string (xstrdup (opt_prompt)); + if (cmd == oProtect) read_and_protect (fname); else if (cmd == oUnprotect) @@ -965,21 +1122,27 @@ agent_exit (int rc) /* Return the passphrase string and ask the agent if it has not been - set from the command line. */ -static const char * -get_passphrase (void) + set from the command line PROMPTNO select the prompt to display: + 0 = default + 1 = taken from the option --prompt +*/ +static char * +get_passphrase (int promptno) { char *pw; int err; + const char *desc; + + if (opt_passphrase) + return xstrdup (opt_passphrase); - if (passphrase) - return passphrase; + if (promptno == 1 && opt_prompt) + desc = opt_prompt; + else + desc = _("Please enter the passphrase or the PIN\n" + "needed to complete this operation."); - pw = simple_pwquery (NULL,NULL, - _("Enter passphrase:"), - _("Please enter the passphrase or the PIN\n" - "needed to complete this operation."), - &err); + pw = simple_pwquery (NULL,NULL, _("Passphrase:"), desc, &err); if (!pw) { if (err) @@ -988,10 +1151,19 @@ get_passphrase (void) log_info ("cancelled\n"); agent_exit (0); } - passphrase = pw; - return passphrase; + + return pw; } +static void +release_passphrase (char *pw) +{ + if (pw) + { + wipememory (pw, strlen (pw)); + xfree (pw); + } +} static int store_private_key (const unsigned char *grip, |