From 5d216f624e2be25a4c6fb5dd8b865f4b5a6c389e Mon Sep 17 00:00:00 2001 From: hpya93 Date: Wed, 13 Dec 2023 01:38:33 +0530 Subject: [PATCH 1/2] . --- verification/certifyKey.go | 792 +++++++++++++++------------- verification/certs.go | 378 ++++++++++++- verification/extendTCI.go | 34 +- verification/getCertificateChain.go | 16 +- verification/simulator.go | 5 + verification/verification.go | 11 +- 6 files changed, 824 insertions(+), 412 deletions(-) diff --git a/verification/certifyKey.go b/verification/certifyKey.go index 99c69999..daf0a62e 100755 --- a/verification/certifyKey.go +++ b/verification/certifyKey.go @@ -15,107 +15,67 @@ import ( "math/big" "reflect" "testing" - "time" "go.mozilla.org/pkcs7" zx509 "github.com/zmap/zcrypto/x509" zlint "github.com/zmap/zlint/v3" "github.com/zmap/zlint/v3/lint" - - "golang.org/x/exp/slices" ) -var TcgDiceCriticalExtensions = [...]string{ - OidExtensionTcgDiceMultiTcbInfo.String(), - OidExtensionTcgDiceUeid.String(), +type CertifyKeyParams struct { + Label []byte + Flags CertifyKeyFlags } -var TcgDiceExtendedKeyUsages = [...]string{ - OidExtensionTcgDiceKpIdentityLoc.String(), - OidExtensionTcgDiceKpAttestLoc.String(), +func TestCertifyKey(d TestDPEInstance, c DPEClient, t *testing.T) { + testCertifyKey(d, c, t, false) } -// tcg-dice-Ueid OBJECT IDENTIFIER ::= {tcg-dice 4} -// -// TcgUeid ::== SEQUENCE { -// ueid OCTET STRING -// } -type TcgUeidExtension struct { - Ueid []uint8 `asn1:"ueid,implicit"` +func TestCertifyKeySimulation(d TestDPEInstance, c DPEClient, t *testing.T) { + testCertifyKey(d, c, t, true) } -// tcg-dice-MultiTcbInfo OBJECT IDENTIFIER ::= {tcg-dice 5} -// DiceTcbInfoSeq ::= SEQUENCE SIZE (1..MAX) OF DiceTcbInfo -// -// tcg-dice-TcbInfo OBJECT IDENTIFIER ::= {tcg-dice 1} -// -// DiceTcbInfo ::== SEQUENCE { -// vendor [0] IMPLICIT UTF8String OPTIONAL, -// model [1] IMPLICIT UTF8String OPTIONAL, -// version [2] IMPLICIT UTF8String OPTIONAL, -// svn [3] IMPLICIT INTEGER OPTIONAL, -// layer [4] IMPLICIT INTEGER OPTIONAL, -// index [5] IMPLICIT INTEGER OPTIONAL, -// fwids [6] IMPLICIT FWIDLIST OPTIONAL, -// flags [7] IMPLICIT OperationalFlags OPTIONAL, -// vendorInfo [8] IMPLICIT OCTET STRING OPTIONAL, -// type [9] IMPLICIT OCTET STRING OPTIONAL, -// } -// -// FWIDLIST ::== SEQUENCE SIZE (1..MAX) OF FWID -// FWID ::== SEQUENCE { -// hashAlg OBJECT IDENTIFIER, -// digest OCTET STRING -// } -// -// OperationalFlags ::= BIT STRING { -// notConfigured (0), -// notSecure (1), -// recovery (2), -// debug (3) -// } - -type Fwid struct { - HashAlg asn1.ObjectIdentifier - Digest []byte -} +// Checks whether FWID array omits index-1 when extend TCI is not supported in DPE profile. +func TestCertifyKeyWithoutExtendTciSupport(d TestDPEInstance, c DPEClient, t *testing.T) { + simulation := false + handle := getInitialContextHandle(d, c, t, simulation) -type DiceTcbInfo struct { - Vendor string `asn1:"optional,tag:0,utf8"` - Model string `asn1:"optional,tag:1,utf8"` - Version string `asn1:"optional,tag:2,utf8"` - SVN int `asn1:"optional,tag:3"` - Layer int `asn1:"optional,tag:4"` - Index int `asn1:"optional,tag:5"` - Fwids []Fwid `asn1:"optional,tag:6"` - Flags OperationalFlag `asn1:"optional,tag:7"` - VendorInfo []byte `asn1:"optional,tag:8"` - Type []byte `asn1:"optional,tag:9"` -} + profile, err := GetTransportProfile(d) + if err != nil { + t.Fatalf("Could not get profile: %v", err) + } + digestLen := profile.GetDigestSize() -type OperationalFlag int + // Get DPE leaf certificate from CertifyKey + certifiedKey, err := c.CertifyKey(handle, make([]byte, digestLen), CertifyKeyX509, CertifyKeyFlags(0)) + if err != nil { + t.Fatalf("[FATAL]: Could not certify key: %v", err) + } -const ( - NotConfigured OperationalFlag = iota - NotSecure - Debug - Recovery -) + leafCertBytes := certifiedKey.Certificate + var leafCert *x509.Certificate -type TcgMultiTcbInfo = []DiceTcbInfo + if leafCert, err = x509.ParseCertificate(leafCertBytes); err != nil { + t.Errorf("[ERROR]: Could not parse leaf certificate %s", err) + } -type CertifyKeyParams struct { - Label []byte - Flags CertifyKeyFlags -} + multiTcbInfo, err := getMultiTcbInfo(leafCert.Extensions) + if err != nil { + t.Errorf("[ERROR]: Could not parse multi TCB information: extension %s", err) + } -func TestCertifyKey(d TestDPEInstance, c DPEClient, t *testing.T) { - testCertifyKey(d, c, t, false) -} + if len(multiTcbInfo) == 0 { + t.Errorf("[ERROR]: Certificate MutliTcbInfo is empty") + } -func TestCertifyKeySimulation(d TestDPEInstance, c DPEClient, t *testing.T) { - testCertifyKey(d, c, t, true) + // Check whether fwids array has only fwids[0] i.e, TCI_CURRENT measurement + // and TCI_CUMULATIVE i.e fwids[1] is omitted in every Dice TCB info block + for i, tcbinfo := range multiTcbInfo { + if len(tcbinfo.Fwids) != 1 { + t.Errorf("Extend TCI is not supported by profile, expected FWIDs length in block-%d is %d but got %d", i, 1, len(tcbinfo.Fwids)) + } + } } func TestCertifyKey_Csr(d TestDPEInstance, c DPEClient, t *testing.T) { @@ -194,197 +154,362 @@ func TestCertifyKey_Csr(d TestDPEInstance, c DPEClient, t *testing.T) { } } -// Ignores critical extensions that are unknown to x509 package -// but atleast defined in DPE certificate profile specification. -// UnhandledCriticalExtensions may have only custom extensions mentioned in spec -// unknownExtnMap collects extensions unknown to both x509 and the DICE certificate profiles spec. -// positive case expects the unknownExtnMap to be empty. -func removeTcgDiceCriticalExtensions(t *testing.T, certs []*x509.Certificate) { - t.Helper() - unknownExtnMap := map[string][]string{} - for _, cert := range certs { - if len(cert.UnhandledCriticalExtensions) > 0 { - unknownExtns := []string{} - for _, extn := range cert.UnhandledCriticalExtensions { - if !slices.Contains(TcgDiceCriticalExtensions[:], extn.String()) { - unknownExtns = append(unknownExtns, extn.String()) - } +func testCertifyKey(d TestDPEInstance, c DPEClient, t *testing.T, simulation bool) { + handle := getInitialContextHandle(d, c, t, simulation) + if simulation { + // Clean up contexts + defer func() { + err := c.DestroyContext(handle, DestroyDescendants) + if err != nil { + t.Errorf("[ERROR]: Error while cleaning contexts, this may cause failure in subsequent tests: %s", err) } + }() + } - if len(unknownExtnMap) == 0 { - cert.UnhandledCriticalExtensions = []asn1.ObjectIdentifier{} - } else { - unknownExtnMap[cert.Subject.String()] = unknownExtns - } - } + profile, err := GetTransportProfile(d) + if err != nil { + t.Fatalf("Could not get profile: %v", err) } - // The error details in this map will be logged - if len(unknownExtnMap) > 0 { - for certSubject, ext := range unknownExtnMap { - t.Errorf("[ERROR]: Certificate \"%s\" has unhandled critical extension \"%s\"", certSubject, ext) - } - t.Errorf("[ERROR]: Certificate chain validation will fail with non-empty unhandled critical extensions list") + digestLen := profile.GetDigestSize() + seqLabel := make([]byte, digestLen) + for i := range seqLabel { + seqLabel[i] = byte(i) } -} -// Ignores extended key usages that are unknown to x509 package -// but atleast defined in DPE certificate profile specification. -// UnhandledExtendedKeyUsages may have only custom key usages mentioned in spec -// unknownKeyUsagesMap collects keyusages unknown to both x509 and the DICE certificate profiles spec. -// positive case expects the unknownKeyUsagesMap to be empty. -func removeTcgDiceExtendedKeyUsages(t *testing.T, certs []*x509.Certificate) { - t.Helper() - unknownKeyUsagesMap := map[string][]string{} - for _, cert := range certs { - if len(cert.UnknownExtKeyUsage) > 0 { - unknownKeyUsages := []string{} - for _, eku := range cert.UnknownExtKeyUsage { - if !slices.Contains(TcgDiceExtendedKeyUsages[:], eku.String()) { - unknownKeyUsages = append(unknownKeyUsages, eku.String()) - } - } + certifyKeyParams := []CertifyKeyParams{ + {Label: make([]byte, digestLen), Flags: CertifyKeyFlags(0)}, + {Label: make([]byte, digestLen), Flags: CertifyKeyFlags(CertifyAddIsCA)}, + {Label: seqLabel, Flags: CertifyKeyFlags(0)}, + } - if len(unknownKeyUsagesMap) == 0 { - cert.UnknownExtKeyUsage = []asn1.ObjectIdentifier{} - } else { - unknownKeyUsagesMap[cert.Subject.String()] = unknownKeyUsages - } + for _, params := range certifyKeyParams { + // Get DPE leaf certificate from CertifyKey + certifyKeyResp, err := c.CertifyKey(handle, params.Label, CertifyKeyX509, params.Flags) + if err != nil { + t.Fatalf("[FATAL]: Could not certify key: %v", err) } - } - // The error details in this map will be logged - if len(unknownKeyUsagesMap) > 0 { - for certSubject, ext := range unknownKeyUsagesMap { - t.Errorf("[ERROR]: Certificate \"%s\" has unknown extended key usages \"%s\"", certSubject, ext) + + // Get root and intermediate certificates to validate certificate chain of leaf cert + certChainBytes, err := c.GetCertificateChain() + if err != nil { + t.Fatalf("[FATAL]: Could not get Certificate Chain: %v", err) + } + + leafCertBytes := certifyKeyResp.Certificate + + // Run X.509 linter on full certificate chain and file issues for errors + leafCert := checkCertificateStructure(t, leafCertBytes) + certChain := checkCertificateChain(t, certChainBytes) + + // Check default context handle is unchanged + checkCertifyKeyRespHandle(*certifyKeyResp, t, handle) + + // Check public key and algorithm parameters are correct + checkPubKey(t, profile, leafCert.PublicKey, *certifyKeyResp) + + // Check all extensions + checkCertifyKeyExtensions(t, leafCert.Extensions, params.Flags, params.Label) + + // Ensure full certificate chain has valid signatures + // This also checks certificate lifetime, signatures as part of cert chain validation + if err = validateLeafCertChain(certChain, leafCert); err != nil { + t.Errorf("[ERROR]: %v", err) } - t.Errorf("[ERROR]: Certificate chain validation will fail with non-empty unknown extended key usages list") + + // Reassign handle for simulation mode. + // However, this does not impact in default mode because + // same default context handle is returned in default mode. + handle = &certifyKeyResp.Handle + } + // DeriveChild to add more TCIs and call CertifyKey again. + if simulation { + handle = checkWithDerivedChildContextSimulation(d, c, t, handle) + } else { + checkWithDerivedChildContext(d, c, t, handle) } } -// A tcg-dice-Ueid extension MUST be added -// This SHALL be populated by the LABEL input parameter to CertifyKey -// The extension SHOULD be marked as critical -func checkCertifyKeyTcgUeidExtension(t *testing.T, extensions []pkix.Extension, label []byte) { - t.Helper() +// Checks Multi Tcb Info for context derived from non-simulation mode +// Check CertifyKey command after adding more TCIs by DeriveChild command. +// The MultiTcbInfo extension has a DiceTcbInfo block for each TCI node. +// In a DiceTcbInfo block of a given TCI node, +// - the "type" field must contain 4-byte tciType is provided by a client to DeriveChild. +// - the "fwid" field must contain cumulative TCI measurement. +func checkWithDerivedChildContext(d TestDPEInstance, c DPEClient, t *testing.T, handle *ContextHandle) { + profile, err := GetTransportProfile(d) + if err != nil { + t.Fatalf("Could not get profile: %v", err) + } + digestLen := profile.GetDigestSize() - ueid, err := getUeid(extensions) + var hashAlg asn1.ObjectIdentifier + if digestLen == 32 { + hashAlg = OidSHA256 + } else if digestLen == 48 { + hashAlg = OidSHA384 + } + + childTCI1 := make([]byte, digestLen) + for i := range childTCI1 { + childTCI1[i] = byte(i + 1) + } + + // Set tciType to verify in multiTcbInfo extension + tciType := uint32(2) + + // Preserve parent context to restore for subsequent tests + parentHandle, err := c.RotateContextHandle(handle, RotateContextHandleFlags(0)) if err != nil { - t.Errorf("[ERROR]: tcg-dice-Ueid extension is missing: %v", err) + t.Errorf("[ERROR]: Error while rotating parent context handle, this may cause failure in subsequent tests: %s", err) } - if !reflect.DeepEqual(ueid.Ueid, label) { - // Ueid extn value doen not match the label - t.Errorf("[ERROR]: tcg-dice-Ueid value does not match with the \"Label\" passed in CertifyKeyRequest") + // Deferred call to restore default context handle for subsequent tests + defer func() { + _, err = c.RotateContextHandle(parentHandle, RotateContextHandleFlags(TargetIsDefault)) + if err != nil { + t.Errorf("[ERROR]: Error while restoring parent context handle as default context handle, this may cause failure in subsequent tests: %s", err) + } + }() + + // Derive Child context with input data, tag it and check TCI_CUMULATIVE + childCtx, err := c.DeriveChild(parentHandle, + childTCI1, + DeriveChildFlags(InputAllowX509|RetainParent), + tciType, + 0) + + if err != nil { + t.Fatalf("[FATAL]: Error while creating child handle: %s", err) } -} -// Checks whether certificate extended key usage is as per spec -// OID for ExtendedKeyUsage Extension: 2.5.29.37 -// The ExtendedKeyUsage extension SHOULD be marked as critical -// If IsCA = true, the extension SHOULD contain tcg-dice-kp-eca -// If IsCA = false, the extension SHOULD contain tcg-dice-kp-attestLoc -func checkCertifyKeyExtendedKeyUsages(t *testing.T, extensions []pkix.Extension, ca bool) { - t.Helper() + childHandle := &childCtx.NewContextHandle + parentHandle = &childCtx.ParentContextHandle - extKeyUsage, err := getExtendedKeyUsages(extensions) + // Clean up contexts + defer func() { + err := c.DestroyContext(childHandle, DestroyDescendants) + if err != nil { + t.Errorf("[ERROR]: Error while cleaning up derived context, this may cause failure in subsequent tests: %s", err) + } + }() + + var childTcbInfo DiceTcbInfo + childHandle, childTcbInfo, err = getTcbInfoForHandle(c, childHandle) if err != nil { - t.Errorf("[ERROR]: ExtKeyUsage extension is missing: %v", err) + t.Fatalf("[FATAL]: Could not get TcbInfo: %v", err) } - if len(extKeyUsage) == 0 { - t.Errorf("[ERROR]: The Extended Key Usage extension is empty") + // Check vendorInfo field in multitcb + if err = checkDiceTcbVendorInfo(childTcbInfo, d.GetLocality()); err != nil { + t.Errorf("[ERROR]: %v", err) } - // Iterate over the OIDs in the ExtKeyUsage extension - isExtendedKeyUsageValid := false - var expectedKeyUsage asn1.ObjectIdentifier - expectedKeyUsageName := "" - if ca { - expectedKeyUsage = OidExtensionTcgDiceKpEca - expectedKeyUsageName = "tcg-dice-kp-eca" - } else { - expectedKeyUsage = OidExtensionTcgDiceKpAttestLoc - expectedKeyUsageName = "tcg-dice-kp-attest-loc" + // Check tci type field in multitcb + if err = checkCurrentDiceTcbTciType(childTcbInfo, tciType); err != nil { + t.Errorf("[ERROR]: %v", err) } - for _, oid := range extKeyUsage { - if oid.Equal(expectedKeyUsage) { - isExtendedKeyUsageValid = true - break - } + // Check hash algorithm field in multitcb + if err = checkDiceTcbHashAlgorithm(childTcbInfo, hashAlg); err != nil { + t.Errorf("[ERROR]: %v", err) } - if !isExtendedKeyUsageValid { - t.Errorf("[ERROR]: Certificate has IsCA: %v and does not contain specified key usage: %s", ca, expectedKeyUsageName) + + // Extend TCI support is mandatory for this validation + if !d.GetSupport().ExtendTci { + t.Errorf("ExtendTCI is unsupported by profile, unable to run tests to verify TCI_CUMULATIVE measurement") + return + } + + // Check dice tcb measurements of derived children + // Add one more child context + childTCI2 := make([]byte, digestLen) + for i := range childTCI2 { + childTCI2[i] = byte(i + 2) + } + + childCtx, err = c.DeriveChild(childHandle, + childTCI2, + DeriveChildFlags(InputAllowX509), + tciType, + 0) + + if err != nil { + t.Fatalf("[FATAL]: Error while creating child handle: %s", err) + } + + childHandle = &childCtx.NewContextHandle + + // Get latest TCB information + certifiedKey, err := c.CertifyKey(childHandle, childTCI2, CertifyKeyX509, 0) + if err != nil { + t.Fatalf("[FATAL]: Could not certify key: %s", err) + } + + childHandle = &certifiedKey.Handle + leafCertBytes := certifiedKey.Certificate + + // Build list of tci_current for validation and use it for validating TCI measurements + currentTCIs := [][]byte{childTCI2, childTCI1} + if err = validateDiceTcbFwids(leafCertBytes, currentTCIs, digestLen); err != nil { + t.Errorf("[ERROR]: %v", err) } } -// Checks for KeyUsage Extension as per spec -// If IsCA = true, KeyUsage extension MUST contain DigitalSignature and KeyCertSign -// If IsCA = false, KeyUsage extension MUST contain only DigitalSignature -func checkCertifyKeyExtensions(t *testing.T, extensions []pkix.Extension, flags CertifyKeyFlags, label []byte) { - t.Helper() +// Checks Multi Tcb Info for context derived from simulation mode +func checkWithDerivedChildContextSimulation(d TestDPEInstance, c DPEClient, t *testing.T, handle *ContextHandle) *ContextHandle { + profile, err := GetTransportProfile(d) + if err != nil { + t.Fatalf("Could not get profile: %v", err) + } + digestLen := profile.GetDigestSize() + + var hashAlg asn1.ObjectIdentifier + if digestLen == 32 { + hashAlg = OidSHA256 + } else if digestLen == 48 { + hashAlg = OidSHA384 + } + + childTCI := make([]byte, digestLen) + for i := range childTCI { + childTCI[i] = byte(i) + } + + // Set tciType to verify in UEID extension + tciType := uint32(2) + + locality := d.GetLocality() + + // Derive Child context with input data, tag it and check TCI_CUMULATIVE + childCtx, err := c.DeriveChild(handle, + childTCI, + DeriveChildFlags(InputAllowX509|ChangeLocality|RetainParent), + tciType, + locality+1) // Switch locality to derive child context from Simulation context - bc, err := getBasicConstraints(extensions) if err != nil { - t.Error(err) + t.Fatalf("[FATAL]: Error while creating child handle: %s", err) } - checkCertifyKeyBasicConstraints(t, extensions, flags) - checkCertifyKeyExtendedKeyUsages(t, extensions, bc.IsCA) - checkCertifyKeyTcgUeidExtension(t, extensions, label) + handle = &childCtx.NewContextHandle + parentHandle := &childCtx.ParentContextHandle - // Check MultiTcbInfo Extension structure - _, err = getMultiTcbInfo(extensions) + // Clean up contexts + defer func() { + err := c.DestroyContext(handle, DestroyDescendants) + if err != nil { + t.Errorf("[ERROR]: Error while cleaning up derived context, this may cause failure in subsequent tests: %s", err) + } + // Revert locality for other tests + d.SetLocality(0) + }() + + // Switch to simulated child context locality to issue CertifyKey command + d.SetLocality(locality + 1) + + // Make CertifyKey call and get current MultiTcbInfo + var leafCert *x509.Certificate + + certifiedKey, err := c.CertifyKey(handle, childTCI, CertifyKeyX509, 0) if err != nil { - t.Error(err) + t.Errorf("[ERROR]: Could not certify key: %s", err) } - //Check for keyusage extension - var allowedKeyUsages x509.KeyUsage + handle = &certifiedKey.Handle // Update handle + leafCertBytes := certifiedKey.Certificate - if bc.IsCA { - allowedKeyUsages = x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign - } else { - allowedKeyUsages = x509.KeyUsageDigitalSignature + if leafCert, err = x509.ParseCertificate(leafCertBytes); err != nil { + t.Errorf("[ERROR]: Could not parse certificate: %s", err) } - usage, err := getKeyUsage(extensions) + multiTcbInfo, err := getMultiTcbInfo(leafCert.Extensions) if err != nil { - t.Error(err) + t.Errorf("[ERROR]: Could not parse multi TCB info extension: %s", err) } - certKeyUsageList := getKeyUsageNames(usage) - allowedKeyUsageList := getKeyUsageNames(allowedKeyUsages) - if usage != allowedKeyUsages { - t.Errorf("[ERROR]: Certificate KeyUsage got %v but want %v ", certKeyUsageList, allowedKeyUsageList) + if len(multiTcbInfo) == 0 { + t.Errorf("[ERROR]: Certificate MutliTcbInfo is empty") + } + + childTcbInfo := multiTcbInfo[0] + if !bytes.Equal(childTcbInfo.Fwids[0].Digest, childTCI) { + t.Errorf("[ERROR]: Got current TCI %x, expected %x", childTcbInfo.Fwids[0].Digest, childTCI) + } + + // Preform checks on multi Tcb info of child TCI node + // Check vendorInfo field + if err = checkDiceTcbVendorInfo(childTcbInfo, d.GetLocality()); err != nil { + t.Errorf("[ERROR]: %v", err) + } + + // Check type field + if err = checkCurrentDiceTcbTciType(childTcbInfo, tciType); err != nil { + t.Errorf("[ERROR]: %v", err) + } + + // Check hash algorithm + if err = checkDiceTcbHashAlgorithm(childTcbInfo, hashAlg); err != nil { + t.Errorf("[ERROR]: %v", err) } + // Extend TCI support is mandatory for this validation + if !d.GetSupport().ExtendTci { + t.Errorf("ExtendTCI is unsupported by profile, unable to run tests to verify TCI_CUMULATIVE measurements") + return parentHandle + } + + // Check all dice tcb measurements + // Build list of tci_current for validation + currentTCIs := [][]byte{childTCI} + if err = validateDiceTcbFwids(certifiedKey.Certificate, currentTCIs, digestLen); err != nil { + t.Errorf("[ERROR]: %v", err) + } + return parentHandle } -// Validates basic constraints in certificate returned by CertifyKey command -// against the flag set for input parameter. -// The BasicConstraints extension MUST be included -// If CertifyKey AddIsCA is set, IsCA MUST be set to true. -// If CertifyKey AddIsCA is NOT set, IsCA MUST be set to false -func checkCertifyKeyBasicConstraints(t *testing.T, extensions []pkix.Extension, flags CertifyKeyFlags) { - t.Helper() +func checkPubKey(t *testing.T, p Profile, pubkey any, response CertifiedKey) { + var pubKeyInResponse ecdsa.PublicKey + switch p { + case ProfileP256SHA256: + pubKeyInResponse = ecdsa.PublicKey{ + Curve: elliptic.P256(), + X: new(big.Int).SetBytes(response.Pub.X), + Y: new(big.Int).SetBytes(response.Pub.Y), + } + case ProfileP384SHA384: + pubKeyInResponse = ecdsa.PublicKey{ + Curve: elliptic.P384(), + X: new(big.Int).SetBytes(response.Pub.X), + Y: new(big.Int).SetBytes(response.Pub.Y), + } + default: + t.Errorf("[ERROR]: Unsupported profile %v", p) + } - flagsBuf := &bytes.Buffer{} - binary.Write(flagsBuf, binary.LittleEndian, flags) + ecdsaPub, ok := pubkey.(*ecdsa.PublicKey) + if !ok { + t.Fatal("[FATAL]: Public key is not a ecdsa key") + } - bc, err := getBasicConstraints(extensions) - if err != nil { - t.Error(err) + if !(pubKeyInResponse.Equal(ecdsaPub)) { + t.Errorf("[ERROR]: Public key returned in response must match the Public Key Info in the certificate.") } +} - flagIsCA := CertifyAddIsCA&flags != 0 - if flagIsCA != bc.IsCA { - t.Errorf("[ERROR]: ADD_IS_CA is set to %v but the basic constraint IsCA is set to %v", flagIsCA, bc.IsCA) +// Checks whether the context handle is unchanged after certifyKey command when default context handle is used. +func checkCertifyKeyRespHandle(res CertifiedKey, t *testing.T, handle *ContextHandle) { + if *handle != DefaultContextHandle { + t.Logf("[LOG]: Handle is not default context, skipping check...") + return + } + + if res.Handle != *handle { + t.Errorf("[ERROR]: Handle must be unchanged by CertifyKey, want original handle %v but got %v", handle, res.Handle) } } // Parses X509 certificate func checkCertificateStructure(t *testing.T, certBytes []byte) *x509.Certificate { - t.Helper() failed := false var x509Cert *x509.Certificate @@ -392,13 +517,13 @@ func checkCertificateStructure(t *testing.T, certBytes []byte) *x509.Certificate // Check whether certificate is DER encoded. if x509Cert, err = x509.ParseCertificate(certBytes); err != nil { - t.Fatalf("[FATAL]: Could not parse certificate using crypto/x509: %v", err) + t.Fatalf("Could not parse certificate using crypto/x509: %v", err) } // Parse the cert with zcrypto so we can lint it. cert, err := zx509.ParseCertificate(certBytes) if err != nil { - t.Errorf("[ERROR]: Could not parse certificate using zcrypto/x509: %v", err) + t.Errorf("Could not parse certificate using zcrypto/x509: %v", err) failed = true } @@ -457,197 +582,124 @@ func checkCertificateStructure(t *testing.T, certBytes []byte) *x509.Certificate return x509Cert } -func testCertifyKey(d TestDPEInstance, c DPEClient, t *testing.T, simulation bool) { - handle := getInitialContextHandle(d, c, t, simulation) - defer func() { - if simulation { - c.DestroyContext(handle, DestroyDescendants) - } - }() +// A tcg-dice-Ueid extension MUST be added +// This SHALL be populated by the LABEL input parameter to CertifyKey +// The extension SHOULD be marked as critical +func checkCertifyKeyTcgUeidExtension(t *testing.T, extensions []pkix.Extension, label []byte) { + t.Helper() - profile, err := GetTransportProfile(d) + ueid, err := getUeid(extensions) if err != nil { - t.Fatalf("Could not get profile: %v", err) - } - digestLen := profile.GetDigestSize() - - seqLabel := make([]byte, digestLen) - for i := range seqLabel { - seqLabel[i] = byte(i) - } - - certifyKeyParams := []CertifyKeyParams{ - {Label: make([]byte, digestLen), Flags: CertifyKeyFlags(0)}, - {Label: seqLabel, Flags: CertifyKeyFlags(0)}, + t.Errorf("[ERROR]: tcg-dice-Ueid extension is missing: %v", err) } - for _, params := range certifyKeyParams { - // Get DPE leaf certificate from CertifyKey - certifyKeyResp, err := c.CertifyKey(handle, params.Label, CertifyKeyX509, params.Flags) - if err != nil { - t.Fatalf("[FATAL]: Could not certify key: %v", err) - } - - // Get root and intermediate certificates to validate certificate chain of leaf cert - certChainBytes, err := c.GetCertificateChain() - if err != nil { - t.Fatalf("[FATAL]: Could not get Certificate Chain: %v", err) - } - - leafCertBytes := certifyKeyResp.Certificate - - // Run X.509 linter on full certificate chain and file issues for errors - leafCert := checkCertificateStructure(t, leafCertBytes) - certChain := checkCertificateChain(t, certChainBytes) - - // Check default context handle is unchanged - checkCertifyKeyRespHandle(*certifyKeyResp, t, handle) - - // Check public key and algorithm parameters are correct - checkPubKey(t, profile, leafCert.PublicKey, *certifyKeyResp) - - // Check all extensions - checkCertifyKeyExtensions(t, leafCert.Extensions, params.Flags, params.Label) - - // Ensure full certificate chain has valid signatures - // This also checks certificate lifetime, signatures as part of cert chain validation - validateLeafCertChain(t, certChain, leafCert) - - // Reassign handle for simulation mode. - // However, this does not impact in default mode because - // same default context handle is returned in default mode. - handle = &certifyKeyResp.Handle + if !reflect.DeepEqual(ueid.Ueid, label) { + // Ueid extn value doen not match the label + t.Errorf("[ERROR]: tcg-dice-Ueid value does not match with the \"Label\" passed in CertifyKeyRequest") } - // TODO: When DeriveChild is implemented, call it here to add more TCIs and call CertifyKey again. } -// Builds and verifies certificate chain. -func validateLeafCertChain(t *testing.T, certChain []*x509.Certificate, leafCert *x509.Certificate) { +// Checks whether certificate extended key usage is as per spec +// OID for ExtendedKeyUsage Extension: 2.5.29.37 +// The ExtendedKeyUsage extension SHOULD be marked as critical +// If IsCA = true, the extension SHOULD contain tcg-dice-kp-eca +// If IsCA = false, the extension SHOULD contain tcg-dice-kp-attestLoc +func checkCertifyKeyExtendedKeyUsages(t *testing.T, extensions []pkix.Extension, ca bool) { t.Helper() - certsToProcess := []*x509.Certificate{leafCert} - - // Remove unhandled critical extensions and EKUs by x509 but defined in spec - removeTcgDiceCriticalExtensions(t, certsToProcess) - removeTcgDiceExtendedKeyUsages(t, certsToProcess) - // Certificate chain validation for leaf - opts := buildVerifyOptions(t, certChain) - chains, err := leafCert.Verify(opts) + extKeyUsage, err := getExtendedKeyUsages(extensions) if err != nil { - t.Errorf("[ERROR]: Error verifying DPE leaf: %s", err.Error()) + t.Errorf("[ERROR]: ExtKeyUsage extension is missing: %v", err) } - // Log certificate chains linked to leaf - if len(chains) != 1 { - t.Errorf("[ERROR]: Unexpected number of cert chains: %d", len(chains)) + if len(extKeyUsage) == 0 { + t.Errorf("[ERROR]: The Extended Key Usage extension is empty") } -} - -// Builds Certificate chain verifier parameters. -func buildVerifyOptions(t *testing.T, certChain []*x509.Certificate) x509.VerifyOptions { - roots := x509.NewCertPool() - intermediates := x509.NewCertPool() - // Root certificate is expected to be in the beginning of the chain, the rest are expected to be intermediates. - roots.AddCert(certChain[0]) + // Iterate over the OIDs in the ExtKeyUsage extension + isExtendedKeyUsageValid := false + var expectedKeyUsage asn1.ObjectIdentifier + expectedKeyUsageName := "" + if ca { + expectedKeyUsage = OidExtensionTcgDiceKpEca + expectedKeyUsageName = "tcg-dice-kp-eca" + } else { + expectedKeyUsage = OidExtensionTcgDiceKpAttestLoc + expectedKeyUsageName = "tcg-dice-kp-attest-loc" + } - for _, cert := range certChain[1:] { - if cert.Subject.String() == cert.Issuer.String() { - t.Errorf("[ERROR]: Found a self-signed certificate in middle of certificate chain returned by GetCertificateChain.") - continue + for _, oid := range extKeyUsage { + if oid.Equal(expectedKeyUsage) { + isExtendedKeyUsageValid = true + break } - intermediates.AddCert(cert) } - opts := x509.VerifyOptions{ - Roots: roots, - Intermediates: intermediates, - CurrentTime: time.Now().UTC(), - KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, + if !isExtendedKeyUsageValid { + t.Errorf("[ERROR]: Certificate has IsCA: %v and does not contain specified key usage: %s", ca, expectedKeyUsageName) } - - return opts } -// Gets KeyUsage bitmap and returns as list of KeyUsage name strings. -func getKeyUsageNames(keyUsage x509.KeyUsage) []string { - keyUsageNames := []string{} - - if keyUsage&x509.KeyUsageDigitalSignature != 0 { - keyUsageNames = append(keyUsageNames, "DigitalSignature") - } - - if keyUsage&x509.KeyUsageContentCommitment != 0 { - keyUsageNames = append(keyUsageNames, "ContentCommitment") - } +// // Checks for KeyUsage Extension as per spec +// // If IsCA = true, KeyUsage extension MUST contain DigitalSignature and KeyCertSign +// // If IsCA = false, KeyUsage extension MUST contain only DigitalSignature +func checkCertifyKeyExtensions(t *testing.T, extensions []pkix.Extension, flags CertifyKeyFlags, label []byte) { + t.Helper() - if keyUsage&x509.KeyUsageKeyEncipherment != 0 { - keyUsageNames = append(keyUsageNames, "KeyEncipherment") + bc, err := getBasicConstraints(extensions) + if err != nil { + t.Error(err) } - if keyUsage&x509.KeyUsageDataEncipherment != 0 { - keyUsageNames = append(keyUsageNames, "DataEncipherment") - } + checkCertifyKeyBasicConstraints(t, extensions, flags) + checkCertifyKeyExtendedKeyUsages(t, extensions, bc.IsCA) + checkCertifyKeyTcgUeidExtension(t, extensions, label) - if keyUsage&x509.KeyUsageKeyAgreement != 0 { - keyUsageNames = append(keyUsageNames, "KeyAgreement") + // Check MultiTcbInfo Extension structure + _, err = getMultiTcbInfo(extensions) + if err != nil { + t.Error(err) } - if keyUsage&x509.KeyUsageCertSign != 0 { - keyUsageNames = append(keyUsageNames, "CertSign") - } + //Check for keyusage extension + var allowedKeyUsages x509.KeyUsage - if keyUsage&x509.KeyUsageCRLSign != 0 { - keyUsageNames = append(keyUsageNames, "CRLSign") + if bc.IsCA { + allowedKeyUsages = x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign + } else { + allowedKeyUsages = x509.KeyUsageDigitalSignature } - if keyUsage&x509.KeyUsageEncipherOnly != 0 { - keyUsageNames = append(keyUsageNames, "EncipherOnly") + usage, err := getKeyUsage(extensions) + if err != nil { + t.Error(err) } - if keyUsage&x509.KeyUsageDecipherOnly != 0 { - keyUsageNames = append(keyUsageNames, "DecipherOnly") + certKeyUsageList := getKeyUsageNames(usage) + allowedKeyUsageList := getKeyUsageNames(allowedKeyUsages) + if usage != allowedKeyUsages { + t.Errorf("[ERROR]: Certificate KeyUsage got %v but want %v ", certKeyUsageList, allowedKeyUsageList) } - return keyUsageNames } -func checkPubKey(t *testing.T, p Profile, pubkey any, response CertifiedKey) { - var pubKeyInResponse ecdsa.PublicKey - switch p { - case ProfileP256SHA256: - pubKeyInResponse = ecdsa.PublicKey{ - Curve: elliptic.P256(), - X: new(big.Int).SetBytes(response.Pub.X), - Y: new(big.Int).SetBytes(response.Pub.Y), - } - case ProfileP384SHA384: - pubKeyInResponse = ecdsa.PublicKey{ - Curve: elliptic.P384(), - X: new(big.Int).SetBytes(response.Pub.X), - Y: new(big.Int).SetBytes(response.Pub.Y), - } - default: - t.Errorf("[ERROR]: Unsupported profile %v", p) - } - - ecdsaPub, ok := pubkey.(*ecdsa.PublicKey) - if !ok { - t.Fatal("[FATAL]: Public key is not a ecdsa key") - } +// Validates basic constraints in certificate returned by CertifyKey command +// against the flag set for input parameter. +// The BasicConstraints extension MUST be included +// If CertifyKey AddIsCA is set, IsCA MUST be set to true. +// If CertifyKey AddIsCA is NOT set, IsCA MUST be set to false +func checkCertifyKeyBasicConstraints(t *testing.T, extensions []pkix.Extension, flags CertifyKeyFlags) { + t.Helper() - if !(pubKeyInResponse.Equal(ecdsaPub)) { - t.Errorf("[ERROR]: Public key returned in response must match the Public Key Info in the certificate.") - } -} + flagsBuf := &bytes.Buffer{} + binary.Write(flagsBuf, binary.LittleEndian, flags) -// Checks whether the context handle is unchanged after certifyKey command when default context handle is used. -func checkCertifyKeyRespHandle(res CertifiedKey, t *testing.T, handle *ContextHandle) { - if *handle != DefaultContextHandle { - t.Logf("[LOG]: Handle is not default context, skipping check...") - return + bc, err := getBasicConstraints(extensions) + if err != nil { + t.Error(err) } - if res.Handle != *handle { - t.Errorf("[ERROR]: Handle must be unchanged by CertifyKey, want original handle %v but got %v", handle, res.Handle) + flagIsCA := CertifyAddIsCA&flags != 0 + if flagIsCA != bc.IsCA { + t.Errorf("[ERROR]: ADD_IS_CA is set to %v but the basic constraint IsCA is set to %v", flagIsCA, bc.IsCA) } } diff --git a/verification/certs.go b/verification/certs.go index 8a7d3845..d26d34f6 100644 --- a/verification/certs.go +++ b/verification/certs.go @@ -3,13 +3,17 @@ package verification import ( + "bytes" "crypto/x509" "crypto/x509/pkix" "encoding/asn1" + "encoding/binary" "fmt" + "time" + + "golang.org/x/exp/slices" ) -// This file is used to test the certify key command. var ( OidExtensionKeyUsage = asn1.ObjectIdentifier{2, 5, 29, 15} OidExtensionAuthorityKeyIdentifier = asn1.ObjectIdentifier{2, 5, 29, 35} @@ -28,8 +32,87 @@ var ( OidSHA384 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 2} ) +var TcgDiceCriticalExtensions = [...]string{ + OidExtensionTcgDiceMultiTcbInfo.String(), + OidExtensionTcgDiceUeid.String(), +} + +var TcgDiceExtendedKeyUsages = [...]string{ + OidExtensionTcgDiceKpIdentityLoc.String(), + OidExtensionTcgDiceKpAttestLoc.String(), +} + +// tcg-dice-Ueid OBJECT IDENTIFIER ::= {tcg-dice 4} +// +// TcgUeid ::== SEQUENCE { +// ueid OCTET STRING +// } +type TcgUeidExtension struct { + Ueid []uint8 `asn1:"ueid,implicit"` +} + +// tcg-dice-MultiTcbInfo OBJECT IDENTIFIER ::= {tcg-dice 5} +// DiceTcbInfoSeq ::= SEQUENCE SIZE (1..MAX) OF DiceTcbInfo +// +// tcg-dice-TcbInfo OBJECT IDENTIFIER ::= {tcg-dice 1} +// +// DiceTcbInfo ::== SEQUENCE { +// vendor [0] IMPLICIT UTF8String OPTIONAL, +// model [1] IMPLICIT UTF8String OPTIONAL, +// version [2] IMPLICIT UTF8String OPTIONAL, +// svn [3] IMPLICIT INTEGER OPTIONAL, +// layer [4] IMPLICIT INTEGER OPTIONAL, +// index [5] IMPLICIT INTEGER OPTIONAL, +// fwids [6] IMPLICIT FWIDLIST OPTIONAL, +// flags [7] IMPLICIT OperationalFlags OPTIONAL, +// vendorInfo [8] IMPLICIT OCTET STRING OPTIONAL, +// type [9] IMPLICIT OCTET STRING OPTIONAL, +// } +// +// FWIDLIST ::== SEQUENCE SIZE (1..MAX) OF FWID +// FWID ::== SEQUENCE { +// hashAlg OBJECT IDENTIFIER, +// digest OCTET STRING +// } +// +// OperationalFlags ::= BIT STRING { +// notConfigured (0), +// notSecure (1), +// recovery (2), +// debug (3) +// } + +type Fwid struct { + HashAlg asn1.ObjectIdentifier + Digest []byte +} + +type DiceTcbInfo struct { + Vendor string `asn1:"optional,tag:0,utf8"` + Model string `asn1:"optional,tag:1,utf8"` + Version string `asn1:"optional,tag:2,utf8"` + SVN int `asn1:"optional,tag:3"` + Layer int `asn1:"optional,tag:4"` + Index int `asn1:"optional,tag:5"` + Fwids []Fwid `asn1:"optional,tag:6"` + Flags OperationalFlag `asn1:"optional,tag:7"` + VendorInfo []byte `asn1:"optional,tag:8"` + Type []byte `asn1:"optional,tag:9"` +} + +type OperationalFlag int + +const ( + NotConfigured OperationalFlag = iota + NotSecure + Debug + Recovery +) + +type TcgMultiTcbInfo = []DiceTcbInfo + type BasicConstraints struct { - IsCA bool `asn1` + IsCA bool `asn1:"boolean"` PathLenConstraint int `asn1:"optional"` } @@ -40,11 +123,11 @@ func getMultiTcbInfo(extensions []pkix.Extension) (TcgMultiTcbInfo, error) { for _, ext := range extensions { if ext.Id.Equal(OidExtensionTcgDiceMultiTcbInfo) { if !ext.Critical { - return multiTcbInfo, fmt.Errorf("[ERROR]: TCG DICE MultiTcbInfo extension is not marked as CRITICAL") + return multiTcbInfo, fmt.Errorf("TCG DICE MultiTcbInfo extension is not marked as CRITICAL") } _, err := asn1.Unmarshal(ext.Value, &multiTcbInfo) if err != nil { - return multiTcbInfo, fmt.Errorf("[ERROR]: Failed to unmarshal MultiTcbInfo field: %v", err) + return multiTcbInfo, fmt.Errorf("Failed to unmarshal MultiTcbInfo field: %v", err) } break } @@ -57,11 +140,11 @@ func getBasicConstraints(extensions []pkix.Extension) (BasicConstraints, error) for _, ext := range extensions { if ext.Id.Equal(OidExtensionBasicConstraints) { if !ext.Critical { - return bc, fmt.Errorf("[ERROR]: BasicConstraints extension is not marked as CRITICAL") + return bc, fmt.Errorf("BasicConstraints extension is not marked as CRITICAL") } _, err := asn1.Unmarshal(ext.Value, &bc) if err != nil { - return bc, fmt.Errorf("[ERROR]: Failed to unmarshal BasicConstraints extension: %v", err) + return bc, fmt.Errorf("Failed to unmarshal BasicConstraints extension: %v", err) } break } @@ -74,11 +157,11 @@ func getUeid(extensions []pkix.Extension) (TcgUeidExtension, error) { for _, ext := range extensions { if ext.Id.Equal(OidExtensionTcgDiceUeid) { if !ext.Critical { - return ueid, fmt.Errorf("[ERROR]: UEID extension is not marked as CRITICAL") + return ueid, fmt.Errorf("UEID extension is not marked as CRITICAL") } _, err := asn1.Unmarshal(ext.Value, &ueid) if err != nil { - return ueid, fmt.Errorf("[ERROR]: Failed to unmarshal UEID extension: %v", err) + return ueid, fmt.Errorf("Failed to unmarshal UEID extension: %v", err) } break } @@ -91,11 +174,11 @@ func getExtendedKeyUsages(extensions []pkix.Extension) ([]asn1.ObjectIdentifier, for _, ext := range extensions { if ext.Id.Equal(OidExtensionExtKeyUsage) { if !ext.Critical { - return eku, fmt.Errorf("[ERROR]: ExtKeyUsage extension is not marked as CRITICAL") + return eku, fmt.Errorf("ExtKeyUsage extension is not marked as CRITICAL") } _, err := asn1.Unmarshal(ext.Value, &eku) if err != nil { - return eku, fmt.Errorf("[ERROR]: Failed to unmarshal ExtKeyUsage extension: %v", err) + return eku, fmt.Errorf("Failed to unmarshal ExtKeyUsage extension: %v", err) } break } @@ -108,11 +191,11 @@ func getKeyUsage(extensions []pkix.Extension) (x509.KeyUsage, error) { for _, ext := range extensions { if ext.Id.Equal(OidExtensionKeyUsage) { if !ext.Critical { - return x509.KeyUsage(0), fmt.Errorf("[ERROR]: KeyUsage extension is not marked as CRITICAL") + return x509.KeyUsage(0), fmt.Errorf("KeyUsage extension is not marked as CRITICAL") } _, err := asn1.Unmarshal(ext.Value, &usageBits) if err != nil { - return x509.KeyUsage(0), fmt.Errorf("[ERROR]: Failed to unmarshal KeyUsage extension: %v", err) + return x509.KeyUsage(0), fmt.Errorf("Failed to unmarshal KeyUsage extension: %v", err) } break } @@ -166,3 +249,274 @@ func getTcbInfoForHandle(c DPEClient, handle *ContextHandle) (*ContextHandle, Di return outHandle, multiTcbInfo[0], nil } + +// Removes the critical extensions that are unknown to x509 package +// but defined in DPE certificate profile specification for cert chain validation +// UnhandledCriticalExtensions may have only custom extensions mentioned in spec +// unknownExtnMap collects extensions unknown to both x509 and the DICE certificate profiles spec +// positive case expects the unknownExtnMap to be empty. +func removeTcgDiceCriticalExtensions(certs []*x509.Certificate) error { + unknownExtnMap := map[string][]string{} + for _, cert := range certs { + if len(cert.UnhandledCriticalExtensions) > 0 { + unknownExtns := []string{} + for _, extn := range cert.UnhandledCriticalExtensions { + if !slices.Contains(TcgDiceCriticalExtensions[:], extn.String()) { + unknownExtns = append(unknownExtns, extn.String()) + } + } + + if len(unknownExtnMap) == 0 { + cert.UnhandledCriticalExtensions = []asn1.ObjectIdentifier{} + } else { + unknownExtnMap[cert.Subject.String()] = unknownExtns + } + } + } + // The error details in this map will be logged + msg := "" + if len(unknownExtnMap) > 0 { + for certSubject, ext := range unknownExtnMap { + msg += fmt.Errorf("Certificate \"%s\" has unhandled critical extension \"%s\"", certSubject, ext).Error() + } + return fmt.Errorf("%s", msg) + } + return nil +} + +// Ignores extended key usages that are unknown to x509 package +// but atleast defined in DPE certificate profile specification for cert chain validation +// UnhandledExtendedKeyUsages may have only custom key usages mentioned in spec +// unknownKeyUsagesMap collects keyusages unknown to both x509 and the DICE certificate profiles spec +// positive case expects the unknownKeyUsagesMap to be empty. +func removeTcgDiceExtendedKeyUsages(certs []*x509.Certificate) error { + unknownKeyUsagesMap := map[string][]string{} + for _, cert := range certs { + if len(cert.UnknownExtKeyUsage) > 0 { + unknownKeyUsages := []string{} + for _, eku := range cert.UnknownExtKeyUsage { + if !slices.Contains(TcgDiceExtendedKeyUsages[:], eku.String()) { + unknownKeyUsages = append(unknownKeyUsages, eku.String()) + } + } + + if len(unknownKeyUsagesMap) == 0 { + cert.UnknownExtKeyUsage = []asn1.ObjectIdentifier{} + } else { + unknownKeyUsagesMap[cert.Subject.String()] = unknownKeyUsages + } + } + } + // The error details in this map will be logged + msg := "" + if len(unknownKeyUsagesMap) > 0 { + for certSubject, ext := range unknownKeyUsagesMap { + msg += fmt.Errorf("Certificate \"%s\" has unhandled critical extension \"%s\"", certSubject, ext).Error() + } + return fmt.Errorf("%s", msg) + } + return nil +} + +// Checks whether the VendorInfo is 4-bytes TARGET_LOCALITY parameter +func checkDiceTcbVendorInfo(currentTcbInfo DiceTcbInfo, targetLocality uint32) error { + var err error + expectedVendorInfo := make([]byte, 4) + binary.BigEndian.PutUint32(expectedVendorInfo, targetLocality) + if !bytes.Equal(currentTcbInfo.VendorInfo, expectedVendorInfo) { + err = fmt.Errorf("Unexpected VendorInfo for current DICE TCB block, want %v but got %v", expectedVendorInfo, currentTcbInfo.VendorInfo) + } + return err +} + +// Checks whether INPUT_TYPE passed to a deriveChild Request +// populates the "type" field in the DiceTcbInfo extension. +func checkCurrentDiceTcbTciType(currentTcbInfo DiceTcbInfo, expectedTciType uint32) error { + var err error + expectedTciTypeBytes := make([]byte, 4) + binary.BigEndian.PutUint32(expectedTciTypeBytes, expectedTciType) + if !bytes.Equal(currentTcbInfo.Type, expectedTciTypeBytes) { + err = fmt.Errorf("Unexpected TCI type for current DICE TCB block, want %v but got %v", expectedTciTypeBytes, currentTcbInfo.Type) + } + return err +} + +// Checks whether the Hash Algorithm field in FWID block is correct +func checkDiceTcbHashAlgorithm(currentTcbInfo DiceTcbInfo, hashAlg asn1.ObjectIdentifier) error { + for _, fwid := range currentTcbInfo.Fwids { + if !fwid.HashAlg.Equal(hashAlg) { + return fmt.Errorf("Unexpected hash algorithm in FWID block, expected %s but got %s", hashAlg, fwid.HashAlg) + } + } + return nil +} + +// Builds and verifies certificate chain. +func validateLeafCertChain(certChain []*x509.Certificate, leafCert *x509.Certificate) error { + var err error + certsToProcess := []*x509.Certificate{leafCert} + + // Remove unhandled critical extensions reported by x509 but defined in spec + if err = removeTcgDiceCriticalExtensions(certsToProcess); err != nil { + return err + } + + // Remove unhandled extended key usages reported by x509 but defined in spec + if err = removeTcgDiceExtendedKeyUsages(certsToProcess); err != nil { + return err + } + + // Build verify options + var opts *x509.VerifyOptions + if opts, err = buildVerifyOptions(certChain); err != nil { + return err + } + + // Certificate chain validation for leaf + chains, err := leafCert.Verify(*opts) + if err != nil { + // Unable to build certificate chain from leaf to root + return fmt.Errorf("Error verifying DPE leaf: %s", err.Error()) + } + + // Log certificate chains linked to leaf + if len(chains) != 1 { + return fmt.Errorf("Unexpected number of cert chains: %d", len(chains)) + } + return nil +} + +// Builds Certificate chain verifier parameters. +func buildVerifyOptions(certChain []*x509.Certificate) (*x509.VerifyOptions, error) { + var err error + roots := x509.NewCertPool() + intermediates := x509.NewCertPool() + + // Root certificate is expected to be in the beginning of the chain, the rest are expected to be intermediates. + roots.AddCert(certChain[0]) + + for _, cert := range certChain[1:] { + if cert.Subject.String() == cert.Issuer.String() { + return nil, fmt.Errorf("Found a self-signed certificate in middle of certificate chain returned by GetCertificateChain") + } + intermediates.AddCert(cert) + } + opts := x509.VerifyOptions{ + Roots: roots, + Intermediates: intermediates, + CurrentTime: time.Now().UTC(), + KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, + } + + return &opts, err +} + +// Gets KeyUsage bitmap and returns as list of KeyUsage name strings. +func getKeyUsageNames(keyUsage x509.KeyUsage) []string { + keyUsageNames := []string{} + + if keyUsage&x509.KeyUsageDigitalSignature != 0 { + keyUsageNames = append(keyUsageNames, "DigitalSignature") + } + + if keyUsage&x509.KeyUsageContentCommitment != 0 { + keyUsageNames = append(keyUsageNames, "ContentCommitment") + } + + if keyUsage&x509.KeyUsageKeyEncipherment != 0 { + keyUsageNames = append(keyUsageNames, "KeyEncipherment") + } + + if keyUsage&x509.KeyUsageDataEncipherment != 0 { + keyUsageNames = append(keyUsageNames, "DataEncipherment") + } + + if keyUsage&x509.KeyUsageKeyAgreement != 0 { + keyUsageNames = append(keyUsageNames, "KeyAgreement") + } + + if keyUsage&x509.KeyUsageCertSign != 0 { + keyUsageNames = append(keyUsageNames, "CertSign") + } + + if keyUsage&x509.KeyUsageCRLSign != 0 { + keyUsageNames = append(keyUsageNames, "CRLSign") + } + + if keyUsage&x509.KeyUsageEncipherOnly != 0 { + keyUsageNames = append(keyUsageNames, "EncipherOnly") + } + + if keyUsage&x509.KeyUsageDecipherOnly != 0 { + keyUsageNames = append(keyUsageNames, "DecipherOnly") + } + + return keyUsageNames +} + +// Verifies the TCI_Current and TCI_Cumulative of dice tcb information blocks +func verifyDiceTcbDigest(tcbInfo DiceTcbInfo, wantCurrentTCI []byte, lastCumulativeTCI []byte) error { + var err error + + // Check TCI_CURRENT + currentTCI := tcbInfo.Fwids[0].Digest + if !bytes.Equal(currentTCI, wantCurrentTCI) { + err = fmt.Errorf("Unexpected TCI_CURRENT digest, want %v but got %v", wantCurrentTCI, currentTCI) + } + + // Check TCI_CUMULATIVE against expected cumulative TCI + wantCumulativeTCI := computeExpectedCumulative(lastCumulativeTCI, currentTCI) + cumulativeTCI := tcbInfo.Fwids[1].Digest + if !bytes.Equal(cumulativeTCI, wantCumulativeTCI) { + err = fmt.Errorf("Unexpected TCI_CUMULATIVE value, want %v but got %v", wantCumulativeTCI, cumulativeTCI) + } + return err +} + +// Checks the FWID block's Digest. +// FWID at index 0 has the TCI_CURRENT as digest +// FWID at index 1 has the TCI_CUMULATIVE as digest +// FWID array always has two digest/hashAlg blocks when "ExtendTci" is supported by DPE profile. +func validateDiceTcbFwids(leafCertBytes []byte, currentTcis [][]byte, digestLen int) error { + var leafCert *x509.Certificate + var err error + + // Check whether certificate is DER encoded. + if leafCert, err = x509.ParseCertificate(leafCertBytes); err != nil { + return err + } + + // Get DICE information from MultiTcbInfo Extension + var multiTcbInfo []DiceTcbInfo + if multiTcbInfo, err = getMultiTcbInfo(leafCert.Extensions); err != nil { + return err + } + + if len(multiTcbInfo) == 0 { + return fmt.Errorf("Certificate MutliTcbInfo is empty") + } + + // Calculate expected cumulative value + defaultTci := make([]byte, digestLen) + + // Check cumulative, current TCI at the last index of multitcb info + // It must have default TCI value + lastIndex := len(multiTcbInfo) - 1 + if !bytes.Equal(multiTcbInfo[lastIndex].Fwids[0].Digest, defaultTci) { + return fmt.Errorf("Current TCI value for first TCB block, want %v but got %v", defaultTci, multiTcbInfo[lastIndex].Fwids[0].Digest) + } + + if !bytes.Equal(multiTcbInfo[lastIndex].Fwids[1].Digest, defaultTci) { + return fmt.Errorf("Cumulative TCI value for first TCB block, want %v but got %v", defaultTci, multiTcbInfo[lastIndex].Fwids[1].Digest) + } + + // Check cumulative, current TCI of other indices if any + lastCumulativeTCI := defaultTci + multiTcbInfo = multiTcbInfo[:lastIndex] + + for i, tcbinfo := range multiTcbInfo { + wantCurrentTci := currentTcis[i] + verifyDiceTcbDigest(tcbinfo, wantCurrentTci, lastCumulativeTCI) + } + return err +} diff --git a/verification/extendTCI.go b/verification/extendTCI.go index e46d1cdb..aa3a5c9f 100644 --- a/verification/extendTCI.go +++ b/verification/extendTCI.go @@ -35,7 +35,7 @@ func TestExtendTCI(d TestDPEInstance, c DPEClient, t *testing.T) { if err != nil { t.Fatal(err) } - lastCumulative := tcbInfo.Fwids[1].Digest + lastCumulativeTCI := tcbInfo.Fwids[1].Digest // Set current TCI value _, err = c.ExtendTCI(handle, tciValue) @@ -43,9 +43,17 @@ func TestExtendTCI(d TestDPEInstance, c DPEClient, t *testing.T) { t.Fatalf("[FATAL]: Could not extend TCI: %v", err) } - // Check current and cumulative measurement by CertifyKey - expectedCumulative := computeExpectedCumulative(lastCumulative, tciValue) - verifyMeasurements(c, t, handle, tciValue, expectedCumulative) + // Refresh TCB info + _, tcbInfo, err = getTcbInfoForHandle(c, handle) + if err != nil { + t.Fatal(err) + } + + // Check current and cumulative measurement in DiceTcb info block + wantCurrentTCI := tciValue + if err = verifyDiceTcbDigest(tcbInfo, wantCurrentTCI, lastCumulativeTCI); err != nil { + t.Errorf("[ERROR]: %v", err) + } } func computeExpectedCumulative(lastCumulative []byte, tciValue []byte) []byte { @@ -153,21 +161,3 @@ func TestExtendTciOnDerivedContexts(d TestDPEInstance, c DPEClient, t *testing.T t.Errorf("[ERROR]: Child node's cumulative TCI %x, expected %x", childTcbInfo.Fwids[1].Digest, wantCumulativeTCI) } } - -func verifyMeasurements(c DPEClient, t *testing.T, handle *ContextHandle, expectedCurrent []byte, expectedCumulative []byte) { - handle, tcbInfo, err := getTcbInfoForHandle(c, handle) - if err != nil { - t.Fatal(err) - } - - // Check that the last TcbInfo current/cumulative are as expected - current := tcbInfo.Fwids[0].Digest - cumulative := tcbInfo.Fwids[1].Digest - if !bytes.Equal(current, expectedCurrent) { - t.Errorf("[ERROR]: Unexpected TCI_CURRENT digest, want %v but got %v", expectedCurrent, current) - } - - if !bytes.Equal(cumulative, expectedCumulative) { - t.Errorf("[ERROR]: Unexpected cumulative TCI value, want %v but got %v", expectedCumulative, cumulative) - } -} diff --git a/verification/getCertificateChain.go b/verification/getCertificateChain.go index 362ed85c..66f8662e 100644 --- a/verification/getCertificateChain.go +++ b/verification/getCertificateChain.go @@ -102,21 +102,29 @@ func checkCertificateChain(t *testing.T, certData []byte) []*x509.Certificate { // Build certificate chain and calls to validateSignature on each chain. func validateCertChain(t *testing.T, certChain []*x509.Certificate) { t.Helper() + var err error certsToProcess := certChain // Remove unhandled critical extensions reported by x509 but defined in spec - removeTcgDiceCriticalExtensions(t, certsToProcess) + if err = removeTcgDiceCriticalExtensions(certsToProcess); err != nil { + t.Errorf("[ERROR]: %v", err) + } // Remove unhandled extended key usages reported by x509 but defined in spec - removeTcgDiceExtendedKeyUsages(t, certsToProcess) + if err = removeTcgDiceExtendedKeyUsages(certsToProcess); err != nil { + t.Errorf("[ERROR]: %v", err) + } // Build verify options - opts := buildVerifyOptions(t, certChain) + opts, err := buildVerifyOptions(certChain) + if err != nil { + t.Errorf("[ERROR]: %v", err) + } // Certificate chain validation for each intermediate certificate for _, cert := range certChain { - chains, err := cert.Verify(opts) + chains, err := cert.Verify(*opts) if err != nil { t.Errorf("[ERROR]: Error in Certificate Chain of %s: %s", cert.Subject, err.Error()) } diff --git a/verification/simulator.go b/verification/simulator.go index 8307ba27..991ced9e 100644 --- a/verification/simulator.go +++ b/verification/simulator.go @@ -234,6 +234,11 @@ func GetSimulatorTargets() []TestTarget { getTestTarget([]string{"AutoInit", "Simulation", "X509", "Csr", "IsCA", "RotateContext", "ExtendTci", "IsSymmetric"}), AllTestCases, }, + { + "CertifyKey_WithoutExtendTci", + getTestTarget([]string{"AutoInit", "X509"}), + []TestCase{CertifyKeyWithoutExtendTciTestCase}, + }, { "GetProfile_Simulation", getTestTarget([]string{"Simulation"}), diff --git a/verification/verification.go b/verification/verification.go index f64eb6f5..9437f1e4 100644 --- a/verification/verification.go +++ b/verification/verification.go @@ -27,14 +27,17 @@ var InitializeContextSimulationTestCase = TestCase{ "InitializeContextSimulation", TestInitializeSimulation, []string{"Simulation"}, } var CertifyKeyTestCase = TestCase{ - "CertifyKey", TestCertifyKey, []string{"AutoInit", "X509", "IsCA"}, + "CertifyKey", TestCertifyKey, []string{"AutoInit", "X509", "IsCA", "RotateContext", "ExtendTci"}, +} +var CertifyKeySimulationTestCase = TestCase{ + "CertifyKeySimulation", TestCertifyKeySimulation, []string{"AutoInit", "Simulation", "X509", "IsCA", "RotateContext", "ExtendTci"}, +} +var CertifyKeyWithoutExtendTciTestCase = TestCase{ + "CertifyKeyWithoutExtendTciSupport", TestCertifyKeyWithoutExtendTciSupport, []string{"AutoInit", "X509"}, } var CertifyKeyCsrTestCase = TestCase{ "CertifyKeyCsr", TestCertifyKey_Csr, []string{"AutoInit", "Csr", "IsCA"}, } -var CertifyKeySimulationTestCase = TestCase{ - "CertifyKeySimulation", TestCertifyKeySimulation, []string{"AutoInit", "Simulation", "X509", "IsCA"}, -} var GetCertificateChainTestCase = TestCase{ "GetCertificateChain", TestGetCertificateChain, []string{"AutoInit", "X509"}, } From 6e1a959a32689c7f248219fbb49d546f69fb7c1f Mon Sep 17 00:00:00 2001 From: hpya93 Date: Tue, 19 Dec 2023 22:47:32 +0530 Subject: [PATCH 2/2] . --- Cargo.lock | 161 +++++++++++++++++++++++++ dpe/fuzz/Cargo.lock | 74 ++++++++++++ verification/abi.go | 1 + verification/certifyKey.go | 226 +++++------------------------------ verification/simulator.go | 6 +- verification/verification.go | 13 +- 6 files changed, 280 insertions(+), 201 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 81df4f30..b9d7d82c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -134,6 +134,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" version = "0.21.5" @@ -275,15 +281,33 @@ name = "crypto" version = "0.1.0" dependencies = [ "arrayvec", + "base64ct", + "ecdsa", "hkdf", + "hmac", "openssl", + "p256", + "p384", "rand", + "sec1", "sha2", "strum", "strum_macros", "zeroize", ] +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -364,6 +388,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", + "const-oid", "crypto-common", "subtle", ] @@ -398,6 +423,41 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + [[package]] name = "env_logger" version = "0.10.1" @@ -421,6 +481,16 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core", + "subtle", +] + [[package]] name = "flagset" version = "0.4.4" @@ -450,6 +520,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -463,6 +534,17 @@ dependencies = [ "wasi", ] +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + [[package]] name = "heck" version = "0.4.1" @@ -656,6 +738,30 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p384" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + [[package]] name = "pem" version = "2.0.1" @@ -675,6 +781,16 @@ dependencies = [ "base64ct", ] +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.27" @@ -685,8 +801,10 @@ checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" name = "platform" version = "0.1.0" dependencies = [ + "cfg-if", "openssl", "ufmt", + "x509-cert", ] [[package]] @@ -701,6 +819,15 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "proc-macro2" version = "1.0.69" @@ -778,6 +905,16 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + [[package]] name = "rusticata-macros" version = "4.1.0" @@ -806,6 +943,20 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + [[package]] name = "serde" version = "1.0.192" @@ -837,6 +988,16 @@ dependencies = [ "digest", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + [[package]] name = "simulator" version = "0.1.0" diff --git a/dpe/fuzz/Cargo.lock b/dpe/fuzz/Cargo.lock index 83059472..f4b163fc 100644 --- a/dpe/fuzz/Cargo.lock +++ b/dpe/fuzz/Cargo.lock @@ -80,6 +80,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "bitflags" version = "1.3.2" @@ -156,6 +162,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "constant_time_eq" version = "0.3.0" @@ -192,6 +204,30 @@ dependencies = [ "typenum", ] +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "der_derive", + "flagset", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "der_derive" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fe87ce4529967e0ba1dcf8450bab64d97dfd5010a6256187ffe2e43e6f0e049" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] + [[package]] name = "deranged" version = "0.3.8" @@ -262,6 +298,12 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" +[[package]] +name = "flagset" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a7e408202050813e6f1d9addadcaafef3dca7530c7ddfb005d4081cce6779" + [[package]] name = "foreign-types" version = "0.3.2" @@ -417,6 +459,15 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "pkg-config" version = "0.3.27" @@ -427,8 +478,10 @@ checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" name = "platform" version = "0.1.0" dependencies = [ + "cfg-if", "openssl", "ufmt", + "x509-cert", ] [[package]] @@ -528,6 +581,16 @@ dependencies = [ "time", ] +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "strsim" version = "0.10.0" @@ -765,6 +828,17 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "x509-cert" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25eefca1d99701da3a57feb07e5079fc62abba059fc139e98c13bbb250f3ef29" +dependencies = [ + "const-oid", + "der", + "spki", +] + [[package]] name = "xdg" version = "2.5.2" diff --git a/verification/abi.go b/verification/abi.go index 63f76607..952524b3 100755 --- a/verification/abi.go +++ b/verification/abi.go @@ -8,6 +8,7 @@ import ( ) var DefaultContextHandle = ContextHandle{0} +var InvalidatedContextHandle = ContextHandle{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255} const ( CmdMagic uint32 = 0x44504543 diff --git a/verification/certifyKey.go b/verification/certifyKey.go index daf0a62e..fb149027 100755 --- a/verification/certifyKey.go +++ b/verification/certifyKey.go @@ -36,46 +36,12 @@ func TestCertifyKeySimulation(d TestDPEInstance, c DPEClient, t *testing.T) { testCertifyKey(d, c, t, true) } -// Checks whether FWID array omits index-1 when extend TCI is not supported in DPE profile. -func TestCertifyKeyWithoutExtendTciSupport(d TestDPEInstance, c DPEClient, t *testing.T) { - simulation := false - handle := getInitialContextHandle(d, c, t, simulation) - - profile, err := GetTransportProfile(d) - if err != nil { - t.Fatalf("Could not get profile: %v", err) - } - digestLen := profile.GetDigestSize() - - // Get DPE leaf certificate from CertifyKey - certifiedKey, err := c.CertifyKey(handle, make([]byte, digestLen), CertifyKeyX509, CertifyKeyFlags(0)) - if err != nil { - t.Fatalf("[FATAL]: Could not certify key: %v", err) - } - - leafCertBytes := certifiedKey.Certificate - var leafCert *x509.Certificate - - if leafCert, err = x509.ParseCertificate(leafCertBytes); err != nil { - t.Errorf("[ERROR]: Could not parse leaf certificate %s", err) - } - - multiTcbInfo, err := getMultiTcbInfo(leafCert.Extensions) - if err != nil { - t.Errorf("[ERROR]: Could not parse multi TCB information: extension %s", err) - } - - if len(multiTcbInfo) == 0 { - t.Errorf("[ERROR]: Certificate MutliTcbInfo is empty") - } +func TestDiceTcbInfo(d TestDPEInstance, c DPEClient, t *testing.T) { + testDiceTcbInfo(d, c, t, false) +} - // Check whether fwids array has only fwids[0] i.e, TCI_CURRENT measurement - // and TCI_CUMULATIVE i.e fwids[1] is omitted in every Dice TCB info block - for i, tcbinfo := range multiTcbInfo { - if len(tcbinfo.Fwids) != 1 { - t.Errorf("Extend TCI is not supported by profile, expected FWIDs length in block-%d is %d but got %d", i, 1, len(tcbinfo.Fwids)) - } - } +func TestDiceTcbInfoSimulation(d TestDPEInstance, c DPEClient, t *testing.T) { + testDiceTcbInfo(d, c, t, true) } func TestCertifyKey_Csr(d TestDPEInstance, c DPEClient, t *testing.T) { @@ -216,26 +182,35 @@ func testCertifyKey(d TestDPEInstance, c DPEClient, t *testing.T, simulation boo t.Errorf("[ERROR]: %v", err) } + _, err = getMultiTcbInfo(leafCert.Extensions) + if err != nil { + t.Errorf("[ERROR]: Could not parse multi TCB information: extension %s", err) + } + // Reassign handle for simulation mode. // However, this does not impact in default mode because // same default context handle is returned in default mode. handle = &certifyKeyResp.Handle } - // DeriveChild to add more TCIs and call CertifyKey again. - if simulation { - handle = checkWithDerivedChildContextSimulation(d, c, t, handle) - } else { - checkWithDerivedChildContext(d, c, t, handle) - } } -// Checks Multi Tcb Info for context derived from non-simulation mode -// Check CertifyKey command after adding more TCIs by DeriveChild command. -// The MultiTcbInfo extension has a DiceTcbInfo block for each TCI node. +// Checks Multi Tcb Info for context derived from non-simulation mode by adding more TCIs by DeriveChild command. +// MultiTcbInfo extension has a DiceTcbInfo block for each TCI node. // In a DiceTcbInfo block of a given TCI node, // - the "type" field must contain 4-byte tciType is provided by a client to DeriveChild. // - the "fwid" field must contain cumulative TCI measurement. -func checkWithDerivedChildContext(d TestDPEInstance, c DPEClient, t *testing.T, handle *ContextHandle) { +func testDiceTcbInfo(d TestDPEInstance, c DPEClient, t *testing.T, simulation bool) { + handle := getInitialContextHandle(d, c, t, simulation) + if simulation { + // Clean up contexts + defer func() { + err := c.DestroyContext(handle, DestroyDescendants) + if err != nil { + t.Errorf("[ERROR]: Error while cleaning contexts, this may cause failure in subsequent tests: %s", err) + } + }() + } + profile, err := GetTransportProfile(d) if err != nil { t.Fatalf("Could not get profile: %v", err) @@ -254,27 +229,13 @@ func checkWithDerivedChildContext(d TestDPEInstance, c DPEClient, t *testing.T, childTCI1[i] = byte(i + 1) } - // Set tciType to verify in multiTcbInfo extension + // Set tciType to verify in UEID extension tciType := uint32(2) - // Preserve parent context to restore for subsequent tests - parentHandle, err := c.RotateContextHandle(handle, RotateContextHandleFlags(0)) - if err != nil { - t.Errorf("[ERROR]: Error while rotating parent context handle, this may cause failure in subsequent tests: %s", err) - } - - // Deferred call to restore default context handle for subsequent tests - defer func() { - _, err = c.RotateContextHandle(parentHandle, RotateContextHandleFlags(TargetIsDefault)) - if err != nil { - t.Errorf("[ERROR]: Error while restoring parent context handle as default context handle, this may cause failure in subsequent tests: %s", err) - } - }() - // Derive Child context with input data, tag it and check TCI_CUMULATIVE - childCtx, err := c.DeriveChild(parentHandle, + childCtx, err := c.DeriveChild(handle, childTCI1, - DeriveChildFlags(InputAllowX509|RetainParent), + DeriveChildFlags(InputAllowX509), tciType, 0) @@ -282,19 +243,10 @@ func checkWithDerivedChildContext(d TestDPEInstance, c DPEClient, t *testing.T, t.Fatalf("[FATAL]: Error while creating child handle: %s", err) } - childHandle := &childCtx.NewContextHandle - parentHandle = &childCtx.ParentContextHandle - - // Clean up contexts - defer func() { - err := c.DestroyContext(childHandle, DestroyDescendants) - if err != nil { - t.Errorf("[ERROR]: Error while cleaning up derived context, this may cause failure in subsequent tests: %s", err) - } - }() + handle = &childCtx.NewContextHandle var childTcbInfo DiceTcbInfo - childHandle, childTcbInfo, err = getTcbInfoForHandle(c, childHandle) + handle, childTcbInfo, err = getTcbInfoForHandle(c, handle) if err != nil { t.Fatalf("[FATAL]: Could not get TcbInfo: %v", err) } @@ -327,7 +279,7 @@ func checkWithDerivedChildContext(d TestDPEInstance, c DPEClient, t *testing.T, childTCI2[i] = byte(i + 2) } - childCtx, err = c.DeriveChild(childHandle, + childCtx, err = c.DeriveChild(handle, childTCI2, DeriveChildFlags(InputAllowX509), tciType, @@ -337,15 +289,15 @@ func checkWithDerivedChildContext(d TestDPEInstance, c DPEClient, t *testing.T, t.Fatalf("[FATAL]: Error while creating child handle: %s", err) } - childHandle = &childCtx.NewContextHandle + handle = &childCtx.NewContextHandle // Get latest TCB information - certifiedKey, err := c.CertifyKey(childHandle, childTCI2, CertifyKeyX509, 0) + certifiedKey, err := c.CertifyKey(handle, childTCI2, CertifyKeyX509, 0) if err != nil { t.Fatalf("[FATAL]: Could not certify key: %s", err) } - childHandle = &certifiedKey.Handle + handle = &certifiedKey.Handle leafCertBytes := certifiedKey.Certificate // Build list of tci_current for validation and use it for validating TCI measurements @@ -355,118 +307,6 @@ func checkWithDerivedChildContext(d TestDPEInstance, c DPEClient, t *testing.T, } } -// Checks Multi Tcb Info for context derived from simulation mode -func checkWithDerivedChildContextSimulation(d TestDPEInstance, c DPEClient, t *testing.T, handle *ContextHandle) *ContextHandle { - profile, err := GetTransportProfile(d) - if err != nil { - t.Fatalf("Could not get profile: %v", err) - } - digestLen := profile.GetDigestSize() - - var hashAlg asn1.ObjectIdentifier - if digestLen == 32 { - hashAlg = OidSHA256 - } else if digestLen == 48 { - hashAlg = OidSHA384 - } - - childTCI := make([]byte, digestLen) - for i := range childTCI { - childTCI[i] = byte(i) - } - - // Set tciType to verify in UEID extension - tciType := uint32(2) - - locality := d.GetLocality() - - // Derive Child context with input data, tag it and check TCI_CUMULATIVE - childCtx, err := c.DeriveChild(handle, - childTCI, - DeriveChildFlags(InputAllowX509|ChangeLocality|RetainParent), - tciType, - locality+1) // Switch locality to derive child context from Simulation context - - if err != nil { - t.Fatalf("[FATAL]: Error while creating child handle: %s", err) - } - - handle = &childCtx.NewContextHandle - parentHandle := &childCtx.ParentContextHandle - - // Clean up contexts - defer func() { - err := c.DestroyContext(handle, DestroyDescendants) - if err != nil { - t.Errorf("[ERROR]: Error while cleaning up derived context, this may cause failure in subsequent tests: %s", err) - } - // Revert locality for other tests - d.SetLocality(0) - }() - - // Switch to simulated child context locality to issue CertifyKey command - d.SetLocality(locality + 1) - - // Make CertifyKey call and get current MultiTcbInfo - var leafCert *x509.Certificate - - certifiedKey, err := c.CertifyKey(handle, childTCI, CertifyKeyX509, 0) - if err != nil { - t.Errorf("[ERROR]: Could not certify key: %s", err) - } - - handle = &certifiedKey.Handle // Update handle - leafCertBytes := certifiedKey.Certificate - - if leafCert, err = x509.ParseCertificate(leafCertBytes); err != nil { - t.Errorf("[ERROR]: Could not parse certificate: %s", err) - } - - multiTcbInfo, err := getMultiTcbInfo(leafCert.Extensions) - if err != nil { - t.Errorf("[ERROR]: Could not parse multi TCB info extension: %s", err) - } - - if len(multiTcbInfo) == 0 { - t.Errorf("[ERROR]: Certificate MutliTcbInfo is empty") - } - - childTcbInfo := multiTcbInfo[0] - if !bytes.Equal(childTcbInfo.Fwids[0].Digest, childTCI) { - t.Errorf("[ERROR]: Got current TCI %x, expected %x", childTcbInfo.Fwids[0].Digest, childTCI) - } - - // Preform checks on multi Tcb info of child TCI node - // Check vendorInfo field - if err = checkDiceTcbVendorInfo(childTcbInfo, d.GetLocality()); err != nil { - t.Errorf("[ERROR]: %v", err) - } - - // Check type field - if err = checkCurrentDiceTcbTciType(childTcbInfo, tciType); err != nil { - t.Errorf("[ERROR]: %v", err) - } - - // Check hash algorithm - if err = checkDiceTcbHashAlgorithm(childTcbInfo, hashAlg); err != nil { - t.Errorf("[ERROR]: %v", err) - } - - // Extend TCI support is mandatory for this validation - if !d.GetSupport().ExtendTci { - t.Errorf("ExtendTCI is unsupported by profile, unable to run tests to verify TCI_CUMULATIVE measurements") - return parentHandle - } - - // Check all dice tcb measurements - // Build list of tci_current for validation - currentTCIs := [][]byte{childTCI} - if err = validateDiceTcbFwids(certifiedKey.Certificate, currentTCIs, digestLen); err != nil { - t.Errorf("[ERROR]: %v", err) - } - return parentHandle -} - func checkPubKey(t *testing.T, p Profile, pubkey any, response CertifiedKey) { var pubKeyInResponse ecdsa.PublicKey switch p { diff --git a/verification/simulator.go b/verification/simulator.go index 991ced9e..87ac00ff 100644 --- a/verification/simulator.go +++ b/verification/simulator.go @@ -235,9 +235,9 @@ func GetSimulatorTargets() []TestTarget { AllTestCases, }, { - "CertifyKey_WithoutExtendTci", - getTestTarget([]string{"AutoInit", "X509"}), - []TestCase{CertifyKeyWithoutExtendTciTestCase}, + "CertifyKey_TcbValidation", + getTestTarget([]string{"AutoInit", "Simulation", "X509", "Csr", "IsCA", "RotateContext", "ExtendTci"}), + []TestCase{DiceTcbValidationTestCase, DiceTcbValidationSimulationTestCase}, }, { "GetProfile_Simulation", diff --git a/verification/verification.go b/verification/verification.go index 9437f1e4..be90ff2d 100644 --- a/verification/verification.go +++ b/verification/verification.go @@ -27,17 +27,20 @@ var InitializeContextSimulationTestCase = TestCase{ "InitializeContextSimulation", TestInitializeSimulation, []string{"Simulation"}, } var CertifyKeyTestCase = TestCase{ - "CertifyKey", TestCertifyKey, []string{"AutoInit", "X509", "IsCA", "RotateContext", "ExtendTci"}, + "CertifyKey", TestCertifyKey, []string{"AutoInit", "X509", "IsCA", "RotateContext"}, } var CertifyKeySimulationTestCase = TestCase{ - "CertifyKeySimulation", TestCertifyKeySimulation, []string{"AutoInit", "Simulation", "X509", "IsCA", "RotateContext", "ExtendTci"}, -} -var CertifyKeyWithoutExtendTciTestCase = TestCase{ - "CertifyKeyWithoutExtendTciSupport", TestCertifyKeyWithoutExtendTciSupport, []string{"AutoInit", "X509"}, + "CertifyKeySimulation", TestCertifyKeySimulation, []string{"AutoInit", "Simulation", "X509", "IsCA", "RotateContext"}, } var CertifyKeyCsrTestCase = TestCase{ "CertifyKeyCsr", TestCertifyKey_Csr, []string{"AutoInit", "Csr", "IsCA"}, } +var DiceTcbValidationTestCase = TestCase{ + "CheckDiceTcbInfo", TestDiceTcbInfo, []string{"AutoInit", "X509", "IsCA", "RotateContext", "ExtendTci"}, +} +var DiceTcbValidationSimulationTestCase = TestCase{ + "CheckDiceTcbInfoInSimulationMode", TestDiceTcbInfoSimulation, []string{"AutoInit", "Simulation", "X509", "IsCA", "RotateContext", "ExtendTci"}, +} var GetCertificateChainTestCase = TestCase{ "GetCertificateChain", TestGetCertificateChain, []string{"AutoInit", "X509"}, }