/*
* ZeroTier One - Network Virtualization Everywhere
* Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* --
*
* You can be released from the requirements of the license by purchasing
* a commercial license. Buying such a license is mandatory as soon as you
* develop commercial closed-source software that incorporates or links
* directly against ZeroTier software without disclosing the source code
* of your own application.
*/
#include
#include
#include
#include
#include "Constants.hpp"
#include "Identity.hpp"
#include "SHA512.hpp"
#include "Salsa20.hpp"
#include "Utils.hpp"
namespace ZeroTier {
namespace {
// These can't be changed without a new identity type. They define the
// parameters of the hashcash hashing/searching algorithm.
#define ZT_IDENTITY_GEN_HASHCASH_FIRST_BYTE_LESS_THAN 17
#define ZT_IDENTITY_GEN_MEMORY 2097152
// A memory-hard composition of SHA-512 and Salsa20 for hashcash hashing
static void _computeMemoryHardHash(const void *publicKey,unsigned int publicKeyBytes,void *digest,void *genmem)
{
// Digest publicKey[] to obtain initial digest
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
// but is not what we want for sequential memory-hardness.
memset(genmem,0,ZT_IDENTITY_GEN_MEMORY);
Salsa20 s20(digest,(char *)digest + 32);
s20.crypt20((char *)genmem,(char *)genmem,64);
for(unsigned long i=64;i= ZT_IDENTITY_GEN_HASHCASH_FIRST_BYTE_LESS_THAN)
continue;
_address.setTo(digest + 59,ZT_ADDRESS_LENGTH);
} while (_address.isReserved());
_type = P384;
_hasPrivate = true;
} break;
}
delete [] genmem;
}
bool Identity::locallyValidate() const
{
if (_address.isReserved())
return false;
uint8_t digest[64];
char *genmem = nullptr;
try {
genmem = new char[ZT_IDENTITY_GEN_MEMORY];
switch(_type) {
case C25519:
_computeMemoryHardHash(_k.t0.pub.data,ZT_C25519_PUBLIC_KEY_LEN,digest,genmem);
break;
case P384:
_computeMemoryHardHash(_k.t1.pub,ZT_ECC384_PUBLIC_KEY_SIZE,digest,genmem);
break;
default:
return false;
}
delete [] genmem;
unsigned char addrb[5];
_address.copyTo(addrb,5);
return (
(digest[0] < ZT_IDENTITY_GEN_HASHCASH_FIRST_BYTE_LESS_THAN)&&
(digest[59] == addrb[0])&&
(digest[60] == addrb[1])&&
(digest[61] == addrb[2])&&
(digest[62] == addrb[3])&&
(digest[63] == addrb[4]));
} catch ( ... ) {
if (genmem) delete [] genmem;
return false;
}
}
unsigned int Identity::sign(const void *data,unsigned int len,void *sig,unsigned int siglen) const
{
uint8_t h[48];
if (!_hasPrivate)
return 0;
switch(_type) {
case C25519:
if (siglen < ZT_C25519_SIGNATURE_LEN)
return 0;
C25519::sign(_k.t0.priv,_k.t0.pub,data,len,sig);
return ZT_C25519_SIGNATURE_LEN;
case P384:
if (siglen < ZT_ECC384_SIGNATURE_SIZE)
return 0;
SHA384(h,data,len);
ECC384ECDSASign(_k.t1.priv,h,(uint8_t *)sig);
return ZT_ECC384_SIGNATURE_SIZE;
}
return 0;
}
bool Identity::verify(const void *data,unsigned int len,const void *sig,unsigned int siglen) const
{
switch(_type) {
case C25519:
return C25519::verify(_k.t0.pub,data,len,sig,siglen);
case P384:
if (siglen == ZT_ECC384_SIGNATURE_SIZE) {
uint8_t h[48];
SHA384(h,data,len);
return ECC384ECDSAVerify(_k.t1.pub,h,(const uint8_t *)sig);
}
break;
}
return false;
}
bool Identity::agree(const Identity &id,void *key,unsigned int klen) const
{
uint8_t ecc384RawSecret[ZT_ECC384_SHARED_SECRET_SIZE];
uint8_t h[48];
if (_hasPrivate) {
switch(_type) {
case C25519:
C25519::agree(_k.t0.priv,id._k.t0.pub,key,klen);
return true;
case P384:
ECC384ECDH(id._k.t1.pub,_k.t1.priv,ecc384RawSecret);
SHA384(h,ecc384RawSecret,sizeof(ecc384RawSecret));
for(unsigned int i=0,hi=0;i