APPS/pkeyutl: improve -rawin usability (implied by Ed25519 and Ed448) and doc

Reviewed-by: Viktor Dukhovni <viktor@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/22910)
This commit is contained in:
Dr. David von Oheimb 2023-12-02 14:50:36 +01:00
parent 7086332550
commit c7764dacdf
4 changed files with 122 additions and 57 deletions

View File

@ -25,7 +25,7 @@ OpenSSL Releases
- [OpenSSL 1.0.0](#openssl-100)
- [OpenSSL 0.9.x](#openssl-09x)
OpenSSL 3.4
OpenSSL 3.5
-----------
### Changes between 3.4 and 3.5 [xx XXX xxxx]
@ -215,6 +215,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,

View File

@ -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");

View File

@ -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<algorithm>
@ -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.
At this time, 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<filename>
@ -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>

View File

@ -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