This updates the txscript module dependencies, the copyright year in the files modified since the previous release, and serves as a base for txscript/v4.0.0. The updated direct dependencies in this commit are as follows: - github.com/decred/dcrd/chaincfg/chainhash@v1.0.3 - github.com/decred/dcrd/chaincfg/v3@v3.1.0 - github.com/decred/dcrd/dcrec/edwards/v2@v2.0.2 - github.com/decred/dcrd/dcrec/secp256k1/v4@v4.0.1 - github.com/decred/dcrd/wire@v1.5.0 - github.com/decred/slog@v1.2.0 The full list of updated direct dependencies since the previous txscript/v3.0.0 release are as follows: - github.com/dchest/siphash@v1.2.2 - github.com/decred/base58@v1.0.3 - github.com/decred/dcrd/chaincfg/chainhash@v1.0.3 - github.com/decred/dcrd/chaincfg/v3@v3.1.0 - github.com/decred/dcrd/crypto/blake256@v1.0.0 - github.com/decred/dcrd/dcrec/edwards/v2@v2.0.2 - github.com/decred/dcrd/dcrec/secp256k1/v4@v4.0.1 - github.com/decred/dcrd/wire@v1.5.0 - github.com/decred/slog@v1.2.0 The following direct dependencies are no longer required as compared to the previous txscript/v3.0.0 release: - github.com/decred/dcrd/dcrutil/v3 Finally, all modules in the repository that depend on txscript are tidied to ensure they are updated to use the latest versions hoisted forward as a result.
289 lines
10 KiB
Go
289 lines
10 KiB
Go
// Copyright (c) 2015-2021 The Decred developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package txscript
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
|
)
|
|
|
|
const (
|
|
// LockTimeThreshold is the number below which a lock time is
|
|
// interpreted to be a block number. Since an average of one block
|
|
// is generated per 10 minutes, this allows blocks for about 9,512
|
|
// years.
|
|
LockTimeThreshold = 5e8 // Tue Nov 5 00:53:20 1985 UTC
|
|
)
|
|
|
|
// CheckSignatureEncoding returns whether or not the passed signature adheres to
|
|
// the strict encoding requirements.
|
|
func CheckSignatureEncoding(sig []byte) error {
|
|
// The format of a DER encoded signature is as follows:
|
|
//
|
|
// 0x30 <total length> 0x02 <length of R> <R> 0x02 <length of S> <S>
|
|
// - 0x30 is the ASN.1 identifier for a sequence
|
|
// - Total length is 1 byte and specifies length of all remaining data
|
|
// - 0x02 is the ASN.1 identifier that specifies an integer follows
|
|
// - Length of R is 1 byte and specifies how many bytes R occupies
|
|
// - R is the arbitrary length big-endian encoded number which
|
|
// represents the R value of the signature. DER encoding dictates
|
|
// that the value must be encoded using the minimum possible number
|
|
// of bytes. This implies the first byte can only be null if the
|
|
// highest bit of the next byte is set in order to prevent it from
|
|
// being interpreted as a negative number.
|
|
// - 0x02 is once again the ASN.1 integer identifier
|
|
// - Length of S is 1 byte and specifies how many bytes S occupies
|
|
// - S is the arbitrary length big-endian encoded number which
|
|
// represents the S value of the signature. The encoding rules are
|
|
// identical as those for R.
|
|
const (
|
|
asn1SequenceID = 0x30
|
|
asn1IntegerID = 0x02
|
|
|
|
// minSigLen is the minimum length of a DER encoded signature and is
|
|
// when both R and S are 1 byte each.
|
|
//
|
|
// 0x30 + <1-byte> + 0x02 + 0x01 + <byte> + 0x2 + 0x01 + <byte>
|
|
minSigLen = 8
|
|
|
|
// maxSigLen is the maximum length of a DER encoded signature and is
|
|
// when both R and S are 33 bytes each. It is 33 bytes because a
|
|
// 256-bit integer requires 32 bytes and an additional leading null byte
|
|
// might required if the high bit is set in the value.
|
|
//
|
|
// 0x30 + <1-byte> + 0x02 + 0x21 + <33 bytes> + 0x2 + 0x21 + <33 bytes>
|
|
maxSigLen = 72
|
|
|
|
// sequenceOffset is the byte offset within the signature of the
|
|
// expected ASN.1 sequence identifier.
|
|
sequenceOffset = 0
|
|
|
|
// dataLenOffset is the byte offset within the signature of the expected
|
|
// total length of all remaining data in the signature.
|
|
dataLenOffset = 1
|
|
|
|
// rTypeOffset is the byte offset within the signature of the ASN.1
|
|
// identifier for R and is expected to indicate an ASN.1 integer.
|
|
rTypeOffset = 2
|
|
|
|
// rLenOffset is the byte offset within the signature of the length of
|
|
// R.
|
|
rLenOffset = 3
|
|
|
|
// rOffset is the byte offset within the signature of R.
|
|
rOffset = 4
|
|
)
|
|
|
|
// The signature must adhere to the minimum and maximum allowed length.
|
|
sigLen := len(sig)
|
|
if sigLen < minSigLen {
|
|
str := fmt.Sprintf("malformed signature: too short: %d < %d", sigLen,
|
|
minSigLen)
|
|
return scriptError(ErrSigTooShort, str)
|
|
}
|
|
if sigLen > maxSigLen {
|
|
str := fmt.Sprintf("malformed signature: too long: %d > %d", sigLen,
|
|
maxSigLen)
|
|
return scriptError(ErrSigTooLong, str)
|
|
}
|
|
|
|
// The signature must start with the ASN.1 sequence identifier.
|
|
if sig[sequenceOffset] != asn1SequenceID {
|
|
str := fmt.Sprintf("malformed signature: format has wrong type: %#x",
|
|
sig[sequenceOffset])
|
|
return scriptError(ErrSigInvalidSeqID, str)
|
|
}
|
|
|
|
// The signature must indicate the correct amount of data for all elements
|
|
// related to R and S.
|
|
if int(sig[dataLenOffset]) != sigLen-2 {
|
|
str := fmt.Sprintf("malformed signature: bad length: %d != %d",
|
|
sig[dataLenOffset], sigLen-2)
|
|
return scriptError(ErrSigInvalidDataLen, str)
|
|
}
|
|
|
|
// Calculate the offsets of the elements related to S and ensure S is inside
|
|
// the signature.
|
|
//
|
|
// rLen specifies the length of the big-endian encoded number which
|
|
// represents the R value of the signature.
|
|
//
|
|
// sTypeOffset is the offset of the ASN.1 identifier for S and, like its R
|
|
// counterpart, is expected to indicate an ASN.1 integer.
|
|
//
|
|
// sLenOffset and sOffset are the byte offsets within the signature of the
|
|
// length of S and S itself, respectively.
|
|
rLen := int(sig[rLenOffset])
|
|
sTypeOffset := rOffset + rLen
|
|
sLenOffset := sTypeOffset + 1
|
|
if sTypeOffset >= sigLen {
|
|
str := "malformed signature: S type indicator missing"
|
|
return scriptError(ErrSigMissingSTypeID, str)
|
|
}
|
|
if sLenOffset >= sigLen {
|
|
str := "malformed signature: S length missing"
|
|
return scriptError(ErrSigMissingSLen, str)
|
|
}
|
|
|
|
// The lengths of R and S must match the overall length of the signature.
|
|
//
|
|
// sLen specifies the length of the big-endian encoded number which
|
|
// represents the S value of the signature.
|
|
sOffset := sLenOffset + 1
|
|
sLen := int(sig[sLenOffset])
|
|
if sOffset+sLen != sigLen {
|
|
str := "malformed signature: invalid S length"
|
|
return scriptError(ErrSigInvalidSLen, str)
|
|
}
|
|
|
|
// R elements must be ASN.1 integers.
|
|
if sig[rTypeOffset] != asn1IntegerID {
|
|
str := fmt.Sprintf("malformed signature: R integer marker: %#x != %#x",
|
|
sig[rTypeOffset], asn1IntegerID)
|
|
return scriptError(ErrSigInvalidRIntID, str)
|
|
}
|
|
|
|
// Zero-length integers are not allowed for R.
|
|
if rLen == 0 {
|
|
str := "malformed signature: R length is zero"
|
|
return scriptError(ErrSigZeroRLen, str)
|
|
}
|
|
|
|
// R must not be negative.
|
|
if sig[rOffset]&0x80 != 0 {
|
|
str := "malformed signature: R is negative"
|
|
return scriptError(ErrSigNegativeR, str)
|
|
}
|
|
|
|
// Null bytes at the start of R are not allowed, unless R would otherwise be
|
|
// interpreted as a negative number.
|
|
if rLen > 1 && sig[rOffset] == 0x00 && sig[rOffset+1]&0x80 == 0 {
|
|
str := "malformed signature: R value has too much padding"
|
|
return scriptError(ErrSigTooMuchRPadding, str)
|
|
}
|
|
|
|
// S elements must be ASN.1 integers.
|
|
if sig[sTypeOffset] != asn1IntegerID {
|
|
str := fmt.Sprintf("malformed signature: S integer marker: %#x != %#x",
|
|
sig[sTypeOffset], asn1IntegerID)
|
|
return scriptError(ErrSigInvalidSIntID, str)
|
|
}
|
|
|
|
// Zero-length integers are not allowed for S.
|
|
if sLen == 0 {
|
|
str := "malformed signature: S length is zero"
|
|
return scriptError(ErrSigZeroSLen, str)
|
|
}
|
|
|
|
// S must not be negative.
|
|
if sig[sOffset]&0x80 != 0 {
|
|
str := "malformed signature: S is negative"
|
|
return scriptError(ErrSigNegativeS, str)
|
|
}
|
|
|
|
// Null bytes at the start of S are not allowed, unless S would otherwise be
|
|
// interpreted as a negative number.
|
|
if sLen > 1 && sig[sOffset] == 0x00 && sig[sOffset+1]&0x80 == 0 {
|
|
str := "malformed signature: S value has too much padding"
|
|
return scriptError(ErrSigTooMuchSPadding, str)
|
|
}
|
|
|
|
// Strip leading zeroes from S.
|
|
sBytes := sig[sOffset : sOffset+sLen]
|
|
for len(sBytes) > 0 && sBytes[0] == 0x00 {
|
|
sBytes = sBytes[1:]
|
|
}
|
|
|
|
// Verify the S value is <= half the order of the curve. This check is done
|
|
// because when it is higher, the complement modulo the order can be used
|
|
// instead which is a shorter encoding by 1 byte.
|
|
//
|
|
// Also notice the check for the maximum number of bytes is required because
|
|
// SetByteSlice truncates as noted in its comment so it could otherwise fail
|
|
// to detect the overflow.
|
|
var s secp256k1.ModNScalar
|
|
if len(sBytes) > 32 {
|
|
str := "non-canonical signature: S is larger than 256 bits"
|
|
return scriptError(ErrSigHighS, str)
|
|
}
|
|
if overflow := s.SetByteSlice(sBytes); overflow {
|
|
str := "non-canonical signature: S >= group order"
|
|
return scriptError(ErrSigHighS, str)
|
|
}
|
|
if s.IsOverHalfOrder() {
|
|
str := "non-canonical signature: S > group half order"
|
|
return scriptError(ErrSigHighS, str)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// IsStrictSignatureEncoding returns false if the passed signature does not
|
|
// adhere to the strict encoding requirements.
|
|
func IsStrictSignatureEncoding(signature []byte) bool {
|
|
return CheckSignatureEncoding(signature) == nil
|
|
}
|
|
|
|
// CheckPubKeyEncoding returns an error if the passed public key does not
|
|
// adhere to the strict encoding requirements.
|
|
func CheckPubKeyEncoding(pubKey []byte) error {
|
|
if !isStrictPubKeyEncoding(pubKey) {
|
|
return scriptError(ErrPubKeyType, "unsupported public key type")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CheckHashTypeEncoding returns whether or not the passed hashtype adheres to
|
|
// the strict encoding requirements.
|
|
func CheckHashTypeEncoding(hashType SigHashType) error {
|
|
sigHashType := hashType & ^SigHashAnyOneCanPay
|
|
if sigHashType < SigHashAll || sigHashType > SigHashSingle {
|
|
str := fmt.Sprintf("invalid hash type 0x%x", hashType)
|
|
return scriptError(ErrInvalidSigHashType, str)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// IsStrictCompressedPubKeyEncoding returns whether or not the passed public
|
|
// key adheres to the strict compressed encoding requirements.
|
|
func IsStrictCompressedPubKeyEncoding(pubKey []byte) bool {
|
|
if len(pubKey) == 33 && (pubKey[0] == 0x02 || pubKey[0] == 0x03) {
|
|
// Compressed
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// IsStrictNullData returns whether or not the passed data is an OP_RETURN
|
|
// followed by specified length data push. It explicitly verifies that the
|
|
// opcode is identical to the required length. This function will always return
|
|
// false for required lengths > 75 bytes.
|
|
func IsStrictNullData(scriptVersion uint16, script []byte, requiredLen uint32) bool {
|
|
// The only currently supported script version is 0.
|
|
if scriptVersion != 0 {
|
|
return false
|
|
}
|
|
|
|
// The script can't possibly be a null data script if it doesn't start
|
|
// with OP_RETURN. Fail fast to avoid more work below.
|
|
if len(script) < 1 || script[0] != OP_RETURN {
|
|
return false
|
|
}
|
|
|
|
// Allow bare OP_RETURN when the required length is 0.
|
|
if len(script) == 1 && requiredLen == 0 {
|
|
return true
|
|
}
|
|
|
|
// OP_RETURN followed by data push of the required size.
|
|
tokenizer := MakeScriptTokenizer(scriptVersion, script[1:])
|
|
return tokenizer.Next() && tokenizer.Done() &&
|
|
isCanonicalPush(tokenizer.Opcode(), tokenizer.Data()) &&
|
|
((IsSmallInt(tokenizer.Opcode()) && requiredLen == 1) ||
|
|
(tokenizer.Opcode() <= OP_DATA_75 &&
|
|
uint32(len(tokenizer.Data())) == requiredLen))
|
|
}
|