Import initial code for the SM2 cipher
authortb <tb@openbsd.org>
Wed, 18 Aug 2021 16:04:32 +0000 (16:04 +0000)
committertb <tb@openbsd.org>
Wed, 18 Aug 2021 16:04:32 +0000 (16:04 +0000)
This adds the SM2 algorithm defined in the Chinese standards
GB/T 32918.1-2016, GB/T 32918.2-2016, GB/T 32918.3-2016,
GB/T 32918.4-2016 and GB/T 32918.5-2017.

This is an ISC licensed implementation contributed by Ribose.inc, based
on the same code that was contributed to OpenSSL by Jack Lloyd. The port
to LibreSSL was done by Ronald Tse and Nickolay Olshevsky.

Github PR #105

I made quite a few cleanup passes on this, but more is needed, some
of which will happen in-tree before this is linked to the build.

ok deraadt inoguchi (a long time ago), jsing

lib/libcrypto/sm2/sm2.h [new file with mode: 0644]
lib/libcrypto/sm2/sm2_crypt.c [new file with mode: 0644]
lib/libcrypto/sm2/sm2_err.c [new file with mode: 0644]
lib/libcrypto/sm2/sm2_locl.h [new file with mode: 0644]
lib/libcrypto/sm2/sm2_pmeth.c [new file with mode: 0644]
lib/libcrypto/sm2/sm2_sign.c [new file with mode: 0644]
lib/libcrypto/sm2/sm2_za.c [new file with mode: 0644]

