2018-09-05 21:56:14

by David Howells

[permalink] [raw]
Subject: [PATCH 00/22] KEYS: Support TPM-wrapped key and crypto ops


Hi James,

Here's a set of patches that does the following, if you could pull it please:

(1) Adds keyctl() functions that permit an asymmetric-type key to be used
to encrypt, decrypt, sign and verify a small piece of data (typically
a session key or a hash) using the public and/or private key held in
the kernel. A query function is also provided.

(2) Adds an asymmetric-key parser for PKCS#8 to allow RSA private keys to
be passed to the kernel. Currently only DER-encoded and unencrypted
PKCS#8 is supported.

(3) Adds an asymmetric-key parser for TPM-wrapped key blobs. The parser
gets the TPM to unpack the blob and then attaches it to a key from
which it can be used. Currently only TPM-1.2 is supported.

Example usage for a PKCS#8 blob:

j=`openssl pkcs8 -in private_key.pem -topk8 -nocrypt -outform DER | \
keyctl padd asymmetric foo @s`

Example usage for a TPM wrapped blob:

openssl genrsa -out /tmp/privkey.foo.pem 2048
create_tpm_key -s 2048 -w /tmp/privkey.foo.pem /tmp/privkey.foo.tpm
j=`openssl asn1parse -inform pem -in /tmp/privkey.foo.tpm -noout |
keyctl padd asymmetric foo @s`

See http://david.woodhou.se/draft-woodhouse-cert-best-practice.html#rfc.section.4.4

These keys can then be used thusly:

echo -n abcdefghijklmnopqrst >/tmp/data
keyctl pkey_encrypt $j /tmp/data enc=pkcs1 >/tmp/enc
keyctl pkey_decrypt $j /tmp/enc enc=pkcs1 >/tmp/dec
cmp /tmp/data /tmp/dec
keyctl pkey_sign $j /tmp/data enc=pkcs1 hash=sha1 >/tmp/sig
keyctl pkey_verify $j /tmp/data /tmp/sig enc=pkcs1 hash=sha1


The kernel patches can be found tagged here:

https://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs.git/tag/?h=keys-asym-keyctl-20180905

and also on a branch here:

http://git.kernel.org/cgit/linux/kernel/git/dhowells/linux-fs.git/log/?h=keys-asym-keyctl

The keyutils changes needed can be found here:

http://git.kernel.org/cgit/linux/kernel/git/dhowells/keyutils.git/log/?h=next

David
---
David Howells (8):
KEYS: Provide key type operations for asymmetric key ops
KEYS: Provide keyctls to drive the new key type ops for asymmetric keys
KEYS: Provide missing asymmetric key subops for new key type ops
KEYS: Make the X.509 and PKCS7 parsers supply the sig encoding type
KEYS: Provide software public key query function
KEYS: Allow the public_key struct to hold a private key
KEYS: Implement encrypt, decrypt and sign for software asymmetric key
KEYS: Implement PKCS#8 RSA Private Key parser

Denis Kenzior (14):
crypto: rsa-pkcs1pad: Allow hash to be optional
KEYS: asym_tpm: add skeleton for asym_tpm
KEYS: asym_tpm: extract key size & public key
KEYS: Add parser for TPM-based keys
KEYS: asym_tpm: Implement pkey_query
KEYS: asym_tpm: Implement encryption operation
KEYS: trusted: Expose common functionality
KEYS: Move trusted.h to include/keys
KEYS: asym_tpm: Add loadkey2 and flushspecific
KEYS: asym_tpm: Implement tpm_unbind
KEYS: asym_tpm: Implement the decrypt operation
KEYS: asym_tpm: Implement signature verification
KEYS: asym_tpm: Implement tpm_sign
KEYS: asym_tpm: Add support for the sign operation


Documentation/crypto/asymmetric-keys.txt | 26 +
Documentation/security/keys/core.rst | 217 ++++++
crypto/asymmetric_keys/Kconfig | 31 +
crypto/asymmetric_keys/Makefile | 25 +
crypto/asymmetric_keys/asym_tpm.c | 988 +++++++++++++++++++++++++++++
crypto/asymmetric_keys/asymmetric_keys.h | 3
crypto/asymmetric_keys/asymmetric_type.c | 43 +
crypto/asymmetric_keys/pkcs7_parser.c | 1
crypto/asymmetric_keys/pkcs8.asn1 | 24 +
crypto/asymmetric_keys/pkcs8_parser.c | 184 +++++
crypto/asymmetric_keys/public_key.c | 191 +++++-
crypto/asymmetric_keys/signature.c | 95 +++
crypto/asymmetric_keys/tpm.asn1 | 5
crypto/asymmetric_keys/tpm_parser.c | 102 +++
crypto/asymmetric_keys/x509_cert_parser.c | 21 -
crypto/rsa-pkcs1pad.c | 59 +-
include/crypto/asym_tpm_subtype.h | 19 +
include/crypto/public_key.h | 14
include/keys/asymmetric-subtype.h | 9
include/keys/trusted.h | 136 ++++
include/linux/key-type.h | 11
include/linux/keyctl.h | 46 +
include/uapi/linux/keyctl.h | 30 +
security/keys/Makefile | 1
security/keys/compat.c | 18 +
security/keys/internal.h | 39 +
security/keys/keyctl.c | 24 +
security/keys/keyctl_pkey.c | 323 +++++++++
security/keys/trusted.c | 14
security/keys/trusted.h | 124 ----
30 files changed, 2639 insertions(+), 184 deletions(-)
create mode 100644 crypto/asymmetric_keys/asym_tpm.c
create mode 100644 crypto/asymmetric_keys/pkcs8.asn1
create mode 100644 crypto/asymmetric_keys/pkcs8_parser.c
create mode 100644 crypto/asymmetric_keys/tpm.asn1
create mode 100644 crypto/asymmetric_keys/tpm_parser.c
create mode 100644 include/crypto/asym_tpm_subtype.h
create mode 100644 include/keys/trusted.h
create mode 100644 include/linux/keyctl.h
create mode 100644 security/keys/keyctl_pkey.c
delete mode 100644 security/keys/trusted.h



2018-09-05 21:56:13

by David Howells

[permalink] [raw]
Subject: [PATCH 01/22] KEYS: Provide key type operations for asymmetric key ops

Provide five new operations in the key_type struct that can be used to
provide access to asymmetric key operations. These will be implemented for
the asymmetric key type in a later patch and may refer to a key retained in
RAM by the kernel or a key retained in crypto hardware.

int (*asym_query)(const struct kernel_pkey_params *params,
struct kernel_pkey_query *info);
int (*asym_eds_op)(struct kernel_pkey_params *params,
const void *in, void *out);
int (*asym_verify_signature)(struct kernel_pkey_params *params,
const void *in, const void *in2);

Since encrypt, decrypt and sign are identical in their interfaces, they're
rolled together in the asym_eds_op() operation and there's an operation ID
in the params argument to distinguish them.

Verify is different in that we supply the data and the signature instead
and get an error value (or 0) as the only result on the expectation that
this may well be how a hardware crypto device may work.

Signed-off-by: David Howells <[email protected]>
---

Documentation/security/keys/core.rst | 106 ++++++++++++++++++++++++++++++++++
include/linux/key-type.h | 11 ++++
include/linux/keyctl.h | 46 +++++++++++++++
include/uapi/linux/keyctl.h | 5 ++
4 files changed, 168 insertions(+)
create mode 100644 include/linux/keyctl.h

diff --git a/Documentation/security/keys/core.rst b/Documentation/security/keys/core.rst
index 9ce7256c6edb..2cdccde6c585 100644
--- a/Documentation/security/keys/core.rst
+++ b/Documentation/security/keys/core.rst
@@ -1483,6 +1483,112 @@ The structure has a number of fields, some of which are mandatory:
attempted key link operation. If there is no match, -EINVAL is returned.


+ * ``int (*asym_eds_op)(struct kernel_pkey_params *params,
+ const void *in, void *out);``
+ ``int (*asym_verify_signature)(struct kernel_pkey_params *params,
+ const void *in, const void *in2);``
+
+ These methods are optional. If provided the first allows a key to be
+ used to encrypt, decrypt or sign a blob of data, and the second allows a
+ key to verify a signature.
+
+ In all cases, the following information is provided in the params block::
+
+ struct kernel_pkey_params {
+ struct key *key;
+ const char *encoding;
+ const char *hash_algo;
+ char *info;
+ __u32 in_len;
+ union {
+ __u32 out_len;
+ __u32 in2_len;
+ };
+ enum kernel_pkey_operation op : 8;
+ };
+
+ This includes the key to be used; an optional string indicating the
+ encoding to use (for instance, "pkcs1" may be used with an RSA key to
+ indicate RSASSA-PKCS1-v1.5 or RSAES-PKCS1-v1.5 encoding); the name of the
+ hash algorithm used to generate the data for a signature (if appropriate);
+ the sizes of the input and output (or second input) buffers; and the ID of
+ the operation to be performed.
+
+ For a given operation ID, the input and output buffers are used as
+ follows::
+
+ Operation ID in,in_len out,out_len in2,in2_len
+ ======================= =============== =============== ===============
+ kernel_pkey_encrypt Raw data Encrypted data -
+ kernel_pkey_decrypt Encrypted data Raw data -
+ kernel_pkey_sign Raw data Signature -
+ kernel_pkey_verify Raw data - Signature
+
+ asym_eds_op() deals with encryption, decryption and signature creation as
+ specified by params->op. Note that params->op is also set for
+ asym_verify_signature().
+
+ Encrypting and signature creation both take raw data in the input buffer
+ and return the encrypted result in the output buffer. Padding may have
+ been added if an encoding was set. In the case of signature creation,
+ depending on the encoding, the padding created may need to indicate the
+ digest algorithm - the name of which should be supplied in hash_algo.
+
+ Decryption takes encrypted data in the input buffer and returns the raw
+ data in the output buffer. Padding will get checked and stripped off if
+ an encoding was set.
+
+ Verification takes raw data in the input buffer and the signature in the
+ second input buffer and checks that the one matches the other. Padding
+ will be validated. Depending on the encoding, the digest algorithm used
+ to generate the raw data may need to be indicated in hash_algo.
+
+ If successful, asym_eds_op() should return the number of bytes written
+ into the output buffer. asym_verify_signature() should return 0.
+
+ A variety of errors may be returned, including EOPNOTSUPP if the operation
+ is not supported; EKEYREJECTED if verification fails; ENOPKG if the
+ required crypto isn't available.
+
+
+ * ``int (*asym_query)(const struct kernel_pkey_params *params,
+ struct kernel_pkey_query *info);``
+
+ This method is optional. If provided it allows information about the
+ public or asymmetric key held in the key to be determined.
+
+ The parameter block is as for asym_eds_op() and co. but in_len and out_len
+ are unused. The encoding and hash_algo fields should be used to reduce
+ the returned buffer/data sizes as appropriate.
+
+ If successful, the following information is filled in::
+
+ struct kernel_pkey_query {
+ __u32 supported_ops;
+ __u32 key_size;
+ __u16 max_data_size;
+ __u16 max_sig_size;
+ __u16 max_enc_size;
+ __u16 max_dec_size;
+ };
+
+ The supported_ops field will contain a bitmask indicating what operations
+ are supported by the key, including encryption of a blob, decryption of a
+ blob, signing a blob and verifying the signature on a blob. The following
+ constants are defined for this::
+
+ KEYCTL_SUPPORTS_{ENCRYPT,DECRYPT,SIGN,VERIFY}
+
+ The key_size field is the size of the key in bits. max_data_size and
+ max_sig_size are the maximum raw data and signature sizes for creation and
+ verification of a signature; max_enc_size and max_dec_size are the maximum
+ raw data and signature sizes for encryption and decryption. The
+ max_*_size fields are measured in bytes.
+
+ If successful, 0 will be returned. If the key doesn't support this,
+ EOPNOTSUPP will be returned.
+
+
Request-Key Callback Service
============================

diff --git a/include/linux/key-type.h b/include/linux/key-type.h
index 05d8fb5a06c4..bc9af551fc83 100644
--- a/include/linux/key-type.h
+++ b/include/linux/key-type.h
@@ -17,6 +17,9 @@

#ifdef CONFIG_KEYS

+struct kernel_pkey_query;
+struct kernel_pkey_params;
+
/*
* key under-construction record
* - passed to the request_key actor if supplied
@@ -155,6 +158,14 @@ struct key_type {
*/
struct key_restriction *(*lookup_restriction)(const char *params);

+ /* Asymmetric key accessor functions. */
+ int (*asym_query)(const struct kernel_pkey_params *params,
+ struct kernel_pkey_query *info);
+ int (*asym_eds_op)(struct kernel_pkey_params *params,
+ const void *in, void *out);
+ int (*asym_verify_signature)(struct kernel_pkey_params *params,
+ const void *in, const void *in2);
+
/* internal fields */
struct list_head link; /* link in types list */
struct lock_class_key lock_class; /* key->sem lock class */
diff --git a/include/linux/keyctl.h b/include/linux/keyctl.h
new file mode 100644
index 000000000000..e89b4a4e43d0
--- /dev/null
+++ b/include/linux/keyctl.h
@@ -0,0 +1,46 @@
+/* keyctl kernel bits
+ *
+ * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef __LINUX_KEYCTL_H
+#define __LINUX_KEYCTL_H
+
+#include <uapi/linux/keyctl.h>
+
+struct kernel_pkey_query {
+ __u32 supported_ops; /* Which ops are supported */
+ __u32 key_size; /* Size of the key in bits */
+ __u16 max_data_size; /* Maximum size of raw data to sign in bytes */
+ __u16 max_sig_size; /* Maximum size of signature in bytes */
+ __u16 max_enc_size; /* Maximum size of encrypted blob in bytes */
+ __u16 max_dec_size; /* Maximum size of decrypted blob in bytes */
+};
+
+enum kernel_pkey_operation {
+ kernel_pkey_encrypt,
+ kernel_pkey_decrypt,
+ kernel_pkey_sign,
+ kernel_pkey_verify,
+};
+
+struct kernel_pkey_params {
+ struct key *key;
+ const char *encoding; /* Encoding (eg. "oaep" or NULL for raw) */
+ const char *hash_algo; /* Digest algorithm used (eg. "sha1") or NULL if N/A */
+ char *info; /* Modified info string to be released later */
+ __u32 in_len; /* Input data size */
+ union {
+ __u32 out_len; /* Output buffer size (enc/dec/sign) */
+ __u32 in2_len; /* 2nd input data size (verify) */
+ };
+ enum kernel_pkey_operation op : 8;
+};
+
+#endif /* __LINUX_KEYCTL_H */
diff --git a/include/uapi/linux/keyctl.h b/include/uapi/linux/keyctl.h
index 7b8c9e19bad1..d5eac15bc00c 100644
--- a/include/uapi/linux/keyctl.h
+++ b/include/uapi/linux/keyctl.h
@@ -77,4 +77,9 @@ struct keyctl_kdf_params {
__u32 __spare[8];
};

+#define KEYCTL_SUPPORTS_ENCRYPT 0x01
+#define KEYCTL_SUPPORTS_DECRYPT 0x02
+#define KEYCTL_SUPPORTS_SIGN 0x04
+#define KEYCTL_SUPPORTS_VERIFY 0x08
+
#endif /* _LINUX_KEYCTL_H */


2018-09-05 21:56:13

by David Howells

[permalink] [raw]
Subject: [PATCH 02/22] KEYS: Provide keyctls to drive the new key type ops for asymmetric keys

Provide five keyctl functions that permit userspace to make use of the new
key type ops for accessing and driving asymmetric keys.

(*) Query an asymmetric key.

long keyctl(KEYCTL_PKEY_QUERY,
key_serial_t key, unsigned long reserved,
struct keyctl_pkey_query *info);

Get information about an asymmetric key. The information is returned
in the keyctl_pkey_query struct:

__u32 supported_ops;

A bit mask of flags indicating which ops are supported. This is
constructed from a bitwise-OR of:

KEYCTL_SUPPORTS_{ENCRYPT,DECRYPT,SIGN,VERIFY}

__u32 key_size;

The size in bits of the key.

__u16 max_data_size;
__u16 max_sig_size;
__u16 max_enc_size;
__u16 max_dec_size;

The maximum sizes in bytes of a blob of data to be signed, a signature
blob, a blob to be encrypted and a blob to be decrypted.

reserved must be set to 0. This is intended for future use to hand
over one or more passphrases needed unlock a key.

If successful, 0 is returned. If the key is not an asymmetric key,
EOPNOTSUPP is returned.


(*) Encrypt, decrypt, sign or verify a blob using an asymmetric key.

long keyctl(KEYCTL_PKEY_ENCRYPT,
const struct keyctl_pkey_params *params,
const char *info,
const void *in,
void *out);

long keyctl(KEYCTL_PKEY_DECRYPT,
const struct keyctl_pkey_params *params,
const char *info,
const void *in,
void *out);

long keyctl(KEYCTL_PKEY_SIGN,
const struct keyctl_pkey_params *params,
const char *info,
const void *in,
void *out);

long keyctl(KEYCTL_PKEY_VERIFY,
const struct keyctl_pkey_params *params,
const char *info,
const void *in,
const void *in2);

Use an asymmetric key to perform a public-key cryptographic operation
a blob of data.

The parameter block pointed to by params contains a number of integer
values:

__s32 key_id;
__u32 in_len;
__u32 out_len;
__u32 in2_len;

For a given operation, the in and out buffers are used as follows:

Operation ID in,in_len out,out_len in2,in2_len
======================= =============== =============== ===========
KEYCTL_PKEY_ENCRYPT Raw data Encrypted data -
KEYCTL_PKEY_DECRYPT Encrypted data Raw data -
KEYCTL_PKEY_SIGN Raw data Signature -
KEYCTL_PKEY_VERIFY Raw data - Signature

info is a string of key=value pairs that supply supplementary
information.

The __spare space in the parameter block must be set to 0. This is
intended, amongst other things, to allow the passing of passphrases
required to unlock a key.

If successful, encrypt, decrypt and sign all return the amount of data
written into the output buffer. Verification returns 0 on success.

Signed-off-by: David Howells <[email protected]>
---

Documentation/security/keys/core.rst | 111 ++++++++++++
include/uapi/linux/keyctl.h | 25 +++
security/keys/Makefile | 1
security/keys/compat.c | 18 ++
security/keys/internal.h | 39 ++++
security/keys/keyctl.c | 24 +++
security/keys/keyctl_pkey.c | 323 ++++++++++++++++++++++++++++++++++
7 files changed, 541 insertions(+)
create mode 100644 security/keys/keyctl_pkey.c

diff --git a/Documentation/security/keys/core.rst b/Documentation/security/keys/core.rst
index 2cdccde6c585..3d36f42283e6 100644
--- a/Documentation/security/keys/core.rst
+++ b/Documentation/security/keys/core.rst
@@ -859,6 +859,7 @@ The keyctl syscall functions are:
and either the buffer length or the OtherInfo length exceeds the
allowed length.

+
* Restrict keyring linkage::

long keyctl(KEYCTL_RESTRICT_KEYRING, key_serial_t keyring,
@@ -890,6 +891,116 @@ The keyctl syscall functions are:
applicable to the asymmetric key type.


+ * Query an asymmetric key::
+
+ long keyctl(KEYCTL_PKEY_QUERY,
+ key_serial_t key_id, unsigned long reserved,
+ struct keyctl_pkey_query *info);
+
+ Get information about an asymmetric key. The information is returned in
+ the keyctl_pkey_query struct::
+
+ __u32 supported_ops;
+ __u32 key_size;
+ __u16 max_data_size;
+ __u16 max_sig_size;
+ __u16 max_enc_size;
+ __u16 max_dec_size;
+ __u32 __spare[10];
+
+ ``supported_ops`` contains a bit mask of flags indicating which ops are
+ supported. This is constructed from a bitwise-OR of::
+
+ KEYCTL_SUPPORTS_{ENCRYPT,DECRYPT,SIGN,VERIFY}
+
+ ``key_size`` indicated the size of the key in bits.
+
+ ``max_*_size`` indicate the maximum sizes in bytes of a blob of data to be
+ signed, a signature blob, a blob to be encrypted and a blob to be
+ decrypted.
+
+ ``__spare[]`` must be set to 0. This is intended for future use to hand
+ over one or more passphrases needed unlock a key.
+
+ If successful, 0 is returned. If the key is not an asymmetric key,
+ EOPNOTSUPP is returned.
+
+
+ * Encrypt, decrypt, sign or verify a blob using an asymmetric key::
+
+ long keyctl(KEYCTL_PKEY_ENCRYPT,
+ const struct keyctl_pkey_params *params,
+ const char *info,
+ const void *in,
+ void *out);
+
+ long keyctl(KEYCTL_PKEY_DECRYPT,
+ const struct keyctl_pkey_params *params,
+ const char *info,
+ const void *in,
+ void *out);
+
+ long keyctl(KEYCTL_PKEY_SIGN,
+ const struct keyctl_pkey_params *params,
+ const char *info,
+ const void *in,
+ void *out);
+
+ long keyctl(KEYCTL_PKEY_VERIFY,
+ const struct keyctl_pkey_params *params,
+ const char *info,
+ const void *in,
+ const void *in2);
+
+ Use an asymmetric key to perform a public-key cryptographic operation a
+ blob of data. For encryption and verification, the asymmetric key may
+ only need the public parts to be available, but for decryption and signing
+ the private parts are required also.
+
+ The parameter block pointed to by params contains a number of integer
+ values::
+
+ __s32 key_id;
+ __u32 in_len;
+ __u32 out_len;
+ __u32 in2_len;
+
+ ``key_id`` is the ID of the asymmetric key to be used. ``in_len`` and
+ ``in2_len`` indicate the amount of data in the in and in2 buffers and
+ ``out_len`` indicates the size of the out buffer as appropriate for the
+ above operations.
+
+ For a given operation, the in and out buffers are used as follows::
+
+ Operation ID in,in_len out,out_len in2,in2_len
+ ======================= =============== =============== ===============
+ KEYCTL_PKEY_ENCRYPT Raw data Encrypted data -
+ KEYCTL_PKEY_DECRYPT Encrypted data Raw data -
+ KEYCTL_PKEY_SIGN Raw data Signature -
+ KEYCTL_PKEY_VERIFY Raw data - Signature
+
+ ``info`` is a string of key=value pairs that supply supplementary
+ information. These include:
+
+ ``enc=<encoding>`` The encoding of the encrypted/signature blob. This
+ can be "pkcs1" for RSASSA-PKCS1-v1.5 or
+ RSAES-PKCS1-v1.5; "pss" for "RSASSA-PSS"; "oaep" for
+ "RSAES-OAEP". If omitted or is "raw", the raw output
+ of the encryption function is specified.
+
+ ``hash=<algo>`` If the data buffer contains the output of a hash
+ function and the encoding includes some indication of
+ which hash function was used, the hash function can be
+ specified with this, eg. "hash=sha256".
+
+ The ``__spare[]`` space in the parameter block must be set to 0. This is
+ intended, amongst other things, to allow the passing of passphrases
+ required to unlock a key.
+
+ If successful, encrypt, decrypt and sign all return the amount of data
+ written into the output buffer. Verification returns 0 on success.
+
+
Kernel Services
===============

diff --git a/include/uapi/linux/keyctl.h b/include/uapi/linux/keyctl.h
index d5eac15bc00c..1a5a7948606e 100644
--- a/include/uapi/linux/keyctl.h
+++ b/include/uapi/linux/keyctl.h
@@ -61,6 +61,11 @@
#define KEYCTL_INVALIDATE 21 /* invalidate a key */
#define KEYCTL_GET_PERSISTENT 22 /* get a user's persistent keyring */
#define KEYCTL_DH_COMPUTE 23 /* Compute Diffie-Hellman values */
+#define KEYCTL_PKEY_QUERY 24 /* Query public key parameters */
+#define KEYCTL_PKEY_ENCRYPT 25 /* Encrypt a blob using a public key */
+#define KEYCTL_PKEY_DECRYPT 26 /* Decrypt a blob using a public key */
+#define KEYCTL_PKEY_SIGN 27 /* Create a public key signature */
+#define KEYCTL_PKEY_VERIFY 28 /* Verify a public key signature */
#define KEYCTL_RESTRICT_KEYRING 29 /* Restrict keys allowed to link to a keyring */

/* keyctl structures */
@@ -82,4 +87,24 @@ struct keyctl_kdf_params {
#define KEYCTL_SUPPORTS_SIGN 0x04
#define KEYCTL_SUPPORTS_VERIFY 0x08

+struct keyctl_pkey_query {
+ __u32 supported_ops; /* Which ops are supported */
+ __u32 key_size; /* Size of the key in bits */
+ __u16 max_data_size; /* Maximum size of raw data to sign in bytes */
+ __u16 max_sig_size; /* Maximum size of signature in bytes */
+ __u16 max_enc_size; /* Maximum size of encrypted blob in bytes */
+ __u16 max_dec_size; /* Maximum size of decrypted blob in bytes */
+ __u32 __spare[10];
+};
+
+struct keyctl_pkey_params {
+ __s32 key_id; /* Serial no. of public key to use */
+ __u32 in_len; /* Input data size */
+ union {
+ __u32 out_len; /* Output buffer size (encrypt/decrypt/sign) */
+ __u32 in2_len; /* 2nd input data size (verify) */
+ };
+ __u32 __spare[7];
+};
+
#endif /* _LINUX_KEYCTL_H */
diff --git a/security/keys/Makefile b/security/keys/Makefile
index ef1581b337a3..9cef54064f60 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_SYSCTL) += sysctl.o
obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o
obj-$(CONFIG_KEY_DH_OPERATIONS) += dh.o
+obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += keyctl_pkey.o

#
# Key types
diff --git a/security/keys/compat.c b/security/keys/compat.c
index e87c89c0177c..9482df601dc3 100644
--- a/security/keys/compat.c
+++ b/security/keys/compat.c
@@ -141,6 +141,24 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option,
return keyctl_restrict_keyring(arg2, compat_ptr(arg3),
compat_ptr(arg4));

+ case KEYCTL_PKEY_QUERY:
+ if (arg3 != 0)
+ return -EINVAL;
+ return keyctl_pkey_query(arg2,
+ compat_ptr(arg4),
+ compat_ptr(arg5));
+
+ case KEYCTL_PKEY_ENCRYPT:
+ case KEYCTL_PKEY_DECRYPT:
+ case KEYCTL_PKEY_SIGN:
+ return keyctl_pkey_e_d_s(option,
+ compat_ptr(arg2), compat_ptr(arg3),
+ compat_ptr(arg4), compat_ptr(arg5));
+
+ case KEYCTL_PKEY_VERIFY:
+ return keyctl_pkey_verify(compat_ptr(arg2), compat_ptr(arg3),
+ compat_ptr(arg4), compat_ptr(arg5));
+
default:
return -EOPNOTSUPP;
}
diff --git a/security/keys/internal.h b/security/keys/internal.h
index 9f8208dc0e55..74cb0ff42fed 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -298,6 +298,45 @@ static inline long compat_keyctl_dh_compute(
#endif
#endif

+#ifdef CONFIG_ASYMMETRIC_KEY_TYPE
+extern long keyctl_pkey_query(key_serial_t,
+ const char __user *,
+ struct keyctl_pkey_query __user *);
+
+extern long keyctl_pkey_verify(const struct keyctl_pkey_params __user *,
+ const char __user *,
+ const void __user *, const void __user *);
+
+extern long keyctl_pkey_e_d_s(int,
+ const struct keyctl_pkey_params __user *,
+ const char __user *,
+ const void __user *, void __user *);
+#else
+static inline long keyctl_pkey_query(key_serial_t id,
+ const char __user *_info,
+ struct keyctl_pkey_query __user *_res)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline long keyctl_pkey_verify(const struct keyctl_pkey_params __user *params,
+ const char __user *_info,
+ const void __user *_in,
+ const void __user *_in2)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline long keyctl_pkey_e_d_s(int op,
+ const struct keyctl_pkey_params __user *params,
+ const char __user *_info,
+ const void __user *_in,
+ void __user *_out)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+
/*
* Debugging key validation
*/
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index 1ffe60bb2845..18619690ce77 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -1747,6 +1747,30 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
(const char __user *) arg3,
(const char __user *) arg4);

