Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

secp256k1/ecdsa: update error types. #2281

Merged
merged 1 commit into from
Dec 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 35 additions & 108 deletions dcrec/secp256k1/ecdsa/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,155 +4,99 @@

package ecdsa

import (
"fmt"
)

// ErrorCode identifies a kind of signature error. It has full support
// for errors.Is and errors.As, so the caller can directly check against an
// error code when determining the reason for an error.
type ErrorCode int
// ErrorKind identifies a kind of error. It has full support for
// errors.Is and errors.As, so the caller can directly check against
// an error kind when determining the reason for an error.
type ErrorKind string

// These constants are used to identify a specific Error.
const (
// ErrSigTooShort is returned when a signature that should be a DER
// signature is too short.
ErrSigTooShort ErrorCode = iota
ErrSigTooShort = ErrorKind("ErrSigTooShort")

// ErrSigTooLong is returned when a signature that should be a DER signature
// is too long.
ErrSigTooLong
ErrSigTooLong = ErrorKind("ErrSigTooLong")

// ErrSigInvalidSeqID is returned when a signature that should be a DER
// signature does not have the expected ASN.1 sequence ID.
ErrSigInvalidSeqID
ErrSigInvalidSeqID = ErrorKind("ErrSigInvalidSeqID")

// ErrSigInvalidDataLen is returned when a signature that should be a DER
// signature does not specify the correct number of remaining bytes for the
// R and S portions.
ErrSigInvalidDataLen
ErrSigInvalidDataLen = ErrorKind("ErrSigInvalidDataLen")

// ErrSigMissingSTypeID is returned when a signature that should be a DER
// signature does not provide the ASN.1 type ID for S.
ErrSigMissingSTypeID
ErrSigMissingSTypeID = ErrorKind("ErrSigMissingSTypeID")

// ErrSigMissingSLen is returned when a signature that should be a DER
// signature does not provide the length of S.
ErrSigMissingSLen
ErrSigMissingSLen = ErrorKind("ErrSigMissingSLen")

// ErrSigInvalidSLen is returned when a signature that should be a DER
// signature does not specify the correct number of bytes for the S portion.
ErrSigInvalidSLen
ErrSigInvalidSLen = ErrorKind("ErrSigInvalidSLen")

// ErrSigInvalidRIntID is returned when a signature that should be a DER
// signature does not have the expected ASN.1 integer ID for R.
ErrSigInvalidRIntID
ErrSigInvalidRIntID = ErrorKind("ErrSigInvalidRIntID")

// ErrSigZeroRLen is returned when a signature that should be a DER
// signature has an R length of zero.
ErrSigZeroRLen
ErrSigZeroRLen = ErrorKind("ErrSigZeroRLen")

// ErrSigNegativeR is returned when a signature that should be a DER
// signature has a negative value for R.
ErrSigNegativeR
ErrSigNegativeR = ErrorKind("ErrSigNegativeR")

// ErrSigTooMuchRPadding is returned when a signature that should be a DER
// signature has too much padding for R.
ErrSigTooMuchRPadding
ErrSigTooMuchRPadding = ErrorKind("ErrSigTooMuchRPadding")

// ErrSigRIsZero is returned when a signature has R set to the value zero.
ErrSigRIsZero
ErrSigRIsZero = ErrorKind("ErrSigRIsZero")

// ErrSigRTooBig is returned when a signature has R with a value that is
// greater than or equal to the group order.
ErrSigRTooBig
ErrSigRTooBig = ErrorKind("ErrSigRTooBig")

// ErrSigInvalidSIntID is returned when a signature that should be a DER
// signature does not have the expected ASN.1 integer ID for S.
ErrSigInvalidSIntID
ErrSigInvalidSIntID = ErrorKind("ErrSigInvalidSIntID")

// ErrSigZeroSLen is returned when a signature that should be a DER
// signature has an S length of zero.
ErrSigZeroSLen
ErrSigZeroSLen = ErrorKind("ErrSigZeroSLen")

// ErrSigNegativeS is returned when a signature that should be a DER
// signature has a negative value for S.
ErrSigNegativeS
ErrSigNegativeS = ErrorKind("ErrSigNegativeS")

// ErrSigTooMuchSPadding is returned when a signature that should be a DER
// signature has too much padding for S.
ErrSigTooMuchSPadding
ErrSigTooMuchSPadding = ErrorKind("ErrSigTooMuchSPadding")

// ErrSigSIsZero is returned when a signature has S set to the value zero.
ErrSigSIsZero
ErrSigSIsZero = ErrorKind("ErrSigSIsZero")

// ErrSigSTooBig is returned when a signature has S with a value that is
// greater than or equal to the group order.
ErrSigSTooBig

// numSigErrorCodes is the maximum error code number used in tests. This
// entry MUST be the last entry in the enum.
numSigErrorCodes
ErrSigSTooBig = ErrorKind("ErrSigSTooBig")
)

