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