diff options
Diffstat (limited to 'sm/encrypt.c')
| -rw-r--r-- | sm/encrypt.c | 162 |
1 files changed, 96 insertions, 66 deletions
diff --git a/sm/encrypt.c b/sm/encrypt.c index 98d3024ad..52c747321 100644 --- a/sm/encrypt.c +++ b/sm/encrypt.c @@ -186,17 +186,23 @@ ecdh_encrypt (DEK dek, gcry_sexp_t s_pkey, gcry_sexp_t *r_encval) int hash_algo, cipher_algo; unsigned int keylen; 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]. */ + const unsigned char *secret; unsigned int secretlen; - unsigned char *pubkey; /* Alias for ioarray[1]. */ + const unsigned char *pubkey; unsigned int pubkeylen; gcry_cipher_hd_t cipher_hd = NULL; unsigned char *result = NULL; unsigned int resultlen; + unsigned int nbits; + const struct gnupg_ecc_params *ecc; + gcry_mpi_t ecc_pubkey_mpi = NULL; + const unsigned char *ecc_pubkey; + size_t ecc_pubkey_len; + unsigned char ecc_ct[ECC_POINT_LEN_MAX]; + unsigned char ecc_ecdh[ECC_POINT_LEN_MAX]; + size_t ecc_ct_len, ecc_ecdh_len; + *r_encval = NULL; /* Figure out the encryption and wrap algo OIDs. */ @@ -215,18 +221,25 @@ ecdh_encrypt (DEK dek, gcry_sexp_t s_pkey, gcry_sexp_t *r_encval) } /* 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, -1); + * canonical name. 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_oid_to_curve (curvebuf, 1); + xfree (curvebuf); + if (!curve) + { + err = gpg_error (GPG_ERR_UNKNOWN_CURVE); + log_error ("%s: invalid public key: %s\n", __func__, gpg_strerror (err)); + goto leave; + } + + curve = openpgp_is_curve_supported (curve, NULL, &curvebits); 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 supporting the short curves which would require 3DES. */ @@ -273,75 +286,95 @@ ecdh_encrypt (DEK dek, gcry_sexp_t s_pkey, gcry_sexp_t *r_encval) 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) + ecc = gnupg_get_ecc_params (curve); + if (!ecc) { - log_error ("%s: error building ephemeral secret: %s\n", - __func__, gpg_strerror (err)); + if (opt.verbose) + log_info ("%s: ECC curve %s not supported\n", __func__, curve); + err = gpg_error (GPG_ERR_INV_DATA); goto leave; } + ecc_ct_len = ecc_ecdh_len = ecc->point_len; - err = gcry_pk_encrypt (&s_encr, s_data, s_pkey); - if (err) + err = gcry_sexp_extract_param (s_pkey, NULL, "/q", + &ecc_pubkey_mpi, NULL); + ecc_pubkey = gcry_mpi_get_opaque (ecc_pubkey_mpi, &nbits); + ecc_pubkey_len = (nbits+7)/8; + if (ecc_pubkey_len != ecc->pubkey_len) { - log_error ("%s: error encrypting ephemeral secret: %s\n", - __func__, gpg_strerror (err)); - goto leave; + if (ecc->kem_algo == GCRY_KEM_RAW_X25519 + && ecc_pubkey_len == ecc->pubkey_len - 1) + /* For Curve25519, we also accept no prefix in the point + * representation. */ + ; + else + { + if (opt.verbose) + log_info ("%s: ECC public key length invalid (%zu)\n", + __func__, ecc_pubkey_len); + err = gpg_error (GPG_ERR_INV_DATA); + goto leave; + } } - err = gcry_sexp_extract_param (s_encr, NULL, "&se", - &ioarray+0, ioarray+1, NULL); - if (err) + + if (ecc->kem_algo == GCRY_KEM_RAW_X25519) { - log_error ("%s: error extracting ephemeral key and secret: %s\n", - __func__, gpg_strerror (err)); - goto leave; + /* Note: Legacy OID is OK here. */ + /* Optional prefix handling */ + if (ecc_pubkey_len == 33 && *ecc_pubkey == 0x40) + { + ecc_pubkey++; /* Remove the 0x40 prefix. */ + ecc_pubkey_len--; + } } - 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 ..:"); + log_debug ("ECC curve: %s\n", curve); + log_printhex (ecc_pubkey, ecc_pubkey_len, "ECC pubkey:"); } - /* 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) + err = gcry_kem_encap (ecc->kem_algo, + ecc_pubkey, ecc_pubkey_len, + ecc_ct, ecc_ct_len, + ecc_ecdh, ecc_ecdh_len, + NULL, 0); + if (err) { - secretlen--; - memmove (secret, secret+1, secretlen); - if ((secretlen & 1)) - { - err = gpg_error (GPG_ERR_BAD_DATA); - goto leave; - } - secretlen /= 2; + if (opt.verbose) + log_info ("%s: gcry_kem_encap for ECC (%s) failed\n", + __func__, curve); + goto leave; + } + if (DBG_CRYPTO) + { + log_printhex (ecc_ct, ecc_ct_len, "ECC ephem:"); + log_printhex (ecc_ecdh, ecc_ecdh_len, "ECC ecdh:"); } - else if (*secret == 0x40 || *secret == 0x41) + + if (ecc->is_weierstrauss) { - secretlen--; - memmove (secret, secret+1, secretlen); + secret = ecc_ecdh + 1; + secretlen = (ecc_ecdh_len - 1) / 2; + pubkey = ecc_ct; + pubkeylen = ecc_ct_len; } else - err = gpg_error (GPG_ERR_BAD_DATA); - if (err) - goto leave; + { + secret = ecc_ecdh; + secretlen = ecc_ecdh_len; - if (DBG_CRYPTO) - log_printhex (secret, secretlen, "ECDH X ..:"); + if (ecc->may_have_prefix) + { + pubkeylen = ecc_ct_len + 1; + memmove (ecc_ct + 1, ecc_ct, ecc_ct_len); + ecc_ct[0] = 0x40; + } + else + pubkeylen = ecc_ct_len; + + pubkey = ecc_ct; + } err = ecdh_derive_kek (key, keylen, hash_algo, wrap_algo_str, secret, secretlen, NULL, 0); @@ -409,14 +442,11 @@ ecdh_encrypt (DEK dek, gcry_sexp_t s_pkey, gcry_sexp_t *r_encval) __func__, gpg_strerror (err)); leave: + gcry_mpi_release (ecc_pubkey_mpi); gcry_cipher_close (cipher_hd); + wipememory (ecc_ecdh, sizeof ecc_ecdh); 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; } |
