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 }