1 | 'use strict';
|
2 |
|
3 | var _ = require('lodash');
|
4 | var PrivateKey = require('./privatekey');
|
5 | var PublicKey = require('./publickey');
|
6 | var Address = require('./address');
|
7 | var BufferWriter = require('./encoding/bufferwriter');
|
8 | var ECDSA = require('./crypto/ecdsa');
|
9 | var Signature = require('./crypto/signature');
|
10 | var sha256sha256 = require('./crypto/hash').sha256sha256;
|
11 | var JSUtil = require('./util/js');
|
12 | var $ = require('./util/preconditions');
|
13 |
|
14 | function Message(message) {
|
15 | if (!(this instanceof Message)) {
|
16 | return new Message(message);
|
17 | }
|
18 | $.checkArgument(_.isString(message), 'First argument should be a string');
|
19 | this.message = message;
|
20 |
|
21 | return this;
|
22 | }
|
23 |
|
24 | Message.MAGIC_BYTES = Buffer.from('Bitcoin Signed Message:\n');
|
25 |
|
26 | Message.prototype.magicHash = function magicHash() {
|
27 | var prefix1 = BufferWriter.varintBufNum(Message.MAGIC_BYTES.length);
|
28 | var messageBuffer = Buffer.from(this.message);
|
29 | var prefix2 = BufferWriter.varintBufNum(messageBuffer.length);
|
30 | var buf = Buffer.concat([prefix1, Message.MAGIC_BYTES, prefix2, messageBuffer]);
|
31 | var hash = sha256sha256(buf);
|
32 | return hash;
|
33 | };
|
34 |
|
35 | Message.prototype._sign = function _sign(privateKey) {
|
36 | $.checkArgument(privateKey instanceof PrivateKey, 'First argument should be an instance of PrivateKey');
|
37 | var hash = this.magicHash();
|
38 | var ecdsa = new ECDSA();
|
39 | ecdsa.hashbuf = hash;
|
40 | ecdsa.privkey = privateKey;
|
41 | ecdsa.pubkey = privateKey.toPublicKey();
|
42 | ecdsa.signRandomK();
|
43 | ecdsa.calci();
|
44 | return ecdsa.sig;
|
45 | };
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 | Message.prototype.sign = function sign(privateKey) {
|
54 | var signature = this._sign(privateKey);
|
55 | return signature.toCompact().toString('base64');
|
56 | };
|
57 |
|
58 | Message.prototype._verify = function _verify(publicKey, signature) {
|
59 | $.checkArgument(publicKey instanceof PublicKey, 'First argument should be an instance of PublicKey');
|
60 | $.checkArgument(signature instanceof Signature, 'Second argument should be an instance of Signature');
|
61 | var hash = this.magicHash();
|
62 | var verified = ECDSA.verify(hash, signature, publicKey);
|
63 | if (!verified) {
|
64 | this.error = 'The signature was invalid';
|
65 | }
|
66 | return verified;
|
67 | };
|
68 |
|
69 |
|
70 |
|
71 |
|
72 |
|
73 |
|
74 |
|
75 |
|
76 |
|
77 | Message.prototype.verify = function verify(bitcoinAddress, signatureString) {
|
78 | $.checkArgument(bitcoinAddress);
|
79 | $.checkArgument(signatureString && _.isString(signatureString));
|
80 |
|
81 | if (_.isString(bitcoinAddress)) {
|
82 | bitcoinAddress = Address.fromString(bitcoinAddress);
|
83 | }
|
84 | var signature = Signature.fromCompact(Buffer.from(signatureString, 'base64'));
|
85 |
|
86 |
|
87 | var ecdsa = new ECDSA();
|
88 | ecdsa.hashbuf = this.magicHash();
|
89 | ecdsa.sig = signature;
|
90 | var publicKey = ecdsa.toPublicKey();
|
91 |
|
92 | var signatureAddress = Address.fromPublicKey(publicKey, bitcoinAddress.network);
|
93 |
|
94 |
|
95 | if (bitcoinAddress.toString() !== signatureAddress.toString()) {
|
96 | this.error = 'The signature did not match the message digest';
|
97 | return false;
|
98 | }
|
99 |
|
100 | return this._verify(publicKey, signature);
|
101 | };
|
102 |
|
103 |
|
104 |
|
105 |
|
106 |
|
107 |
|
108 |
|
109 |
|
110 |
|
111 | Message.prototype.recoverPublicKey = function recoverPublicKey(bitcoinAddress, signatureString) {
|
112 | $.checkArgument(bitcoinAddress);
|
113 | $.checkArgument(signatureString && _.isString(signatureString));
|
114 |
|
115 | if (_.isString(bitcoinAddress)) {
|
116 | bitcoinAddress = Address.fromString(bitcoinAddress);
|
117 | }
|
118 | var signature = Signature.fromCompact(Buffer.from(signatureString, 'base64'));
|
119 |
|
120 |
|
121 | var ecdsa = new ECDSA();
|
122 | ecdsa.hashbuf = this.magicHash();
|
123 | ecdsa.sig = signature;
|
124 | var publicKey = ecdsa.toPublicKey();
|
125 |
|
126 | var signatureAddress = Address.fromPublicKey(publicKey, bitcoinAddress.network);
|
127 |
|
128 |
|
129 | if (bitcoinAddress.toString() !== signatureAddress.toString()) {
|
130 | this.error = 'The signature did not match the message digest';
|
131 | }
|
132 |
|
133 | return publicKey.toString();
|
134 | };
|
135 |
|
136 |
|
137 |
|
138 |
|
139 |
|
140 |
|
141 |
|
142 | Message.fromString = function(str) {
|
143 | return new Message(str);
|
144 | };
|
145 |
|
146 |
|
147 |
|
148 |
|
149 |
|
150 |
|
151 |
|
152 | Message.fromJSON = function fromJSON(json) {
|
153 | if (JSUtil.isValidJSON(json)) {
|
154 | json = JSON.parse(json);
|
155 | }
|
156 | return new Message(json.message);
|
157 | };
|
158 |
|
159 |
|
160 |
|
161 |
|
162 | Message.prototype.toObject = function toObject() {
|
163 | return {
|
164 | message: this.message
|
165 | };
|
166 | };
|
167 |
|
168 |
|
169 |
|
170 |
|
171 | Message.prototype.toJSON = function toJSON() {
|
172 | return JSON.stringify(this.toObject());
|
173 | };
|
174 |
|
175 |
|
176 |
|
177 |
|
178 |
|
179 |
|
180 | Message.prototype.toString = function() {
|
181 | return this.message;
|
182 | };
|
183 |
|
184 |
|
185 |
|
186 |
|
187 |
|
188 |
|
189 | Message.prototype.inspect = function() {
|
190 | return '<Message: ' + this.toString() + '>';
|
191 | };
|
192 |
|
193 | module.exports = Message;
|
194 |
|
195 | var Script = require('./script');
|