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.

This commit is contained in:
Adam Ierymenko 2013-10-18 17:39:48 -04:00
parent 37e3bc3467
commit 8c9b73f67b
7 changed files with 41 additions and 14 deletions

View File

@ -48,6 +48,9 @@
// Step distance for mixing genmem[] // Step distance for mixing genmem[]
#define ZT_IDENTITY_GEN_MEMORY_MIX_STEP 1024 #define ZT_IDENTITY_GEN_MEMORY_MIX_STEP 1024
// Rounds used for Salsa20 step
#define ZT_IDENTITY_GEN_SALSA20_ROUNDS 20
namespace ZeroTier { namespace ZeroTier {
// A memory-hard composition of SHA-512 and Salsa20 for hashcash hashing // 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 // Generate genmem[] bytes of Salsa20 key stream
memset(genmem,0,ZT_IDENTITY_GEN_MEMORY); 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); s20.encrypt(genmem,genmem,ZT_IDENTITY_GEN_MEMORY);
// Do something to genmem[] that iteratively makes every value // Do something to genmem[] that iteratively makes every value

View File

@ -49,6 +49,7 @@
#include "Logger.hpp" #include "Logger.hpp"
#include "Topology.hpp" #include "Topology.hpp"
#include "Demarc.hpp" #include "Demarc.hpp"
#include "Packet.hpp"
#include "InetAddress.hpp" #include "InetAddress.hpp"
#include "Peer.hpp" #include "Peer.hpp"
#include "Salsa20.hpp" #include "Salsa20.hpp"
@ -283,7 +284,7 @@ std::vector< Buffer<ZT_NODECONFIG_MAX_PACKET_SIZE> > NodeConfig::encodeControlMe
Utils::getSecureRandom(iv,8); Utils::getSecureRandom(iv,8);
memcpy(packet.field(8,8),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); s20.encrypt(packet.field(16,packet.size() - 16),packet.field(16,packet.size() - 16),packet.size() - 16);
memcpy(keytmp,key,32); 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)) if (!Utils::secureEq(packet.field(0,8),poly1305tag,8))
return false; 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); s20.decrypt(packet.field(16,packet.size() - 16),packet.field(16,packet.size() - 16),packet.size() - 16);
conversationId = packet.at<uint32_t>(16); conversationId = packet.at<uint32_t>(16);

View File

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

View File

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

View File

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

View File

@ -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 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 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 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 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 }; static const unsigned char poly1305TV0Tag[16] = { 0x49,0xec,0x78,0x09,0x0e,0x48,0x1e,0xc6,0xc2,0x6b,0x33,0xb9,0x1c,0xcc,0x03,0x07 };

View File

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