summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorjsing <>2025-05-25 05:12:05 +0000
committerjsing <>2025-05-25 05:12:05 +0000
commit06551b0f228f49af60110396bb1a7f0fe0202321 (patch)
treed2a89424948ea33ced8fe1bb82297f1988893a57 /src
parentd1e92fb0ec242ccd5715534e3cfab9187b27c62b (diff)
downloadopenbsd-06551b0f228f49af60110396bb1a7f0fe0202321.tar.gz
openbsd-06551b0f228f49af60110396bb1a7f0fe0202321.tar.bz2
openbsd-06551b0f228f49af60110396bb1a7f0fe0202321.zip
Implement EC field element operations.
Provide EC_FIELD_ELEMENT and EC_FIELD_MODULUS, which allow for operations on fixed width fields in constant time. These can in turn be used to implement Elliptic Curve cryptography for prime fields, without needing to use BN. This will improve the code, reduces timing leaks and enable further optimisation. ok beck@ tb@
Diffstat (limited to 'src')
-rw-r--r--src/lib/libcrypto/Makefile3
-rw-r--r--src/lib/libcrypto/bn/bn_internal.h4
-rw-r--r--src/lib/libcrypto/bn/bn_mont.c71
-rw-r--r--src/lib/libcrypto/ec/ec_field.c189
-rw-r--r--src/lib/libcrypto/ec/ec_internal.h63
5 files changed, 299 insertions, 31 deletions
diff --git a/src/lib/libcrypto/Makefile b/src/lib/libcrypto/Makefile
index dd64f07f48..7564001961 100644
--- a/src/lib/libcrypto/Makefile
+++ b/src/lib/libcrypto/Makefile
@@ -1,4 +1,4 @@
1# $OpenBSD: Makefile,v 1.233 2025/05/25 04:58:32 jsing Exp $ 1# $OpenBSD: Makefile,v 1.234 2025/05/25 05:12:05 jsing Exp $
2 2
3LIB= crypto 3LIB= crypto
4LIBREBUILD=y 4LIBREBUILD=y
@@ -283,6 +283,7 @@ SRCS+= ec_asn1.c
283SRCS+= ec_convert.c 283SRCS+= ec_convert.c
284SRCS+= ec_curve.c 284SRCS+= ec_curve.c
285SRCS+= ec_err.c 285SRCS+= ec_err.c
286SRCS+= ec_field.c
286SRCS+= ec_key.c 287SRCS+= ec_key.c
287SRCS+= ec_lib.c 288SRCS+= ec_lib.c
288SRCS+= ec_mult.c 289SRCS+= ec_mult.c
diff --git a/src/lib/libcrypto/bn/bn_internal.h b/src/lib/libcrypto/bn/bn_internal.h
index b6e903553f..a1f1515b57 100644
--- a/src/lib/libcrypto/bn/bn_internal.h
+++ b/src/lib/libcrypto/bn/bn_internal.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: bn_internal.h,v 1.18 2025/05/25 04:58:32 jsing Exp $ */ 1/* $OpenBSD: bn_internal.h,v 1.19 2025/05/25 05:12:05 jsing Exp $ */
2/* 2/*
3 * Copyright (c) 2023 Joel Sing <jsing@openbsd.org> 3 * Copyright (c) 2023 Joel Sing <jsing@openbsd.org>
4 * 4 *
@@ -45,6 +45,8 @@ void bn_mod_mul_words(BN_ULONG *r, const BN_ULONG *a, const BN_ULONG *b,
45void bn_montgomery_multiply_words(BN_ULONG *rp, const BN_ULONG *ap, 45void bn_montgomery_multiply_words(BN_ULONG *rp, const BN_ULONG *ap,
46 const BN_ULONG *bp, const BN_ULONG *np, BN_ULONG *tp, BN_ULONG n0, 46 const BN_ULONG *bp, const BN_ULONG *np, BN_ULONG *tp, BN_ULONG n0,
47 int n_len); 47 int n_len);
48void bn_montgomery_reduce_words(BN_ULONG *r, BN_ULONG *a, const BN_ULONG *n,
49 BN_ULONG n0, int n_len);
48 50
49#ifndef HAVE_BN_CT_NE_ZERO 51#ifndef HAVE_BN_CT_NE_ZERO
50static inline int 52static inline int
diff --git a/src/lib/libcrypto/bn/bn_mont.c b/src/lib/libcrypto/bn/bn_mont.c
index ce88b23ca9..950846fa5b 100644
--- a/src/lib/libcrypto/bn/bn_mont.c
+++ b/src/lib/libcrypto/bn/bn_mont.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: bn_mont.c,v 1.67 2025/05/25 04:58:32 jsing Exp $ */ 1/* $OpenBSD: bn_mont.c,v 1.68 2025/05/25 05:12:05 jsing Exp $ */
2/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) 2/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
3 * All rights reserved. 3 * All rights reserved.
4 * 4 *
@@ -316,6 +316,44 @@ BN_MONT_CTX_set_locked(BN_MONT_CTX **pmctx, int lock, const BIGNUM *mod,
316LCRYPTO_ALIAS(BN_MONT_CTX_set_locked); 316LCRYPTO_ALIAS(BN_MONT_CTX_set_locked);
317 317
318/* 318/*
319 * bn_montgomery_reduce_words() performs Montgomery reduction, reducing the input
320 * from its Montgomery form aR to a, returning the result in r. a must be twice
321 * the length of the modulus. Note that the input is mutated in the process of
322 * performing the reduction.
323 */
324void
325bn_montgomery_reduce_words(BN_ULONG *r, BN_ULONG *a, const BN_ULONG *n,
326 BN_ULONG n0, int n_len)
327{
328 BN_ULONG v, mask;
329 BN_ULONG carry = 0;
330 int i;
331
332 /* Add multiples of the modulus, so that it becomes divisible by R. */
333 for (i = 0; i < n_len; i++) {
334 v = bn_mul_add_words(&a[i], n, n_len, a[i] * n0);
335 bn_addw_addw(v, a[i + n_len], carry, &carry, &a[i + n_len]);
336 }
337
338 /* Divide by R (this is the equivalent of right shifting by n_len). */
339 a = &a[n_len];
340
341 /*
342 * The output is now in the range of [0, 2N). Attempt to reduce once by
343 * subtracting the modulus. If the reduction was necessary then the
344 * result is already in r, otherwise copy the value prior to reduction
345 * from the top half of a.
346 */
347 mask = carry - bn_sub_words(r, a, n, n_len);
348
349 for (i = 0; i < n_len; i++) {
350 *r = (*r & ~mask) | (*a & mask);
351 r++;
352 a++;
353 }
354}
355
356/*
319 * bn_montgomery_reduce() performs Montgomery reduction, reducing the input 357 * bn_montgomery_reduce() performs Montgomery reduction, reducing the input
320 * from its Montgomery form aR to a, returning the result in r. Note that the 358 * from its Montgomery form aR to a, returning the result in r. Note that the
321 * input is mutated in the process of performing the reduction, destroying its 359 * input is mutated in the process of performing the reduction, destroying its
@@ -325,7 +363,6 @@ static int
325bn_montgomery_reduce(BIGNUM *r, BIGNUM *a, BN_MONT_CTX *mctx) 363bn_montgomery_reduce(BIGNUM *r, BIGNUM *a, BN_MONT_CTX *mctx)
326{ 364{
327 BIGNUM *n; 365 BIGNUM *n;
328 BN_ULONG *ap, *rp, n0, v, carry, mask;
329 int i, max, n_len; 366 int i, max, n_len;
330 367
331 n = &mctx->N; 368 n = &mctx->N;
@@ -341,7 +378,8 @@ bn_montgomery_reduce(BIGNUM *r, BIGNUM *a, BN_MONT_CTX *mctx)
341 378
342 /* 379 /*
343 * Expand a to twice the length of the modulus, zero if necessary. 380 * Expand a to twice the length of the modulus, zero if necessary.
344 * XXX - make this a requirement of the caller. 381 * XXX - make this a requirement of the caller or use a temporary
382 * allocation.
345 */ 383 */
346 if ((max = 2 * n_len) < n_len) 384 if ((max = 2 * n_len) < n_len)
347 return 0; 385 return 0;
@@ -350,33 +388,8 @@ bn_montgomery_reduce(BIGNUM *r, BIGNUM *a, BN_MONT_CTX *mctx)
350 for (i = a->top; i < max; i++) 388 for (i = a->top; i < max; i++)
351 a->d[i] = 0; 389 a->d[i] = 0;
352 390
353 carry = 0; 391 bn_montgomery_reduce_words(r->d, a->d, n->d, mctx->n0[0], n_len);
354 n0 = mctx->n0[0];
355
356 /* Add multiples of the modulus, so that it becomes divisible by R. */
357 for (i = 0; i < n_len; i++) {
358 v = bn_mul_add_words(&a->d[i], n->d, n_len, a->d[i] * n0);
359 bn_addw_addw(v, a->d[i + n_len], carry, &carry,
360 &a->d[i + n_len]);
361 }
362
363 /* Divide by R (this is the equivalent of right shifting by n_len). */
364 ap = &a->d[n_len];
365
366 /*
367 * The output is now in the range of [0, 2N). Attempt to reduce once by
368 * subtracting the modulus. If the reduction was necessary then the
369 * result is already in r, otherwise copy the value prior to reduction
370 * from the top half of a.
371 */
372 mask = carry - bn_sub_words(r->d, ap, n->d, n_len);
373 392
374 rp = r->d;
375 for (i = 0; i < n_len; i++) {
376 *rp = (*rp & ~mask) | (*ap & mask);
377 rp++;
378 ap++;
379 }
380 r->top = n_len; 393 r->top = n_len;
381 394
382 bn_correct_top(r); 395 bn_correct_top(r);
diff --git a/src/lib/libcrypto/ec/ec_field.c b/src/lib/libcrypto/ec/ec_field.c
new file mode 100644
index 0000000000..ec1c7d11e0
--- /dev/null
+++ b/src/lib/libcrypto/ec/ec_field.c
@@ -0,0 +1,189 @@
1/* $OpenBSD: ec_field.c,v 1.1 2025/05/25 05:12:05 jsing Exp $ */
2/*
3 * Copyright (c) 2024 Joel Sing <jsing@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <string.h>
19
20#include <openssl/ec.h>
21
22#include "bn_local.h"
23#include "bn_internal.h"
24#include "ec_local.h"
25#include "ec_internal.h"
26
27int
28ec_field_modulus_from_bn(EC_FIELD_MODULUS *fm, const BIGNUM *bn, BN_CTX *ctx)
29{
30 BN_MONT_CTX *mctx = NULL;
31 size_t i;
32 int ret = 0;
33
34 if (BN_is_negative(bn))
35 goto err;
36 if (BN_num_bits(bn) > EC_FIELD_ELEMENT_MAX_BITS)
37 goto err;
38
39 memset(fm, 0, sizeof(*fm));
40
41 fm->n = (BN_num_bits(bn) + BN_BITS2 - 1) / BN_BITS2;
42
43 for (i = 0; i < bn->top; i++)
44 fm->m.w[i] = bn->d[i];
45
46 /* XXX - implement this without BN_MONT_CTX. */
47 if ((mctx = BN_MONT_CTX_new()) == NULL)
48 goto err;
49 if (!BN_MONT_CTX_set(mctx, bn, ctx))
50 goto err;
51
52 for (i = 0; i < mctx->RR.top; i++)
53 fm->rr.w[i] = mctx->RR.d[i];
54
55 fm->minv0 = mctx->n0[0];
56
57 ret = 1;
58
59 err:
60 BN_MONT_CTX_free(mctx);
61
62 return ret;
63}
64
65int
66ec_field_element_from_bn(const EC_FIELD_MODULUS *fm, const EC_GROUP *group,
67 EC_FIELD_ELEMENT *fe, const BIGNUM *bn, BN_CTX *ctx)
68{
69 BN_ULONG t[EC_FIELD_ELEMENT_MAX_WORDS * 2 + 2];
70 BIGNUM *tmp;
71 size_t i;
72 int ret = 0;
73
74 BN_CTX_start(ctx);
75
76 if ((tmp = BN_CTX_get(ctx)) == NULL)
77 goto err;
78
79 /* XXX - enforce 0 <= n < p. */
80
81 if (BN_num_bits(bn) > EC_FIELD_ELEMENT_MAX_BITS)
82 goto err;
83
84 /* XXX - do this without BN. */
85 if (!BN_nnmod(tmp, bn, group->p, ctx))
86 goto err;
87
88 if (BN_num_bits(tmp) > EC_FIELD_ELEMENT_MAX_BITS)
89 abort();
90
91 memset(fe->w, 0, sizeof(fe->w));
92
93 for (i = 0; i < tmp->top; i++)
94 fe->w[i] = tmp->d[i];
95
96 bn_mod_mul_words(fe->w, fe->w, fm->rr.w, fm->m.w, t, fm->minv0, fm->n);
97
98 ret = 1;
99
100 err:
101 BN_CTX_end(ctx);
102
103 return ret;
104}
105
106int
107ec_field_element_to_bn(const EC_FIELD_MODULUS *fm, const EC_FIELD_ELEMENT *fe,
108 BIGNUM *bn, BN_CTX *ctx)
109{
110 BN_ULONG t[EC_FIELD_ELEMENT_MAX_WORDS * 2 + 2];
111 size_t i;
112
113 if (!bn_wexpand(bn, fm->n))
114 return 0;
115
116 memset(t, 0, sizeof(t));
117 for (i = 0; i < fm->n; i++)
118 t[i] = fe->w[i];
119
120 bn_montgomery_reduce_words(bn->d, t, fm->m.w, fm->minv0, fm->n);
121
122 bn->top = fm->n;
123 bn_correct_top(bn);
124
125 return 1;
126}
127
128void
129ec_field_element_copy(EC_FIELD_ELEMENT *dst, const EC_FIELD_ELEMENT *src)
130{
131 memcpy(dst, src, sizeof(EC_FIELD_ELEMENT));
132}
133
134int
135ec_field_element_equal(const EC_FIELD_MODULUS *fm, const EC_FIELD_ELEMENT *a,
136 const EC_FIELD_ELEMENT *b)
137{
138 BN_ULONG v = 0;
139 int i;
140
141 for (i = 0; i < fm->n; i++)
142 v |= a->w[i] ^ b->w[i];
143
144 return bn_ct_eq_zero(v);
145}
146
147int
148ec_field_element_is_zero(const EC_FIELD_MODULUS *fm, const EC_FIELD_ELEMENT *fe)
149{
150 BN_ULONG v = 0;
151 int i;
152
153 for (i = 0; i < fm->n; i++)
154 v |= fe->w[i];
155
156 return bn_ct_eq_zero(v);
157}
158
159void
160ec_field_element_add(const EC_FIELD_MODULUS *m, EC_FIELD_ELEMENT *r,
161 const EC_FIELD_ELEMENT *a, const EC_FIELD_ELEMENT *b)
162{
163 bn_mod_add_words(r->w, a->w, b->w, m->m.w, m->n);
164}
165
166void
167ec_field_element_sub(const EC_FIELD_MODULUS *m, EC_FIELD_ELEMENT *r,
168 const EC_FIELD_ELEMENT *a, const EC_FIELD_ELEMENT *b)
169{
170 bn_mod_sub_words(r->w, a->w, b->w, m->m.w, m->n);
171}
172
173void
174ec_field_element_mul(const EC_FIELD_MODULUS *m, EC_FIELD_ELEMENT *r,
175 const EC_FIELD_ELEMENT *a, const EC_FIELD_ELEMENT *b)
176{
177 BN_ULONG t[EC_FIELD_ELEMENT_MAX_WORDS * 2 + 2];
178
179 bn_mod_mul_words(r->w, a->w, b->w, m->m.w, t, m->minv0, m->n);
180}
181
182void
183ec_field_element_sqr(const EC_FIELD_MODULUS *m, EC_FIELD_ELEMENT *r,
184 const EC_FIELD_ELEMENT *a)
185{
186 BN_ULONG t[EC_FIELD_ELEMENT_MAX_WORDS * 2 + 2];
187
188 bn_mod_mul_words(r->w, a->w, a->w, m->m.w, t, m->minv0, m->n);
189}
diff --git a/src/lib/libcrypto/ec/ec_internal.h b/src/lib/libcrypto/ec/ec_internal.h
new file mode 100644
index 0000000000..29b447e8c9
--- /dev/null
+++ b/src/lib/libcrypto/ec/ec_internal.h
@@ -0,0 +1,63 @@
1/* $OpenBSD: ec_internal.h,v 1.1 2025/05/25 05:12:05 jsing Exp $ */
2/*
3 * Copyright (c) 2024 Joel Sing <jsing@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <openssl/bn.h>
19
20#ifndef HEADER_EC_INTERNAL_H
21#define HEADER_EC_INTERNAL_H
22
23#define EC_FIELD_ELEMENT_MAX_BITS 521
24#define EC_FIELD_ELEMENT_MAX_BYTES \
25 (EC_FIELD_ELEMENT_MAX_BITS + 7) / 8
26#define EC_FIELD_ELEMENT_MAX_WORDS \
27 ((EC_FIELD_ELEMENT_MAX_BYTES + BN_BYTES - 1) / BN_BYTES)
28
29typedef struct {
30 BN_ULONG w[EC_FIELD_ELEMENT_MAX_WORDS];
31} EC_FIELD_ELEMENT;
32
33typedef struct {
34 size_t n;
35 EC_FIELD_ELEMENT m;
36 EC_FIELD_ELEMENT rr;
37 BN_ULONG minv0;
38} EC_FIELD_MODULUS;
39
40int ec_field_modulus_from_bn(EC_FIELD_MODULUS *fm, const BIGNUM *bn,
41 BN_CTX *ctx);
42
43int ec_field_element_from_bn(const EC_FIELD_MODULUS *fm, const EC_GROUP *group,
44 EC_FIELD_ELEMENT *fe, const BIGNUM *bn, BN_CTX *ctx);
45int ec_field_element_to_bn(const EC_FIELD_MODULUS *fm, const EC_FIELD_ELEMENT *fe,
46 BIGNUM *bn, BN_CTX *ctx);
47
48void ec_field_element_copy(EC_FIELD_ELEMENT *dst, const EC_FIELD_ELEMENT *src);
49
50int ec_field_element_equal(const EC_FIELD_MODULUS *fm, const EC_FIELD_ELEMENT *a,
51 const EC_FIELD_ELEMENT *b);
52int ec_field_element_is_zero(const EC_FIELD_MODULUS *fm, const EC_FIELD_ELEMENT *fe);
53
54void ec_field_element_add(const EC_FIELD_MODULUS *m, EC_FIELD_ELEMENT *r,
55 const EC_FIELD_ELEMENT *a, const EC_FIELD_ELEMENT *b);
56void ec_field_element_sub(const EC_FIELD_MODULUS *m, EC_FIELD_ELEMENT *r,
57 const EC_FIELD_ELEMENT *a, const EC_FIELD_ELEMENT *b);
58void ec_field_element_mul(const EC_FIELD_MODULUS *m, EC_FIELD_ELEMENT *r,
59 const EC_FIELD_ELEMENT *a, const EC_FIELD_ELEMENT *b);
60void ec_field_element_sqr(const EC_FIELD_MODULUS *m, EC_FIELD_ELEMENT *r,
61 const EC_FIELD_ELEMENT *a);
62
63#endif