[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH v2 3/5] protectors: Add TPM2 Key Protector
From: |
Michael Chang |
Subject: |
Re: [PATCH v2 3/5] protectors: Add TPM2 Key Protector |
Date: |
Tue, 22 Mar 2022 11:47:20 +0800 |
User-agent: |
Mutt/1.10.1 (2018-07-13) |
On Tue, Feb 01, 2022 at 05:02:55AM -0800, Hernan Gatta wrote:
> The TPM2 key protector is a module that enables the automatic retrieval of a
> fully-encrypted disk's unlocking key from a TPM 2.0.
>
> The theory of operation is such that the module accepts various arguments,
> most
> of which are optional and therefore possess reasonable defaults. One of these
> arguments is the keyfile parameter, which is mandatory.
>
> The value of this parameter must be a path to a sealed key file (e.g.,
> (hd0,gpt1)/boot/grub2/sealed_key). This sealed key file is created via the
> grub-protect tool. The tool utilizes the TPM's sealing functionality to seal
> (i.e., encrypt) an unlocking key using a Storage Root Key (SRK) to the values
> of
> various Platform Configuration Registers (PCRs). These PCRs reflect the state
> of
> the system as it boots. If the values are as expected, the system may be
> considered trustworthy, at which point the TPM allows for a caller to utilize
> the private component of the SRK to unseal (i.e., decrypt) the sealed key
> file.
> The caller, in this case, is this key protector.
>
> The TPM2 key protector registers two commands:
>
> - tpm2_key_protector_init: Initializes the state of the TPM2 key protector for
> later usage, clearing any previous state, too, if
> any.
>
> - tpm2_key_protector_clear: Clears any state set by tpm2_key_protector_init.
>
> The way this is expected to be used requires the user to, either interactively
> or, normally, via a boot script, initialize (i.e., configure) the key
> protector
> and then specify that it be used by the cryptomount command (modifications to
> this command are in a different patch).
>
> For instance:
>
> tpm2_key_protector_init --keyfile=KEYFILE1
> cryptomount DISK1 -k tpm2
>
> tpm2_key_protector_init --keyfile=KEYFILE2 --pcrs=7,11
> cryptomount DISK2 -k tpm2
>
> If a user does not initialize the key protector and attempts to use it anyway,
> the protector returns an error.
>
> Signed-off-by: Hernan Gatta <hegatta@linux.microsoft.com>
> ---
> grub-core/Makefile.core.def | 10 +
> grub-core/tpm2/args.c | 129 +++++++
> grub-core/tpm2/module.c | 710
> ++++++++++++++++++++++++++++++++++++++
> include/grub/tpm2/internal/args.h | 39 +++
> 4 files changed, 888 insertions(+)
> create mode 100644 grub-core/tpm2/args.c
> create mode 100644 grub-core/tpm2/module.c
> create mode 100644 include/grub/tpm2/internal/args.h
>
> diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
> index e4ae78b..78d09d9 100644
> --- a/grub-core/Makefile.core.def
> +++ b/grub-core/Makefile.core.def
> @@ -2498,6 +2498,16 @@ module = {
> };
>
> module = {
> + name = tpm2;
> + common = tpm2/args.c;
> + common = tpm2/buffer.c;
> + common = tpm2/module.c;
> + common = tpm2/mu.c;
> + common = tpm2/tpm2.c;
> + efi = tpm2/tcg2.c;
The tpm2 module fails to build on non-efi platforms because of undefined
symbols from tpm2/tcg2.c. Maybe adding enable = efi is required here as
long as supporting other platform seems not to be intended.
Thanks,
Michael
> +};
> +
> +module = {
> name = tr;
> common = commands/tr.c;
> };
> diff --git a/grub-core/tpm2/args.c b/grub-core/tpm2/args.c
> new file mode 100644
> index 0000000..90c7cd8
> --- /dev/null
> +++ b/grub-core/tpm2/args.c
> @@ -0,0 +1,129 @@
> +/*
> + * GRUB -- GRand Unified Bootloader
> + * Copyright (C) 2022 Microsoft Corporation
> + *
> + * GRUB is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 3 of the License, or
> + * (at your option) any later version.
> + *
> + * GRUB is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <grub/err.h>
> +#include <grub/mm.h>
> +#include <grub/misc.h>
> +#include <grub/tpm2/internal/args.h>
> +
> +grub_err_t
> +grub_tpm2_protector_parse_pcrs (char *value, grub_uint8_t *pcrs,
> + grub_uint8_t *pcr_count)
> +{
> + char *current_pcr = value;
> + char *next_pcr;
> + unsigned long pcr;
> + grub_uint8_t i;
> +
> + if (grub_strlen (value) == 0)
> + return GRUB_ERR_BAD_ARGUMENT;
> +
> + *pcr_count = 0;
> + for (i = 0; i < TPM_MAX_PCRS; i++)
> + {
> + next_pcr = grub_strchr (current_pcr, ',');
> + if (next_pcr == current_pcr)
> + return grub_error (GRUB_ERR_BAD_ARGUMENT,
> + N_("Empty entry in PCR list"));
> + if (next_pcr)
> + *next_pcr = '\0';
> +
> + grub_errno = GRUB_ERR_NONE;
> + pcr = grub_strtoul (current_pcr, NULL, 10);
> + if (grub_errno != GRUB_ERR_NONE)
> + return grub_error (grub_errno,
> + N_("Entry '%s' in PCR list is not a number"),
> + current_pcr);
> +
> + if (pcr > TPM_MAX_PCRS)
> + return grub_error (GRUB_ERR_OUT_OF_RANGE,
> + N_("Entry %lu in PCR list is too large to be a
> PCR "
> + "number, PCR numbers range from 0 to %u"),
> + pcr, TPM_MAX_PCRS);
> +
> + pcrs[i] = (grub_uint8_t)pcr;
> + *pcr_count += 1;
> +
> + if (!next_pcr)
> + break;
> +
> + current_pcr = next_pcr + 1;
> + if (*current_pcr == '\0')
> + return grub_error (GRUB_ERR_BAD_ARGUMENT,
> + N_("Trailing comma at the end of PCR list"));
> + }
> +
> + if (i == TPM_MAX_PCRS)
> + return grub_error (GRUB_ERR_OUT_OF_RANGE,
> + N_("Too many PCRs in PCR list, the maximum number of "
> + "PCRs is %u"), TPM_MAX_PCRS);
> +
> + return GRUB_ERR_NONE;
> +}
> +
> +grub_err_t
> +grub_tpm2_protector_parse_asymmetric (const char *value, TPM_ALG_ID
> *asymmetric)
> +{
> + if (grub_strcasecmp (value, "ECC") == 0)
> + *asymmetric = TPM_ALG_ECC;
> + else if (grub_strcasecmp (value, "RSA") == 0)
> + *asymmetric = TPM_ALG_RSA;
> + else
> + return grub_error (GRUB_ERR_OUT_OF_RANGE,
> + N_("Value '%s' is not a valid asymmetric key type"),
> + value);
> +
> + return GRUB_ERR_NONE;
> +}
> +
> +grub_err_t
> +grub_tpm2_protector_parse_bank (const char *value, TPM_ALG_ID *bank)
> +{
> + if (grub_strcasecmp (value, "SHA1") == 0)
> + *bank = TPM_ALG_SHA1;
> + else if (grub_strcasecmp (value, "SHA256") == 0)
> + *bank = TPM_ALG_SHA256;
> + else if (grub_strcasecmp (value, "SHA384") == 0)
> + *bank = TPM_ALG_SHA384;
> + else
> + return grub_error (GRUB_ERR_OUT_OF_RANGE,
> + N_("Value '%s' is not a valid PCR bank"), value);
> +
> + return GRUB_ERR_NONE;
> +}
> +
> +grub_err_t
> +grub_tpm2_protector_parse_tpm_handle (const char *value, TPM_HANDLE *handle)
> +{
> + unsigned long num;
> +
> + grub_errno = GRUB_ERR_NONE;
> + num = grub_strtoul (value, NULL, 0);
> + if (grub_errno != GRUB_ERR_NONE)
> + return grub_error (grub_errno, N_("TPM handle value '%s' is not a
> number"),
> + value);
> +
> + if (num > GRUB_UINT_MAX)
> + return grub_error (GRUB_ERR_OUT_OF_RANGE,
> + N_("Value %lu is too large to be a TPM handle, TPM "
> + "handles are unsigned 32-bit integers"), num);
> +
> + *handle = (TPM_HANDLE)num;
> +
> + return GRUB_ERR_NONE;
> +}
> diff --git a/grub-core/tpm2/module.c b/grub-core/tpm2/module.c
> new file mode 100644
> index 0000000..3f2f386
> --- /dev/null
> +++ b/grub-core/tpm2/module.c
> @@ -0,0 +1,710 @@
> +/*
> + * GRUB -- GRand Unified Bootloader
> + * Copyright (C) 2022 Microsoft Corporation
> + *
> + * GRUB is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 3 of the License, or
> + * (at your option) any later version.
> + *
> + * GRUB is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <grub/dl.h>
> +#include <grub/extcmd.h>
> +#include <grub/file.h>
> +#include <grub/misc.h>
> +#include <grub/mm.h>
> +#include <grub/protector.h>
> +#include <grub/tpm2/buffer.h>
> +#include <grub/tpm2/internal/args.h>
> +#include <grub/tpm2/mu.h>
> +#include <grub/tpm2/tpm2.h>
> +
> +GRUB_MOD_LICENSE ("GPLv3+");
> +
> +typedef enum grub_tpm2_protector_mode
> +{
> + GRUB_TPM2_PROTECTOR_MODE_UNSET,
> + GRUB_TPM2_PROTECTOR_MODE_SRK,
> + GRUB_TPM2_PROTECTOR_MODE_NV
> +} grub_tpm2_protector_mode_t;
> +
> +struct grub_tpm2_protector_context
> +{
> + grub_tpm2_protector_mode_t mode;
> + grub_uint8_t pcrs[TPM_MAX_PCRS];
> + grub_uint8_t pcr_count;
> + TPM_ALG_ID asymmetric;
> + TPM_ALG_ID bank;
> + const char *keyfile;
> + TPM_HANDLE srk;
> + TPM_HANDLE nv;
> +};
> +
> +static const struct grub_arg_option grub_tpm2_protector_init_cmd_options[] =
> + {
> + /* Options for all modes */
> + {
> + .longarg = "mode",
> + .shortarg = 'm',
> + .flags = 0,
> + .arg = NULL,
> + .type = ARG_TYPE_STRING,
> + .doc =
> + N_("Unseal key using SRK ('srk') (default) or retrieve it from an NV
> "
> + "Index ('nv')."),
> + },
> + {
> + .longarg = "pcrs",
> + .shortarg = 'p',
> + .flags = 0,
> + .arg = NULL,
> + .type = ARG_TYPE_STRING,
> + .doc =
> + N_("Comma-separated list of PCRs used to authorize key release "
> + "(e.g., '7,11', default is 7."),
> + },
> + {
> + .longarg = "bank",
> + .shortarg = 'b',
> + .flags = 0,
> + .arg = NULL,
> + .type = ARG_TYPE_STRING,
> + .doc =
> + N_("Bank of PCRs used to authorize key release: "
> + "SHA1, SHA256 (default), or SHA384."),
> + },
> + /* SRK-mode options */
> + {
> + .longarg = "keyfile",
> + .shortarg = 'k',
> + .flags = 0,
> + .arg = NULL,
> + .type = ARG_TYPE_STRING,
> + .doc =
> + N_("Required in SRK mode, path to the sealed key file to unseal
> using "
> + "the TPM (e.g., (hd0,gpt1)/boot/grub2/sealed_key)."),
> + },
> + {
> + .longarg = "srk",
> + .shortarg = 's',
> + .flags = 0,
> + .arg = NULL,
> + .type = ARG_TYPE_STRING,
> + .doc =
> + N_("In SRK mode, the SRK handle if the SRK is persistent "
> + "(default is 0x81000001)."),
> + },
> + {
> + .longarg = "asymmetric",
> + .shortarg = 'a',
> + .flags = 0,
> + .arg = NULL,
> + .type = ARG_TYPE_STRING,
> + .doc =
> + N_("In SRK mode, the type of SRK: RSA (default) or ECC."),
> + },
> + /* NV Index-mode options */
> + {
> + .longarg = "nvindex",
> + .shortarg = 'n',
> + .flags = 0,
> + .arg = NULL,
> + .type = ARG_TYPE_STRING,
> + .doc =
> + N_("Required in NV Index mode, the NV handle to read which must "
> + "readily exist on the TPM and which contains the key."),
> + },
> + /* End of list */
> + {0, 0, 0, 0, 0, 0}
> + };
> +
> +static grub_extcmd_t grub_tpm2_protector_init_cmd;
> +static grub_extcmd_t grub_tpm2_protector_clear_cmd;
> +static struct grub_tpm2_protector_context grub_tpm2_protector_ctx = { 0 };
> +
> +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_dprintf ("tpm2", "Could not open sealed key file.\n");
> + /* 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_dprintf ("tpm2", "Could not read sealed key file size.\n");
> + 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_dprintf ("tpm2", "Could not allocate buffer for sealed key.\n");
> + 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_dprintf ("tpm2", "Could not retrieve sealed key file
> contents.\n");
> + 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)
> + {
> + grub_dprintf ("tpm2", "Sealed key file is larger than decode buffer "
> + "(%lu vs %lu bytes).\n", 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);
> +
> + if (buf.error)
> + {
> + grub_dprintf ("tpm2", "Could not unmarshal sealed key file, it is
> likely "
> + "malformed.\n");
> + return GRUB_ERR_BAD_ARGUMENT;
> + }
> +
> + return 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)
> + {
> + grub_dprintf ("tpm2", "The SRK handle (0x%x) exists on the TPM but its
> "
> + "public area could not be read (TPM2_ReadPublic "
> + "failed with TSS/TPM error %u).\n", ctx->srk,
> rc);
> + 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)
> + {
> + grub_dprintf ("tpm2", "Could not create SRK (TPM2_CreatePrimary failed
> "
> + "with TSS/TPM error %u).\n", rc);
> + 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 grub_error (err, N_("Failed to read key file %s"), ctx->keyfile);
> +
> + err = grub_tpm2_protector_srk_unmarshal_keyfile (sealed_key_bytes,
> + sealed_key_size,
> + &sealed_key);
> + if (err)
> + {
> + grub_error (err, N_("Failed to unmarshal key, ensure the key file is
> in "
> + "TPM wire format"));
> + goto exit1;
> + }
> +
> + /* Get SRK */
> + err = grub_tpm2_protector_srk_get (ctx, &srk_handle);
> + if (err)
> + {
> + grub_error (err, N_("Failed to retrieve the SRK"));
> + 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)
> + {
> + grub_error (err, N_("Failed to start auth session
> (TPM2_StartAuthSession "
> + "failed with TSS/TPM error %u)"), 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)
> + {
> + grub_error (err, N_("Failed to submit PCR policy (TPM2_PolicyPCR
> failed "
> + "with TSS/TPM error %u)"), 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)
> + {
> + grub_error (err, N_("Failed to load sealed key (TPM2_Load failed with "
> + "TSS/TPM error %u)"), 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)
> + {
> + grub_error (err, N_("Failed to unseal sealed key (TPM2_Unseal failed "
> + "with TSS/TPM error %u)"), rc);
> + goto exit4;
> + }
> +
> + /* Epilogue */
> + key_out = grub_malloc (data.size);
> + if (!key_out)
> + {
> + err = GRUB_ERR_OUT_OF_MEMORY;
> + grub_error (err, N_("No memory left to allocate unlock key buffer"));
> + 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_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
> + N_("NV Index mode is 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 (grub_uint8_t **key, grub_size_t *key_size)
> +{
> + grub_err_t err;
> +
> + /* Expect a call to tpm2_protector_init before anybody tries to use us */
> + if (grub_tpm2_protector_ctx.mode == GRUB_TPM2_PROTECTOR_MODE_UNSET)
> + return grub_error (GRUB_ERR_INVALID_COMMAND,
> + N_("Cannot use TPM2 key protector without
> initializing "
> + "it, call tpm2_protector_init first"));
> +
> + if (!key)
> + return GRUB_ERR_BAD_ARGUMENT;
> +
> + err = grub_tpm2_protector_recover (&grub_tpm2_protector_ctx, key,
> key_size);
> + if (err)
> + return err;
> +
> + return GRUB_ERR_NONE;
> +}
> +
> +
> +static grub_err_t
> +grub_tpm2_protector_check_args (struct grub_tpm2_protector_context *ctx)
> +{
> + if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_UNSET)
> + ctx->mode = GRUB_TPM2_PROTECTOR_MODE_SRK;
> +
> + /* Checks for SRK mode */
> + if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_SRK && !ctx->keyfile)
> + return grub_error (GRUB_ERR_BAD_ARGUMENT,
> + N_("In SRK mode, a key file must be specified: "
> + "--keyfile or -k"));
> +
> + if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_SRK && ctx->nv)
> + return grub_error (GRUB_ERR_BAD_ARGUMENT,
> + N_("In SRK mode, an NV Index cannot be specified"));
> +
> + /* Checks for NV mode */
> + if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV && !ctx->nv)
> + return grub_error (GRUB_ERR_BAD_ARGUMENT,
> + N_("In NV Index mode, an NV Index must be specified: "
> + "--nvindex or -n"));
> +
> + if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV && ctx->keyfile)
> + return grub_error (GRUB_ERR_BAD_ARGUMENT,
> + N_("In NV Index mode, a keyfile cannot be
> specified"));
> +
> + if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV && ctx->srk)
> + return grub_error (GRUB_ERR_BAD_ARGUMENT,
> + N_("In NV Index mode, an SRK cannot be specified"));
> +
> + if (ctx->mode == GRUB_TPM2_PROTECTOR_MODE_NV && ctx->asymmetric)
> + return grub_error (GRUB_ERR_BAD_ARGUMENT,
> + N_("In NV Index mode, an asymmetric key type cannot
> be "
> + "specified"));
> +
> + /* Defaults assignment */
> + if (!ctx->bank)
> + ctx->bank = TPM_ALG_SHA256;
> +
> + if (!ctx->pcr_count)
> + {
> + 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;
> + }
> +
> + return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_tpm2_protector_parse_keyfile (const char *value, const char **keyfile)
> +{
> + if (grub_strlen (value) == 0)
> + return GRUB_ERR_BAD_ARGUMENT;
> +
> + *keyfile = grub_strdup (value);
> + if (!*keyfile)
> + return grub_error (GRUB_ERR_OUT_OF_MEMORY,
> + N_("No memory to duplicate keyfile path"));
> +
> + return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_tpm2_protector_parse_mode (const char *value,
> + grub_tpm2_protector_mode_t *mode)
> +{
> + if (grub_strcmp (value, "srk") == 0)
> + *mode = GRUB_TPM2_PROTECTOR_MODE_SRK;
> + else if (grub_strcmp (value, "nv") == 0)
> + *mode = GRUB_TPM2_PROTECTOR_MODE_NV;
> + else
> + return grub_error (GRUB_ERR_OUT_OF_RANGE,
> + N_("Value '%s' is not a valid TPM2 key protector
> mode"),
> + value);
> +
> + return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_tpm2_protector_init_cmd_handler (grub_extcmd_context_t ctxt, int argc,
> + char **args __attribute__ ((unused)))
> +{
> + struct grub_arg_list *state = ctxt->state;
> + grub_err_t err;
> +
> + if (argc)
> + return grub_error (GRUB_ERR_BAD_ARGUMENT,
> + N_("The TPM2 key protector does not accept any "
> + "non-option arguments (i.e., like -o and/or
> --option "
> + "only)"));
> +
> + grub_free ((void *) grub_tpm2_protector_ctx.keyfile);
> + grub_memset (&grub_tpm2_protector_ctx, 0, sizeof
> (grub_tpm2_protector_ctx));
> +
> + if (state[0].set) /* mode */
> + {
> + err = grub_tpm2_protector_parse_mode (state[0].arg,
> + &grub_tpm2_protector_ctx.mode);
> + if (err)
> + return err;
> + }
> +
> + if (state[1].set) /* pcrs */
> + {
> + err = grub_tpm2_protector_parse_pcrs (state[1].arg,
> + grub_tpm2_protector_ctx.pcrs,
> +
> &grub_tpm2_protector_ctx.pcr_count);
> + if (err)
> + return err;
> + }
> +
> + if (state[2].set) /* bank */
> + {
> + err = grub_tpm2_protector_parse_bank (state[2].arg,
> + &grub_tpm2_protector_ctx.bank);
> + if (err)
> + return err;
> + }
> +
> + if (state[3].set) /* keyfile */
> + {
> + err = grub_tpm2_protector_parse_keyfile (state[3].arg,
> +
> &grub_tpm2_protector_ctx.keyfile);
> + if (err)
> + return err;
> + }
> +
> + if (state[4].set) /* srk */
> + {
> + err = grub_tpm2_protector_parse_tpm_handle (state[4].arg,
> +
> &grub_tpm2_protector_ctx.srk);
> + if (err)
> + return err;
> + }
> +
> + if (state[5].set) /* asymmetric */
> + {
> + err = grub_tpm2_protector_parse_asymmetric (state[5].arg,
> +
> &grub_tpm2_protector_ctx.asymmetric);
> + if (err)
> + return err;
> + }
> +
> + if (state[6].set) /* nvindex */
> + {
> + err = grub_tpm2_protector_parse_tpm_handle (state[6].arg,
> +
> &grub_tpm2_protector_ctx.nv);
> + if (err)
> + return err;
> + }
> +
> + err = grub_tpm2_protector_check_args (&grub_tpm2_protector_ctx);
> +
> + /* This command only initializes the protector, so nothing else to do. */
> +
> + return err;
> +}
> +
> +static grub_err_t
> +grub_tpm2_protector_clear_cmd_handler (grub_extcmd_context_t ctxt
> __attribute__ ((unused)),
> + int argc,
> + char **args __attribute__ ((unused)))
> +{
> + if (argc)
> + return grub_error (GRUB_ERR_BAD_ARGUMENT,
> + N_("tpm2_key_protector_clear accepts no arguments"));
> +
> + grub_free ((void *) grub_tpm2_protector_ctx.keyfile);
> + grub_memset (&grub_tpm2_protector_ctx, 0, sizeof
> (grub_tpm2_protector_ctx));
> +
> + return GRUB_ERR_NONE;
> +}
> +
> +static struct grub_key_protector grub_tpm2_key_protector =
> + {
> + .name = "tpm2",
> + .recover_key = grub_tpm2_protector_recover_key
> + };
> +
> +GRUB_MOD_INIT (tpm2)
> +{
> + grub_tpm2_protector_init_cmd =
> + grub_register_extcmd ("tpm2_key_protector_init",
> + grub_tpm2_protector_init_cmd_handler, 0,
> + N_("[-m mode] "
> + "[-p pcr_list] "
> + "[-b pcr_bank] "
> + "[-k sealed_key_file_path] "
> + "[-s srk_handle] "
> + "[-a asymmetric_key_type] "
> + "[-n nv_index]"),
> + N_("Initialize the TPM2 key protector."),
> + grub_tpm2_protector_init_cmd_options);
> + grub_tpm2_protector_clear_cmd =
> + grub_register_extcmd ("tpm2_key_protector_clear",
> + grub_tpm2_protector_clear_cmd_handler, 0, NULL,
> + N_("Clear the TPM2 key protector if previously
> initialized."),
> + NULL);
> + grub_key_protector_register (&grub_tpm2_key_protector);
> +}
> +
> +GRUB_MOD_FINI (tpm2)
> +{
> + grub_free ((void *) grub_tpm2_protector_ctx.keyfile);
> + grub_memset (&grub_tpm2_protector_ctx, 0, sizeof
> (grub_tpm2_protector_ctx));
> +
> + grub_key_protector_unregister (&grub_tpm2_key_protector);
> + grub_unregister_extcmd (grub_tpm2_protector_clear_cmd);
> + grub_unregister_extcmd (grub_tpm2_protector_init_cmd);
> +}
> diff --git a/include/grub/tpm2/internal/args.h
> b/include/grub/tpm2/internal/args.h
> new file mode 100644
> index 0000000..6341fce
> --- /dev/null
> +++ b/include/grub/tpm2/internal/args.h
> @@ -0,0 +1,39 @@
> +/*
> + * GRUB -- GRand Unified Bootloader
> + * Copyright (C) 2022 Microsoft Corporation
> + *
> + * GRUB is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 3 of the License, or
> + * (at your option) any later version.
> + *
> + * GRUB is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef GRUB_TPM2_INTERNAL_ARGS_HEADER
> +#define GRUB_TPM2_INTERNAL_ARGS_HEADER 1
> +
> +#include <grub/err.h>
> +#include <grub/tpm2/tpm2.h>
> +
> +grub_err_t
> +grub_tpm2_protector_parse_pcrs (char *value, grub_uint8_t *pcrs,
> + grub_uint8_t *pcr_count);
> +
> +grub_err_t
> +grub_tpm2_protector_parse_asymmetric (const char *value,
> + TPM_ALG_ID *asymmetric);
> +
> +grub_err_t
> +grub_tpm2_protector_parse_bank (const char *value, TPM_ALG_ID *bank);
> +
> +grub_err_t
> +grub_tpm2_protector_parse_tpm_handle (const char *value, TPM_HANDLE *handle);
> +
> +#endif /* ! GRUB_TPM2_INTERNAL_ARGS_HEADER */
> --
> 1.8.3.1
>
>
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> https://lists.gnu.org/mailman/listinfo/grub-devel
- Re: [PATCH v2 3/5] protectors: Add TPM2 Key Protector,
Michael Chang <=