2016-05-11 14:21:56

by David Howells

[permalink] [raw]
Subject: [RFC PATCH 0/8] KEYS: keyctl operations for asymmetric keys [ver 3]


Here's a set of patches that provides keyctl access for asymmetric keys,
including a query function, and functions to do encryption, decryption,
signature creation and signature verification.

I've added a PKCS#8 asymmetric key parser so that you can load an RSA private
key into the kernel. Currently only DER-encoded and unencrypted PKCS#8 is
supported. Encryption and verification can use a public key from an X.509
cert, but signing and decryption require a private key, though encryption and
verification can use that too.

Example usage:

j=`openssl pkcs8 -in ~/pkcs7/firmwarekey2.priv -topk8 -nocrypt -outform DER | \
keyctl padd asymmetric foo @s`
echo -n abcdefghijklmnopqrst >/tmp/data
keyctl pkey_encrypt $j 0 /tmp/data enc=pkcs1 >/tmp/enc
keyctl pkey_decrypt $j 0 /tmp/enc enc=pkcs1 >/tmp/dec
cmp /tmp/data /tmp/dec
keyctl pkey_sign $j 0 /tmp/data enc=pkcs1 hash=sha1 >/tmp/sig
keyctl pkey_verify $j 0 /tmp/data /tmp/sig enc=pkcs1 hash=sha1

The kernel patches can be found here also:

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

The keyutils changes needed can be found here:

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

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


Documentation/crypto/asymmetric-keys.txt | 33 ++-
Documentation/security/keys.txt | 224 +++++++++++++++++++
crypto/asymmetric_keys/Kconfig | 10 +
crypto/asymmetric_keys/Makefile | 13 +
crypto/asymmetric_keys/asymmetric_keys.h | 3
crypto/asymmetric_keys/asymmetric_type.c | 60 +++++
crypto/asymmetric_keys/pkcs7_parser.c | 1
crypto/asymmetric_keys/pkcs7_trust.c | 2
crypto/asymmetric_keys/pkcs8.asn1 | 24 ++
crypto/asymmetric_keys/pkcs8_parser.c | 184 ++++++++++++++++
crypto/asymmetric_keys/public_key.c | 196 +++++++++++++++--
crypto/asymmetric_keys/restrict.c | 2
crypto/asymmetric_keys/signature.c | 112 ++++++++++
crypto/asymmetric_keys/x509_cert_parser.c | 21 +-
include/crypto/public_key.h | 14 +
include/keys/asymmetric-subtype.h | 10 +
include/linux/key-type.h | 11 +
include/linux/keyctl.h | 47 ++++
include/uapi/linux/keyctl.h | 31 +++
security/integrity/digsig_asymmetric.c | 2
security/keys/Makefile | 1
security/keys/compat.c | 15 +
security/keys/internal.h | 39 +++
security/keys/keyctl.c | 23 ++
security/keys/keyctl_pkey.c | 335 +++++++++++++++++++++++++++++
25 files changed, 1364 insertions(+), 49 deletions(-)
create mode 100644 crypto/asymmetric_keys/pkcs8.asn1
create mode 100644 crypto/asymmetric_keys/pkcs8_parser.c
create mode 100644 include/linux/keyctl.h
create mode 100644 security/keys/keyctl_pkey.c


2016-05-11 14:22:00

by David Howells

[permalink] [raw]
Subject: [RFC PATCH 1/8] KEYS: Provide key type operations for asymmetric key ops [ver 3]

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 key *key, const struct key *password,
struct kernel_pkey_query *info);
int (*asym_eds_op)(struct kernel_pkey_params *params,
const void *in, void *out);
int (*asym_verify_signature)(struct kernel_pkey_params *params,
const void *in, const void *in2);

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

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

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

Documentation/security/keys.txt | 113 +++++++++++++++++++++++++++++++++++++++
include/linux/key-type.h | 11 ++++
include/linux/keyctl.h | 47 ++++++++++++++++
include/uapi/linux/keyctl.h | 5 ++
4 files changed, 176 insertions(+)
create mode 100644 include/linux/keyctl.h

diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt
index 20d05719bceb..ca72b70a24b9 100644
--- a/Documentation/security/keys.txt
+++ b/Documentation/security/keys.txt
@@ -1429,6 +1429,119 @@ The structure has a number of fields, some of which are mandatory:
The authorisation key.


