Skip to content

Commit

Permalink
Basic Idea implementation added #32
Browse files Browse the repository at this point in the history
  • Loading branch information
nkaskov committed Jun 24, 2020
1 parent a84f3ba commit b58758f
Show file tree
Hide file tree
Showing 5 changed files with 351 additions and 0 deletions.
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()

0 comments on commit b58758f

Please sign in to comment.