btcec: Avoid panic in fieldVal.SetByteSlice for large inputs
The implementation has been adapted from the dcrec module in dcrd. The bug was initially fixed in decred/dcrd@3d9cda1 while transitioning to a constant time algorithm. A large set of test vectors were subsequently added in decred/dcrd@8c6b52d. The function signature has been preserved for backwards compatibility. This means that returning whether the value has overflowed, and the corresponding test vectors have not been backported. This fixes #1170 and closes a previous attempt to fix the bug in #1178.
This commit is contained in:
parent
875b51c9fb
commit
d28c7167a5
2 changed files with 166 additions and 8 deletions
|
@ -226,20 +226,24 @@ func (f *fieldVal) SetBytes(b *[32]byte) *fieldVal {
|
|||
return f
|
||||
}
|
||||
|
||||
// SetByteSlice packs the passed big-endian value into the internal field value
|
||||
// representation. Only the first 32-bytes are used. As a result, it is up to
|
||||
// the caller to ensure numbers of the appropriate size are used or the value
|
||||
// will be truncated.
|
||||
// SetByteSlice interprets the provided slice as a 256-bit big-endian unsigned
|
||||
// integer (meaning it is truncated to the first 32 bytes), packs it into the
|
||||
// internal field value representation, and returns the updated field value.
|
||||
//
|
||||
// Note that since passing a slice with more than 32 bytes is truncated, it is
|
||||
// possible that the truncated value is less than the field prime. It is up to
|
||||
// the caller to decide whether it needs to provide numbers of the appropriate
|
||||
// size or if it is acceptable to use this function with the described
|
||||
// truncation behavior.
|
||||
//
|
||||
// The field value is returned to support chaining. This enables syntax like:
|
||||
// f := new(fieldVal).SetByteSlice(byteSlice)
|
||||
func (f *fieldVal) SetByteSlice(b []byte) *fieldVal {
|
||||
var b32 [32]byte
|
||||
for i := 0; i < len(b); i++ {
|
||||
if i < 32 {
|
||||
b32[i+(32-len(b))] = b[i]
|
||||
}
|
||||
if len(b) > 32 {
|
||||
b = b[:32]
|
||||
}
|
||||
copy(b32[32-len(b):], b)
|
||||
return f.SetBytes(&b32)
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ package btcec
|
|||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
@ -965,3 +966,156 @@ func testSqrt(t *testing.T, test sqrtTest) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestFieldSetBytes ensures that setting a field value to a 256-bit big-endian
|
||||
// unsigned integer via both the slice and array methods works as expected for
|
||||
// edge cases. Random cases are tested via the various other tests.
|
||||
func TestFieldSetBytes(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string // test description
|
||||
in string // hex encoded test value
|
||||
expected [10]uint32 // expected raw ints
|
||||
}{{
|
||||
name: "zero",
|
||||
in: "00",
|
||||
expected: [10]uint32{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
}, {
|
||||
name: "field prime",
|
||||
in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f",
|
||||
expected: [10]uint32{
|
||||
0x03fffc2f, 0x03ffffbf, 0x03ffffff, 0x03ffffff, 0x03ffffff,
|
||||
0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x003fffff,
|
||||
},
|
||||
}, {
|
||||
name: "field prime - 1",
|
||||
in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e",
|
||||
expected: [10]uint32{
|
||||
0x03fffc2e, 0x03ffffbf, 0x03ffffff, 0x03ffffff, 0x03ffffff,
|
||||
0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x003fffff,
|
||||
},
|
||||
}, {
|
||||
name: "field prime + 1 (overflow in word zero)",
|
||||
in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30",
|
||||
expected: [10]uint32{
|
||||
0x03fffc30, 0x03ffffbf, 0x03ffffff, 0x03ffffff, 0x03ffffff,
|
||||
0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x003fffff,
|
||||
},
|
||||
}, {
|
||||
name: "field prime first 32 bits",
|
||||
in: "fffffc2f",
|
||||
expected: [10]uint32{
|
||||
0x03fffc2f, 0x00000003f, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
},
|
||||
}, {
|
||||
name: "field prime word zero",
|
||||
in: "03fffc2f",
|
||||
expected: [10]uint32{
|
||||
0x03fffc2f, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
},
|
||||
}, {
|
||||
name: "field prime first 64 bits",
|
||||
in: "fffffffefffffc2f",
|
||||
expected: [10]uint32{
|
||||
0x03fffc2f, 0x03ffffbf, 0x00000fff, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
},
|
||||
}, {
|
||||
name: "field prime word zero and one",
|
||||
in: "0ffffefffffc2f",
|
||||
expected: [10]uint32{
|
||||
0x03fffc2f, 0x03ffffbf, 0x00000000, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
},
|
||||
}, {
|
||||
name: "field prime first 96 bits",
|
||||
in: "fffffffffffffffefffffc2f",
|
||||
expected: [10]uint32{
|
||||
0x03fffc2f, 0x03ffffbf, 0x03ffffff, 0x0003ffff, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
},
|
||||
}, {
|
||||
name: "field prime word zero, one, and two",
|
||||
in: "3ffffffffffefffffc2f",
|
||||
expected: [10]uint32{
|
||||
0x03fffc2f, 0x03ffffbf, 0x03ffffff, 0x00000000, 0x00000000,
|
||||
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
||||
},
|
||||
}, {
|
||||
name: "overflow in word one (prime + 1<<26)",
|
||||
in: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff03fffc2f",
|
||||
expected: [10]uint32{
|
||||
0x03fffc2f, 0x03ffffc0, 0x03ffffff, 0x03ffffff, 0x03ffffff,
|
||||
0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x003fffff,
|
||||
},
|
||||
}, {
|
||||
name: "(field prime - 1) * 2 NOT mod P, truncated >32 bytes",
|
||||
in: "01fffffffffffffffffffffffffffffffffffffffffffffffffffffffdfffff85c",
|
||||
expected: [10]uint32{
|
||||
0x01fffff8, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff,
|
||||
0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x00007fff,
|
||||
},
|
||||
}, {
|
||||
name: "2^256 - 1",
|
||||
in: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
expected: [10]uint32{
|
||||
0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff,
|
||||
0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x003fffff,
|
||||
},
|
||||
}, {
|
||||
name: "alternating bits",
|
||||
in: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5",
|
||||
expected: [10]uint32{
|
||||
0x01a5a5a5, 0x01696969, 0x025a5a5a, 0x02969696, 0x01a5a5a5,
|
||||
0x01696969, 0x025a5a5a, 0x02969696, 0x01a5a5a5, 0x00296969,
|
||||
},
|
||||
}, {
|
||||
name: "alternating bits 2",
|
||||
in: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a",
|
||||
expected: [10]uint32{
|
||||
0x025a5a5a, 0x02969696, 0x01a5a5a5, 0x01696969, 0x025a5a5a,
|
||||
0x02969696, 0x01a5a5a5, 0x01696969, 0x025a5a5a, 0x00169696,
|
||||
},
|
||||
}}
|
||||
|
||||
for _, test := range tests {
|
||||
inBytes := hexToBytes(test.in)
|
||||
|
||||
// Ensure setting the bytes via the slice method works as expected.
|
||||
var f fieldVal
|
||||
f.SetByteSlice(inBytes)
|
||||
if !reflect.DeepEqual(f.n, test.expected) {
|
||||
t.Errorf("%s: unexpected result\ngot: %x\nwant: %x", test.name, f.n,
|
||||
test.expected)
|
||||
continue
|
||||
}
|
||||
|
||||
// Ensure setting the bytes via the array method works as expected.
|
||||
var f2 fieldVal
|
||||
var b32 [32]byte
|
||||
truncatedInBytes := inBytes
|
||||
if len(truncatedInBytes) > 32 {
|
||||
truncatedInBytes = truncatedInBytes[:32]
|
||||
}
|
||||
copy(b32[32-len(truncatedInBytes):], truncatedInBytes)
|
||||
f2.SetBytes(&b32)
|
||||
if !reflect.DeepEqual(f2.n, test.expected) {
|
||||
t.Errorf("%s: unexpected result\ngot: %x\nwant: %x", test.name,
|
||||
f2.n, test.expected)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// hexToBytes converts the passed hex string into bytes and will panic if there
|
||||
// is an error. This is only provided for the hard-coded constants so errors in
|
||||
// the source code can be detected. It will only (and must only) be called with
|
||||
// hard-coded values.
|
||||
func hexToBytes(s string) []byte {
|
||||
b, err := hex.DecodeString(s)
|
||||
if err != nil {
|
||||
panic("invalid hex in source file: " + s)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue