/src/nss/lib/freebl/ecl/ecp_jm.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
2 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
3 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
4 | | |
5 | | #include "ecp.h" |
6 | | #include "ecl-priv.h" |
7 | | #include "mplogic.h" |
8 | | #include <stdlib.h> |
9 | | |
10 | 0 | #define MAX_SCRATCH 6 |
11 | | |
12 | | /* Computes R = 2P. Elliptic curve points P and R can be identical. Uses |
13 | | * Modified Jacobian coordinates. |
14 | | * |
15 | | * Assumes input is already field-encoded using field_enc, and returns |
16 | | * output that is still field-encoded. |
17 | | * |
18 | | */ |
19 | | static mp_err |
20 | | ec_GFp_pt_dbl_jm(const mp_int *px, const mp_int *py, const mp_int *pz, |
21 | | const mp_int *paz4, mp_int *rx, mp_int *ry, mp_int *rz, |
22 | | mp_int *raz4, mp_int scratch[], const ECGroup *group) |
23 | 0 | { |
24 | 0 | mp_err res = MP_OKAY; |
25 | 0 | mp_int *t0, *t1, *M, *S; |
26 | |
|
27 | 0 | t0 = &scratch[0]; |
28 | 0 | t1 = &scratch[1]; |
29 | 0 | M = &scratch[2]; |
30 | 0 | S = &scratch[3]; |
31 | |
|
32 | | #if MAX_SCRATCH < 4 |
33 | | #error "Scratch array defined too small " |
34 | | #endif |
35 | | |
36 | | /* Check for point at infinity */ |
37 | 0 | if (ec_GFp_pt_is_inf_jac(px, py, pz) == MP_YES) { |
38 | | /* Set r = pt at infinity by setting rz = 0 */ |
39 | |
|
40 | 0 | MP_CHECKOK(ec_GFp_pt_set_inf_jac(rx, ry, rz)); |
41 | 0 | goto CLEANUP; |
42 | 0 | } |
43 | | |
44 | | /* M = 3 (px^2) + a*(pz^4) */ |
45 | 0 | MP_CHECKOK(group->meth->field_sqr(px, t0, group->meth)); |
46 | 0 | MP_CHECKOK(group->meth->field_add(t0, t0, M, group->meth)); |
47 | 0 | MP_CHECKOK(group->meth->field_add(t0, M, t0, group->meth)); |
48 | 0 | MP_CHECKOK(group->meth->field_add(t0, paz4, M, group->meth)); |
49 | | |
50 | | /* rz = 2 * py * pz */ |
51 | 0 | MP_CHECKOK(group->meth->field_mul(py, pz, S, group->meth)); |
52 | 0 | MP_CHECKOK(group->meth->field_add(S, S, rz, group->meth)); |
53 | | |
54 | | /* t0 = 2y^2 , t1 = 8y^4 */ |
55 | 0 | MP_CHECKOK(group->meth->field_sqr(py, t0, group->meth)); |
56 | 0 | MP_CHECKOK(group->meth->field_add(t0, t0, t0, group->meth)); |
57 | 0 | MP_CHECKOK(group->meth->field_sqr(t0, t1, group->meth)); |
58 | 0 | MP_CHECKOK(group->meth->field_add(t1, t1, t1, group->meth)); |
59 | | |
60 | | /* S = 4 * px * py^2 = 2 * px * t0 */ |
61 | 0 | MP_CHECKOK(group->meth->field_mul(px, t0, S, group->meth)); |
62 | 0 | MP_CHECKOK(group->meth->field_add(S, S, S, group->meth)); |
63 | | |
64 | | /* rx = M^2 - 2S */ |
65 | 0 | MP_CHECKOK(group->meth->field_sqr(M, rx, group->meth)); |
66 | 0 | MP_CHECKOK(group->meth->field_sub(rx, S, rx, group->meth)); |
67 | 0 | MP_CHECKOK(group->meth->field_sub(rx, S, rx, group->meth)); |
68 | | |
69 | | /* ry = M * (S - rx) - t1 */ |
70 | 0 | MP_CHECKOK(group->meth->field_sub(S, rx, S, group->meth)); |
71 | 0 | MP_CHECKOK(group->meth->field_mul(S, M, ry, group->meth)); |
72 | 0 | MP_CHECKOK(group->meth->field_sub(ry, t1, ry, group->meth)); |
73 | | |
74 | | /* ra*z^4 = 2*t1*(apz4) */ |
75 | 0 | MP_CHECKOK(group->meth->field_mul(paz4, t1, raz4, group->meth)); |
76 | 0 | MP_CHECKOK(group->meth->field_add(raz4, raz4, raz4, group->meth)); |
77 | | |
78 | 0 | CLEANUP: |
79 | 0 | return res; |
80 | 0 | } |
81 | | |
82 | | /* Computes R = P + Q where R is (rx, ry, rz), P is (px, py, pz) and Q is |
83 | | * (qx, qy, 1). Elliptic curve points P, Q, and R can all be identical. |
84 | | * Uses mixed Modified_Jacobian-affine coordinates. Assumes input is |
85 | | * already field-encoded using field_enc, and returns output that is still |
86 | | * field-encoded. */ |
87 | | static mp_err |
88 | | ec_GFp_pt_add_jm_aff(const mp_int *px, const mp_int *py, const mp_int *pz, |
89 | | const mp_int *paz4, const mp_int *qx, |
90 | | const mp_int *qy, mp_int *rx, mp_int *ry, mp_int *rz, |
91 | | mp_int *raz4, mp_int scratch[], const ECGroup *group) |
92 | 0 | { |
93 | 0 | mp_err res = MP_OKAY; |
94 | 0 | mp_int *A, *B, *C, *D, *C2, *C3; |
95 | |
|
96 | 0 | A = &scratch[0]; |
97 | 0 | B = &scratch[1]; |
98 | 0 | C = &scratch[2]; |
99 | 0 | D = &scratch[3]; |
100 | 0 | C2 = &scratch[4]; |
101 | 0 | C3 = &scratch[5]; |
102 | |
|
103 | | #if MAX_SCRATCH < 6 |
104 | | #error "Scratch array defined too small " |
105 | | #endif |
106 | | |
107 | | /* If either P or Q is the point at infinity, then return the other |
108 | | * point */ |
109 | 0 | if (ec_GFp_pt_is_inf_jac(px, py, pz) == MP_YES) { |
110 | 0 | MP_CHECKOK(ec_GFp_pt_aff2jac(qx, qy, rx, ry, rz, group)); |
111 | 0 | MP_CHECKOK(group->meth->field_sqr(rz, raz4, group->meth)); |
112 | 0 | MP_CHECKOK(group->meth->field_sqr(raz4, raz4, group->meth)); |
113 | 0 | MP_CHECKOK(group->meth->field_mul(raz4, &group->curvea, raz4, group->meth)); |
114 | 0 | goto CLEANUP; |
115 | 0 | } |
116 | 0 | if (ec_GFp_pt_is_inf_aff(qx, qy) == MP_YES) { |
117 | 0 | MP_CHECKOK(mp_copy(px, rx)); |
118 | 0 | MP_CHECKOK(mp_copy(py, ry)); |
119 | 0 | MP_CHECKOK(mp_copy(pz, rz)); |
120 | 0 | MP_CHECKOK(mp_copy(paz4, raz4)); |
121 | 0 | goto CLEANUP; |
122 | 0 | } |
123 | | |
124 | | /* A = qx * pz^2, B = qy * pz^3 */ |
125 | 0 | MP_CHECKOK(group->meth->field_sqr(pz, A, group->meth)); |
126 | 0 | MP_CHECKOK(group->meth->field_mul(A, pz, B, group->meth)); |
127 | 0 | MP_CHECKOK(group->meth->field_mul(A, qx, A, group->meth)); |
128 | 0 | MP_CHECKOK(group->meth->field_mul(B, qy, B, group->meth)); |
129 | | |
130 | | /* Check P == Q */ |
131 | 0 | if (mp_cmp(A, px) == 0) { |
132 | 0 | if (mp_cmp(B, py) == 0) { |
133 | | /* If Px == Qx && Py == Qy, double P. */ |
134 | 0 | return ec_GFp_pt_dbl_jm(px, py, pz, paz4, rx, ry, rz, raz4, |
135 | 0 | scratch, group); |
136 | 0 | } |
137 | | /* If Px == Qx && Py != Qy, return point at infinity. */ |
138 | 0 | return ec_GFp_pt_set_inf_jac(rx, ry, rz); |
139 | 0 | } |
140 | | |
141 | | /* C = A - px, D = B - py */ |
142 | 0 | MP_CHECKOK(group->meth->field_sub(A, px, C, group->meth)); |
143 | 0 | MP_CHECKOK(group->meth->field_sub(B, py, D, group->meth)); |
144 | | |
145 | | /* C2 = C^2, C3 = C^3 */ |
146 | 0 | MP_CHECKOK(group->meth->field_sqr(C, C2, group->meth)); |
147 | 0 | MP_CHECKOK(group->meth->field_mul(C, C2, C3, group->meth)); |
148 | | |
149 | | /* rz = pz * C */ |
150 | 0 | MP_CHECKOK(group->meth->field_mul(pz, C, rz, group->meth)); |
151 | | |
152 | | /* C = px * C^2 */ |
153 | 0 | MP_CHECKOK(group->meth->field_mul(px, C2, C, group->meth)); |
154 | | /* A = D^2 */ |
155 | 0 | MP_CHECKOK(group->meth->field_sqr(D, A, group->meth)); |
156 | | |
157 | | /* rx = D^2 - (C^3 + 2 * (px * C^2)) */ |
158 | 0 | MP_CHECKOK(group->meth->field_add(C, C, rx, group->meth)); |
159 | 0 | MP_CHECKOK(group->meth->field_add(C3, rx, rx, group->meth)); |
160 | 0 | MP_CHECKOK(group->meth->field_sub(A, rx, rx, group->meth)); |
161 | | |
162 | | /* C3 = py * C^3 */ |
163 | 0 | MP_CHECKOK(group->meth->field_mul(py, C3, C3, group->meth)); |
164 | | |
165 | | /* ry = D * (px * C^2 - rx) - py * C^3 */ |
166 | 0 | MP_CHECKOK(group->meth->field_sub(C, rx, ry, group->meth)); |
167 | 0 | MP_CHECKOK(group->meth->field_mul(D, ry, ry, group->meth)); |
168 | 0 | MP_CHECKOK(group->meth->field_sub(ry, C3, ry, group->meth)); |
169 | | |
170 | | /* raz4 = a * rz^4 */ |
171 | 0 | MP_CHECKOK(group->meth->field_sqr(rz, raz4, group->meth)); |
172 | 0 | MP_CHECKOK(group->meth->field_sqr(raz4, raz4, group->meth)); |
173 | 0 | MP_CHECKOK(group->meth->field_mul(raz4, &group->curvea, raz4, group->meth)); |
174 | 0 | CLEANUP: |
175 | 0 | return res; |
176 | 0 | } |
177 | | |
178 | | /* Computes R = nP where R is (rx, ry) and P is the base point. Elliptic |
179 | | * curve points P and R can be identical. Uses mixed Modified-Jacobian |
180 | | * co-ordinates for doubling and Chudnovsky Jacobian coordinates for |
181 | | * additions. Assumes input is already field-encoded using field_enc, and |
182 | | * returns output that is still field-encoded. Uses 5-bit window NAF |
183 | | * method (algorithm 11) for scalar-point multiplication from Brown, |
184 | | * Hankerson, Lopez, Menezes. Software Implementation of the NIST Elliptic |
185 | | * Curves Over Prime Fields. */ |
186 | | mp_err |
187 | | ec_GFp_pt_mul_jm_wNAF(const mp_int *n, const mp_int *px, const mp_int *py, |
188 | | mp_int *rx, mp_int *ry, const ECGroup *group) |
189 | 0 | { |
190 | 0 | mp_err res = MP_OKAY; |
191 | 0 | mp_int precomp[16][2], rz, tpx, tpy; |
192 | 0 | mp_int raz4; |
193 | 0 | mp_int scratch[MAX_SCRATCH]; |
194 | 0 | signed char *naf = NULL; |
195 | 0 | int i, orderBitSize = 0; |
196 | |
|
197 | 0 | MP_DIGITS(&rz) = 0; |
198 | 0 | MP_DIGITS(&raz4) = 0; |
199 | 0 | MP_DIGITS(&tpx) = 0; |
200 | 0 | MP_DIGITS(&tpy) = 0; |
201 | 0 | for (i = 0; i < 16; i++) { |
202 | 0 | MP_DIGITS(&precomp[i][0]) = 0; |
203 | 0 | MP_DIGITS(&precomp[i][1]) = 0; |
204 | 0 | } |
205 | 0 | for (i = 0; i < MAX_SCRATCH; i++) { |
206 | 0 | MP_DIGITS(&scratch[i]) = 0; |
207 | 0 | } |
208 | |
|
209 | 0 | ARGCHK(group != NULL, MP_BADARG); |
210 | 0 | ARGCHK((n != NULL) && (px != NULL) && (py != NULL), MP_BADARG); |
211 | | |
212 | | /* initialize precomputation table */ |
213 | 0 | MP_CHECKOK(mp_init(&tpx)); |
214 | 0 | MP_CHECKOK(mp_init(&tpy)); |
215 | 0 | ; |
216 | 0 | MP_CHECKOK(mp_init(&rz)); |
217 | 0 | MP_CHECKOK(mp_init(&raz4)); |
218 | | |
219 | 0 | for (i = 0; i < 16; i++) { |
220 | 0 | MP_CHECKOK(mp_init(&precomp[i][0])); |
221 | 0 | MP_CHECKOK(mp_init(&precomp[i][1])); |
222 | 0 | } |
223 | 0 | for (i = 0; i < MAX_SCRATCH; i++) { |
224 | 0 | MP_CHECKOK(mp_init(&scratch[i])); |
225 | 0 | } |
226 | | |
227 | | /* Set out[8] = P */ |
228 | 0 | MP_CHECKOK(mp_copy(px, &precomp[8][0])); |
229 | 0 | MP_CHECKOK(mp_copy(py, &precomp[8][1])); |
230 | | |
231 | | /* Set (tpx, tpy) = 2P */ |
232 | 0 | MP_CHECKOK(group->point_dbl(&precomp[8][0], &precomp[8][1], &tpx, &tpy, |
233 | 0 | group)); |
234 | | |
235 | | /* Set 3P, 5P, ..., 15P */ |
236 | 0 | for (i = 8; i < 15; i++) { |
237 | 0 | MP_CHECKOK(group->point_add(&precomp[i][0], &precomp[i][1], &tpx, &tpy, |
238 | 0 | &precomp[i + 1][0], &precomp[i + 1][1], |
239 | 0 | group)); |
240 | 0 | } |
241 | | |
242 | | /* Set -15P, -13P, ..., -P */ |
243 | 0 | for (i = 0; i < 8; i++) { |
244 | 0 | MP_CHECKOK(mp_copy(&precomp[15 - i][0], &precomp[i][0])); |
245 | 0 | MP_CHECKOK(group->meth->field_neg(&precomp[15 - i][1], &precomp[i][1], |
246 | 0 | group->meth)); |
247 | 0 | } |
248 | | |
249 | | /* R = inf */ |
250 | 0 | MP_CHECKOK(ec_GFp_pt_set_inf_jac(rx, ry, &rz)); |
251 | | |
252 | 0 | orderBitSize = mpl_significant_bits(&group->order); |
253 | | |
254 | | /* Allocate memory for NAF */ |
255 | 0 | naf = (signed char *)malloc(sizeof(signed char) * (orderBitSize + 1)); |
256 | 0 | if (naf == NULL) { |
257 | 0 | res = MP_MEM; |
258 | 0 | goto CLEANUP; |
259 | 0 | } |
260 | | |
261 | | /* Compute 5NAF */ |
262 | 0 | ec_compute_wNAF(naf, orderBitSize, n, 5); |
263 | | |
264 | | /* wNAF method */ |
265 | 0 | for (i = orderBitSize; i >= 0; i--) { |
266 | | /* R = 2R */ |
267 | 0 | ec_GFp_pt_dbl_jm(rx, ry, &rz, &raz4, rx, ry, &rz, |
268 | 0 | &raz4, scratch, group); |
269 | 0 | if (naf[i] != 0) { |
270 | 0 | ec_GFp_pt_add_jm_aff(rx, ry, &rz, &raz4, |
271 | 0 | &precomp[(naf[i] + 15) / 2][0], |
272 | 0 | &precomp[(naf[i] + 15) / 2][1], rx, ry, |
273 | 0 | &rz, &raz4, scratch, group); |
274 | 0 | } |
275 | 0 | } |
276 | | |
277 | | /* convert result S to affine coordinates */ |
278 | 0 | MP_CHECKOK(ec_GFp_pt_jac2aff(rx, ry, &rz, rx, ry, group)); |
279 | | |
280 | 0 | CLEANUP: |
281 | 0 | for (i = 0; i < MAX_SCRATCH; i++) { |
282 | 0 | mp_clear(&scratch[i]); |
283 | 0 | } |
284 | 0 | for (i = 0; i < 16; i++) { |
285 | 0 | mp_clear(&precomp[i][0]); |
286 | 0 | mp_clear(&precomp[i][1]); |
287 | 0 | } |
288 | 0 | mp_clear(&tpx); |
289 | 0 | mp_clear(&tpy); |
290 | 0 | mp_clear(&rz); |
291 | 0 | mp_clear(&raz4); |
292 | 0 | if (naf) { |
293 | 0 | memset(naf, 0, orderBitSize + 1); |
294 | 0 | } |
295 | 0 | free(naf); |
296 | 0 | return res; |
297 | 0 | } |