2013-03-02 18:28:13 +01:00
|
|
|
/// Implements Bitcoin's feature for signing arbitrary messages.
|
|
|
|
|
2014-03-08 06:02:40 +01:00
|
|
|
var SHA256 = require('crypto-js/sha256');
|
2013-03-02 18:28:13 +01:00
|
|
|
var ecdsa = require('./ecdsa');
|
2014-03-11 02:52:48 +01:00
|
|
|
var convert = require('./convert');
|
2013-03-02 18:28:13 +01:00
|
|
|
|
2013-02-17 06:39:15 +01:00
|
|
|
var Message = {};
|
2012-08-16 00:25:06 +02:00
|
|
|
|
2013-02-17 06:39:15 +01:00
|
|
|
Message.magicPrefix = "Bitcoin Signed Message:\n";
|
2012-08-16 00:25:06 +02:00
|
|
|
|
2013-02-17 06:39:15 +01:00
|
|
|
Message.makeMagicMessage = function (message) {
|
2014-03-11 02:52:48 +01:00
|
|
|
var magicBytes = convert.stringToBytes(Message.magicPrefix);
|
|
|
|
var messageBytes = convert.stringToBytes(message);
|
2012-08-16 00:25:06 +02:00
|
|
|
|
2013-02-17 06:39:15 +01:00
|
|
|
var buffer = [];
|
2014-03-11 02:52:48 +01:00
|
|
|
buffer = buffer.concat(convert.numToVarInt(magicBytes.length));
|
2013-02-17 06:39:15 +01:00
|
|
|
buffer = buffer.concat(magicBytes);
|
2014-03-11 02:52:48 +01:00
|
|
|
buffer = buffer.concat(convert.numToVarInt(messageBytes.length));
|
2013-02-17 06:39:15 +01:00
|
|
|
buffer = buffer.concat(messageBytes);
|
2012-08-16 00:25:06 +02:00
|
|
|
|
2013-02-17 06:39:15 +01:00
|
|
|
return buffer;
|
|
|
|
};
|
2012-08-16 00:25:06 +02:00
|
|
|
|
2013-02-17 06:39:15 +01:00
|
|
|
Message.getHash = function (message) {
|
|
|
|
var buffer = Message.makeMagicMessage(message);
|
2014-03-11 02:52:48 +01:00
|
|
|
return convert.wordArrayToBytes(SHA256(SHA256(convert.bytesToWordArray(buffer))));
|
2013-02-17 06:39:15 +01:00
|
|
|
};
|
2012-08-16 00:25:06 +02:00
|
|
|
|
2014-03-11 18:26:40 +01:00
|
|
|
Message.signMessage = function (key, message) {
|
2013-02-17 06:39:15 +01:00
|
|
|
var hash = Message.getHash(message);
|
2012-08-16 00:25:06 +02:00
|
|
|
|
2013-02-17 06:39:15 +01:00
|
|
|
var sig = key.sign(hash);
|
2012-08-16 00:25:06 +02:00
|
|
|
|
2013-03-02 18:28:13 +01:00
|
|
|
var obj = ecdsa.parseSig(sig);
|
2012-08-16 00:25:06 +02:00
|
|
|
|
2013-11-28 20:01:55 +01:00
|
|
|
var i = ecdsa.calcPubkeyRecoveryParam(key, obj.r, obj.s, hash);
|
2012-08-16 00:25:06 +02:00
|
|
|
|
2013-02-17 06:39:15 +01:00
|
|
|
i += 27;
|
2014-03-11 18:26:40 +01:00
|
|
|
if (key.compressed) i += 4;
|
2012-08-16 00:25:06 +02:00
|
|
|
|
2013-02-17 06:39:15 +01:00
|
|
|
var rBa = obj.r.toByteArrayUnsigned();
|
|
|
|
var sBa = obj.s.toByteArrayUnsigned();
|
2012-08-16 00:25:06 +02:00
|
|
|
|
2013-02-17 06:39:15 +01:00
|
|
|
// Pad to 32 bytes per value
|
|
|
|
while (rBa.length < 32) rBa.unshift(0);
|
|
|
|
while (sBa.length < 32) sBa.unshift(0);
|
2012-08-16 00:25:06 +02:00
|
|
|
|
2013-02-17 06:39:15 +01:00
|
|
|
sig = [i].concat(rBa).concat(sBa);
|
2012-08-16 00:25:06 +02:00
|
|
|
|
2014-03-11 02:52:48 +01:00
|
|
|
return convert.bytesToHex(sig);
|
2013-02-17 06:39:15 +01:00
|
|
|
};
|
2012-08-16 00:25:06 +02:00
|
|
|
|
2013-02-17 06:39:15 +01:00
|
|
|
Message.verifyMessage = function (address, sig, message) {
|
2014-03-11 02:52:48 +01:00
|
|
|
sig = convert.hexToBytes(sig);
|
2013-03-02 18:28:13 +01:00
|
|
|
sig = ecdsa.parseSigCompact(sig);
|
2012-08-16 00:25:06 +02:00
|
|
|
|
2013-02-17 06:39:15 +01:00
|
|
|
var hash = Message.getHash(message);
|
2012-08-16 00:25:06 +02:00
|
|
|
|
2013-02-17 06:39:15 +01:00
|
|
|
var isCompressed = !!(sig.i & 4);
|
2013-03-02 18:28:13 +01:00
|
|
|
var pubKey = ecdsa.recoverPubKey(sig.r, sig.s, hash, sig.i);
|
2014-03-11 18:28:04 +01:00
|
|
|
pubKey.compressed = isCompressed;
|
2012-08-16 00:25:06 +02:00
|
|
|
|
2014-03-22 08:17:54 +01:00
|
|
|
var expectedAddress = pubKey.getAddress().toString();
|
2012-08-16 00:25:06 +02:00
|
|
|
|
2013-02-17 06:39:15 +01:00
|
|
|
return (address === expectedAddress);
|
|
|
|
};
|
2012-08-16 00:25:06 +02:00
|
|
|
|
2013-02-17 06:39:15 +01:00
|
|
|
module.exports = Message;
|