The branch master has been updated via e766f4a0531bffdab8ad2038279b755928d7a40a (commit) via a7cef52f9b961dcb1e5d0c3b75185a12a88ad2db (commit) from 4089b4340701e3c13e07169e67a7d14519c98658 (commit)
- Log ----------------------------------------------------------------- commit e766f4a0531bffdab8ad2038279b755928d7a40a Author: Paul Yang <yang.y...@baishancloud.com> Date: Mon Feb 25 17:34:03 2019 +0800 Fix the default digest algorthm of SM2 Currently SM2 shares the ameth with EC, so the current default digest algorithm returned is SHA256. This fixes the default digest algorithm of SM2 to SM3, which is the only valid digest algorithm for SM2 signature. Reviewed-by: Matt Caswell <m...@openssl.org> (Merged from https://github.com/openssl/openssl/pull/8186) commit a7cef52f9b961dcb1e5d0c3b75185a12a88ad2db Author: Paul Yang <yang.y...@baishancloud.com> Date: Wed Jan 16 16:16:28 2019 +0800 Support raw input data in apps/pkeyutl Some signature algorithms require special treatment for digesting, such as SM2. This patch adds the ability of handling raw input data in apps/pkeyutl other than accepting only pre-hashed input data. Beside, SM2 requries an ID string when signing or verifying a piece of data, this patch also adds the ability for apps/pkeyutil to specify that ID string. Reviewed-by: Matt Caswell <m...@openssl.org> (Merged from https://github.com/openssl/openssl/pull/8186) ----------------------------------------------------------------------- Summary of changes: apps/pkeyutl.c | 169 ++++++++++++++++++++++++++++++++++++----- crypto/ec/ec_ameth.c | 7 +- crypto/sm2/sm2_pmeth.c | 3 + doc/man1/pkeyutl.pod | 45 +++++++++++ test/certs/sm2.crt | 13 ++++ test/certs/sm2.key | 5 ++ test/recipes/20-test_pkeyutl.t | 43 +++++++++++ 7 files changed, 267 insertions(+), 18 deletions(-) create mode 100644 test/certs/sm2.crt create mode 100644 test/certs/sm2.key create mode 100644 test/recipes/20-test_pkeyutl.t diff --git a/apps/pkeyutl.c b/apps/pkeyutl.c index 8ee4a30..b3452d3 100644 --- a/apps/pkeyutl.c +++ b/apps/pkeyutl.c @@ -22,7 +22,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); + const int impl, EVP_PKEY **ppkey); static int setup_peer(EVP_PKEY_CTX *ctx, int peerform, const char *file, ENGINE *e); @@ -31,6 +31,11 @@ static int do_keyop(EVP_PKEY_CTX *ctx, int pkey_op, unsigned char *out, size_t *poutlen, const unsigned char *in, size_t inlen); +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, + unsigned char **out, size_t *poutlen); + typedef enum OPTION_choice { OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, OPT_ENGINE, OPT_ENGINE_IMPL, OPT_IN, OPT_OUT, @@ -38,12 +43,16 @@ typedef enum OPTION_choice { OPT_VERIFY, OPT_VERIFYRECOVER, OPT_REV, OPT_ENCRYPT, OPT_DECRYPT, OPT_DERIVE, OPT_SIGFILE, OPT_INKEY, OPT_PEERKEY, OPT_PASSIN, OPT_PEERFORM, OPT_KEYFORM, OPT_PKEYOPT, OPT_PKEYOPT_PASSIN, OPT_KDF, - OPT_KDFLEN, OPT_R_ENUM + OPT_KDFLEN, OPT_R_ENUM, + OPT_RAWIN, OPT_DIGEST } OPTION_CHOICE; const OPTIONS pkeyutl_options[] = { {"help", OPT_HELP, '-', "Display this summary"}, {"in", OPT_IN, '<', "Input file - default stdin"}, + {"rawin", OPT_RAWIN, '-', "Indicate the input data is in raw form"}, + {"digest", OPT_DIGEST, 's', + "Specify the digest algorithm when signing the raw input data"}, {"out", OPT_OUT, '>', "Output file - default stdout"}, {"pubin", OPT_PUBIN, '-', "Input is a public key"}, {"certin", OPT_CERTIN, '-', "Input is a cert with a public key"}, @@ -82,6 +91,7 @@ int pkeyutl_main(int argc, char **argv) BIO *in = NULL, *out = NULL; ENGINE *e = NULL; EVP_PKEY_CTX *ctx = NULL; + EVP_PKEY *pkey = NULL; char *infile = NULL, *outfile = NULL, *sigfile = NULL, *passinarg = NULL; char hexdump = 0, asn1parse = 0, rev = 0, *prog; unsigned char *buf_in = NULL, *buf_out = NULL, *sig = NULL; @@ -97,6 +107,8 @@ int pkeyutl_main(int argc, char **argv) int kdflen = 0; STACK_OF(OPENSSL_STRING) *pkeyopts = NULL; STACK_OF(OPENSSL_STRING) *pkeyopts_passin = NULL; + int rawin = 0; + const EVP_MD *md = NULL; prog = opt_init(argc, argv, pkeyutl_options); while ((o = opt_next()) != OPT_EOF) { @@ -203,12 +215,39 @@ int pkeyutl_main(int argc, char **argv) goto end; } break; + case OPT_RAWIN: + rawin = 1; + break; + case OPT_DIGEST: + if (!opt_md(opt_arg(), &md)) + goto end; + break; } } argc = opt_num_rest(); if (argc != 0) goto opthelp; + if (rawin && pkey_op != EVP_PKEY_OP_SIGN && pkey_op != EVP_PKEY_OP_VERIFY) { + BIO_printf(bio_err, + "%s: -rawin can only be used with -sign or -verify\n", + prog); + goto opthelp; + } + + if (md != NULL && !rawin) { + BIO_printf(bio_err, + "%s: -digest can only be used with -rawin\n", + prog); + goto opthelp; + } + + if (rawin && rev) { + BIO_printf(bio_err, "%s: -rev cannot be used with raw input\n", + prog); + goto opthelp; + } + if (kdfalg != NULL) { if (kdflen == 0) { BIO_printf(bio_err, @@ -225,7 +264,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); + passinarg, pkey_op, e, engine_impl, &pkey); if (ctx == NULL) { BIO_printf(bio_err, "%s: Error initializing context\n", prog); ERR_print_errors(bio_err); @@ -327,7 +366,8 @@ int pkeyutl_main(int argc, char **argv) } } - if (in != NULL) { + /* Raw input data is handled elsewhere */ + if (in != NULL && !rawin) { /* Read the input data */ buf_inlen = bio_to_mem(&buf_in, keysize * 10, in); if (buf_inlen < 0) { @@ -346,8 +386,9 @@ int pkeyutl_main(int argc, char **argv) } } - /* Sanity check the input */ - if (buf_inlen > EVP_MAX_MD_SIZE + /* Sanity check the input if the input is not raw */ + if (!rawin + && buf_inlen > EVP_MAX_MD_SIZE && (pkey_op == EVP_PKEY_OP_SIGN || pkey_op == EVP_PKEY_OP_VERIFY || pkey_op == EVP_PKEY_OP_VERIFYRECOVER)) { @@ -357,8 +398,13 @@ int pkeyutl_main(int argc, char **argv) } if (pkey_op == EVP_PKEY_OP_VERIFY) { - rv = EVP_PKEY_verify(ctx, sig, (size_t)siglen, - buf_in, (size_t)buf_inlen); + if (rawin) { + rv = do_raw_keyop(pkey_op, ctx, md, pkey, in, sig, siglen, + NULL, 0); + } else { + rv = EVP_PKEY_verify(ctx, sig, (size_t)siglen, + buf_in, (size_t)buf_inlen); + } if (rv == 1) { BIO_puts(out, "Signature Verified Successfully\n"); ret = 0; @@ -371,14 +417,20 @@ int pkeyutl_main(int argc, char **argv) buf_outlen = kdflen; rv = 1; } else { - rv = do_keyop(ctx, pkey_op, NULL, (size_t *)&buf_outlen, - buf_in, (size_t)buf_inlen); - } - if (rv > 0 && buf_outlen != 0) { - buf_out = app_malloc(buf_outlen, "buffer output"); - rv = do_keyop(ctx, pkey_op, - buf_out, (size_t *)&buf_outlen, - buf_in, (size_t)buf_inlen); + if (rawin) { + /* rawin allocates the buffer in do_raw_keyop() */ + rv = do_raw_keyop(pkey_op, ctx, md, pkey, in, NULL, 0, + &buf_out, (size_t *)&buf_outlen); + } else { + rv = do_keyop(ctx, pkey_op, NULL, (size_t *)&buf_outlen, + buf_in, (size_t)buf_inlen); + if (rv > 0 && buf_outlen != 0) { + buf_out = app_malloc(buf_outlen, "buffer output"); + rv = do_keyop(ctx, pkey_op, + buf_out, (size_t *)&buf_outlen, + buf_in, (size_t)buf_inlen); + } + } } if (rv <= 0) { if (pkey_op != EVP_PKEY_OP_DERIVE) { @@ -416,7 +468,7 @@ 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) + const int engine_impl, EVP_PKEY **ppkey) { EVP_PKEY *pkey = NULL; EVP_PKEY_CTX *ctx = NULL; @@ -474,10 +526,25 @@ static EVP_PKEY_CTX *init_ctx(const char *kdfalg, int *pkeysize, } ctx = EVP_PKEY_CTX_new_id(kdfnid, impl); } else { + EC_KEY *eckey = NULL; + const EC_GROUP *group = NULL; + int nid; + if (pkey == NULL) goto end; + /* SM2 needs a special treatment */ + if (EVP_PKEY_id(pkey) == EVP_PKEY_EC) { + if ((eckey = EVP_PKEY_get0_EC_KEY(pkey)) == NULL + || (group = EC_KEY_get0_group(eckey)) == NULL + || (nid = EC_GROUP_get_curve_name(group)) == 0) + goto end; + if (nid == NID_sm2) + EVP_PKEY_set_alias_type(pkey, EVP_PKEY_SM2); + } *pkeysize = EVP_PKEY_size(pkey); ctx = EVP_PKEY_CTX_new(pkey, impl); + if (ppkey != NULL) + *ppkey = pkey; EVP_PKEY_free(pkey); } @@ -574,3 +641,71 @@ static int do_keyop(EVP_PKEY_CTX *ctx, int pkey_op, } return rv; } + +#define TBUF_MAXSIZE 2048 + +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, + unsigned char **out, size_t *poutlen) +{ + int rv = 0; + EVP_MD_CTX *mctx = NULL; + unsigned char tbuf[TBUF_MAXSIZE]; + int tbuf_len = 0; + + if ((mctx = EVP_MD_CTX_new()) == NULL) { + BIO_printf(bio_err, "Error: out of memory\n"); + return rv; + } + EVP_MD_CTX_set_pkey_ctx(mctx, ctx); + + 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) + break; + if (tbuf_len < 0) { + BIO_printf(bio_err, "Error reading raw input data\n"); + goto end; + } + rv = EVP_DigestVerifyUpdate(mctx, tbuf, (size_t)tbuf_len); + if (rv != 1) { + BIO_printf(bio_err, "Error verifying raw input data\n"); + goto end; + } + } + rv = EVP_DigestVerifyFinal(mctx, sig, (size_t)siglen); + break; + case EVP_PKEY_OP_SIGN: + if (EVP_DigestSignInit(mctx, NULL, md, NULL, pkey) != 1) + goto end; + for (;;) { + tbuf_len = BIO_read(in, tbuf, TBUF_MAXSIZE); + if (tbuf_len == 0) + break; + if (tbuf_len < 0) { + BIO_printf(bio_err, "Error reading raw input data\n"); + goto end; + } + rv = EVP_DigestSignUpdate(mctx, tbuf, (size_t)tbuf_len); + if (rv != 1) { + BIO_printf(bio_err, "Error signing raw input data\n"); + goto end; + } + } + rv = EVP_DigestSignFinal(mctx, NULL, poutlen); + if (rv == 1 && out != NULL) { + *out = app_malloc(*poutlen, "buffer output"); + rv = EVP_DigestSignFinal(mctx, *out, poutlen); + } + break; + } + + end: + EVP_MD_CTX_free(mctx); + return rv; +} diff --git a/crypto/ec/ec_ameth.c b/crypto/ec/ec_ameth.c index 54d55ad..2beeb82 100644 --- a/crypto/ec/ec_ameth.c +++ b/crypto/ec/ec_ameth.c @@ -504,7 +504,12 @@ static int ec_pkey_ctrl(EVP_PKEY *pkey, int op, long arg1, void *arg2) #endif case ASN1_PKEY_CTRL_DEFAULT_MD_NID: - *(int *)arg2 = NID_sha256; + if (EVP_PKEY_id(pkey) == EVP_PKEY_SM2) { + /* For SM2, the only valid digest-alg is SM3 */ + *(int *)arg2 = NID_sm3; + } else { + *(int *)arg2 = NID_sha256; + } return 1; case ASN1_PKEY_CTRL_SET1_TLS_ENCPT: diff --git a/crypto/sm2/sm2_pmeth.c b/crypto/sm2/sm2_pmeth.c index 8ae7556..5ca430f 100644 --- a/crypto/sm2/sm2_pmeth.c +++ b/crypto/sm2/sm2_pmeth.c @@ -248,6 +248,9 @@ static int pkey_sm2_ctrl_str(EVP_PKEY_CTX *ctx, else return -2; return EVP_PKEY_CTX_set_ec_param_enc(ctx, param_enc); + } else if (strcmp(type, "sm2_id") == 0) { + return pkey_sm2_ctrl(ctx, EVP_PKEY_CTRL_SET1_ID, + (int)strlen(value), (void *)value); } return -2; diff --git a/doc/man1/pkeyutl.pod b/doc/man1/pkeyutl.pod index 24354db..c566f6d 100644 --- a/doc/man1/pkeyutl.pod +++ b/doc/man1/pkeyutl.pod @@ -10,6 +10,8 @@ pkeyutl - public key algorithm utility B<openssl> B<pkeyutl> [B<-help>] [B<-in file>] +[B<-rawin>] +[B<-digest algorithm>] [B<-out file>] [B<-sigfile file>] [B<-inkey file>] @@ -55,6 +57,23 @@ Print out a usage message. This specifies the input filename to read data from or standard input if this option is not specified. +=item B<-rawin> + +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>. + +=item B<-digest algorithm> + +This specifies the digest algorithm which is used to hash the input data before +signing or verifying it with the input key. This option could be omitted if the +signature algorithm does not require one (for instance, EdDSA). If this option +is omitted but the signature algorithm requires one, a default value will be +used. For signature algorithms like RSA, DSA and ECDSA, SHA-256 will be the +default digest algorithm. For SM2, it will be SM3. If this option is present, +then the B<-rawin> option must be also specified to B<pkeyutl>. + =item B<-out filename> Specifies the output filename to write to or standard output by @@ -300,6 +319,22 @@ this digest is assumed by default. The X25519 and X448 algorithms support key derivation only. Currently there are no additional options. +=head1 SM2 + +The SM2 algorithm supports sign, verify, encrypt and decrypt operations. For +the sign and verify operations, SM2 requires an ID string to be passed in. The +following B<pkeyopt> value is supported: + +=over 4 + +=item B<sm2_id:string> + +This sets the ID string used in SM2 sign or verify operations. While verifying +an SM2 signature, the ID string must be the same one used when signing the data. +Otherwise the verification will fail. + +=back + =head1 EXAMPLES Sign some data using a private key: @@ -338,6 +373,16 @@ Derive using the same algorithm, but read key from environment variable MYPASS: openssl pkeyutl -kdf scrypt -kdflen 16 -pkeyopt_passin pass:env:MYPASS \ -pkeyopt hexsalt:aabbcc -pkeyopt N:16384 -pkeyopt r:8 -pkeyopt p:1 +Sign some data using an L<SM2(7)> private key and a specific ID: + + openssl pkeyutl -sign -in file -inkey sm2.key -out sig -rawin -digest sm3 \ + -pkeyopt sm2_id:someid + +Verify some data using an L<SM2(7)> certificate and a specific ID: + + openssl pkeyutl -verify -certin -in file -inkey sm2.cert -sigfile sig \ + -rawin -digest sm3 -pkeyopt sm2_id:someid + =head1 SEE ALSO L<genpkey(1)>, L<pkey(1)>, L<rsautl(1)> diff --git a/test/certs/sm2.crt b/test/certs/sm2.crt new file mode 100644 index 0000000..189abb1 --- /dev/null +++ b/test/certs/sm2.crt @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB6DCCAY6gAwIBAgIJAKH2BR6ITHZeMAoGCCqBHM9VAYN1MGgxCzAJBgNVBAYT +AkNOMQswCQYDVQQIDAJMTjERMA8GA1UEBwwIU2hlbnlhbmcxETAPBgNVBAoMCFRl +c3QgT3JnMRAwDgYDVQQLDAdUZXN0IE9VMRQwEgYDVQQDDAtUZXN0IFNNMiBDQTAe +Fw0xOTAyMTkwNzA1NDhaFw0yMzAzMzAwNzA1NDhaMG8xCzAJBgNVBAYTAkNOMQsw +CQYDVQQIDAJMTjERMA8GA1UEBwwIU2hlbnlhbmcxETAPBgNVBAoMCFRlc3QgT3Jn +MRAwDgYDVQQLDAdUZXN0IE9VMRswGQYDVQQDDBJUZXN0IFNNMiBTaWduIENlcnQw +WTATBgcqhkjOPQIBBggqgRzPVQGCLQNCAAQwqeNkWp7fiu1KZnuDkAucpM8piEzE +TL1ymrcrOBvv8mhNNkeb20asbWgFQI2zOrSM99/sXGn9rM2/usM/MlcaoxowGDAJ +BgNVHRMEAjAAMAsGA1UdDwQEAwIGwDAKBggqgRzPVQGDdQNIADBFAiEA9edBnAqT +TNuGIUIvXsj6/nP+AzXA9HGtAIY4nrqW8LkCIHyZzhRTlxYtgfqkDl0OK5QQRCZH +OZOfmtx613VyzXwc +-----END CERTIFICATE----- diff --git a/test/certs/sm2.key b/test/certs/sm2.key new file mode 100644 index 0000000..1efd364 --- /dev/null +++ b/test/certs/sm2.key @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgSKhk+4xGyDI+IS2H +WVfFPDxh1qv5+wtrddaIsGNXGZihRANCAAQwqeNkWp7fiu1KZnuDkAucpM8piEzE +TL1ymrcrOBvv8mhNNkeb20asbWgFQI2zOrSM99/sXGn9rM2/usM/Mlca +-----END PRIVATE KEY----- diff --git a/test/recipes/20-test_pkeyutl.t b/test/recipes/20-test_pkeyutl.t new file mode 100644 index 0000000..a051138 --- /dev/null +++ b/test/recipes/20-test_pkeyutl.t @@ -0,0 +1,43 @@ +#! /usr/bin/env perl +# Copyright 2018 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +use strict; +use warnings; + +use File::Spec; +use OpenSSL::Test qw/:DEFAULT srctop_file/; +use OpenSSL::Test::Utils; + +setup("test_pkeyutl"); + +plan tests => 2; + +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']))); +} + +ok(sign, "Sign a piece of data using SM2"); +ok(verify, "Verify an SM2 signature against a piece of data"); + +unlink 'signature.sm2';