Hi Mat,
On 07/20/2016 05:19 PM, Mat Martineau wrote:
The kernel (as of v4.8) has asymmetric crypto methods available
using
the keyctl API which make use of the kernel keyrings. The main
advantage of this API over AF_ALG is that typical asymmetric
operations involve fewer system calls.
---
ell/key.c | 200 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ell/key.h | 21 +++++++
2 files changed, 221 insertions(+)
diff --git a/ell/key.c b/ell/key.c
index 60602f9..85e461f 100644
--- a/ell/key.c
+++ b/ell/key.c
@@ -29,10 +29,12 @@
#include <stdint.h>
#include <sys/syscall.h>
#include <linux/keyctl.h>
+#include <errno.h>
#include "private.h"
#include "util.h"
#include "key.h"
+#include "string.h"
#ifndef KEYCTL_DH_COMPUTE
#define KEYCTL_DH_COMPUTE 23
@@ -44,11 +46,47 @@ struct keyctl_dh_params {
};
#endif
+#ifndef KEYCTL_PKEY_QUERY
+#define KEYCTL_PKEY_QUERY 24
+#define KEYCTL_PKEY_ENCRYPT 25
+#define KEYCTL_PKEY_DECRYPT 26
+#define KEYCTL_PKEY_SIGN 27
+#define KEYCTL_PKEY_VERIFY 28
+
+#define KEYCTL_SUPPORTS_ENCRYPT 0x01
+#define KEYCTL_SUPPORTS_DECRYPT 0x02
+#define KEYCTL_SUPPORTS_SIGN 0x04
+#define KEYCTL_SUPPORTS_VERIFY 0x08
+
+struct keyctl_pkey_query {
+ uint32_t supported_ops;
+ uint32_t key_size;
+ uint16_t max_data_size;
+ uint16_t max_sig_size;
+ uint16_t max_enc_size;
+ uint16_t max_dec_size;
+
+ uint32_t __spare[10];
+};
+
+struct keyctl_pkey_params {
+ int32_t key_id;
+ uint32_t in_len;
+ union {
+ uint32_t out_len;
+ uint32_t in2_len;
+ };
+ uint32_t __spare[7];
+};
+#endif
+
static int32_t internal_keyring;
struct l_key {
int type;
int32_t serial;
+ char *encoding;
+ char *hash;
Should these be const char?
};
struct l_keyring {
@@ -117,6 +155,43 @@ static long kernel_unlink_key(int32_t key_serial, int32_t
ring_serial)
return result >= 0 ? result : -errno;
}
+static char *format_key_info(const char *encoding, const char *hash)
+{
+ struct l_string *info;
+
+ if (!encoding && !hash)
+ return NULL;
+
+ info = l_string_new(0);
+
+ if (encoding)
+ l_string_append_printf(info, "enc=%s ", encoding);
+
+ if (hash)
+ l_string_append_printf(info, "hash=%s", hash);
+
+ return l_string_free(info, false);
+}
+
+static long kernel_query_key(int32_t key_serial, size_t *size, bool *public,
+ const char *encoding, const char *hash)
+{
+ long result;
+ struct keyctl_pkey_query query;
+ char *info = format_key_info(encoding, hash);
+
+ result = syscall(__NR_keyctl, KEYCTL_PKEY_QUERY, key_serial, 0,
+ info ?: "", &query);
+ if (result == 0) {
+ *size = query.key_size;
+ *public = ((query.supported_ops & KEYCTL_SUPPORTS_ENCRYPT) &&
+ !(query.supported_ops & KEYCTL_SUPPORTS_DECRYPT));
+ }
+ l_free(info);
+
+ return result >= 0 ? result : -errno;
+}
+
static long kernel_dh_compute(int32_t private, int32_t prime, int32_t base,
void *payload, size_t len)
{
@@ -132,6 +207,40 @@ static long kernel_dh_compute(int32_t private, int32_t prime,
int32_t base,
return result >= 0 ? result : -errno;
}
+static long kernel_key_eds(int op, int32_t serial, const char *encoding,
+ const char *hash, const void *in, void *out,
+ size_t len_in, size_t len_out)
+{
+ long result;
+ struct keyctl_pkey_params params = { .key_id = serial,
+ .in_len = len_in,
+ .out_len = len_out };
+ char *info = format_key_info(encoding, hash);
+
+ result = syscall(__NR_keyctl, op, ¶ms, info ?: "", in, out);
+ l_free(info);
+
+ return result >= 0 ? result : -errno;
+}
+
+static long kernel_key_verify(int32_t serial, const char *encoding,
+ const char *hash, const void *data,
+ const void *sig, size_t len_data,
+ size_t len_sig)
+{
+ long result;
+ struct keyctl_pkey_params params = { .key_id = serial,
+ .in_len = len_data,
+ .in2_len = len_sig };
+ char *info = format_key_info(encoding, hash);
+
+ result = syscall(__NR_keyctl, KEYCTL_PKEY_VERIFY, ¶ms, info ?: "",
+ data, sig);
+ l_free(info);
+
+ return result >= 0 ? result : -errno;
+}
+
static bool setup_internal_keyring(void)
{
internal_keyring = kernel_add_key("keyring", "ell-internal", NULL,
0,
@@ -220,6 +329,12 @@ LIB_EXPORT ssize_t l_key_get_size(struct l_key *key)
return kernel_read_key(key->serial, NULL, 0);
}
+bool l_key_get_info(struct l_key *key, size_t *bits, bool *public)
+{
+ return !kernel_query_key(key->serial, bits, public, key->encoding,
+ key->hash);
+}
+
How is this related to l_key_get_size()?
We might want to start documenting the l_key methods...
static bool compute_common(struct l_key *base,
struct l_key *private,
struct l_key *prime,
@@ -255,6 +370,91 @@ LIB_EXPORT bool l_key_compute_dh_secret(struct l_key *other_public,
return compute_common(other_public, private, prime, payload, len);
}
+LIB_EXPORT bool l_key_set_cipher(struct l_key *key,
+ enum l_asymmetric_cipher_type cipher)
+{
+ if (unlikely(!key))
+ return false;
+
+ switch (cipher) {
+ case L_CIPHER_RSA_PKCS1_V1_5:
+ key->encoding = "pkcs1";
+ break;
+ }
+
+ return true;
+}
+
+LIB_EXPORT bool l_key_set_checksum_info(struct l_key *key,
+ enum l_checksum_type checksum)
+{
+ if (unlikely(!key))
+ return false;
+
+ switch (checksum) {
+ case L_CHECKSUM_MD5:
+ key->hash = "md5";
+ break;
+ case L_CHECKSUM_SHA1:
+ key->hash = "sha1";
+ break;
+ case L_CHECKSUM_SHA256:
+ key->hash = "sha256";
+ break;
+ case L_CHECKSUM_SHA384:
+ key->hash = "sha384";
+ break;
+ case L_CHECKSUM_SHA512:
+ key->hash = "sha512";
+ break;
+ }
+
+ return true;
+}
+
+static ssize_t eds_common(struct l_key *key, const void *in, void *out,
+ size_t len_in, size_t len_out, int op)
Whats eds stand for?
+{
+ if (unlikely(!key))
+ return -EINVAL;
+
+ return kernel_key_eds(op, key->serial, key->encoding, key->hash, in,
+ out, len_in, len_out);
+}
+
+LIB_EXPORT ssize_t l_key_encrypt(struct l_key *key, const void *in, void *out,
+ size_t len_in, size_t len_out)
+{
+ return eds_common(key, in, out, len_in, len_out, KEYCTL_PKEY_ENCRYPT);
+}
+
+LIB_EXPORT ssize_t l_key_decrypt(struct l_key *key, const void *in, void *out,
+ size_t len_in, size_t len_out)
+{
+ return eds_common(key, in, out, len_in, len_out, KEYCTL_PKEY_DECRYPT);
+}
+
+LIB_EXPORT ssize_t l_key_sign(struct l_key *key, const void *in, void *out,
+ size_t len_in, size_t len_out)
+{
+ return eds_common(key, in, out, len_in, len_out, KEYCTL_PKEY_SIGN);
+}
+
+LIB_EXPORT bool l_key_verify(struct l_key *key, const void *data,
+ const void *sig, size_t len_data,
+ size_t len_sig)
+{
+ long result;
+
+ if (unlikely(!key))
+ return false;
+
+ result = kernel_key_verify(key->serial, key->encoding, key->hash,
+ data, sig, len_data, len_sig);
+
+ return result == 0;
+}
+
LIB_EXPORT struct l_keyring *l_keyring_new(enum l_keyring_type type,
const struct l_keyring *trusted)
{
diff --git a/ell/key.h b/ell/key.h
index 5d981f1..f5176d1 100644
--- a/ell/key.h
+++ b/ell/key.h
@@ -30,6 +30,9 @@ extern "C" {
#include <stddef.h>
#include <stdbool.h>
+#include <ell/checksum.h>
+#include <ell/cipher.h>
+
struct l_key;
struct l_keyring;
@@ -54,12 +57,30 @@ bool l_key_extract(struct l_key *key, void *payload, size_t *len);
ssize_t l_key_get_size(struct l_key *key);
+bool l_key_get_info(struct l_key *key, size_t *bits, bool *public);
+
bool l_key_compute_dh_public(struct l_key *generator, struct l_key *private,
struct l_key *prime, void *payload, size_t *len);
bool l_key_compute_dh_secret(struct l_key *other_public, struct l_key *private,
struct l_key *prime, void *payload, size_t *len);
+bool l_key_set_cipher(struct l_key *key, enum l_asymmetric_cipher_type cipher);
+
+bool l_key_set_checksum_info(struct l_key *key, enum l_checksum_type checksum);
+
Why not make these parameters into encrypt / decrypt / sign / verify
operation?
+ssize_t l_key_encrypt(struct l_key *key, const void *in, void *out,
+ size_t len_in, size_t len_out);
+
+ssize_t l_key_decrypt(struct l_key *key, const void *in, void *out,
+ size_t len_in, size_t len_out);
+
+ssize_t l_key_sign(struct l_key *key, const void *in, void *out,
+ size_t len_in, size_t len_out);
+
+bool l_key_verify(struct l_key *key, const void *data, const void *sig,
+ size_t len_data, size_t len_sig);
+
struct l_keyring *l_keyring_new(enum l_keyring_type type,
const struct l_keyring *trust);
Regards,
-Denis