Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ultraviolet
GitHub Repository: ultraviolet/bitaddress.org
Path: blob/master/src/bitcoinjs-lib.ecdsa.js
248 views
1
//https://raw.github.com/bitcoinjs/bitcoinjs-lib/e90780d3d3b8fc0d027d2bcb38b80479902f223e/src/ecdsa.js
2
Bitcoin.ECDSA = (function () {
3
var ecparams = EllipticCurve.getSECCurveByName("secp256k1");
4
var rng = new SecureRandom();
5
6
var P_OVER_FOUR = null;
7
8
function implShamirsTrick(P, k, Q, l) {
9
var m = Math.max(k.bitLength(), l.bitLength());
10
var Z = P.add2D(Q);
11
var R = P.curve.getInfinity();
12
13
for (var i = m - 1; i >= 0; --i) {
14
R = R.twice2D();
15
16
R.z = BigInteger.ONE;
17
18
if (k.testBit(i)) {
19
if (l.testBit(i)) {
20
R = R.add2D(Z);
21
} else {
22
R = R.add2D(P);
23
}
24
} else {
25
if (l.testBit(i)) {
26
R = R.add2D(Q);
27
}
28
}
29
}
30
31
return R;
32
};
33
34
var ECDSA = {
35
getBigRandom: function (limit) {
36
return new BigInteger(limit.bitLength(), rng)
37
.mod(limit.subtract(BigInteger.ONE))
38
.add(BigInteger.ONE);
39
},
40
sign: function (hash, priv) {
41
var d = priv;
42
var n = ecparams.getN();
43
var e = BigInteger.fromByteArrayUnsigned(hash);
44
45
do {
46
var k = ECDSA.getBigRandom(n);
47
var G = ecparams.getG();
48
var Q = G.multiply(k);
49
var r = Q.getX().toBigInteger().mod(n);
50
} while (r.compareTo(BigInteger.ZERO) <= 0);
51
52
var s = k.modInverse(n).multiply(e.add(d.multiply(r))).mod(n);
53
54
return ECDSA.serializeSig(r, s);
55
},
56
57
verify: function (hash, sig, pubkey) {
58
var r, s;
59
if (Bitcoin.Util.isArray(sig)) {
60
var obj = ECDSA.parseSig(sig);
61
r = obj.r;
62
s = obj.s;
63
} else if ("object" === typeof sig && sig.r && sig.s) {
64
r = sig.r;
65
s = sig.s;
66
} else {
67
throw "Invalid value for signature";
68
}
69
70
var Q;
71
if (pubkey instanceof ec.PointFp) {
72
Q = pubkey;
73
} else if (Bitcoin.Util.isArray(pubkey)) {
74
Q = EllipticCurve.PointFp.decodeFrom(ecparams.getCurve(), pubkey);
75
} else {
76
throw "Invalid format for pubkey value, must be byte array or ec.PointFp";
77
}
78
var e = BigInteger.fromByteArrayUnsigned(hash);
79
80
return ECDSA.verifyRaw(e, r, s, Q);
81
},
82
83
verifyRaw: function (e, r, s, Q) {
84
var n = ecparams.getN();
85
var G = ecparams.getG();
86
87
if (r.compareTo(BigInteger.ONE) < 0 ||
88
r.compareTo(n) >= 0)
89
return false;
90
91
if (s.compareTo(BigInteger.ONE) < 0 ||
92
s.compareTo(n) >= 0)
93
return false;
94
95
var c = s.modInverse(n);
96
97
var u1 = e.multiply(c).mod(n);
98
var u2 = r.multiply(c).mod(n);
99
100
// TODO(!!!): For some reason Shamir's trick isn't working with
101
// signed message verification!? Probably an implementation
102
// error!
103
//var point = implShamirsTrick(G, u1, Q, u2);
104
var point = G.multiply(u1).add(Q.multiply(u2));
105
106
var v = point.getX().toBigInteger().mod(n);
107
108
return v.equals(r);
109
},
110
111
/**
112
* Serialize a signature into DER format.
113
*
114
* Takes two BigIntegers representing r and s and returns a byte array.
115
*/
116
serializeSig: function (r, s) {
117
var rBa = r.toByteArraySigned();
118
var sBa = s.toByteArraySigned();
119
120
var sequence = [];
121
sequence.push(0x02); // INTEGER
122
sequence.push(rBa.length);
123
sequence = sequence.concat(rBa);
124
125
sequence.push(0x02); // INTEGER
126
sequence.push(sBa.length);
127
sequence = sequence.concat(sBa);
128
129
sequence.unshift(sequence.length);
130
sequence.unshift(0x30); // SEQUENCE
131
132
return sequence;
133
},
134
135
/**
136
* Parses a byte array containing a DER-encoded signature.
137
*
138
* This function will return an object of the form:
139
*
140
* {
141
* r: BigInteger,
142
* s: BigInteger
143
* }
144
*/
145
parseSig: function (sig) {
146
var cursor;
147
if (sig[0] != 0x30)
148
throw new Error("Signature not a valid DERSequence");
149
150
cursor = 2;
151
if (sig[cursor] != 0x02)
152
throw new Error("First element in signature must be a DERInteger"); ;
153
var rBa = sig.slice(cursor + 2, cursor + 2 + sig[cursor + 1]);
154
155
cursor += 2 + sig[cursor + 1];
156
if (sig[cursor] != 0x02)
157
throw new Error("Second element in signature must be a DERInteger");
158
var sBa = sig.slice(cursor + 2, cursor + 2 + sig[cursor + 1]);
159
160
cursor += 2 + sig[cursor + 1];
161
162
//if (cursor != sig.length)
163
// throw new Error("Extra bytes in signature");
164
165
var r = BigInteger.fromByteArrayUnsigned(rBa);
166
var s = BigInteger.fromByteArrayUnsigned(sBa);
167
168
return { r: r, s: s };
169
},
170
171
parseSigCompact: function (sig) {
172
if (sig.length !== 65) {
173
throw "Signature has the wrong length";
174
}
175
176
// Signature is prefixed with a type byte storing three bits of
177
// information.
178
var i = sig[0] - 27;
179
if (i < 0 || i > 7) {
180
throw "Invalid signature type";
181
}
182
183
var n = ecparams.getN();
184
var r = BigInteger.fromByteArrayUnsigned(sig.slice(1, 33)).mod(n);
185
var s = BigInteger.fromByteArrayUnsigned(sig.slice(33, 65)).mod(n);
186
187
return { r: r, s: s, i: i };
188
},
189
190
/**
191
* Recover a public key from a signature.
192
*
193
* See SEC 1: Elliptic Curve Cryptography, section 4.1.6, "Public
194
* Key Recovery Operation".
195
*
196
* http://www.secg.org/download/aid-780/sec1-v2.pdf
197
*/
198
recoverPubKey: function (r, s, hash, i) {
199
// The recovery parameter i has two bits.
200
i = i & 3;
201
202
// The less significant bit specifies whether the y coordinate
203
// of the compressed point is even or not.
204
var isYEven = i & 1;
205
206
// The more significant bit specifies whether we should use the
207
// first or second candidate key.
208
var isSecondKey = i >> 1;
209
210
var n = ecparams.getN();
211
var G = ecparams.getG();
212
var curve = ecparams.getCurve();
213
var p = curve.getQ();
214
var a = curve.getA().toBigInteger();
215
var b = curve.getB().toBigInteger();
216
217
// We precalculate (p + 1) / 4 where p is if the field order
218
if (!P_OVER_FOUR) {
219
P_OVER_FOUR = p.add(BigInteger.ONE).divide(BigInteger.valueOf(4));
220
}
221
222
// 1.1 Compute x
223
var x = isSecondKey ? r.add(n) : r;
224
225
// 1.3 Convert x to point
226
var alpha = x.multiply(x).multiply(x).add(a.multiply(x)).add(b).mod(p);
227
var beta = alpha.modPow(P_OVER_FOUR, p);
228
229
var xorOdd = beta.isEven() ? (i % 2) : ((i + 1) % 2);
230
// If beta is even, but y isn't or vice versa, then convert it,
231
// otherwise we're done and y == beta.
232
var y = (beta.isEven() ? !isYEven : isYEven) ? beta : p.subtract(beta);
233
234
// 1.4 Check that nR is at infinity
235
var R = new EllipticCurve.PointFp(curve,
236
curve.fromBigInteger(x),
237
curve.fromBigInteger(y));
238
R.validate();
239
240
// 1.5 Compute e from M
241
var e = BigInteger.fromByteArrayUnsigned(hash);
242
var eNeg = BigInteger.ZERO.subtract(e).mod(n);
243
244
// 1.6 Compute Q = r^-1 (sR - eG)
245
var rInv = r.modInverse(n);
246
var Q = implShamirsTrick(R, s, G, eNeg).multiply(rInv);
247
248
Q.validate();
249
if (!ECDSA.verifyRaw(e, r, s, Q)) {
250
throw "Pubkey recovery unsuccessful";
251
}
252
253
var pubKey = new Bitcoin.ECKey();
254
pubKey.pub = Q;
255
return pubKey;
256
},
257
258
/**
259
* Calculate pubkey extraction parameter.
260
*
261
* When extracting a pubkey from a signature, we have to
262
* distinguish four different cases. Rather than putting this
263
* burden on the verifier, Bitcoin includes a 2-bit value with the
264
* signature.
265
*
266
* This function simply tries all four cases and returns the value
267
* that resulted in a successful pubkey recovery.
268
*/
269
calcPubkeyRecoveryParam: function (address, r, s, hash) {
270
for (var i = 0; i < 4; i++) {
271
try {
272
var pubkey = Bitcoin.ECDSA.recoverPubKey(r, s, hash, i);
273
if (pubkey.getBitcoinAddress().toString() == address) {
274
return i;
275
}
276
} catch (e) { }
277
}
278
throw "Unable to find valid recovery factor";
279
}
280
};
281
282
return ECDSA;
283
})();
284