1 /**
2 * GOST 34.10-2001
3 * 
4 * Copyright:
5 * (C) 2007 Falko Strenzke, FlexSecure GmbH
6 *             Manuel Hartl, FlexSecure GmbH
7 * (C) 2008-2010 Jack Lloyd
8 * (C) 2014-2015 Etienne Cimon
9 *
10 * License:
11 * Botan is released under the Simplified BSD License (see LICENSE.md)
12 */
13 module botan.pubkey.algo.gost_3410;
14 
15 import botan.constants;
16 static if (BOTAN_HAS_PUBLIC_KEY_CRYPTO && BOTAN_HAS_GOST_34_10_2001):
17 
18 public import botan.pubkey.pubkey;
19 import botan.pubkey.algo.ecc_key;
20 import botan.pubkey.pk_ops;
21 import botan.pubkey.algo.gost_3410;
22 import botan.asn1.der_enc;
23 import botan.asn1.ber_dec;
24 import botan.math.ec_gfp.point_gfp;
25 import botan.rng.rng;
26 import std.algorithm;
27 import memutils.helpers : Embed;
28 
29 struct GOST3410Options // applied to ECPublicKey
30 {
31     enum algoName = "GOST-34.10";
32     enum msgParts = 2;
33 
34     static AlgorithmIdentifier algorithmIdentifier(in ECPublicKey pkey)
35     {
36         //logTrace("Encode algorithmIdentifier x509");
37         Vector!ubyte params = DEREncoder().startCons(ASN1Tag.SEQUENCE)
38                 .encode(OID(pkey.domain().getOid()))
39                 .endCons()
40                 .getContentsUnlocked();
41         
42         return AlgorithmIdentifier(pkey.getOid(), params);
43     }
44 
45     static Vector!ubyte x509SubjectPublicKey(in ECPublicKey pkey)
46     {
47         //logTrace("Encode x509SubjectPublicKey");
48         // Trust CryptoPro to come up with something obnoxious
49         const BigInt x = pkey.publicPoint().getAffineX();
50         const BigInt y = pkey.publicPoint().getAffineY();
51         
52         size_t part_size = max(x.bytes(), y.bytes());
53         
54         Vector!ubyte bits = Vector!ubyte(2*part_size);
55         
56         x.binaryEncode(&bits[part_size - x.bytes()]);
57         y.binaryEncode(&bits[2*part_size - y.bytes()]);
58         
59         // Keys are stored in little endian format (WTF)
60         foreach (size_t i; 0 .. (part_size / 2))
61         {
62             swap(bits[i], bits[part_size-1-i]);
63             swap(bits[part_size+i], bits[2*part_size-1-i]);
64         }
65         
66         return DEREncoder().encode(bits, ASN1Tag.OCTET_STRING).getContentsUnlocked();
67     }
68 }
69 
70 /**
71 * GOST-34.10 Public Key
72 */
73 struct GOST3410PublicKey
74 {
75 public:
76     alias Options = GOST3410Options;
77     __gshared immutable algoName = Options.algoName;
78     /**
79     * Construct a public key from a given public point.
80     *
81     * Params:
82     *  dom_par = the domain parameters associated with this key
83     *  public_point = the public point defining this key
84     */
85     this(const ref ECGroup dom_par, const ref PointGFp public_point) 
86     {
87 		m_owned = true;
88         m_pub = new ECPublicKey(Options(), dom_par, public_point);
89     }
90 
91     /**
92     * Construct from X.509 algorithm id and subject public key bits
93     */
94     this(in AlgorithmIdentifier alg_id, const ref SecureVector!ubyte key_bits)
95     {
96         //logTrace("Decode public key");
97         OID ecc_param_id = OID();
98         
99         // Also includes hash and cipher OIDs... brilliant design guys
100         BERDecoder(alg_id.parameters).startCons(ASN1Tag.SEQUENCE).decode(ecc_param_id);
101         
102         ECGroup domain_params = ECGroup(ecc_param_id);
103         SecureVector!ubyte bits;
104         BERDecoder(key_bits).decode(bits, ASN1Tag.OCTET_STRING);
105         
106         const size_t part_size = bits.length / 2;
107         
108         // Keys are stored in little endian format (WTF)
109         foreach (size_t i; 0 .. (part_size / 2))
110         {
111             swap(bits[i], bits[part_size-1-i]);
112             swap(bits[part_size+i], bits[2*part_size-1-i]);
113         }
114         
115         BigInt x = BigInt(bits.ptr, part_size);
116         BigInt y = BigInt(&bits[part_size], part_size);
117         
118         PointGFp public_point = PointGFp(domain_params.getCurve(), &x, &y);
119 		m_owned = true;
120         m_pub = new ECPublicKey(Options(), domain_params, public_point);
121         assert(public_point.onTheCurve(), "Loaded GOST 34.10 public key is on the curve");
122     }
123 
124     this(PublicKey pkey) { m_pub = cast(ECPublicKey) pkey; }
125 
126     this(PrivateKey pkey) { m_pub = cast(ECPublicKey) pkey; }
127 
128     mixin Embed!(m_pub, m_owned);
129 
130 	bool m_owned;
131     ECPublicKey m_pub;
132 }
133 
134 /**
135 * GOST-34.10 Private Key
136 */
137 struct GOST3410PrivateKey
138 {
139 public:
140     alias Options = GOST3410Options;
141     __gshared immutable algoName = Options.algoName;
142 
143     this(in AlgorithmIdentifier alg_id, const ref SecureVector!ubyte key_bits)
144     {
145 		m_owned = true;
146         m_priv = new ECPrivateKey(Options(), alg_id, key_bits);
147     }
148 
149     /**
150     * Generate a new private key
151     * Params:
152     *  rng = a random number generator
153     *  domain = parameters to used for this key
154     *  x = the private key; if zero, a new random key is generated
155     */
156     this(RandomNumberGenerator rng, const ref ECGroup domain, BigInt x = BigInt(0))
157     {
158 		m_owned = true;
159         m_priv = new ECPrivateKey(Options(), rng, domain, x);
160     }
161 
162     this(PrivateKey pkey) { m_priv = cast(ECPrivateKey) pkey; }
163 
164     mixin Embed!(m_priv, m_owned);
165 
166 	bool m_owned;
167     ECPrivateKey m_priv;
168 }
169 
170 /**
171 * GOST-34.10 signature operation
172 */
173 final class GOST3410SignatureOperation : Signature
174 {
175 public:    
176     this(in PrivateKey pkey) {
177         this(cast(ECPrivateKey) pkey);
178     }
179 
180     this(in GOST3410PrivateKey pkey) {
181         this(pkey.m_priv);
182     }
183 
184     this(in ECPrivateKey gost_3410)
185     {
186         assert(gost_3410.algoName == GOST3410PublicKey.algoName);
187         m_base_point = &gost_3410.domain().getBasePoint();
188         m_order = &gost_3410.domain().getOrder();
189         m_x = &gost_3410.privateValue();
190     }
191 
192     override size_t messageParts() const { return 2; }
193     override size_t messagePartSize() const { return m_order.bytes(); }
194     override size_t maxInputBits() const { return m_order.bits(); }
195 
196     override SecureVector!ubyte sign(const(ubyte)* msg, size_t msg_len,
197                                       RandomNumberGenerator rng)
198     {
199         BigInt k;
200         do
201             k.randomize(rng, m_order.bits()-1);
202         while (k >= *m_order);
203         
204         BigInt e = decodeLittleEndian(msg, msg_len);
205         
206         e %= *m_order;
207         if (e == 0)
208             e = 1;
209         
210         PointGFp k_times_P = (*m_base_point) * &k;
211         
212         assert(k_times_P.onTheCurve(), "GOST 34.10 k*g is on the curve");
213         
214         BigInt r = k_times_P.getAffineX() % (*m_order);
215         
216         auto s_0 = r * m_x;
217         s_0 += k * &e;
218         BigInt s = s_0 % m_order;
219         
220         if (r == 0 || s == 0)
221             throw new InvalidState("GOST 34.10: r == 0 || s == 0");
222         
223         SecureVector!ubyte output = SecureVector!ubyte(2*m_order.bytes());
224         s.binaryEncode(&output[output.length / 2 - s.bytes()]);
225         r.binaryEncode(&output[output.length - r.bytes()]);
226         return output;
227     }
228 
229 private:
230     const PointGFp* m_base_point;
231     const BigInt* m_order;
232     const BigInt* m_x;
233 }
234 
235 /**
236 * GOST-34.10 verification operation
237 */
238 final class GOST3410VerificationOperation : Verification
239 {
240 public:
241     this(in PublicKey pkey) {
242         this(cast(ECPublicKey) pkey);
243     }
244 
245     this(in GOST3410PublicKey pkey) {
246         this(pkey.m_pub);
247     }
248 
249     this(in ECPublicKey gost) 
250     {
251         assert(gost.algoName == GOST3410PublicKey.algoName);
252         m_ec_publickey = gost;
253         m_base_point = &m_ec_publickey.domain().getBasePoint();
254         m_public_point = &m_ec_publickey.publicPoint();
255         m_order = &m_ec_publickey.domain().getOrder();
256     }
257 
258     override size_t messageParts() const { return 2; }
259     override size_t messagePartSize() const { return m_order.bytes(); }
260     override size_t maxInputBits() const { return m_order.bits(); }
261 
262     override bool withRecovery() const { return false; }
263 
264     override SecureVector!ubyte verifyMr(const(ubyte)*, size_t) { throw new InvalidState("Message recovery not supported"); }
265     override bool verify(const(ubyte)* msg, size_t msg_len,
266                          const(ubyte)* sig, size_t sig_len)
267     {
268         if (sig_len != m_order.bytes()*2)
269             return false;
270         
271         BigInt e = decodeLittleEndian(msg, msg_len);
272         
273         BigInt s = BigInt(sig, sig_len / 2);
274         BigInt r = BigInt(sig + sig_len / 2, sig_len / 2);
275         
276         if (r <= 0 || r >= (*m_order) || s <= 0 || s >= (*m_order))
277             return false;
278         
279         e %= (*m_order);
280         if (e == 0)
281             e = 1;
282         
283         BigInt v = inverseMod(&e, m_order);
284         
285         BigInt z1 = (s*v) % (*m_order);
286         BigInt z2 = (-r*v) % (*m_order);
287         
288         PointGFp R = PointGFp.multiExponentiate(*m_base_point, &z1,
289                                                 *m_public_point, &z2);
290         
291         if (R.isZero())
292             return false;
293         
294         return (R.getAffineX() == r);
295     }
296    // const ~this() { destroy(cast(GOST3410VerificationOperation)this); }
297 private:
298     const ECPublicKey m_ec_publickey;
299     const PointGFp* m_base_point;
300     const PointGFp* m_public_point;
301     const BigInt* m_order;
302 }
303 
304 
305 private:
306 
307 BigInt decodeLittleEndian(const(ubyte)* msg, size_t msg_len)
308 {
309     SecureVector!ubyte msg_le = SecureVector!ubyte(msg[0 .. msg_len]);
310     
311     for (size_t i = 0; i != msg_le.length / 2; ++i)
312         swap(msg_le[i], msg_le[msg_le.length-1-i]);
313     
314     return BigInt(msg_le.ptr, msg_le.length);
315 }
316 
317 
318 static if (BOTAN_TEST):
319 
320 import botan.test;
321 import botan.pubkey.test;
322 import botan.rng.auto_rng;
323 import botan.pubkey.pubkey;
324 import botan.asn1.oids;
325 import botan.codec.hex;
326 import core.atomic;
327 import memutils.hashmap;
328 
329 private shared size_t total_tests;
330 
331 size_t testPkKeygen(RandomNumberGenerator rng)
332 {
333     size_t fails;
334     string[] gost_list = ["gost_256A", "secp112r1", "secp128r1", "secp160r1",
335         "secp192r1", "secp224r1", "secp256r1", "secp384r1", "secp521r1"];
336     
337     foreach (gost; gost_list) {
338         atomicOp!"+="(total_tests, 1);
339         auto ec = ECGroup(OIDS.lookup(gost));
340         auto key = GOST3410PrivateKey(rng, ec);
341         key.checkKey(rng, true);
342         fails += validateSaveAndLoad(key, rng);
343     }
344     
345     return fails;
346 }
347 
348 size_t gostVerify(string group_id,
349                    string x,
350                    string hash,
351                    string msg,
352                    string signature)
353 {
354     atomicOp!"+="(total_tests, 1);
355     
356     ECGroup group = ECGroup(OIDS.lookup(group_id));
357     auto x_dec = hexDecode(x);
358     PointGFp public_point = OS2ECP(x_dec, group.getCurve());
359     
360     auto gost = GOST3410PublicKey(group, public_point);
361     
362     const string padding = "EMSA1(" ~ hash ~ ")";
363     
364     PKVerifier v = PKVerifier(gost, padding);
365     
366     if (!v.verifyMessage(hexDecode(msg), hexDecode(signature)))
367         return 1;
368     
369     return 0;
370 }
371 
372 static if (BOTAN_HAS_TESTS && !SKIP_GOST_TEST) unittest
373 {
374     logDebug("Testing gost_3410.d ...");
375     size_t fails = 0;
376 
377 	Unique!AutoSeededRNG rng = new AutoSeededRNG;
378 
379     fails += testPkKeygen(*rng);
380 
381     File ecdsa_sig = File("test_data/pubkey/gost_3410.vec", "r");
382     
383     fails += runTestsBb(ecdsa_sig, "GOST-34.10 Signature", "Signature", true,
384         (ref HashMap!(string, string) m) {
385             return gostVerify(m["Group"], m["Pubkey"], m["Hash"], m["Msg"], m["Signature"]);
386         });
387     
388     testReport("gost_3410", total_tests, fails);
389 }
390