ext-cryptopp/hmqv.h

417 lines
18 KiB
C
Raw Normal View History

2016-07-13 19:47:58 +00:00
// hmqv.h - written and placed in the public domain by Uri Blumenthal
2019-08-03 23:22:17 +00:00
// Shamelessly based upon Wei Dai's MQV source files
2016-07-13 19:47:58 +00:00
#ifndef CRYPTOPP_HMQV_H
#define CRYPTOPP_HMQV_H
/// \file hmqv.h
/// \brief Classes for Hashed Menezes-Qu-Vanstone key agreement in GF(p)
/// \since Crypto++ 5.6.4
2016-07-13 19:47:58 +00:00
#include "gfpcrypt.h"
#include "algebra.h"
#include "sha.h"
NAMESPACE_BEGIN(CryptoPP)
/// \brief Hashed Menezes-Qu-Vanstone in GF(p)
/// \details This implementation follows Hugo Krawczyk's <a href="http://eprint.iacr.org/2005/176">HMQV: A High-Performance
/// Secure Diffie-Hellman Protocol</a>. Note: this implements HMQV only. HMQV-C with Key Confirmation is not provided.
/// \sa MQV, HMQV, FHMQV, and AuthenticatedKeyAgreementDomain
/// \since Crypto++ 5.6.4
template <class GROUP_PARAMETERS, class COFACTOR_OPTION = typename GROUP_PARAMETERS::DefaultCofactorOption, class HASH = SHA512>
2016-07-13 19:47:58 +00:00
class HMQV_Domain: public AuthenticatedKeyAgreementDomain
{
public:
typedef GROUP_PARAMETERS GroupParameters;
typedef typename GroupParameters::Element Element;
typedef HMQV_Domain<GROUP_PARAMETERS, COFACTOR_OPTION, HASH> Domain;
virtual ~HMQV_Domain() {}
2019-08-03 23:22:17 +00:00
/// \brief Construct a HMQV domain
2019-08-04 01:58:26 +00:00
/// \param clientRole flag indicating initiator or recipient
2019-08-03 23:22:17 +00:00
/// \details <tt>clientRole = true</tt> indicates initiator, and
/// <tt>clientRole = false</tt> indicates recipient or server.
HMQV_Domain(bool clientRole = true)
: m_role(clientRole ? RoleClient : RoleServer) {}
/// \brief Construct a HMQV domain
/// \param params group parameters and options
2019-08-04 01:58:26 +00:00
/// \param clientRole flag indicating initiator or recipient
2019-08-03 23:22:17 +00:00
/// \details <tt>clientRole = true</tt> indicates initiator, and
/// <tt>clientRole = false</tt> indicates recipient or server.
2016-07-13 19:47:58 +00:00
HMQV_Domain(const GroupParameters &params, bool clientRole = true)
: m_role(clientRole ? RoleClient : RoleServer), m_groupParameters(params) {}
2019-08-03 23:22:17 +00:00
/// \brief Construct a HMQV domain
/// \param bt BufferedTransformation with group parameters and options
2019-08-04 01:58:26 +00:00
/// \param clientRole flag indicating initiator or recipient
2019-08-03 23:22:17 +00:00
/// \details <tt>clientRole = true</tt> indicates initiator, and
/// <tt>clientRole = false</tt> indicates recipient or server.
2016-07-13 19:47:58 +00:00
HMQV_Domain(BufferedTransformation &bt, bool clientRole = true)
: m_role(clientRole ? RoleClient : RoleServer)
2019-08-03 23:22:17 +00:00
{m_groupParameters.BERDecode(bt);}
/// \brief Construct a HMQV domain
/// \tparam T1 template parameter used as a constructor parameter
/// \param v1 first parameter
2019-08-04 01:58:26 +00:00
/// \param clientRole flag indicating initiator or recipient
2019-08-04 02:05:01 +00:00
/// \details v1 is passed directly to the GROUP_PARAMETERS object.
2019-08-03 23:22:17 +00:00
/// \details <tt>clientRole = true</tt> indicates initiator, and
/// <tt>clientRole = false</tt> indicates recipient or server.
2016-07-13 19:47:58 +00:00
template <class T1>
HMQV_Domain(T1 v1, bool clientRole = true)
: m_role(clientRole ? RoleClient : RoleServer)
2019-08-03 23:22:17 +00:00
{m_groupParameters.Initialize(v1);}
/// \brief Construct a HMQV domain
/// \tparam T1 template parameter used as a constructor parameter
/// \tparam T2 template parameter used as a constructor parameter
/// \param v1 first parameter
/// \param v2 second parameter
2019-08-04 01:58:26 +00:00
/// \param clientRole flag indicating initiator or recipient
2019-08-03 23:22:17 +00:00
/// \details v1 and v2 are passed directly to the GROUP_PARAMETERS object.
/// \details <tt>clientRole = true</tt> indicates initiator, and
/// <tt>clientRole = false</tt> indicates recipient or server.
2016-07-13 19:47:58 +00:00
template <class T1, class T2>
HMQV_Domain(T1 v1, T2 v2, bool clientRole = true)
: m_role(clientRole ? RoleClient : RoleServer)
2019-08-03 23:22:17 +00:00
{m_groupParameters.Initialize(v1, v2);}
/// \brief Construct a HMQV domain
/// \tparam T1 template parameter used as a constructor parameter
/// \tparam T2 template parameter used as a constructor parameter
/// \tparam T3 template parameter used as a constructor parameter
/// \param v1 first parameter
/// \param v2 second parameter
/// \param v3 third parameter
2019-08-04 01:58:26 +00:00
/// \param clientRole flag indicating initiator or recipient
2019-08-03 23:22:17 +00:00
/// \details v1, v2 and v3 are passed directly to the GROUP_PARAMETERS object.
/// \details <tt>clientRole = true</tt> indicates initiator, and
/// <tt>clientRole = false</tt> indicates recipient or server.
2016-07-13 19:47:58 +00:00
template <class T1, class T2, class T3>
HMQV_Domain(T1 v1, T2 v2, T3 v3, bool clientRole = true)
: m_role(clientRole ? RoleClient : RoleServer)
2019-08-03 23:22:17 +00:00
{m_groupParameters.Initialize(v1, v2, v3);}
/// \brief Construct a HMQV domain
/// \tparam T1 template parameter used as a constructor parameter
/// \tparam T2 template parameter used as a constructor parameter
/// \tparam T3 template parameter used as a constructor parameter
/// \tparam T4 template parameter used as a constructor parameter
/// \param v1 first parameter
/// \param v2 second parameter
/// \param v3 third parameter
/// \param v4 third parameter
2019-08-04 01:58:26 +00:00
/// \param clientRole flag indicating initiator or recipient
2019-08-03 23:22:17 +00:00
/// \details v1, v2, v3 and v4 are passed directly to the GROUP_PARAMETERS object.
/// \details <tt>clientRole = true</tt> indicates initiator, and
/// <tt>clientRole = false</tt> indicates recipient or server.
2016-07-13 19:47:58 +00:00
template <class T1, class T2, class T3, class T4>
HMQV_Domain(T1 v1, T2 v2, T3 v3, T4 v4, bool clientRole = true)
: m_role(clientRole ? RoleClient : RoleServer)
2019-08-03 23:22:17 +00:00
{m_groupParameters.Initialize(v1, v2, v3, v4);}
2016-07-13 19:47:58 +00:00
public:
2019-08-03 23:22:17 +00:00
/// \brief Retrieves the group parameters for this domain
/// \return the group parameters for this domain as a const reference
2016-07-13 19:47:58 +00:00
const GroupParameters & GetGroupParameters() const {return m_groupParameters;}
2019-08-03 23:22:17 +00:00
/// \brief Retrieves the group parameters for this domain
/// \return the group parameters for this domain as a non-const reference
GroupParameters & AccessGroupParameters() {return m_groupParameters;}
/// \brief Retrieves the crypto parameters for this domain
/// \return the crypto parameters for this domain as a non-const reference
CryptoParameters & AccessCryptoParameters() {return AccessAbstractGroupParameters();}
/// \brief Provides the size of the agreed value
/// \return size of agreed value produced in this domain
/// \details The length is calculated using <tt>GetEncodedElementSize(false)</tt>,
/// which means the element is encoded in a non-reversible format. A
/// non-reversible format means its a raw byte array, and it lacks presentation
/// format like an ASN.1 BIT_STRING or OCTET_STRING.
unsigned int AgreedValueLength() const
{return GetAbstractGroupParameters().GetEncodedElementSize(false);}
/// \brief Provides the size of the static private key
/// \return size of static private keys in this domain
/// \details The length is calculated using the byte count of the subgroup order.
unsigned int StaticPrivateKeyLength() const
{return GetAbstractGroupParameters().GetSubgroupOrder().ByteCount();}
/// \brief Provides the size of the static public key
/// \return size of static public keys in this domain
/// \details The length is calculated using <tt>GetEncodedElementSize(true)</tt>,
/// which means the element is encoded in a reversible format. A reversible
/// format means it has a presentation format, and its an ANS.1 encoded element
/// or point.
unsigned int StaticPublicKeyLength() const
{return GetAbstractGroupParameters().GetEncodedElementSize(true);}
/// \brief Generate static private key in this domain
/// \param rng a RandomNumberGenerator derived class
/// \param privateKey a byte buffer for the generated private key in this domain
/// \details The private key is a random scalar used as an exponent in the range
/// <tt>[1,MaxExponent()]</tt>.
/// \pre <tt>COUNTOF(privateKey) == PrivateStaticKeyLength()</tt>
2016-07-13 19:47:58 +00:00
void GenerateStaticPrivateKey(RandomNumberGenerator &rng, byte *privateKey) const
{
Integer x(rng, Integer::One(), GetAbstractGroupParameters().GetMaxExponent());
x.Encode(privateKey, StaticPrivateKeyLength());
}
2019-08-03 23:22:17 +00:00
/// \brief Generate a static public key from a private key in this domain
/// \param rng a RandomNumberGenerator derived class
/// \param privateKey a byte buffer with the previously generated private key
/// \param publicKey a byte buffer for the generated public key in this domain
/// \details The public key is an element or point on the curve, and its stored
/// in a revrsible format. A reversible format means it has a presentation
/// format, and its an ANS.1 encoded element or point.
/// \pre <tt>COUNTOF(publicKey) == PublicStaticKeyLength()</tt>
2016-07-13 19:47:58 +00:00
void GenerateStaticPublicKey(RandomNumberGenerator &rng, const byte *privateKey, byte *publicKey) const
{
CRYPTOPP_UNUSED(rng);
2016-07-13 19:47:58 +00:00
const DL_GroupParameters<Element> &params = 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
2016-07-13 19:47:58 +00:00
void GenerateEphemeralPrivateKey(RandomNumberGenerator &rng, byte *privateKey) const
{
const DL_GroupParameters<Element> &params = 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
2016-07-13 19:47:58 +00:00
void GenerateEphemeralPublicKey(RandomNumberGenerator &rng, const byte *privateKey, byte *publicKey) const
{
CRYPTOPP_UNUSED(rng);
2016-07-13 19:47:58 +00:00
memcpy(publicKey, privateKey+StaticPrivateKeyLength(), EphemeralPublicKeyLength());
}
2019-08-04 02:33:46 +00:00
/// \brief Derive shared secret from your private keys and couterparty's public keys
2019-08-03 23:22:17 +00:00
/// \param agreedValue the shared secret
/// \param staticPrivateKey your long term private key
/// \param ephemeralPrivateKey your ephemeral private key
/// \param staticOtherPublicKey couterparty's long term public key
2019-08-04 02:08:07 +00:00
/// \param ephemeralOtherPublicKey couterparty's ephemeral public key
2019-08-03 23:22:17 +00:00
/// \param validateStaticOtherPublicKey flag indicating validation
/// \details Agree() performs the authenticated key agreement. Each instance
/// or run of the protocol should use a new ephemeral key pair.
/// \details The other's ephemeral public key will always be validated at
/// Level 1 to ensure it is a point on the curve.
/// <tt>validateStaticOtherPublicKey</tt> determines how thoroughly other's
/// static public key is validated. If you have previously validated the
/// couterparty's static public key, then use
/// <tt>validateStaticOtherPublicKey=false</tt> to save time.
2019-08-04 02:33:46 +00:00
/// \pre <tt>COUNTOF(agreedValue) == AgreedValueLength()
/// \pre <tt>COUNTOF(staticPrivateKey) == StaticPrivateKeyLength()</tt>
/// \pre <tt>COUNTOF(ephemeralPrivateKey) == EphemeralPrivateKeyLength()</tt>
/// \pre <tt>COUNTOF(staticOtherPublicKey) == StaticPublicKeyLength()</tt>
/// \pre <tt>COUNTOF(ephemeralOtherPublicKey) == EphemeralPublicKeyLength()</tt>
2016-07-13 19:47:58 +00:00
bool Agree(byte *agreedValue,
const byte *staticPrivateKey, const byte *ephemeralPrivateKey,
const byte *staticOtherPublicKey, const byte *ephemeralOtherPublicKey,
bool validateStaticOtherPublicKey=true) const
{
2019-08-03 07:24:26 +00:00
const byte *XX = NULLPTR, *YY = NULLPTR, *AA = NULLPTR, *BB = NULLPTR;
2016-07-13 19:47:58 +00:00
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
{
this->GetMaterial().DoQuickSanityCheck();
2016-07-13 19:47:58 +00:00
const DL_GroupParameters<Element> &params = GetAbstractGroupParameters();
if(m_role == RoleServer)
{
Integer b(staticPrivateKey, StaticPrivateKeyLength());
Element B = params.ExponentiateBase(b);
params.EncodeElement(true, B, tt);
2019-08-03 07:24:26 +00:00
XX = ephemeralOtherPublicKey;
2016-07-13 19:47:58 +00:00
xxs = EphemeralPublicKeyLength();
2019-08-03 07:24:26 +00:00
YY = ephemeralPrivateKey + StaticPrivateKeyLength();
2016-07-13 19:47:58 +00:00
yys = EphemeralPublicKeyLength();
2019-08-03 07:24:26 +00:00
AA = staticOtherPublicKey;
2016-07-13 19:47:58 +00:00
aas = StaticPublicKeyLength();
BB = tt.BytePtr();
bbs = tt.SizeInBytes();
}
2019-08-04 01:58:26 +00:00
else
2016-07-13 19:47:58 +00:00
{
Integer a(staticPrivateKey, StaticPrivateKeyLength());
Element A = params.ExponentiateBase(a);
params.EncodeElement(true, A, tt);
2019-08-03 07:24:26 +00:00
XX = ephemeralPrivateKey + StaticPrivateKeyLength();
2016-07-13 19:47:58 +00:00
xxs = EphemeralPublicKeyLength();
2019-08-03 07:24:26 +00:00
YY = ephemeralOtherPublicKey;
2016-07-13 19:47:58 +00:00
yys = EphemeralPublicKeyLength();
AA = tt.BytePtr();
aas = tt.SizeInBytes();
2019-08-03 07:24:26 +00:00
BB = staticOtherPublicKey;
2016-07-13 19:47:58 +00:00
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);
2017-03-01 11:10:06 +00:00
if(!params.ValidateElement(validateStaticOtherPublicKey ? 3 : 1, VV1, NULLPTR))
2019-08-03 23:22:17 +00:00
{
CRYPTOPP_ASSERT(0);
2016-07-13 19:47:58 +00:00
return false;
2019-08-03 23:22:17 +00:00
}
2016-07-13 19:47:58 +00:00
// 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);
2017-03-01 11:10:06 +00:00
if(!params.ValidateElement(3, VV2, NULLPTR))
2019-08-03 23:22:17 +00:00
{
CRYPTOPP_ASSERT(0);
2016-07-13 19:47:58 +00:00
return false;
2019-08-03 23:22:17 +00:00
}
2016-07-13 19:47:58 +00:00
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})$
2017-03-01 11:10:06 +00:00
Hash(NULLPTR, XX, xxs, BB, bbs, dd.BytePtr(), dd.SizeInBytes());
2016-07-13 19:47:58 +00:00
d.Decode(dd.BytePtr(), dd.SizeInBytes());
// Compute $e = \hat{H}(Y, \hat{A})$
2017-03-01 11:10:06 +00:00
Hash(NULLPTR, YY, yys, AA, aas, ee.BytePtr(), ee.SizeInBytes());
2016-07-13 19:47:58 +00:00
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);
2016-07-13 22:32:18 +00:00
// $\sigma_B}=(X \cdot A^{d})^{s_B}
2016-07-13 19:47:58 +00:00
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);
2016-07-13 22:32:18 +00:00
// $\sigma_A}=(Y \cdot B^{e})^{s_A}
2016-07-13 19:47:58 +00:00
sigma = params.ExponentiateElement(t2, s_A);
}
2017-03-01 11:10:06 +00:00
Hash(&sigma, NULLPTR, 0, NULLPTR, 0, agreedValue, AgreedValueLength());
2016-07-13 19:47:58 +00:00
}
catch (DL_BadElement &)
{
2019-08-03 23:22:17 +00:00
CRYPTOPP_ASSERT(0);
2016-07-13 19:47:58 +00:00
return false;
}
return true;
}
2016-07-13 22:32:18 +00:00
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 = STDMIN(dlen, (size_t)HASH::DIGESTSIZE);
2016-07-13 22:32:18 +00:00
if(sigma)
{
if (e1len != 0 || s1len != 0) {
CRYPTOPP_ASSERT(0);
2016-07-13 22:32:18 +00:00
}
2019-08-03 23:22:17 +00:00
//SecByteBlock sbb(GetAbstractGroupParameters().GetEncodedElementSize(false));
//GetAbstractGroupParameters().EncodeElement(false, *sigma, sbb);
2016-07-13 22:32:18 +00:00
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) {
CRYPTOPP_ASSERT(0);
2016-07-13 22:32:18 +00:00
}
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 = STDMIN(req, (size_t)HASH::DIGESTSIZE);
2016-07-13 22:32:18 +00:00
hash.TruncatedFinal(&digest[idx], blk);
req -= blk;
}
}
2016-07-13 19:47:58 +00:00
private:
// The paper uses Initiator and Recipient - make it classical.
enum KeyAgreementRole{ RoleServer = 1, RoleClient };
2019-08-03 23:22:17 +00:00
DL_GroupParameters<Element> & AccessAbstractGroupParameters()
{return m_groupParameters;}
const DL_GroupParameters<Element> & GetAbstractGroupParameters() const
{return m_groupParameters;}
2016-07-13 19:47:58 +00:00
GroupParameters m_groupParameters;
2016-07-13 22:32:18 +00:00
KeyAgreementRole m_role;
2016-07-13 19:47:58 +00:00
};
/// \brief Hashed Menezes-Qu-Vanstone in GF(p)
/// \details This implementation follows Hugo Krawczyk's <a href="http://eprint.iacr.org/2005/176">HMQV: A High-Performance
/// Secure Diffie-Hellman Protocol</a>. Note: this implements HMQV only. HMQV-C with Key Confirmation is not provided.
2019-08-03 23:22:17 +00:00
/// \sa HMQV, HMQV_Domain, FHMQV_Domain, AuthenticatedKeyAgreementDomain
/// \since Crypto++ 5.6.4
typedef HMQV_Domain<DL_GroupParameters_GFP_DefaultSafePrime> HMQV;
2016-07-13 19:47:58 +00:00
NAMESPACE_END
#endif