UNPKG

bitcore-lib

Version:

A pure and powerful JavaScript Bitcoin library.

188 lines (159 loc) 5.84 kB
'use strict'; var _ = require('lodash'); var PrivateKey = require('./privatekey'); var PublicKey = require('./publickey'); var Address = require('./address'); var BufferWriter = require('./encoding/bufferwriter'); var ECDSA = require('./crypto/ecdsa'); var Signature = require('./crypto/signature'); var sha256sha256 = require('./crypto/hash').sha256sha256; var JSUtil = require('./util/js'); var $ = require('./util/preconditions'); function Message(message) { if (!(this instanceof Message)) { return new Message(message); } $.checkArgument(_.isString(message), 'First argument should be a string'); this.message = message; return this; } Message.MAGIC_BYTES = Buffer.from('Bitcoin Signed Message:\n'); Message.prototype.magicHash = function magicHash() { var prefix1 = BufferWriter.varintBufNum(Message.MAGIC_BYTES.length); var messageBuffer = Buffer.from(this.message); var prefix2 = BufferWriter.varintBufNum(messageBuffer.length); var buf = Buffer.concat([prefix1, Message.MAGIC_BYTES, prefix2, messageBuffer]); var hash = sha256sha256(buf); return hash; }; Message.prototype._sign = function _sign(privateKey) { $.checkArgument(privateKey instanceof PrivateKey, 'First argument should be an instance of PrivateKey'); const hash = this.magicHash(); const sig = ECDSA.sign(hash, privateKey, { randomK: true }); ECDSA.calci(hash, sig, privateKey.toPublicKey()); return sig; }; /** * Will sign a message with a given bitcoin private key. * * @param {PrivateKey} privateKey - An instance of PrivateKey * @returns {String} A base64 encoded compact signature */ Message.prototype.sign = function sign(privateKey) { var signature = this._sign(privateKey); return signature.toCompact().toString('base64'); }; Message.prototype._verify = function _verify(publicKey, signature) { $.checkArgument(publicKey instanceof PublicKey, 'First argument should be an instance of PublicKey'); $.checkArgument(signature instanceof Signature, 'Second argument should be an instance of Signature'); var hash = this.magicHash(); var verified = ECDSA.verify(hash, signature, publicKey); if (!verified) { this.error = 'The signature was invalid'; } return verified; }; /** * Will return a boolean of the signature is valid for a given bitcoin address. * If it isn't the specific reason is accessible via the "error" member. * * @param {Address|String} bitcoinAddress - A bitcoin address * @param {String} signatureString - A base64 encoded compact signature * @returns {Boolean} */ Message.prototype.verify = function verify(bitcoinAddress, signatureString) { $.checkArgument(bitcoinAddress); $.checkArgument(signatureString && _.isString(signatureString)); if (_.isString(bitcoinAddress)) { bitcoinAddress = Address.fromString(bitcoinAddress); } var signature = Signature.fromCompact(Buffer.from(signatureString, 'base64')); // recover the public key const hashbuf = this.magicHash(); var publicKey = ECDSA.recoverPublicKey(hashbuf, signature); var signatureAddress = Address.fromPublicKey(publicKey, bitcoinAddress.network); // check that the recovered address and specified address match if (bitcoinAddress.toString() !== signatureAddress.toString()) { this.error = 'The signature did not match the message digest'; return false; } return this._verify(publicKey, signature); }; /** * Will return a public key string if the provided signature and the message digest is correct * If it isn't the specific reason is accessible via the "error" member. * * @param {Address|String} bitcoinAddress - A bitcoin address * @param {String} signatureString - A base64 encoded compact signature * @returns {String} */ Message.prototype.recoverPublicKey = function recoverPublicKey(bitcoinAddress, signatureString) { $.checkArgument(bitcoinAddress); $.checkArgument(signatureString && _.isString(signatureString)); if (_.isString(bitcoinAddress)) { bitcoinAddress = Address.fromString(bitcoinAddress); } var signature = Signature.fromCompact(Buffer.from(signatureString, 'base64')); // recover the public key const hashbuf = this.magicHash(); var publicKey = ECDSA.recoverPublicKey(hashbuf, signature); var signatureAddress = Address.fromPublicKey(publicKey, bitcoinAddress.network); // check that the recovered address and specified address match if (bitcoinAddress.toString() !== signatureAddress.toString()) { this.error = 'The signature did not match the message digest'; } return publicKey.toString(); }; /** * Instantiate a message from a message string * * @param {String} str - A string of the message * @returns {Message} A new instance of a Message */ Message.fromString = function(str) { return new Message(str); }; /** * Instantiate a message from JSON * * @param {String} json - An JSON string or Object with keys: message * @returns {Message} A new instance of a Message */ Message.fromJSON = function fromJSON(json) { if (JSUtil.isValidJSON(json)) { json = JSON.parse(json); } return new Message(json.message); }; /** * @returns {Object} A plain object with the message information */ Message.prototype.toObject = function toObject() { return { message: this.message }; }; /** * @returns {String} A JSON representation of the message information */ Message.prototype.toJSON = function toJSON() { return JSON.stringify(this.toObject()); }; /** * Will return a the string representation of the message * * @returns {String} Message */ Message.prototype.toString = function() { return this.message; }; /** * Will return a string formatted for the console * * @returns {String} Message */ Message.prototype.inspect = function() { return '<Message: ' + this.toString() + '>'; }; module.exports = Message; var Script = require('./script');