diff mbox series

feat: crypt support sm3 algorithm

Message ID 20220801025359.1363-1-yangmingjun@uniontech.com
State New
Headers show
Series feat: crypt support sm3 algorithm | expand

Commit Message

Mingjun Yang Aug. 1, 2022, 2:53 a.m. UTC
This patch aim to let crypt packet support SM3 algorithms, the
proccess of SM3 encryption is the same to this semi-formal
SHA-crypt document (https://www.akkadia.org/drepper/SHA-crypt.txt).
noted that this patch define that SM3 algorithms ID is 8.

SM3 algorithm is defined in section 23 of ISO/IEC 10118-3:2018
(https://www.iso.org/standard/67116.html)
SM3 is similar to SHA256, but its algorithm is more complex.
SM3 is much safer than SHA256 at the same bit size.

Signed-off-by: Mingjun Yang <yangmingjun@uniontech.com>
---
 crypt/Makefile      |  11 +-
 crypt/crypt-entry.c |  15 ++
 crypt/sm3-crypt.c   | 412 ++++++++++++++++++++++++++++++++++++++++++++
 crypt/sm3.c         | 341 ++++++++++++++++++++++++++++++++++++
 crypt/sm3.h         |  80 +++++++++
 crypt/sm3c-test.c   |  53 ++++++
 crypt/sm3test.c     |  60 +++++++
 7 files changed, 968 insertions(+), 4 deletions(-)
 create mode 100644 crypt/sm3-crypt.c
 create mode 100644 crypt/sm3.c
 create mode 100644 crypt/sm3.h
 create mode 100644 crypt/sm3c-test.c
 create mode 100644 crypt/sm3test.c

Comments

Carlos O'Donell Aug. 4, 2022, 2:44 p.m. UTC | #1
On 7/31/22 22:53, Mingjun Yang wrote:
> This patch aim to let crypt packet support SM3 algorithms, the
> proccess of SM3 encryption is the same to this semi-formal
> SHA-crypt document (https://www.akkadia.org/drepper/SHA-crypt.txt).
> noted that this patch define that SM3 algorithms ID is 8.
> 
> SM3 algorithm is defined in section 23 of ISO/IEC 10118-3:2018
> (https://www.iso.org/standard/67116.html)
> SM3 is similar to SHA256, but its algorithm is more complex.
> SM3 is much safer than SHA256 at the same bit size.
> 
> Signed-off-by: Mingjun Yang <yangmingjun@uniontech.com>

What needs do you have that drive this requirement?

I do not want to add any new algorithms to crypt in glibc.

I would instead suggest using libxcrypt, which is an ABI-compatible drop-in
replacement for crypt APIs.

The community worked closely with libxcrypt upstream to ensure it could be
used to provide all the functions required by applications.

For example Fedora uses --disable-crypt to configure glibc and uses libxcrypt
to provide the APIs. This change was done in 2018, and so for almost 4 years
Fedora has been using libxcrypt [1].

If anything I think glibc should switch to disabling libcrypt by default
and provide --enable-legacy-crypt (or something like this) to enable the
legacy library is built and installed. This is similar to the transition we
made with SunRPC to TIRPC [2].
Cristian Rodríguez Aug. 4, 2022, 3:02 p.m. UTC | #2
On Sun, Jul 31, 2022 at 10:54 PM Mingjun Yang <yangmingjun@uniontech.com> wrote:
>
> This patch aim to let crypt packet support SM3 algorithms, the
> proccess of SM3 encryption is the same to this semi-formal
> SHA-crypt document (https://www.akkadia.org/drepper/SHA-crypt.txt).
> noted that this patch define that SM3 algorithms ID is 8.
>
> SM3 algorithm is defined in section 23 of ISO/IEC 10118-3:2018
> (https://www.iso.org/standard/67116.html)
> SM3 is similar to SHA256, but its algorithm is more complex.
> SM3 is much safer than SHA256 at the same bit size.
>
> Signed-off-by: Mingjun Yang <yangmingjun@uniontech.com>


Cool, but you have a talk to libxcrypt developers.

https://github.com/besser82/libxcrypt

No mainstream distribution uses the version included with glibc anymore AFAIK.
diff mbox series

Patch

diff --git a/crypt/Makefile b/crypt/Makefile
index 6dd58ffd6d..5bb9b40871 100644
--- a/crypt/Makefile
+++ b/crypt/Makefile
@@ -27,22 +27,23 @@  headers := crypt.h
 extra-libs := libcrypt
 extra-libs-others := $(extra-libs)
 
-libcrypt-routines := crypt-entry md5-crypt sha256-crypt sha512-crypt crypt \
+libcrypt-routines := crypt-entry md5-crypt sm3-crypt sha256-crypt sha512-crypt crypt \
 		     crypt_util
 
-tests := cert md5c-test sha256c-test sha512c-test badsalttest
+tests := cert md5c-test sm3c-test sha256c-test sha512c-test badsalttest
 
 ifeq ($(nss-crypt),yes)
 nss-cpp-flags := -DUSE_NSS \
   -I$(shell nss-config --includedir) -I$(shell nspr-config --includedir)
+CPPFLAGS-sm3-crypt.c += $(nss-cpp-flags)
 CPPFLAGS-sha256-crypt.c += $(nss-cpp-flags)
 CPPFLAGS-sha512-crypt.c += $(nss-cpp-flags)
 CPPFLAGS-md5-crypt.c += $(nss-cpp-flags)
 LDLIBS-crypt.so = -lfreebl3
 else
-libcrypt-routines += md5 sha256 sha512
+libcrypt-routines += md5 sm3 sha256 sha512
 
-tests += md5test sha256test sha512test
+tests += md5test sm3test sha256test sha512test
 
 # The test md5test-giant uses up to 400 MB of RSS and runs on a fast
 # machine over a minute.
@@ -53,11 +54,13 @@  include ../Rules
 
 ifneq ($(nss-crypt),yes)
 md5-routines := md5 $(filter md5%,$(libcrypt-sysdep_routines))
+sm3-routines := sm3 $(filter sm3%,$(libcrypt-sysdep_routines))
 sha256-routines := sha256 $(filter sha256%,$(libcrypt-sysdep_routines))
 sha512-routines := sha512 $(filter sha512%,$(libcrypt-sysdep_routines))
 
 $(objpfx)md5test: $(patsubst %, $(objpfx)%.o,$(md5-routines))
 $(objpfx)md5test-giant: $(patsubst %, $(objpfx)%.o,$(md5-routines))
+$(objpfx)sm3test: $(patsubst %, $(objpfx)%.o,$(sm3-routines))
 $(objpfx)sha256test: $(patsubst %, $(objpfx)%.o,$(sha256-routines))
 $(objpfx)sha512test: $(patsubst %, $(objpfx)%.o,$(sha512-routines))
 endif
diff --git a/crypt/crypt-entry.c b/crypt/crypt-entry.c
index 646a021b45..1c6544a98e 100644
--- a/crypt/crypt-entry.c
+++ b/crypt/crypt-entry.c
@@ -52,6 +52,9 @@  extern char *__sha256_crypt (const char *key, const char *salt);
 extern char *__sha512_crypt_r (const char *key, const char *salt,
 			       char *buffer, int buflen);
 extern char *__sha512_crypt (const char *key, const char *salt);
+extern char *__sm3_crypt_r (const char *key, const char *salt,
+			       char *buffer, int buflen);
+extern char *__sm3_crypt (const char *key, const char *salt);
 
 /* Define our magic string to mark salt for MD5 encryption
    replacement.  This is meant to be the same as for other MD5 based
@@ -64,6 +67,9 @@  static const char sha256_salt_prefix[] = "$5$";
 /* Magic string for SHA512 encryption.  */
 static const char sha512_salt_prefix[] = "$6$";
 
+/* Magic string for SM3 encryption.  */
+static const char sm3_salt_prefix[] = "$8$";
+
 /* For use by the old, non-reentrant routines (crypt/encrypt/setkey)  */
 extern struct crypt_data _ufc_foobar;
 
@@ -102,6 +108,11 @@  __crypt_r (const char *key, const char *salt,
   if (strncmp (sha512_salt_prefix, salt, sizeof (sha512_salt_prefix) - 1) == 0)
     return __sha512_crypt_r (key, salt, (char *) data,
 			     sizeof (struct crypt_data));
+
+  /* Try to find out whether we have to use SM3 encryption replacement.  */
+  if (strncmp (sm3_salt_prefix, salt, sizeof (sm3_salt_prefix) - 1) == 0)
+    return __sm3_crypt_r (key, salt, (char *) data,
+			     sizeof (struct crypt_data));
 #endif
 
   /*
@@ -172,6 +183,10 @@  crypt (const char *key, const char *salt)
   /* Try to find out whether we have to use SHA512 encryption replacement.  */
   if (strncmp (sha512_salt_prefix, salt, sizeof (sha512_salt_prefix) - 1) == 0)
     return __sha512_crypt (key, salt);
+
+  /* Try to find out whether we have to use SM3 encryption replacement.  */
+  if (strncmp (sm3_salt_prefix, salt, sizeof (sm3_salt_prefix) - 1) == 0)
+    return __sm3_crypt (key, salt);
 #endif
 
   return __crypt_r (key, salt, &_ufc_foobar);
diff --git a/crypt/sm3-crypt.c b/crypt/sm3-crypt.c
new file mode 100644
index 0000000000..dbc08e1d0c
--- /dev/null
+++ b/crypt/sm3-crypt.c
@@ -0,0 +1,412 @@ 
+/*  Written by Mingjun Yang <yangmingjun@uniontech.com>, 2022.
+
+    One way encryption based on SM3 sum.
+    Copyright (C) 2022-2022 Free Software Foundation, Inc.
+    This file is part of the GNU C Library.
+    Contributed by Mingjun Yang <yangmingjun@uniontech.com>, 2022.
+
+    The GNU C Library 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.
+
+    The GNU C Library 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 the GNU C Library; if not, see
+    <http://www.gnu.org/licenses/>.
+*/
+
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/param.h>
+#include <stdio.h>
+
+#include "sm3.h"
+#include "crypt-private.h"
+
+#ifdef USE_NSS
+typedef int PRBool;
+# include <hasht.h>
+# include <nsslowhash.h>
+
+# define sm3_init_ctx(ctxp, nss_ctxp)                                   \
+    do                                                                  \
+    {                                                                   \
+        if (((nss_ctxp = NSSLOWHASH_NewContext (nss_ictx, HASH_AlgSM3)) \
+              == NULL))                                                 \
+        {                                                               \
+            if (nss_ctx != NULL)                                        \
+                NSSLOWHASH_Destroy (nss_ctx);                           \
+            if (nss_alt_ctx != NULL)                                    \
+                NSSLOWHASH_Destroy (nss_alt_ctx);                       \
+            return NULL;                                                \
+        }                                                               \
+        NSSLOWHASH_Begin (nss_ctxp);                                    \
+    }                                                                   \
+    while (0)
+
+# define sm3_process_bytes(buf, len, ctxp, nss_ctxp)                    \
+    NSSLOWHASH_Update (nss_ctxp, (const unsigned char *) buf, len)
+
+# define sm3_finish_ctx(ctxp, nss_ctxp, result)                         \
+    do                                                                  \
+    {                                                                   \
+        unsigned int ret;                                               \
+        NSSLOWHASH_End (nss_ctxp, result, &ret, sizeof (result));       \
+        assert (ret == sizeof (result));                                \
+        NSSLOWHASH_Destroy (nss_ctxp);                                  \
+        nss_ctxp = NULL;                                                \
+    }                                                                   \
+    while (0)
+#else
+# define sm3_init_ctx(ctxp, nss_ctxp)                 \
+    __sm3_init_ctx (ctxp)
+
+# define sm3_process_bytes(buf, len, ctxp, nss_ctxp)  \
+    __sm3_process_bytes(buf, len, ctxp)
+
+# define sm3_finish_ctx(ctxp, nss_ctxp, result)       \
+    __sm3_finish_ctx (ctxp, result)
+#endif
+
+/* Define our magic string to mark salt for SM3 "encryption"
+   replacement.  */
+static const char sm3_salt_prefix[] = "$8$";
+
+/* Prefix for optional rounds specification.  */
+static const char sm3_rounds_prefix[] = "rounds=";
+
+/* Maximum salt string length.  */
+#define SALT_LEN_MAX 16
+/* Default number of rounds if not explicitly specified.  */
+#define ROUNDS_DEFAULT 5000
+/* Minimum number of rounds.  */
+#define ROUNDS_MIN 1000
+/* Maximum number of rounds.  */
+#define ROUNDS_MAX 999999999
+
+/* Prototypes for local functions.  */
+extern char *__sm3_crypt_r (const char *key, const char *salt,
+                   char *buffer, int buflen);
+extern char *__sm3_crypt (const char *key, const char *salt);
+
+char *__sm3_crypt_r (const char *key, const char *salt, char *buffer, int buflen) {
+
+    unsigned char alt_result[32]
+        __attribute__ ((__aligned__ (__alignof__ (uint32_t))));
+    unsigned char temp_result[32]
+        __attribute__ ((__aligned__ (__alignof__ (uint32_t))));
+    size_t salt_len;
+    size_t key_len;
+    size_t cnt;
+    char *cp;
+    char *copied_key = NULL;
+    char *copied_salt = NULL;
+    char *p_bytes;
+    char *s_bytes;
+    /* Default number of rounds.  */
+    size_t rounds = ROUNDS_DEFAULT;
+    bool rounds_custom = false;
+    size_t alloca_used = 0;
+    char *free_key = NULL;
+    char *free_pbytes = NULL;
+
+    /* Find beginning of salt string.  The prefix should normally always
+        be present.  Just in case it is not.  */
+    if (strncmp (sm3_salt_prefix, salt, sizeof (sm3_salt_prefix) - 1) == 0)
+        /* Skip salt prefix.  */
+        salt += sizeof (sm3_salt_prefix) - 1;
+
+    if (strncmp (salt, sm3_rounds_prefix, sizeof (sm3_rounds_prefix) - 1)
+            == 0) {
+        const char *num = salt + sizeof (sm3_rounds_prefix) - 1;
+        char *endp;
+        unsigned long int srounds = strtoul (num, &endp, 10);
+        if (*endp == '$') {
+            salt = endp + 1;
+            rounds = MAX (ROUNDS_MIN, MIN (srounds, ROUNDS_MAX));
+            rounds_custom = true;
+        }
+    }
+
+    salt_len = MIN (strcspn (salt, "$"), SALT_LEN_MAX);
+    key_len = strlen (key);
+
+    if ((key - (char *) 0) % __alignof__ (uint32_t) != 0) {
+
+        char *tmp;
+
+        if (__libc_use_alloca (alloca_used + key_len + __alignof__ (uint32_t)))
+            tmp = alloca_account (key_len + __alignof__ (uint32_t), alloca_used);
+        else {
+            free_key = tmp = (char *) malloc (key_len + __alignof__ (uint32_t));
+            if (tmp == NULL)
+                return NULL;
+        }
+
+        key = copied_key = memcpy (tmp + __alignof__ (uint32_t)
+                  - (tmp - (char *) 0) % __alignof__ (uint32_t),
+                  key, key_len);
+        assert ((key - (char *) 0) % __alignof__ (uint32_t) == 0);
+    }
+
+    if ((salt - (char *) 0) % __alignof__ (uint32_t) != 0) {
+        char *tmp = (char *) alloca (salt_len + __alignof__ (uint32_t));
+        alloca_used += salt_len + __alignof__ (uint32_t);
+        salt = copied_salt = memcpy (tmp + __alignof__ (uint32_t)
+                  - (tmp - (char *) 0) % __alignof__ (uint32_t),
+                  salt, salt_len);
+        assert ((salt - (char *) 0) % __alignof__ (uint32_t) == 0);
+    }
+
+#ifdef USE_NSS
+  /* Initialize libfreebl3.  */
+    NSSLOWInitContext *nss_ictx = NSSLOW_Init ();
+    if (nss_ictx == NULL) {
+      free (free_key);
+      return NULL;
+    }
+    NSSLOWHASHContext *nss_ctx = NULL;
+    NSSLOWHASHContext *nss_alt_ctx = NULL;
+#else
+    SM3_CTX ctx;
+    SM3_CTX alt_ctx;
+#endif
+
+    /* Prepare for the real work.  */
+    sm3_init_ctx (&ctx, nss_ctx);
+
+    /* Add the key string.  */
+    sm3_process_bytes (key, key_len, &ctx, nss_ctx);
+
+    /* The last part is the salt string.  This must be at most 16
+        characters and it ends at the first `$' character.  */
+    sm3_process_bytes (salt, salt_len, &ctx, nss_ctx);
+
+
+    /* Compute alternate SM3 sum with input KEY, SALT, and KEY.  The
+        final result will be added to the first context.  */
+    sm3_init_ctx (&alt_ctx, nss_alt_ctx);
+
+    /* Add key.  */
+    sm3_process_bytes (key, key_len, &alt_ctx, nss_alt_ctx);
+
+    /* Add salt.  */
+    sm3_process_bytes (salt, salt_len, &alt_ctx, nss_alt_ctx);
+
+    /* Add key again.  */
+    sm3_process_bytes (key, key_len, &alt_ctx, nss_alt_ctx);
+
+    /* Now get result of this (32 bytes) and add it to the other
+        context.  */
+    sm3_finish_ctx (&alt_ctx, nss_alt_ctx, alt_result);
+
+    /* Add for any character in the key one byte of the alternate sum.  */
+    for (cnt = key_len; cnt > 32; cnt -= 32)
+        sm3_process_bytes (alt_result, 32, &ctx, nss_ctx);
+    sm3_process_bytes (alt_result, cnt, &ctx, nss_ctx);
+
+    /* Take the binary representation of the length of the key and for every
+      1 add the alternate sum, for every 0 the key.  */
+    for (cnt = key_len; cnt > 0; cnt >>= 1)
+        if ((cnt & 1) != 0)
+            sm3_process_bytes (alt_result, 32, &ctx, nss_ctx);
+        else
+            sm3_process_bytes (key, key_len, &ctx, nss_ctx);
+
+    /* Create intermediate result.  */
+    sm3_finish_ctx (&ctx, nss_ctx, alt_result);
+
+    /* Start computation of P byte sequence.  */
+    sm3_init_ctx (&alt_ctx, nss_alt_ctx);
+
+    /* For every character in the password add the entire password.  */
+    for (cnt = 0; cnt < key_len; ++cnt)
+        sm3_process_bytes (key, key_len, &alt_ctx, nss_alt_ctx);
+
+    /* Finish the digest.  */
+    sm3_finish_ctx (&alt_ctx, nss_alt_ctx, temp_result);
+
+    /* Create byte sequence P.  */
+    if (__libc_use_alloca (alloca_used + key_len))
+        cp = p_bytes = (char *) alloca (key_len);
+    else {
+        free_pbytes = cp = p_bytes = (char *)malloc (key_len);
+        if (free_pbytes == NULL) {
+            free (free_key);
+            return NULL;
+        }
+    }
+
+    for (cnt = key_len; cnt >= 32; cnt -= 32)
+        cp = mempcpy (cp, temp_result, 32);
+    memcpy (cp, temp_result, cnt);
+
+    /* Start computation of S byte sequence.  */
+    sm3_init_ctx (&alt_ctx, nss_alt_ctx);
+
+    /* For every character in the password add the entire password.  */
+    for (cnt = 0; cnt < 16 + alt_result[0]; ++cnt)
+        sm3_process_bytes (salt, salt_len, &alt_ctx, nss_alt_ctx);
+
+    /* Finish the digest.  */
+    sm3_finish_ctx (&alt_ctx, nss_alt_ctx, temp_result);
+
+    /* Create byte sequence S.  */
+    cp = s_bytes = alloca (salt_len);
+    for (cnt = salt_len; cnt >= 32; cnt -= 32)
+        cp = mempcpy (cp, temp_result, 32);
+    memcpy (cp, temp_result, cnt);
+
+    /* Repeatedly run the collected hash value through SM3 to burn
+        CPU cycles.  */
+    for (cnt = 0; cnt < rounds; ++cnt) {
+        /* New context.  */
+        sm3_init_ctx (&ctx, nss_ctx);
+
+        /* Add key or last result.  */
+        if ((cnt & 1) != 0)
+            sm3_process_bytes (p_bytes, key_len, &ctx, nss_ctx);
+        else
+            sm3_process_bytes (alt_result, 32, &ctx, nss_ctx);
+
+        /* Add salt for numbers not divisible by 3.  */
+        if (cnt % 3 != 0)
+            sm3_process_bytes (s_bytes, salt_len, &ctx, nss_ctx);
+
+        /* Add key for numbers not divisible by 7.  */
+        if (cnt % 7 != 0)
+            sm3_process_bytes (p_bytes, key_len, &ctx, nss_ctx);
+
+        /* Add key or last result.  */
+        if ((cnt & 1) != 0)
+            sm3_process_bytes (alt_result, 32, &ctx, nss_ctx);
+        else
+            sm3_process_bytes (p_bytes, key_len, &ctx, nss_ctx);
+
+        /* Create intermediate result.  */
+        sm3_finish_ctx (&ctx, nss_ctx, alt_result);
+    }
+
+#ifdef USE_NSS
+    /* Free libfreebl3 resources. */
+    NSSLOW_Shutdown (nss_ictx);
+#endif
+
+    /* Now we can construct the result string.  It consists of three
+      parts.  */
+    cp = __stpncpy (buffer, sm3_salt_prefix, MAX (0, buflen));
+    buflen -= sizeof (sm3_salt_prefix) - 1;
+
+    if (rounds_custom) {
+        int n = __snprintf (cp, MAX (0, buflen), "%s%zu$",
+                  sm3_rounds_prefix, rounds);
+        cp += n;
+        buflen -= n;
+    }
+
+    cp = __stpncpy (cp, salt, MIN ((size_t) MAX (0, buflen), salt_len));
+    buflen -= MIN ((size_t) MAX (0, buflen), salt_len);
+
+    if (buflen > 0) {
+      *cp++ = '$';
+      --buflen;
+    }
+
+    __b64_from_24bit (&cp, &buflen,
+              alt_result[0], alt_result[10], alt_result[20], 4);
+    __b64_from_24bit (&cp, &buflen,
+              alt_result[21], alt_result[1], alt_result[11], 4);
+    __b64_from_24bit (&cp, &buflen,
+              alt_result[12], alt_result[22], alt_result[2], 4);
+    __b64_from_24bit (&cp, &buflen,
+              alt_result[3], alt_result[13], alt_result[23], 4);
+    __b64_from_24bit (&cp, &buflen,
+              alt_result[24], alt_result[4], alt_result[14], 4);
+    __b64_from_24bit (&cp, &buflen,
+              alt_result[15], alt_result[25], alt_result[5], 4);
+    __b64_from_24bit (&cp, &buflen,
+              alt_result[6], alt_result[16], alt_result[26], 4);
+    __b64_from_24bit (&cp, &buflen,
+              alt_result[27], alt_result[7], alt_result[17], 4);
+    __b64_from_24bit (&cp, &buflen,
+              alt_result[18], alt_result[28], alt_result[8], 4);
+    __b64_from_24bit (&cp, &buflen,
+              alt_result[9], alt_result[19], alt_result[29], 4);
+    __b64_from_24bit (&cp, &buflen,
+              0, alt_result[31], alt_result[30], 3);
+    if (buflen <= 0) {
+        __set_errno (ERANGE);
+        buffer = NULL;
+    } else
+        *cp = '\0';        /* Terminate the string.  */
+
+    /* Clear the buffer for the intermediate result so that people
+      attaching to processes or reading core dumps cannot get any
+      information.  We do it in this way to clear correct_words[]
+      inside the SM3 implementation as well.  */
+#ifndef USE_NSS
+    __sm3_init_ctx (&ctx);
+    __sm3_finish_ctx (&ctx, alt_result);
+    explicit_bzero (&ctx, sizeof (ctx));
+    explicit_bzero (&alt_ctx, sizeof (alt_ctx));
+#endif
+    explicit_bzero (temp_result, sizeof (temp_result));
+    explicit_bzero (p_bytes, key_len);
+    explicit_bzero (s_bytes, salt_len);
+    if (copied_key != NULL)
+        explicit_bzero (copied_key, key_len);
+    if (copied_salt != NULL)
+        explicit_bzero (copied_salt, salt_len);
+
+    free (free_key);
+    free (free_pbytes);
+    return buffer;
+}
+
+#ifndef _LIBC
+# define libc_freeres_ptr(decl) decl
+#endif
+libc_freeres_ptr (static char *buffer);
+
+/* This entry point is equivalent to the `crypt' function in Unix
+   libcs.  */
+char *__sm3_crypt (const char *key, const char *salt)
+{
+    /* We don't want to have an arbitrary limit in the size of the
+      password.  We can compute an upper bound for the size of the
+      result in advance and so we can prepare the buffer we pass to
+      `sm3_crypt_r'.  */
+    static int buflen;
+    int needed = (sizeof (sm3_salt_prefix) - 1
+          + sizeof (sm3_rounds_prefix) + 9 + 1
+          + strlen (salt) + 1 + 43 + 1);
+
+    if (buflen < needed) {
+        char *new_buffer = (char *) realloc (buffer, needed);
+        if (new_buffer == NULL)
+            return NULL;
+
+        buffer = new_buffer;
+        buflen = needed;
+    }
+
+    return __sm3_crypt_r (key, salt, buffer, buflen);
+}
+
+#ifndef _LIBC
+static void
+__attribute__ ((__destructor__))
+free_mem (void)
+{
+    free (buffer);
+}
+#endif
diff --git a/crypt/sm3.c b/crypt/sm3.c
new file mode 100644
index 0000000000..260a8b53d2
--- /dev/null
+++ b/crypt/sm3.c
@@ -0,0 +1,341 @@ 
+/* Functions to compute SM3 message digest of files or memory blocks.
+   according to the definition of SM3 in GM/T 0004-2012.
+   Copyright (C) 2022-2022 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library 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.
+
+   The GNU C Library 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 the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* Written by Mingjun Yang <yangmingjun@uniontech.com>, 2022.  */
+
+#include "sm3.h"
+#include <string.h>
+#include <stdio.h>
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+# ifdef _LIBC
+#  include <byteswap.h>
+#  define SWAP(n) bswap_32 (n)
+#  define SWAP64(n) bswap_64 (n)
+# else
+#  define SWAP(n) \
+    (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))
+#  define SWAP64(n) \
+  (((n) << 56)					\
+   | (((n) & 0xff00) << 40)			\
+   | (((n) & 0xff0000) << 24)			\
+   | (((n) & 0xff000000) << 8)			\
+   | (((n) >> 8) & 0xff000000)			\
+   | (((n) >> 24) & 0xff0000)			\
+   | (((n) >> 40) & 0xff00)			\
+   | ((n) >> 56))
+# endif
+#else
+# define SWAP(n) (n)
+# define SWAP64(n) (n)
+#endif
+
+/* This array contains the bytes used to pad the buffer to the next
+   64-byte boundary.  (in section 5.2 of GM/T 0004-2012)  */
+static const unsigned char sm3_padding[64] = {
+    0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+//constants for SM3 in section 4.2 of GM/T 0004-2012
+static const uint32_t T[64] = {
+    0x79CC4519, 0x79CC4519, 0x79CC4519, 0x79CC4519,
+    0x79CC4519, 0x79CC4519, 0x79CC4519, 0x79CC4519,
+    0x79CC4519, 0x79CC4519, 0x79CC4519, 0x79CC4519,
+    0x79CC4519, 0x79CC4519, 0x79CC4519, 0x79CC4519,
+    0x7A879D8A, 0x7A879D8A, 0x7A879D8A, 0x7A879D8A,
+    0x7A879D8A, 0x7A879D8A, 0x7A879D8A, 0x7A879D8A,
+    0x7A879D8A, 0x7A879D8A, 0x7A879D8A, 0x7A879D8A,
+    0x7A879D8A, 0x7A879D8A, 0x7A879D8A, 0x7A879D8A,
+    0x7A879D8A, 0x7A879D8A, 0x7A879D8A, 0x7A879D8A,
+    0x7A879D8A, 0x7A879D8A, 0x7A879D8A, 0x7A879D8A,
+    0x7A879D8A, 0x7A879D8A, 0x7A879D8A, 0x7A879D8A,
+    0x7A879D8A, 0x7A879D8A, 0x7A879D8A, 0x7A879D8A,
+    0x7A879D8A, 0x7A879D8A, 0x7A879D8A, 0x7A879D8A,
+    0x7A879D8A, 0x7A879D8A, 0x7A879D8A, 0x7A879D8A,
+    0x7A879D8A, 0x7A879D8A, 0x7A879D8A, 0x7A879D8A,
+    0x7A879D8A, 0x7A879D8A, 0x7A879D8A, 0x7A879D8A
+};
+
+/* Process LEN bytes of BUFFER, accumulating context into CTX.
+   It is assumed that LEN % 64 == 0.  */
+void __sm3_process_block(const void *buffer, size_t len, SM3_CTX *ctx)
+{
+    const uint32_t *words = buffer;
+    size_t nwords = len / sizeof (uint32_t);
+
+    // section 5.3.3 in GM/T 0004-2012
+    uint32_t A, B, C, D, E, F, G, H;
+    A = ctx->H[0];
+    B = ctx->H[1];
+    C = ctx->H[2];
+    D = ctx->H[3];
+    E = ctx->H[4];
+    F = ctx->H[5];
+    G = ctx->H[6];
+    H = ctx->H[7];
+
+    if(nwords % 16 != 0)
+        return;
+
+    ctx->total64 += len;
+
+    while (nwords > 0) {
+
+        uint32_t W[68], W1[64];
+        memset(W, 0, sizeof(W));
+        memset(W1, 0, sizeof(W1));
+        uint32_t last_A = A;
+        uint32_t last_B = B;
+        uint32_t last_C = C;
+        uint32_t last_D = D;
+        uint32_t last_E = E;
+        uint32_t last_F = F;
+        uint32_t last_G = G;
+        uint32_t last_H = H;
+
+// Operators defined in section 4.3 of GM/T 0004-2012
+#define FF0(x,y,z) ( (x) ^ (y) ^ (z))
+#define FF1(x,y,z) (((x) & (y)) | ( (x) & (z)) | ( (y) & (z)))
+
+#define GG0(x,y,z) ( (x) ^ (y) ^ (z))
+#define GG1(x,y,z) (((x) & (y)) | ( (~(x)) & (z)) )
+
+#define ROTL(a,n) (((a) << (n)) | (((a) & 0xffffffff) >> (32-(n))))
+
+// Operators defined in section 4.4 of GM/T 0004-2012
+#define P0(x) ((x) ^  ROTL((x),  9) ^ ROTL((x), 17))
+#define P1(x) ((x) ^  ROTL((x), 15) ^ ROTL((x), 23))
+
+        int j;
+        for(j = 0; j < 16; j++) {
+            W[j] = SWAP(*words);
+            ++words;
+        }
+
+#ifdef _DEBUG_SM3_CRYPT_
+        printf("Message with padding:\n");
+        int i;
+        for(i=0; i< 8; i++)
+            printf("%08x ",W[i]);
+        printf("\n");
+        for(i=8; i< 16; i++)
+            printf("%08x ",W[i]);
+        printf("\n");
+#endif//_DEBUG_SM3_CRYPT_
+
+        // section 5.3.2 in GM/T 0004-2012
+        for(j = 16; j <= 67; j++) {
+            W[j] = P1(W[j-16] ^ W[j-9] ^ ROTL(W[j-3], 15)) ^ ROTL(W[j - 13], 7) ^ W[j-6];
+        }
+
+        for(j =  0; j < 64; j++) {
+            W1[j] = W[j] ^ W[j+4];
+        }
+
+#ifdef _DEBUG_SM3_CRYPT_
+        printf("Expanding message W0-67:\n");
+        for(i=0; i<68; i++) {
+            printf("%08x ",W[i]);
+            if(((i+1) % 8) == 0) printf("\n");
+        }
+        printf("\n");
+
+        printf("Expanding message W'0-63:\n");
+        for(i=0; i<64; i++) {
+            printf("%08x ",W1[i]);
+            if(((i+1) % 8) == 0) printf("\n");
+        }
+        printf("\n");
+
+        printf("j     A       B        C         D         E        F        G       H\n");
+        printf("   %08x %08x %08x %08x %08x %08x %08x %08x\n",A,B,C,D,E,F,G,H);
+#endif//_DEBUG_SM3_CRYPT_
+
+        uint32_t SS1=0, SS2=0, TT1=0, TT2=0;
+        for(j = 0; j <= 63; j++) {
+            SS1 = ROTL((ROTL(A, 12) + E + ROTL(T[j], (j%32))), 7);
+            SS2 = SS1 ^ ROTL(A, 12);
+            TT1 = (j < 16 ? FF0(A,B,C) : FF1(A,B,C)) + D + SS2 + W1[j];
+            TT2 = (j < 16 ? GG0(E,F,G) : GG1(E,F,G)) + H + SS1 + W[j];
+            D = C;
+            C = ROTL(B, 9);
+            B = A;
+            A = TT1;
+            H = G;
+            G = ROTL(F, 19);
+            F = E;
+            E = P0(TT2);
+#ifdef _DEBUG_SM3_CRYPT_
+            printf("%02d %08x %08x %08x %08x %08x %08x %08x %08x\n",j,A,B,C,D,E,F,G,H);
+#endif//_DEBUG_SM3_CRYPT_
+        }
+
+        // V(i+1) = ABCDEFGH ^ V(i)
+        A ^= last_A;
+        B ^= last_B;
+        C ^= last_C;
+        D ^= last_D;
+        E ^= last_E;
+        F ^= last_F;
+        G ^= last_G;
+        H ^= last_H;
+
+        /* Prepare for the next round.  */
+        nwords -= 16;
+    }
+
+    ctx->H[0] = A;
+    ctx->H[1] = B;
+    ctx->H[2] = C;
+    ctx->H[3] = D;
+    ctx->H[4] = E;
+    ctx->H[5] = F;
+    ctx->H[6] = G;
+    ctx->H[7] = H;
+#ifdef _DEBUG_SM3_CRYPT_
+    printf("   %08x %08x %08x %08x %08x %08x %08x %08x\n\n",ctx->H[0],ctx->H[1],ctx->H[2],
+                                  ctx->H[3],ctx->H[4],ctx->H[5],ctx->H[6],ctx->H[7]);
+#endif//_DEBUG_SM3_CRYPT_
+}
+
+/* Initialize structure containing state of computation. */
+void __sm3_init_ctx( SM3_CTX *ctx )
+{
+    memset(ctx, 0, sizeof(SM3_CTX));
+
+    // IV in section 4.1 of GM/T 0004-2012
+    ctx->H[0] = 0x7380166F;
+    ctx->H[1] = 0x4914B2B9;
+    ctx->H[2] = 0x172442D7;
+    ctx->H[3] = 0xDA8A0600;
+    ctx->H[4] = 0xA96F30BC;
+    ctx->H[5] = 0x163138AA;
+    ctx->H[6] = 0xE38DEE4D;
+    ctx->H[7] = 0xB0FB0E4E;
+}
+
+/* Process the remaining bytes in the internal buffer and the usual
+   prolog according to the standard and write the result to RESBUF.
+
+   IMPORTANT: On some systems it is required that RESBUF is correctly
+   aligned for a 32 bits value.  */
+void *__sm3_finish_ctx (SM3_CTX *ctx, void *resbuf)
+{
+    /* Take yet unprocessed bytes into account. */
+    uint32_t bytes = ctx->buflen;
+    size_t pad;
+
+    /* Now count remaining bytes.  */
+    ctx->total64 += bytes;
+
+    pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes;
+    memcpy (&ctx->buffer[bytes], sm3_padding, pad);
+
+    /* Put the 64-bit file length in *bits* at the end of the buffer.  */
+#if _STRING_ARCH_unaligned
+    ctx->buffer64[(bytes + pad) / 8] = SWAP64 (ctx->total64 << 3);
+#else
+    ctx->buffer32[(bytes + pad + 4) / 4] = SWAP (ctx->total[TOTAL64_low] << 3);
+    // both total[0] and total[1] * 8 (<< 3), and they operate >> 32.
+    // total[TOTAL64_high] << 3, (total[TOTAL64_low] << 3) >> 32.
+    ctx->buffer32[(bytes + pad) / 4] = SWAP ((ctx->total[TOTAL64_high] << 3) |
+                                             (ctx->total[TOTAL64_low] >> 29));
+#endif
+
+    /* Process last bytes.  */
+    __sm3_process_block (ctx->buffer, bytes + pad + 8, ctx);
+
+    /* Put result from CTX in first 32 bytes following RESBUF.  */
+    for (unsigned int i = 0; i < 8; ++i)
+        ((uint32_t *) resbuf)[i] = SWAP (ctx->H[i]);
+
+    return resbuf;
+}
+
+void __sm3_process_bytes (const void *buffer, size_t len, SM3_CTX *ctx)
+{
+    /* When we already have some bits in our internal buffer concatenate
+        both inputs first.  */
+    if (ctx->buflen != 0) {
+        size_t left_over = ctx->buflen;
+        size_t add = 128 - left_over > len ? len : 128 - left_over;
+
+        memcpy (&ctx->buffer[left_over], buffer, add);
+        ctx->buflen += add;
+
+        if (ctx->buflen > 64) {
+            __sm3_process_block (ctx->buffer, ctx->buflen & ~63, ctx);
+
+            ctx->buflen &= 63;
+            /* The regions in the following copy operation cannot overlap.  */
+            memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63],
+            ctx->buflen);
+        }
+
+        buffer = (const char *) buffer + add;
+        len -= add;
+    }
+
+    /* Process available complete blocks.  */
+    if (len >= 64) {
+
+#if !_STRING_ARCH_unaligned
+/* To check alignment gcc has an appropriate operator. Other
+    compilers don't.  */
+# if __GNUC__ >= 2
+#   define UNALIGNED_P(p) (((uintptr_t) p) % __alignof__ (uint32_t) != 0)
+# else
+#   define UNALIGNED_P(p) (((uintptr_t) p) % sizeof (uint32_t) != 0)
+# endif
+        if (UNALIGNED_P (buffer))
+            while (len > 64) {
+                __sm3_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx);
+                buffer = (const char *) buffer + 64;
+                len -= 64;
+            }
+        else
+#endif
+        {
+            __sm3_process_block (buffer, len & ~63, ctx);
+            buffer = (const char *) buffer + (len & ~63);
+            len &= 63;
+        }
+    }
+
+    /* Move remaining bytes into internal buffer.  */
+    if (len > 0) {
+        size_t left_over = ctx->buflen;
+
+        memcpy (&ctx->buffer[left_over], buffer, len);
+        left_over += len;
+        if (left_over >= 64) {
+            __sm3_process_block (ctx->buffer, 64, ctx);
+            left_over -= 64;
+            memcpy (ctx->buffer, &ctx->buffer[64], left_over);
+        }
+        ctx->buflen = left_over;
+    }
+}
\ No newline at end of file
diff --git a/crypt/sm3.h b/crypt/sm3.h
new file mode 100644
index 0000000000..fae73cc8c3
--- /dev/null
+++ b/crypt/sm3.h
@@ -0,0 +1,80 @@ 
+/* 
+    Declaration of functions and data types used for SM3 sum computing
+    library functions.
+    Copyright (C) 2022-2022 Free Software Foundation, Inc.
+    This file is part of the GNU C Library.
+
+    The GNU C Library 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.
+
+    The GNU C Library 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 the GNU C Library; if not, see
+    <http://www.gnu.org/licenses/>.
+
+    Written by Mingjun Yang <yangmingjun@uniontech.com>, 2022.
+*/
+
+#ifndef _SM3_CRYPT_H_
+#define _SM3_CRYPT_H_
+#include <stddef.h>
+#include <stdint.h>
+#include <endian.h>
+
+// #define _DEBUG_SM3_CRYPT_
+
+/* Structure to save state of computation between the single steps.  */
+typedef struct SM3State_st {
+    uint32_t H[8];     /* intermediate digest state  */
+
+    union {
+        uint64_t total64;
+#define TOTAL64_low (1 - (BYTE_ORDER == LITTLE_ENDIAN))
+#define TOTAL64_high (BYTE_ORDER == LITTLE_ENDIAN)
+        uint32_t total[2];     /* number of bytes processed  */
+    };
+
+    uint32_t buflen;
+
+    union {
+        unsigned char buffer[128];   /* data block being processed */
+        uint32_t buffer32[32];
+        uint64_t buffer64[16];
+    };
+} SM3_CTX;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \brief           Initialize structure containing state of computation.
+ * \param ctx       context to be initialized
+ */
+void __sm3_init_ctx(SM3_CTX *ctx);
+
+/**
+ * \brief           SM3 process buffer
+ * \param buffer    buffer holding the  data
+ * \param len       length of the input data
+ * \param ctx       SM3 context
+ */
+void __sm3_process_bytes(const void *buffer, size_t len, SM3_CTX *ctx);
+
+/**
+ * \brief           SM3 final digest
+ * \param ctx       SM3 context
+ */
+void* __sm3_finish_ctx(SM3_CTX *ctx, void *resbuf);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SM3_CRYPT_H_ */
diff --git a/crypt/sm3c-test.c b/crypt/sm3c-test.c
new file mode 100644
index 0000000000..2160e8a818
--- /dev/null
+++ b/crypt/sm3c-test.c
@@ -0,0 +1,53 @@ 
+/* Written by Mingjun Yang <yangmingjun@uniontech.com>, 2022.  */
+
+#include <crypt.h>
+#include <stdio.h>
+#include <string.h>
+
+static const struct
+{
+    const char *salt;
+    const char *input;
+    const char *expected;
+} tests[] = {
+    { "$8$saltstring", "Hello world!",
+      "$8$saltstring$6RCuSuWbADZmLALkvsvtcYYzhrw3xxpuDcqwdPIWxTD" },
+    { "$8$rounds=10000$saltstringsaltstring", "Hello world!",
+      "$8$rounds=10000$saltstringsaltst$H.iZTrCfA6u8TuT4tXLwKdsEKgdqnNHdXlB3./SazdB" },
+    { "$8$rounds=5000$toolongsaltstring", "This is just a test",
+      "$8$rounds=5000$toolongsaltstrin$8EF2.EaVGKi1KdgOPVnq2i/K3.yjZkaid6gTpnJaT.D" },
+    { "$8$rounds=1400$anotherlongsaltstring",
+      "a very much longer text to encrypt.  This one even stretches over more"
+      "than one line.",
+      "$8$rounds=1400$anotherlongsalts$S6rNaiqWdeULVlmEVpHbJMsms5thLSB0HIMy42IL/02" },
+    { "$8$rounds=77777$short",
+      "we have a short salt string but not a short password",
+      "$8$rounds=77777$short$Fw1rxIQJYMzgn761Abq0wy/xMEKT2UVT3/cDDrpU4G3" },
+    { "$8$rounds=123456$asaltof16chars..", "a short string",
+      "$8$rounds=123456$asaltof16chars..$lu9bW/WDKvC40Ee5V0KEnH3AtUTOvwKTY8a5vlzpDX/" },
+    { "$8$rounds=10$roundstoolow", "the minimum number is still observed",
+      "$8$rounds=1000$roundstoolow$JJfjMGRVfyti6fC.k1HkxwE5wvJJ6L7QdJJLlkddD3A" },
+};
+#define ntests (sizeof (tests) / sizeof (tests[0]))
+
+static int do_test (void)
+{
+    int result = 0;
+    int i;
+
+    for (i = 0; i < ntests; ++i) {
+        char *cp = crypt (tests[i].input, tests[i].salt);
+
+        if (NULL == cp || strcmp (cp, tests[i].expected) != 0) {
+            printf ("test %d: expected \"%s\", got \"%s\"\n",
+                    i, tests[i].expected, cp);
+            result = 1;
+        }
+    }
+
+    return result;
+}
+
+// #define TIMEOUT 6
+// #define TEST_FUNCTION do_test ()
+// #include "../test-skeleton.c"
\ No newline at end of file
diff --git a/crypt/sm3test.c b/crypt/sm3test.c
new file mode 100644
index 0000000000..0f12cd0462
--- /dev/null
+++ b/crypt/sm3test.c
@@ -0,0 +1,60 @@ 
+/*  Written by Mingjun Yang <yangmingjun@uniontech.com>, 2022.
+
+    Testing data from SM3 Standards in GM/T 0004-2012
+    Sample 1
+    Input:"abc"
+    Output:66c7f0f4 62eeedd9 d1f2d46b dc10e4e2 4167c487 5cf2f7a2 297da02b 8f4ba8e0
+
+    Sample 2
+    Input:"abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd"
+    Outpuf:debe9ff9 2275b8a1 38604889 c18e5a4d 6fdb70e5 387e5765 293dcba3 9c0c5732
+*/
+
+#include <string.h>
+#include <stdio.h>
+#include "sm3.h"
+
+static const struct
+{
+    const char *input;
+    const char result[32];
+} tests[] = {
+    /* Test vectors from GM/T 0004-2012, Appendix A.1 and A.2.  */
+    { "abc",
+        "\x66\xc7\xf0\xf4\x62\xee\xed\xd9\xd1\xf2\xd4\x6b\xdc\x10\xe4\xe2"
+        "\x41\x67\xc4\x87\x5c\xf2\xf7\xa2\x29\x7d\xa0\x2b\x8f\x4b\xa8\xe0" },
+    { "abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd",
+        "\xde\xbe\x9f\xf9\x22\x75\xb8\xa1\x38\x60\x48\x89\xc1\x8e\x5a\x4d"
+        "\x6f\xdb\x70\xe5\x38\x7e\x57\x65\x29\x3d\xcb\xa3\x9c\x0c\x57\x32" }
+};
+
+int main (void)
+{
+    SM3_CTX ctx;
+    unsigned char dgst[32];
+    int result = 0;
+    int cnt;
+
+    for (cnt = 0; cnt < (int) (sizeof (tests) / sizeof (tests[0])); ++cnt) {
+        __sm3_init_ctx (&ctx);
+        __sm3_process_bytes (tests[cnt].input, strlen (tests[cnt].input), &ctx);
+        __sm3_finish_ctx (&ctx, dgst);
+
+        if (memcmp (tests[cnt].result, dgst, 32) != 0) {
+            printf ("test %d run %d failed\n", cnt, 1);
+            result = 1;
+        }
+
+        __sm3_init_ctx (&ctx);
+        for (int i = 0; tests[cnt].input[i] != '\0'; ++i)
+            __sm3_process_bytes (&tests[cnt].input[i], 1, &ctx);
+        __sm3_finish_ctx (&ctx, dgst);
+
+        if (memcmp (tests[cnt].result, dgst, 32) != 0) {
+            printf ("test %d run %d failed\n", cnt, 2);
+            result = 1;
+        }
+    }
+
+    return result;
+}