diff --git a/lib/libcrypto/sm2/sm2.h b/lib/libcrypto/sm2/sm2.h
new file mode 100644 (file)
index 0000000..92eef8a
--- /dev/null
@@ -0,0 +1,147 @@
+/*     $OpenBSD: sm2.h,v 1.1.1.1 2021/08/18 16:04:32 tb Exp $ */
+/*
+ * Copyright (c) 2017, 2019 Ribose Inc
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef HEADER_SM2_H
+#define HEADER_SM2_H
+
+#include <openssl/opensslconf.h>
+
+#include <openssl/ec.h>
+#include <openssl/ecdsa.h>
+
+#ifdef OPENSSL_NO_SM2
+#error SM2 is disabled.
+#endif
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+/*
+ * SM2 signature generation.
+ */
+int SM2_sign(const unsigned char *dgst, int dgstlen, unsigned char *sig,
+    unsigned int *siglen, EC_KEY *eckey);
+
+/*
+ * SM2 signature verification. Assumes input is an SM3 digest
+ */
+int SM2_verify(const unsigned char *dgst, int dgstlen, const unsigned char *sig,
+    int siglen, EC_KEY *eckey);
+
+/*
+ * SM2 encryption
+ */
+int SM2_ciphertext_size(const EC_KEY *key, const EVP_MD *digest, size_t msg_len,
+    size_t *c_size);
+
+int SM2_plaintext_size(const EC_KEY *key, const EVP_MD *digest, size_t msg_len,
+    size_t *pl_size);
+
+int SM2_encrypt(const EC_KEY *key, const EVP_MD *digest, const uint8_t *msg,
+    size_t msg_len, uint8_t *ciphertext_buf, size_t *ciphertext_len);
+
+int SM2_decrypt(const EC_KEY *key, const EVP_MD *digest,
+    const uint8_t *ciphertext, size_t ciphertext_len, uint8_t *ptext_buf,
+    size_t *ptext_len);
+
+/* BEGIN ERROR CODES */
+/* The following lines are auto generated by the script mkerr.pl. Any changes
+ * made after this point may be overwritten when the script is next run.
+ */
+void ERR_load_SM2_strings(void);
+
+/* Error codes for the SM2 functions. */
+
+/* Function codes. */
+# define SM2_F_PKEY_SM2_CTRL                              274
+# define SM2_F_PKEY_SM2_CTRL_STR                          275
+# define SM2_F_PKEY_SM2_KEYGEN                            276
+# define SM2_F_PKEY_SM2_PARAMGEN                          277
+# define SM2_F_PKEY_SM2_SIGN                              278
+# define SM2_F_PKEY_SM2_VERIFY                            279
+# define SM2_F_PKEY_SM2_ENCRYPT                           280
+# define SM2_F_PKEY_SM2_DECRYPT                           281
+
+/* Reason codes. */
+# define SM2_R_ASN1_ERROR                                 115
+# define SM2_R_ASN5_ERROR                                 1150
+# define SM2_R_BAD_SIGNATURE                              156
+# define SM2_R_BIGNUM_OUT_OF_RANGE                        144
+# define SM2_R_BUFFER_TOO_SMALL                           100
+# define SM2_R_COORDINATES_OUT_OF_RANGE                   146
+# define SM2_R_CURVE_DOES_NOT_SUPPORT_ECDH                160
+# define SM2_R_CURVE_DOES_NOT_SUPPORT_SIGNING             159
+# define SM2_R_D2I_ECPKPARAMETERS_FAILURE                 117
+# define SM2_R_DECODE_ERROR                               142
+# define SM2_R_DIGEST_FAILURE                             163
+# define SM2_R_DISCRIMINANT_IS_ZERO                       118
+# define SM2_R_EC_GROUP_NEW_BY_NAME_FAILURE               119
+# define SM2_R_FIELD_TOO_LARGE                            143
+# define SM2_R_GF2M_NOT_SUPPORTED                         147
+# define SM2_R_GROUP2PKPARAMETERS_FAILURE                 120
+# define SM2_R_I2D_ECPKPARAMETERS_FAILURE                 121
+# define SM2_R_INCOMPATIBLE_OBJECTS                       101
+# define SM2_R_INVALID_ARGUMENT                           112
+# define SM2_R_INVALID_COMPRESSED_POINT                   110
+# define SM2_R_INVALID_COMPRESSION_BIT                    109
+# define SM2_R_INVALID_CURVE                              141
+# define SM2_R_INVALID_DIGEST                             151
+# define SM2_R_INVALID_DIGEST_TYPE                        138
+# define SM2_R_INVALID_ENCODING                           102
+# define SM2_R_INVALID_FIELD                              103
+# define SM2_R_INVALID_FORM                               104
+# define SM2_R_INVALID_GROUP_ORDER                        122
+# define SM2_R_INVALID_KEY                                116
+# define SM2_R_INVALID_OUTPUT_LENGTH                      161
+# define SM2_R_INVALID_PEER_KEY                           133
+# define SM2_R_INVALID_PENTANOMIAL_BASIS                  132
+# define SM2_R_INVALID_PRIVATE_KEY                        123
+# define SM2_R_INVALID_TRINOMIAL_BASIS                    137
+# define SM2_R_KDF_FAILURE                                162
+# define SM2_R_KDF_PARAMETER_ERROR                        148
+# define SM2_R_KEYS_NOT_SET                               140
+# define SM2_R_MISSING_PARAMETERS                         124
+# define SM2_R_MISSING_PRIVATE_KEY                        125
+# define SM2_R_NEED_NEW_SETUP_VALUES                      157
+# define SM2_R_NOT_A_NIST_PRIME                           135
+# define SM2_R_NOT_IMPLEMENTED                            126
+# define SM2_R_NOT_INITIALIZED                            111
+# define SM2_R_NO_PARAMETERS_SET                          139
+# define SM2_R_NO_PRIVATE_VALUE                           154
+# define SM2_R_OPERATION_NOT_SUPPORTED                    152
+# define SM2_R_PASSED_NULL_PARAMETER                      134
+# define SM2_R_PEER_KEY_ERROR                             149
+# define SM2_R_PKPARAMETERS2GROUP_FAILURE                 127
+# define SM2_R_POINT_ARITHMETIC_FAILURE                   155
+# define SM2_R_POINT_AT_INFINITY                          106
+# define SM2_R_POINT_IS_NOT_ON_CURVE                      107
+# define SM2_R_RANDOM_NUMBER_GENERATION_FAILED            158
+# define SM2_R_SHARED_INFO_ERROR                          150
+# define SM2_R_SLOT_FULL                                  108
+# define SM2_R_UNDEFINED_GENERATOR                        113
+# define SM2_R_UNDEFINED_ORDER                            128
+# define SM2_R_UNKNOWN_GROUP                              129
+# define SM2_R_UNKNOWN_ORDER                              114
+# define SM2_R_UNSUPPORTED_FIELD                          131
+# define SM2_R_WRONG_CURVE_PARAMETERS                     145
+# define SM2_R_WRONG_ORDER                                130
+
+#ifdef  __cplusplus
+}
+#endif
+#endif
diff --git a/lib/libcrypto/sm2/sm2_crypt.c b/lib/libcrypto/sm2/sm2_crypt.c
new file mode 100644 (file)
index 0000000..2bb3b8f
--- /dev/null
@@ -0,0 +1,632 @@
+/*     $OpenBSD: sm2_crypt.c,v 1.1.1.1 2021/08/18 16:04:32 tb Exp $ */
+/*
+ * Copyright (c) 2017, 2019 Ribose Inc
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef OPENSSL_NO_SM2
+
+#include <string.h>
+
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/bn.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/sm2.h>
+
+#include "sm2_locl.h"
+
+typedef struct SM2_Ciphertext_st SM2_Ciphertext;
+
+SM2_Ciphertext *SM2_Ciphertext_new(void);
+void SM2_Ciphertext_free(SM2_Ciphertext *a);
+SM2_Ciphertext *d2i_SM2_Ciphertext(SM2_Ciphertext **a, const unsigned char **in,
+    long len);
+int i2d_SM2_Ciphertext(SM2_Ciphertext *a, unsigned char **out);
+
+struct SM2_Ciphertext_st {
+       BIGNUM *C1x;
+       BIGNUM *C1y;
+       ASN1_OCTET_STRING *C3;
+       ASN1_OCTET_STRING *C2;
+};
+
+static const ASN1_TEMPLATE SM2_Ciphertext_seq_tt[] = {
+       {
+               .flags = 0,
+               .tag = 0,
+               .offset = offsetof(SM2_Ciphertext, C1x),
+               .field_name = "C1x",
+               .item = &BIGNUM_it,
+       },
+       {
+               .flags = 0,
+               .tag = 0,
+               .offset = offsetof(SM2_Ciphertext, C1y),
+               .field_name = "C1y",
+               .item = &BIGNUM_it,
+       },
+       {
+               .flags = 0,
+               .tag = 0,
+               .offset = offsetof(SM2_Ciphertext, C3),
+               .field_name = "C3",
+               .item = &ASN1_OCTET_STRING_it,
+       },
+       {
+               .flags = 0,
+               .tag = 0,
+               .offset = offsetof(SM2_Ciphertext, C2),
+               .field_name = "C2",
+               .item = &ASN1_OCTET_STRING_it,
+       },
+};
+
+const ASN1_ITEM SM2_Ciphertext_it = {
+       .itype = ASN1_ITYPE_SEQUENCE,
+       .utype = V_ASN1_SEQUENCE,
+       .templates = SM2_Ciphertext_seq_tt,
+       .tcount = sizeof(SM2_Ciphertext_seq_tt) / sizeof(ASN1_TEMPLATE),
+       .funcs = NULL,
+       .size = sizeof(SM2_Ciphertext),
+       .sname = "SM2_Ciphertext",
+};
+
+SM2_Ciphertext *
+d2i_SM2_Ciphertext(SM2_Ciphertext **a, const unsigned char **in, long len)
+{
+       return (SM2_Ciphertext *) ASN1_item_d2i((ASN1_VALUE **)a, in, len,
+           &SM2_Ciphertext_it);
+}
+
+int
+i2d_SM2_Ciphertext(SM2_Ciphertext *a, unsigned char **out)
+{
+       return ASN1_item_i2d((ASN1_VALUE *)a, out, &SM2_Ciphertext_it);
+}
+
+SM2_Ciphertext *
+SM2_Ciphertext_new(void)
+{
+       return (SM2_Ciphertext *)ASN1_item_new(&SM2_Ciphertext_it);
+}
+
+void
+SM2_Ciphertext_free(SM2_Ciphertext *a)
+{
+       ASN1_item_free((ASN1_VALUE *)a, &SM2_Ciphertext_it);
+}
+
+static size_t
+ec_field_size(const EC_GROUP *group)
+{
+       /* Is there some simpler way to do this? */
+       BIGNUM *p;
+       size_t field_size = 0;
+
+       if ((p = BN_new()) == NULL)
+               goto err;
+       if (!EC_GROUP_get_curve(group, p, NULL, NULL, NULL))
+               goto err;
+       field_size = BN_num_bytes(p);
+ err:
+       BN_free(p);
+       return field_size;
+}
+
+int
+SM2_plaintext_size(const EC_KEY *key, const EVP_MD *digest, size_t msg_len,
+    size_t *pl_size)
+{
+       size_t field_size, overhead;
+       int md_size;
+
+       if ((field_size = ec_field_size(EC_KEY_get0_group(key))) == 0) {
+               SM2error(SM2_R_INVALID_FIELD);
+               return 0;
+       }
+
+       if ((md_size = EVP_MD_size(digest)) < 0) {
+               SM2error(SM2_R_INVALID_DIGEST);
+               return 0;
+       }
+
+       overhead = 10 + 2 * field_size + md_size;
+       if (msg_len <= overhead) {
+               SM2error(SM2_R_INVALID_ARGUMENT);
+               return 0;
+       }
+
+       *pl_size = msg_len - overhead;
+       return 1;
+}
+
+int
+SM2_ciphertext_size(const EC_KEY *key, const EVP_MD *digest, size_t msg_len,
+    size_t *c_size)
+{
+       size_t asn_size, field_size;
+       int md_size;
+
+       if ((field_size = ec_field_size(EC_KEY_get0_group(key))) == 0) {
+               SM2error(SM2_R_INVALID_FIELD);
+               return 0;
+       }
+
+       if ((md_size = EVP_MD_size(digest)) < 0) {
+               SM2error(SM2_R_INVALID_DIGEST);
+               return 0;
+       }
+
+       asn_size = 2 * ASN1_object_size(0, field_size + 1, V_ASN1_INTEGER) +
+           ASN1_object_size(0, md_size, V_ASN1_OCTET_STRING) +
+           ASN1_object_size(0, msg_len, V_ASN1_OCTET_STRING);
+
+       *c_size = ASN1_object_size(1, asn_size, V_ASN1_SEQUENCE);
+       return 1;
+}
+
+int
+sm2_kdf(uint8_t *key, size_t key_len, uint8_t *secret, size_t secret_len,
+    const EVP_MD *digest)
+{
+       EVP_MD_CTX *hash;
+       uint8_t *hash_buf = NULL;
+       uint32_t ctr = 1;
+       uint8_t ctr_buf[4] = {0};
+       size_t hadd, hlen;
+       int rc = 0;
+
+       if ((hash = EVP_MD_CTX_new()) == NULL) {
+               SM2error(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+
+       if ((hlen = EVP_MD_size(digest)) < 0) {
+               SM2error(SM2_R_INVALID_DIGEST);
+               goto err;
+       }
+       if ((hash_buf = malloc(hlen)) == NULL) {
+               SM2error(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+
+       EVP_MD_CTX_init(hash);
+       while ((key_len > 0) && (ctr != 0)) {
+               if (!EVP_DigestInit_ex(hash, digest, NULL)) {
+                       SM2error(ERR_R_EVP_LIB);
+                       goto err;
+               }
+               if (!EVP_DigestUpdate(hash, secret, secret_len)) {
+                       SM2error(ERR_R_EVP_LIB);
+                       goto err;
+               }
+
+               /* big-endian counter representation */
+               ctr_buf[0] = (ctr >> 24) & 0xff;
+               ctr_buf[1] = (ctr >> 16) & 0xff;
+               ctr_buf[2] = (ctr >> 8) & 0xff;
+               ctr_buf[3] = ctr & 0xff;
+               ctr++;
+
+               if (!EVP_DigestUpdate(hash, ctr_buf, 4)) {
+                       SM2error(ERR_R_EVP_LIB);
+                       goto err;
+               }
+               if (!EVP_DigestFinal(hash, hash_buf, NULL)) {
+                       SM2error(ERR_R_EVP_LIB);
+                       goto err;
+               }
+
+               hadd = key_len > hlen ? hlen : key_len;
+               memcpy(key, hash_buf, hadd);
+               memset(hash_buf, 0, hlen);
+               key_len -= hadd;
+               key += hadd;
+       }
+
+       rc = 1;
+ err:
+       free(hash_buf);
+       EVP_MD_CTX_free(hash);
+       return rc;
+}
+
+int
+SM2_encrypt(const EC_KEY *key, const EVP_MD *digest, const uint8_t *msg,
+    size_t msg_len, uint8_t *ciphertext_buf, size_t *ciphertext_len)
+{
+       SM2_Ciphertext ctext_struct;
+       EVP_MD_CTX *hash = NULL;
+       BN_CTX *ctx = NULL;
+       BIGNUM *order = NULL;
+       BIGNUM *k, *x1, *y1, *x2, *y2;
+       const EC_GROUP *group;
+       const EC_POINT *P;
+       EC_POINT *kG = NULL, *kP = NULL;
+       uint8_t *msg_mask = NULL, *x2y2 = NULL, *C3 = NULL;
+       size_t C3_size, field_size, i, x2size, y2size;
+       int rc = 0;
+       int clen;
+
+       ctext_struct.C2 = NULL;
+       ctext_struct.C3 = NULL;
+
+       if ((hash = EVP_MD_CTX_new()) == NULL) {
+               SM2error(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+
+       if ((group = EC_KEY_get0_group(key)) == NULL) {
+               SM2error(SM2_R_INVALID_KEY);
+               goto err;
+       }
+
+       if ((order = BN_new()) == NULL) {
+               SM2error(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+
+       if (!EC_GROUP_get_order(group, order, NULL)) {
+               SM2error(SM2_R_INVALID_GROUP_ORDER);
+               goto err;
+       }
+
+       if ((P = EC_KEY_get0_public_key(key)) == NULL) {
+               SM2error(SM2_R_INVALID_KEY);
+               goto err;
+       }
+
+       if ((field_size = ec_field_size(group)) == 0) {
+               SM2error(SM2_R_INVALID_FIELD);
+               goto err;
+       }
+
+       if ((C3_size = EVP_MD_size(digest)) < 0) {
+               SM2error(SM2_R_INVALID_DIGEST);
+               goto err;
+       }
+
+       if ((kG = EC_POINT_new(group)) == NULL) {
+               SM2error(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+       if ((kP = EC_POINT_new(group)) == NULL) {
+               SM2error(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+
+       if ((ctx = BN_CTX_new()) == NULL) {
+               SM2error(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+
+       BN_CTX_start(ctx);
+       if ((k = BN_CTX_get(ctx)) == NULL) {
+               SM2error(ERR_R_BN_LIB);
+               goto err;
+       }
+       if ((x1 = BN_CTX_get(ctx)) == NULL) {
+               SM2error(ERR_R_BN_LIB);
+               goto err;
+       }
+       if ((x2 = BN_CTX_get(ctx)) == NULL) {
+               SM2error(ERR_R_BN_LIB);
+               goto err;
+       }
+       if ((y1 = BN_CTX_get(ctx)) == NULL) {
+               SM2error(ERR_R_BN_LIB);
+               goto err;
+       }
+       if ((y2 = BN_CTX_get(ctx)) == NULL) {
+               SM2error(ERR_R_BN_LIB);
+               goto err;
+       }
+
+       if ((x2y2 = calloc(2, field_size)) == NULL) {
+               SM2error(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+
+       if ((C3 = calloc(1, C3_size)) == NULL) {
+               SM2error(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+
+       memset(ciphertext_buf, 0, *ciphertext_len);
+
+       if (!BN_rand_range(k, order)) {
+               SM2error(SM2_R_RANDOM_NUMBER_GENERATION_FAILED);
+               goto err;
+       }
+
+       if (!EC_POINT_mul(group, kG, k, NULL, NULL, ctx)) {
+               SM2error(ERR_R_EC_LIB);
+               goto err;
+       }
+
+       if (!EC_POINT_get_affine_coordinates(group, kG, x1, y1, ctx)) {
+               SM2error(ERR_R_EC_LIB);
+               goto err;
+       }
+
+       if (!EC_POINT_mul(group, kP, NULL, P, k, ctx)) {
+               SM2error(ERR_R_EC_LIB);
+               goto err;
+       }
+
+       if (!EC_POINT_get_affine_coordinates(group, kP, x2, y2, ctx)) {
+               SM2error(ERR_R_EC_LIB);
+               goto err;
+       }
+
+       if ((x2size = BN_num_bytes(x2)) > field_size ||
+           (y2size = BN_num_bytes(y2)) > field_size) {
+               SM2error(SM2_R_BIGNUM_OUT_OF_RANGE);
+               goto err;
+       }
+
+       BN_bn2bin(x2, x2y2 + field_size - x2size);
+       BN_bn2bin(y2, x2y2 + 2 * field_size - y2size);
+
+       if ((msg_mask = calloc(1, msg_len)) == NULL) {
+               SM2error(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+
+       if (!sm2_kdf(msg_mask, msg_len, x2y2, 2 * field_size, digest)) {
+               SM2error(SM2_R_KDF_FAILURE);
+               goto err;
+       }
+
+       for (i = 0; i != msg_len; i++)
+               msg_mask[i] ^= msg[i];
+
+       if (!EVP_DigestInit(hash, digest)) {
+               SM2error(ERR_R_EVP_LIB);
+               goto err;
+       }
+
+       if (!EVP_DigestUpdate(hash, x2y2, field_size)) {
+               SM2error(ERR_R_EVP_LIB);
+               goto err;
+       }
+
+       if (!EVP_DigestUpdate(hash, msg, msg_len)) {
+               SM2error(ERR_R_EVP_LIB);
+               goto err;
+       }
+
+       if (!EVP_DigestUpdate(hash, x2y2 + field_size, field_size)) {
+               SM2error(ERR_R_EVP_LIB);
+               goto err;
+       }
+
+       if (!EVP_DigestFinal(hash, C3, NULL)) {
+               SM2error(ERR_R_EVP_LIB);
+               goto err;
+       }
+
+       ctext_struct.C1x = x1;
+       ctext_struct.C1y = y1;
+       if ((ctext_struct.C3 = ASN1_OCTET_STRING_new()) == NULL) {
+               SM2error(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+       if ((ctext_struct.C2 = ASN1_OCTET_STRING_new()) == NULL) {
+               SM2error(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+       if (!ASN1_OCTET_STRING_set(ctext_struct.C3, C3, C3_size)) {
+               SM2error(ERR_R_INTERNAL_ERROR);
+               goto err;
+       }
+       if (!ASN1_OCTET_STRING_set(ctext_struct.C2, msg_mask, msg_len)) {
+               SM2error(ERR_R_INTERNAL_ERROR);
+               goto err;
+       }
+
+       if ((clen = i2d_SM2_Ciphertext(&ctext_struct, &ciphertext_buf)) < 0) {
+               SM2error(ERR_R_INTERNAL_ERROR);
+               goto err;
+       }
+
+       *ciphertext_len = clen;
+       rc = 1;
+
+ err:
+       ASN1_OCTET_STRING_free(ctext_struct.C2);
+       ASN1_OCTET_STRING_free(ctext_struct.C3);
+       free(msg_mask);
+       free(x2y2);
+       free(C3);
+       EVP_MD_CTX_free(hash);
+       BN_CTX_end(ctx);
+       BN_CTX_free(ctx);
+       EC_POINT_free(kG);
+       EC_POINT_free(kP);
+       BN_free(order);
+       return rc;
+}
+
+int
+SM2_decrypt(const EC_KEY *key, const EVP_MD *digest, const uint8_t *ciphertext,
+    size_t ciphertext_len, uint8_t *ptext_buf, size_t *ptext_len)
+{
+       SM2_Ciphertext *sm2_ctext = NULL;
+       EVP_MD_CTX *hash = NULL;
+       BN_CTX *ctx = NULL;
+       BIGNUM *x2, *y2;
+       const EC_GROUP *group;
+       EC_POINT *C1 = NULL;
+       const uint8_t *C2, *C3;
+       uint8_t *computed_C3 = NULL, *msg_mask = NULL, *x2y2 = NULL;
+       size_t field_size, x2size, y2size;
+       int msg_len = 0, rc = 0;
+       int hash_size, i;
+
+       if ((group = EC_KEY_get0_group(key)) == NULL) {
+               SM2error(SM2_R_INVALID_KEY);
+               goto err;
+       }
+
+       if ((field_size = ec_field_size(group)) == 0) {
+               SM2error(SM2_R_INVALID_FIELD);
+               goto err;
+       }
+
+       if ((hash_size = EVP_MD_size(digest)) < 0) {
+               SM2error(SM2_R_INVALID_DIGEST);
+               goto err;
+       }
+
+       memset(ptext_buf, 0xFF, *ptext_len);
+
+       if ((sm2_ctext = d2i_SM2_Ciphertext(NULL, &ciphertext,
+           ciphertext_len)) == NULL) {
+               SM2error(SM2_R_ASN1_ERROR);
+               goto err;
+       }
+
+       if (sm2_ctext->C3->length != hash_size) {
+               SM2error(SM2_R_INVALID_ENCODING);
+               goto err;
+       }
+
+       C2 = sm2_ctext->C2->data;
+       C3 = sm2_ctext->C3->data;
+       msg_len = sm2_ctext->C2->length;
+
+       if ((ctx = BN_CTX_new()) == NULL) {
+               SM2error(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+
+       BN_CTX_start(ctx);
+       if ((x2 = BN_CTX_get(ctx)) == NULL) {
+               SM2error(ERR_R_BN_LIB);
+               goto err;
+       }
+       if ((y2 = BN_CTX_get(ctx)) == NULL) {
+               SM2error(ERR_R_BN_LIB);
+               goto err;
+       }
+
+       if ((msg_mask = calloc(1, msg_len)) == NULL) {
+               SM2error(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+       if ((x2y2 = calloc(2, field_size)) == NULL) {
+               SM2error(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+       if ((computed_C3 = calloc(1, hash_size)) == NULL) {
+               SM2error(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+
+       if ((C1 = EC_POINT_new(group)) == NULL) {
+               SM2error(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+
+       if (!EC_POINT_set_affine_coordinates(group, C1, sm2_ctext->C1x,
+           sm2_ctext->C1y, ctx))
+       {
+               SM2error(ERR_R_EC_LIB);
+               goto err;
+       }
+
+       if (!EC_POINT_mul(group, C1, NULL, C1, EC_KEY_get0_private_key(key),
+           ctx)) {
+               SM2error(ERR_R_EC_LIB);
+               goto err;
+       }
+
+       if (!EC_POINT_get_affine_coordinates(group, C1, x2, y2, ctx)) {
+               SM2error(ERR_R_EC_LIB);
+               goto err;
+       }
+
+       if ((x2size = BN_num_bytes(x2)) > field_size ||
+           (y2size = BN_num_bytes(y2)) > field_size) {
+               SM2error(SM2_R_BIGNUM_OUT_OF_RANGE);
+               goto err;
+       }
+
+       BN_bn2bin(x2, x2y2 + field_size - x2size);
+       BN_bn2bin(y2, x2y2 + 2 * field_size - y2size);
+
+       if (!sm2_kdf(msg_mask, msg_len, x2y2, 2 * field_size, digest)) {
+               SM2error(SM2_R_KDF_FAILURE);
+               goto err;
+       }
+
+       for (i = 0; i != msg_len; ++i)
+               ptext_buf[i] = C2[i] ^ msg_mask[i];
+
+       if ((hash = EVP_MD_CTX_new()) == NULL) {
+               SM2error(ERR_R_EVP_LIB);
+               goto err;
+       }
+
+       if (!EVP_DigestInit(hash, digest)) {
+               SM2error(ERR_R_EVP_LIB);
+               goto err;
+       }
+
+       if (!EVP_DigestUpdate(hash, x2y2, field_size)) {
+               SM2error(ERR_R_EVP_LIB);
+               goto err;
+       }
+
+       if (!EVP_DigestUpdate(hash, ptext_buf, msg_len)) {
+               SM2error(ERR_R_EVP_LIB);
+               goto err;
+       }
+
+       if (!EVP_DigestUpdate(hash, x2y2 + field_size, field_size)) {
+               SM2error(ERR_R_EVP_LIB);
+               goto err;
+       }
+
+       if (!EVP_DigestFinal(hash, computed_C3, NULL)) {
+               SM2error(ERR_R_EVP_LIB);
+               goto err;
+       }
+
+       if (memcmp(computed_C3, C3, hash_size) != 0)
+               goto err;
+
+       rc = 1;
+       *ptext_len = msg_len;
+
+ err:
+       if (rc == 0)
+               memset(ptext_buf, 0, *ptext_len);
+
+       free(msg_mask);
+       free(x2y2);
+       free(computed_C3);
+       EC_POINT_free(C1);
+       BN_CTX_end(ctx);
+       BN_CTX_free(ctx);
+       SM2_Ciphertext_free(sm2_ctext);
+       EVP_MD_CTX_free(hash);
+
+       return rc;
+}
+
+#endif /* OPENSSL_NO_SM2 */
diff --git a/lib/libcrypto/sm2/sm2_err.c b/lib/libcrypto/sm2/sm2_err.c
new file mode 100644 (file)
index 0000000..d34123c
--- /dev/null
@@ -0,0 +1,106 @@
+/*     $OpenBSD: sm2_err.c,v 1.1.1.1 2021/08/18 16:04:32 tb Exp $ */
+/*
+ * Generated by util/mkerr.pl DO NOT EDIT
+ * Copyright 1995-2017 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL license (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
+ */
+
+#ifndef OPENSSL_NO_SM2
+
+#include <openssl/err.h>
+#include <openssl/sm2.h>
+
+#ifndef OPENSSL_NO_ERR
+
+#define ERR_FUNC(func) ERR_PACK(ERR_LIB_SM2,func,0)
+#define ERR_REASON(reason) ERR_PACK(ERR_LIB_SM2,0,reason)
+
+static ERR_STRING_DATA SM2_str_functs[] = {
+       {ERR_FUNC(0xfff), "CRYPTO_internal"},
+       {0, NULL}
+};
+
+static ERR_STRING_DATA SM2_str_reasons[] = {
+       {ERR_REASON(SM2_R_ASN1_ERROR), "asn1 error"},
+       {ERR_REASON(SM2_R_ASN5_ERROR), "asn5 error"},
+       {ERR_REASON(SM2_R_BAD_SIGNATURE), "bad signature"},
+       {ERR_REASON(SM2_R_BIGNUM_OUT_OF_RANGE), "bignum out of range"},
+       {ERR_REASON(SM2_R_BUFFER_TOO_SMALL), "buffer too small"},
+       {ERR_REASON(SM2_R_COORDINATES_OUT_OF_RANGE), "coordinates out of range"},
+       {ERR_REASON(SM2_R_CURVE_DOES_NOT_SUPPORT_ECDH), "curve does not support ecdh"},
+       {ERR_REASON(SM2_R_CURVE_DOES_NOT_SUPPORT_SIGNING), "curve does not support signing"},
+       {ERR_REASON(SM2_R_D2I_ECPKPARAMETERS_FAILURE), "d2i ecpkparameters failure"},
+       {ERR_REASON(SM2_R_DECODE_ERROR), "decode error"},
+       {ERR_REASON(SM2_R_DIGEST_FAILURE), "digest calculation failure"},
+       {ERR_REASON(SM2_R_DISCRIMINANT_IS_ZERO), "discriminant is zero"},
+       {ERR_REASON(SM2_R_EC_GROUP_NEW_BY_NAME_FAILURE), "ec group new by name failure"},
+       {ERR_REASON(SM2_R_FIELD_TOO_LARGE), "field too large"},
+       {ERR_REASON(SM2_R_GF2M_NOT_SUPPORTED), "gf2m not supported"},
+       {ERR_REASON(SM2_R_GROUP2PKPARAMETERS_FAILURE), "group2pkparameters failure"},
+       {ERR_REASON(SM2_R_I2D_ECPKPARAMETERS_FAILURE), "i2d ecpkparameters failure"},
+       {ERR_REASON(SM2_R_INCOMPATIBLE_OBJECTS), "incompatible objects"},
+       {ERR_REASON(SM2_R_INVALID_ARGUMENT), "invalid argument"},
+       {ERR_REASON(SM2_R_INVALID_COMPRESSED_POINT), "invalid compressed point"},
+       {ERR_REASON(SM2_R_INVALID_COMPRESSION_BIT), "invalid compression bit"},
+       {ERR_REASON(SM2_R_INVALID_CURVE), "invalid curve"},
+       {ERR_REASON(SM2_R_INVALID_DIGEST), "invalid digest"},
+       {ERR_REASON(SM2_R_INVALID_DIGEST_TYPE), "invalid digest type"},
+       {ERR_REASON(SM2_R_INVALID_ENCODING), "invalid encoding"},
+       {ERR_REASON(SM2_R_INVALID_FIELD), "invalid field"},
+       {ERR_REASON(SM2_R_INVALID_FORM), "invalid form"},
+       {ERR_REASON(SM2_R_INVALID_GROUP_ORDER), "invalid group order"},
+       {ERR_REASON(SM2_R_INVALID_KEY), "invalid key"},
+       {ERR_REASON(SM2_R_INVALID_OUTPUT_LENGTH), "invalid output length"},
+       {ERR_REASON(SM2_R_INVALID_PEER_KEY), "invalid peer key"},
+       {ERR_REASON(SM2_R_INVALID_PENTANOMIAL_BASIS), "invalid pentanomial basis"},
+       {ERR_REASON(SM2_R_INVALID_PRIVATE_KEY), "invalid private key"},
+       {ERR_REASON(SM2_R_INVALID_TRINOMIAL_BASIS), "invalid trinomial basis"},
+       {ERR_REASON(SM2_R_KDF_FAILURE), "kdf calculation failure"},
+       {ERR_REASON(SM2_R_KDF_PARAMETER_ERROR), "kdf parameter error"},
+       {ERR_REASON(SM2_R_KEYS_NOT_SET), "keys not set"},
+       {ERR_REASON(SM2_R_MISSING_PARAMETERS), "missing parameters"},
+       {ERR_REASON(SM2_R_MISSING_PRIVATE_KEY), "missing private key"},
+       {ERR_REASON(SM2_R_NEED_NEW_SETUP_VALUES), "need new setup values"},
+       {ERR_REASON(SM2_R_NOT_A_NIST_PRIME), "not a NIST prime"},
+       {ERR_REASON(SM2_R_NOT_IMPLEMENTED), "not implemented"},
+       {ERR_REASON(SM2_R_NOT_INITIALIZED), "not initialized"},
+       {ERR_REASON(SM2_R_NO_PARAMETERS_SET), "no parameters set"},
+       {ERR_REASON(SM2_R_NO_PRIVATE_VALUE), "no private value"},
+       {ERR_REASON(SM2_R_OPERATION_NOT_SUPPORTED), "operation not supported"},
+       {ERR_REASON(SM2_R_PASSED_NULL_PARAMETER), "passed null parameter"},
+       {ERR_REASON(SM2_R_PEER_KEY_ERROR), "peer key error"},
+       {ERR_REASON(SM2_R_PKPARAMETERS2GROUP_FAILURE), "pkparameters2group failure"},
+       {ERR_REASON(SM2_R_POINT_ARITHMETIC_FAILURE), "point arithmetic failure"},
+       {ERR_REASON(SM2_R_POINT_AT_INFINITY), "point at infinity"},
+       {ERR_REASON(SM2_R_POINT_IS_NOT_ON_CURVE), "point is not on curve"},
+       {ERR_REASON(SM2_R_RANDOM_NUMBER_GENERATION_FAILED), "random number generation failed"},
+       {ERR_REASON(SM2_R_SHARED_INFO_ERROR), "shared info error"},
+       {ERR_REASON(SM2_R_SLOT_FULL), "slot full"},
+       {ERR_REASON(SM2_R_UNDEFINED_GENERATOR), "undefined generator"},
+       {ERR_REASON(SM2_R_UNDEFINED_ORDER), "undefined order"},
+       {ERR_REASON(SM2_R_UNKNOWN_GROUP), "unknown group"},
+       {ERR_REASON(SM2_R_UNKNOWN_ORDER), "unknown order"},
+       {ERR_REASON(SM2_R_UNSUPPORTED_FIELD), "unsupported field"},
+       {ERR_REASON(SM2_R_WRONG_CURVE_PARAMETERS), "wrong curve parameters"},
+       {ERR_REASON(SM2_R_WRONG_ORDER), "wrong order"},
+       {0, NULL}
+};
+
+#endif
+
+void
+ERR_load_SM2_strings(void)
+{
+#ifndef OPENSSL_NO_ERR
+       if (ERR_func_error_string(SM2_str_functs[0].error) == NULL) {
+               ERR_load_strings(0, SM2_str_functs);
+               ERR_load_strings(0, SM2_str_reasons);
+       }
+#endif
+}
+
+#endif /* OPENSSL_NO_SM2 */
diff --git a/lib/libcrypto/sm2/sm2_locl.h b/lib/libcrypto/sm2/sm2_locl.h
new file mode 100644 (file)
index 0000000..2e16268
--- /dev/null
@@ -0,0 +1,43 @@
+/*     $OpenBSD: sm2_locl.h,v 1.1.1.1 2021/08/18 16:04:32 tb Exp $ */
+/*
+ * Copyright (c) 2017, 2019 Ribose Inc
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef HEADER_SM2_LOCL_H
+#define HEADER_SM2_LOCL_H
+
+#include <openssl/ec.h>
+#include <openssl/ecdsa.h>
+
+__BEGIN_HIDDEN_DECLS
+
+int sm2_compute_userid_digest(uint8_t *out, const EVP_MD *digest,
+       const uint8_t *uid, size_t uid_len, const EC_KEY *key);
+
+/*
+ * SM2 signature operation. Computes ZA (user id digest) and then signs
+ * H(ZA || msg) using SM2
+ */
+ECDSA_SIG *sm2_do_sign(const EC_KEY *key, const EVP_MD *digest,
+       const uint8_t *uid, size_t uid_len, const uint8_t *msg, size_t msg_len);
+
+int sm2_do_verify(const EC_KEY *key, const EVP_MD *digest,
+       const ECDSA_SIG *signature, const uint8_t *uid, size_t uid_len,
+       const uint8_t *msg, size_t msg_len);
+
+__END_HIDDEN_DECLS
+
+#endif
+
diff --git a/lib/libcrypto/sm2/sm2_pmeth.c b/lib/libcrypto/sm2/sm2_pmeth.c
new file mode 100644 (file)
index 0000000..af6e180
--- /dev/null
@@ -0,0 +1,316 @@
+/*     $OpenBSD: sm2_pmeth.c,v 1.1.1.1 2021/08/18 16:04:32 tb Exp $ */
+/*
+ * Copyright (c) 2017, 2019 Ribose Inc
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef OPENSSL_NO_SM2
+
+#include <string.h>
+
+#include <openssl/sm2.h>
+#include <openssl/asn1t.h>
+#include <openssl/x509.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+
+#include "evp_locl.h"
+#include "sm2_locl.h"
+
+/* SM2 pkey context structure */
+
+typedef struct {
+       /* key and paramgen group */
+       EC_GROUP *gen_group;
+       /* message  digest */
+       const EVP_MD *md;
+       EVP_MD_CTX *md_ctx;
+       /* personalization string */
+       uint8_t* uid;
+       size_t uid_len;
+} SM2_PKEY_CTX;
+
+static int
+pkey_sm2_init(EVP_PKEY_CTX *ctx)
+{
+       SM2_PKEY_CTX *dctx;
+
+       if ((dctx = calloc(1, sizeof(*dctx))) == NULL) {
+               SM2error(ERR_R_MALLOC_FAILURE);
+               return 0;
+       }
+       ctx->data = dctx;
+       return 1;
+}
+
+static void
+pkey_sm2_cleanup(EVP_PKEY_CTX *ctx)
+{
+       SM2_PKEY_CTX *dctx = ctx->data;
+
+       if (ctx == NULL || ctx->data == NULL)
+               return;
+
+       EC_GROUP_free(dctx->gen_group);
+       free(dctx->uid);
+       free(dctx);
+       ctx->data = NULL;
+}
+
+static int
+pkey_sm2_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src)
+{
+       SM2_PKEY_CTX *dctx, *sctx;
+
+       if (!pkey_sm2_init(dst))
+               return 0;
+       sctx = src->data;
+       dctx = dst->data;
+       if (sctx->gen_group) {
+               if ((dctx->gen_group = EC_GROUP_dup(sctx->gen_group)) == NULL) {
+                       SM2error(ERR_R_MALLOC_FAILURE);
+                       goto err;
+               }
+       }
+
+       if (sctx->uid != NULL) {
+               if ((dctx->uid = malloc(sctx->uid_len)) == NULL) {
+                       SM2error(ERR_R_MALLOC_FAILURE);
+                       goto err;
+               }
+               memcpy(dctx->uid, sctx->uid, sctx->uid_len);
+               dctx->uid_len = sctx->uid_len;
+       }
+
+       dctx->md = sctx->md;
+
+       if (!EVP_MD_CTX_copy(dctx->md_ctx, sctx->md_ctx))
+               goto err;
+
+       return 1;
+
+ err:
+       pkey_sm2_cleanup(dst);
+       return 0;
+}
+
+static int
+pkey_sm2_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen,
+    const unsigned char *tbs, size_t tbslen)
+{
+       unsigned int sltmp;
+       int ret, sig_sz;
+
+       if ((sig_sz = ECDSA_size(ctx->pkey->pkey.ec)) <= 0)
+               return 0;
+
+       if (sig == NULL) {
+               *siglen = sig_sz;
+               return 1;
+       }
+
+       if (*siglen < (size_t)sig_sz) {
+               SM2error(SM2_R_BUFFER_TOO_SMALL);
+               return 0;
+       }
+
+       if ((ret = SM2_sign(tbs, tbslen, sig, &sltmp, ctx->pkey->pkey.ec)) <= 0)
+               return ret;
+
+       *siglen = (size_t)sltmp;
+       return 1;
+}
+
+static int
+pkey_sm2_verify(EVP_PKEY_CTX *ctx, const unsigned char *sig, size_t siglen,
+    const unsigned char *tbs, size_t tbslen)
+{
+       return SM2_verify(tbs, tbslen, sig, siglen, ctx->pkey->pkey.ec);
+}
+
+static int
+pkey_sm2_encrypt(EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen,
+    const unsigned char *in, size_t inlen)
+{
+       SM2_PKEY_CTX *dctx = ctx->data;
+       const EVP_MD *md = (dctx->md == NULL) ? EVP_sm3() : dctx->md;
+
+       if (out == NULL) {
+               if (!SM2_ciphertext_size(ctx->pkey->pkey.ec, md, inlen, outlen))
+                       return -1;
+               else
+                       return 1;
+       }
+
+       return SM2_encrypt(ctx->pkey->pkey.ec, md, in, inlen, out, outlen);
+}
+
+static int
+pkey_sm2_decrypt(EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen,
+    const unsigned char *in, size_t inlen)
+{
+       SM2_PKEY_CTX *dctx = ctx->data;
+       const EVP_MD *md = (dctx->md == NULL) ? EVP_sm3() : dctx->md;
+
+       if (out == NULL) {
+               if (!SM2_plaintext_size(ctx->pkey->pkey.ec, md, inlen, outlen))
+                       return -1;
+               else
+                       return 1;
+       }
+
+       return SM2_decrypt(ctx->pkey->pkey.ec, md, in, inlen, out, outlen);
+}
+
+static int
+pkey_sm2_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
+{
+       SM2_PKEY_CTX *dctx = ctx->data;
+       EC_GROUP *group = NULL;
+
+       switch (type) {
+       case EVP_PKEY_CTRL_DIGESTINIT:
+               dctx->md_ctx = p2;
+               return 1;
+
+       case EVP_PKEY_CTRL_EC_PARAMGEN_CURVE_NID:
+               if ((group = EC_GROUP_new_by_curve_name(p1)) == NULL) {
+                       SM2error(SM2_R_INVALID_CURVE);
+                       return 0;
+               }
+               EC_GROUP_free(dctx->gen_group);
+               dctx->gen_group = group;
+               return 1;
+
+       case EVP_PKEY_CTRL_SM2_SET_UID:
+               if ((p1 < 0) || ((p1 == 0) && (p2 != NULL)))  {
+                       SM2error(SM2_R_INVALID_ARGUMENT);
+                       return 0;
+               }
+               if ((p1 > 0) && (p2 == NULL)) {
+                       SM2error(ERR_R_PASSED_NULL_PARAMETER);
+                       return 0;
+               }
+               free(dctx->uid);
+               if (p2 == NULL) {
+                       dctx->uid = NULL;
+                       dctx->uid_len = 0;
+                       return 1;
+               }
+
+               if ((dctx->uid = malloc(p1)) == NULL) {
+                       SM2error(ERR_R_MALLOC_FAILURE);
+                       return 1;
+               }
+               memcpy(dctx->uid, p2, p1);
+               dctx->uid_len = p1;
+               return 1;
+
+       case EVP_PKEY_CTRL_SM2_HASH_UID:
+           {
+               const EVP_MD* md;
+               uint8_t za[EVP_MAX_MD_SIZE] = {0};
+               int md_len;
+
+               if (dctx->uid == NULL) {
+                       SM2error(SM2_R_INVALID_ARGUMENT);
+                       return 0;
+               }
+
+               if ((md = EVP_MD_CTX_md(dctx->md_ctx)) == NULL) {
+                       SM2error(ERR_R_EVP_LIB);
+                       return 0;
+               }
+
+               if ((md_len = EVP_MD_size(md)) < 0) {
+                       SM2error(SM2_R_INVALID_DIGEST);
+                       return 0;
+               }
+
+               if (sm2_compute_userid_digest(za, md, dctx->uid, dctx->uid_len,
+                   ctx->pkey->pkey.ec) != 1) {
+                       SM2error(SM2_R_DIGEST_FAILURE);
+                       return 0;
+               }
+               return EVP_DigestUpdate(dctx->md_ctx, za, md_len);
+           }
+
+       case EVP_PKEY_CTRL_SM2_GET_UID_LEN:
+               if (p2 == NULL) {
+                       SM2error(ERR_R_PASSED_NULL_PARAMETER);
+                       return 0;
+               }
+               *(size_t *)p2 = dctx->uid_len;
+               return 1;
+
+       case EVP_PKEY_CTRL_SM2_GET_UID:
+               if (p2 == NULL) {
+                       SM2error(ERR_R_PASSED_NULL_PARAMETER);
+                       return 0;
+               }
+               if (dctx->uid_len == 0) {
+                       return 1;
+               }
+               memcpy(p2, dctx->uid, dctx->uid_len);
+               return 1;
+
+       case EVP_PKEY_CTRL_MD:
+               dctx->md = p2;
+               return 1;
+
+       default:
+               return -2;
+       }
+}
+
+static int
+pkey_sm2_ctrl_str(EVP_PKEY_CTX *ctx, const char *type, const char *value)
+{
+       int nid;
+
+       if (strcmp(type, "ec_paramgen_curve") == 0) {
+               if (((nid = EC_curve_nist2nid(value)) == NID_undef) &&
+                   ((nid = OBJ_sn2nid(value)) == NID_undef) &&
+                   ((nid = OBJ_ln2nid(value)) == NID_undef)) {
+                       SM2error(SM2_R_INVALID_CURVE);
+                       return 0;
+               }
+               return EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, nid);
+       } else if (strcmp(type, "sm2_uid") == 0) {
+               return EVP_PKEY_CTX_set_sm2_uid(ctx, (void*) value,
+                   (int)strlen(value));
+       }
+
+       return -2;
+}
+
+const EVP_PKEY_METHOD sm2_pkey_meth = {
+       .pkey_id = EVP_PKEY_SM2,
+       .init = pkey_sm2_init,
+       .copy = pkey_sm2_copy,
+       .cleanup = pkey_sm2_cleanup,
+
+       .sign = pkey_sm2_sign,
+
+       .verify = pkey_sm2_verify,
+
+       .encrypt = pkey_sm2_encrypt,
+
+       .decrypt = pkey_sm2_decrypt,
+
+       .ctrl = pkey_sm2_ctrl,
+       .ctrl_str = pkey_sm2_ctrl_str
+};
+
+#endif /* OPENSSL_NO_SM2 */
diff --git a/lib/libcrypto/sm2/sm2_sign.c b/lib/libcrypto/sm2/sm2_sign.c
new file mode 100644 (file)
index 0000000..d306658
--- /dev/null
@@ -0,0 +1,465 @@
+/*     $OpenBSD: sm2_sign.c,v 1.1.1.1 2021/08/18 16:04:32 tb Exp $ */
+/*
+ * Copyright (c) 2017, 2019 Ribose Inc
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef OPENSSL_NO_SM2
+
+#include <string.h>
+
+#include <openssl/sm2.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <openssl/bn.h>
+
+#include "bn_lcl.h"
+#include "sm2_locl.h"
+
+static BIGNUM *
+sm2_compute_msg_hash(const EVP_MD *digest, const EC_KEY *key,
+    const uint8_t *uid, size_t uid_len, const uint8_t *msg, size_t msg_len)
+{
+       EVP_MD_CTX *hash;
+       BIGNUM *e = NULL;
+       int md_size;
+       uint8_t *za = NULL;
+
+       if ((hash = EVP_MD_CTX_new()) == NULL) {
+               SM2error(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+
+       if ((md_size = EVP_MD_size(digest)) < 0) {
+               SM2error(SM2_R_INVALID_DIGEST);
+               goto err;
+       }
+
+       if ((za = calloc(1, md_size)) == NULL) {
+               SM2error(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+
+       if (!sm2_compute_userid_digest(za, digest, uid, uid_len, key)) {
+               SM2error(SM2_R_DIGEST_FAILURE);
+               goto err;
+       }
+
+       if (!EVP_DigestInit(hash, digest)) {
+               SM2error(ERR_R_EVP_LIB);
+               goto err;
+       }
+
+       if (!EVP_DigestUpdate(hash, za, md_size)) {
+               SM2error(ERR_R_EVP_LIB);
+               goto err;
+       }
+
+       if (!EVP_DigestUpdate(hash, msg, msg_len)) {
+               SM2error(ERR_R_EVP_LIB);
+               goto err;
+       }
+
+       /* reuse za buffer to hold H(ZA || M) */
+       if (!EVP_DigestFinal(hash, za, NULL)) {
+               SM2error(ERR_R_EVP_LIB);
+               goto err;
+       }
+
+       e = BN_bin2bn(za, md_size, NULL);
+
+ err:
+       free(za);
+       EVP_MD_CTX_free(hash);
+       return e;
+}
+
+static ECDSA_SIG *
+sm2_sig_gen(const EC_KEY *key, const BIGNUM *e)
+{
+       ECDSA_SIG *sig = NULL;
+       const EC_GROUP *group;
+       EC_POINT *kG = NULL;
+       BN_CTX *ctx = NULL;
+       const BIGNUM *dA;
+       BIGNUM *order = NULL, *r = NULL, *s = NULL;
+       BIGNUM *k, *rk, *tmp, *x1;
+       
+       if ((dA = EC_KEY_get0_private_key(key)) == NULL) {
+               SM2error(SM2_R_INVALID_FIELD);
+               goto err;
+       }
+       
+       if ((group = EC_KEY_get0_group(key)) == NULL) {
+               SM2error(SM2_R_INVALID_FIELD);
+               goto err;
+       }
+
+       if ((order = BN_new()) == NULL) {
+               SM2error(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+
+       if (!EC_GROUP_get_order(group, order, NULL)) {
+               SM2error(ERR_R_EC_LIB);
+               goto err;
+       }
+
+       if ((kG = EC_POINT_new(group)) == NULL) {
+               SM2error(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+
+       if ((ctx = BN_CTX_new()) == NULL) {
+               SM2error(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+
+       BN_CTX_start(ctx);
+
+       if ((k = BN_CTX_get(ctx)) == NULL) {
+               SM2error(ERR_R_BN_LIB);
+               goto err;
+       }
+       if ((rk = BN_CTX_get(ctx)) == NULL) {
+               SM2error(ERR_R_BN_LIB);
+               goto err;
+       }
+       if ((x1 = BN_CTX_get(ctx)) == NULL) {
+               SM2error(ERR_R_BN_LIB);
+               goto err;
+       }
+       if ((tmp = BN_CTX_get(ctx)) == NULL) {
+               SM2error(ERR_R_BN_LIB);
+               goto err;
+       }
+
+       /* r and s are returned as part of sig, so they can't be part of ctx. */
+       if ((r = BN_new()) == NULL) {
+               SM2error(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+       if ((s = BN_new()) == NULL) {
+               SM2error(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+
+       for (;;) {
+               if (!BN_rand_range(k, order)) {
+                       SM2error(SM2_R_RANDOM_NUMBER_GENERATION_FAILED);
+                       goto err;
+               }
+
+               if (!EC_POINT_mul(group, kG, k, NULL, NULL, ctx)) {
+                       SM2error(ERR_R_EC_LIB);
+                       goto err;
+               }
+
+               if (!EC_POINT_get_affine_coordinates(group, kG, x1, NULL,
+                   ctx)) {
+                       SM2error(ERR_R_EC_LIB);
+                       goto err;
+               }
+
+               if (!BN_mod_add(r, e, x1, order, ctx)) {
+                       SM2error(ERR_R_BN_LIB);
+                       goto err;
+               }
+
+               /* try again if r == 0 or r + k == n */
+               if (BN_is_zero(r))
+                       continue;
+
+               if (!BN_add(rk, r, k)) {
+                       SM2error(ERR_R_BN_LIB);
+                       goto err;
+               }
+
+               if (BN_cmp(rk, order) == 0)
+                       continue;
+
+               if (!BN_add(s, dA, BN_value_one())) {
+                       SM2error(ERR_R_BN_LIB);
+                       goto err;
+               }
+
+               if (!BN_mod_inverse_ct(s, s, order, ctx)) {
+                       SM2error(ERR_R_BN_LIB);
+                       goto err;
+               }
+
+               if (!BN_mod_mul(tmp, dA, r, order, ctx)) {
+                       SM2error(ERR_R_BN_LIB);
+                       goto err;
+               }
+
+               if (!BN_sub(tmp, k, tmp)) {
+                       SM2error(ERR_R_BN_LIB);
+                       goto err;
+               }
+
+               if (!BN_mod_mul(s, s, tmp, order, ctx)) {
+                       SM2error(ERR_R_BN_LIB);
+                       goto err;
+               }
+
+               if ((sig = ECDSA_SIG_new()) == NULL) {
+                       SM2error(ERR_R_MALLOC_FAILURE);
+                       goto err;
+               }
+
+               /* sig takes ownership of r and s */
+               if (!ECDSA_SIG_set0(sig, r, s)) {
+                       SM2error(ERR_R_INTERNAL_ERROR);
+                       goto err;
+               }
+               break;
+       }
+
+ err:
+       if (sig == NULL) {
+               BN_free(r);
+               BN_free(s);
+       }
+
+       BN_free(order);
+       BN_CTX_end(ctx);
+       BN_CTX_free(ctx);
+       EC_POINT_free(kG);
+       return sig;
+}
+
+static int
+sm2_sig_verify(const EC_KEY *key, const ECDSA_SIG *sig, const BIGNUM *e)
+{
+       const EC_GROUP *group;
+       EC_POINT *pt = NULL;
+       const BIGNUM *r = NULL, *s = NULL;
+       BN_CTX *ctx = NULL;
+       BIGNUM *order, *t, *x1;
+       int ret = 0;
+
+       if ((group = EC_KEY_get0_group(key)) == NULL) {
+               SM2error(SM2_R_INVALID_FIELD);
+               goto err;
+       }
+
+       if ((ctx = BN_CTX_new()) == NULL) {
+               SM2error(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+
+       BN_CTX_start(ctx);
+
+       if ((order = BN_CTX_get(ctx)) == NULL) {
+               SM2error(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+
+       if (!EC_GROUP_get_order(group, order, NULL)) {
+               SM2error(ERR_R_EC_LIB);
+               goto err;
+       }
+
+       if ((pt = EC_POINT_new(group)) == NULL) {
+               SM2error(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+
+       if ((t = BN_CTX_get(ctx)) == NULL) {
+               SM2error(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+       if ((x1 = BN_CTX_get(ctx)) == NULL) {
+               SM2error(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+
+       /*
+        * Section 5.3.1 in https://tools.ietf.org/html/draft-shen-sm2-ecdsa-00
+        *
+        * B1: verify that r' is in [1, n-1]
+        * B2: verify that s' is in [1, n-1]
+        * B3: set M' ~= ZA || M'
+        * B4: calculate e' = Hv(M'~)
+        * B5: verify that t = r' + s' (mod n) is not zero
+        * B6: calculate the point (x1', y1') = [s']G + [t]PA
+        * B7: verify that r' == e' + x1' (mod n)
+        */
+
+       ECDSA_SIG_get0(sig, &r, &s);
+
+       /* B1: verify that r' is in [1, n-1] */
+       if (BN_cmp(r, BN_value_one()) < 0 || BN_cmp(order, r) <= 0) {
+               SM2error(SM2_R_BAD_SIGNATURE);
+               goto err;
+       }
+
+       /* B2: verify that s' is in [1, n-1] */
+       if (BN_cmp(s, BN_value_one()) < 0 || BN_cmp(order, s) <= 0) {
+               SM2error(SM2_R_BAD_SIGNATURE);
+               goto err;
+       }
+
+       /* B5: verify that t = r + s is not zero */
+       if (!BN_mod_add(t, r, s, order, ctx)) {
+               SM2error(ERR_R_BN_LIB);
+               goto err;
+       }
+       if (BN_is_zero(t)) {
+               SM2error(SM2_R_BAD_SIGNATURE);
+               goto err;
+       }
+
+       /* B6: calculate pt = (x1', y1') = [s']G + [t]PA */
+       if (!EC_POINT_mul(group, pt, s, EC_KEY_get0_public_key(key), t, ctx)) {
+               SM2error(ERR_R_EC_LIB);
+               goto err;
+       }
+
+       if (!EC_POINT_get_affine_coordinates(group, pt, x1, NULL, ctx)) {
+               SM2error(ERR_R_EC_LIB);
+               goto err;
+       }
+
+       /* B7: verify that r' == e' + x1' (mod n) */
+       if (!BN_mod_add(t, e, x1, order, ctx)) {
+               SM2error(ERR_R_BN_LIB);
+               goto err;
+       }
+       if (BN_cmp(r, t) == 0)
+               ret = 1;
+
+ err:
+       EC_POINT_free(pt);
+       BN_CTX_end(ctx);
+       BN_CTX_free(ctx);
+       return ret;
+}
+
+ECDSA_SIG *
+sm2_do_sign(const EC_KEY *key, const EVP_MD *digest, const uint8_t *uid,
+    size_t uid_len, const uint8_t *msg, size_t msg_len)
+{
+       ECDSA_SIG *sig = NULL;
+       BIGNUM *e;
+
+       e = sm2_compute_msg_hash(digest, key, uid, uid_len, msg, msg_len);
+       if (e == NULL) {
+               SM2error(SM2_R_DIGEST_FAILURE);
+               goto err;
+       }
+
+       sig = sm2_sig_gen(key, e);
+
+ err:
+       BN_free(e);
+       return sig;
+}
+
+int
+sm2_do_verify(const EC_KEY *key, const EVP_MD *digest, const ECDSA_SIG *sig,
+    const uint8_t *uid, size_t uid_len, const uint8_t *msg, size_t msg_len)
+{
+       BIGNUM *e;
+       int ret = -1;
+
+       e = sm2_compute_msg_hash(digest, key, uid, uid_len, msg, msg_len);
+       if (e == NULL) {
+               SM2error(SM2_R_DIGEST_FAILURE);
+               goto err;
+       }
+
+       ret = sm2_sig_verify(key, sig, e);
+
+ err:
+       BN_free(e);
+       return ret;
+}
+
+int
+SM2_sign(const unsigned char *dgst, int dgstlen, unsigned char *sig,
+    unsigned int *siglen, EC_KEY *eckey)
+{
+       BIGNUM *e;
+       ECDSA_SIG *s = NULL;
+       int outlen = 0;
+       int ret = -1;
+
+       if ((e = BN_bin2bn(dgst, dgstlen, NULL)) == NULL) {
+               SM2error(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+
+       if ((s = sm2_sig_gen(eckey, e)) == NULL) {
+               goto err;
+       }
+       
+       if ((outlen = i2d_ECDSA_SIG(s, &sig)) < 0) {
+               SM2error(SM2_R_ASN1_ERROR);
+               goto err;
+       }
+
+       *siglen = outlen;
+       ret = 1;
+
+ err:
+       ECDSA_SIG_free(s);
+       BN_free(e);
+       return ret;
+}
+
+int
+SM2_verify(const unsigned char *dgst, int dgstlen, const unsigned char *sig,
+    int sig_len, EC_KEY *eckey)
+{
+       ECDSA_SIG *s;
+       BIGNUM *e = NULL;
+       const unsigned char *p = sig;
+       unsigned char *der = NULL;
+       int derlen = -1;
+       int ret = -1;
+
+       if ((s = ECDSA_SIG_new()) == NULL) {
+               SM2error(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+
+       if (d2i_ECDSA_SIG(&s, &p, sig_len) == NULL) {
+               SM2error(SM2_R_INVALID_ENCODING);
+               goto err;
+       }
+
+       /* Ensure signature uses DER and doesn't have trailing garbage */
+       derlen = i2d_ECDSA_SIG(s, &der);
+       if (derlen != sig_len || memcmp(sig, der, derlen) != 0) {
+               SM2error(SM2_R_INVALID_ENCODING);
+               goto err;
+       }
+
+       if ((e = BN_bin2bn(dgst, dgstlen, NULL)) == NULL) {
+               SM2error(ERR_R_BN_LIB);
+               goto err;
+       }
+
+       ret = sm2_sig_verify(eckey, s, e);
+
+ err:
+       free(der);
+       BN_free(e);
+       ECDSA_SIG_free(s);
+       return ret;
+}
+
+#endif /* OPENSSL_NO_SM2 */
diff --git a/lib/libcrypto/sm2/sm2_za.c b/lib/libcrypto/sm2/sm2_za.c
new file mode 100644 (file)
index 0000000..2a7f284
--- /dev/null
@@ -0,0 +1,161 @@
+/*     $OpenBSD: sm2_za.c,v 1.1.1.1 2021/08/18 16:04:32 tb Exp $ */
+/*
+ * Copyright (c) 2017, 2019 Ribose Inc
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef OPENSSL_NO_SM2
+
+#include <openssl/sm2.h>
+#include <openssl/evp.h>
+#include <openssl/bn.h>
+#include <string.h>
+
+int
+sm2_compute_userid_digest(uint8_t *out, const EVP_MD *digest, uint8_t *uid,
+    size_t uid_len, const EC_KEY *key)
+{
+       const EC_GROUP *group;
+       EVP_MD_CTX *hash = NULL;
+       BN_CTX *ctx = NULL;
+       BIGNUM *p, *a, *b, *xG, *yG, *xA, *yA;
+       uint8_t *buf = NULL;
+       uint16_t entla;
+       uint8_t e_byte;
+       int bytes, p_bytes;
+       int rc = 0;
+
+       if ((group = EC_KEY_get0_group(key)) == NULL)
+               goto err;
+
+       if ((hash = EVP_MD_CTX_new()) == NULL)
+               goto err;
+
+       if ((ctx = BN_CTX_new()) == NULL)
+               goto err;
+
+       if ((p = BN_CTX_get(ctx)) == NULL)
+               goto err;
+       if ((a = BN_CTX_get(ctx)) == NULL)
+               goto err;
+       if ((b = BN_CTX_get(ctx)) == NULL)
+               goto err;
+       if ((xG = BN_CTX_get(ctx)) == NULL)
+               goto err;
+       if ((yG = BN_CTX_get(ctx)) == NULL)
+               goto err;
+       if ((xA = BN_CTX_get(ctx)) == NULL)
+               goto err;
+       if ((yA = BN_CTX_get(ctx)) == NULL)
+               goto err;
+
+       memset(out, 0, EVP_MD_size(digest));
+
+       if (!EVP_DigestInit(hash, digest))
+               goto err;
+
+       /*
+        * ZA=H256(ENTLA || IDA || a || b || xG || yG || xA || yA)
+        */
+
+       if (uid_len >= 8192)
+               goto err;
+
+       entla = (unsigned short)(8 * uid_len);
+
+       e_byte = entla >> 8;
+       if (!EVP_DigestUpdate(hash, &e_byte, 1))
+               goto err;
+
+       e_byte = entla & 0xFF;
+       if (!EVP_DigestUpdate(hash, &e_byte, 1))
+               goto err;
+
+       if (!EVP_DigestUpdate(hash, uid, uid_len))
+               goto err;
+
+       if (!EC_GROUP_get_curve(group, p, a, b, ctx))
+               goto err;
+
+       p_bytes = BN_num_bytes(p);
+
+       if ((buf = calloc(1, p_bytes)) == NULL)
+               goto err;
+
+       if ((bytes = BN_num_bytes(a)) > p_bytes)
+               goto err;
+       BN_bn2bin(a, buf + p_bytes - bytes);
+       if (!EVP_DigestUpdate(hash, buf, p_bytes))
+               goto err;
+
+       if ((bytes = BN_num_bytes(b)) > p_bytes)
+               goto err;
+       memset(buf, 0, p_bytes - bytes);
+       BN_bn2bin(b, buf + p_bytes - bytes);
+       if (!EVP_DigestUpdate(hash, buf, p_bytes))
+               goto err;
+
+       if (!EC_POINT_get_affine_coordinates(group,
+           EC_GROUP_get0_generator(group), xG, yG, ctx))
+               goto err;
+
+       if ((bytes = BN_num_bytes(xG)) > p_bytes)
+               goto err;
+       memset(buf, 0, p_bytes - bytes);
+       BN_bn2bin(xG, buf + p_bytes - bytes);
+
+       if (!EVP_DigestUpdate(hash, buf, p_bytes))
+               goto err;
+
+       if ((bytes = BN_num_bytes(yG)) > p_bytes)
+               goto err;
+       memset(buf, 0, p_bytes - bytes);
+       BN_bn2bin(yG, buf + p_bytes - bytes);
+
+       if (!EVP_DigestUpdate(hash, buf, p_bytes))
+               goto err;
+
+       if (!EC_POINT_get_affine_coordinates(group,
+           EC_KEY_get0_public_key(key), xA, yA, ctx))
+               goto err;
+
+       if ((bytes = BN_num_bytes(xA)) > p_bytes)
+               goto err;
+       memset(buf, 0, p_bytes - bytes);
+       BN_bn2bin(xA, buf + p_bytes - bytes);
+
+       if (!EVP_DigestUpdate(hash, buf, p_bytes))
+               goto err;
+
+       if ((bytes = BN_num_bytes(yA)) > p_bytes)
+               goto err;
+       memset(buf, 0, p_bytes - bytes);
+       BN_bn2bin(yA, buf + p_bytes - bytes);
+
+       if (!EVP_DigestUpdate(hash, buf, p_bytes))
+               goto err;
+
+       if (!EVP_DigestFinal(hash, out, NULL))
+               goto err;
+
+       rc = 1;
+
+ err:
+       free(buf);
+       BN_CTX_free(ctx);
+       EVP_MD_CTX_free(hash);
+       return rc;
+}
+
+#endif /* OPENSSL_NO_SM2 */