Line data Source code
1 : import 'dart:convert';
2 : import 'dart:typed_data';
3 : import 'package:pointycastle/export.dart';
4 : import 'package:sec/sec.dart';
5 : import 'package:walletkit_dart/src/utils/keccak.dart';
6 : import 'package:walletkit_dart/walletkit_dart.dart';
7 : import 'package:pointycastle/src/utils.dart' as p_utils;
8 :
9 : class Signature {
10 : final BigInt r;
11 : final BigInt s;
12 : final int v;
13 :
14 0 : Uint8List get rBytes => padUint8ListTo32(p_utils.encodeBigIntAsUnsigned(r));
15 0 : Uint8List get sBytes => padUint8ListTo32(p_utils.encodeBigIntAsUnsigned(s));
16 :
17 4 : const Signature(this.r, this.s, this.v);
18 :
19 0 : @override
20 : String toString() {
21 0 : return 'Signature{r: $r, s: $s, v: $v}';
22 : }
23 :
24 3 : factory Signature.fromHex(String hex) => Signature.fromBytes(hex.hexToBytes);
25 :
26 2 : factory Signature.fromBytes(Uint8List bytes) {
27 4 : final r = bytes.sublist(0, 32).toUBigInt;
28 4 : final s = bytes.sublist(32, 64).toUBigInt;
29 2 : final v = bytes[64];
30 :
31 2 : return Signature(r, s, v);
32 : }
33 :
34 1 : factory Signature.fromRSV(BigInt r, BigInt s, int v) {
35 1 : return Signature(r, s, v);
36 : }
37 :
38 3 : factory Signature.createSignature(
39 : Uint8List payload,
40 : Uint8List privateKey, {
41 : TransactionType txType = TransactionType.Legacy,
42 : int? chainId,
43 : bool hashPayload = true,
44 : }) {
45 3 : final digest = SHA256Digest();
46 6 : final signer = ECDSASigner(null, HMac(digest, 64));
47 3 : final params = ECCurve_secp256k1();
48 6 : final key = ECPrivateKey(bytesToUnsignedInt(privateKey), params);
49 6 : signer.init(true, PrivateKeyParameter(key));
50 :
51 : if (hashPayload) {
52 2 : payload = keccak256(payload);
53 : }
54 3 : var sig = signer.generateSignature(payload) as ECSignature;
55 :
56 15 : if (sig.s.compareTo(params.n >> 1) > 0) {
57 0 : final canonicalS = params.n - sig.s;
58 0 : sig = ECSignature(sig.r, canonicalS);
59 : }
60 :
61 9 : final publickey = bytesToUnsignedInt(privateKeyToPublic(bytesToUnsignedInt(privateKey)));
62 :
63 6 : final rcID = EC.secp256k1.calculateRecoveryId(publickey, sig, payload);
64 :
65 : if (rcID == null) {
66 0 : throw Exception('Failed to calculate recovery id');
67 : }
68 :
69 : final int v = switch (txType) {
70 9 : TransactionType.Legacy => chainId != null ? (rcID + (chainId * 2 + 35)) : (rcID + 27),
71 0 : TransactionType.Type1 || TransactionType.Type2 => rcID,
72 : };
73 :
74 9 : return Signature(sig.r, sig.s, v);
75 : }
76 :
77 0 : int get yParity => v % 2;
78 :
79 : static const _messagePrefix = '\u0019Ethereum Signed Message:\n';
80 :
81 0 : static Uint8List signPersonalMessageToUint8List(Uint8List payload, Uint8List privateKey) {
82 0 : final prefix = _messagePrefix + payload.length.toString();
83 0 : final prefixBytes = ascii.encode(prefix);
84 :
85 0 : final concat = uint8ListFromList(prefixBytes + payload);
86 :
87 0 : final signature = Signature.createSignature(concat, privateKey);
88 0 : return signature.bytes;
89 : }
90 :
91 0 : bool isValidETHSignature(
92 : Uint8List payload,
93 : Uint8List publicKey,
94 : ) {
95 0 : payload = keccak256(payload);
96 0 : final recoverdPublicKey = recoverPublicKey(payload, this);
97 :
98 0 : return recoverdPublicKey.toHex == publicKey.toHex;
99 : }
100 :
101 1 : Uint8List get bytes {
102 3 : final rBytes = padUint8ListTo32(p_utils.encodeBigIntAsUnsigned(r));
103 3 : final sBytes = padUint8ListTo32(p_utils.encodeBigIntAsUnsigned(s));
104 :
105 4 : return Uint8List.fromList([...rBytes, ...sBytes, v]);
106 : }
107 :
108 1 : String get hex {
109 2 : return bytes.toHex;
110 : }
111 : }
|