Add skeleton of certificate-based private network authentication. Also remove some old code.

This commit is contained in:
Adam Ierymenko 2013-07-27 16:20:08 -04:00
parent d35d322890
commit 7a17f6ca80
8 changed files with 40 additions and 399 deletions

View File

@ -1,94 +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_BLOBARRAY_HPP
#define _ZT_BLOBARRAY_HPP
#include <vector>
#include <string>
#include <algorithm>
namespace ZeroTier {
/**
* A vector of binary strings serializable in a packed format
*
* The format uses variable-length integers to indicate the length of each
* field. Each byte of the length has another byte with seven more significant
* bits if its 8th bit is set. Fields can be up to 2^28 in length.
*/
class BlobArray : public std::vector<std::string>
{
public:
inline std::string serialize() const
{
std::string r;
for(BlobArray::const_iterator i=begin();i!=end();++i) {
unsigned int flen = (unsigned int)i->length();
do {
unsigned char flenb = (unsigned char)(flen & 0x7f);
flen >>= 7;
flenb |= (flen) ? 0x80 : 0;
r.push_back((char)flenb);
} while (flen);
r.append(*i);
}
return r;
}
/**
* Deserialize, replacing the current contents of this array
*
* @param data Serialized binary data
* @param len Length of serialized data
*/
inline void deserialize(const void *data,unsigned int len)
{
clear();
for(unsigned int i=0;i<len;) {
unsigned int flen = 0;
unsigned int chunk = 0;
while (i < len) {
flen |= ((unsigned int)(((const unsigned char *)data)[i] & 0x7f)) << (7 * chunk++);
if (!(((const unsigned char *)data)[i++] & 0x80))
break;
}
flen = std::min(flen,len - i);
push_back(std::string(((const char *)data) + i,flen));
i += flen;
}
}
inline void deserialize(const std::string &data)
{
deserialize(data.data(),(unsigned int)data.length());
}
};
} // namespace ZeroTier
#endif

View File

