mirror of
https://github.com/openssl/openssl.git
synced 2026-05-07 20:12:39 +00:00
Use mmap for pkeyutl -rawin and dgst one-shot input
When using openssl pkeyutl -rawin or openssl dgst for one-shot sign/verify (e.g. Ed25519, Ed448), file input is now read via mmap() on Unix where supported, avoiding a full buffer allocation and copy. Large files are supported without doubling memory use; on failure of the mmap path we do not fall back to the buffer path. - Add app_mmap_file() in apps/lib/apps.c: stat/open/mmap/close, tri-state return (1 mapped, 0 size zero, -1 error). Parameter err_bio avoids shadowing global bio_err (-Wshadow). - apps/pkeyutl.c and apps/dgst.c: use app_mmap_file(); single exit for mmap path in pkeyutl; dgst includes apps.h first for _FILE_OFFSET_BITS; do_fp_oneshot_sign returns EXIT_SUCCESS/EXIT_FAILURE like do_fp(); no fallback when mmap attempted but fails. - pkeyutl mmap/buffer path: pass filesize to EVP_DigestVerify and EVP_DigestSign (review suggestion, avoids casting buf_len). - Error messages: per-file messages for stat/size (dgst, pkeyutl); CHANGES.md "Unix-like" and "16 MB" (documentation style). - Centralize _FILE_OFFSET_BITS and mmap includes in apps/include/apps.h. - Tests: pkeyutl/dgst oneshot from file, no-fallback regression tests; use srctop_dir for test paths; stderr patterns for mmap errors. - Docs: man pages and CHANGES.md. CI fixes: return failure from dgst one-shot sign when mmap fails; treat non-regular paths as mmap errors in app_mmap_file() and pkeyutl; reject directories before mmap. Addresses review feedback from DDvO, npajkovsky, and vdukhovni (PR #30429). Fixes #11677 Co-authored-by: Viktor Dukhovni <viktor1ghub@dukhovni.org> Co-authored-by: David von Oheimb <DDvO@users.noreply.github.com> Reviewed-by: David von Oheimb <david.von.oheimb@siemens.com> Reviewed-by: Viktor Dukhovni <viktor@openssl.org> Reviewed-by: Paul Dale <paul.dale@oracle.com> MergeDate: Fri Mar 27 16:25:33 2026 (Merged from https://github.com/openssl/openssl/pull/30429)
This commit is contained in:
committed by
Tomas Mraz
parent
b31d15ba6a
commit
80b7e49c27
+11
@@ -31,6 +31,17 @@ OpenSSL Releases
|
||||
|
||||
### Changes between 4.0 and 4.1 [xx XXX xxxx]
|
||||
|
||||
* The `openssl pkeyutl` command now uses memory-mapped I/O when reading
|
||||
raw input from a file for oneshot sign/verify operations (such as Ed25519,
|
||||
Ed448, and ML-DSA) on platforms that support it (Unix-like). The
|
||||
`openssl dgst` command uses the same approach for one-shot sign/verify
|
||||
when the input is from a file, removing the previous 16 MB limit for
|
||||
file-based input. This improves performance and supports large files
|
||||
without doubling memory use. Other platforms and stdin input continue to
|
||||
use the existing buffer-based path.
|
||||
|
||||
*John Claus*
|
||||
|
||||
* Added AVX2 optimized ML-DSA NTT operations on `x86_64`.
|
||||
|
||||
*Marcel Cornu and Tomasz Kantecki*
|
||||
|
||||
+72
-35
@@ -7,10 +7,10 @@
|
||||
* https://www.openssl.org/source/license.html
|
||||
*/
|
||||
|
||||
#include "apps.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "apps.h"
|
||||
#include "progs.h"
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/err.h>
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/hmac.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#undef BUFSIZE
|
||||
#define BUFSIZE 1024 * 8
|
||||
@@ -482,7 +483,7 @@ int dgst_main(int argc, char **argv)
|
||||
BIO_set_fp(in, stdin, BIO_NOCLOSE);
|
||||
if (oneshot_sign)
|
||||
ret = do_fp_oneshot_sign(out, signctx, in, separator, out_bin,
|
||||
sigkey, sigbuf, siglen, NULL, "stdin");
|
||||
sigkey, sigbuf, siglen, NULL, NULL);
|
||||
else
|
||||
ret = do_fp(out, buf, inp, separator, out_bin, xoflen,
|
||||
sigkey, sigbuf, siglen, NULL, md_name, "stdin");
|
||||
@@ -722,6 +723,43 @@ end:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform one-shot verify or sign on a contiguous data buffer.
|
||||
* Returns 0 on failure, 1 on success.
|
||||
*/
|
||||
static int do_oneshot_verify_sign(EVP_MD_CTX *ctx, BIO *out,
|
||||
unsigned char *sigin, int siglen, EVP_PKEY *key,
|
||||
const unsigned char *data, size_t len,
|
||||
int sep, int binout, const char *sig_name, const char *file)
|
||||
{
|
||||
int res;
|
||||
size_t siglen_out = 0;
|
||||
unsigned char *sig = NULL;
|
||||
|
||||
if (sigin != NULL) {
|
||||
res = EVP_DigestVerify(ctx, sigin, siglen, data, len);
|
||||
print_verify_result(out, res);
|
||||
return res > 0;
|
||||
}
|
||||
if (key != NULL) {
|
||||
if (EVP_DigestSign(ctx, NULL, &siglen_out, data, len) != 1) {
|
||||
BIO_puts(bio_err, "Error getting maximum length of signed data\n");
|
||||
return 0;
|
||||
}
|
||||
sig = app_malloc(siglen_out, "Signature buffer");
|
||||
if (EVP_DigestSign(ctx, sig, &siglen_out, data, len) != 1) {
|
||||
BIO_puts(bio_err, "Error signing data\n");
|
||||
OPENSSL_free(sig);
|
||||
return 0;
|
||||
}
|
||||
print_out(out, sig, siglen_out, sep, binout, sig_name, NULL, file);
|
||||
OPENSSL_free(sig);
|
||||
return 1;
|
||||
}
|
||||
BIO_puts(bio_err, "key must be set for one-shot algorithms\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some new algorithms only support one shot operations.
|
||||
* For these we need to buffer all input and then do the sign on the
|
||||
@@ -732,43 +770,42 @@ static int do_fp_oneshot_sign(BIO *out, EVP_MD_CTX *ctx, BIO *in, int sep, int b
|
||||
EVP_PKEY *key, unsigned char *sigin, int siglen,
|
||||
const char *sig_name, const char *file)
|
||||
{
|
||||
int res, ret = EXIT_FAILURE;
|
||||
size_t len = 0;
|
||||
int ret = EXIT_FAILURE;
|
||||
size_t buflen = 0;
|
||||
size_t maxlen = 16 * 1024 * 1024;
|
||||
uint8_t *buf = NULL, *sig = NULL;
|
||||
uint8_t *buf = NULL;
|
||||
|
||||
if (!bio_to_mem(&buf, &buflen, maxlen, in)) {
|
||||
BIO_printf(bio_err, "Read error in %s\n", file);
|
||||
return ret;
|
||||
}
|
||||
if (sigin != NULL) {
|
||||
res = EVP_DigestVerify(ctx, sigin, siglen, buf, buflen);
|
||||
print_verify_result(out, res);
|
||||
if (res > 0)
|
||||
ret = EXIT_SUCCESS;
|
||||
goto end;
|
||||
}
|
||||
if (key != NULL) {
|
||||
if (EVP_DigestSign(ctx, NULL, &len, buf, buflen) != 1) {
|
||||
BIO_puts(bio_err, "Error getting maximum length of signed data\n");
|
||||
goto end;
|
||||
}
|
||||
sig = app_malloc(len, "Signature buffer");
|
||||
if (EVP_DigestSign(ctx, sig, &len, buf, buflen) != 1) {
|
||||
BIO_puts(bio_err, "Error signing data\n");
|
||||
goto end;
|
||||
}
|
||||
print_out(out, sig, len, sep, binout, sig_name, NULL, file);
|
||||
ret = EXIT_SUCCESS;
|
||||
} else {
|
||||
BIO_puts(bio_err, "key must be set for one-shot algorithms\n");
|
||||
goto end;
|
||||
}
|
||||
#if defined(OPENSSL_SYS_UNIX) && defined(_POSIX_MAPPED_FILES) && _POSIX_MAPPED_FILES > 0
|
||||
if (file != NULL) {
|
||||
const unsigned char *data = NULL;
|
||||
size_t filesize = 0;
|
||||
int r = app_mmap_file(file, bio_err, (size_t)-1, &data, &filesize);
|
||||
|
||||
end:
|
||||
OPENSSL_free(sig);
|
||||
OPENSSL_clear_free(buf, buflen);
|
||||
if (r == 1) {
|
||||
ret = do_oneshot_verify_sign(ctx, out, sigin, siglen, key, data,
|
||||
filesize, sep, binout, sig_name, file)
|
||||
? EXIT_SUCCESS
|
||||
: EXIT_FAILURE;
|
||||
munmap((void *)data, filesize);
|
||||
return ret;
|
||||
}
|
||||
if (r == -1)
|
||||
return EXIT_FAILURE; /* error already printed */
|
||||
/* r == 0: empty file, fall through to buffer path */
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
const char *display_file = file != NULL ? file : "stdin";
|
||||
|
||||
if (!bio_to_mem(&buf, &buflen, maxlen, in))
|
||||
return EXIT_FAILURE;
|
||||
ret = do_oneshot_verify_sign(ctx, out, sigin, siglen, key, buf, buflen,
|
||||
sep, binout, sig_name, display_file)
|
||||
? EXIT_SUCCESS
|
||||
: EXIT_FAILURE;
|
||||
OPENSSL_clear_free(buf, buflen);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,16 @@
|
||||
#ifndef OSSL_APPS_H
|
||||
#define OSSL_APPS_H
|
||||
|
||||
#if defined(__linux__) || defined(__sun__) || defined(__hpux)
|
||||
/*
|
||||
* Allow open() and stat() to work with files larger than 2GB on 32-bit
|
||||
* systems. See crypto/o_fopen.c and crypto/bio/bss_file.c.
|
||||
*/
|
||||
#ifndef _FILE_OFFSET_BITS
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "internal/common.h" /* for HAS_PREFIX */
|
||||
#include "internal/nelem.h"
|
||||
#include <assert.h>
|
||||
@@ -22,6 +32,19 @@
|
||||
#endif
|
||||
|
||||
#include <openssl/e_os2.h>
|
||||
#if defined(OPENSSL_SYS_UNIX) && defined(_POSIX_MAPPED_FILES) && _POSIX_MAPPED_FILES > 0
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
/*
|
||||
* Map a file read-only into memory. Returns 1 on success (*out_data and
|
||||
* *out_size set; caller must munmap when done), 0 when file size is 0 (no
|
||||
* error, caller may use buffer path), or -1 on error (message printed to
|
||||
* bio_err). known_size: (size_t)-1 = stat to get size; 0 = do not map
|
||||
* (return 0); > 0 = use this size (caller obtained it from stat of same path).
|
||||
*/
|
||||
int app_mmap_file(const char *path, BIO *err_bio, size_t known_size,
|
||||
const unsigned char **out_data, size_t *out_size);
|
||||
#endif
|
||||
#include <openssl/types.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/x509.h>
|
||||
|
||||
@@ -2202,6 +2202,63 @@ int bio_to_mem(unsigned char **out, size_t *outlen, size_t maxlen, BIO *in)
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if defined(OPENSSL_SYS_UNIX) && defined(_POSIX_MAPPED_FILES) && _POSIX_MAPPED_FILES > 0
|
||||
int app_mmap_file(const char *path, BIO *err_bio, size_t known_size,
|
||||
const unsigned char **out_data, size_t *out_size)
|
||||
{
|
||||
struct stat st;
|
||||
size_t filesize;
|
||||
int fd;
|
||||
void *p;
|
||||
|
||||
*out_data = NULL;
|
||||
*out_size = 0;
|
||||
|
||||
if (known_size == 0)
|
||||
return 0;
|
||||
|
||||
if (known_size == (size_t)-1) {
|
||||
if (stat(path, &st) != 0 || st.st_size < 0) {
|
||||
BIO_printf(err_bio, "Error: failed to get size of file '%s'\n", path);
|
||||
return -1;
|
||||
}
|
||||
if (!S_ISREG(st.st_mode)) {
|
||||
/*
|
||||
* mmap() is only for regular files. Directories and other non-regular
|
||||
* paths can report st_size == 0; do not treat those like empty files
|
||||
* and fall back to the buffer path in callers.
|
||||
*/
|
||||
BIO_puts(err_bio, "Error: failed to use memory-mapped file\n");
|
||||
return -1;
|
||||
}
|
||||
filesize = (size_t)st.st_size;
|
||||
if ((off_t)filesize != st.st_size) {
|
||||
BIO_puts(err_bio, "Error: failed to convert file size, likely too big\n");
|
||||
return -1;
|
||||
}
|
||||
if (filesize == 0)
|
||||
return 0;
|
||||
} else {
|
||||
filesize = known_size;
|
||||
}
|
||||
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
BIO_puts(err_bio, "Error opening file for memory mapping\n");
|
||||
return -1;
|
||||
}
|
||||
p = mmap(NULL, filesize, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
(void)close(fd);
|
||||
if (p == MAP_FAILED) {
|
||||
BIO_puts(err_bio, "Error: failed to use memory-mapped file\n");
|
||||
return -1;
|
||||
}
|
||||
*out_data = (const unsigned char *)p;
|
||||
*out_size = filesize;
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
int pkey_ctrl_string(EVP_PKEY_CTX *ctx, const char *value)
|
||||
{
|
||||
int rv = 0;
|
||||
|
||||
+62
-18
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "apps.h"
|
||||
#include "progs.h"
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/pem.h>
|
||||
@@ -37,8 +38,8 @@ static int do_keyop(EVP_PKEY_CTX *ctx, int pkey_op,
|
||||
unsigned char *secret, size_t *psecretlen);
|
||||
|
||||
static int do_raw_keyop(int pkey_op, EVP_MD_CTX *mctx,
|
||||
EVP_PKEY *pkey, BIO *in,
|
||||
int filesize, unsigned char *sig, size_t siglen,
|
||||
EVP_PKEY *pkey, BIO *in, const char *infile,
|
||||
size_t filesize, unsigned char *sig, size_t siglen,
|
||||
unsigned char **out, size_t *poutlen);
|
||||
|
||||
static int only_nomd(EVP_PKEY *pkey)
|
||||
@@ -162,7 +163,7 @@ int pkeyutl_main(int argc, char **argv)
|
||||
int rawin = 0;
|
||||
EVP_MD_CTX *mctx = NULL;
|
||||
EVP_MD *md = NULL;
|
||||
int filesize = -1;
|
||||
size_t filesize = (size_t)-1; /* (size_t)-1 means unknown */
|
||||
OSSL_LIB_CTX *libctx = app_get0_libctx();
|
||||
|
||||
prog = opt_init(argc, argv, pkeyutl_options);
|
||||
@@ -454,8 +455,11 @@ int pkeyutl_main(int argc, char **argv)
|
||||
if (infile != NULL) {
|
||||
struct stat st;
|
||||
|
||||
if (stat(infile, &st) == 0 && st.st_size <= INT_MAX)
|
||||
filesize = (int)st.st_size;
|
||||
if (stat(infile, &st) == 0 && st.st_size >= 0) {
|
||||
filesize = (size_t)st.st_size;
|
||||
if ((off_t)filesize != st.st_size)
|
||||
filesize = (size_t)-1;
|
||||
}
|
||||
}
|
||||
if (in == NULL)
|
||||
goto end;
|
||||
@@ -539,7 +543,7 @@ int pkeyutl_main(int argc, char **argv)
|
||||
|
||||
if (pkey_op == EVP_PKEY_OP_VERIFY) {
|
||||
if (rawin) {
|
||||
rv = do_raw_keyop(pkey_op, mctx, pkey, in, filesize, sig, siglen,
|
||||
rv = do_raw_keyop(pkey_op, mctx, pkey, in, infile, filesize, sig, siglen,
|
||||
NULL, 0);
|
||||
} else {
|
||||
rv = EVP_PKEY_verify(ctx, sig, siglen, buf_in, buf_inlen);
|
||||
@@ -554,7 +558,7 @@ int pkeyutl_main(int argc, char **argv)
|
||||
}
|
||||
if (rawin) {
|
||||
/* rawin allocates the buffer in do_raw_keyop() */
|
||||
rv = do_raw_keyop(pkey_op, mctx, pkey, in, filesize, NULL, 0,
|
||||
rv = do_raw_keyop(pkey_op, mctx, pkey, in, infile, filesize, NULL, 0,
|
||||
&buf_out, &buf_outlen);
|
||||
} else {
|
||||
if (kdflen != 0) {
|
||||
@@ -821,8 +825,8 @@ static int do_keyop(EVP_PKEY_CTX *ctx, int pkey_op,
|
||||
#define TBUF_MAXSIZE 2048
|
||||
|
||||
static int do_raw_keyop(int pkey_op, EVP_MD_CTX *mctx,
|
||||
EVP_PKEY *pkey, BIO *in,
|
||||
int filesize, unsigned char *sig, size_t siglen,
|
||||
EVP_PKEY *pkey, BIO *in, const char *infile,
|
||||
size_t filesize, unsigned char *sig, size_t siglen,
|
||||
unsigned char **out, size_t *poutlen)
|
||||
{
|
||||
int rv = 0;
|
||||
@@ -832,32 +836,72 @@ static int do_raw_keyop(int pkey_op, EVP_MD_CTX *mctx,
|
||||
|
||||
/* Some algorithms only support oneshot digests */
|
||||
if (only_nomd(pkey)) {
|
||||
if (filesize < 0) {
|
||||
if (filesize == (size_t)-1) {
|
||||
BIO_printf(bio_err,
|
||||
"Error: unable to determine size of file '%s' for oneshot operation\n",
|
||||
infile);
|
||||
goto end;
|
||||
}
|
||||
#if defined(OPENSSL_SYS_UNIX) && defined(_POSIX_MAPPED_FILES) && _POSIX_MAPPED_FILES > 0
|
||||
if (infile != NULL) {
|
||||
struct stat st;
|
||||
|
||||
if (stat(infile, &st) == 0 && !S_ISREG(st.st_mode)) {
|
||||
BIO_puts(bio_err, "Error: failed to use memory-mapped file\n");
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
if (filesize > 0 && infile != NULL) {
|
||||
const unsigned char *data = NULL;
|
||||
size_t mapped_size = 0;
|
||||
|
||||
if (app_mmap_file(infile, bio_err, filesize, &data, &mapped_size) == 1) {
|
||||
switch (pkey_op) {
|
||||
case EVP_PKEY_OP_VERIFY:
|
||||
rv = EVP_DigestVerify(mctx, sig, siglen, data, mapped_size);
|
||||
break;
|
||||
case EVP_PKEY_OP_SIGN:
|
||||
rv = EVP_DigestSign(mctx, NULL, poutlen, data, mapped_size);
|
||||
if (rv == 1 && out != NULL) {
|
||||
*out = app_malloc(*poutlen, "buffer output");
|
||||
rv = EVP_DigestSign(mctx, *out, poutlen, data, mapped_size);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
munmap((void *)data, mapped_size);
|
||||
}
|
||||
/* Success or mmap failure: do not fall back to buffer path */
|
||||
goto end;
|
||||
}
|
||||
#endif
|
||||
if (filesize > INT_MAX) {
|
||||
BIO_puts(bio_err,
|
||||
"Error: unable to determine file size for oneshot operation\n");
|
||||
"Error: file too large for oneshot operation without memory mapping\n");
|
||||
goto end;
|
||||
}
|
||||
if (filesize > 0)
|
||||
mbuf = app_malloc(filesize, "oneshot sign/verify buffer");
|
||||
switch (pkey_op) {
|
||||
case EVP_PKEY_OP_VERIFY:
|
||||
buf_len = BIO_read(in, mbuf, filesize);
|
||||
if (buf_len != filesize) {
|
||||
buf_len = BIO_read(in, mbuf, (int)filesize);
|
||||
if (buf_len < 0 || (size_t)buf_len != filesize) {
|
||||
BIO_puts(bio_err, "Error reading raw input data\n");
|
||||
goto end;
|
||||
}
|
||||
rv = EVP_DigestVerify(mctx, sig, siglen, mbuf, buf_len);
|
||||
rv = EVP_DigestVerify(mctx, sig, siglen, mbuf, filesize);
|
||||
break;
|
||||
case EVP_PKEY_OP_SIGN:
|
||||
buf_len = BIO_read(in, mbuf, filesize);
|
||||
if (buf_len != filesize) {
|
||||
buf_len = BIO_read(in, mbuf, (int)filesize);
|
||||
if (buf_len < 0 || (size_t)buf_len != filesize) {
|
||||
BIO_puts(bio_err, "Error reading raw input data\n");
|
||||
goto end;
|
||||
}
|
||||
rv = EVP_DigestSign(mctx, NULL, poutlen, mbuf, buf_len);
|
||||
rv = EVP_DigestSign(mctx, NULL, poutlen, mbuf, filesize);
|
||||
if (rv == 1 && out != NULL) {
|
||||
*out = app_malloc(*poutlen, "buffer output");
|
||||
rv = EVP_DigestSign(mctx, *out, poutlen, mbuf, buf_len);
|
||||
rv = EVP_DigestSign(mctx, *out, poutlen, mbuf, filesize);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -118,10 +118,11 @@ Filename to output to, or standard output by default.
|
||||
Digitally sign the digest using the given private key.
|
||||
|
||||
Note that for algorithms that only support one-shot signing
|
||||
(such as Ed25519, ED448, ML-DSA-44, ML-DSA-65 andML-DSA-87) the digest must not
|
||||
(such as Ed25519, ED448, ML-DSA-44, ML-DSA-65 and ML-DSA-87) the digest must not
|
||||
be set. For these algorithms the input is buffered (and not digested) before
|
||||
signing. For these algorithms, if the input is larger than 16MB an error
|
||||
will occur.
|
||||
signing. When the input is from a file, memory-mapped I/O is used on
|
||||
supported platforms (Unix\-like), allowing large files without a size limit; when
|
||||
input is from stdin or on unsupported platforms, input is limited to 16MB.
|
||||
|
||||
=item B<-keyform> B<DER>|B<PEM>|B<P12>
|
||||
|
||||
@@ -143,6 +144,10 @@ see L<openssl-passphrase-options(1)>.
|
||||
|
||||
Verify the signature using the public key in "filename".
|
||||
The output is either "Verified OK" or "Verification Failure".
|
||||
For one-shot verification algorithms (e.g. Ed25519, Ed448), when the input
|
||||
is from a file, memory-mapped I/O is used on supported platforms (Unix\-like),
|
||||
allowing large files; when input is from stdin or on unsupported platforms,
|
||||
input is limited to 16MB.
|
||||
|
||||
=item B<-prverify> I<filename>
|
||||
|
||||
@@ -331,6 +336,11 @@ The B<-engine> and B<-engine_impl> options were removed in OpenSSL 4.0.
|
||||
|
||||
The B<-hmac-env> and B<-hmac-stdin> options were added in OpenSSL 4.0.
|
||||
|
||||
Since OpenSSL 4.1, one-shot sign and verify (e.g. Ed25519, Ed448) with input
|
||||
from a file uses memory-mapped I/O on supported platforms (Unix\-like), allowing
|
||||
large files to be processed without the previous 16MB limit for file-based
|
||||
input.
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright 2000-2025 The OpenSSL Project Authors. All Rights Reserved.
|
||||
|
||||
@@ -77,6 +77,10 @@ is implied since OpenSSL 3.5, and required in earlier versions.
|
||||
|
||||
The B<-digest> option implies B<-rawin> since OpenSSL 3.5.
|
||||
|
||||
When the input is read from a file (B<-in> I<filename>), the command may
|
||||
use memory-mapped I/O on supported platforms for better performance and
|
||||
to handle large files without loading the entire file into memory.
|
||||
|
||||
=item B<-digest> I<algorithm>
|
||||
|
||||
This option can only be used with B<-sign> and B<-verify>.
|
||||
@@ -681,6 +685,11 @@ the supported algorithms, the only supported B<mode> is now the default.
|
||||
|
||||
The B<-engine> option was removed in OpenSSL 4.0.
|
||||
|
||||
Since OpenSSL 4.1, when reading raw input from a file (B<-in> I<filename>) for
|
||||
oneshot sign/verify (such as Ed25519, Ed448, and ML-DSA), the command uses
|
||||
memory-mapped I/O on supported platforms, allowing large files to be processed
|
||||
without loading the entire file into memory.
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright 2006-2025 The OpenSSL Project Authors. All Rights Reserved.
|
||||
|
||||
+173
-115
@@ -12,13 +12,13 @@ use warnings;
|
||||
|
||||
use File::Spec;
|
||||
use File::Basename;
|
||||
use OpenSSL::Test qw/:DEFAULT with srctop_file data_file bldtop_dir/;
|
||||
use OpenSSL::Test qw/:DEFAULT with srctop_file srctop_dir data_file bldtop_dir/;
|
||||
use OpenSSL::Test::Utils;
|
||||
use Cwd qw(abs_path);
|
||||
|
||||
setup("test_dgst");
|
||||
|
||||
plan tests => 24;
|
||||
plan tests => 25;
|
||||
|
||||
sub tsignverify {
|
||||
my $testtext = shift;
|
||||
@@ -89,91 +89,143 @@ sub tsignverify_sha512 {
|
||||
$testtext.": Expect failure verifying mismatching data");
|
||||
}
|
||||
|
||||
SKIP: {
|
||||
skip "RSA is not supported by this OpenSSL build", 1
|
||||
if disabled("rsa");
|
||||
|
||||
subtest "RSA signature generation and verification with `dgst` CLI" => sub {
|
||||
tsignverify("RSA",
|
||||
srctop_file("test","testrsa.pem"),
|
||||
srctop_file("test","testrsapub.pem"));
|
||||
};
|
||||
|
||||
subtest "RSA signature generation and verification with `sha512` CLI" => sub {
|
||||
tsignverify_sha512("RSA",
|
||||
srctop_file("test","testrsa2048.pem"),
|
||||
srctop_file("test","testrsa2048pub.pem"));
|
||||
};
|
||||
}
|
||||
|
||||
SKIP: {
|
||||
skip "DSA is not supported by this OpenSSL build", 1
|
||||
if disabled("dsa");
|
||||
|
||||
subtest "DSA signature generation and verification with `dgst` CLI" => sub {
|
||||
tsignverify("DSA",
|
||||
srctop_file("test","testdsa.pem"),
|
||||
srctop_file("test","testdsapub.pem"));
|
||||
};
|
||||
}
|
||||
|
||||
SKIP: {
|
||||
skip "ECDSA is not supported by this OpenSSL build", 1
|
||||
if disabled("ec");
|
||||
|
||||
subtest "ECDSA signature generation and verification with `dgst` CLI" => sub {
|
||||
tsignverify("ECDSA",
|
||||
srctop_file("test","testec-p256.pem"),
|
||||
srctop_file("test","testecpub-p256.pem"));
|
||||
};
|
||||
}
|
||||
|
||||
SKIP: {
|
||||
skip "EdDSA is not supported by this OpenSSL build", 2
|
||||
if disabled("ecx");
|
||||
|
||||
subtest "Ed25519 signature generation and verification with `dgst` CLI" => sub {
|
||||
tsignverify("Ed25519",
|
||||
srctop_file("test","tested25519.pem"),
|
||||
srctop_file("test","tested25519pub.pem"));
|
||||
};
|
||||
|
||||
subtest "Ed448 signature generation and verification with `dgst` CLI" => sub {
|
||||
tsignverify("Ed448",
|
||||
srctop_file("test","tested448.pem"),
|
||||
srctop_file("test","tested448pub.pem"));
|
||||
};
|
||||
}
|
||||
|
||||
SKIP: {
|
||||
skip "ML-DSA is not supported by this OpenSSL build", 3
|
||||
if disabled("ml-dsa");
|
||||
|
||||
subtest "ML-DSA-44 signature generation and verification with `dgst` CLI" => sub {
|
||||
tsignverify("Ml-DSA-44",
|
||||
srctop_file("test","testmldsa44.pem"),
|
||||
srctop_file("test","testmldsa44pub.pem"));
|
||||
};
|
||||
subtest "ML-DSA-65 signature generation and verification with `dgst` CLI" => sub {
|
||||
tsignverify("Ml-DSA-65",
|
||||
srctop_file("test","testmldsa65.pem"),
|
||||
srctop_file("test","testmldsa65pub.pem"));
|
||||
};
|
||||
subtest "ML-DSA-87 signature generation and verification with `dgst` CLI" => sub {
|
||||
tsignverify("Ml-DSA-87",
|
||||
srctop_file("test","testmldsa87.pem"),
|
||||
srctop_file("test","testmldsa87pub.pem"));
|
||||
};
|
||||
}
|
||||
|
||||
SKIP: {
|
||||
skip "dgst with provider is not supported by this OpenSSL build", 1
|
||||
if disabled("module");
|
||||
|
||||
subtest "SHA1 generation by provider with `dgst` CLI" => sub {
|
||||
subtest "RSA signature generation and verification with `dgst` CLI" => sub {
|
||||
if (disabled("rsa")) {
|
||||
plan tests => 1;
|
||||
ok(1, "Skipped (RSA not supported)");
|
||||
return;
|
||||
}
|
||||
tsignverify("RSA",
|
||||
srctop_file("test","testrsa.pem"),
|
||||
srctop_file("test","testrsapub.pem"));
|
||||
};
|
||||
|
||||
$ENV{OPENSSL_MODULES} = abs_path(bldtop_dir("test"));
|
||||
subtest "RSA signature generation and verification with `sha512` CLI" => sub {
|
||||
if (disabled("rsa")) {
|
||||
plan tests => 1;
|
||||
ok(1, "Skipped (RSA not supported)");
|
||||
return;
|
||||
}
|
||||
tsignverify_sha512("RSA",
|
||||
srctop_file("test","testrsa2048.pem"),
|
||||
srctop_file("test","testrsa2048pub.pem"));
|
||||
};
|
||||
|
||||
subtest "DSA signature generation and verification with `dgst` CLI" => sub {
|
||||
if (disabled("dsa")) {
|
||||
plan tests => 1;
|
||||
ok(1, "Skipped (DSA not supported)");
|
||||
return;
|
||||
}
|
||||
tsignverify("DSA",
|
||||
srctop_file("test","testdsa.pem"),
|
||||
srctop_file("test","testdsapub.pem"));
|
||||
};
|
||||
|
||||
subtest "ECDSA signature generation and verification with `dgst` CLI" => sub {
|
||||
if (disabled("ec")) {
|
||||
plan tests => 1;
|
||||
ok(1, "Skipped (ECDSA not supported)");
|
||||
return;
|
||||
}
|
||||
tsignverify("ECDSA",
|
||||
srctop_file("test","testec-p256.pem"),
|
||||
srctop_file("test","testecpub-p256.pem"));
|
||||
};
|
||||
|
||||
subtest "Ed25519 signature generation and verification with `dgst` CLI" => sub {
|
||||
if (disabled("ecx")) {
|
||||
plan tests => 1;
|
||||
ok(1, "Skipped (EdDSA not supported)");
|
||||
return;
|
||||
}
|
||||
tsignverify("Ed25519",
|
||||
srctop_file("test","tested25519.pem"),
|
||||
srctop_file("test","tested25519pub.pem"));
|
||||
};
|
||||
|
||||
subtest "Ed448 signature generation and verification with `dgst` CLI" => sub {
|
||||
if (disabled("ecx")) {
|
||||
plan tests => 1;
|
||||
ok(1, "Skipped (EdDSA not supported)");
|
||||
return;
|
||||
}
|
||||
tsignverify("Ed448",
|
||||
srctop_file("test","tested448.pem"),
|
||||
srctop_file("test","tested448pub.pem"));
|
||||
};
|
||||
|
||||
subtest "dgst one-shot: no buffer fallback when mmap path fails (Unix)" => sub {
|
||||
if ($^O eq 'MSWin32' || disabled("ecx")) {
|
||||
plan tests => 1;
|
||||
ok(1, "Skipped (Unix/mmap or EdDSA not available)");
|
||||
return;
|
||||
}
|
||||
plan tests => 2;
|
||||
|
||||
# Use a directory with non-zero st_size so app_mmap_file() attempts open+mmap
|
||||
# (curdir "." often has st_size 0 on some FS, which skips mmap and breaks this test).
|
||||
# mmap() on a directory must fail; we must not fall back to bio_to_mem.
|
||||
my $key = srctop_file("test", "tested25519.pem");
|
||||
my $dir = srctop_dir("test");
|
||||
my $stderr_file = "dgst_nofallback_err.txt";
|
||||
|
||||
with({ exit_checker => sub { return shift != 0; } },
|
||||
sub {
|
||||
ok(run(app(['openssl', 'dgst', '-sign', $key, $dir],
|
||||
stderr => $stderr_file)),
|
||||
"dgst one-shot with un-mmapable file fails (no fallback)");
|
||||
});
|
||||
if (open(my $fh, '<', $stderr_file)) {
|
||||
my $err = do { local $/; <$fh> };
|
||||
close($fh);
|
||||
ok($err =~ /Error: failed to use memory-mapped file/, "stderr mentions mmap failure");
|
||||
} else {
|
||||
ok(0, "could not read stderr file");
|
||||
}
|
||||
unlink($stderr_file) if -f $stderr_file;
|
||||
};
|
||||
|
||||
subtest "ML-DSA-44 signature generation and verification with `dgst` CLI" => sub {
|
||||
if (disabled("ml-dsa")) {
|
||||
plan tests => 1;
|
||||
ok(1, "Skipped (ML-DSA not supported)");
|
||||
return;
|
||||
}
|
||||
tsignverify("Ml-DSA-44",
|
||||
srctop_file("test","testmldsa44.pem"),
|
||||
srctop_file("test","testmldsa44pub.pem"));
|
||||
};
|
||||
subtest "ML-DSA-65 signature generation and verification with `dgst` CLI" => sub {
|
||||
if (disabled("ml-dsa")) {
|
||||
plan tests => 1;
|
||||
ok(1, "Skipped (ML-DSA not supported)");
|
||||
return;
|
||||
}
|
||||
tsignverify("Ml-DSA-65",
|
||||
srctop_file("test","testmldsa65.pem"),
|
||||
srctop_file("test","testmldsa65pub.pem"));
|
||||
};
|
||||
subtest "ML-DSA-87 signature generation and verification with `dgst` CLI" => sub {
|
||||
if (disabled("ml-dsa")) {
|
||||
plan tests => 1;
|
||||
ok(1, "Skipped (ML-DSA not supported)");
|
||||
return;
|
||||
}
|
||||
tsignverify("Ml-DSA-87",
|
||||
srctop_file("test","testmldsa87.pem"),
|
||||
srctop_file("test","testmldsa87pub.pem"));
|
||||
};
|
||||
|
||||
subtest "SHA1 generation by provider with `dgst` CLI" => sub {
|
||||
if (disabled("module")) {
|
||||
plan tests => 1;
|
||||
ok(1, "Skipped (dgst with provider not supported)");
|
||||
return;
|
||||
}
|
||||
plan tests => 1;
|
||||
|
||||
$ENV{OPENSSL_MODULES} = abs_path(bldtop_dir("test"));
|
||||
my $testdata = srctop_file('test', 'data.bin');
|
||||
my @macdata = run(app(['openssl', 'dgst', '-sha1',
|
||||
'-provider', "p_ossltest",
|
||||
@@ -183,8 +235,7 @@ SKIP: {
|
||||
chomp(@macdata);
|
||||
my $expected = qr/SHA1\(\Q$testdata\E\)= 000102030405060708090a0b0c0d0e0f10111213/;
|
||||
ok($macdata[0] =~ $expected, "SHA1: Check HASH value is as expected ($macdata[0]) vs ($expected)");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
subtest "HMAC generation with `dgst` CLI" => sub {
|
||||
plan tests => 2;
|
||||
@@ -357,33 +408,40 @@ subtest "SHAKE digest generation with no xoflen set `dgst` CLI" => sub {
|
||||
ok(!run(app(['openssl', 'dgst', '-shake256', $testdata])), "SHAKE256 must fail without xoflen");
|
||||
};
|
||||
|
||||
SKIP: {
|
||||
skip "ECDSA is not supported by this OpenSSL build", 2
|
||||
if disabled("ec");
|
||||
|
||||
subtest "signing with xoflen is not supported `dgst` CLI" => sub {
|
||||
subtest "signing with xoflen is not supported `dgst` CLI" => sub {
|
||||
if (disabled("ec")) {
|
||||
plan tests => 1;
|
||||
my $data_to_sign = srctop_file('test', 'data.bin');
|
||||
|
||||
ok(!run(app(['openssl', 'dgst', '-shake256', '-xoflen', '64',
|
||||
'-sign', srctop_file("test","testec-p256.pem"),
|
||||
'-out', 'test.sig',
|
||||
srctop_file('test', 'data.bin')])),
|
||||
"Generating signature with xoflen should fail");
|
||||
};
|
||||
|
||||
skip "HMAC-DRBG-KDF is not supported by this OpenSSL build", 1
|
||||
if disabled("hmac-drbg-kdf");
|
||||
|
||||
subtest "signing using the nonce-type sigopt" => sub {
|
||||
plan tests => 1;
|
||||
my $data_to_sign = srctop_file('test', 'data.bin');
|
||||
|
||||
ok(run(app(['openssl', 'dgst', '-sha256',
|
||||
'-sign', srctop_file("test","testec-p256.pem"),
|
||||
'-out', 'test.sig',
|
||||
'-sigopt', 'nonce-type:1',
|
||||
srctop_file('test', 'data.bin')])),
|
||||
"Sign using the nonce-type sigopt");
|
||||
ok(1, "Skipped (ECDSA not supported)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
plan tests => 1;
|
||||
my $data_to_sign = srctop_file('test', 'data.bin');
|
||||
|
||||
ok(!run(app(['openssl', 'dgst', '-shake256', '-xoflen', '64',
|
||||
'-sign', srctop_file("test","testec-p256.pem"),
|
||||
'-out', 'test.sig',
|
||||
srctop_file('test', 'data.bin')])),
|
||||
"Generating signature with xoflen should fail");
|
||||
};
|
||||
|
||||
subtest "signing using the nonce-type sigopt" => sub {
|
||||
if (disabled("ec")) {
|
||||
plan tests => 1;
|
||||
ok(1, "Skipped (ECDSA not supported)");
|
||||
return;
|
||||
}
|
||||
if (disabled("hmac-drbg-kdf")) {
|
||||
plan tests => 1;
|
||||
ok(1, "Skipped (HMAC-DRBG-KDF not supported)");
|
||||
return;
|
||||
}
|
||||
plan tests => 1;
|
||||
my $data_to_sign = srctop_file('test', 'data.bin');
|
||||
|
||||
ok(run(app(['openssl', 'dgst', '-sha256',
|
||||
'-sign', srctop_file("test","testec-p256.pem"),
|
||||
'-out', 'test.sig',
|
||||
'-sigopt', 'nonce-type:1',
|
||||
srctop_file('test', 'data.bin')])),
|
||||
"Sign using the nonce-type sigopt");
|
||||
};
|
||||
|
||||
@@ -11,13 +11,13 @@ use warnings;
|
||||
|
||||
use File::Spec;
|
||||
use File::Basename;
|
||||
use OpenSSL::Test qw/:DEFAULT srctop_file ok_nofips with/;
|
||||
use OpenSSL::Test qw/:DEFAULT srctop_file srctop_dir ok_nofips with/;
|
||||
use OpenSSL::Test::Utils;
|
||||
use File::Compare qw/compare_text compare/;
|
||||
|
||||
setup("test_pkeyutl");
|
||||
|
||||
plan tests => 27;
|
||||
plan tests => 29;
|
||||
|
||||
# For the tests below we use the cert itself as the TBS file
|
||||
|
||||
@@ -214,9 +214,65 @@ SKIP: {
|
||||
}
|
||||
|
||||
SKIP: {
|
||||
skip "EdDSA is not supported by this OpenSSL build", 4
|
||||
skip "EdDSA is not supported by this OpenSSL build", 6
|
||||
if disabled("ecx");
|
||||
|
||||
subtest "pkeyutl -rawin oneshot with file input (mmap or buffer path)" => sub {
|
||||
my $data = srctop_file("test", "data.bin");
|
||||
my $ed25519_key = srctop_file("test", "tested25519.pem");
|
||||
my $ed25519_pub = srctop_file("test", "tested25519pub.pem");
|
||||
my $ed448_key = srctop_file("test", "tested448.pem");
|
||||
my $ed448_pub = srctop_file("test", "tested448pub.pem");
|
||||
|
||||
plan tests => 4;
|
||||
|
||||
# -in <file> for oneshot: uses mmap on Unix when supported, else buffer+BIO_read
|
||||
ok(run(app(['openssl', 'pkeyutl', '-sign', '-rawin', '-inkey', $ed25519_key,
|
||||
'-in', $data, '-out', 'rawin_file_ed25519.sig'])),
|
||||
"Ed25519 -rawin sign from file");
|
||||
ok(run(app(['openssl', 'pkeyutl', '-verify', '-rawin', '-pubin', '-inkey', $ed25519_pub,
|
||||
'-sigfile', 'rawin_file_ed25519.sig', '-in', $data])),
|
||||
"Ed25519 -rawin verify from file");
|
||||
ok(run(app(['openssl', 'pkeyutl', '-sign', '-rawin', '-inkey', $ed448_key,
|
||||
'-in', $data, '-out', 'rawin_file_ed448.sig'])),
|
||||
"Ed448 -rawin sign from file");
|
||||
ok(run(app(['openssl', 'pkeyutl', '-verify', '-rawin', '-pubin', '-inkey', $ed448_pub,
|
||||
'-sigfile', 'rawin_file_ed448.sig', '-in', $data])),
|
||||
"Ed448 -rawin verify from file");
|
||||
};
|
||||
|
||||
subtest "pkeyutl -rawin oneshot: no buffer fallback when mmap path fails (Unix)" => sub {
|
||||
if ($^O eq 'MSWin32') {
|
||||
plan tests => 1;
|
||||
ok(1, "Skipped (Unix/mmap only)");
|
||||
return;
|
||||
}
|
||||
plan tests => 2;
|
||||
|
||||
# Use a directory with non-zero st_size so the mmap path is attempted
|
||||
# (curdir "." often has st_size 0 on some FS and skips mmap).
|
||||
my $ed25519_key = srctop_file("test", "tested25519.pem");
|
||||
my $dir = srctop_dir("test");
|
||||
my $stderr_file = "pkeyutl_nofallback_err.txt";
|
||||
|
||||
with({ exit_checker => sub { return shift != 0; } },
|
||||
sub {
|
||||
ok(run(app(['openssl', 'pkeyutl', '-sign', '-rawin', '-inkey', $ed25519_key,
|
||||
'-in', $dir, '-out', 'nofallback.sig'],
|
||||
stderr => $stderr_file)),
|
||||
"pkeyutl -rawin with un-mmapable input fails (no fallback)");
|
||||
});
|
||||
if (open(my $fh, '<', $stderr_file)) {
|
||||
my $err = do { local $/; <$fh> };
|
||||
close($fh);
|
||||
ok($err =~ /Error(?: opening file for memory mapping|: failed to use memory-mapped file)/,
|
||||
"stderr mentions mmap failure");
|
||||
} else {
|
||||
ok(0, "could not read stderr file");
|
||||
}
|
||||
unlink($stderr_file) if -f $stderr_file;
|
||||
};
|
||||
|
||||
subtest "Ed2559 CLI signature generation and verification" => sub {
|
||||
tsignverify("Ed25519",
|
||||
srctop_file("test","tested25519.pem"),
|
||||
|
||||
Reference in New Issue
Block a user