// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2010 IBM Corporation
* Copyright (c) 2019-2021, Linaro Limited
*
* See Documentation/security/keys/trusted-encrypted.rst
*/
#include <crypto/hash_info.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/parser.h>
#include <linux/string.h>
#include <linux/err.h>
#include <keys/trusted-type.h>
#include <linux/key-type.h>
#include <linux/crypto.h>
#include <crypto/hash.h>
#include <crypto/sha1.h>
#include <linux/tpm.h>
#include <linux/tpm_command.h>
#include <keys/trusted_tpm.h>
static const char hmac_alg[] = "hmac(sha1)";
static const char hash_alg[] = "sha1";
static struct tpm_chip *chip;
static struct tpm_digest *digests;
struct sdesc {
struct shash_desc shash;
char ctx[];
};
static struct crypto_shash *hashalg;
static struct crypto_shash *hmacalg;
static struct sdesc *init_sdesc(struct crypto_shash *alg)
{
struct 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;
return sdesc;
}
static int TSS_sha1(const unsigned char *data, unsigned int datalen,
unsigned char *digest)
{
struct sdesc *sdesc;
int ret;
sdesc = init_sdesc(hashalg);
if (IS_ERR(sdesc)) {
pr_info("can't alloc %s\n", hash_alg);
return PTR_ERR(sdesc);
}
ret = crypto_shash_digest(&sdesc->shash, data, datalen, digest);
kfree_sensitive(sdesc);
return ret;
}
static int TSS_rawhmac(unsigned char *digest, const unsigned char *key,
unsigned int keylen, ...)
{
struct sdesc *sdesc;
va_list argp;
unsigned int dlen;
unsigned char *data;
int ret;
sdesc = init_sdesc(hmacalg);
if (IS_ERR(sdesc)) {
pr_info("can't alloc %s\n", hmac_alg);
return PTR_ERR(sdesc);
}
ret = crypto_shash_setkey(hmacalg, key, keylen);
if (ret < 0)
goto out;
ret = crypto_shash_init(&sdesc->shash);
if (ret < 0)
goto out;
va_start(argp, keylen);
for (;;) {
dlen = va_arg(argp, unsigned int);
if (dlen == 0)
break;
data = va_arg(argp, unsigned char *);
if (data == NULL) {
ret = -EINVAL;
break;
}
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_sensitive(sdesc);
return ret;
}
/*
* calculate authorization info fields to send to TPM
*/
int TSS_authhmac(unsigned char *digest, const unsigned char *key,
unsigned int keylen, unsigned char *h1,
unsigned char *h2, unsigned int h3, ...)
{
unsigned char paramdigest[SHA1_DIGEST_SIZE];
struct sdesc *sdesc;
unsigned int dlen;
unsigned char *data;
unsigned char c;
int ret;
va_list argp;
if (!chip)
return -ENODEV;
sdesc = init_sdesc(hashalg);
if (IS_ERR(sdesc)) {
pr_info("can't alloc %s\n", hash_alg);
return PTR_ERR(sdesc);
}
c = !!h3;
ret = crypto_shash_init(&sdesc->shash);
if (ret < 0)
goto out;
va_start(argp, h3);
for (;;) {
dlen = va_arg(argp, unsigned int);
if (dlen == 0)
break;
data = va_arg(argp, unsigned char *);
if (!data) {
ret = -EINVAL;
break;
}
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, SHA1_DIGEST_SIZE,
paramdigest, TPM_NONCE_SIZE, h1,
TPM_NONCE_SIZE, h2, 1,