+ case KEYCTL_PKEY_QUERY:
+ if (arg3 != 0)
+ return -EINVAL;
+ return keyctl_pkey_query((key_serial_t)arg2,
+ (const char __user *)arg4,
+ (struct keyctl_pkey_query *)arg5);
+
+ case KEYCTL_PKEY_ENCRYPT:
+ case KEYCTL_PKEY_DECRYPT:
+ case KEYCTL_PKEY_SIGN:
+ return keyctl_pkey_e_d_s(
+ option,
+ (const struct keyctl_pkey_params __user *)arg2,
+ (const char __user *)arg3,
+ (const void __user *)arg4,
+ (void __user *)arg5);
+
+ case KEYCTL_PKEY_VERIFY:
+ return keyctl_pkey_verify(
+ (const struct keyctl_pkey_params __user *)arg2,
+ (const char __user *)arg3,
+ (const void __user *)arg4,
+ (const void __user *)arg5);
+
default:
return -EOPNOTSUPP;
}
diff --git a/security/keys/keyctl_pkey.c b/security/keys/keyctl_pkey.c
new file mode 100644
index 000000000000..783978842f13
--- /dev/null
+++ b/security/keys/keyctl_pkey.c
@@ -0,0 +1,323 @@
+/* Public-key operation keyctls
+ *
+ * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/key.h>
+#include <linux/keyctl.h>
+#include <linux/parser.h>
+#include <linux/uaccess.h>
+#include <keys/user-type.h>
+#include "internal.h"
+
+static void keyctl_pkey_params_free(struct kernel_pkey_params *params)
+{
+ kfree(params->info);
+ key_put(params->key);
+}
+
+enum {
+ Opt_err = -1,
+ Opt_enc, /* "enc=<encoding>" eg. "enc=oaep" */
+ Opt_hash, /* "hash=<digest-name>" eg. "hash=sha1" */
+};
+
+static const match_table_t param_keys = {
+ { Opt_enc, "enc=%s" },
+ { Opt_hash, "hash=%s" },
+ { Opt_err, NULL }
+};
+
+/*
+ * Parse the information string which consists of key=val pairs.
+ */
+static int keyctl_pkey_params_parse(struct kernel_pkey_params *params)
+{
+ unsigned long token_mask = 0;
+ substring_t args[MAX_OPT_ARGS];
+ char *c = params->info, *p, *q;
+ int token;
+
+ while ((p = strsep(&c, " \t"))) {
+ if (*p == '\0' || *p == ' ' || *p == '\t')
+ continue;
+ token = match_token(p, param_keys, args);
+ if (__test_and_set_bit(token, &token_mask))
+ return -EINVAL;
+ q = args[0].from;
+ if (!q[0])
+ return -EINVAL;
+
+ switch (token) {
+ case Opt_enc:
+ params->encoding = q;
+ break;
+
+ case Opt_hash:
+ params->hash_algo = q;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Interpret parameters. Callers must always call the free function
+ * on params, even if an error is returned.
+ */
+static int keyctl_pkey_params_get(key_serial_t id,
+ const char __user *_info,
+ struct kernel_pkey_params *params)
+{
+ key_ref_t key_ref;
+ void *p;
+ int ret;
+
+ memset(params, 0, sizeof(*params));
+ params->encoding = "raw";
+
+ p = strndup_user(_info, PAGE_SIZE);
+ if (IS_ERR(p))
+ return PTR_ERR(p);
+ params->info = p;
+
+ ret = keyctl_pkey_params_parse(params);
+ if (ret < 0)
+ return ret;
+
+ key_ref = lookup_user_key(id, 0, KEY_NEED_SEARCH);
+ if (IS_ERR(key_ref))
+ return PTR_ERR(key_ref);
+ params->key = key_ref_to_ptr(key_ref);
+
+ if (!params->key->type->asym_query)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+/*
+ * Get parameters from userspace. Callers must always call the free function
+ * on params, even if an error is returned.
+ */
+static int keyctl_pkey_params_get_2(const struct keyctl_pkey_params __user *_params,
+ const char __user *_info,
+ int op,
+ struct kernel_pkey_params *params)
+{
+ struct keyctl_pkey_params uparams;
+ struct kernel_pkey_query info;
+ int ret;
+
+ memset(params, 0, sizeof(*params));
+ params->encoding = "raw";
+
+ if (copy_from_user(&uparams, _params, sizeof(uparams)) != 0)
+ return -EFAULT;
+
+ ret = keyctl_pkey_params_get(uparams.key_id, _info, params);
+ if (ret < 0)
+ return ret;
+
+ ret = params->key->type->asym_query(params, &info);
+ if (ret < 0)
+ return ret;
+
+ switch (op) {
+ case KEYCTL_PKEY_ENCRYPT:
+ case KEYCTL_PKEY_DECRYPT:
+ if (uparams.in_len > info.max_enc_size ||
+ uparams.out_len > info.max_dec_size)
+ return -EINVAL;
+ break;
+ case KEYCTL_PKEY_SIGN:
+ case KEYCTL_PKEY_VERIFY:
+ if (uparams.in_len > info.max_sig_size ||
+ uparams.out_len > info.max_data_size)
+ return -EINVAL;
+ break;
+ default:
+ BUG();
+ }
+
+ params->in_len = uparams.in_len;
+ params->out_len = uparams.out_len;
+ return 0;
+}
+
+/*
+ * Query information about an asymmetric key.
+ */
+long keyctl_pkey_query(key_serial_t id,
+ const char __user *_info,
+ struct keyctl_pkey_query __user *_res)
+{
+ struct kernel_pkey_params params;
+ struct kernel_pkey_query res;
+ long ret;
+
+ memset(&params, 0, sizeof(params));
+
+ ret = keyctl_pkey_params_get(id, _info, &params);
+ if (ret < 0)
+ goto error;
+
+ ret = params.key->type->asym_query(&params, &res);
+ if (ret < 0)
+ goto error;
+
+ ret = -EFAULT;
+ if (copy_to_user(_res, &res, sizeof(res)) == 0 &&
+ clear_user(_res->__spare, sizeof(_res->__spare)) == 0)
+ ret = 0;
+
+error:
+ keyctl_pkey_params_free(&params);
+ return ret;
+}
+
+/*
+ * Encrypt/decrypt/sign
+ *
+ * Encrypt data, decrypt data or sign data using a public key.
+ *
+ * _info is a string of supplementary information in key=val format. For
+ * instance, it might contain:
+ *
+ * "enc=pkcs1 hash=sha256"
+ *
+ * where enc= specifies the encoding and hash= selects the OID to go in that
+ * particular encoding if required. If enc= isn't supplied, it's assumed that
+ * the caller is supplying raw values.
+ *
+ * If successful, the amount of data written into the output buffer is
+ * returned.
+ */
+long keyctl_pkey_e_d_s(int op,
+ const struct keyctl_pkey_params __user *_params,
+ const char __user *_info,
+ const void __user *_in,
+ void __user *_out)
+{
+ struct kernel_pkey_params params;
+ void *in, *out;
+ long ret;
+
+ ret = keyctl_pkey_params_get_2(_params, _info, op, &params);
+ if (ret < 0)
+ goto error_params;
+
+ ret = -EOPNOTSUPP;
+ if (!params.key->type->asym_eds_op)
+ goto error_params;
+
+ switch (op) {
+ case KEYCTL_PKEY_ENCRYPT:
+ params.op = kernel_pkey_encrypt;
+ break;
+ case KEYCTL_PKEY_DECRYPT:
+ params.op = kernel_pkey_decrypt;
+ break;
+ case KEYCTL_PKEY_SIGN:
+ params.op = kernel_pkey_sign;
+ break;
+ default:
+ BUG();
+ }
+
+ in = memdup_user(_in, params.in_len);
+ if (IS_ERR(in)) {
+ ret = PTR_ERR(in);
+ goto error_params;
+ }
+
+ ret = -ENOMEM;
+ out = kmalloc(params.out_len, GFP_KERNEL);
+ if (!out)
+ goto error_in;
+
+ ret = params.key->type->asym_eds_op(&params, in, out);
+ if (ret < 0)
+ goto error_out;
+
+ if (copy_to_user(_out, out, ret) != 0)
+ ret = -EFAULT;
+
+error_out:
+ kfree(out);
+error_in:
+ kfree(in);
+error_params:
+ keyctl_pkey_params_free(&params);
+ return ret;
+}
+
+/*
+ * Verify a signature.
+ *
+ * Verify a public key signature using the given key, or if not given, search
+ * for a matching key.
+ *
+ * _info is a string of supplementary information in key=val format. For
+ * instance, it might contain:
+ *
+ * "enc=pkcs1 hash=sha256"
+ *
+ * where enc= specifies the signature blob encoding and hash= selects the OID
+ * to go in that particular encoding. If enc= isn't supplied, it's assumed
+ * that the caller is supplying raw values.
+ *
+ * If successful, 0 is returned.
+ */
+long keyctl_pkey_verify(const struct keyctl_pkey_params __user *_params,
+ const char __user *_info,
+ const void __user *_in,
+ const void __user *_in2)
+{
+ struct kernel_pkey_params params;
+ void *in, *in2;
+ long ret;
+
+ ret = keyctl_pkey_params_get_2(_params, _info, KEYCTL_PKEY_VERIFY,
+ &params);
+ if (ret < 0)
+ goto error_params;
+
+ ret = -EOPNOTSUPP;
+ if (!params.key->type->asym_verify_signature)
+ goto error_params;
+
+ in = memdup_user(_in, params.in_len);
+ if (IS_ERR(in)) {
+ ret = PTR_ERR(in);
+ goto error_params;
+ }
+
+ in2 = memdup_user(_in2, params.in2_len);
+ if (IS_ERR(in2)) {
+ ret = PTR_ERR(in2);
+ goto error_in;
+ }
+
+ params.op = kernel_pkey_verify;
+ ret = params.key->type->asym_verify_signature(&params, in, in2);
+
+ kfree(in2);
+error_in:
+ kfree(in);
+error_params:
+ keyctl_pkey_params_free(&params);
+ return ret;
+}


2018-09-05 21:56:22

by David Howells

[permalink] [raw]
Subject: [PATCH 03/22] KEYS: Provide missing asymmetric key subops for new key type ops

Provide the missing asymmetric key subops for new key type ops. This
include query, encrypt, decrypt and create signature. Verify signature
already exists. Also provided are accessor functions for this:

int query_asymmetric_key(const struct key *key,
struct kernel_pkey_query *info);

int encrypt_blob(struct kernel_pkey_params *params,
const void *data, void *enc);
int decrypt_blob(struct kernel_pkey_params *params,
const void *enc, void *data);
int create_signature(struct kernel_pkey_params *params,
const void *data, void *enc);

The public_key_signature struct gains an encoding field to carry the
encoding for verify_signature().

Signed-off-by: David Howells <[email protected]>
---

Documentation/crypto/asymmetric-keys.txt | 24 ++++++--
crypto/asymmetric_keys/asymmetric_keys.h | 3 +
crypto/asymmetric_keys/asymmetric_type.c | 43 ++++++++++++++
crypto/asymmetric_keys/signature.c | 95 ++++++++++++++++++++++++++++++
include/crypto/public_key.h | 13 +++-
include/keys/asymmetric-subtype.h | 9 +++
6 files changed, 180 insertions(+), 7 deletions(-)

diff --git a/Documentation/crypto/asymmetric-keys.txt b/Documentation/crypto/asymmetric-keys.txt
index 5969bf42562a..deb656ef008b 100644
--- a/Documentation/crypto/asymmetric-keys.txt
+++ b/Documentation/crypto/asymmetric-keys.txt
@@ -183,6 +183,10 @@ and looks like the following:

void (*describe)(const struct key *key, struct seq_file *m);
void (*destroy)(void *payload);
+ int (*query)(const struct kernel_pkey_params *params,
+ struct kernel_pkey_query *info);
+ int (*eds_op)(struct kernel_pkey_params *params,
+ const void *in, void *out);
int (*verify_signature)(const struct key *key,
const struct public_key_signature *sig);
};
@@ -207,12 +211,22 @@ There are a number of operations defined by the subtype:
asymmetric key will look after freeing the fingerprint and releasing the
reference on the subtype module.

- (3) verify_signature().
+ (3) query().

- Optional. These are the entry points for the key usage operations.
- Currently there is only the one defined. If not set, the caller will be
- given -ENOTSUPP. The subtype may do anything it likes to implement an
- operation, including offloading to hardware.
+ Mandatory. This is a function for querying the capabilities of a key.
+
+ (4) eds_op().
+
+ Optional. This is the entry point for the encryption, decryption and
+ signature creation operations (which are distinguished by the operation ID
+ in the parameter struct). The subtype may do anything it likes to
+ implement an operation, including offloading to hardware.
+
+ (5) verify_signature().
+
+ Optional. This is the entry point for signature verification. The
+ subtype may do anything it likes to implement an operation, including
+ offloading to hardware.


==========================
diff --git a/crypto/asymmetric_keys/asymmetric_keys.h b/crypto/asymmetric_keys/asymmetric_keys.h
index ca8e9ac34ce6..7be1ccf4fa9f 100644
--- a/crypto/asymmetric_keys/asymmetric_keys.h
+++ b/crypto/asymmetric_keys/asymmetric_keys.h
@@ -16,3 +16,6 @@ extern struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id);
extern int __asymmetric_key_hex_to_key_id(const char *id,
struct asymmetric_key_id *match_id,
size_t hexlen);
+
+extern int asymmetric_key_eds_op(struct kernel_pkey_params *params,
+ const void *in, void *out);
diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c
index 26539e9a8bda..69a0788a7de5 100644
--- a/crypto/asymmetric_keys/asymmetric_type.c
+++ b/crypto/asymmetric_keys/asymmetric_type.c
@@ -18,6 +18,7 @@
#include <linux/slab.h>
#include <linux/ctype.h>
#include <keys/system_keyring.h>
+#include <keys/user-type.h>
#include "asymmetric_keys.h"

MODULE_LICENSE("GPL");
@@ -538,6 +539,45 @@ static struct key_restriction *asymmetric_lookup_restriction(
return ret;
}

+int asymmetric_key_eds_op(struct kernel_pkey_params *params,
+ const void *in, void *out)
+{
+ const struct asymmetric_key_subtype *subtype;
+ struct key *key = params->key;
+ int ret;
+
+ pr_devel("==>%s()\n", __func__);
+
+ if (key->type != &key_type_asymmetric)
+ return -EINVAL;
+ subtype = asymmetric_key_subtype(key);
+ if (!subtype ||
+ !key->payload.data[0])
+ return -EINVAL;
+ if (!subtype->eds_op)
+ return -ENOTSUPP;
+
+ ret = subtype->eds_op(params, in, out);
+
+ pr_devel("<==%s() = %d\n", __func__, ret);
+ return ret;
+}
+
+static int asymmetric_key_verify_signature(struct kernel_pkey_params *params,
+ const void *in, const void *in2)
+{
+ struct public_key_signature sig = {
+ .s_size = params->in2_len,
+ .digest_size = params->in_len,
+ .encoding = params->encoding,
+ .hash_algo = params->hash_algo,
+ .digest = (void *)in,
+ .s = (void *)in2,
+ };
+
+ return verify_signature(params->key, &sig);
+}
+
struct key_type key_type_asymmetric = {
.name = "asymmetric",
.preparse = asymmetric_key_preparse,
@@ -548,6 +588,9 @@ struct key_type key_type_asymmetric = {
.destroy = asymmetric_key_destroy,
.describe = asymmetric_key_describe,
.lookup_restriction = asymmetric_lookup_restriction,
+ .asym_query = query_asymmetric_key,
+ .asym_eds_op = asymmetric_key_eds_op,
+ .asym_verify_signature = asymmetric_key_verify_signature,
};
EXPORT_SYMBOL_GPL(key_type_asymmetric);

diff --git a/crypto/asymmetric_keys/signature.c b/crypto/asymmetric_keys/signature.c
index 28198314bc39..ad95a58c6642 100644
--- a/crypto/asymmetric_keys/signature.c
+++ b/crypto/asymmetric_keys/signature.c
@@ -16,7 +16,9 @@
#include <linux/export.h>
#include <linux/err.h>
#include <linux/slab.h>
+#include <linux/keyctl.h>
#include <crypto/public_key.h>
+#include <keys/user-type.h>
#include "asymmetric_keys.h"

/*
@@ -36,6 +38,99 @@ void public_key_signature_free(struct public_key_signature *sig)
}
EXPORT_SYMBOL_GPL(public_key_signature_free);

+/**
+ * query_asymmetric_key - Get information about an aymmetric key.
+ * @params: Various parameters.
+ * @info: Where to put the information.
+ */
+int query_asymmetric_key(const struct kernel_pkey_params *params,
+ struct kernel_pkey_query *info)
+{
+ const struct asymmetric_key_subtype *subtype;
+ struct key *key = params->key;
+ int ret;
+
+ pr_devel("==>%s()\n", __func__);
+
+ if (key->type != &key_type_asymmetric)
+ return -EINVAL;
+ subtype = asymmetric_key_subtype(key);
+ if (!subtype ||
+ !key->payload.data[0])
+ return -EINVAL;
+ if (!subtype->query)
+ return -ENOTSUPP;
+
+ ret = subtype->query(params, info);
+
+ pr_devel("<==%s() = %d\n", __func__, ret);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(query_asymmetric_key);
+
+/**
+ * encrypt_blob - Encrypt data using an asymmetric key
+ * @params: Various parameters
+ * @data: Data blob to be encrypted, length params->data_len
+ * @enc: Encrypted data buffer, length params->enc_len
+ *
+ * Encrypt the specified data blob using the private key specified by
+ * params->key. The encrypted data is wrapped in an encoding if
+ * params->encoding is specified (eg. "pkcs1").
+ *
+ * Returns the length of the data placed in the encrypted data buffer or an
+ * error.
+ */
+int encrypt_blob(struct kernel_pkey_params *params,
+ const void *data, void *enc)
+{
+ params->op = kernel_pkey_encrypt;
+ return asymmetric_key_eds_op(params, data, enc);
+}
+EXPORT_SYMBOL_GPL(encrypt_blob);
+
+/**
+ * decrypt_blob - Decrypt data using an asymmetric key
+ * @params: Various parameters
+ * @enc: Encrypted data to be decrypted, length params->enc_len
+ * @data: Decrypted data buffer, length params->data_len
+ *
+ * Decrypt the specified data blob using the private key specified by
+ * params->key. The decrypted data is wrapped in an encoding if
+ * params->encoding is specified (eg. "pkcs1").
+ *
+ * Returns the length of the data placed in the decrypted data buffer or an
+ * error.
+ */
+int decrypt_blob(struct kernel_pkey_params *params,
+ const void *enc, void *data)
+{
+ params->op = kernel_pkey_decrypt;
+ return asymmetric_key_eds_op(params, enc, data);
+}
+EXPORT_SYMBOL_GPL(decrypt_blob);
+
+/**
+ * create_signature - Sign some data using an asymmetric key
+ * @params: Various parameters
+ * @data: Data blob to be signed, length params->data_len
+ * @enc: Signature buffer, length params->enc_len
+ *
+ * Sign the specified data blob using the private key specified by params->key.
+ * The signature is wrapped in an encoding if params->encoding is specified
+ * (eg. "pkcs1"). If the encoding needs to know the digest type, this can be
+ * passed through params->hash_algo (eg. "sha1").
+ *
+ * Returns the length of the data placed in the signature buffer or an error.
+ */
+int create_signature(struct kernel_pkey_params *params,
+ const void *data, void *enc)
+{
+ params->op = kernel_pkey_sign;
+ return asymmetric_key_eds_op(params, data, enc);
+}
+EXPORT_SYMBOL_GPL(create_signature);
+
/**
* verify_signature - Initiate the use of an asymmetric key to verify a signature
* @key: The asymmetric key to verify against
diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h
index e0b681a717ba..3a1047a0195c 100644
--- a/include/crypto/public_key.h
+++ b/include/crypto/public_key.h
@@ -14,6 +14,8 @@
#ifndef _LINUX_PUBLIC_KEY_H
#define _LINUX_PUBLIC_KEY_H

+#include <linux/keyctl.h>
+
/*
* Cryptographic data for the public-key subtype of the asymmetric key type.
*
@@ -40,6 +42,7 @@ struct public_key_signature {
u8 digest_size; /* Number of bytes in digest */
const char *pkey_algo;
const char *hash_algo;
+ const char *encoding;
};

extern void public_key_signature_free(struct public_key_signature *sig);
@@ -65,8 +68,14 @@ extern int restrict_link_by_key_or_keyring_chain(struct key *trust_keyring,
const union key_payload *payload,
struct key *trusted);

-extern int verify_signature(const struct key *key,
- const struct public_key_signature *sig);
+extern int query_asymmetric_key(const struct kernel_pkey_params *,
+ struct kernel_pkey_query *);
+
+extern int encrypt_blob(struct kernel_pkey_params *, const void *, void *);
+extern int decrypt_blob(struct kernel_pkey_params *, const void *, void *);
+extern int create_signature(struct kernel_pkey_params *, const void *, void *);
+extern int verify_signature(const struct key *,
+ const struct public_key_signature *);

int public_key_verify_signature(const struct public_key *pkey,
const struct public_key_signature *sig);
diff --git a/include/keys/asymmetric-subtype.h b/include/keys/asymmetric-subtype.h
index e0a9c2368872..9ce2f0fae57e 100644
--- a/include/keys/asymmetric-subtype.h
+++ b/include/keys/asymmetric-subtype.h
@@ -17,6 +17,8 @@
#include <linux/seq_file.h>
#include <keys/asymmetric-type.h>

+struct kernel_pkey_query;
+struct kernel_pkey_params;
struct public_key_signature;

/*
@@ -34,6 +36,13 @@ struct asymmetric_key_subtype {
/* Destroy a key of this subtype */
void (*destroy)(void *payload_crypto, void *payload_auth);

+ int (*query)(const struct kernel_pkey_params *params,
+ struct kernel_pkey_query *info);
+
+ /* Encrypt/decrypt/sign data */
+ int (*eds_op)(struct kernel_pkey_params *params,
+ const void *in, void *out);
+
/* Verify the signature on a key of this subtype (optional) */
int (*verify_signature)(const struct key *key,
const struct public_key_signature *sig);


2018-09-05 21:56:36

by David Howells

[permalink] [raw]
Subject: [PATCH 05/22] KEYS: Provide software public key query function

Provide a query function for the software public key implementation. This
permits information about such a key to be obtained using
query_asymmetric_key() or KEYCTL_PKEY_QUERY.

Signed-off-by: David Howells <[email protected]>
---

crypto/asymmetric_keys/public_key.c | 99 +++++++++++++++++++++++++++++------
1 file changed, 82 insertions(+), 17 deletions(-)

diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c
index e929fe1e4106..761bfab352dc 100644
--- a/crypto/asymmetric_keys/public_key.c
+++ b/crypto/asymmetric_keys/public_key.c
@@ -59,6 +59,81 @@ static void public_key_destroy(void *payload0, void *payload3)
public_key_signature_free(payload3);
}

+/*
+ * Determine the crypto algorithm name.
+ */
+static
+int software_key_determine_akcipher(const char *encoding,
+ const char *hash_algo,
+ const struct public_key *pkey,
+ char alg_name[CRYPTO_MAX_ALG_NAME])
+{
+ int n;
+
+ if (strcmp(encoding, "pkcs1") == 0) {
+ /* The data wangled by the RSA algorithm is typically padded
+ * and encoded in some manner, such as EMSA-PKCS1-1_5 [RFC3447
+ * sec 8.2].
+ */
+ if (!hash_algo)
+ n = snprintf(alg_name, CRYPTO_MAX_ALG_NAME,
+ "pkcs1pad(%s)",
+ pkey->pkey_algo);
+ else
+ n = snprintf(alg_name, CRYPTO_MAX_ALG_NAME,
+ "pkcs1pad(%s,%s)",
+ pkey->pkey_algo, hash_algo);
+ return n >= CRYPTO_MAX_ALG_NAME ? -EINVAL : 0;
+ }
+
+ if (strcmp(encoding, "raw") == 0) {
+ strcpy(alg_name, pkey->pkey_algo);
+ return 0;
+ }
+
+ return -ENOPKG;
+}
+
+/*
+ * Query information about a key.
+ */
+static int software_key_query(const struct kernel_pkey_params *params,
+ struct kernel_pkey_query *info)
+{
+ struct crypto_akcipher *tfm;
+ struct public_key *pkey = params->key->payload.data[asym_crypto];
+ char alg_name[CRYPTO_MAX_ALG_NAME];
+ int ret, len;
+
+ ret = software_key_determine_akcipher(params->encoding,
+ params->hash_algo,
+ pkey, alg_name);
+ if (ret < 0)
+ return ret;
+
+ tfm = crypto_alloc_akcipher(alg_name, 0, 0);
+ if (IS_ERR(tfm))
+ return PTR_ERR(tfm);
+
+ ret = crypto_akcipher_set_pub_key(tfm, pkey->key, pkey->keylen);
+ if (ret < 0)
+ goto error_free_tfm;
+
+ len = crypto_akcipher_maxsize(tfm);
+ info->key_size = len * 8;
+ info->max_data_size = len;
+ info->max_sig_size = len;
+ info->max_enc_size = len;
+ info->max_dec_size = len;
+ info->supported_ops = KEYCTL_SUPPORTS_VERIFY;
+ ret = 0;
+
+error_free_tfm:
+ crypto_free_akcipher(tfm);
+ pr_devel("<==%s() = %d\n", __func__, ret);
+ return ret;
+}
+
/*
* Verify a signature using a public key.
*/
@@ -69,8 +144,7 @@ int public_key_verify_signature(const struct public_key *pkey,
struct crypto_akcipher *tfm;
struct akcipher_request *req;
struct scatterlist sig_sg, digest_sg;
- const char *alg_name;
- char alg_name_buf[CRYPTO_MAX_ALG_NAME];
+ char alg_name[CRYPTO_MAX_ALG_NAME];
void *output;
unsigned int outlen;
int ret;
@@ -81,21 +155,11 @@ int public_key_verify_signature(const struct public_key *pkey,
BUG_ON(!sig);
BUG_ON(!sig->s);

- if (!sig->digest)
- return -ENOPKG;
-
- alg_name = sig->pkey_algo;
- if (strcmp(sig->pkey_algo, "rsa") == 0) {
- /* The data wangled by the RSA algorithm is typically padded
- * and encoded in some manner, such as EMSA-PKCS1-1_5 [RFC3447
- * sec 8.2].
- */
- if (snprintf(alg_name_buf, CRYPTO_MAX_ALG_NAME,
- "pkcs1pad(rsa,%s)", sig->hash_algo
- ) >= CRYPTO_MAX_ALG_NAME)
- return -EINVAL;
- alg_name = alg_name_buf;
- }
+ ret = software_key_determine_akcipher(sig->encoding,
+ sig->hash_algo,
+ pkey, alg_name);
+ if (ret < 0)
+ return ret;

tfm = crypto_alloc_akcipher(alg_name, 0, 0);
if (IS_ERR(tfm))
@@ -167,6 +231,7 @@ struct asymmetric_key_subtype public_key_subtype = {
.name_len = sizeof("public_key") - 1,
.describe = public_key_describe,
.destroy = public_key_destroy,
+ .query = software_key_query,
.verify_signature = public_key_verify_signature_2,
};
EXPORT_SYMBOL_GPL(public_key_subtype);


2018-09-05 21:56:50

by David Howells

[permalink] [raw]
Subject: [PATCH 06/22] KEYS: Allow the public_key struct to hold a private key

Put a flag in the public_key struct to indicate if the structure is holding
a private key. The private key must be held ASN.1 encoded in the format
specified in RFC 3447 A.1.2. This is the form required by crypto/rsa.c.

The software encryption subtype's verification and query functions then
need to select the appropriate crypto function to set the key.

Signed-off-by: David Howells <[email protected]>
---

crypto/asymmetric_keys/public_key.c | 14 ++++++++++++--
include/crypto/public_key.h | 1 +
2 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c
index 761bfab352dc..f2dc27897319 100644
--- a/crypto/asymmetric_keys/public_key.c
+++ b/crypto/asymmetric_keys/public_key.c
@@ -115,7 +115,12 @@ static int software_key_query(const struct kernel_pkey_params *params,
if (IS_ERR(tfm))
return PTR_ERR(tfm);

- ret = crypto_akcipher_set_pub_key(tfm, pkey->key, pkey->keylen);
+ if (pkey->key_is_private)
+ ret = crypto_akcipher_set_priv_key(tfm,
+ pkey->key, pkey->keylen);
+ else
+ ret = crypto_akcipher_set_pub_key(tfm,
+ pkey->key, pkey->keylen);
if (ret < 0)
goto error_free_tfm;

@@ -170,7 +175,12 @@ int public_key_verify_signature(const struct public_key *pkey,
if (!req)
goto error_free_tfm;

- ret = crypto_akcipher_set_pub_key(tfm, pkey->key, pkey->keylen);
+ if (pkey->key_is_private)
+ ret = crypto_akcipher_set_priv_key(tfm,
+ pkey->key, pkey->keylen);
+ else
+ ret = crypto_akcipher_set_pub_key(tfm,
+ pkey->key, pkey->keylen);
if (ret)
goto error_free_req;

diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h
index 3a1047a0195c..be626eac9113 100644
--- a/include/crypto/public_key.h
+++ b/include/crypto/public_key.h
@@ -25,6 +25,7 @@
struct public_key {
void *key;
u32 keylen;
+ bool key_is_private;
const char *id_type;
const char *pkey_algo;
};


2018-09-05 21:56:54

by David Howells

[permalink] [raw]
Subject: [PATCH 07/22] KEYS: Implement encrypt, decrypt and sign for software asymmetric key

Implement the encrypt, decrypt and sign operations for the software
asymmetric key subtype. This mostly involves offloading the call to the
crypto layer.

Note that the decrypt and sign operations require a private key to be
supplied. Encrypt (and also verify) will work with either a public or a
private key. A public key can be supplied with an X.509 certificate and a
private key can be supplied using a PKCS#8 blob:

# j=`openssl pkcs8 -in ~/pkcs7/firmwarekey2.priv -topk8 -nocrypt -outform DER | keyctl padd asymmetric foo @s`
# keyctl pkey_query $j - enc=pkcs1
key_size=4096
max_data_size=512
max_sig_size=512
max_enc_size=512
max_dec_size=512
encrypt=y
decrypt=y
sign=y
verify=y
# keyctl pkey_encrypt $j 0 data enc=pkcs1 >/tmp/enc
# keyctl pkey_decrypt $j 0 /tmp/enc enc=pkcs1 >/tmp/dec
# cmp data /tmp/dec
# keyctl pkey_sign $j 0 data enc=pkcs1 hash=sha1 >/tmp/sig
# keyctl pkey_verify $j 0 data /tmp/sig enc=pkcs1 hash=sha1
#

Signed-off-by: David Howells <[email protected]>
---

crypto/asymmetric_keys/public_key.c | 82 +++++++++++++++++++++++++++++++++++
1 file changed, 81 insertions(+), 1 deletion(-)

diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c
index f2dc27897319..f5d85b47fcc6 100644
--- a/crypto/asymmetric_keys/public_key.c
+++ b/crypto/asymmetric_keys/public_key.c
@@ -130,7 +130,11 @@ static int software_key_query(const struct kernel_pkey_params *params,
info->max_sig_size = len;
info->max_enc_size = len;
info->max_dec_size = len;
- info->supported_ops = KEYCTL_SUPPORTS_VERIFY;
+ info->supported_ops = (KEYCTL_SUPPORTS_ENCRYPT |
+ KEYCTL_SUPPORTS_VERIFY);
+ if (pkey->key_is_private)
+ info->supported_ops |= (KEYCTL_SUPPORTS_DECRYPT |
+ KEYCTL_SUPPORTS_SIGN);
ret = 0;

error_free_tfm:
@@ -139,6 +143,81 @@ static int software_key_query(const struct kernel_pkey_params *params,
return ret;
}

+/*
+ * Do encryption, decryption and signing ops.
+ */
+static int software_key_eds_op(struct kernel_pkey_params *params,
+ const void *in, void *out)
+{
+ const struct public_key *pkey = params->key->payload.data[asym_crypto];
+ struct akcipher_request *req;
+ struct crypto_akcipher *tfm;
+ struct crypto_wait cwait;
+ struct scatterlist in_sg, out_sg;
+ char alg_name[CRYPTO_MAX_ALG_NAME];
+ int ret;
+
+ pr_devel("==>%s()\n", __func__);
+
+ ret = software_key_determine_akcipher(params->encoding,
+ params->hash_algo,
+ pkey, alg_name);
+ if (ret < 0)
+ return ret;
+
+ tfm = crypto_alloc_akcipher(alg_name, 0, 0);
+ if (IS_ERR(tfm))
+ return PTR_ERR(tfm);
+
+ req = akcipher_request_alloc(tfm, GFP_KERNEL);
+ if (!req)
+ goto error_free_tfm;
+
+ if (pkey->key_is_private)
+ ret = crypto_akcipher_set_priv_key(tfm,
+ pkey->key, pkey->keylen);
+ else
+ ret = crypto_akcipher_set_pub_key(tfm,
+ pkey->key, pkey->keylen);
+ if (ret)
+ goto error_free_req;
+
+ sg_init_one(&in_sg, in, params->in_len);
+ sg_init_one(&out_sg, out, params->out_len);
+ akcipher_request_set_crypt(req, &in_sg, &out_sg, params->in_len,
+ params->out_len);
+ crypto_init_wait(&cwait);
+ akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
+ CRYPTO_TFM_REQ_MAY_SLEEP,
+ crypto_req_done, &cwait);
+
+ /* Perform the encryption calculation. */
+ switch (params->op) {
+ case kernel_pkey_encrypt:
+ ret = crypto_akcipher_encrypt(req);
+ break;
+ case kernel_pkey_decrypt:
+ ret = crypto_akcipher_decrypt(req);
+ break;
+ case kernel_pkey_sign:
+ ret = crypto_akcipher_sign(req);
+ break;
+ default:
+ BUG();
+ }
+
+ ret = crypto_wait_req(ret, &cwait);
+ if (ret == 0)
+ ret = req->dst_len;
+
+error_free_req:
+ akcipher_request_free(req);
+error_free_tfm:
+ crypto_free_akcipher(tfm);
+ pr_devel("<==%s() = %d\n", __func__, ret);
+ return ret;
+}
+
/*
* Verify a signature using a public key.
*/
@@ -242,6 +321,7 @@ struct asymmetric_key_subtype public_key_subtype = {
.describe = public_key_describe,
.destroy = public_key_destroy,
.query = software_key_query,
+ .eds_op = software_key_eds_op,
.verify_signature = public_key_verify_signature_2,
};
EXPORT_SYMBOL_GPL(public_key_subtype);


2018-09-05 21:57:10

by David Howells

[permalink] [raw]
Subject: [PATCH 08/22] KEYS: Implement PKCS#8 RSA Private Key parser

Implement PKCS#8 RSA Private Key format [RFC 5208] parser for the
asymmetric key type. For the moment, this will only support unencrypted
DER blobs. PEM and decryption can be added later.

PKCS#8 keys can be loaded like this:

openssl pkcs8 -in private_key.pem -topk8 -nocrypt -outform DER | \
keyctl padd asymmetric foo @s

Signed-off-by: David Howells <[email protected]>
---

Documentation/crypto/asymmetric-keys.txt | 2
crypto/asymmetric_keys/Kconfig | 10 ++
crypto/asymmetric_keys/Makefile | 13 ++
crypto/asymmetric_keys/pkcs8.asn1 | 24 ++++
crypto/asymmetric_keys/pkcs8_parser.c | 184 ++++++++++++++++++++++++++++++
5 files changed, 233 insertions(+)
create mode 100644 crypto/asymmetric_keys/pkcs8.asn1
create mode 100644 crypto/asymmetric_keys/pkcs8_parser.c

diff --git a/Documentation/crypto/asymmetric-keys.txt b/Documentation/crypto/asymmetric-keys.txt
index deb656ef008b..8763866b11cf 100644
--- a/Documentation/crypto/asymmetric-keys.txt
+++ b/Documentation/crypto/asymmetric-keys.txt
@@ -248,6 +248,8 @@ Examples of blob formats for which parsers could be implemented include:
- X.509 ASN.1 stream.
- Pointer to TPM key.
- Pointer to UEFI key.
+ - PKCS#8 private key [RFC 5208].
+ - PKCS#5 encrypted private key [RFC 2898].

During key instantiation each parser in the list is tried until one doesn't
return -EBADMSG.
diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
index f3702e533ff4..66a7dad7ed3d 100644
--- a/crypto/asymmetric_keys/Kconfig
+++ b/crypto/asymmetric_keys/Kconfig
@@ -31,6 +31,16 @@ config X509_CERTIFICATE_PARSER
data and provides the ability to instantiate a crypto key from a
public key packet found inside the certificate.

+config PKCS8_PRIVATE_KEY_PARSER
+ tristate "PKCS#8 private key parser"
+ depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+ select ASN1
+ select OID_REGISTRY
+ help
+ This option provides support for parsing PKCS#8 format blobs for
+ private key data and provides the ability to instantiate a crypto key
+ from that data.
+
config PKCS7_MESSAGE_PARSER
tristate "PKCS#7 message parser"
depends on X509_CERTIFICATE_PARSER
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index d4b2e1b2dc65..c38424f55b08 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -29,6 +29,19 @@ $(obj)/x509_cert_parser.o: \
$(obj)/x509.asn1.o: $(obj)/x509.asn1.c $(obj)/x509.asn1.h
$(obj)/x509_akid.asn1.o: $(obj)/x509_akid.asn1.c $(obj)/x509_akid.asn1.h

+#
+# PKCS#8 private key handling
+#
+obj-$(CONFIG_PKCS8_PRIVATE_KEY_PARSER) += pkcs8_key_parser.o
+pkcs8_key_parser-y := \
+ pkcs8.asn1.o \
+ pkcs8_parser.o
+
+$(obj)/pkcs8_parser.o: $(obj)/pkcs8.asn1.h
+$(obj)/pkcs8-asn1.o: $(obj)/pkcs8.asn1.c $(obj)/pkcs8.asn1.h
+
+clean-files += pkcs8.asn1.c pkcs8.asn1.h
+
#
# PKCS#7 message handling
#
diff --git a/crypto/asymmetric_keys/pkcs8.asn1 b/crypto/asymmetric_keys/pkcs8.asn1
new file mode 100644
index 000000000000..702c41a3c713
--- /dev/null
+++ b/crypto/asymmetric_keys/pkcs8.asn1
@@ -0,0 +1,24 @@
+--
+-- This is the unencrypted variant
+--
+PrivateKeyInfo ::= SEQUENCE {
+ version Version,
+ privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
+ privateKey PrivateKey,
+ attributes [0] IMPLICIT Attributes OPTIONAL
+}
+
+Version ::= INTEGER ({ pkcs8_note_version })
+
+PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier ({ pkcs8_note_algo })
+
+PrivateKey ::= OCTET STRING ({ pkcs8_note_key })
+
+Attributes ::= SET OF Attribute
+
+Attribute ::= ANY
+
+AlgorithmIdentifier ::= SEQUENCE {
+ algorithm OBJECT IDENTIFIER ({ pkcs8_note_OID }),
+ parameters ANY OPTIONAL
+}
diff --git a/crypto/asymmetric_keys/pkcs8_parser.c b/crypto/asymmetric_keys/pkcs8_parser.c
new file mode 100644
index 000000000000..5f6a7ecc9765
--- /dev/null
+++ b/crypto/asymmetric_keys/pkcs8_parser.c
@@ -0,0 +1,184 @@
+/* PKCS#8 Private Key parser [RFC 5208].
+ *
+ * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) "PKCS8: "fmt
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/oid_registry.h>
+#include <keys/asymmetric-subtype.h>
+#include <keys/asymmetric-parser.h>
+#include <crypto/public_key.h>
+#include "pkcs8.asn1.h"
+
+struct pkcs8_parse_context {
+ struct public_key *pub;
+ unsigned long data; /* Start of data */
+ enum OID last_oid; /* Last OID encountered */
+ enum OID algo_oid; /* Algorithm OID */
+ u32 key_size;
+ const void *key;
+};
+
+/*
+ * Note an OID when we find one for later processing when we know how to
+ * interpret it.
+ */
+int pkcs8_note_OID(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pkcs8_parse_context *ctx = context;
+
+ ctx->last_oid = look_up_OID(value, vlen);
+ if (ctx->last_oid == OID__NR) {
+ char buffer[50];
+
+ sprint_oid(value, vlen, buffer, sizeof(buffer));
+ pr_info("Unknown OID: [%lu] %s\n",
+ (unsigned long)value - ctx->data, buffer);
+ }
+ return 0;
+}
+
+/*
+ * Note the version number of the ASN.1 blob.
+ */
+int pkcs8_note_version(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ if (vlen != 1 || ((const u8 *)value)[0] != 0) {
+ pr_warn("Unsupported PKCS#8 version\n");
+ return -EBADMSG;
+ }
+ return 0;
+}
+
+/*
+ * Note the public algorithm.
+ */
+int pkcs8_note_algo(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pkcs8_parse_context *ctx = context;
+
+ if (ctx->last_oid != OID_rsaEncryption)
+ return -ENOPKG;
+
+ ctx->pub->pkey_algo = "rsa";
+ return 0;
+}
+
+/*
+ * Note the key data of the ASN.1 blob.
+ */
+int pkcs8_note_key(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pkcs8_parse_context *ctx = context;
+
+ ctx->key = value;
+ ctx->key_size = vlen;
+ return 0;
+}
+
+/*
+ * Parse a PKCS#8 private key blob.
+ */
+static struct public_key *pkcs8_parse(const void *data, size_t datalen)
+{
+ struct pkcs8_parse_context ctx;
+ struct public_key *pub;
+ long ret;
+
+ memset(&ctx, 0, sizeof(ctx));
+
+ ret = -ENOMEM;
+ ctx.pub = kzalloc(sizeof(struct public_key), GFP_KERNEL);
+ if (!ctx.pub)
+ goto error;
+
+ ctx.data = (unsigned long)data;
+
+ /* Attempt to decode the private key */
+ ret = asn1_ber_decoder(&pkcs8_decoder, &ctx, data, datalen);
+ if (ret < 0)
+ goto error_decode;
+
+ ret = -ENOMEM;
+ pub = ctx.pub;
+ pub->key = kmemdup(ctx.key, ctx.key_size, GFP_KERNEL);
+ if (!pub->key)
+ goto error_decode;
+
+ pub->keylen = ctx.key_size;
+ pub->key_is_private = true;
+ return pub;
+
+error_decode:
+ kfree(ctx.pub);
+error:
+ return ERR_PTR(ret);
+}
+
+/*
+ * Attempt to parse a data blob for a key as a PKCS#8 private key.
+ */
+static int pkcs8_key_preparse(struct key_preparsed_payload *prep)
+{
+ struct public_key *pub;
+
+ pub = pkcs8_parse(prep->data, prep->datalen);
+ if (IS_ERR(pub))
+ return PTR_ERR(pub);
+
+ pr_devel("Cert Key Algo: %s\n", pub->pkey_algo);
+ pub->id_type = "PKCS8";
+
+ /* We're pinning the module by being linked against it */
+ __module_get(public_key_subtype.owner);
+ prep->payload.data[asym_subtype] = &public_key_subtype;
+ prep->payload.data[asym_key_ids] = NULL;
+ prep->payload.data[asym_crypto] = pub;
+ prep->payload.data[asym_auth] = NULL;
+ prep->quotalen = 100;
+ return 0;
+}
+
+static struct asymmetric_key_parser pkcs8_key_parser = {
+ .owner = THIS_MODULE,
+ .name = "pkcs8",
+ .parse = pkcs8_key_preparse,
+};
+
+/*
+ * Module stuff
+ */
+static int __init pkcs8_key_init(void)
+{
+ return register_asymmetric_key_parser(&pkcs8_key_parser);
+}
+
+static void __exit pkcs8_key_exit(void)
+{
+ unregister_asymmetric_key_parser(&pkcs8_key_parser);
+}
+
+module_init(pkcs8_key_init);
+module_exit(pkcs8_key_exit);
+
+MODULE_DESCRIPTION("PKCS#8 certificate parser");
+MODULE_LICENSE("GPL");


2018-09-05 21:57:12

by David Howells

[permalink] [raw]
Subject: [PATCH 09/22] crypto: rsa-pkcs1pad: Allow hash to be optional

From: Denis Kenzior <[email protected]>

The original pkcs1pad implementation allowed to pad/unpad raw RSA
output. However, this has been taken out in commit:
commit c0d20d22e0ad ("crypto: rsa-pkcs1pad - Require hash to be present")

This patch restored this ability as it is needed by the asymmetric key
implementation.

Signed-off-by: Denis Kenzior <[email protected]>
Signed-off-by: David Howells <[email protected]>
---

crypto/rsa-pkcs1pad.c | 59 ++++++++++++++++++++++++++++++++++---------------
1 file changed, 41 insertions(+), 18 deletions(-)

diff --git a/crypto/rsa-pkcs1pad.c b/crypto/rsa-pkcs1pad.c
index 9893dbfc1af4..27b1e2bd4bfb 100644
--- a/crypto/rsa-pkcs1pad.c
+++ b/crypto/rsa-pkcs1pad.c
@@ -401,7 +401,8 @@ static int pkcs1pad_sign(struct akcipher_request *req)
if (!ctx->key_size)
return -EINVAL;

- digest_size = digest_info->size;
+ if (digest_info)
+ digest_size = digest_info->size;

if (req->src_len + digest_size > ctx->key_size - 11)
return -EOVERFLOW;
@@ -421,8 +422,9 @@ static int pkcs1pad_sign(struct akcipher_request *req)
memset(req_ctx->in_buf + 1, 0xff, ps_end - 1);
req_ctx->in_buf[ps_end] = 0x00;

- memcpy(req_ctx->in_buf + ps_end + 1, digest_info->data,
- digest_info->size);
+ if (digest_info)
+ memcpy(req_ctx->in_buf + ps_end + 1, digest_info->data,
+ digest_info->size);

pkcs1pad_sg_set_buf(req_ctx->in_sg, req_ctx->in_buf,
ctx->key_size - 1 - req->src_len, req->src);
@@ -484,10 +486,13 @@ static int pkcs1pad_verify_complete(struct akcipher_request *req, int err)
goto done;
pos++;

- if (crypto_memneq(out_buf + pos, digest_info->data, digest_info->size))
- goto done;
+ if (digest_info) {
+ if (crypto_memneq(out_buf + pos, digest_info->data,
+ digest_info->size))
+ goto done;

- pos += digest_info->size;
+ pos += digest_info->size;
+ }

err = 0;

@@ -617,11 +622,14 @@ static int pkcs1pad_create(struct crypto_template *tmpl, struct rtattr **tb)

hash_name = crypto_attr_alg_name(tb[2]);
if (IS_ERR(hash_name))
- return PTR_ERR(hash_name);
+ hash_name = NULL;

- digest_info = rsa_lookup_asn1(hash_name);
- if (!digest_info)
- return -EINVAL;
+ if (hash_name) {
+ digest_info = rsa_lookup_asn1(hash_name);
+ if (!digest_info)
+ return -EINVAL;
+ } else
+ digest_info = NULL;

inst = kzalloc(sizeof(*inst) + sizeof(*ctx), GFP_KERNEL);
if (!inst)
@@ -641,14 +649,29 @@ static int pkcs1pad_create(struct crypto_template *tmpl, struct rtattr **tb)

err = -ENAMETOOLONG;

- if (snprintf(inst->alg.base.cra_name, CRYPTO_MAX_ALG_NAME,
- "pkcs1pad(%s,%s)", rsa_alg->base.cra_name, hash_name) >=
- CRYPTO_MAX_ALG_NAME ||
- snprintf(inst->alg.base.cra_driver_name, CRYPTO_MAX_ALG_NAME,
- "pkcs1pad(%s,%s)",
- rsa_alg->base.cra_driver_name, hash_name) >=
- CRYPTO_MAX_ALG_NAME)
- goto out_drop_alg;
+ if (!hash_name) {
+ if (snprintf(inst->alg.base.cra_name,
+ CRYPTO_MAX_ALG_NAME, "pkcs1pad(%s)",
+ rsa_alg->base.cra_name) >= CRYPTO_MAX_ALG_NAME)
+ goto out_drop_alg;
+
+ if (snprintf(inst->alg.base.cra_driver_name,
+ CRYPTO_MAX_ALG_NAME, "pkcs1pad(%s)",
+ rsa_alg->base.cra_driver_name) >=
+ CRYPTO_MAX_ALG_NAME)
+ goto out_drop_alg;
+ } else {
+ if (snprintf(inst->alg.base.cra_name, CRYPTO_MAX_ALG_NAME,
+ "pkcs1pad(%s,%s)", rsa_alg->base.cra_name,
+ hash_name) >= CRYPTO_MAX_ALG_NAME)
+ goto out_drop_alg;
+
+ if (snprintf(inst->alg.base.cra_driver_name,
+ CRYPTO_MAX_ALG_NAME, "pkcs1pad(%s,%s)",
+ rsa_alg->base.cra_driver_name,
+ hash_name) >= CRYPTO_MAX_ALG_NAME)
+ goto out_drop_alg;
+ }

inst->alg.base.cra_flags = rsa_alg->base.cra_flags & CRYPTO_ALG_ASYNC;
inst->alg.base.cra_priority = rsa_alg->base.cra_priority;


2018-09-05 21:57:16

by David Howells

[permalink] [raw]
Subject: [PATCH 10/22] KEYS: asym_tpm: add skeleton for asym_tpm

From: Denis Kenzior <[email protected]>

This patch adds the basic skeleton for the asym_tpm asymmetric key
subtype.

Signed-off-by: Denis Kenzior <[email protected]>
Signed-off-by: David Howells <[email protected]>
---

crypto/asymmetric_keys/Kconfig | 11 +++++
crypto/asymmetric_keys/Makefile | 1
crypto/asymmetric_keys/asym_tpm.c | 90 +++++++++++++++++++++++++++++++++++++
include/crypto/asym_tpm_subtype.h | 16 +++++++
4 files changed, 118 insertions(+)
create mode 100644 crypto/asymmetric_keys/asym_tpm.c
create mode 100644 include/crypto/asym_tpm_subtype.h

diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
index 66a7dad7ed3d..b75555c7d8ae 100644
--- a/crypto/asymmetric_keys/Kconfig
+++ b/crypto/asymmetric_keys/Kconfig
@@ -21,6 +21,17 @@ config ASYMMETRIC_PUBLIC_KEY_SUBTYPE
appropriate hash algorithms (such as SHA-1) must be available.
ENOPKG will be reported if the requisite algorithm is unavailable.

+config ASYMMETRIC_TPM_KEY_SUBTYPE
+ tristate "Asymmetric TPM backed private key subtype"
+ depends on TCG_TPM
+ select CRYPTO_HMAC
+ select CRYPTO_SHA1
+ select CRYPTO_HASH_INFO
+ help
+ This option provides support for TPM backed private key type handling.
+ Operations such as sign, verify, encrypt, decrypt are performed by
+ the TPM after the private key is loaded.
+
config X509_CERTIFICATE_PARSER
tristate "X.509 certificate parser"
depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index c38424f55b08..73fbe650ff1d 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -11,6 +11,7 @@ asymmetric_keys-y := \
signature.o

obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o
+obj-$(CONFIG_ASYMMETRIC_TPM_KEY_SUBTYPE) += asym_tpm.o

#
# X.509 Certificate handling
diff --git a/crypto/asymmetric_keys/asym_tpm.c b/crypto/asymmetric_keys/asym_tpm.c
new file mode 100644
index 000000000000..d0b2b97e8e54
--- /dev/null
+++ b/crypto/asymmetric_keys/asym_tpm.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0
+#define pr_fmt(fmt) "ASYM-TPM: "fmt
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/seq_file.h>
+#include <linux/scatterlist.h>
+#include <linux/tpm.h>
+#include <keys/asymmetric-subtype.h>
+#include <crypto/asym_tpm_subtype.h>
+
+/*
+ * Provide a part of a description of the key for /proc/keys.
+ */
+static void asym_tpm_describe(const struct key *asymmetric_key,
+ struct seq_file *m)
+{
+ struct tpm_key *tk = asymmetric_key->payload.data[asym_crypto];
+
+ if (!tk)
+ return;
+
+ seq_printf(m, "TPM1.2/Blob");
+}
+
+static void asym_tpm_destroy(void *payload0, void *payload3)
+{
+ struct tpm_key *tk = payload0;
+
+ if (!tk)
+ return;
+
+ kfree(tk->blob);
+ tk->blob_len = 0;
+
+ kfree(tk);
+}
+
+/* Given the blob, parse it and load it into the TPM */
+struct tpm_key *tpm_key_create(const void *blob, uint32_t blob_len)
+{
+ int r;
+ struct tpm_key *tk;
+
+ r = tpm_is_tpm2(NULL);
+ if (r < 0)
+ goto error;
+
+ /* We don't support TPM2 yet */
+ if (r > 0) {
+ r = -ENODEV;
+ goto error;
+ }
+
+ r = -ENOMEM;
+ tk = kzalloc(sizeof(struct tpm_key), GFP_KERNEL);
+ if (!tk)
+ goto error;
+
+ tk->blob = kmemdup(blob, blob_len, GFP_KERNEL);
+ if (!tk->blob)
+ goto error_memdup;
+
+ tk->blob_len = blob_len;
+
+ return tk;
+
+error_memdup:
+ kfree(tk);
+error:
+ return ERR_PTR(r);
+}
+EXPORT_SYMBOL_GPL(tpm_key_create);
+
+/*
+ * TPM-based asymmetric key subtype
+ */
+struct asymmetric_key_subtype asym_tpm_subtype = {
+ .owner = THIS_MODULE,
+ .name = "asym_tpm",
+ .name_len = sizeof("asym_tpm") - 1,
+ .describe = asym_tpm_describe,
+ .destroy = asym_tpm_destroy,
+};
+EXPORT_SYMBOL_GPL(asym_tpm_subtype);
+
+MODULE_DESCRIPTION("TPM based asymmetric key subtype");
+MODULE_AUTHOR("Intel Corporation");
+MODULE_LICENSE("GPL v2");
diff --git a/include/crypto/asym_tpm_subtype.h b/include/crypto/asym_tpm_subtype.h
new file mode 100644
index 000000000000..03550b850998
--- /dev/null
+++ b/include/crypto/asym_tpm_subtype.h
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
+#ifndef _LINUX_ASYM_TPM_SUBTYPE_H
+#define _LINUX_ASYM_TPM_SUBTYPE_H
+
+#include <linux/keyctl.h>
+
+struct tpm_key {
+ void *blob;
+ u32 blob_len;
+};
+
+struct tpm_key *tpm_key_create(const void *blob, uint32_t blob_len);
+
+extern struct asymmetric_key_subtype asym_tpm_subtype;
+
+#endif /* _LINUX_ASYM_TPM_SUBTYPE_H */


2018-09-05 21:57:33

by David Howells

[permalink] [raw]
Subject: [PATCH 12/22] KEYS: Add parser for TPM-based keys

From: Denis Kenzior <[email protected]>

For TPM based keys, the only standard seems to be described here:
http://david.woodhou.se/draft-woodhouse-cert-best-practice.html#rfc.section.4.4

Quote from the relevant section:
"Rather, a common form of storage for "wrapped" keys is to encode the
binary TCPA_KEY structure in a single ASN.1 OCTET-STRING, and store the
result in PEM format with the tag "-----BEGIN TSS KEY BLOB-----". "

This patch implements the above behavior. It is assumed that the PEM
encoding is stripped out by userspace and only the raw DER/BER format is
provided. This is similar to how PKCS7, PKCS8 and X.509 keys are
handled.

Signed-off-by: Denis Kenzior <[email protected]>
Signed-off-by: David Howells <[email protected]>
---

crypto/asymmetric_keys/Kconfig | 9 +++
crypto/asymmetric_keys/Makefile | 11 ++++
crypto/asymmetric_keys/tpm.asn1 | 5 ++
crypto/asymmetric_keys/tpm_parser.c | 102 +++++++++++++++++++++++++++++++++++
4 files changed, 127 insertions(+)
create mode 100644 crypto/asymmetric_keys/tpm.asn1
create mode 100644 crypto/asymmetric_keys/tpm_parser.c

diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
index b75555c7d8ae..88353a9ebc9b 100644
--- a/crypto/asymmetric_keys/Kconfig
+++ b/crypto/asymmetric_keys/Kconfig
@@ -52,6 +52,15 @@ config PKCS8_PRIVATE_KEY_PARSER
private key data and provides the ability to instantiate a crypto key
from that data.

+config TPM_KEY_PARSER
+ tristate "TPM private key parser"
+ depends on ASYMMETRIC_TPM_KEY_SUBTYPE
+ select ASN1
+ help
+ This option provides support for parsing TPM format blobs for
+ private key data and provides the ability to instantiate a crypto key
+ from that data.
+
config PKCS7_MESSAGE_PARSER
tristate "PKCS#7 message parser"
depends on X509_CERTIFICATE_PARSER
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index 73fbe650ff1d..28b91adba2ae 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -75,3 +75,14 @@ verify_signed_pefile-y := \

$(obj)/mscode_parser.o: $(obj)/mscode.asn1.h $(obj)/mscode.asn1.h
$(obj)/mscode.asn1.o: $(obj)/mscode.asn1.c $(obj)/mscode.asn1.h
+
+#
+# TPM private key parsing
+#
+obj-$(CONFIG_TPM_KEY_PARSER) += tpm_key_parser.o
+tpm_key_parser-y := \
+ tpm.asn1.o \
+ tpm_parser.o
+
+$(obj)/tpm_parser.o: $(obj)/tpm.asn1.h
+$(obj)/tpm.asn1.o: $(obj)/tpm.asn1.c $(obj)/tpm.asn1.h
diff --git a/crypto/asymmetric_keys/tpm.asn1 b/crypto/asymmetric_keys/tpm.asn1
new file mode 100644
index 000000000000..d7f194232f30
--- /dev/null
+++ b/crypto/asymmetric_keys/tpm.asn1
@@ -0,0 +1,5 @@
+--
+-- Unencryted TPM Blob. For details of the format, see:
+-- http://david.woodhou.se/draft-woodhouse-cert-best-practice.html#I-D.mavrogiannopoulos-tpmuri
+--
+PrivateKeyInfo ::= OCTET STRING ({ tpm_note_key })
diff --git a/crypto/asymmetric_keys/tpm_parser.c b/crypto/asymmetric_keys/tpm_parser.c
new file mode 100644
index 000000000000..96405d8dcd98
--- /dev/null
+++ b/crypto/asymmetric_keys/tpm_parser.c
@@ -0,0 +1,102 @@
+// SPDX-License-Identifier: GPL-2.0
+#define pr_fmt(fmt) "TPM-PARSER: "fmt
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <keys/asymmetric-subtype.h>
+#include <keys/asymmetric-parser.h>
+#include <crypto/asym_tpm_subtype.h>
+#include "tpm.asn1.h"
+
+struct tpm_parse_context {
+ const void *blob;
+ u32 blob_len;
+};
+
+/*
+ * Note the key data of the ASN.1 blob.
+ */
+int tpm_note_key(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct tpm_parse_context *ctx = context;
+
+ ctx->blob = value;
+ ctx->blob_len = vlen;
+
+ return 0;
+}
+
+/*
+ * Parse a TPM-encrypted private key blob.
+ */
+static struct tpm_key *tpm_parse(const void *data, size_t datalen)
+{
+ struct tpm_parse_context ctx;
+ long ret;
+
+ memset(&ctx, 0, sizeof(ctx));
+
+ /* Attempt to decode the private key */
+ ret = asn1_ber_decoder(&tpm_decoder, &ctx, data, datalen);
+ if (ret < 0)
+ goto error;
+
+ return tpm_key_create(ctx.blob, ctx.blob_len);
+
+error:
+ return ERR_PTR(ret);
+}
+/*
+ * Attempt to parse a data blob for a key as a TPM private key blob.
+ */
+static int tpm_key_preparse(struct key_preparsed_payload *prep)
+{
+ struct tpm_key *tk;
+
+ /*
+ * TPM 1.2 keys are max 2048 bits long, so assume the blob is no
+ * more than 4x that
+ */
+ if (prep->datalen > 256 * 4)
+ return -EMSGSIZE;
+
+ tk = tpm_parse(prep->data, prep->datalen);
+
+ if (IS_ERR(tk))
+ return PTR_ERR(tk);
+
+ /* We're pinning the module by being linked against it */
+ __module_get(asym_tpm_subtype.owner);
+ prep->payload.data[asym_subtype] = &asym_tpm_subtype;
+ prep->payload.data[asym_key_ids] = NULL;
+ prep->payload.data[asym_crypto] = tk;
+ prep->payload.data[asym_auth] = NULL;
+ prep->quotalen = 100;
+ return 0;
+}
+
+static struct asymmetric_key_parser tpm_key_parser = {
+ .owner = THIS_MODULE,
+ .name = "tpm_parser",
+ .parse = tpm_key_preparse,
+};
+
+static int __init tpm_key_init(void)
+{
+ return register_asymmetric_key_parser(&tpm_key_parser);
+}
+
+static void __exit tpm_key_exit(void)
+{
+ unregister_asymmetric_key_parser(&tpm_key_parser);
+}
+
+module_init(tpm_key_init);
+module_exit(tpm_key_exit);
+
+MODULE_DESCRIPTION("TPM private key-blob parser");
+MODULE_LICENSE("GPL v2");


2018-09-05 21:57:49

by David Howells

[permalink] [raw]
Subject: [PATCH 11/22] KEYS: asym_tpm: extract key size & public key

From: Denis Kenzior <[email protected]>

The parsed BER/DER blob obtained from user space contains a TPM_Key
structure. This structure has some information about the key as well as
the public key portion.

This patch extracts this information for future use.

Signed-off-by: Denis Kenzior <[email protected]>
Signed-off-by: David Howells <[email protected]>
---

crypto/asymmetric_keys/asym_tpm.c | 112 +++++++++++++++++++++++++++++++++++++
include/crypto/asym_tpm_subtype.h | 3 +
2 files changed, 115 insertions(+)

diff --git a/crypto/asymmetric_keys/asym_tpm.c b/crypto/asymmetric_keys/asym_tpm.c
index d0b2b97e8e54..308c51e055a4 100644
--- a/crypto/asymmetric_keys/asym_tpm.c
+++ b/crypto/asymmetric_keys/asym_tpm.c
@@ -7,6 +7,7 @@
#include <linux/seq_file.h>
#include <linux/scatterlist.h>
#include <linux/tpm.h>
+#include <asm/unaligned.h>
#include <keys/asymmetric-subtype.h>
#include <crypto/asym_tpm_subtype.h>

@@ -37,6 +38,110 @@ static void asym_tpm_destroy(void *payload0, void *payload3)
kfree(tk);
}

+/*
+ * Parse enough information out of TPM_KEY structure:
+ * TPM_STRUCT_VER -> 4 bytes
+ * TPM_KEY_USAGE -> 2 bytes
+ * TPM_KEY_FLAGS -> 4 bytes
+ * TPM_AUTH_DATA_USAGE -> 1 byte
+ * TPM_KEY_PARMS -> variable
+ * UINT32 PCRInfoSize -> 4 bytes
+ * BYTE* -> PCRInfoSize bytes
+ * TPM_STORE_PUBKEY
+ * UINT32 encDataSize;
+ * BYTE* -> encDataSize;
+ *
+ * TPM_KEY_PARMS:
+ * TPM_ALGORITHM_ID -> 4 bytes
+ * TPM_ENC_SCHEME -> 2 bytes
+ * TPM_SIG_SCHEME -> 2 bytes
+ * UINT32 parmSize -> 4 bytes
+ * BYTE* -> variable
+ */
+static int extract_key_parameters(struct tpm_key *tk)
+{
+ const void *cur = tk->blob;
+ uint32_t len = tk->blob_len;
+ const void *pub_key;
+ uint32_t sz;
+ uint32_t key_len;
+
+ if (len < 11)
+ return -EBADMSG;
+
+ /* Ensure this is a legacy key */
+ if (get_unaligned_be16(cur + 4) != 0x0015)
+ return -EBADMSG;
+
+ /* Skip to TPM_KEY_PARMS */
+ cur += 11;
+ len -= 11;
+
+ if (len < 12)
+ return -EBADMSG;
+
+ /* Make sure this is an RSA key */
+ if (get_unaligned_be32(cur) != 0x00000001)
+ return -EBADMSG;
+
+ /* Make sure this is TPM_ES_RSAESPKCSv15 encoding scheme */
+ if (get_unaligned_be16(cur + 4) != 0x0002)
+ return -EBADMSG;
+
+ /* Make sure this is TPM_SS_RSASSAPKCS1v15_DER signature scheme */
+ if (get_unaligned_be16(cur + 6) != 0x0003)
+ return -EBADMSG;
+
+ sz = get_unaligned_be32(cur + 8);
+ if (len < sz + 12)
+ return -EBADMSG;
+
+ /* Move to TPM_RSA_KEY_PARMS */
+ len -= 12;
+ cur += 12;
+
+ /* Grab the RSA key length */
+ key_len = get_unaligned_be32(cur);
+
+ switch (key_len) {
+ case 512:
+ case 1024:
+ case 1536:
+ case 2048:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Move just past TPM_KEY_PARMS */
+ cur += sz;
+ len -= sz;
+
+ if (len < 4)
+ return -EBADMSG;
+
+ sz = get_unaligned_be32(cur);
+ if (len < 4 + sz)
+ return -EBADMSG;
+
+ /* Move to TPM_STORE_PUBKEY */
+ cur += 4 + sz;
+ len -= 4 + sz;
+
+ /* Grab the size of the public key, it should jive with the key size */
+ sz = get_unaligned_be32(cur);
+ if (sz > 256)
+ return -EINVAL;
+
+ pub_key = cur + 4;
+
+ tk->key_len = key_len;
+ tk->pub_key = pub_key;
+ tk->pub_key_len = sz;
+
+ return 0;
+}
+
/* Given the blob, parse it and load it into the TPM */
struct tpm_key *tpm_key_create(const void *blob, uint32_t blob_len)
{
@@ -64,8 +169,15 @@ struct tpm_key *tpm_key_create(const void *blob, uint32_t blob_len)

tk->blob_len = blob_len;

+ r = extract_key_parameters(tk);
+ if (r < 0)
+ goto error_extract;
+
return tk;

+error_extract:
+ kfree(tk->blob);
+ tk->blob_len = 0;
error_memdup:
kfree(tk);
error:
diff --git a/include/crypto/asym_tpm_subtype.h b/include/crypto/asym_tpm_subtype.h
index 03550b850998..48198c36d6b9 100644
--- a/include/crypto/asym_tpm_subtype.h
+++ b/include/crypto/asym_tpm_subtype.h
@@ -7,6 +7,9 @@
struct tpm_key {
void *blob;
u32 blob_len;
+ uint16_t key_len; /* Size in bits of the key */
+ const void *pub_key; /* pointer inside blob to the public key bytes */
+ uint16_t pub_key_len; /* length of the public key */
};

struct tpm_key *tpm_key_create(const void *blob, uint32_t blob_len);


2018-09-05 21:57:53

by David Howells

[permalink] [raw]
Subject: [PATCH 15/22] KEYS: trusted: Expose common functionality

From: Denis Kenzior <[email protected]>

This patch exposes some common functionality needed to send TPM
commands. The current approach is a complete hack and needs to be
addressed properly. This commit just show-cases what functionality
would need to be moved to a shared location or exposed in some other
manner.

Signed-off-by: Denis Kenzior <[email protected]>
Signed-off-by: David Howells <[email protected]>
---

crypto/asymmetric_keys/Kconfig | 1 +
security/keys/trusted.c | 12 ++++++++----
security/keys/trusted.h | 14 +++++++++++++-
3 files changed, 22 insertions(+), 5 deletions(-)

diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
index 88353a9ebc9b..be70ca6c85d3 100644
--- a/crypto/asymmetric_keys/Kconfig
+++ b/crypto/asymmetric_keys/Kconfig
@@ -24,6 +24,7 @@ config ASYMMETRIC_PUBLIC_KEY_SUBTYPE
config ASYMMETRIC_TPM_KEY_SUBTYPE
tristate "Asymmetric TPM backed private key subtype"
depends on TCG_TPM
+ depends on TRUSTED_KEYS
select CRYPTO_HMAC
select CRYPTO_SHA1
select CRYPTO_HASH_INFO
diff --git a/security/keys/trusted.c b/security/keys/trusted.c
index b69d3b1777c2..1c025fdfe0e0 100644
--- a/security/keys/trusted.c
+++ b/security/keys/trusted.c
@@ -121,7 +121,7 @@ static int TSS_rawhmac(unsigned char *digest, const unsigned char *key,
/*
* calculate authorization info fields to send to TPM
*/
-static int TSS_authhmac(unsigned char *digest, const unsigned char *key,
+int TSS_authhmac(unsigned char *digest, const unsigned char *key,
unsigned int keylen, unsigned char *h1,
unsigned char *h2, unsigned char h3, ...)
{
@@ -168,11 +168,12 @@ static int TSS_authhmac(unsigned char *digest, const unsigned char *key,
kzfree(sdesc);
return ret;
}
+EXPORT_SYMBOL_GPL(TSS_authhmac);

/*
* verify the AUTH1_COMMAND (Seal) result from TPM
*/
-static int TSS_checkhmac1(unsigned char *buffer,
+int TSS_checkhmac1(unsigned char *buffer,
const uint32_t command,
const unsigned char *ononce,
const unsigned char *key,
@@ -249,6 +250,7 @@ static int TSS_checkhmac1(unsigned char *buffer,
kzfree(sdesc);
return ret;
}
+EXPORT_SYMBOL_GPL(TSS_checkhmac1);

/*
* verify the AUTH2_COMMAND (unseal) result from TPM
@@ -355,7 +357,7 @@ static int TSS_checkhmac2(unsigned char *buffer,
* For key specific tpm requests, we will generate and send our
* own TPM command packets using the drivers send function.
*/
-static int trusted_tpm_send(unsigned char *cmd, size_t buflen)
+int trusted_tpm_send(unsigned char *cmd, size_t buflen)
{
int rc;

@@ -367,6 +369,7 @@ static int trusted_tpm_send(unsigned char *cmd, size_t buflen)
rc = -EPERM;
return rc;
}
+EXPORT_SYMBOL_GPL(trusted_tpm_send);

/*
* Lock a trusted key, by extending a selected PCR.
@@ -425,7 +428,7 @@ static int osap(struct tpm_buf *tb, struct osapsess *s,
/*
* Create an object independent authorisation protocol (oiap) session
*/
-static int oiap(struct tpm_buf *tb, uint32_t *handle, unsigned char *nonce)
+int oiap(struct tpm_buf *tb, uint32_t *handle, unsigned char *nonce)
{
int ret;

@@ -442,6 +445,7 @@ static int oiap(struct tpm_buf *tb, uint32_t *handle, unsigned char *nonce)
TPM_NONCE_SIZE);
return 0;
}
+EXPORT_SYMBOL_GPL(oiap);

struct tpm_digests {
unsigned char encauth[SHA1_DIGEST_SIZE];
diff --git a/security/keys/trusted.h b/security/keys/trusted.h
index 8d5fe9eafb22..adbcb6817826 100644
--- a/security/keys/trusted.h
+++ b/security/keys/trusted.h
@@ -3,7 +3,7 @@
#define __TRUSTED_KEY_H

/* implementation specific TPM constants */
-#define MAX_BUF_SIZE 512
+#define MAX_BUF_SIZE 1024
#define TPM_GETRANDOM_SIZE 14
#define TPM_OSAP_SIZE 36
#define TPM_OIAP_SIZE 10
@@ -36,6 +36,18 @@ enum {
SRK_keytype = 4
};

+int TSS_authhmac(unsigned char *digest, const unsigned char *key,
+ unsigned int keylen, unsigned char *h1,
+ unsigned char *h2, unsigned char h3, ...);
+int TSS_checkhmac1(unsigned char *buffer,
+ const uint32_t command,
+ const unsigned char *ononce,
+ const unsigned char *key,
+ unsigned int keylen, ...);
+
+int trusted_tpm_send(unsigned char *cmd, size_t buflen);
+int oiap(struct tpm_buf *tb, uint32_t *handle, unsigned char *nonce);
+
#define TPM_DEBUG 0

#if TPM_DEBUG


2018-09-05 21:58:03

by David Howells

[permalink] [raw]
Subject: [PATCH 13/22] KEYS: asym_tpm: Implement pkey_query

From: Denis Kenzior <[email protected]>

This commit implements the pkey_query operation. This is accomplished
by utilizing the public key portion to obtain max encryption size
information for the operations that utilize the public key (encrypt,
verify). The private key size extracted from the TPM_Key data structure
is used to fill the information where the private key is used (decrypt,
sign).

The kernel uses a DER/BER format for public keys and does not support
setting the key via the raw binary form. To get around this a simple
DER/BER formatter is implemented which stores the DER/BER formatted key
and exponent in a temporary buffer for use by the crypto API.

The only exponent supported currently is 65537. This holds true for
other Linux TPM tools such as 'create_tpm_key' and
trousers-openssl_tpm_engine.

Signed-off-by: Denis Kenzior <[email protected]>
Signed-off-by: David Howells <[email protected]>
---

crypto/asymmetric_keys/asym_tpm.c | 135 +++++++++++++++++++++++++++++++++++++
1 file changed, 135 insertions(+)

diff --git a/crypto/asymmetric_keys/asym_tpm.c b/crypto/asymmetric_keys/asym_tpm.c
index 308c51e055a4..837472d107d5 100644
--- a/crypto/asymmetric_keys/asym_tpm.c
+++ b/crypto/asymmetric_keys/asym_tpm.c
@@ -7,10 +7,24 @@
#include <linux/seq_file.h>
#include <linux/scatterlist.h>
#include <linux/tpm.h>
+#include <crypto/akcipher.h>
#include <asm/unaligned.h>
#include <keys/asymmetric-subtype.h>
#include <crypto/asym_tpm_subtype.h>

+/*
+ * Maximum buffer size for the BER/DER encoded public key. The public key
+ * is of the form SEQUENCE { INTEGER n, INTEGER e } where n is a maximum 2048
+ * bit key and e is usually 65537
+ * The encoding overhead is:
+ * - max 4 bytes for SEQUENCE
+ * - max 4 bytes for INTEGER n type/length
+ * - 257 bytes of n
+ * - max 2 bytes for INTEGER e type/length
+ * - 3 bytes of e
+ */
+#define PUB_KEY_BUF_SIZE (4 + 4 + 257 + 2 + 3)
+
/*
* Provide a part of a description of the key for /proc/keys.
*/
@@ -38,6 +52,126 @@ static void asym_tpm_destroy(void *payload0, void *payload3)
kfree(tk);
}

+/* How many bytes will it take to encode the length */
+static inline uint32_t definite_length(uint32_t len)
+{
+ if (len <= 127)
+ return 1;
+ if (len <= 255)
+ return 2;
+ return 3;
+}
+
+static inline uint8_t *encode_tag_length(uint8_t *buf, uint8_t tag,
+ uint32_t len)
+{
+ *buf++ = tag;
+
+ if (len <= 127) {
+ buf[0] = len;
+ return buf + 1;
+ }
+
+ if (len <= 255) {
+ buf[0] = 0x81;
+ buf[1] = len;
+ return buf + 2;
+ }
+
+ buf[0] = 0x82;
+ put_unaligned_be16(len, buf + 1);
+ return buf + 3;
+}
+
+static uint32_t derive_pub_key(const void *pub_key, uint32_t len, uint8_t *buf)
+{
+ uint8_t *cur = buf;
+ uint32_t n_len = definite_length(len) + 1 + len + 1;
+ uint32_t e_len = definite_length(3) + 1 + 3;
+ uint8_t e[3] = { 0x01, 0x00, 0x01 };
+
+ /* SEQUENCE */
+ cur = encode_tag_length(cur, 0x30, n_len + e_len);
+ /* INTEGER n */
+ cur = encode_tag_length(cur, 0x02, len + 1);
+ cur[0] = 0x00;
+ memcpy(cur + 1, pub_key, len);
+ cur += len + 1;
+ cur = encode_tag_length(cur, 0x02, sizeof(e));
+ memcpy(cur, e, sizeof(e));
+ cur += sizeof(e);
+
+ return cur - buf;
+}
+
+/*
+ * Determine the crypto algorithm name.
+ */
+static int determine_akcipher(const char *encoding, const char *hash_algo,
+ char alg_name[CRYPTO_MAX_ALG_NAME])
+{
+ /* TODO: We don't support hashing yet */
+ if (hash_algo)
+ return -ENOPKG;
+
+ if (strcmp(encoding, "pkcs1") == 0) {
+ strcpy(alg_name, "pkcs1pad(rsa)");
+ return 0;
+ }
+
+ if (strcmp(encoding, "raw") == 0) {
+ strcpy(alg_name, "rsa");
+ return 0;
+ }
+
+ return -ENOPKG;
+}
+
+/*
+ * Query information about a key.
+ */
+static int tpm_key_query(const struct kernel_pkey_params *params,
+ struct kernel_pkey_query *info)
+{
+ struct tpm_key *tk = params->key->payload.data[asym_crypto];
+ int ret;
+ char alg_name[CRYPTO_MAX_ALG_NAME];
+ struct crypto_akcipher *tfm;
+ uint8_t der_pub_key[PUB_KEY_BUF_SIZE];
+ uint32_t der_pub_key_len;
+ int len;
+
+ /* TPM only works on private keys, public keys still done in software */
+ ret = determine_akcipher(params->encoding, params->hash_algo, alg_name);
+ if (ret < 0)
+ return ret;
+
+ tfm = crypto_alloc_akcipher(alg_name, 0, 0);
+ if (IS_ERR(tfm))
+ return PTR_ERR(tfm);
+
+ der_pub_key_len = derive_pub_key(tk->pub_key, tk->pub_key_len,
+ der_pub_key);
+
+ ret = crypto_akcipher_set_pub_key(tfm, der_pub_key, der_pub_key_len);
+ if (ret < 0)
+ goto error_free_tfm;
+
+ len = crypto_akcipher_maxsize(tfm);
+
+ info->key_size = tk->key_len;
+ info->max_data_size = tk->key_len / 8;
+ info->max_sig_size = len;
+ info->max_enc_size = len;
+ info->max_dec_size = tk->key_len / 8;
+
+ ret = 0;
+error_free_tfm:
+ crypto_free_akcipher(tfm);
+ pr_devel("<==%s() = %d\n", __func__, ret);
+ return ret;
+}
+
/*
* Parse enough information out of TPM_KEY structure:
* TPM_STRUCT_VER -> 4 bytes
@@ -194,6 +328,7 @@ struct asymmetric_key_subtype asym_tpm_subtype = {
.name_len = sizeof("asym_tpm") - 1,
.describe = asym_tpm_describe,
.destroy = asym_tpm_destroy,
+ .query = tpm_key_query,
};
EXPORT_SYMBOL_GPL(asym_tpm_subtype);



2018-09-05 21:58:13

by David Howells

[permalink] [raw]
Subject: [PATCH 14/22] KEYS: asym_tpm: Implement encryption operation

From: Denis Kenzior <[email protected]>

This patch impelements the pkey_encrypt operation. The public key
portion extracted from the TPM key blob is used. The operation is
performed entirely in software using the crypto API.

Signed-off-by: Denis Kenzior <[email protected]>
Signed-off-by: David Howells <[email protected]>
---

crypto/asymmetric_keys/asym_tpm.c | 84 +++++++++++++++++++++++++++++++++++++
1 file changed, 84 insertions(+)

diff --git a/crypto/asymmetric_keys/asym_tpm.c b/crypto/asymmetric_keys/asym_tpm.c
index 837472d107d5..8edca3c4c193 100644
--- a/crypto/asymmetric_keys/asym_tpm.c
+++ b/crypto/asymmetric_keys/asym_tpm.c
@@ -165,6 +165,8 @@ static int tpm_key_query(const struct kernel_pkey_params *params,
info->max_enc_size = len;
info->max_dec_size = tk->key_len / 8;

+ info->supported_ops = KEYCTL_SUPPORTS_ENCRYPT;
+
ret = 0;
error_free_tfm:
crypto_free_akcipher(tfm);
@@ -172,6 +174,87 @@ static int tpm_key_query(const struct kernel_pkey_params *params,
return ret;
}

+/*
+ * Encryption operation is performed with the public key. Hence it is done
+ * in software
+ */
+static int tpm_key_encrypt(struct tpm_key *tk,
+ struct kernel_pkey_params *params,
+ const void *in, void *out)
+{
+ char alg_name[CRYPTO_MAX_ALG_NAME];
+ struct crypto_akcipher *tfm;
+ struct akcipher_request *req;
+ struct crypto_wait cwait;
+ struct scatterlist in_sg, out_sg;
+ uint8_t der_pub_key[PUB_KEY_BUF_SIZE];
+ uint32_t der_pub_key_len;
+ int ret;
+
+ pr_devel("==>%s()\n", __func__);
+
+ ret = determine_akcipher(params->encoding, params->hash_algo, alg_name);
+ if (ret < 0)
+ return ret;
+
+ tfm = crypto_alloc_akcipher(alg_name, 0, 0);
+ if (IS_ERR(tfm))
+ return PTR_ERR(tfm);
+
+ der_pub_key_len = derive_pub_key(tk->pub_key, tk->pub_key_len,
+ der_pub_key);
+
+ ret = crypto_akcipher_set_pub_key(tfm, der_pub_key, der_pub_key_len);
+ if (ret < 0)
+ goto error_free_tfm;
+
+ req = akcipher_request_alloc(tfm, GFP_KERNEL);
+ if (!req)
+ goto error_free_tfm;
+
+ sg_init_one(&in_sg, in, params->in_len);
+ sg_init_one(&out_sg, out, params->out_len);
+ akcipher_request_set_crypt(req, &in_sg, &out_sg, params->in_len,
+ params->out_len);
+ crypto_init_wait(&cwait);
+ akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
+ CRYPTO_TFM_REQ_MAY_SLEEP,
+ crypto_req_done, &cwait);
+
+ ret = crypto_akcipher_encrypt(req);
+ ret = crypto_wait_req(ret, &cwait);
+
+ if (ret == 0)
+ ret = req->dst_len;
+
+ akcipher_request_free(req);
+error_free_tfm:
+ crypto_free_akcipher(tfm);
+ pr_devel("<==%s() = %d\n", __func__, ret);
+ return ret;
+}
+
+/*
+ * Do encryption, decryption and signing ops.
+ */
+static int tpm_key_eds_op(struct kernel_pkey_params *params,
+ const void *in, void *out)
+{
+ struct tpm_key *tk = params->key->payload.data[asym_crypto];
+ int ret = -EOPNOTSUPP;
+
+ /* Perform the encryption calculation. */
+ switch (params->op) {
+ case kernel_pkey_encrypt:
+ ret = tpm_key_encrypt(tk, params, in, out);
+ break;
+ default:
+ BUG();
+ }
+
+ return ret;
+}
+
/*
* Parse enough information out of TPM_KEY structure:
* TPM_STRUCT_VER -> 4 bytes
@@ -329,6 +412,7 @@ struct asymmetric_key_subtype asym_tpm_subtype = {
.describe = asym_tpm_describe,
.destroy = asym_tpm_destroy,
.query = tpm_key_query,
+ .eds_op = tpm_key_eds_op,
};
EXPORT_SYMBOL_GPL(asym_tpm_subtype);



2018-09-05 21:58:45

by David Howells

[permalink] [raw]
Subject: [PATCH 19/22] KEYS: asym_tpm: Implement the decrypt operation

From: Denis Kenzior <[email protected]>

This patch implements the pkey_decrypt operation using the private key
blob. The blob is first loaded into the TPM via tpm_loadkey2. Once the
handle is obtained, tpm_unbind operation is used to decrypt the data on
the TPM and the result is returned. The key loaded by tpm_loadkey2 is
then evicted via tpm_flushspecific operation.

This patch assumes that the SRK authorization is a well known 20-byte of
zeros and the same holds for the key authorization of the provided key.

Signed-off-by: Denis Kenzior <[email protected]>
Signed-off-by: David Howells <[email protected]>
---

crypto/asymmetric_keys/asym_tpm.c | 58 ++++++++++++++++++++++++++++++++++++-
1 file changed, 57 insertions(+), 1 deletion(-)

diff --git a/crypto/asymmetric_keys/asym_tpm.c b/crypto/asymmetric_keys/asym_tpm.c
index e893b5212222..6f5d5cf98910 100644
--- a/crypto/asymmetric_keys/asym_tpm.c
+++ b/crypto/asymmetric_keys/asym_tpm.c
@@ -341,7 +341,8 @@ static int tpm_key_query(const struct kernel_pkey_params *params,
info->max_enc_size = len;
info->max_dec_size = tk->key_len / 8;

- info->supported_ops = KEYCTL_SUPPORTS_ENCRYPT;
+ info->supported_ops = KEYCTL_SUPPORTS_ENCRYPT |
+ KEYCTL_SUPPORTS_DECRYPT;

ret = 0;
error_free_tfm:
@@ -410,6 +411,58 @@ static int tpm_key_encrypt(struct tpm_key *tk,
return ret;
}

+/*
+ * Decryption operation is performed with the private key in the TPM.
+ */
+static int tpm_key_decrypt(struct tpm_key *tk,
+ struct kernel_pkey_params *params,
+ const void *in, void *out)
+{
+ struct tpm_buf *tb;
+ uint32_t keyhandle;
+ uint8_t srkauth[SHA1_DIGEST_SIZE];
+ uint8_t keyauth[SHA1_DIGEST_SIZE];
+ int r;
+
+ pr_devel("==>%s()\n", __func__);
+
+ if (params->hash_algo)
+ return -ENOPKG;
+
+ if (strcmp(params->encoding, "pkcs1"))
+ return -ENOPKG;
+
+ tb = kzalloc(sizeof(*tb), GFP_KERNEL);
+ if (!tb)
+ return -ENOMEM;
+
+ /* TODO: Handle a non-all zero SRK authorization */
+ memset(srkauth, 0, sizeof(srkauth));
+
+ r = tpm_loadkey2(tb, SRKHANDLE, srkauth,
+ tk->blob, tk->blob_len, &keyhandle);
+ if (r < 0) {
+ pr_devel("loadkey2 failed (%d)\n", r);
+ goto error;
+ }
+
+ /* TODO: Handle a non-all zero key authorization */
+ memset(keyauth, 0, sizeof(keyauth));
+
+ r = tpm_unbind(tb, keyhandle, keyauth,
+ in, params->in_len, out, params->out_len);
+ if (r < 0)
+ pr_devel("tpm_unbind failed (%d)\n", r);
+
+ if (tpm_flushspecific(tb, keyhandle) < 0)
+ pr_devel("flushspecific failed (%d)\n", r);
+
+error:
+ kzfree(tb);
+ pr_devel("<==%s() = %d\n", __func__, r);
+ return r;
+}
+
/*
* Do encryption, decryption and signing ops.
*/
@@ -424,6 +477,9 @@ static int tpm_key_eds_op(struct kernel_pkey_params *params,
case kernel_pkey_encrypt:
ret = tpm_key_encrypt(tk, params, in, out);
break;
+ case kernel_pkey_decrypt:
+ ret = tpm_key_decrypt(tk, params, in, out);
+ break;
default:
BUG();
}


2018-09-05 21:58:45

by David Howells

[permalink] [raw]
Subject: [PATCH 21/22] KEYS: asym_tpm: Implement tpm_sign

From: Denis Kenzior <[email protected]>

Signed-off-by: Denis Kenzior <[email protected]>
Signed-off-by: David Howells <[email protected]>
---

crypto/asymmetric_keys/asym_tpm.c | 87 +++++++++++++++++++++++++++++++++++++
1 file changed, 87 insertions(+)

diff --git a/crypto/asymmetric_keys/asym_tpm.c b/crypto/asymmetric_keys/asym_tpm.c
index a38ba375675e..a5a5f913a74f 100644
--- a/crypto/asymmetric_keys/asym_tpm.c
+++ b/crypto/asymmetric_keys/asym_tpm.c
@@ -20,9 +20,11 @@
#define TPM_ORD_FLUSHSPECIFIC 186
#define TPM_ORD_LOADKEY2 65
#define TPM_ORD_UNBIND 30
+#define TPM_ORD_SIGN 60
#define TPM_LOADKEY2_SIZE 59
#define TPM_FLUSHSPECIFIC_SIZE 18
#define TPM_UNBIND_SIZE 63
+#define TPM_SIGN_SIZE 63

#define TPM_RT_KEY 0x00000001

@@ -189,6 +191,91 @@ static int tpm_unbind(struct tpm_buf *tb,
return datalen;
}

+/*
+ * Sign a blob provided by userspace (that has had the hash function applied)
+ * using a specific key handle. The handle is assumed to have been previously
+ * loaded by e.g. LoadKey2.
+ *
+ * Note that the key signature scheme of the used key should be set to
+ * TPM_SS_RSASSAPKCS1v15_DER. This allows the hashed input to be of any size
+ * up to key_length_in_bytes - 11 and not be limited to size 20 like the
+ * TPM_SS_RSASSAPKCS1v15_SHA1 signature scheme.
+ */
+static int tpm_sign(struct tpm_buf *tb,
+ uint32_t keyhandle, unsigned char *keyauth,
+ const unsigned char *blob, uint32_t bloblen,
+ void *out, uint32_t outlen)
+{
+ unsigned char nonceodd[TPM_NONCE_SIZE];
+ unsigned char enonce[TPM_NONCE_SIZE];
+ unsigned char authdata[SHA1_DIGEST_SIZE];
+ uint32_t authhandle = 0;
+ unsigned char cont = 0;
+ uint32_t ordinal;
+ uint32_t datalen;
+ int ret;
+
+ ordinal = htonl(TPM_ORD_SIGN);
+ datalen = htonl(bloblen);
+
+ /* session for loading the key */
+ ret = oiap(tb, &authhandle, enonce);
+ if (ret < 0) {
+ pr_info("oiap failed (%d)\n", ret);
+ return ret;
+ }
+
+ /* generate odd nonce */
+ ret = tpm_get_random(NULL, nonceodd, TPM_NONCE_SIZE);
+ if (ret < 0) {
+ pr_info("tpm_get_random failed (%d)\n", ret);
+ return ret;
+ }
+
+ /* calculate authorization HMAC value */
+ ret = TSS_authhmac(authdata, keyauth, SHA1_DIGEST_SIZE, enonce,
+ nonceodd, cont, sizeof(uint32_t), &ordinal,
+ sizeof(uint32_t), &datalen,
+ bloblen, blob, 0, 0);
+ if (ret < 0)
+ return ret;
+
+ /* build the request buffer */
+ INIT_BUF(tb);
+ store16(tb, TPM_TAG_RQU_AUTH1_COMMAND);
+ store32(tb, TPM_SIGN_SIZE + bloblen);
+ store32(tb, TPM_ORD_SIGN);
+ store32(tb, keyhandle);
+ store32(tb, bloblen);
+ storebytes(tb, blob, bloblen);
+ store32(tb, authhandle);
+ storebytes(tb, nonceodd, TPM_NONCE_SIZE);
+ store8(tb, cont);
+ storebytes(tb, authdata, SHA1_DIGEST_SIZE);
+
+ ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE);
+ if (ret < 0) {
+ pr_info("authhmac failed (%d)\n", ret);
+ return ret;
+ }
+
+ datalen = LOAD32(tb->data, TPM_DATA_OFFSET);
+
+ ret = TSS_checkhmac1(tb->data, ordinal, nonceodd,
+ keyauth, SHA1_DIGEST_SIZE,
+ sizeof(uint32_t), TPM_DATA_OFFSET,
+ datalen, TPM_DATA_OFFSET + sizeof(uint32_t),
+ 0, 0);
+ if (ret < 0) {
+ pr_info("TSS_checkhmac1 failed (%d)\n", ret);
+ return ret;
+ }
+
+ memcpy(out, tb->data + TPM_DATA_OFFSET + sizeof(uint32_t),
+ min(datalen, outlen));
+
+ return datalen;
+}
/*
* Maximum buffer size for the BER/DER encoded public key. The public key
* is of the form SEQUENCE { INTEGER n, INTEGER e } where n is a maximum 2048


2018-09-05 21:58:45

by David Howells

[permalink] [raw]
Subject: [PATCH 16/22] KEYS: Move trusted.h to include/keys

From: Denis Kenzior <[email protected]>

Signed-off-by: Denis Kenzior <[email protected]>
Signed-off-by: David Howells <[email protected]>
---

include/keys/trusted.h | 136 +++++++++++++++++++++++++++++++++++++++++++++++
security/keys/trusted.c | 2 -
security/keys/trusted.h | 136 -----------------------------------------------
3 files changed, 137 insertions(+), 137 deletions(-)
create mode 100644 include/keys/trusted.h
delete mode 100644 security/keys/trusted.h

diff --git a/include/keys/trusted.h b/include/keys/trusted.h
new file mode 100644
index 000000000000..adbcb6817826
--- /dev/null
+++ b/include/keys/trusted.h
@@ -0,0 +1,136 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __TRUSTED_KEY_H
+#define __TRUSTED_KEY_H
+
+/* implementation specific TPM constants */
+#define MAX_BUF_SIZE 1024
+#define TPM_GETRANDOM_SIZE 14
+#define TPM_OSAP_SIZE 36
+#define TPM_OIAP_SIZE 10
+#define TPM_SEAL_SIZE 87
+#define TPM_UNSEAL_SIZE 104
+#define TPM_SIZE_OFFSET 2
+#define TPM_RETURN_OFFSET 6
+#define TPM_DATA_OFFSET 10
+
+#define LOAD32(buffer, offset) (ntohl(*(uint32_t *)&buffer[offset]))
+#define LOAD32N(buffer, offset) (*(uint32_t *)&buffer[offset])
+#define LOAD16(buffer, offset) (ntohs(*(uint16_t *)&buffer[offset]))
+
+struct tpm_buf {
+ int len;
+ unsigned char data[MAX_BUF_SIZE];
+};
+
+#define INIT_BUF(tb) (tb->len = 0)
+
+struct osapsess {
+ uint32_t handle;
+ unsigned char secret[SHA1_DIGEST_SIZE];
+ unsigned char enonce[TPM_NONCE_SIZE];
+};
+
+/* discrete values, but have to store in uint16_t for TPM use */
+enum {
+ SEAL_keytype = 1,
+ SRK_keytype = 4
+};
+
+int TSS_authhmac(unsigned char *digest, const unsigned char *key,
+ unsigned int keylen, unsigned char *h1,
+ unsigned char *h2, unsigned char h3, ...);
+int TSS_checkhmac1(unsigned char *buffer,
+ const uint32_t command,
+ const unsigned char *ononce,
+ const unsigned char *key,
+ unsigned int keylen, ...);
+
+int trusted_tpm_send(unsigned char *cmd, size_t buflen);
+int oiap(struct tpm_buf *tb, uint32_t *handle, unsigned char *nonce);
+
+#define TPM_DEBUG 0
+
+#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);
+ 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");
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE,
+ 16, 1, &s->secret, SHA1_DIGEST_SIZE, 0);
+ pr_info("trusted-key: enonce:\n");
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE,
+ 16, 1, &s->enonce, SHA1_DIGEST_SIZE, 0);
+}
+
+static inline void dump_tpm_buf(unsigned char *buf)
+{
+ int len;
+
+ pr_info("\ntrusted-key: tpm buffer\n");
+ len = LOAD32(buf, TPM_SIZE_OFFSET);
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, buf, len, 0);
+}
+#else
+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)
+{
+}
+
+static inline void dump_tpm_buf(unsigned char *buf)
+{
+}
+#endif
+
+static inline void store8(struct tpm_buf *buf, const unsigned char value)
+{
+ buf->data[buf->len++] = value;
+}
+
+static inline void store16(struct tpm_buf *buf, const uint16_t value)
+{
+ *(uint16_t *) & buf->data[buf->len] = htons(value);
+ buf->len += sizeof value;
+}
+
+static inline void store32(struct tpm_buf *buf, const uint32_t value)
+{
+ *(uint32_t *) & buf->data[buf->len] = htonl(value);
+ buf->len += sizeof value;
+}
+
+static inline void storebytes(struct tpm_buf *buf, const unsigned char *in,
+ const int len)
+{
+ memcpy(buf->data + buf->len, in, len);
+ buf->len += len;
+}
+#endif
diff --git a/security/keys/trusted.c b/security/keys/trusted.c
index 1c025fdfe0e0..ff6789365a12 100644
--- a/security/keys/trusted.c
+++ b/security/keys/trusted.c
@@ -30,7 +30,7 @@
#include <linux/tpm.h>
#include <linux/tpm_command.h>

-#include "trusted.h"
+#include <keys/trusted.h>

static const char hmac_alg[] = "hmac(sha1)";
static const char hash_alg[] = "sha1";
diff --git a/security/keys/trusted.h b/security/keys/trusted.h
deleted file mode 100644
index adbcb6817826..000000000000
--- a/security/keys/trusted.h
+++ /dev/null
@@ -1,136 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef __TRUSTED_KEY_H
-#define __TRUSTED_KEY_H
-
-/* implementation specific TPM constants */
-#define MAX_BUF_SIZE 1024
-#define TPM_GETRANDOM_SIZE 14
-#define TPM_OSAP_SIZE 36
-#define TPM_OIAP_SIZE 10
-#define TPM_SEAL_SIZE 87
-#define TPM_UNSEAL_SIZE 104
-#define TPM_SIZE_OFFSET 2
-#define TPM_RETURN_OFFSET 6
-#define TPM_DATA_OFFSET 10
-
-#define LOAD32(buffer, offset) (ntohl(*(uint32_t *)&buffer[offset]))
-#define LOAD32N(buffer, offset) (*(uint32_t *)&buffer[offset])
-#define LOAD16(buffer, offset) (ntohs(*(uint16_t *)&buffer[offset]))
-
-struct tpm_buf {
- int len;
- unsigned char data[MAX_BUF_SIZE];
-};
-
-#define INIT_BUF(tb) (tb->len = 0)
-
-struct osapsess {
- uint32_t handle;
- unsigned char secret[SHA1_DIGEST_SIZE];
- unsigned char enonce[TPM_NONCE_SIZE];
-};
-
-/* discrete values, but have to store in uint16_t for TPM use */
-enum {
- SEAL_keytype = 1,
- SRK_keytype = 4
-};
-
-int TSS_authhmac(unsigned char *digest, const unsigned char *key,
- unsigned int keylen, unsigned char *h1,
- unsigned char *h2, unsigned char h3, ...);
-int TSS_checkhmac1(unsigned char *buffer,
- const uint32_t command,
- const unsigned char *ononce,
- const unsigned char *key,
- unsigned int keylen, ...);
-
-int trusted_tpm_send(unsigned char *cmd, size_t buflen);
-int oiap(struct tpm_buf *tb, uint32_t *handle, unsigned char *nonce);
-
-#define TPM_DEBUG 0
-
-#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);
- 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");
- print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE,
- 16, 1, &s->secret, SHA1_DIGEST_SIZE, 0);
- pr_info("trusted-key: enonce:\n");
- print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE,
- 16, 1, &s->enonce, SHA1_DIGEST_SIZE, 0);
-}
-
-static inline void dump_tpm_buf(unsigned char *buf)
-{
- int len;
-
- pr_info("\ntrusted-key: tpm buffer\n");
- len = LOAD32(buf, TPM_SIZE_OFFSET);
- print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, buf, len, 0);
-}
-#else
-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)
-{
-}
-
-static inline void dump_tpm_buf(unsigned char *buf)
-{
-}
-#endif
-
-static inline void store8(struct tpm_buf *buf, const unsigned char value)
-{
- buf->data[buf->len++] = value;
-}
-
-static inline void store16(struct tpm_buf *buf, const uint16_t value)
-{
- *(uint16_t *) & buf->data[buf->len] = htons(value);
- buf->len += sizeof value;
-}
-
-static inline void store32(struct tpm_buf *buf, const uint32_t value)
-{
- *(uint32_t *) & buf->data[buf->len] = htonl(value);
- buf->len += sizeof value;
-}
-
-static inline void storebytes(struct tpm_buf *buf, const unsigned char *in,
- const int len)
-{
- memcpy(buf->data + buf->len, in, len);
- buf->len += len;
-}
-#endif


