This adds the ability for the short form script parsing to handle different script versions and updates all of the tests to use the version 0 function appropriately.
474 lines
16 KiB
Go
474 lines
16 KiB
Go
// Copyright (c) 2018-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 (
|
|
"errors"
|
|
"testing"
|
|
)
|
|
|
|
// TestCheckSignatureEncoding ensures that checking strict signature encoding
|
|
// works as expected.
|
|
func TestCheckSignatureEncoding(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
sig []byte
|
|
err error
|
|
}{{
|
|
// signature from Decred blockchain tx
|
|
// 76634e947f49dfc6228c3e8a09cd3e9e15893439fc06df7df0fc6f08d659856c:0
|
|
name: "valid signature 1",
|
|
sig: hexToBytes("3045022100cd496f2ab4fe124f977ffe3caa09f7576d8a34156" +
|
|
"b4e55d326b4dffc0399a094022013500a0510b5094bff220c74656879b8ca03" +
|
|
"69d3da78004004c970790862fc03"),
|
|
err: nil,
|
|
}, {
|
|
// signature from Decred blockchain tx
|
|
// 76634e947f49dfc6228c3e8a09cd3e9e15893439fc06df7df0fc6f08d659856c:1
|
|
name: "valid signature 2",
|
|
sig: hexToBytes("3044022036334e598e51879d10bf9ce3171666bc2d1bbba6164" +
|
|
"cf46dd1d882896ba35d5d022056c39af9ea265c1b6d7eab5bc977f06f81e35c" +
|
|
"dcac16f3ec0fd218e30f2bad2a"),
|
|
err: nil,
|
|
}, {
|
|
name: "empty",
|
|
sig: nil,
|
|
err: ErrSigTooShort,
|
|
}, {
|
|
name: "too short",
|
|
sig: hexToBytes("30050201000200"),
|
|
err: ErrSigTooShort,
|
|
}, {
|
|
name: "too long",
|
|
sig: hexToBytes("3045022100f5353150d31a63f4a0d06d1f5a01ac65f7267a719e" +
|
|
"49f2a1ac584fd546bef074022030e09575e7a1541aa018876a4003cefe1b061a" +
|
|
"90556b5140c63e0ef8481352480101"),
|
|
err: ErrSigTooLong,
|
|
}, {
|
|
name: "bad ASN.1 sequence id",
|
|
sig: hexToBytes("3145022100f5353150d31a63f4a0d06d1f5a01ac65f7267a719e" +
|
|
"49f2a1ac584fd546bef074022030e09575e7a1541aa018876a4003cefe1b061a" +
|
|
"90556b5140c63e0ef848135248"),
|
|
err: ErrSigInvalidSeqID,
|
|
}, {
|
|
name: "mismatched data length (short one byte)",
|
|
sig: hexToBytes("3044022100f5353150d31a63f4a0d06d1f5a01ac65f7267a719e" +
|
|
"49f2a1ac584fd546bef074022030e09575e7a1541aa018876a4003cefe1b061a" +
|
|
"90556b5140c63e0ef848135248"),
|
|
err: ErrSigInvalidDataLen,
|
|
}, {
|
|
name: "mismatched data length (long one byte)",
|
|
sig: hexToBytes("3046022100f5353150d31a63f4a0d06d1f5a01ac65f7267a719e" +
|
|
"49f2a1ac584fd546bef074022030e09575e7a1541aa018876a4003cefe1b061a" +
|
|
"90556b5140c63e0ef848135248"),
|
|
err: ErrSigInvalidDataLen,
|
|
}, {
|
|
name: "bad R ASN.1 int marker",
|
|
sig: hexToBytes("304403204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d6" +
|
|
"24c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56c" +
|
|
"bbac4622082221a8768d1d09"),
|
|
err: ErrSigInvalidRIntID,
|
|
}, {
|
|
name: "zero R length",
|
|
sig: hexToBytes("30240200022030e09575e7a1541aa018876a4003cefe1b061a90" +
|
|
"556b5140c63e0ef848135248"),
|
|
err: ErrSigZeroRLen,
|
|
}, {
|
|
name: "negative R (too little padding)",
|
|
sig: hexToBytes("30440220b2ec8d34d473c3aa2ab5eb7cc4a0783977e5db8c8daf" +
|
|
"777e0b6d7bfa6b6623f302207df6f09af2c40460da2c2c5778f636d3b2e27e20" +
|
|
"d10d90f5a5afb45231454700"),
|
|
err: ErrSigNegativeR,
|
|
}, {
|
|
name: "too much R padding",
|
|
sig: hexToBytes("304402200077f6e93de5ed43cf1dfddaa79fca4b766e1a8fc879" +
|
|
"b0333d377f62538d7eb5022054fed940d227ed06d6ef08f320976503848ed1f5" +
|
|
"2d0dd6d17f80c9c160b01d86"),
|
|
err: ErrSigTooMuchRPadding,
|
|
}, {
|
|
name: "bad S ASN.1 int marker",
|
|
sig: hexToBytes("3045022100f5353150d31a63f4a0d06d1f5a01ac65f7267a719e" +
|
|
"49f2a1ac584fd546bef074032030e09575e7a1541aa018876a4003cefe1b061a" +
|
|
"90556b5140c63e0ef848135248"),
|
|
err: ErrSigInvalidSIntID,
|
|
}, {
|
|
name: "missing S ASN.1 int marker",
|
|
sig: hexToBytes("3023022100f5353150d31a63f4a0d06d1f5a01ac65f7267a719e" +
|
|
"49f2a1ac584fd546bef074"),
|
|
err: ErrSigMissingSTypeID,
|
|
}, {
|
|
name: "S length missing",
|
|
sig: hexToBytes("3024022100f5353150d31a63f4a0d06d1f5a01ac65f7267a719e" +
|
|
"49f2a1ac584fd546bef07402"),
|
|
err: ErrSigMissingSLen,
|
|
}, {
|
|
name: "invalid S length (short one byte)",
|
|
sig: hexToBytes("3045022100f5353150d31a63f4a0d06d1f5a01ac65f7267a719e" +
|
|
"49f2a1ac584fd546bef074021f30e09575e7a1541aa018876a4003cefe1b061a" +
|
|
"90556b5140c63e0ef848135248"),
|
|
err: ErrSigInvalidSLen,
|
|
}, {
|
|
name: "invalid S length (long one byte)",
|
|
sig: hexToBytes("3045022100f5353150d31a63f4a0d06d1f5a01ac65f7267a719e" +
|
|
"49f2a1ac584fd546bef074022130e09575e7a1541aa018876a4003cefe1b061a" +
|
|
"90556b5140c63e0ef848135248"),
|
|
err: ErrSigInvalidSLen,
|
|
}, {
|
|
name: "zero S length",
|
|
sig: hexToBytes("3025022100f5353150d31a63f4a0d06d1f5a01ac65f7267a719e" +
|
|
"49f2a1ac584fd546bef0740200"),
|
|
err: ErrSigZeroSLen,
|
|
}, {
|
|
name: "negative S (too little padding)",
|
|
sig: hexToBytes("304402204fc10344934662ca0a93a84d14d650d8a21cf2ab91f6" +
|
|
"08e8783d2999c955443202208441aacd6b17038ff3f6700b042934f9a6fea0ce" +
|
|
"c2051b51dc709e52a5bb7d61"),
|
|
err: ErrSigNegativeS,
|
|
}, {
|
|
name: "too much S padding",
|
|
sig: hexToBytes("304402206ad2fdaf8caba0f2cb2484e61b81ced77474b4c2aa06" +
|
|
"9c852df1351b3314fe20022000695ad175b09a4a41cd9433f6b2e8e83253d6a7" +
|
|
"402096ba313a7be1f086dde5"),
|
|
err: ErrSigTooMuchSPadding,
|
|
}, {
|
|
// Although signatures with R == 0 will ultimately be invalid, it is
|
|
// considered a valid encoding from the standpoint of preventing
|
|
// malleability.
|
|
name: "R == 0",
|
|
sig: hexToBytes("30250201000220181522ec8eca07de4860a4acdd12909d831cc5" +
|
|
"6cbbac4622082221a8768d1d09"),
|
|
err: nil,
|
|
}, {
|
|
// Although signatures with R == N will ultimately be invalid, it is
|
|
// considered a valid encoding from the standpoint of preventing
|
|
// malleability.
|
|
name: "R == N",
|
|
sig: hexToBytes("3045022100fffffffffffffffffffffffffffffffebaaedce6af" +
|
|
"48a03bbfd25e8cd03641410220181522ec8eca07de4860a4acdd12909d831cc5" +
|
|
"6cbbac4622082221a8768d1d09"),
|
|
err: nil,
|
|
}, {
|
|
// Although signatures with R > N will ultimately be invalid, it is
|
|
// considered a valid encoding from the standpoint of preventing
|
|
// malleability.
|
|
name: "R > N (>32 bytes)",
|
|
sig: hexToBytes("3045022101cd496f2ab4fe124f977ffe3caa09f756283910fc1a" +
|
|
"96f60ee6873e88d3cfe1d50220181522ec8eca07de4860a4acdd12909d831cc5" +
|
|
"6cbbac4622082221a8768d1d09"),
|
|
err: nil,
|
|
}, {
|
|
// Although signatures with R > N will ultimately be invalid, it is
|
|
// considered a valid encoding from the standpoint of preventing
|
|
// malleability.
|
|
name: "R > N",
|
|
sig: hexToBytes("3045022100fffffffffffffffffffffffffffffffebaaedce6af" +
|
|
"48a03bbfd25e8cd03641420220181522ec8eca07de4860a4acdd12909d831cc5" +
|
|
"6cbbac4622082221a8768d1d09"),
|
|
err: nil,
|
|
}, {
|
|
// Although signatures with S == 0 will ultimately be invalid, it is
|
|
// considered a valid encoding from the standpoint of preventing
|
|
// malleability.
|
|
name: "S == 0",
|
|
sig: hexToBytes("302502204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d6" +
|
|
"24c6c61548ab5fb8cd41020100"),
|
|
err: nil,
|
|
}, {
|
|
name: "S > N/2 (half order)",
|
|
sig: hexToBytes("304602210080e256f8a9df823ff0322c5515fc4d4538d65a3785" +
|
|
"fb6dd1b448af216864318d022100cfbf242e941d77555bd79fadd3d23b49d3ca" +
|
|
"929459fa114247e55ff8b4fcf832"),
|
|
err: ErrSigHighS,
|
|
}, {
|
|
name: "S == N",
|
|
sig: hexToBytes("304502204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d6" +
|
|
"24c6c61548ab5fb8cd41022100fffffffffffffffffffffffffffffffebaaedc" +
|
|
"e6af48a03bbfd25e8cd0364141"),
|
|
err: ErrSigHighS,
|
|
}, {
|
|
name: "S > N (>32 bytes)",
|
|
sig: hexToBytes("304502204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d6" +
|
|
"24c6c61548ab5fb8cd4102210113500a0510b5094bff220c74656879b784b246" +
|
|
"ba89c0a07bc49bcf05d8993d44"),
|
|
err: ErrSigHighS,
|
|
}, {
|
|
name: "S > N",
|
|
sig: hexToBytes("304502204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d6" +
|
|
"24c6c61548ab5fb8cd41022100fffffffffffffffffffffffffffffffebaaedc" +
|
|
"e6af48a03bbfd25e8cd0364142"),
|
|
err: ErrSigHighS,
|
|
}}
|
|
|
|
for _, test := range tests {
|
|
err := CheckSignatureEncoding(test.sig)
|
|
if !errors.Is(err, test.err) {
|
|
t.Errorf("%s mismatched err -- got %v, want %v", test.name, err,
|
|
test.err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestCheckPubKeyEncoding ensures that checking strict public key encoding
|
|
// works as expected.
|
|
func TestCheckPubKeyEncoding(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
key []byte
|
|
err error
|
|
}{{
|
|
name: "uncompressed ok",
|
|
key: hexToBytes("04" +
|
|
"11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" +
|
|
"b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3"),
|
|
err: nil,
|
|
}, {
|
|
name: "compressed ok (ybit = 0)",
|
|
key: hexToBytes("02" +
|
|
"ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4d"),
|
|
err: nil,
|
|
}, {
|
|
name: "compressed ok (ybit = 1)",
|
|
key: hexToBytes("03" +
|
|
"2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e"),
|
|
err: nil,
|
|
}, {
|
|
// Although public keys not on the curve will ultimately be invalid, it
|
|
// is considered a valid encoding from the standpoint of preventing
|
|
// malleability.
|
|
name: "uncompressed x changed (not on curve)",
|
|
key: hexToBytes("04" +
|
|
"15db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" +
|
|
"b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3"),
|
|
err: nil,
|
|
}, {
|
|
// Although public keys not on the curve will ultimately be invalid, it
|
|
// is considered a valid encoding from the standpoint of preventing
|
|
// malleability.
|
|
name: "uncompressed y changed (not on curve)",
|
|
key: hexToBytes("04" +
|
|
"11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" +
|
|
"b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a4"),
|
|
err: nil,
|
|
}, {
|
|
name: "empty rejected",
|
|
key: nil,
|
|
err: ErrPubKeyType,
|
|
}, {
|
|
name: "hybrid rejected (ybit = 0)",
|
|
key: hexToBytes("06" +
|
|
"79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" +
|
|
"483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8"),
|
|
err: ErrPubKeyType,
|
|
}, {
|
|
name: "hybrid rejected (ybit = 1)",
|
|
key: hexToBytes("07" +
|
|
"11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" +
|
|
"b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3"),
|
|
err: ErrPubKeyType,
|
|
}, {
|
|
name: "uncompressed claims compressed rejected",
|
|
key: hexToBytes("03" +
|
|
"11db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c" +
|
|
"b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3"),
|
|
err: ErrPubKeyType,
|
|
}, {
|
|
name: "compressed claims uncompressed rejected (ybit = 0)",
|
|
key: hexToBytes("04" +
|
|
"ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4d"),
|
|
err: ErrPubKeyType,
|
|
}, {
|
|
name: "compressed claims uncompressed rejected (ybit = 1)",
|
|
key: hexToBytes("04" +
|
|
"2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e"),
|
|
err: ErrPubKeyType,
|
|
}, {
|
|
name: "compressed claims hybrid rejected (ybit = 0)",
|
|
key: hexToBytes("06" +
|
|
"ce0b14fb842b1ba549fdd675c98075f12e9c510f8ef52bd021a9a1f4809d3b4d"),
|
|
err: ErrPubKeyType,
|
|
}, {
|
|
name: "compressed claims hybrid rejected (ybit = 1)",
|
|
key: hexToBytes("07" +
|
|
"2689c7c2dab13309fb143e0e8fe396342521887e976690b6b47f5b2a4b7d448e"),
|
|
err: ErrPubKeyType,
|
|
}}
|
|
|
|
for _, test := range tests {
|
|
err := CheckPubKeyEncoding(test.key)
|
|
if !errors.Is(err, test.err) {
|
|
t.Errorf("%s mismatched err -- got %v, want %v", test.name, err,
|
|
test.err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestIsStrictNullData ensures the function that deals with strict null data
|
|
// requirements works as expected.
|
|
func TestIsStrictNullData(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
scriptVer uint16
|
|
script []byte
|
|
requiredLen uint32
|
|
want bool
|
|
}{{
|
|
name: "empty (bare OP_RETURN), req len 0",
|
|
scriptVer: 0,
|
|
script: mustParseShortFormV0("RETURN"),
|
|
requiredLen: 0,
|
|
want: true,
|
|
}, {
|
|
name: "empty (bare OP_RETURN), req len 0, unsupported script ver",
|
|
scriptVer: 65535,
|
|
script: mustParseShortFormV0("RETURN"),
|
|
requiredLen: 0,
|
|
want: false,
|
|
}, {
|
|
name: "small int push 0, req len 1",
|
|
scriptVer: 0,
|
|
script: mustParseShortFormV0("RETURN 0"),
|
|
requiredLen: 1,
|
|
want: true,
|
|
}, {
|
|
name: "small int push 0, req len 1, unsupported script ver",
|
|
scriptVer: 65535,
|
|
script: mustParseShortFormV0("RETURN 0"),
|
|
requiredLen: 1,
|
|
want: false,
|
|
}, {
|
|
name: "non-canonical small int push 0, req len 1",
|
|
scriptVer: 0,
|
|
script: mustParseShortFormV0("RETURN DATA_1 0x00"),
|
|
requiredLen: 1,
|
|
want: false,
|
|
}, {
|
|
name: "small int push 1, req len 1",
|
|
scriptVer: 0,
|
|
script: mustParseShortFormV0("RETURN 1"),
|
|
requiredLen: 1,
|
|
want: true,
|
|
}, {
|
|
name: "small int push 1, req len 1, unsupported script ver",
|
|
scriptVer: 65535,
|
|
script: mustParseShortFormV0("RETURN 1"),
|
|
requiredLen: 1,
|
|
want: false,
|
|
}, {
|
|
name: "small int push 16, req len 1",
|
|
scriptVer: 0,
|
|
script: mustParseShortFormV0("RETURN 16"),
|
|
requiredLen: 1,
|
|
want: true,
|
|
}, {
|
|
name: "small int push 16, req len 1, unsupported script ver",
|
|
scriptVer: 65535,
|
|
script: mustParseShortFormV0("RETURN 16"),
|
|
requiredLen: 1,
|
|
want: false,
|
|
}, {
|
|
name: "small int push 0, req len 2",
|
|
scriptVer: 0,
|
|
script: mustParseShortFormV0("RETURN 0"),
|
|
requiredLen: 2,
|
|
want: false,
|
|
}, {
|
|
name: "small int push 1, req len 2",
|
|
scriptVer: 0,
|
|
script: mustParseShortFormV0("RETURN 1"),
|
|
requiredLen: 2,
|
|
want: false,
|
|
}, {
|
|
name: "small int push 16, req len 2",
|
|
scriptVer: 0,
|
|
script: mustParseShortFormV0("RETURN 16"),
|
|
requiredLen: 2,
|
|
want: false,
|
|
}, {
|
|
name: "32-byte push, req len 32",
|
|
scriptVer: 0,
|
|
script: mustParseShortFormV0("RETURN DATA_32 0x0102030405060708090a0b0c" +
|
|
"0d0e0f101112131415161718191a1b1c1d1e1f20"),
|
|
requiredLen: 32,
|
|
want: true,
|
|
}, {
|
|
name: "32-byte push, req len 32, unsupported script ver",
|
|
scriptVer: 65535,
|
|
script: mustParseShortFormV0("RETURN DATA_32 0x0102030405060708090a0b0c" +
|
|
"0d0e0f101112131415161718191a1b1c1d1e1f20"),
|
|
requiredLen: 32,
|
|
want: false,
|
|
}, {
|
|
name: "32-byte push, req len 31",
|
|
scriptVer: 0,
|
|
script: mustParseShortFormV0("RETURN DATA_32 0x0102030405060708090a0b0c" +
|
|
"0d0e0f101112131415161718191a1b1c1d1e1f20"),
|
|
requiredLen: 31,
|
|
want: false,
|
|
}, {
|
|
name: "32-byte push, req len 33",
|
|
scriptVer: 0,
|
|
script: mustParseShortFormV0("RETURN DATA_32 0x0102030405060708090a0b0c" +
|
|
"0d0e0f101112131415161718191a1b1c1d1e1f20"),
|
|
requiredLen: 33,
|
|
want: false,
|
|
}, {
|
|
name: "32-byte push, req len 32, no leading OP_RETURN",
|
|
scriptVer: 0,
|
|
script: mustParseShortFormV0("DATA_32 0x0102030405060708090a0b0c0d0e0f" +
|
|
"101112131415161718191a1b1c1d1e1f20"),
|
|
requiredLen: 32,
|
|
want: false,
|
|
}, {
|
|
name: "non-canonical 32-byte push via PUSHDATA1, req len 32",
|
|
scriptVer: 0,
|
|
script: mustParseShortFormV0("RETURN PUSHDATA1 0x20 0x0102030405060708" +
|
|
"090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"),
|
|
requiredLen: 32,
|
|
want: false,
|
|
}, {
|
|
name: "non-canonical 32-byte push via PUSHDATA2, req len 32",
|
|
scriptVer: 0,
|
|
script: mustParseShortFormV0("RETURN PUSHDATA2 0x2000 0x01020304050607" +
|
|
"08090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"),
|
|
requiredLen: 32,
|
|
want: false,
|
|
}, {
|
|
name: "non-canonical 32-byte push via PUSHDATA4, req len 32",
|
|
scriptVer: 0,
|
|
script: mustParseShortFormV0("RETURN PUSHDATA4 0x20000000 0x0102030405" +
|
|
"060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"),
|
|
requiredLen: 32,
|
|
want: false,
|
|
}, {
|
|
name: "76-byte push, req len 76",
|
|
scriptVer: 0,
|
|
script: mustParseShortFormV0("RETURN PUSHDATA1 0x4c 0x0102030405060708" +
|
|
"090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728" +
|
|
"292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748" +
|
|
"494a4b4c"),
|
|
requiredLen: 76,
|
|
want: false,
|
|
}}
|
|
|
|
for _, test := range tests {
|
|
// Ensure the test data scripts are well formed.
|
|
if err := checkScriptParses(0, test.script); err != nil {
|
|
t.Errorf("%s: unexpected script parse failure: %v", test.name, err)
|
|
continue
|
|
}
|
|
|
|
// Ensure the result is as expected.
|
|
got := IsStrictNullData(test.scriptVer, test.script, test.requiredLen)
|
|
if got != test.want {
|
|
t.Errorf("%s: mismatched result -- got %v, want %v", test.name, got,
|
|
test.want)
|
|
}
|
|
}
|
|
}
|