Integrating new crypto -- work in progress, wont build yet.

This commit is contained in:
Adam Ierymenko 2013-09-16 09:20:59 -04:00
commit 3b2d98e7dc
105 changed files with 124 additions and 37722 deletions

View file

@ -2311,13 +2311,13 @@ C25519::Pair C25519::generate()
return kp;
}
void C25519::agree(const C25519::Pair &mine,const C25519::Public &their,void *keybuf,unsigned int keylen)
void C25519::agree(const C25519::Private &mine,const C25519::Public &their,void *keybuf,unsigned int keylen)
throw()
{
unsigned char rawkey[32];
unsigned char digest[64];
crypto_scalarmult(rawkey,mine.priv.data,their.data);
crypto_scalarmult(rawkey,mine.data,their.data);
SHA512::hash(digest,rawkey,32);
for(unsigned int i=0,k=0;i<keylen;) {
if (k == 64) {
@ -2328,7 +2328,7 @@ void C25519::agree(const C25519::Pair &mine,const C25519::Public &their,void *ke
}
}
void C25519::sign(const C25519::Pair &mine,const void *msg,unsigned int len,void *signature)
void C25519::sign(const C25519::Private &myPrivate,const C25519::Public &myPublic,const void *msg,unsigned int len,void *signature)
throw()
{
sc25519 sck, scs, scsk;
@ -2343,7 +2343,7 @@ void C25519::sign(const C25519::Pair &mine,const void *msg,unsigned int len,void
SHA512::hash(digest,msg,len);
SHA512::hash(extsk,mine.priv.data + 32,32);
SHA512::hash(extsk,myPrivate.data + 32,32);
extsk[0] &= 248;
extsk[31] &= 127;
extsk[31] |= 64;
@ -2365,7 +2365,7 @@ void C25519::sign(const C25519::Pair &mine,const void *msg,unsigned int len,void
for(unsigned int i=0;i<32;i++)
sig[i] = r[i];
get_hram(hram,sig,mine.pub.data + 32,sig,96);
get_hram(hram,sig,myPublic.data + 32,sig,96);
sc25519_from64bytes(&scs, hram);
sc25519_from32bytes(&scsk, extsk);

View file

@ -77,13 +77,18 @@ public:
* Actual key bytes are generated from one or more SHA-512 digests of
* the raw result of key agreement.
*
* @param mine My key pair including secret
* @param mine My private key
* @param their Their public key
* @param keybuf Buffer to fill
* @param keylen Number of key bytes to generate
*/
static void agree(const Pair &mine,const Public &their,void *keybuf,unsigned int keylen)
static void agree(const Private &mine,const Public &their,void *keybuf,unsigned int keylen)
throw();
static inline void agree(const Pair &mine,const Public &their,void *keybuf,unsigned int keylen)
throw()
{
agree(mine.priv,their,keybuf,keylen);
}
/**
* Sign a message with a sender's key pair
@ -98,27 +103,41 @@ public:
* produces a signature of fixed 96-byte length based on the hash of an
* arbitrary-length message.
*
* @param Key pair to sign with
* @param myPrivate My private key
* @param myPublic My public key
* @param msg Message to sign
* @param len Length of message in bytes
* @param signature Buffer to fill with signature -- MUST be 96 bytes in length
*/
static void sign(const Pair &mine,const void *msg,unsigned int len,void *signature)
static void sign(const Private &myPrivate,const Public &myPublic,const void *msg,unsigned int len,void *signature)
throw();
static inline void sign(const Pair &mine,const void *msg,unsigned int len,void *signature)
throw()
{
sign(mine.priv,mine.pub,msg,len,signature);
}
/**
* Sign a message with a sender's key pair
*
* @param Key pair to sign with
* @param myPrivate My private key
* @param myPublic My public key
* @param msg Message to sign
* @param len Length of message in bytes
* @return Signature
*/
static Signature sign(const Pair &mine,const void *msg,unsigned int len)
static inline Signature sign(const Private &myPrivate,const Public &myPublic,const void *msg,unsigned int len)
throw()
{
Signature sig;
sign(mine,msg,len,sig.data);
sign(myPrivate,myPublic,msg,len,sig.data);
return sig;
}
static inline Signature sign(const Pair &mine,const void *msg,unsigned int len)
throw()
{
Signature sig;
sign(mine.priv,mine.pub,msg,len,sig.data);
return sig;
}

View file

@ -1,114 +0,0 @@
/*
* ZeroTier One - Global Peer to Peer Ethernet
* Copyright (C) 2012-2013 ZeroTier Networks LLC
*
* 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 <http://www.gnu.org/licenses/>.
*
* --
*
* ZeroTier may be used and distributed under the terms of the GPLv3, which
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
*
* If you would like to embed ZeroTier into a commercial application or
* redistribute it in a modified binary form, please contact ZeroTier Networks
* LLC. Start here: http://www.zerotier.com/
*/
#ifndef _ZT_ELLIPTICCURVEKEY_H
#define _ZT_ELLIPTICCURVEKEY_H
#include <string>
#include <algorithm>
#include <stdexcept>
#include <string.h>
#include "Utils.hpp"
/**
* Key type ID for identifying our use of NIST-P-521
*
* If in the future other types of keys are supported (post-quantum crypto?)
* then we'll need a key type 2, etc. When keys are stored in the database
* they are prefixed by this key type ID byte.
*/
#define ZT_KEY_TYPE 1
#define ZT_EC_OPENSSL_CURVE NID_secp521r1
#define ZT_EC_CURVE_NAME "NIST-P-521"
#define ZT_EC_PRIME_BYTES 66
#define ZT_EC_PUBLIC_KEY_BYTES (ZT_EC_PRIME_BYTES + 1)
#define ZT_EC_PRIVATE_KEY_BYTES ZT_EC_PRIME_BYTES
#define ZT_EC_MAX_BYTES ZT_EC_PUBLIC_KEY_BYTES
namespace ZeroTier {
class EllipticCurveKeyPair;
/**
* An elliptic curve public or private key
*/
class EllipticCurveKey
{
friend class EllipticCurveKeyPair;
public:
EllipticCurveKey()
throw() :
_bytes(0)
{
memset(_key,0,sizeof(_key));
}
EllipticCurveKey(const void *data,unsigned int len)
throw(std::out_of_range)
{
set(data,len);
}
EllipticCurveKey(const std::string &data)
throw(std::out_of_range)
{
set(data.data(),(unsigned int)data.length());
}
inline void set(const void *data,unsigned int len)
throw(std::out_of_range)
{
if (len <= ZT_EC_MAX_BYTES) {
_bytes = len;
memcpy(_key,data,len);
} else throw std::out_of_range("key too large");
}
inline const unsigned char *data() const throw() { return _key; }
inline unsigned int size() const throw() { return _bytes; }
inline std::string toHex() const throw() { return Utils::hex(_key,_bytes); }
inline unsigned char operator[](const unsigned int i) const throw() { return _key[i]; }
inline bool operator==(const EllipticCurveKey &k) const throw() { return ((_bytes == k._bytes)&&(!memcmp(_key,k._key,_bytes))); }
inline bool operator<(const EllipticCurveKey &k) const throw() { return std::lexicographical_compare(_key,&_key[_bytes],k._key,&k._key[k._bytes]); }
inline bool operator!=(const EllipticCurveKey &k) const throw() { return !(*this == k); }
inline bool operator>(const EllipticCurveKey &k) const throw() { return (k < *this); }
inline bool operator<=(const EllipticCurveKey &k) const throw() { return !(k < *this); }
inline bool operator>=(const EllipticCurveKey &k) const throw() { return !(*this < k); }
private:
unsigned int _bytes;
unsigned char _key[ZT_EC_MAX_BYTES];
};
} // namespace ZeroTier
#endif

View file

@ -1,369 +0,0 @@
/*
* ZeroTier One - Global Peer to Peer Ethernet
* Copyright (C) 2012-2013 ZeroTier Networks LLC
*
* 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 <http://www.gnu.org/licenses/>.
*
* --
*
* ZeroTier may be used and distributed under the terms of the GPLv3, which
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
*
* If you would like to embed ZeroTier into a commercial application or
* redistribute it in a modified binary form, please contact ZeroTier Networks
* LLC. Start here: http://www.zerotier.com/
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Constants.hpp"
#ifdef __WINDOWS__
#include <WinSock2.h>
#include <Windows.h>
#endif
#include <openssl/bn.h>
#include <openssl/obj_mac.h>
#include <openssl/rand.h>
#include <openssl/ec.h>
#include <openssl/ecdh.h>
#include <openssl/ecdsa.h>
#include <openssl/sha.h>
#include "EllipticCurveKey.hpp"
#include "EllipticCurveKeyPair.hpp"
namespace ZeroTier {
class _EC_Group
{
public:
_EC_Group()
{
g = EC_GROUP_new_by_curve_name(ZT_EC_OPENSSL_CURVE);
}
~_EC_Group() {}
EC_GROUP *g;
};
static _EC_Group ZT_EC_GROUP;
/**
* Key derivation function
*
* TODO:
* If/when we document the protocol, this will have to be documented as
* well. It's a fairly standard KDF that uses SHA-256 to transform the
* raw EC key. It's generally considered good crypto practice to do this
* to eliminate the possibility of leaking information from EC exchange to
* downstream algorithms.
*
* In our code it is used to produce a two 32-bit keys. One key is used
* for Salsa20 and the other for HMAC-SHA-256. They are generated together
* as a single 64-bit key.
*/
static void *_zt_EC_KDF(const void *in,size_t inlen,void *out,size_t *outlen)
{
SHA256_CTX sha;
unsigned char dig[SHA256_DIGEST_LENGTH];
SHA256_Init(&sha);
SHA256_Update(&sha,(const unsigned char *)in,inlen);
SHA256_Final(dig,&sha);
for(unsigned long i=0,k=0;i<(unsigned long)*outlen;) {
if (k == SHA256_DIGEST_LENGTH) {
k = 0;
SHA256_Init(&sha);
SHA256_Update(&sha,(const unsigned char *)in,inlen);
SHA256_Update(&sha,dig,SHA256_DIGEST_LENGTH);
SHA256_Final(dig,&sha);
}
((unsigned char *)out)[i++] = dig[k++];
}
return out;
}
EllipticCurveKeyPair::EllipticCurveKeyPair() :
_pub(),
_priv(),
_internal_key((void *)0)
{
}
EllipticCurveKeyPair::EllipticCurveKeyPair(const EllipticCurveKeyPair &pair) :
_pub(pair._pub),
_priv(pair._priv),
_internal_key((void *)0)
{
}
EllipticCurveKeyPair::EllipticCurveKeyPair(const EllipticCurveKey &pubk,const EllipticCurveKey &privk) :
_pub(pubk),
_priv(privk),
_internal_key((void *)0)
{
}
EllipticCurveKeyPair::~EllipticCurveKeyPair()
{
if (_internal_key)
EC_KEY_free((EC_KEY *)_internal_key);
}
const EllipticCurveKeyPair &EllipticCurveKeyPair::operator=(const EllipticCurveKeyPair &pair)
{
if (_internal_key)
EC_KEY_free((EC_KEY *)_internal_key);
_pub = pair._pub;
_priv = pair._priv;
_internal_key = (void *)0;
return *this;
}
bool EllipticCurveKeyPair::generate()
{
EC_KEY *key;
int len;
key = EC_KEY_new();
if (!key) return false;
if (!EC_KEY_set_group(key,ZT_EC_GROUP.g)) {
EC_KEY_free(key);
return false;
}
if (!EC_KEY_generate_key(key)) {
EC_KEY_free(key);
return false;
}
memset(_priv._key,0,sizeof(_priv._key));
len = (int)BN_num_bytes(EC_KEY_get0_private_key(key));
if ((len > ZT_EC_PRIME_BYTES)||(len < 0)) {
EC_KEY_free(key);
return false;
}
BN_bn2bin(EC_KEY_get0_private_key(key),&(_priv._key[ZT_EC_PRIME_BYTES - len]));
_priv._bytes = ZT_EC_PRIME_BYTES;
memset(_pub._key,0,sizeof(_pub._key));
len = (int)EC_POINT_point2oct(ZT_EC_GROUP.g,EC_KEY_get0_public_key(key),POINT_CONVERSION_COMPRESSED,_pub._key,sizeof(_pub._key),0);
if (len != ZT_EC_PUBLIC_KEY_BYTES) {
EC_KEY_free(key);
return false;
}
_pub._bytes = ZT_EC_PUBLIC_KEY_BYTES;
if (_internal_key)
EC_KEY_free((EC_KEY *)_internal_key);
_internal_key = key;
return true;
}
bool EllipticCurveKeyPair::agree(const EllipticCurveKey &theirPublicKey,unsigned char *agreedUponKey,unsigned int agreedUponKeyLength) const
{
if (theirPublicKey._bytes != ZT_EC_PUBLIC_KEY_BYTES)
return false;
if (!_internal_key) {
if (!(const_cast <EllipticCurveKeyPair *> (this))->initInternalKey())
return false;
}
EC_POINT *pub = EC_POINT_new(ZT_EC_GROUP.g);
if (!pub)
return false;
EC_POINT_oct2point(ZT_EC_GROUP.g,pub,theirPublicKey._key,ZT_EC_PUBLIC_KEY_BYTES,0);
int i = ECDH_compute_key(agreedUponKey,agreedUponKeyLength,pub,(EC_KEY *)_internal_key,&_zt_EC_KDF);
EC_POINT_free(pub);
return (i == (int)agreedUponKeyLength);
}
std::string EllipticCurveKeyPair::sign(const void *sha256) const
{
unsigned char buf[256];
std::string sigbin;
if (!_internal_key) {
if (!(const_cast <EllipticCurveKeyPair *> (this))->initInternalKey())
return std::string();
}
ECDSA_SIG *sig = ECDSA_do_sign((const unsigned char *)sha256,SHA256_DIGEST_LENGTH,(EC_KEY *)_internal_key);
if (!sig)
return std::string();
int rlen = BN_num_bytes(sig->r);
if ((rlen > 255)||(rlen <= 0)) {
ECDSA_SIG_free(sig);
return std::string();
}
sigbin.push_back((char)rlen);
BN_bn2bin(sig->r,buf);
sigbin.append((const char *)buf,rlen);
int slen = BN_num_bytes(sig->s);
if ((slen > 255)||(slen <= 0)) {
ECDSA_SIG_free(sig);
return std::string();
}
sigbin.push_back((char)slen);
BN_bn2bin(sig->s,buf);
sigbin.append((const char *)buf,slen);
ECDSA_SIG_free(sig);
return sigbin;
}
std::string EllipticCurveKeyPair::sign(const void *data,unsigned int len) const
{
SHA256_CTX sha;
unsigned char dig[SHA256_DIGEST_LENGTH];
SHA256_Init(&sha);
SHA256_Update(&sha,(const unsigned char *)data,len);
SHA256_Final(dig,&sha);
return sign(dig);
}
bool EllipticCurveKeyPair::verify(const void *sha256,const EllipticCurveKey &pk,const void *sigbytes,unsigned int siglen)
{
bool result = false;
ECDSA_SIG *sig = (ECDSA_SIG *)0;
EC_POINT *pub = (EC_POINT *)0;
EC_KEY *key = (EC_KEY *)0;
int rlen,slen;
if (!siglen)
goto verify_sig_return;
rlen = ((const unsigned char *)sigbytes)[0];
if (!rlen)
goto verify_sig_return;
if (siglen < (unsigned int)(rlen + 2))
goto verify_sig_return;
slen = ((const unsigned char *)sigbytes)[rlen + 1];
if (!slen)
goto verify_sig_return;
if (siglen < (unsigned int)(rlen + slen + 2))
goto verify_sig_return;
sig = ECDSA_SIG_new();
if (!sig)
goto verify_sig_return;
BN_bin2bn((const unsigned char *)sigbytes + 1,rlen,sig->r);
BN_bin2bn((const unsigned char *)sigbytes + (1 + rlen + 1),slen,sig->s);
pub = EC_POINT_new(ZT_EC_GROUP.g);
if (!pub)
goto verify_sig_return;
EC_POINT_oct2point(ZT_EC_GROUP.g,pub,pk._key,ZT_EC_PUBLIC_KEY_BYTES,0);
key = EC_KEY_new();
if (!key)
goto verify_sig_return;
if (!EC_KEY_set_group(key,ZT_EC_GROUP.g))
goto verify_sig_return;
EC_KEY_set_public_key(key,pub);
result = (ECDSA_do_verify((const unsigned char *)sha256,SHA256_DIGEST_LENGTH,sig,key) == 1);
verify_sig_return:
if (key)
EC_KEY_free(key);
if (pub)
EC_POINT_free(pub);
if (sig)
ECDSA_SIG_free(sig);
return result;
}
bool EllipticCurveKeyPair::verify(const void *data,unsigned int len,const EllipticCurveKey &pk,const void *sigbytes,unsigned int siglen)
{
SHA256_CTX sha;
unsigned char dig[SHA256_DIGEST_LENGTH];
SHA256_Init(&sha);
SHA256_Update(&sha,(const unsigned char *)data,len);
SHA256_Final(dig,&sha);
return verify(dig,pk,sigbytes,siglen);
}
bool EllipticCurveKeyPair::initInternalKey()
{
EC_KEY *key;
EC_POINT *kxy;
BIGNUM *pn;
if (_priv._bytes != ZT_EC_PRIME_BYTES) return false;
if (_pub._bytes != ZT_EC_PUBLIC_KEY_BYTES) return false;
key = EC_KEY_new();
if (!key) return false;
if (!EC_KEY_set_group(key,ZT_EC_GROUP.g)) {
EC_KEY_free(key);
return false;
}
pn = BN_new();
if (!pn) {
EC_KEY_free(key);
return false;
}
if (!BN_bin2bn(_priv._key,ZT_EC_PRIME_BYTES,pn)) {
BN_free(pn);
EC_KEY_free(key);
return false;
}
if (!EC_KEY_set_private_key(key,pn)) {
BN_free(pn);
EC_KEY_free(key);
return false;
}
BN_free(pn);
kxy = EC_POINT_new(ZT_EC_GROUP.g);
if (!kxy) {
EC_KEY_free(key);
return false;
}
EC_POINT_oct2point(ZT_EC_GROUP.g,kxy,_pub._key,ZT_EC_PUBLIC_KEY_BYTES,0);
if (!EC_KEY_set_public_key(key,kxy)) {
EC_POINT_free(kxy);
EC_KEY_free(key);
return false;
}
EC_POINT_free(kxy);
if (_internal_key)
EC_KEY_free((EC_KEY *)_internal_key);
_internal_key = key;
return true;
}
} // namespace ZeroTier

View file

@ -1,130 +0,0 @@
/*
* ZeroTier One - Global Peer to Peer Ethernet
* Copyright (C) 2012-2013 ZeroTier Networks LLC
*
* 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 <http://www.gnu.org/licenses/>.
*
* --
*
* ZeroTier may be used and distributed under the terms of the GPLv3, which
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
*
* If you would like to embed ZeroTier into a commercial application or
* redistribute it in a modified binary form, please contact ZeroTier Networks
* LLC. Start here: http://www.zerotier.com/
*/
#ifndef _ZT_ELLIPTICCURVEKEYPAIR_HPP
#define _ZT_ELLIPTICCURVEKEYPAIR_HPP
#include <string>
#include "EllipticCurveKey.hpp"
namespace ZeroTier {
/**
* An elliptic curve key pair supporting generation and key agreement
*
* This is basically OpenSSL libcrypto glue.
*/
class EllipticCurveKeyPair
{
public:
EllipticCurveKeyPair();
EllipticCurveKeyPair(const EllipticCurveKeyPair &pair);
EllipticCurveKeyPair(const EllipticCurveKey &pubk,const EllipticCurveKey &privk);
~EllipticCurveKeyPair();
const EllipticCurveKeyPair &operator=(const EllipticCurveKeyPair &pair);
/**
* Fill this structure with a newly generated public/private key pair
*
* @return True if key generation is successful
*/
bool generate();
/**
* Perform elliptic curve key agreement
*
* @param theirPublicKey Remote side's public key
* @param agreedUponKey Buffer to fill with agreed-upon symmetric key
* @param agreedUponKeyLength Number of bytes to generate
* @return True if key agreement is successful
*/
bool agree(const EllipticCurveKey &theirPublicKey,unsigned char *agreedUponKey,unsigned int agreedUponKeyLength) const;
/**
* Sign a SHA256 hash
*
* @param sha256 Pointer to 256-bit / 32-byte SHA hash to sign
* @return ECDSA signature (r and s in binary format, each prefixed by an 8-bit size)
*/
std::string sign(const void *sha256) const;
/**
* Sign something with this pair's private key, computing its hash first
*
* @param data Data to hash and sign
* @param len Length of data
* @return Signature bytes
*/
std::string sign(const void *data,unsigned int len) const;
/**
* Verify a signature
*
* @param sha256 Pointer to 256-bit / 32-byte SHA hash to verify
* @param pk Public key to verify against
* @param sigbytes Signature bytes
* @param siglen Length of signature
*/
static bool verify(const void *sha256,const EllipticCurveKey &pk,const void *sigbytes,unsigned int siglen);
/**
* Verify a signature
*
* @param data Data to verify
* @param len Length of data
* @param pk Public key to verify against
* @param sigbytes Signature bytes
* @param siglen Length of signature
*/
static bool verify(const void *data,unsigned int len,const EllipticCurveKey &pk,const void *sigbytes,unsigned int siglen);
inline bool operator==(const EllipticCurveKeyPair &kp) const
throw()
{
return ((_pub == kp._pub)&&(_priv == kp._priv));
}
inline bool operator!=(const EllipticCurveKeyPair &kp) const
throw()
{
return ((_pub != kp._pub)||(_priv != kp._priv));
}
inline const EllipticCurveKey &pub() const throw() { return _pub; }
inline const EllipticCurveKey &priv() const throw() { return _priv; }
private:
bool initInternalKey();
EllipticCurveKey _pub;
EllipticCurveKey _priv;
void *_internal_key;
};
} // namespace ZeroTier
#endif

View file

@ -1,81 +0,0 @@
/*
* ZeroTier One - Global Peer to Peer Ethernet
* Copyright (C) 2012-2013 ZeroTier Networks LLC
*
* 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 <http://www.gnu.org/licenses/>.
*
* --
*
* ZeroTier may be used and distributed under the terms of the GPLv3, which
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
*
* If you would like to embed ZeroTier into a commercial application or
* redistribute it in a modified binary form, please contact ZeroTier Networks
* LLC. Start here: http://www.zerotier.com/
*/
#include "HMAC.hpp"
#include <openssl/sha.h>
namespace ZeroTier {
void HMAC::sha256(const void *key,unsigned int klen,const void *message,unsigned int len,void *mac)
throw()
{
union {
uint64_t q[12];
uint8_t b[96];
} key2,opad,ipad;
SHA256_CTX sha;
if (klen == 32) { // this is what we use, so handle this quickly
key2.q[0] = ((const uint64_t *)key)[0];
key2.q[1] = ((const uint64_t *)key)[1];
key2.q[2] = ((const uint64_t *)key)[2];
key2.q[3] = ((const uint64_t *)key)[3];
key2.q[4] = 0ULL;
key2.q[5] = 0ULL;
key2.q[6] = 0ULL;
key2.q[7] = 0ULL;
} else { // for correctness and testing against test vectors
if (klen > 64) {
SHA256_Init(&sha);
SHA256_Update(&sha,key,klen);
SHA256_Final(key2.b,&sha);
klen = 32;
} else {
for(unsigned int i=0;i<klen;++i)
key2.b[i] = ((const uint8_t *)key)[i];
}
while (klen < 64)
key2.b[klen++] = (uint8_t)0;
}
for(unsigned int i=0;i<8;++i)
opad.q[i] = 0x5c5c5c5c5c5c5c5cULL ^ key2.q[i];
for(unsigned int i=0;i<8;++i)
ipad.q[i] = 0x3636363636363636ULL ^ key2.q[i];
SHA256_Init(&sha);
SHA256_Update(&sha,(const unsigned char *)ipad.b,64);
SHA256_Update(&sha,(const unsigned char *)message,len);
SHA256_Final((unsigned char *)(opad.b + 64),&sha);
SHA256_Init(&sha);
SHA256_Update(&sha,opad.b,96);
SHA256_Final((unsigned char *)mac,&sha);
}
} // namespace ZeroTier

View file

@ -1,55 +0,0 @@
/*
* ZeroTier One - Global Peer to Peer Ethernet
* Copyright (C) 2012-2013 ZeroTier Networks LLC
*
* 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 <http://www.gnu.org/licenses/>.
*
* --
*
* ZeroTier may be used and distributed under the terms of the GPLv3, which
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
*
* If you would like to embed ZeroTier into a commercial application or
* redistribute it in a modified binary form, please contact ZeroTier Networks
* LLC. Start here: http://www.zerotier.com/
*/
#ifndef _ZT_HMAC_HPP
#define _ZT_HMAC_HPP
#include <stdint.h>
namespace ZeroTier {
/**
* HMAC authenticator functions
*/
class HMAC
{
public:
/**
* Compute HMAC-SHA256
*
* @param key Key bytes
* @param klen Length of key
* @param len Length of message
* @param mac Buffer to receive 32-byte MAC
*/
static void sha256(const void *key,unsigned int klen,const void *message,unsigned int len,void *mac)
throw();
};
} // namespace ZeroTier
#endif

View file

@ -30,130 +30,60 @@
#include <string.h>
#include <stdint.h>
#include <openssl/sha.h>
#include "Identity.hpp"
#include "Salsa20.hpp"
#include "HMAC.hpp"
#include "Utils.hpp"
namespace ZeroTier {
void Identity::generate()
{
delete [] _keyPair;
// Generate key pair and derive address
C25519::Pair kp;
do {
_keyPair = new EllipticCurveKeyPair();
_keyPair->generate();
_address = deriveAddress(_keyPair->pub().data(),_keyPair->pub().size());
kp = C25519::generate();
_address = deriveAddress(kp.pub.data,kp.pub.size());
} while (_address.isReserved());
_publicKey = _keyPair->pub();
// Sign address, key type, and public key with private key (with a zero
// byte between each field). Including this extra data means simply editing
// the address of an identity will be detected as its signature will be
// invalid. Of course, deep verification of address/key relationship is
// required to cover the more elaborate address claim jump attempt case.
unsigned char atmp[ZT_ADDRESS_LENGTH];
_address.copyTo(atmp,ZT_ADDRESS_LENGTH);
SHA256_CTX sha;
unsigned char dig[32];
unsigned char idtype = IDENTITY_TYPE_NIST_P_521,zero = 0;
SHA256_Init(&sha);
SHA256_Update(&sha,atmp,ZT_ADDRESS_LENGTH);
SHA256_Update(&sha,&zero,1);
SHA256_Update(&sha,&idtype,1);
SHA256_Update(&sha,&zero,1);
SHA256_Update(&sha,_publicKey.data(),_publicKey.size());
SHA256_Update(&sha,&zero,1);
SHA256_Final(dig,&sha);
_signature = _keyPair->sign(dig);
_publicKey = kp.pub;
if (!_privateKey)
_privateKey = new C25519::Private();
*_privateKey = kp.priv;
unsigned char tmp[ZT_ADDRESS_LENGTH + ZT_C25519_PUBLIC_KEY_LEN];
_address.copyTo(tmp,ZT_ADDRESS_LENGTH);
memcpy(tmp + ZT_ADDRESS_LENGTH,_publicKey.data,ZT_C25519_PUBLIC_KEY_LEN);
_signature = C25519::sign(kp,tmp,sizeof(tmp));
}
bool Identity::locallyValidate(bool doAddressDerivationCheck) const
{
unsigned char atmp[ZT_ADDRESS_LENGTH];
_address.copyTo(atmp,ZT_ADDRESS_LENGTH);
SHA256_CTX sha;
unsigned char dig[32];
unsigned char idtype = IDENTITY_TYPE_NIST_P_521,zero = 0;
SHA256_Init(&sha);
SHA256_Update(&sha,atmp,ZT_ADDRESS_LENGTH);
SHA256_Update(&sha,&zero,1);
SHA256_Update(&sha,&idtype,1);
SHA256_Update(&sha,&zero,1);
SHA256_Update(&sha,_publicKey.data(),_publicKey.size());
SHA256_Update(&sha,&zero,1);
SHA256_Final(dig,&sha);
return ((EllipticCurveKeyPair::verify(dig,_publicKey,_signature.data(),(unsigned int)_signature.length()))&&((!doAddressDerivationCheck)||(deriveAddress(_publicKey.data(),_publicKey.size()) == _address)));
unsigned char tmp[ZT_ADDRESS_LENGTH + ZT_C25519_PUBLIC_KEY_LEN];
_address.copyTo(tmp,ZT_ADDRESS_LENGTH);
memcpy(tmp + ZT_ADDRESS_LENGTH,_publicKey.data,ZT_C25519_PUBLIC_KEY_LEN);
if (!C25519::verify(_publicKey,tmp,sizeof(tmp),_signature))
return false;
if ((doAddressDerivationCheck)&&(deriveAddress(_publicKey.data,_publicKey.size()) != _address))
return false;
return true;
}
std::string Identity::toString(bool includePrivate) const
{
std::string r;
r.append(_address.toString());
r.append(":1:"); // 1 == IDENTITY_TYPE_NIST_P_521
r.append(Utils::base64Encode(_publicKey.data(),_publicKey.size()));
r.push_back(':');
r.append(Utils::base64Encode(_signature.data(),(unsigned int)_signature.length()));
if ((includePrivate)&&(_keyPair)) {
r.push_back(':');
r.append(Utils::base64Encode(_keyPair->priv().data(),_keyPair->priv().size()));
}
r.append(":2:"); // 2 == IDENTITY_TYPE_C25519
return r;
}
bool Identity::fromString(const char *str)
{
delete _keyPair;
_keyPair = (EllipticCurveKeyPair *)0;
std::vector<std::string> fields(Utils::split(Utils::trim(std::string(str)).c_str(),":","",""));
if (fields.size() < 4)
return false;
if (fields[1] != "1")
return false; // version mismatch
std::string b(Utils::unhex(fields[0]));
if (b.length() != ZT_ADDRESS_LENGTH)
return false;
_address.setTo(b.data(),ZT_ADDRESS_LENGTH);
b = Utils::base64Decode(fields[2]);
if ((!b.length())||(b.length() > ZT_EC_MAX_BYTES))
return false;
_publicKey.set(b.data(),(unsigned int)b.length());
_signature = Utils::base64Decode(fields[3]);
if (!_signature.length())
return false;
if (fields.size() >= 5) {
b = Utils::base64Decode(fields[4]);
if ((!b.length())||(b.length() > ZT_EC_MAX_BYTES))
return false;
_keyPair = new EllipticCurveKeyPair(_publicKey,EllipticCurveKey(b.data(),(unsigned int)b.length()));
}
return true;
}
// These are core protocol parameters and can't be changed without a new
// identity type.
#define ZT_IDENTITY_DERIVEADDRESS_DIGESTS 540672
#define ZT_IDENTITY_DERIVEADDRESS_ROUNDS 4
#define ZT_IDENTITY_DERIVEADDRESS_MEMORY 33554432
Address Identity::deriveAddress(const void *keyBytes,unsigned int keyLen)
{
unsigned char dig[32];
Salsa20 s20a,s20b;
SHA256_CTX sha;
/*
* Sequential memory-hard algorithm wedding address to public key
*
@ -164,64 +94,28 @@ Address Identity::deriveAddress(const void *keyBytes,unsigned int keyLen)
* that creates a costly 1:~1 mapping from key to address, hence this odd
* algorithm.
*
* This is designed not to be parallelizable and to be resistant to
* implementation on things like GPUs with tiny-memory nodes and poor
* branching capability. Toward that end it throws branching and a large
* memory buffer into the mix. It can only be efficiently computed by a
* single core with at least ~32MB RAM.
*
* Search for "sequential memory hard algorithm" for academic references
* to similar concepts.
*
* Right now this takes ~1700ms on a 2.4ghz Intel Core i5. If this could
* be reduced to 1ms per derivation, it would take about 34 years to search
* the entire 40-bit address space for an average of ~17 years to generate
* a key colliding with a known existing address.
*/
// Initial starting digest
SHA256_Init(&sha);
SHA256_Update(&sha,(const unsigned char *)keyBytes,keyLen); // key
SHA256_Final(dig,&sha);
unsigned char finalDigest[ZT_SHA512_DIGEST_LEN];
unsigned char *digests = new unsigned char[ZT_SHA512_DIGEST_LEN * ZT_IDENTITY_DERIVEADDRESS_DIGESTS];
s20a.init(dig,256,"ZeroTier");
SHA512::hash(finalDigest,keyBytes,keyLen);
for(unsigned int i=0;i<(unsigned int)sizeof(digests);++i)
digests[i] = ((const unsigned char *)keyBytes)[i % keyLen];
unsigned char *ram = new unsigned char[ZT_IDENTITY_DERIVEADDRESS_MEMORY];
// Encrypt and digest a large memory buffer for several rounds
for(unsigned long i=0;i<ZT_IDENTITY_DERIVEADDRESS_MEMORY;++i)
ram[i] = (unsigned char)(i & 0xff) ^ dig[i & 31];
for(unsigned long r=0;r<ZT_IDENTITY_DERIVEADDRESS_ROUNDS;++r) {
SHA256_Init(&sha);
SHA256_Update(&sha,(const unsigned char *)keyBytes,keyLen);
SHA256_Update(&sha,dig,32);
for(unsigned long i=0;i<ZT_IDENTITY_DERIVEADDRESS_MEMORY;++i) {
if (ram[i] == 17) // Forces a branch to be required
ram[i] ^= dig[i & 31];
}
s20b.init(dig,256,"ZeroTier");
s20a.encrypt(ram,ram,ZT_IDENTITY_DERIVEADDRESS_MEMORY);
s20b.encrypt(ram,ram,ZT_IDENTITY_DERIVEADDRESS_MEMORY);
SHA256_Update(&sha,ram,ZT_IDENTITY_DERIVEADDRESS_MEMORY);
SHA256_Final(dig,&sha);
for(unsigned int r=0;r<ZT_IDENTITY_DERIVEADDRESS_ROUNDS;++r) {
for(unsigned int i=0;i<(ZT_SHA512_DIGEST_LEN * ZT_IDENTITY_DERIVEADDRESS_DIGESTS);++i)
digests[i] ^= finalDigest[i % ZT_SHA512_DIGEST_LEN];
for(unsigned int d=0;d<ZT_IDENTITY_DERIVEADDRESS_DIGESTS;++d)
SHA512::hash(digests + (ZT_SHA512_DIGEST_LEN * d),digests,ZT_SHA512_DIGEST_LEN * ZT_IDENTITY_DERIVEADDRESS_DIGESTS);
SHA512::hash(finalDigest,digests,ZT_SHA512_DIGEST_LEN * ZT_IDENTITY_DERIVEADDRESS_DIGESTS);
}
// Final digest, executed for twice our number of rounds
SHA256_Init(&sha);
for(unsigned long r=0;r<(ZT_IDENTITY_DERIVEADDRESS_ROUNDS * 2);++r) {
SHA256_Update(&sha,(const unsigned char *)keyBytes,keyLen);
SHA256_Update(&sha,ram,ZT_IDENTITY_DERIVEADDRESS_ROUNDS);
SHA256_Update(&sha,dig,32);
SHA256_Update(&sha,(const unsigned char *)keyBytes,keyLen);
}
SHA256_Final(dig,&sha);
delete [] digests;
delete [] ram;
return Address(dig,ZT_ADDRESS_LENGTH); // first 5 bytes of dig[]
return Address(finalDigest,ZT_ADDRESS_LENGTH); // first 5 bytes of dig[]
}
} // namespace ZeroTier

View file

@ -32,18 +32,13 @@
#include <stdlib.h>
#include <string>
#include "EllipticCurveKey.hpp"
#include "EllipticCurveKeyPair.hpp"
#include "Constants.hpp"
#include "Array.hpp"
#include "Utils.hpp"
#include "Address.hpp"
#include "C25519.hpp"
#include "Buffer.hpp"
/**
* Maximum length for a serialized identity
*/
#define IDENTITY_MAX_BINARY_SERIALIZED_LENGTH ((ZT_EC_MAX_BYTES * 2) + 256)
namespace ZeroTier {
/**
@ -55,22 +50,6 @@ namespace ZeroTier {
* The address derivation algorithm makes it computationally very expensive to
* search for a different public key that duplicates an existing address. (See
* code for deriveAddress() for this algorithm.)
*
* After derivation, the address must be checked against isReserved(). If the
* address is reserved, generation is repeated until a valid address results.
*
* Serialization of an identity:
*
* <[5] address> - 40-bit ZeroTier network address
* <[1] type> - Identity type ID (rest is type-dependent)
* <[1] key length> - Length of public key
* <[n] public key> - Elliptic curve public key
* <[1] sig length> - Length of ECDSA self-signature
* <[n] signature> - ECDSA signature of first four fields
* [<[1] key length>] - [Optional] Length of private key
* [<[n] private key>] - [Optional] Private key
*
* Local storage of an identity also requires storage of its private key.
*/
class Identity
{
@ -80,28 +59,26 @@ public:
*/
enum Type
{
/* Elliptic curve NIST-P-521 and ECDSA signature */
IDENTITY_TYPE_NIST_P_521 = 1
/* We won't need another identity type until quantum computers with
* tens of thousands of qubits are a reality. */
IDENTITY_TYPE_NIST_P_521 = 1, // OBSOLETE -- only present in some early alpha versions
IDENTITY_TYPE_C25519 = 2
};
Identity() :
_keyPair((EllipticCurveKeyPair *)0)
_privateKey((C25519::Private *)0)
{
}
Identity(const Identity &id) :
_keyPair((id._keyPair) ? new EllipticCurveKeyPair(*id._keyPair) : (EllipticCurveKeyPair *)0),
_publicKey(id._publicKey),
_address(id._address),
_signature(id._signature)
_publicKey(id._publicKey),
_signature(id._signature),
_privateKey((id._privateKey) ? new C25519::Private(*(id._privateKey)) : (C25519::Private *)0)
{
}
Identity(const char *str)
throw(std::invalid_argument) :
_keyPair((EllipticCurveKeyPair *)0)
_privateKey((C25519::Private *)0)
{
if (!fromString(str))
throw std::invalid_argument(std::string("invalid string-serialized identity: ") + str);
@ -109,7 +86,7 @@ public:
Identity(const std::string &str)
throw(std::invalid_argument) :
_keyPair((EllipticCurveKeyPair *)0)
_privateKey((C25519::Private *)0)
{
if (!fromString(str))
throw std::invalid_argument(std::string("invalid string-serialized identity: ") + str);
@ -118,35 +95,36 @@ public:
template<unsigned int C>
Identity(const Buffer<C> &b,unsigned int startAt = 0)
throw(std::out_of_range,std::invalid_argument) :
_keyPair((EllipticCurveKeyPair *)0)
_privateKey((C25519::Private *)0)
{
deserialize(b,startAt);
}
~Identity()
{
delete _keyPair;
delete _privateKey;
}
inline Identity &operator=(const Identity &id)
{
_keyPair = (id._keyPair) ? new EllipticCurveKeyPair(*id._keyPair) : (EllipticCurveKeyPair *)0;
_publicKey = id._publicKey;
_address = id._address;
_publicKey = id._publicKey;
_signature = id._signature;
if (id._privateKey) {
if (!_privateKey)
_privateKey = new C25519::Private();
*_privateKey = *(id._privateKey);
} else {
delete _privateKey;
_privateKey = (C25519::Private *)0;
}
return *this;
}
/**
* Generate a new identity (address, key pair)
*
* This is a somewhat time consuming operation by design, as the address
* is derived from the key using a purposefully expensive many-round
* hash/encrypt/hash operation. This took about two seconds on a 2.4ghz
* Intel Core i5 in 2013.
*
* In the very unlikely event that a reserved address is created, generate
* will automatically run again.
*
* This is a time consuming operation.
*/
void generate();
@ -166,19 +144,14 @@ public:
bool locallyValidate(bool doAddressDerivationCheck) const;
/**
* @return Private key pair or NULL if not included with this identity
* @return True if this identity contains a private key
*/
inline const EllipticCurveKeyPair *privateKeyPair() const throw() { return _keyPair; }
/**
* @return True if this identity has its private portion
*/
inline bool hasPrivate() const throw() { return (_keyPair != (EllipticCurveKeyPair *)0); }
inline bool hasPrivate() const throw() { return (_privateKey != (C25519::Private *)0); }
/**
* Shortcut method to perform key agreement with another identity
*
* This identity must have its private portion.
* This identity must have a private key. (Check hasPrivate())
*
* @param id Identity to agree with
* @param key Result parameter to fill with key bytes
@ -187,75 +160,17 @@ public:
*/
inline bool agree(const Identity &id,void *key,unsigned int klen) const
{
if ((id)&&(_keyPair))
return _keyPair->agree(id._publicKey,(unsigned char *)key,klen);
if (_privateKey) {
C25519::agree(*_privateKey,id._publicKey,key,klen);
return true;
}
return false;
}
/**
* Sign a hash with this identity's private key
*
* @param sha256 32-byte hash to sign
* @return ECDSA signature or empty string on failure or if identity has no private portion
*/
inline std::string sign(const void *sha256) const
{
if (_keyPair)
return _keyPair->sign(sha256);
return std::string();
}
/**
* Sign a block of data with this identity's private key
*
* This is a shortcut to SHA-256 hashing then signing.
*
* @param sha256 32-byte hash to sign
* @return ECDSA signature or empty string on failure or if identity has no private portion
*/
inline std::string sign(const void *data,unsigned int len) const
{
if (_keyPair)
return _keyPair->sign(data,len);
return std::string();
}
/**
* Verify something signed with this identity's public key
*
* @param sha256 32-byte hash to verify
* @param sigbytes Signature bytes
* @param siglen Length of signature
* @return True if signature is valid
*/
inline bool verifySignature(const void *sha256,const void *sigbytes,unsigned int siglen) const
{
return EllipticCurveKeyPair::verify(sha256,_publicKey,sigbytes,siglen);
}
/**
* Verify something signed with this identity's public key
*
* @param data Data to verify
* @param len Length of data to verify
* @param sigbytes Signature bytes
* @param siglen Length of signature
* @return True if signature is valid
*/
inline bool verifySignature(const void *data,unsigned int len,const void *sigbytes,unsigned int siglen) const
{
return EllipticCurveKeyPair::verify(data,len,_publicKey,sigbytes,siglen);
}
/**
* @return Public key (available in all identities)
*/
inline const EllipticCurveKey &publicKey() const throw() { return _publicKey; }
/**
* @return Identity type
*/
inline Type type() const throw() { return IDENTITY_TYPE_NIST_P_521; }
inline Type type() const throw() { return IDENTITY_TYPE_C25519; }
/**
* @return This identity's address
@ -274,14 +189,12 @@ public:
throw(std::out_of_range)
{
_address.appendTo(b);
b.append((unsigned char)IDENTITY_TYPE_NIST_P_521);
b.append((unsigned char)(_publicKey.size() & 0xff));
b.append(_publicKey.data(),_publicKey.size());
b.append((unsigned char)(_signature.length() & 0xff));
b.append(_signature);
if ((includePrivate)&&(_keyPair)) {
b.append((unsigned char)(_keyPair->priv().size() & 0xff));
b.append(_keyPair->priv().data(),_keyPair->priv().size());
b.append((unsigned char)IDENTITY_TYPE_C25519);
b.append(_publicKey.data,_publicKey.size());
b.append(_signature.data,_signature.size());
if ((_privateKey)&&(includePrivate)) {
b.append((unsigned char)_privateKey.size());
b.append(_privateKey.data,_privateKey.size());
} else b.append((unsigned char)0);
}
@ -301,33 +214,27 @@ public:
inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
throw(std::out_of_range,std::invalid_argument)
{
delete _keyPair;
_keyPair = (EllipticCurveKeyPair *)0;
delete _privateKey;
_privateKey = (C25519::Private *)0;
unsigned int p = startAt;
_address.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
p += ZT_ADDRESS_LENGTH;
if (b[p++] != IDENTITY_TYPE_NIST_P_521)
if (b[p++] != IDENTITY_TYPE_C25519)
throw std::invalid_argument("Identity: deserialize(): unsupported identity type");
unsigned int publicKeyLength = b[p++];
if (!publicKeyLength)
throw std::invalid_argument("Identity: deserialize(): no public key");
_publicKey.set(b.field(p,publicKeyLength),publicKeyLength);
p += publicKeyLength;
unsigned int signatureLength = b[p++];
if (!signatureLength)
throw std::invalid_argument("Identity: deserialize(): no signature");
_signature.assign((const char *)b.field(p,signatureLength),signatureLength);
p += signatureLength;
memcpy(_publicKey.data,field(p,_publicKey.size()),_publicKey.size());
p += _publicKey.size();
memcpy(_signature.data,field(p,_signature.size()),_signature.size());
p += _signature.size();
unsigned int privateKeyLength = b[p++];
if (privateKeyLength) {
_keyPair = new EllipticCurveKeyPair(_publicKey,EllipticCurveKey(b.field(p,privateKeyLength),privateKeyLength));
p += privateKeyLength;
if ((privateKeyLength)&&(privateKeyLength == ZT_C25519_PRIVATE_KEY_LEN)) {
_privateKey = new C25519::Private();
memcpy(_privateKey->data,field(p,ZT_C25519_PRIVATE_KEY_LEN),ZT_C25519_PRIVATE_KEY_LEN);
p += ZT_C25519_PRIVATE_KEY_LEN;
}
return (p - startAt);
@ -356,27 +263,10 @@ public:
/**
* @return True if this identity contains something
*/
inline operator bool() const throw() { return (_publicKey.size() != 0); }
inline operator bool() const throw() { return (_address); }
inline bool operator==(const Identity &id) const
throw()
{
if (_address == id._address) {
if ((_keyPair)&&(id._keyPair))
return (*_keyPair == *id._keyPair);
return (_publicKey == id._publicKey);
}
return false;
}
inline bool operator<(const Identity &id) const
throw()
{
if (_address < id._address)
return true;
else if (_address == id._address)
return (_publicKey < id._publicKey);
return false;
}
inline bool operator==(const Identity &id) const throw() { return ((_address == id._address)&&(_publicKey == id._publicKey)); }
inline bool operator<(const Identity &id) const throw() { return ((_address < id._address)||((_address == id._address)&&(_publicKey < id._publicKey))); }
inline bool operator!=(const Identity &id) const throw() { return !(*this == id); }
inline bool operator>(const Identity &id) const throw() { return (id < *this); }
inline bool operator<=(const Identity &id) const throw() { return !(id < *this); }
@ -386,10 +276,10 @@ private:
// Compute an address from public key bytes
static Address deriveAddress(const void *keyBytes,unsigned int keyLen);
EllipticCurveKeyPair *_keyPair;
EllipticCurveKey _publicKey;
Address _address;
std::string _signature;
C25519::Public _publicKey;
C25519::Signature _signature;
C25519::Private *_privateKey;
};
} // namespace ZeroTier