Skip to content

Commit 699e3ef

Browse files
James Bottomleyjarkkojs
James Bottomley
authored andcommitted
tpm: Add HMAC session start and end functions
Add session based HMAC authentication plus parameter decryption and response encryption using AES. The basic design is to segregate all the nasty crypto, hash and hmac code into tpm2-sessions.c and export a usable API. The API first of all starts off by gaining a session with tpm2_start_auth_session() which initiates a session with the TPM and allocates an opaque tpm2_auth structure to handle the session parameters. The design is that session use will be single threaded from start to finish under the ops lock, so the tpm2_auth structure is stored in struct tpm2_chip to simpify the externally visible API. The session can be ended with tpm2_end_auth_session() which is designed only to be used in error legs. Ordinarily the further session API (future patches) will end or continue the session appropriately without having to call this. Signed-off-by: James Bottomley <[email protected]> Reviewed-by: Ard Biesheuvel <[email protected]> # crypto API parts Reviewed-by: Jarkko Sakkinen <[email protected]> Tested-by: Jarkko Sakkinen <[email protected]> Signed-off-by: Jarkko Sakkinen <[email protected]>
1 parent 033ee84 commit 699e3ef

File tree

5 files changed

+325
-0
lines changed

5 files changed

+325
-0
lines changed

drivers/char/tpm/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ if TCG_TPM
3030
config TCG_TPM2_HMAC
3131
bool "Use HMAC and encrypted transactions on the TPM bus"
3232
default y
33+
select CRYPTO_ECDH
34+
select CRYPTO_LIB_AESCFB
3335
select CRYPTO_LIB_SHA256
3436
help
3537
Setting this causes us to deploy a scheme which uses request

drivers/char/tpm/tpm-buf.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ void tpm_buf_reset(struct tpm_buf *buf, u16 tag, u32 ordinal)
4444
head->tag = cpu_to_be16(tag);
4545
head->length = cpu_to_be32(sizeof(*head));
4646
head->ordinal = cpu_to_be32(ordinal);
47+
buf->handles = 0;
4748
}
4849
EXPORT_SYMBOL_GPL(tpm_buf_reset);
4950

drivers/char/tpm/tpm-chip.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,9 @@ static void tpm_dev_release(struct device *dev)
275275
kfree(chip->work_space.context_buf);
276276
kfree(chip->work_space.session_buf);
277277
kfree(chip->allocated_banks);
278+
#ifdef CONFIG_TCG_TPM2_HMAC
279+
kfree(chip->auth);
280+
#endif
278281
kfree(chip);
279282
}
280283

drivers/char/tpm/tpm2-sessions.c

Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,101 @@
33
/*
44
* Copyright (C) 2018 [email protected]
55
*
6+
* Cryptographic helper routines for handling TPM2 sessions for
7+
* authorization HMAC and request response encryption.
8+
*
9+
* The idea is to ensure that every TPM command is HMAC protected by a
10+
* session, meaning in-flight tampering would be detected and in
11+
* addition all sensitive inputs and responses should be encrypted.
12+
*
13+
* The basic way this works is to use a TPM feature called salted
14+
* sessions where a random secret used in session construction is
15+
* encrypted to the public part of a known TPM key. The problem is we
16+
* have no known keys, so initially a primary Elliptic Curve key is
17+
* derived from the NULL seed (we use EC because most TPMs generate
18+
* these keys much faster than RSA ones). The curve used is NIST_P256
19+
* because that's now mandated to be present in 'TCG TPM v2.0
20+
* Provisioning Guidance'
21+
*
22+
* Threat problems: the initial TPM2_CreatePrimary is not (and cannot
23+
* be) session protected, so a clever Man in the Middle could return a
24+
* public key they control to this command and from there intercept
25+
* and decode all subsequent session based transactions. The kernel
26+
* cannot mitigate this threat but, after boot, userspace can get
27+
* proof this has not happened by asking the TPM to certify the NULL
28+
* key. This certification would chain back to the TPM Endorsement
29+
* Certificate and prove the NULL seed primary had not been tampered
30+
* with and thus all sessions must have been cryptographically secure.
31+
* To assist with this, the initial NULL seed public key name is made
32+
* available in a sysfs file.
33+
*
34+
* Use of these functions:
35+
*
36+
* The design is all the crypto, hash and hmac gunk is confined in this
37+
* file and never needs to be seen even by the kernel internal user. To
38+
* the user there's an init function tpm2_sessions_init() that needs to
39+
* be called once per TPM which generates the NULL seed primary key.
40+
*
41+
* These are the usage functions:
42+
*
43+
* tpm2_start_auth_session() which allocates the opaque auth structure
44+
* and gets a session from the TPM. This must be called before
45+
* any of the following functions. The session is protected by a
46+
* session_key which is derived from a random salt value
47+
* encrypted to the NULL seed.
48+
* tpm2_end_auth_session() kills the session and frees the resources.
49+
* Under normal operation this function is done by
50+
* tpm_buf_check_hmac_response(), so this is only to be used on
51+
* error legs where the latter is not executed.
652
*/
753

