Skip to content

Commit

Permalink
Add Signer and SignerAsync interfaces, and signAsync function
Browse files Browse the repository at this point in the history
  • Loading branch information
junderw committed Jan 27, 2021
1 parent dc5e526 commit cb87a98
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 13 deletions.
33 changes: 31 additions & 2 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,20 @@ interface SignatureOptions {
extraEntropy?: Buffer;
}

export interface Signer {
// param hash: 32 byte Buffer containing the digest of the message
// param extraEntropy (optional): the 32 byte Buffer of the "extra data" part of RFC6979 nonces
// returns object
// attribute signature: 64 byte Buffer, first 32 R value, last 32 S value of ECDSA signature
// attribute recovery: Number (integer) from 0 to 3 (inclusive), also known as recid, used for pubkey recovery
sign(hash: Buffer, extraEntropy?: Buffer): { signature: Buffer; recovery: number; };
}

export interface SignerAsync {
// Same as Signer, but return is wrapped in a Promise
sign(hash: Buffer, extraEntropy?: Buffer): Promise<{ signature: Buffer; recovery: number; }>;
}

export function magicHash(
message: string | Buffer,
messagePrefix?: string
Expand All @@ -11,18 +25,33 @@ export function magicHash(
// sign function is overloaded
export function sign(
message: string | Buffer,
privateKey: Buffer,
privateKey: Buffer | Signer,
compressed?: boolean,
sigOptions?: SignatureOptions
): Buffer;
export function sign(
message: string | Buffer,
privateKey: Buffer,
privateKey: Buffer | Signer,
compressed?: boolean,
messagePrefix?: string,
sigOptions?: SignatureOptions
): Buffer;

// signAsync function is overloaded
export function signAsync(
message: string | Buffer,
privateKey: Buffer | SignerAsync | Signer,
compressed?: boolean,
sigOptions?: SignatureOptions
): Promise<Buffer>;
export function signAsync(
message: string | Buffer,
privateKey: Buffer | SignerAsync | Signer,
compressed?: boolean,
messagePrefix?: string,
sigOptions?: SignatureOptions
): Promise<Buffer>;

export function verify(
message: string | Buffer,
address: string,
Expand Down
72 changes: 62 additions & 10 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,13 @@ function magicHash (message, messagePrefix) {
return hash256(buffer)
}

function sign (
message,
privateKey,
compressed,
messagePrefix,
function prepareSign (
messagePrefixArg,
sigOptions
) {
if (typeof messagePrefix === 'object' && sigOptions === undefined) {
sigOptions = messagePrefix
messagePrefix = undefined
if (typeof messagePrefixArg === 'object' && sigOptions === undefined) {
sigOptions = messagePrefixArg
messagePrefixArg = undefined
}
let { segwitType, extraEntropy } = sigOptions || {}
if (
Expand All @@ -103,8 +100,34 @@ function sign (
'"'
)
}
const hash = magicHash(message, messagePrefix)
const sigObj = secp256k1.sign(hash, privateKey, { data: extraEntropy })

return {
messagePrefixArg,
segwitType,
extraEntropy
}
}

function isSigner (obj) {
return obj && typeof obj.sign === 'function'
}

function sign (
message,
privateKey,
compressed,
messagePrefix,
sigOptions
) {
const {
messagePrefixArg,
segwitType,
extraEntropy
} = prepareSign(messagePrefix, sigOptions)
const hash = magicHash(message, messagePrefixArg)
const sigObj = isSigner(privateKey)
? privateKey.sign(hash, extraEntropy)
: secp256k1.sign(hash, privateKey, { data: extraEntropy })
return encodeSignature(
sigObj.signature,
sigObj.recovery,
Expand All @@ -113,6 +136,34 @@ function sign (
)
}

function signAsync (
message,
privateKey,
compressed,
messagePrefix,
sigOptions
) {
let messagePrefixArg, segwitType, extraEntropy
return Promise.resolve().then(() => {
({
messagePrefixArg,
segwitType,
extraEntropy
} = prepareSign(messagePrefix, sigOptions))
const hash = magicHash(message, messagePrefixArg)
return isSigner(privateKey)
? privateKey.sign(hash, extraEntropy)
: secp256k1.sign(hash, privateKey, { data: extraEntropy })
}).then((sigObj) => {
return encodeSignature(
sigObj.signature,
sigObj.recovery,
compressed,
segwitType
)
})
}

function segwitRedeemHash (publicKeyHash) {
const redeemScript = Buffer.concat([
Buffer.from('0014', 'hex'),
Expand Down Expand Up @@ -184,5 +235,6 @@ function verify (message, address, signature, messagePrefix, checkSegwitAlways)
module.exports = {
magicHash: magicHash,
sign: sign,
signAsync: signAsync,
verify: verify
}
33 changes: 32 additions & 1 deletion test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const bech32 = require('bech32')
const createHash = require('create-hash')
const bitcoin = require('bitcoinjs-lib')
const BigInteger = require('bigi')
const secp256k1 = require('secp256k1')
const message = require('../')

const fixtures = require('./fixtures.json')
Expand All @@ -24,15 +25,45 @@ fixtures.valid.magicHash.forEach(f => {
})

fixtures.valid.sign.forEach(f => {
test('sign: ' + f.description, t => {
test('sign: ' + f.description, async t => {
const pk = new bitcoin.ECPair(new BigInteger(f.d)).d.toBuffer(32)
const signer = (hash, ex) => secp256k1.sign(hash, pk, { data: ex })
const signerAsync = async (hash, ex) => secp256k1.sign(hash, pk, { data: ex })
let signature = message.sign(
f.message,
pk,
false,
getMessagePrefix(f.network)
)
let signature2 = message.sign(
f.message,
{ sign: signer },
false,
getMessagePrefix(f.network)
)
let signature3 = await message.signAsync(
f.message,
{ sign: signerAsync },
false,
getMessagePrefix(f.network)
)
let signature4 = await message.signAsync(
f.message,
{ sign: signer },
false,
getMessagePrefix(f.network)
)
let signature5 = await message.signAsync(
f.message,
pk,
false,
getMessagePrefix(f.network)
)
t.same(signature.toString('base64'), f.signature)
t.same(signature2.toString('base64'), f.signature)
t.same(signature3.toString('base64'), f.signature)
t.same(signature4.toString('base64'), f.signature)
t.same(signature5.toString('base64'), f.signature)

if (f.compressed) {
signature = message.sign(f.message, pk, true, getMessagePrefix(f.network))
Expand Down

0 comments on commit cb87a98

Please sign in to comment.