1 // hmqv.h - written and placed in the public domain by Uri Blumenthal
2 //          Shamelessly based upon Jeffrey Walton's FHMQV and Wei Dai's MQV source files
3 
4 #ifndef CRYPTOPP_HMQV_H
5 #define CRYPTOPP_HMQV_H
6 
7 //! \file hmqv.h
8 //! \brief Classes for Hashed Menezes-Qu-Vanstone key agreement in GF(p)
9 //! \since Crypto++ 5.6.4
10 
11 #include "gfpcrypt.h"
12 #include "algebra.h"
13 #include "sha.h"
14 
NAMESPACE_BEGIN(CryptoPP)15 NAMESPACE_BEGIN(CryptoPP)
16 
17 //! \brief Hashed Menezes-Qu-Vanstone in GF(p)
18 //! \details This implementation follows Hugo Krawczyk's <a href="http://eprint.iacr.org/2005/176">HMQV: A High-Performance
19 //!   Secure Diffie-Hellman Protocol</a>. Note: this implements HMQV only. HMQV-C with Key Confirmation is not provided.
20 //! \sa MQV, HMQV, FHMQV, and AuthenticatedKeyAgreementDomain
21 //! \since Crypto++ 5.6.4
22 template <class GROUP_PARAMETERS, class COFACTOR_OPTION = typename GROUP_PARAMETERS::DefaultCofactorOption, class HASH = SHA512>
23 class HMQV_Domain: public AuthenticatedKeyAgreementDomain
24 {
25 public:
26   typedef GROUP_PARAMETERS GroupParameters;
27   typedef typename GroupParameters::Element Element;
28   typedef HMQV_Domain<GROUP_PARAMETERS, COFACTOR_OPTION, HASH> Domain;
29 
30   virtual ~HMQV_Domain() {}
31 
32   HMQV_Domain(bool clientRole = true): m_role(clientRole ? RoleClient : RoleServer) {}
33 
34   HMQV_Domain(const GroupParameters &params, bool clientRole = true)
35     : m_role(clientRole ? RoleClient : RoleServer), m_groupParameters(params) {}
36 
37   HMQV_Domain(BufferedTransformation &bt, bool clientRole = true)
38     : m_role(clientRole ? RoleClient : RoleServer)
39   {m_groupParameters.BERDecode(bt);}
40 
41   template <class T1>
42   HMQV_Domain(T1 v1, bool clientRole = true)
43     : m_role(clientRole ? RoleClient : RoleServer)
44   {m_groupParameters.Initialize(v1);}
45 
46   template <class T1, class T2>
47   HMQV_Domain(T1 v1, T2 v2, bool clientRole = true)
48     : m_role(clientRole ? RoleClient : RoleServer)
49   {m_groupParameters.Initialize(v1, v2);}
50 
51   template <class T1, class T2, class T3>
52   HMQV_Domain(T1 v1, T2 v2, T3 v3, bool clientRole = true)
53     : m_role(clientRole ? RoleClient : RoleServer)
54   {m_groupParameters.Initialize(v1, v2, v3);}
55 
56   template <class T1, class T2, class T3, class T4>
57   HMQV_Domain(T1 v1, T2 v2, T3 v3, T4 v4, bool clientRole = true)
58     : m_role(clientRole ? RoleClient : RoleServer)
59   {m_groupParameters.Initialize(v1, v2, v3, v4);}
60 
61 public:
62 
63   const GroupParameters & GetGroupParameters() const {return m_groupParameters;}
64   GroupParameters & AccessGroupParameters(){return m_groupParameters;}
65 
66   CryptoParameters & AccessCryptoParameters(){return AccessAbstractGroupParameters();}
67 
68   //! return length of agreed value produced
69   unsigned int AgreedValueLength() const {return GetAbstractGroupParameters().GetEncodedElementSize(false);}
70   //! return length of static private keys in this domain
71   unsigned int StaticPrivateKeyLength() const {return GetAbstractGroupParameters().GetSubgroupOrder().ByteCount();}
72   //! return length of static public keys in this domain
73   unsigned int StaticPublicKeyLength() const{return GetAbstractGroupParameters().GetEncodedElementSize(true);}
74 
75   //! generate static private key
76   /*! \pre size of privateKey == PrivateStaticKeyLength() */
77   void GenerateStaticPrivateKey(RandomNumberGenerator &rng, byte *privateKey) const
78   {
79     Integer x(rng, Integer::One(), GetAbstractGroupParameters().GetMaxExponent());
80     x.Encode(privateKey, StaticPrivateKeyLength());
81   }
82 
83   //! generate static public key
84   /*! \pre size of publicKey == PublicStaticKeyLength() */
85   void GenerateStaticPublicKey(RandomNumberGenerator &rng, const byte *privateKey, byte *publicKey) const
86   {
87     CRYPTOPP_UNUSED(rng);
88     const DL_GroupParameters<Element> &params = GetAbstractGroupParameters();
89     Integer x(privateKey, StaticPrivateKeyLength());
90     Element y = params.ExponentiateBase(x);
91     params.EncodeElement(true, y, publicKey);
92   }
93 
94   unsigned int EphemeralPrivateKeyLength() const {return StaticPrivateKeyLength() + StaticPublicKeyLength();}
95   unsigned int EphemeralPublicKeyLength() const{return StaticPublicKeyLength();}
96 
97   //! return length of ephemeral private keys in this domain
98   void GenerateEphemeralPrivateKey(RandomNumberGenerator &rng, byte *privateKey) const
99   {
100     const DL_GroupParameters<Element> &params = GetAbstractGroupParameters();
101     Integer x(rng, Integer::One(), params.GetMaxExponent());
102     x.Encode(privateKey, StaticPrivateKeyLength());
103     Element y = params.ExponentiateBase(x);
104     params.EncodeElement(true, y, privateKey+StaticPrivateKeyLength());
105   }
106 
107   //! return length of ephemeral public keys in this domain
108   void GenerateEphemeralPublicKey(RandomNumberGenerator &rng, const byte *privateKey, byte *publicKey) const
109   {
110     CRYPTOPP_UNUSED(rng);
111     memcpy(publicKey, privateKey+StaticPrivateKeyLength(), EphemeralPublicKeyLength());
112   }
113 
114   //! derive agreed value from your private keys and couterparty's public keys, return false in case of failure
115   /*! \note The ephemeral public key will always be validated.
116   If you have previously validated the static public key, use validateStaticOtherPublicKey=false to save time.
117   \pre size of agreedValue == AgreedValueLength()
118   \pre length of staticPrivateKey == StaticPrivateKeyLength()
119   \pre length of ephemeralPrivateKey == EphemeralPrivateKeyLength()
120   \pre length of staticOtherPublicKey == StaticPublicKeyLength()
121   \pre length of ephemeralOtherPublicKey == EphemeralPublicKeyLength()
122   */
123   bool Agree(byte *agreedValue,
124     const byte *staticPrivateKey, const byte *ephemeralPrivateKey,
125     const byte *staticOtherPublicKey, const byte *ephemeralOtherPublicKey,
126     bool validateStaticOtherPublicKey=true) const
127   {
128     byte *XX = NULL, *YY = NULL, *AA = NULL, *BB = NULL;
129     size_t xxs = 0, yys = 0, aas = 0, bbs = 0;
130 
131     // Depending on the role, this will hold either A's or B's static
132     // (long term) public key. AA or BB will then point into tt.
133     SecByteBlock tt(StaticPublicKeyLength());
134 
135     try
136     {
137       const DL_GroupParameters<Element> &params = GetAbstractGroupParameters();
138 
139       if(m_role == RoleServer)
140       {
141         Integer b(staticPrivateKey, StaticPrivateKeyLength());
142         Element B = params.ExponentiateBase(b);
143         params.EncodeElement(true, B, tt);
144 
145         XX = const_cast<byte*>(ephemeralOtherPublicKey);
146         xxs = EphemeralPublicKeyLength();
147         YY = const_cast<byte*>(ephemeralPrivateKey) + StaticPrivateKeyLength();
148         yys = EphemeralPublicKeyLength();
149         AA = const_cast<byte*>(staticOtherPublicKey);
150         aas = StaticPublicKeyLength();
151         BB = tt.BytePtr();
152         bbs = tt.SizeInBytes();
153       }
154       else if(m_role == RoleClient)
155       {
156         Integer a(staticPrivateKey, StaticPrivateKeyLength());
157         Element A = params.ExponentiateBase(a);
158         params.EncodeElement(true, A, tt);
159 
160         XX = const_cast<byte*>(ephemeralPrivateKey) + StaticPrivateKeyLength();
161         xxs = EphemeralPublicKeyLength();
162         YY = const_cast<byte*>(ephemeralOtherPublicKey);
163         yys = EphemeralPublicKeyLength();
164         AA = tt.BytePtr();
165         aas = tt.SizeInBytes();
166         BB = const_cast<byte*>(staticOtherPublicKey);
167         bbs = StaticPublicKeyLength();
168       }
169       else
170       {
171         CRYPTOPP_ASSERT(0);
172         return false;
173       }
174 
175       // DecodeElement calls ValidateElement at level 1. Level 1 only calls
176       // VerifyPoint to ensure the element is in G*. If the other's PublicKey is
177       // requested to be validated, we manually call ValidateElement at level 3.
178       Element VV1 = params.DecodeElement(staticOtherPublicKey, false);
179       if(!params.ValidateElement(validateStaticOtherPublicKey ? 3 : 1, VV1, NULL))
180         return false;
181 
182       // DecodeElement calls ValidateElement at level 1. Level 1 only calls
183       // VerifyPoint to ensure the element is in G*. Crank it up.
184       Element VV2 = params.DecodeElement(ephemeralOtherPublicKey, false);
185       if(!params.ValidateElement(3, VV2, NULL))
186         return false;
187 
188       // const Integer& p = params.GetGroupOrder(); // not used, remove later
189       const Integer& q = params.GetSubgroupOrder();
190       const unsigned int len /*bytes*/ = (((q.BitCount()+1)/2 +7)/8);
191 
192       Integer d, e;
193       SecByteBlock dd(len), ee(len);
194 
195       // Compute $d = \hat{H}(X, \hat{B})$
196       Hash(NULL, XX, xxs, BB, bbs, dd.BytePtr(), dd.SizeInBytes());
197       d.Decode(dd.BytePtr(), dd.SizeInBytes());
198 
199       // Compute $e = \hat{H}(Y, \hat{A})$
200       Hash(NULL, YY, yys, AA, aas, ee.BytePtr(), ee.SizeInBytes());
201       e.Decode(ee.BytePtr(), ee.SizeInBytes());
202 
203       Element sigma;
204       if(m_role == RoleServer)
205       {
206         Integer y(ephemeralPrivateKey, StaticPrivateKeyLength());
207         Integer b(staticPrivateKey, StaticPrivateKeyLength());
208         Integer s_B = (y + e * b) % q;
209 
210         Element A = params.DecodeElement(AA, false);
211         Element X = params.DecodeElement(XX, false);
212 
213         Element t1 = params.ExponentiateElement(A, d);
214         Element t2 = m_groupParameters.MultiplyElements(X, t1);
215 
216         // $\sigma_B}=(X \cdot A^{d})^{s_B}
217         sigma = params.ExponentiateElement(t2, s_B);
218       }
219       else
220       {
221         Integer x(ephemeralPrivateKey, StaticPrivateKeyLength());
222         Integer a(staticPrivateKey, StaticPrivateKeyLength());
223         Integer s_A = (x + d * a) % q;
224 
225         Element B = params.DecodeElement(BB, false);
226         Element Y = params.DecodeElement(YY, false);
227 
228         Element t1 = params.ExponentiateElement(B, e);
229         Element t2 = m_groupParameters.MultiplyElements(Y, t1);
230 
231         // $\sigma_A}=(Y \cdot B^{e})^{s_A}
232         sigma = params.ExponentiateElement(t2, s_A);
233       }
234       Hash(&sigma, NULL, 0, NULL, 0, agreedValue, AgreedValueLength());
235     }
236     catch (DL_BadElement &)
237     {
238       return false;
239     }
240     return true;
241   }
242 
243 protected:
244   // Hash invocation by client and server differ only in what keys
245   // each provides.
246 
247   inline void Hash(const Element* sigma,
248     const byte* e1, size_t e1len, // Ephemeral key and key length
249     const byte* s1, size_t s1len, // Static key and key length
250     byte* digest, size_t dlen) const
251   {
252     HASH hash;
253     size_t idx = 0, req = dlen;
254     size_t blk = STDMIN(dlen, (size_t)HASH::DIGESTSIZE);
255 
256     if(sigma)
257     {
258       if (e1len != 0 || s1len != 0) {
259         CRYPTOPP_ASSERT(0);
260       }
261       Integer x = GetAbstractGroupParameters().ConvertElementToInteger(*sigma);
262       SecByteBlock sbb(x.MinEncodedSize());
263       x.Encode(sbb.BytePtr(), sbb.SizeInBytes());
264       hash.Update(sbb.BytePtr(), sbb.SizeInBytes());
265     } else {
266       if (e1len == 0 || s1len == 0) {
267         CRYPTOPP_ASSERT(0);
268       }
269       hash.Update(e1, e1len);
270       hash.Update(s1, s1len);
271     }
272 
273     hash.TruncatedFinal(digest, blk);
274     req -= blk;
275 
276     // All this to catch tail bytes for large curves and small hashes
277     while(req != 0)
278     {
279       hash.Update(&digest[idx], (size_t)HASH::DIGESTSIZE);
280 
281       idx += (size_t)HASH::DIGESTSIZE;
282       blk = STDMIN(req, (size_t)HASH::DIGESTSIZE);
283       hash.TruncatedFinal(&digest[idx], blk);
284 
285       req -= blk;
286     }
287   }
288 
289 private:
290 
291   // The paper uses Initiator and Recipient - make it classical.
292   enum KeyAgreementRole{ RoleServer = 1, RoleClient };
293 
294   DL_GroupParameters<Element> & AccessAbstractGroupParameters() {return m_groupParameters;}
295   const DL_GroupParameters<Element> & GetAbstractGroupParameters() const{return m_groupParameters;}
296 
297   GroupParameters m_groupParameters;
298   KeyAgreementRole m_role;
299 };
300 
301 //! \brief Hashed Menezes-Qu-Vanstone in GF(p)
302 //! \details This implementation follows Hugo Krawczyk's <a href="http://eprint.iacr.org/2005/176">HMQV: A High-Performance
303 //!   Secure Diffie-Hellman Protocol</a>. Note: this implements HMQV only. HMQV-C with Key Confirmation is not provided.
304 //! \sa HMQV, MQV_Domain, FHMQV_Domain, AuthenticatedKeyAgreementDomain
305 //! \since Crypto++ 5.6.4
306 typedef HMQV_Domain<DL_GroupParameters_GFP_DefaultSafePrime> HMQV;
307 
308 NAMESPACE_END
309 
310 #endif
311