[PATCH 1/2] Add elliptic curve SM2 implementation.

Tianjia Zhang tianjia.zhang at linux.alibaba.com
Mon Jan 20 04:42:24 CET 2020


* configure.ac (enabled_pubkey_ciphers): Add ecc-sm2.
* cipher/Makefile.am (EXTRA_libcipher_la_SOURCES): Add ecc-sm2.c.
* cipher/pubkey-util.c (_gcry_pk_util_parse_flaglist,
  _gcry_pk_util_preparse_sigval): Add sm2 flags.
* cipher/ecc.c: Support ecc-sm2.
* cipher/ecc-common.h: Add declarations for ecc-sm2.
* cipher/ecc-sm2.c: New.
* src/cipher.h: Define PUBKEY_FLAG_SM2.
--

Signed-off-by: Tianjia Zhang <tianjia.zhang at linux.alibaba.com>
---
 cipher/Makefile.am   |   2 +-
 cipher/ecc-common.h  |  12 +
 cipher/ecc-sm2.c     | 566 +++++++++++++++++++++++++++++++++++++++++++
 cipher/ecc.c         |  53 +++-
 cipher/pubkey-util.c |   7 +
 configure.ac         |   3 +-
 src/cipher.h         |   1 +
 7 files changed, 630 insertions(+), 14 deletions(-)
 create mode 100644 cipher/ecc-sm2.c

diff --git a/cipher/Makefile.am b/cipher/Makefile.am
index 020a9616..10a5ab62 100644
--- a/cipher/Makefile.am
+++ b/cipher/Makefile.am
@@ -87,7 +87,7 @@ EXTRA_libcipher_la_SOURCES = \
 	dsa.c \
 	elgamal.c \
 	ecc.c ecc-curves.c ecc-misc.c ecc-common.h \
-	ecc-ecdh.c ecc-ecdsa.c ecc-eddsa.c ecc-gost.c \
+	ecc-ecdh.c ecc-ecdsa.c ecc-eddsa.c ecc-gost.c ecc-sm2.c \
 	idea.c \
 	gost28147.c gost.h \
 	gostr3411-94.c \
diff --git a/cipher/ecc-common.h b/cipher/ecc-common.h
index 7fbc950a..b8b7c763 100644
--- a/cipher/ecc-common.h
+++ b/cipher/ecc-common.h
@@ -125,4 +125,16 @@ gpg_err_code_t _gcry_ecc_gost_verify (gcry_mpi_t input, mpi_ec_t ec,
                                       gcry_mpi_t r, gcry_mpi_t s);
 
 
+/*-- ecc-sm2.c --*/
+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 _gcry_ecc_sm2_decrypt (gcry_sexp_t *r_plain,
+                                      gcry_sexp_t data_list, mpi_ec_t ec);
+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 _gcry_ecc_sm2_verify (gcry_mpi_t input, mpi_ec_t ec,
+                                     gcry_mpi_t r, gcry_mpi_t s);
+
+
 #endif /*GCRY_ECC_COMMON_H*/
diff --git a/cipher/ecc-sm2.c b/cipher/ecc-sm2.c
new file mode 100644
index 00000000..a6341132
--- /dev/null
+++ b/cipher/ecc-sm2.c
@@ -0,0 +1,566 @@
+/* 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_os2ec (&c1, data_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;
+
+  /* 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;
+}
diff --git a/cipher/ecc.c b/cipher/ecc.c
index 1195f5ec..49c2c0f6 100644
--- a/cipher/ecc.c
+++ b/cipher/ecc.c
@@ -69,6 +69,7 @@ static const char *ecc_names[] =
     "ecdh",
     "eddsa",
     "gost",
+    "sm2",
     NULL,
   };
 
@@ -723,6 +724,14 @@ ecc_sign (gcry_sexp_t *r_sig, gcry_sexp_t s_data, gcry_sexp_t keyparms)
         rc = sexp_build (r_sig, NULL,
                          "(sig-val(gost(r%M)(s%M)))", sig_r, sig_s);
     }
+  else if ((ctx.flags & PUBKEY_FLAG_SM2))
+    {
+      rc = _gcry_ecc_sm2_sign (data, ec, sig_r, sig_s,
+                               ctx.flags, ctx.hash_algo);
+      if (!rc)
+        rc = sexp_build (r_sig, NULL,
+                         "(sig-val(sm2(r%M)(s%M)))", sig_r, sig_s);
+    }
   else
     {
       rc = _gcry_ecc_ecdsa_sign (data, ec, sig_r, sig_s,
@@ -811,6 +820,10 @@ ecc_verify (gcry_sexp_t s_sig, gcry_sexp_t s_data, gcry_sexp_t s_keyparms)
     {
       rc = _gcry_ecc_gost_verify (data, ec, sig_r, sig_s);
     }
+  else if ((sigflags & PUBKEY_FLAG_SM2))
+    {
+      rc = _gcry_ecc_sm2_verify (data, ec, sig_r, sig_s);
+    }
   else
     {
       rc = _gcry_ecc_ecdsa_verify (data, ec, sig_r, sig_s);
@@ -918,6 +931,13 @@ ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms)
       goto leave;
     }
 
+  if ((ctx.flags & PUBKEY_FLAG_SM2))
+    {
+      /* All encryption will be done, return it.  */
+      rc = _gcry_ecc_sm2_encrypt (r_ciph, data, ec);
+      goto leave;
+    }
+
   /* The following is false: assert( mpi_cmp_ui( R.x, 1 )==0 );, so */
   {
     mpi_point_struct R;  /* Result that we return.  */
@@ -1041,18 +1061,6 @@ ecc_decrypt_raw (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms)
   _gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_DECRYPT,
                                    (nbits = ecc_get_nbits (keyparms)));
 
