[PATCH 2/2] Add crypto pubkey SM2.

Tianjia Zhang tianjia.zhang at linux.alibaba.com
Sun Dec 22 10:20:11 CET 2019


* cipher/Makefile.am (EXTRA_libcipher_la_SOURCES): Add sm2.c.
* cipher/ecc-curves.c (domain_parms): Add sm2p256v1 for SM2.
* cipher/pubkey.c [USE_SM2] (pubkey_list): Add _gcry_pubkey_spec_sm2.
* cipher/sm2.c: New.
* configure.ac (available_pubkey_ciphers): Add sm2.
* src/cipher.h: Add declarations for SM2.
* src/fips.c (algos): Add GCRY_PK_SM2.
* src/gcrypt.h.in (gcry_pk_algos): Add algorithm ID for SM2.
* tests/basic.c (check_pubkey): Add test cases for SM2.
* tests/curves.c (N_CURVES): Update N_CURVES for SM2.

Signed-off-by: Tianjia Zhang <tianjia.zhang at linux.alibaba.com>
---
 cipher/Makefile.am  |    1 +
 cipher/ecc-curves.c |   14 +
 cipher/pubkey.c     |    3 +
 cipher/sm2.c        | 1161 +++++++++++++++++++++++++++++++++++++++++++
 configure.ac        |    8 +-
 src/cipher.h        |    1 +
 src/fips.c          |    1 +
 src/gcrypt.h.in     |    3 +-
 tests/basic.c       |  130 ++++-
 tests/curves.c      |    2 +-
 10 files changed, 1315 insertions(+), 9 deletions(-)
 create mode 100644 cipher/sm2.c

diff --git a/cipher/Makefile.am b/cipher/Makefile.am
index 020a9616..2cfb29e1 100644
--- a/cipher/Makefile.am
+++ b/cipher/Makefile.am
@@ -91,6 +91,7 @@ EXTRA_libcipher_la_SOURCES = \
 	idea.c \
 	gost28147.c gost.h \
 	gostr3411-94.c \
+	sm2.c \
 	md4.c \
 	md5.c \
 	rijndael.c rijndael-internal.h rijndael-tables.h   \
diff --git a/cipher/ecc-curves.c b/cipher/ecc-curves.c
index 52872c5e..1592d23a 100644
--- a/cipher/ecc-curves.c
+++ b/cipher/ecc-curves.c
@@ -115,6 +115,8 @@ static const struct
 
     { "secp256k1", "1.3.132.0.10" },
 
+    { "sm2p256v1", "1.2.156.10197.1.301" },
+
     { NULL, NULL}
   };
 
@@ -512,6 +514,18 @@ static const ecc_domain_parms_t domain_parms[] =
       1
     },
 
+    {
+      "sm2p256v1", 256, 0,
+      MPI_EC_WEIERSTRASS, ECC_DIALECT_STANDARD,
+      "0xfffffffeffffffffffffffffffffffffffffffff00000000ffffffffffffffff",
+      "0xfffffffeffffffffffffffffffffffffffffffff00000000fffffffffffffffc",
+      "0x28e9fa9e9d9f5e344d5a9e4bcf6509a7f39789f515ab8f92ddbcbd414d940e93",
+      "0xfffffffeffffffffffffffffffffffff7203df6b21c6052b53bbf40939d54123",
+      "0x32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7",
+      "0xbc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0",
+      1
+    },
+
     { NULL, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL }
   };
 
diff --git a/cipher/pubkey.c b/cipher/pubkey.c
index 4c07e33b..1c3836bc 100644
--- a/cipher/pubkey.c
+++ b/cipher/pubkey.c
@@ -47,6 +47,9 @@ static gcry_pk_spec_t * const pubkey_list[] =
 #endif
 #if USE_ELGAMAL
     &_gcry_pubkey_spec_elg,
+#endif
+#if USE_SM2
+    &_gcry_pubkey_spec_sm2,
 #endif
     NULL
   };
