tpmdd updates for v5.19-rc1
- Strictened validation of key hashes for SYSTEM_BLACKLIST_HASH_LIST. An invalid hash format causes a compilation error. Previously, they got included to the kernel binary but were silently ignored at run-time. - Allow root user to append new hashes to the blacklist keyring. - Trusted keys backed with Cryptographic Acceleration and Assurance Module (CAAM), which part of some of the new NXP's SoC's. Now there is total three hardware backends for trusted keys: TPM, ARM TEE and CAAM. - A scattered set of fixes and small improvements for the TPM driver. Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org> -----BEGIN PGP SIGNATURE----- iIgEABYIADAWIQRE6pSOnaBC00OEHEIaerohdGur0gUCYoux6xIcamFya2tvQGtl cm5lbC5vcmcACgkQGnq6IXRrq9LTQgEA4zRrlmLPjhZ1iZpPZiyBBv5eOx20/c+y R7tCfJFB2+ABAOT1E885vt+GgKTY4mYloHJ+ZtnTIf1QRMP6EoSX+TwP =oBOO -----END PGP SIGNATURE----- Merge tag 'tpmdd-next-v5.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-tpmdd Pull tpm updates from Jarkko Sakkinen: - Tightened validation of key hashes for SYSTEM_BLACKLIST_HASH_LIST. An invalid hash format causes a compilation error. Previously, they got included to the kernel binary but were silently ignored at run-time. - Allow root user to append new hashes to the blacklist keyring. - Trusted keys backed with Cryptographic Acceleration and Assurance Module (CAAM), which part of some of the new NXP's SoC's. Now there is total three hardware backends for trusted keys: TPM, ARM TEE and CAAM. - A scattered set of fixes and small improvements for the TPM driver. * tag 'tpmdd-next-v5.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-tpmdd: MAINTAINERS: add KEYS-TRUSTED-CAAM doc: trusted-encrypted: describe new CAAM trust source KEYS: trusted: Introduce support for NXP CAAM-based trusted keys crypto: caam - add in-kernel interface for blob generator crypto: caam - determine whether CAAM supports blob encap/decap KEYS: trusted: allow use of kernel RNG for key material KEYS: trusted: allow use of TEE as backend without TCG_TPM support tpm: Add field upgrade mode support for Infineon TPM2 modules tpm: Fix buffer access in tpm2_get_tpm_pt() char: tpm: cr50_i2c: Suppress duplicated error message in .remove() tpm: cr50: Add new device/vendor ID 0x504a6666 tpm: Remove read16/read32/write32 calls from tpm_tis_phy_ops tpm: ibmvtpm: Correct the return value in tpm_ibmvtpm_probe() tpm/tpm_ftpm_tee: Return true/false (not 1/0) from bool functions certs: Explain the rationale to call panic() certs: Allow root user to append signed hashes to the blacklist keyring certs: Check that builtin blacklist hashes are valid certs: Make blacklist_vet_description() more strict certs: Factor out the blacklist hash creation tools/certs: Add print-cert-tbs-hash.sh
This commit is contained in:
commit
7cf6a8a17f
@ -6081,11 +6081,22 @@
|
||||
sources:
|
||||
- "tpm"
|
||||
- "tee"
|
||||
- "caam"
|
||||
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.
|
||||
|
||||
trusted.rng= [KEYS]
|
||||
Format: <string>
|
||||
The RNG used to generate key material for trusted keys.
|
||||
Can be one of:
|
||||
- "kernel"
|
||||
- the same value as trusted.source: "tpm" or "tee"
|
||||
- "default"
|
||||
If not specified, "default" is used. In this case,
|
||||
the RNG's choice is left to each individual trust source.
|
||||
|
||||
tsc= Disable clocksource stability checks for TSC.
|
||||
Format: <string>
|
||||
[x86] reliable: mark tsc clocksource as reliable, this
|
||||
|
@ -35,6 +35,13 @@ safe.
|
||||
Rooted to Hardware Unique Key (HUK) which is generally burnt in on-chip
|
||||
fuses and is accessible to TEE only.
|
||||
|
||||
(3) CAAM (Cryptographic Acceleration and Assurance Module: IP on NXP SoCs)
|
||||
|
||||
When High Assurance Boot (HAB) is enabled and the CAAM is in secure
|
||||
mode, trust is rooted to the OTPMK, a never-disclosed 256-bit key
|
||||
randomly generated and fused into each SoC at manufacturing time.
|
||||
Otherwise, a common fixed test key is used instead.
|
||||
|
||||
* Execution isolation
|
||||
|
||||
(1) TPM
|
||||
@ -46,6 +53,10 @@ safe.
|
||||
Customizable set of operations running in isolated execution
|
||||
environment verified via Secure/Trusted boot process.
|
||||
|
||||
(3) CAAM
|
||||
|
||||
Fixed set of operations running in isolated execution environment.
|
||||
|
||||
* Optional binding to platform integrity state
|
||||
|
||||
(1) TPM
|
||||
@ -63,6 +74,11 @@ safe.
|
||||
Relies on Secure/Trusted boot process for platform integrity. It can
|
||||
be extended with TEE based measured boot process.
|
||||
|
||||
(3) CAAM
|
||||
|
||||
Relies on the High Assurance Boot (HAB) mechanism of NXP SoCs
|
||||
for platform integrity.
|
||||
|
||||
* Interfaces and APIs
|
||||
|
||||
(1) TPM
|
||||
@ -74,10 +90,13 @@ safe.
|
||||
TEEs have well-documented, standardized client interface and APIs. For
|
||||
more details refer to ``Documentation/staging/tee.rst``.
|
||||
|
||||
(3) CAAM
|
||||
|
||||
Interface is specific to silicon vendor.
|
||||
|
||||
* Threat model
|
||||
|
||||
The strength and appropriateness of a particular TPM or TEE for a given
|
||||
The strength and appropriateness of a particular trust source for a given
|
||||
purpose must be assessed when using them to protect security-relevant data.
|
||||
|
||||
|
||||
@ -87,22 +106,32 @@ 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.
|
||||
New keys are created from random numbers. 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. The random number generator in use differs according to the
|
||||
selected trust source:
|
||||
|
||||
* TPM (hardware device) based RNG
|
||||
* TPM: hardware device based RNG
|
||||
|
||||
Strength of random numbers may vary from one device manufacturer to
|
||||
another.
|
||||
Keys are generated within the TPM. Strength of random numbers may vary
|
||||
from one device manufacturer to another.
|
||||
|
||||
* TEE (OP-TEE based on Arm TrustZone) based RNG
|
||||
* 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.
|
||||
|
||||
* CAAM: Kernel RNG
|
||||
|
||||
The normal kernel random number generator is used. To seed it from the
|
||||
CAAM HWRNG, enable CRYPTO_DEV_FSL_CAAM_RNG_API and ensure the device
|
||||
is probed.
|
||||
|
||||
Users may override this by specifying ``trusted.rng=kernel`` on the kernel
|
||||
command-line to override the used RNG with the kernel's random number pool.
|
||||
|
||||
Encrypted Keys
|
||||
--------------
|
||||
|
||||
@ -189,6 +218,19 @@ Usage::
|
||||
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).
|
||||
|
||||
Trusted Keys usage: CAAM
|
||||
------------------------
|
||||
|
||||
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 a
|
||||
CAAM-specific format. The key length for new keys is always in bytes.
|
||||
Trusted Keys can be 32 - 128 bytes (256 - 1024 bits).
|
||||
|
||||
Encrypted Keys usage
|
||||
--------------------
|
||||
|
||||
|
11
MAINTAINERS
11
MAINTAINERS
@ -4575,7 +4575,9 @@ L: keyrings@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/admin-guide/module-signing.rst
|
||||
F: certs/
|
||||
F: scripts/check-blacklist-hashes.awk
|
||||
F: scripts/sign-file.c
|
||||
F: tools/certs/
|
||||
|
||||
CFAG12864B LCD DRIVER
|
||||
M: Miguel Ojeda <ojeda@kernel.org>
|
||||
@ -10865,6 +10867,15 @@ S: Supported
|
||||
F: include/keys/trusted_tee.h
|
||||
F: security/keys/trusted-keys/trusted_tee.c
|
||||
|
||||
KEYS-TRUSTED-CAAM
|
||||
M: Ahmad Fatoum <a.fatoum@pengutronix.de>
|
||||
R: Pengutronix Kernel Team <kernel@pengutronix.de>
|
||||
L: linux-integrity@vger.kernel.org
|
||||
L: keyrings@vger.kernel.org
|
||||
S: Maintained
|
||||
F: include/keys/trusted_caam.h
|
||||
F: security/keys/trusted-keys/trusted_caam.c
|
||||
|
||||
KEYS/KEYRINGS
|
||||
M: David Howells <dhowells@redhat.com>
|
||||
M: Jarkko Sakkinen <jarkko@kernel.org>
|
||||
|
1
certs/.gitignore
vendored
1
certs/.gitignore
vendored
@ -1,4 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
/blacklist_hashes_checked
|
||||
/extract-cert
|
||||
/x509_certificate_list
|
||||
/x509_revocation_list
|
||||
|
@ -104,8 +104,11 @@ config SYSTEM_BLACKLIST_HASH_LIST
|
||||
help
|
||||
If set, this option should be the filename of a list of hashes in the
|
||||
form "<hash>", "<hash>", ... . This will be included into a C
|
||||
wrapper to incorporate the list into the kernel. Each <hash> should
|
||||
be a string of hex digits.
|
||||
wrapper to incorporate the list into the kernel. Each <hash> must be a
|
||||
string starting with a prefix ("tbs" or "bin"), then a colon (":"), and
|
||||
finally an even number of hexadecimal lowercase characters (up to 128).
|
||||
Certificate hashes can be generated with
|
||||
tools/certs/print-cert-tbs-hash.sh .
|
||||
|
||||
config SYSTEM_REVOCATION_LIST
|
||||
bool "Provide system-wide ring of revocation certificates"
|
||||
@ -124,4 +127,14 @@ config SYSTEM_REVOCATION_KEYS
|
||||
containing X.509 certificates to be included in the default blacklist
|
||||
keyring.
|
||||
|
||||
config SYSTEM_BLACKLIST_AUTH_UPDATE
|
||||
bool "Allow root to add signed blacklist keys"
|
||||
depends on SYSTEM_BLACKLIST_KEYRING
|
||||
depends on SYSTEM_DATA_VERIFICATION
|
||||
help
|
||||
If set, provide the ability to load new blacklist keys at run time if
|
||||
they are signed and vouched by a certificate from the builtin trusted
|
||||
keyring. The PKCS#7 signature of the description is set in the key
|
||||
payload. Blacklist keys cannot be removed.
|
||||
|
||||
endmenu
|
||||
|
@ -7,6 +7,18 @@ obj-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += system_keyring.o system_certificates.o c
|
||||
obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist.o common.o
|
||||
obj-$(CONFIG_SYSTEM_REVOCATION_LIST) += revocation_certificates.o
|
||||
ifneq ($(CONFIG_SYSTEM_BLACKLIST_HASH_LIST),)
|
||||
quiet_cmd_check_blacklist_hashes = CHECK $(patsubst "%",%,$(2))
|
||||
cmd_check_blacklist_hashes = $(AWK) -f $(srctree)/scripts/check-blacklist-hashes.awk $(2); touch $@
|
||||
|
||||
$(eval $(call config_filename,SYSTEM_BLACKLIST_HASH_LIST))
|
||||
|
||||
$(obj)/blacklist_hashes.o: $(obj)/blacklist_hashes_checked
|
||||
|
||||
CFLAGS_blacklist_hashes.o += -I$(srctree)
|
||||
|
||||
targets += blacklist_hashes_checked
|
||||
$(obj)/blacklist_hashes_checked: $(SYSTEM_BLACKLIST_HASH_LIST_SRCPREFIX)$(SYSTEM_BLACKLIST_HASH_LIST_FILENAME) scripts/check-blacklist-hashes.awk FORCE
|
||||
$(call if_changed,check_blacklist_hashes,$(SYSTEM_BLACKLIST_HASH_LIST_SRCPREFIX)$(CONFIG_SYSTEM_BLACKLIST_HASH_LIST))
|
||||
obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist_hashes.o
|
||||
else
|
||||
obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist_nohashes.o
|
||||
@ -21,7 +33,7 @@ $(obj)/system_certificates.o: $(obj)/x509_certificate_list
|
||||
$(obj)/x509_certificate_list: $(CONFIG_SYSTEM_TRUSTED_KEYS) $(obj)/extract-cert FORCE
|
||||
$(call if_changed,extract_certs)
|
||||
|
||||
targets += x509_certificate_list
|
||||
targets += x509_certificate_list blacklist_hashes_checked
|
||||
|
||||
# If module signing is requested, say by allyesconfig, but a key has not been
|
||||
# supplied, then one will need to be generated to make sure the build does not
|
||||
|
@ -15,10 +15,24 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/uidgid.h>
|
||||
#include <linux/verification.h>
|
||||
#include <keys/system_keyring.h>
|
||||
#include "blacklist.h"
|
||||
#include "common.h"
|
||||
|
||||
/*
|
||||
* According to crypto/asymmetric_keys/x509_cert_parser.c:x509_note_pkey_algo(),
|
||||
* the size of the currently longest supported hash algorithm is 512 bits,
|
||||
* which translates into 128 hex characters.
|
||||
*/
|
||||
#define MAX_HASH_LEN 128
|
||||
|
||||
#define BLACKLIST_KEY_PERM (KEY_POS_SEARCH | KEY_POS_VIEW | \
|
||||
KEY_USR_SEARCH | KEY_USR_VIEW)
|
||||
|
||||
static const char tbs_prefix[] = "tbs";
|
||||
static const char bin_prefix[] = "bin";
|
||||
|
||||
static struct key *blacklist_keyring;
|
||||
|
||||
#ifdef CONFIG_SYSTEM_REVOCATION_LIST
|
||||
@ -32,41 +46,89 @@ extern __initconst const unsigned long revocation_certificate_list_size;
|
||||
*/
|
||||
static int blacklist_vet_description(const char *desc)
|
||||
{
|
||||
int n = 0;
|
||||
int i, prefix_len, tbs_step = 0, bin_step = 0;
|
||||
|
||||
if (*desc == ':')
|
||||
return -EINVAL;
|
||||
for (; *desc; desc++)
|
||||
if (*desc == ':')
|
||||
goto found_colon;
|
||||
/* The following algorithm only works if prefix lengths match. */
|
||||
BUILD_BUG_ON(sizeof(tbs_prefix) != sizeof(bin_prefix));
|
||||
prefix_len = sizeof(tbs_prefix) - 1;
|
||||
for (i = 0; *desc; desc++, i++) {
|
||||
if (*desc == ':') {
|
||||
if (tbs_step == prefix_len)
|
||||
goto found_colon;
|
||||
if (bin_step == prefix_len)
|
||||
goto found_colon;
|
||||
return -EINVAL;
|
||||
}
|
||||
if (i >= prefix_len)
|
||||
return -EINVAL;
|
||||
if (*desc == tbs_prefix[i])
|
||||
tbs_step++;
|
||||
if (*desc == bin_prefix[i])
|
||||
bin_step++;
|
||||
}
|
||||
return -EINVAL;
|
||||
|
||||
found_colon:
|
||||
desc++;
|
||||
for (; *desc; desc++) {
|
||||
for (i = 0; *desc && i < MAX_HASH_LEN; desc++, i++) {
|
||||
if (!isxdigit(*desc) || isupper(*desc))
|
||||
return -EINVAL;
|
||||
n++;
|
||||
}
|
||||
if (*desc)
|
||||
/* The hash is greater than MAX_HASH_LEN. */
|
||||
return -ENOPKG;
|
||||
|
||||
if (n == 0 || n & 1)
|
||||
/* Checks for an even number of hexadecimal characters. */
|
||||
if (i == 0 || i & 1)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The hash to be blacklisted is expected to be in the description. There will
|
||||
* be no payload.
|
||||
*/
|
||||
static int blacklist_preparse(struct key_preparsed_payload *prep)
|
||||
static int blacklist_key_instantiate(struct key *key,
|
||||
struct key_preparsed_payload *prep)
|
||||
{
|
||||
if (prep->datalen > 0)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
#ifdef CONFIG_SYSTEM_BLACKLIST_AUTH_UPDATE
|
||||
int err;
|
||||
#endif
|
||||
|
||||
/* Sets safe default permissions for keys loaded by user space. */
|
||||
key->perm = BLACKLIST_KEY_PERM;
|
||||
|
||||
/*
|
||||
* Skips the authentication step for builtin hashes, they are not
|
||||
* signed but still trusted.
|
||||
*/
|
||||
if (key->flags & (1 << KEY_FLAG_BUILTIN))
|
||||
goto out;
|
||||
|
||||
#ifdef CONFIG_SYSTEM_BLACKLIST_AUTH_UPDATE
|
||||
/*
|
||||
* Verifies the description's PKCS#7 signature against the builtin
|
||||
* trusted keyring.
|
||||
*/
|
||||
err = verify_pkcs7_signature(key->description,
|
||||
strlen(key->description), prep->data, prep->datalen,
|
||||
NULL, VERIFYING_UNSPECIFIED_SIGNATURE, NULL, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
#else
|
||||
/*
|
||||
* It should not be possible to come here because the keyring doesn't
|
||||
* have KEY_USR_WRITE and the only other way to call this function is
|
||||
* for builtin hashes.
|
||||
*/
|
||||
WARN_ON_ONCE(1);
|
||||
return -EPERM;
|
||||
#endif
|
||||
|
||||
out:
|
||||
return generic_key_instantiate(key, prep);
|
||||
}
|
||||
|
||||
static void blacklist_free_preparse(struct key_preparsed_payload *prep)
|
||||
static int blacklist_key_update(struct key *key,
|
||||
struct key_preparsed_payload *prep)
|
||||
{
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
static void blacklist_describe(const struct key *key, struct seq_file *m)
|
||||
@ -77,17 +139,48 @@ static void blacklist_describe(const struct key *key, struct seq_file *m)
|
||||
static struct key_type key_type_blacklist = {
|
||||
.name = "blacklist",
|
||||
.vet_description = blacklist_vet_description,
|
||||
.preparse = blacklist_preparse,
|
||||
.free_preparse = blacklist_free_preparse,
|
||||
.instantiate = generic_key_instantiate,
|
||||
.instantiate = blacklist_key_instantiate,
|
||||
.update = blacklist_key_update,
|
||||
.describe = blacklist_describe,
|
||||
};
|
||||
|
||||
static char *get_raw_hash(const u8 *hash, size_t hash_len,
|
||||
enum blacklist_hash_type hash_type)
|
||||
{
|
||||
size_t type_len;
|
||||
const char *type_prefix;
|
||||
char *buffer, *p;
|
||||
|
||||
switch (hash_type) {
|
||||
case BLACKLIST_HASH_X509_TBS:
|
||||
type_len = sizeof(tbs_prefix) - 1;
|
||||
type_prefix = tbs_prefix;
|
||||
break;
|
||||
case BLACKLIST_HASH_BINARY:
|
||||
type_len = sizeof(bin_prefix) - 1;
|
||||
type_prefix = bin_prefix;
|
||||
break;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
buffer = kmalloc(type_len + 1 + hash_len * 2 + 1, GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
p = memcpy(buffer, type_prefix, type_len);
|
||||
p += type_len;
|
||||
*p++ = ':';
|
||||
bin2hex(p, hash, hash_len);
|
||||
p += hash_len * 2;
|
||||
*p = '\0';
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* mark_hash_blacklisted - Add a hash to the system blacklist
|
||||
* mark_raw_hash_blacklisted - Add a hash to the system blacklist
|
||||
* @hash: The hash as a hex string with a type prefix (eg. "tbs:23aa429783")
|
||||
*/
|
||||
int mark_hash_blacklisted(const char *hash)
|
||||
static int mark_raw_hash_blacklisted(const char *hash)
|
||||
{
|
||||
key_ref_t key;
|
||||
|
||||
@ -96,8 +189,7 @@ int mark_hash_blacklisted(const char *hash)
|
||||
hash,
|
||||
NULL,
|
||||
0,
|
||||
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW),
|
||||
BLACKLIST_KEY_PERM,
|
||||
KEY_ALLOC_NOT_IN_QUOTA |
|
||||
KEY_ALLOC_BUILT_IN);
|
||||
if (IS_ERR(key)) {
|
||||
@ -107,29 +199,36 @@ int mark_hash_blacklisted(const char *hash)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mark_hash_blacklisted(const u8 *hash, size_t hash_len,
|
||||
enum blacklist_hash_type hash_type)
|
||||
{
|
||||
const char *buffer;
|
||||
int err;
|
||||
|
||||
buffer = get_raw_hash(hash, hash_len, hash_type);
|
||||
if (IS_ERR(buffer))
|
||||
return PTR_ERR(buffer);
|
||||
err = mark_raw_hash_blacklisted(buffer);
|
||||
kfree(buffer);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* is_hash_blacklisted - Determine if a hash is blacklisted
|
||||
* @hash: The hash to be checked as a binary blob
|
||||
* @hash_len: The length of the binary hash
|
||||
* @type: Type of hash
|
||||
* @hash_type: Type of hash
|
||||
*/
|
||||
int is_hash_blacklisted(const u8 *hash, size_t hash_len, const char *type)
|
||||
int is_hash_blacklisted(const u8 *hash, size_t hash_len,
|
||||
enum blacklist_hash_type hash_type)
|
||||
{
|
||||
key_ref_t kref;
|
||||
size_t type_len = strlen(type);
|
||||
char *buffer, *p;
|
||||
const char *buffer;
|
||||
int ret = 0;
|
||||
|
||||
buffer = kmalloc(type_len + 1 + hash_len * 2 + 1, GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
p = memcpy(buffer, type, type_len);
|
||||
p += type_len;
|
||||
*p++ = ':';
|
||||
bin2hex(p, hash, hash_len);
|
||||
p += hash_len * 2;
|
||||
*p = 0;
|
||||
|
||||
buffer = get_raw_hash(hash, hash_len, hash_type);
|
||||
if (IS_ERR(buffer))
|
||||
return PTR_ERR(buffer);
|
||||
kref = keyring_search(make_key_ref(blacklist_keyring, true),
|
||||
&key_type_blacklist, buffer, false);
|
||||
if (!IS_ERR(kref)) {
|
||||
@ -144,7 +243,8 @@ EXPORT_SYMBOL_GPL(is_hash_blacklisted);
|
||||
|
||||
int is_binary_blacklisted(const u8 *hash, size_t hash_len)
|
||||
{
|
||||
if (is_hash_blacklisted(hash, hash_len, "bin") == -EKEYREJECTED)
|
||||
if (is_hash_blacklisted(hash, hash_len, BLACKLIST_HASH_BINARY) ==
|
||||
-EKEYREJECTED)
|
||||
return -EPERM;
|
||||
|
||||
return 0;
|
||||
@ -166,8 +266,10 @@ int add_key_to_revocation_list(const char *data, size_t size)
|
||||
NULL,
|
||||
data,
|
||||
size,
|
||||
((KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW),
|
||||
KEY_ALLOC_NOT_IN_QUOTA | KEY_ALLOC_BUILT_IN);
|
||||
KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH
|
||||
| KEY_USR_VIEW,
|
||||
KEY_ALLOC_NOT_IN_QUOTA | KEY_ALLOC_BUILT_IN
|
||||
| KEY_ALLOC_BYPASS_RESTRICTION);
|
||||
|
||||
if (IS_ERR(key)) {
|
||||
pr_err("Problem with revocation key (%ld)\n", PTR_ERR(key));
|
||||
@ -194,30 +296,57 @@ int is_key_on_revocation_list(struct pkcs7_message *pkcs7)
|
||||
}
|
||||
#endif
|
||||
|
||||
static int restrict_link_for_blacklist(struct key *dest_keyring,
|
||||
const struct key_type *type, const union key_payload *payload,
|
||||
struct key *restrict_key)
|
||||
{
|
||||
if (type == &key_type_blacklist)
|
||||
return 0;
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialise the blacklist
|
||||
*
|
||||
* The blacklist_init() function is registered as an initcall via
|
||||
* device_initcall(). As a result if the blacklist_init() function fails for
|
||||
* any reason the kernel continues to execute. While cleanly returning -ENODEV
|
||||
* could be acceptable for some non-critical kernel parts, if the blacklist
|
||||
* keyring fails to load it defeats the certificate/key based deny list for
|
||||
* signed modules. If a critical piece of security functionality that users
|
||||
* expect to be present fails to initialize, panic()ing is likely the right
|
||||
* thing to do.
|
||||
*/
|
||||
static int __init blacklist_init(void)
|
||||
{
|
||||
const char *const *bl;
|
||||
struct key_restriction *restriction;
|
||||
|
||||
if (register_key_type(&key_type_blacklist) < 0)
|
||||
panic("Can't allocate system blacklist key type\n");
|
||||
|
||||
restriction = kzalloc(sizeof(*restriction), GFP_KERNEL);
|
||||
if (!restriction)
|
||||
panic("Can't allocate blacklist keyring restriction\n");
|
||||
restriction->check = restrict_link_for_blacklist;
|
||||
|
||||
blacklist_keyring =
|
||||
keyring_alloc(".blacklist",
|
||||
GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, current_cred(),
|
||||
(KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW | KEY_USR_READ |
|
||||
KEY_USR_SEARCH,
|
||||
KEY_ALLOC_NOT_IN_QUOTA |
|
||||
KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH |
|
||||
KEY_POS_WRITE |
|
||||
KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH
|
||||
#ifdef CONFIG_SYSTEM_BLACKLIST_AUTH_UPDATE
|
||||
| KEY_USR_WRITE
|
||||
#endif
|
||||
, KEY_ALLOC_NOT_IN_QUOTA |
|
||||
KEY_ALLOC_SET_KEEP,
|
||||
NULL, NULL);
|
||||
restriction, NULL);
|
||||
if (IS_ERR(blacklist_keyring))
|
||||
panic("Can't allocate system blacklist keyring\n");
|
||||
|
||||
for (bl = blacklist_hashes; *bl; bl++)
|
||||
if (mark_hash_blacklisted(*bl) < 0)
|
||||
if (mark_raw_hash_blacklisted(*bl) < 0)
|
||||
pr_err("- blacklisting failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
@ -69,7 +69,8 @@ int x509_get_sig_params(struct x509_certificate *cert)
|
||||
if (ret < 0)
|
||||
goto error_2;
|
||||
|
||||
ret = is_hash_blacklisted(sig->digest, sig->digest_size, "tbs");
|
||||
ret = is_hash_blacklisted(sig->digest, sig->digest_size,
|
||||
BLACKLIST_HASH_X509_TBS);
|
||||
if (ret == -EKEYREJECTED) {
|
||||
pr_err("Cert %*phN is blacklisted\n",
|
||||
sig->digest_size, sig->digest);
|
||||
|
@ -400,7 +400,16 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value,
|
||||
if (!rc) {
|
||||
out = (struct tpm2_get_cap_out *)
|
||||
&buf.data[TPM_HEADER_SIZE];
|
||||
*value = be32_to_cpu(out->value);
|
||||
/*
|
||||
* To prevent failing boot up of some systems, Infineon TPM2.0
|
||||
* returns SUCCESS on TPM2_Startup in field upgrade mode. Also
|
||||
* the TPM2_Getcapability command returns a zero length list
|
||||
* in field upgrade mode.
|
||||
*/
|
||||
if (be32_to_cpu(out->property_cnt) > 0)
|
||||
*value = be32_to_cpu(out->value);
|
||||
else
|
||||
rc = -ENODATA;
|
||||
}
|
||||
tpm_buf_destroy(&buf);
|
||||
return rc;
|
||||
@ -745,7 +754,11 @@ int tpm2_auto_startup(struct tpm_chip *chip)
|
||||
rc = tpm2_get_cc_attrs_tbl(chip);
|
||||
|
||||
out:
|
||||
if (rc == TPM2_RC_UPGRADE) {
|
||||
/*
|
||||
* Infineon TPM in field upgrade mode will return no data for the number
|
||||
* of supported commands.
|
||||
*/
|
||||
if (rc == TPM2_RC_UPGRADE || rc == -ENODATA) {
|
||||
dev_info(&chip->dev, "TPM in field upgrade mode, requires firmware upgrade\n");
|
||||
chip->flags |= TPM_CHIP_FLAG_FIRMWARE_UPGRADE;
|
||||
rc = 0;
|
||||
|
@ -177,7 +177,7 @@ static u8 ftpm_tee_tpm_op_status(struct tpm_chip *chip)
|
||||
|
||||
static bool ftpm_tee_tpm_req_canceled(struct tpm_chip *chip, u8 status)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
static const struct tpm_class_ops ftpm_tee_tpm_ops = {
|
||||
|
@ -681,6 +681,7 @@ static int tpm_ibmvtpm_probe(struct vio_dev *vio_dev,
|
||||
if (!wait_event_timeout(ibmvtpm->crq_queue.wq,
|
||||
ibmvtpm->rtce_buf != NULL,
|
||||
HZ)) {
|
||||
rc = -ENODEV;
|
||||
dev_err(dev, "CRQ response timed out\n");
|
||||
goto init_irq_cleanup;
|
||||
}
|
||||
|
@ -153,50 +153,46 @@ static int check_acpi_tpm2(struct device *dev)
|
||||
#endif
|
||||
|
||||
static int tpm_tcg_read_bytes(struct tpm_tis_data *data, u32 addr, u16 len,
|
||||
u8 *result)
|
||||
u8 *result, enum tpm_tis_io_mode io_mode)
|
||||
{
|
||||
struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
|
||||
__le16 result_le16;
|
||||
__le32 result_le32;
|
||||
|
||||
while (len--)
|
||||
*result++ = ioread8(phy->iobase + addr);
|
||||
switch (io_mode) {
|
||||
case TPM_TIS_PHYS_8:
|
||||
while (len--)
|
||||
*result++ = ioread8(phy->iobase + addr);
|
||||
break;
|
||||
case TPM_TIS_PHYS_16:
|
||||
result_le16 = cpu_to_le16(ioread16(phy->iobase + addr));
|
||||
memcpy(result, &result_le16, sizeof(u16));
|
||||
break;
|
||||
case TPM_TIS_PHYS_32:
|
||||
result_le32 = cpu_to_le32(ioread32(phy->iobase + addr));
|
||||
memcpy(result, &result_le32, sizeof(u32));
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpm_tcg_write_bytes(struct tpm_tis_data *data, u32 addr, u16 len,
|
||||
const u8 *value)
|
||||
const u8 *value, enum tpm_tis_io_mode io_mode)
|
||||
{
|
||||
struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
|
||||
|
||||
while (len--)
|
||||
iowrite8(*value++, phy->iobase + addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpm_tcg_read16(struct tpm_tis_data *data, u32 addr, u16 *result)
|
||||
{
|
||||
struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
|
||||
|
||||
*result = ioread16(phy->iobase + addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpm_tcg_read32(struct tpm_tis_data *data, u32 addr, u32 *result)
|
||||
{
|
||||
struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
|
||||
|
||||
*result = ioread32(phy->iobase + addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpm_tcg_write32(struct tpm_tis_data *data, u32 addr, u32 value)
|
||||
{
|
||||
struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
|
||||
|
||||
iowrite32(value, phy->iobase + addr);
|
||||
switch (io_mode) {
|
||||
case TPM_TIS_PHYS_8:
|
||||
while (len--)
|
||||
iowrite8(*value++, phy->iobase + addr);
|
||||
break;
|
||||
case TPM_TIS_PHYS_16:
|
||||
return -EINVAL;
|
||||
case TPM_TIS_PHYS_32:
|
||||
iowrite32(le32_to_cpu(*((__le32 *)value)), phy->iobase + addr);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -204,9 +200,6 @@ static int tpm_tcg_write32(struct tpm_tis_data *data, u32 addr, u32 value)
|
||||
static const struct tpm_tis_phy_ops tpm_tcg = {
|
||||
.read_bytes = tpm_tcg_read_bytes,
|
||||
.write_bytes = tpm_tcg_write_bytes,
|
||||
.read16 = tpm_tcg_read16,
|
||||
.read32 = tpm_tcg_read32,
|
||||
.write32 = tpm_tcg_write32,
|
||||
};
|
||||
|
||||
static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info)
|
||||
|
@ -104,54 +104,88 @@ struct tpm_tis_data {
|
||||
unsigned int timeout_max; /* usecs */
|
||||
};
|
||||
|
||||
/*
|
||||
* IO modes to indicate how many bytes should be read/written at once in the
|
||||
* tpm_tis_phy_ops read_bytes/write_bytes calls. Use TPM_TIS_PHYS_8 to
|
||||
* receive/transmit byte-wise, TPM_TIS_PHYS_16 for two bytes etc.
|
||||
*/
|
||||
enum tpm_tis_io_mode {
|
||||
TPM_TIS_PHYS_8,
|
||||
TPM_TIS_PHYS_16,
|
||||
TPM_TIS_PHYS_32,
|
||||
};
|
||||
|
||||
struct tpm_tis_phy_ops {
|
||||
/* data is passed in little endian */
|
||||
int (*read_bytes)(struct tpm_tis_data *data, u32 addr, u16 len,
|
||||
u8 *result);
|
||||
u8 *result, enum tpm_tis_io_mode mode);
|
||||
int (*write_bytes)(struct tpm_tis_data *data, u32 addr, u16 len,
|
||||
const u8 *value);
|
||||
int (*read16)(struct tpm_tis_data *data, u32 addr, u16 *result);
|
||||
int (*read32)(struct tpm_tis_data *data, u32 addr, u32 *result);
|
||||
int (*write32)(struct tpm_tis_data *data, u32 addr, u32 src);
|
||||
const u8 *value, enum tpm_tis_io_mode mode);
|
||||
};
|
||||
|
||||
static inline int tpm_tis_read_bytes(struct tpm_tis_data *data, u32 addr,
|
||||
u16 len, u8 *result)
|
||||
{
|
||||
return data->phy_ops->read_bytes(data, addr, len, result);
|
||||
return data->phy_ops->read_bytes(data, addr, len, result,
|
||||
TPM_TIS_PHYS_8);
|
||||
}
|
||||
|
||||
static inline int tpm_tis_read8(struct tpm_tis_data *data, u32 addr, u8 *result)
|
||||
{
|
||||
return data->phy_ops->read_bytes(data, addr, 1, result);
|
||||
return data->phy_ops->read_bytes(data, addr, 1, result, TPM_TIS_PHYS_8);
|
||||
}
|
||||
|
||||
static inline int tpm_tis_read16(struct tpm_tis_data *data, u32 addr,
|
||||
u16 *result)
|
||||
{
|
||||
return data->phy_ops->read16(data, addr, result);
|
||||
__le16 result_le;
|
||||
int rc;
|
||||
|
||||
rc = data->phy_ops->read_bytes(data, addr, sizeof(u16),
|
||||
(u8 *)&result_le, TPM_TIS_PHYS_16);
|
||||
if (!rc)
|
||||
*result = le16_to_cpu(result_le);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline int tpm_tis_read32(struct tpm_tis_data *data, u32 addr,
|
||||
u32 *result)
|
||||
{
|
||||
return data->phy_ops->read32(data, addr, result);
|
||||
__le32 result_le;
|
||||
int rc;
|
||||
|
||||
rc = data->phy_ops->read_bytes(data, addr, sizeof(u32),
|
||||
(u8 *)&result_le, TPM_TIS_PHYS_32);
|
||||
if (!rc)
|
||||
*result = le32_to_cpu(result_le);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline int tpm_tis_write_bytes(struct tpm_tis_data *data, u32 addr,
|
||||
u16 len, const u8 *value)
|
||||
{
|
||||
return data->phy_ops->write_bytes(data, addr, len, value);
|
||||
return data->phy_ops->write_bytes(data, addr, len, value,
|
||||
TPM_TIS_PHYS_8);
|
||||
}
|
||||
|
||||
static inline int tpm_tis_write8(struct tpm_tis_data *data, u32 addr, u8 value)
|
||||
{
|
||||
return data->phy_ops->write_bytes(data, addr, 1, &value);
|
||||
return data->phy_ops->write_bytes(data, addr, 1, &value,
|
||||
TPM_TIS_PHYS_8);
|
||||
}
|
||||
|
||||
static inline int tpm_tis_write32(struct tpm_tis_data *data, u32 addr,
|
||||
u32 value)
|
||||
{
|
||||
return data->phy_ops->write32(data, addr, value);
|
||||
__le32 value_le;
|
||||
int rc;
|
||||
|
||||
value_le = cpu_to_le32(value);
|
||||
rc = data->phy_ops->write_bytes(data, addr, sizeof(u32),
|
||||
(u8 *)&value_le, TPM_TIS_PHYS_32);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline bool is_bsw(void)
|
||||
|
@ -31,6 +31,7 @@
|
||||
#define TPM_CR50_TIMEOUT_SHORT_MS 2 /* Short timeout during transactions */
|
||||
#define TPM_CR50_TIMEOUT_NOIRQ_MS 20 /* Timeout for TPM ready without IRQ */
|
||||
#define TPM_CR50_I2C_DID_VID 0x00281ae0L /* Device and vendor ID reg value */
|
||||
#define TPM_TI50_I2C_DID_VID 0x504a6666L /* Device and vendor ID reg value */
|
||||
#define TPM_CR50_I2C_MAX_RETRIES 3 /* Max retries due to I2C errors */
|
||||
#define TPM_CR50_I2C_RETRY_DELAY_LO 55 /* Min usecs between retries on I2C */
|
||||
#define TPM_CR50_I2C_RETRY_DELAY_HI 65 /* Max usecs between retries on I2C */
|
||||
@ -742,15 +743,15 @@ static int tpm_cr50_i2c_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
vendor = le32_to_cpup((__le32 *)buf);
|
||||
if (vendor != TPM_CR50_I2C_DID_VID) {
|
||||
if (vendor != TPM_CR50_I2C_DID_VID && vendor != TPM_TI50_I2C_DID_VID) {
|
||||
dev_err(dev, "Vendor ID did not match! ID was %08x\n", vendor);
|
||||
tpm_cr50_release_locality(chip, true);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dev_info(dev, "cr50 TPM 2.0 (i2c 0x%02x irq %d id 0x%x)\n",
|
||||
dev_info(dev, "%s TPM 2.0 (i2c 0x%02x irq %d id 0x%x)\n",
|
||||
vendor == TPM_TI50_I2C_DID_VID ? "ti50" : "cr50",
|
||||
client->addr, client->irq, vendor >> 16);
|
||||
|
||||
return tpm_chip_register(chip);
|
||||
}
|
||||
|
||||
@ -768,8 +769,8 @@ static int tpm_cr50_i2c_remove(struct i2c_client *client)
|
||||
struct device *dev = &client->dev;
|
||||
|
||||
if (!chip) {
|
||||
dev_err(dev, "Could not get client data at remove\n");
|
||||
return -ENODEV;
|
||||
dev_crit(dev, "Could not get client data at remove, memory corruption ahead\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
tpm_chip_unregister(chip);
|
||||
|
@ -31,10 +31,6 @@ extern int tpm_tis_spi_init(struct spi_device *spi, struct tpm_tis_spi_phy *phy,
|
||||
extern int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len,
|
||||
u8 *in, const u8 *out);
|
||||
|
||||
extern int tpm_tis_spi_read16(struct tpm_tis_data *data, u32 addr, u16 *result);
|
||||
extern int tpm_tis_spi_read32(struct tpm_tis_data *data, u32 addr, u32 *result);
|
||||
extern int tpm_tis_spi_write32(struct tpm_tis_data *data, u32 addr, u32 value);
|
||||
|
||||
#ifdef CONFIG_TCG_TIS_SPI_CR50
|
||||
extern int cr50_spi_probe(struct spi_device *spi);
|
||||
#else
|
||||
|
@ -222,13 +222,13 @@ static int tpm_tis_spi_cr50_transfer(struct tpm_tis_data *data, u32 addr, u16 le
|
||||
}
|
||||
|
||||
static int tpm_tis_spi_cr50_read_bytes(struct tpm_tis_data *data, u32 addr,
|
||||
u16 len, u8 *result)
|
||||
u16 len, u8 *result, enum tpm_tis_io_mode io_mode)
|
||||
{
|
||||
return tpm_tis_spi_cr50_transfer(data, addr, len, result, NULL);
|
||||
}
|
||||
|
||||
static int tpm_tis_spi_cr50_write_bytes(struct tpm_tis_data *data, u32 addr,
|
||||
u16 len, const u8 *value)
|
||||
u16 len, const u8 *value, enum tpm_tis_io_mode io_mode)
|
||||
{
|
||||
return tpm_tis_spi_cr50_transfer(data, addr, len, NULL, value);
|
||||
}
|
||||
@ -236,9 +236,6 @@ static int tpm_tis_spi_cr50_write_bytes(struct tpm_tis_data *data, u32 addr,
|
||||
static const struct tpm_tis_phy_ops tpm_spi_cr50_phy_ops = {
|
||||
.read_bytes = tpm_tis_spi_cr50_read_bytes,
|
||||
.write_bytes = tpm_tis_spi_cr50_write_bytes,
|
||||
.read16 = tpm_tis_spi_read16,
|
||||
.read32 = tpm_tis_spi_read32,
|
||||
.write32 = tpm_tis_spi_write32,
|
||||
};
|
||||
|
||||
static void cr50_print_fw_version(struct tpm_tis_data *data)
|
||||
|
@ -141,55 +141,17 @@ exit:
|
||||
}
|
||||
|
||||
static int tpm_tis_spi_read_bytes(struct tpm_tis_data *data, u32 addr,
|
||||
u16 len, u8 *result)
|
||||
u16 len, u8 *result, enum tpm_tis_io_mode io_mode)
|
||||
{
|
||||
return tpm_tis_spi_transfer(data, addr, len, result, NULL);
|
||||
}
|
||||
|
||||
static int tpm_tis_spi_write_bytes(struct tpm_tis_data *data, u32 addr,
|
||||
u16 len, const u8 *value)
|
||||
u16 len, const u8 *value, enum tpm_tis_io_mode io_mode)
|
||||
{
|
||||
return tpm_tis_spi_transfer(data, addr, len, NULL, value);
|
||||
}
|
||||
|
||||
int tpm_tis_spi_read16(struct tpm_tis_data *data, u32 addr, u16 *result)
|
||||
{
|
||||
__le16 result_le;
|
||||
int rc;
|
||||
|
||||
rc = data->phy_ops->read_bytes(data, addr, sizeof(u16),
|
||||
(u8 *)&result_le);
|
||||
if (!rc)
|
||||
*result = le16_to_cpu(result_le);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int tpm_tis_spi_read32(struct tpm_tis_data *data, u32 addr, u32 *result)
|
||||
{
|
||||
__le32 result_le;
|
||||
int rc;
|
||||
|
||||
rc = data->phy_ops->read_bytes(data, addr, sizeof(u32),
|
||||
(u8 *)&result_le);
|
||||
if (!rc)
|
||||
*result = le32_to_cpu(result_le);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int tpm_tis_spi_write32(struct tpm_tis_data *data, u32 addr, u32 value)
|
||||
{
|
||||
__le32 value_le;
|
||||
int rc;
|
||||
|
||||
value_le = cpu_to_le32(value);
|
||||
rc = data->phy_ops->write_bytes(data, addr, sizeof(u32),
|
||||
(u8 *)&value_le);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int tpm_tis_spi_init(struct spi_device *spi, struct tpm_tis_spi_phy *phy,
|
||||
int irq, const struct tpm_tis_phy_ops *phy_ops)
|
||||
{
|
||||
@ -205,9 +167,6 @@ int tpm_tis_spi_init(struct spi_device *spi, struct tpm_tis_spi_phy *phy,
|
||||
static const struct tpm_tis_phy_ops tpm_spi_phy_ops = {
|
||||
.read_bytes = tpm_tis_spi_read_bytes,
|
||||
.write_bytes = tpm_tis_spi_write_bytes,
|
||||
.read16 = tpm_tis_spi_read16,
|
||||
.read32 = tpm_tis_spi_read32,
|
||||
.write32 = tpm_tis_spi_write32,
|
||||
};
|
||||
|
||||
static int tpm_tis_spi_probe(struct spi_device *dev)
|
||||
|
@ -35,72 +35,53 @@ static inline struct tpm_tis_synquacer_phy *to_tpm_tis_tcg_phy(struct tpm_tis_da
|
||||
}
|
||||
|
||||
static int tpm_tis_synquacer_read_bytes(struct tpm_tis_data *data, u32 addr,
|
||||
u16 len, u8 *result)
|
||||
u16 len, u8 *result,
|
||||
enum tpm_tis_io_mode io_mode)
|
||||
{
|
||||
struct tpm_tis_synquacer_phy *phy = to_tpm_tis_tcg_phy(data);
|
||||
|
||||
while (len--)
|
||||
*result++ = ioread8(phy->iobase + addr);
|
||||
switch (io_mode) {
|
||||
case TPM_TIS_PHYS_8:
|
||||
while (len--)
|
||||
*result++ = ioread8(phy->iobase + addr);
|
||||
break;
|
||||
case TPM_TIS_PHYS_16:
|
||||
result[1] = ioread8(phy->iobase + addr + 1);
|
||||
result[0] = ioread8(phy->iobase + addr);
|
||||
break;
|
||||
case TPM_TIS_PHYS_32:
|
||||
result[3] = ioread8(phy->iobase + addr + 3);
|
||||
result[2] = ioread8(phy->iobase + addr + 2);
|
||||
result[1] = ioread8(phy->iobase + addr + 1);
|
||||
result[0] = ioread8(phy->iobase + addr);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpm_tis_synquacer_write_bytes(struct tpm_tis_data *data, u32 addr,
|
||||
u16 len, const u8 *value)
|
||||
u16 len, const u8 *value,
|
||||
enum tpm_tis_io_mode io_mode)
|
||||
{
|
||||
struct tpm_tis_synquacer_phy *phy = to_tpm_tis_tcg_phy(data);
|
||||
|
||||
while (len--)
|
||||
iowrite8(*value++, phy->iobase + addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpm_tis_synquacer_read16_bw(struct tpm_tis_data *data,
|
||||
u32 addr, u16 *result)
|
||||
{
|
||||
struct tpm_tis_synquacer_phy *phy = to_tpm_tis_tcg_phy(data);
|
||||
|
||||
/*
|
||||
* Due to the limitation of SPI controller on SynQuacer,
|
||||
* 16/32 bits access must be done in byte-wise and descending order.
|
||||
*/
|
||||
*result = (ioread8(phy->iobase + addr + 1) << 8) |
|
||||
(ioread8(phy->iobase + addr));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpm_tis_synquacer_read32_bw(struct tpm_tis_data *data,
|
||||
u32 addr, u32 *result)
|
||||
{
|
||||
struct tpm_tis_synquacer_phy *phy = to_tpm_tis_tcg_phy(data);
|
||||
|
||||
/*
|
||||
* Due to the limitation of SPI controller on SynQuacer,
|
||||
* 16/32 bits access must be done in byte-wise and descending order.
|
||||
*/
|
||||
*result = (ioread8(phy->iobase + addr + 3) << 24) |
|
||||
(ioread8(phy->iobase + addr + 2) << 16) |
|
||||
(ioread8(phy->iobase + addr + 1) << 8) |
|
||||
(ioread8(phy->iobase + addr));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpm_tis_synquacer_write32_bw(struct tpm_tis_data *data,
|
||||
u32 addr, u32 value)
|
||||
{
|
||||
struct tpm_tis_synquacer_phy *phy = to_tpm_tis_tcg_phy(data);
|
||||
|
||||
/*
|
||||
* Due to the limitation of SPI controller on SynQuacer,
|
||||
* 16/32 bits access must be done in byte-wise and descending order.
|
||||
*/
|
||||
iowrite8(value >> 24, phy->iobase + addr + 3);
|
||||
iowrite8(value >> 16, phy->iobase + addr + 2);
|
||||
iowrite8(value >> 8, phy->iobase + addr + 1);
|
||||
iowrite8(value, phy->iobase + addr);
|
||||
switch (io_mode) {
|
||||
case TPM_TIS_PHYS_8:
|
||||
while (len--)
|
||||
iowrite8(*value++, phy->iobase + addr);
|
||||
break;
|
||||
case TPM_TIS_PHYS_16:
|
||||
return -EINVAL;
|
||||
case TPM_TIS_PHYS_32:
|
||||
/*
|
||||
* Due to the limitation of SPI controller on SynQuacer,
|
||||
* 16/32 bits access must be done in byte-wise and descending order.
|
||||
*/
|
||||
iowrite8(value[3], phy->iobase + addr + 3);
|
||||
iowrite8(value[2], phy->iobase + addr + 2);
|
||||
iowrite8(value[1], phy->iobase + addr + 1);
|
||||
iowrite8(value[0], phy->iobase + addr);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -108,9 +89,6 @@ static int tpm_tis_synquacer_write32_bw(struct tpm_tis_data *data,
|
||||
static const struct tpm_tis_phy_ops tpm_tcg_bw = {
|
||||
.read_bytes = tpm_tis_synquacer_read_bytes,
|
||||
.write_bytes = tpm_tis_synquacer_write_bytes,
|
||||
.read16 = tpm_tis_synquacer_read16_bw,
|
||||
.read32 = tpm_tis_synquacer_read32_bw,
|
||||
.write32 = tpm_tis_synquacer_write32_bw,
|
||||
};
|
||||
|
||||
static int tpm_tis_synquacer_init(struct device *dev,
|
||||
|
@ -151,6 +151,9 @@ config CRYPTO_DEV_FSL_CAAM_RNG_API
|
||||
Selecting this will register the SEC4 hardware rng to
|
||||
the hw_random API for supplying the kernel entropy pool.
|
||||
|
||||
config CRYPTO_DEV_FSL_CAAM_BLOB_GEN
|
||||
bool
|
||||
|
||||
endif # CRYPTO_DEV_FSL_CAAM_JR
|
||||
|
||||
endif # CRYPTO_DEV_FSL_CAAM
|
||||
|
@ -21,6 +21,7 @@ caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_QI) += caamalg_qi.o
|
||||
caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_AHASH_API) += caamhash.o
|
||||
caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_API) += caamrng.o
|
||||
caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_PKC_API) += caampkc.o pkc_desc.o
|
||||
caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_BLOB_GEN) += blob_gen.o
|
||||
|
||||
caam-$(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_QI) += qi.o
|
||||
ifneq ($(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_QI),)
|
||||
|
182
drivers/crypto/caam/blob_gen.c
Normal file
182
drivers/crypto/caam/blob_gen.c
Normal file
@ -0,0 +1,182 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2015 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de>
|
||||
* Copyright (C) 2021 Pengutronix, Ahmad Fatoum <kernel@pengutronix.de>
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "caam blob_gen: " fmt
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <soc/fsl/caam-blob.h>
|
||||
|
||||
#include "compat.h"
|
||||
#include "desc_constr.h"
|
||||
#include "desc.h"
|
||||
#include "error.h"
|
||||
#include "intern.h"
|
||||
#include "jr.h"
|
||||
#include "regs.h"
|
||||
|
||||
#define CAAM_BLOB_DESC_BYTES_MAX \
|
||||
/* Command to initialize & stating length of descriptor */ \
|
||||
(CAAM_CMD_SZ + \
|
||||
/* Command to append the key-modifier + key-modifier data */ \
|
||||
CAAM_CMD_SZ + CAAM_BLOB_KEYMOD_LENGTH + \
|
||||
/* Command to include input key + pointer to the input key */ \
|
||||
CAAM_CMD_SZ + CAAM_PTR_SZ_MAX + \
|
||||
/* Command to include output key + pointer to the output key */ \
|
||||
CAAM_CMD_SZ + CAAM_PTR_SZ_MAX + \
|
||||
/* Command describing the operation to perform */ \
|
||||
CAAM_CMD_SZ)
|
||||
|
||||
struct caam_blob_priv {
|
||||
struct device jrdev;
|
||||
};
|
||||
|
||||
struct caam_blob_job_result {
|
||||
int err;
|
||||
struct completion completion;
|
||||
};
|
||||
|
||||
static void caam_blob_job_done(struct device *dev, u32 *desc, u32 err, void *context)
|
||||
{
|
||||
struct caam_blob_job_result *res = context;
|
||||
int ecode = 0;
|
||||
|
||||
dev_dbg(dev, "%s %d: err 0x%x\n", __func__, __LINE__, err);
|
||||
|
||||
if (err)
|
||||
ecode = caam_jr_strstatus(dev, err);
|
||||
|
||||
res->err = ecode;
|
||||
|
||||
/*
|
||||
* Upon completion, desc points to a buffer containing a CAAM job
|
||||
* descriptor which encapsulates data into an externally-storable
|
||||
* blob.
|
||||
*/
|
||||
complete(&res->completion);
|
||||
}
|
||||
|
||||
int caam_process_blob(struct caam_blob_priv *priv,
|
||||
struct caam_blob_info *info, bool encap)
|
||||
{
|
||||
struct caam_blob_job_result testres;
|
||||
struct device *jrdev = &priv->jrdev;
|
||||
dma_addr_t dma_in, dma_out;
|
||||
int op = OP_PCLID_BLOB;
|
||||
size_t output_len;
|
||||
u32 *desc;
|
||||
int ret;
|
||||
|
||||
if (info->key_mod_len > CAAM_BLOB_KEYMOD_LENGTH)
|
||||
return -EINVAL;
|
||||
|
||||
if (encap) {
|
||||
op |= OP_TYPE_ENCAP_PROTOCOL;
|
||||
output_len = info->input_len + CAAM_BLOB_OVERHEAD;
|
||||
} else {
|
||||
op |= OP_TYPE_DECAP_PROTOCOL;
|
||||
output_len = info->input_len - CAAM_BLOB_OVERHEAD;
|
||||
}
|
||||
|
||||
desc = kzalloc(CAAM_BLOB_DESC_BYTES_MAX, GFP_KERNEL | GFP_DMA);
|
||||
if (!desc)
|
||||
return -ENOMEM;
|
||||
|
||||
dma_in = dma_map_single(jrdev, info->input, info->input_len,
|
||||
DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(jrdev, dma_in)) {
|
||||
dev_err(jrdev, "unable to map input DMA buffer\n");
|
||||
ret = -ENOMEM;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
dma_out = dma_map_single(jrdev, info->output, output_len,
|
||||
DMA_FROM_DEVICE);
|
||||
if (dma_mapping_error(jrdev, dma_out)) {
|
||||
dev_err(jrdev, "unable to map output DMA buffer\n");
|
||||
ret = -ENOMEM;
|
||||
goto out_unmap_in;
|
||||
}
|
||||
|
||||
/*
|
||||
* A data blob is encrypted using a blob key (BK); a random number.
|
||||
* The BK is used as an AES-CCM key. The initial block (B0) and the
|
||||
* initial counter (Ctr0) are generated automatically and stored in
|
||||
* Class 1 Context DWords 0+1+2+3. The random BK is stored in the
|
||||
* Class 1 Key Register. Operation Mode is set to AES-CCM.
|
||||
*/
|
||||
|
||||
init_job_desc(desc, 0);
|
||||
append_key_as_imm(desc, info->key_mod, info->key_mod_len,
|
||||
info->key_mod_len, CLASS_2 | KEY_DEST_CLASS_REG);
|
||||
append_seq_in_ptr_intlen(desc, dma_in, info->input_len, 0);
|
||||
append_seq_out_ptr_intlen(desc, dma_out, output_len, 0);
|
||||
append_operation(desc, op);
|
||||
|
||||
print_hex_dump_debug("data@"__stringify(__LINE__)": ",
|
||||
DUMP_PREFIX_ADDRESS, 16, 1, info->input,
|
||||
info->input_len, false);
|
||||
print_hex_dump_debug("jobdesc@"__stringify(__LINE__)": ",
|
||||
DUMP_PREFIX_ADDRESS, 16, 1, desc,
|
||||
desc_bytes(desc), false);
|
||||
|
||||
testres.err = 0;
|
||||
init_completion(&testres.completion);
|
||||
|
||||
ret = caam_jr_enqueue(jrdev, desc, caam_blob_job_done, &testres);
|
||||
if (ret == -EINPROGRESS) {
|
||||
wait_for_completion(&testres.completion);
|
||||
ret = testres.err;
|
||||
print_hex_dump_debug("output@"__stringify(__LINE__)": ",
|
||||
DUMP_PREFIX_ADDRESS, 16, 1, info->output,
|
||||
output_len, false);
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
info->output_len = output_len;
|
||||
|
||||
dma_unmap_single(jrdev, dma_out, output_len, DMA_FROM_DEVICE);
|
||||
out_unmap_in:
|
||||
dma_unmap_single(jrdev, dma_in, info->input_len, DMA_TO_DEVICE);
|
||||
out_free:
|
||||
kfree(desc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(caam_process_blob);
|
||||
|
||||
struct caam_blob_priv *caam_blob_gen_init(void)
|
||||
{
|
||||
struct caam_drv_private *ctrlpriv;
|
||||
struct device *jrdev;
|
||||
|
||||
/*
|
||||
* caam_blob_gen_init() may expectedly fail with -ENODEV, e.g. when
|
||||
* CAAM driver didn't probe or when SoC lacks BLOB support. An
|
||||
* error would be harsh in this case, so we stick to info level.
|
||||
*/
|
||||
|
||||
jrdev = caam_jr_alloc();
|
||||
if (IS_ERR(jrdev)) {
|
||||
pr_info("job ring requested, but none currently available\n");
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
ctrlpriv = dev_get_drvdata(jrdev->parent);
|
||||
if (!ctrlpriv->blob_present) {
|
||||
dev_info(jrdev, "no hardware blob generation support\n");
|
||||
caam_jr_free(jrdev);
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
return container_of(jrdev, struct caam_blob_priv, jrdev);
|
||||
}
|
||||
EXPORT_SYMBOL(caam_blob_gen_init);
|
||||
|
||||
void caam_blob_gen_exit(struct caam_blob_priv *priv)
|
||||
{
|
||||
caam_jr_free(&priv->jrdev);
|
||||
}
|
||||
EXPORT_SYMBOL(caam_blob_gen_exit);
|
@ -820,12 +820,25 @@ static int caam_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (ctrlpriv->era < 10)
|
||||
comp_params = rd_reg32(&ctrl->perfmon.comp_parms_ls);
|
||||
ctrlpriv->blob_present = !!(comp_params & CTPR_LS_BLOB);
|
||||
|
||||
/*
|
||||
* Some SoCs like the LS1028A (non-E) indicate CTPR_LS_BLOB support,
|
||||
* but fail when actually using it due to missing AES support, so
|
||||
* check both here.
|
||||
*/
|
||||
if (ctrlpriv->era < 10) {
|
||||
rng_vid = (rd_reg32(&ctrl->perfmon.cha_id_ls) &
|
||||
CHA_ID_LS_RNG_MASK) >> CHA_ID_LS_RNG_SHIFT;
|
||||
else
|
||||
ctrlpriv->blob_present = ctrlpriv->blob_present &&
|
||||
(rd_reg32(&ctrl->perfmon.cha_num_ls) & CHA_ID_LS_AES_MASK);
|
||||
} else {
|
||||
rng_vid = (rd_reg32(&ctrl->vreg.rng) & CHA_VER_VID_MASK) >>
|
||||
CHA_VER_VID_SHIFT;
|
||||
ctrlpriv->blob_present = ctrlpriv->blob_present &&
|
||||
(rd_reg32(&ctrl->vreg.aesa) & CHA_VER_MISC_AES_NUM_MASK);
|
||||
}
|
||||
|
||||
/*
|
||||
* If SEC has RNG version >= 4 and RNG state handle has not been
|
||||
|
@ -92,6 +92,7 @@ struct caam_drv_private {
|
||||
*/
|
||||
u8 total_jobrs; /* Total Job Rings in device */
|
||||
u8 qi_present; /* Nonzero if QI present in device */
|
||||
u8 blob_present; /* Nonzero if BLOB support present in device */
|
||||
u8 mc_en; /* Nonzero if MC f/w is active */
|
||||
int secvio_irq; /* Security violation interrupt number */
|
||||
int virt_en; /* Virtualization enabled in CAAM */
|
||||
|
@ -320,7 +320,8 @@ struct version_regs {
|
||||
#define CHA_VER_VID_MASK (0xffull << CHA_VER_VID_SHIFT)
|
||||
|
||||
/* CHA Miscellaneous Information - AESA_MISC specific */
|
||||
#define CHA_VER_MISC_AES_GCM BIT(1 + CHA_VER_MISC_SHIFT)
|
||||
#define CHA_VER_MISC_AES_NUM_MASK GENMASK(7, 0)
|
||||
#define CHA_VER_MISC_AES_GCM BIT(1 + CHA_VER_MISC_SHIFT)
|
||||
|
||||
/* CHA Miscellaneous Information - PKHA_MISC specific */
|
||||
#define CHA_VER_MISC_PKHA_NO_CRYPT BIT(7 + CHA_VER_MISC_SHIFT)
|
||||
@ -414,6 +415,7 @@ struct caam_perfmon {
|
||||
#define CTPR_MS_PG_SZ_MASK 0x10
|
||||
#define CTPR_MS_PG_SZ_SHIFT 4
|
||||
u32 comp_parms_ms; /* CTPR - Compile Parameters Register */
|
||||
#define CTPR_LS_BLOB BIT(1)
|
||||
u32 comp_parms_ls; /* CTPR - Compile Parameters Register */
|
||||
u64 rsvd1[2];
|
||||
|
||||
|
@ -10,6 +10,13 @@
|
||||
|
||||
#include <linux/key.h>
|
||||
|
||||
enum blacklist_hash_type {
|
||||
/* TBSCertificate hash */
|
||||
BLACKLIST_HASH_X509_TBS = 1,
|
||||
/* Raw data hash */
|
||||
BLACKLIST_HASH_BINARY = 2,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SYSTEM_TRUSTED_KEYRING
|
||||
|
||||
extern int restrict_link_by_builtin_trusted(struct key *keyring,
|
||||
@ -54,13 +61,14 @@ static inline void __init set_machine_trusted_keys(struct key *keyring)
|
||||
|
||||
extern struct pkcs7_message *pkcs7;
|
||||
#ifdef CONFIG_SYSTEM_BLACKLIST_KEYRING
|
||||
extern int mark_hash_blacklisted(const char *hash);
|
||||
extern int mark_hash_blacklisted(const u8 *hash, size_t hash_len,
|
||||
enum blacklist_hash_type hash_type);
|
||||
extern int is_hash_blacklisted(const u8 *hash, size_t hash_len,
|
||||
const char *type);
|
||||
enum blacklist_hash_type hash_type);
|
||||
extern int is_binary_blacklisted(const u8 *hash, size_t hash_len);
|
||||
#else
|
||||
static inline int is_hash_blacklisted(const u8 *hash, size_t hash_len,
|
||||
const char *type)
|
||||
enum blacklist_hash_type hash_type)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ struct trusted_key_ops {
|
||||
/* Unseal a key. */
|
||||
int (*unseal)(struct trusted_key_payload *p, char *datablob);
|
||||
|
||||
/* Get a randomized key. */
|
||||
/* Optional: Get a randomized key. */
|
||||
int (*get_random)(unsigned char *key, size_t key_len);
|
||||
|
||||
/* Exit key interface. */
|
||||
|
11
include/keys/trusted_caam.h
Normal file
11
include/keys/trusted_caam.h
Normal file
@ -0,0 +1,11 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (C) 2021 Pengutronix, Ahmad Fatoum <kernel@pengutronix.de>
|
||||
*/
|
||||
|
||||
#ifndef __CAAM_TRUSTED_KEY_H
|
||||
#define __CAAM_TRUSTED_KEY_H
|
||||
|
||||
extern struct trusted_key_ops trusted_key_caam_ops;
|
||||
|
||||
#endif
|
103
include/soc/fsl/caam-blob.h
Normal file
103
include/soc/fsl/caam-blob.h
Normal file
@ -0,0 +1,103 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (C) 2020 Pengutronix, Ahmad Fatoum <kernel@pengutronix.de>
|
||||
*/
|
||||
|
||||
#ifndef __CAAM_BLOB_GEN
|
||||
#define __CAAM_BLOB_GEN
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/errno.h>
|
||||
|
||||
#define CAAM_BLOB_KEYMOD_LENGTH 16
|
||||
#define CAAM_BLOB_OVERHEAD (32 + 16)
|
||||
#define CAAM_BLOB_MAX_LEN 4096
|
||||
|
||||
struct caam_blob_priv;
|
||||
|
||||
/**
|
||||
* struct caam_blob_info - information for CAAM blobbing
|
||||
* @input: pointer to input buffer (must be DMAable)
|
||||
* @input_len: length of @input buffer in bytes.
|
||||
* @output: pointer to output buffer (must be DMAable)
|
||||
* @output_len: length of @output buffer in bytes.
|
||||
* @key_mod: key modifier
|
||||
* @key_mod_len: length of @key_mod in bytes.
|
||||
* May not exceed %CAAM_BLOB_KEYMOD_LENGTH
|
||||
*/
|
||||
struct caam_blob_info {
|
||||
void *input;
|
||||
size_t input_len;
|
||||
|
||||
void *output;
|
||||
size_t output_len;
|
||||
|
||||
const void *key_mod;
|
||||
size_t key_mod_len;
|
||||
};
|
||||
|
||||
/**
|
||||
* caam_blob_gen_init - initialize blob generation
|
||||
* Return: pointer to new &struct caam_blob_priv instance on success
|
||||
* and ``ERR_PTR(-ENODEV)`` if CAAM has no hardware blobbing support
|
||||
* or no job ring could be allocated.
|
||||
*/
|
||||
struct caam_blob_priv *caam_blob_gen_init(void);
|
||||
|
||||
/**
|
||||
* caam_blob_gen_exit - free blob generation resources
|
||||
* @priv: instance returned by caam_blob_gen_init()
|
||||
*/
|
||||
void caam_blob_gen_exit(struct caam_blob_priv *priv);
|
||||
|
||||
/**
|
||||
* caam_process_blob - encapsulate or decapsulate blob
|
||||
* @priv: instance returned by caam_blob_gen_init()
|
||||
* @info: pointer to blobbing info describing key, blob and
|
||||
* key modifier buffers.
|
||||
* @encap: true for encapsulation, false for decapsulation
|
||||
*
|
||||
* Return: %0 and sets ``info->output_len`` on success and a negative
|
||||
* error code otherwise.
|
||||
*/
|
||||
int caam_process_blob(struct caam_blob_priv *priv,
|
||||
struct caam_blob_info *info, bool encap);
|
||||
|
||||
/**
|
||||
* caam_encap_blob - encapsulate blob
|
||||
* @priv: instance returned by caam_blob_gen_init()
|
||||
* @info: pointer to blobbing info describing input key,
|
||||
* output blob and key modifier buffers.
|
||||
*
|
||||
* Return: %0 and sets ``info->output_len`` on success and
|
||||
* a negative error code otherwise.
|
||||
*/
|
||||
static inline int caam_encap_blob(struct caam_blob_priv *priv,
|
||||
struct caam_blob_info *info)
|
||||
{
|
||||
if (info->output_len < info->input_len + CAAM_BLOB_OVERHEAD)
|
||||
return -EINVAL;
|
||||
|
||||
return caam_process_blob(priv, info, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* caam_decap_blob - decapsulate blob
|
||||
* @priv: instance returned by caam_blob_gen_init()
|
||||
* @info: pointer to blobbing info describing output key,
|
||||
* input blob and key modifier buffers.
|
||||
*
|
||||
* Return: %0 and sets ``info->output_len`` on success and
|
||||
* a negative error code otherwise.
|
||||
*/
|
||||
static inline int caam_decap_blob(struct caam_blob_priv *priv,
|
||||
struct caam_blob_info *info)
|
||||
{
|
||||
if (info->input_len < CAAM_BLOB_OVERHEAD ||
|
||||
info->output_len < info->input_len - CAAM_BLOB_OVERHEAD)
|
||||
return -EINVAL;
|
||||
|
||||
return caam_process_blob(priv, info, false);
|
||||
}
|
||||
|
||||
#endif
|
37
scripts/check-blacklist-hashes.awk
Executable file
37
scripts/check-blacklist-hashes.awk
Executable file
@ -0,0 +1,37 @@
|
||||
#!/usr/bin/awk -f
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Copyright © 2020, Microsoft Corporation. All rights reserved.
|
||||
#
|
||||
# Author: Mickaël Salaün <mic@linux.microsoft.com>
|
||||
#
|
||||
# Check that a CONFIG_SYSTEM_BLACKLIST_HASH_LIST file contains a valid array of
|
||||
# hash strings. Such string must start with a prefix ("tbs" or "bin"), then a
|
||||
# colon (":"), and finally an even number of hexadecimal lowercase characters
|
||||
# (up to 128).
|
||||
|
||||
BEGIN {
|
||||
RS = ","
|
||||
}
|
||||
{
|
||||
if (!match($0, "^[ \t\n\r]*\"([^\"]*)\"[ \t\n\r]*$", part1)) {
|
||||
print "Not a string (item " NR "):", $0;
|
||||
exit 1;
|
||||
}
|
||||
if (!match(part1[1], "^(tbs|bin):(.*)$", part2)) {
|
||||
print "Unknown prefix (item " NR "):", part1[1];
|
||||
exit 1;
|
||||
}
|
||||
if (!match(part2[2], "^([0-9a-f]+)$", part3)) {
|
||||
print "Not a lowercase hexadecimal string (item " NR "):", part2[2];
|
||||
exit 1;
|
||||
}
|
||||
if (length(part3[1]) > 128) {
|
||||
print "Hash string too long (item " NR "):", part3[1];
|
||||
exit 1;
|
||||
}
|
||||
if (length(part3[1]) % 2 == 1) {
|
||||
print "Not an even number of hexadecimal characters (item " NR "):", part3[1];
|
||||
exit 1;
|
||||
}
|
||||
}
|
@ -16,35 +16,13 @@ static efi_guid_t efi_cert_x509_sha256_guid __initdata =
|
||||
EFI_CERT_X509_SHA256_GUID;
|
||||
static efi_guid_t efi_cert_sha256_guid __initdata = EFI_CERT_SHA256_GUID;
|
||||
|
||||
/*
|
||||
* Blacklist a hash.
|
||||
*/
|
||||
static __init void uefi_blacklist_hash(const char *source, const void *data,
|
||||
size_t len, const char *type,
|
||||
size_t type_len)
|
||||
{
|
||||
char *hash, *p;
|
||||
|
||||
hash = kmalloc(type_len + len * 2 + 1, GFP_KERNEL);
|
||||
if (!hash)
|
||||
return;
|
||||
p = memcpy(hash, type, type_len);
|
||||
p += type_len;
|
||||
bin2hex(p, data, len);
|
||||
p += len * 2;
|
||||
*p = 0;
|
||||
|
||||
mark_hash_blacklisted(hash);
|
||||
kfree(hash);
|
||||
}
|
||||
|
||||
/*
|
||||
* Blacklist an X509 TBS hash.
|
||||
*/
|
||||
static __init void uefi_blacklist_x509_tbs(const char *source,
|
||||
const void *data, size_t len)
|
||||
{
|
||||
uefi_blacklist_hash(source, data, len, "tbs:", 4);
|
||||
mark_hash_blacklisted(data, len, BLACKLIST_HASH_X509_TBS);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -53,7 +31,7 @@ static __init void uefi_blacklist_x509_tbs(const char *source,
|
||||
static __init void uefi_blacklist_binary(const char *source,
|
||||
const void *data, size_t len)
|
||||
{
|
||||
uefi_blacklist_hash(source, data, len, "bin:", 4);
|
||||
mark_hash_blacklisted(data, len, BLACKLIST_HASH_BINARY);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -70,23 +70,19 @@ config BIG_KEYS
|
||||
|
||||
config TRUSTED_KEYS
|
||||
tristate "TRUSTED KEYS"
|
||||
depends on KEYS && TCG_TPM
|
||||
select CRYPTO
|
||||
select CRYPTO_HMAC
|
||||
select CRYPTO_SHA1
|
||||
select CRYPTO_HASH_INFO
|
||||
select ASN1_ENCODER
|
||||
select OID_REGISTRY
|
||||
select ASN1
|
||||
depends on KEYS
|
||||
help
|
||||
This option provides support for creating, sealing, and unsealing
|
||||
keys in the kernel. Trusted keys are random number symmetric keys,
|
||||
generated and RSA-sealed by the TPM. The TPM only unseals the keys,
|
||||
if the boot PCRs and other criteria match. Userspace will only ever
|
||||
see encrypted blobs.
|
||||
generated and sealed by a trust source selected at kernel boot-time.
|
||||
Userspace will only ever see encrypted blobs.
|
||||
|
||||
If you are unsure as to whether this is required, answer N.
|
||||
|
||||
if TRUSTED_KEYS
|
||||
source "security/keys/trusted-keys/Kconfig"
|
||||
endif
|
||||
|
||||
config ENCRYPTED_KEYS
|
||||
tristate "ENCRYPTED KEYS"
|
||||
depends on KEYS
|
||||
|
38
security/keys/trusted-keys/Kconfig
Normal file
38
security/keys/trusted-keys/Kconfig
Normal file
@ -0,0 +1,38 @@
|
||||
config TRUSTED_KEYS_TPM
|
||||
bool "TPM-based trusted keys"
|
||||
depends on TCG_TPM >= TRUSTED_KEYS
|
||||
default y
|
||||
select CRYPTO
|
||||
select CRYPTO_HMAC
|
||||
select CRYPTO_SHA1
|
||||
select CRYPTO_HASH_INFO
|
||||
select ASN1_ENCODER
|
||||
select OID_REGISTRY
|
||||
select ASN1
|
||||
help
|
||||
Enable use of the Trusted Platform Module (TPM) as trusted key
|
||||
backend. Trusted keys are random number symmetric keys,
|
||||
which will be generated and RSA-sealed by the TPM.
|
||||
The TPM only unseals the keys, if the boot PCRs and other
|
||||
criteria match.
|
||||
|
||||
config TRUSTED_KEYS_TEE
|
||||
bool "TEE-based trusted keys"
|
||||
depends on TEE >= TRUSTED_KEYS
|
||||
default y
|
||||
help
|
||||
Enable use of the Trusted Execution Environment (TEE) as trusted
|
||||
key backend.
|
||||
|
||||
config TRUSTED_KEYS_CAAM
|
||||
bool "CAAM-based trusted keys"
|
||||
depends on CRYPTO_DEV_FSL_CAAM_JR >= TRUSTED_KEYS
|
||||
select CRYPTO_DEV_FSL_CAAM_BLOB_GEN
|
||||
default y
|
||||
help
|
||||
Enable use of NXP's Cryptographic Accelerator and Assurance Module
|
||||
(CAAM) as trusted key backend.
|
||||
|
||||
if !TRUSTED_KEYS_TPM && !TRUSTED_KEYS_TEE && !TRUSTED_KEYS_CAAM
|
||||
comment "No trust source selected!"
|
||||
endif
|
@ -5,10 +5,12 @@
|
||||
|
||||
obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
|
||||
trusted-y += trusted_core.o
|
||||
trusted-y += trusted_tpm1.o
|
||||
trusted-$(CONFIG_TRUSTED_KEYS_TPM) += trusted_tpm1.o
|
||||
|
||||
$(obj)/trusted_tpm2.o: $(obj)/tpm2key.asn1.h
|
||||
trusted-y += trusted_tpm2.o
|
||||
trusted-y += tpm2key.asn1.o
|
||||
trusted-$(CONFIG_TRUSTED_KEYS_TPM) += trusted_tpm2.o
|
||||
trusted-$(CONFIG_TRUSTED_KEYS_TPM) += tpm2key.asn1.o
|
||||
|
||||
trusted-$(CONFIG_TEE) += trusted_tee.o
|
||||
trusted-$(CONFIG_TRUSTED_KEYS_TEE) += trusted_tee.o
|
||||
|
||||
trusted-$(CONFIG_TRUSTED_KEYS_CAAM) += trusted_caam.o
|
||||
|
80
security/keys/trusted-keys/trusted_caam.c
Normal file
80
security/keys/trusted-keys/trusted_caam.c
Normal file
@ -0,0 +1,80 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2021 Pengutronix, Ahmad Fatoum <kernel@pengutronix.de>
|
||||
*/
|
||||
|
||||
#include <keys/trusted_caam.h>
|
||||
#include <keys/trusted-type.h>
|
||||
#include <linux/build_bug.h>
|
||||
#include <linux/key-type.h>
|
||||
#include <soc/fsl/caam-blob.h>
|
||||
|
||||
static struct caam_blob_priv *blobifier;
|
||||
|
||||
#define KEYMOD "SECURE_KEY"
|
||||
|
||||
static_assert(MAX_KEY_SIZE + CAAM_BLOB_OVERHEAD <= CAAM_BLOB_MAX_LEN);
|
||||
static_assert(MAX_BLOB_SIZE <= CAAM_BLOB_MAX_LEN);
|
||||
|
||||
static int trusted_caam_seal(struct trusted_key_payload *p, char *datablob)
|
||||
{
|
||||
int ret;
|
||||
struct caam_blob_info info = {
|
||||
.input = p->key, .input_len = p->key_len,
|
||||
.output = p->blob, .output_len = MAX_BLOB_SIZE,
|
||||
.key_mod = KEYMOD, .key_mod_len = sizeof(KEYMOD) - 1,
|
||||
};
|
||||
|
||||
ret = caam_encap_blob(blobifier, &info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
p->blob_len = info.output_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int trusted_caam_unseal(struct trusted_key_payload *p, char *datablob)
|
||||
{
|
||||
int ret;
|
||||
struct caam_blob_info info = {
|
||||
.input = p->blob, .input_len = p->blob_len,
|
||||
.output = p->key, .output_len = MAX_KEY_SIZE,
|
||||
.key_mod = KEYMOD, .key_mod_len = sizeof(KEYMOD) - 1,
|
||||
};
|
||||
|
||||
ret = caam_decap_blob(blobifier, &info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
p->key_len = info.output_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int trusted_caam_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
blobifier = caam_blob_gen_init();
|
||||
if (IS_ERR(blobifier))
|
||||
return PTR_ERR(blobifier);
|
||||
|
||||
ret = register_key_type(&key_type_trusted);
|
||||
if (ret)
|
||||
caam_blob_gen_exit(blobifier);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void trusted_caam_exit(void)
|
||||
{
|
||||
unregister_key_type(&key_type_trusted);
|
||||
caam_blob_gen_exit(blobifier);
|
||||
}
|
||||
|
||||
struct trusted_key_ops trusted_key_caam_ops = {
|
||||
.migratable = 0, /* non-migratable */
|
||||
.init = trusted_caam_init,
|
||||
.seal = trusted_caam_seal,
|
||||
.unseal = trusted_caam_unseal,
|
||||
.exit = trusted_caam_exit,
|
||||
};
|
@ -9,6 +9,7 @@
|
||||
#include <keys/user-type.h>
|
||||
#include <keys/trusted-type.h>
|
||||
#include <keys/trusted_tee.h>
|
||||
#include <keys/trusted_caam.h>
|
||||
#include <keys/trusted_tpm.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/err.h>
|
||||
@ -16,23 +17,31 @@
|
||||
#include <linux/key-type.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/parser.h>
|
||||
#include <linux/random.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_rng = "default";
|
||||
module_param_named(rng, trusted_rng, charp, 0);
|
||||
MODULE_PARM_DESC(rng, "Select trusted key RNG");
|
||||
|
||||
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)");
|
||||
MODULE_PARM_DESC(source, "Select trusted keys source (tpm, tee or caam)");
|
||||
|
||||
static const struct trusted_key_source trusted_key_sources[] = {
|
||||
#if IS_REACHABLE(CONFIG_TCG_TPM)
|
||||
#if defined(CONFIG_TRUSTED_KEYS_TPM)
|
||||
{ "tpm", &trusted_key_tpm_ops },
|
||||
#endif
|
||||
#if IS_REACHABLE(CONFIG_TEE)
|
||||
#if defined(CONFIG_TRUSTED_KEYS_TEE)
|
||||
{ "tee", &trusted_key_tee_ops },
|
||||
#endif
|
||||
#if defined(CONFIG_TRUSTED_KEYS_CAAM)
|
||||
{ "caam", &trusted_key_caam_ops },
|
||||
#endif
|
||||
};
|
||||
|
||||
DEFINE_STATIC_CALL_NULL(trusted_key_init, *trusted_key_sources[0].ops->init);
|
||||
@ -312,8 +321,14 @@ struct key_type key_type_trusted = {
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(key_type_trusted);
|
||||
|
||||
static int kernel_get_random(unsigned char *key, size_t key_len)
|
||||
{
|
||||
return get_random_bytes_wait(key, key_len) ?: key_len;
|
||||
}
|
||||
|
||||
static int __init init_trusted(void)
|
||||
{
|
||||
int (*get_random)(unsigned char *key, size_t key_len);
|
||||
int i, ret = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(trusted_key_sources); i++) {
|
||||
@ -322,6 +337,28 @@ static int __init init_trusted(void)
|
||||
strlen(trusted_key_sources[i].name)))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* We always support trusted.rng="kernel" and "default" as
|
||||
* well as trusted.rng=$trusted.source if the trust source
|
||||
* defines its own get_random callback.
|
||||
*/
|
||||
get_random = trusted_key_sources[i].ops->get_random;
|
||||
if (trusted_rng && strcmp(trusted_rng, "default")) {
|
||||
if (!strcmp(trusted_rng, "kernel")) {
|
||||
get_random = kernel_get_random;
|
||||
} else if (strcmp(trusted_rng, trusted_key_sources[i].name) ||
|
||||
!get_random) {
|
||||
pr_warn("Unsupported RNG. Supported: kernel");
|
||||
if (get_random)
|
||||
pr_cont(", %s", trusted_key_sources[i].name);
|
||||
pr_cont(", default\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!get_random)
|
||||
get_random = kernel_get_random;
|
||||
|
||||
static_call_update(trusted_key_init,
|
||||
trusted_key_sources[i].ops->init);
|
||||
static_call_update(trusted_key_seal,
|
||||
@ -329,7 +366,7 @@ static int __init init_trusted(void)
|
||||
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);
|
||||
get_random);
|
||||
static_call_update(trusted_key_exit,
|
||||
trusted_key_sources[i].ops->exit);
|
||||
migratable = trusted_key_sources[i].ops->migratable;
|
||||
|
91
tools/certs/print-cert-tbs-hash.sh
Executable file
91
tools/certs/print-cert-tbs-hash.sh
Executable file
@ -0,0 +1,91 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Copyright © 2020, Microsoft Corporation. All rights reserved.
|
||||
#
|
||||
# Author: Mickaël Salaün <mic@linux.microsoft.com>
|
||||
#
|
||||
# Compute and print the To Be Signed (TBS) hash of a certificate. This is used
|
||||
# as description of keys in the blacklist keyring to identify certificates.
|
||||
# This output should be redirected, without newline, in a file (hash0.txt) and
|
||||
# signed to create a PKCS#7 file (hash0.p7s). Both of these files can then be
|
||||
# loaded in the kernel with.
|
||||
#
|
||||
# Exemple on a workstation:
|
||||
# ./print-cert-tbs-hash.sh certificate-to-invalidate.pem > hash0.txt
|
||||
# openssl smime -sign -in hash0.txt -inkey builtin-private-key.pem \
|
||||
# -signer builtin-certificate.pem -certfile certificate-chain.pem \
|
||||
# -noattr -binary -outform DER -out hash0.p7s
|
||||
#
|
||||
# Exemple on a managed system:
|
||||
# keyctl padd blacklist "$(< hash0.txt)" %:.blacklist < hash0.p7s
|
||||
|
||||
set -u -e -o pipefail
|
||||
|
||||
CERT="${1:-}"
|
||||
BASENAME="$(basename -- "${BASH_SOURCE[0]}")"
|
||||
|
||||
if [ $# -ne 1 ] || [ ! -f "${CERT}" ]; then
|
||||
echo "usage: ${BASENAME} <certificate>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Checks that it is indeed a certificate (PEM or DER encoded) and exclude the
|
||||
# optional PEM text header.
|
||||
if ! PEM="$(openssl x509 -inform DER -in "${CERT}" 2>/dev/null || openssl x509 -in "${CERT}")"; then
|
||||
echo "ERROR: Failed to parse certificate" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# TBSCertificate starts at the second entry.
|
||||
# Cf. https://tools.ietf.org/html/rfc3280#section-4.1
|
||||
#
|
||||
# Exemple of first lines printed by openssl asn1parse:
|
||||
# 0:d=0 hl=4 l= 763 cons: SEQUENCE
|
||||
# 4:d=1 hl=4 l= 483 cons: SEQUENCE
|
||||
# 8:d=2 hl=2 l= 3 cons: cont [ 0 ]
|
||||
# 10:d=3 hl=2 l= 1 prim: INTEGER :02
|
||||
# 13:d=2 hl=2 l= 20 prim: INTEGER :3CEB2CB8818D968AC00EEFE195F0DF9665328B7B
|
||||
# 35:d=2 hl=2 l= 13 cons: SEQUENCE
|
||||
# 37:d=3 hl=2 l= 9 prim: OBJECT :sha256WithRSAEncryption
|
||||
RANGE_AND_DIGEST_RE='
|
||||
2s/^\s*\([0-9]\+\):d=\s*[0-9]\+\s\+hl=\s*[0-9]\+\s\+l=\s*\([0-9]\+\)\s\+cons:\s*SEQUENCE\s*$/\1 \2/p;
|
||||
7s/^\s*[0-9]\+:d=\s*[0-9]\+\s\+hl=\s*[0-9]\+\s\+l=\s*[0-9]\+\s\+prim:\s*OBJECT\s*:\(.*\)$/\1/p;
|
||||
'
|
||||
|
||||
RANGE_AND_DIGEST=($(echo "${PEM}" | \
|
||||
openssl asn1parse -in - | \
|
||||
sed -n -e "${RANGE_AND_DIGEST_RE}"))
|
||||
|
||||
if [ "${#RANGE_AND_DIGEST[@]}" != 3 ]; then
|
||||
echo "ERROR: Failed to parse TBSCertificate." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
OFFSET="${RANGE_AND_DIGEST[0]}"
|
||||
END="$(( OFFSET + RANGE_AND_DIGEST[1] ))"
|
||||
DIGEST="${RANGE_AND_DIGEST[2]}"
|
||||
|
||||
# The signature hash algorithm is used by Linux to blacklist certificates.
|
||||
# Cf. crypto/asymmetric_keys/x509_cert_parser.c:x509_note_pkey_algo()
|
||||
DIGEST_MATCH=""
|
||||
while read -r DIGEST_ITEM; do
|
||||
if [ -z "${DIGEST_ITEM}" ]; then
|
||||
break
|
||||
fi
|
||||
if echo "${DIGEST}" | grep -qiF "${DIGEST_ITEM}"; then
|
||||
DIGEST_MATCH="${DIGEST_ITEM}"
|
||||
break
|
||||
fi
|
||||
done < <(openssl list -digest-commands | tr ' ' '\n' | sort -ur)
|
||||
|
||||
if [ -z "${DIGEST_MATCH}" ]; then
|
||||
echo "ERROR: Unknown digest algorithm: ${DIGEST}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "${PEM}" | \
|
||||
openssl x509 -in - -outform DER | \
|
||||
dd "bs=1" "skip=${OFFSET}" "count=${END}" "status=none" | \
|
||||
openssl dgst "-${DIGEST_MATCH}" - | \
|
||||
awk '{printf "tbs:" $2}'
|
Loading…
Reference in New Issue
Block a user