LCOV - code coverage report
Current view: top level - crypto/utxo/utils - ecurve.dart (source / functions) Coverage Total Hit
Test: lcov.info Lines: 48.4 % 122 59
Test Date: 2025-01-30 01:10:00 Functions: - 0 0

            Line data    Source code
       1              : import 'dart:typed_data';
       2              : import 'package:hex/hex.dart';
       3              : import "package:pointycastle/ecc/curves/secp256k1.dart";
       4              : import "package:pointycastle/api.dart"
       5              :     show PrivateKeyParameter, PublicKeyParameter;
       6              : import 'package:pointycastle/ecc/api.dart'
       7              :     show ECPrivateKey, ECPublicKey, ECSignature, ECPoint;
       8              : import "package:pointycastle/signers/ecdsa_signer.dart";
       9              : import 'package:pointycastle/macs/hmac.dart';
      10              : import "package:pointycastle/digests/sha256.dart";
      11              : import 'package:pointycastle/src/utils.dart';
      12              : 
      13           15 : final ZERO32 = Uint8List.fromList(List.generate(32, (index) => 0));
      14            6 : final EC_GROUP_ORDER = HEX
      15            3 :     .decode("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141");
      16            2 : final EC_P = HEX
      17            1 :     .decode("fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f");
      18            9 : final secp256k1 = new ECCurve_secp256k1();
      19            0 : final n = secp256k1.n;
      20            8 : final G = secp256k1.G;
      21            0 : BigInt nDiv2 = n >> 1;
      22              : const THROW_BAD_PRIVATE = 'Expected Private';
      23              : const THROW_BAD_POINT = 'Expected Point';
      24              : const THROW_BAD_TWEAK = 'Expected Tweak';
      25              : const THROW_BAD_HASH = 'Expected Hash';
      26              : const THROW_BAD_SIGNATURE = 'Expected Signature';
      27              : 
      28            2 : bool isPrivate(Uint8List x) {
      29            2 :   if (!isScalar(x)) return false;
      30            6 :   return _compare(x, ZERO32) > 0 && // > 0
      31            6 :       _compare(x, EC_GROUP_ORDER as Uint8List) < 0; // < G
      32              : }
      33              : 
      34            1 : bool isPoint(Uint8List p) {
      35            2 :   if (p.length < 33) {
      36              :     return false;
      37              :   }
      38            1 :   var t = p[0];
      39            1 :   var x = p.sublist(1, 33);
      40              : 
      41            3 :   if (_compare(x, ZERO32) == 0) {
      42              :     return false;
      43              :   }
      44            3 :   if (_compare(x, EC_P as Uint8List) == 1) {
      45              :     return false;
      46              :   }
      47              :   try {
      48            1 :     decodeFrom(p);
      49              :   } catch (err) {
      50              :     return false;
      51              :   }
      52            4 :   if ((t == 0x02 || t == 0x03) && p.length == 33) {
      53              :     return true;
      54              :   }
      55            0 :   var y = p.sublist(33);
      56            0 :   if (_compare(y, ZERO32) == 0) {
      57              :     return false;
      58              :   }
      59            0 :   if (_compare(y, EC_P as Uint8List) == 1) {
      60              :     return false;
      61              :   }
      62            0 :   if (t == 0x04 && p.length == 65) {
      63              :     return true;
      64              :   }
      65              :   return false;
      66              : }
      67              : 
      68            3 : bool isScalar(Uint8List x) {
      69            6 :   return x.length == 32;
      70              : }
      71              : 
      72            0 : bool isOrderScalar(x) {
      73            0 :   if (!isScalar(x)) return false;
      74            0 :   return _compare(x, EC_GROUP_ORDER as Uint8List) < 0; // < G
      75              : }
      76              : 
      77            1 : bool isSignature(Uint8List value) {
      78            1 :   Uint8List r = value.sublist(0, 32);
      79            1 :   Uint8List s = value.sublist(32, 64);
      80              : 
      81            2 :   return value.length == 64 &&
      82            3 :       _compare(r, EC_GROUP_ORDER as Uint8List) < 0 &&
      83            3 :       _compare(s, EC_GROUP_ORDER as Uint8List) < 0;
      84              : }
      85              : 
      86            0 : bool _isPointCompressed(Uint8List p) {
      87            0 :   return p[0] != 0x04;
      88              : }
      89              : 
      90            0 : bool assumeCompression(bool? value, Uint8List? pubkey) {
      91            0 :   if (value == null && pubkey != null) return _isPointCompressed(pubkey);
      92              :   if (value == null) return true;
      93              :   return value;
      94              : }
      95              : 
      96            2 : Uint8List? pointFromScalar(Uint8List d, bool _compressed) {
      97            2 :   if (!isPrivate(d)) throw new ArgumentError(THROW_BAD_PRIVATE);
      98            2 :   BigInt dd = fromBuffer(d);
      99            4 :   ECPoint pp = (G * dd) as ECPoint;
     100            2 :   if (pp.isInfinity) return null;
     101            2 :   return getEncoded(pp, _compressed);
     102              : }
     103              : 
     104            0 : Uint8List? pointAddScalar(Uint8List p, Uint8List tweak, bool _compressed) {
     105            0 :   if (!isPoint(p)) throw new ArgumentError(THROW_BAD_POINT);
     106            0 :   if (!isOrderScalar(tweak)) throw new ArgumentError(THROW_BAD_TWEAK);
     107            0 :   bool compressed = assumeCompression(_compressed, p);
     108            0 :   ECPoint? pp = decodeFrom(p);
     109            0 :   if (_compare(tweak, ZERO32) == 0) return getEncoded(pp, compressed);
     110            0 :   BigInt tt = fromBuffer(tweak);
     111            0 :   ECPoint qq = (G * tt) as ECPoint;
     112            0 :   ECPoint uu = (pp! + qq) as ECPoint;
     113            0 :   if (uu.isInfinity) return null;
     114            0 :   return getEncoded(uu, compressed);
     115              : }
     116              : 
     117            0 : Uint8List? privateAdd(Uint8List d, Uint8List tweak) {
     118            0 :   if (!isPrivate(d)) throw new ArgumentError(THROW_BAD_PRIVATE);
     119            0 :   if (!isOrderScalar(tweak)) throw new ArgumentError(THROW_BAD_TWEAK);
     120            0 :   BigInt dd = fromBuffer(d);
     121            0 :   BigInt tt = fromBuffer(tweak);
     122            0 :   Uint8List dt = toBuffer((dd + tt) % n);
     123              : 
     124            0 :   if (dt.length < 32) {
     125            0 :     Uint8List padLeadingZero = Uint8List(32 - dt.length);
     126            0 :     dt = Uint8List.fromList(padLeadingZero + dt);
     127              :   }
     128              : 
     129            0 :   if (!isPrivate(dt)) return null;
     130              :   return dt;
     131              : }
     132              : 
     133            0 : Uint8List sign(Uint8List hash, Uint8List x) {
     134            0 :   if (!isScalar(hash)) throw new ArgumentError(THROW_BAD_HASH);
     135            0 :   if (!isPrivate(x)) throw new ArgumentError(THROW_BAD_PRIVATE);
     136            0 :   ECSignature sig = deterministicGenerateK(hash, x);
     137            0 :   Uint8List buffer = new Uint8List(64);
     138            0 :   buffer.setRange(0, 32, _encodeBigInt(sig.r));
     139              :   var s;
     140            0 :   if (sig.s.compareTo(nDiv2) > 0) {
     141            0 :     s = n - sig.s;
     142              :   } else {
     143            0 :     s = sig.s;
     144              :   }
     145            0 :   buffer.setRange(32, 64, _encodeBigInt(s));
     146              :   return buffer;
     147              : }
     148              : 
     149            1 : bool verify(Uint8List hash, Uint8List q, Uint8List signature) {
     150            1 :   if (!isScalar(hash)) throw new ArgumentError(THROW_BAD_HASH);
     151            1 :   if (!isPoint(q)) throw new ArgumentError(THROW_BAD_POINT);
     152              :   // 1.4.1 Enforce r and s are both integers in the interval [1, n − 1] (1, isSignature enforces '< n - 1')
     153            1 :   if (!isSignature(signature)) throw new ArgumentError(THROW_BAD_SIGNATURE);
     154              : 
     155            1 :   ECPoint? Q = decodeFrom(q);
     156            2 :   BigInt r = fromBuffer(signature.sublist(0, 32));
     157            2 :   BigInt s = fromBuffer(signature.sublist(32, 64));
     158              : 
     159            3 :   final signer = new ECDSASigner(null, new HMac(new SHA256Digest(), 64));
     160            4 :   signer.init(false, new PublicKeyParameter(new ECPublicKey(Q, secp256k1)));
     161            2 :   return signer.verifySignature(hash, new ECSignature(r, s));
     162              :   /* STEP BY STEP
     163              :   // 1.4.1 Enforce r and s are both integers in the interval [1, n − 1] (2, enforces '> 0')
     164              :   if (r.compareTo(n) >= 0) return false;
     165              :   if (s.compareTo(n) >= 0) return false;
     166              : 
     167              :   // 1.4.2 H = Hash(M), already done by the user
     168              :   // 1.4.3 e = H
     169              :   BigInt e = fromBuffer(hash);
     170              : 
     171              :   BigInt sInv = s.modInverse(n);
     172              :   BigInt u1 = (e * sInv) % n;
     173              :   BigInt u2 = (r * sInv) % n;
     174              : 
     175              :   // 1.4.5 Compute R = (xR, yR)
     176              :   //               R = u1G + u2Q
     177              :   ECPoint R = G * u1 + Q * u2;
     178              : 
     179              :   // 1.4.5 (cont.) Enforce R is not at infinity
     180              :   if (R.isInfinity) return false;
     181              : 
     182              :   // 1.4.6 Convert the field element R.x to an integer
     183              :   BigInt xR = R.x.toBigInteger();
     184              : 
     185              :   // 1.4.7 Set v = xR mod n
     186              :   BigInt v = xR % n;
     187              : 
     188              :   // 1.4.8 If v = r, output "valid", and if v != r, output "invalid"
     189              :   return v.compareTo(r) == 0;
     190              :   */
     191              : }
     192              : 
     193              : /// Decode a BigInt from bytes in big-endian encoding.
     194            3 : BigInt _decodeBigInt(List<int> bytes) {
     195            3 :   BigInt result = new BigInt.from(0);
     196            9 :   for (int i = 0; i < bytes.length; i++) {
     197           24 :     result += new BigInt.from(bytes[bytes.length - i - 1]) << (8 * i);
     198              :   }
     199              :   return result;
     200              : }
     201              : 
     202            0 : var _byteMask = new BigInt.from(0xff);
     203              : 
     204              : /// Encode a BigInt into bytes using big-endian encoding.
     205            0 : Uint8List _encodeBigInt(BigInt number) {
     206              :   int needsPaddingByte;
     207              :   int rawSize;
     208              : 
     209            0 :   if (number > BigInt.zero) {
     210            0 :     rawSize = (number.bitLength + 7) >> 3;
     211              :     needsPaddingByte =
     212            0 :         ((number >> (rawSize - 1) * 8) & negativeFlag) == negativeFlag ? 1 : 0;
     213              : 
     214            0 :     if (rawSize < 32) {
     215              :       needsPaddingByte = 1;
     216              :     }
     217              :   } else {
     218              :     needsPaddingByte = 0;
     219            0 :     rawSize = (number.bitLength + 8) >> 3;
     220              :   }
     221              : 
     222            0 :   final size = rawSize < 32 ? rawSize + needsPaddingByte : rawSize;
     223            0 :   var result = new Uint8List(size);
     224            0 :   for (int i = 0; i < size; i++) {
     225            0 :     result[size - i - 1] = (number & _byteMask).toInt();
     226            0 :     number = number >> 8;
     227              :   }
     228              :   return result;
     229              : }
     230              : 
     231            3 : BigInt fromBuffer(Uint8List d) {
     232            3 :   return _decodeBigInt(d);
     233              : }
     234              : 
     235            0 : Uint8List toBuffer(BigInt d) {
     236            0 :   return _encodeBigInt(d);
     237              : }
     238              : 
     239            1 : ECPoint? decodeFrom(Uint8List P) {
     240            3 :   return secp256k1.curve.decodePoint(P);
     241              : }
     242              : 
     243            2 : Uint8List getEncoded(ECPoint? P, compressed) {
     244            2 :   return P!.getEncoded(compressed);
     245              : }
     246              : 
     247            0 : ECSignature deterministicGenerateK(Uint8List hash, Uint8List x) {
     248            0 :   final signer = new ECDSASigner(null, new HMac(new SHA256Digest(), 64));
     249              :   var pkp =
     250            0 :       new PrivateKeyParameter(new ECPrivateKey(_decodeBigInt(x), secp256k1));
     251            0 :   signer.init(true, pkp);
     252              : //  signer.init(false, new PublicKeyParameter(new ECPublicKey(secp256k1.curve.decodePoint(x), secp256k1)));
     253            0 :   return signer.generateSignature(hash) as ECSignature;
     254              : }
     255              : 
     256            3 : int _compare(Uint8List a, Uint8List b) {
     257            3 :   BigInt aa = fromBuffer(a);
     258            3 :   BigInt bb = fromBuffer(b);
     259            3 :   if (aa == bb) return 0;
     260            3 :   if (aa > bb) return 1;
     261            3 :   return -1;
     262              : }
        

Generated by: LCOV version 2.0-1