diff options
Diffstat (limited to 'g10/ecdh.c')
-rw-r--r-- | g10/ecdh.c | 187 |
1 files changed, 104 insertions, 83 deletions
diff --git a/g10/ecdh.c b/g10/ecdh.c index 5bbea96c0..97d483838 100644 --- a/g10/ecdh.c +++ b/g10/ecdh.c @@ -82,15 +82,73 @@ pk_ecdh_default_params (unsigned int qbits) } +/* Extract x-component from the point (SHARED,NSHARED) and strore it + * in a new buffer at R_SECRET_X. POINT_NBYTES is the size to + * represent an EC point which is determined by the public key. + * SECRET_X_SIZE is the size of x component to represent an integer + * which is determined by the curve. */ +static gpg_error_t +extract_secret_x (byte **r_secret_x, + const char *shared, size_t nshared, + size_t point_nbytes, size_t secret_x_size) +{ + byte *secret_x; + + *r_secret_x = NULL; + + /* Extract X from the result. It must be in the format of: + 04 || X || Y + 40 || X + 41 || X + + Since it may come with the prefix, the size of point is larger + than or equals to the size of an integer X. We also better check + that the provided shared point is not larger than the size needed + to represent the point. */ + if (point_nbytes < secret_x_size) + return gpg_error (GPG_ERR_BAD_DATA); + if (point_nbytes < nshared) + return gpg_error (GPG_ERR_BAD_DATA); + + /* Extract x component of the shared point: this is the actual + shared secret. */ + secret_x = xtrymalloc_secure (point_nbytes); + if (!secret_x) + return gpg_error_from_syserror (); + + memcpy (secret_x, shared, nshared); + + /* Wrangle the provided point unless only the x-component w/o any + * prefix was provided. */ + if (nshared != secret_x_size) + { + /* Remove the prefix. */ + if ((point_nbytes & 1)) + memmove (secret_x, secret_x+1, secret_x_size); + + /* Clear the rest of data. */ + if (point_nbytes - secret_x_size) + memset (secret_x+secret_x_size, 0, point_nbytes-secret_x_size); + } + + if (DBG_CRYPTO) + log_printhex (secret_x, secret_x_size, "ECDH shared secret X is:"); + + *r_secret_x = secret_x; + return 0; +} + + /* Encrypts/decrypts DATA using a key derived from the ECC shared - point SHARED_MPI using the FIPS SP 800-56A compliant method + point (SHARED,NSHARED) using the FIPS SP 800-56A compliant method key_derivation+key_wrapping. If IS_ENCRYPT is true the function encrypts; if false, it decrypts. 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 (int is_encrypt, gcry_mpi_t shared_mpi, +pk_ecdh_encrypt_with_shared_point (int is_encrypt, + const char *shared, size_t nshared, const byte pk_fp[MAX_FINGERPRINT_LEN], gcry_mpi_t data, gcry_mpi_t *pkey, gcry_mpi_t *r_result) @@ -103,78 +161,15 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, size_t kek_params_size; int kdf_hash_algo; int kdf_encr_algo; + size_t kek_size; unsigned char message[256]; size_t message_size; *r_result = NULL; - nbits = pubkey_nbits (PUBKEY_ALGO_ECDH, pkey); - if (!nbits) - return gpg_error (GPG_ERR_TOO_SHORT); - - { - size_t nbytes; - - /* Extract x component of the shared point: this is the actual - shared secret. */ - nbytes = (mpi_get_nbits (pkey[1] /* public point */)+7)/8; - secret_x = xtrymalloc_secure (nbytes); - if (!secret_x) - return gpg_error_from_syserror (); - - err = gcry_mpi_print (GCRYMPI_FMT_USG, secret_x, nbytes, - &nbytes, shared_mpi); - if (err) - { - xfree (secret_x); - log_error ("ECDH ephemeral export of shared point failed: %s\n", - gpg_strerror (err)); - return err; - } - - /* Expected size of the x component */ - secret_x_size = (nbits+7)/8; - - /* Extract X from the result. It must be in the format of: - 04 || X || Y - 40 || X - 41 || X - - Since it always comes with the prefix, it's larger than X. In - old experimental version of libgcrypt, there is a case where it - returns X with no prefix of 40, so, nbytes == secret_x_size - is allowed. */ - if (nbytes < secret_x_size) - { - xfree (secret_x); - return gpg_error (GPG_ERR_BAD_DATA); - } - - /* Remove the prefix. */ - if ((nbytes & 1)) - memmove (secret_x, secret_x+1, secret_x_size); - - /* Clear the rest of data. */ - if (nbytes - secret_x_size) - memset (secret_x+secret_x_size, 0, nbytes-secret_x_size); - - if (DBG_CRYPTO) - log_printhex (secret_x, secret_x_size, "ECDH shared secret X is:"); - } - - /*** We have now the shared secret bytes in secret_x. ***/ - - /* At this point we are done with PK encryption and the rest of the - * function uses symmetric key encryption techniques to protect the - * input DATA. The following two sections will simply replace - * current secret_x with a value derived from it. This will become - * a KEK. - */ if (!gcry_mpi_get_flag (pkey[2], GCRYMPI_FLAG_OPAQUE)) - { - xfree (secret_x); - return gpg_error (GPG_ERR_BUG); - } + return gpg_error (GPG_ERR_BUG); + kek_params = gcry_mpi_get_opaque (pkey[2], &nbits); kek_params_size = (nbits+7)/8; @@ -183,10 +178,7 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, /* Expect 4 bytes 03 01 hash_alg symm_alg. */ if (kek_params_size != 4 || kek_params[0] != 3 || kek_params[1] != 1) - { - xfree (secret_x); - return gpg_error (GPG_ERR_BAD_PUBKEY); - } + return gpg_error (GPG_ERR_BAD_PUBKEY); kdf_hash_algo = kek_params[2]; kdf_encr_algo = kek_params[3]; @@ -199,17 +191,43 @@ pk_ecdh_encrypt_with_shared_point (int is_encrypt, gcry_mpi_t shared_mpi, if (kdf_hash_algo != GCRY_MD_SHA256 && kdf_hash_algo != GCRY_MD_SHA384 && kdf_hash_algo != GCRY_MD_SHA512) - { - xfree (secret_x); - return gpg_error (GPG_ERR_BAD_PUBKEY); - } + return gpg_error (GPG_ERR_BAD_PUBKEY); + if (kdf_encr_algo != CIPHER_ALGO_AES && kdf_encr_algo != CIPHER_ALGO_AES192 && kdf_encr_algo != CIPHER_ALGO_AES256) - { - xfree (secret_x); - return gpg_error (GPG_ERR_BAD_PUBKEY); - } + return gpg_error (GPG_ERR_BAD_PUBKEY); + + kek_size = gcry_cipher_get_algo_keylen (kdf_encr_algo); + if (kek_size > gcry_md_get_algo_dlen (kdf_hash_algo)) + return gpg_error (GPG_ERR_BAD_PUBKEY); + + + nbits = pubkey_nbits (PUBKEY_ALGO_ECDH, pkey); + if (!nbits) + return gpg_error (GPG_ERR_TOO_SHORT); + + /* Expected size of the x component */ + secret_x_size = (nbits+7)/8; + + if (kek_size > secret_x_size) + return gpg_error (GPG_ERR_BAD_PUBKEY); + + err = extract_secret_x (&secret_x, shared, nshared, + (mpi_get_nbits (pkey[1] /* public point */)+7)/8, + secret_x_size); + if (err) + return err; + + /*** We have now the shared secret bytes in secret_x. ***/ + + /* At this point we are done with PK encryption and the rest of the + * function uses symmetric key encryption techniques to protect the + * input DATA. The following two sections will simply replace + * current secret_x with a value derived from it. This will become + * a KEK. + */ + /* Build kdf_params. */ { @@ -484,12 +502,15 @@ pk_ecdh_generate_ephemeral_key (gcry_mpi_t *pkey, gcry_mpi_t *r_k) /* Perform ECDH decryption. */ int -pk_ecdh_decrypt (gcry_mpi_t * result, const byte sk_fp[MAX_FINGERPRINT_LEN], - gcry_mpi_t data, gcry_mpi_t shared, gcry_mpi_t * skey) +pk_ecdh_decrypt (gcry_mpi_t *result, const byte sk_fp[MAX_FINGERPRINT_LEN], + gcry_mpi_t data, + const char *shared, size_t nshared, + gcry_mpi_t *skey) { if (!data) return gpg_error (GPG_ERR_BAD_MPI); - return pk_ecdh_encrypt_with_shared_point (0 /*=decryption*/, shared, + return pk_ecdh_encrypt_with_shared_point (0 /*=decryption*/, + shared, nshared, sk_fp, data/*encr data as an MPI*/, skey, result); } |