/* -- 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; }
/* * 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); }
/* Return R = a*P where P is curve25519 base point */ void x25519_BasePointMultiply(OUT U8 *r, IN const U8 *sk) { Ext_POINT S; U_WORD t[K_WORDS]; ecp_BytesToWords(t, sk); edp_BasePointMult(&S, t, edp_custom_blinding.zr); /* Convert ed25519 point to x25519 point */ /* u = (1 + y)/(1 - y) = (Z + Y)/(Z - Y) */ ecp_AddReduce(S.t, S.z, S.y); ecp_SubReduce(S.z, S.z, S.y); ecp_Inverse(S.z, S.z); ecp_MulMod(S.t, S.t, S.z); ecp_WordsToBytes(r, S.t); }
/* 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); }
int curve25519_SelfTest(int level) { int rc = 0; U64 A[4], B[4], C[4]; U8 a[32], b[32], c[32], d[32]; ecp_AddReduce(A, _w_I, _w_P); ECP_MOD(A); if (ecp_Cmp(A, _w_I) != 0) { rc++; printf("assert I+p == I mod p FAILED!!\n"); ecp_PrintHexWords("A_1", A, 4); } if (ecp_Cmp(_w_I, _w_P) >= 0) { rc++; printf("assert I < P FAILED!!\n"); } if (ecp_Cmp(_w_P, _w_I) <= 0) { rc++; printf("assert P > I FAILED!!\n"); } ecp_MulReduce(B, _w_I, _w_D); ECP_MOD(B); if (ecp_Cmp(B, _w_IxD) != 0) { rc++; printf("assert I*D FAILED!!\n"); ecp_PrintHexWords("A_2", B, 4); } // assert I*I == p-1 ecp_MulMod(A, _w_I, _w_I); if (ecp_Cmp(A, _w_Pm1) != 0) { rc++; printf("assert mul(I,I) == p-1 FAILED!!\n"); ecp_PrintHexWords("A_3", A, 4); } // assert I**2 == p-1 ecp_SqrReduce(B, _w_I); ECP_MOD(B); if (ecp_Cmp(B, _w_Pm1) != 0) { rc++; printf("assert square(I) == p-1 FAILED!!\n"); ecp_PrintHexWords("B_4", B, 4); } // assert (-I)*(-I) == p-1 ecp_Sub(B, _w_P, _w_I); ecp_MulMod(A, B, B); if (ecp_Cmp(A, _w_Pm1) != 0) { rc++; printf("assert mul(-I,-I) == p-1 FAILED!!\n"); ecp_PrintHexWords("A_5", A, 4); ecp_PrintHexWords("B_5", B, 4); } ecp_SetValue(A, 50153); ecp_Inverse(B, A); ecp_MulMod(A, A, B); if (ecp_Cmp(A, _w_One) != 0) { rc++; printf("invmod FAILED!!\n"); ecp_PrintHexWords("inv_50153", B, 4); ecp_PrintHexWords("expected_1", A, 4); } // assert expmod(d,(p-1)/2,p) == p-1 ecp_ExpMod(A, _w_D, _b_Pm1d2, 32); if (ecp_Cmp(A, _w_Pm1) != 0) { rc++; printf("assert expmod(d,(p-1)/2,p) == p-1 FAILED!!\n"); ecp_PrintHexWords("A_3", A, 4); } ecp_CalculateY(a, ecp_BasePoint); ecp_BytesToWords(A, a); if (ecp_Cmp(A, _w_Gy) != 0) { rc++; printf("assert clacY(Base) == Base.y FAILED!!\n"); ecp_PrintHexBytes("Calculated_Base.y", a, 32); } ecp_PointMultiply(a, ecp_BasePoint, _b_Om1, 32); if (memcmp(a, ecp_BasePoint, 32) != 0) { rc++; printf("assert (l-1).Base == Base FAILED!!\n"); ecp_PrintHexBytes("A_5", a, 32); } ecp_PointMultiply(a, ecp_BasePoint, _b_O, 32); ecp_BytesToWords(A, a); if (!ecp_IsZero(A)) { rc++; printf("assert l.Base == 0 FAILED!!\n"); ecp_PrintHexBytes("A_6", a, 32); } // Key generation ecp_PointMultiply(a, ecp_BasePoint, pk1, 32); ecp_PrintHexBytes("PublicKey1", a, 32); ecp_PointMultiply(b, ecp_BasePoint, pk2, 32); ecp_PrintHexBytes("PublicKey2", b, 32); // ECDH - key exchange ecp_PointMultiply(c, b, pk1, 32); ecp_PrintHexBytes("SharedKey1", c, 32); ecp_PointMultiply(d, a, pk2, 32); ecp_PrintHexBytes("SharedKey2", d, 32); if (memcmp(c, d, 32) != 0) { rc++; printf("ECDH key exchange FAILED!!\n"); } memset(a, 0x44, 32); // our secret key ecp_PointMultiply(b, ecp_BasePoint, a, 32); // public key ecp_PointMultiply(c, b, _b_k1, 32); ecp_PointMultiply(d, c, _b_k2, 32); if (memcmp(d, b, 32) != 0) { rc++; printf("assert k1.k2.D == D FAILED!!\n"); ecp_PrintHexBytes("D", d, 4); ecp_PrintHexBytes("C", c, 4); ecp_PrintHexBytes("A", a, 4); } ecp_BytesToWords(A, _b_k1); ecp_BytesToWords(B, _b_k2); eco_InvModBPO(C, A); if (ecp_Cmp(C, B) != 0) { rc++; printf("assert 1/k1 == k2 mod BPO FAILED!!\n"); ecp_PrintHexWords("Calc", C, 4); ecp_PrintHexWords("Expt", B, 4); } eco_MulMod(C, A, B); if (ecp_Cmp(C, _w_One) != 0) { rc++; printf("assert k1*k2 == 1 mod BPO FAILED!!\n"); ecp_PrintHexWords("Calc", C, 4); } return rc; }
/* K in a little-endian byte array */ void ecp_PointMultiply( OUT U8 *PublicKey, IN const U8 *BasePoint, IN const U8 *SecretKey, IN int len) { int i, j, k; U_WORD X[K_WORDS]; XZ_POINT P, Q, *PP[2], *QP[2]; ecp_BytesToWords(X, BasePoint); /* 1: P = (2k+1)G, Q = (2k+2)G */ /* 0: Q = (2k+1)G, P = (2k)G */ /* Find first non-zero bit */ while (len-- > 0) { k = SecretKey[len]; for (i = 0; i < 8; i++, k <<= 1) { /* P = kG, Q = (k+1)G */ if (k & 0x80) { /* We have first non-zero bit // This is always bit 254 for keys created according to the spec. // Start with randomized base point */ ecp_Add(P.Z, X, edp_custom_blinding.zr); /* P.Z = random */ ecp_MulReduce(P.X, X, P.Z); ecp_MontDouble(&Q, &P); PP[1] = &P; PP[0] = &Q; QP[1] = &Q; QP[0] = &P; /* Everything we reference in the below loop are on the stack // and already touched (cached) */ while (++i < 8) { k <<= 1; ECP_MONT(7); } while (len > 0) { k = SecretKey[--len]; ECP_MONT(7); ECP_MONT(6); ECP_MONT(5); ECP_MONT(4); ECP_MONT(3); ECP_MONT(2); ECP_MONT(1); ECP_MONT(0); } ecp_Inverse(Q.Z, P.Z); ecp_MulMod(X, P.X, Q.Z); ecp_WordsToBytes(PublicKey, X); return; } } } /* K is 0 */ mem_fill(PublicKey, 0, 32); }