+ (*) 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;
+ struct key *password;
+ const char *encoding;
+ const char *hash_algo;
+ char *info;
+ __u32 in_len;
+ union {
+ __u32 out_len;
+ __u32 in2_len;
+ };
+ enum kernel_pkey_operation op : 8;
+ };
+
+ This includes the key to be used; an optional second key that can be used
+ to provide a password (and must be of logon type); an optional string
+ indicating the encoding to use (for instance, "pkcs1" may be used with an
+ RSA key to indicate RSASSA-PKCS1-v1.5 or RSAES-PKCS1-v1.5 encoding); the
+ name of the hash algorithm used to generate the data for a signature (if
+ appropriate); the sizes of the input and output (or second input) buffers;
+ and the ID of the operation to be performed.
+
+ For a given operation ID, the input and output buffers are used as
+ follows:
+
+ Operation ID in,in_len out,out_len in2,in2_len
+ ======================= =============== =============== ===============
+ kernel_pkey_encrypt Raw data Encrypted data -
+ kernel_pkey_decrypt Encrypted data Raw data -
+ kernel_pkey_sign Raw data Signature -
+ kernel_pkey_verify Raw data - Signature
+
+ asym_eds_op() deals with encryption, decryption and signature creation as
+ specified by params->op. Note that params->op is also set for
+ asym_verify_signature().
+
+ Encrypting and signature creation both take raw data in the input buffer
+ and return the encrypted result in the output buffer. Padding may have
+ been added if an encoding was set. In the case of signature creation,
+ depending on the encoding, the padding created may need to indicate the
+ digest algorithm - the name of which should be supplied in hash_algo.
+
+ Decryption takes encrypted data in the input buffer and returns the raw
+ data in the output buffer. Padding will get checked and stripped off if
+ an encoding was set.
+
+ Verification takes raw data in the input buffer and the signature in the
+ second input buffer and checks that the one matches the other. Padding
+ will be validated. Depending on the encoding, the digest algorithm used
+ to generate the raw data may need to be indicated in hash_algo.
+
+ If successful, asym_eds_op() should return the number of bytes written
+ into the output buffer. asym_verify_signature() should return 0.
+
+ A variety of errors may be returned, including EOPNOTSUPP if the operation
+ is not supported; EKEYREJECTED if verification fails; ENOKEY if the
+ operation needs a password that isn't supplied; EKEYREJECTED if the
+ password is supplied but isn't correct; 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 the key needs to be unlocked, the parameter block contains a pointer
+ that can point to a logon-type key holding a passphrase.
+
+ If successful, 0 will be returned. If the key doesn't support this,
+ EOPNOTSUPP will be returned.
+
+
============================
REQUEST-KEY CALLBACK SERVICE
============================
diff --git a/include/linux/key-type.h b/include/linux/key-type.h
index eaee981c5558..63b2a92c2085 100644
--- a/include/linux/key-type.h
+++ b/include/linux/key-type.h
@@ -17,6 +17,9 @@

#ifdef CONFIG_KEYS

+struct kernel_pkey_query;
+struct kernel_pkey_params;
+
/*
* key under-construction record
* - passed to the request_key actor if supplied
@@ -147,6 +150,14 @@ struct key_type {
*/
request_key_actor_t request_key;

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

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

2016-05-11 14:22:08

by David Howells

[permalink] [raw]
Subject: [RFC PATCH 2/8] KEYS: Provide keyctls to drive the new key type ops for asymmetric keys [ver 3]

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

(*) Query an asymmetric key.

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

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

__u32 supported_ops;

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

KEYCTL_SUPPORTS_{ENCRYPT,DECRYPT,SIGN,VERIFY}

__u32 key_size;

The size in bits of the key.

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

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

If the key needs to be unlocked with a password, a logon-type key that
holds the password may be given as the password argument

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


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

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

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

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

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

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

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

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

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

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

If the key must be unlocked with a password before it can be used,
password_id should point to a logon-type key that holds this.

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

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

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

Documentation/security/keys.txt | 111 +++++++++++++
include/uapi/linux/keyctl.h | 26 +++
security/keys/Makefile | 1
security/keys/compat.c | 15 ++
security/keys/internal.h | 39 +++++
security/keys/keyctl.c | 23 +++
security/keys/keyctl_pkey.c | 335 +++++++++++++++++++++++++++++++++++++++
7 files changed, 550 insertions(+)
create mode 100644 security/keys/keyctl_pkey.c

diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt
index ca72b70a24b9..01c2ae28a8c0 100644
--- a/Documentation/security/keys.txt
+++ b/Documentation/security/keys.txt
@@ -854,6 +854,117 @@ The keyctl syscall functions are:
supported, error ENOKEY if the key could not be found, or error
EACCES if the key is not readable by the caller.

+
+ (*) Query an asymmetric key.
+
+ long keyctl(KEYCTL_PKEY_QUERY,
+ key_serial_t key, key_serial_t password,
+ struct keyctl_pkey_query *info);
+
+ Get information about an asymmetric key. The information is returned in
+ the keyctl_pkey_query struct:
+
+ __u32 supported_ops;
+
+ A bit mask of flags indicating which ops are supported. This is
+ constructed from a bitwise-OR of:
+
+ KEYCTL_SUPPORTS_{ENCRYPT,DECRYPT,SIGN,VERIFY}
+
+ __u32 key_size;
+
+ The size in bits of the key.
+
+ __u16 max_data_size;
+ __u16 max_sig_size;
+ __u16 max_enc_size;
+ __u16 max_dec_size;
+
+ The maximum sizes in bytes of a blob of data to be signed, a signature
+ blob, a blob to be encrypted and a blob to be decypted.
+
+ If the key needs to be unlocked with a password, a logon-type key that
+ holds the password may be given as the password argument
+
+ 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;
+ __s32 password_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
+
+ If the key must be unlocked with a password before it can be used,
+ password_id should point to a logon-type key that holds this.
+
+ 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".
+
+ If successful, encrypt, decrypt and sign all return the amount of data
+ written into the output buffer. Verification returns 0 on success.
+
+
===============
KERNEL SERVICES
===============
diff --git a/include/uapi/linux/keyctl.h b/include/uapi/linux/keyctl.h
index 8ac2c5fbc8fc..05f4f6ffbad2 100644
--- a/include/uapi/linux/keyctl.h
+++ b/include/uapi/linux/keyctl.h
@@ -60,6 +60,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 */

