[PATCH] feat: crypt support sm3 algorithm
Mingjun Yang
yangmingjun@uniontech.com
Mon Aug 1 02:53:59 GMT 2022
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
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;
+}
--
2.20.1
More information about the Libc-alpha
mailing list