Skip to content

Commit

Permalink
feat: enhancements
Browse files Browse the repository at this point in the history
  • Loading branch information
james-d-elliott committed Nov 27, 2024
1 parent 0ee4cb8 commit bf6866e
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 55 deletions.
46 changes: 2 additions & 44 deletions protocol/attestation.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package protocol
import (
"context"
"crypto/sha256"
"crypto/x509"
"encoding/json"
"fmt"

Expand Down Expand Up @@ -173,7 +172,6 @@ func (a *AttestationObject) VerifyAttestation(clientDataHash []byte, mds metadat

var (
aaguid uuid.UUID
entry *metadata.Entry
)

if len(a.AuthData.AttData.AAGUID) != 0 {
Expand All @@ -188,48 +186,8 @@ func (a *AttestationObject) VerifyAttestation(clientDataHash []byte, mds metadat

var protoErr *Error

ctx := context.Background()

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

if entry == nil {
return nil
}

if mds.GetValidateTrustAnchor(ctx) {
if x5cs == nil {
return nil
}

var (
x5c *x509.Certificate
raw []byte
ok bool
)

if len(x5cs) == 0 {
return ErrInvalidAttestation.WithDetails("Unable to parse attestation certificate from x5c during attestation validation").WithInfo("The attestation had no certificates")
}

if raw, ok = x5cs[0].([]byte); !ok {
return ErrInvalidAttestation.WithDetails("Unable to parse attestation certificate from x5c during attestation validation").WithInfo(fmt.Sprintf("The first certificate in the attestation was type '%T' but '[]byte' was expected", x5cs[0]))
}

if x5c, err = x509.ParseCertificate(raw); err != nil {
return ErrInvalidAttestation.WithDetails("Unable to parse attestation certificate from x5c during attestation validation").WithInfo(fmt.Sprintf("Error returned from x509.ParseCertificate: %+v", err))
}

if x5c.Subject.CommonName != x5c.Issuer.CommonName {
if !entry.MetadataStatement.AttestationTypes.HasBasicFull() {
return ErrInvalidAttestation.WithDetails("Unable to validate attestation statement signature during attestation validation: attestation with full attestation from authenticator that does not support full attestation")
}

if _, err = x5c.Verify(entry.MetadataStatement.Verifier()); err != nil {
return ErrInvalidAttestation.WithDetails(fmt.Sprintf("Unable to validate attestation signature statement during attestation validation: invalid certificate chain from MDS: %v", err))
}
}
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 nil
Expand Down
56 changes: 46 additions & 10 deletions protocol/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,38 @@ package protocol

import (
"context"
"crypto/x509"
"fmt"

"github.com/google/uuid"

"github.com/go-webauthn/webauthn/metadata"
)

func ValidateMetadata(ctx context.Context, aaguid uuid.UUID, attestationType string, mds metadata.Provider) (entry *metadata.Entry, protoErr *Error) {
func ValidateMetadata(ctx context.Context, mds metadata.Provider, aaguid uuid.UUID, attestationType string, x5cs []any) (protoErr *Error) {
if mds == nil {
return nil, nil
return nil
}

var (
err error
entry *metadata.Entry
err error
)

if entry, err = mds.GetEntry(ctx, aaguid); err != nil {
return nil, ErrMetadata.WithInfo(fmt.Sprintf("Failed to validate authenticator metadata for Authenticator Attestation GUID '%s'. Error occurred retreiving the metadata entry: %+v", aaguid, err))
return ErrMetadata.WithInfo(fmt.Sprintf("Failed to validate authenticator metadata for Authenticator Attestation GUID '%s'. Error occurred retreiving the metadata entry: %+v", aaguid, err))
}

if entry == nil {
if aaguid == uuid.Nil && mds.GetValidateEntryPermitZeroAAGUID(ctx) {
return nil, nil
return nil
}

if mds.GetValidateEntry(ctx) {
return nil, ErrMetadata.WithInfo(fmt.Sprintf("Failed to validate authenticator metadata for Authenticator Attestation GUID '%s'. The authenticator has no registered metadata.", aaguid))
return ErrMetadata.WithInfo(fmt.Sprintf("Failed to validate authenticator metadata for Authenticator Attestation GUID '%s'. The authenticator has no registered metadata.", aaguid))
}

return nil, nil
return nil
}

if mds.GetValidateAttestationTypes(ctx) {
Expand All @@ -46,15 +48,49 @@ func ValidateMetadata(ctx context.Context, aaguid uuid.UUID, attestationType str
}

if !found {
return entry, ErrMetadata.WithInfo(fmt.Sprintf("Failed to validate authenticator metadata for Authenticator Attestation GUID '%s'. The attestation type '%s' is not known to be used by this authenticator.", aaguid.String(), attestationType))
return ErrMetadata.WithInfo(fmt.Sprintf("Failed to validate authenticator metadata for Authenticator Attestation GUID '%s'. The attestation type '%s' is not known to be used by this authenticator.", aaguid.String(), attestationType))
}
}

if mds.GetValidateStatus(ctx) {
if err = mds.ValidateStatusReports(ctx, entry.StatusReports); err != nil {
return entry, ErrMetadata.WithInfo(fmt.Sprintf("Failed to validate authenticator metadata for Authenticator Attestation GUID '%s'. Error occurred validating the authenticator status: %+v", aaguid, err))
return ErrMetadata.WithInfo(fmt.Sprintf("Failed to validate authenticator metadata for Authenticator Attestation GUID '%s'. Error occurred validating the authenticator status: %+v", aaguid, err))
}
}

return entry, nil
if mds.GetValidateTrustAnchor(ctx) {
if x5cs == nil {
return nil
}

var (
x5c *x509.Certificate
data []byte
ok bool
)

if len(x5cs) == 0 {
return ErrMetadata.WithDetails(fmt.Sprintf("Failed to parse attestation certificate from x5c during attestation validation for Authenticator Attestation GUID '%s'.", aaguid)).WithInfo("The attestation had no certificates")
}

if data, ok = x5cs[0].([]byte); !ok {
return ErrMetadata.WithDetails(fmt.Sprintf("Failed to parse attestation certificate from x5c during attestation validation for Authenticator Attestation GUID '%s'.", aaguid)).WithInfo(fmt.Sprintf("The first certificate in the attestation was type '%T' but '[]byte' was expected", x5cs[0]))
}

if x5c, err = x509.ParseCertificate(data); err != nil {
return ErrMetadata.WithDetails(fmt.Sprintf("Failed to parse attestation certificate from x5c during attestation validation for Authenticator Attestation GUID '%s'.", aaguid)).WithInfo(fmt.Sprintf("Error returned from x509.ParseCertificate: %+v", err))
}

if x5c.Subject.CommonName != x5c.Issuer.CommonName {
if !entry.MetadataStatement.AttestationTypes.HasBasicFull() {
return ErrMetadata.WithDetails(fmt.Sprintf("Failed to validate attestation statement signature during attestation validation for Authenticator Attestation GUID '%s'. Attestation was provided in the full format but the authenticator doesn't support the full attestation format.", aaguid))
}

if _, err = x5c.Verify(entry.MetadataStatement.Verifier()); err != nil {
return ErrMetadata.WithDetails(fmt.Sprintf("Failed to validate attestation statement signature during attestation validation for Authenticator Attestation GUID '%s'. The attestation certificate could not be verified due to an error validating the trust chain agaisnt the Metadata Service.", aaguid))
}
}
}

return nil
}
2 changes: 1 addition & 1 deletion webauthn/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ func (webauthn *WebAuthn) validateLogin(user User, session SessionData, parsedRe

var protoErr *protocol.Error

if _, protoErr = protocol.ValidateMetadata(context.Background(), aaguid, credential.AttestationType, webauthn.Config.MDS); protoErr != nil {
if protoErr = protocol.ValidateMetadata(context.Background(), webauthn.Config.MDS, aaguid, credential.AttestationType, nil); protoErr != nil {
return nil, protocol.ErrBadRequest.WithDetails("Failed to validate credential record metadata").WithInfo(protoErr.DevInfo)
}
}
Expand Down

0 comments on commit bf6866e

Please sign in to comment.