diff --git a/bitcore.js b/bitcore.js index 46d28548e..4e2b37052 100644 --- a/bitcore.js +++ b/bitcore.js @@ -48,6 +48,7 @@ requireWhenAccessed('RpcClient', './lib/RpcClient'); requireWhenAccessed('Wallet', './lib/Wallet'); requireWhenAccessed('WalletKey', './lib/WalletKey'); requireWhenAccessed('PeerManager', './lib/PeerManager'); +requireWhenAccessed('Message', './lib/Message'); module.exports.Buffer = Buffer; if (typeof process.versions === 'undefined') { diff --git a/lib/Message.js b/lib/Message.js new file mode 100644 index 000000000..018090c6c --- /dev/null +++ b/lib/Message.js @@ -0,0 +1,42 @@ +'use strict'; +var imports = require('soop').imports(); +var coinUtil = imports.coinUtil || require('../util'); +var Key = imports.Key || require('./Key'); + +var Message = function() { +}; + +Message.sign = function(str, key) { + var hash = Message.magicHash(str); + var sig = key.signSync(hash); + return sig; +}; + +Message.verifyWithPubKey = function(pubkey, message, sig) { + var hash = Message.magicHash(message); + var key = new Key(); + if (pubkey.length == 65) + key.compressed = false; + key.public = pubkey; + + return key.verifySignatureSync(hash, sig); +}; + +//TODO: Message.verify ... with address, not pubkey + +Message.magicBytes = new Buffer('Bitcoin Signed Message:\n'); + +Message.magicHash = function(str) { + var magicBytes = Message.magicBytes; + var prefix1 = coinUtil.varIntBuf(magicBytes.length); + var message = new Buffer(str); + var prefix2 = coinUtil.varIntBuf(message.length); + + var buf = Buffer.concat([prefix1, magicBytes, prefix2, message]); + + var hash = coinUtil.twoSha256(buf); + + return hash; +}; + +module.exports = require('soop')(Message); diff --git a/test/test.Message.js b/test/test.Message.js new file mode 100644 index 000000000..9ec581f13 --- /dev/null +++ b/test/test.Message.js @@ -0,0 +1,57 @@ +'use strict'; + +var chai = chai || require('chai'); +var should = chai.should(); +var bitcore = bitcore || require('../bitcore'); +var Message = bitcore.Message; +var coinUtil = bitcore.util; + +describe('Message', function() { + describe('sign', function() { + it('should return a signature', function() { + var key = bitcore.Key.generateSync(); + var sig = Message.sign('my message', key); + sig.length.should.be.greaterThan(0); + }); + }); + + describe('verifyWithPubKey', function() { + it('should verify a signed message', function() { + var message = 'my message'; + var key = bitcore.Key.generateSync(); + var sig = Message.sign(message, key); + Message.verifyWithPubKey(key.public, message, sig).should.equal(true); + }); + }); + + describe('magicBytes', function() { + it('should be "Bitcoin Signed Message:\\n"', function() { + Message.magicBytes.toString().should.equal('Bitcoin Signed Message:\n'); + }); + }); + + describe('magicHash', function() { + it('should hash the message with the magic bytes', function() { + var str = 'my message'; + var magicBytes = Message.magicBytes; + var prefix1 = coinUtil.varIntBuf(magicBytes.length); + var message = new Buffer(str); + var prefix2 = coinUtil.varIntBuf(message.length); + + var buf = Buffer.concat([prefix1, magicBytes, prefix2, message]); + + var hash = coinUtil.twoSha256(buf); + + var hash2 = Message.magicHash(str); + + hash.toString().should.equal(hash2.toString()); + }); + + it('should hash this message the same way as bitcoinjs-lib', function() { + var hashHex = '74eacdc6c04724869380907bf4aab561a1494a4a800fba266b29b8158c2c4cec'; + + var str = 'this is a test message'; + hashHex.should.equal(Message.magicHash(str).toString('hex')); + }); + }); +});