UNPKG

3.46 kBJavaScriptView Raw
1'use strict';
2
3var bitcore = require('bitcore');
4var PrivateKey = bitcore.PrivateKey;
5var PublicKey = bitcore.PublicKey;
6var Address = bitcore.Address;
7var BufferWriter = bitcore.encoding.BufferWriter;
8var ECDSA = bitcore.crypto.ECDSA;
9var Signature = bitcore.crypto.Signature;
10var sha256sha256 = bitcore.crypto.Hash.sha256sha256;
11
12/**
13 * Will construct a new message to sign and verify.
14 *
15 * @param {String} message
16 * @returns {Message}
17 */
18var Message = function Message(message) {
19 if (!(this instanceof Message)) {
20 return new Message(message);
21 }
22 if (typeof message !== 'string') {
23 throw new TypeError('First argument should be a string');
24 }
25 this.message = message;
26
27 return this;
28};
29
30Message.MAGIC_BYTES = new Buffer('Bitcoin Signed Message:\n');
31
32Message.prototype.magicHash = function magicHash() {
33 var prefix1 = BufferWriter.varintBufNum(Message.MAGIC_BYTES.length);
34 var messageBuffer = new Buffer(this.message);
35 var prefix2 = BufferWriter.varintBufNum(messageBuffer.length);
36 var buf = Buffer.concat([prefix1, Message.MAGIC_BYTES, prefix2, messageBuffer]);
37 var hash = sha256sha256(buf);
38 return hash;
39};
40
41Message.prototype._sign = function _sign(privateKey) {
42 if (!(privateKey instanceof PrivateKey)) {
43 throw new TypeError('First argument should be an instance of PrivateKey');
44 }
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 if (!(publicKey instanceof PublicKey)) {
68 throw new TypeError('First argument should be an instance of PublicKey');
69 }
70 if (!(signature instanceof Signature)) {
71 throw new TypeError('Second argument should be an instance of Signature');
72 }
73 var hash = this.magicHash();
74 var verified = ECDSA.verify(hash, signature, publicKey);
75 if (!verified) {
76 this.error = 'The signature was invalid';
77 }
78 return verified;
79};
80
81/**
82 * Will return a boolean of the signature is valid for a given bitcoin address.
83 * If it isn't the specific reason is accessible via the "error" member.
84 *
85 * @param {String} bitcoinAddress - A bitcoin address
86 * @param {String} signatureString - A base64 encoded compact signature
87 * @returns {Boolean}
88 */
89Message.prototype.verify = function verify(bitcoinAddress, signatureString) {
90 var signature = Signature.fromCompact(new Buffer(signatureString, 'base64'));
91
92 // recover the public key
93 var ecdsa = new ECDSA();
94 ecdsa.hashbuf = this.magicHash();
95 ecdsa.sig = signature;
96 var publicKey = ecdsa.toPublicKey();
97
98 var expectedAddress = Address.fromString(bitcoinAddress);
99 var signatureAddress = Address.fromPublicKey(publicKey, expectedAddress.network);
100
101 // check that the recovered address and specified address match
102 if (expectedAddress.toString() !== signatureAddress.toString()) {
103 this.error = 'The signature did not match the message digest';
104 return false;
105 }
106
107 return this._verify(publicKey, signature);
108};
109
110module.exports = Message;
111