From 2292c8e17f0b870b48bb7a5f8ed8c37dfb36580f Mon Sep 17 00:00:00 2001 From: Richard Levitte Date: Tue, 10 Mar 2020 23:08:59 +0100 Subject: [PATCH] APPS: Remove all traces of special SM2 treatment. SM2 IDs are now passed entirely as '-pkeyopt', '-sigopt' or '-vfyopt' values, just like any other valid option. Fixes #11293 Reviewed-by: Paul Yang (Merged from https://github.com/openssl/openssl/pull/11302) --- apps/ca.c | 110 ++++++------------- apps/include/apps.h | 1 + apps/pkeyutl.c | 16 --- apps/req.c | 183 ++++++++++++-------------------- apps/verify.c | 83 +++++---------- apps/x509.c | 29 +++-- doc/man1/openssl-ca.pod.in | 30 +++--- doc/man1/openssl-pkeyutl.pod.in | 12 +-- doc/man1/openssl-req.pod.in | 34 +++--- doc/man1/openssl-verify.pod.in | 22 ++-- doc/man1/openssl-x509.pod.in | 8 +- 11 files changed, 200 insertions(+), 328 deletions(-) diff --git a/apps/ca.c b/apps/ca.c index e3e2fd2e7e..192e602028 100644 --- a/apps/ca.c +++ b/apps/ca.c @@ -89,17 +89,20 @@ typedef enum { static char *lookup_conf(const CONF *conf, const char *group, const char *tag); static int certify(X509 **xret, const char *infile, EVP_PKEY *pkey, X509 *x509, - const EVP_MD *dgst, STACK_OF(OPENSSL_STRING) *sigopts, + const EVP_MD *dgst, + STACK_OF(OPENSSL_STRING) *sigopts, + STACK_OF(OPENSSL_STRING) *vfyopts, STACK_OF(CONF_VALUE) *policy, CA_DB *db, BIGNUM *serial, const char *subj, unsigned long chtype, int multirdn, int email_dn, const char *startdate, const char *enddate, long days, int batch, const char *ext_sect, CONF *conf, int verbose, unsigned long certopt, unsigned long nameopt, - int default_op, int ext_copy, int selfsign, - unsigned char *sm2_id, size_t sm2idlen); + int default_op, int ext_copy, int selfsign); static int certify_cert(X509 **xret, const char *infile, EVP_PKEY *pkey, X509 *x509, - const EVP_MD *dgst, STACK_OF(OPENSSL_STRING) *sigopts, + const EVP_MD *dgst, + STACK_OF(OPENSSL_STRING) *sigopts, + STACK_OF(OPENSSL_STRING) *vfyopts, STACK_OF(CONF_VALUE) *policy, CA_DB *db, BIGNUM *serial, const char *subj, unsigned long chtype, int multirdn, int email_dn, const char *startdate, @@ -142,13 +145,13 @@ typedef enum OPTION_choice { OPT_ENGINE, OPT_VERBOSE, OPT_CONFIG, OPT_NAME, OPT_SUBJ, OPT_UTF8, OPT_CREATE_SERIAL, OPT_MULTIVALUE_RDN, OPT_STARTDATE, OPT_ENDDATE, OPT_DAYS, OPT_MD, OPT_POLICY, OPT_KEYFILE, OPT_KEYFORM, OPT_PASSIN, - OPT_KEY, OPT_CERT, OPT_SELFSIGN, OPT_IN, OPT_OUT, OPT_OUTDIR, + OPT_KEY, OPT_CERT, OPT_SELFSIGN, OPT_IN, OPT_OUT, OPT_OUTDIR, OPT_VFYOPT, OPT_SIGOPT, OPT_NOTEXT, OPT_BATCH, OPT_PRESERVEDN, OPT_NOEMAILDN, OPT_GENCRL, OPT_MSIE_HACK, OPT_CRLDAYS, OPT_CRLHOURS, OPT_CRLSEC, OPT_INFILES, OPT_SS_CERT, OPT_SPKAC, OPT_REVOKE, OPT_VALID, OPT_EXTENSIONS, OPT_EXTFILE, OPT_STATUS, OPT_UPDATEDB, OPT_CRLEXTS, OPT_RAND_SERIAL, - OPT_R_ENUM, OPT_SM2ID, OPT_SM2HEXID, OPT_PROV_ENUM, + OPT_R_ENUM, OPT_PROV_ENUM, /* Do not change the order here; see related case statements below */ OPT_CRL_REASON, OPT_CRL_HOLD, OPT_CRL_COMPROMISE, OPT_CRL_CA_COMPROMISE } OPTION_CHOICE; @@ -197,12 +200,6 @@ const OPTIONS ca_options[] = { "Extension section (override value in config file)"}, {"extfile", OPT_EXTFILE, '<', "Configuration file with X509v3 extensions to add"}, -#ifndef OPENSSL_NO_SM2 - {"sm2-id", OPT_SM2ID, 's', - "Specify an ID string to verify an SM2 certificate request"}, - {"sm2-hex-id", OPT_SM2HEXID, 's', - "Specify a hex ID string to verify an SM2 certificate request"}, -#endif {"preserveDN", OPT_PRESERVEDN, '-', "Don't re-order the DN"}, {"noemailDN", OPT_NOEMAILDN, '-', "Don't add the EMAIL field to the DN"}, @@ -216,6 +213,7 @@ const OPTIONS ca_options[] = { {"selfsign", OPT_SELFSIGN, '-', "Sign a cert with the key associated with it"}, {"sigopt", OPT_SIGOPT, 's', "Signature parameter in n:v form"}, + {"vfyopt", OPT_SIGOPT, 's', "Verification parameter in n:v form"}, OPT_SECTION("Revocation"), {"gencrl", OPT_GENCRL, '-', "Generate a new CRL"}, @@ -257,7 +255,7 @@ int ca_main(int argc, char **argv) CA_DB *db = NULL; DB_ATTR db_attr; STACK_OF(CONF_VALUE) *attribs = NULL; - STACK_OF(OPENSSL_STRING) *sigopts = NULL; + STACK_OF(OPENSSL_STRING) *sigopts = NULL, *vfyopts = NULL; STACK_OF(X509) *cert_sk = NULL; X509_CRL *crl = NULL; const EVP_MD *dgst = NULL; @@ -286,9 +284,6 @@ int ca_main(int argc, char **argv) REVINFO_TYPE rev_type = REV_NONE; X509_REVOKED *r = NULL; OPTION_CHOICE o; - unsigned char *sm2_id = NULL; - size_t sm2_idlen = 0; - int sm2_free = 0; prog = opt_init(argc, argv, ca_options); while ((o = opt_next()) != OPT_EOF) { @@ -385,6 +380,12 @@ opthelp: if (sigopts == NULL || !sk_OPENSSL_STRING_push(sigopts, opt_arg())) goto end; break; + case OPT_VFYOPT: + if (vfyopts == NULL) + vfyopts = sk_OPENSSL_STRING_new_null(); + if (vfyopts == NULL || !sk_OPENSSL_STRING_push(vfyopts, opt_arg())) + goto end; + break; case OPT_NOTEXT: notext = 1; break; @@ -456,30 +457,6 @@ opthelp: case OPT_ENGINE: e = setup_engine(opt_arg(), 0); break; - case OPT_SM2ID: - /* we assume the input is not a hex string */ - if (sm2_id != NULL) { - BIO_printf(bio_err, - "Use one of the options 'sm2-hex-id' or 'sm2-id'\n"); - goto end; - } - sm2_id = (unsigned char *)opt_arg(); - sm2_idlen = strlen((const char *)sm2_id); - break; - case OPT_SM2HEXID: - /* try to parse the input as hex string first */ - if (sm2_id != NULL) { - BIO_printf(bio_err, - "Use one of the options 'sm2-hex-id' or 'sm2-id'\n"); - goto end; - } - sm2_free = 1; - sm2_id = OPENSSL_hexstr2buf(opt_arg(), (long *)&sm2_idlen); - if (sm2_id == NULL) { - BIO_printf(bio_err, "Invalid hex string input\n"); - goto end; - } - break; } } end_of_options: @@ -944,8 +921,8 @@ end_of_options: } if (ss_cert_file != NULL) { total++; - j = certify_cert(&x, ss_cert_file, pkey, x509, dgst, sigopts, - attribs, + j = certify_cert(&x, ss_cert_file, pkey, x509, dgst, + sigopts, vfyopts, attribs, db, serial, subj, chtype, multirdn, email_dn, startdate, enddate, days, batch, extensions, conf, verbose, certopt, get_nameopt(), default_op, @@ -965,11 +942,11 @@ end_of_options: } if (infile != NULL) { total++; - j = certify(&x, infile, pkey, x509p, dgst, sigopts, attribs, db, + j = certify(&x, infile, pkey, x509p, dgst, sigopts, vfyopts, + attribs, db, serial, subj, chtype, multirdn, email_dn, startdate, enddate, days, batch, extensions, conf, verbose, - certopt, get_nameopt(), default_op, ext_copy, selfsign, - sm2_id, sm2_idlen); + certopt, get_nameopt(), default_op, ext_copy, selfsign); if (j < 0) goto end; if (j > 0) { @@ -985,11 +962,11 @@ end_of_options: } for (i = 0; i < argc; i++) { total++; - j = certify(&x, argv[i], pkey, x509p, dgst, sigopts, attribs, db, + j = certify(&x, argv[i], pkey, x509p, dgst, sigopts, vfyopts, + attribs, db, serial, subj, chtype, multirdn, email_dn, startdate, enddate, days, batch, extensions, conf, verbose, - certopt, get_nameopt(), default_op, ext_copy, selfsign, - sm2_id, sm2_idlen); + certopt, get_nameopt(), default_op, ext_copy, selfsign); if (j < 0) goto end; if (j > 0) { @@ -1287,8 +1264,6 @@ end_of_options: ret = 0; end: - if (sm2_free) - OPENSSL_free(sm2_id); if (ret) ERR_print_errors(bio_err); BIO_free_all(Sout); @@ -1302,6 +1277,7 @@ end_of_options: BN_free(crlnumber); free_index(db); sk_OPENSSL_STRING_free(sigopts); + sk_OPENSSL_STRING_free(vfyopts); EVP_PKEY_free(pkey); X509_free(x509); X509_CRL_free(crl); @@ -1320,15 +1296,16 @@ static char *lookup_conf(const CONF *conf, const char *section, const char *tag) } static int certify(X509 **xret, const char *infile, EVP_PKEY *pkey, X509 *x509, - const EVP_MD *dgst, STACK_OF(OPENSSL_STRING) *sigopts, + const EVP_MD *dgst, + STACK_OF(OPENSSL_STRING) *sigopts, + STACK_OF(OPENSSL_STRING) *vfyopts, STACK_OF(CONF_VALUE) *policy, CA_DB *db, BIGNUM *serial, const char *subj, unsigned long chtype, int multirdn, int email_dn, const char *startdate, const char *enddate, long days, int batch, const char *ext_sect, CONF *lconf, int verbose, unsigned long certopt, unsigned long nameopt, - int default_op, int ext_copy, int selfsign, - unsigned char *sm2id, size_t sm2idlen) + int default_op, int ext_copy, int selfsign) { X509_REQ *req = NULL; BIO *in = NULL; @@ -1360,26 +1337,7 @@ static int certify(X509 **xret, const char *infile, EVP_PKEY *pkey, X509 *x509, BIO_printf(bio_err, "error unpacking public key\n"); goto end; } - if (sm2id != NULL) { -#ifndef OPENSSL_NO_SM2 - ASN1_OCTET_STRING *v; - - v = ASN1_OCTET_STRING_new(); - if (v == NULL) { - BIO_printf(bio_err, "error: SM2 ID allocation failed\n"); - goto end; - } - - if (!ASN1_OCTET_STRING_set(v, sm2id, sm2idlen)) { - BIO_printf(bio_err, "error: setting SM2 ID failed\n"); - ASN1_OCTET_STRING_free(v); - goto end; - } - - X509_REQ_set0_sm2_id(req, v); -#endif - } - i = X509_REQ_verify(req, pktmp); + i = do_X509_REQ_verify(req, pktmp, vfyopts); pktmp = NULL; if (i < 0) { ok = 0; @@ -1409,7 +1367,9 @@ static int certify(X509 **xret, const char *infile, EVP_PKEY *pkey, X509 *x509, } static int certify_cert(X509 **xret, const char *infile, EVP_PKEY *pkey, X509 *x509, - const EVP_MD *dgst, STACK_OF(OPENSSL_STRING) *sigopts, + const EVP_MD *dgst, + STACK_OF(OPENSSL_STRING) *sigopts, + STACK_OF(OPENSSL_STRING) *vfyopts, STACK_OF(CONF_VALUE) *policy, CA_DB *db, BIGNUM *serial, const char *subj, unsigned long chtype, int multirdn, int email_dn, const char *startdate, @@ -1433,7 +1393,7 @@ static int certify_cert(X509 **xret, const char *infile, EVP_PKEY *pkey, X509 *x BIO_printf(bio_err, "error unpacking public key\n"); goto end; } - i = X509_verify(req, pktmp); + i = do_X509_verify(req, pktmp, vfyopts); if (i < 0) { ok = 0; BIO_printf(bio_err, "Signature verification problems....\n"); diff --git a/apps/include/apps.h b/apps/include/apps.h index ccafaef5f1..de068d9670 100644 --- a/apps/include/apps.h +++ b/apps/include/apps.h @@ -203,6 +203,7 @@ int init_gen_str(EVP_PKEY_CTX **pctx, const char *algname, ENGINE *e, int do_param); int do_X509_sign(X509 *x, EVP_PKEY *pkey, const EVP_MD *md, STACK_OF(OPENSSL_STRING) *sigopts); +int do_X509_verify(X509 *x, EVP_PKEY *pkey, STACK_OF(OPENSSL_STRING) *vfyopts); int do_X509_REQ_sign(X509_REQ *x, EVP_PKEY *pkey, const EVP_MD *md, STACK_OF(OPENSSL_STRING) *sigopts); int do_X509_REQ_verify(X509_REQ *x, EVP_PKEY *pkey, diff --git a/apps/pkeyutl.c b/apps/pkeyutl.c index 7f11b168f5..7dc558b13a 100644 --- a/apps/pkeyutl.c +++ b/apps/pkeyutl.c @@ -550,22 +550,6 @@ static EVP_PKEY_CTX *init_ctx(const char *kdfalg, int *pkeysize, if (pkey == NULL) goto end; -#ifndef OPENSSL_NO_EC - /* SM2 needs a special treatment */ - if (EVP_PKEY_id(pkey) == EVP_PKEY_EC) { - EC_KEY *eckey = NULL; - const EC_GROUP *group = NULL; - int nid; - - if ((eckey = EVP_PKEY_get0_EC_KEY(pkey)) == NULL - || (group = EC_KEY_get0_group(eckey)) == NULL - || (nid = EC_GROUP_get_curve_name(group)) == 0) - goto end; - if (nid == NID_sm2 - && !EVP_PKEY_set_alias_type(pkey, EVP_PKEY_SM2)) - goto end; - } -#endif *pkeysize = EVP_PKEY_size(pkey); ctx = EVP_PKEY_CTX_new(pkey, impl); if (ppkey != NULL) diff --git a/apps/req.c b/apps/req.c index d1c93a68f7..a8db866523 100644 --- a/apps/req.c +++ b/apps/req.c @@ -87,11 +87,11 @@ typedef enum OPTION_choice { OPT_INFORM, OPT_OUTFORM, OPT_ENGINE, OPT_KEYGEN_ENGINE, OPT_KEY, OPT_PUBKEY, OPT_NEW, OPT_CONFIG, OPT_KEYFORM, OPT_IN, OPT_OUT, OPT_KEYOUT, OPT_PASSIN, OPT_PASSOUT, OPT_NEWKEY, - OPT_PKEYOPT, OPT_SIGOPT, OPT_BATCH, OPT_NEWHDR, OPT_MODULUS, + OPT_PKEYOPT, OPT_SIGOPT, OPT_VFYOPT, OPT_BATCH, OPT_NEWHDR, OPT_MODULUS, OPT_VERIFY, OPT_NODES, OPT_NOOUT, OPT_VERBOSE, OPT_UTF8, OPT_NAMEOPT, OPT_REQOPT, OPT_SUBJ, OPT_SUBJECT, OPT_TEXT, OPT_X509, OPT_MULTIVALUE_RDN, OPT_DAYS, OPT_SET_SERIAL, OPT_ADDEXT, OPT_EXTENSIONS, - OPT_REQEXTS, OPT_PRECERT, OPT_MD, OPT_SM2ID, OPT_SM2HEXID, + OPT_REQEXTS, OPT_PRECERT, OPT_MD, OPT_SECTION, OPT_R_ENUM, OPT_PROV_ENUM } OPTION_CHOICE; @@ -143,13 +143,8 @@ const OPTIONS req_options[] = { {"newkey", OPT_NEWKEY, 's', "Specify as type:bits"}, {"pkeyopt", OPT_PKEYOPT, 's', "Public key options as opt:value"}, {"sigopt", OPT_SIGOPT, 's', "Signature parameter in n:v form"}, + {"vfyopt", OPT_VFYOPT, 's', "Verification parameter in n:v form"}, {"", OPT_MD, '-', "Any supported digest"}, -#ifndef OPENSSL_NO_SM2 - {"sm2-id", OPT_SM2ID, 's', - "Specify an ID string to verify an SM2 certificate request"}, - {"sm2-hex-id", OPT_SM2HEXID, 's', - "Specify a hex ID string to verify an SM2 certificate request"}, -#endif OPT_SECTION("Output"), {"out", OPT_OUT, '>', "Output file"}, @@ -237,7 +232,7 @@ int req_main(int argc, char **argv) ENGINE *e = NULL, *gen_eng = NULL; EVP_PKEY *pkey = NULL; EVP_PKEY_CTX *genctx = NULL; - STACK_OF(OPENSSL_STRING) *pkeyopts = NULL, *sigopts = NULL; + STACK_OF(OPENSSL_STRING) *pkeyopts = NULL, *sigopts = NULL, *vfyopts = NULL; LHASH_OF(OPENSSL_STRING) *addexts = NULL; X509 *x509ss = NULL; X509_REQ *req = NULL; @@ -260,9 +255,6 @@ int req_main(int argc, char **argv) int nodes = 0, newhdr = 0, subject = 0, pubkey = 0, precert = 0; long newkey = -1; unsigned long chtype = MBSTRING_ASC, reqflag = 0; - unsigned char *sm2_id = NULL; - size_t sm2_idlen = 0; - int sm2_free = 0; #ifndef OPENSSL_NO_DES cipher = EVP_des_ede3_cbc(); @@ -359,6 +351,12 @@ int req_main(int argc, char **argv) if (!sigopts || !sk_OPENSSL_STRING_push(sigopts, opt_arg())) goto opthelp; break; + case OPT_VFYOPT: + if (!vfyopts) + vfyopts = sk_OPENSSL_STRING_new_null(); + if (!vfyopts || !sk_OPENSSL_STRING_push(vfyopts, opt_arg())) + goto opthelp; + break; case OPT_BATCH: batch = 1; break; @@ -446,29 +444,6 @@ int req_main(int argc, char **argv) goto opthelp; digest = md_alg; break; - case OPT_SM2ID: - if (sm2_id != NULL) { - BIO_printf(bio_err, - "Use one of the options 'sm2-hex-id' or 'sm2-id'\n"); - goto end; - } - sm2_id = (unsigned char *)opt_arg(); - sm2_idlen = strlen((const char *)sm2_id); - break; - case OPT_SM2HEXID: - if (sm2_id != NULL) { - BIO_printf(bio_err, - "Use one of the options 'sm2-hex-id' or 'sm2-id'\n"); - goto end; - } - /* try to parse the input as hex string first */ - sm2_free = 1; - sm2_id = OPENSSL_hexstr2buf(opt_arg(), (long *)&sm2_idlen); - if (sm2_id == NULL) { - BIO_printf(bio_err, "Invalid hex string input\n"); - goto end; - } - break; } } argc = opt_num_rest(); @@ -901,27 +876,7 @@ int req_main(int argc, char **argv) goto end; } - if (sm2_id != NULL) { -#ifndef OPENSSL_NO_SM2 - ASN1_OCTET_STRING *v; - - v = ASN1_OCTET_STRING_new(); - if (v == NULL) { - BIO_printf(bio_err, "error: SM2 ID allocation failed\n"); - goto end; - } - - if (!ASN1_OCTET_STRING_set(v, sm2_id, sm2_idlen)) { - BIO_printf(bio_err, "error: setting SM2 ID failed\n"); - ASN1_OCTET_STRING_free(v); - goto end; - } - - X509_REQ_set0_sm2_id(req, v); -#endif - } - - i = X509_REQ_verify(req, tpubkey); + i = do_X509_REQ_verify(req, tpubkey, vfyopts); if (i < 0) { goto end; @@ -1029,8 +984,6 @@ int req_main(int argc, char **argv) } ret = 0; end: - if (sm2_free) - OPENSSL_free(sm2_id); if (ret) { ERR_print_errors(bio_err); } @@ -1043,6 +996,7 @@ int req_main(int argc, char **argv) EVP_PKEY_CTX_free(genctx); sk_OPENSSL_STRING_free(pkeyopts); sk_OPENSSL_STRING_free(sigopts); + sk_OPENSSL_STRING_free(vfyopts); lh_OPENSSL_STRING_doall(addexts, exts_cleanup); lh_OPENSSL_STRING_free(addexts); #ifndef OPENSSL_NO_ENGINE @@ -1685,6 +1639,44 @@ static int genpkey_cb(EVP_PKEY_CTX *ctx) return 1; } +static int do_pkey_ctx_init(EVP_PKEY_CTX *pkctx, STACK_OF(OPENSSL_STRING) *opts) +{ + int i; + + if (opts == NULL) + return 1; + + for (i = 0; i < sk_OPENSSL_STRING_num(opts); i++) { + char *opt = sk_OPENSSL_STRING_value(opts, i); + if (pkey_ctrl_string(pkctx, opt) <= 0) { + BIO_printf(bio_err, "parameter error \"%s\"\n", opt); + ERR_print_errors(bio_err); + return 0; + } + } + + return 1; +} + +static int do_x509_init(X509 *x, STACK_OF(OPENSSL_STRING) *opts) +{ + int i; + + if (opts == NULL) + return 1; + + for (i = 0; i < sk_OPENSSL_STRING_num(opts); i++) { + char *opt = sk_OPENSSL_STRING_value(opts, i); + if (x509_ctrl_string(x, opt) <= 0) { + BIO_printf(bio_err, "parameter error \"%s\"\n", opt); + ERR_print_errors(bio_err); + return 0; + } + } + + return 1; +} + static int do_x509_req_init(X509_REQ *x, STACK_OF(OPENSSL_STRING) *opts) { int i; @@ -1708,28 +1700,10 @@ static int do_sign_init(EVP_MD_CTX *ctx, EVP_PKEY *pkey, const EVP_MD *md, STACK_OF(OPENSSL_STRING) *sigopts) { EVP_PKEY_CTX *pkctx = NULL; - EVP_PKEY_CTX *pctx = NULL; - int i, def_nid, ret = 0; + int def_nid; if (ctx == NULL) - goto err; - if (EVP_PKEY_id(pkey) == EVP_PKEY_SM2) { - pctx = EVP_PKEY_CTX_new(pkey, NULL); - if (pctx == NULL) { - BIO_printf(bio_err, "memory allocation failure.\n"); - goto err; - } - /* set SM2 ID from sig options before calling the real init routine */ - for (i = 0; i < sk_OPENSSL_STRING_num(sigopts); i++) { - char *sigopt = sk_OPENSSL_STRING_value(sigopts, i); - if (pkey_ctrl_string(pctx, sigopt) <= 0) { - BIO_printf(bio_err, "parameter error \"%s\"\n", sigopt); - ERR_print_errors(bio_err); - goto err; - } - } - EVP_MD_CTX_set_pkey_ctx(ctx, pctx); - } + return 0; /* * EVP_PKEY_get_default_digest_nid() returns 2 if the digest is mandatory * for this algorithm. @@ -1739,36 +1713,8 @@ static int do_sign_init(EVP_MD_CTX *ctx, EVP_PKEY *pkey, /* The signing algorithm requires there to be no digest */ md = NULL; } - if (!EVP_DigestSignInit(ctx, &pkctx, md, NULL, pkey)) - goto err; - for (i = 0; i < sk_OPENSSL_STRING_num(sigopts); i++) { - char *sigopt = sk_OPENSSL_STRING_value(sigopts, i); - if (pkey_ctrl_string(pkctx, sigopt) <= 0) { - BIO_printf(bio_err, "parameter error \"%s\"\n", sigopt); - ERR_print_errors(bio_err); - goto err; - } - } - - ret = 1; - err: - if (!ret) - EVP_PKEY_CTX_free(pctx); - return ret; -} - -static void do_sign_cleanup(EVP_MD_CTX *ctx, EVP_PKEY *pkey) -{ - /* - * With SM2, do_sign_init() attached an EVP_PKEY_CTX to the EVP_MD_CTX, - * and we have to free it explicitly. - */ - if (EVP_PKEY_id(pkey) == EVP_PKEY_SM2) { - EVP_PKEY_CTX *pctx = EVP_MD_CTX_pkey_ctx(ctx); - - EVP_MD_CTX_set_pkey_ctx(ctx, NULL); - EVP_PKEY_CTX_free(pctx); - } + return EVP_DigestSignInit(ctx, &pkctx, md, NULL, pkey) + && do_pkey_ctx_init(pkctx, sigopts); } int do_X509_sign(X509 *x, EVP_PKEY *pkey, const EVP_MD *md, @@ -1777,10 +1723,8 @@ int do_X509_sign(X509 *x, EVP_PKEY *pkey, const EVP_MD *md, int rv = 0; EVP_MD_CTX *mctx = EVP_MD_CTX_new(); - if (do_sign_init(mctx, pkey, md, sigopts) > 0) { + if (do_sign_init(mctx, pkey, md, sigopts) > 0) rv = (X509_sign_ctx(x, mctx) > 0); - do_sign_cleanup(mctx, pkey); - } EVP_MD_CTX_free(mctx); return rv; } @@ -1791,14 +1735,21 @@ int do_X509_REQ_sign(X509_REQ *x, EVP_PKEY *pkey, const EVP_MD *md, int rv = 0; EVP_MD_CTX *mctx = EVP_MD_CTX_new(); - if (do_sign_init(mctx, pkey, md, sigopts) > 0) { + if (do_sign_init(mctx, pkey, md, sigopts) > 0) rv = (X509_REQ_sign_ctx(x, mctx) > 0); - do_sign_cleanup(mctx, pkey); - } EVP_MD_CTX_free(mctx); return rv; } +int do_X509_verify(X509 *x, EVP_PKEY *pkey, STACK_OF(OPENSSL_STRING) *vfyopts) +{ + int rv = 0; + + if (do_x509_init(x, vfyopts) > 0) + rv = (X509_verify(x, pkey) > 0); + return rv; +} + int do_X509_REQ_verify(X509_REQ *x, EVP_PKEY *pkey, STACK_OF(OPENSSL_STRING) *vfyopts) { @@ -1815,10 +1766,8 @@ int do_X509_CRL_sign(X509_CRL *x, EVP_PKEY *pkey, const EVP_MD *md, int rv = 0; EVP_MD_CTX *mctx = EVP_MD_CTX_new(); - if (do_sign_init(mctx, pkey, md, sigopts) > 0) { + if (do_sign_init(mctx, pkey, md, sigopts) > 0) rv = (X509_CRL_sign_ctx(x, mctx) > 0); - do_sign_cleanup(mctx, pkey); - } EVP_MD_CTX_free(mctx); return rv; } diff --git a/apps/verify.c b/apps/verify.c index 82ca35e971..f626009f55 100644 --- a/apps/verify.c +++ b/apps/verify.c @@ -22,7 +22,7 @@ static int cb(int ok, X509_STORE_CTX *ctx); static int check(X509_STORE *ctx, const char *file, STACK_OF(X509) *uchain, STACK_OF(X509) *tchain, STACK_OF(X509_CRL) *crls, int show_chain, - unsigned char *sm2id, size_t sm2idlen); + STACK_OF(OPENSSL_STRING) *opts); static int v_verbose = 0, vflags = 0; typedef enum OPTION_choice { @@ -30,8 +30,8 @@ typedef enum OPTION_choice { OPT_ENGINE, OPT_CAPATH, OPT_CAFILE, OPT_CASTORE, OPT_NOCAPATH, OPT_NOCAFILE, OPT_NOCASTORE, OPT_UNTRUSTED, OPT_TRUSTED, OPT_CRLFILE, OPT_CRL_DOWNLOAD, OPT_SHOW_CHAIN, - OPT_V_ENUM, OPT_NAMEOPT, - OPT_VERBOSE, OPT_SM2ID, OPT_SM2HEXID, + OPT_V_ENUM, OPT_NAMEOPT, OPT_VFYOPT, + OPT_VERBOSE, OPT_PROV_ENUM } OPTION_CHOICE; @@ -67,12 +67,7 @@ const OPTIONS verify_options[] = { "Display information about the certificate chain"}, OPT_V_OPTIONS, -#ifndef OPENSSL_NO_SM2 - {"sm2-id", OPT_SM2ID, 's', - "Specify an ID string to verify an SM2 certificate"}, - {"sm2-hex-id", OPT_SM2HEXID, 's', - "Specify a hex ID string to verify an SM2 certificate"}, -#endif + {"vfyopt", OPT_VFYOPT, 's', "Verification parameter in n:v form"}, OPT_PROV_OPTIONS, @@ -86,15 +81,13 @@ int verify_main(int argc, char **argv) ENGINE *e = NULL; STACK_OF(X509) *untrusted = NULL, *trusted = NULL; STACK_OF(X509_CRL) *crls = NULL; + STACK_OF(OPENSSL_STRING) *vfyopts = NULL; X509_STORE *store = NULL; X509_VERIFY_PARAM *vpm = NULL; const char *prog, *CApath = NULL, *CAfile = NULL, *CAstore = NULL; int noCApath = 0, noCAfile = 0, noCAstore = 0; int vpmtouched = 0, crl_download = 0, show_chain = 0, i = 0, ret = 1; OPTION_CHOICE o; - unsigned char *sm2_id = NULL; - size_t sm2_idlen = 0; - int sm2_free = 0; if ((vpm = X509_VERIFY_PARAM_new()) == NULL) goto end; @@ -104,6 +97,7 @@ int verify_main(int argc, char **argv) switch (o) { case OPT_EOF: case OPT_ERR: + opthelp: BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); goto end; case OPT_HELP: @@ -186,32 +180,15 @@ int verify_main(int argc, char **argv) if (!set_nameopt(opt_arg())) goto end; break; + case OPT_VFYOPT: + if (!vfyopts) + vfyopts = sk_OPENSSL_STRING_new_null(); + if (!vfyopts || !sk_OPENSSL_STRING_push(vfyopts, opt_arg())) + goto opthelp; + break; case OPT_VERBOSE: v_verbose = 1; break; - case OPT_SM2ID: - if (sm2_id != NULL) { - BIO_printf(bio_err, - "Use one of the options 'sm2-hex-id' or 'sm2-id' \n"); - goto end; - } - sm2_id = (unsigned char *)opt_arg(); - sm2_idlen = strlen((const char *)sm2_id); - break; - case OPT_SM2HEXID: - if (sm2_id != NULL) { - BIO_printf(bio_err, - "Use one of the options 'sm2-hex-id' or 'sm2-id' \n"); - goto end; - } - /* try to parse the input as hex string first */ - sm2_free = 1; - sm2_id = OPENSSL_hexstr2buf(opt_arg(), (long *)&sm2_idlen); - if (sm2_id == NULL) { - BIO_printf(bio_err, "Invalid hex string input\n"); - goto end; - } - break; case OPT_PROV_CASES: if (!opt_provider(o)) goto end; @@ -244,23 +221,22 @@ int verify_main(int argc, char **argv) ret = 0; if (argc < 1) { if (check(store, NULL, untrusted, trusted, crls, show_chain, - sm2_id, sm2_idlen) != 1) + vfyopts) != 1) ret = -1; } else { for (i = 0; i < argc; i++) - if (check(store, argv[i], untrusted, trusted, crls, - show_chain, sm2_id, sm2_idlen) != 1) + if (check(store, argv[i], untrusted, trusted, crls, show_chain, + vfyopts) != 1) ret = -1; } end: - if (sm2_free) - OPENSSL_free(sm2_id); X509_VERIFY_PARAM_free(vpm); X509_STORE_free(store); sk_X509_pop_free(untrusted, X509_free); sk_X509_pop_free(trusted, X509_free); sk_X509_CRL_pop_free(crls, X509_CRL_free); + sk_OPENSSL_STRING_free(vfyopts); release_engine(e); return (ret < 0 ? 2 : ret); } @@ -268,7 +244,7 @@ int verify_main(int argc, char **argv) static int check(X509_STORE *ctx, const char *file, STACK_OF(X509) *uchain, STACK_OF(X509) *tchain, STACK_OF(X509_CRL) *crls, int show_chain, - unsigned char *sm2id, size_t sm2idlen) + STACK_OF(OPENSSL_STRING) *opts) { X509 *x = NULL; int i = 0, ret = 0; @@ -280,24 +256,15 @@ static int check(X509_STORE *ctx, const char *file, if (x == NULL) goto end; - if (sm2id != NULL) { -#ifndef OPENSSL_NO_SM2 - ASN1_OCTET_STRING *v; - - v = ASN1_OCTET_STRING_new(); - if (v == NULL) { - BIO_printf(bio_err, "error: SM2 ID allocation failed\n"); - goto end; - } - - if (!ASN1_OCTET_STRING_set(v, sm2id, sm2idlen)) { - BIO_printf(bio_err, "error: setting SM2 ID failed\n"); - ASN1_OCTET_STRING_free(v); - goto end; + if (opts != NULL) { + for (i = 0; i < sk_OPENSSL_STRING_num(opts); i++) { + char *opt = sk_OPENSSL_STRING_value(opts, i); + if (x509_ctrl_string(x, opt) <= 0) { + BIO_printf(bio_err, "parameter error \"%s\"\n", opt); + ERR_print_errors(bio_err); + return 0; + } } - - X509_set0_sm2_id(x, v); -#endif } csc = X509_STORE_CTX_new(); diff --git a/apps/x509.c b/apps/x509.c index 3176cf528c..e2a68828e3 100644 --- a/apps/x509.c +++ b/apps/x509.c @@ -33,7 +33,9 @@ #define DEF_DAYS 30 static int callb(int ok, X509_STORE_CTX *ctx); -static int sign(X509 *x, EVP_PKEY *pkey, EVP_PKEY *fkey, int days, int clrext, +static int sign(X509 *x, EVP_PKEY *pkey, EVP_PKEY *fkey, + STACK_OF(OPENSSL_STRING) *sigopts, + int days, int clrext, const EVP_MD *digest, CONF *conf, const char *section, int preserve_dates); static int x509_certify(X509_STORE *ctx, const char *CAfile, const EVP_MD *digest, @@ -48,7 +50,7 @@ static int print_x509v3_exts(BIO *bio, X509 *x, const char *exts); typedef enum OPTION_choice { OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, OPT_INFORM, OPT_OUTFORM, OPT_KEYFORM, OPT_REQ, OPT_CAFORM, - OPT_CAKEYFORM, OPT_SIGOPT, OPT_DAYS, OPT_PASSIN, OPT_EXTFILE, + OPT_CAKEYFORM, OPT_VFYOPT, OPT_SIGOPT, OPT_DAYS, OPT_PASSIN, OPT_EXTFILE, OPT_EXTENSIONS, OPT_IN, OPT_OUT, OPT_SIGNKEY, OPT_CA, OPT_CAKEY, OPT_CASERIAL, OPT_SET_SERIAL, OPT_NEW, OPT_FORCE_PUBKEY, OPT_SUBJ, OPT_ADDTRUST, OPT_ADDREJECT, OPT_SETALIAS, OPT_CERTOPT, OPT_NAMEOPT, @@ -80,6 +82,7 @@ const OPTIONS x509_options[] = { {"out", OPT_OUT, '>', "Output file - default stdout"}, {"keyform", OPT_KEYFORM, 'E', "Private key format - default PEM"}, {"req", OPT_REQ, '-', "Input is a certificate request, sign and output"}, + {"vfyopt", OPT_VFYOPT, 's', "Verification parameter in n:v form"}, OPT_SECTION("Output"), {"serial", OPT_SERIAL, '-', "Print serial number value"}, @@ -174,7 +177,7 @@ int x509_main(int argc, char **argv) const unsigned long chtype = MBSTRING_ASC; const int multirdn = 0; STACK_OF(ASN1_OBJECT) *trust = NULL, *reject = NULL; - STACK_OF(OPENSSL_STRING) *sigopts = NULL; + STACK_OF(OPENSSL_STRING) *sigopts = NULL, *vfyopts = NULL; X509 *x = NULL, *xca = NULL; X509_REQ *req = NULL, *rq = NULL; X509_STORE *ctx = NULL; @@ -256,6 +259,12 @@ int x509_main(int argc, char **argv) if (!sigopts || !sk_OPENSSL_STRING_push(sigopts, opt_arg())) goto opthelp; break; + case OPT_VFYOPT: + if (!vfyopts) + vfyopts = sk_OPENSSL_STRING_new_null(); + if (!vfyopts || !sk_OPENSSL_STRING_push(vfyopts, opt_arg())) + goto opthelp; + break; case OPT_DAYS: if (preserve_dates) goto opthelp; @@ -576,7 +585,7 @@ int x509_main(int argc, char **argv) BIO_printf(bio_err, "error unpacking public key\n"); goto end; } - i = X509_REQ_verify(req, pkey); + i = do_X509_REQ_verify(req, pkey, vfyopts); if (i < 0) { BIO_printf(bio_err, "Request self-signature verification error\n"); ERR_print_errors(bio_err); @@ -848,8 +857,8 @@ int x509_main(int argc, char **argv) goto end; } - if (!sign(x, Upkey, fkey, days, clrext, digest, extconf, - extsect, preserve_dates)) + if (!sign(x, Upkey, fkey, sigopts, days, clrext, digest, + extconf, extsect, preserve_dates)) goto end; } else if (CA_flag == i) { BIO_printf(bio_err, "Getting CA Private Key\n"); @@ -949,6 +958,7 @@ int x509_main(int argc, char **argv) EVP_PKEY_free(CApkey); EVP_PKEY_free(fkey); sk_OPENSSL_STRING_free(sigopts); + sk_OPENSSL_STRING_free(vfyopts); X509_REQ_free(rq); ASN1_INTEGER_free(sno); sk_ASN1_OBJECT_pop_free(trust, ASN1_OBJECT_free); @@ -1106,11 +1116,12 @@ static int callb(int ok, X509_STORE_CTX *ctx) } /* self-issue; self-sign unless a forced public key (fkey) is given */ -static int sign(X509 *x, EVP_PKEY *pkey, EVP_PKEY *fkey, int days, int clrext, +static int sign(X509 *x, EVP_PKEY *pkey, EVP_PKEY *fkey, + STACK_OF(OPENSSL_STRING) *sigopts, + int days, int clrext, const EVP_MD *digest, CONF *conf, const char *section, int preserve_dates) { - if (!X509_set_issuer_name(x, X509_get_subject_name(x))) goto err; if (!preserve_dates && !set_cert_times(x, NULL, NULL, days)) @@ -1129,7 +1140,7 @@ static int sign(X509 *x, EVP_PKEY *pkey, EVP_PKEY *fkey, int days, int clrext, if (!X509V3_EXT_add_nconf(conf, &ctx, section, x)) goto err; } - if (!X509_sign(x, pkey, digest)) + if (!do_X509_sign(x, pkey, digest, sigopts)) goto err; return 1; err: diff --git a/doc/man1/openssl-ca.pod.in b/doc/man1/openssl-ca.pod.in index 5192b25fa5..e07295cffc 100644 --- a/doc/man1/openssl-ca.pod.in +++ b/doc/man1/openssl-ca.pod.in @@ -53,17 +53,16 @@ B B [B<-subj> I] [B<-utf8>] [B<-sigopt> I:I] +[B<-vfyopt> I:I] [B<-create_serial>] [B<-rand_serial>] [B<-multivalue-rdn>] -[B<-sm2-id> I] -[B<-sm2-hex-id> I] {- $OpenSSL::safe::opt_r_synopsis -} {- $OpenSSL::safe::opt_engine_synopsis -} {- $OpenSSL::safe::opt_provider_synopsis -} [I...] -=for openssl ifdef engine sm2-id sm2-hex-id +=for openssl ifdef engine =head1 DESCRIPTION @@ -147,9 +146,18 @@ See L for details. =item B<-sigopt> I:I -Pass options to the signature algorithm during sign or verify operations. +Pass options to the signature algorithm during sign operations. Names and values of these options are algorithm-specific. +=item B<-vfyopt> I:I + +Pass options to the signature algorithm during verify operations. +Names and values of these options are algorithm-specific. + +This often needs to be given while signing too, because the input +certificate signature request is verified against its own public key, +and that verification may need its own set of options. + =item B<-key> I =for openssl foreign manual ps(1) @@ -297,16 +305,6 @@ C If B<-multi-rdn> is not used then the UID value is C<123456+CN=John Doe>. -=item B<-sm2-id> I - -Specify the ID string to use when verifying an SM2 certificate. The ID string is -required by the SM2 signature algorithm for signing and verification. - -=item B<-sm2-hex-id> I - -Specify a binary ID string to use when signing or verifying using an SM2 -certificate. The argument for this option is string of hexadecimal digits. - {- $OpenSSL::safe::opt_r_item -} {- $OpenSSL::safe::opt_engine_item -} @@ -617,7 +615,9 @@ Sign a certificate request: Sign an SM2 certificate request: - openssl ca -in sm2.csr -out sm2.crt -md sm3 -sigopt "sm2_id:1234567812345678" -sm2-id "1234567812345678" + openssl ca -in sm2.csr -out sm2.crt -md sm3 \ + -sigopt "distid:1234567812345678" \ + -vfyopt "distid:1234567812345678" Sign a certificate request, using CA extensions: diff --git a/doc/man1/openssl-pkeyutl.pod.in b/doc/man1/openssl-pkeyutl.pod.in index 583ea68734..8f9060a239 100644 --- a/doc/man1/openssl-pkeyutl.pod.in +++ b/doc/man1/openssl-pkeyutl.pod.in @@ -321,18 +321,18 @@ must be known for this to work. If the size of the file cannot be determined =head1 SM2 The SM2 algorithm supports sign, verify, encrypt and decrypt operations. For -the sign and verify operations, SM2 requires an ID string to be passed in. The -following B<-pkeyopt> value is supported: +the sign and verify operations, SM2 requires an Distinguishing ID string to +be passed in. The following B<-pkeyopt> value is supported: =over 4 -=item BI +=item BI This sets the ID string used in SM2 sign or verify operations. While verifying an SM2 signature, the ID string must be the same one used when signing the data. Otherwise the verification will fail. -=item BI +=item BI This sets the ID string used in SM2 sign or verify operations. While verifying an SM2 signature, the ID string must be the same one used when signing the data. @@ -382,12 +382,12 @@ Derive using the same algorithm, but read key from environment variable MYPASS: Sign some data using an L private key and a specific ID: openssl pkeyutl -sign -in file -inkey sm2.key -out sig -rawin -digest sm3 \ - -pkeyopt sm2_id:someid + -pkeyopt distid:someid Verify some data using an L certificate and a specific ID: openssl pkeyutl -verify -certin -in file -inkey sm2.cert -sigfile sig \ - -rawin -digest sm3 -pkeyopt sm2_id:someid + -rawin -digest sm3 -pkeyopt distid:someid =head1 SEE ALSO diff --git a/doc/man1/openssl-req.pod.in b/doc/man1/openssl-req.pod.in index ca3416e799..c8abeb368d 100644 --- a/doc/man1/openssl-req.pod.in +++ b/doc/man1/openssl-req.pod.in @@ -45,16 +45,15 @@ B B [B<-subject>] [B<-subj> I] [B<-sigopt> I:I] +[B<-vfyopt> I:I] [B<-batch>] [B<-verbose>] -[B<-sm2-id> I] -[B<-sm2-hex-id> I] {- $OpenSSL::safe::opt_name_synopsis -} {- $OpenSSL::safe::opt_r_synopsis -} {- $OpenSSL::safe::opt_engine_synopsis -} {- $OpenSSL::safe::opt_provider_synopsis -} -=for openssl ifdef engine keygen_engine sm2-id sm2-hex-id +=for openssl ifdef engine keygen_engine =head1 DESCRIPTION @@ -85,9 +84,22 @@ options (B<-new> and B<-newkey>) are not specified. =item B<-sigopt> I:I -Pass options to the signature algorithm during sign or verify operations. +Pass options to the signature algorithm during sign operations. Names and values of these options are algorithm-specific. +=item B<-vfyopt> I:I + +Pass options to the signature algorithm during verify operations. +Names and values of these options are algorithm-specific. + +=begin comment + +Maybe it would be preferable to only have -opts instead of -sigopt and +-vfyopt? They are both present here to be compatible with L, +which supports both options for good reasons. + +=end comment + =item B<-passin> I, B<-passout> I The password source for the input and output file. @@ -313,16 +325,6 @@ Print extra details about the operations being performed. Specifies an engine (by its unique I string) which would be used for key generation operations. -=item B<-sm2-id> - -Specify the ID string to use when verifying an SM2 certificate request. The ID -string is required by the SM2 signature algorithm for signing and verification. - -=item B<-sm2-hex-id> - -Specify a binary ID string to use when verifying an SM2 certificate request. The -argument for this option is string of hexadecimal digits. - {- $OpenSSL::safe::opt_name_item -} {- $OpenSSL::safe::opt_r_item -} @@ -531,11 +533,11 @@ Generate a self signed root certificate: Create an SM2 private key and then generate a certificate request from it: openssl ecparam -genkey -name SM2 -out sm2.key - openssl req -new -key sm2.key -out sm2.csr -sm3 -sigopt "sm2_id:1234567812345678" + openssl req -new -key sm2.key -out sm2.csr -sm3 -sigopt "distid:1234567812345678" Examine and verify an SM2 certificate request: - openssl req -verify -in sm2.csr -sm3 -sm2-id 1234567812345678 + openssl req -verify -in sm2.csr -sm3 -vfyopt "distid:1234567812345678" Example of a file pointed to by the B option: diff --git a/doc/man1/openssl-verify.pod.in b/doc/man1/openssl-verify.pod.in index 7a15e73721..821f88dae9 100644 --- a/doc/man1/openssl-verify.pod.in +++ b/doc/man1/openssl-verify.pod.in @@ -12,11 +12,10 @@ B B [B<-CRLfile> I] [B<-crl_download>] [B<-show_chain>] -[B<-sm2-id> I] -[B<-sm2-hex-id> I] [B<-verbose>] [B<-trusted> I] [B<-untrusted> I] +[B<-vfyopt> I:I] {- $OpenSSL::safe::opt_name_synopsis -} {- $OpenSSL::safe::opt_trust_synopsis -} {- $OpenSSL::safe::opt_engine_synopsis -} @@ -25,7 +24,7 @@ B B [B<-->] [I ...] -=for openssl ifdef engine sm2-id sm2-hex-id +=for openssl ifdef engine =head1 DESCRIPTION @@ -59,16 +58,6 @@ Display information about the certificate chain that has been built (if successful). Certificates in the chain that came from the untrusted list will be flagged as "untrusted". -=item B<-sm2-id> I - -Specify the ID string to use when verifying an SM2 certificate. The ID string is -required by the SM2 signature algorithm for signing and verification. - -=item B<-sm2-hex-id> I - -Specify a binary ID string to use when signing or verifying using an SM2 -certificate. The argument for this option is string of hexadecimal digits. - =item B<-verbose> Print extra information about the operations being performed. @@ -81,6 +70,11 @@ A file of trusted certificates. A file of untrusted certificates. +=item B<-vfyopt> I:I + +Pass options to the signature algorithm during verify operations. +Names and values of these options are algorithm-specific. + {- $OpenSSL::safe::opt_name_item -} {- $OpenSSL::safe::opt_engine_item -} @@ -159,8 +153,6 @@ L The B<-show_chain> option was added in OpenSSL 1.1.0. -The B<-sm2-id> and B<-sm2-hex-id> options were added in OpenSSL 3.0. - =head1 COPYRIGHT Copyright 2000-2019 The OpenSSL Project Authors. All Rights Reserved. diff --git a/doc/man1/openssl-x509.pod.in b/doc/man1/openssl-x509.pod.in index 98b4e71231..568a2053fc 100644 --- a/doc/man1/openssl-x509.pod.in +++ b/doc/man1/openssl-x509.pod.in @@ -71,6 +71,7 @@ B B [B<-extfile> I] [B<-extensions> I
] [B<-sigopt> I:I] +[B<-vfyopt> I:I] [B<-preserve_dates>] {- $OpenSSL::safe::opt_name_synopsis -} {- $OpenSSL::safe::opt_r_synopsis -} @@ -371,7 +372,12 @@ for testing. =item B<-sigopt> I:I -Pass options to the signature algorithm during sign or verify operations. +Pass options to the signature algorithm during sign operations. +Names and values of these options are algorithm-specific. + +=item B<-vfyopt> I:I + +Pass options to the signature algorithm during verify operations. Names and values of these options are algorithm-specific. =item B<-passin> I -- 2.25.1