diff --git a/cipher/sm2.c b/cipher/sm2.c
new file mode 100644
index 00000000..8b7d6bec
--- /dev/null
+++ b/cipher/sm2.c
@@ -0,0 +1,1161 @@
+/* sm2.c - SM2 implementation
+ * Copyright (C) 2019 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)
+
+
+static const char *sm2_names[] =
+  {
+    "sm2",
+    "1.2.156.10197.1.301",
+    NULL,
+  };
+
+
+
+/*********************************************
+ **************  interface  ******************
+ *********************************************/
+
+static gcry_err_code_t
+sm2_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey)
+{
+  gpg_err_code_t rc;
+  gcry_mpi_t Gx = NULL;
+  gcry_mpi_t Gy = NULL;
+  gcry_mpi_t Qx = NULL;
+  gcry_mpi_t Qy = NULL;
+  mpi_ec_t ec = NULL;
+  gcry_sexp_t curve_info = NULL;
+  gcry_sexp_t curve_flags = NULL;
+  gcry_mpi_t base = NULL;
+  gcry_mpi_t public = NULL;
+  int flags = 0;
+
+  rc = _gcry_mpi_ec_internal_new (&ec, &flags, "ecgen curve", genparms, NULL);
+  if (rc)
+    goto leave;
+
+  rc = _gcry_ecc_nist_generate_key (ec, flags, &Qx, &Qy);
+  if (rc)
+    goto leave;
+
+  /* Copy data to the result.  */
+  Gx = mpi_new (0);
+  Gy = mpi_new (0);
+  if (_gcry_mpi_ec_get_affine (Gx, Gy, ec->G, ec))
+    log_fatal ("ecgen: Failed to get affine coordinates for %s\n", "G");
+  base = _gcry_ecc_ec2os (Gx, Gy, ec->p);
+
+  if (!Qx)
+    {
+      Qx = mpi_new (0);
+      Qy = mpi_new (0);
+      if (_gcry_mpi_ec_get_affine (Qx, Qy, ec->Q, ec))
+        log_fatal ("ecgen: Failed to get affine coordinates for %s\n", "Q");
+    }
+  public = _gcry_ecc_ec2os (Qx, Qy, ec->p);
+
+  if (ec->name)
+    {
+      rc = sexp_build (&curve_info, NULL, "(curve %s)", ec->name);
+      if (rc)
+        goto leave;
+    }
+
+  if (flags & PUBKEY_FLAG_PARAM)
+    {
+      rc = sexp_build (&curve_flags, NULL, "(flags param)");
+      if (rc)
+        goto leave;
+    }
+
+  if ((flags & PUBKEY_FLAG_PARAM) && ec->name)
+    rc = sexp_build (r_skey, NULL,
+                     "(key-data"
+                     " (public-key"
+                     "  (sm2%S%S(p%m)(a%m)(b%m)(g%m)(n%m)(h%u)(q%m)))"
+                     " (private-key"
+                     "  (sm2%S%S(p%m)(a%m)(b%m)(g%m)(n%m)(h%u)(q%m)(d%m)))"
+                     " )",
+                     curve_info, curve_flags,
+                     ec->p, ec->a, ec->b, base, ec->n, ec->h, public,
+                     curve_info, curve_flags,
+                     ec->p, ec->a, ec->b, base, ec->n, ec->h, public,
+                     ec->d);
+  else
+    rc = sexp_build (r_skey, NULL,
+                     "(key-data"
+                     " (public-key"
+                     "  (sm2%S%S(q%m)))"
+                     " (private-key"
+                     "  (sm2%S%S(q%m)(d%m)))"
+                     " )",
+                     curve_info, curve_flags, public,
+                     curve_info, curve_flags, public, ec->d);
+  if (rc)
+    goto leave;
+
+  if (DBG_CIPHER)
+    {
+      log_printmpi ("ecgen result  p", ec->p);
+      log_printmpi ("ecgen result  a", ec->a);
+      log_printmpi ("ecgen result  b", ec->b);
+      log_printmpi ("ecgen result  G", base);
+      log_printmpi ("ecgen result  n", ec->n);
+      log_debug    ("ecgen result  h:+%02x\n", ec->h);
+      log_printmpi ("ecgen result  Q", public);
+      log_printmpi ("ecgen result  d", ec->d);
+    }
+
+ leave:
+  mpi_free (public);
+  mpi_free (base);
+  mpi_free (Gx);
+  mpi_free (Gy);
+  mpi_free (Qx);
+  mpi_free (Qy);
+  _gcry_mpi_ec_free (ec);
+  sexp_release (curve_flags);
+  sexp_release (curve_info);
+  return rc;
+}
+
+
+/* Key derivation function from X9.63/SECG */
+static gcry_err_code_t
+kdf_x9_63 (int algo, const void *in, size_t inlen, void *out, size_t outlen)
+{
+  gcry_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;
+}
+
+
+/* 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
+ */
+static gcry_err_code_t
+sm2_encrypt (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms)
+{
+  gcry_err_code_t rc;
+  struct pk_encoding_ctx ctx;
+  gcry_mpi_t data = NULL;
+  mpi_ec_t ec = NULL;
+  int flags = 0;
+
+  _gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_ENCRYPT,
+                                   _gcry_ecc_get_nbits (keyparms));
+
+  /* Extract the key. */
+  rc = _gcry_mpi_ec_internal_new (&ec, &flags, "sm2_encrypt", keyparms, NULL);
+  if (rc)
+    goto leave;
+
+  /* Extract the data. */
+  rc = _gcry_pk_util_data_to_mpi (s_data, &data, &ctx);
+  if (rc)
+    goto leave;
+
+  if (DBG_CIPHER)
+    log_mpidump ("sm2_encrypt data", data);
+
+  if (!ec->p || !ec->a || !ec->b || !ec->G || !ec->n || !ec->Q)
+    {
+      rc = GPG_ERR_NO_OBJ;
+      goto leave;
+    }
+
+  {
+    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 (data, 0, &inlen, NULL);
+    if (!in)
+      {
+        rc = gpg_err_code_from_syserror ();
+        goto leave_main;
+      }
+
+    cipher = xtrymalloc (inlen);
+    if (!cipher)
+      {
+        rc = gpg_err_code_from_syserror ();
+        goto leave_main;
+      }
+
+    /* 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_main;
+      }
+
+    /* [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_main;
+      }
+
+    /* 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_main;
+
+    /* 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_main;
+    _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_main;
+      }
+
+    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(sm2(a%M)(b%M)(c%M)))",
+                         c1, c3, c2);
+
+        mpi_free (c1);
+        mpi_free (c3);
+        mpi_free (c2);
+      }
+
+  leave_main:
+    _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);
+  }
+
+ leave:
+  _gcry_mpi_release (data);
+  _gcry_mpi_ec_free (ec);
+  _gcry_pk_util_free_encoding_ctx (&ctx);
+  if (DBG_CIPHER)
+    log_debug ("sm2_encrypt    => %s\n", gpg_strerror (rc));
+  return rc;
+}
+
+
+static gcry_err_code_t
+sm2_decrypt (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms)
+{
+  gcry_err_code_t rc;
+  struct pk_encoding_ctx ctx;
+  gcry_sexp_t l1 = NULL;
+  gcry_mpi_t data_c1 = NULL;
+  gcry_mpi_t data_c3 = NULL;
+  gcry_mpi_t data_c2 = NULL;
+  mpi_ec_t ec = NULL;
+  int flags = 0;
+
+  _gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_DECRYPT,
+                                   _gcry_ecc_get_nbits (keyparms));
+
+  /* extract the data */
+  rc = _gcry_pk_util_preparse_encval (s_data, sm2_names, &l1, &ctx);
+  if (rc)
+    goto leave;
+  if (ctx.encoding != PUBKEY_ENC_UNKNOWN)
+    {
+      rc = GPG_ERR_ENCODING_PROBLEM;
+      goto leave;
+    }
+
+  rc = sexp_extract_param (l1, NULL, "/a/b/c", &data_c1, &data_c3, &data_c2, NULL);
+  if (rc)
+    goto leave;
+
+  /* extract the key */
+  rc = _gcry_mpi_ec_internal_new (&ec, &flags, "sm2_decrypt", keyparms, NULL);
+
+  if (!ec->p || !ec->a || !ec->b || !ec->G || !ec->n || !ec->d)
+    {
+      rc = GPG_ERR_NO_OBJ;
+      goto leave;
+    }
+
+  {
+    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);
+  _gcry_mpi_ec_free (ec);
+  sexp_release (l1);
+  _gcry_pk_util_free_encoding_ctx (&ctx);
+  if (DBG_CIPHER)
+    log_debug ("sm2_decrypt    => %s\n", gpg_strerror (rc));
+  return rc;
+}
+
+
+static gcry_err_code_t
+sm2_sign (gcry_sexp_t *r_sig, gcry_sexp_t s_data, gcry_sexp_t keyparms)
+{
+  gcry_err_code_t rc;
+  struct pk_encoding_ctx ctx;
+  gcry_mpi_t data = NULL;
+  gcry_mpi_t hash = NULL;
+  mpi_ec_t ec = NULL;
+  int flags;
+
+  _gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_SIGN, 0);
+
+  /* Extract the data */
+  rc = _gcry_pk_util_data_to_mpi (s_data, &data, &ctx);
+  if (rc)
+    goto leave;
+  if (mpi_is_opaque(data))
+    {
+      const void *buf;
+      unsigned int nbits;
+      buf = mpi_get_opaque (data, &nbits);
+      rc = _gcry_mpi_scan (&hash, GCRYMPI_FMT_USG, buf, (nbits + 7) / 8, NULL);
+      if (rc)
+        goto leave;
+    }
+  else
+    hash = data;
+
+  /* Extract the key */
+  rc = _gcry_mpi_ec_internal_new (&ec, &flags, "sm2_sign", keyparms, NULL);
+  if (rc)
+    goto leave;
+  if (!ec->p || !ec->a || !ec->b || !ec->G || !ec->n || !ec->d)
+    {
+      rc = GPG_ERR_NO_OBJ;
+      goto leave;
+    }
+
+  {
+    gcry_mpi_t sig_r = NULL;
+    gcry_mpi_t sig_s = NULL;
+    gcry_mpi_t tmp = NULL;
+    gcry_mpi_t k = NULL;
+    gcry_mpi_t rk = NULL;
+    mpi_point_struct kG;
+    gcry_mpi_t x1;
+
+    point_init (&kG);
+    x1 = mpi_new (0);
+    sig_r = mpi_new (0);
+    sig_s = mpi_new (0);
+    rk = mpi_new (0);
+    tmp = mpi_new (0);
+
+    for (;;)
+      {
+        /* 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, NULL, &kG, ec))
+          {
+            rc = GPG_ERR_INV_DATA;
+            goto leave_main;
+          }
+
+        /* r = (e + x1) % n */
+        mpi_addm (sig_r, hash, x1, ec->n);
+
+        /* r != 0 && r + k != n */
+        if (mpi_cmp_ui (sig_r, 0) == 0)
+          continue;
+        mpi_add (rk, sig_r, k);
+        if (mpi_cmp (rk, ec->n) == 0)
+          continue;
+
+        /* s = ((d + 1)^-1 * (k - rd)) % n */
+        mpi_addm (sig_s, ec->d, GCRYMPI_CONST_ONE, ec->n);
+        mpi_invm (sig_s, sig_s, ec->n);
+        mpi_mulm (tmp, sig_r, ec->d, ec->n);
+        mpi_subm (tmp, k, tmp, ec->n);
+        mpi_mulm (sig_s, sig_s, tmp, ec->n);
+
+        break;
+      }
+
+    rc = sexp_build (r_sig, NULL, "(sig-val(sm2(r%M)(s%M)))", sig_r, sig_s);
+
+  leave_main:
+    point_free (&kG);
+    mpi_free (x1);
+    mpi_free (k);
+    mpi_free (rk);
+    mpi_free (sig_r);
+    mpi_free (sig_s);
+    mpi_free (tmp);
+  }
+
+ leave:
+  _gcry_mpi_ec_free (ec);
+  if (hash != data)
+    mpi_free (hash);
+  mpi_free (data);
+  _gcry_pk_util_free_encoding_ctx (&ctx);
+  if (DBG_CIPHER)
+    log_debug ("sm2_sign      => %s\n", gpg_strerror (rc));
+  return rc;
+}
+
+
+static gcry_err_code_t
+sm2_verify (gcry_sexp_t s_sig, gcry_sexp_t s_data, gcry_sexp_t keyparms)
+{
+  gcry_err_code_t rc;
+  struct pk_encoding_ctx ctx;
+  gcry_sexp_t l1 = NULL;
+  gcry_mpi_t data = NULL;
+  gcry_mpi_t hash = NULL;
+  gcry_mpi_t sig_r = NULL;
+  gcry_mpi_t sig_s = NULL;
+  mpi_ec_t ec = NULL;
+  int sigflags;
+  int flags;
+
+  _gcry_pk_util_init_encoding_ctx (&ctx, PUBKEY_OP_VERIFY,
+                                   _gcry_ecc_get_nbits (keyparms));
+
+  /* Extract the data */
+  rc = _gcry_pk_util_data_to_mpi (s_data, &data, &ctx);
+  if (rc)
+    goto leave;
+  if (mpi_is_opaque (data))
+    {
+      const void *buf;
+      unsigned int nbits;
+      buf = mpi_get_opaque (data, &nbits);
+      rc = _gcry_mpi_scan (&hash, GCRYMPI_FMT_USG, buf, (nbits + 7) / 8, NULL);
+      if (rc)
+        goto leave;
+    }
+  else
+    hash = data;
+
+  rc = _gcry_pk_util_preparse_sigval (s_sig, sm2_names, &l1, &sigflags);
+  if (rc)
+    goto leave;
+  rc = sexp_extract_param (l1, NULL, "rs", &sig_r, &sig_s, NULL);
+  if (rc)
+    goto leave;
+
+  /* Extract the key */
+  rc = _gcry_mpi_ec_internal_new (&ec, &flags, "sm2_verify", keyparms, NULL);
+  if (rc)
+    goto leave;
+  if (!ec->p || !ec->a || !ec->b || !ec->G || !ec->n || !ec->Q)
+    {
+      rc = GPG_ERR_NO_OBJ;
+      goto leave;
+    }
+
+  {
+    gcry_mpi_t t = NULL;
+    mpi_point_struct sG, tP;
+    gcry_mpi_t x1, y1;
+
+    point_init (&sG);
+    point_init (&tP);
+    x1 = mpi_new (0);
+    y1 = mpi_new (0);
+    t = mpi_new (0);
+
+    /* r, s in [1, n-1] */
+    if (mpi_cmp_ui (sig_r, 1) < 0 || mpi_cmp (sig_r, ec->n) > 0 ||
+        mpi_cmp_ui (sig_s, 1) < 0 || mpi_cmp (sig_s, ec->n) > 0)
+      {
+        rc = GPG_ERR_BAD_SIGNATURE;
+        goto leave_main;
+      }
+
+    /* t = (r + s) % n, t == 0 */
+    mpi_addm (t, sig_r, sig_s, ec->n);
+    if (mpi_cmp_ui (t, 0) == 0)
+      {
+        rc = GPG_ERR_BAD_SIGNATURE;
+        goto leave_main;
+      }
+
+    /* sG + tP = (x1, y1) */
+    _gcry_mpi_ec_mul_point (&sG, sig_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))
+      {
+        rc = GPG_ERR_INV_DATA;
+        goto leave_main;
+      }
+
+    /* R = (e + x1) % n */
+    mpi_addm (t, hash, x1, ec->n);
+
+    /* check R == r */
+    if (mpi_cmp (t, sig_r))
+      rc = GPG_ERR_BAD_SIGNATURE;
+    else
+      rc = 0;
+
+  leave_main:
+    point_free (&sG);
+    point_free (&tP);
+    mpi_free (x1);
+    mpi_free (y1);
+    mpi_free (t);
+  }
+
+ leave:
+  _gcry_mpi_ec_free (ec);
+  sexp_release (l1);
+  if (hash != data)
+    mpi_free (hash);
+  mpi_free (data);
+  _gcry_pk_util_free_encoding_ctx (&ctx);
+  if (DBG_CIPHER)
+    log_debug ("sm2_verify    => %s\n", rc ? gpg_strerror (rc) : "Good");
+  return rc;
+}
+
+
+static const char *
+selftest_genkey (gcry_sexp_t *pkey, gcry_sexp_t *skey)
+{
+  const char *errtxt;
+  gpg_err_code_t err;
+  gcry_sexp_t key_spec = NULL;
+  gcry_sexp_t key = NULL;
+  gcry_sexp_t pub_key = NULL;
+  gcry_sexp_t sec_key = NULL;
+  static const char genkey[] = "(genkey (sm2 (curve sm2p256v1)))";
+  unsigned char keygrip[20];
+
+  errtxt = "build key spec failed";
+  err = sexp_sscan (&key_spec, NULL, genkey, strlen(genkey));
+  if (err)
+    goto leave;
+
+  errtxt = "genkey failed";
+  err = _gcry_pk_genkey (&key, key_spec);
+  if (err)
+    goto leave;
+
+  errtxt = "encrypt signature validity failed";
+  pub_key = _gcry_sexp_find_token (key, "public-key", 0);
+  if (!pub_key)
+    goto leave;
+  sec_key = _gcry_sexp_find_token (key, "private-key", 0);
+  if (!sec_key)
+    goto leave;
+
+  errtxt = "testkey failed";
+  err = _gcry_pk_testkey (sec_key);
+  if (err)
+    goto leave;
+
+  errtxt = "get keygrip failed";
+  if (!_gcry_pk_get_keygrip (pub_key, keygrip))
+    goto leave;
+
+  *pkey = pub_key;
+  *skey = sec_key;
+
+  sexp_release (key_spec);
+  sexp_release (key);
+  return NULL;
+
+ leave:
+  sexp_release (key_spec);
+  sexp_release (key);
+  sexp_release (pub_key);
+  sexp_release (sec_key);
+  return errtxt;
+}
+
+
+#define SM2TEST_CURVE                                                      \
+  "(p #8542D69E4C044F18E8B92435BF6FF7DE457283915C45517D722EDB8B08F1DFC3#)" \
+  "(a #787968B4FA32C3FD2417842E73BBFEFF2F3C848B6831D7E0EC65228B3937E498#)" \
+  "(b #63E4C6D3B23B0C849CF84241484BFE48F61D59A5B16BA06E6E12D1DA27C5249A#)" \
+  "(g #04"                                                                 \
+  "    421DEBD61B62EAB6746434EBC3CC315E32220B3BADD50BDC4C4E6C147FEDD43D"   \
+  "    0680512BCBB42C07D47349D2153B70C4E5D7FDFCBFA36EA1A85841B9E46E09A2#)" \
+  "(n #8542D69E4C044F18E8B92435BF6FF7DD297720630485628D5AE74EE7C32E79B7#)" \
+  "(h #0000000000000000000000000000000000000000000000000000000000000001#)"
+
+
+static const char *
+selftest_encrypt (void)
+{
+#define SM2TEST_PUBLIC_KEY                                                 \
+  "(q #04"                                                                 \
+  "    435B39CCA8F3B508C1488AFC67BE491A0F7BA07E581A0E4849A5CF70628A7E0A"   \
+  "    75DDBA78F15FEECB4C7895E2C1CDF5FE01DEBB2CDBADF45399CCF77BBA076A42#)"
+
+  static const char secret_key[] =
+    "(private-key"
+    " (sm2"
+    SM2TEST_CURVE
+    SM2TEST_PUBLIC_KEY
+    "  (d #1649AB77A00637BD5E2EFE283FBF353534AA7F7CB89463F208DDBC2920BB0DA0#)"
+    "))";
+  static const char public_key[] =
+    "(public-key"
+    " (sm2"
+    SM2TEST_CURVE
+    SM2TEST_PUBLIC_KEY
+    "))";
+#undef SM2TEST_PUBLIC_KEY
+
+  static const char plain_text[] = "encryption standard";
+  static const char plain_fmt[] =
+    "(data\n"
+    " (flags raw)\n"
+    " (hash-algo %s)\n"
+    " (value %m)\n"
+    ")";
+
+  const char *errtxt = NULL;
+  gpg_err_code_t err;
+  gcry_sexp_t skey = NULL;
+  gcry_sexp_t pkey = NULL;
+  gcry_mpi_t m = NULL;
+  gcry_mpi_t calculated_m = NULL;
+  gcry_sexp_t plain = NULL;
+  gcry_sexp_t cipher = NULL;
+  gcry_sexp_t result = NULL;
+  gcry_sexp_t l1 = NULL;
+  gcry_sexp_t l2 = NULL;
+  gcry_sexp_t a = NULL;
+  gcry_sexp_t b = NULL;
+  gcry_sexp_t c = NULL;
+  gcry_sexp_t value = NULL;
+  unsigned int inlen;
+  int cmp;
+
+  errtxt = "build key failed";
+  err = sexp_sscan (&skey, NULL, secret_key, strlen(secret_key));
+  if (err)
+    goto leave;
+  err = sexp_sscan (&pkey, NULL, public_key, strlen(public_key));
+  if (err)
+    goto leave;
+
+  inlen = strlen (plain_text);
+  m = mpi_new (inlen * 8);
+  _gcry_mpi_set_buffer (m, plain_text, inlen, 0);
+
+  err = sexp_build (&plain, NULL, plain_fmt, "sm3", m);
+  if (err)
+   {
+      errtxt = "build plain data failed";
+      goto leave;
+   }
+
+  /* encrypt with pkey */
+  err = _gcry_pk_encrypt (&cipher, plain, pkey);
+  if (err)
+    {
+      errtxt = "encrypt failed";
+      goto leave;
+    }
+
+  errtxt = "encrypt signature validity failed";
+  l1 = _gcry_sexp_find_token (cipher, "enc-val", 0);
+  if (!l1)
+    goto leave;
+  l2 = _gcry_sexp_find_token (l1, "sm2", 0);
+  if (!l2)
+    goto leave;
+  a = _gcry_sexp_find_token (l1, "a", 0);
+  if (!a)
+    goto leave;
+  b = _gcry_sexp_find_token (l1, "b", 0);
+  if (!a)
+    goto leave;
+  c = _gcry_sexp_find_token (l1, "c", 0);
+  if (!a)
+    goto leave;
+
+  /* decrypt with skey */
+  err = _gcry_pk_decrypt (&result, cipher, skey);
+  if (err)
+    {
+      errtxt = "decrypt failed";
+      goto leave;
+    }
+
+  errtxt = "decrypt signature validity failed";
+  value = _gcry_sexp_find_token (result, "value", 0);
+  if (!value)
+    goto leave;
+
+  calculated_m = _gcry_sexp_nth_mpi (value, 1, GCRYMPI_FMT_USG);
+  if (!calculated_m)
+    goto leave;
+
+  cmp = _gcry_mpi_cmp (m, calculated_m);
+  if (cmp)
+    {
+      errtxt = "mismatch decrypt data";
+      goto leave;
+    }
+
+  errtxt = NULL;
+
+ leave:
+  sexp_release (result);
+  sexp_release (l1);
+  sexp_release (l2);
+  sexp_release (a);
+  sexp_release (b);
+  sexp_release (c);
+  sexp_release (value);
+  sexp_release (cipher);
+  sexp_release (plain);
+  sexp_release (skey);
+  sexp_release (pkey);
+  mpi_free (m);
+  mpi_free (calculated_m);
+  return errtxt;
+}
+
+
+static const char *
+selftest_sign (void)
+{
+#define SM2TEST_PUBLIC_KEY                                                 \
+  "(q #04"                                                                 \
+  "    0AE4C7798AA0F119471BEE11825BE46202BB79E2A5844495E97C04FF4DF2548A"   \
+  "    7C0240F88F1CD4E16352A73C17B7F16F07353E53A176D684A9FE0C6BB798E857#)"
+
+  static const char secret_key[] =
+    "(private-key"
+    " (sm2"
+    SM2TEST_CURVE
+    SM2TEST_PUBLIC_KEY
+    "  (d #128B2FA8BD433C6C068C8D803DFF79792A519A55171B1B650C23661D15897263#)"
+    "))";
+  static const char public_key[] =
+    "(public-key"
+    " (sm2"
+    SM2TEST_CURVE
+    SM2TEST_PUBLIC_KEY
+    "))";
+#undef SM2TEST_PUBLIC_KEY
+
+  static const char sample_data[] =
+    "(data (flags raw)"
+    " (hash sm3"
+    " #B524F552CD82B8B028476E005C377FB19A87E6FC682D48BB5D42E3D9B9EFFE76#))";
+  static const char sample_data_bad[] =
+    "(data (flags raw)"
+    " (hash sm3"
+    " #cd85698fecab7843e09bcde2289096872345bcbcdaa8870bbef23d8a110bcd9f#))";
+  static const char signature_r[] =
+    "40F1EC59F793D9F49E09DCEF49130D4194F79FB1EED2CAA55BACDB49C4E755D1";
+  static const char signature_s[] =
+    "6FC6DAC32C5D5CF10C77DFB20F7C2EB667A457872FB09EC56327A67EC7DEEBE7";
+
+  const char *errtxt = NULL;
+  gcry_error_t err;
+  gcry_sexp_t skey = NULL;
+  gcry_sexp_t pkey = NULL;
+  gcry_sexp_t data = NULL;
+  gcry_sexp_t data_bad = NULL;
+  gcry_sexp_t sig = NULL;
+  gcry_sexp_t l1 = NULL;
+  gcry_sexp_t l2 = NULL;
+  gcry_sexp_t lr = NULL;
+  gcry_sexp_t ls = NULL;
+  gcry_mpi_t r = NULL;
+  gcry_mpi_t s = NULL;
+
+  errtxt = "build key failed";
+  err = sexp_sscan (&skey, NULL, secret_key, strlen (secret_key));
+  if (err)
+    goto leave;
+  err = sexp_sscan (&pkey, NULL, public_key, strlen (public_key));
+  if (err)
+    goto leave;
+
+  errtxt = "build data failed";
+  err = sexp_sscan (&data, NULL, sample_data, strlen (sample_data));
+  if (err)
+    goto leave;
+  err = sexp_sscan (&data_bad, NULL, sample_data_bad, strlen (sample_data_bad));
+  if (err)
+    goto leave;
+  /* TODO: r and s are only valid for fixed k in sm2test */
+  err = _gcry_mpi_scan (&r, GCRYMPI_FMT_HEX, signature_r, 0, NULL);
+  if (err)
+    goto leave;
+  err = _gcry_mpi_scan (&s, GCRYMPI_FMT_HEX, signature_s, 0, NULL);
+  if (err)
+    goto leave;
+
+  /* sign with skey */
+  errtxt = "signing failed";
+  err = _gcry_pk_sign (&sig, data, skey);
+  if (err)
+    goto leave;
+
+  /* check against known signature */
+  errtxt = "signature validity failed";
+  l1 = _gcry_sexp_find_token (sig, "sig-val", 0);
+  if (!l1)
+    goto leave;
+  l2 = _gcry_sexp_find_token (l1, "sm2", 0);
+  if (!l2)
+    goto leave;
+  lr = _gcry_sexp_find_token (l2, "r", 0);
+  if (!r)
+    goto leave;
+  ls = _gcry_sexp_find_token (l2, "s", 0);
+  if (!s)
+    goto leave;
+
+  /* verify with pkey */
+  errtxt = "verify failed";
+  err = _gcry_pk_verify (sig, data, pkey);
+  if (err)
+    goto leave;
+
+  errtxt = "bad signature not detected";
+  err = _gcry_pk_verify (sig, data_bad, pkey);
+  if (gcry_err_code (err) != GPG_ERR_BAD_SIGNATURE)
+    goto leave;
+
+  errtxt = NULL;
+
+ leave:
+  sexp_release (skey);
+  sexp_release (pkey);
+  sexp_release (data);
+  sexp_release (data_bad);
+  sexp_release (sig);
+  sexp_release (l1);
+  sexp_release (l2);
+  sexp_release (lr);
+  sexp_release (ls);
+  mpi_free (r);
+  mpi_free (s);
+  return errtxt;
+}
+
+#undef SM2TEST_CURVE
+
+
+static gpg_err_code_t
+run_selftests (int algo, int extended, selftest_report_func_t report)
+{
+  const char *what;
+  const char *errtxt;
+  gcry_sexp_t pkey = NULL;
+  gcry_sexp_t skey = NULL;
+
+  (void)extended;
+
+  if (algo != GCRY_PK_SM2)
+    return GPG_ERR_PUBKEY_ALGO;
+
+  what = "genkey";
+  errtxt = selftest_genkey (&pkey, &skey);
+  if (errtxt)
+    goto failed;
+
+  what = "encrypt";
+  errtxt = selftest_encrypt ();
+  if (errtxt)
+    goto failed;
+
+  what = "sign";
+  errtxt = selftest_sign ();
+  if (errtxt)
+    goto failed;
+
+  return 0;
+
+ failed:
+  sexp_release (pkey);
+  sexp_release (skey);
+  if (report)
+      report ("pubkey", GCRY_PK_SM2, what, errtxt);
+  return GPG_ERR_SELFTEST_FAILED;
+}
+
+
+
+
+gcry_pk_spec_t _gcry_pubkey_spec_sm2 =
+  {
+    GCRY_PK_SM2, { 0, 1 },
+    (GCRY_PK_USAGE_SIGN | GCRY_PK_USAGE_ENCR),
+    "SM2", sm2_names,
+    "pabgnhq", "pabgnhqd", "abc", "rs", "pabgnhq",
+    sm2_generate,
+    _gcry_ecc_check_secret_key,
+    sm2_encrypt,
+    sm2_decrypt,
+    sm2_sign,
+    sm2_verify,
+    _gcry_ecc_get_nbits,
+    run_selftests,
+    _gcry_ecc_compute_keygrip,
+    _gcry_ecc_get_curve,
+    _gcry_ecc_get_param_sexp
+  };
diff --git a/configure.ac b/configure.ac
index 4d4fb49a..893ea5d3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -209,7 +209,7 @@ available_ciphers="$available_ciphers camellia idea salsa20 gost28147 chacha20"
 enabled_ciphers=""
 
 # Definitions for public-key ciphers.
