From aee59e3e5b86b88c8811803e883e4c9365c83a08 Mon Sep 17 00:00:00 2001 From: Eric Lombrozo Date: Sun, 17 Mar 2013 22:57:55 -0700 Subject: [PATCH 1/3] Added constant time Normalize operation to FieldElem class. --- field.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/field.cpp b/field.cpp index 5f7714cd8b..91e9b0ddcd 100644 --- a/field.cpp +++ b/field.cpp @@ -8,6 +8,7 @@ using namespace std; #include "field.h" // #define VERIFY_MAGNITUDE 1 +#define CONSTANT_TIME namespace secp256k1 { @@ -33,6 +34,7 @@ FieldElem::FieldElem(const unsigned char *b32) { void FieldElem::Normalize() { uint64_t c; +#ifndef CONSTANT_TIME if (n[0] > 0xFFFFFFFFFFFFFULL || n[1] > 0xFFFFFFFFFFFFFULL || n[2] > 0xFFFFFFFFFFFFFULL || n[3] > 0xFFFFFFFFFFFFFULL || n[4] > 0xFFFFFFFFFFFFULL) { c = n[0]; uint64_t t0 = c & 0xFFFFFFFFFFFFFULL; @@ -67,6 +69,45 @@ void FieldElem::Normalize() { n[1] = 0; n[0] -= 0xFFFFEFFFFFC2FULL; } +#else + c = n[0]; + uint64_t t0 = c & 0xFFFFFFFFFFFFFULL; + c = (c >> 52) + n[1]; + uint64_t t1 = c & 0xFFFFFFFFFFFFFULL; + c = (c >> 52) + n[2]; + uint64_t t2 = c & 0xFFFFFFFFFFFFFULL; + c = (c >> 52) + n[3]; + uint64_t t3 = c & 0xFFFFFFFFFFFFFULL; + c = (c >> 52) + n[4]; + uint64_t t4 = c & 0x0FFFFFFFFFFFFULL; + c >>= 48; + + // The following code will not modify the t's if c is initially 0. + c = c * 0x1000003D1ULL + t0; + t0 = c & 0xFFFFFFFFFFFFFULL; + c = (c >> 52) + t1; + t1 = c & 0xFFFFFFFFFFFFFULL; + c = (c >> 52) + t2; + t2 = c & 0xFFFFFFFFFFFFFULL; + c = (c >> 52) + t3; + t3 = c & 0xFFFFFFFFFFFFFULL; + c = (c >> 52) + t4; + t4 = c & 0x0FFFFFFFFFFFFULL; + + // Replace n's with t's if one of the n's overflows. + // If none of the n's overflow to begin with, the t's will just be the n's already and + // we effectively ignore the results of the previous computations. + n[0] = t0; n[1] = t1; n[2] = t2; n[3] = t3; n[4] = t4; + + // Subtract p if result >= p + uint64_t mask = (uint64_t)~(-(n[4] == 0xFFFFFFFFFFFFULL && n[3] == 0xFFFFFFFFFFFFFULL && n[2] == 0xFFFFFFFFFFFFFULL && n[1] == 0xFFFFFFFFFFFFF && n[0] >= 0xFFFFEFFFFFC2FULL)); + n[4] &= mask; + n[3] &= mask; + n[2] &= mask; + n[1] &= mask; + n[0] -= (~mask & 0xFFFFEFFFFFC2FULL); +#endif + #ifdef VERIFY_MAGNITUDE magnitude = 1; #endif From b358450114fc442315aa69066f23b07cbffb63e5 Mon Sep 17 00:00:00 2001 From: Eric Lombrozo Date: Mon, 18 Mar 2013 07:31:33 -0700 Subject: [PATCH 2/3] First cast the conditional to an int64 so it works on 32-bit platforms. --- field.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/field.cpp b/field.cpp index 91e9b0ddcd..67a86441f7 100644 --- a/field.cpp +++ b/field.cpp @@ -100,7 +100,7 @@ void FieldElem::Normalize() { n[0] = t0; n[1] = t1; n[2] = t2; n[3] = t3; n[4] = t4; // Subtract p if result >= p - uint64_t mask = (uint64_t)~(-(n[4] == 0xFFFFFFFFFFFFULL && n[3] == 0xFFFFFFFFFFFFFULL && n[2] == 0xFFFFFFFFFFFFFULL && n[1] == 0xFFFFFFFFFFFFF && n[0] >= 0xFFFFEFFFFFC2FULL)); + uint64_t mask = (uint64_t)~(-(int64_t)(n[4] == 0xFFFFFFFFFFFFULL && n[3] == 0xFFFFFFFFFFFFFULL && n[2] == 0xFFFFFFFFFFFFFULL && n[1] == 0xFFFFFFFFFFFFF && n[0] >= 0xFFFFEFFFFFC2FULL)); n[4] &= mask; n[3] &= mask; n[2] &= mask; From 8803181c6178a39129b15f21f70aaa2553f56c49 Mon Sep 17 00:00:00 2001 From: Eric Lombrozo Date: Tue, 19 Mar 2013 10:21:46 -0700 Subject: [PATCH 3/3] Added constant time methods to FieldElem class. --- field.cpp | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++---- field.h | 10 ++++++++++ 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/field.cpp b/field.cpp index 67a86441f7..67ee38a128 100644 --- a/field.cpp +++ b/field.cpp @@ -33,8 +33,11 @@ FieldElem::FieldElem(const unsigned char *b32) { } void FieldElem::Normalize() { +#ifdef CONSTANT_TIME + NormalizeCT(); + return; +#else uint64_t c; -#ifndef CONSTANT_TIME if (n[0] > 0xFFFFFFFFFFFFFULL || n[1] > 0xFFFFFFFFFFFFFULL || n[2] > 0xFFFFFFFFFFFFFULL || n[3] > 0xFFFFFFFFFFFFFULL || n[4] > 0xFFFFFFFFFFFFULL) { c = n[0]; uint64_t t0 = c & 0xFFFFFFFFFFFFFULL; @@ -69,7 +72,14 @@ void FieldElem::Normalize() { n[1] = 0; n[0] -= 0xFFFFEFFFFFC2FULL; } -#else +#ifdef VERIFY_MAGNITUDE + magnitude = 1; +#endif +#endif +} + +void FieldElem::NormalizeCT() { + uint64_t c; c = n[0]; uint64_t t0 = c & 0xFFFFFFFFFFFFFULL; c = (c >> 52) + n[1]; @@ -106,8 +116,7 @@ void FieldElem::Normalize() { n[2] &= mask; n[1] &= mask; n[0] -= (~mask & 0xFFFFEFFFFFC2FULL); -#endif - + #ifdef VERIFY_MAGNITUDE magnitude = 1; #endif @@ -118,12 +127,23 @@ bool inline FieldElem::IsZero() { return (n[0] == 0 && n[1] == 0 && n[2] == 0 && n[3] == 0 && n[4] == 0); } +bool inline FieldElem::IsZeroCT() { + NormalizeCT(); + return (n[0] == 0 && n[1] == 0 && n[2] == 0 && n[3] == 0 && n[4] == 0); +} + bool inline operator==(FieldElem &a, FieldElem &b) { a.Normalize(); b.Normalize(); return (a.n[0] == b.n[0] && a.n[1] == b.n[1] && a.n[2] == b.n[2] && a.n[3] == b.n[3] && a.n[4] == b.n[4]); } +bool inline EqualCT(FieldElem &a, FieldElem &b) { + a.NormalizeCT(); + b.NormalizeCT(); + return (a.n[0] == b.n[0] && a.n[1] == b.n[1] && a.n[2] == b.n[2] && a.n[3] == b.n[3] && a.n[4] == b.n[4]); +} + void FieldElem::GetBytes(unsigned char *o) { Normalize(); for (int i=0; i<32; i++) { @@ -137,6 +157,19 @@ void FieldElem::GetBytes(unsigned char *o) { } } +void FieldElem::GetBytesCT(unsigned char *o) { + NormalizeCT(); + for (int i=0; i<32; i++) { + int c = 0; + for (int j=0; j<2; j++) { + int limb = (8*i+4*j)/52; + int shift = (8*i+4*j)%52; + c |= ((n[limb] >> shift) & 0xF) << (4 * j); + } + o[31-i] = c; + } +} + void FieldElem::SetBytes(const unsigned char *in) { n[0] = n[1] = n[2] = n[3] = n[4] = 0; for (int i=0; i<32; i++) { @@ -329,6 +362,11 @@ bool FieldElem::IsOdd() { return n[0] & 1; } +bool FieldElem::IsOddCT() { + NormalizeCT(); + return n[0] & 1; +} + std::string FieldElem::ToString() { unsigned char tmp[32]; GetBytes(tmp); @@ -341,6 +379,18 @@ std::string FieldElem::ToString() { return ret; } +std::string FieldElem::ToStringCT() { + unsigned char tmp[32]; + GetBytesCT(tmp); + std::string ret; + for (int i=0; i<32; i++) { + static const char *c = "0123456789ABCDEF"; + ret += c[(tmp[i] >> 4) & 0xF]; + ret += c[(tmp[i]) & 0xF]; + } + return ret; +} + void FieldElem::SetHex(const std::string &str) { unsigned char tmp[32] = {}; static const int cvt[256] = {0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,0,0, @@ -378,6 +428,7 @@ const FieldConstants &GetFieldConst() { return field_const; } +// Nonbuiltin Field Inverse is not constant time. void FieldElem::SetInverse(FieldElem &a) { #if defined(USE_FIELDINVERSE_BUILTIN) // calculate a^p, with p={45,63,1019,1023} diff --git a/field.h b/field.h index 0584dbabda..c413b9875f 100644 --- a/field.h +++ b/field.h @@ -18,6 +18,10 @@ namespace secp256k1 { * is at most M*(2^53-1), except the most significant one, which is limited to M*(2^49-1). All operations * accept any input with magnitude at most M, and have different rules for propagating magnitude to their * output. + * + * There are two implementations of the Normalize method, NormalizeCT executing in constant time. + * Similarly, a second version of all the other methods that call Normalize have been added which + * NormalizeCT instead to prevent sidechannel leaks. */ class FieldElem { private: @@ -36,13 +40,17 @@ public: /** Normalizes the internal representation entries. Magnitude=1 */ void Normalize(); + void NormalizeCT(); bool IsZero(); + bool IsZeroCT(); bool friend operator==(FieldElem &a, FieldElem &b); + bool friend EqualCT(FieldElem &a, FieldElem &b); /** extract as 32-byte big endian array */ void GetBytes(unsigned char *o); + void GetBytesCT(unsigned char *o); /** set value of 32-byte big endian array */ void SetBytes(const unsigned char *in); @@ -65,11 +73,13 @@ public: void SetSquareRoot(const FieldElem &a); bool IsOdd(); + bool IsOddCT(); /** Set this to be the (modular) inverse of another FieldElem. Magnitude=1 */ void SetInverse(FieldElem &a); std::string ToString(); + std::string ToStringCT(); void SetHex(const std::string &str); };