]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
test: add tests for systemd-cryptenroll --tpm2-seal-key-handle
authorDan Streetman <ddstreet@ieee.org>
Fri, 6 Oct 2023 15:14:25 +0000 (11:14 -0400)
committerDan Streetman <ddstreet@ieee.org>
Tue, 10 Oct 2023 09:56:45 +0000 (05:56 -0400)
In TEST-70-TPM2, test systemd-cryptenroll --tpm2-seal-key-handle using the
default (0) as well as the SRK handle (0x81000001), and test using a non-SRK
handle index after creating and persisting a primary key.

In test/test-tpm2, test tpm2_seal() and tpm2_unseal() using default (0), the SRK
handle, and a transient handle.

src/shared/tpm2-util.c
src/shared/tpm2-util.h
src/test/test-tpm2.c
test/TEST-70-TPM2/test.sh
test/units/testsuite-70.sh

index 5ee841e8e65e32486333162f53a8aa189145d38a..0b6d6f0ffcadbd364ac0cb5ad26244b94b6f76e4 100644 (file)
@@ -1083,7 +1083,7 @@ static int tpm2_get_legacy_template(TPMI_ALG_PUBLIC alg, TPMT_PUBLIC *ret_templa
  *
  * These templates are only needed to create a new persistent SRK (or a new transient key that is
  * SRK-compatible). Preferably, the TPM should contain a shared SRK located at the reserved shared SRK handle
- * (see TPM2_SRK_HANDLE and tpm2_get_srk() below).
+ * (see TPM2_SRK_HANDLE in tpm2-util.h, and tpm2_get_srk() below).
  *
  * The alg must be TPM2_ALG_RSA or TPM2_ALG_ECC. Returns error if the requested template is not supported on
  * this TPM. Also see tpm2_get_best_srk_template() below. */
@@ -1182,14 +1182,6 @@ static int tpm2_get_best_srk_template(Tpm2Context *c, TPMT_PUBLIC *ret_template)
                                "TPM does not support either SRK template L-1 (RSA) or L-2 (ECC).");
 }
 
