mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-07 22:03:14 +00:00
tpmdd updates for Linux v5.13
-----BEGIN PGP SIGNATURE----- iIgEABYIADAWIQRE6pSOnaBC00OEHEIaerohdGur0gUCYHbwjxIcamFya2tvQGtl cm5lbC5vcmcACgkQGnq6IXRrq9KQvAD/chBQK3FrcaWYLmPEY8y/6mo2ZByPUv5D paLXgBkeFU0A/Rti+rATM7n95hgCIlTILK1boXvv0FBJTts0ZHUyZykG =03e0 -----END PGP SIGNATURE----- Merge tag 'tpmdd-next-v5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-tpmdd Pull tpm updates from Jarkko Sakkinen: "New features: - ARM TEE backend for kernel trusted keys to complete the existing TPM backend - ASN.1 format for TPM2 trusted keys to make them interact with the user space stack, such as OpenConnect VPN Other than that, a bunch of bug fixes" * tag 'tpmdd-next-v5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-tpmdd: KEYS: trusted: Fix missing null return from kzalloc call char: tpm: fix error return code in tpm_cr50_i2c_tis_recv() MAINTAINERS: Add entry for TEE based Trusted Keys doc: trusted-encrypted: updates with TEE as a new trust source KEYS: trusted: Introduce TEE based Trusted Keys KEYS: trusted: Add generic trusted keys framework security: keys: trusted: Make sealed key properly interoperable security: keys: trusted: use ASN.1 TPM2 key format for the blobs security: keys: trusted: fix TPM2 authorizations oid_registry: Add TCG defined OIDS for TPM keys lib: Add ASN.1 encoder tpm: vtpm_proxy: Avoid reading host log when using a virtual device tpm: acpi: Check eventlog signature before using it tpm: efi: Use local variable for calculating final log size
This commit is contained in:
commit
7dd1ce1a52
@ -5462,6 +5462,18 @@
|
||||
See Documentation/admin-guide/mm/transhuge.rst
|
||||
for more details.
|
||||
|
||||
trusted.source= [KEYS]
|
||||
Format: <string>
|
||||
This parameter identifies the trust source as a backend
|
||||
for trusted keys implementation. Supported trust
|
||||
sources:
|
||||
- "tpm"
|
||||
- "tee"
|
||||
If not specified then it defaults to iterating through
|
||||
the trust source list starting with TPM and assigns the
|
||||
first trust source as a backend which is initialized
|
||||
successfully during iteration.
|
||||
|
||||
tsc= Disable clocksource stability checks for TSC.
|
||||
Format: <string>
|
||||
[x86] reliable: mark tsc clocksource as reliable, this
|
||||
|
@ -6,30 +6,127 @@ Trusted and Encrypted Keys are two new key types added to the existing kernel
|
||||
key ring service. Both of these new types are variable length symmetric keys,
|
||||
and in both cases all keys are created in the kernel, and user space sees,
|
||||
stores, and loads only encrypted blobs. Trusted Keys require the availability
|
||||
of a Trusted Platform Module (TPM) chip for greater security, while Encrypted
|
||||
Keys can be used on any system. All user level blobs, are displayed and loaded
|
||||
in hex ascii for convenience, and are integrity verified.
|
||||
of a Trust Source for greater security, while Encrypted Keys can be used on any
|
||||
system. All user level blobs, are displayed and loaded in hex ASCII for
|
||||
convenience, and are integrity verified.
|
||||
|
||||
Trusted Keys use a TPM both to generate and to seal the keys. Keys are sealed
|
||||
under a 2048 bit RSA key in the TPM, and optionally sealed to specified PCR
|
||||
(integrity measurement) values, and only unsealed by the TPM, if PCRs and blob
|
||||
integrity verifications match. A loaded Trusted Key can be updated with new
|
||||
(future) PCR values, so keys are easily migrated to new pcr values, such as
|
||||
when the kernel and initramfs are updated. The same key can have many saved
|
||||
blobs under different PCR values, so multiple boots are easily supported.
|
||||
|
||||
TPM 1.2
|
||||
-------
|
||||
Trust Source
|
||||
============
|
||||
|
||||
By default, trusted keys are sealed under the SRK, which has the default
|
||||
authorization value (20 zeros). This can be set at takeownership time with the
|
||||
trouser's utility: "tpm_takeownership -u -z".
|
||||
A trust source provides the source of security for Trusted Keys. This
|
||||
section lists currently supported trust sources, along with their security
|
||||
considerations. Whether or not a trust source is sufficiently safe depends
|
||||
on the strength and correctness of its implementation, as well as the threat
|
||||
environment for a specific use case. Since the kernel doesn't know what the
|
||||
environment is, and there is no metric of trust, it is dependent on the
|
||||
consumer of the Trusted Keys to determine if the trust source is sufficiently
|
||||
safe.
|
||||
|
||||
TPM 2.0
|
||||
-------
|
||||
* Root of trust for storage
|
||||
|
||||
The user must first create a storage key and make it persistent, so the key is
|
||||
available after reboot. This can be done using the following commands.
|
||||
(1) TPM (Trusted Platform Module: hardware device)
|
||||
|
||||
Rooted to Storage Root Key (SRK) which never leaves the TPM that
|
||||
provides crypto operation to establish root of trust for storage.
|
||||
|
||||
(2) TEE (Trusted Execution Environment: OP-TEE based on Arm TrustZone)
|
||||
|
||||
Rooted to Hardware Unique Key (HUK) which is generally burnt in on-chip
|
||||
fuses and is accessible to TEE only.
|
||||
|
||||
* Execution isolation
|
||||
|
||||
(1) TPM
|
||||
|
||||
Fixed set of operations running in isolated execution environment.
|
||||
|
||||
(2) TEE
|
||||
|
||||
Customizable set of operations running in isolated execution
|
||||
environment verified via Secure/Trusted boot process.
|
||||
|
||||
* Optional binding to platform integrity state
|
||||
|
||||
(1) TPM
|
||||
|
||||
Keys can be optionally sealed to specified PCR (integrity measurement)
|
||||
values, and only unsealed by the TPM, if PCRs and blob integrity
|
||||
verifications match. A loaded Trusted Key can be updated with new
|
||||
(future) PCR values, so keys are easily migrated to new PCR values,
|
||||
such as when the kernel and initramfs are updated. The same key can
|
||||
have many saved blobs under different PCR values, so multiple boots are
|
||||
easily supported.
|
||||
|
||||
(2) TEE
|
||||
|
||||
Relies on Secure/Trusted boot process for platform integrity. It can
|
||||
be extended with TEE based measured boot process.
|
||||
|
||||
* Interfaces and APIs
|
||||
|
||||
(1) TPM
|
||||
|
||||
TPMs have well-documented, standardized interfaces and APIs.
|
||||
|
||||
(2) TEE
|
||||
|
||||
TEEs have well-documented, standardized client interface and APIs. For
|
||||
more details refer to ``Documentation/staging/tee.rst``.
|
||||
|
||||
|
||||
* Threat model
|
||||
|
||||
The strength and appropriateness of a particular TPM or TEE for a given
|
||||
purpose must be assessed when using them to protect security-relevant data.
|
||||
|
||||
|
||||
Key Generation
|
||||
==============
|
||||
|
||||
Trusted Keys
|
||||
------------
|
||||
|
||||
New keys are created from random numbers generated in the trust source. They
|
||||
are encrypted/decrypted using a child key in the storage key hierarchy.
|
||||
Encryption and decryption of the child key must be protected by a strong
|
||||
access control policy within the trust source.
|
||||
|
||||
* TPM (hardware device) based RNG
|
||||
|
||||
Strength of random numbers may vary from one device manufacturer to
|
||||
another.
|
||||
|
||||
* TEE (OP-TEE based on Arm TrustZone) based RNG
|
||||
|
||||
RNG is customizable as per platform needs. It can either be direct output
|
||||
from platform specific hardware RNG or a software based Fortuna CSPRNG
|
||||
which can be seeded via multiple entropy sources.
|
||||
|
||||
Encrypted Keys
|
||||
--------------
|
||||
|
||||
Encrypted keys do not depend on a trust source, and are faster, as they use AES
|
||||
for encryption/decryption. New keys are created from kernel-generated random
|
||||
numbers, and are encrypted/decrypted using a specified ‘master’ key. The
|
||||
‘master’ key can either be a trusted-key or user-key type. The main disadvantage
|
||||
of encrypted keys is that if they are not rooted in a trusted key, they are only
|
||||
as secure as the user key encrypting them. The master user key should therefore
|
||||
be loaded in as secure a way as possible, preferably early in boot.
|
||||
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
Trusted Keys usage: TPM
|
||||
-----------------------
|
||||
|
||||
TPM 1.2: By default, trusted keys are sealed under the SRK, which has the
|
||||
default authorization value (20 bytes of 0s). This can be set at takeownership
|
||||
time with the TrouSerS utility: "tpm_takeownership -u -z".
|
||||
|
||||
TPM 2.0: The user must first create a storage key and make it persistent, so the
|
||||
key is available after reboot. This can be done using the following commands.
|
||||
|
||||
With the IBM TSS 2 stack::
|
||||
|
||||
@ -78,14 +175,21 @@ TPM_STORED_DATA format. The key length for new keys are always in bytes.
|
||||
Trusted Keys can be 32 - 128 bytes (256 - 1024 bits), the upper limit is to fit
|
||||
within the 2048 bit SRK (RSA) keylength, with all necessary structure/padding.
|
||||
|
||||
Encrypted keys do not depend on a TPM, and are faster, as they use AES for
|
||||
encryption/decryption. New keys are created from kernel generated random
|
||||
numbers, and are encrypted/decrypted using a specified 'master' key. The
|
||||
'master' key can either be a trusted-key or user-key type. The main
|
||||
disadvantage of encrypted keys is that if they are not rooted in a trusted key,
|
||||
they are only as secure as the user key encrypting them. The master user key
|
||||
should therefore be loaded in as secure a way as possible, preferably early in
|
||||
boot.
|
||||
Trusted Keys usage: TEE
|
||||
-----------------------
|
||||
|
||||
Usage::
|
||||
|
||||
keyctl add trusted name "new keylen" ring
|
||||
keyctl add trusted name "load hex_blob" ring
|
||||
keyctl print keyid
|
||||
|
||||
"keyctl print" returns an ASCII hex copy of the sealed key, which is in format
|
||||
specific to TEE device implementation. The key length for new keys is always
|
||||
in bytes. Trusted Keys can be 32 - 128 bytes (256 - 1024 bits).
|
||||
|
||||
Encrypted Keys usage
|
||||
--------------------
|
||||
|
||||
The decrypted portion of encrypted keys can contain either a simple symmetric
|
||||
key or a more complex structure. The format of the more complex structure is
|
||||
@ -103,8 +207,8 @@ Where::
|
||||
format:= 'default | ecryptfs | enc32'
|
||||
key-type:= 'trusted' | 'user'
|
||||
|
||||
|
||||
Examples of trusted and encrypted key usage:
|
||||
Examples of trusted and encrypted key usage
|
||||
-------------------------------------------
|
||||
|
||||
Create and save a trusted key named "kmk" of length 32 bytes.
|
||||
|
||||
@ -150,7 +254,7 @@ Load a trusted key from the saved blob::
|
||||
f1f8fff03ad0acb083725535636addb08d73dedb9832da198081e5deae84bfaf0409c22b
|
||||
e4a8aea2b607ec96931e6f4d4fe563ba
|
||||
|
||||
Reseal a trusted key under new pcr values::
|
||||
Reseal (TPM specific) a trusted key under new PCR values::
|
||||
|
||||
$ keyctl update 268728824 "update pcrinfo=`cat pcr.blob`"
|
||||
$ keyctl print 268728824
|
||||
@ -164,11 +268,12 @@ Reseal a trusted key under new pcr values::
|
||||
7ef6a24defe4846104209bf0c3eced7fa1a672ed5b125fc9d8cd88b476a658a4434644ef
|
||||
df8ae9a178e9f83ba9f08d10fa47e4226b98b0702f06b3b8
|
||||
|
||||
|
||||
The initial consumer of trusted keys is EVM, which at boot time needs a high
|
||||
quality symmetric key for HMAC protection of file metadata. The use of a
|
||||
quality symmetric key for HMAC protection of file metadata. The use of a
|
||||
trusted key provides strong guarantees that the EVM key has not been
|
||||
compromised by a user level problem, and when sealed to specific boot PCR
|
||||
values, protects against boot and offline attacks. Create and save an
|
||||
compromised by a user level problem, and when sealed to a platform integrity
|
||||
state, protects against boot and offline attacks. Create and save an
|
||||
encrypted key "evm" using the above trusted key "kmk":
|
||||
|
||||
option 1: omitting 'format'::
|
||||
@ -207,3 +312,61 @@ about the usage can be found in the file
|
||||
Another new format 'enc32' has been defined in order to support encrypted keys
|
||||
with payload size of 32 bytes. This will initially be used for nvdimm security
|
||||
but may expand to other usages that require 32 bytes payload.
|
||||
|
||||
|
||||
TPM 2.0 ASN.1 Key Format
|
||||
------------------------
|
||||
|
||||
The TPM 2.0 ASN.1 key format is designed to be easily recognisable,
|
||||
even in binary form (fixing a problem we had with the TPM 1.2 ASN.1
|
||||
format) and to be extensible for additions like importable keys and
|
||||
policy::
|
||||
|
||||
TPMKey ::= SEQUENCE {
|
||||
type OBJECT IDENTIFIER
|
||||
emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL
|
||||
parent INTEGER
|
||||
pubkey OCTET STRING
|
||||
privkey OCTET STRING
|
||||
}
|
||||
|
||||
type is what distinguishes the key even in binary form since the OID
|
||||
is provided by the TCG to be unique and thus forms a recognizable
|
||||
binary pattern at offset 3 in the key. The OIDs currently made
|
||||
available are::
|
||||
|
||||
2.23.133.10.1.3 TPM Loadable key. This is an asymmetric key (Usually
|
||||
RSA2048 or Elliptic Curve) which can be imported by a
|
||||
TPM2_Load() operation.
|
||||
|
||||
2.23.133.10.1.4 TPM Importable Key. This is an asymmetric key (Usually
|
||||
RSA2048 or Elliptic Curve) which can be imported by a
|
||||
TPM2_Import() operation.
|
||||
|
||||
2.23.133.10.1.5 TPM Sealed Data. This is a set of data (up to 128
|
||||
bytes) which is sealed by the TPM. It usually
|
||||
represents a symmetric key and must be unsealed before
|
||||
use.
|
||||
|
||||
The trusted key code only uses the TPM Sealed Data OID.
|
||||
|
||||
emptyAuth is true if the key has well known authorization "". If it
|
||||
is false or not present, the key requires an explicit authorization
|
||||
phrase. This is used by most user space consumers to decide whether
|
||||
to prompt for a password.
|
||||
|
||||
parent represents the parent key handle, either in the 0x81 MSO space,
|
||||
like 0x81000001 for the RSA primary storage key. Userspace programmes
|
||||
also support specifying the primary handle in the 0x40 MSO space. If
|
||||
this happens the Elliptic Curve variant of the primary key using the
|
||||
TCG defined template will be generated on the fly into a volatile
|
||||
object and used as the parent. The current kernel code only supports
|
||||
the 0x81 MSO form.
|
||||
|
||||
pubkey is the binary representation of TPM2B_PRIVATE excluding the
|
||||
initial TPM2B header, which can be reconstructed from the ASN.1 octet
|
||||
string length.
|
||||
|
||||
privkey is the binary representation of TPM2B_PUBLIC excluding the
|
||||
initial TPM2B header which can be reconstructed from the ASN.1 octed
|
||||
string length.
|
||||
|
@ -9887,6 +9887,14 @@ F: include/keys/trusted-type.h
|
||||
F: include/keys/trusted_tpm.h
|
||||
F: security/keys/trusted-keys/
|
||||
|
||||
KEYS-TRUSTED-TEE
|
||||
M: Sumit Garg <sumit.garg@linaro.org>
|
||||
L: linux-integrity@vger.kernel.org
|
||||
L: keyrings@vger.kernel.org
|
||||
S: Supported
|
||||
F: include/keys/trusted_tee.h
|
||||
F: security/keys/trusted-keys/trusted_tee.c
|
||||
|
||||
KEYS/KEYRINGS
|
||||
M: David Howells <dhowells@redhat.com>
|
||||
M: Jarkko Sakkinen <jarkko@kernel.org>
|
||||
|
@ -41,6 +41,27 @@ struct acpi_tcpa {
|
||||
};
|
||||
};
|
||||
|
||||
/* Check that the given log is indeed a TPM2 log. */
|
||||
static bool tpm_is_tpm2_log(void *bios_event_log, u64 len)
|
||||
{
|
||||
struct tcg_efi_specid_event_head *efispecid;
|
||||
struct tcg_pcr_event *event_header;
|
||||
int n;
|
||||
|
||||
if (len < sizeof(*event_header))
|
||||
return false;
|
||||
len -= sizeof(*event_header);
|
||||
event_header = bios_event_log;
|
||||
|
||||
if (len < sizeof(*efispecid))
|
||||
return false;
|
||||
efispecid = (struct tcg_efi_specid_event_head *)event_header->event;
|
||||
|
||||
n = memcmp(efispecid->signature, TCG_SPECID_SIG,
|
||||
sizeof(TCG_SPECID_SIG));
|
||||
return n == 0;
|
||||
}
|
||||
|
||||
/* read binary bios log */
|
||||
int tpm_read_log_acpi(struct tpm_chip *chip)
|
||||
{
|
||||
@ -52,6 +73,7 @@ int tpm_read_log_acpi(struct tpm_chip *chip)
|
||||
struct acpi_table_tpm2 *tbl;
|
||||
struct acpi_tpm2_phy *tpm2_phy;
|
||||
int format;
|
||||
int ret;
|
||||
|
||||
log = &chip->log;
|
||||
|
||||
@ -112,6 +134,7 @@ 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)
|
||||
goto err;
|
||||
@ -119,11 +142,19 @@ int tpm_read_log_acpi(struct tpm_chip *chip)
|
||||
memcpy_fromio(log->bios_event_log, virt, len);
|
||||
|
||||
acpi_os_unmap_iomem(virt, len);
|
||||
|
||||
if (chip->flags & TPM_CHIP_FLAG_TPM2 &&
|
||||
!tpm_is_tpm2_log(log->bios_event_log, len)) {
|
||||
/* try EFI log next */
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
return format;
|
||||
|
||||
err:
|
||||
kfree(log->bios_event_log);
|
||||
log->bios_event_log = NULL;
|
||||
return -EIO;
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
@ -107,6 +107,9 @@ void tpm_bios_log_setup(struct tpm_chip *chip)
|
||||
int log_version;
|
||||
int rc = 0;
|
||||
|
||||
if (chip->flags & TPM_CHIP_FLAG_VIRTUAL)
|
||||
return;
|
||||
|
||||
rc = tpm_read_log(chip);
|
||||
if (rc < 0)
|
||||
return;
|
||||
|
@ -17,6 +17,7 @@ int tpm_read_log_efi(struct tpm_chip *chip)
|
||||
{
|
||||
|
||||
struct efi_tcg2_final_events_table *final_tbl = NULL;
|
||||
int final_events_log_size = efi_tpm_final_log_size;
|
||||
struct linux_efi_tpm_eventlog *log_tbl;
|
||||
struct tpm_bios_log *log;
|
||||
u32 log_size;
|
||||
@ -66,12 +67,12 @@ int tpm_read_log_efi(struct tpm_chip *chip)
|
||||
ret = tpm_log_version;
|
||||
|
||||
if (efi.tpm_final_log == EFI_INVALID_TABLE_ADDR ||
|
||||
efi_tpm_final_log_size == 0 ||
|
||||
final_events_log_size == 0 ||
|
||||
tpm_log_version != EFI_TCG2_EVENT_LOG_FORMAT_TCG_2)
|
||||
goto out;
|
||||
|
||||
final_tbl = memremap(efi.tpm_final_log,
|
||||
sizeof(*final_tbl) + efi_tpm_final_log_size,
|
||||
sizeof(*final_tbl) + final_events_log_size,
|
||||
MEMREMAP_WB);
|
||||
if (!final_tbl) {
|
||||
pr_err("Could not map UEFI TPM final log\n");
|
||||
@ -80,10 +81,18 @@ int tpm_read_log_efi(struct tpm_chip *chip)
|
||||
goto out;
|
||||
}
|
||||
|
||||
efi_tpm_final_log_size -= log_tbl->final_events_preboot_size;
|
||||
/*
|
||||
* The 'final events log' size excludes the 'final events preboot log'
|
||||
* at its beginning.
|
||||
*/
|
||||
final_events_log_size -= log_tbl->final_events_preboot_size;
|
||||
|
||||
/*
|
||||
* Allocate memory for the 'combined log' where we will append the
|
||||
* 'final events log' to.
|
||||
*/
|
||||
tmp = krealloc(log->bios_event_log,
|
||||
log_size + efi_tpm_final_log_size,
|
||||
log_size + final_events_log_size,
|
||||
GFP_KERNEL);
|
||||
if (!tmp) {
|
||||
kfree(log->bios_event_log);
|
||||
@ -94,15 +103,19 @@ int tpm_read_log_efi(struct tpm_chip *chip)
|
||||
log->bios_event_log = tmp;
|
||||
|
||||
/*
|
||||
* Copy any of the final events log that didn't also end up in the
|
||||
* main log. Events can be logged in both if events are generated
|
||||
* Append any of the 'final events log' that didn't also end up in the
|
||||
* 'main log'. Events can be logged in both if events are generated
|
||||
* between GetEventLog() and ExitBootServices().
|
||||
*/
|
||||
memcpy((void *)log->bios_event_log + log_size,
|
||||
final_tbl->events + log_tbl->final_events_preboot_size,
|
||||
efi_tpm_final_log_size);
|
||||
final_events_log_size);
|
||||
/*
|
||||
* The size of the 'combined log' is the size of the 'main log' plus
|
||||
* the size of the 'final events log'.
|
||||
*/
|
||||
log->bios_event_log_end = log->bios_event_log +
|
||||
log_size + efi_tpm_final_log_size;
|
||||
log_size + final_events_log_size;
|
||||
|
||||
out:
|
||||
memunmap(final_tbl);
|
||||
|
@ -483,6 +483,7 @@ static int tpm_cr50_i2c_tis_recv(struct tpm_chip *chip, u8 *buf, size_t buf_len)
|
||||
expected = be32_to_cpup((__be32 *)(buf + 2));
|
||||
if (expected > buf_len) {
|
||||
dev_err(&chip->dev, "Buffer too small to receive i2c data\n");
|
||||
rc = -E2BIG;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,12 @@
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/tpm.h>
|
||||
|
||||
#ifdef pr_fmt
|
||||
#undef pr_fmt
|
||||
#endif
|
||||
|
||||
#define pr_fmt(fmt) "trusted_key: " fmt
|
||||
|
||||
#define MIN_KEY_SIZE 32
|
||||
#define MAX_KEY_SIZE 128
|
||||
#define MAX_BLOB_SIZE 512
|
||||
@ -22,6 +28,7 @@ struct trusted_key_payload {
|
||||
unsigned int key_len;
|
||||
unsigned int blob_len;
|
||||
unsigned char migratable;
|
||||
unsigned char old_format;
|
||||
unsigned char key[MAX_KEY_SIZE + 1];
|
||||
unsigned char blob[MAX_BLOB_SIZE];
|
||||
};
|
||||
@ -30,6 +37,7 @@ struct trusted_key_options {
|
||||
uint16_t keytype;
|
||||
uint32_t keyhandle;
|
||||
unsigned char keyauth[TPM_DIGEST_SIZE];
|
||||
uint32_t blobauth_len;
|
||||
unsigned char blobauth[TPM_DIGEST_SIZE];
|
||||
uint32_t pcrinfo_len;
|
||||
unsigned char pcrinfo[MAX_PCRINFO_SIZE];
|
||||
@ -40,6 +48,53 @@ struct trusted_key_options {
|
||||
uint32_t policyhandle;
|
||||
};
|
||||
|
||||
struct trusted_key_ops {
|
||||
/*
|
||||
* flag to indicate if trusted key implementation supports migration
|
||||
* or not.
|
||||
*/
|
||||
unsigned char migratable;
|
||||
|
||||
/* Initialize key interface. */
|
||||
int (*init)(void);
|
||||
|
||||
/* Seal a key. */
|
||||
int (*seal)(struct trusted_key_payload *p, char *datablob);
|
||||
|
||||
/* Unseal a key. */
|
||||
int (*unseal)(struct trusted_key_payload *p, char *datablob);
|
||||
|
||||
/* Get a randomized key. */
|
||||
int (*get_random)(unsigned char *key, size_t key_len);
|
||||
|
||||
/* Exit key interface. */
|
||||
void (*exit)(void);
|
||||
};
|
||||
|
||||
struct trusted_key_source {
|
||||
char *name;
|
||||
struct trusted_key_ops *ops;
|
||||
};
|
||||
|
||||
extern struct key_type key_type_trusted;
|
||||
|
||||
#define TRUSTED_DEBUG 0
|
||||
|
||||
#if TRUSTED_DEBUG
|
||||
static inline void dump_payload(struct trusted_key_payload *p)
|
||||
{
|
||||
pr_info("key_len %d\n", p->key_len);
|
||||
print_hex_dump(KERN_INFO, "key ", DUMP_PREFIX_NONE,
|
||||
16, 1, p->key, p->key_len, 0);
|
||||
pr_info("bloblen %d\n", p->blob_len);
|
||||
print_hex_dump(KERN_INFO, "blob ", DUMP_PREFIX_NONE,
|
||||
16, 1, p->blob, p->blob_len, 0);
|
||||
pr_info("migratable %d\n", p->migratable);
|
||||
}
|
||||
#else
|
||||
static inline void dump_payload(struct trusted_key_payload *p)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _KEYS_TRUSTED_TYPE_H */
|
||||
|
16
include/keys/trusted_tee.h
Normal file
16
include/keys/trusted_tee.h
Normal file
@ -0,0 +1,16 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2019-2021 Linaro Ltd.
|
||||
*
|
||||
* Author:
|
||||
* Sumit Garg <sumit.garg@linaro.org>
|
||||
*/
|
||||
|
||||
#ifndef __TEE_TRUSTED_KEY_H
|
||||
#define __TEE_TRUSTED_KEY_H
|
||||
|
||||
#include <keys/trusted-type.h>
|
||||
|
||||
extern struct trusted_key_ops trusted_key_tee_ops;
|
||||
|
||||
#endif
|
@ -16,6 +16,8 @@
|
||||
#define LOAD32N(buffer, offset) (*(uint32_t *)&buffer[offset])
|
||||
#define LOAD16(buffer, offset) (ntohs(*(uint16_t *)&buffer[offset]))
|
||||
|
||||
extern struct trusted_key_ops trusted_key_tpm_ops;
|
||||
|
||||
struct osapsess {
|
||||
uint32_t handle;
|
||||
unsigned char secret[SHA1_DIGEST_SIZE];
|
||||
@ -52,30 +54,19 @@ int tpm2_unseal_trusted(struct tpm_chip *chip,
|
||||
#if TPM_DEBUG
|
||||
static inline void dump_options(struct trusted_key_options *o)
|
||||
{
|
||||
pr_info("trusted_key: sealing key type %d\n", o->keytype);
|
||||
pr_info("trusted_key: sealing key handle %0X\n", o->keyhandle);
|
||||
pr_info("trusted_key: pcrlock %d\n", o->pcrlock);
|
||||
pr_info("trusted_key: pcrinfo %d\n", o->pcrinfo_len);
|
||||
pr_info("sealing key type %d\n", o->keytype);
|
||||
pr_info("sealing key handle %0X\n", o->keyhandle);
|
||||
pr_info("pcrlock %d\n", o->pcrlock);
|
||||
pr_info("pcrinfo %d\n", o->pcrinfo_len);
|
||||
print_hex_dump(KERN_INFO, "pcrinfo ", DUMP_PREFIX_NONE,
|
||||
16, 1, o->pcrinfo, o->pcrinfo_len, 0);
|
||||
}
|
||||
|
||||
static inline void dump_payload(struct trusted_key_payload *p)
|
||||
{
|
||||
pr_info("trusted_key: key_len %d\n", p->key_len);
|
||||
print_hex_dump(KERN_INFO, "key ", DUMP_PREFIX_NONE,
|
||||
16, 1, p->key, p->key_len, 0);
|
||||
pr_info("trusted_key: bloblen %d\n", p->blob_len);
|
||||
print_hex_dump(KERN_INFO, "blob ", DUMP_PREFIX_NONE,
|
||||
16, 1, p->blob, p->blob_len, 0);
|
||||
pr_info("trusted_key: migratable %d\n", p->migratable);
|
||||
}
|
||||
|
||||
static inline void dump_sess(struct osapsess *s)
|
||||
{
|
||||
print_hex_dump(KERN_INFO, "trusted-key: handle ", DUMP_PREFIX_NONE,
|
||||
16, 1, &s->handle, 4, 0);
|
||||
pr_info("trusted-key: secret:\n");
|
||||
pr_info("secret:\n");
|
||||
print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE,
|
||||
16, 1, &s->secret, SHA1_DIGEST_SIZE, 0);
|
||||
pr_info("trusted-key: enonce:\n");
|
||||
@ -87,7 +78,7 @@ static inline void dump_tpm_buf(unsigned char *buf)
|
||||
{
|
||||
int len;
|
||||
|
||||
pr_info("\ntrusted-key: tpm buffer\n");
|
||||
pr_info("\ntpm buffer\n");
|
||||
len = LOAD32(buf, TPM_SIZE_OFFSET);
|
||||
print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, buf, len, 0);
|
||||
}
|
||||
@ -96,10 +87,6 @@ static inline void dump_options(struct trusted_key_options *o)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void dump_payload(struct trusted_key_payload *p)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void dump_sess(struct osapsess *s)
|
||||
{
|
||||
}
|
||||
|
32
include/linux/asn1_encoder.h
Normal file
32
include/linux/asn1_encoder.h
Normal file
@ -0,0 +1,32 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#ifndef _LINUX_ASN1_ENCODER_H
|
||||
#define _LINUX_ASN1_ENCODER_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/asn1.h>
|
||||
#include <linux/asn1_ber_bytecode.h>
|
||||
#include <linux/bug.h>
|
||||
|
||||
#define asn1_oid_len(oid) (sizeof(oid)/sizeof(u32))
|
||||
unsigned char *
|
||||
asn1_encode_integer(unsigned char *data, const unsigned char *end_data,
|
||||
s64 integer);
|
||||
unsigned char *
|
||||
asn1_encode_oid(unsigned char *data, const unsigned char *end_data,
|
||||
u32 oid[], int oid_len);
|
||||
unsigned char *
|
||||
asn1_encode_tag(unsigned char *data, const unsigned char *end_data,
|
||||
u32 tag, const unsigned char *string, int len);
|
||||
unsigned char *
|
||||
asn1_encode_octet_string(unsigned char *data,
|
||||
const unsigned char *end_data,
|
||||
const unsigned char *string, u32 len);
|
||||
unsigned char *
|
||||
asn1_encode_sequence(unsigned char *data, const unsigned char *end_data,
|
||||
const unsigned char *seq, int len);
|
||||
unsigned char *
|
||||
asn1_encode_boolean(unsigned char *data, const unsigned char *end_data,
|
||||
bool val);
|
||||
|
||||
#endif
|
@ -113,6 +113,11 @@ enum OID {
|
||||
OID_SM2_with_SM3, /* 1.2.156.10197.1.501 */
|
||||
OID_sm3WithRSAEncryption, /* 1.2.156.10197.1.504 */
|
||||
|
||||
/* TCG defined OIDS for TPM based keys */
|
||||
OID_TPMLoadableKey, /* 2.23.133.10.1.3 */
|
||||
OID_TPMImportableKey, /* 2.23.133.10.1.4 */
|
||||
OID_TPMSealedData, /* 2.23.133.10.1.5 */
|
||||
|
||||
OID__NR
|
||||
};
|
||||
|
||||
|
@ -305,6 +305,8 @@ struct tpm_buf {
|
||||
};
|
||||
|
||||
enum tpm2_object_attributes {
|
||||
TPM2_OA_FIXED_TPM = BIT(1),
|
||||
TPM2_OA_FIXED_PARENT = BIT(4),
|
||||
TPM2_OA_USER_WITH_AUTH = BIT(6),
|
||||
};
|
||||
|
||||
|
@ -701,3 +701,6 @@ config GENERIC_LIB_DEVMEM_IS_ALLOWED
|
||||
config PLDMFW
|
||||
bool
|
||||
default n
|
||||
|
||||
config ASN1_ENCODER
|
||||
tristate
|
||||
|
@ -280,6 +280,7 @@ obj-$(CONFIG_INTERVAL_TREE_TEST) += interval_tree_test.o
|
||||
obj-$(CONFIG_PERCPU_TEST) += percpu_test.o
|
||||
|
||||
obj-$(CONFIG_ASN1) += asn1_decoder.o
|
||||
obj-$(CONFIG_ASN1_ENCODER) += asn1_encoder.o
|
||||
|
||||
obj-$(CONFIG_FONT_SUPPORT) += fonts/
|
||||
|
||||
|
454
lib/asn1_encoder.c
Normal file
454
lib/asn1_encoder.c
Normal file
@ -0,0 +1,454 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Simple encoder primitives for ASN.1 BER/DER/CER
|
||||
*
|
||||
* Copyright (C) 2019 James.Bottomley@HansenPartnership.com
|
||||
*/
|
||||
|
||||
#include <linux/asn1_encoder.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
/**
|
||||
* asn1_encode_integer() - encode positive integer to ASN.1
|
||||
* @data: pointer to the pointer to the data
|
||||
* @end_data: end of data pointer, points one beyond last usable byte in @data
|
||||
* @integer: integer to be encoded
|
||||
*
|
||||
* This is a simplified encoder: it only currently does
|
||||
* positive integers, but it should be simple enough to add the
|
||||
* negative case if a use comes along.
|
||||
*/
|
||||
unsigned char *
|
||||
asn1_encode_integer(unsigned char *data, const unsigned char *end_data,
|
||||
s64 integer)
|
||||
{
|
||||
int data_len = end_data - data;
|
||||
unsigned char *d = &data[2];
|
||||
bool found = false;
|
||||
int i;
|
||||
|
||||
if (WARN(integer < 0,
|
||||
"BUG: integer encode only supports positive integers"))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (IS_ERR(data))
|
||||
return data;
|
||||
|
||||
/* need at least 3 bytes for tag, length and integer encoding */
|
||||
if (data_len < 3)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
/* remaining length where at d (the start of the integer encoding) */
|
||||
data_len -= 2;
|
||||
|
||||
data[0] = _tag(UNIV, PRIM, INT);
|
||||
if (integer == 0) {
|
||||
*d++ = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = sizeof(integer); i > 0 ; i--) {
|
||||
int byte = integer >> (8 * (i - 1));
|
||||
|
||||
if (!found && byte == 0)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* for a positive number the first byte must have bit
|
||||
* 7 clear in two's complement (otherwise it's a
|
||||
* negative number) so prepend a leading zero if
|
||||
* that's not the case
|
||||
*/
|
||||
if (!found && (byte & 0x80)) {
|
||||
/*
|
||||
* no check needed here, we already know we
|
||||
* have len >= 1
|
||||
*/
|
||||
*d++ = 0;
|
||||
data_len--;
|
||||
}
|
||||
|
||||
found = true;
|
||||
if (data_len == 0)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
*d++ = byte;
|
||||
data_len--;
|
||||
}
|
||||
|
||||
out:
|
||||
data[1] = d - data - 2;
|
||||
|
||||
return d;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(asn1_encode_integer);
|
||||
|
||||
/* calculate the base 128 digit values setting the top bit of the first octet */
|
||||
static int asn1_encode_oid_digit(unsigned char **_data, int *data_len, u32 oid)
|
||||
{
|
||||
unsigned char *data = *_data;
|
||||
int start = 7 + 7 + 7 + 7;
|
||||
int ret = 0;
|
||||
|
||||
if (*data_len < 1)
|
||||
return -EINVAL;
|
||||
|
||||
/* quick case */
|
||||
if (oid == 0) {
|
||||
*data++ = 0x80;
|
||||
(*data_len)--;
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (oid >> start == 0)
|
||||
start -= 7;
|
||||
|
||||
while (start > 0 && *data_len > 0) {
|
||||
u8 byte;
|
||||
|
||||
byte = oid >> start;
|
||||
oid = oid - (byte << start);
|
||||
start -= 7;
|
||||
byte |= 0x80;
|
||||
*data++ = byte;
|
||||
(*data_len)--;
|
||||
}
|
||||
|
||||
if (*data_len > 0) {
|
||||
*data++ = oid;
|
||||
(*data_len)--;
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
out:
|
||||
*_data = data;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* asn1_encode_oid() - encode an oid to ASN.1
|
||||
* @data: position to begin encoding at
|
||||
* @end_data: end of data pointer, points one beyond last usable byte in @data
|
||||
* @oid: array of oids
|
||||
* @oid_len: length of oid array
|
||||
*
|
||||
* this encodes an OID up to ASN.1 when presented as an array of OID values
|
||||
*/
|
||||
unsigned char *
|
||||
asn1_encode_oid(unsigned char *data, const unsigned char *end_data,
|
||||
u32 oid[], int oid_len)
|
||||
{
|
||||
int data_len = end_data - data;
|
||||
unsigned char *d = data + 2;
|
||||
int i, ret;
|
||||
|
||||
if (WARN(oid_len < 2, "OID must have at least two elements"))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (WARN(oid_len > 32, "OID is too large"))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (IS_ERR(data))
|
||||
return data;
|
||||
|
||||
|
||||
/* need at least 3 bytes for tag, length and OID encoding */
|
||||
if (data_len < 3)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
data[0] = _tag(UNIV, PRIM, OID);
|
||||
*d++ = oid[0] * 40 + oid[1];
|
||||
|
||||
data_len -= 3;
|
||||
|
||||
ret = 0;
|
||||
|
||||
for (i = 2; i < oid_len; i++) {
|
||||
ret = asn1_encode_oid_digit(&d, &data_len, oid[i]);
|
||||
if (ret < 0)
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
data[1] = d - data - 2;
|
||||
|
||||
return d;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(asn1_encode_oid);
|
||||
|
||||
/**
|
||||
* asn1_encode_length() - encode a length to follow an ASN.1 tag
|
||||
* @data: pointer to encode at
|
||||
* @data_len: pointer to remaning length (adjusted by routine)
|
||||
* @len: length to encode
|
||||
*
|
||||
* This routine can encode lengths up to 65535 using the ASN.1 rules.
|
||||
* It will accept a negative length and place a zero length tag
|
||||
* instead (to keep the ASN.1 valid). This convention allows other
|
||||
* encoder primitives to accept negative lengths as singalling the
|
||||
* sequence will be re-encoded when the length is known.
|
||||
*/
|
||||
static int asn1_encode_length(unsigned char **data, int *data_len, int len)
|
||||
{
|
||||
if (*data_len < 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (len < 0) {
|
||||
*((*data)++) = 0;
|
||||
(*data_len)--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (len <= 0x7f) {
|
||||
*((*data)++) = len;
|
||||
(*data_len)--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (*data_len < 2)
|
||||
return -EINVAL;
|
||||
|
||||
if (len <= 0xff) {
|
||||
*((*data)++) = 0x81;
|
||||
*((*data)++) = len & 0xff;
|
||||
*data_len -= 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (*data_len < 3)
|
||||
return -EINVAL;
|
||||
|
||||
if (len <= 0xffff) {
|
||||
*((*data)++) = 0x82;
|
||||
*((*data)++) = (len >> 8) & 0xff;
|
||||
*((*data)++) = len & 0xff;
|
||||
*data_len -= 3;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (WARN(len > 0xffffff, "ASN.1 length can't be > 0xffffff"))
|
||||
return -EINVAL;
|
||||
|
||||
if (*data_len < 4)
|
||||
return -EINVAL;
|
||||
*((*data)++) = 0x83;
|
||||
*((*data)++) = (len >> 16) & 0xff;
|
||||
*((*data)++) = (len >> 8) & 0xff;
|
||||
*((*data)++) = len & 0xff;
|
||||
*data_len -= 4;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* asn1_encode_tag() - add a tag for optional or explicit value
|
||||
* @data: pointer to place tag at
|
||||
* @end_data: end of data pointer, points one beyond last usable byte in @data
|
||||
* @tag: tag to be placed
|
||||
* @string: the data to be tagged
|
||||
* @len: the length of the data to be tagged
|
||||
*
|
||||
* Note this currently only handles short form tags < 31.
|
||||
*
|
||||
* Standard usage is to pass in a @tag, @string and @length and the
|
||||
* @string will be ASN.1 encoded with @tag and placed into @data. If
|
||||
* the encoding would put data past @end_data then an error is
|
||||
* returned, otherwise a pointer to a position one beyond the encoding
|
||||
* is returned.
|
||||
*
|
||||
* To encode in place pass a NULL @string and -1 for @len and the
|
||||
* maximum allowable beginning and end of the data; all this will do
|
||||
* is add the current maximum length and update the data pointer to
|
||||
* the place where the tag contents should be placed is returned. The
|
||||
* data should be copied in by the calling routine which should then
|
||||
* repeat the prior statement but now with the known length. In order
|
||||
* to avoid having to keep both before and after pointers, the repeat
|
||||
* expects to be called with @data pointing to where the first encode
|
||||
* returned it and still NULL for @string but the real length in @len.
|
||||
*/
|
||||
unsigned char *
|
||||
asn1_encode_tag(unsigned char *data, const unsigned char *end_data,
|
||||
u32 tag, const unsigned char *string, int len)
|
||||
{
|
||||
int data_len = end_data - data;
|
||||
int ret;
|
||||
|
||||
if (WARN(tag > 30, "ASN.1 tag can't be > 30"))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (!string && WARN(len > 127,
|
||||
"BUG: recode tag is too big (>127)"))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (IS_ERR(data))
|
||||
return data;
|
||||
|
||||
if (!string && len > 0) {
|
||||
/*
|
||||
* we're recoding, so move back to the start of the
|
||||
* tag and install a dummy length because the real
|
||||
* data_len should be NULL
|
||||
*/
|
||||
data -= 2;
|
||||
data_len = 2;
|
||||
}
|
||||
|
||||
if (data_len < 2)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
*(data++) = _tagn(CONT, CONS, tag);
|
||||
data_len--;
|
||||
ret = asn1_encode_length(&data, &data_len, len);
|
||||
if (ret < 0)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
if (!string)
|
||||
return data;
|
||||
|
||||
if (data_len < len)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
memcpy(data, string, len);
|
||||
data += len;
|
||||
|
||||
return data;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(asn1_encode_tag);
|
||||
|
||||
/**
|
||||
* asn1_encode_octet_string() - encode an ASN.1 OCTET STRING
|
||||
* @data: pointer to encode at
|
||||
* @end_data: end of data pointer, points one beyond last usable byte in @data
|
||||
* @string: string to be encoded
|
||||
* @len: length of string
|
||||
*
|
||||
* Note ASN.1 octet strings may contain zeros, so the length is obligatory.
|
||||
*/
|
||||
unsigned char *
|
||||
asn1_encode_octet_string(unsigned char *data,
|
||||
const unsigned char *end_data,
|
||||
const unsigned char *string, u32 len)
|
||||
{
|
||||
int data_len = end_data - data;
|
||||
int ret;
|
||||
|
||||
if (IS_ERR(data))
|
||||
return data;
|
||||
|
||||
/* need minimum of 2 bytes for tag and length of zero length string */
|
||||
if (data_len < 2)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
*(data++) = _tag(UNIV, PRIM, OTS);
|
||||
data_len--;
|
||||
|
||||
ret = asn1_encode_length(&data, &data_len, len);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
if (data_len < len)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
memcpy(data, string, len);
|
||||
data += len;
|
||||
|
||||
return data;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(asn1_encode_octet_string);
|
||||
|
||||
/**
|
||||
* asn1_encode_sequence() - wrap a byte stream in an ASN.1 SEQUENCE
|
||||
* @data: pointer to encode at
|
||||
* @end_data: end of data pointer, points one beyond last usable byte in @data
|
||||
* @seq: data to be encoded as a sequence
|
||||
* @len: length of the data to be encoded as a sequence
|
||||
*
|
||||
* Fill in a sequence. To encode in place, pass NULL for @seq and -1
|
||||
* for @len; then call again once the length is known (still with NULL
|
||||
* for @seq). In order to avoid having to keep both before and after
|
||||
* pointers, the repeat expects to be called with @data pointing to
|
||||
* where the first encode placed it.
|
||||
*/
|
||||
unsigned char *
|
||||
asn1_encode_sequence(unsigned char *data, const unsigned char *end_data,
|
||||
const unsigned char *seq, int len)
|
||||
{
|
||||
int data_len = end_data - data;
|
||||
int ret;
|
||||
|
||||
if (!seq && WARN(len > 127,
|
||||
"BUG: recode sequence is too big (>127)"))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (IS_ERR(data))
|
||||
return data;
|
||||
|
||||
if (!seq && len >= 0) {
|
||||
/*
|
||||
* we're recoding, so move back to the start of the
|
||||
* sequence and install a dummy length because the
|
||||
* real length should be NULL
|
||||
*/
|
||||
data -= 2;
|
||||
data_len = 2;
|
||||
}
|
||||
|
||||
if (data_len < 2)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
*(data++) = _tag(UNIV, CONS, SEQ);
|
||||
data_len--;
|
||||
|
||||
ret = asn1_encode_length(&data, &data_len, len);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
if (!seq)
|
||||
return data;
|
||||
|
||||
if (data_len < len)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
memcpy(data, seq, len);
|
||||
data += len;
|
||||
|
||||
return data;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(asn1_encode_sequence);
|
||||
|
||||
/**
|
||||
* asn1_encode_boolean() - encode a boolean value to ASN.1
|
||||
* @data: pointer to encode at
|
||||
* @end_data: end of data pointer, points one beyond last usable byte in @data
|
||||
* @val: the boolean true/false value
|
||||
*/
|
||||
unsigned char *
|
||||
asn1_encode_boolean(unsigned char *data, const unsigned char *end_data,
|
||||
bool val)
|
||||
{
|
||||
int data_len = end_data - data;
|
||||
|
||||
if (IS_ERR(data))
|
||||
return data;
|
||||
|
||||
/* booleans are 3 bytes: tag, length == 1 and value == 0 or 1 */
|
||||
if (data_len < 3)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
*(data++) = _tag(UNIV, PRIM, BOOL);
|
||||
data_len--;
|
||||
|
||||
asn1_encode_length(&data, &data_len, 1);
|
||||
|
||||
if (val)
|
||||
*(data++) = 1;
|
||||
else
|
||||
*(data++) = 0;
|
||||
|
||||
return data;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(asn1_encode_boolean);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
@ -75,6 +75,9 @@ config TRUSTED_KEYS
|
||||
select CRYPTO_HMAC
|
||||
select CRYPTO_SHA1
|
||||
select CRYPTO_HASH_INFO
|
||||
select ASN1_ENCODER
|
||||
select OID_REGISTRY
|
||||
select ASN1
|
||||
help
|
||||
This option provides support for creating, sealing, and unsealing
|
||||
keys in the kernel. Trusted keys are random number symmetric keys,
|
||||
|
@ -4,5 +4,11 @@
|
||||
#
|
||||
|
||||
obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
|
||||
trusted-y += trusted_core.o
|
||||
trusted-y += trusted_tpm1.o
|
||||
|
||||
$(obj)/trusted_tpm2.o: $(obj)/tpm2key.asn1.h
|
||||
trusted-y += trusted_tpm2.o
|
||||
trusted-y += tpm2key.asn1.o
|
||||
|
||||
trusted-$(CONFIG_TEE) += trusted_tee.o
|
||||
|
11
security/keys/trusted-keys/tpm2key.asn1
Normal file
11
security/keys/trusted-keys/tpm2key.asn1
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
--- ASN.1 for TPM 2.0 keys
|
||||
---
|
||||
|
||||
TPMKey ::= SEQUENCE {
|
||||
type OBJECT IDENTIFIER ({tpm2_key_type}),
|
||||
emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL,
|
||||
parent INTEGER ({tpm2_key_parent}),
|
||||
pubkey OCTET STRING ({tpm2_key_pub}),
|
||||
privkey OCTET STRING ({tpm2_key_priv})
|
||||
}
|
360
security/keys/trusted-keys/trusted_core.c
Normal file
360
security/keys/trusted-keys/trusted_core.c
Normal file
@ -0,0 +1,360 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2010 IBM Corporation
|
||||
* Copyright (c) 2019-2021, Linaro Limited
|
||||
*
|
||||
* See Documentation/security/keys/trusted-encrypted.rst
|
||||
*/
|
||||
|
||||
#include <keys/user-type.h>
|
||||
#include <keys/trusted-type.h>
|
||||
#include <keys/trusted_tee.h>
|
||||
#include <keys/trusted_tpm.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/key-type.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/parser.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/static_call.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
static char *trusted_key_source;
|
||||
module_param_named(source, trusted_key_source, charp, 0);
|
||||
MODULE_PARM_DESC(source, "Select trusted keys source (tpm or tee)");
|
||||
|
||||
static const struct trusted_key_source trusted_key_sources[] = {
|
||||
#if defined(CONFIG_TCG_TPM)
|
||||
{ "tpm", &trusted_key_tpm_ops },
|
||||
#endif
|
||||
#if defined(CONFIG_TEE)
|
||||
{ "tee", &trusted_key_tee_ops },
|
||||
#endif
|
||||
};
|
||||
|
||||
DEFINE_STATIC_CALL_NULL(trusted_key_init, *trusted_key_sources[0].ops->init);
|
||||
DEFINE_STATIC_CALL_NULL(trusted_key_seal, *trusted_key_sources[0].ops->seal);
|
||||
DEFINE_STATIC_CALL_NULL(trusted_key_unseal,
|
||||
*trusted_key_sources[0].ops->unseal);
|
||||
DEFINE_STATIC_CALL_NULL(trusted_key_get_random,
|
||||
*trusted_key_sources[0].ops->get_random);
|
||||
DEFINE_STATIC_CALL_NULL(trusted_key_exit, *trusted_key_sources[0].ops->exit);
|
||||
static unsigned char migratable;
|
||||
|
||||
enum {
|
||||
Opt_err,
|
||||
Opt_new, Opt_load, Opt_update,
|
||||
};
|
||||
|
||||
static const match_table_t key_tokens = {
|
||||
{Opt_new, "new"},
|
||||
{Opt_load, "load"},
|
||||
{Opt_update, "update"},
|
||||
{Opt_err, NULL}
|
||||
};
|
||||
|
||||
/*
|
||||
* datablob_parse - parse the keyctl data and fill in the
|
||||
* payload structure
|
||||
*
|
||||
* On success returns 0, otherwise -EINVAL.
|
||||
*/
|
||||
static int datablob_parse(char *datablob, struct trusted_key_payload *p)
|
||||
{
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
long keylen;
|
||||
int ret = -EINVAL;
|
||||
int key_cmd;
|
||||
char *c;
|
||||
|
||||
/* main command */
|
||||
c = strsep(&datablob, " \t");
|
||||
if (!c)
|
||||
return -EINVAL;
|
||||
key_cmd = match_token(c, key_tokens, args);
|
||||
switch (key_cmd) {
|
||||
case Opt_new:
|
||||
/* first argument is key size */
|
||||
c = strsep(&datablob, " \t");
|
||||
if (!c)
|
||||
return -EINVAL;
|
||||
ret = kstrtol(c, 10, &keylen);
|
||||
if (ret < 0 || keylen < MIN_KEY_SIZE || keylen > MAX_KEY_SIZE)
|
||||
return -EINVAL;
|
||||
p->key_len = keylen;
|
||||
ret = Opt_new;
|
||||
break;
|
||||
case Opt_load:
|
||||
/* first argument is sealed blob */
|
||||
c = strsep(&datablob, " \t");
|
||||
if (!c)
|
||||
return -EINVAL;
|
||||
p->blob_len = strlen(c) / 2;
|
||||
if (p->blob_len > MAX_BLOB_SIZE)
|
||||
return -EINVAL;
|
||||
ret = hex2bin(p->blob, c, p->blob_len);
|
||||
if (ret < 0)
|
||||
return -EINVAL;
|
||||
ret = Opt_load;
|
||||
break;
|
||||
case Opt_update:
|
||||
ret = Opt_update;
|
||||
break;
|
||||
case Opt_err:
|
||||
return -EINVAL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct trusted_key_payload *trusted_payload_alloc(struct key *key)
|
||||
{
|
||||
struct trusted_key_payload *p = NULL;
|
||||
int ret;
|
||||
|
||||
ret = key_payload_reserve(key, sizeof(*p));
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
p = kzalloc(sizeof(*p), GFP_KERNEL);
|
||||
if (!p)
|
||||
goto err;
|
||||
|
||||
p->migratable = migratable;
|
||||
err:
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
* trusted_instantiate - create a new trusted key
|
||||
*
|
||||
* Unseal an existing trusted blob or, for a new key, get a
|
||||
* random key, then seal and create a trusted key-type key,
|
||||
* adding it to the specified keyring.
|
||||
*
|
||||
* On success, return 0. Otherwise return errno.
|
||||
*/
|
||||
static int trusted_instantiate(struct key *key,
|
||||
struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct trusted_key_payload *payload = NULL;
|
||||
size_t datalen = prep->datalen;
|
||||
char *datablob;
|
||||
int ret = 0;
|
||||
int key_cmd;
|
||||
size_t key_len;
|
||||
|
||||
if (datalen <= 0 || datalen > 32767 || !prep->data)
|
||||
return -EINVAL;
|
||||
|
||||
datablob = kmalloc(datalen + 1, GFP_KERNEL);
|
||||
if (!datablob)
|
||||
return -ENOMEM;
|
||||
memcpy(datablob, prep->data, datalen);
|
||||
datablob[datalen] = '\0';
|
||||
|
||||
payload = trusted_payload_alloc(key);
|
||||
if (!payload) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
key_cmd = datablob_parse(datablob, payload);
|
||||
if (key_cmd < 0) {
|
||||
ret = key_cmd;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dump_payload(payload);
|
||||
|
||||
switch (key_cmd) {
|
||||
case Opt_load:
|
||||
ret = static_call(trusted_key_unseal)(payload, datablob);
|
||||
dump_payload(payload);
|
||||
if (ret < 0)
|
||||
pr_info("key_unseal failed (%d)\n", ret);
|
||||
break;
|
||||
case Opt_new:
|
||||
key_len = payload->key_len;
|
||||
ret = static_call(trusted_key_get_random)(payload->key,
|
||||
key_len);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (ret != key_len) {
|
||||
pr_info("key_create failed (%d)\n", ret);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = static_call(trusted_key_seal)(payload, datablob);
|
||||
if (ret < 0)
|
||||
pr_info("key_seal failed (%d)\n", ret);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
out:
|
||||
kfree_sensitive(datablob);
|
||||
if (!ret)
|
||||
rcu_assign_keypointer(key, payload);
|
||||
else
|
||||
kfree_sensitive(payload);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void trusted_rcu_free(struct rcu_head *rcu)
|
||||
{
|
||||
struct trusted_key_payload *p;
|
||||
|
||||
p = container_of(rcu, struct trusted_key_payload, rcu);
|
||||
kfree_sensitive(p);
|
||||
}
|
||||
|
||||
/*
|
||||
* trusted_update - reseal an existing key with new PCR values
|
||||
*/
|
||||
static int trusted_update(struct key *key, struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct trusted_key_payload *p;
|
||||
struct trusted_key_payload *new_p;
|
||||
size_t datalen = prep->datalen;
|
||||
char *datablob;
|
||||
int ret = 0;
|
||||
|
||||
if (key_is_negative(key))
|
||||
return -ENOKEY;
|
||||
p = key->payload.data[0];
|
||||
if (!p->migratable)
|
||||
return -EPERM;
|
||||
if (datalen <= 0 || datalen > 32767 || !prep->data)
|
||||
return -EINVAL;
|
||||
|
||||
datablob = kmalloc(datalen + 1, GFP_KERNEL);
|
||||
if (!datablob)
|
||||
return -ENOMEM;
|
||||
|
||||
new_p = trusted_payload_alloc(key);
|
||||
if (!new_p) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(datablob, prep->data, datalen);
|
||||
datablob[datalen] = '\0';
|
||||
ret = datablob_parse(datablob, new_p);
|
||||
if (ret != Opt_update) {
|
||||
ret = -EINVAL;
|
||||
kfree_sensitive(new_p);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* copy old key values, and reseal with new pcrs */
|
||||
new_p->migratable = p->migratable;
|
||||
new_p->key_len = p->key_len;
|
||||
memcpy(new_p->key, p->key, p->key_len);
|
||||
dump_payload(p);
|
||||
dump_payload(new_p);
|
||||
|
||||
ret = static_call(trusted_key_seal)(new_p, datablob);
|
||||
if (ret < 0) {
|
||||
pr_info("key_seal failed (%d)\n", ret);
|
||||
kfree_sensitive(new_p);
|
||||
goto out;
|
||||
}
|
||||
|
||||
rcu_assign_keypointer(key, new_p);
|
||||
call_rcu(&p->rcu, trusted_rcu_free);
|
||||
out:
|
||||
kfree_sensitive(datablob);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* trusted_read - copy the sealed blob data to userspace in hex.
|
||||
* On success, return to userspace the trusted key datablob size.
|
||||
*/
|
||||
static long trusted_read(const struct key *key, char *buffer,
|
||||
size_t buflen)
|
||||
{
|
||||
const struct trusted_key_payload *p;
|
||||
char *bufp;
|
||||
int i;
|
||||
|
||||
p = dereference_key_locked(key);
|
||||
if (!p)
|
||||
return -EINVAL;
|
||||
|
||||
if (buffer && buflen >= 2 * p->blob_len) {
|
||||
bufp = buffer;
|
||||
for (i = 0; i < p->blob_len; i++)
|
||||
bufp = hex_byte_pack(bufp, p->blob[i]);
|
||||
}
|
||||
return 2 * p->blob_len;
|
||||
}
|
||||
|
||||
/*
|
||||
* trusted_destroy - clear and free the key's payload
|
||||
*/
|
||||
static void trusted_destroy(struct key *key)
|
||||
{
|
||||
kfree_sensitive(key->payload.data[0]);
|
||||
}
|
||||
|
||||
struct key_type key_type_trusted = {
|
||||
.name = "trusted",
|
||||
.instantiate = trusted_instantiate,
|
||||
.update = trusted_update,
|
||||
.destroy = trusted_destroy,
|
||||
.describe = user_describe,
|
||||
.read = trusted_read,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(key_type_trusted);
|
||||
|
||||
static int __init init_trusted(void)
|
||||
{
|
||||
int i, ret = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(trusted_key_sources); i++) {
|
||||
if (trusted_key_source &&
|
||||
strncmp(trusted_key_source, trusted_key_sources[i].name,
|
||||
strlen(trusted_key_sources[i].name)))
|
||||
continue;
|
||||
|
||||
static_call_update(trusted_key_init,
|
||||
trusted_key_sources[i].ops->init);
|
||||
static_call_update(trusted_key_seal,
|
||||
trusted_key_sources[i].ops->seal);
|
||||
static_call_update(trusted_key_unseal,
|
||||
trusted_key_sources[i].ops->unseal);
|
||||
static_call_update(trusted_key_get_random,
|
||||
trusted_key_sources[i].ops->get_random);
|
||||
static_call_update(trusted_key_exit,
|
||||
trusted_key_sources[i].ops->exit);
|
||||
migratable = trusted_key_sources[i].ops->migratable;
|
||||
|
||||
ret = static_call(trusted_key_init)();
|
||||
if (!ret)
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* encrypted_keys.ko depends on successful load of this module even if
|
||||
* trusted key implementation is not found.
|
||||
*/
|
||||
if (ret == -ENODEV)
|
||||
return 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit cleanup_trusted(void)
|
||||
{
|
||||
static_call(trusted_key_exit)();
|
||||
}
|
||||
|
||||
late_initcall(init_trusted);
|
||||
module_exit(cleanup_trusted);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
318
security/keys/trusted-keys/trusted_tee.c
Normal file
318
security/keys/trusted-keys/trusted_tee.c
Normal file
@ -0,0 +1,318 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2019-2021 Linaro Ltd.
|
||||
*
|
||||
* Author:
|
||||
* Sumit Garg <sumit.garg@linaro.org>
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/key-type.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/tee_drv.h>
|
||||
#include <linux/uuid.h>
|
||||
|
||||
#include <keys/trusted_tee.h>
|
||||
|
||||
#define DRIVER_NAME "trusted-key-tee"
|
||||
|
||||
/*
|
||||
* Get random data for symmetric key
|
||||
*
|
||||
* [out] memref[0] Random data
|
||||
*/
|
||||
#define TA_CMD_GET_RANDOM 0x0
|
||||
|
||||
/*
|
||||
* Seal trusted key using hardware unique key
|
||||
*
|
||||
* [in] memref[0] Plain key
|
||||
* [out] memref[1] Sealed key datablob
|
||||
*/
|
||||
#define TA_CMD_SEAL 0x1
|
||||
|
||||
/*
|
||||
* Unseal trusted key using hardware unique key
|
||||
*
|
||||
* [in] memref[0] Sealed key datablob
|
||||
* [out] memref[1] Plain key
|
||||
*/
|
||||
#define TA_CMD_UNSEAL 0x2
|
||||
|
||||
/**
|
||||
* struct trusted_key_tee_private - TEE Trusted key private data
|
||||
* @dev: TEE based Trusted key device.
|
||||
* @ctx: TEE context handler.
|
||||
* @session_id: Trusted key TA session identifier.
|
||||
* @shm_pool: Memory pool shared with TEE device.
|
||||
*/
|
||||
struct trusted_key_tee_private {
|
||||
struct device *dev;
|
||||
struct tee_context *ctx;
|
||||
u32 session_id;
|
||||
struct tee_shm *shm_pool;
|
||||
};
|
||||
|
||||
static struct trusted_key_tee_private pvt_data;
|
||||
|
||||
/*
|
||||
* Have the TEE seal(encrypt) the symmetric key
|
||||
*/
|
||||
static int trusted_tee_seal(struct trusted_key_payload *p, char *datablob)
|
||||
{
|
||||
int ret;
|
||||
struct tee_ioctl_invoke_arg inv_arg;
|
||||
struct tee_param param[4];
|
||||
struct tee_shm *reg_shm_in = NULL, *reg_shm_out = NULL;
|
||||
|
||||
memset(&inv_arg, 0, sizeof(inv_arg));
|
||||
memset(¶m, 0, sizeof(param));
|
||||
|
||||
reg_shm_in = tee_shm_register(pvt_data.ctx, (unsigned long)p->key,
|
||||
p->key_len, TEE_SHM_DMA_BUF |
|
||||
TEE_SHM_KERNEL_MAPPED);
|
||||
if (IS_ERR(reg_shm_in)) {
|
||||
dev_err(pvt_data.dev, "key shm register failed\n");
|
||||
return PTR_ERR(reg_shm_in);
|
||||
}
|
||||
|
||||
reg_shm_out = tee_shm_register(pvt_data.ctx, (unsigned long)p->blob,
|
||||
sizeof(p->blob), TEE_SHM_DMA_BUF |
|
||||
TEE_SHM_KERNEL_MAPPED);
|
||||
if (IS_ERR(reg_shm_out)) {
|
||||
dev_err(pvt_data.dev, "blob shm register failed\n");
|
||||
ret = PTR_ERR(reg_shm_out);
|
||||
goto out;
|
||||
}
|
||||
|
||||
inv_arg.func = TA_CMD_SEAL;
|
||||
inv_arg.session = pvt_data.session_id;
|
||||
inv_arg.num_params = 4;
|
||||
|
||||
param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
|
||||
param[0].u.memref.shm = reg_shm_in;
|
||||
param[0].u.memref.size = p->key_len;
|
||||
param[0].u.memref.shm_offs = 0;
|
||||
param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT;
|
||||
param[1].u.memref.shm = reg_shm_out;
|
||||
param[1].u.memref.size = sizeof(p->blob);
|
||||
param[1].u.memref.shm_offs = 0;
|
||||
|
||||
ret = tee_client_invoke_func(pvt_data.ctx, &inv_arg, param);
|
||||
if ((ret < 0) || (inv_arg.ret != 0)) {
|
||||
dev_err(pvt_data.dev, "TA_CMD_SEAL invoke err: %x\n",
|
||||
inv_arg.ret);
|
||||
ret = -EFAULT;
|
||||
} else {
|
||||
p->blob_len = param[1].u.memref.size;
|
||||
}
|
||||
|
||||
out:
|
||||
if (reg_shm_out)
|
||||
tee_shm_free(reg_shm_out);
|
||||
if (reg_shm_in)
|
||||
tee_shm_free(reg_shm_in);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Have the TEE unseal(decrypt) the symmetric key
|
||||
*/
|
||||
static int trusted_tee_unseal(struct trusted_key_payload *p, char *datablob)
|
||||
{
|
||||
int ret;
|
||||
struct tee_ioctl_invoke_arg inv_arg;
|
||||
struct tee_param param[4];
|
||||
struct tee_shm *reg_shm_in = NULL, *reg_shm_out = NULL;
|
||||
|
||||
memset(&inv_arg, 0, sizeof(inv_arg));
|
||||
memset(¶m, 0, sizeof(param));
|
||||
|
||||
reg_shm_in = tee_shm_register(pvt_data.ctx, (unsigned long)p->blob,
|
||||
p->blob_len, TEE_SHM_DMA_BUF |
|
||||
TEE_SHM_KERNEL_MAPPED);
|
||||
if (IS_ERR(reg_shm_in)) {
|
||||
dev_err(pvt_data.dev, "blob shm register failed\n");
|
||||
return PTR_ERR(reg_shm_in);
|
||||
}
|
||||
|
||||
reg_shm_out = tee_shm_register(pvt_data.ctx, (unsigned long)p->key,
|
||||
sizeof(p->key), TEE_SHM_DMA_BUF |
|
||||
TEE_SHM_KERNEL_MAPPED);
|
||||
if (IS_ERR(reg_shm_out)) {
|
||||
dev_err(pvt_data.dev, "key shm register failed\n");
|
||||
ret = PTR_ERR(reg_shm_out);
|
||||
goto out;
|
||||
}
|
||||
|
||||
inv_arg.func = TA_CMD_UNSEAL;
|
||||
inv_arg.session = pvt_data.session_id;
|
||||
inv_arg.num_params = 4;
|
||||
|
||||
param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
|
||||
param[0].u.memref.shm = reg_shm_in;
|
||||
param[0].u.memref.size = p->blob_len;
|
||||
param[0].u.memref.shm_offs = 0;
|
||||
param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT;
|
||||
param[1].u.memref.shm = reg_shm_out;
|
||||
param[1].u.memref.size = sizeof(p->key);
|
||||
param[1].u.memref.shm_offs = 0;
|
||||
|
||||
ret = tee_client_invoke_func(pvt_data.ctx, &inv_arg, param);
|
||||
if ((ret < 0) || (inv_arg.ret != 0)) {
|
||||
dev_err(pvt_data.dev, "TA_CMD_UNSEAL invoke err: %x\n",
|
||||
inv_arg.ret);
|
||||
ret = -EFAULT;
|
||||
} else {
|
||||
p->key_len = param[1].u.memref.size;
|
||||
}
|
||||
|
||||
out:
|
||||
if (reg_shm_out)
|
||||
tee_shm_free(reg_shm_out);
|
||||
if (reg_shm_in)
|
||||
tee_shm_free(reg_shm_in);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Have the TEE generate random symmetric key
|
||||
*/
|
||||
static int trusted_tee_get_random(unsigned char *key, size_t key_len)
|
||||
{
|
||||
int ret;
|
||||
struct tee_ioctl_invoke_arg inv_arg;
|
||||
struct tee_param param[4];
|
||||
struct tee_shm *reg_shm = NULL;
|
||||
|
||||
memset(&inv_arg, 0, sizeof(inv_arg));
|
||||
memset(¶m, 0, sizeof(param));
|
||||
|
||||
reg_shm = tee_shm_register(pvt_data.ctx, (unsigned long)key, key_len,
|
||||
TEE_SHM_DMA_BUF | TEE_SHM_KERNEL_MAPPED);
|
||||
if (IS_ERR(reg_shm)) {
|
||||
dev_err(pvt_data.dev, "key shm register failed\n");
|
||||
return PTR_ERR(reg_shm);
|
||||
}
|
||||
|
||||
inv_arg.func = TA_CMD_GET_RANDOM;
|
||||
inv_arg.session = pvt_data.session_id;
|
||||
inv_arg.num_params = 4;
|
||||
|
||||
param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT;
|
||||
param[0].u.memref.shm = reg_shm;
|
||||
param[0].u.memref.size = key_len;
|
||||
param[0].u.memref.shm_offs = 0;
|
||||
|
||||
ret = tee_client_invoke_func(pvt_data.ctx, &inv_arg, param);
|
||||
if ((ret < 0) || (inv_arg.ret != 0)) {
|
||||
dev_err(pvt_data.dev, "TA_CMD_GET_RANDOM invoke err: %x\n",
|
||||
inv_arg.ret);
|
||||
ret = -EFAULT;
|
||||
} else {
|
||||
ret = param[0].u.memref.size;
|
||||
}
|
||||
|
||||
tee_shm_free(reg_shm);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
|
||||
{
|
||||
if (ver->impl_id == TEE_IMPL_ID_OPTEE)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int trusted_key_probe(struct device *dev)
|
||||
{
|
||||
struct tee_client_device *rng_device = to_tee_client_device(dev);
|
||||
int ret;
|
||||
struct tee_ioctl_open_session_arg sess_arg;
|
||||
|
||||
memset(&sess_arg, 0, sizeof(sess_arg));
|
||||
|
||||
pvt_data.ctx = tee_client_open_context(NULL, optee_ctx_match, NULL,
|
||||
NULL);
|
||||
if (IS_ERR(pvt_data.ctx))
|
||||
return -ENODEV;
|
||||
|
||||
memcpy(sess_arg.uuid, rng_device->id.uuid.b, TEE_IOCTL_UUID_LEN);
|
||||
sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL;
|
||||
sess_arg.num_params = 0;
|
||||
|
||||
ret = tee_client_open_session(pvt_data.ctx, &sess_arg, NULL);
|
||||
if ((ret < 0) || (sess_arg.ret != 0)) {
|
||||
dev_err(dev, "tee_client_open_session failed, err: %x\n",
|
||||
sess_arg.ret);
|
||||
ret = -EINVAL;
|
||||
goto out_ctx;
|
||||
}
|
||||
pvt_data.session_id = sess_arg.session;
|
||||
|
||||
ret = register_key_type(&key_type_trusted);
|
||||
if (ret < 0)
|
||||
goto out_sess;
|
||||
|
||||
pvt_data.dev = dev;
|
||||
|
||||
return 0;
|
||||
|
||||
out_sess:
|
||||
tee_client_close_session(pvt_data.ctx, pvt_data.session_id);
|
||||
out_ctx:
|
||||
tee_client_close_context(pvt_data.ctx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int trusted_key_remove(struct device *dev)
|
||||
{
|
||||
unregister_key_type(&key_type_trusted);
|
||||
tee_client_close_session(pvt_data.ctx, pvt_data.session_id);
|
||||
tee_client_close_context(pvt_data.ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct tee_client_device_id trusted_key_id_table[] = {
|
||||
{UUID_INIT(0xf04a0fe7, 0x1f5d, 0x4b9b,
|
||||
0xab, 0xf7, 0x61, 0x9b, 0x85, 0xb4, 0xce, 0x8c)},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(tee, trusted_key_id_table);
|
||||
|
||||
static struct tee_client_driver trusted_key_driver = {
|
||||
.id_table = trusted_key_id_table,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.bus = &tee_bus_type,
|
||||
.probe = trusted_key_probe,
|
||||
.remove = trusted_key_remove,
|
||||
},
|
||||
};
|
||||
|
||||
static int trusted_tee_init(void)
|
||||
{
|
||||
return driver_register(&trusted_key_driver.driver);
|
||||
}
|
||||
|
||||
static void trusted_tee_exit(void)
|
||||
{
|
||||
driver_unregister(&trusted_key_driver.driver);
|
||||
}
|
||||
|
||||
struct trusted_key_ops trusted_key_tee_ops = {
|
||||
.migratable = 0, /* non-migratable */
|
||||
.init = trusted_tee_init,
|
||||
.seal = trusted_tee_seal,
|
||||
.unseal = trusted_tee_unseal,
|
||||
.get_random = trusted_tee_get_random,
|
||||
.exit = trusted_tee_exit,
|
||||
};
|
@ -1,29 +1,22 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2010 IBM Corporation
|
||||
*
|
||||
* Author:
|
||||
* David Safford <safford@us.ibm.com>
|
||||
* Copyright (c) 2019-2021, Linaro Limited
|
||||
*
|
||||
* See Documentation/security/keys/trusted-encrypted.rst
|
||||
*/
|
||||
|
||||
#include <crypto/hash_info.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/parser.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/err.h>
|
||||
#include <keys/user-type.h>
|
||||
#include <keys/trusted-type.h>
|
||||
#include <linux/key-type.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <crypto/hash.h>
|
||||
#include <crypto/sha1.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/tpm.h>
|
||||
#include <linux/tpm_command.h>
|
||||
|
||||
@ -63,7 +56,7 @@ static int TSS_sha1(const unsigned char *data, unsigned int datalen,
|
||||
|
||||
sdesc = init_sdesc(hashalg);
|
||||
if (IS_ERR(sdesc)) {
|
||||
pr_info("trusted_key: can't alloc %s\n", hash_alg);
|
||||
pr_info("can't alloc %s\n", hash_alg);
|
||||
return PTR_ERR(sdesc);
|
||||
}
|
||||
|
||||
@ -83,7 +76,7 @@ static int TSS_rawhmac(unsigned char *digest, const unsigned char *key,
|
||||
|
||||
sdesc = init_sdesc(hmacalg);
|
||||
if (IS_ERR(sdesc)) {
|
||||
pr_info("trusted_key: can't alloc %s\n", hmac_alg);
|
||||
pr_info("can't alloc %s\n", hmac_alg);
|
||||
return PTR_ERR(sdesc);
|
||||
}
|
||||
|
||||
@ -136,7 +129,7 @@ int TSS_authhmac(unsigned char *digest, const unsigned char *key,
|
||||
|
||||
sdesc = init_sdesc(hashalg);
|
||||
if (IS_ERR(sdesc)) {
|
||||
pr_info("trusted_key: can't alloc %s\n", hash_alg);
|
||||
pr_info("can't alloc %s\n", hash_alg);
|
||||
return PTR_ERR(sdesc);
|
||||
}
|
||||
|
||||
@ -212,7 +205,7 @@ int TSS_checkhmac1(unsigned char *buffer,
|
||||
|
||||
sdesc = init_sdesc(hashalg);
|
||||
if (IS_ERR(sdesc)) {
|
||||
pr_info("trusted_key: can't alloc %s\n", hash_alg);
|
||||
pr_info("can't alloc %s\n", hash_alg);
|
||||
return PTR_ERR(sdesc);
|
||||
}
|
||||
ret = crypto_shash_init(&sdesc->shash);
|
||||
@ -305,7 +298,7 @@ static int TSS_checkhmac2(unsigned char *buffer,
|
||||
|
||||
sdesc = init_sdesc(hashalg);
|
||||
if (IS_ERR(sdesc)) {
|
||||
pr_info("trusted_key: can't alloc %s\n", hash_alg);
|
||||
pr_info("can't alloc %s\n", hash_alg);
|
||||
return PTR_ERR(sdesc);
|
||||
}
|
||||
ret = crypto_shash_init(&sdesc->shash);
|
||||
@ -597,12 +590,12 @@ static int tpm_unseal(struct tpm_buf *tb,
|
||||
/* sessions for unsealing key and data */
|
||||
ret = oiap(tb, &authhandle1, enonce1);
|
||||
if (ret < 0) {
|
||||
pr_info("trusted_key: oiap failed (%d)\n", ret);
|
||||
pr_info("oiap failed (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = oiap(tb, &authhandle2, enonce2);
|
||||
if (ret < 0) {
|
||||
pr_info("trusted_key: oiap failed (%d)\n", ret);
|
||||
pr_info("oiap failed (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -612,7 +605,7 @@ static int tpm_unseal(struct tpm_buf *tb,
|
||||
return ret;
|
||||
|
||||
if (ret != TPM_NONCE_SIZE) {
|
||||
pr_info("trusted_key: tpm_get_random failed (%d)\n", ret);
|
||||
pr_info("tpm_get_random failed (%d)\n", ret);
|
||||
return -EIO;
|
||||
}
|
||||
ret = TSS_authhmac(authdata1, keyauth, TPM_NONCE_SIZE,
|
||||
@ -641,7 +634,7 @@ static int tpm_unseal(struct tpm_buf *tb,
|
||||
|
||||
ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE);
|
||||
if (ret < 0) {
|
||||
pr_info("trusted_key: authhmac failed (%d)\n", ret);
|
||||
pr_info("authhmac failed (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -653,7 +646,7 @@ static int tpm_unseal(struct tpm_buf *tb,
|
||||
*datalen, TPM_DATA_OFFSET + sizeof(uint32_t), 0,
|
||||
0);
|
||||
if (ret < 0) {
|
||||
pr_info("trusted_key: TSS_checkhmac2 failed (%d)\n", ret);
|
||||
pr_info("TSS_checkhmac2 failed (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
memcpy(data, tb->data + TPM_DATA_OFFSET + sizeof(uint32_t), *datalen);
|
||||
@ -680,7 +673,7 @@ static int key_seal(struct trusted_key_payload *p,
|
||||
p->key, p->key_len + 1, p->blob, &p->blob_len,
|
||||
o->blobauth, o->pcrinfo, o->pcrinfo_len);
|
||||
if (ret < 0)
|
||||
pr_info("trusted_key: srkseal failed (%d)\n", ret);
|
||||
pr_info("srkseal failed (%d)\n", ret);
|
||||
|
||||
tpm_buf_destroy(&tb);
|
||||
return ret;
|
||||
@ -702,7 +695,7 @@ static int key_unseal(struct trusted_key_payload *p,
|
||||
ret = tpm_unseal(&tb, o->keyhandle, o->keyauth, p->blob, p->blob_len,
|
||||
o->blobauth, p->key, &p->key_len);
|
||||
if (ret < 0)
|
||||
pr_info("trusted_key: srkunseal failed (%d)\n", ret);
|
||||
pr_info("srkunseal failed (%d)\n", ret);
|
||||
else
|
||||
/* pull migratable flag out of sealed key */
|
||||
p->migratable = p->key[--p->key_len];
|
||||
@ -713,7 +706,6 @@ static int key_unseal(struct trusted_key_payload *p,
|
||||
|
||||
enum {
|
||||
Opt_err,
|
||||
Opt_new, Opt_load, Opt_update,
|
||||
Opt_keyhandle, Opt_keyauth, Opt_blobauth,
|
||||
Opt_pcrinfo, Opt_pcrlock, Opt_migratable,
|
||||
Opt_hash,
|
||||
@ -722,9 +714,6 @@ enum {
|
||||
};
|
||||
|
||||
static const match_table_t key_tokens = {
|
||||
{Opt_new, "new"},
|
||||
{Opt_load, "load"},
|
||||
{Opt_update, "update"},
|
||||
{Opt_keyhandle, "keyhandle=%s"},
|
||||
{Opt_keyauth, "keyauth=%s"},
|
||||
{Opt_blobauth, "blobauth=%s"},
|
||||
@ -791,13 +780,33 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
|
||||
return -EINVAL;
|
||||
break;
|
||||
case Opt_blobauth:
|
||||
if (strlen(args[0].from) != 2 * SHA1_DIGEST_SIZE)
|
||||
return -EINVAL;
|
||||
res = hex2bin(opt->blobauth, args[0].from,
|
||||
SHA1_DIGEST_SIZE);
|
||||
if (res < 0)
|
||||
return -EINVAL;
|
||||
/*
|
||||
* TPM 1.2 authorizations are sha1 hashes passed in as
|
||||
* hex strings. TPM 2.0 authorizations are simple
|
||||
* passwords (although it can take a hash as well)
|
||||
*/
|
||||
opt->blobauth_len = strlen(args[0].from);
|
||||
|
||||
if (opt->blobauth_len == 2 * TPM_DIGEST_SIZE) {
|
||||
res = hex2bin(opt->blobauth, args[0].from,
|
||||
TPM_DIGEST_SIZE);
|
||||
if (res < 0)
|
||||
return -EINVAL;
|
||||
|
||||
opt->blobauth_len = TPM_DIGEST_SIZE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (tpm2 && opt->blobauth_len <= sizeof(opt->blobauth)) {
|
||||
memcpy(opt->blobauth, args[0].from,
|
||||
opt->blobauth_len);
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
|
||||
break;
|
||||
|
||||
case Opt_migratable:
|
||||
if (*args[0].from == '0')
|
||||
pay->migratable = 0;
|
||||
@ -822,7 +831,7 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
|
||||
if (i == HASH_ALGO__LAST)
|
||||
return -EINVAL;
|
||||
if (!tpm2 && i != HASH_ALGO_SHA1) {
|
||||
pr_info("trusted_key: TPM 1.x only supports SHA-1.\n");
|
||||
pr_info("TPM 1.x only supports SHA-1.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
@ -851,71 +860,6 @@ static int getoptions(char *c, struct trusted_key_payload *pay,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* datablob_parse - parse the keyctl data and fill in the
|
||||
* payload and options structures
|
||||
*
|
||||
* On success returns 0, otherwise -EINVAL.
|
||||
*/
|
||||
static int datablob_parse(char *datablob, struct trusted_key_payload *p,
|
||||
struct trusted_key_options *o)
|
||||
{
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
long keylen;
|
||||
int ret = -EINVAL;
|
||||
int key_cmd;
|
||||
char *c;
|
||||
|
||||
/* main command */
|
||||
c = strsep(&datablob, " \t");
|
||||
if (!c)
|
||||
return -EINVAL;
|
||||
key_cmd = match_token(c, key_tokens, args);
|
||||
switch (key_cmd) {
|
||||
case Opt_new:
|
||||
/* first argument is key size */
|
||||
c = strsep(&datablob, " \t");
|
||||
if (!c)
|
||||
return -EINVAL;
|
||||
ret = kstrtol(c, 10, &keylen);
|
||||
if (ret < 0 || keylen < MIN_KEY_SIZE || keylen > MAX_KEY_SIZE)
|
||||
return -EINVAL;
|
||||
p->key_len = keylen;
|
||||
ret = getoptions(datablob, p, o);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = Opt_new;
|
||||
break;
|
||||
case Opt_load:
|
||||
/* first argument is sealed blob */
|
||||
c = strsep(&datablob, " \t");
|
||||
if (!c)
|
||||
return -EINVAL;
|
||||
p->blob_len = strlen(c) / 2;
|
||||
if (p->blob_len > MAX_BLOB_SIZE)
|
||||
return -EINVAL;
|
||||
ret = hex2bin(p->blob, c, p->blob_len);
|
||||
if (ret < 0)
|
||||
return -EINVAL;
|
||||
ret = getoptions(datablob, p, o);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = Opt_load;
|
||||
break;
|
||||
case Opt_update:
|
||||
/* all arguments are options */
|
||||
ret = getoptions(datablob, p, o);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = Opt_update;
|
||||
break;
|
||||
case Opt_err:
|
||||
return -EINVAL;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct trusted_key_options *trusted_options_alloc(void)
|
||||
{
|
||||
struct trusted_key_options *options;
|
||||
@ -936,252 +880,99 @@ static struct trusted_key_options *trusted_options_alloc(void)
|
||||
return options;
|
||||
}
|
||||
|
||||
static struct trusted_key_payload *trusted_payload_alloc(struct key *key)
|
||||
static int trusted_tpm_seal(struct trusted_key_payload *p, char *datablob)
|
||||
{
|
||||
struct trusted_key_payload *p = NULL;
|
||||
int ret;
|
||||
|
||||
ret = key_payload_reserve(key, sizeof *p);
|
||||
if (ret < 0)
|
||||
return p;
|
||||
p = kzalloc(sizeof *p, GFP_KERNEL);
|
||||
if (p)
|
||||
p->migratable = 1; /* migratable by default */
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
* trusted_instantiate - create a new trusted key
|
||||
*
|
||||
* Unseal an existing trusted blob or, for a new key, get a
|
||||
* random key, then seal and create a trusted key-type key,
|
||||
* adding it to the specified keyring.
|
||||
*
|
||||
* On success, return 0. Otherwise return errno.
|
||||
*/
|
||||
static int trusted_instantiate(struct key *key,
|
||||
struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct trusted_key_payload *payload = NULL;
|
||||
struct trusted_key_options *options = NULL;
|
||||
size_t datalen = prep->datalen;
|
||||
char *datablob;
|
||||
int ret = 0;
|
||||
int key_cmd;
|
||||
size_t key_len;
|
||||
int tpm2;
|
||||
|
||||
tpm2 = tpm_is_tpm2(chip);
|
||||
if (tpm2 < 0)
|
||||
return tpm2;
|
||||
|
||||
if (datalen <= 0 || datalen > 32767 || !prep->data)
|
||||
return -EINVAL;
|
||||
|
||||
datablob = kmalloc(datalen + 1, GFP_KERNEL);
|
||||
if (!datablob)
|
||||
options = trusted_options_alloc();
|
||||
if (!options)
|
||||
return -ENOMEM;
|
||||
memcpy(datablob, prep->data, datalen);
|
||||
datablob[datalen] = '\0';
|
||||
|
||||
ret = getoptions(datablob, p, options);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
dump_options(options);
|
||||
|
||||
if (!options->keyhandle && !tpm2) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (tpm2)
|
||||
ret = tpm2_seal_trusted(chip, p, options);
|
||||
else
|
||||
ret = key_seal(p, options);
|
||||
if (ret < 0) {
|
||||
pr_info("key_seal failed (%d)\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (options->pcrlock) {
|
||||
ret = pcrlock(options->pcrlock);
|
||||
if (ret < 0) {
|
||||
pr_info("pcrlock failed (%d)\n", ret);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
out:
|
||||
kfree_sensitive(options);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int trusted_tpm_unseal(struct trusted_key_payload *p, char *datablob)
|
||||
{
|
||||
struct trusted_key_options *options = NULL;
|
||||
int ret = 0;
|
||||
int tpm2;
|
||||
|
||||
tpm2 = tpm_is_tpm2(chip);
|
||||
if (tpm2 < 0)
|
||||
return tpm2;
|
||||
|
||||
options = trusted_options_alloc();
|
||||
if (!options) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
payload = trusted_payload_alloc(key);
|
||||
if (!payload) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
if (!options)
|
||||
return -ENOMEM;
|
||||
|
||||
key_cmd = datablob_parse(datablob, payload, options);
|
||||
if (key_cmd < 0) {
|
||||
ret = key_cmd;
|
||||
ret = getoptions(datablob, p, options);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
dump_options(options);
|
||||
|
||||
if (!options->keyhandle) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dump_payload(payload);
|
||||
dump_options(options);
|
||||
|
||||
switch (key_cmd) {
|
||||
case Opt_load:
|
||||
if (tpm2)
|
||||
ret = tpm2_unseal_trusted(chip, payload, options);
|
||||
else
|
||||
ret = key_unseal(payload, options);
|
||||
dump_payload(payload);
|
||||
dump_options(options);
|
||||
if (ret < 0)
|
||||
pr_info("trusted_key: key_unseal failed (%d)\n", ret);
|
||||
break;
|
||||
case Opt_new:
|
||||
key_len = payload->key_len;
|
||||
ret = tpm_get_random(chip, payload->key, key_len);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (ret != key_len) {
|
||||
pr_info("trusted_key: key_create failed (%d)\n", ret);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
if (tpm2)
|
||||
ret = tpm2_seal_trusted(chip, payload, options);
|
||||
else
|
||||
ret = key_seal(payload, options);
|
||||
if (ret < 0)
|
||||
pr_info("trusted_key: key_seal failed (%d)\n", ret);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (!ret && options->pcrlock)
|
||||
ret = pcrlock(options->pcrlock);
|
||||
out:
|
||||
kfree_sensitive(datablob);
|
||||
kfree_sensitive(options);
|
||||
if (!ret)
|
||||
rcu_assign_keypointer(key, payload);
|
||||
if (tpm2)
|
||||
ret = tpm2_unseal_trusted(chip, p, options);
|
||||
else
|
||||
kfree_sensitive(payload);
|
||||
return ret;
|
||||
}
|
||||
ret = key_unseal(p, options);
|
||||
if (ret < 0)
|
||||
pr_info("key_unseal failed (%d)\n", ret);
|
||||
|
||||
static void trusted_rcu_free(struct rcu_head *rcu)
|
||||
{
|
||||
struct trusted_key_payload *p;
|
||||
|
||||
p = container_of(rcu, struct trusted_key_payload, rcu);
|
||||
kfree_sensitive(p);
|
||||
}
|
||||
|
||||
/*
|
||||
* trusted_update - reseal an existing key with new PCR values
|
||||
*/
|
||||
static int trusted_update(struct key *key, struct key_preparsed_payload *prep)
|
||||
{
|
||||
struct trusted_key_payload *p;
|
||||
struct trusted_key_payload *new_p;
|
||||
struct trusted_key_options *new_o;
|
||||
size_t datalen = prep->datalen;
|
||||
char *datablob;
|
||||
int ret = 0;
|
||||
|
||||
if (key_is_negative(key))
|
||||
return -ENOKEY;
|
||||
p = key->payload.data[0];
|
||||
if (!p->migratable)
|
||||
return -EPERM;
|
||||
if (datalen <= 0 || datalen > 32767 || !prep->data)
|
||||
return -EINVAL;
|
||||
|
||||
datablob = kmalloc(datalen + 1, GFP_KERNEL);
|
||||
if (!datablob)
|
||||
return -ENOMEM;
|
||||
new_o = trusted_options_alloc();
|
||||
if (!new_o) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
new_p = trusted_payload_alloc(key);
|
||||
if (!new_p) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(datablob, prep->data, datalen);
|
||||
datablob[datalen] = '\0';
|
||||
ret = datablob_parse(datablob, new_p, new_o);
|
||||
if (ret != Opt_update) {
|
||||
ret = -EINVAL;
|
||||
kfree_sensitive(new_p);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!new_o->keyhandle) {
|
||||
ret = -EINVAL;
|
||||
kfree_sensitive(new_p);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* copy old key values, and reseal with new pcrs */
|
||||
new_p->migratable = p->migratable;
|
||||
new_p->key_len = p->key_len;
|
||||
memcpy(new_p->key, p->key, p->key_len);
|
||||
dump_payload(p);
|
||||
dump_payload(new_p);
|
||||
|
||||
ret = key_seal(new_p, new_o);
|
||||
if (ret < 0) {
|
||||
pr_info("trusted_key: key_seal failed (%d)\n", ret);
|
||||
kfree_sensitive(new_p);
|
||||
goto out;
|
||||
}
|
||||
if (new_o->pcrlock) {
|
||||
ret = pcrlock(new_o->pcrlock);
|
||||
if (options->pcrlock) {
|
||||
ret = pcrlock(options->pcrlock);
|
||||
if (ret < 0) {
|
||||
pr_info("trusted_key: pcrlock failed (%d)\n", ret);
|
||||
kfree_sensitive(new_p);
|
||||
pr_info("pcrlock failed (%d)\n", ret);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
rcu_assign_keypointer(key, new_p);
|
||||
call_rcu(&p->rcu, trusted_rcu_free);
|
||||
out:
|
||||
kfree_sensitive(datablob);
|
||||
kfree_sensitive(new_o);
|
||||
kfree_sensitive(options);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* trusted_read - copy the sealed blob data to userspace in hex.
|
||||
* On success, return to userspace the trusted key datablob size.
|
||||
*/
|
||||
static long trusted_read(const struct key *key, char *buffer,
|
||||
size_t buflen)
|
||||
static int trusted_tpm_get_random(unsigned char *key, size_t key_len)
|
||||
{
|
||||
const struct trusted_key_payload *p;
|
||||
char *bufp;
|
||||
int i;
|
||||
|
||||
p = dereference_key_locked(key);
|
||||
if (!p)
|
||||
return -EINVAL;
|
||||
|
||||
if (buffer && buflen >= 2 * p->blob_len) {
|
||||
bufp = buffer;
|
||||
for (i = 0; i < p->blob_len; i++)
|
||||
bufp = hex_byte_pack(bufp, p->blob[i]);
|
||||
}
|
||||
return 2 * p->blob_len;
|
||||
return tpm_get_random(chip, key, key_len);
|
||||
}
|
||||
|
||||
/*
|
||||
* trusted_destroy - clear and free the key's payload
|
||||
*/
|
||||
static void trusted_destroy(struct key *key)
|
||||
{
|
||||
kfree_sensitive(key->payload.data[0]);
|
||||
}
|
||||
|
||||
struct key_type key_type_trusted = {
|
||||
.name = "trusted",
|
||||
.instantiate = trusted_instantiate,
|
||||
.update = trusted_update,
|
||||
.destroy = trusted_destroy,
|
||||
.describe = user_describe,
|
||||
.read = trusted_read,
|
||||
};
|
||||
|
||||
EXPORT_SYMBOL_GPL(key_type_trusted);
|
||||
|
||||
static void trusted_shash_release(void)
|
||||
{
|
||||
if (hashalg)
|
||||
@ -1196,14 +987,14 @@ static int __init trusted_shash_alloc(void)
|
||||
|
||||
hmacalg = crypto_alloc_shash(hmac_alg, 0, 0);
|
||||
if (IS_ERR(hmacalg)) {
|
||||
pr_info("trusted_key: could not allocate crypto %s\n",
|
||||
pr_info("could not allocate crypto %s\n",
|
||||
hmac_alg);
|
||||
return PTR_ERR(hmacalg);
|
||||
}
|
||||
|
||||
hashalg = crypto_alloc_shash(hash_alg, 0, 0);
|
||||
if (IS_ERR(hashalg)) {
|
||||
pr_info("trusted_key: could not allocate crypto %s\n",
|
||||
pr_info("could not allocate crypto %s\n",
|
||||
hash_alg);
|
||||
ret = PTR_ERR(hashalg);
|
||||
goto hashalg_fail;
|
||||
@ -1231,16 +1022,13 @@ static int __init init_digests(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init init_trusted(void)
|
||||
static int __init trusted_tpm_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* encrypted_keys.ko depends on successful load of this module even if
|
||||
* TPM is not used.
|
||||
*/
|
||||
chip = tpm_default_chip();
|
||||
if (!chip)
|
||||
return 0;
|
||||
return -ENODEV;
|
||||
|
||||
ret = init_digests();
|
||||
if (ret < 0)
|
||||
@ -1261,7 +1049,7 @@ static int __init init_trusted(void)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit cleanup_trusted(void)
|
||||
static void trusted_tpm_exit(void)
|
||||
{
|
||||
if (chip) {
|
||||
put_device(&chip->dev);
|
||||
@ -1271,7 +1059,11 @@ static void __exit cleanup_trusted(void)
|
||||
}
|
||||
}
|
||||
|
||||
late_initcall(init_trusted);
|
||||
module_exit(cleanup_trusted);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
struct trusted_key_ops trusted_key_tpm_ops = {
|
||||
.migratable = 1, /* migratable by default */
|
||||
.init = trusted_tpm_init,
|
||||
.seal = trusted_tpm_seal,
|
||||
.unseal = trusted_tpm_unseal,
|
||||
.get_random = trusted_tpm_get_random,
|
||||
.exit = trusted_tpm_exit,
|
||||
};
|
||||
|
@ -4,6 +4,8 @@
|
||||
* Copyright (C) 2014 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <linux/asn1_encoder.h>
|
||||
#include <linux/oid_registry.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/tpm.h>
|
||||
@ -12,6 +14,10 @@
|
||||
#include <keys/trusted-type.h>
|
||||
#include <keys/trusted_tpm.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include "tpm2key.asn1.h"
|
||||
|
||||
static struct tpm2_hash tpm2_hash_map[] = {
|
||||
{HASH_ALGO_SHA1, TPM_ALG_SHA1},
|
||||
{HASH_ALGO_SHA256, TPM_ALG_SHA256},
|
||||
@ -20,6 +26,165 @@ static struct tpm2_hash tpm2_hash_map[] = {
|
||||
{HASH_ALGO_SM3_256, TPM_ALG_SM3_256},
|
||||
};
|
||||
|
||||
static u32 tpm2key_oid[] = { 2, 23, 133, 10, 1, 5 };
|
||||
|
||||
static int tpm2_key_encode(struct trusted_key_payload *payload,
|
||||
struct trusted_key_options *options,
|
||||
u8 *src, u32 len)
|
||||
{
|
||||
const int SCRATCH_SIZE = PAGE_SIZE;
|
||||
u8 *scratch = kmalloc(SCRATCH_SIZE, GFP_KERNEL);
|
||||
u8 *work = scratch, *work1;
|
||||
u8 *end_work = scratch + SCRATCH_SIZE;
|
||||
u8 *priv, *pub;
|
||||
u16 priv_len, pub_len;
|
||||
|
||||
priv_len = get_unaligned_be16(src) + 2;
|
||||
priv = src;
|
||||
|
||||
src += priv_len;
|
||||
|
||||
pub_len = get_unaligned_be16(src) + 2;
|
||||
pub = src;
|
||||
|
||||
if (!scratch)
|
||||
return -ENOMEM;
|
||||
|
||||
work = asn1_encode_oid(work, end_work, tpm2key_oid,
|
||||
asn1_oid_len(tpm2key_oid));
|
||||
|
||||
if (options->blobauth_len == 0) {
|
||||
unsigned char bool[3], *w = bool;
|
||||
/* tag 0 is emptyAuth */
|
||||
w = asn1_encode_boolean(w, w + sizeof(bool), true);
|
||||
if (WARN(IS_ERR(w), "BUG: Boolean failed to encode"))
|
||||
return PTR_ERR(w);
|
||||
work = asn1_encode_tag(work, end_work, 0, bool, w - bool);
|
||||
}
|
||||
|
||||
/*
|
||||
* Assume both octet strings will encode to a 2 byte definite length
|
||||
*
|
||||
* Note: For a well behaved TPM, this warning should never
|
||||
* trigger, so if it does there's something nefarious going on
|
||||
*/
|
||||
if (WARN(work - scratch + pub_len + priv_len + 14 > SCRATCH_SIZE,
|
||||
"BUG: scratch buffer is too small"))
|
||||
return -EINVAL;
|
||||
|
||||
work = asn1_encode_integer(work, end_work, options->keyhandle);
|
||||
work = asn1_encode_octet_string(work, end_work, pub, pub_len);
|
||||
work = asn1_encode_octet_string(work, end_work, priv, priv_len);
|
||||
|
||||
work1 = payload->blob;
|
||||
work1 = asn1_encode_sequence(work1, work1 + sizeof(payload->blob),
|
||||
scratch, work - scratch);
|
||||
if (WARN(IS_ERR(work1), "BUG: ASN.1 encoder failed"))
|
||||
return PTR_ERR(work1);
|
||||
|
||||
return work1 - payload->blob;
|
||||
}
|
||||
|
||||
struct tpm2_key_context {
|
||||
u32 parent;
|
||||
const u8 *pub;
|
||||
u32 pub_len;
|
||||
const u8 *priv;
|
||||
u32 priv_len;
|
||||
};
|
||||
|
||||
static int tpm2_key_decode(struct trusted_key_payload *payload,
|
||||
struct trusted_key_options *options,
|
||||
u8 **buf)
|
||||
{
|
||||
int ret;
|
||||
struct tpm2_key_context ctx;
|
||||
u8 *blob;
|
||||
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
|
||||
ret = asn1_ber_decoder(&tpm2key_decoder, &ctx, payload->blob,
|
||||
payload->blob_len);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ctx.priv_len + ctx.pub_len > MAX_BLOB_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
blob = kmalloc(ctx.priv_len + ctx.pub_len + 4, GFP_KERNEL);
|
||||
if (!blob)
|
||||
return -ENOMEM;
|
||||
|
||||
*buf = blob;
|
||||
options->keyhandle = ctx.parent;
|
||||
|
||||
memcpy(blob, ctx.priv, ctx.priv_len);
|
||||
blob += ctx.priv_len;
|
||||
|
||||
memcpy(blob, ctx.pub, ctx.pub_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tpm2_key_parent(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct tpm2_key_context *ctx = context;
|
||||
const u8 *v = value;
|
||||
int i;
|
||||
|
||||
ctx->parent = 0;
|
||||
for (i = 0; i < vlen; i++) {
|
||||
ctx->parent <<= 8;
|
||||
ctx->parent |= v[i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tpm2_key_type(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
enum OID oid = look_up_OID(value, vlen);
|
||||
|
||||
if (oid != OID_TPMSealedData) {
|
||||
char buffer[50];
|
||||
|
||||
sprint_oid(value, vlen, buffer, sizeof(buffer));
|
||||
pr_debug("OID is \"%s\" which is not TPMSealedData\n",
|
||||
buffer);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tpm2_key_pub(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct tpm2_key_context *ctx = context;
|
||||
|
||||
ctx->pub = value;
|
||||
ctx->pub_len = vlen;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tpm2_key_priv(void *context, size_t hdrlen,
|
||||
unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
struct tpm2_key_context *ctx = context;
|
||||
|
||||
ctx->priv = value;
|
||||
ctx->priv_len = vlen;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer.
|
||||
*
|
||||
@ -63,9 +228,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
|
||||
struct trusted_key_payload *payload,
|
||||
struct trusted_key_options *options)
|
||||
{
|
||||
unsigned int blob_len;
|
||||
int blob_len = 0;
|
||||
struct tpm_buf buf;
|
||||
u32 hash;
|
||||
u32 flags;
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
@ -79,6 +245,9 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
|
||||
if (i == ARRAY_SIZE(tpm2_hash_map))
|
||||
return -EINVAL;
|
||||
|
||||
if (!options->keyhandle)
|
||||
return -EINVAL;
|
||||
|
||||
rc = tpm_try_get_ops(chip);
|
||||
if (rc)
|
||||
return rc;
|
||||
@ -97,29 +266,32 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
|
||||
TPM_DIGEST_SIZE);
|
||||
|
||||
/* sensitive */
|
||||
tpm_buf_append_u16(&buf, 4 + TPM_DIGEST_SIZE + payload->key_len + 1);
|
||||
tpm_buf_append_u16(&buf, 4 + options->blobauth_len + payload->key_len);
|
||||
|
||||
tpm_buf_append_u16(&buf, TPM_DIGEST_SIZE);
|
||||
tpm_buf_append(&buf, options->blobauth, TPM_DIGEST_SIZE);
|
||||
tpm_buf_append_u16(&buf, payload->key_len + 1);
|
||||
tpm_buf_append_u16(&buf, options->blobauth_len);
|
||||
if (options->blobauth_len)
|
||||
tpm_buf_append(&buf, 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_u8(&buf, payload->migratable);
|
||||
|
||||
/* public */
|
||||
tpm_buf_append_u16(&buf, 14 + options->policydigest_len);
|
||||
tpm_buf_append_u16(&buf, TPM_ALG_KEYEDHASH);
|
||||
tpm_buf_append_u16(&buf, hash);
|
||||
|
||||
/* key properties */
|
||||
flags = 0;
|
||||
flags |= options->policydigest_len ? 0 : TPM2_OA_USER_WITH_AUTH;
|
||||
flags |= payload->migratable ? (TPM2_OA_FIXED_TPM |
|
||||
TPM2_OA_FIXED_PARENT) : 0;
|
||||
tpm_buf_append_u32(&buf, flags);
|
||||
|
||||
/* policy */
|
||||
if (options->policydigest_len) {
|
||||
tpm_buf_append_u32(&buf, 0);
|
||||
tpm_buf_append_u16(&buf, options->policydigest_len);
|
||||
tpm_buf_append_u16(&buf, options->policydigest_len);
|
||||
if (options->policydigest_len)
|
||||
tpm_buf_append(&buf, options->policydigest,
|
||||
options->policydigest_len);
|
||||
} else {
|
||||
tpm_buf_append_u32(&buf, TPM2_OA_USER_WITH_AUTH);
|
||||
tpm_buf_append_u16(&buf, 0);
|
||||
}
|
||||
|
||||
/* public parameters */
|
||||
tpm_buf_append_u16(&buf, TPM_ALG_NULL);
|
||||
@ -150,8 +322,9 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(payload->blob, &buf.data[TPM_HEADER_SIZE + 4], blob_len);
|
||||
payload->blob_len = blob_len;
|
||||
blob_len = tpm2_key_encode(payload, options,
|
||||
&buf.data[TPM_HEADER_SIZE + 4],
|
||||
blob_len);
|
||||
|
||||
out:
|
||||
tpm_buf_destroy(&buf);
|
||||
@ -162,6 +335,10 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
|
||||
else
|
||||
rc = -EPERM;
|
||||
}
|
||||
if (blob_len < 0)
|
||||
return blob_len;
|
||||
|
||||
payload->blob_len = blob_len;
|
||||
|
||||
tpm_put_ops(chip);
|
||||
return rc;
|
||||
@ -189,13 +366,45 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
|
||||
unsigned int private_len;
|
||||
unsigned int public_len;
|
||||
unsigned int blob_len;
|
||||
u8 *blob, *pub;
|
||||
int rc;
|
||||
u32 attrs;
|
||||
|
||||
private_len = be16_to_cpup((__be16 *) &payload->blob[0]);
|
||||
if (private_len > (payload->blob_len - 2))
|
||||
rc = tpm2_key_decode(payload, options, &blob);
|
||||
if (rc) {
|
||||
/* old form */
|
||||
blob = payload->blob;
|
||||
payload->old_format = 1;
|
||||
}
|
||||
|
||||
/* new format carries keyhandle but old format doesn't */
|
||||
if (!options->keyhandle)
|
||||
return -EINVAL;
|
||||
|
||||
/* must be big enough for at least the two be16 size counts */
|
||||
if (payload->blob_len < 4)
|
||||
return -EINVAL;
|
||||
|
||||
private_len = get_unaligned_be16(blob);
|
||||
|
||||
/* must be big enough for following public_len */
|
||||
if (private_len + 2 + 2 > (payload->blob_len))
|
||||
return -E2BIG;
|
||||
|
||||
public_len = be16_to_cpup((__be16 *) &payload->blob[2 + private_len]);
|
||||
public_len = get_unaligned_be16(blob + 2 + private_len);
|
||||
if (private_len + 2 + public_len + 2 > payload->blob_len)
|
||||
return -E2BIG;
|
||||
|
||||
pub = blob + 2 + private_len + 2;
|
||||
/* key attributes are always at offset 4 */
|
||||
attrs = get_unaligned_be32(pub + 4);
|
||||
|
||||
if ((attrs & (TPM2_OA_FIXED_TPM | TPM2_OA_FIXED_PARENT)) ==
|
||||
(TPM2_OA_FIXED_TPM | TPM2_OA_FIXED_PARENT))
|
||||
payload->migratable = 0;
|
||||
else
|
||||
payload->migratable = 1;
|
||||
|
||||
blob_len = private_len + public_len + 4;
|
||||
if (blob_len > payload->blob_len)
|
||||
return -E2BIG;
|
||||
@ -211,7 +420,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
|
||||
options->keyauth /* hmac */,
|
||||
TPM_DIGEST_SIZE);
|
||||
|
||||
tpm_buf_append(&buf, payload->blob, blob_len);
|
||||
tpm_buf_append(&buf, blob, blob_len);
|
||||
|
||||
if (buf.flags & TPM_BUF_OVERFLOW) {
|
||||
rc = -E2BIG;
|
||||
@ -224,6 +433,8 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
|
||||
(__be32 *) &buf.data[TPM_HEADER_SIZE]);
|
||||
|
||||
out:
|
||||
if (blob != payload->blob)
|
||||
kfree(blob);
|
||||
tpm_buf_destroy(&buf);
|
||||
|
||||
if (rc > 0)
|
||||
@ -265,7 +476,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
|
||||
NULL /* nonce */, 0,
|
||||
TPM2_SA_CONTINUE_SESSION,
|
||||
options->blobauth /* hmac */,
|
||||
TPM_DIGEST_SIZE);
|
||||
options->blobauth_len);
|
||||
|
||||
rc = tpm_transmit_cmd(chip, &buf, 6, "unsealing");
|
||||
if (rc > 0)
|
||||
@ -274,7 +485,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
|
||||
if (!rc) {
|
||||
data_len = be16_to_cpup(
|
||||
(__be16 *) &buf.data[TPM_HEADER_SIZE + 4]);
|
||||
if (data_len < MIN_KEY_SIZE || data_len > MAX_KEY_SIZE + 1) {
|
||||
if (data_len < MIN_KEY_SIZE || data_len > MAX_KEY_SIZE) {
|
||||
rc = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
@ -285,9 +496,19 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
|
||||
}
|
||||
data = &buf.data[TPM_HEADER_SIZE + 6];
|
||||
|
||||
memcpy(payload->key, data, data_len - 1);
|
||||
payload->key_len = data_len - 1;
|
||||
payload->migratable = data[data_len - 1];
|
||||
if (payload->old_format) {
|
||||
/* migratable flag is at the end of the key */
|
||||
memcpy(payload->key, data, data_len - 1);
|
||||
payload->key_len = data_len - 1;
|
||||
payload->migratable = data[data_len - 1];
|
||||
} else {
|
||||
/*
|
||||
* migratable flag already collected from key
|
||||
* attributes
|
||||
*/
|
||||
memcpy(payload->key, data, data_len);
|
||||
payload->key_len = data_len;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
|
Loading…
Reference in New Issue
Block a user