Use public multi-scalar mults in Trust Tokens where applicable.
The input points are all public, so we can use a faster multi-scalar
multiplication. This generalizes ec_point_mul_scalar_public to
ec_point_mul_scalar_public_batch. To support the batched DLEQ
construction, this function takes an arbirarily-length array of points
and allocates some temporaries if necessary.
First, to confirm that this doesn't affect the basic ECDSA verify case:
Before:
Did 6324 ECDSA P-384 verify operations in 3069342us (2060.4 ops/sec)
After:
Did 6324 ECDSA P-384 verify operations in 3063355us (2064.4 ops/sec) [+0.2%]
Results for Trust Tokens issue (Exp1) and finish_issuance (both):
Before:
Did 147 TrustToken-Exp0-Batch1 finish_issuance operations in 2059145us (71.4 ops/sec)
Did 14 TrustToken-Exp0-Batch10 finish_issuance operations in 2085888us (6.7 ops/sec)
Did 357 TrustToken-Exp1-Batch1 issue operations in 2068238us (172.6 ops/sec)
Did 286 TrustToken-Exp1-Batch1 finish_issuance operations in 2090932us (136.8 ops/sec)
Did 63 TrustToken-Exp1-Batch10 issue operations in 2068201us (30.5 ops/sec)
Did 56 TrustToken-Exp1-Batch10 finish_issuance operations in 2064796us (27.1 ops/sec)
After:
Did 168 TrustToken-Exp0-Batch1 finish_issuance operations in 2058891us (81.6 ops/sec) [+14.3%]
Did 16 TrustToken-Exp0-Batch10 finish_issuance operations in 2075742us (7.7 ops/sec) [+14.8%]
Did 378 TrustToken-Exp1-Batch1 issue operations in 2067956us (182.8 ops/sec) [+5.9%]
Did 336 TrustToken-Exp1-Batch1 finish_issuance operations in 2097757us (160.2 ops/sec) [+17.1%]
Did 105 TrustToken-Exp1-Batch10 issue operations in 2069934us (50.7 ops/sec) [+66.5%]
Did 88 TrustToken-Exp1-Batch10 finish_issuance operations in 2014621us (43.7 ops/sec) [+61.1%]
(This CL doesn't affect other operations.)
Change-Id: Ie643b06f44990ab52bf892a007732fde61cdffe5
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/41285
Reviewed-by: Steven Valdez <svaldez@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/crypto/fipsmodule/ec/ec.c b/crypto/fipsmodule/ec/ec.c
index 89ce64e..ab2fd89 100644
--- a/crypto/fipsmodule/ec/ec.c
+++ b/crypto/fipsmodule/ec/ec.c
@@ -1006,10 +1006,27 @@
return 0;
}
+ if (group->meth->mul_public == NULL) {
+ return group->meth->mul_public_batch(group, r, g_scalar, p, p_scalar, 1);
+ }
+
group->meth->mul_public(group, r, g_scalar, p, p_scalar);
return 1;
}
+int ec_point_mul_scalar_public_batch(const EC_GROUP *group, EC_RAW_POINT *r,
+ const EC_SCALAR *g_scalar,
+ const EC_RAW_POINT *points,
+ const EC_SCALAR *scalars, size_t num) {
+ if (group->meth->mul_public_batch == NULL) {
+ OPENSSL_PUT_ERROR(EC, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+ return 0;
+ }
+
+ return group->meth->mul_public_batch(group, r, g_scalar, points, scalars,
+ num);
+}
+
int ec_point_mul_scalar(const EC_GROUP *group, EC_RAW_POINT *r,
const EC_RAW_POINT *p, const EC_SCALAR *scalar) {
if (p == NULL || scalar == NULL) {
diff --git a/crypto/fipsmodule/ec/ec_montgomery.c b/crypto/fipsmodule/ec/ec_montgomery.c
index e9a0958..21d5d40 100644
--- a/crypto/fipsmodule/ec/ec_montgomery.c
+++ b/crypto/fipsmodule/ec/ec_montgomery.c
@@ -508,7 +508,7 @@
out->mul = ec_GFp_mont_mul;
out->mul_base = ec_GFp_mont_mul_base;
out->mul_batch = ec_GFp_mont_mul_batch;
- out->mul_public = ec_GFp_mont_mul_public;
+ out->mul_public_batch = ec_GFp_mont_mul_public_batch;
out->init_precomp = ec_GFp_mont_init_precomp;
out->mul_precomp = ec_GFp_mont_mul_precomp;
out->felem_mul = ec_GFp_mont_felem_mul;
diff --git a/crypto/fipsmodule/ec/internal.h b/crypto/fipsmodule/ec/internal.h
index 278c071..836d3ca 100644
--- a/crypto/fipsmodule/ec/internal.h
+++ b/crypto/fipsmodule/ec/internal.h
@@ -389,6 +389,19 @@
const EC_RAW_POINT *p,
const EC_SCALAR *p_scalar);
+// ec_point_mul_scalar_public_batch sets |r| to the sum of generator *
+// |g_scalar| and |points[i]| * |scalars[i]| where |points| and |scalars| have
+// |num| elements. It assumes that the inputs are public so there is no concern
+// about leaking their values through timing. |g_scalar| may be NULL to skip
+// that term.
+//
+// This function is not implemented for all curves. Add implementations as
+// needed.
+int ec_point_mul_scalar_public_batch(const EC_GROUP *group, EC_RAW_POINT *r,
+ const EC_SCALAR *g_scalar,
+ const EC_RAW_POINT *points,
+ const EC_SCALAR *scalars, size_t num);
+
// ec_point_select, in constant time, sets |out| to |a| if |mask| is all ones
// and |b| if |mask| is all zeros.
void ec_point_select(const EC_GROUP *group, EC_RAW_POINT *out, BN_ULONG mask,
@@ -483,9 +496,15 @@
// mul_public sets |r| to |g_scalar|*generator + |p_scalar|*|p|. It assumes
// that the inputs are public so there is no concern about leaking their
// values through timing.
+ //
+ // This function may be omitted if |mul_public_batch| is provided.
void (*mul_public)(const EC_GROUP *group, EC_RAW_POINT *r,
const EC_SCALAR *g_scalar, const EC_RAW_POINT *p,
const EC_SCALAR *p_scalar);
+ // mul_public_batch implements |ec_point_mul_scalar_public_batch|.
+ int (*mul_public_batch)(const EC_GROUP *group, EC_RAW_POINT *r,
+ const EC_SCALAR *g_scalar, const EC_RAW_POINT *points,
+ const EC_SCALAR *scalars, size_t num);
// init_precomp implements |ec_init_precomp|.
int (*init_precomp)(const EC_GROUP *group, EC_PRECOMP *out,
@@ -632,9 +651,10 @@
void ec_compute_wNAF(const EC_GROUP *group, int8_t *out,
const EC_SCALAR *scalar, size_t bits, int w);
-void ec_GFp_mont_mul_public(const EC_GROUP *group, EC_RAW_POINT *r,
- const EC_SCALAR *g_scalar, const EC_RAW_POINT *p,
- const EC_SCALAR *p_scalar);
+int ec_GFp_mont_mul_public_batch(const EC_GROUP *group, EC_RAW_POINT *r,
+ const EC_SCALAR *g_scalar,
+ const EC_RAW_POINT *points,
+ const EC_SCALAR *scalars, size_t num);
// method functions in simple.c
int ec_GFp_simple_group_init(EC_GROUP *);
diff --git a/crypto/fipsmodule/ec/wnaf.c b/crypto/fipsmodule/ec/wnaf.c
index fd1b480..ff90dcd 100644
--- a/crypto/fipsmodule/ec/wnaf.c
+++ b/crypto/fipsmodule/ec/wnaf.c
@@ -174,24 +174,57 @@
// EC_WNAF_TABLE_SIZE is the table size to use for |ec_GFp_mont_mul_public|.
#define EC_WNAF_TABLE_SIZE (1 << (EC_WNAF_WINDOW_BITS - 1))
-void ec_GFp_mont_mul_public(const EC_GROUP *group, EC_RAW_POINT *r,
- const EC_SCALAR *g_scalar, const EC_RAW_POINT *p,
- const EC_SCALAR *p_scalar) {
+// EC_WNAF_STACK is the number of points worth of data to stack-allocate and
+// avoid a malloc.
+#define EC_WNAF_STACK 3
+
+int ec_GFp_mont_mul_public_batch(const EC_GROUP *group, EC_RAW_POINT *r,
+ const EC_SCALAR *g_scalar,
+ const EC_RAW_POINT *points,
+ const EC_SCALAR *scalars, size_t num) {
size_t bits = BN_num_bits(&group->order);
size_t wNAF_len = bits + 1;
+ int ret = 0;
+ int8_t wNAF_stack[EC_WNAF_STACK][EC_MAX_BYTES * 8 + 1];
+ int8_t (*wNAF_alloc)[EC_MAX_BYTES * 8 + 1] = NULL;
+ int8_t (*wNAF)[EC_MAX_BYTES * 8 + 1];
+ EC_RAW_POINT precomp_stack[EC_WNAF_STACK][EC_WNAF_TABLE_SIZE];
+ EC_RAW_POINT (*precomp_alloc)[EC_WNAF_TABLE_SIZE] = NULL;
+ EC_RAW_POINT (*precomp)[EC_WNAF_TABLE_SIZE];
+ if (num <= EC_WNAF_STACK) {
+ wNAF = wNAF_stack;
+ precomp = precomp_stack;
+ } else {
+ if (num >= ((size_t)-1) / sizeof(wNAF_alloc[0]) ||
+ num >= ((size_t)-1) / sizeof(precomp_alloc[0])) {
+ OPENSSL_PUT_ERROR(EC, ERR_R_OVERFLOW);
+ goto err;
+ }
+ wNAF_alloc = OPENSSL_malloc(num * sizeof(wNAF_alloc[0]));
+ precomp_alloc = OPENSSL_malloc(num * sizeof(precomp_alloc[0]));
+ if (wNAF_alloc == NULL || precomp_alloc == NULL) {
+ OPENSSL_PUT_ERROR(EC, ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+ wNAF = wNAF_alloc;
+ precomp = precomp_alloc;
+ }
+
int8_t g_wNAF[EC_MAX_BYTES * 8 + 1];
EC_RAW_POINT g_precomp[EC_WNAF_TABLE_SIZE];
assert(wNAF_len <= OPENSSL_ARRAY_SIZE(g_wNAF));
const EC_RAW_POINT *g = &group->generator->raw;
- ec_compute_wNAF(group, g_wNAF, g_scalar, bits, EC_WNAF_WINDOW_BITS);
- compute_precomp(group, g_precomp, g, EC_WNAF_TABLE_SIZE);
+ if (g_scalar != NULL) {
+ ec_compute_wNAF(group, g_wNAF, g_scalar, bits, EC_WNAF_WINDOW_BITS);
+ compute_precomp(group, g_precomp, g, EC_WNAF_TABLE_SIZE);
+ }
- int8_t p_wNAF[EC_MAX_BYTES * 8 + 1];
- EC_RAW_POINT p_precomp[EC_WNAF_TABLE_SIZE];
- assert(wNAF_len <= OPENSSL_ARRAY_SIZE(p_wNAF));
- ec_compute_wNAF(group, p_wNAF, p_scalar, bits, EC_WNAF_WINDOW_BITS);
- compute_precomp(group, p_precomp, p, EC_WNAF_TABLE_SIZE);
+ for (size_t i = 0; i < num; i++) {
+ assert(wNAF_len <= OPENSSL_ARRAY_SIZE(wNAF[i]));
+ ec_compute_wNAF(group, wNAF[i], &scalars[i], bits, EC_WNAF_WINDOW_BITS);
+ compute_precomp(group, precomp[i], &points[i], EC_WNAF_TABLE_SIZE);
+ }
EC_RAW_POINT tmp;
int r_is_at_infinity = 1;
@@ -200,7 +233,7 @@
ec_GFp_mont_dbl(group, r, r);
}
- if (g_wNAF[k] != 0) {
+ if (g_scalar != NULL && g_wNAF[k] != 0) {
lookup_precomp(group, &tmp, g_precomp, g_wNAF[k]);
if (r_is_at_infinity) {
ec_GFp_simple_point_copy(r, &tmp);
@@ -210,13 +243,15 @@
}
}
- if (p_wNAF[k] != 0) {
- lookup_precomp(group, &tmp, p_precomp, p_wNAF[k]);
- if (r_is_at_infinity) {
- ec_GFp_simple_point_copy(r, &tmp);
- r_is_at_infinity = 0;
- } else {
- ec_GFp_mont_add(group, r, r, &tmp);
+ for (size_t i = 0; i < num; i++) {
+ if (wNAF[i][k] != 0) {
+ lookup_precomp(group, &tmp, precomp[i], wNAF[i][k]);
+ if (r_is_at_infinity) {
+ ec_GFp_simple_point_copy(r, &tmp);
+ r_is_at_infinity = 0;
+ } else {
+ ec_GFp_mont_add(group, r, r, &tmp);
+ }
}
}
}
@@ -224,4 +259,11 @@
if (r_is_at_infinity) {
ec_GFp_simple_point_set_to_infinity(group, r);
}
+
+ ret = 1;
+
+err:
+ OPENSSL_free(wNAF_alloc);
+ OPENSSL_free(precomp_alloc);
+ return ret;
}
diff --git a/crypto/trust_token/pmbtoken.c b/crypto/trust_token/pmbtoken.c
index 258cd40..291cb86 100644
--- a/crypto/trust_token/pmbtoken.c
+++ b/crypto/trust_token/pmbtoken.c
@@ -128,6 +128,16 @@
return 1;
}
+static int mul_public_3(const EC_GROUP *group, EC_RAW_POINT *out,
+ const EC_RAW_POINT *p0, const EC_SCALAR *scalar0,
+ const EC_RAW_POINT *p1, const EC_SCALAR *scalar1,
+ const EC_RAW_POINT *p2, const EC_SCALAR *scalar2) {
+ EC_RAW_POINT points[3] = {*p0, *p1, *p2};
+ EC_SCALAR scalars[3] = {*scalar0, *scalar1, *scalar2};
+ return ec_point_mul_scalar_public_batch(group, out, /*g_scalar=*/NULL, points,
+ scalars, 3);
+}
+
void PMBTOKEN_PRETOKEN_free(PMBTOKEN_PRETOKEN *pretoken) {
OPENSSL_free(pretoken);
}
@@ -594,7 +604,9 @@
// We verify a DLEQ proof for the validity token and a DLEQOR2 proof for the
// private metadata token. To allow amortizing Jacobian-to-affine conversions,
- // we compute Ki for both proofs first.
+ // we compute Ki for both proofs first. Additionally, all inputs to this
+ // function are public, so we can use the faster variable-time
+ // multiplications.
enum {
idx_T,
idx_S,
@@ -620,17 +632,14 @@
}
// Ks = us*(G;T) + vs*(H;S) - cs*(pubs;Ws)
- //
- // TODO(davidben): The multiplications in this function are public and can be
- // switched to a public batch multiplication function if we add one.
EC_RAW_POINT pubs;
ec_affine_to_jacobian(group, &pubs, &pub->pubs);
EC_SCALAR minus_cs;
ec_scalar_neg(group, &minus_cs, &cs);
- if (!ec_point_mul_scalar_batch(group, &jacobians[idx_Ks0], g, &us, &method->h,
- &vs, &pubs, &minus_cs) ||
- !ec_point_mul_scalar_batch(group, &jacobians[idx_Ks1], T, &us, S, &vs, Ws,
- &minus_cs)) {
+ if (!mul_public_3(group, &jacobians[idx_Ks0], g, &us, &method->h, &vs, &pubs,
+ &minus_cs) ||
+ !mul_public_3(group, &jacobians[idx_Ks1], T, &us, S, &vs, Ws,
+ &minus_cs)) {
return 0;
}
@@ -653,15 +662,13 @@
ec_scalar_neg(group, &minus_c0, &c0);
ec_scalar_neg(group, &minus_c1, &c1);
if (// K0 = u0*(G;T) + v0*(H;S) - c0*(pub0;W)
- !ec_point_mul_scalar_batch(group, &jacobians[idx_K00], g, &u0, &method->h,
- &v0, &pub0, &minus_c0) ||
- !ec_point_mul_scalar_batch(group, &jacobians[idx_K01], T, &u0, S, &v0, W,
- &minus_c0) ||
+ !mul_public_3(group, &jacobians[idx_K00], g, &u0, &method->h, &v0, &pub0,
+ &minus_c0) ||
+ !mul_public_3(group, &jacobians[idx_K01], T, &u0, S, &v0, W, &minus_c0) ||
// K1 = u1*(G;T) + v1*(H;S) - c1*(pub1;W)
- !ec_point_mul_scalar_batch(group, &jacobians[idx_K10], g, &u1, &method->h,
- &v1, &pub1, &minus_c1) ||
- !ec_point_mul_scalar_batch(group, &jacobians[idx_K11], T, &u1, S, &v1, W,
- &minus_c1)) {
+ !mul_public_3(group, &jacobians[idx_K10], g, &u1, &method->h, &v1, &pub1,
+ &minus_c1) ||
+ !mul_public_3(group, &jacobians[idx_K11], T, &u1, S, &v1, W, &minus_c1)) {
return 0;
}
@@ -722,10 +729,12 @@
EC_RAW_POINT *Sps = NULL;
EC_RAW_POINT *Wps = NULL;
EC_RAW_POINT *Wsps = NULL;
+ EC_SCALAR *es = NULL;
CBB batch_cbb;
CBB_zero(&batch_cbb);
if (method->batched_proof) {
- if (num_to_issue > ((size_t)-1) / sizeof(EC_RAW_POINT)) {
+ if (num_to_issue > ((size_t)-1) / sizeof(EC_RAW_POINT) ||
+ num_to_issue > ((size_t)-1) / sizeof(EC_SCALAR)) {
OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_OVERFLOW);
goto err;
}
@@ -733,10 +742,12 @@
Sps = OPENSSL_malloc(num_to_issue * sizeof(EC_RAW_POINT));
Wps = OPENSSL_malloc(num_to_issue * sizeof(EC_RAW_POINT));
Wsps = OPENSSL_malloc(num_to_issue * sizeof(EC_RAW_POINT));
+ es = OPENSSL_malloc(num_to_issue * sizeof(EC_SCALAR));
if (!Tps ||
!Sps ||
!Wps ||
!Wsps ||
+ !es ||
!CBB_init(&batch_cbb, 0) ||
!point_to_cbb(&batch_cbb, method->group, &key->pubs) ||
!point_to_cbb(&batch_cbb, method->group, &key->pub0) ||
@@ -808,35 +819,29 @@
}
// The DLEQ batching construction is described in appendix B of
- // https://eprint.iacr.org/2020/072/20200324:214215.
+ // https://eprint.iacr.org/2020/072/20200324:214215. Note the additional
+ // computations all act on public inputs.
if (method->batched_proof) {
- EC_RAW_POINT Tp_batch, Sp_batch, Wp_batch, Wsp_batch;
for (size_t i = 0; i < num_to_issue; i++) {
- EC_SCALAR e;
- if (!hash_c_batch(method, &e, &batch_cbb, i)) {
+ if (!hash_c_batch(method, &es[i], &batch_cbb, i)) {
goto err;
}
+ }
- EC_RAW_POINT Tp_e, Sp_e, Wp_e, Wsp_e;
- if (!ec_point_mul_scalar(group, &Tp_e, &Tps[i], &e) ||
- !ec_point_mul_scalar(group, &Sp_e, &Sps[i], &e) ||
- !ec_point_mul_scalar(group, &Wp_e, &Wps[i], &e) ||
- !ec_point_mul_scalar(group, &Wsp_e, &Wsps[i], &e)) {
- goto err;
- }
-
- // TODO: Switch this to a multi-scalar multiplication.
- if (i == 0) {
- Tp_batch = Tp_e;
- Sp_batch = Sp_e;
- Wp_batch = Wp_e;
- Wsp_batch = Wsp_e;
- } else {
- group->meth->add(group, &Tp_batch, &Tp_batch, &Tp_e);
- group->meth->add(group, &Sp_batch, &Sp_batch, &Sp_e);
- group->meth->add(group, &Wp_batch, &Wp_batch, &Wp_e);
- group->meth->add(group, &Wsp_batch, &Wsp_batch, &Wsp_e);
- }
+ EC_RAW_POINT Tp_batch, Sp_batch, Wp_batch, Wsp_batch;
+ if (!ec_point_mul_scalar_public_batch(group, &Tp_batch,
+ /*g_scalar=*/NULL, Tps, es,
+ num_to_issue) ||
+ !ec_point_mul_scalar_public_batch(group, &Sp_batch,
+ /*g_scalar=*/NULL, Sps, es,
+ num_to_issue) ||
+ !ec_point_mul_scalar_public_batch(group, &Wp_batch,
+ /*g_scalar=*/NULL, Wps, es,
+ num_to_issue) ||
+ !ec_point_mul_scalar_public_batch(group, &Wsp_batch,
+ /*g_scalar=*/NULL, Wsps, es,
+ num_to_issue)) {
+ goto err;
}
CBB proof;
@@ -862,6 +867,7 @@
OPENSSL_free(Sps);
OPENSSL_free(Wps);
OPENSSL_free(Wsps);
+ OPENSSL_free(es);
CBB_cleanup(&batch_cbb);
return ret;
}
@@ -888,10 +894,12 @@
EC_RAW_POINT *Sps = NULL;
EC_RAW_POINT *Wps = NULL;
EC_RAW_POINT *Wsps = NULL;
+ EC_SCALAR *es = NULL;
CBB batch_cbb;
CBB_zero(&batch_cbb);
if (method->batched_proof) {
- if (count > ((size_t)-1) / sizeof(EC_RAW_POINT)) {
+ if (count > ((size_t)-1) / sizeof(EC_RAW_POINT) ||
+ count > ((size_t)-1) / sizeof(EC_SCALAR)) {
OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_OVERFLOW);
goto err;
}
@@ -899,10 +907,12 @@
Sps = OPENSSL_malloc(count * sizeof(EC_RAW_POINT));
Wps = OPENSSL_malloc(count * sizeof(EC_RAW_POINT));
Wsps = OPENSSL_malloc(count * sizeof(EC_RAW_POINT));
+ es = OPENSSL_malloc(count * sizeof(EC_SCALAR));
if (!Tps ||
!Sps ||
!Wps ||
!Wsps ||
+ !es ||
!CBB_init(&batch_cbb, 0) ||
!point_to_cbb(&batch_cbb, method->group, &key->pubs) ||
!point_to_cbb(&batch_cbb, method->group, &key->pub0) ||
@@ -1006,35 +1016,25 @@
}
// The DLEQ batching construction is described in appendix B of
- // https://eprint.iacr.org/2020/072/20200324:214215.
+ // https://eprint.iacr.org/2020/072/20200324:214215. Note the additional
+ // computations all act on public inputs.
if (method->batched_proof) {
- EC_RAW_POINT Tp_batch, Sp_batch, Wp_batch, Wsp_batch;
for (size_t i = 0; i < count; i++) {
- EC_SCALAR e;
- if (!hash_c_batch(method, &e, &batch_cbb, i)) {
+ if (!hash_c_batch(method, &es[i], &batch_cbb, i)) {
goto err;
}
+ }
- EC_RAW_POINT Tp_e, Sp_e, Wp_e, Wsp_e;
- if (!ec_point_mul_scalar(group, &Tp_e, &Tps[i], &e) ||
- !ec_point_mul_scalar(group, &Sp_e, &Sps[i], &e) ||
- !ec_point_mul_scalar(group, &Wp_e, &Wps[i], &e) ||
- !ec_point_mul_scalar(group, &Wsp_e, &Wsps[i], &e)) {
- goto err;
- }
-
- // TODO: Switch this to a multi-scalar multiplication.
- if (i == 0) {
- Tp_batch = Tp_e;
- Sp_batch = Sp_e;
- Wp_batch = Wp_e;
- Wsp_batch = Wsp_e;
- } else {
- group->meth->add(group, &Tp_batch, &Tp_batch, &Tp_e);
- group->meth->add(group, &Sp_batch, &Sp_batch, &Sp_e);
- group->meth->add(group, &Wp_batch, &Wp_batch, &Wp_e);
- group->meth->add(group, &Wsp_batch, &Wsp_batch, &Wsp_e);
- }
+ EC_RAW_POINT Tp_batch, Sp_batch, Wp_batch, Wsp_batch;
+ if (!ec_point_mul_scalar_public_batch(group, &Tp_batch,
+ /*g_scalar=*/NULL, Tps, es, count) ||
+ !ec_point_mul_scalar_public_batch(group, &Sp_batch,
+ /*g_scalar=*/NULL, Sps, es, count) ||
+ !ec_point_mul_scalar_public_batch(group, &Wp_batch,
+ /*g_scalar=*/NULL, Wps, es, count) ||
+ !ec_point_mul_scalar_public_batch(group, &Wsp_batch,
+ /*g_scalar=*/NULL, Wsps, es, count)) {
+ goto err;
}
CBS proof;
@@ -1053,6 +1053,7 @@
OPENSSL_free(Sps);
OPENSSL_free(Wps);
OPENSSL_free(Wsps);
+ OPENSSL_free(es);
CBB_cleanup(&batch_cbb);
if (!ok) {
sk_TRUST_TOKEN_pop_free(ret, TRUST_TOKEN_free);