diff --git a/node/Identity.cpp b/node/Identity.cpp index 0ea3dc188..d50c56fe1 100644 --- a/node/Identity.cpp +++ b/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 diff --git a/node/NodeConfig.cpp b/node/NodeConfig.cpp index 0dda8da7a..f26cd8ea9 100644 --- a/node/NodeConfig.cpp +++ b/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 > 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(16); diff --git a/node/Packet.hpp b/node/Packet.hpp index 486faebb8..aeb5d0bbc 100644 --- a/node/Packet.hpp +++ b/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); diff --git a/node/Salsa20.cpp b/node/Salsa20.cpp index d80ea28d4..b634dd694 100644 --- a/node/Salsa20.cpp +++ b/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)); diff --git a/node/Salsa20.hpp b/node/Salsa20.hpp index fa95ed393..9f34ba786 100644 --- a/node/Salsa20.hpp +++ b/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 diff --git a/selftest-crypto-vectors.hpp b/selftest-crypto-vectors.hpp index baf3cddb0..8e07e36aa 100644 --- a/selftest-crypto-vectors.hpp +++ b/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 }; diff --git a/selftest.cpp b/selftest.cpp index 637b579e9..8404f4a1a 100644 --- a/selftest.cpp +++ b/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;