Skip to content

Commit 339114c

Browse files
authored
feat(protocol): include intermediate certificate parsing (#345)
1 parent aab4d9d commit 339114c

File tree

2 files changed

+58
-11
lines changed

2 files changed

+58
-11
lines changed

protocol/attestation.go

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -232,35 +232,56 @@ func (a *AttestationObject) VerifyAttestation(clientDataHash []byte, mds metadat
232232
}
233233

234234
var (
235-
x5c *x509.Certificate
236-
raw []byte
237-
ok bool
235+
x5c *x509.Certificate
236+
parents []*x509.Certificate
238237
)
239238

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

244-
if raw, ok = x5cs[0].([]byte); !ok {
245-
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]))
246-
}
247-
248-
if x5c, err = x509.ParseCertificate(raw); err != nil {
249-
return ErrInvalidAttestation.WithDetails("Unable to parse attestation certificate from x5c during attestation validation").WithInfo(fmt.Sprintf("Error returned from x509.ParseCertificate: %+v", err))
243+
for _, x5cAny := range x5cs {
244+
x5cRaw, ok := x5cAny.([]byte)
245+
if !ok {
246+
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]))
247+
}
248+
x5cParsed, err := x509.ParseCertificate(x5cRaw)
249+
if err != nil {
250+
return ErrInvalidAttestation.WithDetails("Unable to parse attestation certificate from x5c during attestation validation").WithInfo(fmt.Sprintf("Error returned from x509.ParseCertificate: %+v", err))
251+
}
252+
if x5c == nil {
253+
x5c = x5cParsed
254+
} else {
255+
parents = append(parents, x5cParsed)
256+
}
250257
}
251258

252259
if attestationType == string(metadata.AttCA) {
253260
if err = tpmParseSANExtension(x5c); err != nil {
254261
return err
255262
}
263+
if err = tpmRemoveEKU(x5c); err != nil {
264+
return err
265+
}
266+
for _, parent := range parents {
267+
if err = tpmRemoveEKU(parent); err != nil {
268+
return err
269+
}
270+
}
256271
}
257272

258273
if x5c.Subject.CommonName != x5c.Issuer.CommonName {
259274
if !entry.MetadataStatement.AttestationTypes.HasBasicFull() {
260275
return ErrInvalidAttestation.WithDetails("Unable to validate attestation statement signature during attestation validation: attestation with full attestation from authenticator that does not support full attestation")
261276
}
262-
263-
if _, err = x5c.Verify(entry.MetadataStatement.Verifier()); err != nil {
277+
verifier := entry.MetadataStatement.Verifier()
278+
if len(parents) != 0 {
279+
verifier.Intermediates = x509.NewCertPool()
280+
for _, parent := range parents {
281+
verifier.Intermediates.AddCert(parent)
282+
}
283+
}
284+
if _, err = x5c.Verify(verifier); err != nil {
264285
return ErrInvalidAttestation.WithDetails(fmt.Sprintf("Unable to validate attestation signature statement during attestation validation: invalid certificate chain from MDS: %v", err))
265286
}
266287
}

protocol/attestation_tpm.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,9 +395,35 @@ var (
395395
oidExtensionSubjectAltName = []int{2, 5, 29, 17}
396396
oidExtensionExtendedKeyUsage = []int{2, 5, 29, 37}
397397
oidExtensionBasicConstraints = []int{2, 5, 29, 19}
398+
399+
// From wincrypt.h of Windows SDK.
400+
// Enhanced Key Usage for Privacy CA encryption certificate
401+
oidKpPrivacyCA = []int{1, 3, 6, 1, 4, 1, 311, 21, 36}
398402
)
399403

400404
type tpmBasicConstraints struct {
401405
IsCA bool `asn1:"optional"`
402406
MaxPathLen int `asn1:"optional,default:-1"`
403407
}
408+
409+
// remove extension key usage to avoid ExtKeyUsage check failure
410+
// see also https://github.com/go-webauthn/webauthn/issues/342
411+
func tpmRemoveEKU(x5c *x509.Certificate) error {
412+
var unknown []asn1.ObjectIdentifier
413+
hasAiK := false
414+
for _, eku := range x5c.UnknownExtKeyUsage {
415+
if eku.Equal(tcgKpAIKCertificate) {
416+
hasAiK = true
417+
continue
418+
}
419+
if eku.Equal(oidKpPrivacyCA) {
420+
continue
421+
}
422+
unknown = append(unknown, eku)
423+
}
424+
if !hasAiK {
425+
return ErrAttestationFormat.WithDetails("AIK certificate missing EKU")
426+
}
427+
x5c.UnknownExtKeyUsage = unknown
428+
return nil
429+
}

0 commit comments

Comments
 (0)