1 /**
2 * Elliptic curves over GF(p)
3 *
4 * Copyright:
5 * (C) 2007 Martin Doering, Christoph Ludwig, Falko Strenzke
6 *     2010-2011,2012,2014 Jack Lloyd
7 * (C) 2014-2015 Etienne Cimon
8 *
9 * License:
10 * Botan is released under the Simplified BSD License (see LICENSE.md)
11 */
12 module botan.math.ec_gfp.curve_gfp;
13 
14 import botan.constants;
15 static if (BOTAN_HAS_PUBLIC_KEY_CRYPTO):
16 
17 import botan.math.numbertheory.numthry;
18 import botan.math.mp.mp_types;
19 import botan.math.mp.mp_core;
20 import botan.math.ec_gfp.curve_nistp;
21 import std.algorithm : swap;
22 import botan.constants;
23 import memutils.unique;
24 import std.conv : to;
25 
26 abstract class CurveGFpRepr
27 {
28 public:
29 
30     abstract ref const(BigInt) getP() const;
31     
32     abstract ref const(BigInt) getA() const;
33     
34     abstract ref const(BigInt) getB() const;
35 
36     abstract size_t getPWords() const;
37 
38     /// Returns toCurveRep(getA())
39     abstract ref const(BigInt) getARep() const;
40 
41     /// Returns toCurveRep(getB())
42     abstract ref const(BigInt) getBRep() const;
43 
44     abstract void toCurveRep(ref BigInt x, ref SecureVector!word ws) const;
45 
46     abstract void fromCurveRep(ref BigInt x, ref SecureVector!word ws) const;
47 
48     abstract void curveMul(ref BigInt z, const ref BigInt x, const ref BigInt y, ref SecureVector!word ws) const;
49 
50     abstract void curveSqr(ref BigInt z, const ref BigInt x, ref SecureVector!word ws) const;
51 
52     void normalize(ref BigInt x, ref SecureVector!word ws, size_t bound) const {
53         const BigInt* p = &getP();
54         
55         while(x.isNegative())
56             x += *p;
57         
58         const size_t p_words = getPWords();
59         const word* prime = p.ptr;
60         
61         x.growTo(p_words + 1);
62         
63         if (ws.length < p_words + 1)
64             ws.resize(p_words + 1);
65         
66         //FIXME: take into account bound if > 0
67         while(true)
68         {
69             if (bigint_sub3(ws.ptr, x.ptr, p_words, prime, p_words)) // borrow?
70                 break;
71             
72             x.swapReg(ws);
73         }
74     }
75 
76     abstract Vector!char toVector() const;
77 }
78 
79 class CurveGFpMontgomery : CurveGFpRepr
80 {
81 
82 	static if (BOTAN_HAS_ENGINE_OPENSSL) {
83 		import deimos.openssl.bn;
84 		~this() {
85 			m_p_bn.d = null; m_p_bn.top = cast(int)0;
86 			m_x_.d = null; m_x_.top = cast(int)0;
87 			m_y_.d = null; m_y_.top = cast(int)0;
88 			m_z_.d = null; m_z_.top = cast(int)0;
89 			BN_free(m_z_);
90 			BN_free(m_y_);
91 			BN_free(m_x_);
92 			BN_free(m_p_bn);
93 			BN_CTX_free(m_ctx);
94 			BN_MONT_CTX_free(m_mont);
95 		}
96 		BIGNUM* m_z_;
97 		BIGNUM* m_x_;
98 		BIGNUM* m_y_;
99 		BIGNUM* m_p_bn;
100 		BN_CTX* m_ctx;
101 		BN_MONT_CTX* m_mont;
102 	}
103 
104     this()(auto const ref BigInt p, auto const ref BigInt a, auto const ref BigInt b)
105     {
106         m_p = p.dup;
107         m_a = a.dup;
108         m_b = b.dup;
109         m_p_words = m_p.sigWords();
110         m_p_dash = montyInverse(m_p.wordAt(0));
111         BigInt r = BigInt.powerOf2(m_p_words * BOTAN_MP_WORD_BITS);
112         m_r2  = (r * r) % m_p;
113         m_a_r = (m_a * r) % m_p;
114         m_b_r = (m_b * r) % m_p;
115 		static if (BOTAN_HAS_ENGINE_OPENSSL) {
116 			m_z_ = BN_new();
117 			m_x_ = BN_new();
118 			m_y_ = BN_new();
119 			m_p_bn = BN_new();
120 			m_p_bn.d = cast(BN_ULONG*)m_p.ptr;
121 			m_p_bn.top = cast(int)m_p.sigWords();
122 			m_ctx = BN_CTX_new();
123 			m_mont = BN_MONT_CTX_new();
124 			BN_MONT_CTX_set(m_mont, m_p_bn, m_ctx);
125 		}
126     }
127 
128     override ref const(BigInt) getP() const { return m_p; }
129 
130     override ref const(BigInt) getA() const { return m_a; }
131 
132     override ref const(BigInt) getB() const { return m_b; }
133         
134     override ref const(BigInt) getARep() const { return m_a_r; }
135 
136     override ref const(BigInt) getBRep() const { return m_b_r; }
137 
138     override size_t getPWords() const { return m_p_words; }
139 
140     override void toCurveRep(ref BigInt x, ref SecureVector!word ws) const
141     {
142         const BigInt tx = x.dup;
143         curveMul(x, tx, m_r2, ws);
144     }
145 
146     override void fromCurveRep(ref BigInt x, ref SecureVector!word ws) const
147     {
148         const BigInt tx = x.dup;
149         BigInt bi = BigInt(1);
150         curveMul(x, tx, bi, ws);
151     }
152 
153     /**
154     * Montgomery multiplication/reduction
155     * Notes: z cannot alias x or y
156     * Params:
157     *  z = output
158     *  x = first multiplicand
159     *  y = second multiplicand
160     */
161     override void curveMul(ref BigInt z, const ref BigInt x, const ref BigInt y, ref SecureVector!word ws) const
162     {
163         
164         if (x.isZero() || y.isZero())
165         {
166             z = 0;
167             return;
168         }
169 
170 		const size_t output_size = 2*m_p_words + 1;
171 		
172 		z.growTo(output_size);
173 		z.clear();
174 
175 		static if (BOTAN_HAS_ENGINE_OPENSSL) {
176 			// about 2-3x faster than the optimized botan-math
177 			CurveGFpMontgomery this_ = cast()this;
178 			this_.m_x_.neg = cast(int)0;
179 			this_.m_x_.d = cast(BN_ULONG*)x.ptr;
180 			this_.m_x_.top = cast(int) x.sigWords();
181 			this_.m_y_.neg = cast(int)0;
182 			this_.m_y_.d = cast(BN_ULONG*)y.ptr;
183 			this_.m_y_.top = cast(int) y.sigWords();
184 			BN_CTX* ctx_ = BN_CTX_new();
185 			scope(exit) BN_CTX_free(ctx_);
186 			BN_mod_mul_montgomery(this_.m_z_, this_.m_x_, this_.m_y_, this_.m_mont, ctx_);
187 			memcpy(cast(ubyte*)z.mutablePtr(), cast(ubyte*)this_.m_z_.d, cast(ubyte*)(this_.m_z_.top*word.sizeof));
188 		}
189 		else {
190 			ws.resize(2*(m_p_words+2));	        
191 	        bigint_monty_mul(z.mutablePtr(), output_size,
192 	            x.ptr, x.length, x.sigWords(),
193 	            y.ptr, y.length, y.sigWords(),
194 	            m_p.ptr, m_p_words, m_p_dash,
195 	            ws.ptr);
196 		}
197     }
198 
199     /**
200     * Montgomery squaring/reduction
201     * Notes: z cannot alias x
202     * Params:
203     *  z = output
204     *  x = multiplicand
205     */
206     override void curveSqr(ref BigInt z, const ref BigInt x, ref SecureVector!word ws) const
207     {
208         if (x.isZero())
209         {
210             z = 0;
211             return;
212         }
213 		const size_t output_size = 2*m_p_words + 1;	
214 		z.growTo(output_size);
215 		z.clear();
216 		static if (BOTAN_HAS_ENGINE_OPENSSL) {
217 			// about 2-3x faster than the optimized botan-math
218 			CurveGFpMontgomery this_ = cast()this;
219 			this_.m_x_.neg = cast(int)0;
220 			this_.m_x_.d = cast(BN_ULONG*)x.ptr;
221 			this_.m_x_.top = cast(int) x.sigWords();
222 			BN_CTX* ctx_ = BN_CTX_new();
223 			scope(exit) BN_CTX_free(ctx_);
224 			BN_mod_mul_montgomery(this_.m_z_, this_.m_x_, this_.m_x_, this_.m_mont, ctx_);
225 			memcpy(cast(ubyte*)z.mutablePtr(), cast(ubyte*)this_.m_z_.d, cast(ubyte*)(this_.m_z_.top*word.sizeof));
226 		}
227 		else {
228 	        ws.resize(2*(m_p_words+2));
229 	        
230 
231 	        bigint_monty_sqr(z.mutablePtr(), output_size,
232 	            x.ptr, x.length, x.sigWords(),
233 	            m_p.ptr, m_p_words, m_p_dash,
234 	            ws.ptr);
235 		}
236     }
237 
238     override Vector!char toVector() const
239     {
240         Vector!char ret;
241         ret ~= "m_p: ";
242         ret ~= m_p.toString();
243         ret ~= "\nm_a: ";
244         ret ~= m_a.toString();
245         ret ~= "\nm_b: ";
246         ret ~= m_b.toString();
247         ret ~= "\nm_r2: ";
248         ret ~= m_r2.toString();
249         ret ~= "\nm_a_r: ";
250         ret ~= m_a_r.toString();
251         ret ~= "\nm_b_r: ";
252         ret ~= m_b_r.toString();
253         ret ~= "\nm_p_dash: ";
254         ret ~= m_p_dash.to!string;
255         ret ~= "\nm_p_words: ";
256         ret ~= m_p_words.to!string;
257         ret ~= "\n";
258         return ret.move();
259     }
260 
261 private:
262     // Curve parameters
263     BigInt m_p, m_a, m_b;
264     
265     size_t m_p_words; // cache of m_p.sigWords()
266     
267     // Montgomery parameters
268     BigInt m_r2, m_a_r, m_b_r;
269     word m_p_dash;
270 
271 }
272 
273 /**
274 * This class represents an elliptic curve over GF(p)
275 */
276 struct CurveGFp
277 {
278     /**
279     * Construct the elliptic curve E: y^2 = x^3 + ax + b over GF(p)
280     * Params:
281     *  p = prime number of the field
282     *  a = first coefficient
283     *  b = second coefficient
284     */
285     this()(auto const ref BigInt p, auto const ref BigInt a, auto const ref BigInt b)
286     {
287         m_repr = chooseRepr(p, a, b);
288     }
289 
290     /**
291     * Returns: curve coefficient a
292     */
293     ref const(BigInt) getA() const { return m_repr.getA(); }
294 
295     /**
296     * Returns: curve coefficient b
297     */
298     ref const(BigInt) getB() const { return m_repr.getB(); }
299 
300     /**
301     * Get prime modulus of the field of the curve
302     * Returns: prime modulus of the field of the curve
303     */
304     ref const(BigInt) getP() const { return m_repr.getP(); }
305 
306     /**
307     * Returns: a * r mod p
308     */
309     ref const(BigInt) getARep() const { return m_repr.getARep(); }
310 
311     /**
312     * Returns: b * r mod p
313     */
314     ref const(BigInt) getBRep() const { return m_repr.getBRep(); }
315 
316     void toRep()(ref BigInt x, SecureVector!word* ws) const
317     { 
318         m_repr.toCurveRep(x, *ws); 
319     }
320     
321     void fromRep(ref BigInt x, SecureVector!word* ws) const 
322     { 
323         m_repr.fromCurveRep(x, *ws);
324     }
325 
326     BigInt fromRep()(auto const ref BigInt x, SecureVector!word* ws) const
327     { 
328         BigInt xt = x.dup;
329         m_repr.fromCurveRep(xt, *ws);
330         return xt.move;
331     }
332 
333     /**
334     * swaps the states of this and other, does not throw
335     * Params:
336     *  other = curve to swap values with
337     */
338     void swap()(auto ref CurveGFp other)
339     {
340         .swap(m_repr, other.m_repr);
341     }
342 
343     /**
344     * Equality operator
345     * Params:
346     *  other = curve to compare with
347     * Returns: true iff this is the same curve as other
348     */
349     bool opEquals(const ref CurveGFp other) const
350     {
351         return (getP() == other.getP() &&
352                   getA() == other.getA() &&
353                   getB() == other.getB());
354     }
355 
356     /**
357     * Equality operator
358     * Params:
359     *  rhs = a curve
360     * Returns: true iff lhs is not the same as rhs
361     */
362     int opCmp(ref CurveGFp rhs) const
363     {
364         if (this == rhs) return 0;
365         else return -1;
366     }
367 
368     @property CurveGFp dup() const {
369         return CurveGFp(getP(), getA(), getB());
370     }
371 
372     // TODO: fromRep taking && ref
373 
374     void mul()(ref BigInt z, auto const ref BigInt x, auto const ref BigInt y, SecureVector!word* ws) const
375     {
376         m_repr.curveMul(z, x, y, *ws);
377     }
378 
379     BigInt mul()(auto const ref BigInt x, auto const ref BigInt y, SecureVector!word* ws) const
380     {
381         BigInt z;
382         m_repr.curveMul(z, x, y, *ws);
383         return z.move;
384     }
385 
386     void sqr()(auto ref BigInt z, auto const ref BigInt x, SecureVector!word* ws) const
387     {
388         m_repr.curveSqr(z, x, *ws);
389     }
390 
391     BigInt sqr()(auto const ref BigInt x, SecureVector!word* ws) const
392     {
393         BigInt z;
394         m_repr.curveSqr(z, x, *ws);
395         return z.move;
396     }
397 
398     /**
399      * Adjust x to be in [0,p)
400      * @param bound if greater than zero, assume that no more than bound
401      * additions or subtractions are required to move x into range.
402      */
403     void normalize(ref BigInt x, SecureVector!word* ws, size_t bound = 0) const
404     {
405         m_repr.normalize(x, *ws, bound);
406     }
407 
408     @disable this(this);
409 
410     string toString() const {
411         return toVector()[].idup;
412     }
413 
414     Vector!char toVector() const {
415         return m_repr.toVector();
416     }
417 
418     ~this() {
419     }
420 
421     static CurveGFpRepr chooseRepr()(auto const ref BigInt p, auto const ref BigInt a, auto const ref BigInt b)
422     {
423         //if (p == CurveGFpP521.prime)
424         //    return cast(CurveGFpRepr) new CurveGFpP521(a, b);
425         return cast(CurveGFpRepr) new CurveGFpMontgomery(p, a, b);
426     }
427 
428     Unique!CurveGFpRepr m_repr;
429 }