123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570 |
- /* ecc-sm2.c - Elliptic Curve SM2 implementation
- * Copyright (C) 2020 Tianjia Zhang
- *
- * This file is part of Libgcrypt.
- *
- * Libgcrypt is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * Libgcrypt is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this program; if not, see <http://www.gnu.org/licenses/>.
- */
- #include <config.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <errno.h>
- #include "g10lib.h"
- #include "bithelp.h"
- #include "mpi.h"
- #include "cipher.h"
- #include "context.h"
- #include "ec-context.h"
- #include "pubkey-internal.h"
- #include "ecc-common.h"
- #define MPI_NBYTES(m) ((mpi_get_nbits(m) + 7) / 8)
- /* Key derivation function from X9.63/SECG */
- static gpg_err_code_t
- kdf_x9_63 (int algo, const void *in, size_t inlen, void *out, size_t outlen)
- {
- gpg_err_code_t rc;
- gcry_md_hd_t hd;
- int mdlen;
- u32 counter = 1;
- u32 counter_be;
- unsigned char *dgst;
- unsigned char *pout = out;
- size_t rlen = outlen;
- size_t len;
- rc = _gcry_md_open (&hd, algo, 0);
- if (rc)
- return rc;
- mdlen = _gcry_md_get_algo_dlen (algo);
- while (rlen > 0)
- {
- counter_be = be_bswap32 (counter); /* cpu_to_be32 */
- counter++;
- _gcry_md_write (hd, in, inlen);
- _gcry_md_write (hd, &counter_be, sizeof(counter_be));
- dgst = _gcry_md_read (hd, algo);
- if (dgst == NULL)
- {
- rc = GPG_ERR_DIGEST_ALGO;
- break;
- }
- len = mdlen < rlen ? mdlen : rlen; /* min(mdlen, rlen) */
- memcpy (pout, dgst, len);
- rlen -= len;
- pout += len;
- _gcry_md_reset (hd);
- }
- _gcry_md_close (hd);
- return rc;
- }
- /* _gcry_ecc_sm2_encrypt description:
- * input:
- * data[0] : octet string
- * output: A new S-expression with the parameters:
- * a: c1 : generated ephemeral public key (kG)
- * b: c3 : Hash(x2 || IN || y2)
- * c: c2 : cipher
- *
- * sm2_decrypt description:
- * in contrast to encrypt
- */
- gpg_err_code_t
- _gcry_ecc_sm2_encrypt (gcry_sexp_t *r_ciph, gcry_mpi_t input, mpi_ec_t ec)
- {
- gpg_err_code_t rc;
- const int algo = GCRY_MD_SM3;
- gcry_md_hd_t md = NULL;
- int mdlen;
- unsigned char *dgst;
- gcry_mpi_t k = NULL;
- mpi_point_struct kG, kP;
- gcry_mpi_t x1, y1;
- gcry_mpi_t x2, y2;
- gcry_mpi_t x2y2 = NULL;
- unsigned char *in = NULL;
- unsigned int inlen;
- unsigned char *raw;
- unsigned int rawlen;
- unsigned char *cipher = NULL;
- int i;
- point_init (&kG);
- point_init (&kP);
- x1 = mpi_new (0);
- y1 = mpi_new (0);
- x2 = mpi_new (0);
- y2 = mpi_new (0);
- in = _gcry_mpi_get_buffer (input, 0, &inlen, NULL);
- if (!in)
- {
- rc = gpg_err_code_from_syserror ();
- goto leave;
- }
- cipher = xtrymalloc (inlen);
- if (!cipher)
- {
- rc = gpg_err_code_from_syserror ();
- goto leave;
- }
- /* rand k in [1, n-1] */
- k = _gcry_dsa_gen_k (ec->n, GCRY_VERY_STRONG_RANDOM);
- /* [k]G = (x1, y1) */
- _gcry_mpi_ec_mul_point (&kG, k, ec->G, ec);
- if (_gcry_mpi_ec_get_affine (x1, y1, &kG, ec))
- {
- if (DBG_CIPHER)
- log_debug ("Bad check: kG can not be a Point at Infinity!\n");
- rc = GPG_ERR_INV_DATA;
- goto leave;
- }
- /* [k]P = (x2, y2) */
- _gcry_mpi_ec_mul_point (&kP, k, ec->Q, ec);
- if (_gcry_mpi_ec_get_affine (x2, y2, &kP, ec))
- {
- rc = GPG_ERR_INV_DATA;
- goto leave;
- }
- /* t = KDF(x2 || y2, klen) */
- x2y2 = _gcry_mpi_ec_ec2os (&kP, ec);
- raw = mpi_get_opaque (x2y2, &rawlen);
- rawlen = (rawlen + 7) / 8;
- /* skip the prefix '0x04' */
- raw += 1;
- rawlen -= 1;
- rc = kdf_x9_63 (algo, raw, rawlen, cipher, inlen);
- if (rc)
- goto leave;
- /* cipher = t xor in */
- for (i = 0; i < inlen; i++)
- cipher[i] ^= in[i];
- /* hash(x2 || IN || y2) */
- mdlen = _gcry_md_get_algo_dlen (algo);
- rc = _gcry_md_open (&md, algo, 0);
- if (rc)
- goto leave;
- _gcry_md_write (md, raw, MPI_NBYTES(x2));
- _gcry_md_write (md, in, inlen);
- _gcry_md_write (md, raw + MPI_NBYTES(x2), MPI_NBYTES(y2));
- dgst = _gcry_md_read (md, algo);
- if (dgst == NULL)
- {
- rc = GPG_ERR_DIGEST_ALGO;
- goto leave;
- }
- if (!rc)
- {
- gcry_mpi_t c1;
- gcry_mpi_t c3;
- gcry_mpi_t c2;
- c3 = mpi_new (0);
- c2 = mpi_new (0);
- c1 = _gcry_ecc_ec2os (x1, y1, ec->p);
- _gcry_mpi_set_opaque_copy (c3, dgst, mdlen * 8);
- _gcry_mpi_set_opaque_copy (c2, cipher, inlen * 8);
- rc = sexp_build (r_ciph, NULL,
- "(enc-val(flags sm2)(sm2(a%M)(b%M)(c%M)))",
- c1, c3, c2);
- mpi_free (c1);
- mpi_free (c3);
- mpi_free (c2);
- }
- leave:
- _gcry_md_close (md);
- mpi_free (x2y2);
- mpi_free (k);
- point_free (&kG);
- point_free (&kP);
- mpi_free (x1);
- mpi_free (y1);
- mpi_free (x2);
- mpi_free (y2);
- xfree (cipher);
- xfree (in);
- return rc;
- }
- gpg_err_code_t
- _gcry_ecc_sm2_decrypt (gcry_sexp_t *r_plain, gcry_sexp_t data_list, mpi_ec_t ec)
- {
- gpg_err_code_t rc;
- gcry_mpi_t data_c1 = NULL;
- gcry_mpi_t data_c3 = NULL;
- gcry_mpi_t data_c2 = NULL;
- /*
- * Extract the data.
- */
- rc = sexp_extract_param (data_list, NULL, "/a/b/c",
- &data_c1, &data_c3, &data_c2, NULL);
- if (rc)
- goto leave;
- if (DBG_CIPHER)
- {
- log_printmpi ("ecc_decrypt d_c1", data_c1);
- log_printmpi ("ecc_decrypt d_c3", data_c3);
- log_printmpi ("ecc_decrypt d_c2", data_c2);
- }
- {
- const int algo = GCRY_MD_SM3;
- gcry_md_hd_t md = NULL;
- int mdlen;
- unsigned char *dgst;
- mpi_point_struct c1;
- mpi_point_struct kP;
- gcry_mpi_t x2, y2;
- gcry_mpi_t x2y2 = NULL;
- unsigned char *in = NULL;
- unsigned int inlen;
- unsigned char *plain = NULL;
- unsigned char *raw;
- unsigned int rawlen;
- unsigned char *c3 = NULL;
- unsigned int c3_len;
- int i;
- point_init (&c1);
- point_init (&kP);
- x2 = mpi_new (0);
- y2 = mpi_new (0);
- in = mpi_get_opaque (data_c2, &inlen);
- inlen = (inlen + 7) / 8;
- plain = xtrymalloc (inlen);
- if (!plain)
- {
- rc = gpg_err_code_from_syserror ();
- goto leave_main;
- }
- rc = _gcry_ecc_sec_decodepoint (data_c1, ec, &c1);
- if (rc)
- goto leave_main;
- if (!_gcry_mpi_ec_curve_point (&c1, ec))
- {
- rc = GPG_ERR_INV_DATA;
- goto leave_main;
- }
- /* [d]C1 = (x2, y2), C1 = [k]G */
- _gcry_mpi_ec_mul_point (&kP, ec->d, &c1, ec);
- if (_gcry_mpi_ec_get_affine (x2, y2, &kP, ec))
- {
- rc = GPG_ERR_INV_DATA;
- goto leave_main;
- }
- /* t = KDF(x2 || y2, inlen) */
- x2y2 = _gcry_mpi_ec_ec2os (&kP, ec);
- raw = mpi_get_opaque (x2y2, &rawlen);
- rawlen = (rawlen + 7) / 8;
- /* skip the prefix '0x04' */
- raw += 1;
- rawlen -= 1;
- rc = kdf_x9_63 (algo, raw, rawlen, plain, inlen);
- if (rc)
- goto leave_main;
- /* plain = C2 xor t */
- for (i = 0; i < inlen; i++)
- plain[i] ^= in[i];
- /* Hash(x2 || IN || y2) == C3 */
- mdlen = _gcry_md_get_algo_dlen (algo);
- rc = _gcry_md_open (&md, algo, 0);
- if (rc)
- goto leave_main;
- _gcry_md_write (md, raw, MPI_NBYTES(x2));
- _gcry_md_write (md, plain, inlen);
- _gcry_md_write (md, raw + MPI_NBYTES(x2), MPI_NBYTES(y2));
- dgst = _gcry_md_read (md, algo);
- if (dgst == NULL)
- {
- memset (plain, 0, inlen);
- rc = GPG_ERR_DIGEST_ALGO;
- goto leave_main;
- }
- c3 = mpi_get_opaque (data_c3, &c3_len);
- c3_len = (c3_len + 7) / 8;
- if (c3_len != mdlen || memcmp (dgst, c3, c3_len) != 0)
- {
- memset (plain, 0, inlen);
- rc = GPG_ERR_INV_DATA;
- goto leave_main;
- }
- if (!rc)
- {
- gcry_mpi_t r;
- r = mpi_new (inlen * 8);
- _gcry_mpi_set_buffer (r, plain, inlen, 0);
- rc = sexp_build (r_plain, NULL, "(value %m)", r);
- mpi_free (r);
- }
- leave_main:
- _gcry_md_close (md);
- mpi_free (x2y2);
- xfree (plain);
- point_free (&c1);
- point_free (&kP);
- mpi_free (x2);
- mpi_free (y2);
- }
- leave:
- _gcry_mpi_release (data_c1);
- _gcry_mpi_release (data_c3);
- _gcry_mpi_release (data_c2);
- return rc;
- }
- /* Compute an SM2 signature.
- * Return the signature struct (r,s) from the message hash. The caller
- * must have allocated R and S.
- */
- gpg_err_code_t
- _gcry_ecc_sm2_sign (gcry_mpi_t input, mpi_ec_t ec,
- gcry_mpi_t r, gcry_mpi_t s,
- int flags, int hashalgo)
- {
- gpg_err_code_t rc = 0;
- int extraloops = 0;
- gcry_mpi_t hash;
- const void *abuf;
- unsigned int abits, qbits;
- gcry_mpi_t tmp = NULL;
- gcry_mpi_t k = NULL;
- gcry_mpi_t rk = NULL;
- mpi_point_struct kG;
- gcry_mpi_t x1;
- if (DBG_CIPHER)
- log_mpidump ("sm2 sign hash ", input);
- qbits = mpi_get_nbits (ec->n);
- /* Convert the INPUT into an MPI if needed. */
- rc = _gcry_dsa_normalize_hash (input, &hash, qbits);
- if (rc)
- return rc;
- point_init (&kG);
- x1 = mpi_new (0);
- rk = mpi_new (0);
- tmp = mpi_new (0);
- for (;;)
- {
- /* rand k in [1, n-1] */
- if ((flags & PUBKEY_FLAG_RFC6979) && hashalgo)
- {
- /* Use Pornin's method for deterministic DSA. If this
- flag is set, it is expected that HASH is an opaque
- MPI with the to be signed hash. That hash is also
- used as h1 from 3.2.a. */
- if (!mpi_is_opaque (input))
- {
- rc = GPG_ERR_CONFLICT;
- goto leave;
- }
- abuf = mpi_get_opaque (input, &abits);
- rc = _gcry_dsa_gen_rfc6979_k (&k, ec->n, ec->d,
- abuf, (abits+7)/8,
- hashalgo, extraloops);
- if (rc)
- goto leave;
- extraloops++;
- }
- else
- k = _gcry_dsa_gen_k (ec->n, GCRY_VERY_STRONG_RANDOM);
- _gcry_dsa_modify_k (k, ec->n, qbits);
- /* [k]G = (x1, y1) */
- _gcry_mpi_ec_mul_point (&kG, k, ec->G, ec);
- if (_gcry_mpi_ec_get_affine (x1, NULL, &kG, ec))
- {
- rc = GPG_ERR_INV_DATA;
- goto leave;
- }
- /* r = (e + x1) % n */
- mpi_addm (r, hash, x1, ec->n);
- /* r != 0 && r + k != n */
- if (mpi_cmp_ui (r, 0) == 0)
- continue;
- mpi_add (rk, r, k);
- if (mpi_cmp (rk, ec->n) == 0)
- continue;
- /* s = ((d + 1)^-1 * (k - rd)) % n */
- mpi_addm (s, ec->d, GCRYMPI_CONST_ONE, ec->n);
- mpi_invm (s, s, ec->n);
- mpi_mulm (tmp, r, ec->d, ec->n);
- mpi_subm (tmp, k, tmp, ec->n);
- mpi_mulm (s, s, tmp, ec->n);
- /* s != 0 */
- if (mpi_cmp_ui (s, 0) == 0)
- continue;
- break; /* Okay */
- }
- if (DBG_CIPHER)
- {
- log_mpidump ("sm2 sign result r ", r);
- log_mpidump ("sm2 sign result s ", s);
- }
- leave:
- point_free (&kG);
- mpi_free (k);
- mpi_free (x1);
- mpi_free (rk);
- mpi_free (tmp);
- if (hash != input)
- mpi_free (hash);
- return rc;
- }
- /* Verify an SM2 signature.
- * Check if R and S verifies INPUT.
- */
- gpg_err_code_t
- _gcry_ecc_sm2_verify (gcry_mpi_t input, mpi_ec_t ec,
- gcry_mpi_t r, gcry_mpi_t s)
- {
- gpg_err_code_t err = 0;
- gcry_mpi_t hash = NULL;
- gcry_mpi_t t = NULL;
- mpi_point_struct sG, tP;
- gcry_mpi_t x1, y1;
- unsigned int nbits;
- if (!_gcry_mpi_ec_curve_point (ec->Q, ec))
- return GPG_ERR_BROKEN_PUBKEY;
- /* r, s within [1, n-1] */
- if (mpi_cmp_ui (r, 1) < 0 || mpi_cmp (r, ec->n) > 0)
- return GPG_ERR_BAD_SIGNATURE;
- if (mpi_cmp_ui (s, 1) < 0 || mpi_cmp (s, ec->n) > 0)
- return GPG_ERR_BAD_SIGNATURE;
- nbits = mpi_get_nbits (ec->n);
- err = _gcry_dsa_normalize_hash (input, &hash, nbits);
- if (err)
- return err;
- point_init (&sG);
- point_init (&tP);
- x1 = mpi_new (0);
- y1 = mpi_new (0);
- t = mpi_new (0);
- /* t = (r + s) % n, t != 0 */
- mpi_addm (t, r, s, ec->n);
- if (mpi_cmp_ui (t, 0) == 0)
- {
- err = GPG_ERR_BAD_SIGNATURE;
- goto leave;
- }
- /* sG + tP = (x1, y1) */
- _gcry_mpi_ec_mul_point (&sG, s, ec->G, ec);
- _gcry_mpi_ec_mul_point (&tP, t, ec->Q, ec);
- _gcry_mpi_ec_add_points (&sG, &sG, &tP, ec);
- if (_gcry_mpi_ec_get_affine (x1, y1, &sG, ec))
- {
- err = GPG_ERR_INV_DATA;
- goto leave;
- }
- /* R = (e + x1) % n */
- mpi_addm (t, hash, x1, ec->n);
- /* check R == r */
- if (mpi_cmp (t, r))
- {
- if (DBG_CIPHER)
- {
- log_mpidump (" R", t);
- log_mpidump (" r", r);
- log_mpidump (" s", s);
- }
- err = GPG_ERR_BAD_SIGNATURE;
- goto leave;
- }
- if (DBG_CIPHER)
- log_debug ("sm2 verify: Accepted\n");
- leave:
- point_free (&sG);
- point_free (&tP);
- mpi_free (x1);
- mpi_free (y1);
- mpi_free (t);
- if (hash != input)
- mpi_free (hash);
- return err;
- }
|