On Mon, Oct 21, 2024 at 04:07:01PM +0800, Gary Lin wrote:
> From: Hernan Gatta <hega...@linux.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 and therefore possess reasonable
> defaults. One of these arguments is the keyfile/tpm2key parameter, which
> is mandatory. There are two supported key formats:
>
> 1. Raw Sealed Key (--keyfile)
>    When sealing a key with TPM2_Create, the public portion of the sealed
>    key is stored in TPM2B_PUBLIC, and the private portion is in
>    TPM2B_PRIVATE. The raw sealed key glues the fully marshalled
>    TPM2B_PUBLIC and TPM2B_PRIVATE into one file.
>
> 2. TPM 2.0 Key (--tpm2key)
>    The following is the ASN.1 definition of TPM 2.0 Key File:
>
>    TPMPolicy ::= SEQUENCE {
>      CommandCode   [0] EXPLICIT INTEGER
>      CommandPolicy [1] EXPLICIT OCTET STRING
>    }
>
>    TPMAuthPolicy ::= SEQUENCE {
>      Name    [0] EXPLICIT UTF8STRING OPTIONAL
>      Policy  [1] EXPLICIT SEQUENCE OF TPMPolicy
>    }
>
>    TPMKey ::= SEQUENCE {
>      type        OBJECT IDENTIFIER
>      emptyAuth   [0] EXPLICIT BOOLEAN OPTIONAL
>      policy      [1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL
>      secret      [2] EXPLICIT OCTET STRING OPTIONAL
>      authPolicy  [3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL
>      description [4] EXPLICIT UTF8String OPTIONAL,
>      rsaParent   [5] EXPLICIT BOOLEAN OPTIONAL,
>      parent      INTEGER
>      pubkey      OCTET STRING
>      privkey     OCTET STRING
>    }
>
>   The TPM2 key protector only expects a "sealed" key in DER encoding,
>   so 'type' is always 2.23.133.10.1.5, 'emptyAuth' is 'TRUE', and
>   'secret' is empty. 'policy' and 'authPolicy' are the possible policy
>   command sequences to construst the policy digest to unseal the key.
>   Similar to the raw sealed key, the public portion (TPM2B_PUBLIC) of
>   the sealed key is stored in 'pubkey', and the private portion
>   (TPM2B_PRIVATE) is in 'privkey'.
>
>   For more details: 
> https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html
>
> 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/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, to unseal the raw sealed key file:
>
> tpm2_key_protector_init --keyfile=(hd0,gpt1)/efi/grub/sealed-1.key
> cryptomount -u <PART1_UUID> -P tpm2
>
> tpm2_key_protector_init --keyfile=(hd0,gpt1)/efi/grub/sealed-2.key --pcrs=7,11
> cryptomount -u <PART2_UUID> -P tpm2
>
> Or, to unseal the TPM 2.0 Key file:
>
> tpm2_key_protector_init --tpm2key=(hd0,gpt1)/efi/grub/sealed-1.tpm
> cryptomount -u <PART1_UUID> -P tpm2
>
> tpm2_key_protector_init --tpm2key=(hd0,gpt1)/efi/grub/sealed-2.tpm --pcrs=7,11
> cryptomount -u <PART2_UUID> -P tpm2
>
> If a user does not initialize the key protector and attempts to use it
> anyway, the protector returns an error.
>
> Before unsealing the key, the TPM2 key protector follows the "TPMPolicy"
> sequences to enforce the TPM policy commands to construct a valid policy
> digest to unseal the key.
>
> For the TPM 2.0 Key files, 'authPolicy' may contain multiple "TPMPolicy"
> sequences, the TPM2 key protector iterates 'authPolicy' to find a valid
> sequence to unseal key. If 'authPolicy' is empty or all sequences in
> 'authPolicy' fail, the protector tries the one from 'policy'. In case
> 'policy' is also empty, the protector creates a "TPMPolicy" sequence
> based on the given PCR selection.
>
> For the raw sealed key, the TPM2 key protector treats the key file as a
> TPM 2.0 Key file without 'authPolicy' and 'policy', so the "TPMPolicy"
> sequence is always based on the PCR selection from the command
> parameters.
>
> This commit only supports one policy command: TPM2_PolicyPCR. The
> command set will be extended to support advanced features, such as
> authorized policy, in the later commits.
>
> Cc: Stefan Berger <stef...@linux.ibm.com>
> Cc: James Bottomley <j...@linux.ibm.com>
> Signed-off-by: Hernan Gatta <hega...@linux.microsoft.com>
> Signed-off-by: Gary Lin <g...@suse.com>
> ---
>  grub-core/Makefile.core.def                   |   11 +
>  grub-core/commands/tpm2_key_protector/args.c  |  127 ++
>  .../commands/tpm2_key_protector/module.c      | 1153 +++++++++++++++++
>  grub-core/commands/tpm2_key_protector/tpm2.h  |   36 +
>  .../commands/tpm2_key_protector/tpm2_args.h   |   49 +
>  .../commands/tpm2_key_protector/tpm2key.asn   |   49 +
>  .../commands/tpm2_key_protector/tpm2key.c     |  499 +++++++
>  .../commands/tpm2_key_protector/tpm2key.h     |   87 ++
>  .../tpm2_key_protector/tpm2key_asn1_tab.c     |   63 +
>  9 files changed, 2074 insertions(+)
>  create mode 100644 grub-core/commands/tpm2_key_protector/args.c
>  create mode 100644 grub-core/commands/tpm2_key_protector/module.c
>  create mode 100644 grub-core/commands/tpm2_key_protector/tpm2.h
>  create mode 100644 grub-core/commands/tpm2_key_protector/tpm2_args.h
>  create mode 100644 grub-core/commands/tpm2_key_protector/tpm2key.asn
>  create mode 100644 grub-core/commands/tpm2_key_protector/tpm2key.c
>  create mode 100644 grub-core/commands/tpm2_key_protector/tpm2key.h
>  create mode 100644 grub-core/commands/tpm2_key_protector/tpm2key_asn1_tab.c
>
> diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
> index 45b705a34..97ae4e49b 100644
> --- a/grub-core/Makefile.core.def
> +++ b/grub-core/Makefile.core.def
> @@ -2578,6 +2578,17 @@ module = {
>    cppflags = '-I$(srcdir)/lib/tss2';
>  };
>
> +module = {
> +  name = tpm2_key_protector;
> +  common = commands/tpm2_key_protector/args.c;
> +  common = commands/tpm2_key_protector/module.c;
> +  common = commands/tpm2_key_protector/tpm2key.c;
> +  common = commands/tpm2_key_protector/tpm2key_asn1_tab.c;
> +  /* The plaform support of tpm2_key_protector depends on the tcg2 
> implementation in tss2. */
> +  enable = efi;
> +  cppflags = '-I$(srcdir)/lib/tss2 -I$(srcdir)/lib/libtasn1-grub';
> +};
> +
>  module = {
>    name = tr;
>    common = commands/tr.c;
> diff --git a/grub-core/commands/tpm2_key_protector/args.c 
> b/grub-core/commands/tpm2_key_protector/args.c
> new file mode 100644
> index 000000000..5d6eab56c
> --- /dev/null
> +++ b/grub-core/commands/tpm2_key_protector/args.c
> @@ -0,0 +1,127 @@
> +/*
> + *  GRUB  --  GRand Unified Bootloader
> + *  Copyright (C) 2022 Microsoft Corporation
> + *  Copyright (C) 2024 Free Software Foundation, Inc.
> + *
> + *  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 "tpm2_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;
> +  const char *pcr_end;
> +  grub_uint64_t 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, "Empty entry in PCR list");

All grub_error() messages should start with lowercase. Of course there
are exceptions, e.g. abbreviations. Additionally, you can mark simple
error messages like this one for translation using N_() macro. Please
fix this kind of issues in all patches.

> +      if (next_pcr != NULL)
> +     *next_pcr = '\0';
> +
> +      pcr = grub_strtoul (current_pcr, &pcr_end, 10);
> +      if (*current_pcr == '\0' || *pcr_end != '\0')
> +     return grub_error (GRUB_ERR_BAD_NUMBER, "Entry '%s' in PCR list is not 
> a number", current_pcr);

Ditto...

> +
> +      if (pcr > TPM_MAX_PCRS)
> +     return grub_error (GRUB_ERR_OUT_OF_RANGE, "Entry %" PRIuGRUB_UINT64_T " 
> in PCR list is too large to be a PCR number, PCR numbers range from 0 to %u", 
> pcr, TPM_MAX_PCRS);

Ditto...

FYI, PRIuGRUB_UINT64_T et consortes do not work with N_() macro. Sadly you
have to cast variables to something long enough.

> +
> +      pcrs[i] = (grub_uint8_t) pcr;
> +      ++(*pcr_count);
> +
> +      if (next_pcr == NULL)
> +     break;
> +
> +      current_pcr = next_pcr + 1;
> +      if (*current_pcr == '\0')
> +     return grub_error (GRUB_ERR_BAD_ARGUMENT, "Trailing comma at the end of 
> PCR list");

Ditto...

> +    }
> +
> +  if (i == TPM_MAX_PCRS)
> +    return grub_error (GRUB_ERR_OUT_OF_RANGE, "Too many PCRs in PCR list, 
> the maximum number of PCRs is %u", TPM_MAX_PCRS);

Ditto...

> +
> +  return GRUB_ERR_NONE;
> +}

[...]

> diff --git a/grub-core/commands/tpm2_key_protector/tpm2_args.h 
> b/grub-core/commands/tpm2_key_protector/tpm2_args.h
> new file mode 100644
> index 000000000..7cf1ebc46
> --- /dev/null
> +++ b/grub-core/commands/tpm2_key_protector/tpm2_args.h
> @@ -0,0 +1,49 @@
> +/*
> + *  GRUB  --  GRand Unified Bootloader
> + *  Copyright (C) 2022 Microsoft Corporation
> + *  Copyright (C) 2024 Free Software Foundation, Inc.
> + *
> + *  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 "tpm2.h"
> +
> +struct grub_srk_type
> +{
> +  TPMI_ALG_PUBLIC_t type;
> +  union {
> +    TPM_KEY_BITS_t rsa_bits;
> +    TPM_ECC_CURVE_t ecc_curve;
> +  } detail;
> +};
> +typedef struct grub_srk_type grub_srk_type_t;
> +
> +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, grub_srk_type_t 
> *srk_type);
> +
> +grub_err_t
> +grub_tpm2_protector_parse_bank (const char *value, TPM_ALG_ID_t *bank);
> +
> +grub_err_t
> +grub_tpm2_protector_parse_tpm_handle (const char *value, TPM_HANDLE_t 
> *handle);

Please prefix all variables and functions declarations with extern
everywhere including other patches in this series.

If you fix all these minor issues feel free to add to this patch
Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com>.

Daniel

_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

Reply via email to