From c9ef9420e762b91cc06463d349cf06e04c749b9d Mon Sep 17 00:00:00 2001 From: Jeffrey Walton Date: Mon, 5 Aug 2019 03:51:58 -0400 Subject: [PATCH] Fix ECP leakage in Add() and Double() (GH #869, PR #871) This check-in provides the fix for leaks in ECP's Add() and Double(). The fixes were taken from Joost Renes, Craig Costello, and Lejla Batina's [Complete addition formulas for prime order elliptic curves](https://eprint.iacr.org/2015/1060.pdf). The Pull Request includes two additional changes that were related to testing the primary fix. First, an `AuthenticatedKeyAgreementWithRolesValidate` interface was added. It allows us to test key agreement when roles are involved. Roles are "client", "server", "initiator", "recipient", etc. Second, `SetGlobalSeed` was added to `test.cpp` to help with reproducible results. We had code in two different places that set the seed value for the random number generator. But it was sloppy and doing a poor job since results could not be reproduced under some circumstances. --- cryptest.vcxproj.user | 2 +- ec2n.cpp | 11 +- ec2n.h | 1 - eccrypto.cpp | 10 + eccrypto.h | 4 + ecp.cpp | 457 +++++++++++++++++++++++++++++++++++++++--- ecp.h | 36 ++++ fhmqv.h | 38 +--- hmqv.h | 44 ++-- mqv.cpp | 15 +- test.cpp | 77 ++++--- validat6.cpp | 2 +- validat7.cpp | 46 ++++- 13 files changed, 613 insertions(+), 130 deletions(-) diff --git a/cryptest.vcxproj.user b/cryptest.vcxproj.user index cd22e2e2..4b2caa7e 100644 --- a/cryptest.vcxproj.user +++ b/cryptest.vcxproj.user @@ -3,4 +3,4 @@ v - + \ No newline at end of file diff --git a/ec2n.cpp b/ec2n.cpp index 97763232..7b6cf451 100644 --- a/ec2n.cpp +++ b/ec2n.cpp @@ -16,7 +16,8 @@ ANONYMOUS_NAMESPACE_BEGIN using CryptoPP::EC2N; #if defined(HAVE_GCC_INIT_PRIORITY) - const EC2N::Point g_identity __attribute__ ((init_priority (CRYPTOPP_INIT_PRIORITY + 50))) = EC2N::Point(); + #define INIT_ATTRIBUTE __attribute__ ((init_priority (CRYPTOPP_INIT_PRIORITY + 51))) + const EC2N::Point g_identity INIT_ATTRIBUTE = EC2N::Point(); #elif defined(HAVE_MSC_INIT_PRIORITY) #pragma warning(disable: 4075) #pragma init_seg(".CRT$XCU") @@ -51,8 +52,8 @@ void EC2N::DEREncode(BufferedTransformation &bt) const { m_field->DEREncode(bt); DERSequenceEncoder seq(bt); - m_field->DEREncodeElement(seq, m_a); - m_field->DEREncodeElement(seq, m_b); + m_field->DEREncodeElement(seq, m_a); + m_field->DEREncodeElement(seq, m_b); seq.MessageEnd(); } @@ -260,7 +261,7 @@ const EC2N::Point& EC2N::Double(const Point &P) const // ******************************************************** -/* +#if 0 EcPrecomputation& EcPrecomputation::operator=(const EcPrecomputation &rhs) { m_ec = rhs.m_ec; @@ -312,7 +313,7 @@ EC2N::Point EcPrecomputation::CascadeExponentiate(const Integer &exponent, { return m_ep.CascadeExponentiate(exponent, static_cast &>(pc2).m_ep, exponent2); } -*/ +#endif NAMESPACE_END diff --git a/ec2n.h b/ec2n.h index 6806444b..48e9fa28 100644 --- a/ec2n.h +++ b/ec2n.h @@ -3,7 +3,6 @@ /// \file ec2n.h /// \brief Classes for Elliptic Curves over binary fields - #ifndef CRYPTOPP_EC2N_H #define CRYPTOPP_EC2N_H diff --git a/eccrypto.cpp b/eccrypto.cpp index f76c495a..9a0914c0 100644 --- a/eccrypto.cpp +++ b/eccrypto.cpp @@ -28,6 +28,9 @@ #include "ec2n.h" #include "misc.h" +#include +#include + // Squash MS LNK4221 and libtool warnings #ifndef CRYPTOPP_MANUALLY_INSTANTIATE_TEMPLATES extern const char ECCRYPTO_FNAME[] = __FILE__; @@ -683,6 +686,13 @@ OID DL_GroupParameters_EC::GetAlgorithmID() const return ASN1::id_ecPublicKey(); } +std::ostream& operator<<(std::ostream& os, const DL_GroupParameters_EC::Element& obj) +{ + std::ostringstream oss; + oss << "(" << std::hex << obj.x << ", " << std::hex << obj.y << ")"; + return os << oss.str(); +} + // ****************************************************************** template diff --git a/eccrypto.h b/eccrypto.h index 716369a2..7fce9cfa 100644 --- a/eccrypto.h +++ b/eccrypto.h @@ -22,6 +22,8 @@ #include "ecp.h" #include "ec2n.h" +#include + #if CRYPTOPP_MSC_VERSION # pragma warning(push) # pragma warning(disable: 4231 4275) @@ -168,6 +170,8 @@ protected: mutable bool m_compress, m_encodeAsOID; // presentation details }; +inline std::ostream& operator<<(std::ostream& os, const DL_GroupParameters_EC::Element& obj); + /// \brief Elliptic Curve Discrete Log (DL) public key /// \tparam EC elliptic curve field template diff --git a/ecp.cpp b/ecp.cpp index f5aa08e3..5e243a3a 100644 --- a/ecp.cpp +++ b/ecp.cpp @@ -15,10 +15,12 @@ ANONYMOUS_NAMESPACE_BEGIN using CryptoPP::ECP; +using CryptoPP::Integer; using CryptoPP::ModularArithmetic; #if defined(HAVE_GCC_INIT_PRIORITY) - const ECP::Point g_identity __attribute__ ((init_priority (CRYPTOPP_INIT_PRIORITY + 51))) = ECP::Point(); + #define INIT_ATTRIBUTE __attribute__ ((init_priority (CRYPTOPP_INIT_PRIORITY + 50))) + const ECP::Point g_identity INIT_ATTRIBUTE = ECP::Point(); #elif defined(HAVE_MSC_INIT_PRIORITY) #pragma warning(disable: 4075) #pragma init_seg(".CRT$XCU") @@ -39,6 +41,12 @@ inline ECP::Point FromMontgomery(const ModularArithmetic &mr, const ECP::Point & return P.identity ? P : ECP::Point(mr.ConvertOut(P.x), mr.ConvertOut(P.y)); } +template +inline Integer ToInteger(const T& val) +{ + return !!val ? Integer::One() : Integer::Zero(); +} + ANONYMOUS_NAMESPACE_END NAMESPACE_BEGIN(CryptoPP) @@ -243,34 +251,14 @@ const ECP::Point& ECP::Inverse(const Point &P) const const ECP::Point& ECP::Add(const Point &P, const Point &Q) const { - if (P.identity) return Q; - if (Q.identity) return P; - if (GetField().Equal(P.x, Q.x)) - return GetField().Equal(P.y, Q.y) ? Double(P) : Identity(); - - FieldElement t = GetField().Subtract(Q.y, P.y); - t = GetField().Divide(t, GetField().Subtract(Q.x, P.x)); - FieldElement x = GetField().Subtract(GetField().Subtract(GetField().Square(t), P.x), Q.x); - m_R.y = GetField().Subtract(GetField().Multiply(t, GetField().Subtract(P.x, x)), P.y); - - m_R.x.swap(x); - m_R.identity = false; - return m_R; + AdditionFunction add(*this); + return (m_R = add(P, Q)); } const ECP::Point& ECP::Double(const Point &P) const { - if (P.identity || P.y==GetField().Identity()) return Identity(); - - FieldElement t = GetField().Square(P.x); - t = GetField().Add(GetField().Add(GetField().Double(t), t), m_a); - t = GetField().Divide(t, GetField().Double(P.y)); - FieldElement x = GetField().Subtract(GetField().Subtract(GetField().Square(t), P.x), P.x); - m_R.y = GetField().Subtract(GetField().Multiply(t, GetField().Subtract(P.x, x)), P.y); - - m_R.x.swap(x); - m_R.identity = false; - return m_R; + AdditionFunction add(*this); + return (m_R = add(P)); } template void ParallelInvert(const AbstractRing &ring, Iterator begin, Iterator end) @@ -323,7 +311,7 @@ class ProjectiveDoubling { public: ProjectiveDoubling(const ModularArithmetic &m_mr, const Integer &m_a, const Integer &m_b, const ECPPoint &Q) - : mr(m_mr), firstDoubling(true), negated(false) + : mr(m_mr) { CRYPTOPP_UNUSED(m_b); if (Q.identity) @@ -360,7 +348,6 @@ public: const ModularArithmetic &mr; ProjectivePoint P; - bool firstDoubling, negated; Integer sixteenY4, aZ4, twoY, fourY2, S, M; }; @@ -495,6 +482,422 @@ ECP::Point ECP::CascadeScalarMultiply(const Point &P, const Integer &k1, const P return AbstractGroup::CascadeScalarMultiply(P, k1, Q, k2); } +#define X p.x +#define Y p.y +#define Z p.z + +#define X1 p.x +#define Y1 p.y +#define Z1 p.z + +#define X2 q.x +#define Y2 q.y +#define Z2 q.z + +#define X3 r.x +#define Y3 r.y +#define Z3 r.z + +ECP::AdditionFunction::AdditionFunction(const ECP& ecp) + : m_ecp(ecp), m_alpha(static_cast(0)) +{ + if (m_ecp.GetField().IsMontgomeryRepresentation()) + { + m_alpha = A_Montgomery; + } + else + { + if (m_ecp.m_a == 0) + { + m_alpha = A_0; + } + else if (m_ecp.m_a == -3 || (m_ecp.m_a - m_ecp.GetField().GetModulus()) == -3) + { + m_alpha = A_3; + } + else + { + m_alpha = A_Star; + } + } +} + +ECP::Point ECP::AdditionFunction::operator()(const Point& P) const +{ + if (m_alpha == A_3) + { + const ECP::Field& field = m_ecp.GetField(); + const FieldElement& b = m_ecp.m_b; + + // Gyrations attempt to maintain constant-timeness + // We need either (P.x, P.y, 1) or (0, 1, 0). + const Integer x = P.x * ToInteger(!P.identity); + const Integer y = P.y * ToInteger(!P.identity) + 1 * ToInteger(P.identity); + const Integer z = 1 * ToInteger(!P.identity); + + ProjectivePoint p(x, y, z), r; + + FieldElement t0 = field.Square(X); + FieldElement t1 = field.Square(Y); + FieldElement t2 = field.Square(Z); + FieldElement t3 = field.Multiply(X,Y); + t3 = field.Add(t3,t3); + Z3 = field.Multiply(X,Z); + Z3 = field.Add(Z3,Z3); + Y3 = field.Multiply(b,t2); + Y3 = field.Subtract(Y3,Z3); + X3 = field.Add(Y3,Y3); + Y3 = field.Add(X3,Y3); + X3 = field.Subtract(t1,Y3); + Y3 = field.Add(t1,Y3); + Y3 = field.Multiply(X3,Y3); + X3 = field.Multiply(X3,t3); + t3 = field.Add(t2,t2); + t2 = field.Add(t2,t3); + Z3 = field.Multiply(b,Z3); + Z3 = field.Subtract(Z3,t2); + Z3 = field.Subtract(Z3,t0); + t3 = field.Add(Z3,Z3); + Z3 = field.Add(Z3,t3); + t3 = field.Add(t0,t0); + t0 = field.Add(t3,t0); + t0 = field.Subtract(t0,t2); + t0 = field.Multiply(t0,Z3); + Y3 = field.Add(Y3,t0); + t0 = field.Multiply(Y,Z); + t0 = field.Add(t0,t0); + Z3 = field.Multiply(t0,Z3); + X3 = field.Subtract(X3,Z3); + Z3 = field.Multiply(t0,t1); + Z3 = field.Add(Z3,Z3); + Z3 = field.Add(Z3,Z3); + + const FieldElement inv = field.MultiplicativeInverse(Z3.IsZero() ? Integer::One() : Z3); + X3 = field.Multiply(X3, inv); Y3 = field.Multiply(Y3, inv); + + // More gyrations + ECP::Point result(X3*Z3.NotZero(), Y3*Z3.NotZero()); + result.identity = Z3.IsZero(); + return result; + } + else if (m_alpha == A_0) + { + const ECP::Field& field = m_ecp.GetField(); + const FieldElement b3 = field.Multiply(m_ecp.m_b, 3); + + // Gyrations attempt to maintain constant-timeness + // We need either (P.x, P.y, 1) or (0, 1, 0). + const Integer x = P.x * ToInteger(!P.identity); + const Integer y = P.y * ToInteger(!P.identity) + 1 * ToInteger(P.identity); + const Integer z = 1 * ToInteger(!P.identity); + + ProjectivePoint p(x, y, z), r; + + FieldElement t0 = field.Square(Y); + Z3 = field.Add(t0,t0); + Z3 = field.Add(Z3,Z3); + Z3 = field.Add(Z3,Z3); + FieldElement t1 = field.Add(Y,Z); + FieldElement t2 = field.Square(Z); + t2 = field.Multiply(b3,t2); + X3 = field.Multiply(t2,Z3); + Y3 = field.Add(t0,t2); + Z3 = field.Multiply(t1,Z3); + t1 = field.Add(t2,t2); + t2 = field.Add(t1,t2); + t0 = field.Subtract(t0,t2); + Y3 = field.Multiply(t0,Y3); + Y3 = field.Add(X3,Y3); + t1 = field.Multiply(X,Y); + X3 = field.Multiply(t0,t1); + X3 = field.Add(X3,X3); + + const FieldElement inv = field.MultiplicativeInverse(Z3.IsZero() ? Integer::One() : Z3); + X3 = field.Multiply(X3, inv); Y3 = field.Multiply(Y3, inv); + + // More gyrations + ECP::Point result(X3*Z3.NotZero(), Y3*Z3.NotZero()); + result.identity = Z3.IsZero(); + return result; + } + else if (m_alpha == A_Star) + { + const ECP::Field& field = m_ecp.GetField(); + const FieldElement b3 = field.Multiply(m_ecp.m_b, 3); + + // Gyrations attempt to maintain constant-timeness + // We need either (P.x, P.y, 1) or (0, 1, 0). + const Integer x = P.x * ToInteger(!P.identity); + const Integer y = P.y * ToInteger(!P.identity) + 1 * ToInteger(P.identity); + const Integer z = 1 * ToInteger(!P.identity); + + ProjectivePoint p(x, y, z), r; + + FieldElement t0 = field.Square(Y); + Z3 = field.Add(t0,t0); + Z3 = field.Add(Z3,Z3); + Z3 = field.Add(Z3,Z3); + FieldElement t1 = field.Add(Y,Z); + FieldElement t2 = field.Square(Z); + t2 = field.Multiply(b3,t2); + X3 = field.Multiply(t2,Z3); + Y3 = field.Add(t0,t2); + Z3 = field.Multiply(t1,Z3); + t1 = field.Add(t2,t2); + t2 = field.Add(t1,t2); + t0 = field.Subtract(t0,t2); + Y3 = field.Multiply(t0,Y3); + Y3 = field.Add(X3,Y3); + t1 = field.Multiply(X,Y); + X3 = field.Multiply(t0,t1); + X3 = field.Add(X3,X3); + + const FieldElement inv = field.MultiplicativeInverse(Z3.IsZero() ? Integer::One() : Z3); + X3 = field.Multiply(X3, inv); Y3 = field.Multiply(Y3, inv); + + // More gyrations + ECP::Point result(X3*Z3.NotZero(), Y3*Z3.NotZero()); + result.identity = Z3.IsZero(); + return result; + } + else // A_Montgomery + { + ECP::Point& m_R = m_ecp.m_R; + const ECP::Field& field = m_ecp.GetField(); + + if (P.identity || P.y==field.Identity()) return m_ecp.Identity(); + + FieldElement t = field.Square(P.x); + t = field.Add(field.Add(field.Double(t), t), m_ecp.m_a); + t = field.Divide(t, field.Double(P.y)); + FieldElement x = field.Subtract(field.Subtract(field.Square(t), P.x), P.x); + m_R.y = field.Subtract(field.Multiply(t, field.Subtract(P.x, x)), P.y); + + m_R.x.swap(x); + m_R.identity = false; + return m_R; + } +} + +ECP::Point ECP::AdditionFunction::operator()(const Point& P, const Point& Q) const +{ + if (m_alpha == A_3) + { + const ECP::Field& field = m_ecp.GetField(); + const FieldElement& b = m_ecp.m_b; + + // Gyrations attempt to maintain constant-timeness + // We need either (P.x, P.y, 1) or (0, 1, 0). + const Integer x1 = P.x * ToInteger(!P.identity); + const Integer y1 = P.y * ToInteger(!P.identity) + 1 * ToInteger(P.identity); + const Integer z1 = 1 * ToInteger(!P.identity); + + const Integer x2 = Q.x * ToInteger(!Q.identity); + const Integer y2 = Q.y * ToInteger(!Q.identity) + 1 * ToInteger(Q.identity); + const Integer z2 = 1 * ToInteger(!Q.identity); + + ProjectivePoint p(x1, y1, z1), q(x2, y2, z2), r; + + FieldElement t0 = field.Multiply(X1,X2); + FieldElement t1 = field.Multiply(Y1,Y2); + FieldElement t2 = field.Multiply(Z1,Z2); + FieldElement t3 = field.Add(X1,Y1); + FieldElement t4 = field.Add(X2,Y2); + t3 = field.Multiply(t3,t4); + t4 = field.Add(t0,t1); + t3 = field.Subtract(t3,t4); + t4 = field.Add(Y1,Z1); + X3 = field.Add(Y2,Z2); + t4 = field.Multiply(t4,X3); + X3 = field.Add(t1,t2); + t4 = field.Subtract(t4,X3); + X3 = field.Add(X1,Z1); + Y3 = field.Add(X2,Z2); + X3 = field.Multiply(X3,Y3); + Y3 = field.Add(t0,t2); + Y3 = field.Subtract(X3,Y3); + Z3 = field.Multiply(b,t2); + X3 = field.Subtract(Y3,Z3); + Z3 = field.Add(X3,X3); + X3 = field.Add(X3,Z3); + Z3 = field.Subtract(t1,X3); + X3 = field.Add(t1,X3); + Y3 = field.Multiply(b,Y3); + t1 = field.Add(t2,t2); + t2 = field.Add(t1,t2); + Y3 = field.Subtract(Y3,t2); + Y3 = field.Subtract(Y3,t0); + t1 = field.Add(Y3,Y3); + Y3 = field.Add(t1,Y3); + t1 = field.Add(t0,t0); + t0 = field.Add(t1,t0); + t0 = field.Subtract(t0,t2); + t1 = field.Multiply(t4,Y3); + t2 = field.Multiply(t0,Y3); + Y3 = field.Multiply(X3,Z3); + Y3 = field.Add(Y3,t2); + X3 = field.Multiply(t3,X3); + X3 = field.Subtract(X3,t1); + Z3 = field.Multiply(t4,Z3); + t1 = field.Multiply(t3,t0); + Z3 = field.Add(Z3,t1); + + const FieldElement inv = field.MultiplicativeInverse(Z3.IsZero() ? Integer::One() : Z3); + X3 = field.Multiply(X3, inv); Y3 = field.Multiply(Y3, inv); + + // More gyrations + ECP::Point result(X3*Z3.NotZero(), Y3*Z3.NotZero()); + result.identity = Z3.IsZero(); + return result; + } + else if (m_alpha == A_0) + { + const ECP::Field& field = m_ecp.GetField(); + const FieldElement b3 = field.Multiply(m_ecp.m_b, 3); + + // Gyrations attempt to maintain constant-timeness + // We need either (P.x, P.y, 1) or (0, 1, 0). + const Integer x1 = P.x * ToInteger(!P.identity); + const Integer y1 = P.y * ToInteger(!P.identity) + 1 * ToInteger(P.identity); + const Integer z1 = 1 * ToInteger(!P.identity); + + const Integer x2 = Q.x * ToInteger(!Q.identity); + const Integer y2 = Q.y * ToInteger(!Q.identity) + 1 * ToInteger(Q.identity); + const Integer z2 = 1 * ToInteger(!Q.identity); + + ProjectivePoint p(x1, y1, z1), q(x2, y2, z2), r; + + FieldElement t0 = field.Square(Y); + Z3 = field.Add(t0,t0); + Z3 = field.Add(Z3,Z3); + Z3 = field.Add(Z3,Z3); + FieldElement t1 = field.Add(Y,Z); + FieldElement t2 = field.Square(Z); + t2 = field.Multiply(b3,t2); + X3 = field.Multiply(t2,Z3); + Y3 = field.Add(t0,t2); + Z3 = field.Multiply(t1,Z3); + t1 = field.Add(t2,t2); + t2 = field.Add(t1,t2); + t0 = field.Subtract(t0,t2); + Y3 = field.Multiply(t0,Y3); + Y3 = field.Add(X3,Y3); + t1 = field.Multiply(X,Y); + X3 = field.Multiply(t0,t1); + X3 = field.Add(X3,X3); + + const FieldElement inv = field.MultiplicativeInverse(Z3.IsZero() ? Integer::One() : Z3); + X3 = field.Multiply(X3, inv); Y3 = field.Multiply(Y3, inv); + + // More gyrations + ECP::Point result(X3*Z3.NotZero(), Y3*Z3.NotZero()); + result.identity = Z3.IsZero(); + return result; + } + else if (m_alpha == A_Star) + { + const ECP::Field& field = m_ecp.GetField(); + const FieldElement &a = m_ecp.m_a; + const FieldElement b3 = field.Multiply(m_ecp.m_b, 3); + + // Gyrations attempt to maintain constant-timeness + // We need either (P.x, P.y, 1) or (0, 1, 0). + const Integer x1 = P.x * ToInteger(!P.identity); + const Integer y1 = P.y * ToInteger(!P.identity) + 1 * ToInteger(P.identity); + const Integer z1 = 1 * ToInteger(!P.identity); + + const Integer x2 = Q.x * ToInteger(!Q.identity); + const Integer y2 = Q.y * ToInteger(!Q.identity) + 1 * ToInteger(Q.identity); + const Integer z2 = 1 * ToInteger(!Q.identity); + + ProjectivePoint p(x1, y1, z1), q(x2, y2, z2), r; + + FieldElement t0 = field.Multiply(X1,X2); + FieldElement t1 = field.Multiply(Y1,Y2); + FieldElement t2 = field.Multiply(Z1,Z2); + FieldElement t3 = field.Add(X1,Y1); + FieldElement t4 = field.Add(X2,Y2); + t3 = field.Multiply(t3,t4); + t4 = field.Add(t0,t1); + t3 = field.Subtract(t3,t4); + t4 = field.Add(X1,Z1); + FieldElement t5 = field.Add(X2,Z2); + t4 = field.Multiply(t4,t5); + t5 = field.Add(t0,t2); + t4 = field.Subtract(t4,t5); + t5 = field.Add(Y1,Z1); + X3 = field.Add(Y2,Z2); + t5 = field.Multiply(t5,X3); + X3 = field.Add(t1,t2); + t5 = field.Subtract(t5,X3); + Z3 = field.Multiply(a,t4); + X3 = field.Multiply(b3,t2); + Z3 = field.Add(X3,Z3); + X3 = field.Subtract(t1,Z3); + Z3 = field.Add(t1,Z3); + Y3 = field.Multiply(X3,Z3); + t1 = field.Add(t0,t0); + t1 = field.Add(t1,t0); + t2 = field.Multiply(a,t2); + t4 = field.Multiply(b3,t4); + t1 = field.Add(t1,t2); + t2 = field.Subtract(t0,t2); + t2 = field.Multiply(a,t2); + t4 = field.Add(t4,t2); + t0 = field.Multiply(t1,t4); + Y3 = field.Add(Y3,t0); + t0 = field.Multiply(t5,t4); + X3 = field.Multiply(t3,X3); + X3 = field.Subtract(X3,t0); + t0 = field.Multiply(t3,t1); + Z3 = field.Multiply(t5,Z3); + Z3 = field.Add(Z3,t0); + + const FieldElement inv = field.MultiplicativeInverse(Z3.IsZero() ? Integer::One() : Z3); + X3 = field.Multiply(X3, inv); Y3 = field.Multiply(Y3, inv); + + // More gyrations + ECP::Point result(X3*Z3.NotZero(), Y3*Z3.NotZero()); + result.identity = Z3.IsZero(); + return result; + } + else // A_Montgomery + { + ECP::Point& m_R = m_ecp.m_R; + const ECP::Field& field = m_ecp.GetField(); + + if (P.identity) return Q; + if (Q.identity) return P; + if (field.Equal(P.x, Q.x)) + return field.Equal(P.y, Q.y) ? m_ecp.Double(P) : m_ecp.Identity(); + + FieldElement t = field.Subtract(Q.y, P.y); + t = field.Divide(t, field.Subtract(Q.x, P.x)); + FieldElement x = field.Subtract(field.Subtract(field.Square(t), P.x), Q.x); + m_R.y = field.Subtract(field.Multiply(t, field.Subtract(P.x, x)), P.y); + + m_R.x.swap(x); + m_R.identity = false; + return m_R; + } +} + +#undef X +#undef Y +#undef Z + +#undef X1 +#undef Y1 +#undef Z1 + +#undef X2 +#undef Y2 +#undef Z2 + +#undef X3 +#undef Y3 +#undef Z3 + NAMESPACE_END #endif diff --git a/ecp.h b/ecp.h index f7c919aa..dc4e86b0 100644 --- a/ecp.h +++ b/ecp.h @@ -106,6 +106,42 @@ public: bool operator==(const ECP &rhs) const {return GetField() == rhs.GetField() && m_a == rhs.m_a && m_b == rhs.m_b;} +protected: + /// \brief Addition and Double functions + /// \sa Complete + /// addition formulas for prime order elliptic curves + class AdditionFunction + { + public: + explicit AdditionFunction(const ECP& ecp); + // Double(P) + Point operator()(const Point& P) const; + // Add(P, Q) + Point operator()(const Point& P, const Point& Q) const; + + protected: + /// \brief Parameters and representation for Addition + /// \details Addition and Doubling will use different algorithms, + /// depending on the A coefficient and the representation + /// (Affine or Montgomery with precomputation). + enum Alpha { + /// \brief Coefficient A is 0 + A_0=1, + /// \brief Coefficient A is -3 + A_3=2, + /// \brief Coefficient A is arbitrary + A_Star=4, + /// \brief Representation is Montgomery + A_Montgomery=8 + }; + + const ECP& m_ecp; + Alpha m_alpha; + + private: + AdditionFunction(const AdditionFunction&); + }; + private: clonable_ptr m_fieldPtr; FieldElement m_a, m_b; diff --git a/fhmqv.h b/fhmqv.h index 8f8292b5..e03cc933 100644 --- a/fhmqv.h +++ b/fhmqv.h @@ -288,36 +288,18 @@ public: bbs = StaticPublicKeyLength(); } - // DecodeElement calls ValidateElement at level 1. Level 1 only calls - // VerifyPoint to ensure the element is in G*. If the other's PublicKey is - // requested to be validated, we manually call ValidateElement at level 3. - Element VV1 = params.DecodeElement(staticOtherPublicKey, false); - if(!params.ValidateElement(validateStaticOtherPublicKey ? 3 : 1, VV1, NULLPTR)) - { - CRYPTOPP_ASSERT(0); - return false; - } - - // DecodeElement calls ValidateElement at level 1. Level 1 only calls - // VerifyPoint to ensure the element is in G*. Crank it up. - Element VV2 = params.DecodeElement(ephemeralOtherPublicKey, false); - if(!params.ValidateElement(3, VV2, NULLPTR)) - { - CRYPTOPP_ASSERT(0); - return false; - } + Element VV1 = params.DecodeElement(staticOtherPublicKey, validateStaticOtherPublicKey); + Element VV2 = params.DecodeElement(ephemeralOtherPublicKey, true); const Integer& q = params.GetSubgroupOrder(); const unsigned int len /*bytes*/ = (((q.BitCount()+1)/2 +7)/8); - - Integer d, e; SecByteBlock dd(len), ee(len); Hash(NULLPTR, XX, xxs, YY, yys, AA, aas, BB, bbs, dd.BytePtr(), dd.SizeInBytes()); - d.Decode(dd.BytePtr(), dd.SizeInBytes()); + Integer d(dd.BytePtr(), dd.SizeInBytes()); Hash(NULLPTR, YY, yys, XX, xxs, AA, aas, BB, bbs, ee.BytePtr(), ee.SizeInBytes()); - e.Decode(ee.BytePtr(), ee.SizeInBytes()); + Integer e(ee.BytePtr(), ee.SizeInBytes()); Element sigma; if(m_role == RoleServer) @@ -372,11 +354,11 @@ protected: if(sigma) { - //SecByteBlock sbb(GetAbstractGroupParameters().GetEncodedElementSize(false)); - //GetAbstractGroupParameters().EncodeElement(false, *sigma, sbb); - Integer x = GetAbstractGroupParameters().ConvertElementToInteger(*sigma); - SecByteBlock sbb(x.MinEncodedSize()); - x.Encode(sbb.BytePtr(), sbb.SizeInBytes()); + //Integer x = GetAbstractGroupParameters().ConvertElementToInteger(*sigma); + //SecByteBlock sbb(x.MinEncodedSize()); + //x.Encode(sbb.BytePtr(), sbb.SizeInBytes()); + SecByteBlock sbb(GetAbstractGroupParameters().GetEncodedElementSize(false)); + GetAbstractGroupParameters().EncodeElement(false, *sigma, sbb); hash.Update(sbb.BytePtr(), sbb.SizeInBytes()); } @@ -404,7 +386,7 @@ protected: private: // The paper uses Initiator and Recipient - make it classical. - enum KeyAgreementRole{ RoleServer = 1, RoleClient }; + enum KeyAgreementRole { RoleServer = 1, RoleClient }; DL_GroupParameters & AccessAbstractGroupParameters() {return m_groupParameters;} const DL_GroupParameters & GetAbstractGroupParameters() const{return m_groupParameters;} diff --git a/hmqv.h b/hmqv.h index 6de2ba3b..dabd018c 100644 --- a/hmqv.h +++ b/hmqv.h @@ -287,38 +287,20 @@ public: bbs = StaticPublicKeyLength(); } - // DecodeElement calls ValidateElement at level 1. Level 1 only calls - // VerifyPoint to ensure the element is in G*. If the other's PublicKey is - // requested to be validated, we manually call ValidateElement at level 3. - Element VV1 = params.DecodeElement(staticOtherPublicKey, false); - if(!params.ValidateElement(validateStaticOtherPublicKey ? 3 : 1, VV1, NULLPTR)) - { - CRYPTOPP_ASSERT(0); - return false; - } - - // DecodeElement calls ValidateElement at level 1. Level 1 only calls - // VerifyPoint to ensure the element is in G*. Crank it up. - Element VV2 = params.DecodeElement(ephemeralOtherPublicKey, false); - if(!params.ValidateElement(3, VV2, NULLPTR)) - { - CRYPTOPP_ASSERT(0); - return false; - } + Element VV1 = params.DecodeElement(staticOtherPublicKey, validateStaticOtherPublicKey); + Element VV2 = params.DecodeElement(ephemeralOtherPublicKey, true); const Integer& q = params.GetSubgroupOrder(); const unsigned int len /*bytes*/ = (((q.BitCount()+1)/2 +7)/8); - - Integer d, e; SecByteBlock dd(len), ee(len); // Compute $d = \hat{H}(X, \hat{B})$ Hash(NULLPTR, XX, xxs, BB, bbs, dd.BytePtr(), dd.SizeInBytes()); - d.Decode(dd.BytePtr(), dd.SizeInBytes()); + Integer d(dd.BytePtr(), dd.SizeInBytes()); // Compute $e = \hat{H}(Y, \hat{A})$ Hash(NULLPTR, YY, yys, AA, aas, ee.BytePtr(), ee.SizeInBytes()); - e.Decode(ee.BytePtr(), ee.SizeInBytes()); + Integer e(ee.BytePtr(), ee.SizeInBytes()); Element sigma; if(m_role == RoleServer) @@ -345,11 +327,11 @@ public: Element B = params.DecodeElement(BB, false); Element Y = params.DecodeElement(YY, false); - Element t1 = params.ExponentiateElement(B, e); - Element t2 = m_groupParameters.MultiplyElements(Y, t1); + Element t3 = params.ExponentiateElement(B, e); + Element t4 = m_groupParameters.MultiplyElements(Y, t3); // $\sigma_A}=(Y \cdot B^{e})^{s_A} - sigma = params.ExponentiateElement(t2, s_A); + sigma = params.ExponentiateElement(t4, s_A); } Hash(&sigma, NULLPTR, 0, NULLPTR, 0, agreedValue, AgreedValueLength()); } @@ -379,11 +361,11 @@ protected: if (e1len != 0 || s1len != 0) { CRYPTOPP_ASSERT(0); } - //SecByteBlock sbb(GetAbstractGroupParameters().GetEncodedElementSize(false)); - //GetAbstractGroupParameters().EncodeElement(false, *sigma, sbb); - Integer x = GetAbstractGroupParameters().ConvertElementToInteger(*sigma); - SecByteBlock sbb(x.MinEncodedSize()); - x.Encode(sbb.BytePtr(), sbb.SizeInBytes()); + //Integer x = GetAbstractGroupParameters().ConvertElementToInteger(*sigma); + //SecByteBlock sbb(x.MinEncodedSize()); + //x.Encode(sbb.BytePtr(), sbb.SizeInBytes()); + SecByteBlock sbb(GetAbstractGroupParameters().GetEncodedElementSize(false)); + GetAbstractGroupParameters().EncodeElement(false, *sigma, sbb); hash.Update(sbb.BytePtr(), sbb.SizeInBytes()); } else { if (e1len == 0 || s1len == 0) { @@ -412,7 +394,7 @@ protected: private: // The paper uses Initiator and Recipient - make it classical. - enum KeyAgreementRole{ RoleServer = 1, RoleClient }; + enum KeyAgreementRole { RoleServer = 1, RoleClient }; DL_GroupParameters & AccessAbstractGroupParameters() {return m_groupParameters;} diff --git a/mqv.cpp b/mqv.cpp index 1189dfb1..ef886251 100644 --- a/mqv.cpp +++ b/mqv.cpp @@ -7,6 +7,7 @@ #include "mqv.h" #include "hmqv.h" #include "fhmqv.h" +#include "eccrypto.h" // Squash MS LNK4221 and libtool warnings extern const char MQV_FNAME[] = __FILE__; @@ -16,17 +17,29 @@ NAMESPACE_BEGIN(CryptoPP) #if defined(CRYPTOPP_DEBUG) && !defined(CRYPTOPP_DOXYGEN_PROCESSING) void TestInstantiations_MQV() { - MQV mqv; + MQV mqv; + ECMQV ecmqv; + + CRYPTOPP_UNUSED(mqv); + CRYPTOPP_UNUSED(ecmqv); } void TestInstantiations_HMQV() { HMQV hmqv; + ECHMQV echmqv; + + CRYPTOPP_UNUSED(hmqv); + CRYPTOPP_UNUSED(echmqv); } void TestInstantiations_FHMQV() { FHMQV fhmqv; + ECFHMQV ecfhmqv; + + CRYPTOPP_UNUSED(fhmqv); + CRYPTOPP_UNUSED(ecfhmqv); } #endif diff --git a/test.cpp b/test.cpp index 5eb5dcbf..d1c143bc 100644 --- a/test.cpp +++ b/test.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #ifdef CRYPTOPP_WIN32_AVAILABLE @@ -89,7 +90,8 @@ int (*AdhocTest)(int argc, char *argv[]) = NULLPTR; NAMESPACE_BEGIN(CryptoPP) NAMESPACE_BEGIN(Test) -const int MAX_PHRASE_LENGTH=250; +const int MAX_PHRASE_LENGTH = 250; +const int GLOBAL_SEED_LENGTH = 16; std::string g_argvPathHint=""; void GenerateRSAKey(unsigned int keyLength, const char *privFilename, const char *pubFilename, const char *seed); @@ -125,7 +127,8 @@ void HexDecode(const char *infile, const char *outfile); void FIPS140_GenerateRandomFiles(); -bool Validate(int, bool, const char *); +bool Validate(int, bool); +bool SetGlobalSeed(int argc, char* argv[], std::string& seed); void SetArgvPathHint(const char* argv0, std::string& pathHint); ANONYMOUS_NAMESPACE_BEGIN @@ -166,23 +169,26 @@ int scoped_main(int argc, char *argv[]) cin.set_safe_flag(stream_MT::unsafe_object); #endif - // A hint to help locate TestData/ and TestVectors/ after install. - SetArgvPathHint(argv[0], g_argvPathHint); - try { RegisterFactories(All); - // Some editors have problems with the '\0' character when redirecting output. - s_globalSeed = IntToString(time(NULLPTR)); - s_globalSeed.resize(16, ' '); + // A hint to help locate TestData/ and TestVectors/ after install. + SetArgvPathHint(argv[0], g_argvPathHint); + + // Set a seed for reproducible results. It can be set on the command line. + // If the seed is short then it is padded with spaces. If the seed is + // missing then time() is used. + // For example: + // ./cryptest.exe v seed=abcdefg + SetGlobalSeed(argc, argv, s_globalSeed); #if (CRYPTOPP_USE_AES_GENERATOR) // Fetch the SymmetricCipher interface, not the RandomNumberGenerator // interface, to key the underlying cipher. If CRYPTOPP_USE_AES_GENERATOR is 1 // then AES/OFB based is used. Otherwise the OS random number generator is used. SymmetricCipher& cipher = dynamic_cast(GlobalRNG()); - cipher.SetKeyWithIV((byte *)s_globalSeed.data(), 16, (byte *)s_globalSeed.data()); + cipher.SetKeyWithIV((byte *)s_globalSeed.data(), s_globalSeed.size(), (byte *)s_globalSeed.data()); #endif std::string command, executableName, macFilename; @@ -393,7 +399,7 @@ int scoped_main(int argc, char *argv[]) else if (command == "ir") InformationRecoverFile(argc-3, argv[2], argv+3); else if (command == "v" || command == "vv") - return !Validate(argc>2 ? StringToValue(argv[2]) : 0, argv[1][1] == 'v', argc>3 ? argv[3] : NULLPTR); + return !Validate(argc>2 ? StringToValue(argv[2]) : 0, argv[1][1] == 'v'); else if (command.substr(0,1) == "b") // "b", "b1", "b2", ... BenchmarkWithCommand(argc, argv); else if (command == "z") @@ -446,6 +452,35 @@ int scoped_main(int argc, char *argv[]) } } // main() +bool SetGlobalSeed(int argc, char* argv[], std::string& seed) +{ + bool ret = false; + + for (int i=0; i::Domain hmqvA256(oid, true /*client*/); - success = AuthenticatedKeyAgreementWithRolesValidate(hmqvA256, hmqvB256) && success; + fail = !AuthenticatedKeyAgreementWithRolesValidate(hmqvA256, hmqvB256); + success = !fail && success; + if (fail == false) + std::cout << "passed authenticated key agreement" << std::endl; + else + std::cout << "FAILED authenticated key agreement" << std::endl; ///////////////////////// @@ -102,7 +107,12 @@ bool ValidateHMQV() const OID oid384 = ASN1::secp384r1(); ECHMQV384 hmqvA384(oid384, true /*client*/); - success = AuthenticatedKeyAgreementWithRolesValidate(hmqvA384, hmqvB384) && success; + fail = !AuthenticatedKeyAgreementWithRolesValidate(hmqvA384, hmqvB384); + success = !fail && success; + if (fail == false) + std::cout << "passed authenticated key agreement" << std::endl; + else + std::cout << "FAILED authenticated key agreement" << std::endl; ///////////////////////// @@ -113,7 +123,12 @@ bool ValidateHMQV() const OID oid521 = ASN1::secp521r1(); ECHMQV512 hmqvA521(oid521, true /*client*/); - success = AuthenticatedKeyAgreementWithRolesValidate(hmqvA521, hmqvB521) && success; + fail = !AuthenticatedKeyAgreementWithRolesValidate(hmqvA521, hmqvB521); + success = !fail && success; + if (fail == false) + std::cout << "passed authenticated key agreement" << std::endl; + else + std::cout << "FAILED authenticated key agreement" << std::endl; return success; } @@ -121,7 +136,7 @@ bool ValidateHMQV() bool ValidateFHMQV() { std::cout << "\nFHMQV validation suite running...\n\n"; - bool success = true; + bool success = true, fail; FileSource f256(DataDir("TestData/fhmqv256.dat").c_str(), true, new HexDecoder); FileSource f384(DataDir("TestData/fhmqv384.dat").c_str(), true, new HexDecoder); @@ -136,7 +151,12 @@ bool ValidateFHMQV() const OID oid = ASN1::secp256r1(); ECFHMQV< ECP >::Domain fhmqvA256(oid, true /*client*/); - success = AuthenticatedKeyAgreementWithRolesValidate(fhmqvA256, fhmqvB256) && success; + fail = !AuthenticatedKeyAgreementWithRolesValidate(fhmqvA256, fhmqvB256); + success = !fail && success; + if (fail == false) + std::cout << "passed authenticated key agreement" << std::endl; + else + std::cout << "FAILED authenticated key agreement" << std::endl; ///////////////////////// @@ -147,7 +167,12 @@ bool ValidateFHMQV() const OID oid384 = ASN1::secp384r1(); ECHMQV384 fhmqvA384(oid384, true /*client*/); - success = AuthenticatedKeyAgreementWithRolesValidate(fhmqvA384, fhmqvB384) && success; + fail = !AuthenticatedKeyAgreementWithRolesValidate(fhmqvA384, fhmqvB384); + success = !fail && success; + if (fail == false) + std::cout << "passed authenticated key agreement" << std::endl; + else + std::cout << "FAILED authenticated key agreement" << std::endl; ///////////////////////// @@ -158,7 +183,12 @@ bool ValidateFHMQV() const OID oid521 = ASN1::secp521r1(); ECHMQV512 fhmqvA521(oid521, true /*client*/); - success = AuthenticatedKeyAgreementWithRolesValidate(fhmqvA521, fhmqvB521) && success; + fail = !AuthenticatedKeyAgreementWithRolesValidate(fhmqvA521, fhmqvB521); + success = !fail && success; + if (fail == false) + std::cout << "passed authenticated key agreement" << std::endl; + else + std::cout << "FAILED authenticated key agreement" << std::endl; return success; }