[PATCH v2 13/14] crypto: ecdh - Use functions to copy digits from and to byte array

From: Stefan Berger
Date: Thu Feb 15 2024 - 18:18:13 EST


All curves supported so far provide full 64bit digit arrays to convert
coordinates from and to. For NIST P521 only 8 digits and 2 bytes will be
given per coordinate so that conversion from 9 digits does not work since
some bytes are missing. Therefore, regard the input (output)
arrays as byte arrays that need to be converted to digits (from digits).
Use ecc_digits_from_bytes to convert a byte array to digits and
ecc_digits_to_bytes to convert digits to a byte array.

crypt_ecdh_shared_secret creates 'rand_z' from 'nbytes' of random bytes.
Also convert this array to fill a byte array with 'nbytes' of random bytes
and create the rand_z digits from the byte array. The most significant
digit of rand_z needs to be adjusted to mask out unnecessary bits beyond
the 521 bits of NIST P521. Therefore, apply the appropriate mask to the
most significant digit keeping only the 9 most significant bits.

Signed-off-by: Stefan Berger <stefanb@xxxxxxxxxxxxx>
---
crypto/ecc.c | 27 +++++++++++++++------------
crypto/ecdh.c | 26 +++++++++++++++-----------
include/crypto/internal/ecc.h | 10 +++++++---
3 files changed, 37 insertions(+), 26 deletions(-)

diff --git a/crypto/ecc.c b/crypto/ecc.c
index f643719450b8..ced77eb6e533 100644
--- a/crypto/ecc.c
+++ b/crypto/ecc.c
@@ -1542,7 +1542,8 @@ int ecc_gen_privkey(unsigned int curve_id, unsigned int ndigits, u64 *privkey)
EXPORT_SYMBOL(ecc_gen_privkey);