-/* The SRK handle is defined in the Provisioning Guidance document (see above) in the table "Reserved Handles
- * for TPM Provisioning Fundamental Elements". The SRK is useful because it is "shared", meaning it has no
- * authValue nor authPolicy set, and thus may be used by anyone on the system to generate derived keys or
- * seal secrets. This is useful if the TPM has an auth (password) set for the 'owner hierarchy', which would
- * prevent users from generating primary transient keys, unless they knew the owner hierarchy auth. See
- * the Provisioning Guidance document for more details. */
-#define TPM2_SRK_HANDLE UINT32_C(0x81000001)
-
 /* Get the SRK. Returns 1 if SRK is found, 0 if there is no SRK, or < 0 on error. Also see
  * tpm2_get_or_create_srk() below. */
 static int tpm2_get_srk(
index 47e2c858c478eedb41e1477da1a2d62ec056e736..f593915449adec5e2a5a0c9e1522558c2883a931 100644 (file)
@@ -20,6 +20,14 @@ typedef enum TPM2Flags {
 #define TPM2_PCRS_MAX 24U
 #define TPM2_PCRS_MASK ((UINT32_C(1) << TPM2_PCRS_MAX) - 1)
 
+/* The SRK handle is defined in the Provisioning Guidance document (see above) in the table "Reserved Handles
+ * for TPM Provisioning Fundamental Elements". The SRK is useful because it is "shared", meaning it has no
+ * authValue nor authPolicy set, and thus may be used by anyone on the system to generate derived keys or
+ * seal secrets. This is useful if the TPM has an auth (password) set for the 'owner hierarchy', which would
+ * prevent users from generating primary transient keys, unless they knew the owner hierarchy auth. See
+ * the Provisioning Guidance document for more details. */
+#define TPM2_SRK_HANDLE UINT32_C(0x81000001)
+
 static inline bool TPM2_PCR_INDEX_VALID(unsigned pcr) {
         return pcr < TPM2_PCRS_MAX;
 }
index 7195bd613bc50988d1707b8a147a8331994c23bc..bbf2790a475bf77d08fcb620a79642c15f06c77b 100644 (file)
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include "hexdecoct.h"
+#include "macro.h"
 #include "tpm2-util.h"
 #include "tests.h"
 
@@ -702,21 +703,44 @@ TEST(parse_pcr_argument) {
         check_parse_pcr_argument_to_mask("debug+24", -EINVAL);
 }
 
-static void tpm2b_public_rsa_init(TPM2B_PUBLIC *public, const char *rsa_n) {
-        TPMT_PUBLIC tpmt = {
-                .type = TPM2_ALG_RSA,
-                .nameAlg = TPM2_ALG_SHA256,
-                .objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH,
-                .parameters.rsaDetail = {
-                        .symmetric = {
-                                .algorithm = TPM2_ALG_AES,
-                                .keyBits.aes = 128,
-                                .mode.aes = TPM2_ALG_CFB,
-                        },
-                        .scheme.scheme = TPM2_ALG_NULL,
-                        .keyBits = 2048,
+static const TPMT_PUBLIC test_rsa_template = {
+        .type = TPM2_ALG_RSA,
+        .nameAlg = TPM2_ALG_SHA256,
+        .objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH,
+        .parameters.rsaDetail = {
+                .symmetric = {
+                        .algorithm = TPM2_ALG_AES,
+                        .keyBits.aes = 128,
+                        .mode.aes = TPM2_ALG_CFB,
                 },
-        };
+                .scheme.scheme = TPM2_ALG_NULL,
+                .keyBits = 2048,
+        },
+};
+
+static const TPMT_PUBLIC test_ecc_template = {
+        .type = TPM2_ALG_ECC,
+        .nameAlg = TPM2_ALG_SHA256,
+        .objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH,
+        .parameters.eccDetail = {
+                .symmetric = {
+                        .algorithm = TPM2_ALG_AES,
+                        .keyBits.aes = 128,
+                        .mode.aes = TPM2_ALG_CFB,
+                },
+                .scheme.scheme = TPM2_ALG_NULL,
+                .curveID = TPM2_ECC_NIST_P256,
+                .kdf.scheme = TPM2_ALG_NULL,
+        },
+};
+
+static const TPMT_PUBLIC *test_templates[] = {
+        &test_rsa_template,
+        &test_ecc_template,
+};
+
+static void tpm2b_public_rsa_init(TPM2B_PUBLIC *public, const char *rsa_n) {
+        TPMT_PUBLIC tpmt = test_rsa_template;
 
         DEFINE_HEX_PTR(key, rsa_n);
         tpmt.unique.rsa = TPM2B_PUBLIC_KEY_RSA_MAKE(key, key_len);
@@ -726,21 +750,8 @@ static void tpm2b_public_rsa_init(TPM2B_PUBLIC *public, const char *rsa_n) {
 }
 
 static void tpm2b_public_ecc_init(TPM2B_PUBLIC *public, TPMI_ECC_CURVE curve, const char *x, const char *y) {
-        TPMT_PUBLIC tpmt = {
-                .type = TPM2_ALG_ECC,
-                .nameAlg = TPM2_ALG_SHA256,
-                .objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH,
-                .parameters.eccDetail = {
-                        .symmetric = {
-                                .algorithm = TPM2_ALG_AES,
-                                .keyBits.aes = 128,
-                                .mode.aes = TPM2_ALG_CFB,
-                        },
-                        .scheme.scheme = TPM2_ALG_NULL,
-                        .curveID = curve,
-                        .kdf.scheme = TPM2_ALG_NULL,
-                },
-        };
+        TPMT_PUBLIC tpmt = test_ecc_template;
+        tpmt.parameters.eccDetail.curveID = curve;
 
         DEFINE_HEX_PTR(buf_x, x);
         tpmt.unique.ecc.x = TPM2B_ECC_PARAMETER_MAKE(buf_x, buf_x_len);
@@ -950,15 +961,8 @@ TEST(calculate_policy_pcr) {
         assert_se(digest_check(&d, "7481fd1b116078eb3ac2456e4ad542c9b46b9b8eb891335771ca8e7c8f8e4415"));
 }
 
-TEST(tpm_required_tests) {
-        int r;
-
-        _cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
-        r = tpm2_context_new(NULL, &c);
-        if (r < 0) {
-                log_tests_skipped("Could not find TPM");
-                return;
-        }
+static void check_test_parms(Tpm2Context *c) {
+        assert(c);
 
         TPMU_PUBLIC_PARMS parms = {
                 .symDetail.sym = {
@@ -977,6 +981,10 @@ TEST(tpm_required_tests) {
 
         /* Test with valid parms */
         assert_se(tpm2_test_parms(c, TPM2_ALG_SYMCIPHER, &parms));
+}
+
+static void check_supports_alg(Tpm2Context *c) {
+        assert(c);
 
         /* Test invalid algs */
         assert_se(!tpm2_supports_alg(c, TPM2_ALG_ERROR));
@@ -986,6 +994,10 @@ TEST(tpm_required_tests) {
         assert_se(tpm2_supports_alg(c, TPM2_ALG_RSA));
         assert_se(tpm2_supports_alg(c, TPM2_ALG_AES));
         assert_se(tpm2_supports_alg(c, TPM2_ALG_CFB));
+}
+
+static void check_supports_command(Tpm2Context *c) {
+        assert(c);
 
         /* Test invalid commands. TPM specification Part 2 ("Structures") section "TPM_CC (Command Codes)"
          * states bits 31:30 and 28:16 are reserved and must be 0. */
@@ -1004,6 +1016,91 @@ TEST(tpm_required_tests) {
         assert_se(tpm2_supports_command(c, TPM2_CC_Unseal));
 }
 
+static void check_seal_unseal_for_handle(Tpm2Context *c, TPM2_HANDLE handle) {
+        TPM2B_DIGEST policy = TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE);
+
+        assert(c);
+
+        log_debug("Check seal/unseal for handle 0x%" PRIx32, handle);
+
+        _cleanup_free_ void *secret = NULL, *blob = NULL, *srk = NULL, *unsealed_secret = NULL;
+        size_t secret_size, blob_size, srk_size, unsealed_secret_size;
+        assert_se(tpm2_seal(
+                        c,
+                        handle,
+                        &policy,
+                        /* pin= */ NULL,
+                        &secret, &secret_size,
+                        &blob, &blob_size,
+                        /* ret_primary_alg= */ NULL,
+                        &srk, &srk_size) >= 0);
+
+        assert_se(tpm2_unseal(
+                        c,
+                        /* hash_pcr_mask= */ 0,
+                        /* pcr_bank= */ 0,
+                        /* pubkey= */ NULL, /* pubkey_size= */ 0,
+                        /* pubkey_pcr_mask= */ 0,
+                        /* signature= */ NULL,
+                        /* pin= */ NULL,
+                        /* primary_alg= */ 0,
+                        blob, blob_size,
+                        /* policy_hash= */ NULL, /* policy_hash_size= */ 0,
+                        srk, srk_size,
+                        &unsealed_secret, &unsealed_secret_size) >= 0);
+
+        assert_se(memcmp_nn(secret, secret_size, unsealed_secret, unsealed_secret_size) == 0);
+}
+
+static void check_seal_unseal(Tpm2Context *c) {
+        int r;
+
+        assert(c);
+
+        check_seal_unseal_for_handle(c, 0);
+        check_seal_unseal_for_handle(c, TPM2_SRK_HANDLE);
+
+        FOREACH_ARRAY(template, test_templates, ELEMENTSOF(test_templates)) {
+                TPM2B_PUBLIC public = {
+                        .publicArea = **template,
+                        .size = sizeof(**template),
+                };
+                _cleanup_(tpm2_handle_freep) Tpm2Handle *transient_handle = NULL;
+                assert_se(tpm2_create_primary(
+                                c,
+                                /* session= */ NULL,
+                                &public,
+                                /* sensitive= */ NULL,
+                                /* ret_public= */ NULL,
+                                &transient_handle) >= 0);
+
+                TPMI_DH_PERSISTENT transient_handle_index;
+                r = tpm2_index_from_handle(c, transient_handle, &transient_handle_index);
+                if (r == -EOPNOTSUPP) {
+                        /* libesys too old */
+                        log_tests_skipped("libesys too old for tpm2_index_from_handle");
+                        return;
+                }
+                assert_se(r >= 0);
+
+                check_seal_unseal_for_handle(c, transient_handle_index);
+        }
+}
+
+TEST_RET(tests_which_require_tpm) {
+        _cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
+
+        if (tpm2_context_new(NULL, &c) < 0)
+                return log_tests_skipped("Could not find TPM");
+
+        check_test_parms(c);
+        check_supports_alg(c);
+        check_supports_command(c);
+        check_seal_unseal(c);
+
+        return 0;
+}
+
 #endif /* HAVE_TPM2 */
 
 DEFINE_TEST_MAIN(LOG_DEBUG);
index ec1daf9afce8214c83d43f4de22dea7d550e97a4..3b3cce915402f6479e6d2faaf506e0a13c9a9583 100755 (executable)
@@ -11,7 +11,7 @@ TEST_REQUIRE_INSTALL_TESTS=0
 # shellcheck source=test/test-functions
 . "${TEST_BASE_DIR:?}/test-functions"
 
-test_require_bin swtpm tpm2_pcrextend tpm2_dictionarylockout
+test_require_bin openssl swtpm tpm2_createprimary tpm2_dictionarylockout tpm2_evictcontrol tpm2_flushcontext tpm2_pcrextend tpm2_pcrread
 
 test_append_files() {
     local workspace="${1:?}"
@@ -24,10 +24,13 @@ test_append_files() {
     fi
     install_dmevent
     generate_module_dependencies
+    inst_binary openssl
+    inst_binary tpm2_createprimary
     inst_binary tpm2_dictionarylockout
+    inst_binary tpm2_evictcontrol
+    inst_binary tpm2_flushcontext
     inst_binary tpm2_pcrextend
     inst_binary tpm2_pcrread
-    inst_binary openssl
 }
 
 do_test "$@"
index 9aba54a48f23c87e2a04e90f0d43b84d5b3701c6..d39624ef488f42970486ddfd44b4e44806f05d9a 100755 (executable)
@@ -8,6 +8,15 @@ SD_PCREXTEND="/usr/lib/systemd/systemd-pcrextend"
 SD_TPM2SETUP="/usr/lib/systemd/systemd-tpm2-setup"
 export SYSTEMD_LOG_LEVEL=debug
 
+trap cleanup ERR
+cleanup() {
+    # Evict the TPM primary key that we persisted
+    if [[ -n $persistent ]]; then
+        tpm2_evictcontrol -c "$persistent"
+    fi
+}
+persistent=""
+
 cryptsetup_has_token_plugin_support() {
     local plugin_path
 
@@ -131,6 +140,57 @@ if tpm_has_pcr sha256 12; then
     rm -f /tmp/pcr.dat
 fi
 
+# Use default (0) seal key handle
+systemd-cryptenroll --wipe-slot=tpm2 "$img"
+PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0 "$img"
+systemd-cryptsetup attach test-volume "$img" - tpm2-device=auto,headless=1
+systemd-cryptsetup detach test-volume
+
+systemd-cryptenroll --wipe-slot=tpm2 "$img"
+PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x0 "$img"
+systemd-cryptsetup attach test-volume "$img" - tpm2-device=auto,headless=1
+systemd-cryptsetup detach test-volume
+
+# Use SRK seal key handle
+systemd-cryptenroll --wipe-slot=tpm2 "$img"
+PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=81000001 "$img"
+systemd-cryptsetup attach test-volume "$img" - tpm2-device=auto,headless=1
+systemd-cryptsetup detach test-volume
+
+systemd-cryptenroll --wipe-slot=tpm2 "$img"
+PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x81000001 "$img"
+systemd-cryptsetup attach test-volume "$img" - tpm2-device=auto,headless=1
+systemd-cryptsetup detach test-volume
+
+# Test invalid ranges: pcr, nv, session, permanent
+systemd-cryptenroll --wipe-slot=tpm2 "$img"
+(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=7 "$img")          # PCR
+(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x01000001 "$img") # NV index
+(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x02000001 "$img") # HMAC/loaded session
+(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x03000001 "$img") # Policy/saved session
+(! PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle=0x40000001 "$img") # Permanent
+
+# Use non-SRK persistent seal key handle (by creating/persisting new key)
+primary=/tmp/primary.ctx
+tpm2_createprimary -c "$primary"
+persistent_line=$(tpm2_evictcontrol -c "$primary" | grep persistent-handle)
+persistent="0x${persistent_line##*0x}"
+tpm2_flushcontext -t
+
+systemd-cryptenroll --wipe-slot=tpm2 "$img"
+PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle="${persistent#0x}" "$img"
+systemd-cryptsetup attach test-volume "$img" - tpm2-device=auto,headless=1
+systemd-cryptsetup detach test-volume
+
+systemd-cryptenroll --wipe-slot=tpm2 "$img"
+PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-seal-key-handle="$persistent" "$img"
+systemd-cryptsetup attach test-volume "$img" - tpm2-device=auto,headless=1
+systemd-cryptsetup detach test-volume
+
+tpm2_evictcontrol -c "$persistent"
+persistent=""
+rm -f "$primary"
+
 rm -f "${img:?}"
 
 if [[ -x "$SD_MEASURE" ]]; then
@@ -390,4 +450,6 @@ if [[ -x "$SD_TPM2SETUP" ]]; then
     "$SD_TPM2SETUP" --early=no
 fi
 
+cleanup
+
 touch /testok