[RFC PATCH v1 08/13] SAE: Add DecoyAuth worker support

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


Parallelize cached H2E candidate preparation and interpolation matrix
construction and weaving. Retain the serial paths when worker support is
not selected.

Signed-off-by: Jeff Hansen <x at jeffhansen.com>
---
 src/common/sae.c            | 329 ++++++++++++++++++++++++++++++++++++
 src/crypto/crypto_openssl.c | 268 +++++++++++++++++++++++++++++
 2 files changed, 597 insertions(+)

diff --git a/src/common/sae.c b/src/common/sae.c
index 687407d58..c5491490e 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -21,6 +21,9 @@
 #include "ieee802_11_defs.h"
 #include "dragonfly.h"
 #include "sae.h"
+#ifdef CONFIG_SAE_DECOYAUTH_THREADS
+#include <pthread.h>
+#endif /* CONFIG_SAE_DECOYAUTH_THREADS */
 
 
 int sae_set_group(struct sae_data *sae, int group)
@@ -1474,6 +1477,320 @@ int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
 
 
 #ifdef CONFIG_SAE_DECOYAUTH
+#ifdef CONFIG_SAE_DECOYAUTH_THREADS
+struct sae_candidate_batch {
+	const u8 **passwords;
+	const size_t *password_lens;
+	const u8 *pt_bins;
+	const u8 *mask_bin;
+	const u8 *addr_scalar_bin;
+	size_t prime_len;
+	size_t order_len;
+	int group;
+	int num_passwords;
+	int next;
+	int failed;
+	int failed_candidate;
+	struct crypto_bignum **hash_values;
+	struct crypto_bignum **u_values;
+	struct crypto_bignum **v_values;
+	u8 *pwe_bins;
+	u8 *commit_bins;
+	pthread_mutex_t lock;
+};
+
+
+static struct crypto_bignum *
+sae_decoyauth_addr_scalar(struct crypto_ec *ec, size_t prime_len,
+			  const u8 *addr1, const u8 *addr2)
+{
+	const u8 *addr[2];
+	size_t len[2];
+	u8 salt[64], hash[64];
+	size_t hash_len;
+	const struct crypto_bignum *order;
+	struct crypto_bignum *tmp = NULL, *val = NULL, *one = NULL;
+
+	sae_max_min_addr(addr, len, addr1, addr2);
+	hash_len = sae_ecc_prime_len_2_hash_len(prime_len);
+	os_memset(salt, 0, hash_len);
+	if (hkdf_extract(hash_len, salt, hash_len, 2, addr, len, hash) < 0)
+		return NULL;
+
+	order = crypto_ec_get_order(ec);
+	tmp = crypto_bignum_init();
+	val = crypto_bignum_init_set(hash, hash_len);
+	one = crypto_bignum_init_uint(1);
+	if (!order || !tmp || !val || !one ||
+	    crypto_bignum_sub(order, one, tmp) < 0 ||
+	    crypto_bignum_mod(val, tmp, val) < 0 ||
+	    crypto_bignum_add(val, one, val) < 0) {
+		crypto_bignum_deinit(val, 1);
+		val = NULL;
+	}
+	crypto_bignum_deinit(tmp, 1);
+	crypto_bignum_deinit(one, 0);
+	forced_memzero(hash, sizeof(hash));
+	return val;
+}
+
+
+static int sae_decoyauth_prepare_candidate(struct sae_candidate_batch *batch,
+					   struct sae_data *worker,
+					   struct crypto_bignum *mask,
+					   struct crypto_bignum *addr_scalar,
+					   int candidate)
+{
+	struct crypto_ec_point *pt = NULL;
+	struct crypto_bignum **encoded = NULL;
+	struct crypto_bignum *hash_value = NULL;
+	struct crypto_bignum *u_value = NULL, *v_value = NULL;
+	const u8 *pt_bin = batch->pt_bins +
+		2 * batch->prime_len * (size_t) candidate;
+	u8 hash[SHA256_MAC_LEN];
+	int ret = -1;
+
+	pt = crypto_ec_point_from_bin(worker->tmp->ec, pt_bin);
+	worker->tmp->pwe_ecc = crypto_ec_point_init(worker->tmp->ec);
+	if (!pt || !worker->tmp->pwe_ecc ||
+	    crypto_ec_point_mul(worker->tmp->ec, pt, addr_scalar,
+				worker->tmp->pwe_ecc) < 0 ||
+	    sae_derive_commit_element_ecc(worker, mask) < 0 ||
+	    sha256_vector(1, &batch->passwords[candidate],
+			  &batch->password_lens[candidate], hash) < 0)
+		goto out;
+
+	hash_value = crypto_bignum_init_set(hash, sizeof(hash));
+	encoded = crypto_point_to_values(worker->tmp->own_commit_element_ecc,
+					 worker->tmp->ec);
+	if (!hash_value || !encoded || !encoded[0] || !encoded[1] ||
+	    crypto_ec_point_to_bin(
+		    worker->tmp->ec, worker->tmp->pwe_ecc,
+		    batch->pwe_bins + 2 * batch->prime_len * candidate,
+		    batch->pwe_bins +
+		    batch->prime_len * (2 * candidate + 1)) < 0 ||
+	    crypto_ec_point_to_bin(
+		    worker->tmp->ec, worker->tmp->own_commit_element_ecc,
+		    batch->commit_bins + 2 * batch->prime_len * candidate,
+		    batch->commit_bins +
+		    batch->prime_len * (2 * candidate + 1)) < 0)
+		goto out;
+
+	u_value = encoded[0];
+	v_value = encoded[1];
+	encoded[0] = encoded[1] = NULL;
+	batch->hash_values[candidate] = hash_value;
+	batch->u_values[candidate] = u_value;
+	batch->v_values[candidate] = v_value;
+	hash_value = u_value = v_value = NULL;
+	ret = 0;
+out:
+	forced_memzero(hash, sizeof(hash));
+	crypto_ec_point_deinit(pt, 1);
+	crypto_bignum_deinit(hash_value, 1);
+	crypto_bignum_deinit(u_value, 1);
+	crypto_bignum_deinit(v_value, 1);
+	sae_free_bignum_array(&encoded, 2, 1);
+	crypto_ec_point_deinit(worker->tmp->pwe_ecc, 1);
+	worker->tmp->pwe_ecc = NULL;
+	crypto_ec_point_deinit(worker->tmp->own_commit_element_ecc, 1);
+	worker->tmp->own_commit_element_ecc = NULL;
+	return ret;
+}
+
+
+static void * sae_decoyauth_candidate_worker(void *ctx)
+{
+	struct sae_candidate_batch *batch = ctx;
+	struct sae_data worker;
+	struct crypto_bignum *mask = NULL, *addr_scalar = NULL;
+	int candidate;
+
+	os_memset(&worker, 0, sizeof(worker));
+	if (sae_set_group(&worker, batch->group) < 0)
+		goto init_fail;
+	mask = crypto_bignum_init_set(batch->mask_bin, batch->order_len);
+	addr_scalar = crypto_bignum_init_set(batch->addr_scalar_bin,
+					 batch->order_len);
+	if (!mask || !addr_scalar)
+		goto init_fail;
+
+	for (;;) {
+		pthread_mutex_lock(&batch->lock);
+		if (batch->failed || batch->next >= batch->num_passwords) {
+			pthread_mutex_unlock(&batch->lock);
+			break;
+		}
+		candidate = batch->next++;
+		pthread_mutex_unlock(&batch->lock);
+
+		if (sae_decoyauth_prepare_candidate(batch, &worker, mask,
+						 addr_scalar, candidate) < 0) {
+			pthread_mutex_lock(&batch->lock);
+			batch->failed = 1;
+			batch->failed_candidate = candidate;
+			pthread_mutex_unlock(&batch->lock);
+			break;
+		}
+	}
+	goto out;
+
+init_fail:
+	pthread_mutex_lock(&batch->lock);
+	batch->failed = 1;
+	batch->failed_candidate = -1;
+	pthread_mutex_unlock(&batch->lock);
+out:
+	crypto_bignum_deinit(mask, 1);
+	crypto_bignum_deinit(addr_scalar, 1);
+	sae_clear_data(&worker);
+	return NULL;
+}
+
+
+/*
+ * Return 1 when the cached H2E candidates were prepared in parallel, 0 when
+ * the parallel path is unavailable, and -1 after a worker failure.
+ */
+static int sae_decoyauth_prepare_candidates_parallel(
+	const u8 *addr1, const u8 *addr2,
+	const u8 **passwords, const size_t *password_lens,
+	const struct sae_pt * const *password_pts, int num_passwords,
+	struct crypto_bignum *mask, struct sae_data *sae,
+	struct crypto_bignum **hash_values,
+	struct crypto_bignum **u_values,
+	struct crypto_bignum **v_values)
+{
+	struct sae_candidate_batch batch;
+	struct crypto_bignum *addr_scalar = NULL;
+	struct os_reltime started_at, finished_at, elapsed;
+	pthread_t *threads = NULL;
+	u8 *mask_bin = NULL, *addr_scalar_bin = NULL;
+	u8 *pt_bins = NULL, *pwe_bins = NULL, *commit_bins = NULL;
+	long cpus;
+	int workers, started = 0, ret = 0, timed;
+	size_t point_bytes;
+
+	if (!password_pts || num_passwords < 2)
+		return 0;
+	cpus = sysconf(_SC_NPROCESSORS_ONLN);
+	if (cpus < 2)
+		return 0;
+	timed = os_get_reltime(&started_at) == 0;
+	workers = (int) cpus - 1;
+	if (workers > num_passwords)
+		workers = num_passwords;
+
+	os_memset(&batch, 0, sizeof(batch));
+	batch.passwords = passwords;
+	batch.password_lens = password_lens;
+	batch.prime_len = sae->tmp->prime_len;
+	batch.order_len = sae->tmp->order_len;
+	batch.group = sae->group;
+	batch.num_passwords = num_passwords;
+	batch.failed_candidate = -1;
+	batch.hash_values = hash_values;
+	batch.u_values = u_values;
+	batch.v_values = v_values;
+	point_bytes = 2 * batch.prime_len * (size_t) num_passwords;
+	mask_bin = os_malloc(batch.order_len);
+	addr_scalar_bin = os_malloc(batch.order_len);
+	pt_bins = os_malloc(point_bytes);
+	pwe_bins = os_malloc(point_bytes);
+	commit_bins = os_malloc(point_bytes);
+	threads = os_calloc(workers, sizeof(*threads));
+	if (!mask_bin || !addr_scalar_bin || !pt_bins || !pwe_bins ||
+	    !commit_bins || !threads)
+		goto out;
+
+	for (int i = 0; i < num_passwords; i++) {
+		const struct sae_pt *pt = password_pts[i];
+
+		while (pt && pt->group != sae->group)
+			pt = pt->next;
+		if (!pt || !pt->ec || !pt->ecc_pt ||
+		    crypto_ec_point_to_bin(
+			    pt->ec, pt->ecc_pt,
+			    pt_bins + 2 * batch.prime_len * i,
+			    pt_bins + batch.prime_len * (2 * i + 1)) < 0)
+			goto out;
+	}
+
+	addr_scalar = sae_decoyauth_addr_scalar(sae->tmp->ec,
+						batch.prime_len, addr1, addr2);
+	if (!addr_scalar ||
+	    crypto_bignum_to_bin(mask, mask_bin, batch.order_len,
+				 batch.order_len) < 0 ||
+	    crypto_bignum_to_bin(addr_scalar, addr_scalar_bin,
+				 batch.order_len, batch.order_len) < 0)
+		goto out;
+
+	batch.pt_bins = pt_bins;
+	batch.mask_bin = mask_bin;
+	batch.addr_scalar_bin = addr_scalar_bin;
+	batch.pwe_bins = pwe_bins;
+	batch.commit_bins = commit_bins;
+	if (pthread_mutex_init(&batch.lock, NULL) != 0)
+		goto out;
+
+	for (started = 0; started < workers; started++) {
+		if (pthread_create(&threads[started], NULL,
+				   sae_decoyauth_candidate_worker, &batch) != 0)
+			break;
+	}
+	if (!started)
+		sae_decoyauth_candidate_worker(&batch);
+	for (int i = 0; i < started; i++)
+		pthread_join(threads[i], NULL);
+	pthread_mutex_destroy(&batch.lock);
+
+	if (batch.failed) {
+		wpa_printf(MSG_DEBUG,
+			   "SAE: DecoyAuth parallel candidate preparation "
+			   "failed candidate=%d workers=%d",
+			   batch.failed_candidate, started ? started : 1);
+		ret = -1;
+		goto out;
+	}
+	for (int i = 0; i < num_passwords; i++) {
+		sae->tmp->pwe_eccs[i] = crypto_ec_point_from_bin(
+			sae->tmp->ec, pwe_bins + 2 * batch.prime_len * i);
+		sae->tmp->own_commit_element_eccs[i] =
+			crypto_ec_point_from_bin(
+				sae->tmp->ec,
+				commit_bins + 2 * batch.prime_len * i);
+		if (!sae->tmp->pwe_eccs[i] ||
+		    !sae->tmp->own_commit_element_eccs[i]) {
+			ret = -1;
+			goto out;
+		}
+	}
+	if (timed && os_get_reltime(&finished_at) == 0) {
+		os_reltime_sub(&finished_at, &started_at, &elapsed);
+		wpa_printf(MSG_DEBUG,
+			   "SAE: DecoyAuth prepared %d cached H2E "
+			   "candidates with %d workers in %ld ms",
+			   num_passwords, started ? started : 1,
+			   elapsed.sec * 1000L + elapsed.usec / 1000);
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "SAE: DecoyAuth prepared %d cached H2E candidates with %d workers",
+			   num_passwords, started ? started : 1);
+	}
+	ret = 1;
+out:
+	crypto_bignum_deinit(addr_scalar, 1);
+	bin_clear_free(mask_bin, batch.order_len);
+	bin_clear_free(addr_scalar_bin, batch.order_len);
+	bin_clear_free(pt_bins, point_bytes);
+	bin_clear_free(pwe_bins, point_bytes);
+	bin_clear_free(commit_bins, point_bytes);
+	os_free(threads);
+	return ret;
+}
+#endif /* CONFIG_SAE_DECOYAUTH_THREADS */
+
+
 static int sae_ap_prepare_commit_internal(const u8 *addr1, const u8 *addr2,
 					  const u8 *ssid, size_t ssid_len,
 					  const u8 **passwords,
@@ -1542,6 +1859,18 @@ static int sae_ap_prepare_commit_internal(const u8 *addr1, const u8 *addr2,
 		goto fail;
 	}
 
+#ifdef CONFIG_SAE_DECOYAUTH_THREADS
+	if (password_pts) {
+		fail_stage = "prepare cached H2E candidates in parallel";
+		candidates_ready = sae_decoyauth_prepare_candidates_parallel(
+			addr1, addr2, passwords, password_lens, password_pts,
+			num_passwords, mask, sae, hash_values, u_values,
+			v_values);
+		if (candidates_ready < 0)
+			goto fail;
+	}
+#endif /* CONFIG_SAE_DECOYAUTH_THREADS */
+
 	for (int i = 0; !candidates_ready && i < num_passwords; i++) {
 		struct crypto_bignum **encoded_point;
 		const struct sae_pt *pt = NULL;
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index 82dcc4f60..48a08d409 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -24,6 +24,9 @@
 #include <limits.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
+#ifdef CONFIG_SAE_DECOYAUTH_THREADS
+#include <pthread.h>
+#endif /* CONFIG_SAE_DECOYAUTH_THREADS */
 #endif /* CONFIG_SAE_DECOYAUTH_CACHE */
 #ifdef CONFIG_ECC
 #include <openssl/ec.h>
@@ -6371,6 +6374,270 @@ out:
 #define DECOYAUTH_CACHE_HEADER_LEN 64
 #define DECOYAUTH_CACHE_PREFIX "decoyauth-matrix-v1-"
 
+#ifdef CONFIG_SAE_DECOYAUTH_THREADS
+struct interpolation_matrix_batch {
+	BIGNUM **x;
+	BIGNUM **poly;
+	BIGNUM **weights;
+	const BIGNUM *prime;
+	u8 *matrix;
+	size_t element_len;
+	int count;
+	int next;
+	int failed;
+	pthread_mutex_t lock;
+};
+
+struct interpolation_weave_batch {
+	BIGNUM **first;
+	BIGNUM **second;
+	BIGNUM **first_result;
+	BIGNUM **second_result;
+	const BIGNUM *prime;
+	const u8 *matrix;
+	size_t element_len;
+	int count;
+	int next;
+	int failed;
+	pthread_mutex_t lock;
+};
+
+
+static long interpolation_elapsed_ms(const struct os_reltime *start,
+				     const struct os_reltime *end)
+{
+	struct os_reltime elapsed;
+
+	os_reltime_sub(end, start, &elapsed);
+	return elapsed.sec * 1000L + elapsed.usec / 1000;
+}
+
+
+static int interpolation_worker_count(int count)
+{
+	long cpus = sysconf(_SC_NPROCESSORS_ONLN);
+	int workers = cpus > 1 ? (int) cpus - 1 : 1;
+
+	return workers < count ? workers : count;
+}
+
+
+static void * interpolation_matrix_worker(void *ctx)
+{
+	struct interpolation_matrix_batch *batch = ctx;
+	BIGNUM **quotient = NULL;
+	BIGNUM *tmp = NULL;
+	BN_CTX *bnctx = NULL;
+	int column;
+
+	quotient = crypto_bignum_vector_alloc(batch->count);
+	tmp = BN_new();
+	bnctx = BN_CTX_new();
+	if (!quotient || !tmp || !bnctx)
+		goto fail;
+
+	for (;;) {
+		pthread_mutex_lock(&batch->lock);
+		column = batch->failed ? batch->count : batch->next++;
+		pthread_mutex_unlock(&batch->lock);
+		if (column >= batch->count)
+			break;
+
+		if (!BN_copy(quotient[batch->count - 1],
+			     batch->poly[batch->count]))
+			goto fail;
+		for (int row = batch->count - 2; row >= 0; row--) {
+			if (!BN_mod_mul(tmp, batch->x[column], quotient[row + 1],
+					batch->prime, bnctx) ||
+			    !BN_mod_add(quotient[row], batch->poly[row + 1], tmp,
+					batch->prime, bnctx))
+				goto fail;
+		}
+		for (int row = 0; row < batch->count; row++) {
+			u8 *out = batch->matrix +
+				((size_t) row * batch->count + column) *
+				batch->element_len;
+
+			if (!BN_mod_mul(tmp, quotient[row], batch->weights[column],
+					batch->prime, bnctx) ||
+			    BN_bn2binpad(tmp, out, batch->element_len) !=
+			    (int) batch->element_len)
+				goto fail;
+		}
+	}
+	goto out;
+
+fail:
+	pthread_mutex_lock(&batch->lock);
+	batch->failed = 1;
+	pthread_mutex_unlock(&batch->lock);
+out:
+	crypto_bignum_vector_free(quotient, batch->count);
+	BN_clear_free(tmp);
+	BN_CTX_free(bnctx);
+	return NULL;
+}
+
+
+static int interpolation_matrix_build(BIGNUM **x, int count,
+				      const BIGNUM *prime, BN_CTX *ctx,
+				      u8 *matrix, size_t element_len)
+{
+	struct interpolation_matrix_batch batch;
+	BIGNUM **poly = NULL, **weights = NULL;
+	pthread_t *threads = NULL;
+	int workers, started = 0, ret = -1;
+
+	if (crypto_interpolation_basis(x, count, prime, ctx,
+				       &poly, &weights) < 0)
+		return -1;
+
+	os_memset(&batch, 0, sizeof(batch));
+	batch.x = x;
+	batch.poly = poly;
+	batch.weights = weights;
+	batch.prime = prime;
+	batch.matrix = matrix;
+	batch.element_len = element_len;
+	batch.count = count;
+	workers = interpolation_worker_count(count);
+	threads = os_calloc(workers, sizeof(*threads));
+	if (!threads || pthread_mutex_init(&batch.lock, NULL) != 0)
+		goto out;
+
+	for (; started < workers; started++) {
+		if (pthread_create(&threads[started], NULL,
+				   interpolation_matrix_worker, &batch) != 0)
+			break;
+	}
+	if (!started)
+		interpolation_matrix_worker(&batch);
+	for (int i = 0; i < started; i++)
+		pthread_join(threads[i], NULL);
+	pthread_mutex_destroy(&batch.lock);
+	ret = batch.failed ? -1 : 0;
+out:
+	os_free(threads);
+	crypto_bignum_vector_free(poly, count + 1);
+	crypto_bignum_vector_free(weights, count);
+	return ret;
+}
+
+
+static void * interpolation_weave_worker(void *ctx)
+{
+	struct interpolation_weave_batch *batch = ctx;
+	BIGNUM *matrix_value = NULL, *product = NULL;
+	BIGNUM *first_sum = NULL, *second_sum = NULL;
+	BN_CTX *bnctx = NULL;
+	int row;
+
+	matrix_value = BN_new();
+	product = BN_new();
+	first_sum = BN_new();
+	second_sum = BN_new();
+	bnctx = BN_CTX_new();
+	if (!matrix_value || !product || !first_sum || !second_sum || !bnctx)
+		goto fail;
+
+	for (;;) {
+		pthread_mutex_lock(&batch->lock);
+		row = batch->failed ? batch->count : batch->next++;
+		pthread_mutex_unlock(&batch->lock);
+		if (row >= batch->count)
+			break;
+
+		BN_zero(first_sum);
+		BN_zero(second_sum);
+		for (int column = 0; column < batch->count; column++) {
+			const u8 *value = batch->matrix +
+				((size_t) row * batch->count + column) *
+				batch->element_len;
+
+			if (!BN_bin2bn(value, batch->element_len, matrix_value) ||
+			    !BN_mul(product, matrix_value, batch->first[column],
+				    bnctx) ||
+			    !BN_add(first_sum, first_sum, product) ||
+			    !BN_mul(product, matrix_value, batch->second[column],
+				    bnctx) ||
+			    !BN_add(second_sum, second_sum, product))
+				goto fail;
+		}
+		if (!BN_nnmod(batch->first_result[row], first_sum,
+			      batch->prime, bnctx) ||
+		    !BN_nnmod(batch->second_result[row], second_sum,
+			      batch->prime, bnctx))
+			goto fail;
+	}
+	goto out;
+
+fail:
+	pthread_mutex_lock(&batch->lock);
+	batch->failed = 1;
+	pthread_mutex_unlock(&batch->lock);
+out:
+	BN_clear_free(matrix_value);
+	BN_clear_free(product);
+	BN_clear_free(first_sum);
+	BN_clear_free(second_sum);
+	BN_CTX_free(bnctx);
+	return NULL;
+}
+
+
+static int interpolation_matrix_weave_pair(
+	const u8 *matrix, size_t element_len,
+	BIGNUM **first, BIGNUM **second, int count, const BIGNUM *prime,
+	BIGNUM ***first_out, BIGNUM ***second_out)
+{
+	struct interpolation_weave_batch batch;
+	BIGNUM **first_result = NULL, **second_result = NULL;
+	pthread_t *threads = NULL;
+	int workers, started = 0, ret = -1;
+
+	first_result = crypto_bignum_vector_alloc(count);
+	second_result = crypto_bignum_vector_alloc(count);
+	if (!first_result || !second_result)
+		goto out;
+
+	os_memset(&batch, 0, sizeof(batch));
+	batch.first = first;
+	batch.second = second;
+	batch.first_result = first_result;
+	batch.second_result = second_result;
+	batch.prime = prime;
+	batch.matrix = matrix;
+	batch.element_len = element_len;
+	batch.count = count;
+	workers = interpolation_worker_count(count);
+	threads = os_calloc(workers, sizeof(*threads));
+	if (!threads || pthread_mutex_init(&batch.lock, NULL) != 0)
+		goto out;
+
+	for (; started < workers; started++) {
+		if (pthread_create(&threads[started], NULL,
+				   interpolation_weave_worker, &batch) != 0)
+			break;
+	}
+	if (!started)
+		interpolation_weave_worker(&batch);
+	for (int i = 0; i < started; i++)
+		pthread_join(threads[i], NULL);
+	pthread_mutex_destroy(&batch.lock);
+	if (batch.failed)
+		goto out;
+
+	*first_out = first_result;
+	*second_out = second_result;
+	first_result = second_result = NULL;
+	ret = 0;
+out:
+	os_free(threads);
+	crypto_bignum_vector_free(first_result, count);
+	crypto_bignum_vector_free(second_result, count);
+	return ret;
+}
+#else /* CONFIG_SAE_DECOYAUTH_THREADS */
 static int interpolation_matrix_build(BIGNUM **x, int count,
 				      const BIGNUM *prime, BN_CTX *ctx,
 				      u8 *matrix, size_t element_len)
@@ -6473,6 +6740,7 @@ out:
 	BN_CTX_free(ctx);
 	return ret;
 }
+#endif /* CONFIG_SAE_DECOYAUTH_THREADS */
 
 
 static int interpolation_cache_mkdir(const char *directory)
-- 
2.53.0




More information about the Hostap mailing list