Signed-off-by: Dmitry Baryshkov dbaryshkov@gmail.com --- Makefile.in | 2 +- gost28147-params.c | 11 +++++ gost28147.c | 86 ++++++++++++++++++++++++++++++++++++++ gost28147.h | 85 +++++++++++++++++++++++++++++++++++++ testsuite/Makefile.in | 2 +- testsuite/gost28147-test.c | 77 ++++++++++++++++++++++++++++++++++ 6 files changed, 261 insertions(+), 2 deletions(-) create mode 100644 gost28147.h create mode 100644 testsuite/gost28147-test.c
diff --git a/Makefile.in b/Makefile.in index 41e0389f2979..d4bec88615ed 100644 --- a/Makefile.in +++ b/Makefile.in @@ -211,7 +211,7 @@ HEADERS = aes.h arcfour.h arctwo.h asn1.h blowfish.h \ cbc.h ccm.h cfb.h chacha.h chacha-poly1305.h ctr.h \ curve25519.h curve448.h des.h dsa.h dsa-compat.h eax.h \ ecc-curve.h ecc.h ecdsa.h eddsa.h \ - gcm.h gostdsa.h gosthash94.h hmac.h \ + gcm.h gost28147.h gostdsa.h gosthash94.h hmac.h \ knuth-lfib.h hkdf.h \ macros.h \ cmac.h siv-cmac.h \ diff --git a/gost28147-params.c b/gost28147-params.c index 6addec31f170..7489641ba5a1 100644 --- a/gost28147-params.c +++ b/gost28147-params.c @@ -33,6 +33,7 @@ #endif
#include "macros.h" +#include "gost28147.h" #include "gost28147-internal.h"
/* pre-initialized GOST lookup tables based on rotated S-Box */ @@ -569,3 +570,13 @@ const struct gost28147_param _gost28147_param_CryptoPro_3411 = } } }; + +const struct gost28147_param * gost28147_get_param_test_3411 (void) +{ + return &_gost28147_param_test_3411; +} + +const struct gost28147_param * gost28147_get_param_CryptoPro_3411 (void) +{ + return &_gost28147_param_CryptoPro_3411; +} diff --git a/gost28147.c b/gost28147.c index 669fc85d2083..9525b41b148b 100644 --- a/gost28147.c +++ b/gost28147.c @@ -32,7 +32,10 @@ #include "config.h" #endif
+#include <assert.h> + #include "macros.h" +#include "gost28147.h" #include "gost28147-internal.h"
/* @@ -79,3 +82,86 @@ void _gost28147_encrypt_block (const uint32_t *key, const uint32_t sbox[4][256], GOST_ENCRYPT_ROUND(l, r, key[1], key[0], sbox); *out = l, *(out + 1) = r; } + +static +void _gost28147_decrypt_block (const uint32_t *key, const uint32_t sbox[4][256], + const uint32_t *in, uint32_t *out) +{ + uint32_t l, r; + + r = in[0], l = in[1]; + GOST_ENCRYPT_ROUND(l, r, key[0], key[1], sbox); + GOST_ENCRYPT_ROUND(l, r, key[2], key[3], sbox); + GOST_ENCRYPT_ROUND(l, r, key[4], key[5], sbox); + GOST_ENCRYPT_ROUND(l, r, key[6], key[7], sbox); + GOST_ENCRYPT_ROUND(l, r, key[7], key[6], sbox); + GOST_ENCRYPT_ROUND(l, r, key[5], key[4], sbox); + GOST_ENCRYPT_ROUND(l, r, key[3], key[2], sbox); + GOST_ENCRYPT_ROUND(l, r, key[1], key[0], sbox); + GOST_ENCRYPT_ROUND(l, r, key[7], key[6], sbox); + GOST_ENCRYPT_ROUND(l, r, key[5], key[4], sbox); + GOST_ENCRYPT_ROUND(l, r, key[3], key[2], sbox); + GOST_ENCRYPT_ROUND(l, r, key[1], key[0], sbox); + GOST_ENCRYPT_ROUND(l, r, key[7], key[6], sbox); + GOST_ENCRYPT_ROUND(l, r, key[5], key[4], sbox); + GOST_ENCRYPT_ROUND(l, r, key[3], key[2], sbox); + GOST_ENCRYPT_ROUND(l, r, 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); +} + +void +gost28147_set_param(struct gost28147_ctx *ctx, const struct gost28147_param *param) +{ + assert(param); + ctx->sbox = param->sbox; +} + +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_block(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_block(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 new file mode 100644 index 000000000000..0df1662ce86d --- /dev/null +++ b/gost28147.h @@ -0,0 +1,85 @@ +/* gost28147.h + + The GOST 28147-89 cipher function, described in RFC 5831. + + Copyright (C) 2020 Dmitry Baryshkov + + 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_GOST28147_H_INCLUDED +#define NETTLE_GOST28147_H_INCLUDED + +#include "nettle-types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define gost28147_get_param_test_3411 nettle_gost28147_get_param_test_3411 +#define gost28147_get_param_CryptoPro_3411 nettle_gost28147_get_param_CryptoPro_3411 + +#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)[256]; +}; + +struct gost28147_param; + +const struct gost28147_param * gost28147_get_param_test_3411 (void); +const struct gost28147_param * gost28147_get_param_CryptoPro_3411 (void); + +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 + +#endif /* NETTLE_GOST28147_H_INCLUDED */ diff --git a/testsuite/Makefile.in b/testsuite/Makefile.in index 3f5e5f6b995c..07dd0ecf3e8d 100644 --- a/testsuite/Makefile.in +++ b/testsuite/Makefile.in @@ -17,7 +17,7 @@ TS_NETTLE_SOURCES = aes-test.c arcfour-test.c arctwo-test.c \ cnd-memcpy-test.c \ des-test.c des3-test.c \ md2-test.c md4-test.c md5-test.c md5-compat-test.c \ - memeql-test.c memxor-test.c gosthash94-test.c \ + memeql-test.c memxor-test.c gost28147-test.c gosthash94-test.c \ ripemd160-test.c hkdf-test.c \ salsa20-test.c \ sha1-test.c sha224-test.c sha256-test.c \ diff --git a/testsuite/gost28147-test.c b/testsuite/gost28147-test.c new file mode 100644 index 000000000000..ee9615b15cf2 --- /dev/null +++ b/testsuite/gost28147-test.c @@ -0,0 +1,77 @@ +#include "testutils.h" +#include "gost28147.h" +#include "cfb.h" +#include "macros.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_param(&ctx, param); + gost28147_set_key(&ctx, key->data); + 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_param(&ctx, param); + gost28147_set_key(&ctx, key->data); + 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, see RFC 5831, Section 7.3.1. + * Exaples there are represented in different endianness */ + test_gost28147(gost28147_get_param_test_3411(), + SHEX("546D2033 68656C32 69736520 73736E62 20616779 69677474 73656865 202C3D73"), + SHEX("00000000 00000000"), + SHEX("1B0BBC32 CEBCAB42")); + + test_gost28147(gost28147_get_param_test_3411(), + SHEX("2033394D 6C320D09 65201A16 6E62001D 67794106 74740E13 6865160D 3D730C11"), + SHEX("00000000 00000000"), + SHEX("FDCF9B5D C8EB0352")); + + test_gost28147(gost28147_get_param_test_3411(), + SHEX("39B213F5 F209A13F 1AE9BA3A FF1D0C62 41F9E1C7 F1130085 16F20D73 F311B180"), + SHEX("00000000 00000000"), + SHEX("280EFF00 9958348D")); + + test_gost28147(gost28147_get_param_test_3411(), + SHEX("EC0A8BA1 5EC004A8 BAC50CAC 0C621DEE E1C7B8E7 007AE2EC F2731BFF 4E80E2A0 "), + SHEX("00000000 00000000"), + SHEX("2D562A0D 190486E7 ")); +}