summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Ierymenko <adam.ierymenko@gmail.com>2015-05-14 17:41:05 -0700
committerAdam Ierymenko <adam.ierymenko@gmail.com>2015-05-14 17:41:05 -0700
commite94518590de48e2718539f98b87e2f617fa02f75 (patch)
tree8329028e4fc1e2e198f2e1e7ee275a47d02b9dc0
parenta8835cd8b33903440f372ed66f4e3b49745ea68f (diff)
downloadinfinitytier-e94518590de48e2718539f98b87e2f617fa02f75.tar.gz
infinitytier-e94518590de48e2718539f98b87e2f617fa02f75.zip
First stab of PFS design work with PKC security -- may not implement in 1.0.3 but stubbing out.
-rw-r--r--node/Packet.cpp1
-rw-r--r--node/Packet.hpp114
-rw-r--r--node/Utils.cpp9
-rw-r--r--selftest.cpp18
4 files changed, 112 insertions, 30 deletions
diff --git a/node/Packet.cpp b/node/Packet.cpp
index f72f64b2..a81873ff 100644
--- a/node/Packet.cpp
+++ b/node/Packet.cpp
@@ -50,6 +50,7 @@ const char *Packet::verbString(Verb v)
case VERB_NETWORK_CONFIG_REFRESH: return "NETWORK_CONFIG_REFRESH";
case VERB_MULTICAST_GATHER: return "MULTICAST_GATHER";
case VERB_MULTICAST_FRAME: return "MULTICAST_FRAME";
+ case VERB_SET_EPHEMERAL_KEY: return "SET_EPHEMERAL_KEY";
}
return "(unknown)";
}
diff --git a/node/Packet.hpp b/node/Packet.hpp
index 76f84996..efe58c78 100644
--- a/node/Packet.hpp
+++ b/node/Packet.hpp
@@ -99,6 +99,17 @@
#define ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012 1
/**
+ * Cipher suite: PFS negotiated ephemeral cipher suite and authentication
+ *
+ * This message is encrypted with the latest negotiated ephemeral (PFS)
+ * key pair and cipher suite. If authentication fails, VERB_SET_EPHEMERAL_KEY
+ * may be sent to renegotiate ephemeral keys. To prevent attacks, this
+ * attempted renegotiation should be limited to some sane rate such as
+ * once per second.
+ */
+#define ZT_PROTO_CIPHER_SUITE__EPHEMERAL 7
+
+/**
* DEPRECATED payload encrypted flag, will be removed for re-use soon.
*
* This has been replaced by the two-bit cipher suite selection field where
@@ -115,13 +126,6 @@
#define ZT_PROTO_FLAG_FRAGMENTED 0x40
/**
- * Flag indicating encryption with a PFS session key
- *
- * Not used yet -- for future PFS session re-keying support.
- */
-#define ZT_PROTO_FLAG_PFS_SESSION 0x20
-
-/**
* Verb flag indicating payload is compressed with LZ4
*/
#define ZT_PROTO_VERB_FLAG_COMPRESSED 0x80
@@ -186,6 +190,17 @@
#define ZT_PROTO_DEST_ADDRESS_TYPE_IPV4 4
#define ZT_PROTO_DEST_ADDRESS_TYPE_IPV6 6
+// Ephemeral key record flags
+#define ZT_PROTO_EPHEMERAL_KEY_FLAG_FIPS 0x01
+
+// Ephemeral key record symmetric cipher types
+#define ZT_PROTO_EPHEMERAL_KEY_SYMMETRIC_CIPHER_SALSA2012_POLY1305 0x01
+#define ZT_PROTO_EPHEMERAL_KEY_SYMMETRIC_CIPHER_AES256_GCM 0x02
+
+// Ephemeral key record public key types
+#define ZT_PROTO_EPHEMERAL_KEY_PK_C25519 0x01
+#define ZT_PROTO_EPHEMERAL_KEY_PK_NISTP256 0x02
+
// Field incides for parsing verbs -------------------------------------------
// Some verbs have variable-length fields. Those aren't fully defined here
@@ -298,8 +313,8 @@ namespace ZeroTier {
*
* Packets smaller than 28 bytes are invalid and silently discarded.
*
- * The flags/cipher/hops bit field is: FFFCCHHH where C is a 2-bit cipher
- * selection allowing up to 4 cipher suites, F is outside-envelope flags,
+ * The flags/cipher/hops bit field is: FFCCCHHH where C is a 3-bit cipher
+ * selection allowing up to 7 cipher suites, F is outside-envelope flags,
* and H is hop count.
*
* The three-bit hop count is the only part of a packet that is mutable in
@@ -752,7 +767,59 @@ public:
* <[6] multicast group MAC>
* <[4] 32-bit multicast group ADI>
*/
- VERB_MULTICAST_FRAME = 14
+ VERB_MULTICAST_FRAME = 14,
+
+ /* Ephemeral (PFS) key push:
+ * <[8] 64-bit PFS key set ID sender holds for recipient (0==none)>
+ * <[8] 64-bit PFS key set ID of this key set>
+ * [... begin PFS key record ...]
+ * <[1] flags>
+ * <[1] symmetric cipher ID>
+ * <[1] public key type ID>
+ * <[2] public key length in bytes>
+ * <[2] identity signature length in bytes (0 for none)>
+ * <[...] public key>
+ * <[...] signature of sender's ZT identity with public key>
+ * [... additional records may follow up to max packet length ...]
+ *
+ * This message is sent to negotiate an ephemeral key. If the recipient's
+ * current key pair for the sender does not match the one the sender
+ * claims to have on file, it must respond with its own SET_EPHEMERAL_KEY.
+ *
+ * PFS key IDs are random and must not be zero, since zero indicates that
+ * the sender does not have an ephemeral key on file for the recipient.
+ *
+ * For each public key, the sender may sign its ZeroTier identity (public
+ * portion only) using the associated digital signature algorithm. This
+ * permits the extension of FIPS-compliant cryptographic algorithms to
+ * cover verification of the identity for full FIPS compliant mode. For
+ * non-FIPS mode, this is optional. If no signature is included the
+ * signature length field must be zero.
+ *
+ * One or more records may be sent. If multiple records are present,
+ * the first record with common symmetric cipher, public key type,
+ * and relevant flags must be used.
+ *
+ * Flags (all unspecified flags must be zero):
+ * 0x01 - FIPS mode, only use record if FIPS compliant crypto in use
+ *
+ * Symmetric cipher IDs:
+ * 0x01 - Salsa20/12 with Poly1305 authentication (ZT default)
+ * 0x02 - AES256-GCM combined crypto and authentication
+ *
+ * Public key types:
+ * 0x01 - Curve25519 ECDH with SHA-512 KDF, Ed25519 signatures
+ * 0x02 - NIST P-256 ECDH with SHA-512 KDF, ECDSA signatures
+ *
+ * Once both peers have a PFS key, they will attempt to send PFS key
+ * encrypted messages with the PFS flag set using the negotiated
+ * cipher/auth type.
+ *
+ * Note: most of these features such as FIPS and other cipher suites are
+ * not implemented yet. They're just specified in the protocol for future
+ * use to support e.g. FIPS requirements.
+ */
+ VERB_SET_EPHEMERAL_KEY = 15
};
/**
@@ -824,7 +891,7 @@ public:
Buffer<ZT_PROTO_MAX_PACKET_LENGTH>(ZT_PROTO_MIN_PACKET_LENGTH)
{
Utils::getSecureRandom(field(ZT_PACKET_IDX_IV,8),8);
- (*this)[ZT_PACKET_IDX_FLAGS] = 0; // zero flags and hops
+ (*this)[ZT_PACKET_IDX_FLAGS] = 0; // zero flags, cipher ID, and hops
}
/**
@@ -873,7 +940,7 @@ public:
Utils::getSecureRandom(field(ZT_PACKET_IDX_IV,8),8);
setDestination(dest);
setSource(source);
- (*this)[ZT_PACKET_IDX_FLAGS] = 0; // zero flags and hops
+ (*this)[ZT_PACKET_IDX_FLAGS] = 0; // zero flags, cipher ID, and hops
setVerb(v);
}
@@ -884,34 +951,21 @@ public:
* technically different but otherwise identical copies of the same
* packet.
*/
- inline void newInitializationVector()
- {
- Utils::getSecureRandom(field(ZT_PACKET_IDX_IV,8),8);
- }
+ inline void newInitializationVector() { Utils::getSecureRandom(field(ZT_PACKET_IDX_IV,8),8); }
/**
* Set this packet's destination
*
* @param dest ZeroTier address of destination
*/
- inline void setDestination(const Address &dest)
- {
- unsigned char *d = field(ZT_PACKET_IDX_DEST,ZT_ADDRESS_LENGTH);
- for(unsigned int i=0;i<ZT_ADDRESS_LENGTH;++i)
- d[i] = dest[i];
- }
+ inline void setDestination(const Address &dest) { dest.copyTo(field(ZT_PACKET_IDX_DEST,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); }
/**
* Set this packet's source
*
* @param source ZeroTier address of source
*/
- inline void setSource(const Address &source)
- {
- unsigned char *s = field(ZT_PACKET_IDX_SOURCE,ZT_ADDRESS_LENGTH);
- for(unsigned int i=0;i<ZT_ADDRESS_LENGTH;++i)
- s[i] = source[i];
- }
+ inline void setSource(const Address &source) { source.copyTo(field(ZT_PACKET_IDX_SOURCE,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); }
/**
* Get this packet's destination
@@ -974,7 +1028,7 @@ public:
inline unsigned int cipher() const
{
// Note: this uses the new cipher spec field, which is incompatible with <1.0.0 peers
- return (((unsigned int)(*this)[ZT_PACKET_IDX_FLAGS] & 0x18) >> 3);
+ return (((unsigned int)(*this)[ZT_PACKET_IDX_FLAGS] & 0x38) >> 3);
}
/**
@@ -983,7 +1037,7 @@ public:
inline void setCipher(unsigned int c)
{
unsigned char &b = (*this)[ZT_PACKET_IDX_FLAGS];
- b = (b & 0xe7) | (unsigned char)((c << 3) & 0x18); // bits: FFFCCHHH
+ b = (b & 0xc7) | (unsigned char)((c << 3) & 0x38); // bits: FFCCCHHH
// DEPRECATED "encrypted" flag -- used by pre-1.0.3 peers
if (c == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012)
b |= ZT_PROTO_FLAG_ENCRYPTED;
diff --git a/node/Utils.cpp b/node/Utils.cpp
index 3380b324..9630e6b3 100644
--- a/node/Utils.cpp
+++ b/node/Utils.cpp
@@ -49,6 +49,7 @@
#include "Utils.hpp"
#include "Mutex.hpp"
+#include "Salsa20.hpp"
namespace ZeroTier {
@@ -152,6 +153,7 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes)
static HCRYPTPROV cryptProvider = NULL;
static Mutex globalLock;
+ static Salsa20 s20;
Mutex::Lock _l(globalLock);
@@ -161,12 +163,19 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes)
exit(1);
return;
}
+ char s20key[32];
+ if (!CryptGenRandom(cryptProvider,(DWORD)sizeof(s20key),(BYTE *)s20key)) {
+ fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() CryptGenRandom failed!\r\n");
+ exit(1);
+ }
+ s20.init(s20key,256,s20key,8);
}
if (!CryptGenRandom(cryptProvider,(DWORD)bytes,(BYTE *)buf)) {
fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() CryptGenRandom failed!\r\n");
exit(1);
}
+ s20.encrypt(buf,buf,bytes);
#else // not __WINDOWS__
diff --git a/selftest.cpp b/selftest.cpp
index 319271f3..5d5067fd 100644
--- a/selftest.cpp
+++ b/selftest.cpp
@@ -204,6 +204,24 @@ static int testCrypto()
::free((void *)bb);
}
+ std::cout << "[crypto] Benchmarking Salsa20/20... "; std::cout.flush();
+ {
+ unsigned char *bb = (unsigned char *)::malloc(1234567);
+ for(unsigned int i=0;i<1234567;++i)
+ bb[i] = (unsigned char)i;
+ Salsa20 s20(s20TV0Key,256,s20TV0Iv,20);
+ double bytes = 0.0;
+ uint64_t start = OSUtils::now();
+ for(unsigned int i=0;i<200;++i) {
+ s20.encrypt(bb,bb,1234567);
+ bytes += 1234567.0;
+ }
+ uint64_t end = OSUtils::now();
+ SHA512::hash(buf1,bb,1234567);
+ std::cout << ((bytes / 1048576.0) / ((double)(end - start) / 1000.0)) << " MiB/second (" << Utils::hex(buf1,16) << ')' << std::endl;
+ ::free((void *)bb);
+ }
+
std::cout << "[crypto] Testing SHA-512... "; std::cout.flush();
SHA512::hash(buf1,sha512TV0Input,(unsigned int)strlen(sha512TV0Input));
if (memcmp(buf1,sha512TV0Digest,64)) {