854
#include "tpm.h"
55+
#include <linux/random.h>
56+
#include <linux/scatterlist.h>
957
#include <asm/unaligned.h>
58+
#include <crypto/kpp.h>
59+
#include <crypto/ecdh.h>
1060
#include <crypto/hash.h>
1161
#include <crypto/hmac.h>
1262

63+
/*
64+
* This is the structure that carries all the auth information (like
65+
* session handle, nonces, session key and auth) from use to use it is
66+
* designed to be opaque to anything outside.
67+
*/
68+
struct tpm2_auth {
69+
u32 handle;
70+
/*
71+
* This has two meanings: before tpm_buf_fill_hmac_session()
72+
* it marks the offset in the buffer of the start of the
73+
* sessions (i.e. after all the handles). Once the buffer has
74+
* been filled it markes the session number of our auth
75+
* session so we can find it again in the response buffer.
76+
*
77+
* The two cases are distinguished because the first offset
78+
* must always be greater than TPM_HEADER_SIZE and the second
79+
* must be less than or equal to 5.
80+
*/
81+
u32 session;
82+
/*
83+
* the size here is variable and set by the size of our_nonce
84+
* which must be between 16 and the name hash length. we set
85+
* the maximum sha256 size for the greatest protection
86+
*/
87+
u8 our_nonce[SHA256_DIGEST_SIZE];
88+
u8 tpm_nonce[SHA256_DIGEST_SIZE];
89+
/*
90+
* the salt is only used across the session command/response
91+
* after that it can be used as a scratch area
92+
*/
93+
union {
94+
u8 salt[EC_PT_SZ];
95+
/* scratch for key + IV */
96+
u8 scratch[AES_KEY_BYTES + AES_BLOCK_SIZE];
97+
};
98+
u8 session_key[SHA256_DIGEST_SIZE];
99+
};
100+
13101
/*
14102
* It turns out the crypto hmac(sha256) is hard for us to consume
15103
* because it assumes a fixed key and the TPM seems to change the key
@@ -113,6 +201,199 @@ static void tpm2_KDFe(u8 z[EC_PT_SZ], const char *str, u8 *pt_u, u8 *pt_v,
113201
sha256_final(&sctx, out);
114202
}
115203

204+
static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip)
205+
{
206+
struct crypto_kpp *kpp;
207+
struct kpp_request *req;
208+
struct scatterlist s[2], d[1];
209+
struct ecdh p = {0};
210+
u8 encoded_key[EC_PT_SZ], *x, *y;
211+
unsigned int buf_len;
212+
213+
/* secret is two sized points */
214+
tpm_buf_append_u16(buf, (EC_PT_SZ + 2)*2);
215+
/*
216+
* we cheat here and append uninitialized data to form
217+
* the points. All we care about is getting the two
218+
* co-ordinate pointers, which will be used to overwrite
219+
* the uninitialized data
220+
*/
221+
tpm_buf_append_u16(buf, EC_PT_SZ);
222+
x = &buf->data[tpm_buf_length(buf)];
223+
tpm_buf_append(buf, encoded_key, EC_PT_SZ);
224+
tpm_buf_append_u16(buf, EC_PT_SZ);
225+
y = &buf->data[tpm_buf_length(buf)];
226+
tpm_buf_append(buf, encoded_key, EC_PT_SZ);
227+
sg_init_table(s, 2);
228+
sg_set_buf(&s[0], x, EC_PT_SZ);
229+
sg_set_buf(&s[1], y, EC_PT_SZ);
230+
231+
kpp = crypto_alloc_kpp("ecdh-nist-p256", CRYPTO_ALG_INTERNAL, 0);
232+
if (IS_ERR(kpp)) {
233+
dev_err(&chip->dev, "crypto ecdh allocation failed\n");
234+
return;
235+
}
236+
237+
buf_len = crypto_ecdh_key_len(&p);
238+
if (sizeof(encoded_key) < buf_len) {
239+
dev_err(&chip->dev, "salt buffer too small needs %d\n",
240+
buf_len);
241+
goto out;
242+
}
243+
crypto_ecdh_encode_key(encoded_key, buf_len, &p);
244+
/* this generates a random private key */
245+
crypto_kpp_set_secret(kpp, encoded_key, buf_len);
246+
247+
/* salt is now the public point of this private key */
248+
req = kpp_request_alloc(kpp, GFP_KERNEL);
249+
if (!req)
250+
goto out;
251+
kpp_request_set_input(req, NULL, 0);
252+
kpp_request_set_output(req, s, EC_PT_SZ*2);
253+
crypto_kpp_generate_public_key(req);
254+
/*
255+
* we're not done: now we have to compute the shared secret
256+
* which is our private key multiplied by the tpm_key public
257+
* point, we actually only take the x point and discard the y
258+
* point and feed it through KDFe to get the final secret salt
259+
*/
260+
sg_set_buf(&s[0], chip->null_ec_key_x, EC_PT_SZ);
261+
sg_set_buf(&s[1], chip->null_ec_key_y, EC_PT_SZ);
262+
kpp_request_set_input(req, s, EC_PT_SZ*2);
263+
sg_init_one(d, chip->auth->salt, EC_PT_SZ);
264+
kpp_request_set_output(req, d, EC_PT_SZ);
265+
crypto_kpp_compute_shared_secret(req);
266+
kpp_request_free(req);
267+
268+
/*
269+
* pass the shared secret through KDFe for salt. Note salt
270+
* area is used both for input shared secret and output salt.
271+
* This works because KDFe fully consumes the secret before it
272+
* writes the salt
273+
*/
274+
tpm2_KDFe(chip->auth->salt, "SECRET", x, chip->null_ec_key_x,
275+
chip->auth->salt);
276+
277+
out:
278+
crypto_free_kpp(kpp);
279+
}
280+
/**
281+
* tpm2_end_auth_session() - kill the allocated auth session
282+
* @chip: the TPM chip structure
283+
*
284+
* ends the session started by tpm2_start_auth_session and frees all
285+
* the resources. Under normal conditions,
286+
* tpm_buf_check_hmac_response() will correctly end the session if
287+
* required, so this function is only for use in error legs that will
288+
* bypass the normal invocation of tpm_buf_check_hmac_response().
289+
*/
290+
void tpm2_end_auth_session(struct tpm_chip *chip)
291+
{
292+
tpm2_flush_context(chip, chip->auth->handle);
293+
memzero_explicit(chip->auth, sizeof(*chip->auth));
294+
}
295+
EXPORT_SYMBOL(tpm2_end_auth_session);
296+
297+
static int tpm2_parse_start_auth_session(struct tpm2_auth *auth,
298+
struct tpm_buf *buf)
299+
{
300+
struct tpm_header *head = (struct tpm_header *)buf->data;
301+
u32 tot_len = be32_to_cpu(head->length);
302+
off_t offset = TPM_HEADER_SIZE;
303+
u32 val;
304+
305+
/* we're starting after the header so adjust the length */
306+
tot_len -= TPM_HEADER_SIZE;
307+
308+
/* should have handle plus nonce */
309+
if (tot_len != 4 + 2 + sizeof(auth->tpm_nonce))
310+
return -EINVAL;
311+
312+
auth->handle = tpm_buf_read_u32(buf, &offset);
313+
val = tpm_buf_read_u16(buf, &offset);
314+
if (val != sizeof(auth->tpm_nonce))
315+
return -EINVAL;
316+
memcpy(auth->tpm_nonce, &buf->data[offset], sizeof(auth->tpm_nonce));
317+
/* now compute the session key from the nonces */
318+
tpm2_KDFa(auth->salt, sizeof(auth->salt), "ATH", auth->tpm_nonce,
319+
auth->our_nonce, sizeof(auth->session_key),
320+
auth->session_key);
321+
322+
return 0;
323+
}
324+
325+
/**
326+
* tpm2_start_auth_session() - create a HMAC authentication session with the TPM
327+
* @chip: the TPM chip structure to create the session with
328+
*
329+
* This function loads the NULL seed from its saved context and starts
330+
* an authentication session on the null seed, fills in the
331+
* @chip->auth structure to contain all the session details necessary
332+
* for performing the HMAC, encrypt and decrypt operations and
333+
* returns. The NULL seed is flushed before this function returns.
334+
*
335+
* Return: zero on success or actual error encountered.
336+
*/
337+
int tpm2_start_auth_session(struct tpm_chip *chip)
338+
{
339+
struct tpm_buf buf;
340+
struct tpm2_auth *auth = chip->auth;
341+
int rc;
342+
/* null seed context has no offset, but we must provide one */
343+
unsigned int offset = 0;
344+
u32 nullkey;
345+
346+
rc = tpm2_load_context(chip, chip->null_key_context, &offset,
347+
&nullkey);
348+
if (rc)
349+
goto out;
350+
351+
auth->session = TPM_HEADER_SIZE;
352+
353+
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_START_AUTH_SESS);
354+
if (rc)
355+
goto out;
356+
357+
/* salt key handle */
358+
tpm_buf_append_u32(&buf, nullkey);
359+
/* bind key handle */
360+
tpm_buf_append_u32(&buf, TPM2_RH_NULL);
361+
/* nonce caller */
362+
get_random_bytes(auth->our_nonce, sizeof(auth->our_nonce));
363+
tpm_buf_append_u16(&buf, sizeof(auth->our_nonce));
364+
tpm_buf_append(&buf, auth->our_nonce, sizeof(auth->our_nonce));
365+
366+
/* append encrypted salt and squirrel away unencrypted in auth */
367+
tpm_buf_append_salt(&buf, chip);
368+
/* session type (HMAC, audit or policy) */
369+
tpm_buf_append_u8(&buf, TPM2_SE_HMAC);
370+
371+
/* symmetric encryption parameters */
372+
/* symmetric algorithm */
373+
tpm_buf_append_u16(&buf, TPM_ALG_AES);
374+
/* bits for symmetric algorithm */
375+
tpm_buf_append_u16(&buf, AES_KEY_BITS);
376+
/* symmetric algorithm mode (must be CFB) */
377+
tpm_buf_append_u16(&buf, TPM_ALG_CFB);
378+
/* hash algorithm for session */
379+
tpm_buf_append_u16(&buf, TPM_ALG_SHA256);
380+
381+
rc = tpm_transmit_cmd(chip, &buf, 0, "start auth session");
382+
tpm2_flush_context(chip, nullkey);
383+
384+
if (rc == TPM2_RC_SUCCESS)
385+
rc = tpm2_parse_start_auth_session(auth, &buf);
386+
387+
tpm_buf_destroy(&buf);
388+
389+
if (rc)
390+
goto out;
391+
392+
out:
393+
return rc;
394+
}
395+
EXPORT_SYMBOL(tpm2_start_auth_session);
396+
116397
/**
117398
* tpm2_parse_create_primary() - parse the data returned from TPM_CC_CREATE_PRIMARY
118399
*
@@ -423,5 +704,9 @@ int tpm2_sessions_init(struct tpm_chip *chip)
423704
if (rc)
424705
dev_err(&chip->dev, "TPM: security failed (NULL seed derivation): %d\n", rc);
425706

707+
chip->auth = kmalloc(sizeof(*chip->auth), GFP_KERNEL);
708+
if (!chip->auth)
709+
return -ENOMEM;
710+
426711
return rc;
427712
}

0 commit comments

Comments
 (0)