Browse Source

Merge branch 'edge' into cmake

Grant Limberg 6 years ago
parent
commit
8e04f83232
57 changed files with 3773 additions and 2071 deletions
  1. 158 0
      attic/root.cpp
  2. 0 7
      attic/world/README.md
  3. 0 3
      attic/world/build.sh
  4. 0 154
      attic/world/mkworld.cpp
  5. BIN
      attic/world/world.bin
  6. 0 2
      attic/world/world.c
  7. 1 0
      controller/EmbeddedNetworkController.hpp
  8. 0 2
      controller/FileDB.cpp
  9. 0 1
      controller/FileDB.hpp
  10. 0 44
      include/ZeroTierOne.h
  11. 362 0
      node/AES.cpp
  12. 172 0
      node/AES.hpp
  13. 20 11
      node/C25519.cpp
  14. 9 8
      node/C25519.hpp
  15. 18 1
      node/Capability.cpp
  16. 9 22
      node/Capability.hpp
  17. 6 7
      node/CertificateOfMembership.cpp
  18. 21 9
      node/CertificateOfMembership.hpp
  19. 27 1
      node/CertificateOfOwnership.cpp
  20. 10 30
      node/CertificateOfOwnership.hpp
  21. 17 16
      node/Constants.hpp
  22. 1363 0
      node/ECC384.cpp
  23. 108 0
      node/ECC384.hpp
  24. 193 57
      node/Identity.cpp
  25. 173 113
      node/Identity.hpp
  26. 76 107
      node/IncomingPacket.cpp
  27. 257 0
      node/Locator.hpp
  28. 24 32
      node/Membership.hpp
  29. 1 0
      node/MulticastGroup.hpp
  30. 0 45
      node/Multicaster.cpp
  31. 3 17
      node/Network.cpp
  32. 3 37
      node/NetworkConfig.hpp
  33. 22 80
      node/Node.cpp
  34. 0 7
      node/Node.hpp
  35. 0 10
      node/Packet.cpp
  36. 16 74
      node/Packet.hpp
  37. 0 21
      node/Peer.cpp
  38. 2 1
      node/Peer.hpp
  39. 13 1
      node/Revocation.cpp
  40. 14 24
      node/Revocation.hpp
  41. 189 296
      node/SHA512.cpp
  42. 3 8
      node/SHA512.hpp
  43. 158 0
      node/Str.hpp
  44. 0 7
      node/Switch.cpp
  45. 13 1
      node/Tag.cpp
  46. 13 21
      node/Tag.hpp
  47. 0 26
      node/Topology.cpp
  48. 4 155
      node/Topology.hpp
  49. 160 5
      node/Utils.cpp
  50. 7 4
      node/Utils.hpp
  51. 0 284
      node/World.hpp
  52. 1 0
      objects.mk
  53. 5 172
      one.cpp
  54. 1 0
      osdep/Phy.hpp
  55. 113 23
      selftest.cpp
  56. 6 123
      service/OneService.cpp
  57. 2 2
      service/SoftwareUpdater.cpp

+ 158 - 0
attic/root.cpp

@@ -0,0 +1,158 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2019  ZeroTier, Inc.  https://www.zerotier.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#include "node/Constants.hpp"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/un.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/tcp.h>
+
+#include <string>
+#include <thread>
+#include <map>
+#include <vector>
+#include <iostream>
+
+#include "include/ZeroTierOne.h"
+
+static int bindSocket(struct sockaddr *bindAddr)
+{
+	int s = socket(bindAddr->sa_family,SOCK_DGRAM,0);
+	if (s < 0) {
+		close(s);
+		return -1;
+	}
+
+	int f = 131072;
+	setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char *)&f,sizeof(f));
+	f = 131072;
+	setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char *)&f,sizeof(f));
+
+	if (bindAddr->sa_family == AF_INET6) {
+		f = 1; setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(void *)&f,sizeof(f));
+#ifdef IPV6_MTU_DISCOVER
+		f = 0; setsockopt(s,IPPROTO_IPV6,IPV6_MTU_DISCOVER,&f,sizeof(f));
+#endif
+#ifdef IPV6_DONTFRAG
+		f = 0; setsockopt(s,IPPROTO_IPV6,IPV6_DONTFRAG,&f,sizeof(f));
+#endif
+	}
+	f = 1; setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(void *)&f,sizeof(f));
+	f = 1; setsockopt(s,SOL_SOCKET,SO_REUSEPORT,(void *)&f,sizeof(f));
+	f = 1; setsockopt(s,SOL_SOCKET,SO_BROADCAST,(void *)&f,sizeof(f));
+#ifdef IP_DONTFRAG
+	f = 0; setsockopt(s,IPPROTO_IP,IP_DONTFRAG,&f,sizeof(f));
+#endif
+#ifdef IP_MTU_DISCOVER
+	f = IP_PMTUDISC_DONT; setsockopt(s,IPPROTO_IP,IP_MTU_DISCOVER,&f,sizeof(f));
+#endif
+#ifdef SO_NO_CHECK
+	if (bindAddr->sa_family == AF_INET) {
+		f = 1; setsockopt(s,SOL_SOCKET,SO_NO_CHECK,(void *)&f,sizeof(f));
+	}
+#endif
+
+	if (bind(s,bindAddr,(bindAddr->sa_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6))) {
+		close(s);
+		return -1;
+	}
+
+	return s;
+}
+
+int main(int argc,char **argv)
+{
+	unsigned int ncores = std::thread::hardware_concurrency();
+	if (ncores == 0) ncores = 1;
+
+	std::vector<int> sockets;
+	std::vector<std::thread> threads;
+	for(unsigned int tn=0;tn<ncores;++tn) {
+		struct sockaddr_in6 in6;
+		memset(&in6,0,sizeof(in6));
+		in6.sin6_family = AF_INET6;
+		in6.sin6_port = htons(ZT_DEFAULT_PORT);
+		const int s6 = bindSocket((struct sockaddr *)&in6);
+		if (s6 < 0) {
+			std::cout << "ERROR: unable to bind to port " << ZT_DEFAULT_PORT << ZT_EOL_S;
+			exit(1);
+		}
+
+		struct sockaddr_in in4;
+		memset(&in4,0,sizeof(in4));
+		in4.sin_family = AF_INET;
+		in4.sin_port = htons(ZT_DEFAULT_PORT);
+		const int s4 = bindSocket((struct sockaddr *)&in4);
+		if (s4 < 0) {
+			std::cout << "ERROR: unable to bind to port " << ZT_DEFAULT_PORT << ZT_EOL_S;
+			exit(1);
+		}
+
+		sockets.push_back(s6);
+		sockets.push_back(s4);
+
+		threads.push_back(std::thread([s6]() {
+			struct sockaddr_in6 in6;
+			char buf[10000];
+			memset(&in6,0,sizeof(in6));
+			for(;;) {
+				socklen_t sl = sizeof(in6);
+				const int pl = (int)recvfrom(s6,buf,sizeof(buf),0,(struct sockaddr *)&in6,&sl);
+				if (pl > 0) {
+				} else break;
+			}
+		}));
+
+		threads.push_back(std::thread([s4]() {
+			struct sockaddr_in in4;
+			char buf[10000];
+			memset(&in4,0,sizeof(in4));
+			for(;;) {
+				socklen_t sl = sizeof(in4);
+				const int pl = (int)recvfrom(s4,buf,sizeof(buf),0,(struct sockaddr *)&in4,&sl);
+				if (pl > 0) {
+				} else break;
+			}
+		}));
+	}
+
+	return 0;
+}

+ 0 - 7
attic/world/README.md

@@ -1,7 +0,0 @@
-World Definitions and Generator Code
-======
-
-This little bit of code is used to generate world updates. Ordinary users probably will never need this unless they want to test or experiment.
-
-See mkworld.cpp for documentation. To build from this directory use 'source ./build.sh'.
-

+ 0 - 3
attic/world/build.sh

@@ -1,3 +0,0 @@
-#!/bin/bash
-
-c++ -std=c++11 -I../.. -I.. -O -o mkworld ../../node/C25519.cpp ../../node/Salsa20.cpp ../../node/SHA512.cpp ../../node/Identity.cpp ../../node/Utils.cpp ../../node/InetAddress.cpp ../../osdep/OSUtils.cpp mkworld.cpp -lm

+ 0 - 154
attic/world/mkworld.cpp

@@ -1,154 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2016  ZeroTier, Inc.  https://www.zerotier.com/
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-/*
- * This utility makes the World from the configuration specified below.
- * It probably won't be much use to anyone outside ZeroTier, Inc. except
- * for testing and experimentation purposes.
- *
- * If you want to make your own World you must edit this file.
- *
- * When run, it expects two files in the current directory:
- *
- * previous.c25519 - key pair to sign this world (key from previous world)
- * current.c25519 - key pair whose public key should be embedded in this world
- *
- * If these files do not exist, they are both created with the same key pair
- * and a self-signed initial World is born.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdint.h>
-
-#include <string>
-#include <vector>
-#include <algorithm>
-
-#include <node/Constants.hpp>
-#include <node/World.hpp>
-#include <node/C25519.hpp>
-#include <node/Identity.hpp>
-#include <node/InetAddress.hpp>
-#include <osdep/OSUtils.hpp>
-
-using namespace ZeroTier;
-
-int main(int argc,char **argv)
-{
-	std::string previous,current;
-	if ((!OSUtils::readFile("previous.c25519",previous))||(!OSUtils::readFile("current.c25519",current))) {
-		C25519::Pair np(C25519::generate());
-		previous = std::string();
-		previous.append((const char *)np.pub.data,ZT_C25519_PUBLIC_KEY_LEN);
-		previous.append((const char *)np.priv.data,ZT_C25519_PRIVATE_KEY_LEN);
-		current = previous;
-		OSUtils::writeFile("previous.c25519",previous);
-		OSUtils::writeFile("current.c25519",current);
-		fprintf(stderr,"INFO: created initial world keys: previous.c25519 and current.c25519 (both initially the same)" ZT_EOL_S);
-	}
-
-	if ((previous.length() != (ZT_C25519_PUBLIC_KEY_LEN + ZT_C25519_PRIVATE_KEY_LEN))||(current.length() != (ZT_C25519_PUBLIC_KEY_LEN + ZT_C25519_PRIVATE_KEY_LEN))) {
-		fprintf(stderr,"FATAL: previous.c25519 or current.c25519 empty or invalid" ZT_EOL_S);
-		return 1;
-	}
-	C25519::Pair previousKP;
-	memcpy(previousKP.pub.data,previous.data(),ZT_C25519_PUBLIC_KEY_LEN);
-	memcpy(previousKP.priv.data,previous.data() + ZT_C25519_PUBLIC_KEY_LEN,ZT_C25519_PRIVATE_KEY_LEN);
-	C25519::Pair currentKP;
-	memcpy(currentKP.pub.data,current.data(),ZT_C25519_PUBLIC_KEY_LEN);
-	memcpy(currentKP.priv.data,current.data() + ZT_C25519_PUBLIC_KEY_LEN,ZT_C25519_PRIVATE_KEY_LEN);
-
-	// =========================================================================
-	// EDIT BELOW HERE
-
-	std::vector<World::Root> roots;
-
-	const uint64_t id = ZT_WORLD_ID_EARTH;
-	const uint64_t ts = 1562631342273ULL; // July 8th, 2019
-
-	roots.push_back(World::Root());
-	roots.back().identity = Identity("3a46f1bf30:0:76e66fab33e28549a62ee2064d1843273c2c300ba45c3f20bef02dbad225723bb59a9bb4b13535730961aeecf5a163ace477cceb0727025b99ac14a5166a09a3");
-	roots.back().stableEndpoints.push_back(InetAddress("185.180.13.82/9993"));
-	roots.back().stableEndpoints.push_back(InetAddress("2a02:6ea0:c815::/9993"));
-
-	// Alice
-	roots.push_back(World::Root());
-	roots.back().identity = Identity("9d219039f3:0:01f0922a98e3b34ebcbff333269dc265d7a020aab69d72be4d4acc9c8c9294785771256cd1d942a90d1bd1d2dca3ea84ef7d85afe6611fb43ff0b74126d90a6e");
-	roots.back().stableEndpoints.push_back(InetAddress("188.166.94.177/9993")); // Amsterdam
-	roots.back().stableEndpoints.push_back(InetAddress("2a03:b0c0:2:d0::7d:1/9993")); // Amsterdam
-	roots.back().stableEndpoints.push_back(InetAddress("154.66.197.33/9993")); // Johannesburg
-	roots.back().stableEndpoints.push_back(InetAddress("2c0f:f850:154:197::33/9993")); // Johannesburg
-	roots.back().stableEndpoints.push_back(InetAddress("159.203.97.171/9993")); // New York
-	roots.back().stableEndpoints.push_back(InetAddress("2604:a880:800:a1::54:6001/9993")); // New York
-	roots.back().stableEndpoints.push_back(InetAddress("131.255.6.16/9993")); // Buenos Aires
-	roots.back().stableEndpoints.push_back(InetAddress("2803:eb80:0:e::2/9993")); // Buenos Aires
-	roots.back().stableEndpoints.push_back(InetAddress("107.170.197.14/9993")); // San Francisco
-	roots.back().stableEndpoints.push_back(InetAddress("2604:a880:1:20::200:e001/9993")); // San Francisco
-	roots.back().stableEndpoints.push_back(InetAddress("128.199.197.217/9993")); // Singapore
-	roots.back().stableEndpoints.push_back(InetAddress("2400:6180:0:d0::b7:4001/9993")); // Singapore
-
-	// Bob
-	roots.push_back(World::Root());
-	roots.back().identity = Identity("8841408a2e:0:bb1d31f2c323e264e9e64172c1a74f77899555ed10751cd56e86405cde118d02dffe555d462ccf6a85b5631c12350c8d5dc409ba10b9025d0f445cf449d92b1c");
-	roots.back().stableEndpoints.push_back(InetAddress("45.32.198.130/9993")); // Dallas
-	roots.back().stableEndpoints.push_back(InetAddress("2001:19f0:6400:81c3:5400:00ff:fe18:1d61/9993")); // Dallas
-	roots.back().stableEndpoints.push_back(InetAddress("46.101.160.249/9993")); // Frankfurt
-	roots.back().stableEndpoints.push_back(InetAddress("2a03:b0c0:3:d0::6a:3001/9993")); // Frankfurt
-	roots.back().stableEndpoints.push_back(InetAddress("107.191.46.210/9993")); // Paris
-	roots.back().stableEndpoints.push_back(InetAddress("2001:19f0:6800:83a4::64/9993")); // Paris
-	roots.back().stableEndpoints.push_back(InetAddress("45.32.246.179/9993")); // Sydney
-	roots.back().stableEndpoints.push_back(InetAddress("2001:19f0:5800:8bf8:5400:ff:fe15:b39a/9993")); // Sydney
-	roots.back().stableEndpoints.push_back(InetAddress("45.32.248.87/9993")); // Tokyo
-	roots.back().stableEndpoints.push_back(InetAddress("2001:19f0:7000:9bc9:5400:00ff:fe15:c4f5/9993")); // Tokyo
-	roots.back().stableEndpoints.push_back(InetAddress("159.203.2.154/9993")); // Toronto
-	roots.back().stableEndpoints.push_back(InetAddress("2604:a880:cad:d0::26:7001/9993")); // Toronto
-
-	// END WORLD DEFINITION
-	// =========================================================================
-
-	fprintf(stderr,"INFO: generating and signing id==%llu ts==%llu" ZT_EOL_S,(unsigned long long)id,(unsigned long long)ts);
-
-	World nw = World::make(World::TYPE_PLANET,id,ts,currentKP.pub,roots,previousKP);
-
-	Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> outtmp;
-	nw.serialize(outtmp,false);
-	World testw;
-	testw.deserialize(outtmp,0);
-	if (testw != nw) {
-		fprintf(stderr,"FATAL: serialization test failed!" ZT_EOL_S);
-		return 1;
-	}
-
-	OSUtils::writeFile("world.bin",std::string((const char *)outtmp.data(),outtmp.size()));
-	fprintf(stderr,"INFO: world.bin written with %u bytes of binary world data." ZT_EOL_S,outtmp.size());
-
-	fprintf(stdout,ZT_EOL_S);
-	fprintf(stdout,"#define ZT_DEFAULT_WORLD_LENGTH %u" ZT_EOL_S,outtmp.size());
-	fprintf(stdout,"static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {");
-	for(unsigned int i=0;i<outtmp.size();++i) {
-		const unsigned char *d = (const unsigned char *)outtmp.data();
-		if (i > 0)
-			fprintf(stdout,",");
-		fprintf(stdout,"0x%.2x",(unsigned int)d[i]);
-	}
-	fprintf(stdout,"};" ZT_EOL_S);
-
-	return 0;
-}

BIN
attic/world/world.bin


File diff suppressed because it is too large
+ 0 - 2
attic/world/world.c


+ 1 - 0
controller/EmbeddedNetworkController.hpp

@@ -64,6 +64,7 @@ class EmbeddedNetworkController : public NetworkController,public DB::ChangeList
 public:
 	/**
 	 * @param node Parent node
+	 * @param ztPath ZeroTier base path
 	 * @param dbPath Database path (file path or database credentials)
 	 */
 	EmbeddedNetworkController(Node *node,const char *ztPath,const char *dbPath, int listenPort, MQConfig *mqc = NULL);

+ 0 - 2
controller/FileDB.cpp

@@ -33,13 +33,11 @@ FileDB::FileDB(const char *path) :
 	DB(),
 	_path(path),
 	_networksPath(_path + ZT_PATH_SEPARATOR_S + "network"),
-	_tracePath(_path + ZT_PATH_SEPARATOR_S + "trace"),
 	_running(true)
 {
 	OSUtils::mkdir(_path.c_str());
 	OSUtils::lockDownFile(_path.c_str(),true);
 	OSUtils::mkdir(_networksPath.c_str());
-	OSUtils::mkdir(_tracePath.c_str());
 
 	std::vector<std::string> networks(OSUtils::listDirectory(_networksPath.c_str(),false));
 	std::string buf;

+ 0 - 1
controller/FileDB.hpp

@@ -48,7 +48,6 @@ public:
 protected:
 	std::string _path;
 	std::string _networksPath;
-	std::string _tracePath;
 	std::thread _onlineUpdateThread;
 	std::map< uint64_t,std::map<uint64_t,std::map<int64_t,InetAddress> > > _online;
 	std::mutex _online_l;

+ 0 - 44
include/ZeroTierOne.h

@@ -1390,24 +1390,6 @@ enum ZT_StateObjectType
 	 */
 	ZT_STATE_OBJECT_IDENTITY_SECRET = 2,
 
-	/**
-	 * The planet (there is only one per... well... planet!)
-	 *
-	 * Object ID: world ID of planet, or 0 if unknown (first query)
-	 * Canonical path: <HOME>/planet
-	 * Persistence: recommended
-	 */
-	ZT_STATE_OBJECT_PLANET = 3,
-
-	/**
-	 * A moon (federated root set)
-	 *
-	 * Object ID: world ID of moon
-	 * Canonical path: <HOME>/moons.d/<ID>.moon (16-digit hex ID)
-	 * Persistence: required if moon memberships should persist
-	 */
-	ZT_STATE_OBJECT_MOON = 4,
-
 	/**
 	 * Peer and related state
 	 *
@@ -1844,32 +1826,6 @@ ZT_SDK_API enum ZT_ResultCode ZT_Node_multicastSubscribe(ZT_Node *node,void *tpt
  */
 ZT_SDK_API enum ZT_ResultCode ZT_Node_multicastUnsubscribe(ZT_Node *node,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
 
-/**
- * Add or update a moon
- *
- * Moons are persisted in the data store in moons.d/, so this can persist
- * across invocations if the contents of moon.d are scanned and orbit is
- * called for each on startup.
- *
- * @param node Node instance
- * @param tptr Thread pointer to pass to functions/callbacks resulting from this call
- * @param moonWorldId Moon's world ID
- * @param moonSeed If non-zero, the ZeroTier address of any member of the moon to query for moon definition
- * @param len Length of moonWorld in bytes
- * @return Error if moon was invalid or failed to be added
- */
-ZT_SDK_API enum ZT_ResultCode ZT_Node_orbit(ZT_Node *node,void *tptr,uint64_t moonWorldId,uint64_t moonSeed);
-
-/**
- * Remove a moon (does nothing if not present)
- *
- * @param node Node instance
- * @param tptr Thread pointer to pass to functions/callbacks resulting from this call
- * @param moonWorldId World ID of moon to remove
- * @return Error if anything bad happened
- */
-ZT_SDK_API enum ZT_ResultCode ZT_Node_deorbit(ZT_Node *node,void *tptr,uint64_t moonWorldId);
-
 /**
  * Get this node's 40-bit ZeroTier address
  *

+ 362 - 0
node/AES.cpp

@@ -0,0 +1,362 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2019  ZeroTier, Inc.  https://www.zerotier.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#include "AES.hpp"
+
+namespace ZeroTier {
+
+#if (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64))
+
+#include <wmmintrin.h>
+#include <emmintrin.h>
+#include <smmintrin.h>
+static inline bool _zt_aesni_supported()
+{
+	uint32_t eax,ebx,ecx,edx;
+	__asm__ __volatile__ (
+		"cpuid"
+		: "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx)
+		: "a"(1), "c"(0)
+	);
+	return ((ecx & (1 << 25)) != 0);
+}
+const bool AES::HW_ACCEL = _zt_aesni_supported();
+
+#else
+
+const bool AES::HW_ACCEL = false;
+
+#endif
+
+namespace {
+
+#ifdef ZT_NO_TYPE_PUNNING
+static inline uint32_t GETU32(const void *in)
+{
+	uint32_t v = ((const uint8_t *)in)[0];
+	v <<= 8;
+	v |= ((const uint8_t *)in)[1];
+	v <<= 8;
+	v |= ((const uint8_t *)in)[2];
+	v <<= 8;
+	v |= ((const uint8_t *)in)[3];
+	return v;
+}
+static inline void PUTU32(void *out,const uint32_t v)
+{
+	((uint8_t *)out)[0] = (uint8_t)(v >> 24);
+	((uint8_t *)out)[1] = (uint8_t)(v >> 16);
+	((uint8_t *)out)[2] = (uint8_t)(v >> 8);
+	((uint8_t *)out)[3] = (uint8_t)v;
+}
+#else
+#define GETU32(i) (Utils::ntoh(*((const uint32_t *)(i))))
+#define PUTU32(o,v) (*((uint32_t *)(o)) = Utils::hton(v))
+#endif
+
+static const uint32_t Te0[256] = {
+	0xc66363a5,0xf87c7c84,0xee777799,0xf67b7b8d,0xfff2f20d,
+	0xd66b6bbd,0xde6f6fb1,0x91c5c554,0x60303050,0x02010103,
+	0xce6767a9,0x562b2b7d,0xe7fefe19,0xb5d7d762,0x4dababe6,
+	0xec76769a,0x8fcaca45,0x1f82829d,0x89c9c940,0xfa7d7d87,
+	0xeffafa15,0xb25959eb,0x8e4747c9,0xfbf0f00b,0x41adadec,
+	0xb3d4d467,0x5fa2a2fd,0x45afafea,0x239c9cbf,0x53a4a4f7,
+	0xe4727296,0x9bc0c05b,0x75b7b7c2,0xe1fdfd1c,0x3d9393ae,
+	0x4c26266a,0x6c36365a,0x7e3f3f41,0xf5f7f702,0x83cccc4f,
+	0x6834345c,0x51a5a5f4,0xd1e5e534,0xf9f1f108,0xe2717193,
+	0xabd8d873,0x62313153,0x2a15153f,0x0804040c,0x95c7c752,
+	0x46232365,0x9dc3c35e,0x30181828,0x379696a1,0x0a05050f,
+	0x2f9a9ab5,0x0e070709,0x24121236,0x1b80809b,0xdfe2e23d,
+	0xcdebeb26,0x4e272769,0x7fb2b2cd,0xea75759f,0x1209091b,
+	0x1d83839e,0x582c2c74,0x341a1a2e,0x361b1b2d,0xdc6e6eb2,
+	0xb45a5aee,0x5ba0a0fb,0xa45252f6,0x763b3b4d,0xb7d6d661,
+	0x7db3b3ce,0x5229297b,0xdde3e33e,0x5e2f2f71,0x13848497,
+	0xa65353f5,0xb9d1d168,0x00000000,0xc1eded2c,0x40202060,
+	0xe3fcfc1f,0x79b1b1c8,0xb65b5bed,0xd46a6abe,0x8dcbcb46,
+	0x67bebed9,0x7239394b,0x944a4ade,0x984c4cd4,0xb05858e8,
+	0x85cfcf4a,0xbbd0d06b,0xc5efef2a,0x4faaaae5,0xedfbfb16,
+	0x864343c5,0x9a4d4dd7,0x66333355,0x11858594,0x8a4545cf,
+	0xe9f9f910,0x04020206,0xfe7f7f81,0xa05050f0,0x783c3c44,
+	0x259f9fba,0x4ba8a8e3,0xa25151f3,0x5da3a3fe,0x804040c0,
+	0x058f8f8a,0x3f9292ad,0x219d9dbc,0x70383848,0xf1f5f504,
+	0x63bcbcdf,0x77b6b6c1,0xafdada75,0x42212163,0x20101030,
+	0xe5ffff1a,0xfdf3f30e,0xbfd2d26d,0x81cdcd4c,0x180c0c14,
+	0x26131335,0xc3ecec2f,0xbe5f5fe1,0x359797a2,0x884444cc,
+	0x2e171739,0x93c4c457,0x55a7a7f2,0xfc7e7e82,0x7a3d3d47,
+	0xc86464ac,0xba5d5de7,0x3219192b,0xe6737395,0xc06060a0,
+	0x19818198,0x9e4f4fd1,0xa3dcdc7f,0x44222266,0x542a2a7e,
+	0x3b9090ab,0x0b888883,0x8c4646ca,0xc7eeee29,0x6bb8b8d3,
+	0x2814143c,0xa7dede79,0xbc5e5ee2,0x160b0b1d,0xaddbdb76,
+	0xdbe0e03b,0x64323256,0x743a3a4e,0x140a0a1e,0x924949db,
+	0x0c06060a,0x4824246c,0xb85c5ce4,0x9fc2c25d,0xbdd3d36e,
+	0x43acacef,0xc46262a6,0x399191a8,0x319595a4,0xd3e4e437,
+	0xf279798b,0xd5e7e732,0x8bc8c843,0x6e373759,0xda6d6db7,
+	0x018d8d8c,0xb1d5d564,0x9c4e4ed2,0x49a9a9e0,0xd86c6cb4,
+	0xac5656fa,0xf3f4f407,0xcfeaea25,0xca6565af,0xf47a7a8e,
+	0x47aeaee9,0x10080818,0x6fbabad5,0xf0787888,0x4a25256f,
+	0x5c2e2e72,0x381c1c24,0x57a6a6f1,0x73b4b4c7,0x97c6c651,
+	0xcbe8e823,0xa1dddd7c,0xe874749c,0x3e1f1f21,0x964b4bdd,
+	0x61bdbddc,0x0d8b8b86,0x0f8a8a85,0xe0707090,0x7c3e3e42,
+	0x71b5b5c4,0xcc6666aa,0x904848d8,0x06030305,0xf7f6f601,
+	0x1c0e0e12,0xc26161a3,0x6a35355f,0xae5757f9,0x69b9b9d0,
+	0x17868691,0x99c1c158,0x3a1d1d27,0x279e9eb9,0xd9e1e138,
+	0xebf8f813,0x2b9898b3,0x22111133,0xd26969bb,0xa9d9d970,
+	0x078e8e89,0x339494a7,0x2d9b9bb6,0x3c1e1e22,0x15878792,
+	0xc9e9e920,0x87cece49,0xaa5555ff,0x50282878,0xa5dfdf7a,
+	0x038c8c8f,0x59a1a1f8,0x09898980,0x1a0d0d17,0x65bfbfda,
+	0xd7e6e631,0x844242c6,0xd06868b8,0x824141c3,0x299999b0,
+	0x5a2d2d77,0x1e0f0f11,0x7bb0b0cb,0xa85454fc,0x6dbbbbd6,
+	0x2c16163a
+};
+static const uint32_t Te1[256] = {
+	0xa5c66363,0x84f87c7c,0x99ee7777,0x8df67b7b,0x0dfff2f2,
+	0xbdd66b6b,0xb1de6f6f,0x5491c5c5,0x50603030,0x03020101,
+	0xa9ce6767,0x7d562b2b,0x19e7fefe,0x62b5d7d7,0xe64dabab,
+	0x9aec7676,0x458fcaca,0x9d1f8282,0x4089c9c9,0x87fa7d7d,
+	0x15effafa,0xebb25959,0xc98e4747,0x0bfbf0f0,0xec41adad,
+	0x67b3d4d4,0xfd5fa2a2,0xea45afaf,0xbf239c9c,0xf753a4a4,
+	0x96e47272,0x5b9bc0c0,0xc275b7b7,0x1ce1fdfd,0xae3d9393,
+	0x6a4c2626,0x5a6c3636,0x417e3f3f,0x02f5f7f7,0x4f83cccc,
+	0x5c683434,0xf451a5a5,0x34d1e5e5,0x08f9f1f1,0x93e27171,
+	0x73abd8d8,0x53623131,0x3f2a1515,0x0c080404,0x5295c7c7,
+	0x65462323,0x5e9dc3c3,0x28301818,0xa1379696,0x0f0a0505,
+	0xb52f9a9a,0x090e0707,0x36241212,0x9b1b8080,0x3ddfe2e2,
+	0x26cdebeb,0x694e2727,0xcd7fb2b2,0x9fea7575,0x1b120909,
+	0x9e1d8383,0x74582c2c,0x2e341a1a,0x2d361b1b,0xb2dc6e6e,
+	0xeeb45a5a,0xfb5ba0a0,0xf6a45252,0x4d763b3b,0x61b7d6d6,
+	0xce7db3b3,0x7b522929,0x3edde3e3,0x715e2f2f,0x97138484,
+	0xf5a65353,0x68b9d1d1,0x00000000,0x2cc1eded,0x60402020,
+	0x1fe3fcfc,0xc879b1b1,0xedb65b5b,0xbed46a6a,0x468dcbcb,
+	0xd967bebe,0x4b723939,0xde944a4a,0xd4984c4c,0xe8b05858,
+	0x4a85cfcf,0x6bbbd0d0,0x2ac5efef,0xe54faaaa,0x16edfbfb,
+	0xc5864343,0xd79a4d4d,0x55663333,0x94118585,0xcf8a4545,
+	0x10e9f9f9,0x06040202,0x81fe7f7f,0xf0a05050,0x44783c3c,
+	0xba259f9f,0xe34ba8a8,0xf3a25151,0xfe5da3a3,0xc0804040,
+	0x8a058f8f,0xad3f9292,0xbc219d9d,0x48703838,0x04f1f5f5,
+	0xdf63bcbc,0xc177b6b6,0x75afdada,0x63422121,0x30201010,
+	0x1ae5ffff,0x0efdf3f3,0x6dbfd2d2,0x4c81cdcd,0x14180c0c,
+	0x35261313,0x2fc3ecec,0xe1be5f5f,0xa2359797,0xcc884444,
+	0x392e1717,0x5793c4c4,0xf255a7a7,0x82fc7e7e,0x477a3d3d,
+	0xacc86464,0xe7ba5d5d,0x2b321919,0x95e67373,0xa0c06060,
+	0x98198181,0xd19e4f4f,0x7fa3dcdc,0x66442222,0x7e542a2a,
+	0xab3b9090,0x830b8888,0xca8c4646,0x29c7eeee,0xd36bb8b8,
+	0x3c281414,0x79a7dede,0xe2bc5e5e,0x1d160b0b,0x76addbdb,
+	0x3bdbe0e0,0x56643232,0x4e743a3a,0x1e140a0a,0xdb924949,
+	0x0a0c0606,0x6c482424,0xe4b85c5c,0x5d9fc2c2,0x6ebdd3d3,
+	0xef43acac,0xa6c46262,0xa8399191,0xa4319595,0x37d3e4e4,
+	0x8bf27979,0x32d5e7e7,0x438bc8c8,0x596e3737,0xb7da6d6d,
+	0x8c018d8d,0x64b1d5d5,0xd29c4e4e,0xe049a9a9,0xb4d86c6c,
+	0xfaac5656,0x07f3f4f4,0x25cfeaea,0xafca6565,0x8ef47a7a,
+	0xe947aeae,0x18100808,0xd56fbaba,0x88f07878,0x6f4a2525,
+	0x725c2e2e,0x24381c1c,0xf157a6a6,0xc773b4b4,0x5197c6c6,
+	0x23cbe8e8,0x7ca1dddd,0x9ce87474,0x213e1f1f,0xdd964b4b,
+	0xdc61bdbd,0x860d8b8b,0x850f8a8a,0x90e07070,0x427c3e3e,
+	0xc471b5b5,0xaacc6666,0xd8904848,0x05060303,0x01f7f6f6,
+	0x121c0e0e,0xa3c26161,0x5f6a3535,0xf9ae5757,0xd069b9b9,
+	0x91178686,0x5899c1c1,0x273a1d1d,0xb9279e9e,0x38d9e1e1,
+	0x13ebf8f8,0xb32b9898,0x33221111,0xbbd26969,0x70a9d9d9,
+	0x89078e8e,0xa7339494,0xb62d9b9b,0x223c1e1e,0x92158787,
+	0x20c9e9e9,0x4987cece,0xffaa5555,0x78502828,0x7aa5dfdf,
+	0x8f038c8c,0xf859a1a1,0x80098989,0x171a0d0d,0xda65bfbf,
+	0x31d7e6e6,0xc6844242,0xb8d06868,0xc3824141,0xb0299999,
+	0x775a2d2d,0x111e0f0f,0xcb7bb0b0,0xfca85454,0xd66dbbbb,
+	0x3a2c1616
+};
+static const uint32_t Te2[256] = {
+	0x63a5c663,0x7c84f87c,0x7799ee77,0x7b8df67b,0xf20dfff2,
+	0x6bbdd66b,0x6fb1de6f,0xc55491c5,0x30506030,0x01030201,
+	0x67a9ce67,0x2b7d562b,0xfe19e7fe,0xd762b5d7,0xabe64dab,
+	0x769aec76,0xca458fca,0x829d1f82,0xc94089c9,0x7d87fa7d,
+	0xfa15effa,0x59ebb259,0x47c98e47,0xf00bfbf0,0xadec41ad,
+	0xd467b3d4,0xa2fd5fa2,0xafea45af,0x9cbf239c,0xa4f753a4,
+	0x7296e472,0xc05b9bc0,0xb7c275b7,0xfd1ce1fd,0x93ae3d93,
+	0x266a4c26,0x365a6c36,0x3f417e3f,0xf702f5f7,0xcc4f83cc,
+	0x345c6834,0xa5f451a5,0xe534d1e5,0xf108f9f1,0x7193e271,
+	0xd873abd8,0x31536231,0x153f2a15,0x040c0804,0xc75295c7,
+	0x23654623,0xc35e9dc3,0x18283018,0x96a13796,0x050f0a05,
+	0x9ab52f9a,0x07090e07,0x12362412,0x809b1b80,0xe23ddfe2,
+	0xeb26cdeb,0x27694e27,0xb2cd7fb2,0x759fea75,0x091b1209,
+	0x839e1d83,0x2c74582c,0x1a2e341a,0x1b2d361b,0x6eb2dc6e,
+	0x5aeeb45a,0xa0fb5ba0,0x52f6a452,0x3b4d763b,0xd661b7d6,
+	0xb3ce7db3,0x297b5229,0xe33edde3,0x2f715e2f,0x84971384,
+	0x53f5a653,0xd168b9d1,0x00000000,0xed2cc1ed,0x20604020,
+	0xfc1fe3fc,0xb1c879b1,0x5bedb65b,0x6abed46a,0xcb468dcb,
+	0xbed967be,0x394b7239,0x4ade944a,0x4cd4984c,0x58e8b058,
+	0xcf4a85cf,0xd06bbbd0,0xef2ac5ef,0xaae54faa,0xfb16edfb,
+	0x43c58643,0x4dd79a4d,0x33556633,0x85941185,0x45cf8a45,
+	0xf910e9f9,0x02060402,0x7f81fe7f,0x50f0a050,0x3c44783c,
+	0x9fba259f,0xa8e34ba8,0x51f3a251,0xa3fe5da3,0x40c08040,
+	0x8f8a058f,0x92ad3f92,0x9dbc219d,0x38487038,0xf504f1f5,
+	0xbcdf63bc,0xb6c177b6,0xda75afda,0x21634221,0x10302010,
+	0xff1ae5ff,0xf30efdf3,0xd26dbfd2,0xcd4c81cd,0x0c14180c,
+	0x13352613,0xec2fc3ec,0x5fe1be5f,0x97a23597,0x44cc8844,
+	0x17392e17,0xc45793c4,0xa7f255a7,0x7e82fc7e,0x3d477a3d,
+	0x64acc864,0x5de7ba5d,0x192b3219,0x7395e673,0x60a0c060,
+	0x81981981,0x4fd19e4f,0xdc7fa3dc,0x22664422,0x2a7e542a,
+	0x90ab3b90,0x88830b88,0x46ca8c46,0xee29c7ee,0xb8d36bb8,
+	0x143c2814,0xde79a7de,0x5ee2bc5e,0x0b1d160b,0xdb76addb,
+	0xe03bdbe0,0x32566432,0x3a4e743a,0x0a1e140a,0x49db9249,
+	0x060a0c06,0x246c4824,0x5ce4b85c,0xc25d9fc2,0xd36ebdd3,
+	0xacef43ac,0x62a6c462,0x91a83991,0x95a43195,0xe437d3e4,
+	0x798bf279,0xe732d5e7,0xc8438bc8,0x37596e37,0x6db7da6d,
+	0x8d8c018d,0xd564b1d5,0x4ed29c4e,0xa9e049a9,0x6cb4d86c,
+	0x56faac56,0xf407f3f4,0xea25cfea,0x65afca65,0x7a8ef47a,
+	0xaee947ae,0x08181008,0xbad56fba,0x7888f078,0x256f4a25,
+	0x2e725c2e,0x1c24381c,0xa6f157a6,0xb4c773b4,0xc65197c6,
+	0xe823cbe8,0xdd7ca1dd,0x749ce874,0x1f213e1f,0x4bdd964b,
+	0xbddc61bd,0x8b860d8b,0x8a850f8a,0x7090e070,0x3e427c3e,
+	0xb5c471b5,0x66aacc66,0x48d89048,0x03050603,0xf601f7f6,
+	0x0e121c0e,0x61a3c261,0x355f6a35,0x57f9ae57,0xb9d069b9,
+	0x86911786,0xc15899c1,0x1d273a1d,0x9eb9279e,0xe138d9e1,
+	0xf813ebf8,0x98b32b98,0x11332211,0x69bbd269,0xd970a9d9,
+	0x8e89078e,0x94a73394,0x9bb62d9b,0x1e223c1e,0x87921587,
+	0xe920c9e9,0xce4987ce,0x55ffaa55,0x28785028,0xdf7aa5df,
+	0x8c8f038c,0xa1f859a1,0x89800989,0x0d171a0d,0xbfda65bf,
+	0xe631d7e6,0x42c68442,0x68b8d068,0x41c38241,0x99b02999,
+	0x2d775a2d,0x0f111e0f,0xb0cb7bb0,0x54fca854,0xbbd66dbb,
+	0x163a2c16
+};
+static const uint32_t Te3[256] = {
+	0x6363a5c6,0x7c7c84f8,0x777799ee,0x7b7b8df6,0xf2f20dff,
+	0x6b6bbdd6,0x6f6fb1de,0xc5c55491,0x30305060,0x01010302,
+	0x6767a9ce,0x2b2b7d56,0xfefe19e7,0xd7d762b5,0xababe64d,
+	0x76769aec,0xcaca458f,0x82829d1f,0xc9c94089,0x7d7d87fa,
+	0xfafa15ef,0x5959ebb2,0x4747c98e,0xf0f00bfb,0xadadec41,
+	0xd4d467b3,0xa2a2fd5f,0xafafea45,0x9c9cbf23,0xa4a4f753,
+	0x727296e4,0xc0c05b9b,0xb7b7c275,0xfdfd1ce1,0x9393ae3d,
+	0x26266a4c,0x36365a6c,0x3f3f417e,0xf7f702f5,0xcccc4f83,
+	0x34345c68,0xa5a5f451,0xe5e534d1,0xf1f108f9,0x717193e2,
+	0xd8d873ab,0x31315362,0x15153f2a,0x04040c08,0xc7c75295,
+	0x23236546,0xc3c35e9d,0x18182830,0x9696a137,0x05050f0a,
+	0x9a9ab52f,0x0707090e,0x12123624,0x80809b1b,0xe2e23ddf,
+	0xebeb26cd,0x2727694e,0xb2b2cd7f,0x75759fea,0x09091b12,
+	0x83839e1d,0x2c2c7458,0x1a1a2e34,0x1b1b2d36,0x6e6eb2dc,
+	0x5a5aeeb4,0xa0a0fb5b,0x5252f6a4,0x3b3b4d76,0xd6d661b7,
+	0xb3b3ce7d,0x29297b52,0xe3e33edd,0x2f2f715e,0x84849713,
+	0x5353f5a6,0xd1d168b9,0x00000000,0xeded2cc1,0x20206040,
+	0xfcfc1fe3,0xb1b1c879,0x5b5bedb6,0x6a6abed4,0xcbcb468d,
+	0xbebed967,0x39394b72,0x4a4ade94,0x4c4cd498,0x5858e8b0,
+	0xcfcf4a85,0xd0d06bbb,0xefef2ac5,0xaaaae54f,0xfbfb16ed,
+	0x4343c586,0x4d4dd79a,0x33335566,0x85859411,0x4545cf8a,
+	0xf9f910e9,0x02020604,0x7f7f81fe,0x5050f0a0,0x3c3c4478,
+	0x9f9fba25,0xa8a8e34b,0x5151f3a2,0xa3a3fe5d,0x4040c080,
+	0x8f8f8a05,0x9292ad3f,0x9d9dbc21,0x38384870,0xf5f504f1,
+	0xbcbcdf63,0xb6b6c177,0xdada75af,0x21216342,0x10103020,
+	0xffff1ae5,0xf3f30efd,0xd2d26dbf,0xcdcd4c81,0x0c0c1418,
+	0x13133526,0xecec2fc3,0x5f5fe1be,0x9797a235,0x4444cc88,
+	0x1717392e,0xc4c45793,0xa7a7f255,0x7e7e82fc,0x3d3d477a,
+	0x6464acc8,0x5d5de7ba,0x19192b32,0x737395e6,0x6060a0c0,
+	0x81819819,0x4f4fd19e,0xdcdc7fa3,0x22226644,0x2a2a7e54,
+	0x9090ab3b,0x8888830b,0x4646ca8c,0xeeee29c7,0xb8b8d36b,
+	0x14143c28,0xdede79a7,0x5e5ee2bc,0x0b0b1d16,0xdbdb76ad,
+	0xe0e03bdb,0x32325664,0x3a3a4e74,0x0a0a1e14,0x4949db92,
+	0x06060a0c,0x24246c48,0x5c5ce4b8,0xc2c25d9f,0xd3d36ebd,
+	0xacacef43,0x6262a6c4,0x9191a839,0x9595a431,0xe4e437d3,
+	0x79798bf2,0xe7e732d5,0xc8c8438b,0x3737596e,0x6d6db7da,
+	0x8d8d8c01,0xd5d564b1,0x4e4ed29c,0xa9a9e049,0x6c6cb4d8,
+	0x5656faac,0xf4f407f3,0xeaea25cf,0x6565afca,0x7a7a8ef4,
+	0xaeaee947,0x08081810,0xbabad56f,0x787888f0,0x25256f4a,
+	0x2e2e725c,0x1c1c2438,0xa6a6f157,0xb4b4c773,0xc6c65197,
+	0xe8e823cb,0xdddd7ca1,0x74749ce8,0x1f1f213e,0x4b4bdd96,
+	0xbdbddc61,0x8b8b860d,0x8a8a850f,0x707090e0,0x3e3e427c,
+	0xb5b5c471,0x6666aacc,0x4848d890,0x03030506,0xf6f601f7,
+	0x0e0e121c,0x6161a3c2,0x35355f6a,0x5757f9ae,0xb9b9d069,
+	0x86869117,0xc1c15899,0x1d1d273a,0x9e9eb927,0xe1e138d9,
+	0xf8f813eb,0x9898b32b,0x11113322,0x6969bbd2,0xd9d970a9,
+	0x8e8e8907,0x9494a733,0x9b9bb62d,0x1e1e223c,0x87879215,
+	0xe9e920c9,0xcece4987,0x5555ffaa,0x28287850,0xdfdf7aa5,
+	0x8c8c8f03,0xa1a1f859,0x89898009,0x0d0d171a,0xbfbfda65,
+	0xe6e631d7,0x4242c684,0x6868b8d0,0x4141c382,0x9999b029,
+	0x2d2d775a,0x0f0f111e,0xb0b0cb7b,0x5454fca8,0xbbbbd66d,
+	0x16163a2c
+};
+static const uint32_t rcon[] = {
+	0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
+	0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000,
+};
+
+} // anonymous namespace
+
+void AES::_initSW(const uint8_t key[32])
+{
+	uint32_t *rk = _k.sw;
+	rk[0] = GETU32(key);
+	rk[1] = GETU32(key + 4);
+	rk[2] = GETU32(key + 8);
+	rk[3] = GETU32(key + 12);
+	rk[4] = GETU32(key + 16);
+	rk[5] = GETU32(key + 20);
+	rk[6] = GETU32(key + 24);
+	rk[7] = GETU32(key + 28);
+	for(int i=0;;) {
+		uint32_t temp = rk[7];
+		rk[8] = rk[0] ^ (Te2[(temp >> 16) & 0xff] & 0xff000000) ^ (Te3[(temp >> 8) & 0xff] & 0x00ff0000) ^ (Te0[(temp) & 0xff] & 0x0000ff00) ^ (Te1[(temp >> 24)] & 0x000000ff) ^ rcon[i];
+		rk[9] = rk[1] ^ rk[8];
+		rk[10] = rk[2] ^ rk[9];
+		rk[11] = rk[3] ^ rk[10];
+		if (++i == 7)
+			break;
+		temp = rk[11];
+		rk[12] = rk[4] ^ (Te2[(temp >> 24)] & 0xff000000) ^ (Te3[(temp >> 16) & 0xff] & 0x00ff0000) ^ (Te0[(temp >> 8) & 0xff] & 0x0000ff00) ^ (Te1[(temp) & 0xff] & 0x000000ff);
+		rk[13] = rk[5] ^ rk[12];
+		rk[14] = rk[6] ^ rk[13];
+		rk[15] = rk[7] ^ rk[14];
+		rk += 8;
+	}
+}
+
+void AES::_encryptSW(const uint8_t in[16],uint8_t out[16]) const
+{
+	const uint32_t *rk = _k.sw;
+	uint32_t s0, s1, s2, s3, t0, t1, t2, t3;
+	s0 = GETU32(in) ^ rk[0];
+	s1 = GETU32(in + 4) ^ rk[1];
+	s2 = GETU32(in + 8) ^ rk[2];
+	s3 = GETU32(in + 12) ^ rk[3];
+	for (int r=(14>>1);;) {
+		t0 = Te0[(s0 >> 24)] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[(s3) & 0xff] ^ rk[4];
+		t1 = Te0[(s1 >> 24)] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[(s0) & 0xff] ^ rk[5];
+		t2 = Te0[(s2 >> 24)] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[(s1) & 0xff] ^ rk[6];
+		t3 = Te0[(s3 >> 24)] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[(s2) & 0xff] ^ rk[7];
+		rk += 8;
+		if (--r == 0)
+			break;
+		s0 = Te0[(t0 >> 24)] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[(t3) & 0xff] ^ rk[0];
+		s1 = Te0[(t1 >> 24)] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[(t0) & 0xff] ^ rk[1];
+		s2 = Te0[(t2 >> 24)] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[(t1) & 0xff] ^ rk[2];
+		s3 = Te0[(t3 >> 24)] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[(t2) & 0xff] ^ rk[3];
+	}
+	s0 = (Te2[(t0 >> 24)] & 0xff000000) ^ (Te3[(t1 >> 16) & 0xff] & 0x00ff0000) ^ (Te0[(t2 >> 8) & 0xff] & 0x0000ff00) ^ (Te1[(t3) & 0xff] & 0x000000ff) ^ rk[0];
+	PUTU32(out, s0);
+	s1 = (Te2[(t1 >> 24)] & 0xff000000) ^ (Te3[(t2 >> 16) & 0xff] & 0x00ff0000) ^ (Te0[(t3 >> 8) & 0xff] & 0x0000ff00) ^ (Te1[(t0) & 0xff] & 0x000000ff) ^ rk[1];
+	PUTU32(out + 4, s1);
+	s2 = (Te2[(t2 >> 24)] & 0xff000000) ^ (Te3[(t3 >> 16) & 0xff] & 0x00ff0000) ^ (Te0[(t0 >> 8) & 0xff] & 0x0000ff00) ^ (Te1[(t1) & 0xff] & 0x000000ff) ^ rk[2];
+	PUTU32(out + 8, s2);
+	s3 = (Te2[(t3 >> 24)] & 0xff000000) ^ (Te3[(t0 >> 16) & 0xff] & 0x00ff0000) ^ (Te0[(t1 >> 8) & 0xff] & 0x0000ff00) ^ (Te1[(t2) & 0xff] & 0x000000ff) ^ rk[3];
+	PUTU32(out + 12, s3);
+}
+
+} // namespace ZeroTier

+ 172 - 0
node/AES.hpp

@@ -0,0 +1,172 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2019  ZeroTier, Inc.  https://www.zerotier.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#ifndef ZT_AES_HPP
+#define ZT_AES_HPP
+
+#include "Constants.hpp"
+#include "Utils.hpp"
+
+#if (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64))
+#include <wmmintrin.h>
+#include <emmintrin.h>
+#include <smmintrin.h>
+#define ZT_AES_AESNI 1
+#endif
+
+namespace ZeroTier {
+
+/**
+ * AES-256 and GCM AEAD
+ * 
+ * AES with 128-bit or 192-bit key sizes isn't supported here. This also only
+ * supports the encrypt operation since we use AES in GCM mode. For HW acceleration
+ * the code is inlined for maximum performance.
+ */
+class AES
+{
+public:
+	/**
+	 * This will be true if your platform's type of AES acceleration is supported on this machine
+	 */
+	static const bool HW_ACCEL;
+
+	inline AES() {}
+	inline AES(const uint8_t key[32]) { this->init(key); }
+
+	inline ~AES()
+	{
+		Utils::burn(&_k,sizeof(_k)); // ensure that expanded key memory is zeroed on object destruction
+	}
+
+	inline void init(const uint8_t key[32])
+	{
+		if (HW_ACCEL) {
+#ifdef ZT_AES_AESNI
+			_init_aesni(key);
+#endif
+		} else {
+			_initSW(key);
+		}
+	}
+
+	inline void encrypt(const uint8_t in[16],uint8_t out[16]) const
+	{
+		if (HW_ACCEL) {
+#ifdef ZT_AES_AESNI
+			_encrypt_aesni(in,out);
+#endif
+		} else {
+			_encryptSW(in,out);
+		}
+	}
+
+	// These are public so the software mode can always be tested in self-test.
+	// Normally init(), encrypt(), etc. should be used.
+	void _initSW(const uint8_t key[32]);
+	void _encryptSW(const uint8_t in[16],uint8_t out[16]) const;
+
+private:
+#ifdef ZT_AES_AESNI
+	static inline __m128i _init256_1(__m128i a,__m128i b)
+	{
+		__m128i x,y;
+		b = _mm_shuffle_epi32(b,0xff);
+		y = _mm_slli_si128(a,0x04);
+		x = _mm_xor_si128(a,y);
+		y = _mm_slli_si128(y,0x04);
+		x = _mm_xor_si128(x,y);
+		y = _mm_slli_si128(y,0x04);
+		x = _mm_xor_si128(x,y);
+		x = _mm_xor_si128(x,b);
+		return x;
+	}
+	static inline __m128i _init256_2(__m128i a,__m128i b)
+	{
+		__m128i x,y,z;
+		y = _mm_aeskeygenassist_si128(a,0x00);
+		z = _mm_shuffle_epi32(y,0xaa);
+		y = _mm_slli_si128(b,0x04);
+		x = _mm_xor_si128(b,y);
+		y = _mm_slli_si128(y,0x04);
+		x = _mm_xor_si128(x,y);
+		y = _mm_slli_si128(y,0x04);
+		x = _mm_xor_si128(x,y);
+		x = _mm_xor_si128(x,z);
+		return x;
+	}
+	inline void _init_aesni(const uint8_t key[32])
+	{
+		__m128i t1,t2;
+		_k.ni[0] = t1 = _mm_loadu_si128((const __m128i *)key);
+		_k.ni[1] = t2 = _mm_loadu_si128((const __m128i *)(key+16));
+		_k.ni[2] = t1 = _init256_1(t1,_mm_aeskeygenassist_si128(t2,0x01));
+		_k.ni[3] = t2 = _init256_2(t1,t2);
+		_k.ni[4] = t1 = _init256_1(t1,_mm_aeskeygenassist_si128(t2,0x02));
+		_k.ni[5] = t2 = _init256_2(t1,t2);
+		_k.ni[6] = t1 = _init256_1(t1,_mm_aeskeygenassist_si128(t2,0x04));
+		_k.ni[7] = t2 = _init256_2(t1,t2);
+		_k.ni[8] = t1 = _init256_1(t1,_mm_aeskeygenassist_si128(t2,0x08));
+		_k.ni[9] = t2 = _init256_2(t1,t2);
+		_k.ni[10] = t1 = _init256_1(t1,_mm_aeskeygenassist_si128(t2,0x10));
+		_k.ni[11] = t2 = _init256_2(t1,t2);
+		_k.ni[12] = t1 = _init256_1(t1,_mm_aeskeygenassist_si128(t2,0x20));
+		_k.ni[13] = t2 = _init256_2(t1,t2);
+		_k.ni[14] = _init256_1(t1,_mm_aeskeygenassist_si128(t2,0x40));
+	}
+	inline void _encrypt_aesni(const void *in,void *out) const
+	{
+		__m128i tmp;
+		tmp = _mm_loadu_si128((const __m128i *)in);
+		tmp = _mm_xor_si128(tmp,_k.ni[0]);
+		tmp = _mm_aesenc_si128(tmp,_k.ni[1]);
+		tmp = _mm_aesenc_si128(tmp,_k.ni[2]);
+		tmp = _mm_aesenc_si128(tmp,_k.ni[3]);
+		tmp = _mm_aesenc_si128(tmp,_k.ni[4]);
+		tmp = _mm_aesenc_si128(tmp,_k.ni[5]);
+		tmp = _mm_aesenc_si128(tmp,_k.ni[6]);
+		tmp = _mm_aesenc_si128(tmp,_k.ni[7]);
+		tmp = _mm_aesenc_si128(tmp,_k.ni[8]);
+		tmp = _mm_aesenc_si128(tmp,_k.ni[9]);
+		tmp = _mm_aesenc_si128(tmp,_k.ni[10]);
+		tmp = _mm_aesenc_si128(tmp,_k.ni[11]);
+		tmp = _mm_aesenc_si128(tmp,_k.ni[12]);
+		tmp = _mm_aesenc_si128(tmp,_k.ni[13]);
+		_mm_storeu_si128((__m128i *)out,_mm_aesenclast_si128(tmp,_k.ni[14]));
+	}
+#endif
+
+	union {
+#ifdef ZT_AES_AESNI
+		__m128i ni[15]; // AES-NI expanded key
+#endif
+		uint32_t sw[60]; // software mode expanded key
+	} _k;
+};
+
+} // namespace ZeroTier
+
+#endif

+ 20 - 11
node/C25519.cpp

@@ -2439,7 +2439,7 @@ static inline void get_hram(unsigned char *hram, const unsigned char *sm, const
 	for (i = 32;i < 64;++i)    playground[i] = pk[i-32];
 	for (i = 64;i < smlen;++i) playground[i] = sm[i];
 
-	ZeroTier::SHA512::hash(hram,playground,(unsigned int)smlen);
+	ZeroTier::SHA512(hram,playground,(unsigned int)smlen);
 }
 
 //////////////////////////////////////////////////////////////////////////////
@@ -2459,11 +2459,11 @@ void C25519::agree(const C25519::Private &mine,const C25519::Public &their,void
 	unsigned char digest[64];
 
 	crypto_scalarmult(rawkey,mine.data,their.data);
-	SHA512::hash(digest,rawkey,32);
+	SHA512(digest,rawkey,32);
 	for(unsigned int i=0,k=0;i<keylen;) {
 		if (k == 64) {
 			k = 0;
-			SHA512::hash(digest,digest,64);
+			SHA512(digest,digest,64);
 		}
 		((unsigned char *)keybuf)[i++] = digest[k++];
 	}
@@ -2472,7 +2472,7 @@ void C25519::agree(const C25519::Private &mine,const C25519::Public &their,void
 void C25519::sign(const C25519::Private &myPrivate,const C25519::Public &myPublic,const void *msg,unsigned int len,void *signature)
 {
 	unsigned char digest[64]; // we sign the first 32 bytes of SHA-512(msg)
-	SHA512::hash(digest,msg,len);
+	SHA512(digest,msg,len);
 
 #ifdef ZT_USE_FAST_X64_ED25519
 	ed25519_amd64_asm_sign(myPrivate.data + 32,myPublic.data + 32,digest,(unsigned char *)signature);
@@ -2486,7 +2486,7 @@ void C25519::sign(const C25519::Private &myPrivate,const C25519::Public &myPubli
 	unsigned char hram[crypto_hash_sha512_BYTES];
 	unsigned char *sig = (unsigned char *)signature;
 
-	SHA512::hash(extsk,myPrivate.data + 32,32);
+	SHA512(extsk,myPrivate.data + 32,32);
 	extsk[0] &= 248;
 	extsk[31] &= 127;
 	extsk[31] |= 64;
@@ -2496,7 +2496,7 @@ void C25519::sign(const C25519::Private &myPrivate,const C25519::Public &myPubli
 	for(unsigned int i=0;i<32;i++)
 		sig[64 + i] = digest[i];
 
-	SHA512::hash(hmg,sig + 32,64);
+	SHA512(hmg,sig + 32,64);
 
 	/* Computation of R */
 	sc25519_from64bytes(&sck, hmg);
@@ -2521,13 +2521,22 @@ void C25519::sign(const C25519::Private &myPrivate,const C25519::Public &myPubli
 #endif
 }
 
-bool C25519::verify(const C25519::Public &their,const void *msg,unsigned int len,const void *signature)
+bool C25519::verify(const C25519::Public &their,const void *msg,unsigned int len,const void *signature,const unsigned int siglen)
 {
-	const unsigned char *const sig = (const unsigned char *)signature;
+	if (siglen < 64) return false;
+
+	const unsigned char *sig = (const unsigned char *)signature;
 	unsigned char digest[64]; // we sign the first 32 bytes of SHA-512(msg)
-	SHA512::hash(digest,msg,len);
-	if (!Utils::secureEq(sig + 64,digest,32))
+	unsigned char sigtmp[96];
+	SHA512(digest,msg,len);
+
+	if ((siglen == 96)&&(!Utils::secureEq(sig+64,digest,32))) {
 		return false;
+	} else if (siglen == 64) {
+		memcpy(sigtmp,sig,64);
+		memcpy(sigtmp+64,digest,32);
+		sig = sigtmp;
+	}
 
 	unsigned char t2[32];
 	ge25519 get1, get2;
@@ -2565,7 +2574,7 @@ void C25519::_calcPubED(C25519::Pair &kp)
 
 	// Second 32 bytes of pub and priv are the keys for ed25519
 	// signing and verification.
-	SHA512::hash(extsk,kp.priv.data + 32,32);
+	SHA512(extsk,kp.priv.data + 32,32);
 	extsk[0] &= 248;
 	extsk[31] &= 127;
 	extsk[31] |= 64;

+ 9 - 8
node/C25519.hpp

@@ -98,7 +98,6 @@ public:
 	 * @param keylen Number of key bytes to generate
 	 */
 	static void agree(const Private &mine,const Public &their,void *keybuf,unsigned int keylen);
-	static inline void agree(const Pair &mine,const Public &their,void *keybuf,unsigned int keylen) { agree(mine.priv,their,keybuf,keylen); }
 
 	/**
 	 * Sign a message with a sender's key pair
@@ -120,11 +119,15 @@ public:
 	 * @param signature Buffer to fill with signature -- MUST be 96 bytes in length
 	 */
 	static void sign(const Private &myPrivate,const Public &myPublic,const void *msg,unsigned int len,void *signature);
-	static inline void sign(const Pair &mine,const void *msg,unsigned int len,void *signature) { sign(mine.priv,mine.pub,msg,len,signature); }
 
 	/**
 	 * Sign a message with a sender's key pair
 	 *
+	 * Note that this generates a 96-byte signature that contains an extra 32 bytes
+	 * of hash data. This data is included for historical reasons and is optional. The
+	 * verify function here will take the first 64 bytes only (normal ed25519 signature)
+	 * or a 96-byte length signature with the extra input hash data.
+	 * 
 	 * @param myPrivate My private key
 	 * @param myPublic My public key
 	 * @param msg Message to sign
@@ -150,10 +153,11 @@ public:
 	 * @param their Public key to verify against
 	 * @param msg Message to verify signature integrity against
 	 * @param len Length of message in bytes
-	 * @param signature 96-byte signature
+	 * @param signature Signature bytes
+	 * @param siglen Length of signature in bytes
 	 * @return True if signature is valid and the message is authentic and unmodified
 	 */
-	static bool verify(const Public &their,const void *msg,unsigned int len,const void *signature);
+	static bool verify(const Public &their,const void *msg,unsigned int len,const void *signature,const unsigned int siglen);
 
 	/**
 	 * Verify a message's signature
@@ -164,10 +168,7 @@ public:
 	 * @param signature 96-byte signature
 	 * @return True if signature is valid and the message is authentic and unmodified
 	 */
-	static inline bool verify(const Public &their,const void *msg,unsigned int len,const Signature &signature)
-	{
-		return verify(their,msg,len,signature.data);
-	}
+	static inline bool verify(const Public &their,const void *msg,unsigned int len,const Signature &signature) { return verify(their,msg,len,signature.data,96); }
 
 private:
 	// derive first 32 bytes of kp.pub from first 32 bytes of kp.priv

+ 18 - 1
node/Capability.cpp

@@ -34,6 +34,23 @@
 
 namespace ZeroTier {
 
+bool Capability::sign(const Identity &from,const Address &to)
+{
+	try {
+		for(unsigned int i=0;((i<_maxCustodyChainLength)&&(i<ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH));++i) {
+			if (!(_custody[i].to)) {
+				Buffer<(sizeof(Capability) * 2)> tmp;
+				this->serialize(tmp,true);
+				_custody[i].to = to;
+				_custody[i].from = from.address();
+				_custody[i].signatureLength = from.sign(tmp.data(),tmp.size(),_custody[i].signature,sizeof(_custody[i].signature));
+				return true;
+			}
+		}
+	} catch ( ... ) {}
+	return false;
+}
+
 int Capability::verify(const RuntimeEnvironment *RR,void *tPtr) const
 {
 	try {
@@ -57,7 +74,7 @@ int Capability::verify(const RuntimeEnvironment *RR,void *tPtr) const
 
 			const Identity id(RR->topology->getIdentity(tPtr,_custody[c].from));
 			if (id) {
-				if (!id.verify(tmp.data(),tmp.size(),_custody[c].signature))
+				if (!id.verify(tmp.data(),tmp.size(),_custody[c].signature,_custody[c].signatureLength))
 					return -1;
 			} else {
 				RR->sw->requestWhois(tPtr,RR->node->now(),_custody[c].from);

+ 9 - 22
node/Capability.hpp

@@ -154,22 +154,7 @@ public:
 	 * @param to Recipient of this signature
 	 * @return True if signature successful and chain of custody appended
 	 */
-	inline bool sign(const Identity &from,const Address &to)
-	{
-		try {
-			for(unsigned int i=0;((i<_maxCustodyChainLength)&&(i<ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH));++i) {
-				if (!(_custody[i].to)) {
-					Buffer<(sizeof(Capability) * 2)> tmp;
-					this->serialize(tmp,true);
-					_custody[i].to = to;
-					_custody[i].from = from.address();
-					_custody[i].signature = from.sign(tmp.data(),tmp.size());
-					return true;
-				}
-			}
-		} catch ( ... ) {}
-		return false;
-	}
+	bool sign(const Identity &from,const Address &to);
 
 	/**
 	 * Verify this capability's chain of custody and signatures
@@ -409,9 +394,9 @@ public:
 				if ((i < _maxCustodyChainLength)&&(i < ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH)&&(_custody[i].to)) {
 					_custody[i].to.appendTo(b);
 					_custody[i].from.appendTo(b);
-					b.append((uint8_t)1); // 1 == Ed25519 signature
-					b.append((uint16_t)ZT_C25519_SIGNATURE_LEN); // length of signature
-					b.append(_custody[i].signature.data,ZT_C25519_SIGNATURE_LEN);
+					b.append((uint8_t)1);
+					b.append((uint16_t)_custody[i].signatureLength);
+					b.append(_custody[i].signature,_custody[i].signatureLength);
 				} else {
 					b.append((unsigned char)0,ZT_ADDRESS_LENGTH); // zero 'to' terminates chain
 					break;
@@ -454,10 +439,11 @@ public:
 			_custody[i].to = to;
 			_custody[i].from.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
 			if (b[p++] == 1) {
-				if (b.template at<uint16_t>(p) != ZT_C25519_SIGNATURE_LEN)
+				_custody[i].signatureLength = b.template at<uint16_t>(p);
+				if (_custody[i].signatureLength > sizeof(_custody[i].signature))
 					throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
 				p += 2;
-				memcpy(_custody[i].signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
+				memcpy(_custody[i].signature,b.field(p,_custody[i].signatureLength),_custody[i].signatureLength); p += _custody[i].signatureLength;
 			} else {
 				p += 2 + b.template at<uint16_t>(p);
 			}
@@ -489,7 +475,8 @@ private:
 	struct {
 		Address to;
 		Address from;
-		C25519::Signature signature;
+		unsigned int signatureLength;
+		uint8_t signature[ZT_SIGNATURE_BUFFER_SIZE];
 	} _custody[ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH];
 };
 

+ 6 - 7
node/CertificateOfMembership.cpp

@@ -84,7 +84,7 @@ std::string CertificateOfMembership::toString() const
 
 	if (_signedBy) {
 		s.push_back(':');
-		s.append(Utils::hex(_signature.data,ZT_C25519_SIGNATURE_LEN,tmp));
+		s.append(Utils::hex(_signature,_signatureLength,tmp));
 	}
 
 	return s;
@@ -92,9 +92,9 @@ std::string CertificateOfMembership::toString() const
 
 void CertificateOfMembership::fromString(const char *s)
 {
-	_qualifierCount = 0;
 	_signedBy.zero();
-	memset(_signature.data,0,ZT_C25519_SIGNATURE_LEN);
+	_qualifierCount = 0;
+	_signatureLength = 0;
 
 	if (!*s)
 		return;
@@ -145,8 +145,7 @@ void CertificateOfMembership::fromString(const char *s)
 				colonAt = 0;
 				while ((s[colonAt])&&(s[colonAt] != ':')) ++colonAt;
 				if (colonAt) {
-					if (Utils::unhex(s,colonAt,_signature.data,ZT_C25519_SIGNATURE_LEN) != ZT_C25519_SIGNATURE_LEN)
-						_signedBy.zero();
+					_signatureLength = Utils::unhex(s,colonAt,_signature,sizeof(_signature));
 				} else {
 					_signedBy.zero();
 				}
@@ -208,7 +207,7 @@ bool CertificateOfMembership::sign(const Identity &with)
 	}
 
 	try {
-		_signature = with.sign(buf,ptr * sizeof(uint64_t));
+		_signatureLength = with.sign(buf,ptr * sizeof(uint64_t),_signature,sizeof(_signature));
 		_signedBy = with.address();
 		return true;
 	} catch ( ... ) {
@@ -235,7 +234,7 @@ int CertificateOfMembership::verify(const RuntimeEnvironment *RR,void *tPtr) con
 		buf[ptr++] = Utils::hton(_qualifiers[i].value);
 		buf[ptr++] = Utils::hton(_qualifiers[i].maxDelta);
 	}
-	return (id.verify(buf,ptr * sizeof(uint64_t),_signature) ? 0 : -1);
+	return (id.verify(buf,ptr * sizeof(uint64_t),_signature,_signatureLength) ? 0 : -1);
 }
 
 } // namespace ZeroTier

+ 21 - 9
node/CertificateOfMembership.hpp

@@ -113,7 +113,8 @@ public:
 	 * Create an empty certificate of membership
 	 */
 	CertificateOfMembership() :
-		_qualifierCount(0) {}
+		_qualifierCount(0),
+		_signatureLength(0) {}
 
 	/**
 	 * Create from required fields common to all networks
@@ -135,7 +136,7 @@ public:
 		_qualifiers[2].value = issuedTo.toInt();
 		_qualifiers[2].maxDelta = 0xffffffffffffffffULL;
 		_qualifierCount = 3;
-		memset(_signature.data,0,ZT_C25519_SIGNATURE_LEN);
+		_signatureLength = 0;
 	}
 
 	/**
@@ -279,8 +280,13 @@ public:
 			b.append(_qualifiers[i].maxDelta);
 		}
 		_signedBy.appendTo(b);
-		if (_signedBy)
-			b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
+		if ((_signedBy)&&(_signatureLength == 96)) {
+			// UGLY: Ed25519 signatures in ZT are 96 bytes (64 + 32 bytes of hash).
+			// P-384 signatures are also 96 bytes, praise the horned one. That means
+			// we don't need to include a length. If we ever do we will need a new
+			// serialized object version, but only for those with length != 96.
+			b.append(_signature,96);
+		}
 	}
 
 	template<unsigned int C>
@@ -288,8 +294,9 @@ public:
 	{
 		unsigned int p = startAt;
 
-		_qualifierCount = 0;
 		_signedBy.zero();
+		_qualifierCount = 0;
+		_signatureLength = 0;
 
 		if (b[p++] != 1)
 			throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
@@ -316,8 +323,10 @@ public:
 		p += ZT_ADDRESS_LENGTH;
 
 		if (_signedBy) {
-			memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN);
-			p += ZT_C25519_SIGNATURE_LEN;
+			// See "UGLY" comment in serialize()...
+			_signatureLength = 96;
+			memcpy(_signature,b.field(p,96),96);
+			p += 96;
 		}
 
 		return (p - startAt);
@@ -329,13 +338,15 @@ public:
 			return false;
 		if (_qualifierCount != c._qualifierCount)
 			return false;
+		if (_signatureLength != c._signatureLength)
+			return false;
 		for(unsigned int i=0;i<_qualifierCount;++i) {
 			const _Qualifier &a = _qualifiers[i];
 			const _Qualifier &b = c._qualifiers[i];
 			if ((a.id != b.id)||(a.value != b.value)||(a.maxDelta != b.maxDelta))
 				return false;
 		}
-		return (memcmp(_signature.data,c._signature.data,ZT_C25519_SIGNATURE_LEN) == 0);
+		return (memcmp(_signature,c._signature,_signatureLength) == 0);
 	}
 	inline bool operator!=(const CertificateOfMembership &c) const { return (!(*this == c)); }
 
@@ -352,7 +363,8 @@ private:
 	Address _signedBy;
 	_Qualifier _qualifiers[ZT_NETWORK_COM_MAX_QUALIFIERS];
 	unsigned int _qualifierCount;
-	C25519::Signature _signature;
+	unsigned int _signatureLength;
+	uint8_t _signature[ZT_SIGNATURE_BUFFER_SIZE];
 };
 
 } // namespace ZeroTier

+ 27 - 1
node/CertificateOfOwnership.cpp

@@ -34,6 +34,32 @@
 
 namespace ZeroTier {
 
+void CertificateOfOwnership::addThing(const InetAddress &ip)
+{
+	if (_thingCount >= ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS) return;
+	if (ip.ss_family == AF_INET) {
+		_thingTypes[_thingCount] = THING_IPV4_ADDRESS;
+		memcpy(_thingValues[_thingCount],&(reinterpret_cast<const struct sockaddr_in *>(&ip)->sin_addr.s_addr),4);
+		++_thingCount;
+	} else if (ip.ss_family == AF_INET6) {
+		_thingTypes[_thingCount] = THING_IPV6_ADDRESS;
+		memcpy(_thingValues[_thingCount],reinterpret_cast<const struct sockaddr_in6 *>(&ip)->sin6_addr.s6_addr,16);
+		++_thingCount;
+	}
+}
+
+bool CertificateOfOwnership::sign(const Identity &signer)
+{
+	if (signer.hasPrivate()) {
+		Buffer<sizeof(CertificateOfOwnership) + 64> tmp;
+		_signedBy = signer.address();
+		this->serialize(tmp,true);
+		_signatureLength = signer.sign(tmp.data(),tmp.size(),_signature,sizeof(_signature));
+		return true;
+	}
+	return false;
+}
+
 int CertificateOfOwnership::verify(const RuntimeEnvironment *RR,void *tPtr) const
 {
 	if ((!_signedBy)||(_signedBy != Network::controllerFor(_networkId)))
@@ -46,7 +72,7 @@ int CertificateOfOwnership::verify(const RuntimeEnvironment *RR,void *tPtr) cons
 	try {
 		Buffer<(sizeof(CertificateOfOwnership) + 64)> tmp;
 		this->serialize(tmp,true);
-		return (id.verify(tmp.data(),tmp.size(),_signature) ? 0 : -1);
+		return (id.verify(tmp.data(),tmp.size(),_signature,_signatureLength) ? 0 : -1);
 	} catch ( ... ) {
 		return -1;
 	}

+ 10 - 30
node/CertificateOfOwnership.hpp

@@ -107,19 +107,7 @@ public:
 		return this->_owns(THING_MAC_ADDRESS,tmp,6);
 	}
 
-	inline void addThing(const InetAddress &ip)
-	{
-		if (_thingCount >= ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS) return;
-		if (ip.ss_family == AF_INET) {
-			_thingTypes[_thingCount] = THING_IPV4_ADDRESS;
-			memcpy(_thingValues[_thingCount],&(reinterpret_cast<const struct sockaddr_in *>(&ip)->sin_addr.s_addr),4);
-			++_thingCount;
-		} else if (ip.ss_family == AF_INET6) {
-			_thingTypes[_thingCount] = THING_IPV6_ADDRESS;
-			memcpy(_thingValues[_thingCount],reinterpret_cast<const struct sockaddr_in6 *>(&ip)->sin6_addr.s6_addr,16);
-			++_thingCount;
-		}
-	}
+	void addThing(const InetAddress &ip);
 
 	inline void addThing(const MAC &mac)
 	{
@@ -133,17 +121,7 @@ public:
 	 * @param signer Signing identity, must have private key
 	 * @return True if signature was successful
 	 */
-	inline bool sign(const Identity &signer)
-	{
-		if (signer.hasPrivate()) {
-			Buffer<sizeof(CertificateOfOwnership) + 64> tmp;
-			_signedBy = signer.address();
-			this->serialize(tmp,true);
-			_signature = signer.sign(tmp.data(),tmp.size());
-			return true;
-		}
-		return false;
-	}
+	bool sign(const Identity &signer);
 
 	/**
 	 * @param RR Runtime environment to allow identity lookup for signedBy
@@ -170,9 +148,9 @@ public:
 		_issuedTo.appendTo(b);
 		_signedBy.appendTo(b);
 		if (!forSign) {
-			b.append((uint8_t)1); // 1 == Ed25519
-			b.append((uint16_t)ZT_C25519_SIGNATURE_LEN); // length of signature
-			b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
+			b.append((uint8_t)1);
+			b.append((uint16_t)_signatureLength); // length of signature
+			b.append(_signature,_signatureLength);
 		}
 
 		b.append((uint16_t)0); // length of additional fields, currently 0
@@ -203,10 +181,11 @@ public:
 		_issuedTo.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
 		_signedBy.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
 		if (b[p++] == 1) {
-			if (b.template at<uint16_t>(p) != ZT_C25519_SIGNATURE_LEN)
+			_signatureLength = b.template at<uint16_t>(p);
+			if (_signatureLength > sizeof(_signature))
 				throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
 			p += 2;
-			memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
+			memcpy(_signature,b.field(p,_signatureLength),_signatureLength); p += _signatureLength;
 		} else {
 			p += 2 + b.template at<uint16_t>(p);
 		}
@@ -236,7 +215,8 @@ private:
 	uint8_t _thingValues[ZT_CERTIFICATEOFOWNERSHIP_MAX_THINGS][ZT_CERTIFICATEOFOWNERSHIP_MAX_THING_VALUE_SIZE];
 	Address _issuedTo;
 	Address _signedBy;
-	C25519::Signature _signature;
+	unsigned int _signatureLength;
+	uint8_t _signature[ZT_SIGNATURE_BUFFER_SIZE];
 };
 
 } // namespace ZeroTier

+ 17 - 16
node/Constants.hpp

@@ -60,8 +60,6 @@
 #endif
 
 #ifdef __APPLE__
-#define likely(x) __builtin_expect((x),1)
-#define unlikely(x) __builtin_expect((x),0)
 #include <TargetConditionals.h>
 #ifndef __UNIX_LIKE__
 #define __UNIX_LIKE__
@@ -79,7 +77,7 @@
 #ifndef __BSD__
 #define __BSD__
 #endif
-#include <machine/endian.h>
+#include <sys/endian.h>
 #ifndef __BYTE_ORDER
 #define __BYTE_ORDER _BYTE_ORDER
 #define __LITTLE_ENDIAN _LITTLE_ENDIAN
@@ -109,14 +107,14 @@
 #endif
 #endif
 
-// Define ZT_NO_TYPE_PUNNING to disable reckless casts on anything other than x86/x64.
+// Define ZT_NO_TYPE_PUNNING to disable reckless casts on anything other than x86 and x86_64.
 #if (!(defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_AMD64) || defined(_M_X64) || defined(i386) || defined(__i386) || defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(_M_IX86) || defined(__X86__) || defined(_X86_) || defined(__I86__) || defined(__INTEL__) || defined(__386)))
 #ifndef ZT_NO_TYPE_PUNNING
 #define ZT_NO_TYPE_PUNNING
 #endif
 #endif
 
-// Assume little endian if not defined
+// Assume little endian if not defined on Mac and Windows as these don't run on any BE architectures.
 #if (defined(__APPLE__) || defined(__WINDOWS__)) && (!defined(__BYTE_ORDER))
 #undef __BYTE_ORDER
 #undef __LITTLE_ENDIAN
@@ -156,7 +154,7 @@
 #endif
 #endif
 
-#ifdef __WINDOWS__
+#if defined(__WINDOWS__) && !defined(__GNUC__) && !defined (__clang__) && !defined(__INTEL_COMPILER)
 #define ZT_PACKED_STRUCT(D) __pragma(pack(push,1)) D __pragma(pack(pop))
 #else
 #define ZT_PACKED_STRUCT(D) D __attribute__((packed))
@@ -178,7 +176,7 @@
 #define ZT_ADDRESS_RESERVED_PREFIX 0xff
 
 /**
- * Default MTU used for Ethernet tap device
+ * Default virtual network MTU (not physical)
  */
 #define ZT_DEFAULT_MTU 2800
 
@@ -188,17 +186,17 @@
 #define ZT_MAX_PACKET_FRAGMENTS 7
 
 /**
- * Size of RX queue
+ * Size of RX queue in packets
  */
 #define ZT_RX_QUEUE_SIZE 32
 
 /**
- * Size of TX queue
+ * Size of TX queue in packets
  */
 #define ZT_TX_QUEUE_SIZE 32
 
 /**
- * Length of secret key in bytes -- 256-bit -- do not change
+ * Length of peer shared secrets (256-bit, do not change)
  */
 #define ZT_PEER_SECRET_KEY_LENGTH 32
 
@@ -232,7 +230,7 @@
  *
  * The protocol allows up to 7, but we limit it to something smaller.
  */
-#define ZT_RELAY_MAX_HOPS 3
+#define ZT_RELAY_MAX_HOPS 4
 
 /**
  * Expire time for multicast 'likes' and indirect multicast memberships in ms
@@ -261,11 +259,6 @@
  */
 #define ZT_PING_CHECK_INVERVAL 5000
 
-/**
- * How often the local.conf file is checked for changes (service, should be moved there)
- */
-#define ZT_LOCAL_CONF_FILE_CHECK_INTERVAL 10000
-
 /**
  * How frequently to check for changes to the system's network interfaces. When
  * the service decides to use this constant it's because we want to react more
@@ -615,6 +608,11 @@
  */
 #define ZT_SUPPORT_OLD_STYLE_NETCONF 1
 
+/**
+ * Size of a buffer to store either a C25519 or an ECC P-384 signature
+ */
+#define ZT_SIGNATURE_BUFFER_SIZE 96
+
 /**
  * Desired buffer size for UDP sockets (used in service and osdep but defined here)
  */
@@ -629,6 +627,9 @@
  */
 #define ZT_THREAD_MIN_STACK_SIZE 1048576
 
+#define ZT_CRYPTO_ALG_C25519 0
+#define ZT_CRYPTO_ALG_P384 1
+
 // Exceptions thrown in core ZT code
 #define ZT_EXCEPTION_OUT_OF_BOUNDS 100
 #define ZT_EXCEPTION_OUT_OF_MEMORY 101

+ 1363 - 0
node/ECC384.cpp

@@ -0,0 +1,1363 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2019  ZeroTier, Inc.  https://www.zerotier.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "Constants.hpp"
+#include "ECC384.hpp"
+#include "Utils.hpp"
+
+namespace ZeroTier {
+
+namespace {
+//////////////////////////////////////////////////////////////////////////////
+// This is EASY-ECC by Kenneth MacKay
+// https://github.com/esxgx/easy-ecc
+// This code is under the BSD 2-clause license, not ZeroTier's license
+//////////////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////////////////////////////
+// ecc.h from easy-ecc
+//////////////////////////////////////////////////////////////////////////////
+
+#define secp128r1 16
+#define secp192r1 24
+#define secp256r1 32
+#define secp384r1 48
+
+//#ifndef ECC_CURVE
+//	#define ECC_CURVE secp256r1
+//#endif
+#define ECC_CURVE secp384r1
+
+//#if (ECC_CURVE != secp128r1 && ECC_CURVE != secp192r1 && ECC_CURVE != secp256r1 && ECC_CURVE != secp384r1)
+//	#error "Must define ECC_CURVE to one of the available curves"
+//#endif
+
+#define ECC_BYTES ECC_CURVE
+
+//////////////////////////////////////////////////////////////////////////////
+// ecc.c from easy-ecc
+//////////////////////////////////////////////////////////////////////////////
+
+//#include "ecc.h"
+//#include <string.h>
+
+#define NUM_ECC_DIGITS (ECC_BYTES/8)
+#define MAX_TRIES 1024
+
+typedef unsigned int uint;
+
+#if defined(__SIZEOF_INT128__) || ((__clang_major__ * 100 + __clang_minor__) >= 302)
+	#define SUPPORTS_INT128 1
+#else
+	#define SUPPORTS_INT128 0
+#endif
+
+#if SUPPORTS_INT128
+typedef unsigned __int128 uint128_t;
+#else
+typedef struct
+{
+	uint64_t m_low;
+	uint64_t m_high;
+} uint128_t;
+#endif
+
+typedef struct EccPoint
+{
+	uint64_t x[NUM_ECC_DIGITS];
+	uint64_t y[NUM_ECC_DIGITS];
+} EccPoint;
+
+#define CONCAT1(a, b) a##b
+#define CONCAT(a, b) CONCAT1(a, b)
+
+#define Curve_P_16 {0xFFFFFFFFFFFFFFFF, 0xFFFFFFFDFFFFFFFF}
+#define Curve_P_24 {0xFFFFFFFFFFFFFFFFull, 0xFFFFFFFFFFFFFFFEull, 0xFFFFFFFFFFFFFFFFull}
+#define Curve_P_32 {0xFFFFFFFFFFFFFFFFull, 0x00000000FFFFFFFFull, 0x0000000000000000ull, 0xFFFFFFFF00000001ull}
+#define Curve_P_48 {0x00000000FFFFFFFF, 0xFFFFFFFF00000000, 0xFFFFFFFFFFFFFFFE, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}
+
+#define Curve_B_16 {0xD824993C2CEE5ED3, 0xE87579C11079F43D}
+#define Curve_B_24 {0xFEB8DEECC146B9B1ull, 0x0FA7E9AB72243049ull, 0x64210519E59C80E7ull}
+#define Curve_B_32 {0x3BCE3C3E27D2604Bull, 0x651D06B0CC53B0F6ull, 0xB3EBBD55769886BCull, 0x5AC635D8AA3A93E7ull}
+#define Curve_B_48 {0x2A85C8EDD3EC2AEF, 0xC656398D8A2ED19D, 0x0314088F5013875A, 0x181D9C6EFE814112, 0x988E056BE3F82D19, 0xB3312FA7E23EE7E4}
+
+#define Curve_G_16 { \
+	{0x0C28607CA52C5B86, 0x161FF7528B899B2D}, \
+	{0xC02DA292DDED7A83, 0xCF5AC8395BAFEB13}}
+
+#define Curve_G_24 { \
+	{0xF4FF0AFD82FF1012ull, 0x7CBF20EB43A18800ull, 0x188DA80EB03090F6ull}, \
+	{0x73F977A11E794811ull, 0x631011ED6B24CDD5ull, 0x07192B95FFC8DA78ull}}
+	
+#define Curve_G_32 { \
+	{0xF4A13945D898C296ull, 0x77037D812DEB33A0ull, 0xF8BCE6E563A440F2ull, 0x6B17D1F2E12C4247ull}, \
+	{0xCBB6406837BF51F5ull, 0x2BCE33576B315ECEull, 0x8EE7EB4A7C0F9E16ull, 0x4FE342E2FE1A7F9Bull}}
+
+#define Curve_G_48 { \
+	{0x3A545E3872760AB7, 0x5502F25DBF55296C, 0x59F741E082542A38, 0x6E1D3B628BA79B98, 0x8EB1C71EF320AD74, 0xAA87CA22BE8B0537}, \
+	{0x7A431D7C90EA0E5F, 0x0A60B1CE1D7E819D, 0xE9DA3113B5F0B8C0, 0xF8F41DBD289A147C, 0x5D9E98BF9292DC29, 0x3617DE4A96262C6F}}
+
+#define Curve_N_16 {0x75A30D1B9038A115, 0xFFFFFFFE00000000}
+#define Curve_N_24 {0x146BC9B1B4D22831ull, 0xFFFFFFFF99DEF836ull, 0xFFFFFFFFFFFFFFFFull}
+#define Curve_N_32 {0xF3B9CAC2FC632551ull, 0xBCE6FAADA7179E84ull, 0xFFFFFFFFFFFFFFFFull, 0xFFFFFFFF00000000ull}
+#define Curve_N_48 {0xECEC196ACCC52973, 0x581A0DB248B0A77A, 0xC7634D81F4372DDF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}
+
+static uint64_t curve_p[NUM_ECC_DIGITS] = CONCAT(Curve_P_, ECC_CURVE);
+static uint64_t curve_b[NUM_ECC_DIGITS] = CONCAT(Curve_B_, ECC_CURVE);
+static EccPoint curve_G = CONCAT(Curve_G_, ECC_CURVE);
+static uint64_t curve_n[NUM_ECC_DIGITS] = CONCAT(Curve_N_, ECC_CURVE);
+
+// Use ZeroTier's secure PRNG
+static inline int getRandomNumber(uint64_t *p_vli)
+{
+	Utils::getSecureRandom(p_vli,ECC_BYTES);
+	return 1;
+}
+
+static inline void vli_clear(uint64_t *p_vli)
+{
+	uint i;
+	for(i=0; i<NUM_ECC_DIGITS; ++i)
+	{
+		p_vli[i] = 0;
+	}
+}
+
+/* Returns 1 if p_vli == 0, 0 otherwise. */
+static inline int vli_isZero(uint64_t *p_vli)
+{
+	uint i;
+	for(i = 0; i < NUM_ECC_DIGITS; ++i)
+	{
+		if(p_vli[i])
+		{
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/* Returns nonzero if bit p_bit of p_vli is set. */
+static inline uint64_t vli_testBit(uint64_t *p_vli, uint p_bit)
+{
+	return (p_vli[p_bit/64] & ((uint64_t)1 << (p_bit % 64)));
+}
+
+/* Counts the number of 64-bit "digits" in p_vli. */
+static inline uint vli_numDigits(uint64_t *p_vli)
+{
+	int i;
+	/* Search from the end until we find a non-zero digit.
+	   We do it in reverse because we expect that most digits will be nonzero. */
+	for(i = NUM_ECC_DIGITS - 1; i >= 0 && p_vli[i] == 0; --i)
+	{
+	}
+
+	return (i + 1);
+}
+
+/* Counts the number of bits required for p_vli. */
+static inline uint vli_numBits(uint64_t *p_vli)
+{
+	uint i;
+	uint64_t l_digit;
+	
+	uint l_numDigits = vli_numDigits(p_vli);
+	if(l_numDigits == 0)
+	{
+		return 0;
+	}
+
+	l_digit = p_vli[l_numDigits - 1];
+	for(i=0; l_digit; ++i)
+	{
+		l_digit >>= 1;
+	}
+	
+	return ((l_numDigits - 1) * 64 + i);
+}
+
+/* Sets p_dest = p_src. */
+static inline void vli_set(uint64_t *p_dest, uint64_t *p_src)
+{
+	uint i;
+	for(i=0; i<NUM_ECC_DIGITS; ++i)
+	{
+		p_dest[i] = p_src[i];
+	}
+}
+
+/* Returns sign of p_left - p_right. */
+static inline int vli_cmp(uint64_t *p_left, uint64_t *p_right)
+{
+	int i;
+	for(i = NUM_ECC_DIGITS-1; i >= 0; --i)
+	{
+		if(p_left[i] > p_right[i])
+		{
+			return 1;
+		}
+		else if(p_left[i] < p_right[i])
+		{
+			return -1;
+		}
+	}
+	return 0;
+}
+
+/* Computes p_result = p_in << c, returning carry. Can modify in place (if p_result == p_in). 0 < p_shift < 64. */
+static inline uint64_t vli_lshift(uint64_t *p_result, uint64_t *p_in, uint p_shift)
+{
+	uint64_t l_carry = 0;
+	uint i;
+	for(i = 0; i < NUM_ECC_DIGITS; ++i)
+	{
+		uint64_t l_temp = p_in[i];
+		p_result[i] = (l_temp << p_shift) | l_carry;
+		l_carry = l_temp >> (64 - p_shift);
+	}
+	
+	return l_carry;
+}
+
+/* Computes p_vli = p_vli >> 1. */
+static inline void vli_rshift1(uint64_t *p_vli)
+{
+	uint64_t *l_end = p_vli;
+	uint64_t l_carry = 0;
+	
+	p_vli += NUM_ECC_DIGITS;
+	while(p_vli-- > l_end)
+	{
+		uint64_t l_temp = *p_vli;
+		*p_vli = (l_temp >> 1) | l_carry;
+		l_carry = l_temp << 63;
+	}
+}
+
+/* Computes p_result = p_left + p_right, returning carry. Can modify in place. */
+static inline uint64_t vli_add(uint64_t *p_result, uint64_t *p_left, uint64_t *p_right)
+{
+	uint64_t l_carry = 0;
+	uint i;
+	for(i=0; i<NUM_ECC_DIGITS; ++i)
+	{
+		uint64_t l_sum = p_left[i] + p_right[i] + l_carry;
+		if(l_sum != p_left[i])
+		{
+			l_carry = (l_sum < p_left[i]);
+		}
+		p_result[i] = l_sum;
+	}
+	return l_carry;
+}
+
+/* Computes p_result = p_left - p_right, returning borrow. Can modify in place. */
+static inline uint64_t vli_sub(uint64_t *p_result, uint64_t *p_left, uint64_t *p_right)
+{
+	uint64_t l_borrow = 0;
+	uint i;
+	for(i=0; i<NUM_ECC_DIGITS; ++i)
+	{
+		uint64_t l_diff = p_left[i] - p_right[i] - l_borrow;
+		if(l_diff != p_left[i])
+		{
+			l_borrow = (l_diff > p_left[i]);
+		}
+		p_result[i] = l_diff;
+	}
+	return l_borrow;
+}
+
+#if SUPPORTS_INT128
+
+/* Computes p_result = p_left * p_right. */
+static inline void vli_mult(uint64_t *p_result, uint64_t *p_left, uint64_t *p_right)
+{
+	uint128_t r01 = 0;
+	uint64_t r2 = 0;
+	
+	uint i, k;
+	
+	/* Compute each digit of p_result in sequence, maintaining the carries. */
+	for(k=0; k < NUM_ECC_DIGITS*2 - 1; ++k)
+	{
+		uint l_min = (k < NUM_ECC_DIGITS ? 0 : (k + 1) - NUM_ECC_DIGITS);
+		for(i=l_min; i<=k && i<NUM_ECC_DIGITS; ++i)
+		{
+			uint128_t l_product = (uint128_t)p_left[i] * p_right[k-i];
+			r01 += l_product;
+			r2 += (r01 < l_product);
+		}
+		p_result[k] = (uint64_t)r01;
+		r01 = (r01 >> 64) | (((uint128_t)r2) << 64);
+		r2 = 0;
+	}
+	
+	p_result[NUM_ECC_DIGITS*2 - 1] = (uint64_t)r01;
+}
+
+/* Computes p_result = p_left^2. */
+static inline void vli_square(uint64_t *p_result, uint64_t *p_left)
+{
+	uint128_t r01 = 0;
+	uint64_t r2 = 0;
+	
+	uint i, k;
+	for(k=0; k < NUM_ECC_DIGITS*2 - 1; ++k)
+	{
+		uint l_min = (k < NUM_ECC_DIGITS ? 0 : (k + 1) - NUM_ECC_DIGITS);
+		for(i=l_min; i<=k && i<=k-i; ++i)
+		{
+			uint128_t l_product = (uint128_t)p_left[i] * p_left[k-i];
+			if(i < k-i)
+			{
+				r2 += l_product >> 127;
+				l_product *= 2;
+			}
+			r01 += l_product;
+			r2 += (r01 < l_product);
+		}
+		p_result[k] = (uint64_t)r01;
+		r01 = (r01 >> 64) | (((uint128_t)r2) << 64);
+		r2 = 0;
+	}
+	
+	p_result[NUM_ECC_DIGITS*2 - 1] = (uint64_t)r01;
+}
+
+#else /* #if SUPPORTS_INT128 */
+
+static inline uint128_t mul_64_64(uint64_t p_left, uint64_t p_right)
+{
+	uint128_t l_result;
+	
+	uint64_t a0 = p_left & 0xffffffffull;
+	uint64_t a1 = p_left >> 32;
+	uint64_t b0 = p_right & 0xffffffffull;
+	uint64_t b1 = p_right >> 32;
+	
+	uint64_t m0 = a0 * b0;
+	uint64_t m1 = a0 * b1;
+	uint64_t m2 = a1 * b0;
+	uint64_t m3 = a1 * b1;
+	
+	m2 += (m0 >> 32);
+	m2 += m1;
+	if(m2 < m1)
+	{ // overflow
+		m3 += 0x100000000ull;
+	}
+	
+	l_result.m_low = (m0 & 0xffffffffull) | (m2 << 32);
+	l_result.m_high = m3 + (m2 >> 32);
+	
+	return l_result;
+}
+
+static inline uint128_t add_128_128(uint128_t a, uint128_t b)
+{
+	uint128_t l_result;
+	l_result.m_low = a.m_low + b.m_low;
+	l_result.m_high = a.m_high + b.m_high + (l_result.m_low < a.m_low);
+	return l_result;
+}
+
+static inline void vli_mult(uint64_t *p_result, uint64_t *p_left, uint64_t *p_right)
+{
+	uint128_t r01 = {0, 0};
+	uint64_t r2 = 0;
+	
+	uint i, k;
+	
+	/* Compute each digit of p_result in sequence, maintaining the carries. */
+	for(k=0; k < NUM_ECC_DIGITS*2 - 1; ++k)
+	{
+		uint l_min = (k < NUM_ECC_DIGITS ? 0 : (k + 1) - NUM_ECC_DIGITS);
+		for(i=l_min; i<=k && i<NUM_ECC_DIGITS; ++i)
+		{
+			uint128_t l_product = mul_64_64(p_left[i], p_right[k-i]);
+			r01 = add_128_128(r01, l_product);
+			r2 += (r01.m_high < l_product.m_high);
+		}
+		p_result[k] = r01.m_low;
+		r01.m_low = r01.m_high;
+		r01.m_high = r2;
+		r2 = 0;
+	}
+	
+	p_result[NUM_ECC_DIGITS*2 - 1] = r01.m_low;
+}
+
+static inline void vli_square(uint64_t *p_result, uint64_t *p_left)
+{
+	uint128_t r01 = {0, 0};
+	uint64_t r2 = 0;
+	
+	uint i, k;
+	for(k=0; k < NUM_ECC_DIGITS*2 - 1; ++k)
+	{
+		uint l_min = (k < NUM_ECC_DIGITS ? 0 : (k + 1) - NUM_ECC_DIGITS);
+		for(i=l_min; i<=k && i<=k-i; ++i)
+		{
+			uint128_t l_product = mul_64_64(p_left[i], p_left[k-i]);
+			if(i < k-i)
+			{
+				r2 += l_product.m_high >> 63;
+				l_product.m_high = (l_product.m_high << 1) | (l_product.m_low >> 63);
+				l_product.m_low <<= 1;
+			}
+			r01 = add_128_128(r01, l_product);
+			r2 += (r01.m_high < l_product.m_high);
+		}
+		p_result[k] = r01.m_low;
+		r01.m_low = r01.m_high;
+		r01.m_high = r2;
+		r2 = 0;
+	}
+	
+	p_result[NUM_ECC_DIGITS*2 - 1] = r01.m_low;
+}
+
+#endif /* SUPPORTS_INT128 */
+
+/* Computes p_result = (p_left + p_right) % p_mod.
+   Assumes that p_left < p_mod and p_right < p_mod, p_result != p_mod. */
+static inline void vli_modAdd(uint64_t *p_result, uint64_t *p_left, uint64_t *p_right, uint64_t *p_mod)
+{
+	uint64_t l_carry = vli_add(p_result, p_left, p_right);
+	if(l_carry || vli_cmp(p_result, p_mod) >= 0)
+	{ /* p_result > p_mod (p_result = p_mod + remainder), so subtract p_mod to get remainder. */
+		vli_sub(p_result, p_result, p_mod);
+	}
+}
+
+/* Computes p_result = (p_left - p_right) % p_mod.
+   Assumes that p_left < p_mod and p_right < p_mod, p_result != p_mod. */
+static inline void vli_modSub(uint64_t *p_result, uint64_t *p_left, uint64_t *p_right, uint64_t *p_mod)
+{
+	uint64_t l_borrow = vli_sub(p_result, p_left, p_right);
+	if(l_borrow)
+	{ /* In this case, p_result == -diff == (max int) - diff.
+		 Since -x % d == d - x, we can get the correct result from p_result + p_mod (with overflow). */
+		vli_add(p_result, p_result, p_mod);
+	}
+}
+
+#if ECC_CURVE == secp128r1
+
+/* Computes p_result = p_product % curve_p.
+   See algorithm 5 and 6 from http://www.isys.uni-klu.ac.at/PDF/2001-0126-MT.pdf */
+static void vli_mmod_fast(uint64_t *p_result, uint64_t *p_product)
+{
+	uint64_t l_tmp[NUM_ECC_DIGITS];
+	int l_carry;
+	
+	vli_set(p_result, p_product);
+	
+	l_tmp[0] = p_product[2];
+	l_tmp[1] = (p_product[3] & 0x1FFFFFFFFull) | (p_product[2] << 33);
+	l_carry = vli_add(p_result, p_result, l_tmp);
+	
+	l_tmp[0] = (p_product[2] >> 31) | (p_product[3] << 33);
+	l_tmp[1] = (p_product[3] >> 31) | ((p_product[2] & 0xFFFFFFFF80000000ull) << 2);
+	l_carry += vli_add(p_result, p_result, l_tmp);
+	
+	l_tmp[0] = (p_product[2] >> 62) | (p_product[3] << 2);
+	l_tmp[1] = (p_product[3] >> 62) | ((p_product[2] & 0xC000000000000000ull) >> 29) | (p_product[3] << 35);
+	l_carry += vli_add(p_result, p_result, l_tmp);
+	
+	l_tmp[0] = (p_product[3] >> 29);
+	l_tmp[1] = ((p_product[3] & 0xFFFFFFFFE0000000ull) << 4);
+	l_carry += vli_add(p_result, p_result, l_tmp);
+	
+	l_tmp[0] = (p_product[3] >> 60);
+	l_tmp[1] = (p_product[3] & 0xFFFFFFFE00000000ull);
+	l_carry += vli_add(p_result, p_result, l_tmp);
+	
+	l_tmp[0] = 0;
+	l_tmp[1] = ((p_product[3] & 0xF000000000000000ull) >> 27);
+	l_carry += vli_add(p_result, p_result, l_tmp);
+	
+	while(l_carry || vli_cmp(curve_p, p_result) != 1)
+	{
+		l_carry -= vli_sub(p_result, p_result, curve_p);
+	}
+}
+
+#elif ECC_CURVE == secp192r1
+
+/* Computes p_result = p_product % curve_p.
+   See algorithm 5 and 6 from http://www.isys.uni-klu.ac.at/PDF/2001-0126-MT.pdf */
+static void vli_mmod_fast(uint64_t *p_result, uint64_t *p_product)
+{
+	uint64_t l_tmp[NUM_ECC_DIGITS];
+	int l_carry;
+	
+	vli_set(p_result, p_product);
+	
+	vli_set(l_tmp, &p_product[3]);
+	l_carry = vli_add(p_result, p_result, l_tmp);
+	
+	l_tmp[0] = 0;
+	l_tmp[1] = p_product[3];
+	l_tmp[2] = p_product[4];
+	l_carry += vli_add(p_result, p_result, l_tmp);
+	
+	l_tmp[0] = l_tmp[1] = p_product[5];
+	l_tmp[2] = 0;
+	l_carry += vli_add(p_result, p_result, l_tmp);
+	
+	while(l_carry || vli_cmp(curve_p, p_result) != 1)
+	{
+		l_carry -= vli_sub(p_result, p_result, curve_p);
+	}
+}
+
+#elif ECC_CURVE == secp256r1
+
+/* Computes p_result = p_product % curve_p
+   from http://www.nsa.gov/ia/_files/nist-routines.pdf */
+static void vli_mmod_fast(uint64_t *p_result, uint64_t *p_product)
+{
+	uint64_t l_tmp[NUM_ECC_DIGITS];
+	int l_carry;
+	
+	/* t */
+	vli_set(p_result, p_product);
+	
+	/* s1 */
+	l_tmp[0] = 0;
+	l_tmp[1] = p_product[5] & 0xffffffff00000000ull;
+	l_tmp[2] = p_product[6];
+	l_tmp[3] = p_product[7];
+	l_carry = vli_lshift(l_tmp, l_tmp, 1);
+	l_carry += vli_add(p_result, p_result, l_tmp);
+	
+	/* s2 */
+	l_tmp[1] = p_product[6] << 32;
+	l_tmp[2] = (p_product[6] >> 32) | (p_product[7] << 32);
+	l_tmp[3] = p_product[7] >> 32;
+	l_carry += vli_lshift(l_tmp, l_tmp, 1);
+	l_carry += vli_add(p_result, p_result, l_tmp);
+	
+	/* s3 */
+	l_tmp[0] = p_product[4];
+	l_tmp[1] = p_product[5] & 0xffffffff;
+	l_tmp[2] = 0;
+	l_tmp[3] = p_product[7];
+	l_carry += vli_add(p_result, p_result, l_tmp);
+	
+	/* s4 */
+	l_tmp[0] = (p_product[4] >> 32) | (p_product[5] << 32);
+	l_tmp[1] = (p_product[5] >> 32) | (p_product[6] & 0xffffffff00000000ull);
+	l_tmp[2] = p_product[7];
+	l_tmp[3] = (p_product[6] >> 32) | (p_product[4] << 32);
+	l_carry += vli_add(p_result, p_result, l_tmp);
+	
+	/* d1 */
+	l_tmp[0] = (p_product[5] >> 32) | (p_product[6] << 32);
+	l_tmp[1] = (p_product[6] >> 32);
+	l_tmp[2] = 0;
+	l_tmp[3] = (p_product[4] & 0xffffffff) | (p_product[5] << 32);
+	l_carry -= vli_sub(p_result, p_result, l_tmp);
+	
+	/* d2 */
+	l_tmp[0] = p_product[6];
+	l_tmp[1] = p_product[7];
+	l_tmp[2] = 0;
+	l_tmp[3] = (p_product[4] >> 32) | (p_product[5] & 0xffffffff00000000ull);
+	l_carry -= vli_sub(p_result, p_result, l_tmp);
+	
+	/* d3 */
+	l_tmp[0] = (p_product[6] >> 32) | (p_product[7] << 32);
+	l_tmp[1] = (p_product[7] >> 32) | (p_product[4] << 32);
+	l_tmp[2] = (p_product[4] >> 32) | (p_product[5] << 32);
+	l_tmp[3] = (p_product[6] << 32);
+	l_carry -= vli_sub(p_result, p_result, l_tmp);
+	
+	/* d4 */
+	l_tmp[0] = p_product[7];
+	l_tmp[1] = p_product[4] & 0xffffffff00000000ull;
+	l_tmp[2] = p_product[5];
+	l_tmp[3] = p_product[6] & 0xffffffff00000000ull;
+	l_carry -= vli_sub(p_result, p_result, l_tmp);
+	
+	if(l_carry < 0)
+	{
+		do
+		{
+			l_carry += vli_add(p_result, p_result, curve_p);
+		} while(l_carry < 0);
+	}
+	else
+	{
+		while(l_carry || vli_cmp(curve_p, p_result) != 1)
+		{
+			l_carry -= vli_sub(p_result, p_result, curve_p);
+		}
+	}
+}
+
+#elif ECC_CURVE == secp384r1
+
+static inline void omega_mult(uint64_t *p_result, uint64_t *p_right)
+{
+	uint64_t l_tmp[NUM_ECC_DIGITS];
+	uint64_t l_carry, l_diff;
+	
+	/* Multiply by (2^128 + 2^96 - 2^32 + 1). */
+	vli_set(p_result, p_right); /* 1 */
+	l_carry = vli_lshift(l_tmp, p_right, 32);
+	p_result[1 + NUM_ECC_DIGITS] = l_carry + vli_add(p_result + 1, p_result + 1, l_tmp); /* 2^96 + 1 */
+	p_result[2 + NUM_ECC_DIGITS] = vli_add(p_result + 2, p_result + 2, p_right); /* 2^128 + 2^96 + 1 */
+	l_carry += vli_sub(p_result, p_result, l_tmp); /* 2^128 + 2^96 - 2^32 + 1 */
+	l_diff = p_result[NUM_ECC_DIGITS] - l_carry;
+	if(l_diff > p_result[NUM_ECC_DIGITS])
+	{ /* Propagate borrow if necessary. */
+		uint i;
+		for(i = 1 + NUM_ECC_DIGITS; ; ++i)
+		{
+			--p_result[i];
+			if(p_result[i] != (uint64_t)-1)
+			{
+				break;
+			}
+		}
+	}
+	p_result[NUM_ECC_DIGITS] = l_diff;
+}
+
+/* Computes p_result = p_product % curve_p
+	see PDF "Comparing Elliptic Curve Cryptography and RSA on 8-bit CPUs"
+	section "Curve-Specific Optimizations" */
+static inline void vli_mmod_fast(uint64_t *p_result, uint64_t *p_product)
+{
+	uint64_t l_tmp[2*NUM_ECC_DIGITS];
+	 
+	while(!vli_isZero(p_product + NUM_ECC_DIGITS)) /* While c1 != 0 */
+	{
+		uint64_t l_carry = 0;
+		uint i;
+		
+		vli_clear(l_tmp);
+		vli_clear(l_tmp + NUM_ECC_DIGITS);
+		omega_mult(l_tmp, p_product + NUM_ECC_DIGITS); /* tmp = w * c1 */
+		vli_clear(p_product + NUM_ECC_DIGITS); /* p = c0 */
+		
+		/* (c1, c0) = c0 + w * c1 */
+		for(i=0; i<NUM_ECC_DIGITS+3; ++i)
+		{
+			uint64_t l_sum = p_product[i] + l_tmp[i] + l_carry;
+			if(l_sum != p_product[i])
+			{
+				l_carry = (l_sum < p_product[i]);
+			}
+			p_product[i] = l_sum;
+		}
+	}
+	
+	while(vli_cmp(p_product, curve_p) > 0)
+	{
+		vli_sub(p_product, p_product, curve_p);
+	}
+	vli_set(p_result, p_product);
+}
+
+#endif
+
+/* Computes p_result = (p_left * p_right) % curve_p. */
+static inline void vli_modMult_fast(uint64_t *p_result, uint64_t *p_left, uint64_t *p_right)
+{
+	uint64_t l_product[2 * NUM_ECC_DIGITS];
+	vli_mult(l_product, p_left, p_right);
+	vli_mmod_fast(p_result, l_product);
+}
+
+/* Computes p_result = p_left^2 % curve_p. */
+static inline void vli_modSquare_fast(uint64_t *p_result, uint64_t *p_left)
+{
+	uint64_t l_product[2 * NUM_ECC_DIGITS];
+	vli_square(l_product, p_left);
+	vli_mmod_fast(p_result, l_product);
+}
+
+#define EVEN(vli) (!(vli[0] & 1))
+/* Computes p_result = (1 / p_input) % p_mod. All VLIs are the same size.
+   See "From Euclid's GCD to Montgomery Multiplication to the Great Divide"
+   https://labs.oracle.com/techrep/2001/smli_tr-2001-95.pdf */
+static inline void vli_modInv(uint64_t *p_result, uint64_t *p_input, uint64_t *p_mod)
+{
+	uint64_t a[NUM_ECC_DIGITS], b[NUM_ECC_DIGITS], u[NUM_ECC_DIGITS], v[NUM_ECC_DIGITS];
+	uint64_t l_carry;
+	int l_cmpResult;
+	
+	if(vli_isZero(p_input))
+	{
+		vli_clear(p_result);
+		return;
+	}
+
+	vli_set(a, p_input);
+	vli_set(b, p_mod);
+	vli_clear(u);
+	u[0] = 1;
+	vli_clear(v);
+	
+	while((l_cmpResult = vli_cmp(a, b)) != 0)
+	{
+		l_carry = 0;
+		if(EVEN(a))
+		{
+			vli_rshift1(a);
+			if(!EVEN(u))
+			{
+				l_carry = vli_add(u, u, p_mod);
+			}
+			vli_rshift1(u);
+			if(l_carry)
+			{
+				u[NUM_ECC_DIGITS-1] |= 0x8000000000000000ull;
+			}
+		}
+		else if(EVEN(b))
+		{
+			vli_rshift1(b);
+			if(!EVEN(v))
+			{
+				l_carry = vli_add(v, v, p_mod);
+			}
+			vli_rshift1(v);
+			if(l_carry)
+			{
+				v[NUM_ECC_DIGITS-1] |= 0x8000000000000000ull;
+			}
+		}
+		else if(l_cmpResult > 0)
+		{
+			vli_sub(a, a, b);
+			vli_rshift1(a);
+			if(vli_cmp(u, v) < 0)
+			{
+				vli_add(u, u, p_mod);
+			}
+			vli_sub(u, u, v);
+			if(!EVEN(u))
+			{
+				l_carry = vli_add(u, u, p_mod);
+			}
+			vli_rshift1(u);
+			if(l_carry)
+			{
+				u[NUM_ECC_DIGITS-1] |= 0x8000000000000000ull;
+			}
+		}
+		else
+		{
+			vli_sub(b, b, a);
+			vli_rshift1(b);
+			if(vli_cmp(v, u) < 0)
+			{
+				vli_add(v, v, p_mod);
+			}
+			vli_sub(v, v, u);
+			if(!EVEN(v))
+			{
+				l_carry = vli_add(v, v, p_mod);
+			}
+			vli_rshift1(v);
+			if(l_carry)
+			{
+				v[NUM_ECC_DIGITS-1] |= 0x8000000000000000ull;
+			}
+		}
+	}
+	
+	vli_set(p_result, u);
+}
+
+/* ------ Point operations ------ */
+
+/* Returns 1 if p_point is the point at infinity, 0 otherwise. */
+static inline int EccPoint_isZero(EccPoint *p_point)
+{
+	return (vli_isZero(p_point->x) && vli_isZero(p_point->y));
+}
+
+/* Point multiplication algorithm using Montgomery's ladder with co-Z coordinates.
+From http://eprint.iacr.org/2011/338.pdf
+*/
+
+/* Double in place */
+static inline void EccPoint_double_jacobian(uint64_t *X1, uint64_t *Y1, uint64_t *Z1)
+{
+	/* t1 = X, t2 = Y, t3 = Z */
+	uint64_t t4[NUM_ECC_DIGITS];
+	uint64_t t5[NUM_ECC_DIGITS];
+	
+	if(vli_isZero(Z1))
+	{
+		return;
+	}
+	
+	vli_modSquare_fast(t4, Y1);   /* t4 = y1^2 */
+	vli_modMult_fast(t5, X1, t4); /* t5 = x1*y1^2 = A */
+	vli_modSquare_fast(t4, t4);   /* t4 = y1^4 */
+	vli_modMult_fast(Y1, Y1, Z1); /* t2 = y1*z1 = z3 */
+	vli_modSquare_fast(Z1, Z1);   /* t3 = z1^2 */
+	
+	vli_modAdd(X1, X1, Z1, curve_p); /* t1 = x1 + z1^2 */
+	vli_modAdd(Z1, Z1, Z1, curve_p); /* t3 = 2*z1^2 */
+	vli_modSub(Z1, X1, Z1, curve_p); /* t3 = x1 - z1^2 */
+	vli_modMult_fast(X1, X1, Z1);	/* t1 = x1^2 - z1^4 */
+	
+	vli_modAdd(Z1, X1, X1, curve_p); /* t3 = 2*(x1^2 - z1^4) */
+	vli_modAdd(X1, X1, Z1, curve_p); /* t1 = 3*(x1^2 - z1^4) */
+	if(vli_testBit(X1, 0))
+	{
+		uint64_t l_carry = vli_add(X1, X1, curve_p);
+		vli_rshift1(X1);
+		X1[NUM_ECC_DIGITS-1] |= l_carry << 63;
+	}
+	else
+	{
+		vli_rshift1(X1);
+	}
+	/* t1 = 3/2*(x1^2 - z1^4) = B */
+	
+	vli_modSquare_fast(Z1, X1);	  /* t3 = B^2 */
+	vli_modSub(Z1, Z1, t5, curve_p); /* t3 = B^2 - A */
+	vli_modSub(Z1, Z1, t5, curve_p); /* t3 = B^2 - 2A = x3 */
+	vli_modSub(t5, t5, Z1, curve_p); /* t5 = A - x3 */
+	vli_modMult_fast(X1, X1, t5);	/* t1 = B * (A - x3) */
+	vli_modSub(t4, X1, t4, curve_p); /* t4 = B * (A - x3) - y1^4 = y3 */
+	
+	vli_set(X1, Z1);
+	vli_set(Z1, Y1);
+	vli_set(Y1, t4);
+}
+
+/* Modify (x1, y1) => (x1 * z^2, y1 * z^3) */
+static inline void apply_z(uint64_t *X1, uint64_t *Y1, uint64_t *Z)
+{
+	uint64_t t1[NUM_ECC_DIGITS];
+
+	vli_modSquare_fast(t1, Z);	/* z^2 */
+	vli_modMult_fast(X1, X1, t1); /* x1 * z^2 */
+	vli_modMult_fast(t1, t1, Z);  /* z^3 */
+	vli_modMult_fast(Y1, Y1, t1); /* y1 * z^3 */
+}
+
+/* P = (x1, y1) => 2P, (x2, y2) => P' */
+static inline void XYcZ_initial_double(uint64_t *X1, uint64_t *Y1, uint64_t *X2, uint64_t *Y2, uint64_t *p_initialZ)
+{
+	uint64_t z[NUM_ECC_DIGITS];
+	
+	vli_set(X2, X1);
+	vli_set(Y2, Y1);
+	
+	vli_clear(z);
+	z[0] = 1;
+	if(p_initialZ)
+	{
+		vli_set(z, p_initialZ);
+	}
+
+	apply_z(X1, Y1, z);
+	
+	EccPoint_double_jacobian(X1, Y1, z);
+	
+	apply_z(X2, Y2, z);
+}
+
+/* Input P = (x1, y1, Z), Q = (x2, y2, Z)
+   Output P' = (x1', y1', Z3), P + Q = (x3, y3, Z3)
+   or P => P', Q => P + Q
+*/
+static inline void XYcZ_add(uint64_t *X1, uint64_t *Y1, uint64_t *X2, uint64_t *Y2)
+{
+	/* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */
+	uint64_t t5[NUM_ECC_DIGITS];
+	
+	vli_modSub(t5, X2, X1, curve_p); /* t5 = x2 - x1 */
+	vli_modSquare_fast(t5, t5);	  /* t5 = (x2 - x1)^2 = A */
+	vli_modMult_fast(X1, X1, t5);	/* t1 = x1*A = B */
+	vli_modMult_fast(X2, X2, t5);	/* t3 = x2*A = C */
+	vli_modSub(Y2, Y2, Y1, curve_p); /* t4 = y2 - y1 */
+	vli_modSquare_fast(t5, Y2);	  /* t5 = (y2 - y1)^2 = D */
+	
+	vli_modSub(t5, t5, X1, curve_p); /* t5 = D - B */
+	vli_modSub(t5, t5, X2, curve_p); /* t5 = D - B - C = x3 */
+	vli_modSub(X2, X2, X1, curve_p); /* t3 = C - B */
+	vli_modMult_fast(Y1, Y1, X2);	/* t2 = y1*(C - B) */
+	vli_modSub(X2, X1, t5, curve_p); /* t3 = B - x3 */
+	vli_modMult_fast(Y2, Y2, X2);	/* t4 = (y2 - y1)*(B - x3) */
+	vli_modSub(Y2, Y2, Y1, curve_p); /* t4 = y3 */
+	
+	vli_set(X2, t5);
+}
+
+/* Input P = (x1, y1, Z), Q = (x2, y2, Z)
+   Output P + Q = (x3, y3, Z3), P - Q = (x3', y3', Z3)
+   or P => P - Q, Q => P + Q
+*/
+static inline void XYcZ_addC(uint64_t *X1, uint64_t *Y1, uint64_t *X2, uint64_t *Y2)
+{
+	/* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */
+	uint64_t t5[NUM_ECC_DIGITS];
+	uint64_t t6[NUM_ECC_DIGITS];
+	uint64_t t7[NUM_ECC_DIGITS];
+	
+	vli_modSub(t5, X2, X1, curve_p); /* t5 = x2 - x1 */
+	vli_modSquare_fast(t5, t5);	  /* t5 = (x2 - x1)^2 = A */
+	vli_modMult_fast(X1, X1, t5);	/* t1 = x1*A = B */
+	vli_modMult_fast(X2, X2, t5);	/* t3 = x2*A = C */
+	vli_modAdd(t5, Y2, Y1, curve_p); /* t4 = y2 + y1 */
+	vli_modSub(Y2, Y2, Y1, curve_p); /* t4 = y2 - y1 */
+
+	vli_modSub(t6, X2, X1, curve_p); /* t6 = C - B */
+	vli_modMult_fast(Y1, Y1, t6);	/* t2 = y1 * (C - B) */
+	vli_modAdd(t6, X1, X2, curve_p); /* t6 = B + C */
+	vli_modSquare_fast(X2, Y2);	  /* t3 = (y2 - y1)^2 */
+	vli_modSub(X2, X2, t6, curve_p); /* t3 = x3 */
+	
+	vli_modSub(t7, X1, X2, curve_p); /* t7 = B - x3 */
+	vli_modMult_fast(Y2, Y2, t7);	/* t4 = (y2 - y1)*(B - x3) */
+	vli_modSub(Y2, Y2, Y1, curve_p); /* t4 = y3 */
+	
+	vli_modSquare_fast(t7, t5);	  /* t7 = (y2 + y1)^2 = F */
+	vli_modSub(t7, t7, t6, curve_p); /* t7 = x3' */
+	vli_modSub(t6, t7, X1, curve_p); /* t6 = x3' - B */
+	vli_modMult_fast(t6, t6, t5);	/* t6 = (y2 + y1)*(x3' - B) */
+	vli_modSub(Y1, t6, Y1, curve_p); /* t2 = y3' */
+	
+	vli_set(X1, t7);
+}
+
+static inline void EccPoint_mult(EccPoint *p_result, EccPoint *p_point, uint64_t *p_scalar, uint64_t *p_initialZ)
+{
+	/* R0 and R1 */
+	uint64_t Rx[2][NUM_ECC_DIGITS];
+	uint64_t Ry[2][NUM_ECC_DIGITS];
+	uint64_t z[NUM_ECC_DIGITS];
+	
+	int i, nb;
+	
+	vli_set(Rx[1], p_point->x);
+	vli_set(Ry[1], p_point->y);
+
+	XYcZ_initial_double(Rx[1], Ry[1], Rx[0], Ry[0], p_initialZ);
+
+	for(i = vli_numBits(p_scalar) - 2; i > 0; --i)
+	{
+		nb = !vli_testBit(p_scalar, i);
+		XYcZ_addC(Rx[1-nb], Ry[1-nb], Rx[nb], Ry[nb]);
+		XYcZ_add(Rx[nb], Ry[nb], Rx[1-nb], Ry[1-nb]);
+	}
+
+	nb = !vli_testBit(p_scalar, 0);
+	XYcZ_addC(Rx[1-nb], Ry[1-nb], Rx[nb], Ry[nb]);
+	
+	/* Find final 1/Z value. */
+	vli_modSub(z, Rx[1], Rx[0], curve_p); /* X1 - X0 */
+	vli_modMult_fast(z, z, Ry[1-nb]);	 /* Yb * (X1 - X0) */
+	vli_modMult_fast(z, z, p_point->x);   /* xP * Yb * (X1 - X0) */
+	vli_modInv(z, z, curve_p);			/* 1 / (xP * Yb * (X1 - X0)) */
+	vli_modMult_fast(z, z, p_point->y);   /* yP / (xP * Yb * (X1 - X0)) */
+	vli_modMult_fast(z, z, Rx[1-nb]);	 /* Xb * yP / (xP * Yb * (X1 - X0)) */
+	/* End 1/Z calculation */
+
+	XYcZ_add(Rx[nb], Ry[nb], Rx[1-nb], Ry[1-nb]);
+	
+	apply_z(Rx[0], Ry[0], z);
+	
+	vli_set(p_result->x, Rx[0]);
+	vli_set(p_result->y, Ry[0]);
+}
+
+static inline void ecc_bytes2native(uint64_t p_native[NUM_ECC_DIGITS], const uint8_t p_bytes[ECC_BYTES])
+{
+	unsigned i;
+	for(i=0; i<NUM_ECC_DIGITS; ++i)
+	{
+		const uint8_t *p_digit = p_bytes + 8 * (NUM_ECC_DIGITS - 1 - i);
+		p_native[i] = ((uint64_t)p_digit[0] << 56) | ((uint64_t)p_digit[1] << 48) | ((uint64_t)p_digit[2] << 40) | ((uint64_t)p_digit[3] << 32) |
+			((uint64_t)p_digit[4] << 24) | ((uint64_t)p_digit[5] << 16) | ((uint64_t)p_digit[6] << 8) | (uint64_t)p_digit[7];
+	}
+}
+
+static inline void ecc_native2bytes(uint8_t p_bytes[ECC_BYTES], const uint64_t p_native[NUM_ECC_DIGITS])
+{
+	unsigned i;
+	for(i=0; i<NUM_ECC_DIGITS; ++i)
+	{
+		uint8_t *p_digit = p_bytes + 8 * (NUM_ECC_DIGITS - 1 - i);
+		p_digit[0] = p_native[i] >> 56;
+		p_digit[1] = p_native[i] >> 48;
+		p_digit[2] = p_native[i] >> 40;
+		p_digit[3] = p_native[i] >> 32;
+		p_digit[4] = p_native[i] >> 24;
+		p_digit[5] = p_native[i] >> 16;
+		p_digit[6] = p_native[i] >> 8;
+		p_digit[7] = p_native[i];
+	}
+}
+
+/* Compute a = sqrt(a) (mod curve_p). */
+static inline void mod_sqrt(uint64_t a[NUM_ECC_DIGITS])
+{
+	unsigned i;
+	uint64_t p1[NUM_ECC_DIGITS] = {1};
+	uint64_t l_result[NUM_ECC_DIGITS] = {1};
+	
+	/* Since curve_p == 3 (mod 4) for all supported curves, we can
+	   compute sqrt(a) = a^((curve_p + 1) / 4) (mod curve_p). */
+	vli_add(p1, curve_p, p1); /* p1 = curve_p + 1 */
+	for(i = vli_numBits(p1) - 1; i > 1; --i)
+	{
+		vli_modSquare_fast(l_result, l_result);
+		if(vli_testBit(p1, i))
+		{
+			vli_modMult_fast(l_result, l_result, a);
+		}
+	}
+	vli_set(a, l_result);
+}
+
+static inline void ecc_point_decompress(EccPoint *p_point, const uint8_t p_compressed[ECC_BYTES+1])
+{
+	uint64_t _3[NUM_ECC_DIGITS] = {3}; /* -a = 3 */
+	ecc_bytes2native(p_point->x, p_compressed+1);
+	
+	vli_modSquare_fast(p_point->y, p_point->x); /* y = x^2 */
+	vli_modSub(p_point->y, p_point->y, _3, curve_p); /* y = x^2 - 3 */
+	vli_modMult_fast(p_point->y, p_point->y, p_point->x); /* y = x^3 - 3x */
+	vli_modAdd(p_point->y, p_point->y, curve_b, curve_p); /* y = x^3 - 3x + b */
+	
+	mod_sqrt(p_point->y);
+	
+	if((p_point->y[0] & 0x01) != (p_compressed[0] & 0x01))
+	{
+		vli_sub(p_point->y, curve_p, p_point->y);
+	}
+}
+
+static inline int ecc_make_key(uint8_t p_publicKey[ECC_BYTES+1], uint8_t p_privateKey[ECC_BYTES])
+{
+	uint64_t l_private[NUM_ECC_DIGITS];
+	EccPoint l_public;
+	unsigned l_tries = 0;
+	
+	do
+	{
+		if(!getRandomNumber(l_private) || (l_tries++ >= MAX_TRIES))
+		{
+			return 0;
+		}
+		if(vli_isZero(l_private))
+		{
+			continue;
+		}
+	
+		/* Make sure the private key is in the range [1, n-1].
+		   For the supported curves, n is always large enough that we only need to subtract once at most. */
+		if(vli_cmp(curve_n, l_private) != 1)
+		{
+			vli_sub(l_private, l_private, curve_n);
+		}
+
+		EccPoint_mult(&l_public, &curve_G, l_private, NULL);
+	} while(EccPoint_isZero(&l_public));
+	
+	ecc_native2bytes(p_privateKey, l_private);
+	ecc_native2bytes(p_publicKey + 1, l_public.x);
+	p_publicKey[0] = 2 + (l_public.y[0] & 0x01);
+	return 1;
+}
+
+static inline int ecdh_shared_secret(const uint8_t p_publicKey[ECC_BYTES+1], const uint8_t p_privateKey[ECC_BYTES], uint8_t p_secret[ECC_BYTES])
+{
+	EccPoint l_public;
+	uint64_t l_private[NUM_ECC_DIGITS];
+	uint64_t l_random[NUM_ECC_DIGITS];
+	
+	if(!getRandomNumber(l_random))
+	{
+		return 0;
+	}
+	
+	ecc_point_decompress(&l_public, p_publicKey);
+	ecc_bytes2native(l_private, p_privateKey);
+	
+	EccPoint l_product;
+	EccPoint_mult(&l_product, &l_public, l_private, l_random);
+	
+	ecc_native2bytes(p_secret, l_product.x);
+	
+	return !EccPoint_isZero(&l_product);
+}
+
+/* -------- ECDSA code -------- */
+
+/* Computes p_result = (p_left * p_right) % p_mod. */
+static inline void vli_modMult(uint64_t *p_result, uint64_t *p_left, uint64_t *p_right, uint64_t *p_mod)
+{
+	uint64_t l_product[2 * NUM_ECC_DIGITS];
+	uint64_t l_modMultiple[2 * NUM_ECC_DIGITS];
+	uint l_digitShift, l_bitShift;
+	uint l_productBits;
+	uint l_modBits = vli_numBits(p_mod);
+	
+	vli_mult(l_product, p_left, p_right);
+	l_productBits = vli_numBits(l_product + NUM_ECC_DIGITS);
+	if(l_productBits)
+	{
+		l_productBits += NUM_ECC_DIGITS * 64;
+	}
+	else
+	{
+		l_productBits = vli_numBits(l_product);
+	}
+	
+	if(l_productBits < l_modBits)
+	{ /* l_product < p_mod. */
+		vli_set(p_result, l_product);
+		return;
+	}
+	
+	/* Shift p_mod by (l_leftBits - l_modBits). This multiplies p_mod by the largest
+	   power of two possible while still resulting in a number less than p_left. */
+	vli_clear(l_modMultiple);
+	vli_clear(l_modMultiple + NUM_ECC_DIGITS);
+	l_digitShift = (l_productBits - l_modBits) / 64;
+	l_bitShift = (l_productBits - l_modBits) % 64;
+	if(l_bitShift)
+	{
+		l_modMultiple[l_digitShift + NUM_ECC_DIGITS] = vli_lshift(l_modMultiple + l_digitShift, p_mod, l_bitShift);
+	}
+	else
+	{
+		vli_set(l_modMultiple + l_digitShift, p_mod);
+	}
+
+	/* Subtract all multiples of p_mod to get the remainder. */
+	vli_clear(p_result);
+	p_result[0] = 1; /* Use p_result as a temp var to store 1 (for subtraction) */
+	while(l_productBits > NUM_ECC_DIGITS * 64 || vli_cmp(l_modMultiple, p_mod) >= 0)
+	{
+		int l_cmp = vli_cmp(l_modMultiple + NUM_ECC_DIGITS, l_product + NUM_ECC_DIGITS);
+		if(l_cmp < 0 || (l_cmp == 0 && vli_cmp(l_modMultiple, l_product) <= 0))
+		{
+			if(vli_sub(l_product, l_product, l_modMultiple))
+			{ /* borrow */
+				vli_sub(l_product + NUM_ECC_DIGITS, l_product + NUM_ECC_DIGITS, p_result);
+			}
+			vli_sub(l_product + NUM_ECC_DIGITS, l_product + NUM_ECC_DIGITS, l_modMultiple + NUM_ECC_DIGITS);
+		}
+		uint64_t l_carry = (l_modMultiple[NUM_ECC_DIGITS] & 0x01) << 63;
+		vli_rshift1(l_modMultiple + NUM_ECC_DIGITS);
+		vli_rshift1(l_modMultiple);
+		l_modMultiple[NUM_ECC_DIGITS-1] |= l_carry;
+		
+		--l_productBits;
+	}
+	vli_set(p_result, l_product);
+}
+
+static inline uint umax(uint a, uint b)
+{
+	return (a > b ? a : b);
+}
+
+static inline int ecdsa_sign(const uint8_t p_privateKey[ECC_BYTES], const uint8_t p_hash[ECC_BYTES], uint8_t p_signature[ECC_BYTES*2])
+{
+	uint64_t k[NUM_ECC_DIGITS];
+	uint64_t l_tmp[NUM_ECC_DIGITS];
+	uint64_t l_s[NUM_ECC_DIGITS];
+	EccPoint p;
+	unsigned l_tries = 0;
+	
+	do
+	{
+		if(!getRandomNumber(k) || (l_tries++ >= MAX_TRIES))
+		{
+			return 0;
+		}
+		if(vli_isZero(k))
+		{
+			continue;
+		}
+	
+		if(vli_cmp(curve_n, k) != 1)
+		{
+			vli_sub(k, k, curve_n);
+		}
+	
+		/* tmp = k * G */
+		EccPoint_mult(&p, &curve_G, k, NULL);
+	
+		/* r = x1 (mod n) */
+		if(vli_cmp(curve_n, p.x) != 1)
+		{
+			vli_sub(p.x, p.x, curve_n);
+		}
+	} while(vli_isZero(p.x));
+
+	ecc_native2bytes(p_signature, p.x);
+	
+	ecc_bytes2native(l_tmp, p_privateKey);
+	vli_modMult(l_s, p.x, l_tmp, curve_n); /* s = r*d */
+	ecc_bytes2native(l_tmp, p_hash);
+	vli_modAdd(l_s, l_tmp, l_s, curve_n); /* s = e + r*d */
+	vli_modInv(k, k, curve_n); /* k = 1 / k */
+	vli_modMult(l_s, l_s, k, curve_n); /* s = (e + r*d) / k */
+	ecc_native2bytes(p_signature + ECC_BYTES, l_s);
+	
+	return 1;
+}
+
+static inline int ecdsa_verify(const uint8_t p_publicKey[ECC_BYTES+1], const uint8_t p_hash[ECC_BYTES], const uint8_t p_signature[ECC_BYTES*2])
+{
+	uint64_t u1[NUM_ECC_DIGITS], u2[NUM_ECC_DIGITS];
+	uint64_t z[NUM_ECC_DIGITS];
+	EccPoint l_public, l_sum;
+	uint64_t rx[NUM_ECC_DIGITS];
+	uint64_t ry[NUM_ECC_DIGITS];
+	uint64_t tx[NUM_ECC_DIGITS];
+	uint64_t ty[NUM_ECC_DIGITS];
+	uint64_t tz[NUM_ECC_DIGITS];
+	
+	uint64_t l_r[NUM_ECC_DIGITS], l_s[NUM_ECC_DIGITS];
+	
+	ecc_point_decompress(&l_public, p_publicKey);
+	ecc_bytes2native(l_r, p_signature);
+	ecc_bytes2native(l_s, p_signature + ECC_BYTES);
+	
+	if(vli_isZero(l_r) || vli_isZero(l_s))
+	{ /* r, s must not be 0. */
+		return 0;
+	}
+	
+	if(vli_cmp(curve_n, l_r) != 1 || vli_cmp(curve_n, l_s) != 1)
+	{ /* r, s must be < n. */
+		return 0;
+	}
+
+	/* Calculate u1 and u2. */
+	vli_modInv(z, l_s, curve_n); /* Z = s^-1 */
+	ecc_bytes2native(u1, p_hash);
+	vli_modMult(u1, u1, z, curve_n); /* u1 = e/s */
+	vli_modMult(u2, l_r, z, curve_n); /* u2 = r/s */
+	
+	/* Calculate l_sum = G + Q. */
+	vli_set(l_sum.x, l_public.x);
+	vli_set(l_sum.y, l_public.y);
+	vli_set(tx, curve_G.x);
+	vli_set(ty, curve_G.y);
+	vli_modSub(z, l_sum.x, tx, curve_p); /* Z = x2 - x1 */
+	XYcZ_add(tx, ty, l_sum.x, l_sum.y);
+	vli_modInv(z, z, curve_p); /* Z = 1/Z */
+	apply_z(l_sum.x, l_sum.y, z);
+	
+	/* Use Shamir's trick to calculate u1*G + u2*Q */
+	EccPoint *l_points[4] = {NULL, &curve_G, &l_public, &l_sum};
+	uint l_numBits = umax(vli_numBits(u1), vli_numBits(u2));
+	
+	EccPoint *l_point = l_points[(!!vli_testBit(u1, l_numBits-1)) | ((!!vli_testBit(u2, l_numBits-1)) << 1)];
+	vli_set(rx, l_point->x);
+	vli_set(ry, l_point->y);
+	vli_clear(z);
+	z[0] = 1;
+
+	int i;
+	for(i = l_numBits - 2; i >= 0; --i)
+	{
+		EccPoint_double_jacobian(rx, ry, z);
+		
+		int l_index = (!!vli_testBit(u1, i)) | ((!!vli_testBit(u2, i)) << 1);
+		EccPoint *l_point = l_points[l_index];
+		if(l_point)
+		{
+			vli_set(tx, l_point->x);
+			vli_set(ty, l_point->y);
+			apply_z(tx, ty, z);
+			vli_modSub(tz, rx, tx, curve_p); /* Z = x2 - x1 */
+			XYcZ_add(tx, ty, rx, ry);
+			vli_modMult_fast(z, z, tz);
+		}
+	}
+
+	vli_modInv(z, z, curve_p); /* Z = 1/Z */
+	apply_z(rx, ry, z);
+	
+	/* v = x1 (mod n) */
+	if(vli_cmp(curve_n, rx) != 1)
+	{
+		vli_sub(rx, rx, curve_n);
+	}
+
+	/* Accept only if v == r. */
+	return (vli_cmp(rx, l_r) == 0);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+} // anonymous namespace
+
+void ECC384GenerateKey(uint8_t pub[ZT_ECC384_PUBLIC_KEY_SIZE],uint8_t priv[ZT_ECC384_PRIVATE_KEY_SIZE])
+{
+	if (!ecc_make_key(pub,priv)) {
+		fprintf(stderr,"FATAL: ecdsa_make_key() failed!" ZT_EOL_S);
+		abort();
+	}
+}
+
+void ECC384ECDSASign(const uint8_t priv[ZT_ECC384_PRIVATE_KEY_SIZE],const uint8_t hash[ZT_ECC384_SIGNATURE_HASH_SIZE],uint8_t sig[ZT_ECC384_SIGNATURE_SIZE])
+{
+	if (!ecdsa_sign(priv,hash,sig)) {
+		fprintf(stderr,"FATAL: ecdsa_sign() failed!" ZT_EOL_S);
+		abort();
+	}
+}
+
+bool ECC384ECDSAVerify(const uint8_t pub[ZT_ECC384_PUBLIC_KEY_SIZE],const uint8_t hash[ZT_ECC384_SIGNATURE_HASH_SIZE],const uint8_t sig[ZT_ECC384_SIGNATURE_SIZE])
+{
+	return (ecdsa_verify(pub,hash,sig) != 0);
+}
+
+bool ECC384ECDH(const uint8_t theirPub[ZT_ECC384_PUBLIC_KEY_SIZE],const uint8_t ourPriv[ZT_ECC384_PRIVATE_KEY_SIZE],uint8_t secret[ZT_ECC384_SHARED_SECRET_SIZE])
+{
+	return (ecdh_shared_secret(theirPub,ourPriv,secret) != 0);
+}
+
+} // namespace ZeroTier

+ 108 - 0
node/ECC384.hpp

@@ -0,0 +1,108 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2019  ZeroTier, Inc.  https://www.zerotier.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+// This is glue code to ease the use of the NIST P-384 elliptic curve.
+
+// Note that most of the code inside ECC384.cpp is third party code and
+// is under the BSD 2-clause license rather than ZeroTier's license.
+
+#ifndef ZT_ECC384_HPP
+#define ZT_ECC384_HPP
+
+#include "Constants.hpp"
+
+/**
+ * Size of a (point compressed) P-384 public key
+ */
+#define ZT_ECC384_PUBLIC_KEY_SIZE 49
+
+/**
+ * Size of a P-384 private key
+ */
+#define ZT_ECC384_PRIVATE_KEY_SIZE 48
+
+/**
+ * Size of the hash that should be signed using P-384
+ */
+#define ZT_ECC384_SIGNATURE_HASH_SIZE 48
+
+/**
+ * Size of a P-384 signature
+ */
+#define ZT_ECC384_SIGNATURE_SIZE 96
+
+/**
+ * Size of raw shared secret generated by ECDH key agreement
+ */
+#define ZT_ECC384_SHARED_SECRET_SIZE 48
+
+namespace ZeroTier {
+
+/**
+ * Generate a NIST P-384 key pair
+ * 
+ * @param pub Buffer to receive point compressed public key
+ * @param priv Buffer to receiver private key
+ */
+void ECC384GenerateKey(uint8_t pub[ZT_ECC384_PUBLIC_KEY_SIZE],uint8_t priv[ZT_ECC384_PRIVATE_KEY_SIZE]);
+
+/**
+ * Sign a hash with a NIST P-384 private key
+ * 
+ * The hash must be 48 bytes in size and is typically the first 48 bytes
+ * of a SHA512 hash or something similar. Extra bytes of course are ignored.
+ * 
+ * @param priv Private key
+ * @param hash 48-byte hash
+ * @param sig Buffer to receive signature
+ */
+void ECC384ECDSASign(const uint8_t priv[ZT_ECC384_PRIVATE_KEY_SIZE],const uint8_t hash[ZT_ECC384_SIGNATURE_HASH_SIZE],uint8_t sig[ZT_ECC384_SIGNATURE_SIZE]);
+
+/**
+ * Verify a signature
+ * 
+ * @param pub Public key
+ * @param hash 48-byte hash (usually first 48 bytes of SHA512(msg))
+ * @param sig Signature to check
+ * @return True if signature is valid
+ */
+bool ECC384ECDSAVerify(const uint8_t pub[ZT_ECC384_PUBLIC_KEY_SIZE],const uint8_t hash[ZT_ECC384_SIGNATURE_HASH_SIZE],const uint8_t sig[ZT_ECC384_SIGNATURE_SIZE]);
+
+/**
+ * Perform ECDH key agreement
+ * 
+ * The secret generated here is the raw 48-byte result of ECDH.
+ * It's typically hashed prior to use.
+ * 
+ * @param theirPub Remote public key
+ * @param ourPriv Local private key
+ * @param secret Buffer to receive 48-byte secret
+ */
+bool ECC384ECDH(const uint8_t theirPub[ZT_ECC384_PUBLIC_KEY_SIZE],const uint8_t ourPriv[ZT_ECC384_PRIVATE_KEY_SIZE],uint8_t secret[ZT_ECC384_SHARED_SECRET_SIZE]);
+
+} // namespace ZeroTier
+
+#endif

+ 193 - 57
node/Identity.cpp

@@ -35,19 +35,20 @@
 #include "Salsa20.hpp"
 #include "Utils.hpp"
 
+namespace ZeroTier {
+
+namespace {
+
 // These can't be changed without a new identity type. They define the
 // parameters of the hashcash hashing/searching algorithm.
-
 #define ZT_IDENTITY_GEN_HASHCASH_FIRST_BYTE_LESS_THAN 17
 #define ZT_IDENTITY_GEN_MEMORY 2097152
 
-namespace ZeroTier {
-
 // A memory-hard composition of SHA-512 and Salsa20 for hashcash hashing
-static inline void _computeMemoryHardHash(const void *publicKey,unsigned int publicKeyBytes,void *digest,void *genmem)
+static void _computeMemoryHardHash(const void *publicKey,unsigned int publicKeyBytes,void *digest,void *genmem)
 {
 	// Digest publicKey[] to obtain initial digest
-	SHA512::hash(digest,publicKey,publicKeyBytes);
+	SHA512(digest,publicKey,publicKeyBytes);
 
 	// Initialize genmem[] using Salsa20 in a CBC-like configuration since
 	// ordinary Salsa20 is randomly seek-able. This is good for a cipher
@@ -94,22 +95,36 @@ struct _Identity_generate_cond
 	char *genmem;
 };
 
-void Identity::generate()
-{
-	unsigned char digest[64];
-	char *genmem = new char[ZT_IDENTITY_GEN_MEMORY];
-
-	C25519::Pair kp;
-	do {
-		kp = C25519::generateSatisfying(_Identity_generate_cond(digest,genmem));
-		_address.setTo(digest + 59,ZT_ADDRESS_LENGTH); // last 5 bytes are address
-	} while (_address.isReserved());
-
-	_publicKey = kp.pub;
-	if (!_privateKey)
-		_privateKey = new C25519::Private();
-	*_privateKey = kp.priv;
+} // anonymous namespace
 
+void Identity::generate(const Type t)
+{
+	uint8_t digest[64];
+	char *const genmem = new char[ZT_IDENTITY_GEN_MEMORY];
+	switch(t) {
+		case C25519: {
+			C25519::Pair kp;
+			do {
+				kp = C25519::generateSatisfying(_Identity_generate_cond(digest,genmem));
+				_address.setTo(digest + 59,ZT_ADDRESS_LENGTH); // last 5 bytes are address
+			} while (_address.isReserved());
+			memcpy(_k.t0.pub.data,kp.pub.data,ZT_C25519_PUBLIC_KEY_LEN);
+			memcpy(_k.t0.priv.data,kp.priv.data,ZT_C25519_PRIVATE_KEY_LEN);
+			_type = C25519;
+			_hasPrivate = true;
+		}	break;
+		case P384: {
+			do {
+				ECC384GenerateKey(_k.t1.pub,_k.t1.priv);
+				_computeMemoryHardHash(_k.t1.pub,ZT_ECC384_PUBLIC_KEY_SIZE,digest,genmem);
+				if (digest[0] >= ZT_IDENTITY_GEN_HASHCASH_FIRST_BYTE_LESS_THAN)
+					continue;
+				_address.setTo(digest + 59,ZT_ADDRESS_LENGTH);
+			} while (_address.isReserved());
+			_type = P384;
+			_hasPrivate = true;
+		}	break;
+	}
 	delete [] genmem;
 }
 
@@ -117,41 +132,136 @@ bool Identity::locallyValidate() const
 {
 	if (_address.isReserved())
 		return false;
+	uint8_t digest[64];
+	char *genmem = nullptr;
+	try {
+		genmem = new char[ZT_IDENTITY_GEN_MEMORY];
+		switch(_type) {
+			case C25519:
+				_computeMemoryHardHash(_k.t0.pub.data,ZT_C25519_PUBLIC_KEY_LEN,digest,genmem);
+				break;
+			case P384:
+				_computeMemoryHardHash(_k.t1.pub,ZT_ECC384_PUBLIC_KEY_SIZE,digest,genmem);
+				break;
+			default:
+				return false;
+		}
+		delete [] genmem;
+		unsigned char addrb[5];
+		_address.copyTo(addrb,5);
+		return (
+			(digest[0] < ZT_IDENTITY_GEN_HASHCASH_FIRST_BYTE_LESS_THAN)&&
+			(digest[59] == addrb[0])&&
+			(digest[60] == addrb[1])&&
+			(digest[61] == addrb[2])&&
+			(digest[62] == addrb[3])&&
+			(digest[63] == addrb[4]));
+	} catch ( ... ) {
+		if (genmem) delete [] genmem;
+		return false;
+	}
+}
 
-	unsigned char digest[64];
-	char *genmem = new char[ZT_IDENTITY_GEN_MEMORY];
-	_computeMemoryHardHash(_publicKey.data,ZT_C25519_PUBLIC_KEY_LEN,digest,genmem);
-	delete [] genmem;
+unsigned int Identity::sign(const void *data,unsigned int len,void *sig,unsigned int siglen) const
+{
+	uint8_t h[48];
+	if (!_hasPrivate)
+		return 0;
+	switch(_type) {
+		case C25519:
+			if (siglen < ZT_C25519_SIGNATURE_LEN)
+				return 0;
+			C25519::sign(_k.t0.priv,_k.t0.pub,data,len,sig);
+			return ZT_C25519_SIGNATURE_LEN;
+		case P384:
+			if (siglen < ZT_ECC384_SIGNATURE_SIZE)
+				return 0;
+			SHA384(h,data,len);
+			ECC384ECDSASign(_k.t1.priv,h,(uint8_t *)sig);
+			return ZT_ECC384_SIGNATURE_SIZE;
+	}
+	return 0;
+}
 
-	unsigned char addrb[5];
-	_address.copyTo(addrb,5);
+bool Identity::verify(const void *data,unsigned int len,const void *sig,unsigned int siglen) const
+{
+	switch(_type) {
+		case C25519:
+			return C25519::verify(_k.t0.pub,data,len,sig,siglen);
+		case P384:
+			if (siglen == ZT_ECC384_SIGNATURE_SIZE) {
+				uint8_t h[48];
+				SHA384(h,data,len);
+				return ECC384ECDSAVerify(_k.t1.pub,h,(const uint8_t *)sig);
+			}
+			break;
+	}
+	return false;
+}
 
-	return (
-		(digest[0] < ZT_IDENTITY_GEN_HASHCASH_FIRST_BYTE_LESS_THAN)&&
-		(digest[59] == addrb[0])&&
-		(digest[60] == addrb[1])&&
-		(digest[61] == addrb[2])&&
-		(digest[62] == addrb[3])&&
-		(digest[63] == addrb[4]));
+bool Identity::agree(const Identity &id,void *key,unsigned int klen) const
+{
+	uint8_t ecc384RawSecret[ZT_ECC384_SHARED_SECRET_SIZE];
+	uint8_t h[48];
+	if (_hasPrivate) {
+		switch(_type) {
+			case C25519:
+				C25519::agree(_k.t0.priv,id._k.t0.pub,key,klen);
+				return true;
+			case P384:
+				ECC384ECDH(id._k.t1.pub,_k.t1.priv,ecc384RawSecret);
+				SHA384(h,ecc384RawSecret,sizeof(ecc384RawSecret));
+				for(unsigned int i=0,hi=0;i<klen;++i) {
+					if (hi == 48) {
+						hi = 0;
+						SHA384(h,h,48);
+					}
+					((uint8_t *)key)[i] = h[hi++];
+				}
+				return true;
+		}
+	}
+	return false;
 }
 
 char *Identity::toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]) const
 {
-	char *p = buf;
-	Utils::hex10(_address.toInt(),p);
-	p += 10;
-	*(p++) = ':';
-	*(p++) = '0';
-	*(p++) = ':';
-	Utils::hex(_publicKey.data,ZT_C25519_PUBLIC_KEY_LEN,p);
-	p += ZT_C25519_PUBLIC_KEY_LEN * 2;
-	if ((_privateKey)&&(includePrivate)) {
-		*(p++) = ':';
-		Utils::hex(_privateKey->data,ZT_C25519_PRIVATE_KEY_LEN,p);
-		p += ZT_C25519_PRIVATE_KEY_LEN * 2;
+	switch(_type) {
+		case C25519: {
+			char *p = buf;
+			Utils::hex10(_address.toInt(),p);
+			p += 10;
+			*(p++) = ':';
+			*(p++) = '0';
+			*(p++) = ':';
+			Utils::hex(_k.t0.pub.data,ZT_C25519_PUBLIC_KEY_LEN,p);
+			p += ZT_C25519_PUBLIC_KEY_LEN * 2;
+			if ((_hasPrivate)&&(includePrivate)) {
+				*(p++) = ':';
+				Utils::hex(_k.t0.priv.data,ZT_C25519_PRIVATE_KEY_LEN,p);
+				p += ZT_C25519_PRIVATE_KEY_LEN * 2;
+			}
+			*p = (char)0;
+			return buf;
+		}
+		case P384: {
+			char *p = buf;
+			Utils::hex10(_address.toInt(),p);
+			p += 10;
+			*(p++) = ':';
+			*(p++) = '1';
+			*(p++) = ':';
+			Utils::hex(_k.t1.pub,ZT_ECC384_PUBLIC_KEY_SIZE,p);
+			p += ZT_ECC384_PUBLIC_KEY_SIZE * 2;
+			if ((_hasPrivate)&&(includePrivate)) {
+				*(p++) = ':';
+				Utils::hex(_k.t1.priv,ZT_ECC384_PRIVATE_KEY_SIZE,p);
+				p += ZT_ECC384_PRIVATE_KEY_SIZE * 2;
+			}
+			*p = (char)0;
+			return buf;
+		}	break;
 	}
-	*p = (char)0;
-	return buf;
 }
 
 bool Identity::fromString(const char *str)
@@ -166,8 +276,7 @@ bool Identity::fromString(const char *str)
 		return false;
 	}
 
-	delete _privateKey;
-	_privateKey = (C25519::Private *)0;
+	_hasPrivate = false;
 
 	int fno = 0;
 	char *saveptr = (char *)0;
@@ -181,22 +290,49 @@ bool Identity::fromString(const char *str)
 				}
 				break;
 			case 1:
-				if ((f[0] != '0')||(f[1])) {
+				if ((f[0] == '0')&&(!f[1])) {
+					_type = C25519;
+				} else if ((f[0] == '1')&&(!f[1])) {
+					_type = P384;
+				} else {
 					_address.zero();
 					return false;
 				}
 				break;
 			case 2:
-				if (Utils::unhex(f,_publicKey.data,ZT_C25519_PUBLIC_KEY_LEN) != ZT_C25519_PUBLIC_KEY_LEN) {
-					_address.zero();
-					return false;
+				switch(_type) {
+					case C25519:
+						if (Utils::unhex(f,_k.t0.pub.data,ZT_C25519_PUBLIC_KEY_LEN) != ZT_C25519_PUBLIC_KEY_LEN) {
+							_address.zero();
+							return false;
+						}
+						break;
+					case P384:
+						if (Utils::unhex(f,_k.t1.pub,ZT_ECC384_PUBLIC_KEY_SIZE) != ZT_ECC384_PUBLIC_KEY_SIZE) {
+							_address.zero();
+							return false;
+						}
+						break;
 				}
 				break;
 			case 3:
-				_privateKey = new C25519::Private();
-				if (Utils::unhex(f,_privateKey->data,ZT_C25519_PRIVATE_KEY_LEN) != ZT_C25519_PRIVATE_KEY_LEN) {
-					_address.zero();
-					return false;
+				switch(_type) {
+					case C25519:
+						if (Utils::unhex(f,_k.t0.priv.data,ZT_C25519_PRIVATE_KEY_LEN) != ZT_C25519_PRIVATE_KEY_LEN) {
+							_address.zero();
+							return false;
+						} else {
+							_hasPrivate = true;
+						}
+						break;
+					case P384:
+						if (Utils::unhex(f,_k.t1.priv,ZT_ECC384_PRIVATE_KEY_SIZE) != ZT_ECC384_PRIVATE_KEY_SIZE) {
+							_address.zero();
+							return false;
+						} else {
+							_hasPrivate = true;
+						}
+						break;
 				}
 				break;
 			default:

+ 173 - 113
node/Identity.hpp

@@ -36,6 +36,7 @@
 #include "C25519.hpp"
 #include "Buffer.hpp"
 #include "SHA512.hpp"
+#include "ECC384.hpp"
 
 #define ZT_IDENTITY_STRING_BUFFER_LENGTH 384
 
@@ -54,61 +55,50 @@ namespace ZeroTier {
 class Identity
 {
 public:
-	Identity() :
-		_privateKey((C25519::Private *)0)
+	/**
+	 * Identity type -- numeric values of these enums are protocol constants
+	 */
+	enum Type
 	{
-	}
+		C25519 = ZT_CRYPTO_ALG_C25519, // Type 0 -- Curve25519 and Ed25519 (1.0 and 2.0, default)
+		P384 = ZT_CRYPTO_ALG_P384      // Type 1 -- NIST P-384 ECDH and ECDSA (2.0+ only)
+	};
 
-	Identity(const Identity &id) :
-		_address(id._address),
-		_publicKey(id._publicKey),
-		_privateKey((id._privateKey) ? new C25519::Private(*(id._privateKey)) : (C25519::Private *)0)
-	{
-	}
+	Identity() { memset(reinterpret_cast<void *>(this),0,sizeof(Identity)); }
+	Identity(const Identity &id) { memcpy(reinterpret_cast<void *>(this),&id,sizeof(Identity)); }
 
-	Identity(const char *str) :
-		_privateKey((C25519::Private *)0)
+	Identity(const char *str)
 	{
 		if (!fromString(str))
 			throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
 	}
 
 	template<unsigned int C>
-	Identity(const Buffer<C> &b,unsigned int startAt = 0) :
-		_privateKey((C25519::Private *)0)
-	{
-		deserialize(b,startAt);
-	}
+	Identity(const Buffer<C> &b,unsigned int startAt = 0) { deserialize(b,startAt); }
 
-	~Identity()
-	{
-		if (_privateKey) {
-			Utils::burn(_privateKey,sizeof(C25519::Private));
-			delete _privateKey;
-		}
-	}
+	~Identity() { Utils::burn(reinterpret_cast<void *>(this),sizeof(Identity)); }
+
+	inline void zero() { Utils::burn(reinterpret_cast<void *>(this),sizeof(Identity)); }
 
 	inline Identity &operator=(const Identity &id)
 	{
-		_address = id._address;
-		_publicKey = id._publicKey;
-		if (id._privateKey) {
-			if (!_privateKey)
-				_privateKey = new C25519::Private();
-			*_privateKey = *(id._privateKey);
-		} else {
-			delete _privateKey;
-			_privateKey = (C25519::Private *)0;
-		}
+		memcpy(reinterpret_cast<void *>(this),&id,sizeof(Identity));
 		return *this;
 	}
 
+	/**
+	 * @return Identity type
+	 */
+	inline Type type() const { return _type; }
+
 	/**
 	 * Generate a new identity (address, key pair)
 	 *
 	 * This is a time consuming operation.
+	 * 
+	 * @param t Type of identity to generate
 	 */
-	void generate();
+	void generate(const Type t);
 
 	/**
 	 * Check the validity of this identity's pairing of key to address
@@ -120,7 +110,7 @@ public:
 	/**
 	 * @return True if this identity contains a private key
 	 */
-	inline bool hasPrivate() const { return (_privateKey != (C25519::Private *)0); }
+	inline bool hasPrivate() const { return _hasPrivate; }
 
 	/**
 	 * Compute the SHA512 hash of our private key (if we have one)
@@ -130,54 +120,64 @@ public:
 	 */
 	inline bool sha512PrivateKey(void *sha) const
 	{
-		if (_privateKey) {
-			SHA512::hash(sha,_privateKey->data,ZT_C25519_PRIVATE_KEY_LEN);
-			return true;
+		if (_hasPrivate) {
+			switch(_type) {
+				case C25519:
+					SHA512(sha,_k.t0.priv.data,ZT_C25519_PRIVATE_KEY_LEN);
+					return true;
+				case P384:
+					SHA512(sha,_k.t1.priv,ZT_ECC384_PRIVATE_KEY_SIZE);
+					return true;
+			}
 		}
 		return false;
 	}
 
 	/**
-	 * Sign a message with this identity (private key required)
-	 *
-	 * @param data Data to sign
-	 * @param len Length of data
+	 * Compute the SHA512 hash of our public key
+	 * 
+	 * @param sha Buffer to receive hash bytes
+	 * @return True on success, false if identity is empty or invalid
 	 */
-	inline C25519::Signature sign(const void *data,unsigned int len) const
+	inline bool sha512PublicKey(void *sha) const
 	{
-		if (_privateKey)
-			return C25519::sign(*_privateKey,_publicKey,data,len);
-		throw ZT_EXCEPTION_PRIVATE_KEY_REQUIRED;
+		if (_hasPrivate) {
+			switch(_type) {
+				case C25519:
+					SHA512(sha,_k.t0.pub.data,ZT_C25519_PUBLIC_KEY_LEN);
+					return true;
+				case P384:
+					SHA512(sha,_k.t1.pub,ZT_ECC384_PUBLIC_KEY_SIZE);
+					return true;
+			}
+		}
+		return false;
 	}
 
 	/**
-	 * Verify a message signature against this identity
+	 * Sign a message with this identity (private key required)
 	 *
-	 * @param data Data to check
+	 * The signature buffer should be large enough for the largest
+	 * signature, which is currently 96 bytes.
+	 * 
+	 * @param data Data to sign
 	 * @param len Length of data
-	 * @param signature Signature bytes
-	 * @param siglen Length of signature in bytes
-	 * @return True if signature validates and data integrity checks
+	 * @param sig Buffer to receive signature
+	 * @param siglen Length of buffer
+	 * @return Number of bytes actually written to sig or 0 on error
 	 */
-	inline bool verify(const void *data,unsigned int len,const void *signature,unsigned int siglen) const
-	{
-		if (siglen != ZT_C25519_SIGNATURE_LEN)
-			return false;
-		return C25519::verify(_publicKey,data,len,signature);
-	}
+	unsigned int sign(const void *data,unsigned int len,void *sig,unsigned int siglen) const;
 
 	/**
 	 * Verify a message signature against this identity
 	 *
 	 * @param data Data to check
 	 * @param len Length of data
-	 * @param signature Signature
+	 * @param signature Signature bytes
+	 * @param siglen Length of signature in bytes
 	 * @return True if signature validates and data integrity checks
 	 */
-	inline bool verify(const void *data,unsigned int len,const C25519::Signature &signature) const
-	{
-		return C25519::verify(_publicKey,data,len,signature);
-	}
+	bool verify(const void *data,unsigned int len,const void *sig,unsigned int siglen) const;
 
 	/**
 	 * Shortcut method to perform key agreement with another identity
@@ -189,14 +189,7 @@ public:
 	 * @param klen Length of key in bytes
 	 * @return Was agreement successful?
 	 */
-	inline bool agree(const Identity &id,void *key,unsigned int klen) const
-	{
-		if (_privateKey) {
-			C25519::agree(*_privateKey,id._publicKey,key,klen);
-			return true;
-		}
-		return false;
-	}
+	bool agree(const Identity &id,void *key,unsigned int klen) const;
 
 	/**
 	 * @return This identity's address
@@ -214,12 +207,31 @@ public:
 	inline void serialize(Buffer<C> &b,bool includePrivate = false) const
 	{
 		_address.appendTo(b);
-		b.append((uint8_t)0); // C25519/Ed25519 identity type
-		b.append(_publicKey.data,ZT_C25519_PUBLIC_KEY_LEN);
-		if ((_privateKey)&&(includePrivate)) {
-			b.append((unsigned char)ZT_C25519_PRIVATE_KEY_LEN);
-			b.append(_privateKey->data,ZT_C25519_PRIVATE_KEY_LEN);
-		} else b.append((unsigned char)0);
+		switch(_type) {
+
+			case C25519:
+				b.append((uint8_t)C25519);
+				b.append(_k.t0.pub.data,ZT_C25519_PUBLIC_KEY_LEN);
+				if ((_hasPrivate)&&(includePrivate)) {
+					b.append((uint8_t)ZT_C25519_PRIVATE_KEY_LEN);
+					b.append(_k.t0.priv.data,ZT_C25519_PRIVATE_KEY_LEN);
+				} else {
+					b.append((uint8_t)0);
+				}
+				break;
+
+			case P384:
+				b.append((uint8_t)P384);
+				b.append(_k.t1.pub,ZT_ECC384_PUBLIC_KEY_SIZE);
+				if ((_hasPrivate)&&(includePrivate)) {
+					b.append((uint8_t)ZT_ECC384_PRIVATE_KEY_SIZE);
+					b.append(_k.t1.priv,ZT_ECC384_PRIVATE_KEY_SIZE);
+				} else {
+					b.append((uint8_t)0);
+				}
+				break;
+
+		}
 	}
 
 	/**
@@ -237,27 +249,51 @@ public:
 	template<unsigned int C>
 	inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
 	{
-		delete _privateKey;
-		_privateKey = (C25519::Private *)0;
-
+		_hasPrivate = false;
 		unsigned int p = startAt;
+		unsigned int pkl;
 
 		_address.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
 		p += ZT_ADDRESS_LENGTH;
 
-		if (b[p++] != 0)
-			throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
-
-		memcpy(_publicKey.data,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN);
-		p += ZT_C25519_PUBLIC_KEY_LEN;
+		_type = (Type)b[p++];
+		switch(_type) {
+
+			case C25519:
+				memcpy(_k.t0.pub.data,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN);
+				p += ZT_C25519_PUBLIC_KEY_LEN;
+				pkl = (unsigned int)b[p++];
+				if (pkl) {
+					if (pkl != ZT_C25519_PRIVATE_KEY_LEN)
+						throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
+					_hasPrivate = true;
+					memcpy(_k.t0.priv.data,b.field(p,ZT_C25519_PRIVATE_KEY_LEN),ZT_C25519_PRIVATE_KEY_LEN);
+					p += ZT_C25519_PRIVATE_KEY_LEN;
+				} else {
+					memset(_k.t0.priv.data,0,ZT_C25519_PRIVATE_KEY_LEN);
+					_hasPrivate = false;
+				}
+				break;
+
+			case P384:
+				memcpy(_k.t0.pub.data,b.field(p,ZT_ECC384_PUBLIC_KEY_SIZE),ZT_ECC384_PUBLIC_KEY_SIZE);
+				p += ZT_ECC384_PUBLIC_KEY_SIZE;
+				pkl = (unsigned int)b[p++];
+				if (pkl) {
+					if (pkl != ZT_ECC384_PRIVATE_KEY_SIZE)
+						throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
+					_hasPrivate = true;
+					memcpy(_k.t1.priv,b.field(p,ZT_ECC384_PRIVATE_KEY_SIZE),ZT_ECC384_PRIVATE_KEY_SIZE);
+					p += ZT_ECC384_PRIVATE_KEY_SIZE;
+				} else {
+					memset(_k.t1.priv,0,ZT_ECC384_PRIVATE_KEY_SIZE);
+					_hasPrivate = false;
+				}
+				break;
+
+			default:
+				throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
 
-		unsigned int privateKeyLength = (unsigned int)b[p++];
-		if (privateKeyLength) {
-			if (privateKeyLength != ZT_C25519_PRIVATE_KEY_LEN)
-				throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
-			_privateKey = new C25519::Private();
-			memcpy(_privateKey->data,b.field(p,ZT_C25519_PRIVATE_KEY_LEN),ZT_C25519_PRIVATE_KEY_LEN);
-			p += ZT_C25519_PRIVATE_KEY_LEN;
 		}
 
 		return (p - startAt);
@@ -283,40 +319,64 @@ public:
 	 */
 	bool fromString(const char *str);
 
-	/**
-	 * @return C25519 public key
-	 */
-	inline const C25519::Public &publicKey() const { return _publicKey; }
-
-	/**
-	 * @return C25519 key pair (only returns valid pair if private key is present in this Identity object)
-	 */
-	inline const C25519::Pair privateKeyPair() const
-	{
-		C25519::Pair pair;
-		pair.pub = _publicKey;
-		if (_privateKey)
-			pair.priv = *_privateKey;
-		else memset(pair.priv.data,0,ZT_C25519_PRIVATE_KEY_LEN);
-		return pair;
-	}
-
 	/**
 	 * @return True if this identity contains something
 	 */
 	inline operator bool() const { return (_address); }
 
-	inline bool operator==(const Identity &id) const { return ((_address == id._address)&&(memcmp(_publicKey.data,id._publicKey.data,ZT_C25519_PUBLIC_KEY_LEN) == 0)); }
-	inline bool operator<(const Identity &id) const { return ((_address < id._address)||((_address == id._address)&&(memcmp(_publicKey.data,id._publicKey.data,ZT_C25519_PUBLIC_KEY_LEN) < 0))); }
+	inline bool operator==(const Identity &id) const
+	{
+		if ((_address == id._address)&&(_type == id._type)) {
+			switch(_type) {
+				case C25519:
+					return (memcmp(_k.t0.pub.data,id._k.t0.pub.data,ZT_C25519_PUBLIC_KEY_LEN) == 0);
+				case P384:
+					return (memcmp(_k.t1.pub,id._k.t1.pub,ZT_ECC384_PUBLIC_KEY_SIZE) == 0);
+				default:
+					return false;
+			}
+		}
+		return false;
+	}
+	inline bool operator<(const Identity &id) const
+	{
+		if (_address < id._address)
+			return true;
+		if (_address == id._address) {
+			if ((int)_type < (int)id._type)
+				return true;
+			if (_type == id._type) {
+				switch(_type) {
+					case C25519:
+						return (memcmp(_k.t0.pub.data,id._k.t0.pub.data,ZT_C25519_PUBLIC_KEY_LEN) < 0);
+					case P384:
+						return (memcmp(_k.t1.pub,id._k.t1.pub,ZT_ECC384_PUBLIC_KEY_SIZE) < 0);
+				}
+			}
+		}
+		return false;
+	}
 	inline bool operator!=(const Identity &id) const { return !(*this == id); }
 	inline bool operator>(const Identity &id) const { return (id < *this); }
 	inline bool operator<=(const Identity &id) const { return !(id < *this); }
 	inline bool operator>=(const Identity &id) const { return !(*this < id); }
 
+	inline unsigned long hashCode() const { return (unsigned long)_address.toInt(); }
+
 private:
 	Address _address;
-	C25519::Public _publicKey;
-	C25519::Private *_privateKey;
+	union {
+		struct {
+			C25519::Public pub;
+			C25519::Private priv;
+		} t0;
+		struct {
+			uint8_t pub[ZT_ECC384_PUBLIC_KEY_SIZE];
+			uint8_t priv[ZT_ECC384_PRIVATE_KEY_SIZE];
+		} t1;
+	} _k;
+	Type _type;
+	bool _hasPrivate;
 };
 
 } // namespace ZeroTier

+ 76 - 107
node/IncomingPacket.cpp

@@ -28,6 +28,8 @@
 #include <string.h>
 #include <stdlib.h>
 
+#include <list>
+
 #include "../version.h"
 #include "../include/ZeroTierOne.h"
 
@@ -41,7 +43,6 @@
 #include "SelfAwareness.hpp"
 #include "Salsa20.hpp"
 #include "SHA512.hpp"
-#include "World.hpp"
 #include "Node.hpp"
 #include "CertificateOfMembership.hpp"
 #include "Capability.hpp"
@@ -168,13 +169,14 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,void *tPtr,const Shar
 			break;
 
 		case Packet::ERROR_IDENTITY_COLLISION:
-			// FIXME: for federation this will need a payload with a signature or something.
+			// This is a trusted upstream telling us our 5-digit ID is taken. This
+			// causes the node to generate another.
 			if (RR->topology->isUpstream(peer->identity()))
 				RR->node->postEvent(tPtr,ZT_EVENT_FATAL_ERROR_IDENTITY_COLLISION);
 			break;
 
 		case Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE: {
-			// Peers can send this in response to frames if they do not have a recent enough COM from us
+			// Peers can send this to ask for a cert for a network.
 			networkId = at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD);
 			const SharedPtr<Network> network(RR->node->network(networkId));
 			const int64_t now = RR->node->now();
@@ -365,30 +367,6 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool
 			RR->sa->iam(tPtr,id.address(),_path->localSocket(),_path->address(),externalSurfaceAddress,RR->topology->isUpstream(id),now);
 	}
 
-	// Get primary planet world ID and world timestamp if present
-	uint64_t planetWorldId = 0;
-	uint64_t planetWorldTimestamp = 0;
-	if ((ptr + 16) <= size()) {
-		planetWorldId = at<uint64_t>(ptr); ptr += 8;
-		planetWorldTimestamp = at<uint64_t>(ptr); ptr += 8;
-	}
-
-	std::vector< std::pair<uint64_t,uint64_t> > moonIdsAndTimestamps;
-	if (ptr < size()) {
-		// Remainder of packet, if present, is encrypted
-		cryptField(peer->key(),ptr,size() - ptr);
-
-		// Get moon IDs and timestamps if present
-		if ((ptr + 2) <= size()) {
-			const unsigned int numMoons = at<uint16_t>(ptr); ptr += 2;
-			for(unsigned int i=0;i<numMoons;++i) {
-				if ((World::Type)(*this)[ptr++] == World::TYPE_MOON)
-					moonIdsAndTimestamps.push_back(std::pair<uint64_t,uint64_t>(at<uint64_t>(ptr),at<uint64_t>(ptr + 8)));
-				ptr += 16;
-			}
-		}
-	}
-
 	// Send OK(HELLO) with an echo of the packet's timestamp and some of the same
 	// information about us: version, sent-to address, etc.
 
@@ -432,25 +410,6 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool
 		tmpa.serialize(outp);
 	}
 
-	const unsigned int worldUpdateSizeAt = outp.size();
-	outp.addSize(2); // make room for 16-bit size field
-	if ((planetWorldId)&&(RR->topology->planetWorldTimestamp() > planetWorldTimestamp)&&(planetWorldId == RR->topology->planetWorldId())) {
-		RR->topology->planet().serialize(outp,false);
-	}
-	if (moonIdsAndTimestamps.size() > 0) {
-		std::vector<World> moons(RR->topology->moons());
-		for(std::vector<World>::const_iterator m(moons.begin());m!=moons.end();++m) {
-			for(std::vector< std::pair<uint64_t,uint64_t> >::const_iterator i(moonIdsAndTimestamps.begin());i!=moonIdsAndTimestamps.end();++i) {
-				if (i->first == m->id()) {
-					if (m->timestamp() > i->second)
-						m->serialize(outp,false);
-					break;
-				}
-			}
-		}
-	}
-	outp.setAt<uint16_t>(worldUpdateSizeAt,(uint16_t)(outp.size() - (worldUpdateSizeAt + 2)));
-
 	outp.armor(peer->key(),true);
 	_path->send(RR,tPtr,outp.data(),outp.size(),now);
 
@@ -486,22 +445,6 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedP
 			// Get reported external surface address if present
 			if (ptr < size())
 				ptr += externalSurfaceAddress.deserialize(*this,ptr);
-
-			// Handle planet or moon updates if present
-			if ((ptr + 2) <= size()) {
-				const unsigned int worldsLen = at<uint16_t>(ptr); ptr += 2;
-				if (RR->topology->shouldAcceptWorldUpdateFrom(peer->address())) {
-					const unsigned int endOfWorlds = ptr + worldsLen;
-					while (ptr < endOfWorlds) {
-						World w;
-						ptr += w.deserialize(*this,ptr);
-						RR->topology->addWorld(tPtr,w,false);
-					}
-				} else {
-					ptr += worldsLen;
-				}
-			}
-
 			if (!hops()) {
 				_path->updateLatency((unsigned int)latency,RR->node->now());
 			}
@@ -572,8 +515,9 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedP
 
 bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
 {
-	if ((!RR->topology->amUpstream())&&(!peer->rateGateInboundWhoisRequest(RR->node->now())))
-		return true;
+	// TODO
+	//if ((!RR->topology->amUpstream())&&(!peer->rateGateInboundWhoisRequest(RR->node->now())))
+	//	return true;
 
 	Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
 	outp.append((unsigned char)Packet::VERB_WHOIS);
@@ -769,8 +713,8 @@ bool IncomingPacket::_doMULTICAST_LIKE(const RuntimeEnvironment *RR,void *tPtr,c
 			SharedPtr<Network> network(RR->node->network(nwid));
 			if (network)
 				authorized = network->gate(tPtr,peer);
-			if (!authorized)
-				authorized = ((RR->topology->amUpstream())||(RR->node->localControllerHasAuthorized(now,nwid,peer->address())));
+			//if (!authorized)
+			//	authorized = ((RR->topology->amUpstream())||(RR->node->localControllerHasAuthorized(now,nwid,peer->address())));
 		}
 		if (authorized)
 			RR->mc->add(tPtr,now,nwid,MulticastGroup(MAC(field(ptr + 8,6),6),at<uint32_t>(ptr + 14)),peer->address());
@@ -979,7 +923,8 @@ bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,void *tPtr
 	}
 
 	const int64_t now = RR->node->now();
-	if ((gatherLimit > 0)&&((trustEstablished)||(RR->topology->amUpstream())||(RR->node->localControllerHasAuthorized(now,nwid,peer->address())))) {
+	//if ((gatherLimit > 0)&&((trustEstablished)||(RR->topology->amUpstream())||(RR->node->localControllerHasAuthorized(now,nwid,peer->address())))) {
+	if (gatherLimit) {
 		Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
 		outp.append((unsigned char)Packet::VERB_MULTICAST_GATHER);
 		outp.append(packetId());
@@ -1000,18 +945,16 @@ bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,void *tPtr
 
 bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
 {
-	const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_NETWORK_ID);
-	const unsigned int flags = (*this)[ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FLAGS];
+	unsigned int offset = ZT_PACKET_IDX_PAYLOAD;
+	const uint64_t nwid = at<uint64_t>(offset); offset += 8;
+	const unsigned int flags = (*this)[offset]; ++offset;
 
 	const SharedPtr<Network> network(RR->node->network(nwid));
 	if (network) {
-		// Offset -- size of optional fields added to position of later fields
-		unsigned int offset = 0;
-
 		if ((flags & 0x01) != 0) {
 			// This is deprecated but may still be sent by old peers
 			CertificateOfMembership com;
-			offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME_IDX_COM);
+			offset += com.deserialize(*this,offset);
 			if (com)
 				network->addCredential(tPtr,com);
 		}
@@ -1023,60 +966,87 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,void *tPtr,
 
 		unsigned int gatherLimit = 0;
 		if ((flags & 0x02) != 0) {
-			gatherLimit = at<uint32_t>(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_GATHER_LIMIT);
-			offset += 4;
+			gatherLimit = at<uint32_t>(offset); offset += 4;
 		}
 
 		MAC from;
 		if ((flags & 0x04) != 0) {
-			from.setTo(field(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_SOURCE_MAC,6),6);
-			offset += 6;
+			from.setTo(field(offset,6),6); offset += 6;
 		} else {
 			from.fromAddress(peer->address(),nwid);
 		}
 
-		const MulticastGroup to(MAC(field(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_DEST_MAC,6),6),at<uint32_t>(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_DEST_ADI));
-		const unsigned int etherType = at<uint16_t>(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_ETHERTYPE);
-		const unsigned int frameLen = size() - (offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME);
+		const unsigned int recipientsOffset = offset;
+		std::list<Address> recipients;
+		if ((flags & 0x08) != 0) {
+			const unsigned int rc = at<uint16_t>(offset); offset += 2;
+			for(unsigned int i=0;i<rc;++i) {
+				const Address a(field(offset,5),5);
+				if ((a != peer->address())&&(a != RR->identity.address())) {
+					recipients.push_back(a);
+				}
+				offset += 5;
+			}
+		}
+		const unsigned int afterRecipientsOffset = offset;
+
+		const MulticastGroup to(MAC(field(offset,6),6),at<uint32_t>(offset + 6)); offset += 10;
+		const unsigned int etherType = at<uint16_t>(offset); offset += 2;
+		const unsigned int frameLen = size() - offset;
 
 		if (network->config().multicastLimit == 0) {
 			RR->t->incomingNetworkFrameDropped(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_MULTICAST_FRAME,from,to.mac(),"multicast disabled");
-			peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,false,nwid);
+			peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true,nwid);
+			return true;
+		}
+		if (!to.mac().isMulticast()) {
+			RR->t->incomingPacketInvalid(tPtr,_path,packetId(),source(),hops(),Packet::VERB_MULTICAST_FRAME,"destination not multicast");
+			peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true,nwid);
+			return true;
+		}
+		if ((!from)||(from.isMulticast())||(from == network->mac())) {
+			RR->t->incomingPacketInvalid(tPtr,_path,packetId(),source(),hops(),Packet::VERB_MULTICAST_FRAME,"invalid source MAC");
+			peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true,nwid);
 			return true;
 		}
 
 		if ((frameLen > 0)&&(frameLen <= ZT_MAX_MTU)) {
-			if (!to.mac().isMulticast()) {
-				RR->t->incomingPacketInvalid(tPtr,_path,packetId(),source(),hops(),Packet::VERB_MULTICAST_FRAME,"destination not multicast");
-				peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
-				return true;
-			}
-			if ((!from)||(from.isMulticast())||(from == network->mac())) {
-				RR->t->incomingPacketInvalid(tPtr,_path,packetId(),source(),hops(),Packet::VERB_MULTICAST_FRAME,"invalid source MAC");
-				peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
-				return true;
+			const uint8_t *const frameData = ((const uint8_t *)unsafeData()) + offset;
+			if (network->filterIncomingPacket(tPtr,peer,RR->identity.address(),from,to.mac(),frameData,frameLen,etherType,0) > 0) {
+				RR->node->putFrame(tPtr,nwid,network->userPtr(),from,to.mac(),etherType,0,(const void *)frameData,frameLen);
 			}
+		}
 
-			const uint8_t *const frameData = (const uint8_t *)field(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME,frameLen);
-
-			if ((flags & 0x08)&&(network->config().isMulticastReplicator(RR->identity.address())))
-				RR->mc->send(tPtr,RR->node->now(),network,peer->address(),to,from,etherType,frameData,frameLen);
+		if (!recipients.empty()) {
+			const std::vector<Address> anchors = network->config().anchors();
+			const bool amAnchor = (std::find(anchors.begin(),anchors.end(),RR->identity.address()) != anchors.end());
+
+			for(std::list<Address>::iterator ra(recipients.begin());ra!=recipients.end();) {
+				SharedPtr<Peer> recipient(RR->topology->getPeer(tPtr,*ra));
+				if ((recipient)&&((recipient->remoteVersionProtocol() < 10)||(amAnchor))) {
+					Packet outp(*ra,RR->identity.address(),Packet::VERB_MULTICAST_FRAME);
+					outp.append(field(ZT_PACKET_IDX_PAYLOAD,recipientsOffset - ZT_PACKET_IDX_PAYLOAD),recipientsOffset - ZT_PACKET_IDX_PAYLOAD);
+					outp.append(field(afterRecipientsOffset,size() - afterRecipientsOffset),size() - afterRecipientsOffset);
+					RR->sw->send(tPtr,outp,true);
+					recipients.erase(ra++);
+				} else ++ra;
+			}
 
-			if (from != MAC(peer->address(),nwid)) {
-				if (network->config().permitsBridging(peer->address())) {
-					network->learnBridgeRoute(from,peer->address());
-				} else {
-					RR->t->incomingNetworkFrameDropped(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_MULTICAST_FRAME,from,to.mac(),"bridging not allowed (remote)");
-					peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
-					return true;
+			if (!recipients.empty()) {
+				Packet outp(recipients.front(),RR->identity.address(),Packet::VERB_MULTICAST_FRAME);
+				recipients.pop_front();
+				outp.append(field(ZT_PACKET_IDX_PAYLOAD,recipientsOffset - ZT_PACKET_IDX_PAYLOAD),recipientsOffset - ZT_PACKET_IDX_PAYLOAD);
+				if (!recipients.empty()) {
+					outp.append((uint16_t)recipients.size());
+					for(std::list<Address>::iterator ra(recipients.begin());ra!=recipients.end();++ra)
+						ra->appendTo(outp);
 				}
+				outp.append(field(afterRecipientsOffset,size() - afterRecipientsOffset),size() - afterRecipientsOffset);
+				RR->sw->send(tPtr,outp,true);
 			}
-
-			if (network->filterIncomingPacket(tPtr,peer,RR->identity.address(),from,to.mac(),frameData,frameLen,etherType,0) > 0)
-				RR->node->putFrame(tPtr,nwid,network->userPtr(),from,to.mac(),etherType,0,(const void *)frameData,frameLen);
 		}
 
-		if (gatherLimit) {
+		if (gatherLimit) { // DEPRECATED but still supported
 			Packet outp(source(),RR->identity.address(),Packet::VERB_OK);
 			outp.append((unsigned char)Packet::VERB_MULTICAST_FRAME);
 			outp.append(packetId());
@@ -1091,12 +1061,11 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,void *tPtr,
 		}
 
 		peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true,nwid);
+		return true;
 	} else {
 		_sendErrorNeedCredentials(RR,tPtr,peer,nwid);
 		return false;
 	}
-
-	return true;
 }
 
 bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)

+ 257 - 0
node/Locator.hpp

@@ -0,0 +1,257 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2019  ZeroTier, Inc.  https://www.zerotier.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#ifndef ZT_LOCATOR_HPP
+#define ZT_LOCATOR_HPP
+
+#include "Constants.hpp"
+#include "Identity.hpp"
+#include "InetAddress.hpp"
+#include "Utils.hpp"
+#include "Buffer.hpp"
+#include "SHA512.hpp"
+#include "Str.hpp"
+
+#include <algorithm>
+#include <vector>
+
+#define ZT_LOCATOR_MAX_PHYSICAL_ADDRESSES 255
+#define ZT_LOCATOR_MAX_VIRTUAL_ADDRESSES 255
+
+namespace ZeroTier {
+
+/**
+ * Signed information about a node's location on the network
+ * 
+ * A locator can be stored in DNS as a series of TXT records with a DNS name
+ * that includes a public key that can be used to validate the locator's
+ * signature. That way DNS records can't be spoofed even if no DNSSEC or
+ * anything else is present to secure DNS.
+ */
+class Locator
+{
+public:
+	Locator() : _signatureLength(0) {}
+
+	inline const std::vector<InetAddress> &phy() const { return _physical; }
+	inline const std::vector<Identity> &virt() const { return _virtual; }
+
+	inline void add(const InetAddress &ip)
+	{
+		if (_physical.size() < ZT_LOCATOR_MAX_PHYSICAL_ADDRESSES)
+			_physical.push_back(ip);
+	}
+	inline void add(const Identity &zt)
+	{
+		if (_virtual.size() < ZT_LOCATOR_MAX_VIRTUAL_ADDRESSES)
+			_virtual.push_back(zt);
+	}
+
+	inline void finish(const Identity &id,const int64_t ts)
+	{
+		_ts = ts;
+		_id = id;
+		std::sort(_physical.begin(),_physical.end());
+		_physical.erase(std::unique(_physical.begin(),_physical.end()),_physical.end());
+		std::sort(_virtual.begin(),_virtual.end());
+		_virtual.erase(std::unique(_virtual.begin(),_virtual.end()),_virtual.end());
+	}
+
+	inline bool sign(const Identity &signingId)
+	{
+		if (!signingId.hasPrivate())
+			return false;
+		if (signingId == _id) {
+			_signedBy.zero();
+		} else {
+			_signedBy = signingId;
+		}
+		Buffer<65536> *tmp = new Buffer<65536>();
+		try {
+			serialize(*tmp,true);
+			_signatureLength = signingId.sign(tmp->data(),tmp->size(),_signature,ZT_SIGNATURE_BUFFER_SIZE);
+			delete tmp;
+			return (_signatureLength > 0);
+		} catch ( ... ) {
+			delete tmp;
+			return false;
+		}
+	}
+
+	inline bool verify() const
+	{
+		if ((_signatureLength == 0)||(_signatureLength > sizeof(_signature)))
+			return false;
+		Buffer<65536> *tmp = nullptr;
+		try {
+			tmp = new Buffer<65536>();
+			serialize(*tmp,true);
+			const bool ok = (_signedBy) ? _signedBy.verify(tmp->data(),tmp->size(),_signature,_signatureLength) : _id.verify(tmp->data(),tmp->size(),_signature,_signatureLength);
+			delete tmp;
+			return ok;
+		} catch ( ... ) {
+			if (tmp) delete tmp;
+			return false;
+		}
+	}
+
+	inline std::vector<Str> makeTxtRecords(const uint8_t p384SigningKeyPublic[ZT_ECC384_PUBLIC_KEY_SIZE],const uint8_t p384SigningKeyPrivate[ZT_ECC384_PUBLIC_KEY_SIZE])
+	{
+		uint8_t s384[48],dnsSig[ZT_ECC384_SIGNATURE_SIZE];
+		char enc[256];
+
+		Buffer<65536> *const tmp = new Buffer<65536>();
+		serialize(*tmp,false);
+		SHA384(s384,tmp->data(),tmp->size());
+		ECC384ECDSASign(p384SigningKeyPrivate,s384,dnsSig);
+		tmp->append(dnsSig,ZT_ECC384_SIGNATURE_SIZE);
+
+		// Blob must be broken into multiple TXT records that must remain sortable so they are prefixed by a hex value.
+		// 186-byte chunks yield 248-byte base64 chunks which leaves some margin below the limit of 255.
+		std::vector<Str> txtRecords;
+		for(unsigned int p=0;p<tmp->size();p+=186) {
+			unsigned int rem = tmp->size() - p;
+			if (rem > 186) rem = 186;
+			Utils::b64e(((const uint8_t *)tmp->data()) + p,rem,enc,sizeof(enc));
+			txtRecords.push_back(Str());
+			txtRecords.back() << Utils::HEXCHARS[(p >> 4) & 0xf] << Utils::HEXCHARS[p & 0xf] << enc;
+		}
+
+		delete tmp;
+		return txtRecords;
+	}
+
+	template<typename I>
+	inline bool decodeTxtRecords(I start,I end,const uint8_t p384SigningKeyPublic[ZT_ECC384_PUBLIC_KEY_SIZE])
+	{
+		uint8_t dec[256],s384[48];
+		Buffer<65536> *tmp = nullptr;
+		try {
+			tmp = new Buffer<65536>();
+			while (start != end) {
+				tmp->append(dec,Utils::b64d(start->c_str(),dec,sizeof(dec)));
+				++start;
+			}
+
+			if (tmp->size() <= ZT_ECC384_SIGNATURE_SIZE) {
+				delete tmp;
+				return false;
+			}
+			SHA384(s384,tmp->data(),tmp->size() - ZT_ECC384_SIGNATURE_SIZE);
+			if (!ECC384ECDSAVerify(p384SigningKeyPublic,s384,((const uint8_t *)tmp->data()) + (tmp->size() - ZT_ECC384_SIGNATURE_SIZE))) {
+				delete tmp;
+				return false;
+			}
+
+			deserialize(*tmp,0);
+
+			delete tmp;
+			return verify();
+		} catch ( ... ) {
+			if (tmp) delete tmp;
+			return false;
+		}
+	}
+
+	template<unsigned int C>
+	inline void serialize(Buffer<C> &b,const bool forSign = false) const
+	{
+		if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
+
+		b.append((uint8_t)0); // version/flags, currently 0
+		b.append((uint64_t)_ts);
+		_id.serialise(b,false);
+		if (_signedBy) {
+			b.append((uint8_t)1); // number of signers, current max is 1
+			_signedBy.serialize(b,false);
+		} else {
+			b.append((uint8_t)0); // signer is _id
+		}
+		b.append((uint8_t)_physical.size());
+		for(std::vector<InetAddress>::const_iterator i(_physical.begin());i!=_physical.end();++i)
+			i->serialize(b);
+		b.append((uint8_t)_virtual.size());
+		for(std::vector<Identity>::const_iterator i(_virtual.begin());i!=_virtual.end();++i)
+			i->serialize(b,false);
+		if (!forSign) {
+			b.append((uint16_t)_signatureLength);
+			b.append(_signature,_signatureLength);
+		}
+		b.append((uint16_t)0); // length of additional fields, currently 0
+
+		if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
+	}
+
+	template<unsigned int C>
+	inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
+	{
+		unsigned int p = startAt;
+
+		if (b[p++] != 0)
+			throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
+		_ts = (int64_t)b.template at<uint64_t>(p); p += 8;
+		p += _id.deserialize(b,p);
+		const unsigned int signerCount = b[p++];
+		if (signerCount > 1)
+			throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
+		if (signerCount == 1) {
+			p += _signedBy.deserialize(b,p);
+		} else {
+			_signedBy.zero();
+		}
+		const unsigned int physicalCount = b[p++];
+		_physical.resize(physicalCount);
+		for(unsigned int i=0;i<physicalCount;++i)
+			p += _physical[i].deserialize(b,p);
+		const unsigned int virtualCount = b[p++];
+		_virtual.resize(virtualCount);
+		for(unsigned int i=0;i<virtualCount;++i)
+			p += _virtual[i].deserialize(b,p);
+		_signatureLen = b.template at<uint16_t>(p); p += 2;
+		if (_signatureLength > ZT_SIGNATURE_BUFFER_SIZE)
+			throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
+		memcpy(_signature,b.field(p,_signatureLength),_signatureLength);
+		p += _signatureLength;
+		p += b.template at<uint16_t>(p); p += 2;
+		if (p > b.size())
+			throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
+
+		return (p - startAt);
+	}
+
+private:
+	int64_t _ts;
+	Identity _id;
+	Identity _signedBy; // signed by _id if nil/zero
+	std::vector<InetAddress> _physical;
+	std::vector<Identity> _virtual;
+	unsigned int _signatureLength;
+	uint8_t _signature[ZT_SIGNATURE_BUFFER_SIZE];
+};
+
+} // namespace ZeroTier
+
+#endif

+ 24 - 32
node/Membership.hpp

@@ -108,9 +108,9 @@ public:
 	 */
 	inline bool isAllowedOnNetwork(const NetworkConfig &nconf) const
 	{
-		if (nconf.isPublic()) return true;
-		if (_com.timestamp() <= _comRevocationThreshold) return false;
-		return nconf.com.agreesWith(_com);
+		if (nconf.isPublic()) return true; // public network
+		if (_com.timestamp() <= _comRevocationThreshold) return false; // COM has been revoked
+		return nconf.com.agreesWith(_com); // check timestamp agreement window
 	}
 
 	inline bool recentlyAssociated(const int64_t now) const
@@ -119,7 +119,7 @@ public:
 	}
 
 	/**
-	 * Check whether the peer represented by this Membership owns a given resource
+	 * Check whether the peer represented by this Membership owns a given address
 	 *
 	 * @tparam Type of resource: InetAddress or MAC
 	 * @param nconf Our network config
@@ -127,8 +127,10 @@ public:
 	 * @return True if this peer has a certificate of ownership for the given resource
 	 */
 	template<typename T>
-	inline bool hasCertificateOfOwnershipFor(const NetworkConfig &nconf,const T &r) const
+	inline bool peerOwnsAddress(const NetworkConfig &nconf,const T &r) const
 	{
+		if (_isUnspoofableAddress(nconf,r))
+			return true;
 		uint32_t *k = (uint32_t *)0;
 		CertificateOfOwnership *v = (CertificateOfOwnership *)0;
 		Hashtable< uint32_t,CertificateOfOwnership >::Iterator i(*(const_cast< Hashtable< uint32_t,CertificateOfOwnership> *>(&_remoteCoos)));
@@ -136,7 +138,7 @@ public:
 			if (_isCredentialTimestampValid(nconf,*v)&&(v->owns(r)))
 				return true;
 		}
-		return _isV6NDPEmulated(nconf,r);
+		return false;
 	}
 
 	/**
@@ -152,29 +154,10 @@ public:
 		return (((t)&&(_isCredentialTimestampValid(nconf,*t))) ? t : (Tag *)0);
 	}
 
-	/**
-	 * Validate and add a credential if signature is okay and it's otherwise good
-	 */
 	AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const CertificateOfMembership &com);
-
-	/**
-	 * Validate and add a credential if signature is okay and it's otherwise good
-	 */
 	AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Tag &tag);
-
-	/**
-	 * Validate and add a credential if signature is okay and it's otherwise good
-	 */
 	AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Capability &cap);
-
-	/**
-	 * Validate and add a credential if signature is okay and it's otherwise good
-	 */
 	AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const CertificateOfOwnership &coo);
-
-	/**
-	 * Validate and add a credential if signature is okay and it's otherwise good
-	 */
 	AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Revocation &rev);
 
 	/**
@@ -186,20 +169,29 @@ public:
 	void clean(const int64_t now,const NetworkConfig &nconf);
 
 	/**
-	 * Generates a key for the internal use in indexing credentials by type and credential ID
+	 * Generates a key for internal use in indexing credentials by type and credential ID
 	 */
 	static uint64_t credentialKey(const Credential::Type &t,const uint32_t i) { return (((uint64_t)t << 32) | (uint64_t)i); }
 
 private:
-	inline bool _isV6NDPEmulated(const NetworkConfig &nconf,const MAC &m) const { return false; }
-	inline bool _isV6NDPEmulated(const NetworkConfig &nconf,const InetAddress &ip) const
+	// This returns true if a resource is an IPv6 NDP-emulated address. These embed the ZT
+	// address of the peer and therefore cannot be spoofed, causing peerOwnsAddress() to
+	// always return true for them. A certificate is not required for these.
+	inline bool _isUnspoofableAddress(const NetworkConfig &nconf,const MAC &m) const { return false; }
+	inline bool _isUnspoofableAddress(const NetworkConfig &nconf,const InetAddress &ip) const
 	{
-		if ((ip.isV6())&&(nconf.ndpEmulation())&&((InetAddress::makeIpv66plane(nconf.networkId,nconf.issuedTo.toInt()).ipsEqual(ip))||(InetAddress::makeIpv6rfc4193(nconf.networkId,nconf.issuedTo.toInt()).ipsEqual(ip)))) {
-			return true;
-		}
-		return false;
+		return (
+			(ip.ss_family == AF_INET6)&&
+			(nconf.ndpEmulation())&&
+			(
+				(InetAddress::makeIpv66plane(nconf.networkId,nconf.issuedTo.toInt()).ipsEqual(ip))||
+				(InetAddress::makeIpv6rfc4193(nconf.networkId,nconf.issuedTo.toInt()).ipsEqual(ip))
+			)
+		);
 	}
 
+	// This compares the remote credential's timestamp to the timestamp in our network config
+	// plus or minus the permitted maximum timestamp delta.
 	template<typename C>
 	inline bool _isCredentialTimestampValid(const NetworkConfig &nconf,const C &remoteCredential) const
 	{

+ 1 - 0
node/MulticastGroup.hpp

@@ -31,6 +31,7 @@
 
 #include "MAC.hpp"
 #include "InetAddress.hpp"
+#include "Utils.hpp"
 
 namespace ZeroTier {
 

+ 0 - 45
node/Multicaster.cpp

@@ -170,51 +170,6 @@ void Multicaster::send(
 	unsigned long idxbuf[4096];
 	unsigned long *indexes = idxbuf;
 
-	// If we're in hub-and-spoke designated multicast replication mode, see if we
-	// have a multicast replicator active. If so, pick the best and send it
-	// there. If we are a multicast replicator or if none are alive, fall back
-	// to sender replication. Note that bridges do not do this since this would
-	// break bridge route learning. This is sort of an edge case limitation of
-	// the current protocol and could be fixed, but fixing it would add more
-	// complexity than the fix is probably worth. Bridges are generally high
-	// bandwidth nodes.
-	if (!network->config().isActiveBridge(RR->identity.address())) {
-		Address multicastReplicators[ZT_MAX_NETWORK_SPECIALISTS];
-		const unsigned int multicastReplicatorCount = network->config().multicastReplicators(multicastReplicators);
-		if (multicastReplicatorCount) {
-			if (std::find(multicastReplicators,multicastReplicators + multicastReplicatorCount,RR->identity.address()) == (multicastReplicators + multicastReplicatorCount)) {
-				SharedPtr<Peer> bestMulticastReplicator;
-				SharedPtr<Path> bestMulticastReplicatorPath;
-				unsigned int bestMulticastReplicatorLatency = 0xffff;
-				for(unsigned int i=0;i<multicastReplicatorCount;++i) {
-					const SharedPtr<Peer> p(RR->topology->getPeerNoCache(multicastReplicators[i]));
-					if ((p)&&(p->isAlive(now))) {
-						const SharedPtr<Path> pp(p->getAppropriatePath(now,false));
-						if ((pp)&&(pp->latency() < bestMulticastReplicatorLatency)) {
-							bestMulticastReplicatorLatency = pp->latency();
-							bestMulticastReplicatorPath = pp;
-							bestMulticastReplicator = p;
-						}
-					}
-				}
-				if (bestMulticastReplicator) {
-					Packet outp(bestMulticastReplicator->address(),RR->identity.address(),Packet::VERB_MULTICAST_FRAME);
-					outp.append((uint64_t)network->id());
-					outp.append((uint8_t)0x0c); // includes source MAC | please replicate
-					((src) ? src : MAC(RR->identity.address(),network->id())).appendTo(outp);
-					mg.mac().appendTo(outp);
-					outp.append((uint32_t)mg.adi());
-					outp.append((uint16_t)etherType);
-					outp.append(data,len);
-					if (!network->config().disableCompression()) outp.compress();
-					outp.armor(bestMulticastReplicator->key(),true);
-					bestMulticastReplicatorPath->send(RR,tPtr,outp.data(),outp.size(),now);
-					return;
-				}
-			}
-		}
-	}
-
 	try {
 		Mutex::Lock _l(_groups_m);
 		MulticastGroupStatus &gs = _groups[Multicaster::Key(network->id(),mg)];

+ 3 - 17
node/Network.cpp

@@ -399,9 +399,9 @@ static _doZtFilterResult _doZtFilter(
 					}
 					if (inbound) {
 						if (membership) {
-							if ((src)&&(membership->hasCertificateOfOwnershipFor<InetAddress>(nconf,src)))
+							if ((src)&&(membership->peerOwnsAddress<InetAddress>(nconf,src)))
 								ownershipVerificationMask |= ZT_RULE_PACKET_CHARACTERISTICS_SENDER_IP_AUTHENTICATED;
-							if (membership->hasCertificateOfOwnershipFor<MAC>(nconf,macSource))
+							if (membership->peerOwnsAddress<MAC>(nconf,macSource))
 								ownershipVerificationMask |= ZT_RULE_PACKET_CHARACTERISTICS_SENDER_MAC_AUTHENTICATED;
 						}
 					} else {
@@ -1467,22 +1467,8 @@ void Network::_sendUpdatesToMembers(void *tPtr,const MulticastGroup *const newMu
 		}
 		std::sort(alwaysAnnounceTo.begin(),alwaysAnnounceTo.end());
 
-		for(std::vector<Address>::const_iterator a(alwaysAnnounceTo.begin());a!=alwaysAnnounceTo.end();++a) {
-			/*
-			// push COM to non-members so they can do multicast request auth
-			if ( (_config.com) && (!_memberships.contains(*a)) && (*a != RR->identity.address()) ) {
-				Packet outp(*a,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
-				_config.com.serialize(outp);
-				outp.append((uint8_t)0x00);
-				outp.append((uint16_t)0); // no capabilities
-				outp.append((uint16_t)0); // no tags
-				outp.append((uint16_t)0); // no revocations
-				outp.append((uint16_t)0); // no certificates of ownership
-				RR->sw->send(tPtr,outp,true);
-			}
-			*/
+		for(std::vector<Address>::const_iterator a(alwaysAnnounceTo.begin());a!=alwaysAnnounceTo.end();++a)
 			_announceMulticastGroupsTo(tPtr,*a,groups);
-		}
 	}
 
 	{

+ 3 - 37
node/NetworkConfig.hpp

@@ -98,11 +98,6 @@
  */
 #define ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR 0x0000040000000000ULL
 
-/**
- * Designated multicast replicators replicate multicast in place of sender-side replication
- */
-#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR 0x0000080000000000ULL
-
 namespace ZeroTier {
 
 // Dictionary capacity needed for max size network config
@@ -339,40 +334,11 @@ public:
 		return r;
 	}
 
-	inline std::vector<Address> multicastReplicators() const
-	{
-		std::vector<Address> r;
-		for(unsigned int i=0;i<specialistCount;++i) {
-			if ((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR) != 0)
-				r.push_back(Address(specialists[i]));
-		}
-		return r;
-	}
-
-	inline unsigned int multicastReplicators(Address mr[ZT_MAX_NETWORK_SPECIALISTS]) const
-	{
-		unsigned int c = 0;
-		for(unsigned int i=0;i<specialistCount;++i) {
-			if ((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR) != 0)
-				mr[c++] = specialists[i];
-		}
-		return c;
-	}
-
-	inline bool isMulticastReplicator(const Address &a) const
-	{
-		for(unsigned int i=0;i<specialistCount;++i) {
-			if (((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR) != 0)&&(a == specialists[i]))
-				return true;
-		}
-		return false;
-	}
-
 	inline std::vector<Address> alwaysContactAddresses() const
 	{
 		std::vector<Address> r;
 		for(unsigned int i=0;i<specialistCount;++i) {
-			if ((specialists[i] & (ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR | ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR)) != 0)
+			if ((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR) != 0)
 				r.push_back(Address(specialists[i]));
 		}
 		return r;
@@ -382,7 +348,7 @@ public:
 	{
 		unsigned int c = 0;
 		for(unsigned int i=0;i<specialistCount;++i) {
-			if ((specialists[i] & (ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR | ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR)) != 0)
+			if ((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR) != 0)
 				ac[c++] = specialists[i];
 		}
 		return c;
@@ -391,7 +357,7 @@ public:
 	inline void alwaysContactAddresses(Hashtable< Address,std::vector<InetAddress> > &a) const
 	{
 		for(unsigned int i=0;i<specialistCount;++i) {
-			if ((specialists[i] & (ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR | ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR)) != 0) {
+			if ((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR) != 0) {
 				a[Address(specialists[i])];
 			}
 		}

+ 22 - 80
node/Node.cpp

@@ -93,7 +93,7 @@ Node::Node(void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,int64
 	}
 
 	if (n <= 0) {
-		RR->identity.generate();
+		RR->identity.generate(Identity::C25519);
 		RR->identity.toString(false,RR->publicIdentityStr);
 		RR->identity.toString(true,RR->secretIdentityStr);
 		idtmp[0] = RR->identity.address().toInt(); idtmp[1] = 0;
@@ -192,16 +192,17 @@ ZT_ResultCode Node::processVirtualNetworkFrame(
 	} else return ZT_RESULT_ERROR_NETWORK_NOT_FOUND;
 }
 
-// Closure used to ping upstream and active/online peers
-class _PingPeersThatNeedPing
+// Function object used to traverse the peer list, check peer status, and ping
+// those that need pinging.
+struct _PingPeersThatNeedPing
 {
-public:
 	_PingPeersThatNeedPing(const RuntimeEnvironment *renv,void *tPtr,Hashtable< Address,std::vector<InetAddress> > &alwaysContact,int64_t now) :
 		RR(renv),
 		_tPtr(tPtr),
 		_alwaysContact(alwaysContact),
 		_now(now),
-		_bestCurrentUpstream(RR->topology->getUpstreamPeer())
+		_bestCurrentUpstream(RR->topology->getUpstreamPeer()),
+		online(false)
 	{
 	}
 
@@ -209,6 +210,7 @@ public:
 	{
 		const std::vector<InetAddress> *const alwaysContactEndpoints = _alwaysContact.get(p->address());
 		if (alwaysContactEndpoints) {
+			online |= p->isAlive(_now);
 			const unsigned int sent = p->doPingAndKeepalive(_tPtr,_now);
 			bool contacted = (sent != 0);
 
@@ -246,12 +248,13 @@ public:
 		}
 	}
 
-private:
 	const RuntimeEnvironment *RR;
 	void *_tPtr;
 	Hashtable< Address,std::vector<InetAddress> > &_alwaysContact;
 	const int64_t _now;
 	const SharedPtr<Peer> _bestCurrentUpstream;
+
+	bool online;
 };
 
 ZT_ResultCode Node::processBackgroundTasks(void *tptr,int64_t now,volatile int64_t *nextBackgroundTaskDeadline)
@@ -265,47 +268,25 @@ ZT_ResultCode Node::processBackgroundTasks(void *tptr,int64_t now,volatile int64
 		try {
 			_lastPingCheck = now;
 
-			// Get designated VL1 upstreams
-			Hashtable< Address,std::vector<InetAddress> > alwaysContact;
-			RR->topology->getUpstreamsToContact(alwaysContact);
-
-			// Uncomment to dump stats
-			/*
-			for(unsigned int i=0;i<32;i++) {
-				if (_stats.inVerbCounts[i] > 0)
-					printf("%.2x\t%12lld %lld\n",i,(unsigned long long)_stats.inVerbCounts[i],(unsigned long long)_stats.inVerbBytes[i]);
-			}
-			printf("\n");
-			*/
-
-			// Check last receive time on designated upstreams to see if we seem to be online
-			int64_t lastReceivedFromUpstream = 0;
-			{
-				Hashtable< Address,std::vector<InetAddress> >::Iterator i(alwaysContact);
-				Address *upstreamAddress = (Address *)0;
-				std::vector<InetAddress> *upstreamStableEndpoints = (std::vector<InetAddress> *)0;
-				while (i.next(upstreamAddress,upstreamStableEndpoints)) {
-					SharedPtr<Peer> p(RR->topology->getPeerNoCache(*upstreamAddress));
-					if (p)
-						lastReceivedFromUpstream = std::max(p->lastReceive(),lastReceivedFromUpstream);
-				}
-			}
-
-			// Clean up any old local controller auth memorizations.
+			// Clean up any old local controller auth memoizations. This is an
+			// optimization for network controllers to know whether to accept
+			// or trust nodes without doing an extra cert check.
 			{
 				_localControllerAuthorizations_m.lock();
 				Hashtable< _LocalControllerAuth,int64_t >::Iterator i(_localControllerAuthorizations);
 				_LocalControllerAuth *k = (_LocalControllerAuth *)0;
 				int64_t *v = (int64_t *)0;
 				while (i.next(k,v)) {
-					if ((*v - now) > (ZT_NETWORK_AUTOCONF_DELAY * 3))
+					if ((*v - now) > (ZT_NETWORK_AUTOCONF_DELAY * 3)) {
 						_localControllerAuthorizations.erase(*k);
+					}
 				}
 				_localControllerAuthorizations_m.unlock();
 			}
 
-			// Get peers we should stay connected to according to network configs
-			// Also get networks and whether they need config so we only have to do one pass over networks
+			// (1) Get peers we should remain connected to and (2) get networks that need config.
+			Hashtable< Address,std::vector<InetAddress> > alwaysContact;
+			RR->topology->getUpstreamsToContact(alwaysContact);
 			std::vector< std::pair< SharedPtr<Network>,bool > > networkConfigNeeded;
 			{
 				Mutex::Lock l(_networks_m);
@@ -340,7 +321,7 @@ ZT_ResultCode Node::processBackgroundTasks(void *tptr,int64_t now,volatile int64
 
 			// Update online status, post status change as event
 			const bool oldOnline = _online;
-			_online = (((now - lastReceivedFromUpstream) < ZT_PEER_ACTIVITY_TIMEOUT)||(RR->topology->amUpstream()));
+			_online = pfunc.online;
 			if (oldOnline != _online)
 				postEvent(tptr,_online ? ZT_EVENT_ONLINE : ZT_EVENT_OFFLINE);
 		} catch ( ... ) {
@@ -434,18 +415,6 @@ ZT_ResultCode Node::multicastUnsubscribe(uint64_t nwid,uint64_t multicastGroup,u
 	} else return ZT_RESULT_ERROR_NETWORK_NOT_FOUND;
 }
 
-ZT_ResultCode Node::orbit(void *tptr,uint64_t moonWorldId,uint64_t moonSeed)
-{
-	RR->topology->addMoon(tptr,moonWorldId,Address(moonSeed));
-	return ZT_RESULT_OK;
-}
-
-ZT_ResultCode Node::deorbit(void *tptr,uint64_t moonWorldId)
-{
-	RR->topology->removeMoon(tptr,moonWorldId);
-	return ZT_RESULT_OK;
-}
-
 uint64_t Node::address() const
 {
 	return RR->identity.address().toInt();
@@ -644,16 +613,6 @@ ZT_ResultCode Node::setPhysicalPathConfiguration(const struct sockaddr_storage *
 	return ZT_RESULT_OK;
 }
 
-World Node::planet() const
-{
-	return RR->topology->planet();
-}
-
-std::vector<World> Node::moons() const
-{
-	return RR->topology->moons();
-}
-
 void Node::ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &destination,const NetworkConfig &nc,bool sendLegacyFormatConfig)
 {
 	_localControllerAuthorizations_m.lock();
@@ -691,10 +650,11 @@ void Node::ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &de
 					outp.append((uint32_t)totalSize);
 					outp.append((uint32_t)chunkIndex);
 
-					C25519::Signature sig(RR->identity.sign(reinterpret_cast<const uint8_t *>(outp.data()) + sigStart,outp.size() - sigStart));
+					uint8_t sig[256];
+					const unsigned int siglen = RR->identity.sign(reinterpret_cast<const uint8_t *>(outp.data()) + sigStart,outp.size() - sigStart,sig,sizeof(sig));
 					outp.append((uint8_t)1);
-					outp.append((uint16_t)ZT_C25519_SIGNATURE_LEN);
-					outp.append(sig.data,ZT_C25519_SIGNATURE_LEN);
+					outp.append((uint16_t)siglen);
+					outp.append(sig,siglen);
 
 					outp.compress();
 					RR->sw->send((void *)0,outp,true);
@@ -888,24 +848,6 @@ enum ZT_ResultCode ZT_Node_multicastUnsubscribe(ZT_Node *node,uint64_t nwid,uint
 	}
 }
 
-enum ZT_ResultCode ZT_Node_orbit(ZT_Node *node,void *tptr,uint64_t moonWorldId,uint64_t moonSeed)
-{
-	try {
-		return reinterpret_cast<ZeroTier::Node *>(node)->orbit(tptr,moonWorldId,moonSeed);
-	} catch ( ... ) {
-		return ZT_RESULT_FATAL_ERROR_INTERNAL;
-	}
-}
-
-enum ZT_ResultCode ZT_Node_deorbit(ZT_Node *node,void *tptr,uint64_t moonWorldId)
-{
-	try {
-		return reinterpret_cast<ZeroTier::Node *>(node)->deorbit(tptr,moonWorldId);
-	} catch ( ... ) {
-		return ZT_RESULT_FATAL_ERROR_INTERNAL;
-	}
-}
-
 uint64_t ZT_Node_address(ZT_Node *node)
 {
 	return reinterpret_cast<ZeroTier::Node *>(node)->address();

+ 0 - 7
node/Node.hpp

@@ -54,8 +54,6 @@
 
 namespace ZeroTier {
 
-class World;
-
 /**
  * Implementation of Node object as defined in CAPI
  *
@@ -99,8 +97,6 @@ public:
 	ZT_ResultCode leave(uint64_t nwid,void **uptr,void *tptr);
 	ZT_ResultCode multicastSubscribe(void *tptr,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
 	ZT_ResultCode multicastUnsubscribe(uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
-	ZT_ResultCode orbit(void *tptr,uint64_t moonWorldId,uint64_t moonSeed);
-	ZT_ResultCode deorbit(void *tptr,uint64_t moonWorldId);
 	uint64_t address() const;
 	void status(ZT_NodeStatus *status) const;
 	ZT_PeerList *peers() const;
@@ -194,9 +190,6 @@ public:
 	uint64_t prng();
 	ZT_ResultCode setPhysicalPathConfiguration(const struct sockaddr_storage *pathNetwork,const ZT_PhysicalPathConfiguration *pathConfig);
 
-	World planet() const;
-	std::vector<World> moons() const;
-
 	inline const Identity &identity() const { return _RR.identity; }
 
 	/**

+ 0 - 10
node/Packet.cpp

@@ -972,16 +972,6 @@ bool Packet::dearmor(const void *key)
 	}
 }
 
-void Packet::cryptField(const void *key,unsigned int start,unsigned int len)
-{
-	uint8_t *const data = reinterpret_cast<uint8_t *>(unsafeData());
-	uint8_t iv[8];
-	for(int i=0;i<8;++i) iv[i] = data[i];
-	iv[7] &= 0xf8; // mask off least significant 3 bits of packet ID / IV since this is unset when this function gets called
-	Salsa20 s20(key,iv);
-	s20.crypt12(data + start,data + start,len);
-}
-
 bool Packet::compress()
 {
 	char *const data = reinterpret_cast<char *>(unsafeData());

+ 16 - 74
node/Packet.hpp

@@ -68,10 +68,13 @@
  *    + Tags and Capabilities
  *    + Inline push of CertificateOfMembership deprecated
  * 9  - 1.2.0 ... 1.2.14
- * 10 - 1.4.0 ... CURRENT
+ * 10 - 1.4.0 ... 1.6.0
  *    + Multipath capability and load balancing
+ * 11 - 1.6.0 ... CURRENT
+ *    + Peer-to-peer multicast replication (optional)
+ *    + Old planet/moon stuff is DEAD!
  */
-#define ZT_PROTO_VERSION 10
+#define ZT_PROTO_VERSION 11
 
 /**
  * Minimum supported protocol version
@@ -309,17 +312,6 @@
 #define ZT_PROTO_VERB_MULTICAST_GATHER_IDX_GATHER_LIMIT (ZT_PROTO_VERB_MULTICAST_GATHER_IDX_ADI + 4)
 #define ZT_PROTO_VERB_MULTICAST_GATHER_IDX_COM (ZT_PROTO_VERB_MULTICAST_GATHER_IDX_GATHER_LIMIT + 4)
 
-// Note: COM, GATHER_LIMIT, and SOURCE_MAC are optional, and so are specified without size
-#define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_NETWORK_ID (ZT_PACKET_IDX_PAYLOAD)
-#define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FLAGS (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_NETWORK_ID + 8)
-#define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_COM (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FLAGS + 1)
-#define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_GATHER_LIMIT (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FLAGS + 1)
-#define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_SOURCE_MAC (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FLAGS + 1)
-#define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_DEST_MAC (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FLAGS + 1)
-#define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_DEST_ADI (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_DEST_MAC + 6)
-#define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_ETHERTYPE (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_DEST_ADI + 4)
-#define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_ETHERTYPE + 2)
-
 #define ZT_PROTO_VERB_HELLO__OK__IDX_TIMESTAMP (ZT_PROTO_VERB_OK_IDX_PAYLOAD)
 #define ZT_PROTO_VERB_HELLO__OK__IDX_PROTOCOL_VERSION (ZT_PROTO_VERB_HELLO__OK__IDX_TIMESTAMP + 8)
 #define ZT_PROTO_VERB_HELLO__OK__IDX_MAJOR_VERSION (ZT_PROTO_VERB_HELLO__OK__IDX_PROTOCOL_VERSION + 1)
@@ -542,22 +534,9 @@ public:
 		 *   <[8] timestamp for determining latency>
 		 *   <[...] binary serialized identity (see Identity)>
 		 *   <[...] physical destination address of packet>
-		 *   <[8] 64-bit world ID of current planet>
-		 *   <[8] 64-bit timestamp of current planet>
-		 *   [... remainder if packet is encrypted using cryptField() ...]
-		 *   <[2] 16-bit number of moons>
-		 *   [<[1] 8-bit type ID of moon>]
-		 *   [<[8] 64-bit world ID of moon>]
-		 *   [<[8] 64-bit timestamp of moon>]
-		 *   [... additional moon type/ID/timestamp tuples ...]
-		 *
+		 * 
 		 * HELLO is sent in the clear as it is how peers share their identity
-		 * public keys. A few additional fields are sent in the clear too, but
-		 * these are things that are public info or are easy to determine. As
-		 * of 1.2.0 we have added a few more fields, but since these could have
-		 * the potential to be sensitive we introduced the encryption of the
-		 * remainder of the packet. See cryptField(). Packet MAC is still
-		 * performed of course, so authentication occurs as normal.
+		 * public keys.
 		 *
 		 * Destination address is the actual wire address to which the packet
 		 * was sent. See InetAddress::serialize() for format.
@@ -569,15 +548,10 @@ public:
 		 *   <[1] software minor version>
 		 *   <[2] software revision>
 		 *   <[...] physical destination address of packet>
-		 *   <[2] 16-bit length of world update(s) or 0 if none>
-		 *   [[...] updates to planets and/or moons]
 		 *
 		 * With the exception of the timestamp, the other fields pertain to the
 		 * respondent who is sending OK and are not echoes.
 		 *
-		 * Note that OK is fully encrypted so no selective cryptField() of
-		 * potentially sensitive fields is needed.
-		 *
 		 * ERROR has no payload.
 		 */
 		VERB_HELLO = 0x01,
@@ -710,10 +684,6 @@ public:
 		 * controllers and root servers. In the current network, root servers
 		 * will provide the service of final multicast cache.
 		 *
-		 * VERB_NETWORK_CREDENTIALS should be pushed along with this, especially
-		 * if using upstream (e.g. root) nodes as multicast databases. This allows
-		 * GATHERs to be authenticated.
-		 *
 		 * OK/ERROR are not generated.
 		 */
 		VERB_MULTICAST_LIKE = 0x09,
@@ -832,19 +802,11 @@ public:
 		 *   [<[...] network certificate of membership>]
 		 *
 		 * Flags:
-		 *   0x01 - COM is attached
-		 *
-		 * This message asks a peer for additional known endpoints that have
-		 * LIKEd a given multicast group. It's sent when the sender wishes
-		 * to send multicast but does not have the desired number of recipient
-		 * peers.
-		 *
+		 *   0x01 - COM is attached (DEPRECATED)
+		 * 
 		 * More than one OK response can occur if the response is broken up across
 		 * multiple packets or if querying a clustered node.
 		 *
-		 * The COM should be included so that upstream nodes that are not
-		 * members of our network can validate our request.
-		 *
 		 * OK response payload:
 		 *   <[8] 64-bit network ID>
 		 *   <[6] MAC address of multicast group being queried>
@@ -864,6 +826,8 @@ public:
 		 *   <[1] flags>
 		 *  [<[4] 32-bit implicit gather limit>]
 		 *  [<[6] source MAC>]
+		 *  [<[2] number of explicitly specified recipients>]
+		 *  [<[...] series of 5-byte explicitly specified recipients>]
 		 *   <[6] destination MAC (multicast address)>
 		 *   <[4] 32-bit multicast ADI (multicast address extension)>
 		 *   <[2] 16-bit ethertype>
@@ -871,15 +835,12 @@ public:
 		 *
 		 * Flags:
 		 *   0x01 - Network certificate of membership attached (DEPRECATED)
-		 *   0x02 - Implicit gather limit field is present
+		 *   0x02 - Implicit gather limit field is present (DEPRECATED)
 		 *   0x04 - Source MAC is specified -- otherwise it's computed from sender
-		 *   0x08 - Please replicate (sent to multicast replicators)
-		 *
-		 * OK and ERROR responses are optional. OK may be generated if there are
-		 * implicit gather results or if the recipient wants to send its own
-		 * updated certificate of network membership to the sender. ERROR may be
-		 * generated if a certificate is needed or if multicasts to this group
-		 * are no longer wanted (multicast unsubscribe).
+		 *   0x08 - Explicit recipient list included for P2P/HS replication
+		 * 
+		 * Explicit recipient lists are used for peer to peer or hub and spoke
+		 * replication.
 		 *
 		 * OK response payload:
 		 *   <[8] 64-bit network ID>
@@ -1004,10 +965,6 @@ public:
 		 * be sent to observers configured at the network level for those that
 		 * pertain directly to activity on a network, or to global observers if
 		 * locally configured.
-		 *
-		 * The instance ID is a random 64-bit value generated by each ZeroTier
-		 * node on startup. This is helpful in identifying traces from different
-		 * members of a cluster.
 		 */
 		VERB_REMOTE_TRACE = 0x15
 	};
@@ -1295,21 +1252,6 @@ public:
 	 */
 	bool dearmor(const void *key);
 
-	/**
-	 * Encrypt/decrypt a separately armored portion of a packet
-	 *
-	 * This is currently only used to mask portions of HELLO as an extra
-	 * security precaution since most of that message is sent in the clear.
-	 *
-	 * This must NEVER be used more than once in the same packet, as doing
-	 * so will result in re-use of the same key stream.
-	 *
-	 * @param key 32-byte key
-	 * @param start Start of encrypted portion
-	 * @param len Length of encrypted portion
-	 */
-	void cryptField(const void *key,unsigned int start,unsigned int len);
-
 	/**
 	 * Attempt to compress payload if not already (must be unencrypted)
 	 *

+ 0 - 21
node/Peer.cpp

@@ -711,27 +711,6 @@ void Peer::sendHELLO(void *tPtr,const int64_t localSocket,const InetAddress &atA
 	RR->identity.serialize(outp,false);
 	atAddress.serialize(outp);
 
-	outp.append((uint64_t)RR->topology->planetWorldId());
-	outp.append((uint64_t)RR->topology->planetWorldTimestamp());
-
-	const unsigned int startCryptedPortionAt = outp.size();
-
-	std::vector<World> moons(RR->topology->moons());
-	std::vector<uint64_t> moonsWanted(RR->topology->moonsWanted());
-	outp.append((uint16_t)(moons.size() + moonsWanted.size()));
-	for(std::vector<World>::const_iterator m(moons.begin());m!=moons.end();++m) {
-		outp.append((uint8_t)m->type());
-		outp.append((uint64_t)m->id());
-		outp.append((uint64_t)m->timestamp());
-	}
-	for(std::vector<uint64_t>::const_iterator m(moonsWanted.begin());m!=moonsWanted.end();++m) {
-		outp.append((uint8_t)World::TYPE_MOON);
-		outp.append(*m);
-		outp.append((uint64_t)0);
-	}
-
-	outp.cryptField(_key,startCryptedPortionAt,outp.size() - startCryptedPortionAt);
-
 	RR->node->expectReplyTo(outp.packetId());
 
 	if (atAddress) {

+ 2 - 1
node/Peer.hpp

@@ -540,7 +540,8 @@ public:
 	/**
 	 * @return Whether this peer is reachable via an aggregate link
 	 */
-	inline bool hasAggregateLink() {
+	inline bool hasAggregateLink()
+	{
 		return _localMultipathSupported && _remoteMultipathSupported && _remotePeerMultipathEnabled;
 	}
 

+ 13 - 1
node/Revocation.cpp

@@ -34,6 +34,18 @@
 
 namespace ZeroTier {
 
+bool Revocation::sign(const Identity &signer)
+{
+	if (signer.hasPrivate()) {
+		Buffer<sizeof(Revocation) + 64> tmp;
+		_signedBy = signer.address();
+		this->serialize(tmp,true);
+		_signatureLength = signer.sign(tmp.data(),tmp.size(),_signature,sizeof(_signature));
+		return true;
+	}
+	return false;
+}
+
 int Revocation::verify(const RuntimeEnvironment *RR,void *tPtr) const
 {
 	if ((!_signedBy)||(_signedBy != Network::controllerFor(_networkId)))
@@ -46,7 +58,7 @@ int Revocation::verify(const RuntimeEnvironment *RR,void *tPtr) const
 	try {
 		Buffer<sizeof(Revocation) + 64> tmp;
 		this->serialize(tmp,true);
-		return (id.verify(tmp.data(),tmp.size(),_signature) ? 0 : -1);
+		return (id.verify(tmp.data(),tmp.size(),_signature,_signatureLength) ? 0 : -1);
 	} catch ( ... ) {
 		return -1;
 	}

+ 14 - 24
node/Revocation.hpp

@@ -66,9 +66,9 @@ public:
 		_flags(0),
 		_target(),
 		_signedBy(),
-		_type(Credential::CREDENTIAL_TYPE_NULL)
+		_type(Credential::CREDENTIAL_TYPE_NULL),
+		_signatureLength(0)
 	{
-		memset(_signature.data,0,sizeof(_signature.data));
 	}
 
 	/**
@@ -88,9 +88,9 @@ public:
 		_flags(fl),
 		_target(tgt),
 		_signedBy(),
-		_type(ct)
+		_type(ct),
+		_signatureLength(0)
 	{
-		memset(_signature.data,0,sizeof(_signature.data));
 	}
 
 	inline uint32_t id() const { return _id; }
@@ -107,17 +107,7 @@ public:
 	 * @param signer Signing identity, must have private key
 	 * @return True if signature was successful
 	 */
-	inline bool sign(const Identity &signer)
-	{
-		if (signer.hasPrivate()) {
-			Buffer<sizeof(Revocation) + 64> tmp;
-			_signedBy = signer.address();
-			this->serialize(tmp,true);
-			_signature = signer.sign(tmp.data(),tmp.size());
-			return true;
-		}
-		return false;
-	}
+	bool sign(const Identity &signer);
 
 	/**
 	 * Verify this revocation's signature
@@ -145,9 +135,9 @@ public:
 		b.append((uint8_t)_type);
 
 		if (!forSign) {
-			b.append((uint8_t)1); // 1 == Ed25519 signature
-			b.append((uint16_t)ZT_C25519_SIGNATURE_LEN);
-			b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
+			b.append((uint8_t)1);
+			b.append((uint16_t)_signatureLength);
+			b.append(_signature,_signatureLength);
 		}
 
 		// This is the size of any additional fields, currently 0.
@@ -175,11 +165,10 @@ public:
 		_type = (Credential::Type)b[p++];
 
 		if (b[p++] == 1) {
-			if (b.template at<uint16_t>(p) == ZT_C25519_SIGNATURE_LEN) {
-				p += 2;
-				memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN);
-				p += ZT_C25519_SIGNATURE_LEN;
-			} else throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
+			_signatureLength = b.template at<uint16_t>(p);
+			if (_signatureLength > sizeof(_signature))
+				throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
+			memcpy(_signature,b.field(p,_signatureLength),_signatureLength);
 		} else {
 			p += 2 + b.template at<uint16_t>(p);
 		}
@@ -200,7 +189,8 @@ private:
 	Address _target;
 	Address _signedBy;
 	Credential::Type _type;
-	C25519::Signature _signature;
+	unsigned int _signatureLength;
+	uint8_t _signature[ZT_SIGNATURE_BUFFER_SIZE];
 };
 
 } // namespace ZeroTier

+ 189 - 296
node/SHA512.cpp

@@ -18,13 +18,20 @@ Public domain.
 #include <CommonCrypto/CommonDigest.h>
 #define ZT_HAVE_NATIVE_SHA512
 namespace ZeroTier {
-void SHA512::hash(void *digest,const void *data,unsigned int len)
+void SHA512(void *digest,const void *data,unsigned int len)
 {
 	CC_SHA512_CTX ctx;
 	CC_SHA512_Init(&ctx);
 	CC_SHA512_Update(&ctx,data,len);
 	CC_SHA512_Final(reinterpret_cast<unsigned char *>(digest),&ctx);
 }
+void SHA384(void *digest,const void *data,unsigned int len)
+{
+	CC_SHA512_CTX ctx;
+	CC_SHA384_Init(&ctx);
+	CC_SHA384_Update(&ctx,data,len);
+	CC_SHA384_Final(reinterpret_cast<unsigned char *>(digest),&ctx);
+}
 }
 #endif
 
@@ -32,336 +39,222 @@ void SHA512::hash(void *digest,const void *data,unsigned int len)
 #include <openssl/sha.h>
 #define ZT_HAVE_NATIVE_SHA512
 namespace ZeroTier {
-void SHA512::hash(void *digest,const void *data,unsigned int len)
+void SHA512(void *digest,const void *data,unsigned int len)
 {
 	SHA512_CTX ctx;
 	SHA512_Init(&ctx);
 	SHA512_Update(&ctx,data,len);
 	SHA512_Final(reinterpret_cast<unsigned char *>(digest),&ctx);
 }
+void SHA384(void *digest,const void *data,unsigned int len)
+{
+	SHA512_CTX ctx;
+	SHA384_Init(&ctx);
+	SHA384_Update(&ctx,data,len);
+	SHA384_Final(reinterpret_cast<unsigned char *>(digest),&ctx);
+}
 }
 #endif
 
+// If a platform-native SHA512 isn't available we use this 64-bit C version.
 #ifndef ZT_HAVE_NATIVE_SHA512
 
 namespace ZeroTier {
 
-#define uint64 uint64_t
-
-#ifdef ZT_NO_TYPE_PUNNING
+namespace {
 
-static uint64 load_bigendian(const unsigned char *x)
+static inline void sha512_encode(uint64_t input, uint8_t *output, uint32_t idx)
 {
-	return
-			(uint64) (x[7]) \
-	| (((uint64) (x[6])) << 8) \
-	| (((uint64) (x[5])) << 16) \
-	| (((uint64) (x[4])) << 24) \
-	| (((uint64) (x[3])) << 32) \
-	| (((uint64) (x[2])) << 40) \
-	| (((uint64) (x[1])) << 48) \
-	| (((uint64) (x[0])) << 56)
-	;
+	output[idx + 0] = (uint8_t)(input >> 56);
+	output[idx + 1] = (uint8_t)(input >> 48);
+	output[idx + 2] = (uint8_t)(input >> 40);
+	output[idx + 3] = (uint8_t)(input >> 32);
+	output[idx + 4] = (uint8_t)(input >> 24);
+	output[idx + 5] = (uint8_t)(input >> 16);
+	output[idx + 6] = (uint8_t)(input >>  8);
+	output[idx + 7] = (uint8_t)(input >>  0);
 }
-
-static void store_bigendian(unsigned char *x,uint64 u)
+static inline void sha512_decode(uint64_t *output, uint8_t *input, uint32_t idx)
 {
-	x[7] = u; u >>= 8;
-	x[6] = u; u >>= 8;
-	x[5] = u; u >>= 8;
-	x[4] = u; u >>= 8;
-	x[3] = u; u >>= 8;
-	x[2] = u; u >>= 8;
-	x[1] = u; u >>= 8;
-	x[0] = u;
+	*output = ((uint64_t)input[idx + 0] << 56)
+		| ((uint64_t)input[idx + 1] << 48)
+		| ((uint64_t)input[idx + 2] << 40)
+		| ((uint64_t)input[idx + 3] << 32)
+		| ((uint64_t)input[idx + 4] << 24)
+		| ((uint64_t)input[idx + 5] << 16)
+		| ((uint64_t)input[idx + 6] <<  8)
+		| ((uint64_t)input[idx + 7] <<  0);
 }
 
-#else // !ZT_NO_TYPE_PUNNING
-
-#define load_bigendian(x) Utils::ntoh(*((const uint64_t *)(x)))
-#define store_bigendian(x,u) (*((uint64_t *)(x)) = Utils::hton((u)))
-
-#endif // ZT_NO_TYPE_PUNNING
-
-#define SHR(x,c) ((x) >> (c))
-#define ROTR(x,c) (((x) >> (c)) | ((x) << (64 - (c))))
-
-#define Ch(x,y,z) ((x & y) ^ (~x & z))
-#define Maj(x,y,z) ((x & y) ^ (x & z) ^ (y & z))
-#define Sigma0(x) (ROTR(x,28) ^ ROTR(x,34) ^ ROTR(x,39))
-#define Sigma1(x) (ROTR(x,14) ^ ROTR(x,18) ^ ROTR(x,41))
-#define sigma0(x) (ROTR(x, 1) ^ ROTR(x, 8) ^ SHR(x,7))
-#define sigma1(x) (ROTR(x,19) ^ ROTR(x,61) ^ SHR(x,6))
-
-#define M(w0,w14,w9,w1) w0 = sigma1(w14) + w9 + sigma0(w1) + w0;
-
-#define EXPAND \
-	M(w0 ,w14,w9 ,w1 ) \
-	M(w1 ,w15,w10,w2 ) \
-	M(w2 ,w0 ,w11,w3 ) \
-	M(w3 ,w1 ,w12,w4 ) \
-	M(w4 ,w2 ,w13,w5 ) \
-	M(w5 ,w3 ,w14,w6 ) \
-	M(w6 ,w4 ,w15,w7 ) \
-	M(w7 ,w5 ,w0 ,w8 ) \
-	M(w8 ,w6 ,w1 ,w9 ) \
-	M(w9 ,w7 ,w2 ,w10) \
-	M(w10,w8 ,w3 ,w11) \
-	M(w11,w9 ,w4 ,w12) \
-	M(w12,w10,w5 ,w13) \
-	M(w13,w11,w6 ,w14) \
-	M(w14,w12,w7 ,w15) \
-	M(w15,w13,w8 ,w0 )
-
-#define F(w,k) \
-	T1 = h + Sigma1(e) + Ch(e,f,g) + k + w; \
-	T2 = Sigma0(a) + Maj(a,b,c); \
-	h = g; \
-	g = f; \
-	f = e; \
-	e = d + T1; \
-	d = c; \
-	c = b; \
-	b = a; \
-	a = T1 + T2;
+typedef struct sha512_ctx_tag {
+	uint32_t is_sha384;
+	uint8_t block[128];
+	uint64_t len[2];
+	uint64_t val[8];
+	uint8_t *payload_addr;
+	uint64_t payload_len;
+} sha512_ctx_t;
+
+#define LSR(x,n) (x >> n)
+#define ROR(x,n) (LSR(x,n) | (x << (64 - n)))
+
+#define MA(x,y,z) ((x & y) | (z & (x | y)))
+#define CH(x,y,z) (z ^ (x & (y ^ z)))
+#define GAMMA0(x) (ROR(x, 1) ^ ROR(x, 8) ^  LSR(x, 7))
+#define GAMMA1(x) (ROR(x,19) ^ ROR(x,61) ^  LSR(x, 6))
+#define SIGMA0(x) (ROR(x,28) ^ ROR(x,34) ^ ROR(x,39))
+#define SIGMA1(x) (ROR(x,14) ^ ROR(x,18) ^ ROR(x,41))
+
+#define INIT_COMPRESSOR() uint64_t tmp0 = 0, tmp1 = 0
+#define COMPRESS( a,  b,  c, d,  e,  f,  g,  h, x,  k)   \
+    tmp0 = h + SIGMA1(e) + CH(e,f,g) + k + x;              \
+    tmp1 = SIGMA0(a) + MA(a,b,c); d += tmp0; h = tmp0 + tmp1;
+
+static const uint8_t sha512_padding[128] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+static const uint64_t K[80] = {
+	0x428A2F98D728AE22ULL,  0x7137449123EF65CDULL, 0xB5C0FBCFEC4D3B2FULL,  0xE9B5DBA58189DBBCULL,
+	0x3956C25BF348B538ULL,  0x59F111F1B605D019ULL, 0x923F82A4AF194F9BULL,  0xAB1C5ED5DA6D8118ULL,
+	0xD807AA98A3030242ULL,  0x12835B0145706FBEULL, 0x243185BE4EE4B28CULL,  0x550C7DC3D5FFB4E2ULL,
+	0x72BE5D74F27B896FULL,  0x80DEB1FE3B1696B1ULL, 0x9BDC06A725C71235ULL,  0xC19BF174CF692694ULL,
+	0xE49B69C19EF14AD2ULL,  0xEFBE4786384F25E3ULL, 0x0FC19DC68B8CD5B5ULL,  0x240CA1CC77AC9C65ULL,
+	0x2DE92C6F592B0275ULL,  0x4A7484AA6EA6E483ULL, 0x5CB0A9DCBD41FBD4ULL,  0x76F988DA831153B5ULL,
+	0x983E5152EE66DFABULL,  0xA831C66D2DB43210ULL, 0xB00327C898FB213FULL,  0xBF597FC7BEEF0EE4ULL,
+	0xC6E00BF33DA88FC2ULL,  0xD5A79147930AA725ULL, 0x06CA6351E003826FULL,  0x142929670A0E6E70ULL,
+	0x27B70A8546D22FFCULL,  0x2E1B21385C26C926ULL, 0x4D2C6DFC5AC42AEDULL,  0x53380D139D95B3DFULL,
+	0x650A73548BAF63DEULL,  0x766A0ABB3C77B2A8ULL, 0x81C2C92E47EDAEE6ULL,  0x92722C851482353BULL,
+	0xA2BFE8A14CF10364ULL,  0xA81A664BBC423001ULL, 0xC24B8B70D0F89791ULL,  0xC76C51A30654BE30ULL,
+	0xD192E819D6EF5218ULL,  0xD69906245565A910ULL, 0xF40E35855771202AULL,  0x106AA07032BBD1B8ULL,
+	0x19A4C116B8D2D0C8ULL,  0x1E376C085141AB53ULL, 0x2748774CDF8EEB99ULL,  0x34B0BCB5E19B48A8ULL,
+	0x391C0CB3C5C95A63ULL,  0x4ED8AA4AE3418ACBULL, 0x5B9CCA4F7763E373ULL,  0x682E6FF3D6B2B8A3ULL,
+	0x748F82EE5DEFB2FCULL,  0x78A5636F43172F60ULL, 0x84C87814A1F0AB72ULL,  0x8CC702081A6439ECULL,
+	0x90BEFFFA23631E28ULL,  0xA4506CEBDE82BDE9ULL, 0xBEF9A3F7B2C67915ULL,  0xC67178F2E372532BULL,
+	0xCA273ECEEA26619CULL,  0xD186B8C721C0C207ULL, 0xEADA7DD6CDE0EB1EULL,  0xF57D4F7FEE6ED178ULL,
+	0x06F067AA72176FBAULL,  0x0A637DC5A2C898A6ULL, 0x113F9804BEF90DAEULL,  0x1B710B35131C471BULL,
+	0x28DB77F523047D84ULL,  0x32CAAB7B40C72493ULL, 0x3C9EBE0A15C9BEBCULL,  0x431D67C49C100D4CULL,
+	0x4CC5D4BECB3E42B6ULL,  0x597F299CFC657E2AULL, 0x5FCB6FAB3AD6FAECULL,  0x6C44198C4A475817ULL
+};
 
-static inline int crypto_hashblocks(unsigned char *statebytes,const unsigned char *in,unsigned long long inlen)
+static inline void sha512_memcpy(uint8_t *src, uint8_t *dst, uint32_t size)
 {
-	uint64 state[8];
-	uint64 a;
-	uint64 b;
-	uint64 c;
-	uint64 d;
-	uint64 e;
-	uint64 f;
-	uint64 g;
-	uint64 h;
-	uint64 T1;
-	uint64 T2;
-
-	a = load_bigendian(statebytes +  0); state[0] = a;
-	b = load_bigendian(statebytes +  8); state[1] = b;
-	c = load_bigendian(statebytes + 16); state[2] = c;
-	d = load_bigendian(statebytes + 24); state[3] = d;
-	e = load_bigendian(statebytes + 32); state[4] = e;
-	f = load_bigendian(statebytes + 40); state[5] = f;
-	g = load_bigendian(statebytes + 48); state[6] = g;
-	h = load_bigendian(statebytes + 56); state[7] = h;
-
-	while (inlen >= 128) {
-		uint64 w0  = load_bigendian(in +   0);
-		uint64 w1  = load_bigendian(in +   8);
-		uint64 w2  = load_bigendian(in +  16);
-		uint64 w3  = load_bigendian(in +  24);
-		uint64 w4  = load_bigendian(in +  32);
-		uint64 w5  = load_bigendian(in +  40);
-		uint64 w6  = load_bigendian(in +  48);
-		uint64 w7  = load_bigendian(in +  56);
-		uint64 w8  = load_bigendian(in +  64);
-		uint64 w9  = load_bigendian(in +  72);
-		uint64 w10 = load_bigendian(in +  80);
-		uint64 w11 = load_bigendian(in +  88);
-		uint64 w12 = load_bigendian(in +  96);
-		uint64 w13 = load_bigendian(in + 104);
-		uint64 w14 = load_bigendian(in + 112);
-		uint64 w15 = load_bigendian(in + 120);
-
-		F(w0 ,0x428a2f98d728ae22ULL)
-		F(w1 ,0x7137449123ef65cdULL)
-		F(w2 ,0xb5c0fbcfec4d3b2fULL)
-		F(w3 ,0xe9b5dba58189dbbcULL)
-		F(w4 ,0x3956c25bf348b538ULL)
-		F(w5 ,0x59f111f1b605d019ULL)
-		F(w6 ,0x923f82a4af194f9bULL)
-		F(w7 ,0xab1c5ed5da6d8118ULL)
-		F(w8 ,0xd807aa98a3030242ULL)
-		F(w9 ,0x12835b0145706fbeULL)
-		F(w10,0x243185be4ee4b28cULL)
-		F(w11,0x550c7dc3d5ffb4e2ULL)
-		F(w12,0x72be5d74f27b896fULL)
-		F(w13,0x80deb1fe3b1696b1ULL)
-		F(w14,0x9bdc06a725c71235ULL)
-		F(w15,0xc19bf174cf692694ULL)
-
-		EXPAND
-
-		F(w0 ,0xe49b69c19ef14ad2ULL)
-		F(w1 ,0xefbe4786384f25e3ULL)
-		F(w2 ,0x0fc19dc68b8cd5b5ULL)
-		F(w3 ,0x240ca1cc77ac9c65ULL)
-		F(w4 ,0x2de92c6f592b0275ULL)
-		F(w5 ,0x4a7484aa6ea6e483ULL)
-		F(w6 ,0x5cb0a9dcbd41fbd4ULL)
-		F(w7 ,0x76f988da831153b5ULL)
-		F(w8 ,0x983e5152ee66dfabULL)
-		F(w9 ,0xa831c66d2db43210ULL)
-		F(w10,0xb00327c898fb213fULL)
-		F(w11,0xbf597fc7beef0ee4ULL)
-		F(w12,0xc6e00bf33da88fc2ULL)
-		F(w13,0xd5a79147930aa725ULL)
-		F(w14,0x06ca6351e003826fULL)
-		F(w15,0x142929670a0e6e70ULL)
-
-		EXPAND
-
-		F(w0 ,0x27b70a8546d22ffcULL)
-		F(w1 ,0x2e1b21385c26c926ULL)
-		F(w2 ,0x4d2c6dfc5ac42aedULL)
-		F(w3 ,0x53380d139d95b3dfULL)
-		F(w4 ,0x650a73548baf63deULL)
-		F(w5 ,0x766a0abb3c77b2a8ULL)
-		F(w6 ,0x81c2c92e47edaee6ULL)
-		F(w7 ,0x92722c851482353bULL)
-		F(w8 ,0xa2bfe8a14cf10364ULL)
-		F(w9 ,0xa81a664bbc423001ULL)
-		F(w10,0xc24b8b70d0f89791ULL)
-		F(w11,0xc76c51a30654be30ULL)
-		F(w12,0xd192e819d6ef5218ULL)
-		F(w13,0xd69906245565a910ULL)
-		F(w14,0xf40e35855771202aULL)
-		F(w15,0x106aa07032bbd1b8ULL)
-
-		EXPAND
-
-		F(w0 ,0x19a4c116b8d2d0c8ULL)
-		F(w1 ,0x1e376c085141ab53ULL)
-		F(w2 ,0x2748774cdf8eeb99ULL)
-		F(w3 ,0x34b0bcb5e19b48a8ULL)
-		F(w4 ,0x391c0cb3c5c95a63ULL)
-		F(w5 ,0x4ed8aa4ae3418acbULL)
-		F(w6 ,0x5b9cca4f7763e373ULL)
-		F(w7 ,0x682e6ff3d6b2b8a3ULL)
-		F(w8 ,0x748f82ee5defb2fcULL)
-		F(w9 ,0x78a5636f43172f60ULL)
-		F(w10,0x84c87814a1f0ab72ULL)
-		F(w11,0x8cc702081a6439ecULL)
-		F(w12,0x90befffa23631e28ULL)
-		F(w13,0xa4506cebde82bde9ULL)
-		F(w14,0xbef9a3f7b2c67915ULL)
-		F(w15,0xc67178f2e372532bULL)
-
-		EXPAND
-
-		F(w0 ,0xca273eceea26619cULL)
-		F(w1 ,0xd186b8c721c0c207ULL)
-		F(w2 ,0xeada7dd6cde0eb1eULL)
-		F(w3 ,0xf57d4f7fee6ed178ULL)
-		F(w4 ,0x06f067aa72176fbaULL)
-		F(w5 ,0x0a637dc5a2c898a6ULL)
-		F(w6 ,0x113f9804bef90daeULL)
-		F(w7 ,0x1b710b35131c471bULL)
-		F(w8 ,0x28db77f523047d84ULL)
-		F(w9 ,0x32caab7b40c72493ULL)
-		F(w10,0x3c9ebe0a15c9bebcULL)
-		F(w11,0x431d67c49c100d4cULL)
-		F(w12,0x4cc5d4becb3e42b6ULL)
-		F(w13,0x597f299cfc657e2aULL)
-		F(w14,0x5fcb6fab3ad6faecULL)
-		F(w15,0x6c44198c4a475817ULL)
+	uint32_t i = 0;
+	for (;i < size;i++) { *dst++ = *src++; }
+}
+static inline void sha512_memclr(uint8_t *dst, uint32_t size)
+{
+	uint32_t i = 0;
+	for (;i < size;i++) { *dst++ = 0; }
+}
 
-		a += state[0];
-		b += state[1];
-		c += state[2];
-		d += state[3];
-		e += state[4];
-		f += state[5];
-		g += state[6];
-		h += state[7];
+static inline void sha512_init_512(sha512_ctx_t *sha512_ctx, uint8_t *payload_addr, uint64_t payload_len)
+{
+	sha512_memclr((uint8_t *)sha512_ctx,sizeof(sha512_ctx_t));
+	sha512_ctx->val[0] = 0x6A09E667F3BCC908ULL;
+	sha512_ctx->val[1] = 0xBB67AE8584CAA73BULL;
+	sha512_ctx->val[2] = 0x3C6EF372FE94F82BULL;
+	sha512_ctx->val[3] = 0xA54FF53A5F1D36F1ULL;
+	sha512_ctx->val[4] = 0x510E527FADE682D1ULL;
+	sha512_ctx->val[5] = 0x9B05688C2B3E6C1FULL;
+	sha512_ctx->val[6] = 0x1F83D9ABFB41BD6BULL;
+	sha512_ctx->val[7] = 0x5BE0CD19137E2179ULL;
+	sha512_ctx->is_sha384 = 0;
+	sha512_ctx->payload_addr = payload_addr;
+	sha512_ctx->payload_len = (uint64_t)payload_len;
+	sha512_ctx->len[0] = payload_len << 3;
+	sha512_ctx->len[1] = payload_len >> 61;
+}
 
-		state[0] = a;
-		state[1] = b;
-		state[2] = c;
-		state[3] = d;
-		state[4] = e;
-		state[5] = f;
-		state[6] = g;
-		state[7] = h;
+static inline void sha512_init_384(sha512_ctx_t *sha512_ctx, uint8_t *payload_addr, uint64_t payload_len)
+{
+	sha512_memclr((uint8_t *)sha512_ctx,sizeof(sha512_ctx_t));
+	sha512_ctx->val[0] = 0xCBBB9D5DC1059ED8ULL;
+	sha512_ctx->val[1] = 0x629A292A367CD507ULL;
+	sha512_ctx->val[2] = 0x9159015A3070DD17ULL;
+	sha512_ctx->val[3] = 0x152FECD8F70E5939ULL;
+	sha512_ctx->val[4] = 0x67332667FFC00B31ULL;
+	sha512_ctx->val[5] = 0x8EB44A8768581511ULL;
+	sha512_ctx->val[6] = 0xDB0C2E0D64F98FA7ULL;
+	sha512_ctx->val[7] = 0x47B5481DBEFA4FA4ULL;
+	sha512_ctx->is_sha384 = 1;
+	sha512_ctx->payload_addr = payload_addr;
+	sha512_ctx->payload_len = (uint64_t)payload_len;
+	sha512_ctx->len[0] = payload_len << 3;
+	sha512_ctx->len[1] = payload_len >> 61;
+}
 
-		in += 128;
-		inlen -= 128;
+static inline void sha512_hash_factory(sha512_ctx_t *ctx, uint8_t data[128])
+{
+	uint32_t i = 0;
+	uint64_t W[80];
+	uint64_t v[8];
+	INIT_COMPRESSOR();
+	for(i = 0; i < 16; i++) { sha512_decode(&W[i], data, i << 3 ); }
+	for(; i < 80; i++) { W[i] = GAMMA1(W[i -  2]) + W[i -  7] + GAMMA0(W[i - 15]) + W[i - 16]; }
+	for (i = 0;i < 8; i++) { v[i] = ctx->val[i]; }
+	for(i = 0; i < 80;) {
+		COMPRESS(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], W[i], K[i] ); i++;
+		COMPRESS(v[7], v[0], v[1], v[2], v[3], v[4], v[5], v[6], W[i], K[i] ); i++;
+		COMPRESS(v[6], v[7], v[0], v[1], v[2], v[3], v[4], v[5], W[i], K[i] ); i++;
+		COMPRESS(v[5], v[6], v[7], v[0], v[1], v[2], v[3], v[4], W[i], K[i] ); i++;
+		COMPRESS(v[4], v[5], v[6], v[7], v[0], v[1], v[2], v[3], W[i], K[i] ); i++;
+		COMPRESS(v[3], v[4], v[5], v[6], v[7], v[0], v[1], v[2], W[i], K[i] ); i++;
+		COMPRESS(v[2], v[3], v[4], v[5], v[6], v[7], v[0], v[1], W[i], K[i] ); i++;
+		COMPRESS(v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[0], W[i], K[i] ); i++;
 	}
-
-	store_bigendian(statebytes +  0,state[0]);
-	store_bigendian(statebytes +  8,state[1]);
-	store_bigendian(statebytes + 16,state[2]);
-	store_bigendian(statebytes + 24,state[3]);
-	store_bigendian(statebytes + 32,state[4]);
-	store_bigendian(statebytes + 40,state[5]);
-	store_bigendian(statebytes + 48,state[6]);
-	store_bigendian(statebytes + 56,state[7]);
-
-	return 0;
+	for (i = 0; i < 8; i++) { ctx->val[i] += v[i]; }
 }
 
-#define blocks crypto_hashblocks
-
-static const unsigned char iv[64] = {
-	0x6a,0x09,0xe6,0x67,0xf3,0xbc,0xc9,0x08,
-	0xbb,0x67,0xae,0x85,0x84,0xca,0xa7,0x3b,
-	0x3c,0x6e,0xf3,0x72,0xfe,0x94,0xf8,0x2b,
-	0xa5,0x4f,0xf5,0x3a,0x5f,0x1d,0x36,0xf1,
-	0x51,0x0e,0x52,0x7f,0xad,0xe6,0x82,0xd1,
-	0x9b,0x05,0x68,0x8c,0x2b,0x3e,0x6c,0x1f,
-	0x1f,0x83,0xd9,0xab,0xfb,0x41,0xbd,0x6b,
-	0x5b,0xe0,0xcd,0x19,0x13,0x7e,0x21,0x79
-};
-
-void SHA512::hash(void *digest,const void *data,unsigned int len)
+static inline void sha512_stage1(sha512_ctx_t *sha512_ctx)
 {
-	unsigned char h[64];
-	unsigned char padded[256];
-	int i;
-	uint64_t bytes = len;
-
-	const unsigned char *in = (const unsigned char *)data;
-	unsigned int inlen = len;
-
-	for (i = 0;i < 64;++i) h[i] = iv[i];
+	while (sha512_ctx->payload_len >= 128) {
+		sha512_hash_factory(sha512_ctx, sha512_ctx->payload_addr);
+		sha512_ctx->payload_addr += 128;
+		sha512_ctx->payload_len -= 128;
+	}
+}
 
-	blocks(h,in,inlen);
-	in += inlen;
-	inlen &= 127;
-	in -= inlen;
+static inline void sha512_stage2(sha512_ctx_t *sha512_ctx, uint8_t output[64])
+{
+	uint32_t block_pos = sha512_ctx->payload_len;
+	uint32_t padding_bytes = 0;
+	uint8_t temp_data[128] = {0};
+	uint8_t *temp_data_p = (uint8_t *)&temp_data[0];
+	uint8_t len_be[16] = {0};
+	uint8_t i = 0;
+	sha512_memcpy(sha512_ctx->payload_addr, temp_data_p, sha512_ctx->payload_len);
+	padding_bytes = 112 - block_pos;
+	temp_data_p += block_pos;
+	sha512_memcpy((uint8_t *)sha512_padding, temp_data_p, padding_bytes);
+	temp_data_p += padding_bytes;
+	sha512_encode(sha512_ctx->len[1], len_be, 0);
+	sha512_encode(sha512_ctx->len[0], len_be, 8);
+	sha512_memcpy(len_be, temp_data_p, 16);
+	sha512_hash_factory(sha512_ctx, temp_data);
+	for (i = 0; i < 6; i++) { sha512_encode(sha512_ctx->val[i], output, i * 8); }
+	for ( ;(i < 8) && (sha512_ctx->is_sha384 == 0); i++) { sha512_encode(sha512_ctx->val[i], output, i * 8); }
+}
 
-	for (i = 0;i < (int)inlen;++i) padded[i] = in[i];
-	padded[inlen] = 0x80;
+} // anonymous namespace
 
-	if (inlen < 112) {
-		for (i = inlen + 1;i < 119;++i) padded[i] = 0;
-		padded[119] = (unsigned char)((bytes >> 61) & 0xff);
-		padded[120] = (unsigned char)((bytes >> 53) & 0xff);
-		padded[121] = (unsigned char)((bytes >> 45) & 0xff);
-		padded[122] = (unsigned char)((bytes >> 37) & 0xff);
-		padded[123] = (unsigned char)((bytes >> 29) & 0xff);
-		padded[124] = (unsigned char)((bytes >> 21) & 0xff);
-		padded[125] = (unsigned char)((bytes >> 13) & 0xff);
-		padded[126] = (unsigned char)((bytes >> 5) & 0xff);
-		padded[127] = (unsigned char)((bytes << 3) & 0xff);
-		blocks(h,padded,128);
-	} else {
-		for (i = inlen + 1;i < 247;++i) padded[i] = 0;
-		padded[247] = (unsigned char)((bytes >> 61) & 0xff);
-		padded[248] = (unsigned char)((bytes >> 53) & 0xff);
-		padded[249] = (unsigned char)((bytes >> 45) & 0xff);
-		padded[250] = (unsigned char)((bytes >> 37) & 0xff);
-		padded[251] = (unsigned char)((bytes >> 29) & 0xff);
-		padded[252] = (unsigned char)((bytes >> 21) & 0xff);
-		padded[253] = (unsigned char)((bytes >> 13) & 0xff);
-		padded[254] = (unsigned char)((bytes >> 5) & 0xff);
-		padded[255] = (unsigned char)((bytes << 3) & 0xff);
-		blocks(h,padded,256);
-	}
+void SHA512(void *digest,const void *data,unsigned int len)
+{
+	sha512_ctx_t h;
+	sha512_init_512(&h,(uint8_t *)data,len);
+	sha512_stage1(&h);
+	sha512_stage2(&h,(uint8_t *)digest);
+}
 
-	for (i = 0;i < 64;++i) ((unsigned char *)digest)[i] = h[i];
+void SHA384(void *digest,const void *data,unsigned int len)
+{
+	sha512_ctx_t h;
+	sha512_init_384(&h,(uint8_t *)data,len);
+	sha512_stage1(&h);
+	sha512_stage2(&h,(uint8_t *)digest);
 }
 
 } // namespace ZeroTier
 
 #endif // !ZT_HAVE_NATIVE_SHA512
 
-// Internally re-export to included C code, which includes some fast crypto code ported in on some platforms.
-// This eliminates the need to link against a third party SHA512() from this code
-extern "C" void ZT_sha512internal(void *digest,const void *data,unsigned int len)
-{
-	ZeroTier::SHA512::hash(digest,data,len);
-}
+extern "C" void ZT_sha512internal(void *digest,const void *data,unsigned int len) { ZeroTier::SHA512(digest,data,len); }
+extern "C" void ZT_sha384internal(void *digest,const void *data,unsigned int len) { ZeroTier::SHA384(digest,data,len); }

+ 3 - 8
node/SHA512.hpp

@@ -31,14 +31,9 @@
 
 namespace ZeroTier {
 
-/**
- * SHA-512 digest algorithm
- */
-class SHA512
-{
-public:
-	static void hash(void *digest,const void *data,unsigned int len);
-};
+void SHA512(void *digest,const void *data,unsigned int len);
+
+void SHA384(void *digest,const void *data,unsigned int len);
 
 } // namespace ZeroTier
 

+ 158 - 0
node/Str.hpp

@@ -0,0 +1,158 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2019  ZeroTier, Inc.  https://www.zerotier.com/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#ifndef ZT_STR_HPP
+#define ZT_STR_HPP
+
+#include "Constants.hpp"
+#include "Utils.hpp"
+#include "Address.hpp"
+#include "MAC.hpp"
+#include "InetAddress.hpp"
+
+#define ZT_STR_CAPACITY 254
+
+namespace ZeroTier {
+
+class Str
+{
+public:
+	Str() { _l = 0; _s[0] = 0; }
+	Str(const Str &s)
+	{
+		_l = s._l;
+		memcpy(_s,s._s,_l+1);
+	}
+	Str(const char *s)
+	{
+		_l = 0;
+		_s[0] = 0;
+		(*this) << s;
+	}
+
+	inline Str &operator=(const Str &s)
+	{
+		_l = s._l;
+		memcpy(_s,s._s,_l+1);
+		return *this;
+	}
+	inline Str &operator=(const char *s)
+	{
+		_l = 0;
+		_s[0] = 0;
+		return ((*this) << s);
+	}
+
+	inline char operator[](const unsigned int i) const
+	{
+		if (unlikely(i >= (unsigned int)_l))
+			throw ZT_EXCEPTION_OUT_OF_BOUNDS;
+		return _s[i];
+	}
+
+	inline void clear() { _l = 0; _s[0] = 0; }
+	inline const char *c_str() const { return _s; }
+	inline unsigned int length() const { return _l; }
+
+	inline Str &operator<<(const char *s)
+	{
+		if (likely(s != (const char *)0)) {
+			unsigned long l = _l;
+			while (*s) {
+				if (unlikely(l >= ZT_STR_CAPACITY)) {
+					_s[l] = 0;
+					_l = (uint8_t)l;
+					throw ZT_EXCEPTION_OUT_OF_BOUNDS;
+				}
+				_s[l++] = *s;
+			}
+			_s[l] = 0;
+			_l = (uint8_t)l;
+		}
+		return *this;
+	}
+	inline Str &operator<<(const Str &s) { return ((*this) << s._s); }
+	inline Str &operator<<(const char c)
+	{
+		if (likely(c != 0)) {
+			if (unlikely(_l >= ZT_STR_CAPACITY)) {
+				_s[_l] = 0;
+				throw ZT_EXCEPTION_OUT_OF_BOUNDS;
+			}
+			_s[_l++] = c;
+			_s[_l] = 0;
+		}
+	}
+	inline Str &operator<<(const unsigned long n)
+	{
+		char tmp[32];
+		Utils::decimal(n,tmp);
+		return ((*this) << tmp);
+	}
+	inline Str &operator<<(const unsigned int n)
+	{
+		char tmp[32];
+		Utils::decimal((unsigned long)n,tmp);
+		return ((*this) << tmp);
+	}
+	inline Str &operator<<(const Address &a)
+	{
+		char tmp[32];
+		return ((*this) << a.toString(tmp));
+	}
+	inline Str &operator<<(const InetAddress &a)
+	{
+		char tmp[128];
+		return ((*this) << a.toString(tmp));
+	}
+	inline Str &operator<<(const MAC &a)
+	{
+		char tmp[64];
+		return ((*this) << a.toString(tmp));
+	}
+
+	inline bool operator==(const Str &s) const { return ((_l == s._l)&&(strcmp(_s,s._s) == 0)); }
+	inline bool operator!=(const Str &s) const { return ((_l != s._l)||(strcmp(_s,s._s) != 0)); }
+	inline bool operator<(const Str &s) const { return ((_l < s._l)&&(strcmp(_s,s._s) < 0)); }
+	inline bool operator>(const Str &s) const { return ((_l > s._l)&&(strcmp(_s,s._s) > 0)); }
+	inline bool operator<=(const Str &s) const { return ((_l <= s._l)&&(strcmp(_s,s._s) <= 0)); }
+	inline bool operator>=(const Str &s) const { return ((_l >= s._l)&&(strcmp(_s,s._s) >= 0)); }
+
+	inline bool operator==(const char *s) const { return (strcmp(_s,s) == 0); }
+	inline bool operator!=(const char *s) const { return (strcmp(_s,s) != 0); }
+	inline bool operator<(const char *s) const { return (strcmp(_s,s) < 0); }
+	inline bool operator>(const char *s) const { return (strcmp(_s,s) > 0); }
+	inline bool operator<=(const char *s) const { return (strcmp(_s,s) <= 0); }
+	inline bool operator>=(const char *s) const { return (strcmp(_s,s) >= 0); }
+
+private:
+	uint8_t _l;
+	char _s[ZT_STR_CAPACITY+1];
+};
+
+} // namespace ZeroTier
+
+#endif

+ 0 - 7
node/Switch.cpp

@@ -92,9 +92,6 @@ void Switch::onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddre
 				const Address destination(fragment.destination());
 
 				if (destination != RR->identity.address()) {
-					if ( (!RR->topology->amUpstream()) && (!path->trustEstablished(now)) )
-						return;
-
 					if (fragment.hops() < ZT_RELAY_MAX_HOPS) {
 						fragment.incrementHops();
 
@@ -164,11 +161,7 @@ void Switch::onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddre
 					return;
 
 				if (destination != RR->identity.address()) {
-					if ( (!RR->topology->amUpstream()) && (!path->trustEstablished(now)) && (source != RR->identity.address()) )
-						return;
-
 					Packet packet(data,len);
-
 					if (packet.hops() < ZT_RELAY_MAX_HOPS) {
 						packet.incrementHops();
 						SharedPtr<Peer> relayTo = RR->topology->getPeer(tPtr,destination);

+ 13 - 1
node/Tag.cpp

@@ -34,6 +34,18 @@
 
 namespace ZeroTier {
 
+bool Tag::sign(const Identity &signer)
+{
+	if (signer.hasPrivate()) {
+		Buffer<sizeof(Tag) + 64> tmp;
+		_signedBy = signer.address();
+		this->serialize(tmp,true);
+		_signatureLength = signer.sign(tmp.data(),tmp.size(),_signature,sizeof(_signature));
+		return true;
+	}
+	return false;
+}
+
 int Tag::verify(const RuntimeEnvironment *RR,void *tPtr) const
 {
 	if ((!_signedBy)||(_signedBy != Network::controllerFor(_networkId)))
@@ -46,7 +58,7 @@ int Tag::verify(const RuntimeEnvironment *RR,void *tPtr) const
 	try {
 		Buffer<(sizeof(Tag) * 2)> tmp;
 		this->serialize(tmp,true);
-		return (id.verify(tmp.data(),tmp.size(),_signature) ? 0 : -1);
+		return (id.verify(tmp.data(),tmp.size(),_signature,_signatureLength) ? 0 : -1);
 	} catch ( ... ) {
 		return -1;
 	}

+ 13 - 21
node/Tag.hpp

@@ -69,9 +69,9 @@ public:
 		_id(0),
 		_value(0),
 		_networkId(0),
-		_ts(0)
+		_ts(0),
+		_signatureLength(0)
 	{
-		memset(_signature.data,0,sizeof(_signature.data));
 	}
 
 	/**
@@ -87,9 +87,9 @@ public:
 		_networkId(nwid),
 		_ts(ts),
 		_issuedTo(issuedTo),
-		_signedBy()
+		_signedBy(),
+		_signatureLength(0)
 	{
-		memset(_signature.data,0,sizeof(_signature.data));
 	}
 
 	inline uint32_t id() const { return _id; }
@@ -105,17 +105,7 @@ public:
 	 * @param signer Signing identity, must have private key
 	 * @return True if signature was successful
 	 */
-	inline bool sign(const Identity &signer)
-	{
-		if (signer.hasPrivate()) {
-			Buffer<sizeof(Tag) + 64> tmp;
-			_signedBy = signer.address();
-			this->serialize(tmp,true);
-			_signature = signer.sign(tmp.data(),tmp.size());
-			return true;
-		}
-		return false;
-	}
+	bool sign(const Identity &signer);
 
 	/**
 	 * Check this tag's signature
@@ -139,9 +129,9 @@ public:
 		_issuedTo.appendTo(b);
 		_signedBy.appendTo(b);
 		if (!forSign) {
-			b.append((uint8_t)1); // 1 == Ed25519
-			b.append((uint16_t)ZT_C25519_SIGNATURE_LEN); // length of signature
-			b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
+			b.append((uint8_t)1);
+			b.append((uint16_t)_signatureLength);
+			b.append(_signature,_signatureLength);
 		}
 
 		b.append((uint16_t)0); // length of additional fields, currently 0
@@ -165,10 +155,11 @@ public:
 		_issuedTo.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
 		_signedBy.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
 		if (b[p++] == 1) {
-			if (b.template at<uint16_t>(p) != ZT_C25519_SIGNATURE_LEN)
+			_signatureLength = b.template at<uint16_t>(p);
+			if (_signatureLength > sizeof(_signature))
 				throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
 			p += 2;
-			memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
+			memcpy(_signature,b.field(p,_signatureLength),_signatureLength); p += _signatureLength;
 		} else {
 			p += 2 + b.template at<uint16_t>(p);
 		}
@@ -207,7 +198,8 @@ private:
 	int64_t _ts;
 	Address _issuedTo;
 	Address _signedBy;
-	C25519::Signature _signature;
+	unsigned int _signatureLength;
+	uint8_t _signature[ZT_SIGNATURE_BUFFER_SIZE];
 };
 
 } // namespace ZeroTier

File diff suppressed because it is too large
+ 0 - 26
node/Topology.cpp


+ 4 - 155
node/Topology.hpp

@@ -45,7 +45,6 @@
 #include "Mutex.hpp"
 #include "InetAddress.hpp"
 #include "Hashtable.hpp"
-#include "World.hpp"
 
 namespace ZeroTier {
 
@@ -136,12 +135,6 @@ public:
 	 */
 	bool isUpstream(const Identity &id) const;
 
-	/**
-	 * @param addr Address to check
-	 * @return True if we should accept a world update from this address
-	 */
-	bool shouldAcceptWorldUpdateFrom(const Address &addr) const;
-
 	/**
 	 * @param ztaddr ZeroTier address
 	 * @return Peer role for this device
@@ -171,29 +164,6 @@ public:
 	 */
 	inline void getUpstreamsToContact(Hashtable< Address,std::vector<InetAddress> > &eps) const
 	{
-		Mutex::Lock _l(_upstreams_m);
-		for(std::vector<World::Root>::const_iterator i(_planet.roots().begin());i!=_planet.roots().end();++i) {
-			if (i->identity != RR->identity) {
-				std::vector<InetAddress> &ips = eps[i->identity.address()];
-				for(std::vector<InetAddress>::const_iterator j(i->stableEndpoints.begin());j!=i->stableEndpoints.end();++j) {
-					if (std::find(ips.begin(),ips.end(),*j) == ips.end())
-						ips.push_back(*j);
-				}
-			}
-		}
-		for(std::vector<World>::const_iterator m(_moons.begin());m!=_moons.end();++m) {
-			for(std::vector<World::Root>::const_iterator i(m->roots().begin());i!=m->roots().end();++i) {
-				if (i->identity != RR->identity) {
-					std::vector<InetAddress> &ips = eps[i->identity.address()];
-					for(std::vector<InetAddress>::const_iterator j(i->stableEndpoints.begin());j!=i->stableEndpoints.end();++j) {
-						if (std::find(ips.begin(),ips.end(),*j) == ips.end())
-							ips.push_back(*j);
-					}
-				}
-			}
-		}
-		for(std::vector< std::pair<uint64_t,Address> >::const_iterator m(_moonSeeds.begin());m!=_moonSeeds.end();++m)
-			eps[m->second];
 	}
 
 	/**
@@ -201,87 +171,10 @@ public:
 	 */
 	inline std::vector<Address> upstreamAddresses() const
 	{
-		Mutex::Lock _l(_upstreams_m);
-		return _upstreamAddresses;
-	}
-
-	/**
-	 * @return Current moons
-	 */
-	inline std::vector<World> moons() const
-	{
-		Mutex::Lock _l(_upstreams_m);
-		return _moons;
-	}
-
-	/**
-	 * @return Moon IDs we are waiting for from seeds
-	 */
-	inline std::vector<uint64_t> moonsWanted() const
-	{
-		Mutex::Lock _l(_upstreams_m);
-		std::vector<uint64_t> mw;
-		for(std::vector< std::pair<uint64_t,Address> >::const_iterator s(_moonSeeds.begin());s!=_moonSeeds.end();++s) {
-			if (std::find(mw.begin(),mw.end(),s->first) == mw.end())
-				mw.push_back(s->first);
-		}
-		return mw;
-	}
-
-	/**
-	 * @return Current planet
-	 */
-	inline World planet() const
-	{
-		Mutex::Lock _l(_upstreams_m);
-		return _planet;
+		// TODO
+		return std::vector<Address>();
 	}
 
-	/**
-	 * @return Current planet's world ID
-	 */
-	inline uint64_t planetWorldId() const
-	{
-		return _planet.id(); // safe to read without lock, and used from within eachPeer() so don't lock
-	}
-
-	/**
-	 * @return Current planet's world timestamp
-	 */
-	inline uint64_t planetWorldTimestamp() const
-	{
-		return _planet.timestamp(); // safe to read without lock, and used from within eachPeer() so don't lock
-	}
-
-	/**
-	 * Validate new world and update if newer and signature is okay
-	 *
-	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
-	 * @param newWorld A new or updated planet or moon to learn
-	 * @param alwaysAcceptNew If true, always accept new moons even if we're not waiting for one
-	 * @return True if it was valid and newer than current (or totally new for moons)
-	 */
-	bool addWorld(void *tPtr,const World &newWorld,bool alwaysAcceptNew);
-
-	/**
-	 * Add a moon
-	 *
-	 * This loads it from moons.d if present, and if not adds it to
-	 * a list of moons that we want to contact.
-	 *
-	 * @param id Moon ID
-	 * @param seed If non-NULL, an address of any member of the moon to contact
-	 */
-	void addMoon(void *tPtr,const uint64_t id,const Address &seed);
-
-	/**
-	 * Remove a moon
-	 *
-	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
-	 * @param id Moon's world ID
-	 */
-	void removeMoon(void *tPtr,const uint64_t id);
-
 	/**
 	 * Clean and flush database
 	 */
@@ -333,11 +226,6 @@ public:
 		return _peers.entries();
 	}
 
-	/**
-	 * @return True if I am a root server in a planet or moon
-	 */
-	inline bool amUpstream() const { return _amUpstream; }
-
 	/**
 	 * Get info about a path
 	 *
@@ -406,39 +294,7 @@ public:
 	/**
 	 * Set or clear physical path configuration (called via Node::setPhysicalPathConfiguration)
 	 */
-	inline void setPhysicalPathConfiguration(const struct sockaddr_storage *pathNetwork,const ZT_PhysicalPathConfiguration *pathConfig)
-	{
-		if (!pathNetwork) {
-			_numConfiguredPhysicalPaths = 0;
-		} else {
-			std::map<InetAddress,ZT_PhysicalPathConfiguration> cpaths;
-			for(unsigned int i=0,j=_numConfiguredPhysicalPaths;i<j;++i)
-				cpaths[_physicalPathConfig[i].first] = _physicalPathConfig[i].second;
-
-			if (pathConfig) {
-				ZT_PhysicalPathConfiguration pc(*pathConfig);
-
-				if (pc.mtu <= 0)
-					pc.mtu = ZT_DEFAULT_PHYSMTU;
-				else if (pc.mtu < ZT_MIN_PHYSMTU)
-					pc.mtu = ZT_MIN_PHYSMTU;
-				else if (pc.mtu > ZT_MAX_PHYSMTU)
-					pc.mtu = ZT_MAX_PHYSMTU;
-
-				cpaths[*(reinterpret_cast<const InetAddress *>(pathNetwork))] = pc;
-			} else {
-				cpaths.erase(*(reinterpret_cast<const InetAddress *>(pathNetwork)));
-			}
-
-			unsigned int cnt = 0;
-			for(std::map<InetAddress,ZT_PhysicalPathConfiguration>::const_iterator i(cpaths.begin());((i!=cpaths.end())&&(cnt<ZT_MAX_CONFIGURABLE_PATHS));++i) {
-				_physicalPathConfig[cnt].first = i->first;
-				_physicalPathConfig[cnt].second = i->second;
-				++cnt;
-			}
-			_numConfiguredPhysicalPaths = cnt;
-		}
-	}
+	void setPhysicalPathConfiguration(const struct sockaddr_storage *pathNetwork,const ZT_PhysicalPathConfiguration *pathConfig);
 
 private:
 	Identity _getIdentity(void *tPtr,const Address &zta);
@@ -448,20 +304,13 @@ private:
 	const RuntimeEnvironment *const RR;
 
 	std::pair<InetAddress,ZT_PhysicalPathConfiguration> _physicalPathConfig[ZT_MAX_CONFIGURABLE_PATHS];
-	volatile unsigned int _numConfiguredPhysicalPaths;
+	unsigned int _numConfiguredPhysicalPaths;
 
 	Hashtable< Address,SharedPtr<Peer> > _peers;
 	Mutex _peers_m;
 
 	Hashtable< Path::HashKey,SharedPtr<Path> > _paths;
 	Mutex _paths_m;
-
-	World _planet;
-	std::vector<World> _moons;
-	std::vector< std::pair<uint64_t,Address> > _moonSeeds;
-	std::vector<Address> _upstreamAddresses;
-	bool _amUpstream;
-	Mutex _upstreams_m; // locks worlds, upstream info, moon info, etc.
 };
 
 } // namespace ZeroTier

+ 160 - 5
node/Utils.cpp

@@ -1,10 +1,10 @@
 /*
  * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2019  ZeroTier, Inc.  https://www.zerotier.com/
+ * Copyright (C) 2011-2019  ZeroTier,Inc.  https://www.zerotier.com/
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
+ * the Free Software Foundation,either version 3 of the License,or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
@@ -13,7 +13,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * along with this program. If not,see <http://www.gnu.org/licenses/>.
  *
  * --
  *
@@ -69,7 +69,7 @@ static unsigned long _Utils_itoa(unsigned long n,char *s)
 	if (n == 0)
 		return 0;
 	unsigned long pos = _Utils_itoa(n / 10,s);
-	if (pos >= 22) // sanity check, should be impossible
+	if (pos >= 22) // sanity check,should be impossible
 		pos = 22;
 	s[pos] = '0' + (char)(n % 10);
 	return pos + 1;
@@ -99,7 +99,7 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes)
 	 * CSPRNG we use. There have been several bugs at the OS or OS distribution
 	 * level in the past that resulted in systematically weak or predictable
 	 * keys due to random seeding problems. This mitigates that by grabbing
-	 * a bit of extra entropy and further randomizing the result, and comes
+	 * a bit of extra entropy and further randomizing the result,and comes
 	 * at almost no cost and with no real downside if the random source is
 	 * good. */
 	if (!s20Initialized) {
@@ -171,4 +171,159 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes)
 #endif // __WINDOWS__ or not
 }
 
+int Utils::b32d(const char *encoded,uint8_t *result,int bufSize)
+{
+  int buffer = 0;
+  int bitsLeft = 0;
+  int count = 0;
+  for (const uint8_t *ptr = (const uint8_t *)encoded;count<bufSize && *ptr; ++ptr) {
+    uint8_t ch = *ptr;
+    if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == '-' || ch == '.') {
+      continue;
+    }
+    buffer <<= 5;
+
+    if (ch == '0') {
+      ch = 'O';
+    } else if (ch == '1') {
+      ch = 'L';
+    } else if (ch == '8') {
+      ch = 'B';
+    }
+
+    if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) {
+      ch = (ch & 0x1F) - 1;
+    } else if (ch >= '2' && ch <= '7') {
+      ch -= '2' - 26;
+    } else {
+      return -1;
+    }
+
+    buffer |= ch;
+    bitsLeft += 5;
+    if (bitsLeft >= 8) {
+      result[count++] = buffer >> (bitsLeft - 8);
+      bitsLeft -= 8;
+    }
+  }
+  if (count < bufSize)
+    result[count] = (uint8_t)0;
+  return count;
+}
+
+int Utils::b32e(const uint8_t *data,int length,char *result,int bufSize)
+{
+  if (length < 0 || length > (1 << 28)) {
+		result[0] = (char)0;
+    return -1;
+	}
+	int count = 0;
+  if (length > 0) {
+    int buffer = data[0];
+    int next = 1;
+    int bitsLeft = 8;
+    while (count < bufSize && (bitsLeft > 0 || next < length)) {
+      if (bitsLeft < 5) {
+        if (next < length) {
+          buffer <<= 8;
+          buffer |= data[next++] & 0xFF;
+          bitsLeft += 8;
+        } else {
+          int pad = 5 - bitsLeft;
+          buffer <<= pad;
+          bitsLeft += pad;
+        }
+      }
+      int index = 0x1F & (buffer >> (bitsLeft - 5));
+      bitsLeft -= 5;
+      result[count++] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"[index];
+    }
+  }
+  if (count < bufSize) {
+		result[count] = (char)0;
+		return count;
+	}
+	result[0] = (char)0;
+	return -1;
+}
+
+unsigned int Utils::b64e(const uint8_t *in,unsigned int inlen,char *out,unsigned int outlen)
+{
+	static const char base64en[64] = { 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' };
+	unsigned int i = 0,j = 0;
+	uint8_t l = 0;
+	int s = 0;
+	for (;i<inlen;++i) {
+		uint8_t c = in[i];
+		switch (s) {
+		case 0:
+			s = 1;
+			if (j >= outlen) return 0;
+			out[j++] = base64en[(c >> 2) & 0x3f];
+			break;
+		case 1:
+			s = 2;
+			if (j >= outlen) return 0;
+			out[j++] = base64en[((l & 0x3) << 4) | ((c >> 4) & 0xf)];
+			break;
+		case 2:
+			s = 0;
+			if (j >= outlen) return 0;
+			out[j++] = base64en[((l & 0xf) << 2) | ((c >> 6) & 0x3)];
+			if (j >= outlen) return 0;
+			out[j++] = base64en[c & 0x3f];
+			break;
+		}
+		l = c;
+	}
+	switch (s) {
+	case 1:
+		if (j >= outlen) return 0;
+		out[j++] = base64en[(l & 0x3) << 4];
+		//out[j++] = '=';
+		//out[j++] = '=';
+		break;
+	case 2:
+		if (j >= outlen) return 0;
+		out[j++] = base64en[(l & 0xf) << 2];
+		//out[j++] = '=';
+		break;
+	}
+	if (j >= outlen) return 0;
+	out[j] = 0;
+	return j;
+}
+
+unsigned int Utils::b64d(const char *in,unsigned char *out,unsigned int outlen)
+{
+	static const uint8_t base64de[256] = { 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,62,255,255,255,63,52,53,54,55,56,57,58,59,60,61,255,255,255,255,255,255,255,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,255,255,255,255,255,255,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,255,255,255,255,255 };
+	unsigned int i = 0;
+	unsigned int j = 0;
+	while ((in[i] != '=')&&(in[i] != 0)) {
+		if (j >= outlen)
+			break;
+		uint8_t c = base64de[(unsigned char)in[i]];
+		if (c != 255) {
+			switch (i & 0x3) {
+				case 0:
+					out[j] = (c << 2) & 0xff;
+					break;
+				case 1:
+					out[j++] |= (c >> 4) & 0x3;
+					out[j] = (c & 0xf) << 4; 
+					break;
+				case 2:
+					out[j++] |= (c >> 2) & 0xf;
+					out[j] = (c & 0x3) << 6;
+					break;
+				case 3:
+					out[j++] |= c;
+					break;
+			}
+		}
+		++i;
+	}
+	return j;
+}
+
 } // namespace ZeroTier

+ 7 - 4
node/Utils.hpp

@@ -38,10 +38,6 @@
 #include <vector>
 #include <map>
 
-#if defined(__FreeBSD__)
-#include <sys/endian.h>
-#endif
-
 #include "Constants.hpp"
 
 namespace ZeroTier {
@@ -246,6 +242,13 @@ public:
 	 */
 	static void getSecureRandom(void *buf,unsigned int bytes);
 
+	static int b32d(const char *encoded, uint8_t *result, int bufSize);
+	static int b32e(const uint8_t *data,int length,char *result,int bufSize);
+
+	static inline unsigned int b64MaxEncodedSize(const unsigned int s) { return ((((s + 2) / 3) * 4) + 1); }
+	static unsigned int b64e(const uint8_t *in,unsigned int inlen,char *out,unsigned int outlen);
+	static unsigned int b64d(const char *in,uint8_t *out,unsigned int outlen);
+
 	/**
 	 * Tokenize a string (alias for strtok_r or strtok_s depending on platform)
 	 *

+ 0 - 284
node/World.hpp

@@ -1,284 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2019  ZeroTier, Inc.  https://www.zerotier.com/
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * --
- *
- * You can be released from the requirements of the license by purchasing
- * a commercial license. Buying such a license is mandatory as soon as you
- * develop commercial closed-source software that incorporates or links
- * directly against ZeroTier software without disclosing the source code
- * of your own application.
- */
-
-#ifndef ZT_WORLD_HPP
-#define ZT_WORLD_HPP
-
-#include <vector>
-#include <string>
-
-#include "Constants.hpp"
-#include "InetAddress.hpp"
-#include "Identity.hpp"
-#include "Buffer.hpp"
-#include "C25519.hpp"
-
-/**
- * Maximum number of roots (sanity limit, okay to increase)
- *
- * A given root can (through multi-homing) be distributed across any number of
- * physical endpoints, but having more than one is good to permit total failure
- * of one root or its withdrawal due to compromise without taking the whole net
- * down.
- */
-#define ZT_WORLD_MAX_ROOTS 4
-
-/**
- * Maximum number of stable endpoints per root (sanity limit, okay to increase)
- */
-#define ZT_WORLD_MAX_STABLE_ENDPOINTS_PER_ROOT 32
-
-/**
- * The (more than) maximum length of a serialized World
- */
-#define ZT_WORLD_MAX_SERIALIZED_LENGTH (((1024 + (32 * ZT_WORLD_MAX_STABLE_ENDPOINTS_PER_ROOT)) * ZT_WORLD_MAX_ROOTS) + ZT_C25519_PUBLIC_KEY_LEN + ZT_C25519_SIGNATURE_LEN + 128)
-
-/**
- * World ID for Earth
- *
- * This is the ID for the ZeroTier World used on planet Earth. It is unrelated
- * to the public network 8056c2e21c000001 of the same name. It was chosen
- * from Earth's approximate distance from the sun in kilometers.
- */
-#define ZT_WORLD_ID_EARTH 149604618
-
-/**
- * World ID for Mars -- for future use by SpaceX or others
- */
-#define ZT_WORLD_ID_MARS 227883110
-
-namespace ZeroTier {
-
-/**
- * A world definition (formerly known as a root topology)
- *
- * Think of a World as a single data center. Within this data center a set
- * of distributed fault tolerant root servers provide stable anchor points
- * for a peer to peer network that provides VLAN service. Updates to a world
- * definition can be published by signing them with the previous revision's
- * signing key, and should be very infrequent.
- *
- * The maximum data center size is approximately 2.5 cubic light seconds,
- * since many protocols have issues with >5s RTT latencies.
- *
- * ZeroTier operates a World for Earth capable of encompassing the planet, its
- * orbits, the Moon (about 1.3 light seconds), and nearby Lagrange points. A
- * world ID for Mars and nearby space is defined but not yet used, and a test
- * world ID is provided for testing purposes.
- */
-class World
-{
-public:
-	/**
-	 * World type -- do not change IDs
-	 */
-	enum Type
-	{
-		TYPE_NULL = 0,
-		TYPE_PLANET = 1, // Planets, of which there is currently one (Earth)
-		TYPE_MOON = 127  // Moons, which are user-created and many
-	};
-
-	/**
-	 * Upstream server definition in world/moon
-	 */
-	struct Root
-	{
-		Identity identity;
-		std::vector<InetAddress> stableEndpoints;
-
-		inline bool operator==(const Root &r) const { return ((identity == r.identity)&&(stableEndpoints == r.stableEndpoints)); }
-		inline bool operator!=(const Root &r) const { return (!(*this == r)); }
-		inline bool operator<(const Root &r) const { return (identity < r.identity); } // for sorting
-	};
-
-	/**
-	 * Construct an empty / null World
-	 */
-	World() :
-		_id(0),
-		_ts(0),
-		_type(TYPE_NULL) {}
-
-	/**
-	 * @return Root servers for this world and their stable endpoints
-	 */
-	inline const std::vector<World::Root> &roots() const { return _roots; }
-
-	/**
-	 * @return World type: planet or moon
-	 */
-	inline Type type() const { return _type; }
-
-	/**
-	 * @return World unique identifier
-	 */
-	inline uint64_t id() const { return _id; }
-
-	/**
-	 * @return World definition timestamp
-	 */
-	inline uint64_t timestamp() const { return _ts; }
-
-	/**
-	 * @return C25519 signature
-	 */
-	inline const C25519::Signature &signature() const { return _signature; }
-
-	/**
-	 * @return Public key that must sign next update
-	 */
-	inline const C25519::Public &updatesMustBeSignedBy() const { return _updatesMustBeSignedBy; }
-
-	/**
-	 * Check whether a world update should replace this one
-	 *
-	 * @param update Candidate update
-	 * @return True if update is newer than current, matches its ID and type, and is properly signed (or if current is NULL)
-	 */
-	inline bool shouldBeReplacedBy(const World &update)
-	{
-		if ((_id == 0)||(_type == TYPE_NULL))
-			return true;
-		if ((_id == update._id)&&(_ts < update._ts)&&(_type == update._type)) {
-			Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> tmp;
-			update.serialize(tmp,true);
-			return C25519::verify(_updatesMustBeSignedBy,tmp.data(),tmp.size(),update._signature);
-		}
-		return false;
-	}
-
-	/**
-	 * @return True if this World is non-empty
-	 */
-	inline operator bool() const { return (_type != TYPE_NULL); }
-
-	template<unsigned int C>
-	inline void serialize(Buffer<C> &b,bool forSign = false) const
-	{
-		if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
-
-		b.append((uint8_t)_type);
-		b.append((uint64_t)_id);
-		b.append((uint64_t)_ts);
-		b.append(_updatesMustBeSignedBy.data,ZT_C25519_PUBLIC_KEY_LEN);
-		if (!forSign)
-			b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
-		b.append((uint8_t)_roots.size());
-		for(std::vector<Root>::const_iterator r(_roots.begin());r!=_roots.end();++r) {
-			r->identity.serialize(b);
-			b.append((uint8_t)r->stableEndpoints.size());
-			for(std::vector<InetAddress>::const_iterator ep(r->stableEndpoints.begin());ep!=r->stableEndpoints.end();++ep)
-				ep->serialize(b);
-		}
-		if (_type == TYPE_MOON)
-			b.append((uint16_t)0); // no attached dictionary (for future use)
-
-		if (forSign) b.append((uint64_t)0xf7f7f7f7f7f7f7f7ULL);
-	}
-
-	template<unsigned int C>
-	inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
-	{
-		unsigned int p = startAt;
-
-		_roots.clear();
-
-		switch((Type)b[p++]) {
-			case TYPE_NULL: _type = TYPE_NULL; break; // shouldn't ever really happen in serialized data but it's not invalid
-			case TYPE_PLANET: _type = TYPE_PLANET; break;
-			case TYPE_MOON: _type = TYPE_MOON; break;
-			default:
-				throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
-		}
-
-		_id = b.template at<uint64_t>(p); p += 8;
-		_ts = b.template at<uint64_t>(p); p += 8;
-		memcpy(_updatesMustBeSignedBy.data,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN); p += ZT_C25519_PUBLIC_KEY_LEN;
-		memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
-		const unsigned int numRoots = (unsigned int)b[p++];
-		if (numRoots > ZT_WORLD_MAX_ROOTS)
-			throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
-		for(unsigned int k=0;k<numRoots;++k) {
-			_roots.push_back(Root());
-			Root &r = _roots.back();
-			p += r.identity.deserialize(b,p);
-			unsigned int numStableEndpoints = b[p++];
-			if (numStableEndpoints > ZT_WORLD_MAX_STABLE_ENDPOINTS_PER_ROOT)
-				throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
-			for(unsigned int kk=0;kk<numStableEndpoints;++kk) {
-				r.stableEndpoints.push_back(InetAddress());
-				p += r.stableEndpoints.back().deserialize(b,p);
-			}
-		}
-		if (_type == TYPE_MOON)
-			p += b.template at<uint16_t>(p) + 2;
-
-		return (p - startAt);
-	}
-
-	inline bool operator==(const World &w) const { return ((_id == w._id)&&(_ts == w._ts)&&(memcmp(_updatesMustBeSignedBy.data,w._updatesMustBeSignedBy.data,ZT_C25519_PUBLIC_KEY_LEN) == 0)&&(memcmp(_signature.data,w._signature.data,ZT_C25519_SIGNATURE_LEN) == 0)&&(_roots == w._roots)&&(_type == w._type)); }
-	inline bool operator!=(const World &w) const { return (!(*this == w)); }
-
-	/**
-	 * Create a World object signed with a key pair
-	 *
-	 * @param t World type
-	 * @param id World ID
-	 * @param ts World timestamp / revision
-	 * @param sk Key that must be used to sign the next future update to this world
-	 * @param roots Roots and their stable endpoints
-	 * @param signWith Key to sign this World with (can have the same public as the next-update signing key, but doesn't have to)
-	 * @return Signed World object
-	 */
-	static inline World make(World::Type t,uint64_t id,uint64_t ts,const C25519::Public &sk,const std::vector<World::Root> &roots,const C25519::Pair &signWith)
-	{
-		World w;
-		w._id = id;
-		w._ts = ts;
-		w._type = t;
-		w._updatesMustBeSignedBy = sk;
-		w._roots = roots;
-
-		Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> tmp;
-		w.serialize(tmp,true);
-		w._signature = C25519::sign(signWith,tmp.data(),tmp.size());
-
-		return w;
-	}
-
-protected:
-	uint64_t _id;
-	uint64_t _ts;
-	Type _type;
-	C25519::Public _updatesMustBeSignedBy;
-	C25519::Signature _signature;
-	std::vector<Root> _roots;
-};
-
-} // namespace ZeroTier
-
-#endif

+ 1 - 0
objects.mk

@@ -3,6 +3,7 @@ CORE_OBJS=\
 	node/Capability.o \
 	node/CertificateOfMembership.o \
 	node/CertificateOfOwnership.o \
+	node/ECC384.o \
 	node/Identity.o \
 	node/IncomingPacket.o \
 	node/InetAddress.o \

+ 5 - 172
one.cpp

@@ -79,7 +79,6 @@
 #include "node/Utils.hpp"
 #include "node/NetworkController.hpp"
 #include "node/Buffer.hpp"
-#include "node/World.hpp"
 
 #include "osdep/OSUtils.hpp"
 #include "osdep/Http.hpp"
@@ -136,9 +135,6 @@ static void cliPrintHelp(const char *pn,FILE *out)
 	fprintf(out,"  leave <network>         - Leave a network" ZT_EOL_S);
 	fprintf(out,"  set <network> <setting> - Set a network setting" ZT_EOL_S);
 	fprintf(out,"  get <network> <setting> - Get a network setting" ZT_EOL_S);
-	fprintf(out,"  listmoons               - List moons (federated root sets)" ZT_EOL_S);
-	fprintf(out,"  orbit <world ID> <seed> - Join a moon via any member root" ZT_EOL_S);
-	fprintf(out,"  deorbit <world ID>      - Leave a moon" ZT_EOL_S);
 	fprintf(out,ZT_EOL_S"Available settings:" ZT_EOL_S);
 	fprintf(out,"  Settings to use with [get/set] may include property names from " ZT_EOL_S);
 	fprintf(out,"  the JSON output of \"zerotier-cli -j listnetworks\". Additionally, " ZT_EOL_S);
@@ -593,80 +589,6 @@ static int cli(int argc,char **argv)
 			printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
 			return 1;
 		}
-	} else if (command == "listmoons") {
-		const unsigned int scode = Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/moon",requestHeaders,responseHeaders,responseBody);
-
-		if (scode == 0) {
-			printf("Error connecting to the ZeroTier service: %s\n\nPlease check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S, responseBody.c_str());
-			return 1;
-		}
-
-		nlohmann::json j;
-		try {
-			j = OSUtils::jsonParse(responseBody);
-		} catch (std::exception &exc) {
-			printf("%u %s invalid JSON response (%s)" ZT_EOL_S,scode,command.c_str(),exc.what());
-			return 1;
-		} catch ( ... ) {
-			printf("%u %s invalid JSON response (unknown exception)" ZT_EOL_S,scode,command.c_str());
-			return 1;
-		}
-
-		if (scode == 200) {
-			printf("%s" ZT_EOL_S,OSUtils::jsonDump(j).c_str());
-			return 0;
-		} else {
-			printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
-			return 1;
-		}
-	} else if (command == "orbit") {
-		const uint64_t worldId = Utils::hexStrToU64(arg1.c_str());
-		const uint64_t seed = Utils::hexStrToU64(arg2.c_str());
-		if ((worldId)&&(seed)) {
-			char jsons[1024];
-			OSUtils::ztsnprintf(jsons,sizeof(jsons),"{\"seed\":\"%s\"}",arg2.c_str());
-			char cl[128];
-			OSUtils::ztsnprintf(cl,sizeof(cl),"%u",(unsigned int)strlen(jsons));
-			requestHeaders["Content-Type"] = "application/json";
-			requestHeaders["Content-Length"] = cl;
-			unsigned int scode = Http::POST(
-				1024 * 1024 * 16,
-				60000,
-				(const struct sockaddr *)&addr,
-				(std::string("/moon/") + arg1).c_str(),
-				requestHeaders,
-				jsons,
-				(unsigned long)strlen(jsons),
-				responseHeaders,
-				responseBody);
-			if (scode == 200) {
-				printf("200 orbit OK" ZT_EOL_S);
-				return 0;
-			} else {
-				printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
-				return 1;
-			}
-		}
-	} else if (command == "deorbit") {
-		unsigned int scode = Http::DEL(
-			1024 * 1024 * 16,
-			60000,
-			(const struct sockaddr *)&addr,
-			(std::string("/moon/") + arg1).c_str(),
-			requestHeaders,
-			responseHeaders,
-			responseBody);
-		if (scode == 200) {
-			if (json) {
-				printf("%s",cliFixJsonCRs(responseBody).c_str());
-			} else {
-				printf("200 deorbit OK" ZT_EOL_S);
-			}
-			return 0;
-		} else {
-			printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
-			return 1;
-		}
 	} else if (command == "set") {
 		if (arg1.length() != 16) {
 			fprintf(stderr,"invalid format: must be a 16-digit (network) ID\n");
@@ -829,8 +751,6 @@ static void idtoolPrintHelp(FILE *out,const char *pn)
 	fprintf(out,"  getpublic <identity.secret>" ZT_EOL_S);
 	fprintf(out,"  sign <identity.secret> <file>" ZT_EOL_S);
 	fprintf(out,"  verify <identity.secret/public> <file> <signature>" ZT_EOL_S);
-	fprintf(out,"  initmoon <identity.public of first seed>" ZT_EOL_S);
-	fprintf(out,"  genmoon <moon json>" ZT_EOL_S);
 }
 
 static Identity getIdFromArg(char *arg)
@@ -872,7 +792,7 @@ static int idtool(int argc,char **argv)
 
 		Identity id;
 		for(;;) {
-			id.generate();
+			id.generate(Identity::C25519);
 			if ((id.address().toInt() >> (40 - vanityBits)) == vanity) {
 				if (vanityBits > 0) {
 					fprintf(stderr,"vanity address: found %.10llx !\n",(unsigned long long)id.address().toInt());
@@ -950,9 +870,10 @@ static int idtool(int argc,char **argv)
 			fprintf(stderr,"%s is not readable" ZT_EOL_S,argv[3]);
 			return 1;
 		}
-		C25519::Signature signature = id.sign(inf.data(),(unsigned int)inf.length());
-		char hexbuf[1024];
-		printf("%s",Utils::hex(signature.data,ZT_C25519_SIGNATURE_LEN,hexbuf));
+		uint8_t signature[ZT_SIGNATURE_BUFFER_SIZE];
+		const unsigned int siglen = id.sign(inf.data(),(unsigned int)inf.length(),signature,sizeof(signature));
+		char hexbuf[256];
+		printf("%s",Utils::hex(signature,siglen,hexbuf));
 	} else if (!strcmp(argv[1],"verify")) {
 		if (argc < 5) {
 			idtoolPrintHelp(stdout,argv[0]);
@@ -990,94 +911,6 @@ static int idtool(int argc,char **argv)
 				return 1;
 			}
 		}
-	} else if (!strcmp(argv[1],"initmoon")) {
-		if (argc < 3) {
-			idtoolPrintHelp(stdout,argv[0]);
-		} else {
-			const Identity id = getIdFromArg(argv[2]);
-			if (!id) {
-				fprintf(stderr,"%s is not a valid identity" ZT_EOL_S,argv[2]);
-				return 1;
-			}
-
-			C25519::Pair kp(C25519::generate());
-
-			char idtmp[4096];
-			nlohmann::json mj;
-			mj["objtype"] = "world";
-			mj["worldType"] = "moon";
-			mj["updatesMustBeSignedBy"] = mj["signingKey"] = Utils::hex(kp.pub.data,ZT_C25519_PUBLIC_KEY_LEN,idtmp);
-			mj["signingKey_SECRET"] = Utils::hex(kp.priv.data,ZT_C25519_PRIVATE_KEY_LEN,idtmp);
-			mj["id"] = id.address().toString(idtmp);
-			nlohmann::json seedj;
-			seedj["identity"] = id.toString(false,idtmp);
-			seedj["stableEndpoints"] = nlohmann::json::array();
-			(mj["roots"] = nlohmann::json::array()).push_back(seedj);
-			std::string mjd(OSUtils::jsonDump(mj));
-
-			printf("%s" ZT_EOL_S,mjd.c_str());
-		}
-	} else if (!strcmp(argv[1],"genmoon")) {
-		if (argc < 3) {
-			idtoolPrintHelp(stdout,argv[0]);
-		} else {
-			std::string buf;
-			if (!OSUtils::readFile(argv[2],buf)) {
-				fprintf(stderr,"cannot read %s" ZT_EOL_S,argv[2]);
-				return 1;
-			}
-			nlohmann::json mj(OSUtils::jsonParse(buf));
-
-			const uint64_t id = Utils::hexStrToU64(OSUtils::jsonString(mj["id"],"0").c_str());
-			if (!id) {
-				fprintf(stderr,"ID in %s is invalid" ZT_EOL_S,argv[2]);
-				return 1;
-			}
-
-			World::Type t;
-			if (mj["worldType"] == "moon") {
-				t = World::TYPE_MOON;
-			} else if (mj["worldType"] == "planet") {
-				t = World::TYPE_PLANET;
-			} else {
-				fprintf(stderr,"invalid worldType" ZT_EOL_S);
-				return 1;
-			}
-
-			C25519::Pair signingKey;
-			C25519::Public updatesMustBeSignedBy;
-			Utils::unhex(OSUtils::jsonString(mj["signingKey"],"").c_str(),signingKey.pub.data,ZT_C25519_PUBLIC_KEY_LEN);
-			Utils::unhex(OSUtils::jsonString(mj["signingKey_SECRET"],"").c_str(),signingKey.priv.data,ZT_C25519_PRIVATE_KEY_LEN);
-			Utils::unhex(OSUtils::jsonString(mj["updatesMustBeSignedBy"],"").c_str(),updatesMustBeSignedBy.data,ZT_C25519_PUBLIC_KEY_LEN);
-
-			std::vector<World::Root> roots;
-			nlohmann::json &rootsj = mj["roots"];
-			if (rootsj.is_array()) {
-				for(unsigned long i=0;i<(unsigned long)rootsj.size();++i) {
-					nlohmann::json &r = rootsj[i];
-					if (r.is_object()) {
-						roots.push_back(World::Root());
-						roots.back().identity = Identity(OSUtils::jsonString(r["identity"],"").c_str());
-						nlohmann::json &stableEndpointsj = r["stableEndpoints"];
-						if (stableEndpointsj.is_array()) {
-							for(unsigned long k=0;k<(unsigned long)stableEndpointsj.size();++k)
-								roots.back().stableEndpoints.push_back(InetAddress(OSUtils::jsonString(stableEndpointsj[k],"").c_str()));
-							std::sort(roots.back().stableEndpoints.begin(),roots.back().stableEndpoints.end());
-						}
-					}
-				}
-			}
-			std::sort(roots.begin(),roots.end());
-
-			const int64_t now = OSUtils::now();
-			World w(World::make(t,id,now,updatesMustBeSignedBy,roots,signingKey));
-			Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> wbuf;
-			w.serialize(wbuf);
-			char fn[128];
-			OSUtils::ztsnprintf(fn,sizeof(fn),"%.16llx.moon",w.id());
-			OSUtils::writeFile(fn,wbuf.data(),wbuf.size());
-			printf("wrote %s (signed world with timestamp %llu)" ZT_EOL_S,fn,(unsigned long long)now);
-		}
 	} else {
 		idtoolPrintHelp(stdout,argv[0]);
 		return 1;

+ 1 - 0
osdep/Phy.hpp

@@ -61,6 +61,7 @@
 #include <sys/un.h>
 #include <arpa/inet.h>
 #include <netinet/in.h>
+#include <netinet/ip6.h>
 #include <netinet/tcp.h>
 
 #if defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux)

+ 113 - 23
selftest.cpp

@@ -50,6 +50,7 @@
 #include "node/Dictionary.hpp"
 #include "node/SHA512.hpp"
 #include "node/C25519.hpp"
+#include "node/ECC384.hpp"
 #include "node/Poly1305.hpp"
 #include "node/CertificateOfMembership.hpp"
 #include "node/Node.hpp"
@@ -78,6 +79,14 @@ using namespace ZeroTier;
 #define KNOWN_GOOD_IDENTITY "8e4df28b72:0:ac3d46abe0c21f3cfe7a6c8d6a85cfcffcb82fbd55af6a4d6350657c68200843fa2e16f9418bbd9702cae365f2af5fb4c420908b803a681d4daef6114d78a2d7:bd8dd6e4ce7022d2f812797a80c6ee8ad180dc4ebf301dec8b06d1be08832bddd63a2f1cfa7b2c504474c75bdc8898ba476ef92e8e2d0509f8441985171ff16e"
 #define KNOWN_BAD_IDENTITY "9e4df28b72:0:ac3d46abe0c21f3cfe7a6c8d6a85cfcffcb82fbd55af6a4d6350657c68200843fa2e16f9418bbd9702cae365f2af5fb4c420908b803a681d4daef6114d78a2d7:bd8dd6e4ce7022d2f812797a80c6ee8ad180dc4ebf301dec8b06d1be08832bddd63a2f1cfa7b2c504474c75bdc8898ba476ef92e8e2d0509f8441985171ff16e"
 
+// These were generated with some Go code using the NIST P-384 elliptic curve. There
+// are official P-384 test vectors but the format of these is funny and converting is
+// a pain, so this is easier. We assume the Go runtime's P-384 implementation is correct.
+#define ECC384_TEST_PUBLIC "02edbcbb1f239bbd9d3d7cef6b37a32669e94df42664fbac7640c22221a6a3df8c9681760f0e67abd45158b31563fb4971"
+#define ECC384_TEST_PRIVATE "62939b4a293cc68698c3d07fb7ff97a2fbc9368a1da5408e4913d41546cbb408fa8cb27fcc3f72f80d167bf0a4c329d3"
+#define ECC384_TEST_DH_SELF_AGREE "f696bd1bda5e528c1d56a36ed9bad784dd201b50c9d868b9529327ab17edc6ae895e7fd9461587f4c8472ef786f5870b"
+#define ECC384_TEST_SIG "98935f0a052cba3ad7d208de64e7772cbde6d91611d2ef03ba129f1498498c2d3650d9cfbb2beacb28e70b90439e018b52db46ecc7f6a95688003cdb4ffe04a1c74c3ffcb8c8704212f437facdb9172f608cb605c6ce37d6c9f00b233910290d"
+
 static const unsigned char s20TV0Key[32] = { 0x0f,0x62,0xb5,0x08,0x5b,0xae,0x01,0x54,0xa7,0xfa,0x4d,0xa0,0xf3,0x46,0x99,0xec,0x3f,0x92,0xe5,0x38,0x8b,0xde,0x31,0x84,0xd7,0x2a,0x7d,0xd0,0x23,0x76,0xc9,0x1c };
 static const unsigned char s20TV0Iv[8] = { 0x28,0x8f,0xf6,0x5d,0xc4,0x2b,0x92,0xf9 };
 static const unsigned char s20TV0Ks[64] = { 0x5e,0x5e,0x71,0xf9,0x01,0x99,0x34,0x03,0x04,0xab,0xb2,0x2a,0x37,0xb6,0x62,0x5b,0xf8,0x83,0xfb,0x89,0xce,0x3b,0x21,0xf5,0x4a,0x10,0xb8,0x10,0x66,0xef,0x87,0xda,0x30,0xb7,0x76,0x99,0xaa,0x73,0x79,0xda,0x59,0x5c,0x77,0xdd,0x59,0x54,0x2d,0xa2,0x08,0xe5,0x95,0x4f,0x89,0xe4,0x0e,0xb7,0xaa,0x80,0xa8,0x4a,0x61,0x76,0x66,0x3f };
@@ -96,6 +105,7 @@ static const unsigned char poly1305TV1Tag[16] = { 0xa6,0xf7,0x45,0x00,0x8f,0x81,
 
 static const char *sha512TV0Input = "supercalifragilisticexpealidocious";
 static const unsigned char sha512TV0Digest[64] = { 0x18,0x2a,0x85,0x59,0x69,0xe5,0xd3,0xe6,0xcb,0xf6,0x05,0x24,0xad,0xf2,0x88,0xd1,0xbb,0xf2,0x52,0x92,0x81,0x24,0x31,0xf6,0xd2,0x52,0xf1,0xdb,0xc1,0xcb,0x44,0xdf,0x21,0x57,0x3d,0xe1,0xb0,0x6b,0x68,0x75,0x95,0x9f,0x3b,0x6f,0x87,0xb1,0x13,0x81,0xd0,0xbc,0x79,0x2c,0x43,0x3a,0x13,0x55,0x3c,0xe0,0x84,0xc2,0x92,0x55,0x31,0x1c };
+static const unsigned char sha384TV0Digest[48] = { 0x71,0xe7,0x71,0x79,0xae,0xc3,0xf3,0x5f,0x93,0xea,0xe2,0x1d,0xe3,0x3f,0x24,0x6d,0xed,0x2a,0x59,0xae,0x22,0x45,0x27,0x6c,0x12,0x57,0xf3,0xbe,0xe6,0xce,0xe2,0x73,0xd8,0xad,0xaa,0x9b,0x99,0xa4,0x8a,0x1b,0x7a,0xb9,0x5d,0xfb,0x9c,0x1a,0x1c,0xf6 };
 
 struct C25519TestVector
 {
@@ -211,7 +221,7 @@ static int testCrypto()
 			bytes += 1234567.0;
 		}
 		uint64_t end = OSUtils::now();
-		SHA512::hash(buf1,bb,1234567);
+		SHA512(buf1,bb,1234567);
 		std::cout << ((bytes / 1048576.0) / ((long double)(end - start) / 1024.0)) << " MiB/second (" << Utils::hex(buf1,16,hexbuf) << ')' << std::endl;
 		::free((void *)bb);
 	}
@@ -263,18 +273,25 @@ static int testCrypto()
 			bytes += 1234567.0;
 		}
 		uint64_t end = OSUtils::now();
-		SHA512::hash(buf1,bb,1234567);
+		SHA512(buf1,bb,1234567);
 		std::cout << ((bytes / 1048576.0) / ((long double)(end - start) / 1024.0)) << " MiB/second (" << Utils::hex(buf1,16,hexbuf) << ')' << std::endl;
 		::free((void *)bb);
 	}
 
 	std::cout << "[crypto] Testing SHA-512... "; std::cout.flush();
-	SHA512::hash(buf1,sha512TV0Input,(unsigned int)strlen(sha512TV0Input));
+	SHA512(buf1,sha512TV0Input,(unsigned int)strlen(sha512TV0Input));
 	if (memcmp(buf1,sha512TV0Digest,64)) {
 		std::cout << "FAIL" << std::endl;
 		return -1;
 	}
 	std::cout << "PASS" << std::endl;
+	std::cout << "[crypto] Testing SHA-384... "; std::cout.flush();
+	SHA384(buf1,sha512TV0Input,(unsigned int)strlen(sha512TV0Input));
+	if (memcmp(buf1,sha384TV0Digest,48)) {
+		std::cout << "FAIL" << std::endl;
+		return -1;
+	}
+	std::cout << "PASS" << std::endl;
 
 	std::cout << "[crypto] Testing Poly1305... "; std::cout.flush();
 	Poly1305::compute(buf1,poly1305TV0Input,sizeof(poly1305TV0Input),poly1305TV0Key);
@@ -305,18 +322,51 @@ static int testCrypto()
 		::free((void *)bb);
 	}
 
-	/*
-	for(unsigned int d=8;d<=10;++d) {
-		for(int k=0;k<8;++k) {
-			std::cout << "[crypto] computeSalsa2012Sha512ProofOfWork(" << d << ",\"foobarbaz\",9) == "; std::cout.flush();
-			unsigned char result[16];
-			uint64_t start = OSUtils::now();
-			IncomingPacket::computeSalsa2012Sha512ProofOfWork(d,"foobarbaz",9,result);
-			uint64_t end = OSUtils::now();
-			std::cout << Utils::hex(result,16) << " -- valid: " << IncomingPacket::testSalsa2012Sha512ProofOfWorkResult(d,"foobarbaz",9,result) << ", " << (end - start) << "ms" << std::endl;
+	std::cout << "[crypto] Testing ECC384 (NIST P-384)..." << std::endl;
+	{
+		uint8_t p384pub[ZT_ECC384_PUBLIC_KEY_SIZE],p384priv[ZT_ECC384_PRIVATE_KEY_SIZE],p384sig[ZT_ECC384_SIGNATURE_SIZE],p384hash[ZT_ECC384_SIGNATURE_HASH_SIZE];
+		char p384hex[256];
+		ECC384GenerateKey(p384pub,p384priv);
+		std::cout << "[crypto]   Public Key: " << Utils::hex(p384pub,sizeof(p384pub),p384hex) << std::endl;
+		Utils::getSecureRandom(p384hash,sizeof(p384hash));
+		ECC384ECDSASign(p384priv,p384hash,p384sig);
+		if (!ECC384ECDSAVerify(p384pub,p384hash,p384sig)) {
+			std::cout << "[crypto]   ECDSA Signature: FAILED (verify good signature)" << std::endl;
+			return -1;
 		}
+		++p384sig[0];
+		if (ECC384ECDSAVerify(p384pub,p384hash,p384sig)) {
+			std::cout << "[crypto]   ECDSA Signature: FAILED (verify bad signature)" << std::endl;
+			return -1;
+		}
+		--p384sig[0];
+		std::cout << "[crypto]   ECDSA Signature: " << Utils::hex(p384sig,sizeof(p384sig),p384hex) << std::endl;
+		uint8_t p384pub2[ZT_ECC384_PUBLIC_KEY_SIZE],p384priv2[ZT_ECC384_PRIVATE_KEY_SIZE],p384sec[ZT_ECC384_SHARED_SECRET_SIZE],p384sec2[ZT_ECC384_SHARED_SECRET_SIZE];
+		ECC384GenerateKey(p384pub2,p384priv2);
+		ECC384ECDH(p384pub,p384priv2,p384sec);
+		ECC384ECDH(p384pub2,p384priv,p384sec2);
+		if (memcmp(p384sec,p384sec2,ZT_ECC384_SHARED_SECRET_SIZE)) {
+			std::cout << "[crypto]   ECDH Agree: FAILED (secrets do not match)" << std::endl;
+			return -1;
+		}
+		std::cout << "[crypto]   ECDH Agree: " << Utils::hex(p384sec,sizeof(p384sec),p384hex) << std::endl;
+
+		Utils::unhex(ECC384_TEST_PUBLIC,p384pub,sizeof(p384pub));
+		Utils::unhex(ECC384_TEST_PRIVATE,p384priv,sizeof(p384priv));
+		ECC384ECDH(p384pub,p384priv,p384sec);
+		Utils::unhex(ECC384_TEST_DH_SELF_AGREE,p384sec2,sizeof(p384sec2));
+		if (memcmp(p384sec,p384sec2,ZT_ECC384_SHARED_SECRET_SIZE)) {
+			std::cout << "[crypto]   ECDH Test Vector: FAILED (secrets do not match)" << std::endl;
+			return -1;
+		}
+		std::cout << "[crypto]   ECDH Test Vector: PASS" << std::endl;
+		Utils::unhex(ECC384_TEST_SIG,p384sig,sizeof(p384sig));
+		if (!ECC384ECDSAVerify(p384pub,p384pub,p384sig)) {
+			std::cout << "[crypto]   ECDSA Test Vector: FAILED (verify failed)" << std::endl;
+			return -1;
+		}
+		std::cout << "[crypto]   ECDSA Test Vector: PASS" << std::endl;
 	}
-	*/
 
 	std::cout << "[crypto] Testing C25519 and Ed25519 against test vectors... "; std::cout.flush();
 	for(int k=0;k<ZT_NUM_C25519_TEST_VECTORS;++k) {
@@ -325,8 +375,8 @@ static int testCrypto()
 		memcpy(p1.priv.data,C25519_TEST_VECTORS[k].priv1,ZT_C25519_PRIVATE_KEY_LEN);
 		memcpy(p2.pub.data,C25519_TEST_VECTORS[k].pub2,ZT_C25519_PUBLIC_KEY_LEN);
 		memcpy(p2.priv.data,C25519_TEST_VECTORS[k].priv2,ZT_C25519_PRIVATE_KEY_LEN);
-		C25519::agree(p1,p2.pub,buf1,64);
-		C25519::agree(p2,p1.pub,buf2,64);
+		C25519::agree(p1.priv,p2.pub,buf1,64);
+		C25519::agree(p2.priv,p1.pub,buf2,64);
 		if (memcmp(buf1,buf2,64)) {
 			std::cout << "FAIL (1)" << std::endl;
 			return -1;
@@ -356,9 +406,9 @@ static int testCrypto()
 		C25519::Pair p1 = C25519::generate();
 		C25519::Pair p2 = C25519::generate();
 		C25519::Pair p3 = C25519::generate();
-		C25519::agree(p1,p2.pub,buf1,64);
-		C25519::agree(p2,p1.pub,buf2,64);
-		C25519::agree(p3,p1.pub,buf3,64);
+		C25519::agree(p1.priv,p2.pub,buf1,64);
+		C25519::agree(p2.priv,p1.pub,buf2,64);
+		C25519::agree(p3.priv,p1.pub,buf3,64);
 		// p1<>p2 should equal p1<>p2
 		if (memcmp(buf1,buf2,64)) {
 			std::cout << "FAIL (1)" << std::endl;
@@ -378,7 +428,7 @@ static int testCrypto()
 		bp[k] = C25519::generate();
 	uint64_t st = OSUtils::now();
 	for(unsigned int k=0;k<50;++k) {
-		C25519::agree(bp[~k & 7],bp[k & 7].pub,buf1,64);
+		C25519::agree(bp[~k & 7].priv,bp[k & 7].pub,buf1,64);
 	}
 	uint64_t et = OSUtils::now();
 	std::cout << ((double)(et - st) / 50.0) << "ms per agreement." << std::endl;
@@ -466,7 +516,7 @@ static int testIdentity()
 	for(unsigned int k=0;k<4;++k) {
 		std::cout << "[identity] Generate identity... "; std::cout.flush();
 		uint64_t genstart = OSUtils::now();
-		id.generate();
+		id.generate(Identity::C25519);
 		uint64_t genend = OSUtils::now();
 		std::cout << "(took " << (genend - genstart) << "ms): " << id.toString(true,buf2) << std::endl;
 		std::cout << "[identity] Locally validate identity: ";
@@ -539,13 +589,13 @@ static int testCertificate()
 
 	Identity authority;
 	std::cout << "[certificate] Generating identity to act as authority... "; std::cout.flush();
-	authority.generate();
+	authority.generate(Identity::C25519);
 	std::cout << authority.address().toString(buf) << std::endl;
 
 	Identity idA,idB;
 	std::cout << "[certificate] Generating identities A and B... "; std::cout.flush();
-	idA.generate();
-	idB.generate();
+	idA.generate(Identity::C25519);
+	idB.generate(Identity::C25519);
 	std::cout << idA.address().toString(buf) << ", " << idB.address().toString(buf) << std::endl;
 
 	std::cout << "[certificate] Generating certificates A and B...";
@@ -662,6 +712,46 @@ static int testOther()
 		return -1;
 	}
 
+	std::cout << "[other] Testing base32... "; std::cout.flush();
+	for(unsigned int i=1;i<1024;++i) {
+		Utils::getSecureRandom(buf,(unsigned int)sizeof(buf));
+		int l = Utils::b32e((const uint8_t *)buf,i,buf2,sizeof(buf2));
+		if (l <= 0) {
+			std::cout << "FAIL (encode returned 0)" << std::endl;
+			return -1;
+		}
+		int l2 = Utils::b32d(buf2,(uint8_t *)buf3,sizeof(buf3));
+		if (l2 != (int)i) {
+			std::cout << "FAIL (decode returned wrong count)" << std::endl;
+			return -1;
+		}
+		if (memcmp(buf,buf3,i) != 0) {
+			std::cout << "FAIL (decode result incorrect)" << std::endl;
+			return -1;
+		}
+	}
+	std::cout << "PASS" << std::endl;
+
+	std::cout << "[other] Testing base64... "; std::cout.flush();
+	for(unsigned int i=1;i<1024;++i) {
+		Utils::getSecureRandom(buf,(unsigned int)sizeof(buf));
+		unsigned int l = Utils::b64e((const uint8_t *)buf,i,buf2,sizeof(buf2));
+		if (l == 0) {
+			std::cout << "FAIL (encode returned 0)" << std::endl;
+			return -1;
+		}
+		unsigned int l2 = Utils::b64d(buf2,(uint8_t *)buf3,sizeof(buf3));
+		if (l2 != i) {
+			std::cout << "FAIL (decode returned wrong count)" << std::endl;
+			return -1;
+		}
+		if (memcmp(buf,buf3,i) != 0) {
+			std::cout << "FAIL (decode result incorrect)" << std::endl;
+			return -1;
+		}
+	}
+	std::cout << "PASS" << std::endl;
+
 	std::cout << "[other] Testing InetAddress encode/decode..."; std::cout.flush();
 	std::cout << " " << InetAddress("127.0.0.1/9993").toString(buf);
 	std::cout << " " << InetAddress("feed:dead:babe:dead:beef:f00d:1234:5678/12345").toString(buf);

+ 6 - 123
service/OneService.cpp

@@ -48,7 +48,6 @@
 #include "../node/InetAddress.hpp"
 #include "../node/MAC.hpp"
 #include "../node/Identity.hpp"
-#include "../node/World.hpp"
 #include "../node/Salsa20.hpp"
 #include "../node/Poly1305.hpp"
 #include "../node/SHA512.hpp"
@@ -140,6 +139,9 @@ using json = nlohmann::json;
 // TCP activity timeout
 #define ZT_TCP_ACTIVITY_TIMEOUT 60000
 
+// How often local.conf is checked for changes
+#define ZT_LOCAL_CONF_FILE_CHECK_INTERVAL 10000
+
 #if ZT_VAULT_SUPPORT
 size_t curlResponseWrite(void *ptr, size_t size, size_t nmemb, std::string *data)
 {
@@ -313,28 +315,6 @@ static void _peerAggregateLinkToJson(nlohmann::json &pj,const ZT_Peer *peer)
 	pj["paths"] = pa;
 }
 
-static void _moonToJson(nlohmann::json &mj,const World &world)
-{
-	char tmp[4096];
-	OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.16llx",world.id());
-	mj["id"] = tmp;
-	mj["timestamp"] = world.timestamp();
-	mj["signature"] = Utils::hex(world.signature().data,ZT_C25519_SIGNATURE_LEN,tmp);
-	mj["updatesMustBeSignedBy"] = Utils::hex(world.updatesMustBeSignedBy().data,ZT_C25519_PUBLIC_KEY_LEN,tmp);
-	nlohmann::json ra = nlohmann::json::array();
-	for(std::vector<World::Root>::const_iterator r(world.roots().begin());r!=world.roots().end();++r) {
-		nlohmann::json rj;
-		rj["identity"] = r->identity.toString(false,tmp);
-		nlohmann::json eps = nlohmann::json::array();
-		for(std::vector<InetAddress>::const_iterator a(r->stableEndpoints.begin());a!=r->stableEndpoints.end();++a)
-			eps.push_back(a->toString(tmp));
-		rj["stableEndpoints"] = eps;
-		ra.push_back(rj);
-	}
-	mj["roots"] = ra;
-	mj["waiting"] = false;
-}
-
 class OneServiceImpl;
 
 static int SnodeVirtualNetworkConfigFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t nwid,void **nuptr,enum ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nwconf);
@@ -747,16 +727,6 @@ public:
 				}
 			}
 
-			// Orbit existing moons in moons.d
-			{
-				std::vector<std::string> moonsDotD(OSUtils::listDirectory((_homePath + ZT_PATH_SEPARATOR_S "moons.d").c_str()));
-				for(std::vector<std::string>::iterator f(moonsDotD.begin());f!=moonsDotD.end();++f) {
-					std::size_t dot = f->find_last_of('.');
-					if ((dot == 16)&&(f->substr(16) == ".moon"))
-						_node->orbit((void *)0,Utils::hexStrToU64(f->substr(0,dot).c_str()),0);
-				}
-			}
-
 			// Main I/O loop
 			_nextBackgroundTaskDeadline = 0;
 			int64_t clockShouldBe = OSUtils::now();
@@ -1254,37 +1224,8 @@ public:
 					settings["softwareUpdate"] = OSUtils::jsonString(settings["softwareUpdate"],ZT_SOFTWARE_UPDATE_DEFAULT);
 					settings["softwareUpdateChannel"] = OSUtils::jsonString(settings["softwareUpdateChannel"],ZT_SOFTWARE_UPDATE_DEFAULT_CHANNEL);
 #endif
-					const World planet(_node->planet());
-					res["planetWorldId"] = planet.id();
-					res["planetWorldTimestamp"] = planet.timestamp();
 
 					scode = 200;
-				} else if (ps[0] == "moon") {
-					std::vector<World> moons(_node->moons());
-					if (ps.size() == 1) {
-						// Return [array] of all moons
-
-						res = json::array();
-						for(std::vector<World>::const_iterator m(moons.begin());m!=moons.end();++m) {
-							json mj;
-							_moonToJson(mj,*m);
-							res.push_back(mj);
-						}
-
-						scode = 200;
-					} else {
-						// Return a single moon by ID
-
-						const uint64_t id = Utils::hexStrToU64(ps[1].c_str());
-						for(std::vector<World>::const_iterator m(moons.begin());m!=moons.end();++m) {
-							if (m->id() == id) {
-								_moonToJson(res,*m);
-								scode = 200;
-								break;
-							}
-						}
-
-					}
 				} else if (ps[0] == "network") {
 					ZT_VirtualNetworkList *nws = _node->networks();
 					if (nws) {
@@ -1357,44 +1298,7 @@ public:
 		} else if ((httpMethod == HTTP_POST)||(httpMethod == HTTP_PUT)) {
 			if (isAuth) {
 
-				if (ps[0] == "moon") {
-					if (ps.size() == 2) {
-
-						uint64_t seed = 0;
-						try {
-							json j(OSUtils::jsonParse(body));
-							if (j.is_object()) {
-								seed = Utils::hexStrToU64(OSUtils::jsonString(j["seed"],"0").c_str());
-							}
-						} catch ( ... ) {
-							// discard invalid JSON
-						}
-
-						std::vector<World> moons(_node->moons());
-						const uint64_t id = Utils::hexStrToU64(ps[1].c_str());
-						for(std::vector<World>::const_iterator m(moons.begin());m!=moons.end();++m) {
-							if (m->id() == id) {
-								_moonToJson(res,*m);
-								scode = 200;
-								break;
-							}
-						}
-
-						if ((scode != 200)&&(seed != 0)) {
-							char tmp[64];
-							OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.16llx",id);
-							res["id"] = tmp;
-							res["roots"] = json::array();
-							res["timestamp"] = 0;
-							res["signature"] = json();
-							res["updatesMustBeSignedBy"] = json();
-							res["waiting"] = true;
-							_node->orbit((void *)0,id,seed);
-							scode = 200;
-						}
-
-					} else scode = 404;
-				} else if (ps[0] == "network") {
+				if (ps[0] == "network") {
 					if (ps.size() == 2) {
 
 						uint64_t wantnw = Utils::hexStrToU64(ps[1].c_str());
@@ -1441,13 +1345,7 @@ public:
 		} else if (httpMethod == HTTP_DELETE) {
 			if (isAuth) {
 
-				if (ps[0] == "moon") {
-					if (ps.size() == 2) {
-						_node->deorbit((void *)0,Utils::hexStrToU64(ps[1].c_str()));
-						res["result"] = true;
-						scode = 200;
-					} // else 404
-				} else if (ps[0] == "network") {
+				if (ps[0] == "network") {
 					ZT_VirtualNetworkList *nws = _node->networks();
 					if (nws) {
 						if (ps.size() == 2) {
@@ -2234,9 +2132,7 @@ public:
 			}	break;
 
 			case ZT_EVENT_REMOTE_TRACE: {
-				const ZT_RemoteTrace *rt = reinterpret_cast<const ZT_RemoteTrace *>(metaData);
-				if ((rt)&&(rt->len > 0)&&(rt->len <= ZT_MAX_REMOTE_TRACE_SIZE)&&(rt->data))
-					_controller->handleRemoteTrace(*rt);
+				// TODO
 			}
 
 			default:
@@ -2340,13 +2236,6 @@ public:
 				OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "identity.secret",_homePath.c_str());
 				secure = true;
 				break;
-			case ZT_STATE_OBJECT_PLANET:
-				OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "planet",_homePath.c_str());
-				break;
-			case ZT_STATE_OBJECT_MOON:
-				OSUtils::ztsnprintf(dirname,sizeof(dirname),"%s" ZT_PATH_SEPARATOR_S "moons.d",_homePath.c_str());
-				OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "%.16llx.moon",dirname,(unsigned long long)id[0]);
-				break;
 			case ZT_STATE_OBJECT_NETWORK_CONFIG:
 				OSUtils::ztsnprintf(dirname,sizeof(dirname),"%s" ZT_PATH_SEPARATOR_S "networks.d",_homePath.c_str());
 				OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "%.16llx.conf",dirname,(unsigned long long)id[0]);
@@ -2492,12 +2381,6 @@ public:
 			case ZT_STATE_OBJECT_IDENTITY_SECRET:
 				OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "identity.secret",_homePath.c_str());
 				break;
-			case ZT_STATE_OBJECT_PLANET:
-				OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "planet",_homePath.c_str());
-				break;
-			case ZT_STATE_OBJECT_MOON:
-				OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "moons.d" ZT_PATH_SEPARATOR_S "%.16llx.moon",_homePath.c_str(),(unsigned long long)id[0]);
-				break;
 			case ZT_STATE_OBJECT_NETWORK_CONFIG:
 				OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S "%.16llx.conf",_homePath.c_str(),(unsigned long long)id[0]);
 				break;

+ 2 - 2
service/SoftwareUpdater.cpp

@@ -127,7 +127,7 @@ void SoftwareUpdater::setUpdateDistribution(bool distribute)
 						const std::string metaHash(OSUtils::jsonBinFromHex(d.meta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_HASH]));
 						if ((metaHash.length() == ZT_SHA512_DIGEST_LEN)&&(OSUtils::readFile(binPath.c_str(),d.bin))) {
 							std::array<uint8_t,ZT_SHA512_DIGEST_LEN> sha512;
-							SHA512::hash(sha512.data(),d.bin.data(),(unsigned int)d.bin.length());
+							SHA512(sha512.data(),d.bin.data(),(unsigned int)d.bin.length());
 							if (!memcmp(sha512.data(),metaHash.data(),ZT_SHA512_DIGEST_LEN)) { // double check that hash in JSON is correct
 								d.meta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIZE] = d.bin.length(); // override with correct value -- setting this in meta json is optional
 								std::array<uint8_t,16> shakey;
@@ -347,7 +347,7 @@ bool SoftwareUpdater::check(const int64_t now)
 			try {
 				// (1) Check the hash itself to make sure the image is basically okay
 				uint8_t sha512[ZT_SHA512_DIGEST_LEN];
-				SHA512::hash(sha512,_download.data(),(unsigned int)_download.length());
+				SHA512(sha512,_download.data(),(unsigned int)_download.length());
 				char hexbuf[(ZT_SHA512_DIGEST_LEN * 2) + 2];
 				if (OSUtils::jsonString(_latestMeta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_HASH],"") == Utils::hex(sha512,ZT_SHA512_DIGEST_LEN,hexbuf)) {
 					// (2) Check signature by signing authority

Some files were not shown because too many files changed in this diff