diff --git a/Makefile.am b/Makefile.am index 09433ffe..42570fe0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -29,6 +29,8 @@ UTIL_FILES = \ crypto/tea.hpp \ crypto/xtea.cpp \ crypto/xtea.hpp \ + crypto/xxtea.cpp \ + crypto/xxtea.hpp \ crypto/arc4.cpp \ crypto/arc4.hpp \ debug.cpp \ @@ -270,6 +272,7 @@ TEST_BIN = \ test/crypto/arc4 \ test/crypto/tea \ test/crypto/xtea \ + test/crypto/xxtea \ test/extent \ test/fixed \ test/float \ diff --git a/crypto/xxtea.cpp b/crypto/xxtea.cpp new file mode 100644 index 00000000..16ea4f9e --- /dev/null +++ b/crypto/xxtea.cpp @@ -0,0 +1,104 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright 2015 Danny Robson + */ + +#include "xxtea.hpp" + +#include + +// test vectors: http://www.cix.co.uk/~klockstone/teavect.htm + +using util::crypto::XXTEA; + + +//----------------------------------------------------------------------------- +static const uint32_t MAGIC = 0x9E3779B9; +static const unsigned ITERATIONS = 32; // each iteration performs two feistel rounds + + +//----------------------------------------------------------------------------- +static constexpr uint32_t mix (uint32_t Z, + uint32_t Y, + uint32_t S, + size_t E, + size_t P, + const uint32_t *restrict K) +{ + return ((Z >> 5 ^ Y << 2) + (Y >> 3 ^ Z << 4)) ^ ((S ^ Y) + (K[(P & 3) ^ E] ^ Z)); +} + + +//----------------------------------------------------------------------------- +XXTEA::XXTEA (key_t _key): + m_key (_key) +{ ; } + + +//----------------------------------------------------------------------------- +void +XXTEA::encrypt (uint32_t *restrict data, size_t count) +{ + if (count < 2) + throw std::invalid_argument ("minimum blocksize is 64 bits"); + + uint32_t sum = 0; + uint32_t z = data[count - 1]; + uint32_t y, e, p; + + unsigned rounds = 6 + 52 / count; + + do { + sum += MAGIC; + e = (sum >> 2) & 3; + + for (p = 0; p < count - 1; p++) { + y = data[p + 1]; + z = data[p] += mix (z, y, sum, e, p, m_key.data ()); + } + + y = data[0]; + z = data[count - 1] += mix (z, y, sum, e, p, m_key.data ()); + } while (--rounds); +} + + +//----------------------------------------------------------------------------- +void +XXTEA::decrypt (uint32_t *restrict data, size_t count) +{ + if (count < 2) + throw std::invalid_argument ("minimum blocksize is 64 bits"); + + uint32_t y, z, sum; + unsigned p, rounds, e; + + rounds = 6 + 52 / count; + sum = rounds * MAGIC; + y = data[0]; + + do { + e = (sum >> 2) & 3; + + for (p = count - 1; p > 0; p--) { + z = data[p - 1]; + y = data[p ] -= mix (z, y, sum, e, p, m_key.data ()); + } + + z = data[count - 1]; + y = data[ 0] -= mix (z, y, sum, e, p, m_key.data ()); + + sum -= MAGIC; + } while (--rounds); +} diff --git a/crypto/xxtea.hpp b/crypto/xxtea.hpp new file mode 100644 index 00000000..82f69eac --- /dev/null +++ b/crypto/xxtea.hpp @@ -0,0 +1,42 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright 2015 Danny Robson + */ + +#ifndef __UTIL_TEA_HPP +#define __UTIL_TEA_HPP + +#include +#include +#include + +namespace util { namespace crypto { + // http://en.wikipedia.org/wiki/XXTEA + // 'corrected' block TEA + class XXTEA { + public: + using key_t = std::array; + + XXTEA (key_t); + + void encrypt (uint32_t *restrict data, size_t count); + void decrypt (uint32_t *restrict data, size_t count); + + private: + key_t m_key; + }; +} } + +#endif + diff --git a/test/crypto/xxtea.cpp b/test/crypto/xxtea.cpp new file mode 100644 index 00000000..0a4dbb39 --- /dev/null +++ b/test/crypto/xxtea.cpp @@ -0,0 +1,121 @@ +#include "crypto/xxtea.hpp" + +#include "tap.hpp" +#include "types.hpp" + + +int +main () +{ + // test vectors from 'TeaCrypt', by Logan J. Drews. + static const struct { + std::array key; + std::vector dec; + std::vector enc; + } TESTS[] = { + // 64 bit + { + { 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x00000000, 0x00000000 }, + { 0x053704AB, 0x575D8C80 } + }, + + { + {0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x01020304, 0x05060708}, + {0xE6911910, 0x0C35DCDA}, + }, + + { + {0x00112233, 0x44556677, 0x8899AABB, 0xCCDDEEFF}, + {0x01020304, 0x05060708}, + {0x961D49FC, 0x61FF12D6}, + }, + + { + {0x00112233, 0x44556677, 0x8899AABB, 0xCCDDEEFF}, + {0x01234567, 0x89ABCDEF}, + {0x34354989, 0xDD7D1A7A}, + }, + + // 96 bit + { + {0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000}, + {0x5E3CD3F0, 0xE109E3CE, 0x79D7C945}, + }, + + { + {0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00010203, 0x04050607, 0x08090A0B}, + {0x5A545AAC, 0x684EB2CB, 0x3E1B8AA0}, + }, + + { + {0x00112233, 0x44556677, 0x8899AABB, 0xCCDDEEFF}, + {0x00010203, 0x04050607, 0x08090A0B}, + {0x2E77CCEC, 0x674F5149, 0xA0E56496}, + }, + + { + {0x00112233, 0x44556677, 0x8899AABB, 0xCCDDEEFF}, + {0x01234567, 0x89ABCDEF, 0x01234567}, + {0xEBC5DD46, 0xBE0FEE71, 0xC6BF7193}, + }, + + // 128 bit + { + {0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0xE6C8D5FF, 0x070FB6E4, 0x98A534F7, 0xAC03E399}, + }, + + { + {0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00010203, 0x04050607, 0x08090A0B, 0x0C0D0E0F}, + {0xAF5CFB0E, 0xAE73552B, 0x1D968A9F, 0x5CB94509}, + }, + + { + {0x00112233, 0x44556677, 0x8899AABB, 0xCCDDEEFF}, + {0x00010203, 0x04050607, 0x08090A0B, 0x0C0D0E0F}, + {0x3EA0E16C, 0x9969535A, 0xE4796D50, 0xF217EEEA}, + }, + + { + {0x00112233, 0x44556677, 0x8899AABB, 0xCCDDEEFF}, + {0x01234567, 0x89ABCDEF, 0x01234567, 0x89ABCDEF}, + {0x2B4AB1A4, 0x0E487B6D, 0x9A3AACC7, 0xE4132216}, + }, + }; + + util::TAP::logger tap; + + for (size_t i = 0; i < elems (TESTS); ++i) { + const auto &t = TESTS[i]; + + CHECK_EQ (t.dec.size (), t.enc.size ()); + + util::crypto::XXTEA gen (t.key); + + std::vector enc (t.dec); + gen.encrypt (enc.data (), enc.size ()); + + std::vector dec (enc); + gen.decrypt (dec.data (), dec.size ()); + + { + std::ostringstream os; + os << "XXTEA_enc " << i; + tap.expect (enc == t.enc, os.str ()); + } + + { + std::ostringstream os; + os << "XXTEA_dec " << i; + tap.expect (dec == t.dec, os.str ()); + } + } + + return tap.status (); +}