TON of refactoring, moon/planet is DEAD, ECC P-384 is integrated (but not enabled), and multicast work and cleanup. Whew.

This commit is contained in:
Adam Ierymenko 2019-07-17 10:52:08 -05:00
parent 640bbaabbf
commit fe2215df00
36 changed files with 556 additions and 1844 deletions

View file

@ -36,6 +36,7 @@
#include "C25519.hpp"
#include "Buffer.hpp"
#include "SHA512.hpp"
#include "ECC384.hpp"
#define ZT_IDENTITY_STRING_BUFFER_LENGTH 384
@ -54,61 +55,45 @@ namespace ZeroTier {
class Identity
{
public:
Identity() :
_privateKey((C25519::Private *)0)
enum Type
{
}
C25519 = 0, // Curve25519 and Ed25519
P384 = 1 // NIST P-384 ECDH and ECDSA
};
Identity(const Identity &id) :
_address(id._address),
_publicKey(id._publicKey),
_privateKey((id._privateKey) ? new C25519::Private(*(id._privateKey)) : (C25519::Private *)0)
{
}
Identity() { memset(reinterpret_cast<void *>(this),0,sizeof(Identity)); }
Identity(const Identity &id) { memcpy(reinterpret_cast<void *>(this),&id,sizeof(Identity)); }
Identity(const char *str) :
_privateKey((C25519::Private *)0)
Identity(const char *str)
{
if (!fromString(str))
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
}
template<unsigned int C>
Identity(const Buffer<C> &b,unsigned int startAt = 0) :
_privateKey((C25519::Private *)0)
{
deserialize(b,startAt);
}
Identity(const Buffer<C> &b,unsigned int startAt = 0) { deserialize(b,startAt); }
~Identity()
{
if (_privateKey) {
Utils::burn(_privateKey,sizeof(C25519::Private));
delete _privateKey;
}
}
~Identity() { Utils::burn(reinterpret_cast<void *>(this),sizeof(Identity)); }
inline Identity &operator=(const Identity &id)
{
_address = id._address;
_publicKey = id._publicKey;
if (id._privateKey) {
if (!_privateKey)
_privateKey = new C25519::Private();
*_privateKey = *(id._privateKey);
} else {
delete _privateKey;
_privateKey = (C25519::Private *)0;
}
memcpy(reinterpret_cast<void *>(this),&id,sizeof(Identity));
return *this;
}
/**
* @return Identity type
*/
inline Type type() const { return _type; }
/**
* Generate a new identity (address, key pair)
*
* This is a time consuming operation.
*
* @param t Type of identity to generate
*/
void generate();
void generate(const Type t);
/**
* Check the validity of this identity's pairing of key to address
@ -120,7 +105,7 @@ public:
/**
* @return True if this identity contains a private key
*/
inline bool hasPrivate() const { return (_privateKey != (C25519::Private *)0); }
inline bool hasPrivate() const { return _hasPrivate; }
/**
* Compute the SHA512 hash of our private key (if we have one)
@ -130,9 +115,15 @@ public:
*/
inline bool sha512PrivateKey(void *sha) const
{
if (_privateKey) {
SHA512::hash(sha,_privateKey->data,ZT_C25519_PRIVATE_KEY_LEN);
return true;
if (_hasPrivate) {
switch(_type) {
case C25519:
SHA512::hash(sha,_k.t0.priv.data,ZT_C25519_PRIVATE_KEY_LEN);
return true;
case P384:
SHA512::hash(sha,_k.t1.priv,ZT_ECC384_PRIVATE_KEY_SIZE);
return true;
}
}
return false;
}
@ -140,14 +131,34 @@ public:
/**
* Sign a message with this identity (private key required)
*
* The signature buffer should be large enough for the largest
* signature, which is currently 96 bytes.
*
* @param data Data to sign
* @param len Length of data
* @param sig Buffer to receive signature
* @param siglen Length of buffer
* @return Number of bytes actually written to sig or 0 on error
*/
inline C25519::Signature sign(const void *data,unsigned int len) const
inline unsigned int sign(const void *data,unsigned int len,void *sig,unsigned int siglen) const
{
if (_privateKey)
return C25519::sign(*_privateKey,_publicKey,data,len);
throw ZT_EXCEPTION_PRIVATE_KEY_REQUIRED;
uint8_t h[64];
if (!_hasPrivate)
return 0;
switch(_type) {
case C25519:
if (siglen < ZT_C25519_SIGNATURE_LEN)
return 0;
C25519::sign(_k.t0.priv,_k.t0.pub,data,len,sig);
return ZT_C25519_SIGNATURE_LEN;
case P384:
if (siglen < ZT_ECC384_SIGNATURE_SIZE)
return 0;
SHA512::hash(h,data,len);
ECC384ECDSASign(_k.t1.priv,h,(uint8_t *)sig);
return ZT_ECC384_SIGNATURE_SIZE;
}
return 0;
}
/**
@ -159,17 +170,20 @@ public:
* @param siglen Length of signature in bytes
* @return True if signature validates and data integrity checks
*/
inline bool verify(const void *data,unsigned int len,const void *signature,unsigned int siglen) const { return C25519::verify(_publicKey,data,len,signature,siglen); }
/**
* Verify a message signature against this identity
*
* @param data Data to check
* @param len Length of data
* @param signature Signature
* @return True if signature validates and data integrity checks
*/
inline bool verify(const void *data,unsigned int len,const C25519::Signature &signature) const { return C25519::verify(_publicKey,data,len,signature); }
inline bool verify(const void *data,unsigned int len,const void *sig,unsigned int siglen) const
{
uint8_t h[64];
switch(_type) {
case C25519:
return C25519::verify(_k.t0.pub,data,len,sig,siglen);
case P384:
if (siglen != ZT_ECC384_SIGNATURE_SIZE)
return false;
SHA512::hash(h,data,len);
return ECC384ECDSAVerify(_k.t1.pub,h,(const uint8_t *)sig);
}
return false;
}
/**
* Shortcut method to perform key agreement with another identity
@ -183,9 +197,26 @@ public:
*/
inline bool agree(const Identity &id,void *key,unsigned int klen) const
{
if (_privateKey) {
C25519::agree(*_privateKey,id._publicKey,key,klen);
return true;
uint8_t ecc384RawSecret[ZT_ECC384_SHARED_SECRET_SIZE];
uint8_t h[64];
if (_hasPrivate) {
switch(_type) {
case C25519:
C25519::agree(_k.t0.priv,id._k.t0.pub,key,klen);
return true;
case P384:
ECC384ECDH(id._k.t1.pub,_k.t1.priv,ecc384RawSecret);
SHA512::hash(h,ecc384RawSecret,sizeof(ecc384RawSecret));
unsigned int hi = 0;
for(unsigned int i=0;i<klen;++i) {
if (hi == 64) {
hi = 0;
SHA512::hash(h,h,64);
}
((uint8_t *)key)[i] = h[hi++];
}
return true;
}
}
return false;
}
@ -206,12 +237,28 @@ public:
inline void serialize(Buffer<C> &b,bool includePrivate = false) const
{
_address.appendTo(b);
b.append((uint8_t)0); // C25519/Ed25519 identity type
b.append(_publicKey.data,ZT_C25519_PUBLIC_KEY_LEN);
if ((_privateKey)&&(includePrivate)) {
b.append((unsigned char)ZT_C25519_PRIVATE_KEY_LEN);
b.append(_privateKey->data,ZT_C25519_PRIVATE_KEY_LEN);
} else b.append((unsigned char)0);
switch(_type) {
case C25519:
b.append((uint8_t)C25519);
b.append(_k.t0.pub.data,ZT_C25519_PUBLIC_KEY_LEN);
if ((_hasPrivate)&&(includePrivate)) {
b.append((uint8_t)ZT_C25519_PRIVATE_KEY_LEN);
b.append(_k.t0.priv.data,ZT_C25519_PRIVATE_KEY_LEN);
} else {
b.append((uint8_t)0);
}
break;
case P384:
b.append((uint8_t)P384);
b.append(_k.t1.pub,ZT_ECC384_PUBLIC_KEY_SIZE);
if ((_hasPrivate)&&(includePrivate)) {
b.append((uint8_t)ZT_ECC384_PRIVATE_KEY_SIZE);
b.append(_k.t1.priv,ZT_ECC384_PRIVATE_KEY_SIZE);
} else {
b.append((uint8_t)0);
}
break;
}
}
/**
@ -229,27 +276,47 @@ public:
template<unsigned int C>
inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
{
delete _privateKey;
_privateKey = (C25519::Private *)0;
_hasPrivate = false;
unsigned int p = startAt;
unsigned int pkl;
_address.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
p += ZT_ADDRESS_LENGTH;
if (b[p++] != 0)
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
memcpy(_publicKey.data,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN);
p += ZT_C25519_PUBLIC_KEY_LEN;
unsigned int privateKeyLength = (unsigned int)b[p++];
if (privateKeyLength) {
if (privateKeyLength != ZT_C25519_PRIVATE_KEY_LEN)
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
_privateKey = new C25519::Private();
memcpy(_privateKey->data,b.field(p,ZT_C25519_PRIVATE_KEY_LEN),ZT_C25519_PRIVATE_KEY_LEN);
p += ZT_C25519_PRIVATE_KEY_LEN;
_type = (Type)b[p++];
switch(_type) {
case C25519:
memcpy(_k.t0.pub.data,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN);
p += ZT_C25519_PUBLIC_KEY_LEN;
pkl = (unsigned int)b[p++];
if (pkl) {
if (pkl != ZT_C25519_PRIVATE_KEY_LEN)
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
_hasPrivate = true;
memcpy(_k.t0.priv.data,b.field(p,ZT_C25519_PRIVATE_KEY_LEN),ZT_C25519_PRIVATE_KEY_LEN);
p += ZT_C25519_PRIVATE_KEY_LEN;
} else {
memset(_k.t0.priv.data,0,ZT_C25519_PRIVATE_KEY_LEN);
_hasPrivate = false;
}
break;
case P384:
memcpy(_k.t0.pub.data,b.field(p,ZT_ECC384_PUBLIC_KEY_SIZE),ZT_ECC384_PUBLIC_KEY_SIZE);
p += ZT_ECC384_PUBLIC_KEY_SIZE;
pkl = (unsigned int)b[p++];
if (pkl) {
if (pkl != ZT_ECC384_PRIVATE_KEY_SIZE)
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN;
_hasPrivate = true;
memcpy(_k.t1.priv,b.field(p,ZT_ECC384_PRIVATE_KEY_SIZE),ZT_ECC384_PRIVATE_KEY_SIZE);
p += ZT_ECC384_PRIVATE_KEY_SIZE;
} else {
memset(_k.t1.priv,0,ZT_ECC384_PRIVATE_KEY_SIZE);
_hasPrivate = false;
}
break;
default:
throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
}
return (p - startAt);
@ -275,40 +342,64 @@ public:
*/
bool fromString(const char *str);
/**
* @return C25519 public key
*/
inline const C25519::Public &publicKey() const { return _publicKey; }
/**
* @return C25519 key pair (only returns valid pair if private key is present in this Identity object)
*/
inline const C25519::Pair privateKeyPair() const
{
C25519::Pair pair;
pair.pub = _publicKey;
if (_privateKey)
pair.priv = *_privateKey;
else memset(pair.priv.data,0,ZT_C25519_PRIVATE_KEY_LEN);
return pair;
}
/**
* @return True if this identity contains something
*/
inline operator bool() const { return (_address); }
inline bool operator==(const Identity &id) const { return ((_address == id._address)&&(memcmp(_publicKey.data,id._publicKey.data,ZT_C25519_PUBLIC_KEY_LEN) == 0)); }
inline bool operator<(const Identity &id) const { return ((_address < id._address)||((_address == id._address)&&(memcmp(_publicKey.data,id._publicKey.data,ZT_C25519_PUBLIC_KEY_LEN) < 0))); }
inline bool operator==(const Identity &id) const
{
if ((_address == id._address)&&(_type == id._type)) {
switch(_type) {
case C25519:
return (memcmp(_k.t0.pub.data,id._k.t0.pub.data,ZT_C25519_PUBLIC_KEY_LEN) == 0);
case P384:
return (memcmp(_k.t1.pub,id._k.t1.pub,ZT_ECC384_PUBLIC_KEY_SIZE) == 0);
default:
return false;
}
}
return false;
}
inline bool operator<(const Identity &id) const
{
if (_address < id._address)
return true;
if (_address == id._address) {
if ((int)_type < (int)id._type)
return true;
if (_type == id._type) {
switch(_type) {
case C25519:
return (memcmp(_k.t0.pub.data,id._k.t0.pub.data,ZT_C25519_PUBLIC_KEY_LEN) < 0);
case P384:
return (memcmp(_k.t1.pub,id._k.t1.pub,ZT_ECC384_PUBLIC_KEY_SIZE) < 0);
}
}
}
return false;
}
inline bool operator!=(const Identity &id) const { return !(*this == id); }
inline bool operator>(const Identity &id) const { return (id < *this); }
inline bool operator<=(const Identity &id) const { return !(id < *this); }
inline bool operator>=(const Identity &id) const { return !(*this < id); }
inline unsigned long hashCode() const { return (unsigned long)_address.toInt(); }
private:
Address _address;
C25519::Public _publicKey;
C25519::Private *_privateKey;
union {
struct {
C25519::Public pub;
C25519::Private priv;
} t0;
struct {
uint8_t pub[ZT_ECC384_PUBLIC_KEY_SIZE];
uint8_t priv[ZT_ECC384_PRIVATE_KEY_SIZE];
} t1;
} _k;
Type _type;
bool _hasPrivate;
};
} // namespace ZeroTier