diff --git a/verification/testing/deriveContext.go b/verification/testing/deriveContext.go new file mode 100644 index 00000000..80be8bdc --- /dev/null +++ b/verification/testing/deriveContext.go @@ -0,0 +1,310 @@ +// Licensed under the Apache-2.0 license + +package verification + +import ( + "errors" + "testing" + + "github.com/chipsalliance/caliptra-dpe/verification/client" +) + +func TestDeriveContext(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) + }() + + // Get digest size + profile, err := client.GetTransportProfile(d) + if err != nil { + t.Fatalf("Could not get profile: %v", err) + } + + digestLen := profile.GetDigestSize() + + // Child context handle returned will be the default context handle when MakeDefault flag is set + if resp, err = c.DeriveContext(handle, make([]byte, digestLen), client.MakeDefault, 0, 0); err != nil { + t.Errorf("[ERROR]: Error while creating child context handle: %s", err) + } + handle = &resp.NewContextHandle + if resp.NewContextHandle != client.DefaultContextHandle { + t.Fatalf("[FATAL]: Incorrect handle. Should return %v, but returned %v", client.DefaultContextHandle, *handle) + } + + // When there is already a default handle, setting MakeDefault and RetainParentContext will cause error + // because there cannot be default *and* non-default handles in a locality + if _, err = c.DeriveContext(handle, make([]byte, digestLen), client.MakeDefault|client.RetainParentContext, 0, 0); err == nil { + t.Errorf("[ERROR]: Should return %q, but returned no error", client.StatusInvalidArgument) + } else if !errors.Is(err, client.StatusInvalidArgument) { + t.Errorf("[ERROR]: Incorrect error type. Should return %q, but returned %q", client.StatusInvalidArgument, err) + } + + // Retain parent should fail because parent handle is a default handle + // and child handle will be a non-default handle. + // Default and non-default handle cannot coexist in same locality. + if _, err = c.DeriveContext(handle, make([]byte, digestLen), client.RetainParentContext, 0, 0); err == nil { + t.Errorf("[ERROR]: Should return %q, but returned no error", client.StatusInvalidArgument) + } else if !errors.Is(err, client.StatusInvalidArgument) { + t.Errorf("[ERROR]: Incorrect error type. Should return %q, but returned %q", client.StatusInvalidArgument, err) + } + + // Child context handle should be a random handle when MakeDefault flag is NOT used + if resp, err = c.DeriveContext(handle, make([]byte, digestLen), 0, 0, 0); err != nil { + t.Errorf("[ERROR]: Error while creating child context handle: %s", err) + } + handle = &resp.NewContextHandle + if resp.NewContextHandle == client.DefaultContextHandle { + t.Fatalf("[FATAL]: Incorrect handle. Should return non-default handle, but returned %v", *handle) + } + + // Now, there is no default handle, setting RetainParentContext flag should succeed + if resp, err = c.DeriveContext(handle, make([]byte, digestLen), client.RetainParentContext, 0, 0); err != nil { + t.Errorf("[ERROR]: Error while making child context handle as default handle: %s", err) + } + handle = &resp.NewContextHandle +} + +// Validates DerivedChild command with ChangeLocality flag. +func TestChangeLocality(d client.TestDPEInstance, c client.DPEClient, t *testing.T) { + if !d.HasLocalityControl() { + t.Skip("WARNING: DPE target does not have control over locality. Skipping this test...") + } + + var resp *client.DeriveContextResp + simulation := false + handle := getInitialContextHandle(d, c, t, simulation) + // Clean up contexts + defer func() { + err := c.DestroyContext(handle) + if err != nil { + t.Errorf("[ERROR]: Error while cleaning contexts, this may cause failure in subsequent tests: %s", err) + } + }() + + // Get digest size + profile, err := client.GetTransportProfile(d) + if err != nil { + t.Fatalf("Could not get profile: %v", err) + } + + digestLen := profile.GetDigestSize() + currentLocality := d.GetLocality() + otherLocality := currentLocality + 1 + + // Create child context in other locality + if resp, err = c.DeriveContext(handle, make([]byte, digestLen), client.ChangeLocality, 0, otherLocality); err != nil { + t.Fatalf("[ERROR]: Error while creating child context handle in other locality: %s", err) + } + handle = &resp.NewContextHandle + + // Revert to same locality from other locality + prevLocality := currentLocality + d.SetLocality(otherLocality) + + if resp, err = c.DeriveContext(handle, make([]byte, digestLen), client.ChangeLocality, 0, prevLocality); err != nil { + t.Fatalf("[FATAL]: Error while creating child context handle in previous locality: %s", err) + } + handle = &resp.NewContextHandle + d.SetLocality(prevLocality) +} + +// Checks whether the DeriveContext input flags - InternalDiceInfo, InternalInputInfo are supported +// while creating child contexts when these features are supported in DPE profile. +func TestInternalInputFlags(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) + }() + + // Get digest size + profile, err := client.GetTransportProfile(d) + if err != nil { + t.Fatalf("Could not get profile: %v", err) + } + + digestLen := profile.GetDigestSize() + // Internal input flags + if d.GetSupport().InternalDice { + if resp, err = c.DeriveContext(handle, make([]byte, digestLen), client.DeriveContextFlags(client.InternalInputDice), 0, 0); err != nil { + t.Errorf("[ERROR]: Error while making child context handle as default handle: %s", err) + } + handle = &resp.NewContextHandle + } else { + t.Skip("[WARNING]: Profile has no support for \"InternalInputDice\" flag. Skipping the validation for this flag...") + } + + if d.GetSupport().InternalInfo { + if resp, err = c.DeriveContext(handle, make([]byte, digestLen), client.DeriveContextFlags(client.InternalInputInfo), 0, 0); err != nil { + t.Errorf("[ERROR]: Error while making child context handle as default handle: %s", err) + } + handle = &resp.NewContextHandle + } else { + t.Skip("[WARNING]: Profile has no support for \"InternalInputInfo\" flag. Skipping the validation for this flag...") + } +} + +// Checks the privilege escalation of child +// When commands try to make use of features that are unsupported by child context, they fail. +func TestPrivilegesEscalation(d client.TestDPEInstance, c client.DPEClient, t *testing.T) { + var err error + simulation := false + handle := getInitialContextHandle(d, c, t, simulation) + + // Get digest size + profile, err := client.GetTransportProfile(d) + if err != nil { + t.Fatalf("Could not get profile: %v", err) + } + + digestLen := profile.GetDigestSize() + + // Create a child TCI node with no special privileges + resp, err := c.DeriveContext(handle, + make([]byte, digestLen), + client.DeriveContextFlags(0), + 0, 0) + if err != nil { + t.Fatalf("[FATAL]: Error encountered in getting child context: %v", err) + } + handle = &resp.NewContextHandle + + // Adding new privileges to child that parent does NOT possess will cause failure + _, err = c.DeriveContext(handle, + make([]byte, digestLen), + client.DeriveContextFlags(client.InputAllowX509|client.InputAllowCA), + 0, 0) + if err == nil { + t.Errorf("[ERROR]: Should return %q, but returned no error", client.StatusInvalidArgument) + } else if !errors.Is(err, client.StatusInvalidArgument) { + t.Errorf("[ERROR]: Incorrect error type. Should return %q, but returned %q", client.StatusInvalidArgument, err) + } + + // Similarly, when commands like CertifyKey try to make use of features/flags that are unsupported + // by child context, it will fail. + if _, err = c.CertifyKey(handle, make([]byte, digestLen), client.CertifyKeyX509, client.CertifyAddIsCA); err == nil { + t.Errorf("[ERROR]: Should return %q, but returned no error", client.StatusInvalidArgument) + } else if !errors.Is(err, client.StatusInvalidArgument) { + t.Errorf("[ERROR]: Incorrect error type. Should return %q, but returned %q", client.StatusInvalidArgument, err) + } +} + +// Checks whether the number of derived contexts (TCI nodes) are limited by MAX_TCI_NODES attribute of the profile +func TestMaxTCIs(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) }() + + // Get digest size + profile, err := client.GetTransportProfile(d) + if err != nil { + t.Fatalf("Could not get profile: %v", err) + } + digestSize := profile.GetDigestSize() + + // Get Max TCI count + maxTciCount := int(d.GetMaxTciNodes()) + allowedTciCount := maxTciCount - 1 // since, a TCI node is already auto-initialized + for i := 0; i < allowedTciCount; i++ { + resp, err = c.DeriveContext(handle, make([]byte, digestSize), 0, 0, 0) + if err != nil { + t.Fatalf("[FATAL]: Error encountered in executing derive child: %v", err) + } + handle = &resp.NewContextHandle + } + + // Exceed the Max TCI node count limit + _, err = c.DeriveContext(handle, make([]byte, digestSize), 0, 0, 0) + if err == nil { + t.Fatalf("[FATAL]: Should return %q, but returned no error", client.StatusMaxTCIs) + } else if !errors.Is(err, client.StatusMaxTCIs) { + t.Fatalf("[FATAL]: Incorrect error type. Should return %q, but returned %q", client.StatusMaxTCIs, err) + } +} + +func TestDeriveContextSimulation(d client.TestDPEInstance, c client.DPEClient, t *testing.T) { + if !d.HasLocalityControl() { + t.Skip("WARNING: DPE target does not have control over locality, DeriveContext in Simulation mode cannot be tested without this support. Skipping this test...") + } + var resp *client.DeriveContextResp + + simulation := true + handle := getInitialContextHandle(d, c, t, simulation) + defer func() { + c.DestroyContext(handle) + }() + + // Get digest size + profile, err := client.GetTransportProfile(d) + if err != nil { + t.Fatalf("Could not get profile: %v", err) + } + + digestLen := profile.GetDigestSize() + locality := d.GetLocality() + + // MakeDefault should fail because parent handle is a non-default handle + // and child handle will be a default handle. + // Default and non-default handle cannot coexist in same locality. + if _, err = c.DeriveContext(handle, make([]byte, digestLen), client.MakeDefault, 0, 0); err == nil { + t.Errorf("[ERROR]: Should return %q, but returned no error", client.StatusInvalidArgument) + } else if !errors.Is(err, client.StatusInvalidArgument) { + t.Errorf("[ERROR]: Incorrect error type. Should return %q, but returned %q", client.StatusInvalidArgument, err) + } + + // Make default child context in other locality + childCtx, err := c.DeriveContext(handle, + make([]byte, digestLen), + client.DeriveContextFlags(client.ChangeLocality|client.RetainParentContext|client.MakeDefault), + 0, + 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 parent context + defer func() { + err := c.DestroyContext(parentHandle) + if err != nil { + t.Errorf("[ERROR]: Error while cleaning contexts, this may cause failure in subsequent tests: %s", err) + } + }() + + d.SetLocality(locality + 1) + + defer func() { + // Clean up contexts after test + err := c.DestroyContext(handle) + 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(locality) + }() + + // Retain parent should fail because parent handle is a default handle + // and child handle will be a non-default handle. + // Default and non-default handle cannot coexist in same locality. + if _, err = c.DeriveContext(handle, make([]byte, digestLen), client.RetainParentContext, 0, 0); err == nil { + t.Errorf("[ERROR]: Should return %q, but returned no error", client.StatusInvalidArgument) + } else if !errors.Is(err, client.StatusInvalidArgument) { + t.Errorf("[ERROR]: Incorrect error type. Should return %q, but returned %q", client.StatusInvalidArgument, err) + } + + // Setting RetainParentContext flag should not invalidate the parent handle + if resp, err = c.DeriveContext(handle, make([]byte, digestLen), client.RetainParentContext, 0, 0); err != nil { + t.Fatalf("[FATAL]: Error while making child context and retaining parent handle %s", err) + } + handle = &resp.NewContextHandle +} diff --git a/verification/testing/simulator.go b/verification/testing/simulator.go index 81e3deb7..50ed86ef 100644 --- a/verification/testing/simulator.go +++ b/verification/testing/simulator.go @@ -338,6 +338,36 @@ func GetSimulatorTargets() []TestTarget { getTestTarget([]string{"AutoInit", "RotateContext"}), []TestCase{UnsupportedCommandFlag}, }, + { + "DeriveContext", + getTestTarget([]string{"AutoInit", "X509", "IsCA", "RetainParentContext"}), + []TestCase{DeriveContextTestCase}, + }, + { + "DeriveContext_Simulation", + getTestTarget([]string{"AutoInit", "Simulation", "X509", "IsCA", "RetainParentContext"}), + []TestCase{DeriveContextSimulationTestCase}, + }, + { + "DeriveContext_PrivilegeEscalation", + getTestTarget([]string{"AutoInit", "X509", "IsCA"}), + []TestCase{DeriveContextPrivilegeEscalationTestCase}, + }, + { + "DeriveContext_InputFlags", + getTestTarget([]string{"AutoInit", "Simulation", "InternalDice", "InternalInfo"}), + []TestCase{DeriveContextInputFlagsTestCase}, + }, + { + "DeriveContext_MaxTCIs", + getTestTarget([]string{"AutoInit", "Simulation"}), + []TestCase{DeriveContextMaxTCIsTestCase}, + }, + { + "DeriveContext_ChangeLocality", + getTestTarget([]string{"AutoInit", "Simulation"}), + []TestCase{DeriveContextLocalityTestCase}, + }, } } diff --git a/verification/testing/verification.go b/verification/testing/verification.go index 990f195a..57926c76 100644 --- a/verification/testing/verification.go +++ b/verification/testing/verification.go @@ -112,6 +112,36 @@ var TpmPolicySigningTestCase = TestCase{ "TPMPolicySigning", TestTpmPolicySigning, []string{"AutoInit", "X509"}, } +// DeriveContextTestCase tests DeriveContext +var DeriveContextTestCase = TestCase{ + "DeriveContext", TestDeriveContext, []string{"AutoInit", "RetainParentContext"}, +} + +// DeriveContextSimulationTestCase tests DeriveContext with Simulation contexts +var DeriveContextSimulationTestCase = TestCase{ + "DeriveContextSimulation", TestDeriveContextSimulation, []string{"AutoInit", "Simulation", "X509", "InternalDice", "InternalInfo", "RetainParentContext"}, +} + +// DeriveContextMaxTCIsTestCase checks whether the number of derived contexts is limited by MAX_TCI_NODES attribute of the profile +var DeriveContextMaxTCIsTestCase = TestCase{ + "DeriveContext_MaxTCIs", TestMaxTCIs, []string{"AutoInit"}, +} + +// DeriveContextLocalityTestCase tests DerivedContext with the ChangeLocality flag. +var DeriveContextLocalityTestCase = TestCase{ + "DeriveContext_ChangeLocality", TestChangeLocality, []string{"AutoInit"}, +} + +// DeriveContextPrivilegeEscalationTestCase tests that commands trying to use features that are unsupported by child context fail. +var DeriveContextPrivilegeEscalationTestCase = TestCase{ + "DeriveContext_PrivilegeEscalation", TestPrivilegesEscalation, []string{"AutoInit", "X509", "IsCA"}, +} + +// DeriveContextInputFlagsTestCase tests DeriveContext with the input flags InternalDiceInfo and InternalInputInfo. +var DeriveContextInputFlagsTestCase = TestCase{ + "DeriveContext_InputFlagsSupport", TestInternalInputFlags, []string{"AutoInit", "InternalDice", "InternalInfo"}, +} + // AllTestCases contains all DPE test cases var AllTestCases = []TestCase{ CertifyKeyTestCase, @@ -131,6 +161,13 @@ var AllTestCases = []TestCase{ WrongLocalityTestCase, } +var IrreversibleTestCases = []TestCase{ + DeriveContextTestCase, + DeriveContextLocalityTestCase, + DeriveContextPrivilegeEscalationTestCase, + DeriveContextMaxTCIsTestCase, +} + // RunTargetTestCases runs all test cases for target func RunTargetTestCases(target TestTarget, t *testing.T) { // This needs to be in a separate function to make sure it is powered off before running the