From 70025f84e5b79627a6739533c4fe7cef5b605886 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 9 Oct 2018 17:46:51 +0100 Subject: [PATCH 01/22] KEYS: Provide key type operations for asymmetric key ops [ver #2] 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 Tested-by: Marcel Holtmann Reviewed-by: Marcel Holtmann Reviewed-by: Denis Kenzior Tested-by: Denis Kenzior Signed-off-by: James Morris --- 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..c144978479d5 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; 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:: + + 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..c7c48c79ce0e --- /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 (dhowells@redhat.com) + * + * 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 + +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 "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 */ + 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 0f3cb13db8e9..1d1e9f2877af 100644 --- a/include/uapi/linux/keyctl.h +++ b/include/uapi/linux/keyctl.h @@ -82,4 +82,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 */ From 00d60fd3b93219ea854220f0fd264b86398cbc53 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 9 Oct 2018 17:46:59 +0100 Subject: [PATCH 02/22] KEYS: Provide keyctls to drive the new key type ops for asymmetric keys [ver #2] 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 Tested-by: Marcel Holtmann Reviewed-by: Marcel Holtmann Reviewed-by: Denis Kenzior Tested-by: Denis Kenzior Signed-off-by: James Morris --- 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 c144978479d5..9521c4207f01 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=`` 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=`` 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 1d1e9f2877af..f45ee0f69c0c 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 */ @@ -87,4 +92,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 (dhowells@redhat.com) + * + * 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 +#include +#include +#include +#include +#include +#include +#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=" eg. "enc=oaep" */ + Opt_hash, /* "hash=" 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(¶ms, 0, sizeof(params)); + + ret = keyctl_pkey_params_get(id, _info, ¶ms); + if (ret < 0) + goto error; + + ret = params.key->type->asym_query(¶ms, &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(¶ms); + 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, ¶ms); + 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(¶ms, 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(¶ms); + 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, + ¶ms); + 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(¶ms, in, in2); + + kfree(in2); +error_in: + kfree(in); +error_params: + keyctl_pkey_params_free(¶ms); + return ret; +} From 5a30771832aab228e0863e414f9182f86797429e Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 9 Oct 2018 17:47:07 +0100 Subject: [PATCH 03/22] KEYS: Provide missing asymmetric key subops for new key type ops [ver #2] 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 Tested-by: Marcel Holtmann Reviewed-by: Marcel Holtmann Reviewed-by: Denis Kenzior Tested-by: Denis Kenzior Signed-off-by: James Morris --- 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 #include #include +#include #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 #include #include +#include #include +#include #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 + /* * 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 #include +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); From 039884907787e55166e84e0b654a5342cc8a04ab Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 9 Oct 2018 17:47:15 +0100 Subject: [PATCH 04/22] KEYS: Make the X.509 and PKCS7 parsers supply the sig encoding type [ver #2] 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 Tested-by: Marcel Holtmann Reviewed-by: Marcel Holtmann Reviewed-by: Denis Kenzior Tested-by: Denis Kenzior Signed-off-by: James Morris --- 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; } From 82f94f24475c89c6d5cb673e1277b5b4394499c8 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 9 Oct 2018 17:47:23 +0100 Subject: [PATCH 05/22] KEYS: Provide software public key query function [ver #2] 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 Tested-by: Marcel Holtmann Reviewed-by: Marcel Holtmann Reviewed-by: Denis Kenzior Tested-by: Denis Kenzior Signed-off-by: James Morris --- 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); From f7c4e06e066c3df282e6e3d4e7d8c498be9e1e46 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 9 Oct 2018 17:47:31 +0100 Subject: [PATCH 06/22] KEYS: Allow the public_key struct to hold a private key [ver #2] 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 Tested-by: Marcel Holtmann Reviewed-by: Marcel Holtmann Reviewed-by: Denis Kenzior Tested-by: Denis Kenzior Signed-off-by: James Morris --- 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; }; From c08fed73712620eb0a19244dbbbbdf00edbe5e47 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 9 Oct 2018 17:47:38 +0100 Subject: [PATCH 07/22] KEYS: Implement encrypt, decrypt and sign for software asymmetric key [ver #2] 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 Tested-by: Marcel Holtmann Reviewed-by: Marcel Holtmann Reviewed-by: Denis Kenzior Tested-by: Denis Kenzior Signed-off-by: James Morris --- 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); From 3c58b2362ba828ee2970c66c6a6fd7b04fde4413 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 9 Oct 2018 17:47:46 +0100 Subject: [PATCH 08/22] KEYS: Implement PKCS#8 RSA Private Key parser [ver #2] 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 Tested-by: Marcel Holtmann Reviewed-by: Marcel Holtmann Reviewed-by: Denis Kenzior Tested-by: Denis Kenzior Signed-off-by: James Morris --- 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 (dhowells@redhat.com) + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#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"); From b3a8c8a5ebb5b4c3eb7b104364e63c453cc85f14 Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Tue, 9 Oct 2018 17:47:53 +0100 Subject: [PATCH 09/22] crypto: rsa-pkcs1pad: Allow hash to be optional [ver #2] 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 Signed-off-by: David Howells Tested-by: Marcel Holtmann Reviewed-by: Marcel Holtmann Signed-off-by: James Morris --- 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 812476e46821..cfc04e15fd97 100644 --- a/crypto/rsa-pkcs1pad.c +++ b/crypto/rsa-pkcs1pad.c @@ -392,7 +392,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; @@ -412,8 +413,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); @@ -475,10 +477,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; @@ -608,11 +613,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) @@ -632,14 +640,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; From 903be6bb84c544551150a6f5aab9fda1ed9a6895 Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Tue, 9 Oct 2018 17:48:02 +0100 Subject: [PATCH 10/22] KEYS: asym_tpm: add skeleton for asym_tpm [ver #2] This patch adds the basic skeleton for the asym_tpm asymmetric key subtype. Signed-off-by: Denis Kenzior Signed-off-by: David Howells Tested-by: Marcel Holtmann Reviewed-by: Marcel Holtmann Signed-off-by: James Morris --- 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 +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 + +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 */ From f8c54e1ac4b82933dfcf88c37892da8ae35ccbe4 Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Tue, 9 Oct 2018 17:48:10 +0100 Subject: [PATCH 11/22] KEYS: asym_tpm: extract key size & public key [ver #2] 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 Signed-off-by: David Howells Tested-by: Marcel Holtmann Reviewed-by: Marcel Holtmann Signed-off-by: James Morris --- 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 #include #include +#include #include #include @@ -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); From d5e72745ca121459f68c598dac7b374a76322b94 Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Tue, 9 Oct 2018 17:48:17 +0100 Subject: [PATCH 12/22] KEYS: Add parser for TPM-based keys [ver #2] 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 Signed-off-by: David Howells Tested-by: Marcel Holtmann Reviewed-by: Marcel Holtmann Signed-off-by: James Morris --- 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 +#include +#include +#include +#include +#include +#include +#include +#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"); From dff5a61a59614c110d06a5dc1466cdb762b6affd Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Tue, 9 Oct 2018 17:48:25 +0100 Subject: [PATCH 13/22] KEYS: asym_tpm: Implement pkey_query [ver #2] 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 Signed-off-by: David Howells Tested-by: Marcel Holtmann Reviewed-by: Marcel Holtmann Signed-off-by: James Morris --- 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 #include #include +#include #include #include #include +/* + * 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); From ad4b1eb5fb3350c979a4f86eacfe7aac0595f335 Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Tue, 9 Oct 2018 17:48:33 +0100 Subject: [PATCH 14/22] KEYS: asym_tpm: Implement encryption operation [ver #2] 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 Signed-off-by: David Howells Tested-by: Marcel Holtmann Reviewed-by: Marcel Holtmann Signed-off-by: James Morris --- 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); From e1ea9f86023e7668604cc6456a818e5e4d0361c9 Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Tue, 9 Oct 2018 17:48:41 +0100 Subject: [PATCH 15/22] KEYS: trusted: Expose common functionality [ver #2] This patch exposes some common functionality needed to send TPM commands. Several functions from keys/trusted.c are exposed for use by the new tpm key subtype and a module dependency is introduced. In the future, common functionality between the trusted key type and the asym_tpm subtype should be factored out into a common utility library. Signed-off-by: Denis Kenzior Signed-off-by: David Howells Tested-by: Marcel Holtmann Reviewed-by: Marcel Holtmann Signed-off-by: James Morris --- 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 From 22447981fc050b5f1bdd0e7cbee89b4152a2b2d8 Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Tue, 9 Oct 2018 17:48:49 +0100 Subject: [PATCH 16/22] KEYS: Move trusted.h to include/keys [ver #2] Signed-off-by: Denis Kenzior Signed-off-by: David Howells Tested-by: Marcel Holtmann Reviewed-by: Marcel Holtmann Signed-off-by: James Morris --- {security => include}/keys/trusted.h | 0 security/keys/trusted.c | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename {security => include}/keys/trusted.h (100%) diff --git a/security/keys/trusted.h b/include/keys/trusted.h similarity index 100% rename from security/keys/trusted.h rename to include/keys/trusted.h 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 #include -#include "trusted.h" +#include static const char hmac_alg[] = "hmac(sha1)"; static const char hash_alg[] = "sha1"; From 0c36264aa1d5b77eb21f646f83223134e4528cfe Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Tue, 9 Oct 2018 17:48:58 +0100 Subject: [PATCH 17/22] KEYS: asym_tpm: Add loadkey2 and flushspecific [ver #2] This commit adds TPM_LoadKey2 and TPM_FlushSpecific operations. Signed-off-by: Denis Kenzior Signed-off-by: David Howells Reviewed-by: James Morris Tested-by: Marcel Holtmann Reviewed-by: Marcel Holtmann Signed-off-by: James Morris --- 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 #include #include +#include #include +#include +#include #include #include +#include #include +#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 From f884fe5a158f750e232b029e1fac0283e388e062 Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Tue, 9 Oct 2018 17:49:05 +0100 Subject: [PATCH 18/22] KEYS: asym_tpm: Implement tpm_unbind [ver #2] Signed-off-by: Denis Kenzior Reviewed-by: James Morris Signed-off-by: David Howells Tested-by: Marcel Holtmann Reviewed-by: Marcel Holtmann Signed-off-by: James Morris --- 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 From a335974ae0883e045151a2160093a22aa02c3626 Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Tue, 9 Oct 2018 17:49:13 +0100 Subject: [PATCH 19/22] KEYS: asym_tpm: Implement the decrypt operation [ver #2] 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 Signed-off-by: David Howells Tested-by: Marcel Holtmann Reviewed-by: Marcel Holtmann Signed-off-by: James Morris --- 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(); } From e08e6891231f5fae82a6ffb4affdfa2ced8c1a77 Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Tue, 9 Oct 2018 17:49:20 +0100 Subject: [PATCH 20/22] KEYS: asym_tpm: Implement signature verification [ver #2] 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 Signed-off-by: David Howells Tested-by: Marcel Holtmann Reviewed-by: Marcel Holtmann Signed-off-by: James Morris --- 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 #include #include +#include #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); From e73d170f6c77e7006b48c5e9c325fe520f6012ca Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Tue, 9 Oct 2018 17:49:28 +0100 Subject: [PATCH 21/22] KEYS: asym_tpm: Implement tpm_sign [ver #2] Signed-off-by: Denis Kenzior Signed-off-by: David Howells Tested-by: Marcel Holtmann Reviewed-by: Marcel Holtmann Signed-off-by: James Morris --- 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 From 64ae16dfeefec670276607fa789ce096c7ebd7c4 Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Tue, 9 Oct 2018 17:49:35 +0100 Subject: [PATCH 22/22] KEYS: asym_tpm: Add support for the sign operation [ver #2] 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 Signed-off-by: David Howells Tested-by: Marcel Holtmann Reviewed-by: Marcel Holtmann Signed-off-by: James Morris --- 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(); }