From d48b7d916b606a3adb038d76b95583c82debfda6 Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Thu, 22 Dec 2022 14:31:23 +0800 Subject: [PATCH] Add SM4 RNG --- include/gmssl/sm4_cbc_mac.h | 35 +++++ include/gmssl/sm4_rng.h | 43 ++++++ src/sm4_cbc_mac.c | 113 ++++++++++++++++ src/sm4_rng.c | 253 ++++++++++++++++++++++++++++++++++++ 4 files changed, 444 insertions(+) create mode 100644 include/gmssl/sm4_cbc_mac.h create mode 100644 include/gmssl/sm4_rng.h create mode 100644 src/sm4_cbc_mac.c create mode 100644 src/sm4_rng.c diff --git a/include/gmssl/sm4_cbc_mac.h b/include/gmssl/sm4_cbc_mac.h new file mode 100644 index 00000000..e803c8d7 --- /dev/null +++ b/include/gmssl/sm4_cbc_mac.h @@ -0,0 +1,35 @@ +/* + * Copyright 2014-2022 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +#ifndef GMSSL_SM4_CBC_MAC_H +#define GMSSL_SM4_CBC_MAC_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef struct { + SM4_KEY key; + uint8_t iv[16]; + size_t ivlen; +} SM4_CBC_MAC_CTX; + +void sm4_cbc_mac_init(SM4_CBC_MAC_CTX *ctx, const uint8_t key[16]); +void sm4_cbc_mac_update(SM4_CBC_MAC_CTX *ctx, const uint8_t *data, size_t datalen); +void sm4_cbc_mac_finish(SM4_CBC_MAC_CTX *ctx, uint8_t mac[16]); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/include/gmssl/sm4_rng.h b/include/gmssl/sm4_rng.h new file mode 100644 index 00000000..67e86dcf --- /dev/null +++ b/include/gmssl/sm4_rng.h @@ -0,0 +1,43 @@ +/* + * Copyright 2014-2022 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +#ifndef GMSSL_SM4_RNG_H +#define GMSSL_SM4_RNG_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +#define SM4_RNG_MAX_RESEED_COUNTER (1<<20) +#define SM4_RNG_MAX_RESEED_SECONDS 600 + +typedef struct { + uint8_t V[16]; + uint8_t K[16]; + uint32_t reseed_counter; + time_t last_reseed_time; +} SM4_RNG; + + +int sm4_rng_init(SM4_RNG *rng, const uint8_t *nonce, size_t nonce_len, + const uint8_t *label, size_t label_len); +int sm4_rng_update(SM4_RNG *rng, const uint8_t seed[32]); +int sm4_rng_reseed(SM4_RNG *rng, const uint8_t *addin, size_t addin_len); +int sm4_rng_generate(SM4_RNG *rng, const uint8_t *addin, size_t addin_len, + uint8_t *out, size_t outlen); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/sm4_cbc_mac.c b/src/sm4_cbc_mac.c new file mode 100644 index 00000000..f3833499 --- /dev/null +++ b/src/sm4_cbc_mac.c @@ -0,0 +1,113 @@ +/* + * Copyright 2014-2022 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +void sm4_cbc_mac_init(SM4_CBC_MAC_CTX *ctx, const uint8_t key[16]) +{ + sm4_set_encrypt_key(&ctx->key, key); + memset(ctx->iv, 0, 16); + ctx->ivlen = 0; +} + +void sm4_cbc_mac_update(SM4_CBC_MAC_CTX *ctx, const uint8_t *data, size_t datalen) +{ + while (datalen) { + size_t ivleft = 16 - ctx->ivlen; + size_t len = datalen < ivleft ? datalen : ivleft; + gmssl_memxor(ctx->iv + ctx->ivlen, ctx->iv + ctx->ivlen, data, len); + ctx->ivlen += len; + if (ctx->ivlen >= 16) { + sm4_encrypt(&ctx->key, ctx->iv, ctx->iv); + ctx->ivlen = 0; + } + data += len; + datalen -= len; + } +} + +void sm4_cbc_mac_finish(SM4_CBC_MAC_CTX *ctx, uint8_t mac[16]) +{ + if (ctx->ivlen) { + sm4_encrypt(&ctx->key, ctx->iv, ctx->iv); + } + memcpy(mac, ctx->iv, 16); +} + +static int test_sm4_cbc_mac(void) +{ + SM4_KEY sm4_key; + SM4_CBC_MAC_CTX ctx; + uint8_t key[16]; + uint8_t iv[16] = {0}; + uint8_t m[128]; + uint8_t c[128]; + uint8_t mac1[16]; + uint8_t mac2[16]; + uint8_t *p; + size_t len, left; + + rand_bytes(key, sizeof(key)); + rand_bytes(m, sizeof(m)); + sm4_set_encrypt_key(&sm4_key, key); + + // test 1 + sm4_cbc_encrypt(&sm4_key, iv, m, sizeof(m)/16, c); + memcpy(mac1, c + sizeof(m) - 16, 16); + + sm4_cbc_mac_init(&ctx, key); + p = m; + len = 0; + left = sizeof(m); + while (left) { + len = left < len ? left : len; + sm4_cbc_mac_update(&ctx, p, len); + p += len; + left -= len; + len++; + } + sm4_cbc_mac_finish(&ctx, mac2); + if (memcmp(mac1, mac2, 16)) { + error_print(); + return -1; + } + + // test 2 + m[sizeof(m) - 1] = 0; + sm4_cbc_encrypt(&sm4_key, iv, m, sizeof(m)/16, c); + memcpy(mac1, c + sizeof(m) - 16, 16); + + sm4_cbc_mac_init(&ctx, key); + p = m; + len = 0; + left = sizeof(m) - 1; + while (left) { + len = left < len ? left : len; + sm4_cbc_mac_update(&ctx, p, len); + p += len; + left -= len; + len++; + } + sm4_cbc_mac_finish(&ctx, mac2); + if (memcmp(mac1, mac2, 16)) { + error_print(); + return -1; + } + + printf("%s() ok\n", __FUNCTION__); + return 1; +} diff --git a/src/sm4_rng.c b/src/sm4_rng.c new file mode 100644 index 00000000..e6958bc9 --- /dev/null +++ b/src/sm4_rng.c @@ -0,0 +1,253 @@ +/* + * Copyright 2014-2022 The GmSSL Project. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +// see GM/T 0105-2021 Design Guide for Software-based Random Number Generators + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* +u8[16] R0, R1 + +(R0,R1) = sm4_df(in): + + L = nbytes(in) + N = 32 -- nbytes(R0||R1) + S = be32(L) || be32(N) || in || 0x80 || 0x00^*, nbytes(S) = 0 (mod 16) + K = 0x000102030405060708090a0b0c0d0e0f + + T = CBC_MAC(K, be32(0) || 0x00^12 || S) = CBC_MAC(K, be32(0) || 0x00^12 || be32(L) || be32(N) || in || 0x80) + X = CBC_MAC(K, be32(1) || 0x00^12 || S) = CBC_MAC(K, be32(1) || 0x00^12 || be32(L) || be32(N) || in || 0x80) + K = T + + R0 = sm4(K, X) + R1 = sm4(K, R0) +*/ + +typedef struct { + SM4_CBC_MAC_CTX cbc_mac_ctx[2]; + uint32_t len; + uint32_t len_check; +} SM4_DF_CTX; + +static void sm4_df_init(SM4_DF_CTX *df_ctx, size_t len) +{ + const uint8_t key[16] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; + uint8_t prefix[16] = {0}; + uint8_t Lbuf[4] = {0}; + uint8_t Nbuf[4] = {0}; + + Lbuf[0] = (len >> 24) & 0xff; + Lbuf[1] = (len >> 16) & 0xff; + Lbuf[2] = (len >> 8) & 0xff; + Lbuf[3] = len & 0xff; + + Nbuf[3] = 32; + + sm4_cbc_mac_init(&df_ctx->cbc_mac_ctx[0], key); + sm4_cbc_mac_update(&df_ctx->cbc_mac_ctx[0], prefix, 16); + sm4_cbc_mac_update(&df_ctx->cbc_mac_ctx[0], Lbuf, 4); + sm4_cbc_mac_update(&df_ctx->cbc_mac_ctx[0], Nbuf, 4); + + prefix[3] = 1; + sm4_cbc_mac_init(&df_ctx->cbc_mac_ctx[1], key); + sm4_cbc_mac_update(&df_ctx->cbc_mac_ctx[1], prefix, 16); + sm4_cbc_mac_update(&df_ctx->cbc_mac_ctx[1], Lbuf, 4); + sm4_cbc_mac_update(&df_ctx->cbc_mac_ctx[1], Nbuf, 4); + + df_ctx->len = (uint32_t)len; + df_ctx->len_check = 0; +} + +static void sm4_df_update(SM4_DF_CTX *df_ctx, const uint8_t *data, size_t datalen) +{ + sm4_cbc_mac_update(&df_ctx->cbc_mac_ctx[0], data, datalen); + sm4_cbc_mac_update(&df_ctx->cbc_mac_ctx[1], data, datalen); + df_ctx->len_check += datalen; +} + +static void sm4_df_finish(SM4_DF_CTX *df_ctx, uint8_t out[32]) +{ + const uint8_t suffix[1] = {0x80}; + uint8_t K[16]; + uint8_t X[16]; + SM4_KEY sm4_key; + + assert(df_ctx->len == df_ctx->len_check); + + sm4_cbc_mac_update(&df_ctx->cbc_mac_ctx[0], suffix, 1); + sm4_cbc_mac_finish(&df_ctx->cbc_mac_ctx[0], K); + sm4_cbc_mac_update(&df_ctx->cbc_mac_ctx[1], suffix, 1); + sm4_cbc_mac_finish(&df_ctx->cbc_mac_ctx[1], X); + + sm4_set_encrypt_key(&sm4_key, K); + sm4_encrypt(&sm4_key, X, out); + sm4_encrypt(&sm4_key, out, out + 16); + + gmssl_secure_clear(K, sizeof(K)); + gmssl_secure_clear(X, sizeof(X)); + gmssl_secure_clear(&sm4_key, sizeof(sm4_key)); +} + +static void be_incr(uint8_t a[16]) +{ + int i; + for (i = 15; i >= 0; i--) { + a[i]++; + if (a[i]) break; + } +} + + +int sm4_rng_update(SM4_RNG *rng, const uint8_t seed[32]) +{ + SM4_KEY sm4_key; + + sm4_set_encrypt_key(&sm4_key, rng->K); + be_incr(rng->V); + sm4_encrypt(&sm4_key, rng->V, rng->K); + be_incr(rng->V); + sm4_encrypt(&sm4_key, rng->V, rng->V); + + return 1; +} + +int sm4_rng_init(SM4_RNG *rng, const uint8_t *nonce, size_t nonce_len, + const uint8_t *label, size_t label_len) +{ + SM4_DF_CTX df_ctx; + uint8_t entropy[512]; + uint8_t seed[32]; + + // get_entropy, 512-byte might be too long for some system RNGs + if (rand_bytes(entropy, 256) != 1 + || rand_bytes(entropy + 256, 256) != 1) { + error_print(); + return -1; + } + + // seed = sm4_df(entropy || nonce || label) + sm4_df_init(&df_ctx, sizeof(entropy) + nonce_len + label_len); + sm4_df_update(&df_ctx, entropy, sizeof(entropy)); + sm4_df_update(&df_ctx, nonce, nonce_len); + sm4_df_update(&df_ctx, label, label_len); + sm4_df_finish(&df_ctx, seed); + + memset(rng->K, 0, 16); + memset(rng->V, 0, 16); + + // (K, V) = sm3_rng_update(seed, K, V) + sm4_rng_update(rng, seed); + + // reseed_counter = 1, last_ressed_time = now() + rng->reseed_counter = 1; + rng->last_reseed_time = time(NULL); + + gmssl_secure_clear(&df_ctx, sizeof(df_ctx)); + gmssl_secure_clear(entropy, sizeof(entropy)); + gmssl_secure_clear(seed, sizeof(seed)); + return 1; +} + +int sm4_rng_reseed(SM4_RNG *rng, const uint8_t *addin, size_t addin_len) +{ + SM4_DF_CTX df_ctx; + uint8_t entropy[512]; + uint8_t seed[32]; + + // get_entropy, 512-byte might be too long for some system RNGs + if (rand_bytes(entropy, 256) != 1 + || rand_bytes(entropy + 256, 256) != 1) { + error_print(); + return -1; + } + + // seed = sm4_df(entropy || addin) + sm4_df_init(&df_ctx, sizeof(entropy) + addin_len); + sm4_df_update(&df_ctx, entropy, sizeof(entropy)); + sm4_df_update(&df_ctx, addin, addin_len); + sm4_df_finish(&df_ctx, seed); + + sm4_rng_update(rng, seed); + + + gmssl_secure_clear(&df_ctx, sizeof(df_ctx)); + gmssl_secure_clear(entropy, sizeof(entropy)); + return 1; +} + + +#define SM4_RNG_MAX_RESEED_COUNTER (1<<20) +#define SM4_RNG_MAX_RESEED_SECONDS 600 + +int sm4_rng_generate(SM4_RNG *rng, const uint8_t *addin, size_t addin_len, + uint8_t *out, size_t outlen) +{ + uint8_t seed[32] = {0}; + SM4_KEY sm4_key; + + if (!outlen || outlen > 16) { + error_print(); + return -1; + } + + if (rng->reseed_counter > SM4_RNG_MAX_RESEED_COUNTER + || time(NULL) - rng->last_reseed_time > SM4_RNG_MAX_RESEED_SECONDS) { + if (sm4_rng_reseed(rng, addin, addin_len) != 1) { + error_print(); + return -1; + } + if (addin) { + addin = NULL; + } + } + + if (addin && addin_len) { + // seed = sm4_df(addin) + SM4_DF_CTX df_ctx; + sm4_df_init(&df_ctx, addin_len); + sm4_df_update(&df_ctx, addin, addin_len); + sm4_df_finish(&df_ctx, seed); + gmssl_secure_clear(&df_ctx, sizeof(df_ctx)); + + // rng_update(seed) + sm4_rng_update(rng, seed); + } + + // V = (V + 1) mod 2^128 + be_incr(rng->V); + + // output sm4(K, V)[0:outlen] + sm4_set_encrypt_key(&sm4_key, rng->K); + if (outlen < 16) { + uint8_t buf[16]; + sm4_encrypt(&sm4_key, rng->V, buf); + memcpy(out, buf, outlen); + } else { + sm4_encrypt(&sm4_key, rng->V, out); + } + + // (K, V) = update(seed, (K, V)) + sm4_rng_update(rng, seed); + + // reseed_counter++ + (rng->reseed_counter)++; + + + gmssl_secure_clear(seed, sizeof(seed)); + gmssl_secure_clear(&sm4_key, sizeof(sm4_key)); + return 1; +}