These are the changes for the TPM driver with a single major new
 feature: TPM bus encryption and integrity protection. The key pair
 on TPM side is generated from so called null random seed per power
 on of the machine [1]. This supports the TPM encryption of the hard
 drive by adding layer of protection against bus interposer attacks.
 
 Other than the pull request a few minor fixes and documentation for
 tpm_tis to clarify basics of TPM localities for future patch review
 discussions (will be extended and refined over times, just a seed).
 
 [1] https://lore.kernel.org/linux-integrity/20240429202811.13643-1-James.Bottomley@HansenPartnership.com/
 
 BR, Jarkko
 -----BEGIN PGP SIGNATURE-----
 
 iJYEABYKAD4WIQRE6pSOnaBC00OEHEIaerohdGur0gUCZj0l2iAcamFya2tvLnNh
 a2tpbmVuQGxpbnV4LmludGVsLmNvbQAKCRAaerohdGur0m8yAP4hBjMtpgAJZ4eZ
 5o9tEQJrh/1JFZJ+8HU5IKPc4RU8BAEAyyYOCtxtS/C5B95iP+LvNla0KWi0pprU
 HsCLULnV2Aw=
 =RTXJ
 -----END PGP SIGNATURE-----

Merge tag 'tpmdd-next-6.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-tpmdd

Pull TPM updates from Jarkko Sakkinen:
 "These are the changes for the TPM driver with a single major new
  feature: TPM bus encryption and integrity protection. The key pair on
  TPM side is generated from so called null random seed per power on of
  the machine [1]. This supports the TPM encryption of the hard drive by
  adding layer of protection against bus interposer attacks.

  Other than that, a few minor fixes and documentation for tpm_tis to
  clarify basics of TPM localities for future patch review discussions
  (will be extended and refined over times, just a seed)"

Link: https://lore.kernel.org/linux-integrity/20240429202811.13643-1-James.Bottomley@HansenPartnership.com/ [1]

* tag 'tpmdd-next-6.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-tpmdd: (28 commits)
  Documentation: tpm: Add TPM security docs toctree entry
  tpm: disable the TPM if NULL name changes
  Documentation: add tpm-security.rst
  tpm: add the null key name as a sysfs export
  KEYS: trusted: Add session encryption protection to the seal/unseal path
  tpm: add session encryption protection to tpm2_get_random()
  tpm: add hmac checks to tpm2_pcr_extend()
  tpm: Add the rest of the session HMAC API
  tpm: Add HMAC session name/handle append
  tpm: Add HMAC session start and end functions
  tpm: Add TCG mandated Key Derivation Functions (KDFs)
  tpm: Add NULL primary creation
  tpm: export the context save and load commands
  tpm: add buffer function to point to returned parameters
  crypto: lib - implement library version of AES in CFB mode
  KEYS: trusted: tpm2: Use struct tpm_buf for sized buffers
  tpm: Add tpm_buf_read_{u8,u16,u32}
  tpm: TPM2B formatted buffers
  tpm: Store the length of the tpm_buf data separately.
  tpm: Update struct tpm_buf documentation comments
  ...
This commit is contained in:
Linus Torvalds 2024-05-13 10:40:15 -07:00
commit b19239143e
25 changed files with 2518 additions and 211 deletions

View File

@ -32,6 +32,7 @@ properties:
- enum:
- infineon,slb9673
- nuvoton,npct75x
- st,st33ktpm2xi2c
- const: tcg,tpm-tis-i2c
- description: TPM 1.2 and 2.0 chips with vendor-specific I²C interface

View File

@ -5,6 +5,8 @@ Trusted Platform Module documentation
.. toctree::
tpm_event_log
tpm-security
tpm_tis
tpm_vtpm_proxy
xen-tpmfront
tpm_ftpm_tee

View File

@ -0,0 +1,216 @@
.. SPDX-License-Identifier: GPL-2.0-only
TPM Security
============
The object of this document is to describe how we make the kernel's
use of the TPM reasonably robust in the face of external snooping and
packet alteration attacks (called passive and active interposer attack
in the literature). The current security document is for TPM 2.0.
Introduction
------------
The TPM is usually a discrete chip attached to a PC via some type of
low bandwidth bus. There are exceptions to this such as the Intel
PTT, which is a software TPM running inside a software environment
close to the CPU, which are subject to different attacks, but right at
the moment, most hardened security environments require a discrete
hardware TPM, which is the use case discussed here.
Snooping and Alteration Attacks against the bus
-----------------------------------------------
The current state of the art for snooping the `TPM Genie`_ hardware
interposer which is a simple external device that can be installed in
a couple of seconds on any system or laptop. Recently attacks were
successfully demonstrated against the `Windows Bitlocker TPM`_ system.
Most recently the same `attack against TPM based Linux disk
encryption`_ schemes. The next phase of research seems to be hacking
existing devices on the bus to act as interposers, so the fact that
the attacker requires physical access for a few seconds might
evaporate. However, the goal of this document is to protect TPM
secrets and integrity as far as we are able in this environment and to
try to insure that if we can't prevent the attack then at least we can
detect it.
Unfortunately, most of the TPM functionality, including the hardware
reset capability can be controlled by an attacker who has access to
the bus, so we'll discuss some of the disruption possibilities below.
Measurement (PCR) Integrity
---------------------------
Since the attacker can send their own commands to the TPM, they can
send arbitrary PCR extends and thus disrupt the measurement system,
which would be an annoying denial of service attack. However, there
are two, more serious, classes of attack aimed at entities sealed to
trust measurements.
1. The attacker could intercept all PCR extends coming from the system
and completely substitute their own values, producing a replay of
an untampered state that would cause PCR measurements to attest to
a trusted state and release secrets
2. At some point in time the attacker could reset the TPM, clearing
the PCRs and then send down their own measurements which would
effectively overwrite the boot time measurements the TPM has
already done.
The first can be thwarted by always doing HMAC protection of the PCR
extend and read command meaning measurement values cannot be
substituted without producing a detectable HMAC failure in the
response. However, the second can only really be detected by relying
on some sort of mechanism for protection which would change over TPM
reset.
Secrets Guarding
----------------
Certain information passing in and out of the TPM, such as key sealing
and private key import and random number generation, is vulnerable to
interception which HMAC protection alone cannot protect against, so
for these types of command we must also employ request and response
encryption to prevent the loss of secret information.
Establishing Initial Trust with the TPM
---------------------------------------
In order to provide security from the beginning, an initial shared or
asymmetric secret must be established which must also be unknown to
the attacker. The most obvious avenues for this are the endorsement
and storage seeds, which can be used to derive asymmetric keys.
However, using these keys is difficult because the only way to pass
them into the kernel would be on the command line, which requires
extensive support in the boot system, and there's no guarantee that
either hierarchy would not have some type of authorization.
The mechanism chosen for the Linux Kernel is to derive the primary
elliptic curve key from the null seed using the standard storage seed
parameters. The null seed has two advantages: firstly the hierarchy
physically cannot have an authorization, so we are always able to use
it and secondly, the null seed changes across TPM resets, meaning if
we establish trust on the null seed at start of day, all sessions
salted with the derived key will fail if the TPM is reset and the seed
changes.
Obviously using the null seed without any other prior shared secrets,
we have to create and read the initial public key which could, of
course, be intercepted and substituted by the bus interposer.
However, the TPM has a key certification mechanism (using the EK
endorsement certificate, creating an attestation identity key and
certifying the null seed primary with that key) which is too complex
to run within the kernel, so we keep a copy of the null primary key
name, which is what is exported via sysfs so user-space can run the
full certification when it boots. The definitive guarantee here is
that if the null primary key certifies correctly, you know all your
TPM transactions since start of day were secure and if it doesn't, you
know there's an interposer on your system (and that any secret used
during boot may have been leaked).
Stacking Trust
--------------
In the current null primary scenario, the TPM must be completely
cleared before handing it on to the next consumer. However the kernel
hands to user-space the name of the derived null seed key which can
then be verified by certification in user-space. Therefore, this chain
of name handoff can be used between the various boot components as
well (via an unspecified mechanism). For instance, grub could use the
null seed scheme for security and hand the name off to the kernel in
the boot area. The kernel could make its own derivation of the key
and the name and know definitively that if they differ from the handed
off version that tampering has occurred. Thus it becomes possible to
chain arbitrary boot components together (UEFI to grub to kernel) via
the name handoff provided each successive component knows how to
collect the name and verifies it against its derived key.
Session Properties
------------------
All TPM commands the kernel uses allow sessions. HMAC sessions may be
used to check the integrity of requests and responses and decrypt and
encrypt flags may be used to shield parameters and responses. The
HMAC and encryption keys are usually derived from the shared
authorization secret, but for a lot of kernel operations that is well
known (and usually empty). Thus, every HMAC session used by the
kernel must be created using the null primary key as the salt key
which thus provides a cryptographic input into the session key
derivation. Thus, the kernel creates the null primary key once (as a
volatile TPM handle) and keeps it around in a saved context stored in
tpm_chip for every in-kernel use of the TPM. Currently, because of a
lack of de-gapping in the in-kernel resource manager, the session must
be created and destroyed for each operation, but, in future, a single
session may also be reused for the in-kernel HMAC, encryption and
decryption sessions.
Protection Types
----------------
For every in-kernel operation we use null primary salted HMAC to
protect the integrity. Additionally, we use parameter encryption to
protect key sealing and parameter decryption to protect key unsealing
and random number generation.
Null Primary Key Certification in Userspace
===========================================
Every TPM comes shipped with a couple of X.509 certificates for the
primary endorsement key. This document assumes that the Elliptic
Curve version of the certificate exists at 01C00002, but will work
equally well with the RSA certificate (at 01C00001).
The first step in the certification is primary creation using the
template from the `TCG EK Credential Profile`_ which allows comparison
of the generated primary key against the one in the certificate (the
public key must match). Note that generation of the EK primary
requires the EK hierarchy password, but a pre-generated version of the
EC primary should exist at 81010002 and a TPM2_ReadPublic() may be
performed on this without needing the key authority. Next, the
certificate itself must be verified to chain back to the manufacturer
root (which should be published on the manufacturer website). Once
this is done, an attestation key (AK) is generated within the TPM and
it's name and the EK public key can be used to encrypt a secret using
TPM2_MakeCredential. The TPM then runs TPM2_ActivateCredential which
will only recover the secret if the binding between the TPM, the EK
and the AK is true. the generated AK may now be used to run a
certification of the null primary key whose name the kernel has
exported. Since TPM2_MakeCredential/ActivateCredential are somewhat
complicated, a more simplified process involving an externally
generated private key is described below.
This process is a simplified abbreviation of the usual privacy CA
based attestation process. The assumption here is that the
attestation is done by the TPM owner who thus has access to only the
owner hierarchy. The owner creates an external public/private key
pair (assume elliptic curve in this case) and wraps the private key
for import using an inner wrapping process and parented to the EC
derived storage primary. The TPM2_Import() is done using a parameter
decryption HMAC session salted to the EK primary (which also does not
require the EK key authority) meaning that the inner wrapping key is
the encrypted parameter and thus the TPM will not be able to perform
the import unless is possesses the certified EK so if the command
succeeds and the HMAC verifies on return we know we have a loadable
copy of the private key only for the certified TPM. This key is now
loaded into the TPM and the Storage primary flushed (to free up space
for the null key generation).
The null EC primary is now generated using the Storage profile
outlined in the `TCG TPM v2.0 Provisioning Guidance`_; the name of
this key (the hash of the public area) is computed and compared to the
null seed name presented by the kernel in
/sys/class/tpm/tpm0/null_name. If the names do not match, the TPM is
compromised. If the names match, the user performs a TPM2_Certify()
using the null primary as the object handle and the loaded private key
as the sign handle and providing randomized qualifying data. The
signature of the returned certifyInfo is verified against the public
part of the loaded private key and the qualifying data checked to
prevent replay. If all of these tests pass, the user is now assured
that TPM integrity and privacy was preserved across the entire boot
sequence of this kernel.
.. _TPM Genie: https://www.nccgroup.trust/globalassets/about-us/us/documents/tpm-genie.pdf
.. _Windows Bitlocker TPM: https://dolosgroup.io/blog/2021/7/9/from-stolen-laptop-to-inside-the-company-network
.. _attack against TPM based Linux disk encryption: https://www.secura.com/blog/tpm-sniffing-attacks-against-non-bitlocker-targets
.. _TCG EK Credential Profile: https://trustedcomputinggroup.org/resource/tcg-ek-credential-profile-for-tpm-family-2-0/
.. _TCG TPM v2.0 Provisioning Guidance: https://trustedcomputinggroup.org/resource/tcg-tpm-v2-0-provisioning-guidance/

