### 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*
* 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>
#include <openssl/pem.h>
#include <openssl/hmac.h>
#include <ctype.h>
+#include <sys/stat.h>
#undef BUFSIZE
#define BUFSIZE 1024 * 8
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");
}
/*
- * Some new algorithms only support one shot operations.
- * For these we need to buffer all input and then do the sign on the
- * total buffered input. These algorithms set a NULL digest name which is
- * then used inside EVP_DigestVerify() and EVP_DigestSign().
+ * Perform one-shot verify or sign on a contiguous data buffer.
+ * Returns 0 on failure, 1 on success.
*/
-static int do_fp_oneshot_sign(BIO *out, EVP_MD_CTX *ctx, BIO *in, int sep, int binout,
- EVP_PKEY *key, unsigned char *sigin, int siglen,
- const char *sig_name, const char *file)
+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, ret = EXIT_FAILURE;
- size_t len = 0;
- size_t buflen = 0;
- size_t maxlen = 16 * 1024 * 1024;
- uint8_t *buf = NULL, *sig = NULL;
+ int res;
+ size_t siglen_out = 0;
+ unsigned char *sig = 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);
+ res = EVP_DigestVerify(ctx, sigin, siglen, data, len);
print_verify_result(out, res);
- if (res > 0)
- ret = EXIT_SUCCESS;
- goto end;
+ return res > 0;
}
if (key != NULL) {
- if (EVP_DigestSign(ctx, NULL, &len, buf, buflen) != 1) {
+ if (EVP_DigestSign(ctx, NULL, &siglen_out, data, len) != 1) {
BIO_puts(bio_err, "Error getting maximum length of signed data\n");
- goto end;
+ return 0;
}
- sig = app_malloc(len, "Signature buffer");
- if (EVP_DigestSign(ctx, sig, &len, buf, buflen) != 1) {
+ 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");
- goto end;
+ OPENSSL_free(sig);
+ return 0;
}
- 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;
+ 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;
+}
-end:
- OPENSSL_free(sig);
- OPENSSL_clear_free(buf, buflen);
+/*
+ * Some new algorithms only support one shot operations.
+ * For these we need to buffer all input and then do the sign on the
+ * total buffered input. These algorithms set a NULL digest name which is
+ * then used inside EVP_DigestVerify() and EVP_DigestSign().
+ */
+static int do_fp_oneshot_sign(BIO *out, EVP_MD_CTX *ctx, BIO *in, int sep, int binout,
+ EVP_PKEY *key, unsigned char *sigin, int siglen,
+ const char *sig_name, const char *file)
+{
+ int ret = EXIT_FAILURE;
+ size_t buflen = 0;
+ size_t maxlen = 16 * 1024 * 1024;
+ uint8_t *buf = NULL;
+
+#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);
+
+ 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;
}
#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>
#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>
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;
#include "apps.h"
#include "progs.h"
+#include <limits.h>
#include <string.h>
#include <openssl/err.h>
#include <openssl/pem.h>
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)
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);
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;
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);
}
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) {
#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;
/* 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;
}
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>
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>
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.
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>.
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.
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;
$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"));
- };
-}
+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"));
+};
-SKIP: {
- skip "DSA is not supported by this OpenSSL build", 1
- if disabled("dsa");
+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 {
- tsignverify("DSA",
- srctop_file("test","testdsa.pem"),
- srctop_file("test","testdsapub.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"));
+};
-SKIP: {
- skip "ECDSA is not supported by this OpenSSL build", 1
- if disabled("ec");
+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 "ECDSA signature generation and verification with `dgst` CLI" => sub {
- 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"));
+};
-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"));
- };
-}
+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"));
+};
-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"));
- };
-}
+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;
-SKIP: {
- skip "dgst with provider is not supported by this OpenSSL build", 1
- if disabled("module");
+ # 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 "SHA1 generation by provider with `dgst` CLI" => sub {
+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"));
+ $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",
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;
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");
- };
+ ok(1, "Skipped (ECDSA not supported)");
+ return;
+ }
+ plan tests => 1;
+ my $data_to_sign = srctop_file('test', 'data.bin');
- skip "HMAC-DRBG-KDF is not supported by this OpenSSL build", 1
- if disabled("hmac-drbg-kdf");
+ 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 {
+subtest "signing using the nonce-type sigopt" => sub {
+ if (disabled("ec")) {
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;
}
-}
+ 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");
+};
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
}
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"),