@ -49,7 +49,20 @@ namespace ZeroTier {
class NodeConfig;
/**
* Local membership to a network
* A virtual LAN
*
* Networks can be open or closed.
*
* Open networks do not track membership. Anyone is allowed to communicate
* over them.
*
* Closed networks track membership by way of timestamped signatures. When
* the network requests its configuration, one of the fields returned is
* a signature for the identity of the peer on the network. This signature
* includes a timestamp. When a peer communicates with other peers on a
* closed network, it periodically (and pre-emptively) propagates this
* signature to the peers with which it is communicating. Peers reject
* packets with an error if no recent signature is on file.
*/
class Network : NonCopyable
{

View File

@ -56,7 +56,6 @@
#include "Logger.hpp"
#include "Constants.hpp"
#include "InetAddress.hpp"
#include "Pack.hpp"
#include "Salsa20.hpp"
#include "HMAC.hpp"
#include "RuntimeEnvironment.hpp"

View File

@ -1,159 +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 <iostream>
#include <string.h>
#include <stdlib.h>
#include "Pack.hpp"
#include "BlobArray.hpp"
#include "Utils.hpp"
#include <openssl/sha.h>
namespace ZeroTier {
std::vector<const Pack::Entry *> Pack::getAll() const
{
std::vector<const Entry *> v;
for(std::map<std::string,Entry>::const_iterator e=_entries.begin();e!=_entries.end();++e)
v.push_back(&(e->second));
return v;
}
const Pack::Entry *Pack::get(const std::string &name) const
{
std::map<std::string,Entry>::const_iterator e(_entries.find(name));
return ((e == _entries.end()) ? (const Entry *)0 : &(e->second));
}
const Pack::Entry *Pack::put(const std::string &name,const std::string &content)
{
SHA256_CTX sha;
Pack::Entry &e = _entries[name];
e.name = name;
e.content = content;
SHA256_Init(&sha);
SHA256_Update(&sha,content.data(),content.length());
SHA256_Final(e.sha256,&sha);
e.signedBy = 0;
e.signature.assign((const char *)0,0);
return &e;
}
void Pack::clear()
{
_entries.clear();
}
std::string Pack::serialize() const
{
BlobArray archive;
for(std::map<std::string,Entry>::const_iterator e=_entries.begin();e!=_entries.end();++e) {
BlobArray entry;
entry.push_back(e->second.name);
entry.push_back(e->second.content);
entry.push_back(std::string((const char *)e->second.sha256,sizeof(e->second.sha256)));
entry.push_back(e->second.signedBy.toBinaryString());
entry.push_back(e->second.signature);
archive.push_back(entry.serialize());
}
std::string ser(archive.serialize());
std::string comp;
Utils::compress(ser.begin(),ser.end(),Utils::StringAppendOutput(comp));
return comp;
}
bool Pack::deserialize(const void *sd,unsigned int sdlen)
{
unsigned char dig[32];
SHA256_CTX sha;
std::string decomp;
if (!Utils::decompress(((const char *)sd),((const char *)sd) + sdlen,Utils::StringAppendOutput(decomp)))
return false;
BlobArray archive;
archive.deserialize(decomp.data(),decomp.length());
clear();
for(BlobArray::const_iterator i=archive.begin();i!=archive.end();++i) {
BlobArray entry;
entry.deserialize(i->data(),i->length());
if (entry.size() != 5) return false;
if (entry[2].length() != 32) return false; // SHA-256
if (entry[3].length() != ZT_ADDRESS_LENGTH) return false; // Address
Pack::Entry &e = _entries[entry[0]];
e.name = entry[0];
e.content = entry[1];
SHA256_Init(&sha);
SHA256_Update(&sha,e.content.data(),e.content.length());
SHA256_Final(dig,&sha);
if (memcmp(dig,entry[2].data(),32)) return false; // integrity check failed
memcpy(e.sha256,dig,32);
if (entry[3].length() == ZT_ADDRESS_LENGTH)
e.signedBy.setTo(entry[3].data());
else e.signedBy = 0;
e.signature = entry[4];
}
return true;
}
bool Pack::signAll(const Identity &id)
{
for(std::map<std::string,Entry>::iterator e=_entries.begin();e!=_entries.end();++e) {
e->second.signedBy = id.address();
e->second.signature = id.sign(e->second.sha256);
if (!e->second.signature.length())
return false;
}
return true;
}
std::vector<const Pack::Entry *> Pack::verifyAll(const Identity &id,bool mandatory) const
{
std::vector<const Entry *> bad;
for(std::map<std::string,Entry>::const_iterator e=_entries.begin();e!=_entries.end();++e) {
if ((e->second.signedBy)&&(e->second.signature.length())) {
if (id.address() != e->second.signedBy)
bad.push_back(&(e->second));
else if (!id.verifySignature(e->second.sha256,e->second.signature.data(),e->second.signature.length()))
bad.push_back(&(e->second));
} else if (mandatory)
bad.push_back(&(e->second));
}
return bad;
}
} // namespace ZeroTier

View File

@ -1,141 +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_PACK_HPP
#define _ZT_PACK_HPP
#include <string>
#include <map>
#include <list>
#include <stdexcept>
#include "Address.hpp"
#include "Identity.hpp"
namespace ZeroTier {
/**
* A very simple archive format for distributing packs of files or resources
*
* This is used for things like the auto-updater. It's not suitable for huge
* files, since at present it must work in memory. Packs support signing with
* identities and signature verification.
*/
class Pack
{
public:
/**
* Pack entry structure for looking up deserialized entries
*/
struct Entry
{
std::string name;
std::string content;
unsigned char sha256[32];
Address signedBy;
std::string signature;
};
Pack() {}
~Pack() {}
/**
* @return Vector of all entries
*/
std::vector<const Entry *> getAll() const;
/**
* Look up an entry
*
* @param name Name to look up
* @return Pointer to entry if it exists or NULL if not found
*/
const Entry *get(const std::string &name) const;
/**
* Add an entry to this pack
*
* @param name Entry to add
* @param content Entry's contents
* @return The new entry
*/
const Entry *put(const std::string &name,const std::string &content);
/**
* Remove all entries
*/
void clear();
/**
* @return Number of entries in pack
*/
inline unsigned int numEntries() const { return (unsigned int)_entries.size(); }
/**
* Serialize this pack
*
* @return Serialized form (compressed with LZ4)
*/
std::string serialize() const;
/**
* Deserialize this pack
*
* Any current contents are lost. This does not verify signatures,
* but does check SHA256 hashes for entry integrity. If the return
* value is false, the pack's contents are undefined.
*
* @param sd Serialized data
* @param sdlen Length of serialized data
* @return True on success, false on deserialization error
*/
bool deserialize(const void *sd,unsigned int sdlen);
inline bool deserialize(const std::string &sd) { return deserialize(sd.data(),sd.length()); }
/**
* Sign all entries in this pack with a given identity
*
* @param id Identity to sign with
* @return True on signature success, false if error
*/
bool signAll(const Identity &id);
/**
* Verify all signed entries
*
* @param id Identity to verify against
* @param mandatory If true, require that all entries be signed and fail if no signature
* @return Vector of entries that failed verification or empty vector if all passed
*/
std::vector<const Entry *> verifyAll(const Identity &id,bool mandatory) const;
private:
std::map<std::string,Entry> _entries;
};
} // namespace ZeroTier
#endif

View File

@ -42,6 +42,7 @@ const char *Packet::verbString(Verb v)
case VERB_FRAME: return "FRAME";
case VERB_MULTICAST_FRAME: return "MULTICAST_FRAME";
case VERB_MULTICAST_LIKE: return "MULTICAST_LIKE";
case VERB_NETWORK_PERMISSION_CERTIFICATE: return "NETWORK_PERMISSION_CERTIFICATE";
}
return "(unknown)";
}
@ -57,6 +58,8 @@ const char *Packet::errorString(ErrorCode e)
case ERROR_IDENTITY_COLLISION: return "IDENTITY_COLLISION";
case ERROR_IDENTITY_INVALID: return "IDENTITY_INVALID";
case ERROR_UNSUPPORTED_OPERATION: return "UNSUPPORTED_OPERATION";
case ERROR_NO_NETWORK_CERTIFICATE_ON_FILE: return "NO_NETWORK_CERTIFICATE_ON_FILE";
case ERROR_OBJECT_EXPIRED: return "OBJECT_EXPIRED";
}
return "(unknown)";
}

