Add sighash checks for signer

This commit is contained in:
junderw 2019-07-10 11:15:12 +09:00
parent fa897cf78e
commit ccab2652f9
No known key found for this signature in database
GPG key ID: B256185D3A971908
3 changed files with 117 additions and 20 deletions

View file

@ -273,7 +273,7 @@ class Psbt extends bip174_1.Psbt {
}
return results.every(res => res === true);
}
sign(keyPair) {
sign(keyPair, sighashTypes = [transaction_1.Transaction.SIGHASH_ALL]) {
if (!keyPair || !keyPair.publicKey)
throw new Error('Need Signer to sign input');
// TODO: Add a pubkey/pubkeyhash cache to each input
@ -282,7 +282,7 @@ class Psbt extends bip174_1.Psbt {
const results = [];
for (const i of range(this.inputs.length)) {
try {
this.signInput(i, keyPair);
this.signInput(i, keyPair, sighashTypes);
results.push(true);
} catch (err) {
results.push(false);
@ -293,7 +293,7 @@ class Psbt extends bip174_1.Psbt {
}
return this;
}
signAsync(keyPair) {
signAsync(keyPair, sighashTypes = [transaction_1.Transaction.SIGHASH_ALL]) {
return new Promise((resolve, reject) => {
if (!keyPair || !keyPair.publicKey)
return reject(new Error('Need Signer to sign input'));
@ -304,7 +304,7 @@ class Psbt extends bip174_1.Psbt {
const promises = [];
for (const [i] of this.inputs.entries()) {
promises.push(
this.signInputAsync(i, keyPair).then(
this.signInputAsync(i, keyPair, sighashTypes).then(
() => {
results.push(true);
},
@ -322,7 +322,11 @@ class Psbt extends bip174_1.Psbt {
});
});
}
signInput(inputIndex, keyPair) {
signInput(
inputIndex,
keyPair,
sighashTypes = [transaction_1.Transaction.SIGHASH_ALL],
) {
if (!keyPair || !keyPair.publicKey)
throw new Error('Need Signer to sign input');
const { hash, sighashType } = getHashAndSighashType(
@ -330,6 +334,7 @@ class Psbt extends bip174_1.Psbt {
inputIndex,
keyPair.publicKey,
this.__CACHE,
sighashTypes,
);
const partialSig = {
pubkey: keyPair.publicKey,
@ -337,7 +342,11 @@ class Psbt extends bip174_1.Psbt {
};
return this.addPartialSigToInput(inputIndex, partialSig);
}
signInputAsync(inputIndex, keyPair) {
signInputAsync(
inputIndex,
keyPair,
sighashTypes = [transaction_1.Transaction.SIGHASH_ALL],
) {
return new Promise((resolve, reject) => {
if (!keyPair || !keyPair.publicKey)
return reject(new Error('Need Signer to sign input'));
@ -346,6 +355,7 @@ class Psbt extends bip174_1.Psbt {
inputIndex,
keyPair.publicKey,
this.__CACHE,
sighashTypes,
);
Promise.resolve(keyPair.sign(hash)).then(signature => {
const partialSig = {
@ -548,19 +558,37 @@ function getFinalScripts(
finalScriptWitness,
};
}
function getHashAndSighashType(inputs, inputIndex, pubkey, cache) {
function getHashAndSighashType(
inputs,
inputIndex,
pubkey,
cache,
sighashTypes,
) {
const input = utils_1.checkForInput(inputs, inputIndex);
const { hash, sighashType, script } = getHashForSig(inputIndex, input, cache);
const { hash, sighashType, script } = getHashForSig(
inputIndex,
input,
cache,
sighashTypes,
);
checkScriptForPubkey(pubkey, script, 'sign');
return {
hash,
sighashType,
};
}
function getHashForSig(inputIndex, input, cache) {
function getHashForSig(inputIndex, input, cache, sighashTypes) {
const unsignedTx = cache.__TX;
const sighashType =
input.sighashType || transaction_1.Transaction.SIGHASH_ALL;
if (sighashTypes && sighashTypes.indexOf(sighashType) < 0) {
const str = sighashTypeToString(sighashType);
throw new Error(
`Sighash type is not allowed. Retry the sign method passing the ` +
`sighashTypes array of whitelisted types. Sighash type: ${str}`,
);
}
let hash;
let script;
if (input.nonWitnessUtxo) {
@ -800,6 +828,25 @@ function scriptWitnessToWitnessStack(buffer) {
}
return readVector();
}
function sighashTypeToString(sighashType) {
let text =
sighashType & transaction_1.Transaction.SIGHASH_ANYONECANPAY
? 'SIGHASH_ANYONECANPAY | '
: '';
const sigMod = sighashType & 0x1f;
switch (sigMod) {
case transaction_1.Transaction.SIGHASH_ALL:
text += 'SIGHASH_ALL';
break;
case transaction_1.Transaction.SIGHASH_SINGLE:
text += 'SIGHASH_SINGLE';
break;
case transaction_1.Transaction.SIGHASH_NONE:
text += 'SIGHASH_NONE';
break;
}
return text;
}
function witnessStackToScriptWitness(witness) {
let buffer = Buffer.allocUnsafe(0);
function writeSlice(slice) {

View file

@ -332,7 +332,10 @@ export class Psbt extends PsbtBase {
return results.every(res => res === true);
}
sign(keyPair: Signer): this {
sign(
keyPair: Signer,
sighashTypes: number[] = [Transaction.SIGHASH_ALL],
): this {
if (!keyPair || !keyPair.publicKey)
throw new Error('Need Signer to sign input');
@ -342,7 +345,7 @@ export class Psbt extends PsbtBase {
const results: boolean[] = [];
for (const i of range(this.inputs.length)) {
try {
this.signInput(i, keyPair);
this.signInput(i, keyPair, sighashTypes);
results.push(true);
} catch (err) {
results.push(false);
@ -354,7 +357,10 @@ export class Psbt extends PsbtBase {
return this;
}
signAsync(keyPair: SignerAsync): Promise<void> {
signAsync(
keyPair: SignerAsync,
sighashTypes: number[] = [Transaction.SIGHASH_ALL],
): Promise<void> {
return new Promise(
(resolve, reject): any => {
if (!keyPair || !keyPair.publicKey)
@ -367,7 +373,7 @@ export class Psbt extends PsbtBase {
const promises: Array<Promise<void>> = [];
for (const [i] of this.inputs.entries()) {
promises.push(
this.signInputAsync(i, keyPair).then(
this.signInputAsync(i, keyPair, sighashTypes).then(
() => {
results.push(true);
},
@ -387,7 +393,11 @@ export class Psbt extends PsbtBase {
);
}
signInput(inputIndex: number, keyPair: Signer): this {
signInput(
inputIndex: number,
keyPair: Signer,
sighashTypes: number[] = [Transaction.SIGHASH_ALL],
): this {
if (!keyPair || !keyPair.publicKey)
throw new Error('Need Signer to sign input');
const { hash, sighashType } = getHashAndSighashType(
@ -395,6 +405,7 @@ export class Psbt extends PsbtBase {
inputIndex,
keyPair.publicKey,
this.__CACHE,
sighashTypes,
);
const partialSig = {
@ -405,7 +416,11 @@ export class Psbt extends PsbtBase {
return this.addPartialSigToInput(inputIndex, partialSig);
}
signInputAsync(inputIndex: number, keyPair: SignerAsync): Promise<void> {
signInputAsync(
inputIndex: number,
keyPair: SignerAsync,
sighashTypes: number[] = [Transaction.SIGHASH_ALL],
): Promise<void> {
return new Promise(
(resolve, reject): void => {
if (!keyPair || !keyPair.publicKey)
@ -415,6 +430,7 @@ export class Psbt extends PsbtBase {
inputIndex,
keyPair.publicKey,
this.__CACHE,
sighashTypes,
);
Promise.resolve(keyPair.sign(hash)).then(signature => {
@ -683,12 +699,18 @@ function getHashAndSighashType(
inputIndex: number,
pubkey: Buffer,
cache: PsbtCache,
sighashTypes: number[],
): {
hash: Buffer;
sighashType: number;
} {
const input = checkForInput(inputs, inputIndex);
const { hash, sighashType, script } = getHashForSig(inputIndex, input, cache);
const { hash, sighashType, script } = getHashForSig(
inputIndex,
input,
cache,
sighashTypes,
);
checkScriptForPubkey(pubkey, script, 'sign');
return {
hash,
@ -700,6 +722,7 @@ function getHashForSig(
inputIndex: number,
input: PsbtInput,
cache: PsbtCache,
sighashTypes?: number[],
): {
script: Buffer;
hash: Buffer;
@ -707,6 +730,13 @@ function getHashForSig(
} {
const unsignedTx = cache.__TX;
const sighashType = input.sighashType || Transaction.SIGHASH_ALL;
if (sighashTypes && sighashTypes.indexOf(sighashType) < 0) {
const str = sighashTypeToString(sighashType);
throw new Error(
`Sighash type is not allowed. Retry the sign method passing the ` +
`sighashTypes array of whitelisted types. Sighash type: ${str}`,
);
}
let hash: Buffer;
let script: Buffer;
@ -981,6 +1011,26 @@ function scriptWitnessToWitnessStack(buffer: Buffer): Buffer[] {
return readVector();
}
function sighashTypeToString(sighashType: number): string {
let text =
sighashType & Transaction.SIGHASH_ANYONECANPAY
? 'SIGHASH_ANYONECANPAY | '
: '';
const sigMod = sighashType & 0x1f;
switch (sigMod) {
case Transaction.SIGHASH_ALL:
text += 'SIGHASH_ALL';
break;
case Transaction.SIGHASH_SINGLE:
text += 'SIGHASH_SINGLE';
break;
case Transaction.SIGHASH_NONE:
text += 'SIGHASH_NONE';
break;
}
return text;
}
function witnessStackToScriptWitness(witness: Buffer[]): Buffer {
let buffer = Buffer.allocUnsafe(0);

8
types/psbt.d.ts vendored
View file

@ -27,10 +27,10 @@ export declare class Psbt extends PsbtBase {
finalizeInput(inputIndex: number): this;
validateAllSignatures(): boolean;
validateSignatures(inputIndex: number, pubkey?: Buffer): boolean;
sign(keyPair: Signer): this;
signAsync(keyPair: SignerAsync): Promise<void>;
signInput(inputIndex: number, keyPair: Signer): this;
signInputAsync(inputIndex: number, keyPair: SignerAsync): Promise<void>;
sign(keyPair: Signer, sighashTypes?: number[]): this;
signAsync(keyPair: SignerAsync, sighashTypes?: number[]): Promise<void>;
signInput(inputIndex: number, keyPair: Signer, sighashTypes?: number[]): this;
signInputAsync(inputIndex: number, keyPair: SignerAsync, sighashTypes?: number[]): Promise<void>;
}
interface PsbtOptsOptional {
network?: Network;