]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
creds-util: add automatic mode for tpm2 based creds
authorLennart Poettering <lennart@poettering.net>
Thu, 6 Jun 2024 09:25:50 +0000 (11:25 +0200)
committerLennart Poettering <lennart@poettering.net>
Sun, 2 Nov 2025 20:14:35 +0000 (21:14 +0100)
This reworkds TPM2 based creds a bit. Instead of mapping the key type
"tpm2" directly to a TPM2 key without PK, let's map it to an "automatic"
key type that either picks PK or doesn't, depending on what's available.
That should make things easier to grok for people, as the nitty gritty
details of PK or not PK are made autmatic. Moreover it gives us more
leverage to change the TPM2 enrollment types later (for example, we
definitely want to start pinning SRK, and hook up pcrlock too, for
creds, which we currently don't).

This hence adds a new _CRED_AUTO_TPM2
pseudo-type we automatically maps to CRED_AES256_GCM_BY_TPM2_HMAC_WITH_PK
or CRED_AES256_GCM_BY_TPM2_HMAC depending if PK as available. Similar,
_CRED_AUTO_HOST_AND_TPM2 is added, which does the same for the
host/nonhost cred type.

This does not introduce any new type on the wire, it just changes how we
select the right key type.

To make the code more readable this also adds some categorization macros
for the keys, instead of repeating the list of key types at multiple
places.

man/systemd-creds.xml
src/creds/creds.c
src/shared/creds-util.c
src/shared/creds-util.h

index 7b9ce14e5f4df686b196fadc22b95103a7a7596b..60ded4219327e31f3fd09e5413d075b3ea8063fd 100644 (file)
         <listitem><para>When specified with the <command>encrypt</command> command controls the
         encryption/signature key to use. Takes one of <literal>host</literal>, <literal>tpm2</literal>,
         <literal>host+tpm2</literal>, <literal>null</literal>, <literal>auto</literal>,
-        <literal>auto-initrd</literal>. See above for details on the three key types. If set to
+        <literal>auto-initrd</literal>. See above for details on the key types. If set to
         <literal>auto</literal> (which is the default) the TPM2 key is used if a TPM2 device is found and not
         running in a container. The host key is used if <filename>/var/lib/systemd/</filename> is on
         persistent media. This means on typical systems the encryption is by default bound to both the TPM2
         chip and the OS installation, and both need to be available to decrypt the credential again. If
         <literal>auto</literal> is selected but neither TPM2 is available (or running in container) nor
         <filename>/var/lib/systemd/</filename> is on persistent media, encryption will fail. If set to
-        <literal>null</literal> a fixed zero length key is used (thus, in this mode no confidentiality
-        nor authenticity are provided!). This logic is useful to cover for systems that lack a TPM2 chip but
+        <literal>null</literal> a fixed zero length key is used (thus, in this mode no confidentiality nor
+        authenticity are provided!). This logic is useful to cover for systems that lack a TPM2 chip but
         where credentials shall be generated. Note that decryption of such credentials is refused on systems
         that have a TPM2 chip and where UEFI SecureBoot is enabled (this is done so that such a locked down
         system cannot be tricked into loading a credential generated this way that lacks authentication
index 92b527270d1b5c6b6c52aaf5fe8648b99ebc31e3..a4a90dc88355657ce5e61ccbe5b399a8ea8c0157 100644 (file)
@@ -958,7 +958,6 @@ static int parse_argv(int argc, char *argv[]) {
                         if (streq(optarg, "help")) {
                                 if (arg_legend)
                                         puts("Supported key types:");
-
                                 return DUMP_STRING_TABLE(cred_key_type, CredKeyType, _CRED_KEY_TYPE_MAX);
                         }
 
@@ -978,7 +977,7 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case 'T':
-                        arg_with_key = CRED_AES256_GCM_BY_TPM2_HMAC;
+                        arg_with_key = _CRED_AUTO_TPM2;
                         break;
 
                 case ARG_TPM2_DEVICE:
index fe3fce3576b1788230fb0c4669c2c4be8daf826c..a190e3720ca4f5ef24658bd06ba65a4f12652b69 100644 (file)
@@ -815,19 +815,7 @@ int encrypt_credential_and_warn(
         /* Only one of these two flags may be set at the same time */
         assert(!FLAGS_SET(flags, CREDENTIAL_ALLOW_NULL) || !FLAGS_SET(flags, CREDENTIAL_REFUSE_NULL));
 
-        if (!sd_id128_in_set(with_key,
-                             _CRED_AUTO,
-                             _CRED_AUTO_INITRD,
-                             _CRED_AUTO_SCOPED,
-                             CRED_AES256_GCM_BY_HOST,
-                             CRED_AES256_GCM_BY_HOST_SCOPED,
-                             CRED_AES256_GCM_BY_TPM2_HMAC,
-                             CRED_AES256_GCM_BY_TPM2_HMAC_WITH_PK,
-                             CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC,
-                             CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_SCOPED,
-                             CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_WITH_PK,
-                             CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_WITH_PK_SCOPED,
-                             CRED_AES256_GCM_BY_NULL))
+        if (!CRED_KEY_IS_VALID(with_key) && !CRED_KEY_IS_AUTO(with_key))
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid key type: " SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(with_key));
 
         if (name && !credential_name_valid(name))
@@ -847,33 +835,21 @@ int encrypt_credential_and_warn(
                         log_debug("Including not-after timestamp '%s' in encrypted credential.", format_timestamp(buf, sizeof(buf), not_after));
         }
 
-        if (sd_id128_in_set(with_key,
-                            _CRED_AUTO_SCOPED,
-                            CRED_AES256_GCM_BY_HOST_SCOPED,
-                            CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_SCOPED,
-                            CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_WITH_PK_SCOPED)) {
+        if (CRED_KEY_IS_SCOPED(with_key)) {
                 if (!uid_is_valid(uid))
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                                "Scoped credential key type "SD_ID128_FORMAT_STR" selected, but no UID specified.", SD_ID128_FORMAT_VAL(with_key));
         } else
                 uid = UID_INVALID;
 
-        if (sd_id128_in_set(with_key,
-                            _CRED_AUTO,
-                            _CRED_AUTO_SCOPED,
-                            CRED_AES256_GCM_BY_HOST,
-                            CRED_AES256_GCM_BY_HOST_SCOPED,
-                            CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC,
-                            CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_SCOPED,
-                            CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_WITH_PK,
-                            CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_WITH_PK_SCOPED)) {
+        if (CRED_KEY_WANTS_HOST(with_key) || CRED_KEY_REQUIRES_HOST(with_key)) {
 
                 r = get_credential_host_secret(
                                 CREDENTIAL_SECRET_GENERATE|
                                 CREDENTIAL_SECRET_WARN_NOT_ENCRYPTED|
-                                (sd_id128_in_set(with_key, _CRED_AUTO, _CRED_AUTO_SCOPED) ? CREDENTIAL_SECRET_FAIL_ON_TEMPORARY_FS : 0),
+                                (CRED_KEY_WANTS_HOST(with_key) ? CREDENTIAL_SECRET_FAIL_ON_TEMPORARY_FS : 0),
                                 &host_key);
-                if (r == -ENOMEDIUM && sd_id128_in_set(with_key, _CRED_AUTO, _CRED_AUTO_SCOPED))
+                if (r == -ENOMEDIUM && CRED_KEY_WANTS_HOST(with_key))
                         log_debug_errno(r, "Credential host secret location on temporary file system, not using.");
                 else if (r < 0)
                         return log_error_errno(r, "Failed to determine local credential host secret: %m");
@@ -881,7 +857,7 @@ int encrypt_credential_and_warn(
 
 #if HAVE_TPM2
         bool try_tpm2;
-        if (sd_id128_in_set(with_key, _CRED_AUTO, _CRED_AUTO_INITRD, _CRED_AUTO_SCOPED)) {
+        if (CRED_KEY_WANTS_TPM2(with_key)) {
                 /* If automatic mode is selected lets see if a TPM2 it is present. If we are running in a
                  * container tpm2_support will detect this, and will return a different flag combination of
                  * TPM2_SUPPORT_FULL, effectively skipping the use of TPM2 when inside one. */
@@ -890,28 +866,16 @@ int encrypt_credential_and_warn(
                 if (!try_tpm2)
                         log_debug("System lacks TPM2 support or running in a container, not attempting to use TPM2.");
         } else
-                try_tpm2 = sd_id128_in_set(with_key,
-                                           CRED_AES256_GCM_BY_TPM2_HMAC,
-                                           CRED_AES256_GCM_BY_TPM2_HMAC_WITH_PK,
-                                           CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC,
-                                           CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_SCOPED,
-                                           CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_WITH_PK,
-                                           CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_WITH_PK_SCOPED);
+                try_tpm2 = CRED_KEY_REQUIRES_TPM2(with_key);
 
         if (try_tpm2) {
-                if (sd_id128_in_set(with_key,
-                                    _CRED_AUTO,
-                                    _CRED_AUTO_INITRD,
-                                    _CRED_AUTO_SCOPED,
-                                    CRED_AES256_GCM_BY_TPM2_HMAC_WITH_PK,
-                                    CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_WITH_PK,
-                                    CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_WITH_PK_SCOPED)) {
+                if (CRED_KEY_WANTS_TPM2_PK(with_key) || CRED_KEY_REQUIRES_TPM2_PK(with_key)) {
 
                         /* Load public key for PCR policies, if one is specified, or explicitly requested */
 
                         r = tpm2_load_pcr_public_key(tpm2_pubkey_path, &pubkey.iov_base, &pubkey.iov_len);
                         if (r < 0) {
-                                if (tpm2_pubkey_path || r != -ENOENT || !sd_id128_in_set(with_key, _CRED_AUTO, _CRED_AUTO_INITRD, _CRED_AUTO_SCOPED))
+                                if (r != -ENOENT || tpm2_pubkey_path || CRED_KEY_REQUIRES_TPM2_PK(with_key))
                                         return log_error_errno(r, "Failed to read TPM PCR public key: %m");
 
                                 log_debug_errno(r, "Failed to read TPM2 PCR public key, proceeding without: %m");
@@ -991,7 +955,7 @@ int encrypt_credential_and_warn(
         }
 #endif
 
-        if (sd_id128_in_set(with_key, _CRED_AUTO, _CRED_AUTO_INITRD, _CRED_AUTO_SCOPED)) {
+        if (CRED_KEY_IS_AUTO(with_key)) {
                 /* Let's settle the key type in auto mode now. */
 
                 if (iovec_is_set(&host_key) && iovec_is_set(&tpm2_key))
@@ -1200,7 +1164,6 @@ int decrypt_credential_and_warn(
         struct encrypted_credential_header *h;
         struct metadata_credential_header *m;
         uint8_t md[SHA256_DIGEST_LENGTH];
-        bool with_tpm2, with_tpm2_pk, with_host_key, with_null, with_scope;
         const EVP_CIPHER *cc;
         size_t p, hs;
         int r, added;
@@ -1230,16 +1193,10 @@ int decrypt_credential_and_warn(
         if (input->iov_len < sizeof(h->id))
                 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Encrypted file too short.");
 
-        with_host_key = sd_id128_in_set(h->id, CRED_AES256_GCM_BY_HOST, CRED_AES256_GCM_BY_HOST_SCOPED, CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC, CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_SCOPED, CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_WITH_PK, CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_WITH_PK_SCOPED);
-        with_tpm2_pk = sd_id128_in_set(h->id, CRED_AES256_GCM_BY_TPM2_HMAC_WITH_PK, CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_WITH_PK, CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_WITH_PK_SCOPED);
-        with_tpm2 = sd_id128_in_set(h->id, CRED_AES256_GCM_BY_TPM2_HMAC, CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC, CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_SCOPED) || with_tpm2_pk;
-        with_null = sd_id128_equal(h->id, CRED_AES256_GCM_BY_NULL);
-        with_scope = sd_id128_in_set(h->id, CRED_AES256_GCM_BY_HOST_SCOPED, CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_SCOPED, CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_WITH_PK_SCOPED);
-
-        if (!with_host_key && !with_tpm2 && !with_null)
+        if (!CRED_KEY_IS_VALID(h->id))
                 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Unknown encryption format, or corrupted data.");
 
-        if (with_tpm2_pk) {
+        if (CRED_KEY_REQUIRES_TPM2_PK(h->id)) {
                 r = tpm2_load_pcr_signature(tpm2_signature_path, &signature_json);
                 if (r == -ENOENT)
                         return log_error_errno(SYNTHETIC_ERRNO(EHOSTDOWN), "Couldn't find PCR signature file: %m");
@@ -1247,7 +1204,7 @@ int decrypt_credential_and_warn(
                         return log_error_errno(r, "Failed to load PCR signature: %m");
         }
 
-        if (with_null) {
+        if (sd_id128_equal(h->id, CRED_AES256_GCM_BY_NULL)) {
                 if (FLAGS_SET(flags, CREDENTIAL_REFUSE_NULL))
                         return log_error_errno(SYNTHETIC_ERRNO(EHWPOISON),
                                                "Credential uses null key, but that's not allowed, refusing.");
@@ -1273,7 +1230,7 @@ int decrypt_credential_and_warn(
                 }
         }
 
-        if (with_scope) {
+        if (CRED_KEY_IS_SCOPED(h->id)) {
                 if (!uid_is_valid(uid))
                         return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE), "Encrypted file is scoped to a user, but no user selected.");
         } else {
@@ -1302,16 +1259,16 @@ int decrypt_credential_and_warn(
          * lower limit only) */
         if (input->iov_len <
             ALIGN8(offsetof(struct encrypted_credential_header, iv) + le32toh(h->iv_size)) +
-            ALIGN8(with_tpm2 ? offsetof(struct tpm2_credential_header, policy_hash_and_blob) : 0) +
-            ALIGN8(with_tpm2_pk ? offsetof(struct tpm2_public_key_credential_header, data) : 0) +
-            ALIGN8(with_scope ? sizeof(struct scoped_credential_header) : 0) +
+            ALIGN8(CRED_KEY_REQUIRES_TPM2(h->id) ? offsetof(struct tpm2_credential_header, policy_hash_and_blob) : 0) +
+            ALIGN8(CRED_KEY_REQUIRES_TPM2_PK(h->id) ? offsetof(struct tpm2_public_key_credential_header, data) : 0) +
+            ALIGN8(CRED_KEY_IS_SCOPED(h->id) ? sizeof(struct scoped_credential_header) : 0) +
             ALIGN8(offsetof(struct metadata_credential_header, name)) +
             le32toh(h->tag_size))
                 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Encrypted file too short.");
 
         p = ALIGN8(offsetof(struct encrypted_credential_header, iv) + le32toh(h->iv_size));
 
-        if (with_tpm2) {
+        if (CRED_KEY_REQUIRES_TPM2(h->id)) {
 #if HAVE_TPM2
                 struct tpm2_credential_header* t = (struct tpm2_credential_header*) ((uint8_t*) input->iov_base + p);
                 struct tpm2_public_key_credential_header *z = NULL;
@@ -1332,8 +1289,8 @@ int decrypt_credential_and_warn(
                 if (input->iov_len <
                     p +
                     ALIGN8(offsetof(struct tpm2_credential_header, policy_hash_and_blob) + le32toh(t->blob_size) + le32toh(t->policy_hash_size)) +
-                    ALIGN8(with_tpm2_pk ? offsetof(struct tpm2_public_key_credential_header, data) : 0) +
-                    ALIGN8(with_scope ? sizeof(struct scoped_credential_header) : 0) +
+                    ALIGN8(CRED_KEY_REQUIRES_TPM2_PK(h->id) ? offsetof(struct tpm2_public_key_credential_header, data) : 0) +
+                    ALIGN8(CRED_KEY_IS_SCOPED(h->id) ? sizeof(struct scoped_credential_header) : 0) +
                     ALIGN8(offsetof(struct metadata_credential_header, name)) +
                     le32toh(h->tag_size))
                         return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Encrypted file too short.");
@@ -1342,7 +1299,7 @@ int decrypt_credential_and_warn(
                             le32toh(t->blob_size) +
                             le32toh(t->policy_hash_size));
 
-                if (with_tpm2_pk) {
+                if (CRED_KEY_REQUIRES_TPM2_PK(h->id)) {
                         z = (struct tpm2_public_key_credential_header*) ((uint8_t*) input->iov_base + p);
 
                         if (!TPM2_PCR_MASK_VALID(le64toh(z->pcr_mask)) || le64toh(z->pcr_mask) == 0)
@@ -1353,7 +1310,7 @@ int decrypt_credential_and_warn(
                         if (input->iov_len <
                             p +
                             ALIGN8(offsetof(struct tpm2_public_key_credential_header, data) + le32toh(z->size)) +
-                            ALIGN8(with_scope ? sizeof(struct scoped_credential_header) : 0) +
+                            ALIGN8(CRED_KEY_IS_SCOPED(h->id) ? sizeof(struct scoped_credential_header) : 0) +
                             ALIGN8(offsetof(struct metadata_credential_header, name)) +
                             le32toh(h->tag_size))
                                 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Encrypted file too short.");
@@ -1395,7 +1352,7 @@ int decrypt_credential_and_warn(
 #endif
         }
 
-        if (with_scope) {
+        if (CRED_KEY_IS_SCOPED(h->id)) {
                 struct scoped_credential_header* sh = (struct scoped_credential_header*) ((uint8_t*) input->iov_base + p);
 
                 if (le64toh(sh->flags) != SCOPE_HASH_DATA_BASE_FLAGS)
@@ -1411,18 +1368,18 @@ int decrypt_credential_and_warn(
                 p += sizeof(struct scoped_credential_header);
         }
 
-        if (with_host_key) {
+        if (CRED_KEY_REQUIRES_HOST(h->id)) {
                 r = get_credential_host_secret(/* flags= */ 0, &host_key);
                 if (r < 0)
                         return log_error_errno(r, "Failed to determine local credential key: %m");
         }
 
-        if (with_null && !FLAGS_SET(flags, CREDENTIAL_ALLOW_NULL))
+        if (sd_id128_equal(h->id, CRED_AES256_GCM_BY_NULL) && !FLAGS_SET(flags, CREDENTIAL_ALLOW_NULL))
                 log_warning("Warning: using a null key for decryption and authentication. Confidentiality or authenticity are not provided.");
 
         sha256_hash_host_and_tpm2_key(&host_key, &tpm2_key, md);
 
-        if (with_scope) {
+        if (CRED_KEY_IS_SCOPED(h->id)) {
                 r = mangle_uid_into_key(uid, md);
                 if (r < 0)
                         return r;
index d23afa6c135ea7cd0114a9d092c166306e5e55c2..e5194c7f07af21137fbff9331bfa4782cf85bf30 100644 (file)
@@ -79,15 +79,90 @@ typedef enum CredentialFlags {
                                               SD_ID128_MAKE(ad,bc,4c,a3,ef,b6,42,01,ba,88,1b,6f,2e,40,95,ea)
 #define CRED_AES256_GCM_BY_NULL               SD_ID128_MAKE(05,84,69,da,f6,f5,43,24,80,05,49,da,0f,8e,a2,fb)
 
-/* Two special IDs to pick a general automatic mode (i.e. tpm2+host if TPM2 exists, only host otherwise) or
- * an initrd-specific automatic mode (i.e. tpm2 if firmware can do it, otherwise fixed zero-length key, and
- * never involve host keys). These IDs will never be stored on disk, but are useful only internally while
- * figuring out what precisely to write to disk. To mark that these aren't a "real" type, we'll prefix them
- * with an underscore. */
+/* Five special IDs to pick a general automatic mode. These IDs will never be stored on disk, but are useful
+ * only internally while figuring out what precisely to write to disk. To mark that these aren't a "real"
+ * type, we'll prefix them with an underscore. */
+
+/* Use TPM2 if available + host if available and on physical media. If neither are available, fail. */
 #define _CRED_AUTO                            SD_ID128_MAKE(a2,19,cb,07,85,b2,4c,04,b1,6d,18,ca,b9,d2,ee,01)
+
+/* Use best TPM2, and do not use host, and fail if no TPM */
+#define _CRED_AUTO_TPM2                       SD_ID128_MAKE(45,f3,a6,7e,0c,12,42,56,a4,ee,75,eb,44,c6,5a,6f)
+
+/* Use TPM2 *and* host, and fail if one of the two isn't available. */
+#define _CRED_AUTO_HOST_AND_TPM2              SD_ID128_MAKE(da,f6,7a,60,d3,eb,47,b3,a9,be,2f,d5,fe,c2,15,22)
+
+/* Like _CRED_AUTO_TPM2, but uses "null" if not TPM is around */
 #define _CRED_AUTO_INITRD                     SD_ID128_MAKE(02,dc,8e,de,3a,02,43,ab,a9,ec,54,9c,05,e6,a0,71)
+
+/* Like _CRED_AUTO, but with per-UID scoping */
 #define _CRED_AUTO_SCOPED                     SD_ID128_MAKE(23,88,96,85,6f,74,48,8a,9c,78,6f,6a,b0,e7,3b,6a)
 
+#define CRED_KEY_IS_VALID(key)                                          \
+        sd_id128_in_set((key),                                          \
+                        CRED_AES256_GCM_BY_HOST,                        \
+                        CRED_AES256_GCM_BY_HOST_SCOPED,                 \
+                        CRED_AES256_GCM_BY_TPM2_HMAC,                   \
+                        CRED_AES256_GCM_BY_TPM2_HMAC_WITH_PK,           \
+                        CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC,          \
+                        CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_SCOPED,   \
+                        CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_WITH_PK,  \
+                        CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_WITH_PK_SCOPED, \
+                        CRED_AES256_GCM_BY_NULL)
+#define CRED_KEY_IS_AUTO(key)                                           \
+        sd_id128_in_set((key),                                          \
+                        _CRED_AUTO,                                     \
+                        _CRED_AUTO_TPM2,                                \
+                        _CRED_AUTO_HOST_AND_TPM2,                       \
+                        _CRED_AUTO_INITRD,                              \
+                        _CRED_AUTO_SCOPED)
+#define CRED_KEY_IS_SCOPED(key)                                         \
+        sd_id128_in_set((key),                                          \
+                        _CRED_AUTO_SCOPED,                              \
+                        CRED_AES256_GCM_BY_HOST_SCOPED,                 \
+                        CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_SCOPED,   \
+                        CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_WITH_PK_SCOPED)
+#define CRED_KEY_REQUIRES_HOST(key)                                     \
+        sd_id128_in_set((key),                                          \
+                        _CRED_AUTO_HOST_AND_TPM2,                       \
+                        CRED_AES256_GCM_BY_HOST,                        \
+                        CRED_AES256_GCM_BY_HOST_SCOPED,                 \
+                        CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC,          \
+                        CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_SCOPED,   \
+                        CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_WITH_PK,  \
+                        CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_WITH_PK_SCOPED)
+#define CRED_KEY_WANTS_HOST(key)                                        \
+        sd_id128_in_set((key),                                          \
+                        _CRED_AUTO,                                     \
+                        _CRED_AUTO_SCOPED)
+#define CRED_KEY_REQUIRES_TPM2(key)                                     \
+        sd_id128_in_set((key),                                          \
+                        _CRED_AUTO_TPM2,                                \
+                        _CRED_AUTO_HOST_AND_TPM2,                       \
+                        CRED_AES256_GCM_BY_TPM2_HMAC,                   \
+                        CRED_AES256_GCM_BY_TPM2_HMAC_WITH_PK,           \
+                        CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC,          \
+                        CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_SCOPED,   \
+                        CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_WITH_PK,  \
+                        CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_WITH_PK_SCOPED)
+#define CRED_KEY_WANTS_TPM2(key)                                        \
+        sd_id128_in_set((key),                                          \
+                        _CRED_AUTO,                                     \
+                        _CRED_AUTO_INITRD,                              \
+                        _CRED_AUTO_SCOPED)
+#define CRED_KEY_REQUIRES_TPM2_PK(key)                                  \
+        sd_id128_in_set((key),                                          \
+                        CRED_AES256_GCM_BY_TPM2_HMAC_WITH_PK,           \
+                        CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_WITH_PK,  \
+                        CRED_AES256_GCM_BY_HOST_AND_TPM2_HMAC_WITH_PK_SCOPED)
+#define CRED_KEY_WANTS_TPM2_PK(key)                                     \
+        sd_id128_in_set((key),                                          \
+                        _CRED_AUTO,                                     \
+                        _CRED_AUTO_TPM2,                                \
+                        _CRED_AUTO_HOST_AND_TPM2,                       \
+                        _CRED_AUTO_INITRD,                              \
+                        _CRED_AUTO_SCOPED)
+
 int encrypt_credential_and_warn(sd_id128_t with_key, const char *name, usec_t timestamp, usec_t not_after, const char *tpm2_device, uint32_t tpm2_hash_pcr_mask, const char *tpm2_pubkey_path, uint32_t tpm2_pubkey_pcr_mask, uid_t uid, const struct iovec *input, CredentialFlags flags, struct iovec *ret);
 int decrypt_credential_and_warn(const char *validate_name, usec_t validate_timestamp, const char *tpm2_device, const char *tpm2_signature_path, uid_t uid, const struct iovec *input, CredentialFlags flags, struct iovec *ret);