// SPDX-License-Identifier: GPL-2.0
#define pr_fmt(fmt) "ASYM-TPM: "fmt
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/export.h>
#include <linux/kernel.h>
#include <linux/seq_file.h>
#include <linux/scatterlist.h>
#include <linux/tpm.h>
#include <linux/tpm_command.h>
#include <crypto/akcipher.h>
#include <crypto/hash.h>
#include <crypto/sha.h>
#include <asm/unaligned.h>
#include <keys/asymmetric-subtype.h>
#include <keys/trusted.h>
#include <crypto/asym_tpm_subtype.h>
#include <crypto/public_key.h>
#define TPM_ORD_FLUSHSPECIFIC 186
#define TPM_ORD_LOADKEY2 65
#define TPM_ORD_UNBIND 30
#define TPM_ORD_SIGN 60
#define TPM_LOADKEY2_SIZE 59
#define TPM_FLUSHSPECIFIC_SIZE 18
#define TPM_UNBIND_SIZE 63
#define TPM_SIGN_SIZE 63
#define TPM_RT_KEY 0x00000001
/*
* Load a TPM key from the blob provided by userspace
*/
static int tpm_loadkey2(struct tpm_buf *tb,
uint32_t keyhandle, unsigned char *keyauth,
const unsigned char *keyblob, int keybloblen,
uint32_t *newhandle)
{
unsigned char nonceodd[TPM_NONCE_SIZE];
unsigned char enonce[TPM_NONCE_SIZE];
unsigned char authdata[SHA1_DIGEST_SIZE];
uint32_t authhandle = 0;
unsigned char cont = 0;
uint32_t ordinal;
int ret;
ordinal = htonl(TPM_ORD_LOADKEY2);
/* session for loading the key */
ret = oiap(tb, &authhandle, enonce);
if (ret < 0) {
pr_info("oiap failed (%d)\n", ret);
return ret;
}
/* generate odd nonce */
ret = tpm_get_random(NULL, nonceodd, TPM_NONCE_SIZE);
if (ret < 0) {
pr_info("tpm_get_random failed (%d)\n", ret);
return ret;
}
/* calculate authorization HMAC value */
ret = TSS_authhmac(authdata, keyauth, SHA1_DIGEST_SIZE, enonce,
nonceodd, cont, sizeof(uint32_t), &ordinal,
keybloblen, keyblob, 0, 0);
if (ret < 0)
return ret;
/* build the request buffer */
INIT_BUF(tb);
store16(tb, TPM_TAG_RQU_AUTH1_COMMAND);
store32(tb, TPM_LOADKEY2_SIZE + keybloblen);
store32(tb, TPM_ORD_LOADKEY2);
store32(tb, keyhandle);
storebytes(tb, keyblob, keybloblen);
store32(tb, authhandle);
storebytes(tb, nonceodd, TPM_NONCE_SIZE);
store8(tb, cont);
storebytes(tb, authdata, SHA1_DIGEST_SIZE);
ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE);
if (ret < 0) {
pr_info("authhmac failed (%d)\n", ret);
return ret;
}
ret = TSS_checkhmac1(tb->data, ordinal, nonceodd, keyauth,
SHA1_DIGEST_SIZE, 0, 0);
if (ret < 0) {
pr_info("TSS_checkhmac1 failed (%d)\n", ret);
return ret;
}
*newhandle = LOAD32(tb->data, TPM_DATA_OFFSET);
return 0;
}
/*
* Execute the FlushSpecific TPM command
*/
static int tpm_flushspecific(struct tpm_buf *tb, uint32_t handle)
{
INIT_BUF(tb);
store16(tb, TPM_TAG_RQU_COMMAND);
store32(tb, TPM_FLUSHSPECIFIC_SIZE);
store32(tb, TPM_ORD_FLUSHSPECIFIC);
store32(tb, handle);
store32(tb, TPM_RT_KEY);
return trusted_tpm_send(tb->data, MAX_BUF_SIZE);
}
/*
* Decrypt a blob provided by userspace using a specific key handle.
* The handle is a well known handle or previously loaded by e.g. LoadKey2
*/
static int tpm_unbind(struct tpm_buf *tb,
uint32_t keyhandle, unsigned char *keyauth,
const unsigned char *blob, uint32_t bloblen,
void *out, uint32_t outlen)
{
unsigned char nonceodd[TPM_NONCE_SIZE];
unsigned char enonce[TPM_NONCE_SIZE];
unsigned char authdata[SHA1_DIGEST_SIZE];
uint32_t authhandle = 0;
unsigned char cont = 0;
uint32_t ordinal;
uint32_t datalen;
int ret;
ordinal = htonl(TPM_ORD_UNBIND);
datalen = htonl(bloblen);
/* session for loading the key */
ret = oiap(tb, &authhandle, enonce);
if (ret < 0) {
pr_info("oiap failed (%d)\n", ret);
return ret;
}
/* generate odd nonce */
ret = tpm_get_random(NULL, nonceodd, TPM_NONCE_SIZE);
if (ret < 0) {
pr_info("tpm_get_random failed (%d)\n", ret);
return ret;
}
/* cal