/* * Copyright (c)2019 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: 2023-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_EPHEMERALKEY_HPP #define ZT_EPHEMERALKEY_HPP #include "Constants.hpp" #include "C25519.hpp" #include "ECC384.hpp" #include "SHA512.hpp" #include "Buffer.hpp" #include "Utils.hpp" namespace ZeroTier { #define ZT_EPHEMERAL_KEY_TYPE_1_PUBLIC_SIZE (ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE) #define ZT_EPHEMERAL_KEY_TYPE_1_PRIVATE_SIZE (ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE) /** * An ephemeral key exchanged to implement forward secrecy * * This key includes both C25519 and ECC384 keys and key agreement executes * ECDH for both and hashes the results together. This should be able to be * FIPS compliant (if the C25519 portion is just considered a nonce) while * simultaneously being more secure than either curve alone. * * Serialization includes only the public portion since ephemeral private * keys are never shared or stored anywhere. */ class EphemeralKey { public: enum Type { NONE = 0, C25519ECC384 = 1 }; ZT_ALWAYS_INLINE EphemeralKey() : _priv(nullptr),_type(NONE) {} ZT_ALWAYS_INLINE ~EphemeralKey() { if (_priv) { Utils::burn(_priv,ZT_EPHEMERAL_KEY_TYPE_1_PRIVATE_SIZE); delete [] _priv; } } ZT_ALWAYS_INLINE Type type() const { return (Type)_type; } ZT_ALWAYS_INLINE bool hasPrivate() const { return (_priv != nullptr); } ZT_ALWAYS_INLINE void generate() { if (!_priv) _priv = new uint8_t[ZT_EPHEMERAL_KEY_TYPE_1_PRIVATE_SIZE]; C25519::generate(_pub,_priv); ECC384GenerateKey(_pub + ZT_C25519_PUBLIC_KEY_LEN,_priv + ZT_C25519_PRIVATE_KEY_LEN); _type = C25519ECC384; } ZT_ALWAYS_INLINE bool agree(const EphemeralKey &theirs,uint8_t key[ZT_PEER_SECRET_KEY_LENGTH]) const { if ((_priv)&&(_type == 1)) { uint8_t rawkey[128],h[48]; C25519::agree(_priv,theirs._pub,rawkey); ECC384ECDH(theirs._pub + ZT_C25519_PUBLIC_KEY_LEN,_priv + ZT_C25519_PRIVATE_KEY_LEN,rawkey + ZT_C25519_SHARED_KEY_LEN); SHA384(h,rawkey,ZT_C25519_SHARED_KEY_LEN + ZT_ECC384_SHARED_SECRET_SIZE); memcpy(key,h,ZT_PEER_SECRET_KEY_LENGTH); return true; } return false; } template ZT_ALWAYS_INLINE void serialize(Buffer &b) const { b.append(_type); if (_type == C25519ECC384) b.append(_pub,ZT_EPHEMERAL_KEY_TYPE_1_PUBLIC_SIZE); } template ZT_ALWAYS_INLINE unsigned int deserialize(const Buffer &b,unsigned int startAt = 0) { unsigned int p = startAt; delete [] _priv; _priv = nullptr; switch(b[p++]) { case C25519ECC384: memcpy(_pub,b.field(p,ZT_EPHEMERAL_KEY_TYPE_1_PUBLIC_SIZE),ZT_EPHEMERAL_KEY_TYPE_1_PUBLIC_SIZE); p += ZT_EPHEMERAL_KEY_TYPE_1_PUBLIC_SIZE; break; default: _type = NONE; break; } return (p - startAt); } private: uint8_t *_priv; uint8_t _pub[ZT_EPHEMERAL_KEY_TYPE_1_PUBLIC_SIZE]; uint8_t _type; }; } // namespace ZeroTier #endif