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:
herbenderbler
2026-03-25 00:49:06 -06:00
committed by Tomas Mraz
parent b31d15ba6a
commit 80b7e49c27
9 changed files with 479 additions and 174 deletions
+11
View File
@@ -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
View File
@@ -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;
}
+23
View File
@@ -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>
+57
View File
@@ -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
View File
@@ -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;
}
+13 -3
View File
@@ -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.
+9
View File
@@ -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
View File
@@ -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");
};
+59 -3
View File
@@ -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"),