]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - crypto/ecc.c
perf probe: Fix to show calling lines of inlined functions
[mirror_ubuntu-bionic-kernel.git] / crypto / ecc.c
index 633a9bcdc5746ae46845529d3fff02a0260e2e42..d672b6865fd78bbe1de6f14aa3284f8918850826 100644 (file)
@@ -898,36 +898,50 @@ static void ecc_point_mult(struct ecc_point *result,
 static inline void ecc_swap_digits(const u64 *in, u64 *out,
                                   unsigned int ndigits)
 {
+       const __be64 *src = (__force __be64 *)in;
        int i;
 
        for (i = 0; i < ndigits; i++)
-               out[i] = __swab64(in[ndigits - 1 - i]);
+               out[i] = be64_to_cpu(src[ndigits - 1 - i]);
 }
 
-int ecc_is_key_valid(unsigned int curve_id, unsigned int ndigits,
-                    const u64 *private_key, unsigned int private_key_len)
+static int __ecc_is_key_valid(const struct ecc_curve *curve,
+                             const u64 *private_key, unsigned int ndigits)
 {
-       int nbytes;
-       const struct ecc_curve *curve = ecc_get_curve(curve_id);
+       u64 one[ECC_MAX_DIGITS] = { 1, };
+       u64 res[ECC_MAX_DIGITS];
 
        if (!private_key)
                return -EINVAL;
 
-       nbytes = ndigits << ECC_DIGITS_TO_BYTES_SHIFT;
-
-       if (private_key_len != nbytes)
+       if (curve->g.ndigits != ndigits)
                return -EINVAL;
 
-       if (vli_is_zero(private_key, ndigits))
+       /* Make sure the private key is in the range [2, n-3]. */
+       if (vli_cmp(one, private_key, ndigits) != -1)
                return -EINVAL;
-
-       /* Make sure the private key is in the range [1, n-1]. */
-       if (vli_cmp(curve->n, private_key, ndigits) != 1)
+       vli_sub(res, curve->n, one, ndigits);
+       vli_sub(res, res, one, ndigits);
+       if (vli_cmp(res, private_key, ndigits) != 1)
                return -EINVAL;
 
        return 0;
 }
 
+int ecc_is_key_valid(unsigned int curve_id, unsigned int ndigits,
+                    const u64 *private_key, unsigned int private_key_len)
+{
+       int nbytes;
+       const struct ecc_curve *curve = ecc_get_curve(curve_id);
+
+       nbytes = ndigits << ECC_DIGITS_TO_BYTES_SHIFT;
+
+       if (private_key_len != nbytes)
+               return -EINVAL;
+
+       return __ecc_is_key_valid(curve, private_key, ndigits);
+}
+
 /*
  * ECC private keys are generated using the method of extra random bits,
  * equivalent to that described in FIPS 186-4, Appendix B.4.1.
@@ -964,18 +978,15 @@ int ecc_gen_privkey(unsigned int curve_id, unsigned int ndigits, u64 *privkey)
         * DRBG with a security strength of 256.
         */
        if (crypto_get_default_rng())
-               err = -EFAULT;
+               return -EFAULT;
 
        err = crypto_rng_get_bytes(crypto_default_rng, (u8 *)priv, nbytes);
        crypto_put_default_rng();
        if (err)
                return err;
 
-       if (vli_is_zero(priv, ndigits))
-               return -EINVAL;
-
-       /* Make sure the private key is in the range [1, n-1]. */
-       if (vli_cmp(curve->n, priv, ndigits) != 1)
+       /* Make sure the private key is in the valid range. */
+       if (__ecc_is_key_valid(curve, priv, ndigits))
                return -EINVAL;
 
        ecc_swap_digits(priv, privkey, ndigits);
@@ -1019,6 +1030,36 @@ out:
        return ret;
 }
 
+/* SP800-56A section 5.6.2.3.4 partial verification: ephemeral keys only */
+static int ecc_is_pubkey_valid_partial(const struct ecc_curve *curve,
+                                      struct ecc_point *pk)
+{
+       u64 yy[ECC_MAX_DIGITS], xxx[ECC_MAX_DIGITS], w[ECC_MAX_DIGITS];
+
+       /* Check 1: Verify key is not the zero point. */
+       if (ecc_point_is_zero(pk))
+               return -EINVAL;
+
+       /* Check 2: Verify key is in the range [1, p-1]. */
+       if (vli_cmp(curve->p, pk->x, pk->ndigits) != 1)
+               return -EINVAL;
+       if (vli_cmp(curve->p, pk->y, pk->ndigits) != 1)
+               return -EINVAL;
+
+       /* Check 3: Verify that y^2 == (x^3 + a·x + b) mod p */
+       vli_mod_square_fast(yy, pk->y, curve->p, pk->ndigits); /* y^2 */
+       vli_mod_square_fast(xxx, pk->x, curve->p, pk->ndigits); /* x^2 */
+       vli_mod_mult_fast(xxx, xxx, pk->x, curve->p, pk->ndigits); /* x^3 */
+       vli_mod_mult_fast(w, curve->a, pk->x, curve->p, pk->ndigits); /* a·x */
+       vli_mod_add(w, w, curve->b, curve->p, pk->ndigits); /* a·x + b */
+       vli_mod_add(w, w, xxx, curve->p, pk->ndigits); /* x^3 + a·x + b */
+       if (vli_cmp(yy, w, pk->ndigits) != 0) /* Equation */
+               return -EINVAL;
+
+       return 0;
+
+}
+
 int crypto_ecdh_shared_secret(unsigned int curve_id, unsigned int ndigits,
                              const u64 *private_key, const u64 *public_key,
                              u64 *secret)
@@ -1045,16 +1086,20 @@ 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);
+       ret = ecc_is_pubkey_valid_partial(curve, pk);
+       if (ret)
+               goto err_alloc_product;
+
+       ecc_swap_digits(private_key, priv, ndigits);
+
        product = ecc_alloc_point(ndigits);
        if (!product) {
                ret = -ENOMEM;
                goto err_alloc_product;
        }
 
-       ecc_swap_digits(public_key, pk->x, ndigits);
-       ecc_swap_digits(&public_key[ndigits], pk->y, ndigits);
-       ecc_swap_digits(private_key, priv, ndigits);
-
        ecc_point_mult(product, pk, priv, rand_z, curve->p, ndigits);
 
        ecc_swap_digits(product->x, secret, ndigits);