summaryrefslogtreecommitdiff
path: root/zto/node/CertificateOfMembership.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'zto/node/CertificateOfMembership.cpp')
-rw-r--r--zto/node/CertificateOfMembership.cpp231
1 files changed, 231 insertions, 0 deletions
diff --git a/zto/node/CertificateOfMembership.cpp b/zto/node/CertificateOfMembership.cpp
new file mode 100644
index 0000000..43efcd2
--- /dev/null
+++ b/zto/node/CertificateOfMembership.cpp
@@ -0,0 +1,231 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
+ *
+ * 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/>.
+ */
+
+#include "CertificateOfMembership.hpp"
+#include "RuntimeEnvironment.hpp"
+#include "Topology.hpp"
+#include "Switch.hpp"
+#include "Network.hpp"
+
+namespace ZeroTier {
+
+void CertificateOfMembership::setQualifier(uint64_t id,uint64_t value,uint64_t maxDelta)
+{
+ _signedBy.zero();
+
+ for(unsigned int i=0;i<_qualifierCount;++i) {
+ if (_qualifiers[i].id == id) {
+ _qualifiers[i].value = value;
+ _qualifiers[i].maxDelta = maxDelta;
+ return;
+ }
+ }
+
+ if (_qualifierCount < ZT_NETWORK_COM_MAX_QUALIFIERS) {
+ _qualifiers[_qualifierCount].id = id;
+ _qualifiers[_qualifierCount].value = value;
+ _qualifiers[_qualifierCount].maxDelta = maxDelta;
+ ++_qualifierCount;
+ std::sort(&(_qualifiers[0]),&(_qualifiers[_qualifierCount]));
+ }
+}
+
+#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
+
+std::string CertificateOfMembership::toString() const
+{
+ std::string s;
+
+ s.append("1:"); // COM_UINT64_ED25519
+
+ uint64_t *const buf = new uint64_t[_qualifierCount * 3];
+ try {
+ unsigned int ptr = 0;
+ for(unsigned int i=0;i<_qualifierCount;++i) {
+ buf[ptr++] = Utils::hton(_qualifiers[i].id);
+ buf[ptr++] = Utils::hton(_qualifiers[i].value);
+ buf[ptr++] = Utils::hton(_qualifiers[i].maxDelta);
+ }
+ s.append(Utils::hex(buf,ptr * sizeof(uint64_t)));
+ delete [] buf;
+ } catch ( ... ) {
+ delete [] buf;
+ throw;
+ }
+
+ s.push_back(':');
+
+ s.append(_signedBy.toString());
+
+ if (_signedBy) {
+ s.push_back(':');
+ s.append(Utils::hex(_signature.data,(unsigned int)_signature.size()));
+ }
+
+ return s;
+}
+
+void CertificateOfMembership::fromString(const char *s)
+{
+ _qualifierCount = 0;
+ _signedBy.zero();
+ memset(_signature.data,0,_signature.size());
+
+ if (!*s)
+ return;
+
+ unsigned int colonAt = 0;
+ while ((s[colonAt])&&(s[colonAt] != ':')) ++colonAt;
+
+ if (!((colonAt == 1)&&(s[0] == '1'))) // COM_UINT64_ED25519?
+ return;
+
+ s += colonAt + 1;
+ colonAt = 0;
+ while ((s[colonAt])&&(s[colonAt] != ':')) ++colonAt;
+
+ if (colonAt) {
+ const unsigned int buflen = colonAt / 2;
+ char *const buf = new char[buflen];
+ unsigned int bufactual = Utils::unhex(s,colonAt,buf,buflen);
+ char *bufptr = buf;
+ try {
+ while (bufactual >= 24) {
+ if (_qualifierCount < ZT_NETWORK_COM_MAX_QUALIFIERS) {
+ _qualifiers[_qualifierCount].id = Utils::ntoh(*((uint64_t *)bufptr)); bufptr += 8;
+ _qualifiers[_qualifierCount].value = Utils::ntoh(*((uint64_t *)bufptr)); bufptr += 8;
+ _qualifiers[_qualifierCount].maxDelta = Utils::ntoh(*((uint64_t *)bufptr)); bufptr += 8;
+ ++_qualifierCount;
+ } else {
+ bufptr += 24;
+ }
+ bufactual -= 24;
+ }
+ } catch ( ... ) {}
+ delete [] buf;
+ }
+
+ if (s[colonAt]) {
+ s += colonAt + 1;
+ colonAt = 0;
+ while ((s[colonAt])&&(s[colonAt] != ':')) ++colonAt;
+
+ if (colonAt) {
+ char addrbuf[ZT_ADDRESS_LENGTH];
+ if (Utils::unhex(s,colonAt,addrbuf,sizeof(addrbuf)) == ZT_ADDRESS_LENGTH)
+ _signedBy.setTo(addrbuf,ZT_ADDRESS_LENGTH);
+
+ if ((_signedBy)&&(s[colonAt])) {
+ s += colonAt + 1;
+ colonAt = 0;
+ while ((s[colonAt])&&(s[colonAt] != ':')) ++colonAt;
+ if (colonAt) {
+ if (Utils::unhex(s,colonAt,_signature.data,(unsigned int)_signature.size()) != _signature.size())
+ _signedBy.zero();
+ } else {
+ _signedBy.zero();
+ }
+ } else {
+ _signedBy.zero();
+ }
+ }
+ }
+
+ std::sort(&(_qualifiers[0]),&(_qualifiers[_qualifierCount]));
+}
+
+#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
+
+bool CertificateOfMembership::agreesWith(const CertificateOfMembership &other) const
+{
+ unsigned int myidx = 0;
+ unsigned int otheridx = 0;
+
+ if ((_qualifierCount == 0)||(other._qualifierCount == 0))
+ return false;
+
+ while (myidx < _qualifierCount) {
+ // Fail if we're at the end of other, since this means the field is
+ // missing.
+ if (otheridx >= other._qualifierCount)
+ return false;
+
+ // Seek to corresponding tuple in other, ignoring tuples that
+ // we may not have. If we run off the end of other, the tuple is
+ // missing. This works because tuples are sorted by ID.
+ while (other._qualifiers[otheridx].id != _qualifiers[myidx].id) {
+ ++otheridx;
+ if (otheridx >= other._qualifierCount)
+ return false;
+ }
+
+ // Compare to determine if the absolute value of the difference
+ // between these two parameters is within our maxDelta.
+ const uint64_t a = _qualifiers[myidx].value;
+ const uint64_t b = other._qualifiers[myidx].value;
+ if (((a >= b) ? (a - b) : (b - a)) > _qualifiers[myidx].maxDelta)
+ return false;
+
+ ++myidx;
+ }
+
+ return true;
+}
+
+bool CertificateOfMembership::sign(const Identity &with)
+{
+ uint64_t buf[ZT_NETWORK_COM_MAX_QUALIFIERS * 3];
+ unsigned int ptr = 0;
+ for(unsigned int i=0;i<_qualifierCount;++i) {
+ buf[ptr++] = Utils::hton(_qualifiers[i].id);
+ buf[ptr++] = Utils::hton(_qualifiers[i].value);
+ buf[ptr++] = Utils::hton(_qualifiers[i].maxDelta);
+ }
+
+ try {
+ _signature = with.sign(buf,ptr * sizeof(uint64_t));
+ _signedBy = with.address();
+ return true;
+ } catch ( ... ) {
+ _signedBy.zero();
+ return false;
+ }
+}
+
+int CertificateOfMembership::verify(const RuntimeEnvironment *RR) const
+{
+ if ((!_signedBy)||(_signedBy != Network::controllerFor(networkId()))||(_qualifierCount > ZT_NETWORK_COM_MAX_QUALIFIERS))
+ return -1;
+
+ const Identity id(RR->topology->getIdentity(_signedBy));
+ if (!id) {
+ RR->sw->requestWhois(_signedBy);
+ return 1;
+ }
+
+ uint64_t buf[ZT_NETWORK_COM_MAX_QUALIFIERS * 3];
+ unsigned int ptr = 0;
+ for(unsigned int i=0;i<_qualifierCount;++i) {
+ buf[ptr++] = Utils::hton(_qualifiers[i].id);
+ buf[ptr++] = Utils::hton(_qualifiers[i].value);
+ buf[ptr++] = Utils::hton(_qualifiers[i].maxDelta);
+ }
+ return (id.verify(buf,ptr * sizeof(uint64_t),_signature) ? 0 : -1);
+}
+
+} // namespace ZeroTier