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]
  ...
This commit is contained in:
Linus Torvalds 2018-11-01 15:23:59 -07:00
commit baa888d25e
29 changed files with 2516 additions and 61 deletions

View File

@ -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.

View File

@ -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
============================

View File

@ -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

View File

@ -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
@ -29,6 +30,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
#
@ -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

View File

@ -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) {
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;
info->supported_ops = KEYCTL_SUPPORTS_ENCRYPT |
KEYCTL_SUPPORTS_DECRYPT |
KEYCTL_SUPPORTS_VERIFY |
KEYCTL_SUPPORTS_SIGN;
ret = 0;
error_free_tfm:
crypto_free_akcipher(tfm);
pr_devel("<==%s() = %d\n", __func__, ret);
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;
}
/*
* 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;
}
/*
* 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.
*/
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;
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();
}
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
* 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)
{
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;
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:
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,
.query = tpm_key_query,
.eds_op = tpm_key_eds_op,
.verify_signature = tpm_key_verify_signature,
};
EXPORT_SYMBOL_GPL(asym_tpm_subtype);
MODULE_DESCRIPTION("TPM based asymmetric key subtype");
MODULE_AUTHOR("Intel Corporation");
MODULE_LICENSE("GPL v2");

View File

@ -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);

View File

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

View File

@ -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);

View File

@ -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
}

View File

@ -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 <linux/module.h>
#include <linux/kernel.h>
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/oid_registry.h>
#include <keys/asymmetric-subtype.h>
#include <keys/asymmetric-parser.h>
#include <crypto/public_key.h>
#include "pkcs8.asn1.h"
struct pkcs8_parse_context {
struct public_key *pub;
unsigned long data; /* Start of data */
enum OID last_oid; /* Last OID encountered */
enum OID algo_oid; /* Algorithm OID */
u32 key_size;
const void *key;
};
/*
* Note an OID when we find one for later processing when we know how to
* interpret it.
*/
int pkcs8_note_OID(void *context, size_t hdrlen,
unsigned char tag,
const void *value, size_t vlen)
{
struct pkcs8_parse_context *ctx = context;
ctx->last_oid = look_up_OID(value, vlen);
if (ctx->last_oid == OID__NR) {
char buffer[50];
sprint_oid(value, vlen, buffer, sizeof(buffer));
pr_info("Unknown OID: [%lu] %s\n",
(unsigned long)value - ctx->data, buffer);
}
return 0;
}
/*
* Note the version number of the ASN.1 blob.
*/
int pkcs8_note_version(void *context, size_t hdrlen,
unsigned char tag,
const void *value, size_t vlen)
{
if (vlen != 1 || ((const u8 *)value)[0] != 0) {
pr_warn("Unsupported PKCS#8 version\n");
return -EBADMSG;
}
return 0;
}
/*
* Note the public algorithm.
*/
int pkcs8_note_algo(void *context, size_t hdrlen,
unsigned char tag,
const void *value, size_t vlen)
{
struct pkcs8_parse_context *ctx = context;
if (ctx->last_oid != OID_rsaEncryption)
return -ENOPKG;
ctx->pub->pkey_algo = "rsa";
return 0;
}
/*
* Note the key data of the ASN.1 blob.
*/
int pkcs8_note_key(void *context, size_t hdrlen,
unsigned char tag,
const void *value, size_t vlen)
{
struct pkcs8_parse_context *ctx = context;
ctx->key = value;
ctx->key_size = vlen;
return 0;
}
/*
* Parse a PKCS#8 private key blob.
*/
static struct public_key *pkcs8_parse(const void *data, size_t datalen)
{
struct pkcs8_parse_context ctx;
struct public_key *pub;
long ret;
memset(&ctx, 0, sizeof(ctx));
ret = -ENOMEM;
ctx.pub = kzalloc(sizeof(struct public_key), GFP_KERNEL);
if (!ctx.pub)
goto error;
ctx.data = (unsigned long)data;
/* Attempt to decode the private key */
ret = asn1_ber_decoder(&pkcs8_decoder, &ctx, data, datalen);
if (ret < 0)
goto error_decode;
ret = -ENOMEM;
pub = ctx.pub;
pub->key = kmemdup(ctx.key, ctx.key_size, GFP_KERNEL);
if (!pub->key)
goto error_decode;
pub->keylen = ctx.key_size;
pub->key_is_private = true;
return pub;
error_decode:
kfree(ctx.pub);
error:
return ERR_PTR(ret);
}
/*
* Attempt to parse a data blob for a key as a PKCS#8 private key.
*/
static int pkcs8_key_preparse(struct key_preparsed_payload *prep)
{
struct public_key *pub;
pub = pkcs8_parse(prep->data, prep->datalen);
if (IS_ERR(pub))
return PTR_ERR(pub);
pr_devel("Cert Key Algo: %s\n", pub->pkey_algo);
pub->id_type = "PKCS8";
/* We're pinning the module by being linked against it */
__module_get(public_key_subtype.owner);
prep->payload.data[asym_subtype] = &public_key_subtype;
prep->payload.data[asym_key_ids] = NULL;
prep->payload.data[asym_crypto] = pub;
prep->payload.data[asym_auth] = NULL;
prep->quotalen = 100;
return 0;
}
static struct asymmetric_key_parser pkcs8_key_parser = {
.owner = THIS_MODULE,
.name = "pkcs8",
.parse = pkcs8_key_preparse,
};
/*
* Module stuff
*/
static int __init pkcs8_key_init(void)
{
return register_asymmetric_key_parser(&pkcs8_key_parser);
}
static void __exit pkcs8_key_exit(void)
{
unregister_asymmetric_key_parser(&pkcs8_key_parser);
}
module_init(pkcs8_key_init);
module_exit(pkcs8_key_exit);
MODULE_DESCRIPTION("PKCS#8 certificate parser");
MODULE_LICENSE("GPL");