2018-09-05 21:58:52

by David Howells

[permalink] [raw]
Subject: [PATCH 17/22] KEYS: asym_tpm: Add loadkey2 and flushspecific

From: Denis Kenzior <[email protected]>

This commit adds TPM_LoadKey2 and TPM_FlushSpecific operations.

Signed-off-by: Denis Kenzior <[email protected]>
Signed-off-by: David Howells <[email protected]>
Reviewed-by: James Morris <[email protected]>
---

crypto/asymmetric_keys/asym_tpm.c | 94 +++++++++++++++++++++++++++++++++++++
1 file changed, 94 insertions(+)

diff --git a/crypto/asymmetric_keys/asym_tpm.c b/crypto/asymmetric_keys/asym_tpm.c
index 8edca3c4c193..6a2d33014ecc 100644
--- a/crypto/asymmetric_keys/asym_tpm.c
+++ b/crypto/asymmetric_keys/asym_tpm.c
@@ -7,11 +7,105 @@
#include <linux/seq_file.h>
#include <linux/scatterlist.h>
#include <linux/tpm.h>
+#include <linux/tpm_command.h>
#include <crypto/akcipher.h>
+#include <crypto/hash.h>
+#include <crypto/sha.h>
#include <asm/unaligned.h>
#include <keys/asymmetric-subtype.h>
+#include <keys/trusted.h>
#include <crypto/asym_tpm_subtype.h>

