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

Message ID 20240215231414.3857320-14-stefanb@linux.ibm.com
State New
Headers
Series Add support for NIST P521 to ecdsa and ecdh |

Commit Message

Stefan Berger Feb. 15, 2024, 11:14 p.m. UTC
  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@linux.ibm.com>
---
 crypto/ecc.c                  | 27 +++++++++++++++------------
 crypto/ecdh.c                 | 26 +++++++++++++++-----------
 include/crypto/internal/ecc.h | 10 +++++++---
 3 files changed, 37 insertions(+), 26 deletions(-)
  

Patch

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