AES integrated on send side.

This commit is contained in:
Adam Ierymenko 2020-08-21 14:23:31 -07:00
parent 3fd8efe642
commit 2ac49d99dd
15 changed files with 446 additions and 427 deletions

View File

@ -618,7 +618,7 @@ void Bond::sendPATH_NEGOTIATION_REQUEST(void *tPtr, const SharedPtr<Path> &path)
Packet outp(_peer->_id.address(),RR->identity.address(),Packet::VERB_PATH_NEGOTIATION_REQUEST);
outp.append<int16_t>(_localUtility);
if (path->address()) {
outp.armor(_peer->key(),false);
outp.armor(_peer->key(),false,_peer->aesKeysIfSupported());
RR->node->putPacket(tPtr,path->localSocket(),path->address(),outp.data(),outp.size());
}
}
@ -639,7 +639,7 @@ void Bond::sendACK(void *tPtr, const SharedPtr<Path> &path,const int64_t localSo
//RR->t->bondStateMessage(NULL, traceMsg);
outp.append<uint32_t>(bytesToAck);
if (atAddress) {
outp.armor(_peer->key(),false);
outp.armor(_peer->key(),false,_peer->aesKeysIfSupported());
RR->node->putPacket(tPtr,localSocket,atAddress,outp.data(),outp.size());
} else {
RR->sw->send(tPtr,outp,false);
@ -662,7 +662,7 @@ void Bond::sendQOS_MEASUREMENT(void *tPtr,const SharedPtr<Path> &path,const int6
int16_t len = generateQoSPacket(path, _now,qosData);
outp.append(qosData,len);
if (atAddress) {
outp.armor(_peer->key(),false);
outp.armor(_peer->key(),false,_peer->aesKeysIfSupported());
RR->node->putPacket(tPtr,localSocket,atAddress,outp.data(),outp.size());
} else {
RR->sw->send(tPtr,outp,false);

View File

@ -2439,7 +2439,7 @@ static inline void get_hram(unsigned char *hram, const unsigned char *sm, const
for (i = 32;i < 64;++i) playground[i] = pk[i-32];
for (i = 64;i < smlen;++i) playground[i] = sm[i];
ZeroTier::SHA512::hash(hram,playground,(unsigned int)smlen);
ZeroTier::SHA512(hram,playground,(unsigned int)smlen);
}
//////////////////////////////////////////////////////////////////////////////
@ -2459,11 +2459,11 @@ void C25519::agree(const C25519::Private &mine,const C25519::Public &their,void
unsigned char digest[64];
crypto_scalarmult(rawkey,mine.data,their.data);
SHA512::hash(digest,rawkey,32);
SHA512(digest,rawkey,32);
for(unsigned int i=0,k=0;i<keylen;) {
if (k == 64) {
k = 0;
SHA512::hash(digest,digest,64);
SHA512(digest,digest,64);
}
((unsigned char *)keybuf)[i++] = digest[k++];
}
@ -2472,7 +2472,7 @@ void C25519::agree(const C25519::Private &mine,const C25519::Public &their,void
void C25519::sign(const C25519::Private &myPrivate,const C25519::Public &myPublic,const void *msg,unsigned int len,void *signature)
{
unsigned char digest[64]; // we sign the first 32 bytes of SHA-512(msg)
SHA512::hash(digest,msg,len);
SHA512(digest,msg,len);
#ifdef ZT_USE_FAST_X64_ED25519
ed25519_amd64_asm_sign(myPrivate.data + 32,myPublic.data + 32,digest,(unsigned char *)signature);
@ -2486,7 +2486,7 @@ void C25519::sign(const C25519::Private &myPrivate,const C25519::Public &myPubli
unsigned char hram[crypto_hash_sha512_BYTES];
unsigned char *sig = (unsigned char *)signature;
SHA512::hash(extsk,myPrivate.data + 32,32);
SHA512(extsk,myPrivate.data + 32,32);
extsk[0] &= 248;
extsk[31] &= 127;
extsk[31] |= 64;
@ -2496,7 +2496,7 @@ void C25519::sign(const C25519::Private &myPrivate,const C25519::Public &myPubli
for(unsigned int i=0;i<32;i++)
sig[64 + i] = digest[i];
SHA512::hash(hmg,sig + 32,64);
SHA512(hmg,sig + 32,64);
/* Computation of R */
sc25519_from64bytes(&sck, hmg);
@ -2525,7 +2525,7 @@ bool C25519::verify(const C25519::Public &their,const void *msg,unsigned int len
{
const unsigned char *const sig = (const unsigned char *)signature;
unsigned char digest[64]; // we sign the first 32 bytes of SHA-512(msg)
SHA512::hash(digest,msg,len);
SHA512(digest,msg,len);
if (!Utils::secureEq(sig + 64,digest,32))
return false;
@ -2565,7 +2565,7 @@ void C25519::_calcPubED(C25519::Pair &kp)
// Second 32 bytes of pub and priv are the keys for ed25519
// signing and verification.
SHA512::hash(extsk,kp.priv.data + 32,32);
SHA512(extsk,kp.priv.data + 32,32);
extsk[0] &= 248;
extsk[31] &= 127;
extsk[31] |= 64;

View File

@ -174,6 +174,8 @@
*/
#define ZT_ADDRESS_LENGTH_HEX 10
#define ZT_SYMMETRIC_KEY_SIZE 48
/**
* Addresses beginning with this byte are reserved for the joy of in-band signaling
*/

View File

@ -34,7 +34,7 @@ namespace ZeroTier {
static inline void _computeMemoryHardHash(const void *publicKey,unsigned int publicKeyBytes,void *digest,void *genmem)
{
// Digest publicKey[] to obtain initial digest
SHA512::hash(digest,publicKey,publicKeyBytes);
SHA512(digest,publicKey,publicKeyBytes);
// Initialize genmem[] using Salsa20 in a CBC-like configuration since
// ordinary Salsa20 is randomly seek-able. This is good for a cipher

View File

@ -118,7 +118,7 @@ public:
inline bool sha512PrivateKey(void *sha) const
{
if (_privateKey) {
SHA512::hash(sha,_privateKey->data,ZT_C25519_PRIVATE_KEY_LEN);
SHA512(sha,_privateKey->data,ZT_C25519_PRIVATE_KEY_LEN);
return true;
}
return false;

View File

@ -294,7 +294,7 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool
outp.append((uint8_t)Packet::VERB_HELLO);
outp.append((uint64_t)pid);
outp.append((uint8_t)Packet::ERROR_IDENTITY_COLLISION);
outp.armor(key,true);
outp.armor(key,true,peer->aesKeysIfSupported());
_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
} else {
RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,pid,fromAddress,hops(),"invalid MAC");
@ -444,8 +444,8 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool
}
outp.setAt<uint16_t>(worldUpdateSizeAt,(uint16_t)(outp.size() - (worldUpdateSizeAt + 2)));
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
peer->recordOutgoingPacket(_path,outp.packetId(),outp.payloadLength(),outp.verb(),ZT_QOS_NO_FLOW,now);
outp.armor(peer->key(),true);
_path->send(RR,tPtr,outp.data(),outp.size(),now);
peer->setRemoteVersion(protoVersion,vMajor,vMinor,vRevision); // important for this to go first so received() knows the version
@ -593,7 +593,7 @@ bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,void *tPtr,const Shar
}
if (count > 0) {
outp.armor(peer->key(),true);
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
}
@ -816,8 +816,8 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,void *tPtr,const
outp.append((uint64_t)packetId());
outp.append((uint64_t)nwid);
const int64_t now = RR->node->now();
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
peer->recordOutgoingPacket(_path,outp.packetId(),outp.payloadLength(),outp.verb(),ZT_QOS_NO_FLOW,now);
outp.armor(peer->key(),true);
_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
}
@ -842,8 +842,8 @@ bool IncomingPacket::_doECHO(const RuntimeEnvironment *RR,void *tPtr,const Share
outp.append((uint64_t)pid);
if (size() > ZT_PACKET_IDX_PAYLOAD)
outp.append(reinterpret_cast<const unsigned char *>(data()) + ZT_PACKET_IDX_PAYLOAD,size() - ZT_PACKET_IDX_PAYLOAD);
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
peer->recordOutgoingPacket(_path,outp.packetId(),outp.payloadLength(),outp.verb(),ZT_QOS_NO_FLOW,now);
outp.armor(peer->key(),true);
_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
peer->received(tPtr,_path,hops(),pid,payloadLength(),Packet::VERB_ECHO,0,Packet::VERB_NOP,false,0,ZT_QOS_NO_FLOW);
@ -1016,7 +1016,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,void
outp.append(requestPacketId);
outp.append((unsigned char)Packet::ERROR_UNSUPPORTED_OPERATION);
outp.append(nwid);
outp.armor(peer->key(),true);
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
}
@ -1037,8 +1037,8 @@ bool IncomingPacket::_doNETWORK_CONFIG(const RuntimeEnvironment *RR,void *tPtr,c
outp.append((uint64_t)network->id());
outp.append((uint64_t)configUpdateId);
const int64_t now = RR->node->now();
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
peer->recordOutgoingPacket(_path,outp.packetId(),outp.payloadLength(),outp.verb(),ZT_QOS_NO_FLOW,now);
outp.armor(peer->key(),true);
_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
}
}
@ -1086,8 +1086,8 @@ bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,void *tPtr
outp.append((uint32_t)mg.adi());
const unsigned int gatheredLocally = RR->mc->gather(peer->address(),nwid,mg,outp,gatherLimit);
if (gatheredLocally > 0) {
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
peer->recordOutgoingPacket(_path,outp.packetId(),outp.payloadLength(),outp.verb(),ZT_QOS_NO_FLOW,now);
outp.armor(peer->key(),true);
_path->send(RR,tPtr,outp.data(),outp.size(),now);
}
}
@ -1185,8 +1185,8 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,void *tPtr,
outp.append((unsigned char)0x02); // flag 0x02 = contains gather results
if (RR->mc->gather(peer->address(),nwid,to,outp,gatherLimit)) {
const int64_t now = RR->node->now();
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
peer->recordOutgoingPacket(_path,outp.packetId(),outp.payloadLength(),outp.verb(),ZT_QOS_NO_FLOW,now);
outp.armor(peer->key(),true);
_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
}
}
@ -1327,7 +1327,7 @@ void IncomingPacket::_sendErrorNeedCredentials(const RuntimeEnvironment *RR,void
outp.append(packetId());
outp.append((uint8_t)Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE);
outp.append(nwid);
outp.armor(peer->key(),true);
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
}

View File

@ -194,7 +194,7 @@ void Multicaster::send(
outp.append((uint16_t)etherType);
outp.append(data,len);
if (!network->config().disableCompression()) outp.compress();
outp.armor(bestMulticastReplicator->key(),true);
outp.armor(bestMulticastReplicator->key(),true,bestMulticastReplicator->aesKeysIfSupported());
bestMulticastReplicatorPath->send(RR,tPtr,outp.data(),outp.size(),now);
return;
}

View File

@ -876,39 +876,61 @@ static inline int LZ4_decompress_safe(const char* source, char* dest, int compre
const unsigned char Packet::ZERO_KEY[32] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
void Packet::armor(const void *key,bool encryptPayload)
void Packet::armor(const void *key,bool encryptPayload,const AES aesKeys[2])
{
uint8_t mangledKey[32];
uint8_t *const data = reinterpret_cast<uint8_t *>(unsafeData());
if ((aesKeys) && (encryptPayload)) {
setCipher(ZT_PROTO_CIPHER_SUITE__AES_GMAC_SIV);
// Set flag now, since it affects key mangle function
setCipher(encryptPayload ? ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012 : ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_NONE);
_salsa20MangleKey((const unsigned char *)key,mangledKey);
if (ZT_HAS_FAST_CRYPTO()) {
const unsigned int encryptLen = (encryptPayload) ? (size() - ZT_PACKET_IDX_VERB) : 0;
uint64_t keyStream[(ZT_PROTO_MAX_PACKET_LENGTH + 64 + 8) / 8];
ZT_FAST_SINGLE_PASS_SALSA2012(keyStream,encryptLen + 64,(data + ZT_PACKET_IDX_IV),mangledKey);
Salsa20::memxor(data + ZT_PACKET_IDX_VERB,reinterpret_cast<const uint8_t *>(keyStream + 8),encryptLen);
uint64_t mac[2];
Poly1305::compute(mac,data + ZT_PACKET_IDX_VERB,size() - ZT_PACKET_IDX_VERB,keyStream);
#ifdef ZT_NO_TYPE_PUNNING
memcpy(data + ZT_PACKET_IDX_MAC,mac,8);
#else
(*reinterpret_cast<uint64_t *>(data + ZT_PACKET_IDX_MAC)) = mac[0];
#endif
} else {
Salsa20 s20(mangledKey,data + ZT_PACKET_IDX_IV);
uint64_t macKey[4];
s20.crypt12(ZERO_KEY,macKey,sizeof(macKey));
uint8_t *const payload = data + ZT_PACKET_IDX_VERB;
const unsigned int payloadLen = size() - ZT_PACKET_IDX_VERB;
if (encryptPayload)
s20.crypt12(payload,payload,payloadLen);
uint64_t mac[2];
Poly1305::compute(mac,payload,payloadLen,macKey);
memcpy(data + ZT_PACKET_IDX_MAC,mac,8);
AES::GMACSIVEncryptor enc(aesKeys[0],aesKeys[1]);
enc.init(Utils::loadMachineEndian<uint64_t>(data + ZT_PACKET_IDX_IV),payload);
enc.aad(data + ZT_PACKET_IDX_DEST,11);
enc.update1(payload,payloadLen);
enc.finish1();
enc.update2(payload,payloadLen);
const uint64_t *const tag = enc.finish2();
#ifdef ZT_NO_UNALIGNED_ACCESS
Utils::copy<8>(data,tag);
Utils::copy<8>(data + ZT_PACKET_IDX_MAC,tag + 1);
#else
*reinterpret_cast<uint64_t *>(data) = tag[0];
*reinterpret_cast<uint64_t *>(data + ZT_PACKET_IDX_MAC) = tag[1];
#endif
} else {
uint8_t mangledKey[32];
setCipher(encryptPayload ? ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012 : ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_NONE);
_salsa20MangleKey((const unsigned char *)key,mangledKey);
if (ZT_HAS_FAST_CRYPTO()) {
const unsigned int encryptLen = (encryptPayload) ? (size() - ZT_PACKET_IDX_VERB) : 0;
uint64_t keyStream[(ZT_PROTO_MAX_PACKET_LENGTH + 64 + 8) / 8];
ZT_FAST_SINGLE_PASS_SALSA2012(keyStream,encryptLen + 64,(data + ZT_PACKET_IDX_IV),mangledKey);
Salsa20::memxor(data + ZT_PACKET_IDX_VERB,reinterpret_cast<const uint8_t *>(keyStream + 8),encryptLen);
uint64_t mac[2];
Poly1305::compute(mac,data + ZT_PACKET_IDX_VERB,size() - ZT_PACKET_IDX_VERB,keyStream);
#ifdef ZT_NO_TYPE_PUNNING
memcpy(data + ZT_PACKET_IDX_MAC,mac,8);
#else
(*reinterpret_cast<uint64_t *>(data + ZT_PACKET_IDX_MAC)) = mac[0];
#endif
} else {
Salsa20 s20(mangledKey,data + ZT_PACKET_IDX_IV);
uint64_t macKey[4];
s20.crypt12(ZERO_KEY,macKey,sizeof(macKey));
uint8_t *const payload = data + ZT_PACKET_IDX_VERB;
const unsigned int payloadLen = size() - ZT_PACKET_IDX_VERB;
if (encryptPayload)
s20.crypt12(payload,payload,payloadLen);
uint64_t mac[2];
Poly1305::compute(mac,payload,payloadLen,macKey);
memcpy(data + ZT_PACKET_IDX_MAC,mac,8);
}
}
}

View File

@ -26,6 +26,7 @@
#include "Address.hpp"
#include "Poly1305.hpp"
#include "Salsa20.hpp"
#include "AES.hpp"
#include "Utils.hpp"
#include "Buffer.hpp"
@ -55,10 +56,12 @@
* + Tags and Capabilities
* + Inline push of CertificateOfMembership deprecated
* 9 - 1.2.0 ... 1.2.14
* 10 - 1.4.0 ... CURRENT
* + Multipath capability and load balancing (tentative)
* 10 - 1.4.0 ... 1.4.6
* 11 - 1.4.8 ... end of 1.4 series
* + Multipath capability and load balancing (beta)
* + AES-GMAC-SIV backported for faster peer-to-peer crypto
*/
#define ZT_PROTO_VERSION 10
#define ZT_PROTO_VERSION 11
/**
* Minimum supported protocol version
@ -96,6 +99,21 @@
*/
#define ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012 1
/**
* AES-GMAC-SIV backported from 2.x
*/
#define ZT_PROTO_CIPHER_SUITE__AES_GMAC_SIV 3
/**
* AES-GMAC-SIV first of two keys
*/
#define ZT_KBKDF_LABEL_AES_GMAC_SIV_K0 '0'
/**
* AES-GMAC-SIV second of two keys
*/
#define ZT_KBKDF_LABEL_AES_GMAC_SIV_K1 '1'
/**
* Cipher suite: NONE
*
@ -1295,8 +1313,9 @@ public:
*
* @param key 32-byte key
* @param encryptPayload If true, encrypt packet payload, else just MAC
* @param aes Use new AES-GMAC-SIV constrution
*/
void armor(const void *key,bool encryptPayload);
void armor(const void *key,bool encryptPayload,const AES aesKeys[2]);
/**
* Verify and (if encrypted) decrypt packet

View File

@ -61,6 +61,13 @@ Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Ident
if (!myIdentity.agree(peerIdentity,_key,ZT_PEER_SECRET_KEY_LENGTH)) {
throw ZT_EXCEPTION_INVALID_ARGUMENT;
}
uint8_t ktmp[32];
KBKDFHMACSHA384(_key,ZT_KBKDF_LABEL_AES_GMAC_SIV_K0,0,0,ktmp);
_aesKeys[0].init(ktmp);
KBKDFHMACSHA384(_key,ZT_KBKDF_LABEL_AES_GMAC_SIV_K1,0,0,ktmp);
_aesKeys[0].init(ktmp);
Utils::burn(ktmp, 32);
}
void Peer::received(
@ -209,7 +216,7 @@ void Peer::received(
if (count) {
outp->setAt(ZT_PACKET_IDX_PAYLOAD,(uint16_t)count);
outp->compress();
outp->armor(_key,true);
outp->armor(_key,true,aesKeysIfSupported());
path->send(RR,tPtr,outp->data(),outp->size(),now);
}
delete outp;
@ -347,7 +354,7 @@ void Peer::introduce(void *const tPtr,const int64_t now,const SharedPtr<Peer> &o
outp.append((uint8_t)4);
outp.append(other->_paths[theirs].p->address().rawIpData(),4);
}
outp.armor(_key,true);
outp.armor(_key,true,aesKeysIfSupported());
_paths[mine].p->send(RR,tPtr,outp.data(),outp.size(),now);
} else {
Packet outp(other->_id.address(),RR->identity.address(),Packet::VERB_RENDEZVOUS);
@ -361,7 +368,7 @@ void Peer::introduce(void *const tPtr,const int64_t now,const SharedPtr<Peer> &o
outp.append((uint8_t)4);
outp.append(_paths[mine].p->address().rawIpData(),4);
}
outp.armor(other->_key,true);
outp.armor(other->_key,true,aesKeysIfSupported());
other->_paths[theirs].p->send(RR,tPtr,outp.data(),outp.size(),now);
}
++alt;
@ -402,12 +409,12 @@ void Peer::sendHELLO(void *tPtr,const int64_t localSocket,const InetAddress &atA
outp.cryptField(_key,startCryptedPortionAt,outp.size() - startCryptedPortionAt);
RR->node->expectReplyTo(outp.packetId());
if (atAddress) {
outp.armor(_key,false); // false == don't encrypt full payload, but add MAC
outp.armor(_key,false,aesKeysIfSupported()); // false == don't encrypt full payload, but add MAC
RR->node->expectReplyTo(outp.packetId());
RR->node->putPacket(tPtr,localSocket,atAddress,outp.data(),outp.size());
} else {
RR->node->expectReplyTo(outp.packetId());
RR->sw->send(tPtr,outp,false); // false == don't encrypt full payload, but add MAC
}
}
@ -416,8 +423,8 @@ void Peer::attemptToContactAt(void *tPtr,const int64_t localSocket,const InetAdd
{
if ( (!sendFullHello) && (_vProto >= 5) && (!((_vMajor == 1)&&(_vMinor == 1)&&(_vRevision == 0))) ) {
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_ECHO);
outp.armor(_key,true,aesKeysIfSupported());
RR->node->expectReplyTo(outp.packetId());
outp.armor(_key,true);
RR->node->putPacket(tPtr,localSocket,atAddress,outp.data(),outp.size());
} else {
sendHELLO(tPtr,localSocket,atAddress,now);

View File

@ -33,6 +33,7 @@
#include "Mutex.hpp"
#include "Bond.hpp"
#include "BondController.hpp"
#include "AES.hpp"
#define ZT_PEER_MAX_SERIALIZED_STATE_SIZE (sizeof(Peer) + 32 + (sizeof(Path) * 2))
@ -532,6 +533,9 @@ public:
*/
inline int8_t bondingPolicy() { return _bondingPolicy; }
const AES *aesKeysIfSupported() const
{ return (_vProto >= 10) ? _aesKeys : (const AES *)0; }
private:
struct _PeerPath
{
@ -542,6 +546,7 @@ private:
};
uint8_t _key[ZT_PEER_SECRET_KEY_LENGTH];
AES _aesKeys[2];
const RuntimeEnvironment *RR;

View File

@ -1,367 +1,274 @@
// Code taken from NaCl by D. J. Bernstein and others
// Public domain
/*
20080913
D. J. Bernstein
Public domain.
*/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
// This code is public domain, taken from a PD crypto source file on GitHub.
#include "SHA512.hpp"
#include "Utils.hpp"
#ifdef __APPLE__
#include <CommonCrypto/CommonDigest.h>
#define ZT_HAVE_NATIVE_SHA512
namespace ZeroTier {
void SHA512::hash(void *digest,const void *data,unsigned int len)
{
CC_SHA512_CTX ctx;
CC_SHA512_Init(&ctx);
CC_SHA512_Update(&ctx,data,len);
CC_SHA512_Final(reinterpret_cast<unsigned char *>(digest),&ctx);
}
}
#endif
#ifdef ZT_USE_LIBCRYPTO
#include <openssl/sha.h>
#define ZT_HAVE_NATIVE_SHA512
namespace ZeroTier {
void SHA512::hash(void *digest,const void *data,unsigned int len)
{
SHA512_CTX ctx;
SHA512_Init(&ctx);
SHA512_Update(&ctx,data,len);
SHA512_Final(reinterpret_cast<unsigned char *>(digest),&ctx);
}
}
#endif
#ifndef ZT_HAVE_NATIVE_SHA512
namespace ZeroTier {
namespace {
#define uint64 uint64_t
#ifdef ZT_NO_TYPE_PUNNING
static uint64 load_bigendian(const unsigned char *x)
{
return
(uint64) (x[7]) \
| (((uint64) (x[6])) << 8) \
| (((uint64) (x[5])) << 16) \
| (((uint64) (x[4])) << 24) \
| (((uint64) (x[3])) << 32) \
| (((uint64) (x[2])) << 40) \
| (((uint64) (x[1])) << 48) \
| (((uint64) (x[0])) << 56)
;
}
static void store_bigendian(unsigned char *x,uint64 u)
{
x[7] = u; u >>= 8;
x[6] = u; u >>= 8;
x[5] = u; u >>= 8;
x[4] = u; u >>= 8;
x[3] = u; u >>= 8;
x[2] = u; u >>= 8;
x[1] = u; u >>= 8;
x[0] = u;
}
#else // !ZT_NO_TYPE_PUNNING
#define load_bigendian(x) Utils::ntoh(*((const uint64_t *)(x)))
#define store_bigendian(x,u) (*((uint64_t *)(x)) = Utils::hton((u)))
#endif // ZT_NO_TYPE_PUNNING
#define SHR(x,c) ((x) >> (c))
#define ROTR(x,c) (((x) >> (c)) | ((x) << (64 - (c))))
#define Ch(x,y,z) ((x & y) ^ (~x & z))
#define Maj(x,y,z) ((x & y) ^ (x & z) ^ (y & z))
#define Sigma0(x) (ROTR(x,28) ^ ROTR(x,34) ^ ROTR(x,39))
#define Sigma1(x) (ROTR(x,14) ^ ROTR(x,18) ^ ROTR(x,41))
#define sigma0(x) (ROTR(x, 1) ^ ROTR(x, 8) ^ SHR(x,7))
#define sigma1(x) (ROTR(x,19) ^ ROTR(x,61) ^ SHR(x,6))
#define M(w0,w14,w9,w1) w0 = sigma1(w14) + w9 + sigma0(w1) + w0;
#define EXPAND \
M(w0 ,w14,w9 ,w1 ) \
M(w1 ,w15,w10,w2 ) \
M(w2 ,w0 ,w11,w3 ) \
M(w3 ,w1 ,w12,w4 ) \
M(w4 ,w2 ,w13,w5 ) \
M(w5 ,w3 ,w14,w6 ) \
M(w6 ,w4 ,w15,w7 ) \
M(w7 ,w5 ,w0 ,w8 ) \
M(w8 ,w6 ,w1 ,w9 ) \
M(w9 ,w7 ,w2 ,w10) \
M(w10,w8 ,w3 ,w11) \
M(w11,w9 ,w4 ,w12) \
M(w12,w10,w5 ,w13) \
M(w13,w11,w6 ,w14) \
M(w14,w12,w7 ,w15) \
M(w15,w13,w8 ,w0 )
#define F(w,k) \
T1 = h + Sigma1(e) + Ch(e,f,g) + k + w; \
T2 = Sigma0(a) + Maj(a,b,c); \
h = g; \
g = f; \
f = e; \
e = d + T1; \
d = c; \
c = b; \
b = a; \
a = T1 + T2;
static inline int crypto_hashblocks(unsigned char *statebytes,const unsigned char *in,unsigned long long inlen)
{
uint64 state[8];
uint64 a;
uint64 b;
uint64 c;
uint64 d;
uint64 e;
uint64 f;
uint64 g;
uint64 h;
uint64 T1;
uint64 T2;
a = load_bigendian(statebytes + 0); state[0] = a;
b = load_bigendian(statebytes + 8); state[1] = b;
c = load_bigendian(statebytes + 16); state[2] = c;
d = load_bigendian(statebytes + 24); state[3] = d;
e = load_bigendian(statebytes + 32); state[4] = e;
f = load_bigendian(statebytes + 40); state[5] = f;
g = load_bigendian(statebytes + 48); state[6] = g;
h = load_bigendian(statebytes + 56); state[7] = h;
while (inlen >= 128) {
uint64 w0 = load_bigendian(in + 0);
uint64 w1 = load_bigendian(in + 8);
uint64 w2 = load_bigendian(in + 16);
uint64 w3 = load_bigendian(in + 24);
uint64 w4 = load_bigendian(in + 32);
uint64 w5 = load_bigendian(in + 40);
uint64 w6 = load_bigendian(in + 48);
uint64 w7 = load_bigendian(in + 56);
uint64 w8 = load_bigendian(in + 64);
uint64 w9 = load_bigendian(in + 72);
uint64 w10 = load_bigendian(in + 80);
uint64 w11 = load_bigendian(in + 88);
uint64 w12 = load_bigendian(in + 96);
uint64 w13 = load_bigendian(in + 104);
uint64 w14 = load_bigendian(in + 112);
uint64 w15 = load_bigendian(in + 120);
F(w0 ,0x428a2f98d728ae22ULL)
F(w1 ,0x7137449123ef65cdULL)
F(w2 ,0xb5c0fbcfec4d3b2fULL)
F(w3 ,0xe9b5dba58189dbbcULL)
F(w4 ,0x3956c25bf348b538ULL)
F(w5 ,0x59f111f1b605d019ULL)
F(w6 ,0x923f82a4af194f9bULL)
F(w7 ,0xab1c5ed5da6d8118ULL)
F(w8 ,0xd807aa98a3030242ULL)
F(w9 ,0x12835b0145706fbeULL)
F(w10,0x243185be4ee4b28cULL)
F(w11,0x550c7dc3d5ffb4e2ULL)
F(w12,0x72be5d74f27b896fULL)
F(w13,0x80deb1fe3b1696b1ULL)
F(w14,0x9bdc06a725c71235ULL)
F(w15,0xc19bf174cf692694ULL)
EXPAND
F(w0 ,0xe49b69c19ef14ad2ULL)
F(w1 ,0xefbe4786384f25e3ULL)
F(w2 ,0x0fc19dc68b8cd5b5ULL)
F(w3 ,0x240ca1cc77ac9c65ULL)
F(w4 ,0x2de92c6f592b0275ULL)
F(w5 ,0x4a7484aa6ea6e483ULL)
F(w6 ,0x5cb0a9dcbd41fbd4ULL)
F(w7 ,0x76f988da831153b5ULL)
F(w8 ,0x983e5152ee66dfabULL)
F(w9 ,0xa831c66d2db43210ULL)
F(w10,0xb00327c898fb213fULL)
F(w11,0xbf597fc7beef0ee4ULL)
F(w12,0xc6e00bf33da88fc2ULL)
F(w13,0xd5a79147930aa725ULL)
F(w14,0x06ca6351e003826fULL)
F(w15,0x142929670a0e6e70ULL)
EXPAND
F(w0 ,0x27b70a8546d22ffcULL)
F(w1 ,0x2e1b21385c26c926ULL)
F(w2 ,0x4d2c6dfc5ac42aedULL)
F(w3 ,0x53380d139d95b3dfULL)
F(w4 ,0x650a73548baf63deULL)
F(w5 ,0x766a0abb3c77b2a8ULL)
F(w6 ,0x81c2c92e47edaee6ULL)
F(w7 ,0x92722c851482353bULL)
F(w8 ,0xa2bfe8a14cf10364ULL)
F(w9 ,0xa81a664bbc423001ULL)
F(w10,0xc24b8b70d0f89791ULL)
F(w11,0xc76c51a30654be30ULL)
F(w12,0xd192e819d6ef5218ULL)
F(w13,0xd69906245565a910ULL)
F(w14,0xf40e35855771202aULL)
F(w15,0x106aa07032bbd1b8ULL)
EXPAND
F(w0 ,0x19a4c116b8d2d0c8ULL)
F(w1 ,0x1e376c085141ab53ULL)
F(w2 ,0x2748774cdf8eeb99ULL)
F(w3 ,0x34b0bcb5e19b48a8ULL)
F(w4 ,0x391c0cb3c5c95a63ULL)
F(w5 ,0x4ed8aa4ae3418acbULL)
F(w6 ,0x5b9cca4f7763e373ULL)
F(w7 ,0x682e6ff3d6b2b8a3ULL)
F(w8 ,0x748f82ee5defb2fcULL)
F(w9 ,0x78a5636f43172f60ULL)
F(w10,0x84c87814a1f0ab72ULL)
F(w11,0x8cc702081a6439ecULL)
F(w12,0x90befffa23631e28ULL)
F(w13,0xa4506cebde82bde9ULL)
F(w14,0xbef9a3f7b2c67915ULL)
F(w15,0xc67178f2e372532bULL)
EXPAND
F(w0 ,0xca273eceea26619cULL)
F(w1 ,0xd186b8c721c0c207ULL)
F(w2 ,0xeada7dd6cde0eb1eULL)
F(w3 ,0xf57d4f7fee6ed178ULL)
F(w4 ,0x06f067aa72176fbaULL)
F(w5 ,0x0a637dc5a2c898a6ULL)
F(w6 ,0x113f9804bef90daeULL)
F(w7 ,0x1b710b35131c471bULL)
F(w8 ,0x28db77f523047d84ULL)
F(w9 ,0x32caab7b40c72493ULL)
F(w10,0x3c9ebe0a15c9bebcULL)
F(w11,0x431d67c49c100d4cULL)
F(w12,0x4cc5d4becb3e42b6ULL)
F(w13,0x597f299cfc657e2aULL)
F(w14,0x5fcb6fab3ad6faecULL)
F(w15,0x6c44198c4a475817ULL)
a += state[0];
b += state[1];
c += state[2];
d += state[3];
e += state[4];
f += state[5];
g += state[6];
h += state[7];
state[0] = a;
state[1] = b;
state[2] = c;
state[3] = d;
state[4] = e;
state[5] = f;
state[6] = g;
state[7] = h;
in += 128;
inlen -= 128;
}
store_bigendian(statebytes + 0,state[0]);
store_bigendian(statebytes + 8,state[1]);
store_bigendian(statebytes + 16,state[2]);
store_bigendian(statebytes + 24,state[3]);
store_bigendian(statebytes + 32,state[4]);
store_bigendian(statebytes + 40,state[5]);
store_bigendian(statebytes + 48,state[6]);
store_bigendian(statebytes + 56,state[7]);
return 0;
}
#define blocks crypto_hashblocks
static const unsigned char iv[64] = {
0x6a,0x09,0xe6,0x67,0xf3,0xbc,0xc9,0x08,
0xbb,0x67,0xae,0x85,0x84,0xca,0xa7,0x3b,
0x3c,0x6e,0xf3,0x72,0xfe,0x94,0xf8,0x2b,
0xa5,0x4f,0xf5,0x3a,0x5f,0x1d,0x36,0xf1,
0x51,0x0e,0x52,0x7f,0xad,0xe6,0x82,0xd1,
0x9b,0x05,0x68,0x8c,0x2b,0x3e,0x6c,0x1f,
0x1f,0x83,0xd9,0xab,0xfb,0x41,0xbd,0x6b,
0x5b,0xe0,0xcd,0x19,0x13,0x7e,0x21,0x79
struct sha512_state {
uint64_t length,state[8];
unsigned long curlen;
uint8_t buf[128];
};
void SHA512::hash(void *digest,const void *data,unsigned int len)
static const uint64_t K[80] = {
0x428a2f98d728ae22ULL,0x7137449123ef65cdULL,0xb5c0fbcfec4d3b2fULL,0xe9b5dba58189dbbcULL,
0x3956c25bf348b538ULL,0x59f111f1b605d019ULL,0x923f82a4af194f9bULL,0xab1c5ed5da6d8118ULL,
0xd807aa98a3030242ULL,0x12835b0145706fbeULL,0x243185be4ee4b28cULL,0x550c7dc3d5ffb4e2ULL,
0x72be5d74f27b896fULL,0x80deb1fe3b1696b1ULL,0x9bdc06a725c71235ULL,0xc19bf174cf692694ULL,
0xe49b69c19ef14ad2ULL,0xefbe4786384f25e3ULL,0x0fc19dc68b8cd5b5ULL,0x240ca1cc77ac9c65ULL,
0x2de92c6f592b0275ULL,0x4a7484aa6ea6e483ULL,0x5cb0a9dcbd41fbd4ULL,0x76f988da831153b5ULL,
0x983e5152ee66dfabULL,0xa831c66d2db43210ULL,0xb00327c898fb213fULL,0xbf597fc7beef0ee4ULL,
0xc6e00bf33da88fc2ULL,0xd5a79147930aa725ULL,0x06ca6351e003826fULL,0x142929670a0e6e70ULL,
0x27b70a8546d22ffcULL,0x2e1b21385c26c926ULL,0x4d2c6dfc5ac42aedULL,0x53380d139d95b3dfULL,
0x650a73548baf63deULL,0x766a0abb3c77b2a8ULL,0x81c2c92e47edaee6ULL,0x92722c851482353bULL,
0xa2bfe8a14cf10364ULL,0xa81a664bbc423001ULL,0xc24b8b70d0f89791ULL,0xc76c51a30654be30ULL,
0xd192e819d6ef5218ULL,0xd69906245565a910ULL,0xf40e35855771202aULL,0x106aa07032bbd1b8ULL,
0x19a4c116b8d2d0c8ULL,0x1e376c085141ab53ULL,0x2748774cdf8eeb99ULL,0x34b0bcb5e19b48a8ULL,
0x391c0cb3c5c95a63ULL,0x4ed8aa4ae3418acbULL,0x5b9cca4f7763e373ULL,0x682e6ff3d6b2b8a3ULL,
0x748f82ee5defb2fcULL,0x78a5636f43172f60ULL,0x84c87814a1f0ab72ULL,0x8cc702081a6439ecULL,
0x90befffa23631e28ULL,0xa4506cebde82bde9ULL,0xbef9a3f7b2c67915ULL,0xc67178f2e372532bULL,
0xca273eceea26619cULL,0xd186b8c721c0c207ULL,0xeada7dd6cde0eb1eULL,0xf57d4f7fee6ed178ULL,
0x06f067aa72176fbaULL,0x0a637dc5a2c898a6ULL,0x113f9804bef90daeULL,0x1b710b35131c471bULL,
0x28db77f523047d84ULL,0x32caab7b40c72493ULL,0x3c9ebe0a15c9bebcULL,0x431d67c49c100d4cULL,
0x4cc5d4becb3e42b6ULL,0x597f299cfc657e2aULL,0x5fcb6fab3ad6faecULL,0x6c44198c4a475817ULL
};
#define STORE64H(x, y) Utils::storeBigEndian<uint64_t>(y,x)
#define LOAD64H(x, y) x = Utils::loadBigEndian<uint64_t>(y)
#define ROL64c(x,y) (((x)<<(y)) | ((x)>>(64-(y))))
#define ROR64c(x,y) (((x)>>(y)) | ((x)<<(64-(y))))
#define Ch(x,y,z) (z ^ (x & (y ^ z)))
#define Maj(x,y,z) (((x | y) & z) | (x & y))
#define S(x, n) ROR64c(x, n)
#define R(x, n) ((x)>>(n))
#define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39))
#define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41))
#define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7))
#define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6))
static ZT_INLINE void sha512_compress(sha512_state *const md,uint8_t *const buf)
{
unsigned char h[64];
unsigned char padded[256];
uint64_t S[8], W[80], t0, t1;
int i;
uint64_t bytes = len;
const unsigned char *in = (const unsigned char *)data;
unsigned int inlen = len;
for (i = 0; i < 8; i++)
S[i] = md->state[i];
for (i = 0; i < 16; i++)
LOAD64H(W[i], buf + (8*i));
for (i = 16; i < 80; i++)
W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16];
for (i = 0;i < 64;++i) h[i] = iv[i];
#define RND(a,b,c,d,e,f,g,h,i) \
t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \
t1 = Sigma0(a) + Maj(a, b, c); \
d += t0; \
h = t0 + t1;
blocks(h,in,inlen);
in += inlen;
inlen &= 127;
in -= inlen;
for (i = 0;i < (int)inlen;++i) padded[i] = in[i];
padded[inlen] = 0x80;
if (inlen < 112) {
for (i = inlen + 1;i < 119;++i) padded[i] = 0;
padded[119] = (unsigned char)((bytes >> 61) & 0xff);
padded[120] = (unsigned char)((bytes >> 53) & 0xff);
padded[121] = (unsigned char)((bytes >> 45) & 0xff);
padded[122] = (unsigned char)((bytes >> 37) & 0xff);
padded[123] = (unsigned char)((bytes >> 29) & 0xff);
padded[124] = (unsigned char)((bytes >> 21) & 0xff);
padded[125] = (unsigned char)((bytes >> 13) & 0xff);
padded[126] = (unsigned char)((bytes >> 5) & 0xff);
padded[127] = (unsigned char)((bytes << 3) & 0xff);
blocks(h,padded,128);
} else {
for (i = inlen + 1;i < 247;++i) padded[i] = 0;
padded[247] = (unsigned char)((bytes >> 61) & 0xff);
padded[248] = (unsigned char)((bytes >> 53) & 0xff);
padded[249] = (unsigned char)((bytes >> 45) & 0xff);
padded[250] = (unsigned char)((bytes >> 37) & 0xff);
padded[251] = (unsigned char)((bytes >> 29) & 0xff);
padded[252] = (unsigned char)((bytes >> 21) & 0xff);
padded[253] = (unsigned char)((bytes >> 13) & 0xff);
padded[254] = (unsigned char)((bytes >> 5) & 0xff);
padded[255] = (unsigned char)((bytes << 3) & 0xff);
blocks(h,padded,256);
for (i = 0; i < 80; i += 8) {
RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i+0);
RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],i+1);
RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],i+2);
RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],i+3);
RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],i+4);
RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],i+5);
RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],i+6);
RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],i+7);
}
for (i = 0;i < 64;++i) ((unsigned char *)digest)[i] = h[i];
for (i = 0; i < 8; i++)
md->state[i] = md->state[i] + S[i];
}
} // namespace ZeroTier
static ZT_INLINE void sha384_init(sha512_state *const md)
{
md->curlen = 0;
md->length = 0;
md->state[0] = 0xcbbb9d5dc1059ed8ULL;
md->state[1] = 0x629a292a367cd507ULL;
md->state[2] = 0x9159015a3070dd17ULL;
md->state[3] = 0x152fecd8f70e5939ULL;
md->state[4] = 0x67332667ffc00b31ULL;
md->state[5] = 0x8eb44a8768581511ULL;
md->state[6] = 0xdb0c2e0d64f98fa7ULL;
md->state[7] = 0x47b5481dbefa4fa4ULL;
}
static ZT_INLINE void sha512_init(sha512_state *const md)
{
md->curlen = 0;
md->length = 0;
md->state[0] = 0x6a09e667f3bcc908ULL;
md->state[1] = 0xbb67ae8584caa73bULL;
md->state[2] = 0x3c6ef372fe94f82bULL;
md->state[3] = 0xa54ff53a5f1d36f1ULL;
md->state[4] = 0x510e527fade682d1ULL;
md->state[5] = 0x9b05688c2b3e6c1fULL;
md->state[6] = 0x1f83d9abfb41bd6bULL;
md->state[7] = 0x5be0cd19137e2179ULL;
}
static void sha512_process(sha512_state *const md,const uint8_t *in,unsigned long inlen)
{
while (inlen > 0) {
if (md->curlen == 0 && inlen >= 128) {
sha512_compress(md,(uint8_t *)in);
md->length += 128 * 8;
in += 128;
inlen -= 128;
} else {
unsigned long n = std::min(inlen,(128 - md->curlen));
Utils::copy(md->buf + md->curlen,in,n);
md->curlen += n;
in += n;
inlen -= n;
if (md->curlen == 128) {
sha512_compress(md,md->buf);
md->length += 8*128;
md->curlen = 0;
}
}
}
}
static ZT_INLINE void sha512_done(sha512_state *const md,uint8_t *out)
{
int i;
md->length += md->curlen * 8ULL;
md->buf[md->curlen++] = (uint8_t)0x80;
if (md->curlen > 112) {
while (md->curlen < 128) {
md->buf[md->curlen++] = (uint8_t)0;
}
sha512_compress(md, md->buf);
md->curlen = 0;
}
while (md->curlen < 120) {
md->buf[md->curlen++] = (uint8_t)0;
}
STORE64H(md->length, md->buf+120);
sha512_compress(md, md->buf);
for (i = 0; i < 8; i++) {
STORE64H(md->state[i], out+(8*i));
}
}
} // anonymous namespace
void SHA512(void *digest,const void *data,unsigned int len)
{
sha512_state state;
sha512_init(&state);
sha512_process(&state,(uint8_t *)data,(unsigned long)len);
sha512_done(&state,(uint8_t *)digest);
}
void SHA384(void *digest,const void *data,unsigned int len)
{
uint8_t tmp[64];
sha512_state state;
sha384_init(&state);
sha512_process(&state,(uint8_t *)data,(unsigned long)len);
sha512_done(&state,tmp);
Utils::copy<48>(digest,tmp);
}
void SHA384(void *digest,const void *data0,unsigned int len0,const void *data1,unsigned int len1)
{
uint8_t tmp[64];
sha512_state state;
sha384_init(&state);
sha512_process(&state,(uint8_t *)data0,(unsigned long)len0);
sha512_process(&state,(uint8_t *)data1,(unsigned long)len1);
sha512_done(&state,tmp);
Utils::copy<48>(digest,tmp);
}
#endif // !ZT_HAVE_NATIVE_SHA512
// Internally re-export to included C code, which includes some fast crypto code ported in on some platforms.
// This eliminates the need to link against a third party SHA512() from this code
extern "C" void ZT_sha512internal(void *digest,const void *data,unsigned int len)
void HMACSHA384(const uint8_t key[ZT_SYMMETRIC_KEY_SIZE],const void *msg,const unsigned int msglen,uint8_t mac[48])
{
ZeroTier::SHA512::hash(digest,data,len);
uint64_t kInPadded[16]; // input padded key
uint64_t outer[22]; // output padded key | H(input padded key | msg)
const uint64_t k0 = Utils::loadMachineEndian< uint64_t >(key);
const uint64_t k1 = Utils::loadMachineEndian< uint64_t >(key + 8);
const uint64_t k2 = Utils::loadMachineEndian< uint64_t >(key + 16);
const uint64_t k3 = Utils::loadMachineEndian< uint64_t >(key + 24);
const uint64_t k4 = Utils::loadMachineEndian< uint64_t >(key + 32);
const uint64_t k5 = Utils::loadMachineEndian< uint64_t >(key + 40);
const uint64_t ipad = 0x3636363636363636ULL;
kInPadded[0] = k0 ^ ipad;
kInPadded[1] = k1 ^ ipad;
kInPadded[2] = k2 ^ ipad;
kInPadded[3] = k3 ^ ipad;
kInPadded[4] = k4 ^ ipad;
kInPadded[5] = k5 ^ ipad;
kInPadded[6] = ipad;
kInPadded[7] = ipad;
kInPadded[8] = ipad;
kInPadded[9] = ipad;
kInPadded[10] = ipad;
kInPadded[11] = ipad;
kInPadded[12] = ipad;
kInPadded[13] = ipad;
kInPadded[14] = ipad;
kInPadded[15] = ipad;
const uint64_t opad = 0x5c5c5c5c5c5c5c5cULL;
outer[0] = k0 ^ opad;
outer[1] = k1 ^ opad;
outer[2] = k2 ^ opad;
outer[3] = k3 ^ opad;
outer[4] = k4 ^ opad;
outer[5] = k5 ^ opad;
outer[6] = opad;
outer[7] = opad;
outer[8] = opad;
outer[9] = opad;
outer[10] = opad;
outer[11] = opad;
outer[12] = opad;
outer[13] = opad;
outer[14] = opad;
outer[15] = opad;
// H(output padded key | H(input padded key | msg))
SHA384(reinterpret_cast<uint8_t *>(outer) + 128,kInPadded,128,msg,msglen);
SHA384(mac,outer,176);
}
void KBKDFHMACSHA384(const uint8_t key[ZT_SYMMETRIC_KEY_SIZE],const char label,const char context,const uint32_t iter,uint8_t out[ZT_SYMMETRIC_KEY_SIZE])
{
uint8_t kbkdfMsg[13];
Utils::storeBigEndian<uint32_t>(kbkdfMsg,(uint32_t)iter);
kbkdfMsg[4] = (uint8_t)'Z';
kbkdfMsg[5] = (uint8_t)'T'; // preface our labels with something ZT-specific
kbkdfMsg[6] = (uint8_t)label;
kbkdfMsg[7] = 0;
kbkdfMsg[8] = (uint8_t)context;
// Output key length: 384 bits (as 32-bit big-endian value)
kbkdfMsg[9] = 0;
kbkdfMsg[10] = 0;
kbkdfMsg[11] = 0x01;
kbkdfMsg[12] = 0x80;
static_assert(ZT_SYMMETRIC_KEY_SIZE == ZT_SHA384_DIGEST_SIZE,"sizeof(out) != ZT_SHA384_DIGEST_SIZE");
HMACSHA384(key,&kbkdfMsg,sizeof(kbkdfMsg),out);
}
} // namespace ZeroTier