+#define TPM_ORD_FLUSHSPECIFIC 186
+#define TPM_ORD_LOADKEY2 65
+#define TPM_LOADKEY2_SIZE 59
+#define TPM_FLUSHSPECIFIC_SIZE 18
+
+#define TPM_RT_KEY 0x00000001
+
+/*
+ * Load a TPM key from the blob provided by userspace
+ */
+static int tpm_loadkey2(struct tpm_buf *tb,
+ uint32_t keyhandle, unsigned char *keyauth,
+ const unsigned char *keyblob, int keybloblen,
+ uint32_t *newhandle)
+{
+ unsigned char nonceodd[TPM_NONCE_SIZE];
+ unsigned char enonce[TPM_NONCE_SIZE];
+ unsigned char authdata[SHA1_DIGEST_SIZE];
+ uint32_t authhandle = 0;
+ unsigned char cont = 0;
+ uint32_t ordinal;
+ int ret;
+
+ ordinal = htonl(TPM_ORD_LOADKEY2);
+
+ /* session for loading the key */
+ ret = oiap(tb, &authhandle, enonce);
+ if (ret < 0) {
+ pr_info("oiap failed (%d)\n", ret);
+ return ret;
+ }
+
+ /* generate odd nonce */
+ ret = tpm_get_random(NULL, nonceodd, TPM_NONCE_SIZE);
+ if (ret < 0) {
+ pr_info("tpm_get_random failed (%d)\n", ret);
+ return ret;
+ }
+
+ /* calculate authorization HMAC value */
+ ret = TSS_authhmac(authdata, keyauth, SHA1_DIGEST_SIZE, enonce,
+ nonceodd, cont, sizeof(uint32_t), &ordinal,
+ keybloblen, keyblob, 0, 0);
+ if (ret < 0)
+ return ret;
+
+ /* build the request buffer */
+ INIT_BUF(tb);
+ store16(tb, TPM_TAG_RQU_AUTH1_COMMAND);
+ store32(tb, TPM_LOADKEY2_SIZE + keybloblen);
+ store32(tb, TPM_ORD_LOADKEY2);
+ store32(tb, keyhandle);
+ storebytes(tb, keyblob, keybloblen);
+ store32(tb, authhandle);
+ storebytes(tb, nonceodd, TPM_NONCE_SIZE);
+ store8(tb, cont);
+ storebytes(tb, authdata, SHA1_DIGEST_SIZE);
+
+ ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE);
+ if (ret < 0) {
+ pr_info("authhmac failed (%d)\n", ret);
+ return ret;
+ }
+
+ ret = TSS_checkhmac1(tb->data, ordinal, nonceodd, keyauth,
+ SHA1_DIGEST_SIZE, 0, 0);
+ if (ret < 0) {
+ pr_info("TSS_checkhmac1 failed (%d)\n", ret);
+ return ret;
+ }
+
+ *newhandle = LOAD32(tb->data, TPM_DATA_OFFSET);
+ return 0;
+}
+
+/*
+ * Execute the FlushSpecific TPM command
+ */
+static int tpm_flushspecific(struct tpm_buf *tb, uint32_t handle)
+{
+ INIT_BUF(tb);
+ store16(tb, TPM_TAG_RQU_COMMAND);
+ store32(tb, TPM_FLUSHSPECIFIC_SIZE);
+ store32(tb, TPM_ORD_FLUSHSPECIFIC);
+ store32(tb, handle);
+ store32(tb, TPM_RT_KEY);
+
+ return trusted_tpm_send(tb->data, MAX_BUF_SIZE);
+}
+
/*
* Maximum buffer size for the BER/DER encoded public key. The public key
* is of the form SEQUENCE { INTEGER n, INTEGER e } where n is a maximum 2048


2018-09-05 21:58:52

by David Howells

[permalink] [raw]
Subject: [PATCH 20/22] KEYS: asym_tpm: Implement signature verification

From: Denis Kenzior <[email protected]>

This patch implements the verify_signature operation. The public key
portion extracted from the TPM key blob is used. The operation is
performed entirely in software using the crypto API.

Signed-off-by: Denis Kenzior <[email protected]>
Signed-off-by: David Howells <[email protected]>
---

crypto/asymmetric_keys/asym_tpm.c | 106 +++++++++++++++++++++++++++++++++++--
1 file changed, 100 insertions(+), 6 deletions(-)

diff --git a/crypto/asymmetric_keys/asym_tpm.c b/crypto/asymmetric_keys/asym_tpm.c
index 6f5d5cf98910..a38ba375675e 100644
--- a/crypto/asymmetric_keys/asym_tpm.c
+++ b/crypto/asymmetric_keys/asym_tpm.c
@@ -15,6 +15,7 @@
#include <keys/asymmetric-subtype.h>
#include <keys/trusted.h>
#include <crypto/asym_tpm_subtype.h>
+#include <crypto/public_key.h>

#define TPM_ORD_FLUSHSPECIFIC 186
#define TPM_ORD_LOADKEY2 65
@@ -286,12 +287,16 @@ static uint32_t derive_pub_key(const void *pub_key, uint32_t len, uint8_t *buf)
static int determine_akcipher(const char *encoding, const char *hash_algo,
char alg_name[CRYPTO_MAX_ALG_NAME])
{
- /* TODO: We don't support hashing yet */
- if (hash_algo)
- return -ENOPKG;
-
if (strcmp(encoding, "pkcs1") == 0) {
- strcpy(alg_name, "pkcs1pad(rsa)");
+ if (!hash_algo) {
+ strcpy(alg_name, "pkcs1pad(rsa)");
+ return 0;
+ }
+
+ if (snprintf(alg_name, CRYPTO_MAX_ALG_NAME, "pkcs1pad(rsa,%s)",
+ hash_algo) >= CRYPTO_MAX_ALG_NAME)
+ return -EINVAL;
+
return 0;
}

