Path: blob/main/contrib/bearssl/src/ec/ec_prime_i31.c
39507 views
/*1* Copyright (c) 2016 Thomas Pornin <[email protected]>2*3* Permission is hereby granted, free of charge, to any person obtaining4* a copy of this software and associated documentation files (the5* "Software"), to deal in the Software without restriction, including6* without limitation the rights to use, copy, modify, merge, publish,7* distribute, sublicense, and/or sell copies of the Software, and to8* permit persons to whom the Software is furnished to do so, subject to9* the following conditions:10*11* The above copyright notice and this permission notice shall be12* included in all copies or substantial portions of the Software.13*14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,15* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF16* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND17* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS18* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN19* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN20* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE21* SOFTWARE.22*/2324#include "inner.h"2526/*27* Parameters for supported curves (field modulus, and 'b' equation28* parameter; both values use the 'i31' format, and 'b' is in Montgomery29* representation).30*/3132static const uint32_t P256_P[] = {330x00000108,340x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x00000007,350x00000000, 0x00000000, 0x00000040, 0x7FFFFF80,360x000000FF37};3839static const uint32_t P256_R2[] = {400x00000108,410x00014000, 0x00018000, 0x00000000, 0x7FF40000,420x7FEFFFFF, 0x7FF7FFFF, 0x7FAFFFFF, 0x005FFFFF,430x0000000044};4546static const uint32_t P256_B[] = {470x00000108,480x6FEE1803, 0x6229C4BD, 0x21B139BE, 0x327150AA,490x3567802E, 0x3F7212ED, 0x012E4355, 0x782DD38D,500x0000000E51};5253static const uint32_t P384_P[] = {540x0000018C,550x7FFFFFFF, 0x00000001, 0x00000000, 0x7FFFFFF8,560x7FFFFFEF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF,570x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF,580x00000FFF59};6061static const uint32_t P384_R2[] = {620x0000018C,630x00000000, 0x00000080, 0x7FFFFE00, 0x000001FF,640x00000800, 0x00000000, 0x7FFFE000, 0x00001FFF,650x00008000, 0x00008000, 0x00000000, 0x00000000,660x0000000067};6869static const uint32_t P384_B[] = {700x0000018C,710x6E666840, 0x070D0392, 0x5D810231, 0x7651D50C,720x17E218D6, 0x1B192002, 0x44EFE441, 0x3A524E2B,730x2719BA5F, 0x41F02209, 0x36C5643E, 0x5813EFFE,740x000008A575};7677static const uint32_t P521_P[] = {780x00000219,790x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF,800x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF,810x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF,820x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF,830x01FFFFFF84};8586static const uint32_t P521_R2[] = {870x00000219,880x00001000, 0x00000000, 0x00000000, 0x00000000,890x00000000, 0x00000000, 0x00000000, 0x00000000,900x00000000, 0x00000000, 0x00000000, 0x00000000,910x00000000, 0x00000000, 0x00000000, 0x00000000,920x0000000093};9495static const uint32_t P521_B[] = {960x00000219,970x540FC00A, 0x228FEA35, 0x2C34F1EF, 0x67BF107A,980x46FC1CD5, 0x1605E9DD, 0x6937B165, 0x272A3D8F,990x42785586, 0x44C8C778, 0x15F3B8B4, 0x64B73366,1000x03BA8B69, 0x0D05B42A, 0x21F929A2, 0x2C31C393,1010x00654FAE102};103104typedef struct {105const uint32_t *p;106const uint32_t *b;107const uint32_t *R2;108uint32_t p0i;109size_t point_len;110} curve_params;111112static inline const curve_params *113id_to_curve(int curve)114{115static const curve_params pp[] = {116{ P256_P, P256_B, P256_R2, 0x00000001, 65 },117{ P384_P, P384_B, P384_R2, 0x00000001, 97 },118{ P521_P, P521_B, P521_R2, 0x00000001, 133 }119};120121return &pp[curve - BR_EC_secp256r1];122}123124#define I31_LEN ((BR_MAX_EC_SIZE + 61) / 31)125126/*127* Type for a point in Jacobian coordinates:128* -- three values, x, y and z, in Montgomery representation129* -- affine coordinates are X = x / z^2 and Y = y / z^3130* -- for the point at infinity, z = 0131*/132typedef struct {133uint32_t c[3][I31_LEN];134} jacobian;135136/*137* We use a custom interpreter that uses a dozen registers, and138* only six operations:139* MSET(d, a) copy a into d140* MADD(d, a) d = d+a (modular)141* MSUB(d, a) d = d-a (modular)142* MMUL(d, a, b) d = a*b (Montgomery multiplication)143* MINV(d, a, b) invert d modulo p; a and b are used as scratch registers144* MTZ(d) clear return value if d = 0145* Destination of MMUL (d) must be distinct from operands (a and b).146* There is no such constraint for MSUB and MADD.147*148* Registers include the operand coordinates, and temporaries.149*/150#define MSET(d, a) (0x0000 + ((d) << 8) + ((a) << 4))151#define MADD(d, a) (0x1000 + ((d) << 8) + ((a) << 4))152#define MSUB(d, a) (0x2000 + ((d) << 8) + ((a) << 4))153#define MMUL(d, a, b) (0x3000 + ((d) << 8) + ((a) << 4) + (b))154#define MINV(d, a, b) (0x4000 + ((d) << 8) + ((a) << 4) + (b))155#define MTZ(d) (0x5000 + ((d) << 8))156#define ENDCODE 0157158/*159* Registers for the input operands.160*/161#define P1x 0162#define P1y 1163#define P1z 2164#define P2x 3165#define P2y 4166#define P2z 5167168/*169* Alternate names for the first input operand.170*/171#define Px 0172#define Py 1173#define Pz 2174175/*176* Temporaries.177*/178#define t1 6179#define t2 7180#define t3 8181#define t4 9182#define t5 10183#define t6 11184#define t7 12185186/*187* Extra scratch registers available when there is no second operand (e.g.188* for "double" and "affine").189*/190#define t8 3191#define t9 4192#define t10 5193194/*195* Doubling formulas are:196*197* s = 4*x*y^2198* m = 3*(x + z^2)*(x - z^2)199* x' = m^2 - 2*s200* y' = m*(s - x') - 8*y^4201* z' = 2*y*z202*203* If y = 0 (P has order 2) then this yields infinity (z' = 0), as it204* should. This case should not happen anyway, because our curves have205* prime order, and thus do not contain any point of order 2.206*207* If P is infinity (z = 0), then again the formulas yield infinity,208* which is correct. Thus, this code works for all points.209*210* Cost: 8 multiplications211*/212static const uint16_t code_double[] = {213/*214* Compute z^2 (in t1).215*/216MMUL(t1, Pz, Pz),217218/*219* Compute x-z^2 (in t2) and then x+z^2 (in t1).220*/221MSET(t2, Px),222MSUB(t2, t1),223MADD(t1, Px),224225/*226* Compute m = 3*(x+z^2)*(x-z^2) (in t1).227*/228MMUL(t3, t1, t2),229MSET(t1, t3),230MADD(t1, t3),231MADD(t1, t3),232233/*234* Compute s = 4*x*y^2 (in t2) and 2*y^2 (in t3).235*/236MMUL(t3, Py, Py),237MADD(t3, t3),238MMUL(t2, Px, t3),239MADD(t2, t2),240241/*242* Compute x' = m^2 - 2*s.243*/244MMUL(Px, t1, t1),245MSUB(Px, t2),246MSUB(Px, t2),247248/*249* Compute z' = 2*y*z.250*/251MMUL(t4, Py, Pz),252MSET(Pz, t4),253MADD(Pz, t4),254255/*256* Compute y' = m*(s - x') - 8*y^4. Note that we already have257* 2*y^2 in t3.258*/259MSUB(t2, Px),260MMUL(Py, t1, t2),261MMUL(t4, t3, t3),262MSUB(Py, t4),263MSUB(Py, t4),264265ENDCODE266};267268/*269* Addtions formulas are:270*271* u1 = x1 * z2^2272* u2 = x2 * z1^2273* s1 = y1 * z2^3274* s2 = y2 * z1^3275* h = u2 - u1276* r = s2 - s1277* x3 = r^2 - h^3 - 2 * u1 * h^2278* y3 = r * (u1 * h^2 - x3) - s1 * h^3279* z3 = h * z1 * z2280*281* If both P1 and P2 are infinity, then z1 == 0 and z2 == 0, implying that282* z3 == 0, so the result is correct.283* If either of P1 or P2 is infinity, but not both, then z3 == 0, which is284* not correct.285* h == 0 only if u1 == u2; this happens in two cases:286* -- if s1 == s2 then P1 and/or P2 is infinity, or P1 == P2287* -- if s1 != s2 then P1 + P2 == infinity (but neither P1 or P2 is infinity)288*289* Thus, the following situations are not handled correctly:290* -- P1 = 0 and P2 != 0291* -- P1 != 0 and P2 = 0292* -- P1 = P2293* All other cases are properly computed. However, even in "incorrect"294* situations, the three coordinates still are properly formed field295* elements.296*297* The returned flag is cleared if r == 0. This happens in the following298* cases:299* -- Both points are on the same horizontal line (same Y coordinate).300* -- Both points are infinity.301* -- One point is infinity and the other is on line Y = 0.302* The third case cannot happen with our curves (there is no valid point303* on line Y = 0 since that would be a point of order 2). If the two304* source points are non-infinity, then remains only the case where the305* two points are on the same horizontal line.306*307* This allows us to detect the "P1 == P2" case, assuming that P1 != 0 and308* P2 != 0:309* -- If the returned value is not the point at infinity, then it was properly310* computed.311* -- Otherwise, if the returned flag is 1, then P1+P2 = 0, and the result312* is indeed the point at infinity.313* -- Otherwise (result is infinity, flag is 0), then P1 = P2 and we should314* use the 'double' code.315*316* Cost: 16 multiplications317*/318static const uint16_t code_add[] = {319/*320* Compute u1 = x1*z2^2 (in t1) and s1 = y1*z2^3 (in t3).321*/322MMUL(t3, P2z, P2z),323MMUL(t1, P1x, t3),324MMUL(t4, P2z, t3),325MMUL(t3, P1y, t4),326327/*328* Compute u2 = x2*z1^2 (in t2) and s2 = y2*z1^3 (in t4).329*/330MMUL(t4, P1z, P1z),331MMUL(t2, P2x, t4),332MMUL(t5, P1z, t4),333MMUL(t4, P2y, t5),334335/*336* Compute h = u2 - u1 (in t2) and r = s2 - s1 (in t4).337*/338MSUB(t2, t1),339MSUB(t4, t3),340341/*342* Report cases where r = 0 through the returned flag.343*/344MTZ(t4),345346/*347* Compute u1*h^2 (in t6) and h^3 (in t5).348*/349MMUL(t7, t2, t2),350MMUL(t6, t1, t7),351MMUL(t5, t7, t2),352353/*354* Compute x3 = r^2 - h^3 - 2*u1*h^2.355* t1 and t7 can be used as scratch registers.356*/357MMUL(P1x, t4, t4),358MSUB(P1x, t5),359MSUB(P1x, t6),360MSUB(P1x, t6),361362/*363* Compute y3 = r*(u1*h^2 - x3) - s1*h^3.364*/365MSUB(t6, P1x),366MMUL(P1y, t4, t6),367MMUL(t1, t5, t3),368MSUB(P1y, t1),369370/*371* Compute z3 = h*z1*z2.372*/373MMUL(t1, P1z, P2z),374MMUL(P1z, t1, t2),375376ENDCODE377};378379/*380* Check that the point is on the curve. This code snippet assumes the381* following conventions:382* -- Coordinates x and y have been freshly decoded in P1 (but not383* converted to Montgomery coordinates yet).384* -- P2x, P2y and P2z are set to, respectively, R^2, b*R and 1.385*/386static const uint16_t code_check[] = {387388/* Convert x and y to Montgomery representation. */389MMUL(t1, P1x, P2x),390MMUL(t2, P1y, P2x),391MSET(P1x, t1),392MSET(P1y, t2),393394/* Compute x^3 in t1. */395MMUL(t2, P1x, P1x),396MMUL(t1, P1x, t2),397398/* Subtract 3*x from t1. */399MSUB(t1, P1x),400MSUB(t1, P1x),401MSUB(t1, P1x),402403/* Add b. */404MADD(t1, P2y),405406/* Compute y^2 in t2. */407MMUL(t2, P1y, P1y),408409/* Compare y^2 with x^3 - 3*x + b; they must match. */410MSUB(t1, t2),411MTZ(t1),412413/* Set z to 1 (in Montgomery representation). */414MMUL(P1z, P2x, P2z),415416ENDCODE417};418419/*420* Conversion back to affine coordinates. This code snippet assumes that421* the z coordinate of P2 is set to 1 (not in Montgomery representation).422*/423static const uint16_t code_affine[] = {424425/* Save z*R in t1. */426MSET(t1, P1z),427428/* Compute z^3 in t2. */429MMUL(t2, P1z, P1z),430MMUL(t3, P1z, t2),431MMUL(t2, t3, P2z),432433/* Invert to (1/z^3) in t2. */434MINV(t2, t3, t4),435436/* Compute y. */437MSET(t3, P1y),438MMUL(P1y, t2, t3),439440/* Compute (1/z^2) in t3. */441MMUL(t3, t2, t1),442443/* Compute x. */444MSET(t2, P1x),445MMUL(P1x, t2, t3),446447ENDCODE448};449450static uint32_t451run_code(jacobian *P1, const jacobian *P2,452const curve_params *cc, const uint16_t *code)453{454uint32_t r;455uint32_t t[13][I31_LEN];456size_t u;457458r = 1;459460/*461* Copy the two operands in the dedicated registers.462*/463memcpy(t[P1x], P1->c, 3 * I31_LEN * sizeof(uint32_t));464memcpy(t[P2x], P2->c, 3 * I31_LEN * sizeof(uint32_t));465466/*467* Run formulas.468*/469for (u = 0;; u ++) {470unsigned op, d, a, b;471472op = code[u];473if (op == 0) {474break;475}476d = (op >> 8) & 0x0F;477a = (op >> 4) & 0x0F;478b = op & 0x0F;479op >>= 12;480switch (op) {481uint32_t ctl;482size_t plen;483unsigned char tp[(BR_MAX_EC_SIZE + 7) >> 3];484485case 0:486memcpy(t[d], t[a], I31_LEN * sizeof(uint32_t));487break;488case 1:489ctl = br_i31_add(t[d], t[a], 1);490ctl |= NOT(br_i31_sub(t[d], cc->p, 0));491br_i31_sub(t[d], cc->p, ctl);492break;493case 2:494br_i31_add(t[d], cc->p, br_i31_sub(t[d], t[a], 1));495break;496case 3:497br_i31_montymul(t[d], t[a], t[b], cc->p, cc->p0i);498break;499case 4:500plen = (cc->p[0] - (cc->p[0] >> 5) + 7) >> 3;501br_i31_encode(tp, plen, cc->p);502tp[plen - 1] -= 2;503br_i31_modpow(t[d], tp, plen,504cc->p, cc->p0i, t[a], t[b]);505break;506default:507r &= ~br_i31_iszero(t[d]);508break;509}510}511512/*513* Copy back result.514*/515memcpy(P1->c, t[P1x], 3 * I31_LEN * sizeof(uint32_t));516return r;517}518519static void520set_one(uint32_t *x, const uint32_t *p)521{522size_t plen;523524plen = (p[0] + 63) >> 5;525memset(x, 0, plen * sizeof *x);526x[0] = p[0];527x[1] = 0x00000001;528}529530static void531point_zero(jacobian *P, const curve_params *cc)532{533memset(P, 0, sizeof *P);534P->c[0][0] = P->c[1][0] = P->c[2][0] = cc->p[0];535}536537static inline void538point_double(jacobian *P, const curve_params *cc)539{540run_code(P, P, cc, code_double);541}542543static inline uint32_t544point_add(jacobian *P1, const jacobian *P2, const curve_params *cc)545{546return run_code(P1, P2, cc, code_add);547}548549static void550point_mul(jacobian *P, const unsigned char *x, size_t xlen,551const curve_params *cc)552{553/*554* We do a simple double-and-add ladder with a 2-bit window555* to make only one add every two doublings. We thus first556* precompute 2P and 3P in some local buffers.557*558* We always perform two doublings and one addition; the559* addition is with P, 2P and 3P and is done in a temporary560* array.561*562* The addition code cannot handle cases where one of the563* operands is infinity, which is the case at the start of the564* ladder. We therefore need to maintain a flag that controls565* this situation.566*/567uint32_t qz;568jacobian P2, P3, Q, T, U;569570memcpy(&P2, P, sizeof P2);571point_double(&P2, cc);572memcpy(&P3, P, sizeof P3);573point_add(&P3, &P2, cc);574575point_zero(&Q, cc);576qz = 1;577while (xlen -- > 0) {578int k;579580for (k = 6; k >= 0; k -= 2) {581uint32_t bits;582uint32_t bnz;583584point_double(&Q, cc);585point_double(&Q, cc);586memcpy(&T, P, sizeof T);587memcpy(&U, &Q, sizeof U);588bits = (*x >> k) & (uint32_t)3;589bnz = NEQ(bits, 0);590CCOPY(EQ(bits, 2), &T, &P2, sizeof T);591CCOPY(EQ(bits, 3), &T, &P3, sizeof T);592point_add(&U, &T, cc);593CCOPY(bnz & qz, &Q, &T, sizeof Q);594CCOPY(bnz & ~qz, &Q, &U, sizeof Q);595qz &= ~bnz;596}597x ++;598}599memcpy(P, &Q, sizeof Q);600}601602/*603* Decode point into Jacobian coordinates. This function does not support604* the point at infinity. If the point is invalid then this returns 0, but605* the coordinates are still set to properly formed field elements.606*/607static uint32_t608point_decode(jacobian *P, const void *src, size_t len, const curve_params *cc)609{610/*611* Points must use uncompressed format:612* -- first byte is 0x04;613* -- coordinates X and Y use unsigned big-endian, with the same614* length as the field modulus.615*616* We don't support hybrid format (uncompressed, but first byte617* has value 0x06 or 0x07, depending on the least significant bit618* of Y) because it is rather useless, and explicitly forbidden619* by PKIX (RFC 5480, section 2.2).620*621* We don't support compressed format either, because it is not622* much used in practice (there are or were patent-related623* concerns about point compression, which explains the lack of624* generalised support). Also, point compression support would625* need a bit more code.626*/627const unsigned char *buf;628size_t plen, zlen;629uint32_t r;630jacobian Q;631632buf = src;633point_zero(P, cc);634plen = (cc->p[0] - (cc->p[0] >> 5) + 7) >> 3;635if (len != 1 + (plen << 1)) {636return 0;637}638r = br_i31_decode_mod(P->c[0], buf + 1, plen, cc->p);639r &= br_i31_decode_mod(P->c[1], buf + 1 + plen, plen, cc->p);640641/*642* Check first byte.643*/644r &= EQ(buf[0], 0x04);645/* obsolete646r &= EQ(buf[0], 0x04) | (EQ(buf[0] & 0xFE, 0x06)647& ~(uint32_t)(buf[0] ^ buf[plen << 1]));648*/649650/*651* Convert coordinates and check that the point is valid.652*/653zlen = ((cc->p[0] + 63) >> 5) * sizeof(uint32_t);654memcpy(Q.c[0], cc->R2, zlen);655memcpy(Q.c[1], cc->b, zlen);656set_one(Q.c[2], cc->p);657r &= ~run_code(P, &Q, cc, code_check);658return r;659}660661/*662* Encode a point. This method assumes that the point is correct and is663* not the point at infinity. Encoded size is always 1+2*plen, where664* plen is the field modulus length, in bytes.665*/666static void667point_encode(void *dst, const jacobian *P, const curve_params *cc)668{669unsigned char *buf;670uint32_t xbl;671size_t plen;672jacobian Q, T;673674buf = dst;675xbl = cc->p[0];676xbl -= (xbl >> 5);677plen = (xbl + 7) >> 3;678buf[0] = 0x04;679memcpy(&Q, P, sizeof *P);680set_one(T.c[2], cc->p);681run_code(&Q, &T, cc, code_affine);682br_i31_encode(buf + 1, plen, Q.c[0]);683br_i31_encode(buf + 1 + plen, plen, Q.c[1]);684}685686static const br_ec_curve_def *687id_to_curve_def(int curve)688{689switch (curve) {690case BR_EC_secp256r1:691return &br_secp256r1;692case BR_EC_secp384r1:693return &br_secp384r1;694case BR_EC_secp521r1:695return &br_secp521r1;696}697return NULL;698}699700static const unsigned char *701api_generator(int curve, size_t *len)702{703const br_ec_curve_def *cd;704705cd = id_to_curve_def(curve);706*len = cd->generator_len;707return cd->generator;708}709710static const unsigned char *711api_order(int curve, size_t *len)712{713const br_ec_curve_def *cd;714715cd = id_to_curve_def(curve);716*len = cd->order_len;717return cd->order;718}719720static size_t721api_xoff(int curve, size_t *len)722{723api_generator(curve, len);724*len >>= 1;725return 1;726}727728static uint32_t729api_mul(unsigned char *G, size_t Glen,730const unsigned char *x, size_t xlen, int curve)731{732uint32_t r;733const curve_params *cc;734jacobian P;735736cc = id_to_curve(curve);737if (Glen != cc->point_len) {738return 0;739}740r = point_decode(&P, G, Glen, cc);741point_mul(&P, x, xlen, cc);742point_encode(G, &P, cc);743return r;744}745746static size_t747api_mulgen(unsigned char *R,748const unsigned char *x, size_t xlen, int curve)749{750const unsigned char *G;751size_t Glen;752753G = api_generator(curve, &Glen);754memcpy(R, G, Glen);755api_mul(R, Glen, x, xlen, curve);756return Glen;757}758759static uint32_t760api_muladd(unsigned char *A, const unsigned char *B, size_t len,761const unsigned char *x, size_t xlen,762const unsigned char *y, size_t ylen, int curve)763{764uint32_t r, t, z;765const curve_params *cc;766jacobian P, Q;767768/*769* TODO: see about merging the two ladders. Right now, we do770* two independent point multiplications, which is a bit771* wasteful of CPU resources (but yields short code).772*/773774cc = id_to_curve(curve);775if (len != cc->point_len) {776return 0;777}778r = point_decode(&P, A, len, cc);779if (B == NULL) {780size_t Glen;781782B = api_generator(curve, &Glen);783}784r &= point_decode(&Q, B, len, cc);785point_mul(&P, x, xlen, cc);786point_mul(&Q, y, ylen, cc);787788/*789* We want to compute P+Q. Since the base points A and B are distinct790* from infinity, and the multipliers are non-zero and lower than the791* curve order, then we know that P and Q are non-infinity. This792* leaves two special situations to test for:793* -- If P = Q then we must use point_double().794* -- If P+Q = 0 then we must report an error.795*/796t = point_add(&P, &Q, cc);797point_double(&Q, cc);798z = br_i31_iszero(P.c[2]);799800/*801* If z is 1 then either P+Q = 0 (t = 1) or P = Q (t = 0). So we802* have the following:803*804* z = 0, t = 0 return P (normal addition)805* z = 0, t = 1 return P (normal addition)806* z = 1, t = 0 return Q (a 'double' case)807* z = 1, t = 1 report an error (P+Q = 0)808*/809CCOPY(z & ~t, &P, &Q, sizeof Q);810point_encode(A, &P, cc);811r &= ~(z & t);812813return r;814}815816/* see bearssl_ec.h */817const br_ec_impl br_ec_prime_i31 = {818(uint32_t)0x03800000,819&api_generator,820&api_order,821&api_xoff,822&api_mul,823&api_mulgen,824&api_muladd825};826827828