Skip to content

Commit

Permalink
Add verification tests for the "export-cdi" DeriveContext feature
Browse files Browse the repository at this point in the history
  • Loading branch information
clundin25 committed Jan 27, 2025
1 parent c145caf commit 96c706c
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 39 deletions.
5 changes: 5 additions & 0 deletions simulator/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}
Expand Down Expand Up @@ -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::<SimTypes> {
crypto: <SimTypes as DpeTypes>::Crypto::new(),
Expand Down
59 changes: 47 additions & 12 deletions verification/client/abi.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -733,5 +765,8 @@ func (s *Support) ToFlags() uint32 {
if s.RetainParentContext {
flags |= (1 << 19)
}
if s.CdiExport {
flags |= (1 << 18)
}
return flags
}
3 changes: 3 additions & 0 deletions verification/sim/transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
53 changes: 26 additions & 27 deletions verification/testing/certifyKey.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@
package verification

import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/sha256"
"crypto/sha512"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/binary"
"encoding/pem"
"fmt"
"hash"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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)
}
}

Expand All @@ -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)
Expand Down Expand Up @@ -331,22 +330,22 @@ 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) {
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)
if err != nil {
t.Error(err)
}

checkCertifyKeyBasicConstraints(t, extensions, flags)
checkCertifyKeyExtendedKeyUsages(t, extensions, bc.IsCA)
checkCertifyKeyTcgUeidExtension(t, extensions, label)
checkBasicConstraints(t, extensions, IsCA)
checkExtendedKeyUsages(t, extensions, bc.IsCA)
checkTcgUeidExtension(t, extensions, label)
if IsX509 {
checkCertifyKeySubjectKeyIdentifierExtension(t, extensions, bc.IsCA, pubkey)
checkCertifyKeyAuthorityKeyIdentifierExtension(t, extensions, bc.IsCA, IssuerSki)
checkSubjectKeyIdentifierExtension(t, extensions, bc.IsCA, pubkey)
checkKeyIdentifierExtension(t, extensions, bc.IsCA, IssuerSki)
}

// Check MultiTcbInfo Extension structure
Expand All @@ -355,7 +354,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 {
Expand All @@ -377,11 +376,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)
Expand All @@ -392,6 +391,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()
Expand All @@ -410,11 +412,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)
Expand All @@ -429,21 +431,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)
}
}

Expand Down Expand Up @@ -577,7 +576,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, params.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
Expand Down
54 changes: 54 additions & 0 deletions verification/testing/deriveContext.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,60 @@ 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)
}

// []byte{0xA, 17} does not correctly match the deep equal :(
label := []byte{10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}
// Check all extensions
checkCertificateExtension(t, leafCert.Extensions, label, 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() {
Expand Down
5 changes: 5 additions & 0 deletions verification/testing/simulator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"}),
Expand Down
5 changes: 5 additions & 0 deletions verification/testing/verification.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"},
Expand Down

0 comments on commit 96c706c

Please sign in to comment.