[RFC PATCH v1 04/13] crypto: Add DecoyAuth encoding and interpolation support

Jeff Hansen x at jeffhansen.com
Fri Sep 11 12:20:01 PDT 2026


Add reversible point encoding and paired polynomial interpolation for
DecoyAuth while keeping OpenSSL representations behind the generic
crypto API.

Signed-off-by: Jeff Hansen <x at jeffhansen.com>
---
 src/crypto/crypto.h         |  70 ++++
 src/crypto/crypto_openssl.c | 637 ++++++++++++++++++++++++++++++++++++
 2 files changed, 707 insertions(+)

diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index e6bc7bac3..3db2fe80e 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -852,6 +852,19 @@ int crypto_ec_point_to_bin(struct crypto_ec *e,
 struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
 						  const u8 *val);
 
+#ifdef CONFIG_SAE_DECOYAUTH
+/**
+ * crypto_ec_point_copy - Copy an EC point
+ * @e: EC context from crypto_ec_init()
+ * @src: Source EC point
+ * @dst: Destination EC point
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_point_copy(struct crypto_ec *e,
+			 const struct crypto_ec_point *src,
+			 struct crypto_ec_point *dst);
+#endif /* CONFIG_SAE_DECOYAUTH */
+
 /**
  * crypto_ec_point_add - c = a + b
  * @e: EC context from crypto_ec_init()
@@ -1386,4 +1399,61 @@ struct wpabuf * hpke_base_open(enum hpke_kem_id kem_id,
  */
 void crypto_unload(void);
 
+#ifdef CONFIG_SAE_DECOYAUTH
+/**
+ * crypto_point_to_values - Encode an EC point as two field elements
+ * @point: EC point
+ * @ec: EC context
+ * Returns: An allocated two-element array, or %NULL on failure
+ *
+ * The caller is responsible for freeing both elements and the array.
+ */
+struct crypto_bignum **
+crypto_point_to_values(struct crypto_ec_point *point, struct crypto_ec *ec);
+
+/**
+ * crypto_values_to_point - Decode two field elements as an EC point
+ * @values: Two-element array from crypto_point_to_values()
+ * @ec: EC context
+ * Returns: An allocated EC point, or %NULL on failure
+ */
+struct crypto_ec_point *
+crypto_values_to_point(struct crypto_bignum **values, struct crypto_ec *ec);
+
+/**
+ * crypto_interpolate_pair - Interpolate two polynomials at common x values
+ * @x_values: Interpolation x coordinates
+ * @first_values: Values for the first polynomial
+ * @second_values: Values for the second polynomial
+ * @num_elements: Number of values in each input array
+ * @ec: EC context that defines the field
+ * @first_coefficients: Returned coefficients for the first polynomial
+ * @second_coefficients: Returned coefficients for the second polynomial
+ * Returns: 0 on success, -1 on failure
+ *
+ * The returned arrays contain @num_elements allocated bignums. The caller is
+ * responsible for freeing the bignums and arrays.
+ */
+int crypto_interpolate_pair(struct crypto_bignum **x_values,
+			    struct crypto_bignum **first_values,
+			    struct crypto_bignum **second_values,
+			    int num_elements, struct crypto_ec *ec,
+			    struct crypto_bignum ***first_coefficients,
+			    struct crypto_bignum ***second_coefficients);
+
+
+/**
+ * crypto_evaluate - Evaluate a polynomial over an EC field
+ * @poly: Polynomial coefficients, least-significant coefficient first
+ * @x: Evaluation point
+ * @num_elements: Number of coefficients in @poly
+ * @ec: EC context that defines the field
+ * Returns: An allocated bignum containing the result, or %NULL on failure
+ */
+struct crypto_bignum * crypto_evaluate(struct crypto_bignum **poly,
+				       struct crypto_bignum *x,
+				       int num_elements,
+				       struct crypto_ec *ec);
+#endif /* CONFIG_SAE_DECOYAUTH */
+
 #endif /* CRYPTO_H */
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index a6bfc7a7c..2a9f6f77b 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -2643,6 +2643,20 @@ struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
 }
 
 