@@ -342,7 +347,8 @@ static int tpm_key_query(const struct kernel_pkey_params *params,
info->max_dec_size = tk->key_len / 8;

info->supported_ops = KEYCTL_SUPPORTS_ENCRYPT |
- KEYCTL_SUPPORTS_DECRYPT;
+ KEYCTL_SUPPORTS_DECRYPT |
+ KEYCTL_SUPPORTS_VERIFY;

ret = 0;
error_free_tfm:
@@ -487,6 +493,93 @@ static int tpm_key_eds_op(struct kernel_pkey_params *params,
return ret;
}

+/*
+ * Verify a signature using a public key.
+ */
+static int tpm_key_verify_signature(const struct key *key,
+ const struct public_key_signature *sig)
+{
+ const struct tpm_key *tk = key->payload.data[asym_crypto];
+ struct crypto_wait cwait;
+ struct crypto_akcipher *tfm;
+ struct akcipher_request *req;
+ struct scatterlist sig_sg, digest_sg;
+ char alg_name[CRYPTO_MAX_ALG_NAME];
+ uint8_t der_pub_key[PUB_KEY_BUF_SIZE];
+ uint32_t der_pub_key_len;
+ void *output;
+ unsigned int outlen;
+ int ret;
+
+ pr_devel("==>%s()\n", __func__);
+
+ BUG_ON(!tk);
+ BUG_ON(!sig);
+ BUG_ON(!sig->s);
+
+ if (!sig->digest)
+ return -ENOPKG;
+
+ ret = determine_akcipher(sig->encoding, sig->hash_algo, alg_name);
+ if (ret < 0)
+ return ret;
+
+ tfm = crypto_alloc_akcipher(alg_name, 0, 0);
+ if (IS_ERR(tfm))
+ return PTR_ERR(tfm);
+
+ der_pub_key_len = derive_pub_key(tk->pub_key, tk->pub_key_len,
+ der_pub_key);
+
+ ret = crypto_akcipher_set_pub_key(tfm, der_pub_key, der_pub_key_len);
+ if (ret < 0)
+ goto error_free_tfm;
+
+ ret = -ENOMEM;
+ req = akcipher_request_alloc(tfm, GFP_KERNEL);
+ if (!req)
+ goto error_free_tfm;
+
+ ret = -ENOMEM;
+ outlen = crypto_akcipher_maxsize(tfm);
+ output = kmalloc(outlen, GFP_KERNEL);
+ if (!output)
+ goto error_free_req;
+
+ sg_init_one(&sig_sg, sig->s, sig->s_size);
+ sg_init_one(&digest_sg, output, outlen);
+ akcipher_request_set_crypt(req, &sig_sg, &digest_sg, sig->s_size,
+ outlen);
+ crypto_init_wait(&cwait);
+ akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
+ CRYPTO_TFM_REQ_MAY_SLEEP,
+ crypto_req_done, &cwait);
+
+ /* Perform the verification calculation. This doesn't actually do the
+ * verification, but rather calculates the hash expected by the
+ * signature and returns that to us.
+ */
+ ret = crypto_wait_req(crypto_akcipher_verify(req), &cwait);
+ if (ret)
+ goto out_free_output;
+
+ /* Do the actual verification step. */
+ if (req->dst_len != sig->digest_size ||
+ memcmp(sig->digest, output, sig->digest_size) != 0)
+ ret = -EKEYREJECTED;
+
+out_free_output:
+ kfree(output);
+error_free_req:
+ akcipher_request_free(req);
+error_free_tfm:
+ crypto_free_akcipher(tfm);
+ pr_devel("<==%s() = %d\n", __func__, ret);
+ if (WARN_ON_ONCE(ret > 0))
+ ret = -EINVAL;
+ return ret;
+}
+
/*
* Parse enough information out of TPM_KEY structure:
* TPM_STRUCT_VER -> 4 bytes
@@ -645,6 +738,7 @@ struct asymmetric_key_subtype asym_tpm_subtype = {
.destroy = asym_tpm_destroy,
.query = tpm_key_query,
.eds_op = tpm_key_eds_op,
+ .verify_signature = tpm_key_verify_signature,
};
EXPORT_SYMBOL_GPL(asym_tpm_subtype);



2018-09-05 21:58:52

by David Howells

[permalink] [raw]
Subject: [PATCH 18/22] KEYS: asym_tpm: Implement tpm_unbind

From: Denis Kenzior <[email protected]>

Signed-off-by: Denis Kenzior <[email protected]>
Reviewed-by: James Morris <[email protected]>
Signed-off-by: David Howells <[email protected]>
---

crypto/asymmetric_keys/asym_tpm.c | 82 +++++++++++++++++++++++++++++++++++++
1 file changed, 82 insertions(+)

diff --git a/crypto/asymmetric_keys/asym_tpm.c b/crypto/asymmetric_keys/asym_tpm.c
index 6a2d33014ecc..e893b5212222 100644
--- a/crypto/asymmetric_keys/asym_tpm.c
+++ b/crypto/asymmetric_keys/asym_tpm.c
@@ -18,8 +18,10 @@

#define TPM_ORD_FLUSHSPECIFIC 186
#define TPM_ORD_LOADKEY2 65
+#define TPM_ORD_UNBIND 30
#define TPM_LOADKEY2_SIZE 59
#define TPM_FLUSHSPECIFIC_SIZE 18
+#define TPM_UNBIND_SIZE 63

#define TPM_RT_KEY 0x00000001

@@ -106,6 +108,86 @@ static int tpm_flushspecific(struct tpm_buf *tb, uint32_t handle)
return trusted_tpm_send(tb->data, MAX_BUF_SIZE);
}

+/*
+ * Decrypt a blob provided by userspace using a specific key handle.
+ * The handle is a well known handle or previously loaded by e.g. LoadKey2
+ */
+static int tpm_unbind(struct tpm_buf *tb,
+ uint32_t keyhandle, unsigned char *keyauth,
+ const unsigned char *blob, uint32_t bloblen,
+ void *out, uint32_t outlen)
+{
+ unsigned char nonceodd[TPM_NONCE_SIZE];
+ unsigned char enonce[TPM_NONCE_SIZE];
+ unsigned char authdata[SHA1_DIGEST_SIZE];
+ uint32_t authhandle = 0;
+ unsigned char cont = 0;
+ uint32_t ordinal;
+ uint32_t datalen;
+ int ret;
+
+ ordinal = htonl(TPM_ORD_UNBIND);
+ datalen = htonl(bloblen);
+
+ /* session for loading the key */
+ ret = oiap(tb, &authhandle, enonce);
+ if (ret < 0) {
+ pr_info("oiap failed (%d)\n", ret);
+ return ret;
+ }
+
+ /* generate odd nonce */
+ ret = tpm_get_random(NULL, nonceodd, TPM_NONCE_SIZE);
+ if (ret < 0) {
+ pr_info("tpm_get_random failed (%d)\n", ret);
+ return ret;
+ }
+
+ /* calculate authorization HMAC value */
+ ret = TSS_authhmac(authdata, keyauth, SHA1_DIGEST_SIZE, enonce,
+ nonceodd, cont, sizeof(uint32_t), &ordinal,
+ sizeof(uint32_t), &datalen,
+ bloblen, blob, 0, 0);
+ if (ret < 0)
+ return ret;
+
+ /* build the request buffer */
+ INIT_BUF(tb);
+ store16(tb, TPM_TAG_RQU_AUTH1_COMMAND);
+ store32(tb, TPM_UNBIND_SIZE + bloblen);
+ store32(tb, TPM_ORD_UNBIND);
+ store32(tb, keyhandle);
+ store32(tb, bloblen);
+ storebytes(tb, blob, bloblen);
+ store32(tb, authhandle);
+ storebytes(tb, nonceodd, TPM_NONCE_SIZE);
+ store8(tb, cont);
+ storebytes(tb, authdata, SHA1_DIGEST_SIZE);
+
+ ret = trusted_tpm_send(tb->data, MAX_BUF_SIZE);
+ if (ret < 0) {
+ pr_info("authhmac failed (%d)\n", ret);
+ return ret;
+ }
+
+ datalen = LOAD32(tb->data, TPM_DATA_OFFSET);
+
+ ret = TSS_checkhmac1(tb->data, ordinal, nonceodd,
+ keyauth, SHA1_DIGEST_SIZE,
+ sizeof(uint32_t), TPM_DATA_OFFSET,
+ datalen, TPM_DATA_OFFSET + sizeof(uint32_t),
+ 0, 0);
+ if (ret < 0) {
+ pr_info("TSS_checkhmac1 failed (%d)\n", ret);
+ return ret;
+ }
+
+ memcpy(out, tb->data + TPM_DATA_OFFSET + sizeof(uint32_t),
+ min(outlen, datalen));
+
+ return datalen;
+}
+
/*
* Maximum buffer size for the BER/DER encoded public key. The public key
* is of the form SEQUENCE { INTEGER n, INTEGER e } where n is a maximum 2048


2018-09-05 21:59:21

by David Howells

[permalink] [raw]
Subject: [PATCH 22/22] KEYS: asym_tpm: Add support for the sign operation

From: Denis Kenzior <[email protected]>

The sign operation can operate in a non-hashed mode by running the RSA
sign operation directly on the input. This assumes that the input is
less than key_size_in_bytes - 11. Since the TPM performs its own PKCS1
padding, it isn't possible to support 'raw' mode, only 'pkcs1'.

Alternatively, a hashed version is also possible. In this variant the
input is hashed (by userspace) via the selected hash function first.
Then this implementation takes care of converting the hash to ASN.1
format and the sign operation is performed on the result. This is
similar to the implementation inside crypto/rsa-pkcs1pad.c.

ASN1 templates were copied from crypto/rsa-pkcs1pad.c. There seems to
be no easy way to expose that functionality, but likely the templates
should be shared somehow.

The sign operation is implemented via TPM_Sign operation on the TPM.
It is assumed that the TPM wrapped key provided uses
TPM_SS_RSASSAPKCS1v15_DER signature scheme. This allows the TPM_Sign
operation to work on data up to key_len_in_bytes - 11 bytes long.

In theory, we could also use TPM_Unbind instead of TPM_Sign, but we would
have to manually pkcs1 pad the digest first.

Signed-off-by: Denis Kenzior <[email protected]>
Signed-off-by: David Howells <[email protected]>
---

crypto/asymmetric_keys/asym_tpm.c | 156 +++++++++++++++++++++++++++++++++++++
1 file changed, 155 insertions(+), 1 deletion(-)

diff --git a/crypto/asymmetric_keys/asym_tpm.c b/crypto/asymmetric_keys/asym_tpm.c
index a5a5f913a74f..5d4c270463f6 100644
--- a/crypto/asymmetric_keys/asym_tpm.c
+++ b/crypto/asymmetric_keys/asym_tpm.c
@@ -435,7 +435,8 @@ static int tpm_key_query(const struct kernel_pkey_params *params,

info->supported_ops = KEYCTL_SUPPORTS_ENCRYPT |
KEYCTL_SUPPORTS_DECRYPT |
- KEYCTL_SUPPORTS_VERIFY;
+ KEYCTL_SUPPORTS_VERIFY |
+ KEYCTL_SUPPORTS_SIGN;

ret = 0;
error_free_tfm:
@@ -556,6 +557,156 @@ static int tpm_key_decrypt(struct tpm_key *tk,
return r;
}

+/*
+ * Hash algorithm OIDs plus ASN.1 DER wrappings [RFC4880 sec 5.2.2].
+ */
+static const u8 digest_info_md5[] = {
+ 0x30, 0x20, 0x30, 0x0c, 0x06, 0x08,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, /* OID */
+ 0x05, 0x00, 0x04, 0x10
+};
+
+static const u8 digest_info_sha1[] = {
+ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05,
+ 0x2b, 0x0e, 0x03, 0x02, 0x1a,
+ 0x05, 0x00, 0x04, 0x14
+};
+
+static const u8 digest_info_rmd160[] = {
+ 0x30, 0x21, 0x30, 0x09, 0x06, 0x05,
+ 0x2b, 0x24, 0x03, 0x02, 0x01,
+ 0x05, 0x00, 0x04, 0x14
+};
+
+static const u8 digest_info_sha224[] = {
+ 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09,
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04,
+ 0x05, 0x00, 0x04, 0x1c
+};
+
+static const u8 digest_info_sha256[] = {
+ 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09,
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
+ 0x05, 0x00, 0x04, 0x20
+};
+
+static const u8 digest_info_sha384[] = {
+ 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09,
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02,
+ 0x05, 0x00, 0x04, 0x30
+};
+
+static const u8 digest_info_sha512[] = {
+ 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09,
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
+ 0x05, 0x00, 0x04, 0x40
+};
+
+static const struct asn1_template {
+ const char *name;
+ const u8 *data;
+ size_t size;
+} asn1_templates[] = {
+#define _(X) { #X, digest_info_##X, sizeof(digest_info_##X) }
+ _(md5),
+ _(sha1),
+ _(rmd160),
+ _(sha256),
+ _(sha384),
+ _(sha512),
+ _(sha224),
+ { NULL }
+#undef _
+};
+
+static const struct asn1_template *lookup_asn1(const char *name)
+{
+ const struct asn1_template *p;
+
+ for (p = asn1_templates; p->name; p++)
+ if (strcmp(name, p->name) == 0)
+ return p;
+ return NULL;
+}
+
+/*
+ * Sign operation is performed with the private key in the TPM.
+ */
+static int tpm_key_sign(struct tpm_key *tk,
+ struct kernel_pkey_params *params,
+ const void *in, void *out)
+{
+ struct tpm_buf *tb;
+ uint32_t keyhandle;
+ uint8_t srkauth[SHA1_DIGEST_SIZE];
+ uint8_t keyauth[SHA1_DIGEST_SIZE];
+ void *asn1_wrapped = NULL;
+ uint32_t in_len = params->in_len;
+ int r;
+
+ pr_devel("==>%s()\n", __func__);
+
+ if (strcmp(params->encoding, "pkcs1"))
+ return -ENOPKG;
+
+ if (params->hash_algo) {
+ const struct asn1_template *asn1 =
+ lookup_asn1(params->hash_algo);
+
+ if (!asn1)
+ return -ENOPKG;
+
+ /* request enough space for the ASN.1 template + input hash */
+ asn1_wrapped = kzalloc(in_len + asn1->size, GFP_KERNEL);
+ if (!asn1_wrapped)
+ return -ENOMEM;
+
+ /* Copy ASN.1 template, then the input */
+ memcpy(asn1_wrapped, asn1->data, asn1->size);
+ memcpy(asn1_wrapped + asn1->size, in, in_len);
+
+ in = asn1_wrapped;
+ in_len += asn1->size;
+ }
+
+ if (in_len > tk->key_len / 8 - 11) {
+ r = -EOVERFLOW;
+ goto error_free_asn1_wrapped;
+ }
+
+ r = -ENOMEM;
+ tb = kzalloc(sizeof(*tb), GFP_KERNEL);
+ if (!tb)
+ goto error_free_asn1_wrapped;
+
+ /* TODO: Handle a non-all zero SRK authorization */
+ memset(srkauth, 0, sizeof(srkauth));
+
+ r = tpm_loadkey2(tb, SRKHANDLE, srkauth,
+ tk->blob, tk->blob_len, &keyhandle);
+ if (r < 0) {
+ pr_devel("loadkey2 failed (%d)\n", r);
+ goto error_free_tb;
+ }
+
+ /* TODO: Handle a non-all zero key authorization */
+ memset(keyauth, 0, sizeof(keyauth));
+
+ r = tpm_sign(tb, keyhandle, keyauth, in, in_len, out, params->out_len);
+ if (r < 0)
+ pr_devel("tpm_sign failed (%d)\n", r);
+
+ if (tpm_flushspecific(tb, keyhandle) < 0)
+ pr_devel("flushspecific failed (%d)\n", r);
+
+error_free_tb:
+ kzfree(tb);
+error_free_asn1_wrapped:
+ kfree(asn1_wrapped);
+ pr_devel("<==%s() = %d\n", __func__, r);
+ return r;
+}
+
/*
* Do encryption, decryption and signing ops.
*/
@@ -573,6 +724,9 @@ static int tpm_key_eds_op(struct kernel_pkey_params *params,
case kernel_pkey_decrypt:
ret = tpm_key_decrypt(tk, params, in, out);
break;
+ case kernel_pkey_sign:
+ ret = tpm_key_sign(tk, params, in, out);
+ break;
default:
BUG();
}