/* keyctl structures */
struct keyctl_dh_params {
@@ -73,4 +78,25 @@ struct keyctl_dh_params {
#define KEYCTL_SUPPORTS_SIGN 0x04
#define KEYCTL_SUPPORTS_VERIFY 0x08

+struct keyctl_pkey_query {
+ __u32 supported_ops; /* Which ops are supported */
+ __u32 key_size; /* Size of the key in bits */
+ __u16 max_data_size; /* Maximum size of raw data to sign in bytes */
+ __u16 max_sig_size; /* Maximum size of signature in bytes */
+ __u16 max_enc_size; /* Maximum size of encrypted blob in bytes */
+ __u16 max_dec_size; /* Maximum size of decrypted blob in bytes */
+ __u32 __spare[10];
+};
+
+struct keyctl_pkey_params {
+ __s32 key_id; /* Serial no. of public key to use */
+ __s32 password_id; /* Serial no. of password-containing key to use (or 0) */
+ __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[4];
+};
+
#endif /* _LINUX_KEYCTL_H */
diff --git a/security/keys/Makefile b/security/keys/Makefile
index 1fd4a16e6daf..981be73e938f 100644
--- a/security/keys/Makefile
+++ b/security/keys/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_SYSCTL) += sysctl.o
obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o
obj-$(CONFIG_KEY_DH_OPERATIONS) += dh.o
+obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += keyctl_pkey.o

#
# Key types
diff --git a/security/keys/compat.c b/security/keys/compat.c
index c8783b3b628c..6298b651e522 100644
--- a/security/keys/compat.c
+++ b/security/keys/compat.c
@@ -136,6 +136,21 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option,
return keyctl_dh_compute(compat_ptr(arg2), compat_ptr(arg3),
arg4);

+ case KEYCTL_PKEY_QUERY:
+ return keyctl_pkey_query(arg2, arg3, compat_ptr(arg4),
+ compat_ptr(arg5));
+
+ case KEYCTL_PKEY_ENCRYPT:
+ case KEYCTL_PKEY_DECRYPT:
+ case KEYCTL_PKEY_SIGN:
+ return keyctl_pkey_e_d_s(option,
+ compat_ptr(arg2), compat_ptr(arg3),
+ compat_ptr(arg4), compat_ptr(arg5));
+
+ case KEYCTL_PKEY_VERIFY:
+ return keyctl_pkey_verify(compat_ptr(arg2), compat_ptr(arg3),
+ compat_ptr(arg4), compat_ptr(arg5));
+
default:
return -EOPNOTSUPP;
}
diff --git a/security/keys/internal.h b/security/keys/internal.h
index 8ec7a528365d..7da61fb8c6b4 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -269,6 +269,45 @@ static inline long keyctl_dh_compute(struct keyctl_dh_params __user *params,
}
#endif

+#ifdef CONFIG_ASYMMETRIC_KEY_TYPE
+extern long keyctl_pkey_query(key_serial_t, 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, key_serial_t password,
+ const char __user *_info,
+ struct keyctl_pkey_query __user *_res)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline long keyctl_pkey_verify(const struct keyctl_pkey_params __user *params,
+ const char __user *_info,
+ const void __user *_in,
+ const void __user *_in2)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline long keyctl_pkey_e_d_s(int op,
+ const struct keyctl_pkey_params __user *params,
+ const char __user *_info,
+ const void __user *_in,
+ void __user *_out)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+
/*
* Debugging key validation
*/
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index 3b135a0af344..4414c959fad3 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -1691,6 +1691,29 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
(char __user *) arg3,
(size_t) arg4);

