diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-11-01 15:23:59 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-11-01 15:23:59 -0700 |
| commit | baa888d25ea64d0c59344d474284ca99cfdd449a (patch) | |
| tree | 06b85d1c3b70a12ad5c8a49394d57c14d5a69993 | |
| parent | 7260935d71b6d582376543844185add72848dde8 (diff) | |
| parent | 64ae16dfeefec670276607fa789ce096c7ebd7c4 (diff) | |
Merge branch 'next-keys2' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull keys updates from James Morris:
"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"
* 'next-keys2' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (22 commits)
KEYS: asym_tpm: Add support for the sign operation [ver #2]
KEYS: asym_tpm: Implement tpm_sign [ver #2]
KEYS: asym_tpm: Implement signature verification [ver #2]
KEYS: asym_tpm: Implement the decrypt operation [ver #2]
KEYS: asym_tpm: Implement tpm_unbind [ver #2]
KEYS: asym_tpm: Add loadkey2 and flushspecific [ver #2]
KEYS: Move trusted.h to include/keys [ver #2]
KEYS: trusted: Expose common functionality [ver #2]
KEYS: asym_tpm: Implement encryption operation [ver #2]
KEYS: asym_tpm: Implement pkey_query [ver #2]
KEYS: Add parser for TPM-based keys [ver #2]
KEYS: asym_tpm: extract key size & public key [ver #2]
KEYS: asym_tpm: add skeleton for asym_tpm [ver #2]
crypto: rsa-pkcs1pad: Allow hash to be optional [ver #2]
KEYS: Implement PKCS#8 RSA Private Key parser [ver #2]
KEYS: Implement encrypt, decrypt and sign for software asymmetric key [ver #2]
KEYS: Allow the public_key struct to hold a private key [ver #2]
KEYS: Provide software public key query function [ver #2]
KEYS: Make the X.509 and PKCS7 parsers supply the sig encoding type [ver #2]
KEYS: Provide missing asymmetric key subops for new key type ops [ver #2]
...
29 files changed, 2516 insertions, 61 deletions
diff --git a/Documentation/crypto/asymmetric-keys.txt b/Documentation/crypto/asymmetric-keys.txt index 5969bf42562a..8763866b11cf 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. ========================== @@ -234,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/Documentation/security/keys/core.rst b/Documentation/security/keys/core.rst index 9ce7256c6edb..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=<encoding>`` The encoding of the encrypted/signature blob. This + can be "pkcs1" for RSASSA-PKCS1-v1.5 or + RSAES-PKCS1-v1.5; "pss" for "RSASSA-PSS"; "oaep" for + "RSAES-OAEP". If omitted or is "raw", the raw output + of the encryption function is specified. + + ``hash=<algo>`` If the data buffer contains the output of a hash + function and the encoding includes some indication of + which hash function was used, the hash function can be + specified with this, eg. "hash=sha256". + + The ``__spare[]`` space in the parameter block must be set to 0. This is + intended, amongst other things, to allow the passing of passphrases + required to unlock a key. + + If successful, encrypt, decrypt and sign all return the amount of data + written into the output buffer. Verification returns 0 on success. + + Kernel Services =============== @@ -1483,6 +1594,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/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index f3702e533ff4..be70ca6c85d3 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -21,6 +21,18 @@ 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 + depends on TRUSTED_KEYS + 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 @@ -31,6 +43,25 @@ 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 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 d4b2e1b2dc65..28b91adba2ae 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 @@ -30,6 +31,19 @@ $(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 # obj-$(CONFIG_PKCS7_MESSAGE_PARSER) += pkcs7_message.o @@ -61,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/asym_tpm.c b/crypto/asymmetric_keys/asym_tpm.c new file mode 100644 index 000000000000..5d4c270463f6 --- /dev/null +++ b/crypto/asymmetric_keys/asym_tpm.c @@ -0,0 +1,988 @@ +// SPDX-License-Identifier: GPL-2.0 +#define pr_fmt(fmt) "ASYM-TPM: "fmt +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/export.h> +#include <linux/kernel.h> +#include <linux/seq_file.h> +#include <linux/scatterlist.h> +#include <linux/tpm.h> +#include <linux/tpm_command.h> +#include <crypto/akcipher.h> +#include <crypto/hash.h> +#include <crypto/sha.h> +#include <asm/unaligned.h> +#include <keys/asymmetric-subtype.h> +#include <keys/trusted.h> +#include <crypto/asym_tpm_subtype.h> +#include <crypto/public_key.h> + +#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 + +/* + * 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); +} + +/* + * 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; +} + +/* + * 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 + * 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. + */ +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); +} + +/* 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]) +{ + if (strcmp(encoding, "pkcs1") == 0) { + 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; + } + + if (strcmp(encoding, "raw") == 0) { + s |
