/* The MIT License (MIT) * * Copyright (c) 2015 mehdi sotoodeh * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "../include/external_calls.h" #include "curve25519_mehdi.h" #include "../include/ed25519_signature.h" #include "sha512.h" /* * Arithmetic on twisted Edwards curve y^2 - x^2 = 1 + dx^2y^2 * with d = -(121665/121666) mod p * d = 0x52036CEE2B6FFE738CC740797779E89800700A4D4141D8AB75EB4DCA135978A3 * p = 2**255 - 19 * p = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED * Base point: y=4/5 mod p * x = 0x216936D3CD6E53FEC0A4E231FDD6DC5C692CC7609525A7B2C9562D608F25D51A * y = 0x6666666666666666666666666666666666666666666666666666666666666658 * Base point order: * l = 2**252 + 27742317777372353535851937790883648493 * l = 0x1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED */ extern const U_WORD _w_maxP[K_WORDS]; extern const U_WORD _w_NxBPO[16][K_WORDS]; #define _w_BPO _w_NxBPO[1] /* // -- custom blind --------------------------------------------------------- // // edp_custom_blinding is defined in source/custom_blind.c // source/custom_blind is created randomly on every new build // // ------------------------------------------------------------------------- */ extern EDP_BLINDING_CTX edp_custom_blinding; const U_WORD _w_2d[K_WORDS] = /* 2*d */ W256(0x26B2F159,0xEBD69B94,0x8283B156,0x00E0149A,0xEEF3D130,0x198E80F2,0x56DFFCE7,0x2406D9DC); const U_WORD _w_di[K_WORDS] = /* 1/d */ W256(0xCDC9F843,0x25E0F276,0x4279542E,0x0B5DD698,0xCDB9CF66,0x2B162114,0x14D5CE43,0x40907ED2); #include "base_folding8.h" /* Reference: http://eprint.iacr.org/2008/522 Cost: 7M + 7add Return: R = P + BasePoint */ void edp_AddBasePoint(Ext_POINT *p) { U_WORD a[K_WORDS], b[K_WORDS], c[K_WORDS], d[K_WORDS], e[K_WORDS]; ecp_SubReduce(a, p->y, p->x); /* A = (Y1-X1)*(Y2-X2) */ ecp_MulReduce(a, a, _w_base_folding8[1].YmX); ecp_AddReduce(b, p->y, p->x); /* B = (Y1+X1)*(Y2+X2) */ ecp_MulReduce(b, b, _w_base_folding8[1].YpX); ecp_MulReduce(c, p->t, _w_base_folding8[1].T2d); /* C = T1*2d*T2 */ ecp_AddReduce(d, p->z, p->z); /* D = 2*Z1 */ ecp_SubReduce(e, b, a); /* E = B-A */ ecp_AddReduce(b, b, a); /* H = B+A */ ecp_SubReduce(a, d, c); /* F = D-C */ ecp_AddReduce(d, d, c); /* G = D+C */ ecp_MulReduce(p->x, e, a); /* E*F */ ecp_MulReduce(p->y, b, d); /* H*G */ ecp_MulReduce(p->t, e, b); /* E*H */ ecp_MulReduce(p->z, d, a); /* G*F */ } /* Assumptions: pre-computed q, q->Z=1 Cost: 7M + 7add Return: P = P + Q */ void edp_AddAffinePoint(Ext_POINT *p, const PA_POINT *q) { U_WORD a[K_WORDS], b[K_WORDS], c[K_WORDS], d[K_WORDS], e[K_WORDS]; ecp_SubReduce(a, p->y, p->x); /* A = (Y1-X1)*(Y2-X2) */ ecp_MulReduce(a, a, q->YmX); ecp_AddReduce(b, p->y, p->x); /* B = (Y1+X1)*(Y2+X2) */ ecp_MulReduce(b, b, q->YpX); ecp_MulReduce(c, p->t, q->T2d); /* C = T1*2d*T2 */ ecp_AddReduce(d, p->z, p->z); /* D = Z1*2*Z2 (Z2=1)*/ ecp_SubReduce(e, b, a); /* E = B-A */ ecp_AddReduce(b, b, a); /* H = B+A */ ecp_SubReduce(a, d, c); /* F = D-C */ ecp_AddReduce(d, d, c); /* G = D+C */ ecp_MulReduce(p->x, e, a); /* E*F */ ecp_MulReduce(p->y, b, d); /* H*G */ ecp_MulReduce(p->t, e, b); /* E*H */ ecp_MulReduce(p->z, d, a); /* G*F */ } /* Reference: http://eprint.iacr.org/2008/522 Cost: 4M + 4S + 7add Return: P = 2*P */ void edp_DoublePoint(Ext_POINT *p) { U_WORD a[K_WORDS], b[K_WORDS], c[K_WORDS], d[K_WORDS], e[K_WORDS]; ecp_SqrReduce(a, p->x); /* A = X1^2 */ ecp_SqrReduce(b, p->y); /* B = Y1^2 */ ecp_SqrReduce(c, p->z); /* C = 2*Z1^2 */ ecp_AddReduce(c, c, c); ecp_SubReduce(d, _w_maxP, a); /* D = -A */ ecp_SubReduce(a, d, b); /* H = D-B */ ecp_AddReduce(d, d, b); /* G = D+B */ ecp_SubReduce(b, d, c); /* F = G-C */ ecp_AddReduce(e, p->x, p->y); /* E = (X1+Y1)^2-A-B = (X1+Y1)^2+H */ ecp_SqrReduce(e, e); ecp_AddReduce(e, e, a); ecp_MulReduce(p->x, e, b); /* E*F */ ecp_MulReduce(p->y, a, d); /* H*G */ ecp_MulReduce(p->z, d, b); /* G*F */ ecp_MulReduce(p->t, e, a); /* E*H */ } /* -- FOLDING --------------------------------------------------------------- // // The performance boost is achieved by a process that I call it FOLDING. // Folding can be viewed as an extension of Shamir's trick but it is based // on break down of the scalar multiplier of a*P into a polynomial of the // form: // // a*P = SUM(a_i*2^(i*w))*P for i = 0,1,2,...n-1 // // a*P = SUM(a_i*P_i) // // where P_i = (2^(i*w))*P // n = number of folds // w = bit-length of a_i // // For folding of 8, 256-bit multiplier 'a' is chopped into 8 limbs of // 32-bits each (a_0, a_1,...a_7). P_0 - P_7 can be pre-calculated and // their 256-different permutations can be cached or hard-coded // directly into the code. // This arrangement combined with double-and-add approach reduces the // number of EC point calculations by a factor of 8. We only need 31 // double & add operations. // // +---+---+---+---+---+---+- .... -+---+---+---+---+---+---+ // a = (|255|254|253|252|251|250| | 5 | 4 | 3 | 2 | 1 | 0 |) // +---+---+---+---+---+---+- .... -+---+---+---+---+---+---+ // // a_i P_i // +---+---+---+ .... -+---+---+---+ ---------- // a7 = (|255|254|253| |226|225|224|) * (2**224)*P // +---+---+---+ .... -+---+---+---+ // a6 = (|225|224|223| |194|193|192|) * (2**192)*P // +---+---+---+ .... -+---+---+---+ // a5 = (|191|190|189| |162|161|160|) * (2**160)*P // +---+---+---+ .... -+---+---+---+ // a4 = (|159|158|157| |130|129|128|) * (2**128)*P // +---+---+---+ .... -+---+---+---+ // a3 = (|127|126|125| | 98| 97| 96|) * (2**96)*P // +---+---+---+ .... -+---+---+---+ // a2 = (| 95| 94| 93| | 66| 65| 64|) * (2**64)*P // +---+---+---+ .... -+---+---+---+ // a1 = (| 63| 62| 61| | 34| 33| 32|) * (2**32)*P // +---+---+---+ .... -+---+---+---+ // a0 = (| 31| 30| 29| | 2 | 1 | 0 |) * (2**0)*P // +---+---+---+ .... -+---+---+---+ // | | | | // | +--+ | +--+ // | | | | // V V slices V V // +---+ +---+ .... +---+ +---+ // |255| |254| |225| |224| P7 // +---+ +---+ .... +---+ +---+ // |225| |224| |193| |192| P6 // +---+ +---+ .... +---+ +---+ // |191| |190| |161| |160| P5 // +---+ +---+ .... +---+ +---+ // |159| |158| |129| |128| P4 // +---+ +---+ .... +---+ +---+ // |127| |126| | 97| | 96| P3 // +---+ +---+ .... +---+ +---+ // | 95| | 94| | 65| | 64| P2 // +---+ +---+ .... +---+ +---+ // | 63| | 62| | 33| | 32| P1 // +---+ +---+ .... +---+ +---+ // | 31| | 30| | 1 | | 0 | P0 // +---+ +---+ .... +---+ +---+ // cut[]: 0 1 .... 30 31 // -------------------------------------------------------------------------- // Return S = a*P where P is ed25519 base point and R is random */ void edp_BasePointMult( OUT Ext_POINT *S, IN const U_WORD *sk, IN const U_WORD *R) { int i = 1; U8 cut[32]; const PA_POINT *p0; ecp_8Folds(cut, sk); p0 = &_w_base_folding8[cut[0]]; ecp_SubReduce(S->x, p0->YpX, p0->YmX); /* 2x */ ecp_AddReduce(S->y, p0->YpX, p0->YmX); /* 2y */ ecp_MulReduce(S->t, p0->T2d, _w_di); /* 2xy */ /* Randomize starting point */ ecp_AddReduce(S->z, R, R); /* Z = 2R */ ecp_MulReduce(S->x, S->x, R); /* X = 2xR */ ecp_MulReduce(S->t, S->t, R); /* T = 2xyR */ ecp_MulReduce(S->y, S->y, R); /* Y = 2yR */ do { edp_DoublePoint(S); edp_AddAffinePoint(S, &_w_base_folding8[cut[i]]); } while (i++ < 31); } void edp_BasePointMultiply( OUT Affine_POINT *R, IN const U_WORD *sk, IN const void *blinding) { Ext_POINT S; U_WORD t[K_WORDS]; if (blinding) { eco_AddReduce(t, sk, ((EDP_BLINDING_CTX*)blinding)->bl); edp_BasePointMult(&S, t, ((EDP_BLINDING_CTX*)blinding)->zr); edp_AddPoint(&S, &S, &((EDP_BLINDING_CTX*)blinding)->BP); } else { edp_BasePointMult(&S, sk, edp_custom_blinding.zr); } ecp_Inverse(S.z, S.z); ecp_MulMod(R->x, S.x, S.z); ecp_MulMod(R->y, S.y, S.z); } void edp_ExtPoint2PE(PE_POINT *r, const Ext_POINT *p) { ecp_AddReduce(r->YpX, p->y, p->x); ecp_SubReduce(r->YmX, p->y, p->x); ecp_MulReduce(r->T2d, p->t, _w_2d); ecp_AddReduce(r->Z2, p->z, p->z); } /* -- Blinding ------------------------------------------------------------- // // Blinding is a measure to protect against side channel attacks. // Blinding randomizes the scalar multiplier. // // Instead of calculating a*P, calculate (a+b mod BPO)*P + B // // Where b = random blinding and B = -b*P // // ------------------------------------------------------------------------- */ void *ed25519_Blinding_Init( void *context, /* IO: null or ptr blinding context */ const unsigned char *seed, /* IN: [size bytes] random blinding seed */ size_t size) /* IN: size of blinding seed */ { struct { Ext_POINT T; U_WORD t[K_WORDS]; SHA512_CTX H; U8 digest[SHA512_DIGEST_LENGTH]; } d; EDP_BLINDING_CTX *ctx = (EDP_BLINDING_CTX*)context; if (ctx == 0) { ctx = (EDP_BLINDING_CTX*)mem_alloc(sizeof(EDP_BLINDING_CTX)); if (ctx == 0) return 0; } /* Use edp_custom_blinding to protect generation of the new blinder */ SHA512_Init(&d.H); SHA512_Update(&d.H, edp_custom_blinding.zr, 32); SHA512_Update(&d.H, seed, size); SHA512_Final(d.digest, &d.H); ecp_BytesToWords(ctx->zr, d.digest+32); ecp_BytesToWords(d.t, d.digest); eco_Mod(d.t); ecp_Sub(ctx->bl, _w_BPO, d.t); eco_AddReduce(d.t, d.t, edp_custom_blinding.bl); edp_BasePointMult(&d.T, d.t, edp_custom_blinding.zr); edp_AddPoint(&d.T, &d.T, &edp_custom_blinding.BP); edp_ExtPoint2PE(&ctx->BP, &d.T); /* clear potentially sensitive data */ mem_clear (&d, sizeof(d)); return ctx; } void ed25519_Blinding_Finish( void *context) /* IN: blinding context */ { if (context) { mem_clear (context, sizeof(EDP_BLINDING_CTX)); mem_free (context); } } /* Generate public and private key pair associated with the secret key */ void ed25519_CreateKeyPair( unsigned char *pubKey, /* OUT: public key */ unsigned char *privKey, /* OUT: private key */ const void *blinding, /* IN: [optional] null or blinding context */ const unsigned char *sk) /* IN: secret key (32 bytes) */ { U8 md[SHA512_DIGEST_LENGTH]; U_WORD t[K_WORDS]; SHA512_CTX H; Affine_POINT Q; /* [a:b] = H(sk) */ SHA512_Init(&H); SHA512_Update(&H, sk, 32); SHA512_Final(md, &H); ecp_TrimSecretKey(md); ecp_BytesToWords(t, md); edp_BasePointMultiply(&Q, t, blinding); ed25519_PackPoint(pubKey, Q.y, Q.x[0]); memcpy(privKey, sk, 32); memcpy(privKey+32, pubKey, 32); } /* * Generate message signature */ void ed25519_SignMessage( unsigned char *signature, /* OUT: [64 bytes] signature (R,S) */ const unsigned char *privKey, /* IN: [64 bytes] private key (sk,pk) */ const void *blinding, /* IN: [optional] null or blinding context */ const unsigned char *msg, /* IN: [msg_size bytes] message to sign */ size_t msg_size) { SHA512_CTX H; Affine_POINT R; U_WORD a[K_WORDS], t[K_WORDS], r[K_WORDS]; U8 md[SHA512_DIGEST_LENGTH]; /* [a:b] = H(sk) */ SHA512_Init(&H); SHA512_Update(&H, privKey, 32); SHA512_Final(md, &H); ecp_TrimSecretKey(md); /* a = first 32 bytes */ ecp_BytesToWords(a, md); /* r = H(b + m) mod BPO */ SHA512_Init(&H); SHA512_Update(&H, md+32, 32); SHA512_Update(&H, msg, msg_size); SHA512_Final(md, &H); eco_DigestToWords(r, md); eco_Mod(r); /* r mod BPO */ /* R = r*P */ edp_BasePointMultiply(&R, r, blinding); ed25519_PackPoint(signature, R.y, R.x[0]); /* R part of signature */ /* S = r + H(encoded(R) + pk + m) * a mod BPO */ SHA512_Init(&H); SHA512_Update(&H, signature, 32); /* encoded(R) */ SHA512_Update(&H, privKey+32, 32); /* pk */ SHA512_Update(&H, msg, msg_size); /* m */ SHA512_Final(md, &H); eco_DigestToWords(t, md); eco_MulReduce(t, t, a); /* h()*a */ eco_AddReduce(t, t, r); eco_Mod(t); ecp_WordsToBytes(signature+32, t); /* S part of signature */ /* Clear sensitive data */ ecp_SetValue(a, 0); ecp_SetValue(r, 0); }