diff --git a/sm/decrypt.c b/sm/decrypt.c index bd6f61038..bac63e1bc 100644 --- a/sm/decrypt.c +++ b/sm/decrypt.c @@ -1,1425 +1,1444 @@ /* decrypt.c - Decrypt a message * Copyright (C) 2001, 2003, 2010 Free Software Foundation, Inc. * Copyright (C) 2001-2019 Werner Koch * Copyright (C) 2015-2021 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 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 . * SPDX-License-Identifier: GPL-3.0-or-later */ #include #include #include #include #include #include #include #include "gpgsm.h" #include #include #include "keydb.h" #include "../common/i18n.h" #include "../common/tlv.h" #include "../common/compliance.h" struct decrypt_filter_parm_s { int algo; int mode; int blklen; gcry_cipher_hd_t hd; char iv[16]; size_t ivlen; int any_data; /* did we push anything through the filter at all? */ unsigned char lastblock[16]; /* to strip the padding we have to keep this one */ char helpblock[16]; /* needed because there is no block buffering in libgcrypt (yet) */ int helpblocklen; int is_de_vs; /* Helper to track CO_DE_VS state. */ }; /* Return the hash algorithm's algo id from its name given in the * non-null termnated string in (buffer,buflen). Returns 0 on failure * or if the algo is not known. */ static char * string_from_gcry_buffer (gcry_buffer_t *buffer) { char *string; string = xtrymalloc (buffer->len + 1); if (!string) return NULL; memcpy (string, buffer->data, buffer->len); string[buffer->len] = 0; return string; } /* Helper to construct and hash the * ECC-CMS-SharedInfo ::= SEQUENCE { * keyInfo AlgorithmIdentifier, * entityUInfo [0] EXPLICIT OCTET STRING OPTIONAL, * suppPubInfo [2] EXPLICIT OCTET STRING } * as described in RFC-5753, 7.2. */ static gpg_error_t hash_ecc_cms_shared_info (gcry_md_hd_t hash_hd, const char *wrap_algo_str, unsigned int keylen, const void *ukm, unsigned int ukmlen) { gpg_error_t err; void *p; unsigned char *oid; size_t n, oidlen, toidlen, tkeyinfo, tukmlen, tsupppubinfo; unsigned char keylenbuf[6]; membuf_t mb = MEMBUF_ZERO; err = ksba_oid_from_str (wrap_algo_str, &oid, &oidlen); if (err) return err; toidlen = get_tlv_length (CLASS_UNIVERSAL, TAG_OBJECT_ID, 0, oidlen); tkeyinfo = get_tlv_length (CLASS_UNIVERSAL, TAG_SEQUENCE, 1, toidlen); tukmlen = ukm? get_tlv_length (CLASS_CONTEXT, 0, 1, ukmlen) : 0; keylen *= 8; keylenbuf[0] = TAG_OCTET_STRING; keylenbuf[1] = 4; keylenbuf[2] = (keylen >> 24); keylenbuf[3] = (keylen >> 16); keylenbuf[4] = (keylen >> 8); keylenbuf[5] = keylen; tsupppubinfo = get_tlv_length (CLASS_CONTEXT, 2, 1, sizeof keylenbuf); put_tlv_to_membuf (&mb, CLASS_UNIVERSAL, TAG_SEQUENCE, 1, tkeyinfo + tukmlen + tsupppubinfo); put_tlv_to_membuf (&mb, CLASS_UNIVERSAL, TAG_SEQUENCE, 1, toidlen); put_tlv_to_membuf (&mb, CLASS_UNIVERSAL, TAG_OBJECT_ID, 0, oidlen); put_membuf (&mb, oid, oidlen); ksba_free (oid); if (ukm) { put_tlv_to_membuf (&mb, CLASS_CONTEXT, 0, 1, ukmlen); put_membuf (&mb, ukm, ukmlen); } put_tlv_to_membuf (&mb, CLASS_CONTEXT, 2, 1, sizeof keylenbuf); put_membuf (&mb, keylenbuf, sizeof keylenbuf); p = get_membuf (&mb, &n); if (!p) return gpg_error_from_syserror (); gcry_md_write (hash_hd, p, n); xfree (p); return 0; } /* Derive a KEK (key wrapping key) using (SECRET,SECRETLEN), an * optional (UKM,ULMLEN), the wrap algorithm WRAP_ALGO_STR in decimal * dotted form, and the hash algorithm HASH_ALGO. On success a key of * length KEYLEN is stored at KEY. */ gpg_error_t ecdh_derive_kek (unsigned char *key, unsigned int keylen, int hash_algo, const char *wrap_algo_str, const void *secret, unsigned int secretlen, const void *ukm, unsigned int ukmlen) { gpg_error_t err = 0; unsigned int hashlen; gcry_md_hd_t hash_hd; unsigned char counter; unsigned int n, ncopy; hashlen = gcry_md_get_algo_dlen (hash_algo); if (!hashlen) return gpg_error (GPG_ERR_INV_ARG); err = gcry_md_open (&hash_hd, hash_algo, 0); if (err) return err; /* According to SEC1 3.6.1 we should check that * SECRETLEN + UKMLEN + 4 < maxhashlen * However, we have no practical limit on the hash length and thus * there is no point in checking this. The second check that * KEYLEN < hashlen*(2^32-1) * is obviously also not needed. */ for (n=0, counter=1; n < keylen; counter++) { if (counter > 1) gcry_md_reset (hash_hd); gcry_md_write (hash_hd, secret, secretlen); gcry_md_write (hash_hd, "\x00\x00\x00", 3); /* MSBs of counter */ gcry_md_write (hash_hd, &counter, 1); err = hash_ecc_cms_shared_info (hash_hd, wrap_algo_str, keylen, ukm, ukmlen); if (err) break; gcry_md_final (hash_hd); if (n + hashlen > keylen) ncopy = keylen - n; else ncopy = hashlen; memcpy (key+n, gcry_md_read (hash_hd, 0), ncopy); n += ncopy; } gcry_md_close (hash_hd); return err; } /* This function will modify SECRET. NBITS is the size of the curve * which which we took from the certificate. */ static gpg_error_t ecdh_decrypt (unsigned char *secret, size_t secretlen, unsigned int nbits, gcry_sexp_t enc_val, unsigned char **r_result, unsigned int *r_resultlen) { gpg_error_t err; gcry_buffer_t ioarray[4] = { {0}, {0}, {0}, {0} }; char *encr_algo_str = NULL; char *wrap_algo_str = NULL; int hash_algo, cipher_algo; const unsigned char *ukm; /* Alias for ioarray[2]. */ unsigned int ukmlen; const unsigned char *data; /* Alias for ioarray[3]. */ unsigned int datalen; unsigned int keylen; unsigned char key[32]; gcry_cipher_hd_t cipher_hd = NULL; unsigned char *result = NULL; unsigned int resultlen; *r_resultlen = 0; *r_result = NULL; /* Extract X from SECRET; this is the actual secret. Unless a * smartcard diretcly returns X, it must be in the format of: * * 04 || X || Y * 40 || X * 41 || X */ if (secretlen < 2) return gpg_error (GPG_ERR_BAD_DATA); if (secretlen == (nbits+7)/8) ; /* Matches curve length - this is already the X coordinate. */ else if (*secret == 0x04) { secretlen--; memmove (secret, secret+1, secretlen); if ((secretlen & 1)) return gpg_error (GPG_ERR_BAD_DATA); secretlen /= 2; } else if (*secret == 0x40 || *secret == 0x41) { secretlen--; memmove (secret, secret+1, secretlen); } else return gpg_error (GPG_ERR_BAD_DATA); if (!secretlen) return gpg_error (GPG_ERR_BAD_DATA); if (DBG_CRYPTO) log_printhex (secret, secretlen, "ECDH X ..:"); /* We have now the shared secret bytes in (SECRET,SECRETLEN). Now * we will compute the KEK using a value dervied from the secret * bytes. */ err = gcry_sexp_extract_param (enc_val, "enc-val", "&'encr-algo''wrap-algo''ukm'?s", ioarray+0, ioarray+1, ioarray+2, ioarray+3, NULL); if (err) { log_error ("extracting ECDH parameter failed: %s\n", gpg_strerror (err)); goto leave; } encr_algo_str = string_from_gcry_buffer (ioarray); if (!encr_algo_str) { err = gpg_error_from_syserror (); goto leave; } wrap_algo_str = string_from_gcry_buffer (ioarray+1); if (!wrap_algo_str) { err = gpg_error_from_syserror (); goto leave; } ukm = ioarray[2].data; ukmlen = ioarray[2].len; data = ioarray[3].data; datalen = ioarray[3].len; /* Check parameters. */ if (DBG_CRYPTO) { log_debug ("encr_algo: %s\n", encr_algo_str); log_debug ("wrap_algo: %s\n", wrap_algo_str); log_printhex (ukm, ukmlen, "ukm .....:"); log_printhex (data, datalen, "data ....:"); } if (!strcmp (encr_algo_str, "1.3.132.1.11.1")) { /* dhSinglePass-stdDH-sha256kdf-scheme */ hash_algo = GCRY_MD_SHA256; } else if (!strcmp (encr_algo_str, "1.3.132.1.11.2")) { /* dhSinglePass-stdDH-sha384kdf-scheme */ hash_algo = GCRY_MD_SHA384; } else if (!strcmp (encr_algo_str, "1.3.132.1.11.3")) { /* dhSinglePass-stdDH-sha512kdf-scheme */ hash_algo = GCRY_MD_SHA512; } else if (!strcmp (encr_algo_str, "1.3.133.16.840.63.0.2")) { /* dhSinglePass-stdDH-sha1kdf-scheme */ hash_algo = GCRY_MD_SHA1; } else { err = gpg_error (GPG_ERR_PUBKEY_ALGO); goto leave; } if (!strcmp (wrap_algo_str, "2.16.840.1.101.3.4.1.5")) { cipher_algo = GCRY_CIPHER_AES128; keylen = 16; } else if (!strcmp (wrap_algo_str, "2.16.840.1.101.3.4.1.25")) { cipher_algo = GCRY_CIPHER_AES192; keylen = 24; } else if (!strcmp (wrap_algo_str, "2.16.840.1.101.3.4.1.45")) { cipher_algo = GCRY_CIPHER_AES256; keylen = 32; } else { err = gpg_error (GPG_ERR_PUBKEY_ALGO); goto leave; } err = ecdh_derive_kek (key, keylen, hash_algo, wrap_algo_str, secret, secretlen, ukm, ukmlen); if (err) goto leave; if (DBG_CRYPTO) log_printhex (key, keylen, "KEK .....:"); /* Unwrap the key. */ if ((datalen % 8) || datalen < 16) { log_error ("can't use a shared secret of %u bytes for ecdh\n", datalen); err = gpg_error (GPG_ERR_BAD_DATA); goto leave; } resultlen = datalen - 8; result = xtrymalloc_secure (resultlen); if (!result) { err = gpg_error_from_syserror (); goto leave; } err = gcry_cipher_open (&cipher_hd, cipher_algo, GCRY_CIPHER_MODE_AESWRAP, 0); if (err) { log_error ("ecdh failed to initialize AESWRAP: %s\n", gpg_strerror (err)); goto leave; } err = gcry_cipher_setkey (cipher_hd, key, keylen); wipememory (key, sizeof key); if (err) { log_error ("ecdh failed in gcry_cipher_setkey: %s\n", gpg_strerror (err)); goto leave; } err = gcry_cipher_decrypt (cipher_hd, result, resultlen, data, datalen); if (err) { log_error ("ecdh failed in gcry_cipher_decrypt: %s\n",gpg_strerror (err)); goto leave; } *r_resultlen = resultlen; *r_result = result; result = NULL; leave: if (result) { wipememory (result, resultlen); xfree (result); } gcry_cipher_close (cipher_hd); xfree (encr_algo_str); xfree (wrap_algo_str); xfree (ioarray[0].data); xfree (ioarray[1].data); xfree (ioarray[2].data); xfree (ioarray[3].data); return err; } /* Helper for pwri_decrypt to parse the derive info. * Example data for (DER,DERLEN): * SEQUENCE { * OCTET STRING * 60 76 4B E9 5E DF 3C F8 B2 F9 B6 C2 7D 5A FB 90 * 23 B6 47 DF * INTEGER 10000 * SEQUENCE { * OBJECT IDENTIFIER * hmacWithSHA512 (1 2 840 113549 2 11) * NULL * } * } */ static gpg_error_t pwri_parse_pbkdf2 (const unsigned char *der, size_t derlen, unsigned char const **r_salt, unsigned int *r_saltlen, unsigned long *r_iterations, enum gcry_md_algos *r_digest) { gpg_error_t err; size_t objlen, hdrlen; int class, tag, constructed, ndef; char *oidstr; err = parse_ber_header (&der, &derlen, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > derlen || tag != TAG_SEQUENCE || !constructed || ndef)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) return err; derlen = objlen; err = parse_ber_header (&der, &derlen, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > derlen || tag != TAG_OCTET_STRING || constructed || ndef)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) return err; *r_salt = der; *r_saltlen = objlen; der += objlen; derlen -= objlen; err = parse_ber_header (&der, &derlen, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > derlen || tag != TAG_INTEGER || constructed || ndef)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) return err; *r_iterations = 0; for (; objlen; objlen--) { *r_iterations <<= 8; *r_iterations |= (*der++) & 0xff; derlen--; } err = parse_ber_header (&der, &derlen, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > derlen || tag != TAG_SEQUENCE || !constructed || ndef)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) return err; derlen = objlen; err = parse_ber_header (&der, &derlen, &class, &tag, &constructed, &ndef, &objlen, &hdrlen); if (!err && (objlen > derlen || tag != TAG_OBJECT_ID || constructed || ndef)) err = gpg_error (GPG_ERR_INV_OBJ); if (err) return err; oidstr = ksba_oid_to_str (der, objlen); if (!oidstr) return gpg_error_from_syserror (); *r_digest = gcry_md_map_name (oidstr); if (*r_digest) ; else if (!strcmp (oidstr, "1.2.840.113549.2.7")) *r_digest = GCRY_MD_SHA1; else if (!strcmp (oidstr, "1.2.840.113549.2.8")) *r_digest = GCRY_MD_SHA224; else if (!strcmp (oidstr, "1.2.840.113549.2.9")) *r_digest = GCRY_MD_SHA256; else if (!strcmp (oidstr, "1.2.840.113549.2.10")) *r_digest = GCRY_MD_SHA384; else if (!strcmp (oidstr, "1.2.840.113549.2.11")) *r_digest = GCRY_MD_SHA512; else err = gpg_error (GPG_ERR_DIGEST_ALGO); ksba_free (oidstr); return err; } /* Password based decryption. * ENC_VAL has the form: * (enc-val * (pwri * (derive-algo ) --| both are optional * (derive-parm ) --| * (encr-algo ) * (encr-parm ) * (encr-key ))) -- this is the encrypted session key * */ static gpg_error_t -pwri_decrypt (gcry_sexp_t enc_val, +pwri_decrypt (ctrl_t ctrl, gcry_sexp_t enc_val, unsigned char **r_result, unsigned int *r_resultlen, struct decrypt_filter_parm_s *parm) { gpg_error_t err; gcry_buffer_t ioarray[5] = { {0} }; char *derive_algo_str = NULL; char *encr_algo_str = NULL; const unsigned char *dparm; /* Alias for ioarray[1]. */ unsigned int dparmlen; const unsigned char *eparm; /* Alias for ioarray[3]. */ unsigned int eparmlen; const unsigned char *ekey; /* Alias for ioarray[4]. */ unsigned int ekeylen; unsigned char kek[32]; unsigned int keklen; enum gcry_cipher_algos encr_algo; enum gcry_cipher_modes encr_mode; gcry_cipher_hd_t encr_hd = NULL; unsigned char *result = NULL; unsigned int resultlen; unsigned int blklen; const unsigned char *salt; /* Points int dparm. */ unsigned int saltlen; unsigned long iterations; enum gcry_md_algos digest_algo; + char *passphrase = NULL; *r_resultlen = 0; *r_result = NULL; err = gcry_sexp_extract_param (enc_val, "enc-val!pwri", "&'derive-algo'?'derive-parm'?" "'encr-algo''encr-parm''encr-key'", ioarray+0, ioarray+1, ioarray+2, ioarray+3, ioarray+4, NULL); if (err) { /* If this is not pwri element, it is likly a kekri element * which we do not yet support. Change the error back to the * original as returned by ksba_cms_get_issuer. */ if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) err = gpg_error (GPG_ERR_UNSUPPORTED_CMS_OBJ); else log_error ("extracting PWRI parameter failed: %s\n", gpg_strerror (err)); goto leave; } if (ioarray[0].data) { derive_algo_str = string_from_gcry_buffer (ioarray+0); if (!derive_algo_str) { err = gpg_error_from_syserror (); goto leave; } } dparm = ioarray[1].data; dparmlen = ioarray[1].len; encr_algo_str = string_from_gcry_buffer (ioarray+2); if (!encr_algo_str) { err = gpg_error_from_syserror (); goto leave; } eparm = ioarray[3].data; eparmlen = ioarray[3].len; ekey = ioarray[4].data; ekeylen = ioarray[4].len; /* Check parameters. */ if (DBG_CRYPTO) { if (derive_algo_str) { log_debug ("derive algo: %s\n", derive_algo_str); log_printhex (dparm, dparmlen, "derive parm:"); } log_debug ("encr algo .: %s\n", encr_algo_str); log_printhex (eparm, eparmlen, "encr parm .:"); log_printhex (ekey, ekeylen, "encr key .:"); } if (!derive_algo_str) { err = gpg_error (GPG_ERR_NOT_SUPPORTED); log_info ("PWRI with no key derivation detected\n"); goto leave; } if (strcmp (derive_algo_str, "1.2.840.113549.1.5.12")) { err = gpg_error (GPG_ERR_NOT_SUPPORTED); log_info ("PWRI does not use PBKDF2 (but %s)\n", derive_algo_str); goto leave; } digest_algo = 0; /*(silence cc warning)*/ err = pwri_parse_pbkdf2 (dparm, dparmlen, &salt, &saltlen, &iterations, &digest_algo); if (err) { log_error ("parsing PWRI parameter failed: %s\n", gpg_strerror (err)); goto leave; } parm->is_de_vs = (parm->is_de_vs && gnupg_digest_is_compliant (CO_DE_VS, digest_algo)); encr_algo = gcry_cipher_map_name (encr_algo_str); encr_mode = gcry_cipher_mode_from_oid (encr_algo_str); if (!encr_algo || !encr_mode) { log_error ("PWRI uses unknown algorithm %s\n", encr_algo_str); err = gpg_error (GPG_ERR_CIPHER_ALGO); goto leave; } parm->is_de_vs = (parm->is_de_vs && gnupg_cipher_is_compliant (CO_DE_VS, encr_algo, encr_mode)); keklen = gcry_cipher_get_algo_keylen (encr_algo); blklen = gcry_cipher_get_algo_blklen (encr_algo); if (!keklen || keklen > sizeof kek || blklen != 16 ) { log_error ("PWRI algorithm %s cannot be used\n", encr_algo_str); err = gpg_error (GPG_ERR_INV_KEYLEN); goto leave; } if ((ekeylen % blklen) || (ekeylen / blklen < 2)) { /* Note that we need at least two full blocks. */ log_error ("PWRI uses a wrong length of encrypted key\n"); err = gpg_error (GPG_ERR_INV_KEYLEN); goto leave; } - err = gcry_kdf_derive ("abc", 3, + err = gpgsm_agent_ask_passphrase + (ctrl, + i18n_utf8 (N_("Please enter the password for decryption.")), + 0, &passphrase); + if (err) + goto leave; + + err = gcry_kdf_derive (passphrase, strlen (passphrase), GCRY_KDF_PBKDF2, digest_algo, salt, saltlen, iterations, keklen, kek); + if (passphrase) + { + wipememory (passphrase, strlen (passphrase)); + xfree (passphrase); + passphrase = NULL; + } if (err) { log_error ("deriving key from passphrase failed: %s\n", gpg_strerror (err)); goto leave; } if (DBG_CRYPTO) log_printhex (kek, keklen, "KEK .......:"); /* Unwrap the key. */ resultlen = ekeylen; result = xtrymalloc_secure (resultlen); if (!result) { err = gpg_error_from_syserror (); goto leave; } err = gcry_cipher_open (&encr_hd, encr_algo, encr_mode, 0); if (err) { log_error ("PWRI failed to open cipher: %s\n", gpg_strerror (err)); goto leave; } err = gcry_cipher_setkey (encr_hd, kek, keklen); wipememory (kek, sizeof kek); if (!err) err = gcry_cipher_setiv (encr_hd, ekey + ekeylen - 2 * blklen, blklen); if (!err) err = gcry_cipher_decrypt (encr_hd, result + ekeylen - blklen, blklen, ekey + ekeylen - blklen, blklen); if (!err) err = gcry_cipher_setiv (encr_hd, result + ekeylen - blklen, blklen); if (!err) err = gcry_cipher_decrypt (encr_hd, result, ekeylen - blklen, ekey, ekeylen - blklen); /* (We assume that that eparm is the octet string with the IV) */ if (!err) err = gcry_cipher_setiv (encr_hd, eparm, eparmlen); if (!err) err = gcry_cipher_decrypt (encr_hd, result, resultlen, NULL, 0); if (err) { log_error ("KEK decryption failed for PWRI: %s\n", gpg_strerror (err)); goto leave; } if (DBG_CRYPTO) log_printhex (result, resultlen, "Frame .....:"); if (result[0] < 8 /* At least 64 bits */ || (result[0] % 8) /* Multiple of 64 bits */ || result[0] > resultlen - 4 /* Not more than the size of the input */ || ( (result[1] ^ result[4]) /* Matching check bytes. */ & (result[2] ^ result[5]) & (result[3] ^ result[6]) ) != 0xff) { err = gpg_error (GPG_ERR_BAD_PASSPHRASE); goto leave; } *r_resultlen = result[0]; *r_result = memmove (result, result + 4, result[0]); result = NULL; leave: if (result) { wipememory (result, resultlen); xfree (result); } + if (passphrase) + { + wipememory (passphrase, strlen (passphrase)); + xfree (passphrase); + } gcry_cipher_close (encr_hd); xfree (derive_algo_str); xfree (encr_algo_str); xfree (ioarray[0].data); xfree (ioarray[1].data); xfree (ioarray[2].data); xfree (ioarray[3].data); xfree (ioarray[4].data); return err; } /* Decrypt the session key and fill in the parm structure. The algo and the IV is expected to be already in PARM. */ static int prepare_decryption (ctrl_t ctrl, const char *hexkeygrip, int pk_algo, unsigned int nbits, const char *desc, ksba_const_sexp_t enc_val, struct decrypt_filter_parm_s *parm) { char *seskey = NULL; size_t n, seskeylen; int pwri = !hexkeygrip && !pk_algo; int rc; if (DBG_CRYPTO) log_printcanon ("decrypting:", enc_val, 0); if (!pwri) { rc = gpgsm_agent_pkdecrypt (ctrl, hexkeygrip, desc, enc_val, &seskey, &seskeylen); if (rc) { log_error ("error decrypting session key: %s\n", gpg_strerror (rc)); goto leave; } if (DBG_CRYPTO) log_printhex (seskey, seskeylen, "DEK frame:"); } n=0; if (pwri) /* Password based encryption. */ { gcry_sexp_t s_enc_val; unsigned char *decrypted; unsigned int decryptedlen; rc = gcry_sexp_sscan (&s_enc_val, NULL, enc_val, gcry_sexp_canon_len (enc_val, 0, NULL, NULL)); if (rc) goto leave; - rc = pwri_decrypt (s_enc_val, &decrypted, &decryptedlen, parm); + rc = pwri_decrypt (ctrl, s_enc_val, &decrypted, &decryptedlen, parm); gcry_sexp_release (s_enc_val); if (rc) goto leave; xfree (seskey); seskey = decrypted; seskeylen = decryptedlen; } else if (pk_algo == GCRY_PK_ECC) { gcry_sexp_t s_enc_val; unsigned char *decrypted; unsigned int decryptedlen; rc = gcry_sexp_sscan (&s_enc_val, NULL, enc_val, gcry_sexp_canon_len (enc_val, 0, NULL, NULL)); if (rc) goto leave; rc = ecdh_decrypt (seskey, seskeylen, nbits, s_enc_val, &decrypted, &decryptedlen); gcry_sexp_release (s_enc_val); if (rc) goto leave; xfree (seskey); seskey = decrypted; seskeylen = decryptedlen; } else if (seskeylen == 32 || seskeylen == 24 || seskeylen == 16) { /* Smells like an AES-128, 3-DES, or AES-256 key. This might * happen because a SC has already done the unpacking. A better * solution would be to test for this only after we triggered * the GPG_ERR_INV_SESSION_KEY. */ } else { if (n + 7 > seskeylen ) { rc = gpg_error (GPG_ERR_INV_SESSION_KEY); goto leave; } /* FIXME: Actually the leading zero is required but due to the way we encode the output in libgcrypt as an MPI we are not able to encode that leading zero. However, when using a Smartcard we are doing it the right way and therefore we have to skip the zero. This should be fixed in gpg-agent of course. */ if (!seskey[n]) n++; if (seskey[n] != 2 ) /* Wrong block type version. */ { rc = gpg_error (GPG_ERR_INV_SESSION_KEY); goto leave; } for (n++; n < seskeylen && seskey[n]; n++) /* Skip the random bytes. */ ; n++; /* and the zero byte */ if (n >= seskeylen ) { rc = gpg_error (GPG_ERR_INV_SESSION_KEY); goto leave; } } if (DBG_CRYPTO) { log_printhex (seskey+n, seskeylen-n, "CEK .......:"); log_printhex (parm->iv, parm->ivlen, "IV ........:"); } if (opt.verbose) log_info (_("%s.%s encrypted data\n"), gcry_cipher_algo_name (parm->algo), cipher_mode_to_string (parm->mode)); rc = gcry_cipher_open (&parm->hd, parm->algo, parm->mode, 0); if (rc) { log_error ("error creating decryptor: %s\n", gpg_strerror (rc)); goto leave; } rc = gcry_cipher_setkey (parm->hd, seskey+n, seskeylen-n); if (gpg_err_code (rc) == GPG_ERR_WEAK_KEY) { log_info (_("WARNING: message was encrypted with " "a weak key in the symmetric cipher.\n")); rc = 0; } if (rc) { log_error("key setup failed: %s\n", gpg_strerror(rc) ); goto leave; } rc = gcry_cipher_setiv (parm->hd, parm->iv, parm->ivlen); if (rc) { log_error("IV setup failed: %s\n", gpg_strerror(rc) ); goto leave; } leave: xfree (seskey); return rc; } /* This function is called by the KSBA writer just before the actual write is done. The function must take INLEN bytes from INBUF, decrypt it and store it inoutbuf which has a maximum size of maxoutlen. The valid bytes in outbuf should be return in outlen. Due to different buffer sizes or different length of input and output, it may happen that fewer bytes are processed or fewer bytes are written. */ static gpg_error_t decrypt_filter (void *arg, const void *inbuf, size_t inlen, size_t *inused, void *outbuf, size_t maxoutlen, size_t *outlen) { struct decrypt_filter_parm_s *parm = arg; int blklen = parm->blklen; size_t orig_inlen = inlen; /* fixme: Should we issue an error when we have not seen one full block? */ if (!inlen) return gpg_error (GPG_ERR_BUG); if (maxoutlen < 2*parm->blklen) return gpg_error (GPG_ERR_BUG); /* Make some space because we will later need an extra block at the end. */ maxoutlen -= blklen; if (parm->helpblocklen) { int i, j; for (i=parm->helpblocklen,j=0; i < blklen && j < inlen; i++, j++) parm->helpblock[i] = ((const char*)inbuf)[j]; inlen -= j; if (blklen > maxoutlen) return gpg_error (GPG_ERR_BUG); if (i < blklen) { parm->helpblocklen = i; *outlen = 0; } else { parm->helpblocklen = 0; if (parm->any_data) { memcpy (outbuf, parm->lastblock, blklen); *outlen =blklen; } else *outlen = 0; gcry_cipher_decrypt (parm->hd, parm->lastblock, blklen, parm->helpblock, blklen); parm->any_data = 1; } *inused = orig_inlen - inlen; return 0; } if (inlen > maxoutlen) inlen = maxoutlen; if (inlen % blklen) { /* store the remainder away */ parm->helpblocklen = inlen%blklen; inlen = inlen/blklen*blklen; memcpy (parm->helpblock, (const char*)inbuf+inlen, parm->helpblocklen); } *inused = inlen + parm->helpblocklen; if (inlen) { log_assert (inlen >= blklen); if (parm->any_data) { gcry_cipher_decrypt (parm->hd, (char*)outbuf+blklen, inlen, inbuf, inlen); memcpy (outbuf, parm->lastblock, blklen); memcpy (parm->lastblock,(char*)outbuf+inlen, blklen); *outlen = inlen; } else { gcry_cipher_decrypt (parm->hd, outbuf, inlen, inbuf, inlen); memcpy (parm->lastblock, (char*)outbuf+inlen-blklen, blklen); *outlen = inlen - blklen; parm->any_data = 1; } } else *outlen = 0; return 0; } /* Perform a decrypt operation. */ int gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) { int rc; gnupg_ksba_io_t b64reader = NULL; gnupg_ksba_io_t b64writer = NULL; ksba_reader_t reader; ksba_writer_t writer; ksba_cms_t cms = NULL; ksba_stop_reason_t stopreason; KEYDB_HANDLE kh; int recp; estream_t in_fp = NULL; struct decrypt_filter_parm_s dfparm; memset (&dfparm, 0, sizeof dfparm); audit_set_type (ctrl->audit, AUDIT_TYPE_DECRYPT); kh = keydb_new (ctrl); if (!kh) { log_error (_("failed to allocate keyDB handle\n")); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } in_fp = es_fdopen_nc (in_fd, "rb"); if (!in_fp) { rc = gpg_error_from_syserror (); log_error ("fdopen() failed: %s\n", strerror (errno)); goto leave; } rc = gnupg_ksba_create_reader (&b64reader, ((ctrl->is_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->is_base64? GNUPG_KSBA_IO_BASE64 : 0) | (ctrl->autodetect_encoding? GNUPG_KSBA_IO_AUTODETECT : 0)), in_fp, &reader); if (rc) { log_error ("can't create reader: %s\n", gpg_strerror (rc)); goto leave; } rc = gnupg_ksba_create_writer (&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 : 0)), ctrl->pem_name, out_fp, &writer); if (rc) { log_error ("can't create writer: %s\n", gpg_strerror (rc)); goto leave; } rc = ksba_cms_new (&cms); if (rc) goto leave; rc = ksba_cms_set_reader_writer (cms, reader, writer); if (rc) { log_error ("ksba_cms_set_reader_writer failed: %s\n", gpg_strerror (rc)); goto leave; } audit_log (ctrl->audit, AUDIT_SETUP_READY); /* Parser loop. */ do { rc = ksba_cms_parse (cms, &stopreason); if (rc) { log_error ("ksba_cms_parse failed: %s\n", gpg_strerror (rc)); goto leave; } if (stopreason == KSBA_SR_BEGIN_DATA || stopreason == KSBA_SR_DETACHED_DATA) { int algo, mode; const char *algoid; int any_key = 0; audit_log (ctrl->audit, AUDIT_GOT_DATA); algoid = ksba_cms_get_content_oid (cms, 2/* encryption algo*/); algo = gcry_cipher_map_name (algoid); mode = gcry_cipher_mode_from_oid (algoid); if (!algo || !mode) { rc = gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); log_error ("unsupported algorithm '%s'\n", algoid? algoid:"?"); if (algoid && !strcmp (algoid, "1.2.840.113549.3.2")) log_info (_("(this is the RC2 algorithm)\n")); else if (!algoid) log_info (_("(this does not seem to be an encrypted" " message)\n")); { char numbuf[50]; sprintf (numbuf, "%d", rc); gpgsm_status2 (ctrl, STATUS_ERROR, "decrypt.algorithm", numbuf, algoid?algoid:"?", NULL); audit_log_s (ctrl->audit, AUDIT_BAD_DATA_CIPHER_ALGO, algoid); } /* If it seems that this is not an encrypted message we return a more sensible error code. */ if (!algoid) rc = gpg_error (GPG_ERR_NO_DATA); goto leave; } /* Check compliance. */ if (! gnupg_cipher_is_allowed (opt.compliance, 0, algo, mode)) { log_error (_("cipher algorithm '%s'" " may not be used in %s mode\n"), gcry_cipher_algo_name (algo), gnupg_compliance_option_string (opt.compliance)); rc = gpg_error (GPG_ERR_CIPHER_ALGO); goto leave; } /* For CMS, CO_DE_VS demands CBC mode. */ dfparm.is_de_vs = gnupg_cipher_is_compliant (CO_DE_VS, algo, mode); audit_log_i (ctrl->audit, AUDIT_DATA_CIPHER_ALGO, algo); dfparm.algo = algo; dfparm.mode = mode; dfparm.blklen = gcry_cipher_get_algo_blklen (algo); if (dfparm.blklen > sizeof (dfparm.helpblock)) return gpg_error (GPG_ERR_BUG); rc = ksba_cms_get_content_enc_iv (cms, dfparm.iv, sizeof (dfparm.iv), &dfparm.ivlen); if (rc) { log_error ("error getting IV: %s\n", gpg_strerror (rc)); goto leave; } for (recp=0; !any_key; recp++) { char *issuer; ksba_sexp_t serial; ksba_sexp_t enc_val; char *hexkeygrip = NULL; char *pkalgostr = NULL; char *pkfpr = NULL; char *desc = NULL; char kidbuf[16+1]; int tmp_rc; ksba_cert_t cert = NULL; unsigned int nbits; int pk_algo = 0; int maybe_pwri = 0; *kidbuf = 0; tmp_rc = ksba_cms_get_issuer_serial (cms, recp, &issuer, &serial); if (tmp_rc == -1 && recp) break; /* no more recipients */ audit_log_i (ctrl->audit, AUDIT_NEW_RECP, recp); if (gpg_err_code (tmp_rc) == GPG_ERR_UNSUPPORTED_CMS_OBJ) { maybe_pwri = 1; } else if (tmp_rc) { log_error ("recp %d - error getting info: %s\n", recp, gpg_strerror (tmp_rc)); } else { if (opt.verbose) { log_info ("recp %d - issuer: '%s'\n", recp, issuer? issuer:"[NONE]"); log_info ("recp %d - serial: ", recp); gpgsm_dump_serial (serial); log_printf ("\n"); } if (ctrl->audit) { char *tmpstr = gpgsm_format_sn_issuer (serial, issuer); audit_log_s (ctrl->audit, AUDIT_RECP_NAME, tmpstr); xfree (tmpstr); } keydb_search_reset (kh); rc = keydb_search_issuer_sn (ctrl, kh, issuer, serial); if (rc) { log_error ("failed to find the certificate: %s\n", gpg_strerror(rc)); goto oops; } rc = keydb_get_cert (kh, &cert); if (rc) { log_error ("failed to get cert: %s\n", gpg_strerror (rc)); goto oops; } /* Print the ENC_TO status line. Note that we can do so only if we have the certificate. This is in contrast to gpg where the keyID is commonly included in the encrypted messages. It is too cumbersome to retrieve the used algorithm, thus we don't print it for now. We also record the keyid for later use. */ { unsigned long kid[2]; kid[0] = gpgsm_get_short_fingerprint (cert, kid+1); snprintf (kidbuf, sizeof kidbuf, "%08lX%08lX", kid[1], kid[0]); gpgsm_status2 (ctrl, STATUS_ENC_TO, kidbuf, "0", "0", NULL); } /* Put the certificate into the audit log. */ audit_log_cert (ctrl->audit, AUDIT_SAVE_CERT, cert, 0); /* Just in case there is a problem with the own certificate we print this message - should never happen of course */ rc = gpgsm_cert_use_decrypt_p (cert); if (rc) { char numbuf[50]; sprintf (numbuf, "%d", rc); gpgsm_status2 (ctrl, STATUS_ERROR, "decrypt.keyusage", numbuf, NULL); rc = 0; } hexkeygrip = gpgsm_get_keygrip_hexstring (cert); desc = gpgsm_format_keydesc (cert); pkfpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); pkalgostr = gpgsm_pubkey_algo_string (cert, NULL); pk_algo = gpgsm_get_key_algo_info (cert, &nbits); if (!opt.quiet) log_info (_("encrypted to %s key %s\n"), pkalgostr, pkfpr); /* Check compliance. */ if (!gnupg_pk_is_allowed (opt.compliance, PK_USE_DECRYPTION, pk_algo, 0, NULL, nbits, NULL)) { char kidstr[10+1]; snprintf (kidstr, sizeof kidstr, "0x%08lX", gpgsm_get_short_fingerprint (cert, NULL)); log_info (_("key %s is not suitable for decryption" " in %s mode\n"), kidstr, gnupg_compliance_option_string(opt.compliance)); rc = gpg_error (GPG_ERR_PUBKEY_ALGO); goto oops; } /* Check that all certs are compliant with CO_DE_VS. */ dfparm.is_de_vs = (dfparm.is_de_vs && gnupg_pk_is_compliant (CO_DE_VS, pk_algo, 0, NULL, nbits, NULL)); oops: if (rc) { /* We cannot check compliance of certs that we * don't have. */ dfparm.is_de_vs = 0; } xfree (issuer); xfree (serial); ksba_cert_release (cert); } if ((!hexkeygrip || !pk_algo) && !maybe_pwri) ; else if (!(enc_val = ksba_cms_get_enc_val (cms, recp))) { log_error ("recp %d - error getting encrypted session key\n", recp); if (maybe_pwri) log_info ("(possibly unsupported KEK info)\n"); } else { if (maybe_pwri && opt.verbose) log_info ("recp %d - KEKRI or PWRI\n", recp); rc = prepare_decryption (ctrl, hexkeygrip, pk_algo, nbits, desc, enc_val, &dfparm); xfree (enc_val); if (rc) { log_info ("decrypting session key failed: %s\n", gpg_strerror (rc)); if (gpg_err_code (rc) == GPG_ERR_NO_SECKEY && *kidbuf) gpgsm_status2 (ctrl, STATUS_NO_SECKEY, kidbuf, NULL); } else { /* setup the bulk decrypter */ any_key = 1; ksba_writer_set_filter (writer, decrypt_filter, &dfparm); if (dfparm.is_de_vs && gnupg_gcrypt_is_compliant (CO_DE_VS)) gpgsm_status (ctrl, STATUS_DECRYPTION_COMPLIANCE_MODE, gnupg_status_compliance_flag (CO_DE_VS)); } audit_log_ok (ctrl->audit, AUDIT_RECP_RESULT, rc); } xfree (pkalgostr); xfree (pkfpr); xfree (hexkeygrip); xfree (desc); } /* If we write an audit log add the unused recipients to the log as well. */ if (ctrl->audit && any_key) { for (;; recp++) { char *issuer; ksba_sexp_t serial; int tmp_rc; tmp_rc = ksba_cms_get_issuer_serial (cms, recp, &issuer, &serial); if (tmp_rc == -1) break; /* no more recipients */ audit_log_i (ctrl->audit, AUDIT_NEW_RECP, recp); if (tmp_rc) log_error ("recp %d - error getting info: %s\n", recp, gpg_strerror (tmp_rc)); else { char *tmpstr = gpgsm_format_sn_issuer (serial, issuer); audit_log_s (ctrl->audit, AUDIT_RECP_NAME, tmpstr); xfree (tmpstr); xfree (issuer); xfree (serial); } } } if (!any_key) { if (!rc) rc = gpg_error (GPG_ERR_NO_SECKEY); goto leave; } } else if (stopreason == KSBA_SR_END_DATA) { ksba_writer_set_filter (writer, NULL, NULL); if (dfparm.any_data) { /* write the last block with padding removed */ int i, npadding = dfparm.lastblock[dfparm.blklen-1]; if (!npadding || npadding > dfparm.blklen) { log_error ("invalid padding with value %d\n", npadding); rc = gpg_error (GPG_ERR_INV_DATA); goto leave; } rc = ksba_writer_write (writer, dfparm.lastblock, dfparm.blklen - npadding); if (rc) goto leave; for (i=dfparm.blklen - npadding; i < dfparm.blklen; i++) { if (dfparm.lastblock[i] != npadding) { log_error ("inconsistent padding\n"); rc = gpg_error (GPG_ERR_INV_DATA); goto leave; } } } } } while (stopreason != KSBA_SR_READY); rc = gnupg_ksba_finish_writer (b64writer); if (rc) { log_error ("write failed: %s\n", gpg_strerror (rc)); goto leave; } gpgsm_status (ctrl, STATUS_DECRYPTION_OKAY, NULL); leave: audit_log_ok (ctrl->audit, AUDIT_DECRYPTION_RESULT, rc); if (rc) { gpgsm_status (ctrl, STATUS_DECRYPTION_FAILED, NULL); log_error ("message decryption failed: %s <%s>\n", gpg_strerror (rc), gpg_strsource (rc)); } ksba_cms_release (cms); gnupg_ksba_destroy_reader (b64reader); gnupg_ksba_destroy_writer (b64writer); keydb_release (kh); es_fclose (in_fp); if (dfparm.hd) gcry_cipher_close (dfparm.hd); return rc; } diff --git a/sm/export.c b/sm/export.c index 32f04565f..54893b54d 100644 --- a/sm/export.c +++ b/sm/export.c @@ -1,773 +1,773 @@ /* export.c - Export certificates and private keys. * Copyright (C) 2002, 2003, 2004, 2007, 2009, * 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 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 . */ #include #include #include #include #include #include #include "gpgsm.h" #include #include #include "keydb.h" #include "../common/exechelp.h" #include "../common/i18n.h" #include "../common/sysutils.h" #include "minip12.h" /* A table to store a fingerprint as used in a duplicates table. We don't need to hash here because a fingerprint is already a perfect hash value. This we use the most significant bits to index the table and then use a linked list for the overflow. Possible enhancement for very large number of certificates: Add a second level table and then resort to a linked list. */ struct duptable_s { struct duptable_s *next; /* Note that we only need to store 19 bytes because the first byte is implicitly given by the table index (we require at least 8 bits). */ unsigned char fpr[19]; }; typedef struct duptable_s *duptable_t; #define DUPTABLE_BITS 12 #define DUPTABLE_SIZE (1 << DUPTABLE_BITS) static void print_short_info (ksba_cert_t cert, estream_t stream); static gpg_error_t export_p12 (ctrl_t ctrl, const unsigned char *certimg, size_t certimglen, const char *prompt, const char *keygrip, int rawmode, void **r_result, size_t *r_resultlen); /* Create a table used to indetify duplicated certificates. */ static duptable_t * create_duptable (void) { return xtrycalloc (DUPTABLE_SIZE, sizeof (duptable_t)); } static void destroy_duptable (duptable_t *table) { int idx; duptable_t t, t2; if (table) { for (idx=0; idx < DUPTABLE_SIZE; idx++) for (t = table[idx]; t; t = t2) { t2 = t->next; xfree (t); } xfree (table); } } /* Insert the 20 byte fingerprint FPR into TABLE. Sets EXITS to true if the fingerprint already exists in the table. */ static gpg_error_t insert_duptable (duptable_t *table, unsigned char *fpr, int *exists) { size_t idx; duptable_t t; *exists = 0; idx = fpr[0]; #if DUPTABLE_BITS > 16 || DUPTABLE_BITS < 8 #error cannot handle a table larger than 16 bits or smaller than 8 bits #elif DUPTABLE_BITS > 8 idx <<= (DUPTABLE_BITS - 8); idx |= (fpr[1] & ~(~0U << 4)); #endif for (t = table[idx]; t; t = t->next) if (!memcmp (t->fpr, fpr+1, 19)) break; if (t) { *exists = 1; return 0; } /* Insert that fingerprint. */ t = xtrymalloc (sizeof *t); if (!t) return gpg_error_from_syserror (); memcpy (t->fpr, fpr+1, 19); t->next = table[idx]; table[idx] = t; return 0; } /* Export all certificates or just those given in NAMES. The output is written to STREAM. */ void gpgsm_export (ctrl_t ctrl, strlist_t names, estream_t stream) { KEYDB_HANDLE hd = NULL; KEYDB_SEARCH_DESC *desc = NULL; int ndesc; gnupg_ksba_io_t b64writer = NULL; ksba_writer_t writer; strlist_t sl; ksba_cert_t cert = NULL; int rc=0; int count = 0; int i; duptable_t *dtable; dtable = create_duptable (); if (!dtable) { log_error ("creating duplicates table failed: %s\n", strerror (errno)); goto leave; } hd = keydb_new (ctrl); if (!hd) { log_error ("keydb_new failed\n"); goto leave; } if (!names) ndesc = 1; else { for (sl=names, ndesc=0; sl; sl = sl->next, ndesc++) ; } desc = xtrycalloc (ndesc, sizeof *desc); if (!ndesc) { log_error ("allocating memory for export failed: %s\n", gpg_strerror (out_of_core ())); goto leave; } if (!names) desc[0].mode = KEYDB_SEARCH_MODE_FIRST; else { for (ndesc=0, sl=names; sl; sl = sl->next) { rc = classify_user_id (sl->d, desc+ndesc, 0); if (rc) { log_error ("key '%s' not found: %s\n", sl->d, gpg_strerror (rc)); rc = 0; } else ndesc++; } } /* If all specifications are done by fingerprint or keygrip, we switch to ephemeral mode so that _all_ currently available and matching certificates are exported. */ if (names && ndesc) { for (i=0; (i < ndesc && (desc[i].mode == KEYDB_SEARCH_MODE_FPR || desc[i].mode == KEYDB_SEARCH_MODE_KEYGRIP)); i++) ; if (i == ndesc) keydb_set_ephemeral (hd, 1); } while (!(rc = keydb_search (ctrl, hd, desc, ndesc))) { unsigned char fpr[20]; int exists; if (!names) desc[0].mode = KEYDB_SEARCH_MODE_NEXT; rc = keydb_get_cert (hd, &cert); if (rc) { log_error ("keydb_get_cert failed: %s\n", gpg_strerror (rc)); goto leave; } gpgsm_get_fingerprint (cert, 0, fpr, NULL); rc = insert_duptable (dtable, fpr, &exists); if (rc) { log_error ("inserting into duplicates table failed: %s\n", gpg_strerror (rc)); goto leave; } if (!exists && count && !ctrl->create_pem) { log_info ("exporting more than one certificate " "is not possible in binary mode\n"); log_info ("ignoring other certificates\n"); break; } if (!exists) { const unsigned char *image; size_t imagelen; image = ksba_cert_get_image (cert, &imagelen); if (!image) { log_error ("ksba_cert_get_image failed\n"); goto leave; } if (ctrl->create_pem) { if (count) es_putc ('\n', stream); print_short_info (cert, stream); es_putc ('\n', stream); } count++; if (!b64writer) { ctrl->pem_name = "CERTIFICATE"; rc = gnupg_ksba_create_writer (&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 :0)), ctrl->pem_name, stream, &writer); if (rc) { log_error ("can't create writer: %s\n", gpg_strerror (rc)); goto leave; } } rc = ksba_writer_write (writer, image, imagelen); if (rc) { log_error ("write error: %s\n", gpg_strerror (rc)); goto leave; } if (ctrl->create_pem) { /* We want one certificate per PEM block */ rc = gnupg_ksba_finish_writer (b64writer); if (rc) { log_error ("write failed: %s\n", gpg_strerror (rc)); goto leave; } gnupg_ksba_destroy_writer (b64writer); b64writer = NULL; } } ksba_cert_release (cert); cert = NULL; } if (rc && gpg_err_code (rc) != GPG_ERR_NOT_FOUND) log_error ("keydb_search failed: %s\n", gpg_strerror (rc)); else if (b64writer) { rc = gnupg_ksba_finish_writer (b64writer); if (rc) { log_error ("write failed: %s\n", gpg_strerror (rc)); goto leave; } } leave: gnupg_ksba_destroy_writer (b64writer); ksba_cert_release (cert); xfree (desc); keydb_release (hd); destroy_duptable (dtable); } /* Export a certificate and its private key. RAWMODE controls the actual output: 0 - Private key and certifciate in PKCS#12 format 1 - Only unencrypted private key in PKCS#8 format 2 - Only unencrypted private key in PKCS#1 format */ void gpgsm_p12_export (ctrl_t ctrl, const char *name, estream_t stream, int rawmode) { gpg_error_t err = 0; KEYDB_HANDLE hd; KEYDB_SEARCH_DESC *desc = NULL; gnupg_ksba_io_t b64writer = NULL; ksba_writer_t writer; ksba_cert_t cert = NULL; const unsigned char *image; size_t imagelen; char *keygrip = NULL; char *prompt; void *data; size_t datalen; hd = keydb_new (ctrl); if (!hd) { log_error ("keydb_new failed\n"); goto leave; } desc = xtrycalloc (1, sizeof *desc); if (!desc) { log_error ("allocating memory for export failed: %s\n", gpg_strerror (out_of_core ())); goto leave; } err = classify_user_id (name, desc, 0); if (err) { log_error ("key '%s' not found: %s\n", name, gpg_strerror (err)); goto leave; } /* Lookup the certificate and make sure that it is unique. */ err = keydb_search (ctrl, hd, desc, 1); if (!err) { err = keydb_get_cert (hd, &cert); if (err) { log_error ("keydb_get_cert failed: %s\n", gpg_strerror (err)); goto leave; } next_ambiguous: err = keydb_search (ctrl, hd, desc, 1); if (!err) { ksba_cert_t cert2 = NULL; if (!keydb_get_cert (hd, &cert2)) { if (gpgsm_certs_identical_p (cert, cert2)) { ksba_cert_release (cert2); goto next_ambiguous; } ksba_cert_release (cert2); } err = gpg_error (GPG_ERR_AMBIGUOUS_NAME); } else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) err = 0; if (err) { log_error ("key '%s' not found: %s\n", name, gpg_strerror (err)); goto leave; } } keygrip = gpgsm_get_keygrip_hexstring (cert); if (!keygrip || gpgsm_agent_havekey (ctrl, keygrip)) { /* Note, that the !keygrip case indicates a bad certificate. */ err = gpg_error (GPG_ERR_NO_SECKEY); log_error ("can't export key '%s': %s\n", name, gpg_strerror (err)); goto leave; } image = ksba_cert_get_image (cert, &imagelen); if (!image) { log_error ("ksba_cert_get_image failed\n"); goto leave; } if (ctrl->create_pem) { print_short_info (cert, stream); es_putc ('\n', stream); } if (opt.p12_charset && ctrl->create_pem && !rawmode) { es_fprintf (stream, "The passphrase is %s encoded.\n\n", opt.p12_charset); } if (rawmode == 0) ctrl->pem_name = "PKCS12"; else if (gpgsm_get_key_algo_info (cert, NULL) == GCRY_PK_ECC) ctrl->pem_name = "EC PRIVATE KEY"; else if (rawmode == 1) ctrl->pem_name = "PRIVATE KEY"; else ctrl->pem_name = "RSA PRIVATE KEY"; err = gnupg_ksba_create_writer (&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 : 0)), ctrl->pem_name, stream, &writer); if (err) { log_error ("can't create writer: %s\n", gpg_strerror (err)); goto leave; } prompt = gpgsm_format_keydesc (cert); err = export_p12 (ctrl, image, imagelen, prompt, keygrip, rawmode, &data, &datalen); xfree (prompt); if (err) goto leave; err = ksba_writer_write (writer, data, datalen); xfree (data); if (err) { log_error ("write failed: %s\n", gpg_strerror (err)); goto leave; } if (ctrl->create_pem) { /* We want one certificate per PEM block */ err = gnupg_ksba_finish_writer (b64writer); if (err) { log_error ("write failed: %s\n", gpg_strerror (err)); goto leave; } gnupg_ksba_destroy_writer (b64writer); b64writer = NULL; } ksba_cert_release (cert); cert = NULL; leave: gnupg_ksba_destroy_writer (b64writer); ksba_cert_release (cert); xfree (keygrip); xfree (desc); keydb_release (hd); } /* Print some info about the certifciate CERT to FP or STREAM */ static void print_short_info (ksba_cert_t cert, estream_t stream) { char *p; ksba_sexp_t sexp; int idx; for (idx=0; (p = ksba_cert_get_issuer (cert, idx)); idx++) { es_fputs ((!idx ? "Issuer ...: " : "\n aka ...: "), stream); gpgsm_es_print_name (stream, p); xfree (p); } es_putc ('\n', stream); es_fputs ("Serial ...: ", stream); sexp = ksba_cert_get_serial (cert); if (sexp) { int len; const unsigned char *s = sexp; if (*s == '(') { s++; for (len=0; *s && *s != ':' && digitp (s); s++) len = len*10 + atoi_1 (s); if (*s == ':') es_write_hexstring (stream, s+1, len, 0, NULL); } xfree (sexp); } es_putc ('\n', stream); for (idx=0; (p = ksba_cert_get_subject (cert, idx)); idx++) { es_fputs ((!idx ? "Subject ..: " : "\n aka ..: "), stream); gpgsm_es_print_name (stream, p); xfree (p); } es_putc ('\n', stream); p = gpgsm_get_keygrip_hexstring (cert); if (p) { es_fprintf (stream, "Keygrip ..: %s\n", p); xfree (p); } } /* Parse a private key S-expression and return a malloced array with the RSA parameters in pkcs#12 order. The caller needs to deep-release this array. */ static gcry_mpi_t * sexp_to_kparms (gcry_sexp_t sexp) { gcry_sexp_t list, l2; const char *name; const char *s; size_t n; int idx; gcry_mpi_t *array; list = gcry_sexp_find_token (sexp, "private-key", 0 ); if(!list) return NULL; l2 = gcry_sexp_cadr (list); gcry_sexp_release (list); list = l2; name = gcry_sexp_nth_data (list, 0, &n); if (name && n == 3 && !memcmp (name, "rsa", 3)) { /* Parameter names used with RSA in the pkcs#12 order. */ const char *elems = "nedqp--u"; array = xtrycalloc (strlen(elems) + 1, sizeof *array); if (!array) { gcry_sexp_release (list); return NULL; } for (idx=0, s=elems; *s; s++, idx++ ) { if (*s == '-') continue; /* Computed below */ l2 = gcry_sexp_find_token (list, s, 1); if (l2) { array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); gcry_sexp_release (l2); } if (!array[idx]) /* Required parameter not found or invalid. */ { for (idx=0; array[idx]; idx++) gcry_mpi_release (array[idx]); xfree (array); gcry_sexp_release (list); return NULL; } } array[5] = gcry_mpi_snew (0); /* compute d mod (q-1) */ gcry_mpi_sub_ui (array[5], array[3], 1); gcry_mpi_mod (array[5], array[2], array[5]); array[6] = gcry_mpi_snew (0); /* compute d mod (p-1) */ gcry_mpi_sub_ui (array[6], array[4], 1); gcry_mpi_mod (array[6], array[2], array[6]); } else if (name && n == 3 && !memcmp (name, "ecc", 3)) { array = xtrycalloc (3 + 1, sizeof *array); if (!array) { gcry_sexp_release (list); return NULL; } if (gcry_sexp_extract_param (list, NULL, "/'curve'qd", array+0, array+1, array+2, NULL)) { xfree (array); array = NULL; /* Error. */ } } else { array = NULL; } gcry_sexp_release (list); return array; } static gpg_error_t export_p12 (ctrl_t ctrl, const unsigned char *certimg, size_t certimglen, const char *prompt, const char *keygrip, int rawmode, void **r_result, size_t *r_resultlen) { gpg_error_t err = 0; void *kek = NULL; size_t keklen; unsigned char *wrappedkey = NULL; size_t wrappedkeylen; gcry_cipher_hd_t cipherhd = NULL; gcry_sexp_t s_skey = NULL; gcry_mpi_t *kparms = NULL; unsigned char *key = NULL; size_t keylen; char *passphrase = NULL; unsigned char *result = NULL; size_t resultlen; int i; *r_result = NULL; /* Get the current KEK. */ err = gpgsm_agent_keywrap_key (ctrl, 1, &kek, &keklen); if (err) { log_error ("error getting the KEK: %s\n", gpg_strerror (err)); goto leave; } /* Receive the wrapped key from the agent. */ err = gpgsm_agent_export_key (ctrl, keygrip, prompt, &wrappedkey, &wrappedkeylen); if (err) goto leave; /* Unwrap the key. */ err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_AESWRAP, 0); if (err) goto leave; err = gcry_cipher_setkey (cipherhd, kek, keklen); if (err) goto leave; xfree (kek); kek = NULL; if (wrappedkeylen < 24) { err = gpg_error (GPG_ERR_INV_LENGTH); goto leave; } keylen = wrappedkeylen - 8; key = xtrymalloc_secure (keylen); if (!key) { err = gpg_error_from_syserror (); goto leave; } err = gcry_cipher_decrypt (cipherhd, key, keylen, wrappedkey, wrappedkeylen); if (err) goto leave; xfree (wrappedkey); wrappedkey = NULL; gcry_cipher_close (cipherhd); cipherhd = NULL; /* Convert to a gcrypt S-expression. */ err = gcry_sexp_create (&s_skey, key, keylen, 0, xfree_fnc); if (err) goto leave; key = NULL; /* Key is now owned by S_KEY. */ /* Get the parameters from the S-expression. */ kparms = sexp_to_kparms (s_skey); gcry_sexp_release (s_skey); s_skey = NULL; if (!kparms) { log_error ("error converting key parameters\n"); err = GPG_ERR_BAD_SECKEY; goto leave; } if (rawmode) { /* Export in raw mode, that is only the pkcs#1/#8 private key. */ result = p12_raw_build (kparms, rawmode, &resultlen); if (!result) err = gpg_error (GPG_ERR_GENERAL); } else { err = gpgsm_agent_ask_passphrase (ctrl, - i18n_utf8 ("Please enter the passphrase to protect the " - "new PKCS#12 object."), + i18n_utf8 (N_("Please enter the passphrase to protect the " + "new PKCS#12 object.")), 1, &passphrase); if (err) goto leave; result = p12_build (kparms, certimg, certimglen, passphrase, opt.p12_charset, &resultlen); xfree (passphrase); passphrase = NULL; if (!result) err = gpg_error (GPG_ERR_GENERAL); } leave: xfree (key); gcry_sexp_release (s_skey); if (kparms) { for (i=0; kparms[i]; i++) gcry_mpi_release (kparms[i]); xfree (kparms); } gcry_cipher_close (cipherhd); xfree (wrappedkey); xfree (kek); if (gpg_err_code (err) == GPG_ERR_BAD_PASSPHRASE) { /* During export this is the passphrase used to unprotect the key and not the pkcs#12 thing as in export. Therefore we can issue the regular passphrase status. FIXME: replace the all zero keyid by a regular one. */ gpgsm_status (ctrl, STATUS_BAD_PASSPHRASE, "0000000000000000"); } if (err) { xfree (result); } else { *r_result = result; *r_resultlen = resultlen; } return err; } diff --git a/sm/import.c b/sm/import.c index 3d08254c8..0b8fbadb6 100644 --- a/sm/import.c +++ b/sm/import.c @@ -1,986 +1,986 @@ /* import.c - Import certificates * Copyright (C) 2001, 2003, 2004, 2009, 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 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 . */ #include #include #include #include #include #include #include #include "gpgsm.h" #include #include #include "keydb.h" #include "../common/exechelp.h" #include "../common/i18n.h" #include "../common/sysutils.h" #include "../kbx/keybox.h" /* for KEYBOX_FLAG_* */ #include "../common/membuf.h" #include "minip12.h" /* The arbitrary limit of one PKCS#12 object. */ #define MAX_P12OBJ_SIZE 128 /*kb*/ struct stats_s { unsigned long count; unsigned long imported; unsigned long unchanged; unsigned long not_imported; unsigned long secret_read; unsigned long secret_imported; unsigned long secret_dups; }; struct rsa_secret_key_s { gcry_mpi_t n; /* public modulus */ gcry_mpi_t e; /* public exponent */ gcry_mpi_t d; /* exponent */ gcry_mpi_t p; /* prime p. */ gcry_mpi_t q; /* prime q. */ gcry_mpi_t u; /* inverse of p mod q. */ }; static gpg_error_t parse_p12 (ctrl_t ctrl, ksba_reader_t reader, struct stats_s *stats); static void print_imported_status (ctrl_t ctrl, ksba_cert_t cert, int new_cert) { char *fpr; fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); if (new_cert) gpgsm_status2 (ctrl, STATUS_IMPORTED, fpr, "[X.509]", NULL); gpgsm_status2 (ctrl, STATUS_IMPORT_OK, new_cert? "1":"0", fpr, NULL); xfree (fpr); } /* Print an IMPORT_PROBLEM status. REASON is one of: 0 := "No specific reason given". 1 := "Invalid Certificate". 2 := "Issuer Certificate missing". 3 := "Certificate Chain too long". 4 := "Error storing certificate". */ static void print_import_problem (ctrl_t ctrl, ksba_cert_t cert, int reason) { char *fpr = NULL; char buf[25]; int i; sprintf (buf, "%d", reason); if (cert) { fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); /* detetect an error (all high) value */ for (i=0; fpr[i] == 'F'; i++) ; if (!fpr[i]) { xfree (fpr); fpr = NULL; } } gpgsm_status2 (ctrl, STATUS_IMPORT_PROBLEM, buf, fpr, NULL); xfree (fpr); } void print_imported_summary (ctrl_t ctrl, struct stats_s *stats) { char buf[14*25]; if (!opt.quiet) { log_info (_("total number processed: %lu\n"), stats->count); if (stats->imported) { log_info (_(" imported: %lu"), stats->imported ); log_printf ("\n"); } if (stats->unchanged) log_info (_(" unchanged: %lu\n"), stats->unchanged); if (stats->secret_read) log_info (_(" secret keys read: %lu\n"), stats->secret_read ); if (stats->secret_imported) log_info (_(" secret keys imported: %lu\n"), stats->secret_imported ); if (stats->secret_dups) log_info (_(" secret keys unchanged: %lu\n"), stats->secret_dups ); if (stats->not_imported) log_info (_(" not imported: %lu\n"), stats->not_imported); } sprintf(buf, "%lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu", stats->count, 0l /*stats->no_user_id*/, stats->imported, 0l /*stats->imported_rsa*/, stats->unchanged, 0l /*stats->n_uids*/, 0l /*stats->n_subk*/, 0l /*stats->n_sigs*/, 0l /*stats->n_revoc*/, stats->secret_read, stats->secret_imported, stats->secret_dups, 0l /*stats->skipped_new_keys*/, stats->not_imported ); gpgsm_status (ctrl, STATUS_IMPORT_RES, buf); } static void check_and_store (ctrl_t ctrl, struct stats_s *stats, ksba_cert_t cert, int depth) { int rc; if (stats) stats->count++; if ( depth >= 50 ) { log_error (_("certificate chain too long\n")); if (stats) stats->not_imported++; print_import_problem (ctrl, cert, 3); return; } /* Some basic checks, but don't care about missing certificates; this is so that we are able to import entire certificate chains w/o requiring a special order (i.e. root-CA first). This used to be different but because gpgsm_verify even imports certificates without any checks, it doesn't matter much and the code gets much cleaner. A housekeeping function to remove certificates w/o an anchor would be nice, though. Optionally we do a full validation in addition to the basic test. */ rc = gpgsm_basic_cert_check (ctrl, cert); if (!rc && ctrl->with_validation) rc = gpgsm_validate_chain (ctrl, cert, "", NULL, 0, NULL, 0, NULL); if (!rc || (!ctrl->with_validation && (gpg_err_code (rc) == GPG_ERR_MISSING_CERT || gpg_err_code (rc) == GPG_ERR_MISSING_ISSUER_CERT))) { int existed; if (!keydb_store_cert (ctrl, cert, 0, &existed)) { ksba_cert_t next = NULL; if (!existed) { print_imported_status (ctrl, cert, 1); if (stats) stats->imported++; } else { print_imported_status (ctrl, cert, 0); if (stats) stats->unchanged++; } if (opt.verbose > 1 && existed) { if (depth) log_info ("issuer certificate already in DB\n"); else log_info ("certificate already in DB\n"); } else if (opt.verbose && !existed) { if (depth) log_info ("issuer certificate imported\n"); else log_info ("certificate imported\n"); } /* Now lets walk up the chain and import all certificates up the chain. This is required in case we already stored parent certificates in the ephemeral keybox. Do not update the statistics, though. */ if (!gpgsm_walk_cert_chain (ctrl, cert, &next)) { check_and_store (ctrl, NULL, next, depth+1); ksba_cert_release (next); } } else { log_error (_("error storing certificate\n")); if (stats) stats->not_imported++; print_import_problem (ctrl, cert, 4); } } else { log_error (_("basic certificate checks failed - not imported\n")); if (stats) stats->not_imported++; /* We keep the test for GPG_ERR_MISSING_CERT only in case GPG_ERR_MISSING_CERT has been used instead of the newer GPG_ERR_MISSING_ISSUER_CERT. */ print_import_problem (ctrl, cert, gpg_err_code (rc) == GPG_ERR_MISSING_ISSUER_CERT? 2 : gpg_err_code (rc) == GPG_ERR_MISSING_CERT? 2 : gpg_err_code (rc) == GPG_ERR_BAD_CERT? 1 : 0); } } static int import_one (ctrl_t ctrl, struct stats_s *stats, int in_fd) { int rc; gnupg_ksba_io_t b64reader = NULL; ksba_reader_t reader; ksba_cert_t cert = NULL; ksba_cms_t cms = NULL; estream_t fp = NULL; ksba_content_type_t ct; int any = 0; fp = es_fdopen_nc (in_fd, "rb"); if (!fp) { rc = gpg_error_from_syserror (); log_error ("fdopen() failed: %s\n", strerror (errno)); goto leave; } rc = gnupg_ksba_create_reader (&b64reader, ((ctrl->is_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->is_base64? GNUPG_KSBA_IO_BASE64 : 0) | (ctrl->autodetect_encoding? GNUPG_KSBA_IO_AUTODETECT : 0) | GNUPG_KSBA_IO_MULTIPEM), fp, &reader); if (rc) { log_error ("can't create reader: %s\n", gpg_strerror (rc)); goto leave; } /* We need to loop here to handle multiple PEM objects in one file. */ do { ksba_cms_release (cms); cms = NULL; ksba_cert_release (cert); cert = NULL; ct = ksba_cms_identify (reader); if (ct == KSBA_CT_SIGNED_DATA) { /* This is probably a signed-only message - import the certs */ ksba_stop_reason_t stopreason; int i; rc = ksba_cms_new (&cms); if (rc) goto leave; rc = ksba_cms_set_reader_writer (cms, reader, NULL); if (rc) { log_error ("ksba_cms_set_reader_writer failed: %s\n", gpg_strerror (rc)); goto leave; } do { rc = ksba_cms_parse (cms, &stopreason); if (rc) { log_error ("ksba_cms_parse failed: %s\n", gpg_strerror (rc)); goto leave; } if (stopreason == KSBA_SR_BEGIN_DATA) log_info ("not a certs-only message\n"); } while (stopreason != KSBA_SR_READY); for (i=0; (cert=ksba_cms_get_cert (cms, i)); i++) { check_and_store (ctrl, stats, cert, 0); ksba_cert_release (cert); cert = NULL; } if (!i) log_error ("no certificate found\n"); else any = 1; } else if (ct == KSBA_CT_PKCS12) { /* This seems to be a pkcs12 message. */ rc = parse_p12 (ctrl, reader, stats); if (!rc) any = 1; } else if (ct == KSBA_CT_NONE) { /* Failed to identify this message - assume a certificate */ rc = ksba_cert_new (&cert); if (rc) goto leave; rc = ksba_cert_read_der (cert, reader); if (rc) goto leave; check_and_store (ctrl, stats, cert, 0); any = 1; } else { log_error ("can't extract certificates from input\n"); rc = gpg_error (GPG_ERR_NO_DATA); } ksba_reader_clear (reader, NULL, NULL); } while (!gnupg_ksba_reader_eof_seen (b64reader)); leave: if (any && gpg_err_code (rc) == GPG_ERR_EOF) rc = 0; ksba_cms_release (cms); ksba_cert_release (cert); gnupg_ksba_destroy_reader (b64reader); es_fclose (fp); return rc; } /* Re-import certifciates. IN_FD is a list of linefeed delimited fingerprints t re-import. The actual re-import is done by clearing the ephemeral flag. */ static int reimport_one (ctrl_t ctrl, struct stats_s *stats, int in_fd) { gpg_error_t err = 0; estream_t fp = NULL; char line[100]; /* Sufficient for a fingerprint. */ KEYDB_HANDLE kh; KEYDB_SEARCH_DESC desc; ksba_cert_t cert = NULL; unsigned int flags; kh = keydb_new (ctrl); if (!kh) { err = gpg_error (GPG_ERR_ENOMEM);; log_error (_("failed to allocate keyDB handle\n")); goto leave; } keydb_set_ephemeral (kh, 1); fp = es_fdopen_nc (in_fd, "r"); if (!fp) { err = gpg_error_from_syserror (); log_error ("es_fdopen(%d) failed: %s\n", in_fd, gpg_strerror (err)); goto leave; } while (es_fgets (line, DIM(line)-1, fp) ) { if (*line && line[strlen(line)-1] != '\n') { err = gpg_error (GPG_ERR_LINE_TOO_LONG); goto leave; } trim_spaces (line); if (!*line) continue; stats->count++; err = classify_user_id (line, &desc, 0); if (err) { print_import_problem (ctrl, NULL, 0); stats->not_imported++; continue; } keydb_search_reset (kh); err = keydb_search (ctrl, kh, &desc, 1); if (err) { print_import_problem (ctrl, NULL, 0); stats->not_imported++; continue; } ksba_cert_release (cert); cert = NULL; err = keydb_get_cert (kh, &cert); if (err) { log_error ("keydb_get_cert() failed: %s\n", gpg_strerror (err)); print_import_problem (ctrl, NULL, 1); stats->not_imported++; continue; } err = keydb_get_flags (kh, KEYBOX_FLAG_BLOB, 0, &flags); if (err) { log_error (_("error getting stored flags: %s\n"), gpg_strerror (err)); print_imported_status (ctrl, cert, 0); stats->not_imported++; continue; } if ( !(flags & KEYBOX_FLAG_BLOB_EPHEMERAL) ) { print_imported_status (ctrl, cert, 0); stats->unchanged++; continue; } err = keydb_set_cert_flags (ctrl, cert, 1, KEYBOX_FLAG_BLOB, 0, KEYBOX_FLAG_BLOB_EPHEMERAL, 0); if (err) { log_error ("clearing ephemeral flag failed: %s\n", gpg_strerror (err)); print_import_problem (ctrl, cert, 0); stats->not_imported++; continue; } print_imported_status (ctrl, cert, 1); stats->imported++; } err = 0; if (es_ferror (fp)) { err = gpg_error_from_syserror (); log_error ("error reading fd %d: %s\n", in_fd, gpg_strerror (err)); goto leave; } leave: ksba_cert_release (cert); keydb_release (kh); es_fclose (fp); return err; } int gpgsm_import (ctrl_t ctrl, int in_fd, int reimport_mode) { int rc; struct stats_s stats; memset (&stats, 0, sizeof stats); if (reimport_mode) rc = reimport_one (ctrl, &stats, in_fd); else rc = import_one (ctrl, &stats, in_fd); print_imported_summary (ctrl, &stats); /* If we never printed an error message do it now so that a command line invocation will return with an error (log_error keeps a global errorcount) */ if (rc && !log_get_errorcount (0)) log_error (_("error importing certificate: %s\n"), gpg_strerror (rc)); return rc; } int gpgsm_import_files (ctrl_t ctrl, int nfiles, char **files, int (*of)(const char *fname)) { int rc = 0; struct stats_s stats; memset (&stats, 0, sizeof stats); if (!nfiles) rc = import_one (ctrl, &stats, 0); else { for (; nfiles && !rc ; nfiles--, files++) { int fd = of (*files); rc = import_one (ctrl, &stats, fd); close (fd); if (rc == -1/* legacy*/ || gpg_err_code (rc) == GPG_ERR_NOT_FOUND) rc = 0; } } print_imported_summary (ctrl, &stats); /* If we never printed an error message do it now so that a command line invocation will return with an error (log_error keeps a global errorcount) */ if (rc && !log_get_errorcount (0)) log_error (_("error importing certificate: %s\n"), gpg_strerror (rc)); return rc; } /* Check that the RSA secret key SKEY is valid. Swap parameters to the libgcrypt standard. */ static gpg_error_t rsa_key_check (struct rsa_secret_key_s *skey) { int err = 0; gcry_mpi_t t = gcry_mpi_snew (0); gcry_mpi_t t1 = gcry_mpi_snew (0); gcry_mpi_t t2 = gcry_mpi_snew (0); gcry_mpi_t phi = gcry_mpi_snew (0); /* Check that n == p * q. */ gcry_mpi_mul (t, skey->p, skey->q); if (gcry_mpi_cmp( t, skey->n) ) { log_error ("RSA oops: n != p * q\n"); err++; } /* Check that p is less than q. */ if (gcry_mpi_cmp (skey->p, skey->q) > 0) { gcry_mpi_t tmp; log_info ("swapping secret primes\n"); tmp = gcry_mpi_copy (skey->p); gcry_mpi_set (skey->p, skey->q); gcry_mpi_set (skey->q, tmp); gcry_mpi_release (tmp); /* Recompute u. */ gcry_mpi_invm (skey->u, skey->p, skey->q); } /* Check that e divides neither p-1 nor q-1. */ gcry_mpi_sub_ui (t, skey->p, 1 ); gcry_mpi_div (NULL, t, t, skey->e, 0); if (!gcry_mpi_cmp_ui( t, 0) ) { log_error ("RSA oops: e divides p-1\n"); err++; } gcry_mpi_sub_ui (t, skey->q, 1); gcry_mpi_div (NULL, t, t, skey->e, 0); if (!gcry_mpi_cmp_ui( t, 0)) { log_info ("RSA oops: e divides q-1\n" ); err++; } /* Check that d is correct. */ gcry_mpi_sub_ui (t1, skey->p, 1); gcry_mpi_sub_ui (t2, skey->q, 1); gcry_mpi_mul (phi, t1, t2); gcry_mpi_invm (t, skey->e, phi); if (gcry_mpi_cmp (t, skey->d)) { /* No: try universal exponent. */ gcry_mpi_gcd (t, t1, t2); gcry_mpi_div (t, NULL, phi, t, 0); gcry_mpi_invm (t, skey->e, t); if (gcry_mpi_cmp (t, skey->d)) { log_error ("RSA oops: bad secret exponent\n"); err++; } } /* Check for correctness of u. */ gcry_mpi_invm (t, skey->p, skey->q); if (gcry_mpi_cmp (t, skey->u)) { log_info ("RSA oops: bad u parameter\n"); err++; } if (err) log_info ("RSA secret key check failed\n"); gcry_mpi_release (t); gcry_mpi_release (t1); gcry_mpi_release (t2); gcry_mpi_release (phi); return err? gpg_error (GPG_ERR_BAD_SECKEY):0; } /* Object passed to store_cert_cb. */ struct store_cert_parm_s { gpg_error_t err; /* First error seen. */ struct stats_s *stats; /* The stats object. */ ctrl_t ctrl; /* The control object. */ }; /* Helper to store the DER encoded certificate CERTDATA of length CERTDATALEN. */ static void store_cert_cb (void *opaque, const unsigned char *certdata, size_t certdatalen) { struct store_cert_parm_s *parm = opaque; gpg_error_t err; ksba_cert_t cert; err = ksba_cert_new (&cert); if (err) { if (!parm->err) parm->err = err; return; } err = ksba_cert_init_from_mem (cert, certdata, certdatalen); if (err) { log_error ("failed to parse a certificate: %s\n", gpg_strerror (err)); if (!parm->err) parm->err = err; } else check_and_store (parm->ctrl, parm->stats, cert, 0); ksba_cert_release (cert); } /* Assume that the reader is at a pkcs#12 message and try to import certificates from that stupid format. We will transfer secret keys to the agent. */ static gpg_error_t parse_p12 (ctrl_t ctrl, ksba_reader_t reader, struct stats_s *stats) { gpg_error_t err = 0; char buffer[1024]; size_t ntotal, nread; membuf_t p12mbuf; char *p12buffer = NULL; size_t p12buflen; size_t p12bufoff; gcry_mpi_t *kparms = NULL; struct rsa_secret_key_s sk; char *passphrase = NULL; unsigned char *key = NULL; size_t keylen; void *kek = NULL; size_t keklen; unsigned char *wrappedkey = NULL; size_t wrappedkeylen; gcry_cipher_hd_t cipherhd = NULL; gcry_sexp_t s_key = NULL; unsigned char grip[20]; int bad_pass = 0; char *curve = NULL; int i; struct store_cert_parm_s store_cert_parm; memset (&store_cert_parm, 0, sizeof store_cert_parm); store_cert_parm.ctrl = ctrl; store_cert_parm.stats = stats; init_membuf (&p12mbuf, 4096); ntotal = 0; while (!(err = ksba_reader_read (reader, buffer, sizeof buffer, &nread))) { if (ntotal >= MAX_P12OBJ_SIZE*1024) { /* Arbitrary limit to avoid DoS attacks. */ err = gpg_error (GPG_ERR_TOO_LARGE); log_error ("pkcs#12 object is larger than %dk\n", MAX_P12OBJ_SIZE); break; } put_membuf (&p12mbuf, buffer, nread); ntotal += nread; } if (gpg_err_code (err) == GPG_ERR_EOF) err = 0; if (!err) { p12buffer = get_membuf (&p12mbuf, &p12buflen); if (!p12buffer) err = gpg_error_from_syserror (); } if (err) { log_error (_("error reading input: %s\n"), gpg_strerror (err)); goto leave; } /* GnuPG 2.0.4 accidentally created binary P12 files with the string "The passphrase is %s encoded.\n\n" prepended to the ASN.1 data. We fix that here. */ if (p12buflen > 29 && !memcmp (p12buffer, "The passphrase is ", 18)) { for (p12bufoff=18; p12bufoff < p12buflen && p12buffer[p12bufoff] != '\n'; p12bufoff++) ; p12bufoff++; if (p12bufoff < p12buflen && p12buffer[p12bufoff] == '\n') p12bufoff++; } else p12bufoff = 0; err = gpgsm_agent_ask_passphrase (ctrl, - i18n_utf8 ("Please enter the passphrase to unprotect the PKCS#12 object."), + i18n_utf8 (N_("Please enter the passphrase to unprotect the PKCS#12 object.")), 0, &passphrase); if (err) goto leave; kparms = p12_parse (p12buffer + p12bufoff, p12buflen - p12bufoff, passphrase, store_cert_cb, &store_cert_parm, &bad_pass, &curve); xfree (passphrase); passphrase = NULL; if (!kparms) { log_error ("error parsing or decrypting the PKCS#12 file\n"); err = gpg_error (GPG_ERR_INV_OBJ); goto leave; } if (curve) { gcry_ctx_t ecctx = NULL; /* log_debug ("curve: %s\n", curve); */ /* gcry_log_debugmpi ("MPI[0]", kparms[0]); */ /* We need to get the public key. */ err = gcry_mpi_ec_new (&ecctx, NULL, curve); if (err) { log_error ("error creating context for curve '%s': %s\n", curve, gpg_strerror (err)); goto leave; } err = gcry_mpi_ec_set_mpi ("d", kparms[0], ecctx); if (err) { log_error ("error setting 'd' into context of curve '%s': %s\n", curve, gpg_strerror (err)); gcry_ctx_release (ecctx); goto leave; } kparms[1] = gcry_mpi_ec_get_mpi ("q", ecctx, 1); if (!kparms[1]) { log_error ("error computing 'q' from 'd' for curve '%s'\n", curve); gcry_ctx_release (ecctx); goto leave; } gcry_ctx_release (ecctx); err = gcry_sexp_build (&s_key, NULL, "(private-key(ecc(curve %s)(q%m)(d%m)))", curve, kparms[1], kparms[0], NULL); } else /* RSA */ { /* print_mpi (" n", kparms[0]); */ /* print_mpi (" e", kparms[1]); */ /* print_mpi (" d", kparms[2]); */ /* print_mpi (" p", kparms[3]); */ /* print_mpi (" q", kparms[4]); */ /* print_mpi ("dmp1", kparms[5]); */ /* print_mpi ("dmq1", kparms[6]); */ /* print_mpi (" u", kparms[7]); */ sk.n = kparms[0]; sk.e = kparms[1]; sk.d = kparms[2]; sk.q = kparms[3]; sk.p = kparms[4]; sk.u = kparms[7]; err = rsa_key_check (&sk); if (err) goto leave; /* print_mpi (" n", sk.n); */ /* print_mpi (" e", sk.e); */ /* print_mpi (" d", sk.d); */ /* print_mpi (" p", sk.p); */ /* print_mpi (" q", sk.q); */ /* print_mpi (" u", sk.u); */ /* Create an S-expression from the parameters. */ err = gcry_sexp_build (&s_key, NULL, "(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))", sk.n, sk.e, sk.d, sk.p, sk.q, sk.u, NULL); } /* The next is very ugly - we really should not rely on our * knowledge of p12_parse internals. */ for (i=0; i < 8; i++) gcry_mpi_release (kparms[i]); gcry_free (kparms); kparms = NULL; if (err) { log_error ("failed to create S-expression from key: %s\n", gpg_strerror (err)); goto leave; } /* Compute the keygrip. */ if (!gcry_pk_get_keygrip (s_key, grip)) { err = gpg_error (GPG_ERR_GENERAL); log_error ("can't calculate keygrip\n"); goto leave; } if (DBG_X509) log_printhex (grip, 20, "keygrip:"); /* Convert to canonical encoding using a function which pads it to a multiple of 64 bits. We need this padding for AESWRAP. */ err = make_canon_sexp_pad (s_key, 1, &key, &keylen); if (err) { log_error ("error creating canonical S-expression\n"); goto leave; } gcry_sexp_release (s_key); s_key = NULL; /* Get the current KEK. */ err = gpgsm_agent_keywrap_key (ctrl, 0, &kek, &keklen); if (err) { log_error ("error getting the KEK: %s\n", gpg_strerror (err)); goto leave; } /* Wrap the key. */ err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_AESWRAP, 0); if (err) goto leave; err = gcry_cipher_setkey (cipherhd, kek, keklen); if (err) goto leave; xfree (kek); kek = NULL; wrappedkeylen = keylen + 8; wrappedkey = xtrymalloc (wrappedkeylen); if (!wrappedkey) { err = gpg_error_from_syserror (); goto leave; } err = gcry_cipher_encrypt (cipherhd, wrappedkey, wrappedkeylen, key, keylen); if (err) goto leave; xfree (key); key = NULL; gcry_cipher_close (cipherhd); cipherhd = NULL; /* Send the wrapped key to the agent. */ err = gpgsm_agent_import_key (ctrl, wrappedkey, wrappedkeylen); if (!err) { stats->count++; stats->secret_read++; stats->secret_imported++; } else if ( gpg_err_code (err) == GPG_ERR_EEXIST ) { err = 0; stats->count++; stats->secret_read++; stats->secret_dups++; } /* If we did not get an error from storing the secret key we return a possible error from parsing the certificates. We do this after storing the secret keys so that a bad certificate does not inhibit our chance to store the secret key. */ if (!err && store_cert_parm.err) err = store_cert_parm.err; leave: if (kparms) { for (i=0; i < 8; i++) gcry_mpi_release (kparms[i]); gcry_free (kparms); kparms = NULL; } xfree (key); gcry_sexp_release (s_key); xfree (passphrase); gcry_cipher_close (cipherhd); xfree (wrappedkey); xfree (kek); xfree (get_membuf (&p12mbuf, NULL)); xfree (p12buffer); xfree (curve); if (bad_pass) { /* We only write a plain error code and not direct BAD_PASSPHRASE because the pkcs12 parser might issue this message multiple times, BAD_PASSPHRASE in general requires a keyID and parts of the import might actually succeed so that IMPORT_PROBLEM is also not appropriate. */ gpgsm_status_with_err_code (ctrl, STATUS_ERROR, "import.parsep12", GPG_ERR_BAD_PASSPHRASE); } return err; }