diff --git a/Makefile.am b/Makefile.am index a7cc4d44..51bc13d6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -53,6 +53,8 @@ UTIL_FILES = \ coord/store.hpp \ crypto/arc4.cpp \ crypto/arc4.hpp \ + crypto/ice.cpp \ + crypto/ice.hpp \ crypto/tea.cpp \ crypto/tea.hpp \ crypto/xtea.cpp \ @@ -126,6 +128,8 @@ UTIL_FILES = \ hash/hmac.hpp \ hash/hotp.cpp \ hash/hotp.hpp \ + hash/keccak.cpp \ + hash/keccak.hpp \ hash/md2.cpp \ hash/md2.hpp \ hash/md4.cpp \ @@ -393,6 +397,7 @@ TEST_BIN = \ test/colour \ test/coord \ test/crypto/arc4 \ + test/crypto/ice \ test/crypto/tea \ test/crypto/xtea \ test/crypto/xxtea \ @@ -405,6 +410,7 @@ TEST_BIN = \ test/geom/ray \ test/hash/murmur \ test/hash/fasthash \ + test/hash/keccak \ test/hmac \ test/hotp \ test/hton \ diff --git a/README b/README index df7200d2..aab59119 100644 --- a/README +++ b/README @@ -1 +1,3 @@ A simple cross-platform C++ utility library. + +For the love of God, do not use the crypto routines. I am not a cryptographer or a security specialist. You have been warned. diff --git a/bitwise.hpp b/bitwise.hpp index 6be6c2d7..62c052bd 100644 --- a/bitwise.hpp +++ b/bitwise.hpp @@ -31,25 +31,21 @@ const uint8_t BITMASK_7BITS = 0x7F; const uint8_t BITMASK_8BITS = 0xFF; /////////////////////////////////////////////////////////////////////////////// -#define MODT(x) ((x) % (sizeof (T) * 8)) - template constexpr T -rotatel (const T &value, size_t magnitude) { - return (value << MODT (magnitude)) | - (value >> sizeof (value) * 8 - MODT (magnitude)); +rotatel [[gnu::pure]] (const T value, size_t magnitude) +{ + return (value << magnitude) | (value >> sizeof (value) * 8 - magnitude); } template constexpr T -rotater (const T &value, size_t magnitude) { - return (value >> MODT (magnitude)) | - (value << sizeof (value) * 8 - MODT (magnitude)); +rotater [[gnu::pure]] (const T value, size_t magnitude) +{ + return (value >> magnitude) | (value << sizeof (value) * 8 - magnitude); } -#undef MODT - /////////////////////////////////////////////////////////////////////////////// // TODO: make constexpr for C++14 diff --git a/crypto/ice.cpp b/crypto/ice.cpp new file mode 100644 index 00000000..ff090e5f --- /dev/null +++ b/crypto/ice.cpp @@ -0,0 +1,437 @@ +/* + * 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 2016 Danny Robson + */ + +// Derived from Mathew Kwan's 1996 C++ public domain implementation of ICE: +// http://www.darkside.com.au/ice/ +// +// M. Kwan, The Design of the ICE Encryption Algorithm, proceedings of Fast +// Software Encryption - Fourth International Workshop, Haifa, Israel, +// Springer-Verlag, pp. 69-82, 1997 + +#include "./ice.hpp" + +#include "endian.hpp" + +#include + + +/////////////////////////////////////////////////////////////////////////////// +/* + * C++ implementation of the ICE encryption algorithm. + * + * Written by Matthew Kwan - July 1996 + */ + + +/* The S-boxes */ +static uint32_t ice_sbox[4][1024]; +static bool ice_sboxes_initialised = false; + + +/* Modulo values for the S-boxes */ +static +constexpr +uint_fast16_t +ice_smod[4][4] = { + {333, 313, 505, 369}, + {379, 375, 319, 391}, + {361, 445, 451, 397}, + {397, 425, 395, 505} +}; + + +/* XOR values for the S-boxes */ +constexpr +uint8_t +ice_sxor[4][4] = { + {0x83, 0x85, 0x9b, 0xcd}, + {0xcc, 0xa7, 0xad, 0x41}, + {0x4b, 0x2e, 0xd4, 0x33}, + {0xea, 0xcb, 0x2e, 0x04} +}; + + +/* Permutation values for the P-box */ +constexpr +uint32_t +ice_pbox[32] = { + 0x00000001, 0x00000080, 0x00000400, 0x00002000, + 0x00080000, 0x00200000, 0x01000000, 0x40000000, + 0x00000008, 0x00000020, 0x00000100, 0x00004000, + 0x00010000, 0x00800000, 0x04000000, 0x20000000, + 0x00000004, 0x00000010, 0x00000200, 0x00008000, + 0x00020000, 0x00400000, 0x08000000, 0x10000000, + 0x00000002, 0x00000040, 0x00000800, 0x00001000, + 0x00040000, 0x00100000, 0x02000000, 0x80000000 +}; + + +/* The key rotation schedule */ +constexpr +std::array +ice_keyrot[2] = { + { 0, 1, 2, 3, 2, 1, 3, 0, }, + { 1, 3, 2, 0, 3, 1, 0, 2, }, +}; + + +/* + * 8-bit Galois Field multiplication of a by b, modulo m. + * Just like arithmetic multiplication, except that additions and + * subtractions are replaced by XOR. + */ + +template +static +T +gf_mult (T a, T b, const T m) +{ + T res = 0; + + while (b) { + if (b & 1u) + res ^= a; + + a <<= 1u; + b >>= 1u; + + if (a >= 256) + a ^= m; + } + + return res; +} + + +/* + * Galois Field exponentiation. + * Raise the base to the power of 7, modulo m. + */ + +template +static +T +gf_exp7 (const T b, + const T m) +{ + if (b == 0) + return 0; + + T x; + + x = gf_mult (b, b, m); + x = gf_mult (b, x, m); + x = gf_mult (x, x, m); + + return gf_mult (b, x, m); +} + + +/* + * Carry out the ICE 32-bit P-box permutation. + */ + +static +uint32_t +ice_perm32 (uint32_t x) +{ + uint32_t res = 0; + const uint32_t *pbox = ice_pbox; + + while (x) { + if (x & 1) + res |= *pbox; + pbox++; + x >>= 1; + } + + return res; +} + + +/* + * Initialise the ICE S-boxes. + * This only has to be done once. + */ +static +void +ice_sboxes_init (void) +{ + for (unsigned i = 0; i < 1024; i++) { + const uint_fast16_t col = (i >> 1) & 0xff; + const uint_fast16_t row = (i & 0x1) | ((i & 0x200) >> 8); + + for (unsigned j = 0; j < 4; ++j) { + const auto p = gf_exp7 ( + col ^ ice_sxor[j][row], + ice_smod[j][row] + ) << (24 - j * 8); + + ice_sbox[j][i] = ice_perm32 (p); + } + } +} + + +/* + * Create a new ICE key. + */ + +ice::ice (unsigned n, + const uint64_t *key_first, + const uint64_t *key_last) +{ + if (!ice_sboxes_initialised) { + ice_sboxes_init (); + ice_sboxes_initialised = true; + } + + if (n < 1) { + m_size = 1; + m_rounds = 8; + } else { + m_size = n; + m_rounds = n * 16; + } + + m_schedule.resize (m_rounds); + + set (key_first, key_last); +} + + +/* + * Destroy an ICE key. + */ + +ice::~ice () +{ + for (auto &s: m_schedule) + std::fill (std::begin (s), std::end (s), 0); + + m_rounds = m_size = 0; +} + + +/* + * The single round ICE f function. + */ + +static +uint32_t +ice_f (uint32_t p, const ice::subkey_t &sk) +{ + uint_fast64_t tl, tr; /* Expanded 40-bit values */ + uint_fast64_t al, ar; /* Salted expanded 40-bit values */ + + /* Left half expansion */ + tl = ((p >> 16) & 0x3ff) | (((p >> 14) | (p << 18)) & 0xffc00); + + /* Right half expansion */ + tr = (p & 0x3ff) | ((p << 2) & 0xffc00); + + /* Perform the salt permutation */ + // al = (tr & sk->val[2]) | (tl & ~sk->val[2]); + // ar = (tl & sk->val[2]) | (tr & ~sk->val[2]); + al = sk[2] & (tl ^ tr); + ar = al ^ tr; + al ^= tl; + + al ^= sk[0]; /* XOR with the subkey */ + ar ^= sk[1]; + + /* S-box lookup and permutation */ + return ( + ice_sbox[0][al >> 10] | ice_sbox[1][al & 0x3ff] + | ice_sbox[2][ar >> 10] | ice_sbox[3][ar & 0x3ff] + ); +} + + +/* + * Encrypt a block of 8 bytes of data with the given ICE key. + */ +uint64_t +ice::encrypt (const uint64_t _ptext) const +{ + union { + uint64_t pword; + uint8_t pbytes[8]; + }; + + pword = hton (_ptext); + + uint32_t l, r; + + l = (((uint32_t) pbytes[0]) << 24u) + | (((uint32_t) pbytes[1]) << 16u) + | (((uint32_t) pbytes[2]) << 8u) + | pbytes[3]; + r = (((uint32_t) pbytes[4]) << 24u) + | (((uint32_t) pbytes[5]) << 16u) + | (((uint32_t) pbytes[6]) << 8u) + | pbytes[7]; + + for (unsigned i = 0; i < m_rounds; i += 2) { + l ^= ice_f (r, m_schedule[i]); + r ^= ice_f (l, m_schedule[i + 1]); + } + + union { + uint64_t cword; + uint8_t cbytes[8]; + }; + + for (unsigned i = 0; i < 4; i++) { + cbytes[3 - i] = r & 0xff; + cbytes[7 - i] = l & 0xff; + + r >>= 8u; + l >>= 8u; + } + + return hton (cword); +} + + +/* + * Decrypt a block of 8 bytes of data with the given ICE key. + */ + +uint64_t +ice::decrypt (const uint64_t _ctext) const +{ + union { + uint64_t cword; + uint8_t cbytes[8]; + }; + + cword = hton (_ctext); + + uint32_t l, r; + + l = (((uint32_t) cbytes[0]) << 24u) + | (((uint32_t) cbytes[1]) << 16u) + | (((uint32_t) cbytes[2]) << 8u) + | cbytes[3]; + r = (((uint32_t) cbytes[4]) << 24u) + | (((uint32_t) cbytes[5]) << 16u) + | (((uint32_t) cbytes[6]) << 8u) + | cbytes[7]; + + for (int i = m_rounds - 1; i > 0; i -= 2) { + l ^= ice_f (r, m_schedule[i]); + r ^= ice_f (l, m_schedule[i - 1]); + } + + union { + uint64_t pword; + uint8_t pbytes[8]; + }; + + for (unsigned i = 0; i < 4; i++) { + pbytes[3 - i] = r & 0xff; + pbytes[7 - i] = l & 0xff; + + r >>= 8; + l >>= 8; + } + + return hton (pword); +} + + +/* + * Set 8 rounds [n, n+7] of the key schedule of an ICE key. + */ + +void +ice::scheduleBuild (std::array &kb, + int n, + const std::array &keyrot) +{ + for (unsigned i = 0; i < 8; i++) { + int kr = keyrot[i]; + subkey_t &isk = m_schedule[n + i]; + + std::fill (std::begin (isk), std::end (isk), 0); + + for (unsigned j = 0; j < 15; j++) { + uint32_t &curr_sk = isk[j % 3]; + + for (unsigned k = 0; k < 4; k++) { + auto &curr_kb = kb[(kr + k) & 3]; + unsigned bit = curr_kb & 1; + + curr_sk = (curr_sk << 1) | bit; + curr_kb = (curr_kb >> 1) | ((bit ^ 1) << 15); + } + } + } +} + + +/* + * Set the key schedule of an ICE key. + */ + +void +ice::set (const uint64_t *_key_first, const uint64_t *_key_last) +{ + auto key = reinterpret_cast (_key_first); + + if (m_rounds == 8) { + std::array kb; + + for (unsigned i = 0; i < 4; i++) + kb[3 - i] = (key[i * 2] << 8) | key[i * 2 + 1]; + + scheduleBuild (kb, 0, ice_keyrot[0]); + return; + } + + for (unsigned i = 0; i < m_size; i++) { + std::array kb; + + for (unsigned j = 0; j < 4; j++) + kb[3 - j] = (key[i * 8 + j * 2] << 8) | key[i * 8 + j * 2 + 1]; + + scheduleBuild (kb, i * 8, ice_keyrot[0]); + scheduleBuild (kb, m_rounds - 8 - i * 8, ice_keyrot[1]); + } +} + + +/* + * Return the key size, in bytes. + */ + +unsigned +ice::key_size () const +{ + return (m_size * 8); +} + + +/* + * Return the block size, in bytes. + */ + +unsigned +ice::block_size () const +{ + return (8); +} diff --git a/crypto/ice.hpp b/crypto/ice.hpp new file mode 100644 index 00000000..d0426d57 --- /dev/null +++ b/crypto/ice.hpp @@ -0,0 +1,64 @@ +/* + * 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 2016 Danny Robson + */ + +#ifndef __CRYPTO_ICE_HPP +#define __CRYPTO_ICE_HPP + +#include +#include +#include + +// An implementation of the ICE symmetric-key block cipher +// +// we make a token attempt to zero our buffers, but we can't guarantee this +// will take place (given compiler optimisations). further security is +// outside the scope of this class. +class ice { + public: + ice (unsigned n, uint64_t key); + + ice (unsigned n, + const uint64_t *key_first, + const uint64_t *key_last); + ~ice (); + + void + set (const uint64_t *key_first, const uint64_t *key_last); + + uint64_t encrypt (uint64_t plaintext) const; + uint64_t decrypt (uint64_t ciphertext) const; + + unsigned key_size () const; + unsigned block_size () const; + + using subkey_t = std::array; + + private: + void + scheduleBuild (std::array &k, + int n, + const std::array &keyrot); + + unsigned m_size; + unsigned m_rounds; + + std::vector m_schedule; +}; + +struct ice_error : public std::runtime_error { using runtime_error::runtime_error; }; +struct level_error : public ice_error { using ice_error::ice_error; }; + +#endif diff --git a/extent.cpp b/extent.cpp index 9528bdf8..7e1c3eb0 100644 --- a/extent.cpp +++ b/extent.cpp @@ -20,7 +20,7 @@ #include "./maths.hpp" #include "./stream.hpp" -#include +#include #include using util::extent; diff --git a/geom/aabb.ipp b/geom/aabb.ipp index 1f3ec558..793f2998 100644 --- a/geom/aabb.ipp +++ b/geom/aabb.ipp @@ -24,6 +24,8 @@ #include + +/////////////////////////////////////////////////////////////////////////////// namespace util { namespace geom { template struct sampler { diff --git a/hash/keccak.cpp b/hash/keccak.cpp new file mode 100644 index 00000000..b614c049 --- /dev/null +++ b/hash/keccak.cpp @@ -0,0 +1,485 @@ +#include "./keccak.hpp" + +#include "../endian.hpp" +#include "../maths.hpp" +#include "../bitwise.hpp" + +#include +#include +#include +#include + +#if 0 +#define FOR(i,n) for(i=0; i>1; } +#define ROL(a,o) ((((u64)a)<>(64-o))) +static u64 load64(const u8 *x) { ui i; u64 u=0; FOR(i,8) { u<<=8; u|=x[7-i]; } return u; } +static void store64(u8 *x, u64 u) { ui i; FOR(i,8) { x[i]=u; u>>=8; } } +static void xor64(u8 *x, u64 u) { ui i; FOR(i,8) { x[i]^=u; u>>=8; } } +#define rL(x,y) load64((u8*)s+8*(x+5*y)) +#define wL(x,y,l) store64((u8*)s+8*(x+5*y),l) +#define XL(x,y,l) xor64((u8*)s+8*(x+5*y),l) +void KeccakF1600(void *s) +{ + ui r,x,y,i,j,Y; u8 R=0x01; u64 C[5],D; + for(i=0; i<24; i++) { + /*θ*/ FOR(x,5) C[x]=rL(x,0)^rL(x,1)^rL(x,2)^rL(x,3)^rL(x,4); FOR(x,5) { D=C[(x+4)%5]^ROL(C[(x+1)%5],1); FOR(y,5) XL(x,y,D); } + /*ρπ*/ x=1; y=r=0; D=rL(x,y); FOR(j,24) { r+=j+1; Y=(2*x+3*y)%5; x=y; y=Y; C[0]=rL(x,y); wL(x,y,ROL(D,r%64)); D=C[0]; } + /*χ*/ FOR(y,5) { FOR(x,5) C[x]=rL(x,y); FOR(x,5) wL(x,y,C[x]^((~C[(x+1)%5])&C[(x+2)%5])); } + /*ι*/ FOR(j,7) if (LFSR86540(&R)) XL(0,0,(u64)1<<((1<0) { b=(inLen0) { b=(outLen0) KeccakF1600(s); } +} + +#else + +// derived from Keccak (KCP) readable-and-compact C implementation + +/* +Implementation by the Keccak, Keyak and Ketje Teams, namely, Guido Bertoni, +Joan Daemen, Michaël Peeters, Gilles Van Assche and Ronny Van Keer, hereby +denoted as "the implementer". + +For more information, feedback or questions, please refer to our websites: +http://keccak.noekeon.org/ +http://keyak.noekeon.org/ +http://ketje.noekeon.org/ + +To the extent possible under law, the implementer has waived all copyright +and related or neighboring rights to the source code in this file. +http://creativecommons.org/publicdomain/zero/1.0/ +*/ + +/* +================================================================ +The purpose of this source file is to demonstrate a readable and compact +implementation of all the Keccak instances approved in the FIPS 202 standard, +including the hash functions and the extendable-output functions (XOFs). + +We focused on clarity and on source-code compactness, +rather than on the performance. + +The advantages of this implementation are: + + The source code is compact, after removing the comments, that is. :-) + + There are no tables with arbitrary constants. + + For clarity, the comments link the operations to the specifications using + the same notation as much as possible. + + There is no restriction in cryptographic features. In particular, + the SHAKE128 and SHAKE256 XOFs can produce any output length. + + The code does not use much RAM, as all operations are done in place. + +The drawbacks of this implementation are: + - There is no message queue. The whole message must be ready in a buffer. + - It is not optimized for peformance. + +The implementation is even simpler on a little endian platform. Just define the +LITTLE_ENDIAN symbol in that case. + +For a more complete set of implementations, please refer to +the Keccak Code Package at https://github.com/gvanas/KeccakCodePackage + +For more information, please refer to: + * [Keccak Reference] http://keccak.noekeon.org/Keccak-reference-3.0.pdf + * [Keccak Specifications Summary] http://keccak.noekeon.org/specs_summary.html + +This file uses UTF-8 encoding, as some comments use Greek letters. +================================================================ +*/ + +/** + * Function to compute the Keccak[r, c] sponge function over a given input. + * @param rate The value of the rate r. + * @param capacity The value of the capacity c. + * @param input Pointer to the input message. + * @param inputByteLen The number of input bytes provided in the input message. + * @param delimitedSuffix Bits that will be automatically appended to the end + * of the input message, as in domain separation. + * This is a byte containing from 0 to 7 bits + * These n bits must be in the least significant bit positions + * and must be delimited with a bit 1 at position n + * (counting from 0=LSB to 7=MSB) and followed by bits 0 + * from position n+1 to position 7. + * Some examples: + * - If no bits are to be appended, then @a delimitedSuffix must be 0x01. + * - If the 2-bit sequence 0,1 is to be appended (as for SHA3-*), @a delimitedSuffix must be 0x06. + * - If the 4-bit sequence 1,1,1,1 is to be appended (as for SHAKE*), @a delimitedSuffix must be 0x1F. + * - If the 7-bit sequence 1,1,0,1,0,0,0 is to be absorbed, @a delimitedSuffix must be 0x8B. + * @param output Pointer to the buffer where to store the output. + * @param outputByteLen The number of output bytes desired. + * @pre One must have r+c=1600 and the rate a multiple of 8 bits in this implementation. + */ + +/** + * Function to compute SHAKE128 on the input message with any output length. + */ +void +FIPS202_SHAKE128(const uint8_t *input, size_t inputByteLen, uint8_t *output, size_t outputByteLen) +{ + keccak k (1344, 256, 0x1f); + k.update (input, inputByteLen); + k.digest (output, outputByteLen); +} + +/** + * Function to compute SHAKE256 on the input message with any output length. + */ +void FIPS202_SHAKE256(const uint8_t *input, size_t inputByteLen, uint8_t *output, size_t outputByteLen) +{ + keccak k (1088, 512, 0x1f); + k.update (input, inputByteLen); + k.digest (output, outputByteLen); +} + +/** + * Function to compute SHA3-224 on the input message. The output length is fixed to 28 bytes. + */ +void FIPS202_SHA3_224(const uint8_t *input, size_t inputByteLen, uint8_t *output) +{ + keccak k (1152, 448, 0x06); + + for (unsigned int i = 0; i < inputByteLen; ++i) + k.update (&input[i], 1); + //k.update (input, inputByteLen); + k.digest (output, 28); +} + +/** + * Function to compute SHA3-256 on the input message. The output length is fixed to 32 bytes. + */ +void FIPS202_SHA3_256(const uint8_t *input, size_t inputByteLen, uint8_t *output) +{ + keccak k (1088, 512, 0x06); + k.update (input, inputByteLen); + k.digest (output, 32); +} + +/** + * Function to compute SHA3-384 on the input message. The output length is fixed to 48 bytes. + */ +void FIPS202_SHA3_384(const uint8_t *input, size_t inputByteLen, uint8_t *output) +{ + keccak k (832, 768, 0x06); + k.update (input, inputByteLen); + k.digest (output, 48); +} + +/** + * Function to compute SHA3-512 on the input message. The output length is fixed to 64 bytes. + */ +void FIPS202_SHA3_512(const uint8_t *input, size_t inputByteLen, uint8_t *output) +{ + keccak k (576, 1024, 0x06); + k.update (input, inputByteLen); + k.digest (output, 64); +} + + +/* +================================================================ +A readable and compact implementation of the Keccak-f[1600] permutation. +================================================================ +*/ + +static constexpr +size_t +i (size_t x, size_t y) +{ + return x + 5 * y; +} + + +/** + * Function that computes the linear feedback shift register (LFSR) used to + * define the round constants (see [Keccak Reference, Section 1.2]). + */ +class lfsr86540 { +public: + lfsr86540 (): + value (0x1) + { ; } + + + bool + update (void) + { + bool result = value & 0x01; + if (value & 0x80) + // Primitive polynomial over GF(2): x^8+x^6+x^5+x^4+1 + value = (value << 1) ^ 0x71; + else + value <<= 1; + return result; + } + + +private: + uint8_t value; +}; + + +// θ step, see [Keccak Reference, Section 2.3.2] +static void +permute_theta (uint64_t m_words[5][5]) +{ + uint64_t C[5], D; + + // Compute the parity of the columns + for (unsigned x = 0; x < 5; ++x) + C[x] = m_words[0][x] ^ m_words[1][x] ^ m_words[2][x] ^ m_words[3][x] ^ m_words[4][x]; + + for (unsigned x = 0; x < 5; ++x) { + // Compute the θ effect for a given column + D = C[(x+4)%5] ^ rotatel (C[(x+1)%5], 1); + + // Add the θ effect to the whole column + for (unsigned y = 0; y < 5; ++y) + m_words[y][x] ^= D; + } +} + + +void +permute_rho (uint64_t m_words[5][5]) +{ + m_words[0][1] = rotatel (m_words[0][1], 1); + m_words[0][2] = rotatel (m_words[0][2], 62); + m_words[0][3] = rotatel (m_words[0][3], 28); + m_words[0][4] = rotatel (m_words[0][4], 27); + m_words[1][0] = rotatel (m_words[1][0], 36); + m_words[1][1] = rotatel (m_words[1][1], 44); + m_words[1][2] = rotatel (m_words[1][2], 6); + m_words[1][3] = rotatel (m_words[1][3], 55); + m_words[1][4] = rotatel (m_words[1][4], 20); + m_words[2][0] = rotatel (m_words[2][0], 3); + m_words[2][1] = rotatel (m_words[2][1], 10); + m_words[2][2] = rotatel (m_words[2][2], 43); + m_words[2][3] = rotatel (m_words[2][3], 25); + m_words[2][4] = rotatel (m_words[2][4], 39); + m_words[3][0] = rotatel (m_words[3][0], 41); + m_words[3][1] = rotatel (m_words[3][1], 45); + m_words[3][2] = rotatel (m_words[3][2], 15); + m_words[3][3] = rotatel (m_words[3][3], 21); + m_words[3][4] = rotatel (m_words[3][4], 8); + m_words[4][0] = rotatel (m_words[4][0], 18); + m_words[4][1] = rotatel (m_words[4][1], 2); + m_words[4][2] = rotatel (m_words[4][2], 61); + m_words[4][3] = rotatel (m_words[4][3], 56); + m_words[4][4] = rotatel (m_words[4][4], 14); + return; + + + for (size_t i = 1; i < 25; ++i) { + //unsigned r = ((t+1)*(t+2)/2)%64; + unsigned r = ((i + 1) * (i + 2) / 2) % 64; + + m_words[i/5][i%5] = rotatel (m_words[i/5][i%5], r); + } +} + + +void +permute_pi (uint64_t m_words[5][5]) +{ + //auto A = reinterpret_cast (m_words); + + //uint64_t A1; + //A1 = A[1]; + //A[ 1] = A[ 6]; + //A[ 6] = A[ 9]; + //A[ 9] = A[22]; + //A[22] = A[14]; + //A[14] = A[20]; + //A[20] = A[ 2]; + //A[ 2] = A[12]; + //A[12] = A[13]; + //A[13] = A[19]; + //A[19] = A[23]; + //A[23] = A[15]; + //A[15] = A[ 4]; + //A[ 4] = A[24]; + //A[24] = A[21]; + //A[21] = A[ 8]; + //A[ 8] = A[16]; + //A[16] = A[ 5]; + //A[ 5] = A[ 3]; + //A[ 3] = A[18]; + //A[18] = A[17]; + //A[17] = A[11]; + //A[11] = A[ 7]; + //A[ 7] = A[10]; + //A[10] = A1; + //return; + + unsigned x = 1, y = 0; + uint64_t current = m_words[y][x]; + uint64_t temp; + // Iterate over ((0 1)(2 3))^t * (1 0) for 0 ≤ t ≤ 23 + for (unsigned t = 0; t < 24; ++t) { + unsigned int Y = (2*x+3*y)%5; + x = y; + y = Y; + + temp = m_words[y][x]; + m_words[y][x] = current; + current = temp; + } + + //for (unsigned int i = 0; i < 5; ++i) + // for (unsigned int j = 0; j < 5; ++j) + // m_words[j][(2*i+3*j)%5] = m_words[i][j]; +} + + +/** + * Function that computes the Keccak-f[1600] permutation on the given state. + */ +void +keccak::permute (void) +{ + for (size_t i = 0; i < m_bitrate/64; ++i) + m_words[i/5][i%5] = ltoh (m_words[i/5][i%5]); + + lfsr86540 shift; + + for (unsigned round = 0; round < 24; ++round) { + permute_theta (m_words); + permute_rho (m_words); + permute_pi (m_words); + + + if (0) { // === ρ and π steps (see [Keccak Reference, Sections 2.3.3 and 2.3.4]) === + uint64_t current, temp; + // Start at coordinates (1 0) + unsigned x = 1, y = 0; + current = m_words[y][x]; + // Iterate over ((0 1)(2 3))^t * (1 0) for 0 ≤ t ≤ 23 + for (unsigned t = 0; t < 24; ++t) { + // Compute the rotation constant r = (t+1)(t+2)/2 + unsigned int r = ((t+1)*(t+2)/2)%64; + + // Compute ((0 1)(2 3)) * (x y) + unsigned int Y = (2*x+3*y)%5; x = y; y = Y; + + // Swap current and state(x,y), and rotate + temp = m_words[y][x]; + m_words[y][x] = rotatel (current, r); + current = temp; + } + } + + { // === χ step (see [Keccak Reference, Section 2.3.1]) === + uint64_t temp[5]; + for (unsigned y = 0; y < 5; ++y) { + // Take a copy of the plane + for (unsigned x = 0; x < 5; ++x) + temp[x] = m_words[y][x]; + + // Compute χ on the plane + for(unsigned x = 0; x < 5; ++x) + m_words[y][x] = temp[x] ^((~temp[(x+1)%5]) & temp[(x+2)%5]); + } + } + + { // === ι step (see [Keccak Reference, Section 2.3.5]) === + for (unsigned j = 0; j < 7; ++j) { + unsigned int bitPosition = (1 << j) - 1; //2^j-1 + if (shift.update ()) + m_words[0][0] ^= uint64_t{1} << bitPosition; + } + } + } +} + +/* +================================================================ +A readable and compact implementation of the Keccak sponge functions +that use the Keccak-f[1600] permutation. +================================================================ +*/ + +#include + + +void +keccak::update ( + const uint8_t *input, + size_t len +) { + unsigned int byterate = m_bitrate / 8; + + while (len) { + auto chunk = util::min (len, byterate - m_cursor); + + for (unsigned i = 0; i < chunk; ++i) + m_bytes[m_cursor++] ^= *input++; + + len -= chunk; + + if (m_cursor == byterate) { + permute (); + m_cursor = 0; + } + } +} + + +void +keccak::digest ( + uint8_t *output, + size_t len +) { + unsigned byterate = m_bitrate / 8u; + + // === Do the padding and switch to the squeezing phase === + // Absorb the last few bits and add the first bit of padding (which + // coincides with the delimiter in delimitedSuffix) + m_bytes[m_cursor] ^= m_suffix; + + // If the first bit of padding is at position rate-1, we need a whole new + // block for the second bit of padding + if (m_suffix & 0x80 && m_cursor == byterate - 1) + permute (); + + // Add the second bit of padding + m_bytes[byterate - 1] ^= 0x80; + + // === Squeeze out all the output blocks === + while (len) { + permute (); + + auto chunk = util::min (len, byterate); + std::copy_n (m_bytes.begin (), chunk, output); + + output += chunk; + len -= chunk; + } +} + + +keccak::keccak (unsigned _bitrate, + unsigned _capacity, + uint8_t _suffix): + m_bitrate (_bitrate), + m_capacity (_capacity), + m_suffix (_suffix), + m_cursor (0) +{ + // we could support bitrates that are multiples of 8, but 64 simplifies + // some state handling, and the SHA-3 constants are all multiples of 64 + // bits anyway. + if ((m_bitrate + m_capacity) / 8 != sizeof (m_bytes) || m_bitrate % 64 != 0) + throw "error"; + + std::fill (std::begin (m_bytes), std::end (m_bytes), 0); +} + + + +#endif diff --git a/hash/keccak.hpp b/hash/keccak.hpp new file mode 100644 index 00000000..682146d9 --- /dev/null +++ b/hash/keccak.hpp @@ -0,0 +1,53 @@ +/* + * 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 2016 Danny Robson + */ + +#ifndef __HASH_KECCAK_HPP +#define __HASH_KECCAK_HPP + +#include +#include + +class keccak { +public: + keccak (unsigned bitrate, unsigned capacity, uint8_t suffix); + + void update (const uint8_t *input, size_t len); + void digest (uint8_t *output, size_t len); + +private: + void permute (void); + + const unsigned m_bitrate; + const unsigned m_capacity; + const uint8_t m_suffix; + + size_t m_cursor; + + union { + std::array m_bytes; + uint64_t m_words[5][5]; + }; +}; + + +void FIPS202_SHAKE128(const uint8_t *input, size_t inputByteLen, uint8_t *output, size_t outputByteLen); +void FIPS202_SHAKE256(const uint8_t *input, size_t inputByteLen, uint8_t *output, size_t outputByteLen); +void FIPS202_SHA3_224(const uint8_t *input, size_t inputByteLen, uint8_t *output); +void FIPS202_SHA3_256(const uint8_t *input, size_t inputByteLen, uint8_t *output); +void FIPS202_SHA3_384(const uint8_t *input, size_t inputByteLen, uint8_t *output); +void FIPS202_SHA3_512(const uint8_t *input, size_t inputByteLen, uint8_t *output); + +#endif diff --git a/tap.hpp b/tap.hpp index edf5f0bc..bc1c462a 100644 --- a/tap.hpp +++ b/tap.hpp @@ -45,34 +45,34 @@ namespace util { namespace TAP { //--------------------------------------------------------------------- template - void expect_eq (const T&, const U&, const std::string &fmt, Args&...); + void expect_eq (const T&, const U&, const std::string &fmt, Args&&...); template - void expect_neq (const T&, const U&, const std::string &fmt, Args&...); + void expect_neq (const T&, const U&, const std::string &fmt, Args&&...); //--------------------------------------------------------------------- template - void expect_gt (const T&, const U&, const std::string &fmt, Args&...); + void expect_gt (const T&, const U&, const std::string &fmt, Args&&...); template - void expect_ge (const T&, const U&, const std::string &fmt, Args&...); + void expect_ge (const T&, const U&, const std::string &fmt, Args&&...); template - void expect_lt (const T&, const U&, const std::string &fmt, Args&...); + void expect_lt (const T&, const U&, const std::string &fmt, Args&&...); template - void expect_le (const T&, const U&, const std::string &fmt, Args&...); + void expect_le (const T&, const U&, const std::string &fmt, Args&&...); //--------------------------------------------------------------------- template - void expect_nan (const T&, const std::string &fmt, Args&...); + void expect_nan (const T&, const std::string &fmt, Args&&...); //--------------------------------------------------------------------- template - void expect_nothrow (T&&, const std::string &fmt, Args&...); + void expect_nothrow (T&&, const std::string &fmt, Args&&...); template - void expect_throw (T&&, const std::string &fmt, Args&...); + void expect_throw (T&&, const std::string &fmt, Args&&...); //--------------------------------------------------------------------- void skip (const std::string &msg); diff --git a/tap.ipp b/tap.ipp index 9842b2ca..c0de09b4 100644 --- a/tap.ipp +++ b/tap.ipp @@ -54,7 +54,7 @@ util::TAP::logger::expect (const std::function &test, Args&&... a //----------------------------------------------------------------------------- template void -util::TAP::logger::expect_eq (const T&a, const U &b, const std::string &fmt, Args&... args) +util::TAP::logger::expect_eq (const T&a, const U &b, const std::string &fmt, Args&&... args) { static const std::function TEST = [] (const T &t, const U &u) -> bool { return almost_equal (t, u); @@ -67,7 +67,7 @@ util::TAP::logger::expect_eq (const T&a, const U &b, const std::string &fmt, Arg //----------------------------------------------------------------------------- template void -util::TAP::logger::expect_neq (const T&a, const U &b, const std::string &fmt, Args&... args) +util::TAP::logger::expect_neq (const T&a, const U &b, const std::string &fmt, Args&&... args) { static const std::function TEST = [] (const T &t, const U &u) -> bool { return !almost_equal (t, u); @@ -84,7 +84,7 @@ void \ util::TAP::logger::expect_ ## SUFFIX (const T &a, \ const U &b, \ const std::string &fmt, \ - Args&... args) \ + Args&&... args) \ { \ static const std::function< \ bool(const T&,const U&) \ @@ -111,7 +111,7 @@ TAP_TEST(le, <=) //----------------------------------------------------------------------------- template void -util::TAP::logger::expect_nan (const T &t, const std::string &fmt, Args&... args) +util::TAP::logger::expect_nan (const T &t, const std::string &fmt, Args&&... args) { bool(*func)(T) = std::isnan; expect ( @@ -125,7 +125,7 @@ util::TAP::logger::expect_nan (const T &t, const std::string &fmt, Args&... args //----------------------------------------------------------------------------- template void -util::TAP::logger::expect_nothrow (T &&t, const std::string &fmt, Args&... args) +util::TAP::logger::expect_nothrow (T &&t, const std::string &fmt, Args&&... args) { bool success = true; @@ -142,7 +142,7 @@ util::TAP::logger::expect_nothrow (T &&t, const std::string &fmt, Args&... args) //----------------------------------------------------------------------------- template void -util::TAP::logger::expect_throw (T &&t, const std::string &fmt, Args&... args) +util::TAP::logger::expect_throw (T &&t, const std::string &fmt, Args&&... args) { bool success = false; diff --git a/test/crypto/ice.cpp b/test/crypto/ice.cpp new file mode 100644 index 00000000..0c6403e0 --- /dev/null +++ b/test/crypto/ice.cpp @@ -0,0 +1,43 @@ +#include "crypto/ice.hpp" +#include "tap.hpp" +#include "iterator.hpp" +#include "stream.hpp" +#include "endian.hpp" + +#include +#include +#include +#include + +int +main (int, char**) +{ + const uint64_t data = 0xfedcba9876543210; + + struct { + unsigned level; + uint64_t crypt; + std::vector key; + } TESTS[] = { + { 0, 0xde240d83a00a9cc0, { 0xdeadbeef01234567 } }, + { 1, 0x7d6ef1ef30d47a96, { 0xdeadbeef01234567 } }, + { 2, 0xf94840d86972f21c, { 0x0011223344556677, 0x8899aabbccddeeff } }, + }; + + util::TAP::logger tap; + + for (const auto &t: TESTS) { + std::vector k (t.key.cbegin (), t.key.cend ()); + std::transform (k.cbegin (), k.cend (), k.begin (), hton); + + ice key (t.level, k.data (), k.data () + k.size ()); + + auto crypt = key.encrypt (data); + auto plain = key.decrypt (t.crypt); + + tap.expect_eq (crypt, t.crypt, "ICE level %u certification, encrypt", t.level); + tap.expect_eq (plain, data, "ICE level %u certification, decrypt", t.level); + } + + return tap.status (); +} diff --git a/test/hash/keccak.cpp b/test/hash/keccak.cpp new file mode 100644 index 00000000..45fc7487 --- /dev/null +++ b/test/hash/keccak.cpp @@ -0,0 +1,179 @@ +#include "hash/keccak.hpp" +#include "debug.hpp" +#include "tap.hpp" + +#include + + +static constexpr +uint8_t +from_hex (char c) { + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return c - '0'; + + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + return 10 + c - 'A'; + + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + return 10 + c - 'a'; + } + + unreachable (); +} + + +template +static constexpr +std::array +make_array (const char (&str)[S]) +{ + if (S < 1) + return {}; + + static_assert (S % 2 == 1, "requires full bytes + null"); + std::array out {}; + + for (size_t i = 0; i < S - 1; i+=2) { + out[i / 2] = +from_hex (str[i]) << 4 | +from_hex (str[i+1]); + } + + return out; +} + + +template +static +std::vector +make_vector (const char (&str)[S]) { + const auto arr = make_array (str); + return std::vector (arr.cbegin (), arr.cend ()); +} + + +int +main (int, char**) +{ + util::TAP::logger tap; + + static const struct { + const char *msg; + std::vector data; + + std::array sha3_224; + std::array sha3_256; + std::array sha3_384; + std::array sha3_512; + } TESTS[] = { + { + " empty string", + { }, + make_array ("6B4E03423667DBB73B6E15454F0EB1ABD4597F9A1B078E3F5B5A6BC7"), + make_array ("A7FFC6F8BF1ED76651C14756A061D662F580FF4DE43B49FA82D80A4B80F8434A"), + make_array ("0C63A75B845E4F7D01107D852E4C2485C51A50AAAA94FC61995E71BBEE983A2AC3713831264ADB47FB6BD1E058D5F004"), + make_array ("A69F73CCA23A9AC5C8B567DC185A756E97C982164FE25859E0D1DCC1475C80A615B2123AF1F5F94C11E3E9402C3AC558F500199D95B6D3E301758586281DCD26"), + }, + { + " 8-bit string", + make_vector ("CC"), + make_array ("DF70ADC49B2E76EEE3A6931B93FA41841C3AF2CDF5B32A18B5478C39"), + make_array ("677035391CD3701293D385F037BA32796252BB7CE180B00B582DD9B20AAAD7F0"), + make_array ("5EE7F374973CD4BB3DC41E3081346798497FF6E36CB9352281DFE07D07FC530CA9AD8EF7AAD56EF5D41BE83D5E543807"), + make_array ("3939FCC8B57B63612542DA31A834E5DCC36E2EE0F652AC72E02624FA2E5ADEECC7DD6BB3580224B4D6138706FC6E80597B528051230B00621CC2B22999EAA205"), + }, + { + " 16-bit string", + make_vector ("41FB"), + make_array ("BFF295861DAEDF33E70519B1E2BCB4C2E9FE3364D789BC3B17301C15"), + make_array ("39F31B6E653DFCD9CAED2602FD87F61B6254F581312FB6EEEC4D7148FA2E72AA"), + make_array ("1DD81609DCC290EFFD7AC0A95D4A20821580E56BD50DBD843920650BE7A80A1719577DA337CFDF86E51C764CAA2E10BD"), + make_array ("AA092865A40694D91754DBC767B5202C546E226877147A95CB8B4C8F8709FE8CD6905256B089DA37896EA5CA19D2CD9AB94C7192FC39F7CD4D598975A3013C69"), + }, + { + "224-bit string", + make_vector ("0F8B2D8FCFD9D68CFFC17CCFB117709B53D26462A3F346FB7C79B85E"), + make_array ("1E693B0BCE2372550DAEF35B14F13AB43441ED6742DEE3E86FD1D8EF"), + make_array ("6DE164A9626D5A4F54D854AC158994F35A8E362ECC753F55182790934A2E0D06"), + make_array ("641A7AF13B889D1A0F1AA3E4E4FF8CC5903C47E1A52BDEA257D80E37E596564AB33EEAD06717CDB6B706CB6986293D4F"), + make_array ("21132FC11F6040AD493D627027C752CE29816589DE7BE78562914B63D1A9219803DDBD9673AA749F37FF4D6E1B5AE2A12633BA8B0C9994E031EBF6C42E58A793"), + }, + { + "256-bit string", + make_vector ("9F2FCC7C90DE090D6B87CD7E9718C1EA6CB21118FC2D5DE9F97E5DB6AC1E9C10"), + make_array ("887921848AD98458F3DB3E0ECD5AD5DB1F0BF9F2D0CA08601074D597"), + make_array ("2F1A5F7159E34EA19CDDC70EBF9B81F1A66DB40615D7EAD3CC1F1B954D82A3AF"), + make_array ("BAAE7AAED4FBF42F9316C7E8F722EEB06A598B509F184B22FBD5A81C93D95FFF711F5DE90847B3248B6DF76CABCE07EE"), + make_array ("B087C90421AEBF87911647DE9D465CBDA166B672EC47CCD4054A7135A1EF885E7903B52C3F2C3FE722B1C169297A91B82428956A02C631A2240F12162C7BC726"), + }, + { + "384-bit string", + make_vector ("D8FABA1F5194C4DB5F176FABFFF856924EF627A37CD08CF55608BBA8F1E324D7C7F157298EABC4DCE7D89CE5162499F9"), + make_array ("B7A51FBB084DEEB55136EFD7260E5B112E3C40D1A2D14B142DF930DF"), + make_array ("34F8607EC10C092C1BA0B6565CE6197062C4E1A35A8E8C723E48A2D2416C3790"), + make_array ("A127FEFCDD240F762CCE3F5F1551FC7E1CDEBC7950D1CD94C6888F490CB2285A10FD0EE797B168C5CA4761FA232AAF05"), + make_array ("7EF3A2894C6ECBC4201B15348F90671515ACCBA3C8166621F864A9184BF08C3F5A895F6B599D3CB41F20A8A1DF25AE84F1A6D7C8DE74FB7CEF48F7E96FDE8D43"), + }, + { + "512-bit string", + make_vector ("E926AE8B0AF6E53176DBFFCC2A6B88C6BD765F939D3D178A9BDE9EF3AA131C61E31C1E42CDFAF4B4DCDE579A37E150EFBEF5555B4C1CB40439D835A724E2FAE7"), + make_array ("C154607F986F9BF902D831293C8386D36B201EABA6F6FB0B678B4B81"), + make_array ("27A6441EE939B46E2C378D7AFEB0E891C47A28120E488EFF0AB71AF08788CEB3"), + make_array ("423BA134D3BCB5E440AC83372C7EDDBA3AE3BDDF1222F505C19CDE246AD76A2B0D07239A54E1D0934C9B3D29D49E5FBD"), + make_array ("EB5067BF762A291CF258AD69A816A0B089E0BD44F8E5B74CF60BCE64734E59853CCB8D091CD2E33F90AA063FB7942CF5965D459200144C1A0801ABD69A9A094A"), + }, + { + "520-bit string", + make_vector ("16E8B3D8F988E9BB04DE9C96F2627811C973CE4A5296B4772CA3EEFEB80A652BDF21F50DF79F32DB23F9F73D393B2D57D9A0297F7A2F2E79CFDA39FA393DF1AC00"), + make_array ("95E87AC90F541AB90CBCF7FD7E0E0C152CEF78D5EE1830E9ED8A1ED7"), + make_array ("C4BB067383002DB44CA773918BB74104B604A583E12B06BE56C270F8B43512F2"), + make_array ("662C4851D311A786DE4CDA7E9EA1EFF0BFA462761FF6CF804E591ED9A15B0DC93A2BB6A6CFFDC8D7D23A233A52C86EAD"), + make_array ("B0E23D600BA4215F79D50047BBFED50DF7D6E769514D796AFD166DEECA88BD1CBE0AFC72A41E0317A223225B4F5882F723AFCBA3AF7C457EB525946DA6C53BB0"), + }, + { + "1,000,000 'a' string", + std::vector (1'000'000, 'a'), + make_array ("d69335b93325192e516a912e6d19a15cb51c6ed5c15243e7a7fd653c"), + make_array ("5c8875ae474a3634ba4fd55ec85bffd661f32aca75c6d699d0cdcb6c115891c1"), + make_array ("eee9e24d78c1855337983451df97c8ad9eedf256c6334f8e948d252d5e0e76847aa0774ddb90a842190d2c558b4b8340"), + make_array ("3c3a876da14034ab60627c077bb98f7e120a2a5370212dffb3385a18d4f38859ed311d0a9d5141ce9cc5c66ee689b266a8aa18ace8282a0e0db596c90b0a7b87"), + } + }; + + for (const auto &t: TESTS) { + #define TEST(WIDTH) \ + do { \ + std::array sha3_##WIDTH; \ + std::fill (std::begin (sha3_##WIDTH), std::end (sha3_##WIDTH), 0); \ + \ + FIPS202_SHA3_##WIDTH (t.data.data (), t.data.size (), &sha3_##WIDTH[0]); \ + \ + tap.expect_eq (t.sha3_##WIDTH, sha3_##WIDTH, "sha3-%u %s", unsigned (WIDTH), t.msg); \ + } while (0) + + TEST(224); + TEST(256); + TEST(384); + TEST(512); + } + + return tap.status (); +}