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 : }
|