summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2018-11-02 14:51:28 +0000
committerDavid Howells <dhowells@redhat.com>2018-11-02 15:09:11 +0000
commit722c93ed66a23dde775ec2bd7928987a00c4b0d4 (patch)
tree8c710d12477003631d112a58ef12f342a1c4e733
parent7194eeed229280e97d2402a8c2e06c9552fa612f (diff)
downloadkeyutils-722c93ed66a23dde775ec2bd7928987a00c4b0d4.tar.gz
Add public key operations for encrypt, decrypt, sign and verify
Add encryption, decryption, signature creation and signature verification public key operations. 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 Signed-off-by: David Howells <dhowells@redhat.com> Acked-and-tested-by: Denis Kenzior <denkenz@gmail.com>
-rw-r--r--keyctl.c259
-rw-r--r--keyutils.c63
-rw-r--r--keyutils.h50
-rw-r--r--version.lds5
4 files changed, 377 insertions, 0 deletions
diff --git a/keyctl.c b/keyctl.c
index 321fdb9..2c8fdff 100644
--- a/keyctl.c
+++ b/keyctl.c
@@ -16,8 +16,10 @@
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
+#include <fcntl.h>
#include <ctype.h>
#include <errno.h>
+#include <sys/stat.h>
#include <asm/unistd.h>
#include "keyutils.h"
#include <limits.h>
@@ -71,6 +73,11 @@ static nr void act_keyctl_dh_compute(int argc, char *argv[]);
static nr void act_keyctl_dh_compute_kdf(int argc, char *argv[]);
static nr void act_keyctl_dh_compute_kdf_oi(int argc, char *argv[]);
static nr void act_keyctl_restrict_keyring(int argc, char *argv[]);
+static nr void act_keyctl_pkey_query(int argc, char *argv[]);
+static nr void act_keyctl_pkey_encrypt(int argc, char *argv[]);
+static nr void act_keyctl_pkey_decrypt(int argc, char *argv[]);
+static nr void act_keyctl_pkey_sign(int argc, char *argv[]);
+static nr void act_keyctl_pkey_verify(int argc, char *argv[]);
const struct command commands[] = {
{ act_keyctl___version, "--version", "" },
@@ -93,6 +100,11 @@ const struct command commands[] = {
{ act_keyctl_padd, "padd", "<type> <desc> <keyring>" },
{ act_keyctl_pinstantiate, "pinstantiate","<key> <keyring>" },
{ act_keyctl_pipe, "pipe", "<key>" },
+ { act_keyctl_pkey_query, "pkey_query", "<key> <pass> [k=v]*" },
+ { act_keyctl_pkey_encrypt, "pkey_encrypt", "<key> <pass> <datafile> [k=v]*" },
+ { act_keyctl_pkey_decrypt, "pkey_decrypt", "<key> <pass> <datafile> [k=v]*" },
+ { act_keyctl_pkey_sign, "pkey_sign", "<key> <pass> <datafile> [k=v]*" },
+ { act_keyctl_pkey_verify, "pkey_verify", "<key> <pass> <datafile> <sigfile> [k=v]*" },
{ act_keyctl_prequest2, "prequest2", "<type> <desc> [<dest_keyring>]" },
{ act_keyctl_print, "print", "<key>" },
{ act_keyctl_pupdate, "pupdate", "<key>" },
@@ -125,6 +137,7 @@ static int dump_key_tree(key_serial_t keyring, const char *name, int hex_key_IDs
static void format(void) __attribute__((noreturn));
static void error(const char *msg) __attribute__((noreturn));
static key_serial_t get_key_id(char *arg);
+static void *read_file(const char *name, size_t *_size);
static uid_t myuid;
static gid_t mygid, *mygroups;
@@ -1841,6 +1854,219 @@ static void act_keyctl_restrict_keyring(int argc, char *argv[])
exit(0);
}
+/*
+ * Parse public key operation info arguments.
+ */
+static void pkey_parse_info(char **argv, char info[4096])
+{
+ int i_len = 0;
+
+ /* A number of key=val pairs can be provided after the main arguments
+ * to inform the kernel of things like encoding type and hash function.
+ */
+ for (; *argv; argv++) {
+ int n = strlen(argv[0]);
+
+ if (!memchr(argv[0], '=', n)) {
+ fprintf(stderr, "Option not in key=val form\n");
+ exit(2);
+ }
+ if (n + 1 > 4096 - 1 - i_len) {
+ fprintf(stderr, "Too many info options\n");
+ exit(2);
+ }
+
+ if (i_len > 0)
+ info[i_len++] = ' ';
+ memcpy(info + i_len, argv[0], n);
+ i_len += n;
+ }
+
+ info[i_len] = 0;
+}
+
+/*
+ * Query a public key.
+ */
+static void act_keyctl_pkey_query(int argc, char *argv[])
+{
+ struct keyctl_pkey_query result;
+ key_serial_t key;
+ char info[4096];
+
+ if (argc < 3)
+ format();
+ pkey_parse_info(argv + 2, info);
+
+ key = get_key_id(argv[1]);
+ if (strcmp(argv[2], "0") != 0) {
+ fprintf(stderr, "Password passing is not yet supported\n");
+ exit(2);
+ }
+
+ if (keyctl_pkey_query(key, info, &result) < 0)
+ error("keyctl_pkey_query");
+
+ printf("key_size=%u\n", result.key_size);
+ printf("max_data_size=%u\n", result.max_data_size);
+ printf("max_sig_size=%u\n", result.max_sig_size);
+ printf("max_enc_size=%u\n", result.max_enc_size);
+ printf("max_dec_size=%u\n", result.max_dec_size);
+ printf("encrypt=%c\n", result.supported_ops & KEYCTL_SUPPORTS_ENCRYPT ? 'y' : 'n');
+ printf("decrypt=%c\n", result.supported_ops & KEYCTL_SUPPORTS_DECRYPT ? 'y' : 'n');
+ printf("sign=%c\n", result.supported_ops & KEYCTL_SUPPORTS_SIGN ? 'y' : 'n');
+ printf("verify=%c\n", result.supported_ops & KEYCTL_SUPPORTS_VERIFY ? 'y' : 'n');
+ exit(0);
+}
+
+/*
+ * Encrypt a blob.
+ */
+static void act_keyctl_pkey_encrypt(int argc, char *argv[])
+{
+ struct keyctl_pkey_query result;
+ key_serial_t key;
+ size_t in_len;
+ long out_len;
+ void *in, *out;
+ char info[4096];
+
+ if (argc < 5)
+ format();
+ pkey_parse_info(argv + 4, info);
+
+ key = get_key_id(argv[1]);
+ if (strcmp(argv[2], "0") != 0) {
+ fprintf(stderr, "Password passing is not yet supported\n");
+ exit(2);
+ }
+ in = read_file(argv[3], &in_len);
+
+ if (keyctl_pkey_query(key, info, &result) < 0)
+ error("keyctl_pkey_query");
+
+ out = malloc(result.max_dec_size);
+ if (!out)
+ error("malloc");
+
+ out_len = keyctl_pkey_encrypt(key, info,
+ in, in_len, out, result.max_dec_size);
+ if (out_len < 0)
+ error("keyctl_pkey_encrypt");
+
+ if (fwrite(out, out_len, 1, stdout) != 1)
+ error("stdout");
+ exit(0);
+}
+
+/*
+ * Decrypt a blob.
+ */
+static void act_keyctl_pkey_decrypt(int argc, char *argv[])
+{
+ struct keyctl_pkey_query result;
+ key_serial_t key;
+ size_t in_len;
+ long out_len;
+ void *in, *out;
+ char info[4096];
+
+ if (argc < 5)
+ format();
+ pkey_parse_info(argv + 4, info);
+
+ key = get_key_id(argv[1]);
+ if (strcmp(argv[2], "0") != 0) {
+ fprintf(stderr, "Password passing is not yet supported\n");
+ exit(2);
+ }
+ in = read_file(argv[3], &in_len);
+
+ if (keyctl_pkey_query(key, info, &result) < 0)
+ error("keyctl_pkey_query");
+
+ out = malloc(result.max_enc_size);
+ if (!out)
+ error("malloc");
+
+ out_len = keyctl_pkey_decrypt(key, info,
+ in, in_len, out, result.max_enc_size);
+ if (out_len < 0)
+ error("keyctl_pkey_decrypt");
+
+ if (fwrite(out, out_len, 1, stdout) != 1)
+ error("stdout");
+ exit(0);
+}
+
+/*
+ * Create a signature
+ */
+static void act_keyctl_pkey_sign(int argc, char *argv[])
+{
+ struct keyctl_pkey_query result;
+ key_serial_t key;
+ size_t in_len;
+ long out_len;
+ void *in, *out;
+ char info[4096];
+
+ if (argc < 5)
+ format();
+ pkey_parse_info(argv + 4, info);
+
+ key = get_key_id(argv[1]);
+ if (strcmp(argv[2], "0") != 0) {
+ fprintf(stderr, "Password passing is not yet supported\n");
+ exit(2);
+ }
+ in = read_file(argv[3], &in_len);
+
+ if (keyctl_pkey_query(key, info, &result) < 0)
+ error("keyctl_pkey_query");
+
+ out = malloc(result.max_sig_size);
+ if (!out)
+ error("malloc");
+
+ out_len = keyctl_pkey_sign(key, info,
+ in, in_len, out, result.max_sig_size);
+ if (out_len < 0)
+ error("keyctl_pkey_sign");
+
+ if (fwrite(out, out_len, 1, stdout) != 1)
+ error("stdout");
+ exit(0);
+}
+
+/*
+ * Verify a signature.
+ */
+static void act_keyctl_pkey_verify(int argc, char *argv[])
+{
+ key_serial_t key;
+ size_t data_len, sig_len;
+ void *data, *sig;
+ char info[4096];
+
+ if (argc < 5)
+ format();
+ pkey_parse_info(argv + 5, info);
+
+ key = get_key_id(argv[1]);
+ if (strcmp(argv[2], "0") != 0) {
+ fprintf(stderr, "Password passing is not yet supported\n");
+ exit(2);
+ }
+ data = read_file(argv[3], &data_len);
+ sig = read_file(argv[4], &sig_len);
+
+ if (keyctl_pkey_verify(key, info,
+ data, data_len, sig, sig_len) < 0)
+ error("keyctl_pkey_verify");
+ exit(0);
+}
+
/*****************************************************************************/
/*
* parse a key identifier
@@ -1909,6 +2135,39 @@ incorrect_key_by_name_spec:
} /* end get_key_id() */
+/*
+ * Read the contents of a file into a buffer and return it.
+ */
+static void *read_file(const char *name, size_t *_size)
+{
+ struct stat st;
+ ssize_t r;
+ void *p;
+ int fd;
+
+ fd = open(name, O_RDONLY);
+ if (fd < 0)
+ error(name);
+ if (fstat(fd, &st) < 0)
+ error(name);
+
+ p = malloc(st.st_size);
+ if (!p)
+ error("malloc");
+ r = read(fd, p, st.st_size);
+ if (r == -1)
+ error(name);
+ if (r != st.st_size) {
+ fprintf(stderr, "%s: Short read\n", name);
+ exit(1);
+ }
+ if (close(fd) < 0)
+ error(name);
+
+ *_size = st.st_size;
+ return p;
+}
+
/*****************************************************************************/
/*
* recursively display a key/keyring tree
diff --git a/keyutils.c b/keyutils.c
index d2bb34c..d2df4b1 100644
--- a/keyutils.c
+++ b/keyutils.c
@@ -264,6 +264,69 @@ long keyctl_restrict_keyring(key_serial_t keyring, const char *type,
return keyctl(KEYCTL_RESTRICT_KEYRING, keyring, type, restriction);
}
+long keyctl_pkey_query(key_serial_t key_id,
+ const char *info,
+ struct keyctl_pkey_query *result)
+{
+ return keyctl(KEYCTL_PKEY_QUERY, key_id, info, result);
+}
+
+long keyctl_pkey_encrypt(key_serial_t key_id,
+ const char *info,
+ const void *data, size_t data_len,
+ void *enc, size_t enc_len)
+{
+ struct keyctl_pkey_params params = {
+ .key_id = key_id,
+ .in_len = data_len,
+ .out_len = enc_len,
+ };
+
+ return keyctl(KEYCTL_PKEY_ENCRYPT, &params, info, data, enc);
+}
+
+long keyctl_pkey_decrypt(key_serial_t key_id,
+ const char *info,
+ const void *enc, size_t enc_len,
+ void *data, size_t data_len)
+{
+ struct keyctl_pkey_params params = {
+ .key_id = key_id,
+ .in_len = enc_len,
+ .out_len = data_len,
+ };
+
+ return keyctl(KEYCTL_PKEY_DECRYPT, &params, info, enc, data);
+}
+
+long keyctl_pkey_sign(key_serial_t key_id,
+ const char *info,
+ const void *data, size_t data_len,
+ void *sig, size_t sig_len)
+{
+ struct keyctl_pkey_params params = {
+ .key_id = key_id,
+ .in_len = data_len,
+ .out_len = sig_len,
+ };
+
+ return keyctl(KEYCTL_PKEY_SIGN, &params, info, data, sig);
+}
+
+long keyctl_pkey_verify(key_serial_t key_id,
+ const char *info,
+ const void *data, size_t data_len,
+ const void *sig, size_t sig_len)
+{
+ struct keyctl_pkey_params params = {
+ .key_id = key_id,
+ .in_len = data_len,
+ .in2_len = sig_len,
+ };
+
+ return keyctl(KEYCTL_PKEY_VERIFY, &params, info, data, sig);
+}
+
/*****************************************************************************/
/*
* fetch key description into an allocated buffer
diff --git a/keyutils.h b/keyutils.h
index 89c5b08..911e93e 100644
--- a/keyutils.h
+++ b/keyutils.h
@@ -100,6 +100,11 @@ typedef uint32_t key_perm_t;
#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 */
@@ -116,6 +121,31 @@ struct keyctl_kdf_params {
uint32_t __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 {
+ unsigned int supported_ops; /* Which ops are supported */
+ unsigned int key_size; /* Size of the key in bits */
+ unsigned short max_data_size; /* Maximum size of raw data to sign in bytes */
+ unsigned short max_sig_size; /* Maximum size of signature in bytes */
+ unsigned short max_enc_size; /* Maximum size of encrypted blob in bytes */
+ unsigned short max_dec_size; /* Maximum size of decrypted blob in bytes */
+ unsigned int __spare[10];
+};
+
+struct keyctl_pkey_params {
+ key_serial_t key_id; /* Serial no. of public key to use */
+ unsigned int in_len; /* Input data size */
+ union {
+ unsigned int out_len; /* Output buffer size (encrypt/decrypt/sign) */
+ unsigned int in2_len; /* Second input data size (verify) */
+ };
+ unsigned int __spare[7];
+};
+
/*
* syscall wrappers
*/
@@ -178,6 +208,26 @@ extern long keyctl_dh_compute_kdf(key_serial_t private, key_serial_t prime,
extern long keyctl_restrict_keyring(key_serial_t keyring, const char *type,
const char *restriction);
+extern long keyctl_pkey_query(key_serial_t key_id,
+ const char *info,
+ struct keyctl_pkey_query *result);
+extern long keyctl_pkey_encrypt(key_serial_t key_id,
+ const char *info,
+ const void *data, size_t data_len,
+ void *enc, size_t enc_len);
+extern long keyctl_pkey_decrypt(key_serial_t key_id,
+ const char *info,
+ const void *enc, size_t enc_len,
+ void *data, size_t data_len);
+extern long keyctl_pkey_sign(key_serial_t key_id,
+ const char *info,
+ const void *data, size_t data_len,
+ void *sig, size_t sig_len);
+extern long keyctl_pkey_verify(key_serial_t key_id,
+ const char *info,
+ const void *data, size_t data_len,
+ const void *sig, size_t sig_len);
+
/*
* utilities
*/
diff --git a/version.lds b/version.lds
index 4c5babe..018d4f3 100644
--- a/version.lds
+++ b/version.lds
@@ -66,6 +66,11 @@ KEYUTILS_1.6 {
/* management functions */
keyctl_dh_compute;
keyctl_dh_compute_alloc;
+ keyctl_pkey_query;
+ keyctl_pkey_encrypt;
+ keyctl_pkey_decrypt;
+ keyctl_pkey_sign;
+ keyctl_pkey_verify;
} KEYUTILS_1.5;