2018-09-05 21:59:52

by David Howells

[permalink] [raw]
Subject: [PATCH 04/22] KEYS: Make the X.509 and PKCS7 parsers supply the sig encoding type

Make the X.509 and PKCS7 parsers fill in the signature encoding type field
recently added to the public_key_signature struct.

Signed-off-by: David Howells <[email protected]>
---

crypto/asymmetric_keys/pkcs7_parser.c | 1 +
crypto/asymmetric_keys/x509_cert_parser.c | 21 +++++++++------------
2 files changed, 10 insertions(+), 12 deletions(-)

diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys/pkcs7_parser.c
index 0f134162cef4..f0d56e1a8b7e 100644
--- a/crypto/asymmetric_keys/pkcs7_parser.c
+++ b/crypto/asymmetric_keys/pkcs7_parser.c
@@ -271,6 +271,7 @@ int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen,
switch (ctx->last_oid) {
case OID_rsaEncryption:
ctx->sinfo->sig->pkey_algo = "rsa";
+ ctx->sinfo->sig->encoding = "pkcs1";
break;
default:
printk("Unsupported pkey algo: %u\n", ctx->last_oid);
diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c
index b6cabac4b62b..991f4d735a4e 100644
--- a/crypto/asymmetric_keys/x509_cert_parser.c
+++ b/crypto/asymmetric_keys/x509_cert_parser.c
@@ -199,35 +199,32 @@ int x509_note_pkey_algo(void *context, size_t hdrlen,

case OID_md4WithRSAEncryption:
ctx->cert->sig->hash_algo = "md4";
- ctx->cert->sig->pkey_algo = "rsa";
- break;
+ goto rsa_pkcs1;

case OID_sha1WithRSAEncryption:
ctx->cert->sig->hash_algo = "sha1";
- ctx->cert->sig->pkey_algo = "rsa";
- break;
+ goto rsa_pkcs1;

case OID_sha256WithRSAEncryption:
ctx->cert->sig->hash_algo = "sha256";
- ctx->cert->sig->pkey_algo = "rsa";
- break;
+ goto rsa_pkcs1;

case OID_sha384WithRSAEncryption:
ctx->cert->sig->hash_algo = "sha384";
- ctx->cert->sig->pkey_algo = "rsa";
- break;
+ goto rsa_pkcs1;

case OID_sha512WithRSAEncryption:
ctx->cert->sig->hash_algo = "sha512";
- ctx->cert->sig->pkey_algo = "rsa";
- break;
+ goto rsa_pkcs1;

case OID_sha224WithRSAEncryption:
ctx->cert->sig->hash_algo = "sha224";
- ctx->cert->sig->pkey_algo = "rsa";
- break;
+ goto rsa_pkcs1;
}

+rsa_pkcs1:
+ ctx->cert->sig->pkey_algo = "rsa";
+ ctx->cert->sig->encoding = "pkcs1";
ctx->algo_oid = ctx->last_oid;
return 0;
}


