|
@@ -7,11 +7,105 @@
|
|
|
#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>
|
|
|
|
|
|
+#define TPM_ORD_FLUSHSPECIFIC 186
|
|
|
+#define TPM_ORD_LOADKEY2 65
|
|
|
+#define TPM_LOADKEY2_SIZE 59
|
|
|
+#define TPM_FLUSHSPECIFIC_SIZE 18
|
|
|
+
|
|
|
+#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);
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Maximum buffer size for the BER/DER encoded public key. The public key
|
|
|
* is of the form SEQUENCE { INTEGER n, INTEGER e } where n is a maximum 2048
|