Use a 5-bit comb for some Trust Tokens multiplications.
Several of the Trust Tokens multiplications use repeated points (G, H,
and the public keys). We can precompute a 5-bit comb for those points
and perform only 1/5th as many doubles in the multiplication.
Before:
Did 483 TrustToken-Exp0-Batch1 generate_key operations in 2017082us (239.5 ops/sec)
Did 1449 TrustToken-Exp0-Batch1 begin_issuance operations in 2086097us (694.6 ops/sec)
Did 176 TrustToken-Exp0-Batch1 issue operations in 2089640us (84.2 ops/sec)
Did 147 TrustToken-Exp0-Batch1 finish_issuance operations in 2027924us (72.5 ops/sec)
Did 12284000 TrustToken-Exp0-Batch1 begin_redemption operations in 2000151us (6141536.3 ops/sec)
Did 483 TrustToken-Exp0-Batch1 redeem operations in 2063241us (234.1 ops/sec)
Did 35000 TrustToken-Exp0-Batch1 finish_redemption operations in 2050694us (17067.4 ops/sec)
Did 483 TrustToken-Exp0-Batch10 generate_key operations in 2003222us (241.1 ops/sec)
Did 138 TrustToken-Exp0-Batch10 begin_issuance operations in 2000845us (69.0 ops/sec)
Did 16 TrustToken-Exp0-Batch10 issue operations in 2010264us (8.0 ops/sec)
Did 14 TrustToken-Exp0-Batch10 finish_issuance operations in 2036137us (6.9 ops/sec)
Did 12106000 TrustToken-Exp0-Batch10 begin_redemption operations in 2000126us (6052618.7 ops/sec)
Did 483 TrustToken-Exp0-Batch10 redeem operations in 2062366us (234.2 ops/sec)
Did 35000 TrustToken-Exp0-Batch10 finish_redemption operations in 2023617us (17295.8 ops/sec)
Did 1254 TrustToken-Exp1-Batch1 generate_key operations in 2086776us (600.9 ops/sec)
Did 3612 TrustToken-Exp1-Batch1 begin_issuance operations in 2052090us (1760.2 ops/sec)
Did 420 TrustToken-Exp1-Batch1 issue operations in 2002421us (209.7 ops/sec)
Did 378 TrustToken-Exp1-Batch1 finish_issuance operations in 2078074us (181.9 ops/sec)
Did 12843000 TrustToken-Exp1-Batch1 begin_redemption operations in 2000068us (6421281.7 ops/sec)
Did 1210 TrustToken-Exp1-Batch1 redeem operations in 2083419us (580.8 ops/sec)
Did 35000 TrustToken-Exp1-Batch1 finish_redemption operations in 2023704us (17295.0 ops/sec)
Did 1239 TrustToken-Exp1-Batch10 generate_key operations in 2060962us (601.2 ops/sec)
Did 357 TrustToken-Exp1-Batch10 begin_issuance operations in 2031131us (175.8 ops/sec)
Did 42 TrustToken-Exp1-Batch10 issue operations in 2045185us (20.5 ops/sec)
Did 36 TrustToken-Exp1-Batch10 finish_issuance operations in 2028604us (17.7 ops/sec)
Did 12435000 TrustToken-Exp1-Batch10 begin_redemption operations in 2000084us (6217238.9 ops/sec)
Did 1176 TrustToken-Exp1-Batch10 redeem operations in 2023934us (581.0 ops/sec)
Did 35000 TrustToken-Exp1-Batch10 finish_redemption operations in 2002899us (17474.7 ops/sec)
After:
Did 875 TrustToken-Exp0-Batch1 generate_key operations in 2028222us (431.4 ops/sec) [+80.2%]
Did 1449 TrustToken-Exp0-Batch1 begin_issuance operations in 2097298us (690.9 ops/sec) [-0.5%]
Did 207 TrustToken-Exp0-Batch1 issue operations in 2083578us (99.3 ops/sec) [+18.0%]
Did 147 TrustToken-Exp0-Batch1 finish_issuance operations in 2018783us (72.8 ops/sec) [+0.5%]
Did 12020250 TrustToken-Exp0-Batch1 begin_redemption operations in 2000036us (6010016.8 ops/sec) [-2.1%]
Did 525 TrustToken-Exp0-Batch1 redeem operations in 2077137us (252.8 ops/sec) [+8.0%]
Did 35000 TrustToken-Exp0-Batch1 finish_redemption operations in 2006257us (17445.4 ops/sec) [+2.2%]
Did 903 TrustToken-Exp0-Batch10 generate_key operations in 2091846us (431.7 ops/sec) [+79.0%]
Did 138 TrustToken-Exp0-Batch10 begin_issuance operations in 2006432us (68.8 ops/sec) [-0.3%]
Did 19 TrustToken-Exp0-Batch10 issue operations in 2000665us (9.5 ops/sec) [+19.3%]
Did 14 TrustToken-Exp0-Batch10 finish_issuance operations in 2045846us (6.8 ops/sec) [-0.5%]
Did 12124000 TrustToken-Exp0-Batch10 begin_redemption operations in 2000055us (6061833.3 ops/sec) [+0.2%]
Did 525 TrustToken-Exp0-Batch10 redeem operations in 2076637us (252.8 ops/sec) [+7.9%]
Did 35000 TrustToken-Exp0-Batch10 finish_redemption operations in 2000072us (17499.4 ops/sec) [+1.2%]
Did 2142 TrustToken-Exp1-Batch1 generate_key operations in 2031447us (1054.4 ops/sec) [+75.5%]
Did 3633 TrustToken-Exp1-Batch1 begin_issuance operations in 2073265us (1752.3 ops/sec) [-0.4%]
Did 504 TrustToken-Exp1-Batch1 issue operations in 2043677us (246.6 ops/sec) [+17.6%]
Did 378 TrustToken-Exp1-Batch1 finish_issuance operations in 2086624us (181.2 ops/sec) [-0.4%]
Did 12548250 TrustToken-Exp1-Batch1 begin_redemption operations in 2000020us (6274062.3 ops/sec) [-2.3%]
Did 1281 TrustToken-Exp1-Batch1 redeem operations in 2067790us (619.5 ops/sec) [+6.7%]
Did 35000 TrustToken-Exp1-Batch1 finish_redemption operations in 2012117us (17394.6 ops/sec) [+0.6%]
Did 2184 TrustToken-Exp1-Batch10 generate_key operations in 2069977us (1055.1 ops/sec) [+75.5%]
Did 357 TrustToken-Exp1-Batch10 begin_issuance operations in 2041930us (174.8 ops/sec) [-0.5%]
Did 50 TrustToken-Exp1-Batch10 issue operations in 2063927us (24.2 ops/sec) [+18.0%]
Did 36 TrustToken-Exp1-Batch10 finish_issuance operations in 2038115us (17.7 ops/sec) [-0.5%]
Did 12693000 TrustToken-Exp1-Batch10 begin_redemption operations in 2000070us (6346277.9 ops/sec) [+2.1%]
Did 1281 TrustToken-Exp1-Batch10 redeem operations in 2066940us (619.8 ops/sec) [+6.7%]
Did 35000 TrustToken-Exp1-Batch10 finish_redemption operations in 2020506us (17322.4 ops/sec) [-0.9%]
Change-Id: Id26600c07401d6567275155aa389839ac0e87013
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/41124
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Steven Valdez <svaldez@google.com>
diff --git a/crypto/fipsmodule/ec/ec.c b/crypto/fipsmodule/ec/ec.c
index 5497aac..89ce64e 100644
--- a/crypto/fipsmodule/ec/ec.c
+++ b/crypto/fipsmodule/ec/ec.c
@@ -1070,6 +1070,38 @@
return 1;
}
+int ec_init_precomp(const EC_GROUP *group, EC_PRECOMP *out,
+ const EC_RAW_POINT *p) {
+ if (group->meth->init_precomp == NULL) {
+ OPENSSL_PUT_ERROR(EC, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+ return 0;
+ }
+
+ return group->meth->init_precomp(group, out, p);
+}
+
+int ec_point_mul_scalar_precomp(const EC_GROUP *group, EC_RAW_POINT *r,
+ const EC_PRECOMP *p0, const EC_SCALAR *scalar0,
+ const EC_PRECOMP *p1, const EC_SCALAR *scalar1,
+ const EC_PRECOMP *p2,
+ const EC_SCALAR *scalar2) {
+ if (group->meth->mul_precomp == NULL) {
+ OPENSSL_PUT_ERROR(EC, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+ return 0;
+ }
+
+ group->meth->mul_precomp(group, r, p0, scalar0, p1, scalar1, p2, scalar2);
+
+ // Check the result is on the curve to defend against fault attacks or bugs.
+ // This has negligible cost compared to the multiplication.
+ if (!ec_GFp_simple_is_on_curve(group, r)) {
+ OPENSSL_PUT_ERROR(EC, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ return 1;
+}
+
void ec_point_select(const EC_GROUP *group, EC_RAW_POINT *out, BN_ULONG mask,
const EC_RAW_POINT *a, const EC_RAW_POINT *b) {
ec_felem_select(group, &out->X, mask, &a->X, &b->X);
@@ -1083,6 +1115,15 @@
ec_felem_select(group, &out->Y, mask, &a->Y, &b->Y);
}
+void ec_precomp_select(const EC_GROUP *group, EC_PRECOMP *out, BN_ULONG mask,
+ const EC_PRECOMP *a, const EC_PRECOMP *b) {
+ OPENSSL_STATIC_ASSERT(sizeof(out->comb) == sizeof(*out),
+ "out->comb does not span the entire structure");
+ for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(out->comb); i++) {
+ ec_affine_select(group, &out->comb[i], mask, &a->comb[i], &b->comb[i]);
+ }
+}
+
int ec_cmp_x_coordinate(const EC_GROUP *group, const EC_RAW_POINT *p,
const EC_SCALAR *r) {
return group->meth->cmp_x_coordinate(group, p, r);
diff --git a/crypto/fipsmodule/ec/ec_montgomery.c b/crypto/fipsmodule/ec/ec_montgomery.c
index 8e94f30..e9a0958 100644
--- a/crypto/fipsmodule/ec/ec_montgomery.c
+++ b/crypto/fipsmodule/ec/ec_montgomery.c
@@ -509,6 +509,8 @@
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->init_precomp = ec_GFp_mont_init_precomp;
+ out->mul_precomp = ec_GFp_mont_mul_precomp;
out->felem_mul = ec_GFp_mont_felem_mul;
out->felem_sqr = ec_GFp_mont_felem_sqr;
out->felem_to_bytes = ec_GFp_mont_felem_to_bytes;
diff --git a/crypto/fipsmodule/ec/internal.h b/crypto/fipsmodule/ec/internal.h
index a30af1c..278c071 100644
--- a/crypto/fipsmodule/ec/internal.h
+++ b/crypto/fipsmodule/ec/internal.h
@@ -334,6 +334,52 @@
const EC_RAW_POINT *p1, const EC_SCALAR *scalar1,
const EC_RAW_POINT *p2, const EC_SCALAR *scalar2);
+#define EC_MONT_PRECOMP_COMB_SIZE 5
+
+// An |EC_PRECOMP| stores precomputed information about a point, to optimize
+// repeated multiplications involving it. It is a union so different
+// |EC_METHOD|s can store different information in it.
+typedef union {
+ EC_AFFINE comb[(1 << EC_MONT_PRECOMP_COMB_SIZE) - 1];
+} EC_PRECOMP;
+
+// ec_init_precomp precomputes multiples of |p| and writes the result to |out|.
+// It returns one on success and zero on error. The resulting table may be used
+// with |ec_point_mul_scalar_precomp|. This function will fail if |p| is the
+// point at infinity.
+//
+// This function is not implemented for all curves. Add implementations as
+// needed.
+int ec_init_precomp(const EC_GROUP *group, EC_PRECOMP *out,
+ const EC_RAW_POINT *p);
+
+// ec_point_mul_scalar_precomp sets |r| to |p0| * |scalar0| + |p1| * |scalar1| +
+// |p2| * |scalar2|. |p1| or |p2| may be NULL to skip the corresponding term.
+// The points are represented as |EC_PRECOMP| and must be initialized with
+// |ec_init_precomp|. This function runs faster than |ec_point_mul_scalar_batch|
+// but requires setup work per input point, so it is only appropriate for points
+// which are used frequently.
+//
+// The inputs are treated as secret, however, this function leaks information
+// about whether intermediate computations add a point to itself. Callers must
+// ensure that discrete logs between |p0|, |p1|, and |p2| are uniformly
+// distributed and independent of the scalars, which should be uniformly
+// selected and not under the attackers control. This ensures the doubling case
+// will occur with negligible probability.
+//
+// This function is not implemented for all curves. Add implementations as
+// needed.
+//
+// TODO(davidben): This function does not use base point tables. For now, it is
+// only used with the generic |EC_GFp_mont_method| implementation which has
+// none. If generalizing to tuned curves, we should add a parameter for the base
+// point and arrange for the generic implementation to have base point tables
+// available.
+int ec_point_mul_scalar_precomp(const EC_GROUP *group, EC_RAW_POINT *r,
+ const EC_PRECOMP *p0, const EC_SCALAR *scalar0,
+ const EC_PRECOMP *p1, const EC_SCALAR *scalar1,
+ const EC_PRECOMP *p2, const EC_SCALAR *scalar2);
+
// ec_point_mul_scalar_public sets |r| to
// generator * |g_scalar| + |p| * |p_scalar|. It assumes that the inputs are
// public so there is no concern about leaking their values through timing.
@@ -352,6 +398,10 @@
void ec_affine_select(const EC_GROUP *group, EC_AFFINE *out, BN_ULONG mask,
const EC_AFFINE *a, const EC_AFFINE *b);
+// ec_precomp_select behaves like |ec_point_select| but acts on |EC_PRECOMP|.
+void ec_precomp_select(const EC_GROUP *group, EC_PRECOMP *out, BN_ULONG mask,
+ const EC_PRECOMP *a, const EC_PRECOMP *b);
+
// ec_cmp_x_coordinate compares the x (affine) coordinate of |p|, mod the group
// order, with |r|. It returns one if the values match and zero if |p| is the
// point at infinity of the values do not match.
@@ -437,6 +487,15 @@
const EC_SCALAR *g_scalar, const EC_RAW_POINT *p,
const EC_SCALAR *p_scalar);
+ // init_precomp implements |ec_init_precomp|.
+ int (*init_precomp)(const EC_GROUP *group, EC_PRECOMP *out,
+ const EC_RAW_POINT *p);
+ // mul_precomp implements |ec_point_mul_scalar_precomp|.
+ void (*mul_precomp)(const EC_GROUP *group, EC_RAW_POINT *r,
+ const EC_PRECOMP *p0, const EC_SCALAR *scalar0,
+ const EC_PRECOMP *p1, const EC_SCALAR *scalar1,
+ const EC_PRECOMP *p2, const EC_SCALAR *scalar2);
+
// felem_mul and felem_sqr implement multiplication and squaring,
// respectively, so that the generic |EC_POINT_add| and |EC_POINT_dbl|
// implementations can work both with |EC_GFp_mont_method| and the tuned
@@ -555,6 +614,12 @@
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);
+int ec_GFp_mont_init_precomp(const EC_GROUP *group, EC_PRECOMP *out,
+ const EC_RAW_POINT *p);
+void ec_GFp_mont_mul_precomp(const EC_GROUP *group, EC_RAW_POINT *r,
+ const EC_PRECOMP *p0, const EC_SCALAR *scalar0,
+ const EC_PRECOMP *p1, const EC_SCALAR *scalar1,
+ const EC_PRECOMP *p2, const EC_SCALAR *scalar2);
// ec_compute_wNAF writes the modified width-(w+1) Non-Adjacent Form (wNAF) of
// |scalar| to |out|. |out| must have room for |bits| + 1 elements, each of
diff --git a/crypto/fipsmodule/ec/simple_mul.c b/crypto/fipsmodule/ec/simple_mul.c
index 02063df..127e2b3 100644
--- a/crypto/fipsmodule/ec/simple_mul.c
+++ b/crypto/fipsmodule/ec/simple_mul.c
@@ -167,3 +167,104 @@
ec_GFp_simple_point_set_to_infinity(group, r);
}
}
+
+static unsigned ec_GFp_mont_comb_stride(const EC_GROUP *group) {
+ return (BN_num_bits(&group->field) + EC_MONT_PRECOMP_COMB_SIZE - 1) /
+ EC_MONT_PRECOMP_COMB_SIZE;
+}
+
+int ec_GFp_mont_init_precomp(const EC_GROUP *group, EC_PRECOMP *out,
+ const EC_RAW_POINT *p) {
+ // comb[i - 1] stores the ith element of the comb. That is, if i is
+ // b4 * 2^4 + b3 * 2^3 + ... + b0 * 2^0, it stores k * |p|, where k is
+ // b4 * 2^(4*stride) + b3 * 2^(3*stride) + ... + b0 * 2^(0*stride). stride
+ // here is |ec_GFp_mont_comb_stride|. We store at index i - 1 because the 0th
+ // comb entry is always infinity.
+ EC_RAW_POINT comb[(1 << EC_MONT_PRECOMP_COMB_SIZE) - 1];
+ unsigned stride = ec_GFp_mont_comb_stride(group);
+
+ // We compute the comb sequentially by the highest set bit. Initially, all
+ // entries up to 2^0 are filled.
+ comb[(1 << 0) - 1] = *p;
+ for (unsigned i = 1; i < EC_MONT_PRECOMP_COMB_SIZE; i++) {
+ // Compute entry 2^i by doubling the entry for 2^(i-1) |stride| times.
+ unsigned bit = 1 << i;
+ ec_GFp_mont_dbl(group, &comb[bit - 1], &comb[bit / 2 - 1]);
+ for (unsigned j = 1; j < stride; j++) {
+ ec_GFp_mont_dbl(group, &comb[bit - 1], &comb[bit - 1]);
+ }
+ // Compute entries from 2^i + 1 to 2^i + (2^i - 1) by adding entry 2^i to
+ // a previous entry.
+ for (unsigned j = 1; j < bit; j++) {
+ ec_GFp_mont_add(group, &comb[bit + j - 1], &comb[bit - 1], &comb[j - 1]);
+ }
+ }
+
+ // Store the comb in affine coordinates to shrink the table. (This reduces
+ // cache pressure and makes the constant-time selects faster.)
+ OPENSSL_STATIC_ASSERT(
+ OPENSSL_ARRAY_SIZE(comb) == OPENSSL_ARRAY_SIZE(out->comb),
+ "comb sizes did not match");
+ return ec_jacobian_to_affine_batch(group, out->comb, comb,
+ OPENSSL_ARRAY_SIZE(comb));
+}
+
+static void ec_GFp_mont_get_comb_window(const EC_GROUP *group,
+ EC_RAW_POINT *out,
+ const EC_PRECOMP *precomp,
+ const EC_SCALAR *scalar, unsigned i) {
+ const size_t width = group->order.width;
+ unsigned stride = ec_GFp_mont_comb_stride(group);
+ // Select the bits corresponding to the comb shifted up by |i|.
+ unsigned window = 0;
+ for (unsigned j = 0; j < EC_MONT_PRECOMP_COMB_SIZE; j++) {
+ window |= bn_is_bit_set_words(scalar->words, width, j * stride + i)
+ << j;
+ }
+
+ // Select precomp->comb[window - 1]. If |window| is zero, |match| will always
+ // be zero, which will leave |out| at infinity.
+ OPENSSL_memset(out, 0, sizeof(EC_RAW_POINT));
+ for (unsigned j = 0; j < OPENSSL_ARRAY_SIZE(precomp->comb); j++) {
+ BN_ULONG match = constant_time_eq_w(window, j + 1);
+ ec_felem_select(group, &out->X, match, &precomp->comb[j].X, &out->X);
+ ec_felem_select(group, &out->Y, match, &precomp->comb[j].Y, &out->Y);
+ }
+ BN_ULONG is_infinity = constant_time_is_zero_w(window);
+ ec_felem_select(group, &out->Z, is_infinity, &out->Z, &group->one);
+}
+
+void ec_GFp_mont_mul_precomp(const EC_GROUP *group, EC_RAW_POINT *r,
+ const EC_PRECOMP *p0, const EC_SCALAR *scalar0,
+ const EC_PRECOMP *p1, const EC_SCALAR *scalar1,
+ const EC_PRECOMP *p2, const EC_SCALAR *scalar2) {
+ unsigned stride = ec_GFp_mont_comb_stride(group);
+ int r_is_at_infinity = 1;
+ for (unsigned i = stride - 1; i < stride; i--) {
+ if (!r_is_at_infinity) {
+ ec_GFp_mont_dbl(group, r, r);
+ }
+
+ EC_RAW_POINT tmp;
+ ec_GFp_mont_get_comb_window(group, &tmp, p0, scalar0, i);
+ 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);
+ }
+
+ if (p1 != NULL) {
+ ec_GFp_mont_get_comb_window(group, &tmp, p1, scalar1, i);
+ ec_GFp_mont_add(group, r, r, &tmp);
+ }
+
+ if (p2 != NULL) {
+ ec_GFp_mont_get_comb_window(group, &tmp, p2, scalar2, i);
+ ec_GFp_mont_add(group, r, r, &tmp);
+ }
+ }
+ if (r_is_at_infinity) {
+ ec_GFp_simple_point_set_to_infinity(group, r);
+ }
+}
diff --git a/crypto/trust_token/internal.h b/crypto/trust_token/internal.h
index 6c9904d..db9d0cf 100644
--- a/crypto/trust_token/internal.h
+++ b/crypto/trust_token/internal.h
@@ -42,6 +42,8 @@
#define PMBTOKEN_NONCE_SIZE 64
typedef struct {
+ // TODO(https://crbug.com/boringssl/334): These should store |EC_PRECOMP| so
+ // that |TRUST_TOKEN_finish_issuance| can use |ec_point_mul_scalar_precomp|.
EC_AFFINE pub0;
EC_AFFINE pub1;
EC_AFFINE pubs;
@@ -55,8 +57,11 @@
EC_SCALAR xs;
EC_SCALAR ys;
EC_AFFINE pub0;
+ EC_PRECOMP pub0_precomp;
EC_AFFINE pub1;
+ EC_PRECOMP pub1_precomp;
EC_AFFINE pubs;
+ EC_PRECOMP pubs_precomp;
} PMBTOKEN_ISSUER_KEY;
// PMBTOKEN_PRETOKEN represents the intermediate state a client keeps during a
diff --git a/crypto/trust_token/pmbtoken.c b/crypto/trust_token/pmbtoken.c
index 0407537..7eca5bb 100644
--- a/crypto/trust_token/pmbtoken.c
+++ b/crypto/trust_token/pmbtoken.c
@@ -30,36 +30,70 @@
#include "internal.h"
+typedef int (*hash_t_func_t)(const EC_GROUP *group, EC_RAW_POINT *out,
+ const uint8_t t[PMBTOKEN_NONCE_SIZE]);
+typedef int (*hash_s_func_t)(const EC_GROUP *group, EC_RAW_POINT *out,
+ const EC_AFFINE *t,
+ const uint8_t s[PMBTOKEN_NONCE_SIZE]);
+typedef int (*hash_c_func_t)(const EC_GROUP *group, EC_SCALAR *out,
+ uint8_t *buf, size_t len);
+
typedef struct {
const EC_GROUP *group;
+ EC_PRECOMP g_precomp;
+ EC_PRECOMP h_precomp;
EC_RAW_POINT h;
- // hash_t implements the H_t operation in PMBTokens. It returns on on success
+ // hash_t implements the H_t operation in PMBTokens. It returns one on success
// and zero on error.
- int (*hash_t)(const EC_GROUP *group, EC_RAW_POINT *out,
- const uint8_t t[PMBTOKEN_NONCE_SIZE]);
- // hash_s implements the H_s operation in PMBTokens. It returns on on success
+ hash_t_func_t hash_t;
+ // hash_s implements the H_s operation in PMBTokens. It returns one on success
// and zero on error.
- int (*hash_s)(const EC_GROUP *group, EC_RAW_POINT *out, const EC_AFFINE *t,
- const uint8_t s[PMBTOKEN_NONCE_SIZE]);
- // hash_c implements the H_c operation in PMBTokens. It returns on on success
+ hash_s_func_t hash_s;
+ // hash_c implements the H_c operation in PMBTokens. It returns one on success
// and zero on error.
- int (*hash_c)(const EC_GROUP *group, EC_SCALAR *out, uint8_t *buf,
- size_t len);
+ hash_c_func_t hash_c;
} PMBTOKEN_METHOD;
static const uint8_t kDefaultAdditionalData[32] = {0};
+static int pmbtoken_init_method(PMBTOKEN_METHOD *method, int curve_nid,
+ const uint8_t *h_bytes, size_t h_len,
+ hash_t_func_t hash_t, hash_s_func_t hash_s,
+ hash_c_func_t hash_c) {
+ method->group = EC_GROUP_new_by_curve_name(curve_nid);
+ if (method->group == NULL) {
+ return 0;
+ }
+
+ method->hash_t = hash_t;
+ method->hash_s = hash_s;
+ method->hash_c = hash_c;
+
+ EC_AFFINE h;
+ if (!ec_point_from_uncompressed(method->group, &h, h_bytes, h_len)) {
+ return 0;
+ }
+ ec_affine_to_jacobian(method->group, &method->h, &h);
+
+ if (!ec_init_precomp(method->group, &method->g_precomp,
+ &method->group->generator->raw) ||
+ !ec_init_precomp(method->group, &method->h_precomp, &method->h)) {
+ return 0;
+ }
+ return 1;
+}
+
// generate_keypair generates a keypair for the PMBTokens construction.
// |out_x| and |out_y| are set to the secret half of the keypair, while
// |*out_pub| is set to the public half of the keypair. It returns one on
// success and zero on failure.
static int generate_keypair(const PMBTOKEN_METHOD *method, EC_SCALAR *out_x,
EC_SCALAR *out_y, EC_RAW_POINT *out_pub) {
- const EC_RAW_POINT *g = &method->group->generator->raw;
if (!ec_random_nonzero_scalar(method->group, out_x, kDefaultAdditionalData) ||
!ec_random_nonzero_scalar(method->group, out_y, kDefaultAdditionalData) ||
- !ec_point_mul_scalar_batch(method->group, out_pub, g, out_x, &method->h,
- out_y, NULL, NULL)) {
+ !ec_point_mul_scalar_precomp(method->group, out_pub, &method->g_precomp,
+ out_x, &method->h_precomp, out_y, NULL,
+ NULL)) {
OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_MALLOC_FAILURE);
return 0;
}
@@ -178,13 +212,15 @@
// Recompute the public key.
EC_RAW_POINT pub[3];
EC_AFFINE pub_affine[3];
- const EC_RAW_POINT *g = &group->generator->raw;
- if (!ec_point_mul_scalar_batch(group, &pub[0], g, &key->x0, &method->h,
- &key->y0, NULL, NULL) ||
- !ec_point_mul_scalar_batch(group, &pub[1], g, &key->x1, &method->h,
- &key->y1, NULL, NULL) ||
- !ec_point_mul_scalar_batch(group, &pub[2], g, &key->xs, &method->h,
- &key->ys, NULL, NULL) ||
+ if (!ec_point_mul_scalar_precomp(group, &pub[0], &method->g_precomp, &key->x0,
+ &method->h_precomp, &key->y0, NULL, NULL) ||
+ !ec_init_precomp(group, &key->pub0_precomp, &pub[0]) ||
+ !ec_point_mul_scalar_precomp(group, &pub[1], &method->g_precomp, &key->x1,
+ &method->h_precomp, &key->y1, NULL, NULL) ||
+ !ec_init_precomp(group, &key->pub1_precomp, &pub[1]) ||
+ !ec_point_mul_scalar_precomp(group, &pub[2], &method->g_precomp, &key->xs,
+ &method->h_precomp, &key->ys, NULL, NULL) ||
+ !ec_init_precomp(group, &key->pubs_precomp, &pub[2]) ||
!ec_jacobian_to_affine_batch(group, pub_affine, pub, 3)) {
return 0;
}
@@ -357,7 +393,6 @@
const EC_RAW_POINT *S, const EC_RAW_POINT *W,
const EC_RAW_POINT *Ws, uint8_t private_metadata) {
const EC_GROUP *group = method->group;
- const EC_RAW_POINT *g = &group->generator->raw;
// We generate a DLEQ proof for the validity token and a DLEQOR2 proof for the
// private metadata token. To allow amortizing Jacobian-to-affine conversions,
@@ -383,8 +418,9 @@
!ec_random_nonzero_scalar(group, &ks0, kDefaultAdditionalData) ||
!ec_random_nonzero_scalar(group, &ks1, kDefaultAdditionalData) ||
// Ks = ks0*(G;T) + ks1*(H;S)
- !ec_point_mul_scalar_batch(group, &jacobians[idx_Ks0], g, &ks0,
- &method->h, &ks1, NULL, NULL) ||
+ !ec_point_mul_scalar_precomp(group, &jacobians[idx_Ks0],
+ &method->g_precomp, &ks0, &method->h_precomp,
+ &ks1, NULL, NULL) ||
!ec_point_mul_scalar_batch(group, &jacobians[idx_Ks1], T, &ks0, S, &ks1,
NULL, NULL)) {
return 0;
@@ -394,21 +430,21 @@
// to the private metadata value) and pubo (public key corresponding to the
// other value) in constant time.
BN_ULONG mask = ((BN_ULONG)0) - (private_metadata & 1);
- EC_AFFINE pubo_affine;
- EC_RAW_POINT pubo;
+ EC_PRECOMP pubo_precomp;
EC_SCALAR xb, yb;
ec_scalar_select(group, &xb, mask, &priv->x1, &priv->x0);
ec_scalar_select(group, &yb, mask, &priv->y1, &priv->y0);
- ec_affine_select(group, &pubo_affine, mask, &priv->pub0, &priv->pub1);
- ec_affine_to_jacobian(group, &pubo, &pubo_affine);
+ ec_precomp_select(group, &pubo_precomp, mask, &priv->pub0_precomp,
+ &priv->pub1_precomp);
EC_SCALAR k0, k1, minus_co, uo, vo;
if (// k0, k1 <- Zp
!ec_random_nonzero_scalar(group, &k0, kDefaultAdditionalData) ||
!ec_random_nonzero_scalar(group, &k1, kDefaultAdditionalData) ||
// Kb = k0*(G;T) + k1*(H;S)
- !ec_point_mul_scalar_batch(group, &jacobians[idx_Kb0], g, &k0, &method->h,
- &k1, NULL, NULL) ||
+ !ec_point_mul_scalar_precomp(group, &jacobians[idx_Kb0],
+ &method->g_precomp, &k0, &method->h_precomp,
+ &k1, NULL, NULL) ||
!ec_point_mul_scalar_batch(group, &jacobians[idx_Kb1], T, &k0, S, &k1,
NULL, NULL) ||
// co, uo, vo <- Zp
@@ -416,8 +452,9 @@
!ec_random_nonzero_scalar(group, &uo, kDefaultAdditionalData) ||
!ec_random_nonzero_scalar(group, &vo, kDefaultAdditionalData) ||
// Ko = uo*(G;T) + vo*(H;S) - co*(pubo;W)
- !ec_point_mul_scalar_batch(group, &jacobians[idx_Ko0], g, &uo, &method->h,
- &vo, &pubo, &minus_co) ||
+ !ec_point_mul_scalar_precomp(group, &jacobians[idx_Ko0],
+ &method->g_precomp, &uo, &method->h_precomp,
+ &vo, &pubo_precomp, &minus_co) ||
!ec_point_mul_scalar_batch(group, &jacobians[idx_Ko1], T, &uo, S, &vo, W,
&minus_co)) {
return 0;
@@ -823,21 +860,30 @@
return 0;
}
- EC_RAW_POINT S_jacobian, calculated;
- // Check the validity of the token.
+ // We perform three multiplications with S and T. This is enough that it is
+ // worth using |ec_point_mul_scalar_precomp|.
+ EC_RAW_POINT S_jacobian;
+ EC_PRECOMP S_precomp, T_precomp;
ec_affine_to_jacobian(group, &S_jacobian, &S);
- if (!ec_point_mul_scalar_batch(group, &calculated, &T, &key->xs, &S_jacobian,
- &key->ys, NULL, NULL) ||
- !ec_affine_jacobian_equal(group, &Ws, &calculated)) {
+ if (!ec_init_precomp(group, &S_precomp, &S_jacobian) ||
+ !ec_init_precomp(group, &T_precomp, &T)) {
+ return 0;
+ }
+
+ EC_RAW_POINT Ws_calculated;
+ // Check the validity of the token.
+ if (!ec_point_mul_scalar_precomp(group, &Ws_calculated, &T_precomp, &key->xs,
+ &S_precomp, &key->ys, NULL, NULL) ||
+ !ec_affine_jacobian_equal(group, &Ws, &Ws_calculated)) {
OPENSSL_PUT_ERROR(TRUST_TOKEN, TRUST_TOKEN_R_BAD_VALIDITY_CHECK);
return 0;
}
EC_RAW_POINT W0, W1;
- if (!ec_point_mul_scalar_batch(group, &W0, &T, &key->x0, &S_jacobian,
- &key->y0, NULL, NULL) ||
- !ec_point_mul_scalar_batch(group, &W1, &T, &key->x1, &S_jacobian,
- &key->y1, NULL, NULL)) {
+ if (!ec_point_mul_scalar_precomp(group, &W0, &T_precomp, &key->x0, &S_precomp,
+ &key->y0, NULL, NULL) ||
+ !ec_point_mul_scalar_precomp(group, &W1, &T_precomp, &key->x1, &S_precomp,
+ &key->y1, NULL, NULL)) {
return 0;
}
@@ -952,16 +998,11 @@
if __name__ == "__main__":
gen_point(SEED_H)
*/
-static int pmbtoken_exp0_init_method(PMBTOKEN_METHOD *method) {
- method->group = EC_GROUP_new_by_curve_name(NID_secp521r1);
- if (method->group == NULL) {
- return 0;
- }
+static int pmbtoken_exp0_ok = 0;
+static PMBTOKEN_METHOD pmbtoken_exp0_method;
+static CRYPTO_once_t pmbtoken_exp0_method_once = CRYPTO_ONCE_INIT;
- method->hash_t = pmbtoken_exp0_hash_t;
- method->hash_c = pmbtoken_exp0_hash_c;
- method->hash_s = pmbtoken_exp0_hash_s;
-
+static void pmbtoken_exp0_init_method_impl(void) {
static const uint8_t kH[] = {
0x04, 0x01, 0xf0, 0xa9, 0xf7, 0x9e, 0xbc, 0x12, 0x6c, 0xef, 0xd1, 0xab,
0x29, 0x10, 0x03, 0x6f, 0x4e, 0xf5, 0xbd, 0xeb, 0x0f, 0x6b, 0xc0, 0x5c,
@@ -977,81 +1018,81 @@
0xcd,
};
- EC_AFFINE h;
- if (!ec_point_from_uncompressed(method->group, &h, kH, sizeof(kH))) {
+ pmbtoken_exp0_ok = pmbtoken_init_method(
+ &pmbtoken_exp0_method, NID_secp521r1, kH, sizeof(kH),
+ pmbtoken_exp0_hash_t, pmbtoken_exp0_hash_s, pmbtoken_exp0_hash_c);
+}
+
+static int pmbtoken_exp0_init_method(void) {
+ CRYPTO_once(&pmbtoken_exp0_method_once, pmbtoken_exp0_init_method_impl);
+ if (!pmbtoken_exp0_ok) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_INTERNAL_ERROR);
return 0;
}
- ec_affine_to_jacobian(method->group, &method->h, &h);
return 1;
}
int pmbtoken_exp0_generate_key(CBB *out_private, CBB *out_public) {
- PMBTOKEN_METHOD method;
- if (!pmbtoken_exp0_init_method(&method)) {
+ if (!pmbtoken_exp0_init_method()) {
return 0;
}
- return pmbtoken_generate_key(&method, out_private, out_public);
+ return pmbtoken_generate_key(&pmbtoken_exp0_method, out_private, out_public);
}
int pmbtoken_exp0_client_key_from_bytes(PMBTOKEN_CLIENT_KEY *key,
const uint8_t *in, size_t len) {
- PMBTOKEN_METHOD method;
- if (!pmbtoken_exp0_init_method(&method)) {
+ if (!pmbtoken_exp0_init_method()) {
return 0;
}
- return pmbtoken_client_key_from_bytes(&method, key, in, len);
+ return pmbtoken_client_key_from_bytes(&pmbtoken_exp0_method, key, in, len);
}
int pmbtoken_exp0_issuer_key_from_bytes(PMBTOKEN_ISSUER_KEY *key,
const uint8_t *in, size_t len) {
- PMBTOKEN_METHOD method;
- if (!pmbtoken_exp0_init_method(&method)) {
+ if (!pmbtoken_exp0_init_method()) {
return 0;
}
- return pmbtoken_issuer_key_from_bytes(&method, key, in, len);
+ return pmbtoken_issuer_key_from_bytes(&pmbtoken_exp0_method, key, in, len);
}
STACK_OF(PMBTOKEN_PRETOKEN) * pmbtoken_exp0_blind(CBB *cbb, size_t count) {
- PMBTOKEN_METHOD method;
- if (!pmbtoken_exp0_init_method(&method)) {
+ if (!pmbtoken_exp0_init_method()) {
return NULL;
}
- return pmbtoken_blind(&method, cbb, count);
+ return pmbtoken_blind(&pmbtoken_exp0_method, cbb, count);
}
int pmbtoken_exp0_sign(const PMBTOKEN_ISSUER_KEY *key, CBB *cbb, CBS *cbs,
size_t num_requested, size_t num_to_issue,
uint8_t private_metadata) {
- PMBTOKEN_METHOD method;
- if (!pmbtoken_exp0_init_method(&method)) {
+ if (!pmbtoken_exp0_init_method()) {
return 0;
}
- return pmbtoken_sign(&method, key, cbb, cbs, num_requested, num_to_issue,
- private_metadata);
+ return pmbtoken_sign(&pmbtoken_exp0_method, key, cbb, cbs, num_requested,
+ num_to_issue, private_metadata);
}
STACK_OF(TRUST_TOKEN) *
pmbtoken_exp0_unblind(const PMBTOKEN_CLIENT_KEY *key,
- const STACK_OF(PMBTOKEN_PRETOKEN) *pretokens,
+ const STACK_OF(PMBTOKEN_PRETOKEN) * pretokens,
CBS *cbs, size_t count, uint32_t key_id) {
- PMBTOKEN_METHOD method;
- if (!pmbtoken_exp0_init_method(&method)) {
+ if (!pmbtoken_exp0_init_method()) {
return NULL;
}
- return pmbtoken_unblind(&method, key, pretokens, cbs, count, key_id);
+ return pmbtoken_unblind(&pmbtoken_exp0_method, key, pretokens, cbs, count,
+ key_id);
}
int pmbtoken_exp0_read(const PMBTOKEN_ISSUER_KEY *key,
uint8_t out_nonce[PMBTOKEN_NONCE_SIZE],
uint8_t *out_private_metadata, const uint8_t *token,
size_t token_len) {
- PMBTOKEN_METHOD method;
- if (!pmbtoken_exp0_init_method(&method)) {
+ if (!pmbtoken_exp0_init_method()) {
return 0;
}
- return pmbtoken_read(&method, key, out_nonce, out_private_metadata, token,
- token_len);
+ return pmbtoken_read(&pmbtoken_exp0_method, key, out_nonce,
+ out_private_metadata, token, token_len);
}
@@ -1097,16 +1138,11 @@
group, out, kHashCLabel, sizeof(kHashCLabel), buf, len);
}
-static int pmbtoken_exp1_init_method(PMBTOKEN_METHOD *method) {
- method->group = EC_GROUP_new_by_curve_name(NID_secp384r1);
- if (method->group == NULL) {
- return 0;
- }
+static int pmbtoken_exp1_ok = 0;
+static PMBTOKEN_METHOD pmbtoken_exp1_method;
+static CRYPTO_once_t pmbtoken_exp1_method_once = CRYPTO_ONCE_INIT;
- method->hash_t = pmbtoken_exp1_hash_t;
- method->hash_c = pmbtoken_exp1_hash_c;
- method->hash_s = pmbtoken_exp1_hash_s;
-
+static void pmbtoken_exp1_init_method_impl(void) {
// This is the output of |ec_hash_to_scalar_p384_xmd_sha512_draft07| with DST
// "PMBTokens Experiment V1 HashH" and message "generator".
static const uint8_t kH[] = {
@@ -1121,90 +1157,90 @@
0x87, 0xc3, 0x95, 0xd0, 0x13, 0xb7, 0x0b, 0x5c, 0xc7,
};
- EC_AFFINE h;
- if (!ec_point_from_uncompressed(method->group, &h, kH, sizeof(kH))) {
+ pmbtoken_exp1_ok = pmbtoken_init_method(
+ &pmbtoken_exp1_method, NID_secp384r1, kH, sizeof(kH),
+ pmbtoken_exp1_hash_t, pmbtoken_exp1_hash_s, pmbtoken_exp1_hash_c);
+}
+
+static int pmbtoken_exp1_init_method(void) {
+ CRYPTO_once(&pmbtoken_exp1_method_once, pmbtoken_exp1_init_method_impl);
+ if (!pmbtoken_exp1_ok) {
+ OPENSSL_PUT_ERROR(TRUST_TOKEN, ERR_R_INTERNAL_ERROR);
return 0;
}
- ec_affine_to_jacobian(method->group, &method->h, &h);
return 1;
}
int pmbtoken_exp1_generate_key(CBB *out_private, CBB *out_public) {
- PMBTOKEN_METHOD method;
- if (!pmbtoken_exp1_init_method(&method)) {
+ if (!pmbtoken_exp1_init_method()) {
return 0;
}
- return pmbtoken_generate_key(&method, out_private, out_public);
+ return pmbtoken_generate_key(&pmbtoken_exp1_method, out_private, out_public);
}
int pmbtoken_exp1_client_key_from_bytes(PMBTOKEN_CLIENT_KEY *key,
const uint8_t *in, size_t len) {
- PMBTOKEN_METHOD method;
- if (!pmbtoken_exp1_init_method(&method)) {
+ if (!pmbtoken_exp1_init_method()) {
return 0;
}
- return pmbtoken_client_key_from_bytes(&method, key, in, len);
+ return pmbtoken_client_key_from_bytes(&pmbtoken_exp1_method, key, in, len);
}
int pmbtoken_exp1_issuer_key_from_bytes(PMBTOKEN_ISSUER_KEY *key,
const uint8_t *in, size_t len) {
- PMBTOKEN_METHOD method;
- if (!pmbtoken_exp1_init_method(&method)) {
+ if (!pmbtoken_exp1_init_method()) {
return 0;
}
- return pmbtoken_issuer_key_from_bytes(&method, key, in, len);
+ return pmbtoken_issuer_key_from_bytes(&pmbtoken_exp1_method, key, in, len);
}
STACK_OF(PMBTOKEN_PRETOKEN) * pmbtoken_exp1_blind(CBB *cbb, size_t count) {
- PMBTOKEN_METHOD method;
- if (!pmbtoken_exp1_init_method(&method)) {
+ if (!pmbtoken_exp1_init_method()) {
return NULL;
}
- return pmbtoken_blind(&method, cbb, count);
+ return pmbtoken_blind(&pmbtoken_exp1_method, cbb, count);
}
int pmbtoken_exp1_sign(const PMBTOKEN_ISSUER_KEY *key, CBB *cbb, CBS *cbs,
size_t num_requested, size_t num_to_issue,
uint8_t private_metadata) {
- PMBTOKEN_METHOD method;
- if (!pmbtoken_exp1_init_method(&method)) {
+ if (!pmbtoken_exp1_init_method()) {
return 0;
}
- return pmbtoken_sign(&method, key, cbb, cbs, num_requested, num_to_issue,
- private_metadata);
+ return pmbtoken_sign(&pmbtoken_exp1_method, key, cbb, cbs, num_requested,
+ num_to_issue, private_metadata);
}
STACK_OF(TRUST_TOKEN) *
pmbtoken_exp1_unblind(const PMBTOKEN_CLIENT_KEY *key,
- const STACK_OF(PMBTOKEN_PRETOKEN) *pretokens,
+ const STACK_OF(PMBTOKEN_PRETOKEN) * pretokens,
CBS *cbs, size_t count, uint32_t key_id) {
- PMBTOKEN_METHOD method;
- if (!pmbtoken_exp1_init_method(&method)) {
+ if (!pmbtoken_exp1_init_method()) {
return NULL;
}
- return pmbtoken_unblind(&method, key, pretokens, cbs, count, key_id);
+ return pmbtoken_unblind(&pmbtoken_exp1_method, key, pretokens, cbs, count,
+ key_id);
}
int pmbtoken_exp1_read(const PMBTOKEN_ISSUER_KEY *key,
uint8_t out_nonce[PMBTOKEN_NONCE_SIZE],
uint8_t *out_private_metadata, const uint8_t *token,
size_t token_len) {
- PMBTOKEN_METHOD method;
- if (!pmbtoken_exp1_init_method(&method)) {
+ if (!pmbtoken_exp1_init_method()) {
return 0;
}
- return pmbtoken_read(&method, key, out_nonce, out_private_metadata, token,
- token_len);
+ return pmbtoken_read(&pmbtoken_exp1_method, key, out_nonce,
+ out_private_metadata, token, token_len);
}
int pmbtoken_exp1_get_h_for_testing(uint8_t out[97]) {
- PMBTOKEN_METHOD method;
- if (!pmbtoken_exp1_init_method(&method)) {
+ if (!pmbtoken_exp1_init_method()) {
return 0;
}
EC_AFFINE h;
- return ec_jacobian_to_affine(method.group, &h, &method.h) &&
- ec_point_to_bytes(method.group, &h, POINT_CONVERSION_UNCOMPRESSED, out,
- 97) == 97;
+ return ec_jacobian_to_affine(pmbtoken_exp1_method.group, &h,
+ &pmbtoken_exp1_method.h) &&
+ ec_point_to_bytes(pmbtoken_exp1_method.group, &h,
+ POINT_CONVERSION_UNCOMPRESSED, out, 97) == 97;
}