2018-09-06 00:09:36

by James Morris

[permalink] [raw]
Subject: Re: [PATCH 00/22] KEYS: Support TPM-wrapped key and crypto ops

On Wed, 5 Sep 2018, David Howells wrote:

>
> Hi James,
>
> Here's a set of patches that does the following, if you could pull it please:

Thanks, it would be good to see more review / acks, though.

--
James Morris
<[email protected]>


2018-09-07 17:33:51

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [PATCH 00/22] KEYS: Support TPM-wrapped key and crypto ops

Hi James,

>> Here's a set of patches that does the following, if you could pull it please:
>
> Thanks, it would be good to see more review / acks, though.

so I have reviewed and tested this code. In addition, we have test cases for it in ELL (embedded linux library).

Tested-by: Marcel Holtmann <[email protected]>
Reviewed-by: Marcel Holtmann <[email protected]>

Regards

Marcel


2018-09-07 17:34:37

by James Morris

[permalink] [raw]
Subject: Re: [PATCH 00/22] KEYS: Support TPM-wrapped key and crypto ops

On Fri, 7 Sep 2018, Marcel Holtmann wrote:

> Hi James,
>
> >> Here's a set of patches that does the following, if you could pull it please:
> >
> > Thanks, it would be good to see more review / acks, though.
>
> so I have reviewed and tested this code. In addition, we have test cases for it in ELL (embedded linux library).
>
> Tested-by: Marcel Holtmann <[email protected]>
> Reviewed-by: Marcel Holtmann <[email protected]>

Thanks!

--
James Morris
<[email protected]>


2018-09-08 15:27:37

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 00/22] KEYS: Support TPM-wrapped key and crypto ops

Marcel Holtmann <[email protected]> wrote:

> so I have reviewed and tested this code. In addition, we have test cases for
> it in ELL (embedded linux library).

I wonder if there's any practical way to add a test for this to the keyutils
test suite. I'm guessing it's quite tricky, given the extra bits you need to
emulate the TPM.

David

2018-09-18 07:00:24

by David Woodhouse

[permalink] [raw]
Subject: Re: [PATCH 00/22] KEYS: Support TPM-wrapped key and crypto ops

On Wed, 2018-09-05 at 22:54 +0100, David Howells wrote:
>
> Example usage for a PKCS#8 blob:
>
>         j=`openssl pkcs8 -in private_key.pem -topk8 -nocrypt -outform DER | \
>             keyctl padd asymmetric foo @s`
>
> Example usage for a TPM wrapped blob:
>
>         openssl genrsa -out /tmp/privkey.foo.pem 2048
>         create_tpm_key -s 2048 -w /tmp/privkey.foo.pem /tmp/privkey.foo.tpm
>         j=`openssl asn1parse -inform pem -in /tmp/privkey.foo.tpm -noout |
>             keyctl padd asymmetric foo @s`

Those examples aren't equivalent. For the PKCS#8 blob you are first
using openssl to convert from an encrypted PKCS#8 PEM to unencrypted
DER, presumably because you haven't added decryption support (or base64
decode) to keyctl yet.

For the TPM example though, you are also showing the *generation* of
the key, and importing it into the TPM. And then I'm confused by the
'openssl asn1parse' line there... what is that actually doing? If I run
it on a '-----BEGIN TSS KEY BLOB-----' file I have lying around, I get
no output at all.


Attachments:
smime.p7s (5.09 kB)

2018-09-18 07:03:08

by David Woodhouse

[permalink] [raw]
Subject: Re: [PATCH 00/22] KEYS: Support TPM-wrapped key and crypto ops



On Sat, 2018-09-08 at 16:26 +0100, David Howells wrote:
> Marcel Holtmann <[email protected]> wrote:
>
> >
> > so I have reviewed and tested this code. In addition, we have test cases for
> > it in ELL (embedded linux library).
>
> I wonder if there's any practical way to add a test for this to the keyutils
> test suite.  I'm guessing it's quite tricky, given the extra bits you need to
> emulate the TPM.

Right, for a lot of userspace stuff we have the TPM emulator but for
the kernel you might need to run in qemu, which I believe can emulate a
TPM now (or at least, can talk to the TPM emulator, which has the same
effect).


Attachments:
smime.p7s (5.09 kB)

2018-09-18 11:30:36

by James Bottomley

[permalink] [raw]
Subject: Re: [PATCH 00/22] KEYS: Support TPM-wrapped key and crypto ops

On Tue, 2018-09-18 at 08:00 +0100, David Woodhouse wrote:
>
> On Sat, 2018-09-08 at 16:26 +0100, David Howells wrote:
> > Marcel Holtmann <[email protected]> wrote:
> >
> > >
> > > so I have reviewed and tested this code. In addition, we have
> > > test cases for it in ELL (embedded linux library).
> >
> > I wonder if there's any practical way to add a test for this to the
> > keyutils test suite.  I'm guessing it's quite tricky, given the
> > extra bits you need to emulate the TPM.
>
> Right, for a lot of userspace stuff we have the TPM emulator but for
> the kernel you might need to run in qemu, which I believe can emulate
> a TPM now (or at least, can talk to the TPM emulator, which has the
> same effect).

Actually, you don't necessarily. I use this patch:

https://marc.info/?l=tpmdd-devel&m=148392353230117

Which allows me to make a TCP connection to the software TPM running in
userspace without having to have the TPM components in qemu (or even to
run virtual). I used it to debug all the in-kernel resource manager
patches. It's TPM 2.0, but could easily be modified to work with 1.2

James


2018-09-18 15:03:37

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 00/22] KEYS: Support TPM-wrapped key and crypto ops

David Woodhouse <[email protected]> wrote:

> Those examples aren't equivalent

No one said that they are.

But if you really can't figure it out, I can add:

openssl genrsa -out private_key.pem 2048

at the front of the PKCS#8 example;-)

I can even change the examples to have the same private key name.

> For the PKCS#8 blob you are first using openssl to convert from an encrypted
> PKCS#8 PEM to unencrypted DER, presumably because you haven't added
> decryption support (or base64 decode) to keyctl yet.

It would probably be done in the kernel rather than keyctl. If it's done in
userspace, there's no need to do it in keyctl. The PKCS#8 parser is primarily
a test port - especially as it doesn't need anything like a TPM.

It's of limited real utility, I think, because even if the blob is encrypted
and you can pass the decryption password to the kernel, the password still has
to be present in userspace, however briefly.

> For the TPM example though, you are also showing the *generation* of
> the key, and importing it into the TPM. And then I'm confused by the
> 'openssl asn1parse' line there... what is that actually doing?

It's meant to be stripping off the PEM wrapper and outputting the DER, but see
below.

> If I run it on a '-----BEGIN TSS KEY BLOB-----' file I have lying around, I
> get no output at all.

I lost a bit from the cover note. It needs "-out -" attaching.

openssl asn1parse -inform pem -in modulekey1.priv -noout -out - | wc -c

David

2018-09-18 15:23:56

by Denis Kenzior

[permalink] [raw]
Subject: Re: [PATCH 00/22] KEYS: Support TPM-wrapped key and crypto ops

Hi David,

On 09/18/2018 01:59 AM, David Woodhouse wrote:
> On Wed, 2018-09-05 at 22:54 +0100, David Howells wrote:
>>
>> Example usage for a PKCS#8 blob:
>>
>>         j=`openssl pkcs8 -in private_key.pem -topk8 -nocrypt -outform DER | \
>>             keyctl padd asymmetric foo @s`

The kernel expects a raw DER formatted PKCS8 certificate. And as you
point out, keyctl doesn't grok PEM files. So, that is why this is being
done via openssl. The example above simply shows one how to import a
private key in PEM format into the kernel keys framework.

>>
>> Example usage for a TPM wrapped blob:
>>
>>         openssl genrsa -out /tmp/privkey.foo.pem 2048
>>         create_tpm_key -s 2048 -w /tmp/privkey.foo.pem /tmp/privkey.foo.tpm
>>         j=`openssl asn1parse -inform pem -in /tmp/privkey.foo.tpm -noout |
>>             keyctl padd asymmetric foo @s`
>
> Those examples aren't equivalent. For the PKCS#8 blob you are first
> using openssl to convert from an encrypted PKCS#8 PEM to unencrypted
> DER, presumably because you haven't added decryption support (or base64
> decode) to keyctl yet.

To be pedantic, it converts an optionally encrypted PEM to unencrypted
DER. But yes, correct.

>
> For the TPM example though, you are also showing the *generation* of
> the key, and importing it into the TPM. And then I'm confused by the
> 'openssl asn1parse' line there... what is that actually doing? If I run
> it on a '-----BEGIN TSS KEY BLOB-----' file I have lying around, I get
> no output at all.
>

Same thing applies as above. The kernel has no PEM parser, so the raw
DER must be passed in. openssl asn1parse line simply does that. It
strips the PEM layer leaving the raw DER.

However, now that you mention it, the actual command incantation is
wrong. It seems openssl asn1parse acts slightly different from openssl
pkcs8 and so it needs to be modified to add an extra -out parameter. So
the example incantation should be:

openssl genrsa -out /tmp/privkey.2048.pem 2048
create_tpm_key -s 2048 -w /tmp/privkey.2048.pem /tmp/privkey.2048.tpm
openssl asn1parse -inform pem -in /tmp/privkey.2048.tpm -noout \
-out /tmp/privkey.2048.der
j=`cat /tmp/privkey.2048.der | keyctl padd asymmetric tpm @u`
echo "TPM key serial is: $j"

Sorry, I should have caught this earlier.

Regards,
-Denis

2018-09-18 15:28:07

by David Woodhouse

[permalink] [raw]
Subject: Re: [PATCH 00/22] KEYS: Support TPM-wrapped key and crypto ops

