| // See file LICENSE for more information. |
| |
| library impl.ecc.ecc_fp; |
| |
| import 'dart:typed_data'; |
| |
| import 'package:pointycastle/api.dart'; |
| import 'package:pointycastle/ecc/ecc_base.dart' |
| hide ECCurveBase, ECFieldElementBase, ECPointBase; |
| import 'package:pointycastle/ecc/ecc_base.dart' as ecc; |
| import 'package:pointycastle/src/utils.dart' as utils; |
| |
| /// return index of lowest 1-bit in x, x < 2^31 */ |
| int _lbit(BigInt x) { |
| // Implementation borrowed from bignum.BigIntegerDartvm. |
| if (x == BigInt.zero) return -1; |
| var r = 0; |
| while ((x & BigInt.from(0xffffffff)) == BigInt.zero) { |
| x >>= 32; |
| r += 32; |
| } |
| if ((x & BigInt.from(0xffff)) == BigInt.zero) { |
| x >>= 16; |
| r += 16; |
| } |
| if ((x & BigInt.from(0xff)) == BigInt.zero) { |
| x >>= 8; |
| r += 8; |
| } |
| if ((x & BigInt.from(0xf)) == BigInt.zero) { |
| x >>= 4; |
| r += 4; |
| } |
| if ((x & BigInt.from(3)) == BigInt.zero) { |
| x >>= 2; |
| r += 2; |
| } |
| if ((x & BigInt.one) == BigInt.zero) ++r; |
| return r; |
| } |
| |
| bool _testBit(BigInt i, int n) { |
| return i & (BigInt.one << n) != BigInt.zero; |
| } |
| |
| class ECFieldElement extends ecc.ECFieldElementBase { |
| final BigInt? q; |
| final BigInt? x; |
| |
| ECFieldElement(this.q, this.x) { |
| if (x! >= q!) { |
| throw ArgumentError('Value x must be smaller than q'); |
| } |
| } |
| |
| @override |
| String get fieldName => 'Fp'; |
| @override |
| int get fieldSize => q!.bitLength; |
| |
| @override |
| BigInt? toBigInteger() => x; |
| |
| @override |
| ECFieldElement operator +(ECFieldElement b) => |
| ECFieldElement(q, (x! + b.toBigInteger()!) % q!); |
| @override |
| ECFieldElement operator -(ECFieldElement b) => |
| ECFieldElement(q, (x! - b.toBigInteger()!) % q!); |
| @override |
| ECFieldElement operator *(ECFieldElement b) => |
| ECFieldElement(q, (x! * b.toBigInteger()!) % q!); |
| @override |
| ECFieldElement operator /(ECFieldElement b) => |
| ECFieldElement(q, (x! * b.toBigInteger()!.modInverse(q!)) % q!); |
| |
| @override |
| ECFieldElement operator -() => ECFieldElement(q, -x! % q!); |
| |
| @override |
| ECFieldElement invert() => ECFieldElement(q, x!.modInverse(q!)); |
| @override |
| ECFieldElement square() => ECFieldElement(q, x!.modPow(BigInt.two, q!)); |
| |
| // D.1.4 91 |
| /// return a sqrt root - the routine verifies that the calculation |
| /// returns the right value - if none exists it returns null. |
| @override |
| ECFieldElement? sqrt() { |
| if (!_testBit(q!, 0)) { |
| throw UnimplementedError('Not implemented yet'); |
| } |
| |
| // p % 4 == 3 |
| if (_testBit(q!, 1)) { |
| // z = g^(u+1) + p, p = 4u + 3 |
| var z = ECFieldElement(q, x!.modPow((q! >> 2) + BigInt.one, q!)); |
| return z.square() == this ? z : null; |
| } |
| |
| // p % 4 == 1 |
| var qMinusOne = q! - BigInt.one; |
| |
| var legendreExponent = qMinusOne >> 1; |
| if (x!.modPow(legendreExponent, q!) != BigInt.one) { |
| return null; |
| } |
| |
| var u = qMinusOne >> 2; |
| var k = (u << 1) + BigInt.one; |
| |
| var Q = x!; |
| var fourQ = (Q >> 2) % q!; |
| |
| BigInt U, V; |
| var rand = SecureRandom(); |
| do { |
| BigInt? P; |
| do { |
| P = rand.nextBigInteger(q!.bitLength); |
| } while ((P >= q!) || |
| (((P * P) - fourQ).modPow(legendreExponent, q!) != qMinusOne)); |
| |
| var result = _lucasSequence(q!, P, Q, k); |
| U = result[0]; |
| V = result[1]; |
| |
| if (((V * V) % q!) == fourQ) { |
| // Integer division by 2, mod q |
| if (_testBit(V, 0)) { |
| V = V + q!; |
| } |
| |
| V = V >> 1; |
| |
| //assert V.multiply(V).mod(q).equals(x); |
| |
| return ECFieldElement(q, V); |
| } |
| } while ((U == BigInt.one) || (U == qMinusOne)); |
| |
| return null; |
| } |
| |
| List<BigInt> _lucasSequence(BigInt p, BigInt P, BigInt Q, BigInt k) { |
| var n = k.bitLength; |
| var s = _lbit(k); |
| |
| var uh = BigInt.one; |
| var vl = BigInt.two; |
| var vh = P; |
| var ql = BigInt.one; |
| var qh = BigInt.one; |
| |
| for (var j = n - 1; j >= (s + 1); j--) { |
| ql = (ql * qh) % p; |
| |
| if (_testBit(k, j)) { |
| qh = (ql * Q) % p; |
| uh = (uh * vh) % p; |
| vl = ((vh * vl) - (P * ql)) % p; |
| vh = ((vh * vh) - (qh << 1)) % p; |
| } else { |
| qh = ql; |
| uh = ((uh * vl) - ql) % p; |
| vh = ((vh * vl) - (P * ql)) % p; |
| vl = ((vl * vl) - (ql << 1)) % p; |
| } |
| } |
| |
| ql = (ql * qh) % p; |
| qh = (ql * Q) % p; |
| uh = ((uh * vl) - ql) % p; |
| vl = ((vh * vl) - (P * ql)) % p; |
| ql = (ql * qh) % p; |
| |
| for (var j = 1; j <= s; j++) { |
| uh = (uh * vl) % p; |
| vl = ((vl * vl) - (ql << 1)) % p; |
| ql = (ql * ql) % p; |
| } |
| |
| return [uh, vl]; |
| } |
| |
| @override |
| bool operator ==(Object other) { |
| if (other is ECFieldElement) { |
| return (q == other.q) && (x == other.x); |
| } |
| return false; |
| } |
| |
| @override |
| int get hashCode => q.hashCode ^ x.hashCode; |
| } |
| |
| /// Elliptic curve points over Fp |
| class ECPoint extends ecc.ECPointBase { |
| /// Create a point that encodes with or without point compression. |
| /// |
| /// @param curve the curve to use |
| /// @param x affine x co-ordinate |
| /// @param y affine y co-ordinate |
| /// @param withCompression if true encode with point compression |
| ECPoint(ECCurve curve, ECFieldElement? x, ECFieldElement? y, |
| [bool withCompression = false]) |
| : super(curve, x, y, withCompression, _wNafMultiplier) { |
| if ((x != null && y == null) || (x == null && y != null)) { |
| throw ArgumentError('Exactly one of the field elements is null'); |
| } |
| } |
| |
| /// return the field element encoded with point compression. (S 4.3.6) |
| @override |
| Uint8List getEncoded([bool compressed = true]) { |
| if (isInfinity) { |
| return Uint8List.fromList([1]); |
| } |
| |
| var qLength = x!.byteLength; |
| if (compressed) { |
| int pc; |
| |
| if (_testBit(y!.toBigInteger()!, 0)) { |
| pc = 0x03; |
| } else { |
| pc = 0x02; |
| } |
| |
| var X = _x9IntegerToBytes(x!.toBigInteger(), qLength); |
| var po = Uint8List(X.length + 1); |
| |
| po[0] = pc.toInt(); |
| po.setAll(1, X); |
| |
| return po; |
| } else { |
| var X = _x9IntegerToBytes(x!.toBigInteger(), qLength); |
| var Y = _x9IntegerToBytes(y!.toBigInteger(), qLength); |
| var po = Uint8List(X.length + Y.length + 1); |
| |
| po[0] = 0x04; |
| po.setAll(1, X); |
| po.setAll(X.length + 1, Y); |
| |
| return po; |
| } |
| } |
| |
| // B.3 pg 62 |
| @override |
| ECPoint? operator +(ECPoint? b) { |
| if (isInfinity) { |
| return b; |
| } |
| |
| if (b!.isInfinity) { |
| return this; |
| } |
| |
| // Check if b = this or b = -this |
| if (x == b.x) { |
| if (y == b.y) { |
| // this = b, i.e. this must be doubled |
| return twice(); |
| } |
| |
| // this = -b, i.e. the result is the point at infinity |
| return curve.infinity as ECPoint?; |
| } |
| |
| var gamma = (b.y! - y!) / (b.x! - x!); |
| |
| var x3 = (gamma.square() - x!) - b.x!; |
| var y3 = (gamma * (x! - x3)) - y!; |
| |
| return ECPoint(curve as ECCurve, x3 as ECFieldElement?, |
| y3 as ECFieldElement?, isCompressed); |
| } |
| |
| // B.3 pg 62 |
| @override |
| ECPoint? twice() { |
| if (isInfinity) { |
| // Twice identity element (point at infinity) is identity |
| return this; |
| } |
| |
| if (y!.toBigInteger() == BigInt.zero) { |
| // if y1 == 0, then (x1, y1) == (x1, -y1) |
| // and hence this = -this and thus 2(x1, y1) == infinity |
| return curve.infinity as ECPoint?; |
| } |
| |
| var two = curve.fromBigInteger(BigInt.two); |
| var three = curve.fromBigInteger(BigInt.from(3)); |
| var gamma = ((x!.square() * three) + curve.a!) / (y! * two); |
| |
| var x3 = gamma.square() - (x! * two); |
| var y3 = (gamma * (x! - x3)) - y!; |
| |
| return ECPoint(curve as ECCurve, x3 as ECFieldElement?, |
| y3 as ECFieldElement?, isCompressed); |
| } |
| |
| // D.3.2 pg 102 (see Note:) |
| @override |
| ECPoint? operator -(ECPoint b) { |
| if (b.isInfinity) { |
| return this; |
| } |
| |
| // Add -b |
| return this + (-b); |
| } |
| |
| @override |
| ECPoint operator -() { |
| return ECPoint(curve as ECCurve, x as ECFieldElement?, |
| -y! as ECFieldElement?, isCompressed); |
| } |
| } |
| |
| /// Elliptic curve over Fp |
| class ECCurve extends ecc.ECCurveBase { |
| final BigInt? q; |
| ECPoint? _infinity; |
| |
| ECCurve(this.q, BigInt? a, BigInt? b) : super(a, b) { |
| _infinity = ECPoint(this, null, null); |
| } |
| |
| @override |
| int get fieldSize => q!.bitLength; |
| @override |
| ECPoint? get infinity => _infinity; |
| |
| @override |
| ECFieldElement fromBigInteger(BigInt? x) => ECFieldElement(q, x); |
| @override |
| ECPoint createPoint(BigInt x, BigInt y, [bool withCompression = false]) => |
| ECPoint(this, fromBigInteger(x), fromBigInteger(y), withCompression); |
| |
| @override |
| ECPoint decompressPoint(int yTilde, BigInt x1) { |
| var x = fromBigInteger(x1); |
| var alpha = (x * ((x * x) + (a as ECFieldElement))) + (b as ECFieldElement); |
| var beta = alpha.sqrt(); |
| |
| // |
| // if we can't find a sqrt we haven't got a point on the |
| // curve - run! |
| // |
| if (beta == null) { |
| throw ArgumentError('Invalid point compression'); |
| } |
| |
| var betaValue = beta.toBigInteger()!; |
| var bit0 = _testBit(betaValue, 0) ? 1 : 0; |
| |
| if (bit0 != yTilde) { |
| // Use the other root |
| beta = fromBigInteger(q! - betaValue); |
| } |
| |
| return ECPoint(this, x, beta, true); |
| } |
| |
| @override |
| bool operator ==(Object other) { |
| if (other is ECCurve) { |
| return q == other.q && a == other.a && b == other.b; |
| } |
| return false; |
| } |
| |
| @override |
| int get hashCode => a.hashCode ^ b.hashCode ^ q.hashCode; |
| } |
| |
| /// Class holding precomputation data for the WNAF (Window Non-Adjacent Form) |
| /// algorithm. |
| class _WNafPreCompInfo implements PreCompInfo { |
| /// Array holding the precomputed [ECPoint]s used for the Window NAF multiplication. |
| List<ECPoint>? preComp; |
| |
| /// Holds an [ECPoint] representing twice(this). Used for the Window NAF multiplication. |
| ECPoint? twiceP; |
| } |
| |
| /// Function implementing the WNAF (Window Non-Adjacent Form) multiplication algorithm. Multiplies [p]] by an integer [k] using |
| /// the Window NAF method. |
| ecc.ECPointBase? _wNafMultiplier( |
| ecc.ECPointBase p, BigInt? k, PreCompInfo? preCompInfo) { |
| // Ignore empty PreCompInfo or PreCompInfo of incorrect type |
| _WNafPreCompInfo wnafPreCompInfo; |
| if (preCompInfo is! _WNafPreCompInfo) { |
| wnafPreCompInfo = _WNafPreCompInfo(); |
| } else { |
| wnafPreCompInfo = preCompInfo; |
| } |
| |
| // floor(log2(k)) |
| var m = k!.bitLength; |
| |
| // width of the Window NAF |
| int width; |
| |
| // Required length of precomputation array |
| int reqPreCompLen; |
| |
| // Determine optimal width and corresponding length of precomputation |
| // array based on literature values |
| if (m < 13) { |
| width = 2; |
| reqPreCompLen = 1; |
| } else { |
| if (m < 41) { |
| width = 3; |
| reqPreCompLen = 2; |
| } else { |
| if (m < 121) { |
| width = 4; |
| reqPreCompLen = 4; |
| } else { |
| if (m < 337) { |
| width = 5; |
| reqPreCompLen = 8; |
| } else { |
| if (m < 897) { |
| width = 6; |
| reqPreCompLen = 16; |
| } else { |
| if (m < 2305) { |
| width = 7; |
| reqPreCompLen = 32; |
| } else { |
| width = 8; |
| reqPreCompLen = 127; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| // The length of the precomputation array |
| var preCompLen = 1; |
| |
| List<ECPoint?>? preComp = wnafPreCompInfo.preComp; |
| var twiceP = wnafPreCompInfo.twiceP; |
| |
| // Check if the precomputed ECPoints already exist |
| if (preComp == null) { |
| // Precomputation must be performed from scratch, create an empty |
| // precomputation array of desired length |
| preComp = List<ECPoint>.filled(1, p as ECPoint); |
| } else { |
| // Take the already precomputed ECPoints to start with |
| preCompLen = preComp.length; |
| } |
| |
| twiceP ??= p.twice() as ECPoint?; |
| |
| if (preCompLen < reqPreCompLen) { |
| // Precomputation array must be made bigger, copy existing preComp |
| // array into the larger preComp array |
| var oldPreComp = preComp as List<ECPoint>; |
| preComp = List<ECPoint?>.filled(reqPreCompLen, null, growable: false); |
| preComp.setAll(0, oldPreComp); |
| |
| for (var i = preCompLen; i < reqPreCompLen; i++) { |
| // Compute the ECPoints for the precomputation array. |
| // The values 1, 3, 5, ..., 2^(width-1)-1 times p are |
| // computed |
| preComp[i] = twiceP! + (preComp[i - 1]); |
| } |
| } |
| |
| // Compute the Window NAF of the desired width |
| var wnaf = _windowNaf(width, k); |
| var l = wnaf.length; |
| |
| // Apply the Window NAF to p using the precomputed ECPoint values. |
| var q = p.curve.infinity; |
| for (var i = l - 1; i >= 0; i--) { |
| q = q!.twice(); |
| |
| if (wnaf[i] != 0) { |
| if (wnaf[i]! > 0) { |
| q = q! + preComp[(wnaf[i]! - 1) ~/ 2]; |
| } else { |
| // wnaf[i] < 0 |
| q = q! - preComp[(-wnaf[i]! - 1) ~/ 2]!; |
| } |
| } |
| } |
| |
| // Set PreCompInfo in ECPoint, such that it is available for next |
| // multiplication. |
| wnafPreCompInfo.preComp = preComp.map((e) => e as ECPoint).toList(); |
| wnafPreCompInfo.twiceP = twiceP; |
| p.preCompInfo = wnafPreCompInfo; |
| return q; |
| } |
| |
| /// Computes the Window NAF (non-adjacent Form) of an integer. |
| /// @param width The width <code>w</code> of the Window NAF. The width is |
| /// defined as the minimal number <code>w</code>, such that for any |
| /// <code>w</code> consecutive digits in the resulting representation, at |
| /// most one is non-zero. |
| /// @param k The integer of which the Window NAF is computed. |
| /// @return The Window NAF of the given width, such that the following holds: |
| /// <code>k = ∑<sub>i=0</sub><sup>l-1</sup> k<sub>i</sub>2<sup>i</sup> |
| /// </code>, where the <code>k<sub>i</sub></code> denote the elements of the |
| /// returned <code>byte[]</code>. |
| List<int?> _windowNaf(int width, BigInt k) { |
| // The window NAF is at most 1 element longer than the binary |
| // representation of the integer k. byte can be used instead of short or |
| // int unless the window width is larger than 8. For larger width use |
| // short or int. However, a width of more than 8 is not efficient for |
| // m = log2(q) smaller than 2305 Bits. Note: Values for m larger than |
| // 1000 Bits are currently not used in practice. |
| var wnaf = List<int?>.filled(k.bitLength + 1, null, growable: false); |
| |
| // 2^width as short and BigInt |
| var pow2wB = 1 << width; |
| var pow2wBI = BigInt.from(pow2wB); |
| |
| var i = 0; |
| |
| // The actual length of the WNAF |
| var length = 0; |
| |
| // while k >= 1 |
| while (k.sign > 0) { |
| // if k is odd |
| if (_testBit(k, 0)) { |
| // k mod 2^width |
| var remainder = k % pow2wBI; |
| |
| // if remainder > 2^(width - 1) - 1 |
| if (_testBit(remainder, width - 1)) { |
| wnaf[i] = remainder.toInt() - pow2wB; |
| } else { |
| wnaf[i] = remainder.toInt(); |
| } |
| |
| // convert to 'Java byte' |
| wnaf[i] = wnaf[i]! % 0x100; |
| if ((wnaf[i]! & 0x80) != 0) { |
| wnaf[i] = wnaf[i]! - 256; |
| } |
| |
| // wnaf[i] is now in [-2^(width-1), 2^(width-1)-1] |
| |
| k = k - BigInt.from(wnaf[i]!); |
| length = i; |
| } else { |
| wnaf[i] = 0; |
| } |
| |
| // k = k/2 |
| k = k >> 1; |
| i++; |
| } |
| |
| length++; |
| |
| // Reduce the WNAF array to its actual length |
| var wnafShort = List<int?>.filled(length, null, growable: false); |
| wnafShort.setAll(0, wnaf.sublist(0, length)); |
| return wnafShort; |
| } |
| |
| Uint8List _x9IntegerToBytes(BigInt? s, int qLength) { |
| var bytes = Uint8List.fromList(utils.encodeBigInt(s)); |
| |
| if (qLength < bytes.length) { |
| return bytes.sublist(bytes.length - qLength); |
| } else if (qLength > bytes.length) { |
| return Uint8List(qLength)..setAll(qLength - bytes.length, bytes); |
| } |
| |
| return bytes; |
| } |