UNPKG

4.83 kBJavaScriptView Raw
1'use strict';
2
3var bitcore = require('bitcore');
4var _ = bitcore.deps._;
5var PrivateKey = bitcore.PrivateKey;
6var PublicKey = bitcore.PublicKey;
7var Address = bitcore.Address;
8var BufferWriter = bitcore.encoding.BufferWriter;
9var ECDSA = bitcore.crypto.ECDSA;
10var Signature = bitcore.crypto.Signature;
11var sha256sha256 = bitcore.crypto.Hash.sha256sha256;
12var JSUtil = bitcore.util.js;
13var $ = bitcore.util.preconditions;
14
15/**
16 * constructs a new message to sign and verify.
17 *
18 * @param {String} message
19 * @returns {Message}
20 */
21var Message = function Message(message) {
22 if (!(this instanceof Message)) {
23 return new Message(message);
24 }
25 $.checkArgument(_.isString(message), 'First argument should be a string');
26 this.message = message;
27
28 return this;
29};
30
31Message.MAGIC_BYTES = new Buffer('Bitcoin Signed Message:\n');
32
33Message.prototype.magicHash = function magicHash() {
34 var prefix1 = BufferWriter.varintBufNum(Message.MAGIC_BYTES.length);
35 var messageBuffer = new Buffer(this.message);
36 var prefix2 = BufferWriter.varintBufNum(messageBuffer.length);
37 var buf = Buffer.concat([prefix1, Message.MAGIC_BYTES, prefix2, messageBuffer]);
38 var hash = sha256sha256(buf);
39 return hash;
40};
41
42Message.prototype._sign = function _sign(privateKey) {
43 $.checkArgument(privateKey instanceof PrivateKey,
44 'First argument should be an instance of PrivateKey');
45 var hash = this.magicHash();
46 var ecdsa = new ECDSA();
47 ecdsa.hashbuf = hash;
48 ecdsa.privkey = privateKey;
49 ecdsa.pubkey = privateKey.toPublicKey();
50 ecdsa.signRandomK();
51 ecdsa.calci();
52 return ecdsa.sig;
53};
54
55/**
56 * Will sign a message with a given bitcoin private key.
57 *
58 * @param {PrivateKey} privateKey - An instance of PrivateKey
59 * @returns {String} A base64 encoded compact signature
60 */
61Message.prototype.sign = function sign(privateKey) {
62 var signature = this._sign(privateKey);
63 return signature.toCompact().toString('base64');
64};
65
66Message.prototype._verify = function _verify(publicKey, signature) {
67 $.checkArgument(publicKey instanceof PublicKey, 'First argument should be an instance of PublicKey');
68 $.checkArgument(signature instanceof Signature, 'Second argument should be an instance of Signature');
69 var hash = this.magicHash();
70 var verified = ECDSA.verify(hash, signature, publicKey);
71 if (!verified) {
72 this.error = 'The signature was invalid';
73 }
74 return verified;
75};
76
77/**
78 * Will return a boolean of the signature is valid for a given bitcoin address.
79 * If it isn't the specific reason is accessible via the "error" member.
80 *
81 * @param {Address|String} bitcoinAddress - A bitcoin address
82 * @param {String} signatureString - A base64 encoded compact signature
83 * @returns {Boolean}
84 */
85Message.prototype.verify = function verify(bitcoinAddress, signatureString) {
86 $.checkArgument(bitcoinAddress);
87 $.checkArgument(signatureString && _.isString(signatureString));
88
89 if (_.isString(bitcoinAddress)) {
90 bitcoinAddress = Address.fromString(bitcoinAddress);
91 }
92 var signature = Signature.fromCompact(new Buffer(signatureString, 'base64'));
93
94 // recover the public key
95 var ecdsa = new ECDSA();
96 ecdsa.hashbuf = this.magicHash();
97 ecdsa.sig = signature;
98 var publicKey = ecdsa.toPublicKey();
99
100 var signatureAddress = Address.fromPublicKey(publicKey, bitcoinAddress.network);
101
102 // check that the recovered address and specified address match
103 if (bitcoinAddress.toString() !== signatureAddress.toString()) {
104 this.error = 'The signature did not match the message digest';
105 return false;
106 }
107
108 return this._verify(publicKey, signature);
109};
110
111/**
112 * Instantiate a message from a message string
113 *
114 * @param {String} str - A string of the message
115 * @returns {Message} A new instance of a Message
116 */
117Message.fromString = function(str) {
118 return new Message(str);
119};
120
121/**
122 * Instantiate a message from JSON
123 *
124 * @param {String} json - An JSON string or Object with keys: message
125 * @returns {Message} A new instance of a Message
126 */
127Message.fromJSON = function fromJSON(json) {
128 if (JSUtil.isValidJSON(json)) {
129 json = JSON.parse(json);
130 }
131 return new Message(json.message);
132};
133
134/**
135 * @returns {Object} A plain object with the message information
136 */
137Message.prototype.toObject = function toObject() {
138 return {
139 message: this.message
140 };
141};
142
143/**
144 * @returns {String} A JSON representation of the message information
145 */
146Message.prototype.toJSON = function toJSON() {
147 return JSON.stringify(this.toObject());
148};
149
150/**
151 * Will return a the string representation of the message
152 *
153 * @returns {String} Message
154 */
155Message.prototype.toString = function() {
156 return this.message;
157};
158
159/**
160 * Will return a string formatted for the console
161 *
162 * @returns {String} Message
163 */
164Message.prototype.inspect = function() {
165 return '<Message: ' + this.toString() + '>';
166};
167
168module.exports = Message;