View File

@ -0,0 +1,46 @@
.. SPDX-License-Identifier: GPL-2.0
=========================
TPM FIFO interface driver
=========================
TCG PTP Specification defines two interface types: FIFO and CRB. The former is
based on sequenced read and write operations, and the latter is based on a
buffer containing the full command or response.
FIFO (First-In-First-Out) interface is used by the tpm_tis_core dependent
drivers. Originally Linux had only a driver called tpm_tis, which covered
memory mapped (aka MMIO) interface but it was later on extended to cover other
physical interfaces supported by the TCG standard.
For historical reasons above the original MMIO driver is called tpm_tis and the
framework for FIFO drivers is named as tpm_tis_core. The postfix "tis" in
tpm_tis comes from the TPM Interface Specification, which is the hardware
interface specification for TPM 1.x chips.
Communication is based on a 20 KiB buffer shared by the TPM chip through a
hardware bus or memory map, depending on the physical wiring. The buffer is
further split into five equal-size 4 KiB buffers, which provide equivalent
sets of registers for communication between the CPU and TPM. These
communication endpoints are called localities in the TCG terminology.
When the kernel wants to send commands to the TPM chip, it first reserves
locality 0 by setting the requestUse bit in the TPM_ACCESS register. The bit is
cleared by the chip when the access is granted. Once it completes its
communication, the kernel writes the TPM_ACCESS.activeLocality bit. This
informs the chip that the locality has been relinquished.
Pending localities are served in order by the chip in descending order, one at
a time:
- Locality 0 has the lowest priority.
- Locality 5 has the highest priority.
Further information on the purpose and meaning of the localities can be found
in section 3.2 of the TCG PC Client Platform TPM Profile Specification.
References
==========
TCG PC Client Platform TPM Profile (PTP) Specification
https://trustedcomputinggroup.org/resource/pc-client-platform-tpm-profile-ptp-specification/

View File

@ -27,6 +27,20 @@ menuconfig TCG_TPM
if TCG_TPM
config TCG_TPM2_HMAC
bool "Use HMAC and encrypted transactions on the TPM bus"
default y
select CRYPTO_ECDH
select CRYPTO_LIB_AESCFB
select CRYPTO_LIB_SHA256
help
Setting this causes us to deploy a scheme which uses request
and response HMACs in addition to encryption for
communicating with the TPM to prevent or detect bus snooping
and interposer attacks (see tpm-security.rst). Saying Y
here adds some encryption overhead to all kernel to TPM
transactions.
config HW_RANDOM_TPM
bool "TPM HW Random Number Generator support"
depends on TCG_TPM && HW_RANDOM && !(TCG_TPM=y && HW_RANDOM=m)
@ -149,6 +163,7 @@ config TCG_NSC
config TCG_ATMEL
tristate "Atmel TPM Interface"
depends on PPC64 || HAS_IOPORT_MAP
depends on HAS_IOPORT
help
If you have a TPM security chip from Atmel say Yes and it
will be accessible from within Linux. To compile this driver
@ -156,7 +171,7 @@ config TCG_ATMEL
config TCG_INFINEON
tristate "Infineon Technologies TPM Interface"
depends on PNP
depends on PNP || COMPILE_TEST
help
If you have a TPM security chip from Infineon Technologies
(either SLD 9630 TT 1.1 or SLB 9635 TT 1.2) say Yes and it

View File

@ -15,7 +15,9 @@ tpm-y += tpm-sysfs.o
tpm-y += eventlog/common.o
tpm-y += eventlog/tpm1.o
tpm-y += eventlog/tpm2.o
tpm-y += tpm-buf.o
tpm-$(CONFIG_TCG_TPM2_HMAC) += tpm2-sessions.o
tpm-$(CONFIG_ACPI) += tpm_ppi.o eventlog/acpi.o
tpm-$(CONFIG_EFI) += eventlog/efi.o
tpm-$(CONFIG_OF) += eventlog/of.o

View File

