diff --git a/node/Dictionary.cpp b/node/Dictionary.cpp
new file mode 100644
index 000000000..0572783b6
--- /dev/null
+++ b/node/Dictionary.cpp
@@ -0,0 +1,154 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2011-2014 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 .
+ *
+ * --
+ *
+ * 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 "Dictionary.hpp"
+#include "C25519.hpp"
+#include "Identity.hpp"
+#include "Utils.hpp"
+
+namespace ZeroTier {
+
+void Dictionary::fromString(const char *s)
+{
+ clear();
+ bool escapeState = false;
+ std::string keyBuf;
+ std::string *element = &keyBuf;
+ while (*s) {
+ if (escapeState) {
+ escapeState = false;
+ switch(*s) {
+ case '0':
+ element->push_back((char)0);
+ break;
+ case 'r':
+ element->push_back('\r');
+ break;
+ case 'n':
+ element->push_back('\n');
+ break;
+ default:
+ element->push_back(*s);
+ break;
+ }
+ } else {
+ if (*s == '\\') {
+ escapeState = true;
+ } else if (*s == '=') {
+ if (element == &keyBuf)
+ element = &((*this)[keyBuf]);
+ } else if ((*s == '\r')||(*s == '\n')) {
+ if ((element == &keyBuf)&&(keyBuf.length() > 0))
+ (*this)[keyBuf];
+ keyBuf = "";
+ element = &keyBuf;
+ } else element->push_back(*s);
+ }
+ ++s;
+ }
+ if ((element == &keyBuf)&&(keyBuf.length() > 0))
+ (*this)[keyBuf];
+}
+
+bool Dictionary::sign(const Identity &id)
+{
+ try {
+ std::string buf;
+ _mkSigBuf(buf);
+ char nows[32];
+ Utils::snprintf(nows,sizeof(nows),"%llx",(unsigned long long)Utils::now());
+ C25519::Signature sig(id.sign(buf.data(),buf.length()));
+ (*this)[ZT_DICTIONARY_SIGNATURE] = Utils::hex(sig.data,sig.size());
+ (*this)[ZT_DICTIONARY_SIGNATURE_IDENTITY] = id.toString(false);
+ (*this)[ZT_DICTIONARY_SIGNATURE_TIMESTAMP] = nows;
+ return true;
+ } catch ( ... ) {
+ return false;
+ }
+}
+
+bool Dictionary::verify(const Identity &id) const
+{
+ try {
+ std::string buf;
+ _mkSigBuf(buf);
+ const_iterator sig(find(ZT_DICTIONARY_SIGNATURE));
+ if (sig == end())
+ return false;
+ std::string sigbin(Utils::unhex(sig->second));
+ return id.verify(buf.data(),buf.length(),sigbin.data(),sigbin.length());
+ } catch ( ... ) {
+ return false;
+ }
+}
+
+void Dictionary::_mkSigBuf(std::string &buf) const
+{
+ unsigned long pairs = 0;
+ for(const_iterator i(begin());i!=end();++i) {
+ if ((i->first.length() < 2)||( (i->first[0] != '~')&&(i->first[1] != '!') )) {
+ buf.append(i->first);
+ buf.push_back('=');
+ buf.append(i->second);
+ buf.push_back('\0');
+ ++pairs;
+ }
+ }
+ buf.push_back((char)0xff);
+ buf.push_back((char)((pairs >> 24) & 0xff)); // pad with number of key/value pairs at end
+ buf.push_back((char)((pairs >> 16) & 0xff));
+ buf.push_back((char)((pairs >> 8) & 0xff));
+ buf.push_back((char)(pairs & 0xff));
+}
+
+void Dictionary::_appendEsc(const char *data,unsigned int len,std::string &to)
+{
+ for(unsigned int i=0;i being sorted, but no
+ * other code does. So if the underlying data structure is ever swapped
+ * out for an unsorted one, the signature code will have to be updated
+ * to sort before composing the string to sign.
*/
class Dictionary : public std::map
{
public:
- Dictionary()
- {
- }
+ Dictionary() {}
/**
* @param s String-serialized dictionary
*/
- Dictionary(const char *s)
- {
- fromString(s);
- }
+ Dictionary(const char *s) { fromString(s); }
/**
* @param s String-serialized dictionary
*/
- Dictionary(const std::string &s)
- {
- fromString(s.c_str());
- }
+ Dictionary(const std::string &s) { fromString(s.c_str()); }
/**
* Get a key, throwing an exception if it is not present
@@ -102,10 +109,7 @@ public:
* @param key Key to check
* @return True if dictionary contains key
*/
- inline bool contains(const std::string &key) const
- {
- return (find(key) != end());
- }
+ inline bool contains(const std::string &key) const { return (find(key) != end()); }
/**
* @return String-serialized dictionary
@@ -113,14 +117,12 @@ public:
inline std::string toString() const
{
std::string s;
-
for(const_iterator kv(begin());kv!=end();++kv) {
_appendEsc(kv->first.data(),(unsigned int)kv->first.length(),s);
s.push_back('=');
_appendEsc(kv->second.data(),(unsigned int)kv->second.length(),s);
s.append(ZT_EOL_S);
}
-
return s;
}
@@ -129,78 +131,43 @@ public:
*
* @param s String-serialized dictionary
*/
- inline void fromString(const char *s)
+ void fromString(const char *s);
+ inline void fromString(const std::string &s) { fromString(s.c_str()); }
+
+ /**
+ * @return True if this dictionary is cryptographically signed
+ */
+ inline bool hasSignature() const { return (find(ZT_DICTIONARY_SIGNATURE) != end()); }
+
+ /**
+ * Remove any signature from this dictionary
+ */
+ inline void removeSignature()
{
- clear();
- bool escapeState = false;
- std::string keyBuf;
- std::string *element = &keyBuf;
- while (*s) {
- if (escapeState) {
- escapeState = false;
- switch(*s) {
- case '0':
- element->push_back((char)0);
- break;
- case 'r':
- element->push_back('\r');
- break;
- case 'n':
- element->push_back('\n');
- break;
- default:
- element->push_back(*s);
- break;
- }
- } else {
- if (*s == '\\') {
- escapeState = true;
- } else if (*s == '=') {
- if (element == &keyBuf)
- element = &((*this)[keyBuf]);
- } else if ((*s == '\r')||(*s == '\n')) {
- if ((element == &keyBuf)&&(keyBuf.length() > 0))
- (*this)[keyBuf];
- keyBuf = "";
- element = &keyBuf;
- } else element->push_back(*s);
- }
- ++s;
- }
- if ((element == &keyBuf)&&(keyBuf.length() > 0))
- (*this)[keyBuf];
- }
- inline void fromString(const std::string &s)
- {
- fromString(s.c_str());
+ erase(ZT_DICTIONARY_SIGNATURE);
+ erase(ZT_DICTIONARY_SIGNATURE_IDENTITY);
+ erase(ZT_DICTIONARY_SIGNATURE_TIMESTAMP);
}
+ /**
+ * Add or update signature fields with a signature of all other keys and values
+ *
+ * @param with Identity to sign with (must have secret key)
+ * @return True on success
+ */
+ bool sign(const Identity &id);
+
+ /**
+ * Verify signature against an identity
+ *
+ * @param id Identity to verify against
+ * @return True if signature verification OK
+ */
+ bool verify(const Identity &id) const;
+
private:
- static inline void _appendEsc(const char *data,unsigned int len,std::string &to)
- {
- for(unsigned int i=0;i= 48)&&(c <= 57)) { // 0..9
if ((n ^= 1))
@@ -117,16 +121,22 @@ std::string Utils::unhex(const char *hex)
r.push_back((char)(b | (c - (97 - 10))));
else b = (c - (97 - 10)) << 4;
}
+ if (hex == eof)
+ break;
}
return r;
}
-unsigned int Utils::unhex(const char *hex,void *buf,unsigned int len)
+unsigned int Utils::unhex(const char *hex,unsigned int maxlen,void *buf,unsigned int len)
{
int n = 1;
unsigned char c,b = 0;
unsigned int l = 0;
+ const char *eof = hex + maxlen;
+
+ if (!maxlen)
+ return 0;
while ((c = (unsigned char)*(hex++))) {
if ((c >= 48)&&(c <= 57)) { // 0..9
@@ -145,36 +155,8 @@ unsigned int Utils::unhex(const char *hex,void *buf,unsigned int len)
((unsigned char *)buf)[l++] = (b | (c - (97 - 10)));
} else b = (c - (97 - 10)) << 4;
}
- }
-
- return l;
-}
-
-unsigned int Utils::unhex(const char *hex,unsigned int hexlen,void *buf,unsigned int len)
-{
- int n = 1;
- unsigned char c,b = 0;
- unsigned int l = 0;
- const char *const end = hex + hexlen;
-
- while (hex != end) {
- c = (unsigned char)*(hex++);
- if ((c >= 48)&&(c <= 57)) { // 0..9
- if ((n ^= 1)) {
- if (l >= len) break;
- ((unsigned char *)buf)[l++] = (b | (c - 48));
- } else b = (c - 48) << 4;
- } else if ((c >= 65)&&(c <= 70)) { // A..F
- if ((n ^= 1)) {
- if (l >= len) break;
- ((unsigned char *)buf)[l++] = (b | (c - (65 - 10)));
- } else b = (c - (65 - 10)) << 4;
- } else if ((c >= 97)&&(c <= 102)) { // a..f
- if ((n ^= 1)) {
- if (l >= len) break;
- ((unsigned char *)buf)[l++] = (b | (c - (97 - 10)));
- } else b = (c - (97 - 10)) << 4;
- }
+ if (hex == eof)
+ break;
}
return l;
diff --git a/node/Utils.hpp b/node/Utils.hpp
index 4298d0455..bafe0b399 100644
--- a/node/Utils.hpp
+++ b/node/Utils.hpp
@@ -145,11 +145,12 @@ public:
* This ignores all non-hex characters, just stepping over them and
* continuing. Upper and lower case are supported for letters a-f.
*
- * @param hex Hexadecimal ASCII code (non-hex chars are ignored)
+ * @param hex Hexadecimal ASCII code (non-hex chars are ignored, stops at zero or maxlen)
+ * @param maxlen Maximum length of hex string buffer
* @return Binary data
*/
- static std::string unhex(const char *hex);
- static inline std::string unhex(const std::string &hex) { return unhex(hex.c_str()); }
+ static std::string unhex(const char *hex,unsigned int maxlen);
+ static inline std::string unhex(const std::string &hex) { return unhex(hex.c_str(),(unsigned int)hex.length()); }
/**
* Convert hexadecimal to binary data
@@ -158,26 +159,13 @@ public:
* continuing. Upper and lower case are supported for letters a-f.
*
* @param hex Hexadecimal ASCII
+ * @param maxlen Maximum length of hex string buffer
* @param buf Buffer to fill
* @param len Length of buffer
* @return Number of characters actually written
*/
- static unsigned int unhex(const char *hex,void *buf,unsigned int len);
- static inline unsigned int unhex(const std::string &hex,void *buf,unsigned int len) { return unhex(hex.c_str(),buf,len); }
-
- /**
- * Convert hexadecimal to binary data
- *
- * This ignores all non-hex characters, just stepping over them and
- * continuing. Upper and lower case are supported for letters a-f.
- *
- * @param hex Hexadecimal ASCII
- * @param hexlen Length of hex ASCII
- * @param buf Buffer to fill
- * @param len Length of buffer
- * @return Number of bytes actually written to buffer
- */
- static unsigned int unhex(const char *hex,unsigned int hexlen,void *buf,unsigned int len);
+ static unsigned int unhex(const char *hex,unsigned int maxlen,void *buf,unsigned int len);
+ static inline unsigned int unhex(const std::string &hex,void *buf,unsigned int len) { return unhex(hex.c_str(),hex.length(),buf,len); }
/**
* Generate secure random bytes
diff --git a/objects.mk b/objects.mk
index 24a54683a..1dd619152 100644
--- a/objects.mk
+++ b/objects.mk
@@ -3,6 +3,7 @@ OBJS=\
node/C25519.o \
node/CertificateOfMembership.o \
node/Defaults.o \
+ node/Dictionary.o \
node/HttpClient.o \
node/Identity.o \
node/InetAddress.o \