]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
APPS/pkeyutl: improve -rawin usability (implied by Ed25519 and Ed448) and doc
authorDr. David von Oheimb <dev@ddvo.net>
Sat, 2 Dec 2023 13:50:36 +0000 (14:50 +0100)
committerDr. David von Oheimb <dev@ddvo.net>
Mon, 4 Nov 2024 09:18:24 +0000 (10:18 +0100)
Reviewed-by: Viktor Dukhovni <viktor@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/22910)

CHANGES.md
apps/pkeyutl.c
doc/man1/openssl-pkeyutl.pod.in
test/recipes/20-test_pkeyutl.t

index f455694538c9cf5ab9c3c48549689d8ed2cc6d55..a32cf7946621775ae90e979877d0d01a36d12bf3 100644 (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,
index b73ef3297bed47e79ee29c19cf37427b590a3cea..961a2f7650c6c954a6c9316e5a004c218ce8a1f6 100644 (file)
 #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");
index 1284d0d941c911602c13fbec397cc7e827351353..99b2c11aee64a39b2003105fcad3746c465ff543 100644 (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>
 
index 4f2f4ed04881d368a64b659b31acf13f7ce7994e..d78e74d38abdc3392084d9897aab9e7be30ad131 100644 (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