c5f199e40f
As described in CVE-2020-14199 it is unsafe to only rely on witness UTXO information when signing. Hardware wallets fixed this by also requiring the full non-witness UTXO to be present for a witness input. To be compatible with those newer hardware wallet firmware, we need to remove the sanity checks that disallowed setting witness and non-witness UTXOs at the same time. See https://github.com/bitcoin/bitcoin/pull/19215 for comparison which removed the sanity checks in Bitcoin Core.
361 lines
7.6 KiB
Go
361 lines
7.6 KiB
Go
package psbt
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"io"
|
|
"sort"
|
|
|
|
"github.com/btcsuite/btcd/txscript"
|
|
"github.com/btcsuite/btcd/wire"
|
|
)
|
|
|
|
// PInput is a struct encapsulating all the data that can be attached to any
|
|
// specific input of the PSBT.
|
|
type PInput struct {
|
|
NonWitnessUtxo *wire.MsgTx
|
|
WitnessUtxo *wire.TxOut
|
|
PartialSigs []*PartialSig
|
|
SighashType txscript.SigHashType
|
|
RedeemScript []byte
|
|
WitnessScript []byte
|
|
Bip32Derivation []*Bip32Derivation
|
|
FinalScriptSig []byte
|
|
FinalScriptWitness []byte
|
|
Unknowns []*Unknown
|
|
}
|
|
|
|
// NewPsbtInput creates an instance of PsbtInput given either a nonWitnessUtxo
|
|
// or a witnessUtxo.
|
|
//
|
|
// NOTE: Only one of the two arguments should be specified, with the other
|
|
// being `nil`; otherwise the created PsbtInput object will fail IsSane()
|
|
// checks and will not be usable.
|
|
func NewPsbtInput(nonWitnessUtxo *wire.MsgTx,
|
|
witnessUtxo *wire.TxOut) *PInput {
|
|
|
|
return &PInput{
|
|
NonWitnessUtxo: nonWitnessUtxo,
|
|
WitnessUtxo: witnessUtxo,
|
|
PartialSigs: []*PartialSig{},
|
|
SighashType: 0,
|
|
RedeemScript: nil,
|
|
WitnessScript: nil,
|
|
Bip32Derivation: []*Bip32Derivation{},
|
|
FinalScriptSig: nil,
|
|
FinalScriptWitness: nil,
|
|
Unknowns: nil,
|
|
}
|
|
}
|
|
|
|
// IsSane returns true only if there are no conflicting values in the Psbt
|
|
// PInput. For segwit v0 no checks are currently implemented.
|
|
func (pi *PInput) IsSane() bool {
|
|
|
|
// TODO(guggero): Implement sanity checks for segwit v1. For segwit v0
|
|
// it is unsafe to only rely on the witness UTXO so we don't check that
|
|
// only one is set anymore.
|
|
// See https://github.com/bitcoin/bitcoin/pull/19215.
|
|
|
|
return true
|
|
}
|
|
|
|
// deserialize attempts to deserialize a new PInput from the passed io.Reader.
|
|
func (pi *PInput) deserialize(r io.Reader) error {
|
|
for {
|
|
keyint, keydata, err := getKey(r)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if keyint == -1 {
|
|
// Reached separator byte
|
|
break
|
|
}
|
|
value, err := wire.ReadVarBytes(
|
|
r, 0, MaxPsbtValueLength, "PSBT value",
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
switch InputType(keyint) {
|
|
|
|
case NonWitnessUtxoType:
|
|
if pi.NonWitnessUtxo != nil {
|
|
return ErrDuplicateKey
|
|
}
|
|
if keydata != nil {
|
|
return ErrInvalidKeydata
|
|
}
|
|
tx := wire.NewMsgTx(2)
|
|
|
|
err := tx.Deserialize(bytes.NewReader(value))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
pi.NonWitnessUtxo = tx
|
|
|
|
case WitnessUtxoType:
|
|
if pi.WitnessUtxo != nil {
|
|
return ErrDuplicateKey
|
|
}
|
|
if keydata != nil {
|
|
return ErrInvalidKeydata
|
|
}
|
|
txout, err := readTxOut(value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
pi.WitnessUtxo = txout
|
|
|
|
case PartialSigType:
|
|
newPartialSig := PartialSig{
|
|
PubKey: keydata,
|
|
Signature: value,
|
|
}
|
|
|
|
if !newPartialSig.checkValid() {
|
|
return ErrInvalidPsbtFormat
|
|
}
|
|
|
|
// Duplicate keys are not allowed
|
|
for _, x := range pi.PartialSigs {
|
|
if bytes.Equal(x.PubKey, newPartialSig.PubKey) {
|
|
return ErrDuplicateKey
|
|
}
|
|
}
|
|
|
|
pi.PartialSigs = append(pi.PartialSigs, &newPartialSig)
|
|
|
|
case SighashType:
|
|
if pi.SighashType != 0 {
|
|
return ErrDuplicateKey
|
|
}
|
|
if keydata != nil {
|
|
return ErrInvalidKeydata
|
|
}
|
|
|
|
// Bounds check on value here since the sighash type must be a
|
|
// 32-bit unsigned integer.
|
|
if len(value) != 4 {
|
|
return ErrInvalidKeydata
|
|
}
|
|
|
|
shtype := txscript.SigHashType(
|
|
binary.LittleEndian.Uint32(value),
|
|
)
|
|
pi.SighashType = shtype
|
|
|
|
case RedeemScriptInputType:
|
|
if pi.RedeemScript != nil {
|
|
return ErrDuplicateKey
|
|
}
|
|
if keydata != nil {
|
|
return ErrInvalidKeydata
|
|
}
|
|
pi.RedeemScript = value
|
|
|
|
case WitnessScriptInputType:
|
|
if pi.WitnessScript != nil {
|
|
return ErrDuplicateKey
|
|
}
|
|
if keydata != nil {
|
|
return ErrInvalidKeydata
|
|
}
|
|
pi.WitnessScript = value
|
|
|
|
case Bip32DerivationInputType:
|
|
if !validatePubkey(keydata) {
|
|
return ErrInvalidPsbtFormat
|
|
}
|
|
master, derivationPath, err := readBip32Derivation(value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Duplicate keys are not allowed
|
|
for _, x := range pi.Bip32Derivation {
|
|
if bytes.Equal(x.PubKey, keydata) {
|
|
return ErrDuplicateKey
|
|
}
|
|
}
|
|
|
|
pi.Bip32Derivation = append(
|
|
pi.Bip32Derivation,
|
|
&Bip32Derivation{
|
|
PubKey: keydata,
|
|
MasterKeyFingerprint: master,
|
|
Bip32Path: derivationPath,
|
|
},
|
|
)
|
|
|
|
case FinalScriptSigType:
|
|
if pi.FinalScriptSig != nil {
|
|
return ErrDuplicateKey
|
|
}
|
|
if keydata != nil {
|
|
return ErrInvalidKeydata
|
|
}
|
|
|
|
pi.FinalScriptSig = value
|
|
|
|
case FinalScriptWitnessType:
|
|
if pi.FinalScriptWitness != nil {
|
|
return ErrDuplicateKey
|
|
}
|
|
if keydata != nil {
|
|
return ErrInvalidKeydata
|
|
}
|
|
|
|
pi.FinalScriptWitness = value
|
|
|
|
default:
|
|
// A fall through case for any proprietary types.
|
|
keyintanddata := []byte{byte(keyint)}
|
|
keyintanddata = append(keyintanddata, keydata...)
|
|
newUnknown := &Unknown{
|
|
Key: keyintanddata,
|
|
Value: value,
|
|
}
|
|
|
|
// Duplicate key+keydata are not allowed
|
|
for _, x := range pi.Unknowns {
|
|
if bytes.Equal(x.Key, newUnknown.Key) &&
|
|
bytes.Equal(x.Value, newUnknown.Value) {
|
|
return ErrDuplicateKey
|
|
}
|
|
}
|
|
|
|
pi.Unknowns = append(pi.Unknowns, newUnknown)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// serialize attempts to serialize the target PInput into the passed io.Writer.
|
|
func (pi *PInput) serialize(w io.Writer) error {
|
|
|
|
if !pi.IsSane() {
|
|
return ErrInvalidPsbtFormat
|
|
}
|
|
|
|
if pi.NonWitnessUtxo != nil {
|
|
var buf bytes.Buffer
|
|
err := pi.NonWitnessUtxo.Serialize(&buf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = serializeKVPairWithType(
|
|
w, uint8(NonWitnessUtxoType), nil, buf.Bytes(),
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if pi.WitnessUtxo != nil {
|
|
var buf bytes.Buffer
|
|
err := wire.WriteTxOut(&buf, 0, 0, pi.WitnessUtxo)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = serializeKVPairWithType(
|
|
w, uint8(WitnessUtxoType), nil, buf.Bytes(),
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if pi.FinalScriptSig == nil && pi.FinalScriptWitness == nil {
|
|
sort.Sort(PartialSigSorter(pi.PartialSigs))
|
|
for _, ps := range pi.PartialSigs {
|
|
err := serializeKVPairWithType(
|
|
w, uint8(PartialSigType), ps.PubKey,
|
|
ps.Signature,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if pi.SighashType != 0 {
|
|
var shtBytes [4]byte
|
|
binary.LittleEndian.PutUint32(
|
|
shtBytes[:], uint32(pi.SighashType),
|
|
)
|
|
|
|
err := serializeKVPairWithType(
|
|
w, uint8(SighashType), nil, shtBytes[:],
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if pi.RedeemScript != nil {
|
|
err := serializeKVPairWithType(
|
|
w, uint8(RedeemScriptInputType), nil,
|
|
pi.RedeemScript,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if pi.WitnessScript != nil {
|
|
err := serializeKVPairWithType(
|
|
w, uint8(WitnessScriptInputType), nil,
|
|
pi.WitnessScript,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
sort.Sort(Bip32Sorter(pi.Bip32Derivation))
|
|
for _, kd := range pi.Bip32Derivation {
|
|
err := serializeKVPairWithType(
|
|
w,
|
|
uint8(Bip32DerivationInputType), kd.PubKey,
|
|
SerializeBIP32Derivation(
|
|
kd.MasterKeyFingerprint, kd.Bip32Path,
|
|
),
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
if pi.FinalScriptSig != nil {
|
|
err := serializeKVPairWithType(
|
|
w, uint8(FinalScriptSigType), nil, pi.FinalScriptSig,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if pi.FinalScriptWitness != nil {
|
|
err := serializeKVPairWithType(
|
|
w, uint8(FinalScriptWitnessType), nil, pi.FinalScriptWitness,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Unknown is a special case; we don't have a key type, only a key and
|
|
// a value field
|
|
for _, kv := range pi.Unknowns {
|
|
err := serializeKVpair(w, kv.Key, kv.Value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|