From 6be2f9b5849e64e6e75213c8193cf323122034dc Mon Sep 17 00:00:00 2001 From: Alon Bar-Lev Date: Sat, 20 May 2023 15:22:34 +0300 Subject: [PATCH] libmycms: use keyopt as list as order is important --- include/mycms/mycms.h | 6 ++-- src/libmycms/mycms-core-encrypt.c | 34 +++++++++++++----- src/libmycms/mycms-core-sign.c | 33 +++++++++++++---- src/mycms-tool/cmd-encrypt.c | 54 ++++++++-------------------- src/mycms-tool/cmd-sign.c | 29 +++++---------- test/mycms-tool/encrypt/test-file.sh | 4 +-- test/mycms-tool/sign/test-file.sh | 2 +- 7 files changed, 82 insertions(+), 80 deletions(-) diff --git a/include/mycms/mycms.h b/include/mycms/mycms.h index 0bd1515..132c0cf 100644 --- a/include/mycms/mycms.h +++ b/include/mycms/mycms.h @@ -55,7 +55,7 @@ mycms_sign( const mycms mycms __attribute__((unused)), const mycms_certificate certificate, const mycms_list_str digests, - const mycms_dict keyopt, + const mycms_list_str keyopts, const mycms_io cms_in, const mycms_io cms_out, const mycms_io data_in @@ -88,7 +88,7 @@ mycms_encrypt( const mycms mycms, const char * const cipher_name, const mycms_list_blob to, - const mycms_dict keyopt, + const mycms_list_str keyopts, const mycms_io cms_out, const mycms_io data_pt, const mycms_io data_ct @@ -99,7 +99,7 @@ mycms_encrypt_add( const mycms mycms, const mycms_certificate certificate, const mycms_list_blob to, - const mycms_dict keyopt, + const mycms_list_str keyopts, const mycms_io cms_in, const mycms_io cms_out ); diff --git a/src/libmycms/mycms-core-encrypt.c b/src/libmycms/mycms-core-encrypt.c index afaf8fc..69bc505 100644 --- a/src/libmycms/mycms-core-encrypt.c +++ b/src/libmycms/mycms-core-encrypt.c @@ -19,7 +19,7 @@ __add_recipients( const mycms mycms, CMS_ContentInfo *cms, const mycms_list_blob to, - const mycms_dict keyopt, + const mycms_list_str keyopts, int flags ) { STACK_OF(CMS_RecipientInfo) *ret = NULL; @@ -41,7 +41,7 @@ __add_recipients( for (t = to;t != NULL;t = t->next) { CMS_RecipientInfo *ri; EVP_PKEY_CTX *ctx; - mycms_list_dict_entry opt; + mycms_list_str keyopt; unsigned const char * p; p = t->blob.data; @@ -81,8 +81,26 @@ __add_recipients( goto cleanup; } - for (opt = mycms_dict_entries(keyopt); opt != NULL; opt = opt->next) { - if (!EVP_PKEY_CTX_ctrl_str(ctx, opt->entry.k, opt->entry.v)) { + for (keyopt = keyopts; keyopt != NULL; keyopt = keyopt->next) { + char opt[1024]; + char *p; + strncpy(opt, keyopt->str, sizeof(opt)); + opt[sizeof(opt) - 1] = '\0'; + if ((p = strchr(opt, ':')) == NULL) { + _mycms_error_entry_dispatch(_mycms_error_entry_base( + _mycms_error_capture(mycms_context_get_error(mycms_get_context(mycms))), + "core.sign.keyopt", + MYCMS_ERROR_CODE_ARGS, + true, + "Invalid keyopts '%s' no separator", + opt + )); + goto cleanup; + } + *p = '\0'; + p++; + + if (!EVP_PKEY_CTX_ctrl_str(ctx, opt, p)) { _mycms_error_entry_dispatch(_error_entry_openssl_status(_mycms_error_entry_base( _mycms_error_capture(mycms_context_get_error(mycms_get_context(mycms))), "core.encrypt.add.prms", @@ -115,7 +133,7 @@ mycms_encrypt( const mycms mycms, const char * const cipher_name, const mycms_list_blob to, - const mycms_dict keyopt, + const mycms_list_str keyopts, const mycms_io cms_out, const mycms_io data_pt, const mycms_io data_ct @@ -208,7 +226,7 @@ mycms_encrypt( goto cleanup; } - if ((added = __add_recipients(mycms, cms, to, keyopt, flags)) == NULL) { + if ((added = __add_recipients(mycms, cms, to, keyopts, flags)) == NULL) { goto cleanup; } @@ -252,7 +270,7 @@ mycms_encrypt_add( const mycms mycms, const mycms_certificate certificate, const mycms_list_blob to, - const mycms_dict keyopt, + const mycms_list_str keyopts, const mycms_io cms_in, const mycms_io cms_out ) { @@ -332,7 +350,7 @@ mycms_encrypt_add( goto cleanup; } - if ((added = __add_recipients(mycms, cms, to, keyopt, flags)) == NULL) { + if ((added = __add_recipients(mycms, cms, to, keyopts, flags)) == NULL) { goto cleanup; } diff --git a/src/libmycms/mycms-core-sign.c b/src/libmycms/mycms-core-sign.c index c5e8ae8..454af70 100644 --- a/src/libmycms/mycms-core-sign.c +++ b/src/libmycms/mycms-core-sign.c @@ -17,7 +17,7 @@ mycms_sign( const mycms mycms, const mycms_certificate certificate, const mycms_list_str digests, - const mycms_dict keyopt, + const mycms_list_str keyopts, const mycms_io cms_in, const mycms_io cms_out, const mycms_io data_in @@ -75,7 +75,6 @@ mycms_sign( for (digest = digests;digest != NULL; digest = digest->next) { const EVP_MD *md = NULL; CMS_SignerInfo *signer = NULL; - mycms_list_dict_entry opt; if ((md = EVP_get_digestbyname(digest->str)) == NULL) { _mycms_error_entry_dispatch(_mycms_error_entry_base( @@ -94,7 +93,7 @@ mycms_sign( _mycms_certificate_get_X509(certificate), _mycms_certificate_get_EVP_PKEY(certificate), md, - flags | (mycms_dict_entries(keyopt) == NULL ? 0 : CMS_KEY_PARAM) /* Does not work for 2nd sign, see https://github.com/openssl/openssl/issues/14257 */ + flags | (keyopts == NULL ? 0 : CMS_KEY_PARAM) /* Does not work for 2nd sign, see https://github.com/openssl/openssl/issues/14257 */ )) == NULL) { _mycms_error_entry_dispatch(_error_entry_openssl_status(_mycms_error_entry_base( _mycms_error_capture(mycms_context_get_error(mycms_get_context(mycms))), @@ -106,7 +105,9 @@ mycms_sign( goto cleanup; } - if (mycms_dict_entries(keyopt) != NULL) { /* TODO: remove when openssl bug fixed */ + if (keyopts != NULL) { /* TODO: remove when openssl bug fixed */ + mycms_list_str keyopt; + if ((ctx = CMS_SignerInfo_get0_pkey_ctx(signer)) == NULL) { _mycms_error_entry_dispatch(_error_entry_openssl_status(_mycms_error_entry_base( _mycms_error_capture(mycms_context_get_error(mycms_get_context(mycms))), @@ -118,11 +119,29 @@ mycms_sign( goto cleanup; } - for (opt = mycms_dict_entries(keyopt); opt != NULL; opt = opt->next) { - if (!EVP_PKEY_CTX_ctrl_str(ctx, opt->entry.k, opt->entry.v)) { + for (keyopt = keyopts;keyopt != NULL; keyopt = keyopt->next) { + char opt[1024]; + char *p; + strncpy(opt, keyopt->str, sizeof(opt)); + opt[sizeof(opt) - 1] = '\0'; + if ((p = strchr(opt, ':')) == NULL) { + _mycms_error_entry_dispatch(_mycms_error_entry_base( + _mycms_error_capture(mycms_context_get_error(mycms_get_context(mycms))), + "core.sign.keyopt", + MYCMS_ERROR_CODE_ARGS, + true, + "Invalid keyopts '%s' no separator", + opt + )); + goto cleanup; + } + *p = '\0'; + p++; + + if (!EVP_PKEY_CTX_ctrl_str(ctx, opt, p)) { _mycms_error_entry_dispatch(_error_entry_openssl_status(_mycms_error_entry_base( _mycms_error_capture(mycms_context_get_error(mycms_get_context(mycms))), - "core.sign.add.prm", + "core.sign.add.keyopt", MYCMS_ERROR_CODE_CRYPTO, true, "Failed to set signer key parameters" diff --git a/src/mycms-tool/cmd-encrypt.c b/src/mycms-tool/cmd-encrypt.c index e845e8a..bf088d5 100644 --- a/src/mycms-tool/cmd-encrypt.c +++ b/src/mycms-tool/cmd-encrypt.c @@ -37,7 +37,7 @@ _cmd_encrypt( {"data-pt\0FILE|input plain text data", required_argument, NULL, OPT_DATA_PT}, {"data-ct\0FILE|output plain text data", required_argument, NULL, OPT_DATA_CT}, {"to\0FILE|target DER encoded certificate, may be specified several times", required_argument, NULL, OPT_TO}, - {"keyopt\0KEYOPT_EXPRESSION|key options expression", required_argument, NULL, OPT_KEYOPT}, + {"keyopt\0KEYOPT|key options opt:value, may be specified multiple times", required_argument, NULL, OPT_KEYOPT}, {NULL, 0, NULL, 0} }; @@ -47,15 +47,13 @@ _cmd_encrypt( const char *cipher = "AES-256-CBC"; - const char * keyopt_exp = NULL; - mycms_system system = mycms_context_get_system(context); mycms mycms = NULL; mycms_io cms_out = NULL; mycms_io data_pt = NULL; mycms_io data_ct = NULL; mycms_list_blob to = NULL; - mycms_dict keyopt_dict = NULL; + mycms_list_str keyopts = NULL; if ((mycms = mycms_new(context)) == NULL) { goto cleanup; @@ -132,7 +130,9 @@ _cmd_encrypt( } break; case OPT_KEYOPT: - keyopt_exp = optarg; + if (!mycms_list_str_add(system, &keyopts, optarg)) { + goto cleanup; + } break; default: fprintf(stderr, "Invalid option\n"); @@ -157,19 +157,7 @@ _cmd_encrypt( goto cleanup; } - if ((keyopt_dict = mycms_dict_new(context)) == NULL) { - goto cleanup; - } - - if (!mycms_dict_construct(keyopt_dict)) { - goto cleanup; - } - - if (!util_split_string(keyopt_dict, keyopt_exp)) { - goto cleanup; - } - - if (!mycms_encrypt(mycms, cipher, to, keyopt_dict, cms_out, data_pt, data_ct)) { + if (!mycms_encrypt(mycms, cipher, to, keyopts, cms_out, data_pt, data_ct)) { goto cleanup; } @@ -186,8 +174,8 @@ _cmd_encrypt( mycms_io_destruct(data_ct); data_ct = NULL; - mycms_dict_destruct(keyopt_dict); - keyopt_dict = NULL; + mycms_list_str_free(system, keyopts); + keyopts = NULL; while(to != NULL) { mycms_list_blob t = to; @@ -228,7 +216,7 @@ _cmd_encrypt_add( {"recip-cert\0CERTIFICATE_EXPRESSION|recipient certificate to use", required_argument, NULL, OPT_RECIP_CERT}, {"recip-cert-pass\0PASSPHRASE_EXPRESSION|recipient certificate passphrase to use", required_argument, NULL, OPT_RECIP_CERT_PASS}, {"to\0FILE|target DER encoded certificate, may be specified several times", required_argument, NULL, OPT_TO}, - {"keyopt\0KEYOPT_EXPRESSION|key options expression", required_argument, NULL, OPT_KEYOPT}, + {"keyopt\0KEYOPT|key options opt:value, may be specified multiple times", required_argument, NULL, OPT_KEYOPT}, {NULL, 0, NULL, 0} }; @@ -238,14 +226,13 @@ _cmd_encrypt_add( const char * certificate_exp = NULL; const char * pass_exp = NULL; - const char * keyopt_exp = NULL; mycms_system system = mycms_context_get_system(context); mycms mycms = NULL; mycms_io cms_in = NULL; mycms_io cms_out = NULL; mycms_list_blob to = NULL; - mycms_dict keyopt_dict = NULL; + mycms_list_str keyopts = NULL; mycms_dict certificate_dict = NULL; mycms_dict pass_dict = NULL; mycms_certificate certificate = NULL; @@ -317,7 +304,9 @@ _cmd_encrypt_add( } break; case OPT_KEYOPT: - keyopt_exp = optarg; + if (!mycms_list_str_add(system, &keyopts, optarg)) { + goto cleanup; + } break; default: fprintf(stderr, "Invalid option\n"); @@ -382,18 +371,6 @@ _cmd_encrypt_add( goto cleanup; } - if ((keyopt_dict = mycms_dict_new(context)) == NULL) { - goto cleanup; - } - - if (!mycms_dict_construct(keyopt_dict)) { - goto cleanup; - } - - if (!util_split_string(keyopt_dict, keyopt_exp)) { - goto cleanup; - } - if (!mycms_certificate_set_passphrase_callback(certificate, _cmd_common_passphrase_callback)) { goto cleanup; } @@ -414,7 +391,7 @@ _cmd_encrypt_add( goto cleanup; } - if (!mycms_encrypt_add(mycms, certificate, to, keyopt_dict, cms_in, cms_out)) { + if (!mycms_encrypt_add(mycms, certificate, to, keyopts, cms_in, cms_out)) { goto cleanup; } @@ -437,8 +414,7 @@ _cmd_encrypt_add( mycms_dict_destruct(pass_dict); pass_dict = NULL; - mycms_dict_destruct(keyopt_dict); - keyopt_dict = NULL; + mycms_list_str_free(system, keyopts); while(to != NULL) { mycms_list_blob t = to; diff --git a/src/mycms-tool/cmd-sign.c b/src/mycms-tool/cmd-sign.c index 4a977b1..2304a8e 100644 --- a/src/mycms-tool/cmd-sign.c +++ b/src/mycms-tool/cmd-sign.c @@ -36,7 +36,7 @@ _cmd_sign( {"digest\0DIGEST|digest to use, default is SHA3-256", required_argument, NULL, OPT_DIGEST}, {"signer-cert\0CERTIFICATE_EXPRESSION|signer certificate to use", required_argument, NULL, OPT_SIGNER_CERT}, {"signer-cert-pass\0PASSPHRASE_EXPRESSION|signer certificate passphrase to use", required_argument, NULL, OPT_SIGNER_CERT_PASS}, - {"keyopt\0KEYOPT_EXPRESSION|key options expression", required_argument, NULL, OPT_KEYOPT}, + {"keyopt\0KEYOPT|key options opt:value, may be specified multiple times", required_argument, NULL, OPT_KEYOPT}, {"cms-in\0FILE|input cms for resign", required_argument, NULL, OPT_CMS_IN}, {"cms-out\0FILE|output cms", required_argument, NULL, OPT_CMS_OUT}, {"data-in\0FILE|input text data", required_argument, NULL, OPT_DATA_IN}, @@ -49,7 +49,6 @@ _cmd_sign( const char * certificate_exp = NULL; const char * pass_exp = NULL; - const char * keyopt_exp = NULL; mycms_system system = mycms_context_get_system(context); mycms mycms = NULL; @@ -58,7 +57,7 @@ _cmd_sign( mycms_io data_in = NULL; mycms_dict certificate_dict = NULL; mycms_dict pass_dict = NULL; - mycms_dict keyopt_dict = NULL; + mycms_list_str keyopts = NULL; mycms_certificate certificate = NULL; mycms_list_str digests = NULL; @@ -127,7 +126,9 @@ _cmd_sign( pass_exp = optarg; break; case OPT_KEYOPT: - keyopt_exp = optarg; + if (!mycms_list_str_add(system, &keyopts, optarg)) { + goto cleanup; + } break; default: fprintf(stderr, "Invalid option\n"); @@ -176,18 +177,6 @@ _cmd_sign( goto cleanup; } - if ((keyopt_dict = mycms_dict_new(context)) == NULL) { - goto cleanup; - } - - if (!mycms_dict_construct(keyopt_dict)) { - goto cleanup; - } - - if (!util_split_string(keyopt_dict, keyopt_exp)) { - goto cleanup; - } - if ((certificate = mycms_certificate_new(context)) == NULL) { goto cleanup; } @@ -220,7 +209,7 @@ _cmd_sign( goto cleanup; } - if (!mycms_sign(mycms, certificate, digests, keyopt_dict, cms_in, cms_out, data_in)) { + if (!mycms_sign(mycms, certificate, digests, keyopts, cms_in, cms_out, data_in)) { goto cleanup; } @@ -231,6 +220,9 @@ _cmd_sign( mycms_list_str_free(system, digests); digests = NULL; + mycms_list_str_free(system, keyopts); + keyopts = NULL; + mycms_io_destruct(cms_in); cms_in = NULL; @@ -249,9 +241,6 @@ _cmd_sign( mycms_dict_destruct(pass_dict); pass_dict = NULL; - mycms_dict_destruct(keyopt_dict); - keyopt_dict = NULL; - mycms_destruct(mycms); mycms = NULL; diff --git a/test/mycms-tool/encrypt/test-file.sh b/test/mycms-tool/encrypt/test-file.sh index 0c8fce1..ffbbadc 100755 --- a/test/mycms-tool/encrypt/test-file.sh +++ b/test/mycms-tool/encrypt/test-file.sh @@ -221,7 +221,7 @@ test_keyopt() { --data-ct="${CT}" \ --to="${builddir}/gen/test1.crt" \ --to="${builddir}/gen/test2.crt" \ - --keyopt="rsa_padding_mode=${padding}" \ + --keyopt="rsa_padding_mode:${padding}" \ || die "add-recip.encrypt" echo "Adding to test3 and test4 using test1" @@ -231,7 +231,7 @@ test_keyopt() { --recip-cert="file:cert=${builddir}/gen/test1.crt:key=${builddir}/gen/test1.key" \ --to="${builddir}/gen/test3.crt" \ --to="${builddir}/gen/test4.crt" \ - --keyopt="rsa_padding_mode=${padding}" \ + --keyopt="rsa_padding_mode:${padding}" \ || die "add-recip.encrypt" [ 4 -eq $("${OPENSSL}" asn1parse -in "${CMS2}" -inform DER | grep "${padding_str}" | wc -l) ] || die "Expected '${padding_str}' for '${padding}'" diff --git a/test/mycms-tool/sign/test-file.sh b/test/mycms-tool/sign/test-file.sh index 8915f2d..f8b9b5b 100755 --- a/test/mycms-tool/sign/test-file.sh +++ b/test/mycms-tool/sign/test-file.sh @@ -279,7 +279,7 @@ test_keyopt() { --cms-out="${CMS}" \ --data-in="${DATA}" \ --signer-cert="file:cert=${builddir}/gen/test1.crt:key=${builddir}/gen/test1.key" \ - --keyopt="rsa_padding_mode=${padding}" \ + --keyopt="rsa_padding_mode:${padding}" \ || die "keyopt.sign.test1" [ 1 -eq $("${OPENSSL}" asn1parse -in "${CMS}" -inform DER | grep "${padding_str}" | wc -l) ] || die "Expected '${padding_str}' for '${padding}'"