Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement IDEA block cipher #50

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions include/nil/crypto3/block/detail/idea/idea_functions.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//---------------------------------------------------------------------------//
// Copyright (c) 2018-2020 Mikhail Komarov <nemo@nil.foundation>
//
// Distributed under the Boost Software License, Version 1.0
// See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt
//---------------------------------------------------------------------------//

#ifndef CRYPTO3_IDEA_FUNCTIONS_HPP
#define CRYPTO3_IDEA_FUNCTIONS_HPP

#include <nil/crypto3/detail/basic_functions.hpp>
#include <nil/crypto3/block/detail/utilities/constant_time_utilities.hpp>

namespace nil {
namespace crypto3 {
namespace block {
namespace detail {
template<std::size_t WordBits>
struct idea_functions : public ::nil::crypto3::detail::basic_functions<WordBits> {
typedef typename ::nil::crypto3::detail::basic_functions<WordBits>::word_type word_type;

/*
* Multiplication modulo 65537
*/
static inline word_type mul(word_type x, word_type y) {
const uint32_t P = static_cast<uint32_t>(x) * y;

const word_type Z_mask = static_cast<uint16_t>(crypto3::detail::expand_mask(P) & 0xFFFF);

const uint32_t P_hi = P >> 16;
const uint32_t P_lo = P & 0xFFFF;

const word_type carry = (P_lo < P_hi);
const word_type r_1 = static_cast<uint16_t>((P_lo - P_hi) + carry);
const word_type r_2 = 1 - x - y;

return crypto3::detail::select(Z_mask, r_1, r_2);
}

/*
* Find multiplicative inverses modulo 65537
*
* 65537 is prime; thus Fermat's little theorem tells us that
* x^65537 == x modulo 65537, which means
* x^(65537-2) == x^-1 modulo 65537 since
* x^(65537-2) * x == 1 mod 65537
*
* Do the exponentiation with a basic square and multiply: all bits are
* of exponent are 1 so we always multiply
*/
static inline word_type mul_inv(word_type x) {
word_type y = x;

for (size_t i = 0; i != 15; ++i) {
y = mul(y, y); // square
y = mul(y, x);
}

return y;
}
};
} // namespace detail
} // namespace block
} // namespace crypto3
} // namespace nil

#endif // CRYPTO3_IDEA_FUNCTIONS_CPP_HPP
39 changes: 39 additions & 0 deletions include/nil/crypto3/block/detail/idea/idea_policy.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//---------------------------------------------------------------------------//
// Copyright (c) 2018-2020 Mikhail Komarov <nemo@nil.foundation>
//
// Distributed under the Boost Software License, Version 1.0
// See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt
//---------------------------------------------------------------------------//

#ifndef CRYPTO3_IDEA_POLICY_HPP
#define CRYPTO3_IDEA_POLICY_HPP

#include <array>

#include <nil/crypto3/block/detail/idea/idea_functions.hpp>

namespace nil {
namespace crypto3 {
namespace block {
namespace detail {
struct idea_policy : idea_functions<16> {
constexpr static const std::size_t rounds = 8;

constexpr static const std::size_t block_bits = 64;
constexpr static const std::size_t block_words = block_bits / word_bits;
typedef std::array<word_type, block_words> block_type;

constexpr static const std::size_t key_bits = 128;
constexpr static const std::size_t key_words = key_bits / word_bits;
typedef std::array<word_type, key_words> key_type;

constexpr static const std::size_t key_schedule_size = 52;
typedef std::array<word_type, key_schedule_size> key_schedule_type;
};
} // namespace detail
} // namespace block
} // namespace crypto3
} // namespace nil

#endif // CRYPTO3_IDEA_POLICY_HPP
172 changes: 172 additions & 0 deletions include/nil/crypto3/block/idea.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
//---------------------------------------------------------------------------//
// Copyright (c) 2018-2020 Mikhail Komarov <nemo@nil.foundation>
// Copyright (c) 2020 Nikita Kaskov <nbering@nil.foundation>
//
// Distributed under the Boost Software License, Version 1.0
// See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt
//---------------------------------------------------------------------------//

#ifndef CRYPTO3_BLOCK_IDEA_HPP
#define CRYPTO3_BLOCK_IDEA_HPP

#include <boost/endian/arithmetic.hpp>
#include <boost/endian/conversion.hpp>

#include <nil/crypto3/block/detail/idea/idea_policy.hpp>

#include <nil/crypto3/block/detail/block_stream_processor.hpp>
#include <nil/crypto3/block/detail/cipher_modes.hpp>