-  /*
-   * Extract the data.
-   */
-  rc = _gcry_pk_util_preparse_encval (s_data, ecc_names, &l1, &ctx);
-  if (rc)
-    goto leave;
-  rc = sexp_extract_param (l1, NULL, "/e", &data_e, NULL);
-  if (rc)
-    goto leave;
-  if (DBG_CIPHER)
-    log_printmpi ("ecc_decrypt  d_e", data_e);
-
   /*
    * Extract the key.
    */
@@ -1066,6 +1074,27 @@ ecc_decrypt_raw (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms)
       goto leave;
     }
 
+  /*
+   * Extract the data.
+   */
+  rc = _gcry_pk_util_preparse_encval (s_data, ecc_names, &l1, &ctx);
+  if (rc)
+    goto leave;
+  if ((ctx.flags & PUBKEY_FLAG_SM2))
+    {
+      /* All decryption will be done, return it.  */
+      rc = _gcry_ecc_sm2_decrypt (r_plain, l1, ec);
+      goto leave;
+    }
+  else
+    {
+      rc = sexp_extract_param (l1, NULL, "/e", &data_e, NULL);
+      if (rc)
+        goto leave;
+      if (DBG_CIPHER)
+        log_printmpi ("ecc_decrypt  d_e", data_e);
+    }
+
   if (ec->dialect == ECC_DIALECT_SAFECURVE || (flags & PUBKEY_FLAG_DJB_TWEAK))
     enable_specific_point_validation = 1;
   else
diff --git a/cipher/pubkey-util.c b/cipher/pubkey-util.c
index 4a6bf462..c52185de 100644
--- a/cipher/pubkey-util.c
+++ b/cipher/pubkey-util.c
@@ -81,6 +81,11 @@ _gcry_pk_util_parse_flaglist (gcry_sexp_t list,
               encoding = PUBKEY_ENC_RAW;
               flags |= PUBKEY_FLAG_RAW_FLAG; /* Explicitly given.  */
             }
+          else if (!memcmp (s, "sm2", 3))
+            {
+                encoding = PUBKEY_ENC_RAW;
+                flags |= PUBKEY_FLAG_SM2 | PUBKEY_FLAG_RAW_FLAG;
+            }
           else if (!igninvflag)
             rc = GPG_ERR_INV_FLAG;
           break;
@@ -429,6 +434,8 @@ _gcry_pk_util_preparse_sigval (gcry_sexp_t s_sig, const char **algo_names,
         *r_eccflags = PUBKEY_FLAG_EDDSA;
       if (!strcmp (name, "gost"))
         *r_eccflags = PUBKEY_FLAG_GOST;
+      if (!strcmp (name, "sm2"))
+        *r_eccflags = PUBKEY_FLAG_SM2;
     }
 
   *r_parms = l2;
diff --git a/configure.ac b/configure.ac
index 4d4fb49a..e45452ee 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2546,7 +2546,8 @@ LIST_MEMBER(ecc, $enabled_pubkey_ciphers)
 if test "$found" = "1" ; then
    GCRYPT_PUBKEY_CIPHERS="$GCRYPT_PUBKEY_CIPHERS \
                           ecc.lo ecc-curves.lo ecc-misc.lo \
-                          ecc-ecdh.lo ecc-ecdsa.lo ecc-eddsa.lo ecc-gost.lo"
+                          ecc-ecdh.lo ecc-ecdsa.lo ecc-eddsa.lo ecc-gost.lo \
+                          ecc-sm2.lo"
    AC_DEFINE(USE_ECC, 1, [Defined if this module should be included])
 fi
 
diff --git a/src/cipher.h b/src/cipher.h
index 5aac19f1..1fe50890 100644
--- a/src/cipher.h
+++ b/src/cipher.h
@@ -42,6 +42,7 @@
 #define PUBKEY_FLAG_GOST           (1 << 13)
 #define PUBKEY_FLAG_NO_KEYTEST     (1 << 14)
 #define PUBKEY_FLAG_DJB_TWEAK      (1 << 15)
+#define PUBKEY_FLAG_SM2            (1 << 16)
 
 
 enum pk_operation
-- 
2.17.1




More information about the Gcrypt-devel mailing list