Throw errors when p2wsh or p2wpkh contain uncompressed pubkeys.

This will enforce BIP143 compressed pubkey rules on an address generation level.
This commit is contained in:
junderw 2020-05-21 11:11:12 +09:00
commit 25b5806cf1
No known key found for this signature in database
GPG key ID: B256185D3A971908
7 changed files with 107 additions and 17 deletions
ts_src/payments

View file

@ -118,13 +118,15 @@ export function p2wpkh(a: Payment, opts?: PaymentOpts): Payment {
if (hash.length > 0 && !hash.equals(pkh))
throw new TypeError('Hash mismatch');
else hash = pkh;
if (!ecc.isPoint(a.pubkey) || a.pubkey.length !== 33)
throw new TypeError('Invalid pubkey for p2wpkh');
}
if (a.witness) {
if (a.witness.length !== 2) throw new TypeError('Witness is invalid');
if (!bscript.isCanonicalScriptSignature(a.witness[0]))
throw new TypeError('Witness has invalid signature');
if (!ecc.isPoint(a.witness[1]))
if (!ecc.isPoint(a.witness[1]) || a.witness[1].length !== 33)
throw new TypeError('Witness has invalid pubkey');
if (a.signature && !a.signature.equals(a.witness[0]))

View file

@ -1,10 +1,11 @@
import * as bcrypto from '../crypto';
import { bitcoin as BITCOIN_NETWORK } from '../networks';
import * as bscript from '../script';
import { Payment, PaymentOpts, StackFunction } from './index';
import { Payment, PaymentOpts, StackElement, StackFunction } from './index';
import * as lazy from './lazy';
const typef = require('typeforce');
const OPS = bscript.OPS;
const ecc = require('tiny-secp256k1');
const bech32 = require('bech32');
@ -18,6 +19,15 @@ function stacksEqual(a: Buffer[], b: Buffer[]): boolean {
});
}
function chunkHasUncompressedPubkey(chunk: StackElement): boolean {
if (Buffer.isBuffer(chunk) && chunk.length === 65) {
if (ecc.isPoint(chunk)) return true;
else return false;
} else {
return false;
}
}
// input: <>
// witness: [redeemScriptSig ...] {redeemScript}
// output: OP_0 {sha256(redeemScript)}
@ -59,6 +69,9 @@ export function p2wsh(a: Payment, opts?: PaymentOpts): Payment {
const _rchunks = lazy.value(() => {
return bscript.decompile(a.redeem!.input!);
}) as StackFunction;
const _rochunks = lazy.value(() => {
return bscript.decompile(a.redeem!.output!);
}) as StackFunction;
let network = a.network;
if (!network) {
@ -187,15 +200,25 @@ export function p2wsh(a: Payment, opts?: PaymentOpts): Payment {
!stacksEqual(a.witness, a.redeem.witness)
)
throw new TypeError('Witness and redeem.witness mismatch');
if (
(a.redeem.input && _rchunks().some(chunkHasUncompressedPubkey)) ||
(a.redeem.output && _rochunks().some(chunkHasUncompressedPubkey))
) {
throw new TypeError(
'redeem.input or redeem.output contains uncompressed pubkey',
);
}
}
if (a.witness) {
if (
a.redeem &&
a.redeem.output &&
!a.redeem.output.equals(a.witness[a.witness.length - 1])
)
if (a.witness && a.witness.length > 0) {
const wScript = a.witness[a.witness.length - 1];
if (a.redeem && a.redeem.output && !a.redeem.output.equals(wScript))
throw new TypeError('Witness and redeem.output mismatch');
if (
a.witness.some(chunkHasUncompressedPubkey) ||
(bscript.decompile(wScript) || []).some(chunkHasUncompressedPubkey)
)
throw new TypeError('Witness contains uncompressed pubkey');
}
}