On Tue, 2018-09-18 at 16:02 +0100, David Howells wrote:
> It's meant to be stripping off the PEM wrapper and outputting the DER, but see
> below.
>
> > If I run it on a '-----BEGIN TSS KEY BLOB-----' file I have lying around, I
> > get no output at all.
>
> I lost a bit from the cover note. It needs "-out -" attaching.
>
> openssl asn1parse -inform pem -in modulekey1.priv -noout -out - | wc -c

Ah, OK. So it's just a complex base64 decoder :)


Attachments:
smime.p7s (5.09 kB)

2018-09-18 15:51:13

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 00/22] KEYS: Support TPM-wrapped key and crypto ops

Denis Kenzior <[email protected]> wrote:

> openssl asn1parse -inform pem -in /tmp/privkey.2048.tpm -noout \
> -out /tmp/privkey.2048.der

You can use "... -out - | ..." instead.

David

2018-09-18 16:12:34

by Denis Kenzior

[permalink] [raw]
Subject: Re: [PATCH 00/22] KEYS: Support TPM-wrapped key and crypto ops

Hi David,

On 09/18/2018 10:50 AM, David Howells wrote:
> Denis Kenzior <[email protected]> wrote:
>
>> openssl asn1parse -inform pem -in /tmp/privkey.2048.tpm -noout \
>> -out /tmp/privkey.2048.der
>
> You can use "... -out - | ..." instead.

Aha! okay, that is even more elegant. Your openssl-fu is better than
mine :)

Regards,
-Denis


2018-09-18 16:18:01

by David Woodhouse

[permalink] [raw]
Subject: Re: [PATCH 00/22] KEYS: Support TPM-wrapped key and crypto ops

On Tue, 2018-09-18 at 00:24 -0500, Denis Kenzior wrote:
> Hi David,
>
> On 09/18/2018 10:50 AM, David Howells wrote:
> > Denis Kenzior <[email protected]> wrote:
> >
> > > openssl asn1parse -inform pem -in /tmp/privkey.2048.tpm -noout \
> > > -out /tmp/privkey.2048.der
> >
> > You can use "... -out - | ..." instead.
>
> Aha! okay, that is even more elegant. Your openssl-fu is better than
> mine :)

'grep -v ^----- | base64 -d' also works most of the time :)

You are passing the raw DER to the kernel in both cases, right? And the
kernel just happens to know that if it receives a bare OCTET-STRING
it's supposed to treat it as a TPMv1.2 key?


Attachments:
smime.p7s (5.09 kB)

2018-09-18 16:31:08

by Denis Kenzior

[permalink] [raw]
Subject: Re: [PATCH 00/22] KEYS: Support TPM-wrapped key and crypto ops

Hi David,

On 09/18/2018 11:17 AM, David Woodhouse wrote:
> On Tue, 2018-09-18 at 00:24 -0500, Denis Kenzior wrote:
>> Hi David,
>>
>> On 09/18/2018 10:50 AM, David Howells wrote:
>>> Denis Kenzior <[email protected]> wrote:
>>>
>>>> openssl asn1parse -inform pem -in /tmp/privkey.2048.tpm -noout \
>>>> -out /tmp/privkey.2048.der
>>>
>>> You can use "... -out - | ..." instead.
>>
>> Aha! okay, that is even more elegant. Your openssl-fu is better than
>> mine :)
>
> 'grep -v ^----- | base64 -d' also works most of the time :)
>
> You are passing the raw DER to the kernel in both cases, right? And the
> kernel just happens to know that if it receives a bare OCTET-STRING
> it's supposed to treat it as a TPMv1.2 key?
>

Short answer: right.

Long answer: The kernel runs all the registered parsers until all fail
or one of them recognizes the format. All the currently supported
asymmetric key formats are DER based, e.g. PKCS8, PKCS7, TPM-1.2, etc.
All these have a very specific DER structure with the TPM-1.2 being the
simplest format.

Regards,
-Denis

2018-09-18 16:35:27

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 00/22] KEYS: Support TPM-wrapped key and crypto ops

David Woodhouse <[email protected]> wrote:

> You are passing the raw DER to the kernel in both cases, right?

I haven't put a PEM stripper and base64 decoder into the kernel, though we
certainly could do that. We could even do the decode in-place in the same
buffer since the resulting data would be smaller than the original.

> And the kernel just happens to know that if it receives a bare OCTET-STRING
> it's supposed to treat it as a TPMv1.2 key?

It passes it to each parser in turn till one says it can parse it. It's not
ideal, but it seems to work - so far. Better would be to annotate it in some
way. I have considered annotating the type field so that the payload doesn't
have to have it added:

keyctl padd asymmetric.x509 "" @s </tmp/foo.x509
keyctl padd asymmetric.pkcs#8 ...
keyctl padd asymmetric.tpm ...

However, this doesn't work with "keyctl update" or "keyctl instantiate".

David

2018-09-18 16:40:32

by Denis Kenzior

[permalink] [raw]
Subject: Re: [PATCH 00/22] KEYS: Support TPM-wrapped key and crypto ops

Hi David,

> It passes it to each parser in turn till one says it can parse it. It's not
> ideal, but it seems to work - so far. Better would be to annotate it in some
> way. I have considered annotating the type field so that the payload doesn't
> have to have it added:
>
> keyctl padd asymmetric.x509 "" @s </tmp/foo.x509
> keyctl padd asymmetric.pkcs#8 ...
> keyctl padd asymmetric.tpm ...
>
> However, this doesn't work with "keyctl update" or "keyctl instantiate".
>

In theory the PEM file already contains the type of the certificate, at
least at a high level. E.g. private, public, tpm. So if we accept PEM
files directly that could be potentially a faster way of determining the
parser to use and would still work with keyctl update/instantiate, right?

Regards,
-Denis

2018-09-18 16:56:46

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 00/22] KEYS: Support TPM-wrapped key and crypto ops

Denis Kenzior <[email protected]> wrote:

> In theory the PEM file already contains the type of the certificate, at least
> at a high level. E.g. private, public, tpm. So if we accept PEM files
> directly that could be potentially a faster way of determining the parser to
> use and would still work with keyctl update/instantiate, right?

Yes. It shouldn't be much code, either. You still have to check for X.509
DER since the kernel currently supports that.

David

2018-09-18 17:01:10

by Denis Kenzior

[permalink] [raw]
Subject: Re: [PATCH 00/22] KEYS: Support TPM-wrapped key and crypto ops

Hi David,

On 09/18/2018 11:55 AM, David Howells wrote:
> Denis Kenzior <[email protected]> wrote:
>
>> In theory the PEM file already contains the type of the certificate, at least
>> at a high level. E.g. private, public, tpm. So if we accept PEM files
>> directly that could be potentially a faster way of determining the parser to
>> use and would still work with keyctl update/instantiate, right?
>
> Yes. It shouldn't be much code, either. You still have to check for X.509
> DER since the kernel currently supports that.

For reasons of backward compatibility, correct? The kernel also has
mscode.asn1 which we would need to support as well. Since we can't
break compatibility then perhaps this doesn't buy us a whole lot in the end.

Regards,
-Denis

2018-09-18 17:19:06

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 00/22] KEYS: Support TPM-wrapped key and crypto ops

Denis Kenzior <[email protected]> wrote:

> > Yes. It shouldn't be much code, either. You still have to check for X.509
> > DER since the kernel currently supports that.
>
> For reasons of backward compatibility, correct? The kernel also has
> mscode.asn1 which we would need to support as well. Since we can't break
> compatibility then perhaps this doesn't buy us a whole lot in the end.

Don't worry about mscode - that's not an asymmetric key parser. That's only
ever used directly from verify_pefile_signature().

Currently, we have to retain support for DER-encoded X.509.

But there's no reason we can't have a PEM parser that decodes the PEM and
selects X.509, PKCS#8 or TPM based on the ascii header in that. PKCS#8 and
TPM don't need to take DER directly.

David


2018-09-20 07:26:55

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [PATCH 00/22] KEYS: Support TPM-wrapped key and crypto ops

Hi David,

>>> Yes. It shouldn't be much code, either. You still have to check for X.509
>>> DER since the kernel currently supports that.
>>
>> For reasons of backward compatibility, correct? The kernel also has
>> mscode.asn1 which we would need to support as well. Since we can't break
>> compatibility then perhaps this doesn't buy us a whole lot in the end.
>
> Don't worry about mscode - that's not an asymmetric key parser. That's only
> ever used directly from verify_pefile_signature().
>
> Currently, we have to retain support for DER-encoded X.509.
>
> But there's no reason we can't have a PEM parser that decodes the PEM and
> selects X.509, PKCS#8 or TPM based on the ascii header in that. PKCS#8 and
> TPM don't need to take DER directly.

since we have to support DER-encoded anyway, can we get the current patches merged (with fixes to the commit messages for the openssl examples if needed) and then work on PEM support inside the kernel. For me these seems to be two independent features. And in the current form the patches have been tested and used.

Or let me ask this differently, are there any objections to merging these patches with just DER support?

For example in ELL we have implemented the PEM to DER conversion before calling keyctl. This can be surely added to keyutils (if calling openssl is too much to ask) or even the kernel itself. However this doesn’t change the actual functionality of handling asymmetric key operations. And I would rather see that merged and established instead of worrying to much about the multitudes of possible encoding forms of keys.

Regards

Marcel


2018-09-20 16:46:05

by David Woodhouse

[permalink] [raw]
Subject: Re: [PATCH 00/22] KEYS: Support TPM-wrapped key and crypto ops

On Thu, 2018-09-20 at 09:26 +0200, Marcel Holtmann wrote:
> Hi David,
>
> > > > Yes. It shouldn't be much code, either. You still have to check for X.509
> > > > DER since the kernel currently supports that.
> > >
> > > For reasons of backward compatibility, correct? The kernel also has
> > > mscode.asn1 which we would need to support as well. Since we can't break
> > > compatibility then perhaps this doesn't buy us a whole lot in the end.
> >
> > Don't worry about mscode - that's not an asymmetric key parser. That's only
> > ever used directly from verify_pefile_signature().
> >
> > Currently, we have to retain support for DER-encoded X.509.
> >
> > But there's no reason we can't have a PEM parser that decodes the PEM and
> > selects X.509, PKCS#8 or TPM based on the ascii header in that. PKCS#8 and
> > TPM don't need to take DER directly.
>
> since we have to support DER-encoded anyway, can we get the current
> patches merged (with fixes to the commit messages for the openssl
> examples if needed) and then work on PEM support inside the kernel.
> For me these seems to be two independent features. And in the current
> form the patches have been tested and used.
>
> Or let me ask this differently, are there any objections to merging
> these patches with just DER support?

Let me rephrase that question slightly: Are we happy to have to make
inferences from the ASN.1 structure, and in particular that a bare
OCTET-STRING is a TPMv1 blob? I believe James ended up doing something
somewhat more sensible for the TPMv2 blob so that might end up being
OK...?


Attachments:
smime.p7s (5.09 kB)

2018-09-20 17:08:34

by Denis Kenzior

[permalink] [raw]
Subject: Re: [PATCH 00/22] KEYS: Support TPM-wrapped key and crypto ops

David,

On 09/20/2018 11:45 AM, David Woodhouse wrote:
> On Thu, 2018-09-20 at 09:26 +0200, Marcel Holtmann wrote:
>> Hi David,
>>
>>>>> Yes. It shouldn't be much code, either. You still have to check for X.509
>>>>> DER since the kernel currently supports that.
>>>>
>>>> For reasons of backward compatibility, correct? The kernel also has
>>>> mscode.asn1 which we would need to support as well. Since we can't break
>>>> compatibility then perhaps this doesn't buy us a whole lot in the end.
>>>
>>> Don't worry about mscode - that's not an asymmetric key parser. That's only
>>> ever used directly from verify_pefile_signature().
>>>
>>> Currently, we have to retain support for DER-encoded X.509.
>>>
>>> But there's no reason we can't have a PEM parser that decodes the PEM and
>>> selects X.509, PKCS#8 or TPM based on the ascii header in that. PKCS#8 and
>>> TPM don't need to take DER directly.
>>
>> since we have to support DER-encoded anyway, can we get the current
>> patches merged (with fixes to the commit messages for the openssl
>> examples if needed) and then work on PEM support inside the kernel.
>> For me these seems to be two independent features. And in the current
>> form the patches have been tested and used.
>>
>> Or let me ask this differently, are there any objections to merging
>> these patches with just DER support?
>
> Let me rephrase that question slightly: Are we happy to have to make
> inferences from the ASN.1 structure, and in particular that a bare
> OCTET-STRING is a TPMv1 blob? I believe James ended up doing something
> somewhat more sensible for the TPMv2 blob so that might end up being
> OK...?
>

I think it should be OK. It is highly unlikely that we get another
OCTET string type format, and even then we can actually peek inside and
try to parse the TPM structure carried in the DER if there is indeed a
conflict.

Also, after refreshing my memory of how PEM format is actually
specified, I'm no longer convinced that having it in the kernel is such
a good idea. It would probably be made to work, but the PEM file format
itself isn't that precise, at least compared to DER.

James, is there a document / official place with your TPMKey ASN.1
format? The only reference I can find is here:
https://kernel.googlesource.com/pub/scm/linux/kernel/git/jejb/openssl_tpm2_engine/+/93b2f4f3f0f2260292f54de4e6333219063c77b1/tpm2-asn.h

Regards,
-Denis

2018-09-28 17:22:05

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [PATCH 00/22] KEYS: Support TPM-wrapped key and crypto ops

Hi David,

>>>>> Yes. It shouldn't be much code, either. You still have to check for X.509
>>>>> DER since the kernel currently supports that.
>>>>
>>>> For reasons of backward compatibility, correct? The kernel also has
>>>> mscode.asn1 which we would need to support as well. Since we can't break
>>>> compatibility then perhaps this doesn't buy us a whole lot in the end.
>>>
>>> Don't worry about mscode - that's not an asymmetric key parser. That's only
>>> ever used directly from verify_pefile_signature().
>>>
>>> Currently, we have to retain support for DER-encoded X.509.
>>>
>>> But there's no reason we can't have a PEM parser that decodes the PEM and
>>> selects X.509, PKCS#8 or TPM based on the ascii header in that. PKCS#8 and
>>> TPM don't need to take DER directly.
>>
>> since we have to support DER-encoded anyway, can we get the current
>> patches merged (with fixes to the commit messages for the openssl
>> examples if needed) and then work on PEM support inside the kernel.
>> For me these seems to be two independent features. And in the current
>> form the patches have been tested and used.
>>
>> Or let me ask this differently, are there any objections to merging
>> these patches with just DER support?
>
> Let me rephrase that question slightly: Are we happy to have to make
> inferences from the ASN.1 structure, and in particular that a bare
> OCTET-STRING is a TPMv1 blob? I believe James ended up doing something
> somewhat more sensible for the TPMv2 blob so that might end up being
> OK...?

similar to Denis’ comment, I don’t see an issue here with using DER encoding.

James, can you take this series into your -next tree?

Regards

Marcel


2018-10-03 19:04:46

by James Morris

[permalink] [raw]
Subject: Re: [PATCH 03/22] KEYS: Provide missing asymmetric key subops for new key type ops

On Wed, 5 Sep 2018, David Howells wrote:

> +int asymmetric_key_eds_op(struct kernel_pkey_params *params,
> + const void *in, void *out)
> +{
> + const struct asymmetric_key_subtype *subtype;
> + struct key *key = params->key;
> + int ret;
> +
> + pr_devel("==>%s()\n", __func__);

Are you planning on leaving these pr_devel()s in?

--
James Morris
<[email protected]>


2018-10-03 19:13:22

by James Morris

[permalink] [raw]
Subject: Re: [PATCH 04/22] KEYS: Make the X.509 and PKCS7 parsers supply the sig encoding type

On Wed, 5 Sep 2018, David Howells wrote:

> diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c
> index b6cabac4b62b..991f4d735a4e 100644
> --- a/crypto/asymmetric_keys/x509_cert_parser.c
> +++ b/crypto/asymmetric_keys/x509_cert_parser.c
> @@ -199,35 +199,32 @@ int x509_note_pkey_algo(void *context, size_t hdrlen,
>
> case OID_md4WithRSAEncryption:
> ctx->cert->sig->hash_algo = "md4";
> - ctx->cert->sig->pkey_algo = "rsa";
> - break;
> + goto rsa_pkcs1;
>
> case OID_sha1WithRSAEncryption:
> ctx->cert->sig->hash_algo = "sha1";
> - ctx->cert->sig->pkey_algo = "rsa";
> - break;
> + goto rsa_pkcs1;
>
> case OID_sha256WithRSAEncryption:
> ctx->cert->sig->hash_algo = "sha256";
> - ctx->cert->sig->pkey_algo = "rsa";
> - break;
> + goto rsa_pkcs1;
>
> case OID_sha384WithRSAEncryption:
> ctx->cert->sig->hash_algo = "sha384";
> - ctx->cert->sig->pkey_algo = "rsa";
> - break;
> + goto rsa_pkcs1;
>
> case OID_sha512WithRSAEncryption:
> ctx->cert->sig->hash_algo = "sha512";
> - ctx->cert->sig->pkey_algo = "rsa";
> - break;
> + goto rsa_pkcs1;
>
> case OID_sha224WithRSAEncryption:
> ctx->cert->sig->hash_algo = "sha224";
> - ctx->cert->sig->pkey_algo = "rsa";
> - break;
> + goto rsa_pkcs1;
> }
>
> +rsa_pkcs1:
> + ctx->cert->sig->pkey_algo = "rsa";
> + ctx->cert->sig->encoding = "pkcs1";
> ctx->algo_oid = ctx->last_oid;
> return 0;
> }

Perhaps I'm missing something here but why do you need the gotos vs. just
breaking to this code?


--
James Morris
<[email protected]>


2018-10-03 19:24:33

by James Morris

[permalink] [raw]
Subject: Re: [PATCH 05/22] KEYS: Provide software public key query function

On Wed, 5 Sep 2018, David Howells wrote:

> + if (strcmp(encoding, "raw") == 0) {
> + strcpy(alg_name, pkey->pkey_algo);
> + return 0;
> + }

Can encoding here also be NULL to indicate raw? per patch 01:

> +struct kernel_pkey_params {
> + struct key *key;
> + const char *encoding; /* Encoding (eg. "oaep" or NULL for raw) */


--
James Morris
<[email protected]>


2018-10-05 15:40:27

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 03/22] KEYS: Provide missing asymmetric key subops for new key type ops

James Morris <[email protected]> wrote:

> > + pr_devel("==>%s()\n", __func__);
>
> Are you planning on leaving these pr_devel()s in?

Well, they don't do anything unless you #define DEBUG at the top of the file.

If they really offend you, I can remove them.

David

2018-10-05 15:45:37

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 04/22] KEYS: Make the X.509 and PKCS7 parsers supply the sig encoding type

James Morris <[email protected]> wrote:

> Perhaps I'm missing something here but why do you need the gotos vs. just
> breaking to this code?

Because at some point we might add support for some other public key
algorithm, such as EC. This makes it clearer that that piece of code is
specific to a certain set of algorithm identifiers.

David

2018-10-05 15:52:06

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 05/22] KEYS: Provide software public key query function

James Morris <[email protected]> wrote:

> > + if (strcmp(encoding, "raw") == 0) {
> > + strcpy(alg_name, pkey->pkey_algo);
> > + return 0;
> > + }
>
> Can encoding here also be NULL to indicate raw? per patch 01:

Ummm... By the letter of the documentation, yes, though in practice it isn't
(it is defaulted to "raw") in the keyctl interface. I wonder if it would be
better to require it to say "raw" and never be NULL.

David

2018-10-05 16:28:42

by James Morris

[permalink] [raw]
Subject: Re: [PATCH 05/22] KEYS: Provide software public key query function

On Fri, 5 Oct 2018, David Howells wrote:

> James Morris <[email protected]> wrote:
>
> > > + if (strcmp(encoding, "raw") == 0) {
> > > + strcpy(alg_name, pkey->pkey_algo);
> > > + return 0;
> > > + }
> >
> > Can encoding here also be NULL to indicate raw? per patch 01:
>
> Ummm... By the letter of the documentation, yes, though in practice it isn't
> (it is defaulted to "raw") in the keyctl interface. I wonder if it would be
> better to require it to say "raw" and never be NULL.

Probably.

--
James Morris
<[email protected]>


2018-10-05 18:24:31

by James Morris

[permalink] [raw]
Subject: Re: [PATCH 05/22] KEYS: Provide software public key query function

On Sat, 6 Oct 2018, James Morris wrote:

> On Fri, 5 Oct 2018, David Howells wrote:
>
> > James Morris <[email protected]> wrote:
> >
> > > > + if (strcmp(encoding, "raw") == 0) {
> > > > + strcpy(alg_name, pkey->pkey_algo);
> > > > + return 0;
> > > > + }
> > >
> > > Can encoding here also be NULL to indicate raw? per patch 01:
> >
> > Ummm... By the letter of the documentation, yes, though in practice it isn't
> > (it is defaulted to "raw") in the keyctl interface. I wonder if it would be
> > better to require it to say "raw" and never be NULL.
>
> Probably.

If you respin the patches please add all the signoffs.

--
James Morris
<[email protected]>


2018-10-09 15:23:59

by David Howells

[permalink] [raw]
Subject: Re: [PATCH 05/22] KEYS: Provide software public key query function

James Morris <[email protected]> wrote:

> > + if (strcmp(encoding, "raw") == 0) {
> > + strcpy(alg_name, pkey->pkey_algo);
> > + return 0;
> > + }
>
> Can encoding here also be NULL to indicate raw? per patch 01:

Okay, I'm making the attached changes to patch 01. They're
documentary/commentary only.

David
---
diff --git a/Documentation/security/keys/core.rst b/Documentation/security/keys/core.rst
index 2cdccde6c585..c144978479d5 100644
--- a/Documentation/security/keys/core.rst
+++ b/Documentation/security/keys/core.rst
@@ -1507,12 +1507,12 @@ The structure has a number of fields, some of which are mandatory:
enum kernel_pkey_operation op : 8;
};

- This includes the key to be used; an optional string indicating the
- encoding to use (for instance, "pkcs1" may be used with an RSA key to
- indicate RSASSA-PKCS1-v1.5 or RSAES-PKCS1-v1.5 encoding); the name of the
- hash algorithm used to generate the data for a signature (if appropriate);
- the sizes of the input and output (or second input) buffers; and the ID of
- the operation to be performed.
+ This includes the key to be used; a string indicating the encoding to use
+ (for instance, "pkcs1" may be used with an RSA key to indicate
+ RSASSA-PKCS1-v1.5 or RSAES-PKCS1-v1.5 encoding or "raw" if no encoding);
+ the name of the hash algorithm used to generate the data for a signature
+ (if appropriate); the sizes of the input and output (or second input)
+ buffers; and the ID of the operation to be performed.

For a given operation ID, the input and output buffers are used as
follows::
diff --git a/include/linux/keyctl.h b/include/linux/keyctl.h
index e89b4a4e43d0..c7c48c79ce0e 100644
--- a/include/linux/keyctl.h
+++ b/include/linux/keyctl.h
@@ -32,7 +32,7 @@ enum kernel_pkey_operation {

struct kernel_pkey_params {
struct key *key;
- const char *encoding; /* Encoding (eg. "oaep" or NULL for raw) */
+ const char *encoding; /* Encoding (eg. "oaep" or "raw" for none) */
const char *hash_algo; /* Digest algorithm used (eg. "sha1") or NULL if N/A */
char *info; /* Modified info string to be released later */
__u32 in_len; /* Input data size */