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