@ -142,7 +142,6 @@ int tpm_read_log_acpi(struct tpm_chip *chip)
log->bios_event_log_end = log->bios_event_log + len;
ret = -EIO;
virt = acpi_os_map_iomem(start, len);
if (!virt) {
dev_warn(&chip->dev, "%s: Failed to map ACPI memory\n", __func__);

252
drivers/char/tpm/tpm-buf.c Normal file
View File

@ -0,0 +1,252 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Handling of TPM command and other buffers.
*/
#include <linux/tpm_command.h>
#include <linux/module.h>
#include <linux/tpm.h>
/**
* tpm_buf_init() - Allocate and initialize a TPM command
* @buf: A &tpm_buf
* @tag: TPM_TAG_RQU_COMMAND, TPM2_ST_NO_SESSIONS or TPM2_ST_SESSIONS
* @ordinal: A command ordinal
*
* Return: 0 or -ENOMEM
*/
int tpm_buf_init(struct tpm_buf *buf, u16 tag, u32 ordinal)
{
buf->data = (u8 *)__get_free_page(GFP_KERNEL);
if (!buf->data)
return -ENOMEM;
tpm_buf_reset(buf, tag, ordinal);
return 0;
}
EXPORT_SYMBOL_GPL(tpm_buf_init);
/**
* tpm_buf_reset() - Initialize a TPM command
* @buf: A &tpm_buf
* @tag: TPM_TAG_RQU_COMMAND, TPM2_ST_NO_SESSIONS or TPM2_ST_SESSIONS
* @ordinal: A command ordinal
*/
void tpm_buf_reset(struct tpm_buf *buf, u16 tag, u32 ordinal)
{
struct tpm_header *head = (struct tpm_header *)buf->data;
WARN_ON(tag != TPM_TAG_RQU_COMMAND && tag != TPM2_ST_NO_SESSIONS &&
tag != TPM2_ST_SESSIONS && tag != 0);
buf->flags = 0;
buf->length = sizeof(*head);
head->tag = cpu_to_be16(tag);
head->length = cpu_to_be32(sizeof(*head));
head->ordinal = cpu_to_be32(ordinal);
buf->handles = 0;
}
EXPORT_SYMBOL_GPL(tpm_buf_reset);
/**
* tpm_buf_init_sized() - Allocate and initialize a sized (TPM2B) buffer
* @buf: A @tpm_buf
*
* Return: 0 or -ENOMEM
*/
int tpm_buf_init_sized(struct tpm_buf *buf)
{
buf->data = (u8 *)__get_free_page(GFP_KERNEL);
if (!buf->data)
return -ENOMEM;
tpm_buf_reset_sized(buf);
return 0;
}
EXPORT_SYMBOL_GPL(tpm_buf_init_sized);
/**
* tpm_buf_reset_sized() - Initialize a sized buffer
* @buf: A &tpm_buf
*/
void tpm_buf_reset_sized(struct tpm_buf *buf)
{
buf->flags = TPM_BUF_TPM2B;
buf->length = 2;
buf->data[0] = 0;
buf->data[1] = 0;
}
EXPORT_SYMBOL_GPL(tpm_buf_reset_sized);
void tpm_buf_destroy(struct tpm_buf *buf)
{
free_page((unsigned long)buf->data);
}
EXPORT_SYMBOL_GPL(tpm_buf_destroy);
/**
* tpm_buf_length() - Return the number of bytes consumed by the data
* @buf: A &tpm_buf
*
* Return: The number of bytes consumed by the buffer
*/
u32 tpm_buf_length(struct tpm_buf *buf)
{
return buf->length;
}
EXPORT_SYMBOL_GPL(tpm_buf_length);
/**
* tpm_buf_append() - Append data to an initialized buffer
* @buf: A &tpm_buf
* @new_data: A data blob
* @new_length: Size of the appended data
*/
void tpm_buf_append(struct tpm_buf *buf, const u8 *new_data, u16 new_length)
{
/* Return silently if overflow has already happened. */
if (buf->flags & TPM_BUF_OVERFLOW)
return;
if ((buf->length + new_length) > PAGE_SIZE) {
WARN(1, "tpm_buf: write overflow\n");
buf->flags |= TPM_BUF_OVERFLOW;
return;
}
memcpy(&buf->data[buf->length], new_data, new_length);
buf->length += new_length;
if (buf->flags & TPM_BUF_TPM2B)
((__be16 *)buf->data)[0] = cpu_to_be16(buf->length - 2);
else
((struct tpm_header *)buf->data)->length = cpu_to_be32(buf->length);
}
EXPORT_SYMBOL_GPL(tpm_buf_append);
void tpm_buf_append_u8(struct tpm_buf *buf, const u8 value)
{
tpm_buf_append(buf, &value, 1);
}
EXPORT_SYMBOL_GPL(tpm_buf_append_u8);
void tpm_buf_append_u16(struct tpm_buf *buf, const u16 value)
{
__be16 value2 = cpu_to_be16(value);
tpm_buf_append(buf, (u8 *)&value2, 2);
}
EXPORT_SYMBOL_GPL(tpm_buf_append_u16);
void tpm_buf_append_u32(struct tpm_buf *buf, const u32 value)
{
__be32 value2 = cpu_to_be32(value);
tpm_buf_append(buf, (u8 *)&value2, 4);
}
EXPORT_SYMBOL_GPL(tpm_buf_append_u32);
/**
* tpm_buf_read() - Read from a TPM buffer
* @buf: &tpm_buf instance
* @offset: offset within the buffer
* @count: the number of bytes to read
* @output: the output buffer
*/
static void tpm_buf_read(struct tpm_buf *buf, off_t *offset, size_t count, void *output)
{
off_t next_offset;
/* Return silently if overflow has already happened. */
if (buf->flags & TPM_BUF_BOUNDARY_ERROR)
return;
next_offset = *offset + count;
if (next_offset > buf->length) {
WARN(1, "tpm_buf: read out of boundary\n");
buf->flags |= TPM_BUF_BOUNDARY_ERROR;
return;
}
memcpy(output, &buf->data[*offset], count);
*offset = next_offset;
}
/**
* tpm_buf_read_u8() - Read 8-bit word from a TPM buffer
* @buf: &tpm_buf instance
* @offset: offset within the buffer
*
* Return: next 8-bit word
*/
u8 tpm_buf_read_u8(struct tpm_buf *buf, off_t *offset)
{
u8 value;
tpm_buf_read(buf, offset, sizeof(value), &value);
return value;
}
EXPORT_SYMBOL_GPL(tpm_buf_read_u8);
/**
* tpm_buf_read_u16() - Read 16-bit word from a TPM buffer
* @buf: &tpm_buf instance
* @offset: offset within the buffer
*
* Return: next 16-bit word
*/
u16 tpm_buf_read_u16(struct tpm_buf *buf, off_t *offset)
{
u16 value;
tpm_buf_read(buf, offset, sizeof(value), &value);
return be16_to_cpu(value);
}
EXPORT_SYMBOL_GPL(tpm_buf_read_u16);
/**
* tpm_buf_read_u32() - Read 32-bit word from a TPM buffer
* @buf: &tpm_buf instance
* @offset: offset within the buffer
*
* Return: next 32-bit word
*/
u32 tpm_buf_read_u32(struct tpm_buf *buf, off_t *offset)
{
u32 value;
tpm_buf_read(buf, offset, sizeof(value), &value);
return be32_to_cpu(value);
}
EXPORT_SYMBOL_GPL(tpm_buf_read_u32);
static u16 tpm_buf_tag(struct tpm_buf *buf)
{
struct tpm_header *head = (struct tpm_header *)buf->data;
return be16_to_cpu(head->tag);
}
/**
* tpm_buf_parameters - return the TPM response parameters area of the tpm_buf
* @buf: tpm_buf to use
*
* Where the parameters are located depends on the tag of a TPM
* command (it's immediately after the header for TPM_ST_NO_SESSIONS
* or 4 bytes after for TPM_ST_SESSIONS). Evaluate this and return a
* pointer to the first byte of the parameters area.
*
* @return: pointer to parameters area
*/
u8 *tpm_buf_parameters(struct tpm_buf *buf)
{
int offset = TPM_HEADER_SIZE;
if (tpm_buf_tag(buf) == TPM2_ST_SESSIONS)
offset += 4;
return &buf->data[offset];
}

View File

@ -158,6 +158,9 @@ int tpm_try_get_ops(struct tpm_chip *chip)
{
int rc = -EIO;
if (chip->flags & TPM_CHIP_FLAG_DISABLE)
return rc;
get_device(&chip->dev);
down_read(&chip->ops_sem);
@ -275,6 +278,9 @@ static void tpm_dev_release(struct device *dev)
kfree(chip->work_space.context_buf);
kfree(chip->work_space.session_buf);
kfree(chip->allocated_banks);
#ifdef CONFIG_TCG_TPM2_HMAC
kfree(chip->auth);
#endif
kfree(chip);
}

View File

@ -232,6 +232,7 @@ ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_buf *buf,
if (len < min_rsp_body_length + TPM_HEADER_SIZE)
return -EFAULT;
buf->length = len;
return 0;
}
EXPORT_SYMBOL_GPL(tpm_transmit_cmd);
@ -342,31 +343,6 @@ out:
}
EXPORT_SYMBOL_GPL(tpm_pcr_extend);
/**
* tpm_send - send a TPM command
* @chip: a &struct tpm_chip instance, %NULL for the default chip
* @cmd: a TPM command buffer
* @buflen: the length of the TPM command buffer
*
* Return: same as with tpm_transmit_cmd()
*/
int tpm_send(struct tpm_chip *chip, void *cmd, size_t buflen)
{
struct tpm_buf buf;
int rc;
chip = tpm_find_get_ops(chip);
if (!chip)
return -ENODEV;
buf.data = cmd;
rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to a send a command");
tpm_put_ops(chip);
return rc;
}
EXPORT_SYMBOL_GPL(tpm_send);
int tpm_auto_startup(struct tpm_chip *chip)
{
int rc;

View File

@ -309,6 +309,21 @@ static ssize_t tpm_version_major_show(struct device *dev,
}
static DEVICE_ATTR_RO(tpm_version_major);
#ifdef CONFIG_TCG_TPM2_HMAC
static ssize_t null_name_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct tpm_chip *chip = to_tpm_chip(dev);
int size = TPM2_NAME_SIZE;
bin2hex(buf, chip->null_key_name, size);
size *= 2;
buf[size++] = '\n';
return size;
}
static DEVICE_ATTR_RO(null_name);
#endif
static struct attribute *tpm1_dev_attrs[] = {
&dev_attr_pubek.attr,
&dev_attr_pcrs.attr,
@ -326,6 +341,9 @@ static struct attribute *tpm1_dev_attrs[] = {
static struct attribute *tpm2_dev_attrs[] = {
&dev_attr_tpm_version_major.attr,
#ifdef CONFIG_TCG_TPM2_HMAC
&dev_attr_null_name.attr,
#endif
NULL
};

View File

@ -312,9 +312,23 @@ int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space, void *buf,
size_t *bufsiz);
int tpm_devs_add(struct tpm_chip *chip);
void tpm_devs_remove(struct tpm_chip *chip);
int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf,
unsigned int buf_size, unsigned int *offset);
int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
unsigned int *offset, u32 *handle);
void tpm_bios_log_setup(struct tpm_chip *chip);
void tpm_bios_log_teardown(struct tpm_chip *chip);
int tpm_dev_common_init(void);
void tpm_dev_common_exit(void);
#ifdef CONFIG_TCG_TPM2_HMAC
int tpm2_sessions_init(struct tpm_chip *chip);
#else
static inline int tpm2_sessions_init(struct tpm_chip *chip)
{
return 0;
}
#endif
#endif