-available_pubkey_ciphers="dsa elgamal rsa ecc"
+available_pubkey_ciphers="dsa elgamal rsa ecc sm2"
 enabled_pubkey_ciphers=""
 
 # Definitions for message digests.
@@ -2550,6 +2550,12 @@ if test "$found" = "1" ; then
    AC_DEFINE(USE_ECC, 1, [Defined if this module should be included])
 fi
 
+LIST_MEMBER(sm2, $enabled_pubkey_ciphers)
+if test "$found" = "1" ; then
+   GCRYPT_PUBKEY_CIPHERS="$GCRYPT_PUBKEY_CIPHERS sm2.lo"
+   AC_DEFINE(USE_SM2, 1, [Defined if this module should be included])
+fi
+
 LIST_MEMBER(crc, $enabled_digests)
 if test "$found" = "1" ; then
    GCRYPT_DIGESTS="$GCRYPT_DIGESTS crc.lo"
diff --git a/src/cipher.h b/src/cipher.h
index 5aac19f1..fc64f440 100644
--- a/src/cipher.h
+++ b/src/cipher.h
@@ -346,6 +346,7 @@ extern gcry_pk_spec_t _gcry_pubkey_spec_elg;
 extern gcry_pk_spec_t _gcry_pubkey_spec_elg_e;
 extern gcry_pk_spec_t _gcry_pubkey_spec_dsa;
 extern gcry_pk_spec_t _gcry_pubkey_spec_ecc;