View File

@ -59,6 +59,165 @@ 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);
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;
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_ENCRYPT |
KEYCTL_SUPPORTS_VERIFY);
if (pkey->key_is_private)
info->supported_ops |= (KEYCTL_SUPPORTS_DECRYPT |
KEYCTL_SUPPORTS_SIGN);
ret = 0;
error_free_tfm:
crypto_free_akcipher(tfm);
pr_devel("<==%s() = %d\n", __func__, ret);
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.
*/
@ -69,8 +228,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 +239,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))
@ -106,7 +254,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;
@ -167,6 +320,8 @@ 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,
.eds_op = software_key_eds_op,
.verify_signature = public_key_verify_signature_2,
};
EXPORT_SYMBOL_GPL(public_key_subtype);

View File

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

View File

@ -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 })

View File

@ -0,0 +1,102 @@
// SPDX-License-Identifier: GPL-2.0
#define pr_fmt(fmt) "TPM-PARSER: "fmt
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <keys/asymmetric-subtype.h>
#include <keys/asymmetric-parser.h>
#include <crypto/asym_tpm_subtype.h>
#include "tpm.asn1.h"
struct tpm_parse_context {
const void *blob;
u32 blob_len;
};
/*
* Note the key data of the ASN.1 blob.
*/
int tpm_note_key(void *context, size_t hdrlen,
unsigned char tag,
const void *value, size_t vlen)
{
struct tpm_parse_context *ctx = context;
ctx->blob = value;
ctx->blob_len = vlen;
return 0;
}
/*
* Parse a TPM-encrypted private key blob.
*/
static struct tpm_key *tpm_parse(const void *data, size_t datalen)
{
struct tpm_parse_context ctx;
long ret;
memset(&ctx, 0, sizeof(ctx));
/* Attempt to decode the private key */
ret = asn1_ber_decoder(&tpm_decoder, &ctx, data, datalen);
if (ret < 0)
goto error;
return tpm_key_create(ctx.blob, ctx.blob_len);
error:
return ERR_PTR(ret);
}
/*
* Attempt to parse a data blob for a key as a TPM private key blob.
*/
static int tpm_key_preparse(struct key_preparsed_payload *prep)
{
struct tpm_key *tk;
/*
* TPM 1.2 keys are max 2048 bits long, so assume the blob is no
* more than 4x that
*/
if (prep->datalen > 256 * 4)
return -EMSGSIZE;
tk = tpm_parse(prep->data, prep->datalen);
if (IS_ERR(tk))
return PTR_ERR(tk);
/* We're pinning the module by being linked against it */
__module_get(asym_tpm_subtype.owner);
prep->payload.data[asym_subtype] = &asym_tpm_subtype;
prep->payload.data[asym_key_ids] = NULL;
prep->payload.data[asym_crypto] = tk;
prep->payload.data[asym_auth] = NULL;
prep->quotalen = 100;
return 0;
}
static struct asymmetric_key_parser tpm_key_parser = {
.owner = THIS_MODULE,
.name = "tpm_parser",
.parse = tpm_key_preparse,
};
static int __init tpm_key_init(void)
{
return register_asymmetric_key_parser(&tpm_key_parser);
}
static void __exit tpm_key_exit(void)
{
unregister_asymmetric_key_parser(&tpm_key_parser);
}
module_init(tpm_key_init);
module_exit(tpm_key_exit);
MODULE_DESCRIPTION("TPM private key-blob parser");
MODULE_LICENSE("GPL v2");

