Browse Source

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.

pull/1/head
Adam Ierymenko 13 years ago
parent
commit
8c9b73f67b
  1. 5
      node/Identity.cpp
  2. 5
      node/NodeConfig.cpp
  3. 9
      node/Packet.hpp
  4. 7
      node/Salsa20.cpp
  5. 11
      node/Salsa20.hpp
  6. 4
      selftest-crypto-vectors.hpp
  7. 14
      selftest.cpp

5
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

5
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);

9
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);

7
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));

11
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
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 };

14
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;

Loading…
Cancel
Save