From 240774f5ce70f0bcbf64999a3db5c25be3f44a9c Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Tue, 1 Aug 2023 14:09:04 -0400 Subject: [PATCH 01/20] tpm2: add debug logging to functions converting hash or asym algs to/from strings or ids Add debug log message if the algorithm name or id is not known. --- src/shared/tpm2-util.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index bc4f7f3c90..a26bf35c70 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -4248,6 +4248,7 @@ const char *tpm2_hash_alg_to_string(uint16_t alg) { return "sha384"; if (alg == TPM2_ALG_SHA512) return "sha512"; + log_debug("Unknown hash algorithm id 0x%" PRIx16, alg); return NULL; } @@ -4260,7 +4261,7 @@ int tpm2_hash_alg_from_string(const char *alg) { return TPM2_ALG_SHA384; if (strcaseeq_ptr(alg, "sha512")) return TPM2_ALG_SHA512; - return -EINVAL; + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown hash algorithm name '%s'", alg); } const char *tpm2_asym_alg_to_string(uint16_t alg) { @@ -4268,6 +4269,7 @@ const char *tpm2_asym_alg_to_string(uint16_t alg) { return "ecc"; if (alg == TPM2_ALG_RSA) return "rsa"; + log_debug("Unknown asymmetric algorithm id 0x%" PRIx16, alg); return NULL; } @@ -4276,7 +4278,7 @@ int tpm2_asym_alg_from_string(const char *alg) { return TPM2_ALG_ECC; if (strcaseeq_ptr(alg, "rsa")) return TPM2_ALG_RSA; - return -EINVAL; + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown asymmetric algorithm name '%s'", alg); } Tpm2Support tpm2_support(void) { From c9df1fb119b3e57b0468457cc681920f453ff6e7 Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Tue, 11 Jul 2023 11:11:59 -0400 Subject: [PATCH 02/20] tpm2: add tpm2_hash_alg_to_size() Add function to get the hash size for a hash algorithm --- src/shared/tpm2-util.c | 12 ++++++++++++ src/shared/tpm2-util.h | 2 ++ 2 files changed, 14 insertions(+) diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index a26bf35c70..88cd7144b9 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -4239,6 +4239,18 @@ int tpm2_parse_luks2_json( return 0; } +int tpm2_hash_alg_to_size(uint16_t alg) { + if (alg == TPM2_ALG_SHA1) + return 20; + if (alg == TPM2_ALG_SHA256) + return 32; + if (alg == TPM2_ALG_SHA384) + return 48; + if (alg == TPM2_ALG_SHA512) + return 64; + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown hash algorithm id 0x%" PRIx16, alg); +} + const char *tpm2_hash_alg_to_string(uint16_t alg) { if (alg == TPM2_ALG_SHA1) return "sha1"; diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h index 12f05b43f9..9ea8e5bda3 100644 --- a/src/shared/tpm2-util.h +++ b/src/shared/tpm2-util.h @@ -164,6 +164,8 @@ int tpm2_parse_luks2_json(JsonVariant *v, int *ret_keyslot, uint32_t *ret_hash_p #define TPM2_ALG_RSA 0x1 #endif +int tpm2_hash_alg_to_size(uint16_t alg); + const char *tpm2_hash_alg_to_string(uint16_t alg); int tpm2_hash_alg_from_string(const char *alg); From dbaae766c7eaacdfb19ee23600f0f382a16ae33b Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Tue, 1 Aug 2023 12:55:17 -0400 Subject: [PATCH 03/20] tpm2: change tpm2_tpm*_pcr_selection_to_mask() to return mask This simplifies use of the functions, as well as avoiding the use of -ENOENT from tpm2_tpml_pcr_selection_to_mask(). --- src/shared/tpm2-util.c | 52 ++++++++++++++++-------------------------- src/shared/tpm2-util.h | 4 ++-- src/test/test-tpm2.c | 8 ++----- 3 files changed, 24 insertions(+), 40 deletions(-) diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index 88cd7144b9..be25790444 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -1204,15 +1204,14 @@ static int tpm2_get_or_create_srk( /* Utility functions for TPMS_PCR_SELECTION. */ /* Convert a TPMS_PCR_SELECTION object to a mask. */ -void tpm2_tpms_pcr_selection_to_mask(const TPMS_PCR_SELECTION *s, uint32_t *ret) { +uint32_t tpm2_tpms_pcr_selection_to_mask(const TPMS_PCR_SELECTION *s) { assert(s); assert(s->sizeofSelect <= sizeof(s->pcrSelect)); - assert(ret); uint32_t mask = 0; for (unsigned i = 0; i < s->sizeofSelect; i++) SET_FLAG(mask, (uint32_t)s->pcrSelect[i] << (i * 8), true); - *ret = mask; + return mask; } /* Convert a mask and hash alg to a TPMS_PCR_SELECTION object. */ @@ -1235,25 +1234,27 @@ void tpm2_tpms_pcr_selection_from_mask(uint32_t mask, TPMI_ALG_HASH hash_alg, TP /* Add all PCR selections in 'b' to 'a'. Both must have the same hash alg. */ void tpm2_tpms_pcr_selection_add(TPMS_PCR_SELECTION *a, const TPMS_PCR_SELECTION *b) { + uint32_t maska, maskb; + assert(a); assert(b); assert(a->hash == b->hash); - uint32_t maska, maskb; - tpm2_tpms_pcr_selection_to_mask(a, &maska); - tpm2_tpms_pcr_selection_to_mask(b, &maskb); + maska = tpm2_tpms_pcr_selection_to_mask(a); + maskb = tpm2_tpms_pcr_selection_to_mask(b); tpm2_tpms_pcr_selection_from_mask(maska | maskb, a->hash, a); } /* Remove all PCR selections in 'b' from 'a'. Both must have the same hash alg. */ void tpm2_tpms_pcr_selection_sub(TPMS_PCR_SELECTION *a, const TPMS_PCR_SELECTION *b) { + uint32_t maska, maskb; + assert(a); assert(b); assert(a->hash == b->hash); - uint32_t maska, maskb; - tpm2_tpms_pcr_selection_to_mask(a, &maska); - tpm2_tpms_pcr_selection_to_mask(b, &maskb); + maska = tpm2_tpms_pcr_selection_to_mask(a); + maskb = tpm2_tpms_pcr_selection_to_mask(b); tpm2_tpms_pcr_selection_from_mask(maska & ~maskb, a->hash, a); } @@ -1269,11 +1270,7 @@ void tpm2_tpms_pcr_selection_move(TPMS_PCR_SELECTION *a, TPMS_PCR_SELECTION *b) #define FOREACH_PCR_IN_TPMS_PCR_SELECTION(pcr, tpms) \ _FOREACH_PCR_IN_TPMS_PCR_SELECTION(pcr, tpms, UNIQ) #define _FOREACH_PCR_IN_TPMS_PCR_SELECTION(pcr, tpms, uniq) \ - FOREACH_PCR_IN_MASK(pcr, \ - ({ uint32_t UNIQ_T(_mask, uniq); \ - tpm2_tpms_pcr_selection_to_mask(tpms, &UNIQ_T(_mask, uniq)); \ - UNIQ_T(_mask, uniq); \ - })) + FOREACH_PCR_IN_MASK(pcr, tpm2_tpms_pcr_selection_to_mask(tpms)) #define FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(tpms, tpml) \ UNIQ_FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(tpms, tpml, UNIQ) @@ -1295,21 +1292,17 @@ char *tpm2_tpms_pcr_selection_to_string(const TPMS_PCR_SELECTION *s) { const char *algstr = strna(tpm2_hash_alg_to_string(s->hash)); - uint32_t mask; - tpm2_tpms_pcr_selection_to_mask(s, &mask); - _cleanup_free_ char *maskstr = tpm2_pcr_mask_to_string(mask); - if (!maskstr) + _cleanup_free_ char *mask = tpm2_pcr_mask_to_string(tpm2_tpms_pcr_selection_to_mask(s)); + if (!mask) return NULL; - return strjoin(algstr, "(", maskstr, ")"); + return strjoin(algstr, "(", mask, ")"); } size_t tpm2_tpms_pcr_selection_weight(const TPMS_PCR_SELECTION *s) { assert(s); - uint32_t mask; - tpm2_tpms_pcr_selection_to_mask(s, &mask); - return popcount(mask); + return popcount(tpm2_tpms_pcr_selection_to_mask(s)); } /* Utility functions for TPML_PCR_SELECTION. */ @@ -1360,10 +1353,9 @@ static TPMS_PCR_SELECTION *tpm2_tpml_pcr_selection_get_tpms_pcr_selection( return selection; } -/* Convert a TPML_PCR_SELECTION object to a mask. Returns -ENOENT if 'hash_alg' is not in the object. */ -int tpm2_tpml_pcr_selection_to_mask(const TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash_alg, uint32_t *ret) { +/* Convert a TPML_PCR_SELECTION object to a mask. Returns empty mask (i.e. 0) if 'hash_alg' is not in the object. */ +uint32_t tpm2_tpml_pcr_selection_to_mask(const TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash_alg) { assert(l); - assert(ret); /* Make a copy, as tpm2_tpml_pcr_selection_get_tpms_pcr_selection() will modify the object if there * are multiple entries with the requested hash alg. */ @@ -1372,10 +1364,9 @@ int tpm2_tpml_pcr_selection_to_mask(const TPML_PCR_SELECTION *l, TPMI_ALG_HASH h TPMS_PCR_SELECTION *s; s = tpm2_tpml_pcr_selection_get_tpms_pcr_selection(&lcopy, hash_alg); if (!s) - return SYNTHETIC_ERRNO(ENOENT); + return 0; - tpm2_tpms_pcr_selection_to_mask(s, ret); - return 0; + return tpm2_tpms_pcr_selection_to_mask(s); } /* Convert a mask and hash alg to a TPML_PCR_SELECTION object. */ @@ -2634,10 +2625,7 @@ static int find_signature( return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Signature is not a JSON object."); uint16_t pcr_bank = pcr_selection->pcrSelections[0].hash; - uint32_t pcr_mask; - r = tpm2_tpml_pcr_selection_to_mask(pcr_selection, pcr_bank, &pcr_mask); - if (r < 0) - return r; + uint32_t pcr_mask = tpm2_tpml_pcr_selection_to_mask(pcr_selection, pcr_bank); k = tpm2_hash_alg_to_string(pcr_bank); if (!k) diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h index 9ea8e5bda3..c908e012db 100644 --- a/src/shared/tpm2-util.h +++ b/src/shared/tpm2-util.h @@ -102,7 +102,7 @@ int tpm2_get_good_pcr_banks_strv(Tpm2Context *c, uint32_t pcr_mask, char ***ret) int tpm2_extend_bytes(Tpm2Context *c, char **banks, unsigned pcr_index, const void *data, size_t data_size, const void *secret, size_t secret_size); -void tpm2_tpms_pcr_selection_to_mask(const TPMS_PCR_SELECTION *s, uint32_t *ret); +uint32_t tpm2_tpms_pcr_selection_to_mask(const TPMS_PCR_SELECTION *s); void tpm2_tpms_pcr_selection_from_mask(uint32_t mask, TPMI_ALG_HASH hash, TPMS_PCR_SELECTION *ret); void tpm2_tpms_pcr_selection_add(TPMS_PCR_SELECTION *a, const TPMS_PCR_SELECTION *b); void tpm2_tpms_pcr_selection_sub(TPMS_PCR_SELECTION *a, const TPMS_PCR_SELECTION *b); @@ -111,7 +111,7 @@ char *tpm2_tpms_pcr_selection_to_string(const TPMS_PCR_SELECTION *s); size_t tpm2_tpms_pcr_selection_weight(const TPMS_PCR_SELECTION *s); #define tpm2_tpms_pcr_selection_is_empty(s) (tpm2_tpms_pcr_selection_weight(s) == 0) -int tpm2_tpml_pcr_selection_to_mask(const TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash, uint32_t *ret); +uint32_t tpm2_tpml_pcr_selection_to_mask(const TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash); void tpm2_tpml_pcr_selection_from_mask(uint32_t mask, TPMI_ALG_HASH hash, TPML_PCR_SELECTION *ret); void tpm2_tpml_pcr_selection_add_tpms_pcr_selection(TPML_PCR_SELECTION *l, const TPMS_PCR_SELECTION *s); void tpm2_tpml_pcr_selection_sub_tpms_pcr_selection(TPML_PCR_SELECTION *l, const TPMS_PCR_SELECTION *s); diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c index 9eb9085a59..401ddddda8 100644 --- a/src/test/test-tpm2.c +++ b/src/test/test-tpm2.c @@ -158,9 +158,7 @@ static void verify_tpms_pcr_selection(TPMS_PCR_SELECTION *s, uint32_t mask, TPMI assert_se(s->pcrSelect[2] == ((mask >> 16) & 0xff)); assert_se(s->pcrSelect[3] == 0); - uint32_t m = POISON_U32; - tpm2_tpms_pcr_selection_to_mask(s, &m); - assert_se(m == mask); + assert_se(tpm2_tpms_pcr_selection_to_mask(s) == mask); } static void verify_tpml_pcr_selection(TPML_PCR_SELECTION *l, TPMS_PCR_SELECTION s[], size_t count) { @@ -168,10 +166,8 @@ static void verify_tpml_pcr_selection(TPML_PCR_SELECTION *l, TPMS_PCR_SELECTION for (size_t i = 0; i < count; i++) { assert_tpms_pcr_selection_eq(&s[i], &l->pcrSelections[i]); - uint32_t mask = POISON_U32; TPMI_ALG_HASH hash = l->pcrSelections[i].hash; - assert_se(tpm2_tpml_pcr_selection_to_mask(l, hash, &mask) == 0); - verify_tpms_pcr_selection(&l->pcrSelections[i], mask, hash); + verify_tpms_pcr_selection(&l->pcrSelections[i], tpm2_tpml_pcr_selection_to_mask(l, hash), hash); } } From 13b551744b9df9ea08d7e06dee57a8cea7b48d1b Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Wed, 19 Jul 2023 08:50:06 -0400 Subject: [PATCH 04/20] tpm2: add more helper functions for managing TPML_PCR_SELECTION and TPMS_PCR_SELECTION Add more functions to help manage these objects. --- src/shared/tpm2-util.c | 62 +++++++++++++++++++++++++++++++++++------- src/shared/tpm2-util.h | 6 ++++ 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index be25790444..51f2bad041 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -1232,30 +1232,45 @@ void tpm2_tpms_pcr_selection_from_mask(uint32_t mask, TPMI_ALG_HASH hash_alg, TP }; } +/* Test if all bits in the mask are set in the TPMS_PCR_SELECTION. */ +bool tpm2_tpms_pcr_selection_has_mask(const TPMS_PCR_SELECTION *s, uint32_t mask) { + assert(s); + + return FLAGS_SET(tpm2_tpms_pcr_selection_to_mask(s), mask); +} + +static void tpm2_tpms_pcr_selection_update_mask(TPMS_PCR_SELECTION *s, uint32_t mask, bool b) { + assert(s); + + tpm2_tpms_pcr_selection_from_mask(UPDATE_FLAG(tpm2_tpms_pcr_selection_to_mask(s), mask, b), s->hash, s); +} + +/* Add all PCR selections in the mask. */ +void tpm2_tpms_pcr_selection_add_mask(TPMS_PCR_SELECTION *s, uint32_t mask) { + tpm2_tpms_pcr_selection_update_mask(s, mask, 1); +} + +/* Remove all PCR selections in the mask. */ +void tpm2_tpms_pcr_selection_sub_mask(TPMS_PCR_SELECTION *s, uint32_t mask) { + tpm2_tpms_pcr_selection_update_mask(s, mask, 0); +} + /* Add all PCR selections in 'b' to 'a'. Both must have the same hash alg. */ void tpm2_tpms_pcr_selection_add(TPMS_PCR_SELECTION *a, const TPMS_PCR_SELECTION *b) { - uint32_t maska, maskb; - assert(a); assert(b); assert(a->hash == b->hash); - maska = tpm2_tpms_pcr_selection_to_mask(a); - maskb = tpm2_tpms_pcr_selection_to_mask(b); - tpm2_tpms_pcr_selection_from_mask(maska | maskb, a->hash, a); + tpm2_tpms_pcr_selection_add_mask(a, tpm2_tpms_pcr_selection_to_mask(b)); } /* Remove all PCR selections in 'b' from 'a'. Both must have the same hash alg. */ void tpm2_tpms_pcr_selection_sub(TPMS_PCR_SELECTION *a, const TPMS_PCR_SELECTION *b) { - uint32_t maska, maskb; - assert(a); assert(b); assert(a->hash == b->hash); - maska = tpm2_tpms_pcr_selection_to_mask(a); - maskb = tpm2_tpms_pcr_selection_to_mask(b); - tpm2_tpms_pcr_selection_from_mask(maska & ~maskb, a->hash, a); + tpm2_tpms_pcr_selection_sub_mask(a, tpm2_tpms_pcr_selection_to_mask(b)); } /* Move all PCR selections in 'b' to 'a'. Both must have the same hash alg. */ @@ -1430,6 +1445,33 @@ void tpm2_tpml_pcr_selection_sub_tpms_pcr_selection(TPML_PCR_SELECTION *l, const tpm2_tpms_pcr_selection_sub(selection, s); } +/* Test if all bits in the mask for the hash are set in the TPML_PCR_SELECTION. */ +bool tpm2_tpml_pcr_selection_has_mask(const TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash, uint32_t mask) { + assert(l); + + return FLAGS_SET(tpm2_tpml_pcr_selection_to_mask(l, hash), mask); +} + +/* Add the PCR selections in the mask, with the provided hash. */ +void tpm2_tpml_pcr_selection_add_mask(TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash, uint32_t mask) { + TPMS_PCR_SELECTION tpms; + + assert(l); + + tpm2_tpms_pcr_selection_from_mask(mask, hash, &tpms); + tpm2_tpml_pcr_selection_add_tpms_pcr_selection(l, &tpms); +} + +/* Remove the PCR selections in the mask, with the provided hash. */ +void tpm2_tpml_pcr_selection_sub_mask(TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash, uint32_t mask) { + TPMS_PCR_SELECTION tpms; + + assert(l); + + tpm2_tpms_pcr_selection_from_mask(mask, hash, &tpms); + tpm2_tpml_pcr_selection_sub_tpms_pcr_selection(l, &tpms); +} + /* Add all PCR selections in 'b' to 'a'. */ void tpm2_tpml_pcr_selection_add(TPML_PCR_SELECTION *a, const TPML_PCR_SELECTION *b) { assert(a); diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h index c908e012db..ad80cc8ebd 100644 --- a/src/shared/tpm2-util.h +++ b/src/shared/tpm2-util.h @@ -104,6 +104,9 @@ int tpm2_extend_bytes(Tpm2Context *c, char **banks, unsigned pcr_index, const vo uint32_t tpm2_tpms_pcr_selection_to_mask(const TPMS_PCR_SELECTION *s); void tpm2_tpms_pcr_selection_from_mask(uint32_t mask, TPMI_ALG_HASH hash, TPMS_PCR_SELECTION *ret); +bool tpm2_tpms_pcr_selection_has_mask(const TPMS_PCR_SELECTION *s, uint32_t mask); +void tpm2_tpms_pcr_selection_add_mask(TPMS_PCR_SELECTION *s, uint32_t mask); +void tpm2_tpms_pcr_selection_sub_mask(TPMS_PCR_SELECTION *s, uint32_t mask); void tpm2_tpms_pcr_selection_add(TPMS_PCR_SELECTION *a, const TPMS_PCR_SELECTION *b); void tpm2_tpms_pcr_selection_sub(TPMS_PCR_SELECTION *a, const TPMS_PCR_SELECTION *b); void tpm2_tpms_pcr_selection_move(TPMS_PCR_SELECTION *a, TPMS_PCR_SELECTION *b); @@ -113,6 +116,9 @@ size_t tpm2_tpms_pcr_selection_weight(const TPMS_PCR_SELECTION *s); uint32_t tpm2_tpml_pcr_selection_to_mask(const TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash); void tpm2_tpml_pcr_selection_from_mask(uint32_t mask, TPMI_ALG_HASH hash, TPML_PCR_SELECTION *ret); +bool tpm2_tpml_pcr_selection_has_mask(const TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash, uint32_t mask); +void tpm2_tpml_pcr_selection_add_mask(TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash, uint32_t mask); +void tpm2_tpml_pcr_selection_sub_mask(TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash, uint32_t mask); void tpm2_tpml_pcr_selection_add_tpms_pcr_selection(TPML_PCR_SELECTION *l, const TPMS_PCR_SELECTION *s); void tpm2_tpml_pcr_selection_sub_tpms_pcr_selection(TPML_PCR_SELECTION *l, const TPMS_PCR_SELECTION *s); void tpm2_tpml_pcr_selection_add(TPML_PCR_SELECTION *a, const TPML_PCR_SELECTION *b); From 323eb4803a29a9cc255aa16ef7cab3a00429b146 Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Tue, 11 Jul 2023 21:23:36 -0400 Subject: [PATCH 05/20] tpm2: add Tpm2PCRValue struct and associated functions Add a new struct that can represent a PCR index, hash, and value all together. This replaces code (e.g. the tpm2_pcr_read() parameters) that required using both a TPML_PCR_SELECTION as well as array of TPM2B_DIGEST entries, which was difficult to correlate the selection hash/index to each digest. --- .../cryptsetup-token-systemd-tpm2.c | 2 +- src/cryptsetup/cryptsetup.c | 2 +- src/shared/tpm2-util.c | 421 ++++++++++++++++-- src/shared/tpm2-util.h | 25 +- 4 files changed, 400 insertions(+), 50 deletions(-) diff --git a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c index aab3a4b4c0..94d568c17f 100644 --- a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c +++ b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c @@ -278,7 +278,7 @@ _public_ int cryptsetup_token_validate( } u = json_variant_unsigned(e); - if (!TPM2_PCR_VALID(u)) { + if (!TPM2_PCR_INDEX_VALID(u)) { crypt_log_debug(cd, "TPM2 PCR number out of range."); return 1; } diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index f62401b9bc..5dc3c7f123 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -435,7 +435,7 @@ static int parse_one_option(const char *option) { } pcr = r ? TPM_PCR_INDEX_VOLUME_KEY : UINT_MAX; - } else if (!TPM2_PCR_VALID(pcr)) { + } else if (!TPM2_PCR_INDEX_VALID(pcr)) { log_warning("Selected TPM index for measurement %u outside of allowed range 0…%u, ignoring.", pcr, TPM2_PCRS_MAX-1); return 0; } diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index 51f2bad041..26a67accff 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -23,6 +23,7 @@ #include "parse-util.h" #include "random-util.h" #include "sha256.h" +#include "sort-util.h" #include "stat-util.h" #include "string-table.h" #include "time-util.h" @@ -1520,6 +1521,318 @@ size_t tpm2_tpml_pcr_selection_weight(const TPML_PCR_SELECTION *l) { return weight; } +bool TPM2_PCR_VALUE_VALID(const Tpm2PCRValue *pcr_value) { + int r; + + assert(pcr_value); + + if (!TPM2_PCR_INDEX_VALID(pcr_value->index)) { + log_debug("PCR index %u invalid.", pcr_value->index); + return false; + } + + /* If it contains a value, the value size must match the hash size. */ + if (pcr_value->value.size > 0) { + r = tpm2_hash_alg_to_size(pcr_value->hash); + if (r < 0) + return false; + + if ((int) pcr_value->value.size != r) { + log_debug("PCR hash 0x%" PRIx16 " expected size %d does not match actual size %" PRIu16 ".", + pcr_value->hash, r, pcr_value->value.size); + return false; + } + } + + return true; +} + +/* Verify all entries are valid, and consistent with each other. The requirements for consistency are: + * + * 1) all entries must be sorted in ascending order (e.g. using tpm2_sort_pcr_values()) + * 2) all entries must be unique, i.e. there cannot be 2 entries with the same hash and index + */ +bool TPM2_PCR_VALUES_VALID(const Tpm2PCRValue *pcr_values, size_t n_pcr_values) { + assert(pcr_values || n_pcr_values == 0); + + for (size_t i = 0; i < n_pcr_values; i++) { + const Tpm2PCRValue *v = &pcr_values[i]; + + if (!TPM2_PCR_VALUE_VALID(v)) + return false; + + if (i == 0) + continue; + + const Tpm2PCRValue *l = &pcr_values[i - 1]; + + /* Hashes must be sorted in ascending order */ + if (v->hash < l->hash) { + log_debug("PCR values not in ascending order, hash %" PRIu16 " is after %" PRIu16 ".", + v->hash, l->hash); + return false; + } + + if (v->hash == l->hash) { + /* Indexes (for the same hash) must be sorted in ascending order */ + if (v->index < l->index) { + log_debug("PCR values not in ascending order, hash %" PRIu16 " index %u is after %u.", + v->hash, v->index, l->index); + return false; + } + + /* Indexes (for the same hash) must not be duplicates */ + if (v->index == l->index) { + log_debug("PCR values contain duplicates for hash %" PRIu16 " index %u.", + v->hash, v->index); + return false; + } + } + } + + return true; +} + +static int cmp_pcr_values(const Tpm2PCRValue *a, const Tpm2PCRValue *b) { + assert(a); + assert(b); + + return CMP(a->hash, b->hash) ?: CMP(a->index, b->index); +} + +/* Sort the array of Tpm2PCRValue entries in-place. This sorts first in ascending order of hash algorithm + * (sorting simply by the TPM2 hash algorithm number), and then sorting by pcr index. */ +void tpm2_sort_pcr_values(Tpm2PCRValue *pcr_values, size_t n_pcr_values) { + typesafe_qsort(pcr_values, n_pcr_values, cmp_pcr_values); +} + +int tpm2_pcr_values_from_mask(uint32_t mask, TPMI_ALG_HASH hash, Tpm2PCRValue **ret_pcr_values, size_t *ret_n_pcr_values) { + _cleanup_free_ Tpm2PCRValue *pcr_values = NULL; + size_t n_pcr_values = 0; + + assert(ret_pcr_values); + assert(ret_n_pcr_values); + + FOREACH_PCR_IN_MASK(index, mask) + if (!GREEDY_REALLOC_APPEND( + pcr_values, + n_pcr_values, + &TPM2_PCR_VALUE_MAKE(index, hash, {}), + 1)) + return log_oom_debug(); + + *ret_pcr_values = TAKE_PTR(pcr_values); + *ret_n_pcr_values = n_pcr_values; + + return 0; +} + +int tpm2_pcr_values_to_mask(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, TPMI_ALG_HASH hash, uint32_t *ret_mask) { + uint32_t mask = 0; + + assert(pcr_values || n_pcr_values == 0); + assert(ret_mask); + + if (!TPM2_PCR_VALUES_VALID(pcr_values, n_pcr_values)) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PCR values."); + + for (size_t i = 0; i < n_pcr_values; i++) + if (pcr_values[i].hash == hash) + SET_BIT(mask, pcr_values[i].index); + + *ret_mask = mask; + + return 0; +} + +int tpm2_tpml_pcr_selection_from_pcr_values( + const Tpm2PCRValue *pcr_values, + size_t n_pcr_values, + TPML_PCR_SELECTION *ret_selection, + TPM2B_DIGEST **ret_values, + size_t *ret_n_values) { + + TPML_PCR_SELECTION selection = {}; + _cleanup_free_ TPM2B_DIGEST *values = NULL; + size_t n_values = 0; + + assert(pcr_values || n_pcr_values == 0); + + if (!TPM2_PCR_VALUES_VALID(pcr_values, n_pcr_values)) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "PCR values are not valid."); + + for (size_t i = 0; i < n_pcr_values; i++) { + unsigned index = pcr_values[i].index; + TPMI_ALG_HASH hash = pcr_values[i].hash; + const TPM2B_DIGEST *digest = &pcr_values[i].value; + + tpm2_tpml_pcr_selection_add_mask(&selection, hash, INDEX_TO_MASK(uint32_t, index)); + + if (!GREEDY_REALLOC_APPEND(values, n_values, digest, 1)) + return log_oom_debug(); + } + + if (ret_selection) + *ret_selection = selection; + if (ret_values) + *ret_values = TAKE_PTR(values); + if (ret_n_values) + *ret_n_values = n_values; + + return 0; +} + +/* Count the number of different hash algorithms for all the entries. */ +int tpm2_pcr_values_hash_count(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, size_t *ret_count) { + TPML_PCR_SELECTION selection; + int r; + + assert(pcr_values); + assert(ret_count); + + r = tpm2_tpml_pcr_selection_from_pcr_values( + pcr_values, + n_pcr_values, + &selection, + /* ret_values= */ NULL, + /* ret_n_values= */ NULL); + if (r < 0) + return r; + + *ret_count = selection.count; + + return 0; +} + +/* Parse a string argument into a Tpm2PCRValue object. + * + * The format is [:hash[=value]] where index is the index number (or name) of the PCR, e.g. 0 (or + * platform-code), hash is the name of the hash algorithm (e.g. sha256) and value is the hex hash digest + * value, optionally with a leading 0x. This does not check for validity of the fields. */ +int tpm2_pcr_value_from_string(const char *arg, Tpm2PCRValue *ret_pcr_value) { + Tpm2PCRValue pcr_value = {}; + const char *p = arg; + int r; + + assert(arg); + assert(ret_pcr_value); + + _cleanup_free_ char *index = NULL; + r = extract_first_word(&p, &index, ":", /* flags= */ 0); + if (r < 1) + return log_error_errno(r, "Could not parse pcr value '%s': %m", p); + + r = pcr_index_from_string(index); + if (r < 0) + return log_error_errno(r, "Invalid pcr index '%s': %m", index); + pcr_value.index = (unsigned) r; + + if (!isempty(p)) { + _cleanup_free_ char *hash = NULL; + r = extract_first_word(&p, &hash, "=", /* flags= */ 0); + if (r < 1) + return log_error_errno(r, "Could not parse pcr hash algorithm '%s': %m", p); + + r = tpm2_hash_alg_from_string(hash); + if (r < 0) + return log_error_errno(r, "Invalid pcr hash algorithm '%s': %m", hash); + pcr_value.hash = (TPMI_ALG_HASH) r; + } + + if (!isempty(p)) { + /* Remove leading 0x if present */ + p = startswith_no_case(p, "0x") ?: p; + + _cleanup_free_ void *buf = NULL; + size_t buf_size = 0; + r = unhexmem(p, strlen(p), &buf, &buf_size); + if (r < 0) + return log_error_errno(r, "Invalid pcr hash value '%s': %m", p); + + pcr_value.value.size = buf_size; + assert(sizeof(pcr_value.value.buffer) >= pcr_value.value.size); + memcpy(pcr_value.value.buffer, buf, pcr_value.value.size); + } + + *ret_pcr_value = pcr_value; + + return 0; +} + +/* Return a string for the PCR value. The format is described in tpm2_pcr_value_from_string(). Note that if + * the hash algorithm is not recognized, neither hash name nor hash digest value is included in the + * string. This does not check for validity. */ +char *tpm2_pcr_value_to_string(const Tpm2PCRValue *pcr_value) { + _cleanup_free_ char *index = NULL, *value = NULL; + int r; + + r = asprintf(&index, "%u", pcr_value->index); + if (r < 0) + return NULL; + + const char *hash = tpm2_hash_alg_to_string(pcr_value->hash); + + if (hash && pcr_value->value.size > 0) { + value = hexmem(pcr_value->value.buffer, pcr_value->value.size); + if (!value) + return NULL; + } + + return strjoin(index, hash ? ":" : "", hash ?: "", value ? "=" : "", value ?: ""); +} + +/* Parse a string argument into an array of Tpm2PCRValue objects. + * + * The format is zero or more entries separated by ',' or '+'. The format of each entry is described in + * tpm2_pcr_value_from_string(). This does not check for validity of the entries. */ +int tpm2_pcr_values_from_string(const char *arg, Tpm2PCRValue **ret_pcr_values, size_t *ret_n_pcr_values) { + const char *p = arg; + int r; + + assert(arg); + assert(ret_pcr_values); + assert(ret_n_pcr_values); + + _cleanup_free_ Tpm2PCRValue *pcr_values = NULL; + size_t n_pcr_values = 0; + + for (;;) { + _cleanup_free_ char *pcr_arg = NULL; + r = extract_first_word(&p, &pcr_arg, ",+", /* flags= */ 0); + if (r < 0) + return log_error_errno(r, "Could not parse pcr values '%s': %m", p); + if (r == 0) + break; + + Tpm2PCRValue pcr_value; + r = tpm2_pcr_value_from_string(pcr_arg, &pcr_value); + if (r < 0) + return r; + + if (!GREEDY_REALLOC_APPEND(pcr_values, n_pcr_values, &pcr_value, 1)) + return log_oom(); + } + + *ret_pcr_values = TAKE_PTR(pcr_values); + *ret_n_pcr_values = n_pcr_values; + + return 0; +} + +/* Return a string representing the array of PCR values. The format is as described in + * tpm2_pcr_values_from_string(). This does not check for validity. */ +char *tpm2_pcr_values_to_string(const Tpm2PCRValue *pcr_values, size_t n_pcr_values) { + _cleanup_free_ char *s = NULL; + + for (size_t i = 0; i < n_pcr_values; i++) { + _cleanup_free_ char *pcrstr = tpm2_pcr_value_to_string(&pcr_values[i]); + if (!pcrstr || !strextend_with_separator(&s, "+", pcrstr)) + return NULL; + } + + return s ? TAKE_PTR(s) : strdup(""); +} + static void tpm2_log_debug_tpml_pcr_selection(const TPML_PCR_SELECTION *l, const char *msg) { if (!DEBUG_LOGGING || !l) return; @@ -1528,6 +1841,14 @@ static void tpm2_log_debug_tpml_pcr_selection(const TPML_PCR_SELECTION *l, const log_debug("%s: %s", msg ?: "PCR selection", strna(s)); } +static void tpm2_log_debug_pcr_value(const Tpm2PCRValue *pcr_value, const char *msg) { + if (!DEBUG_LOGGING || !pcr_value) + return; + + _cleanup_free_ char *s = tpm2_pcr_value_to_string(pcr_value); + log_debug("%s: %s", msg ?: "PCR value", strna(s)); +} + static void tpm2_log_debug_buffer(const void *buffer, size_t size, const char *msg) { if (!DEBUG_LOGGING || !buffer || size == 0) return; @@ -1923,22 +2244,27 @@ int tpm2_create_loaded( return 0; } +/* Read hash values from the specified PCR selection. Provides a Tpm2PCRValue array that contains all + * requested PCR values, in the order provided by the TPM. Normally, the provided pcr values will match + * exactly what is in the provided selection, but the TPM may ignore some selected PCRs (for example, if an + * unimplemented PCR index is requested), in which case those PCRs will be absent from the provided pcr + * values. */ static int tpm2_pcr_read( Tpm2Context *c, const TPML_PCR_SELECTION *pcr_selection, - TPML_PCR_SELECTION *ret_pcr_selection, - TPM2B_DIGEST **ret_pcr_values, + Tpm2PCRValue **ret_pcr_values, size_t *ret_n_pcr_values) { - _cleanup_free_ TPM2B_DIGEST *pcr_values = NULL; - TPML_PCR_SELECTION remaining, total_read = {}; + _cleanup_free_ Tpm2PCRValue *pcr_values = NULL; size_t n_pcr_values = 0; TSS2_RC rc; assert(c); assert(pcr_selection); + assert(ret_pcr_values); + assert(ret_n_pcr_values); - remaining = *pcr_selection; + TPML_PCR_SELECTION remaining = *pcr_selection; while (!tpm2_tpml_pcr_selection_is_empty(&remaining)) { _cleanup_(Esys_Freep) TPML_PCR_SELECTION *current_read = NULL; _cleanup_(Esys_Freep) TPML_DIGEST *current_values = NULL; @@ -1959,44 +2285,39 @@ static int tpm2_pcr_read( return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to read TPM2 PCRs: %s", sym_Tss2_RC_Decode(rc)); + tpm2_log_debug_tpml_pcr_selection(current_read, "Read PCR selection"); + if (tpm2_tpml_pcr_selection_is_empty(current_read)) { log_warning("TPM2 refused to read possibly unimplemented PCRs, ignoring."); break; } - tpm2_tpml_pcr_selection_sub(&remaining, current_read); - tpm2_tpml_pcr_selection_add(&total_read, current_read); + unsigned i = 0; + FOREACH_PCR_IN_TPML_PCR_SELECTION(index, tpms, current_read) { + assert(i < current_values->count); + Tpm2PCRValue pcr_value = { + .index = index, + .hash = tpms->hash, + .value = current_values->digests[i++], + }; - if (!GREEDY_REALLOC(pcr_values, n_pcr_values + current_values->count)) - return log_oom(); + tpm2_log_debug_pcr_value(&pcr_value, /* msg= */ NULL); - memcpy_safe(&pcr_values[n_pcr_values], current_values->digests, - current_values->count * sizeof(TPM2B_DIGEST)); - n_pcr_values += current_values->count; - - if (DEBUG_LOGGING) { - unsigned i = 0; - FOREACH_PCR_IN_TPML_PCR_SELECTION(pcr, s, current_read) { - assert(i < current_values->count); - - TPM2B_DIGEST *d = ¤t_values->digests[i]; - i++; - - TPML_PCR_SELECTION l; - tpm2_tpml_pcr_selection_from_mask(INDEX_TO_MASK(uint32_t, pcr), s->hash, &l); - - _cleanup_free_ char *desc = tpm2_tpml_pcr_selection_to_string(&l); - tpm2_log_debug_digest(d, strna(desc)); - } + if (!GREEDY_REALLOC_APPEND(pcr_values, n_pcr_values, &pcr_value, 1)) + return log_oom(); } + assert(i == current_values->count); + + tpm2_tpml_pcr_selection_sub(&remaining, current_read); } - if (ret_pcr_selection) - *ret_pcr_selection = total_read; - if (ret_pcr_values) - *ret_pcr_values = TAKE_PTR(pcr_values); - if (ret_n_pcr_values) - *ret_n_pcr_values = n_pcr_values; + tpm2_sort_pcr_values(pcr_values, n_pcr_values); + + if (!TPM2_PCR_VALUES_VALID(pcr_values, n_pcr_values)) + return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "PCR values read from TPM are not valid."); + + *ret_pcr_values = TAKE_PTR(pcr_values); + *ret_n_pcr_values = n_pcr_values; return 0; } @@ -2006,9 +2327,7 @@ static int tpm2_pcr_mask_good( TPMI_ALG_HASH bank, uint32_t mask) { - _cleanup_free_ TPM2B_DIGEST *pcr_values = NULL; TPML_PCR_SELECTION selection; - size_t n_pcr_values = 0; int r; assert(c); @@ -2019,22 +2338,18 @@ static int tpm2_pcr_mask_good( tpm2_tpml_pcr_selection_from_mask(mask, bank, &selection); - r = tpm2_pcr_read(c, &selection, &selection, &pcr_values, &n_pcr_values); + _cleanup_free_ Tpm2PCRValue *pcr_values = NULL; + size_t n_pcr_values; + r = tpm2_pcr_read(c, &selection, &pcr_values, &n_pcr_values); if (r < 0) return r; /* If at least one of the selected PCR values is something other than all 0x00 or all 0xFF we are happy. */ - unsigned i = 0; - FOREACH_PCR_IN_TPML_PCR_SELECTION(pcr, s, &selection) { - assert(i < n_pcr_values); - - if (!memeqbyte(0x00, pcr_values[i].buffer, pcr_values[i].size) && - !memeqbyte(0xFF, pcr_values[i].buffer, pcr_values[i].size)) + for (unsigned i = 0; i < n_pcr_values; i++) + if (!memeqbyte(0x00, pcr_values[i].value.buffer, pcr_values[i].value.size) && + !memeqbyte(0xFF, pcr_values[i].value.buffer, pcr_values[i].value.size)) return true; - i++; - } - return false; } @@ -3314,14 +3629,26 @@ int tpm2_seal(const char *device, TPML_PCR_SELECTION hash_pcr_selection = {}; _cleanup_free_ TPM2B_DIGEST *hash_pcr_values = NULL; - size_t n_hash_pcr_values = 0; + size_t n_hash_pcr_values; if (hash_pcr_mask) { /* For now, we just read the current values from the system; we need to be able to specify * expected values, eventually. */ tpm2_tpml_pcr_selection_from_mask(hash_pcr_mask, pcr_bank, &hash_pcr_selection); - r = tpm2_pcr_read(c, &hash_pcr_selection, &hash_pcr_selection, &hash_pcr_values, &n_hash_pcr_values); + + _cleanup_free_ Tpm2PCRValue *pcr_values = NULL; + size_t n_pcr_values; + r = tpm2_pcr_read(c, &hash_pcr_selection, &pcr_values, &n_pcr_values); if (r < 0) return r; + + r = tpm2_tpml_pcr_selection_from_pcr_values( + pcr_values, + n_pcr_values, + &hash_pcr_selection, + &hash_pcr_values, + &n_hash_pcr_values); + if (r < 0) + return log_error_errno(r, "Could not get PCR selection from values: %m"); } TPM2B_PUBLIC pubkey_tpm2, *authorize_key = NULL; diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h index ad80cc8ebd..4757c5db9b 100644 --- a/src/shared/tpm2-util.h +++ b/src/shared/tpm2-util.h @@ -17,7 +17,7 @@ typedef enum TPM2Flags { * TPM2 on a Client PC must have at least 24 PCRs. This hardcodes our expectation of 24. */ #define TPM2_PCRS_MAX 24U #define TPM2_PCRS_MASK ((UINT32_C(1) << TPM2_PCRS_MAX) - 1) -static inline bool TPM2_PCR_VALID(unsigned pcr) { +static inline bool TPM2_PCR_INDEX_VALID(unsigned pcr) { return pcr < TPM2_PCRS_MAX; } static inline bool TPM2_PCR_MASK_VALID(uint32_t pcr_mask) { @@ -88,6 +88,26 @@ int tpm2_handle_new(Tpm2Context *context, Tpm2Handle **ret_handle); Tpm2Handle *tpm2_handle_free(Tpm2Handle *handle); DEFINE_TRIVIAL_CLEANUP_FUNC(Tpm2Handle*, tpm2_handle_free); +typedef struct { + unsigned index; + TPMI_ALG_HASH hash; + TPM2B_DIGEST value; +} Tpm2PCRValue; + +#define TPM2_PCR_VALUE_MAKE(i, h, v) (Tpm2PCRValue) { .index = (i), .hash = (h), .value = ((TPM2B_DIGEST) v), } +bool TPM2_PCR_VALUE_VALID(const Tpm2PCRValue *pcr_value); +int tpm2_pcr_value_from_string(const char *arg, Tpm2PCRValue *ret_pcr_value); +char *tpm2_pcr_value_to_string(const Tpm2PCRValue *pcr_value); + +bool TPM2_PCR_VALUES_VALID(const Tpm2PCRValue *pcr_values, size_t n_pcr_values); +void tpm2_sort_pcr_values(Tpm2PCRValue *pcr_values, size_t n_pcr_values); +int tpm2_pcr_values_from_mask(uint32_t mask, TPMI_ALG_HASH hash, Tpm2PCRValue **ret_pcr_values, size_t *ret_n_pcr_values); +int tpm2_pcr_values_to_mask(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, TPMI_ALG_HASH hash, uint32_t *ret_mask); +int tpm2_pcr_values_from_string(const char *arg, Tpm2PCRValue **ret_pcr_values, size_t *ret_n_pcr_values); +char *tpm2_pcr_values_to_string(const Tpm2PCRValue *pcr_values, size_t n_pcr_values); +int tpm2_pcr_values_hash_count(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, size_t *ret_count); +int tpm2_tpml_pcr_selection_from_pcr_values(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, TPML_PCR_SELECTION *ret_selection, TPM2B_DIGEST **ret_values, size_t *ret_n_values); + int tpm2_create_primary(Tpm2Context *c, const Tpm2Handle *session, const TPM2B_PUBLIC *template, const TPM2B_SENSITIVE_CREATE *sensitive, TPM2B_PUBLIC **ret_public, Tpm2Handle **ret_handle); int tpm2_create(Tpm2Context *c, const Tpm2Handle *parent, const Tpm2Handle *session, const TPMT_PUBLIC *template, const TPMS_SENSITIVE_CREATE *sensitive, TPM2B_PUBLIC **ret_public, TPM2B_PRIVATE **ret_private); int tpm2_create_loaded(Tpm2Context *c, const Tpm2Handle *parent, const Tpm2Handle *session, const TPMT_PUBLIC *template, const TPMS_SENSITIVE_CREATE *sensitive, TPM2B_PUBLIC **ret_public, TPM2B_PRIVATE **ret_private, Tpm2Handle **ret_handle); @@ -130,6 +150,9 @@ size_t tpm2_tpml_pcr_selection_weight(const TPML_PCR_SELECTION *l); #else /* HAVE_TPM2 */ typedef struct {} Tpm2Context; typedef struct {} Tpm2Handle; +typedef struct {} Tpm2PCRValue; + +#define TPM2_PCR_VALUE_MAKE(i, h, v) (Tpm2PCRValue) {} #endif /* HAVE_TPM2 */ int tpm2_list_devices(void); From e00f46ac7ff47819602c87f5078d90f676e64e1f Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Fri, 14 Jul 2023 11:38:11 -0400 Subject: [PATCH 06/20] tpm2: move declared functions in header lower down Move some function declarations lower down, below the Tpm2Context and Tpm2Handle typedefs; later commits will reference the typedefs in some of the functions, so the typedefs need to come first in the header. This only moves the declarations, none of the declarations are modified. --- src/shared/tpm2-util.h | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h index 4757c5db9b..bf8135d0af 100644 --- a/src/shared/tpm2-util.h +++ b/src/shared/tpm2-util.h @@ -34,26 +34,6 @@ static inline bool TPM2_PCR_MASK_VALID(uint32_t pcr_mask) { int dlopen_tpm2(void); -int tpm2_digest_many(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest, const struct iovec data[], size_t count, bool extend); -static inline int tpm2_digest_buffer(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest, const void *data, size_t len, bool extend) { - return tpm2_digest_many(alg, digest, &IOVEC_MAKE((void*) data, len), 1, extend); -} -int tpm2_digest_many_digests(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest, const TPM2B_DIGEST data[], size_t count, bool extend); -static inline int tpm2_digest_rehash(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest) { - return tpm2_digest_many(alg, digest, NULL, 0, true); -} -static inline int tpm2_digest_init(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest) { - return tpm2_digest_many(alg, digest, NULL, 0, false); -} - -int tpm2_calculate_name(const TPMT_PUBLIC *public, TPM2B_NAME *ret_name); -int tpm2_calculate_policy_auth_value(TPM2B_DIGEST *digest); -int tpm2_calculate_policy_authorize(const TPM2B_PUBLIC *public, const TPM2B_DIGEST *policy_ref, TPM2B_DIGEST *digest); -int tpm2_calculate_policy_pcr(const TPML_PCR_SELECTION *pcr_selection, const TPM2B_DIGEST pcr_values[], size_t pcr_values_count, TPM2B_DIGEST *digest); - -int tpm2_seal(const char *device, uint32_t hash_pcr_mask, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, const char *pin, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, void **ret_pcr_hash, size_t *ret_pcr_hash_size, uint16_t *ret_pcr_bank, uint16_t *ret_primary_alg, void **ret_srk_buf, size_t *ret_srk_buf_size); -int tpm2_unseal(const char *device, uint32_t hash_pcr_mask, uint16_t pcr_bank, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, JsonVariant *signature, const char *pin, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, const void *srk_buf, size_t srk_buf_size, void **ret_secret, size_t *ret_secret_size); - typedef struct { unsigned n_ref; @@ -147,6 +127,26 @@ char *tpm2_tpml_pcr_selection_to_string(const TPML_PCR_SELECTION *l); size_t tpm2_tpml_pcr_selection_weight(const TPML_PCR_SELECTION *l); #define tpm2_tpml_pcr_selection_is_empty(l) (tpm2_tpml_pcr_selection_weight(l) == 0) +int tpm2_digest_many(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest, const struct iovec data[], size_t count, bool extend); +static inline int tpm2_digest_buffer(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest, const void *data, size_t len, bool extend) { + return tpm2_digest_many(alg, digest, &IOVEC_MAKE((void*) data, len), 1, extend); +} +int tpm2_digest_many_digests(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest, const TPM2B_DIGEST data[], size_t count, bool extend); +static inline int tpm2_digest_rehash(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest) { + return tpm2_digest_many(alg, digest, NULL, 0, true); +} +static inline int tpm2_digest_init(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest) { + return tpm2_digest_many(alg, digest, NULL, 0, false); +} + +int tpm2_calculate_name(const TPMT_PUBLIC *public, TPM2B_NAME *ret_name); +int tpm2_calculate_policy_auth_value(TPM2B_DIGEST *digest); +int tpm2_calculate_policy_authorize(const TPM2B_PUBLIC *public, const TPM2B_DIGEST *policy_ref, TPM2B_DIGEST *digest); +int tpm2_calculate_policy_pcr(const TPML_PCR_SELECTION *pcr_selection, const TPM2B_DIGEST pcr_values[], size_t pcr_values_count, TPM2B_DIGEST *digest); + +int tpm2_seal(const char *device, uint32_t hash_pcr_mask, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, const char *pin, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, void **ret_pcr_hash, size_t *ret_pcr_hash_size, uint16_t *ret_pcr_bank, uint16_t *ret_primary_alg, void **ret_srk_buf, size_t *ret_srk_buf_size); +int tpm2_unseal(const char *device, uint32_t hash_pcr_mask, uint16_t pcr_bank, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, JsonVariant *signature, const char *pin, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, const void *srk_buf, size_t srk_buf_size, void **ret_secret, size_t *ret_secret_size); + #else /* HAVE_TPM2 */ typedef struct {} Tpm2Context; typedef struct {} Tpm2Handle; From 75de375aafa78ccf2cd93a2c0a15d3bc4b0fe4b7 Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Fri, 14 Jul 2023 07:23:55 -0400 Subject: [PATCH 07/20] tpm2: declare tpm2_log_debug_*() functions in tpm2_util.h Allow other code to use the log debug functions; e.g. they are useful in test code. --- src/shared/tpm2-util.c | 10 +++++----- src/shared/tpm2-util.h | 6 ++++++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index 26a67accff..f717784a39 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -1833,7 +1833,7 @@ char *tpm2_pcr_values_to_string(const Tpm2PCRValue *pcr_values, size_t n_pcr_val return s ? TAKE_PTR(s) : strdup(""); } -static void tpm2_log_debug_tpml_pcr_selection(const TPML_PCR_SELECTION *l, const char *msg) { +void tpm2_log_debug_tpml_pcr_selection(const TPML_PCR_SELECTION *l, const char *msg) { if (!DEBUG_LOGGING || !l) return; @@ -1841,7 +1841,7 @@ static void tpm2_log_debug_tpml_pcr_selection(const TPML_PCR_SELECTION *l, const log_debug("%s: %s", msg ?: "PCR selection", strna(s)); } -static void tpm2_log_debug_pcr_value(const Tpm2PCRValue *pcr_value, const char *msg) { +void tpm2_log_debug_pcr_value(const Tpm2PCRValue *pcr_value, const char *msg) { if (!DEBUG_LOGGING || !pcr_value) return; @@ -1849,7 +1849,7 @@ static void tpm2_log_debug_pcr_value(const Tpm2PCRValue *pcr_value, const char * log_debug("%s: %s", msg ?: "PCR value", strna(s)); } -static void tpm2_log_debug_buffer(const void *buffer, size_t size, const char *msg) { +void tpm2_log_debug_buffer(const void *buffer, size_t size, const char *msg) { if (!DEBUG_LOGGING || !buffer || size == 0) return; @@ -1857,12 +1857,12 @@ static void tpm2_log_debug_buffer(const void *buffer, size_t size, const char *m log_debug("%s: %s", msg ?: "Buffer", strna(h)); } -static void tpm2_log_debug_digest(const TPM2B_DIGEST *digest, const char *msg) { +void tpm2_log_debug_digest(const TPM2B_DIGEST *digest, const char *msg) { if (digest) tpm2_log_debug_buffer(digest->buffer, digest->size, msg ?: "Digest"); } -static void tpm2_log_debug_name(const TPM2B_NAME *name, const char *msg) { +void tpm2_log_debug_name(const TPM2B_NAME *name, const char *msg) { if (name) tpm2_log_debug_buffer(name->name, name->size, msg ?: "Name"); } diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h index bf8135d0af..4916620a25 100644 --- a/src/shared/tpm2-util.h +++ b/src/shared/tpm2-util.h @@ -139,6 +139,12 @@ static inline int tpm2_digest_init(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest) { return tpm2_digest_many(alg, digest, NULL, 0, false); } +void tpm2_log_debug_tpml_pcr_selection(const TPML_PCR_SELECTION *l, const char *msg); +void tpm2_log_debug_pcr_value(const Tpm2PCRValue *pcr_value, const char *msg); +void tpm2_log_debug_buffer(const void *buffer, size_t size, const char *msg); +void tpm2_log_debug_digest(const TPM2B_DIGEST *digest, const char *msg); +void tpm2_log_debug_name(const TPM2B_NAME *name, const char *msg); + int tpm2_calculate_name(const TPMT_PUBLIC *public, TPM2B_NAME *ret_name); int tpm2_calculate_policy_auth_value(TPM2B_DIGEST *digest); int tpm2_calculate_policy_authorize(const TPM2B_PUBLIC *public, const TPM2B_DIGEST *policy_ref, TPM2B_DIGEST *digest); From 6e8fb3ad5ff2dab03b9e2b189adaf463c06a8101 Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Wed, 12 Jul 2023 22:14:18 -0400 Subject: [PATCH 08/20] tpm2: change tpm2_calculate_policy_pcr(), tpm2_calculate_sealing_policy() to use Tpm2PCRValue array An array of Tpm2PCRValue objects effectively replaces a TPML_PCR_SELECTION object combined with an array of (properly ordered) TPM2B_DIGEST objects. Also update tpm2_calculate_sealing_policy() pin parameter to boolean use_pin, since the function does not need to know the pin value, only if a pin is being used. --- src/boot/measure.c | 15 ++++---- src/shared/tpm2-util.c | 51 ++++++++++++-------------- src/shared/tpm2-util.h | 2 +- src/test/test-tpm2.c | 82 +++++++++++++++++++++++------------------- 4 files changed, 74 insertions(+), 76 deletions(-) diff --git a/src/boot/measure.c b/src/boot/measure.c index e388fb666e..4dd039877c 100644 --- a/src/boot/measure.c +++ b/src/boot/measure.c @@ -831,23 +831,20 @@ static int verb_sign(int argc, char *argv[], void *userdata) { if (tpmalg < 0) return log_error_errno(tpmalg, "Unsupported PCR bank"); - TPML_PCR_SELECTION pcr_selection; - tpm2_tpml_pcr_selection_from_mask(1 << TPM_PCR_INDEX_KERNEL_IMAGE, - tpmalg, - &pcr_selection); - - TPM2B_DIGEST pcr_values = { + TPM2B_DIGEST pcr_digest = { .size = p->value_size, }; - assert(sizeof(pcr_values.buffer) >= p->value_size); - memcpy_safe(pcr_values.buffer, p->value, p->value_size); + assert(sizeof(pcr_digest.buffer) >= p->value_size); + memcpy_safe(pcr_digest.buffer, p->value, p->value_size); + + Tpm2PCRValue pcr_value = TPM2_PCR_VALUE_MAKE(TPM_PCR_INDEX_KERNEL_IMAGE, tpmalg, pcr_digest); TPM2B_DIGEST pcr_policy_digest; r = tpm2_digest_init(TPM2_ALG_SHA256, &pcr_policy_digest); if (r < 0) return r; - r = tpm2_calculate_policy_pcr(&pcr_selection, &pcr_values, 1, &pcr_policy_digest); + r = tpm2_calculate_policy_pcr(&pcr_value, 1, &pcr_policy_digest); if (r < 0) return r; diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index f717784a39..0ef6d3400a 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -3212,8 +3212,7 @@ static int tpm2_policy_auth_value( /* Extend 'digest' with the PolicyPCR calculated hash. */ int tpm2_calculate_policy_pcr( - const TPML_PCR_SELECTION *pcr_selection, - const TPM2B_DIGEST pcr_values[], + const Tpm2PCRValue *pcr_values, size_t n_pcr_values, TPM2B_DIGEST *digest) { @@ -3221,7 +3220,6 @@ int tpm2_calculate_policy_pcr( TSS2_RC rc; int r; - assert(pcr_selection); assert(pcr_values || n_pcr_values == 0); assert(digest); assert(digest->size == SHA256_DIGEST_SIZE); @@ -3230,13 +3228,20 @@ int tpm2_calculate_policy_pcr( if (r < 0) return log_error_errno(r, "TPM2 support not installed: %m"); + TPML_PCR_SELECTION pcr_selection; + _cleanup_free_ TPM2B_DIGEST *values = NULL; + size_t n_values; + r = tpm2_tpml_pcr_selection_from_pcr_values(pcr_values, n_pcr_values, &pcr_selection, &values, &n_values); + if (r < 0) + return log_error_errno(r, "Could not convert PCR values to TPML_PCR_SELECTION: %m"); + TPM2B_DIGEST hash = {}; - r = tpm2_digest_many_digests(TPM2_ALG_SHA256, &hash, pcr_values, n_pcr_values, /* extend= */ false); + r = tpm2_digest_many_digests(TPM2_ALG_SHA256, &hash, values, n_values, /* extend= */ false); if (r < 0) return r; _cleanup_free_ uint8_t *buf = NULL; - size_t size = 0, maxsize = sizeof(command) + sizeof(*pcr_selection); + size_t size = 0, maxsize = sizeof(command) + sizeof(pcr_selection); buf = malloc(maxsize); if (!buf) @@ -3247,7 +3252,7 @@ int tpm2_calculate_policy_pcr( return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to marshal PolicyPCR command: %s", sym_Tss2_RC_Decode(rc)); - rc = sym_Tss2_MU_TPML_PCR_SELECTION_Marshal(pcr_selection, buf, maxsize, &size); + rc = sym_Tss2_MU_TPML_PCR_SELECTION_Marshal(&pcr_selection, buf, maxsize, &size); if (rc != TSS2_RC_SUCCESS) return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to marshal PCR selection: %s", sym_Tss2_RC_Decode(rc)); @@ -3473,15 +3478,15 @@ static int tpm2_policy_authorize( /* Extend 'digest' with the calculated policy hash. */ static int tpm2_calculate_sealing_policy( - const TPML_PCR_SELECTION *hash_pcr_selection, - const TPM2B_DIGEST *hash_pcr_values, - size_t n_hash_pcr_values, + const Tpm2PCRValue *pcr_values, + size_t n_pcr_values, const TPM2B_PUBLIC *public, - const char *pin, + bool use_pin, TPM2B_DIGEST *digest) { int r; + assert(pcr_values || n_pcr_values == 0); assert(digest); if (public) { @@ -3490,13 +3495,13 @@ static int tpm2_calculate_sealing_policy( return r; } - if (hash_pcr_selection && !tpm2_tpml_pcr_selection_is_empty(hash_pcr_selection)) { - r = tpm2_calculate_policy_pcr(hash_pcr_selection, hash_pcr_values, n_hash_pcr_values, digest); + if (n_pcr_values > 0) { + r = tpm2_calculate_policy_pcr(pcr_values, n_pcr_values, digest); if (r < 0) return r; } - if (pin) { + if (use_pin) { r = tpm2_calculate_policy_auth_value(digest); if (r < 0) return r; @@ -3627,28 +3632,17 @@ int tpm2_seal(const char *device, return r; } - TPML_PCR_SELECTION hash_pcr_selection = {}; - _cleanup_free_ TPM2B_DIGEST *hash_pcr_values = NULL; + _cleanup_free_ Tpm2PCRValue *hash_pcr_values = NULL; size_t n_hash_pcr_values; if (hash_pcr_mask) { /* For now, we just read the current values from the system; we need to be able to specify * expected values, eventually. */ + TPML_PCR_SELECTION hash_pcr_selection; tpm2_tpml_pcr_selection_from_mask(hash_pcr_mask, pcr_bank, &hash_pcr_selection); - _cleanup_free_ Tpm2PCRValue *pcr_values = NULL; - size_t n_pcr_values; - r = tpm2_pcr_read(c, &hash_pcr_selection, &pcr_values, &n_pcr_values); + r = tpm2_pcr_read(c, &hash_pcr_selection, &hash_pcr_values, &n_hash_pcr_values); if (r < 0) return r; - - r = tpm2_tpml_pcr_selection_from_pcr_values( - pcr_values, - n_pcr_values, - &hash_pcr_selection, - &hash_pcr_values, - &n_hash_pcr_values); - if (r < 0) - return log_error_errno(r, "Could not get PCR selection from values: %m"); } TPM2B_PUBLIC pubkey_tpm2, *authorize_key = NULL; @@ -3665,11 +3659,10 @@ int tpm2_seal(const char *device, return r; r = tpm2_calculate_sealing_policy( - &hash_pcr_selection, hash_pcr_values, n_hash_pcr_values, authorize_key, - pin, + !!pin, &policy_digest); if (r < 0) return r; diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h index 4916620a25..68eec1cb58 100644 --- a/src/shared/tpm2-util.h +++ b/src/shared/tpm2-util.h @@ -148,7 +148,7 @@ void tpm2_log_debug_name(const TPM2B_NAME *name, const char *msg); int tpm2_calculate_name(const TPMT_PUBLIC *public, TPM2B_NAME *ret_name); int tpm2_calculate_policy_auth_value(TPM2B_DIGEST *digest); int tpm2_calculate_policy_authorize(const TPM2B_PUBLIC *public, const TPM2B_DIGEST *policy_ref, TPM2B_DIGEST *digest); -int tpm2_calculate_policy_pcr(const TPML_PCR_SELECTION *pcr_selection, const TPM2B_DIGEST pcr_values[], size_t pcr_values_count, TPM2B_DIGEST *digest); +int tpm2_calculate_policy_pcr(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, TPM2B_DIGEST *digest); int tpm2_seal(const char *device, uint32_t hash_pcr_mask, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, const char *pin, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, void **ret_pcr_hash, size_t *ret_pcr_hash_size, uint16_t *ret_pcr_bank, uint16_t *ret_primary_alg, void **ret_srk_buf, size_t *ret_srk_buf_size); int tpm2_unseal(const char *device, uint32_t hash_pcr_mask, uint16_t pcr_bank, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, JsonVariant *signature, const char *pin, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, const void *srk_buf, size_t srk_buf_size, void **ret_secret, size_t *ret_secret_size); diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c index 401ddddda8..8513581526 100644 --- a/src/test/test-tpm2.c +++ b/src/test/test-tpm2.c @@ -619,50 +619,58 @@ TEST(calculate_policy_authorize) { } TEST(calculate_policy_pcr) { - TPML_PCR_SELECTION pcr_selection; - TPM2B_DIGEST pcr_values[16]; - TPM2B_DIGEST d; - uint32_t pcr_mask; + TPM2B_DIGEST d, dN[16]; + + digest_init_sha256(&dN[ 0], "2124793cbbe60c3a8637d3b84a5d054e87c351e1469a285acc04755e8b204dec"); + digest_init_sha256(&dN[ 1], "bf7592f18adcfdc549fc0b94939f5069a24697f9cff4a0dca29014767b97559d"); + digest_init_sha256(&dN[ 2], "4b00cff9dee3a364979b2dc241b34568a8ad49fcf2713df259e47dff8875feed"); + digest_init_sha256(&dN[ 3], "3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969"); + digest_init_sha256(&dN[ 4], "368f85b3013041dfe203faaa364f00b07c5da7b1e5f1dbf2efb06fa6b9bd92de"); + digest_init_sha256(&dN[ 5], "c97c40369691c8e4aa78fb3a52655cd193b780a838b8e23f5f476576919db5e5"); + digest_init_sha256(&dN[ 6], "3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969"); + digest_init_sha256(&dN[ 7], "aa1154c9e0a774854ccbed4c8ce7e9b906b3d700a1a8db1772d0341a62dbe51b"); + digest_init_sha256(&dN[ 8], "cfde439a2c06af3479ca6bdc60429b90553d65300c5cfcc40004a08c6b5ad81a"); + digest_init_sha256(&dN[ 9], "9c2bac22ef5ec84fcdb71c3ebf776cba1247e5da980e5ee08e45666a2edf0b8b"); + digest_init_sha256(&dN[10], "9885873f4d7348199ad286f8f2476d4f866940950f6f9fb9f945ed352dbdcbd2"); + digest_init_sha256(&dN[11], "42400ab950d21aa79d12cc4fdef67d1087a39ad64900619831c0974dbae54e44"); + digest_init_sha256(&dN[12], "767d064382e56ca1ad3bdcc6bc596112e6c2008b593d3570d24c2bfa64c4628c"); + digest_init_sha256(&dN[13], "30c16133175959408c9745d8dafadef5daf4b39cb2be04df0d60089bd46d3cc4"); + digest_init_sha256(&dN[14], "e3991b7ddd47be7e92726a832d6874c5349b52b789fa0db8b558c69fea29574e"); + digest_init_sha256(&dN[15], "852dae3ecb992bdeb13d6002fefeeffdd90feca8b378d56681ef2c885d0e5137"); digest_init_sha256(&d, "0000000000000000000000000000000000000000000000000000000000000000"); - pcr_mask = (1<<4) | (1<<7) | (1<<8); - tpm2_tpml_pcr_selection_from_mask(pcr_mask, TPM2_ALG_SHA256, &pcr_selection); - digest_init_sha256(&pcr_values[0], "368f85b3013041dfe203faaa364f00b07c5da7b1e5f1dbf2efb06fa6b9bd92de"); - digest_init_sha256(&pcr_values[1], "aa1154c9e0a774854ccbed4c8ce7e9b906b3d700a1a8db1772d0341a62dbe51b"); - digest_init_sha256(&pcr_values[2], "cfde439a2c06af3479ca6bdc60429b90553d65300c5cfcc40004a08c6b5ad81a"); - assert_se(tpm2_calculate_policy_pcr(&pcr_selection, pcr_values, 3, &d) == 0); + Tpm2PCRValue v1[] = { + TPM2_PCR_VALUE_MAKE(4, TPM2_ALG_SHA256, dN[4]), + TPM2_PCR_VALUE_MAKE(7, TPM2_ALG_SHA256, dN[7]), + TPM2_PCR_VALUE_MAKE(8, TPM2_ALG_SHA256, dN[8]), + }; + assert_se(tpm2_calculate_policy_pcr(v1, ELEMENTSOF(v1), &d) == 0); assert_se(digest_check(&d, "76532a0e16f7e6bf6b02918c11f75d99d729fab0cc81d0df2c4284a2c4fe6e05")); - - pcr_mask = (1<<4) | (1<<7) | (1<<8); - tpm2_tpml_pcr_selection_from_mask(pcr_mask, TPM2_ALG_SHA256, &pcr_selection); - digest_init_sha256(&pcr_values[0], "368f85b3013041dfe203faaa364f00b07c5da7b1e5f1dbf2efb06fa6b9bd92de"); - digest_init_sha256(&pcr_values[1], "aa1154c9e0a774854ccbed4c8ce7e9b906b3d700a1a8db1772d0341a62dbe51b"); - digest_init_sha256(&pcr_values[2], "cfde439a2c06af3479ca6bdc60429b90553d65300c5cfcc40004a08c6b5ad81a"); - assert_se(tpm2_calculate_policy_pcr(&pcr_selection, pcr_values, 3, &d) == 0); + assert_se(tpm2_calculate_policy_pcr(v1, ELEMENTSOF(v1), &d) == 0); assert_se(digest_check(&d, "97e64bcabb64c1fa4b726528644926c8029f5b4458b0575c98c04fe225629a0b")); digest_init_sha256(&d, "0000000000000000000000000000000000000000000000000000000000000000"); - pcr_mask = 0xffff; - tpm2_tpml_pcr_selection_from_mask(pcr_mask, TPM2_ALG_SHA256, &pcr_selection); - digest_init_sha256(&pcr_values[ 0], "2124793cbbe60c3a8637d3b84a5d054e87c351e1469a285acc04755e8b204dec"); - digest_init_sha256(&pcr_values[ 1], "bf7592f18adcfdc549fc0b94939f5069a24697f9cff4a0dca29014767b97559d"); - digest_init_sha256(&pcr_values[ 2], "4b00cff9dee3a364979b2dc241b34568a8ad49fcf2713df259e47dff8875feed"); - digest_init_sha256(&pcr_values[ 3], "3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969"); - digest_init_sha256(&pcr_values[ 4], "368f85b3013041dfe203faaa364f00b07c5da7b1e5f1dbf2efb06fa6b9bd92de"); - digest_init_sha256(&pcr_values[ 5], "c97c40369691c8e4aa78fb3a52655cd193b780a838b8e23f5f476576919db5e5"); - digest_init_sha256(&pcr_values[ 6], "3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969"); - digest_init_sha256(&pcr_values[ 7], "aa1154c9e0a774854ccbed4c8ce7e9b906b3d700a1a8db1772d0341a62dbe51b"); - digest_init_sha256(&pcr_values[ 8], "cfde439a2c06af3479ca6bdc60429b90553d65300c5cfcc40004a08c6b5ad81a"); - digest_init_sha256(&pcr_values[ 9], "9c2bac22ef5ec84fcdb71c3ebf776cba1247e5da980e5ee08e45666a2edf0b8b"); - digest_init_sha256(&pcr_values[10], "9885873f4d7348199ad286f8f2476d4f866940950f6f9fb9f945ed352dbdcbd2"); - digest_init_sha256(&pcr_values[11], "42400ab950d21aa79d12cc4fdef67d1087a39ad64900619831c0974dbae54e44"); - digest_init_sha256(&pcr_values[12], "767d064382e56ca1ad3bdcc6bc596112e6c2008b593d3570d24c2bfa64c4628c"); - digest_init_sha256(&pcr_values[13], "30c16133175959408c9745d8dafadef5daf4b39cb2be04df0d60089bd46d3cc4"); - digest_init_sha256(&pcr_values[14], "e3991b7ddd47be7e92726a832d6874c5349b52b789fa0db8b558c69fea29574e"); - digest_init_sha256(&pcr_values[15], "852dae3ecb992bdeb13d6002fefeeffdd90feca8b378d56681ef2c885d0e5137"); - assert_se(tpm2_calculate_policy_pcr(&pcr_selection, pcr_values, 16, &d) == 0); + Tpm2PCRValue v2[] = { + TPM2_PCR_VALUE_MAKE( 0, TPM2_ALG_SHA256, dN[ 0]), + TPM2_PCR_VALUE_MAKE( 1, TPM2_ALG_SHA256, dN[ 1]), + TPM2_PCR_VALUE_MAKE( 2, TPM2_ALG_SHA256, dN[ 2]), + TPM2_PCR_VALUE_MAKE( 3, TPM2_ALG_SHA256, dN[ 3]), + TPM2_PCR_VALUE_MAKE( 4, TPM2_ALG_SHA256, dN[ 4]), + TPM2_PCR_VALUE_MAKE( 5, TPM2_ALG_SHA256, dN[ 5]), + TPM2_PCR_VALUE_MAKE( 6, TPM2_ALG_SHA256, dN[ 6]), + TPM2_PCR_VALUE_MAKE( 7, TPM2_ALG_SHA256, dN[ 7]), + TPM2_PCR_VALUE_MAKE( 8, TPM2_ALG_SHA256, dN[ 8]), + TPM2_PCR_VALUE_MAKE( 9, TPM2_ALG_SHA256, dN[ 9]), + TPM2_PCR_VALUE_MAKE(10, TPM2_ALG_SHA256, dN[10]), + TPM2_PCR_VALUE_MAKE(11, TPM2_ALG_SHA256, dN[11]), + TPM2_PCR_VALUE_MAKE(12, TPM2_ALG_SHA256, dN[12]), + TPM2_PCR_VALUE_MAKE(13, TPM2_ALG_SHA256, dN[13]), + TPM2_PCR_VALUE_MAKE(14, TPM2_ALG_SHA256, dN[14]), + TPM2_PCR_VALUE_MAKE(15, TPM2_ALG_SHA256, dN[15]), + }; + assert_se(tpm2_calculate_policy_pcr(v2, ELEMENTSOF(v2), &d) == 0); assert_se(digest_check(&d, "22be4f1674f792d6345cea9427701068f0e8d9f42755dcc0e927e545a68f9c13")); - assert_se(tpm2_calculate_policy_pcr(&pcr_selection, pcr_values, 16, &d) == 0); + assert_se(tpm2_calculate_policy_pcr(v2, ELEMENTSOF(v2), &d) == 0); assert_se(digest_check(&d, "7481fd1b116078eb3ac2456e4ad542c9b46b9b8eb891335771ca8e7c8f8e4415")); } From 07c040611751facf075dab7a72ab4935142dda3c Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Wed, 12 Jul 2023 22:36:37 -0400 Subject: [PATCH 09/20] tpm2: change tpm2_parse_pcr_argument() parameters to parse to Tpm2PCRValue array In order to allow users to specify expected PCR values, change the tpm2_parse_pcr_argument() to parse the text argument into an array of Tpm2PCRValue objects, which provide not only the selected PCR indexes, but also (optionally) the hash algorithm and hash value for each PCR index. --- src/creds/creds.c | 4 +- src/cryptenroll/cryptenroll.c | 4 +- src/cryptsetup/cryptsetup.c | 2 +- src/partition/repart.c | 4 +- src/shared/tpm2-util.c | 187 ++++++++++++++++------- src/shared/tpm2-util.h | 5 +- src/test/test-tpm2.c | 277 ++++++++++++++++++++++++++-------- 7 files changed, 358 insertions(+), 125 deletions(-) diff --git a/src/creds/creds.c b/src/creds/creds.c index 679b0750de..36531d7d18 100644 --- a/src/creds/creds.c +++ b/src/creds/creds.c @@ -860,7 +860,7 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_TPM2_PCRS: /* For fixed hash PCR policies only */ - r = tpm2_parse_pcr_argument(optarg, &arg_tpm2_pcr_mask); + r = tpm2_parse_pcr_argument_to_mask(optarg, &arg_tpm2_pcr_mask); if (r < 0) return r; @@ -874,7 +874,7 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_TPM2_PUBLIC_KEY_PCRS: /* For public key PCR policies only */ - r = tpm2_parse_pcr_argument(optarg, &arg_tpm2_public_key_pcr_mask); + r = tpm2_parse_pcr_argument_to_mask(optarg, &arg_tpm2_public_key_pcr_mask); if (r < 0) return r; diff --git a/src/cryptenroll/cryptenroll.c b/src/cryptenroll/cryptenroll.c index fe25619d85..b2e4c0a5f5 100644 --- a/src/cryptenroll/cryptenroll.c +++ b/src/cryptenroll/cryptenroll.c @@ -356,7 +356,7 @@ static int parse_argv(int argc, char *argv[]) { } case ARG_TPM2_PCRS: - r = tpm2_parse_pcr_argument(optarg, &arg_tpm2_pcr_mask); + r = tpm2_parse_pcr_argument_to_mask(optarg, &arg_tpm2_pcr_mask); if (r < 0) return r; @@ -377,7 +377,7 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_TPM2_PUBLIC_KEY_PCRS: - r = tpm2_parse_pcr_argument(optarg, &arg_tpm2_public_key_pcr_mask); + r = tpm2_parse_pcr_argument_to_mask(optarg, &arg_tpm2_public_key_pcr_mask); if (r < 0) return r; diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index 5dc3c7f123..b5aa396cde 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -399,7 +399,7 @@ static int parse_one_option(const char *option) { } else if ((val = startswith(option, "tpm2-pcrs="))) { - r = tpm2_parse_pcr_argument(val, &arg_tpm2_pcr_mask); + r = tpm2_parse_pcr_argument_to_mask(val, &arg_tpm2_pcr_mask); if (r < 0) return r; diff --git a/src/partition/repart.c b/src/partition/repart.c index 4fe2c63a46..9e30c6bbc3 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -6447,7 +6447,7 @@ static int parse_argv(int argc, char *argv[]) { } case ARG_TPM2_PCRS: - r = tpm2_parse_pcr_argument(optarg, &arg_tpm2_pcr_mask); + r = tpm2_parse_pcr_argument_to_mask(optarg, &arg_tpm2_pcr_mask); if (r < 0) return r; @@ -6461,7 +6461,7 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_TPM2_PUBLIC_KEY_PCRS: - r = tpm2_parse_pcr_argument(optarg, &arg_tpm2_public_key_pcr_mask); + r = tpm2_parse_pcr_argument_to_mask(optarg, &arg_tpm2_public_key_pcr_mask); if (r < 0) return r; diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index 0ef6d3400a..70c3f0a8e4 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -4259,45 +4259,6 @@ char *tpm2_pcr_mask_to_string(uint32_t mask) { return TAKE_PTR(s); } -int tpm2_pcr_mask_from_string(const char *arg, uint32_t *ret_mask) { - uint32_t mask = 0; - int r; - - assert(arg); - assert(ret_mask); - - if (isempty(arg)) { - *ret_mask = 0; - return 0; - } - - /* Parses a "," or "+" separated list of PCR indexes. We support "," since this is a list after all, - * and most other tools expect comma separated PCR specifications. We also support "+" since in - * /etc/crypttab the "," is already used to separate options, hence a different separator is nice to - * avoid escaping. */ - - const char *p = arg; - for (;;) { - _cleanup_free_ char *pcr = NULL; - unsigned n; - - r = extract_first_word(&p, &pcr, ",+", EXTRACT_DONT_COALESCE_SEPARATORS); - if (r == 0) - break; - if (r < 0) - return log_error_errno(r, "Failed to parse PCR list: %s", arg); - - r = pcr_index_from_string(pcr); - if (r < 0) - return log_error_errno(r, "Failed to parse specified PCR or specified PCR is out of range: %s", pcr); - n = r; - SET_BIT(mask, n); - } - - *ret_mask = mask; - return 0; -} - int tpm2_make_pcr_json_array(uint32_t pcr_mask, JsonVariant **ret) { _cleanup_(json_variant_unrefp) JsonVariant *a = NULL; int r; @@ -4678,29 +4639,147 @@ Tpm2Support tpm2_support(void) { return support; } -int tpm2_parse_pcr_argument(const char *arg, uint32_t *mask) { - uint32_t m; +#if HAVE_TPM2 +static void tpm2_pcr_values_apply_default_hash_alg(Tpm2PCRValue *pcr_values, size_t n_pcr_values) { + TPMI_ALG_HASH default_hash = 0; + for (size_t i = 0; i < n_pcr_values; i++) + if (pcr_values[i].hash != 0) { + default_hash = pcr_values[i].hash; + break; + } + + if (default_hash != 0) + for (size_t i = 0; i < n_pcr_values; i++) + if (pcr_values[i].hash == 0) + pcr_values[i].hash = default_hash; +} +#endif + +/* Parse the PCR selection/value arg(s) and return a corresponding array of Tpm2PCRValue objects. + * + * The format is the same as tpm2_pcr_values_from_string(). The first provided entry with a hash algorithm + * set will be used as the 'default' hash algorithm. All entries with an unset hash algorithm will be updated + * with the 'default' hash algorithm. The resulting array will be sorted and checked for validity. + * + * This will replace *ret_pcr_values with the new array of pcr values; to append to an existing array, use + * tpm2_parse_pcr_argument_append(). */ +int tpm2_parse_pcr_argument(const char *arg, Tpm2PCRValue **ret_pcr_values, size_t *ret_n_pcr_values) { +#if HAVE_TPM2 int r; - assert(mask); + assert(arg); + assert(ret_pcr_values); + assert(ret_n_pcr_values); - /* For use in getopt_long() command line parsers: merges masks specified on the command line */ - - if (isempty(arg)) { - *mask = 0; - return 0; - } - - r = tpm2_pcr_mask_from_string(arg, &m); + _cleanup_free_ Tpm2PCRValue *pcr_values = NULL; + size_t n_pcr_values = 0; + r = tpm2_pcr_values_from_string(arg, &pcr_values, &n_pcr_values); if (r < 0) return r; - if (*mask == UINT32_MAX) - *mask = m; - else - *mask |= m; + tpm2_pcr_values_apply_default_hash_alg(pcr_values, n_pcr_values); + + tpm2_sort_pcr_values(pcr_values, n_pcr_values); + + if (!TPM2_PCR_VALUES_VALID(pcr_values, n_pcr_values)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Parsed PCR values are not valid."); + + *ret_pcr_values = TAKE_PTR(pcr_values); + *ret_n_pcr_values = n_pcr_values; return 0; +#else + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM2 support is disabled."); +#endif +} + +/* Same as tpm2_parse_pcr_argument(), but the pcr values array is appended to. If the provided pcr values + * array is not NULL, it must point to an allocated pcr values array and the provided number of pcr values + * must be correct. + * + * Note that 'arg' is parsed into a new array of pcr values independently of any previous pcr values, + * including application of the default hash algorithm. Then the two arrays are combined, the default hash + * algorithm check applied again (in case either the previous or current array had no default hash + * algorithm), and then the resulting array is sorted and rechecked for validity. */ +int tpm2_parse_pcr_argument_append(const char *arg, Tpm2PCRValue **ret_pcr_values, size_t *ret_n_pcr_values) { +#if HAVE_TPM2 + int r; + + assert(arg); + assert(ret_pcr_values); + assert(ret_n_pcr_values); + + _cleanup_free_ Tpm2PCRValue *pcr_values = NULL; + size_t n_pcr_values; + r = tpm2_parse_pcr_argument(arg, &pcr_values, &n_pcr_values); + if (r < 0) + return r; + + /* If we got previous values, append them. */ + if (*ret_pcr_values && !GREEDY_REALLOC_APPEND(pcr_values, n_pcr_values, *ret_pcr_values, *ret_n_pcr_values)) + return log_oom(); + + tpm2_pcr_values_apply_default_hash_alg(pcr_values, n_pcr_values); + + tpm2_sort_pcr_values(pcr_values, n_pcr_values); + + if (!TPM2_PCR_VALUES_VALID(pcr_values, n_pcr_values)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Parsed PCR values are not valid."); + + SWAP_TWO(*ret_pcr_values, pcr_values); + *ret_n_pcr_values = n_pcr_values; + + return 0; +#else + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM2 support is disabled."); +#endif +} + +/* Same as tpm2_parse_pcr_argument() but converts the pcr values to a pcr mask. If more than one hash + * algorithm is included in the pcr values array this results in error. This retains the previous behavior of + * tpm2_parse_pcr_argument() of clearing the mask if 'arg' is empty, replacing the mask if it is set to + * UINT32_MAX, and or-ing the mask otherwise. */ +int tpm2_parse_pcr_argument_to_mask(const char *arg, uint32_t *ret_mask) { +#if HAVE_TPM2 + _cleanup_free_ Tpm2PCRValue *pcr_values = NULL; + size_t n_pcr_values; + int r; + + assert(arg); + assert(ret_mask); + + r = tpm2_parse_pcr_argument(arg, &pcr_values, &n_pcr_values); + if (r < 0) + return r; + + if (n_pcr_values == 0) { + /* This retains the previous behavior of clearing the mask if the arg is empty */ + *ret_mask = 0; + return 0; + } + + size_t hash_count; + r = tpm2_pcr_values_hash_count(pcr_values, n_pcr_values, &hash_count); + if (r < 0) + return log_error_errno(r, "Could not get hash count from pcr values: %m"); + + if (hash_count > 1) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Multiple PCR hash banks selected."); + + uint32_t new_mask; + r = tpm2_pcr_values_to_mask(pcr_values, n_pcr_values, pcr_values[0].hash, &new_mask); + if (r < 0) + return log_error_errno(r, "Could not get pcr values mask: %m"); + + if (*ret_mask == UINT32_MAX) + *ret_mask = new_mask; + else + *ret_mask |= new_mask; + + return 0; +#else + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM2 support is disabled."); +#endif } int tpm2_load_pcr_signature(const char *path, JsonVariant **ret) { diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h index 68eec1cb58..e44aa14106 100644 --- a/src/shared/tpm2-util.h +++ b/src/shared/tpm2-util.h @@ -208,7 +208,6 @@ const char *tpm2_asym_alg_to_string(uint16_t alg); int tpm2_asym_alg_from_string(const char *alg); char *tpm2_pcr_mask_to_string(uint32_t mask); -int tpm2_pcr_mask_from_string(const char *arg, uint32_t *mask); typedef struct { uint32_t search_pcr_mask; @@ -257,7 +256,9 @@ enum { Tpm2Support tpm2_support(void); -int tpm2_parse_pcr_argument(const char *arg, uint32_t *mask); +int tpm2_parse_pcr_argument(const char *arg, Tpm2PCRValue **ret_pcr_values, size_t *ret_n_pcr_values); +int tpm2_parse_pcr_argument_append(const char *arg, Tpm2PCRValue **ret_pcr_values, size_t *ret_n_pcr_values); +int tpm2_parse_pcr_argument_to_mask(const char *arg, uint32_t *mask); int tpm2_load_pcr_signature(const char *path, JsonVariant **ret); int tpm2_load_pcr_public_key(const char *path, void **ret_pubkey, size_t *ret_pubkey_size); diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c index 8513581526..2b285fab47 100644 --- a/src/test/test-tpm2.c +++ b/src/test/test-tpm2.c @@ -4,39 +4,6 @@ #include "tpm2-util.h" #include "tests.h" -static void test_tpm2_pcr_mask_from_string_one(const char *s, uint32_t mask, int ret) { - uint32_t m; - - assert_se(tpm2_pcr_mask_from_string(s, &m) == ret); - - if (ret >= 0) - assert_se(m == mask); -} - -TEST(tpm2_mask_from_string) { - test_tpm2_pcr_mask_from_string_one("", 0, 0); - test_tpm2_pcr_mask_from_string_one("0", 1, 0); - test_tpm2_pcr_mask_from_string_one("1", 2, 0); - test_tpm2_pcr_mask_from_string_one("0,1", 3, 0); - test_tpm2_pcr_mask_from_string_one("0+1", 3, 0); - test_tpm2_pcr_mask_from_string_one("0-1", 0, -EINVAL); - test_tpm2_pcr_mask_from_string_one("0,1,2", 7, 0); - test_tpm2_pcr_mask_from_string_one("0+1+2", 7, 0); - test_tpm2_pcr_mask_from_string_one("0+1,2", 7, 0); - test_tpm2_pcr_mask_from_string_one("0,1+2", 7, 0); - test_tpm2_pcr_mask_from_string_one("0,2", 5, 0); - test_tpm2_pcr_mask_from_string_one("0+2", 5, 0); - test_tpm2_pcr_mask_from_string_one("foo", 0, -EINVAL); - test_tpm2_pcr_mask_from_string_one("7+application-support", 8388736, 0); - test_tpm2_pcr_mask_from_string_one("8+boot-loader-code", 272, 0); - test_tpm2_pcr_mask_from_string_one("6+boot-loader-code,44", 0, -EINVAL); - test_tpm2_pcr_mask_from_string_one("7,shim-policy,4", 16528, 0); - test_tpm2_pcr_mask_from_string_one("sysexts,shim-policy+kernel-boot", 26624, 0); - test_tpm2_pcr_mask_from_string_one("sysexts,shim+kernel-boot", 0, -EINVAL); - test_tpm2_pcr_mask_from_string_one("sysexts+17+23", 8527872, 0); - test_tpm2_pcr_mask_from_string_one("debug+24", 0, -EINVAL); -} - TEST(pcr_index_from_string) { assert_se(pcr_index_from_string("platform-code") == 0); assert_se(pcr_index_from_string("0") == 0); @@ -463,18 +430,19 @@ static bool digest_check(const TPM2B_DIGEST *digest, const char *expect) { h = hexmem(digest->buffer, digest->size); assert_se(h); - return streq(expect, h); + return strcaseeq(expect, h); } -static void digest_init_sha256(TPM2B_DIGEST *digest, const char *hash) { +static void digest_init(TPM2B_DIGEST *digest, const char *hash) { _cleanup_free_ void *h = NULL; size_t s = 0; - assert_se(strlen(hash) == SHA256_DIGEST_SIZE * 2); assert_se(strlen(hash) <= sizeof(digest->buffer) * 2); assert_se(unhexmem(hash, strlen(hash), &h, &s) == 0); - assert_se(s == SHA256_DIGEST_SIZE); + + /* Make sure the length matches a known hash algorithm */ + assert_se(IN_SET(s, TPM2_SHA1_DIGEST_SIZE, TPM2_SHA256_DIGEST_SIZE, TPM2_SHA384_DIGEST_SIZE, TPM2_SHA512_DIGEST_SIZE)); memcpy_safe(digest->buffer, h, s); digest->size = s; @@ -485,11 +453,11 @@ static void digest_init_sha256(TPM2B_DIGEST *digest, const char *hash) { TEST(digest_many) { TPM2B_DIGEST d, d0, d1, d2, d3, d4; - digest_init_sha256(&d0, "0000000000000000000000000000000000000000000000000000000000000000"); - digest_init_sha256(&d1, "17b7703d9d00776310ba032e88c1a8c2a9c630ebdd799db622f6631530789175"); - digest_init_sha256(&d2, "12998c017066eb0d2a70b94e6ed3192985855ce390f321bbdb832022888bd251"); - digest_init_sha256(&d3, "c3a65887fedd3fb4f5d0047e906dff830bcbd1293160909eb4b05f485e7387ad"); - digest_init_sha256(&d4, "6491fb4bc08fc0b2ef47fc63db57e249917885e69d8c0d99667df83a59107a33"); + digest_init(&d0, "0000000000000000000000000000000000000000000000000000000000000000"); + digest_init(&d1, "17b7703d9d00776310ba032e88c1a8c2a9c630ebdd799db622f6631530789175"); + digest_init(&d2, "12998c017066eb0d2a70b94e6ed3192985855ce390f321bbdb832022888bd251"); + digest_init(&d3, "c3a65887fedd3fb4f5d0047e906dff830bcbd1293160909eb4b05f485e7387ad"); + digest_init(&d4, "6491fb4bc08fc0b2ef47fc63db57e249917885e69d8c0d99667df83a59107a33"); /* tpm2_digest_init, tpm2_digest_rehash */ d = (TPM2B_DIGEST){ .size = 1, .buffer = { 2, }, }; @@ -553,6 +521,191 @@ TEST(digest_many) { assert_se(digest_check(&d, "02ecb0628264235111e0053e271092981c8b15d59cd46617836bee3149a4ecb0")); } +static void check_parse_pcr_argument( + const char *arg, + const Tpm2PCRValue *prev_values, + size_t n_prev_values, + const Tpm2PCRValue *expected_values, + size_t n_expected_values) { + + _cleanup_free_ Tpm2PCRValue *values = NULL; + size_t n_values = 0; + + if (n_prev_values > 0) { + assert_se(GREEDY_REALLOC_APPEND(values, n_values, prev_values, n_prev_values)); + assert_se(tpm2_parse_pcr_argument_append(arg, &values, &n_values) == 0); + } else + assert_se(tpm2_parse_pcr_argument(arg, &values, &n_values) == 0); + + assert_se(n_values == n_expected_values); + for (size_t i = 0; i < n_values; i++) { + const Tpm2PCRValue *v = &values[i], *e = &expected_values[i]; + //tpm2_log_debug_pcr_value(e, "Expected value"); + //tpm2_log_debug_pcr_value(v, "Actual value"); + + assert_se(v->index == e->index); + assert_se(v->hash == e->hash); + assert_se(v->value.size == e->value.size); + assert_se(memcmp(v->value.buffer, e->value.buffer, e->value.size) == 0); + } + + size_t hash_count; + assert_se(tpm2_pcr_values_hash_count(expected_values, n_expected_values, &hash_count) == 0); + if (hash_count == 1) { + uint32_t mask = UINT32_MAX, expected_mask = 0; + + if (n_prev_values > 0) + assert_se(tpm2_pcr_values_to_mask(prev_values, n_prev_values, prev_values[0].hash, &mask) == 0); + + assert_se(tpm2_pcr_values_to_mask(expected_values, n_expected_values, expected_values[0].hash, &expected_mask) == 0); + + assert_se(tpm2_parse_pcr_argument_to_mask(arg, &mask) == 0); + assert_se(mask == expected_mask); + } + + size_t old_n_values = n_values; + assert_se(tpm2_parse_pcr_argument_append("", &values, &n_values) == 0); + assert_se(values); + assert_se(n_values == old_n_values); +} + +static void check_parse_pcr_argument_to_mask(const char *arg, int mask) { + uint32_t m = 0; + int r = tpm2_parse_pcr_argument_to_mask(arg, &m); + + if (mask < 0) + assert_se(mask == r); + else + assert_se((uint32_t) mask == m); +} + +TEST(parse_pcr_argument) { + _cleanup_free_ Tpm2PCRValue *t0p = NULL; + size_t n_t0p; + assert_se(tpm2_parse_pcr_argument("", &t0p, &n_t0p) == 0); + assert_se(n_t0p == 0); + assert_se(tpm2_parse_pcr_argument_append("", &t0p, &n_t0p) == 0); + assert_se(n_t0p == 0); + uint32_t m0 = 0xf; + assert_se(tpm2_parse_pcr_argument_to_mask("", &m0) == 0); + assert_se(m0 == 0); + assert_se(tpm2_parse_pcr_argument_to_mask("", &m0) == 0); + assert_se(m0 == 0); + + Tpm2PCRValue t1[] = { + TPM2_PCR_VALUE_MAKE(0, 0, {}), + TPM2_PCR_VALUE_MAKE(4, 0, {}), + TPM2_PCR_VALUE_MAKE(7, 0, {}), + TPM2_PCR_VALUE_MAKE(11, 0, {}), + }; + check_parse_pcr_argument("0,4,7,11", NULL, 0, t1, ELEMENTSOF(t1)); + check_parse_pcr_argument("11,4,7,0", NULL, 0, t1, ELEMENTSOF(t1)); + check_parse_pcr_argument("7,4,0,11", NULL, 0, t1, ELEMENTSOF(t1)); + check_parse_pcr_argument("11,7,4,0", NULL, 0, t1, ELEMENTSOF(t1)); + check_parse_pcr_argument("0+4+7+11", NULL, 0, t1, ELEMENTSOF(t1)); + check_parse_pcr_argument("0,4+7,11", NULL, 0, t1, ELEMENTSOF(t1)); + + Tpm2PCRValue t2[] = { + TPM2_PCR_VALUE_MAKE(0, TPM2_ALG_SHA1, {}), + TPM2_PCR_VALUE_MAKE(4, TPM2_ALG_SHA1, {}), + TPM2_PCR_VALUE_MAKE(7, TPM2_ALG_SHA1, {}), + TPM2_PCR_VALUE_MAKE(11, TPM2_ALG_SHA1, {}), + }; + check_parse_pcr_argument("0:sha1,4,7,11", NULL, 0, t2, ELEMENTSOF(t2)); + check_parse_pcr_argument("11,4,7,0:sha1", NULL, 0, t2, ELEMENTSOF(t2)); + check_parse_pcr_argument("7,4:sha1,0,11", NULL, 0, t2, ELEMENTSOF(t2)); + check_parse_pcr_argument("0:sha1,4:sha1,7:sha1,11:sha1", NULL, 0, t2, ELEMENTSOF(t2)); + check_parse_pcr_argument("0:sha1+4:sha1,11:sha1+7:sha1", NULL, 0, t2, ELEMENTSOF(t2)); + + Tpm2PCRValue t3[] = { + TPM2_PCR_VALUE_MAKE(0, TPM2_ALG_SHA1, {}), + TPM2_PCR_VALUE_MAKE(1, TPM2_ALG_SHA1, {}), + TPM2_PCR_VALUE_MAKE(2, TPM2_ALG_SHA1, {}), + TPM2_PCR_VALUE_MAKE(3, TPM2_ALG_SHA1, {}), + TPM2_PCR_VALUE_MAKE(4, TPM2_ALG_SHA1, {}), + TPM2_PCR_VALUE_MAKE(7, TPM2_ALG_SHA1, {}), + TPM2_PCR_VALUE_MAKE(11, TPM2_ALG_SHA1, {}), + TPM2_PCR_VALUE_MAKE(12, TPM2_ALG_SHA1, {}), + }; + check_parse_pcr_argument("1,2,3,12", t2, ELEMENTSOF(t2), t3, ELEMENTSOF(t3)); + check_parse_pcr_argument("12,2,3,1", t2, ELEMENTSOF(t2), t3, ELEMENTSOF(t3)); + check_parse_pcr_argument("1,2,3,12:sha1", t1, ELEMENTSOF(t1), t3, ELEMENTSOF(t3)); + check_parse_pcr_argument("1,2,3,12:sha1", t2, ELEMENTSOF(t2), t3, ELEMENTSOF(t3)); + check_parse_pcr_argument("1:sha1,2,3,12", t1, ELEMENTSOF(t1), t3, ELEMENTSOF(t3)); + check_parse_pcr_argument("1:sha1,2,3,12", t2, ELEMENTSOF(t2), t3, ELEMENTSOF(t3)); + check_parse_pcr_argument("1:sha1,2:sha1,3:sha1,12:sha1", t1, ELEMENTSOF(t1), t3, ELEMENTSOF(t3)); + check_parse_pcr_argument("1:sha1,2:sha1,3:sha1,12:sha1", t2, ELEMENTSOF(t2), t3, ELEMENTSOF(t3)); + + TPM2B_DIGEST d4; + digest_init(&d4, "FCE7F1083082B16CFE2B085DD7858BB11A37C09B78E36C79E5A2FD529353C4E2"); + Tpm2PCRValue t4[] = { + TPM2_PCR_VALUE_MAKE(0, TPM2_ALG_SHA256, {}), + TPM2_PCR_VALUE_MAKE(1, TPM2_ALG_SHA256, d4), + TPM2_PCR_VALUE_MAKE(2, TPM2_ALG_SHA256, {}), + TPM2_PCR_VALUE_MAKE(3, TPM2_ALG_SHA256, {}), + TPM2_PCR_VALUE_MAKE(4, TPM2_ALG_SHA256, {}), + TPM2_PCR_VALUE_MAKE(7, TPM2_ALG_SHA256, {}), + TPM2_PCR_VALUE_MAKE(11, TPM2_ALG_SHA256, {}), + TPM2_PCR_VALUE_MAKE(12, TPM2_ALG_SHA256, {}), + }; + check_parse_pcr_argument("1:sha256=0xFCE7F1083082B16CFE2B085DD7858BB11A37C09B78E36C79E5A2FD529353C4E2,2,3,12", t1, ELEMENTSOF(t1), t4, ELEMENTSOF(t4)); + check_parse_pcr_argument("12,2,3,1:sha256=FCE7F1083082B16CFE2B085DD7858BB11A37C09B78E36C79E5A2FD529353C4E2", t1, ELEMENTSOF(t1), t4, ELEMENTSOF(t4)); + check_parse_pcr_argument("12,2,3,1:sha256=0xFCE7F1083082B16CFE2B085DD7858BB11A37C09B78E36C79E5A2FD529353C4E2", t1, ELEMENTSOF(t1), t4, ELEMENTSOF(t4)); + check_parse_pcr_argument("1:sha256=0xFCE7F1083082B16CFE2B085DD7858BB11A37C09B78E36C79E5A2FD529353C4E2,2,3,12:SHA256", t1, ELEMENTSOF(t1), t4, ELEMENTSOF(t4)); + check_parse_pcr_argument("1:sha256=0xFCE7F1083082B16CFE2B085DD7858BB11A37C09B78E36C79E5A2FD529353C4E2,2,3,12", t1, ELEMENTSOF(t1), t4, ELEMENTSOF(t4)); + check_parse_pcr_argument("1:sha256=FCE7F1083082B16CFE2B085DD7858BB11A37C09B78E36C79E5A2FD529353C4E2,2:sha256,3:sha256,12:sha256", t1, ELEMENTSOF(t1), t4, ELEMENTSOF(t4)); + check_parse_pcr_argument("1:sha256=0xFCE7F1083082B16CFE2B085DD7858BB11A37C09B78E36C79E5A2FD529353C4E2,2:sha256,3:sha256,12:sha256", t1, ELEMENTSOF(t1), t4, ELEMENTSOF(t4)); + + TPM2B_DIGEST d5; + digest_init(&d5, "0F21EADB7F27377668E3C8069BE88D116491FBEE"); + Tpm2PCRValue t5[] = { + TPM2_PCR_VALUE_MAKE(1, TPM2_ALG_SHA1, d5), + TPM2_PCR_VALUE_MAKE(0, TPM2_ALG_SHA256, {}), + TPM2_PCR_VALUE_MAKE(1, TPM2_ALG_SHA256, d4), + TPM2_PCR_VALUE_MAKE(2, TPM2_ALG_SHA256, {}), + TPM2_PCR_VALUE_MAKE(3, TPM2_ALG_SHA256, {}), + TPM2_PCR_VALUE_MAKE(4, TPM2_ALG_SHA256, {}), + TPM2_PCR_VALUE_MAKE(7, TPM2_ALG_SHA256, {}), + TPM2_PCR_VALUE_MAKE(11, TPM2_ALG_SHA256, {}), + TPM2_PCR_VALUE_MAKE(12, TPM2_ALG_SHA256, {}), + TPM2_PCR_VALUE_MAKE(5, TPM2_ALG_SHA384, {}), + TPM2_PCR_VALUE_MAKE(6, TPM2_ALG_SHA512, {}), + }; + check_parse_pcr_argument("0,1:sha256=0xFCE7F1083082B16CFE2B085DD7858BB11A37C09B78E36C79E5A2FD529353C4E2,1:sha1=0F21EADB7F27377668E3C8069BE88D116491FBEE,2,3,4,7,11,12,5:sha384,6:sha512", NULL, 0, t5, ELEMENTSOF(t5)); + check_parse_pcr_argument("1:sha1=0F21EADB7F27377668E3C8069BE88D116491FBEE,6:sha512,5:sha384", t4, ELEMENTSOF(t4), t5, ELEMENTSOF(t5)); + + Tpm2PCRValue *v = NULL; + size_t n_v = 0; + assert_se(tpm2_parse_pcr_argument("1,100", &v, &n_v) < 0); + assert_se(tpm2_parse_pcr_argument("1,2=123456abc", &v, &n_v) < 0); + assert_se(tpm2_parse_pcr_argument("1,2:invalid", &v, &n_v) < 0); + assert_se(tpm2_parse_pcr_argument("1:sha1=invalid", &v, &n_v) < 0); + assert_se(v == NULL); + assert_se(n_v == 0); + + check_parse_pcr_argument_to_mask("", 0x0); + check_parse_pcr_argument_to_mask("0", 0x1); + check_parse_pcr_argument_to_mask("1", 0x2); + check_parse_pcr_argument_to_mask("0,1", 0x3); + check_parse_pcr_argument_to_mask("0+1", 0x3); + check_parse_pcr_argument_to_mask("0-1", -EINVAL); + check_parse_pcr_argument_to_mask("foo", -EINVAL); + check_parse_pcr_argument_to_mask("0,1,2", 0x7); + check_parse_pcr_argument_to_mask("0+1+2", 0x7); + check_parse_pcr_argument_to_mask("0+1,2", 0x7); + check_parse_pcr_argument_to_mask("0,1+2", 0x7); + check_parse_pcr_argument_to_mask("0,2", 0x5); + check_parse_pcr_argument_to_mask("0+2", 0x5); + check_parse_pcr_argument_to_mask("7+application-support", 0x800080); + check_parse_pcr_argument_to_mask("8+boot-loader-code", 0x110); + check_parse_pcr_argument_to_mask("7,shim-policy,4", 0x4090); + check_parse_pcr_argument_to_mask("sysexts,shim-policy+kernel-boot", 0x6800); + check_parse_pcr_argument_to_mask("sysexts,shim+kernel-boot", -EINVAL); + check_parse_pcr_argument_to_mask("sysexts+17+23", 0x822000); + check_parse_pcr_argument_to_mask("6+boot-loader-code,44", -EINVAL); + check_parse_pcr_argument_to_mask("debug+24", -EINVAL); +} + static void tpm2b_public_init(TPM2B_PUBLIC *public) { TPMT_PUBLIC tpmt = { .type = TPM2_ALG_RSA, @@ -599,7 +752,7 @@ TEST(calculate_name) { TEST(calculate_policy_auth_value) { TPM2B_DIGEST d; - digest_init_sha256(&d, "0000000000000000000000000000000000000000000000000000000000000000"); + digest_init(&d, "0000000000000000000000000000000000000000000000000000000000000000"); assert_se(tpm2_calculate_policy_auth_value(&d) == 0); assert_se(digest_check(&d, "8fcd2169ab92694e0c633f1ab772842b8241bbc20288981fc7ac1eddc1fddb0e")); assert_se(tpm2_calculate_policy_auth_value(&d) == 0); @@ -611,7 +764,7 @@ TEST(calculate_policy_authorize) { TPM2B_DIGEST d; tpm2b_public_init(&public); - digest_init_sha256(&d, "0000000000000000000000000000000000000000000000000000000000000000"); + digest_init(&d, "0000000000000000000000000000000000000000000000000000000000000000"); assert_se(tpm2_calculate_policy_authorize(&public, NULL, &d) == 0); assert_se(digest_check(&d, "95213a3784eaab04f427bc7e8851c2f1df0903be8e42428ec25dcefd907baff1")); assert_se(tpm2_calculate_policy_authorize(&public, NULL, &d) == 0); @@ -621,24 +774,24 @@ TEST(calculate_policy_authorize) { TEST(calculate_policy_pcr) { TPM2B_DIGEST d, dN[16]; - digest_init_sha256(&dN[ 0], "2124793cbbe60c3a8637d3b84a5d054e87c351e1469a285acc04755e8b204dec"); - digest_init_sha256(&dN[ 1], "bf7592f18adcfdc549fc0b94939f5069a24697f9cff4a0dca29014767b97559d"); - digest_init_sha256(&dN[ 2], "4b00cff9dee3a364979b2dc241b34568a8ad49fcf2713df259e47dff8875feed"); - digest_init_sha256(&dN[ 3], "3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969"); - digest_init_sha256(&dN[ 4], "368f85b3013041dfe203faaa364f00b07c5da7b1e5f1dbf2efb06fa6b9bd92de"); - digest_init_sha256(&dN[ 5], "c97c40369691c8e4aa78fb3a52655cd193b780a838b8e23f5f476576919db5e5"); - digest_init_sha256(&dN[ 6], "3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969"); - digest_init_sha256(&dN[ 7], "aa1154c9e0a774854ccbed4c8ce7e9b906b3d700a1a8db1772d0341a62dbe51b"); - digest_init_sha256(&dN[ 8], "cfde439a2c06af3479ca6bdc60429b90553d65300c5cfcc40004a08c6b5ad81a"); - digest_init_sha256(&dN[ 9], "9c2bac22ef5ec84fcdb71c3ebf776cba1247e5da980e5ee08e45666a2edf0b8b"); - digest_init_sha256(&dN[10], "9885873f4d7348199ad286f8f2476d4f866940950f6f9fb9f945ed352dbdcbd2"); - digest_init_sha256(&dN[11], "42400ab950d21aa79d12cc4fdef67d1087a39ad64900619831c0974dbae54e44"); - digest_init_sha256(&dN[12], "767d064382e56ca1ad3bdcc6bc596112e6c2008b593d3570d24c2bfa64c4628c"); - digest_init_sha256(&dN[13], "30c16133175959408c9745d8dafadef5daf4b39cb2be04df0d60089bd46d3cc4"); - digest_init_sha256(&dN[14], "e3991b7ddd47be7e92726a832d6874c5349b52b789fa0db8b558c69fea29574e"); - digest_init_sha256(&dN[15], "852dae3ecb992bdeb13d6002fefeeffdd90feca8b378d56681ef2c885d0e5137"); + digest_init(&dN[ 0], "2124793cbbe60c3a8637d3b84a5d054e87c351e1469a285acc04755e8b204dec"); + digest_init(&dN[ 1], "bf7592f18adcfdc549fc0b94939f5069a24697f9cff4a0dca29014767b97559d"); + digest_init(&dN[ 2], "4b00cff9dee3a364979b2dc241b34568a8ad49fcf2713df259e47dff8875feed"); + digest_init(&dN[ 3], "3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969"); + digest_init(&dN[ 4], "368f85b3013041dfe203faaa364f00b07c5da7b1e5f1dbf2efb06fa6b9bd92de"); + digest_init(&dN[ 5], "c97c40369691c8e4aa78fb3a52655cd193b780a838b8e23f5f476576919db5e5"); + digest_init(&dN[ 6], "3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969"); + digest_init(&dN[ 7], "aa1154c9e0a774854ccbed4c8ce7e9b906b3d700a1a8db1772d0341a62dbe51b"); + digest_init(&dN[ 8], "cfde439a2c06af3479ca6bdc60429b90553d65300c5cfcc40004a08c6b5ad81a"); + digest_init(&dN[ 9], "9c2bac22ef5ec84fcdb71c3ebf776cba1247e5da980e5ee08e45666a2edf0b8b"); + digest_init(&dN[10], "9885873f4d7348199ad286f8f2476d4f866940950f6f9fb9f945ed352dbdcbd2"); + digest_init(&dN[11], "42400ab950d21aa79d12cc4fdef67d1087a39ad64900619831c0974dbae54e44"); + digest_init(&dN[12], "767d064382e56ca1ad3bdcc6bc596112e6c2008b593d3570d24c2bfa64c4628c"); + digest_init(&dN[13], "30c16133175959408c9745d8dafadef5daf4b39cb2be04df0d60089bd46d3cc4"); + digest_init(&dN[14], "e3991b7ddd47be7e92726a832d6874c5349b52b789fa0db8b558c69fea29574e"); + digest_init(&dN[15], "852dae3ecb992bdeb13d6002fefeeffdd90feca8b378d56681ef2c885d0e5137"); - digest_init_sha256(&d, "0000000000000000000000000000000000000000000000000000000000000000"); + digest_init(&d, "0000000000000000000000000000000000000000000000000000000000000000"); Tpm2PCRValue v1[] = { TPM2_PCR_VALUE_MAKE(4, TPM2_ALG_SHA256, dN[4]), TPM2_PCR_VALUE_MAKE(7, TPM2_ALG_SHA256, dN[7]), @@ -649,7 +802,7 @@ TEST(calculate_policy_pcr) { assert_se(tpm2_calculate_policy_pcr(v1, ELEMENTSOF(v1), &d) == 0); assert_se(digest_check(&d, "97e64bcabb64c1fa4b726528644926c8029f5b4458b0575c98c04fe225629a0b")); - digest_init_sha256(&d, "0000000000000000000000000000000000000000000000000000000000000000"); + digest_init(&d, "0000000000000000000000000000000000000000000000000000000000000000"); Tpm2PCRValue v2[] = { TPM2_PCR_VALUE_MAKE( 0, TPM2_ALG_SHA256, dN[ 0]), TPM2_PCR_VALUE_MAKE( 1, TPM2_ALG_SHA256, dN[ 1]), From 53b91e1981993f49e079d977e2ac651eaac5cc5a Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Wed, 19 Jul 2023 07:49:07 -0400 Subject: [PATCH 10/20] tpm2: add TPM2B_*_MAKE(), TPM2B_*_CHECK_SIZE() macros The tpm2-tss library has many structs with only an array and size; these macros make it easy to assign to these structs. --- src/boot/measure.c | 15 +++-------- src/shared/tpm2-util.c | 17 +++++++------ src/shared/tpm2-util.h | 56 ++++++++++++++++++++++++++++++++++++++++++ src/test/test-tpm2.c | 7 ++---- 4 files changed, 72 insertions(+), 23 deletions(-) diff --git a/src/boot/measure.c b/src/boot/measure.c index 4dd039877c..518c41f65c 100644 --- a/src/boot/measure.c +++ b/src/boot/measure.c @@ -831,18 +831,11 @@ static int verb_sign(int argc, char *argv[], void *userdata) { if (tpmalg < 0) return log_error_errno(tpmalg, "Unsupported PCR bank"); - TPM2B_DIGEST pcr_digest = { - .size = p->value_size, - }; - assert(sizeof(pcr_digest.buffer) >= p->value_size); - memcpy_safe(pcr_digest.buffer, p->value, p->value_size); + Tpm2PCRValue pcr_value = TPM2_PCR_VALUE_MAKE(TPM_PCR_INDEX_KERNEL_IMAGE, + tpmalg, + TPM2B_DIGEST_MAKE(p->value, p->value_size)); - Tpm2PCRValue pcr_value = TPM2_PCR_VALUE_MAKE(TPM_PCR_INDEX_KERNEL_IMAGE, tpmalg, pcr_digest); - - TPM2B_DIGEST pcr_policy_digest; - r = tpm2_digest_init(TPM2_ALG_SHA256, &pcr_policy_digest); - if (r < 0) - return r; + TPM2B_DIGEST pcr_policy_digest = TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE); r = tpm2_calculate_policy_pcr(&pcr_value, 1, &pcr_policy_digest); if (r < 0) diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index 70c3f0a8e4..ba4baa54f8 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -1749,9 +1749,11 @@ int tpm2_pcr_value_from_string(const char *arg, Tpm2PCRValue *ret_pcr_value) { if (r < 0) return log_error_errno(r, "Invalid pcr hash value '%s': %m", p); - pcr_value.value.size = buf_size; - assert(sizeof(pcr_value.value.buffer) >= pcr_value.value.size); - memcpy(pcr_value.value.buffer, buf, pcr_value.value.size); + r = TPM2B_DIGEST_CHECK_SIZE(buf_size); + if (r < 0) + return log_error_errno(r, "PCR hash value size %zu too large.", buf_size); + + pcr_value.value = TPM2B_DIGEST_MAKE(buf, buf_size); } *ret_pcr_value = pcr_value; @@ -3424,16 +3426,17 @@ static int tpm2_policy_authorize( if (r < 0) return r; + r = TPM2B_PUBLIC_KEY_RSA_CHECK_SIZE(signature_size); + if (r < 0) + return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Signature larger than buffer."); + TPMT_SIGNATURE policy_signature = { .sigAlg = TPM2_ALG_RSASSA, .signature.rsassa = { .hash = TPM2_ALG_SHA256, - .sig.size = signature_size, + .sig = TPM2B_PUBLIC_KEY_RSA_MAKE(signature_raw, signature_size), }, }; - if (signature_size > sizeof(policy_signature.signature.rsassa.sig.buffer)) - return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Signature larger than buffer."); - memcpy(policy_signature.signature.rsassa.sig.buffer, signature_raw, signature_size); rc = sym_Esys_VerifySignature( c->esys_context, diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h index e44aa14106..90b9282780 100644 --- a/src/shared/tpm2-util.h +++ b/src/shared/tpm2-util.h @@ -153,6 +153,62 @@ int tpm2_calculate_policy_pcr(const Tpm2PCRValue *pcr_values, size_t n_pcr_value int tpm2_seal(const char *device, uint32_t hash_pcr_mask, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, const char *pin, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, void **ret_pcr_hash, size_t *ret_pcr_hash_size, uint16_t *ret_pcr_bank, uint16_t *ret_primary_alg, void **ret_srk_buf, size_t *ret_srk_buf_size); int tpm2_unseal(const char *device, uint32_t hash_pcr_mask, uint16_t pcr_bank, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, JsonVariant *signature, const char *pin, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, const void *srk_buf, size_t srk_buf_size, void **ret_secret, size_t *ret_secret_size); +/* The tpm2-tss library has many structs that are simply a combination of an array (or object) and + * size. These macros allow easily initializing or assigning instances of such structs from an existing + * buffer/object and size, while also checking the size for safety with the struct buffer/object size. If the + * provided buffer/object is NULL, the resulting struct's buffer/object will be 0s. If the provided size is + * larger than the struct's buffer/object size, this results in assertion failure; to check the size, use one + * of the TPM2B_*_CHECK_SIZE() macros. */ +#define TPM2B_AUTH_MAKE(b, s) TPM2B_BUF_SIZE_STRUCT_MAKE(b, s, TPM2B_AUTH, buffer, size) +#define TPM2B_DATA_MAKE(b, s) TPM2B_BUF_SIZE_STRUCT_MAKE(b, s, TPM2B_DATA, buffer, size) +#define TPM2B_DIGEST_MAKE(b, s) TPM2B_BUF_SIZE_STRUCT_MAKE(b, s, TPM2B_DIGEST, buffer, size) +#define TPM2B_ECC_PARAMETER_MAKE(b, s) TPM2B_BUF_SIZE_STRUCT_MAKE(b, s, TPM2B_ECC_PARAMETER, buffer, size) +#define TPM2B_ENCRYPTED_SECRET_MAKE(b, s) TPM2B_BUF_SIZE_STRUCT_MAKE(b, s, TPM2B_ENCRYPTED_SECRET, secret, size) +#define TPM2B_MAX_BUFFER_MAKE(b, s) TPM2B_BUF_SIZE_STRUCT_MAKE(b, s, TPM2B_MAX_BUFFER, buffer, size) +#define TPM2B_NAME_MAKE(b, s) TPM2B_BUF_SIZE_STRUCT_MAKE(b, s, TPM2B_NAME, name, size) +#define TPM2B_PRIVATE_MAKE(b, s) TPM2B_BUF_SIZE_STRUCT_MAKE(b, s, TPM2B_PRIVATE, buffer, size) +#define TPM2B_PRIVATE_KEY_RSA_MAKE(b, s) TPM2B_BUF_SIZE_STRUCT_MAKE(b, s, TPM2B_PRIVATE_KEY_RSA, buffer, size) +#define TPM2B_PUBLIC_KEY_RSA_MAKE(b, s) TPM2B_BUF_SIZE_STRUCT_MAKE(b, s, TPM2B_PUBLIC_KEY_RSA, buffer, size) +#define TPM2B_SENSITIVE_DATA_MAKE(b, s) TPM2B_BUF_SIZE_STRUCT_MAKE(b, s, TPM2B_SENSITIVE_DATA, buffer, size) +#define TPM2B_BUF_SIZE_STRUCT_MAKE(buf, size, struct_type, buffer_field, size_field) \ + _TPM2B_BUF_SIZE_STRUCT_MAKE(buf, size, UNIQ, struct_type, buffer_field, size_field) +#define _TPM2B_BUF_SIZE_STRUCT_MAKE(buf, size, uniq, struct_type, buffer_field, size_field) \ + ({ \ + typeof(buf) UNIQ_T(BUF, uniq) = (buf); \ + typeof(size) UNIQ_T(SIZE, uniq) = (size); \ + struct_type UNIQ_T(STRUCT, uniq) = { .size_field = UNIQ_T(SIZE, uniq), }; \ + assert(sizeof(UNIQ_T(STRUCT, uniq).buffer_field) >= (size_t) UNIQ_T(SIZE, uniq)); \ + if (UNIQ_T(BUF, uniq)) \ + memcpy(UNIQ_T(STRUCT, uniq).buffer_field, UNIQ_T(BUF, uniq), UNIQ_T(SIZE, uniq)); \ + UNIQ_T(STRUCT, uniq); \ + }) + +/* Check if the size will fit in the TPM2B struct buffer. Returns 0 if the size will fit, otherwise this logs + * a debug message and returns < 0. */ +#define TPM2B_AUTH_CHECK_SIZE(s) TPM2B_BUF_SIZE_STRUCT_CHECK_SIZE(s, TPM2B_AUTH, buffer) +#define TPM2B_DATA_CHECK_SIZE(s) TPM2B_BUF_SIZE_STRUCT_CHECK_SIZE(s, TPM2B_DATA, buffer) +#define TPM2B_DIGEST_CHECK_SIZE(s) TPM2B_BUF_SIZE_STRUCT_CHECK_SIZE(s, TPM2B_DIGEST, buffer) +#define TPM2B_ECC_PARAMETER_CHECK_SIZE(s) TPM2B_BUF_SIZE_STRUCT_CHECK_SIZE(s, TPM2B_ECC_PARAMETER, buffer) +#define TPM2B_ENCRYPTED_SECRET_CHECK_SIZE(s) TPM2B_BUF_SIZE_STRUCT_CHECK_SIZE(s, TPM2B_ENCRYPTED_SECRET, buffer) +#define TPM2B_MAX_BUFFER_CHECK_SIZE(s) TPM2B_BUF_SIZE_STRUCT_CHECK_SIZE(s, TPM2B_MAX_BUFFER, buffer) +#define TPM2B_NAME_CHECK_SIZE(s) TPM2B_BUF_SIZE_STRUCT_CHECK_SIZE(s, TPM2B_NAME, name) +#define TPM2B_PRIVATE_CHECK_SIZE(s) TPM2B_BUF_SIZE_STRUCT_CHECK_SIZE(s, TPM2B_PRIVATE, buffer) +#define TPM2B_PRIVATE_KEY_RSA_CHECK_SIZE(s) TPM2B_BUF_SIZE_STRUCT_CHECK_SIZE(s, TPM2B_PRIVATE_KEY_RSA, buffer) +#define TPM2B_PUBLIC_KEY_RSA_CHECK_SIZE(s) TPM2B_BUF_SIZE_STRUCT_CHECK_SIZE(s, TPM2B_PUBLIC_KEY_RSA, buffer) +#define TPM2B_SENSITIVE_DATA_CHECK_SIZE(s) TPM2B_BUF_SIZE_STRUCT_CHECK_SIZE(s, TPM2B_SENSITIVE_DATA, buffer) +#define TPM2B_BUF_SIZE_STRUCT_CHECK_SIZE(size, struct_type, buffer_field) \ + _TPM2B_BUF_SIZE_STRUCT_CHECK_SIZE(size, UNIQ, struct_type, buffer_field) +#define _TPM2B_BUF_SIZE_STRUCT_CHECK_SIZE(size, uniq, struct_type, buffer_field) \ + ({ \ + size_t UNIQ_T(SIZE, uniq) = (size_t) (size); \ + size_t UNIQ_T(BUFSIZE, uniq) = sizeof_field(struct_type, buffer_field); \ + UNIQ_T(BUFSIZE, uniq) < UNIQ_T(SIZE, uniq) ? \ + log_debug_errno(SYNTHETIC_ERRNO(EINVAL), \ + "Size %zu larger than " #struct_type " buffer size %zu.", \ + UNIQ_T(SIZE, uniq), UNIQ_T(BUFSIZE, uniq)) : \ + 0; \ + }) + #else /* HAVE_TPM2 */ typedef struct {} Tpm2Context; typedef struct {} Tpm2Handle; diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c index 2b285fab47..331770b527 100644 --- a/src/test/test-tpm2.c +++ b/src/test/test-tpm2.c @@ -444,8 +444,7 @@ static void digest_init(TPM2B_DIGEST *digest, const char *hash) { /* Make sure the length matches a known hash algorithm */ assert_se(IN_SET(s, TPM2_SHA1_DIGEST_SIZE, TPM2_SHA256_DIGEST_SIZE, TPM2_SHA384_DIGEST_SIZE, TPM2_SHA512_DIGEST_SIZE)); - memcpy_safe(digest->buffer, h, s); - digest->size = s; + *digest = TPM2B_DIGEST_MAKE(h, s); assert_se(digest_check(digest, hash)); } @@ -726,9 +725,7 @@ static void tpm2b_public_init(TPM2B_PUBLIC *public) { _cleanup_free_ void *mem = NULL; size_t len = 0; assert_se(unhexmem(key, strlen(key), &mem, &len) == 0); - assert_se(len <= sizeof(tpmt.unique.rsa.buffer)); - memcpy_safe(tpmt.unique.rsa.buffer, mem, len); - tpmt.unique.rsa.size = len; + tpmt.unique.rsa = TPM2B_PUBLIC_KEY_RSA_MAKE(mem, len); public->publicArea = tpmt; } From b4a6fcd5c5cf95dde2d08769a86ff0d3f907b974 Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Tue, 18 Jul 2023 12:56:25 -0400 Subject: [PATCH 11/20] tpm2: add tpm2_pcr_read_missing_values() Add function to read all unset values in an array of Tpm2PCRValue entries. Also publish tpm2_pcr_read() in header. --- src/shared/tpm2-util.c | 68 ++++++++++++++++++++++++++++++++++++++++-- src/shared/tpm2-util.h | 4 +++ 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index ba4baa54f8..ffc1acbb38 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -2251,7 +2251,7 @@ int tpm2_create_loaded( * exactly what is in the provided selection, but the TPM may ignore some selected PCRs (for example, if an * unimplemented PCR index is requested), in which case those PCRs will be absent from the provided pcr * values. */ -static int tpm2_pcr_read( +int tpm2_pcr_read( Tpm2Context *c, const TPML_PCR_SELECTION *pcr_selection, Tpm2PCRValue **ret_pcr_values, @@ -2324,6 +2324,70 @@ static int tpm2_pcr_read( return 0; } +/* Read the PCR value for each TPM2PCRValue entry in the array that does not have a value set. If all entries + * have an unset hash (i.e. hash == 0), this first detects the "best" PCR bank to use; otherwise, all entries + * must have a valid hash set. All entries must have a valid index. If this cannot read a PCR value for all + * appropriate entries, this returns an error. This does not check the array for validity. */ +int tpm2_pcr_read_missing_values(Tpm2Context *c, Tpm2PCRValue *pcr_values, size_t n_pcr_values) { + TPMI_ALG_HASH pcr_bank = 0; + int r; + + assert(c); + assert(pcr_values || n_pcr_values == 0); + + if (n_pcr_values > 0) { + size_t hash_count; + r = tpm2_pcr_values_hash_count(pcr_values, n_pcr_values, &hash_count); + if (r < 0) + return log_error_errno(r, "Could not get hash count from pcr values: %m"); + + if (hash_count == 1 && pcr_values[0].hash == 0) { + uint32_t mask; + r = tpm2_pcr_values_to_mask(pcr_values, n_pcr_values, 0, &mask); + if (r < 0) + return r; + + r = tpm2_get_best_pcr_bank(c, mask, &pcr_bank); + if (r < 0) + return r; + } + } + + for (size_t i = 0; i < n_pcr_values; i++) { + Tpm2PCRValue *v = &pcr_values[i]; + + if (v->hash == 0) + v->hash = pcr_bank; + + if (v->value.size > 0) + continue; + + TPML_PCR_SELECTION selection; + r = tpm2_tpml_pcr_selection_from_pcr_values(v, 1, &selection, NULL, NULL); + if (r < 0) + return r; + + _cleanup_free_ Tpm2PCRValue *read_values = NULL; + size_t n_read_values; + r = tpm2_pcr_read(c, &selection, &read_values, &n_read_values); + if (r < 0) + return r; + + if (n_read_values == 0) + return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), + "Could not read PCR hash 0x%" PRIu16 " index %u", + v->hash, v->index); + + assert(n_read_values == 1); + assert(read_values[0].hash == v->hash); + assert(read_values[0].index == v->index); + + v->value = read_values[0].value; + } + + return 0; +} + static int tpm2_pcr_mask_good( Tpm2Context *c, TPMI_ALG_HASH bank, @@ -2385,7 +2449,7 @@ static int tpm2_bank_has24(const TPMS_PCR_SELECTION *selection) { return valid; } -static int tpm2_get_best_pcr_bank( +int tpm2_get_best_pcr_bank( Tpm2Context *c, uint32_t pcr_mask, TPMI_ALG_HASH *ret) { diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h index 90b9282780..1b08da18ff 100644 --- a/src/shared/tpm2-util.h +++ b/src/shared/tpm2-util.h @@ -99,6 +99,7 @@ bool tpm2_test_parms(Tpm2Context *c, TPMI_ALG_PUBLIC alg, const TPMU_PUBLIC_PARM int tpm2_get_good_pcr_banks(Tpm2Context *c, uint32_t pcr_mask, TPMI_ALG_HASH **ret_banks); int tpm2_get_good_pcr_banks_strv(Tpm2Context *c, uint32_t pcr_mask, char ***ret); +int tpm2_get_best_pcr_bank(Tpm2Context *c, uint32_t pcr_mask, TPMI_ALG_HASH *ret); int tpm2_extend_bytes(Tpm2Context *c, char **banks, unsigned pcr_index, const void *data, size_t data_size, const void *secret, size_t secret_size); @@ -145,6 +146,9 @@ void tpm2_log_debug_buffer(const void *buffer, size_t size, const char *msg); void tpm2_log_debug_digest(const TPM2B_DIGEST *digest, const char *msg); void tpm2_log_debug_name(const TPM2B_NAME *name, const char *msg); +int tpm2_pcr_read(Tpm2Context *c, const TPML_PCR_SELECTION *pcr_selection, Tpm2PCRValue **ret_pcr_values, size_t *ret_n_pcr_values); +int tpm2_pcr_read_missing_values(Tpm2Context *c, Tpm2PCRValue *pcr_values, size_t n_pcr_values); + int tpm2_calculate_name(const TPMT_PUBLIC *public, TPM2B_NAME *ret_name); int tpm2_calculate_policy_auth_value(TPM2B_DIGEST *digest); int tpm2_calculate_policy_authorize(const TPM2B_PUBLIC *public, const TPM2B_DIGEST *policy_ref, TPM2B_DIGEST *digest); From 4af788c70c985b6b87435a90594e2a301929fb5b Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Tue, 18 Jul 2023 22:48:34 -0400 Subject: [PATCH 12/20] openssl: add openssl_pkey_from_pem() Add function to create EVP_PKEY from PEM buffer. --- src/shared/openssl-util.c | 19 +++++++++++++++++++ src/shared/openssl-util.h | 2 ++ 2 files changed, 21 insertions(+) diff --git a/src/shared/openssl-util.c b/src/shared/openssl-util.c index c7fcbd9ea4..9021d91077 100644 --- a/src/shared/openssl-util.c +++ b/src/shared/openssl-util.c @@ -1,10 +1,29 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include "fd-util.h" #include "openssl-util.h" #include "alloc-util.h" #include "hexdecoct.h" #if HAVE_OPENSSL +int openssl_pkey_from_pem(const void *pem, size_t pem_size, EVP_PKEY **ret) { + assert(pem); + assert(ret); + + _cleanup_fclose_ FILE *f = NULL; + f = fmemopen((void*) pem, pem_size, "r"); + if (!f) + return log_oom_debug(); + + _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = PEM_read_PUBKEY(f, NULL, NULL, NULL); + if (!pkey) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to parse PEM."); + + *ret = TAKE_PTR(pkey); + + return 0; +} + int openssl_hash(const EVP_MD *alg, const void *msg, size_t msg_len, diff --git a/src/shared/openssl-util.h b/src/shared/openssl-util.h index 4fa0a95966..231bcc2bf8 100644 --- a/src/shared/openssl-util.h +++ b/src/shared/openssl-util.h @@ -43,6 +43,8 @@ static inline void sk_X509_free_allp(STACK_OF(X509) **sk) { sk_X509_pop_free(*sk, X509_free); } +int openssl_pkey_from_pem(const void *pem, size_t pem_size, EVP_PKEY **ret); + int openssl_hash(const EVP_MD *alg, const void *msg, size_t msg_len, uint8_t *ret_hash, size_t *ret_hash_len); int rsa_encrypt_bytes(EVP_PKEY *pkey, const void *decrypted_key, size_t decrypted_key_size, void **ret_encrypt_key, size_t *ret_encrypt_key_size); From dcec950ca1c122a3e02798f9501db459cb97552f Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Tue, 27 Jun 2023 14:53:46 -0400 Subject: [PATCH 13/20] openssl: add rsa_pkey_new(), rsa_pkey_from_n_e(), rsa_pkey_to_n_e() Add function to generate an EVP_PKEY for a specific 'n' and 'e', and function to get 'n' and 'e' values from existing RSA public key. Also add a function to generate a new RSA key with a specified number of bits. --- src/resolve/resolved-dns-dnssec.c | 2 +- src/shared/openssl-util.c | 143 ++++++++++++++++++++++++++++++ src/shared/openssl-util.h | 14 +++ 3 files changed, 158 insertions(+), 1 deletion(-) diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index 01f79331b9..2580c2333c 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -14,7 +14,7 @@ #include "sort-util.h" #include "string-table.h" -#if PREFER_OPENSSL +#if PREFER_OPENSSL && OPENSSL_VERSION_MAJOR >= 3 # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wdeprecated-declarations" DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(RSA*, RSA_free, NULL); diff --git a/src/shared/openssl-util.c b/src/shared/openssl-util.c index 9021d91077..c02440495d 100644 --- a/src/shared/openssl-util.c +++ b/src/shared/openssl-util.c @@ -128,6 +128,149 @@ int rsa_pkey_to_suitable_key_size( return 0; } +/* Generate RSA public key from provided "n" and "e" values. Note that if "e" is a number (e.g. uint32_t), it + * must be provided here big-endian, e.g. wrap it with htobe32(). */ +int rsa_pkey_from_n_e(const void *n, size_t n_size, const void *e, size_t e_size, EVP_PKEY **ret) { + _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL; + + assert(n); + assert(e); + assert(ret); + + _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL); + if (!ctx) + return log_oom_debug(); + + _cleanup_(BN_freep) BIGNUM *bn_n = BN_bin2bn(n, n_size, NULL); + if (!bn_n) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to create BIGNUM for RSA n."); + + _cleanup_(BN_freep) BIGNUM *bn_e = BN_bin2bn(e, e_size, NULL); + if (!bn_e) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to create BIGNUM for RSA e."); + +#if OPENSSL_VERSION_MAJOR >= 3 + if (EVP_PKEY_fromdata_init(ctx) <= 0) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to initialize EVP_PKEY_CTX."); + + _cleanup_(OSSL_PARAM_BLD_freep) OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new(); + if (!bld) + return log_oom_debug(); + + if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_N, bn_n)) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set RSA OSSL_PKEY_PARAM_RSA_N."); + + if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_E, bn_e)) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set RSA OSSL_PKEY_PARAM_RSA_E."); + + _cleanup_(OSSL_PARAM_freep) OSSL_PARAM *params = OSSL_PARAM_BLD_to_param(bld); + if (!params) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to build RSA OSSL_PARAM."); + + if (EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params) <= 0) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to create RSA EVP_PKEY."); +#else + _cleanup_(RSA_freep) RSA *rsa_key = RSA_new(); + if (!rsa_key) + return log_oom_debug(); + + if (!RSA_set0_key(rsa_key, bn_n, bn_e, NULL)) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set RSA n/e."); + /* rsa_key owns these now, don't free */ + TAKE_PTR(bn_n); + TAKE_PTR(bn_e); + + pkey = EVP_PKEY_new(); + if (!pkey) + return log_oom_debug(); + + if (!EVP_PKEY_assign_RSA(pkey, rsa_key)) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to assign RSA key."); + /* pkey owns this now, don't free */ + TAKE_PTR(rsa_key); +#endif + + *ret = TAKE_PTR(pkey); + + return 0; +} + +/* Get the "n" and "e" values from the pkey. The values are returned in "bin" format, i.e. BN_bn2bin(). */ +int rsa_pkey_to_n_e( + const EVP_PKEY *pkey, + void **ret_n, + size_t *ret_n_size, + void **ret_e, + size_t *ret_e_size) { + + assert(pkey); + assert(ret_n); + assert(ret_n_size); + assert(ret_e); + assert(ret_e_size); + +#if OPENSSL_VERSION_MAJOR >= 3 + _cleanup_(BN_freep) BIGNUM *bn_n = NULL; + if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_N, &bn_n)) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to get RSA n."); + + _cleanup_(BN_freep) BIGNUM *bn_e = NULL; + if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_E, &bn_e)) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to get RSA e."); +#else + const RSA *rsa = EVP_PKEY_get0_RSA((EVP_PKEY*) pkey); + if (!rsa) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), + "Failed to get RSA key from public key."); + + const BIGNUM *bn_n = RSA_get0_n(rsa); + if (!bn_n) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to get RSA n."); + + const BIGNUM *bn_e = RSA_get0_e(rsa); + if (!bn_e) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to get RSA e."); +#endif + + size_t n_size = BN_num_bytes(bn_n), e_size = BN_num_bytes(bn_e); + _cleanup_free_ void *n = malloc(n_size), *e = malloc(e_size); + if (!n || !e) + return log_oom_debug(); + + assert(BN_bn2bin(bn_n, n) == (int) n_size); + assert(BN_bn2bin(bn_e, e) == (int) e_size); + + *ret_n = TAKE_PTR(n); + *ret_n_size = n_size; + *ret_e = TAKE_PTR(e); + *ret_e_size = e_size; + + return 0; +} + +/* Generate a new RSA key with the specified number of bits. */ +int rsa_pkey_new(size_t bits, EVP_PKEY **ret) { + assert(ret); + + _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL); + if (!ctx) + return log_oom_debug(); + + if (EVP_PKEY_keygen_init(ctx) <= 0) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to initialize EVP_PKEY_CTX."); + + if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, (int) bits) <= 0) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set RSA bits to %zu.", bits); + + _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL; + if (EVP_PKEY_keygen(ctx, &pkey) <= 0) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to generate ECC key."); + + *ret = TAKE_PTR(pkey); + + return 0; +} + int pubkey_fingerprint(EVP_PKEY *pk, const EVP_MD *md, void **ret, size_t *ret_size) { _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX* m = NULL; _cleanup_free_ void *d = NULL, *h = NULL; diff --git a/src/shared/openssl-util.h b/src/shared/openssl-util.h index 231bcc2bf8..b9aacfd276 100644 --- a/src/shared/openssl-util.h +++ b/src/shared/openssl-util.h @@ -21,6 +21,7 @@ # endif # if OPENSSL_VERSION_MAJOR >= 3 # include +# include # endif DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(X509_NAME*, X509_NAME_free, NULL); @@ -35,6 +36,13 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(PKCS7*, PKCS7_free, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(SSL*, SSL_free, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(BIO*, BIO_free, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_MD_CTX*, EVP_MD_CTX_free, NULL); +#if OPENSSL_VERSION_MAJOR >= 3 +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(OSSL_PARAM*, OSSL_PARAM_free, NULL); +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(OSSL_PARAM_BLD*, OSSL_PARAM_BLD_free, NULL); +#else +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EC_KEY*, EC_KEY_free, NULL); +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(RSA*, RSA_free, NULL); +#endif static inline void sk_X509_free_allp(STACK_OF(X509) **sk) { if (!sk || !*sk) @@ -51,6 +59,12 @@ int rsa_encrypt_bytes(EVP_PKEY *pkey, const void *decrypted_key, size_t decrypte int rsa_pkey_to_suitable_key_size(EVP_PKEY *pkey, size_t *ret_suitable_key_size); +int rsa_pkey_new(size_t bits, EVP_PKEY **ret); + +int rsa_pkey_from_n_e(const void *n, size_t n_size, const void *e, size_t e_size, EVP_PKEY **ret); + +int rsa_pkey_to_n_e(const EVP_PKEY *pkey, void **ret_n, size_t *ret_n_size, void **ret_e, size_t *ret_e_size); + int pubkey_fingerprint(EVP_PKEY *pk, const EVP_MD *md, void **ret, size_t *ret_size); #else From 900e73f80e87df2295faabd66f66d42c973d8ad6 Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Tue, 4 Jul 2023 18:52:59 -0400 Subject: [PATCH 14/20] openssl: add ecc_pkey_new(), ecc_pkey_from_curve_x_y(), ecc_pkey_to_curve_x_y() Add function to create openssl pkey from ECC curve and point, and function to get curve id and x/y point from existing ECC pkey. Also add function to create new ECC key for specified curve. Also add DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_MACRO() to handle case when func() is a macro, not a function symbol; specifically in this case it is used for OPENSSL_free() which is a macro. --- src/basic/macro.h | 9 ++ src/shared/openssl-util.c | 193 ++++++++++++++++++++++++++++++++++++++ src/shared/openssl-util.h | 8 ++ 3 files changed, 210 insertions(+) diff --git a/src/basic/macro.h b/src/basic/macro.h index 13620a60bc..d9df3b7365 100644 --- a/src/basic/macro.h +++ b/src/basic/macro.h @@ -335,6 +335,15 @@ static inline int __coverity_check_and_return__(int condition) { } \ } +/* When func() doesn't return the appropriate type, and is also a macro, set variable to empty afterwards. */ +#define DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_MACRO(type, func, empty) \ + static inline void func##p(type *p) { \ + if (*p != (empty)) { \ + func(*p); \ + *p = (empty); \ + } \ + } + #define _DEFINE_TRIVIAL_REF_FUNC(type, name, scope) \ scope type *name##_ref(type *p) { \ if (!p) \ diff --git a/src/shared/openssl-util.c b/src/shared/openssl-util.c index c02440495d..c3f8f91eb3 100644 --- a/src/shared/openssl-util.c +++ b/src/shared/openssl-util.c @@ -271,6 +271,199 @@ int rsa_pkey_new(size_t bits, EVP_PKEY **ret) { return 0; } +/* Generate ECC public key from provided curve ID and x/y points. */ +int ecc_pkey_from_curve_x_y( + int curve_id, + const void *x, + size_t x_size, + const void *y, + size_t y_size, + EVP_PKEY **ret) { + + assert(x); + assert(y); + assert(ret); + + _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); + if (!ctx) + return log_oom_debug(); + + _cleanup_(BN_freep) BIGNUM *bn_x = BN_bin2bn(x, x_size, NULL), *bn_y = BN_bin2bn(y, y_size, NULL); + if (!bn_x || !bn_y) + return log_oom_debug(); + + _cleanup_(EC_GROUP_freep) EC_GROUP *group = EC_GROUP_new_by_curve_name(curve_id); + if (!group) + return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "ECC curve id %d not supported.", curve_id); + + _cleanup_(EC_POINT_freep) EC_POINT *point = EC_POINT_new(group); + if (!point) + return log_oom_debug(); + + if (!EC_POINT_set_affine_coordinates(group, point, bn_x, bn_y, NULL)) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set ECC coordinates."); + +#if OPENSSL_VERSION_MAJOR >= 3 + if (EVP_PKEY_fromdata_init(ctx) <= 0) + return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), + "Failed to initialize EVP_PKEY_CTX."); + + _cleanup_(OSSL_PARAM_BLD_freep) OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new(); + if (!bld) + return log_oom_debug(); + + if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_GROUP_NAME, (char*) OSSL_EC_curve_nid2name(curve_id), 0)) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to add ECC OSSL_PKEY_PARAM_GROUP_NAME."); + + _cleanup_(OPENSSL_freep) void *pbuf = NULL; + size_t pbuf_len = 0; + pbuf_len = EC_POINT_point2buf(group, point, POINT_CONVERSION_UNCOMPRESSED, (unsigned char**) &pbuf, NULL); + if (pbuf_len == 0) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to convert ECC point to buffer."); + + if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY, pbuf, pbuf_len)) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to add ECC OSSL_PKEY_PARAM_PUB_KEY."); + + _cleanup_(OSSL_PARAM_freep) OSSL_PARAM *params = OSSL_PARAM_BLD_to_param(bld); + if (!params) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to build ECC OSSL_PARAM."); + + _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL; + if (EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params) <= 0) + return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "Failed to create ECC EVP_PKEY."); +#else + _cleanup_(EC_KEY_freep) EC_KEY *eckey = EC_KEY_new(); + if (!eckey) + return log_oom_debug(); + + if (!EC_KEY_set_group(eckey, group)) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set ECC group."); + + if (!EC_KEY_set_public_key(eckey, point)) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set ECC point."); + + _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = EVP_PKEY_new(); + if (!pkey) + return log_oom_debug(); + + if (!EVP_PKEY_assign_EC_KEY(pkey, eckey)) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to assign ECC key."); + /* pkey owns this now, don't free */ + TAKE_PTR(eckey); +#endif + + *ret = TAKE_PTR(pkey); + + return 0; +} + +int ecc_pkey_to_curve_x_y( + const EVP_PKEY *pkey, + int *ret_curve_id, + void **ret_x, + size_t *ret_x_size, + void **ret_y, + size_t *ret_y_size) { + + _cleanup_(BN_freep) BIGNUM *bn_x = NULL, *bn_y = NULL; + int curve_id; + + assert(pkey); + +#if OPENSSL_VERSION_MAJOR >= 3 + size_t name_size; + if (!EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0, &name_size)) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get ECC group name size."); + + _cleanup_free_ char *name = malloc(name_size + 1); + if (!name) + return log_oom_debug(); + + if (!EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME, name, name_size + 1, NULL)) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get ECC group name."); + + curve_id = OBJ_sn2nid(name); + if (curve_id == NID_undef) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get ECC curve id."); + + if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_X, &bn_x)) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get ECC point x."); + + if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_Y, &bn_y)) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get ECC point y."); +#else + const EC_KEY *eckey = EVP_PKEY_get0_EC_KEY((EVP_PKEY*) pkey); + if (!eckey) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get EC_KEY."); + + const EC_GROUP *group = EC_KEY_get0_group(eckey); + if (!group) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get EC_GROUP."); + + curve_id = EC_GROUP_get_curve_name(group); + if (curve_id == NID_undef) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get ECC curve id."); + + const EC_POINT *point = EC_KEY_get0_public_key(eckey); + if (!point) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get EC_POINT."); + + bn_x = BN_new(); + bn_y = BN_new(); + if (!bn_x || !bn_y) + return log_oom_debug(); + + if (!EC_POINT_get_affine_coordinates(group, point, bn_x, bn_y, NULL)) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get ECC x/y."); +#endif + + size_t x_size = BN_num_bytes(bn_x), y_size = BN_num_bytes(bn_y); + _cleanup_free_ void *x = malloc(x_size), *y = malloc(y_size); + if (!x || !y) + return log_oom_debug(); + + assert(BN_bn2bin(bn_x, x) == (int) x_size); + assert(BN_bn2bin(bn_y, y) == (int) y_size); + + if (ret_curve_id) + *ret_curve_id = curve_id; + if (ret_x) + *ret_x = TAKE_PTR(x); + if (ret_x_size) + *ret_x_size = x_size; + if (ret_y) + *ret_y = TAKE_PTR(y); + if (ret_y_size) + *ret_y_size = y_size; + + return 0; +} + +/* Generate a new ECC key for the specified ECC curve id. */ +int ecc_pkey_new(int curve_id, EVP_PKEY **ret) { + assert(ret); + + _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); + if (!ctx) + return log_oom_debug(); + + if (EVP_PKEY_keygen_init(ctx) <= 0) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to initialize EVP_PKEY_CTX."); + + if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, curve_id) <= 0) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set ECC curve %d.", curve_id); + + _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL; + if (EVP_PKEY_keygen(ctx, &pkey) <= 0) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to generate ECC key."); + + *ret = TAKE_PTR(pkey); + + return 0; +} + int pubkey_fingerprint(EVP_PKEY *pk, const EVP_MD *md, void **ret, size_t *ret_size) { _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX* m = NULL; _cleanup_free_ void *d = NULL, *h = NULL; diff --git a/src/shared/openssl-util.h b/src/shared/openssl-util.h index b9aacfd276..90158f589b 100644 --- a/src/shared/openssl-util.h +++ b/src/shared/openssl-util.h @@ -9,6 +9,7 @@ #if HAVE_OPENSSL # include # include +# include # include # include # include @@ -24,6 +25,7 @@ # include # endif +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_MACRO(void*, OPENSSL_free, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(X509_NAME*, X509_NAME_free, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_PKEY_CTX*, EVP_PKEY_CTX_free, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_CIPHER_CTX*, EVP_CIPHER_CTX_free, NULL); @@ -65,6 +67,12 @@ int rsa_pkey_from_n_e(const void *n, size_t n_size, const void *e, size_t e_size int rsa_pkey_to_n_e(const EVP_PKEY *pkey, void **ret_n, size_t *ret_n_size, void **ret_e, size_t *ret_e_size); +int ecc_pkey_from_curve_x_y(int curve_id, const void *x, size_t x_size, const void *y, size_t y_size, EVP_PKEY **ret); + +int ecc_pkey_to_curve_x_y(const EVP_PKEY *pkey, int *ret_curve_id, void **ret_x, size_t *ret_x_size, void **ret_y, size_t *ret_y_size); + +int ecc_pkey_new(int curve_id, EVP_PKEY **ret); + int pubkey_fingerprint(EVP_PKEY *pk, const EVP_MD *md, void **ret, size_t *ret_size); #else From 0fdcfa7812d88fb40b0cd5496e2f96d77b42efb4 Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Thu, 6 Jul 2023 11:21:29 -0400 Subject: [PATCH 15/20] test: add DEFINE_HEX_PTR() helper function Use function in test-tpm2 to convert hex strings to void* and len vars. --- src/shared/tests.h | 6 ++++++ src/test/test-tpm2.c | 25 ++++++++----------------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/shared/tests.h b/src/shared/tests.h index 6b87026661..27467fabb6 100644 --- a/src/shared/tests.h +++ b/src/shared/tests.h @@ -39,6 +39,12 @@ bool have_namespaces(void); #define CAN_MEMLOCK_SIZE (512 * 1024U) bool can_memlock(void); +/* Define void* buffer and size_t length variables from a hex string. */ +#define DEFINE_HEX_PTR(name, hex) \ + _cleanup_free_ void *name = NULL; \ + size_t name##_len = 0; \ + assert_se(unhexmem(hex, strlen(hex), &name, &name##_len) >= 0); + #define TEST_REQ_RUNNING_SYSTEMD(x) \ if (sd_booted() > 0) { \ x; \ diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c index 331770b527..8e760e6bd2 100644 --- a/src/test/test-tpm2.c +++ b/src/test/test-tpm2.c @@ -434,17 +434,14 @@ static bool digest_check(const TPM2B_DIGEST *digest, const char *expect) { } static void digest_init(TPM2B_DIGEST *digest, const char *hash) { - _cleanup_free_ void *h = NULL; - size_t s = 0; - assert_se(strlen(hash) <= sizeof(digest->buffer) * 2); - assert_se(unhexmem(hash, strlen(hash), &h, &s) == 0); + DEFINE_HEX_PTR(h, hash); /* Make sure the length matches a known hash algorithm */ - assert_se(IN_SET(s, TPM2_SHA1_DIGEST_SIZE, TPM2_SHA256_DIGEST_SIZE, TPM2_SHA384_DIGEST_SIZE, TPM2_SHA512_DIGEST_SIZE)); + assert_se(IN_SET(h_len, TPM2_SHA1_DIGEST_SIZE, TPM2_SHA256_DIGEST_SIZE, TPM2_SHA384_DIGEST_SIZE, TPM2_SHA512_DIGEST_SIZE)); - *digest = TPM2B_DIGEST_MAKE(h, s); + *digest = TPM2B_DIGEST_MAKE(h, h_len); assert_se(digest_check(digest, hash)); } @@ -721,11 +718,8 @@ static void tpm2b_public_init(TPM2B_PUBLIC *public) { }, }; - const char *key = "9ec7341c52093ac40a1965a5df10432513c539adcf905e30577ab6ebc88ffe53cd08cef12ed9bec6125432f4fada3629b8b96d31b8f507aa35029188fe396da823fcb236027f7fbb01b0da3d87be7f999390449ced604bdf7e26c48657cc0671000f1147da195c3861c96642e54427cb7a11572e07567ec3fd6316978abc4bd92b27bb0a0e4958e599804eeb41d682b3b7fc1f960209f80a4fb8a1b64abfd96bf5d554e73cdd6ad1c8becb4fcf5e8f0c3e621d210e5e2f308f6520ad9a966779231b99f06c5989e5a23a9415c8808ab89ce81117632e2f8461cd4428bded40979236aeadafe8de3f51660a45e1dbc87694e6a36360201cca3ff9e7263e712727"; - _cleanup_free_ void *mem = NULL; - size_t len = 0; - assert_se(unhexmem(key, strlen(key), &mem, &len) == 0); - tpmt.unique.rsa = TPM2B_PUBLIC_KEY_RSA_MAKE(mem, len); + DEFINE_HEX_PTR(key, "9ec7341c52093ac40a1965a5df10432513c539adcf905e30577ab6ebc88ffe53cd08cef12ed9bec6125432f4fada3629b8b96d31b8f507aa35029188fe396da823fcb236027f7fbb01b0da3d87be7f999390449ced604bdf7e26c48657cc0671000f1147da195c3861c96642e54427cb7a11572e07567ec3fd6316978abc4bd92b27bb0a0e4958e599804eeb41d682b3b7fc1f960209f80a4fb8a1b64abfd96bf5d554e73cdd6ad1c8becb4fcf5e8f0c3e621d210e5e2f308f6520ad9a966779231b99f06c5989e5a23a9415c8808ab89ce81117632e2f8461cd4428bded40979236aeadafe8de3f51660a45e1dbc87694e6a36360201cca3ff9e7263e712727"); + tpmt.unique.rsa = TPM2B_PUBLIC_KEY_RSA_MAKE(key, key_len); public->publicArea = tpmt; } @@ -738,12 +732,9 @@ TEST(calculate_name) { assert_se(tpm2_calculate_name(&public.publicArea, &name) == 0); assert_se(name.size == SHA256_DIGEST_SIZE + 2); - const char *expect = "000be78f74a470dd92e979ca067cdb2293a35f075e8560b436bd2ccea5da21486a07"; - _cleanup_free_ char *h = hexmem(name.name, name.size); - assert_se(h); - - assert_se(strlen(expect) == strlen(h)); - assert_se(streq(expect, h)); + DEFINE_HEX_PTR(e, "000be78f74a470dd92e979ca067cdb2293a35f075e8560b436bd2ccea5da21486a07"); + assert_se(name.size == e_len); + assert_se(memcmp(name.name, e, e_len) == 0); } TEST(calculate_policy_auth_value) { From cffeee92bbcbd9954a7d3fc909b43d8cc33ff6e3 Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Thu, 6 Jul 2023 11:21:29 -0400 Subject: [PATCH 16/20] openssl: add test-openssl Add openssl unit tests. --- src/test/meson.build | 5 +++ src/test/test-openssl.c | 97 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 src/test/test-openssl.c diff --git a/src/test/meson.build b/src/test/meson.build index 0f7dc8f9d2..0f63a2ee5d 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -353,6 +353,11 @@ executables += [ 'dependencies' : libdl, 'conditions' : ['ENABLE_NSS'], }, + test_template + { + 'sources' : files('test-openssl.c'), + 'dependencies' : libopenssl, + 'conditions' : ['HAVE_OPENSSL'], + }, test_template + { 'sources' : files('test-parse-util.c'), 'dependencies' : libm, diff --git a/src/test/test-openssl.c b/src/test/test-openssl.c new file mode 100644 index 0000000000..7672c8959d --- /dev/null +++ b/src/test/test-openssl.c @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "hexdecoct.h" +#include "openssl-util.h" +#include "tests.h" + +TEST(openssl_pkey_from_pem) { + DEFINE_HEX_PTR(key_ecc, "2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a30444151634451674145726a6e4575424c73496c3972687068777976584e50686a346a426e500a44586e794a304b395579724e6764365335413532542b6f5376746b436a365a726c34685847337741515558706f426c532b7448717452714c35513d3d0a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d0a"); + _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey_ecc = NULL; + assert_se(openssl_pkey_from_pem(key_ecc, key_ecc_len, &pkey_ecc) >= 0); + + _cleanup_free_ void *x = NULL, *y = NULL; + size_t x_len, y_len; + int curve_id; + assert_se(ecc_pkey_to_curve_x_y(pkey_ecc, &curve_id, &x, &x_len, &y, &y_len) >= 0); + assert_se(curve_id == NID_X9_62_prime256v1); + + DEFINE_HEX_PTR(expected_x, "ae39c4b812ec225f6b869870caf5cd3e18f88c19cf0d79f22742bd532acd81de"); + assert_se(x_len == expected_x_len); + assert_se(memcmp(x, expected_x, x_len) == 0); + + DEFINE_HEX_PTR(expected_y, "92e40e764fea12bed9028fa66b9788571b7c004145e9a01952fad1eab51a8be5"); + assert_se(y_len == expected_y_len); + assert_se(memcmp(y, expected_y, y_len) == 0); + + DEFINE_HEX_PTR(key_rsa, "2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d494942496a414e42676b71686b6947397730424151454641414f43415138414d49494243674b4341514541795639434950652f505852337a436f63787045300a6a575262546c3568585844436b472f584b79374b6d2f4439584942334b734f5a31436a5937375571372f674359363170697838697552756a73413464503165380a593445336c68556d374a332b6473766b626f4b64553243626d52494c2f6675627771694c4d587a41673342575278747234547545443533527a373634554650640a307a70304b68775231496230444c67772f344e67566f314146763378784b4d6478774d45683567676b73733038326332706c354a504e32587677426f744e6b4d0a5471526c745a4a35355244436170696e7153334577376675646c4e735851357746766c7432377a7637344b585165616d704c59433037584f6761304c676c536b0a79754774586b6a50542f735542544a705374615769674d5a6f714b7479563463515a58436b4a52684459614c47587673504233687a766d5671636e6b47654e540a65774944415141420a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d0a"); + _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey_rsa = NULL; + assert_se(openssl_pkey_from_pem(key_rsa, key_rsa_len, &pkey_rsa) >= 0); + + _cleanup_free_ void *n = NULL, *e = NULL; + size_t n_len, e_len; + assert_se(rsa_pkey_to_n_e(pkey_rsa, &n, &n_len, &e, &e_len) >= 0); + + DEFINE_HEX_PTR(expected_n, "c95f4220f7bf3d7477cc2a1cc691348d645b4e5e615d70c2906fd72b2eca9bf0fd5c80772ac399d428d8efb52aeff80263ad698b1f22b91ba3b00e1d3f57bc638137961526ec9dfe76cbe46e829d53609b99120bfdfb9bc2a88b317cc0837056471b6be13b840f9dd1cfbeb85053ddd33a742a1c11d486f40cb830ff8360568d4016fdf1c4a31dc7030487982092cb34f36736a65e493cdd97bf0068b4d90c4ea465b59279e510c26a98a7a92dc4c3b7ee76536c5d0e7016f96ddbbcefef829741e6a6a4b602d3b5ce81ad0b8254a4cae1ad5e48cf4ffb140532694ad6968a0319a2a2adc95e1c4195c29094610d868b197bec3c1de1cef995a9c9e419e3537b"); + assert_se(n_len == expected_n_len); + assert_se(memcmp(n, expected_n, n_len) == 0); + + DEFINE_HEX_PTR(expected_e, "010001"); + assert_se(e_len == expected_e_len); + assert_se(memcmp(e, expected_e, e_len) == 0); +} + +TEST(rsa_pkey_n_e) { + DEFINE_HEX_PTR(n, "e3975a2124a7c9fe57752d106314ff62f6da731632eac221f1c0255bdcf2a34eeb21e3ab89ba8759ddad3b68be99463c7f03f3d004028a35e6f7c6596aeab2558d490f1e1c38aed2ff796bda8d6d55704eefb6ac55842dd6e606bb707f66acc02f0db2aed0dabab885bd0c850f1bdc8ac4b6bc1f74858db8ca2ab57a3d4217c091e9cd78727a2e36b8126ea629e81fecc69b0bea601000a6c0b749c5be16f53f4fa9f208a581d804234eb6526ba3fee9822d58d1ab9cac2761d7f630eb7ad6054dff0856d41aea219e1adfd87256aa1532202a070f4b1044e718d1f38bbc5a4b1fcb024f04afaafda5edeacfdf0d0bdf35c359acd059e3edb5024e588458f9b5"); + uint32_t e = htobe32(0x10001); + + _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL; + assert_se(rsa_pkey_from_n_e(n, n_len, &e, sizeof(e), &pkey) >= 0); + + _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new((EVP_PKEY*) pkey, NULL); + assert_se(ctx); + assert_se(EVP_PKEY_verify_init(ctx) == 1); + + const char *msg = "this is a secret"; + DEFINE_HEX_PTR(sig, "14b53e0c6ad99a350c3d7811e8160f4ae03ad159815bb91bddb9735b833588df2eac221fbd3fc4ece0dd63bfaeddfdaf4ae67021e759f3638bc194836413414f54e8c4d01c9c37fa4488ea2ef772276b8a33822a53c97b1c35acfb4bc621cfb8fad88f0cf7d5491f05236886afbf9ed47f9469536482f50f74a20defa59d99676bed62a17b5eb98641df5a2f8080fa4b24f2749cc152fa65ba34c14022fcb27f1b36f52021950d7b9b6c3042c50b84cfb7d55a5f9235bfd58e1bf1f604eb93416c5fb5fd90cb68f1270dfa9daf67f52c604f62c2f2beee5e7e672b0e6e9833dd43dba99b77668540c850c9a81a5ea7aaf6297383e6135bd64572362333121fc7"); + assert_se(EVP_PKEY_verify(ctx, sig, sig_len, (unsigned char*) msg, strlen(msg)) == 1); + + DEFINE_HEX_PTR(invalid_sig, "1234"); + assert_se(EVP_PKEY_verify(ctx, invalid_sig, invalid_sig_len, (unsigned char*) msg, strlen(msg)) != 1); + + _cleanup_free_ void *n2 = NULL, *e2 = NULL; + size_t n2_size, e2_size; + assert_se(rsa_pkey_to_n_e(pkey, &n2, &n2_size, &e2, &e2_size) >= 0); + assert_se(memcmp_nn(n, n_len, n2, n2_size) == 0); + assert_se(e2_size <= sizeof(uint32_t)); + assert_se(memcmp(&((uint8_t*) &e)[sizeof(uint32_t) - e2_size], e2, e2_size) == 0); +} + +TEST(ecc_pkey_curve_x_y) { + int curveid = NID_X9_62_prime256v1; + DEFINE_HEX_PTR(x, "2830d2c8f65d3efbef12303b968b91692f8bd04045dcb8a9656374e4ae61d818"); + DEFINE_HEX_PTR(y, "8a80750f76729defdcc2a4bc1a91c22e60109dd6e1ffde634a650a20bab172e9"); + + _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL; + assert_se(ecc_pkey_from_curve_x_y(curveid, x, x_len, y, y_len, &pkey) >= 0); + + _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new((EVP_PKEY*) pkey, NULL); + assert_se(ctx); + assert_se(EVP_PKEY_verify_init(ctx) == 1); + + const char *msg = "this is a secret"; + DEFINE_HEX_PTR(sig, "3045022100f6ca10f7ed57a020679899b26dd5ac5a1079265885e2a6477f527b6a3f02b5ca02207b550eb3e7b69360aff977f7f6afac99c3f28266b6c5338ce373f6b59263000a"); + assert_se(EVP_PKEY_verify(ctx, sig, sig_len, (unsigned char*) msg, strlen(msg)) == 1); + + DEFINE_HEX_PTR(invalid_sig, "1234"); + assert_se(EVP_PKEY_verify(ctx, invalid_sig, invalid_sig_len, (unsigned char*) msg, strlen(msg)) != 1); + + _cleanup_free_ void *x2 = NULL, *y2 = NULL; + size_t x2_size, y2_size; + int curveid2; + assert_se(ecc_pkey_to_curve_x_y(pkey, &curveid2, &x2, &x2_size, &y2, &y2_size) >= 0); + assert_se(curveid == curveid2); + assert_se(memcmp_nn(x, x_len, x2, x2_size) == 0); + assert_se(memcmp_nn(y, y_len, y2, y2_size) == 0); +} + +DEFINE_TEST_MAIN(LOG_DEBUG); From e3acb4d24c68291376b11bea5787112978e2775f Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Tue, 27 Jun 2023 15:03:08 -0400 Subject: [PATCH 17/20] tpm2: add functions to convert TPM2B_PUBLIC to/from openssl pkey or PEM Add functions to convert a PEM or pkey to TPM2B_PUBLIC, and functions to convert TPM2B_PUBLIC to pkey or fingerprint. Supports both RSA and ECC keys. Add ECC support to some test-tpm2 tests, and tests to cover the newly added functions. --- src/shared/tpm2-util.c | 347 ++++++++++++++++++++++++++--------------- src/shared/tpm2-util.h | 9 ++ src/test/meson.build | 5 +- src/test/test-tpm2.c | 153 ++++++++++++++++-- 4 files changed, 375 insertions(+), 139 deletions(-) diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index ffc1acbb38..6b995fd272 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -19,7 +19,6 @@ #include "logarithm.h" #include "memory-util.h" #include "nulstr-util.h" -#include "openssl-util.h" #include "parse-util.h" #include "random-util.h" #include "sha256.h" @@ -2908,124 +2907,6 @@ static int tpm2_make_policy_session( return 0; } -static int openssl_pubkey_to_tpm2_pubkey( - const void *pubkey, - size_t pubkey_size, - TPM2B_PUBLIC *output, - void **ret_fp, - size_t *ret_fp_size) { - -#if HAVE_OPENSSL -#if OPENSSL_VERSION_MAJOR >= 3 - _cleanup_(BN_freep) BIGNUM *n = NULL, *e = NULL; -#else - const BIGNUM *n = NULL, *e = NULL; - const RSA *rsa = NULL; -#endif - int r, n_bytes, e_bytes; - - assert(pubkey); - assert(pubkey_size > 0); - assert(output); - - /* Converts an OpenSSL public key to a structure that the TPM chip can process. */ - - _cleanup_fclose_ FILE *f = NULL; - f = fmemopen((void*) pubkey, pubkey_size, "r"); - if (!f) - return log_oom(); - - _cleanup_(EVP_PKEY_freep) EVP_PKEY *input = NULL; - input = PEM_read_PUBKEY(f, NULL, NULL, NULL); - if (!input) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse PEM public key."); - - if (EVP_PKEY_base_id(input) != EVP_PKEY_RSA) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Provided public key is not an RSA key."); - -#if OPENSSL_VERSION_MAJOR >= 3 - if (!EVP_PKEY_get_bn_param(input, OSSL_PKEY_PARAM_RSA_N, &n)) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to get RSA modulus from public key."); -#else - rsa = EVP_PKEY_get0_RSA(input); - if (!rsa) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to extract RSA key from public key."); - - n = RSA_get0_n(rsa); - if (!n) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to get RSA modulus from public key."); -#endif - - n_bytes = BN_num_bytes(n); - assert_se(n_bytes > 0); - if ((size_t) n_bytes > sizeof_field(TPM2B_PUBLIC, publicArea.unique.rsa.buffer)) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "RSA modulus too large for TPM2 public key object."); - -#if OPENSSL_VERSION_MAJOR >= 3 - if (!EVP_PKEY_get_bn_param(input, OSSL_PKEY_PARAM_RSA_E, &e)) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to get RSA exponent from public key."); -#else - e = RSA_get0_e(rsa); - if (!e) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to get RSA exponent from public key."); -#endif - - e_bytes = BN_num_bytes(e); - assert_se(e_bytes > 0); - if ((size_t) e_bytes > sizeof_field(TPM2B_PUBLIC, publicArea.parameters.rsaDetail.exponent)) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "RSA exponent too large for TPM2 public key object."); - - *output = (TPM2B_PUBLIC) { - .size = sizeof(TPMT_PUBLIC), - .publicArea = { - .type = TPM2_ALG_RSA, - .nameAlg = TPM2_ALG_SHA256, - .objectAttributes = TPMA_OBJECT_DECRYPT | TPMA_OBJECT_SIGN_ENCRYPT | TPMA_OBJECT_USERWITHAUTH, - .parameters.rsaDetail = { - .scheme = { - .scheme = TPM2_ALG_NULL, - .details.anySig.hashAlg = TPM2_ALG_NULL, - }, - .symmetric = { - .algorithm = TPM2_ALG_NULL, - .mode.sym = TPM2_ALG_NULL, - }, - .keyBits = n_bytes * 8, - /* .exponent will be filled in below. */ - }, - .unique = { - .rsa.size = n_bytes, - /* .rsa.buffer will be filled in below. */ - }, - }, - }; - - if (BN_bn2bin(n, output->publicArea.unique.rsa.buffer) <= 0) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to convert RSA modulus."); - - if (BN_bn2bin(e, (unsigned char*) &output->publicArea.parameters.rsaDetail.exponent) <= 0) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to convert RSA exponent."); - - if (ret_fp) { - _cleanup_free_ void *fp = NULL; - size_t fp_size; - - assert(ret_fp_size); - - r = pubkey_fingerprint(input, EVP_sha256(), &fp, &fp_size); - if (r < 0) - return log_error_errno(r, "Failed to calculate public key fingerprint: %m"); - - *ret_fp = TAKE_PTR(fp); - *ret_fp_size = fp_size; - } - - return 0; -#else /* HAVE_OPENSSL */ - return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL support is disabled."); -#endif -} - static int find_signature( JsonVariant *v, const TPML_PCR_SELECTION *pcr_selection, @@ -3635,6 +3516,212 @@ static int tpm2_build_sealing_policy( return 0; } +#if HAVE_OPENSSL +static int tpm2_ecc_curve_from_openssl_curve_id(int curve_id, TPM2_ECC_CURVE *ret) { + assert(ret); + + switch (curve_id) { + case NID_X9_62_prime192v1: *ret = TPM2_ECC_NIST_P192; return 0; + case NID_secp224r1: *ret = TPM2_ECC_NIST_P192; return 0; + case NID_X9_62_prime256v1: *ret = TPM2_ECC_NIST_P256; return 0; + case NID_secp384r1: *ret = TPM2_ECC_NIST_P384; return 0; + case NID_secp521r1: *ret = TPM2_ECC_NIST_P521; return 0; + case NID_sm2: *ret = TPM2_ECC_SM2_P256; return 0; + } + + return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "Openssl ECC curve id %d not supported.", curve_id); +} + +static int tpm2_ecc_curve_to_openssl_curve_id(TPM2_ECC_CURVE curve, int *ret) { + assert(ret); + + switch (curve) { + case TPM2_ECC_NIST_P192: *ret = NID_X9_62_prime192v1; return 0; + case TPM2_ECC_NIST_P224: *ret = NID_secp224r1; return 0; + case TPM2_ECC_NIST_P256: *ret = NID_X9_62_prime256v1; return 0; + case TPM2_ECC_NIST_P384: *ret = NID_secp384r1; return 0; + case TPM2_ECC_NIST_P521: *ret = NID_secp521r1; return 0; + case TPM2_ECC_SM2_P256: *ret = NID_sm2; return 0; + } + + return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "TPM2 ECC curve %u not supported.", curve); +} + +#define TPM2_RSA_DEFAULT_EXPONENT UINT32_C(0x10001) + +int tpm2_tpm2b_public_to_openssl_pkey(const TPM2B_PUBLIC *public, EVP_PKEY **ret) { + int r; + + assert(public); + assert(ret); + + const TPMT_PUBLIC *p = &public->publicArea; + if (p->type == TPM2_ALG_ECC) { + int curve_id; + r = tpm2_ecc_curve_to_openssl_curve_id(p->parameters.eccDetail.curveID, &curve_id); + if (r < 0) + return r; + + const TPMS_ECC_POINT *point = &p->unique.ecc; + return ecc_pkey_from_curve_x_y( + curve_id, + point->x.buffer, + point->x.size, + point->y.buffer, + point->y.size, + ret); + } + + if (p->type == TPM2_ALG_RSA) { + /* TPM specification Part 2 ("Structures") section for TPMS_RSA_PARAMS states "An exponent of + * zero indicates that the exponent is the default of 2^16 + 1". */ + uint32_t exponent = htobe32(p->parameters.rsaDetail.exponent ?: TPM2_RSA_DEFAULT_EXPONENT); + return rsa_pkey_from_n_e( + p->unique.rsa.buffer, + p->unique.rsa.size, + &exponent, + sizeof(exponent), + ret); + } + + return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "TPM2 asymmetric algorithm 0x%" PRIx16 " not supported.", p->type); +} + +int tpm2_tpm2b_public_from_openssl_pkey(const EVP_PKEY *pkey, TPM2B_PUBLIC *ret) { + int key_id, r; + + assert(pkey); + assert(ret); + + TPMT_PUBLIC public = { + .nameAlg = TPM2_ALG_SHA256, + .objectAttributes = TPMA_OBJECT_DECRYPT | TPMA_OBJECT_SIGN_ENCRYPT | TPMA_OBJECT_USERWITHAUTH, + .parameters.asymDetail = { + .symmetric.algorithm = TPM2_ALG_NULL, + .scheme.scheme = TPM2_ALG_NULL, + }, + }; + +#if OPENSSL_VERSION_MAJOR >= 3 + key_id = EVP_PKEY_get_id(pkey); +#else + key_id = EVP_PKEY_id(pkey); +#endif + + if (key_id == EVP_PKEY_EC) { + public.type = TPM2_ALG_ECC; + + int curve_id; + _cleanup_free_ void *x = NULL, *y = NULL; + size_t x_size, y_size; + r = ecc_pkey_to_curve_x_y(pkey, &curve_id, &x, &x_size, &y, &y_size); + if (r < 0) + return log_error_errno(r, "Could not get ECC key curve/x/y: %m"); + + TPM2_ECC_CURVE curve; + r = tpm2_ecc_curve_from_openssl_curve_id(curve_id, &curve); + if (r < 0) + return r; + + public.parameters.eccDetail.curveID = curve; + + public.parameters.eccDetail.kdf.scheme = TPM2_ALG_NULL; + + r = TPM2B_ECC_PARAMETER_CHECK_SIZE(x_size); + if (r < 0) + return log_error_errno(r, "ECC key x size %zu too large.", x_size); + + public.unique.ecc.x = TPM2B_ECC_PARAMETER_MAKE(x, x_size); + + r = TPM2B_ECC_PARAMETER_CHECK_SIZE(y_size); + if (r < 0) + return log_error_errno(r, "ECC key y size %zu too large.", y_size); + + public.unique.ecc.y = TPM2B_ECC_PARAMETER_MAKE(y, y_size); + } else if (key_id == EVP_PKEY_RSA) { + public.type = TPM2_ALG_RSA; + + _cleanup_free_ void *n = NULL, *e = NULL; + size_t n_size, e_size; + r = rsa_pkey_to_n_e(pkey, &n, &n_size, &e, &e_size); + if (r < 0) + return log_error_errno(r, "Could not get RSA key n/e: %m"); + + r = TPM2B_PUBLIC_KEY_RSA_CHECK_SIZE(n_size); + if (r < 0) + return log_error_errno(r, "RSA key n size %zu too large.", n_size); + + public.unique.rsa = TPM2B_PUBLIC_KEY_RSA_MAKE(n, n_size); + public.parameters.rsaDetail.keyBits = n_size * 8; + + if (sizeof(uint32_t) < e_size) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "RSA key e size %zu too large.", e_size); + + uint32_t exponent = 0; + memcpy((void*) &exponent, e, e_size); + exponent = be32toh(exponent) >> (32 - e_size * 8); + if (exponent == TPM2_RSA_DEFAULT_EXPONENT) + exponent = 0; + public.parameters.rsaDetail.exponent = exponent; + } else + return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "EVP_PKEY type %d not supported.", key_id); + + *ret = (TPM2B_PUBLIC) { + .size = sizeof(public), + .publicArea = public, + }; + + return 0; +} +#endif + +int tpm2_tpm2b_public_to_fingerprint( + const TPM2B_PUBLIC *public, + void **ret_fingerprint, + size_t *ret_fingerprint_size) { + +#if HAVE_OPENSSL + int r; + + assert(public); + assert(ret_fingerprint); + assert(ret_fingerprint_size); + + _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL; + r = tpm2_tpm2b_public_to_openssl_pkey(public, &pkey); + if (r < 0) + return r; + + /* Hardcode fingerprint to SHA256 */ + return pubkey_fingerprint(pkey, EVP_sha256(), ret_fingerprint, ret_fingerprint_size); +#else + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL support is disabled."); +#endif +} + +int tpm2_tpm2b_public_from_pem(const void *pem, size_t pem_size, TPM2B_PUBLIC *ret) { +#if HAVE_OPENSSL + int r; + + assert(pem); + assert(ret); + + _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL; + r = openssl_pkey_from_pem(pem, pem_size, &pkey); + if (r < 0) + return r; + + return tpm2_tpm2b_public_from_openssl_pkey(pkey, ret); +#else + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL support is disabled."); +#endif +} + int tpm2_seal(const char *device, uint32_t hash_pcr_mask, const void *pubkey, @@ -3712,12 +3799,11 @@ int tpm2_seal(const char *device, return r; } - TPM2B_PUBLIC pubkey_tpm2, *authorize_key = NULL; + TPM2B_PUBLIC pubkey_tpm2b; if (pubkey) { - r = openssl_pubkey_to_tpm2_pubkey(pubkey, pubkey_size, &pubkey_tpm2, NULL, NULL); + r = tpm2_tpm2b_public_from_pem(pubkey, pubkey_size, &pubkey_tpm2b); if (r < 0) - return r; - authorize_key = &pubkey_tpm2; + return log_error_errno(r, "Could not create TPMT_PUBLIC: %m"); } TPM2B_DIGEST policy_digest; @@ -3728,7 +3814,7 @@ int tpm2_seal(const char *device, r = tpm2_calculate_sealing_policy( hash_pcr_values, n_hash_pcr_values, - authorize_key, + pubkey ? &pubkey_tpm2b : NULL, !!pin, &policy_digest); if (r < 0) @@ -4011,14 +4097,17 @@ int tpm2_unseal(const char *device, if (r < 0) return r; - TPM2B_PUBLIC pubkey_tpm2, *authorize_key = NULL; + TPM2B_PUBLIC pubkey_tpm2b; _cleanup_free_ void *fp = NULL; size_t fp_size = 0; if (pubkey) { - r = openssl_pubkey_to_tpm2_pubkey(pubkey, pubkey_size, &pubkey_tpm2, &fp, &fp_size); + r = tpm2_tpm2b_public_from_pem(pubkey, pubkey_size, &pubkey_tpm2b); if (r < 0) - return r; - authorize_key = &pubkey_tpm2; + return log_error_errno(r, "Could not create TPMT_PUBLIC: %m"); + + r = tpm2_tpm2b_public_to_fingerprint(&pubkey_tpm2b, &fp, &fp_size); + if (r < 0) + return log_error_errno(r, "Could not get key fingerprint: %m"); } /* @@ -4055,7 +4144,7 @@ int tpm2_unseal(const char *device, policy_session, hash_pcr_mask, pcr_bank, - authorize_key, + pubkey ? &pubkey_tpm2b : NULL, fp, fp_size, pubkey_pcr_mask, signature, diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h index 1b08da18ff..c3bc35172a 100644 --- a/src/shared/tpm2-util.h +++ b/src/shared/tpm2-util.h @@ -7,6 +7,7 @@ #include "io-util.h" #include "json.h" #include "macro.h" +#include "openssl-util.h" #include "sha256.h" typedef enum TPM2Flags { @@ -157,6 +158,14 @@ int tpm2_calculate_policy_pcr(const Tpm2PCRValue *pcr_values, size_t n_pcr_value int tpm2_seal(const char *device, uint32_t hash_pcr_mask, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, const char *pin, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, void **ret_pcr_hash, size_t *ret_pcr_hash_size, uint16_t *ret_pcr_bank, uint16_t *ret_primary_alg, void **ret_srk_buf, size_t *ret_srk_buf_size); int tpm2_unseal(const char *device, uint32_t hash_pcr_mask, uint16_t pcr_bank, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, JsonVariant *signature, const char *pin, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, const void *srk_buf, size_t srk_buf_size, void **ret_secret, size_t *ret_secret_size); +#if HAVE_OPENSSL +int tpm2_tpm2b_public_to_openssl_pkey(const TPM2B_PUBLIC *public, EVP_PKEY **ret); +int tpm2_tpm2b_public_from_openssl_pkey(const EVP_PKEY *pkey, TPM2B_PUBLIC *ret); +#endif + +int tpm2_tpm2b_public_from_pem(const void *pem, size_t pem_size, TPM2B_PUBLIC *ret); +int tpm2_tpm2b_public_to_fingerprint(const TPM2B_PUBLIC *public, void **ret_fingerprint, size_t *ret_fingerprint_size); + /* The tpm2-tss library has many structs that are simply a combination of an array (or object) and * size. These macros allow easily initializing or assigning instances of such structs from an existing * buffer/object and size, while also checking the size for safety with the struct buffer/object size. If the diff --git a/src/test/meson.build b/src/test/meson.build index 0f63a2ee5d..1857c9c53b 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -167,7 +167,6 @@ simple_tests += files( 'test-sysctl-util.c', 'test-terminal-util.c', 'test-tmpfile-util.c', - 'test-tpm2.c', 'test-udev-util.c', 'test-uid-alloc-range.c', 'test-uid-range.c', @@ -420,6 +419,10 @@ executables += [ 'sources' : files('test-time-util.c'), 'timeout' : 120, }, + test_template + { + 'sources' : files('test-tpm2.c'), + 'dependencies' : libopenssl, + }, test_template + { 'sources' : files('test-utmp.c'), 'conditions' : ['ENABLE_UTMP'], diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c index 8e760e6bd2..7e65396b14 100644 --- a/src/test/test-tpm2.c +++ b/src/test/test-tpm2.c @@ -702,7 +702,7 @@ TEST(parse_pcr_argument) { check_parse_pcr_argument_to_mask("debug+24", -EINVAL); } -static void tpm2b_public_init(TPM2B_PUBLIC *public) { +static void tpm2b_public_rsa_init(TPM2B_PUBLIC *public, const char *rsa_n) { TPMT_PUBLIC tpmt = { .type = TPM2_ALG_RSA, .nameAlg = TPM2_ALG_SHA256, @@ -718,23 +718,149 @@ static void tpm2b_public_init(TPM2B_PUBLIC *public) { }, }; - DEFINE_HEX_PTR(key, "9ec7341c52093ac40a1965a5df10432513c539adcf905e30577ab6ebc88ffe53cd08cef12ed9bec6125432f4fada3629b8b96d31b8f507aa35029188fe396da823fcb236027f7fbb01b0da3d87be7f999390449ced604bdf7e26c48657cc0671000f1147da195c3861c96642e54427cb7a11572e07567ec3fd6316978abc4bd92b27bb0a0e4958e599804eeb41d682b3b7fc1f960209f80a4fb8a1b64abfd96bf5d554e73cdd6ad1c8becb4fcf5e8f0c3e621d210e5e2f308f6520ad9a966779231b99f06c5989e5a23a9415c8808ab89ce81117632e2f8461cd4428bded40979236aeadafe8de3f51660a45e1dbc87694e6a36360201cca3ff9e7263e712727"); + DEFINE_HEX_PTR(key, rsa_n); tpmt.unique.rsa = TPM2B_PUBLIC_KEY_RSA_MAKE(key, key_len); + public->size = sizeof(tpmt); public->publicArea = tpmt; } +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, + }, + }; + + DEFINE_HEX_PTR(buf_x, x); + tpmt.unique.ecc.x = TPM2B_ECC_PARAMETER_MAKE(buf_x, buf_x_len); + + DEFINE_HEX_PTR(buf_y, y); + tpmt.unique.ecc.y = TPM2B_ECC_PARAMETER_MAKE(buf_y, buf_y_len); + + public->size = sizeof(tpmt); + public->publicArea = tpmt; +} + +#if HAVE_OPENSSL +TEST(tpm2b_public_to_openssl_pkey) { + DEFINE_HEX_PTR(msg, "edc64c6523778961fe9ba03ab7d624b27ca1dd5b01e7734cc6c891d50db04269"); + TPM2B_PUBLIC public; + + /* RSA */ + tpm2b_public_rsa_init(&public, "d71cff5bba2173f0434a389171048e7da8cf8409b892c62946481cc383089bc754324620967fea3d00a02a717cdda4bfe1525ad957d294b88434e0a3933e86fb40f234e4935fd2ba27eb1d21da87efa466b74eb4ad18d26059904643441cf402ee933d138a2151f40459c49d87fef59e2cb822768b2d8689a9b58f82bf9a37e70693f2b2d40dfa388d365c1b1f029a14c4fc8dadb68978ef377d20ff2ca24e7078464c705eab42f531557c9c6dc0df66b506d0c26ef604f8110c64867099267453c71871e7ed22505a09daf102afc34355209ca7680eccc0ed368d148f402fa58cbb6c9d52351f535f09e4e24ad805e149f130edaa2f5e7efed3a4d2d03adb85"); + _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey_rsa = NULL; + assert_se(tpm2_tpm2b_public_to_openssl_pkey(&public, &pkey_rsa) >= 0); + + _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx_rsa = EVP_PKEY_CTX_new((EVP_PKEY*) pkey_rsa, NULL); + assert_se(ctx_rsa); + assert_se(EVP_PKEY_verify_init(ctx_rsa) == 1); + assert_se(EVP_PKEY_CTX_set_signature_md(ctx_rsa, EVP_sha256()) > 0); + + DEFINE_HEX_PTR(sig_rsa, "9f70a9e68911be3ec464cae91126328307bf355872127e042d6c61e0a80982872c151033bcf727abfae5fc9500c923120011e7ef4aa5fc690a59a034697b6022c141b4b209e2df6f4b282288cd9181073fbe7158ce113c79d87623423c1f3996ff931e59cc91db74f8e8656215b1436fc93ddec0f1f8fa8510826e674b250f047e6cba94c95ff98072a286baca94646b577974a1e00d56c21944e38960d8ee90511a2f938e5cf1ac7b7cc7ff8e3ac001d321254d3e4f988b90e9f6f873c26ecd0a12a626b3474833cdbb9e9f793238f6c97ee5b75a1a89bb7a7858d34ecfa6d34ac58d95085e6c4fbbebd47a4364be2725c2c6b3fa15d916f3c0b62a66fe76ae"); + assert_se(EVP_PKEY_verify(ctx_rsa, sig_rsa, sig_rsa_len, (unsigned char*) msg, msg_len) == 1); + + /* ECC */ + tpm2b_public_ecc_init(&public, TPM2_ECC_NIST_P256, "6fc0ecf3645c673ab7e86d1ec5b315afb950257c5f68ab23296160006711fac2", "8dd2ef7a2c9ecede91493ba98c8fb3f893aff325c6a1e0f752c657b2d6ca1413"); + _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey_ecc = NULL; + assert_se(tpm2_tpm2b_public_to_openssl_pkey(&public, &pkey_ecc) >= 0); + + _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx_ecc = EVP_PKEY_CTX_new((EVP_PKEY*) pkey_ecc, NULL); + assert_se(ctx_ecc); + assert_se(EVP_PKEY_verify_init(ctx_ecc) == 1); + + DEFINE_HEX_PTR(sig_ecc, "304602210092447ac0b5b32e90923f79bb4aba864b9c546a9900cf193a83243d35d189a2110221009a8b4df1dfa85e225eff9c606694d4d205a7a3968c9552f50bc2790209a90001"); + assert_se(EVP_PKEY_verify(ctx_ecc, sig_ecc, sig_ecc_len, (unsigned char*) msg, msg_len) == 1); +} + +static void get_tpm2b_public_from_pem(const void *pem, size_t pem_size, TPM2B_PUBLIC *ret) { + _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL; + TPM2B_PUBLIC p1 = {}, p2 = {}; + + assert(pem); + assert(ret); + + assert_se(openssl_pkey_from_pem(pem, pem_size, &pkey) >= 0); + assert_se(tpm2_tpm2b_public_from_openssl_pkey(pkey, &p1) >= 0); + assert_se(tpm2_tpm2b_public_from_pem(pem, pem_size, &p2) >= 0); + assert_se(memcmp_nn(&p1, sizeof(p1), &p2, sizeof(p2)) == 0); + + *ret = p1; +} + +static void check_tpm2b_public_fingerprint(const TPM2B_PUBLIC *public, const char *hexfp) { + DEFINE_HEX_PTR(expected, hexfp); + _cleanup_free_ void *fp = NULL; + size_t fp_size; + + assert_se(tpm2_tpm2b_public_to_fingerprint(public, &fp, &fp_size) >= 0); + assert_se(memcmp_nn(fp, fp_size, expected, expected_len) == 0); +} + +TEST(tpm2b_public_from_openssl_pkey) { + TPM2B_PUBLIC public; + TPMT_PUBLIC *p = &public.publicArea; + + DEFINE_HEX_PTR(key_ecc, "2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a30444151634451674145726a6e4575424c73496c3972687068777976584e50686a346a426e500a44586e794a304b395579724e6764365335413532542b6f5376746b436a365a726c34685847337741515558706f426c532b7448717452714c35513d3d0a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d0a"); + get_tpm2b_public_from_pem(key_ecc, key_ecc_len, &public); + + assert_se(p->type == TPM2_ALG_ECC); + assert_se(p->parameters.eccDetail.curveID == TPM2_ECC_NIST_P256); + + DEFINE_HEX_PTR(expected_x, "ae39c4b812ec225f6b869870caf5cd3e18f88c19cf0d79f22742bd532acd81de"); + assert_se(memcmp_nn(p->unique.ecc.x.buffer, p->unique.ecc.x.size, expected_x, expected_x_len) == 0); + + DEFINE_HEX_PTR(expected_y, "92e40e764fea12bed9028fa66b9788571b7c004145e9a01952fad1eab51a8be5"); + assert_se(memcmp_nn(p->unique.ecc.y.buffer, p->unique.ecc.y.size, expected_y, expected_y_len) == 0); + + check_tpm2b_public_fingerprint(&public, "cd3373293b62a52b48c12100e80ea9bfd806266ce76893a5ec31cb128052d97c"); + + DEFINE_HEX_PTR(key_rsa, "2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d494942496a414e42676b71686b6947397730424151454641414f43415138414d49494243674b4341514541795639434950652f505852337a436f63787045300a6a575262546c3568585844436b472f584b79374b6d2f4439584942334b734f5a31436a5937375571372f674359363170697838697552756a73413464503165380a593445336c68556d374a332b6473766b626f4b64553243626d52494c2f6675627771694c4d587a41673342575278747234547545443533527a373634554650640a307a70304b68775231496230444c67772f344e67566f314146763378784b4d6478774d45683567676b73733038326332706c354a504e32587677426f744e6b4d0a5471526c745a4a35355244436170696e7153334577376675646c4e735851357746766c7432377a7637344b585165616d704c59433037584f6761304c676c536b0a79754774586b6a50542f735542544a705374615769674d5a6f714b7479563463515a58436b4a52684459614c47587673504233687a766d5671636e6b47654e540a65774944415141420a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d0a"); + get_tpm2b_public_from_pem(key_rsa, key_rsa_len, &public); + + DEFINE_HEX_PTR(expected_n, "c95f4220f7bf3d7477cc2a1cc691348d645b4e5e615d70c2906fd72b2eca9bf0fd5c80772ac399d428d8efb52aeff80263ad698b1f22b91ba3b00e1d3f57bc638137961526ec9dfe76cbe46e829d53609b99120bfdfb9bc2a88b317cc0837056471b6be13b840f9dd1cfbeb85053ddd33a742a1c11d486f40cb830ff8360568d4016fdf1c4a31dc7030487982092cb34f36736a65e493cdd97bf0068b4d90c4ea465b59279e510c26a98a7a92dc4c3b7ee76536c5d0e7016f96ddbbcefef829741e6a6a4b602d3b5ce81ad0b8254a4cae1ad5e48cf4ffb140532694ad6968a0319a2a2adc95e1c4195c29094610d868b197bec3c1de1cef995a9c9e419e3537b"); + assert_se(p->unique.rsa.size == expected_n_len); + assert_se(memcmp(p->unique.rsa.buffer, expected_n, expected_n_len) == 0); + + assert_se(p->parameters.rsaDetail.keyBits == expected_n_len * 8); + + assert_se(p->parameters.rsaDetail.exponent == 0); + + check_tpm2b_public_fingerprint(&public, "d9186d13a7fd5b3644cee05448f49ad3574e82a2942ff93cf89598d36cca78a9"); +} +#endif + +static void check_name(const TPM2B_NAME *name, const char *expect) { + assert_se(name->size == SHA256_DIGEST_SIZE + 2); + + DEFINE_HEX_PTR(e, expect); + assert_se(name->size == e_len); + assert_se(memcmp(name->name, e, e_len) == 0); +} + TEST(calculate_name) { TPM2B_PUBLIC public; TPM2B_NAME name; - tpm2b_public_init(&public); - assert_se(tpm2_calculate_name(&public.publicArea, &name) == 0); - assert_se(name.size == SHA256_DIGEST_SIZE + 2); + /* RSA */ + tpm2b_public_rsa_init(&public, "9ec7341c52093ac40a1965a5df10432513c539adcf905e30577ab6ebc88ffe53cd08cef12ed9bec6125432f4fada3629b8b96d31b8f507aa35029188fe396da823fcb236027f7fbb01b0da3d87be7f999390449ced604bdf7e26c48657cc0671000f1147da195c3861c96642e54427cb7a11572e07567ec3fd6316978abc4bd92b27bb0a0e4958e599804eeb41d682b3b7fc1f960209f80a4fb8a1b64abfd96bf5d554e73cdd6ad1c8becb4fcf5e8f0c3e621d210e5e2f308f6520ad9a966779231b99f06c5989e5a23a9415c8808ab89ce81117632e2f8461cd4428bded40979236aeadafe8de3f51660a45e1dbc87694e6a36360201cca3ff9e7263e712727"); + assert_se(tpm2_calculate_name(&public.publicArea, &name) >= 0); + check_name(&name, "000be78f74a470dd92e979ca067cdb2293a35f075e8560b436bd2ccea5da21486a07"); - DEFINE_HEX_PTR(e, "000be78f74a470dd92e979ca067cdb2293a35f075e8560b436bd2ccea5da21486a07"); - assert_se(name.size == e_len); - assert_se(memcmp(name.name, e, e_len) == 0); + /* ECC */ + tpm2b_public_ecc_init(&public, TPM2_ECC_NIST_P256, "238e02ee4fd5598add6b502429f1815418515e4b0d6551c8e816b38cb15451d1", "70c2d491769775ec43ccd5a571c429233e9d30cf0f486c2e01acd6cb32ba93b6"); + assert_se(tpm2_calculate_name(&public.publicArea, &name) >= 0); + check_name(&name, "000b302787187ba19c82011c987bd2dcdbb652b3a543ccc5cb0b49c33d4caae604a6"); } TEST(calculate_policy_auth_value) { @@ -751,12 +877,21 @@ TEST(calculate_policy_authorize) { TPM2B_PUBLIC public; TPM2B_DIGEST d; - tpm2b_public_init(&public); + /* RSA */ + tpm2b_public_rsa_init(&public, "9ec7341c52093ac40a1965a5df10432513c539adcf905e30577ab6ebc88ffe53cd08cef12ed9bec6125432f4fada3629b8b96d31b8f507aa35029188fe396da823fcb236027f7fbb01b0da3d87be7f999390449ced604bdf7e26c48657cc0671000f1147da195c3861c96642e54427cb7a11572e07567ec3fd6316978abc4bd92b27bb0a0e4958e599804eeb41d682b3b7fc1f960209f80a4fb8a1b64abfd96bf5d554e73cdd6ad1c8becb4fcf5e8f0c3e621d210e5e2f308f6520ad9a966779231b99f06c5989e5a23a9415c8808ab89ce81117632e2f8461cd4428bded40979236aeadafe8de3f51660a45e1dbc87694e6a36360201cca3ff9e7263e712727"); digest_init(&d, "0000000000000000000000000000000000000000000000000000000000000000"); assert_se(tpm2_calculate_policy_authorize(&public, NULL, &d) == 0); assert_se(digest_check(&d, "95213a3784eaab04f427bc7e8851c2f1df0903be8e42428ec25dcefd907baff1")); assert_se(tpm2_calculate_policy_authorize(&public, NULL, &d) == 0); assert_se(digest_check(&d, "95213a3784eaab04f427bc7e8851c2f1df0903be8e42428ec25dcefd907baff1")); + + /* ECC */ + tpm2b_public_ecc_init(&public, TPM2_ECC_NIST_P256, "423a89da6f0998f510489ab9682706e762031ef8f9faef2a185eff67065a187e", "996f73291670cef9e303d6cd9fa19ddf2c9c1fb1e283324ca9acca07c405c8d0"); + digest_init(&d, "0000000000000000000000000000000000000000000000000000000000000000"); + assert_se(tpm2_calculate_policy_authorize(&public, NULL, &d) == 0); + assert_se(digest_check(&d, "2a5b705e83f949c27ac4d2e79e54fb5fb0a60f0b37bbd54a0ee1022ba00d3628")); + assert_se(tpm2_calculate_policy_authorize(&public, NULL, &d) == 0); + assert_se(digest_check(&d, "2a5b705e83f949c27ac4d2e79e54fb5fb0a60f0b37bbd54a0ee1022ba00d3628")); } TEST(calculate_policy_pcr) { From 9e4379945b74ee7920fe375be0bcb04d8ef53873 Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Wed, 12 Jul 2023 17:35:54 -0400 Subject: [PATCH 18/20] tpm2: move policy calculation out of tpm2_seal() Move the calculation of the sealed object policy hash out of the tpm2_seal() function. Instead, callers of tpm2_seal() can directly call tpm2_calculate_sealing_policy() and then provide the policy hash to tpm2_seal(). --- src/cryptenroll/cryptenroll-tpm2.c | 86 ++++++++++++++++++------ src/cryptenroll/cryptenroll-tpm2.h | 5 +- src/cryptenroll/cryptenroll.c | 28 +++++--- src/partition/repart.c | 89 +++++++++++++++++------- src/shared/creds-util.c | 51 ++++++++++++-- src/shared/tpm2-util.c | 104 +++++++---------------------- src/shared/tpm2-util.h | 6 +- 7 files changed, 227 insertions(+), 142 deletions(-) diff --git a/src/cryptenroll/cryptenroll-tpm2.c b/src/cryptenroll/cryptenroll-tpm2.c index 4dc3c1794d..86aed31838 100644 --- a/src/cryptenroll/cryptenroll-tpm2.c +++ b/src/cryptenroll/cryptenroll-tpm2.c @@ -133,7 +133,8 @@ int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, - uint32_t hash_pcr_mask, + Tpm2PCRValue *hash_pcr_values, + size_t n_hash_pcr_values, const char *pubkey_path, uint32_t pubkey_pcr_mask, const char *signature_path, @@ -143,9 +144,8 @@ int enroll_tpm2(struct crypt_device *cd, _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *signature_json = NULL; _cleanup_(erase_and_freep) char *base64_encoded = NULL; _cleanup_free_ void *srk_buf = NULL; - size_t secret_size, blob_size, hash_size, pubkey_size = 0, srk_buf_size = 0; - _cleanup_free_ void *blob = NULL, *hash = NULL, *pubkey = NULL; - uint16_t pcr_bank, primary_alg; + size_t secret_size, blob_size, pubkey_size = 0, srk_buf_size = 0; + _cleanup_free_ void *blob = NULL, *pubkey = NULL; const char *node; _cleanup_(erase_and_freep) char *pin_str = NULL; ssize_t base64_encoded_size; @@ -163,7 +163,7 @@ int enroll_tpm2(struct crypt_device *cd, assert(cd); assert(volume_key); assert(volume_key_size > 0); - assert(TPM2_PCR_MASK_VALID(hash_pcr_mask)); + assert(TPM2_PCR_VALUES_VALID(hash_pcr_values, n_hash_pcr_values)); assert(TPM2_PCR_MASK_VALID(pubkey_pcr_mask)); assert_se(node = crypt_get_device_name(cd)); @@ -206,23 +206,69 @@ int enroll_tpm2(struct crypt_device *cd, return log_debug_errno(r, "Failed to read TPM PCR signature: %m"); } - r = tpm2_seal(device, - hash_pcr_mask, - pubkey, pubkey_size, - pubkey_pcr_mask, + _cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL; + r = tpm2_context_new(device, &tpm2_context); + if (r < 0) + return r; + + bool pcr_value_specified = false; + for (size_t i = 0; i < n_hash_pcr_values; i++) + if (hash_pcr_values[i].value.size > 0) { + pcr_value_specified = true; + break; + } + + r = tpm2_pcr_read_missing_values(tpm2_context, hash_pcr_values, n_hash_pcr_values); + if (r < 0) + return r; + + uint16_t hash_pcr_bank = 0; + uint32_t hash_pcr_mask = 0; + if (n_hash_pcr_values > 0) { + size_t hash_count; + r = tpm2_pcr_values_hash_count(hash_pcr_values, n_hash_pcr_values, &hash_count); + if (r < 0) + return log_error_errno(r, "Could not get hash count: %m"); + + if (hash_count > 1) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Multiple PCR banks selected."); + + hash_pcr_bank = hash_pcr_values[0].hash; + r = tpm2_pcr_values_to_mask(hash_pcr_values, n_hash_pcr_values, hash_pcr_bank, &hash_pcr_mask); + if (r < 0) + return log_error_errno(r, "Could not get hash mask: %m"); + } + + TPM2B_PUBLIC public; + if (pubkey) { + r = tpm2_tpm2b_public_from_pem(pubkey, pubkey_size, &public); + if (r < 0) + return log_error_errno(r, "Could not convert public key to TPM2B_PUBLIC: %m"); + } + + TPM2B_DIGEST policy = TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE); + r = tpm2_calculate_sealing_policy( + hash_pcr_values, + n_hash_pcr_values, + pubkey ? &public : NULL, + use_pin, + &policy); + if (r < 0) + return r; + + r = tpm2_seal(tpm2_context, + &policy, pin_str, &secret, &secret_size, &blob, &blob_size, - &hash, &hash_size, - &pcr_bank, - &primary_alg, + /* ret_primary_alg= */ NULL, &srk_buf, &srk_buf_size); if (r < 0) return r; /* Let's see if we already have this specific PCR policy hash enrolled, if so, exit early. */ - r = search_policy_hash(cd, hash, hash_size); + r = search_policy_hash(cd, policy.buffer, policy.size); if (r == -ENOENT) log_debug_errno(r, "PCR policy hash not yet enrolled, enrolling now."); else if (r < 0) @@ -233,21 +279,21 @@ int enroll_tpm2(struct crypt_device *cd, } /* Quick verification that everything is in order, we are not in a hurry after all. */ - if (!pubkey || signature_json) { + if ((!pubkey || signature_json) && !pcr_value_specified) { _cleanup_(erase_and_freep) void *secret2 = NULL; size_t secret2_size; log_debug("Unsealing for verification..."); r = tpm2_unseal(device, hash_pcr_mask, - pcr_bank, + hash_pcr_bank, pubkey, pubkey_size, pubkey_pcr_mask, signature_json, pin_str, - primary_alg, + /* primary_alg= */ 0, blob, blob_size, - hash, hash_size, + policy.buffer, policy.size, srk_buf, srk_buf_size, &secret2, &secret2_size); if (r < 0) @@ -279,12 +325,12 @@ int enroll_tpm2(struct crypt_device *cd, r = tpm2_make_luks2_json( keyslot, hash_pcr_mask, - pcr_bank, + hash_pcr_bank, pubkey, pubkey_size, pubkey_pcr_mask, - primary_alg, + /* primary_alg= */ 0, blob, blob_size, - hash, hash_size, + policy.buffer, policy.size, use_pin ? binary_salt : NULL, use_pin ? sizeof(binary_salt) : 0, srk_buf, srk_buf_size, diff --git a/src/cryptenroll/cryptenroll-tpm2.h b/src/cryptenroll/cryptenroll-tpm2.h index b6e0c2808e..d43a9a8ffe 100644 --- a/src/cryptenroll/cryptenroll-tpm2.h +++ b/src/cryptenroll/cryptenroll-tpm2.h @@ -5,11 +5,12 @@ #include "cryptsetup-util.h" #include "log.h" +#include "tpm2-util.h" #if HAVE_TPM2 -int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, uint32_t hash_pcr_mask, const char *pubkey_path, uint32_t pubkey_pcr_mask, const char *signature_path, bool use_pin); +int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, Tpm2PCRValue *hash_pcrs, size_t n_hash_pcrs, const char *pubkey_path, uint32_t pubkey_pcr_mask, const char *signature_path, bool use_pin); #else -static inline int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, uint32_t hash_pcr_mask, const char *pubkey_path, uint32_t pubkey_pcr_mask, const char *signature_path, bool use_pin) { +static inline int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, Tpm2PCRValue *hash_pcrs, size_t n_hash_pcrs, const char *pubkey_path, uint32_t pubkey_pcr_mask, const char *signature_path, bool use_pin) { return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM2 key enrollment not supported."); } diff --git a/src/cryptenroll/cryptenroll.c b/src/cryptenroll/cryptenroll.c index b2e4c0a5f5..1ac2c6951e 100644 --- a/src/cryptenroll/cryptenroll.c +++ b/src/cryptenroll/cryptenroll.c @@ -28,7 +28,6 @@ #include "strv.h" #include "terminal-util.h" #include "tpm-pcr.h" -#include "tpm2-util.h" static EnrollType arg_enroll_type = _ENROLL_TYPE_INVALID; static char *arg_unlock_keyfile = NULL; @@ -37,10 +36,13 @@ static char *arg_unlock_fido2_device = NULL; static char *arg_pkcs11_token_uri = NULL; static char *arg_fido2_device = NULL; static char *arg_tpm2_device = NULL; -static uint32_t arg_tpm2_pcr_mask = UINT32_MAX; +static Tpm2PCRValue *arg_tpm2_hash_pcr_values = NULL; +static size_t arg_tpm2_n_hash_pcr_values = 0; +static bool arg_tpm2_hash_pcr_values_use_default = true; static bool arg_tpm2_pin = false; static char *arg_tpm2_public_key = NULL; -static uint32_t arg_tpm2_public_key_pcr_mask = UINT32_MAX; +static uint32_t arg_tpm2_public_key_pcr_mask = 0; +static bool arg_tpm2_public_key_pcr_mask_use_default = true; static char *arg_tpm2_signature = NULL; static char *arg_node = NULL; static int *arg_wipe_slots = NULL; @@ -61,6 +63,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_unlock_fido2_device, freep); STATIC_DESTRUCTOR_REGISTER(arg_pkcs11_token_uri, freep); STATIC_DESTRUCTOR_REGISTER(arg_fido2_device, freep); STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep); +STATIC_DESTRUCTOR_REGISTER(arg_tpm2_hash_pcr_values, freep); STATIC_DESTRUCTOR_REGISTER(arg_tpm2_public_key, freep); STATIC_DESTRUCTOR_REGISTER(arg_tpm2_signature, freep); STATIC_DESTRUCTOR_REGISTER(arg_node, freep); @@ -356,7 +359,8 @@ static int parse_argv(int argc, char *argv[]) { } case ARG_TPM2_PCRS: - r = tpm2_parse_pcr_argument_to_mask(optarg, &arg_tpm2_pcr_mask); + arg_tpm2_hash_pcr_values_use_default = false; + r = tpm2_parse_pcr_argument_append(optarg, &arg_tpm2_hash_pcr_values, &arg_tpm2_n_hash_pcr_values); if (r < 0) return r; @@ -377,6 +381,7 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_TPM2_PUBLIC_KEY_PCRS: + arg_tpm2_public_key_pcr_mask_use_default = false; r = tpm2_parse_pcr_argument_to_mask(optarg, &arg_tpm2_public_key_pcr_mask); if (r < 0) return r; @@ -476,10 +481,15 @@ static int parse_argv(int argc, char *argv[]) { if (r < 0) return r; - if (arg_tpm2_pcr_mask == UINT32_MAX) - arg_tpm2_pcr_mask = TPM2_PCR_MASK_DEFAULT; - if (arg_tpm2_public_key_pcr_mask == UINT32_MAX) - arg_tpm2_public_key_pcr_mask = UINT32_C(1) << TPM_PCR_INDEX_KERNEL_IMAGE; + if (arg_tpm2_public_key_pcr_mask_use_default && arg_tpm2_public_key) + arg_tpm2_public_key_pcr_mask = INDEX_TO_MASK(uint32_t, TPM_PCR_INDEX_KERNEL_IMAGE); + + if (arg_tpm2_hash_pcr_values_use_default && !GREEDY_REALLOC_APPEND( + arg_tpm2_hash_pcr_values, + arg_tpm2_n_hash_pcr_values, + &TPM2_PCR_VALUE_MAKE(TPM2_PCR_INDEX_DEFAULT, /* hash= */ 0, /* value= */ {}), + 1)) + return log_oom(); return 1; } @@ -655,7 +665,7 @@ static int run(int argc, char *argv[]) { break; case ENROLL_TPM2: - slot = enroll_tpm2(cd, vk, vks, arg_tpm2_device, arg_tpm2_pcr_mask, arg_tpm2_public_key, arg_tpm2_public_key_pcr_mask, arg_tpm2_signature, arg_tpm2_pin); + slot = enroll_tpm2(cd, vk, vks, arg_tpm2_device, arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values, arg_tpm2_public_key, arg_tpm2_public_key_pcr_mask, arg_tpm2_signature, arg_tpm2_pin); break; case _ENROLL_TYPE_INVALID: diff --git a/src/partition/repart.c b/src/partition/repart.c index 9e30c6bbc3..1a316012e7 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -140,9 +140,12 @@ static size_t arg_key_size = 0; static EVP_PKEY *arg_private_key = NULL; static X509 *arg_certificate = NULL; static char *arg_tpm2_device = NULL; -static uint32_t arg_tpm2_pcr_mask = UINT32_MAX; +static Tpm2PCRValue *arg_tpm2_hash_pcr_values = NULL; +static size_t arg_tpm2_n_hash_pcr_values = 0; +static bool arg_tpm2_hash_pcr_values_use_default = true; static char *arg_tpm2_public_key = NULL; -static uint32_t arg_tpm2_public_key_pcr_mask = UINT32_MAX; +static uint32_t arg_tpm2_public_key_pcr_mask = 0; +static bool arg_tpm2_public_key_pcr_mask_use_default = true; static bool arg_split = false; static GptPartitionType *arg_filter_partitions = NULL; static size_t arg_n_filter_partitions = 0; @@ -162,6 +165,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_key, erase_and_freep); STATIC_DESTRUCTOR_REGISTER(arg_private_key, EVP_PKEY_freep); STATIC_DESTRUCTOR_REGISTER(arg_certificate, X509_freep); STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep); +STATIC_DESTRUCTOR_REGISTER(arg_tpm2_hash_pcr_values, freep); STATIC_DESTRUCTOR_REGISTER(arg_tpm2_public_key, freep); STATIC_DESTRUCTOR_REGISTER(arg_filter_partitions, freep); STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep); @@ -3639,10 +3643,9 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; _cleanup_(erase_and_freep) void *secret = NULL; _cleanup_free_ void *pubkey = NULL; - _cleanup_free_ void *blob = NULL, *hash = NULL, *srk_buf = NULL; - size_t secret_size, blob_size, hash_size, pubkey_size = 0, srk_buf_size = 0; + _cleanup_free_ void *blob = NULL, *srk_buf = NULL; + size_t secret_size, blob_size, pubkey_size = 0, srk_buf_size = 0; ssize_t base64_encoded_size; - uint16_t pcr_bank, primary_alg; int keyslot; if (arg_tpm2_public_key_pcr_mask != 0) { @@ -3656,18 +3659,51 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta } } - r = tpm2_seal(arg_tpm2_device, - arg_tpm2_pcr_mask, - pubkey, pubkey_size, - arg_tpm2_public_key_pcr_mask, + _cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL; + r = tpm2_context_new(arg_tpm2_device, &tpm2_context); + if (r < 0) + return r; + + TPM2B_PUBLIC public; + if (pubkey) { + r = tpm2_tpm2b_public_from_pem(pubkey, pubkey_size, &public); + if (r < 0) + return log_error_errno(r, "Could not convert public key to TPM2B_PUBLIC: %m"); + } + + r = tpm2_pcr_read_missing_values(tpm2_context, arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values); + if (r < 0) + return r; + + uint16_t hash_pcr_bank = 0; + uint32_t hash_pcr_mask = 0; + if (arg_tpm2_n_hash_pcr_values > 0) { + size_t hash_count; + r = tpm2_pcr_values_hash_count(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values, &hash_count); + if (r < 0) + return log_error_errno(r, "Could not get hash count: %m"); + + if (hash_count > 1) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Multiple PCR banks selected."); + + hash_pcr_bank = arg_tpm2_hash_pcr_values[0].hash; + r = tpm2_pcr_values_to_mask(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values, hash_pcr_bank, &hash_pcr_mask); + if (r < 0) + return log_error_errno(r, "Could not get hash mask: %m"); + } + + TPM2B_DIGEST policy = TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE); + r = tpm2_calculate_sealing_policy(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values, &public, /* use_pin= */ false, &policy); + if (r < 0) + return r; + + r = tpm2_seal(tpm2_context, + &policy, /* pin= */ NULL, &secret, &secret_size, &blob, &blob_size, - &hash, &hash_size, - &pcr_bank, - &primary_alg, - &srk_buf, - &srk_buf_size); + /* ret_primary_alg= */ NULL, + &srk_buf, &srk_buf_size); if (r < 0) return log_error_errno(r, "Failed to seal to TPM2: %m"); @@ -3691,13 +3727,13 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta r = tpm2_make_luks2_json( keyslot, - arg_tpm2_pcr_mask, - pcr_bank, + hash_pcr_mask, + hash_pcr_bank, pubkey, pubkey_size, arg_tpm2_public_key_pcr_mask, - primary_alg, + /* primary_alg= */ 0, blob, blob_size, - hash, hash_size, + policy.buffer, policy.size, NULL, 0, /* no salt because tpm2_seal has no pin */ srk_buf, srk_buf_size, 0, @@ -6447,7 +6483,8 @@ static int parse_argv(int argc, char *argv[]) { } case ARG_TPM2_PCRS: - r = tpm2_parse_pcr_argument_to_mask(optarg, &arg_tpm2_pcr_mask); + arg_tpm2_hash_pcr_values_use_default = false; + r = tpm2_parse_pcr_argument_append(optarg, &arg_tpm2_hash_pcr_values, &arg_tpm2_n_hash_pcr_values); if (r < 0) return r; @@ -6461,6 +6498,7 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_TPM2_PUBLIC_KEY_PCRS: + arg_tpm2_public_key_pcr_mask_use_default = false; r = tpm2_parse_pcr_argument_to_mask(optarg, &arg_tpm2_public_key_pcr_mask); if (r < 0) return r; @@ -6597,10 +6635,15 @@ static int parse_argv(int argc, char *argv[]) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "A path to a loopback file must be specified when --split is used."); - if (arg_tpm2_pcr_mask == UINT32_MAX) - arg_tpm2_pcr_mask = TPM2_PCR_MASK_DEFAULT; - if (arg_tpm2_public_key_pcr_mask == UINT32_MAX) - arg_tpm2_public_key_pcr_mask = UINT32_C(1) << TPM_PCR_INDEX_KERNEL_IMAGE; + if (arg_tpm2_public_key_pcr_mask_use_default && arg_tpm2_public_key) + arg_tpm2_public_key_pcr_mask = INDEX_TO_MASK(uint32_t, TPM_PCR_INDEX_KERNEL_IMAGE); + + if (arg_tpm2_hash_pcr_values_use_default && !GREEDY_REALLOC_APPEND( + arg_tpm2_hash_pcr_values, + arg_tpm2_n_hash_pcr_values, + &TPM2_PCR_VALUE_MAKE(TPM2_PCR_INDEX_DEFAULT, /* hash= */ 0, /* value= */ {}), + 1)) + return log_oom(); if (arg_pretty < 0 && isatty(STDOUT_FILENO)) arg_pretty = true; diff --git a/src/shared/creds-util.c b/src/shared/creds-util.c index 16df01b9ca..8a5240e5f1 100644 --- a/src/shared/creds-util.c +++ b/src/shared/creds-util.c @@ -825,18 +825,49 @@ int encrypt_credential_and_warn( if (!pubkey) tpm2_pubkey_pcr_mask = 0; - r = tpm2_seal(tpm2_device, - tpm2_hash_pcr_mask, - pubkey, pubkey_size, - tpm2_pubkey_pcr_mask, + _cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL; + r = tpm2_context_new(tpm2_device, &tpm2_context); + if (r < 0) + return r; + + r = tpm2_get_best_pcr_bank(tpm2_context, tpm2_hash_pcr_mask | tpm2_pubkey_pcr_mask, &tpm2_pcr_bank); + if (r < 0) + return r; + + TPML_PCR_SELECTION tpm2_hash_pcr_selection; + tpm2_tpml_pcr_selection_from_mask(tpm2_hash_pcr_mask, tpm2_pcr_bank, &tpm2_hash_pcr_selection); + + _cleanup_free_ Tpm2PCRValue *tpm2_hash_pcr_values = NULL; + size_t tpm2_n_hash_pcr_values; + r = tpm2_pcr_read(tpm2_context, &tpm2_hash_pcr_selection, &tpm2_hash_pcr_values, &tpm2_n_hash_pcr_values); + if (r < 0) + return r; + + TPM2B_PUBLIC public; + if (pubkey) { + r = tpm2_tpm2b_public_from_pem(pubkey, pubkey_size, &public); + if (r < 0) + return log_error_errno(r, "Could not convert public key to TPM2B_PUBLIC: %m"); + } + + TPM2B_DIGEST tpm2_policy = TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE); + r = tpm2_calculate_sealing_policy( + tpm2_hash_pcr_values, + tpm2_n_hash_pcr_values, + pubkey ? &public : NULL, + /* use_pin= */ false, + &tpm2_policy); + if (r < 0) + return r; + + r = tpm2_seal(tpm2_context, + &tpm2_policy, /* pin= */ NULL, &tpm2_key, &tpm2_key_size, &tpm2_blob, &tpm2_blob_size, - &tpm2_policy_hash, &tpm2_policy_hash_size, - &tpm2_pcr_bank, &tpm2_primary_alg, /* ret_srk_buf= */ NULL, - /* ret_srk_buf_size= */ 0); + /* ret_srk_buf_size= */ NULL); if (r < 0) { if (sd_id128_equal(with_key, _CRED_AUTO_INITRD)) log_warning("TPM2 present and used, but we didn't manage to talk to it. Credential will be refused if SecureBoot is enabled."); @@ -846,6 +877,12 @@ int encrypt_credential_and_warn( log_notice_errno(r, "TPM2 sealing didn't work, continuing without TPM2: %m"); } + tpm2_policy_hash_size = tpm2_policy.size; + tpm2_policy_hash = malloc(tpm2_policy_hash_size); + if (!tpm2_policy_hash) + return log_oom(); + memcpy(tpm2_policy_hash, tpm2_policy.buffer, tpm2_policy_hash_size); + assert(tpm2_blob_size <= CREDENTIAL_FIELD_SIZE_MAX); assert(tpm2_policy_hash_size <= CREDENTIAL_FIELD_SIZE_MAX); } diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index 6b995fd272..21ee4dc8e0 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -3425,7 +3425,7 @@ static int tpm2_policy_authorize( } /* Extend 'digest' with the calculated policy hash. */ -static int tpm2_calculate_sealing_policy( +int tpm2_calculate_sealing_policy( const Tpm2PCRValue *pcr_values, size_t n_pcr_values, const TPM2B_PUBLIC *public, @@ -3722,38 +3722,25 @@ int tpm2_tpm2b_public_from_pem(const void *pem, size_t pem_size, TPM2B_PUBLIC *r #endif } -int tpm2_seal(const char *device, - uint32_t hash_pcr_mask, - const void *pubkey, - const size_t pubkey_size, - uint32_t pubkey_pcr_mask, +int tpm2_seal(Tpm2Context *c, + const TPM2B_DIGEST *policy, const char *pin, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, - void **ret_pcr_hash, - size_t *ret_pcr_hash_size, - uint16_t *ret_pcr_bank, uint16_t *ret_primary_alg, void **ret_srk_buf, size_t *ret_srk_buf_size) { + uint16_t primary_alg = 0; TSS2_RC rc; int r; - assert(pubkey || pubkey_size == 0); - assert(ret_secret); assert(ret_secret_size); assert(ret_blob); assert(ret_blob_size); - assert(ret_pcr_hash); - assert(ret_pcr_hash_size); - assert(ret_pcr_bank); - - assert(TPM2_PCR_MASK_VALID(hash_pcr_mask)); - assert(TPM2_PCR_MASK_VALID(pubkey_pcr_mask)); /* So here's what we do here: we connect to the TPM2 chip. It persistently contains a "seed" key that * is randomized when the TPM2 is first initialized or reset and remains stable across boots. We @@ -3773,53 +3760,6 @@ int tpm2_seal(const char *device, usec_t start = now(CLOCK_MONOTONIC); - _cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL; - r = tpm2_context_new(device, &c); - if (r < 0) - return r; - - TPMI_ALG_HASH pcr_bank = 0; - if (hash_pcr_mask | pubkey_pcr_mask) { - /* Some TPM2 devices only can do SHA1. Prefer SHA256 but allow SHA1. */ - r = tpm2_get_best_pcr_bank(c, hash_pcr_mask|pubkey_pcr_mask, &pcr_bank); - if (r < 0) - return r; - } - - _cleanup_free_ Tpm2PCRValue *hash_pcr_values = NULL; - size_t n_hash_pcr_values; - if (hash_pcr_mask) { - /* For now, we just read the current values from the system; we need to be able to specify - * expected values, eventually. */ - TPML_PCR_SELECTION hash_pcr_selection; - tpm2_tpml_pcr_selection_from_mask(hash_pcr_mask, pcr_bank, &hash_pcr_selection); - - r = tpm2_pcr_read(c, &hash_pcr_selection, &hash_pcr_values, &n_hash_pcr_values); - if (r < 0) - return r; - } - - TPM2B_PUBLIC pubkey_tpm2b; - if (pubkey) { - r = tpm2_tpm2b_public_from_pem(pubkey, pubkey_size, &pubkey_tpm2b); - if (r < 0) - return log_error_errno(r, "Could not create TPMT_PUBLIC: %m"); - } - - TPM2B_DIGEST policy_digest; - r = tpm2_digest_init(TPM2_ALG_SHA256, &policy_digest); - if (r < 0) - return r; - - r = tpm2_calculate_sealing_policy( - hash_pcr_values, - n_hash_pcr_values, - pubkey ? &pubkey_tpm2b : NULL, - !!pin, - &policy_digest); - if (r < 0) - return r; - /* We use a keyed hash object (i.e. HMAC) to store the secret key we want to use for unlocking the * LUKS2 volume with. We don't ever use for HMAC/keyed hash operations however, we just use it * because it's a key type that is universally supported and suitable for symmetric binary blobs. */ @@ -3829,7 +3769,7 @@ int tpm2_seal(const char *device, .objectAttributes = TPMA_OBJECT_FIXEDTPM | TPMA_OBJECT_FIXEDPARENT, .parameters.keyedHashDetail.scheme.scheme = TPM2_ALG_NULL, .unique.keyedHash.size = SHA256_DIGEST_SIZE, - .authPolicy = policy_digest, + .authPolicy = policy ? *policy : TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE), }; TPMS_SENSITIVE_CREATE hmac_sensitive = { @@ -3854,21 +3794,33 @@ int tpm2_seal(const char *device, if (r < 0) return log_error_errno(r, "Failed to generate secret key: %m"); - _cleanup_(Esys_Freep) TPM2B_PUBLIC *primary_public = NULL; _cleanup_(tpm2_handle_freep) Tpm2Handle *primary_handle = NULL; if (ret_srk_buf) { - r = tpm2_get_or_create_srk(c, NULL, &primary_public, NULL, NULL, &primary_handle); + _cleanup_(Esys_Freep) TPM2B_PUBLIC *primary_public = NULL; + r = tpm2_get_or_create_srk( + c, + /* session= */ NULL, + &primary_public, + /* ret_name= */ NULL, + /* ret_qname= */ NULL, + &primary_handle); if (r < 0) return r; + + primary_alg = primary_public->publicArea.type; } else { /* TODO: force all callers to provide ret_srk_buf, so we can stop sealing with the legacy templates. */ + primary_alg = TPM2_ALG_ECC; + TPM2B_PUBLIC template = { .size = sizeof(TPMT_PUBLIC), }; - r = tpm2_get_legacy_template(TPM2_ALG_ECC, &template.publicArea); + r = tpm2_get_legacy_template(primary_alg, &template.publicArea); if (r < 0) return log_error_errno(r, "Could not get legacy ECC template: %m"); if (!tpm2_supports_tpmt_public(c, &template.publicArea)) { - r = tpm2_get_legacy_template(TPM2_ALG_RSA, &template.publicArea); + primary_alg = TPM2_ALG_RSA; + + r = tpm2_get_legacy_template(primary_alg, &template.publicArea); if (r < 0) return log_error_errno(r, "Could not get legacy RSA template: %m"); @@ -3882,7 +3834,7 @@ int tpm2_seal(const char *device, /* session= */ NULL, &template, /* sensitive= */ NULL, - &primary_public, + /* ret_public= */ NULL, &primary_handle); if (r < 0) return r; @@ -3923,11 +3875,6 @@ int tpm2_seal(const char *device, return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to marshal public key: %s", sym_Tss2_RC_Decode(rc)); - _cleanup_free_ void *hash = NULL; - hash = memdup(policy_digest.buffer, policy_digest.size); - if (!hash) - return log_oom(); - /* serialize the key for storage in the LUKS header. A deserialized ESYS_TR provides both * the raw TPM handle as well as the object name. The object name is used to verify that * the key we use later is the key we expect to establish the session with. @@ -3963,10 +3910,9 @@ int tpm2_seal(const char *device, *ret_secret_size = hmac_sensitive.data.size; *ret_blob = TAKE_PTR(blob); *ret_blob_size = blob_size; - *ret_pcr_hash = TAKE_PTR(hash); - *ret_pcr_hash_size = policy_digest.size; - *ret_pcr_bank = pcr_bank; - *ret_primary_alg = primary_public->publicArea.type; + + if (ret_primary_alg) + *ret_primary_alg = primary_alg; return 0; } diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h index c3bc35172a..32da247c37 100644 --- a/src/shared/tpm2-util.h +++ b/src/shared/tpm2-util.h @@ -154,8 +154,9 @@ int tpm2_calculate_name(const TPMT_PUBLIC *public, TPM2B_NAME *ret_name); int tpm2_calculate_policy_auth_value(TPM2B_DIGEST *digest); int tpm2_calculate_policy_authorize(const TPM2B_PUBLIC *public, const TPM2B_DIGEST *policy_ref, TPM2B_DIGEST *digest); int tpm2_calculate_policy_pcr(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, TPM2B_DIGEST *digest); +int tpm2_calculate_sealing_policy(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, const TPM2B_PUBLIC *public, bool use_pin, TPM2B_DIGEST *digest); -int tpm2_seal(const char *device, uint32_t hash_pcr_mask, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, const char *pin, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, void **ret_pcr_hash, size_t *ret_pcr_hash_size, uint16_t *ret_pcr_bank, uint16_t *ret_primary_alg, void **ret_srk_buf, size_t *ret_srk_buf_size); +int tpm2_seal(Tpm2Context *c, const TPM2B_DIGEST *policy, const char *pin, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, uint16_t *ret_primary_alg, void **ret_srk_buf, size_t *ret_srk_buf_size); int tpm2_unseal(const char *device, uint32_t hash_pcr_mask, uint16_t pcr_bank, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, JsonVariant *signature, const char *pin, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, const void *srk_buf, size_t srk_buf_size, void **ret_secret, size_t *ret_secret_size); #if HAVE_OPENSSL @@ -240,7 +241,8 @@ int tpm2_make_luks2_json(int keyslot, uint32_t hash_pcr_mask, uint16_t pcr_bank, int tpm2_parse_luks2_json(JsonVariant *v, int *ret_keyslot, uint32_t *ret_hash_pcr_mask, uint16_t *ret_pcr_bank, void **ret_pubkey, size_t *ret_pubkey_size, uint32_t *ret_pubkey_pcr_mask, uint16_t *ret_primary_alg, void **ret_blob, size_t *ret_blob_size, void **ret_policy_hash, size_t *ret_policy_hash_size, void **ret_salt, size_t *ret_salt_size, void **ret_srk_buf, size_t *ret_srk_buf_size, TPM2Flags *ret_flags); /* Default to PCR 7 only */ -#define TPM2_PCR_MASK_DEFAULT (UINT32_C(1) << 7) +#define TPM2_PCR_INDEX_DEFAULT (7) +#define TPM2_PCR_MASK_DEFAULT INDEX_TO_MASK(uint32_t, TPM2_PCR_INDEX_DEFAULT) /* We want the helpers below to work also if TPM2 libs are not available, hence define these four defines if * they are missing. */ From 1782b0b88ce531a674102445cdf95f86e6b54cae Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Fri, 14 Jul 2023 18:36:20 -0400 Subject: [PATCH 19/20] man: update systemd-cryptenroll man page with details on --tpm2-pcrs format change The previous commit extended the accepted format of --tpm2-pcrs to allow specifying the hash algorithm (i.e. PCR bank) and hash digest value, this updates the man page with those changes. --- man/systemd-cryptenroll.xml | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/man/systemd-cryptenroll.xml b/man/systemd-cryptenroll.xml index 6ca850e0ef..cea2072c47 100644 --- a/man/systemd-cryptenroll.xml +++ b/man/systemd-cryptenroll.xml @@ -391,13 +391,22 @@ PCR Configures the TPM2 PCRs (Platform Configuration Registers) to bind to when - enrollment is requested via . Takes a list of PCR names or numeric - indices in the range 0…23. Multiple PCR indexes are separated by +. If not - specified, the default is to use PCR 7 only. If an empty string is specified, binds the enrollment to - no PCRs at all. See the table above for a list of available PCRs. + enrollment is requested via . Takes a list of PCR entries, where each + entry starts with a name or numeric index in the range 0…23, optionally followed by + : and a hash algorithm name (specifying the PCR bank), optionally followed by + = and a hash digest value. Multiple PCR entries are separated by + +. If not specified, the default is to use PCR 7 only. If an empty string is + specified, binds the enrollment to no PCRs at all. See the table above for a list of available + PCRs. Example: specifies that PCR registers 4, 1, and 5 should be used. + Example: specifies that PCR register 7 from the SHA256 + bank should be used. + Example: + specifies that PCR register 4 from the SHA1 bank should be used, and a hash digest value of + 0x3A3F780F11A4B49969FCAA80CD6E3957C33B2275 will be used instead of reading the current PCR + value. From e85ddd96446abec938ef75d4e3c256b170920575 Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Thu, 13 Jul 2023 16:02:37 -0400 Subject: [PATCH 20/20] tpm2: update TEST-70-TPM2 to test passing PCR value to systemd-cryptenroll Add tests to use expected, not current, PCR values during sealing. --- test/units/testsuite-70.sh | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/test/units/testsuite-70.sh b/test/units/testsuite-70.sh index f01642f71a..dd8cdb7efa 100755 --- a/test/units/testsuite-70.sh +++ b/test/units/testsuite-70.sh @@ -93,7 +93,43 @@ PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+7 "$img # Check with wrong PCR 0 tpm2_pcrextend 0:sha256=0000000000000000000000000000000000000000000000000000000000000000 -"$SD_CRYPTSETUP" attach test-volume "$img" - tpm2-device=auto,headless=1 && exit 1 +(! "$SD_CRYPTSETUP" attach test-volume "$img" - tpm2-device=auto,headless=1) + +if tpm_has_pcr sha256 12; then + # Enroll using an explict PCR value (that does match current PCR value) + systemd-cryptenroll --wipe-slot=tpm2 "$img" + EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12) + PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="12:sha256=$EXPECTED_PCR_VALUE" "$img" + "$SD_CRYPTSETUP" attach test-volume "$img" - tpm2-device=auto,headless=1 + "$SD_CRYPTSETUP" detach test-volume + + # Same as above plus more PCRs without the value or alg specified + systemd-cryptenroll --wipe-slot=tpm2 "$img" + EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12) + PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="1,12:sha256=$EXPECTED_PCR_VALUE,3" "$img" + "$SD_CRYPTSETUP" attach test-volume "$img" - tpm2-device=auto,headless=1 + "$SD_CRYPTSETUP" detach test-volume + + # Same as above plus more PCRs with hash alg specified but hash value not specified + systemd-cryptenroll --wipe-slot=tpm2 "$img" + EXPECTED_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12) + PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="1:sha256,12:sha256=$EXPECTED_PCR_VALUE,3" "$img" + "$SD_CRYPTSETUP" attach test-volume "$img" - tpm2-device=auto,headless=1 + "$SD_CRYPTSETUP" detach test-volume + + # Now the interesting part, enrolling using a hash value that doesn't match the current PCR value + systemd-cryptenroll --wipe-slot=tpm2 "$img" + tpm2_pcrread -Q -o /tmp/pcr.dat sha256:12 + CURRENT_PCR_VALUE=$(cat /sys/class/tpm/tpm0/pcr-sha256/12) + EXPECTED_PCR_VALUE=$(cat /tmp/pcr.dat /tmp/pcr.dat | openssl dgst -sha256 -r | cut -d ' ' -f 1) + PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs="12:sha256=$EXPECTED_PCR_VALUE" "$img" + (! "$SD_CRYPTSETUP" attach test-volume "$img" - tpm2-device=auto,headless=1) + tpm2_pcrextend "12:sha256=$CURRENT_PCR_VALUE" + "$SD_CRYPTSETUP" attach test-volume "$img" - tpm2-device=auto,headless=1 + "$SD_CRYPTSETUP" detach test-volume + + rm -f /tmp/pcr.dat +fi rm -f "${img:?}"