+ case KEYCTL_PKEY_QUERY:
+ return keyctl_pkey_query((key_serial_t)arg2,
+ (key_serial_t)arg3,
+ (const char __user *)arg4,
+ (struct keyctl_pkey_query *)arg5);
+
+ case KEYCTL_PKEY_ENCRYPT:
+ case KEYCTL_PKEY_DECRYPT:
+ case KEYCTL_PKEY_SIGN:
+ return keyctl_pkey_e_d_s(
+ option,
+ (const struct keyctl_pkey_params __user *)arg2,
+ (const char __user *)arg3,
+ (const void __user *)arg4,
+ (void __user *)arg5);
+
+ case KEYCTL_PKEY_VERIFY:
+ return keyctl_pkey_verify(
+ (const struct keyctl_pkey_params __user *)arg2,
+ (const char __user *)arg3,
+ (const void __user *)arg4,
+ (const void __user *)arg5);
+
default:
return -EOPNOTSUPP;
}
diff --git a/security/keys/keyctl_pkey.c b/security/keys/keyctl_pkey.c
new file mode 100644
index 000000000000..7f51db984aaa
--- /dev/null
+++ b/security/keys/keyctl_pkey.c
@@ -0,0 +1,335 @@
+/* Public-key operation keyctls
+ *
+ * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells ([email protected])
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/key.h>
+#include <linux/keyctl.h>
+#include <linux/parser.h>
+#include <linux/uaccess.h>
+#include <keys/user-type.h>
+#include "internal.h"
+
+static void keyctl_pkey_params_free(struct kernel_pkey_params *params)
+{
+ kfree(params->info);
+ key_put(params->key);
+ key_put(params->password);
+}
+
+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,
+ key_serial_t password_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;
+
+ if (password_id) {
+ key_ref = lookup_user_key(password_id, 0, KEY_NEED_SEARCH);
+ if (IS_ERR(key_ref))
+ return PTR_ERR(key_ref);
+
+ params->password = key_ref_to_ptr(key_ref);
+ if (params->password->type != &key_type_logon)
+ return -EINVAL;
+ }
+
+ 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, uparams.password_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, key_serial_t password_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, password_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 needed, a password may be provided to unlock the private key in a logon
+ * key whose serial number is in _params->password_id.
+ *
+ * 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;
+
+ 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 = kzalloc(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;
+}

2016-05-11 14:22:23

by David Howells

[permalink] [raw]
Subject: [RFC PATCH 4/8] KEYS: Make the X.509 and PKCS7 parsers supply the sig encoding type [ver 3]

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

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

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

diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys/pkcs7_parser.c
index af4cd8649117..5f0c6755a55b 100644
--- a/crypto/asymmetric_keys/pkcs7_parser.c
+++ b/crypto/asymmetric_keys/pkcs7_parser.c
@@ -261,6 +261,7 @@ int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen,
switch (ctx->last_oid) {
case OID_rsaEncryption:
ctx->sinfo->sig->pkey_algo = "rsa";
+ ctx->sinfo->sig->encoding = "pkcs1";
break;
default:
printk("Unsupported pkey algo: %u\n", ctx->last_oid);
diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c
index 865f46ea724f..1f1899d5ab43 100644
--- a/crypto/asymmetric_keys/x509_cert_parser.c
+++ b/crypto/asymmetric_keys/x509_cert_parser.c
@@ -199,35 +199,32 @@ int x509_note_pkey_algo(void *context, size_t hdrlen,

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

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

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

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

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

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

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

2016-05-11 14:22:45

by David Howells

[permalink] [raw]
Subject: [RFC PATCH 7/8] KEYS: Implement encrypt, decrypt and sign for software asymmetric key [ver 3]

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

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

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

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

crypto/asymmetric_keys/public_key.c | 89 ++++++++++++++++++++++++++++++++++-
1 file changed, 86 insertions(+), 3 deletions(-)

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

error_free_tfm:
@@ -142,7 +146,7 @@ struct public_key_completion {
int err;
};

-static void public_key_verify_done(struct crypto_async_request *req, int err)
+static void public_key_crypto_done(struct crypto_async_request *req, int err)
{
struct public_key_completion *compl = req->data;

@@ -154,6 +158,84 @@ static void public_key_verify_done(struct crypto_async_request *req, int err)
}

/*
+ * Do encryption, decryption and signing ops.
+ */
+static int software_key_eds_op(struct kernel_pkey_params *params,
+ const void *in, void *out)
+{
+ struct public_key_completion compl;
+ const struct public_key *pkey = params->key->payload.data[asym_crypto];
+ struct akcipher_request *req;
+ struct crypto_akcipher *tfm;
+ 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);
+ init_completion(&compl.completion);
+ akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
+ CRYPTO_TFM_REQ_MAY_SLEEP,
+ public_key_crypto_done, &compl);
+
+ /* 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();
+ }
+ if (ret == -EINPROGRESS) {
+ wait_for_completion(&compl.completion);
+ ret = compl.err;
+ }
+
+ 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.
*/
int public_key_verify_signature(const struct public_key *pkey,
@@ -210,7 +292,7 @@ int public_key_verify_signature(const struct public_key *pkey,
init_completion(&compl.completion);
akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
CRYPTO_TFM_REQ_MAY_SLEEP,
- public_key_verify_done, &compl);
+ public_key_crypto_done, &compl);