+extern gcry_pk_spec_t _gcry_pubkey_spec_sm2;
 
 
 #endif /*G10_CIPHER_H*/
diff --git a/src/fips.c b/src/fips.c
index 1ac7f477..63b98ad0 100644
--- a/src/fips.c
+++ b/src/fips.c
@@ -536,6 +536,7 @@ run_pubkey_selftests (int extended)
       GCRY_PK_RSA,
       GCRY_PK_DSA,
       GCRY_PK_ECC,
+      GCRY_PK_SM2,
       0
     };
   int idx;
diff --git a/src/gcrypt.h.in b/src/gcrypt.h.in
index c008f0a6..3b46c904 100644
--- a/src/gcrypt.h.in
+++ b/src/gcrypt.h.in
@@ -1117,7 +1117,8 @@ enum gcry_pk_algos
     GCRY_PK_ELG   = 20,     /* Elgamal       */
     GCRY_PK_ECDSA = 301,    /* (only for external use).  */
     GCRY_PK_ECDH  = 302,    /* (only for external use).  */
-    GCRY_PK_EDDSA = 303     /* (only for external use).  */
+    GCRY_PK_EDDSA = 303,    /* (only for external use).  */
+    GCRY_PK_SM2   = 304     /* sm2 TODO: 304 */
   };
 
 /* Flags describing usage capabilities of a PK algorithm. */
