summaryrefslogtreecommitdiffstats
path: root/sm/decrypt.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 21:21:03 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-18 21:21:03 +0000
commit3675f65888fde5fddde20ff811638a338bf08ca6 (patch)
tree065688bbb6355a8a4784ec9c8e112cec342eac5e /sm/decrypt.c
parentAdding upstream version 2.2.40. (diff)
downloadgnupg2-3675f65888fde5fddde20ff811638a338bf08ca6.tar.xz
gnupg2-3675f65888fde5fddde20ff811638a338bf08ca6.zip
Adding upstream version 2.2.43.upstream/2.2.43upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sm/decrypt.c')
-rw-r--r--sm/decrypt.c396
1 files changed, 383 insertions, 13 deletions
diff --git a/sm/decrypt.c b/sm/decrypt.c
index 2aa716f..742653a 100644
--- a/sm/decrypt.c
+++ b/sm/decrypt.c
@@ -1,7 +1,7 @@
/* 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
+ * Copyright (C) 2015-2020 g10 Code GmbH
*
* This file is part of GnuPG.
*
@@ -82,6 +82,335 @@ string_from_gcry_buffer (gcry_buffer_t *buffer)
}
+/* 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. */
+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 {
@@ -100,7 +429,7 @@ 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)
+ int *r_digest)
{
gpg_error_t err;
size_t objlen, hdrlen;
@@ -212,7 +541,7 @@ pwri_decrypt (ctrl_t ctrl, gcry_sexp_t enc_val,
unsigned int ekeylen;
unsigned char kek[32];
unsigned int keklen;
- enum gcry_cipher_algos encr_algo;
+ int encr_algo;
enum gcry_cipher_modes encr_mode;
gcry_cipher_hd_t encr_hd = NULL;
unsigned char *result = NULL;
@@ -221,7 +550,7 @@ pwri_decrypt (ctrl_t ctrl, gcry_sexp_t enc_val,
const unsigned char *salt; /* Points int dparm. */
unsigned int saltlen;
unsigned long iterations;
- enum gcry_md_algos digest_algo;
+ int digest_algo;
char *passphrase = NULL;
@@ -447,13 +776,14 @@ pwri_decrypt (ctrl_t ctrl, gcry_sexp_t enc_val,
/* 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, const char *desc,
+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;
+ int pwri = !hexkeygrip && !pk_algo;
int rc;
if (DBG_CRYPTO)
@@ -468,6 +798,9 @@ prepare_decryption (ctrl_t ctrl, const char *hexkeygrip, const char *desc,
log_error ("error decrypting session key: %s\n", gpg_strerror (rc));
goto leave;
}
+
+ if (DBG_CRYPTO)
+ log_printhex (seskey, seskeylen, "DEK frame:");
}
n=0;
@@ -490,6 +823,27 @@ prepare_decryption (ctrl_t ctrl, const char *hexkeygrip, const char *desc,
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
@@ -530,7 +884,10 @@ prepare_decryption (ctrl_t ctrl, const char *hexkeygrip, const char *desc,
}
if (DBG_CRYPTO)
- log_printhex (seskey+n, seskeylen-n, "session key:");
+ {
+ log_printhex (seskey+n, seskeylen-n, "CEK .......:");
+ log_printhex (parm->iv, parm->ivlen, "IV ........:");
+ }
if (opt.verbose)
log_info (_("%s.%s encrypted data\n"),
@@ -717,6 +1074,7 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
int recp;
estream_t in_fp = NULL;
struct decrypt_filter_parm_s dfparm;
+ char *curve = NULL;
memset (&dfparm, 0, sizeof dfparm);
@@ -759,6 +1117,10 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
goto leave;
}
+ gnupg_ksba_set_progress_cb (b64writer, gpgsm_progress_cb, ctrl);
+ if (ctrl->input_size_hint)
+ gnupg_ksba_set_total (b64writer, ctrl->input_size_hint);
+
rc = ksba_cms_new (&cms);
if (rc)
goto leave;
@@ -839,7 +1201,10 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
dfparm.mode = mode;
dfparm.blklen = gcry_cipher_get_algo_blklen (algo);
if (dfparm.blklen > sizeof (dfparm.helpblock))
- return gpg_error (GPG_ERR_BUG);
+ {
+ rc = gpg_error (GPG_ERR_BUG);
+ goto leave;
+ }
rc = ksba_cms_get_content_enc_iv (cms,
dfparm.iv,
@@ -951,16 +1316,19 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
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);
+ xfree (curve);
+ pk_algo = gpgsm_get_key_algo_info (cert, &nbits, &curve);
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))
+ pk_algo, PK_ALGO_FLAG_ECC18,
+ NULL, nbits, curve))
{
char kidstr[10+1];
@@ -978,7 +1346,7 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
dfparm.is_de_vs =
(dfparm.is_de_vs
&& gnupg_pk_is_compliant (CO_DE_VS, pk_algo, 0,
- NULL, nbits, NULL));
+ NULL, nbits, curve));
oops:
if (rc)
@@ -1006,7 +1374,7 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
if (maybe_pwri && opt.verbose)
log_info ("recp %d - KEKRI or PWRI\n", recp);
- rc = prepare_decryption (ctrl, hexkeygrip,
+ rc = prepare_decryption (ctrl, hexkeygrip, pk_algo, nbits,
desc, enc_val, &dfparm);
xfree (enc_val);
if (rc)
@@ -1076,7 +1444,8 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
if (!any_key)
{
- rc = gpg_error (GPG_ERR_NO_SECKEY);
+ if (!rc)
+ rc = gpg_error (GPG_ERR_NO_SECKEY);
goto leave;
}
}
@@ -1155,6 +1524,7 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp)
log_error ("message decryption failed: %s <%s>\n",
gpg_strerror (rc), gpg_strsource (rc));
}
+ xfree (curve);
ksba_cms_release (cms);
gnupg_ksba_destroy_reader (b64reader);
gnupg_ksba_destroy_writer (b64writer);