From 0ab4e2f75054141f6dc3c93d4fad4a468268c116 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 26 Sep 2024 19:47:57 -0400 Subject: [PATCH] Roots now understand encrypted HELLO. --- node/AES.hpp | 9 --------- node/Identity.hpp | 4 +++- node/Packet.hpp | 34 ++++++++++++++++++++++++++++++++++ root/root.cpp | 21 ++++++++++++++++++++- 4 files changed, 57 insertions(+), 11 deletions(-) diff --git a/node/AES.hpp b/node/AES.hpp index 15f8077c9..7dc92982f 100644 --- a/node/AES.hpp +++ b/node/AES.hpp @@ -135,17 +135,8 @@ public: while (len >= 16) { _encryptSW((const uint8_t *)ctr,(uint8_t *)cenc); ctr[1] = Utils::hton(++bctr); -#ifdef ZT_NO_TYPE_PUNNING for(unsigned int k=0;k<16;++k) *(o++) = *(i++) ^ ((uint8_t *)cenc)[k]; -#else - *((uint64_t *)o) = *((const uint64_t *)i) ^ cenc[0]; - o += 8; - i += 8; - *((uint64_t *)o) = *((const uint64_t *)i) ^ cenc[1]; - o += 8; - i += 8; -#endif len -= 16; } diff --git a/node/Identity.hpp b/node/Identity.hpp index 9b5f7eb72..efe755911 100644 --- a/node/Identity.hpp +++ b/node/Identity.hpp @@ -447,7 +447,9 @@ public: ZT_ALWAYS_INLINE unsigned long hashCode() const { return ((unsigned long)_address.toInt() + (unsigned long)_pub.c25519[0] + (unsigned long)_pub.c25519[1] + (unsigned long)_pub.c25519[2]); } -private: + ZT_ALWAYS_INLINE const uint8_t *c25519SecretKey() const { return this->_priv.c25519; } + + private: Address _address; Type _type; bool _hasPrivate; diff --git a/node/Packet.hpp b/node/Packet.hpp index 13c400179..ea84711f7 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -111,6 +111,17 @@ */ #define ZT_PROTO_FLAG_FRAGMENTED 0x40 +/** + * Header flag indicating ephemeral keying and second encryption pass. + * + * If this is set, the packet will have an ephemeral key appended to it its payload + * will be encrypted with AES-CTR using this ephemeral key and the packet's header + * as an IV. + * + * Note that this is a reuse of a flag that has long been deprecated and ignored. + */ +#define ZT_PROTO_FLAG_EXTENDED_ARMOR 0x80 + /** * Verb flag indicating payload is compressed with LZ4 */ @@ -1153,6 +1164,29 @@ public: b = (b & 0xc7) | (unsigned char)((c << 3) & 0x38); // bits: FFCCCHHH } + /** + * @return True if packet is encrypted with an extra ephemeral key + */ + inline bool extendedArmor() const + { + return (((unsigned char)(*this)[ZT_PACKET_IDX_FLAGS] & ZT_PROTO_FLAG_EXTENDED_ARMOR) != 0); + } + + /** + * Set this packet's extended armor flag + * + * @param f Extended armor flag value + */ + inline void setExtendedArmor(bool f) + { + if (f) { + (*this)[ZT_PACKET_IDX_FLAGS] |= (char)ZT_PROTO_FLAG_EXTENDED_ARMOR; + } + else { + (*this)[ZT_PACKET_IDX_FLAGS] &= (char)(~ZT_PROTO_FLAG_EXTENDED_ARMOR); + } + } + /** * Get the trusted path ID for this packet (only meaningful if cipher is trusted path) * diff --git a/root/root.cpp b/root/root.cpp index f5b45b089..796c879f4 100644 --- a/root/root.cpp +++ b/root/root.cpp @@ -61,6 +61,7 @@ #include "../node/Packet.hpp" #include "../node/SharedPtr.hpp" #include "../node/Utils.hpp" +#include "../node/AES.hpp" #include "../osdep/BlockingQueue.hpp" #include "../osdep/OSUtils.hpp" #include "geoip-html.h" @@ -265,6 +266,8 @@ static ZT_ALWAYS_INLINE std::array ip6ToH128(const InetAddress& ip) return i128; } +#define ZT_PACKET_IDX_EXTENDED_ARMOR_START 19 + static void handlePacket(const int sock, const InetAddress* const ip, Packet& pkt) { char ipstr[128], ipstr2[128], astr[32], astr2[32], tmpstr[256]; @@ -281,6 +284,23 @@ static void handlePacket(const int sock, const InetAddress* const ip, Packet& pk if ((! fragment) && (! pkt.fragmented()) && (dest == s_self.address())) { SharedPtr peer; + if ((pkt.cipher() == ZT_PROTO_CIPHER_SUITE__POLY1305_NONE) && pkt.extendedArmor()) { + if (pkt.size() < (ZT_PROTO_MIN_PACKET_LENGTH + 32)) { + return; + } + uint8_t ephemeralSymmetric[64]; + C25519::agree(s_self.c25519SecretKey(), (const uint8_t *)pkt.data() + (pkt.size() - 32), ephemeralSymmetric); + SHA512(ephemeralSymmetric, ephemeralSymmetric, 32); + + AES cipher(ephemeralSymmetric); + uint32_t ctrIv[4]; + memcpy(ctrIv, pkt.data(), 12); + ctrIv[3] = 0; + cipher.ctr((const uint8_t *)ctrIv, (const uint8_t *)pkt.data() + ZT_PACKET_IDX_EXTENDED_ARMOR_START, (pkt.size() - ZT_PACKET_IDX_EXTENDED_ARMOR_START) - 32, (uint8_t *)pkt.data() + ZT_PACKET_IDX_EXTENDED_ARMOR_START); + + pkt.setSize(pkt.size() - 32); + } + // If this is an un-encrypted HELLO, either learn a new peer or verify // that this is a peer we already know. if ((pkt.cipher() == ZT_PROTO_CIPHER_SUITE__POLY1305_NONE) && (pkt.verb() == Packet::VERB_HELLO)) { @@ -304,7 +324,6 @@ static void handlePacket(const int sock, const InetAddress* const ip, Packet& pk pkt.reset(source, s_self.address(), Packet::VERB_ERROR); pkt.append((uint8_t)Packet::VERB_HELLO); pkt.append(origId); - ; pkt.append((uint8_t)Packet::ERROR_IDENTITY_COLLISION); pkt.armor(key, true); sendto(sock, pkt.data(), pkt.size(), SENDTO_FLAGS, (const struct sockaddr*)ip, (socklen_t)((ip->ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)));