Changeset View
Changeset View
Standalone View
Standalone View
web/xecjs-message/index.js
const bufferEquals = require('buffer-equals'); | const bufferEquals = require('buffer-equals'); | ||||
const createHash = require('create-hash'); | const createHash = require('create-hash'); | ||||
const secp256k1 = require('secp256k1'); | const secp256k1 = require('secp256k1'); | ||||
const varuint = require('varuint-bitcoin'); | const varuint = require('varuint-bitcoin'); | ||||
const ecashaddr = require('ecashaddrjs'); | const ecashaddr = require('ecashaddrjs'); | ||||
const SEGWIT_TYPES = { | |||||
P2WPKH: 'p2wpkh', | |||||
P2SH_P2WPKH: 'p2sh(p2wpkh)', | |||||
}; | |||||
function sha256(b) { | function sha256(b) { | ||||
return createHash('sha256').update(b).digest(); | return createHash('sha256').update(b).digest(); | ||||
} | } | ||||
function hash256(buffer) { | function hash256(buffer) { | ||||
return sha256(sha256(buffer)); | return sha256(sha256(buffer)); | ||||
} | } | ||||
function hash160(buffer) { | function hash160(buffer) { | ||||
return createHash('ripemd160').update(sha256(buffer)).digest(); | return createHash('ripemd160').update(sha256(buffer)).digest(); | ||||
} | } | ||||
function encodeSignature(signature, recovery, compressed, segwitType) { | function encodeSignature(signature, recovery, compressed) { | ||||
if (segwitType !== undefined) { | |||||
recovery += 8; | |||||
if (segwitType === SEGWIT_TYPES.P2WPKH) recovery += 4; | |||||
} else { | |||||
if (compressed) recovery += 4; | if (compressed) recovery += 4; | ||||
} | |||||
return Buffer.concat([Buffer.alloc(1, recovery + 27), signature]); | return Buffer.concat([Buffer.alloc(1, recovery + 27), signature]); | ||||
} | } | ||||
function decodeSignature(buffer) { | function decodeSignature(buffer) { | ||||
if (buffer.length !== 65) throw new Error('Invalid signature length'); | if (buffer.length !== 65) throw new Error('Invalid signature length'); | ||||
const flagByte = buffer.readUInt8(0) - 27; | const flagByte = buffer.readUInt8(0) - 27; | ||||
if (flagByte > 15 || flagByte < 0) { | if (flagByte > 15 || flagByte < 0) { | ||||
throw new Error('Invalid signature parameter'); | throw new Error('Invalid signature parameter'); | ||||
} | } | ||||
return { | return { | ||||
compressed: !!(flagByte & 12), | compressed: !!(flagByte & 12), | ||||
segwitType: !(flagByte & 8) | |||||
? null | |||||
: !(flagByte & 4) | |||||
? SEGWIT_TYPES.P2SH_P2WPKH | |||||
: SEGWIT_TYPES.P2WPKH, | |||||
recovery: flagByte & 3, | recovery: flagByte & 3, | ||||
signature: buffer.slice(1), | signature: buffer.slice(1), | ||||
}; | }; | ||||
} | } | ||||
function magicHash(message, messagePrefix) { | function magicHash(message, messagePrefix) { | ||||
messagePrefix = messagePrefix || '\u0018Bitcoin Signed Message:\n'; | messagePrefix = messagePrefix || '\u0018Bitcoin Signed Message:\n'; | ||||
if (!Buffer.isBuffer(messagePrefix)) { | if (!Buffer.isBuffer(messagePrefix)) { | ||||
Show All 12 Lines | function magicHash(message, messagePrefix) { | ||||
return hash256(buffer); | return hash256(buffer); | ||||
} | } | ||||
function prepareSign(messagePrefixArg, sigOptions) { | function prepareSign(messagePrefixArg, sigOptions) { | ||||
if (typeof messagePrefixArg === 'object' && sigOptions === undefined) { | if (typeof messagePrefixArg === 'object' && sigOptions === undefined) { | ||||
sigOptions = messagePrefixArg; | sigOptions = messagePrefixArg; | ||||
messagePrefixArg = undefined; | messagePrefixArg = undefined; | ||||
} | } | ||||
let { segwitType, extraEntropy } = sigOptions || {}; | let { extraEntropy } = sigOptions || {}; | ||||
if ( | |||||
segwitType && | |||||
(typeof segwitType === 'string' || segwitType instanceof String) | |||||
) { | |||||
segwitType = segwitType.toLowerCase(); | |||||
} | |||||
if ( | |||||
segwitType && | |||||
segwitType !== SEGWIT_TYPES.P2SH_P2WPKH && | |||||
segwitType !== SEGWIT_TYPES.P2WPKH | |||||
) { | |||||
throw new Error( | |||||
'Unrecognized segwitType: use "' + | |||||
SEGWIT_TYPES.P2SH_P2WPKH + | |||||
'" or "' + | |||||
SEGWIT_TYPES.P2WPKH + | |||||
'"', | |||||
); | |||||
} | |||||
return { | return { | ||||
messagePrefixArg, | messagePrefixArg, | ||||
segwitType, | |||||
extraEntropy, | extraEntropy, | ||||
}; | }; | ||||
} | } | ||||
function isSigner(obj) { | function isSigner(obj) { | ||||
return obj && typeof obj.sign === 'function'; | return obj && typeof obj.sign === 'function'; | ||||
} | } | ||||
function sign(message, privateKey, compressed, messagePrefix, sigOptions) { | function sign(message, privateKey, compressed, messagePrefix, sigOptions) { | ||||
const { messagePrefixArg, segwitType, extraEntropy } = prepareSign( | const { messagePrefixArg, extraEntropy } = prepareSign( | ||||
messagePrefix, | messagePrefix, | ||||
sigOptions, | sigOptions, | ||||
); | ); | ||||
const hash = magicHash(message, messagePrefixArg); | const hash = magicHash(message, messagePrefixArg); | ||||
const sigObj = isSigner(privateKey) | const sigObj = isSigner(privateKey) | ||||
? privateKey.sign(hash, extraEntropy) | ? privateKey.sign(hash, extraEntropy) | ||||
: secp256k1.sign(hash, privateKey, { data: extraEntropy }); | : secp256k1.sign(hash, privateKey, { data: extraEntropy }); | ||||
return encodeSignature( | return encodeSignature(sigObj.signature, sigObj.recovery, compressed); | ||||
sigObj.signature, | |||||
sigObj.recovery, | |||||
compressed, | |||||
segwitType, | |||||
); | |||||
} | } | ||||
function signAsync(message, privateKey, compressed, messagePrefix, sigOptions) { | function signAsync(message, privateKey, compressed, messagePrefix, sigOptions) { | ||||
let messagePrefixArg, segwitType, extraEntropy; | let messagePrefixArg, extraEntropy; | ||||
return Promise.resolve() | return Promise.resolve() | ||||
.then(() => { | .then(() => { | ||||
({ messagePrefixArg, segwitType, extraEntropy } = prepareSign( | ({ messagePrefixArg, extraEntropy } = prepareSign( | ||||
messagePrefix, | messagePrefix, | ||||
sigOptions, | sigOptions, | ||||
)); | )); | ||||
const hash = magicHash(message, messagePrefixArg); | const hash = magicHash(message, messagePrefixArg); | ||||
return isSigner(privateKey) | return isSigner(privateKey) | ||||
? privateKey.sign(hash, extraEntropy) | ? privateKey.sign(hash, extraEntropy) | ||||
: secp256k1.sign(hash, privateKey, { data: extraEntropy }); | : secp256k1.sign(hash, privateKey, { data: extraEntropy }); | ||||
}) | }) | ||||
.then(sigObj => { | .then(sigObj => { | ||||
return encodeSignature( | return encodeSignature( | ||||
sigObj.signature, | sigObj.signature, | ||||
sigObj.recovery, | sigObj.recovery, | ||||
compressed, | compressed, | ||||
segwitType, | |||||
); | ); | ||||
}); | }); | ||||
} | } | ||||
function verify(message, xecAddress, signature, messagePrefix) { | function verify(message, xecAddress, signature, messagePrefix) { | ||||
if (!Buffer.isBuffer(signature)) | if (!Buffer.isBuffer(signature)) | ||||
signature = Buffer.from(signature, 'base64'); | signature = Buffer.from(signature, 'base64'); | ||||
▲ Show 20 Lines • Show All 44 Lines • Show Last 20 Lines |