// Map of ErrorCode values back to their constant names for pretty printing.
var errorCodeStrings = map[ErrorCode]string{
ErrSigTooShort: "ErrSigTooShort",
ErrSigTooLong: "ErrSigTooLong",
ErrSigInvalidSeqID: "ErrSigInvalidSeqID",
ErrSigInvalidDataLen: "ErrSigInvalidDataLen",
ErrSigMissingSTypeID: "ErrSigMissingSTypeID",
ErrSigMissingSLen: "ErrSigMissingSLen",
ErrSigInvalidSLen: "ErrSigInvalidSLen",
ErrSigInvalidRIntID: "ErrSigInvalidRIntID",
ErrSigZeroRLen: "ErrSigZeroRLen",
ErrSigNegativeR: "ErrSigNegativeR",
ErrSigTooMuchRPadding: "ErrSigTooMuchRPadding",
ErrSigRIsZero: "ErrSigRIsZero",
ErrSigRTooBig: "ErrSigRTooBig",
ErrSigInvalidSIntID: "ErrSigInvalidSIntID",
ErrSigZeroSLen: "ErrSigZeroSLen",
ErrSigNegativeS: "ErrSigNegativeS",
ErrSigTooMuchSPadding: "ErrSigTooMuchSPadding",
ErrSigSIsZero: "ErrSigSIsZero",
ErrSigSTooBig: "ErrSigSTooBig",
}

// String returns the ErrorCode as a human-readable name.
func (e ErrorCode) String() string {
if s := errorCodeStrings[e]; s != "" {
return s
}
return fmt.Sprintf("Unknown ErrorCode (%d)", int(e))
}

// Error implements the error interface.
func (e ErrorCode) Error() string {
return e.String()
}

