Create a container for identity hashes, replace some "volatile" hacky junk with std::atomic, clean up some security checks, rename raw data in Buf to unsafeData to permit search for unsafe.

This commit is contained in:
Adam Ierymenko 2020-02-19 09:28:18 -08:00
parent 46d3780e89
commit 1e0a930d23
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
18 changed files with 743 additions and 470 deletions

View file

@ -147,7 +147,7 @@ public:
if (l <= ZT_BUF_MEM_SIZE) { if (l <= ZT_BUF_MEM_SIZE) {
r.b.move(s->b); r.b.move(s->b);
if (s->s > 0) if (s->s > 0)
memmove(r.b->b,r.b->b + s->s,l); memmove(r.b->unsafeData,r.b->unsafeData + s->s,l);
r.e = l; r.e = l;
while (++s != fcv.end()) { while (++s != fcv.end()) {
@ -157,7 +157,7 @@ public:
r.e = 0; r.e = 0;
break; break;
} }
memcpy(r.b->b + r.e,s->b->b + s->s,l); memcpy(r.b->unsafeData + r.e,s->b->unsafeData + s->s,l);
s->b.zero(); // let go of buffer in vector as soon as possible s->b.zero(); // let go of buffer in vector as soon as possible
r.e += l; r.e += l;
} }
@ -171,12 +171,12 @@ public:
*/ */
ZT_ALWAYS_INLINE Buf() noexcept {} ZT_ALWAYS_INLINE Buf() noexcept {}
ZT_ALWAYS_INLINE Buf(const Buf &b2) noexcept { memcpy(b,b2.b,ZT_BUF_MEM_SIZE); } ZT_ALWAYS_INLINE Buf(const Buf &b2) noexcept { memcpy(unsafeData,b2.unsafeData,ZT_BUF_MEM_SIZE); }
ZT_ALWAYS_INLINE Buf &operator=(const Buf &b2) noexcept ZT_ALWAYS_INLINE Buf &operator=(const Buf &b2) noexcept
{ {
if (this != &b2) if (this != &b2)
memcpy(b,b2.b,ZT_BUF_MEM_SIZE); memcpy(unsafeData,b2.unsafeData,ZT_BUF_MEM_SIZE);
return *this; return *this;
} }
@ -206,29 +206,29 @@ public:
/** /**
* Set all memory to zero * Set all memory to zero
*/ */
ZT_ALWAYS_INLINE void clear() noexcept { memset(b,0,ZT_BUF_MEM_SIZE); } ZT_ALWAYS_INLINE void clear() noexcept { memset(unsafeData,0,ZT_BUF_MEM_SIZE); }
/** /**
* Zero security critical data using Utils::burn() to ensure it's never optimized out. * Zero security critical data using Utils::burn() to ensure it's never optimized out.
*/ */
ZT_ALWAYS_INLINE void burn() noexcept { Utils::burn(b,ZT_BUF_MEM_SIZE); } ZT_ALWAYS_INLINE void burn() noexcept { Utils::burn(unsafeData,ZT_BUF_MEM_SIZE); }
/** /**
* Read a byte * Read a byte
* *
* @param ii Iterator * @param ii Index value-result parameter (incremented by 1)
* @return Byte (undefined on overflow) * @return Byte (undefined on overflow)
*/ */
ZT_ALWAYS_INLINE uint8_t rI8(int &ii) const noexcept ZT_ALWAYS_INLINE uint8_t rI8(int &ii) const noexcept
{ {
const int s = ii++; const int s = ii++;
return b[(unsigned int)s & ZT_BUF_MEM_MASK]; return unsafeData[(unsigned int)s & ZT_BUF_MEM_MASK];
} }
/** /**
* Read a 16-bit integer * Read a 16-bit integer
* *
* @param ii Integer * @param ii Index value-result parameter (incremented by 2)
* @return Integer (undefined on overflow) * @return Integer (undefined on overflow)
*/ */
ZT_ALWAYS_INLINE uint16_t rI16(int &ii) const noexcept ZT_ALWAYS_INLINE uint16_t rI16(int &ii) const noexcept
@ -240,14 +240,14 @@ public:
((uint16_t)data.bytes[s] << 8U) | ((uint16_t)data.bytes[s] << 8U) |
(uint16_t)data.bytes[s + 1]); (uint16_t)data.bytes[s + 1]);
#else #else
return Utils::ntoh(*reinterpret_cast<const uint16_t *>(b + s)); return Utils::ntoh(*reinterpret_cast<const uint16_t *>(unsafeData + s));
#endif #endif
} }
/** /**
* Read a 32-bit integer * Read a 32-bit integer
* *
* @param ii Integer * @param ii Index value-result parameter (incremented by 4)
* @return Integer (undefined on overflow) * @return Integer (undefined on overflow)
*/ */
ZT_ALWAYS_INLINE uint32_t rI32(int &ii) const noexcept ZT_ALWAYS_INLINE uint32_t rI32(int &ii) const noexcept
@ -261,14 +261,14 @@ public:
((uint32_t)data.bytes[s + 2] << 8U) | ((uint32_t)data.bytes[s + 2] << 8U) |
(uint32_t)data.bytes[s + 3]); (uint32_t)data.bytes[s + 3]);
#else #else
return Utils::ntoh(*reinterpret_cast<const uint32_t *>(b + s)); return Utils::ntoh(*reinterpret_cast<const uint32_t *>(unsafeData + s));
#endif #endif
} }
/** /**
* Read a 64-bit integer * Read a 64-bit integer
* *
* @param ii Integer * @param ii Index value-result parameter (incremented by 8)
* @return Integer (undefined on overflow) * @return Integer (undefined on overflow)
*/ */
ZT_ALWAYS_INLINE uint64_t rI64(int &ii) const noexcept ZT_ALWAYS_INLINE uint64_t rI64(int &ii) const noexcept
@ -286,7 +286,7 @@ public:
((uint64_t)data.bytes[s + 6] << 8U) | ((uint64_t)data.bytes[s + 6] << 8U) |
(uint64_t)data.bytes[s + 7]); (uint64_t)data.bytes[s + 7]);
#else #else
return Utils::ntoh(*reinterpret_cast<const uint64_t *>(b + s)); return Utils::ntoh(*reinterpret_cast<const uint64_t *>(unsafeData + s));
#endif #endif
} }
@ -301,7 +301,7 @@ public:
* indicates. * indicates.
* *
* @tparam T Object type * @tparam T Object type
* @param ii Iterator * @param ii Index value-result parameter (incremented by object's size in bytes)
* @param obj Object to read * @param obj Object to read
* @return Bytes read or a negative value on unmarshal error (passed from object) or overflow * @return Bytes read or a negative value on unmarshal error (passed from object) or overflow
*/ */
@ -309,7 +309,7 @@ public:
ZT_ALWAYS_INLINE int rO(int &ii,T &obj) const noexcept ZT_ALWAYS_INLINE int rO(int &ii,T &obj) const noexcept
{ {
if (ii < ZT_BUF_MEM_SIZE) { if (ii < ZT_BUF_MEM_SIZE) {
int ms = obj.unmarshal(b + ii,ZT_BUF_MEM_SIZE - ii); int ms = obj.unmarshal(unsafeData + ii,ZT_BUF_MEM_SIZE - ii);
if (ms > 0) if (ms > 0)
ii += ms; ii += ms;
return ms; return ms;
@ -323,17 +323,17 @@ public:
* Use this if the buffer's memory may get changed between reading and processing * Use this if the buffer's memory may get changed between reading and processing
* what is read. * what is read.
* *
* @param ii Iterator * @param ii Index value-result parameter (incremented by length of string)
* @param buf Buffer to receive string * @param buf Buffer to receive string
* @param bufSize Capacity of buffer in bytes * @param bufSize Capacity of buffer in bytes
* @return Pointer to buf or NULL on overflow or error * @return Pointer to buf or NULL on overflow or error
*/ */
ZT_ALWAYS_INLINE char *rS(int &ii,char *const buf,const unsigned int bufSize) const noexcept ZT_ALWAYS_INLINE char *rS(int &ii,char *const buf,const unsigned int bufSize) const noexcept
{ {
const char *const s = (const char *)(b + ii); const char *const s = (const char *)(unsafeData + ii);
const int sii = ii; const int sii = ii;
while (ii < ZT_BUF_MEM_SIZE) { while (ii < ZT_BUF_MEM_SIZE) {
if (b[ii++] == 0) { if (unsafeData[ii++] == 0) {
memcpy(buf,s,ii - sii); memcpy(buf,s,ii - sii);
return buf; return buf;
} }
@ -351,14 +351,14 @@ public:
* This version avoids a copy and so is faster if the buffer won't be modified between * This version avoids a copy and so is faster if the buffer won't be modified between
* reading and processing. * reading and processing.
* *
* @param ii Iterator * @param ii Index value-result parameter (incremented by length of string)
* @return Pointer to null-terminated C-style string or NULL on overflow or error * @return Pointer to null-terminated C-style string or NULL on overflow or error
*/ */
ZT_ALWAYS_INLINE const char *rSnc(int &ii) const noexcept ZT_ALWAYS_INLINE const char *rSnc(int &ii) const noexcept
{ {
const char *const s = (const char *)(b + ii); const char *const s = (const char *)(unsafeData + ii);
while (ii < ZT_BUF_MEM_SIZE) { while (ii < ZT_BUF_MEM_SIZE) {
if (b[ii++] == 0) if (unsafeData[ii++] == 0)
return s; return s;
} }
return nullptr; return nullptr;
@ -370,15 +370,15 @@ public:
* Use this if the buffer's memory may get changed between reading and processing * Use this if the buffer's memory may get changed between reading and processing
* what is read. * what is read.
* *
* @param ii Iterator * @param ii Index value-result parameter (incremented by len)
* @param bytes Buffer to contain data to read * @param bytes Buffer to contain data to read
* @param len Length of buffer * @param len Length of buffer
* @return Pointer to data or NULL on overflow or error * @return Pointer to data or NULL on overflow or error
*/ */
ZT_ALWAYS_INLINE uint8_t *rB(int &ii,void *bytes,unsigned int len) const noexcept ZT_ALWAYS_INLINE uint8_t *rB(int &ii,void *const bytes,const unsigned int len) const noexcept
{ {
if ((ii += (int)len) <= ZT_BUF_MEM_SIZE) { if ((ii += (int)len) <= ZT_BUF_MEM_SIZE) {
memcpy(bytes,b + ii,len); memcpy(bytes,unsafeData + ii,len);
return reinterpret_cast<uint8_t *>(bytes); return reinterpret_cast<uint8_t *>(bytes);
} }
return nullptr; return nullptr;
@ -393,7 +393,7 @@ public:
* This version avoids a copy and so is faster if the buffer won't be modified between * This version avoids a copy and so is faster if the buffer won't be modified between
* reading and processing. * reading and processing.
* *
* @param ii Iterator * @param ii Index value-result parameter (incremented by len)
* @param len Length of data field to obtain a pointer to * @param len Length of data field to obtain a pointer to
* @return Pointer to field or NULL on overflow * @return Pointer to field or NULL on overflow
*/ */
@ -403,25 +403,102 @@ public:
return ((ii += (int)len) <= ZT_BUF_MEM_SIZE) ? b : nullptr; return ((ii += (int)len) <= ZT_BUF_MEM_SIZE) ? b : nullptr;
} }
/**
* Load a value at an index without advancing the index
*
* Note that unlike the rI??() methods this does not increment ii and therefore
* will not necessarily result in a 'true' return from readOverflow(). It does
* however subject 'ii' to soft bounds masking like the gI??() methods.
*/
ZT_ALWAYS_INLINE uint8_t lI8(const int ii) const noexcept
{
return unsafeData[(unsigned int)ii & ZT_BUF_MEM_MASK];
}
/**
* Load a value at an index without advancing the index
*
* Note that unlike the rI??() methods this does not increment ii and therefore
* will not necessarily result in a 'true' return from readOverflow(). It does
* however subject 'ii' to soft bounds masking like the gI??() methods.
*/
ZT_ALWAYS_INLINE uint16_t lI16(const int ii) const noexcept
{
const unsigned int s = (unsigned int)ii & ZT_BUF_MEM_MASK;
#ifdef ZT_NO_UNALIGNED_ACCESS
return (
((uint16_t)data.bytes[s] << 8U) |
(uint16_t)data.bytes[s + 1]);
#else
return Utils::ntoh(*reinterpret_cast<const uint16_t *>(unsafeData + s));
#endif
}
/**
* Load a value at an index without advancing the index
*
* Note that unlike the rI??() methods this does not increment ii and therefore
* will not necessarily result in a 'true' return from readOverflow(). It does
* however subject 'ii' to soft bounds masking like the gI??() methods.
*/
ZT_ALWAYS_INLINE uint32_t lI32(const int ii) const noexcept
{
const unsigned int s = (unsigned int)ii & ZT_BUF_MEM_MASK;
#ifdef ZT_NO_UNALIGNED_ACCESS
return (
((uint32_t)data.bytes[s] << 24U) |
((uint32_t)data.bytes[s + 1] << 16U) |
((uint32_t)data.bytes[s + 2] << 8U) |
(uint32_t)data.bytes[s + 3]);
#else
return Utils::ntoh(*reinterpret_cast<const uint32_t *>(unsafeData + s));
#endif
}
/**
* Load a value at an index without advancing the index
*
* Note that unlike the rI??() methods this does not increment ii and therefore
* will not necessarily result in a 'true' return from readOverflow(). It does
* however subject 'ii' to soft bounds masking like the gI??() methods.
*/
ZT_ALWAYS_INLINE uint8_t lI64(const int ii) const noexcept
{
const unsigned int s = (unsigned int)ii & ZT_BUF_MEM_MASK;
#ifdef ZT_NO_UNALIGNED_ACCESS
return (
((uint64_t)data.bytes[s] << 56U) |
((uint64_t)data.bytes[s + 1] << 48U) |
((uint64_t)data.bytes[s + 2] << 40U) |
((uint64_t)data.bytes[s + 3] << 32U) |
((uint64_t)data.bytes[s + 4] << 24U) |
((uint64_t)data.bytes[s + 5] << 16U) |
((uint64_t)data.bytes[s + 6] << 8U) |
(uint64_t)data.bytes[s + 7]);
#else
return Utils::ntoh(*reinterpret_cast<const uint64_t *>(unsafeData + s));
#endif
}
/** /**
* Write a byte * Write a byte
* *
* @param ii Iterator * @param ii Index value-result parameter (incremented by 1)
* @param n Byte * @param n Byte
*/ */
ZT_ALWAYS_INLINE void wI(int &ii,uint8_t n) noexcept ZT_ALWAYS_INLINE void wI8(int &ii,const uint8_t n) noexcept
{ {
const int s = ii++; const int s = ii++;
b[(unsigned int)s & ZT_BUF_MEM_MASK] = n; unsafeData[(unsigned int)s & ZT_BUF_MEM_MASK] = n;
} }
/** /**
* Write a 16-bit integer in big-endian byte order * Write a 16-bit integer in big-endian byte order
* *
* @param ii Iterator * @param ii Index value-result parameter (incremented by 2)
* @param n Integer * @param n Integer
*/ */
ZT_ALWAYS_INLINE void wI(int &ii,uint16_t n) noexcept ZT_ALWAYS_INLINE void wI16(int &ii,const uint16_t n) noexcept
{ {
const unsigned int s = ((unsigned int)ii) & ZT_BUF_MEM_MASK; const unsigned int s = ((unsigned int)ii) & ZT_BUF_MEM_MASK;
ii += 2; ii += 2;
@ -429,17 +506,17 @@ public:
b[s] = (uint8_t)(n >> 8U); b[s] = (uint8_t)(n >> 8U);
b[s + 1] = (uint8_t)n; b[s + 1] = (uint8_t)n;
#else #else
*reinterpret_cast<uint16_t *>(b + s) = Utils::hton(n); *reinterpret_cast<uint16_t *>(unsafeData + s) = Utils::hton(n);
#endif #endif
} }
/** /**
* Write a 32-bit integer in big-endian byte order * Write a 32-bit integer in big-endian byte order
* *
* @param ii Iterator * @param ii Index value-result parameter (incremented by 4)
* @param n Integer * @param n Integer
*/ */
ZT_ALWAYS_INLINE void wI(int &ii,uint32_t n) noexcept ZT_ALWAYS_INLINE void wI32(int &ii,const uint32_t n) noexcept
{ {
const unsigned int s = ((unsigned int)ii) & ZT_BUF_MEM_MASK; const unsigned int s = ((unsigned int)ii) & ZT_BUF_MEM_MASK;
ii += 4; ii += 4;
@ -449,17 +526,17 @@ public:
b[s + 2] = (uint8_t)(n >> 8U); b[s + 2] = (uint8_t)(n >> 8U);
b[s + 3] = (uint8_t)n; b[s + 3] = (uint8_t)n;
#else #else
*reinterpret_cast<uint32_t *>(b + s) = Utils::hton(n); *reinterpret_cast<uint32_t *>(unsafeData + s) = Utils::hton(n);
#endif #endif
} }
/** /**
* Write a 64-bit integer in big-endian byte order * Write a 64-bit integer in big-endian byte order
* *
* @param ii Iterator * @param ii Index value-result parameter (incremented by 8)
* @param n Integer * @param n Integer
*/ */
ZT_ALWAYS_INLINE void wI(int &ii,uint64_t n) noexcept ZT_ALWAYS_INLINE void wI64(int &ii,const uint64_t n) noexcept
{ {
const unsigned int s = ((unsigned int)ii) & ZT_BUF_MEM_MASK; const unsigned int s = ((unsigned int)ii) & ZT_BUF_MEM_MASK;
ii += 8; ii += 8;
@ -473,7 +550,7 @@ public:
b[s + 6] = (uint8_t)(n >> 8U); b[s + 6] = (uint8_t)(n >> 8U);
b[s + 7] = (uint8_t)n; b[s + 7] = (uint8_t)n;
#else #else
*reinterpret_cast<uint64_t *>(b + s) = Utils::hton(n); *reinterpret_cast<uint64_t *>(unsafeData + s) = Utils::hton(n);
#endif #endif
} }
@ -481,7 +558,7 @@ public:
* Write an object implementing the marshal interface * Write an object implementing the marshal interface
* *
* @tparam T Object type * @tparam T Object type
* @param ii Iterator * @param ii Index value-result parameter (incremented by size of object)
* @param t Object to write * @param t Object to write
*/ */
template<typename T> template<typename T>
@ -489,7 +566,7 @@ public:
{ {
const int s = ii; const int s = ii;
if ((s + T::marshalSizeMax()) <= ZT_BUF_MEM_SIZE) { if ((s + T::marshalSizeMax()) <= ZT_BUF_MEM_SIZE) {
int ms = t.marshal(b + s); int ms = t.marshal(unsafeData + s);
if (ms > 0) if (ms > 0)
ii += ms; ii += ms;
} else { } else {
@ -500,7 +577,7 @@ public:
/** /**
* Write a C-style null-terminated string (including the trailing zero) * Write a C-style null-terminated string (including the trailing zero)
* *
* @param ii Iterator * @param ii Index value-result parameter (incremented by length of string)
* @param s String to write (writes an empty string if this is NULL) * @param s String to write (writes an empty string if this is NULL)
*/ */
ZT_ALWAYS_INLINE void wS(int &ii,const char *s) noexcept ZT_ALWAYS_INLINE void wS(int &ii,const char *s) noexcept
@ -509,17 +586,17 @@ public:
char c; char c;
do { do {
c = *(s++); c = *(s++);
wI(ii,(uint8_t)c); wI8(ii,(uint8_t)c);
} while (c); } while (c);
} else { } else {
wI(ii,(uint8_t)0); wI8(ii,0);
} }
} }
/** /**
* Write a byte array * Write a byte array
* *
* @param ii Iterator * @param ii Index value-result parameter (incremented by len)
* @param bytes Bytes to write * @param bytes Bytes to write
* @param len Size of data in bytes * @param len Size of data in bytes
*/ */
@ -527,7 +604,65 @@ public:
{ {
const int s = ii; const int s = ii;
if ((ii += (int)len) <= ZT_BUF_MEM_SIZE) if ((ii += (int)len) <= ZT_BUF_MEM_SIZE)
memcpy(b + s,bytes,len); memcpy(unsafeData + s,bytes,len);
}
/**
* Store a byte without advancing the index
*/
ZT_ALWAYS_INLINE void sI8(const int ii,const uint8_t n) noexcept
{
unsafeData[(unsigned int)ii & ZT_BUF_MEM_MASK] = n;
}
/**
* Store an integer without advancing the index
*/
ZT_ALWAYS_INLINE void sI16(const int ii,const uint16_t n) noexcept
{
const unsigned int s = ((unsigned int)ii) & ZT_BUF_MEM_MASK;
#ifdef ZT_NO_UNALIGNED_ACCESS
b[s] = (uint8_t)(n >> 8U);
b[s + 1] = (uint8_t)n;
#else
*reinterpret_cast<uint16_t *>(unsafeData + s) = Utils::hton(n);
#endif
}
/**
* Store an integer without advancing the index
*/
ZT_ALWAYS_INLINE void sI32(const int ii,const uint32_t n) noexcept
{
const unsigned int s = ((unsigned int)ii) & ZT_BUF_MEM_MASK;
#ifdef ZT_NO_UNALIGNED_ACCESS
b[s] = (uint8_t)(n >> 24U);
b[s + 1] = (uint8_t)(n >> 16U);
b[s + 2] = (uint8_t)(n >> 8U);
b[s + 3] = (uint8_t)n;
#else
*reinterpret_cast<uint32_t *>(unsafeData + s) = Utils::hton(n);
#endif
}
/**
* Store an integer without advancing the index
*/
ZT_ALWAYS_INLINE void sI64(const int ii,const uint64_t n) noexcept
{
const unsigned int s = ((unsigned int)ii) & ZT_BUF_MEM_MASK;
#ifdef ZT_NO_UNALIGNED_ACCESS
b[s] = (uint8_t)(n >> 56U);
b[s + 1] = (uint8_t)(n >> 48U);
b[s + 2] = (uint8_t)(n >> 40U);
b[s + 3] = (uint8_t)(n >> 32U);
b[s + 4] = (uint8_t)(n >> 24U);
b[s + 5] = (uint8_t)(n >> 16U);
b[s + 6] = (uint8_t)(n >> 8U);
b[s + 7] = (uint8_t)n;
#else
*reinterpret_cast<uint64_t *>(unsafeData + s) = Utils::hton(n);
#endif
} }
/** /**
@ -547,7 +682,7 @@ public:
* @return Reference to 'b' cast to type T * @return Reference to 'b' cast to type T
*/ */
template<typename T> template<typename T>
ZT_ALWAYS_INLINE T &as(const unsigned int i = 0) noexcept { return *reinterpret_cast<T *>(b + i); } ZT_ALWAYS_INLINE T &as(const unsigned int i = 0) noexcept { return *reinterpret_cast<T *>(unsafeData + i); }
/** /**
* Cast data in 'b' to a (usually packed) structure type (const) * Cast data in 'b' to a (usually packed) structure type (const)
@ -561,14 +696,14 @@ public:
* @return Reference to 'b' cast to type T * @return Reference to 'b' cast to type T
*/ */
template<typename T> template<typename T>
ZT_ALWAYS_INLINE const T &as(const unsigned int i = 0) const noexcept { return *reinterpret_cast<const T *>(b + i); } ZT_ALWAYS_INLINE const T &as(const unsigned int i = 0) const noexcept { return *reinterpret_cast<const T *>(unsafeData + i); }
ZT_ALWAYS_INLINE bool operator==(const Buf &b2) const noexcept { return (memcmp(b,b2.b,ZT_BUF_MEM_SIZE) == 0); } ZT_ALWAYS_INLINE bool operator==(const Buf &b2) const noexcept { return (memcmp(unsafeData,b2.unsafeData,ZT_BUF_MEM_SIZE) == 0); }
ZT_ALWAYS_INLINE bool operator!=(const Buf &b2) const noexcept { return (memcmp(b,b2.b,ZT_BUF_MEM_SIZE) != 0); } ZT_ALWAYS_INLINE bool operator!=(const Buf &b2) const noexcept { return (memcmp(unsafeData,b2.unsafeData,ZT_BUF_MEM_SIZE) != 0); }
ZT_ALWAYS_INLINE bool operator<(const Buf &b2) const noexcept { return (memcmp(b,b2.b,ZT_BUF_MEM_SIZE) < 0); } ZT_ALWAYS_INLINE bool operator<(const Buf &b2) const noexcept { return (memcmp(unsafeData,b2.unsafeData,ZT_BUF_MEM_SIZE) < 0); }
ZT_ALWAYS_INLINE bool operator<=(const Buf &b2) const noexcept { return (memcmp(b,b2.b,ZT_BUF_MEM_SIZE) <= 0); } ZT_ALWAYS_INLINE bool operator<=(const Buf &b2) const noexcept { return (memcmp(unsafeData,b2.unsafeData,ZT_BUF_MEM_SIZE) <= 0); }
ZT_ALWAYS_INLINE bool operator>(const Buf &b2) const noexcept { return (memcmp(b,b2.b,ZT_BUF_MEM_SIZE) > 0); } ZT_ALWAYS_INLINE bool operator>(const Buf &b2) const noexcept { return (memcmp(unsafeData,b2.unsafeData,ZT_BUF_MEM_SIZE) > 0); }
ZT_ALWAYS_INLINE bool operator>=(const Buf &b2) const noexcept { return (memcmp(b,b2.b,ZT_BUF_MEM_SIZE) >= 0); } ZT_ALWAYS_INLINE bool operator>=(const Buf &b2) const noexcept { return (memcmp(unsafeData,b2.unsafeData,ZT_BUF_MEM_SIZE) >= 0); }
/** /**
* Raw data held in buffer * Raw data held in buffer
@ -577,7 +712,7 @@ public:
* They exist to allow reads and writes of integer types to silently overflow if a * They exist to allow reads and writes of integer types to silently overflow if a
* read or write is performed at the end of the buffer. * read or write is performed at the end of the buffer.
*/ */
uint8_t b[ZT_BUF_MEM_SIZE + 8]; uint8_t unsafeData[ZT_BUF_MEM_SIZE + 8];
private: private:
// Next item in free buffer pool linked list if Buf is placed in pool, undefined and unused otherwise // Next item in free buffer pool linked list if Buf is placed in pool, undefined and unused otherwise

View file

@ -16,6 +16,7 @@ set(core_headers
ECC384.hpp ECC384.hpp
Expect.hpp Expect.hpp
FCV.hpp FCV.hpp
H.hpp
Hashtable.hpp Hashtable.hpp
Identity.hpp Identity.hpp
InetAddress.hpp InetAddress.hpp

View file

@ -66,16 +66,6 @@
*/ */
#define ZT_MAX_NETWORK_CONFIG_BYTES 131072 #define ZT_MAX_NETWORK_CONFIG_BYTES 131072
/**
* Size of RX queue in packets
*/
#define ZT_RX_QUEUE_SIZE 32
/**
* Size of TX queue in packets
*/
#define ZT_TX_QUEUE_SIZE 32
/** /**
* Length of peer shared secrets (256-bit, do not change) * Length of peer shared secrets (256-bit, do not change)
*/ */
@ -109,16 +99,6 @@
*/ */
#define ZT_WHOIS_RETRY_DELAY 500 #define ZT_WHOIS_RETRY_DELAY 500
/**
* Transmit queue entry timeout
*/
#define ZT_TRANSMIT_QUEUE_TIMEOUT 5000
/**
* Receive queue entry timeout
*/
#define ZT_RECEIVE_QUEUE_TIMEOUT 5000
/** /**
* Maximum number of ZT hops allowed (this is not IP hops/TTL) * Maximum number of ZT hops allowed (this is not IP hops/TTL)
* *
@ -156,6 +136,15 @@
*/ */
#define ZT_PEER_ACTIVITY_TIMEOUT (ZT_PEER_PING_PERIOD + 5000) #define ZT_PEER_ACTIVITY_TIMEOUT (ZT_PEER_PING_PERIOD + 5000)
/**
* Global timeout for peers in milliseconds
*
* This is global as in "entire world," and this value is 30 days. In this
* code the global timeout is used to determine when to ignore cached
* peers and their identity<>address mappings.
*/
#define ZT_PEER_GLOBAL_TIMEOUT 2592000000LL
/** /**
* Maximum interval between sort/prioritize of paths for a peer * Maximum interval between sort/prioritize of paths for a peer
*/ */

View file

@ -20,13 +20,12 @@ bool Endpoint::operator==(const Endpoint &ep) const
if (_t == ep._t) { if (_t == ep._t) {
switch(_t) { switch(_t) {
default: return true; default: return true;
case INETADDR_V4: case TYPE_ZEROTIER: return ((_v.zt.a == ep._v.zt.a)&&(memcmp(_v.zt.idh,ep._v.zt.idh,sizeof(_v.zt.idh)) == 0));
case INETADDR_V6: return (inetAddr() == ep.inetAddr()); case TYPE_DNSNAME: return ((_v.dns.port == ep._v.dns.port)&&(strcmp(_v.dns.name,ep._v.dns.name) == 0));
case DNSNAME: return ((_v.dns.port == ep._v.dns.port)&&(strcmp(_v.dns.name,ep._v.dns.name) == 0)); case TYPE_URL: return (strcmp(_v.url,ep._v.url) == 0);
case ZEROTIER: return ((_v.zt.a == ep._v.zt.a)&&(memcmp(_v.zt.idh,ep._v.zt.idh,sizeof(_v.zt.idh)) == 0)); case TYPE_ETHERNET: return (_v.eth == ep._v.eth);
case URL: return (strcmp(_v.url,ep._v.url) == 0); case TYPE_INETADDR_V4:
case ETHERNET: return (_v.eth == ep._v.eth); case TYPE_INETADDR_V6: return (inetAddr() == ep.inetAddr());
case WEBRTC: return ((_v.webrtc.offerLen == ep._v.webrtc.offerLen)&&(memcmp(_v.webrtc.offer,ep._v.webrtc.offer,_v.webrtc.offerLen) == 0));
} }
} }
return false; return false;
@ -39,20 +38,14 @@ bool Endpoint::operator<(const Endpoint &ep) const
} else if (_t == ep._t) { } else if (_t == ep._t) {
int ncmp; int ncmp;
switch(_t) { switch(_t) {
case INETADDR_V4: case TYPE_ZEROTIER: return (_v.zt.a < ep._v.zt.a) ? true : ((_v.zt.a == ep._v.zt.a)&&(memcmp(_v.zt.idh,ep._v.zt.idh,sizeof(_v.zt.idh)) < 0));
case INETADDR_V6: case TYPE_DNSNAME:
return (inetAddr() < ep.inetAddr());
case DNSNAME:
ncmp = strcmp(_v.dns.name,ep._v.dns.name); ncmp = strcmp(_v.dns.name,ep._v.dns.name);
return ((ncmp < 0) ? true : (ncmp == 0)&&(_v.dns.port < ep._v.dns.port)); return ((ncmp < 0) ? true : (ncmp == 0)&&(_v.dns.port < ep._v.dns.port));
case ZEROTIER: return (_v.zt.a < ep._v.zt.a) ? true : ((_v.zt.a == ep._v.zt.a)&&(memcmp(_v.zt.idh,ep._v.zt.idh,sizeof(_v.zt.idh)) < 0)); case TYPE_URL: return (strcmp(_v.url,ep._v.url) < 0);
case URL: return (strcmp(_v.url,ep._v.url) < 0); case TYPE_ETHERNET: return (_v.eth < ep._v.eth);
case ETHERNET: return (_v.eth < ep._v.eth); case TYPE_INETADDR_V4:
case WEBRTC: case TYPE_INETADDR_V6: return (inetAddr() < ep.inetAddr());
if (_v.webrtc.offerLen < ep._v.webrtc.offerLen)
return true;
else if (_v.webrtc.offerLen == ep._v.webrtc.offerLen)
return memcmp(_v.webrtc.offer,ep._v.webrtc.offer,_v.webrtc.offerLen) < 0;
default: return false; default: return false;
} }
} }
@ -67,10 +60,15 @@ int Endpoint::marshal(uint8_t data[ZT_ENDPOINT_MARSHAL_SIZE_MAX]) const noexcept
Utils::storeBigEndian(data + 3,(int16_t)_l[1]); Utils::storeBigEndian(data + 3,(int16_t)_l[1]);
Utils::storeBigEndian(data + 5,(int16_t)_l[2]); Utils::storeBigEndian(data + 5,(int16_t)_l[2]);
switch(_t) { switch(_t) {
case INETADDR_V4: case TYPE_ZEROTIER:
case INETADDR_V6: data[7] = (uint8_t)(_v.zt.a >> 32U);
return 7 + reinterpret_cast<const InetAddress *>(&_v.sa)->marshal(data+1); data[8] = (uint8_t)(_v.zt.a >> 24U);
case DNSNAME: data[9] = (uint8_t)(_v.zt.a >> 16U);
data[10] = (uint8_t)(_v.zt.a >> 8U);
data[11] = (uint8_t)_v.zt.a;
memcpy(data + 12,_v.zt.idh,ZT_IDENTITY_HASH_SIZE);
return ZT_IDENTITY_HASH_SIZE + 12;
case TYPE_DNSNAME:
p = 7; p = 7;
for (;;) { for (;;) {
if ((data[p] = (uint8_t)_v.dns.name[p-1]) == 0) if ((data[p] = (uint8_t)_v.dns.name[p-1]) == 0)
@ -82,15 +80,7 @@ int Endpoint::marshal(uint8_t data[ZT_ENDPOINT_MARSHAL_SIZE_MAX]) const noexcept
data[p++] = (uint8_t)(_v.dns.port >> 8U); data[p++] = (uint8_t)(_v.dns.port >> 8U);
data[p++] = (uint8_t)_v.dns.port; data[p++] = (uint8_t)_v.dns.port;
return p; return p;
case ZEROTIER: case TYPE_URL:
data[7] = (uint8_t)(_v.zt.a >> 32U);
data[8] = (uint8_t)(_v.zt.a >> 24U);
data[9] = (uint8_t)(_v.zt.a >> 16U);
data[10] = (uint8_t)(_v.zt.a >> 8U);
data[11] = (uint8_t)_v.zt.a;
memcpy(data + 12,_v.zt.idh,ZT_IDENTITY_HASH_SIZE);
return ZT_IDENTITY_HASH_SIZE + 12;
case URL:
p = 7; p = 7;
for (;;) { for (;;) {
if ((data[p] = (uint8_t)_v.url[p-1]) == 0) if ((data[p] = (uint8_t)_v.url[p-1]) == 0)
@ -100,7 +90,7 @@ int Endpoint::marshal(uint8_t data[ZT_ENDPOINT_MARSHAL_SIZE_MAX]) const noexcept
return -1; return -1;
} }
return p; return p;
case ETHERNET: case TYPE_ETHERNET:
data[7] = (uint8_t)(_v.eth >> 40U); data[7] = (uint8_t)(_v.eth >> 40U);
data[8] = (uint8_t)(_v.eth >> 32U); data[8] = (uint8_t)(_v.eth >> 32U);
data[9] = (uint8_t)(_v.eth >> 24U); data[9] = (uint8_t)(_v.eth >> 24U);
@ -108,12 +98,14 @@ int Endpoint::marshal(uint8_t data[ZT_ENDPOINT_MARSHAL_SIZE_MAX]) const noexcept
data[11] = (uint8_t)(_v.eth >> 8U); data[11] = (uint8_t)(_v.eth >> 8U);
data[12] = (uint8_t)_v.eth; data[12] = (uint8_t)_v.eth;
return 13; return 13;
case WEBRTC: case TYPE_INETADDR_V4:
Utils::storeBigEndian(data + 7,(uint16_t)_v.webrtc.offerLen); case TYPE_INETADDR_V6:
memcpy(data + 9,_v.webrtc.offer,_v.webrtc.offerLen); p = asInetAddress(_v.sa).marshal(data + 7);
return 9 + _v.webrtc.offerLen; if (p < 0)
return p;
return 7 + p;
default: default:
data[0] = (uint8_t)NIL; data[0] = (uint8_t)TYPE_NIL;
return 7; return 7;
} }
} }
@ -128,12 +120,19 @@ int Endpoint::unmarshal(const uint8_t *restrict data,const int len) noexcept
_l[1] = Utils::loadBigEndian<int16_t>(data + 3); _l[1] = Utils::loadBigEndian<int16_t>(data + 3);
_l[2] = Utils::loadBigEndian<int16_t>(data + 5); _l[2] = Utils::loadBigEndian<int16_t>(data + 5);
switch(_t) { switch(_t) {
case NIL: case TYPE_NIL:
return 7; return 7;
case INETADDR_V4: case TYPE_ZEROTIER:
case INETADDR_V6: if (len < (12 + ZT_IDENTITY_HASH_SIZE))
return 7 + reinterpret_cast<InetAddress *>(&_v.sa)->unmarshal(data+7,len-7); return -1;
case DNSNAME: _v.zt.a = ((uint64_t)data[7]) << 32U;
_v.zt.a |= ((uint64_t)data[8]) << 24U;
_v.zt.a |= ((uint64_t)data[9]) << 16U;
_v.zt.a |= ((uint64_t)data[10]) << 8U;
_v.zt.a |= (uint64_t)data[11];
memcpy(_v.zt.idh,data + 12,ZT_IDENTITY_HASH_SIZE);
return 60;
case TYPE_DNSNAME:
if (len < 10) if (len < 10)
return -1; return -1;
p = 7; p = 7;
@ -149,17 +148,7 @@ int Endpoint::unmarshal(const uint8_t *restrict data,const int len) noexcept
_v.dns.port = (uint16_t)(((unsigned int)data[p++]) << 8U); _v.dns.port = (uint16_t)(((unsigned int)data[p++]) << 8U);
_v.dns.port |= (uint16_t)data[p++]; _v.dns.port |= (uint16_t)data[p++];
return p; return p;
case ZEROTIER: case TYPE_URL:
if (len < 60)
return -1;
_v.zt.a = ((uint64_t)data[7]) << 32U;
_v.zt.a |= ((uint64_t)data[8]) << 24U;
_v.zt.a |= ((uint64_t)data[9]) << 16U;
_v.zt.a |= ((uint64_t)data[10]) << 8U;
_v.zt.a |= (uint64_t)data[11];
memcpy(_v.zt.idh,data + 12,48);
return 60;
case URL:
if (len < 8) if (len < 8)
return -1; return -1;
p = 7; p = 7;
@ -173,7 +162,7 @@ int Endpoint::unmarshal(const uint8_t *restrict data,const int len) noexcept
return -1; return -1;
} }
return p; return p;
case ETHERNET: case TYPE_ETHERNET:
if (len < 13) if (len < 13)
return -1; return -1;
_v.eth = ((uint64_t)data[7]) << 40U; _v.eth = ((uint64_t)data[7]) << 40U;
@ -183,20 +172,19 @@ int Endpoint::unmarshal(const uint8_t *restrict data,const int len) noexcept
_v.eth |= ((uint64_t)data[11]) << 8U; _v.eth |= ((uint64_t)data[11]) << 8U;
_v.eth |= (uint64_t)data[12]; _v.eth |= (uint64_t)data[12];
return 13; return 13;
case WEBRTC: case TYPE_INETADDR_V4:
case TYPE_INETADDR_V6:
p = asInetAddress(_v.sa).unmarshal(data + 7,len - 7);
if (p <= 0)
return -1;
return 7 + p;
default:
// Unrecognized endpoint types not yet specified must start with a 16-bit
// length so that older versions of ZeroTier can skip them.
if (len < 9) if (len < 9)
return -1; return -1;
_v.webrtc.offerLen = Utils::loadBigEndian<uint16_t>(data + 7); p = 9 + (int)Utils::loadBigEndian<uint16_t>(data + 7);
if ((len < (9 + _v.webrtc.offerLen))||(_v.webrtc.offerLen > ZT_ENDPOINT_MAX_NAME_SIZE)) return (p > len) ? -1 : p;
return -1;
memcpy(_v.webrtc.offer,data + 9,_v.webrtc.offerLen);
return 9 + _v.webrtc.offerLen;
default:
// Unrecognized endpoint types not yet specified must start with a byte
// length size so that older versions of ZeroTier can skip them.
if (len < 8)
return -1;
return 8 + (int)data[7];
} }
} }

View file

@ -39,99 +39,96 @@ namespace ZeroTier {
class Endpoint : public TriviallyCopyable class Endpoint : public TriviallyCopyable
{ {
public: public:
/**
* Endpoint type
*/
enum Type enum Type
{ {
NIL = 0, // NIL value TYPE_NIL = 0, // NIL value
INETADDR_V4 = 1, // IPv4 TYPE_ZEROTIER = 1, // ZeroTier Address (for relaying and meshy behavior)
INETADDR_V6 = 2, // IPv6 TYPE_DNSNAME = 2, // DNS name and port that resolves to InetAddress
DNSNAME = 3, // DNS name and port that resolves to InetAddress TYPE_URL = 3, // URL for HTTP or Web Sockets transport
ZEROTIER = 4, // ZeroTier Address (for relaying and meshy behavior) TYPE_INETADDR_V4 = 4, // IPv4
URL = 5, // URL for http/https/ws/etc. (not implemented yet) TYPE_ETHERNET = 5, // 48-bit LAN-local Ethernet address
ETHERNET = 6, // 48-bit LAN-local Ethernet address TYPE_INETADDR_V6 = 6 // IPv6
WEBRTC = 7, // WebRTC data channels
UNRECOGNIZED = 255 // Unrecognized endpoint type encountered in stream
}; };
ZT_ALWAYS_INLINE Endpoint() noexcept { memoryZero(this); } ZT_ALWAYS_INLINE Endpoint() noexcept { memoryZero(this); }
explicit ZT_ALWAYS_INLINE Endpoint(const InetAddress &sa) { *this = sa; } ZT_ALWAYS_INLINE Endpoint(const InetAddress &sa)
{
switch (sa.ss_family) {
case AF_INET:
_t = TYPE_INETADDR_V4;
break;
case AF_INET6:
_t = TYPE_INETADDR_V6;
default:
_t = TYPE_NIL;
return;
}
asInetAddress(_v.sa) = sa;
}
ZT_ALWAYS_INLINE Endpoint(const Address &zt,const uint8_t identityHash[ZT_IDENTITY_HASH_SIZE]) : ZT_ALWAYS_INLINE Endpoint(const Address &zt,const uint8_t identityHash[ZT_IDENTITY_HASH_SIZE]) :
_t(ZEROTIER) _t(TYPE_ZEROTIER)
{ {
_v.zt.a = zt.toInt(); _v.zt.a = zt.toInt();
memcpy(_v.zt.idh,identityHash,ZT_IDENTITY_HASH_SIZE); memcpy(_v.zt.idh,identityHash,ZT_IDENTITY_HASH_SIZE);
} }
ZT_ALWAYS_INLINE Endpoint(const char *name,const int port) : ZT_ALWAYS_INLINE Endpoint(const char *name,const int port) :
_t(DNSNAME) _t(TYPE_DNSNAME)
{ {
_v.dns.port = port; _v.dns.port = port;
Utils::scopy(_v.dns.name,sizeof(_v.dns.name),name); Utils::scopy(_v.dns.name,sizeof(_v.dns.name),name);
} }
explicit ZT_ALWAYS_INLINE Endpoint(const char *url) : explicit ZT_ALWAYS_INLINE Endpoint(const char *url) :
_t(URL) _t(TYPE_URL)
{ Utils::scopy(_v.url,sizeof(_v.url),url); } { Utils::scopy(_v.url,sizeof(_v.url),url); }
ZT_ALWAYS_INLINE Endpoint &operator=(const InetAddress &sa)
{
switch(sa.ss_family) {
case AF_INET:
_t = INETADDR_V4;
break;
case AF_INET6:
_t = INETADDR_V6;
break;
default:
_t = NIL;
return *this;
}
_v.sa = sa;
return *this;
}
/** /**
* @return InetAddress or NIL if not of this type * @return InetAddress or NIL if not of this type
*/ */
ZT_ALWAYS_INLINE const InetAddress &inetAddr() const noexcept { return ((_t == INETADDR_V4)||(_t == INETADDR_V6)) ? *reinterpret_cast<const InetAddress *>(&_v.sa) : InetAddress::NIL; } ZT_ALWAYS_INLINE const InetAddress &inetAddr() const noexcept { return ((_t == TYPE_INETADDR_V4)||(_t == TYPE_INETADDR_V6)) ? asInetAddress(_v.sa) : InetAddress::NIL; }
/** /**
* @return DNS name or empty string if not of this type * @return DNS name or empty string if not of this type
*/ */
ZT_ALWAYS_INLINE const char *dnsName() const noexcept { return (_t == DNSNAME) ? _v.dns.name : ""; } ZT_ALWAYS_INLINE const char *dnsName() const noexcept { return (_t == TYPE_DNSNAME) ? _v.dns.name : ""; }
/** /**
* @return Port associated with DNS name or -1 if not of this type * @return Port associated with DNS name or -1 if not of this type
*/ */
ZT_ALWAYS_INLINE int dnsPort() const noexcept { return (_t == DNSNAME) ? _v.dns.port : -1; } ZT_ALWAYS_INLINE int dnsPort() const noexcept { return (_t == TYPE_DNSNAME) ? _v.dns.port : -1; }
/** /**
* @return ZeroTier address or NIL if not of this type * @return ZeroTier address or NIL if not of this type
*/ */
ZT_ALWAYS_INLINE Address ztAddress() const noexcept { return Address((_t == ZEROTIER) ? _v.zt.a : (uint64_t)0); } ZT_ALWAYS_INLINE Address ztAddress() const noexcept { return Address((_t == TYPE_ZEROTIER) ? _v.zt.a : (uint64_t)0); }
/** /**
* @return 384-bit hash of identity keys or NULL if not of this type * @return 384-bit hash of identity keys or NULL if not of this type
*/ */
ZT_ALWAYS_INLINE const uint8_t *ztIdentityHash() const noexcept { return (_t == ZEROTIER) ? _v.zt.idh : nullptr; } ZT_ALWAYS_INLINE const uint8_t *ztIdentityHash() const noexcept { return (_t == TYPE_ZEROTIER) ? _v.zt.idh : nullptr; }
/** /**
* @return URL or empty string if not of this type * @return URL or empty string if not of this type
*/ */
ZT_ALWAYS_INLINE const char *url() const noexcept { return (_t == URL) ? _v.url : ""; } ZT_ALWAYS_INLINE const char *url() const noexcept { return (_t == TYPE_URL) ? _v.url : ""; }
/** /**
* @return Ethernet address or NIL if not of this type * @return Ethernet address or NIL if not of this type
*/ */
ZT_ALWAYS_INLINE MAC ethernet() const noexcept { return (_t == ETHERNET) ? MAC(_v.eth) : MAC(); } ZT_ALWAYS_INLINE MAC ethernet() const noexcept { return (_t == TYPE_ETHERNET) ? MAC(_v.eth) : MAC(); }
/** /**
* @return Endpoint type or NIL if unset/empty * @return Endpoint type or NIL if unset/empty
*/ */
ZT_ALWAYS_INLINE Type type() const noexcept { return _t; } ZT_ALWAYS_INLINE Type type() const noexcept { return _t; }
explicit ZT_ALWAYS_INLINE operator bool() const noexcept { return _t != NIL; } explicit ZT_ALWAYS_INLINE operator bool() const noexcept { return _t != TYPE_NIL; }
bool operator==(const Endpoint &ep) const; bool operator==(const Endpoint &ep) const;
ZT_ALWAYS_INLINE bool operator!=(const Endpoint &ep) const { return (!(*this == ep)); } ZT_ALWAYS_INLINE bool operator!=(const Endpoint &ep) const { return (!(*this == ep)); }
@ -153,10 +150,6 @@ private:
uint16_t port; uint16_t port;
char name[ZT_ENDPOINT_MAX_NAME_SIZE]; char name[ZT_ENDPOINT_MAX_NAME_SIZE];
} dns; } dns;
struct {
uint16_t offerLen;
uint8_t offer[ZT_ENDPOINT_MAX_NAME_SIZE];
} webrtc;
struct { struct {
uint64_t a; uint64_t a;
uint8_t idh[ZT_IDENTITY_HASH_SIZE]; uint8_t idh[ZT_IDENTITY_HASH_SIZE];

86
node/H.hpp Normal file
View file

@ -0,0 +1,86 @@
/*
* Copyright (c)2013-2020 ZeroTier, Inc.
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory.
*
* Change Date: 2024-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2.0 of the Apache License.
*/
/****/
#ifndef ZT_H_HPP
#define ZT_H_HPP
#include "Constants.hpp"
#include "TriviallyCopyable.hpp"
namespace ZeroTier {
/**
* Container for cryptographic hashes
*
* The size of the hash used with this container must be a multiple of 64 bits.
* Currently it's used as H<384> and H<512>.
*
* Warning: the [] operator is not bounds checked.
*
* @tparam BITS Bits in hash, must be a multiple of 64
*/
template<unsigned int BITS>
class H : public TriviallyCopyable
{
public:
ZT_ALWAYS_INLINE H() noexcept {}
/**
* @param h Hash value of size BITS / 8
*/
explicit ZT_ALWAYS_INLINE H(const void *h) noexcept { memcpy(_h,h,BITS / 8); }
/**
* @param h Hash value of size BITS / 8
*/
ZT_ALWAYS_INLINE void set(const void *h) noexcept { memcpy(_h,h,BITS / 8); }
ZT_ALWAYS_INLINE void zero() noexcept
{
for(int i=0;i<(BITS / sizeof(unsigned long));++i)
_h[i] = 0;
}
ZT_ALWAYS_INLINE uint8_t *data() noexcept { return reinterpret_cast<uint8_t *>(_h); }
ZT_ALWAYS_INLINE const uint8_t *data() const noexcept { return reinterpret_cast<const uint8_t *>(_h); }
ZT_ALWAYS_INLINE uint8_t operator[](const unsigned int i) const noexcept { return reinterpret_cast<const uint8_t *>(_h)[i]; }
ZT_ALWAYS_INLINE uint8_t &operator[](const unsigned int i) noexcept { return reinterpret_cast<uint8_t *>(_h)[i]; }
static constexpr unsigned int size() noexcept { return BITS / 8; }
ZT_ALWAYS_INLINE unsigned long hashCode() const noexcept { return _h[0]; }
ZT_ALWAYS_INLINE operator bool() const noexcept
{
for(int i=0;i<(BITS / sizeof(unsigned long));++i) {
if (_h[i] != 0)
return true;
}
return false;
}
ZT_ALWAYS_INLINE bool operator==(const H &h) const noexcept { return memcmp(_h,h._h,BITS / 8) == 0; }
ZT_ALWAYS_INLINE bool operator!=(const H &h) const noexcept { return memcmp(_h,h._h,BITS / 8) != 0; }
ZT_ALWAYS_INLINE bool operator<(const H &h) const noexcept { return memcmp(_h,h._h,BITS / 8) < 0; }
ZT_ALWAYS_INLINE bool operator>(const H &h) const noexcept { return memcmp(_h,h._h,BITS / 8) > 0; }
ZT_ALWAYS_INLINE bool operator<=(const H &h) const noexcept { return memcmp(_h,h._h,BITS / 8) <= 0; }
ZT_ALWAYS_INLINE bool operator>=(const H &h) const noexcept { return memcmp(_h,h._h,BITS / 8) >= 0; }
private:
unsigned long _h[BITS / sizeof(unsigned long)];
};
} // namespace ZeroTier
#endif

View file

@ -93,7 +93,6 @@ bool Identity::generate(const Type t)
_type = t; _type = t;
_hasPrivate = true; _hasPrivate = true;
_hash[0] = 0; // force hash recompute
switch(t) { switch(t) {
case C25519: { case C25519: {
@ -103,32 +102,28 @@ bool Identity::generate(const Type t)
_address.setTo(digest + 59); // last 5 bytes are address _address.setTo(digest + 59); // last 5 bytes are address
} while (_address.isReserved()); } while (_address.isReserved());
delete[] genmem; delete[] genmem;
_computeHash();
} break; } break;
case P384: { case P384: {
AES c;
do { do {
C25519::generate(_pub.c25519,_priv.c25519); C25519::generate(_pub.c25519,_priv.c25519);
ECC384GenerateKey(_pub.p384,_priv.p384); ECC384GenerateKey(_pub.p384,_priv.p384);
// This is just an intentionally complex hash function for use with a simple hashcash
// design to slow down identity generation as a defense in depth against brute force
// collision searches. V0 used a somewhat more overkill memory intensive design that's
// not really necessary and makes verifications too slow, so V1 uses this instead.
SHA384(digest,&_pub,sizeof(_pub)); SHA384(digest,&_pub,sizeof(_pub));
AES c(digest); c.init(digest);
SHA384(digest,digest,48);
std::sort(digest,digest + 48);
c.encrypt(digest,digest); c.encrypt(digest,digest);
c.encrypt(digest + 16,digest + 16); c.encrypt(digest + 16,digest + 16);
c.encrypt(digest + 32,digest + 32); c.encrypt(digest + 32,digest + 32);
SHA384(digest,digest,48);
if (digest[47] != 0) if (digest[47] != 0)
continue; continue;
_address.setTo(digest); _address.setTo(digest);
} while (_address.isReserved()); } while (_address.isReserved());
_hash.set(digest); // P384 uses the same hash for hash() and address generation
} break; } break;
default: default:
@ -143,6 +138,7 @@ bool Identity::locallyValidate() const
if ((_address.isReserved())||(!_address)) if ((_address.isReserved())||(!_address))
return false; return false;
switch (_type) { switch (_type) {
case C25519: case C25519:
try { try {
uint8_t digest[64]; uint8_t digest[64];
@ -153,51 +149,30 @@ bool Identity::locallyValidate() const
} catch ( ... ) {} } catch ( ... ) {}
return false; return false;
case P384: {
const uint8_t *hash = this->hash();
return ((hash[47] == 0)&&(Address(hash) == _address));
}
default:
return false;
}
}
const uint8_t *Identity::hash() const
{
uint8_t *const hash = const_cast<uint8_t *>(reinterpret_cast<const uint8_t *>(_hash));
switch(_type) {
default:
memset(hash,0,48);
break;
case C25519:
if (_hash[0] == 0)
SHA384(hash,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
break;
case P384: case P384:
if (_hash[0] == 0) { return ((_hash[47] == 0)&&(Address(_hash.data()) == _address));
SHA384(hash,&_pub,sizeof(_pub));
AES c(hash);
std::sort(hash,hash + 48);
c.encrypt(hash,hash);
c.encrypt(hash + 16,hash + 16);
c.encrypt(hash + 32,hash + 32);
SHA384(hash,hash,48);
} }
break; return false;
}
return hash;
} }
void Identity::hashWithPrivate(uint8_t h[48]) const void Identity::hashWithPrivate(uint8_t h[48]) const
{ {
if (_hasPrivate) {
switch (_type) { switch (_type) {
case C25519: SHA384(h,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN,_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN); break;
case P384: SHA384(h,&_pub,sizeof(_pub),&_priv,sizeof(_priv)); break; case C25519:
default: memset(h,0,48); SHA384(h,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN,_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN);
break;
case P384:
SHA384(h,&_pub,sizeof(_pub),&_priv,sizeof(_priv));
break;
} }
return;
}
memset(h,0,48);
} }
unsigned int Identity::sign(const void *data,unsigned int len,void *sig,unsigned int siglen) const unsigned int Identity::sign(const void *data,unsigned int len,void *sig,unsigned int siglen) const
@ -213,9 +188,11 @@ unsigned int Identity::sign(const void *data,unsigned int len,void *sig,unsigned
case P384: case P384:
if (siglen >= ZT_ECC384_SIGNATURE_SIZE) { if (siglen >= ZT_ECC384_SIGNATURE_SIZE) {
// When signing with P-384 we also include the C25519 public key in the hash. // For P384 we sign SHA384(data | public keys) for added defense against any attack
// that attempted to decouple the two keys in some way. Otherwise this has no impact
// on the security of the signature (unless SHA384 had some serious flaw).
uint8_t h[48]; uint8_t h[48];
SHA384(h,data,len,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN); SHA384(h,data,len,&_pub,ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE);
ECC384ECDSASign(_priv.p384,h,(uint8_t *)sig); ECC384ECDSASign(_priv.p384,h,(uint8_t *)sig);
return ZT_ECC384_SIGNATURE_SIZE; return ZT_ECC384_SIGNATURE_SIZE;
} }
@ -235,7 +212,7 @@ bool Identity::verify(const void *data,unsigned int len,const void *sig,unsigned
case P384: case P384:
if (siglen == ZT_ECC384_SIGNATURE_SIZE) { if (siglen == ZT_ECC384_SIGNATURE_SIZE) {
uint8_t h[48]; uint8_t h[48];
SHA384(h,data,len,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN); SHA384(h,data,len,&_pub,ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE);
return ECC384ECDSAVerify(_pub.p384,h,(const uint8_t *)sig); return ECC384ECDSAVerify(_pub.p384,h,(const uint8_t *)sig);
} }
break; break;
@ -263,6 +240,11 @@ bool Identity::agree(const Identity &id,uint8_t key[ZT_PEER_SECRET_KEY_LENGTH])
} else if (_type == P384) { } else if (_type == P384) {
if (id._type == P384) { if (id._type == P384) {
// For another P384 identity we execute DH agreement with BOTH keys and then
// hash the results together. For those (cough FIPS cough) who only consider
// P384 to be kosher, the C25519 secret can be considered a "salt"
// or something. For those who don't trust P384 this means the privacy of
// your traffic is also protected by C25519.
C25519::agree(_priv.c25519,id._pub.c25519,rawkey); C25519::agree(_priv.c25519,id._pub.c25519,rawkey);
ECC384ECDH(id._pub.p384,_priv.p384,rawkey + ZT_C25519_SHARED_KEY_LEN); ECC384ECDH(id._pub.p384,_priv.p384,rawkey + ZT_C25519_SHARED_KEY_LEN);
SHA384(h,rawkey,ZT_C25519_SHARED_KEY_LEN + ZT_ECC384_SHARED_SECRET_SIZE); SHA384(h,rawkey,ZT_C25519_SHARED_KEY_LEN + ZT_ECC384_SHARED_SECRET_SIZE);
@ -283,13 +265,14 @@ bool Identity::agree(const Identity &id,uint8_t key[ZT_PEER_SECRET_KEY_LENGTH])
char *Identity::toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]) const char *Identity::toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]) const
{ {
char *p = buf;
_address.toString(p);
p += 10;
*(p++) = ':';
switch(_type) { switch(_type) {
case C25519: { case C25519: {
char *p = buf;
Utils::hex10(_address.toInt(),p);
p += 10;
*(p++) = ':';
*(p++) = '0'; *(p++) = '0';
*(p++) = ':'; *(p++) = ':';
Utils::hex(_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN,p); Utils::hex(_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN,p);
@ -304,10 +287,6 @@ char *Identity::toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_
} }
case P384: { case P384: {
char *p = buf;
Utils::hex10(_address.toInt(),p);
p += 10;
*(p++) = ':';
*(p++) = '1'; *(p++) = '1';
*(p++) = ':'; *(p++) = ':';
int el = Utils::b32e((const uint8_t *)(&_pub),sizeof(_pub),p,(int)(ZT_IDENTITY_STRING_BUFFER_LENGTH - (uintptr_t)(p - buf))); int el = Utils::b32e((const uint8_t *)(&_pub),sizeof(_pub),p,(int)(ZT_IDENTITY_STRING_BUFFER_LENGTH - (uintptr_t)(p - buf)));
@ -324,13 +303,14 @@ char *Identity::toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_
} }
} }
return nullptr; return nullptr;
} }
bool Identity::fromString(const char *str) bool Identity::fromString(const char *str)
{ {
_hash.zero();
_hasPrivate = false; _hasPrivate = false;
_hash[0] = 0; // force hash recompute
if (!str) { if (!str) {
_address.zero(); _address.zero();
@ -421,6 +401,8 @@ bool Identity::fromString(const char *str)
return false; return false;
} }
_computeHash();
return true; return true;
} }
@ -430,50 +412,67 @@ int Identity::marshal(uint8_t data[ZT_IDENTITY_MARSHAL_SIZE_MAX],const bool incl
switch(_type) { switch(_type) {
case C25519: case C25519:
data[ZT_ADDRESS_LENGTH] = (uint8_t)C25519; data[ZT_ADDRESS_LENGTH] = (uint8_t)C25519;
memcpy(data + ZT_ADDRESS_LENGTH + 1,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN); memcpy(data + ZT_ADDRESS_LENGTH + 1,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
if ((includePrivate)&&(_hasPrivate)) { if ((includePrivate)&&(_hasPrivate)) {
data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN] = ZT_C25519_PRIVATE_KEY_LEN; data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN] = ZT_C25519_PRIVATE_KEY_LEN;
memcpy(data + ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1,_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN); memcpy(data + ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1,_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN);
return (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1 + ZT_C25519_PRIVATE_KEY_LEN); return ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1 + ZT_C25519_PRIVATE_KEY_LEN;
} } else {
data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN] = 0; data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN] = 0;
return ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1; return ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1;
}
case P384: case P384:
data[ZT_ADDRESS_LENGTH] = (uint8_t)P384; data[ZT_ADDRESS_LENGTH] = (uint8_t)P384;
memcpy(data + 1 + ZT_ADDRESS_LENGTH,&_pub,ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE);
memcpy(data + ZT_ADDRESS_LENGTH + 1,&_pub,ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE);
if ((includePrivate)&&(_hasPrivate)) { if ((includePrivate)&&(_hasPrivate)) {
data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE] = ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE; data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE] = ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE;
memcpy(data + ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1,&_priv,ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE); memcpy(data + ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1,&_priv,ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE);
return ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE; return ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE;
} } else {
data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE] = 0; data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE] = 0;
return ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1; return ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1;
} }
}
return -1; return -1;
} }
int Identity::unmarshal(const uint8_t *data,const int len) noexcept int Identity::unmarshal(const uint8_t *data,const int len) noexcept
{ {
_hash.zero();
_hasPrivate = false;
if (len < (ZT_ADDRESS_LENGTH + 1)) if (len < (ZT_ADDRESS_LENGTH + 1))
return -1; return -1;
_hash[0] = 0; // force hash recompute
unsigned int privlen; unsigned int privlen;
switch((_type = (Type)data[ZT_ADDRESS_LENGTH])) { switch((_type = (Type)data[ZT_ADDRESS_LENGTH])) {
case C25519: case C25519:
if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1)) if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1))
return -1; return -1;
memcpy(_pub.c25519,data + ZT_ADDRESS_LENGTH + 1,ZT_C25519_PUBLIC_KEY_LEN); memcpy(_pub.c25519,data + ZT_ADDRESS_LENGTH + 1,ZT_C25519_PUBLIC_KEY_LEN);
privlen = data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN]; privlen = data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN];
if (privlen == ZT_C25519_PRIVATE_KEY_LEN) { if (privlen == ZT_C25519_PRIVATE_KEY_LEN) {
if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1 + ZT_C25519_PRIVATE_KEY_LEN)) if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1 + ZT_C25519_PRIVATE_KEY_LEN))
return -1; return -1;
_hasPrivate = true; _hasPrivate = true;
memcpy(_priv.c25519,data + ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1,ZT_C25519_PRIVATE_KEY_LEN); memcpy(_priv.c25519,data + ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1,ZT_C25519_PRIVATE_KEY_LEN);
_computeHash();
return ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1 + ZT_C25519_PRIVATE_KEY_LEN; return ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1 + ZT_C25519_PRIVATE_KEY_LEN;
} else if (privlen == 0) { } else if (privlen == 0) {
_hasPrivate = false; _hasPrivate = false;
_computeHash();
return ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1; return ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1;
} }
break; break;
@ -481,26 +480,61 @@ int Identity::unmarshal(const uint8_t *data,const int len) noexcept
case P384: case P384:
if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1)) if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1))
return -1; return -1;
memcpy(&_pub,data + ZT_ADDRESS_LENGTH + 1,ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE); memcpy(&_pub,data + ZT_ADDRESS_LENGTH + 1,ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE);
privlen = data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE]; privlen = data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE];
if (privlen == ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE) { if (privlen == ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE) {
if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE)) if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE))
return -1; return -1;
_hasPrivate = true; _hasPrivate = true;
memcpy(&_priv,data + ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1,ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE); memcpy(&_priv,data + ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1,ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE);
_computeHash();
if (!this->locallyValidate()) // for P384 we do this always if (!this->locallyValidate()) // for P384 we do this always
return -1; return -1;
return ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE; return ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE;
} else if (privlen == 0) { } else if (privlen == 0) {
_hasPrivate = false; _hasPrivate = false;
_computeHash();
if (!this->locallyValidate()) // for P384 we do this always
return -1;
return ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1; return ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1;
} }
break; break;
} }
return -1; return -1;
} }
void Identity::_computeHash()
{
switch(_type) {
default:
_hash.zero();
break;
case C25519:
SHA384(_hash.data(),_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
break;
case P384:
if (!_hash) {
uint8_t *const h = _hash.data();
SHA384(h,&_pub,sizeof(_pub));
AES c(h);
c.encrypt(h,h);
c.encrypt(h + 16,h + 16);
c.encrypt(h + 32,h + 32);
}
break;
}
}
} // namespace ZeroTier } // namespace ZeroTier
extern "C" { extern "C" {

View file

@ -24,6 +24,7 @@
#include "SHA512.hpp" #include "SHA512.hpp"
#include "ECC384.hpp" #include "ECC384.hpp"
#include "TriviallyCopyable.hpp" #include "TriviallyCopyable.hpp"
#include "H.hpp"
#define ZT_IDENTITY_STRING_BUFFER_LENGTH 1024 #define ZT_IDENTITY_STRING_BUFFER_LENGTH 1024
#define ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE (ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE) #define ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE (ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE)
@ -96,7 +97,11 @@ public:
bool generate(Type t); bool generate(Type t);
/** /**
* Check the validity of this identity's pairing of key to address * Check the validity of this identity's address
*
* For type 0 identities this is slightly time consuming. For type 1 identities it's
* instantaneous. It should be done when a new identity is accepted for the very first
* time.
* *
* @return True if validation check passes * @return True if validation check passes
*/ */
@ -108,7 +113,7 @@ public:
ZT_ALWAYS_INLINE bool hasPrivate() const noexcept { return _hasPrivate; } ZT_ALWAYS_INLINE bool hasPrivate() const noexcept { return _hasPrivate; }
/** /**
* This gets (computing if needed) a hash of this identity's public key(s). * Get hash of this identity's public key(s)
* *
* The hash returned by this function differs by identity type. For C25519 (type 0) * The hash returned by this function differs by identity type. For C25519 (type 0)
* identities this returns a simple SHA384 of the public key, which is NOT the same * identities this returns a simple SHA384 of the public key, which is NOT the same
@ -117,12 +122,18 @@ public:
* and address computation. This difference is because the v0 hash is expensive while * and address computation. This difference is because the v0 hash is expensive while
* the v1 hash is fast. * the v1 hash is fast.
* *
* @return 384-bit/48-byte hash (pointer remains valid as long as Identity object exists) * While addresses can technically collide (though this is rare and hard to create),
* the full hash of an identity's keys is unique to within cryptographic strength
* bounds of the keys themselves.
*
* @return 384-bit/48-byte hash
*/ */
const uint8_t *hash() const; ZT_ALWAYS_INLINE const H<384> &hash() const noexcept { return _hash; }
/** /**
* Compute a hash of this identity's public and private keys * Compute a hash of this identity's public and private keys.
*
* If there is no private key or the identity is NIL the buffer is filled with zero.
* *
* @param h Buffer to store SHA384 hash * @param h Buffer to store SHA384 hash
*/ */
@ -234,10 +245,10 @@ public:
int unmarshal(const uint8_t *data,int len) noexcept; int unmarshal(const uint8_t *data,int len) noexcept;
private: private:
void _computeHash();
Address _address; Address _address;
uint64_t _hash[6]; // hash of public key memo-ized for performance, recalculated when _hash[0] == 0 H<384> _hash;
Type _type; // _type determines which fields in _priv and _pub are used
bool _hasPrivate;
ZT_PACKED_STRUCT(struct { // don't re-order these ZT_PACKED_STRUCT(struct { // don't re-order these
uint8_t c25519[ZT_C25519_PRIVATE_KEY_LEN]; uint8_t c25519[ZT_C25519_PRIVATE_KEY_LEN];
uint8_t p384[ZT_ECC384_PRIVATE_KEY_SIZE]; uint8_t p384[ZT_ECC384_PRIVATE_KEY_SIZE];
@ -246,6 +257,8 @@ private:
uint8_t c25519[ZT_C25519_PUBLIC_KEY_LEN]; // Curve25519 and Ed25519 public keys uint8_t c25519[ZT_C25519_PUBLIC_KEY_LEN]; // Curve25519 and Ed25519 public keys
uint8_t p384[ZT_ECC384_PUBLIC_KEY_SIZE]; // NIST P-384 public key uint8_t p384[ZT_ECC384_PUBLIC_KEY_SIZE]; // NIST P-384 public key
}) _pub; }) _pub;
Type _type; // _type determines which fields in _priv and _pub are used
bool _hasPrivate;
}; };
} // namespace ZeroTier } // namespace ZeroTier

