From a640100ba5f2831472440399bba103aab2a7e911 Mon Sep 17 00:00:00 2001 From: Dmitry Belyavskiy Date: Thu, 9 Jan 2025 13:54:38 +0100 Subject: [PATCH] Opaque keys support for the openssl command line Support EVP_SKEY object for the `enc` command. Support EVP_SKEYMGMT for the `list` command. --- apps/enc.c | 104 ++++++++++++++++++++++++++++++++--- apps/list.c | 64 +++++++++++++++++++++ doc/man1/openssl-enc.pod.in | 28 ++++++++++ doc/man1/openssl-list.pod.in | 9 +++ 4 files changed, 196 insertions(+), 9 deletions(-) diff --git a/apps/enc.c b/apps/enc.c index e7421ad896e3b9..d5cf76eeca8ba3 100644 --- a/apps/enc.c +++ b/apps/enc.c @@ -49,7 +49,8 @@ typedef enum OPTION_choice { OPT_NOPAD, OPT_SALT, OPT_NOSALT, OPT_DEBUG, OPT_UPPER_P, OPT_UPPER_A, OPT_A, OPT_Z, OPT_BUFSIZE, OPT_K, OPT_KFILE, OPT_UPPER_K, OPT_NONE, OPT_UPPER_S, OPT_IV, OPT_MD, OPT_ITER, OPT_PBKDF2, OPT_CIPHER, - OPT_SALTLEN, OPT_R_ENUM, OPT_PROV_ENUM + OPT_SALTLEN, OPT_R_ENUM, OPT_PROV_ENUM, + OPT_SKEYOPT, OPT_SKEYMGMT, OPT_GENKEY } OPTION_CHOICE; const OPTIONS enc_options[] = { @@ -105,6 +106,9 @@ const OPTIONS enc_options[] = { #ifndef OPENSSL_NO_ZLIB {"z", OPT_Z, '-', "Compress or decompress encrypted data using zlib"}, #endif + {"skeyopt", OPT_SKEYOPT, 's', "Key options as opt:value for opaque keys handling"}, + {"skeymgmt", OPT_SKEYMGMT, 's', "Symmetric key management name for opaque keys handling"}, + {"genkey", OPT_GENKEY, '-', "Generate an opaque symmetric key"}, {"", OPT_CIPHER, '-', "Any supported cipher"}, OPT_R_OPTIONS, @@ -130,10 +134,11 @@ int enc_main(int argc, char **argv) char mbuf[sizeof(magic) - 1]; OPTION_CHOICE o; int bsize = BSIZE, verbose = 0, debug = 0, olb64 = 0, nosalt = 0; - int enc = 1, printkey = 0, i, k; + int enc = 1, printkey = 0, genkey = 0, i, k; int base64 = 0, informat = FORMAT_BINARY, outformat = FORMAT_BINARY; int ret = 1, inl, nopad = 0; unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH]; + int rawkey_set = 0; unsigned char *buff = NULL, salt[EVP_MAX_IV_LENGTH]; int saltlen = 0; int pbkdf2 = 0; @@ -150,6 +155,9 @@ int enc_main(int argc, char **argv) BIO *bbrot = NULL; int do_zstd = 0; BIO *bzstd = NULL; + STACK_OF(OPENSSL_STRING) *skeyopts = NULL; + const char *skeymgmt = NULL; + EVP_SKEY *skey = NULL; /* first check the command name */ if (strcmp(argv[0], "base64") == 0) @@ -310,6 +318,19 @@ int enc_main(int argc, char **argv) case OPT_NONE: cipher = NULL; break; + case OPT_SKEYOPT: + if ((skeyopts == NULL && + (skeyopts = sk_OPENSSL_STRING_new_null()) == NULL) || + sk_OPENSSL_STRING_push(skeyopts, opt_arg()) == 0) { + BIO_puts(bio_err, "out of memory\n"); + goto end; + } + break; + case OPT_SKEYMGMT: + skeymgmt = opt_arg(); + break; + case OPT_GENKEY: + genkey = 1; case OPT_R_CASES: if (!opt_rand(o)) goto end; @@ -391,7 +412,7 @@ int enc_main(int argc, char **argv) str = pass; } - if ((str == NULL) && (cipher != NULL) && (hkey == NULL)) { + if ((str == NULL) && (cipher != NULL) && (hkey == NULL) && (skeyopts == NULL)) { if (1) { #ifndef OPENSSL_NO_UI_CONSOLE for (;;) { @@ -571,6 +592,7 @@ int enc_main(int argc, char **argv) /* split and move data back to global buffer */ memcpy(key, tmpkeyiv, iklen); memcpy(iv, tmpkeyiv+iklen, ivlen); + rawkey_set = 1; } else { BIO_printf(bio_err, "*** WARNING : " "deprecated key derivation used.\n" @@ -581,6 +603,7 @@ int enc_main(int argc, char **argv) BIO_printf(bio_err, "EVP_BytesToKey failed\n"); goto end; } + rawkey_set = 1; } /* * zero the complete buffer or the string passed from the command @@ -618,6 +641,18 @@ int enc_main(int argc, char **argv) } /* wiping secret data as we no longer need it */ cleanse(hkey); + rawkey_set = 1; + } + + /* + * At this moment we should know whether we try to use raw bytes key + * (probably password-derived) or an opaque key. + * We should not allow both set of parameters at the same time + */ + if (rawkey_set > 0 && skeyopts != NULL) { + BIO_printf(bio_err, "You should use either raw key (probably password-derived) " + "or 'skeyopt' args.\n"); + goto end; } if ((benc = BIO_new(BIO_f_cipher())) == NULL) @@ -643,12 +678,36 @@ int enc_main(int argc, char **argv) if (nopad) EVP_CIPHER_CTX_set_padding(ctx, 0); - if (!EVP_CipherInit_ex(ctx, NULL, NULL, key, - (hiv == NULL && wrap == 1 ? NULL : iv), enc)) { - BIO_printf(bio_err, "Error setting cipher %s\n", - EVP_CIPHER_get0_name(cipher)); - ERR_print_errors(bio_err); - goto end; + if (rawkey_set) { + if (!EVP_CipherInit_ex(ctx, NULL, NULL, key, + (hiv == NULL && wrap == 1 ? NULL : iv), enc)) { + BIO_printf(bio_err, "Error setting cipher %s\n", + EVP_CIPHER_get0_name(cipher)); + ERR_print_errors(bio_err); + goto end; + } + } else { + OSSL_PARAM *params = app_params_new_from_opts(skeyopts, + EVP_CIPHER_CTX_settable_params(ctx)); + + skey = EVP_SKEY_import(app_get0_libctx(), skeymgmt ? skeymgmt : EVP_CIPHER_name(cipher), + app_get0_propq(), OSSL_SKEYMGMT_SELECT_ALL, params); + OSSL_PARAM_free(params); + if (skey == NULL) { + BIO_printf(bio_err, "Error creating opaque key object for skeymgmt %s\n", + skeymgmt ? skeymgmt : EVP_CIPHER_name(cipher)); + ERR_print_errors(bio_err); + goto end; + } + + if (!EVP_CipherInit_skey(ctx, NULL, skey, + (hiv == NULL && wrap == 1 ? NULL : iv), + EVP_CIPHER_get_iv_length(cipher), enc, NULL)) { + BIO_printf(bio_err, "Error setting an opaque key for cipher %s\n", + EVP_CIPHER_get0_name(cipher)); + ERR_print_errors(bio_err); + goto end; + } } if (debug) { @@ -680,6 +739,31 @@ int enc_main(int argc, char **argv) goto end; } } + + if (genkey) { + OSSL_PARAM *params = app_params_new_from_opts(skeyopts, + EVP_CIPHER_CTX_settable_params(ctx)); + + skey = EVP_SKEY_generate(app_get0_libctx(), + skeymgmt ? skeymgmt : EVP_CIPHER_name(cipher), + app_get0_propq(), params); + OSSL_PARAM_free(params); + if (skey == NULL) { + BIO_printf(bio_err, "Error creating opaque key for skeymgmt %s\n", + skeymgmt ? skeymgmt : EVP_CIPHER_name(cipher)); + ERR_print_errors(bio_err); + } else { + char *key_name = EVP_SKEY_get1_key_id(skey); + + BIO_printf(bio_out, "An opaque key identified by %s is created\n", + key_name ? key_name : ""); + BIO_printf(bio_out, "Provider: %s\n", EVP_SKEY_get0_provider_name(skey)); + BIO_printf(bio_out, "Key management: %s\n", EVP_SKEY_get0_skeymgmt_name(skey)); + OPENSSL_free(skey); + } + + goto end; + } } /* Only encrypt/decrypt as we write the file */ @@ -716,6 +800,8 @@ int enc_main(int argc, char **argv) } end: ERR_print_errors(bio_err); + sk_OPENSSL_STRING_free(skeyopts); + EVP_SKEY_free(skey); OPENSSL_free(strbuf); OPENSSL_free(buff); BIO_free(in); diff --git a/apps/list.c b/apps/list.c index c0b0a5a4c43222..2d17dfb95158a5 100644 --- a/apps/list.c +++ b/apps/list.c @@ -56,6 +56,7 @@ IS_FETCHABLE(mac, EVP_MAC) IS_FETCHABLE(kdf, EVP_KDF) IS_FETCHABLE(rand, EVP_RAND) IS_FETCHABLE(keymgmt, EVP_KEYMGMT) +IS_FETCHABLE(skeymgmt, EVP_SKEYMGMT) IS_FETCHABLE(signature, EVP_SIGNATURE) IS_FETCHABLE(kem, EVP_KEM) IS_FETCHABLE(asym_cipher, EVP_ASYM_CIPHER) @@ -690,6 +691,61 @@ static void list_keymanagers(void) sk_EVP_KEYMGMT_pop_free(km_stack, EVP_KEYMGMT_free); } +DEFINE_STACK_OF(EVP_SKEYMGMT) +static int skeymanager_cmp(const EVP_SKEYMGMT * const *a, + const EVP_SKEYMGMT * const *b) +{ + return strcmp(OSSL_PROVIDER_get0_name(EVP_SKEYMGMT_get0_provider(*a)), + OSSL_PROVIDER_get0_name(EVP_SKEYMGMT_get0_provider(*b))); +} + +static void collect_skeymanagers(EVP_SKEYMGMT *km, void *stack) +{ + STACK_OF(EVP_SKEYMGMT) *km_stack = stack; + + if (is_skeymgmt_fetchable(km) + && sk_EVP_SKEYMGMT_push(km_stack, km) > 0) + EVP_SKEYMGMT_up_ref(km); +} + +static void list_skeymanagers(void) +{ + int i; + STACK_OF(EVP_SKEYMGMT) *km_stack = sk_EVP_SKEYMGMT_new(skeymanager_cmp); + + EVP_SKEYMGMT_do_all_provided(app_get0_libctx(), collect_skeymanagers, + km_stack); + sk_EVP_SKEYMGMT_sort(km_stack); + + for (i = 0; i < sk_EVP_SKEYMGMT_num(km_stack); i++) { + EVP_SKEYMGMT *k = sk_EVP_SKEYMGMT_value(km_stack, i); + STACK_OF(OPENSSL_CSTRING) *names = NULL; + + if (select_name != NULL && !EVP_SKEYMGMT_is_a(k, select_name)) + continue; + + names = sk_OPENSSL_CSTRING_new(name_cmp); + if (names != NULL && EVP_SKEYMGMT_names_do_all(k, collect_names, names)) { + const char *desc = EVP_SKEYMGMT_get0_description(k); + + BIO_printf(bio_out, " Name: "); + if (desc != NULL) + BIO_printf(bio_out, "%s", desc); + else + BIO_printf(bio_out, "%s", sk_OPENSSL_CSTRING_value(names, 0)); + BIO_printf(bio_out, "\n"); + BIO_printf(bio_out, " Type: Provider Algorithm\n"); + BIO_printf(bio_out, " IDs: "); + print_names(bio_out, names); + BIO_printf(bio_out, " @ %s\n", + OSSL_PROVIDER_get0_name(EVP_SKEYMGMT_get0_provider(k))); + + } + sk_OPENSSL_CSTRING_free(names); + } + sk_EVP_SKEYMGMT_pop_free(km_stack, EVP_SKEYMGMT_free); +} + DEFINE_STACK_OF(EVP_SIGNATURE) static int signature_cmp(const EVP_SIGNATURE * const *a, const EVP_SIGNATURE * const *b) @@ -1510,6 +1566,7 @@ typedef enum HELPLIST_CHOICE { OPT_PK_ALGORITHMS, OPT_PK_METHOD, OPT_DISABLED, OPT_KDF_ALGORITHMS, OPT_RANDOM_INSTANCES, OPT_RANDOM_GENERATORS, OPT_ENCODERS, OPT_DECODERS, OPT_KEYMANAGERS, OPT_KEYEXCHANGE_ALGORITHMS, + OPT_SKEYMANAGERS, OPT_KEM_ALGORITHMS, OPT_SIGNATURE_ALGORITHMS, OPT_TLS_SIGNATURE_ALGORITHMS, OPT_ASYM_CIPHER_ALGORITHMS, OPT_STORE_LOADERS, OPT_PROVIDER_INFO, OPT_OBJECTS, @@ -1555,6 +1612,7 @@ const OPTIONS list_options[] = { {"encoders", OPT_ENCODERS, '-', "List of encoding methods" }, {"decoders", OPT_DECODERS, '-', "List of decoding methods" }, {"key-managers", OPT_KEYMANAGERS, '-', "List of key managers" }, + {"skey-managers", OPT_SKEYMANAGERS, '-', "List of symmetric key managers" }, {"key-exchange-algorithms", OPT_KEYEXCHANGE_ALGORITHMS, '-', "List of key exchange algorithms" }, {"kem-algorithms", OPT_KEM_ALGORITHMS, '-', @@ -1607,6 +1665,7 @@ int list_main(int argc, char **argv) unsigned int encoder_algorithms:1; unsigned int decoder_algorithms:1; unsigned int keymanager_algorithms:1; + unsigned int skeymanager_algorithms:1; unsigned int signature_algorithms:1; unsigned int tls_signature_algorithms:1; unsigned int keyexchange_algorithms:1; @@ -1679,6 +1738,9 @@ int list_main(int argc, char **argv) case OPT_KEYMANAGERS: todo.keymanager_algorithms = 1; break; + case OPT_SKEYMANAGERS: + todo.skeymanager_algorithms = 1; + break; case OPT_SIGNATURE_ALGORITHMS: todo.signature_algorithms = 1; break; @@ -1800,6 +1862,8 @@ int list_main(int argc, char **argv) MAYBE_ADD_NL(list_decoders()); if (todo.keymanager_algorithms) MAYBE_ADD_NL(list_keymanagers()); + if (todo.skeymanager_algorithms) + MAYBE_ADD_NL(list_skeymanagers()); if (todo.signature_algorithms) MAYBE_ADD_NL(list_signatures()); if (todo.tls_signature_algorithms) diff --git a/doc/man1/openssl-enc.pod.in b/doc/man1/openssl-enc.pod.in index 33fe72d282a413..44fbf5f5bf1361 100644 --- a/doc/man1/openssl-enc.pod.in +++ b/doc/man1/openssl-enc.pod.in @@ -39,6 +39,9 @@ B B|I [B<-v>] [B<-debug>] [B<-none>] +[B<-skeymgmt> I] +[B<-skeyopt> I:I] +[B<-genkey>] {- $OpenSSL::safe::opt_engine_synopsis -}{- $OpenSSL::safe::opt_r_synopsis -} {- $OpenSSL::safe::opt_provider_synopsis -} @@ -217,6 +220,25 @@ or zlib-dynamic option. Use NULL cipher (no encryption or decryption of input). +=item B<-skeymgmt> I + +Some providers may support opaque symmetric keys objects. To use them, the +particular we need to know the I. If not specified, the name of the +cipher will be used. + +To find out the name of the suitable symmetric key management, +please refer C command output. + +=item B<-skeyopt> I:I + +To obtain the existing opaque symmetric key or generate the new one, key +options are specified as opt:value. These options can't be used together with +any options implying raw key directly or indirectly. + +=item B<-genkey> + +Generate a new opaque key object. + {- $OpenSSL::safe::opt_r_item -} {- $OpenSSL::safe::opt_provider_item -} @@ -477,6 +499,10 @@ The B command only supports a fixed number of algorithms with certain parameters. So if, for example, you want to use RC2 with a 76 bit key or RC4 with an 84 bit key you can't use this program. +=head1 SEE ALSO + +L, L + =head1 HISTORY The default digest was changed from MD5 to SHA256 in OpenSSL 1.1.0. @@ -487,6 +513,8 @@ The B<-ciphers> and B<-engine> options were deprecated in OpenSSL 3.0. The B<-saltlen> option was added in OpenSSL 3.2. +The B<-genkey>, B<-skeymgmt> and B<-skeyopt> options were added in OpenSSL 3.5. + =head1 COPYRIGHT Copyright 2000-2024 The OpenSSL Project Authors. All Rights Reserved. diff --git a/doc/man1/openssl-list.pod.in b/doc/man1/openssl-list.pod.in index a1d74f8e51930f..74ac67cda1f1ac 100644 --- a/doc/man1/openssl-list.pod.in +++ b/doc/man1/openssl-list.pod.in @@ -30,6 +30,7 @@ B -}[B<-encoders>] [B<-decoders>] [B<-key-managers>] +[B<-skey-managers>] [B<-key-exchange-algorithms>] [B<-kem-algorithms>] [B<-signature-algorithms>] @@ -96,6 +97,8 @@ Display lists of all algorithms. These include: =item Key managers +=item Symmetric key managers + =item Message authentication code algorithms (MAC) =item Random number generators (RNG, DRBG) @@ -183,6 +186,10 @@ Display a list of public key methods. Display a list of key managers. +=item B<-skey-managers> + +Display a list of symmetric key managers. + =item B<-key-exchange-algorithms> Display a list of key exchange algorithms. @@ -278,6 +285,8 @@ In both cases, C is the name of the provider. The B<-engines>, B<-digest-commands>, and B<-cipher-commands> options were deprecated in OpenSSL 3.0. +The B<-skey-managers> option was added in OpenSSL 3.5. + =head1 COPYRIGHT Copyright 2016-2024 The OpenSSL Project Authors. All Rights Reserved.