/* Perform the verification calculation. This doesn't actually do the
* verification, but rather calculates the hash expected by the
@@ -258,6 +340,7 @@ struct asymmetric_key_subtype public_key_subtype = {
.describe = public_key_describe,
.destroy = public_key_destroy,
.query = software_key_query,
+ .eds_op = software_key_eds_op,
.verify_signature = public_key_verify_signature_2,
};
EXPORT_SYMBOL_GPL(public_key_subtype);

2016-05-11 14:22:38

by David Howells

[permalink] [raw]
Subject: [RFC PATCH 6/8] KEYS: Allow the public_key struct to hold a private key [ver 3]

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

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

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

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

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

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

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

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

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

2016-05-11 14:22:30

by David Howells

[permalink] [raw]
Subject: [RFC PATCH 5/8] KEYS: Provide software public key query function [ver 3]

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

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

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

diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c
index 96983906d2a2..e9967e5a2c25 100644
--- a/crypto/asymmetric_keys/public_key.c
+++ b/crypto/asymmetric_keys/public_key.c
@@ -57,6 +57,81 @@ static void public_key_destroy(void *payload0, void *payload3)
public_key_signature_free(payload3);
}

+/*
+ * Determine the crypto algorithm name.
+ */
+static
+int software_key_determine_akcipher(const char *encoding,
+ const char *hash_algo,
+ const struct public_key *pkey,
+ char alg_name[CRYPTO_MAX_ALG_NAME])
+{
+ int n;
+
+ if (strcmp(encoding, "pkcs1") == 0) {
+ /* The data wangled by the RSA algorithm is typically padded
+ * and encoded in some manner, such as EMSA-PKCS1-1_5 [RFC3447
+ * sec 8.2].
+ */
+ if (!hash_algo)
+ n = snprintf(alg_name, CRYPTO_MAX_ALG_NAME,
+ "pkcs1pad(%s)",
+ pkey->pkey_algo);
+ else
+ n = snprintf(alg_name, CRYPTO_MAX_ALG_NAME,
+ "pkcs1pad(%s,%s)",
+ pkey->pkey_algo, hash_algo);
+ return n >= CRYPTO_MAX_ALG_NAME ? -EINVAL : 0;
+ }
+
+ if (strcmp(encoding, "raw") == 0) {
+ strcpy(alg_name, pkey->pkey_algo);
+ return 0;
+ }
+
+ return -ENOPKG;
+}
+
+/*
+ * Query information about a key.
+ */
+static int software_key_query(const struct kernel_pkey_params *params,
+ struct kernel_pkey_query *info)
+{
+ struct crypto_akcipher *tfm;
+ struct public_key *pkey = params->key->payload.data[asym_crypto];
+ char alg_name[CRYPTO_MAX_ALG_NAME];
+ int ret, len;
+
+ ret = software_key_determine_akcipher(params->encoding,
+ params->hash_algo,
+ pkey, alg_name);
+ if (ret < 0)
+ return ret;
+
+ tfm = crypto_alloc_akcipher(alg_name, 0, 0);
+ if (IS_ERR(tfm))
+ return PTR_ERR(tfm);
+
+ ret = crypto_akcipher_set_pub_key(tfm, pkey->key, pkey->keylen);
+ if (ret < 0)
+ goto error_free_tfm;
+
+ len = crypto_akcipher_maxsize(tfm);
+ info->key_size = len * 8;
+ info->max_data_size = len;
+ info->max_sig_size = len;
+ info->max_enc_size = len;
+ info->max_dec_size = len;
+ info->supported_ops = KEYCTL_SUPPORTS_VERIFY;
+ ret = 0;
+
+error_free_tfm:
+ crypto_free_akcipher(tfm);
+ pr_devel("<==%s() = %d\n", __func__, ret);
+ return ret;
+}
+
struct public_key_completion {
struct completion completion;
int err;
@@ -83,8 +158,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 = -ENOMEM;
@@ -96,18 +170,11 @@ int public_key_verify_signature(const struct public_key *pkey,
BUG_ON(!sig->digest);
BUG_ON(!sig->s);

- 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))
@@ -180,6 +247,7 @@ struct asymmetric_key_subtype public_key_subtype = {
.name_len = sizeof("public_key") - 1,
.describe = public_key_describe,
.destroy = public_key_destroy,
+ .query = software_key_query,
.verify_signature = public_key_verify_signature_2,
};
EXPORT_SYMBOL_GPL(public_key_subtype);

2016-05-11 14:22:52

by David Howells

[permalink] [raw]
Subject: [RFC PATCH 8/8] KEYS: Implement PKCS#8 RSA Private Key parser [ver 3]

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

PKCS#8 keys can be loaded like this:

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

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

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

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

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

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

2016-05-11 14:22:15

by David Howells

[permalink] [raw]
Subject: [RFC PATCH 3/8] KEYS: Provide missing asymmetric key subops for new key type ops [ver 3]

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

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

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

The public_key_signature struct gains an encoding field to carry the
encoding for verify_signature() and that function gains an extra key
pointer that can be used to pass a pointer to a logon key carrying a
password to unlock the key.

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

Documentation/crypto/asymmetric-keys.txt | 31 +++++++-
crypto/asymmetric_keys/asymmetric_keys.h | 3 +
crypto/asymmetric_keys/asymmetric_type.c | 60 ++++++++++++++--
crypto/asymmetric_keys/pkcs7_trust.c | 2 -
crypto/asymmetric_keys/public_key.c | 1
crypto/asymmetric_keys/restrict.c | 2 -
crypto/asymmetric_keys/signature.c | 112 +++++++++++++++++++++++++++++-
include/crypto/public_key.h | 13 +++
include/keys/asymmetric-subtype.h | 10 +++
security/integrity/digsig_asymmetric.c | 2 -
10 files changed, 216 insertions(+), 20 deletions(-)

diff --git a/Documentation/crypto/asymmetric-keys.txt b/Documentation/crypto/asymmetric-keys.txt
index 8c07e0ea6bc0..93a071714de7 100644
--- a/Documentation/crypto/asymmetric-keys.txt
+++ b/Documentation/crypto/asymmetric-keys.txt
@@ -123,6 +123,7 @@ An operation is provided to perform cryptographic signature verification, using
an asymmetric key to provide or to provide access to the public key.

int verify_signature(const struct key *key,
+ const struct key *password,
const struct public_key_signature *sig);

The caller must have already obtained the key from some source and can then use
@@ -148,6 +149,9 @@ In addition, the data must have been digested by the caller and the resulting
hash must be pointed to by sig->digest and the size of the hash be placed in
sig->digest_size.

+If the key needs to be unlocked with a passphrase, this can be passed in a
+logon key as the password argument.
+
The function will return 0 upon success or -EKEYREJECTED if the signature
doesn't match.

