SM4 is a block cipher standard published by the government of the People's Republic of China, and it was issued by the State Cryptography Administration on March 21, 2012. The standard is GM/T 0002-2012 "SM4 block cipher algorithm".
SM4 algorithm is a symmetric cipher algorithm in ShangMi cryptosystems. The block length and key length are both 128 bits. Both the encryption algorithm and the key derivation algorithm use 32 rounds of non-linear iterative structure, and the S box is a fixed 8 bits. The RFC 8998 specification defines the usage of ShangMi algorithm suite in TLS 1.3, etc. According to the State Cryptography Administration of China, its security and efficiency are equivalent to AES-128.
Reference specification: 1. http://www.gmbz.org.cn/upload/2018-04-04/1522788048733065051.pdf 2. http://gmbz.org.cn/main/viewfile/20180108015408199368.html 3. https://tools.ietf.org/id/draft-ribose-cfrg-sm4-10.html 4. https://datatracker.ietf.org/doc/html/rfc8998
Tianjia Zhang (7): doc: Add Copyright of SM3 hash algorithm Introduce SM4 symmetric cipher algorithm testsuite: add test for SM4 symmetric algorithm nettle-benchmark: bench SM4 symmetric algorithm doc: documentation for SM4 cipher algorithm gcm: Add SM4 as the GCM underlying cipher doc: documentation for GCM using SM4 cipher
Makefile.in | 2 + examples/nettle-benchmark.c | 2 + gcm-sm4-meta.c | 60 ++++++++++ gcm-sm4.c | 81 +++++++++++++ gcm.h | 25 +++- nettle-meta-aeads.c | 1 + nettle-meta-ciphers.c | 1 + nettle-meta.h | 3 + nettle.texinfo | 81 +++++++++++++ sm4-meta.c | 49 ++++++++ sm4.c | 225 +++++++++++++++++++++++++++++++++++ sm4.h | 71 +++++++++++ testsuite/.gitignore | 1 + testsuite/Makefile.in | 2 +- testsuite/gcm-test.c | 18 +++ testsuite/meta-aead-test.c | 1 + testsuite/meta-cipher-test.c | 3 +- testsuite/sm4-test.c | 19 +++ 18 files changed, 642 insertions(+), 3 deletions(-) create mode 100644 gcm-sm4-meta.c create mode 100644 gcm-sm4.c create mode 100644 sm4-meta.c create mode 100644 sm4.c create mode 100644 sm4.h create mode 100644 testsuite/sm4-test.c
Signed-off-by: Tianjia Zhang tianjia.zhang@linux.alibaba.com --- nettle.texinfo | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/nettle.texinfo b/nettle.texinfo index 76934637..45b06720 100644 --- a/nettle.texinfo +++ b/nettle.texinfo @@ -293,6 +293,10 @@ Written by @value{AUTHOR}, using Peter Gutmann's SHA1 code as a model. @item SHA3 Written by @value{AUTHOR}.
+@item SM3 +The C implementation of the SM3 message digest is written by Tianjia +Zhang, and the code is based on the implementation by Jia Zhang. + @item TWOFISH The implementation of the TWOFISH cipher is written by Ruud de Rooij.
Introduce the SM4 cipher algorithms (OSCCA GB/T 32907-2016).
SM4 (GBT.32907-2016) is a cryptographic standard issued by the Organization of State Commercial Administration of China (OSCCA) as an authorized cryptographic algorithms for the use within China.
SMS4 was originally created for use in protecting wireless networks, and is mandated in the Chinese National Standard for Wireless LAN WAPI (Wired Authentication and Privacy Infrastructure) (GB.15629.11-2003).
Signed-off-by: Tianjia Zhang tianjia.zhang@linux.alibaba.com --- Makefile.in | 1 + nettle-meta-ciphers.c | 1 + nettle-meta.h | 2 + sm4-meta.c | 49 ++++++++ sm4.c | 225 +++++++++++++++++++++++++++++++++++ sm4.h | 71 +++++++++++ testsuite/meta-cipher-test.c | 3 +- 7 files changed, 351 insertions(+), 1 deletion(-) create mode 100644 sm4-meta.c create mode 100644 sm4.c create mode 100644 sm4.h
diff --git a/Makefile.in b/Makefile.in index 0590c370..62511df4 100644 --- a/Makefile.in +++ b/Makefile.in @@ -150,6 +150,7 @@ nettle_SOURCES = aes-decrypt-internal.c aes-decrypt.c aes-decrypt-table.c \ serpent-meta.c \ streebog.c streebog-meta.c \ twofish.c twofish-meta.c \ + sm4.c sm4-meta.c \ umac-nh.c umac-nh-n.c umac-l2.c umac-l3.c \ umac-poly64.c umac-poly128.c umac-set-key.c \ umac32.c umac64.c umac96.c umac128.c \ diff --git a/nettle-meta-ciphers.c b/nettle-meta-ciphers.c index 49cb47a7..f8d691cf 100644 --- a/nettle-meta-ciphers.c +++ b/nettle-meta-ciphers.c @@ -54,6 +54,7 @@ const struct nettle_cipher * const _nettle_ciphers[] = { &nettle_arctwo64, &nettle_arctwo128, &nettle_arctwo_gutmann128, + &nettle_sm4, NULL };
diff --git a/nettle-meta.h b/nettle-meta.h index d684947e..3d0440e8 100644 --- a/nettle-meta.h +++ b/nettle-meta.h @@ -89,6 +89,8 @@ extern const struct nettle_cipher nettle_arctwo64; extern const struct nettle_cipher nettle_arctwo128; extern const struct nettle_cipher nettle_arctwo_gutmann128;
+extern const struct nettle_cipher nettle_sm4; + struct nettle_hash { const char *name; diff --git a/sm4-meta.c b/sm4-meta.c new file mode 100644 index 00000000..ef462299 --- /dev/null +++ b/sm4-meta.c @@ -0,0 +1,49 @@ +/* sm4-meta.c + + Copyright (C) 2021 Tianjia Zhang tianjia.zhang@linux.alibaba.com + + This file is part of GNU Nettle. + + GNU Nettle is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + or both in parallel, as here. + + GNU Nettle 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 + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see http://www.gnu.org/licenses/. +*/ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include "nettle-meta.h" + +#include "sm4.h" + +const struct nettle_cipher nettle_sm4 = { + "sm4", + sizeof(struct sm4_ctx), + SM4_BLOCK_SIZE, + SM4_KEY_SIZE, + (nettle_set_key_func *) sm4_set_key, + (nettle_set_key_func *) sm4_set_key, + (nettle_cipher_func *) sm4_encrypt, + (nettle_cipher_func *) sm4_decrypt +}; diff --git a/sm4.c b/sm4.c new file mode 100644 index 00000000..662e83df --- /dev/null +++ b/sm4.c @@ -0,0 +1,225 @@ +/* sm4.c + + Copyright (C) 2021 Tianjia Zhang tianjia.zhang@linux.alibaba.com + + This file is part of GNU Nettle. + + GNU Nettle is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + or both in parallel, as here. + + GNU Nettle 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 + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see http://www.gnu.org/licenses/. +*/ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include <assert.h> +#include <string.h> + +#include "sm4.h" + +#include "macros.h" + + +static const uint32_t fk[4] = +{ + 0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc +}; + +static const uint32_t ck[32] = +{ + 0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269, + 0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9, + 0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249, + 0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9, + 0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229, + 0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299, + 0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209, + 0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279 +}; + +static const uint8_t sbox[256] = +{ + 0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, + 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05, + 0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, + 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99, + 0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, + 0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62, + 0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, + 0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6, + 0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, + 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8, + 0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, + 0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35, + 0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, + 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87, + 0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52, + 0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e, + 0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5, + 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1, + 0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55, + 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3, + 0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60, + 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f, + 0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, + 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51, + 0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, + 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8, + 0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, + 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0, + 0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, + 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84, + 0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, + 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48 +}; + +static uint32_t +sm4_t_non_lin_sub(uint32_t x) +{ + uint32_t out; + + out = (uint32_t)sbox[x & 0xff]; + out |= (uint32_t)sbox[(x >> 8) & 0xff] << 8; + out |= (uint32_t)sbox[(x >> 16) & 0xff] << 16; + out |= (uint32_t)sbox[(x >> 24) & 0xff] << 24; + + return out; +} + +static uint32_t +sm4_key_lin_sub(uint32_t x) +{ + return x ^ ROTL32(13, x) ^ ROTL32(23, x); +} + +static uint32_t +sm4_enc_lin_sub(uint32_t x) +{ + return x ^ ROTL32(2, x) ^ ROTL32(10, x) ^ ROTL32(18, x) ^ ROTL32(24, x); +} + +static uint32_t +sm4_key_sub(uint32_t x) +{ + return sm4_key_lin_sub(sm4_t_non_lin_sub(x)); +} + +static uint32_t +sm4_enc_sub(uint32_t x) +{ + return sm4_enc_lin_sub(sm4_t_non_lin_sub(x)); +} + +static uint32_t +sm4_round(uint32_t x0, uint32_t x1, uint32_t x2, uint32_t x3, uint32_t rk) +{ + return x0 ^ sm4_enc_sub(x1 ^ x2 ^ x3 ^ rk); +} + +void +sm4_set_key(struct sm4_ctx *ctx, const uint8_t *key) +{ + uint32_t rk[4]; + int i; + + rk[0] = READ_UINT32(key + 0) ^ fk[0]; + rk[1] = READ_UINT32(key + 4) ^ fk[1]; + rk[2] = READ_UINT32(key + 8) ^ fk[2]; + rk[3] = READ_UINT32(key + 12) ^ fk[3]; + + for (i = 0; i < 32; i += 4) + { + rk[0] ^= sm4_key_sub(rk[1] ^ rk[2] ^ rk[3] ^ ck[i + 0]); + rk[1] ^= sm4_key_sub(rk[2] ^ rk[3] ^ rk[0] ^ ck[i + 1]); + rk[2] ^= sm4_key_sub(rk[3] ^ rk[0] ^ rk[1] ^ ck[i + 2]); + rk[3] ^= sm4_key_sub(rk[0] ^ rk[1] ^ rk[2] ^ ck[i + 3]); + + ctx->rkey_enc[i + 0] = rk[0]; + ctx->rkey_enc[i + 1] = rk[1]; + ctx->rkey_enc[i + 2] = rk[2]; + ctx->rkey_enc[i + 3] = rk[3]; + ctx->rkey_dec[31 - 0 - i] = rk[0]; + ctx->rkey_dec[31 - 1 - i] = rk[1]; + ctx->rkey_dec[31 - 2 - i] = rk[2]; + ctx->rkey_dec[31 - 3 - i] = rk[3]; + } +} + +static void +sm4_crypt_block(const uint32_t *rk, uint8_t *dst, const uint8_t *src) +{ + uint32_t x[4], i; + + x[0] = READ_UINT32(src + 0 * 4); + x[1] = READ_UINT32(src + 1 * 4); + x[2] = READ_UINT32(src + 2 * 4); + x[3] = READ_UINT32(src + 3 * 4); + + for (i = 0; i < 32; i += 4) + { + x[0] = sm4_round(x[0], x[1], x[2], x[3], rk[i + 0]); + x[1] = sm4_round(x[1], x[2], x[3], x[0], rk[i + 1]); + x[2] = sm4_round(x[2], x[3], x[0], x[1], rk[i + 2]); + x[3] = sm4_round(x[3], x[0], x[1], x[2], rk[i + 3]); + } + + WRITE_UINT32(dst + 0 * 4, x[3 - 0]); + WRITE_UINT32(dst + 1 * 4, x[3 - 1]); + WRITE_UINT32(dst + 2 * 4, x[3 - 2]); + WRITE_UINT32(dst + 3 * 4, x[3 - 3]); +} + +void +sm4_encrypt(const struct sm4_ctx *context, + size_t length, + uint8_t *dst, + const uint8_t *src) +{ + const uint32_t *keys = context->rkey_enc; + + assert( !(length % SM4_BLOCK_SIZE) ); + for ( ; length; length -= SM4_BLOCK_SIZE) + { + sm4_crypt_block(keys, dst, src); + src += SM4_BLOCK_SIZE; + dst += SM4_BLOCK_SIZE; + } +} + +void +sm4_decrypt(const struct sm4_ctx *context, + size_t length, + uint8_t *dst, + const uint8_t *src) +{ + const uint32_t *keys = context->rkey_dec; + + assert( !(length % SM4_BLOCK_SIZE) ); + for ( ; length; length -= SM4_BLOCK_SIZE) + { + sm4_crypt_block(keys, dst, src); + src += SM4_BLOCK_SIZE; + dst += SM4_BLOCK_SIZE; + } +} diff --git a/sm4.h b/sm4.h new file mode 100644 index 00000000..a55d99c7 --- /dev/null +++ b/sm4.h @@ -0,0 +1,71 @@ +/* sm4.h + + Copyright (C) 2021 Tianjia Zhang tianjia.zhang@linux.alibaba.com + + This file is part of GNU Nettle. + + GNU Nettle is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + or both in parallel, as here. + + GNU Nettle 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 + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see http://www.gnu.org/licenses/. +*/ + +#ifndef NETTLE_SM4_H_INCLUDED +#define NETTLE_SM4_H_INCLUDED + +#include "nettle-types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Name mangling */ +#define sm4_set_key nettle_sm4_set_key +#define sm4_encrypt nettle_sm4_encrypt +#define sm4_decrypt nettle_sm4_decrypt + +#define SM4_BLOCK_SIZE 16 +#define SM4_KEY_SIZE 16 + +struct sm4_ctx +{ + uint32_t rkey_enc[32]; + uint32_t rkey_dec[32]; +}; + +void +sm4_set_key(struct sm4_ctx *ctx, const uint8_t *key); + +void +sm4_encrypt(const struct sm4_ctx *ctx, + size_t length, uint8_t *dst, + const uint8_t *src); +void +sm4_decrypt(const struct sm4_ctx *ctx, + size_t length, uint8_t *dst, + const uint8_t *src); + +#ifdef __cplusplus +} +#endif + +#endif /* NETTLE_SM4_H_INCLUDED */ diff --git a/testsuite/meta-cipher-test.c b/testsuite/meta-cipher-test.c index f949fd76..62488b7f 100644 --- a/testsuite/meta-cipher-test.c +++ b/testsuite/meta-cipher-test.c @@ -18,7 +18,8 @@ const char* ciphers[] = { "serpent256", "twofish128", "twofish192", - "twofish256" + "twofish256", + "sm4" };
void
Tianjia Zhang tianjia.zhang@linux.alibaba.com writes:
Signed-off-by: Tianjia Zhang tianjia.zhang@linux.alibaba.com
Makefile.in | 1 + nettle-meta-ciphers.c | 1 + nettle-meta.h | 2 + sm4-meta.c | 49 ++++++++ sm4.c | 225 +++++++++++++++++++++++++++++++++++ sm4.h | 71 +++++++++++ testsuite/meta-cipher-test.c | 3 +- 7 files changed, 351 insertions(+), 1 deletion(-) create mode 100644 sm4-meta.c create mode 100644 sm4.c create mode 100644 sm4.h
Overall looks pretty good. But I wonder if one could avoid having two copies of the subkeys.
+void +sm4_set_key(struct sm4_ctx *ctx, const uint8_t *key) +{
- uint32_t rk[4];
- int i;
- rk[0] = READ_UINT32(key + 0) ^ fk[0];
- rk[1] = READ_UINT32(key + 4) ^ fk[1];
- rk[2] = READ_UINT32(key + 8) ^ fk[2];
- rk[3] = READ_UINT32(key + 12) ^ fk[3];
- for (i = 0; i < 32; i += 4)
- {
rk[0] ^= sm4_key_sub(rk[1] ^ rk[2] ^ rk[3] ^ ck[i + 0]);
rk[1] ^= sm4_key_sub(rk[2] ^ rk[3] ^ rk[0] ^ ck[i + 1]);
rk[2] ^= sm4_key_sub(rk[3] ^ rk[0] ^ rk[1] ^ ck[i + 2]);
rk[3] ^= sm4_key_sub(rk[0] ^ rk[1] ^ rk[2] ^ ck[i + 3]);
ctx->rkey_enc[i + 0] = rk[0];
ctx->rkey_enc[i + 1] = rk[1];
ctx->rkey_enc[i + 2] = rk[2];
ctx->rkey_enc[i + 3] = rk[3];
ctx->rkey_dec[31 - 0 - i] = rk[0];
ctx->rkey_dec[31 - 1 - i] = rk[1];
ctx->rkey_dec[31 - 2 - i] = rk[2];
ctx->rkey_dec[31 - 3 - i] = rk[3];
- }
+}
So subkeys are identical for encrypt and decrypt, just used in opposite order? It seems unnecessary to use two copies.
+static void +sm4_crypt_block(const uint32_t *rk, uint8_t *dst, const uint8_t *src) +{
- uint32_t x[4], i;
The loop counter i should have type plain int (or unsigned; in Nettle I tend to use unsigned for non-negative values).
- x[0] = READ_UINT32(src + 0 * 4);
- x[1] = READ_UINT32(src + 1 * 4);
- x[2] = READ_UINT32(src + 2 * 4);
- x[3] = READ_UINT32(src + 3 * 4);
- for (i = 0; i < 32; i += 4)
- {
x[0] = sm4_round(x[0], x[1], x[2], x[3], rk[i + 0]);
x[1] = sm4_round(x[1], x[2], x[3], x[0], rk[i + 1]);
x[2] = sm4_round(x[2], x[3], x[0], x[1], rk[i + 2]);
x[3] = sm4_round(x[3], x[0], x[1], x[2], rk[i + 3]);
- }
Since the x[] array is indexed only by constants, you could consider using scalar variables
uint32_t x0, x1, x2 x3;
Probably makes no difference with modern compilers, but we'd like the values to be allocated in registers, not as an array on the stack. And similarly for the rk array above.
- WRITE_UINT32(dst + 0 * 4, x[3 - 0]);
- WRITE_UINT32(dst + 1 * 4, x[3 - 1]);
- WRITE_UINT32(dst + 2 * 4, x[3 - 2]);
- WRITE_UINT32(dst + 3 * 4, x[3 - 3]);
+}
If this is the same for encrypt and decrypt, you could move the block loop into this function too, to avoid code duplication.
+void +sm4_encrypt(const struct sm4_ctx *context,
size_t length,
uint8_t *dst,
const uint8_t *src)
+{
- const uint32_t *keys = context->rkey_enc;
[...]
+void +sm4_decrypt(const struct sm4_ctx *context,
size_t length,
uint8_t *dst,
const uint8_t *src)
+{
- const uint32_t *keys = context->rkey_dec;
I see two alternatives to use only one copy of the subkeys:
1. Keep using a single sm4_set_key function, but store only the first copy (currently rkey_enc). Change the sm4_decrypt function to access subkeys in the opposite order. To keep a shared sm4_crypt_block function, that function would need another +1/-1 argument used to determine subkey access order.
2. Implement two separate functions sm4_set_encrypt_key and sm4_set_decrypt_key, where the latter stores the subkeys in opposite order. sm4_set_decrypt_key could be implemented as sm4_set_encrypt_key + sm4_invert_key, where sm4_invert_key is a function that just reverses the order (a bit similar to _nettle_aes_invert, but simpler). Then the same sm4_crypt function can be used for both encrypt and decrypt.
Not sure what's best, but I'd lean towards (2), since simplicity of the more performance critical encrypt/decrypt operation seems more important than a simplicity at key setup.
Regards, /Niels
Hi
On 2/12/22 11:58 PM, Niels Möller wrote:
Tianjia Zhang tianjia.zhang@linux.alibaba.com writes:
Signed-off-by: Tianjia Zhang tianjia.zhang@linux.alibaba.com
Makefile.in | 1 + nettle-meta-ciphers.c | 1 + nettle-meta.h | 2 + sm4-meta.c | 49 ++++++++ sm4.c | 225 +++++++++++++++++++++++++++++++++++ sm4.h | 71 +++++++++++ testsuite/meta-cipher-test.c | 3 +- 7 files changed, 351 insertions(+), 1 deletion(-) create mode 100644 sm4-meta.c create mode 100644 sm4.c create mode 100644 sm4.h
Overall looks pretty good. But I wonder if one could avoid having two copies of the subkeys.
+void +sm4_set_key(struct sm4_ctx *ctx, const uint8_t *key) +{
- uint32_t rk[4];
- int i;
- rk[0] = READ_UINT32(key + 0) ^ fk[0];
- rk[1] = READ_UINT32(key + 4) ^ fk[1];
- rk[2] = READ_UINT32(key + 8) ^ fk[2];
- rk[3] = READ_UINT32(key + 12) ^ fk[3];
- for (i = 0; i < 32; i += 4)
- {
rk[0] ^= sm4_key_sub(rk[1] ^ rk[2] ^ rk[3] ^ ck[i + 0]);
rk[1] ^= sm4_key_sub(rk[2] ^ rk[3] ^ rk[0] ^ ck[i + 1]);
rk[2] ^= sm4_key_sub(rk[3] ^ rk[0] ^ rk[1] ^ ck[i + 2]);
rk[3] ^= sm4_key_sub(rk[0] ^ rk[1] ^ rk[2] ^ ck[i + 3]);
ctx->rkey_enc[i + 0] = rk[0];
ctx->rkey_enc[i + 1] = rk[1];
ctx->rkey_enc[i + 2] = rk[2];
ctx->rkey_enc[i + 3] = rk[3];
ctx->rkey_dec[31 - 0 - i] = rk[0];
ctx->rkey_dec[31 - 1 - i] = rk[1];
ctx->rkey_dec[31 - 2 - i] = rk[2];
ctx->rkey_dec[31 - 3 - i] = rk[3];
- }
+}
So subkeys are identical for encrypt and decrypt, just used in opposite order? It seems unnecessary to use two copies.
+static void +sm4_crypt_block(const uint32_t *rk, uint8_t *dst, const uint8_t *src) +{
- uint32_t x[4], i;
The loop counter i should have type plain int (or unsigned; in Nettle I tend to use unsigned for non-negative values).
- x[0] = READ_UINT32(src + 0 * 4);
- x[1] = READ_UINT32(src + 1 * 4);
- x[2] = READ_UINT32(src + 2 * 4);
- x[3] = READ_UINT32(src + 3 * 4);
- for (i = 0; i < 32; i += 4)
- {
x[0] = sm4_round(x[0], x[1], x[2], x[3], rk[i + 0]);
x[1] = sm4_round(x[1], x[2], x[3], x[0], rk[i + 1]);
x[2] = sm4_round(x[2], x[3], x[0], x[1], rk[i + 2]);
x[3] = sm4_round(x[3], x[0], x[1], x[2], rk[i + 3]);
- }
Since the x[] array is indexed only by constants, you could consider using scalar variables
uint32_t x0, x1, x2 x3;
Probably makes no difference with modern compilers, but we'd like the values to be allocated in registers, not as an array on the stack. And similarly for the rk array above.
- WRITE_UINT32(dst + 0 * 4, x[3 - 0]);
- WRITE_UINT32(dst + 1 * 4, x[3 - 1]);
- WRITE_UINT32(dst + 2 * 4, x[3 - 2]);
- WRITE_UINT32(dst + 3 * 4, x[3 - 3]);
+}
If this is the same for encrypt and decrypt, you could move the block loop into this function too, to avoid code duplication.
+void +sm4_encrypt(const struct sm4_ctx *context,
size_t length,
uint8_t *dst,
const uint8_t *src)
+{
- const uint32_t *keys = context->rkey_enc;
[...]
+void +sm4_decrypt(const struct sm4_ctx *context,
size_t length,
uint8_t *dst,
const uint8_t *src)
+{
- const uint32_t *keys = context->rkey_dec;
I see two alternatives to use only one copy of the subkeys:
Keep using a single sm4_set_key function, but store only the first copy (currently rkey_enc). Change the sm4_decrypt function to access subkeys in the opposite order. To keep a shared sm4_crypt_block function, that function would need another +1/-1 argument used to determine subkey access order.
Implement two separate functions sm4_set_encrypt_key and sm4_set_decrypt_key, where the latter stores the subkeys in opposite order. sm4_set_decrypt_key could be implemented as sm4_set_encrypt_key + sm4_invert_key, where sm4_invert_key is a function that just reverses the order (a bit similar to _nettle_aes_invert, but simpler). Then the same sm4_crypt function can be used for both encrypt and decrypt.
Not sure what's best, but I'd lean towards (2), since simplicity of the more performance critical encrypt/decrypt operation seems more important than a simplicity at key setup.
Regards, /Niels
sorry for the late reply.
Thanks for your suggestion, it helped me a lot, I agree with your second suggestion, implement two separate set_key functions, and use one function to unify encryption and decryption, and also improve the code with other suggestions you gave, these will be send in a later v2 patch.
Best regards, Tianjia
Add a testuite for SM4 symmetric algorithm. Test vectors are based on: https://tools.ietf.org/id/draft-ribose-cfrg-sm4-10.html
Signed-off-by: Tianjia Zhang tianjia.zhang@linux.alibaba.com --- testsuite/.gitignore | 1 + testsuite/Makefile.in | 2 +- testsuite/sm4-test.c | 19 +++++++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 testsuite/sm4-test.c
diff --git a/testsuite/.gitignore b/testsuite/.gitignore index ca41472e..07127d2b 100644 --- a/testsuite/.gitignore +++ b/testsuite/.gitignore @@ -98,6 +98,7 @@ /sha512-256-test /sha512-test /sm3-test +/sm4-test /streebog-test /twofish-test /umac-test diff --git a/testsuite/Makefile.in b/testsuite/Makefile.in index 6734d3e6..c2662826 100644 --- a/testsuite/Makefile.in +++ b/testsuite/Makefile.in @@ -24,7 +24,7 @@ TS_NETTLE_SOURCES = aes-test.c aes-keywrap-test.c arcfour-test.c arctwo-test.c \ sha384-test.c sha512-test.c sha512-224-test.c sha512-256-test.c \ sha3-permute-test.c sha3-224-test.c sha3-256-test.c \ sha3-384-test.c sha3-512-test.c \ - shake256-test.c streebog-test.c sm3-test.c \ + shake256-test.c streebog-test.c sm3-test.c sm4-test.c \ serpent-test.c twofish-test.c version-test.c \ knuth-lfib-test.c \ cbc-test.c cfb-test.c ctr-test.c gcm-test.c eax-test.c ccm-test.c \ diff --git a/testsuite/sm4-test.c b/testsuite/sm4-test.c new file mode 100644 index 00000000..97d9d58a --- /dev/null +++ b/testsuite/sm4-test.c @@ -0,0 +1,19 @@ +#include "testutils.h" +#include "sm4.h" + +void +test_main(void) +{ + /* test vectors from: + * https://tools.ietf.org/id/draft-ribose-cfrg-sm4-10.html + */ + test_cipher(&nettle_sm4, + SHEX("0123456789ABCDEF FEDCBA9876543210"), + SHEX("0123456789ABCDEF FEDCBA9876543210"), + SHEX("681EDF34D206965E 86B3E94F536E4246")); + + test_cipher(&nettle_sm4, + SHEX("FEDCBA9876543210 0123456789ABCDEF"), + SHEX("0001020304050607 08090A0B0C0D0E0F"), + SHEX("F766678F13F01ADE AC1B3EA955ADB594")); +}
Signed-off-by: Tianjia Zhang tianjia.zhang@linux.alibaba.com --- examples/nettle-benchmark.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/examples/nettle-benchmark.c b/examples/nettle-benchmark.c index ba5dd284..802a7234 100644 --- a/examples/nettle-benchmark.c +++ b/examples/nettle-benchmark.c @@ -63,6 +63,7 @@ #include "sha1.h" #include "sha2.h" #include "sha3.h" +#include "sm4.h" #include "twofish.h" #include "umac.h" #include "cmac.h" @@ -926,6 +927,7 @@ main(int argc, char **argv) &nettle_des3, &nettle_serpent256, &nettle_twofish128, &nettle_twofish192, &nettle_twofish256, + &nettle_sm4, NULL };
Signed-off-by: Tianjia Zhang tianjia.zhang@linux.alibaba.com --- nettle.texinfo | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+)
diff --git a/nettle.texinfo b/nettle.texinfo index 45b06720..a291dc7e 100644 --- a/nettle.texinfo +++ b/nettle.texinfo @@ -297,6 +297,9 @@ Written by @value{AUTHOR}. The C implementation of the SM3 message digest is written by Tianjia Zhang, and the code is based on the implementation by Jia Zhang.
+@item SM4 +The implementation of the SM4 cipher is written by Tianjia Zhang. + @item TWOFISH The implementation of the TWOFISH cipher is written by Ruud de Rooij.
@@ -2277,6 +2280,42 @@ in any other way. Analogous to @code{twofish_encrypt} @end deftypefun
+@node SM4 +@subsection SM4 +@cindex SM4 + +SM4 is a block cipher standard adopted by the government of the People's +Republic of China, and it was issued by the State Cryptography Administration +on March 21, 2012. The standard is GM/T 0002-2012 "SM4 block cipher algorithm". +Nettle defines it in @file{<nettle/sm4.h>}. + +@deftp {Context struct} {struct sm4_ctx} +@end deftp + +@defvr Constant SM4_BLOCK_SIZE +The SM4 block-size, 16. +@end defvr + +@defvr Constant SM4_KEY_SIZE +Default SM4 key size, 16. +@end defvr + +@deftypefun void sm4_set_key (struct sm4_ctx *@var{ctx}, size_t @var{length}, const uint8_t *@var{key}) +Initialize the cipher. The same function is used for both encryption and +decryption. +@end deftypefun + +@deftypefun void sm4_encrypt (struct sm4_ctx *@var{ctx}, size_t @var{length}, uint8_t *@var{dst}, const uint8_t *@var{src}) +Encryption function. @var{length} must be an integral multiple of the +block size. If it is more than one block, the data is processed in ECB +mode. @code{src} and @code{dst} may be equal, but they must not overlap +in any other way. +@end deftypefun + +@deftypefun void sm4_decrypt (struct sm4_ctx *@var{ctx}, size_t @var{length}, uint8_t *@var{dst}, const uint8_t *@var{src}) +Analogous to @code{sm4_encrypt} +@end deftypefun + @node nettle_cipher abstraction @subsection The @code{struct nettle_cipher} abstraction @cindex nettle_cipher
Signed-off-by: Tianjia Zhang tianjia.zhang@linux.alibaba.com --- Makefile.in | 1 + gcm-sm4-meta.c | 60 ++++++++++++++++++++++++++++ gcm-sm4.c | 81 ++++++++++++++++++++++++++++++++++++++ gcm.h | 25 +++++++++++- nettle-meta-aeads.c | 1 + nettle-meta.h | 1 + testsuite/gcm-test.c | 18 +++++++++ testsuite/meta-aead-test.c | 1 + 8 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 gcm-sm4-meta.c create mode 100644 gcm-sm4.c
diff --git a/Makefile.in b/Makefile.in index 62511df4..a4c45201 100644 --- a/Makefile.in +++ b/Makefile.in @@ -112,6 +112,7 @@ nettle_SOURCES = aes-decrypt-internal.c aes-decrypt.c aes-decrypt-table.c \ gcm-aes256.c gcm-aes256-meta.c \ gcm-camellia128.c gcm-camellia128-meta.c \ gcm-camellia256.c gcm-camellia256-meta.c \ + gcm-sm4.c gcm-sm4-meta.c \ cmac.c cmac64.c cmac-aes128.c cmac-aes256.c cmac-des3.c \ cmac-aes128-meta.c cmac-aes256-meta.c cmac-des3-meta.c \ gost28147.c gosthash94.c gosthash94-meta.c \ diff --git a/gcm-sm4-meta.c b/gcm-sm4-meta.c new file mode 100644 index 00000000..dc7bd1ae --- /dev/null +++ b/gcm-sm4-meta.c @@ -0,0 +1,60 @@ +/* gcm-sm4-meta.c + + Copyright (C) 2021 Tianjia Zhang tianjia.zhang@linux.alibaba.com + + This file is part of GNU Nettle. + + GNU Nettle is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + or both in parallel, as here. + + GNU Nettle 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 + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see http://www.gnu.org/licenses/. +*/ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include <assert.h> + +#include "nettle-meta.h" + +#include "gcm.h" + +static nettle_set_key_func gcm_sm4_set_nonce_wrapper; +static void +gcm_sm4_set_nonce_wrapper (void *ctx, const uint8_t *nonce) +{ + gcm_sm4_set_iv (ctx, GCM_IV_SIZE, nonce); +} + +const struct nettle_aead nettle_gcm_sm4 = + { "gcm_sm4", sizeof(struct gcm_sm4_ctx), + GCM_BLOCK_SIZE, SM4_KEY_SIZE, + GCM_IV_SIZE, GCM_DIGEST_SIZE, + (nettle_set_key_func *) gcm_sm4_set_key, + (nettle_set_key_func *) gcm_sm4_set_key, + gcm_sm4_set_nonce_wrapper, + (nettle_hash_update_func *) gcm_sm4_update, + (nettle_crypt_func *) gcm_sm4_encrypt, + (nettle_crypt_func *) gcm_sm4_decrypt, + (nettle_hash_digest_func *) gcm_sm4_digest, + }; diff --git a/gcm-sm4.c b/gcm-sm4.c new file mode 100644 index 00000000..70c1c6cf --- /dev/null +++ b/gcm-sm4.c @@ -0,0 +1,81 @@ +/* gcm-sm4.c + + Galois counter mode using SM4 as the underlying cipher. + + Copyright (C) 2021 Tianjia Zhang tianjia.zhang@linux.alibaba.com + + This file is part of GNU Nettle. + + GNU Nettle is free software: you can redistribute it and/or + modify it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + or both in parallel, as here. + + GNU Nettle 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 + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see http://www.gnu.org/licenses/. +*/ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include <assert.h> + +#include "gcm.h" + +void +gcm_sm4_set_key(struct gcm_sm4_ctx *ctx, const uint8_t *key) +{ + GCM_SET_KEY(ctx, sm4_set_key, sm4_encrypt, key); +} + +void +gcm_sm4_set_iv (struct gcm_sm4_ctx *ctx, + size_t length, const uint8_t *iv) +{ + GCM_SET_IV (ctx, length, iv); +} + +void +gcm_sm4_update (struct gcm_sm4_ctx *ctx, + size_t length, const uint8_t *data) +{ + GCM_UPDATE (ctx, length, data); +} + +void +gcm_sm4_encrypt(struct gcm_sm4_ctx *ctx, + size_t length, uint8_t *dst, const uint8_t *src) +{ + GCM_ENCRYPT(ctx, sm4_encrypt, length, dst, src); +} + +void +gcm_sm4_decrypt(struct gcm_sm4_ctx *ctx, + size_t length, uint8_t *dst, const uint8_t *src) +{ + GCM_DECRYPT(ctx, sm4_encrypt, length, dst, src); +} + +void +gcm_sm4_digest(struct gcm_sm4_ctx *ctx, + size_t length, uint8_t *digest) +{ + GCM_DIGEST(ctx, sm4_encrypt, length, digest); +} diff --git a/gcm.h b/gcm.h index 96578530..2a35de3c 100644 --- a/gcm.h +++ b/gcm.h @@ -40,6 +40,7 @@
#include "aes.h" #include "camellia.h" +#include "sm4.h"
#ifdef __cplusplus extern "C" { @@ -95,6 +96,13 @@ extern "C" { #define gcm_camellia256_decrypt nettle_gcm_camellia256_decrypt #define gcm_camellia256_digest nettle_gcm_camellia256_digest
+#define gcm_sm4_set_key nettle_gcm_sm4_set_key +#define gcm_sm4_set_iv nettle_gcm_sm4_set_iv +#define gcm_sm4_update nettle_gcm_sm4_update +#define gcm_sm4_encrypt nettle_gcm_sm4_encrypt +#define gcm_sm4_decrypt nettle_gcm_sm4_decrypt +#define gcm_sm4_digest nettle_gcm_sm4_digest + #define GCM_BLOCK_SIZE 16 #define GCM_IV_SIZE (GCM_BLOCK_SIZE - 4) #define GCM_DIGEST_SIZE 16 @@ -322,7 +330,22 @@ void gcm_camellia256_decrypt(struct gcm_camellia256_ctx *ctx, void gcm_camellia256_digest(struct gcm_camellia256_ctx *ctx, size_t length, uint8_t *digest);
- + +struct gcm_sm4_ctx GCM_CTX(struct sm4_ctx); + +void gcm_sm4_set_key(struct gcm_sm4_ctx *ctx, const uint8_t *key); +void gcm_sm4_set_iv (struct gcm_sm4_ctx *ctx, + size_t length, const uint8_t *iv); +void gcm_sm4_update (struct gcm_sm4_ctx *ctx, + size_t length, const uint8_t *data); +void gcm_sm4_encrypt(struct gcm_sm4_ctx *ctx, + size_t length, uint8_t *dst, const uint8_t *src); +void gcm_sm4_decrypt(struct gcm_sm4_ctx *ctx, + size_t length, uint8_t *dst, const uint8_t *src); +void gcm_sm4_digest(struct gcm_sm4_ctx *ctx, + size_t length, uint8_t *digest); + + #ifdef __cplusplus } #endif diff --git a/nettle-meta-aeads.c b/nettle-meta-aeads.c index c99cc465..78f38a3c 100644 --- a/nettle-meta-aeads.c +++ b/nettle-meta-aeads.c @@ -43,6 +43,7 @@ const struct nettle_aead * const _nettle_aeads[] = { &nettle_gcm_aes256, &nettle_gcm_camellia128, &nettle_gcm_camellia256, + &nettle_gcm_sm4, &nettle_eax_aes128, &nettle_chacha_poly1305, NULL diff --git a/nettle-meta.h b/nettle-meta.h index 3d0440e8..19dc96c5 100644 --- a/nettle-meta.h +++ b/nettle-meta.h @@ -200,6 +200,7 @@ extern const struct nettle_aead nettle_gcm_aes192; extern const struct nettle_aead nettle_gcm_aes256; extern const struct nettle_aead nettle_gcm_camellia128; extern const struct nettle_aead nettle_gcm_camellia256; +extern const struct nettle_aead nettle_gcm_sm4; extern const struct nettle_aead nettle_eax_aes128; extern const struct nettle_aead nettle_chacha_poly1305;
diff --git a/testsuite/gcm-test.c b/testsuite/gcm-test.c index df1fc94a..d9af0e0a 100644 --- a/testsuite/gcm-test.c +++ b/testsuite/gcm-test.c @@ -540,6 +540,24 @@ test_main(void) "16aedbf5a0de6a57 a637b39b"), /* iv */ SHEX("5791883f822013f8bd136fc36fb9946b")); /* tag */
+ /* + * GCM-SM4 Test Vectors from + * https://datatracker.ietf.org/doc/html/rfc8998 + */ + test_aead(&nettle_gcm_sm4, NULL, + SHEX("0123456789ABCDEFFEDCBA9876543210"), + SHEX("FEEDFACEDEADBEEFFEEDFACEDEADBEEFABADDAD2"), + SHEX("AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBB" + "CCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDD" + "EEEEEEEEEEEEEEEEFFFFFFFFFFFFFFFF" + "EEEEEEEEEEEEEEEEAAAAAAAAAAAAAAAA"), + SHEX("17F399F08C67D5EE19D0DC9969C4BB7D" + "5FD46FD3756489069157B282BB200735" + "D82710CA5C22F0CCFA7CBF93D496AC15" + "A56834CBCF98C397B4024A2691233B8D"), + SHEX("00001234567800000000ABCD"), + SHEX("83DE3541E4C2B58177E065A9BF7B62EC")); + /* Test gcm_hash, with varying message size, keys and iv all zero. Not compared to any other implementation. */ test_gcm_hash (SDATA("a"), diff --git a/testsuite/meta-aead-test.c b/testsuite/meta-aead-test.c index 1fcede40..ceeca227 100644 --- a/testsuite/meta-aead-test.c +++ b/testsuite/meta-aead-test.c @@ -8,6 +8,7 @@ const char* aeads[] = { "gcm_aes256", "gcm_camellia128", "gcm_camellia256", + "gcm_sm4", "eax_aes128", "chacha_poly1305", };
Signed-off-by: Tianjia Zhang tianjia.zhang@linux.alibaba.com --- nettle.texinfo | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+)
diff --git a/nettle.texinfo b/nettle.texinfo index a291dc7e..823d3f41 100644 --- a/nettle.texinfo +++ b/nettle.texinfo @@ -3360,6 +3360,44 @@ that @var{length} is @code{GCM_DIGEST_SIZE}, but if you provide a smaller value, only the first @var{length} octets of the digest are written. @end deftypefun
+@subsubsection @acronym{GCM}-SM4 interface + +The following functions implement the case of @acronym{GCM} using +SM4 as the underlying cipher. + +@deftp {Context struct} {struct gcm_sm4_ctx} +Context structs, defined using @code{GCM_CTX}. +@end deftp + +@deftypefun void gcm_sm4_set_key (struct gcm_sm4_ctx *@var{ctx}, const uint8_t *@var{key}) +Initializes @var{ctx} using the given key. +@end deftypefun + +@deftypefun void gcm_sm4_set_iv (struct gcm_sm4_ctx *@var{ctx}, size_t @var{length}, const uint8_t *@var{iv}) +Initializes the per-message state, using the given @acronym{IV}. +@end deftypefun + +@deftypefun void gcm_sm4_update (struct gcm_sm4_ctx *@var{ctx}, size_t @var{length}, const uint8_t *@var{data}) +Provides associated data to be authenticated. If used, must be called +before @code{gcm_sm4_encrypt} or @code{gcm_sm4_decrypt}. All but the +last call for each message @emph{must} use a length that is a multiple +of the block size. +@end deftypefun + +@deftypefun void gcm_sm4_encrypt (struct gcm_sm4_ctx *@var{ctx}, size_t @var{length}, uint8_t *@var{dst}, const uint8_t *@var{src}) +@deftypefunx void gcm_sm4_decrypt (struct gcm_sm4_ctx *@var{ctx}, size_t @var{length}, uint8_t *@var{dst}, const uint8_t *@var{src}) +Encrypts or decrypts the data of a message. All but the last call for +each message @emph{must} use a length that is a multiple of the block +size. +@end deftypefun + +@deftypefun void gcm_sm4_digest (struct gcm_sm4_ctx *@var{ctx}, size_t @var{length}, uint8_t *@var{digest}) +Extracts the message digest (also known ``authentication tag''). This is +the final operation when processing a message. It's strongly recommended +that @var{length} is @code{GCM_DIGEST_SIZE}, but if you provide a smaller +value, only the first @var{length} octets of the digest are written. +@end deftypefun + @node CCM @subsection Counter with CBC-MAC mode
nettle-bugs@lists.lysator.liu.se