View File

@ -1,5 +1,5 @@
/*
* Copyright (c)2019 ZeroTier, Inc.
* Copyright (c)2013-2020 ZeroTier, Inc.
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory.
@ -14,18 +14,74 @@
#ifndef ZT_SHA512_HPP
#define ZT_SHA512_HPP
#define ZT_SHA512_DIGEST_LEN 64
#include "Constants.hpp"
#ifdef __APPLE__
#include <CommonCrypto/CommonDigest.h>
#endif
#define ZT_SHA512_DIGEST_SIZE 64
#define ZT_SHA384_DIGEST_SIZE 48
#define ZT_SHA512_BLOCK_SIZE 128
#define ZT_SHA384_BLOCK_SIZE 128
#define ZT_HMACSHA384_LEN 48
namespace ZeroTier {
/**
* SHA-512 digest algorithm
*/
class SHA512
// SHA384 and SHA512 are actually in the standard libraries on MacOS and iOS
#ifdef __APPLE__
#define ZT_HAVE_NATIVE_SHA512 1
static ZT_INLINE void SHA512(void *digest,const void *data,unsigned int len)
{
public:
static void hash(void *digest,const void *data,unsigned int len);
};
CC_SHA512_CTX ctx;
CC_SHA512_Init(&ctx);
CC_SHA512_Update(&ctx,data,len);
CC_SHA512_Final(reinterpret_cast<unsigned char *>(digest),&ctx);
}
static ZT_INLINE void SHA384(void *digest,const void *data,unsigned int len)
{
CC_SHA512_CTX ctx;
CC_SHA384_Init(&ctx);
CC_SHA384_Update(&ctx,data,len);
CC_SHA384_Final(reinterpret_cast<unsigned char *>(digest),&ctx);
}
static ZT_INLINE void SHA384(void *digest,const void *data0,unsigned int len0,const void *data1,unsigned int len1)
{
CC_SHA512_CTX ctx;
CC_SHA384_Init(&ctx);
CC_SHA384_Update(&ctx,data0,len0);
CC_SHA384_Update(&ctx,data1,len1);
CC_SHA384_Final(reinterpret_cast<unsigned char *>(digest),&ctx);
}
#endif
#ifndef ZT_HAVE_NATIVE_SHA512
void SHA512(void *digest,const void *data,unsigned int len);
void SHA384(void *digest,const void *data,unsigned int len);
void SHA384(void *digest,const void *data0,unsigned int len0,const void *data1,unsigned int len1);
#endif
/**
* Compute HMAC SHA-384 using a 256-bit key
*
* @param key Secret key
* @param msg Message to HMAC
* @param msglen Length of message
* @param mac Buffer to fill with result
*/
void HMACSHA384(const uint8_t key[ZT_SYMMETRIC_KEY_SIZE],const void *msg,unsigned int msglen,uint8_t mac[48]);
/**
* Compute KBKDF (key-based key derivation function) using HMAC-SHA-384 as a PRF
*
* @param key Source master key
* @param label A label indicating the key's purpose in the ZeroTier system
* @param context An arbitrary "context" or zero if not applicable
* @param iter Key iteration for generation of multiple keys for the same label/context
* @param out Output to receive derived key
*/
void KBKDFHMACSHA384(const uint8_t key[ZT_SYMMETRIC_KEY_SIZE],char label,char context,uint32_t iter,uint8_t out[ZT_SYMMETRIC_KEY_SIZE]);
} // namespace ZeroTier

