Signed-off-by: Dmitry Eremin-Solenikov dbaryshkov@gmail.com --- Makefile.in | 3 +- gost28147-meta.c | 49 ++++++++++++++++++ gost28147.c | 87 +++++++++++++++++++++++++++++++ gost28147.h | 34 +++++++++++++ nettle-meta-ciphers.c | 1 + nettle-meta.h | 2 + nettle.texinfo | 38 ++++++++++++++ testsuite/.gitignore | 1 + testsuite/.test-rules.make | 3 ++ testsuite/Makefile.in | 1 + testsuite/gost28147-test.c | 119 +++++++++++++++++++++++++++++++++++++++++++ testsuite/meta-cipher-test.c | 1 + 12 files changed, 338 insertions(+), 1 deletion(-) create mode 100644 gost28147-meta.c create mode 100644 testsuite/gost28147-test.c
diff --git a/Makefile.in b/Makefile.in index 21f7d742..2ff11c91 100644 --- a/Makefile.in +++ b/Makefile.in @@ -103,7 +103,8 @@ nettle_SOURCES = aes-decrypt-internal.c aes-decrypt.c \ gcm-aes256.c gcm-aes256-meta.c \ gcm-camellia128.c gcm-camellia128-meta.c \ gcm-camellia256.c gcm-camellia256-meta.c \ - gost28147.c gosthash94.c gosthash94-meta.c \ + gost28147.c gost28147-meta.c \ + gosthash94.c gosthash94-meta.c \ hmac.c hmac-md5.c hmac-ripemd160.c hmac-sha1.c \ hmac-sha224.c hmac-sha256.c hmac-sha384.c hmac-sha512.c \ hmac-gosthash94.c \ diff --git a/gost28147-meta.c b/gost28147-meta.c new file mode 100644 index 00000000..69e4d265 --- /dev/null +++ b/gost28147-meta.c @@ -0,0 +1,49 @@ +/* gost28147-meta.c + + Copyright (C) 2016 Dmitry Eremin-Solenikov + + 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 "gost28147.h" + +const struct nettle_cipher nettle_gost28147 = + { "gost28147", sizeof(struct gost28147_ctx), + GOST28147_BLOCK_SIZE, GOST28147_KEY_SIZE, + (nettle_set_key_func *) gost28147_set_key, + (nettle_set_key_func *) gost28147_set_key, + (nettle_cipher_func *) gost28147_encrypt, + (nettle_cipher_func *) gost28147_decrypt + }; diff --git a/gost28147.c b/gost28147.c index d916276a..89e16ed5 100644 --- a/gost28147.c +++ b/gost28147.c @@ -27,6 +27,8 @@ #include "config.h" #endif
+#include <assert.h> + #include "macros.h" #include "gost28147.h"
@@ -2205,3 +2207,88 @@ void gost28147_encrypt_simple (const uint32_t *key, const uint32_t *sbox, GOST_ENCRYPT_ROUND(key[1], key[0], sbox) *out = l, *(out + 1) = r; } + +static void gost28147_decrypt_simple (const uint32_t *key, const uint32_t *sbox, + const uint32_t *in, uint32_t *out) +{ + uint32_t l, r, tmp; + + r = in[0], l = in[1]; + GOST_ENCRYPT_ROUND(key[0], key[1], sbox) + GOST_ENCRYPT_ROUND(key[2], key[3], sbox) + GOST_ENCRYPT_ROUND(key[4], key[5], sbox) + GOST_ENCRYPT_ROUND(key[6], key[7], sbox) + GOST_ENCRYPT_ROUND(key[7], key[6], sbox) + GOST_ENCRYPT_ROUND(key[5], key[4], sbox) + GOST_ENCRYPT_ROUND(key[3], key[2], sbox) + GOST_ENCRYPT_ROUND(key[1], key[0], sbox) + GOST_ENCRYPT_ROUND(key[7], key[6], sbox) + GOST_ENCRYPT_ROUND(key[5], key[4], sbox) + GOST_ENCRYPT_ROUND(key[3], key[2], sbox) + GOST_ENCRYPT_ROUND(key[1], key[0], sbox) + GOST_ENCRYPT_ROUND(key[7], key[6], sbox) + GOST_ENCRYPT_ROUND(key[5], key[4], sbox) + GOST_ENCRYPT_ROUND(key[3], key[2], sbox) + GOST_ENCRYPT_ROUND(key[1], key[0], sbox) + *out = l, *(out + 1) = r; +} + +void +gost28147_set_key(struct gost28147_ctx *ctx, const uint8_t *key) +{ + unsigned i; + + assert(key); + for (i = 0; i < 8; i++, key += 4) + ctx->key[i] = LE_READ_UINT32(key); + ctx->key_count = 0; + gost28147_set_param(ctx, &gost28147_param_TC26_Z); +} + +void +gost28147_set_param(struct gost28147_ctx *ctx, const struct gost28147_param *param) +{ + assert(param); + ctx->sbox = param->sbox; + ctx->key_meshing = param->key_meshing; +} + +void +gost28147_encrypt(const struct gost28147_ctx *ctx, + size_t length, uint8_t *dst, + const uint8_t *src) +{ + uint32_t block[2]; + + assert(!(length % GOST28147_BLOCK_SIZE)); + + while (length) + { + block[0] = LE_READ_UINT32(src); src += 4; + block[1] = LE_READ_UINT32(src); src += 4; + gost28147_encrypt_simple(ctx->key, ctx->sbox, block, block); + LE_WRITE_UINT32(dst, block[0]); dst += 4; + LE_WRITE_UINT32(dst, block[1]); dst += 4; + length -= GOST28147_BLOCK_SIZE; + } +} + +void +gost28147_decrypt(const struct gost28147_ctx *ctx, + size_t length, uint8_t *dst, + const uint8_t *src) +{ + uint32_t block[2]; + + assert(!(length % GOST28147_BLOCK_SIZE)); + + while (length) + { + block[0] = LE_READ_UINT32(src); src += 4; + block[1] = LE_READ_UINT32(src); src += 4; + gost28147_decrypt_simple(ctx->key, ctx->sbox, block, block); + LE_WRITE_UINT32(dst, block[0]); dst += 4; + LE_WRITE_UINT32(dst, block[1]); dst += 4; + length -= GOST28147_BLOCK_SIZE; + } +} diff --git a/gost28147.h b/gost28147.h index f38543f2..21cf0390 100644 --- a/gost28147.h +++ b/gost28147.h @@ -51,8 +51,26 @@ extern "C" { #define gost28147_param_CryptoPro_D nettle_gost28147_param_CryptoPro_D #define gost28147_param_TC26_Z nettle_gost28147_param_TC26_Z
+/* Private */ #define gost28147_encrypt_simple nettle_gost28147_encrypt_simple
+/* Public functions */ +#define gost28147_set_key nettle_gost28147_set_key +#define gost28147_set_param nettle_gost28147_set_param +#define gost28147_encrypt nettle_gost28147_encrypt +#define gost28147_decrypt nettle_gost28147_decrypt + +#define GOST28147_KEY_SIZE 32 +#define GOST28147_BLOCK_SIZE 8 + +struct gost28147_ctx +{ + uint32_t key[GOST28147_KEY_SIZE/4]; + const uint32_t *sbox; + int key_meshing; + int key_count; /* Used for key meshing */ +}; + struct gost28147_param { int key_meshing; @@ -72,6 +90,22 @@ extern const struct gost28147_param gost28147_param_TC26_Z; void gost28147_encrypt_simple (const uint32_t *key, const uint32_t *sbox, const uint32_t *in, uint32_t *out);
+void +gost28147_set_key(struct gost28147_ctx *ctx, const uint8_t *key); + +void +gost28147_set_param(struct gost28147_ctx *ctx, + const struct gost28147_param *param); + +void +gost28147_encrypt(const struct gost28147_ctx *ctx, + size_t length, uint8_t *dst, + const uint8_t *src); +void +gost28147_decrypt(const struct gost28147_ctx *ctx, + size_t length, uint8_t *dst, + const uint8_t *src); + #ifdef __cplusplus } #endif diff --git a/nettle-meta-ciphers.c b/nettle-meta-ciphers.c index 802fa141..97d0270b 100644 --- a/nettle-meta-ciphers.c +++ b/nettle-meta-ciphers.c @@ -54,5 +54,6 @@ const struct nettle_cipher * const nettle_ciphers[] = { &nettle_arctwo64, &nettle_arctwo128, &nettle_arctwo_gutmann128, + &nettle_gost28147, NULL }; diff --git a/nettle-meta.h b/nettle-meta.h index 5a1f85bc..3e775720 100644 --- a/nettle-meta.h +++ b/nettle-meta.h @@ -86,6 +86,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_gost28147; + struct nettle_hash { const char *name; diff --git a/nettle.texinfo b/nettle.texinfo index e012b989..b5f2701f 100644 --- a/nettle.texinfo +++ b/nettle.texinfo @@ -1687,6 +1687,44 @@ in any other way. Analogous to @code{des_encrypt} @end deftypefun
+@subsection GOST 28147-89 (Magma) +GOST 28147-89 (also called Magma) is the Russian standard cipher. It uses a +block size of 64 bits (8 octets), and a key size of 256 bits. Nettle defines +GOST28147 in @file{<nettle/gost28147.h>}. + +@deftp {Context struct} {struct gost28147_ctx} +@end deftp + +@defvr Constant GOST28147_BLOCK_SIZE +The GOST28147 block-size, 8. +@end defvr + +@defvr Constant GOST28147_KEY_SIZE +GOST28147 key size, 32. +@end defvr + +@deftypefun void gost28147_set_key (struct gost28147_ctx *@var{ctx}, const uint8_t *@var{key}) +Initialize the cipher. The same function is used for both encryption and +decryption. +@end deftypefun + +@deftypefun void gost28147_set_sbox (struct gost28147_ctx *@var{ctx}, const uint32_t *@var{sbox}) +Initialize the cipher S-BOX. The same function is used for both encryption and +decryption. +@end deftypefun + +@deftypefun void gost28147_encrypt (struct gost28147_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 gost28147_decrypt (struct gost28147_ctx *@var{ctx}, size_t @var{length}, uint8_t *@var{dst}, const uint8_t *@var{src}) +Analogous to @code{gost28147_encrypt} +@end deftypefun + + @subsection Salsa20 Salsa20 is a fairly recent stream cipher designed by D. J. Bernstein. It is built on the observation that a cryptographic hash function can be diff --git a/testsuite/.gitignore b/testsuite/.gitignore index 8e5521b4..e688a9c8 100644 --- a/testsuite/.gitignore +++ b/testsuite/.gitignore @@ -28,6 +28,7 @@ /ecdsa-sign-test /ecdsa-verify-test /gcm-test +/gost28147-test /gosthash94-test /hmac-test /knuth-lfib-test diff --git a/testsuite/.test-rules.make b/testsuite/.test-rules.make index 1b9c3fae..d665330f 100644 --- a/testsuite/.test-rules.make +++ b/testsuite/.test-rules.make @@ -34,6 +34,9 @@ des3-test$(EXEEXT): des3-test.$(OBJEXT) des-compat-test$(EXEEXT): des-compat-test.$(OBJEXT) $(LINK) des-compat-test.$(OBJEXT) $(TEST_OBJS) -o des-compat-test$(EXEEXT)
+gost28147-test$(EXEEXT): gost28147-test.$(OBJEXT) + $(LINK) gost28147-test.$(OBJEXT) $(TEST_OBJS) -o gost28147-test$(EXEEXT) + md2-test$(EXEEXT): md2-test.$(OBJEXT) $(LINK) md2-test.$(OBJEXT) $(TEST_OBJS) -o md2-test$(EXEEXT)
diff --git a/testsuite/Makefile.in b/testsuite/Makefile.in index 590691ca..d07268bd 100644 --- a/testsuite/Makefile.in +++ b/testsuite/Makefile.in @@ -15,6 +15,7 @@ TS_NETTLE_SOURCES = aes-test.c arcfour-test.c arctwo-test.c \ base16-test.c base64-test.c \ camellia-test.c chacha-test.c \ des-test.c des3-test.c des-compat-test.c \ + gost28147-test.c \ md2-test.c md4-test.c md5-test.c md5-compat-test.c \ memeql-test.c memxor-test.c gosthash94-test.c \ ripemd160-test.c \ diff --git a/testsuite/gost28147-test.c b/testsuite/gost28147-test.c new file mode 100644 index 00000000..3f8046e1 --- /dev/null +++ b/testsuite/gost28147-test.c @@ -0,0 +1,119 @@ +#include "testutils.h" +#include "gost28147.h" + +static void +test_gost28147(const struct gost28147_param *param, + const struct tstring *key, + const struct tstring *cleartext, + const struct tstring *ciphertext) +{ + struct gost28147_ctx ctx; + uint8_t *data = xalloc(cleartext->length); + size_t length; + + ASSERT (cleartext->length == ciphertext->length); + length = cleartext->length; + + gost28147_set_key(&ctx, key->data); + gost28147_set_param(&ctx, param); + gost28147_encrypt(&ctx, length, data, cleartext->data); + + if (!MEMEQ(length, data, ciphertext->data)) + { + fprintf(stderr, "Encrypt failed:\nInput:"); + tstring_print_hex(cleartext); + fprintf(stderr, "\nOutput: "); + print_hex(length, data); + fprintf(stderr, "\nExpected:"); + tstring_print_hex(ciphertext); + fprintf(stderr, "\n"); + FAIL(); + } + + gost28147_set_key(&ctx, key->data); + gost28147_set_param(&ctx, param); + gost28147_decrypt(&ctx, length, data, data); + + if (!MEMEQ(length, data, cleartext->data)) + { + fprintf(stderr, "Decrypt failed:\nInput:"); + tstring_print_hex(ciphertext); + fprintf(stderr, "\nOutput: "); + print_hex(length, data); + fprintf(stderr, "\nExpected:"); + tstring_print_hex(cleartext); + fprintf(stderr, "\n"); + FAIL(); + } + + free(data); +} + +void test_main(void) +{ + /* Examples from GOST R 34.11-94 standard */ + test_gost28147(&gost28147_param_test_3411, + SHEX("546D2033 68656C32 69736520 73736E62 20616779 69677474 73656865 202C3D73"), + SHEX("00000000 00000000"), + SHEX("1B0BBC32 CEBCAB42")); + + test_gost28147(&gost28147_param_test_3411, + SHEX("2033394D 6C320D09 65201A16 6E62001D 67794106 74740E13 6865160D 3D730C11"), + SHEX("00000000 00000000"), + SHEX("FDCF9B5D C8EB0352")); + + test_gost28147(&gost28147_param_test_3411, + SHEX("39B213F5 F209A13F 1AE9BA3A FF1D0C62 41F9E1C7 F1130085 16F20D73 F311B180"), + SHEX("00000000 00000000"), + SHEX("280EFF00 9958348D")); + + test_gost28147(&gost28147_param_test_3411, + SHEX("EC0A8BA1 5EC004A8 BAC50CAC 0C621DEE E1C7B8E7 007AE2EC F2731BFF 4E80E2A0 "), + SHEX("00000000 00000000"), + SHEX("2D562A0D 190486E7 ")); + + test_gost28147(&gost28147_param_test_3411, + SHEX("8182838485868788 898a8b8c8d8e8f80 d1d2d3d4d5d6d7d8 d9dadbdcdddedfd0"), + SHEX("0102030405060708 f1f2f3f4f5f6f7f8"), + SHEX("ced52a7ff7f260d5 bc81a80bb5e65976")); + + test_gost28147(&gost28147_param_CryptoPro_3411, + SHEX("8182838485868788 898a8b8c8d8e8f80 d1d2d3d4d5d6d7d8 d9dadbdcdddedfd0"), + SHEX("0102030405060708 f1f2f3f4f5f6f7f8"), + SHEX("e42175e16922d0a8 48e59157d7106518")); + + test_gost28147(&gost28147_param_Test_89, + SHEX("8182838485868788 898a8b8c8d8e8f80 d1d2d3d4d5d6d7d8 d9dadbdcdddedfd0"), + SHEX("0102030405060708 f1f2f3f4f5f6f7f8"), + SHEX("9856cf8bfcc282f4 3f465801c6539a5c")); + + test_gost28147(&gost28147_param_CryptoPro_A, + SHEX("8182838485868788 898a8b8c8d8e8f80 d1d2d3d4d5d6d7d8 d9dadbdcdddedfd0"), + SHEX("0102030405060708 f1f2f3f4f5f6f7f8"), + SHEX("668184aedc48c917 4164347058845cac")); + + test_gost28147(&gost28147_param_CryptoPro_B, + SHEX("8182838485868788 898a8b8c8d8e8f80 d1d2d3d4d5d6d7d8 d9dadbdcdddedfd0"), + SHEX("0102030405060708 f1f2f3f4f5f6f7f8"), + SHEX("dbee81147b74b0f2 db5ef00eff4bd528")); + + test_gost28147(&gost28147_param_CryptoPro_C, + SHEX("8182838485868788 898a8b8c8d8e8f80 d1d2d3d4d5d6d7d8 d9dadbdcdddedfd0"), + SHEX("0102030405060708 f1f2f3f4f5f6f7f8"), + SHEX("31a3859d0aeeb80e 4afbd6ce7798ffa9")); + + test_gost28147(&gost28147_param_CryptoPro_D, + SHEX("8182838485868788 898a8b8c8d8e8f80 d1d2d3d4d5d6d7d8 d9dadbdcdddedfd0"), + SHEX("0102030405060708 f1f2f3f4f5f6f7f8"), + SHEX("b1323e0b2173cbd1 c5282f2461e97aa8")); + + test_gost28147(&gost28147_param_TC26_Z, + SHEX("8182838485868788 898a8b8c8d8e8f80 d1d2d3d4d5d6d7d8 d9dadbdcdddedfd0"), + SHEX("0102030405060708 f1f2f3f4f5f6f7f8"), + SHEX("ce5a5ed7e0577a5f d0cc85ce31635b8b")); + + test_gost28147(&gost28147_param_TC26_Z, + SHEX("ccddeeff8899aabb4455667700112233f3f2f1f0f7f6f5f4fbfaf9f8fffefdfc"), + SHEX("1032547698badcfe"), + SHEX("3dcad8c2e501e94e")); +} diff --git a/testsuite/meta-cipher-test.c b/testsuite/meta-cipher-test.c index f949fd76..8c435cb5 100644 --- a/testsuite/meta-cipher-test.c +++ b/testsuite/meta-cipher-test.c @@ -13,6 +13,7 @@ const char* ciphers[] = { "camellia192", "camellia256", "cast128", + "gost28147", "serpent128", "serpent192", "serpent256",