UNPKG

3.46 kBJavaScriptView Raw
1/**
2 * Bitcoin Signed Message
3 * ======================
4 *
5 * "Bitcoin Signed Message" just refers to a standard way of signing and
6 * verifying an arbitrary message. The standard way to do this involves using a
7 * "Bitcoin Signed Message:\n" prefix, which this code does. You are probably
8 * interested in the static Bsm.sign( ... ) and Bsm.verify( ... ) functions,
9 * which deal with a base64 string representing the compressed format of a
10 * signature.
11 */
12'use strict'
13
14import { Address } from './address'
15import { Bw } from './bw'
16import { cmp } from './cmp'
17import { Ecdsa } from './ecdsa'
18import { Hash } from './hash'
19import { KeyPair } from './key-pair'
20import { Sig } from './sig'
21import { Struct } from './struct'
22import { Workers } from './workers'
23
24class Bsm extends Struct {
25 constructor (messageBuf, keyPair, sig, address, verified) {
26 super({ messageBuf, keyPair, sig, address, verified })
27 }
28
29 static magicHash (messageBuf) {
30 if (!Buffer.isBuffer(messageBuf)) {
31 throw new Error('messageBuf must be a buffer')
32 }
33 const bw = new Bw()
34 bw.writeVarIntNum(Bsm.magicBytes.length)
35 bw.write(Bsm.magicBytes)
36 bw.writeVarIntNum(messageBuf.length)
37 bw.write(messageBuf)
38 const buf = bw.toBuffer()
39
40 const hashBuf = Hash.sha256Sha256(buf)
41
42 return hashBuf
43 }
44
45 static async asyncMagicHash (messageBuf) {
46 const args = [messageBuf]
47 const workersResult = await Workers.asyncClassMethod(Bsm, 'magicHash', args)
48 return workersResult.resbuf
49 }
50
51 static sign (messageBuf, keyPair) {
52 const m = new Bsm(messageBuf, keyPair)
53 m.sign()
54 const sigbuf = m.sig.toCompact()
55 const sigstr = sigbuf.toString('base64')
56 return sigstr
57 }
58
59 static async asyncSign (messageBuf, keyPair) {
60 const args = [messageBuf, keyPair]
61 const workersResult = await Workers.asyncClassMethod(Bsm, 'sign', args)
62 const sigstr = JSON.parse(workersResult.resbuf.toString())
63 return sigstr
64 }
65
66 static verify (messageBuf, sigstr, address) {
67 const sigbuf = Buffer.from(sigstr, 'base64')
68 const message = new Bsm()
69 message.messageBuf = messageBuf
70 message.sig = new Sig().fromCompact(sigbuf)
71 message.address = address
72
73 return message.verify().verified
74 }
75
76 static async asyncVerify (messageBuf, sigstr, address) {
77 const args = [messageBuf, sigstr, address]
78 const workersResult = await Workers.asyncClassMethod(Bsm, 'verify', args)
79 const res = JSON.parse(workersResult.resbuf.toString())
80 return res
81 }
82
83 sign () {
84 const hashBuf = Bsm.magicHash(this.messageBuf)
85 const ecdsa = new Ecdsa().fromObject({
86 hashBuf: hashBuf,
87 keyPair: this.keyPair
88 })
89 ecdsa.sign()
90 ecdsa.calcrecovery()
91 this.sig = ecdsa.sig
92 return this
93 }
94
95 verify () {
96 const hashBuf = Bsm.magicHash(this.messageBuf)
97
98 const ecdsa = new Ecdsa()
99 ecdsa.hashBuf = hashBuf
100 ecdsa.sig = this.sig
101 ecdsa.keyPair = new KeyPair()
102 ecdsa.keyPair.pubKey = ecdsa.sig2PubKey()
103
104 if (!ecdsa.verify()) {
105 this.verified = false
106 return this
107 }
108
109 const address = new Address().fromPubKey(
110 ecdsa.keyPair.pubKey,
111 undefined,
112 this.sig.compressed
113 )
114 // TODO: what if livenet/testnet mismatch?
115 if (cmp(address.hashBuf, this.address.hashBuf)) {
116 this.verified = true
117 } else {
118 this.verified = false
119 }
120
121 return this
122 }
123}
124
125Bsm.magicBytes = Buffer.from('Bitcoin Signed Message:\n')
126
127export { Bsm }