1 /* 2 * Copyright (C) 2021 - This file is part of libecc project 3 * 4 * Authors: 5 * Ryad BENADJILA <ryadbenadjila@gmail.com> 6 * Arnaud EBALARD <arnaud.ebalard@ssi.gouv.fr> 7 * 8 * This software is licensed under a dual BSD and GPL v2 license. 9 * See LICENSE file at the root folder of the project. 10 */ 11 #include <libecc/curves/aff_pt.h> 12 13 #define AFF_PT_MONTGOMERY_MAGIC ((word_t)(0x7390a9bc43d94598ULL)) 14 15 /* Verify that an affine point has already been initialized. 16 * 17 * Returns 0 on success, -1 on error. 18 */ 19 int aff_pt_montgomery_check_initialized(aff_pt_montgomery_src_t in) 20 { 21 int ret; 22 23 MUST_HAVE(((in != NULL) && (in->magic == AFF_PT_MONTGOMERY_MAGIC)), ret, err); 24 ret = ec_montgomery_crv_check_initialized(in->crv); 25 26 err: 27 return ret; 28 } 29 30 /* 31 * Initialize pointed aff_pt_montgomery structure to make it usable by library 32 * function on given curve. 33 * 34 * Returns 0 on success, -1 on error. 35 */ 36 int aff_pt_montgomery_init(aff_pt_montgomery_t in, ec_montgomery_crv_src_t curve) 37 { 38 int ret; 39 40 MUST_HAVE((in != NULL), ret, err); 41 ret = ec_montgomery_crv_check_initialized(curve); EG(ret, err); 42 43 ret = fp_init(&(in->u), curve->A.ctx); EG(ret, err); 44 ret = fp_init(&(in->v), curve->A.ctx); EG(ret, err); 45 46 in->crv = curve; 47 in->magic = AFF_PT_MONTGOMERY_MAGIC; 48 49 err: 50 return ret; 51 } 52 53 /* 54 * Initialize pointed aff_pt_montgomery structure to make it usable by library 55 * function on given curve with explicit coordinates. 56 * 57 * Returns 0 on success, -1 on error. 58 */ 59 int aff_pt_montgomery_init_from_coords(aff_pt_montgomery_t in, 60 ec_montgomery_crv_src_t curve, 61 fp_src_t ucoord, fp_src_t vcoord) 62 { 63 int ret; 64 65 ret = aff_pt_montgomery_init(in, curve); EG(ret, err); 66 ret = fp_copy(&(in->u), ucoord); EG(ret, err); 67 ret = fp_copy(&(in->v), vcoord); 68 69 err: 70 return ret; 71 } 72 73 /* 74 * Uninitialize pointed affine point to prevent further use (magic field 75 * in the structure is zeroized) and zeroize associated storage space. 76 * Note that the curve context pointed to by the point element (passed 77 * during init) is left untouched. 78 * 79 */ 80 void aff_pt_montgomery_uninit(aff_pt_montgomery_t in) 81 { 82 if ((in != NULL) && (in->magic == AFF_PT_MONTGOMERY_MAGIC) && (in->crv != NULL)) { 83 fp_uninit(&(in->u)); 84 fp_uninit(&(in->v)); 85 86 in->crv = NULL; 87 in->magic = WORD(0); 88 } 89 90 return; 91 } 92 93 /* 94 * 'on_curve' set to 1 if the point of coordinates (u,v) is on the curve, i.e. if it 95 * verifies curve equation B*v^2 = u^3 + A*u^2 + u. It is set to 0 otherwise. 96 * 'on_curve' is not meaningful on error. 97 * 98 * Returns 0 on success, -1 on error. 99 */ 100 int is_on_montgomery_curve(fp_src_t u, fp_src_t v, ec_montgomery_crv_src_t curve, int *on_curve) 101 { 102 fp Bv2, u3, Au2, tmp; 103 int ret, cmp; 104 Bv2.magic = u3.magic = Au2.magic = tmp.magic = WORD(0); 105 106 MUST_HAVE((on_curve != NULL), ret, err); 107 ret = ec_montgomery_crv_check_initialized(curve); EG(ret, err); 108 109 ret = fp_check_initialized(u); EG(ret, err); 110 ret = fp_check_initialized(v); EG(ret, err); 111 112 MUST_HAVE((u->ctx == v->ctx), ret, err); 113 MUST_HAVE((u->ctx == curve->A.ctx), ret, err); 114 115 ret = fp_init(&Bv2, v->ctx); EG(ret, err); 116 ret = fp_sqr(&Bv2, v); EG(ret, err); 117 ret = fp_mul(&Bv2, &(curve->B), &Bv2); EG(ret, err); 118 119 ret = fp_init(&Au2, u->ctx); EG(ret, err); 120 ret = fp_sqr(&Au2, u); EG(ret, err); 121 ret = fp_copy(&u3, &Au2); EG(ret, err); 122 ret = fp_mul(&Au2, &(curve->A), &Au2); EG(ret, err); 123 124 ret = fp_mul(&u3, &u3, u); EG(ret, err); 125 126 ret = fp_init(&tmp, u->ctx); EG(ret, err); 127 ret = fp_add(&tmp, &u3, &Au2); EG(ret, err); 128 ret = fp_add(&tmp, &tmp, u); EG(ret, err); 129 130 ret = fp_cmp(&tmp, &Bv2, &cmp); EG(ret, err); 131 132 (*on_curve) = (!cmp); 133 134 err: 135 fp_uninit(&Bv2); 136 fp_uninit(&u3); 137 fp_uninit(&Au2); 138 fp_uninit(&tmp); 139 140 return ret; 141 } 142 143 /* Checks if affine coordinates point is on a Montgomery curve. 'on_curve' is set to 1 if yes, 144 * 0 if no. 'on_curve' is not meaningful in case of error. 145 * 146 * Returns 0 on success, -1 on error. 147 */ 148 int aff_pt_montgomery_is_on_curve(aff_pt_montgomery_src_t pt, int *on_curve) 149 { 150 int ret; 151 152 ret = aff_pt_montgomery_check_initialized(pt); EG(ret, err); 153 154 ret = is_on_montgomery_curve(&(pt->u), &(pt->v), pt->crv, on_curve); 155 156 err: 157 return ret; 158 } 159 160 /* Copy a Montgomery affine point in an output. The output is initialized properly. 161 * 162 * Returns 0 on success, -1 on error. 163 */ 164 int ec_montgomery_aff_copy(aff_pt_montgomery_t out, aff_pt_montgomery_src_t in) 165 { 166 int ret; 167 168 ret = aff_pt_montgomery_check_initialized(in); EG(ret, err); 169 170 ret = aff_pt_montgomery_init(out, in->crv); EG(ret, err); 171 ret = fp_copy(&(out->u), &(in->u)); EG(ret, err); 172 ret = fp_copy(&(out->v), &(in->v)); 173 174 err: 175 return ret; 176 } 177 178 /* 179 * Compares two given affine points on a Montgomery curve, it returns 0 in input 'cmp' if 180 * they correspond or not 0 if not. 'cmp' is not meaningful on error. 181 * 182 * Returns 0 on success, -1 on error. 183 */ 184 int ec_montgomery_aff_cmp(aff_pt_montgomery_src_t in1, aff_pt_montgomery_src_t in2, int *cmp) 185 { 186 int ret, cmp1, cmp2; 187 188 MUST_HAVE((cmp != NULL), ret, err); 189 ret = aff_pt_montgomery_check_initialized(in1); EG(ret, err); 190 ret = aff_pt_montgomery_check_initialized(in2); EG(ret, err); 191 MUST_HAVE((in1->crv == in2->crv), ret, err); 192 193 ret = fp_cmp(&(in1->u), &(in2->u), &cmp1); EG(ret, err); 194 ret = fp_cmp(&(in1->v), &(in2->v), &cmp2); EG(ret, err); 195 196 (*cmp) = (cmp1 | cmp2); 197 198 err: 199 return ret; 200 } 201 202 /* 203 * Import an Montgomery affine point from a buffer with the following layout; the 2 204 * coordinates (elements of Fp) are each encoded on p_len bytes, where p_len 205 * is the size of p in bytes (e.g. 66 for a prime p of 521 bits). Each 206 * coordinate is encoded in big endian. Size of buffer must exactly match 207 * 2 * p_len. 208 * 209 * Returns 0 on success, -1 on error. 210 */ 211 int aff_pt_montgomery_import_from_buf(aff_pt_montgomery_t pt, 212 const u8 *pt_buf, 213 u16 pt_buf_len, ec_montgomery_crv_src_t crv) 214 { 215 fp_ctx_src_t ctx; 216 u16 coord_len; 217 int ret, on_curve; 218 219 ret = ec_montgomery_crv_check_initialized(crv); EG(ret, err); 220 MUST_HAVE((pt_buf != NULL) && (pt != NULL), ret, err); 221 222 ctx = crv->A.ctx; 223 coord_len = (u16)BYTECEIL(ctx->p_bitlen); 224 225 MUST_HAVE((pt_buf_len == (2 * coord_len)), ret, err); 226 227 ret = fp_init_from_buf(&(pt->u), ctx, pt_buf, coord_len); EG(ret, err); 228 ret = fp_init_from_buf(&(pt->v), ctx, pt_buf + coord_len, coord_len); EG(ret, err); 229 230 /* Set the curve */ 231 pt->crv = crv; 232 233 /* Mark the point as initialized */ 234 pt->magic = AFF_PT_MONTGOMERY_MAGIC; 235 236 /* Check that the point is indeed on the provided curve, uninitialize it 237 * if this is not the case. 238 */ 239 ret = aff_pt_montgomery_is_on_curve(pt, &on_curve); EG(ret, err); 240 if (!on_curve) { 241 aff_pt_montgomery_uninit(pt); 242 ret = -1; 243 } 244 245 err: 246 return ret; 247 } 248 249 250 /* Export an Montgomery affine point to a buffer with the following layout; the 2 251 * coordinates (elements of Fp) are each encoded on p_len bytes, where p_len 252 * is the size of p in bytes (e.g. 66 for a prime p of 521 bits). Each 253 * coordinate is encoded in big endian. Size of buffer must exactly match 254 * 2 * p_len. 255 * 256 * Returns 0 on success, -1 on error. 257 */ 258 int aff_pt_montgomery_export_to_buf(aff_pt_montgomery_src_t pt, u8 *pt_buf, u32 pt_buf_len) 259 { 260 fp_ctx_src_t ctx; 261 u16 coord_len; 262 int ret, on_curve; 263 264 ret = aff_pt_montgomery_check_initialized(pt); EG(ret, err); 265 MUST_HAVE((pt_buf != NULL), ret, err); 266 267 /* The point to be exported must be on the curve */ 268 ret = aff_pt_montgomery_is_on_curve(pt, &on_curve); EG(ret, err); 269 MUST_HAVE(on_curve, ret, err); 270 271 ctx = pt->crv->A.ctx; 272 coord_len = (u16)BYTECEIL(ctx->p_bitlen); 273 274 MUST_HAVE((pt_buf_len == (2 * coord_len)), ret, err); 275 276 /* Export the three coordinates */ 277 ret = fp_export_to_buf(pt_buf, coord_len, &(pt->u)); EG(ret, err); 278 ret = fp_export_to_buf(pt_buf + coord_len, coord_len, &(pt->v)); 279 280 err: 281 return ret; 282 } 283 284 /**** Mappings between curves *************/ 285 /* 286 * Mapping curves from Montgomery to short Weiertstrass. 287 * 288 * M{A, B} is mapped to W{a, b} using the formula: 289 * a = (3-A^2)/(3*B^2) 290 * b = (2*A^3-9*A)/(27*B^3) 291 * 292 * Returns 0 on success, -1 on error. 293 */ 294 int curve_montgomery_to_shortw(ec_montgomery_crv_src_t montgomery_crv, ec_shortw_crv_t shortw_crv) 295 { 296 fp tmp, tmp2, a, b; 297 int ret; 298 tmp.magic = tmp2.magic = a.magic = b.magic = WORD(0); 299 300 ret = ec_montgomery_crv_check_initialized(montgomery_crv); EG(ret, err); 301 302 ret = fp_init(&tmp, montgomery_crv->A.ctx); EG(ret, err); 303 ret = fp_init(&tmp2, montgomery_crv->A.ctx); EG(ret, err); 304 ret = fp_init(&a, montgomery_crv->A.ctx); EG(ret, err); 305 ret = fp_init(&b, montgomery_crv->A.ctx); EG(ret, err); 306 307 /* Compute a */ 308 ret = fp_sqr(&tmp, &(montgomery_crv->B)); EG(ret, err); 309 ret = fp_set_word_value(&tmp2, WORD(3)); EG(ret, err); 310 /* 3*B^2 */ 311 ret = fp_mul(&tmp, &tmp, &tmp2); EG(ret, err); 312 /* (3*B^2)^-1 */ 313 ret = fp_inv(&tmp, &tmp); EG(ret, err); 314 315 /* (3-A^2) */ 316 ret = fp_sqr(&tmp2, &(montgomery_crv->A)); EG(ret, err); 317 ret = fp_set_word_value(&a, WORD(3)); EG(ret, err); 318 ret = fp_sub(&tmp2, &a, &tmp2); EG(ret, err); 319 320 ret = fp_mul(&a, &tmp2, &tmp); EG(ret, err); 321 322 /* Compute b */ 323 ret = fp_sqr(&tmp, &(montgomery_crv->B)); EG(ret, err); 324 ret = fp_mul(&tmp, &tmp, &(montgomery_crv->B)); EG(ret, err); 325 ret = fp_set_word_value(&tmp2, WORD(27)); EG(ret, err); 326 /* (27*B^3) */ 327 ret = fp_mul(&tmp, &tmp, &tmp2); EG(ret, err); 328 /* (27*B^3)^-1 */ 329 ret = fp_inv(&tmp, &tmp); EG(ret, err); 330 331 /* (2*A^3-9*A) */ 332 ret = fp_set_word_value(&tmp2, WORD(2)); EG(ret, err); 333 ret = fp_mul(&tmp2, &tmp2, &(montgomery_crv->A)); EG(ret, err); 334 ret = fp_mul(&tmp2, &tmp2, &(montgomery_crv->A)); EG(ret, err); 335 ret = fp_mul(&tmp2, &tmp2, &(montgomery_crv->A)); EG(ret, err); 336 337 ret = fp_set_word_value(&b, WORD(9)); EG(ret, err); 338 ret = fp_mul(&b, &b, &(montgomery_crv->A)); EG(ret, err); 339 ret = fp_sub(&b, &tmp2, &b); EG(ret, err); 340 341 ret = fp_mul(&b, &b, &tmp); EG(ret, err); 342 343 /* Initialize our short Weiertstrass curve */ 344 ret = ec_shortw_crv_init(shortw_crv, &a, &b, &(montgomery_crv->order)); 345 346 err: 347 fp_uninit(&a); 348 fp_uninit(&b); 349 fp_uninit(&tmp); 350 fp_uninit(&tmp2); 351 352 return ret; 353 } 354 355 /* 356 * Checks that a short Weiertstrass curve and Montgomery curve are compatible. 357 * 358 * Returns 0 on success, -1 on error. 359 */ 360 int curve_montgomery_shortw_check(ec_montgomery_crv_src_t montgomery_crv, 361 ec_shortw_crv_src_t shortw_crv) 362 { 363 int ret, cmp; 364 ec_shortw_crv check; 365 check.magic = WORD(0); 366 367 ret = ec_shortw_crv_check_initialized(shortw_crv); EG(ret, err); 368 ret = curve_montgomery_to_shortw(montgomery_crv, &check); EG(ret, err); 369 370 /* Check elements */ 371 MUST_HAVE((!fp_cmp(&(check.a), &(shortw_crv->a), &cmp)) && (!cmp), ret, err); 372 MUST_HAVE((!fp_cmp(&(check.b), &(shortw_crv->b), &cmp)) && (!cmp), ret, err); 373 MUST_HAVE((!nn_cmp(&(check.order), &(shortw_crv->order), &cmp)) && (!cmp), ret, err); 374 375 err: 376 ec_shortw_crv_uninit(&check); 377 378 return ret; 379 } 380 381 /* 382 * Mapping curves from short Weiertstrass to Montgomery 383 * 384 * W{a, b} is mapped to M{A, B} using the formula: 385 * A = 3 * alpha / gamma 386 * B = 1 / gamma 387 * with gamma square root of c = a + 3 * alpha**2 388 * 389 * Returns 0 on success, -1 on error. 390 */ 391 int curve_shortw_to_montgomery(ec_shortw_crv_src_t shortw_crv, 392 ec_montgomery_crv_t montgomery_crv, 393 fp_src_t alpha, fp_src_t gamma) 394 { 395 int ret, cmp; 396 fp c, gamma_inv, A, tmp; 397 c.magic = gamma_inv.magic = A.magic = tmp.magic = WORD(0); 398 399 ret = ec_shortw_crv_check_initialized(shortw_crv); EG(ret, err); 400 ret = fp_check_initialized(alpha); EG(ret, err); 401 ret = fp_check_initialized(gamma); EG(ret, err); 402 MUST_HAVE((alpha->ctx == shortw_crv->a.ctx) && (gamma->ctx == shortw_crv->a.ctx), ret, err); 403 404 ret = fp_init(&A, shortw_crv->a.ctx); EG(ret, err); 405 ret = fp_init(&gamma_inv, shortw_crv->a.ctx); EG(ret, err); 406 ret = fp_init(&c, shortw_crv->a.ctx); EG(ret, err); 407 ret = fp_init(&tmp, shortw_crv->a.ctx); EG(ret, err); 408 409 /* Compute 1 / gamma */ 410 ret = fp_inv(&gamma_inv, gamma); EG(ret, err); 411 412 /* Compute A */ 413 ret = fp_set_word_value(&A, WORD(3)); EG(ret, err); 414 ret = fp_mul(&A, &A, alpha); EG(ret, err); 415 ret = fp_mul(&A, &A, &gamma_inv); EG(ret, err); 416 417 /* Sanity check on c */ 418 ret = fp_set_word_value(&c, WORD(3)); EG(ret, err); 419 ret = fp_mul(&c, &c, alpha); EG(ret, err); 420 ret = fp_mul(&c, &c, alpha); EG(ret, err); 421 ret = fp_add(&c, &c, &(shortw_crv->a)); EG(ret, err); 422 ret = fp_sqr(&tmp, gamma); EG(ret, err); 423 /* gamma ** 2 must be equal to c */ 424 MUST_HAVE((!fp_cmp(&c, &tmp, &cmp)) && (!cmp), ret, err); 425 426 /* B is simply the inverse of gamma */ 427 ret = ec_montgomery_crv_init(montgomery_crv, &A, &gamma_inv, &(shortw_crv->order)); 428 429 err: 430 fp_uninit(&A); 431 fp_uninit(&gamma_inv); 432 fp_uninit(&c); 433 fp_uninit(&tmp); 434 435 return ret; 436 } 437 438 /* 439 * Mapping points from Montgomery to short Weierstrass. 440 * Point M(u, v) is mapped to W(x, y) with the formula: 441 * - (x, y) = ((u/B)+(A/3B), v/B) 442 * 443 * Returns 0 on success, -1 on error. 444 */ 445 int aff_pt_montgomery_to_shortw(aff_pt_montgomery_src_t in_montgomery, 446 ec_shortw_crv_src_t shortw_crv, aff_pt_t out_shortw) 447 { 448 int ret, on_curve; 449 fp tmp, tmp2; 450 tmp.magic = tmp2.magic = WORD(0); 451 452 ret = ec_shortw_crv_check_initialized(shortw_crv); EG(ret, err); 453 454 /* Check that our input point is on its curve */ 455 MUST_HAVE((!aff_pt_montgomery_is_on_curve(in_montgomery, &on_curve)) && on_curve, ret, err); 456 457 ret = fp_init(&tmp, in_montgomery->crv->A.ctx); EG(ret, err); 458 ret = fp_init(&tmp2, in_montgomery->crv->A.ctx); EG(ret, err); 459 460 ret = aff_pt_montgomery_check_initialized(in_montgomery); EG(ret, err); 461 ret = curve_montgomery_shortw_check(in_montgomery->crv, shortw_crv); EG(ret, err); 462 463 ret = aff_pt_init(out_shortw, shortw_crv); EG(ret, err); 464 465 ret = fp_inv(&tmp, &(in_montgomery->crv->B)); EG(ret, err); 466 ret = fp_mul(&tmp, &tmp, &(in_montgomery->u)); EG(ret, err); 467 468 ret = fp_set_word_value(&tmp2, WORD(3)); EG(ret, err); 469 ret = fp_mul(&tmp2, &tmp2, &(in_montgomery->crv->B)); EG(ret, err); 470 ret = fp_inv(&tmp2, &tmp2); EG(ret, err); 471 ret = fp_mul(&tmp2, &tmp2, &(in_montgomery->crv->A)); EG(ret, err); 472 473 ret = fp_add(&(out_shortw->x), &tmp, &tmp2); EG(ret, err); 474 475 ret = fp_inv(&tmp, &(in_montgomery->crv->B)); EG(ret, err); 476 ret = fp_mul(&(out_shortw->y), &tmp, &(in_montgomery->v)); EG(ret, err); 477 478 /* Final check that the point is on the curve */ 479 MUST_HAVE((!aff_pt_is_on_curve(out_shortw, &on_curve)) && on_curve, ret, err); 480 481 err: 482 fp_uninit(&tmp); 483 fp_uninit(&tmp2); 484 485 return ret; 486 } 487 488 /* 489 * Mapping from short Weierstrass to Montgomery. 490 * Point W(x, y) is mapped to M(u, v) with the formula: 491 * - (u, v) = (((Bx)−(A/3), By) 492 * 493 * Returns 0 on success, -1 on error. 494 */ 495 int aff_pt_shortw_to_montgomery(aff_pt_src_t in_shortw, 496 ec_montgomery_crv_src_t montgomery_crv, 497 aff_pt_montgomery_t out_montgomery) 498 { 499 int ret, on_curve; 500 fp tmp, tmp2; 501 tmp.magic = tmp2.magic = WORD(0); 502 503 ret = ec_montgomery_crv_check_initialized(montgomery_crv); EG(ret, err); 504 505 /* Check that our input point is on its curve */ 506 MUST_HAVE((!aff_pt_is_on_curve(in_shortw, &on_curve)) && on_curve, ret, err); 507 508 ret = fp_init(&tmp, in_shortw->crv->a.ctx); EG(ret, err); 509 ret = fp_init(&tmp2, in_shortw->crv->a.ctx); EG(ret, err); 510 511 ret = curve_montgomery_shortw_check(montgomery_crv, in_shortw->crv); EG(ret, err); 512 513 ret = aff_pt_montgomery_init(out_montgomery, montgomery_crv); EG(ret, err); 514 515 /* A/3 */ 516 ret = fp_inv_word(&tmp, WORD(3)); EG(ret, err); 517 ret = fp_mul(&tmp, &tmp, &(montgomery_crv->A)); EG(ret, err); 518 519 /* Bx */ 520 ret = fp_mul(&tmp2, &(montgomery_crv->B), &(in_shortw->x)); EG(ret, err); 521 522 /* u = (Bx) - (A/3) */ 523 ret = fp_sub(&(out_montgomery->u), &tmp2, &tmp); EG(ret, err); 524 525 /* v = By */ 526 ret = fp_mul(&(out_montgomery->v), &(montgomery_crv->B), &(in_shortw->y)); EG(ret, err); 527 528 /* Final check that the point is on the curve */ 529 MUST_HAVE((!aff_pt_montgomery_is_on_curve(out_montgomery, &on_curve)) && on_curve, ret, err); 530 531 err: 532 fp_uninit(&tmp); 533 fp_uninit(&tmp2); 534 535 return ret; 536 } 537 538 539 /* 540 * Recover the two possible v coordinates from one u on a given 541 * curve. 542 * The two outputs v1 and v2 are initialized in the function. 543 * 544 * The function returns -1 on error, 0 on success. 545 * 546 */ 547 int aff_pt_montgomery_v_from_u(fp_t v1, fp_t v2, fp_src_t u, ec_montgomery_crv_src_t crv) 548 { 549 int ret; 550 551 /* Sanity checks */ 552 ret = fp_check_initialized(u); EG(ret, err); 553 ret = ec_montgomery_crv_check_initialized(crv); EG(ret, err); 554 MUST_HAVE((u->ctx == crv->A.ctx) && (u->ctx == crv->B.ctx), ret, err); 555 MUST_HAVE((v1 != NULL) && (v2 != NULL), ret, err); 556 /* Aliasing is not supported */ 557 MUST_HAVE((v1 != v2) && (v1 != u), ret, err); 558 559 /* Initialize v1 and v2 with context */ 560 ret = fp_init(v1, u->ctx); EG(ret, err); 561 ret = fp_init(v2, u->ctx); EG(ret, err); 562 563 /* v must satisfy the equation B v^2 = u^3 + A u^2 + u, 564 * so we compute square root for B^-1 * (u^3 + A u^2 + u) 565 */ 566 ret = fp_sqr(v2, u); EG(ret, err); 567 ret = fp_mul(v1, v2, u); EG(ret, err); 568 ret = fp_mul(v2, v2, &(crv->A)); EG(ret, err); 569 ret = fp_add(v1, v1, v2); EG(ret, err); 570 ret = fp_add(v1, v1, u); EG(ret, err); 571 ret = fp_inv(v2, &(crv->B)); EG(ret, err); 572 ret = fp_mul(v1, v1, v2); EG(ret, err); 573 574 /* Choose any of the two square roots as the solution */ 575 ret = fp_sqrt(v1, v2, v1); 576 577 err: 578 return ret; 579 } 580