Line data Source code
1 : import 'dart:typed_data';
2 : import 'package:pointycastle/ecc/api.dart';
3 : import 'package:pointycastle/ecc/curves/secp256k1.dart';
4 : import 'package:pointycastle/src/utils.dart' as p_utils;
5 : import 'package:walletkit_dart/src/crypto/evm/entities/raw_transaction/signature.dart';
6 : import 'package:walletkit_dart/src/crypto/utxo/utils/eurocoin_signing.dart';
7 :
8 12 : final params = ECCurve_secp256k1();
9 :
10 3 : Uint8List privateKeyToPublic(BigInt privateKey) {
11 9 : final p = (params.G * privateKey)!;
12 : //skip the type flag, https://github.com/ethereumjs/ethereumjs-util/blob/master/index.js#L319
13 9 : return Uint8List.view(p.getEncoded(false).buffer, 1);
14 : }
15 :
16 4 : BigInt bytesToUnsignedInt(Uint8List bytes) {
17 4 : return p_utils.decodeBigIntWithSign(1, bytes);
18 : }
19 :
20 2 : Uint8List recoverPublicKey(
21 : Uint8List payload,
22 : Signature signature, {
23 : bool hasSignatureYParity = false,
24 : }) {
25 6 : final r = padUint8ListTo32(p_utils.encodeBigIntAsUnsigned(signature.r));
26 6 : final s = padUint8ListTo32(p_utils.encodeBigIntAsUnsigned(signature.s));
27 :
28 6 : assert(r.length == 32);
29 6 : assert(s.length == 32);
30 :
31 2 : final v = signature.v;
32 : int chainId = 0;
33 : int recoveryId;
34 :
35 : // Type 1 and Type 2 Transactions
36 : if (hasSignatureYParity) {
37 1 : recoveryId = signature.v; // Since V is the signature parity which is 0 or 1
38 : } else {
39 : // Handle both pre-EIP-155 and EIP-155 v values
40 4 : if (v == 27 || v == 28) {
41 1 : recoveryId = v - 27;
42 : } else {
43 2 : chainId = (v - 35) ~/ 2;
44 3 : recoveryId = v - (2 * chainId + 35);
45 : }
46 : }
47 :
48 4 : if (recoveryId != 0 && recoveryId != 1) {
49 0 : throw Exception('Invalid recovery id');
50 : }
51 :
52 6 : final sig = ECSignature(signature.r, signature.s);
53 :
54 4 : final pubKey = _recoverFromSignature(recoveryId, sig, payload, params);
55 :
56 : if (pubKey == null) {
57 0 : throw Exception('Failed to recover public key');
58 : }
59 :
60 2 : return unsignedIntToBytes(pubKey);
61 : }
62 :
63 2 : ECPoint _decompressKey(BigInt xBN, bool yBit, ECCurve c) {
64 2 : List<int> x9IntegerToBytes(BigInt s, int qLength) {
65 : //https://github.com/bcgit/bc-java/blob/master/core/src/main/java/org/bouncycastle/asn1/x9/X9IntegerConverter.java#L45
66 2 : final bytes = p_utils.encodeBigInt(s);
67 :
68 4 : if (qLength < bytes.length) {
69 0 : return bytes.sublist(0, bytes.length - qLength);
70 4 : } else if (qLength > bytes.length) {
71 1 : final tmp = List<int>.filled(qLength, 0);
72 :
73 2 : final offset = qLength - bytes.length;
74 3 : for (var i = 0; i < bytes.length; i++) {
75 3 : tmp[i + offset] = bytes[i];
76 : }
77 :
78 : return tmp;
79 : }
80 :
81 : return bytes;
82 : }
83 :
84 10 : final compEnc = x9IntegerToBytes(xBN, 1 + ((c.fieldSize + 7) ~/ 8));
85 2 : compEnc[0] = yBit ? 0x03 : 0x02;
86 2 : return c.decodePoint(compEnc)!;
87 : }
88 :
89 2 : BigInt? _recoverFromSignature(
90 : int recId,
91 : ECSignature sig,
92 : Uint8List msg,
93 : ECDomainParameters params,
94 : ) {
95 2 : final n = params.n;
96 4 : final i = BigInt.from(recId ~/ 2);
97 6 : final x = sig.r + (i * n);
98 :
99 : //Parameter q of curve
100 2 : final prime = BigInt.parse(
101 : 'fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f',
102 : radix: 16,
103 : );
104 4 : if (x.compareTo(prime) >= 0) return null;
105 :
106 8 : final R = _decompressKey(x, (recId & 1) == 1, params.curve);
107 4 : if (!(R * n)!.isInfinity) return null;
108 :
109 2 : final e = bytesToUnsignedInt(msg);
110 :
111 6 : final eInv = (BigInt.zero - e) % n;
112 4 : final rInv = sig.r.modInverse(n);
113 6 : final srInv = (rInv * sig.s) % n;
114 4 : final eInvrInv = (rInv * eInv) % n;
115 :
116 8 : final q = (params.G * eInvrInv)! + (R * srInv);
117 :
118 2 : final bytes = q!.getEncoded(false);
119 4 : return bytesToUnsignedInt(bytes.sublist(1));
120 : }
121 :
122 0 : int extractChainId(int v) {
123 : // Ensure `v` is a BigInt
124 0 : if (v >= 35) {
125 0 : return (v - 35) >> 1;
126 : } else {
127 0 : throw ArgumentError("v parameter does not contain a chain id");
128 : }
129 : }
130 :
131 : /// Given a byte array computes its compressed version and returns it as a byte array,
132 : /// including the leading 02 or 03
133 0 : Uint8List compressPublicKey(Uint8List compressedPubKey) {
134 0 : return Uint8List.view(
135 0 : params.curve.decodePoint(compressedPubKey)!.getEncoded(true).buffer,
136 : );
137 : }
|