diff --git a/TestData/fhmqv160.dat b/TestData/fhmqv160.dat new file mode 100644 index 00000000..0d66deec --- /dev/null +++ b/TestData/fhmqv160.dat @@ -0,0 +1 @@ +3081E0020101302C06072A8648CE3D0101022100FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF30440420FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC04205AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B0441046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5022100FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551020101 \ No newline at end of file diff --git a/TestData/fhmqv256.dat b/TestData/fhmqv256.dat new file mode 100644 index 00000000..0d66deec --- /dev/null +++ b/TestData/fhmqv256.dat @@ -0,0 +1 @@ +3081E0020101302C06072A8648CE3D0101022100FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF30440420FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC04205AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B0441046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5022100FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551020101 \ No newline at end of file diff --git a/TestData/fhmqv384.dat b/TestData/fhmqv384.dat new file mode 100644 index 00000000..63037fef --- /dev/null +++ b/TestData/fhmqv384.dat @@ -0,0 +1 @@ +30820140020101303C06072A8648CE3D0101023100FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF30640430FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC0430B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF046104AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB73617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F023100FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973020101 \ No newline at end of file diff --git a/TestData/fhmqv512.dat b/TestData/fhmqv512.dat new file mode 100644 index 00000000..0053ddf6 --- /dev/null +++ b/TestData/fhmqv512.dat @@ -0,0 +1 @@ +308201AC020101304D06072A8648CE3D0101024201FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF308188044201FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC04420051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F000481850400C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650024201FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409020101 \ No newline at end of file diff --git a/TestData/hmqv160.dat b/TestData/hmqv160.dat new file mode 100644 index 00000000..0d66deec --- /dev/null +++ b/TestData/hmqv160.dat @@ -0,0 +1 @@ +3081E0020101302C06072A8648CE3D0101022100FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF30440420FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC04205AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B0441046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5022100FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551020101 \ No newline at end of file diff --git a/TestData/hmqv256.dat b/TestData/hmqv256.dat new file mode 100644 index 00000000..0d66deec --- /dev/null +++ b/TestData/hmqv256.dat @@ -0,0 +1 @@ +3081E0020101302C06072A8648CE3D0101022100FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF30440420FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC04205AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B0441046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5022100FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551020101 \ No newline at end of file diff --git a/TestData/hmqv384.dat b/TestData/hmqv384.dat new file mode 100644 index 00000000..63037fef --- /dev/null +++ b/TestData/hmqv384.dat @@ -0,0 +1 @@ +30820140020101303C06072A8648CE3D0101023100FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF30640430FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC0430B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF046104AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB73617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F023100FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973020101 \ No newline at end of file diff --git a/TestData/hmqv512.dat b/TestData/hmqv512.dat new file mode 100644 index 00000000..0053ddf6 --- /dev/null +++ b/TestData/hmqv512.dat @@ -0,0 +1 @@ +308201AC020101304D06072A8648CE3D0101024201FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF308188044201FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC04420051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F000481850400C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650024201FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409020101 \ No newline at end of file diff --git a/eccrypto.h b/eccrypto.h index a3d15e95..6642dec5 100644 --- a/eccrypto.h +++ b/eccrypto.h @@ -16,6 +16,8 @@ #include "gfpcrypt.h" #include "dh.h" #include "mqv.h" +#include "hmqv.h" +#include "fhmqv.h" #include "ecp.h" #include "ec2n.h" @@ -213,6 +215,44 @@ struct ECMQV #endif }; +//! Hashed Menezes-Qu-Vanstone in GF(p) with key validation, +/*! HMQV: A High-Performance Secure Diffie-Hellman Protocol + Note: this implements HMQV only. HMQV-C (with Key Confirmation) will be provided separately. +*/ +template ::DefaultCofactorOption, class HASH = SHA256> +struct HMQV +{ + typedef HMQV_Domain, COFACTOR_OPTION, HASH> Domain; + +#ifndef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY_562 + virtual ~HMQV() {} +#endif +}; + +typedef HMQV< ECP, DL_GroupParameters_EC< ECP >::DefaultCofactorOption, SHA1 >::Domain HMQV160; +typedef HMQV< ECP, DL_GroupParameters_EC< ECP >::DefaultCofactorOption, SHA256 >::Domain HMQV256; +typedef HMQV< ECP, DL_GroupParameters_EC< ECP >::DefaultCofactorOption, SHA384 >::Domain HMQV384; +typedef HMQV< ECP, DL_GroupParameters_EC< ECP >::DefaultCofactorOption, SHA512 >::Domain HMQV512; + +//! Fully Hashed Menezes-Qu-Vanstone in GF(p) with key validation, +/*! A Secure and Efficient Authenticated Diffie–Hellman Protocol + Note: this is FHMQV, Protocol 5, from page 11; and not FHMQV-C. +*/ +template ::DefaultCofactorOption, class HASH = SHA256> +struct FHMQV +{ + typedef FHMQV_Domain, COFACTOR_OPTION, HASH> Domain; + +#ifndef CRYPTOPP_MAINTAIN_BACKWARDS_COMPATIBILITY_562 + virtual ~FHMQV() {} +#endif +}; + +typedef FHMQV< ECP, DL_GroupParameters_EC< ECP >::DefaultCofactorOption, SHA1 >::Domain FHMQV160; +typedef FHMQV< ECP, DL_GroupParameters_EC< ECP >::DefaultCofactorOption, SHA256 >::Domain FHMQV256; +typedef FHMQV< ECP, DL_GroupParameters_EC< ECP >::DefaultCofactorOption, SHA384 >::Domain FHMQV384; +typedef FHMQV< ECP, DL_GroupParameters_EC< ECP >::DefaultCofactorOption, SHA512 >::Domain FHMQV512; + //! EC keys template struct DL_Keys_EC @@ -283,10 +323,10 @@ struct ECNR : public DL_SS, DL_Algorithm_ECNR, DL_SignatureMe }; //! Elliptic Curve Integrated Encryption Scheme, AKA ECIES -/*! Default to (NoCofactorMultiplication and DHAES_MODE = false) for compatibilty with SEC1 and Crypto++ 4.2. +/*! Choose NoCofactorMultiplication and DHAES_MODE = false for compatibilty with SEC1 and Crypto++ 4.2. The combination of (IncompatibleCofactorMultiplication and DHAES_MODE = true) is recommended for best efficiency and security. */ -template +template struct ECIES : public DL_ES< DL_Keys_EC, diff --git a/fhmqv.cpp b/fhmqv.cpp new file mode 100644 index 00000000..e6403d49 --- /dev/null +++ b/fhmqv.cpp @@ -0,0 +1,14 @@ +// fhmqv.cpp - written and placed in the public domain by Jeffrey Walton +// Shamelessly based upon Wei Dai's MQV source files + +#include "pch.h" +#include "fhmqv.h" + +NAMESPACE_BEGIN(CryptoPP) + +void TestInstantiations_FHMQV() +{ + FullyHashedMQV fhmqv; +} + +NAMESPACE_END diff --git a/fhmqv.h b/fhmqv.h new file mode 100644 index 00000000..8bec5405 --- /dev/null +++ b/fhmqv.h @@ -0,0 +1,294 @@ +// fhmqv.h - written and placed in the public domain by Jeffrey Walton +// Shamelessly based upon Wei Dai's MQV source files + +#ifndef CRYPTOPP_FHMQV_H +#define CRYPTOPP_FHMQV_H + +/** \file +*/ + +#include "gfpcrypt.h" +#include "algebra.h" +#include "sha.h" + +NAMESPACE_BEGIN(CryptoPP) + +//! Fully Hashed Menezes-Qu-Vanstone in GF(p) with key validation, +/*! A Secure and Efficient Authenticated Diffie–Hellman Protocol + Note: this is FHMQV, Protocol 5, from page 11; and not FHMQV-C. +*/ +template +class FHMQV_Domain: public AuthenticatedKeyAgreementDomain +{ +public: + typedef GROUP_PARAMETERS GroupParameters; + typedef typename GroupParameters::Element Element; + typedef FHMQV_Domain Domain; + + FHMQV_Domain(bool clientRole = true): m_role(clientRole ? RoleClient : RoleServer) {} + + FHMQV_Domain(const GroupParameters ¶ms, bool clientRole = true) + : m_role(clientRole ? RoleClient : RoleServer), m_groupParameters(params) {} + + FHMQV_Domain(BufferedTransformation &bt, bool clientRole = true) + : m_role(clientRole ? RoleClient : RoleServer) + {m_groupParameters.BERDecode(bt);} + + template + FHMQV_Domain(T1 v1, bool clientRole = true) + : m_role(clientRole ? RoleClient : RoleServer) + {m_groupParameters.Initialize(v1);} + + template + FHMQV_Domain(T1 v1, T2 v2, bool clientRole = true) + : m_role(clientRole ? RoleClient : RoleServer) + {m_groupParameters.Initialize(v1, v2);} + + template + FHMQV_Domain(T1 v1, T2 v2, T3 v3, bool clientRole = true) + : m_role(clientRole ? RoleClient : RoleServer) + {m_groupParameters.Initialize(v1, v2, v3);} + + template + FHMQV_Domain(T1 v1, T2 v2, T3 v3, T4 v4, bool clientRole = true) + : m_role(clientRole ? RoleClient : RoleServer) + {m_groupParameters.Initialize(v1, v2, v3, v4);} + +protected: + + inline void Hash(const Element* sigma, + const byte* e1, size_t e1len, const byte* e2, size_t e2len, + const byte* s1, size_t s1len, const byte* s2, size_t s2len, + byte* digest, size_t dlen) const + { + HASH hash; + size_t idx = 0, req = dlen; + size_t blk = std::min(dlen, (size_t)HASH::DIGESTSIZE); + + if(sigma) + { + Integer x = GetAbstractGroupParameters().ConvertElementToInteger(*sigma); + SecByteBlock sbb(x.MinEncodedSize()); + x.Encode(sbb.BytePtr(), sbb.SizeInBytes()); + hash.Update(sbb.BytePtr(), sbb.SizeInBytes()); + } + + hash.Update(e1, e1len); + hash.Update(e2, e2len); + hash.Update(s1, s1len); + hash.Update(s2, s2len); + + hash.TruncatedFinal(digest, blk); + req -= blk; + + // All this to catch tail bytes for large curves and small hashes + while(req != 0) + { + hash.Update(&digest[idx], (size_t)HASH::DIGESTSIZE); + + idx += (size_t)HASH::DIGESTSIZE; + blk = std::min(req, (size_t)HASH::DIGESTSIZE); + hash.TruncatedFinal(&digest[idx], blk); + + req -= blk; + } + } + +public: + + const GroupParameters & GetGroupParameters() const {return m_groupParameters;} + GroupParameters & AccessGroupParameters(){return m_groupParameters;} + + CryptoParameters & AccessCryptoParameters(){return AccessAbstractGroupParameters();} + + //! return length of agreed value produced + unsigned int AgreedValueLength() const {return GetAbstractGroupParameters().GetEncodedElementSize(false);} + //! return length of static private keys in this domain + unsigned int StaticPrivateKeyLength() const {return GetAbstractGroupParameters().GetSubgroupOrder().ByteCount();} + //! return length of static public keys in this domain + unsigned int StaticPublicKeyLength() const{return GetAbstractGroupParameters().GetEncodedElementSize(true);} + + //! generate static private key + /*! \pre size of privateKey == PrivateStaticKeyLength() */ + void GenerateStaticPrivateKey(RandomNumberGenerator &rng, byte *privateKey) const + { + Integer x(rng, Integer::One(), GetAbstractGroupParameters().GetMaxExponent()); + x.Encode(privateKey, StaticPrivateKeyLength()); + } + + //! generate static public key + /*! \pre size of publicKey == PublicStaticKeyLength() */ + void GenerateStaticPublicKey(RandomNumberGenerator &rng, const byte *privateKey, byte *publicKey) const + { + const DL_GroupParameters ¶ms = GetAbstractGroupParameters(); + Integer x(privateKey, StaticPrivateKeyLength()); + Element y = params.ExponentiateBase(x); + params.EncodeElement(true, y, publicKey); + } + + unsigned int EphemeralPrivateKeyLength() const {return StaticPrivateKeyLength() + StaticPublicKeyLength();} + unsigned int EphemeralPublicKeyLength() const{return StaticPublicKeyLength();} + + //! return length of ephemeral private keys in this domain + void GenerateEphemeralPrivateKey(RandomNumberGenerator &rng, byte *privateKey) const + { + const DL_GroupParameters ¶ms = GetAbstractGroupParameters(); + Integer x(rng, Integer::One(), params.GetMaxExponent()); + x.Encode(privateKey, StaticPrivateKeyLength()); + Element y = params.ExponentiateBase(x); + params.EncodeElement(true, y, privateKey+StaticPrivateKeyLength()); + } + + //! return length of ephemeral public keys in this domain + void GenerateEphemeralPublicKey(RandomNumberGenerator &rng, const byte *privateKey, byte *publicKey) const + { + memcpy(publicKey, privateKey+StaticPrivateKeyLength(), EphemeralPublicKeyLength()); + } + + //! derive agreed value from your private keys and couterparty's public keys, return false in case of failure + /*! \note The ephemeral public key will always be validated. + If you have previously validated the static public key, use validateStaticOtherPublicKey=false to save time. + \pre size of agreedValue == AgreedValueLength() + \pre length of staticPrivateKey == StaticPrivateKeyLength() + \pre length of ephemeralPrivateKey == EphemeralPrivateKeyLength() + \pre length of staticOtherPublicKey == StaticPublicKeyLength() + \pre length of ephemeralOtherPublicKey == EphemeralPublicKeyLength() + */ + bool Agree(byte *agreedValue, + const byte *staticPrivateKey, const byte *ephemeralPrivateKey, + const byte *staticOtherPublicKey, const byte *ephemeralOtherPublicKey, + bool validateStaticOtherPublicKey=true) const + { + byte *XX = NULL, *YY = NULL, *AA = NULL, *BB = NULL; + size_t xxs = 0, yys = 0, aas = 0, bbs = 0; + + // Depending on the role, this will hold either A's or B's static + // (long term) public key. AA or BB will then point into tt. + SecByteBlock tt(StaticPublicKeyLength()); + + try + { + const DL_GroupParameters ¶ms = GetAbstractGroupParameters(); + + if(m_role == RoleServer) + { + Integer b(staticPrivateKey, StaticPrivateKeyLength()); + Element B = params.ExponentiateBase(b); + params.EncodeElement(true, B, tt); + + XX = const_cast(ephemeralOtherPublicKey); + xxs = EphemeralPublicKeyLength(); + YY = const_cast(ephemeralPrivateKey) + StaticPrivateKeyLength(); + yys = EphemeralPublicKeyLength(); + AA = const_cast(staticOtherPublicKey); + aas = StaticPublicKeyLength(); + BB = tt.BytePtr(); + bbs = tt.SizeInBytes(); + } + else if(m_role == RoleClient) + { + Integer a(staticPrivateKey, StaticPrivateKeyLength()); + Element A = params.ExponentiateBase(a); + params.EncodeElement(true, A, tt); + + XX = const_cast(ephemeralPrivateKey) + StaticPrivateKeyLength(); + xxs = EphemeralPublicKeyLength(); + YY = const_cast(ephemeralOtherPublicKey); + yys = EphemeralPublicKeyLength(); + AA = tt.BytePtr(); + aas = tt.SizeInBytes(); + BB = const_cast(staticOtherPublicKey); + bbs = StaticPublicKeyLength(); + } + else + { + assert(0); + return false; + } + + // 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, NULL)) + 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, NULL)) + return false; + + const Integer& p = params.GetGroupOrder(); + 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(NULL, XX, xxs, YY, yys, AA, aas, BB, bbs, dd.BytePtr(), dd.SizeInBytes()); + d.Decode(dd.BytePtr(), dd.SizeInBytes()); + + Hash(NULL, YY, yys, XX, xxs, AA, aas, BB, bbs, ee.BytePtr(), ee.SizeInBytes()); + e.Decode(ee.BytePtr(), ee.SizeInBytes()); + + Element sigma; + if(m_role == RoleServer) + { + Integer y(ephemeralPrivateKey, StaticPrivateKeyLength()); + Integer b(staticPrivateKey, StaticPrivateKeyLength()); + Integer s_B = (y + e * b) % q; + + Element A = params.DecodeElement(AA, false); + Element X = params.DecodeElement(XX, false); + + Element t1 = params.ExponentiateElement(A, d); + Element t2 = m_groupParameters.MultiplyElements(X, t1); + + sigma = params.ExponentiateElement(t2, s_B); + } + else + { + Integer x(ephemeralPrivateKey, StaticPrivateKeyLength()); + Integer a(staticPrivateKey, StaticPrivateKeyLength()); + Integer s_A = (x + d * a) % q; + + 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); + + sigma = params.ExponentiateElement(t2, s_A); + } + + Hash(&sigma, XX, xxs, YY, yys, AA, aas, BB, bbs, agreedValue, AgreedValueLength()); + } + catch (DL_BadElement &) + { + return false; + } + return true; + } + +private: + + // The paper uses Initiator and Recipient - make it classical. + enum KeyAgreementRole{ RoleServer = 1, RoleClient }; + + DL_GroupParameters & AccessAbstractGroupParameters() {return m_groupParameters;} + const DL_GroupParameters & GetAbstractGroupParameters() const{return m_groupParameters;} + + KeyAgreementRole m_role; + GroupParameters m_groupParameters; +}; + +//! Fully Hashed Menezes-Qu-Vanstone in GF(p) with key validation, +/*! A Secure and Efficient Authenticated Diffie–Hellman Protocol + Note: this is FHMQV, Protocol 5, from page 11; and not FHMQV-C. +*/ +typedef FHMQV_Domain FullyHashedMQV; + +NAMESPACE_END + +#endif diff --git a/hmqv.cpp b/hmqv.cpp new file mode 100644 index 00000000..86a8f97d --- /dev/null +++ b/hmqv.cpp @@ -0,0 +1,14 @@ +// hmqv.cpp - written and placed in the public domain by Uri Blumenthal +// Shamelessly based upon Jeffrey Walton's FHMQV and Wei Dai's MQV source files + +#include "pch.h" +#include "hmqv.h" + +NAMESPACE_BEGIN(CryptoPP) + +void TestInstantiations_HMQV() +{ + HashedMQV hmqv; +} + +NAMESPACE_END diff --git a/hmqv.h b/hmqv.h new file mode 100644 index 00000000..241d4c66 --- /dev/null +++ b/hmqv.h @@ -0,0 +1,303 @@ +// hmqv.h - written and placed in the public domain by Uri Blumenthal +// Shamelessly based upon Jeffrey Walton's FHMQV and Wei Dai's MQV source files + +#ifndef CRYPTOPP_HMQV_H +#define CRYPTOPP_HMQV_H + +/** \file +*/ + +#include "gfpcrypt.h" +#include "algebra.h" +#include "sha.h" + +NAMESPACE_BEGIN(CryptoPP) + +//! Hashed Menezes-Qu-Vanstone in GF(p) with key validation, +/*! HMQV: A High-Performance Secure Diffie-Hellman Protocol + Note: this implements HMQV only. HMQV-C (with Key Confirmation) will be provided separately. +*/ +template +class HMQV_Domain: public AuthenticatedKeyAgreementDomain +{ +public: + typedef GROUP_PARAMETERS GroupParameters; + typedef typename GroupParameters::Element Element; + typedef HMQV_Domain Domain; + + HMQV_Domain(bool clientRole = true): m_role(clientRole ? RoleClient : RoleServer) {} + + HMQV_Domain(const GroupParameters ¶ms, bool clientRole = true) + : m_role(clientRole ? RoleClient : RoleServer), m_groupParameters(params) {} + + HMQV_Domain(BufferedTransformation &bt, bool clientRole = true) + : m_role(clientRole ? RoleClient : RoleServer) + {m_groupParameters.BERDecode(bt);} + + template + HMQV_Domain(T1 v1, bool clientRole = true) + : m_role(clientRole ? RoleClient : RoleServer) + {m_groupParameters.Initialize(v1);} + + template + HMQV_Domain(T1 v1, T2 v2, bool clientRole = true) + : m_role(clientRole ? RoleClient : RoleServer) + {m_groupParameters.Initialize(v1, v2);} + + template + HMQV_Domain(T1 v1, T2 v2, T3 v3, bool clientRole = true) + : m_role(clientRole ? RoleClient : RoleServer) + {m_groupParameters.Initialize(v1, v2, v3);} + + template + HMQV_Domain(T1 v1, T2 v2, T3 v3, T4 v4, bool clientRole = true) + : m_role(clientRole ? RoleClient : RoleServer) + {m_groupParameters.Initialize(v1, v2, v3, v4);} + +protected: + // Hash invocation by client and server differ only in what keys + // each provides. + + inline void Hash(const Element* sigma, + const byte* e1, size_t e1len, // Ephemeral key and key length + const byte* s1, size_t s1len, // Static key and key length + byte* digest, size_t dlen) const + { + HASH hash; + size_t idx = 0, req = dlen; + size_t blk = std::min(dlen, (size_t)HASH::DIGESTSIZE); + + if(sigma) + { + if (e1len != 0 || s1len != 0) { + assert(0); + } + Integer x = GetAbstractGroupParameters().ConvertElementToInteger(*sigma); + SecByteBlock sbb(x.MinEncodedSize()); + x.Encode(sbb.BytePtr(), sbb.SizeInBytes()); + hash.Update(sbb.BytePtr(), sbb.SizeInBytes()); + } else { + if (e1len == 0 || s1len == 0) { + assert(0); + } + hash.Update(e1, e1len); + hash.Update(s1, s1len); + } + + hash.TruncatedFinal(digest, blk); + req -= blk; + + // All this to catch tail bytes for large curves and small hashes + while(req != 0) + { + hash.Update(&digest[idx], (size_t)HASH::DIGESTSIZE); + + idx += (size_t)HASH::DIGESTSIZE; + blk = std::min(req, (size_t)HASH::DIGESTSIZE); + hash.TruncatedFinal(&digest[idx], blk); + + req -= blk; + } + } + +public: + + const GroupParameters & GetGroupParameters() const {return m_groupParameters;} + GroupParameters & AccessGroupParameters(){return m_groupParameters;} + + CryptoParameters & AccessCryptoParameters(){return AccessAbstractGroupParameters();} + + //! return length of agreed value produced + unsigned int AgreedValueLength() const {return GetAbstractGroupParameters().GetEncodedElementSize(false);} + //! return length of static private keys in this domain + unsigned int StaticPrivateKeyLength() const {return GetAbstractGroupParameters().GetSubgroupOrder().ByteCount();} + //! return length of static public keys in this domain + unsigned int StaticPublicKeyLength() const{return GetAbstractGroupParameters().GetEncodedElementSize(true);} + + //! generate static private key + /*! \pre size of privateKey == PrivateStaticKeyLength() */ + void GenerateStaticPrivateKey(RandomNumberGenerator &rng, byte *privateKey) const + { + Integer x(rng, Integer::One(), GetAbstractGroupParameters().GetMaxExponent()); + x.Encode(privateKey, StaticPrivateKeyLength()); + } + + //! generate static public key + /*! \pre size of publicKey == PublicStaticKeyLength() */ + void GenerateStaticPublicKey(RandomNumberGenerator &rng, const byte *privateKey, byte *publicKey) const + { + const DL_GroupParameters ¶ms = GetAbstractGroupParameters(); + Integer x(privateKey, StaticPrivateKeyLength()); + Element y = params.ExponentiateBase(x); + params.EncodeElement(true, y, publicKey); + } + + unsigned int EphemeralPrivateKeyLength() const {return StaticPrivateKeyLength() + StaticPublicKeyLength();} + unsigned int EphemeralPublicKeyLength() const{return StaticPublicKeyLength();} + + //! return length of ephemeral private keys in this domain + void GenerateEphemeralPrivateKey(RandomNumberGenerator &rng, byte *privateKey) const + { + const DL_GroupParameters ¶ms = GetAbstractGroupParameters(); + Integer x(rng, Integer::One(), params.GetMaxExponent()); + x.Encode(privateKey, StaticPrivateKeyLength()); + Element y = params.ExponentiateBase(x); + params.EncodeElement(true, y, privateKey+StaticPrivateKeyLength()); + } + + //! return length of ephemeral public keys in this domain + void GenerateEphemeralPublicKey(RandomNumberGenerator &rng, const byte *privateKey, byte *publicKey) const + { + memcpy(publicKey, privateKey+StaticPrivateKeyLength(), EphemeralPublicKeyLength()); + } + + //! derive agreed value from your private keys and couterparty's public keys, return false in case of failure + /*! \note The ephemeral public key will always be validated. + If you have previously validated the static public key, use validateStaticOtherPublicKey=false to save time. + \pre size of agreedValue == AgreedValueLength() + \pre length of staticPrivateKey == StaticPrivateKeyLength() + \pre length of ephemeralPrivateKey == EphemeralPrivateKeyLength() + \pre length of staticOtherPublicKey == StaticPublicKeyLength() + \pre length of ephemeralOtherPublicKey == EphemeralPublicKeyLength() + */ + bool Agree(byte *agreedValue, + const byte *staticPrivateKey, const byte *ephemeralPrivateKey, + const byte *staticOtherPublicKey, const byte *ephemeralOtherPublicKey, + bool validateStaticOtherPublicKey=true) const + { + byte *XX = NULL, *YY = NULL, *AA = NULL, *BB = NULL; + size_t xxs = 0, yys = 0, aas = 0, bbs = 0; + + // Depending on the role, this will hold either A's or B's static + // (long term) public key. AA or BB will then point into tt. + SecByteBlock tt(StaticPublicKeyLength()); + + try + { + const DL_GroupParameters ¶ms = GetAbstractGroupParameters(); + + if(m_role == RoleServer) + { + Integer b(staticPrivateKey, StaticPrivateKeyLength()); + Element B = params.ExponentiateBase(b); + params.EncodeElement(true, B, tt); + + XX = const_cast(ephemeralOtherPublicKey); + xxs = EphemeralPublicKeyLength(); + YY = const_cast(ephemeralPrivateKey) + StaticPrivateKeyLength(); + yys = EphemeralPublicKeyLength(); + AA = const_cast(staticOtherPublicKey); + aas = StaticPublicKeyLength(); + BB = tt.BytePtr(); + bbs = tt.SizeInBytes(); + } + else if(m_role == RoleClient) + { + Integer a(staticPrivateKey, StaticPrivateKeyLength()); + Element A = params.ExponentiateBase(a); + params.EncodeElement(true, A, tt); + + XX = const_cast(ephemeralPrivateKey) + StaticPrivateKeyLength(); + xxs = EphemeralPublicKeyLength(); + YY = const_cast(ephemeralOtherPublicKey); + yys = EphemeralPublicKeyLength(); + AA = tt.BytePtr(); + aas = tt.SizeInBytes(); + BB = const_cast(staticOtherPublicKey); + bbs = StaticPublicKeyLength(); + } + else + { + assert(0); + return false; + } + + // 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, NULL)) + 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, NULL)) + return false; + +// const Integer& p = params.GetGroupOrder(); // not used, remove later + 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(NULL, XX, xxs, BB, bbs, dd.BytePtr(), dd.SizeInBytes()); + d.Decode(dd.BytePtr(), dd.SizeInBytes()); + + // Compute $e = \hat{H}(Y, \hat{A})$ + Hash(NULL, YY, yys, AA, aas, ee.BytePtr(), ee.SizeInBytes()); + e.Decode(ee.BytePtr(), ee.SizeInBytes()); + + Element sigma; + if(m_role == RoleServer) + { + Integer y(ephemeralPrivateKey, StaticPrivateKeyLength()); + Integer b(staticPrivateKey, StaticPrivateKeyLength()); + Integer s_B = (y + e * b) % q; + + Element A = params.DecodeElement(AA, false); + Element X = params.DecodeElement(XX, false); + + Element t1 = params.ExponentiateElement(A, d); + Element t2 = m_groupParameters.MultiplyElements(X, t1); + + // $\sigma_B}=(X \cdot A^{d})^{s_B} + sigma = params.ExponentiateElement(t2, s_B); + } + else + { + Integer x(ephemeralPrivateKey, StaticPrivateKeyLength()); + Integer a(staticPrivateKey, StaticPrivateKeyLength()); + Integer s_A = (x + d * a) % q; + + 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); + + // $\sigma_A}=(Y \cdot B^{e})^{s_A} + sigma = params.ExponentiateElement(t2, s_A); + } + Hash(&sigma, NULL, 0, NULL, 0, agreedValue, AgreedValueLength()); + } + catch (DL_BadElement &) + { + return false; + } + return true; + } + +private: + + // The paper uses Initiator and Recipient - make it classical. + enum KeyAgreementRole{ RoleServer = 1, RoleClient }; + + DL_GroupParameters & AccessAbstractGroupParameters() {return m_groupParameters;} + const DL_GroupParameters & GetAbstractGroupParameters() const{return m_groupParameters;} + + KeyAgreementRole m_role; + GroupParameters m_groupParameters; +}; + +//! Hashed Menezes-Qu-Vanstone in GF(p) with key validation, +/*! HMQV: A High-Performance Secure Diffie-Hellman Protocol + Note: this implements HMQV only. HMQV-C (with Key Confirmation) will be provided separately. +*/ +typedef HMQV_Domain HashedMQV; + +NAMESPACE_END + +#endif diff --git a/validat2.cpp b/validat2.cpp index 32885985..ec553a28 100644 --- a/validat2.cpp +++ b/validat2.cpp @@ -18,6 +18,8 @@ #include "dsa.h" #include "dh.h" #include "mqv.h" +#include "hmqv.h" +#include "fhmqv.h" #include "luc.h" #include "xtrcrypt.h" #include "rabin.h" @@ -384,6 +386,240 @@ bool ValidateMQV() return AuthenticatedKeyAgreementValidate(mqv); } +bool ValidateHMQV() +{ + std::cout << "\nHMQV validation suite running...\n\n"; + + //HMQV< ECP >::Domain hmqvB(false /*server*/); + HMQV256 hmqvB(false); + FileSource f256("TestData/hmqv256.dat", true, new HexDecoder()); + FileSource f384("TestData/hmqv384.dat", true, new HexDecoder()); + FileSource f512("TestData/hmqv512.dat", true, new HexDecoder()); + hmqvB.AccessGroupParameters().BERDecode(f256); + + std::cout << "HMQV with NIST P-256 and SHA-256:" << std::endl; + + if (hmqvB.GetCryptoParameters().Validate(GlobalRNG(), 3)) + std::cout << "passed authenticated key agreement domain parameters validation (server)" << std::endl; + else + { + std::cout << "FAILED authenticated key agreement domain parameters invalid (server)" << std::endl; + return false; + } + + const OID oid = ASN1::secp256r1(); + HMQV< ECP >::Domain hmqvA(oid, true /*client*/); + + if (hmqvA.GetCryptoParameters().Validate(GlobalRNG(), 3)) + std::cout << "passed authenticated key agreement domain parameters validation (client)" << std::endl; + else + { + std::cout << "FAILED authenticated key agreement domain parameters invalid (client)" << std::endl; + return false; + } + + SecByteBlock sprivA(hmqvA.StaticPrivateKeyLength()), sprivB(hmqvB.StaticPrivateKeyLength()); + SecByteBlock eprivA(hmqvA.EphemeralPrivateKeyLength()), eprivB(hmqvB.EphemeralPrivateKeyLength()); + SecByteBlock spubA(hmqvA.StaticPublicKeyLength()), spubB(hmqvB.StaticPublicKeyLength()); + SecByteBlock epubA(hmqvA.EphemeralPublicKeyLength()), epubB(hmqvB.EphemeralPublicKeyLength()); + SecByteBlock valA(hmqvA.AgreedValueLength()), valB(hmqvB.AgreedValueLength()); + + hmqvA.GenerateStaticKeyPair(GlobalRNG(), sprivA, spubA); + hmqvB.GenerateStaticKeyPair(GlobalRNG(), sprivB, spubB); + hmqvA.GenerateEphemeralKeyPair(GlobalRNG(), eprivA, epubA); + hmqvB.GenerateEphemeralKeyPair(GlobalRNG(), eprivB, epubB); + + memset(valA.begin(), 0x00, valA.size()); + memset(valB.begin(), 0x11, valB.size()); + + if (!(hmqvA.Agree(valA, sprivA, eprivA, spubB, epubB) && hmqvB.Agree(valB, sprivB, eprivB, spubA, epubA))) + { + std::cout << "FAILED authenticated key agreement failed" << std::endl; + return false; + } + + if (memcmp(valA.begin(), valB.begin(), hmqvA.AgreedValueLength())) + { + std::cout << "FAILED authenticated agreed values not equal" << std::endl; + return false; + } + + std::cout << "passed authenticated key agreement" << std::endl; + + // Now test HMQV with NIST P-384 curve and SHA384 hash + std::cout << endl; + std::cout << "HMQV with NIST P-384 and SHA-384:" << std::endl; + + HMQV384 hmqvB384(false); + hmqvB384.AccessGroupParameters().BERDecode(f384); + + if (hmqvB384.GetCryptoParameters().Validate(GlobalRNG(), 3)) + std::cout << "passed authenticated key agreement domain parameters validation (server)" << std::endl; + else + { + std::cout << "FAILED authenticated key agreement domain parameters invalid (server)" << std::endl; + return false; + } + + const OID oid384 = ASN1::secp384r1(); + HMQV384 hmqvA384(oid384, true /*client*/); + + if (hmqvA384.GetCryptoParameters().Validate(GlobalRNG(), 3)) + std::cout << "passed authenticated key agreement domain parameters validation (client)" << std::endl; + else + { + std::cout << "FAILED authenticated key agreement domain parameters invalid (client)" << std::endl; + return false; + } + + SecByteBlock sprivA384(hmqvA384.StaticPrivateKeyLength()), sprivB384(hmqvB384.StaticPrivateKeyLength()); + SecByteBlock eprivA384(hmqvA384.EphemeralPrivateKeyLength()), eprivB384(hmqvB384.EphemeralPrivateKeyLength()); + SecByteBlock spubA384(hmqvA384.StaticPublicKeyLength()), spubB384(hmqvB384.StaticPublicKeyLength()); + SecByteBlock epubA384(hmqvA384.EphemeralPublicKeyLength()), epubB384(hmqvB384.EphemeralPublicKeyLength()); + SecByteBlock valA384(hmqvA384.AgreedValueLength()), valB384(hmqvB384.AgreedValueLength()); + + hmqvA384.GenerateStaticKeyPair(GlobalRNG(), sprivA384, spubA384); + hmqvB384.GenerateStaticKeyPair(GlobalRNG(), sprivB384, spubB384); + hmqvA384.GenerateEphemeralKeyPair(GlobalRNG(), eprivA384, epubA384); + hmqvB384.GenerateEphemeralKeyPair(GlobalRNG(), eprivB384, epubB384); + + memset(valA384.begin(), 0x00, valA384.size()); + memset(valB384.begin(), 0x11, valB384.size()); + + if (!(hmqvA384.Agree(valA384, sprivA384, eprivA384, spubB384, epubB384) && hmqvB384.Agree(valB384, sprivB384, eprivB384, spubA384, epubA384))) + { + std::cout << "FAILED authenticated key agreement failed" << std::endl; + return false; + } + + if (memcmp(valA384.begin(), valB384.begin(), hmqvA384.AgreedValueLength())) + { + std::cout << "FAILED authenticated agreed values not equal" << std::endl; + return false; + } + + std::cout << "passed authenticated key agreement" << std::endl; + + return true; +} + +bool ValidateFHMQV() +{ + std::cout << "\nFHMQV validation suite running...\n\n"; + + //FHMQV< ECP >::Domain fhmqvB(false /*server*/); + FHMQV256 fhmqvB(false); + FileSource f256("TestData/fhmqv256.dat", true, new HexDecoder()); + FileSource f384("TestData/fhmqv384.dat", true, new HexDecoder()); + FileSource f512("TestData/fhmqv512.dat", true, new HexDecoder()); + fhmqvB.AccessGroupParameters().BERDecode(f256); + + std::cout << "FHMQV with NIST P-256 and SHA-256:" << std::endl; + + if (fhmqvB.GetCryptoParameters().Validate(GlobalRNG(), 3)) + std::cout << "passed authenticated key agreement domain parameters validation (server)" << std::endl; + else + { + std::cout << "FAILED authenticated key agreement domain parameters invalid (server)" << std::endl; + return false; + } + + const OID oid = ASN1::secp256r1(); + FHMQV< ECP >::Domain fhmqvA(oid, true /*client*/); + + if (fhmqvA.GetCryptoParameters().Validate(GlobalRNG(), 3)) + std::cout << "passed authenticated key agreement domain parameters validation (client)" << std::endl; + else + { + std::cout << "FAILED authenticated key agreement domain parameters invalid (client)" << std::endl; + return false; + } + + SecByteBlock sprivA(fhmqvA.StaticPrivateKeyLength()), sprivB(fhmqvB.StaticPrivateKeyLength()); + SecByteBlock eprivA(fhmqvA.EphemeralPrivateKeyLength()), eprivB(fhmqvB.EphemeralPrivateKeyLength()); + SecByteBlock spubA(fhmqvA.StaticPublicKeyLength()), spubB(fhmqvB.StaticPublicKeyLength()); + SecByteBlock epubA(fhmqvA.EphemeralPublicKeyLength()), epubB(fhmqvB.EphemeralPublicKeyLength()); + SecByteBlock valA(fhmqvA.AgreedValueLength()), valB(fhmqvB.AgreedValueLength()); + + fhmqvA.GenerateStaticKeyPair(GlobalRNG(), sprivA, spubA); + fhmqvB.GenerateStaticKeyPair(GlobalRNG(), sprivB, spubB); + fhmqvA.GenerateEphemeralKeyPair(GlobalRNG(), eprivA, epubA); + fhmqvB.GenerateEphemeralKeyPair(GlobalRNG(), eprivB, epubB); + + memset(valA.begin(), 0x00, valA.size()); + memset(valB.begin(), 0x11, valB.size()); + + if (!(fhmqvA.Agree(valA, sprivA, eprivA, spubB, epubB) && fhmqvB.Agree(valB, sprivB, eprivB, spubA, epubA))) + { + std::cout << "FAILED authenticated key agreement failed" << std::endl; + return false; + } + + if (memcmp(valA.begin(), valB.begin(), fhmqvA.AgreedValueLength())) + { + std::cout << "FAILED authenticated agreed values not equal" << std::endl; + return false; + } + + std::cout << "passed authenticated key agreement" << std::endl; + + // Now test FHMQV with NIST P-384 curve and SHA384 hash + std::cout << endl; + std::cout << "FHMQV with NIST P-384 and SHA-384:" << std::endl; + + FHMQV384 fhmqvB384(false); + fhmqvB384.AccessGroupParameters().BERDecode(f384); + + if (fhmqvB384.GetCryptoParameters().Validate(GlobalRNG(), 3)) + std::cout << "passed authenticated key agreement domain parameters validation (server)" << std::endl; + else + { + std::cout << "FAILED authenticated key agreement domain parameters invalid (server)" << std::endl; + return false; + } + + const OID oid384 = ASN1::secp384r1(); + FHMQV384 fhmqvA384(oid384, true /*client*/); + + if (fhmqvA384.GetCryptoParameters().Validate(GlobalRNG(), 3)) + std::cout << "passed authenticated key agreement domain parameters validation (client)" << std::endl; + else + { + std::cout << "FAILED authenticated key agreement domain parameters invalid (client)" << std::endl; + return false; + } + + SecByteBlock sprivA384(fhmqvA384.StaticPrivateKeyLength()), sprivB384(fhmqvB384.StaticPrivateKeyLength()); + SecByteBlock eprivA384(fhmqvA384.EphemeralPrivateKeyLength()), eprivB384(fhmqvB384.EphemeralPrivateKeyLength()); + SecByteBlock spubA384(fhmqvA384.StaticPublicKeyLength()), spubB384(fhmqvB384.StaticPublicKeyLength()); + SecByteBlock epubA384(fhmqvA384.EphemeralPublicKeyLength()), epubB384(fhmqvB384.EphemeralPublicKeyLength()); + SecByteBlock valA384(fhmqvA384.AgreedValueLength()), valB384(fhmqvB384.AgreedValueLength()); + + fhmqvA384.GenerateStaticKeyPair(GlobalRNG(), sprivA384, spubA384); + fhmqvB384.GenerateStaticKeyPair(GlobalRNG(), sprivB384, spubB384); + fhmqvA384.GenerateEphemeralKeyPair(GlobalRNG(), eprivA384, epubA384); + fhmqvB384.GenerateEphemeralKeyPair(GlobalRNG(), eprivB384, epubB384); + + memset(valA384.begin(), 0x00, valA384.size()); + memset(valB384.begin(), 0x11, valB384.size()); + + if (!(fhmqvA384.Agree(valA384, sprivA384, eprivA384, spubB384, epubB384) && fhmqvB384.Agree(valB384, sprivB384, eprivB384, spubA384, epubA384))) + { + std::cout << "FAILED authenticated key agreement failed" << std::endl; + return false; + } + + if (memcmp(valA384.begin(), valB384.begin(), fhmqvA384.AgreedValueLength())) + { + std::cout << "FAILED authenticated agreed values not equal" << std::endl; + return false; + } + + std::cout << "passed authenticated key agreement" << std::endl; + + return true; +} + bool ValidateLUC_DH() { cout << "\nLUC-DH validation suite running...\n\n"; diff --git a/validate.h b/validate.h index 6c0c4793..0a388308 100644 --- a/validate.h +++ b/validate.h @@ -71,6 +71,8 @@ bool ValidateCMAC(); bool ValidateBBS(); bool ValidateDH(); bool ValidateMQV(); +bool ValidateHMQV(); +bool ValidateFHMQV(); bool ValidateRSA(); bool ValidateElGamal(); bool ValidateDLIES();