blob: 27e993f0b0b7fa4929a1eff7bea0d50f9fa71df4 [file] [log] [blame] [edit]
/* TPM call wrapper library.
*
* Copyright (C) 2010 IBM Corporation
*
* Author:
* David Safford <safford@us.ibm.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 of the License.
*/
#define pr_fmt(fmt) "TPMLIB: "fmt
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/crypto.h>
#include <crypto/hash.h>
#include <crypto/sha.h>
#include <linux/tpm.h>
#include <linux/tpm_command.h>
#include "tpm-library.h"
#define kenter(fmt, ...) pr_devel("==>%s("fmt")\n", __func__, ## __VA_ARGS__)
#define kleave(fmt, ...) pr_devel("<==%s()"fmt"\n", __func__, ## __VA_ARGS__)
static const char tpm_hmac_alg[] = "hmac(sha1)";
static const char tpm_hash_alg[] = "sha1";
struct tpm_sdesc {
struct shash_desc shash;
char ctx[];
};
static DEFINE_MUTEX(tpm_library_init_mutex);
static atomic_t tpm_library_usage;
static struct crypto_shash *tpm_hashalg;
static struct crypto_shash *tpm_hmacalg;
static int tpm_gen_odd_nonce(struct tpm_chip *chip,
struct tpm_odd_nonce *ononce)
{
int ret;
ret = tpm_get_random(chip, ononce->data, TPM_NONCE_SIZE);
if (ret == TPM_NONCE_SIZE)
ret = 0;
else
pr_info("tpm_get_random failed (%d)\n", ret);
return ret;
}
static struct tpm_sdesc *tpm_init_sdesc(struct crypto_shash *alg)
{
struct tpm_sdesc *sdesc;
int size;
size = sizeof(struct shash_desc) + crypto_shash_descsize(alg);
sdesc = kmalloc(size, GFP_KERNEL);
if (!sdesc)
return ERR_PTR(-ENOMEM);
sdesc->shash.tfm = alg;
sdesc->shash.flags = 0x0;
return sdesc;
}
static int TSS_sha1(const unsigned char *data, unsigned int datalen,
unsigned char *digest)
{
struct tpm_sdesc *sdesc;
int ret;
sdesc = tpm_init_sdesc(tpm_hashalg);
if (IS_ERR(sdesc)) {
pr_info("Can't alloc %s\n", tpm_hash_alg);
return PTR_ERR(sdesc);
}
ret = crypto_shash_digest(&sdesc->shash, data, datalen, digest);
kfree(sdesc);
return ret;
}
/**
* TSS_rawhmac - Generate a HMAC(SHA1) from raw data
* @digest: Result buffer - must be SHA1_DIGEST_SIZE in size
* @key: The key to use in the HMAC generation
* @keylen: The size of @key
* @...: Pairs of size and pointer of data elements to load into hmac
* @0,NULL: Terminator
*/
static int TSS_rawhmac(unsigned char *digest,
const unsigned char *key, unsigned keylen,
...)
{
struct tpm_sdesc *sdesc;
va_list argp;
unsigned int dlen;
unsigned char *data;
int ret, s;
sdesc = tpm_init_sdesc(tpm_hmacalg);
if (IS_ERR(sdesc)) {
pr_info("Can't alloc %s\n", tpm_hmac_alg);
return PTR_ERR(sdesc);
}
ret = crypto_shash_setkey(tpm_hmacalg, key, keylen);
if (ret < 0)
goto out;
ret = crypto_shash_init(&sdesc->shash);
if (ret < 0)
goto out;
va_start(argp, keylen);
for (s = 1;; s++) {
dlen = va_arg(argp, unsigned int);
data = va_arg(argp, unsigned char *);
if (!data)
break;
pr_devel("RAWHMAC %dH1: [%u] %*phN\n", s, dlen, dlen, data);
ret = crypto_shash_update(&sdesc->shash, data, dlen);
if (ret < 0)
break;
}
va_end(argp);
if (!ret)
ret = crypto_shash_final(&sdesc->shash, digest);
out:
kfree(sdesc);
return ret;
}
/**
* TSS_authhmac - Calculate authorisation info to send to TPM
* @digest: Result buffer - must be SHA1_DIGEST_SIZE in size
* @key: The key to use in the HMAC generation
* @keylen: The size of @key
* @enonce: Even nonce
* @ononce: Odd nonce
* @cont: Continuation flag
* @...: Pairs of size and pointer of data elements to load into hash
* @0,NULL: Terminator
*
* Calculate authorization info fields to send to TPM
*/
static int TSS_authhmac(unsigned char *digest,
const unsigned char *key, unsigned keylen,
const struct tpm_even_nonce *enonce,
const struct tpm_odd_nonce *ononce,
unsigned char cont,
...)
{
unsigned char paramdigest[SHA1_DIGEST_SIZE];
struct tpm_sdesc *sdesc;
unsigned int dlen;
unsigned char *data;
int ret, s;
va_list argp;
sdesc = tpm_init_sdesc(tpm_hashalg);
if (IS_ERR(sdesc)) {
pr_info("Can't alloc %s\n", tpm_hash_alg);
return PTR_ERR(sdesc);
}
ret = crypto_shash_init(&sdesc->shash);
if (ret < 0)
goto out;
va_start(argp, cont);
for (s = 1;; s++) {
dlen = va_arg(argp, unsigned int);
data = va_arg(argp, unsigned char *);
if (!data)
break;
pr_devel("AUTHHASH S%d: [%u] %*phN\n", s, dlen, dlen, data);
ret = crypto_shash_update(&sdesc->shash, data, dlen);
if (ret < 0)
break;
}
va_end(argp);
if (!ret)
ret = crypto_shash_final(&sdesc->shash, paramdigest);
if (!ret)
ret = TSS_rawhmac(digest, key, keylen,
/* 1H1 */ SHA1_DIGEST_SIZE, paramdigest,
/* 2H1 */ TPM_NONCE_SIZE, enonce->data,
/* 3H1 */ TPM_NONCE_SIZE, ononce->data,
/* 4H1 */ 1, &cont,
0, NULL);
out:
kfree(sdesc);
return ret;
}
/**
* TSS_checkhmac1 - Verify the result of an AUTH1_COMMAND (eg. Seal)
* @digest: Reply buffer
* @ordinal: The command ID, BE form
* @ononce: Odd nonce
* @key: The key to use in the HMAC generation
* @keylen: The size of @key
* @...: Pairs of size and pointer of data elements to load into hash
* @0,NULL: Terminator
*/
static int TSS_checkhmac1(struct tpm_buf *tb,
__be32 ordinal,
const struct tpm_odd_nonce *ononce,
const unsigned char *key, unsigned keylen,
...)
{
uint32_t bufsize;
uint16_t tag;
__be32 result;
struct tpm_even_nonce *enonce;
unsigned char *continueflag;
unsigned char *authdata;
unsigned char testhmac[SHA1_DIGEST_SIZE];
unsigned char paramdigest[SHA1_DIGEST_SIZE];
struct tpm_sdesc *sdesc;
unsigned int dlen;
unsigned int dpos;
va_list argp;
int ret;
SET_BUF_OFFSET(tb, 0);
tag = LOAD16(tb);
bufsize = LOAD32(tb);
result = LOAD32BE(tb);
if (tag == TPM_TAG_RSP_COMMAND)
return 0;
if (tag != TPM_TAG_RSP_AUTH1_COMMAND)
return -EINVAL;
authdata = tb->data + bufsize - SHA1_DIGEST_SIZE;
continueflag = authdata - 1;
enonce = (void *)continueflag - TPM_NONCE_SIZE;
/* Load the 1S, 2S, 3S, ... marked fields into a hash. The digest
* value is then 1H1 loaded into the HMAC below.
*/
sdesc = tpm_init_sdesc(tpm_hashalg);
if (IS_ERR(sdesc)) {
pr_info("Can't alloc %s\n", tpm_hash_alg);
return PTR_ERR(sdesc);
}
ret = crypto_shash_init(&sdesc->shash);
if (ret < 0)
goto out;
ret = crypto_shash_update(&sdesc->shash, (const u8 *)&result,
sizeof(result)); /* 1S */
if (ret < 0)
goto out;
ret = crypto_shash_update(&sdesc->shash, (const u8 *)&ordinal,
sizeof(ordinal)); /* 2S */
if (ret < 0)
goto out;
va_start(argp, keylen);
for (;;) {
dlen = va_arg(argp, unsigned int);
dpos = va_arg(argp, unsigned int);
if (!dlen && !dpos)
break;
ret = crypto_shash_update(&sdesc->shash, tb->data + dpos, dlen);
if (ret < 0)
break;
}
va_end(argp);
if (!ret)
ret = crypto_shash_final(&sdesc->shash, paramdigest);
if (ret < 0)
goto out;
/* Generate the HMAC digest */
ret = TSS_rawhmac(testhmac, key, keylen,
/* 1H1 */ SHA1_DIGEST_SIZE, paramdigest,
/* 2H1 */ TPM_NONCE_SIZE, enonce->data,
/* 3H1 */ TPM_NONCE_SIZE, ononce->data,
/* 4H1 */ 1, continueflag,
0, NULL);
if (ret < 0)
goto out;
if (memcmp(testhmac, authdata, SHA1_DIGEST_SIZE))
ret = -EINVAL;
out:
kfree(sdesc);
return ret;
}
/**
* TSS_checkhmac2 - Verify the result of an AUTH2_COMMAND (eg. Unseal)
* @digest: Reply buffer
* @ordinal: The command ID, BE form
* @ononce: Odd nonce
* @key1: The key to use in the authorisation session HMAC generation (nH1)
* @keylen1: The size of @key1
* @key2: The key to use in the data session HMAC generation (nH2)
* @keylen2: The size of @key2
* @...: Pairs of size and pointer of data elements to load into hash
* @0,NULL: Terminator
*
* verify the AUTH2_COMMAND (unseal) result from TPM
*/
static int TSS_checkhmac2(struct tpm_buf *tb,
__be32 ordinal,
const struct tpm_odd_nonce *ononce,
const unsigned char *key1, unsigned keylen1,
const unsigned char *key2, unsigned keylen2,
...)
{
uint32_t bufsize;
uint16_t tag;
__be32 result;
const struct tpm_even_nonce *enonce1;
const unsigned char *continueflag1;
const unsigned char *authdata1;
const struct tpm_even_nonce *enonce2;
const unsigned char *continueflag2;
const unsigned char *authdata2;
unsigned char testhmac1[SHA1_DIGEST_SIZE];
unsigned char testhmac2[SHA1_DIGEST_SIZE];
unsigned char paramdigest[SHA1_DIGEST_SIZE];
struct tpm_sdesc *sdesc;
unsigned int dlen;
unsigned int dpos;
va_list argp;
int ret;
bufsize = LOAD32(tb);
tag = LOAD16(tb);
result = LOAD32BE(tb);
if (tag == TPM_TAG_RSP_COMMAND)
return 0;
if (tag != TPM_TAG_RSP_AUTH2_COMMAND)
return -EINVAL;
authdata1 = tb->data + bufsize - (SHA1_DIGEST_SIZE + 1
+ SHA1_DIGEST_SIZE + SHA1_DIGEST_SIZE);
authdata2 = tb->data + bufsize - (SHA1_DIGEST_SIZE);
continueflag1 = authdata1 - 1;
continueflag2 = authdata2 - 1;
enonce1 = (const void *)continueflag1 - TPM_NONCE_SIZE;
enonce2 = (const void *)continueflag2 - TPM_NONCE_SIZE;
/* Load the 1S, 2S, 3S, ... marked fields into a hash. The digest
* value is then 1H1 loaded into the HMAC below.
*/
sdesc = tpm_init_sdesc(tpm_hashalg);
if (IS_ERR(sdesc)) {
pr_info("Can't alloc %s\n", tpm_hash_alg);
return PTR_ERR(sdesc);
}
ret = crypto_shash_init(&sdesc->shash);
if (ret < 0)
goto out;
ret = crypto_shash_update(&sdesc->shash, (const u8 *)&result,
sizeof(result)); /* 1S */
if (ret < 0)
goto out;
ret = crypto_shash_update(&sdesc->shash, (const u8 *)&ordinal,
sizeof(ordinal)); /* 2S */
if (ret < 0)
goto out;
va_start(argp, keylen2);
for (;;) {
dlen = va_arg(argp, unsigned int);
dpos = va_arg(argp, unsigned int);
if (!dlen && !dpos)
break;
ret = crypto_shash_update(&sdesc->shash, tb->data + dpos, dlen);
if (ret < 0)
break;
}
va_end(argp);
if (!ret)
ret = crypto_shash_final(&sdesc->shash, paramdigest);
if (ret < 0)
goto out;
ret = TSS_rawhmac(testhmac1, key1, keylen1,
/* 1H1 */ SHA1_DIGEST_SIZE, paramdigest,
/* 2H1 */ TPM_NONCE_SIZE, enonce1->data,
/* 3H1 */ TPM_NONCE_SIZE, ononce->data,
/* 4H1 */ 1, continueflag1,
0, NULL);
if (ret < 0)
goto out;
if (memcmp(testhmac1, authdata1, SHA1_DIGEST_SIZE)) {
ret = -EINVAL;
goto out;
}
ret = TSS_rawhmac(testhmac2, key2, keylen2,
/* 1H2 */ SHA1_DIGEST_SIZE, paramdigest,
/* 2H2 */ TPM_NONCE_SIZE, enonce2->data,
/* 3H2 */ TPM_NONCE_SIZE, ononce->data,
/* 4H2 */ 1, continueflag2,
0, NULL);
if (ret < 0)
goto out;
if (memcmp(testhmac2, authdata2, SHA1_DIGEST_SIZE))
ret = -EINVAL;
out:
kfree(sdesc);
return ret;
}
/*
* For key specific tpm requests, we will generate and send our
* own TPM command packets using the drivers send function.
*/
static int tpm_send_dump(struct tpm_chip *chip, struct tpm_buf *cmd,
const char *desc)
{
int rc;
kenter(",{%u,%u},%s",
cmd->len, be32_to_cpu(*(__be32 *)(cmd->data + TPM_SIZE_OFFSET)), desc);
dump_tpm_buf(cmd);
rc = tpm_send_command(chip, cmd->data, MAX_BUF_SIZE, desc);
dump_tpm_buf(cmd);
if (rc > 0)
/* Can't return positive return codes values to keyctl */
rc = -EPERM;
else
SET_BUF_OFFSET(cmd, TPM_DATA_OFFSET);
kleave(" = %d [%u]", rc, be32_to_cpu(*(__be32 *)(cmd->data + TPM_SIZE_OFFSET)));
return rc;
}
/*
* Create an object specific authorisation protocol (OSAP) session
*/
static int tpm_create_osap(struct tpm_chip *chip,
struct tpm_buf *tb, struct tpm_osapsess *s,
const unsigned char *keyauth,
enum tpm_entity_type keytype, uint32_t keyhandle)
{
struct tpm_even_nonce enonce;
struct tpm_odd_nonce ononce;
int ret;
kenter("");
ret = tpm_gen_odd_nonce(chip, &ononce);
if (ret < 0)
return ret;
INIT_BUF(tb);
store16(tb, TPM_TAG_RQU_COMMAND);
store32(tb, TPM_OSAP_SIZE);
store32(tb, TPM_ORD_OSAP);
store16(tb, keytype);
store32(tb, keyhandle);
store_s(tb, ononce.data, TPM_NONCE_SIZE);
ret = tpm_send_dump(chip, tb, "creating OSAP session");
if (ret < 0)
goto out;
s->handle = LOAD32(tb);
LOAD_S(tb, s->enonce.data, TPM_NONCE_SIZE);
LOAD_S(tb, enonce.data, TPM_NONCE_SIZE);
/* Calculate the encrypted shared secret */
ret = TSS_rawhmac(s->secret, keyauth, SHA1_DIGEST_SIZE,
TPM_NONCE_SIZE, enonce.data,
TPM_NONCE_SIZE, ononce.data,
0, NULL);
out:
kleave(" = %d [%08x]", ret, s->handle);
return ret;
}
/*
* Create an object independent authorisation protocol (oiap) session
*/
static int tpm_create_oiap(struct tpm_chip *chip, struct tpm_buf *tb,
uint32_t *handle, struct tpm_even_nonce *enonce)
{
int ret;
kenter("");
INIT_BUF(tb);
store16(tb, TPM_TAG_RQU_COMMAND);
store32(tb, TPM_OIAP_SIZE);
store32(tb, TPM_ORD_OIAP);
ret = tpm_send_dump(chip, tb, "creating OIAP session");
if (ret < 0)
return ret;
*handle = LOAD32(tb);
LOAD_S(tb, enonce->data, TPM_NONCE_SIZE);
kleave(" = 0 [%08x]", *handle);
return 0;
}
struct tpm_digests {
unsigned char encauth[SHA1_DIGEST_SIZE];
unsigned char encauth2[SHA1_DIGEST_SIZE];
unsigned char pubauth[SHA1_DIGEST_SIZE];
unsigned char xorwork[SHA1_DIGEST_SIZE * 2];
unsigned char xorhash[SHA1_DIGEST_SIZE];
struct tpm_odd_nonce ononce;
};
/*
* Calculate an XOR-based symmetric key that can be used to encrypt protected
* data. The key is left in td->xorhash.
*/
static int tpm_calc_symmetric_authkey(struct tpm_digests *td,
const u8 *secret,
const struct tpm_even_nonce *enonce)
{
memcpy(td->xorwork, secret, SHA1_DIGEST_SIZE);
memcpy(td->xorwork + SHA1_DIGEST_SIZE, enonce->data, SHA1_DIGEST_SIZE);
return TSS_sha1(td->xorwork, SHA1_DIGEST_SIZE * 2, td->xorhash);
}
/*
* Encrypt/decrypt data with a previously calculated XOR-based symmetric key.
*/
static void tpm_crypt_with_authkey(const struct tpm_digests *td,
const u8 *data, u8 *buffer)
{
int i;
for (i = 0; i < SHA1_DIGEST_SIZE; ++i)
buffer[i] = td->xorhash[i] ^ data[i];
}
/**
* tpm_seal - Encrypt one key according to another plus PCR state
* @chip: The chip to use
* @tb: Large scratch buffer for I/O
* @keytype: Type of entity attached to @keyhandle
* @keyhandle: TPM-resident key used to encrypt
* @keyauth: 'Password' to use the key.
* @rawdata: Data to be encrypted
* @rawlen: Length of @rawdata
* @encbuffer: Buffer to hold the encrypted data (max SHA1_DIGEST_SIZE)
* @_enclen: Where to place the size of the encrypted data
* @encauth: 'Password' to use to encrypt authorisation key
* @pcrinfo: Information on PCR register values to seal to (must not be NULL)
* @pcrinfosize: size of @pcrinfo
*
* Have the TPM seal (encrypt) the data in the data buffer. The encryption is
* based on a key already resident in the TPM and may also include the state of
* one or more Platform Configuration Registers (PCRs).
*
* AUTH1 is used for sealing key.
*/
int tpm_seal(struct tpm_chip *chip,
struct tpm_buf *tb, enum tpm_entity_type keytype,
uint32_t keyhandle, const unsigned char *keyauth,
const unsigned char *rawdata, uint32_t rawlen,
unsigned char *encbuffer, uint32_t *_enclen,
const unsigned char *encauth,
const unsigned char *pcrinfo, uint32_t pcrinfosize)
{
struct tpm_osapsess sess;
struct tpm_digests *td;
unsigned char cont;
__be32 ordinal_be;
__be32 rawlen_be;
__be32 pcrinfosize_be;
int sealinfosize;
int encdatasize;
int storedsize;
int ret;
kenter("");
/* alloc some work space for all the hashes */
td = kmalloc(sizeof *td, GFP_KERNEL);
if (!td)
return -ENOMEM;
/* get session for sealing key */
ret = tpm_create_osap(chip, tb, &sess, keyauth, keytype, keyhandle);
if (ret < 0)
goto out;
dump_sess(&sess);
/* We need to pass a 'password' to the TPM with which it will encrypt
* the sealed data before returning it. So that the password doesn't
* travel to the TPM in the clear, we generate a symmetric key from the
* negotiated and encrypted session data and encrypt the password with
* that.
*/
ret = tpm_calc_symmetric_authkey(td, sess.secret, &sess.enonce);
if (ret < 0)
goto out;
tpm_crypt_with_authkey(td, encauth, td->encauth);
/* Set up the parameters we will be sending */
ret = tpm_gen_odd_nonce(chip, &td->ononce);
if (ret < 0)
goto out;
ordinal_be = cpu_to_be32(TPM_ORD_SEAL);
rawlen_be = cpu_to_be32(rawlen);
pcrinfosize_be = cpu_to_be32(pcrinfosize);
cont = 0;
/* calculate authorization HMAC value */
BUG_ON(!pcrinfo);
ret = TSS_authhmac(td->pubauth, sess.secret, SHA1_DIGEST_SIZE,
&sess.enonce, &td->ononce, cont,
/* 1S */ sizeof(__be32), &ordinal_be,
/* 2S */ SHA1_DIGEST_SIZE, td->encauth,
/* 3S */ sizeof(__be32), &pcrinfosize_be,
/* 4S */ pcrinfosize, pcrinfo,
/* 5S */ sizeof(__be32), &rawlen_be,
/* 6S */ rawlen, rawdata,
0, NULL);
if (ret < 0)
goto out;
/* build and send the TPM request packet */
INIT_BUF(tb);
store16(tb, TPM_TAG_RQU_AUTH1_COMMAND);
store32(tb, TPM_SEAL_SIZE + pcrinfosize + rawlen);
store32(tb, TPM_ORD_SEAL);
store32(tb, keyhandle);
store_s(tb, td->encauth, SHA1_DIGEST_SIZE);
store32(tb, pcrinfosize);
store_s(tb, pcrinfo, pcrinfosize);
store32(tb, rawlen);
store_s(tb, rawdata, rawlen);
store32(tb, sess.handle);
store_s(tb, td->ononce.data, TPM_NONCE_SIZE);
store_8(tb, cont);
store_s(tb, td->pubauth, SHA1_DIGEST_SIZE);
ret = tpm_send_dump(chip, tb, "sealing data");
if (ret < 0)
goto out;
/* Look inside the TPM_STORED_DATA object to calculate the size of the
* returned encrypted data.
*/
SET_BUF_OFFSET(tb, TPM_DATA_OFFSET + sizeof(uint32_t));
sealinfosize = LOAD32(tb);
SET_BUF_OFFSET(tb, TPM_DATA_OFFSET + sizeof(uint32_t) * 2 + sealinfosize);
storedsize = sizeof(uint32_t) * 2 + sealinfosize +
sizeof(uint32_t) + encdatasize;
/* check the HMAC in the response */
ret = TSS_checkhmac1(tb, ordinal_be, &td->ononce,
sess.secret, SHA1_DIGEST_SIZE,
/* 3S */ storedsize, TPM_DATA_OFFSET,
0, NULL);
/* copy the encrypted data to caller's buffer */
if (!ret) {
SET_BUF_OFFSET(tb, TPM_DATA_OFFSET);
LOAD_S(tb, encbuffer, storedsize);
*_enclen = storedsize;
}
out:
kfree(td);
kleave(" = %d", ret);
return ret;
}
EXPORT_SYMBOL_GPL(tpm_seal);
/**
* tpm_unseal - Encrypt one key according to another plus PCR state
* @chip: The chip to use
* @tb: Large scratch buffer for I/O
* @keyhandle: TPM-resident key used to decrypt
* @keyauth: HMAC key
* @encdata: Data to be decrypted
* @enclen: Length of @encdata
* @decauth: Data to use to decrypt the authorisation key
* @rawbuffer: Buffer to hold the decrypted data (max SHA1_DIGEST_SIZE)
* @_rawlen: Where to place the size of the decrypted data
*
* use the AUTH2_COMMAND form of unseal, to authorize both key and blob
*/
int tpm_unseal(struct tpm_chip *chip, struct tpm_buf *tb,
uint32_t keyhandle, const unsigned char *keyauth,
const unsigned char *encdata, int enclen,
const unsigned char *decauth,
unsigned char *rawbuffer, unsigned int *_rawlen)
{
struct tpm_odd_nonce ononce;
struct tpm_even_nonce enonce1;
struct tpm_even_nonce enonce2;
unsigned char authdata1[SHA1_DIGEST_SIZE];
unsigned char authdata2[SHA1_DIGEST_SIZE];
uint32_t authhandle1 = 0;
uint32_t authhandle2 = 0;
unsigned char cont = 0;
__be32 ordinal;
int ret;
kenter("");
/* sessions for unsealing key and data */
ret = tpm_create_oiap(chip, tb, &authhandle1, &enonce1);
if (ret < 0) {
pr_info("Failed to create OIAP 1 (%d)\n", ret);
goto out;
}
ret = tpm_create_oiap(chip, tb, &authhandle2, &enonce2);
if (ret < 0) {
pr_info("Failed to create OIAP 2 (%d)\n", ret);
goto out;
}
ordinal = cpu_to_be32(TPM_ORD_UNSEAL);
ret = tpm_gen_odd_nonce(chip, &ononce);
if (ret < 0)
goto out;
ret = TSS_authhmac(authdata1, keyauth, TPM_NONCE_SIZE,
&enonce1, &ononce, cont,
/* 1S */ sizeof(__be32), &ordinal,
/* 2S */ enclen, encdata,
0, NULL);
if (ret < 0)
goto out;
ret = TSS_authhmac(authdata2, decauth, TPM_NONCE_SIZE,
&enonce2, &ononce, cont,
/* 1S */ sizeof(__be32), &ordinal,
/* 2S */ enclen, encdata,
0, NULL);
if (ret < 0)
goto out;
/* build and send TPM request packet */
INIT_BUF(tb);
store16(tb, TPM_TAG_RQU_AUTH2_COMMAND);
store32(tb, TPM_UNSEAL_SIZE + enclen);
store32(tb, TPM_ORD_UNSEAL);
store32(tb, keyhandle);
store_s(tb, encdata, enclen);
store32(tb, authhandle1);
store_s(tb, ononce.data, TPM_NONCE_SIZE);
store_8(tb, cont);
store_s(tb, authdata1, SHA1_DIGEST_SIZE);
store32(tb, authhandle2);
store_s(tb, ononce.data, TPM_NONCE_SIZE);
store_8(tb, cont);
store_s(tb, authdata2, SHA1_DIGEST_SIZE);
ret = tpm_send_dump(chip, tb, "unsealing data");
if (ret < 0) {
pr_info("authhmac failed (%d)\n", ret);
goto out;
}
*_rawlen = LOAD32(tb);
ret = TSS_checkhmac2(tb, ordinal, &ononce,
keyauth, SHA1_DIGEST_SIZE,
decauth, SHA1_DIGEST_SIZE,
/* 3S */ sizeof(uint32_t), TPM_DATA_OFFSET,
/* 4S */ *_rawlen, TPM_DATA_OFFSET + sizeof(uint32_t),
0, 0);
if (ret < 0) {
pr_info("TSS_checkhmac2 failed (%d)\n", ret);
goto out;
}
LOAD_S(tb, rawbuffer, *_rawlen);
out:
kleave(" = %d", ret);
return ret;
}
EXPORT_SYMBOL_GPL(tpm_unseal);
enum tpm_key_usage {
TPM_KEY_SIGNING = 0x0010,
TPM_KEY_STORAGE = 0x0011,
TPM_KEY_IDENTITY = 0x0012,
TPM_KEY_AUTHCHANGE = 0x0013,
TPM_KEY_BIND = 0x0014,
TPM_KEY_LEGACY = 0x0015,
TPM_KEY_MIGRATE = 0x0016,
};
enum tpm_algorithm_id {
TPM_ALG_RSA = 0x00000001,
TPM_ALG_SHA = 0x00000004,
TPM_ALG_HMAC = 0x00000005,
TPM_ALG_AES128 = 0x00000006,
TPM_ALG_MGF1 = 0x00000007,
TPM_ALG_AES192 = 0x00000008,
TPM_ALG_AES256 = 0x00000009,
TPM_ALG_XOR = 0x0000000a,
};
enum tpm_enc_scheme {
TPM_ES_NONE = 0x0001,
TPM_ES_RSAESPKCSv15 = 0x0002,
TPM_ES_RSAESOAEP_SHA1_MGF1 = 0x0003,
TPM_ES_SYM_CTR = 0x0004,
TPM_ES_SYM_OFB = 0x0005,
};
enum tpm_sig_scheme {
TPM_SS_NONE = 0x0001,
TPM_SS_RSAESPKCSv15_SHA1 = 0x0002,
TPM_SS_RSAESPKCSv15_DER = 0x0003,
TPM_SS_RSAESPKCSv15_INFO = 0x0004,
};
enum tpm_auth_data_usage {
TPM_AUTH_NEVER = 0x00,
TPM_AUTH_ALWAYS = 0x01,
TPM_NO_READ_PUBKEY_AUTH = 0x03,
};
#define TPM_KEY_REDIRECTION 0x00000001
#define TPM_KEY_MIGRATABLE 0x00000002
#define TPM_KEY_ISVOLATILE 0x00000004
#define TPM_KEY_PCRIGNOREDONREAD 0x00000008
#define TPM_KEY_MIGRATEAUTHORITY 0x00000010
struct tpm_key {
struct tpm_struct_ver {
u8 major, minor, rev_major, rev_minor;
} ver;
__be16 key_usage; /* enum tpm_key_usage */
__be32 key_flags;
u8 auth_data_usage; /* enum tpm_auth_data_usage */
struct tpm_key_parms {
__be32 algorithm_id; /* enum tpm_algorithm_id */
__be16 enc_scheme; /* enum tpm_enc_scheme */
__be16 sig_scheme; /* enum tpm_sig_scheme */
__be32 parm_size;
struct tpm_rsa_key_parms {
__be32 key_length;
__be32 num_primes;
__be32 exponent_size;
} __packed rsa;
} __packed parms;
__be32 pcr_info_size;
struct tpm_store_pubkey {
__be32 key_length;
u8 key_data[0];
} __packed pub;
__be32 enc_data_size;
u8 enc_data[0];
} __packed;
/**
* tpm_create_wrap_key - Generate a new key and return it encrypted
* @chip: The chip to use
* @tb: Large scratch buffer for I/O
* @parent_type: Type of entity attached to @parent_handle
* @parent_handle: TPM-resident key used to encrypt
* @parent_auth: Parent authorisation HMAC key
* @usage_auth: Encrypted usage authdata for the key
* @migration_auth: Encrypted migration authdata for the key (or NULL)
* @_wrapped_key: Pointer to where to return the wrapped key (kmalloc'd)
*
* Have the TPM generate a new key and return it encrypted. The encryption is
* based on a key already resident in the TPM and may also include the state of
* one or more Platform Configuration Registers (PCRs).
*
* AUTH1 is used for sealing key.
*/
int tpm_create_wrap_key(struct tpm_chip *chip,
enum tpm_entity_type parent_type,
uint32_t parent_handle,
const unsigned char *parent_auth,
const unsigned char *usage_auth,
const unsigned char *migration_auth,
struct tpm_wrapped_key **_wrapped_key)
{
struct tpm_wrapped_key *wrapped_key;
struct tpm_osapsess sess;
struct tpm_digests *td;
struct tpm_buf *tb;
struct tpm_key *result;
unsigned char cont;
__be32 ordinal_be;
int key_size;
int ret;
struct tpm_key tpm_key = {
.ver = { 0x01, 0x01, 0x00, 0x00 },
.key_usage = cpu_to_be16(TPM_KEY_SIGNING),
.key_flags = cpu_to_be32(0),
.auth_data_usage = TPM_AUTH_ALWAYS,
.parms.algorithm_id = cpu_to_be32(TPM_ALG_RSA),
.parms.enc_scheme = cpu_to_be16(TPM_ES_RSAESPKCSv15),
.parms.sig_scheme = cpu_to_be16(TPM_SS_RSAESPKCSv15_SHA1),
.parms.parm_size = cpu_to_be32(sizeof(struct tpm_rsa_key_parms)),
.parms.rsa.key_length = cpu_to_be32(2048),
.parms.rsa.num_primes = cpu_to_be32(2),
.parms.rsa.exponent_size = cpu_to_be32(0),
.pcr_info_size = cpu_to_be32(0),
.pub.key_length = cpu_to_be32(0),
.enc_data_size = cpu_to_be32(0),
};
kenter("");
if (migration_auth)
tpm_key.key_flags |= cpu_to_be32(TPM_KEY_MIGRATABLE);
/* alloc some work space */
tb = kmalloc(sizeof(*tb) + sizeof(*td), GFP_KERNEL);
if (!tb)
return -ENOMEM;
td = (void *)tb + sizeof(*tb);
/* Get the encryption session */
ret = tpm_create_osap(chip, tb, &sess,
parent_auth, parent_type, parent_handle);
if (ret < 0)
goto out;
dump_sess(&sess);
/* We need to pass 'passwords' to the TPM with which it will encrypt
* the key before returning it. So that the passwords don't travel to
* the TPM in the clear, we generate a symmetric key from the
* negotiated and encrypted session data and encrypt the passwords with
* that.
*/
ret = tpm_calc_symmetric_authkey(td, sess.secret, &sess.enonce);
if (ret < 0)
goto out;
tpm_crypt_with_authkey(td, usage_auth, td->encauth);
if (migration_auth)
tpm_crypt_with_authkey(td, migration_auth, td->encauth2);
else
tpm_crypt_with_authkey(td, sess.enonce.data, td->encauth2);
/* Set up the parameters we will be sending */
ret = tpm_gen_odd_nonce(chip, &td->ononce);
if (ret < 0)
goto out;
/* calculate authorization HMAC value */
ordinal_be = cpu_to_be32(TPM_ORD_CREATEWRAPKEY);
cont = 0;
ret = TSS_authhmac(td->pubauth, sess.secret, SHA1_DIGEST_SIZE,
&sess.enonce, &td->ononce, cont,
/* 1S */ sizeof(__be32), &ordinal_be,
/* 2S */ SHA1_DIGEST_SIZE, td->encauth,
/* 3S */ SHA1_DIGEST_SIZE, td->encauth2,
/* 4S */ sizeof(tpm_key), &tpm_key,
0, NULL);
if (ret < 0)
goto out;
/* build and send the TPM request packet */
INIT_BUF(tb);
store16(tb, TPM_TAG_RQU_AUTH1_COMMAND);
store32(tb, TPM_DATA_OFFSET + 44 + sizeof(tpm_key) + 45);
store32(tb, TPM_ORD_CREATEWRAPKEY);
store32(tb, parent_handle);
store_s(tb, td->encauth, SHA1_DIGEST_SIZE);
store_s(tb, td->encauth2, SHA1_DIGEST_SIZE);
store_s(tb, &tpm_key, sizeof(tpm_key));
store32(tb, sess.handle);
store_s(tb, td->ononce.data, TPM_NONCE_SIZE);
store_8(tb, cont);
store_s(tb, td->pubauth, SHA1_DIGEST_SIZE);
ret = tpm_send_dump(chip, tb, "creating key");
if (ret < 0)
goto out;
/* We need to work out how big the TPM_KEY or TPM_KEY12 struct we got
* back is. These structs have several variable-length fields inside
* to make parsing more difficult. However, they are only followed by
* fixed-length structs...
*/
SET_BUF_OFFSET(tb, TPM_SIZE_OFFSET);
key_size = LOAD32(tb);
key_size -= TPM_DATA_OFFSET;
key_size -= 2 * TPM_NONCE_SIZE + 1;
if (key_size < sizeof(tpm_key)) {
ret = -EBADMSG;
goto out;
}
/* Check the HMAC in the response */
ret = TSS_checkhmac1(tb, ordinal_be, &td->ononce,
sess.secret, SHA1_DIGEST_SIZE,
/* 3S */ key_size, TPM_DATA_OFFSET,
0, NULL);
if (ret < 0)
goto out;
/* Parse the key */
result = (void *)tb->data + TPM_DATA_OFFSET;
ret = -EBADMSG;
if (key_size < sizeof(tpm_key) + be32_to_cpu(tpm_key.parms.rsa.key_length) / 8)
goto out;
if (memcmp(result, &tpm_key, offsetof(struct tpm_key, pub.key_length)) != 0)
goto out;
if (be32_to_cpu(result->pub.key_length) >
be32_to_cpu(tpm_key.parms.rsa.key_length) / 8)
goto out;
ret = -ENOMEM;
wrapped_key = kmalloc(sizeof(struct tpm_wrapped_key) + key_size, GFP_KERNEL);
if (!wrapped_key)
goto out;
wrapped_key->data_len = key_size;
wrapped_key->pubkey_offset = offsetof(struct tpm_key, pub.key_data);
wrapped_key->pubkey_len = be32_to_cpu(result->pub.key_length);
memcpy(wrapped_key->data, result, key_size);
*_wrapped_key = wrapped_key;
ret = 0;
out:
kfree(tb);
kleave(" = %d", ret);
return ret;
}
EXPORT_SYMBOL_GPL(tpm_create_wrap_key);
/**
* tpm_load_key2 - Load a key and decrypt it
* @chip: The chip to use
* @tb: Large scratch buffer for I/O
* @parent_type: Type of entity attached to @parent_handle
* @parent_handle: TPM-resident key used to encrypt
* @parent_auth: Parent authorisation HMAC key
* @wrapped_key: The wrapped key
* @_key_handle: The TPM's handle on the key
*
* Have the TPM decrypt a key and load it into its memory. The TPM then
* returns a handle to the key that we can use in other operations.
*
* AUTH1 is used for sealing key.
*/
int tpm_load_key2(struct tpm_chip *chip,
enum tpm_entity_type parent_type,
uint32_t parent_handle,
const unsigned char *parent_auth,
const struct tpm_wrapped_key *wrapped_key,
uint32_t *_key_handle)
{
struct tpm_osapsess sess;
struct tpm_digests *td;
struct tpm_buf *tb;
unsigned char cont;
__be32 ordinal_be;
int ret;
kenter("");
/* alloc some work space */
tb = kmalloc(sizeof(*tb) + sizeof(*td), GFP_KERNEL);
if (!tb)
return -ENOMEM;
td = (void *)tb + sizeof(*tb);
/* Get the encryption session */
ret = tpm_create_osap(chip, tb, &sess,
parent_auth, parent_type, parent_handle);
if (ret < 0)
goto out;
dump_sess(&sess);
/* Set up the parameters we will be sending */
ret = tpm_gen_odd_nonce(chip, &td->ononce);
if (ret < 0)
goto out;
/* calculate authorization HMAC value */
ordinal_be = cpu_to_be32(TPM_ORD_LOADKEY2);
cont = 0;
ret = TSS_authhmac(td->pubauth, sess.secret, SHA1_DIGEST_SIZE,
&sess.enonce, &td->ononce, cont,
/* 1S */ sizeof(__be32), &ordinal_be,
/* 2S */ wrapped_key->data_len, wrapped_key->data,
0, NULL);
if (ret < 0)
goto out;
/* build and send the TPM request packet */
INIT_BUF(tb);
store16(tb, TPM_TAG_RQU_AUTH1_COMMAND);
store32(tb, TPM_DATA_OFFSET + 4 + wrapped_key->data_len + 45);
store32(tb, TPM_ORD_LOADKEY2);
store32(tb, parent_handle);
store_s(tb, wrapped_key->data, wrapped_key->data_len);
store32(tb, sess.handle);
store_s(tb, td->ononce.data, TPM_NONCE_SIZE);
store_8(tb, cont);
store_s(tb, td->pubauth, SHA1_DIGEST_SIZE);
ret = tpm_send_dump(chip, tb, "loading key");
if (ret < 0)
goto out;
/* Check the HMAC in the response */
ret = TSS_checkhmac1(tb, ordinal_be, &td->ononce,
sess.secret, SHA1_DIGEST_SIZE,
0, NULL);
if (ret < 0)
goto out;
*_key_handle = LOAD32(tb);
ret = 0;
out:
kfree(tb);
kleave(" = %d [%08x]", ret, *_key_handle);
return ret;
}
EXPORT_SYMBOL_GPL(tpm_load_key2);
/**
* tpm_flush_specific - Tell the TPM to discard a handle and associated resources
* @chip: The chip to use
* @handle: The handle to discard
* @handle_type: The type of handle
*/
int tpm_flush_specific(struct tpm_chip *chip,
uint32_t handle, enum tpm_resource_type handle_type)
{
struct tpm_buf *tb;
int ret;
/* alloc some work space */
tb = kmalloc(sizeof(*tb), GFP_KERNEL);
if (!tb)
return -ENOMEM;
/* build and send the TPM request packet */
INIT_BUF(tb);
store16(tb, TPM_TAG_RQU_COMMAND);
store32(tb, TPM_DATA_OFFSET + 8);
store32(tb, TPM_ORD_FLUSHSPECIFIC);
store32(tb, handle);
store32(tb, handle_type);
ret = tpm_send_dump(chip, tb, "flushing handle");
kfree(tb);
return ret;
}
EXPORT_SYMBOL_GPL(tpm_flush_specific);
/**
* tpm_library_use - Tell the TPM library we want to make use of it
*
* Tell the TPM library that we want to make use of it, allowing it to
* allocate the resources it needs.
*/
int tpm_library_use(void)
{
struct crypto_shash *hashalg = NULL;
struct crypto_shash *hmacalg = NULL;
int ret;
if (atomic_inc_not_zero(&tpm_library_usage))
return 0;
/* We don't want to hold a mutex whilst allocating a crypto
* object as it may have to call up to userspace.
*/
hmacalg = crypto_alloc_shash(tpm_hmac_alg, 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(hmacalg)) {
pr_info("Could not allocate crypto %s\n", tpm_hmac_alg);
ret = PTR_ERR(hmacalg);
goto hmacalg_fail;
}
hashalg = crypto_alloc_shash(tpm_hash_alg, 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(hashalg)) {
pr_info("Could not allocate crypto %s\n", tpm_hash_alg);
ret = PTR_ERR(hashalg);
goto hashalg_fail;
}
mutex_lock(&tpm_library_init_mutex);
if (atomic_inc_return(&tpm_library_usage) == 1) {
tpm_hmacalg = hmacalg;
tpm_hashalg = hashalg;
} else {
crypto_free_shash(hashalg);
crypto_free_shash(hmacalg);
}
mutex_unlock(&tpm_library_init_mutex);
return 0;
hashalg_fail:
crypto_free_shash(tpm_hmacalg);
hmacalg_fail:
return ret;
}
EXPORT_SYMBOL_GPL(tpm_library_use);
/**
* tpm_library_unuse - Tell the TPM library we've finished with it
*
* Tell the TPM library we've finished with it, allowing it to free the
* resources it had allocated.
*/
void tpm_library_unuse(void)
{
if (atomic_add_unless(&tpm_library_usage, -1, 1))
return;
mutex_lock(&tpm_library_init_mutex);
if (atomic_dec_and_test(&tpm_library_usage)) {
crypto_free_shash(tpm_hashalg);
crypto_free_shash(tpm_hmacalg);
}
mutex_unlock(&tpm_library_init_mutex);
}
EXPORT_SYMBOL_GPL(tpm_library_unuse);