From f3b7dcc5bdd5b2b02e12133a0d8e16a75844f766 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 22 Feb 2017 17:35:11 +0100 Subject: common/hexutil: reject big integer inputs > 256 bits This follows the change to common/math big integer parsing in PR #3699. --- common/hexutil/json.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'common/hexutil/json.go') diff --git a/common/hexutil/json.go b/common/hexutil/json.go index 7e4736dd6..6174d6256 100644 --- a/common/hexutil/json.go +++ b/common/hexutil/json.go @@ -91,9 +91,12 @@ func UnmarshalJSON(typname string, input, out []byte) error { return nil } -// Big marshals/unmarshals as a JSON string with 0x prefix. The zero value marshals as -// "0x0". Negative integers are not supported at this time. Attempting to marshal them -// will return an error. +// Big marshals/unmarshals as a JSON string with 0x prefix. +// The zero value marshals as "0x0". +// +// Negative integers are not supported at this time. Attempting to marshal them will +// return an error. Values larger than 256bits are rejected by Unmarshal but will be +// marshaled without error. type Big big.Int // MarshalJSON implements json.Marshaler. @@ -119,6 +122,9 @@ func (b *Big) UnmarshalJSON(input []byte) error { if err != nil { return err } + if len(raw) > 64 { + return ErrBig256Range + } words := make([]big.Word, len(raw)/bigWordNibbles+1) end := len(raw) for i := range words { -- cgit v1.2.3 From d304da380382245190f33878363696cd151201a9 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 22 Feb 2017 17:59:59 +0100 Subject: common/hexutil: implement TextMarshaler, TextUnmarshaler This commit makes the wrapper types more generally applicable. encoding.TextMarshaler is supported by most codec implementations (e.g. for yaml). The tests now ensure that package json actually recognizes the custom marshaler implementation irrespective of how it is implemented. The Uint type has new tests, too. These are tricky because uint size depends on the CPU word size. Turns out that there was one incorrect case where decoding returned ErrUint64Range instead of ErrUintRange. --- common/hexutil/json.go | 123 +++++++++++++++++++++++++++---------------------- 1 file changed, 69 insertions(+), 54 deletions(-) (limited to 'common/hexutil/json.go') diff --git a/common/hexutil/json.go b/common/hexutil/json.go index 6174d6256..ce6a2b048 100644 --- a/common/hexutil/json.go +++ b/common/hexutil/json.go @@ -25,8 +25,7 @@ import ( ) var ( - jsonNull = []byte("null") - jsonZero = []byte(`"0x0"`) + textZero = []byte(`0x0`) errNonString = errors.New("cannot unmarshal non-string as hex data") errNegativeBigInt = errors.New("hexutil.Big: can't marshal negative integer") ) @@ -35,18 +34,25 @@ var ( // The empty slice marshals as "0x". type Bytes []byte -// MarshalJSON implements json.Marshaler. -func (b Bytes) MarshalJSON() ([]byte, error) { - result := make([]byte, len(b)*2+4) - copy(result, `"0x`) - hex.Encode(result[3:], b) - result[len(result)-1] = '"' +// MarshalText implements encoding.TextMarshaler +func (b Bytes) MarshalText() ([]byte, error) { + result := make([]byte, len(b)*2+2) + copy(result, `0x`) + hex.Encode(result[2:], b) return result, nil } // UnmarshalJSON implements json.Unmarshaler. func (b *Bytes) UnmarshalJSON(input []byte) error { - raw, err := checkJSON(input) + if !isString(input) { + return errNonString + } + return b.UnmarshalText(input[1 : len(input)-1]) +} + +// UnmarshalText implements encoding.TextUnmarshaler. +func (b *Bytes) UnmarshalText(input []byte) error { + raw, err := checkText(input) if err != nil { return err } @@ -64,17 +70,11 @@ func (b Bytes) String() string { return Encode(b) } -// UnmarshalJSON decodes input as a JSON string with 0x prefix. The length of out +// UnmarshalFixedText decodes the input as a string with 0x prefix. The length of out // determines the required input length. This function is commonly used to implement the -// UnmarshalJSON method for fixed-size types: -// -// type Foo [8]byte -// -// func (f *Foo) UnmarshalJSON(input []byte) error { -// return hexutil.UnmarshalJSON("Foo", input, f[:]) -// } -func UnmarshalJSON(typname string, input, out []byte) error { - raw, err := checkJSON(input) +// UnmarshalText method for fixed-size types. +func UnmarshalFixedText(typname string, input, out []byte) error { + raw, err := checkText(input) if err != nil { return err } @@ -99,26 +99,33 @@ func UnmarshalJSON(typname string, input, out []byte) error { // marshaled without error. type Big big.Int -// MarshalJSON implements json.Marshaler. -func (b *Big) MarshalJSON() ([]byte, error) { - if b == nil { - return jsonNull, nil - } - bigint := (*big.Int)(b) +// MarshalText implements encoding.TextMarshaler +func (b Big) MarshalText() ([]byte, error) { + bigint := (big.Int)(b) if bigint.Sign() == -1 { return nil, errNegativeBigInt } nbits := bigint.BitLen() if nbits == 0 { - return jsonZero, nil + return textZero, nil } - enc := fmt.Sprintf(`"0x%x"`, bigint) - return []byte(enc), nil + enc := make([]byte, 2, nbits/4+2) + copy(enc, "0x") + enc = bigint.Append(enc, 16) + return enc, nil } // UnmarshalJSON implements json.Unmarshaler. func (b *Big) UnmarshalJSON(input []byte) error { - raw, err := checkNumberJSON(input) + if !isString(input) { + return errNonString + } + return b.UnmarshalText(input[1 : len(input)-1]) +} + +// UnmarshalText implements encoding.TextUnmarshaler +func (b *Big) UnmarshalText(input []byte) error { + raw, err := checkNumberText(input) if err != nil { return err } @@ -162,18 +169,25 @@ func (b *Big) String() string { // The zero value marshals as "0x0". type Uint64 uint64 -// MarshalJSON implements json.Marshaler. -func (b Uint64) MarshalJSON() ([]byte, error) { - buf := make([]byte, 3, 12) - copy(buf, `"0x`) +// MarshalText implements encoding.TextMarshaler. +func (b Uint64) MarshalText() ([]byte, error) { + buf := make([]byte, 2, 10) + copy(buf, `0x`) buf = strconv.AppendUint(buf, uint64(b), 16) - buf = append(buf, '"') return buf, nil } // UnmarshalJSON implements json.Unmarshaler. func (b *Uint64) UnmarshalJSON(input []byte) error { - raw, err := checkNumberJSON(input) + if !isString(input) { + return errNonString + } + return b.UnmarshalText(input[1 : len(input)-1]) +} + +// UnmarshalText implements encoding.TextUnmarshaler +func (b *Uint64) UnmarshalText(input []byte) error { + raw, err := checkNumberText(input) if err != nil { return err } @@ -202,19 +216,27 @@ func (b Uint64) String() string { // The zero value marshals as "0x0". type Uint uint -// MarshalJSON implements json.Marshaler. -func (b Uint) MarshalJSON() ([]byte, error) { - return Uint64(b).MarshalJSON() +// MarshalText implements encoding.TextMarshaler. +func (b Uint) MarshalText() ([]byte, error) { + return Uint64(b).MarshalText() } // UnmarshalJSON implements json.Unmarshaler. func (b *Uint) UnmarshalJSON(input []byte) error { + if !isString(input) { + return errNonString + } + return b.UnmarshalText(input[1 : len(input)-1]) +} + +// UnmarshalText implements encoding.TextUnmarshaler. +func (b *Uint) UnmarshalText(input []byte) error { var u64 Uint64 - err := u64.UnmarshalJSON(input) - if err != nil { - return err - } else if u64 > Uint64(^uint(0)) { + err := u64.UnmarshalText(input) + if u64 > Uint64(^uint(0)) || err == ErrUint64Range { return ErrUintRange + } else if err != nil { + return err } *b = Uint(u64) return nil @@ -233,28 +255,21 @@ func bytesHave0xPrefix(input []byte) bool { return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X') } -func checkJSON(input []byte) (raw []byte, err error) { - if !isString(input) { - return nil, errNonString - } - if len(input) == 2 { +func checkText(input []byte) ([]byte, error) { + if len(input) == 0 { return nil, nil // empty strings are allowed } - if !bytesHave0xPrefix(input[1:]) { + if !bytesHave0xPrefix(input) { return nil, ErrMissingPrefix } - input = input[3 : len(input)-1] + input = input[2:] if len(input)%2 != 0 { return nil, ErrOddLength } return input, nil } -func checkNumberJSON(input []byte) (raw []byte, err error) { - if !isString(input) { - return nil, errNonString - } - input = input[1 : len(input)-1] +func checkNumberText(input []byte) (raw []byte, err error) { if len(input) == 0 { return nil, nil // empty strings are allowed } -- cgit v1.2.3 From 280f08be84325924ea7628a8187fd84e7cc39145 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Sun, 26 Feb 2017 19:40:33 +0100 Subject: common/hexutil: ensure negative big.Int is encoded sensibly Restricting encoding is silly. --- common/hexutil/json.go | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) (limited to 'common/hexutil/json.go') diff --git a/common/hexutil/json.go b/common/hexutil/json.go index ce6a2b048..23393ed2c 100644 --- a/common/hexutil/json.go +++ b/common/hexutil/json.go @@ -25,9 +25,8 @@ import ( ) var ( - textZero = []byte(`0x0`) - errNonString = errors.New("cannot unmarshal non-string as hex data") - errNegativeBigInt = errors.New("hexutil.Big: can't marshal negative integer") + textZero = []byte(`0x0`) + errNonString = errors.New("cannot unmarshal non-string as hex data") ) // Bytes marshals/unmarshals as a JSON string with 0x prefix. @@ -101,18 +100,7 @@ type Big big.Int // MarshalText implements encoding.TextMarshaler func (b Big) MarshalText() ([]byte, error) { - bigint := (big.Int)(b) - if bigint.Sign() == -1 { - return nil, errNegativeBigInt - } - nbits := bigint.BitLen() - if nbits == 0 { - return textZero, nil - } - enc := make([]byte, 2, nbits/4+2) - copy(enc, "0x") - enc = bigint.Append(enc, 16) - return enc, nil + return []byte(EncodeBig((*big.Int)(&b))), nil } // UnmarshalJSON implements json.Unmarshaler. -- cgit v1.2.3