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