View File

@ -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;
}

View File

@ -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;

View File

@ -0,0 +1,19 @@
// SPDX-License-Identifier: GPL-2.0
#ifndef _LINUX_ASYM_TPM_SUBTYPE_H
#define _LINUX_ASYM_TPM_SUBTYPE_H
#include <linux/keyctl.h>
struct tpm_key {
void *blob;
u32 blob_len;
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);
extern struct asymmetric_key_subtype asym_tpm_subtype;
#endif /* _LINUX_ASYM_TPM_SUBTYPE_H */

View File

@ -14,6 +14,8 @@
#ifndef _LINUX_PUBLIC_KEY_H
#define _LINUX_PUBLIC_KEY_H
#include <linux/keyctl.h>
/*
* Cryptographic data for the public-key subtype of the asymmetric key type.
*
@ -23,6 +25,7 @@
struct public_key {
void *key;
u32 keylen;
bool key_is_private;
const char *id_type;
const char *pkey_algo;
};
@ -40,6 +43,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 +69,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);

View File

@ -17,6 +17,8 @@
#include <linux/seq_file.h>
#include <keys/asymmetric-type.h>
struct kernel_pkey_query;
struct kernel_pkey_params;
struct public_key_signature;
/*
@ -34,6 +36,13 @@ struct asymmetric_key_subtype {
/* Destroy a key of this subtype */
void (*destroy)(void *payload_crypto, void *payload_auth);
int (*query)(const struct kernel_pkey_params *params,
struct kernel_pkey_query *info);
/* Encrypt/decrypt/sign data */
int (*eds_op)(struct kernel_pkey_params *params,
const void *in, void *out);
/* Verify the signature on a key of this subtype (optional) */
int (*verify_signature)(const struct key *key,
const struct public_key_signature *sig);

View File

@ -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

View File

@ -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 */

46
include/linux/keyctl.h Normal file
View File

@ -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 <uapi/linux/keyctl.h>
struct kernel_pkey_query {
__u32 supported_ops; /* Which ops are supported */
__u32 key_size; /* Size of the key in bits */
__u16 max_data_size; /* Maximum size of raw data to sign in bytes */
__u16 max_sig_size; /* Maximum size of signature in bytes */
__u16 max_enc_size; /* Maximum size of encrypted blob in bytes */
__u16 max_dec_size; /* Maximum size of decrypted blob in bytes */
};
enum kernel_pkey_operation {
kernel_pkey_encrypt,
kernel_pkey_decrypt,
kernel_pkey_sign,
kernel_pkey_verify,
};
struct kernel_pkey_params {
struct key *key;
const char *encoding; /* Encoding (eg. "oaep" or "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 */

View File

@ -61,6 +61,11 @@
#define KEYCTL_INVALIDATE 21 /* invalidate a key */
#define KEYCTL_GET_PERSISTENT 22 /* get a user's persistent keyring */
#define KEYCTL_DH_COMPUTE 23 /* Compute Diffie-Hellman values */
#define KEYCTL_PKEY_QUERY 24 /* Query public key parameters */
#define KEYCTL_PKEY_ENCRYPT 25 /* Encrypt a blob using a public key */
#define KEYCTL_PKEY_DECRYPT 26 /* Decrypt a blob using a public key */
#define KEYCTL_PKEY_SIGN 27 /* Create a public key signature */
#define KEYCTL_PKEY_VERIFY 28 /* Verify a public key signature */
#define KEYCTL_RESTRICT_KEYRING 29 /* Restrict keys allowed to link to a keyring */
/* keyctl structures */
@ -82,4 +87,29 @@ 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
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 */

View File

@ -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

View File

@ -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;
}

