Denis Kenzior | 903be6b | 2018-10-09 17:48:02 +0100 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | #define pr_fmt(fmt) "ASYM-TPM: "fmt |
| 3 | #include <linux/slab.h> |
| 4 | #include <linux/module.h> |
| 5 | #include <linux/export.h> |
| 6 | #include <linux/kernel.h> |
| 7 | #include <linux/seq_file.h> |
| 8 | #include <linux/scatterlist.h> |
| 9 | #include <linux/tpm.h> |
Denis Kenzior | 0c36264 | 2018-10-09 17:48:58 +0100 | [diff] [blame] | 10 | #include <linux/tpm_command.h> |
Denis Kenzior | dff5a61 | 2018-10-09 17:48:25 +0100 | [diff] [blame] | 11 | #include <crypto/akcipher.h> |
Denis Kenzior | 0c36264 | 2018-10-09 17:48:58 +0100 | [diff] [blame] | 12 | #include <crypto/hash.h> |
| 13 | #include <crypto/sha.h> |
Denis Kenzior | f8c54e1 | 2018-10-09 17:48:10 +0100 | [diff] [blame] | 14 | #include <asm/unaligned.h> |
Denis Kenzior | 903be6b | 2018-10-09 17:48:02 +0100 | [diff] [blame] | 15 | #include <keys/asymmetric-subtype.h> |
Denis Kenzior | 0c36264 | 2018-10-09 17:48:58 +0100 | [diff] [blame] | 16 | #include <keys/trusted.h> |
Denis Kenzior | 903be6b | 2018-10-09 17:48:02 +0100 | [diff] [blame] | 17 | #include <crypto/asym_tpm_subtype.h> |
| 18 | |
Denis Kenzior | 0c36264 | 2018-10-09 17:48:58 +0100 | [diff] [blame] | 19 | #define TPM_ORD_FLUSHSPECIFIC 186 |
| 20 | #define TPM_ORD_LOADKEY2 65 |
Denis Kenzior | f884fe5 | 2018-10-09 17:49:05 +0100 | [diff] [blame^] | 21 | #define TPM_ORD_UNBIND 30 |
Denis Kenzior | 0c36264 | 2018-10-09 17:48:58 +0100 | [diff] [blame] | 22 | #define TPM_LOADKEY2_SIZE 59 |
| 23 | #define TPM_FLUSHSPECIFIC_SIZE 18 |
Denis Kenzior | f884fe5 | 2018-10-09 17:49:05 +0100 | [diff] [blame^] | 24 | #define TPM_UNBIND_SIZE 63 |
Denis Kenzior | 0c36264 | 2018-10-09 17:48:58 +0100 | [diff] [blame] | 25 | |
| 26 | #define TPM_RT_KEY 0x00000001 |
| 27 | |
| 28 | /* |
| 29 | * Load a TPM key from the blob provided by userspace |
| 30 | */ |
| 31 | static int tpm_loadkey2(struct tpm_buf *tb, |
| 32 | uint32_t keyhandle, unsigned char *keyauth, |
| 33 | const unsigned char *keyblob, int keybloblen, |
| 34 | uint32_t *newhandle) |
| 35 | { |
| 36 | unsigned char nonceodd[TPM_NONCE_SIZE]; |
| 37 | unsigned char enonce[TPM_NONCE_SIZE]; |
| 38 | unsigned char authdata[SHA1_DIGEST_SIZE]; |
| 39 | uint32_t authhandle = 0; |
| 40 | unsigned char cont = 0; |
| 41 | uint32_t ordinal; |
| 42 | int ret; |
| 43 | |
| 44 | ordinal = htonl(TPM_ORD_LOADKEY2); |
| 45 | |
| 46 | /* session for loading the key */ |
| 47 | ret = oiap(tb, &authhandle, enonce); |
| 48 | if (ret < 0) { |
| 49 | pr_info("oiap failed (%d)\n", ret); |
| 50 | return ret; |
| 51 | } |
| 52 | |
| 53 | /* generate odd nonce */ |
| 54 | ret = tpm_get_random(NULL, nonceodd, TPM_NONCE_SIZE); |
| 55 | if (ret < 0) { |
| 56 | pr_info("tpm_get_random failed (%d)\n", ret); |
| 57 | return ret; |
| 58 | } |
| 59 | |
| 60 | /* calculate authorization HMAC value */ |
| 61 | ret = TSS_authhmac(authdata, keyauth, SHA1_DIGEST_SIZE, enonce, |
| 62 | nonceodd, cont, sizeof(uint32_t), &ordinal, |
| 63 | keybloblen, keyblob, 0, 0); |
| 64 | if (ret < 0) |
| 65 | return ret; |
| 66 | |
| 67 | /* build the request buffer */ |
| 68 | INIT_BUF(tb); |
| 69 | store16(tb, TPM_TAG_RQU_AUTH1_COMMAND); |
| 70 | store32(tb, TPM_LOADKEY2_SIZE + keybloblen); |
| 71 | store32(tb, TPM_ORD_LOADKEY2); |
| 72 | store32(tb, keyhandle); |
| 73 | storebytes(tb, keyblob, keybloblen); |
| 74 | store32(tb, authhandle); |
| 75 | storebytes(tb, nonceodd, TPM_NONCE_SIZE); |
| 76 | store8(tb, cont); |
| 77 | storebytes(tb, authdata, SHA1_DIGEST_SIZE); |
| 78 | |
| 79 | ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE); |
| 80 | if (ret < 0) { |
| 81 | pr_info("authhmac failed (%d)\n", ret); |
| 82 | return ret; |
| 83 | } |
| 84 | |
| 85 | ret = TSS_checkhmac1(tb->data, ordinal, nonceodd, keyauth, |
| 86 | SHA1_DIGEST_SIZE, 0, 0); |
| 87 | if (ret < 0) { |
| 88 | pr_info("TSS_checkhmac1 failed (%d)\n", ret); |
| 89 | return ret; |
| 90 | } |
| 91 | |
| 92 | *newhandle = LOAD32(tb->data, TPM_DATA_OFFSET); |
| 93 | return 0; |
| 94 | } |
| 95 | |
| 96 | /* |
| 97 | * Execute the FlushSpecific TPM command |
| 98 | */ |
| 99 | static int tpm_flushspecific(struct tpm_buf *tb, uint32_t handle) |
| 100 | { |
| 101 | INIT_BUF(tb); |
| 102 | store16(tb, TPM_TAG_RQU_COMMAND); |
| 103 | store32(tb, TPM_FLUSHSPECIFIC_SIZE); |
| 104 | store32(tb, TPM_ORD_FLUSHSPECIFIC); |
| 105 | store32(tb, handle); |
| 106 | store32(tb, TPM_RT_KEY); |
| 107 | |
| 108 | return trusted_tpm_send(tb->data, MAX_BUF_SIZE); |
| 109 | } |
| 110 | |
Denis Kenzior | 903be6b | 2018-10-09 17:48:02 +0100 | [diff] [blame] | 111 | /* |
Denis Kenzior | f884fe5 | 2018-10-09 17:49:05 +0100 | [diff] [blame^] | 112 | * Decrypt a blob provided by userspace using a specific key handle. |
| 113 | * The handle is a well known handle or previously loaded by e.g. LoadKey2 |
| 114 | */ |
| 115 | static int tpm_unbind(struct tpm_buf *tb, |
| 116 | uint32_t keyhandle, unsigned char *keyauth, |
| 117 | const unsigned char *blob, uint32_t bloblen, |
| 118 | void *out, uint32_t outlen) |
| 119 | { |
| 120 | unsigned char nonceodd[TPM_NONCE_SIZE]; |
| 121 | unsigned char enonce[TPM_NONCE_SIZE]; |
| 122 | unsigned char authdata[SHA1_DIGEST_SIZE]; |
| 123 | uint32_t authhandle = 0; |
| 124 | unsigned char cont = 0; |
| 125 | uint32_t ordinal; |
| 126 | uint32_t datalen; |
| 127 | int ret; |
| 128 | |
| 129 | ordinal = htonl(TPM_ORD_UNBIND); |
| 130 | datalen = htonl(bloblen); |
| 131 | |
| 132 | /* session for loading the key */ |
| 133 | ret = oiap(tb, &authhandle, enonce); |
| 134 | if (ret < 0) { |
| 135 | pr_info("oiap failed (%d)\n", ret); |
| 136 | return ret; |
| 137 | } |
| 138 | |
| 139 | /* generate odd nonce */ |
| 140 | ret = tpm_get_random(NULL, nonceodd, TPM_NONCE_SIZE); |
| 141 | if (ret < 0) { |
| 142 | pr_info("tpm_get_random failed (%d)\n", ret); |
| 143 | return ret; |
| 144 | } |
| 145 | |
| 146 | /* calculate authorization HMAC value */ |
| 147 | ret = TSS_authhmac(authdata, keyauth, SHA1_DIGEST_SIZE, enonce, |
| 148 | nonceodd, cont, sizeof(uint32_t), &ordinal, |
| 149 | sizeof(uint32_t), &datalen, |
| 150 | bloblen, blob, 0, 0); |
| 151 | if (ret < 0) |
| 152 | return ret; |
| 153 | |
| 154 | /* build the request buffer */ |
| 155 | INIT_BUF(tb); |
| 156 | store16(tb, TPM_TAG_RQU_AUTH1_COMMAND); |
| 157 | store32(tb, TPM_UNBIND_SIZE + bloblen); |
| 158 | store32(tb, TPM_ORD_UNBIND); |
| 159 | store32(tb, keyhandle); |
| 160 | store32(tb, bloblen); |
| 161 | storebytes(tb, blob, bloblen); |
| 162 | store32(tb, authhandle); |
| 163 | storebytes(tb, nonceodd, TPM_NONCE_SIZE); |
| 164 | store8(tb, cont); |
| 165 | storebytes(tb, authdata, SHA1_DIGEST_SIZE); |
| 166 | |
| 167 | ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE); |
| 168 | if (ret < 0) { |
| 169 | pr_info("authhmac failed (%d)\n", ret); |
| 170 | return ret; |
| 171 | } |
| 172 | |
| 173 | datalen = LOAD32(tb->data, TPM_DATA_OFFSET); |
| 174 | |
| 175 | ret = TSS_checkhmac1(tb->data, ordinal, nonceodd, |
| 176 | keyauth, SHA1_DIGEST_SIZE, |
| 177 | sizeof(uint32_t), TPM_DATA_OFFSET, |
| 178 | datalen, TPM_DATA_OFFSET + sizeof(uint32_t), |
| 179 | 0, 0); |
| 180 | if (ret < 0) { |
| 181 | pr_info("TSS_checkhmac1 failed (%d)\n", ret); |
| 182 | return ret; |
| 183 | } |
| 184 | |
| 185 | memcpy(out, tb->data + TPM_DATA_OFFSET + sizeof(uint32_t), |
| 186 | min(outlen, datalen)); |
| 187 | |
| 188 | return datalen; |
| 189 | } |
| 190 | |
| 191 | /* |
Denis Kenzior | dff5a61 | 2018-10-09 17:48:25 +0100 | [diff] [blame] | 192 | * Maximum buffer size for the BER/DER encoded public key. The public key |
| 193 | * is of the form SEQUENCE { INTEGER n, INTEGER e } where n is a maximum 2048 |
| 194 | * bit key and e is usually 65537 |
| 195 | * The encoding overhead is: |
| 196 | * - max 4 bytes for SEQUENCE |
| 197 | * - max 4 bytes for INTEGER n type/length |
| 198 | * - 257 bytes of n |
| 199 | * - max 2 bytes for INTEGER e type/length |
| 200 | * - 3 bytes of e |
| 201 | */ |
| 202 | #define PUB_KEY_BUF_SIZE (4 + 4 + 257 + 2 + 3) |
| 203 | |
| 204 | /* |
Denis Kenzior | 903be6b | 2018-10-09 17:48:02 +0100 | [diff] [blame] | 205 | * Provide a part of a description of the key for /proc/keys. |
| 206 | */ |
| 207 | static void asym_tpm_describe(const struct key *asymmetric_key, |
| 208 | struct seq_file *m) |
| 209 | { |
| 210 | struct tpm_key *tk = asymmetric_key->payload.data[asym_crypto]; |
| 211 | |
| 212 | if (!tk) |
| 213 | return; |
| 214 | |
| 215 | seq_printf(m, "TPM1.2/Blob"); |
| 216 | } |
| 217 | |
| 218 | static void asym_tpm_destroy(void *payload0, void *payload3) |
| 219 | { |
| 220 | struct tpm_key *tk = payload0; |
| 221 | |
| 222 | if (!tk) |
| 223 | return; |
| 224 | |
| 225 | kfree(tk->blob); |
| 226 | tk->blob_len = 0; |
| 227 | |
| 228 | kfree(tk); |
| 229 | } |
| 230 | |
Denis Kenzior | dff5a61 | 2018-10-09 17:48:25 +0100 | [diff] [blame] | 231 | /* How many bytes will it take to encode the length */ |
| 232 | static inline uint32_t definite_length(uint32_t len) |
| 233 | { |
| 234 | if (len <= 127) |
| 235 | return 1; |
| 236 | if (len <= 255) |
| 237 | return 2; |
| 238 | return 3; |
| 239 | } |
| 240 | |
| 241 | static inline uint8_t *encode_tag_length(uint8_t *buf, uint8_t tag, |
| 242 | uint32_t len) |
| 243 | { |
| 244 | *buf++ = tag; |
| 245 | |
| 246 | if (len <= 127) { |
| 247 | buf[0] = len; |
| 248 | return buf + 1; |
| 249 | } |
| 250 | |
| 251 | if (len <= 255) { |
| 252 | buf[0] = 0x81; |
| 253 | buf[1] = len; |
| 254 | return buf + 2; |
| 255 | } |
| 256 | |
| 257 | buf[0] = 0x82; |
| 258 | put_unaligned_be16(len, buf + 1); |
| 259 | return buf + 3; |
| 260 | } |
| 261 | |
| 262 | static uint32_t derive_pub_key(const void *pub_key, uint32_t len, uint8_t *buf) |
| 263 | { |
| 264 | uint8_t *cur = buf; |
| 265 | uint32_t n_len = definite_length(len) + 1 + len + 1; |
| 266 | uint32_t e_len = definite_length(3) + 1 + 3; |
| 267 | uint8_t e[3] = { 0x01, 0x00, 0x01 }; |
| 268 | |
| 269 | /* SEQUENCE */ |
| 270 | cur = encode_tag_length(cur, 0x30, n_len + e_len); |
| 271 | /* INTEGER n */ |
| 272 | cur = encode_tag_length(cur, 0x02, len + 1); |
| 273 | cur[0] = 0x00; |
| 274 | memcpy(cur + 1, pub_key, len); |
| 275 | cur += len + 1; |
| 276 | cur = encode_tag_length(cur, 0x02, sizeof(e)); |
| 277 | memcpy(cur, e, sizeof(e)); |
| 278 | cur += sizeof(e); |
| 279 | |
| 280 | return cur - buf; |
| 281 | } |
| 282 | |
| 283 | /* |
| 284 | * Determine the crypto algorithm name. |
| 285 | */ |
| 286 | static int determine_akcipher(const char *encoding, const char *hash_algo, |
| 287 | char alg_name[CRYPTO_MAX_ALG_NAME]) |
| 288 | { |
| 289 | /* TODO: We don't support hashing yet */ |
| 290 | if (hash_algo) |
| 291 | return -ENOPKG; |
| 292 | |
| 293 | if (strcmp(encoding, "pkcs1") == 0) { |
| 294 | strcpy(alg_name, "pkcs1pad(rsa)"); |
| 295 | return 0; |
| 296 | } |
| 297 | |
| 298 | if (strcmp(encoding, "raw") == 0) { |
| 299 | strcpy(alg_name, "rsa"); |
| 300 | return 0; |
| 301 | } |
| 302 | |
| 303 | return -ENOPKG; |
| 304 | } |
| 305 | |
| 306 | /* |
| 307 | * Query information about a key. |
| 308 | */ |
| 309 | static int tpm_key_query(const struct kernel_pkey_params *params, |
| 310 | struct kernel_pkey_query *info) |
| 311 | { |
| 312 | struct tpm_key *tk = params->key->payload.data[asym_crypto]; |
| 313 | int ret; |
| 314 | char alg_name[CRYPTO_MAX_ALG_NAME]; |
| 315 | struct crypto_akcipher *tfm; |
| 316 | uint8_t der_pub_key[PUB_KEY_BUF_SIZE]; |
| 317 | uint32_t der_pub_key_len; |
| 318 | int len; |
| 319 | |
| 320 | /* TPM only works on private keys, public keys still done in software */ |
| 321 | ret = determine_akcipher(params->encoding, params->hash_algo, alg_name); |
| 322 | if (ret < 0) |
| 323 | return ret; |
| 324 | |
| 325 | tfm = crypto_alloc_akcipher(alg_name, 0, 0); |
| 326 | if (IS_ERR(tfm)) |
| 327 | return PTR_ERR(tfm); |
| 328 | |
| 329 | der_pub_key_len = derive_pub_key(tk->pub_key, tk->pub_key_len, |
| 330 | der_pub_key); |
| 331 | |
| 332 | ret = crypto_akcipher_set_pub_key(tfm, der_pub_key, der_pub_key_len); |
| 333 | if (ret < 0) |
| 334 | goto error_free_tfm; |
| 335 | |
| 336 | len = crypto_akcipher_maxsize(tfm); |
| 337 | |
| 338 | info->key_size = tk->key_len; |
| 339 | info->max_data_size = tk->key_len / 8; |
| 340 | info->max_sig_size = len; |
| 341 | info->max_enc_size = len; |
| 342 | info->max_dec_size = tk->key_len / 8; |
| 343 | |
Denis Kenzior | ad4b1eb | 2018-10-09 17:48:33 +0100 | [diff] [blame] | 344 | info->supported_ops = KEYCTL_SUPPORTS_ENCRYPT; |
| 345 | |
Denis Kenzior | dff5a61 | 2018-10-09 17:48:25 +0100 | [diff] [blame] | 346 | ret = 0; |
| 347 | error_free_tfm: |
| 348 | crypto_free_akcipher(tfm); |
| 349 | pr_devel("<==%s() = %d\n", __func__, ret); |
| 350 | return ret; |
| 351 | } |
| 352 | |
Denis Kenzior | f8c54e1 | 2018-10-09 17:48:10 +0100 | [diff] [blame] | 353 | /* |
Denis Kenzior | ad4b1eb | 2018-10-09 17:48:33 +0100 | [diff] [blame] | 354 | * Encryption operation is performed with the public key. Hence it is done |
| 355 | * in software |
| 356 | */ |
| 357 | static int tpm_key_encrypt(struct tpm_key *tk, |
| 358 | struct kernel_pkey_params *params, |
| 359 | const void *in, void *out) |
| 360 | { |
| 361 | char alg_name[CRYPTO_MAX_ALG_NAME]; |
| 362 | struct crypto_akcipher *tfm; |
| 363 | struct akcipher_request *req; |
| 364 | struct crypto_wait cwait; |
| 365 | struct scatterlist in_sg, out_sg; |
| 366 | uint8_t der_pub_key[PUB_KEY_BUF_SIZE]; |
| 367 | uint32_t der_pub_key_len; |
| 368 | int ret; |
| 369 | |
| 370 | pr_devel("==>%s()\n", __func__); |
| 371 | |
| 372 | ret = determine_akcipher(params->encoding, params->hash_algo, alg_name); |
| 373 | if (ret < 0) |
| 374 | return ret; |
| 375 | |
| 376 | tfm = crypto_alloc_akcipher(alg_name, 0, 0); |
| 377 | if (IS_ERR(tfm)) |
| 378 | return PTR_ERR(tfm); |
| 379 | |
| 380 | der_pub_key_len = derive_pub_key(tk->pub_key, tk->pub_key_len, |
| 381 | der_pub_key); |
| 382 | |
| 383 | ret = crypto_akcipher_set_pub_key(tfm, der_pub_key, der_pub_key_len); |
| 384 | if (ret < 0) |
| 385 | goto error_free_tfm; |
| 386 | |
| 387 | req = akcipher_request_alloc(tfm, GFP_KERNEL); |
| 388 | if (!req) |
| 389 | goto error_free_tfm; |
| 390 | |
| 391 | sg_init_one(&in_sg, in, params->in_len); |
| 392 | sg_init_one(&out_sg, out, params->out_len); |
| 393 | akcipher_request_set_crypt(req, &in_sg, &out_sg, params->in_len, |
| 394 | params->out_len); |
| 395 | crypto_init_wait(&cwait); |
| 396 | akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | |
| 397 | CRYPTO_TFM_REQ_MAY_SLEEP, |
| 398 | crypto_req_done, &cwait); |
| 399 | |
| 400 | ret = crypto_akcipher_encrypt(req); |
| 401 | ret = crypto_wait_req(ret, &cwait); |
| 402 | |
| 403 | if (ret == 0) |
| 404 | ret = req->dst_len; |
| 405 | |
| 406 | akcipher_request_free(req); |
| 407 | error_free_tfm: |
| 408 | crypto_free_akcipher(tfm); |
| 409 | pr_devel("<==%s() = %d\n", __func__, ret); |
| 410 | return ret; |
| 411 | } |
| 412 | |
| 413 | /* |
| 414 | * Do encryption, decryption and signing ops. |
| 415 | */ |
| 416 | static int tpm_key_eds_op(struct kernel_pkey_params *params, |
| 417 | const void *in, void *out) |
| 418 | { |
| 419 | struct tpm_key *tk = params->key->payload.data[asym_crypto]; |
| 420 | int ret = -EOPNOTSUPP; |
| 421 | |
| 422 | /* Perform the encryption calculation. */ |
| 423 | switch (params->op) { |
| 424 | case kernel_pkey_encrypt: |
| 425 | ret = tpm_key_encrypt(tk, params, in, out); |
| 426 | break; |
| 427 | default: |
| 428 | BUG(); |
| 429 | } |
| 430 | |
| 431 | return ret; |
| 432 | } |
| 433 | |
| 434 | /* |
Denis Kenzior | f8c54e1 | 2018-10-09 17:48:10 +0100 | [diff] [blame] | 435 | * Parse enough information out of TPM_KEY structure: |
| 436 | * TPM_STRUCT_VER -> 4 bytes |
| 437 | * TPM_KEY_USAGE -> 2 bytes |
| 438 | * TPM_KEY_FLAGS -> 4 bytes |
| 439 | * TPM_AUTH_DATA_USAGE -> 1 byte |
| 440 | * TPM_KEY_PARMS -> variable |
| 441 | * UINT32 PCRInfoSize -> 4 bytes |
| 442 | * BYTE* -> PCRInfoSize bytes |
| 443 | * TPM_STORE_PUBKEY |
| 444 | * UINT32 encDataSize; |
| 445 | * BYTE* -> encDataSize; |
| 446 | * |
| 447 | * TPM_KEY_PARMS: |
| 448 | * TPM_ALGORITHM_ID -> 4 bytes |
| 449 | * TPM_ENC_SCHEME -> 2 bytes |
| 450 | * TPM_SIG_SCHEME -> 2 bytes |
| 451 | * UINT32 parmSize -> 4 bytes |
| 452 | * BYTE* -> variable |
| 453 | */ |
| 454 | static int extract_key_parameters(struct tpm_key *tk) |
| 455 | { |
| 456 | const void *cur = tk->blob; |
| 457 | uint32_t len = tk->blob_len; |
| 458 | const void *pub_key; |
| 459 | uint32_t sz; |
| 460 | uint32_t key_len; |
| 461 | |
| 462 | if (len < 11) |
| 463 | return -EBADMSG; |
| 464 | |
| 465 | /* Ensure this is a legacy key */ |
| 466 | if (get_unaligned_be16(cur + 4) != 0x0015) |
| 467 | return -EBADMSG; |
| 468 | |
| 469 | /* Skip to TPM_KEY_PARMS */ |
| 470 | cur += 11; |
| 471 | len -= 11; |
| 472 | |
| 473 | if (len < 12) |
| 474 | return -EBADMSG; |
| 475 | |
| 476 | /* Make sure this is an RSA key */ |
| 477 | if (get_unaligned_be32(cur) != 0x00000001) |
| 478 | return -EBADMSG; |
| 479 | |
| 480 | /* Make sure this is TPM_ES_RSAESPKCSv15 encoding scheme */ |
| 481 | if (get_unaligned_be16(cur + 4) != 0x0002) |
| 482 | return -EBADMSG; |
| 483 | |
| 484 | /* Make sure this is TPM_SS_RSASSAPKCS1v15_DER signature scheme */ |
| 485 | if (get_unaligned_be16(cur + 6) != 0x0003) |
| 486 | return -EBADMSG; |
| 487 | |
| 488 | sz = get_unaligned_be32(cur + 8); |
| 489 | if (len < sz + 12) |
| 490 | return -EBADMSG; |
| 491 | |
| 492 | /* Move to TPM_RSA_KEY_PARMS */ |
| 493 | len -= 12; |
| 494 | cur += 12; |
| 495 | |
| 496 | /* Grab the RSA key length */ |
| 497 | key_len = get_unaligned_be32(cur); |
| 498 | |
| 499 | switch (key_len) { |
| 500 | case 512: |
| 501 | case 1024: |
| 502 | case 1536: |
| 503 | case 2048: |
| 504 | break; |
| 505 | default: |
| 506 | return -EINVAL; |
| 507 | } |
| 508 | |
| 509 | /* Move just past TPM_KEY_PARMS */ |
| 510 | cur += sz; |
| 511 | len -= sz; |
| 512 | |
| 513 | if (len < 4) |
| 514 | return -EBADMSG; |
| 515 | |
| 516 | sz = get_unaligned_be32(cur); |
| 517 | if (len < 4 + sz) |
| 518 | return -EBADMSG; |
| 519 | |
| 520 | /* Move to TPM_STORE_PUBKEY */ |
| 521 | cur += 4 + sz; |
| 522 | len -= 4 + sz; |
| 523 | |
| 524 | /* Grab the size of the public key, it should jive with the key size */ |
| 525 | sz = get_unaligned_be32(cur); |
| 526 | if (sz > 256) |
| 527 | return -EINVAL; |
| 528 | |
| 529 | pub_key = cur + 4; |
| 530 | |
| 531 | tk->key_len = key_len; |
| 532 | tk->pub_key = pub_key; |
| 533 | tk->pub_key_len = sz; |
| 534 | |
| 535 | return 0; |
| 536 | } |
| 537 | |
Denis Kenzior | 903be6b | 2018-10-09 17:48:02 +0100 | [diff] [blame] | 538 | /* Given the blob, parse it and load it into the TPM */ |
| 539 | struct tpm_key *tpm_key_create(const void *blob, uint32_t blob_len) |
| 540 | { |
| 541 | int r; |
| 542 | struct tpm_key *tk; |
| 543 | |
| 544 | r = tpm_is_tpm2(NULL); |
| 545 | if (r < 0) |
| 546 | goto error; |
| 547 | |
| 548 | /* We don't support TPM2 yet */ |
| 549 | if (r > 0) { |
| 550 | r = -ENODEV; |
| 551 | goto error; |
| 552 | } |
| 553 | |
| 554 | r = -ENOMEM; |
| 555 | tk = kzalloc(sizeof(struct tpm_key), GFP_KERNEL); |
| 556 | if (!tk) |
| 557 | goto error; |
| 558 | |
| 559 | tk->blob = kmemdup(blob, blob_len, GFP_KERNEL); |
| 560 | if (!tk->blob) |
| 561 | goto error_memdup; |
| 562 | |
| 563 | tk->blob_len = blob_len; |
| 564 | |
Denis Kenzior | f8c54e1 | 2018-10-09 17:48:10 +0100 | [diff] [blame] | 565 | r = extract_key_parameters(tk); |
| 566 | if (r < 0) |
| 567 | goto error_extract; |
| 568 | |
Denis Kenzior | 903be6b | 2018-10-09 17:48:02 +0100 | [diff] [blame] | 569 | return tk; |
| 570 | |
Denis Kenzior | f8c54e1 | 2018-10-09 17:48:10 +0100 | [diff] [blame] | 571 | error_extract: |
| 572 | kfree(tk->blob); |
| 573 | tk->blob_len = 0; |
Denis Kenzior | 903be6b | 2018-10-09 17:48:02 +0100 | [diff] [blame] | 574 | error_memdup: |
| 575 | kfree(tk); |
| 576 | error: |
| 577 | return ERR_PTR(r); |
| 578 | } |
| 579 | EXPORT_SYMBOL_GPL(tpm_key_create); |
| 580 | |
| 581 | /* |
| 582 | * TPM-based asymmetric key subtype |
| 583 | */ |
| 584 | struct asymmetric_key_subtype asym_tpm_subtype = { |
| 585 | .owner = THIS_MODULE, |
| 586 | .name = "asym_tpm", |
| 587 | .name_len = sizeof("asym_tpm") - 1, |
| 588 | .describe = asym_tpm_describe, |
| 589 | .destroy = asym_tpm_destroy, |
Denis Kenzior | dff5a61 | 2018-10-09 17:48:25 +0100 | [diff] [blame] | 590 | .query = tpm_key_query, |
Denis Kenzior | ad4b1eb | 2018-10-09 17:48:33 +0100 | [diff] [blame] | 591 | .eds_op = tpm_key_eds_op, |
Denis Kenzior | 903be6b | 2018-10-09 17:48:02 +0100 | [diff] [blame] | 592 | }; |
| 593 | EXPORT_SYMBOL_GPL(asym_tpm_subtype); |
| 594 | |
| 595 | MODULE_DESCRIPTION("TPM based asymmetric key subtype"); |
| 596 | MODULE_AUTHOR("Intel Corporation"); |
| 597 | MODULE_LICENSE("GPL v2"); |