2268 lines
70 KiB
Diff
2268 lines
70 KiB
Diff
From 00dcaa44f08c7aae5d05b85c8ea751d1cd881db3 Mon Sep 17 00:00:00 2001
|
|
From: Hernan Gatta <hegatta@linux.microsoft.com>
|
|
Date: Tue, 1 Feb 2022 05:02:55 -0800
|
|
Subject: [PATCH] key_protector: Add TPM2 Key Protector
|
|
|
|
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: James Bottomley <jejb@linux.ibm.com>
|
|
Signed-off-by: Hernan Gatta <hegatta@linux.microsoft.com>
|
|
Signed-off-by: Gary Lin <glin@suse.com>
|
|
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
|
|
Reviewed-by: Stefan Berger <stefanb@linux.ibm.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
|
|
|
|
Index: grub-2.12/grub-core/Makefile.core.def
|
|
===================================================================
|
|
--- grub-2.12.orig/grub-core/Makefile.core.def
|
|
+++ grub-2.12/grub-core/Makefile.core.def
|
|
@@ -2620,6 +2620,17 @@ module = {
|
|
};
|
|
|
|
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';
|
|
+};
|
|
+
|
|
+module = {
|
|
name = tr;
|
|
common = commands/tr.c;
|
|
};
|
|
Index: grub-2.12/grub-core/commands/tpm2_key_protector/args.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ grub-2.12/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, N_("empty entry in PCR list"));
|
|
+ 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, N_("entry '%s' in PCR list is not a number"), current_pcr);
|
|
+
|
|
+ if (pcr > TPM_MAX_PCRS - 1)
|
|
+ return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("entry %llu in PCR list is too large to be a PCR number, PCR numbers range from 0 to %u"), (unsigned long long)pcr, TPM_MAX_PCRS - 1);
|
|
+
|
|
+ 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, 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,
|
|
+ grub_srk_type_t *srk_type)
|
|
+{
|
|
+ if (grub_strcasecmp (value, "ECC") == 0 ||
|
|
+ grub_strcasecmp (value, "ECC_NIST_P256") == 0)
|
|
+ {
|
|
+ srk_type->type = TPM_ALG_ECC;
|
|
+ srk_type->detail.ecc_curve = TPM_ECC_NIST_P256;
|
|
+ }
|
|
+ else if (grub_strcasecmp (value, "RSA") == 0 ||
|
|
+ grub_strcasecmp (value, "RSA2048") == 0)
|
|
+ {
|
|
+ srk_type->type = TPM_ALG_RSA;
|
|
+ srk_type->detail.rsa_bits = 2048;
|
|
+ }
|
|
+ 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_t *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 if (grub_strcasecmp (value, "SHA512") == 0)
|
|
+ *bank = TPM_ALG_SHA512;
|
|
+ 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_t *handle)
|
|
+{
|
|
+ grub_uint64_t num;
|
|
+ const char *str_end;
|
|
+
|
|
+ num = grub_strtoul (value, &str_end, 0);
|
|
+ if (*value == '\0' || *str_end != '\0')
|
|
+ return grub_error (GRUB_ERR_BAD_NUMBER, 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 %llu is too large to be a TPM handle, TPM handles are unsigned 32-bit integers"), (unsigned long long)num);
|
|
+
|
|
+ *handle = (TPM_HANDLE_t) num;
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
Index: grub-2.12/grub-core/commands/tpm2_key_protector/module.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ grub-2.12/grub-core/commands/tpm2_key_protector/module.c
|
|
@@ -0,0 +1,1153 @@
|
|
+/*
|
|
+ * 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/dl.h>
|
|
+#include <grub/extcmd.h>
|
|
+#include <grub/file.h>
|
|
+#include <grub/list.h>
|
|
+#include <grub/misc.h>
|
|
+#include <grub/mm.h>
|
|
+#include <grub/key_protector.h>
|
|
+
|
|
+#include <tss2_buffer.h>
|
|
+#include <tss2_types.h>
|
|
+#include <tss2_mu.h>
|
|
+
|
|
+#include "tpm2_args.h"
|
|
+#include "tpm2.h"
|
|
+#include "tpm2key.h"
|
|
+
|
|
+GRUB_MOD_LICENSE ("GPLv3+");
|
|
+
|
|
+typedef enum tpm2_protector_mode
|
|
+{
|
|
+ TPM2_PROTECTOR_MODE_UNSET,
|
|
+ TPM2_PROTECTOR_MODE_SRK,
|
|
+ TPM2_PROTECTOR_MODE_NV
|
|
+} tpm2_protector_mode_t;
|
|
+
|
|
+typedef enum tpm2_protector_options
|
|
+{
|
|
+ OPTION_MODE,
|
|
+ OPTION_PCRS,
|
|
+ OPTION_BANK,
|
|
+ OPTION_TPM2KEY,
|
|
+ OPTION_KEYFILE,
|
|
+ OPTION_SRK,
|
|
+ OPTION_ASYMMETRIC,
|
|
+ OPTION_NVINDEX
|
|
+} tpm2_protector_options_t;
|
|
+
|
|
+typedef struct tpm2_protector_context
|
|
+{
|
|
+ tpm2_protector_mode_t mode;
|
|
+ grub_uint8_t pcrs[TPM_MAX_PCRS];
|
|
+ grub_uint8_t pcr_count;
|
|
+ grub_srk_type_t srk_type;
|
|
+ TPM_ALG_ID_t bank;
|
|
+ const char *tpm2key;
|
|
+ const char *keyfile;
|
|
+ TPM_HANDLE_t srk;
|
|
+ TPM_HANDLE_t nv;
|
|
+} tpm2_protector_context_t;
|
|
+
|
|
+static const struct grub_arg_option 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: 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, SHA384 or SHA512. (default: SHA256)"),
|
|
+ },
|
|
+ /* SRK-mode options */
|
|
+ {
|
|
+ .longarg = "tpm2key",
|
|
+ .shortarg = 'T',
|
|
+ .flags = 0,
|
|
+ .arg = NULL,
|
|
+ .type = ARG_TYPE_STRING,
|
|
+ .doc =
|
|
+ N_("In SRK mode, path to the key file in the TPM 2.0 Key File format "
|
|
+ "to unseal using the TPM (e.g., (hd0,gpt1)/boot/grub2/sealed.tpm)."),
|
|
+ },
|
|
+ {
|
|
+ .longarg = "keyfile",
|
|
+ .shortarg = 'k',
|
|
+ .flags = 0,
|
|
+ .arg = NULL,
|
|
+ .type = ARG_TYPE_STRING,
|
|
+ .doc =
|
|
+ N_("In SRK mode, path to the key file in the raw format to unseal "
|
|
+ "using the TPM (e.g., (hd0,gpt1)/boot/grub2/sealed.key). "
|
|
+ "(Mainly for backward compatibility. Please use '--tpm2key'.)"),
|
|
+ },
|
|
+ {
|
|
+ .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."),
|
|
+ },
|
|
+ {
|
|
+ .longarg = "asymmetric",
|
|
+ .shortarg = 'a',
|
|
+ .flags = 0,
|
|
+ .arg = NULL,
|
|
+ .type = ARG_TYPE_STRING,
|
|
+ .doc =
|
|
+ N_("In SRK mode, the type of SRK: RSA (RSA2048) and ECC (ECC_NIST_P256)"
|
|
+ "(default: 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 tpm2_protector_init_cmd;
|
|
+static grub_extcmd_t tpm2_protector_clear_cmd;
|
|
+static tpm2_protector_context_t tpm2_protector_ctx = {0};
|
|
+
|
|
+static grub_err_t
|
|
+tpm2_protector_srk_read_file (const char *filepath, void **buffer, grub_size_t *buffer_size)
|
|
+{
|
|
+ grub_file_t file;
|
|
+ grub_off_t file_size;
|
|
+ void *read_buffer;
|
|
+ grub_off_t read_n;
|
|
+ grub_err_t err;
|
|
+
|
|
+ /*
|
|
+ * Using GRUB_FILE_TYPE_SIGNATURE ensures we do not hash the keyfile into PCR9
|
|
+ * otherwise we'll never be able to predict the value of PCR9 at unseal time
|
|
+ */
|
|
+ file = grub_file_open (filepath, GRUB_FILE_TYPE_SIGNATURE);
|
|
+ if (file == NULL)
|
|
+ {
|
|
+ /* Push errno from grub_file_open() into the error message stack */
|
|
+ grub_error_push();
|
|
+ err = grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("could not open file: %s"), filepath);
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ file_size = grub_file_size (file);
|
|
+ if (file_size == 0)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_OUT_OF_RANGE, N_("could not read file size: %s"), filepath);
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ read_buffer = grub_malloc (file_size);
|
|
+ if (read_buffer == NULL)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("could not allocate buffer for %s"), filepath);
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ read_n = grub_file_read (file, read_buffer, file_size);
|
|
+ if (read_n != file_size)
|
|
+ {
|
|
+ grub_free (read_buffer);
|
|
+ err = grub_error (GRUB_ERR_FILE_READ_ERROR, N_("could not retrieve file contents: %s"), filepath);
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ *buffer = read_buffer;
|
|
+ *buffer_size = file_size;
|
|
+
|
|
+ err = GRUB_ERR_NONE;
|
|
+
|
|
+ error:
|
|
+ if (file != NULL)
|
|
+ grub_file_close (file);
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+tpm2_protector_srk_unmarshal_keyfile (void *sealed_key,
|
|
+ grub_size_t sealed_key_size,
|
|
+ tpm2_sealed_key_t *sk)
|
|
+{
|
|
+ struct grub_tpm2_buffer buf;
|
|
+
|
|
+ grub_tpm2_buffer_init (&buf);
|
|
+ if (sealed_key_size > buf.cap)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("sealed key larger than %llu bytes"), (unsigned long long)buf.cap);
|
|
+
|
|
+ grub_memcpy (buf.data, sealed_key, sealed_key_size);
|
|
+ buf.size = sealed_key_size;
|
|
+
|
|
+ grub_Tss2_MU_TPM2B_PUBLIC_Unmarshal (&buf, &sk->public);
|
|
+ grub_Tss2_MU_TPM2B_PRIVATE_Unmarshal (&buf, &sk->private);
|
|
+
|
|
+ if (buf.error != 0)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("malformed TPM wire key file"));
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+tpm2_protector_srk_unmarshal_tpm2key (void *sealed_key,
|
|
+ grub_size_t sealed_key_size,
|
|
+ tpm2key_policy_t *policy_seq,
|
|
+ tpm2key_authpolicy_t *authpol_seq,
|
|
+ grub_uint8_t *rsaparent,
|
|
+ grub_uint32_t *parent,
|
|
+ tpm2_sealed_key_t *sk)
|
|
+{
|
|
+ asn1_node tpm2key = NULL;
|
|
+ grub_uint8_t rsaparent_tmp;
|
|
+ grub_uint32_t parent_tmp;
|
|
+ void *sealed_pub = NULL;
|
|
+ grub_size_t sealed_pub_size;
|
|
+ void *sealed_priv = NULL;
|
|
+ grub_size_t sealed_priv_size;
|
|
+ struct grub_tpm2_buffer buf;
|
|
+ grub_err_t err;
|
|
+
|
|
+ /*
|
|
+ * Start to parse the tpm2key file
|
|
+ * 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
|
|
+ * }
|
|
+ */
|
|
+ err = grub_tpm2key_start_parsing (&tpm2key, sealed_key, sealed_key_size);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ return err;
|
|
+
|
|
+ /*
|
|
+ * Retrieve the policy sequence from 'policy'
|
|
+ * policy_seq will be NULL when 'policy' is not available
|
|
+ */
|
|
+ err = grub_tpm2key_get_policy_seq (tpm2key, policy_seq);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ goto error;
|
|
+
|
|
+ /*
|
|
+ * Retrieve the authpolicy sequence from 'authPolicy'
|
|
+ * authpol_seq will be NULL when 'authPolicy' is not available
|
|
+ */
|
|
+ err = grub_tpm2key_get_authpolicy_seq (tpm2key, authpol_seq);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ goto error;
|
|
+
|
|
+ /* Retrieve rsaParent */
|
|
+ err = grub_tpm2key_get_rsaparent (tpm2key, &rsaparent_tmp);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ goto error;
|
|
+
|
|
+ *rsaparent = rsaparent_tmp;
|
|
+
|
|
+ /* Retrieve the parent handle */
|
|
+ err = grub_tpm2key_get_parent (tpm2key, &parent_tmp);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ goto error;
|
|
+
|
|
+ /* The parent handle should be either PERMANENT or PERSISTENT. */
|
|
+ if (!TPM_HT_IS_PERMANENT (parent_tmp) && !TPM_HT_IS_PERSISTENT (parent_tmp))
|
|
+ {
|
|
+ err = GRUB_ERR_OUT_OF_RANGE;
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ *parent = parent_tmp;
|
|
+
|
|
+ /* Retrieve the public part of the sealed key */
|
|
+ err = grub_tpm2key_get_pubkey (tpm2key, &sealed_pub, &sealed_pub_size);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ goto error;
|
|
+
|
|
+ /* Retrieve the private part of the sealed key */
|
|
+ err = grub_tpm2key_get_privkey (tpm2key, &sealed_priv, &sealed_priv_size);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ goto error;
|
|
+
|
|
+ /* Unmarshal the sealed key */
|
|
+ grub_tpm2_buffer_init (&buf);
|
|
+ if (sealed_pub_size + sealed_priv_size > buf.cap)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("sealed key larger than %llu bytes"), (unsigned long long)buf.cap);
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ grub_tpm2_buffer_pack (&buf, sealed_pub, sealed_pub_size);
|
|
+ grub_tpm2_buffer_pack (&buf, sealed_priv, sealed_priv_size);
|
|
+
|
|
+ buf.offset = 0;
|
|
+
|
|
+ grub_Tss2_MU_TPM2B_PUBLIC_Unmarshal (&buf, &sk->public);
|
|
+ grub_Tss2_MU_TPM2B_PRIVATE_Unmarshal (&buf, &sk->private);
|
|
+
|
|
+ if (buf.error != 0)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("malformed TPM 2.0 key file"));
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ err = GRUB_ERR_NONE;
|
|
+
|
|
+ error:
|
|
+ /* End the parsing */
|
|
+ grub_tpm2key_end_parsing (tpm2key);
|
|
+ grub_free (sealed_pub);
|
|
+ grub_free (sealed_priv);
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+/* Check if the SRK exists in the specified handle */
|
|
+static grub_err_t
|
|
+tpm2_protector_srk_check (const TPM_HANDLE_t srk_handle)
|
|
+{
|
|
+ TPM_RC_t rc;
|
|
+ TPM2B_PUBLIC_t public;
|
|
+
|
|
+ /* Find SRK */
|
|
+ rc = grub_tpm2_readpublic (srk_handle, NULL, &public);
|
|
+ if (rc == TPM_RC_SUCCESS)
|
|
+ return GRUB_ERR_NONE;
|
|
+
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "failed to retrieve SRK from 0x%x (TPM2_ReadPublic: 0x%x)", srk_handle, rc);
|
|
+}
|
|
+
|
|
+/* Get the SRK with the template */
|
|
+static grub_err_t
|
|
+tpm2_protector_srk_get (const grub_srk_type_t srk_type,
|
|
+ const TPM_HANDLE_t parent,
|
|
+ TPM_HANDLE_t *srk_handle)
|
|
+{
|
|
+ TPM_RC_t rc;
|
|
+ TPMT_PUBLIC_PARMS_t parms = {0};
|
|
+ TPMS_AUTH_COMMAND_t authCommand = {0};
|
|
+ TPM2B_SENSITIVE_CREATE_t inSensitive = {0};
|
|
+ TPM2B_PUBLIC_t inPublic = {0};
|
|
+ TPM2B_DATA_t outsideInfo = {0};
|
|
+ TPML_PCR_SELECTION_t creationPcr = {0};
|
|
+ TPM2B_PUBLIC_t outPublic = {0};
|
|
+ TPM2B_CREATION_DATA_t creationData = {0};
|
|
+ TPM2B_DIGEST_t creationHash = {0};
|
|
+ TPMT_TK_CREATION_t creationTicket = {0};
|
|
+ TPM2B_NAME_t srkName = {0};
|
|
+ TPM_HANDLE_t tmp_handle = 0;
|
|
+
|
|
+ inPublic.publicArea.type = srk_type.type;
|
|
+ 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 (srk_type.type == 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 = srk_type.detail.rsa_bits;
|
|
+ inPublic.publicArea.parameters.rsaDetail.exponent = 0;
|
|
+ }
|
|
+ else if (srk_type.type == 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 = srk_type.detail.ecc_curve;
|
|
+ inPublic.publicArea.parameters.eccDetail.kdf.scheme = TPM_ALG_NULL;
|
|
+ }
|
|
+ else
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown SRK algorithm");
|
|
+
|
|
+ /* Test the parameters before SRK generation */
|
|
+ parms.type = srk_type.type;
|
|
+ grub_memcpy (&parms.parameters, &inPublic.publicArea.parameters,
|
|
+ sizeof (TPMU_PUBLIC_PARMS_t));
|
|
+
|
|
+ rc = grub_tpm2_testparms (&parms, NULL);
|
|
+ if (rc != TPM_RC_SUCCESS)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "unsupported SRK template (TPM2_TestParms: 0x%x)", rc);
|
|
+
|
|
+ /* Create SRK */
|
|
+ authCommand.sessionHandle = TPM_RS_PW;
|
|
+ rc = grub_tpm2_createprimary (parent, &authCommand, &inSensitive, &inPublic,
|
|
+ &outsideInfo, &creationPcr, &tmp_handle, &outPublic,
|
|
+ &creationData, &creationHash, &creationTicket,
|
|
+ &srkName, NULL);
|
|
+ if (rc != TPM_RC_SUCCESS)
|
|
+ return grub_error (GRUB_ERR_BAD_DEVICE, "could not create SRK (TPM2_CreatePrimary: 0x%x)", rc);
|
|
+
|
|
+ *srk_handle = tmp_handle;
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Load the SRK from the persistent handle or create one with a given type of
|
|
+ * template, and then associate the sealed key with the SRK
|
|
+ * Return values:
|
|
+ * - GRUB_ERR_NONE: Everything is fine.
|
|
+ * - GRUB_ERR_BAD_ARGUMENT: The SRK doesn't match. Try another one.
|
|
+ * - Other: Something went wrong.
|
|
+ */
|
|
+static grub_err_t
|
|
+tpm2_protector_srk_load (const grub_srk_type_t srk_type,
|
|
+ const tpm2_sealed_key_t *sealed_key,
|
|
+ const TPM_HANDLE_t parent,
|
|
+ TPM_HANDLE_t *sealed_handle,
|
|
+ TPM_HANDLE_t *srk_handle)
|
|
+{
|
|
+ TPMS_AUTH_COMMAND_t authCmd = {0};
|
|
+ TPM2B_NAME_t name = {0};
|
|
+ TPM_RC_t rc;
|
|
+ grub_err_t err;
|
|
+
|
|
+ if (srk_handle == NULL)
|
|
+ return GRUB_ERR_BUG;
|
|
+
|
|
+ if (*srk_handle != 0)
|
|
+ {
|
|
+ err = tpm2_protector_srk_check (*srk_handle);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ return err;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ err = tpm2_protector_srk_get (srk_type, parent, srk_handle);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ /* Load the sealed key and associate it with the SRK */
|
|
+ authCmd.sessionHandle = TPM_RS_PW;
|
|
+ rc = grub_tpm2_load (*srk_handle, &authCmd, &sealed_key->private, &sealed_key->public,
|
|
+ sealed_handle, &name, NULL);
|
|
+ /*
|
|
+ * If TPM2_Load returns (TPM_RC_INTEGRITY | TPM_RC_P | TPM_RC_1), then it
|
|
+ * implies the wrong SRK is used.
|
|
+ */
|
|
+ if (rc == (TPM_RC_INTEGRITY | TPM_RC_P | TPM_RC_1))
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_BAD_ARGUMENT, "SRK not matched");
|
|
+ goto error;
|
|
+ }
|
|
+ else if (rc != TPM_RC_SUCCESS)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_BAD_DEVICE, "failed to load sealed key (TPM2_Load: 0x%x)", rc);
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+
|
|
+ error:
|
|
+ if (!TPM_HT_IS_PERSISTENT (*srk_handle))
|
|
+ grub_tpm2_flushcontext (*srk_handle);
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static const char *
|
|
+srk_type_to_name (grub_srk_type_t srk_type)
|
|
+{
|
|
+ if (srk_type.type == TPM_ALG_ECC && srk_type.detail.ecc_curve == TPM_ECC_NIST_P256)
|
|
+ return "ECC_NIST_P256";
|
|
+ else if (srk_type.type == TPM_ALG_RSA && srk_type.detail.rsa_bits == 2048)
|
|
+ return "RSA2048";
|
|
+
|
|
+ return "Unknown";
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+tpm2_protector_load_key (const tpm2_protector_context_t *ctx,
|
|
+ const tpm2_sealed_key_t *sealed_key,
|
|
+ const TPM_HANDLE_t parent_handle,
|
|
+ TPM_HANDLE_t *sealed_handle,
|
|
+ TPM_HANDLE_t *srk_handle)
|
|
+{
|
|
+ grub_err_t err;
|
|
+ int i;
|
|
+ grub_srk_type_t fallback_srks[] = {
|
|
+ {
|
|
+ .type = TPM_ALG_ECC,
|
|
+ .detail.ecc_curve = TPM_ECC_NIST_P256,
|
|
+ },
|
|
+ {
|
|
+ .type = TPM_ALG_RSA,
|
|
+ .detail.rsa_bits = 2048,
|
|
+ },
|
|
+ {
|
|
+ .type = TPM_ALG_ERROR,
|
|
+ }
|
|
+ };
|
|
+
|
|
+ /* Try the given persistent SRK if exists */
|
|
+ if (*srk_handle != 0)
|
|
+ {
|
|
+ err = tpm2_protector_srk_load (ctx->srk_type, sealed_key,
|
|
+ parent_handle, sealed_handle,
|
|
+ srk_handle);
|
|
+ if (err != GRUB_ERR_BAD_ARGUMENT)
|
|
+ return err;
|
|
+
|
|
+ grub_print_error ();
|
|
+ grub_printf ("Trying the specified SRK algorithm: %s\n", srk_type_to_name (ctx->srk_type));
|
|
+ grub_errno = GRUB_ERR_NONE;
|
|
+ *srk_handle = 0;
|
|
+ }
|
|
+
|
|
+ /* Try the specified algorithm for the SRK template */
|
|
+ if (*srk_handle == 0)
|
|
+ {
|
|
+ err = tpm2_protector_srk_load (ctx->srk_type, sealed_key,
|
|
+ parent_handle, sealed_handle,
|
|
+ srk_handle);
|
|
+ if (err != GRUB_ERR_BAD_ARGUMENT)
|
|
+ return err;
|
|
+
|
|
+ grub_print_error ();
|
|
+ grub_errno = GRUB_ERR_NONE;
|
|
+ *srk_handle = 0;
|
|
+ }
|
|
+
|
|
+ /* Try all the fallback SRK templates */
|
|
+ for (i = 0; fallback_srks[i].type != TPM_ALG_ERROR; i++)
|
|
+ {
|
|
+ /* Skip the specified algorithm */
|
|
+ if (fallback_srks[i].type == ctx->srk_type.type &&
|
|
+ (fallback_srks[i].detail.rsa_bits == ctx->srk_type.detail.rsa_bits ||
|
|
+ fallback_srks[i].detail.ecc_curve == ctx->srk_type.detail.ecc_curve))
|
|
+ continue;
|
|
+
|
|
+ grub_printf ("Trying fallback %s template\n", srk_type_to_name (fallback_srks[i]));
|
|
+
|
|
+ *srk_handle = 0;
|
|
+
|
|
+ err = tpm2_protector_srk_load (fallback_srks[i], sealed_key,
|
|
+ parent_handle, sealed_handle,
|
|
+ srk_handle);
|
|
+ if (err != GRUB_ERR_BAD_ARGUMENT)
|
|
+ return err;
|
|
+
|
|
+ grub_print_error ();
|
|
+ grub_errno = GRUB_ERR_NONE;
|
|
+ }
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+tpm2_protector_policypcr (TPMI_SH_AUTH_SESSION_t session, struct grub_tpm2_buffer *cmd_buf)
|
|
+{
|
|
+ TPM2B_DIGEST_t pcr_digest;
|
|
+ TPML_PCR_SELECTION_t pcr_sel;
|
|
+ TPM_RC_t rc;
|
|
+
|
|
+ grub_Tss2_MU_TPM2B_DIGEST_Unmarshal (cmd_buf, &pcr_digest);
|
|
+ grub_Tss2_MU_TPML_PCR_SELECTION_Unmarshal (cmd_buf, &pcr_sel);
|
|
+ if (cmd_buf->error != 0)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "failed to unmarshal commandPolicy for TPM2_PolicyPCR");
|
|
+
|
|
+ rc = grub_tpm2_policypcr (session, NULL, &pcr_digest, &pcr_sel, NULL);
|
|
+ if (rc != TPM_RC_SUCCESS)
|
|
+ return grub_error (GRUB_ERR_BAD_DEVICE, "failed to submit PCR policy (TPM2_PolicyPCR: 0x%x)", rc);
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+tpm2_protector_enforce_policy (tpm2key_policy_t policy, TPMI_SH_AUTH_SESSION_t session)
|
|
+{
|
|
+ struct grub_tpm2_buffer buf;
|
|
+ grub_err_t err;
|
|
+
|
|
+ grub_tpm2_buffer_init (&buf);
|
|
+ if (policy->cmd_policy_len > buf.cap)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "CommandPolicy larger than TPM buffer");
|
|
+
|
|
+ grub_memcpy (buf.data, policy->cmd_policy, policy->cmd_policy_len);
|
|
+ buf.size = policy->cmd_policy_len;
|
|
+
|
|
+ switch (policy->cmd_code)
|
|
+ {
|
|
+ case TPM_CC_PolicyPCR:
|
|
+ err = tpm2_protector_policypcr (session, &buf);
|
|
+ break;
|
|
+ default:
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown TPM Command: 0x%x", policy->cmd_code);
|
|
+ }
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+tpm2_protector_enforce_policy_seq (tpm2key_policy_t policy_seq, TPMI_SH_AUTH_SESSION_t session)
|
|
+{
|
|
+ tpm2key_policy_t policy;
|
|
+ grub_err_t err;
|
|
+
|
|
+ FOR_LIST_ELEMENTS (policy, policy_seq)
|
|
+ {
|
|
+ err = tpm2_protector_enforce_policy (policy, session);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+tpm2_protector_simple_policy_seq (const tpm2_protector_context_t *ctx,
|
|
+ tpm2key_policy_t *policy_seq)
|
|
+{
|
|
+ tpm2key_policy_t policy = NULL;
|
|
+ struct grub_tpm2_buffer buf;
|
|
+ TPML_PCR_SELECTION_t pcr_sel = {
|
|
+ .count = 1,
|
|
+ .pcrSelections = {
|
|
+ {
|
|
+ .hash = ctx->bank,
|
|
+ .sizeOfSelect = 3,
|
|
+ .pcrSelect = {0}
|
|
+ },
|
|
+ }
|
|
+ };
|
|
+ grub_uint8_t i;
|
|
+ grub_err_t err;
|
|
+
|
|
+ if (policy_seq == NULL)
|
|
+ return GRUB_ERR_BAD_ARGUMENT;
|
|
+
|
|
+ grub_tpm2_buffer_init (&buf);
|
|
+
|
|
+ for (i = 0; i < ctx->pcr_count; i++)
|
|
+ TPMS_PCR_SELECTION_SelectPCR (&pcr_sel.pcrSelections[0], ctx->pcrs[i]);
|
|
+
|
|
+ grub_tpm2_buffer_pack_u16 (&buf, 0);
|
|
+ grub_Tss2_MU_TPML_PCR_SELECTION_Marshal (&buf, &pcr_sel);
|
|
+
|
|
+ if (buf.error != 0)
|
|
+ return GRUB_ERR_BAD_ARGUMENT;
|
|
+
|
|
+ policy = grub_malloc (sizeof(struct tpm2key_policy));
|
|
+ if (policy == NULL)
|
|
+ {
|
|
+ err = GRUB_ERR_OUT_OF_MEMORY;
|
|
+ goto error;
|
|
+ }
|
|
+ policy->cmd_code = TPM_CC_PolicyPCR;
|
|
+ policy->cmd_policy = grub_malloc (buf.size);
|
|
+ if (policy->cmd_policy == NULL)
|
|
+ {
|
|
+ err = GRUB_ERR_OUT_OF_MEMORY;
|
|
+ goto error;
|
|
+ }
|
|
+ grub_memcpy (policy->cmd_policy, buf.data, buf.size);
|
|
+ policy->cmd_policy_len = buf.size;
|
|
+
|
|
+ grub_list_push (GRUB_AS_LIST_P (policy_seq), GRUB_AS_LIST (policy));
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+
|
|
+ error:
|
|
+ grub_free (policy);
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+tpm2_protector_unseal (tpm2key_policy_t policy_seq, TPM_HANDLE_t sealed_handle,
|
|
+ grub_uint8_t **key, grub_size_t *key_size)
|
|
+{
|
|
+ TPMS_AUTH_COMMAND_t authCmd = {0};
|
|
+ TPM2B_SENSITIVE_DATA_t data;
|
|
+ TPM2B_NONCE_t nonceCaller = {0};
|
|
+ TPMT_SYM_DEF_t symmetric = {0};
|
|
+ TPMI_SH_AUTH_SESSION_t session;
|
|
+ grub_uint8_t *key_out;
|
|
+ TPM_RC_t rc;
|
|
+ grub_err_t err;
|
|
+
|
|
+ /* Start Auth Session */
|
|
+ nonceCaller.size = TPM_SHA256_DIGEST_SIZE;
|
|
+ symmetric.algorithm = TPM_ALG_NULL;
|
|
+ rc = grub_tpm2_startauthsession (TPM_RH_NULL, TPM_RH_NULL, NULL, &nonceCaller, NULL,
|
|
+ TPM_SE_POLICY, &symmetric, TPM_ALG_SHA256,
|
|
+ &session, NULL, NULL);
|
|
+ if (rc != TPM_RC_SUCCESS)
|
|
+ return grub_error (GRUB_ERR_BAD_DEVICE, "failed to start auth session (TPM2_StartAuthSession: 0x%x)", rc);
|
|
+
|
|
+ /* Enforce the policy command sequence */
|
|
+ err = tpm2_protector_enforce_policy_seq (policy_seq, session);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ goto error;
|
|
+
|
|
+ /* Unseal Sealed Key */
|
|
+ authCmd.sessionHandle = session;
|
|
+ rc = grub_tpm2_unseal (sealed_handle, &authCmd, &data, NULL);
|
|
+ if (rc != TPM_RC_SUCCESS)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_BAD_DEVICE, "failed to unseal sealed key (TPM2_Unseal: 0x%x)", rc);
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ /* Epilogue */
|
|
+ key_out = grub_malloc (data.size);
|
|
+ if (key_out == NULL)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("no memory left to allocate unlock key buffer"));
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ grub_memcpy (key_out, data.buffer, data.size);
|
|
+
|
|
+ *key = key_out;
|
|
+ *key_size = data.size;
|
|
+
|
|
+ err = GRUB_ERR_NONE;
|
|
+
|
|
+ error:
|
|
+ grub_tpm2_flushcontext (session);
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+tpm2_protector_srk_recover (const tpm2_protector_context_t *ctx,
|
|
+ grub_uint8_t **key, grub_size_t *key_size)
|
|
+{
|
|
+ tpm2_sealed_key_t sealed_key = {0};
|
|
+ void *file_bytes = NULL;
|
|
+ grub_size_t file_size = 0;
|
|
+ grub_uint8_t rsaparent = 0;
|
|
+ TPM_HANDLE_t parent_handle = 0;
|
|
+ TPM_HANDLE_t srk_handle = 0;
|
|
+ TPM_HANDLE_t sealed_handle = 0;
|
|
+ tpm2key_policy_t policy_seq = NULL;
|
|
+ tpm2key_authpolicy_t authpol = NULL;
|
|
+ tpm2key_authpolicy_t authpol_seq = NULL;
|
|
+ grub_err_t err;
|
|
+
|
|
+ /*
|
|
+ * Retrieve sealed key, parent handle, policy sequence, and authpolicy
|
|
+ * sequence from the key file
|
|
+ */
|
|
+ if (ctx->tpm2key != NULL)
|
|
+ {
|
|
+ err = tpm2_protector_srk_read_file (ctx->tpm2key, &file_bytes,
|
|
+ &file_size);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ return err;
|
|
+
|
|
+ err = tpm2_protector_srk_unmarshal_tpm2key (file_bytes,
|
|
+ file_size,
|
|
+ &policy_seq,
|
|
+ &authpol_seq,
|
|
+ &rsaparent,
|
|
+ &parent_handle,
|
|
+ &sealed_key);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ goto exit1;
|
|
+
|
|
+ if (rsaparent == 1)
|
|
+ {
|
|
+ tpm2_protector_context_t *ctx_w;
|
|
+
|
|
+ /* Overwrite the SRK type as noted in the key */
|
|
+ ctx_w = (tpm2_protector_context_t *)ctx;
|
|
+ ctx_w->srk_type.type = TPM_ALG_RSA;
|
|
+ ctx_w->srk_type.detail.rsa_bits = 2048;
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ err = tpm2_protector_srk_read_file (ctx->keyfile, &file_bytes, &file_size);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ return err;
|
|
+
|
|
+ parent_handle = TPM_RH_OWNER;
|
|
+ err = tpm2_protector_srk_unmarshal_keyfile (file_bytes, file_size, &sealed_key);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ goto exit1;
|
|
+ }
|
|
+
|
|
+ /* Set the SRK handle if it is specified with '--srk' or inside the key file */
|
|
+ if (ctx->srk != 0)
|
|
+ srk_handle = ctx->srk;
|
|
+ else if (TPM_HT_IS_PERSISTENT (parent_handle))
|
|
+ srk_handle = parent_handle;
|
|
+
|
|
+ /* Load the sealed key into TPM and associate it with the SRK */
|
|
+ err = tpm2_protector_load_key (ctx, &sealed_key, parent_handle, &sealed_handle, &srk_handle);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ goto exit1;
|
|
+
|
|
+ /*
|
|
+ * Set err to an error code to trigger the standalone policy sequence
|
|
+ * if there is no authpolicy sequence
|
|
+ */
|
|
+ err = GRUB_ERR_READ_ERROR;
|
|
+
|
|
+ /* Iterate the authpolicy sequence to find one that unseals the key */
|
|
+ FOR_LIST_ELEMENTS (authpol, authpol_seq)
|
|
+ {
|
|
+ err = tpm2_protector_unseal (authpol->policy_seq, sealed_handle, key, key_size);
|
|
+ if (err == GRUB_ERR_NONE)
|
|
+ break;
|
|
+
|
|
+ /*
|
|
+ * Push the error message into the grub_error stack
|
|
+ * Note: The grub_error stack may overflow if there are too many policy
|
|
+ * sequences. Anyway, we still can keep the error messages from
|
|
+ * the first few policy sequences which are usually most likely to
|
|
+ * unseal the key.
|
|
+ */
|
|
+ grub_error_push();
|
|
+ }
|
|
+
|
|
+ /* Give the standalone policy sequence a try */
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ {
|
|
+ /*
|
|
+ * Create a basic policy sequence based on the given PCR selection if the
|
|
+ * key file doesn't provide one
|
|
+ */
|
|
+ if (policy_seq == NULL)
|
|
+ {
|
|
+ err = tpm2_protector_simple_policy_seq (ctx, &policy_seq);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ goto exit2;
|
|
+ }
|
|
+
|
|
+ err = tpm2_protector_unseal (policy_seq, sealed_handle, key, key_size);
|
|
+ }
|
|
+
|
|
+ /* Pop error messages on success */
|
|
+ if (err == GRUB_ERR_NONE)
|
|
+ while (grub_error_pop ());
|
|
+
|
|
+ exit2:
|
|
+ grub_tpm2_flushcontext (sealed_handle);
|
|
+
|
|
+ if (!TPM_HT_IS_PERSISTENT (srk_handle))
|
|
+ grub_tpm2_flushcontext (srk_handle);
|
|
+
|
|
+ exit1:
|
|
+ grub_tpm2key_free_policy_seq (policy_seq);
|
|
+ grub_tpm2key_free_authpolicy_seq (authpol_seq);
|
|
+ grub_free (file_bytes);
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+tpm2_protector_nv_recover (const tpm2_protector_context_t *ctx __attribute__ ((unused)),
|
|
+ grub_uint8_t **key __attribute__ ((unused)),
|
|
+ grub_size_t *key_size __attribute__ ((unused)))
|
|
+{
|
|
+ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "NV Index mode is not implemented yet");
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+tpm2_protector_recover (const tpm2_protector_context_t *ctx,
|
|
+ grub_uint8_t **key, grub_size_t *key_size)
|
|
+{
|
|
+ switch (ctx->mode)
|
|
+ {
|
|
+ case TPM2_PROTECTOR_MODE_SRK:
|
|
+ return tpm2_protector_srk_recover (ctx, key, key_size);
|
|
+ case TPM2_PROTECTOR_MODE_NV:
|
|
+ return tpm2_protector_nv_recover (ctx, key, key_size);
|
|
+ default:
|
|
+ return GRUB_ERR_BAD_ARGUMENT;
|
|
+ }
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+tpm2_protector_recover_key (grub_uint8_t **key, grub_size_t *key_size)
|
|
+{
|
|
+ /* Expect a call to tpm2_protector_init before anybody tries to use us */
|
|
+ if (tpm2_protector_ctx.mode == 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 == NULL || key_size == NULL)
|
|
+ return GRUB_ERR_BAD_ARGUMENT;
|
|
+
|
|
+ return tpm2_protector_recover (&tpm2_protector_ctx, key, key_size);
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+tpm2_protector_check_args (tpm2_protector_context_t *ctx)
|
|
+{
|
|
+ if (ctx->mode == TPM2_PROTECTOR_MODE_UNSET)
|
|
+ ctx->mode = TPM2_PROTECTOR_MODE_SRK;
|
|
+
|
|
+ /* Checks for SRK mode */
|
|
+ if (ctx->mode == TPM2_PROTECTOR_MODE_SRK &&
|
|
+ (ctx->keyfile == NULL && ctx->tpm2key == NULL))
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("in SRK mode, a key file must be specified: --tpm2key/-T or --keyfile/-k"));
|
|
+
|
|
+ if (ctx->mode == TPM2_PROTECTOR_MODE_SRK &&
|
|
+ (ctx->keyfile != NULL && ctx->tpm2key != NULL))
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("in SRK mode, please specify a key file with only --tpm2key/-T or --keyfile/-k"));
|
|
+
|
|
+ if (ctx->mode == TPM2_PROTECTOR_MODE_SRK && ctx->nv != 0)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("in SRK mode, an NV Index cannot be specified"));
|
|
+
|
|
+ /* Checks for NV mode */
|
|
+ if (ctx->mode == TPM2_PROTECTOR_MODE_NV && ctx->nv == 0)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("in NV Index mode, an NV Index must be specified: --nvindex or -n"));
|
|
+
|
|
+ if (ctx->mode == TPM2_PROTECTOR_MODE_NV &&
|
|
+ (ctx->tpm2key != NULL || ctx->keyfile != NULL))
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("in NV Index mode, a keyfile cannot be specified"));
|
|
+
|
|
+ if (ctx->mode == TPM2_PROTECTOR_MODE_NV && ctx->srk != 0)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("in NV Index mode, an SRK cannot be specified"));
|
|
+
|
|
+ if (ctx->mode == TPM2_PROTECTOR_MODE_NV &&
|
|
+ ctx->srk_type.type != TPM_ALG_ERROR)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("in NV Index mode, an asymmetric key type cannot be specified"));
|
|
+
|
|
+ /* Defaults assignment */
|
|
+ if (ctx->bank == TPM_ALG_ERROR)
|
|
+ ctx->bank = TPM_ALG_SHA256;
|
|
+
|
|
+ if (ctx->pcr_count == 0)
|
|
+ {
|
|
+ ctx->pcrs[0] = 7;
|
|
+ ctx->pcr_count = 1;
|
|
+ }
|
|
+
|
|
+ if (ctx->mode == TPM2_PROTECTOR_MODE_SRK &&
|
|
+ ctx->srk_type.type == TPM_ALG_ERROR)
|
|
+ {
|
|
+ ctx->srk_type.type = TPM_ALG_ECC;
|
|
+ ctx->srk_type.detail.ecc_curve = TPM_ECC_NIST_P256;
|
|
+ }
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+tpm2_protector_parse_file (const char *value, const char **file)
|
|
+{
|
|
+ if (grub_strlen (value) == 0)
|
|
+ return GRUB_ERR_BAD_ARGUMENT;
|
|
+
|
|
+ *file = grub_strdup (value);
|
|
+ if (*file == NULL)
|
|
+ return grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("no memory to duplicate file path"));
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+tpm2_protector_parse_mode (const char *value, tpm2_protector_mode_t *mode)
|
|
+{
|
|
+ if (grub_strcmp (value, "srk") == 0)
|
|
+ *mode = TPM2_PROTECTOR_MODE_SRK;
|
|
+ else if (grub_strcmp (value, "nv") == 0)
|
|
+ *mode = 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
|
|
+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 *) tpm2_protector_ctx.keyfile);
|
|
+ grub_memset (&tpm2_protector_ctx, 0, sizeof (tpm2_protector_ctx));
|
|
+
|
|
+ if (state[OPTION_MODE].set) /* mode */
|
|
+ {
|
|
+ err = tpm2_protector_parse_mode (state[OPTION_MODE].arg, &tpm2_protector_ctx.mode);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ if (state[OPTION_PCRS].set) /* pcrs */
|
|
+ {
|
|
+ err = grub_tpm2_protector_parse_pcrs (state[OPTION_PCRS].arg,
|
|
+ tpm2_protector_ctx.pcrs,
|
|
+ &tpm2_protector_ctx.pcr_count);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ if (state[OPTION_BANK].set) /* bank */
|
|
+ {
|
|
+ err = grub_tpm2_protector_parse_bank (state[OPTION_BANK].arg,
|
|
+ &tpm2_protector_ctx.bank);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ if (state[OPTION_TPM2KEY].set) /* tpm2key */
|
|
+ {
|
|
+ err = tpm2_protector_parse_file (state[OPTION_TPM2KEY].arg,
|
|
+ &tpm2_protector_ctx.tpm2key);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ if (state[OPTION_KEYFILE].set) /* keyfile */
|
|
+ {
|
|
+ err = tpm2_protector_parse_file (state[OPTION_KEYFILE].arg,
|
|
+ &tpm2_protector_ctx.keyfile);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ if (state[OPTION_SRK].set) /* srk */
|
|
+ {
|
|
+ err = grub_tpm2_protector_parse_tpm_handle (state[OPTION_SRK].arg,
|
|
+ &tpm2_protector_ctx.srk);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ if (state[OPTION_ASYMMETRIC].set) /* asymmetric */
|
|
+ {
|
|
+ err = grub_tpm2_protector_parse_asymmetric (state[OPTION_ASYMMETRIC].arg,
|
|
+ &tpm2_protector_ctx.srk_type);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ if (state[OPTION_NVINDEX].set) /* nvindex */
|
|
+ {
|
|
+ err = grub_tpm2_protector_parse_tpm_handle (state[OPTION_NVINDEX].arg,
|
|
+ &tpm2_protector_ctx.nv);
|
|
+ if (err != GRUB_ERR_NONE)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ err = tpm2_protector_check_args (&tpm2_protector_ctx);
|
|
+
|
|
+ /* This command only initializes the protector, so nothing else to do. */
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+tpm2_protector_clear_cmd_handler (grub_extcmd_context_t ctxt __attribute__ ((unused)),
|
|
+ int argc, char **args __attribute__ ((unused)))
|
|
+{
|
|
+ if (argc != 0)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("tpm2_key_protector_clear accepts no arguments"));
|
|
+
|
|
+ grub_free ((void *) tpm2_protector_ctx.keyfile);
|
|
+ grub_memset (&tpm2_protector_ctx, 0, sizeof (tpm2_protector_ctx));
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+static struct grub_key_protector tpm2_key_protector =
|
|
+ {
|
|
+ .name = "tpm2",
|
|
+ .recover_key = tpm2_protector_recover_key
|
|
+ };
|
|
+
|
|
+GRUB_MOD_INIT (tpm2_key_protector)
|
|
+{
|
|
+ tpm2_protector_init_cmd =
|
|
+ grub_register_extcmd ("tpm2_key_protector_init",
|
|
+ tpm2_protector_init_cmd_handler, 0,
|
|
+ N_("[-m mode] "
|
|
+ "[-p pcr_list] "
|
|
+ "[-b pcr_bank] "
|
|
+ "[-T tpm2_key_file_path] "
|
|
+ "[-k sealed_key_file_path] "
|
|
+ "[-s srk_handle] "
|
|
+ "[-a asymmetric_key_type] "
|
|
+ "[-n nv_index]"),
|
|
+ N_("Initialize the TPM2 key protector."),
|
|
+ tpm2_protector_init_cmd_options);
|
|
+ tpm2_protector_clear_cmd =
|
|
+ grub_register_extcmd ("tpm2_key_protector_clear",
|
|
+ tpm2_protector_clear_cmd_handler, 0, NULL,
|
|
+ N_("Clear the TPM2 key protector if previously initialized."),
|
|
+ NULL);
|
|
+ grub_key_protector_register (&tpm2_key_protector);
|
|
+}
|
|
+
|
|
+GRUB_MOD_FINI (tpm2_key_protector)
|
|
+{
|
|
+ grub_free ((void *) tpm2_protector_ctx.keyfile);
|
|
+
|
|
+ grub_key_protector_unregister (&tpm2_key_protector);
|
|
+ grub_unregister_extcmd (tpm2_protector_clear_cmd);
|
|
+ grub_unregister_extcmd (tpm2_protector_init_cmd);
|
|
+}
|
|
Index: grub-2.12/grub-core/commands/tpm2_key_protector/tpm2.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ grub-2.12/grub-core/commands/tpm2_key_protector/tpm2.h
|
|
@@ -0,0 +1,36 @@
|
|
+/*
|
|
+ * 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_TPM2_HEADER
|
|
+#define GRUB_TPM2_TPM2_HEADER 1
|
|
+
|
|
+#include <tss2_types.h>
|
|
+#include <tss2_structs.h>
|
|
+#include <tpm2_cmd.h>
|
|
+
|
|
+/* Well-Known Windows SRK handle */
|
|
+#define TPM2_SRK_HANDLE 0x81000001
|
|
+
|
|
+struct tpm2_sealed_key {
|
|
+ TPM2B_PUBLIC_t public;
|
|
+ TPM2B_PRIVATE_t private;
|
|
+};
|
|
+typedef struct tpm2_sealed_key tpm2_sealed_key_t;
|
|
+
|
|
+#endif /* ! GRUB_TPM2_TPM2_HEADER */
|
|
Index: grub-2.12/grub-core/commands/tpm2_key_protector/tpm2_args.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ grub-2.12/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;
|
|
+
|
|
+extern grub_err_t
|
|
+grub_tpm2_protector_parse_pcrs (char *value, grub_uint8_t *pcrs, grub_uint8_t *pcr_count);
|
|
+
|
|
+extern grub_err_t
|
|
+grub_tpm2_protector_parse_asymmetric (const char *value, grub_srk_type_t *srk_type);
|
|
+
|
|
+extern grub_err_t
|
|
+grub_tpm2_protector_parse_bank (const char *value, TPM_ALG_ID_t *bank);
|
|
+
|
|
+extern grub_err_t
|
|
+grub_tpm2_protector_parse_tpm_handle (const char *value, TPM_HANDLE_t *handle);
|
|
+
|
|
+#endif /* ! GRUB_TPM2_INTERNAL_ARGS_HEADER */
|
|
Index: grub-2.12/grub-core/commands/tpm2_key_protector/tpm2key.asn
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ grub-2.12/grub-core/commands/tpm2_key_protector/tpm2key.asn
|
|
@@ -0,0 +1,49 @@
|
|
+--
|
|
+-- GRUB: GRand Unified Bootloader
|
|
+-- 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/>.
|
|
+--
|
|
+-- This file describes TPM 2.0 Key File format for libtasn1.
|
|
+-- To generate tpm2key_asn1_tab.c: asn1Parser tpm2key.asn
|
|
+--
|
|
+TPM2KEY {}
|
|
+DEFINITIONS IMPLICIT TAGS ::=
|
|
+
|
|
+BEGIN
|
|
+
|
|
+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
|
|
+}
|
|
+
|
|
+END
|
|
Index: grub-2.12/grub-core/commands/tpm2_key_protector/tpm2key.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ grub-2.12/grub-core/commands/tpm2_key_protector/tpm2key.c
|
|
@@ -0,0 +1,499 @@
|
|
+/*
|
|
+ * GRUB -- GRand Unified Bootloader
|
|
+ * Copyright (C) 2023 SUSE LLC
|
|
+ * 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/list.h>
|
|
+#include <grub/misc.h>
|
|
+#include <grub/mm.h>
|
|
+
|
|
+#include <tss2_buffer.h>
|
|
+
|
|
+#include "tpm2key.h"
|
|
+
|
|
+extern asn1_static_node tpm2key_asn1_tab[];
|
|
+const char *sealed_key_oid = "2.23.133.10.1.5";
|
|
+
|
|
+static int
|
|
+asn1_allocate_and_read (asn1_node node, const char *name, void **content, grub_size_t *content_size)
|
|
+{
|
|
+ grub_uint8_t *tmpstr = NULL;
|
|
+ int tmpstr_size = 0;
|
|
+ int ret;
|
|
+
|
|
+ if (content == NULL)
|
|
+ return ASN1_MEM_ERROR;
|
|
+
|
|
+ ret = asn1_read_value (node, name, NULL, &tmpstr_size);
|
|
+ if (ret != ASN1_MEM_ERROR)
|
|
+ return ret;
|
|
+
|
|
+ tmpstr = grub_malloc (tmpstr_size);
|
|
+ if (tmpstr == NULL)
|
|
+ return ASN1_MEM_ERROR;
|
|
+
|
|
+ ret = asn1_read_value (node, name, tmpstr, &tmpstr_size);
|
|
+ if (ret != ASN1_SUCCESS)
|
|
+ return ret;
|
|
+
|
|
+ *content = tmpstr;
|
|
+ *content_size = tmpstr_size;
|
|
+
|
|
+ return ASN1_SUCCESS;
|
|
+}
|
|
+
|
|
+static int
|
|
+asn1_read_uint32 (asn1_node node, const char *name, grub_uint32_t *out)
|
|
+{
|
|
+ grub_uint32_t tmp = 0;
|
|
+ grub_uint8_t *ptr;
|
|
+ void *data = NULL;
|
|
+ grub_size_t data_size;
|
|
+ int ret;
|
|
+
|
|
+ ret = asn1_allocate_and_read (node, name, &data, &data_size);
|
|
+ if (ret != ASN1_SUCCESS)
|
|
+ return ret;
|
|
+
|
|
+ /*
|
|
+ * ASN.1 INTEGER is encoded in the following format:
|
|
+ *
|
|
+ * TAG LENGTH OCTECTS
|
|
+ *
|
|
+ * The integer TAG is 02 and LENGTH is the number of followed OCTECTS in
|
|
+ * big endian. For example:
|
|
+ *
|
|
+ * 0x1: 02 01 01
|
|
+ * 0xabcd: 02 02 ab cd
|
|
+ *
|
|
+ * To decribe 0x1, it only takes 1 octect, so LENGTH is 0x01 and the
|
|
+ * octect is 0x01. On the other hand, 0xabcd requires 2 octects: 'ab" and
|
|
+ * 'cd', so LENGTH is 0x02.
|
|
+ *
|
|
+ * This function only expects a uint32 integer, so it rejects any integer
|
|
+ * containing more than 4 octects.
|
|
+ */
|
|
+ if (data_size > 4)
|
|
+ {
|
|
+ ret = ASN1_MEM_ERROR;
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ /* Copy the octects into 'tmp' to make it a big-endian uint32 */
|
|
+ ptr = (grub_uint8_t *) &tmp + (4 - data_size);
|
|
+ grub_memcpy (ptr, data, data_size);
|
|
+
|
|
+ /* Convert the big-endian integer to host uint32 */
|
|
+ tmp = grub_be_to_cpu32 (tmp);
|
|
+
|
|
+ *out = tmp;
|
|
+ error:
|
|
+ if (data)
|
|
+ grub_free (data);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+grub_err_t
|
|
+grub_tpm2key_start_parsing (asn1_node *parsed_tpm2key, void *data, grub_size_t size)
|
|
+{
|
|
+ asn1_node tpm2key;
|
|
+ asn1_node tpm2key_asn1 = NULL;
|
|
+ void *type_oid = NULL;
|
|
+ grub_size_t type_oid_size = 0;
|
|
+ void *empty_auth = NULL;
|
|
+ grub_size_t empty_auth_size = 0;
|
|
+ int tmp_size = 0;
|
|
+ int ret;
|
|
+ grub_err_t err;
|
|
+
|
|
+ /*
|
|
+ * 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
|
|
+ * }
|
|
+ */
|
|
+ ret = asn1_array2tree (tpm2key_asn1_tab, &tpm2key_asn1, NULL);
|
|
+ if (ret != ASN1_SUCCESS)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "failed to parse TPM2KEY ASN.1 array");
|
|
+
|
|
+ ret = asn1_create_element (tpm2key_asn1, "TPM2KEY.TPMKey", &tpm2key);
|
|
+ if (ret != ASN1_SUCCESS)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "failed to create TPM2KEY.TPMKey");
|
|
+
|
|
+ ret = asn1_der_decoding (&tpm2key, data, size, NULL);
|
|
+ if (ret != ASN1_SUCCESS)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "failed to decode TPM2KEY DER");
|
|
+
|
|
+ /* Check if 'type' is Sealed Key or not */
|
|
+ ret = asn1_allocate_and_read (tpm2key, "type", &type_oid, &type_oid_size);
|
|
+ if (ret != ASN1_SUCCESS)
|
|
+ return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a valid TPM2KEY file");
|
|
+
|
|
+ if (grub_memcmp (sealed_key_oid, type_oid, type_oid_size) != 0)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a valid TPM2KEY file");
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ /* 'emptyAuth' must be 'TRUE' since we don't support password authorization */
|
|
+ ret = asn1_allocate_and_read (tpm2key, "emptyAuth", &empty_auth, &empty_auth_size);
|
|
+ if (ret != ASN1_SUCCESS || grub_strncmp ("TRUE", empty_auth, empty_auth_size) != 0)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_BAD_ARGUMENT, "emptyAuth not TRUE");
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ /* 'secret' should not be in a sealed key */
|
|
+ ret = asn1_read_value (tpm2key, "secret", NULL, &tmp_size);
|
|
+ if (ret != ASN1_ELEMENT_NOT_FOUND)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_BAD_ARGUMENT, "\"secret\" not allowed for Sealed Key");
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ *parsed_tpm2key = tpm2key;
|
|
+
|
|
+ err = GRUB_ERR_NONE;
|
|
+
|
|
+ error:
|
|
+ grub_free (type_oid);
|
|
+ grub_free (empty_auth);
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+void
|
|
+grub_tpm2key_end_parsing (asn1_node tpm2key)
|
|
+{
|
|
+ asn1_delete_structure (&tpm2key);
|
|
+ tpm2key = NULL;
|
|
+}
|
|
+
|
|
+grub_err_t
|
|
+grub_tpm2key_get_rsaparent (asn1_node tpm2key, grub_uint8_t *rsaparent)
|
|
+{
|
|
+ void *bool_str = NULL;
|
|
+ grub_size_t bool_str_size = 0;
|
|
+ int ret;
|
|
+
|
|
+ if (rsaparent == NULL)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "NULL pointer detected");
|
|
+
|
|
+ if (tpm2key == NULL)
|
|
+ return grub_error (GRUB_ERR_READ_ERROR, "invalid parent node");
|
|
+
|
|
+ ret = asn1_allocate_and_read (tpm2key, "rsaParent", &bool_str, &bool_str_size);
|
|
+ if (ret == ASN1_SUCCESS)
|
|
+ {
|
|
+ if (grub_strncmp ("TRUE", bool_str, bool_str_size) == 0)
|
|
+ *rsaparent = 1;
|
|
+ else
|
|
+ *rsaparent = 0;
|
|
+ }
|
|
+ else if (ret == ASN1_ELEMENT_NOT_FOUND)
|
|
+ *rsaparent = 0;
|
|
+ else
|
|
+ return grub_error (GRUB_ERR_READ_ERROR, "failed to retrieve rsaParent");
|
|
+
|
|
+ grub_free (bool_str);
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+grub_err_t
|
|
+grub_tpm2key_get_parent (asn1_node tpm2key, grub_uint32_t *parent)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ if (parent == NULL)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "NULL pointer detected");
|
|
+
|
|
+ if (tpm2key == NULL)
|
|
+ return grub_error (GRUB_ERR_READ_ERROR, "invalid parent node");
|
|
+
|
|
+ ret = asn1_read_uint32 (tpm2key, "parent", parent);
|
|
+ if (ret != ASN1_SUCCESS)
|
|
+ return grub_error (GRUB_ERR_READ_ERROR, "failed to retrieve parent");
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+tpm2key_get_octstring (asn1_node tpm2key, const char *name, void **data, grub_size_t *size)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ if (name == NULL || data == NULL || size == NULL)
|
|
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid parameter(s)");
|
|
+
|
|
+ if (tpm2key == NULL)
|
|
+ return grub_error (GRUB_ERR_READ_ERROR, "invalid %s node", name);
|
|
+
|
|
+ ret = asn1_allocate_and_read (tpm2key, name, data, size);
|
|
+ if (ret != ASN1_SUCCESS)
|
|
+ return grub_error (GRUB_ERR_READ_ERROR, "failed to retrieve %s", name);
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+grub_err_t
|
|
+grub_tpm2key_get_pubkey (asn1_node tpm2key, void **data, grub_size_t *size)
|
|
+{
|
|
+ return tpm2key_get_octstring (tpm2key, "pubkey", data, size);
|
|
+}
|
|
+
|
|
+grub_err_t
|
|
+grub_tpm2key_get_privkey (asn1_node tpm2key, void **data, grub_size_t *size)
|
|
+{
|
|
+ return tpm2key_get_octstring (tpm2key, "privkey", data, size);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * The maximum and minimum number of elements for 'policy' and 'authPolicy' sequences
|
|
+ *
|
|
+ * Although there is no limit for the number of sequences elements, we set the upper
|
|
+ * bound to 99 to make it easier to implement the code.
|
|
+ *
|
|
+ * Any 'policy' or 'authPolicy' contains more than 99 commands/policies would become
|
|
+ * extremely complex to manage so it is impractical to support such use case.
|
|
+ */
|
|
+#define TPM2KEY_ELEMENTS_MAX 99
|
|
+#define TPM2KEY_ELEMENTS_MIN 1
|
|
+
|
|
+/*
|
|
+ * The string to fetch 'Policy' from 'authPolicy':
|
|
+ * authPolicy.?XX.Policy
|
|
+ */
|
|
+#define AUTHPOLICY_POL_MAX_STR "authPolicy.?XX.Policy"
|
|
+#define AUTHPOLICY_POL_MAX (sizeof (AUTHPOLICY_POL_MAX_STR))
|
|
+
|
|
+/*
|
|
+ * Expected strings for CommandCode and CommandPolicy:
|
|
+ * policy.?XX.CommandCode
|
|
+ * policy.?XX.CommandPolicy
|
|
+ * authPolicy.?XX.Policy.?YY.CommandCode
|
|
+ * authPolicy.?XX.Policy.?YY.CommandPolicy
|
|
+ */
|
|
+#define CMD_CODE_MAX_STR AUTHPOLICY_POL_MAX_STR".?YY.CommandCode"
|
|
+#define CMD_POL_MAX_STR AUTHPOLICY_POL_MAX_STR".?YY.CommandPolicy"
|
|
+#define CMD_CODE_MAX (sizeof (CMD_CODE_MAX_STR))
|
|
+#define CMD_POL_MAX (sizeof (CMD_POL_MAX_STR))
|
|
+
|
|
+static int
|
|
+tpm2key_get_policy_seq (asn1_node tpm2key, const char *prefix,
|
|
+ tpm2key_policy_t *policy_seq)
|
|
+{
|
|
+ tpm2key_policy_t tmp_seq = NULL;
|
|
+ tpm2key_policy_t policy = NULL;
|
|
+ int policy_n;
|
|
+ char cmd_code[CMD_CODE_MAX];
|
|
+ char cmd_pol[CMD_POL_MAX];
|
|
+ grub_size_t cmd_policy_len;
|
|
+ int i;
|
|
+ int ret;
|
|
+
|
|
+ ret = asn1_number_of_elements (tpm2key, prefix, &policy_n);
|
|
+ if (ret != ASN1_SUCCESS)
|
|
+ return ret;
|
|
+
|
|
+ /*
|
|
+ * Limit the number of policy commands to two digits (99)
|
|
+ * Although there is no upper bound for the number of policy commands,
|
|
+ * in practice, it takes one or two policy commands to unseal the key,
|
|
+ * so the 99 commands limit is more than enough.
|
|
+ */
|
|
+ if (policy_n > TPM2KEY_ELEMENTS_MAX || policy_n < TPM2KEY_ELEMENTS_MIN)
|
|
+ return ASN1_VALUE_NOT_VALID;
|
|
+
|
|
+ /*
|
|
+ * Iterate the policy commands backwards since grub_list_push() prepends
|
|
+ * the item into the list.
|
|
+ */
|
|
+ for (i = policy_n; i >= 1; i--) {
|
|
+ policy = grub_zalloc (sizeof (struct tpm2key_policy));
|
|
+ if (policy == NULL)
|
|
+ {
|
|
+ ret = ASN1_MEM_ALLOC_ERROR;
|
|
+ goto error;
|
|
+ }
|
|
+ grub_snprintf (cmd_code, CMD_CODE_MAX, "%s.?%d.CommandCode", prefix, i);
|
|
+ grub_snprintf (cmd_pol, CMD_POL_MAX, "%s.?%d.CommandPolicy", prefix, i);
|
|
+
|
|
+ /* CommandCode [0] EXPLICIT INTEGER */
|
|
+ ret = asn1_read_uint32 (tpm2key, cmd_code, &policy->cmd_code);
|
|
+ if (ret != ASN1_SUCCESS)
|
|
+ return ret;
|
|
+
|
|
+ /* CommandPolicy [1] EXPLICIT OCTET STRING */
|
|
+ ret = tpm2key_get_octstring (tpm2key, cmd_pol, &policy->cmd_policy,
|
|
+ &cmd_policy_len);
|
|
+ if (ret != ASN1_SUCCESS)
|
|
+ {
|
|
+ goto error;
|
|
+ }
|
|
+ else if (cmd_policy_len > GRUB_TPM2_BUFFER_CAPACITY)
|
|
+ {
|
|
+ /*
|
|
+ * CommandPolicy is the marshalled parameters for the TPM command so
|
|
+ * it should not be larger than the maximum TPM2 buffer.
|
|
+ */
|
|
+ ret = ASN1_VALUE_NOT_VALID;
|
|
+ goto error;
|
|
+ }
|
|
+ policy->cmd_policy_len = (grub_uint16_t)cmd_policy_len;
|
|
+
|
|
+ /* Prepend the policy command into the sequence */
|
|
+ grub_list_push (GRUB_AS_LIST_P (&tmp_seq), GRUB_AS_LIST (policy));
|
|
+ }
|
|
+
|
|
+ *policy_seq = tmp_seq;
|
|
+
|
|
+ return ASN1_SUCCESS;
|
|
+
|
|
+ error:
|
|
+ if (policy != NULL)
|
|
+ {
|
|
+ grub_free (policy->cmd_policy);
|
|
+ grub_free (policy);
|
|
+ }
|
|
+ grub_tpm2key_free_policy_seq (tmp_seq);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+grub_err_t
|
|
+grub_tpm2key_get_policy_seq (asn1_node tpm2key, tpm2key_policy_t *policy_seq)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ ret = tpm2key_get_policy_seq (tpm2key, "policy", policy_seq);
|
|
+ if (ret == ASN1_ELEMENT_NOT_FOUND)
|
|
+ {
|
|
+ /* "policy" is optional, so it may not be available */
|
|
+ *policy_seq = NULL;
|
|
+ return GRUB_ERR_NONE;
|
|
+ }
|
|
+ else if (ret != ASN1_SUCCESS)
|
|
+ return grub_error (GRUB_ERR_READ_ERROR, "failed to retrieve policy");
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+void
|
|
+grub_tpm2key_free_policy_seq (tpm2key_policy_t policy_seq)
|
|
+{
|
|
+ tpm2key_policy_t policy;
|
|
+ tpm2key_policy_t next;
|
|
+
|
|
+ if (policy_seq == NULL)
|
|
+ return;
|
|
+
|
|
+ FOR_LIST_ELEMENTS_SAFE (policy, next, policy_seq)
|
|
+ {
|
|
+ grub_free (policy->cmd_policy);
|
|
+ grub_free (policy);
|
|
+ }
|
|
+}
|
|
+
|
|
+grub_err_t
|
|
+grub_tpm2key_get_authpolicy_seq (asn1_node tpm2key, tpm2key_authpolicy_t *authpol_seq)
|
|
+{
|
|
+ tpm2key_authpolicy_t tmp_seq = NULL;
|
|
+ tpm2key_authpolicy_t authpol = NULL;
|
|
+ int authpol_n;
|
|
+ char authpol_pol[AUTHPOLICY_POL_MAX];
|
|
+ int i;
|
|
+ int ret;
|
|
+ grub_err_t err;
|
|
+
|
|
+ ret = asn1_number_of_elements (tpm2key, "authPolicy", &authpol_n);
|
|
+ if (ret == ASN1_ELEMENT_NOT_FOUND)
|
|
+ {
|
|
+ /* "authPolicy" is optional, so it may not be available */
|
|
+ *authpol_seq = NULL;
|
|
+ return GRUB_ERR_NONE;
|
|
+ }
|
|
+ else if (ret != ASN1_SUCCESS)
|
|
+ return grub_error (GRUB_ERR_READ_ERROR, "failed to retrieve authPolicy");
|
|
+
|
|
+ /* Limit the number of authPolicy elements to two digits (99) */
|
|
+ if (authpol_n > TPM2KEY_ELEMENTS_MAX || authpol_n < TPM2KEY_ELEMENTS_MIN)
|
|
+ return grub_error (GRUB_ERR_OUT_OF_RANGE, "invalid number of authPolicy elements");
|
|
+
|
|
+ /*
|
|
+ * Iterate the authPolicy elements backwards since grub_list_push() prepends
|
|
+ * the item into the list.
|
|
+ */
|
|
+ for (i = authpol_n; i >= 1; i--) {
|
|
+ authpol = grub_zalloc (sizeof (struct tpm2key_authpolicy));
|
|
+ if (authpol == NULL)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_OUT_OF_MEMORY, "failed to allocate memory for authPolicy");
|
|
+ goto error;
|
|
+ }
|
|
+ grub_snprintf (authpol_pol, AUTHPOLICY_POL_MAX, "authPolicy.?%d.Policy", i);
|
|
+
|
|
+ ret = tpm2key_get_policy_seq (tpm2key, authpol_pol, &authpol->policy_seq);
|
|
+ if (ret != ASN1_SUCCESS)
|
|
+ {
|
|
+ err = grub_error (GRUB_ERR_READ_ERROR, "failed to retrieve policy from authPolicy");
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ /* Prepend the authPolicy element into the sequence */
|
|
+ grub_list_push (GRUB_AS_LIST_P (&tmp_seq), GRUB_AS_LIST (authpol));
|
|
+ }
|
|
+
|
|
+ *authpol_seq = tmp_seq;
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+
|
|
+ error:
|
|
+ if (authpol != NULL)
|
|
+ {
|
|
+ grub_tpm2key_free_policy_seq (authpol->policy_seq);
|
|
+ grub_free (authpol);
|
|
+ }
|
|
+
|
|
+ grub_tpm2key_free_authpolicy_seq (tmp_seq);
|
|
+
|
|
+ return err;
|
|
+}
|
|
+
|
|
+void
|
|
+grub_tpm2key_free_authpolicy_seq (tpm2key_authpolicy_t authpol_seq)
|
|
+{
|
|
+ tpm2key_authpolicy_t authpol;
|
|
+ tpm2key_authpolicy_t next;
|
|
+
|
|
+ if (authpol_seq == NULL)
|
|
+ return;
|
|
+
|
|
+ FOR_LIST_ELEMENTS_SAFE (authpol, next, authpol_seq)
|
|
+ {
|
|
+ grub_tpm2key_free_policy_seq (authpol->policy_seq);
|
|
+ grub_free (authpol);
|
|
+ }
|
|
+}
|
|
Index: grub-2.12/grub-core/commands/tpm2_key_protector/tpm2key.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ grub-2.12/grub-core/commands/tpm2_key_protector/tpm2key.h
|
|
@@ -0,0 +1,87 @@
|
|
+/*
|
|
+ * GRUB -- GRand Unified Bootloader
|
|
+ * Copyright (C) 2023 SUSE LLC
|
|
+ * 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_TPM2KEY_HEADER
|
|
+#define GRUB_TPM2_TPM2KEY_HEADER 1
|
|
+
|
|
+#include <grub/types.h>
|
|
+#include <grub/libtasn1.h>
|
|
+
|
|
+/*
|
|
+ * TPMPolicy ::= SEQUENCE {
|
|
+ * CommandCode [0] EXPLICIT INTEGER,
|
|
+ * CommandPolicy [1] EXPLICIT OCTET STRING
|
|
+ * }
|
|
+ */
|
|
+struct tpm2key_policy {
|
|
+ struct tpm2key_policy *next;
|
|
+ struct tpm2key_policy **prev;
|
|
+ grub_uint32_t cmd_code;
|
|
+ void *cmd_policy;
|
|
+ grub_uint16_t cmd_policy_len;
|
|
+};
|
|
+typedef struct tpm2key_policy *tpm2key_policy_t;
|
|
+
|
|
+/*
|
|
+ * TPMAuthPolicy ::= SEQUENCE {
|
|
+ * Name [0] EXPLICIT UTF8String OPTIONAL,
|
|
+ * Policy [1] EXPLICIT SEQUENCE OF TPMPolicy
|
|
+ * }
|
|
+ *
|
|
+ * Name is not a necessary part to unseal the key. Ignore it.
|
|
+ */
|
|
+struct tpm2key_authpolicy {
|
|
+ struct tpm2key_authpolicy *next;
|
|
+ struct tpm2key_authpolicy **prev;
|
|
+ /* char *name; */
|
|
+ tpm2key_policy_t policy_seq;
|
|
+};
|
|
+typedef struct tpm2key_authpolicy *tpm2key_authpolicy_t;
|
|
+
|
|
+extern grub_err_t
|
|
+grub_tpm2key_start_parsing (asn1_node *parsed_tpm2key, void *data, grub_size_t size);
|
|
+
|
|
+extern void
|
|
+grub_tpm2key_end_parsing (asn1_node tpm2key);
|
|
+
|
|
+extern grub_err_t
|
|
+grub_tpm2key_get_rsaparent (asn1_node tpm2key, grub_uint8_t *rsaparent);
|
|
+
|
|
+extern grub_err_t
|
|
+grub_tpm2key_get_parent (asn1_node tpm2key, grub_uint32_t *parent);
|
|
+
|
|
+extern grub_err_t
|
|
+grub_tpm2key_get_pubkey (asn1_node tpm2key, void **data, grub_size_t *size);
|
|
+
|
|
+extern grub_err_t
|
|
+grub_tpm2key_get_privkey (asn1_node tpm2key, void **data, grub_size_t *size);
|
|
+
|
|
+extern grub_err_t
|
|
+grub_tpm2key_get_policy_seq (asn1_node tpm2key, tpm2key_policy_t *policy_seq);
|
|
+
|
|
+extern void
|
|
+grub_tpm2key_free_policy_seq (tpm2key_policy_t policy_seq);
|
|
+
|
|
+extern grub_err_t
|
|
+grub_tpm2key_get_authpolicy_seq (asn1_node tpm2key, tpm2key_authpolicy_t *authpol_seq);
|
|
+
|
|
+extern void
|
|
+grub_tpm2key_free_authpolicy_seq (tpm2key_authpolicy_t authpol_seq);
|
|
+
|
|
+#endif /* GRUB_TPM2_TPM2KEY_HEADER */
|
|
Index: grub-2.12/grub-core/commands/tpm2_key_protector/tpm2key_asn1_tab.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ grub-2.12/grub-core/commands/tpm2_key_protector/tpm2key_asn1_tab.c
|
|
@@ -0,0 +1,63 @@
|
|
+/*
|
|
+ * GRUB -- GRand Unified Bootloader
|
|
+ * 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/>.
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * This file is generated by 'asn1Parser tpm2key.asn' and the '#include'
|
|
+ * headers are replaced with the ones in grub2.
|
|
+ * - 'grub/mm.h' for the definition of 'NULL'
|
|
+ * - 'libtasn1.h' for the definition of 'asn1_static_node'
|
|
+ */
|
|
+
|
|
+#include <grub/mm.h>
|
|
+#include <grub/libtasn1.h>
|
|
+
|
|
+const asn1_static_node tpm2key_asn1_tab[] = {
|
|
+ { "TPM2KEY", 536875024, NULL },
|
|
+ { NULL, 1073741836, NULL },
|
|
+ { "TPMPolicy", 1610612741, NULL },
|
|
+ { "CommandCode", 1610620931, NULL },
|
|
+ { NULL, 2056, "0"},
|
|
+ { "CommandPolicy", 536879111, NULL },
|
|
+ { NULL, 2056, "1"},
|
|
+ { "TPMAuthPolicy", 1610612741, NULL },
|
|
+ { "Name", 1610637346, NULL },
|
|
+ { NULL, 2056, "0"},
|
|
+ { "Policy", 536879115, NULL },
|
|
+ { NULL, 1073743880, "1"},
|
|
+ { NULL, 2, "TPMPolicy"},
|
|
+ { "TPMKey", 536870917, NULL },
|
|
+ { "type", 1073741836, NULL },
|
|
+ { "emptyAuth", 1610637316, NULL },
|
|
+ { NULL, 2056, "0"},
|
|
+ { "policy", 1610637323, NULL },
|
|
+ { NULL, 1073743880, "1"},
|
|
+ { NULL, 2, "TPMPolicy"},
|
|
+ { "secret", 1610637319, NULL },
|
|
+ { NULL, 2056, "2"},
|
|
+ { "authPolicy", 1610637323, NULL },
|
|
+ { NULL, 1073743880, "3"},
|
|
+ { NULL, 2, "TPMAuthPolicy"},
|
|
+ { "description", 1610637346, NULL },
|
|
+ { NULL, 2056, "4"},
|
|
+ { "rsaParent", 1610637316, NULL },
|
|
+ { NULL, 2056, "5"},
|
|
+ { "parent", 1073741827, NULL },
|
|
+ { "pubkey", 1073741831, NULL },
|
|
+ { "privkey", 7, NULL },
|
|
+ { NULL, 0, NULL }
|
|
+};
|