Skip to content

Commit d2add27

Browse files
James Bottomleyjarkkojs
authored andcommitted
tpm: Add NULL primary creation
The session handling code uses a "salted" session, meaning a session whose salt is encrypted to the public part of another TPM key so an observer cannot obtain it (and thus deduce the session keys). This patch creates and context saves in the tpm_chip area the primary key of the NULL hierarchy for this purpose. [[email protected]: fixed documentation errors] Signed-off-by: James Bottomley <[email protected]> Reviewed-by: Jarkko Sakkinen <[email protected]> Tested-by: Jarkko Sakkinen <[email protected]> Signed-off-by: Jarkko Sakkinen <[email protected]>
1 parent fefb9f1 commit d2add27

File tree

6 files changed

+418
-0
lines changed

6 files changed

+418
-0
lines changed

drivers/char/tpm/Kconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,17 @@ menuconfig TCG_TPM
2727

2828
if TCG_TPM
2929

30+
config TCG_TPM2_HMAC
31+
bool "Use HMAC and encrypted transactions on the TPM bus"
32+
default y
33+
help
34+
Setting this causes us to deploy a scheme which uses request
35+
and response HMACs in addition to encryption for
36+
communicating with the TPM to prevent or detect bus snooping
37+
and interposer attacks (see tpm-security.rst). Saying Y
38+
here adds some encryption overhead to all kernel to TPM
39+
transactions.
40+
3041
config HW_RANDOM_TPM
3142
bool "TPM HW Random Number Generator support"
3243
depends on TCG_TPM && HW_RANDOM && !(TCG_TPM=y && HW_RANDOM=m)

drivers/char/tpm/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ tpm-y += eventlog/tpm1.o
1717
tpm-y += eventlog/tpm2.o
1818
tpm-y += tpm-buf.o
1919

20+
tpm-$(CONFIG_TCG_TPM2_HMAC) += tpm2-sessions.o
2021
tpm-$(CONFIG_ACPI) += tpm_ppi.o eventlog/acpi.o
2122
tpm-$(CONFIG_EFI) += eventlog/efi.o
2223
tpm-$(CONFIG_OF) += eventlog/of.o

drivers/char/tpm/tpm.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,4 +321,14 @@ void tpm_bios_log_setup(struct tpm_chip *chip);
321321
void tpm_bios_log_teardown(struct tpm_chip *chip);
322322
int tpm_dev_common_init(void);
323323
void tpm_dev_common_exit(void);
324+
325+
#ifdef CONFIG_TCG_TPM2_HMAC
326+
int tpm2_sessions_init(struct tpm_chip *chip);
327+
#else
328+
static inline int tpm2_sessions_init(struct tpm_chip *chip)
329+
{
330+
return 0;
331+
}
332+
#endif
333+
324334
#endif

drivers/char/tpm/tpm2-cmd.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,11 @@ int tpm2_auto_startup(struct tpm_chip *chip)
759759
rc = 0;
760760
}
761761

