UNPKG

8.89 kBJavaScriptView Raw
1'use strict';
2
3var BN = require('./bn');
4var _ = require('lodash');
5var $ = require('../util/preconditions');
6var BufferUtil = require('../util/buffer');
7var JSUtil = require('../util/js');
8
9var Signature = function Signature(r, s) {
10 if (!(this instanceof Signature)) {
11 return new Signature(r, s);
12 }
13 if (r instanceof BN) {
14 this.set({
15 r: r,
16 s: s
17 });
18 } else if (r) {
19 var obj = r;
20 this.set(obj);
21 }
22};
23
24/* jshint maxcomplexity: 7 */
25Signature.prototype.set = function(obj) {
26 this.r = obj.r || this.r || undefined;
27 this.s = obj.s || this.s || undefined;
28
29 this.i = typeof obj.i !== 'undefined' ? obj.i : this.i; //public key recovery parameter in range [0, 3]
30 this.compressed = typeof obj.compressed !== 'undefined' ?
31 obj.compressed : this.compressed; //whether the recovered pubkey is compressed
32 this.nhashtype = obj.nhashtype || this.nhashtype || undefined;
33 return this;
34};
35
36Signature.fromCompact = function(buf) {
37 $.checkArgument(BufferUtil.isBuffer(buf), 'Argument is expected to be a Buffer');
38
39 var sig = new Signature();
40
41 var compressed = true;
42 var i = buf.slice(0, 1)[0] - 27 - 4;
43 if (i < 0) {
44 compressed = false;
45 i = i + 4;
46 }
47
48 var b2 = buf.slice(1, 33);
49 var b3 = buf.slice(33, 65);
50
51 $.checkArgument(i === 0 || i === 1 || i === 2 || i === 3, new Error('i must be 0, 1, 2, or 3'));
52 $.checkArgument(b2.length === 32, new Error('r must be 32 bytes'));
53 $.checkArgument(b3.length === 32, new Error('s must be 32 bytes'));
54
55 sig.compressed = compressed;
56 sig.i = i;
57 sig.r = BN.fromBuffer(b2);
58 sig.s = BN.fromBuffer(b3);
59
60 return sig;
61};
62
63Signature.fromDER = Signature.fromBuffer = function(buf, strict) {
64 var obj = Signature.parseDER(buf, strict);
65 var sig = new Signature();
66
67 sig.r = obj.r;
68 sig.s = obj.s;
69
70 return sig;
71};
72
73// The format used in a tx
74Signature.fromTxFormat = function(buf) {
75 var nhashtype = buf.readUInt8(buf.length - 1);
76 var derbuf = buf.slice(0, buf.length - 1);
77 var sig = new Signature.fromDER(derbuf, false);
78 sig.nhashtype = nhashtype;
79 return sig;
80};
81
82Signature.fromString = function(str) {
83 var buf = Buffer.from(str, 'hex');
84 return Signature.fromDER(buf);
85};
86
87
88/**
89 * In order to mimic the non-strict DER encoding of OpenSSL, set strict = false.
90 */
91Signature.parseDER = function(buf, strict) {
92 $.checkArgument(BufferUtil.isBuffer(buf), new Error('DER formatted signature should be a buffer'));
93 if (_.isUndefined(strict)) {
94 strict = true;
95 }
96
97 var header = buf[0];
98 $.checkArgument(header === 0x30, new Error('Header byte should be 0x30'));
99
100 var length = buf[1];
101 var buflength = buf.slice(2).length;
102 $.checkArgument(!strict || length === buflength, new Error('Length byte should length of what follows'));
103
104 length = length < buflength ? length : buflength;
105
106 var rheader = buf[2 + 0];
107 $.checkArgument(rheader === 0x02, new Error('Integer byte for r should be 0x02'));
108
109 var rlength = buf[2 + 1];
110 var rbuf = buf.slice(2 + 2, 2 + 2 + rlength);
111 var r = BN.fromBuffer(rbuf);
112 var rneg = buf[2 + 1 + 1] === 0x00 ? true : false;
113 $.checkArgument(rlength === rbuf.length, new Error('Length of r incorrect'));
114
115 var sheader = buf[2 + 2 + rlength + 0];
116 $.checkArgument(sheader === 0x02, new Error('Integer byte for s should be 0x02'));
117
118 var slength = buf[2 + 2 + rlength + 1];
119 var sbuf = buf.slice(2 + 2 + rlength + 2, 2 + 2 + rlength + 2 + slength);
120 var s = BN.fromBuffer(sbuf);
121 var sneg = buf[2 + 2 + rlength + 2 + 2] === 0x00 ? true : false;
122 $.checkArgument(slength === sbuf.length, new Error('Length of s incorrect'));
123
124 var sumlength = 2 + 2 + rlength + 2 + slength;
125 $.checkArgument(length === sumlength - 2, new Error('Length of signature incorrect'));
126
127 var obj = {
128 header: header,
129 length: length,
130 rheader: rheader,
131 rlength: rlength,
132 rneg: rneg,
133 rbuf: rbuf,
134 r: r,
135 sheader: sheader,
136 slength: slength,
137 sneg: sneg,
138 sbuf: sbuf,
139 s: s
140 };
141
142 return obj;
143};
144
145
146Signature.prototype.toCompact = function(i, compressed) {
147 i = typeof i === 'number' ? i : this.i;
148 compressed = typeof compressed === 'boolean' ? compressed : this.compressed;
149
150 if (!(i === 0 || i === 1 || i === 2 || i === 3)) {
151 throw new Error('i must be equal to 0, 1, 2, or 3');
152 }
153
154 var val = i + 27 + 4;
155 if (compressed === false) {
156 val = val - 4;
157 }
158 var b1 = Buffer.from([val]);
159 var b2 = this.r.toBuffer({
160 size: 32
161 });
162 var b3 = this.s.toBuffer({
163 size: 32
164 });
165 return Buffer.concat([b1, b2, b3]);
166};
167
168Signature.prototype.toBuffer = Signature.prototype.toDER = function() {
169 var rnbuf = this.r.toBuffer();
170 var snbuf = this.s.toBuffer();
171
172 var rneg = rnbuf[0] & 0x80 ? true : false;
173 var sneg = snbuf[0] & 0x80 ? true : false;
174
175 var rbuf = rneg ? Buffer.concat([Buffer.from([0x00]), rnbuf]) : rnbuf;
176 var sbuf = sneg ? Buffer.concat([Buffer.from([0x00]), snbuf]) : snbuf;
177
178 var rlength = rbuf.length;
179 var slength = sbuf.length;
180 var length = 2 + rlength + 2 + slength;
181 var rheader = 0x02;
182 var sheader = 0x02;
183 var header = 0x30;
184
185 var der = Buffer.concat([Buffer.from([header, length, rheader, rlength]), rbuf, Buffer.from([sheader, slength]), sbuf]);
186 return der;
187};
188
189Signature.prototype.toString = function() {
190 var buf = this.toDER();
191 return buf.toString('hex');
192};
193
194/**
195 * This function is translated from bitcoind's IsDERSignature and is used in
196 * the script interpreter. This "DER" format actually includes an extra byte,
197 * the nhashtype, at the end. It is really the tx format, not DER format.
198 *
199 * A canonical signature exists of: [30] [total len] [02] [len R] [R] [02] [len S] [S] [hashtype]
200 * Where R and S are not negative (their first byte has its highest bit not set), and not
201 * excessively padded (do not start with a 0 byte, unless an otherwise negative number follows,
202 * in which case a single 0 byte is necessary and even required).
203 *
204 * See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623
205 */
206Signature.isTxDER = function(buf) {
207 if (buf.length < 9) {
208 // Non-canonical signature: too short
209 return false;
210 }
211 if (buf.length > 73) {
212 // Non-canonical signature: too long
213 return false;
214 }
215 if (buf[0] !== 0x30) {
216 // Non-canonical signature: wrong type
217 return false;
218 }
219 if (buf[1] !== buf.length - 3) {
220 // Non-canonical signature: wrong length marker
221 return false;
222 }
223 var nLenR = buf[3];
224 if (5 + nLenR >= buf.length) {
225 // Non-canonical signature: S length misplaced
226 return false;
227 }
228 var nLenS = buf[5 + nLenR];
229 if ((nLenR + nLenS + 7) !== buf.length) {
230 // Non-canonical signature: R+S length mismatch
231 return false;
232 }
233
234 var R = buf.slice(4);
235 if (buf[4 - 2] !== 0x02) {
236 // Non-canonical signature: R value type mismatch
237 return false;
238 }
239 if (nLenR === 0) {
240 // Non-canonical signature: R length is zero
241 return false;
242 }
243 if (R[0] & 0x80) {
244 // Non-canonical signature: R value negative
245 return false;
246 }
247 if (nLenR > 1 && (R[0] === 0x00) && !(R[1] & 0x80)) {
248 // Non-canonical signature: R value excessively padded
249 return false;
250 }
251
252 var S = buf.slice(6 + nLenR);
253 if (buf[6 + nLenR - 2] !== 0x02) {
254 // Non-canonical signature: S value type mismatch
255 return false;
256 }
257 if (nLenS === 0) {
258 // Non-canonical signature: S length is zero
259 return false;
260 }
261 if (S[0] & 0x80) {
262 // Non-canonical signature: S value negative
263 return false;
264 }
265 if (nLenS > 1 && (S[0] === 0x00) && !(S[1] & 0x80)) {
266 // Non-canonical signature: S value excessively padded
267 return false;
268 }
269 return true;
270};
271
272/**
273 * Compares to bitcoind's IsLowDERSignature
274 * See also ECDSA signature algorithm which enforces this.
275 * See also BIP 62, "low S values in signatures"
276 */
277Signature.prototype.hasLowS = function() {
278 if (this.s.lt(new BN(1)) ||
279 this.s.gt(new BN('7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0', 'hex'))) {
280 return false;
281 }
282 return true;
283};
284
285/**
286 * @returns true if the nhashtype is exactly equal to one of the standard options or combinations thereof.
287 * Translated from bitcoind's IsDefinedHashtypeSignature
288 */
289Signature.prototype.hasDefinedHashtype = function() {
290 if (!JSUtil.isNaturalNumber(this.nhashtype)) {
291 return false;
292 }
293 // accept with or without Signature.SIGHASH_ANYONECANPAY by ignoring the bit
294 var temp = this.nhashtype & ~Signature.SIGHASH_ANYONECANPAY;
295 if (temp < Signature.SIGHASH_ALL || temp > Signature.SIGHASH_SINGLE) {
296 return false;
297 }
298 return true;
299};
300
301Signature.prototype.toTxFormat = function() {
302 var derbuf = this.toDER();
303 var buf = Buffer.alloc(1);
304 buf.writeUInt8(this.nhashtype, 0);
305 return Buffer.concat([derbuf, buf]);
306};
307
308Signature.SIGHASH_ALL = 0x01;
309Signature.SIGHASH_NONE = 0x02;
310Signature.SIGHASH_SINGLE = 0x03;
311Signature.SIGHASH_ANYONECANPAY = 0x80;
312
313module.exports = Signature;