@@ -182,6 +186,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);
};
@@ -206,12 +214,25 @@ 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().
+
+ Mandatory. This is a function for querying the capabilities of a key. A
+ password can be provided if the key needs unlocking.
+
+ (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. A password can
+ be provided if the key needs unlocking.
+
+ (5) verify_signature().

- 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.
+ Optional. This is the entry point for signature verification. The
+ subtype may do anything it likes to implement an operation, including
+ offloading to hardware. A password can be provided if the key needs
+ unlocking.


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

MODULE_LICENSE("GPL");
@@ -451,15 +452,58 @@ static void asymmetric_key_destroy(struct key *key)
asymmetric_key_free_kids(kids);
}

+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, *password = params->password;
+ int ret;
+
+ pr_devel("==>%s()\n", __func__);
+
+ if (key->type != &key_type_asymmetric ||
+ (password && password->type != &key_type_logon))
+ 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, params->password, &sig);
+}
+
struct key_type key_type_asymmetric = {
- .name = "asymmetric",
- .preparse = asymmetric_key_preparse,
- .free_preparse = asymmetric_key_free_preparse,
- .instantiate = generic_key_instantiate,
- .match_preparse = asymmetric_key_match_preparse,
- .match_free = asymmetric_key_match_free,
- .destroy = asymmetric_key_destroy,
- .describe = asymmetric_key_describe,
+ .name = "asymmetric",
+ .preparse = asymmetric_key_preparse,
+ .free_preparse = asymmetric_key_free_preparse,
+ .instantiate = generic_key_instantiate,
+ .match_preparse = asymmetric_key_match_preparse,
+ .match_free = asymmetric_key_match_free,
+ .destroy = asymmetric_key_destroy,
+ .describe = asymmetric_key_describe,
+ .asym_query = query_asymmetric_key,
+ .asym_eds_op = asymmetric_key_eds_op,
+ .asym_verify_signature = asymmetric_key_verify_signature,
};
EXPORT_SYMBOL_GPL(key_type_asymmetric);

diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c
index f6a009d88a33..908140e334cf 100644
--- a/crypto/asymmetric_keys/pkcs7_trust.c
+++ b/crypto/asymmetric_keys/pkcs7_trust.c
@@ -115,7 +115,7 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
return -ENOKEY;

