diff --git a/Cryptography/Kuznyechik/C++/Description.md b/Cryptography/Kuznyechik/C++/Description.md new file mode 100644 index 000000000..d137ee078 --- /dev/null +++ b/Cryptography/Kuznyechik/C++/Description.md @@ -0,0 +1,9 @@ +# Kuznyechik cipher + +Kuznyechik (Russian: Кузнечик, literally "grasshopper") is a symmetric block cipher. It has a block size of 128 bits and key length of 256 bits. It is defined in the National Standard of the Russian Federation GOST R 34.12-2015 in English and also in RFC 7801. + +The name of the cipher can be translated from Russian as grasshopper, however, the standard explicitly says that the English name for the cipher is Kuznyechik (/kʊznˈɛtʃɪk/). The designers claim that by naming the cipher Kuznyechik they follow the trend of difficult to pronounce algorithm names set up by Rijndael and Keccak. There is also a rumor that the cipher was named after its creators: A.S.Kuzmin, A.A.Nechaev and Company (Russian: Кузьмин, Нечаев и Компания). + +The standard GOST R 34.12-2015 defines the new cipher in addition to the old GOST block cipher (now called Magma) as one and does not declare the old cipher obsolete. + +Kuznyechik is based on a substitution-permutation network, though the key schedule employs a Feistel network. \ No newline at end of file diff --git a/Cryptography/Kuznyechik/C++/Kuznyechik.cpp b/Cryptography/Kuznyechik/C++/Kuznyechik.cpp new file mode 100644 index 000000000..a151534b0 --- /dev/null +++ b/Cryptography/Kuznyechik/C++/Kuznyechik.cpp @@ -0,0 +1,162 @@ +#include "kuznechik.h" +#include + +constexpr unsigned char Kuznechik::Pi[256]; +constexpr unsigned char Kuznechik::revPi[256]; +constexpr unsigned char Kuznechik::koef[16]; + +uint8_t *Kuznechik::encrypt(const uint8_t *key, const uint8_t *plaintext) +{ + expandKey(key, key + BLOCK_SIZE); + uint8_t* res = new uint8_t[BLOCK_SIZE]; + _encrypt(plaintext, res); + return res; +} + +uint8_t *Kuznechik::decrypt(const uint8_t *key, const uint8_t *ciphertext) +{ + expandKey(key, key + BLOCK_SIZE); + uint8_t* res = new uint8_t[BLOCK_SIZE]; + _decrypt(ciphertext, res); + return res; +} + +void Kuznechik::_encrypt(const uint8_t *in, uint8_t *out) +{ + + std::memcpy(out, in, BLOCK_SIZE); + for (int i = 0; i < 9; ++i) { + transfX(iterKey[i], out, out); + transfS(out, out); + transfL(out, out); + } + transfX(out, iterKey[9], out); +} + +void Kuznechik::_decrypt(const uint8_t *in, uint8_t *out) +{ + std::memcpy(out, in, BLOCK_SIZE); + transfX(out, iterKey[9], out); + for (int i = 8; i >= 0; --i) { + transfRevL(out, out); + transfRevS(out, out); + transfX(iterKey[i], out, out); + } +} + +void Kuznechik::transfX(const uint8_t *a, const uint8_t *b, uint8_t *c) +{ + for (int i = 0; i < BLOCK_SIZE; ++i) c[i] = a[i] ^ b[i]; +} + +void Kuznechik::transfS(const uint8_t *in, uint8_t* out) +{ + for (int i = 0; i < BLOCK_SIZE; ++i) out[i] = Pi[in[i]]; +} + +void Kuznechik::transfRevS(const uint8_t *in, uint8_t *out) +{ + for (int i = 0; i < BLOCK_SIZE; ++i) out[i] = revPi[in[i]]; +} + +void Kuznechik::transfR(uint8_t *vector) +{ + uint8_t a15 = 0; + uint8_t temp[16]; + for (int i = 15; i >= 0; i--) + { + temp[i - 1] = vector[i]; + a15 ^= gfMul(vector[i], koef[i]); + } + temp[15] = a15; + memcpy(vector, temp, BLOCK_SIZE); +} + +void Kuznechik::transfRevR(uint8_t *vector) +{ + + uint8_t a0; + a0 = vector[15]; + uint8_t temp[16]; + for (int i = 0; i < 16; i++) + { + temp[i] = vector[i - 1]; + a0 ^= gfMul(temp[i], koef[i]); + } + temp[0] = a0; + memcpy(vector, temp, BLOCK_SIZE); +} + +void Kuznechik::transfL(const uint8_t *in, uint8_t *out) +{ + std::memcpy(out, in, BLOCK_SIZE); + for (int i = 0; i < 16; ++i) transfR(out); +} + +void Kuznechik::transfRevL(const uint8_t *in, uint8_t *out) +{ + std::memcpy(out, in, BLOCK_SIZE); + for (int i = 0; i < 16; ++i) transfRevR(out); +} + +void Kuznechik::F(const uint8_t *in1, const uint8_t *in2, uint8_t *out1, uint8_t *out2, const uint8_t *iterConst) +{ + uint8_t internal[BLOCK_SIZE]; + std::memcpy(out2, in1, BLOCK_SIZE); + transfX(in1, iterConst, internal); + transfS(internal, internal); + transfL(internal, internal); + transfX(internal, in2, out1); +} + +void Kuznechik::expandKey(const uint8_t *k1, const uint8_t *k2) +{ + uint8_t it1[64]; + uint8_t it2[64]; + uint8_t it3[64]; + uint8_t it4[64]; + getConsts(); + memcpy(iterKey[0], k1, 64); + memcpy(iterKey[1], k2, 64); + memcpy(it1, k1, 64); + memcpy(it2, k2, 64); + for (int i = 0; i < 4; i++) + { + F(it1, it2, it3, it4, iterConsts[0 + 8 * i]); + F(it3, it4, it1, it2, iterConsts[1 + 8 * i]); + F(it1, it2, it3, it4, iterConsts[2 + 8 * i]); + F(it3, it4, it1, it2, iterConsts[3 + 8 * i]); + F(it1, it2, it3, it4, iterConsts[4 + 8 * i]); + F(it3, it4, it1, it2, iterConsts[5 + 8 * i]); + F(it1, it2, it3, it4, iterConsts[6 + 8 * i]); + F(it3, it4, it1, it2, iterConsts[7 + 8 * i]); + memcpy(iterKey[2 * i + 2], it1, 64); + memcpy(iterKey[2 * i + 3], it2, 64); + } +} + +void Kuznechik::getConsts() +{ + uint8_t iter_num[32][16]; + for (int i = 0; i < 32; i++) + { + std::memset(iter_num[i], 0, BLOCK_SIZE); + iter_num[i][0] = i+1; + } + for (int i = 0; i < 32; i++) + transfL(iter_num[i], iterConsts[i]); +} + +uint8_t Kuznechik::gfMul(uint8_t a, uint8_t b) +{ + uint8_t res = 0; + uint8_t h = 0; + for (int i = 0; i < BLOCK_SIZE; ++i) { + if (b & 1) res ^= a; + h = a & 0b10000000; + a <<= 1; + if (h) a ^= 0b11000011; + b >>= 1; + } + return res; +}