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