View File

@ -96,7 +96,7 @@ void Switch::onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddre
if ((now - _lastBeaconResponse) >= 2500) { // limit rate of responses
_lastBeaconResponse = now;
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NOP);
outp.armor(peer->key(),true);
outp.armor(peer->key(),true,peer->aesKeysIfSupported());
path->send(RR,tPtr,outp.data(),outp.size(),now);
}
}
@ -879,7 +879,6 @@ void Switch::requestWhois(void *tPtr,const int64_t now,const Address &addr)
int32_t flowId = ZT_QOS_NO_FLOW;
Packet outp(upstream->address(),RR->identity.address(),Packet::VERB_WHOIS);
addr.appendTo(outp);
RR->node->expectReplyTo(outp.packetId());
send(tPtr,outp,true,flowId);
}
}
@ -1042,14 +1041,16 @@ void Switch::_sendViaSpecificPath(void *tPtr,SharedPtr<Peer> peer,SharedPtr<Path
unsigned int chunkSize = std::min(packet.size(),mtu);
packet.setFragmented(chunkSize < packet.size());
peer->recordOutgoingPacket(viaPath, packet.packetId(), packet.payloadLength(), packet.verb(), flowId, now);
if (trustedPathId) {
packet.setTrusted(trustedPathId);
} else {
packet.armor(peer->key(),encrypt);
Packet::Verb v = packet.verb();
packet.armor(peer->key(),encrypt,peer->aesKeysIfSupported());
RR->node->expectReplyTo(packet.packetId());
}
peer->recordOutgoingPacket(viaPath, packet.packetId(), packet.payloadLength(), packet.verb(), flowId, now);
if (viaPath->send(RR,tPtr,packet.data(),chunkSize,now)) {
if (chunkSize < packet.size()) {
// Too big for one packet, fragment the rest

View File

@ -112,10 +112,10 @@ void SoftwareUpdater::setUpdateDistribution(bool distribute)
// If update meta is called e.g. foo.exe.json, then foo.exe is the update itself
const std::string binPath(udd + ZT_PATH_SEPARATOR_S + u->substr(0,u->length() - 5));
const std::string metaHash(OSUtils::jsonBinFromHex(d.meta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_HASH]));
if ((metaHash.length() == ZT_SHA512_DIGEST_LEN)&&(OSUtils::readFile(binPath.c_str(),d.bin))) {
std::array<uint8_t,ZT_SHA512_DIGEST_LEN> sha512;
SHA512::hash(sha512.data(),d.bin.data(),(unsigned int)d.bin.length());
if (!memcmp(sha512.data(),metaHash.data(),ZT_SHA512_DIGEST_LEN)) { // double check that hash in JSON is correct
if ((metaHash.length() == 64)&&(OSUtils::readFile(binPath.c_str(),d.bin))) {
std::array<uint8_t,64> sha512;
SHA512(sha512.data(),d.bin.data(),(unsigned int)d.bin.length());
if (!memcmp(sha512.data(),metaHash.data(),64)) { // double check that hash in JSON is correct
d.meta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIZE] = d.bin.length(); // override with correct value -- setting this in meta json is optional
std::array<uint8_t,16> shakey;
memcpy(shakey.data(),sha512.data(),16);
@ -333,10 +333,10 @@ bool SoftwareUpdater::check(const int64_t now)
const std::string binPath(_homePath + ZT_PATH_SEPARATOR_S ZT_SOFTWARE_UPDATE_BIN_FILENAME);
try {
// (1) Check the hash itself to make sure the image is basically okay
uint8_t sha512[ZT_SHA512_DIGEST_LEN];
SHA512::hash(sha512,_download.data(),(unsigned int)_download.length());
char hexbuf[(ZT_SHA512_DIGEST_LEN * 2) + 2];
if (OSUtils::jsonString(_latestMeta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_HASH],"") == Utils::hex(sha512,ZT_SHA512_DIGEST_LEN,hexbuf)) {
uint8_t sha512[64];
SHA512(sha512,_download.data(),(unsigned int)_download.length());
char hexbuf[(64 * 2) + 2];
if (OSUtils::jsonString(_latestMeta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_HASH],"") == Utils::hex(sha512,64,hexbuf)) {
// (2) Check signature by signing authority
const std::string sig(OSUtils::jsonBinFromHex(_latestMeta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIGNATURE]));
if (Identity(ZT_SOFTWARE_UPDATE_SIGNING_AUTHORITY).verify(_download.data(),(unsigned int)_download.length(),sig.data(),(unsigned int)sig.length())) {