diff --git a/tests/basic.c b/tests/basic.c
index 8337bcfb..9a5318e5 100644
--- a/tests/basic.c
+++ b/tests/basic.c
@@ -12637,6 +12637,77 @@ check_pubkey_sign_ecdsa (int n, gcry_sexp_t skey, gcry_sexp_t pkey)
 }
 
 
+/* Test the public key sign function using the private ket SKEY. PKEY
+   is used for verification.  This variant is only used for SM2.  */
+static void
+check_pubkey_sign_sm2 (int n, gcry_sexp_t skey, gcry_sexp_t pkey)
+{
+  gcry_error_t rc;
+  gcry_sexp_t sig, badhash, hash;
+  int dataidx;
+  static const char baddata[] =
+    "(data\n (flags raw)\n"
+    " (hash sm3 #11223344556677889900AABBCCDDEEFF10203041#))\n";
+  static const struct
+  {
+    const char *data;
+    int expected_rc;
+  } datas[] =
+    {
+      { "(data\n (flags raw)\n"
+	" (hash sm3 #11223344556677889900AABBCCDDEEFF10203040#))\n",
+	0 },
+      { "(data\n (flags raw)\n"
+        " (hash sm3 #B524F552CD82B8B028476E005C377FB1"
+                    "9A87E6FC682D48BB5D42E3D9B9EFFE76#))\n",
+        0 },
+      { "(data\n (flags oaep)\n"
+	" (hash sm3 #11223344556677889900AABBCCDDEEFF10203040#))\n",
+	GPG_ERR_CONFLICT },
+      {	"(data\n (flags )\n"
+	" (hash sm3 #11223344556677889900AABBCCDDEEFF10203040#))\n",
+	GPG_ERR_CONFLICT },
+      {	"(data\n (flags )\n" " (value #11223344556677889900AA#))\n",
+	0 },
+      {	"(data\n (flags )\n" " (value #0090223344556677889900AA#))\n",
+	0 },
+      { "(data\n (flags raw)\n" " (value #11223344556677889900AA#))\n",
+	0 },
+      { NULL }
+    };
+
+  rc = gcry_sexp_sscan (&badhash, NULL, baddata, strlen (baddata));
+  if (rc)
+    die ("converting data failed: %s\n", gpg_strerror (rc));
+
+  for (dataidx = 0; datas[dataidx].data; dataidx++)
+    {
+      if (verbose)
+	fprintf (stderr, "  test %d, signature test %d (SM2)\n",
+                 n, dataidx);
+
+      rc = gcry_sexp_sscan (&hash, NULL, datas[dataidx].data,
+			    strlen (datas[dataidx].data));
+      if (rc)
+	die ("converting data failed: %s\n", gpg_strerror (rc));
+
+      rc = gcry_pk_sign (&sig, hash, skey);
+      if (gcry_err_code (rc) != datas[dataidx].expected_rc)
+	fail ("gcry_pk_sign failed: %s\n", gpg_strerror (rc));
+
+      if (!rc)
+	verify_one_signature (pkey, hash, badhash, sig);
+
+      gcry_sexp_release (sig);
+      sig = NULL;
+      gcry_sexp_release (hash);
+      hash = NULL;
+    }
+
+  gcry_sexp_release (badhash);
+}
+
+
 static void
 check_pubkey_crypt (int n, gcry_sexp_t skey, gcry_sexp_t pkey, int algo)
 {
@@ -12751,6 +12822,13 @@ check_pubkey_crypt (int n, gcry_sexp_t skey, gcry_sexp_t pkey, int algo)
 	NULL,
 	0,
 	GPG_ERR_CONFLICT },
