mirror of
https://github.com/ZeroTier/ZeroTierOne
synced 2025-08-20 05:13:58 -07:00
Integrating new crypto -- work in progress, wont build yet.
This commit is contained in:
parent
02f3369185
commit
3b2d98e7dc
105 changed files with 124 additions and 37722 deletions
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue