From 8ae03e8105a216109671c6e1f62d9fd85ce95864 Mon Sep 17 00:00:00 2001 From: Zhi Guan Date: Sat, 10 Feb 2024 15:46:59 +0800 Subject: [PATCH] Add more sm4 modes Not tested yet --- CMakeLists.txt | 4 + src/sm4_ccm.c | 122 +++++++++++++++++++++++++++++ src/sm4_cfb.c | 209 +++++++++++++++++++++++++++++++++++++++++++++++++ src/sm4_ecb.c | 95 ++++++++++++++++++++++ src/sm4_ofb.c | 100 +++++++++++++++++++++++ src/tls.c | 5 +- 6 files changed, 533 insertions(+), 2 deletions(-) create mode 100644 src/sm4_ccm.c create mode 100644 src/sm4_cfb.c create mode 100644 src/sm4_ecb.c create mode 100644 src/sm4_ofb.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f5fcd84c..100c0d2da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -268,6 +268,10 @@ if (ENABLE_SM4_AESNI_AVX) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=native") endif() +option(ENABLE_SM4_MORE_MODES "Enable SM4 ECB/CFB/OFB/CCM modes" OFF) +if (ENABLE_SM4_MORE_MODES) + list(APPEND src src/sm4_ecb.c src/sm4_cfb.c src/sm4_ofb.c src/sm4_ccm.c) +endif() option(ENABLE_SM2_EXTS "Enable SM2 Extensions" OFF) if (ENABLE_SM2_EXTS) diff --git a/src/sm4_ccm.c b/src/sm4_ccm.c new file mode 100644 index 000000000..ab701a418 --- /dev/null +++ b/src/sm4_ccm.c @@ -0,0 +1,122 @@ +/* + * Copyright 2014-2024 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 + + +#define SM4_CCM_MIN_IV_SIZE 7 +#define SM4_CCM_MAX_IV_SIZE 13 +#define SM4_CCM_MIN_MAC_SIZE 4 +#define SM4_CCM_MAX_MAC_SIZE 16 + + +static void length_to_bytes(size_t len, size_t nbytes, uint8_t *out) +{ + uint8_t *p = out + nbytes; + while (nbytes--) { + *p-- = len & 0xff; + len >>= 8; + } +} + +int sm4_ccm_encrypt(SM4_KEY *sm4_key, const uint8_t *iv, size_t ivlen, + const uint8_t *aad, size_t aadlen, const uint8_t *in, size_t inlen, + uint8_t *out, size_t taglen, uint8_t *tag) +{ + SM4_CBC_MAC_CTX cbc_mac_ctx; + size_t inlen_size; + uint8_t block[16] = {0}; + uint8_t ctr[16] = {0}; + uint8_t S0[16]; + uint8_t cbc_mac[16]; + const uint8_t zeros[16] = {0}; + size_t padding_len; + + if (ivlen < 7 || ivlen > 13) { + error_print(); + return -1; + } + if (!aad && aadlen) { + error_print(); + return -1; + } + if (taglen < 4 || taglen > 16 || taglen & 1) { + error_print(); + return -1; + } + + inlen_size = 15 - ivlen; + if (inlen >= (1 << (inlen_size * 8))) { + error_print(); + return -1; + } + + // sm4_cbc_mac_init + memset(&cbc_mac_ctx, 0, sizeof(cbc_mac_ctx)); + cbc_mac_ctx.key = *sm4_key; + + // first block + block[0] |= ((aadlen > 0) & 0x1) << 6; + block[0] |= (((taglen - 2)/2) & 0x7) << 3; + block[0] |= (inlen_size - 1) & 0x7; + memcpy(block + 1, iv, ivlen); + length_to_bytes(inlen, inlen_size, block + 1 + ivlen); + sm4_cbc_mac_update(&cbc_mac_ctx, block, 16); + + if (aad && aadlen) { + size_t alen; + + if (aadlen < ((1<<16) - (1<<8))) { + length_to_bytes(aadlen, 2, block); + alen = 2; + } else if (aadlen < ((size_t)1<<32)) { + block[0] = 0xff; + block[1] = 0xfe; + length_to_bytes(aadlen, 4, block + 2); + alen = 6; + } else { + block[0] = 0xff; + block[1] = 0xff; + length_to_bytes(aadlen, 8, block + 2); + } + sm4_cbc_mac_update(&cbc_mac_ctx, block, alen); + + sm4_cbc_mac_update(&cbc_mac_ctx, aad, aadlen); + + if (alen + aadlen % 16) { + sm4_cbc_mac_update(&cbc_mac_ctx, zeros, 16 - (alen + aadlen)%16); + } + } + + sm4_cbc_mac_update(&cbc_mac_ctx, in, inlen); + + if (inlen % 16) { + sm4_cbc_mac_update(&cbc_mac_ctx, zeros, 16 - inlen%16); + } + sm4_cbc_mac_finish(&cbc_mac_ctx, cbc_mac); + + ctr[0] = 0; + ctr[0] |= (inlen_size - 1) & 0x7; + memcpy(ctr + 1, iv, ivlen); + memset(ctr + 1 + ivlen, 0, 15 - ivlen); + + sm4_encrypt(sm4_key, ctr, S0); + gmssl_memxor(out, cbc_mac, S0, taglen); + + ctr[15] = 1; + sm4_ctr_encrypt(sm4_key, ctr, in, inlen, out); + + gmssl_secure_clear(&cbc_mac_ctx, sizeof(cbc_mac_ctx)); + return 1; +} + diff --git a/src/sm4_cfb.c b/src/sm4_cfb.c new file mode 100644 index 000000000..4d0573ed9 --- /dev/null +++ b/src/sm4_cfb.c @@ -0,0 +1,209 @@ +/* + * Copyright 2014-2024 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 + + +void sm4_cfb_encrypt(const SM4_KEY *key, size_t sbytes, uint8_t iv[16], + const uint8_t *in, size_t inlen, uint8_t *out) +{ + uint8_t block[16]; + size_t len, i; + + // assert(1 <= sbytes && sbytes <= 16); + + while (inlen) { + len = inlen < sbytes ? inlen : sbytes; + sm4_encrypt(key, iv, block); + gmssl_memxor(out, in, block, len); + + // iv = (iv << sbytes) | out + for (i = 0; i < 16 - sbytes; i++) { + iv[i] = iv[sbytes + i]; + } + memcpy(iv + i, out, len); + + in += len; + out += len; + inlen -= len; + } +} + +void sm4_cfb_decrypt(const SM4_KEY *key, size_t sbytes, uint8_t iv[16], + const uint8_t *in, size_t inlen, uint8_t *out) +{ + uint8_t block[16]; + size_t len, i; + + // assert(1 <= sbytes && sbytes <= 16); + + while (inlen) { + len = inlen < sbytes ? inlen : sbytes; + sm4_encrypt(key, iv, block); + gmssl_memxor(out, in, block, len); + + // iv = (iv << sbytes) | in + for (i = 0; i < 16 - sbytes; i++) { + iv[i] = iv[sbytes + i]; + } + memcpy(iv + i, in, len); + + in += len; + out += len; + inlen -= len; + } +} + +typedef struct { + SM4_KEY sm4_key; + uint8_t iv[SM4_BLOCK_SIZE]; + uint8_t block[SM4_BLOCK_SIZE]; + size_t block_nbytes; + size_t sbytes; +} SM4_CFB_CTX; + +int sm4_cfb_encrypt_init(SM4_CFB_CTX *ctx, size_t sbytes, + const uint8_t key[SM4_BLOCK_SIZE], const uint8_t iv[SM4_BLOCK_SIZE]) +{ + if (sbytes < 1 || sbytes > 16) { + error_print(); + return -1; + } + sm4_set_encrypt_key(&ctx->sm4_key, key); + memcpy(ctx->iv, iv, SM4_BLOCK_SIZE); + memset(ctx->block, 0, SM4_BLOCK_SIZE); + ctx->block_nbytes = 0; + ctx->sbytes = sbytes; + return 1; +} + +int sm4_cfb_encrypt_update(SM4_CFB_CTX *ctx, + const uint8_t *in, size_t inlen, uint8_t *out, size_t *outlen) +{ + size_t left; + size_t nblocks; + size_t len; + + if (ctx->block_nbytes >= ctx->sbytes) { + error_print(); + return -1; + } + *outlen = 0; + if (ctx->block_nbytes) { + left = ctx->sbytes - ctx->block_nbytes; + if (inlen < left) { + memcpy(ctx->block + ctx->block_nbytes, in, inlen); + ctx->block_nbytes += inlen; + return 1; + } + memcpy(ctx->block + ctx->block_nbytes, in, left); + sm4_cfb_encrypt(&ctx->sm4_key, ctx->sbytes, ctx->iv, ctx->block, ctx->sbytes, out); + in += left; + inlen -= left; + out += ctx->sbytes; + *outlen += ctx->sbytes; + } + if (inlen >= ctx->sbytes) { + nblocks = inlen / ctx->sbytes; + len = nblocks * ctx->sbytes; + sm4_cfb_encrypt(&ctx->sm4_key, ctx->sbytes, ctx->iv, in, len, out); + in += len; + inlen -= len; + out += len; + *outlen += len; + } + if (inlen) { + memcpy(ctx->block, in, inlen); + } + ctx->block_nbytes = inlen; + return 1; +} + +int sm4_cfb_encrypt_finish(SM4_CFB_CTX *ctx, uint8_t *out, size_t *outlen) +{ + if (ctx->block_nbytes >= ctx->sbytes) { + error_print(); + return -1; + } + sm4_cfb_encrypt(&ctx->sm4_key, ctx->sbytes, ctx->iv, ctx->block, ctx->block_nbytes, out); + *outlen = ctx->block_nbytes; + return 1; +} + +int sm4_cfb_decrypt_init(SM4_CFB_CTX *ctx, size_t sbytes, + const uint8_t key[SM4_BLOCK_SIZE], const uint8_t iv[SM4_BLOCK_SIZE]) +{ + if (sbytes < 1 || sbytes > 16) { + error_print(); + return -1; + } + sm4_set_encrypt_key(&ctx->sm4_key, key); + memcpy(ctx->iv, iv, SM4_BLOCK_SIZE); + memset(ctx->block, 0, SM4_BLOCK_SIZE); + ctx->block_nbytes = 0; + ctx->sbytes = sbytes; + return 1; +} + +int sm4_cfb_decrypt_update(SM4_CFB_CTX *ctx, + const uint8_t *in, size_t inlen, uint8_t *out, size_t *outlen) +{ + size_t left; + size_t nblocks; + size_t len; + + if (ctx->block_nbytes >= ctx->sbytes) { + error_print(); + return -1; + } + *outlen = 0; + if (ctx->block_nbytes) { + left = ctx->sbytes - ctx->block_nbytes; + if (inlen < left) { + memcpy(ctx->block + ctx->block_nbytes, in, inlen); + ctx->block_nbytes += inlen; + return 1; + } + memcpy(ctx->block + ctx->block_nbytes, in, left); + sm4_cfb_decrypt(&ctx->sm4_key, ctx->sbytes, ctx->iv, ctx->block, ctx->sbytes, out); + in += left; + inlen -= left; + out += ctx->sbytes; + *outlen += ctx->sbytes; + } + if (inlen >= ctx->sbytes) { + nblocks = inlen / ctx->sbytes; + len = nblocks * ctx->sbytes; + sm4_cfb_decrypt(&ctx->sm4_key, ctx->sbytes, ctx->iv, in, len, out); + in += len; + inlen -= len; + out += len; + *outlen += len; + } + if (inlen) { + memcpy(ctx->block, in, inlen); + } + ctx->block_nbytes = inlen; + return 1; +} + +int sm4_cfb_decrypt_finish(SM4_CFB_CTX *ctx, uint8_t *out, size_t *outlen) +{ + if (ctx->block_nbytes >= ctx->sbytes) { + error_print(); + return -1; + } + sm4_cfb_decrypt(&ctx->sm4_key, ctx->sbytes, ctx->iv, ctx->block, ctx->block_nbytes, out); + *outlen = ctx->block_nbytes; + return 1; +} + diff --git a/src/sm4_ecb.c b/src/sm4_ecb.c new file mode 100644 index 000000000..4b869f0bd --- /dev/null +++ b/src/sm4_ecb.c @@ -0,0 +1,95 @@ +/* + * Copyright 2014-2024 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 + + +void sm4_ecb_encrypt(const SM4_KEY *key, const uint8_t *in, size_t nblocks, uint8_t *out) +{ + while (nblocks--) { + sm4_encrypt(key, in, out); + in += SM4_BLOCK_SIZE; + out += SM4_BLOCK_SIZE; + } +} + + +typedef struct { + SM4_KEY sm4_key; + uint8_t block[SM4_BLOCK_SIZE]; + size_t block_nbytes; +} SM4_ECB_CTX; + +int sm4_ecb_encrypt_init(SM4_ECB_CTX *ctx, const uint8_t key[SM4_BLOCK_SIZE]) +{ + sm4_set_encrypt_key(&ctx->sm4_key, key); + memset(ctx->block, 0, SM4_BLOCK_SIZE); + ctx->block_nbytes = 0; + return 1; +} + +int sm4_efb_encrypt_update(SM4_ECB_CTX *ctx, + const uint8_t *in, size_t inlen, uint8_t *out, size_t *outlen) +{ + size_t left; + size_t nblocks; + size_t len; + + if (ctx->block_nbytes >= SM4_BLOCK_SIZE) { + error_print(); + return -1; + } + *outlen = 0; + if (ctx->block_nbytes) { + left = SM4_BLOCK_SIZE - ctx->block_nbytes; + if (inlen < left) { + memcpy(ctx->block + ctx->block_nbytes, in, inlen); + ctx->block_nbytes += inlen; + return 1; + } + memcpy(ctx->block + ctx->block_nbytes, in, left); + sm4_ecb_encrypt(&ctx->sm4_key, ctx->block, 1, out); + in += left; + inlen -= left; + out += SM4_BLOCK_SIZE; + *outlen += SM4_BLOCK_SIZE; + } + if (inlen >= SM4_BLOCK_SIZE) { + nblocks = inlen / SM4_BLOCK_SIZE; + len = nblocks * SM4_BLOCK_SIZE; + sm4_ecb_encrypt(&ctx->sm4_key, in, nblocks, out); + in += len; + inlen -= len; + out += len; + *outlen += len; + } + if (inlen) { + memcpy(ctx->block, in, inlen); + } + ctx->block_nbytes = inlen; + return 1; +} + +int sm4_ecb_encrypt_finish(SM4_ECB_CTX *ctx, uint8_t *out, size_t *outlen) +{ + if (ctx->block_nbytes >= SM4_BLOCK_SIZE) { + error_print(); + return -1; + } + if (ctx->block_nbytes) { + error_puts("invalid total input length"); + return -1; + } + *outlen = 0; + return 1; +} + diff --git a/src/sm4_ofb.c b/src/sm4_ofb.c new file mode 100644 index 000000000..748ff4ac5 --- /dev/null +++ b/src/sm4_ofb.c @@ -0,0 +1,100 @@ +/* + * Copyright 2014-2024 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 + + +void sm4_ofb_encrypt(const SM4_KEY *key, uint8_t iv[16], const uint8_t *in, size_t inlen, uint8_t *out) +{ + size_t len; + + while (inlen) { + len = inlen < 16 ? inlen : 16; + sm4_encrypt(key, iv, iv); + gmssl_memxor(out, in, iv, len); + in += len; + out += len; + inlen -= len; + } +} + + +typedef struct { + SM4_KEY sm4_key; + uint8_t iv[SM4_BLOCK_SIZE]; + uint8_t block[SM4_BLOCK_SIZE]; + size_t block_nbytes; +} SM4_OFB_CTX; + +int sm4_ofb_encrypt_init(SM4_OFB_CTX *ctx, + const uint8_t key[SM4_BLOCK_SIZE], const uint8_t iv[SM4_BLOCK_SIZE]) +{ + sm4_set_encrypt_key(&ctx->sm4_key, key); + memcpy(ctx->iv, iv, SM4_BLOCK_SIZE); + memset(ctx->block, 0, SM4_BLOCK_SIZE); + ctx->block_nbytes = 0; + return 1; +} + +int sm4_ofb_encrypt_update(SM4_OFB_CTX *ctx, + const uint8_t *in, size_t inlen, uint8_t *out, size_t *outlen) +{ + size_t left; + size_t nblocks; + size_t len; + + if (ctx->block_nbytes >= SM4_BLOCK_SIZE) { + error_print(); + return -1; + } + *outlen = 0; + if (ctx->block_nbytes) { + left = SM4_BLOCK_SIZE - ctx->block_nbytes; + if (inlen < left) { + memcpy(ctx->block + ctx->block_nbytes, in, inlen); + ctx->block_nbytes += inlen; + return 1; + } + memcpy(ctx->block + ctx->block_nbytes, in, left); + sm4_ofb_encrypt(&ctx->sm4_key, ctx->iv, ctx->block, SM4_BLOCK_SIZE, out); + in += left; + inlen -= left; + out += SM4_BLOCK_SIZE; + *outlen += SM4_BLOCK_SIZE; + } + if (inlen >= SM4_BLOCK_SIZE) { + nblocks = inlen / SM4_BLOCK_SIZE; + len = nblocks * SM4_BLOCK_SIZE; + sm4_ofb_encrypt(&ctx->sm4_key, ctx->iv, in, len, out); + in += len; + inlen -= len; + out += len; + *outlen += len; + } + if (inlen) { + memcpy(ctx->block, in, inlen); + } + ctx->block_nbytes = inlen; + return 1; +} + +int sm4_ofb_encrypt_finish(SM4_OFB_CTX *ctx, uint8_t *out, size_t *outlen) +{ + if (ctx->block_nbytes >= SM4_BLOCK_SIZE) { + error_print(); + return -1; + } + sm4_ofb_encrypt(&ctx->sm4_key, ctx->iv, ctx->block, ctx->block_nbytes, out); + *outlen = ctx->block_nbytes; + return 1; +} + diff --git a/src/tls.c b/src/tls.c index 9a38dee33..7ab4e1e1b 100644 --- a/src/tls.c +++ b/src/tls.c @@ -2326,8 +2326,9 @@ int tls_set_socket(TLS_CONNECT *conn, tls_socket_t sock) return -1; } if (flags & O_NONBLOCK) { - error_puts("socket should be in blocking mode"); - return -1; + error_puts("socket in non-blocking mode"); + //nginx will pass a socket in non-blocking mode + //return -1; } conn->sock = sock; return 1;