+      { GCRY_PK_SM2,
+        "(data\n (flags raw)\n (hash-algo sm3)\n"
+        " (value \"encryption standard\"))\n",
+        NULL,
+        1,
+        0,
+        0 },
       { 0, NULL }
     };
 
@@ -12904,6 +12982,8 @@ do_check_one_pubkey (int n, gcry_sexp_t skey, gcry_sexp_t pkey,
    {
      if (algo == GCRY_PK_ECDSA)
        check_pubkey_sign_ecdsa (n, skey, pkey);
+     else if (algo == GCRY_PK_SM2)
+       check_pubkey_sign_sm2 (n, skey, pkey);
      else
        check_pubkey_sign (n, skey, pkey, algo);
    }
@@ -12936,16 +13016,23 @@ check_one_pubkey (int n, test_spec_pubkey_t spec)
 }
 
 static void
-get_keys_new (gcry_sexp_t *pkey, gcry_sexp_t *skey)
+get_keys_new (gcry_sexp_t *pkey, gcry_sexp_t *skey, int algo)
 {
   gcry_sexp_t key_spec, key, pub_key, sec_key;
   int rc;
   if (verbose)
     fprintf (stderr, "  generating RSA key:");
-  rc = gcry_sexp_new (&key_spec,
-		      in_fips_mode ? "(genkey (rsa (nbits 4:2048)))"
-                      : "(genkey (rsa (nbits 4:1024)(transient-key)))",
-                      0, 1);
+  if (algo == GCRY_PK_RSA)
+    rc = gcry_sexp_new (&key_spec,
+		        in_fips_mode ? "(genkey (rsa (nbits 4:2048)))"
+                        : "(genkey (rsa (nbits 4:1024)(transient-key)))",
+                        0, 1);
+  else if (algo == GCRY_PK_SM2)
+    rc = gcry_sexp_new (&key_spec,
+                        "(genkey (sm2 (curve sm2p256v1)))",
+                        0, 1);
+  else
+    return;
   if (rc)
     die ("error creating S-expression: %s\n", gpg_strerror (rc));
   rc = gcry_pk_genkey (&key, key_spec);
@@ -12971,11 +13058,19 @@ check_one_pubkey_new (int n)
 {
   gcry_sexp_t skey, pkey;
 
-  get_keys_new (&pkey, &skey);
+  /* rsa */
+  get_keys_new (&pkey, &skey, GCRY_PK_RSA);
   do_check_one_pubkey (n, skey, pkey, NULL,
 		       GCRY_PK_RSA, FLAG_SIGN | FLAG_CRYPT);
   gcry_sexp_release (pkey);
   gcry_sexp_release (skey);
+
+  /* sm2 */
+  get_keys_new (&pkey, &skey, GCRY_PK_SM2);
+  do_check_one_pubkey (n, skey, pkey, NULL,
+		       GCRY_PK_SM2, FLAG_SIGN | FLAG_CRYPT);
+  gcry_sexp_release (pkey);
+  gcry_sexp_release (skey);
 }
 
 /* Run all tests for the public key functions. */
@@ -13272,6 +13367,29 @@ check_pubkey (void)
 
       "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
       "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }
+  },
+  { /* sm2test */
+    GCRY_PK_SM2, FLAG_CRYPT | FLAG_SIGN,
+    {
+      "(private-key\n"
+      " (sm2\n"
+      "  (curve sm2p256v1)\n"
+      "  (q #04"
+      "      8759389A34AAAD07ECF4E0C8C2650A4459C8D926EE2378324E0261C52538CB47"
+      "      7528106B1E0B7C8DD5FF29A9C86A89065656EB33154BC0556091EF8AC9D17D78#)"
+      "  (d #41EBDBA9C98CBECCE7249CF18BFD427FF8EA0B2FAB7B9D305D9D9BF4DB6ADFC2#)"
+      "))",
+
+      "(public-key\n"
+      " (sm2\n"
+      "  (curve sm2p256v1)\n"
+      "  (q #04"
+      "      8759389A34AAAD07ECF4E0C8C2650A4459C8D926EE2378324E0261C52538CB47"
+      "      7528106B1E0B7C8DD5FF29A9C86A89065656EB33154BC0556091EF8AC9D17D78#)"
+      "))",
+
+      "\xcb\x30\xc9\x71\x54\x05\xde\x05\x20\x7f"
+      "\xa0\x5b\xce\xb9\x0f\x9d\x03\x17\xeb\x73"}
     }
   };
   int i;
diff --git a/tests/curves.c b/tests/curves.c
index ff244bd1..0dfa2acb 100644
--- a/tests/curves.c
+++ b/tests/curves.c
@@ -33,7 +33,7 @@
 #include "t-common.h"
 
 /* Number of curves defined in ../cipger/ecc-curves.c */
-#define N_CURVES 25
+#define N_CURVES 26
 
 /* A real world sample public key.  */
 static char const sample_key_1[] =
-- 
2.17.1




More information about the Gcrypt-devel mailing list