Skip to content

Commit

Permalink
feat(protocol): include intermediate certificate parsing (#345)
Browse files Browse the repository at this point in the history
  • Loading branch information
on-keyday authored Dec 22, 2024
1 parent aab4d9d commit 339114c
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 11 deletions.
43 changes: 32 additions & 11 deletions protocol/attestation.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,35 +232,56 @@ func (a *AttestationObject) VerifyAttestation(clientDataHash []byte, mds metadat
}

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

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))
for _, x5cAny := range x5cs {
x5cRaw, ok := x5cAny.([]byte)
if !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]))
}
x5cParsed, err := x509.ParseCertificate(x5cRaw)
if 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 == nil {
x5c = x5cParsed
} else {
parents = append(parents, x5cParsed)
}
}

if attestationType == string(metadata.AttCA) {
if err = tpmParseSANExtension(x5c); err != nil {
return err
}
if err = tpmRemoveEKU(x5c); err != nil {
return err
}
for _, parent := range parents {
if err = tpmRemoveEKU(parent); err != nil {
return 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 {
verifier := entry.MetadataStatement.Verifier()
if len(parents) != 0 {
verifier.Intermediates = x509.NewCertPool()
for _, parent := range parents {
verifier.Intermediates.AddCert(parent)
}
}
if _, err = x5c.Verify(verifier); err != nil {
return ErrInvalidAttestation.WithDetails(fmt.Sprintf("Unable to validate attestation signature statement during attestation validation: invalid certificate chain from MDS: %v", err))
}
}
Expand Down
26 changes: 26 additions & 0 deletions protocol/attestation_tpm.go
Original file line number Diff line number Diff line change
Expand Up @@ -395,9 +395,35 @@ var (
oidExtensionSubjectAltName = []int{2, 5, 29, 17}
oidExtensionExtendedKeyUsage = []int{2, 5, 29, 37}
oidExtensionBasicConstraints = []int{2, 5, 29, 19}

// From wincrypt.h of Windows SDK.
// Enhanced Key Usage for Privacy CA encryption certificate
oidKpPrivacyCA = []int{1, 3, 6, 1, 4, 1, 311, 21, 36}
)

type tpmBasicConstraints struct {
IsCA bool `asn1:"optional"`
MaxPathLen int `asn1:"optional,default:-1"`
}

// remove extension key usage to avoid ExtKeyUsage check failure
// see also https://github.com/go-webauthn/webauthn/issues/342
func tpmRemoveEKU(x5c *x509.Certificate) error {
var unknown []asn1.ObjectIdentifier
hasAiK := false
for _, eku := range x5c.UnknownExtKeyUsage {
if eku.Equal(tcgKpAIKCertificate) {
hasAiK = true
continue
}
if eku.Equal(oidKpPrivacyCA) {
continue
}
unknown = append(unknown, eku)
}
if !hasAiK {
return ErrAttestationFormat.WithDetails("AIK certificate missing EKU")
}
x5c.UnknownExtKeyUsage = unknown
return nil
}

0 comments on commit 339114c

Please sign in to comment.