summaryrefslogtreecommitdiff
path: root/node/World.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'node/World.hpp')
-rw-r--r--node/World.hpp128
1 files changed, 87 insertions, 41 deletions
diff --git a/node/World.hpp b/node/World.hpp
index 2f1edb00..6e835bec 100644
--- a/node/World.hpp
+++ b/node/World.hpp
@@ -49,16 +49,6 @@
#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 indicating null / empty World object
- */
-#define ZT_WORLD_ID_NULL 0
-
-/**
- * World ID for a test network with ephemeral or temporary roots
- */
-#define ZT_WORLD_ID_TESTNET 1
-
-/**
* World ID for Earth
*
* This is the ID for the ZeroTier World used on planet Earth. It is unrelated
@@ -90,15 +80,23 @@ namespace ZeroTier {
* 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.
- *
- * If you absolutely must run your own "unofficial" ZeroTier network, please
- * define your world IDs above 0xffffffff (4294967295). Code to make a World
- * is in mkworld.cpp in the parent directory and must be edited to change
- * settings.
*/
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;
@@ -113,45 +111,54 @@ public:
* Construct an empty / null World
*/
World() :
- _id(ZT_WORLD_ID_NULL),
- _ts(0) {}
+ _id(0),
+ _ts(0),
+ _type(TYPE_NULL) {}
/**
* @return Root servers for this world and their stable endpoints
*/
- inline const std::vector<World::Root> &roots() const throw() { return _roots; }
+ inline const std::vector<World::Root> &roots() const { return _roots; }
+
+ /**
+ * @return World type: planet or moon
+ */
+ inline Type type() const { return _type; }
/**
* @return World unique identifier
*/
- inline uint64_t id() const throw() { return _id; }
+ inline uint64_t id() const { return _id; }
/**
* @return World definition timestamp
*/
- inline uint64_t timestamp() const throw() { return _ts; }
+ 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
*
- * A new world update is valid if it is for the same world ID, is newer,
- * and is signed by the current world's signing key. If this world object
- * is null, it can always be updated.
- *
* @param update Candidate update
- * @param fullSignatureCheck Perform full cryptographic signature check (true == yes, false == skip)
- * @return True if update is newer than current and is properly signed
+ * @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,bool fullSignatureCheck)
+ inline bool shouldBeReplacedBy(const World &update)
{
- if (_id == ZT_WORLD_ID_NULL)
+ if ((_id == 0)||(_type == TYPE_NULL))
return true;
- if ((_id == update._id)&&(_ts < update._ts)) {
- if (fullSignatureCheck) {
- Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> tmp;
- update.serialize(tmp,true);
- return C25519::verify(_updatesMustBeSignedBy,tmp.data(),tmp.size(),update._signature);
- } else return true;
+ if ((_id == update._id)&&(_ts < update._ts)&&(_type == update._type)) {
+ Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> tmp;
+ update.serialize(tmp,true);
+ return C25519::verify(_updatesMustBeSignedBy,tmp.data(),tmp.size(),update._signature);
}
return false;
}
@@ -159,14 +166,14 @@ public:
/**
* @return True if this World is non-empty
*/
- inline operator bool() const throw() { return (_id != ZT_WORLD_ID_NULL); }
+ inline operator bool() const { return (_type != TYPE_NULL); }
template<unsigned int C>
inline void serialize(Buffer<C> &b,bool forSign = false) const
{
if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
- b.append((uint8_t)0x01);
+ b.append((uint8_t)_type);
b.append((uint64_t)_id);
b.append((uint64_t)_ts);
b.append(_updatesMustBeSignedBy.data,ZT_C25519_PUBLIC_KEY_LEN);
@@ -179,6 +186,8 @@ public:
for(std::vector<InetAddress>::const_iterator ep(r->stableEndpoints.begin());ep!=r->stableEndpoints.end();++ep)
ep->serialize(b);
}
+ if (_type == TYPE_MOON)
+ b.append((uint16_t)0); // no attached dictionary (for future use)
if (forSign) b.append((uint64_t)0xf7f7f7f7f7f7f7f7ULL);
}
@@ -190,14 +199,19 @@ public:
_roots.clear();
- if (b[p++] != 0x01)
- throw std::invalid_argument("invalid object type");
+ 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 std::invalid_argument("invalid world type");
+ }
_id = b.template at<uint64_t>(p); p += 8;
_ts = b.template at<uint64_t>(p); p += 8;
memcpy(_updatesMustBeSignedBy.data,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN); p += ZT_C25519_PUBLIC_KEY_LEN;
memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
- unsigned int numRoots = b[p++];
+ const unsigned int numRoots = (unsigned int)b[p++];
if (numRoots > ZT_WORLD_MAX_ROOTS)
throw std::invalid_argument("too many roots in World");
for(unsigned int k=0;k<numRoots;++k) {
@@ -212,16 +226,48 @@ public:
p += r.stableEndpoints.back().deserialize(b,p);
}
}
+ if (_type == TYPE_MOON)
+ p += b.template at<uint16_t>(p) + 2;
return (p - startAt);
}
- inline bool operator==(const World &w) const throw() { return ((_id == w._id)&&(_ts == w._ts)&&(_updatesMustBeSignedBy == w._updatesMustBeSignedBy)&&(_signature == w._signature)&&(_roots == w._roots)); }
- inline bool operator!=(const World &w) const throw() { return (!(*this == w)); }
+ inline bool operator==(const World &w) const { return ((_id == w._id)&&(_ts == w._ts)&&(_updatesMustBeSignedBy == w._updatesMustBeSignedBy)&&(_signature == w._signature)&&(_roots == w._roots)&&(_type == w._type)); }
+ inline bool operator!=(const World &w) const { return (!(*this == w)); }
+
+ inline bool operator<(const World &w) const { return (((int)_type < (int)w._type) ? true : ((_type == w._type) ? (_id < w._id) : false)); }
+
+ /**
+ * Create a World object signed with a key pair
+ *
+ * @param t World type
+ * @param id World ID
+ * @param ts World timestamp / revision
+ * @param sk Key that must be used to sign the next future update to this world
+ * @param roots Roots and their stable endpoints
+ * @param signWith Key to sign this World with (can have the same public as the next-update signing key, but doesn't have to)
+ * @return Signed World object
+ */
+ static inline World make(World::Type t,uint64_t id,uint64_t ts,const C25519::Public &sk,const std::vector<World::Root> &roots,const C25519::Pair &signWith)
+ {
+ World w;
+ w._id = id;
+ w._ts = ts;
+ w._type = t;
+ w._updatesMustBeSignedBy = sk;
+ w._roots = roots;
+
+ Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> tmp;
+ w.serialize(tmp,true);
+ w._signature = C25519::sign(signWith,tmp.data(),tmp.size());
+
+ return w;
+ }
protected:
uint64_t _id;
uint64_t _ts;
+ Type _type;
C25519::Public _updatesMustBeSignedBy;
C25519::Signature _signature;
std::vector<Root> _roots;