/* * Copyright (c)2013-2020 ZeroTier, Inc. * * Use of this software is governed by the Business Source License included * in the LICENSE.TXT file in the project's root directory. * * Change Date: 2024-01-01 * * On the date above, in accordance with the Business Source License, use * of this software will be governed by version 2.0 of the Apache License. */ /****/ #ifndef ZT_SPECK128_HPP #define ZT_SPECK128_HPP #include "Constants.hpp" #include "Utils.hpp" namespace ZeroTier { /** * Tiny and simple 128-bit ARX block cipher * * Speck does not specify a mandatory endian-ness. This implementation is * little-endian for higher performance on the majority of platforms. * * @tparam R Number of rounds (default: 32) */ template class Speck128 { public: /** * Create an uninitialized instance, init() must be called to set up. */ ZT_INLINE Speck128() noexcept {} /** * Initialize Speck from a 128-bit key * * @param k 128-bit / 16 byte key */ ZT_INLINE Speck128(const void *k) noexcept { this->init(k); } ZT_INLINE ~Speck128() { Utils::burn(_k,sizeof(_k)); } /** * Initialize Speck from a 128-bit key * * @param k 128-bit / 16 byte key */ ZT_INLINE void init(const void *k) noexcept { uint64_t x = Utils::loadLittleEndian(k); uint64_t y = Utils::loadLittleEndian(reinterpret_cast(k) + 8); _k[0] = x; for(uint64_t i=0;i<(R-1);++i) { x = x >> 8U | x << 56U; x += y; x ^= i; y = y << 3U | y >> 61U; y ^= x; _k[i + 1] = y; } } /** * Encrypt a 128-bit block as two 64-bit words * * These should be in host byte order. If read or written to/from data * they should be stored in little-endian byte order. * * @param x Least significant 64 bits * @param y Most significant 64 bits */ ZT_INLINE void encryptXY(uint64_t &x,uint64_t &y) const noexcept { #pragma unroll for (int i=0;i> 8U | x << 56U; x += y; x ^= kk; y = y << 3U | y >> 61U; y ^= x; } } /** * Decrypt a 128-bit block as two 64-bit words * * These should be in host byte order. If read or written to/from data * they should be stored in little-endian byte order. * * @param x Least significant 64 bits * @param y Most significant 64 bits */ ZT_INLINE void decryptXY(uint64_t &x,uint64_t &y) const noexcept { #pragma unroll for (int i=(R-1);i>=0;--i) { const uint64_t kk = _k[i]; y ^= x; y = y >> 3U | y << 61U; x ^= kk; x -= y; x = x << 8U | x >> 56U; } } /** * Encrypt a block * * @param in 128-bit / 16 byte input * @param out 128-bit / 16 byte output */ ZT_INLINE void encrypt(const void *const in,void *const out) const noexcept { uint64_t x = Utils::loadLittleEndian(in); uint64_t y = Utils::loadLittleEndian(reinterpret_cast(in) + 8); encryptXY(x,y); Utils::storeLittleEndian(out,x); Utils::storeLittleEndian(reinterpret_cast(out) + 8,y); } /** * Decrypt a block * * @param in 128-bit / 16 byte input * @param out 128-bit / 16 byte output */ ZT_INLINE void decrypt(const void *const in,void *const out) const noexcept { uint64_t x = Utils::loadLittleEndian(in); uint64_t y = Utils::loadLittleEndian(reinterpret_cast(in) + 8); decryptXY(x,y); Utils::storeLittleEndian(out,x); Utils::storeLittleEndian(reinterpret_cast(out) + 8,y); } private: uint64_t _k[R]; }; } // namespace ZeroTier #endif