namespace nil {
namespace crypto3 {
namespace block {
/*!
* @brief Idea. An older but still unbroken 64-bit cipher with a
* 128-bit key. Somewhat common due to its use in PGP. Avoid in new
* designs.
*
* @ingroup block
*/
class idea {
protected:
typedef detail::idea_policy policy_type;

constexpr static const std::size_t key_schedule_size = policy_type::key_schedule_size;
typedef typename policy_type::key_schedule_type key_schedule_type;

public:
constexpr static const std::size_t rounds = policy_type::rounds;

constexpr static const std::size_t word_bits = policy_type::word_bits;
typedef typename policy_type::word_type word_type;

constexpr static const std::size_t block_bits = policy_type::block_bits;
constexpr static const std::size_t block_words = policy_type::block_words;
typedef typename policy_type::block_type block_type;

constexpr static const std::size_t key_bits = policy_type::key_bits;
constexpr static const std::size_t key_words = policy_type::key_words;
typedef typename policy_type::key_type key_type;

template<class Mode, typename StateAccumulator, std::size_t ValueBits>
struct stream_processor {
struct params_type {

constexpr static const std::size_t value_bits = ValueBits;
constexpr static const std::size_t length_bits = policy_type::word_bits * 2;
};

typedef block_stream_processor<Mode, StateAccumulator, params_type> type;
};

typedef typename stream_endian::little_octet_big_bit endian_type;

idea(const key_type &key) {
schedule_key(key);
}

~idea() {
encryption_key.fill(0);
decryption_key.fill(0);
}

inline block_type encrypt(const block_type &plaintext) const {
return idea_op(plaintext, encryption_key);
}

inline block_type decrypt(const block_type &ciphertext) const {
return idea_op(ciphertext, decryption_key);
}

protected:
key_schedule_type encryption_key, decryption_key;

static inline block_type idea_op(const block_type &input, const key_schedule_type &key) {
block_type out = {0};
word_type X1 = boost::endian::native_to_big(input[0]);
word_type X2 = boost::endian::native_to_big(input[1]);
word_type X3 = boost::endian::native_to_big(input[2]);
word_type X4 = boost::endian::native_to_big(input[3]);

for (size_t j = 0; j != policy_type::rounds; ++j) {
X1 = policy_type::mul(X1, key[6 * j + 0]);
X2 += key[6 * j + 1];
X3 += key[6 * j + 2];
X4 = policy_type::mul(X4, key[6 * j + 3]);

word_type T0 = X3;
X3 = policy_type::mul(X3 ^ X1, key[6 * j + 4]);

word_type T1 = X2;
X2 = policy_type::mul((X2 ^ X4) + X3, key[6 * j + 5]);
X3 += X2;

X1 ^= X2;
X4 ^= X3;
X2 ^= T0;
X3 ^= T1;
}

X1 = policy_type::mul(X1, key[48]);
X2 += key[50];
X3 += key[49];
X4 = policy_type::mul(X4, key[51]);

return {boost::endian::big_to_native(X1), boost::endian::big_to_native(X2),
boost::endian::big_to_native(X3), boost::endian::big_to_native(X4)};
}

inline void schedule_key(const key_type &key) {
crypto3::detail::poison(key.data(), 16);
crypto3::detail::poison(encryption_key.data(), key_schedule_size);
crypto3::detail::poison(decryption_key.data(), key_schedule_size);

std::array<uint64_t, 2> K = {0};

K[0] = boost::endian::native_to_big(key[0]);
K[1] = boost::endian::native_to_big(key[1]);

for (size_t off = 0; off != 48; off += 8) {
for (size_t i = 0; i != 8; ++i) {
encryption_key[off + i] = K[i / 4] >> (48 - 16 * (i % 4));
}

const uint64_t Kx = (K[0] >> 39);
const uint64_t Ky = (K[1] >> 39);

K[0] = (K[0] << 25) | Ky;
K[1] = (K[1] << 25) | Kx;
}

for (size_t i = 0; i != 4; ++i) {
encryption_key[48 + i] = K[i / 4] >> (48 - 16 * (i % 4));
}

K.fill(0);

decryption_key[0] = policy_type::mul_inv(encryption_key[48]);
decryption_key[1] = -encryption_key[49];
decryption_key[2] = -encryption_key[50];
decryption_key[3] = policy_type::mul_inv(encryption_key[51]);

for (size_t i = 0; i != 8 * 6; i += 6) {
decryption_key[i + 4] = encryption_key[46 - i];
decryption_key[i + 5] = encryption_key[47 - i];
decryption_key[i + 6] = policy_type::mul_inv(encryption_key[42 - i]);
decryption_key[i + 7] = -encryption_key[44 - i];
decryption_key[i + 8] = -encryption_key[43 - i];
decryption_key[i + 9] = policy_type::mul_inv(encryption_key[45 - i]);
}

std::swap(decryption_key[49], decryption_key[50]);

crypto3::detail::unpoison(key.data(), 16);
crypto3::detail::unpoison(encryption_key.data(), 52);
crypto3::detail::unpoison(decryption_key.data(), 52);
}
};
} // namespace block
} // namespace crypto3
} // namespace nil
#endif
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ endmacro()
set(TESTS_NAMES
"pack"
"rijndael"
"idea"
"md4"
"md5"
"shacal"
Expand Down
71 changes: 71 additions & 0 deletions test/idea.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//---------------------------------------------------------------------------//
// Copyright (c) 2018-2020 Mikhail Komarov <nemo@nil.foundation>
//
// Distributed under the Boost Software License, Version 1.0
// See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt
//---------------------------------------------------------------------------//

#define BOOST_TEST_MODULE idea_cipher_test

#include <iostream>
#include <unordered_map>

#include <boost/test/unit_test.hpp>
#include <boost/test/data/test_case.hpp>
#include <boost/test/data/monomorphic.hpp>

#include <nil/crypto3/block/algorithm/encrypt.hpp>
#include <nil/crypto3/block/algorithm/decrypt.hpp>

#include <nil/crypto3/block/idea.hpp>

using namespace nil::crypto3::block;

namespace boost {
namespace test_tools {
namespace tt_detail {
template<template<typename, typename> class P, typename K, typename V>
struct print_log_value<P<K, V>> {
void operator()(std::ostream&, P<K, V> const&) {
}
};
} // namespace tt_detail
} // namespace test_tools
} // namespace boost

static const std::unordered_map<std::string, std::vector<uint8_t>> valid_data = {
{"Zg==", {0x66}},
{"Zm8=", {0x66, 0x6F}},
{"Zm9v", {0x66, 0x6F, 0x6F}},
{"aGVsbG8gd29ybGQ=", {0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64}},
{"aGVsbG8gd29ybGQh", {0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x21}},
{"SGVsbG8sIHdvcmxkLg==", {0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x2C, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x2E}},
{"VGhlIDEyIGNoYXJz", {0x54, 0x68, 0x65, 0x20, 0x31, 0x32, 0x20, 0x63, 0x68, 0x61, 0x72, 0x73}},
{"VGhlIDEzIGNoYXJzLg==", {0x54, 0x68, 0x65, 0x20, 0x31, 0x33, 0x20, 0x63, 0x68, 0x61, 0x72, 0x73, 0x2E}},
{"VGhlIDE0IGNoYXJzLi4=", {0x54, 0x68, 0x65, 0x20, 0x31, 0x34, 0x20, 0x63, 0x68, 0x61, 0x72, 0x73, 0x2E, 0x2E}},
{"VGhlIDE1IGNoYXJzLi4u",
{0x54, 0x68, 0x65, 0x20, 0x31, 0x35, 0x20, 0x63, 0x68, 0x61, 0x72, 0x73, 0x2E, 0x2E, 0x2E}},
{"QW4gVVRGLTggdXVtbDogw7w=",
{0x41, 0x6E, 0x20, 0x55, 0x54, 0x46, 0x2D, 0x38, 0x20, 0x75, 0x75, 0x6D, 0x6C, 0x3A, 0x20, 0xC3, 0xBC}},
{"V2VpcmQgR2VybWFuIDIgYnl0ZSB0aGluZzogw58u",
{0x57, 0x65, 0x69, 0x72, 0x64, 0x20, 0x47, 0x65, 0x72, 0x6D, 0x61, 0x6E, 0x20, 0x32, 0x20,
0x62, 0x79, 0x74, 0x65, 0x20, 0x74, 0x68, 0x69, 0x6E, 0x67, 0x3A, 0x20, 0xC3, 0x9F, 0x2E}},
{"mw==", {0x9B}},
{"HGA=", {0x1C, 0x60}},
{"gTS9", {0x81, 0x34, 0xBD}},
{"Xmz/3g==", {0x5E, 0x6C, 0xFF, 0xDE}},
{"ss3w3H8=", {0xb2, 0xcd, 0xf0, 0xdc, 0x7f}},
{"/FYt2tQO", {0xfc, 0x56, 0x2d, 0xda, 0xd4, 0x0e}},
{"KbIyLohB6A==", {0x29, 0xb2, 0x32, 0x2e, 0x88, 0x41, 0xe8}},
{"Dw/O2Ul6r5I=", {0x0f, 0x0f, 0xce, 0xd9, 0x49, 0x7a, 0xaf, 0x92}},
{"Jw+xiYKADaZA", {0x27, 0x0f, 0xb1, 0x89, 0x82, 0x80, 0x0d, 0xa6, 0x40}}};

static const std::vector<std::string> invalid_data = {"ZOOL!isnotvalidbase64", "Neitheris:this?"};

BOOST_AUTO_TEST_SUITE(idea_encode_test_suite)

BOOST_DATA_TEST_CASE(idea_single_range_encode, boost::unit_test::data::make(valid_data), array_element) {
}

BOOST_AUTO_TEST_SUITE_END()