#include "tpm.h"
#include <linux/random.h>
#include <linux/scatterlist.h>
#include <linux/unaligned.h>
#include <crypto/kpp.h>
#include <crypto/ecdh.h>
#include <crypto/sha2.h>
#include <crypto/utils.h>
#define AUTH_MAX_NAMES 3
#define AES_KEY_BYTES AES_KEYSIZE_128
#define AES_KEY_BITS (AES_KEY_BYTES*8)
struct tpm2_auth {
u32 handle;
u32 session;
u8 our_nonce[SHA256_DIGEST_SIZE];
u8 tpm_nonce[SHA256_DIGEST_SIZE];
union {
u8 salt[EC_PT_SZ];
u8 scratch[AES_KEY_BYTES + AES_BLOCK_SIZE];
};
u8 session_key[SHA256_DIGEST_SIZE];
u8 passphrase[SHA256_DIGEST_SIZE];
int passphrase_len;
struct aes_enckey aes_key;
u8 attrs;
__be32 ordinal;
u32 name_h[AUTH_MAX_NAMES];
u8 name[AUTH_MAX_NAMES][2 + SHA512_DIGEST_SIZE];
};
#ifdef CONFIG_TCG_TPM2_HMAC
static int name_size(const u8 *name)
{
u16 hash_alg = get_unaligned_be16(name);
switch (hash_alg) {
case TPM_ALG_SHA1:
return SHA1_DIGEST_SIZE + 2;
case TPM_ALG_SHA256:
return SHA256_DIGEST_SIZE + 2;
case TPM_ALG_SHA384:
return SHA384_DIGEST_SIZE + 2;
case TPM_ALG_SHA512:
return SHA512_DIGEST_SIZE + 2;
default:
pr_warn("tpm: unsupported name algorithm: 0x%04x\n", hash_alg);
return -EINVAL;
}
}
static int tpm2_read_public(struct tpm_chip *chip, u32 handle, void *name)
{
u32 mso = tpm2_handle_mso(handle);
off_t offset = TPM_HEADER_SIZE;
int rc, name_size_alg;
struct tpm_buf buf;
if (mso != TPM2_MSO_PERSISTENT && mso != TPM2_MSO_VOLATILE &&
mso != TPM2_MSO_NVRAM) {
memcpy(name, &handle, sizeof(u32));
return sizeof(u32);
}
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_READ_PUBLIC);
if (rc)
return rc;
tpm_buf_append_u32(&buf, handle);
rc = tpm_transmit_cmd(chip, &buf, 0, "TPM2_ReadPublic");
if (rc) {
tpm_buf_destroy(&buf);
return tpm_ret_to_err(rc);
}
offset += tpm_buf_read_u16(&buf, &offset);
if (offset + 4 > tpm_buf_length(&buf)) {
tpm_buf_destroy(&buf);
return -EIO;
}
rc = tpm_buf_read_u16(&buf, &offset);
name_size_alg = name_size(&buf.data[offset]);
if (name_size_alg < 0)
return name_size_alg;
if (rc != name_size_alg) {
tpm_buf_destroy(&buf);
return -EIO;
}
if (offset + rc > tpm_buf_length(&buf)) {
tpm_buf_destroy(&buf);
return -EIO;
}
memcpy(name, &buf.data[offset], rc);
return name_size_alg;
}
#endif
int tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
u32 handle, u8 *name)
{
#ifdef CONFIG_TCG_TPM2_HMAC
enum tpm2_mso_type mso = tpm2_handle_mso(handle);
struct tpm2_auth *auth;
u16 name_size_alg;
int slot;
int ret;
#endif
if (!tpm2_chip_auth(chip)) {
tpm_buf_append_handle(chip, buf, handle);
return 0;
}
#ifdef CONFIG_TCG_TPM2_HMAC
slot = (tpm_buf_length(buf) - TPM_HEADER_SIZE) / 4;
if (slot >= AUTH_MAX_NAMES) {
dev_err(&chip->dev, "too many handles\n");
ret = -EIO;
goto err;
}
auth = chip->auth;
if (auth->session != tpm_buf_length(buf)) {
dev_err(&chip->dev, "session state malformed");
ret = -EIO;
goto err;
}
tpm_buf_append_u32(buf, handle);
auth->session += 4;
if (mso == TPM2_MSO_PERSISTENT ||
mso == TPM2_MSO_VOLATILE ||
mso == TPM2_MSO_NVRAM) {
if (!name) {
ret = tpm2_read_public(chip, handle, auth->name[slot]);
if (ret < 0)
goto err;
name_size_alg = ret;
}
} else {
if (name) {
dev_err(&chip->dev, "handle 0x%08x does not use a name\n",
handle);
ret = -EIO;
goto err;
}
}
auth->name_h[slot] = handle;
if (name)
memcpy(auth->name[slot], name, name_size_alg);
#endif
return 0;
#ifdef CONFIG_TCG_TPM2_HMAC
err:
tpm2_end_auth_session(chip);
return tpm_ret_to_err(ret);
#endif
}
EXPORT_SYMBOL_GPL(tpm_buf_append_name);
void tpm_buf_append_auth(struct tpm_chip *chip, struct tpm_buf *buf,
u8 *passphrase, int passphrase_len)
{
int offset = buf->handles * 4 + TPM_HEADER_SIZE;
u32 len = 9 + passphrase_len;
if (tpm_buf_length(buf) != offset) {
len += get_unaligned_be32(&buf->data[offset]);
put_unaligned_be32(len, &buf->data[offset]);
} else {
tpm_buf_append_u32(buf, len);
}
tpm_buf_append_u32(buf, TPM2_RS_PW);
tpm_buf_append_u16(buf, 0);
tpm_buf_append_u8(buf, 0);
tpm_buf_append_u16(buf, passphrase_len);
tpm_buf_append(buf, passphrase, passphrase_len);
}
void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
u8 attributes, u8 *passphrase,
int passphrase_len)
{
#ifdef CONFIG_TCG_TPM2_HMAC
u8 nonce[SHA256_DIGEST_SIZE];
struct tpm2_auth *auth;
u32 len;
#endif
if (!tpm2_chip_auth(chip)) {
tpm_buf_append_auth(chip, buf, passphrase, passphrase_len);
return;
}
#ifdef CONFIG_TCG_TPM2_HMAC
attributes |= TPM2_SA_CONTINUE_SESSION;
while (passphrase && passphrase_len > 0 && passphrase[passphrase_len - 1] == '\0')
passphrase_len--;
auth = chip->auth;
auth->attrs = attributes;
auth->passphrase_len = passphrase_len;
if (passphrase_len)
memcpy(auth->passphrase, passphrase, passphrase_len);
if (auth->session != tpm_buf_length(buf)) {
len = get_unaligned_be32(&buf->data[auth->session]);
if (4 + len + auth->session != tpm_buf_length(buf)) {
WARN(1, "session length mismatch, cannot append");
return;
}
len += 9 + 2 * SHA256_DIGEST_SIZE;
put_unaligned_be32(len, &buf->data[auth->session]);
} else {
tpm_buf_append_u32(buf, 9 + 2 * SHA256_DIGEST_SIZE);
}
get_random_bytes(nonce, sizeof(nonce));
memcpy(auth->our_nonce, nonce, sizeof(nonce));
tpm_buf_append_u32(buf, auth->handle);
tpm_buf_append_u16(buf, SHA256_DIGEST_SIZE);
tpm_buf_append(buf, nonce, SHA256_DIGEST_SIZE);
tpm_buf_append_u8(buf, auth->attrs);
tpm_buf_append_u16(buf, SHA256_DIGEST_SIZE);
tpm_buf_append(buf, nonce, SHA256_DIGEST_SIZE);
#endif
}
EXPORT_SYMBOL_GPL(tpm_buf_append_hmac_session);
#ifdef CONFIG_TCG_TPM2_HMAC
static int tpm2_create_primary(struct tpm_chip *chip, u32 hierarchy,
u32 *handle, u8 *name);
static void tpm2_KDFa(u8 *key, u32 key_len, const char *label, u8 *u,
u8 *v, u32 bytes, u8 *out)
{
u32 counter = 1;
const __be32 bits = cpu_to_be32(bytes * 8);
while (bytes > 0) {
struct hmac_sha256_ctx hctx;
__be32 c = cpu_to_be32(counter);
hmac_sha256_init_usingrawkey(&hctx, key, key_len);
hmac_sha256_update(&hctx, (u8 *)&c, sizeof(c));
hmac_sha256_update(&hctx, label, strlen(label) + 1);
hmac_sha256_update(&hctx, u, SHA256_DIGEST_SIZE);
hmac_sha256_update(&hctx, v, SHA256_DIGEST_SIZE);
hmac_sha256_update(&hctx, (u8 *)&bits, sizeof(bits));
hmac_sha256_final(&hctx, out);
bytes -= SHA256_DIGEST_SIZE;
counter++;
out += SHA256_DIGEST_SIZE;
}
}
static void tpm2_KDFe(u8 z[EC_PT_SZ], const char *str, u8 *pt_u, u8 *pt_v,
u8 *out)
{
struct sha256_ctx sctx;
__be32 c = cpu_to_be32(1);
sha256_init(&sctx);
sha256_update(&sctx, (u8 *)&c, sizeof(c));
sha256_update(&sctx, z, EC_PT_SZ);
sha256_update(&sctx, str, strlen(str)+1);
sha256_update(&sctx, pt_u, EC_PT_SZ);
sha256_update(&sctx, pt_v, EC_PT_SZ);
sha256_final(&sctx, out);
}
static void tpm_buf_append_salt(struct tpm_buf *buf, struct tpm_chip *chip,
struct tpm2_auth *auth)
{
struct crypto_kpp *kpp;
struct kpp_request *req;
struct scatterlist s[2], d[1];
struct ecdh p = {0};
u8 encoded_key[EC_PT_SZ], *x, *y;
unsigned int buf_len;
tpm_buf_append_u16(buf, (EC_PT_SZ + 2)*2);
tpm_buf_append_u16(buf, EC_PT_SZ);
x = &buf->data[tpm_buf_length(buf)];
tpm_buf_append(buf, encoded_key, EC_PT_SZ);
tpm_buf_append_u16(buf, EC_PT_SZ);
y = &buf->data[tpm_buf_length(buf)];
tpm_buf_append(buf, encoded_key, EC_PT_SZ);
sg_init_table(s, 2);
sg_set_buf(&s[0], x, EC_PT_SZ);
sg_set_buf(&s[1], y, EC_PT_SZ);
kpp = crypto_alloc_kpp("ecdh-nist-p256", CRYPTO_ALG_INTERNAL, 0);
if (IS_ERR(kpp)) {
dev_err(&chip->dev, "crypto ecdh allocation failed\n");
return;
}
buf_len = crypto_ecdh_key_len(&p);
if (sizeof(encoded_key) < buf_len) {
dev_err(&chip->dev, "salt buffer too small needs %d\n",
buf_len);
goto out;
}
crypto_ecdh_encode_key(encoded_key, buf_len, &p);
crypto_kpp_set_secret(kpp, encoded_key, buf_len);
req = kpp_request_alloc(kpp, GFP_KERNEL);
if (!req)
goto out;
kpp_request_set_input(req, NULL, 0);
kpp_request_set_output(req, s, EC_PT_SZ*2);
crypto_kpp_generate_public_key(req);
sg_set_buf(&s[0], chip->null_ec_key_x, EC_PT_SZ);
sg_set_buf(&s[1], chip->null_ec_key_y, EC_PT_SZ);
kpp_request_set_input(req, s, EC_PT_SZ*2);
sg_init_one(d, auth->salt, EC_PT_SZ);
kpp_request_set_output(req, d, EC_PT_SZ);
crypto_kpp_compute_shared_secret(req);
kpp_request_free(req);
tpm2_KDFe(auth->salt, "SECRET", x, chip->null_ec_key_x, auth->salt);
out:
crypto_free_kpp(kpp);
}
int tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf)
{
u32 cc, handles, val;
struct tpm2_auth *auth = chip->auth;
int i;
struct tpm_header *head = (struct tpm_header *)buf->data;
off_t offset_s = TPM_HEADER_SIZE, offset_p;
u8 *hmac = NULL;
u32 attrs;
u8 cphash[SHA256_DIGEST_SIZE];
struct sha256_ctx sctx;
struct hmac_sha256_ctx hctx;
int ret;
if (!auth) {
ret = -EIO;
goto err;
}
auth->ordinal = head->ordinal;
cc = be32_to_cpu(head->ordinal);
i = tpm2_find_cc(chip, cc);
if (i < 0) {
dev_err(&chip->dev, "command 0x%08x not found\n", cc);
ret = -EIO;
goto err;
}
attrs = chip->cc_attrs_tbl[i];
handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);
for (i = 0; i < handles; i++) {
u32 handle = tpm_buf_read_u32(buf, &offset_s);
if (auth->name_h[i] != handle) {
dev_err(&chip->dev, "invalid handle 0x%08x\n", handle);
ret = -EIO;
goto err;
}
}
val = tpm_buf_read_u32(buf, &offset_s);
offset_p = offset_s + val;
for (i = 1; offset_s < offset_p; i++) {
u32 handle = tpm_buf_read_u32(buf, &offset_s);
u16 len;
u8 a;
len = tpm_buf_read_u16(buf, &offset_s);
offset_s += len;
a = tpm_buf_read_u8(buf, &offset_s);
len = tpm_buf_read_u16(buf, &offset_s);
if (handle == auth->handle && auth->attrs == a) {
hmac = &buf->data[offset_s];
auth->session = i;
}
offset_s += len;
}
if (offset_s != offset_p) {
dev_err(&chip->dev, "session length is incorrect\n");
ret = -EIO;
goto err;
}
if (!hmac) {
dev_err(&chip->dev, "could not find HMAC session\n");
ret = -EIO;
goto err;
}
if (auth->attrs & TPM2_SA_DECRYPT) {
u16 len;
tpm2_KDFa(auth->session_key, SHA256_DIGEST_SIZE
+ auth->passphrase_len, "CFB", auth->our_nonce,
auth->tpm_nonce, AES_KEY_BYTES + AES_BLOCK_SIZE,
auth->scratch);
len = tpm_buf_read_u16(buf, &offset_p);
aes_prepareenckey(&auth->aes_key, auth->scratch, AES_KEY_BYTES);
aescfb_encrypt(&auth->aes_key, &buf->data[offset_p],
&buf->data[offset_p], len,
auth->scratch + AES_KEY_BYTES);
offset_p -= 2;
}
sha256_init(&sctx);
sha256_update(&sctx, (u8 *)&head->ordinal, sizeof(head->ordinal));
for (i = 0; i < handles; i++) {
enum tpm2_mso_type mso = tpm2_handle_mso(auth->name_h[i]);
if (mso == TPM2_MSO_PERSISTENT ||
mso == TPM2_MSO_VOLATILE ||
mso == TPM2_MSO_NVRAM) {
ret = name_size(auth->name[i]);
if (ret < 0)
goto err;
sha256_update(&sctx, auth->name[i], ret);
} else {
__be32 h = cpu_to_be32(auth->name_h[i]);
sha256_update(&sctx, (u8 *)&h, 4);
}
}
if (offset_s != tpm_buf_length(buf))
sha256_update(&sctx, &buf->data[offset_s],
tpm_buf_length(buf) - offset_s);
sha256_final(&sctx, cphash);
hmac_sha256_init_usingrawkey(&hctx, auth->session_key,
sizeof(auth->session_key) +
auth->passphrase_len);
hmac_sha256_update(&hctx, cphash, sizeof(cphash));
hmac_sha256_update(&hctx, auth->our_nonce, sizeof(auth->our_nonce));
hmac_sha256_update(&hctx, auth->tpm_nonce, sizeof(auth->tpm_nonce));
hmac_sha256_update(&hctx, &auth->attrs, 1);
hmac_sha256_final(&hctx, hmac);
return 0;
err:
tpm2_end_auth_session(chip);
return ret;
}
EXPORT_SYMBOL(tpm_buf_fill_hmac_session);
int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf,
int rc)
{
struct tpm_header *head = (struct tpm_header *)buf->data;
struct tpm2_auth *auth = chip->auth;
off_t offset_s, offset_p;
u8 rphash[SHA256_DIGEST_SIZE];
u32 attrs, cc;
struct sha256_ctx sctx;
struct hmac_sha256_ctx hctx;
u16 tag = be16_to_cpu(head->tag);
int parm_len, len, i, handles;
if (!auth)
return rc;
cc = be32_to_cpu(auth->ordinal);
if (auth->session >= TPM_HEADER_SIZE) {
WARN(1, "tpm session not filled correctly\n");
goto out;
}
if (rc != 0)
goto out;
rc = -EINVAL;
if (tag != TPM2_ST_SESSIONS) {
dev_err(&chip->dev, "TPM: HMAC response check has no sessions tag\n");
goto out;
}
i = tpm2_find_cc(chip, cc);
if (i < 0)
goto out;
attrs = chip->cc_attrs_tbl[i];
handles = (attrs >> TPM2_CC_ATTR_RHANDLE) & 1;
offset_s = TPM_HEADER_SIZE + handles * 4;
parm_len = tpm_buf_read_u32(buf, &offset_s);
offset_p = offset_s;
offset_s += parm_len;
for (i = 0; i < auth->session - 1; i++) {
len = tpm_buf_read_u16(buf, &offset_s);
offset_s += len + 1;
len = tpm_buf_read_u16(buf, &offset_s);
offset_s += len;
}
len = tpm_buf_read_u16(buf, &offset_s);
if (offset_s + len > tpm_buf_length(buf))
goto out;
if (len != SHA256_DIGEST_SIZE)
goto out;
memcpy(auth->tpm_nonce, &buf->data[offset_s], len);
offset_s += len;
attrs = tpm_buf_read_u8(buf, &offset_s);
len = tpm_buf_read_u16(buf, &offset_s);
if (offset_s + len != tpm_buf_length(buf))
goto out;
if (len != SHA256_DIGEST_SIZE)
goto out;
sha256_init(&sctx);
sha256_update(&sctx, (u8 *)&head->return_code,
sizeof(head->return_code));
sha256_update(&sctx, (u8 *)&auth->ordinal, sizeof(auth->ordinal));
sha256_update(&sctx, &buf->data[offset_p], parm_len);
sha256_final(&sctx, rphash);
hmac_sha256_init_usingrawkey(&hctx, auth->session_key,
sizeof(auth->session_key) +
auth->passphrase_len);
hmac_sha256_update(&hctx, rphash, sizeof(rphash));
hmac_sha256_update(&hctx, auth->tpm_nonce, sizeof(auth->tpm_nonce));
hmac_sha256_update(&hctx, auth->our_nonce, sizeof(auth->our_nonce));
hmac_sha256_update(&hctx, &auth->attrs, 1);
hmac_sha256_final(&hctx, rphash);
if (crypto_memneq(rphash, &buf->data[offset_s], SHA256_DIGEST_SIZE)) {
dev_err(&chip->dev, "TPM: HMAC check failed\n");
goto out;
}
rc = 0;
if (auth->attrs & TPM2_SA_ENCRYPT) {
tpm2_KDFa(auth->session_key, SHA256_DIGEST_SIZE
+ auth->passphrase_len, "CFB", auth->tpm_nonce,
auth->our_nonce, AES_KEY_BYTES + AES_BLOCK_SIZE,
auth->scratch);
len = tpm_buf_read_u16(buf, &offset_p);
aes_prepareenckey(&auth->aes_key, auth->scratch, AES_KEY_BYTES);
aescfb_decrypt(&auth->aes_key, &buf->data[offset_p],
&buf->data[offset_p], len,
auth->scratch + AES_KEY_BYTES);
}
out:
if ((auth->attrs & TPM2_SA_CONTINUE_SESSION) == 0) {
if (rc)
tpm2_flush_context(chip, auth->handle);
kfree_sensitive(auth);
chip->auth = NULL;
} else {
auth->session = TPM_HEADER_SIZE;
}
return rc;
}
EXPORT_SYMBOL(tpm_buf_check_hmac_response);
void tpm2_end_auth_session(struct tpm_chip *chip)
{
struct tpm2_auth *auth = chip->auth;
if (!auth)
return;
tpm2_flush_context(chip, auth->handle);
kfree_sensitive(auth);
chip->auth = NULL;
}
EXPORT_SYMBOL(tpm2_end_auth_session);
static int tpm2_parse_start_auth_session(struct tpm2_auth *auth,
struct tpm_buf *buf)
{
struct tpm_header *head = (struct tpm_header *)buf->data;
u32 tot_len = be32_to_cpu(head->length);
off_t offset = TPM_HEADER_SIZE;
u32 val;
tot_len -= TPM_HEADER_SIZE;
if (tot_len != 4 + 2 + sizeof(auth->tpm_nonce))
return -EINVAL;
auth->handle = tpm_buf_read_u32(buf, &offset);
val = tpm_buf_read_u16(buf, &offset);
if (val != sizeof(auth->tpm_nonce))
return -EINVAL;
memcpy(auth->tpm_nonce, &buf->data[offset], sizeof(auth->tpm_nonce));
tpm2_KDFa(auth->salt, sizeof(auth->salt), "ATH", auth->tpm_nonce,
auth->our_nonce, sizeof(auth->session_key),
auth->session_key);
return 0;
}
static int tpm2_load_null(struct tpm_chip *chip, u32 *null_key)
{
unsigned int offset = 0;
u8 name[SHA256_DIGEST_SIZE + 2];
u32 tmp_null_key;
int rc;
rc = tpm2_load_context(chip, chip->null_key_context, &offset,
&tmp_null_key);
if (rc != -EINVAL) {
if (!rc)
*null_key = tmp_null_key;
goto err;
}
rc = tpm2_create_primary(chip, TPM2_RH_NULL, &tmp_null_key, name);
if (rc)
goto err;
if (!memcmp(name, chip->null_key_name, sizeof(name))) {
*null_key = tmp_null_key;
return 0;
}
dev_err(&chip->dev, "null key integrity check failed\n");
tpm2_flush_context(chip, tmp_null_key);
err:
if (rc) {
chip->flags |= TPM_CHIP_FLAG_DISABLE;
rc = -ENODEV;
}
return rc;
}
int tpm2_start_auth_session(struct tpm_chip *chip)
{
struct tpm2_auth *auth;
struct tpm_buf buf;
u32 null_key;
int rc;
if (chip->auth) {
dev_dbg_once(&chip->dev, "auth session is active\n");
return 0;
}
auth = kzalloc_obj(*auth);
if (!auth)
return -ENOMEM;
rc = tpm2_load_null(chip, &null_key);
if (rc)
goto out;
auth->session = TPM_HEADER_SIZE;
rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_START_AUTH_SESS);
if (rc)
goto out;
tpm_buf_append_u32(&buf, null_key);
tpm_buf_append_u32(&buf, TPM2_RH_NULL);
get_random_bytes(auth->our_nonce, sizeof(auth->our_nonce));
tpm_buf_append_u16(&buf, sizeof(auth->our_nonce));
tpm_buf_append(&buf, auth->our_nonce, sizeof(auth->our_nonce));
tpm_buf_append_salt(&buf, chip, auth);
tpm_buf_append_u8(&buf, TPM2_SE_HMAC);
tpm_buf_append_u16(&buf, TPM_ALG_AES);
tpm_buf_append_u16(&buf, AES_KEY_BITS);
tpm_buf_append_u16(&buf, TPM_ALG_CFB);
tpm_buf_append_u16(&buf, TPM_ALG_SHA256);
rc = tpm_ret_to_err(tpm_transmit_cmd(chip, &buf, 0, "StartAuthSession"));
tpm2_flush_context(chip, null_key);
if (rc == TPM2_RC_SUCCESS)
rc = tpm2_parse_start_auth_session(auth, &buf);
tpm_buf_destroy(&buf);
if (rc == TPM2_RC_SUCCESS) {
chip->auth = auth;
return 0;
}
out:
kfree_sensitive(auth);
return rc;
}
EXPORT_SYMBOL(tpm2_start_auth_session);
#define TPM2_OA_NULL_KEY ( \
TPM2_OA_NO_DA | \
TPM2_OA_FIXED_TPM | \
TPM2_OA_FIXED_PARENT | \
TPM2_OA_SENSITIVE_DATA_ORIGIN | \
TPM2_OA_USER_WITH_AUTH | \
TPM2_OA_DECRYPT | \
TPM2_OA_RESTRICTED)
static int tpm2_parse_create_primary(struct tpm_chip *chip, struct tpm_buf *buf,
u32 *handle, u32 hierarchy, u8 *name)
{
struct tpm_header *head = (struct tpm_header *)buf->data;
off_t offset_r = TPM_HEADER_SIZE, offset_t;
u16 len = TPM_HEADER_SIZE;
u32 total_len = be32_to_cpu(head->length);
u32 val, param_len, keyhandle;
keyhandle = tpm_buf_read_u32(buf, &offset_r);
if (handle)
*handle = keyhandle;
else
tpm2_flush_context(chip, keyhandle);
param_len = tpm_buf_read_u32(buf, &offset_r);
param_len += TPM_HEADER_SIZE;
if (param_len + 8 > total_len)
return -EINVAL;
len = tpm_buf_read_u16(buf, &offset_r);
offset_t = offset_r;
if (name) {
put_unaligned_be16(TPM_ALG_SHA256, name);
sha256(&buf->data[offset_r], len, name + 2);
}
val = tpm_buf_read_u16(buf, &offset_t);
if (val != TPM_ALG_ECC)
return -EINVAL;
val = tpm_buf_read_u16(buf, &offset_t);
if (val != TPM_ALG_SHA256)
return -EINVAL;
val = tpm_buf_read_u32(buf, &offset_t);
if (val != TPM2_OA_NULL_KEY)
return -EINVAL;
val = tpm_buf_read_u16(buf, &offset_t);
if (val != 0)
return -EINVAL;
val = tpm_buf_read_u16(buf, &offset_t);
if (val != TPM_ALG_AES)
return -EINVAL;
val = tpm_buf_read_u16(buf, &offset_t);
if (val != AES_KEY_BITS)
return -EINVAL;
val = tpm_buf_read_u16(buf, &offset_t);
if (val != TPM_ALG_CFB)
return -EINVAL;
val = tpm_buf_read_u16(buf, &offset_t);
if (val != TPM_ALG_NULL)
return -EINVAL;
val = tpm_buf_read_u16(buf, &offset_t);
if (val != TPM2_ECC_NIST_P256)
return -EINVAL;
val = tpm_buf_read_u16(buf, &offset_t);
if (val != TPM_ALG_NULL)
return -EINVAL;
val = tpm_buf_read_u16(buf, &offset_t);
if (val != EC_PT_SZ)
return -EINVAL;
memcpy(chip->null_ec_key_x, &buf->data[offset_t], val);
offset_t += val;
val = tpm_buf_read_u16(buf, &offset_t);
if (val != EC_PT_SZ)
return -EINVAL;
memcpy(chip->null_ec_key_y, &buf->data[offset_t], val);
offset_t += val;
offset_r += len;
if (offset_t != offset_r)
return -EINVAL;
if (offset_r > param_len)
return -EINVAL;
len = tpm_buf_read_u16(buf, &offset_r);
offset_r += len;
if (offset_r > param_len)
return -EINVAL;
len = tpm_buf_read_u16(buf, &offset_r);
offset_r += len;
if (len != SHA256_DIGEST_SIZE || offset_r > param_len)
return -EINVAL;
val = tpm_buf_read_u16(buf, &offset_r);
if (val != TPM2_ST_CREATION || offset_r > param_len)
return -EINVAL;
val = tpm_buf_read_u32(buf, &offset_r);
if (val != hierarchy || offset_r > param_len)
return -EINVAL;
len = tpm_buf_read_u16(buf, &offset_r);
offset_r += len;
if (offset_r > param_len)
return -EINVAL;
len = tpm_buf_read_u16(buf, &offset_r);
if (offset_r + len != param_len + 8)
return -EINVAL;
if (len != SHA256_DIGEST_SIZE + 2)
return -EINVAL;
if (memcmp(chip->null_key_name, &buf->data[offset_r],
SHA256_DIGEST_SIZE + 2) != 0) {
dev_err(&chip->dev, "NULL Seed name comparison failed\n");
return -EINVAL;
}
return 0;
}
static int tpm2_create_primary(struct tpm_chip *chip, u32 hierarchy,
u32 *handle, u8 *name)
{
int rc;
struct tpm_buf buf;
struct tpm_buf template;
rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE_PRIMARY);
if (rc)
return rc;
rc = tpm_buf_init_sized(&template);
if (rc) {
tpm_buf_destroy(&buf);
return rc;
}
tpm_buf_append_u16(&template, TPM_ALG_ECC);
tpm_buf_append_u16(&template, TPM_ALG_SHA256);
tpm_buf_append_u32(&template, TPM2_OA_NULL_KEY);
tpm_buf_append_u16(&template, 0);
tpm_buf_append_u16(&template, TPM_ALG_AES);
tpm_buf_append_u16(&template, AES_KEY_BITS);
tpm_buf_append_u16(&template, TPM_ALG_CFB);
tpm_buf_append_u16(&template, TPM_ALG_NULL);
tpm_buf_append_u16(&template, TPM2_ECC_NIST_P256);
tpm_buf_append_u16(&template, TPM_ALG_NULL);
tpm_buf_append_u16(&template, 0);
tpm_buf_append_u16(&template, 0);
tpm_buf_append_u32(&buf, hierarchy);
tpm_buf_append_empty_auth(&buf, TPM2_RS_PW);
tpm_buf_append_u16(&buf, 4);
tpm_buf_append_u16(&buf, 0);
tpm_buf_append_u16(&buf, 0);
tpm_buf_append(&buf, template.data, template.length);
tpm_buf_destroy(&template);
tpm_buf_append_u16(&buf, 0);
tpm_buf_append_u32(&buf, 0);
rc = tpm_transmit_cmd(chip, &buf, 0,
"attempting to create NULL primary");
if (rc == TPM2_RC_SUCCESS)
rc = tpm2_parse_create_primary(chip, &buf, handle, hierarchy,
name);
tpm_buf_destroy(&buf);
return rc;
}
static int tpm2_create_null_primary(struct tpm_chip *chip)
{
u32 null_key;
int rc;
rc = tpm2_create_primary(chip, TPM2_RH_NULL, &null_key,
chip->null_key_name);
if (rc == TPM2_RC_SUCCESS) {
unsigned int offset = 0;
rc = tpm2_save_context(chip, null_key, chip->null_key_context,
sizeof(chip->null_key_context), &offset);
tpm2_flush_context(chip, null_key);
}
return rc;
}
int tpm2_sessions_init(struct tpm_chip *chip)
{
int rc;
rc = tpm2_create_null_primary(chip);
if (rc) {
dev_err(&chip->dev, "null key creation failed with %d\n", rc);
return rc;
}
return rc;
}
#endif