Explorar o código

Make Salsa20 variable-round, allowing for Salsa20/12 to be used for Packet encrypt and decrypt. Profiling analysis found that Salsa20 encrypt was accounting for a nontrivial percentage of CPU time, so it makes sense to cut this load fundamentally. There are no published attacks against Salsa20/12, and DJB believes 20 rounds to be overkill. This should be more than enough for our needs. Obviously incorporating ASM Salsa20 is among the next steps for performance.

Adam Ierymenko %!s(int64=12) %!d(string=hai) anos
pai
achega
8c9b73f67b
Modificáronse 7 ficheiros con 41 adicións e 14 borrados
  1. 4 1
      node/Identity.cpp
  2. 3 2
      node/NodeConfig.cpp
  3. 7 2
      node/Packet.hpp
  4. 5 2
      node/Salsa20.cpp
  5. 7 4
      node/Salsa20.hpp
  6. 4 0
      selftest-crypto-vectors.hpp
  7. 11 3
      selftest.cpp

+ 4 - 1
node/Identity.cpp

@@ -48,6 +48,9 @@
 // Step distance for mixing genmem[]
 #define ZT_IDENTITY_GEN_MEMORY_MIX_STEP 1024
 
+// Rounds used for Salsa20 step
+#define ZT_IDENTITY_GEN_SALSA20_ROUNDS 20
+
 namespace ZeroTier {
 
 // A memory-hard composition of SHA-512 and Salsa20 for hashcash hashing
@@ -58,7 +61,7 @@ static inline void _computeMemoryHardHash(const void *publicKey,unsigned int pub
 
 	// Generate genmem[] bytes of Salsa20 key stream
 	memset(genmem,0,ZT_IDENTITY_GEN_MEMORY);
-	Salsa20 s20(digest,256,(char *)digest + 32);
+	Salsa20 s20(digest,256,(char *)digest + 32,ZT_IDENTITY_GEN_SALSA20_ROUNDS);
 	s20.encrypt(genmem,genmem,ZT_IDENTITY_GEN_MEMORY);
 
 	// Do something to genmem[] that iteratively makes every value

+ 3 - 2
node/NodeConfig.cpp

@@ -49,6 +49,7 @@
 #include "Logger.hpp"
 #include "Topology.hpp"
 #include "Demarc.hpp"
+#include "Packet.hpp"
 #include "InetAddress.hpp"
 #include "Peer.hpp"
 #include "Salsa20.hpp"
@@ -283,7 +284,7 @@ std::vector< Buffer<ZT_NODECONFIG_MAX_PACKET_SIZE> > NodeConfig::encodeControlMe
 			Utils::getSecureRandom(iv,8);
 			memcpy(packet.field(8,8),iv,8);
 
-			Salsa20 s20(key,256,iv);
+			Salsa20 s20(key,256,iv,ZT_PROTO_SALSA20_ROUNDS);
 			s20.encrypt(packet.field(16,packet.size() - 16),packet.field(16,packet.size() - 16),packet.size() - 16);
 
 			memcpy(keytmp,key,32);
@@ -322,7 +323,7 @@ bool NodeConfig::decodeControlMessagePacket(const void *key,const void *data,uns
 		if (!Utils::secureEq(packet.field(0,8),poly1305tag,8))
 			return false;
 
-		Salsa20 s20(key,256,packet.field(8,8));
+		Salsa20 s20(key,256,packet.field(8,8),ZT_PROTO_SALSA20_ROUNDS);
 		s20.decrypt(packet.field(16,packet.size() - 16),packet.field(16,packet.size() - 16),packet.size() - 16);
 
 		conversationId = packet.at<uint32_t>(16);

+ 7 - 2
node/Packet.hpp

@@ -92,6 +92,11 @@
  */
 #define ZT_PROTO_VERB_FLAG_COMPRESSED 0x80
 
+/**
+ * Rounds used for Salsa20 encryption in ZT
+ */
+#define ZT_PROTO_SALSA20_ROUNDS 12
+
 // Indices of fields in normal packet header -- do not change as this
 // might require both code rework and will break compatibility.
 #define ZT_PACKET_IDX_IV 0
@@ -852,7 +857,7 @@ public:
 		else (*this)[ZT_PACKET_IDX_FLAGS] &= (char)(~ZT_PROTO_FLAG_ENCRYPTED);
 
 		_mangleKey((const unsigned char *)key,mangledKey);
-		Salsa20 s20(mangledKey,256,field(ZT_PACKET_IDX_IV,8));
+		Salsa20 s20(mangledKey,256,field(ZT_PACKET_IDX_IV,8),ZT_PROTO_SALSA20_ROUNDS);
 
 		// MAC key is always the first 32 bytes of the Salsa20 key stream
 		// This is the same construction DJB's NaCl library uses
@@ -880,7 +885,7 @@ public:
 		unsigned char *const payload = field(ZT_PACKET_IDX_VERB,payloadLen);
 
 		_mangleKey((const unsigned char *)key,mangledKey);
-		Salsa20 s20(mangledKey,256,field(ZT_PACKET_IDX_IV,8));
+		Salsa20 s20(mangledKey,256,field(ZT_PACKET_IDX_IV,8),ZT_PROTO_SALSA20_ROUNDS);
 
 		s20.encrypt(ZERO_KEY,macKey,sizeof(macKey));
 		Poly1305::compute(mac,payload,payloadLen,macKey);

+ 5 - 2
node/Salsa20.cpp

@@ -29,7 +29,7 @@ namespace ZeroTier {
 static const char *sigma = "expand 32-byte k";
 static const char *tau = "expand 16-byte k";
 
-void Salsa20::init(const void *key,unsigned int kbits,const void *iv)
+void Salsa20::init(const void *key,unsigned int kbits,const void *iv,unsigned int rounds)
 	throw()
 {
 	const char *constants;
@@ -59,6 +59,8 @@ void Salsa20::init(const void *key,unsigned int kbits,const void *iv)
 	_state[5] = U8TO32_LITTLE(constants + 4);
 	_state[10] = U8TO32_LITTLE(constants + 8);
 	_state[15] = U8TO32_LITTLE(constants + 12);
+
+	_roundsDiv2 = rounds / 2;
 }
 
 void Salsa20::encrypt(const void *in,void *out,unsigned int bytes)
@@ -114,7 +116,8 @@ void Salsa20::encrypt(const void *in,void *out,unsigned int bytes)
 		x13 = j13;
 		x14 = j14;
 		x15 = j15;
-		for (i = 20;i > 0;i -= 2) {
+		//for (i = 20;i > 0;i -= 2) {
+		for(i=0;i<_roundsDiv2;++i) {
 			 x4 = XOR( x4,ROTATE(PLUS( x0,x12), 7));
 			 x8 = XOR( x8,ROTATE(PLUS( x4, x0), 9));
 			x12 = XOR(x12,ROTATE(PLUS( x8, x4),13));

+ 7 - 4
node/Salsa20.hpp

@@ -14,7 +14,7 @@
 namespace ZeroTier {
 
 /**
- * Salsa20/20 stream cipher
+ * Salsa20 stream cipher
  */
 class Salsa20
 {
@@ -25,11 +25,12 @@ public:
 	 * @param key Key bits
 	 * @param kbits Number of key bits: 128 or 256 (recommended)
 	 * @param iv 64-bit initialization vector
+	 * @param rounds Number of rounds: 8, 12, or 20
 	 */
-	Salsa20(const void *key,unsigned int kbits,const void *iv)
+	Salsa20(const void *key,unsigned int kbits,const void *iv,unsigned int rounds)
 		throw()
 	{
-		init(key,kbits,iv);
+		init(key,kbits,iv,rounds);
 	}
 
 	/**
@@ -38,8 +39,9 @@ public:
 	 * @param key Key bits
 	 * @param kbits Number of key bits: 128 or 256 (recommended)
 	 * @param iv 64-bit initialization vector
+	 * @param rounds Number of rounds: 8, 12, or 20
 	 */
-	void init(const void *key,unsigned int kbits,const void *iv)
+	void init(const void *key,unsigned int kbits,const void *iv,unsigned int rounds)
 		throw();
 
 	/**
@@ -67,6 +69,7 @@ public:
 
 private:
 	uint32_t _state[16];
+	unsigned int _roundsDiv2;
 };
 
 } // namespace ZeroTier

+ 4 - 0
selftest-crypto-vectors.hpp

@@ -10,6 +10,10 @@ static const unsigned char s20TV0Key[32] = { 0x0f,0x62,0xb5,0x08,0x5b,0xae,0x01,
 static const unsigned char s20TV0Iv[8] = { 0x28,0x8f,0xf6,0x5d,0xc4,0x2b,0x92,0xf9 };
 static const unsigned char s20TV0Ks[64] = { 0x5e,0x5e,0x71,0xf9,0x01,0x99,0x34,0x03,0x04,0xab,0xb2,0x2a,0x37,0xb6,0x62,0x5b,0xf8,0x83,0xfb,0x89,0xce,0x3b,0x21,0xf5,0x4a,0x10,0xb8,0x10,0x66,0xef,0x87,0xda,0x30,0xb7,0x76,0x99,0xaa,0x73,0x79,0xda,0x59,0x5c,0x77,0xdd,0x59,0x54,0x2d,0xa2,0x08,0xe5,0x95,0x4f,0x89,0xe4,0x0e,0xb7,0xaa,0x80,0xa8,0x4a,0x61,0x76,0x66,0x3f };
 
+static const unsigned char s2012TV0Key[32] = { 0x0f,0x62,0xb5,0x08,0x5b,0xae,0x01,0x54,0xa7,0xfa,0x4d,0xa0,0xf3,0x46,0x99,0xec,0x3f,0x92,0xe5,0x38,0x8b,0xde,0x31,0x84,0xd7,0x2a,0x7d,0xd0,0x23,0x76,0xc9,0x1c };
+static const unsigned char s2012TV0Iv[8] = { 0x28,0x8f,0xf6,0x5d,0xc4,0x2b,0x92,0xf9 };
+static const unsigned char s2012TV0Ks[64] = { 0x99,0xDB,0x33,0xAD,0x11,0xCE,0x0C,0xCB,0x3B,0xFD,0xBF,0x8D,0x0C,0x18,0x16,0x04,0x52,0xD0,0x14,0xCD,0xE9,0x89,0xB4,0xC4,0x11,0xA5,0x59,0xFF,0x7C,0x20,0xA1,0x69,0xE6,0xDC,0x99,0x09,0xD8,0x16,0xBE,0xCE,0xDC,0x40,0x63,0xCE,0x07,0xCE,0xA8,0x28,0xF4,0x4B,0xF9,0xB6,0xC9,0xA0,0xA0,0xB2,0x00,0xE1,0xB5,0x2A,0xF4,0x18,0x59,0xC5 };
+
 static const unsigned char poly1305TV0Input[32] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };
 static const unsigned char poly1305TV0Key[32] = { 0x74,0x68,0x69,0x73,0x20,0x69,0x73,0x20,0x33,0x32,0x2d,0x62,0x79,0x74,0x65,0x20,0x6b,0x65,0x79,0x20,0x66,0x6f,0x72,0x20,0x50,0x6f,0x6c,0x79,0x31,0x33,0x30,0x35 };
 static const unsigned char poly1305TV0Tag[16] = { 0x49,0xec,0x78,0x09,0x0e,0x48,0x1e,0xc6,0xc2,0x6b,0x33,0xb9,0x1c,0xcc,0x03,0x07 };

+ 11 - 3
selftest.cpp

@@ -180,16 +180,16 @@ static int testCrypto()
 		memset(buf2,0,sizeof(buf2));
 		memset(buf3,0,sizeof(buf3));
 		Salsa20 s20;
-		s20.init("12345678123456781234567812345678",256,"12345678");
+		s20.init("12345678123456781234567812345678",256,"12345678",20);
 		s20.encrypt(buf1,buf2,sizeof(buf1));
-		s20.init("12345678123456781234567812345678",256,"12345678");
+		s20.init("12345678123456781234567812345678",256,"12345678",20);
 		s20.decrypt(buf2,buf3,sizeof(buf2));
 		if (memcmp(buf1,buf3,sizeof(buf1))) {
 			std::cout << "FAIL (encrypt/decrypt test)" << std::endl;
 			return -1;
 		}
 	}
-	Salsa20 s20(s20TV0Key,256,s20TV0Iv);
+	Salsa20 s20(s20TV0Key,256,s20TV0Iv,20);
 	memset(buf1,0,sizeof(buf1));
 	memset(buf2,0,sizeof(buf2));
 	s20.encrypt(buf1,buf2,64);
@@ -197,6 +197,14 @@ static int testCrypto()
 		std::cout << "FAIL (test vector 0)" << std::endl;
 		return -1;
 	}
+	s20.init(s2012TV0Key,256,s2012TV0Iv,12);
+	memset(buf1,0,sizeof(buf1));
+	memset(buf2,0,sizeof(buf2));
+	s20.encrypt(buf1,buf2,64);
+	if (memcmp(buf2,s2012TV0Ks,64)) {
+		std::cout << "FAIL (test vector 1)" << std::endl;
+		return -1;
+	}
 	std::cout << "PASS" << std::endl;
 
 	return 0;