int ecc_make_pub_key(unsigned int curve_id, unsigned int ndigits,
- const u64 *private_key, u64 *public_key)
+ const u64 *private_key, u8 *public_key,
+ unsigned int nbytes)
{
int ret = 0;
struct ecc_point *pk;
@@ -1570,8 +1571,8 @@ int ecc_make_pub_key(unsigned int curve_id, unsigned int ndigits,
goto err_free_point;
}

- ecc_swap_digits(pk->x, public_key, ndigits);
- ecc_swap_digits(pk->y, &public_key[ndigits], ndigits);
+ ecc_digits_to_bytes(pk->x, ndigits, public_key, nbytes);
+ ecc_digits_to_bytes(pk->y, ndigits, &public_key[nbytes], nbytes);

err_free_point:
ecc_free_point(pk);
@@ -1641,14 +1642,14 @@ int ecc_is_pubkey_valid_full(const struct ecc_curve *curve,
EXPORT_SYMBOL(ecc_is_pubkey_valid_full);

int crypto_ecdh_shared_secret(unsigned int curve_id, unsigned int ndigits,
- const u64 *private_key, const u64 *public_key,
- u64 *secret)
+ const u64 *private_key, const u8 *public_key,
+ unsigned int nbytes, u8 *secret, u64 msd_mask)
{
int ret = 0;
struct ecc_point *product, *pk;
u64 priv[ECC_MAX_DIGITS];
u64 rand_z[ECC_MAX_DIGITS];
- unsigned int nbytes;
+ u8 tmp[ECC_MAX_DIGITS << ECC_DIGITS_TO_BYTES_SHIFT];
const struct ecc_curve *curve = ecc_get_curve(curve_id);

if (!private_key || !public_key || !curve ||
@@ -1657,9 +1658,10 @@ int crypto_ecdh_shared_secret(unsigned int curve_id, unsigned int ndigits,
goto out;
}

- nbytes = ndigits << ECC_DIGITS_TO_BYTES_SHIFT;
-
- get_random_bytes(rand_z, nbytes);
+ get_random_bytes(tmp, nbytes);
+ ecc_digits_from_bytes(tmp, nbytes, rand_z, ndigits);
+ if (msd_mask)
+ rand_z[ndigits - 1] &= msd_mask;

pk = ecc_alloc_point(ndigits);
if (!pk) {
@@ -1667,8 +1669,9 @@ int crypto_ecdh_shared_secret(unsigned int curve_id, unsigned int ndigits,
goto out;
}

- ecc_swap_digits(public_key, pk->x, ndigits);
- ecc_swap_digits(&public_key[ndigits], pk->y, ndigits);
+ ecc_digits_from_bytes(public_key, nbytes, pk->x, ndigits);
+ ecc_digits_from_bytes(&public_key[nbytes], nbytes, pk->y, ndigits);
+
ret = ecc_is_pubkey_valid_partial(curve, pk);
if (ret)
goto err_alloc_product;
@@ -1688,7 +1691,7 @@ int crypto_ecdh_shared_secret(unsigned int curve_id, unsigned int ndigits,
goto err_validity;
}

- ecc_swap_digits(product->x, secret, ndigits);
+ ecc_digits_to_bytes(product->x, ndigits, secret, nbytes);

err_validity:
memzero_explicit(priv, sizeof(priv));
diff --git a/crypto/ecdh.c b/crypto/ecdh.c
index f187365db7b6..e64133428552 100644
--- a/crypto/ecdh.c
+++ b/crypto/ecdh.c
@@ -15,6 +15,8 @@
struct ecdh_ctx {
unsigned int curve_id;
unsigned int ndigits;
+ unsigned int nbytes;
+ u64 msd_mask;
u64 private_key[ECC_MAX_DIGITS];
};

@@ -28,7 +30,6 @@ static int ecdh_set_secret(struct crypto_kpp *tfm, const void *buf,
{
struct ecdh_ctx *ctx = ecdh_get_ctx(tfm);
u64 priv[ECC_MAX_DIGITS];
- unsigned int nbytes;
struct ecdh params;

if (crypto_ecdh_decode_key(buf, len, &params) < 0 ||
@@ -39,10 +40,8 @@ static int ecdh_set_secret(struct crypto_kpp *tfm, const void *buf,
return ecc_gen_privkey(ctx->curve_id, ctx->ndigits,
ctx->private_key);

- nbytes = ctx->ndigits << ECC_DIGITS_TO_BYTES_SHIFT;
-
- memcpy(ctx->private_key, params.key, nbytes);
- ecc_swap_digits(ctx->private_key, priv, ctx->ndigits);
+ ecc_digits_from_bytes(params.key, ctx->nbytes, priv, ctx->ndigits);
+ ecc_swap_digits(priv, ctx->private_key, ctx->ndigits);

if (ecc_is_key_valid(ctx->curve_id, ctx->ndigits,
priv, params.key_size) < 0) {
@@ -56,13 +55,13 @@ static int ecdh_compute_value(struct kpp_request *req)
{
struct crypto_kpp *tfm = crypto_kpp_reqtfm(req);
struct ecdh_ctx *ctx = ecdh_get_ctx(tfm);
- u64 *public_key;
- u64 *shared_secret = NULL;
+ unsigned int nbytes = ctx->nbytes;
+ u8 *public_key;
+ u8 *shared_secret = NULL;
void *buf;
- size_t copied, nbytes, public_key_sz;
+ size_t copied, public_key_sz;
int ret = -ENOMEM;

- nbytes = ctx->ndigits << ECC_DIGITS_TO_BYTES_SHIFT;
/* Public part is a point thus it has both coordinates */
public_key_sz = 2 * nbytes;

@@ -91,12 +90,14 @@ static int ecdh_compute_value(struct kpp_request *req)

ret = crypto_ecdh_shared_secret(ctx->curve_id, ctx->ndigits,
ctx->private_key, public_key,
- shared_secret);
+ nbytes, shared_secret,
+ ctx->msd_mask);

buf = shared_secret;
} else {
ret = ecc_make_pub_key(ctx->curve_id, ctx->ndigits,
- ctx->private_key, public_key);
+ ctx->private_key, public_key,
+ nbytes);
buf = public_key;
nbytes = public_key_sz;
}
@@ -134,6 +135,7 @@ static int ecdh_nist_p192_init_tfm(struct crypto_kpp *tfm)

ctx->curve_id = ECC_CURVE_NIST_P192;
ctx->ndigits = ECC_CURVE_NIST_P192_DIGITS;
+ ctx->nbytes = ctx->ndigits << ECC_DIGITS_TO_BYTES_SHIFT;

return 0;
}
@@ -159,6 +161,7 @@ static int ecdh_nist_p256_init_tfm(struct crypto_kpp *tfm)

ctx->curve_id = ECC_CURVE_NIST_P256;
ctx->ndigits = ECC_CURVE_NIST_P256_DIGITS;
+ ctx->nbytes = ctx->ndigits << ECC_DIGITS_TO_BYTES_SHIFT;

return 0;
}
@@ -184,6 +187,7 @@ static int ecdh_nist_p384_init_tfm(struct crypto_kpp *tfm)

ctx->curve_id = ECC_CURVE_NIST_P384;
ctx->ndigits = ECC_CURVE_NIST_P384_DIGITS;
+ ctx->nbytes = ctx->ndigits << ECC_DIGITS_TO_BYTES_SHIFT;

return 0;
}
diff --git a/include/crypto/internal/ecc.h b/include/crypto/internal/ecc.h
index 5d485d3221d3..328a82bd35c8 100644
--- a/include/crypto/internal/ecc.h
+++ b/include/crypto/internal/ecc.h
@@ -138,12 +138,14 @@ int ecc_gen_privkey(unsigned int curve_id, unsigned int ndigits, u64 *privkey);
* @ndigits: curve's number of digits
* @private_key: pregenerated private key for the given curve
* @public_key: buffer for storing the generated public key
+ * @nbytes: number of bytes per coordinate of public key
*
* Returns 0 if the public key was generated successfully, a negative value
* if an error occurred.
*/
int ecc_make_pub_key(const unsigned int curve_id, unsigned int ndigits,
- const u64 *private_key, u64 *public_key);
+ const u64 *private_key, u8 *public_key,
+ unsigned int nbytes);

/**
* crypto_ecdh_shared_secret() - Compute a shared secret
@@ -152,7 +154,9 @@ int ecc_make_pub_key(const unsigned int curve_id, unsigned int ndigits,
* @ndigits: curve's number of digits
* @private_key: private key of part A
* @public_key: public key of counterpart B
+ * @nbytes: number of bytes per coordinate of public key
* @secret: buffer for storing the calculated shared secret
+ * @msd_mask: optional mask to apply to the most significant digit
*
* Note: It is recommended that you hash the result of crypto_ecdh_shared_secret
* before using it for symmetric encryption or HMAC.
@@ -161,8 +165,8 @@ int ecc_make_pub_key(const unsigned int curve_id, unsigned int ndigits,
* if an error occurred.
*/
int crypto_ecdh_shared_secret(unsigned int curve_id, unsigned int ndigits,
- const u64 *private_key, const u64 *public_key,
- u64 *secret);
+ const u64 *private_key, const u8 *public_key,
+ unsigned int nbytes, u8 *secret, u64 msd_mask);

/**
* ecc_is_pubkey_valid_partial() - Partial public key validation
--
2.43.0