Enable pkeyutl to use Ed448 and Ed25519

With the recent addition of the -rawin option it should be possible for
pkeyutl to sign and verify with Ed448 and Ed2559. The main remaining
stumbling block is that those algorirthms only support "oneshot" operation.
This commit enables pkeyutl to handle that.

Reviewed-by: Nicola Tuveri <nic.tuv@gmail.com>
(Merged from https://github.com/openssl/openssl/pull/8431)
This commit is contained in:
Matt Caswell 2019-03-07 10:37:34 +00:00
parent 6bc62a620e
commit ee633ace73
3 changed files with 164 additions and 70 deletions

View file

@ -13,6 +13,9 @@
#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
@ -22,7 +25,7 @@
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);
@ -33,7 +36,7 @@ static int do_keyop(EVP_PKEY_CTX *ctx, int pkey_op,
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 {
@ -109,6 +112,7 @@ int pkeyutl_main(int argc, char **argv)
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) {
@ -264,7 +268,7 @@ int pkeyutl_main(int argc, char **argv)
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);
@ -344,6 +348,15 @@ int pkeyutl_main(int argc, char **argv)
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;
}
@ -399,7 +412,7 @@ int pkeyutl_main(int argc, char **argv)
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,
@ -419,7 +432,7 @@ int pkeyutl_main(int argc, char **argv)
} 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,
@ -468,7 +481,8 @@ int pkeyutl_main(int argc, char **argv)
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;
@ -554,6 +568,14 @@ static EVP_PKEY_CTX *init_ctx(const char *kdfalg, int *pkeysize,
if (ctx == NULL)
goto end;
/*
* 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);
@ -579,6 +601,7 @@ static EVP_PKEY_CTX *init_ctx(const char *kdfalg, int *pkeysize,
rv = EVP_PKEY_derive_init(ctx);
break;
}
}
if (rv <= 0) {
EVP_PKEY_CTX_free(ctx);
@ -649,13 +672,14 @@ static int do_keyop(EVP_PKEY_CTX *ctx, int pkey_op,
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");
@ -663,19 +687,58 @@ static int do_raw_keyop(int pkey_op, EVP_PKEY_CTX *ctx,
}
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;
@ -687,14 +750,14 @@ static int do_raw_keyop(int pkey_op, EVP_PKEY_CTX *ctx,
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;

View file

@ -62,7 +62,7 @@ if this option is not specified.
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>
@ -216,21 +216,18 @@ hash the input data. It is used (by some algorithms) for sanity-checking the
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,
@ -319,6 +316,18 @@ this digest is assumed by default.
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

View file

@ -15,34 +15,56 @@ use OpenSSL::Test::Utils;
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',
'-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',
'-in', srctop_file('test', 'certs', 'sm2.crt'),
'-inkey', srctop_file('test', 'certs', 'sm2.crt'),
'-sigfile', 'signature.sm2', '-rawin',
'-digest', 'sm3', '-pkeyopt', 'sm2_id:someid'])));
}
# 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");
ok(sign, "Sign a piece of data using SM2");
ok(verify, "Verify an SM2 signature against a piece of data");
# SM2
ok(run(app(([ 'openssl', 'pkeyutl', '-sign',
'-in', srctop_file('test', 'certs', 'sm2.crt'),
'-inkey', srctop_file('test', 'certs', 'sm2.key'),
'-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.dat', '-rawin',
'-digest', 'sm3', '-pkeyopt', 'sm2_id:someid']))),
"Verify an SM2 signature against a piece of data");
}
unlink 'signature.sm2';
SKIP: {
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");
# 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.dat';