View File

@ -216,13 +216,6 @@ out:
return rc;
}
struct tpm2_null_auth_area {
__be32 handle;
__be16 nonce_size;
u8 attributes;
__be16 auth_size;
} __packed;
/**
* tpm2_pcr_extend() - extend a PCR value
*
@ -236,24 +229,22 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
struct tpm_digest *digests)
{
struct tpm_buf buf;
struct tpm2_null_auth_area auth_area;
int rc;
int i;
rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND);
rc = tpm2_start_auth_session(chip);
if (rc)
return rc;
tpm_buf_append_u32(&buf, pcr_idx);
rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND);
if (rc) {
tpm2_end_auth_session(chip);
return rc;
}
auth_area.handle = cpu_to_be32(TPM2_RS_PW);
auth_area.nonce_size = 0;
auth_area.attributes = 0;
auth_area.auth_size = 0;
tpm_buf_append_name(chip, &buf, pcr_idx, NULL);
tpm_buf_append_hmac_session(chip, &buf, 0, NULL, 0);
tpm_buf_append_u32(&buf, sizeof(struct tpm2_null_auth_area));
tpm_buf_append(&buf, (const unsigned char *)&auth_area,
sizeof(auth_area));
tpm_buf_append_u32(&buf, chip->nr_allocated_banks);
for (i = 0; i < chip->nr_allocated_banks; i++) {
@ -262,7 +253,9 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
chip->allocated_banks[i].digest_size);
}
tpm_buf_fill_hmac_session(chip, &buf);
rc = tpm_transmit_cmd(chip, &buf, 0, "attempting extend a PCR value");
rc = tpm_buf_check_hmac_response(chip, &buf, rc);
tpm_buf_destroy(&buf);
@ -299,25 +292,35 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
if (!num_bytes || max > TPM_MAX_RNG_DATA)
return -EINVAL;
err = tpm_buf_init(&buf, 0, 0);
err = tpm2_start_auth_session(chip);
if (err)
return err;
err = tpm_buf_init(&buf, 0, 0);
if (err) {
tpm2_end_auth_session(chip);
return err;
}
do {
tpm_buf_reset(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_RANDOM);
tpm_buf_reset(&buf, TPM2_ST_SESSIONS, TPM2_CC_GET_RANDOM);
tpm_buf_append_hmac_session_opt(chip, &buf, TPM2_SA_ENCRYPT
| TPM2_SA_CONTINUE_SESSION,
NULL, 0);
tpm_buf_append_u16(&buf, num_bytes);
tpm_buf_fill_hmac_session(chip, &buf);
err = tpm_transmit_cmd(chip, &buf,
offsetof(struct tpm2_get_random_out,
buffer),
"attempting get random");
err = tpm_buf_check_hmac_response(chip, &buf, err);
if (err) {
if (err > 0)
err = -EIO;
goto out;
}
out = (struct tpm2_get_random_out *)
&buf.data[TPM_HEADER_SIZE];
out = (struct tpm2_get_random_out *)tpm_buf_parameters(&buf);
recd = min_t(u32, be16_to_cpu(out->size), num_bytes);
if (tpm_buf_length(&buf) <
TPM_HEADER_SIZE +
@ -334,9 +337,12 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
} while (retries-- && total < max);
tpm_buf_destroy(&buf);
tpm2_end_auth_session(chip);
return total ? total : -EIO;
out:
tpm_buf_destroy(&buf);
tpm2_end_auth_session(chip);
return err;
}
@ -759,6 +765,11 @@ int tpm2_auto_startup(struct tpm_chip *chip)
rc = 0;
}
if (rc)
goto out;
rc = tpm2_sessions_init(chip);
out:
/*
* Infineon TPM in field upgrade mode will return no data for the number

File diff suppressed because it is too large Load Diff

View File

@ -68,8 +68,8 @@ void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space)
kfree(space->session_buf);
}
static int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
unsigned int *offset, u32 *handle)
int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
unsigned int *offset, u32 *handle)
{
struct tpm_buf tbuf;
struct tpm2_context *ctx;
@ -105,6 +105,9 @@ static int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
*handle = 0;
tpm_buf_destroy(&tbuf);
return -ENOENT;
} else if (tpm2_rc_value(rc) == TPM2_RC_INTEGRITY) {
tpm_buf_destroy(&tbuf);
return -EINVAL;
} else if (rc > 0) {
dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",
__func__, rc);
@ -119,8 +122,8 @@ static int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
return 0;
}
static int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf,
unsigned int buf_size, unsigned int *offset)
int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf,
unsigned int buf_size, unsigned int *offset)
{
struct tpm_buf tbuf;
unsigned int body_size;

View File

@ -51,34 +51,40 @@ static struct tpm_inf_dev tpm_dev;
static inline void tpm_data_out(unsigned char data, unsigned char offset)
{
#ifdef CONFIG_HAS_IOPORT
if (tpm_dev.iotype == TPM_INF_IO_PORT)
outb(data, tpm_dev.data_regs + offset);
else
#endif
writeb(data, tpm_dev.mem_base + tpm_dev.data_regs + offset);
}
static inline unsigned char tpm_data_in(unsigned char offset)
{
#ifdef CONFIG_HAS_IOPORT
if (tpm_dev.iotype == TPM_INF_IO_PORT)
return inb(tpm_dev.data_regs + offset);
else
return readb(tpm_dev.mem_base + tpm_dev.data_regs + offset);
#endif
return readb(tpm_dev.mem_base + tpm_dev.data_regs + offset);
}
static inline void tpm_config_out(unsigned char data, unsigned char offset)
{
#ifdef CONFIG_HAS_IOPORT
if (tpm_dev.iotype == TPM_INF_IO_PORT)
outb(data, tpm_dev.config_port + offset);
else
#endif
writeb(data, tpm_dev.mem_base + tpm_dev.index_off + offset);
}
static inline unsigned char tpm_config_in(unsigned char offset)
{
#ifdef CONFIG_HAS_IOPORT
if (tpm_dev.iotype == TPM_INF_IO_PORT)
return inb(tpm_dev.config_port + offset);
else
return readb(tpm_dev.mem_base + tpm_dev.index_off + offset);
#endif
return readb(tpm_dev.mem_base + tpm_dev.index_off + offset);
}
/* TPM header definitions */

View File

