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/README.md b/verification/README.md index 06e38086..45200d83 100644 --- a/verification/README.md +++ b/verification/README.md @@ -1,4 +1,4 @@ # DPE Verification Tests This test suite is a userspace test-suite which exercises DPE commands -end-to-end and ensures compliance with the DPE iRoT Profile. +end-to-end and ensures compliance with the DPE iRoT Profile. \ No newline at end of file diff --git a/verification/abi.go b/verification/abi.go index a78ad636..bd991b25 100755 --- a/verification/abi.go +++ b/verification/abi.go @@ -9,6 +9,7 @@ import ( // DefaultContextHandle is the default DPE context handle var DefaultContextHandle = ContextHandle{0} +var InvalidatedContextHandle = ContextHandle{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255} // Profile-defined constants const ( diff --git a/verification/deriveChild.go b/verification/deriveChild.go new file mode 100644 index 00000000..adc2307f --- /dev/null +++ b/verification/deriveChild.go @@ -0,0 +1,320 @@ +// Licensed under the Apache-2.0 license + +package verification + +import ( + "errors" + "testing" +) + +func TestDeriveChild(d TestDPEInstance, c DPEClient, t *testing.T) { + var resp *DeriveChildResp + + simulation := false + handle := getInitialContextHandle(d, c, t, simulation) + defer func() { + c.DestroyContext(handle, DestroyDescendants) + }() + + // Get digest size + profile, err := 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.DeriveChild(handle, make([]byte, digestLen), MakeDefault, 0, 0); err != nil { + t.Errorf("[ERROR]: Error while creating child context handle: %s", err) + } + handle = &resp.NewContextHandle + if resp.NewContextHandle != DefaultContextHandle { + t.Fatalf("[FATAL]: Incorrect handle. Should return %v, but returned %v", DefaultContextHandle, *handle) + } + + // When there is already a default handle, setting MakeDefault and RetainParent will cause error + // because there cannot be two default handles in a locality + if _, err = c.DeriveChild(handle, make([]byte, digestLen), MakeDefault|RetainParent, 0, 0); err == nil { + t.Errorf("[ERROR]: Should return %q, but returned no error", StatusInvalidArgument) + } else if !errors.Is(err, StatusInvalidArgument) { + t.Errorf("[ERROR]: Incorrect error type. Should return %q, but returned %q", 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.DeriveChild(handle, make([]byte, digestLen), RetainParent, 0, 0); err == nil { + t.Errorf("[ERROR]: Should return %q, but returned no error", StatusInvalidArgument) + } else if !errors.Is(err, StatusInvalidArgument) { + t.Errorf("[ERROR]: Incorrect error type. Should return %q, but returned %q", StatusInvalidArgument, err) + } + + // Child context handle should be a random handle when MakeDefault flag is NOT used + if resp, err = c.DeriveChild(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 == DefaultContextHandle { + t.Fatalf("[FATAL]: Incorrect handle. Should return non-default handle, but returned %v", *handle) + } + if resp.ParentContextHandle != InvalidatedContextHandle { + t.Errorf("[ERROR]: Incorrect handle. Should be invalidated when retain parent is NOT set, but returned %v", resp.ParentContextHandle) + } + + // Now, there is no default handle, setting RetainParent flag should succeed + if resp, err = c.DeriveChild(handle, make([]byte, digestLen), RetainParent, 0, 0); err != nil { + t.Errorf("[ERROR]: Error while making child context handle as default handle: %s", err) + } + handle = &resp.NewContextHandle + parentHandle := resp.ParentContextHandle + + if parentHandle == InvalidatedContextHandle { + t.Errorf("[ERROR]: Incorrect handle. Should return retained handle, but returned invalidated handle %v", parentHandle) + } +} + +// Validates DerivedChild command with ChangeLocality flag. +func TestChangeLocality(d TestDPEInstance, c DPEClient, t *testing.T) { + if !d.HasLocalityControl() { + t.Skip("WARNING: DPE target does not have control over locality. Skipping this test...") + } + + var resp *DeriveChildResp + simulation := false + handle := getInitialContextHandle(d, c, t, 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) + } + }() + + // Get digest size + profile, err := 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.DeriveChild(handle, make([]byte, digestLen), 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.DeriveChild(handle, make([]byte, digestLen), 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 DeriveChild input flags - InternalDiceInfo, InternalInputInfo are supported +// while creating child contexts when these features are supported in DPE profile. +func TestInternalInputFlags(d TestDPEInstance, c DPEClient, t *testing.T) { + var resp *DeriveChildResp + simulation := false + handle := getInitialContextHandle(d, c, t, simulation) + defer func() { + c.DestroyContext(handle, DestroyDescendants) + }() + + // Get digest size + profile, err := 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.DeriveChild(handle, make([]byte, digestLen), DeriveChildFlags(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.DeriveChild(handle, make([]byte, digestLen), DeriveChildFlags(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 TestDPEInstance, c DPEClient, t *testing.T) { + var err error + simulation := false + handle := getInitialContextHandle(d, c, t, simulation) + + // Get digest size + profile, err := 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.DeriveChild(handle, + make([]byte, digestLen), + DeriveChildFlags(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.DeriveChild(handle, + make([]byte, digestLen), + DeriveChildFlags(InputAllowX509|InputAllowCA), + 0, 0) + if err == nil { + t.Errorf("[ERROR]: Should return %q, but returned no error", StatusInvalidArgument) + } else if !errors.Is(err, StatusInvalidArgument) { + t.Errorf("[ERROR]: Incorrect error type. Should return %q, but returned %q", 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), CertifyKeyX509, CertifyAddIsCA); err == nil { + t.Errorf("[ERROR]: Should return %q, but returned no error", StatusInvalidArgument) + } else if !errors.Is(err, StatusInvalidArgument) { + t.Errorf("[ERROR]: Incorrect error type. Should return %q, but returned %q", StatusInvalidArgument, err) + } +} + +// Checks whether the number of derived contexts (TCI nodes) are limited by MAX_TCI_NODES attribute of the profile +func TestMaxTCIs(d TestDPEInstance, c DPEClient, t *testing.T) { + var resp *DeriveChildResp + + simulation := false + handle := getInitialContextHandle(d, c, t, simulation) + defer func() { c.DestroyContext(handle, DestroyDescendants) }() + + // Get digest size + profile, err := 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.DeriveChild(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.DeriveChild(handle, make([]byte, digestSize), 0, 0, 0) + if err == nil { + t.Fatalf("[FATAL]: Should return %q, but returned no error", StatusMaxTCIs) + } else if !errors.Is(err, StatusMaxTCIs) { + t.Fatalf("[FATAL]: Incorrect error type. Should return %q, but returned %q", StatusMaxTCIs, err) + } +} + +func TestDeriveChildSimulation(d TestDPEInstance, c 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 *DeriveChildResp + + simulation := true + handle := getInitialContextHandle(d, c, t, simulation) + defer func() { + c.DestroyContext(handle, DestroyDescendants) + }() + + // Get digest size + profile, err := 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.DeriveChild(handle, make([]byte, digestLen), MakeDefault, 0, 0); err == nil { + t.Errorf("[ERROR]: Should return %q, but returned no error", StatusInvalidArgument) + } else if !errors.Is(err, StatusInvalidArgument) { + t.Errorf("[ERROR]: Incorrect error type. Should return %q, but returned %q", StatusInvalidArgument, err) + } + + // Make default child context in other locality + childCtx, err := c.DeriveChild(handle, + make([]byte, digestLen), + DeriveChildFlags(ChangeLocality|RetainParent|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, DestroyDescendants) + 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, 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(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.DeriveChild(handle, make([]byte, digestLen), RetainParent, 0, 0); err == nil { + t.Errorf("[ERROR]: Should return %q, but returned no error", StatusInvalidArgument) + } else if !errors.Is(err, StatusInvalidArgument) { + t.Errorf("[ERROR]: Incorrect error type. Should return %q, but returned %q", StatusInvalidArgument, err) + } + + // Setting RetainParent flag should not invalidate the parent handle + if resp, err = c.DeriveChild(handle, make([]byte, digestLen), RetainParent, 0, 0); err != nil { + t.Fatalf("[FATAL]: Error while making child context and retaining parent handle %s", err) + } + handle = &resp.NewContextHandle + + if resp.ParentContextHandle == InvalidatedContextHandle { + t.Errorf("[ERROR]: Incorrect handle. Should return retained handle, but returned invalidated handle %v", parentHandle) + } +} diff --git a/verification/go.sum b/verification/go.sum index bba036dc..bfcab473 100644 --- a/verification/go.sum +++ b/verification/go.sum @@ -153,4 +153,4 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= \ No newline at end of file diff --git a/verification/simulator.go b/verification/simulator.go index 1e3c47f6..2e905477 100644 --- a/verification/simulator.go +++ b/verification/simulator.go @@ -253,6 +253,36 @@ func GetSimulatorTargets() []TestTarget { getTestTarget([]string{"AutoInit", "Simulation", "X509", "Csr", "IsCA", "RotateContext", "ExtendTci", "IsSymmetric"}), AllTestCases, }, + { + "DeriveChild", + getTestTarget([]string{"AutoInit", "X509", "IsCA"}), + []TestCase{DeriveChildTestCase}, + }, + { + "DeriveChild_Simulation", + getTestTarget([]string{"AutoInit", "Simulation", "X509", "IsCA"}), + []TestCase{DeriveChildSimulationTestCase}, + }, + { + "DeriveChild_PrivilegeEscalation", + getTestTarget([]string{"AutoInit", "X509", "IsCA"}), + []TestCase{DeriveChildPrivilegeEscalationTestCase}, + }, + { + "DeriveChild_InputFlags", + getTestTarget([]string{"AutoInit", "Simulation", "InternalDice", "InternalInfo"}), + []TestCase{DeriveChildInputFlagsTestCase}, + }, + { + "DeriveChild_MaxTCIs", + getTestTarget([]string{"AutoInit", "Simulation"}), + []TestCase{DeriveChildMaxTCIsTestCase}, + }, + { + "DeriveChild_ChangeLocality", + getTestTarget([]string{"AutoInit", "Simulation"}), + []TestCase{DeriveChildLocalityTestCase}, + }, { "GetProfile_Simulation", getTestTarget([]string{"Simulation"}), diff --git a/verification/verification.go b/verification/verification.go index 0ccea29e..f342ab8e 100644 --- a/verification/verification.go +++ b/verification/verification.go @@ -119,6 +119,25 @@ var SignSimulationTestCase = TestCase{ "SignSimulation", TestSignSimulation, []string{"Simulation"}, } +var DeriveChildTestCase = TestCase{ + "DeriveChild", TestDeriveChild, []string{"AutoInit"}, +} +var DeriveChildSimulationTestCase = TestCase{ + "DeriveChildSimulation", TestDeriveChildSimulation, []string{"AutoInit", "Simulation", "X509", "InternalDice", "InternalInfo"}, +} +var DeriveChildMaxTCIsTestCase = TestCase{ + "DeriveChild_MaxTCIs", TestMaxTCIs, []string{"AutoInit"}, +} +var DeriveChildLocalityTestCase = TestCase{ + "DeriveChild_ChangeLocality", TestChangeLocality, []string{"AutoInit"}, +} +var DeriveChildPrivilegeEscalationTestCase = TestCase{ + "DeriveChild_PrivilegeEscalation", TestPrivilegesEscalation, []string{"AutoInit", "X509", "IsCA"}, +} +var DeriveChildInputFlagsTestCase = TestCase{ + "DeriveChild_InputFlagsSupport", TestInternalInputFlags, []string{"AutoInit", "InternalDice", "InternalInfo"}, +} + // TpmPolicySigningTestCase tests using DPE to satisfy TPM PolicySigned var TpmPolicySigningTestCase = TestCase{ "TPMPolicySigning", TestTpmPolicySigning, []string{"AutoInit", "X509"}, @@ -145,6 +164,11 @@ var AllTestCases = []TestCase{ WrongLocalityTestCase, } +var IrreversibleTestCases = []TestCase{ + DeriveChildTestCase, + DeriveChildSimulationTestCase, +} + // 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