Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add verification tests for the "export-cdi" DeriveContext feature #380

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
jhand2 marked this conversation as resolved.
Show resolved Hide resolved
}

// 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
58 changes: 30 additions & 28 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,25 @@ 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)
if err != nil {
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
Expand All @@ -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 {
Expand All @@ -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)
Expand All @@ -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
}
jhand2 marked this conversation as resolved.
Show resolved Hide resolved
var hasher hash.Hash
if len(pubkey.X) == 32 {
hasher = sha256.New()
Expand All @@ -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)
Expand All @@ -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)
}
}

Expand Down Expand Up @@ -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, &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
52 changes: 52 additions & 0 deletions verification/testing/deriveContext.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
jhand2 marked this conversation as resolved.
Show resolved Hide resolved
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() {
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