ZeroTierOne/node/SymmetricKey.hpp
2020-05-06 10:42:09 -07:00

109 lines
3.1 KiB
C++

/*
* 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<uint8_t *>(secret), key);
}
ZT_INLINE ~SymmetricKey() noexcept
{
Utils::burn(const_cast<uint8_t *>(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