|
3 | 3 | /*
|
4 | 4 | * Copyright (C) 2018 [email protected]
|
5 | 5 | *
|
| 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. |
6 | 52 | */
|
7 | 53 |
|
8 | 54 | #include "tpm.h"
|
| 55 | +#include <linux/random.h> |
| 56 | +#include <linux/scatterlist.h> |
9 | 57 | #include <asm/unaligned.h>
|
| 58 | +#include <crypto/kpp.h> |
| 59 | +#include <crypto/ecdh.h> |
10 | 60 | #include <crypto/hash.h>
|
11 | 61 | #include <crypto/hmac.h>
|
12 | 62 |
|
| 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 | + |
13 | 101 | /*
|
14 | 102 | * It turns out the crypto hmac(sha256) is hard for us to consume
|
15 | 103 | * 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,
|
113 | 201 | sha256_final(&sctx, out);
|
114 | 202 | }
|
115 | 203 |
|
| 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 | + |
116 | 397 | /**
|
117 | 398 | * tpm2_parse_create_primary() - parse the data returned from TPM_CC_CREATE_PRIMARY
|
118 | 399 | *
|
@@ -423,5 +704,9 @@ int tpm2_sessions_init(struct tpm_chip *chip)
|
423 | 704 | if (rc)
|
424 | 705 | dev_err(&chip->dev, "TPM: security failed (NULL seed derivation): %d\n", rc);
|
425 | 706 |
|
| 707 | + chip->auth = kmalloc(sizeof(*chip->auth), GFP_KERNEL); |
| 708 | + if (!chip->auth) |
| 709 | + return -ENOMEM; |
| 710 | + |
426 | 711 | return rc;
|
427 | 712 | }
|
0 commit comments