matched:
- ret = verify_signature(key, sig);
+ ret = verify_signature(key, NULL, sig);
key_put(key);
if (ret < 0) {
if (ret == -ENOMEM)
diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c
index fd76b5fc3b3a..96983906d2a2 100644
--- a/crypto/asymmetric_keys/public_key.c
+++ b/crypto/asymmetric_keys/public_key.c
@@ -164,6 +164,7 @@ error_free_tfm:
EXPORT_SYMBOL_GPL(public_key_verify_signature);

static int public_key_verify_signature_2(const struct key *key,
+ const struct key *password,
const struct public_key_signature *sig)
{
const struct public_key *pk = key->payload.data[asym_crypto];
diff --git a/crypto/asymmetric_keys/restrict.c b/crypto/asymmetric_keys/restrict.c
index ac4bddf669de..d9efe00c5881 100644
--- a/crypto/asymmetric_keys/restrict.c
+++ b/crypto/asymmetric_keys/restrict.c
@@ -102,7 +102,7 @@ int restrict_link_by_signature(struct key *trust_keyring,
if (use_builtin_keys && !test_bit(KEY_FLAG_BUILTIN, &key->flags))
ret = -ENOKEY;
else
- ret = verify_signature(key, sig);
+ ret = verify_signature(key, NULL, sig);
key_put(key);
return ret;
}
diff --git a/crypto/asymmetric_keys/signature.c b/crypto/asymmetric_keys/signature.c
index 11b7ba170904..d69c76e2e991 100644
--- a/crypto/asymmetric_keys/signature.c
+++ b/crypto/asymmetric_keys/signature.c
@@ -16,7 +16,9 @@
#include <linux/export.h>
#include <linux/err.h>
#include <linux/slab.h>
+#include <linux/keyctl.h>
#include <crypto/public_key.h>
+#include <keys/user-type.h>
#include "asymmetric_keys.h"

/*
@@ -37,13 +39,118 @@ 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, *password = params->password;
+ int ret;
+
+ pr_devel("==>%s()\n", __func__);
+
+ if (key->type != &key_type_asymmetric ||
+ (password && password->type != &key_type_logon))
+ 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").
+ *
+ * If the key needs to be unlocked, a password can be supplied in a logon key
+ * specified by params->password.
+ *
+ * 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").
+ *
+ * If the private key needs to be unlocked, a password can be supplied in a
+ * logon key specified by params->password.
+ *
+ * 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").
+ *
+ * If the private key needs to be unlocked, a password can be supplied in a
+ * logon key specified by params->password.
+ *
+ * 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
+ * @password: Logon key containing passphrase if one is needed
* @sig: The signature to check
*
* Returns 0 if successful or else an error.
*/
int verify_signature(const struct key *key,
+ const struct key *password,
const struct public_key_signature *sig)
{
const struct asymmetric_key_subtype *subtype;
@@ -51,7 +158,8 @@ int verify_signature(const struct key *key,

pr_devel("==>%s()\n", __func__);

- if (key->type != &key_type_asymmetric)
+ if (key->type != &key_type_asymmetric ||
+ (password && password->type != &key_type_logon))
return -EINVAL;
subtype = asymmetric_key_subtype(key);
if (!subtype ||
@@ -60,7 +168,7 @@ int verify_signature(const struct key *key,
if (!subtype->verify_signature)
return -ENOTSUPP;

- ret = subtype->verify_signature(key, sig);
+ ret = subtype->verify_signature(key, password, sig);

pr_devel("<==%s() = %d\n", __func__, ret);
return ret;
diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h
index 882ca0e1e7a5..1db6f1933dbd 100644
--- a/include/crypto/public_key.h
+++ b/include/crypto/public_key.h
@@ -14,6 +14,8 @@
#ifndef _LINUX_PUBLIC_KEY_H
#define _LINUX_PUBLIC_KEY_H

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

extern void public_key_signature_free(struct public_key_signature *sig);
@@ -54,8 +57,14 @@ extern int restrict_link_by_signature(struct key *trust_keyring,
const struct key_type *type,
const union key_payload *payload);

-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 key *,
+ const struct public_key_signature *);

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

+struct kernel_pkey_query;
+struct kernel_pkey_params;
struct public_key_signature;

/*
@@ -34,8 +36,16 @@ 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 key *password,
const struct public_key_signature *sig);
};

diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c
index 80052ed8d467..09105f7d54d2 100644
--- a/security/integrity/digsig_asymmetric.c
+++ b/security/integrity/digsig_asymmetric.c
@@ -110,7 +110,7 @@ int asymmetric_verify(struct key *keyring, const char *sig,
pks.digest_size = datalen;
pks.s = hdr->sig;
pks.s_size = siglen;
- ret = verify_signature(key, &pks);
+ ret = verify_signature(key, NULL, &pks);
key_put(key);
pr_debug("%s() = %d\n", __func__, ret);
return ret;

2016-05-11 19:11:01

by David Woodhouse

[permalink] [raw]
Subject: Re: [RFC PATCH 8/8] KEYS: Implement PKCS#8 RSA Private Key parser [ver 3]

On Wed, 2016-05-11 at 15:22 +0100, David Howells wrote:
> Implement PKCS#8 RSA Private Key format [RFC 5208] parser for the
> asymmetric key type.  For the moment, this will only support unencrypted
> DER blobs.  PEM and decryption can be added later.

I would recommend *not* adding PEM and decryption support. That can
live in userspace. You don't want to end up with the whole set of
handlers for all the weird formats, from PKCS#12 to OpenSSL's non-
standard encrypted PEM files.

Trust me, I implemented a whole bunch of that for OpenConnect. You
don't want it. Just mandate unencrypted binary PKCS#8 (or PKCS#1).

--
David Woodhouse Open Source Technology Centre
[email protected] Intel Corporation


Attachments:
smime.p7s (5.62 kB)

2016-05-11 22:17:34

by Mat Martineau

[permalink] [raw]
Subject: Re: [RFC PATCH 2/8] KEYS: Provide keyctls to drive the new key type ops for asymmetric keys [ver 3]


On Wed, 11 May 2016, David Howells wrote:

> diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt
> index ca72b70a24b9..01c2ae28a8c0 100644
> --- a/Documentation/security/keys.txt
> +++ b/Documentation/security/keys.txt
> + If the key needs to be unlocked with a password, a logon-type key that
> + holds the password may be given as the password argument
...
> + If the key must be unlocked with a password before it can be used,
> + password_id should point to a logon-type key that holds this.

It should be noted that the password_id should be 0 if no password is to
be used.

> diff --git a/security/keys/keyctl_pkey.c b/security/keys/keyctl_pkey.c
> new file mode 100644
> index 000000000000..7f51db984aaa
> --- /dev/null
> +++ b/security/keys/keyctl_pkey.c
> +long keyctl_pkey_e_d_s(int op,
...
> + ret = params.key->type->asym_eds_op(&params, in, out);

Need to check for NULL asym_eds_op before calling.


Regards,

--
Mat Martineau
Intel OTC


2016-05-11 23:50:05

by Mat Martineau

[permalink] [raw]
Subject: Re: [RFC PATCH 5/8] KEYS: Provide software public key query function [ver 3]


On Wed, 11 May 2016, David Howells wrote:

> Provide a query function for the software public key implementation. This
> permits information about such a key to be obtained using
> query_asymmetric_key() or KEYCTL_PKEY_QUERY.
>
> Signed-off-by: David Howells <[email protected]>
> ---
>
> crypto/asymmetric_keys/public_key.c | 96 ++++++++++++++++++++++++++++++-----
> 1 file changed, 82 insertions(+), 14 deletions(-)
>
> diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c
> index 96983906d2a2..e9967e5a2c25 100644
> --- a/crypto/asymmetric_keys/public_key.c
> +++ b/crypto/asymmetric_keys/public_key.c
> +static int software_key_query(const struct kernel_pkey_params *params,
> + struct kernel_pkey_query *info)
> +{
...
> + 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;

If len > UINT16_MAX, should UINT16_MAX be reported as the max size?
Similar question for len*8 and key_size.

--
Mat Martineau
Intel OTC


2016-05-12 00:09:48

by Mat Martineau

[permalink] [raw]
Subject: Re: [RFC PATCH 8/8] KEYS: Implement PKCS#8 RSA Private Key parser [ver 3]


On Wed, 11 May 2016, David Howells wrote:

> diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
> index 6516855bec18..417035a53e98 100644
> --- a/crypto/asymmetric_keys/Makefile
> +++ b/crypto/asymmetric_keys/Makefile
> @@ -34,6 +34,19 @@ clean-files += x509_akid-asn1.c x509_akid-asn1.h
> #
> # PKCS#7 message handling

Update to PKCS#8

> #
> +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


--
Mat Martineau
Intel OTC

2016-05-12 00:18:24

by Tadeusz Struk

[permalink] [raw]
Subject: Re: [RFC PATCH 5/8] KEYS: Provide software public key query function [ver 3]

On 05/11/2016 04:50 PM, Mat Martineau wrote:
>
>> + 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;
>
> If len > UINT16_MAX, should UINT16_MAX be reported as the max size? Similar question for len*8 and key_size.

Hi Mat,
Since all the operations performed are mod key->n the output will never be bigger
than size of pkey->n, which is the return value from crypto_akcipher_maxsize(tfm)
for a given key.
Thanks,
--
TS

2016-05-12 10:16:41

by David Howells

[permalink] [raw]
Subject: Re: [RFC PATCH 2/8] KEYS: Provide keyctls to drive the new key type ops for asymmetric keys [ver 3]

Mat Martineau <[email protected]> wrote:

> > + If the key must be unlocked with a password before it can be used,
> > + password_id should point to a logon-type key that holds this.
>
> It should be noted that the password_id should be 0 if no password is to be
> used.

Currently I'm just ignoring it if I don't need it, but this makes more sense.

David

2016-05-12 10:19:26

by David Howells

[permalink] [raw]
Subject: Re: [RFC PATCH 5/8] KEYS: Provide software public key query function [ver 3]

Mat Martineau <[email protected]> wrote:

> > + 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;
>
> If len > UINT16_MAX, should UINT16_MAX be reported as the max size? Similar
> question for len*8 and key_size.

key_size is 32 bits, but the other sizes are all 16 bits, so you would need a
524288-bit key to exceed their capacity. I'm not sure that's likely anytime
soon, but should I just make all the sizes 32-bit anyway?

David

2016-05-12 10:21:01

by David Howells

[permalink] [raw]
Subject: Re: [RFC PATCH 8/8] KEYS: Implement PKCS#8 RSA Private Key parser [ver 3]

Mat Martineau <[email protected]> wrote:

> > # PKCS#7 message handling
>
> Update to PKCS#8

I guess I've typed PKCS#7 too many times :-)

David

2016-05-12 11:04:13

by David Woodhouse

[permalink] [raw]
Subject: Re: [RFC PATCH 2/8] KEYS: Provide keyctls to drive the new key type ops for asymmetric keys [ver 3]

On Wed, 2016-05-11 at 15:17 -0700, Mat Martineau wrote:
>
> On Wed, 11 May 2016, David Howells wrote:
>
> > diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt
> > index ca72b70a24b9..01c2ae28a8c0 100644
> > --- a/Documentation/security/keys.txt
> > +++ b/Documentation/security/keys.txt
> > +     If the key needs to be unlocked with a password, a logon-type key that
> > +     holds the password may be given as the password argument
> ...
> > +     If the key must be unlocked with a password before it can be used,
> > +     password_id should point to a logon-type key that holds this.
>
> It should be noted that the password_id should be 0 if no password is to 
> be used.

Hm, I would like to properly explore the use cases for these passwords,
before any API is set in stone.

To start with, I'll insist quite strongly that we should never be
passing an encrypted key into the kernel alongside the password needed
to decrypt it. We should let userspace do that gruntwork, and pass in a
canonical DER PKCS#8 (or PKCS#1) blob. As I said before, the other way
lies madness, and requests to support all the obscure formats that keys
are stored in.

So where *might* we want a password... mostly for things like TPM and
other crypto hardware (USB tokens, HSMs). And the usage model there is
normally that the password isn't tied to the *key*, it's a password or
PIN to unlock the *device*.

So I'm not quite sure this 'password_id' makes much sense at all...
unless the idea is that you load the (encrypted) key in advance and
then request the password *later* on demand, in order to use it? Is
that something we really need to support?

--
David Woodhouse Open Source Technology Centre
[email protected] Intel Corporation


Attachments:
smime.p7s (5.62 kB)

2016-05-12 17:01:30

by Mat Martineau

[permalink] [raw]
Subject: Re: [RFC PATCH 5/8] KEYS: Provide software public key query function [ver 3]


On Thu, 12 May 2016, David Howells wrote:

> Mat Martineau <[email protected]> wrote:
>
>>> + 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;
>>
>> If len > UINT16_MAX, should UINT16_MAX be reported as the max size? Similar
>> question for len*8 and key_size.
>
> key_size is 32 bits, but the other sizes are all 16 bits, so you would need a
> 524288-bit key to exceed their capacity. I'm not sure that's likely anytime
> soon, but should I just make all the sizes 32-bit anyway?

Given that cryto_akcipher_maxsize() returns an int and keyctl_pkey_query
is part of the userspace API, I support bumping the sizes to 32-bit.

--
Mat Martineau
Intel OTC