@@ -0,0 +1,795 @@
+/* run a set of tests of the sessions code */
+#include "tpm.h"
+#include "tpm2-sessions.h"
+
+#include <linux/random.h>
+
+#include <asm/unaligned.h>
+
+#include <crypto/aes.h>
+#include <crypto/hash.h>
+
+#include <keys/trusted-type.h>
+
+/* simple policy: command code must be TPM2_CC_UNSEAL */
+static u8 policy[] = {
+ 0xe6, 0x13, 0x13, 0x70, 0x76, 0x52, 0x4b, 0xde,
+ 0x48, 0x75, 0x33, 0x86, 0x58, 0x84, 0xe9, 0x73,
+ 0x2e, 0xbe, 0xe3, 0xaa, 0xcb, 0x09, 0x5d, 0x94,
+ 0xa6, 0xde, 0x49, 0x2e, 0xc0, 0x6c, 0x46, 0xfa,
+};
+
+static struct crypto_shash *sha256_hash;
+
+static int parse_create_primary(struct tpm_chip *chip, u8 *data, u32 *nullkey)
+{
+ struct tpm_header *head = (struct tpm_header *)data;
+ u16 len;
+ u32 tot_len = be32_to_cpu(head->length);
+ u32 val, parm_len;
+ const u8 *resp, *tmp;
+ SHASH_DESC_ON_STACK(desc, sha256_hash);
+
+ data += TPM_HEADER_SIZE;
+ /* we're starting after the header so adjust the length */
+ tot_len -= TPM_HEADER_SIZE;
+
+ resp = data;
+ *nullkey = tpm_get_inc_u32(&resp);
+ parm_len = tpm_get_inc_u32(&resp);
+ if (parm_len + 8 > tot_len)
+ return -EINVAL;
+ len = tpm_get_inc_u16(&resp);
+ tmp = resp;
+ /* now we have the public area, compute the name of the object */
+ desc->tfm = sha256_hash;
+ put_unaligned_be16(TPM_ALG_SHA256, chip->tpmkeyname);
+ crypto_shash_init(desc);
+ crypto_shash_update(desc, resp, len);
+ crypto_shash_final(desc, chip->tpmkeyname + 2);
+ /* validate the public key */
+ val = tpm_get_inc_u16(&tmp);
+ /* key type (must be what we asked for) */
+ if (val != TPM_ALG_ECC)
+ return -EINVAL;
+ val = tpm_get_inc_u16(&tmp);
+ /* name algorithm */
+ if (val != TPM_ALG_SHA256)
+ return -EINVAL;
+ val = tpm_get_inc_u32(&tmp);
+ /* object properties */
+ if (val != (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))
+ return -EINVAL;
+ /* auth policy (empty) */
+ val = tpm_get_inc_u16(&tmp);
+ if (val != 0)
+ return -EINVAL;
+ val = tpm_get_inc_u16(&tmp);
+ /* symmetric key parameters */
+ if (val != TPM_ALG_AES)
+ return -EINVAL;
+ val = tpm_get_inc_u16(&tmp);
+ /* symmetric key length */
+ if (val != AES_KEYSIZE_128 * 8)
+ return -EINVAL;
+ val = tpm_get_inc_u16(&tmp);
+ /* symmetric encryption scheme */
+ if (val != TPM_ALG_CFB)
+ return -EINVAL;
+ val = tpm_get_inc_u16(&tmp);
+ /* signing scheme */
+ if (val != TPM_ALG_NULL)
+ return -EINVAL;
+ val = tpm_get_inc_u16(&tmp);
+ /* ECC Curve */
+ if (val != TPM2_ECC_NIST_P256)
+ return -EINVAL;
+ val = tpm_get_inc_u16(&tmp);
+ /* KDF Scheme */
+ if (val != TPM_ALG_NULL)
+ return -EINVAL;
+ val = tpm_get_inc_u16(&tmp);
+ /* x point */
+ if (val != 32)
+ return -EINVAL;
+ memcpy(chip->ec_point_x, tmp, val);
+ tmp += val;
+ val = tpm_get_inc_u16(&tmp);
+ if (val != 32)
+ return -EINVAL;
+ memcpy(chip->ec_point_y, tmp, val);
+ tmp += val;
+ resp += len;
+ /* should have exactly consumed the tpm2b public structure */
+ if (tmp != resp)
+ return -EINVAL;
+ if (resp - data > parm_len)
+ return -EINVAL;
+ /* creation data (skip) */
+ len = tpm_get_inc_u16(&resp);
+ resp += len;
+ if (resp - data > parm_len)
+ return -EINVAL;
+ /* creation digest (must be sha256) */
+ len = tpm_get_inc_u16(&resp);
+ resp += len;
+ if (len != SHA256_DIGEST_SIZE || resp - data > parm_len)
+ return -EINVAL;
+ /* TPMT_TK_CREATION follows */
+ /* tag, must be TPM_ST_CREATION (0x8021) */
+ val = tpm_get_inc_u16(&resp);
+ if (val != TPM2_ST_CREATION || resp - data > parm_len)
+ return -EINVAL;
+ /* hierarchy (must be NULL) */
+ val = tpm_get_inc_u32(&resp);
+ if (val != TPM2_RH_NULL || resp - data > parm_len)
+ return -EINVAL;
+ /* the ticket digest HMAC (might not be sha256) */
+ len = tpm_get_inc_u16(&resp);
+ resp += len;
+ if (resp - data > parm_len)
+ return -EINVAL;
+ /*
+ * finally we have the name, which is a sha256 digest plus a 2
+ * byte algorithm type
+ */
+ len = tpm_get_inc_u16(&resp);
+ if (resp + len - data != parm_len + 8)
+ return -EINVAL;
+ if (len != SHA256_DIGEST_SIZE + 2)
+ return -EINVAL;
+
+ if (memcmp(chip->tpmkeyname, resp, SHA256_DIGEST_SIZE + 2) != 0) {
+ printk("TPM NULL Seed name comparison failed\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tpm2_create_primary(struct tpm_chip *chip, u32 hierarchy, u32 *handle)
+{
+ 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_2b(&template);
+ if (rc) {
+ tpm_buf_destroy(&buf);
+ return rc;
+ }
+
+ /*
+ * create the template. Note: in order for userspace to
+ * verify the security of the system, it will have to create
+ * and certify this NULL primary, meaning all the template
+ * parameters will have to be identical, so conform exactly to
+ * the TCG TPM v2.0 Provisioning Guidance for the SRK ECC
+ * key
+ */
+
+ /* key type */
+ tpm_buf_append_u16(&template, TPM_ALG_ECC);
+ /* name algorithm */
+ tpm_buf_append_u16(&template, TPM_ALG_SHA256);
+ /* object properties */
+ tpm_buf_append_u32(&template, 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);
+ /* sauth policy (empty) */
+ tpm_buf_append_u16(&template, 0);
+
+ /* BEGIN parameters: key specific; for ECC*/
+ /* symmetric algorithm */
+ tpm_buf_append_u16(&template, TPM_ALG_AES);
+ /* bits for symmetric algorithm */
+ tpm_buf_append_u16(&template, 128);
+ /* algorithm mode (must be CFB) */
+ tpm_buf_append_u16(&template, TPM_ALG_CFB);
+ /* scheme (NULL means any scheme) */
+ tpm_buf_append_u16(&template, TPM_ALG_NULL);
+ /* ECC Curve ID */
+ tpm_buf_append_u16(&template, TPM2_ECC_NIST_P256);
+ /* KDF Scheme */
+ tpm_buf_append_u16(&template, TPM_ALG_NULL);
+ /* unique: key specific; for ECC it is two points */
+ tpm_buf_append_u16(&template, 0);
+ tpm_buf_append_u16(&template, 0);
+ /* END parameters */
+
+ /* primary handle */
+ tpm_buf_append_u32(&buf, hierarchy);
+ /* simple authorization for empty auth */
+ tpm2_buf_append_auth(&buf, TPM2_RS_PW, NULL, 0, 0, NULL, 0);
+ /* sensitive create size is 4 for two empty buffers */
+ tpm_buf_append_u16(&buf, 4);
+ /* sensitive create auth data (empty) */
+ tpm_buf_append_u16(&buf, 0);
+ /* sensitive create sensitive data (empty) */
+ tpm_buf_append_u16(&buf, 0);
+ /* the public template */
+ tpm_buf_append_2b(&buf, &template);
+ tpm_buf_destroy(&template);
+ /* outside info (empty) */
+ tpm_buf_append_u16(&buf, 0);
+ /* creation PCR (none) */
+ tpm_buf_append_u32(&buf, 0);
+
+ rc = tpm_transmit_cmd(chip, &buf, 0,
+ "attempting to create NULL primary");
+
+ if (rc == TPM2_RC_SUCCESS)
+ rc = parse_create_primary(chip, buf.data, handle);
+
+ tpm_buf_destroy(&buf);
+
+ return rc;
+}
+
+static u32 get_policy(struct tpm_chip *chip)
+{
+ struct tpm_buf buf;
+ u8 nonce[SHA256_DIGEST_SIZE];
+ u32 h;
+ int rc;
+
+ rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_START_AUTH_SESS);
+ if (rc)
+ return 0;
+
+ /* salt key */
+ tpm_buf_append_u32(&buf, TPM2_RH_NULL);
+ /* bind key */
+ tpm_buf_append_u32(&buf, TPM2_RH_NULL);
+ /* zero nonce */
+ memset(nonce, 0, sizeof(nonce));
+ tpm_buf_append_u16(&buf, sizeof(nonce));
+ tpm_buf_append(&buf, nonce, sizeof(nonce));
+ /* encrypted salt (empty) */
+ tpm_buf_append_u16(&buf, 0);
+ /* session type (HMAC, audit or policy) */
+ tpm_buf_append_u8(&buf, TPM2_SE_POLICY);
+ /* symmetric encryption parameters */
+ /* symmetric algorithm */
+ tpm_buf_append_u16(&buf, TPM_ALG_NULL);
+
+ /* hash algorithm for session */
+ tpm_buf_append_u16(&buf, TPM_ALG_SHA256);
+
+ rc = tpm_transmit_cmd(chip, &buf, 0, "start policy session");
+
+ h = get_unaligned_be32(&buf.data[TPM_HEADER_SIZE]);
+
+ tpm_buf_reset(&buf, TPM2_ST_NO_SESSIONS,
+ TPM2_CC_POLICY_COMMAND_CODE);
+ tpm_buf_append_u32(&buf, h);
+ tpm_buf_append_u32(&buf, TPM2_CC_UNSEAL);
+
+ rc = tpm_transmit_cmd(chip, &buf, 0, "start policy session");
+
+ tpm_buf_destroy(&buf);
+
+ dev_info(&chip->dev, "got policy session %08x\n", h);
+
+ return h;
+}
+
+int tpm2_sessions_test(void)
+{
+ struct tpm2_auth *auth;
+ struct tpm_buf buf, b1;
+ struct tpm_buf t2b;
+ struct tpm_chip *chip;
+ int rc;
+ char payload[MIN_KEY_SIZE+3];
+ char *password = "Passw0Rd";
+ const u8 *p;
+ u32 h, ps;
+ u8 name[34];
+ u16 len;
+ int ret = -EINVAL;
+ u32 nullkey = 0;
+ struct trusted_key_payload tkp;
+ struct trusted_key_options tko;
+ struct tpm_digest digest, extend[3];
+ u16 digest_len;
+ int i;
+ SHASH_DESC_ON_STACK(desc, sha256_hash);
+
+ chip = tpm_find_get_ops(NULL);
+ if (!chip)
+ return -ENODEV;
+
+ if (!(chip->flags & TPM_CHIP_FLAG_TPM2))
+ return -ENODEV;
+
+ sha256_hash = crypto_alloc_shash("sha256", 0, 0);
+
+ /* precursor: get a session */
+ rc = tpm2_start_auth_session(chip, &auth);
+
+ dev_info(&chip->dev, "TPM: start auth session returned %d\n", rc);
+
+ if (rc)
+ return -ENODEV;
+
+ /* first test: get random bytes from TPM */
+ dev_info(&chip->dev, "test 1: TPM random number generator\n");
+ rc = tpm2_get_random(chip, payload, sizeof(payload));
+ if (rc != sizeof(payload)) {
+ dev_err(&chip->dev, "tpm2_get_random failed: %d\n", rc);
+ goto out;
+ }
+ dev_info(&chip->dev, "test 1: PASS\n");
+
+ /*
+ * second test, seal random data protecting sensitive by
+ * encryption and also doing response encryption (not
+ * necessary) The encrypted payload has two components: an
+ * authorization password which must be presented on useal and
+ * the actual data (the random payload)
+ */
+ dev_info(&chip->dev, "test2: seal data and check tpm unsealing functions\n");
+ rc = tpm2_create_primary(chip, TPM2_RH_NULL, &nullkey);
+
+ if (rc != TPM2_RC_SUCCESS) {
+ dev_err(&chip->dev, "tpm2_create_primary failed: %d\n", rc);
+ goto out;
+ }
+
+ dev_info(&chip->dev, "NULL KEY at %08x\n", nullkey);
+
+ tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
+ tpm_buf_append_name(&buf, auth, nullkey, chip->tpmkeyname);
+ /* close the session here, get a new one for next seal op */
+ tpm_buf_append_hmac_session(&buf, auth, TPM2_SA_DECRYPT
+ | TPM2_SA_ENCRYPT, NULL, 0);
+ /* sensitive */
+ tpm_buf_init_2b(&t2b);
+ /* the authorization */
+ tpm_buf_append_u16(&t2b, strlen(password));
+ tpm_buf_append(&t2b, password, strlen(password));
+ /* the payload */
+ tpm_buf_append_u16(&t2b, sizeof(payload) + 1);
+ tpm_buf_append(&t2b, payload, sizeof(payload));
+ /* the migrateable flag used by the TPM sealing functions */
+ tpm_buf_append_u8(&t2b, 0);
+ tpm_buf_append_2b(&buf, &t2b);
+ /* the public */
+ /* type */
+ tpm_buf_append_u16(&t2b, TPM_ALG_KEYEDHASH);
+ /* name hash */
+ tpm_buf_append_u16(&t2b, TPM_ALG_SHA256);
+ /* object properties */
+ tpm_buf_append_u32(&t2b, TPM2_OA_USER_WITH_AUTH | TPM2_OA_NO_DA);
+ /* auth policy (empty) */
+ tpm_buf_append_u16(&t2b, 0);
+ /* keyed hash parameters (we're null for a non-HMAC data blob) */
+ tpm_buf_append_u16(&t2b, TPM_ALG_NULL);
+ /* unique */
+ tpm_buf_append_u16(&t2b, 0);
+ tpm_buf_append_2b(&buf, &t2b);
+ /* outside info (also empty) */
+ tpm_buf_append_u16(&buf, 0);
+ /* creation PCR (empty) */
+ tpm_buf_append_u32(&buf, 0);
+ tpm_buf_fill_hmac_session(&buf, auth);
+ rc = tpm_transmit_cmd(chip, &buf, 4, "sealing data");
+ rc = tpm_buf_check_hmac_response(&buf, auth, rc);
+ dev_info(&chip->dev, "TPM: sealing response returned %d\n", rc);
+ if (rc)
+ goto out;
+
+ /*
+ * now load the sealed object (we need the pub and priv parts
+ * returned from prior command
+ */
+ memset(&tkp, 0, sizeof(tkp));
+ memset(&tko, 0, sizeof(tko));
+
+ p = &buf.data[TPM_HEADER_SIZE];
+
+ tkp.blob_len = tpm_get_inc_u32(&p);
+ memcpy(tkp.blob, p, tkp.blob_len);
+ tko.keyhandle = nullkey;
+ strcpy(tko.blobauth, password);
+
+ rc = tpm2_unseal_trusted(chip, &tkp, &tko);
+ if (rc) {
+ dev_err(&chip->dev, "tpm2_unseal_trusted[password] returned %d\n", rc);
+ goto out;
+ }
+
+ if (tkp.key_len != sizeof(payload)) {
+ dev_err(&chip->dev, "tpm2_unseal_trusted[password] wrong unseal payload size %d != %ld",
+ tkp.key_len, sizeof(payload));
+ goto out;
+ }
+ if (memcmp(payload, tkp.key, tkp.key_len) != 0) {
+ dev_err(&chip->dev, "tpm2_unseal_trusted[password] Payload DID NOT compare correctly\n");
+ goto out;
+ }
+ dev_info(&chip->dev, "tpm2_unseal_trusted[password] returned correct data\n"
+ "test2: PASS\n");
+
+ /*
+ * third test, seal random data protecting sensitive by
+ * encryption and also doing response encryption (not
+ * necessary) The encrypted payload has two components: an
+ * authorization password which must be presented on useal and
+ * the actual data (the random payload)
+ *
+ * For this test use a policy to release the data testing that
+ * we can have two sessions
+ */
+
+ dev_info(&chip->dev, "test 3: seal to policy and test unseal\n");
+ rc = tpm2_get_random(chip, payload, sizeof(payload));
+ if (rc != sizeof(payload)) {
+ dev_err(&chip->dev, "tpm2_get_random failed: %d\n", rc);
+ goto out;
+ }
+
+ /* auth session should be closed, so get a new one */
+ rc = tpm2_start_auth_session(chip, &auth);
+ if (rc) {
+ dev_err(&chip->dev, "new auth session failed: %d\n", rc);
+ goto out;
+ }
+
+ tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
+ tpm_buf_append_name(&buf, auth, nullkey, chip->tpmkeyname);
+ tpm_buf_append_hmac_session(&buf, auth, TPM2_SA_DECRYPT
+ | TPM2_SA_ENCRYPT
+ | TPM2_SA_CONTINUE_SESSION, NULL, 0);
+ /* sensitive */
+ tpm_buf_init_2b(&t2b);
+ /* the authorization */
+ tpm_buf_append_u16(&t2b, 0);
+ /* the payload */
+ tpm_buf_append_u16(&t2b, sizeof(payload) + 1);
+ tpm_buf_append(&t2b, payload, sizeof(payload));
+ /* extra byte for migratability flag */
+ tpm_buf_append_u8(&t2b, 0);
+ tpm_buf_append_2b(&buf, &t2b);
+ /* the public */
+ /* type */
+ tpm_buf_append_u16(&t2b, TPM_ALG_KEYEDHASH);
+ /* name hash */
+ tpm_buf_append_u16(&t2b, TPM_ALG_SHA256);
+ /* object properties */
+ tpm_buf_append_u32(&t2b, TPM2_OA_NO_DA);
+ /* auth policy (specific to command code) */
+ tpm_buf_append_u16(&t2b, sizeof(policy));
+ tpm_buf_append(&t2b, policy, sizeof(policy));
+ /* keyed hash parameters (we're null for a non-HMAC data blob) */
+ tpm_buf_append_u16(&t2b, TPM_ALG_NULL);
+ /* unique */
+ tpm_buf_append_u16(&t2b, 0);
+ tpm_buf_append_2b(&buf, &t2b);
+ /* outside info (also empty) */
+ tpm_buf_append_u16(&buf, 0);
+ /* creation PCR (empty) */
+ tpm_buf_append_u32(&buf, 0);
+ tpm_buf_fill_hmac_session(&buf, auth);
+ rc = tpm_transmit_cmd(chip, &buf, 4, "policy sealing data");
+ rc = tpm_buf_check_hmac_response(&buf, auth, rc);
+ dev_info(&chip->dev, "policy sealing response returned %d\n", rc);
+ if (rc)
+ goto out;
+
+ /*
+ * now load the sealed object (we need the pub and priv parts
+ * returned from prior command
+ */
+ memset(&tkp, 0, sizeof(tkp));
+ memset(&tko, 0, sizeof(tko));
+
+ p = &buf.data[TPM_HEADER_SIZE];
+
+ /* now get a policy session */
+ ps = get_policy(chip);
+
+ tkp.blob_len = tpm_get_inc_u32(&p);
+ memcpy(tkp.blob, p, tkp.blob_len);
+ tko.keyhandle = nullkey;
+ tko.policyhandle = ps;
+
+ rc = tpm2_unseal_trusted(chip, &tkp, &tko);
+ if (rc) {
+ dev_err(&chip->dev, "tpm2_unseal_trusted[policy] returned %d\n", rc);
+ tpm2_flush_context(chip, ps);
+ goto out;
+ }
+
+ if (tkp.key_len != sizeof(payload)) {
+ dev_err(&chip->dev, "tpm2_unseal_trusted[policy] wrong unseal payload size %d != %ld",
+ tkp.key_len, sizeof(payload));
+ goto out;
+ }
+ if (memcmp(payload, tkp.key, tkp.key_len) != 0) {
+ dev_err(&chip->dev, "tpm2_unseal_trusted[policy] Payload DID NOT compare correctly\n");
+ goto out;
+ }
+ dev_info(&chip->dev, "tpm2_unseal_trusted[policy] returned correct data\n"
+ "test3: PASS\n");
+
+ /*
+ * test4: get the TPM routines to seal to a password and then
+ * do a manual unseal
+ */
+
+ dev_info(&chip->dev, "test4: seal using tpm2-cmd[password] and unseal manually\n");
+
+ memset(&tkp, 0, sizeof(tkp));
+ memset(&tko, 0, sizeof(tko));
+
+ tko.keyhandle = nullkey;
+ tko.hash = HASH_ALGO_SHA256;
+ tkp.key_len = sizeof(payload);
+ memcpy(tkp.key, payload, tkp.key_len);
+ strcpy(tko.blobauth, password);
+
+ rc = tpm2_seal_trusted(chip, &tkp, &tko);
+ if (rc) {
+ dev_err(&chip->dev, "tpm2_seal_trusted returned error: %d\n ", rc);
+ goto out;
+ }
+
+ /*
+ * now load the sealed object (we need the pub and priv parts
+ * returned from prior command. The blob from
+ * tpm2_seal_trusted is the entire return from the create, so
+ * only append the private and public parts
+ */
+ p = tkp.blob;
+ /* private len */
+ len = tpm_get_inc_u16(&p);
+ p += len;
+ /* add public len */
+ len += tpm_get_inc_u16(&p);
+ /* add 4 for the two length values */
+ len += 4;
+
+ tpm_buf_init(&b1, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
+ /* parent */
+ tpm_buf_append_name(&b1, auth, nullkey, chip->tpmkeyname);
+ tpm_buf_append_hmac_session(&b1, auth, TPM2_SA_CONTINUE_SESSION,
+ NULL, 0);
+
+ tpm_buf_append(&b1, tkp.blob, len);
+
+ tpm_buf_fill_hmac_session(&b1, auth);
+ rc = tpm_transmit_cmd(chip, &b1, 4, "loading seal");
+ rc = tpm_buf_check_hmac_response(&b1, auth, rc);
+ dev_info(&chip->dev, "TPM: load response returned %d\n", rc);
+ if (rc)
+ goto out;
+ p = &b1.data[TPM_HEADER_SIZE];
+ h = tpm_get_inc_u32(&p);
+ dev_info(&chip->dev, "sealed data loaded at %08x\n", h);
+ /* skip over parameter size */
+ p += 4;
+ len = tpm_get_inc_u16(&p);
+ if (len != sizeof(name)) {
+ dev_err(&chip->dev, "Wrong name size %d\n", len);
+ goto out;
+ }
+ memcpy(name, p, len);
+ tpm_buf_destroy(&b1);
+ tpm_buf_destroy(&buf);
+
+ /*
+ * now unseal the data using the authority in a HMAC and
+ * protecting the returned unseal by encryption
+ */
+ tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
+ tpm_buf_append_name(&buf, auth, h, name);
+ tpm_buf_append_hmac_session(&buf, auth,
+ TPM2_SA_ENCRYPT | TPM2_SA_CONTINUE_SESSION,
+ password, strlen(password));
+ tpm_buf_fill_hmac_session(&buf, auth);
+ rc = tpm_transmit_cmd(chip, &buf, 4, "unseal");
+ rc = tpm_buf_check_hmac_response(&buf, auth, rc);
+ tpm2_flush_context(chip, h);
+ dev_info(&chip->dev, "unseal returns %d\n", rc);
+ if (rc)
+ goto out;
+ p = &buf.data[TPM_HEADER_SIZE + 4];
+ len = tpm_get_inc_u16(&p);
+ if (len != sizeof(payload) + 1) {
+ dev_err(&chip->dev, "wrong unseal payload size %d != %ld",
+ len, sizeof(payload) + 1);
+ goto out;
+ }
+ if (memcmp(payload, p, sizeof(payload)) != 0) {
+ dev_err(&chip->dev, "Payload DID NOT compare correctly\n");
+ goto out;
+ }
+
+ dev_info(&chip->dev, "test4: PASS\n");
+
+ /*
+ * test5: get the TPM routines to seal to a policy and then
+ * do a manual unseal
+ */
+
+ dev_info(&chip->dev, "test5: seal using tpm2-cmd[policy] and unseal manually\n");
+
+ memset(&tkp, 0, sizeof(tkp));
+ memset(&tko, 0, sizeof(tko));
+
+ tko.keyhandle = nullkey;
+ tko.hash = HASH_ALGO_SHA256;
+ tkp.key_len = sizeof(payload);
+ memcpy(tkp.key, payload, tkp.key_len);
+ tko.policydigest_len = sizeof(policy);
+ memcpy(tko.policydigest, policy, tko.policydigest_len);
+
+ rc = tpm2_seal_trusted(chip, &tkp, &tko);
+ if (rc) {
+ dev_err(&chip->dev, "tpm2_seal_trusted returned error: %d\n ", rc);
+ goto out;
+ }
+
+ ps = get_policy(chip);
+ /*
+ * now load the sealed object (we need the pub and priv parts
+ * returned from prior command. The blob from
+ * tpm2_seal_trusted is the entire return from the create, so
+ * only append the private and public parts
+ */
+ p = tkp.blob;
+ /* private len */
+ len = tpm_get_inc_u16(&p);
+ p += len;
+ /* add public len */
+ len += tpm_get_inc_u16(&p);
+ /* add 4 for the two length values */
+ len += 4;
+
+ tpm_buf_init(&b1, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
+ /* parent */
+ tpm_buf_append_name(&b1, auth, nullkey, chip->tpmkeyname);
+ tpm_buf_append_hmac_session(&b1, auth, TPM2_SA_CONTINUE_SESSION,
+ NULL, 0);
+
+ tpm_buf_append(&b1, tkp.blob, len);
+
+ tpm_buf_fill_hmac_session(&b1, auth);
+ rc = tpm_transmit_cmd(chip, &b1, 4, "loading seal");
+ rc = tpm_buf_check_hmac_response(&b1, auth, rc);
+ dev_info(&chip->dev, "TPM: load response returned %d\n", rc);
+ if (rc) {
+ tpm2_flush_context(chip, ps);
+ goto out;
+ }
+ p = &b1.data[TPM_HEADER_SIZE];
+ h = tpm_get_inc_u32(&p);
+ dev_info(&chip->dev, "sealed data loaded at %08x\n", h);
+ /* skip over parameter size */
+ p += 4;
+ len = tpm_get_inc_u16(&p);
+ if (len != sizeof(name)) {
+ dev_err(&chip->dev, "Wrong name size %d\n", len);
+ goto out;
+ }
+ memcpy(name, p, len);
+ tpm_buf_destroy(&b1);
+ tpm_buf_destroy(&buf);
+
+ /*
+ * now unseal the data using the authority in a HMAC and
+ * protecting the returned unseal by encryption
+ */
+ tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
+ tpm_buf_append_name(&buf, auth, h, name);
+ tpm2_buf_append_auth(&buf, ps, NULL, 0, 0, NULL, 0);
+ tpm_buf_append_hmac_session(&buf, auth, TPM2_SA_ENCRYPT,
+ NULL, 0);
+ tpm_buf_fill_hmac_session(&buf, auth);
+ rc = tpm_transmit_cmd(chip, &buf, 4, "unseal");
+ rc = tpm_buf_check_hmac_response(&buf, auth, rc);
+ tpm2_flush_context(chip, h);
+ dev_info(&chip->dev, "unseal returns %d\n", rc);
+ if (rc)
+ goto out;
+ p = &buf.data[TPM_HEADER_SIZE + 4];
+ len = tpm_get_inc_u16(&p);
+ if (len != sizeof(payload) + 1) {
+ dev_err(&chip->dev, "wrong unseal payload size %d != %ld",
+ len, sizeof(payload) + 1);
+ goto out;
+ }
+ if (memcmp(payload, p, sizeof(payload)) != 0) {
+ dev_err(&chip->dev, "Payload DID NOT compare correctly\n");
+ goto out;
+ }
+
+ dev_info(&chip->dev, "test5: PASS");
+
+ /*
+ * test6: extend PCR 16 with a random value and verify it
+ * comes back correctly
+ */
+
+ dev_info(&chip->dev, "test6: extend PCR 16");
+
+ digest.alg_id = TPM_ALG_SHA256;
+
+ rc = tpm2_pcr_read(chip, 16, &digest, &digest_len);
+ if (rc) {
+ dev_err(&chip->dev, "tpm2_pcr_read returns error: %d\n", rc);
+ goto out;
+ }
+ if (digest_len != SHA256_DIGEST_SIZE) {
+ dev_err(&chip->dev, "wrong digest size %d!=%d\n", digest_len,
+ SHA256_DIGEST_SIZE);
+ goto out;
+ }
+
+ for (i = 0; i < chip->nr_allocated_banks; i++) {
+ rc = tpm2_get_random(chip, extend[i].digest, SHA256_DIGEST_SIZE);
+ if (rc != SHA256_DIGEST_SIZE) {
+ dev_err(&chip->dev, "tpm2_get_random failed: %d\n", rc);
+ goto out;
+ }
+ extend[i].alg_id = chip->allocated_banks[i].alg_id;
+ }
+ rc = tpm2_pcr_extend(chip, 16, extend);
+ if (rc) {
+ dev_err(&chip->dev, "tpm2_pcr_extend failed: %d\n", rc);
+ goto out;
+ }
+
+ desc->tfm = sha256_hash;
+ crypto_shash_init(desc);
+ crypto_shash_update(desc, digest.digest, SHA256_DIGEST_SIZE);
+ crypto_shash_update(desc, extend[0].digest, SHA256_DIGEST_SIZE);
+ crypto_shash_final(desc, digest.digest);
+
+ extend[0].alg_id = TPM_ALG_SHA256;
+ rc = tpm2_pcr_read(chip, 16, &extend[0], &digest_len);
+ if (rc) {
+ dev_err(&chip->dev, "tpm2_pcr_read returns error: %d\n", rc);
+ goto out;
+ }
+ if (digest_len != SHA256_DIGEST_SIZE) {
+ dev_err(&chip->dev, "wrong digest size %d!=%d\n", digest_len,
+ SHA256_DIGEST_SIZE);
+ goto out;
+ }
+
+ if (memcmp(digest.digest, extend[0].digest, SHA256_DIGEST_SIZE) != 0) {
+ dev_err(&chip->dev, "Extend digest comparison failed\n");
+ goto out;
+ }
+
+ dev_info(&chip->dev, "All tests passed\n");
+ ret = 0;
+
+ out:
+ tpm2_flush_context(chip, nullkey);
+
+ tpm_put_ops(chip);
+
+ return ret;
+}
+
+module_init(tpm2_sessions_test);
+
+MODULE_LICENSE("GPL");