UNPKG

10.6 kBJavaScriptView Raw
1/**
2 * Signature
3 * =========
4 *
5 * A signature is the thing you make when you want to sign a transaction, or
6 * the thing you want to verify if you want to ensure that someone signed a
7 * transaction. It has an r and s value, which are the cryptographic big
8 * numbers that define a signature. And since this is a bitcoin library, it
9 * also has nHashType, which is the way to hash a transaction and is used in
10 * the binary format of a signature when it is in a transaction. We also
11 * support a public key recover value, recovery, allowing one to compute the
12 * public key from a signature. The "compressed" value is also necessary to
13 * accurately compute the public key from a signature.
14 *
15 * There are a few different formats of a signature in bitcoin. One is DER, the
16 * other is the TxFormat which is the same as DER but with the nHashType byte
17 * appended, and the final one is Compact, which is used by Bitcoin Signed
18 * Message (Bsm).
19 */
20'use strict'
21
22import { Bn } from './bn'
23import { Struct } from './struct'
24
25/**
26 * r, s: big numbers constiting a cryptographic signature
27 * nHashType: found at the end of a signature in a transaction
28 * recovery: public key recovery number
29 * compressed: whether the recovered pubKey is compressed
30 */
31class Sig extends Struct {
32 constructor (r, s, nHashType, recovery, compressed) {
33 super({ r, s, nHashType, recovery, compressed })
34 }
35
36 fromBuffer (buf) {
37 try {
38 return this.fromDer(buf, true)
39 } catch (e) {}
40 try {
41 return this.fromCompact(buf)
42 } catch (e) {}
43 return this.fromTxFormat(buf)
44 }
45
46 toBuffer () {
47 if (this.nHashType !== undefined) {
48 return this.toTxFormat()
49 } else if (this.recovery !== undefined) {
50 return this.toCompact()
51 }
52 return this.toDer()
53 }
54
55 // The format used by "message"
56 fromCompact (buf) {
57 let compressed = true
58 let recovery = buf.slice(0, 1)[0] - 27 - 4
59 if (recovery < 0) {
60 compressed = false
61 recovery = recovery + 4
62 }
63
64 if (
65 !(recovery === 0 || recovery === 1 || recovery === 2 || recovery === 3)
66 ) {
67 throw new Error('i must be 0, 1, 2, or 3')
68 }
69
70 this.compressed = compressed
71 this.recovery = recovery
72
73 const rsbuf = buf.slice(1)
74 this.fromRS(rsbuf)
75
76 return this
77 }
78
79 static fromCompact (buf) {
80 return new this().fromCompact(buf)
81 }
82
83 fromRS (rsbuf) {
84 const b2 = rsbuf.slice(0, 32)
85 const b3 = rsbuf.slice(32, 64)
86 if (b2.length !== 32) {
87 throw new Error('r must be 32 bytes')
88 }
89 if (b3.length !== 32 || rsbuf.length > 64) {
90 throw new Error('s must be 32 bytes')
91 }
92 this.r = new Bn().fromBuffer(b2)
93 this.s = new Bn().fromBuffer(b3)
94 return this
95 }
96
97 static fromRS (rsbuf) {
98 return new this().fromRS(rsbuf)
99 }
100
101 // The format used in a tx, except without the nHashType at the end
102 fromDer (buf, strict) {
103 const obj = Sig.parseDer(buf, strict)
104 this.r = obj.r
105 this.s = obj.s
106
107 return this
108 }
109
110 static fromDer (buf, strict) {
111 return new this().fromDer(buf, strict)
112 }
113
114 // The format used in a tx
115 fromTxFormat (buf) {
116 if (buf.length === 0) {
117 // allow setting a "blank" signature
118 this.r = new Bn(1)
119 this.s = new Bn(1)
120 this.nHashType = 1
121 return this
122 }
123 const nHashType = buf.readUInt8(buf.length - 1)
124 const derbuf = buf.slice(0, buf.length - 1)
125 this.fromDer(derbuf, false)
126 this.nHashType = nHashType
127 return this
128 }
129
130 static fromTxFormat (buf) {
131 return new this().fromTxFormat(buf)
132 }
133
134 fromString (str) {
135 return this.fromHex(str)
136 }
137
138 /**
139 * In order to mimic the non-strict DER encoding of OpenSSL, set strict = false.
140 */
141 static parseDer (buf, strict) {
142 if (strict === undefined) {
143 strict = true
144 }
145
146 if (!Buffer.isBuffer(buf)) {
147 throw new Error('DER formatted signature should be a buffer')
148 }
149
150 const header = buf[0]
151
152 if (header !== 0x30) {
153 throw new Error('Header byte should be 0x30')
154 }
155
156 let length = buf[1]
157 const buflength = buf.slice(2).length
158 if (strict && length !== buflength) {
159 throw new Error('LEngth byte should length of what follows')
160 } else {
161 length = length < buflength ? length : buflength
162 }
163
164 const rheader = buf[2 + 0]
165 if (rheader !== 0x02) {
166 throw new Error('Integer byte for r should be 0x02')
167 }
168
169 const rlength = buf[2 + 1]
170 const rbuf = buf.slice(2 + 2, 2 + 2 + rlength)
171 const r = new Bn().fromBuffer(rbuf)
172 const rneg = buf[2 + 1 + 1] === 0x00
173 if (rlength !== rbuf.length) {
174 throw new Error('LEngth of r incorrect')
175 }
176
177 const sheader = buf[2 + 2 + rlength + 0]
178 if (sheader !== 0x02) {
179 throw new Error('Integer byte for s should be 0x02')
180 }
181
182 const slength = buf[2 + 2 + rlength + 1]
183 const sbuf = buf.slice(2 + 2 + rlength + 2, 2 + 2 + rlength + 2 + slength)
184 const s = new Bn().fromBuffer(sbuf)
185 const sneg = buf[2 + 2 + rlength + 2 + 2] === 0x00
186 if (slength !== sbuf.length) {
187 throw new Error('LEngth of s incorrect')
188 }
189
190 const sumlength = 2 + 2 + rlength + 2 + slength
191 if (length !== sumlength - 2) {
192 throw new Error('LEngth of signature incorrect')
193 }
194
195 const obj = {
196 header: header,
197 length: length,
198 rheader: rheader,
199 rlength: rlength,
200 rneg: rneg,
201 rbuf: rbuf,
202 r: r,
203 sheader: sheader,
204 slength: slength,
205 sneg: sneg,
206 sbuf: sbuf,
207 s: s
208 }
209
210 return obj
211 }
212
213 /**
214 * This function is translated from bitcoind's IsDERSignature and is used in
215 * the script interpreter. This "DER" format actually includes an extra byte,
216 * the nHashType, at the end. It is really the tx format, not DER format.
217 *
218 * A canonical signature exists of: [30] [total len] [02] [len R] [R] [02] [len S] [S] [hashtype]
219 * Where R and S are not negative (their first byte has its highest bit not set), and not
220 * excessively padded (do not start with a 0 byte, unless an otherwise negative number follows,
221 * in which case a single 0 byte is necessary and even required).
222 *
223 * See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623
224 */
225 static IsTxDer (buf) {
226 if (buf.length < 9) {
227 // Non-canonical signature: too short
228 return false
229 }
230 if (buf.length > 73) {
231 // Non-canonical signature: too long
232 return false
233 }
234 if (buf[0] !== 0x30) {
235 // Non-canonical signature: wrong type
236 return false
237 }
238 if (buf[1] !== buf.length - 3) {
239 // Non-canonical signature: wrong length marker
240 return false
241 }
242 const nLEnR = buf[3]
243 if (5 + nLEnR >= buf.length) {
244 // Non-canonical signature: S length misplaced
245 return false
246 }
247 const nLEnS = buf[5 + nLEnR]
248 if (nLEnR + nLEnS + 7 !== buf.length) {
249 // Non-canonical signature: R+S length mismatch
250 return false
251 }
252
253 const R = buf.slice(4)
254 if (buf[4 - 2] !== 0x02) {
255 // Non-canonical signature: R value type mismatch
256 return false
257 }
258 if (nLEnR === 0) {
259 // Non-canonical signature: R length is zero
260 return false
261 }
262 if (R[0] & 0x80) {
263 // Non-canonical signature: R value negative
264 return false
265 }
266 if (nLEnR > 1 && R[0] === 0x00 && !(R[1] & 0x80)) {
267 // Non-canonical signature: R value excessively padded
268 return false
269 }
270
271 const S = buf.slice(6 + nLEnR)
272 if (buf[6 + nLEnR - 2] !== 0x02) {
273 // Non-canonical signature: S value type mismatch
274 return false
275 }
276 if (nLEnS === 0) {
277 // Non-canonical signature: S length is zero
278 return false
279 }
280 if (S[0] & 0x80) {
281 // Non-canonical signature: S value negative
282 return false
283 }
284 if (nLEnS > 1 && S[0] === 0x00 && !(S[1] & 0x80)) {
285 // Non-canonical signature: S value excessively padded
286 return false
287 }
288 return true
289 }
290
291 /**
292 * Compares to bitcoind's IsLowDERSignature
293 * See also Ecdsa signature algorithm which enforces this.
294 * See also Bip 62, "low S values in signatures"
295 */
296 hasLowS () {
297 if (
298 this.s.lt(1) ||
299 this.s.gt(
300 Bn.fromBuffer(
301 Buffer.from(
302 '7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0',
303 'hex'
304 )
305 )
306 )
307 ) {
308 return false
309 }
310 return true
311 }
312
313 /**
314 * Ensures the nHashType is exactly equal to one of the standard options or combinations thereof.
315 * Translated from bitcoind's IsDefinedHashtypeSignature
316 */
317 hasDefinedHashType () {
318 if (
319 this.nHashType < Sig.SIGHASH_ALL ||
320 this.nHashType > Sig.SIGHASH_SINGLE
321 ) {
322 return false
323 }
324 return true
325 }
326
327 toCompact (recovery, compressed) {
328 recovery = typeof recovery === 'number' ? recovery : this.recovery
329 compressed =
330 typeof compressed === 'boolean' ? compressed : this.compressed
331
332 if (
333 !(recovery === 0 || recovery === 1 || recovery === 2 || recovery === 3)
334 ) {
335 throw new Error('recovery must be equal to 0, 1, 2, or 3')
336 }
337
338 let val = recovery + 27 + 4
339 if (compressed === false) {
340 val = val - 4
341 }
342 const b1 = Buffer.from([val])
343 const b2 = this.r.toBuffer({ size: 32 })
344 const b3 = this.s.toBuffer({ size: 32 })
345 return Buffer.concat([b1, b2, b3])
346 }
347
348 toRS () {
349 return Buffer.concat([
350 this.r.toBuffer({ size: 32 }),
351 this.s.toBuffer({ size: 32 })
352 ])
353 }
354
355 toDer () {
356 const rnbuf = this.r.toBuffer()
357 const snbuf = this.s.toBuffer()
358
359 const rneg = rnbuf[0] & 0x80
360 const sneg = snbuf[0] & 0x80
361
362 const rbuf = rneg ? Buffer.concat([Buffer.from([0x00]), rnbuf]) : rnbuf
363 const sbuf = sneg ? Buffer.concat([Buffer.from([0x00]), snbuf]) : snbuf
364
365 const length = 2 + rbuf.length + 2 + sbuf.length
366 const rlength = rbuf.length
367 const slength = sbuf.length
368 const rheader = 0x02
369 const sheader = 0x02
370 const header = 0x30
371
372 const der = Buffer.concat([
373 Buffer.from([header, length, rheader, rlength]),
374 rbuf,
375 Buffer.from([sheader, slength]),
376 sbuf
377 ])
378 return der
379 }
380
381 toTxFormat () {
382 const derbuf = this.toDer()
383 const buf = Buffer.alloc(1)
384 buf.writeUInt8(this.nHashType, 0)
385 return Buffer.concat([derbuf, buf])
386 }
387
388 toString () {
389 return this.toHex()
390 }
391}
392
393Sig.SIGHASH_ALL = 0x00000001
394Sig.SIGHASH_NONE = 0x00000002
395Sig.SIGHASH_SINGLE = 0x00000003
396Sig.SIGHASH_FORKID = 0x00000040
397Sig.SIGHASH_ANYONECANPAY = 0x00000080
398
399export { Sig }