@ -1057,11 +1057,6 @@ static void tpm_tis_clkrun_enable(struct tpm_chip *chip, bool value)
clkrun_val &= ~LPC_CLKRUN_EN;
iowrite32(clkrun_val, data->ilb_base_addr + LPC_CNTRL_OFFSET);
/*
* Write any random value on port 0x80 which is on LPC, to make
* sure LPC clock is running before sending any TPM command.
*/
outb(0xCC, 0x80);
} else {
data->clkrun_enabled--;
if (data->clkrun_enabled)
@ -1072,13 +1067,15 @@ static void tpm_tis_clkrun_enable(struct tpm_chip *chip, bool value)
/* Enable LPC CLKRUN# */
clkrun_val |= LPC_CLKRUN_EN;
iowrite32(clkrun_val, data->ilb_base_addr + LPC_CNTRL_OFFSET);
/*
* Write any random value on port 0x80 which is on LPC, to make
* sure LPC clock is running before sending any TPM command.
*/
outb(0xCC, 0x80);
}
#ifdef CONFIG_HAS_IOPORT
/*
* Write any random value on port 0x80 which is on LPC, to make
* sure LPC clock is running before sending any TPM command.
*/
outb(0xCC, 0x80);
#endif
}
static const struct tpm_class_ops tpm_tis = {

View File

@ -87,4 +87,9 @@ void aes_decrypt(const struct crypto_aes_ctx *ctx, u8 *out, const u8 *in);
extern const u8 crypto_aes_sbox[];
extern const u8 crypto_aes_inv_sbox[];
void aescfb_encrypt(const struct crypto_aes_ctx *ctx, u8 *dst, const u8 *src,
int len, const u8 iv[AES_BLOCK_SIZE]);
void aescfb_decrypt(const struct crypto_aes_ctx *ctx, u8 *dst, const u8 *src,
int len, const u8 iv[AES_BLOCK_SIZE]);
#endif

View File

@ -6,8 +6,6 @@
#include <linux/tpm_command.h>
/* implementation specific TPM constants */
#define MAX_BUF_SIZE 1024
#define TPM_GETRANDOM_SIZE 14
#define TPM_SIZE_OFFSET 2
#define TPM_RETURN_OFFSET 6
#define TPM_DATA_OFFSET 10

View File

@ -23,6 +23,7 @@
#include <linux/fs.h>
#include <linux/highmem.h>
#include <crypto/hash_info.h>
#include <crypto/aes.h>
#define TPM_DIGEST_SIZE 20 /* Max TPM v1.2 PCR size */
#define TPM_MAX_DIGEST_SIZE SHA512_DIGEST_SIZE
@ -30,17 +31,28 @@
struct tpm_chip;
struct trusted_key_payload;
struct trusted_key_options;
/* opaque structure, holds auth session parameters like the session key */
struct tpm2_auth;
enum tpm2_session_types {
TPM2_SE_HMAC = 0x00,
TPM2_SE_POLICY = 0x01,
TPM2_SE_TRIAL = 0x02,
};
/* if you add a new hash to this, increment TPM_MAX_HASHES below */
enum tpm_algorithms {
TPM_ALG_ERROR = 0x0000,
TPM_ALG_SHA1 = 0x0004,
TPM_ALG_AES = 0x0006,
TPM_ALG_KEYEDHASH = 0x0008,
TPM_ALG_SHA256 = 0x000B,
TPM_ALG_SHA384 = 0x000C,
TPM_ALG_SHA512 = 0x000D,
TPM_ALG_NULL = 0x0010,
TPM_ALG_SM3_256 = 0x0012,
TPM_ALG_ECC = 0x0023,
TPM_ALG_CFB = 0x0043,
};
/*
@ -49,6 +61,11 @@ enum tpm_algorithms {
*/
#define TPM_MAX_HASHES 5
enum tpm2_curves {
TPM2_ECC_NONE = 0x0000,
TPM2_ECC_NIST_P256 = 0x0003,
};
struct tpm_digest {
u16 alg_id;
u8 digest[TPM_MAX_DIGEST_SIZE];
@ -116,6 +133,20 @@ struct tpm_chip_seqops {
const struct seq_operations *seqops;
};
/* fixed define for the curve we use which is NIST_P256 */
#define EC_PT_SZ 32
/*
* fixed define for the size of a name. This is actually HASHALG size
* plus 2, so 32 for SHA256
*/
#define TPM2_NAME_SIZE 34
/*
* The maximum size for an object context
*/
#define TPM2_MAX_CONTEXT_SIZE 4096
struct tpm_chip {
struct device dev;
struct device devs;
@ -170,6 +201,18 @@ struct tpm_chip {
/* active locality */
int locality;
#ifdef CONFIG_TCG_TPM2_HMAC
/* details for communication security via sessions */
/* saved context for NULL seed */
u8 null_key_context[TPM2_MAX_CONTEXT_SIZE];
/* name of NULL seed */
u8 null_key_name[TPM2_NAME_SIZE];
u8 null_ec_key_x[EC_PT_SZ];
u8 null_ec_key_y[EC_PT_SZ];
struct tpm2_auth *auth;
#endif
};
#define TPM_HEADER_SIZE 10
@ -194,6 +237,7 @@ enum tpm2_timeouts {
enum tpm2_structures {
TPM2_ST_NO_SESSIONS = 0x8001,
TPM2_ST_SESSIONS = 0x8002,
TPM2_ST_CREATION = 0x8021,
};
/* Indicates from what layer of the software stack the error comes from */
@ -204,6 +248,7 @@ enum tpm2_return_codes {
TPM2_RC_SUCCESS = 0x0000,
TPM2_RC_HASH = 0x0083, /* RC_FMT1 */
TPM2_RC_HANDLE = 0x008B,
TPM2_RC_INTEGRITY = 0x009F,
TPM2_RC_INITIALIZE = 0x0100, /* RC_VER1 */
TPM2_RC_FAILURE = 0x0101,
TPM2_RC_DISABLED = 0x0120,
@ -231,6 +276,8 @@ enum tpm2_command_codes {
TPM2_CC_CONTEXT_LOAD = 0x0161,
TPM2_CC_CONTEXT_SAVE = 0x0162,
TPM2_CC_FLUSH_CONTEXT = 0x0165,
TPM2_CC_READ_PUBLIC = 0x0173,
TPM2_CC_START_AUTH_SESS = 0x0176,
TPM2_CC_VERIFY_SIGNATURE = 0x0177,
TPM2_CC_GET_CAPABILITY = 0x017A,
TPM2_CC_GET_RANDOM = 0x017B,
@ -243,9 +290,25 @@ enum tpm2_command_codes {
};
enum tpm2_permanent_handles {
TPM2_RH_NULL = 0x40000007,
TPM2_RS_PW = 0x40000009,
};
/* Most Significant Octet for key types */
enum tpm2_mso_type {
TPM2_MSO_NVRAM = 0x01,
TPM2_MSO_SESSION = 0x02,
TPM2_MSO_POLICY = 0x03,
TPM2_MSO_PERMANENT = 0x40,
TPM2_MSO_VOLATILE = 0x80,
TPM2_MSO_PERSISTENT = 0x81,
};
static inline enum tpm2_mso_type tpm2_handle_mso(u32 handle)
{
return handle >> 24;
}
enum tpm2_capabilities {
TPM2_CAP_HANDLES = 1,
TPM2_CAP_COMMANDS = 2,
@ -284,6 +347,7 @@ enum tpm_chip_flags {
TPM_CHIP_FLAG_FIRMWARE_UPGRADE = BIT(7),
TPM_CHIP_FLAG_SUSPENDED = BIT(8),
TPM_CHIP_FLAG_HWRNG_DISABLED = BIT(9),
TPM_CHIP_FLAG_DISABLE = BIT(10),
};
#define to_tpm_chip(d) container_of(d, struct tpm_chip, dev)
@ -297,28 +361,61 @@ struct tpm_header {
};
} __packed;
/* A string buffer type for constructing TPM commands. This is based on the
* ideas of string buffer code in security/keys/trusted.h but is heap based
* in order to keep the stack usage minimal.
*/
enum tpm_buf_flags {
/* the capacity exceeded: */
TPM_BUF_OVERFLOW = BIT(0),
/* TPM2B format: */
TPM_BUF_TPM2B = BIT(1),
/* read out of boundary: */
TPM_BUF_BOUNDARY_ERROR = BIT(2),
};
/*
* A string buffer type for constructing TPM commands.
*/
struct tpm_buf {
unsigned int flags;
u32 flags;
u32 length;
u8 *data;
u8 handles;
};
enum tpm2_object_attributes {
TPM2_OA_FIXED_TPM = BIT(1),
TPM2_OA_ST_CLEAR = BIT(2),
TPM2_OA_FIXED_PARENT = BIT(4),
TPM2_OA_SENSITIVE_DATA_ORIGIN = BIT(5),
TPM2_OA_USER_WITH_AUTH = BIT(6),
TPM2_OA_ADMIN_WITH_POLICY = BIT(7),
TPM2_OA_NO_DA = BIT(10),
TPM2_OA_ENCRYPTED_DUPLICATION = BIT(11),
TPM2_OA_RESTRICTED = BIT(16),
TPM2_OA_DECRYPT = BIT(17),
TPM2_OA_SIGN = BIT(18),
};
/*
* definitions for the canonical template. These are mandated
* by the TCG key template documents
*/
#define AES_KEY_BYTES AES_KEYSIZE_128
#define AES_KEY_BITS (AES_KEY_BYTES*8)
#define TPM2_OA_TMPL (TPM2_OA_NO_DA | \
TPM2_OA_FIXED_TPM | \
TPM2_OA_FIXED_PARENT | \
TPM2_OA_SENSITIVE_DATA_ORIGIN | \
TPM2_OA_USER_WITH_AUTH | \
TPM2_OA_DECRYPT | \
TPM2_OA_RESTRICTED)
enum tpm2_session_attributes {
TPM2_SA_CONTINUE_SESSION = BIT(0),
TPM2_SA_AUDIT_EXCLUSIVE = BIT(1),
TPM2_SA_AUDIT_RESET = BIT(3),
TPM2_SA_DECRYPT = BIT(5),
TPM2_SA_ENCRYPT = BIT(6),
TPM2_SA_AUDIT = BIT(7),
};
struct tpm2_hash {
@ -326,84 +423,21 @@ struct tpm2_hash {
unsigned int tpm_id;
};
static inline void tpm_buf_reset(struct tpm_buf *buf, u16 tag, u32 ordinal)
{
struct tpm_header *head = (struct tpm_header *)buf->data;
int tpm_buf_init(struct tpm_buf *buf, u16 tag, u32 ordinal);
void tpm_buf_reset(struct tpm_buf *buf, u16 tag, u32 ordinal);
int tpm_buf_init_sized(struct tpm_buf *buf);
void tpm_buf_reset_sized(struct tpm_buf *buf);
void tpm_buf_destroy(struct tpm_buf *buf);
u32 tpm_buf_length(struct tpm_buf *buf);
void tpm_buf_append(struct tpm_buf *buf, const u8 *new_data, u16 new_length);
void tpm_buf_append_u8(struct tpm_buf *buf, const u8 value);
void tpm_buf_append_u16(struct tpm_buf *buf, const u16 value);
void tpm_buf_append_u32(struct tpm_buf *buf, const u32 value);
u8 tpm_buf_read_u8(struct tpm_buf *buf, off_t *offset);
u16 tpm_buf_read_u16(struct tpm_buf *buf, off_t *offset);
u32 tpm_buf_read_u32(struct tpm_buf *buf, off_t *offset);
head->tag = cpu_to_be16(tag);
head->length = cpu_to_be32(sizeof(*head));
head->ordinal = cpu_to_be32(ordinal);
}
static inline int tpm_buf_init(struct tpm_buf *buf, u16 tag, u32 ordinal)
{
buf->data = (u8 *)__get_free_page(GFP_KERNEL);
if (!buf->data)
return -ENOMEM;
buf->flags = 0;
tpm_buf_reset(buf, tag, ordinal);
return 0;
}
static inline void tpm_buf_destroy(struct tpm_buf *buf)
{
free_page((unsigned long)buf->data);
}
static inline u32 tpm_buf_length(struct tpm_buf *buf)
{
struct tpm_header *head = (struct tpm_header *)buf->data;
return be32_to_cpu(head->length);
}
static inline u16 tpm_buf_tag(struct tpm_buf *buf)
{
struct tpm_header *head = (struct tpm_header *)buf->data;
return be16_to_cpu(head->tag);
}
static inline void tpm_buf_append(struct tpm_buf *buf,
const unsigned char *new_data,
unsigned int new_len)
{
struct tpm_header *head = (struct tpm_header *)buf->data;
u32 len = tpm_buf_length(buf);
/* Return silently if overflow has already happened. */
if (buf->flags & TPM_BUF_OVERFLOW)
return;
if ((len + new_len) > PAGE_SIZE) {
WARN(1, "tpm_buf: overflow\n");
buf->flags |= TPM_BUF_OVERFLOW;
return;
}
memcpy(&buf->data[len], new_data, new_len);
head->length = cpu_to_be32(len + new_len);
}
static inline void tpm_buf_append_u8(struct tpm_buf *buf, const u8 value)
{
tpm_buf_append(buf, &value, 1);
}
static inline void tpm_buf_append_u16(struct tpm_buf *buf, const u16 value)
{
__be16 value2 = cpu_to_be16(value);
tpm_buf_append(buf, (u8 *) &value2, 2);
}
static inline void tpm_buf_append_u32(struct tpm_buf *buf, const u32 value)
{
__be32 value2 = cpu_to_be32(value);
tpm_buf_append(buf, (u8 *) &value2, 4);
}
u8 *tpm_buf_parameters(struct tpm_buf *buf);
/*
* Check if TPM device is in the firmware upgrade mode.
@ -415,7 +449,7 @@ static inline bool tpm_is_firmware_upgrade(struct tpm_chip *chip)
static inline u32 tpm2_rc_value(u32 rc)
{
return (rc & BIT(7)) ? rc & 0xff : rc;
return (rc & BIT(7)) ? rc & 0xbf : rc;
}
#if defined(CONFIG_TCG_TPM) || defined(CONFIG_TCG_TPM_MODULE)
@ -429,10 +463,19 @@ extern int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
struct tpm_digest *digest);
extern int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
struct tpm_digest *digests);
extern int tpm_send(struct tpm_chip *chip, void *cmd, size_t buflen);
extern int tpm_get_random(struct tpm_chip *chip, u8 *data, size_t max);
extern struct tpm_chip *tpm_default_chip(void);
void tpm2_flush_context(struct tpm_chip *chip, u32 handle);
static inline void tpm_buf_append_empty_auth(struct tpm_buf *buf, u32 handle)
{
/* simple authorization for empty auth */
tpm_buf_append_u32(buf, 9); /* total length of auth */
tpm_buf_append_u32(buf, handle);
tpm_buf_append_u16(buf, 0); /* nonce len */
tpm_buf_append_u8(buf, 0); /* attributes */
tpm_buf_append_u16(buf, 0); /* hmac len */
}
#else
static inline int tpm_is_tpm2(struct tpm_chip *chip)
{
@ -450,10 +493,6 @@ static inline int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
return -ENODEV;
}
static inline int tpm_send(struct tpm_chip *chip, void *cmd, size_t buflen)
{
return -ENODEV;
}
static inline int tpm_get_random(struct tpm_chip *chip, u8 *data, size_t max)
{
return -ENODEV;
@ -463,5 +502,102 @@ static inline struct tpm_chip *tpm_default_chip(void)
{
return NULL;
}
static inline void tpm_buf_append_empty_auth(struct tpm_buf *buf, u32 handle)
{
}
#endif
#ifdef CONFIG_TCG_TPM2_HMAC
int tpm2_start_auth_session(struct tpm_chip *chip);
void tpm_buf_append_name(struct tpm_chip *chip, struct tpm_buf *buf,
u32 handle, u8 *name);
void tpm_buf_append_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf,
u8 attributes, u8 *passphrase,
int passphraselen);
static inline void tpm_buf_append_hmac_session_opt(struct tpm_chip *chip,
struct tpm_buf *buf,
u8 attributes,
u8 *passphrase,
int passphraselen)
{
tpm_buf_append_hmac_session(chip, buf, attributes, passphrase,
passphraselen);
}
void tpm_buf_fill_hmac_session(struct tpm_chip *chip, struct tpm_buf *buf);
int tpm_buf_check_hmac_response(struct tpm_chip *chip, struct tpm_buf *buf,
int rc);
void tpm2_end_auth_session(struct tpm_chip *chip);
#else
#include <asm/unaligned.h>
static inline int tpm2_start_auth_session(struct tpm_chip *chip)
{
return 0;
}
static inline void tpm2_end_auth_session(struct tpm_chip *chip)
{
}
static inline void tpm_buf_append_name(struct tpm_chip *chip,
struct tpm_buf *buf,
u32 handle, u8 *name)
{
tpm_buf_append_u32(buf, handle);
/* count the number of handles in the upper bits of flags */
buf->handles++;
}
static inline void tpm_buf_append_hmac_session(struct tpm_chip *chip,
struct tpm_buf *buf,
u8 attributes, u8 *passphrase,
int passphraselen)
{
/* offset tells us where the sessions area begins */
int offset = buf->handles * 4 + TPM_HEADER_SIZE;
u32 len = 9 + passphraselen;
if (tpm_buf_length(buf) != offset) {
/* not the first session so update the existing length */
len += get_unaligned_be32(&buf->data[offset]);
put_unaligned_be32(len, &buf->data[offset]);
} else {
tpm_buf_append_u32(buf, len);
}
/* auth handle */
tpm_buf_append_u32(buf, TPM2_RS_PW);
/* nonce */
tpm_buf_append_u16(buf, 0);
/* attributes */
tpm_buf_append_u8(buf, 0);
/* passphrase */
tpm_buf_append_u16(buf, passphraselen);
tpm_buf_append(buf, passphrase, passphraselen);
}
static inline void tpm_buf_append_hmac_session_opt(struct tpm_chip *chip,
struct tpm_buf *buf,
u8 attributes,
u8 *passphrase,
int passphraselen)
{
int offset = buf->handles * 4 + TPM_HEADER_SIZE;
struct tpm_header *head = (struct tpm_header *) buf->data;
/*
* if the only sessions are optional, the command tag
* must change to TPM2_ST_NO_SESSIONS
*/
if (tpm_buf_length(buf) == offset)
head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
}
static inline void tpm_buf_fill_hmac_session(struct tpm_chip *chip,
struct tpm_buf *buf)
{
}
static inline int tpm_buf_check_hmac_response(struct tpm_chip *chip,
struct tpm_buf *buf,
int rc)
{
return rc;
}
#endif /* CONFIG_TCG_TPM2_HMAC */
#endif

View File

@ -8,6 +8,11 @@ config CRYPTO_LIB_UTILS
config CRYPTO_LIB_AES
tristate
config CRYPTO_LIB_AESCFB
tristate
select CRYPTO_LIB_AES
select CRYPTO_LIB_UTILS
config CRYPTO_LIB_AESGCM
tristate
select CRYPTO_LIB_AES

View File

@ -10,6 +10,9 @@ obj-$(CONFIG_CRYPTO_LIB_CHACHA_GENERIC) += libchacha.o
obj-$(CONFIG_CRYPTO_LIB_AES) += libaes.o
libaes-y := aes.o
obj-$(CONFIG_CRYPTO_LIB_AESCFB) += libaescfb.o
libaescfb-y := aescfb.o
obj-$(CONFIG_CRYPTO_LIB_AESGCM) += libaesgcm.o
libaesgcm-y := aesgcm.o

257
lib/crypto/aescfb.c Normal file
View File

@ -0,0 +1,257 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Minimal library implementation of AES in CFB mode
*
* Copyright 2023 Google LLC
*/
#include <linux/module.h>
#include <crypto/algapi.h>
#include <crypto/aes.h>
#include <asm/irqflags.h>
static void aescfb_encrypt_block(const struct crypto_aes_ctx *ctx, void *dst,
const void *src)
{
unsigned long flags;
/*
* In AES-CFB, the AES encryption operates on known 'plaintext' (the IV
* and ciphertext), making it susceptible to timing attacks on the
* encryption key. The AES library already mitigates this risk to some
* extent by pulling the entire S-box into the caches before doing any
* substitutions, but this strategy is more effective when running with
* interrupts disabled.
*/
local_irq_save(flags);
aes_encrypt(ctx, dst, src);
local_irq_restore(flags);
}
/**
* aescfb_encrypt - Perform AES-CFB encryption on a block of data
*
* @ctx: The AES-CFB key schedule
* @dst: Pointer to the ciphertext output buffer
* @src: Pointer the plaintext (may equal @dst for encryption in place)
* @len: The size in bytes of the plaintext and ciphertext.
* @iv: The initialization vector (IV) to use for this block of data
*/
void aescfb_encrypt(const struct crypto_aes_ctx *ctx, u8 *dst, const u8 *src,
int len, const u8 iv[AES_BLOCK_SIZE])
{
u8 ks[AES_BLOCK_SIZE];
const u8 *v = iv;
while (len > 0) {
aescfb_encrypt_block(ctx, ks, v);
crypto_xor_cpy(dst, src, ks, min(len, AES_BLOCK_SIZE));
v = dst;
dst += AES_BLOCK_SIZE;
src += AES_BLOCK_SIZE;
len -= AES_BLOCK_SIZE;
}
memzero_explicit(ks, sizeof(ks));
}
EXPORT_SYMBOL(aescfb_encrypt);
/**
* aescfb_decrypt - Perform AES-CFB decryption on a block of data
*
* @ctx: The AES-CFB key schedule
* @dst: Pointer to the plaintext output buffer
* @src: Pointer the ciphertext (may equal @dst for decryption in place)
* @len: The size in bytes of the plaintext and ciphertext.
* @iv: The initialization vector (IV) to use for this block of data
*/
void aescfb_decrypt(const struct crypto_aes_ctx *ctx, u8 *dst, const u8 *src,
int len, const u8 iv[AES_BLOCK_SIZE])
{
u8 ks[2][AES_BLOCK_SIZE];
aescfb_encrypt_block(ctx, ks[0], iv);
for (int i = 0; len > 0; i ^= 1) {
if (len > AES_BLOCK_SIZE)
/*
* Generate the keystream for the next block before
* performing the XOR, as that may update in place and
* overwrite the ciphertext.
*/
aescfb_encrypt_block(ctx, ks[!i], src);
crypto_xor_cpy(dst, src, ks[i], min(len, AES_BLOCK_SIZE));
dst += AES_BLOCK_SIZE;
src += AES_BLOCK_SIZE;
len -= AES_BLOCK_SIZE;
}
memzero_explicit(ks, sizeof(ks));
}
EXPORT_SYMBOL(aescfb_decrypt);
MODULE_DESCRIPTION("Generic AES-CFB library");
MODULE_AUTHOR("Ard Biesheuvel <ardb@kernel.org>");
MODULE_LICENSE("GPL");
#ifndef CONFIG_CRYPTO_MANAGER_DISABLE_TESTS
/*
* Test code below. Vectors taken from crypto/testmgr.h
*/
static struct {
u8 ptext[64];
u8 ctext[64];
u8 key[AES_MAX_KEY_SIZE];
u8 iv[AES_BLOCK_SIZE];
int klen;
int len;
} const aescfb_tv[] __initconst = {
{ /* From NIST SP800-38A */
.key = "\x2b\x7e\x15\x16\x28\xae\xd2\xa6"
"\xab\xf7\x15\x88\x09\xcf\x4f\x3c",
.klen = 16,
.iv = "\x00\x01\x02\x03\x04\x05\x06\x07"
"\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
.ptext = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96"
"\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
"\xae\x2d\x8a\x57\x1e\x03\xac\x9c"
"\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
"\x30\xc8\x1c\x46\xa3\x5c\xe4\x11"
"\xe5\xfb\xc1\x19\x1a\x0a\x52\xef"
"\xf6\x9f\x24\x45\xdf\x4f\x9b\x17"
"\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
.ctext = "\x3b\x3f\xd9\x2e\xb7\x2d\xad\x20"
"\x33\x34\x49\xf8\xe8\x3c\xfb\x4a"
"\xc8\xa6\x45\x37\xa0\xb3\xa9\x3f"
"\xcd\xe3\xcd\xad\x9f\x1c\xe5\x8b"
"\x26\x75\x1f\x67\xa3\xcb\xb1\x40"
"\xb1\x80\x8c\xf1\x87\xa4\xf4\xdf"
"\xc0\x4b\x05\x35\x7c\x5d\x1c\x0e"
"\xea\xc4\xc6\x6f\x9f\xf7\xf2\xe6",
.len = 64,
}, {
.key = "\x8e\x73\xb0\xf7\xda\x0e\x64\x52"
"\xc8\x10\xf3\x2b\x80\x90\x79\xe5"
"\x62\xf8\xea\xd2\x52\x2c\x6b\x7b",
.klen = 24,
.iv = "\x00\x01\x02\x03\x04\x05\x06\x07"
"\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
.ptext = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96"
"\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
"\xae\x2d\x8a\x57\x1e\x03\xac\x9c"
"\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
"\x30\xc8\x1c\x46\xa3\x5c\xe4\x11"
"\xe5\xfb\xc1\x19\x1a\x0a\x52\xef"
"\xf6\x9f\x24\x45\xdf\x4f\x9b\x17"
"\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
.ctext = "\xcd\xc8\x0d\x6f\xdd\xf1\x8c\xab"
"\x34\xc2\x59\x09\xc9\x9a\x41\x74"
"\x67\xce\x7f\x7f\x81\x17\x36\x21"
"\x96\x1a\x2b\x70\x17\x1d\x3d\x7a"
"\x2e\x1e\x8a\x1d\xd5\x9b\x88\xb1"
"\xc8\xe6\x0f\xed\x1e\xfa\xc4\xc9"
"\xc0\x5f\x9f\x9c\xa9\x83\x4f\xa0"
"\x42\xae\x8f\xba\x58\x4b\x09\xff",
.len = 64,
}, {
.key = "\x60\x3d\xeb\x10\x15\xca\x71\xbe"
"\x2b\x73\xae\xf0\x85\x7d\x77\x81"
"\x1f\x35\x2c\x07\x3b\x61\x08\xd7"
"\x2d\x98\x10\xa3\x09\x14\xdf\xf4",
.klen = 32,
.iv = "\x00\x01\x02\x03\x04\x05\x06\x07"
"\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
.ptext = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96"
"\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
"\xae\x2d\x8a\x57\x1e\x03\xac\x9c"
"\x9e\xb7\x6f\xac\x45\xaf\x8e\x51"
"\x30\xc8\x1c\x46\xa3\x5c\xe4\x11"
"\xe5\xfb\xc1\x19\x1a\x0a\x52\xef"
"\xf6\x9f\x24\x45\xdf\x4f\x9b\x17"
"\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
.ctext = "\xdc\x7e\x84\xbf\xda\x79\x16\x4b"
"\x7e\xcd\x84\x86\x98\x5d\x38\x60"
"\x39\xff\xed\x14\x3b\x28\xb1\xc8"
"\x32\x11\x3c\x63\x31\xe5\x40\x7b"
"\xdf\x10\x13\x24\x15\xe5\x4b\x92"
"\xa1\x3e\xd0\xa8\x26\x7a\xe2\xf9"
"\x75\xa3\x85\x74\x1a\xb9\xce\xf8"
"\x20\x31\x62\x3d\x55\xb1\xe4\x71",
.len = 64,
}, { /* > 16 bytes, not a multiple of 16 bytes */
.key = "\x2b\x7e\x15\x16\x28\xae\xd2\xa6"
"\xab\xf7\x15\x88\x09\xcf\x4f\x3c",
.klen = 16,
.iv = "\x00\x01\x02\x03\x04\x05\x06\x07"
"\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
.ptext = "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96"
"\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
"\xae",
.ctext = "\x3b\x3f\xd9\x2e\xb7\x2d\xad\x20"
"\x33\x34\x49\xf8\xe8\x3c\xfb\x4a"
"\xc8",
.len = 17,
}, { /* < 16 bytes */
.key = "\x2b\x7e\x15\x16\x28\xae\xd2\xa6"
"\xab\xf7\x15\x88\x09\xcf\x4f\x3c",
.klen = 16,
.iv = "\x00\x01\x02\x03\x04\x05\x06\x07"
"\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
.ptext = "\x6b\xc1\xbe\xe2\x2e\x40\x9f",
.ctext = "\x3b\x3f\xd9\x2e\xb7\x2d\xad",
.len = 7,
},
};
static int __init libaescfb_init(void)
{
for (int i = 0; i < ARRAY_SIZE(aescfb_tv); i++) {
struct crypto_aes_ctx ctx;
u8 buf[64];
if (aes_expandkey(&ctx, aescfb_tv[i].key, aescfb_tv[i].klen)) {
pr_err("aes_expandkey() failed on vector %d\n", i);
return -ENODEV;
}
aescfb_encrypt(&ctx, buf, aescfb_tv[i].ptext, aescfb_tv[i].len,
aescfb_tv[i].iv);
if (memcmp(buf, aescfb_tv[i].ctext, aescfb_tv[i].len)) {
pr_err("aescfb_encrypt() #1 failed on vector %d\n", i);
return -ENODEV;
}
/* decrypt in place */
aescfb_decrypt(&ctx, buf, buf, aescfb_tv[i].len, aescfb_tv[i].iv);
if (memcmp(buf, aescfb_tv[i].ptext, aescfb_tv[i].len)) {
pr_err("aescfb_decrypt() failed on vector %d\n", i);
return -ENODEV;
}
/* encrypt in place */
aescfb_encrypt(&ctx, buf, buf, aescfb_tv[i].len, aescfb_tv[i].iv);
if (memcmp(buf, aescfb_tv[i].ctext, aescfb_tv[i].len)) {
pr_err("aescfb_encrypt() #2 failed on vector %d\n", i);
return -ENODEV;
}
}
return 0;
}
module_init(libaescfb_init);
static void __exit libaescfb_exit(void)
{
}
module_exit(libaescfb_exit);
#endif

View File

@ -356,17 +356,28 @@ out:
*/
int trusted_tpm_send(unsigned char *cmd, size_t buflen)
{
struct tpm_buf buf;
int rc;
if (!chip)
return -ENODEV;
rc = tpm_try_get_ops(chip);
if (rc)
return rc;
buf.flags = 0;
buf.length = buflen;
buf.data = cmd;
dump_tpm_buf(cmd);
rc = tpm_send(chip, cmd, buflen);
rc = tpm_transmit_cmd(chip, &buf, 4, "sending data");
dump_tpm_buf(cmd);
if (rc > 0)
/* Can't return positive return codes values to keyctl */
/* TPM error */
rc = -EPERM;
tpm_put_ops(chip);
return rc;
}
EXPORT_SYMBOL_GPL(trusted_tpm_send);
@ -407,7 +418,7 @@ static int osap(struct tpm_buf *tb, struct osapsess *s,
tpm_buf_append_u32(tb, handle);
tpm_buf_append(tb, ononce, TPM_NONCE_SIZE);
ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE);
ret = trusted_tpm_send(tb->data, tb->length);
if (ret < 0)
return ret;
@ -431,7 +442,7 @@ int oiap(struct tpm_buf *tb, uint32_t *handle, unsigned char *nonce)
return -ENODEV;
tpm_buf_reset(tb, TPM_TAG_RQU_COMMAND, TPM_ORD_OIAP);
ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE);
ret = trusted_tpm_send(tb->data, tb->length);
if (ret < 0)
return ret;
@ -543,7 +554,7 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype,
tpm_buf_append_u8(tb, cont);
tpm_buf_append(tb, td->pubauth, SHA1_DIGEST_SIZE);
ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE);
ret = trusted_tpm_send(tb->data, tb->length);
if (ret < 0)
goto out;
@ -634,7 +645,7 @@ static int tpm_unseal(struct tpm_buf *tb,
tpm_buf_append_u8(tb, cont);
tpm_buf_append(tb, authdata2, SHA1_DIGEST_SIZE);
ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE);
ret = trusted_tpm_send(tb->data, tb->length);
if (ret < 0) {
pr_info("authhmac failed (%d)\n", ret);
return ret;

View File

@ -228,8 +228,9 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
struct trusted_key_payload *payload,
struct trusted_key_options *options)
{
off_t offset = TPM_HEADER_SIZE;
struct tpm_buf buf, sized;
int blob_len = 0;
struct tpm_buf buf;
u32 hash;
u32 flags;
int i;
@ -252,50 +253,58 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
if (rc)
return rc;
rc = tpm2_start_auth_session(chip);
if (rc)
goto out_put;
rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
if (rc) {
tpm_put_ops(chip);
return rc;
tpm2_end_auth_session(chip);
goto out_put;
}
tpm_buf_append_u32(&buf, options->keyhandle);
tpm2_buf_append_auth(&buf, TPM2_RS_PW,
NULL /* nonce */, 0,
0 /* session_attributes */,
options->keyauth /* hmac */,
TPM_DIGEST_SIZE);
rc = tpm_buf_init_sized(&sized);
if (rc) {
tpm_buf_destroy(&buf);
tpm2_end_auth_session(chip);
goto out_put;
}
tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_DECRYPT,
options->keyauth, TPM_DIGEST_SIZE);
/* sensitive */
tpm_buf_append_u16(&buf, 4 + options->blobauth_len + payload->key_len);
tpm_buf_append_u16(&sized, options->blobauth_len);
tpm_buf_append_u16(&buf, options->blobauth_len);
if (options->blobauth_len)
tpm_buf_append(&buf, options->blobauth, options->blobauth_len);
tpm_buf_append(&sized, options->blobauth, options->blobauth_len);
tpm_buf_append_u16(&buf, payload->key_len);
tpm_buf_append(&buf, payload->key, payload->key_len);
tpm_buf_append_u16(&sized, payload->key_len);
tpm_buf_append(&sized, payload->key, payload->key_len);
tpm_buf_append(&buf, sized.data, sized.length);
/* public */
tpm_buf_append_u16(&buf, 14 + options->policydigest_len);
tpm_buf_append_u16(&buf, TPM_ALG_KEYEDHASH);
tpm_buf_append_u16(&buf, hash);
tpm_buf_reset_sized(&sized);
tpm_buf_append_u16(&sized, TPM_ALG_KEYEDHASH);
tpm_buf_append_u16(&sized, hash);
/* key properties */
flags = 0;
flags |= options->policydigest_len ? 0 : TPM2_OA_USER_WITH_AUTH;
flags |= payload->migratable ? 0 : (TPM2_OA_FIXED_TPM |
TPM2_OA_FIXED_PARENT);
tpm_buf_append_u32(&buf, flags);
flags |= payload->migratable ? 0 : (TPM2_OA_FIXED_TPM | TPM2_OA_FIXED_PARENT);
tpm_buf_append_u32(&sized, flags);
/* policy */
tpm_buf_append_u16(&buf, options->policydigest_len);
tpm_buf_append_u16(&sized, options->policydigest_len);
if (options->policydigest_len)
tpm_buf_append(&buf, options->policydigest,
options->policydigest_len);
tpm_buf_append(&sized, options->policydigest, options->policydigest_len);
/* public parameters */
tpm_buf_append_u16(&buf, TPM_ALG_NULL);
tpm_buf_append_u16(&buf, 0);
tpm_buf_append_u16(&sized, TPM_ALG_NULL);
tpm_buf_append_u16(&sized, 0);
tpm_buf_append(&buf, sized.data, sized.length);
/* outside info */
tpm_buf_append_u16(&buf, 0);
@ -305,28 +314,30 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
if (buf.flags & TPM_BUF_OVERFLOW) {
rc = -E2BIG;
tpm2_end_auth_session(chip);
goto out;
}
tpm_buf_fill_hmac_session(chip, &buf);
rc = tpm_transmit_cmd(chip, &buf, 4, "sealing data");
rc = tpm_buf_check_hmac_response(chip, &buf, rc);
if (rc)
goto out;
blob_len = be32_to_cpup((__be32 *) &buf.data[TPM_HEADER_SIZE]);
if (blob_len > MAX_BLOB_SIZE) {
blob_len = tpm_buf_read_u32(&buf, &offset);
if (blob_len > MAX_BLOB_SIZE || buf.flags & TPM_BUF_BOUNDARY_ERROR) {
rc = -E2BIG;
goto out;
}
if (tpm_buf_length(&buf) < TPM_HEADER_SIZE + 4 + blob_len) {
if (buf.length - offset < blob_len) {
rc = -EFAULT;
goto out;
}
blob_len = tpm2_key_encode(payload, options,
&buf.data[TPM_HEADER_SIZE + 4],
blob_len);
blob_len = tpm2_key_encode(payload, options, &buf.data[offset], blob_len);
out:
tpm_buf_destroy(&sized);
tpm_buf_destroy(&buf);
if (rc > 0) {
@ -340,6 +351,7 @@ out:
else
payload->blob_len = blob_len;
out_put:
tpm_put_ops(chip);
return rc;
}
@ -409,25 +421,31 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
if (blob_len > payload->blob_len)
return -E2BIG;
rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
rc = tpm2_start_auth_session(chip);
if (rc)
return rc;
tpm_buf_append_u32(&buf, options->keyhandle);
tpm2_buf_append_auth(&buf, TPM2_RS_PW,
NULL /* nonce */, 0,
0 /* session_attributes */,
options->keyauth /* hmac */,
TPM_DIGEST_SIZE);
rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
if (rc) {
tpm2_end_auth_session(chip);
return rc;
}
tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
tpm_buf_append_hmac_session(chip, &buf, 0, options->keyauth,
TPM_DIGEST_SIZE);
tpm_buf_append(&buf, blob, blob_len);
if (buf.flags & TPM_BUF_OVERFLOW) {
rc = -E2BIG;
tpm2_end_auth_session(chip);
goto out;
}
tpm_buf_fill_hmac_session(chip, &buf);
rc = tpm_transmit_cmd(chip, &buf, 4, "loading blob");
rc = tpm_buf_check_hmac_response(chip, &buf, rc);
if (!rc)
*blob_handle = be32_to_cpup(
(__be32 *) &buf.data[TPM_HEADER_SIZE]);
@ -465,20 +483,44 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
u8 *data;
int rc;
rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
rc = tpm2_start_auth_session(chip);
if (rc)
return rc;
tpm_buf_append_u32(&buf, blob_handle);
tpm2_buf_append_auth(&buf,
options->policyhandle ?
options->policyhandle : TPM2_RS_PW,
NULL /* nonce */, 0,
TPM2_SA_CONTINUE_SESSION,
options->blobauth /* hmac */,
options->blobauth_len);
rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
if (rc) {
tpm2_end_auth_session(chip);
return rc;
}
tpm_buf_append_name(chip, &buf, blob_handle, NULL);
if (!options->policyhandle) {
tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT,
options->blobauth,
options->blobauth_len);
} else {
/*
* FIXME: The policy session was generated outside the
* kernel so we don't known the nonce and thus can't
* calculate a HMAC on it. Therefore, the user can
* only really use TPM2_PolicyPassword and we must
* send down the plain text password, which could be
* intercepted. We can still encrypt the returned
* key, but that's small comfort since the interposer
* could repeat our actions with the exfiltrated
* password.
*/
tpm2_buf_append_auth(&buf, options->policyhandle,
NULL /* nonce */, 0, 0,
options->blobauth, options->blobauth_len);
tpm_buf_append_hmac_session_opt(chip, &buf, TPM2_SA_ENCRYPT,
NULL, 0);
}
tpm_buf_fill_hmac_session(chip, &buf);
rc = tpm_transmit_cmd(chip, &buf, 6, "unsealing");
rc = tpm_buf_check_hmac_response(chip, &buf, rc);
if (rc > 0)
rc = -EPERM;