Skip to content

Commit

Permalink
feat: refactor errors
Browse files Browse the repository at this point in the history
  • Loading branch information
james-d-elliott committed Jan 25, 2025
1 parent 961325b commit e582e2c
Show file tree
Hide file tree
Showing 18 changed files with 85 additions and 71 deletions.
6 changes: 3 additions & 3 deletions metadata/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ const (
)

var (
errIntermediateCertRevoked = &MetadataError{
errIntermediateCertRevoked = &Error{
Type: "intermediate_revoked",
Details: "Intermediate certificate is on issuers revocation list",
}
errLeafCertRevoked = &MetadataError{
errLeafCertRevoked = &Error{
Type: "leaf_revoked",
Details: "Leaf certificate is on issuers revocation list",
}
errCRLUnavailable = &MetadataError{
errCRLUnavailable = &Error{
Type: "crl_unavailable",
Details: "Certificate revocation list is unavailable",
}
Expand Down
2 changes: 1 addition & 1 deletion metadata/metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func TestConformanceMetadataTOCParsing(t *testing.T) {
var (
res *http.Response
blob *PayloadJSON
me *MetadataError
me *Error
)

for _, endpoint := range endpoints {
Expand Down
6 changes: 3 additions & 3 deletions metadata/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,17 @@ func ValidateStatusReports(reports []StatusReport, desired, undesired []Authenti
case len(present) == 0 && len(absent) == 0:
return nil
case len(present) != 0 && len(absent) == 0:
return &MetadataError{
return &Error{
Type: "invalid_status",
Details: fmt.Sprintf("The following undesired status reports were present: %s", strings.Join(present, ", ")),
}
case len(present) == 0 && len(absent) != 0:
return &MetadataError{
return &Error{
Type: "invalid_status",
Details: fmt.Sprintf("The following desired status reports were absent: %s", strings.Join(absent, ", ")),
}
default:
return &MetadataError{
return &Error{
Type: "invalid_status",
Details: fmt.Sprintf("The following undesired status reports were present: %s; the following desired status reports were absent: %s", strings.Join(present, ", "), strings.Join(absent, ", ")),
}
Expand Down
6 changes: 3 additions & 3 deletions metadata/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ const (
ALG_KEY_COSE PublicKeyAlgAndEncoding = "cose"
)

type MetadataError struct {
type Error struct {
// Short name for the type of error that has occurred.
Type string `json:"type"`

Expand All @@ -310,8 +310,8 @@ type MetadataError struct {
DevInfo string `json:"debug"`
}

func (err *MetadataError) Error() string {
return err.Details
func (e *Error) Error() string {
return e.Details
}

// Clock is an interface used to implement clock functionality in various metadata areas.
Expand Down
12 changes: 6 additions & 6 deletions protocol/assertion.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func ParseCredentialRequestResponseBody(body io.Reader) (par *ParsedCredentialAs
var car CredentialAssertionResponse

if err = decodeBody(body, &car); err != nil {
return nil, ErrBadRequest.WithDetails("Parse error for Assertion").WithInfo(err.Error())
return nil, ErrBadRequest.WithDetails("Parse error for Assertion").WithInfo(err.Error()).WithError(err)
}

return car.Parse()
Expand All @@ -81,7 +81,7 @@ func ParseCredentialRequestResponseBytes(data []byte) (par *ParsedCredentialAsse
var car CredentialAssertionResponse

if err = decodeBytes(data, &car); err != nil {
return nil, ErrBadRequest.WithDetails("Parse error for Assertion").WithInfo(err.Error())
return nil, ErrBadRequest.WithDetails("Parse error for Assertion").WithInfo(err.Error()).WithError(err)
}

return car.Parse()
Expand All @@ -97,7 +97,7 @@ func (car CredentialAssertionResponse) Parse() (par *ParsedCredentialAssertionDa
}

if _, err = base64.RawURLEncoding.DecodeString(car.ID); err != nil {
return nil, ErrBadRequest.WithDetails("CredentialAssertionResponse with ID not base64url encoded")
return nil, ErrBadRequest.WithDetails("CredentialAssertionResponse with ID not base64url encoded").WithError(err)
}

if car.Type != string(PublicKeyCredentialType) {
Expand Down Expand Up @@ -129,7 +129,7 @@ func (car CredentialAssertionResponse) Parse() (par *ParsedCredentialAssertionDa
}

if err = par.Response.AuthenticatorData.Unmarshal(car.AssertionResponse.AuthenticatorData); err != nil {
return nil, ErrParsingData.WithDetails("Error unmarshalling auth data")
return nil, ErrParsingData.WithDetails("Error unmarshalling auth data").WithError(err)
}

return par, nil
Expand Down Expand Up @@ -189,12 +189,12 @@ func (p *ParsedCredentialAssertionData) Verify(storedChallenge string, relyingPa
}

if err != nil {
return ErrAssertionSignature.WithDetails(fmt.Sprintf("Error parsing the assertion public key: %+v", err))
return ErrAssertionSignature.WithDetails(fmt.Sprintf("Error parsing the assertion public key: %+v", err)).WithError(err)
}

valid, err := webauthncose.VerifySignature(key, sigData, p.Response.Signature)
if !valid || err != nil {
return ErrAssertionSignature.WithDetails(fmt.Sprintf("Error validating the assertion signature: %+v", err))
return ErrAssertionSignature.WithDetails(fmt.Sprintf("Error validating the assertion signature: %+v", err)).WithError(err)
}

return nil
Expand Down
10 changes: 5 additions & 5 deletions protocol/attestation.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,18 +94,18 @@ func (ccr *AuthenticatorAttestationResponse) Parse() (p *ParsedAttestationRespon
p = &ParsedAttestationResponse{}

if err = json.Unmarshal(ccr.ClientDataJSON, &p.CollectedClientData); err != nil {
return nil, ErrParsingData.WithInfo(err.Error())
return nil, ErrParsingData.WithInfo(err.Error()).WithError(err)
}

if err = webauthncbor.Unmarshal(ccr.AttestationObject, &p.AttestationObject); err != nil {
return nil, ErrParsingData.WithInfo(err.Error())
return nil, ErrParsingData.WithInfo(err.Error()).WithError(err)
}

// Step 8. Perform CBOR decoding on the attestationObject field of the AuthenticatorAttestationResponse
// structure to obtain the attestation statement format fmt, the authenticator data authData, and
// the attestation statement attStmt.
if err = p.AttestationObject.AuthData.Unmarshal(p.AttestationObject.RawAuthData); err != nil {
return nil, fmt.Errorf("error decoding auth data: %v", err)
return nil, err
}

if !p.AttestationObject.AuthData.Flags.HasAttestedCredentialData() {
Expand Down Expand Up @@ -176,7 +176,7 @@ func (a *AttestationObject) VerifyAttestation(clientDataHash []byte, mds metadat

if len(a.AuthData.AttData.AAGUID) != 0 {
if aaguid, err = uuid.FromBytes(a.AuthData.AttData.AAGUID); err != nil {
return ErrInvalidAttestation.WithInfo("Error occurred parsing AAGUID during attestation validation").WithDetails(err.Error())
return ErrInvalidAttestation.WithInfo("Error occurred parsing AAGUID during attestation validation").WithDetails(err.Error()).WithError(err)
}
}

Expand All @@ -187,7 +187,7 @@ func (a *AttestationObject) VerifyAttestation(clientDataHash []byte, mds metadat
var protoErr *Error

if protoErr = ValidateMetadata(context.Background(), mds, aaguid, attestationType, x5cs); protoErr != nil {
return ErrInvalidAttestation.WithInfo(fmt.Sprintf("Error occurred validating metadata during attestation validation: %+v", protoErr)).WithDetails(protoErr.DevInfo)
return ErrInvalidAttestation.WithInfo(fmt.Sprintf("Error occurred validating metadata during attestation validation: %+v", protoErr)).WithDetails(protoErr.DevInfo).WithError(protoErr)
}

return nil
Expand Down
12 changes: 6 additions & 6 deletions protocol/attestation_androidkey.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,25 +65,25 @@ func verifyAndroidKeyFormat(att AttestationObject, clientDataHash []byte, _ meta

attCert, err := x509.ParseCertificate(attCertBytes)
if err != nil {
return "", nil, ErrAttestationFormat.WithDetails(fmt.Sprintf("Error parsing certificate from ASN.1 data: %+v", err))
return "", nil, ErrAttestationFormat.WithDetails(fmt.Sprintf("Error parsing certificate from ASN.1 data: %+v", err)).WithError(err)
}

coseAlg := webauthncose.COSEAlgorithmIdentifier(alg)
if err = attCert.CheckSignature(webauthncose.SigAlgFromCOSEAlg(coseAlg), signatureData, sig); err != nil {
return "", nil, ErrInvalidAttestation.WithDetails(fmt.Sprintf("Signature validation error: %+v\n", err))
return "", nil, ErrInvalidAttestation.WithDetails(fmt.Sprintf("Signature validation error: %+v\n", err)).WithError(err)
}

// Verify that the public key in the first certificate in x5c matches the credentialPublicKey in the attestedCredentialData in authenticatorData.
pubKey, err := webauthncose.ParsePublicKey(att.AuthData.AttData.CredentialPublicKey)
if err != nil {
return "", nil, ErrInvalidAttestation.WithDetails(fmt.Sprintf("Error parsing public key: %+v\n", err))
return "", nil, ErrInvalidAttestation.WithDetails(fmt.Sprintf("Error parsing public key: %+v\n", err)).WithError(err)
}

e := pubKey.(webauthncose.EC2PublicKeyData)

valid, err = e.Verify(signatureData, sig)
if err != nil || !valid {
return "", nil, ErrInvalidAttestation.WithDetails(fmt.Sprintf("Error parsing public key: %+v\n", err))
return "", nil, ErrInvalidAttestation.WithDetails(fmt.Sprintf("Error parsing public key: %+v\n", err)).WithError(err)
}

// §8.4.3. Verify that the attestationChallenge field in the attestation certificate extension data is identical to clientDataHash.
Expand All @@ -105,11 +105,11 @@ func verifyAndroidKeyFormat(att AttestationObject, clientDataHash []byte, _ meta
decoded := keyDescription{}

if _, err = asn1.Unmarshal(attExtBytes, &decoded); err != nil {
return "", nil, ErrAttestationFormat.WithDetails("Unable to parse Android key attestation certificate extensions")
return "", nil, ErrAttestationFormat.WithDetails("Unable to parse Android key attestation certificate extensions").WithError(err)
}

// Verify that the attestationChallenge field in the attestation certificate extension data is identical to clientDataHash.
if 0 != bytes.Compare(decoded.AttestationChallenge, clientDataHash) {
if bytes.Compare(decoded.AttestationChallenge, clientDataHash) != 0 {
return "", nil, ErrAttestationFormat.WithDetails("Attestation challenge not equal to clientDataHash")
}

Expand Down
6 changes: 3 additions & 3 deletions protocol/attestation_apple.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func verifyAppleFormat(att AttestationObject, clientDataHash []byte, _ metadata.

credCert, err := x509.ParseCertificate(credCertBytes)
if err != nil {
return "", nil, ErrAttestationFormat.WithDetails(fmt.Sprintf("Error parsing certificate from ASN.1 data: %+v", err))
return "", nil, ErrAttestationFormat.WithDetails(fmt.Sprintf("Error parsing certificate from ASN.1 data: %+v", err)).WithError(err)
}

// Step 2. Concatenate authenticatorData and clientDataHash to form nonceToHash.
Expand All @@ -73,7 +73,7 @@ func verifyAppleFormat(att AttestationObject, clientDataHash []byte, _ metadata.
decoded := AppleAnonymousAttestation{}

if _, err = asn1.Unmarshal(attExtBytes, &decoded); err != nil {
return "", nil, ErrAttestationFormat.WithDetails("Unable to parse apple attestation certificate extensions")
return "", nil, ErrAttestationFormat.WithDetails("Unable to parse apple attestation certificate extensions").WithError(err)
}

if !bytes.Equal(decoded.Nonce, nonce[:]) {
Expand All @@ -84,7 +84,7 @@ func verifyAppleFormat(att AttestationObject, clientDataHash []byte, _ metadata.
// TODO: Probably move this part to webauthncose.go
pubKey, err := webauthncose.ParsePublicKey(att.AuthData.AttData.CredentialPublicKey)
if err != nil {
return "", nil, ErrInvalidAttestation.WithDetails(fmt.Sprintf("Error parsing public key: %+v\n", err))
return "", nil, ErrInvalidAttestation.WithDetails(fmt.Sprintf("Error parsing public key: %+v\n", err)).WithError(err)
}

credPK := pubKey.(webauthncose.EC2PublicKeyData)
Expand Down
6 changes: 3 additions & 3 deletions protocol/attestation_packed.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func handleBasicAttestation(signature, clientDataHash, authData, aaguid []byte,

ct, err := x509.ParseCertificate(cb)
if err != nil {
return "", x5c, ErrAttestationFormat.WithDetails(fmt.Sprintf("Error parsing certificate from ASN.1 data: %+v", err))
return "", x5c, ErrAttestationFormat.WithDetails(fmt.Sprintf("Error parsing certificate from ASN.1 data: %+v", err)).WithError(err)
}

if ct.NotBefore.After(time.Now()) || ct.NotAfter.Before(time.Now()) {
Expand All @@ -98,12 +98,12 @@ func handleBasicAttestation(signature, clientDataHash, authData, aaguid []byte,

attCert, err := x509.ParseCertificate(attCertBytes)
if err != nil {
return "", x5c, ErrAttestationFormat.WithDetails(fmt.Sprintf("Error parsing certificate from ASN.1 data: %+v", err))
return "", x5c, ErrAttestationFormat.WithDetails(fmt.Sprintf("Error parsing certificate from ASN.1 data: %+v", err)).WithError(err)
}

coseAlg := webauthncose.COSEAlgorithmIdentifier(alg)
if err = attCert.CheckSignature(webauthncose.SigAlgFromCOSEAlg(coseAlg), signatureData, signature); err != nil {
return "", x5c, ErrInvalidAttestation.WithDetails(fmt.Sprintf("Signature validation error: %+v\n", err))
return "", x5c, ErrInvalidAttestation.WithDetails(fmt.Sprintf("Signature validation error: %+v\n", err)).WithError(err)
}

// Step 2.2 Verify that attestnCert meets the requirements in §8.2.1 Packed attestation statement certificate requirements.
Expand Down
12 changes: 6 additions & 6 deletions protocol/attestation_safetynet.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,14 @@ func verifySafetyNetFormat(att AttestationObject, clientDataHash []byte, mds met
})

if err != nil {
return "", nil, ErrInvalidAttestation.WithDetails(fmt.Sprintf("Error finding cert issued to correct hostname: %+v", err))
return "", nil, ErrInvalidAttestation.WithDetails(fmt.Sprintf("Error finding cert issued to correct hostname: %+v", err)).WithError(err)
}

// marshall the JWT payload into the safetynet response json
var safetyNetResponse SafetyNetResponse

if err = mapstructure.Decode(token.Claims, &safetyNetResponse); err != nil {
return "", nil, ErrAttestationFormat.WithDetails(fmt.Sprintf("Error parsing the SafetyNet response: %+v", err))
return "", nil, ErrAttestationFormat.WithDetails(fmt.Sprintf("Error parsing the SafetyNet response: %+v", err)).WithError(err)
}

// §8.5.3 Verify that the nonce in the response is identical to the Base64 encoding of the SHA-256 hash of the concatenation
Expand All @@ -106,7 +106,7 @@ func verifySafetyNetFormat(att AttestationObject, clientDataHash []byte, mds met

nonceBytes, err := base64.StdEncoding.DecodeString(safetyNetResponse.Nonce)
if !bytes.Equal(nonceBuffer[:], nonceBytes) || err != nil {
return "", nil, ErrInvalidAttestation.WithDetails("Invalid nonce for in SafetyNet response")
return "", nil, ErrInvalidAttestation.WithDetails("Invalid nonce for in SafetyNet response").WithError(err)
}

// §8.5.4 Let attestationCert be the attestation certificate (https://www.w3.org/TR/webauthn/#attestation-certificate)
Expand All @@ -115,18 +115,18 @@ func verifySafetyNetFormat(att AttestationObject, clientDataHash []byte, mds met

n, err := base64.StdEncoding.Decode(l, []byte(certChain[0].(string)))
if err != nil {
return "", nil, ErrInvalidAttestation.WithDetails(fmt.Sprintf("Error finding cert issued to correct hostname: %+v", err))
return "", nil, ErrInvalidAttestation.WithDetails(fmt.Sprintf("Error finding cert issued to correct hostname: %+v", err)).WithError(err)
}

attestationCert, err := x509.ParseCertificate(l[:n])
if err != nil {
return "", nil, ErrInvalidAttestation.WithDetails(fmt.Sprintf("Error finding cert issued to correct hostname: %+v", err))
return "", nil, ErrInvalidAttestation.WithDetails(fmt.Sprintf("Error finding cert issued to correct hostname: %+v", err)).WithError(err)
}

// §8.5.5 Verify that attestationCert is issued to the hostname "attest.android.com"
err = attestationCert.VerifyHostname("attest.android.com")
if err != nil {
return "", nil, ErrInvalidAttestation.WithDetails(fmt.Sprintf("Error finding cert issued to correct hostname: %+v", err))
return "", nil, ErrInvalidAttestation.WithDetails(fmt.Sprintf("Error finding cert issued to correct hostname: %+v", err)).WithError(err)
}

// §8.5.6 Verify that the ctsProfileMatch attribute in the payload of response is true.
Expand Down
4 changes: 2 additions & 2 deletions protocol/attestation_tpm.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func verifyTPMFormat(att AttestationObject, clientDataHash []byte, _ metadata.Pr
// is identical to the credentialPublicKey in the attestedCredentialData in authenticatorData.
pubArea, err := tpm2.DecodePublic(pubAreaBytes)
if err != nil {
return "", nil, ErrAttestationFormat.WithDetails("Unable to decode TPMT_PUBLIC in attestation statement")
return "", nil, ErrAttestationFormat.WithDetails("Unable to decode TPMT_PUBLIC in attestation statement").WithError(err)
}

key, err := webauthncose.ParsePublicKey(att.AuthData.AttData.CredentialPublicKey)
Expand Down Expand Up @@ -394,7 +394,7 @@ func tpmParseSANExtension(attestation *x509.Certificate) (protoErr *Error) {
for _, ext := range attestation.Extensions {
if ext.Id.Equal(oidExtensionSubjectAltName) {
if manufacturer, model, version, err = parseSANExtension(ext.Value); err != nil {
return ErrInvalidAttestation.WithDetails("Authenticator with invalid Authenticator Identity Key SAN data encountered during attestation validation.").WithInfo(fmt.Sprintf("Error occurred parsing SAN extension: %s", err.Error()))
return ErrInvalidAttestation.WithDetails("Authenticator with invalid Authenticator Identity Key SAN data encountered during attestation validation.").WithInfo(fmt.Sprintf("Error occurred parsing SAN extension: %s", err.Error())).WithError(err)
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion protocol/attestation_u2f.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func verifyU2FFormat(att AttestationObject, clientDataHash []byte, _ metadata.Pr

attCert, err := x509.ParseCertificate(asn1Bytes)
if err != nil {
return "", nil, ErrAttestationFormat.WithDetails("Error parsing certificate from ASN.1 data into certificate")
return "", nil, ErrAttestationFormat.WithDetails("Error parsing certificate from ASN.1 data into certificate").WithError(err)
}

// Step 2.3
Expand Down
2 changes: 1 addition & 1 deletion protocol/authenticator.go
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ func (a *AuthenticatorData) unmarshalAttestedData(rawAuthData []byte) (err error

a.AttData.CredentialPublicKey, err = unmarshalCredentialPublicKey(rawAuthData[55+idLength:])
if err != nil {
return ErrBadRequest.WithDetails(fmt.Sprintf("Could not unmarshal Credential Public Key: %v", err))
return ErrBadRequest.WithDetails(fmt.Sprintf("Could not unmarshal Credential Public Key: %v", err)).WithError(err)
}

return nil
Expand Down
4 changes: 2 additions & 2 deletions protocol/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ func (c *CollectedClientData) Verify(storedChallenge string, ceremony CeremonyTy
var fqOrigin string

if fqOrigin, err = FullyQualifiedOrigin(c.Origin); err != nil {
return ErrParsingData.WithDetails("Error decoding clientData origin as URL")
return ErrParsingData.WithDetails("Error decoding clientData origin as URL").WithError(err)
}

found := false
Expand Down Expand Up @@ -146,7 +146,7 @@ func (c *CollectedClientData) Verify(storedChallenge string, ceremony CeremonyTy
)

if fqTopOrigin, err = FullyQualifiedOrigin(c.TopOrigin); err != nil {
return ErrParsingData.WithDetails("Error decoding clientData topOrigin as URL")
return ErrParsingData.WithDetails("Error decoding clientData topOrigin as URL").WithError(err)
}

switch rpTopOriginsVerify {
Expand Down
4 changes: 2 additions & 2 deletions protocol/credential.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func ParseCredentialCreationResponseBody(body io.Reader) (pcc *ParsedCredentialC
var ccr CredentialCreationResponse

if err = decodeBody(body, &ccr); err != nil {
return nil, ErrBadRequest.WithDetails("Parse error for Registration").WithInfo(err.Error())
return nil, ErrBadRequest.WithDetails("Parse error for Registration").WithInfo(err.Error()).WithError(err)
}

return ccr.Parse()
Expand All @@ -91,7 +91,7 @@ func ParseCredentialCreationResponseBytes(data []byte) (pcc *ParsedCredentialCre
var ccr CredentialCreationResponse

if err = decodeBytes(data, &ccr); err != nil {
return nil, ErrBadRequest.WithDetails("Parse error for Registration").WithInfo(err.Error())
return nil, ErrBadRequest.WithDetails("Parse error for Registration").WithInfo(err.Error()).WithError(err)
}

return ccr.Parse()
Expand Down
Loading

0 comments on commit e582e2c

Please sign in to comment.