grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH 3/5] protectors: Add TPM2 Key Protector


From: Glenn Washburn
Subject: Re: [PATCH 3/5] protectors: Add TPM2 Key Protector
Date: Mon, 24 Jan 2022 21:55:35 -0600

On Mon, 24 Jan 2022 06:12:16 -0800
Hernan Gatta <hegatta@linux.microsoft.com> wrote:

> From: Hernan Gatta <hegatta@microsoft.com>
> 
> 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 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.
> 

There are a lot of points in this code where an error can be returned.
In this case, all I'm going to get is a non-descript error code (almost
always GRUB_ERR_BAD_ARGUMENT). So as a user, how am I to know where the
error is actually coming from? For this reason, I like the use of
grub_error(error_code, error_format_string, fmtstr_args...), which will
set grub_errno and an error message, which can be retrieved up the stack
and presented to the user. I'm not sure that every return of a naked
error code should be replaced by grub_error(), depends on how granular
of an error message you want to see. I would error on the side of more
granular. For instance, its really nice that GCC will tell me which
format code type specifier and a argument its choking on, rather than a
catch all "I'm failing to compile your code because you have an
incorrect format code somewhere in your format string".

> Signed-off-by: Hernan Gatta <hegatta@linux.microsoft.com>
> ---
>  grub-core/Makefile.core.def |   9 +
>  grub-core/tpm2/module.c     | 742 
> ++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 751 insertions(+)
>  create mode 100644 grub-core/tpm2/module.c
> 
> diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
> index e4ae78b..8691440 100644
> --- a/grub-core/Makefile.core.def
> +++ b/grub-core/Makefile.core.def
> @@ -2498,6 +2498,15 @@ module = {
>  };
>  
>  module = {
> +  name = tpm2;
> +  common = tpm2/buffer.c;
> +  common = tpm2/module.c;
> +  common = tpm2/mu.c;
> +  common = tpm2/tpm2.c;
> +  efi = tpm2/tcg2.c;
> +};
> +
> +module = {
>    name = tr;
>    common = commands/tr.c;
>  };
> diff --git a/grub-core/tpm2/module.c b/grub-core/tpm2/module.c
> new file mode 100644
> index 0000000..cd97452
> --- /dev/null
> +++ b/grub-core/tpm2/module.c
> @@ -0,0 +1,742 @@
> +/*
> + *  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/file.h>
> +#include <grub/misc.h>
> +#include <grub/mm.h>
> +#include <grub/protector.h>
> +#include <grub/tpm2/buffer.h>
> +#include <grub/tpm2/mu.h>
> +#include <grub/tpm2/tpm2.h>
> +#include <grub/types.h>
> +
> +GRUB_MOD_LICENSE ("GPLv3+");
> +
> +typedef enum grub_tpm2_protector_args
> +{
> +  GRUB_TPM2_PROTECTOR_ARG_MODE       = 1 << 0,
> +  GRUB_TPM2_PROTECTOR_ARG_PCRS       = 1 << 1,
> +  GRUB_TPM2_PROTECTOR_ARG_ASYMMETRIC = 1 << 2,
> +  GRUB_TPM2_PROTECTOR_ARG_BANK       = 1 << 3,
> +  GRUB_TPM2_PROTECTOR_ARG_KEYFILE    = 1 << 4,
> +  GRUB_TPM2_PROTECTOR_ARG_SRK        = 1 << 5,
> +  GRUB_TPM2_PROTECTOR_ARG_NV         = 1 << 6
> +} grub_tpm2_protector_args_t;
> +
> +typedef enum grub_tpm2_protector_mode
> +{
> +  GRUB_TPM2_PROTECTOR_MODE_SRK = 1,
> +  GRUB_TPM2_PROTECTOR_MODE_NV  = 2
> +} grub_tpm2_protector_mode_t;
> +
> +struct grub_tpm2_protector_context
> +{
> +  grub_tpm2_protector_args_t args;
> +  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 grub_err_t
> +grub_tpm2_protector_parse_mode (struct grub_tpm2_protector_context *ctx,
> +                                const char *value)
> +{
> +  if (grub_strcmp (value, "srk") == 0)
> +    ctx->mode = GRUB_TPM2_PROTECTOR_MODE_SRK;
> +  else if (grub_strcmp (value, "nv") == 0)
> +    ctx->mode = GRUB_TPM2_PROTECTOR_MODE_NV;
> +  else
> +    return GRUB_ERR_OUT_OF_RANGE;
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_tpm2_protector_parse_pcrs (struct grub_tpm2_protector_context *ctx,
> +                                char *value)
> +{
> +  grub_err_t err;
> +  char *current_pcr = value;
> +  char *next_pcr;
> +  unsigned long pcr;
> +  grub_uint8_t i;
> +
> +  if (grub_strlen (value) == 0)
> +    return GRUB_ERR_BAD_ARGUMENT;
> +
> +  ctx->pcr_count = 0;
> +  for (i = 0; i < TPM_MAX_PCRS; i++)
> +    {
> +      next_pcr = grub_strchr (current_pcr, ',');
> +      if (next_pcr)
> +        *next_pcr = '\0';
> +      if (next_pcr == current_pcr)
> +        return GRUB_ERR_BAD_ARGUMENT;
> +
> +      grub_errno = GRUB_ERR_NONE;
> +      pcr = grub_strtoul (current_pcr, NULL, 10);
> +      if (grub_errno != GRUB_ERR_NONE)
> +        {
> +          err = grub_errno;
> +          grub_errno = GRUB_ERR_NONE;
> +          return err;
> +        }
> +
> +      if (pcr > GRUB_UCHAR_MAX)
> +        return GRUB_ERR_OUT_OF_RANGE;
> +
> +      ctx->pcrs[i] = (grub_uint8_t)pcr;
> +      ctx->pcr_count++;
> +
> +      if (!next_pcr)
> +        break;
> +
> +      current_pcr = next_pcr + 1;
> +      if (*current_pcr == '\0')
> +        return GRUB_ERR_BAD_ARGUMENT;
> +    }
> +
> +  if (i == TPM_MAX_PCRS)
> +    return GRUB_ERR_OUT_OF_RANGE;
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_tpm2_protector_parse_asymmetric (struct grub_tpm2_protector_context 
> *ctx,
> +                                      const char *value)
> +{
> +  if (grub_strcasecmp (value, "ECC") == 0)
> +    ctx->asymmetric = TPM_ALG_ECC;
> +  else if (grub_strcasecmp (value, "RSA") == 0)
> +    ctx->asymmetric = TPM_ALG_RSA;
> +  else
> +    return GRUB_ERR_OUT_OF_RANGE;
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_tpm2_protector_parse_bank (struct grub_tpm2_protector_context *ctx,
> +                                const char *value)
> +{
> +  if (grub_strcasecmp (value, "SHA1") == 0)
> +    ctx->bank = TPM_ALG_SHA1;
> +  else if (grub_strcasecmp (value, "SHA256") == 0)
> +    ctx->bank = TPM_ALG_SHA256;
> +  else if (grub_strcasecmp (value, "SHA384") == 0)
> +    ctx->bank = TPM_ALG_SHA384;
> +  else
> +    return GRUB_ERR_OUT_OF_RANGE;
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_tpm2_protector_parse_keyfile (struct grub_tpm2_protector_context *ctx,
> +                                   const char *value)
> +{
> +  if (grub_strlen (value) == 0)
> +    return GRUB_ERR_BAD_ARGUMENT;
> +
> +  ctx->keyfile = value;
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_tpm2_protector_parse_tpm_handle (const char *value, TPM_HANDLE *handle)
> +{
> +  grub_err_t err;
> +  unsigned long num;
> +
> +  grub_errno = GRUB_ERR_NONE;
> +  num = grub_strtoul (value, NULL, 0);
> +  if (grub_errno != GRUB_ERR_NONE)
> +    {
> +      err = grub_errno;
> +      grub_errno = GRUB_ERR_NONE;
> +      return err;
> +    }
> +
> +  if (num > GRUB_UINT_MAX)
> +    return GRUB_ERR_OUT_OF_RANGE;
> +
> +  *handle = (TPM_HANDLE)num;
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_tpm2_protector_parse_srk (struct grub_tpm2_protector_context *ctx,
> +                               const char *value)
> +{
> +  return grub_tpm2_protector_parse_tpm_handle (value, &ctx->srk);
> +}
> +
> +static grub_err_t
> +grub_tpm2_protector_parse_nvindex (struct grub_tpm2_protector_context *ctx,
> +                                   const char *value)
> +{
> +  return grub_tpm2_protector_parse_tpm_handle (value, &ctx->nv);
> +}
> +
> +static grub_err_t
> +grub_tpm2_protector_match_arg (struct grub_tpm2_protector_context *ctx,
> +                               const char *key, char *value)
> +{
> +  grub_err_t err;
> +
> +  if (grub_strlen (key) == 0 || grub_strlen (value) == 0)
> +    return GRUB_ERR_BAD_ARGUMENT;
> +
> +  if (grub_strcmp (key, "mode") == 0)
> +  {
> +    if (ctx->args & GRUB_TPM2_PROTECTOR_ARG_MODE)
> +      return GRUB_ERR_BAD_ARGUMENT;
> +
> +    err = grub_tpm2_protector_parse_mode (ctx, value);
> +    if (!err)
> +      ctx->args |= GRUB_TPM2_PROTECTOR_ARG_MODE;
> +
> +    return err;
> +  }
> +
> +  if (grub_strcmp (key, "pcrs") == 0)

Minor nit, why wouldn't this be better as an "else if"? Seems
unnecessary to check all the "if" statements if we've already found a
match.

> +  {
> +    if (ctx->args & GRUB_TPM2_PROTECTOR_ARG_PCRS)
> +      return GRUB_ERR_BAD_ARGUMENT;
> +
> +    err = grub_tpm2_protector_parse_pcrs (ctx, value);
> +    if (!err)
> +      ctx->args |= GRUB_TPM2_PROTECTOR_ARG_PCRS;
> +
> +    return err;
> +  }
> +
> +  if (grub_strcmp (key, "asymmetric") == 0)

Ditto, for the rest of these ifs.

> +  {
> +    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");

It's nice that this is here to at least let the user know that the
failure was because of invalid arguments as opposed to a failure in the
parsing. Even still, which argument was invalid? why was it invalid? As
a user, this might be frustrating to diagnose. This is why a message
specific to the error would be appreciated.

Glenn

> +    return err;
> +  }
> +
> +  err = grub_tpm2_protector_recover (&ctx, key, key_size);
> +  if (err) {
> +    grub_dprintf("tpm2", "Failed to recover key\n");
> +    return err;
> +  }
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +struct grub_key_protector grub_tpm2_key_protector = {
> +  .name = "tpm2",
> +  .flags = GRUB_KEY_PROTECTOR_FLAG_NONE,
> +  .recover_key = grub_tpm2_protector_recover_key
> +};
> +
> +GRUB_MOD_INIT(tpm2)
> +{
> +  grub_key_protector_register (&grub_tpm2_key_protector);
> +}
> +
> +GRUB_MOD_FINI(tpm2)
> +{
> +  grub_key_protector_unregister (&grub_tpm2_key_protector);
> +}



reply via email to

[Prev in Thread] Current Thread [Next in Thread]