bitcoinjs-message
Advanced tools
Comparing version 2.0.0 to 2.1.0
# 2.0.0 | ||
__breaking__ | ||
- `messagePrefix` is now the last parameter for the `sign`, `verify` and `magicHash` functions | ||
- `messagePrefix` is now defaulted to the Bitcoin network message prefix |
133
index.js
@@ -1,9 +0,17 @@ | ||
var bs58check = require('bs58check') | ||
var bufferEquals = require('buffer-equals') | ||
var createHash = require('create-hash') | ||
var secp256k1 = require('secp256k1') | ||
var varuint = require('varuint-bitcoin') | ||
const bs58check = require('bs58check') | ||
const bech32 = require('bech32') | ||
const bufferEquals = require('buffer-equals') | ||
const createHash = require('create-hash') | ||
const secp256k1 = require('secp256k1') | ||
const varuint = require('varuint-bitcoin') | ||
const SEGWIT_TYPES = { | ||
P2WPKH: 'p2wpkh', | ||
P2SH_P2WPKH: 'p2sh(p2wpkh)' | ||
} | ||
function sha256 (b) { | ||
return createHash('sha256').update(b).digest() | ||
return createHash('sha256') | ||
.update(b) | ||
.digest() | ||
} | ||
@@ -14,7 +22,14 @@ function hash256 (buffer) { | ||
function hash160 (buffer) { | ||
return createHash('ripemd160').update(sha256(buffer)).digest() | ||
return createHash('ripemd160') | ||
.update(sha256(buffer)) | ||
.digest() | ||
} | ||
function encodeSignature (signature, recovery, compressed) { | ||
if (compressed) recovery += 4 | ||
function encodeSignature (signature, recovery, compressed, segwitType) { | ||
if (segwitType !== undefined) { | ||
recovery += 8 | ||
if (segwitType === SEGWIT_TYPES.P2WPKH) recovery += 4 | ||
} else { | ||
if (compressed) recovery += 4 | ||
} | ||
return Buffer.concat([Buffer.alloc(1, recovery + 27), signature]) | ||
@@ -26,7 +41,14 @@ } | ||
var flagByte = buffer.readUInt8(0) - 27 | ||
if (flagByte > 7) throw new Error('Invalid signature parameter') | ||
const flagByte = buffer.readUInt8(0) - 27 | ||
if (flagByte > 15 || flagByte < 0) { | ||
throw new Error('Invalid signature parameter') | ||
} | ||
return { | ||
compressed: !!(flagByte & 4), | ||
compressed: !!(flagByte & 12), | ||
segwitType: !(flagByte & 8) | ||
? null | ||
: !(flagByte & 4) | ||
? SEGWIT_TYPES.P2SH_P2WPKH | ||
: SEGWIT_TYPES.P2WPKH, | ||
recovery: flagByte & 3, | ||
@@ -39,6 +61,10 @@ signature: buffer.slice(1) | ||
messagePrefix = messagePrefix || '\u0018Bitcoin Signed Message:\n' | ||
if (!Buffer.isBuffer(messagePrefix)) messagePrefix = Buffer.from(messagePrefix, 'utf8') | ||
if (!Buffer.isBuffer(messagePrefix)) { | ||
messagePrefix = Buffer.from(messagePrefix, 'utf8') | ||
} | ||
var messageVISize = varuint.encodingLength(message.length) | ||
var buffer = Buffer.allocUnsafe(messagePrefix.length + messageVISize + message.length) | ||
const messageVISize = varuint.encodingLength(message.length) | ||
const buffer = Buffer.allocUnsafe( | ||
messagePrefix.length + messageVISize + message.length | ||
) | ||
messagePrefix.copy(buffer, 0) | ||
@@ -50,6 +76,41 @@ varuint.encode(message.length, buffer, messagePrefix.length) | ||
function sign (message, privateKey, compressed, messagePrefix) { | ||
var hash = magicHash(message, messagePrefix) | ||
var sigObj = secp256k1.sign(hash, privateKey) | ||
return encodeSignature(sigObj.signature, sigObj.recovery, compressed) | ||
function sign ( | ||
message, | ||
privateKey, | ||
compressed, | ||
messagePrefix, | ||
sigOptions | ||
) { | ||
if (typeof messagePrefix === 'object' && sigOptions === undefined) { | ||
sigOptions = messagePrefix | ||
messagePrefix = undefined | ||
} | ||
let { segwitType, 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 + | ||
'"' | ||
) | ||
} | ||
const hash = magicHash(message, messagePrefix) | ||
const sigObj = secp256k1.sign(hash, privateKey, { data: extraEntropy }) | ||
return encodeSignature( | ||
sigObj.signature, | ||
sigObj.recovery, | ||
compressed, | ||
segwitType | ||
) | ||
} | ||
@@ -60,8 +121,32 @@ | ||
var parsed = decodeSignature(signature) | ||
var hash = magicHash(message, messagePrefix) | ||
var publicKey = secp256k1.recover(hash, parsed.signature, parsed.recovery, parsed.compressed) | ||
const parsed = decodeSignature(signature) | ||
const hash = magicHash(message, messagePrefix) | ||
const publicKey = secp256k1.recover( | ||
hash, | ||
parsed.signature, | ||
parsed.recovery, | ||
parsed.compressed | ||
) | ||
const publicKeyHash = hash160(publicKey) | ||
let actual, expected | ||
var actual = hash160(publicKey) | ||
var expected = bs58check.decode(address).slice(1) | ||
if (parsed.segwitType) { | ||
if (parsed.segwitType === SEGWIT_TYPES.P2SH_P2WPKH) { | ||
const redeemScript = Buffer.concat([ | ||
Buffer.from('0014', 'hex'), | ||
publicKeyHash | ||
]) | ||
const redeemScriptHash = hash160(redeemScript) | ||
actual = redeemScriptHash | ||
expected = bs58check.decode(address).slice(1) | ||
} else if (parsed.segwitType === SEGWIT_TYPES.P2WPKH) { | ||
const result = bech32.decode(address) | ||
const data = bech32.fromWords(result.words.slice(1)) | ||
actual = publicKeyHash | ||
expected = Buffer.from(data) | ||
} | ||
} else { | ||
actual = publicKeyHash | ||
expected = bs58check.decode(address).slice(1) | ||
} | ||
@@ -68,0 +153,0 @@ return bufferEquals(actual, expected) |
{ | ||
"name": "bitcoinjs-message", | ||
"version": "2.0.0", | ||
"version": "2.1.0", | ||
"description": "bitcoinjs-message", | ||
@@ -30,3 +30,4 @@ "keywords": [ | ||
"dependencies": { | ||
"bs58check": "^2.0.2", | ||
"bech32": "^1.1.3", | ||
"bs58check": "^2.1.2", | ||
"buffer-equals": "^1.0.3", | ||
@@ -40,4 +41,4 @@ "create-hash": "^1.1.2", | ||
"bitcoinjs-lib": "^3.2.0", | ||
"nyc": "^11.2.1", | ||
"standard": "*", | ||
"nyc": "^14.1.1", | ||
"standard": "^12.0.1", | ||
"tape": "^4.5.1" | ||
@@ -44,0 +45,0 @@ }, |
@@ -15,3 +15,7 @@ # bitcoinjs-message | ||
> sign(message, privateKey, compressed[, network.messagePrefix]) | ||
> sign(message, privateKey, compressed[, network.messagePrefix, sigOptions]) | ||
> - If you pass the sigOptions arg instead of messagePrefix it will dynamically replace. | ||
> - sigOptions contains two attributes | ||
> - `segwitType` should be one of `'p2sh(p2wpkh)'` or `'p2wpkh'` | ||
> - `extraEntropy` will be used to create non-deterministic signatures using the RFC6979 extra entropy parameter. R value reuse is not an issue. | ||
@@ -21,3 +25,3 @@ Sign a Bitcoin message | ||
var keyPair = bitcoin.ECPair.fromWIF('5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss') | ||
var privateKey = keyPair.d.toBuffer(32) | ||
var privateKey = keyPair.privateKey | ||
var message = 'This is an example of a signed message.' | ||
@@ -30,2 +34,27 @@ | ||
To produce non-deterministic signatures you can pass an extra option to sign() | ||
``` javascript | ||
var { randomBytes } = require('crypto') | ||
var keyPair = bitcoin.ECPair.fromWIF('5KYZdUEo39z3FPrtuX2QbbwGnNP5zTd7yyr2SC1j299sBCnWjss') | ||
var privateKey = keyPair.privateKey | ||
var message = 'This is an example of a signed message.' | ||
var signature = bitcoinMessage.sign(message, privateKey, keyPair.compressed, { extraEntropy: randomBytes(32) }) | ||
console.log(signature.toString('base64')) | ||
// => different (but valid) signature each time | ||
``` | ||
Sign a Bitcoin message (with segwit addresses) | ||
``` javascript | ||
// P2SH(P2WPKH) address 'p2sh(p2wpkh)' | ||
var signature = bitcoinMessage.sign(message, privateKey, keyPair.compressed, { segwitType: 'p2sh(p2wpkh)' }) | ||
console.log(signature.toString('base64')) | ||
// => 'I9L5yLFjti0QTHhPyFrZCT1V/MMnBtXKmoiKDZ78NDBjERki6ZTQZdSMCtkgoNmp17By9ItJr8o7ChX0XxY91nk=' | ||
// P2WPKH address 'p2wpkh' | ||
var signature = bitcoinMessage.sign(message, privateKey, keyPair.compressed, { segwitType: 'p2wpkh' }) | ||
console.log(signature.toString('base64')) | ||
// => 'J9L5yLFjti0QTHhPyFrZCT1V/MMnBtXKmoiKDZ78NDBjERki6ZTQZdSMCtkgoNmp17By9ItJr8o7ChX0XxY91nk=' | ||
``` | ||
> verify(message, address, signature[, network.messagePrefix]) | ||
@@ -36,4 +65,2 @@ | ||
var address = '1HZwkjkeaoZfTSaJxDw6aKkxp45agDiEzN' | ||
var signature = 'HJLQlDWLyb1Ef8bQKEISzFbDAKctIlaqOpGbrk3YVtRsjmC61lpE5ErkPRUFtDKtx98vHFGUWlFhsh3DiW6N0rE' | ||
var message = 'This is an example of a signed message.' | ||
@@ -40,0 +67,0 @@ console.log(bitcoinMessage.verify(message, address, signature)) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
9437
141
68
6
1
+ Addedbech32@^1.1.3
+ Addedbech32@1.1.4(transitive)
Updatedbs58check@^2.1.2