diff --git a/sm/decrypt.c b/sm/decrypt.c index 0bde2e387..f9dc0376e 100644 --- a/sm/decrypt.c +++ b/sm/decrypt.c @@ -1,981 +1,984 @@ /* decrypt.c - Decrypt a message * Copyright (C) 2001, 2003, 2010 Free Software Foundation, Inc. + * Copyright (C) 2001-2019 Werner Koch + * Copyright (C) 2015-2020 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 #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; }; /* 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 +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; } /* This function will modify SECRET. */ static gpg_error_t ecdh_decrypt (unsigned char *secret, size_t secretlen, 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, hashlen; 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. 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 (*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; hashlen = 32; } else if (!strcmp (encr_algo_str, "1.3.132.1.11.2")) { /* dhSinglePass-stdDH-sha384kdf-scheme */ hash_algo = GCRY_MD_SHA384; hashlen = 48; } else if (!strcmp (encr_algo_str, "1.3.132.1.11.3")) { /* dhSinglePass-stdDH-sha512kdf-scheme */ hash_algo = GCRY_MD_SHA512; hashlen = 64; } 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; } /* Derive a KEK (key wrapping key) using MESSAGE and SECRET_X. * 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. Because with our allowed * parameters KEYLEN is always less or equal to HASHLEN so that we * do not need to iterate at all. */ log_assert (gcry_md_get_algo_dlen (hash_algo) == hashlen); { gcry_md_hd_t hash_hd; err = gcry_md_open (&hash_hd, hash_algo, 0); if (err) goto leave; gcry_md_write(hash_hd, secret, secretlen); gcry_md_write(hash_hd, "\x00\x00\x00\x01", 4); /* counter */ err = hash_ecc_cms_shared_info (hash_hd, wrap_algo_str, keylen, ukm, ukmlen); gcry_md_final (hash_hd); log_assert (keylen <= sizeof key && keylen <= hashlen); memcpy (key, gcry_md_read (hash_hd, 0), keylen); gcry_md_close (hash_hd); 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; } /* 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, const char *desc, ksba_const_sexp_t enc_val, struct decrypt_filter_parm_s *parm) { char *seskey = NULL; size_t n, seskeylen; int rc; if (DBG_CRYPTO) log_printcanon ("decrypting:", enc_val, 0); 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 (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, 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 .....:"); 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; } gcry_cipher_setiv (parm->hd, parm->iv, parm->ivlen); 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) { 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 (); 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; int is_de_vs; /* Computed compliance with CO_DE_VS. */ 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. */ 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 *desc = NULL; char kidbuf[16+1]; int tmp_rc; ksba_cert_t cert = NULL; unsigned int nbits; int pk_algo = 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 (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); pk_algo = gpgsm_get_key_algo_info (cert, &nbits); /* Check compliance. */ if (!gnupg_pk_is_allowed (opt.compliance, PK_USE_DECRYPTION, pk_algo, 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. */ is_de_vs = (is_de_vs && gnupg_pk_is_compliant (CO_DE_VS, pk_algo, NULL, nbits, NULL)); oops: if (rc) { /* We cannot check compliance of certs that we * don't have. */ is_de_vs = 0; } xfree (issuer); xfree (serial); ksba_cert_release (cert); } if (!hexkeygrip || !pk_algo) ; else if (!(enc_val = ksba_cms_get_enc_val (cms, recp))) log_error ("recp %d - error getting encrypted session key\n", recp); else { rc = prepare_decryption (ctrl, hexkeygrip, pk_algo, 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 (is_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 (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/encrypt.c b/sm/encrypt.c index 6213a6604..865b103c1 100644 --- a/sm/encrypt.c +++ b/sm/encrypt.c @@ -1,577 +1,870 @@ /* encrypt.c - Encrypt a message * Copyright (C) 2001, 2003, 2004, 2007, 2008, * 2010 Free Software Foundation, Inc. + * Copyright (C) 2001-2019 Werner Koch + * Copyright (C) 2015-2020 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 #include "gpgsm.h" #include #include #include "keydb.h" #include "../common/i18n.h" #include "../common/compliance.h" struct dek_s { const char *algoid; int algo; gcry_cipher_hd_t chd; char key[32]; int keylen; char iv[32]; int ivlen; }; typedef struct dek_s *DEK; /* Callback parameters for the encryption. */ struct encrypt_cb_parm_s { estream_t fp; DEK dek; int eof_seen; int ready; int readerror; int bufsize; unsigned char *buffer; int buflen; }; /* Initialize the data encryption key (session key). */ static int init_dek (DEK dek) { int rc=0, mode, i; dek->algo = gcry_cipher_map_name (dek->algoid); mode = gcry_cipher_mode_from_oid (dek->algoid); if (!dek->algo || !mode) { log_error ("unsupported algorithm '%s'\n", dek->algoid); return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); } /* Extra check for algorithms we consider to be too weak for encryption, although we support them for decryption. Note that there is another check below discriminating on the key length. */ switch (dek->algo) { case GCRY_CIPHER_DES: case GCRY_CIPHER_RFC2268_40: log_error ("cipher algorithm '%s' not allowed: too weak\n", gnupg_cipher_algo_name (dek->algo)); return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); default: break; } dek->keylen = gcry_cipher_get_algo_keylen (dek->algo); if (!dek->keylen || dek->keylen > sizeof (dek->key)) return gpg_error (GPG_ERR_BUG); dek->ivlen = gcry_cipher_get_algo_blklen (dek->algo); if (!dek->ivlen || dek->ivlen > sizeof (dek->iv)) return gpg_error (GPG_ERR_BUG); /* Make sure we don't use weak keys. */ if (dek->keylen < 100/8) { log_error ("key length of '%s' too small\n", dek->algoid); return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); } rc = gcry_cipher_open (&dek->chd, dek->algo, mode, GCRY_CIPHER_SECURE); if (rc) { log_error ("failed to create cipher context: %s\n", gpg_strerror (rc)); return rc; } for (i=0; i < 8; i++) { gcry_randomize (dek->key, dek->keylen, GCRY_STRONG_RANDOM ); rc = gcry_cipher_setkey (dek->chd, dek->key, dek->keylen); if (gpg_err_code (rc) != GPG_ERR_WEAK_KEY) break; log_info(_("weak key created - retrying\n") ); } if (rc) { log_error ("failed to set the key: %s\n", gpg_strerror (rc)); gcry_cipher_close (dek->chd); dek->chd = NULL; return rc; } gcry_create_nonce (dek->iv, dek->ivlen); rc = gcry_cipher_setiv (dek->chd, dek->iv, dek->ivlen); if (rc) { log_error ("failed to set the IV: %s\n", gpg_strerror (rc)); gcry_cipher_close (dek->chd); dek->chd = NULL; return rc; } return 0; } - +/* Encode an RSA session key. */ static int encode_session_key (DEK dek, gcry_sexp_t * r_data) { gcry_sexp_t data; char *p; int rc; p = xtrymalloc (64 + 2 * dek->keylen); if (!p) return gpg_error_from_syserror (); strcpy (p, "(data\n (flags pkcs1)\n (value #"); bin2hex (dek->key, dek->keylen, p + strlen (p)); strcat (p, "#))\n"); rc = gcry_sexp_sscan (&data, NULL, p, strlen (p)); xfree (p); *r_data = data; return rc; } +/* Encrypt DEK using ECDH. S_PKEY is the public key. On success the + * result is stored at R_ENCVAL. Example of a public key: + * + * (public-key (ecc (curve "1.3.132.0.34") (q #04B0[...]B8#))) + * + */ +static gpg_error_t +ecdh_encrypt (DEK dek, gcry_sexp_t s_pkey, gcry_sexp_t *r_encval) +{ + gpg_error_t err; + gcry_sexp_t l1; + char *curvebuf = NULL; + const char *curve; + unsigned int curvebits; + const char *encr_algo_str; + const char *wrap_algo_str; + int hash_algo, cipher_algo; + unsigned int keylen, hashlen; + unsigned char key[32]; + gcry_sexp_t s_data = NULL; + gcry_sexp_t s_encr = NULL; + gcry_buffer_t ioarray[2] = { {0}, {0} }; + unsigned char *secret; /* Alias for ioarray[0]. */ + unsigned int secretlen; + unsigned char *pubkey; /* Alias for ioarray[1]. */ + unsigned int pubkeylen; + gcry_cipher_hd_t cipher_hd = NULL; + unsigned char *result = NULL; + unsigned int resultlen; + + *r_encval = NULL; + + /* Figure out the encryption and wrap algo OIDs. */ + /* Get the curve name if any, */ + l1 = gcry_sexp_find_token (s_pkey, "curve", 0); + if (l1) + { + curvebuf = gcry_sexp_nth_string (l1, 1); + gcry_sexp_release (l1); + } + if (!curvebuf) + { + err = gpg_error (GPG_ERR_INV_CURVE); + log_error ("%s: invalid public key: no curve\n", __func__); + goto leave; + } + + /* We need to use our OpenPGP mapping to turn a curve name into its + * canonical numerical OID. We also use this to get the size of the + * curve which we need to figure out a suitable hash algo. We + * should have a Libgcrypt function to do this; see bug report #4926. */ + curve = openpgp_curve_to_oid (curvebuf, &curvebits, NULL); + if (!curve) + { + err = gpg_error (GPG_ERR_UNKNOWN_CURVE); + log_error ("%s: invalid public key: %s\n", __func__, gpg_strerror (err)); + goto leave; + } + xfree (curvebuf); + curvebuf = NULL; + + /* Our mapping matches the recommended algorithms from RFC-5753 but + * not supporing the short curves which would require 3DES. */ + if (curvebits < 255) + { + err = gpg_error (GPG_ERR_UNKNOWN_CURVE); + log_error ("%s: curve '%s' is not supported\n", __func__, curve); + goto leave; + } + else if (curvebits <= 256) + { + /* dhSinglePass-stdDH-sha256kdf-scheme */ + encr_algo_str = "1.3.132.1.11.1"; + wrap_algo_str = "2.16.840.1.101.3.4.1.5"; + hash_algo = GCRY_MD_SHA256; + hashlen = 32; + cipher_algo = GCRY_CIPHER_AES128; + keylen = 16; + } + else if (curvebits <= 384) + { + /* dhSinglePass-stdDH-sha384kdf-scheme */ + encr_algo_str = "1.3.132.1.11.2"; + wrap_algo_str = "2.16.840.1.101.3.4.1.25"; + hash_algo = GCRY_MD_SHA384; + hashlen = 48; + cipher_algo = GCRY_CIPHER_AES256; + keylen = 24; + } + else + { + /* dhSinglePass-stdDH-sha512kdf-scheme*/ + encr_algo_str = "1.3.132.1.11.3"; + wrap_algo_str = "2.16.840.1.101.3.4.1.45"; + hash_algo = GCRY_MD_SHA512; + hashlen = 64; + cipher_algo = GCRY_CIPHER_AES256; + keylen = 32; + } + + + /* Create a secret and an ephemeral key. */ + { + char *k; + k = gcry_random_bytes_secure ((curvebits+7)/8, GCRY_STRONG_RANDOM); + if (DBG_CRYPTO) + log_printhex (k, (curvebits+7)/8, "ephm. k .:"); + err = gcry_sexp_build (&s_data, NULL, "%b", (int)(curvebits+7)/8, k); + xfree (k); + } + if (err) + { + log_error ("%s: error building ephemeral secret: %s\n", + __func__, gpg_strerror (err)); + goto leave; + } + + err = gcry_pk_encrypt (&s_encr, s_data, s_pkey); + if (err) + { + log_error ("%s: error encrypting ephemeral secret: %s\n", + __func__, gpg_strerror (err)); + goto leave; + } + err = gcry_sexp_extract_param (s_encr, NULL, "&se", + &ioarray+0, ioarray+1, NULL); + if (err) + { + log_error ("%s: error extracting ephemeral key and secret: %s\n", + __func__, gpg_strerror (err)); + goto leave; + } + secret = ioarray[0].data; + secretlen = ioarray[0].len; + pubkey = ioarray[1].data; + pubkeylen = ioarray[1].len; + + if (DBG_CRYPTO) + { + log_printhex (pubkey, pubkeylen, "pubkey ..:"); + log_printhex (secret, secretlen, "secret ..:"); + } + + /* Extract X coordinate from SECRET. */ + if (secretlen < 5) /* 5 because N could be reduced to (n-1)/2. */ + err = gpg_error (GPG_ERR_BAD_DATA); + else if (*secret == 0x04) + { + secretlen--; + memmove (secret, secret+1, secretlen); + if ((secretlen & 1)) + { + err = gpg_error (GPG_ERR_BAD_DATA); + goto leave; + } + secretlen /= 2; + } + else if (*secret == 0x40 || *secret == 0x41) + { + secretlen--; + memmove (secret, secret+1, secretlen); + } + else + err = gpg_error (GPG_ERR_BAD_DATA); + if (err) + goto leave; + + if (DBG_CRYPTO) + log_printhex (secret, secretlen, "ECDH X ..:"); + + /* Derive a KEK (key wrapping key) using MESSAGE and SECRET_X. + * 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. Because with our allowed + * parameters KEYLEN is always less or equal to HASHLEN so that we + * do not need to iterate at all. + */ + log_assert (gcry_md_get_algo_dlen (hash_algo) == hashlen); + { + gcry_md_hd_t hash_hd; + err = gcry_md_open (&hash_hd, hash_algo, 0); + if (err) + goto leave; + gcry_md_write(hash_hd, secret, secretlen); + gcry_md_write(hash_hd, "\x00\x00\x00\x01", 4); /* counter */ + err = hash_ecc_cms_shared_info (hash_hd, wrap_algo_str, keylen, NULL, 0); + gcry_md_final (hash_hd); + log_assert (keylen <= sizeof key && keylen <= hashlen); + memcpy (key, gcry_md_read (hash_hd, 0), keylen); + gcry_md_close (hash_hd); + if (err) + goto leave; + } + + if (DBG_CRYPTO) + log_printhex (key, keylen, "KEK .....:"); + + /* Wrap the key. */ + if ((dek->keylen % 8) || dek->keylen < 16) + { + log_error ("%s: can't use a session key of %u bytes\n", + __func__, dek->keylen); + err = gpg_error (GPG_ERR_BAD_DATA); + goto leave; + } + + resultlen = dek->keylen + 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 ("%s: failed to initialize AESWRAP: %s\n", + __func__, gpg_strerror (err)); + goto leave; + } + + err = gcry_cipher_setkey (cipher_hd, key, keylen); + wipememory (key, sizeof key); + if (err) + { + log_error ("%s: failed in gcry_cipher_setkey: %s\n", + __func__, gpg_strerror (err)); + goto leave; + } + + err = gcry_cipher_encrypt (cipher_hd, result, resultlen, + dek->key, dek->keylen); + if (err) + { + log_error ("%s: failed in gcry_cipher_encrypt: %s\n", + __func__, gpg_strerror (err)); + goto leave; + } + + if (DBG_CRYPTO) + log_printhex (result, resultlen, "w(CEK) ..:"); + + err = gcry_sexp_build (r_encval, NULL, + "(enc-val(ecdh(e%b)(s%b)(encr-algo%s)(wrap-algo%s)))", + (int)pubkeylen, pubkey, + (int)resultlen, result, + encr_algo_str, + wrap_algo_str, + NULL); + if (err) + log_error ("%s: failed building final S-exp: %s\n", + __func__, gpg_strerror (err)); + + leave: + gcry_cipher_close (cipher_hd); + wipememory (key, sizeof key); + xfree (result); + xfree (ioarray[0].data); + xfree (ioarray[1].data); + gcry_sexp_release (s_data); + gcry_sexp_release (s_encr); + xfree (curvebuf); + return err; +} + + /* Encrypt the DEK under the key contained in CERT and return it as a - canonical S-Exp in encval. */ + * canonical S-expressions at ENCVAL. PK_ALGO is the public key + * algorithm which the caller has already retrieved from CERT. */ static int -encrypt_dek (const DEK dek, ksba_cert_t cert, unsigned char **encval) +encrypt_dek (const DEK dek, ksba_cert_t cert, int pk_algo, + unsigned char **encval) { gcry_sexp_t s_ciph, s_data, s_pkey; int rc; ksba_sexp_t buf; size_t len; *encval = NULL; /* get the key from the cert */ buf = ksba_cert_get_public_key (cert); if (!buf) { log_error ("no public key for recipient\n"); return gpg_error (GPG_ERR_NO_PUBKEY); } len = gcry_sexp_canon_len (buf, 0, NULL, NULL); if (!len) { log_error ("libksba did not return a proper S-Exp\n"); return gpg_error (GPG_ERR_BUG); } rc = gcry_sexp_sscan (&s_pkey, NULL, (char*)buf, len); xfree (buf); buf = NULL; if (rc) { log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc)); return rc; } + if (DBG_CRYPTO) + { + log_printsexp (" pubkey:", s_pkey); + log_printhex (dek->key, dek->keylen, "CEK .....:"); + } + /* Put the encoded cleartext into a simple list. */ s_data = NULL; /* (avoid compiler warning) */ - rc = encode_session_key (dek, &s_data); - if (rc) + if (pk_algo == GCRY_PK_ECC) { - log_error ("encode_session_key failed: %s\n", gpg_strerror (rc)); - return rc; + rc = ecdh_encrypt (dek, s_pkey, &s_ciph); } + else + { + rc = encode_session_key (dek, &s_data); + if (rc) + { + log_error ("encode_session_key failed: %s\n", gpg_strerror (rc)); + return rc; + } + if (DBG_CRYPTO) + log_printsexp (" data:", s_data); - /* pass it to libgcrypt */ - rc = gcry_pk_encrypt (&s_ciph, s_data, s_pkey); + /* pass it to libgcrypt */ + rc = gcry_pk_encrypt (&s_ciph, s_data, s_pkey); + } gcry_sexp_release (s_data); gcry_sexp_release (s_pkey); + if (DBG_CRYPTO) + log_printsexp ("enc-val:", s_ciph); + /* Reformat it. */ if (!rc) { rc = make_canon_sexp (s_ciph, encval, NULL); gcry_sexp_release (s_ciph); } return rc; } /* do the actual encryption */ static int encrypt_cb (void *cb_value, char *buffer, size_t count, size_t *nread) { struct encrypt_cb_parm_s *parm = cb_value; int blklen = parm->dek->ivlen; unsigned char *p; size_t n; *nread = 0; if (!buffer) return -1; /* not supported */ if (parm->ready) return -1; if (count < blklen) BUG (); if (!parm->eof_seen) { /* fillup the buffer */ p = parm->buffer; for (n=parm->buflen; n < parm->bufsize; n++) { int c = es_getc (parm->fp); if (c == EOF) { if (es_ferror (parm->fp)) { parm->readerror = errno; return -1; } parm->eof_seen = 1; break; } p[n] = c; } parm->buflen = n; } n = parm->buflen < count? parm->buflen : count; n = n/blklen * blklen; if (n) { /* encrypt the stuff */ gcry_cipher_encrypt (parm->dek->chd, buffer, n, parm->buffer, n); *nread = n; /* Who cares about cycles, take the easy way and shift the buffer */ parm->buflen -= n; memmove (parm->buffer, parm->buffer+n, parm->buflen); } else if (parm->eof_seen) { /* no complete block but eof: add padding */ /* fixme: we should try to do this also in the above code path */ int i, npad = blklen - (parm->buflen % blklen); p = parm->buffer; for (n=parm->buflen, i=0; n < parm->bufsize && i < npad; n++, i++) p[n] = npad; gcry_cipher_encrypt (parm->dek->chd, buffer, n, parm->buffer, n); *nread = n; parm->ready = 1; } return 0; } /* Perform an encrypt operation. Encrypt the data received on DATA-FD and write it to OUT_FP. The recipients are take from the certificate given in recplist; if this is NULL it will be encrypted for a default recipient */ int gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp) { int rc = 0; gnupg_ksba_io_t b64writer = NULL; gpg_error_t err; ksba_writer_t writer; ksba_reader_t reader = NULL; ksba_cms_t cms = NULL; ksba_stop_reason_t stopreason; KEYDB_HANDLE kh = NULL; struct encrypt_cb_parm_s encparm; DEK dek = NULL; int recpno; estream_t data_fp = NULL; certlist_t cl; int count; int compliant; memset (&encparm, 0, sizeof encparm); audit_set_type (ctrl->audit, AUDIT_TYPE_ENCRYPT); /* Check that the certificate list is not empty and that at least one certificate is not flagged as encrypt_to; i.e. is a real recipient. */ for (cl = recplist; cl; cl = cl->next) if (!cl->is_encrypt_to) break; if (!cl) { log_error(_("no valid recipients given\n")); gpgsm_status (ctrl, STATUS_NO_RECP, "0"); audit_log_i (ctrl->audit, AUDIT_GOT_RECIPIENTS, 0); rc = gpg_error (GPG_ERR_NO_PUBKEY); goto leave; } for (count = 0, cl = recplist; cl; cl = cl->next) count++; audit_log_i (ctrl->audit, AUDIT_GOT_RECIPIENTS, count); kh = keydb_new (); if (!kh) { log_error (_("failed to allocate keyDB handle\n")); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } /* Fixme: We should use the unlocked version of the es functions. */ data_fp = es_fdopen_nc (data_fd, "rb"); if (!data_fp) { rc = gpg_error_from_syserror (); log_error ("fdopen() failed: %s\n", strerror (errno)); goto leave; } err = ksba_reader_new (&reader); if (err) rc = err; if (!rc) rc = ksba_reader_set_cb (reader, encrypt_cb, &encparm); if (rc) goto leave; encparm.fp = data_fp; ctrl->pem_name = "ENCRYPTED MESSAGE"; 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; } err = ksba_cms_new (&cms); if (err) { rc = err; goto leave; } err = ksba_cms_set_reader_writer (cms, reader, writer); if (err) { - log_debug ("ksba_cms_set_reader_writer failed: %s\n", + log_error ("ksba_cms_set_reader_writer failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } audit_log (ctrl->audit, AUDIT_GOT_DATA); /* We are going to create enveloped data with uninterpreted data as inner content */ err = ksba_cms_set_content_type (cms, 0, KSBA_CT_ENVELOPED_DATA); if (!err) err = ksba_cms_set_content_type (cms, 1, KSBA_CT_DATA); if (err) { - log_debug ("ksba_cms_set_content_type failed: %s\n", + log_error ("ksba_cms_set_content_type failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } /* Check compliance. */ if (!gnupg_cipher_is_allowed (opt.compliance, 1, gcry_cipher_map_name (opt.def_cipher_algoid), gcry_cipher_mode_from_oid (opt.def_cipher_algoid))) { log_error (_("cipher algorithm '%s' may not be used in %s mode\n"), opt.def_cipher_algoid, gnupg_compliance_option_string (opt.compliance)); rc = gpg_error (GPG_ERR_CIPHER_ALGO); goto leave; } if (!gnupg_rng_is_compliant (opt.compliance)) { rc = gpg_error (GPG_ERR_FORBIDDEN); log_error (_("%s is not compliant with %s mode\n"), "RNG", gnupg_compliance_option_string (opt.compliance)); gpgsm_status_with_error (ctrl, STATUS_ERROR, "random-compliance", rc); goto leave; } /* Create a session key */ dek = xtrycalloc_secure (1, sizeof *dek); if (!dek) rc = out_of_core (); else { dek->algoid = opt.def_cipher_algoid; rc = init_dek (dek); } if (rc) { log_error ("failed to create the session key: %s\n", gpg_strerror (rc)); goto leave; } err = ksba_cms_set_content_enc_algo (cms, dek->algoid, dek->iv, dek->ivlen); if (err) { log_error ("ksba_cms_set_content_enc_algo failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } encparm.dek = dek; /* Use a ~8k (AES) or ~4k (3DES) buffer */ encparm.bufsize = 500 * dek->ivlen; encparm.buffer = xtrymalloc (encparm.bufsize); if (!encparm.buffer) { rc = out_of_core (); goto leave; } audit_log_s (ctrl->audit, AUDIT_SESSION_KEY, dek->algoid); compliant = gnupg_cipher_is_compliant (CO_DE_VS, dek->algo, GCRY_CIPHER_MODE_CBC); /* Gather certificates of recipients, encrypt the session key for each and store them in the CMS object */ for (recpno = 0, cl = recplist; cl; recpno++, cl = cl->next) { unsigned char *encval; unsigned int nbits; int pk_algo; /* Check compliance. */ pk_algo = gpgsm_get_key_algo_info (cl->cert, &nbits); if (!gnupg_pk_is_compliant (opt.compliance, pk_algo, NULL, nbits, NULL)) { char kidstr[10+1]; snprintf (kidstr, sizeof kidstr, "0x%08lX", gpgsm_get_short_fingerprint (cl->cert, NULL)); log_info (_("WARNING: key %s is not suitable for encryption" " in %s mode\n"), kidstr, gnupg_compliance_option_string (opt.compliance)); } /* Fixme: When adding ECC we need to provide the curvename and * the key to gnupg_pk_is_compliant. */ if (compliant && !gnupg_pk_is_compliant (CO_DE_VS, pk_algo, NULL, nbits, NULL)) compliant = 0; - rc = encrypt_dek (dek, cl->cert, &encval); + rc = encrypt_dek (dek, cl->cert, pk_algo, &encval); if (rc) { audit_log_cert (ctrl->audit, AUDIT_ENCRYPTED_TO, cl->cert, rc); log_error ("encryption failed for recipient no. %d: %s\n", recpno, gpg_strerror (rc)); goto leave; } err = ksba_cms_add_recipient (cms, cl->cert); if (err) { audit_log_cert (ctrl->audit, AUDIT_ENCRYPTED_TO, cl->cert, err); log_error ("ksba_cms_add_recipient failed: %s\n", gpg_strerror (err)); rc = err; xfree (encval); goto leave; } err = ksba_cms_set_enc_val (cms, recpno, encval); xfree (encval); audit_log_cert (ctrl->audit, AUDIT_ENCRYPTED_TO, cl->cert, err); if (err) { log_error ("ksba_cms_set_enc_val failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } } if (compliant) gpgsm_status (ctrl, STATUS_ENCRYPTION_COMPLIANCE_MODE, gnupg_status_compliance_flag (CO_DE_VS)); /* Main control loop for encryption. */ recpno = 0; do { err = ksba_cms_build (cms, &stopreason); if (err) { log_debug ("ksba_cms_build failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } } while (stopreason != KSBA_SR_READY); if (encparm.readerror) { log_error ("error reading input: %s\n", strerror (encparm.readerror)); rc = gpg_error (gpg_err_code_from_errno (encparm.readerror)); goto leave; } rc = gnupg_ksba_finish_writer (b64writer); if (rc) { log_error ("write failed: %s\n", gpg_strerror (rc)); goto leave; } audit_log (ctrl->audit, AUDIT_ENCRYPTION_DONE); log_info ("encrypted data created\n"); leave: ksba_cms_release (cms); gnupg_ksba_destroy_writer (b64writer); ksba_reader_release (reader); keydb_release (kh); xfree (dek); es_fclose (data_fp); xfree (encparm.buffer); return rc; } diff --git a/sm/gpgsm.h b/sm/gpgsm.h index 60b20cc22..580f2d6f6 100644 --- a/sm/gpgsm.h +++ b/sm/gpgsm.h @@ -1,466 +1,470 @@ /* gpgsm.h - Global definitions for GpgSM * Copyright (C) 2001, 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 . */ #ifndef GPGSM_H #define GPGSM_H #ifdef GPG_ERR_SOURCE_DEFAULT #error GPG_ERR_SOURCE_DEFAULT already defined #endif #define GPG_ERR_SOURCE_DEFAULT GPG_ERR_SOURCE_GPGSM #include #include #include "../common/util.h" #include "../common/status.h" #include "../common/audit.h" #include "../common/session-env.h" #include "../common/ksba-io-support.h" #include "../common/compliance.h" #define MAX_DIGEST_LEN 64 struct keyserver_spec { struct keyserver_spec *next; char *host; int port; char *user; char *pass; char *base; unsigned int use_ldaps:1; }; /* A large struct named "opt" to keep global flags. */ EXTERN_UNLESS_MAIN_MODULE struct { unsigned int debug; /* debug flags (DBG_foo_VALUE) */ int verbose; /* verbosity level */ int quiet; /* be as quiet as possible */ int batch; /* run in batch mode, i.e w/o any user interaction */ int answer_yes; /* assume yes on most questions */ int answer_no; /* assume no on most questions */ int dry_run; /* don't change any persistent data */ int no_homedir_creation; const char *config_filename; /* Name of the used config file. */ const char *agent_program; session_env_t session_env; char *lc_ctype; char *lc_messages; int autostart; const char *dirmngr_program; int disable_dirmngr; /* Do not do any dirmngr calls. */ const char *protect_tool_program; char *outfile; /* name of output file */ int with_key_data;/* include raw key in the column delimited output */ int fingerprint; /* list fingerprints in all key listings */ int with_md5_fingerprint; /* Also print an MD5 fingerprint for standard key listings. */ int with_keygrip; /* Option --with-keygrip active. */ int with_key_screening; /* Option --with-key-screening active. */ int pinentry_mode; int request_origin; int armor; /* force base64 armoring (see also ctrl.with_base64) */ int no_armor; /* don't try to figure out whether data is base64 armored*/ const char *p12_charset; /* Use this charset for encoding the pkcs#12 passphrase. */ const char *def_cipher_algoid; /* cipher algorithm to use if nothing else is specified */ int def_compress_algo; /* Ditto for compress algorithm */ int forced_digest_algo; /* User forced hash algorithm. */ char *def_recipient; /* userID of the default recipient */ int def_recipient_self; /* The default recipient is the default key */ int no_encrypt_to; /* Ignore all as encrypt to marked recipients. */ char *local_user; /* NULL or argument to -u */ int extra_digest_algo; /* A digest algorithm also used for verification of signatures. */ int always_trust; /* Trust the given keys even if there is no valid certification chain */ int skip_verify; /* do not check signatures on data */ int lock_once; /* Keep lock once they are set */ int ignore_time_conflict; /* Ignore certain time conflicts */ int no_crl_check; /* Don't do a CRL check */ int no_trusted_cert_crl_check; /* Don't run a CRL check for trusted certs. */ int force_crl_refresh; /* Force refreshing the CRL. */ int enable_issuer_based_crl_check; /* Backward compatibility hack. */ int enable_ocsp; /* Default to use OCSP checks. */ char *policy_file; /* full pathname of policy file */ int no_policy_check; /* ignore certificate policies */ int no_chain_validation; /* Bypass all cert chain validity tests */ int ignore_expiration; /* Ignore the notAfter validity checks. */ int auto_issuer_key_retrieve; /* try to retrieve a missing issuer key. */ int qualsig_approval; /* Set to true if this software has officially been approved to create an verify qualified signatures. This is a runtime option in case we want to check the integrity of the software at runtime. */ struct keyserver_spec *keyserver; /* A list of certificate extension OIDs which are ignored so that one can claim that a critical extension has been handled. One OID per string. */ strlist_t ignored_cert_extensions; enum gnupg_compliance_mode compliance; /* Enable creation of authenticode signatures. */ int authenticode; /* A list of extra attributes put into a signed data object. For a * signed each attribute each string has the format: * :s: * and for an unsigned attribute * :u: * The OID is in the usual dotted decimal for. The HEX_OR_FILENAME * is either a list of hex digits or a filename with the DER encoded * value. A filename is detected by the presence of a slash in the * HEX_OR_FILENAME. The actual value needs to be encoded as a SET OF * attribute values. */ strlist_t attributes; } opt; /* Debug values and macros. */ #define DBG_X509_VALUE 1 /* debug x.509 data reading/writing */ #define DBG_MPI_VALUE 2 /* debug mpi details */ #define DBG_CRYPTO_VALUE 4 /* debug low level crypto */ #define DBG_MEMORY_VALUE 32 /* debug memory allocation stuff */ #define DBG_CACHE_VALUE 64 /* debug the caching */ #define DBG_MEMSTAT_VALUE 128 /* show memory statistics */ #define DBG_HASHING_VALUE 512 /* debug hashing operations */ #define DBG_IPC_VALUE 1024 /* debug assuan communication */ #define DBG_CLOCK_VALUE 4096 #define DBG_LOOKUP_VALUE 8192 /* debug the key lookup */ #define DBG_X509 (opt.debug & DBG_X509_VALUE) #define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE) #define DBG_MEMORY (opt.debug & DBG_MEMORY_VALUE) #define DBG_CACHE (opt.debug & DBG_CACHE_VALUE) #define DBG_HASHING (opt.debug & DBG_HASHING_VALUE) #define DBG_IPC (opt.debug & DBG_IPC_VALUE) #define DBG_CLOCK (opt.debug & DBG_CLOCK_VALUE) #define DBG_LOOKUP (opt.debug & DBG_LOOKUP_VALUE) /* Forward declaration for an object defined in server.c */ struct server_local_s; /* Session control object. This object is passed down to most functions. Note that the default values for it are set by gpgsm_init_default_ctrl(). */ struct server_control_s { int no_server; /* We are not running under server control */ int status_fd; /* Only for non-server mode */ struct server_local_s *server_local; audit_ctx_t audit; /* NULL or a context for the audit subsystem. */ int agent_seen; /* Flag indicating that the gpg-agent has been accessed. */ int with_colons; /* Use column delimited output format */ int with_secret; /* Mark secret keys in a public key listing. */ int with_chain; /* Include the certifying certs in a listing */ int with_validation;/* Validate each key while listing. */ int with_ephemeral_keys; /* Include ephemeral flagged keys in the keylisting. */ int autodetect_encoding; /* Try to detect the input encoding */ int is_pem; /* Is in PEM format */ int is_base64; /* is in plain base-64 format */ int create_base64; /* Create base64 encoded output */ int create_pem; /* create PEM output */ const char *pem_name; /* PEM name to use */ int include_certs; /* -1 to send all certificates in the chain along with a signature or the number of certificates up the chain (0 = none, 1 = only signer) */ int use_ocsp; /* Set to true if OCSP should be used. */ int validation_model; /* 0 := standard model (shell), 1 := chain model, 2 := STEED model. */ int offline; /* If true gpgsm won't do any network access. */ /* The current time. Used as a helper in certchain.c. */ ksba_isotime_t current_time; }; /* An object to keep a list of certificates. */ struct certlist_s { struct certlist_s *next; ksba_cert_t cert; int is_encrypt_to; /* True if the certificate has been set through the --encrypto-to option. */ int hash_algo; /* Used to track the hash algorithm to use. */ const char *hash_algo_oid; /* And the corresponding OID. */ }; typedef struct certlist_s *certlist_t; /* A structure carrying information about trusted root certificates. */ struct rootca_flags_s { unsigned int valid:1; /* The rest of the structure has valid information. */ unsigned int relax:1; /* Relax checking of root certificates. */ unsigned int chain_model:1; /* Root requires the use of the chain model. */ }; /*-- gpgsm.c --*/ void gpgsm_exit (int rc); void gpgsm_init_default_ctrl (struct server_control_s *ctrl); int gpgsm_parse_validation_model (const char *model); /*-- server.c --*/ void gpgsm_server (certlist_t default_recplist); gpg_error_t gpgsm_status (ctrl_t ctrl, int no, const char *text); gpg_error_t gpgsm_status2 (ctrl_t ctrl, int no, ...) GPGRT_ATTR_SENTINEL(0); gpg_error_t gpgsm_status_with_err_code (ctrl_t ctrl, int no, const char *text, gpg_err_code_t ec); gpg_error_t gpgsm_status_with_error (ctrl_t ctrl, int no, const char *text, gpg_error_t err); gpg_error_t gpgsm_proxy_pinentry_notify (ctrl_t ctrl, const unsigned char *line); /*-- fingerprint --*/ unsigned char *gpgsm_get_fingerprint (ksba_cert_t cert, int algo, unsigned char *array, int *r_len); char *gpgsm_get_fingerprint_string (ksba_cert_t cert, int algo); char *gpgsm_get_fingerprint_hexstring (ksba_cert_t cert, int algo); unsigned long gpgsm_get_short_fingerprint (ksba_cert_t cert, unsigned long *r_high); unsigned char *gpgsm_get_keygrip (ksba_cert_t cert, unsigned char *array); char *gpgsm_get_keygrip_hexstring (ksba_cert_t cert); int gpgsm_get_key_algo_info (ksba_cert_t cert, unsigned int *nbits); gcry_mpi_t gpgsm_get_rsa_modulus (ksba_cert_t cert); char *gpgsm_get_certid (ksba_cert_t cert); /*-- certdump.c --*/ void gpgsm_print_serial (estream_t fp, ksba_const_sexp_t p); void gpgsm_print_time (estream_t fp, ksba_isotime_t t); void gpgsm_print_name2 (FILE *fp, const char *string, int translate); void gpgsm_print_name (FILE *fp, const char *string); void gpgsm_es_print_name (estream_t fp, const char *string); void gpgsm_es_print_name2 (estream_t fp, const char *string, int translate); void gpgsm_cert_log_name (const char *text, ksba_cert_t cert); void gpgsm_dump_cert (const char *text, ksba_cert_t cert); void gpgsm_dump_serial (ksba_const_sexp_t p); void gpgsm_dump_time (ksba_isotime_t t); void gpgsm_dump_string (const char *string); char *gpgsm_format_serial (ksba_const_sexp_t p); char *gpgsm_format_name2 (const char *name, int translate); char *gpgsm_format_name (const char *name); char *gpgsm_format_sn_issuer (ksba_sexp_t sn, const char *issuer); char *gpgsm_fpr_and_name_for_status (ksba_cert_t cert); char *gpgsm_format_keydesc (ksba_cert_t cert); /*-- certcheck.c --*/ int gpgsm_check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert); int gpgsm_check_cms_signature (ksba_cert_t cert, ksba_const_sexp_t sigval, gcry_md_hd_t md, int hash_algo, int *r_pkalgo); /* fixme: move create functions to another file */ int gpgsm_create_cms_signature (ctrl_t ctrl, ksba_cert_t cert, gcry_md_hd_t md, int mdalgo, unsigned char **r_sigval); /*-- certchain.c --*/ /* Flags used with gpgsm_validate_chain. */ #define VALIDATE_FLAG_NO_DIRMNGR 1 #define VALIDATE_FLAG_CHAIN_MODEL 2 #define VALIDATE_FLAG_STEED 4 int gpgsm_walk_cert_chain (ctrl_t ctrl, ksba_cert_t start, ksba_cert_t *r_next); int gpgsm_is_root_cert (ksba_cert_t cert); int gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime, ksba_isotime_t r_exptime, int listmode, estream_t listfp, unsigned int flags, unsigned int *retflags); int gpgsm_basic_cert_check (ctrl_t ctrl, ksba_cert_t cert); /*-- certlist.c --*/ int gpgsm_cert_use_sign_p (ksba_cert_t cert, int silent); int gpgsm_cert_use_encrypt_p (ksba_cert_t cert); int gpgsm_cert_use_verify_p (ksba_cert_t cert); int gpgsm_cert_use_decrypt_p (ksba_cert_t cert); int gpgsm_cert_use_cert_p (ksba_cert_t cert); int gpgsm_cert_use_ocsp_p (ksba_cert_t cert); int gpgsm_cert_has_well_known_private_key (ksba_cert_t cert); int gpgsm_certs_identical_p (ksba_cert_t cert_a, ksba_cert_t cert_b); int gpgsm_add_cert_to_certlist (ctrl_t ctrl, ksba_cert_t cert, certlist_t *listaddr, int is_encrypt_to); int gpgsm_add_to_certlist (ctrl_t ctrl, const char *name, int secret, certlist_t *listaddr, int is_encrypt_to); void gpgsm_release_certlist (certlist_t list); int gpgsm_find_cert (ctrl_t ctrl, const char *name, ksba_sexp_t keyid, ksba_cert_t *r_cert, int allow_ambiguous); /*-- keylist.c --*/ gpg_error_t gpgsm_list_keys (ctrl_t ctrl, strlist_t names, estream_t fp, unsigned int mode); /*-- import.c --*/ int gpgsm_import (ctrl_t ctrl, int in_fd, int reimport_mode); int gpgsm_import_files (ctrl_t ctrl, int nfiles, char **files, int (*of)(const char *fname)); /*-- export.c --*/ void gpgsm_export (ctrl_t ctrl, strlist_t names, estream_t stream); void gpgsm_p12_export (ctrl_t ctrl, const char *name, estream_t stream, int rawmode); /*-- delete.c --*/ int gpgsm_delete (ctrl_t ctrl, strlist_t names); /*-- verify.c --*/ int gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp); /*-- sign.c --*/ int gpgsm_get_default_cert (ctrl_t ctrl, ksba_cert_t *r_cert); int gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, int data_fd, int detached, estream_t out_fp); /*-- encrypt.c --*/ int gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int in_fd, estream_t out_fp); /*-- decrypt.c --*/ +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); int gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp); /*-- certreqgen.c --*/ int gpgsm_genkey (ctrl_t ctrl, estream_t in_stream, estream_t out_stream); /*-- certreqgen-ui.c --*/ void gpgsm_gencertreq_tty (ctrl_t ctrl, estream_t out_stream); /*-- qualified.c --*/ gpg_error_t gpgsm_is_in_qualified_list (ctrl_t ctrl, ksba_cert_t cert, char *country); gpg_error_t gpgsm_qualified_consent (ctrl_t ctrl, ksba_cert_t cert); gpg_error_t gpgsm_not_qualified_warning (ctrl_t ctrl, ksba_cert_t cert); /*-- call-agent.c --*/ int gpgsm_agent_pksign (ctrl_t ctrl, const char *keygrip, const char *desc, unsigned char *digest, size_t digestlen, int digestalgo, unsigned char **r_buf, size_t *r_buflen); int gpgsm_scd_pksign (ctrl_t ctrl, const char *keyid, const char *desc, unsigned char *digest, size_t digestlen, int digestalgo, unsigned char **r_buf, size_t *r_buflen); int gpgsm_agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc, ksba_const_sexp_t ciphertext, char **r_buf, size_t *r_buflen); int gpgsm_agent_genkey (ctrl_t ctrl, ksba_const_sexp_t keyparms, ksba_sexp_t *r_pubkey); int gpgsm_agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip, ksba_sexp_t *r_pubkey); int gpgsm_agent_scd_serialno (ctrl_t ctrl, char **r_serialno); int gpgsm_agent_scd_keypairinfo (ctrl_t ctrl, strlist_t *r_list); int gpgsm_agent_istrusted (ctrl_t ctrl, ksba_cert_t cert, const char *hexfpr, struct rootca_flags_s *rootca_flags); int gpgsm_agent_havekey (ctrl_t ctrl, const char *hexkeygrip); int gpgsm_agent_marktrusted (ctrl_t ctrl, ksba_cert_t cert); int gpgsm_agent_learn (ctrl_t ctrl); int gpgsm_agent_passwd (ctrl_t ctrl, const char *hexkeygrip, const char *desc); gpg_error_t gpgsm_agent_get_confirmation (ctrl_t ctrl, const char *desc); gpg_error_t gpgsm_agent_send_nop (ctrl_t ctrl); gpg_error_t gpgsm_agent_keyinfo (ctrl_t ctrl, const char *hexkeygrip, char **r_serialno); gpg_error_t gpgsm_agent_ask_passphrase (ctrl_t ctrl, const char *desc_msg, int repeat, char **r_passphrase); gpg_error_t gpgsm_agent_keywrap_key (ctrl_t ctrl, int forexport, void **r_kek, size_t *r_keklen); gpg_error_t gpgsm_agent_import_key (ctrl_t ctrl, const void *key, size_t keylen); gpg_error_t gpgsm_agent_export_key (ctrl_t ctrl, const char *keygrip, const char *desc, unsigned char **r_result, size_t *r_resultlen); /*-- call-dirmngr.c --*/ int gpgsm_dirmngr_isvalid (ctrl_t ctrl, ksba_cert_t cert, ksba_cert_t issuer_cert, int use_ocsp); int gpgsm_dirmngr_lookup (ctrl_t ctrl, strlist_t names, const char *uri, int cache_only, void (*cb)(void*, ksba_cert_t), void *cb_value); int gpgsm_dirmngr_run_command (ctrl_t ctrl, const char *command, int argc, char **argv); /*-- misc.c --*/ void setup_pinentry_env (void); gpg_error_t transform_sigval (const unsigned char *sigval, size_t sigvallen, int mdalgo, unsigned char **r_newsigval, size_t *r_newsigvallen); #endif /*GPGSM_H*/