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:
parent
7086332550
commit
c7764dacdf
@ -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,
|
||||
|
127
apps/pkeyutl.c
127
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");
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user