#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/evp.h>
+#ifndef OPENSSL_NO_POSIX_IO
+# include <sys/stat.h>
+#endif
#define KEY_NONE 0
#define KEY_PRIVKEY 1
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, EVP_PKEY **ppkey);
+ const int impl, int rawin, EVP_PKEY **ppkey);
static int setup_peer(EVP_PKEY_CTX *ctx, int peerform, const char *file,
ENGINE *e);
static int do_raw_keyop(int pkey_op, EVP_PKEY_CTX *ctx,
const EVP_MD *md, EVP_PKEY *pkey, BIO *in,
- unsigned char *sig, int siglen,
+ int filesize, unsigned char *sig, int siglen,
unsigned char **out, size_t *poutlen);
typedef enum OPTION_choice {
STACK_OF(OPENSSL_STRING) *pkeyopts_passin = NULL;
int rawin = 0;
const EVP_MD *md = NULL;
+ int filesize = -1;
prog = opt_init(argc, argv, pkeyutl_options);
while ((o = opt_next()) != OPT_EOF) {
goto opthelp;
}
ctx = init_ctx(kdfalg, &keysize, inkey, keyform, key_type,
- passinarg, pkey_op, e, engine_impl, &pkey);
+ passinarg, pkey_op, e, engine_impl, rawin, &pkey);
if (ctx == NULL) {
BIO_printf(bio_err, "%s: Error initializing context\n", prog);
ERR_print_errors(bio_err);
if (pkey_op != EVP_PKEY_OP_DERIVE) {
in = bio_open_default(infile, 'r', FORMAT_BINARY);
+#ifndef OPENSSL_NO_POSIX_IO
+ if (infile != NULL)
+ {
+ struct stat st;
+
+ if (stat(infile, &st) == 0 && st.st_size <= INT_MAX)
+ filesize = (int)st.st_size;
+ }
+#endif
if (in == NULL)
goto end;
}
if (pkey_op == EVP_PKEY_OP_VERIFY) {
if (rawin) {
- rv = do_raw_keyop(pkey_op, ctx, md, pkey, in, sig, siglen,
+ rv = do_raw_keyop(pkey_op, ctx, md, pkey, in, filesize, sig, siglen,
NULL, 0);
} else {
rv = EVP_PKEY_verify(ctx, sig, (size_t)siglen,
} else {
if (rawin) {
/* rawin allocates the buffer in do_raw_keyop() */
- rv = do_raw_keyop(pkey_op, ctx, md, pkey, in, NULL, 0,
+ rv = do_raw_keyop(pkey_op, ctx, md, pkey, in, filesize, NULL, 0,
&buf_out, (size_t *)&buf_outlen);
} else {
rv = do_keyop(ctx, pkey_op, NULL, (size_t *)&buf_outlen,
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, EVP_PKEY **ppkey)
+ const int engine_impl, int rawin,
+ EVP_PKEY **ppkey)
{
EVP_PKEY *pkey = NULL;
EVP_PKEY_CTX *ctx = NULL;
if (ctx == NULL)
goto end;
- switch (pkey_op) {
- case EVP_PKEY_OP_SIGN:
- rv = EVP_PKEY_sign_init(ctx);
- break;
+ /*
+ * If rawin then we don't need to actually initialise the EVP_PKEY_CTX
+ * itself. That will get initialised during EVP_DigestSignInit or
+ * EVP_DigestVerifyInit.
+ */
+ if (rawin) {
+ rv = 1;
+ } else {
+ switch (pkey_op) {
+ case EVP_PKEY_OP_SIGN:
+ rv = EVP_PKEY_sign_init(ctx);
+ break;
- case EVP_PKEY_OP_VERIFY:
- rv = EVP_PKEY_verify_init(ctx);
- break;
+ case EVP_PKEY_OP_VERIFY:
+ rv = EVP_PKEY_verify_init(ctx);
+ break;
- case EVP_PKEY_OP_VERIFYRECOVER:
- rv = EVP_PKEY_verify_recover_init(ctx);
- break;
+ case EVP_PKEY_OP_VERIFYRECOVER:
+ rv = EVP_PKEY_verify_recover_init(ctx);
+ break;
- case EVP_PKEY_OP_ENCRYPT:
- rv = EVP_PKEY_encrypt_init(ctx);
- break;
+ case EVP_PKEY_OP_ENCRYPT:
+ rv = EVP_PKEY_encrypt_init(ctx);
+ break;
- case EVP_PKEY_OP_DECRYPT:
- rv = EVP_PKEY_decrypt_init(ctx);
- break;
+ case EVP_PKEY_OP_DECRYPT:
+ rv = EVP_PKEY_decrypt_init(ctx);
+ break;
- case EVP_PKEY_OP_DERIVE:
- rv = EVP_PKEY_derive_init(ctx);
- break;
+ case EVP_PKEY_OP_DERIVE:
+ rv = EVP_PKEY_derive_init(ctx);
+ break;
+ }
}
if (rv <= 0) {
static int do_raw_keyop(int pkey_op, EVP_PKEY_CTX *ctx,
const EVP_MD *md, EVP_PKEY *pkey, BIO *in,
- unsigned char *sig, int siglen,
+ int filesize, unsigned char *sig, int siglen,
unsigned char **out, size_t *poutlen)
{
int rv = 0;
EVP_MD_CTX *mctx = NULL;
unsigned char tbuf[TBUF_MAXSIZE];
- int tbuf_len = 0;
+ unsigned char *mbuf = NULL;
+ int buf_len = 0;
if ((mctx = EVP_MD_CTX_new()) == NULL) {
BIO_printf(bio_err, "Error: out of memory\n");
}
EVP_MD_CTX_set_pkey_ctx(mctx, ctx);
+ /* Some algorithms only support oneshot digests */
+ if (EVP_PKEY_id(pkey) == EVP_PKEY_ED25519
+ || EVP_PKEY_id(pkey) == EVP_PKEY_ED448) {
+ if (filesize < 0) {
+ BIO_printf(bio_err,
+ "Error: unable to determine file size for oneshot operation\n");
+ return rv;
+ }
+ mbuf = app_malloc(filesize, "oneshot sign/verify buffer");
+ switch(pkey_op) {
+ case EVP_PKEY_OP_VERIFY:
+ if (EVP_DigestVerifyInit(mctx, NULL, md, NULL, pkey) != 1)
+ goto end;
+ buf_len = BIO_read(in, mbuf, filesize);
+ if (buf_len != filesize) {
+ BIO_printf(bio_err, "Error reading raw input data\n");
+ goto end;
+ }
+ rv = EVP_DigestVerify(mctx, sig, (size_t)siglen, mbuf, buf_len);
+ break;
+ case EVP_PKEY_OP_SIGN:
+ if (EVP_DigestSignInit(mctx, NULL, md, NULL, pkey) != 1)
+ goto end;
+ buf_len = BIO_read(in, mbuf, filesize);
+ if (buf_len != filesize) {
+ BIO_printf(bio_err, "Error reading raw input data\n");
+ goto end;
+ }
+ rv = EVP_DigestSign(mctx, NULL, poutlen, mbuf, buf_len);
+ if (rv == 1 && out != NULL) {
+ *out = app_malloc(*poutlen, "buffer output");
+ rv = EVP_DigestSign(mctx, *out, poutlen, mbuf, buf_len);
+ }
+ break;
+ }
+ OPENSSL_free(mbuf);
+ goto end;
+ }
+
switch(pkey_op) {
case EVP_PKEY_OP_VERIFY:
if (EVP_DigestVerifyInit(mctx, NULL, md, NULL, pkey) != 1)
goto end;
for (;;) {
- tbuf_len = BIO_read(in, tbuf, TBUF_MAXSIZE);
- if (tbuf_len == 0)
+ buf_len = BIO_read(in, tbuf, TBUF_MAXSIZE);
+ if (buf_len == 0)
break;
- if (tbuf_len < 0) {
+ if (buf_len < 0) {
BIO_printf(bio_err, "Error reading raw input data\n");
goto end;
}
- rv = EVP_DigestVerifyUpdate(mctx, tbuf, (size_t)tbuf_len);
+ rv = EVP_DigestVerifyUpdate(mctx, tbuf, (size_t)buf_len);
if (rv != 1) {
BIO_printf(bio_err, "Error verifying raw input data\n");
goto end;
if (EVP_DigestSignInit(mctx, NULL, md, NULL, pkey) != 1)
goto end;
for (;;) {
- tbuf_len = BIO_read(in, tbuf, TBUF_MAXSIZE);
- if (tbuf_len == 0)
+ buf_len = BIO_read(in, tbuf, TBUF_MAXSIZE);
+ if (buf_len == 0)
break;
- if (tbuf_len < 0) {
+ if (buf_len < 0) {
BIO_printf(bio_err, "Error reading raw input data\n");
goto end;
}
- rv = EVP_DigestSignUpdate(mctx, tbuf, (size_t)tbuf_len);
+ rv = EVP_DigestSignUpdate(mctx, tbuf, (size_t)buf_len);
if (rv != 1) {
BIO_printf(bio_err, "Error signing raw input data\n");
goto end;
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>.
+B<-verify> and must be used with the Ed25519 and Ed448 algorithms.
=item B<-digest algorithm>
lengths of data passed in to the B<pkeyutl> and for creating the structures that
make up the signature (e.g. B<DigestInfo> in RSASSA PKCS#1 v1.5 signatures).
-This utility does not hash the input data but rather it will use the data
-directly as input to the signature algorithm. Depending on the key type,
-signature type, and mode of padding, the maximum acceptable lengths of input
-data differ. The signed data can't be longer than the key modulus with RSA. In
-case of ECDSA and DSA the data shouldn't be longer than the field
-size, otherwise it will be silently truncated to the field size. In any event
-the input size must not be larger than the largest supported digest size.
+This utility does not hash the input data (except where -rawin is used) but
+rather it will use the data directly as input to the signature algorithm.
+Depending on the key type, signature type, and mode of padding, the maximum
+acceptable lengths of input data differ. The signed data can't be longer than
+the key modulus with RSA. In case of ECDSA and DSA the data shouldn't be longer
+than the field size, otherwise it will be silently truncated to the field size.
+In any event the input size must not be larger than the largest supported digest
+size.
In other words, if the value of digest is B<sha1> the input should be the 20
bytes long binary encoding of the SHA-1 hash function output.
-The Ed25519 and Ed448 signature algorithms are not supported by this utility.
-They accept non-hashed input, but this utility can only be used to sign hashed
-input.
-
=head1 RSA ALGORITHM
The RSA algorithm generally supports the encrypt, decrypt, sign,
The X25519 and X448 algorithms support key derivation only. Currently there are
no additional options.
+=head1 Ed25519 and Ed448 ALGORITHMS
+
+These algorithms only support signing and verifying. OpenSSL only implements the
+"pure" variants of these algorithms so raw data can be passed directly to them
+without hashing them first. The option "-rawin" must be used with these
+algorithms with no "-digest" specified. Additionally OpenSSL only supports
+"oneshot" operation with these algorithms. This means that the entire file to
+be signed/verified must be read into memory before processing it. Signing or
+Verifying very large files should be avoided. Additionally the size of the file
+must be known for this to work. If the size of the file cannot be determined
+(for example if the input is stdin) then the sign or verify operation will fail.
+
=head1 SM2
The SM2 algorithm supports sign, verify, encrypt and decrypt operations. For
setup("test_pkeyutl");
-plan tests => 2;
+plan tests => 6;
-sub sign
-{
- # Utilize the sm2.crt as the TBS file
- return run(app(([ 'openssl', 'pkeyutl', '-sign',
+# For the tests below we use the cert itself as the TBS file
+
+SKIP: {
+ skip "Skipping tests that require EC, SM2 or SM3", 2
+ if disabled("ec") || disabled("sm2") || disabled("sm3");
+
+ # SM2
+ ok(run(app(([ 'openssl', 'pkeyutl', '-sign',
'-in', srctop_file('test', 'certs', 'sm2.crt'),
'-inkey', srctop_file('test', 'certs', 'sm2.key'),
- '-out', 'signature.sm2', '-rawin',
- '-digest', 'sm3', '-pkeyopt', 'sm2_id:someid'])));
-}
-
-sub verify
-{
- # Utilize the sm2.crt as the TBS file
- return run(app(([ 'openssl', 'pkeyutl', '-verify', '-certin',
+ '-out', 'signature.dat', '-rawin',
+ '-digest', 'sm3', '-pkeyopt', 'sm2_id:someid']))),
+ "Sign a piece of data using SM2");
+ ok(run(app(([ 'openssl', 'pkeyutl', '-verify', '-certin',
'-in', srctop_file('test', 'certs', 'sm2.crt'),
'-inkey', srctop_file('test', 'certs', 'sm2.crt'),
- '-sigfile', 'signature.sm2', '-rawin',
- '-digest', 'sm3', '-pkeyopt', 'sm2_id:someid'])));
+ '-sigfile', 'signature.dat', '-rawin',
+ '-digest', 'sm3', '-pkeyopt', 'sm2_id:someid']))),
+ "Verify an SM2 signature against a piece of data");
}
SKIP: {
- skip "Skipping tests that require EC, SM2 or SM3", 2
- if disabled("ec") || disabled("sm2") || disabled("sm3");
+ skip "Skipping tests that require EC", 4
+ if disabled("ec");
+
+ # 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', 'signature.dat', '-rawin']))),
+ "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', 'signature.dat', '-rawin']))),
+ "Verify an Ed25519 signature against a piece of data");
- ok(sign, "Sign a piece of data using SM2");
- ok(verify, "Verify an SM2 signature against a piece of data");
+ # Ed448
+ 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', 'signature.dat', '-rawin']))),
+ "Sign a piece of data using Ed448");
+ 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', 'signature.dat', '-rawin']))),
+ "Verify an Ed448 signature against a piece of data");
}
-unlink 'signature.sm2';
+unlink 'signature.dat';