View file

@ -60,10 +60,10 @@ void Membership::pushCredentials(const RuntimeEnvironment *RR,void *tPtr,const i
sendCom = false; sendCom = false;
outp->wO(outl,nconf.com); outp->wO(outl,nconf.com);
} }
outp->wI(outl,(uint8_t)0); outp->wI8(outl,0);
if ((outl + ZT_CAPABILITY_MARSHAL_SIZE_MAX + 2) < ZT_PROTO_MAX_PACKET_LENGTH) { if ((outl + ZT_CAPABILITY_MARSHAL_SIZE_MAX + 2) < ZT_PROTO_MAX_PACKET_LENGTH) {
void *const capCountAt = outp->b + outl; void *const capCountAt = outp->unsafeData + outl;
outl += 2; outl += 2;
unsigned int capCount = 0; unsigned int capCount = 0;
while (capPtr < nconf.capabilityCount) { while (capPtr < nconf.capabilityCount) {
@ -75,7 +75,7 @@ void Membership::pushCredentials(const RuntimeEnvironment *RR,void *tPtr,const i
Utils::storeBigEndian(capCountAt,(uint16_t)capCount); Utils::storeBigEndian(capCountAt,(uint16_t)capCount);
if ((outl + ZT_TAG_MARSHAL_SIZE_MAX + 4) < ZT_PROTO_MAX_PACKET_LENGTH) { if ((outl + ZT_TAG_MARSHAL_SIZE_MAX + 4) < ZT_PROTO_MAX_PACKET_LENGTH) {
void *const tagCountAt = outp->b + outl; void *const tagCountAt = outp->unsafeData + outl;
outl += 2; outl += 2;
unsigned int tagCount = 0; unsigned int tagCount = 0;
while (tagPtr < nconf.tagCount) { while (tagPtr < nconf.tagCount) {
@ -86,10 +86,10 @@ void Membership::pushCredentials(const RuntimeEnvironment *RR,void *tPtr,const i
} }
Utils::storeBigEndian(tagCountAt,(uint16_t)tagCount); Utils::storeBigEndian(tagCountAt,(uint16_t)tagCount);
outp->wI(outl,(uint16_t)0); // no revocations sent here as these propagate differently outp->wI16(outl,0); // no revocations sent here as these propagate differently
if ((outl + ZT_CERTIFICATEOFOWNERSHIP_MARSHAL_SIZE_MAX + 2) < ZT_PROTO_MAX_PACKET_LENGTH) { if ((outl + ZT_CERTIFICATEOFOWNERSHIP_MARSHAL_SIZE_MAX + 2) < ZT_PROTO_MAX_PACKET_LENGTH) {
void *const cooCountAt = outp->b + outl; void *const cooCountAt = outp->unsafeData + outl;
outl += 2; outl += 2;
unsigned int cooCount = 0; unsigned int cooCount = 0;
while (cooPtr < nconf.certificateOfOwnershipCount) { while (cooPtr < nconf.certificateOfOwnershipCount) {
@ -102,15 +102,14 @@ void Membership::pushCredentials(const RuntimeEnvironment *RR,void *tPtr,const i
complete = true; complete = true;
} else { } else {
outp->wI(outl,(uint16_t)0); outp->wI16(outl,0);
} }
} else { } else {
outp->wI(outl,(uint16_t)0); outp->wI32(outl,0);
outp->wI(outl,(uint16_t)0); outp->wI16(outl,0); // three zero 16-bit integers
outp->wI(outl,(uint16_t)0);
} }
} else { } else {
outp->wI(outl,(uint64_t)0); // four zero 16-bit integers outp->wI64(outl,0); // four zero 16-bit integers
} }
if (outl > sizeof(Protocol::Header)) { if (outl > sizeof(Protocol::Header)) {

View file

@ -14,13 +14,6 @@
#ifndef ZT_NETWORK_HPP #ifndef ZT_NETWORK_HPP
#define ZT_NETWORK_HPP #define ZT_NETWORK_HPP
#include <cstdint>
#include <string>
#include <map>
#include <vector>
#include <algorithm>
#include <stdexcept>
#include "Constants.hpp" #include "Constants.hpp"
#include "Hashtable.hpp" #include "Hashtable.hpp"
#include "Address.hpp" #include "Address.hpp"
@ -34,6 +27,12 @@
#include "NetworkConfig.hpp" #include "NetworkConfig.hpp"
#include "CertificateOfMembership.hpp" #include "CertificateOfMembership.hpp"
#include <cstdint>
#include <string>
#include <map>
#include <vector>
#include <algorithm>
#define ZT_NETWORK_MAX_INCOMING_UPDATES 3 #define ZT_NETWORK_MAX_INCOMING_UPDATES 3
namespace ZeroTier { namespace ZeroTier {

View file

@ -122,7 +122,7 @@ void Peer::received(
RR->t->learnedNewPath(tPtr,0x582fabdd,packetId,_id,path->address(),old); RR->t->learnedNewPath(tPtr,0x582fabdd,packetId,_id,path->address(),old);
} else { } else {
if (RR->node->shouldUsePathForZeroTierTraffic(tPtr,_id,path->localSocket(),path->address())) { if (RR->node->shouldUsePathForZeroTierTraffic(tPtr,_id,path->localSocket(),path->address())) {
RR->t->tryingNewPath(tPtr,0xb7747ddd,_id,path->address(),path->address(),packetId,(uint8_t)verb,_id.address(),_id.hash(),ZT_TRACE_TRYING_NEW_PATH_REASON_PACKET_RECEIVED_FROM_UNKNOWN_PATH); RR->t->tryingNewPath(tPtr,0xb7747ddd,_id,path->address(),path->address(),packetId,(uint8_t)verb,_id.address(),_id.hash().data(),ZT_TRACE_TRYING_NEW_PATH_REASON_PACKET_RECEIVED_FROM_UNKNOWN_PATH);
sendHELLO(tPtr,path->localSocket(),path->address(),now); sendHELLO(tPtr,path->localSocket(),path->address(),now);
path->sent(now); path->sent(now);
} }
@ -241,7 +241,7 @@ void Peer::sendNOP(void *tPtr,const int64_t localSocket,const InetAddress &atAdd
ph.flags = 0; ph.flags = 0;
ph.verb = Protocol::VERB_NOP; ph.verb = Protocol::VERB_NOP;
Protocol::armor(outp,sizeof(Protocol::Header),_key,this->cipher()); Protocol::armor(outp,sizeof(Protocol::Header),_key,this->cipher());
RR->node->putPacket(tPtr,localSocket,atAddress,outp.b,sizeof(Protocol::Header)); RR->node->putPacket(tPtr,localSocket,atAddress,outp.unsafeData,sizeof(Protocol::Header));
} }
void Peer::ping(void *tPtr,int64_t now,const bool pingAllAddressTypes) void Peer::ping(void *tPtr,int64_t now,const bool pingAllAddressTypes)
@ -337,24 +337,26 @@ void Peer::getAllPaths(std::vector< SharedPtr<Path> > &paths)
void Peer::save(void *tPtr) const void Peer::save(void *tPtr) const
{ {
uint8_t *const buf = (uint8_t *)malloc(ZT_PEER_MARSHAL_SIZE_MAX); uint8_t *const buf = (uint8_t *)malloc(8 + ZT_PEER_MARSHAL_SIZE_MAX);
if (!buf) return; if (!buf) return;
Utils::storeBigEndian<uint64_t>(buf,(uint64_t)RR->node->now());
_lock.rlock(); _lock.rlock();
const int len = marshal(buf); const int len = marshal(buf + 8);
_lock.runlock(); _lock.runlock();
if (len > 0) { if (len > 0) {
uint64_t id[2]; uint64_t id[2];
id[0] = _id.address().toInt(); id[0] = _id.address().toInt();
id[1] = 0; id[1] = 0;
RR->node->stateObjectPut(tPtr,ZT_STATE_OBJECT_PEER,id,buf,(unsigned int)len); RR->node->stateObjectPut(tPtr,ZT_STATE_OBJECT_PEER,id,buf,(unsigned int)len + 8);
} }
free(buf); free(buf);
} }
void Peer::contact(void *tPtr,const Endpoint &ep,const int64_t now,const bool behindSymmetric,const bool bfg1024) void Peer::contact(void *tPtr,const Endpoint &ep,const int64_t now,const bool bfg1024)
{ {
static uint8_t junk = 0; static uint8_t junk = 0;
@ -376,7 +378,7 @@ void Peer::contact(void *tPtr,const Endpoint &ep,const int64_t now,const bool be
// If the peer indicates that they may be behind a symmetric NAT and there are no // If the peer indicates that they may be behind a symmetric NAT and there are no
// living direct paths, try a few more aggressive things. // living direct paths, try a few more aggressive things.
if ((behindSymmetric) && (phyAddr.ss_family == AF_INET) && (!direct(now))) { if ((phyAddr.ss_family == AF_INET) && (!direct(now))) {
unsigned int port = phyAddr.port(); unsigned int port = phyAddr.port();
if ((bfg1024)&&(port < 1024)&&(RR->node->natMustDie())) { if ((bfg1024)&&(port < 1024)&&(RR->node->natMustDie())) {
// If the other side is using a low-numbered port and has elected to // If the other side is using a low-numbered port and has elected to

View file

@ -170,7 +170,11 @@ public:
/** /**
* @return Bootstrap address or NULL if none * @return Bootstrap address or NULL if none
*/ */
ZT_ALWAYS_INLINE const Endpoint &bootstrap() const noexcept { return _bootstrap; } ZT_ALWAYS_INLINE const Endpoint &bootstrap() const noexcept
{
RWMutex::RLock l(_lock);
return _bootstrap;
}
/** /**
* Set bootstrap endpoint * Set bootstrap endpoint
@ -179,9 +183,8 @@ public:
*/ */
ZT_ALWAYS_INLINE void setBootstrap(const Endpoint &ep) noexcept ZT_ALWAYS_INLINE void setBootstrap(const Endpoint &ep) noexcept
{ {
_lock.lock(); RWMutex::Lock l(_lock);
_bootstrap = ep; _bootstrap = ep;
_lock.unlock();
} }
/** /**
@ -315,17 +318,14 @@ public:
void save(void *tPtr) const; void save(void *tPtr) const;
/** /**
* Attempt to contact this peer at a physical address * Attempt to contact this peer at a physical address, subject to internal checks
*
* This checks rate limits, path usability, sometimes deploys advanced NAT-t techniques, etc.
* *
* @param tPtr External user pointer we pass around * @param tPtr External user pointer we pass around
* @param ep Endpoint to attempt to contact * @param ep Endpoint to attempt to contact
* @param now Current time * @param now Current time
* @param behindSymmetric This peer may be behind a symmetric NAT (only meaningful for IPv4)
* @param bfg1024 Use BFG1024 brute force symmetric NAT busting algorithm if applicable * @param bfg1024 Use BFG1024 brute force symmetric NAT busting algorithm if applicable
*/ */
void contact(void *tPtr,const Endpoint &ep,int64_t now,bool behindSymmetric,bool bfg1024); void contact(void *tPtr,const Endpoint &ep,int64_t now,bool bfg1024);
/** /**
* Called by Node when an alarm set by this peer goes off * Called by Node when an alarm set by this peer goes off
@ -348,20 +348,24 @@ private:
const RuntimeEnvironment *RR; const RuntimeEnvironment *RR;
volatile int64_t _lastReceive; // The last time various things happened, for rate limiting and periodic events.
volatile int64_t _lastWhoisRequestReceived; std::atomic<int64_t> _lastReceive;
volatile int64_t _lastEchoRequestReceived; std::atomic<int64_t> _lastWhoisRequestReceived;
volatile int64_t _lastPushDirectPathsReceived; std::atomic<int64_t> _lastEchoRequestReceived;
volatile int64_t _lastProbeReceived; std::atomic<int64_t> _lastPushDirectPathsReceived;
volatile int64_t _lastAttemptedP2PInit; std::atomic<int64_t> _lastProbeReceived;
volatile int64_t _lastTriedStaticPath; std::atomic<int64_t> _lastAttemptedP2PInit;
volatile int64_t _lastPrioritizedPaths; std::atomic<int64_t> _lastTriedStaticPath;
volatile int64_t _lastAttemptedAggressiveNATTraversal; std::atomic<int64_t> _lastPrioritizedPaths;
volatile unsigned int _latency; std::atomic<int64_t> _lastAttemptedAggressiveNATTraversal;
// Latency in milliseconds
std::atomic<unsigned int> _latency;
// For SharedPtr<>
std::atomic<int> __refCount; std::atomic<int> __refCount;
// Lock for non-volatile read/write fields // Read/write mutex for non-atomic non-const fields.
RWMutex _lock; RWMutex _lock;
// Number of paths current alive as of last _prioritizePaths // Number of paths current alive as of last _prioritizePaths

View file

@ -65,8 +65,8 @@ volatile uintptr_t _checkSizesIMeanIt = _checkSizes();
uint64_t createProbe(const Identity &sender,const Identity &recipient,const uint8_t key[ZT_PEER_SECRET_KEY_LENGTH]) noexcept uint64_t createProbe(const Identity &sender,const Identity &recipient,const uint8_t key[ZT_PEER_SECRET_KEY_LENGTH]) noexcept
{ {
uint8_t tmp[ZT_IDENTITY_HASH_SIZE + ZT_IDENTITY_HASH_SIZE]; uint8_t tmp[ZT_IDENTITY_HASH_SIZE + ZT_IDENTITY_HASH_SIZE];
memcpy(tmp,sender.hash(),ZT_IDENTITY_HASH_SIZE); memcpy(tmp,sender.hash().data(),ZT_IDENTITY_HASH_SIZE);
memcpy(tmp + ZT_IDENTITY_HASH_SIZE,recipient.hash(),ZT_IDENTITY_HASH_SIZE); memcpy(tmp + ZT_IDENTITY_HASH_SIZE,recipient.hash().data(),ZT_IDENTITY_HASH_SIZE);
uint64_t hash[6]; uint64_t hash[6];
SHA384(hash,tmp,sizeof(tmp),key,ZT_PEER_SECRET_KEY_LENGTH); SHA384(hash,tmp,sizeof(tmp),key,ZT_PEER_SECRET_KEY_LENGTH);
return hash[0]; return hash[0];
@ -98,7 +98,7 @@ void armor(Buf &pkt,int packetSize,const uint8_t key[ZT_PEER_SECRET_KEY_LENGTH],
// only difference here is that we don't encrypt the payload // only difference here is that we don't encrypt the payload
uint64_t mac[2]; uint64_t mac[2];
poly1305(mac,pkt.b + ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,packetSize - ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,macKey); poly1305(mac,pkt.unsafeData + ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,packetSize - ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,macKey);
ph.mac = mac[0]; ph.mac = mac[0];
} break; } break;
@ -111,10 +111,10 @@ void armor(Buf &pkt,int packetSize,const uint8_t key[ZT_PEER_SECRET_KEY_LENGTH],
s20.crypt12(Utils::ZERO256,macKey,ZT_POLY1305_KEY_LEN); s20.crypt12(Utils::ZERO256,macKey,ZT_POLY1305_KEY_LEN);
const unsigned int encLen = packetSize - ZT_PROTO_PACKET_ENCRYPTED_SECTION_START; const unsigned int encLen = packetSize - ZT_PROTO_PACKET_ENCRYPTED_SECTION_START;
s20.crypt12(pkt.b + ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,pkt.b + ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,encLen); s20.crypt12(pkt.unsafeData + ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,pkt.unsafeData + ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,encLen);
uint64_t mac[2]; uint64_t mac[2];
poly1305(mac,pkt.b + ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,encLen,macKey); poly1305(mac,pkt.unsafeData + ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,encLen,macKey);
ph.mac = mac[0]; ph.mac = mac[0];
} break; } break;
@ -132,9 +132,9 @@ int compress(SharedPtr<Buf> &pkt,int packetSize) noexcept
if (!pkt2) return packetSize; if (!pkt2) return packetSize;
const int uncompressedLen = packetSize - ZT_PROTO_PACKET_PAYLOAD_START; const int uncompressedLen = packetSize - ZT_PROTO_PACKET_PAYLOAD_START;
const int compressedLen = LZ4_compress_fast(reinterpret_cast<const char *>(pkt->b + ZT_PROTO_PACKET_PAYLOAD_START),reinterpret_cast<char *>(pkt2->b + ZT_PROTO_PACKET_PAYLOAD_START),uncompressedLen,ZT_BUF_MEM_SIZE - ZT_PROTO_PACKET_PAYLOAD_START); const int compressedLen = LZ4_compress_fast(reinterpret_cast<const char *>(pkt->unsafeData + ZT_PROTO_PACKET_PAYLOAD_START),reinterpret_cast<char *>(pkt2->unsafeData + ZT_PROTO_PACKET_PAYLOAD_START),uncompressedLen,ZT_BUF_MEM_SIZE - ZT_PROTO_PACKET_PAYLOAD_START);
if ((compressedLen > 0)&&(compressedLen < uncompressedLen)) { if ((compressedLen > 0)&&(compressedLen < uncompressedLen)) {
memcpy(pkt2->b,pkt->b,ZT_PROTO_PACKET_PAYLOAD_START); memcpy(pkt2->unsafeData,pkt->unsafeData,ZT_PROTO_PACKET_PAYLOAD_START);
pkt.swap(pkt2); pkt.swap(pkt2);
pkt->as<Protocol::Header>().verb |= ZT_PROTO_VERB_FLAG_COMPRESSED; pkt->as<Protocol::Header>().verb |= ZT_PROTO_VERB_FLAG_COMPRESSED;
return compressedLen + ZT_PROTO_PACKET_PAYLOAD_START; return compressedLen + ZT_PROTO_PACKET_PAYLOAD_START;

View file

@ -640,13 +640,17 @@ enum Verb
* <[...] paths> * <[...] paths>
* *
* Path record format: * Path record format:
* <[1] 8-bit path flags (always 0, currently unused)> * <[1] 8-bit path flags>
* <[2] length of extended path characteristics or 0 for none> * <[2] length of extended path characteristics or 0 for none>
* <[...] extended path characteristics> * <[...] extended path characteristics>
* <[1] address type> * <[1] address type>
* <[1] address length in bytes> * <[1] address record length in bytes>
* <[...] address> * <[...] address>
* *
* Path flags:
* 0x01 - Sender is likely behind a symmetric NAT
* 0x02 - Use BFG1024 algorithm for symmetric NAT-t if conditions met
*
* The receiver may, upon receiving a push, attempt to establish a * The receiver may, upon receiving a push, attempt to establish a
* direct link to one or more of the indicated addresses. It is the * direct link to one or more of the indicated addresses. It is the
* responsibility of the sender to limit which peers it pushes direct * responsibility of the sender to limit which peers it pushes direct
@ -965,21 +969,21 @@ ZT_PACKED_STRUCT(struct UNSUPPORTED_OPERATION__NETWORK_CONFIG_REQUEST
* @param packetSize Packet's actual size in bytes * @param packetSize Packet's actual size in bytes
* @return Packet ID or 0 if packet size is less than 8 * @return Packet ID or 0 if packet size is less than 8
*/ */
ZT_ALWAYS_INLINE uint64_t packetId(const Buf &pkt,const unsigned int packetSize) noexcept { return (packetSize >= 8) ? Utils::loadBigEndian<uint64_t>(pkt.b) : 0ULL; } ZT_ALWAYS_INLINE uint64_t packetId(const Buf &pkt,const unsigned int packetSize) noexcept { return (packetSize >= 8) ? Utils::loadBigEndian<uint64_t>(pkt.unsafeData) : 0ULL; }
/** /**
* @param Packet to extract hops from * @param Packet to extract hops from
* @param packetSize Packet's actual size in bytes * @param packetSize Packet's actual size in bytes
* @return 3-bit hops field embedded in packet flags field * @return 3-bit hops field embedded in packet flags field
*/ */
ZT_ALWAYS_INLINE uint8_t packetHops(const Buf &pkt,const unsigned int packetSize) noexcept { return (packetSize >= ZT_PROTO_PACKET_FLAGS_INDEX) ? (pkt.b[ZT_PROTO_PACKET_FLAGS_INDEX] & ZT_PROTO_FLAG_FIELD_HOPS_MASK) : 0; } ZT_ALWAYS_INLINE uint8_t packetHops(const Buf &pkt,const unsigned int packetSize) noexcept { return (packetSize >= ZT_PROTO_PACKET_FLAGS_INDEX) ? (pkt.unsafeData[ZT_PROTO_PACKET_FLAGS_INDEX] & ZT_PROTO_FLAG_FIELD_HOPS_MASK) : 0; }
/** /**
* @param Packet to extract cipher ID from * @param Packet to extract cipher ID from
* @param packetSize Packet's actual size in bytes * @param packetSize Packet's actual size in bytes
* @return 3-bit cipher field embedded in packet flags field * @return 3-bit cipher field embedded in packet flags field
*/ */
ZT_ALWAYS_INLINE uint8_t packetCipher(const Buf &pkt,const unsigned int packetSize) noexcept { return (packetSize >= ZT_PROTO_PACKET_FLAGS_INDEX) ? ((pkt.b[ZT_PROTO_PACKET_FLAGS_INDEX] >> 3U) & 0x07U) : 0; } ZT_ALWAYS_INLINE uint8_t packetCipher(const Buf &pkt,const unsigned int packetSize) noexcept { return (packetSize >= ZT_PROTO_PACKET_FLAGS_INDEX) ? ((pkt.unsafeData[ZT_PROTO_PACKET_FLAGS_INDEX] >> 3U) & 0x07U) : 0; }
/** /**
* @return 3-bit hops field embedded in packet flags field * @return 3-bit hops field embedded in packet flags field
@ -1009,14 +1013,14 @@ ZT_ALWAYS_INLINE void salsa2012DeriveKey(const uint8_t *const in,uint8_t *const
for(int i=0;i<18;++i) for(int i=0;i<18;++i)
out[i] = in[i] ^ packet.b[i]; out[i] = in[i] ^ packet.b[i];
#else #else
*reinterpret_cast<uint64_t *>(out) = *reinterpret_cast<const uint64_t *>(in) ^ *reinterpret_cast<const uint64_t *>(packet.b); *reinterpret_cast<uint64_t *>(out) = *reinterpret_cast<const uint64_t *>(in) ^ *reinterpret_cast<const uint64_t *>(packet.unsafeData);
*reinterpret_cast<uint64_t *>(out + 8) = *reinterpret_cast<const uint64_t *>(in + 8) ^ *reinterpret_cast<const uint64_t *>(packet.b + 8); *reinterpret_cast<uint64_t *>(out + 8) = *reinterpret_cast<const uint64_t *>(in + 8) ^ *reinterpret_cast<const uint64_t *>(packet.unsafeData + 8);
*reinterpret_cast<uint16_t *>(out + 16) = *reinterpret_cast<const uint16_t *>(in + 16) ^ *reinterpret_cast<const uint16_t *>(packet.b + 16); *reinterpret_cast<uint16_t *>(out + 16) = *reinterpret_cast<const uint16_t *>(in + 16) ^ *reinterpret_cast<const uint16_t *>(packet.unsafeData + 16);
#endif #endif
// Flags, but with hop count masked off. Hop count is altered by forwarding // Flags, but with hop count masked off. Hop count is altered by forwarding
// nodes and is the only field that is mutable by unauthenticated third parties. // nodes and is the only field that is mutable by unauthenticated third parties.
out[18] = in[18] ^ (packet.b[18] & 0xf8U); out[18] = in[18] ^ (packet.unsafeData[18] & 0xf8U);
// Raw packet size in bytes -- thus each packet size defines a new key space. // Raw packet size in bytes -- thus each packet size defines a new key space.
out[19] = in[19] ^ (uint8_t)packetSize; out[19] = in[19] ^ (uint8_t)packetSize;

View file

@ -38,8 +38,10 @@ Topology::Topology(const RuntimeEnvironment *renv,const Identity &myId,void *tPt
RR(renv), RR(renv),
_myIdentity(myId), _myIdentity(myId),
_numConfiguredPhysicalPaths(0), _numConfiguredPhysicalPaths(0),
_peers(128), _peers(256),
_paths(256) _peersByIncomingProbe(256),
_peersByIdentityHash(256),
_paths(1024)
{ {
uint64_t idtmp[2]; idtmp[0] = 0; idtmp[1] = 0; uint64_t idtmp[2]; idtmp[0] = 0; idtmp[1] = 0;
std::vector<uint8_t> data(RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_ROOTS,idtmp)); std::vector<uint8_t> data(RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_ROOTS,idtmp));
@ -65,6 +67,9 @@ Topology::Topology(const RuntimeEnvironment *renv,const Identity &myId,void *tPt
p->init(*r); p->init(*r);
} }
_rootPeers.push_back(p); _rootPeers.push_back(p);
_peers[p->address()] = p;
_peersByIncomingProbe[p->incomingProbe()] = p;
_peersByIdentityHash[p->identity().hash()] = p;
} }
} }
@ -83,11 +88,13 @@ SharedPtr<Peer> Topology::add(void *tPtr,const SharedPtr<Peer> &peer)
_loadCached(tPtr,peer->address(),hp); _loadCached(tPtr,peer->address(),hp);
if (hp) { if (hp) {
_peersByIncomingProbe[peer->incomingProbe()] = hp; _peersByIncomingProbe[peer->incomingProbe()] = hp;
_peersByIdentityHash[peer->identity().hash()] = hp;
return hp; return hp;
} }
hp = peer; hp = peer;
_peersByIncomingProbe[peer->incomingProbe()] = peer; _peersByIncomingProbe[peer->incomingProbe()] = peer;
_peersByIdentityHash[peer->identity().hash()] = peer;
return peer; return peer;
} }
@ -150,6 +157,8 @@ void Topology::addRoot(void *tPtr,const Identity &id,const InetAddress &bootstra
p->init(id); p->init(id);
if (bootstrap) if (bootstrap)
p->setBootstrap(Endpoint(bootstrap)); p->setBootstrap(Endpoint(bootstrap));
_peersByIncomingProbe[p->incomingProbe()] = p;
_peersByIdentityHash[p->identity().hash()] = p;
} }
_rootPeers.push_back(p); _rootPeers.push_back(p);
@ -204,6 +213,7 @@ void Topology::doPeriodicTasks(void *tPtr,const int64_t now)
if ( (!(*p)->alive(now)) && (_roots.count((*p)->identity()) == 0) ) { if ( (!(*p)->alive(now)) && (_roots.count((*p)->identity()) == 0) ) {
(*p)->save(tPtr); (*p)->save(tPtr);
_peersByIncomingProbe.erase((*p)->incomingProbe()); _peersByIncomingProbe.erase((*p)->incomingProbe());
_peersByIdentityHash.erase((*p)->identity().hash());
_peers.erase(*a); _peers.erase(*a);
} }
} }
@ -226,11 +236,8 @@ void Topology::saveAll(void *tPtr)
Hashtable< Address,SharedPtr<Peer> >::Iterator i(_peers); Hashtable< Address,SharedPtr<Peer> >::Iterator i(_peers);
Address *a = nullptr; Address *a = nullptr;
SharedPtr<Peer> *p = nullptr; SharedPtr<Peer> *p = nullptr;
while (i.next(a,p)) { while (i.next(a,p))
if ( (!(*p)->alive(RR->node->now())) && (_roots.count((*p)->identity()) == 0) ) { (*p)->save(tPtr);
(*p)->save((void *)0);
}
}
} }
void Topology::_loadCached(void *tPtr,const Address &zta,SharedPtr<Peer> &peer) void Topology::_loadCached(void *tPtr,const Address &zta,SharedPtr<Peer> &peer)
@ -240,19 +247,21 @@ void Topology::_loadCached(void *tPtr,const Address &zta,SharedPtr<Peer> &peer)
id[0] = zta.toInt(); id[0] = zta.toInt();
id[1] = 0; id[1] = 0;
std::vector<uint8_t> data(RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_PEER,id)); std::vector<uint8_t> data(RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_PEER,id));
if (!data.empty()) { if (data.size() > 8) {
const uint8_t *d = data.data(); const uint8_t *d = data.data();
int dl = (int)data.size(); int dl = (int)data.size();
for (;;) {
const int64_t ts = (int64_t)Utils::loadBigEndian<uint64_t>(d);
Peer *const p = new Peer(RR); Peer *const p = new Peer(RR);
int n = p->unmarshal(d,dl); int n = p->unmarshal(d + 8,dl - 8);
if (n > 0) { if (n < 0) {
// TODO: will eventually handle multiple peers delete p;
return;
}
if ((RR->node->now() - ts) < ZT_PEER_GLOBAL_TIMEOUT) {
// TODO: handle many peers, same address (?)
peer.set(p); peer.set(p);
return; return;
} else {
delete p;
}
} }
} }
} catch ( ... ) { } catch ( ... ) {

View file

@ -30,6 +30,7 @@
#include "Hashtable.hpp" #include "Hashtable.hpp"
#include "SharedPtr.hpp" #include "SharedPtr.hpp"
#include "ScopedPtr.hpp" #include "ScopedPtr.hpp"
#include "H.hpp"
namespace ZeroTier { namespace ZeroTier {
@ -66,25 +67,41 @@ public:
ZT_ALWAYS_INLINE SharedPtr<Peer> peer(void *tPtr,const Address &zta,const bool loadFromCached = true) ZT_ALWAYS_INLINE SharedPtr<Peer> peer(void *tPtr,const Address &zta,const bool loadFromCached = true)
{ {
{ {
RWMutex::RLock _l(_peers_l); RWMutex::RLock l(_peers_l);
const SharedPtr<Peer> *const ap = _peers.get(zta); const SharedPtr<Peer> *const ap = _peers.get(zta);
if (ap) if (ap)
return *ap; return *ap;
} }
{
SharedPtr<Peer> p; SharedPtr<Peer> p;
if (loadFromCached) { if (loadFromCached) {
_loadCached(tPtr,zta,p); _loadCached(tPtr,zta,p);
if (p) { if (p) {
RWMutex::Lock _l(_peers_l); RWMutex::Lock l(_peers_l);
SharedPtr<Peer> &hp = _peers[zta]; SharedPtr<Peer> &hp = _peers[zta];
if (!hp) if (hp)
return hp;
hp = p; hp = p;
} }
} }
return p; return p;
} }
}
/**
* Get a peer by its 384-bit identity public key hash
*
* @param hash Identity hash
* @return Peer or NULL if no peer is currently in memory for this hash (cache is not checked in this case)
*/
ZT_ALWAYS_INLINE SharedPtr<Peer> peerByHash(const H<384> &hash)
{
RWMutex::RLock _l(_peers_l);
const SharedPtr<Peer> *const ap = _peersByIdentityHash.get(hash);
if (ap)
return *ap;
return SharedPtr<Peer>();
}
/** /**
* Get a peer by its incoming short probe packet payload * Get a peer by its incoming short probe packet payload
@ -111,30 +128,22 @@ public:
ZT_ALWAYS_INLINE SharedPtr<Path> path(const int64_t l,const InetAddress &r) ZT_ALWAYS_INLINE SharedPtr<Path> path(const int64_t l,const InetAddress &r)
{ {
const uint64_t k = _pathHash(l,r); const uint64_t k = _pathHash(l,r);
{
_paths_l.rlock(); RWMutex::RLock lck(_paths_l);
SharedPtr<Path> p(_paths[k]); SharedPtr<Path> *const p = _paths.get(k);
_paths_l.runlock();
if (p) if (p)
return p; return *p;
}
_paths_l.lock(); {
SharedPtr<Path> p(new Path(l,r));
RWMutex::Lock lck(_paths_l);
SharedPtr<Path> &p2 = _paths[k]; SharedPtr<Path> &p2 = _paths[k];
if (p2) { if (p2)
p = p2; return p2;
} else {
try {
p.set(new Path(l,r));
} catch ( ... ) {
_paths_l.unlock();
return SharedPtr<Path>();
}
p2 = p; p2 = p;
}
_paths_l.unlock();
return p; return p;
} }
}
/** /**
* @return Current best root server * @return Current best root server
@ -173,10 +182,9 @@ public:
Hashtable< Address,SharedPtr<Peer> >::Iterator i(const_cast<Topology *>(this)->_peers); Hashtable< Address,SharedPtr<Peer> >::Iterator i(const_cast<Topology *>(this)->_peers);
Address *a = nullptr; Address *a = nullptr;
SharedPtr<Peer> *p = nullptr; SharedPtr<Peer> *p = nullptr;
while (i.next(a,p)) { while (i.next(a,p))
f(*((const SharedPtr<Peer> *)p)); f(*((const SharedPtr<Peer> *)p));
} }
}
/** /**
* Apply a function or function object to all peers * Apply a function or function object to all peers
@ -202,9 +210,8 @@ public:
Hashtable< Address,SharedPtr<Peer> >::Iterator i(const_cast<Topology *>(this)->_peers); Hashtable< Address,SharedPtr<Peer> >::Iterator i(const_cast<Topology *>(this)->_peers);
Address *a = nullptr; Address *a = nullptr;
SharedPtr<Peer> *p = nullptr; SharedPtr<Peer> *p = nullptr;
while (i.next(a,p)) { while (i.next(a,p))
f(*((const SharedPtr<Peer> *)p),std::binary_search(rootPeerPtrs.begin(),rootPeerPtrs.end(),(uintptr_t)p->ptr())); f(*((const SharedPtr<Peer> *)p),std::binary_search(rootPeerPtrs.begin(),rootPeerPtrs.end(),(uintptr_t)p->ptr()));
}
} catch ( ... ) {} // should not throw } catch ( ... ) {} // should not throw
} }
@ -221,10 +228,9 @@ public:
Hashtable< uint64_t,SharedPtr<Path> >::Iterator i(const_cast<Topology *>(this)->_paths); Hashtable< uint64_t,SharedPtr<Path> >::Iterator i(const_cast<Topology *>(this)->_paths);
uint64_t *k = nullptr; uint64_t *k = nullptr;
SharedPtr<Path> *p = nullptr; SharedPtr<Path> *p = nullptr;
while (i.next(k,p)) { while (i.next(k,p))
f(*((const SharedPtr<Path> *)p)); f(*((const SharedPtr<Path> *)p));
} }
}
/** /**
* @param allPeers vector to fill with all current peers * @param allPeers vector to fill with all current peers
@ -359,6 +365,7 @@ private:
Hashtable< Address,SharedPtr<Peer> > _peers; Hashtable< Address,SharedPtr<Peer> > _peers;
Hashtable< uint64_t,SharedPtr<Peer> > _peersByIncomingProbe; Hashtable< uint64_t,SharedPtr<Peer> > _peersByIncomingProbe;
Hashtable< H<384>,SharedPtr<Peer> > _peersByIdentityHash;
Hashtable< uint64_t,SharedPtr<Path> > _paths; Hashtable< uint64_t,SharedPtr<Path> > _paths;
std::set< Identity > _roots; // locked by _peers_l std::set< Identity > _roots; // locked by _peers_l
std::vector< SharedPtr<Peer> > _rootPeers; // locked by _peers_l std::vector< SharedPtr<Peer> > _rootPeers; // locked by _peers_l

View file

@ -118,15 +118,15 @@ void Trace::_tryingNewPath(
ev.evType = ZT_CONST_TO_BE_UINT16(ZT_TRACE_VL1_TRYING_NEW_PATH); ev.evType = ZT_CONST_TO_BE_UINT16(ZT_TRACE_VL1_TRYING_NEW_PATH);
ev.codeLocation = Utils::hton(codeLocation); ev.codeLocation = Utils::hton(codeLocation);
ev.address = Utils::hton(trying.address().toInt()); ev.address = Utils::hton(trying.address().toInt());
memcpy(ev.identityHash,trying.hash(),48); memcpy(ev.identityHash,trying.hash().data(),48);
physicalAddress.forTrace(ev.physicalAddress); physicalAddress.forTrace(ev.physicalAddress);
triggerAddress.forTrace(ev.triggerAddress); triggerAddress.forTrace(ev.triggerAddress);
ev.triggeringPacketId = triggeringPacketId; ev.triggeringPacketId = triggeringPacketId;
ev.triggeringPacketVerb = triggeringPacketVerb; ev.triggeringPacketVerb = triggeringPacketVerb;
ev.triggeredByAddress = Utils::hton(triggeredByAddress); ev.triggeredByAddress = Utils::hton(triggeredByAddress);
if (triggeredByIdentityHash) if (triggeredByIdentityHash)
memcpy(ev.triggeredByIdentityHash,triggeredByIdentityHash,48); memcpy(ev.triggeredByIdentityHash,triggeredByIdentityHash,ZT_IDENTITY_HASH_SIZE);
else memset(ev.triggeredByIdentityHash,0,48); else memset(ev.triggeredByIdentityHash,0,ZT_IDENTITY_HASH_SIZE);
ev.reason = (uint8_t)reason; ev.reason = (uint8_t)reason;
RR->node->postEvent(tPtr,ZT_EVENT_TRACE,&ev); RR->node->postEvent(tPtr,ZT_EVENT_TRACE,&ev);
} }
@ -145,7 +145,7 @@ void Trace::_learnedNewPath(
ev.codeLocation = Utils::hton(codeLocation); ev.codeLocation = Utils::hton(codeLocation);
ev.packetId = packetId; // packet IDs are kept in big-endian ev.packetId = packetId; // packet IDs are kept in big-endian
ev.address = Utils::hton(peerIdentity.address().toInt()); ev.address = Utils::hton(peerIdentity.address().toInt());
memcpy(ev.identityHash,peerIdentity.hash(),48); memcpy(ev.identityHash,peerIdentity.hash().data(),ZT_IDENTITY_HASH_SIZE);
physicalAddress.forTrace(ev.physicalAddress); physicalAddress.forTrace(ev.physicalAddress);
replaced.forTrace(ev.replaced); replaced.forTrace(ev.replaced);
@ -171,10 +171,10 @@ void Trace::_incomingPacketDropped(
ev.networkId = Utils::hton(networkId); ev.networkId = Utils::hton(networkId);
if (peerIdentity) { if (peerIdentity) {
ev.address = Utils::hton(peerIdentity.address().toInt()); ev.address = Utils::hton(peerIdentity.address().toInt());
memcpy(ev.identityHash,peerIdentity.hash(),48); memcpy(ev.identityHash,peerIdentity.hash().data(),ZT_IDENTITY_HASH_SIZE);
} else { } else {
ev.address = 0; ev.address = 0;
memset(ev.identityHash,0,48); memset(ev.identityHash,0,ZT_IDENTITY_HASH_SIZE);
} }
physicalAddress.forTrace(ev.physicalAddress); physicalAddress.forTrace(ev.physicalAddress);
ev.hops = hops; ev.hops = hops;

View file

@ -68,7 +68,7 @@ void VL1::onRemotePacket(void *const tPtr,const int64_t localSocket,const InetAd
// but we might as well avoid it. When the peer receives NOP on a path that hasn't been handshaked yet // but we might as well avoid it. When the peer receives NOP on a path that hasn't been handshaked yet
// it will send its own HELLO to which we will respond with a fully encrypted OK(HELLO). // it will send its own HELLO to which we will respond with a fully encrypted OK(HELLO).
if (len == ZT_PROTO_PROBE_LENGTH) { if (len == ZT_PROTO_PROBE_LENGTH) {
const SharedPtr<Peer> peer(RR->topology->peerByProbe(Utils::loadAsIsEndian<uint64_t>(data->b))); const SharedPtr<Peer> peer(RR->topology->peerByProbe(data->lI64(0)));
if ((peer)&&(peer->rateGateInboundProbe(now))) { if ((peer)&&(peer->rateGateInboundProbe(now))) {
peer->sendNOP(tPtr,path->localSocket(),path->address(),now); peer->sendNOP(tPtr,path->localSocket(),path->address(),now);
path->sent(now); path->sent(now);
@ -90,7 +90,7 @@ void VL1::onRemotePacket(void *const tPtr,const int64_t localSocket,const InetAd
// Destination address of packet (filled below) // Destination address of packet (filled below)
Address destination; Address destination;
if (data->b[ZT_PROTO_PACKET_FRAGMENT_INDICATOR_INDEX] == ZT_PROTO_PACKET_FRAGMENT_INDICATOR) { if (data->lI8(ZT_PROTO_PACKET_FRAGMENT_INDICATOR_INDEX) == ZT_PROTO_PACKET_FRAGMENT_INDICATOR) {
// Fragment ----------------------------------------------------------------------------------------------------- // Fragment -----------------------------------------------------------------------------------------------------
const Protocol::FragmentHeader &fragmentHeader = data->as<Protocol::FragmentHeader>(); const Protocol::FragmentHeader &fragmentHeader = data->as<Protocol::FragmentHeader>();
@ -234,7 +234,7 @@ void VL1::onRemotePacket(void *const tPtr,const int64_t localSocket,const InetAd
// Verify packet MAC. // Verify packet MAC.
uint64_t mac[2]; uint64_t mac[2];
poly1305(mac,pkt.b->b + ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,packetSize - ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,macKey); poly1305(mac,pkt.b->unsafeData + ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,packetSize - ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,macKey);
if (ph->mac != mac[0]) { if (ph->mac != mac[0]) {
RR->t->incomingPacketDropped(tPtr,0xcc89c812,ph->packetId,0,peer->identity(),path->address(),hops,Protocol::VERB_NOP,ZT_TRACE_PACKET_DROP_REASON_MAC_FAILED); RR->t->incomingPacketDropped(tPtr,0xcc89c812,ph->packetId,0,peer->identity(),path->address(),hops,Protocol::VERB_NOP,ZT_TRACE_PACKET_DROP_REASON_MAC_FAILED);
return; return;
@ -268,7 +268,7 @@ void VL1::onRemotePacket(void *const tPtr,const int64_t localSocket,const InetAd
for(i=0;i<prevOverflow;++i) { for(i=0;i<prevOverflow;++i) {
if (s->s >= s->e) if (s->s >= s->e)
goto next_slice; goto next_slice;
ps->b->b[ps->e++] = s->b->b[s->s++]; // move from head of current to end of previous ps->b->unsafeData[ps->e++] = s->b->unsafeData[s->s++]; // move from head of current to end of previous
} }
next_slice: ps = s++; next_slice: ps = s++;
} }
@ -277,18 +277,18 @@ void VL1::onRemotePacket(void *const tPtr,const int64_t localSocket,const InetAd
// Simultaneously decrypt and assemble packet into a contiguous buffer. // Simultaneously decrypt and assemble packet into a contiguous buffer.
// Since we moved data around above all slices will have sizes that are // Since we moved data around above all slices will have sizes that are
// multiples of 64. // multiples of 64.
memcpy(pkt.b->b,ph,sizeof(Protocol::Header)); memcpy(pkt.b->unsafeData,ph,sizeof(Protocol::Header));
pkt.e = sizeof(Protocol::Header); pkt.e = sizeof(Protocol::Header);
for(FCV<Buf::Slice,ZT_MAX_PACKET_FRAGMENTS>::iterator s(pktv.begin());s!=pktv.end();++s) { for(FCV<Buf::Slice,ZT_MAX_PACKET_FRAGMENTS>::iterator s(pktv.begin());s!=pktv.end();++s) {
const unsigned int sliceSize = s->e - s->s; const unsigned int sliceSize = s->e - s->s;
s20.crypt12(s->b->b + s->s,pkt.b->b + pkt.e,sliceSize); s20.crypt12(s->b->unsafeData + s->s,pkt.b->unsafeData + pkt.e,sliceSize);
pkt.e += sliceSize; pkt.e += sliceSize;
} }
ph = &(pkt.b->as<Protocol::Header>()); ph = &(pkt.b->as<Protocol::Header>());
// Verify packet MAC. // Verify packet MAC.
uint64_t mac[2]; uint64_t mac[2];
poly1305(mac,pkt.b->b + ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,packetSize - ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,macKey); poly1305(mac,pkt.b->unsafeData + ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,packetSize - ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,macKey);
if (ph->mac != mac[0]) { if (ph->mac != mac[0]) {
RR->t->incomingPacketDropped(tPtr,0xbc881231,ph->packetId,0,peer->identity(),path->address(),hops,Protocol::VERB_NOP,ZT_TRACE_PACKET_DROP_REASON_MAC_FAILED); RR->t->incomingPacketDropped(tPtr,0xbc881231,ph->packetId,0,peer->identity(),path->address(),hops,Protocol::VERB_NOP,ZT_TRACE_PACKET_DROP_REASON_MAC_FAILED);
return; return;
@ -353,8 +353,8 @@ void VL1::onRemotePacket(void *const tPtr,const int64_t localSocket,const InetAd
SharedPtr<Buf> nb(new Buf()); SharedPtr<Buf> nb(new Buf());
const int uncompressedLen = LZ4_decompress_safe( const int uncompressedLen = LZ4_decompress_safe(
reinterpret_cast<const char *>(pkt.b->b + ZT_PROTO_PACKET_PAYLOAD_START), reinterpret_cast<const char *>(pkt.b->unsafeData + ZT_PROTO_PACKET_PAYLOAD_START),
reinterpret_cast<char *>(nb->b), reinterpret_cast<char *>(nb->unsafeData),
(int)(packetSize - ZT_PROTO_PACKET_PAYLOAD_START), (int)(packetSize - ZT_PROTO_PACKET_PAYLOAD_START),
ZT_BUF_MEM_SIZE - ZT_PROTO_PACKET_PAYLOAD_START); ZT_BUF_MEM_SIZE - ZT_PROTO_PACKET_PAYLOAD_START);
@ -402,7 +402,7 @@ void VL1::onRemotePacket(void *const tPtr,const int64_t localSocket,const InetAd
case Protocol::VERB_MULTICAST: ok = RR->vl2->_MULTICAST(tPtr,path,peer,*pkt.b,(int)packetSize); break; case Protocol::VERB_MULTICAST: ok = RR->vl2->_MULTICAST(tPtr,path,peer,*pkt.b,(int)packetSize); break;
case Protocol::VERB_ENCAP: ok = _ENCAP(tPtr,path,peer,*pkt.b,(int)packetSize); break; case Protocol::VERB_ENCAP: ok = _ENCAP(tPtr,path,peer,*pkt.b,(int)packetSize); break;
default: default:
RR->t->incomingPacketDropped(tPtr,0xdeadeff0,ph->packetId,0,identityFromPeerPtr(peer),path->address(),hops,verb,ZT_TRACE_PACKET_DROP_REASON_UNRECOGNIZED_VERB); RR->t->incomingPacketDropped(tPtr,0xeeeeeff0,ph->packetId,0,identityFromPeerPtr(peer),path->address(),hops,verb,ZT_TRACE_PACKET_DROP_REASON_UNRECOGNIZED_VERB);
break; break;
} }
if (ok) if (ok)
@ -414,10 +414,10 @@ void VL1::onRemotePacket(void *const tPtr,const int64_t localSocket,const InetAd
void VL1::_relay(void *tPtr,const SharedPtr<Path> &path,const Address &destination,SharedPtr<Buf> &data,unsigned int len) void VL1::_relay(void *tPtr,const SharedPtr<Path> &path,const Address &destination,SharedPtr<Buf> &data,unsigned int len)
{ {
const uint8_t newHopCount = (data->b[ZT_PROTO_PACKET_FLAGS_INDEX] & 7U) + 1; const uint8_t newHopCount = (data->lI8(ZT_PROTO_PACKET_FLAGS_INDEX) & 7U) + 1;
if (newHopCount >= ZT_RELAY_MAX_HOPS) if (newHopCount >= ZT_RELAY_MAX_HOPS)
return; return;
data->b[ZT_PROTO_PACKET_FLAGS_INDEX] = (data->b[ZT_PROTO_PACKET_FLAGS_INDEX] & 0xf8U) | newHopCount; data->sI8(ZT_PROTO_PACKET_FLAGS_INDEX,(data->lI8(ZT_PROTO_PACKET_FLAGS_INDEX) & 0xf8U) | newHopCount);
const SharedPtr<Peer> toPeer(RR->topology->peer(tPtr,destination,false)); const SharedPtr<Peer> toPeer(RR->topology->peer(tPtr,destination,false));
if (!toPeer) if (!toPeer)
@ -427,7 +427,7 @@ void VL1::_relay(void *tPtr,const SharedPtr<Path> &path,const Address &destinati
if (!toPath) if (!toPath)
return; return;
toPath->send(RR,tPtr,data->b,len,now); toPath->send(RR,tPtr,data->unsafeData,len,now);
} }
void VL1::_sendPendingWhois(void *const tPtr,const int64_t now) void VL1::_sendPendingWhois(void *const tPtr,const int64_t now)
@ -467,15 +467,15 @@ void VL1::_sendPendingWhois(void *const tPtr,const int64_t now)
int outl = sizeof(Protocol::Header); int outl = sizeof(Protocol::Header);
while ((a != toSend.end())&&(outl < ZT_PROTO_MAX_PACKET_LENGTH)) { while ((a != toSend.end())&&(outl < ZT_PROTO_MAX_PACKET_LENGTH)) {
a->copyTo(outp.b + outl); a->copyTo(outp.unsafeData + outl);
++a; ++a;
outl += ZT_ADDRESS_LENGTH; outl += ZT_ADDRESS_LENGTH;
} }
if (outl > sizeof(Protocol::Header)) { if (outl > sizeof(Protocol::Header)) {
Protocol::armor(outp,outl,root->key(),peer->cipher()); Protocol::armor(outp,outl,root->key(),root->cipher());
RR->expect->sending(ph.packetId,now); RR->expect->sending(ph.packetId,now);
rootPath->send(RR,tPtr,outp.b,outl,now); rootPath->send(RR,tPtr,outp.unsafeData,outl,now);
} }
} }
} }
@ -525,7 +525,7 @@ bool VL1::_HELLO(void *tPtr,const SharedPtr<Path> &path,SharedPtr<Peer> &peer,Bu
Protocol::salsa2012DeriveKey(peer->key(),perPacketKey,pkt,packetSize); Protocol::salsa2012DeriveKey(peer->key(),perPacketKey,pkt,packetSize);
Salsa20(perPacketKey,&p.h.packetId).crypt12(Utils::ZERO256,macKey,ZT_POLY1305_KEY_LEN); Salsa20(perPacketKey,&p.h.packetId).crypt12(Utils::ZERO256,macKey,ZT_POLY1305_KEY_LEN);
uint64_t mac[2]; uint64_t mac[2];
poly1305(mac,pkt.b + ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,packetSize - ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,macKey); poly1305(mac,pkt.unsafeData + ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,packetSize - ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,macKey);
if (p.h.mac != mac[0]) { if (p.h.mac != mac[0]) {
RR->t->incomingPacketDropped(tPtr,0x11bfff81,p.h.packetId,0,id,path->address(),hops,Protocol::VERB_NOP,ZT_TRACE_PACKET_DROP_REASON_MAC_FAILED); RR->t->incomingPacketDropped(tPtr,0x11bfff81,p.h.packetId,0,id,path->address(),hops,Protocol::VERB_NOP,ZT_TRACE_PACKET_DROP_REASON_MAC_FAILED);
return false; return false;
@ -542,8 +542,8 @@ bool VL1::_HELLO(void *tPtr,const SharedPtr<Path> &path,SharedPtr<Peer> &peer,Bu
} }
packetSize -= ZT_HMACSHA384_LEN; packetSize -= ZT_HMACSHA384_LEN;
KBKDFHMACSHA384(key,ZT_PROTO_KDF_KEY_LABEL_HELLO_HMAC,0,0,hmacKey); // iter == 0 for HELLO, 1 for OK(HELLO) KBKDFHMACSHA384(key,ZT_PROTO_KDF_KEY_LABEL_HELLO_HMAC,0,0,hmacKey); // iter == 0 for HELLO, 1 for OK(HELLO)
HMACSHA384(hmacKey,pkt.b,packetSize,hmac); HMACSHA384(hmacKey,pkt.unsafeData,packetSize,hmac);
if (!Utils::secureEq(pkt.b + packetSize,hmac,ZT_HMACSHA384_LEN)) { if (!Utils::secureEq(pkt.unsafeData + packetSize,hmac,ZT_HMACSHA384_LEN)) {
RR->t->incomingPacketDropped(tPtr,0x1000662a,p.h.packetId,0,id,path->address(),hops,Protocol::VERB_NOP,ZT_TRACE_PACKET_DROP_REASON_MAC_FAILED); RR->t->incomingPacketDropped(tPtr,0x1000662a,p.h.packetId,0,id,path->address(),hops,Protocol::VERB_NOP,ZT_TRACE_PACKET_DROP_REASON_MAC_FAILED);
return false; return false;
} }
@ -568,10 +568,10 @@ bool VL1::_HELLO(void *tPtr,const SharedPtr<Path> &path,SharedPtr<Peer> &peer,Bu
// can't even get ephemeral public keys without first knowing the long term secret key, // can't even get ephemeral public keys without first knowing the long term secret key,
// adding a little defense in depth. // adding a little defense in depth.
uint8_t iv[8]; uint8_t iv[8];
for (int i = 0; i < 8; ++i) iv[i] = pkt.b[i]; for (int i = 0; i < 8; ++i) iv[i] = pkt.unsafeData[i];
iv[7] &= 0xf8U; // this exists for pure legacy reasons, meh... iv[7] &= 0xf8U; // this exists for pure legacy reasons, meh...
Salsa20 s20(key,iv); Salsa20 s20(key,iv);
s20.crypt12(pkt.b + ptr,pkt.b + ptr,packetSize - ptr); s20.crypt12(pkt.unsafeData + ptr,pkt.unsafeData + ptr,packetSize - ptr);
ptr += pkt.rI16(ptr); // skip length field which currently is always zero in v2.0+ ptr += pkt.rI16(ptr); // skip length field which currently is always zero in v2.0+
@ -654,23 +654,23 @@ bool VL1::_HELLO(void *tPtr,const SharedPtr<Path> &path,SharedPtr<Peer> &peer,Bu
int outl = sizeof(Protocol::OK::HELLO); int outl = sizeof(Protocol::OK::HELLO);
outp.wO(outl,path->address()); outp.wO(outl,path->address());
outp.wI(outl,(uint16_t)0); // legacy field, always 0 outp.wI16(outl,0); // legacy field, always 0
if (p.versionProtocol >= 11) { if (p.versionProtocol >= 11) {
outp.wI(outl,(uint16_t)myNodeMetaDataBin.size()); outp.wI16(outl,(uint16_t)myNodeMetaDataBin.size());
outp.wB(outl,myNodeMetaDataBin.data(),(unsigned int)myNodeMetaDataBin.size()); outp.wB(outl,myNodeMetaDataBin.data(),(unsigned int)myNodeMetaDataBin.size());
outp.wI(outl,(uint16_t)0); // length of additional fields, currently 0 outp.wI16(outl,0); // length of additional fields, currently 0
if ((outl + ZT_HMACSHA384_LEN) > ZT_PROTO_MAX_PACKET_LENGTH) // sanity check, shouldn't be possible if ((outl + ZT_HMACSHA384_LEN) > ZT_PROTO_MAX_PACKET_LENGTH) // sanity check, shouldn't be possible
return false; return false;
KBKDFHMACSHA384(key,ZT_PROTO_KDF_KEY_LABEL_HELLO_HMAC,0,1,hmacKey); // iter == 1 for OK KBKDFHMACSHA384(key,ZT_PROTO_KDF_KEY_LABEL_HELLO_HMAC,0,1,hmacKey); // iter == 1 for OK
HMACSHA384(hmacKey,outp.b + sizeof(ok.h),outl - sizeof(ok.h),outp.b + outl); HMACSHA384(hmacKey,outp.unsafeData + sizeof(ok.h),outl - sizeof(ok.h),outp.unsafeData + outl);
outl += ZT_HMACSHA384_LEN; outl += ZT_HMACSHA384_LEN;
} }
Protocol::armor(outp,outl,peer->key(),peer->cipher()); Protocol::armor(outp,outl,peer->key(),peer->cipher());
path->send(RR,tPtr,outp.b,outl,now); path->send(RR,tPtr,outp.unsafeData,outl,now);
return true; return true;
} }
@ -781,7 +781,7 @@ bool VL1::_WHOIS(void *tPtr,const SharedPtr<Path> &path,const SharedPtr<Peer> &p
int outl = sizeof(Protocol::OK::WHOIS); int outl = sizeof(Protocol::OK::WHOIS);
while ( ((ptr + ZT_ADDRESS_LENGTH) <= packetSize) && ((outl + ZT_IDENTITY_MARSHAL_SIZE_MAX + ZT_LOCATOR_MARSHAL_SIZE_MAX) < ZT_PROTO_MAX_PACKET_LENGTH) ) { while ( ((ptr + ZT_ADDRESS_LENGTH) <= packetSize) && ((outl + ZT_IDENTITY_MARSHAL_SIZE_MAX + ZT_LOCATOR_MARSHAL_SIZE_MAX) < ZT_PROTO_MAX_PACKET_LENGTH) ) {
const SharedPtr<Peer> &wp(RR->topology->peer(tPtr,Address(pkt.b + ptr))); const SharedPtr<Peer> &wp(RR->topology->peer(tPtr,Address(pkt.unsafeData + ptr)));
if (wp) { if (wp) {
outp.wO(outl,wp->identity()); outp.wO(outl,wp->identity());
if (peer->remoteVersionProtocol() >= 11) { // older versions don't know what a locator is if (peer->remoteVersionProtocol() >= 11) { // older versions don't know what a locator is
@ -798,7 +798,7 @@ bool VL1::_WHOIS(void *tPtr,const SharedPtr<Path> &path,const SharedPtr<Peer> &p
if (outl > sizeof(Protocol::OK::WHOIS)) { if (outl > sizeof(Protocol::OK::WHOIS)) {
Protocol::armor(outp,outl,peer->key(),peer->cipher()); Protocol::armor(outp,outl,peer->key(),peer->cipher());
path->send(RR,tPtr,outp.b,outl,RR->node->now()); path->send(RR,tPtr,outp.unsafeData,outl,RR->node->now());
} }
} }
@ -823,28 +823,27 @@ bool VL1::_RENDEZVOUS(void *tPtr,const SharedPtr<Path> &path,const SharedPtr<Pee
case 4: case 4:
case 16: case 16:
if ((sizeof(Protocol::RENDEZVOUS) + rdv.addressLength) <= packetSize) { if ((sizeof(Protocol::RENDEZVOUS) + rdv.addressLength) <= packetSize) {
const InetAddress atAddr(pkt.b + sizeof(Protocol::RENDEZVOUS),rdv.addressLength,port); const InetAddress atAddr(pkt.unsafeData + sizeof(Protocol::RENDEZVOUS),rdv.addressLength,port);
peer->contact(tPtr,Endpoint(atAddr),now,false,false); peer->contact(tPtr,Endpoint(atAddr),now,false);
RR->t->tryingNewPath(tPtr,0x55a19aaa,with->identity(),atAddr,path->address(),Protocol::packetId(pkt,packetSize),Protocol::VERB_RENDEZVOUS,peer->address(),peer->identity().hash(),ZT_TRACE_TRYING_NEW_PATH_REASON_RENDEZVOUS); RR->t->tryingNewPath(tPtr,0x55a19aaa,with->identity(),atAddr,path->address(),Protocol::packetId(pkt,packetSize),Protocol::VERB_RENDEZVOUS,peer->address(),peer->identity().hash().data(),ZT_TRACE_TRYING_NEW_PATH_REASON_RENDEZVOUS);
} }
break; break;
case 255: case 255: {
if ((sizeof(Protocol::RENDEZVOUS) + 1) <= packetSize) {
Endpoint ep; Endpoint ep;
int epl = ep.unmarshal(pkt.b + sizeof(Protocol::RENDEZVOUS),packetSize - (int)sizeof(Protocol::RENDEZVOUS)); int p = sizeof(Protocol::RENDEZVOUS);
if ((epl > 0) && (ep)) { int epl = pkt.rO(p,ep);
if ((epl > 0) && (ep) && (!Buf::readOverflow(p,packetSize))) {
switch (ep.type()) { switch (ep.type()) {
case Endpoint::INETADDR_V4: case Endpoint::TYPE_INETADDR_V4:
case Endpoint::INETADDR_V6: case Endpoint::TYPE_INETADDR_V6:
peer->contact(tPtr,ep,now,false,false); peer->contact(tPtr,ep,now,false);
RR->t->tryingNewPath(tPtr,0x55a19aab,with->identity(),ep.inetAddr(),path->address(),Protocol::packetId(pkt,packetSize),Protocol::VERB_RENDEZVOUS,peer->address(),peer->identity().hash(),ZT_TRACE_TRYING_NEW_PATH_REASON_RENDEZVOUS); RR->t->tryingNewPath(tPtr,0x55a19aab,with->identity(),ep.inetAddr(),path->address(),Protocol::packetId(pkt,packetSize),Protocol::VERB_RENDEZVOUS,peer->address(),peer->identity().hash().data(),ZT_TRACE_TRYING_NEW_PATH_REASON_RENDEZVOUS);
break; break;
default: default:
break; break;
} }
} }
} } break;
break;
} }
} }
} }
@ -872,7 +871,7 @@ bool VL1::_ECHO(void *tPtr,const SharedPtr<Path> &path,const SharedPtr<Peer> &pe
outh.h.inReVerb = Protocol::VERB_ECHO; outh.h.inReVerb = Protocol::VERB_ECHO;
outh.h.inRePacketId = packetId; outh.h.inRePacketId = packetId;
int outl = sizeof(Protocol::OK::ECHO); int outl = sizeof(Protocol::OK::ECHO);
outp.wB(outl,pkt.b + sizeof(Protocol::Header),packetSize - sizeof(Protocol::Header)); outp.wB(outl,pkt.unsafeData + sizeof(Protocol::Header),packetSize - sizeof(Protocol::Header));
if (Buf::writeOverflow(outl)) { if (Buf::writeOverflow(outl)) {
RR->t->incomingPacketDropped(tPtr,0x14d70bb0,packetId,0,peer->identity(),path->address(),Protocol::packetHops(pkt,packetSize),Protocol::VERB_ECHO,ZT_TRACE_PACKET_DROP_REASON_MALFORMED_PACKET); RR->t->incomingPacketDropped(tPtr,0x14d70bb0,packetId,0,peer->identity(),path->address(),Protocol::packetHops(pkt,packetSize),Protocol::VERB_ECHO,ZT_TRACE_PACKET_DROP_REASON_MALFORMED_PACKET);
@ -880,7 +879,7 @@ bool VL1::_ECHO(void *tPtr,const SharedPtr<Path> &path,const SharedPtr<Peer> &pe
} }
Protocol::armor(outp,outl,peer->key(),peer->cipher()); Protocol::armor(outp,outl,peer->key(),peer->cipher());
path->send(RR,tPtr,outp.b,outl,now); path->send(RR,tPtr,outp.unsafeData,outl,now);
} else { } else {
RR->t->incomingPacketDropped(tPtr,0x27878bc1,packetId,0,peer->identity(),path->address(),Protocol::packetHops(pkt,packetSize),Protocol::VERB_ECHO,ZT_TRACE_PACKET_DROP_REASON_RATE_LIMIT_EXCEEDED); RR->t->incomingPacketDropped(tPtr,0x27878bc1,packetId,0,peer->identity(),path->address(),Protocol::packetHops(pkt,packetSize),Protocol::VERB_ECHO,ZT_TRACE_PACKET_DROP_REASON_RATE_LIMIT_EXCEEDED);
} }
@ -908,13 +907,21 @@ bool VL1::_PUSH_DIRECT_PATHS(void *tPtr,const SharedPtr<Path> &path,const Shared
Endpoint ep; Endpoint ep;
for(unsigned int pi=0;pi<numPaths;++pi) { for(unsigned int pi=0;pi<numPaths;++pi) {
/*const uint8_t flags = pkt.rI8(ptr);*/ ++ptr; // flags are not presently used /*const uint8_t flags = pkt.rI8(ptr);*/ ++ptr; // flags are not presently used
ptr += pkt.rI16(ptr); // extended attributes size, currently always 0
const int xas = (int)pkt.rI16(ptr);
//const uint8_t *const extendedAttrs = pkt.rBnc(ptr,xas);
ptr += xas;
const unsigned int addrType = pkt.rI8(ptr); const unsigned int addrType = pkt.rI8(ptr);
const unsigned int addrRecordLen = pkt.rI8(ptr); const unsigned int addrRecordLen = pkt.rI8(ptr);
if (addrRecordLen == 0) { if (addrRecordLen == 0) {
RR->t->incomingPacketDropped(tPtr,0xaed00118,pdp.h.packetId,0,peer->identity(),path->address(),Protocol::packetHops(pdp.h),Protocol::VERB_PUSH_DIRECT_PATHS,ZT_TRACE_PACKET_DROP_REASON_MALFORMED_PACKET); RR->t->incomingPacketDropped(tPtr,0xaed00118,pdp.h.packetId,0,peer->identity(),path->address(),Protocol::packetHops(pdp.h),Protocol::VERB_PUSH_DIRECT_PATHS,ZT_TRACE_PACKET_DROP_REASON_MALFORMED_PACKET);
return false; return false;
} }
if (Buf::readOverflow(ptr,packetSize)) {
RR->t->incomingPacketDropped(tPtr,0xb450e10f,pdp.h.packetId,0,peer->identity(),path->address(),Protocol::packetHops(pdp.h),Protocol::VERB_PUSH_DIRECT_PATHS,ZT_TRACE_PACKET_DROP_REASON_MALFORMED_PACKET);
return false;
}
const void *addrBytes = nullptr; const void *addrBytes = nullptr;
unsigned int addrLen = 0; unsigned int addrLen = 0;
@ -934,11 +941,14 @@ bool VL1::_PUSH_DIRECT_PATHS(void *tPtr,const SharedPtr<Path> &path,const Shared
addrLen = 16; addrLen = 16;
addrPort = pkt.rI16(ptr); addrPort = pkt.rI16(ptr);
break; break;
//case 200:
// TODO: this would be a WebRTC SDP offer contained in the extended attrs field
//break;
default: break; default: break;
} }
if (Buf::readOverflow(ptr,packetSize)) { if (Buf::readOverflow(ptr,packetSize)) {
RR->t->incomingPacketDropped(tPtr,0xbad0f10f,pdp.h.packetId,0,peer->identity(),path->address(),Protocol::packetHops(pdp.h),Protocol::VERB_PUSH_DIRECT_PATHS,ZT_TRACE_PACKET_DROP_REASON_MALFORMED_PACKET); RR->t->incomingPacketDropped(tPtr,0xb4d0f10f,pdp.h.packetId,0,peer->identity(),path->address(),Protocol::packetHops(pdp.h),Protocol::VERB_PUSH_DIRECT_PATHS,ZT_TRACE_PACKET_DROP_REASON_MALFORMED_PACKET);
return false; return false;
} }
@ -951,8 +961,8 @@ bool VL1::_PUSH_DIRECT_PATHS(void *tPtr,const SharedPtr<Path> &path,const Shared
} }
switch(ep.type()) { switch(ep.type()) {
case Endpoint::INETADDR_V4: case Endpoint::TYPE_INETADDR_V4:
case Endpoint::INETADDR_V6: case Endpoint::TYPE_INETADDR_V6:
a = ep.inetAddr(); a = ep.inetAddr();
break; break;
default: // other types are not supported yet default: // other types are not supported yet