mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 04:18:39 +08:00
Hi,
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:
commit
b19239143e
@ -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
|
||||
|
@ -5,6 +5,8 @@ Trusted Platform Module documentation
|
||||
.. toctree::
|
||||
|
||||
tpm_event_log
|
||||
tpm-security
|
||||
tpm_tis
|
||||
tpm_vtpm_proxy
|
||||
xen-tpmfront
|
||||
tpm_ftpm_tee
|
||||
|
216
Documentation/security/tpm/tpm-security.rst
Normal file
216
Documentation/security/tpm/tpm-security.rst
Normal 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/
|
46
Documentation/security/tpm/tpm_tis.rst
Normal file
46
Documentation/security/tpm/tpm_tis.rst
Normal 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/
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
252
drivers/char/tpm/tpm-buf.c
Normal 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];
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
1286
drivers/char/tpm/tpm2-sessions.c
Normal file
1286
drivers/char/tpm/tpm2-sessions.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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 = {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
257
lib/crypto/aescfb.c
Normal 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
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user