diff options
-rw-r--r-- | src/basic/bitfield.h | 73 | ||||
-rw-r--r-- | src/basic/macro.h | 9 | ||||
-rw-r--r-- | src/boot/measure.c | 4 | ||||
-rw-r--r-- | src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c | 14 | ||||
-rw-r--r-- | src/cryptsetup/cryptsetup.c | 2 | ||||
-rw-r--r-- | src/shared/tpm2-util.c | 578 | ||||
-rw-r--r-- | src/shared/tpm2-util.h | 49 | ||||
-rw-r--r-- | src/test/meson.build | 1 | ||||
-rw-r--r-- | src/test/test-bitfield.c | 227 | ||||
-rw-r--r-- | src/test/test-macro.c | 184 | ||||
-rw-r--r-- | src/test/test-tpm2.c | 374 |
11 files changed, 1354 insertions, 161 deletions
diff --git a/src/basic/bitfield.h b/src/basic/bitfield.h new file mode 100644 index 0000000000..25bc0ebda7 --- /dev/null +++ b/src/basic/bitfield.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "macro.h" + +/* Bit index (0-based) to mask of specified type. Assertion failure if index is out of range. */ +#define _INDEX_TO_MASK(type, i, uniq) \ + ({ \ + int UNIQ_T(_i, uniq) = (i); \ + assert(UNIQ_T(_i, uniq) < (int)sizeof(type) * 8); \ + ((type)1) << UNIQ_T(_i, uniq); \ + }) +#define INDEX_TO_MASK(type, i) \ + ({ \ + assert_cc(sizeof(type) <= sizeof(unsigned long long)); \ + assert_cc(__builtin_choose_expr(__builtin_constant_p(i), i, 0) < (int)(sizeof(type) * 8)); \ + __builtin_choose_expr(__builtin_constant_p(i), \ + ((type)1) << (i), \ + _INDEX_TO_MASK(type, i, UNIQ)); \ + }) + +/* Builds a mask of specified type with multiple bits set. Note the result will not be constant, even if all + * indexes are constant. */ +#define INDEXES_TO_MASK(type, ...) \ + UNIQ_INDEXES_TO_MASK(type, UNIQ, ##__VA_ARGS__) +#define UNIQ_INDEXES_TO_MASK(type, uniq, ...) \ + ({ \ + typeof(type) UNIQ_T(_mask, uniq) = (type)0; \ + int UNIQ_T(_i, uniq); \ + VA_ARGS_FOREACH(UNIQ_T(_i, uniq), ##__VA_ARGS__) \ + UNIQ_T(_mask, uniq) |= INDEX_TO_MASK(type, UNIQ_T(_i, uniq)); \ + UNIQ_T(_mask, uniq); \ + }) + +/* Same as the FLAG macros, but accept a 0-based bit index instead of a mask. Results in assertion failure if + * index is out of range for the type. */ +#define SET_BIT(bits, i) SET_FLAG(bits, INDEX_TO_MASK(typeof(bits), i), true) +#define CLEAR_BIT(bits, i) SET_FLAG(bits, INDEX_TO_MASK(typeof(bits), i), false) +#define BIT_SET(bits, i) FLAGS_SET(bits, INDEX_TO_MASK(typeof(bits), i)) + +/* As above, but accepts multiple indexes. Note the result will not be constant, even if all indexes are + * constant. */ +#define SET_BITS(bits, ...) SET_FLAG(bits, INDEXES_TO_MASK(typeof(bits), ##__VA_ARGS__), true) +#define CLEAR_BITS(bits, ...) SET_FLAG(bits, INDEXES_TO_MASK(typeof(bits), ##__VA_ARGS__), false) +#define BITS_SET(bits, ...) FLAGS_SET(bits, INDEXES_TO_MASK(typeof(bits), ##__VA_ARGS__)) + +/* Iterate through each set bit. Index is 0-based and type int. */ +#define BIT_FOREACH(index, bits) _BIT_FOREACH(index, bits, UNIQ) +#define _BIT_FOREACH(index, bits, uniq) \ + for (int UNIQ_T(_last, uniq) = -1, index; \ + (index = BIT_NEXT_SET(bits, UNIQ_T(_last, uniq))) >= 0; \ + UNIQ_T(_last, uniq) = index) + +/* Find the next set bit after 0-based index 'prev'. Result is 0-based index of next set bit, or -1 if no + * more bits are set. */ +#define BIT_FIRST_SET(bits) BIT_NEXT_SET(bits, -1) +#define BIT_NEXT_SET(bits, prev) \ + UNIQ_BIT_NEXT_SET(bits, prev, UNIQ) +#define UNIQ_BIT_NEXT_SET(bits, prev, uniq) \ + ({ \ + typeof(bits) UNIQ_T(_bits, uniq) = (bits); \ + int UNIQ_T(_prev, uniq) = (prev); \ + int UNIQ_T(_next, uniq); \ + _BIT_NEXT_SET(UNIQ_T(_bits, uniq), \ + UNIQ_T(_prev, uniq), \ + UNIQ_T(_next, uniq)); \ + }) +#define _BIT_NEXT_SET(bits, prev, next) \ + ((int)(prev + 1) == (int)sizeof(bits) * 8 \ + ? -1 /* Prev index was msb. */ \ + : ((next = __builtin_ffsll(((unsigned long long)(bits)) >> (prev + 1))) == 0 \ + ? -1 /* No more bits set. */ \ + : prev + next)) diff --git a/src/basic/macro.h b/src/basic/macro.h index 63344f8d97..2770d01aa9 100644 --- a/src/basic/macro.h +++ b/src/basic/macro.h @@ -430,4 +430,13 @@ assert_cc(sizeof(dummy_t) == 0); _q && _q > (base) ? &_q[-1] : NULL; \ }) +/* Iterate through each variadic arg. All must be the same type as 'entry' or must be implicitly + * convertable. The iteration variable 'entry' must already be defined. */ +#define VA_ARGS_FOREACH(entry, ...) \ + _VA_ARGS_FOREACH(entry, UNIQ_T(_entries_, UNIQ), UNIQ_T(_current_, UNIQ), ##__VA_ARGS__) +#define _VA_ARGS_FOREACH(entry, _entries_, _current_, ...) \ + for (typeof(entry) _entries_[] = { __VA_ARGS__ }, *_current_ = _entries_; \ + ((long)(_current_ - _entries_) < (long)ELEMENTSOF(_entries_)) && ({ entry = *_current_; true; }); \ + _current_++) + #include "log.h" diff --git a/src/boot/measure.c b/src/boot/measure.c index 18838686a9..072f38f200 100644 --- a/src/boot/measure.c +++ b/src/boot/measure.c @@ -870,7 +870,9 @@ static int verb_sign(int argc, char *argv[], void *userdata) { return log_error_errno(tpmalg, "Unsupported PCR bank"); TPML_PCR_SELECTION pcr_selection; - tpm2_pcr_mask_to_selection(1 << TPM_PCR_INDEX_KERNEL_IMAGE, tpmalg, &pcr_selection); + tpm2_tpml_pcr_selection_from_mask(1 << TPM_PCR_INDEX_KERNEL_IMAGE, + tpmalg, + &pcr_selection); rc = sym_Esys_PolicyPCR( c->esys_context, diff --git a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c index 319b0ca64d..b5d66e389d 100644 --- a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c +++ b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c @@ -205,13 +205,13 @@ _public_ void cryptsetup_token_dump( if (r < 0) return (void) crypt_log_debug_errno(cd, r, "Failed to parse " TOKEN_NAME " JSON fields: %m"); - r = pcr_mask_to_string(hash_pcr_mask, &hash_pcrs_str); - if (r < 0) - return (void) crypt_log_debug_errno(cd, r, "Cannot format PCR hash mask: %m"); + hash_pcrs_str = tpm2_pcr_mask_to_string(hash_pcr_mask); + if (!hash_pcrs_str) + return (void) crypt_log_debug_errno(cd, ENOMEM, "Cannot format PCR hash mask: %m"); - r = pcr_mask_to_string(pubkey_pcr_mask, &pubkey_pcrs_str); - if (r < 0) - return (void) crypt_log_debug_errno(cd, r, "Cannot format PCR hash mask: %m"); + pubkey_pcrs_str = tpm2_pcr_mask_to_string(pubkey_pcr_mask); + if (!pubkey_pcrs_str) + return (void) crypt_log_debug_errno(cd, ENOMEM, "Cannot format PCR hash mask: %m"); r = crypt_dump_buffer_to_hex_string(blob, blob_size, &blob_str); if (r < 0) @@ -271,7 +271,7 @@ _public_ int cryptsetup_token_validate( } u = json_variant_unsigned(e); - if (u >= TPM2_PCRS_MAX) { + if (!TPM2_PCR_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 e776605501..fa160c1f8c 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -438,7 +438,7 @@ static int parse_one_option(const char *option) { } pcr = r ? TPM_PCR_INDEX_VOLUME_KEY : UINT_MAX; - } else if (pcr >= TPM2_PCRS_MAX) { + } else if (!TPM2_PCR_VALID(pcr)) { log_error("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 de706a10db..28d743a576 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -462,96 +462,461 @@ static int tpm2_make_primary( return 0; } -void tpm2_pcr_mask_to_selection(uint32_t mask, uint16_t bank, TPML_PCR_SELECTION *ret) { +/* 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) { + assert(s); + assert(s->sizeofSelect <= sizeof(s->pcrSelect)); assert(ret); - /* We only do 24bit here, as that's what PC TPMs are supposed to support */ - assert(mask <= 0xFFFFFFU); + 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; +} - *ret = (TPML_PCR_SELECTION) { - .count = 1, - .pcrSelections[0] = { - .hash = bank, - .sizeofSelect = 3, - .pcrSelect[0] = mask & 0xFF, - .pcrSelect[1] = (mask >> 8) & 0xFF, - .pcrSelect[2] = (mask >> 16) & 0xFF, - } +/* Convert a mask and hash alg to a TPMS_PCR_SELECTION object. */ +void tpm2_tpms_pcr_selection_from_mask(uint32_t mask, TPMI_ALG_HASH hash_alg, TPMS_PCR_SELECTION *ret) { + assert(ret); + + /* This is currently hardcoded at 24 PCRs, above. */ + if (!TPM2_PCR_MASK_VALID(mask)) + log_warning("PCR mask selections (%x) out of range, ignoring.", + mask & ~((uint32_t)TPM2_PCRS_MASK)); + + *ret = (TPMS_PCR_SELECTION){ + .hash = hash_alg, + .sizeofSelect = TPM2_PCRS_MAX / 8, + .pcrSelect[0] = mask & 0xff, + .pcrSelect[1] = (mask >> 8) & 0xff, + .pcrSelect[2] = (mask >> 16) & 0xff, }; } -static unsigned find_nth_bit(uint32_t mask, unsigned n) { - uint32_t bit = 1; +/* 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) { + 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); + 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) { + 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); + tpm2_tpms_pcr_selection_from_mask(maska & ~maskb, a->hash, a); +} + +/* Move all PCR selections in 'b' to 'a'. Both must have the same hash alg. */ +void tpm2_tpms_pcr_selection_move(TPMS_PCR_SELECTION *a, TPMS_PCR_SELECTION *b) { + if (a == b) + return; + + tpm2_tpms_pcr_selection_add(a, b); + tpm2_tpms_pcr_selection_from_mask(0, b->hash, 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); \ + })) + +#define FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(tpms, tpml) \ + UNIQ_FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(tpms, tpml, UNIQ) +#define UNIQ_FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(tpms, tpml, uniq) \ + for (TPML_PCR_SELECTION *UNIQ_T(_tpml, uniq) = (TPML_PCR_SELECTION*)(tpml); \ + UNIQ_T(_tpml, uniq); UNIQ_T(_tpml, uniq) = NULL) \ + _FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(tpms, UNIQ_T(_tpml, uniq)) +#define _FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(tpms, tpml) \ + for (TPMS_PCR_SELECTION *tpms = tpml->pcrSelections; \ + (uint32_t)(tpms - tpml->pcrSelections) < tpml->count; \ + tpms++) + +#define FOREACH_PCR_IN_TPML_PCR_SELECTION(pcr, tpms, tpml) \ + FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(tpms, tpml) \ + FOREACH_PCR_IN_TPMS_PCR_SELECTION(pcr, tpms) + +char *tpm2_tpms_pcr_selection_to_string(const TPMS_PCR_SELECTION *s) { + assert(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) + return NULL; + + return strjoin(algstr, "(", maskstr, ")"); +} - assert(n < 32); +size_t tpm2_tpms_pcr_selection_weight(const TPMS_PCR_SELECTION *s) { + assert(s); - /* Returns the bit index of the nth set bit, e.g. mask=0b101001, n=3 → 5 */ + uint32_t mask; + tpm2_tpms_pcr_selection_to_mask(s, &mask); + return (size_t)__builtin_popcount(mask); +} + +/* Utility functions for TPML_PCR_SELECTION. */ - for (unsigned i = 0; i < sizeof(mask)*8; i++) { +/* Remove the (0-based) index entry from 'l', shift all following entries, and update the count. */ +static void tpm2_tpml_pcr_selection_remove_index(TPML_PCR_SELECTION *l, uint32_t index) { + assert(l); + assert(l->count <= sizeof(l->pcrSelections)); + assert(index < l->count); + + size_t s = l->count - (index + 1); + memmove(&l->pcrSelections[index], &l->pcrSelections[index + 1], s * sizeof(l->pcrSelections[0])); + l->count--; +} - if (bit & mask) { - if (n == 0) - return i; +/* Get a TPMS_PCR_SELECTION from a TPML_PCR_SELECTION for the given hash alg. Returns NULL if there is no + * entry for the hash alg. This guarantees the returned entry contains all the PCR selections for the given + * hash alg, which may require modifying the TPML_PCR_SELECTION by removing duplicate entries. */ +static TPMS_PCR_SELECTION *tpm2_tpml_pcr_selection_get_tpms_pcr_selection( + TPML_PCR_SELECTION *l, + TPMI_ALG_HASH hash_alg) { - n--; + assert(l); + + TPMS_PCR_SELECTION *selection = NULL; + FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(s, l) + if (s->hash == hash_alg) { + selection = s; + break; } - bit <<= 1; + if (!selection) + return NULL; + + /* Iterate backwards through the entries, removing any other entries for the hash alg. */ + for (uint32_t i = l->count - 1; i > 0; i--) { + TPMS_PCR_SELECTION *s = &l->pcrSelections[i]; + + if (selection == s) + break; + + if (s->hash == hash_alg) { + tpm2_tpms_pcr_selection_move(selection, s); + tpm2_tpml_pcr_selection_remove_index(l, i); + } } - return UINT_MAX; + return selection; } -static int tpm2_pcr_mask_good( +/* 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) { + 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. */ + TPML_PCR_SELECTION lcopy = *l; + + TPMS_PCR_SELECTION *s; + s = tpm2_tpml_pcr_selection_get_tpms_pcr_selection(&lcopy, hash_alg); + if (!s) + return SYNTHETIC_ERRNO(ENOENT); + + tpm2_tpms_pcr_selection_to_mask(s, ret); + return 0; +} + +/* Convert a mask and hash alg to a TPML_PCR_SELECTION object. */ +void tpm2_tpml_pcr_selection_from_mask(uint32_t mask, TPMI_ALG_HASH hash_alg, TPML_PCR_SELECTION *ret) { + assert(ret); + + TPMS_PCR_SELECTION s; + tpm2_tpms_pcr_selection_from_mask(mask, hash_alg, &s); + + *ret = (TPML_PCR_SELECTION){ + .count = 1, + .pcrSelections[0] = s, + }; +} + +/* Combine all duplicate (same hash alg) TPMS_PCR_SELECTION entries in 'l'. */ +static void tpm2_tpml_pcr_selection_cleanup(TPML_PCR_SELECTION *l) { + FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(s, l) + /* This removes all duplicates for s->hash. */ + (void) tpm2_tpml_pcr_selection_get_tpms_pcr_selection(l, s->hash); +} + +/* Add the PCR selections in 's' to the corresponding hash alg TPMS_PCR_SELECTION entry in 'l'. Adds a new + * TPMS_PCR_SELECTION entry for the hash alg if needed. This may modify the TPML_PCR_SELECTION by combining + * entries with the same hash alg. */ +void tpm2_tpml_pcr_selection_add_tpms_pcr_selection(TPML_PCR_SELECTION *l, const TPMS_PCR_SELECTION *s) { + assert(l); + assert(s); + + if (tpm2_tpms_pcr_selection_is_empty(s)) + return; + + TPMS_PCR_SELECTION *selection = tpm2_tpml_pcr_selection_get_tpms_pcr_selection(l, s->hash); + if (selection) { + tpm2_tpms_pcr_selection_add(selection, s); + return; + } + + /* It's already broken if the count is higher than the array has size for. */ + assert(!(l->count > sizeof(l->pcrSelections))); + + /* If full, the cleanup should result in at least one available entry. */ + if (l->count == sizeof(l->pcrSelections)) + tpm2_tpml_pcr_selection_cleanup(l); + + assert(l->count < sizeof(l->pcrSelections)); + l->pcrSelections[l->count++] = *s; +} + +/* Remove the PCR selections in 's' from the corresponding hash alg TPMS_PCR_SELECTION entry in 'l'. This + * will combine all entries for 's->hash' in 'l'. */ +void tpm2_tpml_pcr_selection_sub_tpms_pcr_selection(TPML_PCR_SELECTION *l, const TPMS_PCR_SELECTION *s) { + assert(l); + assert(s); + + if (tpm2_tpms_pcr_selection_is_empty(s)) + return; + + TPMS_PCR_SELECTION *selection = tpm2_tpml_pcr_selection_get_tpms_pcr_selection(l, s->hash); + if (selection) + tpm2_tpms_pcr_selection_sub(selection, s); +} + +/* 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); + assert(b); + + FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(selection_b, (TPML_PCR_SELECTION*) b) + tpm2_tpml_pcr_selection_add_tpms_pcr_selection(a, selection_b); +} + +/* Remove all PCR selections in 'b' from 'a'. */ +void tpm2_tpml_pcr_selection_sub(TPML_PCR_SELECTION *a, const TPML_PCR_SELECTION *b) { + assert(a); + assert(b); + + FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(selection_b, (TPML_PCR_SELECTION*) b) + tpm2_tpml_pcr_selection_sub_tpms_pcr_selection(a, selection_b); +} + +char *tpm2_tpml_pcr_selection_to_string(const TPML_PCR_SELECTION *l) { + assert(l); + + _cleanup_free_ char *banks = NULL; + FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(s, (TPML_PCR_SELECTION*) l) { + if (tpm2_tpms_pcr_selection_is_empty(s)) + continue; + + _cleanup_free_ char *str = tpm2_tpms_pcr_selection_to_string(s); + if (!str || !strextend_with_separator(&banks, ",", str)) + return NULL; + } + + return strjoin("[", strempty(banks), "]"); +} + +size_t tpm2_tpml_pcr_selection_weight(const TPML_PCR_SELECTION *l) { + assert(l); + assert(l->count <= sizeof(l->pcrSelections)); + + size_t weight = 0; + FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(s, l) { + size_t w = tpm2_tpms_pcr_selection_weight(s); + assert(weight <= SIZE_MAX - w); + weight += w; + } + + return weight; +} + +static void tpm2_log_debug_tpml_pcr_selection(const TPML_PCR_SELECTION *l, const char *msg) { + if (!DEBUG_LOGGING || !l) + return; + + _cleanup_free_ char *s = tpm2_tpml_pcr_selection_to_string(l); + log_debug("%s: %s", msg ?: "PCR selection", strna(s)); +} + +static void tpm2_log_debug_buffer(const void *buffer, size_t size, const char *msg) { + if (!DEBUG_LOGGING || !buffer || size == 0) + return; + + _cleanup_free_ char *h = hexmem(buffer, size); + log_debug("%s: %s", msg ?: "Buffer", strna(h)); +} + +static 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 int tpm2_get_policy_digest( Tpm2Context *c, - TPMI_ALG_HASH bank, - uint32_t mask) { + const Tpm2Handle *session, + TPM2B_DIGEST **ret_policy_digest) { - _cleanup_(Esys_Freep) TPML_DIGEST *pcr_values = NULL; - TPML_PCR_SELECTION selection; - bool good = false; TSS2_RC rc; - assert(c); + if (!DEBUG_LOGGING && !ret_policy_digest) + return 0; - /* So we have the problem that some systems might have working TPM2 chips, but the firmware doesn't - * actually measure into them, or only into a suboptimal bank. If so, the PCRs should be all zero or - * all 0xFF. Detect that, so that we can warn and maybe pick a better bank. */ + assert(c); + assert(session); - tpm2_pcr_mask_to_selection(mask, bank, &selection); + log_debug("Acquiring policy digest."); - rc = sym_Esys_PCR_Read( + _cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL; + rc = sym_Esys_PolicyGetDigest( c->esys_context, + session->esys_handle, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, - &selection, - NULL, - NULL, - &pcr_values); + &policy_digest); if (rc != TSS2_RC_SUCCESS) return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), - "Failed to read TPM2 PCRs: %s", sym_Tss2_RC_Decode(rc)); + "Failed to get policy digest from TPM: %s", sym_Tss2_RC_Decode(rc)); + + tpm2_log_debug_digest(policy_digest, "Session policy digest"); + + if (ret_policy_digest) + *ret_policy_digest = TAKE_PTR(policy_digest); + + return 0; +} + +static int tpm2_pcr_read( + Tpm2Context *c, + const TPML_PCR_SELECTION *pcr_selection, + TPML_PCR_SELECTION *ret_pcr_selection, + TPM2B_DIGEST **ret_pcr_values, + size_t *ret_pcr_values_size) { + + _cleanup_free_ TPM2B_DIGEST *pcr_values = NULL; + TPML_PCR_SELECTION remaining, total_read = {}; + size_t pcr_values_size = 0; + TSS2_RC rc; + + assert(c); + assert(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; + + tpm2_log_debug_tpml_pcr_selection(&remaining, "Reading PCR selection"); + + /* Unfortunately, PCR_Read will not return more than 8 values. */ + rc = sym_Esys_PCR_Read( + c->esys_context, + ESYS_TR_NONE, + ESYS_TR_NONE, + ESYS_TR_NONE, + &remaining, + NULL, + ¤t_read, + ¤t_values); + if (rc != TSS2_RC_SUCCESS) + return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), + "Failed to read TPM2 PCRs: %s", sym_Tss2_RC_Decode(rc)); + + 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); + + if (!GREEDY_REALLOC(pcr_values, pcr_values_size + current_values->count)) + return log_oom(); + + memcpy_safe(&pcr_values[pcr_values_size], current_values->digests, + current_values->count * sizeof(TPM2B_DIGEST)); + pcr_values_size += current_values->count; - /* If at least one of the selected PCR values is something other than all 0x00 or all 0xFF we are happy. */ - for (unsigned i = 0; i < pcr_values->count; i++) { if (DEBUG_LOGGING) { - _cleanup_free_ char *h = NULL; - unsigned j; + unsigned i = 0; + FOREACH_PCR_IN_TPML_PCR_SELECTION(pcr, s, current_read) { + assert(i < current_values->count); - h = hexmem(pcr_values->digests[i].buffer, pcr_values->digests[i].size); - j = find_nth_bit(mask, i); - assert(j != UINT_MAX); + TPM2B_DIGEST *d = ¤t_values->digests[i]; + i++; - log_debug("PCR %u value: %s", j, strna(h)); + 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 (!memeqbyte(0x00, pcr_values->digests[i].buffer, pcr_values->digests[i].size) && - !memeqbyte(0xFF, pcr_values->digests[i].buffer, pcr_values->digests[i].size)) - good = true; + if (ret_pcr_selection) + *ret_pcr_selection = total_read; + if (ret_pcr_values) + *ret_pcr_values = TAKE_PTR(pcr_values); + if (ret_pcr_values_size) + *ret_pcr_values_size = pcr_values_size; + + return 0; +} + +static int tpm2_pcr_mask_good( + Tpm2Context *c, + TPMI_ALG_HASH bank, + uint32_t mask) { + + _cleanup_free_ TPM2B_DIGEST *pcr_values = NULL; + TPML_PCR_SELECTION selection; + size_t pcr_values_size = 0; + int r; + + assert(c); + + /* So we have the problem that some systems might have working TPM2 chips, but the firmware doesn't + * actually measure into them, or only into a suboptimal bank. If so, the PCRs should be all zero or + * all 0xFF. Detect that, so that we can warn and maybe pick a better bank. */ + + tpm2_tpml_pcr_selection_from_mask(mask, bank, &selection); + + r = tpm2_pcr_read(c, &selection, &selection, &pcr_values, &pcr_values_size); + 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 < pcr_values_size); + + if (!memeqbyte(0x00, pcr_values[i].buffer, pcr_values[i].size) && + !memeqbyte(0xFF, pcr_values[i].buffer, pcr_values[i].size)) + return true; + + i++; } - return good; + return false; } static int tpm2_bank_has24(const TPMS_PCR_SELECTION *selection) { @@ -1108,7 +1473,6 @@ static int tpm2_make_policy_session( .keyBits.aes = 128, .mode.aes = TPM2_ALG_CFB, }; - _cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL; TSS2_RC rc; int r; @@ -1230,7 +1594,7 @@ static int tpm2_make_policy_session( /* Put together the PCR policy we want to use */ TPML_PCR_SELECTION pcr_selection; - tpm2_pcr_mask_to_selection(pubkey_pcr_mask, pcr_bank, &pcr_selection); + tpm2_tpml_pcr_selection_from_mask(pubkey_pcr_mask, (TPMI_ALG_HASH)pcr_bank, &pcr_selection); rc = sym_Esys_PolicyPCR( c->esys_context, session->esys_handle, @@ -1245,16 +1609,9 @@ static int tpm2_make_policy_session( /* Get the policy hash of the PCR policy */ _cleanup_(Esys_Freep) TPM2B_DIGEST *approved_policy = NULL; - rc = sym_Esys_PolicyGetDigest( - c->esys_context, - session->esys_handle, - ESYS_TR_NONE, - ESYS_TR_NONE, - ESYS_TR_NONE, - &approved_policy); - if (rc != TSS2_RC_SUCCESS) - return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), - "Failed to get policy digest from TPM: %s", sym_Tss2_RC_Decode(rc)); + r = tpm2_get_policy_digest(c, session, &approved_policy); + if (r < 0) + return r; /* When we are unlocking and have a signature, let's pass it to the TPM */ _cleanup_(Esys_Freep) TPMT_TK_VERIFIED *check_ticket_buffer = NULL; @@ -1340,7 +1697,7 @@ static int tpm2_make_policy_session( log_debug("Configuring hash-based PCR policy."); TPML_PCR_SELECTION pcr_selection; - tpm2_pcr_mask_to_selection(hash_pcr_mask, pcr_bank, &pcr_selection); + tpm2_tpml_pcr_selection_from_mask(hash_pcr_mask, (TPMI_ALG_HASH)pcr_bank, &pcr_selection); rc = sym_Esys_PolicyPCR( c->esys_context, session->esys_handle, @@ -1369,38 +1726,13 @@ static int tpm2_make_policy_session( sym_Tss2_RC_Decode(rc)); } - if (DEBUG_LOGGING || ret_policy_digest) { - log_debug("Acquiring policy digest."); - - rc = sym_Esys_PolicyGetDigest( - c->esys_context, - session->esys_handle, - ESYS_TR_NONE, - ESYS_TR_NONE, - ESYS_TR_NONE, - &policy_digest); - - if (rc != TSS2_RC_SUCCESS) - return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), - "Failed to get policy digest from TPM: %s", sym_Tss2_RC_Decode(rc)); - - if (DEBUG_LOGGING) { - _cleanup_free_ char *h = NULL; - - h = hexmem(policy_digest->buffer, policy_digest->size); - if (!h) - return log_oom(); - - log_debug("Session policy digest: %s", h); - } - } + r = tpm2_get_policy_digest(c, session, ret_policy_digest); + if (r < 0) + return r; if (ret_session) *ret_session = TAKE_PTR(session); - if (ret_policy_digest) - *ret_policy_digest = TAKE_PTR(policy_digest); - if (ret_pcr_bank) *ret_pcr_bank = pcr_bank; @@ -1422,7 +1754,6 @@ int tpm2_seal(const char *device, uint16_t *ret_pcr_bank, uint16_t *ret_primary_alg) { - _cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL; _cleanup_(Esys_Freep) TPM2B_PRIVATE *private = NULL; _cleanup_(Esys_Freep) TPM2B_PUBLIC *public = NULL; static const TPML_PCR_SELECTION creation_pcr = {}; @@ -1431,7 +1762,6 @@ int tpm2_seal(const char *device, TPM2B_SENSITIVE_CREATE hmac_sensitive; TPMI_ALG_PUBLIC primary_alg; TPM2B_PUBLIC hmac_template; - TPMI_ALG_HASH pcr_bank; usec_t start; TSS2_RC rc; int r; @@ -1485,6 +1815,8 @@ int tpm2_seal(const char *device, if (r < 0) return r; + _cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL; + TPMI_ALG_HASH pcr_bank; r = tpm2_make_policy_session( c, primary, @@ -1616,7 +1948,6 @@ int tpm2_unseal(const char *device, size_t *ret_secret_size) { _cleanup_(Esys_Freep) TPM2B_SENSITIVE_DATA* unsealed = NULL; - _cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL; _cleanup_(erase_and_freep) char *secret = NULL; TPM2B_PRIVATE private = {}; TPM2B_PUBLIC public = {}; @@ -1716,6 +2047,7 @@ int tpm2_unseal(const char *device, for (unsigned i = RETRY_UNSEAL_MAX;; i--) { _cleanup_tpm2_handle_ Tpm2Handle *policy_session = NULL; + _cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL; r = tpm2_make_policy_session( c, primary, @@ -1981,13 +2313,28 @@ int tpm2_extend_bytes( } #endif -int tpm2_parse_pcrs(const char *s, uint32_t *ret) { - const char *p = ASSERT_PTR(s); +char *tpm2_pcr_mask_to_string(uint32_t mask) { + _cleanup_free_ char *s = NULL; + + FOREACH_PCR_IN_MASK(n, mask) + if (strextendf_with_separator(&s, "+", "%d", n) < 0) + return NULL; + + if (!s) + return strdup(""); + + return TAKE_PTR(s); +} + +int tpm2_pcr_mask_from_string(const char *arg, uint32_t *ret_mask) { uint32_t mask = 0; int r; - if (isempty(s)) { - *ret = 0; + assert(arg); + assert(ret_mask); + + if (isempty(arg)) { + *ret_mask = 0; return 0; } @@ -1996,6 +2343,7 @@ int tpm2_parse_pcrs(const char *s, uint32_t *ret) { * /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; @@ -2004,19 +2352,20 @@ int tpm2_parse_pcrs(const char *s, uint32_t *ret) { if (r == 0) break; if (r < 0) - return log_error_errno(r, "Failed to parse PCR list: %s", s); + return log_error_errno(r, "Failed to parse PCR list: %s", arg); r = safe_atou(pcr, &n); if (r < 0) return log_error_errno(r, "Failed to parse PCR number: %s", pcr); if (n >= TPM2_PCRS_MAX) return log_error_errno(SYNTHETIC_ERRNO(ERANGE), - "PCR number out of range (valid range 0…23): %u", n); + "PCR number out of range (valid range 0…%u): %u", + TPM2_PCRS_MAX - 1, n); - mask |= UINT32_C(1) << n; + SET_BIT(mask, n);; } - *ret = mask; + *ret_mask = mask; return 0; } @@ -2381,7 +2730,7 @@ int tpm2_parse_pcr_argument(const char *arg, uint32_t *mask) { return 0; } - r = tpm2_parse_pcrs(arg, &m); + r = tpm2_pcr_mask_from_string(arg, &m); if (r < 0) return r; @@ -2437,25 +2786,6 @@ int tpm2_load_pcr_public_key(const char *path, void **ret_pubkey, size_t *ret_pu return 0; } -int pcr_mask_to_string(uint32_t mask, char **ret) { - _cleanup_free_ char *buf = NULL; - int r; - - assert(ret); - - for (unsigned i = 0; i < TPM2_PCRS_MAX; i++) { - if (!(mask & (UINT32_C(1) << i))) - continue; - - r = strextendf_with_separator(&buf, "+", "%u", i); - if (r < 0) - return r; - } - - *ret = TAKE_PTR(buf); - return 0; -} - #define PBKDF2_HMAC_SHA256_ITERATIONS 10000 /* diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h index d26a945a90..c2532c61c2 100644 --- a/src/shared/tpm2-util.h +++ b/src/shared/tpm2-util.h @@ -3,6 +3,7 @@ #include <stdbool.h> +#include "bitfield.h" #include "json.h" #include "macro.h" #include "sha256.h" @@ -11,6 +12,20 @@ typedef enum TPM2Flags { TPM2_FLAGS_USE_PIN = 1 << 0, } TPM2Flags; + +/* As per https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClient_PFP_r1p05_v23_pub.pdf a + * 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) { + return pcr < TPM2_PCRS_MAX; +} +static inline bool TPM2_PCR_MASK_VALID(uint32_t pcr_mask) { + return pcr_mask <= TPM2_PCRS_MASK; +} + +#define FOREACH_PCR_IN_MASK(pcr, mask) BIT_FOREACH(pcr, mask) + #if HAVE_TPM2 #include <tss2/tss2_esys.h> @@ -80,8 +95,6 @@ Tpm2Handle *tpm2_handle_free(Tpm2Handle *handle); DEFINE_TRIVIAL_CLEANUP_FUNC(Tpm2Handle*, tpm2_handle_free); #define _cleanup_tpm2_handle_ _cleanup_(tpm2_handle_freep) -void tpm2_pcr_mask_to_selection(uint32_t mask, uint16_t bank, TPML_PCR_SELECTION *ret); - static inline void Esys_Freep(void *p) { if (*(void**) p) sym_Esys_Free(*(void**) p); @@ -92,6 +105,25 @@ 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); +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); +void tpm2_tpms_pcr_selection_move(TPMS_PCR_SELECTION *a, TPMS_PCR_SELECTION *b); +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); +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); +void tpm2_tpml_pcr_selection_add(TPML_PCR_SELECTION *a, const TPML_PCR_SELECTION *b); +void tpm2_tpml_pcr_selection_sub(TPML_PCR_SELECTION *a, const TPML_PCR_SELECTION *b); +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) + #else /* HAVE_TPM2 */ typedef struct {} Tpm2Context; typedef struct {} Tpm2Handle; @@ -100,20 +132,12 @@ typedef struct {} Tpm2Handle; int tpm2_list_devices(void); int tpm2_find_device_auto(int log_level, char **ret); -int tpm2_parse_pcrs(const char *s, uint32_t *ret); - int tpm2_make_pcr_json_array(uint32_t pcr_mask, JsonVariant **ret); int tpm2_parse_pcr_json_array(JsonVariant *v, uint32_t *ret); int tpm2_make_luks2_json(int keyslot, uint32_t hash_pcr_mask, uint16_t pcr_bank, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, const void *salt, size_t salt_size, TPM2Flags flags, JsonVariant **ret); 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, TPM2Flags *ret_flags); -#define TPM2_PCRS_MAX 24U - -static inline bool TPM2_PCR_MASK_VALID(uint64_t pcr_mask) { - return pcr_mask < (UINT64_C(1) << TPM2_PCRS_MAX); /* Support 24 PCR banks */ -} - /* Default to PCR 7 only */ #define TPM2_PCR_MASK_DEFAULT (UINT32_C(1) << 7) @@ -149,6 +173,9 @@ int tpm2_hash_alg_from_string(const char *alg); 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; const char *device; @@ -173,8 +200,6 @@ int tpm2_parse_pcr_argument(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); -int pcr_mask_to_string(uint32_t mask, char **ret); - int tpm2_util_pbkdf2_hmac_sha256(const void *pass, size_t passlen, const void *salt, diff --git a/src/test/meson.build b/src/test/meson.build index b57a6bf2f1..55c0881299 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -44,6 +44,7 @@ simple_tests += files( 'test-architecture.c', 'test-argv-util.c', 'test-barrier.c', + 'test-bitfield.c', 'test-bitmap.c', 'test-blockdev-util.c', 'test-bootspec.c', diff --git a/src/test/test-bitfield.c b/src/test/test-bitfield.c new file mode 100644 index 0000000000..74ebd5cc48 --- /dev/null +++ b/src/test/test-bitfield.c @@ -0,0 +1,227 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include <stddef.h> + +#include "bitfield.h" +#include "log.h" +#include "tests.h" + +#define TEST_BITS(bits, v, ...) \ + ({ \ + assert_se((!!BITS_SET(bits, ##__VA_ARGS__)) == v); \ + assert_se((!!BITS_SET(~(bits), ##__VA_ARGS__)) == !v); \ + }) +#define TEST_BIT(bits, v, i) \ + ({ \ + assert_se((!!BIT_SET(bits, i)) == v); \ + assert_se((!!BIT_SET(~(bits), i)) == !v); \ + TEST_BITS(bits, v, i); \ + }) + +#define TEST_BIT_SET(bits, i) TEST_BIT(bits, 1, i) +#define TEST_BIT_CLEAR(bits, i) TEST_BIT(bits, 0, i) + +#define TEST_BITS_SET(bits, ...) TEST_BITS(bits, 1, ##__VA_ARGS__) +#define TEST_BITS_CLEAR(bits, ...) TEST_BITS(bits, 0, ##__VA_ARGS__) + +TEST(bits) { + int count; + + /* Test uint8_t */ + TEST_BIT_SET(0x81, 0); + TEST_BIT_SET(0x81, 7); + TEST_BITS_SET(0x81, 0, 7); + TEST_BIT_CLEAR(0x81, 4); + TEST_BIT_CLEAR(0x81, 6); + TEST_BITS_CLEAR(0x81, 1, 2, 3, 4, 5, 6); + uint8_t expected8 = 0; + BIT_FOREACH(i, 0x81) + expected8 |= UINT8_C(1) << i; + assert_se(expected8 == 0x81); + uint8_t u8 = 0x91; + TEST_BIT_SET(u8, 4); + TEST_BITS_SET(u8, 0, 4, 7); + TEST_BIT_CLEAR(u8, 2); + TEST_BITS_CLEAR(u8, 1, 2, 3, 5, 6); + SET_BIT(u8, 1); + TEST_BITS_SET(u8, 0, 1, 4, 7); + TEST_BITS_CLEAR(u8, 2, 3, 5, 6); + SET_BITS(u8, 3, 5); + TEST_BITS_SET(u8, 0, 1, 3, 4, 5, 7); + TEST_BITS_CLEAR(u8, 2, 6); + CLEAR_BIT(u8, 4); + TEST_BITS_SET(u8, 0, 1, 3, 5, 7); + TEST_BITS_CLEAR(u8, 2, 4, 6); + CLEAR_BITS(u8, 1); + CLEAR_BITS(u8, 0, 7); + TEST_BITS_SET(u8, 3, 5); + TEST_BITS_CLEAR(u8, 0, 1, 2, 4, 6, 7); + expected8 = 0; + BIT_FOREACH(i, u8) + expected8 |= UINT8_C(1) << i; + assert_se(expected8 == u8); + u8 = 0; + TEST_BITS_CLEAR(u8, 0, 1, 2, 3, 4, 5, 6, 7); + BIT_FOREACH(i, u8) + assert_se(0); + u8 = ~u8; + TEST_BITS_SET(u8, 0, 1, 2, 3, 4, 5, 6, 7); + count = 0; + BIT_FOREACH(i, u8) + count++; + assert_se(count == 8); + uint8_t _u8 = u8; + SET_BITS(u8); + assert_se(_u8 == u8); + CLEAR_BITS(u8); + assert_se(_u8 == u8); + + /* Test uint16_t */ + TEST_BIT_SET(0x1f81, 10); + TEST_BITS_SET(0x1f81, 0, 7, 8, 9, 10, 11, 12); + TEST_BIT_CLEAR(0x1f81, 13); + TEST_BITS_CLEAR(0x1f81, 1, 2, 3, 4, 5, 6, 13, 14, 15); + uint16_t expected16 = 0; + BIT_FOREACH(i, 0x1f81) + expected16 |= UINT16_C(1) << i; + assert_se(expected16 == 0x1f81); + uint16_t u16 = 0xf060; + TEST_BIT_SET(u16, 12); + TEST_BITS_SET(u16, 5, 6, 12, 13, 14, 15); + TEST_BIT_CLEAR(u16, 9); + TEST_BITS_CLEAR(u16, 0, 1, 2, 3, 4, 7, 8, 9, 10, 11); + SET_BITS(u16, 1, 8); + TEST_BITS_SET(u16, 1, 5, 6, 8, 12, 13, 14, 15); + TEST_BITS_CLEAR(u16, 0, 2, 3, 4, 7, 9, 10, 11); + CLEAR_BITS(u16, 13, 14); + TEST_BITS_SET(u16, 1, 5, 6, 8, 12, 15); + TEST_BITS_CLEAR(u16, 0, 2, 3, 4, 7, 9, 10, 11, 13, 14); + expected16 = 0; + BIT_FOREACH(i, u16) + expected16 |= UINT16_C(1) << i; + assert_se(expected16 == u16); + u16 = 0; + TEST_BITS_CLEAR(u16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + BIT_FOREACH(i, u16) + assert_se(0); + u16 = ~u16; + TEST_BITS_SET(u16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + count = 0; + BIT_FOREACH(i, u16) + count++; + assert_se(count == 16); + uint16_t _u16 = u16; + SET_BITS(u16); + assert_se(_u16 == u16); + CLEAR_BITS(u16); + assert_se(_u16 == u16); + + /* Test uint32_t */ + TEST_BIT_SET(0x80224f10, 11); + TEST_BITS_SET(0x80224f10, 4, 8, 9, 10, 11, 14, 17, 21, 31); + TEST_BIT_CLEAR(0x80224f10, 28); + TEST_BITS_CLEAR(0x80224f10, 0, 1, 2, 3, 5, 6, 7, 12, 13, 15, 16, 18, 19, 20, 22, 23, 24, 25, 26, 27, 28, 29, 30); + uint32_t expected32 = 0; + BIT_FOREACH(i, 0x80224f10) + expected32 |= UINT32_C(1) << i; + assert_se(expected32 == 0x80224f10); + uint32_t u32 = 0x605e0388; + TEST_BIT_SET(u32, 3); + TEST_BIT_SET(u32, 30); + TEST_BITS_SET(u32, 3, 7, 8, 9, 17, 18, 19, 20, 22, 29, 30); + TEST_BIT_CLEAR(u32, 0); + TEST_BIT_CLEAR(u32, 31); + TEST_BITS_CLEAR(u32, 0, 1, 2, 4, 5, 6, 10, 11, 12, 13, 14, 15, 16, 21, 23, 24, 25, 26, 27, 28, 31); + SET_BITS(u32, 1, 25, 26); + TEST_BITS_SET(u32, 1, 3, 7, 8, 9, 17, 18, 19, 20, 22, 25, 26, 29, 30); + TEST_BITS_CLEAR(u32, 0, 2, 4, 5, 6, 10, 11, 12, 13, 14, 15, 16, 21, 23, 24, 27, 28, 31); + CLEAR_BITS(u32, 29, 17, 1); + TEST_BITS_SET(u32, 3, 7, 8, 9, 18, 19, 20, 22, 25, 26, 30); + TEST_BITS_CLEAR(u32, 0, 1, 2, 4, 5, 6, 10, 11, 12, 13, 14, 15, 16, 17, 21, 23, 24, 27, 28, 29, 31); + expected32 = 0; + BIT_FOREACH(i, u32) + expected32 |= UINT32_C(1) << i; + assert_se(expected32 == u32); + u32 = 0; + TEST_BITS_CLEAR(u32, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31); + BIT_FOREACH(i, u32) + assert_se(0); + u32 = ~u32; + TEST_BITS_SET(u32, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31); + count = 0; + BIT_FOREACH(i, u32) + count++; + assert_se(count == 32); + uint32_t _u32 = u32; + SET_BITS(u32); + assert_se(_u32 == u32); + CLEAR_BITS(u32); + assert_se(_u32 == u32); + + /* Test uint64_t */ + TEST_BIT_SET(0x18ba1400f4857460, 60); + TEST_BITS_SET(0x18ba1400f4857460, 5, 6, 10, 12, 13, 14, 16, 18, 23, 26, 28, 29, 30, 31, 42, 44, 49, 51, 52, 53, 55, 59, 60); + TEST_BIT_CLEAR(UINT64_C(0x18ba1400f4857460), 0); + TEST_BIT_CLEAR(UINT64_C(0x18ba1400f4857460), 63); + TEST_BITS_CLEAR(UINT64_C(0x18ba1400f4857460), 0, 1, 2, 3, 4, 7, 8, 9, 11, 15, 17, 19, 20, 21, 22, 24, 25, 27, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 43, 45, 46, 47, 48, 50, 54, 56, 57, 58, 61, 62, 63); + uint64_t expected64 = 0; + BIT_FOREACH(i, 0x18ba1400f4857460) + expected64 |= UINT64_C(1) << i; + assert_se(expected64 == 0x18ba1400f4857460); + uint64_t u64 = 0xa90e2d8507a65739; + TEST_BIT_SET(u64, 0); + TEST_BIT_SET(u64, 63); + TEST_BITS_SET(u64, 0, 3, 4, 5, 8, 9, 10, 12, 14, 17, 18, 21, 23, 24, 25, 26, 32, 34, 39, 40, 42, 43, 45, 49, 50, 51, 56, 59, 61, 63); + TEST_BIT_CLEAR(u64, 1); + TEST_BITS_CLEAR(u64, 1, 2, 6, 7, 11, 13, 15, 16, 19, 20, 22, 27, 28, 29, 30, 31, 33, 35, 36, 37, 38, 41, 44, 46, 47, 48, 52, 53, 54, 55, 57, 58, 60, 62); + SET_BIT(u64, 1); + TEST_BITS_SET(u64, 0, 1, 3, 4, 5, 8, 9, 10, 12, 14, 17, 18, 21, 23, 24, 25, 26, 32, 34, 39, 40, 42, 43, 45, 49, 50, 51, 56, 59, 61, 63); + TEST_BITS_CLEAR(u64, 2, 6, 7, 11, 13, 15, 16, 19, 20, 22, 27, 28, 29, 30, 31, 33, 35, 36, 37, 38, 41, 44, 46, 47, 48, 52, 53, 54, 55, 57, 58, 60, 62); + CLEAR_BIT(u64, 63); + TEST_BITS_SET(u64, 0, 1, 3, 4, 5, 8, 9, 10, 12, 14, 17, 18, 21, 23, 24, 25, 26, 32, 34, 39, 40, 42, 43, 45, 49, 50, 51, 56, 59, 61); + TEST_BITS_CLEAR(u64, 2, 6, 7, 11, 13, 15, 16, 19, 20, 22, 27, 28, 29, 30, 31, 33, 35, 36, 37, 38, 41, 44, 46, 47, 48, 52, 53, 54, 55, 57, 58, 60, 62, 63); + SET_BIT(u64, 62); + TEST_BITS_SET(u64, 0, 1, 3, 4, 5, 8, 9, 10, 12, 14, 17, 18, 21, 23, 24, 25, 26, 32, 34, 39, 40, 42, 43, 45, 49, 50, 51, 56, 59, 61, 62); + TEST_BITS_CLEAR(u64, 2, 6, 7, 11, 13, 15, 16, 19, 20, 22, 27, 28, 29, 30, 31, 33, 35, 36, 37, 38, 41, 44, 46, 47, 48, 52, 53, 54, 55, 57, 58, 60, 63); + SET_BITS(u64, 63, 62, 7, 13, 38, 40); + TEST_BITS_SET(u64, 0, 1, 3, 4, 5, 7, 8, 9, 10, 12, 13, 14, 17, 18, 21, 23, 24, 25, 26, 32, 34, 38, 39, 40, 42, 43, 45, 49, 50, 51, 56, 59, 61, 62, 63); + TEST_BITS_CLEAR(u64, 2, 6, 11, 15, 16, 19, 20, 22, 27, 28, 29, 30, 31, 33, 35, 36, 37, 41, 44, 46, 47, 48, 52, 53, 54, 55, 57, 58, 60); + CLEAR_BIT(u64, 32); + TEST_BITS_SET(u64, 0, 1, 3, 4, 5, 7, 8, 9, 10, 12, 13, 14, 17, 18, 21, 23, 24, 25, 26, 34, 38, 39, 40, 42, 43, 45, 49, 50, 51, 56, 59, 61, 62, 63); + TEST_BITS_CLEAR(u64, 2, 6, 11, 15, 16, 19, 20, 22, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 41, 44, 46, 47, 48, 52, 53, 54, 55, 57, 58, 60); + CLEAR_BITS(u64, 0, 2, 11, 63, 32, 58); + TEST_BITS_SET(u64, 1, 3, 4, 5, 7, 8, 9, 10, 12, 13, 14, 17, 18, 21, 23, 24, 25, 26, 34, 38, 39, 40, 42, 43, 45, 49, 50, 51, 56, 59, 61, 62); + TEST_BITS_CLEAR(u64, 0, 2, 6, 11, 15, 16, 19, 20, 22, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 41, 44, 46, 47, 48, 52, 53, 54, 55, 57, 58, 60, 63); + expected64 = 0; + BIT_FOREACH(i, u64) + expected64 |= UINT64_C(1) << i; + assert_se(expected64 == u64); + u64 = 0; + TEST_BITS_CLEAR(u64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63); + BIT_FOREACH(i, u64) + assert_se(0); + u64 = ~u64; + TEST_BITS_SET(u64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63); + count = 0; + BIT_FOREACH(i, u64) + count++; + assert_se(count == 64); + uint64_t _u64 = u64; + SET_BITS(u64); + assert_se(_u64 == u64); + CLEAR_BITS(u64); + assert_se(_u64 == u64); + + /* Verify these use cases are constant-folded. */ + assert_cc(__builtin_constant_p(INDEX_TO_MASK(uint8_t, 1))); + assert_cc(__builtin_constant_p(INDEX_TO_MASK(uint16_t, 1))); + assert_cc(__builtin_constant_p(INDEX_TO_MASK(uint32_t, 1))); + assert_cc(__builtin_constant_p(INDEX_TO_MASK(uint64_t, 1))); + + assert_cc(__builtin_constant_p(BIT_SET((uint8_t)2, 1))); + assert_cc(__builtin_constant_p(BIT_SET((uint16_t)2, 1))); + assert_cc(__builtin_constant_p(BIT_SET((uint32_t)2, 1))); + assert_cc(__builtin_constant_p(BIT_SET((uint64_t)2, 1))); +} + +DEFINE_TEST_MAIN(LOG_INFO); diff --git a/src/test/test-macro.c b/src/test/test-macro.c index ef74b8273e..637d6f1a49 100644 --- a/src/test/test-macro.c +++ b/src/test/test-macro.c @@ -300,6 +300,190 @@ TEST(foreach_pointer) { assert_se(k == 11); } +TEST(foreach_va_args) { + size_t i; + + i = 0; + uint8_t u8, u8_1 = 1, u8_2 = 2, u8_3 = 3; + VA_ARGS_FOREACH(u8, u8_2, 8, 0xff, u8_1, u8_3, 0, 1) { + switch(i++) { + case 0: assert_se(u8 == u8_2); break; + case 1: assert_se(u8 == 8); break; + case 2: assert_se(u8 == 0xff); break; + case 3: assert_se(u8 == u8_1); break; + case 4: assert_se(u8 == u8_3); break; + case 5: assert_se(u8 == 0); break; + case 6: assert_se(u8 == 1); break; + default: assert_se(false); + } + } + assert_se(i == 7); + i = 0; + VA_ARGS_FOREACH(u8, 0) { + assert_se(u8 == 0); + assert_se(i++ == 0); + } + assert_se(i == 1); + i = 0; + VA_ARGS_FOREACH(u8, 0xff) { + assert_se(u8 == 0xff); + assert_se(i++ == 0); + } + assert_se(i == 1); + VA_ARGS_FOREACH(u8) + assert_se(false); + + i = 0; + uint32_t u32, u32_1 = 0xffff0000, u32_2 = 10, u32_3 = 0xffff; + VA_ARGS_FOREACH(u32, 1, 100, u32_2, 1000, u32_3, u32_1, 1, 0) { + switch(i++) { + case 0: assert_se(u32 == 1); break; + case 1: assert_se(u32 == 100); break; + case 2: assert_se(u32 == u32_2); break; + case 3: assert_se(u32 == 1000); break; + case 4: assert_se(u32 == u32_3); break; + case 5: assert_se(u32 == u32_1); break; + case 6: assert_se(u32 == 1); break; + case 7: assert_se(u32 == 0); break; + default: assert_se(false); + } + } + assert_se(i == 8); + i = 0; + VA_ARGS_FOREACH(u32, 0) { + assert_se(u32 == 0); + assert_se(i++ == 0); + } + assert_se(i == 1); + i = 0; + VA_ARGS_FOREACH(u32, 1000) { + assert_se(u32 == 1000); + assert_se(i++ == 0); + } + assert_se(i == 1); + VA_ARGS_FOREACH(u32) + assert_se(false); + + i = 0; + uint64_t u64, u64_1 = 0xffffffffffffffff, u64_2 = 50, u64_3 = 0xffff; + VA_ARGS_FOREACH(u64, 44, 0, u64_3, 100, u64_2, u64_1, 50000) { + switch(i++) { + case 0: assert_se(u64 == 44); break; + case 1: assert_se(u64 == 0); break; + case 2: assert_se(u64 == u64_3); break; + case 3: assert_se(u64 == 100); break; + case 4: assert_se(u64 == u64_2); break; + case 5: assert_se(u64 == u64_1); break; + case 6: assert_se(u64 == 50000); break; + default: assert_se(false); + } + } + assert_se(i == 7); + i = 0; + VA_ARGS_FOREACH(u64, 0) { + assert_se(u64 == 0); + assert_se(i++ == 0); + } + assert_se(i == 1); + i = 0; + VA_ARGS_FOREACH(u64, 0xff00ff00000000) { + assert_se(u64 == 0xff00ff00000000); + assert_se(i++ == 0); + } + assert_se(i == 1); + VA_ARGS_FOREACH(u64) + assert_se(false); + + struct test { + int a; + char b; + }; + + i = 0; + struct test s, + s_1 = { .a = 0, .b = 'c', }, + s_2 = { .a = 100000, .b = 'z', }, + s_3 = { .a = 0xff, .b = 'q', }, + s_4 = { .a = 1, .b = 'x', }; + VA_ARGS_FOREACH(s, s_1, (struct test){ .a = 10, .b = 'd', }, s_2, (struct test){}, s_3, s_4) { + switch(i++) { + case 0: assert_se(s.a == 0 ); assert_se(s.b == 'c'); break; + case 1: assert_se(s.a == 10 ); assert_se(s.b == 'd'); break; + case 2: assert_se(s.a == 100000); assert_se(s.b == 'z'); break; + case 3: assert_se(s.a == 0 ); assert_se(s.b == 0 ); break; + case 4: assert_se(s.a == 0xff ); assert_se(s.b == 'q'); break; + case 5: assert_se(s.a == 1 ); assert_se(s.b == 'x'); break; + default: assert_se(false); + } + } + assert_se(i == 6); + i = 0; + VA_ARGS_FOREACH(s, (struct test){ .a = 1, .b = 'A', }) { + assert_se(s.a == 1); + assert_se(s.b == 'A'); + assert_se(i++ == 0); + } + assert_se(i == 1); + VA_ARGS_FOREACH(s) + assert_se(false); + + i = 0; + struct test *p, *p_1 = &s_1, *p_2 = &s_2, *p_3 = &s_3, *p_4 = &s_4; + VA_ARGS_FOREACH(p, p_1, NULL, p_2, p_3, NULL, p_4, NULL) { + switch(i++) { + case 0: assert_se(p == p_1); break; + case 1: assert_se(p == NULL); break; + case 2: assert_se(p == p_2); break; + case 3: assert_se(p == p_3); break; + case 4: assert_se(p == NULL); break; + case 5: assert_se(p == p_4); break; + case 6: assert_se(p == NULL); break; + default: assert_se(false); + } + } + assert_se(i == 7); + i = 0; + VA_ARGS_FOREACH(p, p_3) { + assert_se(p == p_3); + assert_se(i++ == 0); + } + assert_se(i == 1); + VA_ARGS_FOREACH(p) + assert_se(false); + + i = 0; + void *v, *v_1 = p_1, *v_2 = p_2, *v_3 = p_3; + uint32_t *u32p = &u32; + VA_ARGS_FOREACH(v, v_1, NULL, u32p, v_3, p_2, p_4, v_2, NULL) { + switch(i++) { + case 0: assert_se(v == v_1); break; + case 1: assert_se(v == NULL); break; + case 2: assert_se(v == u32p); break; + case 3: assert_se(v == v_3); break; + case 4: assert_se(v == p_2); break; + case 5: assert_se(v == p_4); break; + case 6: assert_se(v == v_2); break; + case 7: assert_se(v == NULL); break; + default: assert_se(false); + } + } + assert_se(i == 8); + i = 0; + VA_ARGS_FOREACH(v, NULL) { + assert_se(v == NULL); + assert_se(i++ == 0); + } + assert_se(i == 1); + i = 0; + VA_ARGS_FOREACH(v, v_1) { + assert_se(v == v_1); + assert_se(i++ == 0); + } + assert_se(i == 1); + VA_ARGS_FOREACH(v) + assert_se(false); +} + TEST(align_to) { assert_se(ALIGN_TO(0, 1) == 0); assert_se(ALIGN_TO(1, 1) == 1); diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c index 04e08490b3..20baa0f261 100644 --- a/src/test/test-tpm2.c +++ b/src/test/test-tpm2.c @@ -3,29 +3,29 @@ #include "tpm2-util.h" #include "tests.h" -static void test_tpm2_parse_pcrs_one(const char *s, uint32_t mask, int ret) { +static void test_tpm2_pcr_mask_from_string_one(const char *s, uint32_t mask, int ret) { uint32_t m; - assert_se(tpm2_parse_pcrs(s, &m) == ret); + assert_se(tpm2_pcr_mask_from_string(s, &m) == ret); if (ret >= 0) assert_se(m == mask); } -TEST(tpm2_parse_pcrs) { - test_tpm2_parse_pcrs_one("", 0, 0); - test_tpm2_parse_pcrs_one("0", 1, 0); - test_tpm2_parse_pcrs_one("1", 2, 0); - test_tpm2_parse_pcrs_one("0,1", 3, 0); - test_tpm2_parse_pcrs_one("0+1", 3, 0); - test_tpm2_parse_pcrs_one("0-1", 0, -EINVAL); - test_tpm2_parse_pcrs_one("0,1,2", 7, 0); - test_tpm2_parse_pcrs_one("0+1+2", 7, 0); - test_tpm2_parse_pcrs_one("0+1,2", 7, 0); - test_tpm2_parse_pcrs_one("0,1+2", 7, 0); - test_tpm2_parse_pcrs_one("0,2", 5, 0); - test_tpm2_parse_pcrs_one("0+2", 5, 0); - test_tpm2_parse_pcrs_one("foo", 0, -EINVAL); +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_util_pbkdf2_hmac_sha256) { @@ -69,4 +69,346 @@ TEST(tpm2_util_pbkdf2_hmac_sha256) { } } +#if HAVE_TPM2 + +#define POISON(type) \ + ({ \ + type _p; \ + memset(&_p, 0xaa, sizeof(_p)); \ + _p; \ + }) +#define POISON_TPML POISON(TPML_PCR_SELECTION) +#define POISON_TPMS POISON(TPMS_PCR_SELECTION) +#define POISON_U32 POISON(uint32_t) + +static void assert_tpms_pcr_selection_eq(TPMS_PCR_SELECTION *a, TPMS_PCR_SELECTION *b) { + assert_se(a); + assert_se(b); + + assert_se(a->hash == b->hash); + assert_se(a->sizeofSelect == b->sizeofSelect); + + for (size_t i = 0; i < a->sizeofSelect; i++) + assert_se(a->pcrSelect[i] == b->pcrSelect[i]); +} + +static void assert_tpml_pcr_selection_eq(TPML_PCR_SELECTION *a, TPML_PCR_SELECTION *b) { + assert_se(a); + assert_se(b); + + assert_se(a->count == b->count); + for (size_t i = 0; i < a->count; i++) + assert_tpms_pcr_selection_eq(&a->pcrSelections[i], &b->pcrSelections[i]); +} + +static void verify_tpms_pcr_selection(TPMS_PCR_SELECTION *s, uint32_t mask, TPMI_ALG_HASH hash) { + assert_se(s->hash == hash); + assert_se(s->sizeofSelect == 3); + assert_se(s->pcrSelect[0] == (mask & 0xff)); + assert_se(s->pcrSelect[1] == ((mask >> 8) & 0xff)); + 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); +} + +static void verify_tpml_pcr_selection(TPML_PCR_SELECTION *l, TPMS_PCR_SELECTION s[], size_t count) { + assert_se(l->count == count); + 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); + } +} + +static void _test_pcr_selection_mask_hash(uint32_t mask, TPMI_ALG_HASH hash) { + TPMS_PCR_SELECTION s = POISON_TPMS; + tpm2_tpms_pcr_selection_from_mask(mask, hash, &s); + verify_tpms_pcr_selection(&s, mask, hash); + + TPML_PCR_SELECTION l = POISON_TPML; + tpm2_tpml_pcr_selection_from_mask(mask, hash, &l); + verify_tpml_pcr_selection(&l, &s, 1); + verify_tpms_pcr_selection(&l.pcrSelections[0], mask, hash); + + uint32_t test_masks[] = { + 0x0, 0x1, 0x100, 0x10000, 0xf0f0f0, 0xaaaaaa, 0xffffff, + }; + for (unsigned i = 0; i < ELEMENTSOF(test_masks); i++) { + uint32_t test_mask = test_masks[i]; + + TPMS_PCR_SELECTION a = POISON_TPMS, b = POISON_TPMS, test_s = POISON_TPMS; + tpm2_tpms_pcr_selection_from_mask(test_mask, hash, &test_s); + + a = s; + b = test_s; + tpm2_tpms_pcr_selection_add(&a, &b); + verify_tpms_pcr_selection(&a, UPDATE_FLAG(mask, test_mask, true), hash); + verify_tpms_pcr_selection(&b, test_mask, hash); + + a = s; + b = test_s; + tpm2_tpms_pcr_selection_sub(&a, &b); + verify_tpms_pcr_selection(&a, UPDATE_FLAG(mask, test_mask, false), hash); + verify_tpms_pcr_selection(&b, test_mask, hash); + + a = s; + b = test_s; + tpm2_tpms_pcr_selection_move(&a, &b); + verify_tpms_pcr_selection(&a, UPDATE_FLAG(mask, test_mask, true), hash); + verify_tpms_pcr_selection(&b, 0, hash); + } +} + +TEST(tpms_pcr_selection_mask_and_hash) { + TPMI_ALG_HASH HASH_ALGS[] = { TPM2_ALG_SHA1, TPM2_ALG_SHA256, }; + + for (unsigned i = 0; i < ELEMENTSOF(HASH_ALGS); i++) + for (uint32_t m2 = 0; m2 <= 0xffffff; m2 += 0x30000) + for (uint32_t m1 = 0; m1 <= 0xffff; m1 += 0x300) + for (uint32_t m0 = 0; m0 <= 0xff; m0 += 0x3) + _test_pcr_selection_mask_hash(m0 | m1 | m2, HASH_ALGS[i]); +} + +static void _test_tpms_sw( + TPMI_ALG_HASH hash, + uint32_t mask, + const char *expected_str, + size_t expected_weight) { + + TPMS_PCR_SELECTION s = POISON_TPMS; + tpm2_tpms_pcr_selection_from_mask(mask, hash, &s); + + _cleanup_free_ char *tpms_str = tpm2_tpms_pcr_selection_to_string(&s); + assert_se(streq(tpms_str, expected_str)); + + assert_se(tpm2_tpms_pcr_selection_weight(&s) == expected_weight); + assert_se(tpm2_tpms_pcr_selection_is_empty(&s) == (expected_weight == 0)); +} + +TEST(tpms_pcr_selection_string_and_weight) { + TPMI_ALG_HASH sha1 = TPM2_ALG_SHA1, sha256 = TPM2_ALG_SHA256; + + _test_tpms_sw(sha1, 0, "sha1()", 0); + _test_tpms_sw(sha1, 1, "sha1(0)", 1); + _test_tpms_sw(sha1, 0xf, "sha1(0+1+2+3)", 4); + _test_tpms_sw(sha1, 0x00ff00, "sha1(8+9+10+11+12+13+14+15)", 8); + _test_tpms_sw(sha1, 0xffffff, "sha1(0+1+2+3+4+5+6+7+8+9+10+11+12+13+14+15+16+17+18+19+20+21+22+23)", 24); + _test_tpms_sw(sha256, 0, "sha256()", 0); + _test_tpms_sw(sha256, 1, "sha256(0)", 1); + _test_tpms_sw(sha256, 7, "sha256(0+1+2)", 3); + _test_tpms_sw(sha256, 0xf00000, "sha256(20+21+22+23)", 4); + _test_tpms_sw(sha256, 0xffffff, "sha256(0+1+2+3+4+5+6+7+8+9+10+11+12+13+14+15+16+17+18+19+20+21+22+23)", 24); +} + +static void _tpml_pcr_selection_add_tpms(TPMS_PCR_SELECTION s[], size_t count, TPML_PCR_SELECTION *ret) { + for (size_t i = 0; i < count; i++) + tpm2_tpml_pcr_selection_add_tpms_pcr_selection(ret, &s[i]); +} + +static void _tpml_pcr_selection_sub_tpms(TPMS_PCR_SELECTION s[], size_t count, TPML_PCR_SELECTION *ret) { + for (size_t i = 0; i < count; i++) + tpm2_tpml_pcr_selection_sub_tpms_pcr_selection(ret, &s[i]); +} + +static void _test_tpml_sw( + TPMS_PCR_SELECTION s[], + size_t count, + size_t expected_count, + const char *expected_str, + size_t expected_weight) { + + TPML_PCR_SELECTION l = {}; + _tpml_pcr_selection_add_tpms(s, count, &l); + assert_se(l.count == expected_count); + + _cleanup_free_ char *tpml_str = tpm2_tpml_pcr_selection_to_string(&l); + assert_se(streq(tpml_str, expected_str)); + + assert_se(tpm2_tpml_pcr_selection_weight(&l) == expected_weight); + assert_se(tpm2_tpml_pcr_selection_is_empty(&l) == (expected_weight == 0)); +} + +TEST(tpml_pcr_selection_string_and_weight) { + size_t size = 0xaa; + TPMI_ALG_HASH sha1 = TPM2_ALG_SHA1, + sha256 = TPM2_ALG_SHA256, + sha384 = TPM2_ALG_SHA384, + sha512 = TPM2_ALG_SHA512; + TPMS_PCR_SELECTION s[4] = { POISON_TPMS, POISON_TPMS, POISON_TPMS, POISON_TPMS, }; + + size = 0; + tpm2_tpms_pcr_selection_from_mask(0x000002, sha1 , &s[size++]); + tpm2_tpms_pcr_selection_from_mask(0x0080f0, sha384, &s[size++]); + tpm2_tpms_pcr_selection_from_mask(0x010100, sha512, &s[size++]); + tpm2_tpms_pcr_selection_from_mask(0xff0000, sha256, &s[size++]); + _test_tpml_sw(s, + size, + /* expected_count= */ 4, + "[sha1(1),sha384(4+5+6+7+15),sha512(8+16),sha256(16+17+18+19+20+21+22+23)]", + /* expected_weight= */ 16); + + size = 0; + tpm2_tpms_pcr_selection_from_mask(0x0403aa, sha512, &s[size++]); + tpm2_tpms_pcr_selection_from_mask(0x0080f0, sha256, &s[size++]); + _test_tpml_sw(s, + size, + /* expected_count= */ 2, + "[sha512(1+3+5+7+8+9+18),sha256(4+5+6+7+15)]", + /* expected_weight= */ 12); + + size = 0; + /* Empty hashes should be ignored */ + tpm2_tpms_pcr_selection_from_mask(0x0300ce, sha384, &s[size++]); + tpm2_tpms_pcr_selection_from_mask(0xffffff, sha512, &s[size++]); + tpm2_tpms_pcr_selection_from_mask(0x000000, sha1 , &s[size++]); + tpm2_tpms_pcr_selection_from_mask(0x330010, sha256, &s[size++]); + _test_tpml_sw(s, + size, + /* expected_count= */ 3, + "[sha384(1+2+3+6+7+16+17),sha512(0+1+2+3+4+5+6+7+8+9+10+11+12+13+14+15+16+17+18+19+20+21+22+23),sha256(4+16+17+20+21)]", + /* expected_weight= */ 36); + + size = 0; + /* Verify same-hash entries are properly combined. */ + tpm2_tpms_pcr_selection_from_mask(0x000001, sha1 , &s[size++]); + tpm2_tpms_pcr_selection_from_mask(0x000001, sha256, &s[size++]); + tpm2_tpms_pcr_selection_from_mask(0x000010, sha1 , &s[size++]); + tpm2_tpms_pcr_selection_from_mask(0x000010, sha256, &s[size++]); + _test_tpml_sw(s, + size, + /* expected_count= */ 2, + "[sha1(0+4),sha256(0+4)]", + /* expected_weight= */ 4); +} + +/* Test tpml add/sub by changing the tpms individually */ +static void _test_tpml_addsub_tpms( + TPML_PCR_SELECTION *start, + TPMS_PCR_SELECTION add[], + size_t add_count, + TPMS_PCR_SELECTION expected1[], + size_t expected1_count, + TPMS_PCR_SELECTION sub[], + size_t sub_count, + TPMS_PCR_SELECTION expected2[], + size_t expected2_count) { + + TPML_PCR_SELECTION l = *start; + + _tpml_pcr_selection_add_tpms(add, add_count, &l); + verify_tpml_pcr_selection(&l, expected1, expected1_count); + + _tpml_pcr_selection_sub_tpms(sub, sub_count, &l); + verify_tpml_pcr_selection(&l, expected2, expected2_count); +} + +/* Test tpml add/sub by creating new tpmls */ +static void _test_tpml_addsub_tpml( + TPML_PCR_SELECTION *start, + TPMS_PCR_SELECTION add[], + size_t add_count, + TPMS_PCR_SELECTION expected1[], + size_t expected1_count, + TPMS_PCR_SELECTION sub[], + size_t sub_count, + TPMS_PCR_SELECTION expected2[], + size_t expected2_count) { + + TPML_PCR_SELECTION l = {}; + tpm2_tpml_pcr_selection_add(&l, start); + assert_tpml_pcr_selection_eq(&l, start); + + TPML_PCR_SELECTION addl = {}; + _tpml_pcr_selection_add_tpms(add, add_count, &addl); + tpm2_tpml_pcr_selection_add(&l, &addl); + + TPML_PCR_SELECTION e1 = {}; + _tpml_pcr_selection_add_tpms(expected1, expected1_count, &e1); + assert_tpml_pcr_selection_eq(&l, &e1); + + TPML_PCR_SELECTION subl = {}; + _tpml_pcr_selection_add_tpms(sub, sub_count, &subl); + tpm2_tpml_pcr_selection_sub(&l, &subl); + + TPML_PCR_SELECTION e2 = {}; + _tpml_pcr_selection_add_tpms(expected2, expected2_count, &e2); + assert_tpml_pcr_selection_eq(&l, &e2); +} + +#define _test_tpml_addsub(...) \ + ({ \ + _test_tpml_addsub_tpms(__VA_ARGS__); \ + _test_tpml_addsub_tpml(__VA_ARGS__); \ + }) + +TEST(tpml_pcr_selection_add_sub) { + size_t add_count = 0xaa, expected1_count = 0xaa, sub_count = 0xaa, expected2_count = 0xaa; + TPMI_ALG_HASH sha1 = TPM2_ALG_SHA1, + sha256 = TPM2_ALG_SHA256, + sha384 = TPM2_ALG_SHA384, + sha512 = TPM2_ALG_SHA512; + TPML_PCR_SELECTION l = POISON_TPML; + TPMS_PCR_SELECTION add[4] = { POISON_TPMS, POISON_TPMS, POISON_TPMS, POISON_TPMS, }, + sub[4] = { POISON_TPMS, POISON_TPMS, POISON_TPMS, POISON_TPMS, }, + expected1[4] = { POISON_TPMS, POISON_TPMS, POISON_TPMS, POISON_TPMS, }, + expected2[4] = { POISON_TPMS, POISON_TPMS, POISON_TPMS, POISON_TPMS, }; + + l = (TPML_PCR_SELECTION){}; + add_count = 0; + expected1_count = 0; + sub_count = 0; + expected2_count = 0; + tpm2_tpms_pcr_selection_from_mask(0x010101, sha256, &add[add_count++]); + tpm2_tpms_pcr_selection_from_mask(0x101010, sha256, &add[add_count++]); + tpm2_tpms_pcr_selection_from_mask(0x0000ff, sha512, &add[add_count++]); + tpm2_tpms_pcr_selection_from_mask(0x111111, sha256, &expected1[expected1_count++]); + tpm2_tpms_pcr_selection_from_mask(0x0000ff, sha512, &expected1[expected1_count++]); + tpm2_tpms_pcr_selection_from_mask(0x000001, sha256, &sub[sub_count++]); + tpm2_tpms_pcr_selection_from_mask(0xff0000, sha512, &sub[sub_count++]); + tpm2_tpms_pcr_selection_from_mask(0x111110, sha256, &expected2[expected2_count++]); + tpm2_tpms_pcr_selection_from_mask(0x0000ff, sha512, &expected2[expected2_count++]); + _test_tpml_addsub(&l, + add, add_count, + expected1, expected1_count, + sub, sub_count, + expected2, expected2_count); + + l = (TPML_PCR_SELECTION){ + .count = 1, + .pcrSelections[0].hash = sha1, + .pcrSelections[0].sizeofSelect = 3, + .pcrSelections[0].pcrSelect[0] = 0xf0, + }; + add_count = 0; + expected1_count = 0; + sub_count = 0; + expected2_count = 0; + tpm2_tpms_pcr_selection_from_mask(0xff0000, sha256, &add[add_count++]); + tpm2_tpms_pcr_selection_from_mask(0xffff00, sha384, &add[add_count++]); + tpm2_tpms_pcr_selection_from_mask(0x0000ff, sha512, &add[add_count++]); + tpm2_tpms_pcr_selection_from_mask(0xf00000, sha1 , &add[add_count++]); + tpm2_tpms_pcr_selection_from_mask(0xf000f0, sha1 , &expected1[expected1_count++]); + tpm2_tpms_pcr_selection_from_mask(0xff0000, sha256, &expected1[expected1_count++]); + tpm2_tpms_pcr_selection_from_mask(0xffff00, sha384, &expected1[expected1_count++]); + tpm2_tpms_pcr_selection_from_mask(0x0000ff, sha512, &expected1[expected1_count++]); + tpm2_tpms_pcr_selection_from_mask(0x00ffff, sha256, &sub[sub_count++]); + tpm2_tpms_pcr_selection_from_mask(0xf000f0, sha1 , &expected2[expected2_count++]); + tpm2_tpms_pcr_selection_from_mask(0xff0000, sha256, &expected2[expected2_count++]); + tpm2_tpms_pcr_selection_from_mask(0xffff00, sha384, &expected2[expected2_count++]); + tpm2_tpms_pcr_selection_from_mask(0x0000ff, sha512, &expected2[expected2_count++]); + _test_tpml_addsub(&l, + add, add_count, + expected1, expected1_count, + sub, sub_count, + expected2, expected2_count); +} + +#endif /* HAVE_TPM2 */ + DEFINE_TEST_MAIN(LOG_DEBUG); |