/src/nss-nspr/nss/lib/freebl/jpake.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 | | #ifdef FREEBL_NO_DEPEND |
6 | | #include "stubs.h" |
7 | | #endif |
8 | | |
9 | | #include "blapi.h" |
10 | | #include "secerr.h" |
11 | | #include "secitem.h" |
12 | | #include "secmpi.h" |
13 | | |
14 | | /* Hash an item's length and then its value. Only items smaller than 2^16 bytes |
15 | | * are allowed. Lengths are hashed in network byte order. This is designed |
16 | | * to match the OpenSSL J-PAKE implementation. |
17 | | */ |
18 | | static mp_err |
19 | | hashSECItem(HASHContext *hash, const SECItem *it) |
20 | 0 | { |
21 | 0 | unsigned char length[2]; |
22 | |
|
23 | 0 | if (it->len > 0xffff) |
24 | 0 | return MP_BADARG; |
25 | | |
26 | 0 | length[0] = (unsigned char)(it->len >> 8); |
27 | 0 | length[1] = (unsigned char)(it->len); |
28 | 0 | hash->hashobj->update(hash->hash_context, length, 2); |
29 | 0 | hash->hashobj->update(hash->hash_context, it->data, it->len); |
30 | 0 | return MP_OKAY; |
31 | 0 | } |
32 | | |
33 | | /* Hash all public components of the signature, each prefixed with its |
34 | | length, and then convert the hash to an mp_int. */ |
35 | | static mp_err |
36 | | hashPublicParams(HASH_HashType hashType, const SECItem *g, |
37 | | const SECItem *gv, const SECItem *gx, |
38 | | const SECItem *signerID, mp_int *h) |
39 | 0 | { |
40 | 0 | mp_err err; |
41 | 0 | unsigned char hBuf[HASH_LENGTH_MAX]; |
42 | 0 | SECItem hItem; |
43 | 0 | HASHContext hash; |
44 | |
|
45 | 0 | hash.hashobj = HASH_GetRawHashObject(hashType); |
46 | 0 | if (hash.hashobj == NULL || hash.hashobj->length > sizeof hBuf) { |
47 | 0 | return MP_BADARG; |
48 | 0 | } |
49 | 0 | hash.hash_context = hash.hashobj->create(); |
50 | 0 | if (hash.hash_context == NULL) { |
51 | 0 | return MP_MEM; |
52 | 0 | } |
53 | | |
54 | 0 | hItem.data = hBuf; |
55 | 0 | hItem.len = hash.hashobj->length; |
56 | |
|
57 | 0 | hash.hashobj->begin(hash.hash_context); |
58 | 0 | CHECK_MPI_OK(hashSECItem(&hash, g)); |
59 | 0 | CHECK_MPI_OK(hashSECItem(&hash, gv)); |
60 | 0 | CHECK_MPI_OK(hashSECItem(&hash, gx)); |
61 | 0 | CHECK_MPI_OK(hashSECItem(&hash, signerID)); |
62 | 0 | hash.hashobj->end(hash.hash_context, hItem.data, &hItem.len, |
63 | 0 | sizeof hBuf); |
64 | 0 | SECITEM_TO_MPINT(hItem, h); |
65 | | |
66 | 0 | cleanup: |
67 | 0 | if (hash.hash_context != NULL) { |
68 | 0 | hash.hashobj->destroy(hash.hash_context, PR_TRUE); |
69 | 0 | } |
70 | |
|
71 | 0 | return err; |
72 | 0 | } |
73 | | |
74 | | /* Generate a Schnorr signature for round 1 or round 2 */ |
75 | | SECStatus |
76 | | JPAKE_Sign(PLArenaPool *arena, const PQGParams *pqg, HASH_HashType hashType, |
77 | | const SECItem *signerID, const SECItem *x, |
78 | | const SECItem *testRandom, const SECItem *gxIn, SECItem *gxOut, |
79 | | SECItem *gv, SECItem *r) |
80 | 0 | { |
81 | 0 | SECStatus rv = SECSuccess; |
82 | 0 | mp_err err; |
83 | 0 | mp_int p; |
84 | 0 | mp_int q; |
85 | 0 | mp_int g; |
86 | 0 | mp_int X; |
87 | 0 | mp_int GX; |
88 | 0 | mp_int V; |
89 | 0 | mp_int GV; |
90 | 0 | mp_int h; |
91 | 0 | mp_int tmp; |
92 | 0 | mp_int R; |
93 | 0 | SECItem v; |
94 | |
|
95 | 0 | if (!arena || |
96 | 0 | !pqg || !pqg->prime.data || pqg->prime.len == 0 || |
97 | 0 | !pqg->subPrime.data || pqg->subPrime.len == 0 || |
98 | 0 | !pqg->base.data || pqg->base.len == 0 || |
99 | 0 | !signerID || !signerID->data || signerID->len == 0 || |
100 | 0 | !x || !x->data || x->len == 0 || |
101 | 0 | (testRandom && (!testRandom->data || testRandom->len == 0)) || |
102 | 0 | (gxIn == NULL && (!gxOut || gxOut->data != NULL)) || |
103 | 0 | (gxIn != NULL && (!gxIn->data || gxIn->len == 0 || gxOut != NULL)) || |
104 | 0 | !gv || gv->data != NULL || |
105 | 0 | !r || r->data != NULL) { |
106 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
107 | 0 | return SECFailure; |
108 | 0 | } |
109 | | |
110 | 0 | MP_DIGITS(&p) = 0; |
111 | 0 | MP_DIGITS(&q) = 0; |
112 | 0 | MP_DIGITS(&g) = 0; |
113 | 0 | MP_DIGITS(&X) = 0; |
114 | 0 | MP_DIGITS(&GX) = 0; |
115 | 0 | MP_DIGITS(&V) = 0; |
116 | 0 | MP_DIGITS(&GV) = 0; |
117 | 0 | MP_DIGITS(&h) = 0; |
118 | 0 | MP_DIGITS(&tmp) = 0; |
119 | 0 | MP_DIGITS(&R) = 0; |
120 | |
|
121 | 0 | CHECK_MPI_OK(mp_init(&p)); |
122 | 0 | CHECK_MPI_OK(mp_init(&q)); |
123 | 0 | CHECK_MPI_OK(mp_init(&g)); |
124 | 0 | CHECK_MPI_OK(mp_init(&X)); |
125 | 0 | CHECK_MPI_OK(mp_init(&GX)); |
126 | 0 | CHECK_MPI_OK(mp_init(&V)); |
127 | 0 | CHECK_MPI_OK(mp_init(&GV)); |
128 | 0 | CHECK_MPI_OK(mp_init(&h)); |
129 | 0 | CHECK_MPI_OK(mp_init(&tmp)); |
130 | 0 | CHECK_MPI_OK(mp_init(&R)); |
131 | | |
132 | 0 | SECITEM_TO_MPINT(pqg->prime, &p); |
133 | 0 | SECITEM_TO_MPINT(pqg->subPrime, &q); |
134 | 0 | SECITEM_TO_MPINT(pqg->base, &g); |
135 | 0 | SECITEM_TO_MPINT(*x, &X); |
136 | | |
137 | | /* gx = g^x */ |
138 | 0 | if (gxIn == NULL) { |
139 | 0 | CHECK_MPI_OK(mp_exptmod(&g, &X, &p, &GX)); |
140 | 0 | MPINT_TO_SECITEM(&GX, gxOut, arena); |
141 | 0 | gxIn = gxOut; |
142 | 0 | } else { |
143 | 0 | SECITEM_TO_MPINT(*gxIn, &GX); |
144 | 0 | } |
145 | | |
146 | | /* v is a random value in the q subgroup */ |
147 | 0 | if (testRandom == NULL) { |
148 | 0 | v.data = NULL; |
149 | 0 | rv = DSA_NewRandom(arena, &pqg->subPrime, &v); |
150 | 0 | if (rv != SECSuccess) { |
151 | 0 | goto cleanup; |
152 | 0 | } |
153 | 0 | } else { |
154 | 0 | v.data = testRandom->data; |
155 | 0 | v.len = testRandom->len; |
156 | 0 | } |
157 | 0 | SECITEM_TO_MPINT(v, &V); |
158 | | |
159 | | /* gv = g^v (mod q), random v, 1 <= v < q */ |
160 | 0 | CHECK_MPI_OK(mp_exptmod(&g, &V, &p, &GV)); |
161 | 0 | MPINT_TO_SECITEM(&GV, gv, arena); |
162 | | |
163 | | /* h = H(g, gv, gx, signerID) */ |
164 | 0 | CHECK_MPI_OK(hashPublicParams(hashType, &pqg->base, gv, gxIn, signerID, |
165 | 0 | &h)); |
166 | | |
167 | | /* r = v - x*h (mod q) */ |
168 | 0 | CHECK_MPI_OK(mp_mulmod(&X, &h, &q, &tmp)); |
169 | 0 | CHECK_MPI_OK(mp_submod(&V, &tmp, &q, &R)); |
170 | 0 | MPINT_TO_SECITEM(&R, r, arena); |
171 | | |
172 | 0 | cleanup: |
173 | 0 | mp_clear(&p); |
174 | 0 | mp_clear(&q); |
175 | 0 | mp_clear(&g); |
176 | 0 | mp_clear(&X); |
177 | 0 | mp_clear(&GX); |
178 | 0 | mp_clear(&V); |
179 | 0 | mp_clear(&GV); |
180 | 0 | mp_clear(&h); |
181 | 0 | mp_clear(&tmp); |
182 | 0 | mp_clear(&R); |
183 | |
|
184 | 0 | if (rv == SECSuccess && err != MP_OKAY) { |
185 | 0 | MP_TO_SEC_ERROR(err); |
186 | 0 | rv = SECFailure; |
187 | 0 | } |
188 | 0 | return rv; |
189 | 0 | } |
190 | | |
191 | | /* Verify a Schnorr signature generated by the peer in round 1 or round 2. */ |
192 | | SECStatus |
193 | | JPAKE_Verify(PLArenaPool *arena, const PQGParams *pqg, HASH_HashType hashType, |
194 | | const SECItem *signerID, const SECItem *peerID, |
195 | | const SECItem *gx, const SECItem *gv, const SECItem *r) |
196 | 0 | { |
197 | 0 | SECStatus rv = SECSuccess; |
198 | 0 | mp_err err; |
199 | 0 | mp_int p; |
200 | 0 | mp_int q; |
201 | 0 | mp_int g; |
202 | 0 | mp_int p_minus_1; |
203 | 0 | mp_int GX; |
204 | 0 | mp_int h; |
205 | 0 | mp_int one; |
206 | 0 | mp_int R; |
207 | 0 | mp_int gr; |
208 | 0 | mp_int gxh; |
209 | 0 | mp_int gr_gxh; |
210 | 0 | SECItem calculated; |
211 | |
|
212 | 0 | if (!arena || |
213 | 0 | !pqg || !pqg->prime.data || pqg->prime.len == 0 || |
214 | 0 | !pqg->subPrime.data || pqg->subPrime.len == 0 || |
215 | 0 | !pqg->base.data || pqg->base.len == 0 || |
216 | 0 | !signerID || !signerID->data || signerID->len == 0 || |
217 | 0 | !peerID || !peerID->data || peerID->len == 0 || |
218 | 0 | !gx || !gx->data || gx->len == 0 || |
219 | 0 | !gv || !gv->data || gv->len == 0 || |
220 | 0 | !r || !r->data || r->len == 0 || |
221 | 0 | SECITEM_CompareItem(signerID, peerID) == SECEqual) { |
222 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
223 | 0 | return SECFailure; |
224 | 0 | } |
225 | | |
226 | 0 | MP_DIGITS(&p) = 0; |
227 | 0 | MP_DIGITS(&q) = 0; |
228 | 0 | MP_DIGITS(&g) = 0; |
229 | 0 | MP_DIGITS(&p_minus_1) = 0; |
230 | 0 | MP_DIGITS(&GX) = 0; |
231 | 0 | MP_DIGITS(&h) = 0; |
232 | 0 | MP_DIGITS(&one) = 0; |
233 | 0 | MP_DIGITS(&R) = 0; |
234 | 0 | MP_DIGITS(&gr) = 0; |
235 | 0 | MP_DIGITS(&gxh) = 0; |
236 | 0 | MP_DIGITS(&gr_gxh) = 0; |
237 | 0 | calculated.data = NULL; |
238 | |
|
239 | 0 | CHECK_MPI_OK(mp_init(&p)); |
240 | 0 | CHECK_MPI_OK(mp_init(&q)); |
241 | 0 | CHECK_MPI_OK(mp_init(&g)); |
242 | 0 | CHECK_MPI_OK(mp_init(&p_minus_1)); |
243 | 0 | CHECK_MPI_OK(mp_init(&GX)); |
244 | 0 | CHECK_MPI_OK(mp_init(&h)); |
245 | 0 | CHECK_MPI_OK(mp_init(&one)); |
246 | 0 | CHECK_MPI_OK(mp_init(&R)); |
247 | 0 | CHECK_MPI_OK(mp_init(&gr)); |
248 | 0 | CHECK_MPI_OK(mp_init(&gxh)); |
249 | 0 | CHECK_MPI_OK(mp_init(&gr_gxh)); |
250 | | |
251 | 0 | SECITEM_TO_MPINT(pqg->prime, &p); |
252 | 0 | SECITEM_TO_MPINT(pqg->subPrime, &q); |
253 | 0 | SECITEM_TO_MPINT(pqg->base, &g); |
254 | 0 | SECITEM_TO_MPINT(*gx, &GX); |
255 | 0 | SECITEM_TO_MPINT(*r, &R); |
256 | | |
257 | 0 | CHECK_MPI_OK(mp_sub_d(&p, 1, &p_minus_1)); |
258 | 0 | CHECK_MPI_OK(mp_exptmod(&GX, &q, &p, &one)); |
259 | | /* Check g^x is in [1, p-2], R is in [0, q-1], and (g^x)^q mod p == 1 */ |
260 | 0 | if (!(mp_cmp_z(&GX) > 0 && |
261 | 0 | mp_cmp(&GX, &p_minus_1) < 0 && |
262 | 0 | mp_cmp(&R, &q) < 0 && |
263 | 0 | mp_cmp_d(&one, 1) == 0)) { |
264 | 0 | goto badSig; |
265 | 0 | } |
266 | | |
267 | 0 | CHECK_MPI_OK(hashPublicParams(hashType, &pqg->base, gv, gx, peerID, |
268 | 0 | &h)); |
269 | | |
270 | | /* Calculate g^v = g^r * g^x^h */ |
271 | 0 | CHECK_MPI_OK(mp_exptmod(&g, &R, &p, &gr)); |
272 | 0 | CHECK_MPI_OK(mp_exptmod(&GX, &h, &p, &gxh)); |
273 | 0 | CHECK_MPI_OK(mp_mulmod(&gr, &gxh, &p, &gr_gxh)); |
274 | | |
275 | | /* Compare calculated g^v to given g^v */ |
276 | 0 | MPINT_TO_SECITEM(&gr_gxh, &calculated, arena); |
277 | 0 | if (calculated.len == gv->len && |
278 | 0 | NSS_SecureMemcmp(calculated.data, gv->data, calculated.len) == 0) { |
279 | 0 | rv = SECSuccess; |
280 | 0 | } else { |
281 | 0 | badSig: |
282 | 0 | PORT_SetError(SEC_ERROR_BAD_SIGNATURE); |
283 | 0 | rv = SECFailure; |
284 | 0 | } |
285 | |
|
286 | 0 | cleanup: |
287 | 0 | mp_clear(&p); |
288 | 0 | mp_clear(&q); |
289 | 0 | mp_clear(&g); |
290 | 0 | mp_clear(&p_minus_1); |
291 | 0 | mp_clear(&GX); |
292 | 0 | mp_clear(&h); |
293 | 0 | mp_clear(&one); |
294 | 0 | mp_clear(&R); |
295 | 0 | mp_clear(&gr); |
296 | 0 | mp_clear(&gxh); |
297 | 0 | mp_clear(&gr_gxh); |
298 | |
|
299 | 0 | if (rv == SECSuccess && err != MP_OKAY) { |
300 | 0 | MP_TO_SEC_ERROR(err); |
301 | 0 | rv = SECFailure; |
302 | 0 | } |
303 | 0 | return rv; |
304 | 0 | } |
305 | | |
306 | | /* Calculate base = gx1*gx3*gx4 (mod p), i.e. g^(x1+x3+x4) (mod p) */ |
307 | | static mp_err |
308 | | jpake_Round2Base(const SECItem *gx1, const SECItem *gx3, |
309 | | const SECItem *gx4, const mp_int *p, mp_int *base) |
310 | 0 | { |
311 | 0 | mp_err err; |
312 | 0 | mp_int GX1; |
313 | 0 | mp_int GX3; |
314 | 0 | mp_int GX4; |
315 | 0 | mp_int tmp; |
316 | |
|
317 | 0 | MP_DIGITS(&GX1) = 0; |
318 | 0 | MP_DIGITS(&GX3) = 0; |
319 | 0 | MP_DIGITS(&GX4) = 0; |
320 | 0 | MP_DIGITS(&tmp) = 0; |
321 | |
|
322 | 0 | CHECK_MPI_OK(mp_init(&GX1)); |
323 | 0 | CHECK_MPI_OK(mp_init(&GX3)); |
324 | 0 | CHECK_MPI_OK(mp_init(&GX4)); |
325 | 0 | CHECK_MPI_OK(mp_init(&tmp)); |
326 | | |
327 | 0 | SECITEM_TO_MPINT(*gx1, &GX1); |
328 | 0 | SECITEM_TO_MPINT(*gx3, &GX3); |
329 | 0 | SECITEM_TO_MPINT(*gx4, &GX4); |
330 | | |
331 | | /* In round 2, the peer/attacker sends us g^x3 and g^x4 and the protocol |
332 | | requires that these values are distinct. */ |
333 | 0 | if (mp_cmp(&GX3, &GX4) == 0) { |
334 | 0 | return MP_BADARG; |
335 | 0 | } |
336 | | |
337 | 0 | CHECK_MPI_OK(mp_mul(&GX1, &GX3, &tmp)); |
338 | 0 | CHECK_MPI_OK(mp_mul(&tmp, &GX4, &tmp)); |
339 | 0 | CHECK_MPI_OK(mp_mod(&tmp, p, base)); |
340 | | |
341 | 0 | cleanup: |
342 | 0 | mp_clear(&GX1); |
343 | 0 | mp_clear(&GX3); |
344 | 0 | mp_clear(&GX4); |
345 | 0 | mp_clear(&tmp); |
346 | 0 | return err; |
347 | 0 | } |
348 | | |
349 | | SECStatus |
350 | | JPAKE_Round2(PLArenaPool *arena, |
351 | | const SECItem *p, const SECItem *q, const SECItem *gx1, |
352 | | const SECItem *gx3, const SECItem *gx4, SECItem *base, |
353 | | const SECItem *x2, const SECItem *s, SECItem *x2s) |
354 | 0 | { |
355 | 0 | mp_err err; |
356 | 0 | mp_int P; |
357 | 0 | mp_int Q; |
358 | 0 | mp_int X2; |
359 | 0 | mp_int S; |
360 | 0 | mp_int result; |
361 | |
|
362 | 0 | if (!arena || |
363 | 0 | !p || !p->data || p->len == 0 || |
364 | 0 | !q || !q->data || q->len == 0 || |
365 | 0 | !gx1 || !gx1->data || gx1->len == 0 || |
366 | 0 | !gx3 || !gx3->data || gx3->len == 0 || |
367 | 0 | !gx4 || !gx4->data || gx4->len == 0 || |
368 | 0 | !base || base->data != NULL || |
369 | 0 | (x2s != NULL && (x2s->data != NULL || |
370 | 0 | !x2 || !x2->data || x2->len == 0 || |
371 | 0 | !s || !s->data || s->len == 0))) { |
372 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
373 | 0 | return SECFailure; |
374 | 0 | } |
375 | | |
376 | 0 | MP_DIGITS(&P) = 0; |
377 | 0 | MP_DIGITS(&Q) = 0; |
378 | 0 | MP_DIGITS(&X2) = 0; |
379 | 0 | MP_DIGITS(&S) = 0; |
380 | 0 | MP_DIGITS(&result) = 0; |
381 | |
|
382 | 0 | CHECK_MPI_OK(mp_init(&P)); |
383 | 0 | CHECK_MPI_OK(mp_init(&Q)); |
384 | 0 | CHECK_MPI_OK(mp_init(&result)); |
385 | | |
386 | 0 | if (x2s != NULL) { |
387 | 0 | CHECK_MPI_OK(mp_init(&X2)); |
388 | 0 | CHECK_MPI_OK(mp_init(&S)); |
389 | | |
390 | 0 | SECITEM_TO_MPINT(*q, &Q); |
391 | 0 | SECITEM_TO_MPINT(*x2, &X2); |
392 | | |
393 | 0 | SECITEM_TO_MPINT(*s, &S); |
394 | | /* S must be in [1, Q-1] */ |
395 | 0 | if (mp_cmp_z(&S) <= 0 || mp_cmp(&S, &Q) >= 0) { |
396 | 0 | err = MP_BADARG; |
397 | 0 | goto cleanup; |
398 | 0 | } |
399 | | |
400 | 0 | CHECK_MPI_OK(mp_mulmod(&X2, &S, &Q, &result)); |
401 | 0 | MPINT_TO_SECITEM(&result, x2s, arena); |
402 | 0 | } |
403 | | |
404 | 0 | SECITEM_TO_MPINT(*p, &P); |
405 | 0 | CHECK_MPI_OK(jpake_Round2Base(gx1, gx3, gx4, &P, &result)); |
406 | 0 | MPINT_TO_SECITEM(&result, base, arena); |
407 | | |
408 | 0 | cleanup: |
409 | 0 | mp_clear(&P); |
410 | 0 | mp_clear(&Q); |
411 | 0 | mp_clear(&X2); |
412 | 0 | mp_clear(&S); |
413 | 0 | mp_clear(&result); |
414 | |
|
415 | 0 | if (err != MP_OKAY) { |
416 | 0 | MP_TO_SEC_ERROR(err); |
417 | 0 | return SECFailure; |
418 | 0 | } |
419 | 0 | return SECSuccess; |
420 | 0 | } |
421 | | |
422 | | SECStatus |
423 | | JPAKE_Final(PLArenaPool *arena, const SECItem *p, const SECItem *q, |
424 | | const SECItem *x2, const SECItem *gx4, const SECItem *x2s, |
425 | | const SECItem *B, SECItem *K) |
426 | 0 | { |
427 | 0 | mp_err err; |
428 | 0 | mp_int P; |
429 | 0 | mp_int Q; |
430 | 0 | mp_int tmp; |
431 | 0 | mp_int exponent; |
432 | 0 | mp_int divisor; |
433 | 0 | mp_int base; |
434 | |
|
435 | 0 | if (!arena || |
436 | 0 | !p || !p->data || p->len == 0 || |
437 | 0 | !q || !q->data || q->len == 0 || |
438 | 0 | !x2 || !x2->data || x2->len == 0 || |
439 | 0 | !gx4 || !gx4->data || gx4->len == 0 || |
440 | 0 | !x2s || !x2s->data || x2s->len == 0 || |
441 | 0 | !B || !B->data || B->len == 0 || |
442 | 0 | !K || K->data != NULL) { |
443 | 0 | PORT_SetError(SEC_ERROR_INVALID_ARGS); |
444 | 0 | return SECFailure; |
445 | 0 | } |
446 | | |
447 | 0 | MP_DIGITS(&P) = 0; |
448 | 0 | MP_DIGITS(&Q) = 0; |
449 | 0 | MP_DIGITS(&tmp) = 0; |
450 | 0 | MP_DIGITS(&exponent) = 0; |
451 | 0 | MP_DIGITS(&divisor) = 0; |
452 | 0 | MP_DIGITS(&base) = 0; |
453 | |
|
454 | 0 | CHECK_MPI_OK(mp_init(&P)); |
455 | 0 | CHECK_MPI_OK(mp_init(&Q)); |
456 | 0 | CHECK_MPI_OK(mp_init(&tmp)); |
457 | 0 | CHECK_MPI_OK(mp_init(&exponent)); |
458 | 0 | CHECK_MPI_OK(mp_init(&divisor)); |
459 | 0 | CHECK_MPI_OK(mp_init(&base)); |
460 | | |
461 | | /* exponent = -x2s (mod q) */ |
462 | 0 | SECITEM_TO_MPINT(*q, &Q); |
463 | 0 | SECITEM_TO_MPINT(*x2s, &tmp); |
464 | | /* q == 0 (mod q), so q - x2s == -x2s (mod q) */ |
465 | 0 | CHECK_MPI_OK(mp_sub(&Q, &tmp, &exponent)); |
466 | | |
467 | | /* divisor = gx4^-x2s = 1/(gx4^x2s) (mod p) */ |
468 | 0 | SECITEM_TO_MPINT(*p, &P); |
469 | 0 | SECITEM_TO_MPINT(*gx4, &tmp); |
470 | 0 | CHECK_MPI_OK(mp_exptmod(&tmp, &exponent, &P, &divisor)); |
471 | | |
472 | | /* base = B*divisor = B/(gx4^x2s) (mod p) */ |
473 | 0 | SECITEM_TO_MPINT(*B, &tmp); |
474 | 0 | CHECK_MPI_OK(mp_mulmod(&divisor, &tmp, &P, &base)); |
475 | | |
476 | | /* tmp = base^x2 (mod p) */ |
477 | 0 | SECITEM_TO_MPINT(*x2, &exponent); |
478 | 0 | CHECK_MPI_OK(mp_exptmod(&base, &exponent, &P, &tmp)); |
479 | | |
480 | 0 | MPINT_TO_SECITEM(&tmp, K, arena); |
481 | | |
482 | 0 | cleanup: |
483 | 0 | mp_clear(&P); |
484 | 0 | mp_clear(&Q); |
485 | 0 | mp_clear(&tmp); |
486 | 0 | mp_clear(&exponent); |
487 | 0 | mp_clear(&divisor); |
488 | 0 | mp_clear(&base); |
489 | |
|
490 | 0 | if (err != MP_OKAY) { |
491 | 0 | MP_TO_SEC_ERROR(err); |
492 | 0 | return SECFailure; |
493 | 0 | } |
494 | 0 | return SECSuccess; |
495 | 0 | } |