mirror of
git://git.gnupg.org/gnupg.git
synced 2025-05-24 16:43:28 +02:00
gpg: Use the KEM API for ECC encryption.
* g10/ecdh.c (gnupg_ecc_6637_kdf): New. (pk_ecdh_encrypt_with_shared_point, gen_k): Remove. (pk_ecdh_generate_ephemeral_key): Remove. * g10/pkglue.c (get_data_from_sexp): Remove. (do_encrypt_ecdh): Use gcry_kem_encap of the KEM API, gnupg_ecc_6637_kdf, and AESWRAP. * g10/pkglue.h (gnupg_ecc_6637_kdf): New. (pk_ecdh_encrypt_with_shared_point): Remove. (pk_ecdh_generate_ephemeral_key, pk_ecdh_encrypt): Remove. -- GnuPG-bug-id: 7649 Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
This commit is contained in:
parent
fcac10357e
commit
d1c3bfda2a
@ -144,8 +144,8 @@ compute_kmac256 (void *digest, size_t digestlen,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Compute KEK (shared secret) for ECC with HASHALGO, ECDH result,
|
/* Compute KEK for ECC with HASHALGO, ECDH result, ciphertext in
|
||||||
ciphertext in ECC_CT, public key in ECC_PK. */
|
ECC_CT (which is an ephemeral key), and public key in ECC_PK. */
|
||||||
gpg_error_t
|
gpg_error_t
|
||||||
gnupg_ecc_kem_kdf (void *kek, size_t kek_len,
|
gnupg_ecc_kem_kdf (void *kek, size_t kek_len,
|
||||||
int hashalgo, const void *ecdh, size_t ecdh_len,
|
int hashalgo, const void *ecdh, size_t ecdh_len,
|
||||||
|
216
g10/ecdh.c
216
g10/ecdh.c
@ -236,6 +236,68 @@ derive_kek (size_t kek_size,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Compute KEK for ECC with HASHALGO, ECDH result, and public key in
|
||||||
|
PK, using the method defined in RFC 6637. Note that ephemeral key
|
||||||
|
is not used to compute KEK here. */
|
||||||
|
gpg_error_t
|
||||||
|
gnupg_ecc_6637_kdf (void *kek, size_t kek_len,
|
||||||
|
int hashalgo, const void *ecdh, size_t ecdh_len,
|
||||||
|
PKT_public_key *pk)
|
||||||
|
{
|
||||||
|
gpg_error_t err;
|
||||||
|
unsigned char kdf_params[256];
|
||||||
|
size_t kdf_params_size;
|
||||||
|
unsigned int nbits;
|
||||||
|
byte *secret_x;
|
||||||
|
int secret_x_size;
|
||||||
|
gcry_kdf_hd_t hd;
|
||||||
|
unsigned long param[1];
|
||||||
|
byte fp[MAX_FINGERPRINT_LEN];
|
||||||
|
|
||||||
|
fingerprint_from_pk (pk, fp, NULL);
|
||||||
|
|
||||||
|
/* Build kdf_params. */
|
||||||
|
err = build_kdf_params (kdf_params, &kdf_params_size, pk->pkey, fp);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
nbits = pubkey_nbits (PUBKEY_ALGO_ECDH, pk->pkey);
|
||||||
|
if (!nbits)
|
||||||
|
return gpg_error (GPG_ERR_TOO_SHORT);
|
||||||
|
|
||||||
|
secret_x_size = (nbits+7)/8;
|
||||||
|
if (kek_len > secret_x_size)
|
||||||
|
return gpg_error (GPG_ERR_BAD_PUBKEY);
|
||||||
|
|
||||||
|
err = extract_secret_x (&secret_x, ecdh, ecdh_len,
|
||||||
|
/* pk->pkey[1] is the public point */
|
||||||
|
(mpi_get_nbits (pk->pkey[1])+7)/8,
|
||||||
|
secret_x_size);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
param[0] = kek_len;
|
||||||
|
err = gcry_kdf_open (&hd, GCRY_KDF_ONESTEP_KDF, hashalgo, param, 1,
|
||||||
|
secret_x, secret_x_size, NULL, 0, NULL, 0,
|
||||||
|
kdf_params, kdf_params_size);
|
||||||
|
if (!err)
|
||||||
|
{
|
||||||
|
gcry_kdf_compute (hd, NULL);
|
||||||
|
gcry_kdf_final (hd, kek_len, secret_x);
|
||||||
|
gcry_kdf_close (hd);
|
||||||
|
memcpy (kek, secret_x, kek_len);
|
||||||
|
/* Clean the tail before returning. */
|
||||||
|
memset (secret_x+kek_len, 0, secret_x_size - kek_len);
|
||||||
|
xfree (secret_x);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
xfree (secret_x);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Prepare ECDH using SHARED, PK_FP fingerprint, and PKEY array.
|
/* Prepare ECDH using SHARED, PK_FP fingerprint, and PKEY array.
|
||||||
Returns the cipher handle in R_HD, which needs to be closed by
|
Returns the cipher handle in R_HD, which needs to be closed by
|
||||||
@ -358,160 +420,6 @@ prepare_ecdh_with_shared_point (const char *shared, size_t nshared,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Encrypts DATA using a key derived from the ECC shared point SHARED
|
|
||||||
using the FIPS SP 800-56A compliant method
|
|
||||||
key_derivation+key_wrapping. PKEY is the public key and PK_FP the
|
|
||||||
fingerprint of this public key. On success the result is stored at
|
|
||||||
R_RESULT; on failure NULL is stored at R_RESULT and an error code
|
|
||||||
returned. */
|
|
||||||
gpg_error_t
|
|
||||||
pk_ecdh_encrypt_with_shared_point (const char *shared, size_t nshared,
|
|
||||||
const byte pk_fp[MAX_FINGERPRINT_LEN],
|
|
||||||
const byte *data, size_t ndata,
|
|
||||||
gcry_mpi_t *pkey, gcry_mpi_t *r_result)
|
|
||||||
{
|
|
||||||
gpg_error_t err;
|
|
||||||
gcry_cipher_hd_t hd;
|
|
||||||
byte *data_buf;
|
|
||||||
int data_buf_size;
|
|
||||||
gcry_mpi_t result;
|
|
||||||
byte *in;
|
|
||||||
|
|
||||||
*r_result = NULL;
|
|
||||||
|
|
||||||
err = prepare_ecdh_with_shared_point (shared, nshared, pk_fp, pkey, &hd);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
data_buf_size = ndata;
|
|
||||||
if ((data_buf_size & 7) != 0)
|
|
||||||
{
|
|
||||||
log_error ("can't use a shared secret of %d bytes for ecdh\n",
|
|
||||||
data_buf_size);
|
|
||||||
gcry_cipher_close (hd);
|
|
||||||
return gpg_error (GPG_ERR_BAD_DATA);
|
|
||||||
}
|
|
||||||
|
|
||||||
data_buf = xtrymalloc_secure( 1 + 2*data_buf_size + 8);
|
|
||||||
if (!data_buf)
|
|
||||||
{
|
|
||||||
err = gpg_error_from_syserror ();
|
|
||||||
gcry_cipher_close (hd);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
in = data_buf+1+data_buf_size+8;
|
|
||||||
|
|
||||||
/* Write data MPI into the end of data_buf. data_buf is size
|
|
||||||
aeswrap data. */
|
|
||||||
memcpy (in, data, ndata);
|
|
||||||
|
|
||||||
if (DBG_CRYPTO)
|
|
||||||
log_printhex (in, data_buf_size, "ecdh encrypting :");
|
|
||||||
|
|
||||||
err = gcry_cipher_encrypt (hd, data_buf+1, data_buf_size+8,
|
|
||||||
in, data_buf_size);
|
|
||||||
memset (in, 0, data_buf_size);
|
|
||||||
gcry_cipher_close (hd);
|
|
||||||
if (err)
|
|
||||||
{
|
|
||||||
log_error ("ecdh failed in gcry_cipher_encrypt: %s\n",
|
|
||||||
gpg_strerror (err));
|
|
||||||
xfree (data_buf);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
data_buf[0] = data_buf_size+8;
|
|
||||||
|
|
||||||
if (DBG_CRYPTO)
|
|
||||||
log_printhex (data_buf+1, data_buf[0], "ecdh encrypted to:");
|
|
||||||
|
|
||||||
result = gcry_mpi_set_opaque (NULL, data_buf, 8 * (1+data_buf[0]));
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
err = gpg_error_from_syserror ();
|
|
||||||
xfree (data_buf);
|
|
||||||
log_error ("ecdh failed to create an MPI: %s\n",
|
|
||||||
gpg_strerror (err));
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
*r_result = result;
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static gcry_mpi_t
|
|
||||||
gen_k (unsigned nbits, int little_endian, int is_opaque)
|
|
||||||
{
|
|
||||||
gcry_mpi_t k;
|
|
||||||
|
|
||||||
if (is_opaque)
|
|
||||||
{
|
|
||||||
unsigned char *p;
|
|
||||||
size_t nbytes = (nbits+7)/8;
|
|
||||||
|
|
||||||
p = gcry_random_bytes_secure (nbytes, GCRY_STRONG_RANDOM);
|
|
||||||
if ((nbits % 8))
|
|
||||||
{
|
|
||||||
if (little_endian)
|
|
||||||
p[nbytes-1] &= ((1 << (nbits % 8)) - 1);
|
|
||||||
else
|
|
||||||
p[0] &= ((1 << (nbits % 8)) - 1);
|
|
||||||
}
|
|
||||||
k = gcry_mpi_set_opaque (NULL, p, nbits);
|
|
||||||
return k;
|
|
||||||
}
|
|
||||||
|
|
||||||
k = gcry_mpi_snew (nbits);
|
|
||||||
if (DBG_CRYPTO)
|
|
||||||
log_debug ("choosing a random k of %u bits\n", nbits);
|
|
||||||
|
|
||||||
gcry_mpi_randomize (k, nbits-1, GCRY_STRONG_RANDOM);
|
|
||||||
|
|
||||||
if (DBG_CRYPTO)
|
|
||||||
{
|
|
||||||
unsigned char *buffer;
|
|
||||||
if (gcry_mpi_aprint (GCRYMPI_FMT_HEX, &buffer, NULL, k))
|
|
||||||
BUG ();
|
|
||||||
log_debug ("ephemeral scalar MPI #0: %s\n", buffer);
|
|
||||||
gcry_free (buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
return k;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Generate an ephemeral key for the public ECDH key in PKEY. On
|
|
||||||
success the generated key is stored at R_K; on failure NULL is
|
|
||||||
stored at R_K and an error code returned. */
|
|
||||||
gpg_error_t
|
|
||||||
pk_ecdh_generate_ephemeral_key (gcry_mpi_t *pkey, gcry_mpi_t *r_k)
|
|
||||||
{
|
|
||||||
unsigned int nbits;
|
|
||||||
gcry_mpi_t k;
|
|
||||||
int is_little_endian = 0;
|
|
||||||
int require_opaque = 0;
|
|
||||||
|
|
||||||
if (openpgp_oid_is_cv448 (pkey[0]))
|
|
||||||
{
|
|
||||||
is_little_endian = 1;
|
|
||||||
require_opaque = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
*r_k = NULL;
|
|
||||||
|
|
||||||
nbits = pubkey_nbits (PUBKEY_ALGO_ECDH, pkey);
|
|
||||||
if (!nbits)
|
|
||||||
return gpg_error (GPG_ERR_TOO_SHORT);
|
|
||||||
k = gen_k (nbits, is_little_endian, require_opaque);
|
|
||||||
if (!k)
|
|
||||||
BUG ();
|
|
||||||
|
|
||||||
*r_k = k;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Perform ECDH decryption. */
|
/* Perform ECDH decryption. */
|
||||||
int
|
int
|
||||||
|
390
g10/pkglue.c
390
g10/pkglue.c
@ -183,29 +183,6 @@ sexp_extract_param_sos_nlz (gcry_sexp_t sexp, const char *param,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static byte *
|
|
||||||
get_data_from_sexp (gcry_sexp_t sexp, const char *item, size_t *r_size)
|
|
||||||
{
|
|
||||||
gcry_sexp_t list;
|
|
||||||
size_t valuelen;
|
|
||||||
const char *value;
|
|
||||||
byte *v;
|
|
||||||
|
|
||||||
if (DBG_CRYPTO)
|
|
||||||
log_printsexp ("get_data_from_sexp:", sexp);
|
|
||||||
|
|
||||||
list = gcry_sexp_find_token (sexp, item, 0);
|
|
||||||
log_assert (list);
|
|
||||||
value = gcry_sexp_nth_data (list, 1, &valuelen);
|
|
||||||
log_assert (value);
|
|
||||||
v = xtrymalloc (valuelen);
|
|
||||||
memcpy (v, value, valuelen);
|
|
||||||
gcry_sexp_release (list);
|
|
||||||
*r_size = valuelen;
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/****************
|
/****************
|
||||||
* Emulate our old PK interface here - sometime in the future we might
|
* Emulate our old PK interface here - sometime in the future we might
|
||||||
* change the internal design to directly fit to libgcrypt.
|
* change the internal design to directly fit to libgcrypt.
|
||||||
@ -773,95 +750,312 @@ do_encrypt_kem (PKT_public_key *pk, gcry_mpi_t data, int seskey_algo,
|
|||||||
static gpg_error_t
|
static gpg_error_t
|
||||||
do_encrypt_ecdh (PKT_public_key *pk, gcry_mpi_t data, gcry_mpi_t *resarr)
|
do_encrypt_ecdh (PKT_public_key *pk, gcry_mpi_t data, gcry_mpi_t *resarr)
|
||||||
{
|
{
|
||||||
gcry_mpi_t *pkey = pk->pkey;
|
|
||||||
gcry_sexp_t s_ciph = NULL;
|
|
||||||
gcry_sexp_t s_data = NULL;
|
|
||||||
gcry_sexp_t s_pkey = NULL;
|
|
||||||
gpg_error_t err;
|
gpg_error_t err;
|
||||||
gcry_mpi_t k = NULL;
|
|
||||||
char *curve = NULL;
|
|
||||||
int with_djb_tweak_flag;
|
|
||||||
gcry_mpi_t public = NULL;
|
|
||||||
gcry_mpi_t result = NULL;
|
|
||||||
byte fp[MAX_FINGERPRINT_LEN];
|
|
||||||
byte *shared = NULL;
|
|
||||||
byte *p;
|
|
||||||
size_t nshared;
|
|
||||||
unsigned int nbits;
|
unsigned int nbits;
|
||||||
|
gcry_cipher_hd_t hd = NULL;
|
||||||
|
char *ecc_oid = NULL;
|
||||||
|
enum gcry_kem_algos ecc_algo;
|
||||||
|
|
||||||
err = pk_ecdh_generate_ephemeral_key (pkey, &k);
|
const unsigned char *ecc_pubkey;
|
||||||
if (err)
|
size_t ecc_pubkey_len;
|
||||||
goto leave;
|
const unsigned char *seskey;
|
||||||
|
size_t seskey_len;
|
||||||
|
unsigned char *enc_seskey = NULL;
|
||||||
|
size_t enc_seskey_len;
|
||||||
|
|
||||||
curve = openpgp_oid_to_str (pkey[0]);
|
unsigned char ecc_ct[ECC_POINT_LEN_MAX];
|
||||||
if (!curve)
|
unsigned char ecc_ecdh[ECC_POINT_LEN_MAX];
|
||||||
|
size_t ecc_ct_len, ecc_ecdh_len;
|
||||||
|
|
||||||
|
unsigned char *kek = NULL;
|
||||||
|
size_t kek_len;
|
||||||
|
|
||||||
|
const unsigned char *kek_params;
|
||||||
|
size_t kek_params_size;
|
||||||
|
int kdf_hash_algo;
|
||||||
|
int kdf_encr_algo;
|
||||||
|
|
||||||
|
ecc_oid = openpgp_oid_to_str (pk->pkey[0]);
|
||||||
|
if (!ecc_oid)
|
||||||
{
|
{
|
||||||
err = gpg_error_from_syserror ();
|
err = gpg_error_from_syserror ();
|
||||||
|
log_error ("%s: error getting OID for ECC key\n", __func__);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
ecc_algo = openpgp_oid_to_kem_algo (ecc_oid);
|
||||||
|
if (ecc_algo == GCRY_KEM_RAW_X25519)
|
||||||
|
{
|
||||||
|
/* Legacy OID is OK here. */
|
||||||
|
ecc_pubkey = gcry_mpi_get_opaque (pk->pkey[1], &nbits);
|
||||||
|
ecc_pubkey_len = (nbits+7)/8;
|
||||||
|
if (ecc_pubkey_len == 33 && *ecc_pubkey == 0x40)
|
||||||
|
{
|
||||||
|
ecc_pubkey++; /* Remove the 0x40 prefix. */
|
||||||
|
ecc_pubkey_len--;
|
||||||
|
}
|
||||||
|
if (ecc_pubkey_len != 32)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
ecc_ct_len = ecc_ecdh_len = 32;
|
||||||
|
}
|
||||||
|
else if (ecc_algo == GCRY_KEM_RAW_X448)
|
||||||
|
{
|
||||||
|
ecc_pubkey = gcry_mpi_get_opaque (pk->pkey[1], &nbits);
|
||||||
|
ecc_pubkey_len = (nbits+7)/8;
|
||||||
|
if (ecc_pubkey_len != 56)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
ecc_ct_len = ecc_ecdh_len = 56;
|
||||||
|
}
|
||||||
|
else if (ecc_algo == GCRY_KEM_RAW_BP256)
|
||||||
|
{
|
||||||
|
ecc_pubkey = gcry_mpi_get_opaque (pk->pkey[1], &nbits);
|
||||||
|
ecc_pubkey_len = (nbits+7)/8;
|
||||||
|
if (ecc_pubkey_len != 65)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
ecc_ct_len = ecc_ecdh_len = 65;
|
||||||
|
}
|
||||||
|
else if (ecc_algo == GCRY_KEM_RAW_BP384)
|
||||||
|
{
|
||||||
|
ecc_pubkey = gcry_mpi_get_opaque (pk->pkey[1], &nbits);
|
||||||
|
ecc_pubkey_len = (nbits+7)/8;
|
||||||
|
if (ecc_pubkey_len != 97)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
ecc_ct_len = ecc_ecdh_len = 97;
|
||||||
|
}
|
||||||
|
else if (ecc_algo == GCRY_KEM_RAW_BP512)
|
||||||
|
{
|
||||||
|
ecc_pubkey = gcry_mpi_get_opaque (pk->pkey[1], &nbits);
|
||||||
|
ecc_pubkey_len = (nbits+7)/8;
|
||||||
|
if (ecc_pubkey_len != 129)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
ecc_ct_len = ecc_ecdh_len = 129;
|
||||||
|
}
|
||||||
|
else if (ecc_algo == GCRY_KEM_RAW_P256R1)
|
||||||
|
{
|
||||||
|
ecc_pubkey = gcry_mpi_get_opaque (pk->pkey[1], &nbits);
|
||||||
|
ecc_pubkey_len = (nbits+7)/8;
|
||||||
|
if (ecc_pubkey_len != 65)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
ecc_ct_len = ecc_ecdh_len = 65;
|
||||||
|
}
|
||||||
|
else if (ecc_algo == GCRY_KEM_RAW_P384R1)
|
||||||
|
{
|
||||||
|
ecc_pubkey = gcry_mpi_get_opaque (pk->pkey[1], &nbits);
|
||||||
|
ecc_pubkey_len = (nbits+7)/8;
|
||||||
|
if (ecc_pubkey_len != 97)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
ecc_ct_len = ecc_ecdh_len = 97;
|
||||||
|
}
|
||||||
|
else if (ecc_algo == GCRY_KEM_RAW_P521R1)
|
||||||
|
{
|
||||||
|
ecc_pubkey = gcry_mpi_get_opaque (pk->pkey[1], &nbits);
|
||||||
|
ecc_pubkey_len = (nbits+7)/8;
|
||||||
|
if (ecc_pubkey_len != 133)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
ecc_ct_len = ecc_ecdh_len = 133;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (opt.verbose)
|
||||||
|
log_info ("%s: ECC curve %s not supported\n", __func__, ecc_oid);
|
||||||
|
err = gpg_error (GPG_ERR_INV_DATA);
|
||||||
goto leave;
|
goto leave;
|
||||||
}
|
}
|
||||||
|
|
||||||
with_djb_tweak_flag = openpgp_oid_is_cv25519 (pkey[0]);
|
|
||||||
|
|
||||||
/* Now use the ephemeral secret to compute the shared point. */
|
|
||||||
err = gcry_sexp_build (&s_pkey, NULL,
|
|
||||||
with_djb_tweak_flag ?
|
|
||||||
"(public-key(ecdh(curve%s)(flags djb-tweak)(q%m)))"
|
|
||||||
: "(public-key(ecdh(curve%s)(q%m)))",
|
|
||||||
curve, pkey[1]);
|
|
||||||
if (err)
|
|
||||||
goto leave;
|
|
||||||
|
|
||||||
/* Put K into a simplified S-expression. */
|
|
||||||
err = gcry_sexp_build (&s_data, NULL, "%m", k);
|
|
||||||
if (err)
|
|
||||||
goto leave;
|
|
||||||
|
|
||||||
/* Run encryption. */
|
|
||||||
err = gcry_pk_encrypt (&s_ciph, s_data, s_pkey);
|
|
||||||
if (err)
|
|
||||||
goto leave;
|
|
||||||
|
|
||||||
gcry_sexp_release (s_data); s_data = NULL;
|
|
||||||
gcry_sexp_release (s_pkey); s_pkey = NULL;
|
|
||||||
|
|
||||||
|
|
||||||
/* Get the shared point and the ephemeral public key. */
|
|
||||||
shared = get_data_from_sexp (s_ciph, "s", &nshared);
|
|
||||||
if (!shared)
|
|
||||||
{
|
|
||||||
err = gpg_error_from_syserror ();
|
|
||||||
goto leave;
|
|
||||||
}
|
|
||||||
err = sexp_extract_param_sos (s_ciph, "e", &public);
|
|
||||||
gcry_sexp_release (s_ciph); s_ciph = NULL;
|
|
||||||
if (DBG_CRYPTO)
|
if (DBG_CRYPTO)
|
||||||
{
|
{
|
||||||
log_debug ("ECDH ephemeral key:");
|
log_debug ("ECC curve: %s\n", ecc_oid);
|
||||||
gcry_mpi_dump (public);
|
log_printhex (ecc_pubkey, ecc_pubkey_len, "ECC pubkey:");
|
||||||
log_printf ("\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fingerprint_from_pk (pk, fp, NULL);
|
err = gcry_kem_encap (ecc_algo,
|
||||||
|
ecc_pubkey, ecc_pubkey_len,
|
||||||
p = gcry_mpi_get_opaque (data, &nbits);
|
ecc_ct, ecc_ct_len,
|
||||||
result = NULL;
|
ecc_ecdh, ecc_ecdh_len,
|
||||||
err = pk_ecdh_encrypt_with_shared_point (shared, nshared, fp, p,
|
NULL, 0);
|
||||||
(nbits+7)/8, pkey, &result);
|
|
||||||
if (err)
|
if (err)
|
||||||
goto leave;
|
{
|
||||||
|
if (opt.verbose)
|
||||||
|
log_info ("%s: gcry_kem_encap for ECC (%s) failed\n",
|
||||||
|
__func__, ecc_oid);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
if (DBG_CRYPTO)
|
||||||
|
{
|
||||||
|
log_printhex (ecc_ct, ecc_ct_len, "ECC ephem:");
|
||||||
|
log_printhex (ecc_ecdh, ecc_ecdh_len, "ECC ecdh:");
|
||||||
|
}
|
||||||
|
|
||||||
resarr[0] = public; public = NULL;
|
if (!gcry_mpi_get_flag (pk->pkey[2], GCRYMPI_FLAG_OPAQUE))
|
||||||
resarr[1] = result; result = NULL;
|
{
|
||||||
|
err = gpg_error (GPG_ERR_BUG);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
kek_params = gcry_mpi_get_opaque (pk->pkey[2], &nbits);
|
||||||
|
kek_params_size = (nbits+7)/8;
|
||||||
|
|
||||||
|
if (DBG_CRYPTO)
|
||||||
|
log_printhex (kek_params, kek_params_size, "ecdh KDF params:");
|
||||||
|
|
||||||
|
/* Expect 4 bytes 03 01 hash_alg symm_alg. */
|
||||||
|
if (kek_params_size != 4 || kek_params[0] != 3 || kek_params[1] != 1)
|
||||||
|
{
|
||||||
|
err = gpg_error (GPG_ERR_BAD_PUBKEY);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
kdf_hash_algo = kek_params[2];
|
||||||
|
kdf_encr_algo = kek_params[3];
|
||||||
|
|
||||||
|
if (DBG_CRYPTO)
|
||||||
|
log_debug ("ecdh KDF algorithms %s+%s with aeswrap\n",
|
||||||
|
openpgp_md_algo_name (kdf_hash_algo),
|
||||||
|
openpgp_cipher_algo_name (kdf_encr_algo));
|
||||||
|
|
||||||
|
if (kdf_hash_algo != GCRY_MD_SHA256
|
||||||
|
&& kdf_hash_algo != GCRY_MD_SHA384
|
||||||
|
&& kdf_hash_algo != GCRY_MD_SHA512)
|
||||||
|
{
|
||||||
|
err = gpg_error (GPG_ERR_BAD_PUBKEY);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kdf_encr_algo != CIPHER_ALGO_AES
|
||||||
|
&& kdf_encr_algo != CIPHER_ALGO_AES192
|
||||||
|
&& kdf_encr_algo != CIPHER_ALGO_AES256)
|
||||||
|
{
|
||||||
|
err = gpg_error (GPG_ERR_BAD_PUBKEY);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
kek_len = gcry_cipher_get_algo_keylen (kdf_encr_algo);
|
||||||
|
if (kek_len > gcry_md_get_algo_dlen (kdf_hash_algo))
|
||||||
|
{
|
||||||
|
err = gpg_error (GPG_ERR_BAD_PUBKEY);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
kek = xtrymalloc (kek_len);
|
||||||
|
if (!kek)
|
||||||
|
{
|
||||||
|
err = gpg_error_from_syserror ();
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = gnupg_ecc_6637_kdf (kek, kek_len, kdf_hash_algo,
|
||||||
|
ecc_ecdh, ecc_ecdh_len, pk);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
if (opt.verbose)
|
||||||
|
log_info ("%s: kdf for ECC failed\n", __func__);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DBG_CRYPTO)
|
||||||
|
log_printhex (kek, kek_len, "KEK:");
|
||||||
|
|
||||||
|
err = gcry_cipher_open (&hd, kdf_encr_algo, GCRY_CIPHER_MODE_AESWRAP, 0);
|
||||||
|
if (!err)
|
||||||
|
err = gcry_cipher_setkey (hd, kek, kek_len);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
if (opt.verbose)
|
||||||
|
log_error ("%s: failed to initialize AESWRAP: %s\n", __func__,
|
||||||
|
gpg_strerror (err));
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
seskey = gcry_mpi_get_opaque (data, &nbits);
|
||||||
|
seskey_len = (nbits+7)/8;
|
||||||
|
|
||||||
|
enc_seskey_len = 1 + seskey_len + 8;
|
||||||
|
enc_seskey = xtrymalloc (enc_seskey_len);
|
||||||
|
if (!enc_seskey || enc_seskey_len > 254)
|
||||||
|
{
|
||||||
|
err = gpg_error_from_syserror ();
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
enc_seskey[0] = enc_seskey_len - 1;
|
||||||
|
err = gcry_cipher_encrypt (hd, enc_seskey+1, enc_seskey_len-1,
|
||||||
|
seskey, seskey_len);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
log_error ("%s: wrapping session key failed\n", __func__);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
if (DBG_CRYPTO)
|
||||||
|
log_printhex (enc_seskey, enc_seskey_len, "enc_seskey:");
|
||||||
|
|
||||||
|
resarr[0] = gcry_mpi_set_opaque_copy (NULL, ecc_ct, 8 * ecc_ct_len);
|
||||||
|
if (!resarr[0])
|
||||||
|
{
|
||||||
|
err = gpg_error_from_syserror ();
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
resarr[1] = gcry_mpi_set_opaque_copy (NULL, enc_seskey, 8 * enc_seskey_len);
|
||||||
|
if (!resarr[1])
|
||||||
|
{
|
||||||
|
err = gpg_error_from_syserror ();
|
||||||
|
gcry_mpi_release (resarr[0]);
|
||||||
|
}
|
||||||
|
|
||||||
leave:
|
leave:
|
||||||
gcry_mpi_release (public);
|
xfree (enc_seskey);
|
||||||
gcry_mpi_release (result);
|
gcry_cipher_close (hd);
|
||||||
xfree (shared);
|
xfree (kek);
|
||||||
gcry_sexp_release (s_ciph);
|
wipememory (ecc_ct, sizeof ecc_ct);
|
||||||
gcry_sexp_release (s_data);
|
wipememory (ecc_ecdh, sizeof ecc_ecdh);
|
||||||
gcry_sexp_release (s_pkey);
|
xfree (ecc_oid);
|
||||||
xfree (curve);
|
|
||||||
gcry_mpi_release (k);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -917,9 +1111,9 @@ do_encrypt_rsa_elg (PKT_public_key *pk, gcry_mpi_t data, gcry_mpi_t *resarr)
|
|||||||
* change the internal design to directly fit to libgcrypt. PK is is
|
* change the internal design to directly fit to libgcrypt. PK is is
|
||||||
* the OpenPGP public key packet, DATA is an MPI with the to be
|
* the OpenPGP public key packet, DATA is an MPI with the to be
|
||||||
* encrypted data, and RESARR receives the encrypted data. RESARRAY
|
* encrypted data, and RESARR receives the encrypted data. RESARRAY
|
||||||
* is expected to be an two item array which will be filled with newly
|
* is expected to be an two/three item array which will be filled with
|
||||||
* allocated MPIs. SESKEY_ALGO is required for public key algorithms
|
* newly allocated MPIs. SESKEY_ALGO is required for public key
|
||||||
* which do not encode it in DATA.
|
* algorithms which do not encode it in DATA.
|
||||||
*/
|
*/
|
||||||
gpg_error_t
|
gpg_error_t
|
||||||
pk_encrypt (PKT_public_key *pk, gcry_mpi_t data, int seskey_algo,
|
pk_encrypt (PKT_public_key *pk, gcry_mpi_t data, int seskey_algo,
|
||||||
|
12
g10/pkglue.h
12
g10/pkglue.h
@ -38,20 +38,12 @@ int pk_check_secret_key (pubkey_algo_t algo, gcry_mpi_t *skey);
|
|||||||
|
|
||||||
/*-- ecdh.c --*/
|
/*-- ecdh.c --*/
|
||||||
gcry_mpi_t pk_ecdh_default_params (unsigned int qbits);
|
gcry_mpi_t pk_ecdh_default_params (unsigned int qbits);
|
||||||
gpg_error_t pk_ecdh_generate_ephemeral_key (gcry_mpi_t *pkey, gcry_mpi_t *r_k);
|
|
||||||
gpg_error_t pk_ecdh_encrypt_with_shared_point
|
|
||||||
/* */ (const char *shared, size_t nshared,
|
|
||||||
const byte pk_fp[MAX_FINGERPRINT_LEN],
|
|
||||||
const byte *data, size_t ndata,
|
|
||||||
gcry_mpi_t *pkey,
|
|
||||||
gcry_mpi_t *out);
|
|
||||||
|
|
||||||
int pk_ecdh_encrypt (gcry_mpi_t *resarr, const byte pk_fp[MAX_FINGERPRINT_LEN],
|
|
||||||
gcry_mpi_t data, gcry_mpi_t * pkey);
|
|
||||||
int pk_ecdh_decrypt (gcry_mpi_t *result, const byte sk_fp[MAX_FINGERPRINT_LEN],
|
int pk_ecdh_decrypt (gcry_mpi_t *result, const byte sk_fp[MAX_FINGERPRINT_LEN],
|
||||||
gcry_mpi_t data,
|
gcry_mpi_t data,
|
||||||
const byte *frame, size_t nframe,
|
const byte *frame, size_t nframe,
|
||||||
gcry_mpi_t * skey);
|
gcry_mpi_t * skey);
|
||||||
|
|
||||||
|
gpg_error_t gnupg_ecc_6637_kdf (void *kek, size_t kek_len, int hashalgo,
|
||||||
|
const void *ecdh, size_t ecdh_len, PKT_public_key *pk);
|
||||||
|
|
||||||
#endif /*GNUPG_G10_PKGLUE_H*/
|
#endif /*GNUPG_G10_PKGLUE_H*/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user