+ {
+ if (ctx->args & GRUB_TPM2_PROTECTOR_ARG_ASYMMETRIC)
+ return GRUB_ERR_BAD_ARGUMENT;
+
+ err = grub_tpm2_protector_parse_asymmetric (ctx, value);
+ if (!err)
+ ctx->args |= GRUB_TPM2_PROTECTOR_ARG_ASYMMETRIC;
+
+ return err;
+ }
+
+ if (grub_strcmp (key, "bank") == 0)
+ {
+ if (ctx->args & GRUB_TPM2_PROTECTOR_ARG_BANK)
+ return GRUB_ERR_BAD_ARGUMENT;
+
+ err = grub_tpm2_protector_parse_bank (ctx, value);
+ if (!err)
+ ctx->args |= GRUB_TPM2_PROTECTOR_ARG_BANK;
+
+ return err;
+ }
+
+ if (grub_strcmp (key, "keyfile") == 0)
+ {
+ if (ctx->args & GRUB_TPM2_PROTECTOR_ARG_KEYFILE)
+ return GRUB_ERR_BAD_ARGUMENT;
+
+ err = grub_tpm2_protector_parse_keyfile (ctx, value);
+ if (!err)
+ ctx->args |= GRUB_TPM2_PROTECTOR_ARG_KEYFILE;
+
+ return err;
+ }
+
+ if (grub_strcmp (key, "srk") == 0)
+ {
+ if (ctx->args & GRUB_TPM2_PROTECTOR_ARG_SRK)
+ return GRUB_ERR_BAD_ARGUMENT;
+
+ err = grub_tpm2_protector_parse_srk (ctx, value);
+ if (!err)
+ ctx->args |= GRUB_TPM2_PROTECTOR_ARG_SRK;
+
+ return err;
+ }
+
+ if (grub_strcmp (key, "nvindex") == 0)
+ {
+ if (ctx->args & GRUB_TPM2_PROTECTOR_ARG_NV)
+ return GRUB_ERR_BAD_ARGUMENT;
+
+ err = grub_tpm2_protector_parse_nvindex (ctx, value);
+ if (!err)
+ ctx->args |= GRUB_TPM2_PROTECTOR_ARG_NV;
+
+ return err;
+ }
+
+ return GRUB_ERR_OUT_OF_RANGE;
+}
+
+static grub_err_t
+grub_tpm2_protector_parse_arg (struct grub_tpm2_protector_context *ctx,
+ char *arg)
+{
+ char *key = arg;
+ char *value = NULL;
+
+ if (grub_strlen (arg) == 0)
+ return GRUB_ERR_BAD_ARGUMENT;
+
+ value = grub_strchr (arg, '=');
+ if (!value)
+ return GRUB_ERR_BAD_ARGUMENT;
+
+ *value = '\0';
+ value++;
+
+ if (*value == '\0')
+ return GRUB_ERR_BAD_ARGUMENT;
+
+ return grub_tpm2_protector_match_arg (ctx, key, value);
+}
+
+static grub_err_t
+grub_tpm2_protector_parse_args (struct grub_tpm2_protector_context *ctx,
+ char *args)
+{
+ grub_err_t err;
+ char *cur_arg;
+ char *next_arg;
+
+ if (args == NULL)
+ return GRUB_ERR_BAD_ARGUMENT;
+
+ if (grub_strlen (args) == 0)
+ return GRUB_ERR_BAD_ARGUMENT;
+
+ cur_arg = args;
+ for (;;)
+ {
+ next_arg = grub_strchr (cur_arg, ':');
+ if (next_arg == cur_arg)
+ return GRUB_ERR_BAD_ARGUMENT;
+ if (next_arg)
+ *next_arg = '\0';
+
+ err = grub_tpm2_protector_parse_arg (ctx, cur_arg);
+ if (err)
+ return err;
+
+ if (!next_arg)
+ break;
+
+ cur_arg = next_arg + 1;
+ if (*cur_arg == '\0')
+ return GRUB_ERR_BAD_ARGUMENT;
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_tpm2_protector_check_args (struct grub_tpm2_protector_context *ctx)
+{
+ if (!(ctx->args & GRUB_TPM2_PROTECTOR_ARG_MODE))
+ ctx->mode = GRUB_TPM2_PROTECTOR_MODE_SRK;
+
+ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_SRK && !ctx->keyfile)
+ return GRUB_ERR_BAD_ARGUMENT;
+
+ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_SRK && ctx->nv)
+ return GRUB_ERR_BAD_ARGUMENT;
+
+ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV && ctx->srk)
+ return GRUB_ERR_BAD_ARGUMENT;
+
+ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV && !ctx->nv)
+ return GRUB_ERR_BAD_ARGUMENT;
+
+ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV && ctx->asymmetric)
+ return GRUB_ERR_BAD_ARGUMENT;
+
+ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV && ctx->bank)
+ return GRUB_ERR_BAD_ARGUMENT;
+
+ if (!(ctx->args & GRUB_TPM2_PROTECTOR_ARG_PCRS))
+ {
+ ctx->pcrs[0] = 7;
+ ctx->pcr_count = 1;
+ }
+
+ if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_SRK)
+ {
+ if (!ctx->srk)
+ ctx->srk = TPM2_SRK_HANDLE;
+
+ if (!ctx->asymmetric)
+ ctx->asymmetric = TPM_ALG_RSA;
+
+ if (!ctx->bank)
+ ctx->bank = TPM_ALG_SHA256;
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_tpm2_protector_srk_read_keyfile (const char *filepath, void **buffer,
+ grub_size_t *buffer_size)
+{
+ grub_file_t sealed_key_file;
+ grub_off_t sealed_key_size;
+ void *sealed_key_buffer;
+ grub_off_t sealed_key_read;
+
+ sealed_key_file = grub_file_open (filepath, GRUB_FILE_TYPE_NONE);
+ if (!sealed_key_file)
+ {
+ /* grub_file_open sets grub_errno on error, and if we do no unset it,
+ * future calls to grub_file_open will fail (and so will anybody up the
+ * stack who checks the value, if any). */
+ grub_errno = GRUB_ERR_NONE;
+ return GRUB_ERR_FILE_NOT_FOUND;
+ }
+
+ sealed_key_size = grub_file_size (sealed_key_file);
+ if (!sealed_key_size)
+ {
+ grub_file_close (sealed_key_file);
+ return GRUB_ERR_OUT_OF_RANGE;
+ }
+
+ sealed_key_buffer = grub_malloc (sealed_key_size);
+ if (!sealed_key_buffer)
+ {
+ grub_file_close (sealed_key_file);
+ return GRUB_ERR_OUT_OF_MEMORY;
+ }
+
+ sealed_key_read = grub_file_read (sealed_key_file, sealed_key_buffer,
+ sealed_key_size);
+ if (sealed_key_read != sealed_key_size)
+ {
+ grub_free (sealed_key_buffer);
+ grub_file_close (sealed_key_file);
+ return GRUB_ERR_FILE_READ_ERROR;
+ }
+
+ grub_file_close (sealed_key_file);
+
+ *buffer = sealed_key_buffer;
+ *buffer_size = sealed_key_size;
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_tpm2_protector_srk_unmarshal_keyfile (void *sealed_key,
+ grub_size_t sealed_key_size,
+ TPM2_SEALED_KEY *sk)
+{
+ struct grub_tpm2_buffer buf;
+
+ grub_tpm2_buffer_init (&buf);
+ if (sealed_key_size > buf.cap)
+ return GRUB_ERR_BAD_ARGUMENT;
+
+ grub_memcpy (buf.data, sealed_key, sealed_key_size);
+ buf.size = sealed_key_size;
+
+ grub_tpm2_mu_TPM2B_PUBLIC_Unmarshal (&buf, &sk->public);
+ grub_tpm2_mu_TPM2B_Unmarshal (&buf, (TPM2B *)&sk->private);
+
+ return buf.error ? GRUB_ERR_BAD_ARGUMENT : GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_tpm2_protector_srk_get (const struct grub_tpm2_protector_context *ctx,
+ TPM_HANDLE *srk)
+{
+ TPM_RC rc;
+ TPM2B_PUBLIC public;
+ TPMS_AUTH_COMMAND authCommand = { 0 };
+ TPM2B_SENSITIVE_CREATE inSensitive = { 0 };
+ TPM2B_PUBLIC inPublic = { 0 };
+ TPM2B_DATA outsideInfo = { 0 };
+ TPML_PCR_SELECTION creationPcr = { 0 };
+ TPM2B_PUBLIC outPublic = { 0 };
+ TPM2B_CREATION_DATA creationData = { 0 };
+ TPM2B_DIGEST creationHash = { 0 };
+ TPMT_TK_CREATION creationTicket = { 0 };
+ TPM2B_NAME srkName = { 0 };
+ TPM_HANDLE srkHandle;
+
+ /* Find SRK */
+ rc = TPM2_ReadPublic (ctx->srk, NULL, &public);
+ if (rc == TPM_RC_SUCCESS)
+ {
+ *srk = ctx->srk;
+ return GRUB_ERR_NONE;
+ }
+
+ /* The handle exists but its public area could not be read. */
+ if ((rc & ~TPM_RC_N_MASK) != TPM_RC_HANDLE)
+ return GRUB_ERR_BAD_DEVICE;
+
+ /* Create SRK */
+ authCommand.sessionHandle = TPM_RS_PW;
+ inPublic.publicArea.type = ctx->asymmetric;
+ inPublic.publicArea.nameAlg = TPM_ALG_SHA256;
+ inPublic.publicArea.objectAttributes.restricted = 1;
+ inPublic.publicArea.objectAttributes.userWithAuth = 1;
+ inPublic.publicArea.objectAttributes.decrypt = 1;
+ inPublic.publicArea.objectAttributes.fixedTPM = 1;
+ inPublic.publicArea.objectAttributes.fixedParent = 1;
+ inPublic.publicArea.objectAttributes.sensitiveDataOrigin = 1;
+ inPublic.publicArea.objectAttributes.noDA = 1;
+
+ if (ctx->asymmetric == TPM_ALG_RSA)
+ {
+ inPublic.publicArea.parameters.rsaDetail.symmetric.algorithm =
TPM_ALG_AES;
+ inPublic.publicArea.parameters.rsaDetail.symmetric.keyBits.aes = 128;
+ inPublic.publicArea.parameters.rsaDetail.symmetric.mode.aes =
TPM_ALG_CFB;
+ inPublic.publicArea.parameters.rsaDetail.scheme.scheme = TPM_ALG_NULL;
+ inPublic.publicArea.parameters.rsaDetail.keyBits = 2048;
+ inPublic.publicArea.parameters.rsaDetail.exponent = 0;
+ }
+ else if (ctx->asymmetric == TPM_ALG_ECC)
+ {
+ inPublic.publicArea.parameters.eccDetail.symmetric.algorithm =
TPM_ALG_AES;
+ inPublic.publicArea.parameters.eccDetail.symmetric.keyBits.aes = 128;
+ inPublic.publicArea.parameters.eccDetail.symmetric.mode.aes =
TPM_ALG_CFB;
+ inPublic.publicArea.parameters.eccDetail.scheme.scheme = TPM_ALG_NULL;
+ inPublic.publicArea.parameters.eccDetail.curveID = TPM_ECC_NIST_P256;
+ inPublic.publicArea.parameters.eccDetail.kdf.scheme = TPM_ALG_NULL;
+ }
+ else
+ return GRUB_ERR_BAD_ARGUMENT;
+
+ rc = TPM2_CreatePrimary (TPM_RH_OWNER, &authCommand, &inSensitive, &inPublic,
+ &outsideInfo, &creationPcr, &srkHandle, &outPublic,
+ &creationData, &creationHash, &creationTicket,
+ &srkName, NULL);
+ if (rc != TPM_RC_SUCCESS)
+ return GRUB_ERR_BAD_DEVICE;
+
+ *srk = srkHandle;
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_tpm2_protector_srk_recover (const struct grub_tpm2_protector_context *ctx,
+ grub_uint8_t **key, grub_size_t *key_size)
+{
+ TPM_RC rc;
+ TPM2_SEALED_KEY sealed_key;
+ void *sealed_key_bytes;
+ grub_size_t sealed_key_size;
+ TPM_HANDLE srk_handle;
+ TPM2B_NONCE nonceCaller = { 0 };
+ TPM2B_ENCRYPTED_SECRET salt = { 0 };
+ TPMT_SYM_DEF symmetric = { 0 };
+ TPM2B_NONCE nonceTPM = { 0 };
+ TPMI_SH_AUTH_SESSION session;
+ TPML_PCR_SELECTION pcrSel = {
+ .count = 1,
+ .pcrSelections = {
+ {
+ .hash = ctx->bank,
+ .sizeOfSelect = 3,
+ .pcrSelect = { 0 }
+ },
+ }
+ };
+ TPMS_AUTH_COMMAND authCmd = { 0 };
+ TPM_HANDLE sealed_key_handle;
+ TPM2B_NAME name;
+ TPMS_AUTH_RESPONSE authResponse;
+ TPM2B_SENSITIVE_DATA data;
+ grub_uint8_t *key_out;
+ grub_uint8_t i;
+ grub_err_t err;
+
+ /* Retrieve Sealed Key */
+ err = grub_tpm2_protector_srk_read_keyfile (ctx->keyfile, &sealed_key_bytes,
+ &sealed_key_size);
+ if (err)
+ return err;
+
+ err = grub_tpm2_protector_srk_unmarshal_keyfile (sealed_key_bytes,
+ sealed_key_size,
+ &sealed_key);
+ if (err)
+ goto exit1;
+
+ /* Get SRK */
+ err = grub_tpm2_protector_srk_get (ctx, &srk_handle);
+ if (err)
+ goto exit1;
+
+ err = GRUB_ERR_BAD_DEVICE;
+
+ /* Start Auth Session */
+ nonceCaller.size = TPM_SHA256_DIGEST_SIZE;
+ symmetric.algorithm = TPM_ALG_NULL;
+
+ rc = TPM2_StartAuthSession (TPM_RH_NULL, TPM_RH_NULL, 0, &nonceCaller, &salt,
+ TPM_SE_POLICY, &symmetric, TPM_ALG_SHA256,
+ &session, &nonceTPM, 0);
+ if (rc)
+ goto exit2;
+
+ /* Policy PCR */
+ for (i = 0; i < ctx->pcr_count; i++)
+ pcrSel
+ .pcrSelections[0]
+ .pcrSelect[TPM2_PCR_TO_SELECT(ctx->pcrs[i])]
+ |= TPM2_PCR_TO_BIT(ctx->pcrs[i]);
+
+ rc = TPM2_PolicyPCR (session, NULL, NULL, &pcrSel, NULL);
+ if (rc)
+ goto exit3;
+
+ /* Load Sealed Key */
+ authCmd.sessionHandle = TPM_RS_PW;
+ rc = TPM2_Load (srk_handle, &authCmd, &sealed_key.private,
&sealed_key.public,
+ &sealed_key_handle, &name, &authResponse);
+ if (rc)
+ goto exit3;
+
+ /* Unseal Sealed Key */
+ authCmd.sessionHandle = session;
+ grub_memset (&authResponse, 0, sizeof (authResponse));
+
+ rc = TPM2_Unseal (sealed_key_handle, &authCmd, &data, &authResponse);
+ if (rc)
+ goto exit4;
+
+ /* Epilogue */
+ key_out = grub_malloc (data.size);
+ if (!key_out)
+ {
+ err = GRUB_ERR_OUT_OF_MEMORY;
+ goto exit4;
+ }
+
+ grub_memcpy (key_out, data.buffer, data.size);
+
+ *key = key_out;
+ *key_size = data.size;
+
+ err = GRUB_ERR_NONE;
+
+exit4:
+ TPM2_FlushContext (sealed_key_handle);
+
+exit3:
+ TPM2_FlushContext (session);
+
+exit2:
+ TPM2_FlushContext (srk_handle);
+
+exit1:
+ grub_free (sealed_key_bytes);
+ return err;
+}
+
+static grub_err_t
+grub_tpm2_protector_nv_recover (const struct grub_tpm2_protector_context *ctx,
+ grub_uint8_t **key, grub_size_t *key_size)
+{
+ (void)ctx;
+ (void)key;
+ (void)key_size;
+
+ return GRUB_ERR_NOT_IMPLEMENTED_YET;
+}
+
+static grub_err_t
+grub_tpm2_protector_recover (const struct grub_tpm2_protector_context *ctx,
+ grub_uint8_t **key, grub_size_t *key_size)
+{
+ switch (ctx->mode)
+ {
+ case GRUB_TPM2_PROTECTOR_MODE_SRK:
+ return grub_tpm2_protector_srk_recover (ctx, key, key_size);
+ case GRUB_TPM2_PROTECTOR_MODE_NV:
+ return grub_tpm2_protector_nv_recover (ctx, key, key_size);
+ default:
+ return GRUB_ERR_BAD_ARGUMENT;
+ }
+}
+
+static grub_err_t
+grub_tpm2_protector_recover_key (char *args, grub_uint8_t **key,
+ grub_size_t *key_size)
+{
+ grub_err_t err;
+ struct grub_tpm2_protector_context ctx = { 0 };
+
+ grub_dprintf ("tpm2", "Args: %s\n", args);
+
+ if (!args || !key)
+ return GRUB_ERR_BAD_ARGUMENT;
+
+ err = grub_tpm2_protector_parse_args (&ctx, args);
+ if (err) {
+ grub_dprintf("tpm2", "Failed to parse arguments\n");
+ return err;
+ }
+
+ err = grub_tpm2_protector_check_args (&ctx);
+ if (err) {
+ grub_dprintf("tpm2", "Invalid arguments\n");