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

Generated by: LCOV version 2.0-1