diff --git a/attic/root.cpp b/attic/root.cpp new file mode 100644 index 000000000..aa05da7b2 --- /dev/null +++ b/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 . + * + * -- + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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 sockets; + std::vector threads; + for(unsigned int tn=0;tn 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; +} diff --git a/attic/world/README.md b/attic/world/README.md deleted file mode 100644 index dda4920ae..000000000 --- a/attic/world/README.md +++ /dev/null @@ -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'. - diff --git a/attic/world/build.sh b/attic/world/build.sh deleted file mode 100755 index aeb3b87fa..000000000 --- a/attic/world/build.sh +++ /dev/null @@ -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 diff --git a/attic/world/mkworld.cpp b/attic/world/mkworld.cpp deleted file mode 100644 index b8cb027b4..000000000 --- a/attic/world/mkworld.cpp +++ /dev/null @@ -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 . - */ - -/* - * 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 -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -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 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 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 0) - fprintf(stdout,","); - fprintf(stdout,"0x%.2x",(unsigned int)d[i]); - } - fprintf(stdout,"};" ZT_EOL_S); - - return 0; -} diff --git a/attic/world/world.bin b/attic/world/world.bin deleted file mode 100644 index fff7e2a99..000000000 Binary files a/attic/world/world.bin and /dev/null differ diff --git a/attic/world/world.c b/attic/world/world.c deleted file mode 100644 index ecf30e6f0..000000000 --- a/attic/world/world.c +++ /dev/null @@ -1,3 +0,0 @@ - -#define ZT_DEFAULT_WORLD_LENGTH 732 -static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x6b,0xd4,0x16,0x08,0xc1,0xb8,0xb3,0x88,0xa4,0x69,0x22,0x14,0x91,0xaa,0x9a,0xcd,0x66,0xcc,0x76,0x4c,0xde,0xfd,0x56,0x03,0x9f,0x10,0x67,0xae,0x15,0xe6,0x9c,0x6f,0xb4,0x2d,0x7b,0x55,0x33,0x0e,0x3f,0xda,0xac,0x52,0x9c,0x07,0x92,0xfd,0x73,0x40,0xa6,0xaa,0x21,0xab,0xa8,0xa4,0x89,0xfd,0xae,0xa4,0x4a,0x39,0xbf,0x2d,0x00,0x65,0x9a,0xc9,0xc8,0x18,0xeb,0x16,0x93,0xf4,0xe5,0xbd,0x20,0xda,0x10,0xad,0xc7,0x05,0xf4,0x99,0xfe,0x04,0x08,0x9b,0xe0,0x9e,0x77,0x1d,0x9f,0x47,0x16,0xaa,0x92,0x4f,0x10,0x16,0x3d,0xc7,0xec,0xd3,0x90,0x9e,0xd1,0x74,0xfc,0xb3,0xb5,0x07,0x9c,0x4d,0x95,0xc5,0x17,0x8b,0x3d,0x0b,0x60,0x76,0xe8,0x51,0xbb,0xb6,0x3d,0x74,0xb5,0x21,0x83,0x7b,0x95,0x1d,0x02,0x9b,0xcd,0xaf,0x5c,0x3e,0x96,0xdf,0x37,0x2c,0x56,0x6d,0xfa,0x75,0x0f,0xda,0x55,0x85,0x13,0xf4,0x76,0x1a,0x66,0x4d,0x3b,0x8d,0xcf,0x12,0xc9,0x34,0xb9,0x0d,0x61,0x03,0x3a,0x46,0xf1,0xbf,0x30,0x00,0x76,0xe6,0x6f,0xab,0x33,0xe2,0x85,0x49,0xa6,0x2e,0xe2,0x06,0x4d,0x18,0x43,0x27,0x3c,0x2c,0x30,0x0b,0xa4,0x5c,0x3f,0x20,0xbe,0xf0,0x2d,0xba,0xd2,0x25,0x72,0x3b,0xb5,0x9a,0x9b,0xb4,0xb1,0x35,0x35,0x73,0x09,0x61,0xae,0xec,0xf5,0xa1,0x63,0xac,0xe4,0x77,0xcc,0xeb,0x07,0x27,0x02,0x5b,0x99,0xac,0x14,0xa5,0x16,0x6a,0x09,0xa3,0x00,0x02,0x04,0xb9,0xb4,0x0d,0x52,0x27,0x09,0x06,0x2a,0x02,0x6e,0xa0,0xc8,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x27,0x09,0x9d,0x21,0x90,0x39,0xf3,0x00,0x01,0xf0,0x92,0x2a,0x98,0xe3,0xb3,0x4e,0xbc,0xbf,0xf3,0x33,0x26,0x9d,0xc2,0x65,0xd7,0xa0,0x20,0xaa,0xb6,0x9d,0x72,0xbe,0x4d,0x4a,0xcc,0x9c,0x8c,0x92,0x94,0x78,0x57,0x71,0x25,0x6c,0xd1,0xd9,0x42,0xa9,0x0d,0x1b,0xd1,0xd2,0xdc,0xa3,0xea,0x84,0xef,0x7d,0x85,0xaf,0xe6,0x61,0x1f,0xb4,0x3f,0xf0,0xb7,0x41,0x26,0xd9,0x0a,0x6e,0x00,0x0c,0x04,0xbc,0xa6,0x5e,0xb1,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x02,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x7d,0x00,0x01,0x27,0x09,0x04,0x9a,0x42,0xc5,0x21,0x27,0x09,0x06,0x2c,0x0f,0xf8,0x50,0x01,0x54,0x01,0x97,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x27,0x09,0x04,0x9f,0xcb,0x61,0xab,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x08,0x00,0x00,0xa1,0x00,0x00,0x00,0x00,0x00,0x54,0x60,0x01,0x27,0x09,0x04,0x83,0xff,0x06,0x10,0x27,0x09,0x06,0x28,0x03,0xeb,0x80,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x27,0x09,0x04,0x6b,0xaa,0xc5,0x0e,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x00,0x01,0x00,0x20,0x00,0x00,0x00,0x00,0x02,0x00,0xe0,0x01,0x27,0x09,0x04,0x80,0xc7,0xc5,0xd9,0x27,0x09,0x06,0x24,0x00,0x61,0x80,0x00,0x00,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0xb7,0x40,0x01,0x27,0x09,0x88,0x41,0x40,0x8a,0x2e,0x00,0xbb,0x1d,0x31,0xf2,0xc3,0x23,0xe2,0x64,0xe9,0xe6,0x41,0x72,0xc1,0xa7,0x4f,0x77,0x89,0x95,0x55,0xed,0x10,0x75,0x1c,0xd5,0x6e,0x86,0x40,0x5c,0xde,0x11,0x8d,0x02,0xdf,0xfe,0x55,0x5d,0x46,0x2c,0xcf,0x6a,0x85,0xb5,0x63,0x1c,0x12,0x35,0x0c,0x8d,0x5d,0xc4,0x09,0xba,0x10,0xb9,0x02,0x5d,0x0f,0x44,0x5c,0xf4,0x49,0xd9,0x2b,0x1c,0x00,0x0c,0x04,0x2d,0x20,0xc6,0x82,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x64,0x00,0x81,0xc3,0x54,0x00,0x00,0xff,0xfe,0x18,0x1d,0x61,0x27,0x09,0x04,0x2e,0x65,0xa0,0xf9,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x03,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x6a,0x30,0x01,0x27,0x09,0x04,0x6b,0xbf,0x2e,0xd2,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x68,0x00,0x83,0xa4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x27,0x09,0x04,0x2d,0x20,0xf6,0xb3,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x58,0x00,0x8b,0xf8,0x54,0x00,0x00,0xff,0xfe,0x15,0xb3,0x9a,0x27,0x09,0x04,0x2d,0x20,0xf8,0x57,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x70,0x00,0x9b,0xc9,0x54,0x00,0x00,0xff,0xfe,0x15,0xc4,0xf5,0x27,0x09,0x04,0x9f,0xcb,0x02,0x9a,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x0c,0xad,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x26,0x70,0x01,0x27,0x09}; diff --git a/controller/EmbeddedNetworkController.hpp b/controller/EmbeddedNetworkController.hpp index adc2a2eca..096339fb5 100644 --- a/controller/EmbeddedNetworkController.hpp +++ b/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); diff --git a/controller/FileDB.cpp b/controller/FileDB.cpp index cf5847d6a..5b5033bf2 100644 --- a/controller/FileDB.cpp +++ b/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 networks(OSUtils::listDirectory(_networksPath.c_str(),false)); std::string buf; diff --git a/controller/FileDB.hpp b/controller/FileDB.hpp index 8aa2c18ec..69fe9a953 100644 --- a/controller/FileDB.hpp +++ b/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 > > _online; std::mutex _online_l; diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 12ea21140..2da12598f 100644 --- a/include/ZeroTierOne.h +++ b/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: /planet - * Persistence: recommended - */ - ZT_STATE_OBJECT_PLANET = 3, - - /** - * A moon (federated root set) - * - * Object ID: world ID of moon - * Canonical path: /moons.d/.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 * diff --git a/node/AES.cpp b/node/AES.cpp new file mode 100644 index 000000000..9ed2db29c --- /dev/null +++ b/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 . + * + * -- + * + * 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 +#include +#include +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 diff --git a/node/AES.hpp b/node/AES.hpp new file mode 100644 index 000000000..c8f3887df --- /dev/null +++ b/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 . + * + * -- + * + * 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 +#include +#include +#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 diff --git a/node/C25519.cpp b/node/C25519.cpp index d7c7011fd..7dd0a4e76 100644 --- a/node/C25519.cpp +++ b/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 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); diff --git a/node/Capability.hpp b/node/Capability.hpp index 0704c5139..35fe17bac 100644 --- a/node/Capability.hpp +++ b/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 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(p) != ZT_C25519_SIGNATURE_LEN) + _custody[i].signatureLength = b.template at(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(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]; }; diff --git a/node/CertificateOfMembership.cpp b/node/CertificateOfMembership.cpp index 614c68871..0ec7abfaf 100644 --- a/node/CertificateOfMembership.cpp +++ b/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 diff --git a/node/CertificateOfMembership.hpp b/node/CertificateOfMembership.hpp index b2c63f9d0..b4aa51727 100644 --- a/node/CertificateOfMembership.hpp +++ b/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 @@ -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 diff --git a/node/CertificateOfOwnership.cpp b/node/CertificateOfOwnership.cpp index d7266cd29..5a17026ce 100644 --- a/node/CertificateOfOwnership.cpp +++ b/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(&ip)->sin_addr.s_addr),4); + ++_thingCount; + } else if (ip.ss_family == AF_INET6) { + _thingTypes[_thingCount] = THING_IPV6_ADDRESS; + memcpy(_thingValues[_thingCount],reinterpret_cast(&ip)->sin6_addr.s6_addr,16); + ++_thingCount; + } +} + +bool CertificateOfOwnership::sign(const Identity &signer) +{ + if (signer.hasPrivate()) { + Buffer 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; } diff --git a/node/CertificateOfOwnership.hpp b/node/CertificateOfOwnership.hpp index fdffec3b8..cf984ad8e 100644 --- a/node/CertificateOfOwnership.hpp +++ b/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(&ip)->sin_addr.s_addr),4); - ++_thingCount; - } else if (ip.ss_family == AF_INET6) { - _thingTypes[_thingCount] = THING_IPV6_ADDRESS; - memcpy(_thingValues[_thingCount],reinterpret_cast(&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 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(p) != ZT_C25519_SIGNATURE_LEN) + _signatureLength = b.template at(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(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 diff --git a/node/Constants.hpp b/node/Constants.hpp index d58e408f3..73023ae0c 100644 --- a/node/Constants.hpp +++ b/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 #ifndef __UNIX_LIKE__ #define __UNIX_LIKE__ @@ -79,7 +77,7 @@ #ifndef __BSD__ #define __BSD__ #endif -#include +#include #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 diff --git a/node/ECC384.cpp b/node/ECC384.cpp new file mode 100644 index 000000000..465385982 --- /dev/null +++ b/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 . + * + * -- + * + * 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 +#include +#include +#include + +#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 + +#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= 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= 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 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> 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> 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 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> 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 diff --git a/node/ECC384.hpp b/node/ECC384.hpp new file mode 100644 index 000000000..a56cd0eb9 --- /dev/null +++ b/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 . + * + * -- + * + * 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 diff --git a/node/Identity.cpp b/node/Identity.cpp index e914b2701..83800384e 100644 --- a/node/Identity.cpp +++ b/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() +} // anonymous namespace + +void Identity::generate(const Type t) { - 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; - + 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;idata,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: diff --git a/node/Identity.hpp b/node/Identity.hpp index f559bcc5d..eeaf2fd7a 100644 --- a/node/Identity.hpp +++ b/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(this),0,sizeof(Identity)); } + Identity(const Identity &id) { memcpy(reinterpret_cast(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 - Identity(const Buffer &b,unsigned int startAt = 0) : - _privateKey((C25519::Private *)0) - { - deserialize(b,startAt); - } + Identity(const Buffer &b,unsigned int startAt = 0) { deserialize(b,startAt); } - ~Identity() - { - if (_privateKey) { - Utils::burn(_privateKey,sizeof(C25519::Private)); - delete _privateKey; - } - } + ~Identity() { Utils::burn(reinterpret_cast(this),sizeof(Identity)); } + + inline void zero() { Utils::burn(reinterpret_cast(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(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,9 +120,36 @@ 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; + } + + /** + * 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 bool sha512PublicKey(void *sha) const + { + 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; } @@ -140,15 +157,16 @@ public: /** * Sign a message with this identity (private key required) * + * 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 sig Buffer to receive signature + * @param siglen Length of buffer + * @return Number of bytes actually written to sig or 0 on error */ - inline C25519::Signature sign(const void *data,unsigned int len) const - { - if (_privateKey) - return C25519::sign(*_privateKey,_publicKey,data,len); - throw ZT_EXCEPTION_PRIVATE_KEY_REQUIRED; - } + unsigned int sign(const void *data,unsigned int len,void *sig,unsigned int siglen) const; /** * Verify a message signature against this identity @@ -159,25 +177,7 @@ public: * @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 void *signature,unsigned int siglen) const - { - if (siglen != ZT_C25519_SIGNATURE_LEN) - return false; - return C25519::verify(_publicKey,data,len,signature); - } - - /** - * Verify a message signature against this identity - * - * @param data Data to check - * @param len Length of data - * @param signature Signature - * @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 &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 inline unsigned int deserialize(const Buffer &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; + _type = (Type)b[p++]; + switch(_type) { - memcpy(_publicKey.data,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN); - p += ZT_C25519_PUBLIC_KEY_LEN; + 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 diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index b774f1dfe..197bfe225 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -28,6 +28,8 @@ #include #include +#include + #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(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD); const SharedPtr 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(ptr); ptr += 8; - planetWorldTimestamp = at(ptr); ptr += 8; - } - - std::vector< std::pair > 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(ptr); ptr += 2; - for(unsigned int i=0;i(at(ptr),at(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 moons(RR->topology->moons()); - for(std::vector::const_iterator m(moons.begin());m!=moons.end();++m) { - for(std::vector< std::pair >::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(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(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) { - 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(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(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) { - const uint64_t nwid = at(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(offset); offset += 8; + const unsigned int flags = (*this)[offset]; ++offset; const SharedPtr 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(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_GATHER_LIMIT); - offset += 4; + gatherLimit = at(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(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_DEST_ADI)); - const unsigned int etherType = at(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
recipients; + if ((flags & 0x08) != 0) { + const unsigned int rc = at(offset); offset += 2; + for(unsigned int i=0;iaddress())&&(a != RR->identity.address())) { + recipients.push_back(a); + } + offset += 5; + } + } + const unsigned int afterRecipientsOffset = offset; + + const MulticastGroup to(MAC(field(offset,6),6),at(offset + 6)); offset += 10; + const unsigned int etherType = at(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 *)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 (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 (network->filterIncomingPacket(tPtr,peer,RR->identity.address(),from,to.mac(),frameData,frameLen,etherType,0) > 0) + 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); + } } - if (gatherLimit) { + if (!recipients.empty()) { + const std::vector
anchors = network->config().anchors(); + const bool amAnchor = (std::find(anchors.begin(),anchors.end(),RR->identity.address()) != anchors.end()); + + for(std::list
::iterator ra(recipients.begin());ra!=recipients.end();) { + SharedPtr 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 (!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
::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 (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) diff --git a/node/Locator.hpp b/node/Locator.hpp new file mode 100644 index 000000000..4249d8693 --- /dev/null +++ b/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 . + * + * -- + * + * 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 +#include + +#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 &phy() const { return _physical; } + inline const std::vector &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 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 txtRecords; + for(unsigned int p=0;psize();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 + 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 + inline void serialize(Buffer &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::const_iterator i(_physical.begin());i!=_physical.end();++i) + i->serialize(b); + b.append((uint8_t)_virtual.size()); + for(std::vector::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 + inline unsigned int deserialize(const Buffer &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(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(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(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 _physical; + std::vector _virtual; + unsigned int _signatureLength; + uint8_t _signature[ZT_SIGNATURE_BUFFER_SIZE]; +}; + +} // namespace ZeroTier + +#endif diff --git a/node/Membership.hpp b/node/Membership.hpp index 73e11c6c1..1cc06970e 100644 --- a/node/Membership.hpp +++ b/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 - 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 inline bool _isCredentialTimestampValid(const NetworkConfig &nconf,const C &remoteCredential) const { diff --git a/node/MulticastGroup.hpp b/node/MulticastGroup.hpp index 184df1cef..b4ad1bcb4 100644 --- a/node/MulticastGroup.hpp +++ b/node/MulticastGroup.hpp @@ -31,6 +31,7 @@ #include "MAC.hpp" #include "InetAddress.hpp" +#include "Utils.hpp" namespace ZeroTier { diff --git a/node/Multicaster.cpp b/node/Multicaster.cpp index 67e133c0e..58fffbd8b 100644 --- a/node/Multicaster.cpp +++ b/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 bestMulticastReplicator; - SharedPtr bestMulticastReplicatorPath; - unsigned int bestMulticastReplicatorLatency = 0xffff; - for(unsigned int i=0;i p(RR->topology->getPeerNoCache(multicastReplicators[i])); - if ((p)&&(p->isAlive(now))) { - const SharedPtr 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)]; diff --git a/node/Network.cpp b/node/Network.cpp index 4458c9b09..1b1d15138 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -399,9 +399,9 @@ static _doZtFilterResult _doZtFilter( } if (inbound) { if (membership) { - if ((src)&&(membership->hasCertificateOfOwnershipFor(nconf,src))) + if ((src)&&(membership->peerOwnsAddress(nconf,src))) ownershipVerificationMask |= ZT_RULE_PACKET_CHARACTERISTICS_SENDER_IP_AUTHENTICATED; - if (membership->hasCertificateOfOwnershipFor(nconf,macSource)) + if (membership->peerOwnsAddress(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
::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
::const_iterator a(alwaysAnnounceTo.begin());a!=alwaysAnnounceTo.end();++a) _announceMulticastGroupsTo(tPtr,*a,groups); - } } { diff --git a/node/NetworkConfig.hpp b/node/NetworkConfig.hpp index ddad52a56..40d2be396 100644 --- a/node/NetworkConfig.hpp +++ b/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
multicastReplicators() const - { - std::vector
r; - for(unsigned int i=0;i alwaysContactAddresses() const { std::vector
r; for(unsigned int i=0;i > &a) const { for(unsigned int i=0;iidentity.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 > &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 *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 > &_alwaysContact; const int64_t _now; const SharedPtr _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 > 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 >::Iterator i(alwaysContact); - Address *upstreamAddress = (Address *)0; - std::vector *upstreamStableEndpoints = (std::vector *)0; - while (i.next(upstreamAddress,upstreamStableEndpoints)) { - SharedPtr 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 > alwaysContact; + RR->topology->getUpstreamsToContact(alwaysContact); std::vector< std::pair< SharedPtr,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 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(outp.data()) + sigStart,outp.size() - sigStart)); + uint8_t sig[256]; + const unsigned int siglen = RR->identity.sign(reinterpret_cast(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(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(node)->deorbit(tptr,moonWorldId); - } catch ( ... ) { - return ZT_RESULT_FATAL_ERROR_INTERNAL; - } -} - uint64_t ZT_Node_address(ZT_Node *node) { return reinterpret_cast(node)->address(); diff --git a/node/Node.hpp b/node/Node.hpp index a6f92eb11..e751b36bf 100644 --- a/node/Node.hpp +++ b/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 moons() const; - inline const Identity &identity() const { return _RR.identity; } /** diff --git a/node/Packet.cpp b/node/Packet.cpp index 5d0885d37..1c2ac86a7 100644 --- a/node/Packet.cpp +++ b/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(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(unsafeData()); diff --git a/node/Packet.hpp b/node/Packet.hpp index fcb81489d..ae7090cb0 100644 --- a/node/Packet.hpp +++ b/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) * diff --git a/node/Peer.cpp b/node/Peer.cpp index 5f5d1462b..4ef481c08 100644 --- a/node/Peer.cpp +++ b/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 moons(RR->topology->moons()); - std::vector moonsWanted(RR->topology->moonsWanted()); - outp.append((uint16_t)(moons.size() + moonsWanted.size())); - for(std::vector::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::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) { diff --git a/node/Peer.hpp b/node/Peer.hpp index b4cbe0572..b23d8a4f3 100644 --- a/node/Peer.hpp +++ b/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; } diff --git a/node/Revocation.cpp b/node/Revocation.cpp index 043425690..75b71e9c5 100644 --- a/node/Revocation.cpp +++ b/node/Revocation.cpp @@ -34,6 +34,18 @@ namespace ZeroTier { +bool Revocation::sign(const Identity &signer) +{ + if (signer.hasPrivate()) { + Buffer 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 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; } diff --git a/node/Revocation.hpp b/node/Revocation.hpp index 1d001a405..94ba64596 100644 --- a/node/Revocation.hpp +++ b/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 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(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(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(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 diff --git a/node/SHA512.cpp b/node/SHA512.cpp index d3c938afb..e79efc674 100644 --- a/node/SHA512.cpp +++ b/node/SHA512.cpp @@ -18,13 +18,20 @@ Public domain. #include #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(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(digest),&ctx); +} } #endif @@ -32,336 +39,222 @@ void SHA512::hash(void *digest,const void *data,unsigned int len) #include #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(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(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 +namespace { -#ifdef ZT_NO_TYPE_PUNNING - -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 inline void sha512_decode(uint64_t *output, uint8_t *input, uint32_t idx) +{ + *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); } -static void store_bigendian(unsigned char *x,uint64 u) -{ - 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; -} +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; -#else // !ZT_NO_TYPE_PUNNING +#define LSR(x,n) (x >> n) +#define ROR(x,n) (LSR(x,n) | (x << (64 - n))) -#define load_bigendian(x) Utils::ntoh(*((const uint64_t *)(x))) -#define store_bigendian(x,u) (*((uint64_t *)(x)) = Utils::hton((u))) +#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)) -#endif // ZT_NO_TYPE_PUNNING +#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; -#define SHR(x,c) ((x) >> (c)) -#define ROTR(x,c) (((x) >> (c)) | ((x) << (64 - (c)))) +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 }; -#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; - -static inline int crypto_hashblocks(unsigned char *statebytes,const unsigned char *in,unsigned long long inlen) -{ - 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) - - a += state[0]; - b += state[1]; - c += state[2]; - d += state[3]; - e += state[4]; - f += state[5]; - g += state[6]; - h += state[7]; - - state[0] = a; - state[1] = b; - state[2] = c; - state[3] = d; - state[4] = e; - state[5] = f; - state[6] = g; - state[7] = h; - - in += 128; - inlen -= 128; - } - - 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; -} - -#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 +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 }; -void SHA512::hash(void *digest,const void *data,unsigned int len) +static inline void sha512_memcpy(uint8_t *src, uint8_t *dst, uint32_t size) { - unsigned char h[64]; - unsigned char padded[256]; - int i; - uint64_t bytes = len; + 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; } +} - const unsigned char *in = (const unsigned char *)data; - unsigned int inlen = len; +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; +} - for (i = 0;i < 64;++i) h[i] = iv[i]; +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; +} - blocks(h,in,inlen); - in += inlen; - inlen &= 127; - in -= inlen; - - for (i = 0;i < (int)inlen;++i) padded[i] = in[i]; - padded[inlen] = 0x80; - - 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); +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++; } + for (i = 0; i < 8; i++) { ctx->val[i] += v[i]; } +} - for (i = 0;i < 64;++i) ((unsigned char *)digest)[i] = h[i]; +static inline void sha512_stage1(sha512_ctx_t *sha512_ctx) +{ + 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; + } +} + +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); } +} + +} // anonymous namespace + +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); +} + +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); } diff --git a/node/SHA512.hpp b/node/SHA512.hpp index cb7b40a8b..1ede6cc9b 100644 --- a/node/SHA512.hpp +++ b/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 diff --git a/node/Str.hpp b/node/Str.hpp new file mode 100644 index 000000000..83af51500 --- /dev/null +++ b/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 . + * + * -- + * + * 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 diff --git a/node/Switch.cpp b/node/Switch.cpp index a6852d9f4..66d5da6d3 100644 --- a/node/Switch.cpp +++ b/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 relayTo = RR->topology->getPeer(tPtr,destination); diff --git a/node/Tag.cpp b/node/Tag.cpp index 6c78f3e48..3ae29319e 100644 --- a/node/Tag.cpp +++ b/node/Tag.cpp @@ -34,6 +34,18 @@ namespace ZeroTier { +bool Tag::sign(const Identity &signer) +{ + if (signer.hasPrivate()) { + Buffer 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; } diff --git a/node/Tag.hpp b/node/Tag.hpp index 3f888ba91..e5ac891bd 100644 --- a/node/Tag.hpp +++ b/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 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(p) != ZT_C25519_SIGNATURE_LEN) + _signatureLength = b.template at(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(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 diff --git a/node/Topology.cpp b/node/Topology.cpp index 6d6beda33..d179d8e63 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -35,57 +35,10 @@ namespace ZeroTier { -/* - * 2018-07-26 ZeroTier planet definition for the third planet of Sol: - * - * There are two roots, each of which is a cluster spread across multiple - * continents and providers. They are named Alice and Bob after the - * canonical example names used in cryptography. - * - * Alice: - * - * root-alice-ams-01: Amsterdam, Netherlands - * root-alice-joh-01: Johannesburg, South Africa - * root-alice-nyc-01: New York, New York, USA - * root-alice-arg-01: Buenos Aires, Argentina - * root-alice-sfo-01: San Francisco, California, USA - * root-alice-sgp-01: Singapore - * - * Bob: - * - * root-bob-dfw-01: Dallas, Texas, USA - * root-bob-fra-01: Frankfurt, Germany - * root-bob-par-01: Paris, France - * root-bob-syd-01: Sydney, Australia - * root-bob-tok-01: Tokyo, Japan - * root-bob-tor-01: Toronto, Canada - */ -#define ZT_DEFAULT_WORLD_LENGTH 634 -static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x64,0xd3,0x71,0xf0,0x58,0xb8,0xb3,0x88,0xa4,0x69,0x22,0x14,0x91,0xaa,0x9a,0xcd,0x66,0xcc,0x76,0x4c,0xde,0xfd,0x56,0x03,0x9f,0x10,0x67,0xae,0x15,0xe6,0x9c,0x6f,0xb4,0x2d,0x7b,0x55,0x33,0x0e,0x3f,0xda,0xac,0x52,0x9c,0x07,0x92,0xfd,0x73,0x40,0xa6,0xaa,0x21,0xab,0xa8,0xa4,0x89,0xfd,0xae,0xa4,0x4a,0x39,0xbf,0x2d,0x00,0x65,0x9a,0xc9,0xc8,0x18,0xeb,0xbf,0xfd,0xd5,0x32,0xf7,0x15,0x6e,0x02,0x6f,0xb9,0x01,0x0d,0xb5,0x7b,0x04,0xd8,0x3a,0xc5,0x17,0x39,0x04,0x36,0xfd,0x9d,0xc6,0x3d,0xa8,0xf3,0x8e,0x79,0xe7,0xc8,0x77,0x8d,0xcc,0x79,0xb8,0xab,0xc6,0x98,0x7c,0x9f,0x34,0x25,0x14,0xe1,0x2f,0xd7,0x97,0x11,0xec,0x34,0x4c,0x9f,0x0f,0xb4,0x85,0x0d,0x9b,0x11,0xd1,0xc2,0xce,0x00,0xc4,0x0a,0x13,0x4b,0xcb,0xc3,0xae,0x2e,0x16,0x00,0x4b,0xdc,0x90,0x5e,0x7e,0x9b,0x44,0x07,0x15,0x36,0x61,0x3c,0x64,0xaa,0xe9,0x46,0x78,0x3c,0xa7,0x18,0xc8,0xd8,0x02,0x9d,0x21,0x90,0x39,0xf3,0x00,0x01,0xf0,0x92,0x2a,0x98,0xe3,0xb3,0x4e,0xbc,0xbf,0xf3,0x33,0x26,0x9d,0xc2,0x65,0xd7,0xa0,0x20,0xaa,0xb6,0x9d,0x72,0xbe,0x4d,0x4a,0xcc,0x9c,0x8c,0x92,0x94,0x78,0x57,0x71,0x25,0x6c,0xd1,0xd9,0x42,0xa9,0x0d,0x1b,0xd1,0xd2,0xdc,0xa3,0xea,0x84,0xef,0x7d,0x85,0xaf,0xe6,0x61,0x1f,0xb4,0x3f,0xf0,0xb7,0x41,0x26,0xd9,0x0a,0x6e,0x00,0x0c,0x04,0xbc,0xa6,0x5e,0xb1,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x02,0x00,0xd0,0x00,0x7d,0x00,0x01,0x00,0x00,0x00,0x00,0x27,0x09,0x04,0x9a,0x42,0xc5,0x21,0x27,0x09,0x06,0x2c,0x0f,0xf8,0x50,0x01,0x54,0x01,0x97,0x00,0x33,0xcc,0x08,0xf8,0xfa,0xcc,0x08,0x27,0x09,0x04,0x9f,0xcb,0x61,0xab,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x08,0x00,0x00,0xa1,0x00,0x54,0x60,0x01,0x00,0xfc,0xcc,0x08,0x27,0x09,0x04,0x83,0xff,0x06,0x10,0x27,0x09,0x06,0x28,0x03,0xeb,0x80,0x00,0x00,0x00,0x0e,0x00,0x02,0x60,0x01,0x00,0xfc,0xcc,0x08,0x27,0x09,0x04,0x6b,0xaa,0xc5,0x0e,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x00,0x01,0x00,0x20,0x02,0x00,0xe0,0x01,0x08,0xfe,0xcc,0x08,0x27,0x09,0x04,0x80,0xc7,0xc5,0xd9,0x27,0x09,0x06,0x24,0x00,0x61,0x80,0x00,0x00,0x00,0xd0,0x00,0xb7,0x40,0x01,0x08,0xfe,0xcc,0x08,0x27,0x09,0x88,0x41,0x40,0x8a,0x2e,0x00,0xbb,0x1d,0x31,0xf2,0xc3,0x23,0xe2,0x64,0xe9,0xe6,0x41,0x72,0xc1,0xa7,0x4f,0x77,0x89,0x95,0x55,0xed,0x10,0x75,0x1c,0xd5,0x6e,0x86,0x40,0x5c,0xde,0x11,0x8d,0x02,0xdf,0xfe,0x55,0x5d,0x46,0x2c,0xcf,0x6a,0x85,0xb5,0x63,0x1c,0x12,0x35,0x0c,0x8d,0x5d,0xc4,0x09,0xba,0x10,0xb9,0x02,0x5d,0x0f,0x44,0x5c,0xf4,0x49,0xd9,0x2b,0x1c,0x00,0x0c,0x04,0x2d,0x20,0xc6,0x82,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x64,0x00,0x81,0xc3,0x54,0x00,0x00,0xff,0xfe,0x18,0x1d,0x61,0x27,0x09,0x04,0x2e,0x65,0xa0,0xf9,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x03,0x00,0xd0,0x00,0x6a,0x30,0x01,0x78,0x00,0xcd,0x08,0x27,0x09,0x04,0x6b,0xbf,0x2e,0xd2,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x68,0x00,0x83,0xa4,0x00,0x64,0xcd,0x08,0x80,0x01,0xcd,0x08,0x27,0x09,0x04,0x2d,0x20,0xf6,0xb3,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x58,0x00,0x8b,0xf8,0x54,0x00,0x00,0xff,0xfe,0x15,0xb3,0x9a,0x27,0x09,0x04,0x2d,0x20,0xf8,0x57,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x70,0x00,0x9b,0xc9,0x54,0x00,0x00,0xff,0xfe,0x15,0xc4,0xf5,0x27,0x09,0x04,0x9f,0xcb,0x02,0x9a,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x0c,0xad,0x00,0xd0,0x00,0x26,0x70,0x01,0xfe,0x15,0xc4,0xf5,0x27,0x09}; - Topology::Topology(const RuntimeEnvironment *renv,void *tPtr) : RR(renv), - _numConfiguredPhysicalPaths(0), - _amUpstream(false) + _numConfiguredPhysicalPaths(0) { - uint8_t tmp[ZT_WORLD_MAX_SERIALIZED_LENGTH]; - uint64_t idtmp[2]; - idtmp[0] = 0; idtmp[1] = 0; - int n = RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_PLANET,idtmp,tmp,sizeof(tmp)); - if (n > 0) { - try { - World cachedPlanet; - cachedPlanet.deserialize(Buffer(tmp,(unsigned int)n),0); - addWorld(tPtr,cachedPlanet,false); - } catch ( ... ) {} // ignore invalid cached planets - } - - World defaultPlanet; - { - Buffer wtmp(ZT_DEFAULT_WORLD,ZT_DEFAULT_WORLD_LENGTH); - defaultPlanet.deserialize(wtmp,0); // throws on error, which would indicate a bad static variable up top - } - addWorld(tPtr,defaultPlanet,false); } Topology::~Topology() @@ -158,224 +111,33 @@ Identity Topology::getIdentity(void *tPtr,const Address &zta) SharedPtr Topology::getUpstreamPeer() { - const int64_t now = RR->node->now(); - unsigned int bestq = ~((unsigned int)0); - const SharedPtr *best = (const SharedPtr *)0; - - Mutex::Lock _l2(_peers_m); - Mutex::Lock _l1(_upstreams_m); - - for(std::vector
::const_iterator a(_upstreamAddresses.begin());a!=_upstreamAddresses.end();++a) { - const SharedPtr *p = _peers.get(*a); - if (p) { - const unsigned int q = (*p)->relayQuality(now); - if (q <= bestq) { - bestq = q; - best = p; - } - } - } - - if (!best) - return SharedPtr(); - return *best; + return SharedPtr(); } bool Topology::isUpstream(const Identity &id) const { - Mutex::Lock _l(_upstreams_m); - return (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),id.address()) != _upstreamAddresses.end()); -} - -bool Topology::shouldAcceptWorldUpdateFrom(const Address &addr) const -{ - Mutex::Lock _l(_upstreams_m); - if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),addr) != _upstreamAddresses.end()) - return true; - for(std::vector< std::pair< uint64_t,Address> >::const_iterator s(_moonSeeds.begin());s!=_moonSeeds.end();++s) { - if (s->second == addr) - return true; - } return false; } ZT_PeerRole Topology::role(const Address &ztaddr) const { - Mutex::Lock _l(_upstreams_m); - if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),ztaddr) != _upstreamAddresses.end()) { - for(std::vector::const_iterator i(_planet.roots().begin());i!=_planet.roots().end();++i) { - if (i->identity.address() == ztaddr) - return ZT_PEER_ROLE_PLANET; - } - return ZT_PEER_ROLE_MOON; - } return ZT_PEER_ROLE_LEAF; } bool Topology::isProhibitedEndpoint(const Address &ztaddr,const InetAddress &ipaddr) const { - Mutex::Lock _l(_upstreams_m); - - // For roots the only permitted addresses are those defined. This adds just a little - // bit of extra security against spoofing, replaying, etc. - if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),ztaddr) != _upstreamAddresses.end()) { - for(std::vector::const_iterator r(_planet.roots().begin());r!=_planet.roots().end();++r) { - if (r->identity.address() == ztaddr) { - if (r->stableEndpoints.size() == 0) - return false; // no stable endpoints specified, so allow dynamic paths - for(std::vector::const_iterator e(r->stableEndpoints.begin());e!=r->stableEndpoints.end();++e) { - if (ipaddr.ipsEqual(*e)) - return false; - } - } - } - for(std::vector::const_iterator m(_moons.begin());m!=_moons.end();++m) { - for(std::vector::const_iterator r(m->roots().begin());r!=m->roots().end();++r) { - if (r->identity.address() == ztaddr) { - if (r->stableEndpoints.size() == 0) - return false; // no stable endpoints specified, so allow dynamic paths - for(std::vector::const_iterator e(r->stableEndpoints.begin());e!=r->stableEndpoints.end();++e) { - if (ipaddr.ipsEqual(*e)) - return false; - } - } - } - } - return true; - } - return false; } -bool Topology::addWorld(void *tPtr,const World &newWorld,bool alwaysAcceptNew) -{ - if ((newWorld.type() != World::TYPE_PLANET)&&(newWorld.type() != World::TYPE_MOON)) - return false; - - Mutex::Lock _l2(_peers_m); - Mutex::Lock _l1(_upstreams_m); - - World *existing = (World *)0; - switch(newWorld.type()) { - case World::TYPE_PLANET: - existing = &_planet; - break; - case World::TYPE_MOON: - for(std::vector< World >::iterator m(_moons.begin());m!=_moons.end();++m) { - if (m->id() == newWorld.id()) { - existing = &(*m); - break; - } - } - break; - default: - return false; - } - - if (existing) { - if (existing->shouldBeReplacedBy(newWorld)) - *existing = newWorld; - else return false; - } else if (newWorld.type() == World::TYPE_MOON) { - if (alwaysAcceptNew) { - _moons.push_back(newWorld); - existing = &(_moons.back()); - } else { - for(std::vector< std::pair >::iterator m(_moonSeeds.begin());m!=_moonSeeds.end();++m) { - if (m->first == newWorld.id()) { - for(std::vector::const_iterator r(newWorld.roots().begin());r!=newWorld.roots().end();++r) { - if (r->identity.address() == m->second) { - _moonSeeds.erase(m); - _moons.push_back(newWorld); - existing = &(_moons.back()); - break; - } - } - if (existing) - break; - } - } - } - if (!existing) - return false; - } else { - return false; - } - - try { - Buffer sbuf; - existing->serialize(sbuf,false); - uint64_t idtmp[2]; - idtmp[0] = existing->id(); idtmp[1] = 0; - RR->node->stateObjectPut(tPtr,(existing->type() == World::TYPE_PLANET) ? ZT_STATE_OBJECT_PLANET : ZT_STATE_OBJECT_MOON,idtmp,sbuf.data(),sbuf.size()); - } catch ( ... ) {} - - _memoizeUpstreams(tPtr); - - return true; -} - -void Topology::addMoon(void *tPtr,const uint64_t id,const Address &seed) -{ - char tmp[ZT_WORLD_MAX_SERIALIZED_LENGTH]; - uint64_t idtmp[2]; - idtmp[0] = id; idtmp[1] = 0; - int n = RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_MOON,idtmp,tmp,sizeof(tmp)); - if (n > 0) { - try { - World w; - w.deserialize(Buffer(tmp,(unsigned int)n)); - if ((w.type() == World::TYPE_MOON)&&(w.id() == id)) { - addWorld(tPtr,w,true); - return; - } - } catch ( ... ) {} - } - - if (seed) { - Mutex::Lock _l(_upstreams_m); - if (std::find(_moonSeeds.begin(),_moonSeeds.end(),std::pair(id,seed)) == _moonSeeds.end()) - _moonSeeds.push_back(std::pair(id,seed)); - } -} - -void Topology::removeMoon(void *tPtr,const uint64_t id) -{ - Mutex::Lock _l2(_peers_m); - Mutex::Lock _l1(_upstreams_m); - - std::vector nm; - for(std::vector::const_iterator m(_moons.begin());m!=_moons.end();++m) { - if (m->id() != id) { - nm.push_back(*m); - } else { - uint64_t idtmp[2]; - idtmp[0] = id; idtmp[1] = 0; - RR->node->stateObjectDelete(tPtr,ZT_STATE_OBJECT_MOON,idtmp); - } - } - _moons.swap(nm); - - std::vector< std::pair > cm; - for(std::vector< std::pair >::const_iterator m(_moonSeeds.begin());m!=_moonSeeds.end();++m) { - if (m->first != id) - cm.push_back(*m); - } - _moonSeeds.swap(cm); - - _memoizeUpstreams(tPtr); -} - void Topology::doPeriodicTasks(void *tPtr,int64_t now) { { Mutex::Lock _l1(_peers_m); - Mutex::Lock _l2(_upstreams_m); Hashtable< Address,SharedPtr >::Iterator i(_peers); Address *a = (Address *)0; SharedPtr *p = (SharedPtr *)0; while (i.next(a,p)) { - if ( (!(*p)->isAlive(now)) && (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),*a) == _upstreamAddresses.end()) ) { + if (!(*p)->isAlive(now)) { _savePeer(tPtr,*p); _peers.erase(*a); } @@ -394,37 +156,38 @@ void Topology::doPeriodicTasks(void *tPtr,int64_t now) } } -void Topology::_memoizeUpstreams(void *tPtr) +void Topology::setPhysicalPathConfiguration(const struct sockaddr_storage *pathNetwork,const ZT_PhysicalPathConfiguration *pathConfig) { - // assumes _upstreams_m and _peers_m are locked - _upstreamAddresses.clear(); - _amUpstream = false; + if (!pathNetwork) { + _numConfiguredPhysicalPaths = 0; + } else { + std::map cpaths; + for(unsigned int i=0,j=_numConfiguredPhysicalPaths;i::const_iterator i(_planet.roots().begin());i!=_planet.roots().end();++i) { - if (i->identity == RR->identity) { - _amUpstream = true; - } else if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),i->identity.address()) == _upstreamAddresses.end()) { - _upstreamAddresses.push_back(i->identity.address()); - SharedPtr &hp = _peers[i->identity.address()]; - if (!hp) - hp = new Peer(RR,RR->identity,i->identity); + 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(pathNetwork))] = pc; + } else { + cpaths.erase(*(reinterpret_cast(pathNetwork))); } - } - for(std::vector::const_iterator m(_moons.begin());m!=_moons.end();++m) { - for(std::vector::const_iterator i(m->roots().begin());i!=m->roots().end();++i) { - if (i->identity == RR->identity) { - _amUpstream = true; - } else if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),i->identity.address()) == _upstreamAddresses.end()) { - _upstreamAddresses.push_back(i->identity.address()); - SharedPtr &hp = _peers[i->identity.address()]; - if (!hp) - hp = new Peer(RR,RR->identity,i->identity); - } + unsigned int cnt = 0; + for(std::map::const_iterator i(cpaths.begin());((i!=cpaths.end())&&(cntfirst; + _physicalPathConfig[cnt].second = i->second; + ++cnt; } + _numConfiguredPhysicalPaths = cnt; } - - std::sort(_upstreamAddresses.begin(),_upstreamAddresses.end()); } void Topology::_savePeer(void *tPtr,const SharedPtr &peer) diff --git a/node/Topology.hpp b/node/Topology.hpp index b6690f580..b7f387a1d 100644 --- a/node/Topology.hpp +++ b/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 > &eps) const { - Mutex::Lock _l(_upstreams_m); - for(std::vector::const_iterator i(_planet.roots().begin());i!=_planet.roots().end();++i) { - if (i->identity != RR->identity) { - std::vector &ips = eps[i->identity.address()]; - for(std::vector::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::const_iterator m(_moons.begin());m!=_moons.end();++m) { - for(std::vector::const_iterator i(m->roots().begin());i!=m->roots().end();++i) { - if (i->identity != RR->identity) { - std::vector &ips = eps[i->identity.address()]; - for(std::vector::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 >::const_iterator m(_moonSeeds.begin());m!=_moonSeeds.end();++m) - eps[m->second]; } /** @@ -201,87 +171,10 @@ public: */ inline std::vector
upstreamAddresses() const { - Mutex::Lock _l(_upstreams_m); - return _upstreamAddresses; + // TODO + return std::vector
(); } - /** - * @return Current moons - */ - inline std::vector moons() const - { - Mutex::Lock _l(_upstreams_m); - return _moons; - } - - /** - * @return Moon IDs we are waiting for from seeds - */ - inline std::vector moonsWanted() const - { - Mutex::Lock _l(_upstreams_m); - std::vector mw; - for(std::vector< std::pair >::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; - } - - /** - * @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 cpaths; - for(unsigned int i=0,j=_numConfiguredPhysicalPaths;i ZT_MAX_PHYSMTU) - pc.mtu = ZT_MAX_PHYSMTU; - - cpaths[*(reinterpret_cast(pathNetwork))] = pc; - } else { - cpaths.erase(*(reinterpret_cast(pathNetwork))); - } - - unsigned int cnt = 0; - for(std::map::const_iterator i(cpaths.begin());((i!=cpaths.end())&&(cntfirst; - _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 _physicalPathConfig[ZT_MAX_CONFIGURABLE_PATHS]; - volatile unsigned int _numConfiguredPhysicalPaths; + unsigned int _numConfiguredPhysicalPaths; Hashtable< Address,SharedPtr > _peers; Mutex _peers_m; Hashtable< Path::HashKey,SharedPtr > _paths; Mutex _paths_m; - - World _planet; - std::vector _moons; - std::vector< std::pair > _moonSeeds; - std::vector
_upstreamAddresses; - bool _amUpstream; - Mutex _upstreams_m; // locks worlds, upstream info, moon info, etc. }; } // namespace ZeroTier diff --git a/node/Utils.cpp b/node/Utils.cpp index 16d81f651..3c924518f 100644 --- a/node/Utils.cpp +++ b/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 . + * along with this program. If not,see . * * -- * @@ -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= '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= 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 diff --git a/node/Utils.hpp b/node/Utils.hpp index 86db8b02b..f28158b01 100644 --- a/node/Utils.hpp +++ b/node/Utils.hpp @@ -38,10 +38,6 @@ #include #include -#if defined(__FreeBSD__) -#include -#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) * diff --git a/node/World.hpp b/node/World.hpp deleted file mode 100644 index 823d304d8..000000000 --- a/node/World.hpp +++ /dev/null @@ -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 . - * - * -- - * - * 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 -#include - -#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 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 &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 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 - inline void serialize(Buffer &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::const_iterator r(_roots.begin());r!=_roots.end();++r) { - r->identity.serialize(b); - b.append((uint8_t)r->stableEndpoints.size()); - for(std::vector::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 - inline unsigned int deserialize(const Buffer &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(p); p += 8; - _ts = b.template at(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 ZT_WORLD_MAX_STABLE_ENDPOINTS_PER_ROOT) - throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW; - for(unsigned int kk=0;kk(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 &roots,const C25519::Pair &signWith) - { - World w; - w._id = id; - w._ts = ts; - w._type = t; - w._updatesMustBeSignedBy = sk; - w._roots = roots; - - Buffer 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 _roots; -}; - -} // namespace ZeroTier - -#endif diff --git a/objects.mk b/objects.mk index 32f625884..147122b53 100644 --- a/objects.mk +++ b/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 \ diff --git a/one.cpp b/one.cpp index 89acaace5..5a148e7ed 100644 --- a/one.cpp +++ b/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 - Leave a network" ZT_EOL_S); fprintf(out," set - Set a network setting" ZT_EOL_S); fprintf(out," get - Get a network setting" ZT_EOL_S); - fprintf(out," listmoons - List moons (federated root sets)" ZT_EOL_S); - fprintf(out," orbit - Join a moon via any member root" ZT_EOL_S); - fprintf(out," deorbit - 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 " ZT_EOL_S); fprintf(out," sign " ZT_EOL_S); fprintf(out," verify " ZT_EOL_S); - fprintf(out," initmoon " ZT_EOL_S); - fprintf(out," genmoon " 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 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 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; diff --git a/osdep/Phy.hpp b/osdep/Phy.hpp index 333e5c244..58843dc86 100644 --- a/osdep/Phy.hpp +++ b/osdep/Phy.hpp @@ -61,6 +61,7 @@ #include #include #include +#include #include #if defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux) diff --git a/selftest.cpp b/selftest.cpp index 77c06cc03..e93127e8a 100644 --- a/selftest.cpp +++ b/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;kp2 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); diff --git a/service/OneService.cpp b/service/OneService.cpp index 93e97bdef..dfaf730a1 100644 --- a/service/OneService.cpp +++ b/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::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::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 moonsDotD(OSUtils::listDirectory((_homePath + ZT_PATH_SEPARATOR_S "moons.d").c_str())); - for(std::vector::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 moons(_node->moons()); - if (ps.size() == 1) { - // Return [array] of all moons - - res = json::array(); - for(std::vector::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::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 moons(_node->moons()); - const uint64_t id = Utils::hexStrToU64(ps[1].c_str()); - for(std::vector::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(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; diff --git a/service/SoftwareUpdater.cpp b/service/SoftwareUpdater.cpp index df3d89c22..081a468ee 100644 --- a/service/SoftwareUpdater.cpp +++ b/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 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 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