Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added message sign and verify functionality #32

Merged
merged 8 commits into from
Mar 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,47 @@ var Zaddress = zencashjs.zaddress.mkZAddress(a_pk, pk_enc)
// zcTPZR8Hqz2ZcStwMJju9L4VBHW7YWmNyL6tDAT4eVmzmxLaG7h4QmqUXfmrjz8twizH4piDGiRYJRZ1bhHhT5gFL6TKsQZ
```

# Example usage (Sign/Verify message)
```javascript
var zencashjs = require('zencashjs')

var priv = zencashjs.address.mkPrivKey('chris p. bacon, defender of the guardians')
// 2c3a48576fe6e8a466e78cd2957c9dc62128135540bbea0685d7c4a23ea35a6c

var privWIF = zencashjs.address.privKeyToWIF(priv)
// 5J9mKPd531Tk4A73kKp4iowoi6EvhEp8QSMAVzrZhuzZkdpYbK8

var pubKey = zencashjs.address.privKeyToPubKey(priv, true) // generate compressed pubKey
// 038a789e0910b6aa314f63d2cc666bd44fa4b71d7397cb5466902dc594c1a0a0d2

var zAddr = zencashjs.address.pubKeyToAddr(pubKey)
// znnjppzJG7ajT7f6Vp1AD6SjgcXBVPA2E6c

var message = 'ciao'

/**
* Will sign a message with a given zen private key.
*
* @param {string} message - The message to be signed
* @param {string} privateKey - A private key
* @param {Boolean} compressed
* @returns {Buffer} The signature
*/
var signature = zencashjs.message.sign(message, priv, true)
// signature.toString('base64')
// IC7SPUlfyjKjG7hrrPWc6WTsm79ksjHAYXSFmjlGwAL9SSvAIvpYmOm3U5CzG2k0QBxDYuzpltw+UAET3J8e7Gg=

/**
* Validate a signature against a given zend address.
*
* @param {String} message - the message to verify
* @param {String} zenAddress - A zen address
* @param {String|Buffer} signature - A base64 encoded compact signature
* @returns {Boolean} true if the signature is valid
*/
var verification = zencashjs.message.verify(message, zAddr, signature)
// true
```

# Development guide (more to come..)
```bash
Expand Down
20 changes: 18 additions & 2 deletions lib/crypto.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
"use strict";

var _module$exports;

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

/*
* Obtained from https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/src/crypto.js
* 2017/07/25: No ripemd160 in SJCL, so resorted to this
Expand Down Expand Up @@ -28,10 +32,22 @@ function hash160(buffer) {
return hash160;
}

module.exports = {
function sha256Buf(buffer) {
return createHash('sha256').update(buffer).digest();
}

function hash256Buf(buffer) {
return sha256Buf(sha256Buf(buffer));
}

function hash160Buf(buffer) {
return createHash('ripemd160').update(sha256Buf(buffer)).digest();
}

module.exports = (_module$exports = {
hash160: hash160,
ripemd160: ripemd160,
sha1: sha1,
sha256: sha256,
sha256x2: sha256x2
};
}, _defineProperty(_module$exports, "sha256x2", sha256x2), _defineProperty(_module$exports, "hash256Buf", hash256Buf), _defineProperty(_module$exports, "hash160Buf", hash160Buf), _module$exports);
3 changes: 2 additions & 1 deletion lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ module.exports = {
zaddress: require('./zaddress'),
crypto: require('./crypto'),
transaction: require('./transaction'),
bufferutils: require('./bufferutils')
bufferutils: require('./bufferutils'),
message: require('./message')
};
88 changes: 88 additions & 0 deletions lib/message.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
"use strict";

var bs58check = require('bs58check');

var secp256k1 = require('secp256k1');

var zcrypto = require('./crypto');

var varuint = require('varuint-bitcoin');
/**
* Will sign a message with a given zen private key.
*
* @param {string} message - The message to be signed
* @param {string} privateKey - A private key
* @param {Boolean} compressed
* @returns {Buffer} The signature
*/


function sign(message, privateKey, compressed, sigOptions) {
var _ref = sigOptions || {},
extraEntropy = _ref.extraEntropy;

var hash = _magicHash(message, false);

var sigObj = secp256k1.sign(hash, Buffer.from(privateKey, 'hex'), {
data: extraEntropy
});
return encodeSignature(sigObj.signature, sigObj.recovery, compressed);
}
/**
* Validate a signature against a given zend address.
*
* @param {String} message - the message to verify
* @param {String} zenAddress - A zen address
* @param {String|Buffer} signature - A base64 encoded compact signature
* @returns {Boolean} true if the signature is valid
*/


function verify(message, zenAddress, signature) {
if (!Buffer.isBuffer(signature)) signature = Buffer.from(signature, 'base64');
var parsed = decodeSignature(signature);

var hash = _magicHash(message, false);

var publicKey = secp256k1.recover(hash, parsed.signature, parsed.recovery, parsed.compressed);
var publicKeyHash = zcrypto.hash160Buf(publicKey);
var actual, expected;
actual = publicKeyHash; // prefix is 2 bytes in zencash instead of 1

expected = bs58check.decode(zenAddress).slice(2);
return expected.equals(actual);
}

function encodeSignature(signature, recovery, compressed) {
if (compressed) recovery += 4;
return Buffer.concat([Buffer.alloc(1, recovery + 27), signature]);
}

function decodeSignature(buffer) {
if (buffer.length !== 65) throw new Error('Invalid signature length');
var flagByte = buffer.readUInt8(0) - 27;

if (flagByte > 15 || flagByte < 0) {
throw new Error('Invalid signature parameter');
}

return {
compressed: !!(flagByte & 12),
recovery: flagByte & 3,
signature: buffer.slice(1)
};
}

function _magicHash(message) {
var MAGIC_BYTES = Buffer.from('Zcash Signed Message:\n', 'utf8');
var prefix1 = varuint.encode(MAGIC_BYTES.length);
var messageBuffer = Buffer.from(message, 'utf8');
var prefix2 = varuint.encode(messageBuffer.length);
var buf = Buffer.concat([prefix1, MAGIC_BYTES, prefix2, messageBuffer]);
return zcrypto.hash256Buf(buf);
}

module.exports = {
sign: sign,
verify: verify
};
Loading