View File

@ -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
*/

View File

@ -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;
}

323
security/keys/keyctl_pkey.c Normal file
View File

@ -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 <linux/slab.h>
#include <linux/err.h>
#include <linux/key.h>
#include <linux/keyctl.h>
#include <linux/parser.h>
#include <linux/uaccess.h>
#include <keys/user-type.h>
#include "internal.h"
static void keyctl_pkey_params_free(struct kernel_pkey_params *params)
{
kfree(params->info);
key_put(params->key);
}
enum {
Opt_err = -1,
Opt_enc, /* "enc=<encoding>" eg. "enc=oaep" */
Opt_hash, /* "hash=<digest-name>" eg. "hash=sha1" */
};
static const match_table_t param_keys = {
{ Opt_enc, "enc=%s" },
{ Opt_hash, "hash=%s" },
{ Opt_err, NULL }
};
/*
* Parse the information string which consists of key=val pairs.
*/
static int keyctl_pkey_params_parse(struct kernel_pkey_params *params)
{
unsigned long token_mask = 0;
substring_t args[MAX_OPT_ARGS];
char *c = params->info, *p, *q;
int token;
while ((p = strsep(&c, " \t"))) {
if (*p == '\0' || *p == ' ' || *p == '\t')
continue;
token = match_token(p, param_keys, args);
if (__test_and_set_bit(token, &token_mask))
return -EINVAL;
q = args[0].from;
if (!q[0])
return -EINVAL;
switch (token) {
case Opt_enc:
params->encoding = q;
break;
case Opt_hash:
params->hash_algo = q;
break;
default:
return -EINVAL;
}
}
return 0;
}
/*
* Interpret parameters. Callers must always call the free function
* on params, even if an error is returned.
*/
static int keyctl_pkey_params_get(key_serial_t id,
const char __user *_info,
struct kernel_pkey_params *params)
{
key_ref_t key_ref;
void *p;
int ret;
memset(params, 0, sizeof(*params));
params->encoding = "raw";
p = strndup_user(_info, PAGE_SIZE);
if (IS_ERR(p))
return PTR_ERR(p);
params->info = p;
ret = keyctl_pkey_params_parse(params);
if (ret < 0)
return ret;
key_ref = lookup_user_key(id, 0, KEY_NEED_SEARCH);
if (IS_ERR(key_ref))
return PTR_ERR(key_ref);
params->key = key_ref_to_ptr(key_ref);
if (!params->key->type->asym_query)
return -EOPNOTSUPP;
return 0;
}
/*
* Get parameters from userspace. Callers must always call the free function
* on params, even if an error is returned.
*/
static int keyctl_pkey_params_get_2(const struct keyctl_pkey_params __user *_params,
const char __user *_info,
int op,
struct kernel_pkey_params *params)
{
struct keyctl_pkey_params uparams;
struct kernel_pkey_query info;
int ret;
memset(params, 0, sizeof(*params));
params->encoding = "raw";
if (copy_from_user(&uparams, _params, sizeof(uparams)) != 0)
return -EFAULT;
ret = keyctl_pkey_params_get(uparams.key_id, _info, params);
if (ret < 0)
return ret;
ret = params->key->type->asym_query(params, &info);
if (ret < 0)
return ret;
switch (op) {
case KEYCTL_PKEY_ENCRYPT:
case KEYCTL_PKEY_DECRYPT:
if (uparams.in_len > info.max_enc_size ||
uparams.out_len > info.max_dec_size)
return -EINVAL;
break;
case KEYCTL_PKEY_SIGN:
case KEYCTL_PKEY_VERIFY:
if (uparams.in_len > info.max_sig_size ||
uparams.out_len > info.max_data_size)
return -EINVAL;
break;
default:
BUG();
}
params->in_len = uparams.in_len;
params->out_len = uparams.out_len;
return 0;
}
/*
* Query information about an asymmetric key.
*/
long keyctl_pkey_query(key_serial_t id,
const char __user *_info,
struct keyctl_pkey_query __user *_res)
{
struct kernel_pkey_params params;
struct kernel_pkey_query res;
long ret;
memset(&params, 0, sizeof(params));
ret = keyctl_pkey_params_get(id, _info, &params);
if (ret < 0)
goto error;
ret = params.key->type->asym_query(&params, &res);
if (ret < 0)
goto error;
ret = -EFAULT;
if (copy_to_user(_res, &res, sizeof(res)) == 0 &&
clear_user(_res->__spare, sizeof(_res->__spare)) == 0)
ret = 0;
error:
keyctl_pkey_params_free(&params);
return ret;
}
/*
* Encrypt/decrypt/sign
*
* Encrypt data, decrypt data or sign data using a public key.
*
* _info is a string of supplementary information in key=val format. For
* instance, it might contain:
*
* "enc=pkcs1 hash=sha256"
*
* where enc= specifies the encoding and hash= selects the OID to go in that
* particular encoding if required. If enc= isn't supplied, it's assumed that
* the caller is supplying raw values.
*
* If successful, the amount of data written into the output buffer is
* returned.
*/
long keyctl_pkey_e_d_s(int op,
const struct keyctl_pkey_params __user *_params,
const char __user *_info,
const void __user *_in,
void __user *_out)
{
struct kernel_pkey_params params;
void *in, *out;
long ret;
ret = keyctl_pkey_params_get_2(_params, _info, op, &params);
if (ret < 0)
goto error_params;
ret = -EOPNOTSUPP;
if (!params.key->type->asym_eds_op)
goto error_params;
switch (op) {
case KEYCTL_PKEY_ENCRYPT:
params.op = kernel_pkey_encrypt;
break;
case KEYCTL_PKEY_DECRYPT:
params.op = kernel_pkey_decrypt;
break;
case KEYCTL_PKEY_SIGN:
params.op = kernel_pkey_sign;
break;
default:
BUG();
}
in = memdup_user(_in, params.in_len);
if (IS_ERR(in)) {
ret = PTR_ERR(in);
goto error_params;
}
ret = -ENOMEM;
out = kmalloc(params.out_len, GFP_KERNEL);
if (!out)
goto error_in;
ret = params.key->type->asym_eds_op(&params, in, out);
if (ret < 0)
goto error_out;
if (copy_to_user(_out, out, ret) != 0)
ret = -EFAULT;
error_out:
kfree(out);
error_in:
kfree(in);
error_params:
keyctl_pkey_params_free(&params);
return ret;
}
/*
* Verify a signature.
*
* Verify a public key signature using the given key, or if not given, search
* for a matching key.
*
* _info is a string of supplementary information in key=val format. For
* instance, it might contain:
*
* "enc=pkcs1 hash=sha256"
*
* where enc= specifies the signature blob encoding and hash= selects the OID
* to go in that particular encoding. If enc= isn't supplied, it's assumed
* that the caller is supplying raw values.
*
* If successful, 0 is returned.
*/
long keyctl_pkey_verify(const struct keyctl_pkey_params __user *_params,
const char __user *_info,
const void __user *_in,
const void __user *_in2)
{
struct kernel_pkey_params params;
void *in, *in2;
long ret;
ret = keyctl_pkey_params_get_2(_params, _info, KEYCTL_PKEY_VERIFY,
&params);
if (ret < 0)
goto error_params;
ret = -EOPNOTSUPP;
if (!params.key->type->asym_verify_signature)
goto error_params;
in = memdup_user(_in, params.in_len);
if (IS_ERR(in)) {
ret = PTR_ERR(in);
goto error_params;
}
in2 = memdup_user(_in2, params.in2_len);
if (IS_ERR(in2)) {
ret = PTR_ERR(in2);
goto error_in;
}
params.op = kernel_pkey_verify;
ret = params.key->type->asym_verify_signature(&params, in, in2);
kfree(in2);
error_in:
kfree(in);
error_params:
keyctl_pkey_params_free(&params);
return ret;
}

View File

@ -30,7 +30,7 @@
#include <linux/tpm.h>
#include <linux/tpm_command.h>
#include "trusted.h"
#include <keys/trusted.h>
static const char hmac_alg[] = "hmac(sha1)";
static const char hash_alg[] = "sha1";
@ -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];