// Is implements the interface to work with the standard library's errors.Is.
//
// It returns true in the following cases:
// - The target is a Error and the error codes match
// - The target is a ErrorCode and the error codes match
func (e ErrorCode) Is(target error) bool {
switch target := target.(type) {
case Error:
return e == target.ErrorCode

case ErrorCode:
return e == target
}

return false
// Error satisfies the error interface and prints human-readable errors.
func (e ErrorKind) Error() string {
return string(e)
}

// Error identifies a signature-related error. It has full support for
// errors.Is and errors.As, so the caller can ascertain the specific reason for
// the error by checking the underlying error code.
// Error identifies an error related to an ECDSA signature. It has full
// support for errors.Is and errors.As, so the caller can ascertain the
// specific reason for the error by checking the underlying error.
type Error struct {
ErrorCode ErrorCode
Err error
Description string
}

Expand All @@ -161,29 +105,12 @@ func (e Error) Error() string {
return e.Description
}

// Is implements the interface to work with the standard library's errors.Is.
//
// It returns true in the following cases:
// - The target is a Error and the error codes match
// - The target is a ErrorCode and it the error codes match
func (e Error) Is(target error) bool {
switch target := target.(type) {
case Error:
return e.ErrorCode == target.ErrorCode

case ErrorCode:
return target == e.ErrorCode
}

return false
}

// Unwrap returns the underlying wrapped error code.
// Unwrap returns the underlying wrapped error.
func (e Error) Unwrap() error {
return e.ErrorCode
return e.Err
}

// signatureError creates a Error given a set of arguments.
func signatureError(c ErrorCode, desc string) Error {
return Error{ErrorCode: c, Description: desc}
// signatureError creates an Error given a set of arguments.
func signatureError(kind ErrorKind, desc string) Error {
return Error{Err: kind, Description: desc}
}
41 changes: 14 additions & 27 deletions dcrec/secp256k1/ecdsa/error_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import (
"testing"
)

// TestErrorCodeStringer tests the stringized output for the ErrorCode type.
// TestErrorKindStringer tests the stringized output for the ErrorKind type.
func TestErrorCodeStringer(t *testing.T) {
tests := []struct {
in ErrorCode
in ErrorKind
want string
}{
{ErrSigTooShort, "ErrSigTooShort"},
Expand All @@ -34,17 +34,10 @@ func TestErrorCodeStringer(t *testing.T) {
{ErrSigTooMuchSPadding, "ErrSigTooMuchSPadding"},
{ErrSigSIsZero, "ErrSigSIsZero"},
{ErrSigSTooBig, "ErrSigSTooBig"},
{0xffff, "Unknown ErrorCode (65535)"},
}

// Detect additional error codes that don't have the stringer added.
if len(tests)-1 != int(numSigErrorCodes) {
t.Errorf("It appears a signature error code was added without adding " +
"an associated stringer test")
}

for i, test := range tests {
result := test.in.String()
result := test.in.Error()
if result != test.want {
t.Errorf("#%d: got: %s want: %s", i, result, test.want)
continue
Expand Down Expand Up @@ -74,15 +67,15 @@ func TestError(t *testing.T) {
}
}

// TestErrorCodeIsAs ensures both ErrorCode and Error can be identified as being
// a specific error code via errors.Is and unwrapped via errors.As.
func TestErrorCodeIsAs(t *testing.T) {
// TestErrorKindIsAs ensures both ErrorKind and Error can be identified as being
// a specific error kind via errors.Is and unwrapped via errors.As.
func TestErrorKindIsAs(t *testing.T) {
tests := []struct {
name string
err error
target error
wantMatch bool
wantAs ErrorCode
wantAs ErrorKind
}{{
name: "ErrSigTooShort == ErrSigTooShort",
err: ErrSigTooShort,
Expand All @@ -95,12 +88,6 @@ func TestErrorCodeIsAs(t *testing.T) {
target: ErrSigTooShort,
wantMatch: true,
wantAs: ErrSigTooShort,
}, {
name: "ErrSigTooShort == Error.ErrSigTooShort",
err: ErrSigTooShort,
target: signatureError(ErrSigTooShort, ""),
wantMatch: true,
wantAs: ErrSigTooShort,
}, {
name: "Error.ErrSigTooShort == Error.ErrSigTooShort",
err: signatureError(ErrSigTooShort, ""),
Expand Down Expand Up @@ -142,16 +129,16 @@ func TestErrorCodeIsAs(t *testing.T) {
continue
}

// Ensure the underlying error code can be unwrapped is and is the
// Ensure the underlying error kind can be unwrapped is and is the
// expected code.
var code ErrorCode
if !errors.As(test.err, &code) {
t.Errorf("%s: unable to unwrap to error code", test.name)
var kind ErrorKind
if !errors.As(test.err, &kind) {
t.Errorf("%s: unable to unwrap to error", test.name)
continue
}
if code != test.wantAs {
t.Errorf("%s: unexpected unwrapped error code -- got %v, want %v",
test.name, code, test.wantAs)
if !errors.Is(kind, test.wantAs) {
t.Errorf("%s: unexpected unwrapped error -- got %v, want %v",
test.name, kind, test.wantAs)
continue
}
}
Expand Down