View File

@ -463,7 +463,22 @@ public:
*
* No OK or ERROR is generated.
*/
VERB_MULTICAST_FRAME = 9
VERB_MULTICAST_FRAME = 9,
/* Network permission certificate:
* <[8] 64-bit network ID>
* <[1] flags (currently unused, must be 0)>
* <[8] certificate timestamp>
* <[8] 16-bit length of signature>
* <[...] ECDSA signature of my binary serialized identity and timestamp>
*
* This message is used to send ahead of time a certificate proving
* this node has permission to communicate on a private network.
*
* OK is generated on acceptance. ERROR is returned on failure. In both
* cases the payload is the network ID.
*/
VERB_NETWORK_PERMISSION_CERTIFICATE = 10
};
/**
@ -490,7 +505,13 @@ public:
ERROR_IDENTITY_INVALID = 5,
/* Verb or use case not supported/enabled by this node */
ERROR_UNSUPPORTED_OPERATION = 6
ERROR_UNSUPPORTED_OPERATION = 6,
/* Message to private network rejected -- no unexpired certificate on file */
ERROR_NO_NETWORK_CERTIFICATE_ON_FILE = 7,
/* Object is expired (e.g. network certificate) */
ERROR_OBJECT_EXPIRED = 8
};
/**

View File

@ -18,7 +18,6 @@ OBJS=\
node/NodeConfig.o \
node/Packet.o \
node/PacketDecoder.o \
node/Pack.o \
node/Peer.o \
node/Salsa20.o \
node/Switch.o \