diff --git a/CHANGES.md b/CHANGES.md index 08faf0501d6c1..461e8529c0b91 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -208,6 +208,11 @@ OpenSSL 3.4 *Damian Hobson-Garcia* + * The `-rawin` option of the `pkeyutl` command is now implied (and thus no more + required) when signing or verifying with an Ed25519 or Ed448 key. + + *David von Oheimb* + * Added support to build Position Independent Executables (PIE). Configuration option `enable-pie` configures the cflag '-fPIE' and ldflag '-pie' to support Address Space Layout Randomization (ASLR) in the openssl executable, diff --git a/apps/pkeyutl.c b/apps/pkeyutl.c index b73ef3297bed4..961a2f7650c6c 100644 --- a/apps/pkeyutl.c +++ b/apps/pkeyutl.c @@ -20,12 +20,15 @@ #define KEY_PUBKEY 2 #define KEY_CERT 3 +static EVP_PKEY *get_pkey(const char *kdfalg, + const char *keyfile, int keyform, int key_type, + char *passinarg, int pkey_op, ENGINE *e); static EVP_PKEY_CTX *init_ctx(const char *kdfalg, int *pkeysize, - const char *keyfile, int keyform, int key_type, - char *passinarg, int pkey_op, ENGINE *e, - const int impl, int rawin, EVP_PKEY **ppkey, - EVP_MD_CTX *mctx, const char *digestname, const char *kemop, - OSSL_LIB_CTX *libctx, const char *propq); + int pkey_op, ENGINE *e, + const int engine_impl, int rawin, + EVP_PKEY *pkey /* ownership is passed to ctx */, + EVP_MD_CTX *mctx, const char *digestname, + const char *kemop, OSSL_LIB_CTX *libctx, const char *propq); static int setup_peer(EVP_PKEY_CTX *ctx, int peerform, const char *file, ENGINE *e); @@ -40,6 +43,14 @@ static int do_raw_keyop(int pkey_op, EVP_MD_CTX *mctx, int filesize, unsigned char *sig, int siglen, unsigned char **out, size_t *poutlen); +static int only_rawin(const EVP_PKEY *pkey) +{ + if (pkey == NULL) + return 0; + return EVP_PKEY_is_a(pkey, "ED25519") + || EVP_PKEY_is_a(pkey, "ED448"); +} + typedef enum OPTION_choice { OPT_COMMON, OPT_ENGINE, OPT_ENGINE_IMPL, OPT_IN, OPT_OUT, @@ -72,7 +83,7 @@ const OPTIONS pkeyutl_options[] = { OPT_SECTION("Input"), {"in", OPT_IN, '<', "Input file - default stdin"}, - {"rawin", OPT_RAWIN, '-', "Indicate the input data is in raw form"}, + {"rawin", OPT_RAWIN, '-', "Indicate that signature input data is not hashed"}, {"inkey", OPT_INKEY, 's', "Input key, by default private key"}, {"pubin", OPT_PUBIN, '-', "Input key is a public key"}, {"passin", OPT_PASSIN, 's', "Input file pass phrase source"}, @@ -277,26 +288,6 @@ int pkeyutl_main(int argc, char **argv) if (!app_RAND_load()) goto end; - if (rawin && pkey_op != EVP_PKEY_OP_SIGN && pkey_op != EVP_PKEY_OP_VERIFY) { - BIO_printf(bio_err, - "%s: -rawin can only be used with -sign or -verify\n", - prog); - goto opthelp; - } - - if (digestname != NULL && !rawin) { - BIO_printf(bio_err, - "%s: -digest can only be used with -rawin\n", - prog); - goto opthelp; - } - - if (rawin && rev) { - BIO_printf(bio_err, "%s: -rev cannot be used with raw input\n", - prog); - goto opthelp; - } - if (kdfalg != NULL) { if (kdflen == 0) { BIO_printf(bio_err, @@ -313,14 +304,45 @@ int pkeyutl_main(int argc, char **argv) goto opthelp; } + pkey = get_pkey(kdfalg, inkey, keyform, key_type, passinarg, pkey_op, e); + if (pkey_op == EVP_PKEY_OP_SIGN || pkey_op == EVP_PKEY_OP_VERIFY) { + if (only_rawin(pkey)) { + if ((EVP_PKEY_is_a(pkey, "ED25519") || EVP_PKEY_is_a(pkey, "ED448")) + && digestname != NULL) { + BIO_printf(bio_err, + "%s: -digest (prehash) is not supported with EdDSA\n", prog); + EVP_PKEY_free(pkey); + goto end; + } + rawin = 1; /* implied for Ed25519(ph) and Ed448(ph) and maybe others in the future */ + } + } else if (rawin) { + BIO_printf(bio_err, + "%s: -rawin can only be used with -sign or -verify\n", prog); + EVP_PKEY_free(pkey); + goto opthelp; + } + if (digestname != NULL && !rawin) { + BIO_printf(bio_err, + "%s: -digest can only be used with -rawin\n", prog); + EVP_PKEY_free(pkey); + goto opthelp; + } + + if (rawin && rev) { + BIO_printf(bio_err, "%s: -rev cannot be used with raw input\n", prog); + EVP_PKEY_free(pkey); + goto opthelp; + } + if (rawin) { if ((mctx = EVP_MD_CTX_new()) == NULL) { BIO_printf(bio_err, "Error: out of memory\n"); + EVP_PKEY_free(pkey); goto end; } } - ctx = init_ctx(kdfalg, &keysize, inkey, keyform, key_type, - passinarg, pkey_op, e, engine_impl, rawin, &pkey, + ctx = init_ctx(kdfalg, &keysize, pkey_op, e, engine_impl, rawin, pkey, mctx, digestname, kemop, libctx, app_get0_propq()); if (ctx == NULL) { BIO_printf(bio_err, "%s: Error initializing context\n", prog); @@ -374,8 +396,10 @@ int pkeyutl_main(int argc, char **argv) goto end; } } else { - /* Get password as a passin argument: First split option name - * and passphrase argument into two strings */ + /* + * Get password as a passin argument: First split option name + * and passphrase argument into two strings + */ *passin = 0; passin++; if (app_passwd(passin, NULL, &passwd, NULL) == 0) { @@ -457,6 +481,7 @@ int pkeyutl_main(int argc, char **argv) size_t i; unsigned char ctmp; size_t l = (size_t)buf_inlen; + for (i = 0; i < l / 2; i++) { ctmp = buf_in[i]; buf_in[i] = buf_in[l - 1 - i]; @@ -553,29 +578,23 @@ int pkeyutl_main(int argc, char **argv) return ret; } -static EVP_PKEY_CTX *init_ctx(const char *kdfalg, int *pkeysize, - const char *keyfile, int keyform, int key_type, - char *passinarg, int pkey_op, ENGINE *e, - const int engine_impl, int rawin, - EVP_PKEY **ppkey, EVP_MD_CTX *mctx, const char *digestname, - const char *kemop, OSSL_LIB_CTX *libctx, const char *propq) +static EVP_PKEY *get_pkey(const char *kdfalg, + const char *keyfile, int keyform, int key_type, + char *passinarg, int pkey_op, ENGINE *e) { EVP_PKEY *pkey = NULL; - EVP_PKEY_CTX *ctx = NULL; - ENGINE *impl = NULL; char *passin = NULL; - int rv = -1; X509 *x; if (((pkey_op == EVP_PKEY_OP_SIGN) || (pkey_op == EVP_PKEY_OP_DECRYPT) || (pkey_op == EVP_PKEY_OP_DERIVE)) && (key_type != KEY_PRIVKEY && kdfalg == NULL)) { BIO_printf(bio_err, "A private key is needed for this operation\n"); - goto end; + return NULL; } if (!app_passwd(passinarg, NULL, &passin, NULL)) { BIO_printf(bio_err, "Error getting password\n"); - goto end; + return NULL; } switch (key_type) { case KEY_PRIVKEY: @@ -598,6 +617,20 @@ static EVP_PKEY_CTX *init_ctx(const char *kdfalg, int *pkeysize, break; } + OPENSSL_free(passin); + return pkey; +} + +static EVP_PKEY_CTX *init_ctx(const char *kdfalg, int *pkeysize, + int pkey_op, ENGINE *e, + const int engine_impl, int rawin, + EVP_PKEY *pkey /* ownership is passed to ctx */, + EVP_MD_CTX *mctx, const char *digestname, + const char *kemop, OSSL_LIB_CTX *libctx, const char *propq) +{ + EVP_PKEY_CTX *ctx = NULL; + ENGINE *impl = NULL; + int rv = -1; #ifndef OPENSSL_NO_ENGINE if (engine_impl) @@ -628,9 +661,8 @@ static EVP_PKEY_CTX *init_ctx(const char *kdfalg, int *pkeysize, ctx = EVP_PKEY_CTX_new(pkey, impl); else ctx = EVP_PKEY_CTX_new_from_pkey(libctx, pkey, propq); - if (ppkey != NULL) - *ppkey = pkey; EVP_PKEY_free(pkey); + pkey = NULL; } if (ctx == NULL) @@ -696,10 +728,11 @@ static EVP_PKEY_CTX *init_ctx(const char *kdfalg, int *pkeysize, ctx = NULL; } - end: - OPENSSL_free(passin); return ctx; + end: + EVP_PKEY_free(pkey); + return NULL; } static int setup_peer(EVP_PKEY_CTX *ctx, int peerform, const char *file, @@ -729,6 +762,7 @@ static int do_keyop(EVP_PKEY_CTX *ctx, int pkey_op, unsigned char *secret, size_t *pseclen) { int rv = 0; + switch (pkey_op) { case EVP_PKEY_OP_VERIFYRECOVER: rv = EVP_PKEY_verify_recover(ctx, out, poutlen, in, inlen); @@ -775,8 +809,7 @@ static int do_raw_keyop(int pkey_op, EVP_MD_CTX *mctx, int buf_len = 0; /* Some algorithms only support oneshot digests */ - if (EVP_PKEY_get_id(pkey) == EVP_PKEY_ED25519 - || EVP_PKEY_get_id(pkey) == EVP_PKEY_ED448) { + if (only_rawin(pkey)) { if (filesize < 0) { BIO_printf(bio_err, "Error: unable to determine file size for oneshot operation\n"); diff --git a/doc/man1/openssl-pkeyutl.pod.in b/doc/man1/openssl-pkeyutl.pod.in index 1284d0d941c91..d3982daec3d23 100644 --- a/doc/man1/openssl-pkeyutl.pod.in +++ b/doc/man1/openssl-pkeyutl.pod.in @@ -63,10 +63,12 @@ if this option is not specified. =item B<-rawin> -This indicates that the input data is raw data, which is not hashed by any -message digest algorithm. The user can specify a digest algorithm by using -the B<-digest> option. This option can only be used with B<-sign> and -B<-verify> and must be used with the Ed25519 and Ed448 algorithms. +This indicates that signature input data is raw data, which for most signature +algorithms (but not EdDSA) needs to be hashed by some message digest algorithm. +This option can only be used with B<-sign> and B<-verify> +and is implied by the Ed25519 and Ed448 algorithms. +Except with EdDSA, +the user can specify a digest algorithm by using the B<-digest> option. =item B<-digest> I @@ -77,6 +79,8 @@ is omitted but the signature algorithm requires one, a default value will be used. For signature algorithms like RSA, DSA and ECDSA, SHA-256 will be the default digest algorithm. For SM2, it will be SM3. If this option is present, then the B<-rawin> option must be also specified. +So far, HashEdDSA (the ph or "prehash" variant of EdDSA) is not supported, +so the B<-digest> option cannot be used with EdDSA). =item B<-out> I @@ -128,6 +132,7 @@ The input is a certificate containing a public key. Reverse the order of the input buffer. This is useful for some libraries (such as CryptoAPI) which represent the buffer in little endian format. +This cannot be used in conjunction with B<-rawin>. =item B<-sign> diff --git a/test/recipes/20-test_pkeyutl.t b/test/recipes/20-test_pkeyutl.t index 4f2f4ed04881d..d78e74d38abdc 100644 --- a/test/recipes/20-test_pkeyutl.t +++ b/test/recipes/20-test_pkeyutl.t @@ -17,7 +17,7 @@ use File::Compare qw/compare_text compare/; setup("test_pkeyutl"); -plan tests => 19; +plan tests => 23; # For the tests below we use the cert itself as the TBS file @@ -54,19 +54,19 @@ SKIP: { } SKIP: { - skip "Skipping tests that require ECX", 4 + skip "Skipping tests that require ECX", 6 if disabled("ecx"); # Ed25519 ok(run(app(([ 'openssl', 'pkeyutl', '-sign', '-in', srctop_file('test', 'certs', 'server-ed25519-cert.pem'), '-inkey', srctop_file('test', 'certs', 'server-ed25519-key.pem'), - '-out', 'Ed25519.sig', '-rawin']))), + '-out', 'Ed25519.sig']))), "Sign a piece of data using Ed25519"); ok(run(app(([ 'openssl', 'pkeyutl', '-verify', '-certin', '-in', srctop_file('test', 'certs', 'server-ed25519-cert.pem'), '-inkey', srctop_file('test', 'certs', 'server-ed25519-cert.pem'), - '-sigfile', 'Ed25519.sig', '-rawin']))), + '-sigfile', 'Ed25519.sig']))), "Verify an Ed25519 signature against a piece of data"); # Ed448 @@ -80,6 +80,16 @@ SKIP: { '-inkey', srctop_file('test', 'certs', 'server-ed448-cert.pem'), '-sigfile', 'Ed448.sig', '-rawin']))), "Verify an Ed448 signature against a piece of data"); + ok(run(app(([ 'openssl', 'pkeyutl', '-sign', '-in', + srctop_file('test', 'certs', 'server-ed448-cert.pem'), + '-inkey', srctop_file('test', 'certs', 'server-ed448-key.pem'), + '-out', 'Ed448.sig']))), + "Sign a piece of data using Ed448 -rawin no more needed"); + ok(run(app(([ 'openssl', 'pkeyutl', '-verify', '-certin', '-in', + srctop_file('test', 'certs', 'server-ed448-cert.pem'), + '-inkey', srctop_file('test', 'certs', 'server-ed448-cert.pem'), + '-sigfile', 'Ed448.sig']))), + "Verify an Ed448 signature against a piece of data, no -rawin"); } sub tsignverify { @@ -183,7 +193,7 @@ SKIP: { } SKIP: { - skip "EdDSA is not supported by this OpenSSL build", 2 + skip "EdDSA is not supported by this OpenSSL build", 4 if disabled("ecx"); subtest "Ed2559 CLI signature generation and verification" => sub { @@ -199,6 +209,18 @@ SKIP: { srctop_file("test","tested448pub.pem"), "-rawin"); }; + + subtest "Ed2559 CLI signature generation and verification, no -rawin" => sub { + tsignverify("Ed25519", + srctop_file("test","tested25519.pem"), + srctop_file("test","tested25519pub.pem")); + }; + + subtest "Ed448 CLI signature generation and verification, no -rawin" => sub { + tsignverify("Ed448", + srctop_file("test","tested448.pem"), + srctop_file("test","tested448pub.pem")); + }; } #Encap/decap tests