diff --git a/simulator/src/main.rs b/simulator/src/main.rs index d12ae05c..015ef899 100644 --- a/simulator/src/main.rs +++ b/simulator/src/main.rs @@ -112,6 +112,10 @@ struct Args { /// Supports the RETAIN_PARENT_CONTEXT extension to DeriveContext #[arg(long)] supports_retain_parent_context: bool, + + /// Supports the CDI_EXPORT extension to DeriveContext + #[arg(long)] + supports_cdi_export: bool, } struct SimTypes {} @@ -156,6 +160,7 @@ fn main() -> std::io::Result<()> { Support::RETAIN_PARENT_CONTEXT, args.supports_retain_parent_context, ); + support.set(Support::CDI_EXPORT, args.supports_cdi_export); let mut env = DpeEnv:: { crypto: ::Crypto::new(), diff --git a/verification/client/abi.go b/verification/client/abi.go index ec0cfd53..906e8f1c 100644 --- a/verification/client/abi.go +++ b/verification/client/abi.go @@ -35,6 +35,7 @@ type Support struct { InternalDice bool IsCA bool RetainParentContext bool + CdiExport bool } // profileCommandCodes holds command codes for a specific revision of the @@ -125,6 +126,9 @@ const ( // ContextHandle is a DPE context handle type ContextHandle [16]byte +// ExportedCdi is a handle to an exported CDI +type ExportedCdi [32]byte + // DestroyCtxCmd is input parameters to DestroyContext type DestroyCtxCmd struct { handle ContextHandle @@ -218,6 +222,8 @@ const ( InputAllowCA DeriveContextFlags = 1 << 26 InputAllowX509 DeriveContextFlags = 1 << 25 Recursive DeriveContextFlags = 1 << 24 + CdiExport DeriveContextFlags = 1 << 23 + CreateCertificate DeriveContextFlags = 1 << 22 ) // DeriveContextReq is the input request to DeriveContext @@ -233,16 +239,14 @@ type DeriveContextReq[Digest DigestAlgorithm] struct { type DeriveContextResp struct { NewContextHandle ContextHandle ParentContextHandle ContextHandle + ExportedCdi ExportedCdi + CertificateSize uint32 + NewCertificate []byte } // SignFlags is the input flags to Sign type SignFlags uint32 -// Supported Sign flags -const ( - IsSymmetric SignFlags = 1 << 30 -) - // SignReq is the input request to Sign type SignReq[Digest DigestAlgorithm] struct { ContextHandle ContextHandle @@ -512,15 +516,43 @@ func (c *DPEABI[_, _, _]) GetCertificateChainABI() (*GetCertificateChainResp, er } // DeriveContextABI calls DPE DeriveContext command. -func (c *DPEABI[_, Digest, _]) DeriveContextABI(cmd *DeriveContextReq[Digest]) (*DeriveContextResp, error) { - var respStruct DeriveContextResp +func (c *DPEABI[_, Digest, DPECertificate]) DeriveContextABI(cmd *DeriveContextReq[Digest]) (*DeriveContextResp, error) { + // Define an anonymous struct for the response, because the shape changes if exportCdi is set. + if cmd.Flags&CdiExport == CdiExport { + respStruct := struct { + NewContextHandle [16]byte + ParentContextHandle [16]byte + ExportedCdi [32]byte + CertificateSize uint32 + Certificate DPECertificate + }{} + _, err := execCommand(c.transport, c.constants.Codes.DeriveContext, c.Profile, cmd, &respStruct) + if err != nil { + return nil, err + } - _, err := execCommand(c.transport, c.constants.Codes.DeriveContext, c.Profile, cmd, &respStruct) - if err != nil { - return nil, err - } + return &DeriveContextResp{ + NewContextHandle: respStruct.NewContextHandle, + ParentContextHandle: respStruct.ParentContextHandle, + ExportedCdi: respStruct.ExportedCdi, + CertificateSize: respStruct.CertificateSize, + NewCertificate: respStruct.Certificate.Bytes()[:respStruct.CertificateSize], + }, nil + } else { + respStruct := struct { + NewContextHandle [16]byte + ParentContextHandle [16]byte + }{} + _, err := execCommand(c.transport, c.constants.Codes.DeriveContext, c.Profile, cmd, &respStruct) + if err != nil { + return nil, err + } - return &respStruct, err + return &DeriveContextResp{ + NewContextHandle: respStruct.NewContextHandle, + ParentContextHandle: respStruct.ParentContextHandle, + }, nil + } } // RotateContextHandleABI calls DPE RotateContextHandle command. @@ -733,5 +765,8 @@ func (s *Support) ToFlags() uint32 { if s.RetainParentContext { flags |= (1 << 19) } + if s.CdiExport { + flags |= (1 << 18) + } return flags } diff --git a/verification/sim/transport.go b/verification/sim/transport.go index 19518b7a..3aaf51a5 100644 --- a/verification/sim/transport.go +++ b/verification/sim/transport.go @@ -82,6 +82,9 @@ func (s *DpeSimulator) PowerOn() error { if s.supports.RetainParentContext { args = append(args, "--supports-retain-parent-context") } + if s.supports.CdiExport { + args = append(args, "--supports-cdi-export") + } s.cmd = exec.Command(s.exePath, args...) s.cmd.Stdout = os.Stdout diff --git a/verification/testing/certifyKey.go b/verification/testing/certifyKey.go index de8041e9..56adb61b 100644 --- a/verification/testing/certifyKey.go +++ b/verification/testing/certifyKey.go @@ -3,7 +3,6 @@ package verification import ( - "bytes" "crypto/ecdsa" "crypto/elliptic" "crypto/sha256" @@ -11,7 +10,6 @@ import ( "crypto/x509" "crypto/x509/pkix" "encoding/asn1" - "encoding/binary" "encoding/pem" "fmt" "hash" @@ -197,7 +195,7 @@ func TestCertifyKeyCsr(d client.TestDPEInstance, c client.DPEClient, t *testing. } // Check fields and extensions in the CSR - checkCertifyKeyExtensions(t, csr.Extensions, flags, label, certifyKeyResp.Pub, false, certChain[len(certChain)-1].SubjectKeyId) + checkCertificateExtension(t, csr.Extensions, &label, &certifyKeyResp.Pub, false, certChain[len(certChain)-1].SubjectKeyId, false) checkPubKey(t, profile, csr.PublicKey, *certifyKeyResp) // Check that CSR is self-signed @@ -274,9 +272,10 @@ func removeTcgDiceExtendedKeyUsages(t *testing.T, certs []*x509.Certificate) { } // A tcg-dice-Ueid extension MUST be added -// This SHALL be populated by the LABEL input parameter to CertifyKey +// This SHALL be populated by the LABEL input parameter to CertifyKey or retrieved from DPE Platform +// by DeriveContext. // The extension SHOULD be marked as critical -func checkCertifyKeyTcgUeidExtension(t *testing.T, extensions []pkix.Extension, label []byte) { +func checkTcgUeidExtension(t *testing.T, extensions []pkix.Extension, label []byte) { t.Helper() ueid, err := getUeid(extensions) @@ -286,7 +285,7 @@ func checkCertifyKeyTcgUeidExtension(t *testing.T, extensions []pkix.Extension, 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") + t.Errorf("[ERROR]: tcg-dice-Ueid value does not match with the \"Label\". Got %+v but expect %+v", ueid.Ueid, label) } } @@ -295,7 +294,7 @@ func checkCertifyKeyTcgUeidExtension(t *testing.T, extensions []pkix.Extension, // 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) { +func checkExtendedKeyUsages(t *testing.T, extensions []pkix.Extension, ca bool) { t.Helper() extKeyUsage, err := getExtendedKeyUsages(extensions) @@ -331,9 +330,10 @@ func checkCertifyKeyExtendedKeyUsages(t *testing.T, extensions []pkix.Extension, } // Checks for KeyUsage Extension as per spec -// If IsCA = true, KeyUsage extension MUST contain DigitalSignature and KeyCertSign +// If IsCA = true, KeyUsage extension MUST contain DigitalSignature and KeyCertSign. BasicConstraints will also be checked that it matches the `IsCA` bool. // If IsCA = false, KeyUsage extension MUST contain only DigitalSignature -func checkCertifyKeyExtensions(t *testing.T, extensions []pkix.Extension, flags client.CertifyKeyFlags, label []byte, pubkey client.DPEPubKey, IsX509 bool, IssuerSki []byte) { +// If `label` is nil then the Ueid extension check is omitted. +func checkCertificateExtension(t *testing.T, extensions []pkix.Extension, label *[]byte, pubkey *client.DPEPubKey, isX509 bool, IssuerSki []byte, isCA bool) { t.Helper() bc, err := getBasicConstraints(extensions) @@ -341,12 +341,14 @@ func checkCertifyKeyExtensions(t *testing.T, extensions []pkix.Extension, flags t.Error(err) } - checkCertifyKeyBasicConstraints(t, extensions, flags) - checkCertifyKeyExtendedKeyUsages(t, extensions, bc.IsCA) - checkCertifyKeyTcgUeidExtension(t, extensions, label) - if IsX509 { - checkCertifyKeySubjectKeyIdentifierExtension(t, extensions, bc.IsCA, pubkey) - checkCertifyKeyAuthorityKeyIdentifierExtension(t, extensions, bc.IsCA, IssuerSki) + checkBasicConstraints(t, extensions, isCA) + checkExtendedKeyUsages(t, extensions, bc.IsCA) + if label != nil { + checkTcgUeidExtension(t, extensions, *label) + } + if isX509 { + checkSubjectKeyIdentifierExtension(t, extensions, bc.IsCA, pubkey) + checkKeyIdentifierExtension(t, extensions, bc.IsCA, IssuerSki) } // Check MultiTcbInfo Extension structure @@ -355,7 +357,7 @@ func checkCertifyKeyExtensions(t *testing.T, extensions []pkix.Extension, flags t.Error(err) } - //Check for keyusage extension + // Check for keyusage extension var allowedKeyUsages x509.KeyUsage if bc.IsCA { @@ -377,11 +379,11 @@ func checkCertifyKeyExtensions(t *testing.T, extensions []pkix.Extension, flags } -// Validates SubjectKeyIdentifier in certificate returned by CertifyKey command +// Validates SubjectKeyIdentifier in certificate returned by CertifyKey or DeriveContext commands // against the isCa flag set in the BasicConstraints extension // The SubjectKeyIdentifier extension MUST be included if isCA is true // and MUST be excluded if isCA is false. -func checkCertifyKeySubjectKeyIdentifierExtension(t *testing.T, extensions []pkix.Extension, ca bool, pubkey client.DPEPubKey) { +func checkSubjectKeyIdentifierExtension(t *testing.T, extensions []pkix.Extension, ca bool, pubkey *client.DPEPubKey) { t.Helper() ski, err := getSubjectKeyIdentifier(extensions) @@ -392,6 +394,9 @@ func checkCertifyKeySubjectKeyIdentifierExtension(t *testing.T, extensions []pki if ski == nil { t.Errorf("[ERROR]: The certificate is a CA but the SubjectKeyIdentifier extension is not present.") } + if pubkey == nil { + return + } var hasher hash.Hash if len(pubkey.X) == 32 { hasher = sha256.New() @@ -410,11 +415,11 @@ func checkCertifyKeySubjectKeyIdentifierExtension(t *testing.T, extensions []pki } } -// Validates AuthorityKeyIdentifier in certificate returned by CertifyKey command +// Validates AuthorityKeyIdentifier in certificate returned by CertifyKey or DeriveContext commands // against the isCa flag set in the BasicConstraints extension // The AuthorityKeyIdentifier extension MUST be included if isCA is true // and MUST be excluded if isCA is false. -func checkCertifyKeyAuthorityKeyIdentifierExtension(t *testing.T, extensions []pkix.Extension, ca bool, IssuerSki []byte) { +func checkKeyIdentifierExtension(t *testing.T, extensions []pkix.Extension, ca bool, IssuerSki []byte) { t.Helper() aki, err := getAuthorityKeyIdentifier(extensions) @@ -429,21 +434,18 @@ func checkCertifyKeyAuthorityKeyIdentifierExtension(t *testing.T, extensions []p } } -// Validates basic constraints in certificate returned by CertifyKey command +// Validates basic constraints in certificate returned by CertifyKey or DeriveContext command // against the flag set for input parameter. // The BasicConstraints extension MUST be included -func checkCertifyKeyBasicConstraints(t *testing.T, extensions []pkix.Extension, flags client.CertifyKeyFlags) { +func checkBasicConstraints(t *testing.T, extensions []pkix.Extension, IsCA bool) { t.Helper() - flagsBuf := &bytes.Buffer{} - binary.Write(flagsBuf, binary.LittleEndian, flags) - bc, err := getBasicConstraints(extensions) if err != nil { t.Error(err) } - if bc.IsCA { - t.Errorf("[ERROR]: basic constraint IsCA is set to %v but it must always be false for CertifyKey.", bc.IsCA) + if bc.IsCA && !IsCA { + t.Errorf("[ERROR]: basic constraint IsCA is set to %v but expected %v.", bc.IsCA, IsCA) } } @@ -577,7 +579,7 @@ func testCertifyKey(d client.TestDPEInstance, c client.DPEClient, t *testing.T, checkPubKey(t, profile, leafCert.PublicKey, *certifyKeyResp) // Check all extensions - checkCertifyKeyExtensions(t, leafCert.Extensions, params.Flags, params.Label, certifyKeyResp.Pub, true, certChain[len(certChain)-1].SubjectKeyId) + checkCertificateExtension(t, leafCert.Extensions, ¶ms.Label, &certifyKeyResp.Pub, true, certChain[len(certChain)-1].SubjectKeyId, false) // Ensure full certificate chain has valid signatures // This also checks certificate lifetime, signatures as part of cert chain validation diff --git a/verification/testing/deriveContext.go b/verification/testing/deriveContext.go index c59c64c0..b5b84eb1 100644 --- a/verification/testing/deriveContext.go +++ b/verification/testing/deriveContext.go @@ -73,6 +73,58 @@ func TestDeriveContext(d client.TestDPEInstance, c client.DPEClient, t *testing. handle = &resp.NewContextHandle } +func TestDeriveContextCdiExport(d client.TestDPEInstance, c client.DPEClient, t *testing.T) { + var resp *client.DeriveContextResp + + simulation := false + handle := getInitialContextHandle(d, c, t, simulation) + defer func() { + c.DestroyContext(handle) + }() + + profile, err := client.GetTransportProfile(d) + if err != nil { + t.Fatalf("Could not get profile: %v", err) + } + digestLen := profile.GetDigestSize() + resp, err = c.DeriveContext(handle, make([]byte, digestLen), client.CdiExport|client.CreateCertificate, 0, 0) + if err != nil { + t.Fatalf("[ERROR]: Error while exporting CdiExport: %s", err) + } + + if resp.ExportedCdi == client.ExportedCdi(bytes.Repeat([]byte{0x0}, 32)) { + t.Fatalf("[FATAL]: Expected ExportedCdi field to be set but was %v", resp.ExportedCdi) + } + if resp.NewContextHandle != client.ContextHandle(bytes.Repeat([]byte{0xFF}, 16)) { + t.Fatalf("[FATAL]: Expected invalid NewContextHandle field but it was set to %v", resp.NewContextHandle) + } + if resp.ParentContextHandle != client.ContextHandle(bytes.Repeat([]byte{0xFF}, 16)) { + t.Fatalf("[FATAL]: Expected invalid ParentContextHandle field but it was set to %v", resp.ParentContextHandle) + } + if resp.CertificateSize == 0 { + t.Fatalf("[FATAL]: Expected CertificateSize to be set but was set to %v", resp.CertificateSize) + } + + // Check whether certificate is correctly encoded. + if _, err := x509.ParseCertificate(resp.NewCertificate); err != nil { + t.Fatalf("[FATAL]: Could not parse certificate using crypto/x509: %v", err) + } + leafCert := checkCertificateStructure(t, resp.NewCertificate) + + certChainBytes, err := c.GetCertificateChain() + certChain := checkCertificateChain(t, certChainBytes) + if err != nil { + t.Fatalf("[FATAL]: Could not get Certificate Chain: %v", err) + } + + // Check all extensions + checkCertificateExtension(t, leafCert.Extensions, nil, nil, true, certChain[len(certChain)-1].SubjectKeyId, true) + + // Ensure full certificate chain has valid signatures + // This also checks certificate lifetime, signatures as part of cert chain validation + validateLeafCertChain(t, certChain, leafCert) +} + // Validates DerivedChild command with ChangeLocality flag. func TestChangeLocality(d client.TestDPEInstance, c client.DPEClient, t *testing.T) { if !d.HasLocalityControl() { diff --git a/verification/testing/simulator.go b/verification/testing/simulator.go index 5001eccb..82b7b02e 100644 --- a/verification/testing/simulator.go +++ b/verification/testing/simulator.go @@ -120,6 +120,11 @@ func GetSimulatorTargets() []TestTarget { getTestTarget([]string{"AutoInit", "X509", "RetainParentContext"}), []TestCase{DeriveContextTestCase}, }, + { + "TestDeriveContextCdiExport", + getTestTarget([]string{"AutoInit", "CdiExport"}), + []TestCase{TestDeriveContextCdiExportTestCase}, + }, { "DeriveContext_Simulation", getTestTarget([]string{"AutoInit", "Simulation", "X509", "RetainParentContext"}), diff --git a/verification/testing/verification.go b/verification/testing/verification.go index dd210325..cedbe9b2 100644 --- a/verification/testing/verification.go +++ b/verification/testing/verification.go @@ -112,6 +112,11 @@ var DeriveContextTestCase = TestCase{ "DeriveContext", TestDeriveContext, []string{"AutoInit", "RetainParentContext"}, } +// TestDeriveContextCdiExport tests DeriveContext +var TestDeriveContextCdiExportTestCase = TestCase{ + "DeriveContextCdiExport", TestDeriveContextCdiExport, []string{"CdiExport"}, +} + // DeriveContextSimulationTestCase tests DeriveContext with Simulation contexts var DeriveContextSimulationTestCase = TestCase{ "DeriveContextSimulation", TestDeriveContextSimulation, []string{"AutoInit", "Simulation", "X509", "InternalDice", "InternalInfo", "RetainParentContext"},