| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815 |
- /*
- *
- * Embedded Linux library
- *
- * Copyright (C) 2016 Intel Corporation. All rights reserved.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
- #define _GNU_SOURCE
- #include <unistd.h>
- #include <stdint.h>
- #include <sys/syscall.h>
- #include <linux/keyctl.h>
- #include <errno.h>
- #include "private.h"
- #include "useful.h"
- #include "key.h"
- #include "string.h"
- #include "random.h"
- #include "missing.h"
- #ifndef KEYCTL_DH_COMPUTE
- #define KEYCTL_DH_COMPUTE 23
- #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];
- };
- /* Work around the missing (pre-4.7) or broken (4.14.{70,71,72} and
- * 4.18.{8,9,10}) kernel declaration of struct keyctl_dh_params
- */
- struct dh_params {
- int32_t private;
- int32_t prime;
- int32_t base;
- };
- #else
- /* When KEYCTL_PKEY_QUERY is defined by the kernel, the
- * struct keyctl_dh_params declaration is valid.
- */
- #define dh_params keyctl_dh_params
- #endif
- #ifndef KEYCTL_RESTRICT_KEYRING
- #define KEYCTL_RESTRICT_KEYRING 29
- #endif
- static int32_t internal_keyring;
- struct l_key {
- int type;
- int32_t serial;
- };
- struct l_keyring {
- int32_t serial;
- };
- static const char * const key_type_names[] = {
- [L_KEY_RAW] = "user",
- [L_KEY_RSA] = "asymmetric",
- };
- static long kernel_add_key(const char *type, const char *description,
- const void *payload, size_t len, int32_t keyring)
- {
- long result;
- result = syscall(__NR_add_key, type, description, payload, len,
- keyring);
- return result >= 0 ? result : -errno;
- }
- static long kernel_read_key(int32_t serial, const void *payload, size_t len)
- {
- long result;
- result = syscall(__NR_keyctl, KEYCTL_READ, serial, payload, len);
- return result >= 0 ? result : -errno;
- }
- static long kernel_update_key(int32_t serial, const void *payload, size_t len)
- {
- long result;
- result = syscall(__NR_keyctl, KEYCTL_UPDATE, serial, payload, len);
- return result >= 0 ? result : -errno;
- }
- static long kernel_invalidate_key(int32_t serial)
- {
- long result;
- result = syscall(__NR_keyctl, KEYCTL_INVALIDATE, serial);
- return result >= 0 ? result : -errno;
- }
- static long kernel_link_key(int32_t key_serial, int32_t ring_serial)
- {
- long result;
- result = syscall(__NR_keyctl, KEYCTL_LINK, key_serial, ring_serial);
- return result >= 0 ? result : -errno;
- }
- static long kernel_unlink_key(int32_t key_serial, int32_t ring_serial)
- {
- long result;
- result = syscall(__NR_keyctl, KEYCTL_UNLINK, key_serial, 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_unwrap(info);
- }
- static long kernel_query_key(int32_t key_serial, const char *encoding,
- const char *hash, size_t *size, bool *public)
- {
- long result;
- struct keyctl_pkey_query query;
- char *info = format_key_info(encoding, hash);
- memset(&query, 0, sizeof(query));
- 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)
- {
- long result;
- struct dh_params params = { .private = private,
- .prime = prime,
- .base = base };
- result = syscall(__NR_keyctl, KEYCTL_DH_COMPUTE, ¶ms, payload, len,
- NULL);
- return result >= 0 ? result : -errno;
- }
- static long kernel_restrict_keyring(int32_t serial, const char *keytype,
- const char *restriction)
- {
- long result;
- result = syscall(__NR_keyctl, KEYCTL_RESTRICT_KEYRING, serial, keytype,
- restriction);
- 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);
- memset(out, 0, len_out);
- 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, size_t data_len,
- const void *sig, size_t sig_len)
- {
- struct keyctl_pkey_params params = {
- .key_id = serial,
- .in_len = data_len,
- .in2_len = sig_len,
- };
- char *info = format_key_info(encoding, hash);
- long result;
- 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,
- KEY_SPEC_THREAD_KEYRING);
- if (internal_keyring <= 0) {
- internal_keyring = 0;
- return false;
- }
- return true;
- }
- LIB_EXPORT struct l_key *l_key_new(enum l_key_type type, const void *payload,
- size_t payload_length)
- {
- struct l_key *key;
- char *description;
- static unsigned long key_idx;
- if (unlikely(!payload))
- return NULL;
- if (unlikely((size_t)type >= L_ARRAY_SIZE(key_type_names)))
- return NULL;
- if (!internal_keyring && !setup_internal_keyring())
- return NULL;
- key = l_new(struct l_key, 1);
- key->type = type;
- description = l_strdup_printf("ell-key-%lu", key_idx++);
- key->serial = kernel_add_key(key_type_names[type], description, payload,
- payload_length, internal_keyring);
- l_free(description);
- if (key->serial < 0) {
- l_free(key);
- key = NULL;
- }
- /*
- * TODO: Query asymmetric key algorithm from the kernel and
- * ensure that it matches the expected l_key_type. This can
- * currently be found by digging through /proc/keys, but a
- * keyctl() op makes more sense.
- */
- return key;
- }
- LIB_EXPORT void l_key_free(struct l_key *key)
- {
- if (unlikely(!key))
- return;
- /*
- * Use invalidate as, unlike revoke, this doesn't delay the
- * key garbage collection and causes the quota used by the
- * key to be released sooner and more predictably.
- */
- kernel_invalidate_key(key->serial);
- l_free(key);
- }
- LIB_EXPORT void l_key_free_norevoke(struct l_key *key)
- {
- if (unlikely(!key))
- return;
- kernel_unlink_key(key->serial, internal_keyring);
- l_free(key);
- }
- LIB_EXPORT bool l_key_update(struct l_key *key, const void *payload, size_t len)
- {
- long error;
- if (unlikely(!key))
- return false;
- error = kernel_update_key(key->serial, payload, len);
- return error == 0;
- }
- LIB_EXPORT bool l_key_extract(struct l_key *key, void *payload, size_t *len)
- {
- long keylen;
- if (unlikely(!key))
- return false;
- keylen = kernel_read_key(key->serial, payload, *len);
- if (keylen < 0 || (size_t)keylen > *len) {
- explicit_bzero(payload, *len);
- return false;
- }
- *len = keylen;
- return true;
- }
- LIB_EXPORT ssize_t l_key_get_payload_size(struct l_key *key)
- {
- return kernel_read_key(key->serial, NULL, 0);
- }
- static const char *lookup_cipher(enum l_key_cipher_type cipher)
- {
- const char* ret = NULL;
- switch (cipher) {
- case L_KEY_RSA_PKCS1_V1_5:
- ret = "pkcs1";
- break;
- case L_KEY_RSA_RAW:
- ret = "raw";
- break;
- }
- return ret;
- }
- static const char *lookup_checksum(enum l_checksum_type checksum)
- {
- const char* ret = NULL;
- switch (checksum) {
- case L_CHECKSUM_NONE:
- break;
- case L_CHECKSUM_MD4:
- ret = "md4";
- break;
- case L_CHECKSUM_MD5:
- ret = "md5";
- break;
- case L_CHECKSUM_SHA1:
- ret = "sha1";
- break;
- case L_CHECKSUM_SHA224:
- ret = "sha224";
- break;
- case L_CHECKSUM_SHA256:
- ret = "sha256";
- break;
- case L_CHECKSUM_SHA384:
- ret = "sha384";
- break;
- case L_CHECKSUM_SHA512:
- ret = "sha512";
- break;
- }
- return ret;
- }
- LIB_EXPORT bool l_key_get_info(struct l_key *key, enum l_key_cipher_type cipher,
- enum l_checksum_type checksum, size_t *bits,
- bool *public)
- {
- if (unlikely(!key))
- return false;
- return !kernel_query_key(key->serial, lookup_cipher(cipher),
- lookup_checksum(checksum), bits,
- public);
- }
- LIB_EXPORT struct l_key *l_key_generate_dh_private(const void *prime_buf,
- size_t prime_len)
- {
- uint8_t *buf;
- const uint8_t *prime = prime_buf;
- size_t prime_bits;
- unsigned int i;
- size_t private_bytes;
- size_t random_bytes;
- struct l_key *private;
- /* Find the prime's bit length excluding leading 0s */
- for (i = 0; i < prime_len && !prime[i]; i++);
- if (i == prime_len || (i == prime_len - 1 && prime[i] < 5))
- return NULL;
- prime_bits = (prime_len - i) * 8 - __builtin_clz(prime[i]);
- /*
- * Generate a random DH private value conforming to 1 < x < p - 1.
- * To do this covering all possible values in this range with the
- * same probability of generating each value generally requires
- * looping. Instead we generate a value in the range
- * [2 ^ (prime_bits - 2), 2 ^ (prime_bits - 1) - 1] by forcing bit
- * prime_bits - 2 to 1, i.e. the range in PKCS #3 Section 7.1 for
- * l equal to prime_bits - 1. This means we're using between
- * one half and one quarter of the full [2, p - 2] range, i.e.
- * between 1 and 2 bits fewer. Note that since p is odd
- * p - 1 has the same bit length as p and so our maximum value
- * 2 ^ (prime_bits - 1) - 1 is still less than p - 1.
- */
- private_bytes = ((prime_bits - 1) + 7) / 8;
- random_bytes = ((prime_bits - 2) + 7) / 8;
- buf = l_malloc(private_bytes);
- l_getrandom(buf + private_bytes - random_bytes, random_bytes);
- buf[0] &= (1 << ((prime_bits - 2) % 8)) - 1;
- buf[0] |= 1 << ((prime_bits - 2) % 8);
- private = l_key_new(L_KEY_RAW, buf, private_bytes);
- explicit_bzero(buf, private_bytes);
- l_free(buf);
- return private;
- }
- static bool compute_common(struct l_key *base, struct l_key *private,
- struct l_key *prime, void *payload, size_t *len)
- {
- long result_len;
- bool usable_payload = *len != 0;
- result_len = kernel_dh_compute(private->serial, prime->serial,
- base->serial, payload, *len);
- if (result_len > 0) {
- *len = result_len;
- return usable_payload;
- }
- return false;
- }
- LIB_EXPORT bool l_key_compute_dh_public(struct l_key *generator,
- struct l_key *private,
- struct l_key *prime,
- void *payload, size_t *len)
- {
- return compute_common(generator, private, prime, payload, len);
- }
- LIB_EXPORT bool l_key_compute_dh_secret(struct l_key *other_public,
- struct l_key *private,
- struct l_key *prime,
- void *payload, size_t *len)
- {
- return compute_common(other_public, private, prime, payload, len);
- }
- static int be_bignum_compare(const uint8_t *a, size_t a_len,
- const uint8_t *b, size_t b_len)
- {
- unsigned int i;
- if (a_len >= b_len) {
- for (i = 0; i < a_len - b_len; i++)
- if (a[i])
- return 1;
- return memcmp(a + i, b, b_len);
- }
- for (i = 0; i < b_len - a_len; i++)
- if (b[i])
- return -1;
- return memcmp(a, b + i, a_len);
- }
- /*
- * Validate that @payload is within range for a private and public key for
- * a DH computation in the finite field group defined by modulus @prime_buf,
- * both numbers stored as big-endian integers. We require a key in the
- * [2, prime - 2] (inclusive) interval. PKCS #3 does not exclude 1 as a
- * private key but other specs do.
- */
- LIB_EXPORT bool l_key_validate_dh_payload(const void *payload, size_t len,
- const void *prime_buf, size_t prime_len)
- {
- static const uint8_t one[] = { 1 };
- uint8_t prime_1[prime_len];
- /*
- * Produce prime - 1 for the payload < prime - 1 check.
- * prime is odd so just zero the LSB.
- */
- memcpy(prime_1, prime_buf, prime_len);
- if (prime_len < 1 || !(prime_1[prime_len - 1] & 1))
- return false;
- prime_1[prime_len - 1] &= ~1;
- if (be_bignum_compare(payload, len, one, 1) <= 0)
- return false;
- if (be_bignum_compare(payload, len, prime_1, prime_len) >= 0)
- return false;
- return true;
- }
- /* Common code for encrypt/decrypt/sign */
- static ssize_t eds_common(struct l_key *key,
- enum l_key_cipher_type cipher,
- enum l_checksum_type checksum, const void *in,
- void *out, size_t len_in, size_t len_out,
- int op)
- {
- if (unlikely(!key))
- return -EINVAL;
- return kernel_key_eds(op, key->serial, lookup_cipher(cipher),
- lookup_checksum(checksum), in, out, len_in,
- len_out);
- }
- LIB_EXPORT ssize_t l_key_encrypt(struct l_key *key,
- enum l_key_cipher_type cipher,
- enum l_checksum_type checksum,
- const void *in, void *out,
- size_t len_in, size_t len_out)
- {
- ssize_t ret_len;
- ret_len = eds_common(key, cipher, checksum, in, out,
- len_in, len_out,
- KEYCTL_PKEY_ENCRYPT);
- return ret_len;
- }
- LIB_EXPORT ssize_t l_key_decrypt(struct l_key *key,
- enum l_key_cipher_type cipher,
- enum l_checksum_type checksum,
- const void *in, void *out,
- size_t len_in, size_t len_out)
- {
- ssize_t ret_len;
- ret_len = eds_common(key, cipher, checksum, in, out, len_in,
- len_out, KEYCTL_PKEY_DECRYPT);
- if (ret_len < 0)
- goto done;
- done:
- return ret_len;
- }
- LIB_EXPORT ssize_t l_key_sign(struct l_key *key,
- enum l_key_cipher_type cipher,
- enum l_checksum_type checksum, const void *in,
- void *out, size_t len_in, size_t len_out)
- {
- ssize_t ret_len;
- ret_len = eds_common(key, cipher, checksum, in, out,
- len_in, len_out,
- KEYCTL_PKEY_SIGN);
- return ret_len;
- }
- LIB_EXPORT bool l_key_verify(struct l_key *key,
- enum l_key_cipher_type cipher,
- enum l_checksum_type checksum, 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, lookup_cipher(cipher),
- lookup_checksum(checksum),
- data, len_data,
- sig, len_sig);
- return result >= 0;
- }
- LIB_EXPORT struct l_keyring *l_keyring_new(void)
- {
- struct l_keyring *keyring;
- char *description;
- static unsigned long keyring_idx;
- if (!internal_keyring && !setup_internal_keyring())
- return NULL;
- keyring = l_new(struct l_keyring, 1);
- description = l_strdup_printf("ell-keyring-%lu", keyring_idx++);
- keyring->serial = kernel_add_key("keyring", description, NULL, 0,
- internal_keyring);
- l_free(description);
- if (keyring->serial < 0) {
- l_free(keyring);
- return NULL;
- }
- return keyring;
- }
- LIB_EXPORT bool l_keyring_restrict(struct l_keyring *keyring,
- enum l_keyring_restriction res,
- const struct l_keyring *trusted)
- {
- char *restriction = NULL;
- long result;
- switch (res) {
- case L_KEYRING_RESTRICT_ASYM:
- case L_KEYRING_RESTRICT_ASYM_CHAIN:
- {
- char *option = "";
- if (res == L_KEYRING_RESTRICT_ASYM_CHAIN)
- option = ":chain";
- restriction = l_strdup_printf("key_or_keyring:%d%s",
- trusted ? trusted->serial : 0,
- option);
- break;
- }
- default:
- /* Unsupported type */
- return NULL;
- }
- result = kernel_restrict_keyring(keyring->serial, "asymmetric",
- restriction);
- l_free(restriction);
- return result == 0;
- }
- LIB_EXPORT void l_keyring_free(struct l_keyring *keyring)
- {
- if (unlikely(!keyring))
- return;
- kernel_invalidate_key(keyring->serial);
- l_free(keyring);
- }
- LIB_EXPORT void l_keyring_free_norevoke(struct l_keyring *keyring)
- {
- if (unlikely(!keyring))
- return;
- kernel_unlink_key(keyring->serial, internal_keyring);
- l_free(keyring);
- }
- LIB_EXPORT bool l_keyring_link(struct l_keyring *keyring,
- const struct l_key *key)
- {
- long error;
- if (unlikely(!keyring) || unlikely(!key))
- return false;
- error = kernel_link_key(key->serial, keyring->serial);
- return error == 0;
- }
- LIB_EXPORT bool l_keyring_unlink(struct l_keyring *keyring,
- const struct l_key *key)
- {
- long error;
- if (unlikely(!keyring) || unlikely(!key))
- return false;
- error = kernel_unlink_key(key->serial, keyring->serial);
- return error == 0;
- }
- LIB_EXPORT bool l_keyring_link_nested(struct l_keyring *keyring,
- const struct l_keyring *nested)
- {
- long error;
- if (unlikely(!keyring) || unlikely(!nested))
- return false;
- error = kernel_link_key(nested->serial, keyring->serial);
- return error == 0;
- }
- LIB_EXPORT bool l_keyring_unlink_nested(struct l_keyring *keyring,
- const struct l_keyring *nested)
- {
- long error;
- if (unlikely(!keyring) || unlikely(!nested))
- return false;
- error = kernel_unlink_key(nested->serial, keyring->serial);
- return error == 0;
- }
- LIB_EXPORT bool l_key_is_supported(uint32_t features)
- {
- long result;
- if (features & L_KEY_FEATURE_DH) {
- result = syscall(__NR_keyctl, KEYCTL_DH_COMPUTE, NULL, "x", 1,
- NULL);
- if (result == -1 && errno == EOPNOTSUPP)
- return false;
- }
- if (features & L_KEY_FEATURE_RESTRICT) {
- result = syscall(__NR_keyctl, KEYCTL_RESTRICT_KEYRING, 0,
- "asymmetric", "");
- if (result == -1 && errno == EOPNOTSUPP)
- return false;
- }
- if (features & L_KEY_FEATURE_CRYPTO) {
- result = syscall(__NR_keyctl, KEYCTL_PKEY_QUERY, 0, 0, "", 0);
- if (result == -1 && errno == EOPNOTSUPP)
- return false;
- }
- return true;
- }
|