2024-05-18 09:37:50 +08:00

249 lines
6.4 KiB
Dart
Executable File

class ECFieldElementFp {
final BigInt x;
final BigInt q;
ECFieldElementFp(this.q, this.x) {
// TODO if (x.compareTo(q) >= 0) error
}
/// 判断相等
bool equals(ECFieldElementFp other) {
if (other == this) return true;
return (q == other.q && x == other.x);
}
/// 返回具体数值
BigInt toBigInteger() {
return x;
}
/// 取反
ECFieldElementFp negate() {
return ECFieldElementFp(q, (-x) % q);
}
/// 相加
ECFieldElementFp add(ECFieldElementFp b) {
return ECFieldElementFp(q, (x + b.toBigInteger()) % q);
}
/// 相减
ECFieldElementFp subtract(ECFieldElementFp b) {
return ECFieldElementFp(q, (x - b.toBigInteger()) % q);
}
/// 相乘
ECFieldElementFp multiply(ECFieldElementFp b) {
return ECFieldElementFp(q, (x * b.toBigInteger()) % q);
}
/// 相除
ECFieldElementFp divide(ECFieldElementFp b) {
return ECFieldElementFp(q, (x * b.toBigInteger().modInverse(q)) % q);
}
/// 平方
ECFieldElementFp square() {
return ECFieldElementFp(q, (x * x) % q);
}
}
class ECPointFp {
final ECCurveFp curve;
late final ECFieldElementFp? x;
late final ECFieldElementFp? y;
late final BigInt z;
BigInt? zinv;
ECPointFp(this.curve, this.x, this.y, [BigInt? z]) {
this.z = z ?? BigInt.one;
zinv = null;
}
ECFieldElementFp getX() {
zinv ??= z.modInverse(curve.q);
return curve.fromBigInteger(x!.toBigInteger() * zinv! % curve.q);
}
ECFieldElementFp getY() {
zinv ??= z.modInverse(curve.q);
return curve.fromBigInteger(y!.toBigInteger() * zinv! % curve.q);
}
bool equals(ECPointFp other) {
if (other == this) return true;
if (isInfinity()) return other.isInfinity();
if (other.isInfinity()) return isInfinity();
final u = (other.y!.toBigInteger() * z - y!.toBigInteger() * other.z) % curve.q;
if (u != BigInt.zero) return false;
final v = (other.x!.toBigInteger() * z - x!.toBigInteger() * other.z) % curve.q;
return v == BigInt.zero;
}
bool isInfinity() {
if (x == null && y == null) return true;
return z == BigInt.zero && y!.toBigInteger() != BigInt.zero;
}
ECPointFp negate() {
return ECPointFp(curve, x, y!.negate(), z);
}
ECPointFp add(ECPointFp b) {
if (isInfinity()) return b;
if (b.isInfinity()) return this;
final x1 = x!.toBigInteger();
final y1 = y!.toBigInteger();
final z1 = z;
final x2 = b.x!.toBigInteger();
final y2 = b.y!.toBigInteger();
final z2 = b.z;
final q = curve.q;
final w1 = (x1 * z2) % q;
final w2 = (x2 * z1) % q;
final w3 = (w1 - w2) % q;
final w4 = (y1 * z2) % q;
final w5 = (y2 * z1) % q;
final w6 = (w4 - w5) % q;
if (w3 == BigInt.zero) {
if (w6 == BigInt.zero) {
return twice();
}
return curve.infinity;
}
final w7 = (w1 + w2) % q;
final w8 = (z1 * z2) % q;
final w9 = (w3 * w3) % q;
final w10 = (w3 * w9) % q;
final w11 = (w8 * (w6 * w6) % q - w7 * w9) % q;
final x3 = (w3 * w11) % q;
final y3 = (w6 * (w9 * w1 % q - w11) - w4 * w10) % q;
final z3 = (w10 * w8) % q;
return ECPointFp(curve, curve.fromBigInteger(x3), curve.fromBigInteger(y3), z3);
}
ECPointFp twice() {
if (isInfinity()) return this;
if (y!.toBigInteger().sign == 0) return curve.infinity;
final x1 = x!.toBigInteger();
final y1 = y!.toBigInteger();
final z1 = z;
final q = curve.q;
final a = curve.a.toBigInteger();
final w1 = (x1 * x1 * BigInt.from(3) + a * (z1 * z1)) % q;
final w2 = (y1 * BigInt.from(2) * z1) % q;
final w3 = (y1 * y1) % q;
final w4 = (w3 * x1 * z1) % q;
final w5 = (w2 * w2) % q;
final w6 = (w1 * w1 - w4 * BigInt.from(8)) % q;
final x3 = (w2 * w6) % q;
final y3 = (w1 * (w4 * BigInt.from(4) - w6) - w5 * BigInt.from(2) * w3) % q;
final z3 = (w2 * w5) % q;
return ECPointFp(curve, curve.fromBigInteger(x3), curve.fromBigInteger(y3), z3);
}
ECPointFp multiply(BigInt k) {
if (isInfinity()) return this;
if (k.sign == 0) return curve.infinity;
final k3 = k * BigInt.from(3);
final neg = negate();
ECPointFp Q = this;
for (int i = k3.bitLength - 2; i > 0; i--) {
Q = Q.twice();
/*final k3Bit = (k3 >> i) & BigInt.one == BigInt.one;
final kBit = (k >> i) & BigInt.one == BigInt.zero;*/
final k3Bit = (k3 >> i).isOdd;
;
final kBit = (k >> i).isOdd;
if (k3Bit != kBit) {
Q = Q.add(k3Bit ? this : neg);
}
}
return Q;
}
}
class ECCurveFp {
ECCurveFp(this.q, BigInt a, BigInt b) {
this.a = fromBigInteger(a);
this.b = fromBigInteger(b);
infinity = ECPointFp(this, null, null); // 无穷远点
}
final BigInt q;
late ECFieldElementFp a;
late ECFieldElementFp b;
late ECPointFp infinity;
bool equals(Object? other) {
if (identical(this, other)) return true;
if (other is! ECCurveFp) return false;
return q == other.q && a == other.a && b == other.b;
}
ECFieldElementFp fromBigInteger(BigInt x) {
return ECFieldElementFp(q, x);
}
ECPointFp? decodePointHex(String s) {
switch (int.parse(s.substring(0, 2), radix: 16)) {
case 0:
return infinity;
case 2:
case 3:
final x = fromBigInteger(BigInt.parse(s.substring(2), radix: 16));
var y = fromBigInteger(x
.multiply(x.square())
.add(x.multiply(a))
.add(b)
.toBigInteger()
.modPow(q ~/ BigInt.from(4) + BigInt.one, q));
/* var y = x
.multiply(x.square())
.add(x.multiply(a.add(b)))
.toBigInteger()
.modPow(q ~/ BigInt.from(4) + BigInt.one, q);*/
if (y.toBigInteger() % BigInt.two !=
BigInt.parse(s.substring(0, 2), radix: 16) - BigInt.two) {
y = y.negate();
}
return ECPointFp(this, x, y);
case 4:
case 6:
case 7:
final len = (s.length - 2) ~/ 2;
final xHex = s.substring(2, 2 + len);
final yHex = s.substring(2 + len, 2 + 2 * len);
/*print("xHex: ${BigInt.parse(xHex, radix: 16).toRadixString(16)}");
print("yHex: ${BigInt.parse(yHex, radix: 16).toRadixString(16)}");*/
return ECPointFp(this, fromBigInteger(BigInt.parse(xHex, radix: 16)),
fromBigInteger(BigInt.parse(yHex, radix: 16)));
default:
return null;
}
}
}
String leftPad(String input, int num) {
if (input.length >= num) return input;
return List.filled(num - input.length, '0').join() + input;
}