/* * 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_SYMMETRICKEY_HPP #define ZT_SYMMETRICKEY_HPP #include "Constants.hpp" #include "Utils.hpp" #include "InetAddress.hpp" #include "AES.hpp" #include "SharedPtr.hpp" #include "Address.hpp" namespace ZeroTier { /** * Container for symmetric keys and ciphers initialized with them. */ class SymmetricKey { friend class SharedPtr< SymmetricKey >; public: /** * Secret key */ const uint8_t secret[ZT_SYMMETRIC_KEY_SIZE]; /** * Symmetric cipher keyed with this key */ const AES cipher; /** * Construct a new symmetric key * * SECURITY: we use a best effort method to construct the nonce's starting point so as * to avoid nonce duplication across invocations. The most significant bits are the * number of seconds since epoch but with the most significant bit masked to zero. * The least significant bits are random. Key life time is limited to 2^31 messages * per key as per the AES-GMAC-SIV spec, and this is a SIV mode anyway so nonce repetition * is non-catastrophic. * * The masking of the most significant bit is because we bisect the nonce space by * which direction the message is going. If the sender's ZeroTier address is * numerically greater than the receiver, this bit is flipped. This means that * two sides of a conversation that have created their key instances at the same * time are much less likely to duplicate nonces when sending pacekts from either * end. * * @param ts Current time * @param key 48-bit / 384-byte key */ explicit ZT_INLINE SymmetricKey(const int64_t ts, const void *const key) noexcept: secret(), cipher(key), // AES-256 uses first 256 bits of 384-bit key m_initialNonce(((((uint64_t)ts / 1000ULL) << 32U) & 0x7fffffff00000000ULL) | (Utils::random() & 0x00000000ffffffffULL)), m_nonce(m_initialNonce), __refCount(0) { Utils::memoryLock(this, sizeof(SymmetricKey)); Utils::copy< ZT_SYMMETRIC_KEY_SIZE >(const_cast(secret), key); } ZT_INLINE ~SymmetricKey() noexcept { Utils::burn(const_cast(secret), ZT_SYMMETRIC_KEY_SIZE); Utils::memoryUnlock(this, sizeof(SymmetricKey)); } /** * Advance usage counter by one and return the next IV / packet ID. * * @param sender Sending ZeroTier address * @param receiver Receiving ZeroTier address * @return Next unique IV for next message */ ZT_INLINE uint64_t nextMessage(const Address sender, const Address receiver) noexcept { return m_nonce.fetch_add(1) ^ (((uint64_t)(sender > receiver)) << 63U); } /** * @return Number of times nextMessage() has been called since object creation */ ZT_INLINE uint64_t odometer() const noexcept { return m_nonce.load() - m_initialNonce; } private: const uint64_t m_initialNonce; std::atomic< uint64_t > m_nonce; std::atomic< int > __refCount; }; } // namespace ZeroTier #endif