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 : final publickey =
62 9 : bytesToUnsignedInt(privateKeyToPublic(bytesToUnsignedInt(privateKey)));
63 :
64 6 : final rcID = EC.secp256k1.calculateRecoveryId(publickey, sig, payload);
65 :
66 : if (rcID == null) {
67 0 : throw Exception('Failed to calculate recovery id');
68 : }
69 :
70 : final int v = switch (txType) {
71 3 : TransactionType.Legacy =>
72 6 : chainId != null ? (rcID + (chainId * 2 + 35)) : (rcID + 27),
73 0 : TransactionType.Type1 || TransactionType.Type2 => rcID,
74 : };
75 :
76 9 : return Signature(sig.r, sig.s, v);
77 : }
78 :
79 0 : int get yParity => v % 2;
80 :
81 : static const _messagePrefix = '\u0019Ethereum Signed Message:\n';
82 :
83 0 : static Uint8List signPersonalMessageToUint8List(
84 : Uint8List payload, Uint8List privateKey) {
85 0 : final prefix = _messagePrefix + payload.length.toString();
86 0 : final prefixBytes = ascii.encode(prefix);
87 :
88 0 : final concat = uint8ListFromList(prefixBytes + payload);
89 :
90 0 : final signature = Signature.createSignature(concat, privateKey);
91 0 : return signature.bytes;
92 : }
93 :
94 0 : bool isValidETHSignature(
95 : Uint8List payload,
96 : Uint8List publicKey,
97 : ) {
98 0 : payload = keccak256(payload);
99 0 : final recoverdPublicKey = recoverPublicKey(payload, this);
100 :
101 0 : return recoverdPublicKey.toHex == publicKey.toHex;
102 : }
103 :
104 1 : Uint8List get bytes {
105 3 : final rBytes = padUint8ListTo32(p_utils.encodeBigIntAsUnsigned(r));
106 3 : final sBytes = padUint8ListTo32(p_utils.encodeBigIntAsUnsigned(s));
107 :
108 4 : return Uint8List.fromList([...rBytes, ...sBytes, v]);
109 : }
110 :
111 1 : String get hex {
112 2 : return bytes.toHex;
113 : }
114 : }
|