From 1c2fabcdb34e436286b4a8760cfbfbff11ea551a Mon Sep 17 00:00:00 2001 From: Eneas U de Queiroz Date: Sat, 3 Nov 2018 15:41:10 -0300 Subject: eng_devcrypto: add configuration options USE_SOFTDRIVERS: whether to use software (not accelerated) drivers CIPHERS: list of ciphers to enable DIGESTS: list of digests to enable Signed-off-by: Eneas U de Queiroz Reviewed-by: Matthias St. Pierre Reviewed-by: Richard Levitte (Merged from https://github.com/openssl/openssl/pull/7585) diff --git a/crypto/engine/eng_devcrypto.c b/crypto/engine/eng_devcrypto.c index a2c9a966f7..5ec38ca8f3 100644 --- a/crypto/engine/eng_devcrypto.c +++ b/crypto/engine/eng_devcrypto.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -36,6 +37,30 @@ * saner... why re-open /dev/crypto for every session? */ static int cfd; +#define DEVCRYPTO_REQUIRE_ACCELERATED 0 /* require confirmation of acceleration */ +#define DEVCRYPTO_USE_SOFTWARE 1 /* allow software drivers */ +#define DEVCRYPTO_REJECT_SOFTWARE 2 /* only disallow confirmed software drivers */ + +#define DEVCRYPTO_DEFAULT_USE_SOFDTRIVERS DEVCRYPTO_REJECT_SOFTWARE +static int use_softdrivers = DEVCRYPTO_DEFAULT_USE_SOFDTRIVERS; + +/* + * cipher/digest status & acceleration definitions + * Make sure the defaults are set to 0 + */ +struct driver_info_st { + enum devcrypto_status_t { + DEVCRYPTO_STATUS_UNUSABLE = -1, /* session open failed */ + DEVCRYPTO_STATUS_UNKNOWN = 0, /* not tested yet */ + DEVCRYPTO_STATUS_USABLE = 1 /* algo can be used */ + } status; + + enum devcrypto_accelerated_t { + DEVCRYPTO_NOT_ACCELERATED = -1, /* software implemented */ + DEVCRYPTO_ACCELERATION_UNKNOWN = 0, /* acceleration support unkown */ + DEVCRYPTO_ACCELERATED = 1 /* hardware accelerated */ + } accelerated; +}; static int clean_devcrypto_session(struct session_op *sess) { if (ioctl(cfd, CIOCFSESSION, &sess->ses) < 0) { @@ -119,13 +144,22 @@ static const struct cipher_data_st { #endif }; -static size_t get_cipher_data_index(int nid) +static size_t find_cipher_data_index(int nid) { size_t i; for (i = 0; i < OSSL_NELEM(cipher_data); i++) if (nid == cipher_data[i].nid) return i; + return (size_t)-1; +} + +static size_t get_cipher_data_index(int nid) +{ + size_t i = find_cipher_data_index(nid); + + if (i != (size_t)-1) + return i; /* * Code further down must make sure that only NIDs in the table above @@ -333,19 +367,40 @@ static int cipher_cleanup(EVP_CIPHER_CTX *ctx) } /* - * Keep a table of known nids and associated methods. + * Keep tables of known nids, associated methods, selected ciphers, and driver + * info. * Note that known_cipher_nids[] isn't necessarily indexed the same way as - * cipher_data[] above, which known_cipher_methods[] is. + * cipher_data[] above, which the other tables are. */ static int known_cipher_nids[OSSL_NELEM(cipher_data)]; static int known_cipher_nids_amount = -1; /* -1 indicates not yet initialised */ static EVP_CIPHER *known_cipher_methods[OSSL_NELEM(cipher_data)] = { NULL, }; +static int selected_ciphers[OSSL_NELEM(cipher_data)]; +static struct driver_info_st cipher_driver_info[OSSL_NELEM(cipher_data)]; + + +static int devcrypto_test_cipher(size_t cipher_data_index) +{ + return (cipher_driver_info[cipher_data_index].status == DEVCRYPTO_STATUS_USABLE + && selected_ciphers[cipher_data_index] == 1 + && (cipher_driver_info[cipher_data_index].accelerated + == DEVCRYPTO_ACCELERATED + || use_softdrivers == DEVCRYPTO_USE_SOFTWARE + || (cipher_driver_info[cipher_data_index].accelerated + != DEVCRYPTO_NOT_ACCELERATED + && use_softdrivers == DEVCRYPTO_REJECT_SOFTWARE))); +} static void prepare_cipher_methods(void) { size_t i; struct session_op sess; unsigned long cipher_mode; +#ifdef CIOCGSESSINFO + struct session_info_op siop; +#endif + + memset(&cipher_driver_info, 0, sizeof(cipher_driver_info)); memset(&sess, 0, sizeof(sess)); sess.key = (void *)"01234567890123456789012345678901234567890123456789"; @@ -353,15 +408,16 @@ static void prepare_cipher_methods(void) for (i = 0, known_cipher_nids_amount = 0; i < OSSL_NELEM(cipher_data); i++) { + selected_ciphers[i] = 1; /* - * Check that the algo is really availably by trying to open and close - * a session. + * Check that the cipher is usable */ sess.cipher = cipher_data[i].devcryptoid; sess.keylen = cipher_data[i].keylen; - if (ioctl(cfd, CIOCGSESSION, &sess) < 0 - || ioctl(cfd, CIOCFSESSION, &sess.ses) < 0) + if (ioctl(cfd, CIOCGSESSION, &sess) < 0) { + cipher_driver_info[i].status = DEVCRYPTO_STATUS_UNUSABLE; continue; + } cipher_mode = cipher_data[i].flags & EVP_CIPH_MODE; @@ -387,15 +443,41 @@ static void prepare_cipher_methods(void) cipher_cleanup) || !EVP_CIPHER_meth_set_impl_ctx_size(known_cipher_methods[i], sizeof(struct cipher_ctx))) { + cipher_driver_info[i].status = DEVCRYPTO_STATUS_UNUSABLE; EVP_CIPHER_meth_free(known_cipher_methods[i]); known_cipher_methods[i] = NULL; } else { + cipher_driver_info[i].status = DEVCRYPTO_STATUS_USABLE; +#ifdef CIOCGSESSINFO + siop.ses = sess.ses; + if (ioctl(cfd, CIOCGSESSINFO, &siop) < 0) + cipher_driver_info[i].accelerated = DEVCRYPTO_ACCELERATION_UNKNOWN; + else if (!(siop.flags & SIOP_FLAG_KERNEL_DRIVER_ONLY)) + cipher_driver_info[i].accelerated = DEVCRYPTO_NOT_ACCELERATED; + else + cipher_driver_info[i].accelerated = DEVCRYPTO_ACCELERATED; +#endif /* CIOCGSESSINFO */ + } + ioctl(cfd, CIOCFSESSION, &sess.ses); + if (devcrypto_test_cipher(i)) { known_cipher_nids[known_cipher_nids_amount++] = cipher_data[i].nid; } } } +static void rebuild_known_cipher_nids(ENGINE *e) +{ + size_t i; + + for (i = 0, known_cipher_nids_amount = 0; i < OSSL_NELEM(cipher_data); i++) { + if (devcrypto_test_cipher(i)) + known_cipher_nids[known_cipher_nids_amount++] = cipher_data[i].nid; + } + ENGINE_unregister_ciphers(e); + ENGINE_register_ciphers(e); +} + static const EVP_CIPHER *get_cipher_method(int nid) { size_t i = get_cipher_data_index(nid); @@ -438,6 +520,36 @@ static int devcrypto_ciphers(ENGINE *e, const EVP_CIPHER **cipher, return *cipher != NULL; } +static void devcrypto_select_all_ciphers(int *cipher_list) +{ + size_t i; + + for (i = 0; i < OSSL_NELEM(cipher_data); i++) + cipher_list[i] = 1; +} + +static int cryptodev_select_cipher_cb(const char *str, int len, void *usr) +{ + int *cipher_list = (int *)usr; + char *name; + const EVP_CIPHER *EVP; + size_t i; + + if (len == 0) + return 1; + if (usr == NULL || (name = OPENSSL_strndup(str, len)) == NULL) + return 0; + EVP = EVP_get_cipherbyname(name); + if (EVP == NULL) + fprintf(stderr, "devcrypto: unknown cipher %s\n", name); + else if ((i = find_cipher_data_index(EVP_CIPHER_nid(EVP))) != (size_t)-1) + cipher_list[i] = 1; + else + fprintf(stderr, "devcrypto: cipher %s not available\n", name); + OPENSSL_free(name); + return 1; +} + /* * We only support digests if the cryptodev implementation supports multiple * data updates and session copying. Otherwise, we would be forced to maintain @@ -493,13 +605,22 @@ static const struct digest_data_st { #endif }; -static size_t get_digest_data_index(int nid) +static size_t find_digest_data_index(int nid) { size_t i; for (i = 0; i < OSSL_NELEM(digest_data); i++) if (nid == digest_data[i].nid) return i; + return (size_t)-1; +} + +static size_t get_digest_data_index(int nid) +{ + size_t i = find_digest_data_index(nid); + + if (i != (size_t)-1) + return i; /* * Code further down must make sure that only NIDs in the table above @@ -516,8 +637,8 @@ static const struct digest_data_st *get_digest_data(int nid) } /* - * Following are the four necessary functions to map OpenSSL functionality - * with cryptodev. + * Following are the five necessary functions to map OpenSSL functionality + * with cryptodev: init, update, final, cleanup, and copy. */ static int digest_init(EVP_MD_CTX *ctx) @@ -630,52 +751,94 @@ static int digest_cleanup(EVP_MD_CTX *ctx) return clean_devcrypto_session(&digest_ctx->sess); } -static int devcrypto_test_digest(size_t digest_data_index) -{ - struct session_op sess1, sess2; - struct cphash_op cphash; - int ret=0; - - memset(&sess1, 0, sizeof(sess1)); - memset(&sess2, 0, sizeof(sess2)); - sess1.mac = digest_data[digest_data_index].devcryptoid; - if (ioctl(cfd, CIOCGSESSION, &sess1) < 0) - return 0; - /* Make sure the driver is capable of hash state copy */ - sess2.mac = sess1.mac; - if (ioctl(cfd, CIOCGSESSION, &sess2) >= 0) { - cphash.src_ses = sess1.ses; - cphash.dst_ses = sess2.ses; - if (ioctl(cfd, CIOCCPHASH, &cphash) >= 0) - ret = 1; - ioctl(cfd, CIOCFSESSION, &sess2.ses); - } - ioctl(cfd, CIOCFSESSION, &sess1.ses); - return ret; -} - /* - * Keep a table of known nids and associated methods. + * Keep tables of known nids, associated methods, selected digests, and + * driver info. * Note that known_digest_nids[] isn't necessarily indexed the same way as - * digest_data[] above, which known_digest_methods[] is. + * digest_data[] above, which the other tables are. */ static int known_digest_nids[OSSL_NELEM(digest_data)]; static int known_digest_nids_amount = -1; /* -1 indicates not yet initialised */ static EVP_MD *known_digest_methods[OSSL_NELEM(digest_data)] = { NULL, }; +static int selected_digests[OSSL_NELEM(digest_data)]; +static struct driver_info_st digest_driver_info[OSSL_NELEM(digest_data)]; + +static int devcrypto_test_digest(size_t digest_data_index) +{ + return (digest_driver_info[digest_data_index].status == DEVCRYPTO_STATUS_USABLE + && selected_digests[digest_data_index] == 1 + && (digest_driver_info[digest_data_index].accelerated + == DEVCRYPTO_ACCELERATED + || use_softdrivers == DEVCRYPTO_USE_SOFTWARE + || (digest_driver_info[digest_data_index].accelerated + != DEVCRYPTO_NOT_ACCELERATED + && use_softdrivers == DEVCRYPTO_REJECT_SOFTWARE))); +} + +static void rebuild_known_digest_nids(ENGINE *e) +{ + size_t i; + + for (i = 0, known_digest_nids_amount = 0; i < OSSL_NELEM(digest_data); i++) { + if (devcrypto_test_digest(i)) + known_digest_nids[known_digest_nids_amount++] = digest_data[i].nid; + } + ENGINE_unregister_digests(e); + ENGINE_register_digests(e); +} static void prepare_digest_methods(void) { size_t i; + struct session_op sess1, sess2; +#ifdef CIOCGSESSINFO + struct session_info_op siop; +#endif + struct cphash_op cphash; + + memset(&digest_driver_info, 0, sizeof(digest_driver_info)); + + memset(&sess1, 0, sizeof(sess1)); + memset(&sess2, 0, sizeof(sess2)); for (i = 0, known_digest_nids_amount = 0; i < OSSL_NELEM(digest_data); i++) { + selected_digests[i] = 1; + /* - * Check that the algo is usable + * Check that the digest is usable */ - if (!devcrypto_test_digest(i)) - continue; + sess1.mac = digest_data[i].devcryptoid; + sess2.ses = 0; + if (ioctl(cfd, CIOCGSESSION, &sess1) < 0) { + digest_driver_info[i].status = DEVCRYPTO_STATUS_UNUSABLE; + goto finish; + } +#ifdef CIOCGSESSINFO + /* gather hardware acceleration info from the driver */ + siop.ses = sess1.ses; + if (ioctl(cfd, CIOCGSESSINFO, &siop) < 0) + digest_driver_info[i].accelerated = DEVCRYPTO_ACCELERATION_UNKNOWN; + else if (siop.flags & SIOP_FLAG_KERNEL_DRIVER_ONLY) + digest_driver_info[i].accelerated = DEVCRYPTO_ACCELERATED; + else + digest_driver_info[i].accelerated = DEVCRYPTO_NOT_ACCELERATED; +#endif + + /* digest must be capable of hash state copy */ + sess2.mac = sess1.mac; + if (ioctl(cfd, CIOCGSESSION, &sess2) < 0) { + digest_driver_info[i].status = DEVCRYPTO_STATUS_UNUSABLE; + goto finish; + } + cphash.src_ses = sess1.ses; + cphash.dst_ses = sess2.ses; + if (ioctl(cfd, CIOCCPHASH, &cphash) < 0) { + digest_driver_info[i].status = DEVCRYPTO_STATUS_UNUSABLE; + goto finish; + } if ((known_digest_methods[i] = EVP_MD_meth_new(digest_data[i].nid, NID_undef)) == NULL || !EVP_MD_meth_set_input_blocksize(known_digest_methods[i], @@ -689,11 +852,18 @@ static void prepare_digest_methods(void) || !EVP_MD_meth_set_cleanup(known_digest_methods[i], digest_cleanup) || !EVP_MD_meth_set_app_datasize(known_digest_methods[i], sizeof(struct digest_ctx))) { + digest_driver_info[i].status = DEVCRYPTO_STATUS_UNUSABLE; EVP_MD_meth_free(known_digest_methods[i]); known_digest_methods[i] = NULL; - } else { - known_digest_nids[known_digest_nids_amount++] = digest_data[i].nid; + goto finish; } + digest_driver_info[i].status = DEVCRYPTO_STATUS_USABLE; +finish: + ioctl(cfd, CIOCFSESSION, &sess1.ses); + if (sess2.ses != 0) + ioctl(cfd, CIOCFSESSION, &sess2.ses); + if (devcrypto_test_digest(i)) + known_digest_nids[known_digest_nids_amount++] = digest_data[i].nid; } } @@ -739,8 +909,154 @@ static int devcrypto_digests(ENGINE *e, const EVP_MD **digest, return *digest != NULL; } +static void devcrypto_select_all_digests(int *digest_list) +{ + size_t i; + + for (i = 0; i < OSSL_NELEM(digest_data); i++) + digest_list[i] = 1; +} + +static int cryptodev_select_digest_cb(const char *str, int len, void *usr) +{ + int *digest_list = (int *)usr; + char *name; + const EVP_MD *EVP; + size_t i; + + if (len == 0) + return 1; + if (usr == NULL || (name = OPENSSL_strndup(str, len)) == NULL) + return 0; + EVP = EVP_get_digestbyname(name); + if (EVP == NULL) + fprintf(stderr, "devcrypto: unknown digest %s\n", name); + else if ((i = find_digest_data_index(EVP_MD_type(EVP))) != (size_t)-1) + digest_list[i] = 1; + else + fprintf(stderr, "devcrypto: digest %s not available\n", name); + OPENSSL_free(name); + return 1; +} + +#endif + +/****************************************************************************** + * + * CONTROL COMMANDS + * + *****/ + +#define DEVCRYPTO_CMD_USE_SOFTDRIVERS ENGINE_CMD_BASE +#define DEVCRYPTO_CMD_CIPHERS (ENGINE_CMD_BASE + 1) +#define DEVCRYPTO_CMD_DIGESTS (ENGINE_CMD_BASE + 2) +#define DEVCRYPTO_CMD_DUMP_INFO (ENGINE_CMD_BASE + 3) + +/* Helper macros for CPP string composition */ +#ifndef OPENSSL_MSTR +# define OPENSSL_MSTR_HELPER(x) #x +# define OPENSSL_MSTR(x) OPENSSL_MSTR_HELPER(x) +#endif + +static const ENGINE_CMD_DEFN devcrypto_cmds[] = { +#ifdef CIOCGSESSINFO + {DEVCRYPTO_CMD_USE_SOFTDRIVERS, + "USE_SOFTDRIVERS", + "specifies whether to use software (not accelerated) drivers (" + OPENSSL_MSTR(DEVCRYPTO_REQUIRE_ACCELERATED) "=use only accelerated drivers, " + OPENSSL_MSTR(DEVCRYPTO_USE_SOFTWARE) "=allow all drivers, " + OPENSSL_MSTR(DEVCRYPTO_REJECT_SOFTWARE) + "=use if acceleration can't be determined) [default=" + OPENSSL_MSTR(DEVCRYPTO_DEFAULT_USE_SOFDTRIVERS) "]", + ENGINE_CMD_FLAG_NUMERIC}, +#endif + + {DEVCRYPTO_CMD_CIPHERS, + "CIPHERS", + "either ALL, NONE, or a comma-separated list of ciphers to enable [default=ALL]", + ENGINE_CMD_FLAG_STRING}, + +#ifdef IMPLEMENT_DIGEST + {DEVCRYPTO_CMD_DIGESTS, + "DIGESTS", + "either ALL, NONE, or a comma-separated list of digests to enable [default=ALL]", + ENGINE_CMD_FLAG_STRING}, #endif + {0, NULL, NULL, 0} +}; + +static int devcrypto_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f) (void)) +{ + int *new_list; + switch (cmd) { +#ifdef CIOCGSESSINFO + case DEVCRYPTO_CMD_USE_SOFTDRIVERS: + switch (i) { + case DEVCRYPTO_REQUIRE_ACCELERATED: + case DEVCRYPTO_USE_SOFTWARE: + case DEVCRYPTO_REJECT_SOFTWARE: + break; + default: + fprintf(stderr, "devcrypto: invalid value (%ld) for USE_SOFTDRIVERS\n", i); + return 0; + } + if (use_softdrivers == i) + return 1; + use_softdrivers = i; +#ifdef IMPLEMENT_DIGEST + rebuild_known_digest_nids(e); +#endif + rebuild_known_cipher_nids(e); + return 1; +#endif /* CIOCGSESSINFO */ + + case DEVCRYPTO_CMD_CIPHERS: + if (p == NULL) + return 1; + if (strcasecmp((const char *)p, "ALL") == 0) { + devcrypto_select_all_ciphers(selected_ciphers); + } else if (strcasecmp((const char*)p, "NONE") == 0) { + memset(selected_ciphers, 0, sizeof(selected_ciphers)); + } else { + new_list=OPENSSL_zalloc(sizeof(selected_ciphers)); + if (!CONF_parse_list(p, ',', 1, cryptodev_select_cipher_cb, new_list)) { + OPENSSL_free(new_list); + return 0; + } + memcpy(selected_ciphers, new_list, sizeof(selected_ciphers)); + OPENSSL_free(new_list); + } + rebuild_known_cipher_nids(e); + return 1; + +#ifdef IMPLEMENT_DIGEST + case DEVCRYPTO_CMD_DIGESTS: + if (p == NULL) + return 1; + if (strcasecmp((const char *)p, "ALL") == 0) { + devcrypto_select_all_digests(selected_digests); + } else if (strcasecmp((const char*)p, "NONE") == 0) { + memset(selected_digests, 0, sizeof(selected_digests)); + } else { + new_list=OPENSSL_zalloc(sizeof(selected_digests)); + if (!CONF_parse_list(p, ',', 1, cryptodev_select_digest_cb, new_list)) { + OPENSSL_free(new_list); + return 0; + } + memcpy(selected_digests, new_list, sizeof(selected_digests)); + OPENSSL_free(new_list); + } + rebuild_known_digest_nids(e); + return 1; +#endif /* IMPLEMENT_DIGEST */ + + default: + break; + } + return 0; +} + /****************************************************************************** * * LOAD / UNLOAD @@ -793,6 +1109,8 @@ void engine_load_devcrypto_int() if (!ENGINE_set_id(e, "devcrypto") || !ENGINE_set_name(e, "/dev/crypto engine") + || !ENGINE_set_cmd_defns(e, devcrypto_cmds) + || !ENGINE_set_ctrl_function(e, devcrypto_ctrl) /* * Asymmetric ciphers aren't well supported with /dev/crypto. Among the BSD