+#ifdef CONFIG_SAE_DECOYAUTH
+int crypto_ec_point_copy(struct crypto_ec *e,
+			 const struct crypto_ec_point *src,
+			 struct crypto_ec_point *dst)
+{
+	(void) e;
+	if (TEST_FAIL())
+		return -1;
+	return EC_POINT_copy((EC_POINT *) dst, (const EC_POINT *) src) == 1 ?
+		0 : -1;
+}
+#endif /* CONFIG_SAE_DECOYAUTH */
+
+
 int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
 			const struct crypto_ec_point *b,
 			struct crypto_ec_point *c)
@@ -5731,3 +5745,626 @@ void crypto_unload(void)
 	openssl_unload_legacy_provider();
 	openssl_unload_default_provider();
 }
+
+#ifdef CONFIG_SAE_DECOYAUTH
+static BIGNUM * decoyauth_evaluate(BIGNUM **poly, const BIGNUM *x,
+				   int num_elements, const BIGNUM *prime,
+				   BN_CTX *ctx)
+{
+	BIGNUM *result;
+	int i;
+
+	if (!poly || !x || num_elements <= 0 || !prime || !ctx)
+		return NULL;
+
+	result = BN_dup(poly[num_elements - 1]);
+	if (!result)
+		return NULL;
+	for (i = num_elements - 2; i >= 0; i--) {
+		if (!poly[i] ||
+		    !BN_mod_mul(result, result, x, prime, ctx) ||
+		    !BN_mod_add(result, result, poly[i], prime, ctx)) {
+			BN_clear_free(result);
+			return NULL;
+		}
+	}
+
+	return result;
+}
+
+static BIGNUM * decoyauth_random_bignum(const BIGNUM *prime)
+{
+	BIGNUM *value;
+
+	value = BN_new();
+	if (!value || !BN_rand_range(value, prime)) {
+		BN_clear_free(value);
+		return NULL;
+	}
+
+	/* BN_rand_range() includes zero, while this encoding does not. */
+	if (BN_is_zero(value) && !BN_one(value)) {
+		BN_clear_free(value);
+		return NULL;
+	}
+
+	return value;
+}
+
+
+static int decoyauth_random_branch(void)
+{
+	u8 value;
+
+	if (RAND_bytes(&value, sizeof(value)) != 1)
+		return -1;
+
+	return value % 4;
+}
+
+
+static bool decoyauth_u_valid(const BIGNUM *u, const BIGNUM *prime)
+{
+	BIGNUM *minus_one;
+	bool valid;
+
+	minus_one = BN_dup(prime);
+	if (!minus_one || !BN_sub_word(minus_one, 1)) {
+		BN_clear_free(minus_one);
+		return false;
+	}
+
+	valid = !BN_is_zero(u) && !BN_is_one(u) &&
+		BN_cmp(u, minus_one) != 0;
+	BN_clear_free(minus_one);
+	return valid;
+}
+
+
+static BIGNUM * decoyauth_x0(const BIGNUM *u, const BIGNUM *a,
+			     const BIGNUM *b, const BIGNUM *prime)
+{
+	BIGNUM *result = NULL;
+	BIGNUM *u2, *u4, *denominator, *inverse, *factor, *a_inverse;
+	BN_CTX *ctx;
+
+	ctx = BN_CTX_new();
+	if (!ctx)
+		return NULL;
+	BN_CTX_start(ctx);
+	u2 = BN_CTX_get(ctx);
+	u4 = BN_CTX_get(ctx);
+	denominator = BN_CTX_get(ctx);
+	inverse = BN_CTX_get(ctx);
+	factor = BN_CTX_get(ctx);
+	a_inverse = BN_CTX_get(ctx);
+	result = BN_new();
+	if (!a_inverse || !result ||
+	    !BN_mod_sqr(u2, u, prime, ctx) ||
+	    !BN_mod_sqr(u4, u2, prime, ctx) ||
+	    !BN_mod_sub(denominator, u4, u2, prime, ctx) ||
+	    !BN_mod_inverse(inverse, denominator, prime, ctx) ||
+	    !BN_add_word(inverse, 1) ||
+	    !BN_nnmod(inverse, inverse, prime, ctx) ||
+	    !BN_mod_inverse(a_inverse, a, prime, ctx) ||
+	    !BN_mod_mul(factor, b, a_inverse, prime, ctx) ||
+	    !BN_mod_mul(result, factor, inverse, prime, ctx) ||
+	    !BN_mod_sub(result, prime, result, prime, ctx)) {
+		BN_clear_free(result);
+		result = NULL;
+	}
+	BN_CTX_end(ctx);
+	BN_CTX_free(ctx);
+	return result;
+}
+
+
+static BIGNUM * decoyauth_x1(const BIGNUM *u, const BIGNUM *a,
+			     const BIGNUM *b, const BIGNUM *prime)
+{
+	BIGNUM *result = NULL, *x0, *u2;
+	BN_CTX *ctx;
+
+	ctx = BN_CTX_new();
+	x0 = decoyauth_x0(u, a, b, prime);
+	if (!ctx || !x0)
+		goto out;
+	BN_CTX_start(ctx);
+	u2 = BN_CTX_get(ctx);
+	result = BN_new();
+	if (!u2 || !result || !BN_mod_sqr(u2, u, prime, ctx) ||
+	    !BN_mod_mul(result, u2, x0, prime, ctx) ||
+	    !BN_mod_sub(result, prime, result, prime, ctx)) {
+		BN_clear_free(result);
+		result = NULL;
+	}
+	BN_CTX_end(ctx);
+out:
+	BN_clear_free(x0);
+	BN_CTX_free(ctx);
+	return result;
+}
+
+
+static BIGNUM * decoyauth_curve_rhs(const BIGNUM *x, const BIGNUM *a,
+				    const BIGNUM *b, const BIGNUM *prime)
+{
+	BIGNUM *result = NULL, *tmp;
+	BN_CTX *ctx;
+
+	ctx = BN_CTX_new();
+	if (!ctx)
+		return NULL;
+	BN_CTX_start(ctx);
+	tmp = BN_CTX_get(ctx);
+	result = BN_new();
+	if (!tmp || !result || !BN_mod_sqr(result, x, prime, ctx) ||
+	    !BN_mod_mul(result, result, x, prime, ctx) ||
+	    !BN_mod_mul(tmp, a, x, prime, ctx) ||
+	    !BN_mod_add(result, result, tmp, prime, ctx) ||
+	    !BN_mod_add(result, result, b, prime, ctx)) {
+		BN_clear_free(result);
+		result = NULL;
+	}
+	BN_CTX_end(ctx);
+	BN_CTX_free(ctx);
+	return result;
+}
+
+
+static EC_POINT * decoyauth_map_to_point(const BIGNUM *u, EC_GROUP *group,
+					 const BIGNUM *a, const BIGNUM *b,
+					 const BIGNUM *prime)
+{
+	EC_POINT *result = NULL;
+	BIGNUM *x = NULL, *rhs = NULL, *y = NULL;
+	BN_CTX *ctx;
+
+	ctx = BN_CTX_new();
+	result = EC_POINT_new(group);
+	if (!ctx || !result)
+		goto fail;
+	if (!decoyauth_u_valid(u, prime)) {
+		if (EC_POINT_set_to_infinity(group, result) != 1)
+			goto fail;
+		goto out;
+	}
+
+	x = decoyauth_x0(u, a, b, prime);
+	rhs = x ? decoyauth_curve_rhs(x, a, b, prime) : NULL;
+	y = BN_new();
+	if (!rhs || !y)
+		goto fail;
+	if (BN_mod_sqrt(y, rhs, prime, ctx)) {
+		if (EC_POINT_set_affine_coordinates(group, result, x, y,
+						    ctx) != 1)
+			goto fail;
+		goto out;
+	}
+	BN_clear_free(x);
+	BN_clear_free(rhs);
+	x = decoyauth_x1(u, a, b, prime);
+	rhs = x ? decoyauth_curve_rhs(x, a, b, prime) : NULL;
+	if (!rhs || !BN_mod_sqrt(y, rhs, prime, ctx) ||
+	    !BN_mod_sub(y, prime, y, prime, ctx) ||
+	    EC_POINT_set_affine_coordinates(group, result, x, y, ctx) != 1)
+		goto fail;
+	goto out;
+
+fail:
+	EC_POINT_free(result);
+	result = NULL;
+out:
+	BN_clear_free(x);
+	BN_clear_free(rhs);
+	BN_clear_free(y);
+	BN_CTX_free(ctx);
+	return result;
+}
+
+
+static BIGNUM * decoyauth_point_to_v(const EC_POINT *q, int branch,
+				     const EC_GROUP *group, const BIGNUM *a,
+				     const BIGNUM *b, const BIGNUM *prime)
+{
+	BIGNUM *result = NULL;
+	BIGNUM *x, *y, *b_inverse, *omega, *root, *tmp, *multiplier;
+	BN_CTX *ctx;
+	bool y_is_square;
+
+	if (branch < 0 || branch > 3)
+		return NULL;
+	ctx = BN_CTX_new();
+	if (!ctx)
+		return NULL;
+	BN_CTX_start(ctx);
+	x = BN_CTX_get(ctx);
+	y = BN_CTX_get(ctx);
+	b_inverse = BN_CTX_get(ctx);
+	omega = BN_CTX_get(ctx);
+	root = BN_CTX_get(ctx);
+	tmp = BN_CTX_get(ctx);
+	multiplier = BN_CTX_get(ctx);
+	if (!multiplier ||
+	    EC_POINT_get_affine_coordinates(group, q, x, y, ctx) != 1 ||
+	    !BN_mod_inverse(b_inverse, b, prime, ctx) ||
+	    !BN_mod_mul(omega, a, b_inverse, prime, ctx) ||
+	    !BN_mod_mul(omega, omega, x, prime, ctx) ||
+	    !BN_add_word(omega, 1) || !BN_nnmod(omega, omega, prime, ctx) ||
+	    !BN_mod_sqr(root, omega, prime, ctx) ||
+	    !BN_mod_lshift1(tmp, omega, prime, ctx) ||
+	    !BN_mod_lshift1(tmp, tmp, prime, ctx) ||
+	    !BN_mod_sub(root, root, tmp, prime, ctx) ||
+	    !BN_mod_sqrt(root, root, prime, ctx))
+		goto out;
+	if (branch > 1 && !BN_mod_sub(root, prime, root, prime, ctx))
+		goto out;
+
+	y_is_square = BN_mod_sqrt(tmp, y, prime, ctx) != NULL;
+	if (y_is_square) {
+		if (!BN_mod_lshift1(multiplier, omega, prime, ctx) ||
+		    !BN_mod_inverse(multiplier, multiplier, prime, ctx))
+			goto out;
+	} else {
+		if (!BN_set_word(multiplier, 2) ||
+		    !BN_mod_inverse(multiplier, multiplier, prime, ctx))
+			goto out;
+	}
+	if (!BN_mod_add(tmp, omega, root, prime, ctx) ||
+	    !BN_mod_mul(tmp, tmp, multiplier, prime, ctx) ||
+	    !BN_mod_sqrt(tmp, tmp, prime, ctx))
+		goto out;
+	if (branch == 1 || branch == 3) {
+		if (!BN_mod_sub(tmp, prime, tmp, prime, ctx))
+			goto out;
+	}
+	result = BN_dup(tmp);
+out:
+	BN_CTX_end(ctx);
+	BN_CTX_free(ctx);
+	return result;
+}
+
+
+static BIGNUM ** point_to_values(EC_POINT *point, EC_GROUP *group,
+				 BIGNUM *a, BIGNUM *b, BIGNUM *prime)
+{
+	BIGNUM **values;
+	BIGNUM *u, *v;
+	EC_POINT *mapped, *difference;
+	int branch, i;
+
+	if (!point || !group || !a || !b || !prime)
+		return NULL;
+
+	for (i = 0; i < 1000; i++) {
+		u = decoyauth_random_bignum(prime);
+		if (!u || !decoyauth_u_valid(u, prime)) {
+			BN_clear_free(u);
+			continue;
+		}
+
+		mapped = decoyauth_map_to_point(u, group, a, b, prime);
+		difference = EC_POINT_new(group);
+		if (!mapped || !difference ||
+		    EC_POINT_invert(group, mapped, NULL) != 1 ||
+		    EC_POINT_add(group, difference, point, mapped, NULL) != 1 ||
+		    EC_POINT_is_at_infinity(group, difference)) {
+			EC_POINT_free(mapped);
+			EC_POINT_free(difference);
+			BN_clear_free(u);
+			continue;
+		}
+		EC_POINT_free(mapped);
+
+		branch = decoyauth_random_branch();
+		v = branch < 0 ? NULL :
+			decoyauth_point_to_v(difference, branch, group, a, b,
+					     prime);
+		EC_POINT_free(difference);
+		if (!v) {
+			BN_clear_free(u);
+			if (branch < 0)
+				return NULL;
+			continue;
+		}
+
+		values = os_malloc(2 * sizeof(*values));
+		if (!values) {
+			BN_clear_free(u);
+			BN_clear_free(v);
+			return NULL;
+		}
+		values[0] = u;
+		values[1] = v;
+		return values;
+	}
+
+	return NULL;
+}
+
+
+static EC_POINT * values_to_point(BIGNUM **encoded_point, EC_GROUP *group,
+				  BIGNUM *a, BIGNUM *b, BIGNUM *prime)
+{
+	EC_POINT *f_u = NULL, *f_v = NULL, *result = NULL;
+	BN_CTX *ctx = NULL;
+
+	if (!encoded_point || !encoded_point[0] || !encoded_point[1] ||
+	    !group || !a || !b || !prime)
+		return NULL;
+
+	f_u = decoyauth_map_to_point(encoded_point[0], group, a, b, prime);
+	f_v = decoyauth_map_to_point(encoded_point[1], group, a, b, prime);
+	ctx = BN_CTX_new();
+	result = EC_POINT_new(group);
+	if (!f_u || !f_v || !ctx || !result ||
+	    EC_POINT_add(group, result, f_u, f_v, ctx) != 1) {
+		EC_POINT_free(result);
+		result = NULL;
+	}
+
+	EC_POINT_free(f_u);
+	EC_POINT_free(f_v);
+	BN_CTX_free(ctx);
+	return result;
+}
+
+struct crypto_bignum **
+crypto_point_to_values(struct crypto_ec_point *point, struct crypto_ec *ec)
+{
+	if (!point || !ec)
+		return NULL;
+	return (struct crypto_bignum **)
+		point_to_values((EC_POINT *) point, ec->group, ec->a, ec->b,
+				ec->prime);
+}
+
+struct crypto_ec_point *
+crypto_values_to_point(struct crypto_bignum **point, struct crypto_ec *ec)
+{
+	BIGNUM *input[2];
+
+	if (!point || !point[0] || !point[1] || !ec)
+		return NULL;
+	input[0] = (BIGNUM *) point[0];
+	input[1] = (BIGNUM *) point[1];
+	return (struct crypto_ec_point *)
+		values_to_point(input, ec->group, ec->a, ec->b, ec->prime);
+}
+
+static void crypto_bignum_vector_free(BIGNUM **values, int count)
+{
+	if (!values)
+		return;
+	for (int i = 0; i < count; i++)
+		BN_clear_free(values[i]);
+	os_free(values);
+}
+
+
+static BIGNUM ** crypto_bignum_vector_alloc(int count)
+{
+	BIGNUM **values;
+
+	values = os_calloc(count, sizeof(*values));
+	if (!values)
+		return NULL;
+	for (int i = 0; i < count; i++) {
+		values[i] = BN_new();
+		if (!values[i]) {
+			crypto_bignum_vector_free(values, count);
+			return NULL;
+		}
+	}
+	return values;
+}
+
+
+static int crypto_interpolation_basis(BIGNUM **x, int count,
+				      const BIGNUM *prime, BN_CTX *ctx,
+				      BIGNUM ***poly_out,
+				      BIGNUM ***weights_out)
+{
+	BIGNUM **poly = NULL, **derivative = NULL;
+	BIGNUM **denominators = NULL, **weights = NULL;
+	BIGNUM *zero = NULL, *tmp = NULL, *inverse = NULL;
+	int ret = -1;
+
+	*poly_out = NULL;
+	*weights_out = NULL;
+	poly = crypto_bignum_vector_alloc(count + 1);
+	derivative = crypto_bignum_vector_alloc(count);
+	denominators = crypto_bignum_vector_alloc(count);
+	weights = crypto_bignum_vector_alloc(count);
+	zero = BN_new();
+	tmp = BN_new();
+	inverse = BN_new();
+	if (!poly || !derivative || !denominators || !weights || !zero ||
+	    !tmp || !inverse)
+		goto out;
+
+	/* P(x) = product(x - x_i), with coefficients stored low first. */
+	BN_zero(zero);
+	if (!BN_one(poly[0]))
+		goto out;
+	for (int i = 0; i < count; i++) {
+		for (int j = i + 1; j > 0; j--) {
+			if (!BN_mod_mul(tmp, x[i], poly[j], prime, ctx) ||
+			    !BN_mod_sub(poly[j], poly[j - 1], tmp, prime, ctx))
+				goto out;
+		}
+		if (!BN_mod_mul(tmp, x[i], poly[0], prime, ctx) ||
+		    !BN_mod_sub(poly[0], zero, tmp, prime, ctx))
+			goto out;
+	}
+
+	for (int i = 1; i <= count; i++) {
+		if (!BN_copy(derivative[i - 1], poly[i]) ||
+		    !BN_mul_word(derivative[i - 1], (BN_ULONG) i) ||
+		    !BN_nnmod(derivative[i - 1], derivative[i - 1], prime,
+			      ctx))
+			goto out;
+	}
+	for (int i = 0; i < count; i++) {
+		if (!BN_copy(denominators[i], derivative[count - 1]))
+			goto out;
+		for (int j = count - 2; j >= 0; j--) {
+			if (!BN_mod_mul(tmp, denominators[i], x[i], prime,
+					ctx) ||
+			    !BN_mod_add(denominators[i], tmp, derivative[j],
+					prime, ctx))
+				goto out;
+		}
+		if (BN_is_zero(denominators[i]))
+			goto out;
+	}
+
+	/* Batch-invert P'(x_i), requiring only one modular inversion. */
+	if (!BN_copy(weights[0], denominators[0]))
+		goto out;
+	for (int i = 1; i < count; i++) {
+		if (!BN_mod_mul(weights[i], weights[i - 1], denominators[i],
+				prime, ctx))
+			goto out;
+	}
+	if (!BN_mod_inverse(inverse, weights[count - 1], prime, ctx))
+		goto out;
+	for (int i = count - 1; i >= 0; i--) {
+		if (i > 0) {
+			if (!BN_mod_mul(tmp, inverse, weights[i - 1], prime,
+					ctx))
+				goto out;
+		} else if (!BN_copy(tmp, inverse)) {
+			goto out;
+		}
+		if (i > 0 &&
+		    !BN_mod_mul(inverse, inverse, denominators[i], prime, ctx))
+			goto out;
+		if (!BN_copy(weights[i], tmp))
+			goto out;
+	}
+
+	*poly_out = poly;
+	*weights_out = weights;
+	poly = weights = NULL;
+	ret = 0;
+out:
+	crypto_bignum_vector_free(poly, count + 1);
+	crypto_bignum_vector_free(derivative, count);
+	crypto_bignum_vector_free(denominators, count);
+	crypto_bignum_vector_free(weights, count);
+	BN_clear_free(zero);
+	BN_clear_free(tmp);
+	BN_clear_free(inverse);
+	return ret;
+}
+
+
+int crypto_interpolate_pair(struct crypto_bignum **x_values,
+			    struct crypto_bignum **first_values,
+			    struct crypto_bignum **second_values,
+			    int num_elements, struct crypto_ec *ec,
+			    struct crypto_bignum ***first_coefficients,
+			    struct crypto_bignum ***second_coefficients)
+{
+	BIGNUM **x = (BIGNUM **) x_values;
+	BIGNUM **first = (BIGNUM **) first_values;
+	BIGNUM **second = (BIGNUM **) second_values;
+	BIGNUM **poly = NULL, **weights = NULL;
+	BIGNUM **quotient = NULL, **first_result = NULL;
+	BIGNUM **second_result = NULL;
+	BIGNUM *tmp = NULL;
+	BIGNUM *first_factor = NULL, *second_factor = NULL;
+	BIGNUM *prime;
+	BN_CTX *ctx;
+	int ret = -1;
+
+	if (!x || !first || !second || num_elements <= 0 || !ec ||
+	    !first_coefficients || !second_coefficients ||
+	    first_coefficients == second_coefficients)
+		return -1;
+	*first_coefficients = NULL;
+	*second_coefficients = NULL;
+	prime = ec->prime;
+	ctx = ec->bnctx;
+	if (!prime || !ctx)
+		return -1;
+	for (int i = 0; i < num_elements; i++) {
+		if (!x[i] || !first[i] || !second[i])
+			return -1;
+	}
+
+	quotient = crypto_bignum_vector_alloc(num_elements);
+	first_result = crypto_bignum_vector_alloc(num_elements);
+	second_result = crypto_bignum_vector_alloc(num_elements);
+	tmp = BN_new();
+	first_factor = BN_new();
+	second_factor = BN_new();
+	if (!quotient || !first_result || !second_result || !tmp ||
+	    !first_factor || !second_factor ||
+	    crypto_interpolation_basis(x, num_elements, prime, ctx,
+				       &poly, &weights) < 0)
+		goto out;
+
+	/* Accumulate both interpolants without materializing an n-by-n matrix. */
+	for (int i = 0; i < num_elements; i++) {
+		if (!BN_copy(quotient[num_elements - 1], poly[num_elements]))
+			goto out;
+		for (int j = num_elements - 2; j >= 0; j--) {
+			if (!BN_mod_mul(tmp, x[i], quotient[j + 1],
+					prime, ctx) ||
+			    !BN_mod_add(quotient[j], poly[j + 1], tmp,
+					prime, ctx))
+				goto out;
+		}
+		if (!BN_mod_mul(first_factor, first[i], weights[i],
+				prime, ctx) ||
+		    !BN_mod_mul(second_factor, second[i], weights[i],
+				prime, ctx))
+			goto out;
+		for (int j = 0; j < num_elements; j++) {
+			if (!BN_mod_mul(tmp, quotient[j], first_factor,
+					prime, ctx) ||
+			    !BN_mod_add(first_result[j], first_result[j], tmp,
+					prime, ctx) ||
+			    !BN_mod_mul(tmp, quotient[j], second_factor,
+					prime, ctx) ||
+			    !BN_mod_add(second_result[j], second_result[j], tmp,
+					prime, ctx))
+				goto out;
+		}
+	}
+
+	*first_coefficients = (struct crypto_bignum **) first_result;
+	*second_coefficients = (struct crypto_bignum **) second_result;
+	first_result = NULL;
+	second_result = NULL;
+	ret = 0;
+out:
+	crypto_bignum_vector_free(poly, num_elements + 1);
+	crypto_bignum_vector_free(weights, num_elements);
+	crypto_bignum_vector_free(quotient, num_elements);
+	crypto_bignum_vector_free(first_result, num_elements);
+	crypto_bignum_vector_free(second_result, num_elements);
+	BN_clear_free(tmp);
+	BN_clear_free(first_factor);
+	BN_clear_free(second_factor);
+	return ret;
+}
+
+
+
+
+struct crypto_bignum * crypto_evaluate(struct crypto_bignum **poly,
+				       struct crypto_bignum *x,
+				       int num_elements,
+				       struct crypto_ec *ec)
+{
+	if (!poly || !x || num_elements <= 0 || !ec)
+		return NULL;
+	return (struct crypto_bignum *)
+		decoyauth_evaluate((BIGNUM **) poly, (BIGNUM *) x,
+				   num_elements, ec->prime, ec->bnctx);
+}
+#endif /* CONFIG_SAE_DECOYAUTH */
-- 
2.53.0




More information about the Hostap mailing list