762+
if (rc)
763+
goto out;
764+
765+
rc = tpm2_sessions_init(chip);
766+
762767
out:
763768
/*
764769
* Infineon TPM in field upgrade mode will return no data for the number

drivers/char/tpm/tpm2-sessions.c

Lines changed: 322 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,322 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
/*
4+
* Copyright (C) 2018 [email protected]
5+
*
6+
*/
7+
8+
#include "tpm.h"
9+
#include <asm/unaligned.h>
10+
11+
/**
12+
* tpm2_parse_create_primary() - parse the data returned from TPM_CC_CREATE_PRIMARY
13+
*
14+
* @chip: The TPM the primary was created under
15+
* @buf: The response buffer from the chip
16+
* @handle: pointer to be filled in with the return handle of the primary
17+
* @hierarchy: The hierarchy the primary was created for
18+
*
19+
* Return:
20+
* * 0 - OK
21+
* * -errno - A system error
22+
* * TPM_RC - A TPM error
23+
*/
24+
static int tpm2_parse_create_primary(struct tpm_chip *chip, struct tpm_buf *buf,
25+
u32 *handle, u32 hierarchy)
26+
{
27+
struct tpm_header *head = (struct tpm_header *)buf->data;
28+
off_t offset_r = TPM_HEADER_SIZE, offset_t;
29+
u16 len = TPM_HEADER_SIZE;
30+
u32 total_len = be32_to_cpu(head->length);
31+
u32 val, param_len;
32+
33+
*handle = tpm_buf_read_u32(buf, &offset_r);
34+
param_len = tpm_buf_read_u32(buf, &offset_r);
35+
/*
36+
* param_len doesn't include the header, but all the other
37+
* lengths and offsets do, so add it to parm len to make
38+
* the comparisons easier
39+
*/
40+
param_len += TPM_HEADER_SIZE;
41+
42+
if (param_len + 8 > total_len)
43+
return -EINVAL;
44+
len = tpm_buf_read_u16(buf, &offset_r);
45+
offset_t = offset_r;
46+
/* now we have the public area, compute the name of the object */
47+
put_unaligned_be16(TPM_ALG_SHA256, chip->null_key_name);
48+
sha256(&buf->data[offset_r], len, chip->null_key_name + 2);
49+
50+
/* validate the public key */
51+
val = tpm_buf_read_u16(buf, &offset_t);
52+
53+
/* key type (must be what we asked for) */
54+
if (val != TPM_ALG_ECC)
55+
return -EINVAL;
56+
val = tpm_buf_read_u16(buf, &offset_t);
57+
58+
/* name algorithm */
59+
if (val != TPM_ALG_SHA256)
60+
return -EINVAL;
61+
val = tpm_buf_read_u32(buf, &offset_t);
62+
63+
/* object properties */
64+
if (val != TPM2_OA_TMPL)
65+
return -EINVAL;
66+
67+
/* auth policy (empty) */
68+
val = tpm_buf_read_u16(buf, &offset_t);
69+
if (val != 0)
70+
return -EINVAL;
71+
72+
/* symmetric key parameters */
73+
val = tpm_buf_read_u16(buf, &offset_t);
74+
if (val != TPM_ALG_AES)
75+
return -EINVAL;
76+
77+
/* symmetric key length */
78+
val = tpm_buf_read_u16(buf, &offset_t);
79+
if (val != AES_KEY_BITS)
80+
return -EINVAL;
81+
82+
/* symmetric encryption scheme */
83+
val = tpm_buf_read_u16(buf, &offset_t);
84+
if (val != TPM_ALG_CFB)
85+
return -EINVAL;
86+
87+
/* signing scheme */
88+
val = tpm_buf_read_u16(buf, &offset_t);
89+
if (val != TPM_ALG_NULL)
90+
return -EINVAL;
91+
92+
/* ECC Curve */
93+
val = tpm_buf_read_u16(buf, &offset_t);
94+
if (val != TPM2_ECC_NIST_P256)
95+
return -EINVAL;
96+
97+
/* KDF Scheme */
98+
val = tpm_buf_read_u16(buf, &offset_t);
99+
if (val != TPM_ALG_NULL)
100+
return -EINVAL;
101+
102+
/* extract public key (x and y points) */
103+
val = tpm_buf_read_u16(buf, &offset_t);
104+
if (val != EC_PT_SZ)
105+
return -EINVAL;
106+
memcpy(chip->null_ec_key_x, &buf->data[offset_t], val);
107+
offset_t += val;
108+
val = tpm_buf_read_u16(buf, &offset_t);
109+
if (val != EC_PT_SZ)
110+
return -EINVAL;
111+
memcpy(chip->null_ec_key_y, &buf->data[offset_t], val);
112+
offset_t += val;
113+
114+
/* original length of the whole TPM2B */
115+
offset_r += len;
116+
117+
/* should have exactly consumed the TPM2B public structure */
118+
if (offset_t != offset_r)
119+
return -EINVAL;
120+
if (offset_r > param_len)
121+
return -EINVAL;
122+
123+
/* creation data (skip) */
124+
len = tpm_buf_read_u16(buf, &offset_r);
125+
offset_r += len;
126+
if (offset_r > param_len)
127+
return -EINVAL;
128+
129+
/* creation digest (must be sha256) */
130+
len = tpm_buf_read_u16(buf, &offset_r);
131+
offset_r += len;
132+
if (len != SHA256_DIGEST_SIZE || offset_r > param_len)
133+
return -EINVAL;
134+
135+
/* TPMT_TK_CREATION follows */
136+
/* tag, must be TPM_ST_CREATION (0x8021) */
137+
val = tpm_buf_read_u16(buf, &offset_r);
138+
if (val != TPM2_ST_CREATION || offset_r > param_len)
139+
return -EINVAL;
140+
141+
/* hierarchy */
142+
val = tpm_buf_read_u32(buf, &offset_r);
143+
if (val != hierarchy || offset_r > param_len)
144+
return -EINVAL;
145+
146+
/* the ticket digest HMAC (might not be sha256) */
147+
len = tpm_buf_read_u16(buf, &offset_r);
148+
offset_r += len;
149+
if (offset_r > param_len)
150+
return -EINVAL;
151+
152+
/*
153+
* finally we have the name, which is a sha256 digest plus a 2
154+
* byte algorithm type
155+
*/
156+
len = tpm_buf_read_u16(buf, &offset_r);
157+
if (offset_r + len != param_len + 8)
158+
return -EINVAL;
159+
if (len != SHA256_DIGEST_SIZE + 2)
160+
return -EINVAL;
161+
162+
if (memcmp(chip->null_key_name, &buf->data[offset_r],
163+
SHA256_DIGEST_SIZE + 2) != 0) {
164+
dev_err(&chip->dev, "NULL Seed name comparison failed\n");
165+
return -EINVAL;
166+
}
167+
168+
return 0;
169+
}
170+
171+
/**
172+
* tpm2_create_primary() - create a primary key using a fixed P-256 template
173+
*
174+
* @chip: the TPM chip to create under
175+
* @hierarchy: The hierarchy handle to create under
176+
* @handle: The returned volatile handle on success
177+
*
178+
* For platforms that might not have a persistent primary, this can be
179+
* used to create one quickly on the fly (it uses Elliptic Curve not
180+
* RSA, so even slow TPMs can create one fast). The template uses the
181+
* TCG mandated H one for non-endorsement ECC primaries, i.e. P-256
182+
* elliptic curve (the only current one all TPM2s are required to
183+
* have) a sha256 name hash and no policy.
184+
*
185+
* Return:
186+
* * 0 - OK
187+
* * -errno - A system error
188+
* * TPM_RC - A TPM error
189+
*/
190+
static int tpm2_create_primary(struct tpm_chip *chip, u32 hierarchy,
191+
u32 *handle)
192+
{
193+
int rc;
194+
struct tpm_buf buf;
195+
struct tpm_buf template;
196+
197+
rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE_PRIMARY);
198+
if (rc)
199+
return rc;
200+
201+
rc = tpm_buf_init_sized(&template);
202+
if (rc) {
203+
tpm_buf_destroy(&buf);
204+
return rc;
205+
}
206+
207+
/*
208+
* create the template. Note: in order for userspace to
209+
* verify the security of the system, it will have to create
210+
* and certify this NULL primary, meaning all the template
211+
* parameters will have to be identical, so conform exactly to
212+
* the TCG TPM v2.0 Provisioning Guidance for the SRK ECC
213+
* key H template (H has zero size unique points)
214+
*/
215+
216+
/* key type */
217+
tpm_buf_append_u16(&template, TPM_ALG_ECC);
218+
219+
/* name algorithm */
220+
tpm_buf_append_u16(&template, TPM_ALG_SHA256);
221+
222+
/* object properties */
223+
tpm_buf_append_u32(&template, TPM2_OA_TMPL);
224+
225+
/* sauth policy (empty) */
226+
tpm_buf_append_u16(&template, 0);
227+
228+
/* BEGIN parameters: key specific; for ECC*/
229+
230+
/* symmetric algorithm */
231+
tpm_buf_append_u16(&template, TPM_ALG_AES);
232+
233+
/* bits for symmetric algorithm */
234+
tpm_buf_append_u16(&template, AES_KEY_BITS);
235+
236+
/* algorithm mode (must be CFB) */
237+
tpm_buf_append_u16(&template, TPM_ALG_CFB);
238+
239+
/* scheme (NULL means any scheme) */
240+
tpm_buf_append_u16(&template, TPM_ALG_NULL);
241+
242+
/* ECC Curve ID */
243+
tpm_buf_append_u16(&template, TPM2_ECC_NIST_P256);
244+
245+
/* KDF Scheme */
246+
tpm_buf_append_u16(&template, TPM_ALG_NULL);
247+
248+
/* unique: key specific; for ECC it is two zero size points */
249+
tpm_buf_append_u16(&template, 0);
250+
tpm_buf_append_u16(&template, 0);
251+
252+
/* END parameters */
253+
254+
/* primary handle */
255+
tpm_buf_append_u32(&buf, hierarchy);
256+
tpm_buf_append_empty_auth(&buf, TPM2_RS_PW);
257+
258+
/* sensitive create size is 4 for two empty buffers */
259+
tpm_buf_append_u16(&buf, 4);
260+
261+
/* sensitive create auth data (empty) */
262+
tpm_buf_append_u16(&buf, 0);
263+
264+
/* sensitive create sensitive data (empty) */
265+
tpm_buf_append_u16(&buf, 0);
266+
267+
/* the public template */
268+
tpm_buf_append(&buf, template.data, template.length);
269+
tpm_buf_destroy(&template);
270+
271+
/* outside info (empty) */
272+
tpm_buf_append_u16(&buf, 0);
273+
274+
/* creation PCR (none) */
275+
tpm_buf_append_u32(&buf, 0);
276+
277+
rc = tpm_transmit_cmd(chip, &buf, 0,
278+
"attempting to create NULL primary");
279+
280+
if (rc == TPM2_RC_SUCCESS)
281+
rc = tpm2_parse_create_primary(chip, &buf, handle, hierarchy);
282+
283+
tpm_buf_destroy(&buf);
284+
285+
return rc;
286+
}
287+
288+
static int tpm2_create_null_primary(struct tpm_chip *chip)
289+
{
290+
u32 null_key;
291+
int rc;
292+
293+
rc = tpm2_create_primary(chip, TPM2_RH_NULL, &null_key);
294+
295+
if (rc == TPM2_RC_SUCCESS) {
296+
unsigned int offset = 0; /* dummy offset for null key context */
297+
298+
rc = tpm2_save_context(chip, null_key, chip->null_key_context,
299+
sizeof(chip->null_key_context), &offset);
300+
tpm2_flush_context(chip, null_key);
301+
}
302+
303+
return rc;
304+
}
305+
306+
/**
307+
* tpm2_sessions_init() - start of day initialization for the sessions code
308+
* @chip: TPM chip
309+
*
310+
* Derive and context save the null primary and allocate memory in the
311+
* struct tpm_chip for the authorizations.
312+
*/
313+
int tpm2_sessions_init(struct tpm_chip *chip)
314+
{
315+
int rc;
316+
317+
rc = tpm2_create_null_primary(chip);
318+
if (rc)
319+
dev_err(&chip->dev, "TPM: security failed (NULL seed derivation): %d\n", rc);
320+
321+
return rc;
322+
}

0 commit comments

Comments
 (0)