report: Add the session audit digest algorithm to the TPM2 signature
When generating a session audit digest, the algorithm isn't included in the attestation. The algorithm is the auth digest algorithm that the TPM session is created with, which is currently hardcoded as SHA-256. This might not always be the case though, and verifiers shouldn't have to guess what the digest algorithm is, so include the algorithm in the report signature component.
This commit is contained in:
committed by
Luca Boccassi
parent
e8142670aa
commit
efe5e4d271
@@ -586,6 +586,14 @@ static int reply_report(sd_varlink *link, const Tpm2Report *report, const struct
|
||||
return log_error_errno(r, "Cannot convert NvPCR public area to JSON: %m");
|
||||
}
|
||||
|
||||
const char *session_audit_hash_alg = NULL;
|
||||
if (c->type == TPM2_REPORT_TYPE_SESSION_AUDIT) {
|
||||
session_audit_hash_alg = tpm2_hash_alg_to_string_tss2(c->session_audit_hash_alg);
|
||||
if (!session_audit_hash_alg)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
|
||||
"Unsupported session audit hash algorithm 0x%" PRIx16, c->session_audit_hash_alg);
|
||||
}
|
||||
|
||||
TPMT_SIG_SCHEME scheme = {
|
||||
.scheme = c->signature->sigAlg,
|
||||
};
|
||||
@@ -616,6 +624,7 @@ static int reply_report(sd_varlink *link, const Tpm2Report *report, const struct
|
||||
SD_JSON_BUILD_PAIR_CONDITION(c->type == TPM2_REPORT_TYPE_NVPCR, "nvpcrName", SD_JSON_BUILD_STRING(c->nv_pcr_name)),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(c->type == TPM2_REPORT_TYPE_NVPCR, "nvPublic", SD_JSON_BUILD_VARIANT(nv_public)),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(c->type != TPM2_REPORT_TYPE_SESSION_AUDIT && c->authenticated_data, "authenticatedData", SD_JSON_BUILD_STRING(c->authenticated_data)),
|
||||
JSON_BUILD_PAIR_STRING_NON_EMPTY("sessionAuditHashAlg", session_audit_hash_alg),
|
||||
SD_JSON_BUILD_PAIR_VARIANT("attestInfo", attest_info),
|
||||
SD_JSON_BUILD_PAIR_VARIANT("signature", sig),
|
||||
SD_JSON_BUILD_PAIR_STRING("signaturePEM", sig_pem));
|
||||
|
||||
@@ -161,8 +161,10 @@ static int tpm2_generate_report_try(
|
||||
* Audit session exclusivity provides evidence that the sequence of attestations reflects a single
|
||||
* and consistent snapshot of the machine's state. */
|
||||
|
||||
const TPMI_ALG_HASH audit_session_alg = TPM2_ALG_SHA256;
|
||||
|
||||
_cleanup_(tpm2_handle_freep) Tpm2Handle *audit_session = NULL;
|
||||
r = tpm2_make_exclusive_audit_session(c, &audit_session);
|
||||
r = tpm2_make_exclusive_audit_session(c, audit_session_alg, &audit_session);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -309,6 +311,7 @@ static int tpm2_generate_report_try(
|
||||
return log_oom_debug();
|
||||
components[n_components++] = (Tpm2ReportComponent) {
|
||||
.type = TPM2_REPORT_TYPE_SESSION_AUDIT,
|
||||
.session_audit_hash_alg = audit_session_alg,
|
||||
.attestation = TAKE_PTR(audit_info),
|
||||
.signature = TAKE_PTR(audit_signature),
|
||||
};
|
||||
|
||||
@@ -39,6 +39,9 @@ typedef struct Tpm2ReportComponent {
|
||||
* data is the report digest we get. */
|
||||
char *authenticated_data;
|
||||
|
||||
/* For session audit only */
|
||||
TPMI_ALG_HASH session_audit_hash_alg;
|
||||
|
||||
/* Common fields */
|
||||
TPMS_ATTEST *attestation;
|
||||
TPMT_SIGNATURE *signature;
|
||||
|
||||
@@ -4538,7 +4538,7 @@ static bool tpm2_is_audit_session(Tpm2Context *c, const Tpm2Handle *session) {
|
||||
return flags & TPMA_SESSION_AUDIT;
|
||||
}
|
||||
|
||||
int tpm2_make_exclusive_audit_session(Tpm2Context *c, Tpm2Handle **ret_session) {
|
||||
int tpm2_make_exclusive_audit_session(Tpm2Context *c, TPMI_ALG_HASH hash, Tpm2Handle **ret_session) {
|
||||
TSS2_RC rc;
|
||||
int r;
|
||||
|
||||
@@ -4547,6 +4547,11 @@ int tpm2_make_exclusive_audit_session(Tpm2Context *c, Tpm2Handle **ret_session)
|
||||
|
||||
log_debug("Starting HMAC exclusive audit session.");
|
||||
|
||||
/* The caller can specify a hash algorithm, but we only support SHA-256 for now. */
|
||||
if (hash != TPM2_ALG_SHA256)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Unsupported hash algorithm for audit session: only SHA-256 is supported");
|
||||
|
||||
_cleanup_(tpm2_handle_freep) Tpm2Handle *session = NULL;
|
||||
r = tpm2_handle_new(c, &session);
|
||||
if (r < 0)
|
||||
@@ -4562,7 +4567,7 @@ int tpm2_make_exclusive_audit_session(Tpm2Context *c, Tpm2Handle **ret_session)
|
||||
/* nonceCaller= */ NULL,
|
||||
TPM2_SE_HMAC,
|
||||
&SESSION_TEMPLATE_SYM_NULL,
|
||||
TPM2_ALG_SHA256,
|
||||
hash,
|
||||
&session->esys_handle);
|
||||
if (rc != TSS2_RC_SUCCESS)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
|
||||
@@ -10404,7 +10409,7 @@ int tpm2_tpmt_public_to_pem(const TPMT_PUBLIC *public, char **ret) {
|
||||
#endif
|
||||
}
|
||||
|
||||
static const char* tpm2_hash_alg_to_string_tss2(TPMI_ALG_HASH alg) {
|
||||
const char* tpm2_hash_alg_to_string_tss2(TPMI_ALG_HASH alg) {
|
||||
/* Note that this is different to tpm2_hash_alg_to_string because it
|
||||
* returns a string that aligns with those produced by TSS2 and compatible
|
||||
* with the TCG TSS2 JSON format. The JSON format is actually not case
|
||||
|
||||
@@ -143,7 +143,7 @@ int tpm2_pcr_values_hash_count(const Tpm2PCRValue *pcr_values, size_t n_pcr_valu
|
||||
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_make_encryption_session(Tpm2Context *c, const Tpm2Handle *primary, const Tpm2Handle *bind_key, Tpm2Handle **ret_session);
|
||||
int tpm2_make_exclusive_audit_session(Tpm2Context *c, Tpm2Handle **ret_session);
|
||||
int tpm2_make_exclusive_audit_session(Tpm2Context *c, TPMI_ALG_HASH hash, Tpm2Handle **ret_session);
|
||||
|
||||
int tpm2_create_primary(Tpm2Context *c, const Tpm2Handle *session, ESYS_TR hierarchy, 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);
|
||||
@@ -428,6 +428,7 @@ int tpm2_hmac_key_from_pin(Tpm2Context *c, const Tpm2Handle *session, const TPM2
|
||||
int tpm2_tpmt_signature_to_pem(const TPMT_SIGNATURE *signature, char **ret);
|
||||
int tpm2_tpmt_public_to_pem(const TPMT_PUBLIC *public, char **ret);
|
||||
|
||||
const char* tpm2_hash_alg_to_string_tss2(TPMI_ALG_HASH alg);
|
||||
int tpm2_tpmt_signature_to_json(const TPMT_SIGNATURE *signature, sd_json_variant **ret);
|
||||
int tpm2_attest_info_to_json(const TPMT_SIG_SCHEME *scheme, const TPMS_ATTEST *attest, sd_json_variant **ret);
|
||||
int tpm2_tpmt_public_to_json(const TPMT_PUBLIC *public, sd_json_variant **ret);
|
||||
|
||||
@@ -2328,7 +2328,7 @@ static void check_get_session_audit_digest(Tpm2Context *c) {
|
||||
ASSERT_OK(tpm2_create_primary(c, NULL, ESYS_TR_RH_OWNER, &template, NULL, NULL, &key));
|
||||
|
||||
_cleanup_(tpm2_handle_freep) Tpm2Handle *session = NULL;
|
||||
ASSERT_OK(tpm2_make_exclusive_audit_session(c, &session));
|
||||
ASSERT_OK(tpm2_make_exclusive_audit_session(c, TPM2_ALG_SHA256, &session));
|
||||
|
||||
/* Use the session */
|
||||
TPML_PCR_SELECTION pcrs;
|
||||
|
||||
@@ -142,13 +142,6 @@ def hash(alg, data):
|
||||
return h.finalize()
|
||||
|
||||
|
||||
def sha256(data):
|
||||
"""Digest the supplied data using SHA-256."""
|
||||
h = hashes.Hash(hashes.SHA256())
|
||||
h.update(data)
|
||||
return h.finalize()
|
||||
|
||||
|
||||
# Helpers to serialize JSON encodings to the TPM wire format.
|
||||
|
||||
def marshal_u8(v):
|
||||
@@ -356,9 +349,9 @@ def check_nvpcr(i, comp):
|
||||
sys.exit(f"component {i}: authenticatedData digest does not match attested extraData")
|
||||
|
||||
|
||||
def check_session_audit(doc, key_name):
|
||||
def check_session_audit(alg, doc, key_name):
|
||||
"""Check the properties of the session-audit component."""
|
||||
digest = b"\x00" * 32 # Starting audit digest
|
||||
digest = b"\x00" * HASHES[alg]().digest_size # Starting audit digest
|
||||
|
||||
reported = None
|
||||
for comp in doc["components"]:
|
||||
@@ -392,11 +385,11 @@ def check_session_audit(doc, key_name):
|
||||
rp += marshal_tpmt_signature(comp["signature"]) # signature
|
||||
|
||||
# Calculate cpHash and rpHash.
|
||||
cp_hash = sha256(marshal_u32(cc) + names + cp)
|
||||
rp_hash = sha256(marshal_u32(0) + marshal_u32(cc) + rp)
|
||||
cp_hash = hash(alg, marshal_u32(cc) + names + cp)
|
||||
rp_hash = hash(alg, marshal_u32(0) + marshal_u32(cc) + rp)
|
||||
|
||||
# Extend the audit digest.
|
||||
digest = sha256(digest + cp_hash + rp_hash)
|
||||
digest = hash(alg, digest + cp_hash + rp_hash)
|
||||
|
||||
if reported is None:
|
||||
sys.exit("no session audit component present")
|
||||
@@ -419,6 +412,7 @@ def main():
|
||||
sys.exit("publicKeyPEM does not match the JSON public key")
|
||||
|
||||
saw_report_binding = False
|
||||
session_audit_digest_alg = None
|
||||
for i, comp in enumerate(data["components"]):
|
||||
scheme = comp["attestInfo"]["sig_scheme"]
|
||||
message = marshal_tpms_attest(comp["attestInfo"]["attest"])
|
||||
@@ -454,6 +448,12 @@ def main():
|
||||
sys.exit(f"component {i}: session audit extraData does not match the report digest")
|
||||
saw_report_binding = True
|
||||
|
||||
# This is hard-coded as SHA256 for now.
|
||||
alg = comp.get("sessionAuditHashAlg")
|
||||
if alg is None:
|
||||
sys.exit(f"component {i}: missing sessionAuditHashAlg field")
|
||||
session_audit_digest_alg = alg
|
||||
|
||||
print(comp["type"])
|
||||
|
||||
if not saw_report_binding:
|
||||
@@ -462,7 +462,7 @@ def main():
|
||||
# The session audit digest must reproduce the audited command sequence that
|
||||
# produced the other components.
|
||||
key_name = digest_bytes_and_marshal_tpmt_ha(data["publicKey"]["nameAlg"], marshal_tpmt_public(data["publicKey"]))
|
||||
check_session_audit(data, key_name)
|
||||
check_session_audit(session_audit_digest_alg, data, key_name)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user