diff mbox series

[5/6] KEYS: trusted: implement counter/timer policy

Message ID 20240524130459.21510-6-James.Bottomley@HansenPartnership.com (mailing list archive)
State New, archived
Headers show
Series Add policy to sealed keys | expand

Commit Message

James Bottomley May 24, 2024, 1:04 p.m. UTC
This is actually a generic policy allowing a range of comparisons
against any value set in the TPM Clock, which includes things like the
reset count, a monotonic millisecond count and the restart count.  The
most useful comparison is against the millisecond count for expiring
keys.  However, you have to remember that currently Linux doesn't try
to sync the epoch timer with the TPM, so the expiration is actually
measured in how long the TPM itself has been powered on ... the TPM
timer doesn't count while the system is powered down.  The millisecond
counter is a u64 quantity found at offset 8 in the timer structure,
and the <= comparision operand is 9, so a policy set to expire after the
TPM has been up for 100 seconds would look like

0000016d00000000000f424000080009

Where 0x16d is the counter timer policy code and 0xf4240 is 100 000 in
hex.

Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
---
 .../security/keys/trusted-encrypted.rst       | 29 +++++++++++++++
 include/linux/tpm.h                           |  1 +
 security/keys/trusted-keys/tpm2-policy.c      | 37 +++++++++++++++++++
 3 files changed, 67 insertions(+)

Comments

Jarkko Sakkinen July 16, 2024, 12:03 p.m. UTC | #1
On Fri May 24, 2024 at 4:04 PM EEST, James Bottomley wrote:
> This is actually a generic policy allowing a range of comparisons
> against any value set in the TPM Clock, which includes things like the
> reset count, a monotonic millisecond count and the restart count.  The
> most useful comparison is against the millisecond count for expiring
> keys.  However, you have to remember that currently Linux doesn't try
> to sync the epoch timer with the TPM, so the expiration is actually
> measured in how long the TPM itself has been powered on ... the TPM
> timer doesn't count while the system is powered down.  The millisecond
> counter is a u64 quantity found at offset 8 in the timer structure,
> and the <= comparision operand is 9, so a policy set to expire after the
> TPM has been up for 100 seconds would look like
>
> 0000016d00000000000f424000080009

These random magic hex numbers confuse and do not bring any clarity.

Also, please learn to use multiple paragraph when you write text.
>
> Where 0x16d is the counter timer policy code and 0xf4240 is 100 000 in
> hex.
>
> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>

Skip because enough stuff to address in the head.

BR, Jarkko
diff mbox series

Patch

diff --git a/Documentation/security/keys/trusted-encrypted.rst b/Documentation/security/keys/trusted-encrypted.rst
index fbb41cf16f30..7abda48089d8 100644
--- a/Documentation/security/keys/trusted-encrypted.rst
+++ b/Documentation/security/keys/trusted-encrypted.rst
@@ -545,3 +545,32 @@  DCP Blob Format
 
 .. kernel-doc:: security/keys/trusted-keys/trusted_dcp.c
    :identifiers: struct dcp_blob_fmt
+
+Appendix
+--------
+
+TPM 2.0 Policies
+----------------
+
+The current TPM supports PCR lock policies as documented above and
+CounterTimer policies which can be used to create expiring keys.  One
+caveat with expiring keys is that the TPM millisecond counter does not
+update while a system is powered off and Linux does not sync the TPM
+millisecond count with its internal clock, so the best you can expire
+in is in terms of how long any given TPM has been powered on.  (FIXME:
+Linux should simply update the millisecond clock to the current number
+of seconds past the epoch on boot).
+
+A CounterTimer policy is expressed in terms of length and offset
+against the TPM clock structure (TPMS_TIME_INFO), which looks like the
+packed structure::
+
+    struct tpms_time_info {
+	    u64 uptime;       /* time in ms since last start or reset */
+	    u64 clock;        /* cumulative uptime in ms */
+	    u32 resetcount;   /* number of times the TPM has been reset */
+	    u32 restartcount; /* number of times the TPM has been restarted */
+	    u8  safe          /* time was safely loaded from NVRam */
+    };
+
+The usual comparison for expiring keys is against clock, at offset 8.
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 154efceec0a4..894e51a7fe3a 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -291,6 +291,7 @@  enum tpm2_command_codes {
 	TPM2_CC_CONTEXT_SAVE	        = 0x0162,
 	TPM2_CC_FLUSH_CONTEXT	        = 0x0165,
 	TPM2_CC_POLICY_AUTHVALUE	= 0x016B,
+	TPM2_CC_POLICY_COUNTER_TIMER	= 0x016D,
 	TPM2_CC_READ_PUBLIC		= 0x0173,
 	TPM2_CC_START_AUTH_SESS		= 0x0176,
 	TPM2_CC_VERIFY_SIGNATURE        = 0x0177,
diff --git a/security/keys/trusted-keys/tpm2-policy.c b/security/keys/trusted-keys/tpm2-policy.c
index a731c10d9bba..c0508cb95923 100644
--- a/security/keys/trusted-keys/tpm2-policy.c
+++ b/security/keys/trusted-keys/tpm2-policy.c
@@ -63,6 +63,7 @@  static int tpm2_validate_policy(struct tpm2_policies *pols)
 
 	for (i = 0; i < pols->count; i++) {
 		switch (pols->code[i]) {
+		case TPM2_CC_POLICY_COUNTER_TIMER:
 		case TPM2_CC_POLICY_PCR:
 		case TPM2_CC_POLICY_AUTHVALUE:
 			break;
@@ -177,6 +178,7 @@  int tpm2_generate_policy_digest(struct tpm2_policies *pols,
 		u8 *policy = pols->policies[i];
 		int len = pols->len[i];
 		u32 cmd = pols->code[i];
+		u8 digest[MAX_DIGEST_SIZE];
 		u8 code[4];
 		SHASH_DESC_ON_STACK(sdesc, tfm);
 
@@ -192,6 +194,19 @@  int tpm2_generate_policy_digest(struct tpm2_policies *pols,
 		put_unaligned_be32(cmd, code);
 		crypto_shash_update(sdesc, code, 4);
 
+		/* commands that need special handling */
+		if (cmd == TPM2_CC_POLICY_COUNTER_TIMER) {
+			SHASH_DESC_ON_STACK(sdesc1, tfm);
+
+			sdesc1->tfm = tfm;
+
+			/* counter timer policies are double hashed */
+			crypto_shash_digest(sdesc1, policy, len,
+					    digest);
+			policy = digest;
+			len = *plen;
+		}
+
 		if (len)
 			crypto_shash_update(sdesc, policy, len);
 
@@ -302,6 +317,28 @@  int tpm2_get_policy_session(struct tpm_chip *chip, struct tpm2_policies *pols)
 				       pols->len[i] - pols->hash_size);
 			break;
 
+		case TPM2_CC_POLICY_COUNTER_TIMER: {
+			/*
+			 * the format of this is the last two u16
+			 * quantities are the offset and operation
+			 * respectively.  The rest is operandB which
+			 * must be zero padded in a hash digest
+			 */
+			u16 opb_len = pols->len[i] - 4;
+
+			if (opb_len > pols->hash_size)
+				return -EINVAL;
+
+			tpm_buf_append_u16(&buf, opb_len);
+			tpm_buf_append(&buf, pols->policies[i], opb_len);
+
+			/* offset and operand*/
+			tpm_buf_append(&buf, pols->policies[i] + opb_len, 4);
+			failure = "Counter Timer";
+
+			break;
+		}
+
 		default:
 			failure = "unknown policy";
 			if (pols->len[i])