2
0

CertificateOfMembership.cpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. /*
  2. * Copyright (c)2013-2020 ZeroTier, Inc.
  3. *
  4. * Use of this software is governed by the Business Source License included
  5. * in the LICENSE.TXT file in the project's root directory.
  6. *
  7. * Change Date: 2024-01-01
  8. *
  9. * On the date above, in accordance with the Business Source License, use
  10. * of this software will be governed by version 2.0 of the Apache License.
  11. */
  12. /****/
  13. #include "CertificateOfMembership.hpp"
  14. namespace ZeroTier {
  15. CertificateOfMembership::CertificateOfMembership(const int64_t timestamp,const int64_t timestampMaxDelta,const uint64_t nwid,const Identity &issuedTo) noexcept :
  16. _timestamp(timestamp),
  17. _timestampMaxDelta(timestampMaxDelta),
  18. _networkId(nwid),
  19. _issuedTo(issuedTo.fingerprint()),
  20. _signatureLength(0) {}
  21. bool CertificateOfMembership::agreesWith(const CertificateOfMembership &other) const noexcept
  22. {
  23. // NOTE: we always do explicit absolute value with an if() since llabs() can have overflow
  24. // conditions that could introduce a vulnerability.
  25. if (other._timestamp > _timestamp) {
  26. if ((other._timestamp - _timestamp) > std::min(_timestampMaxDelta,other._timestampMaxDelta))
  27. return false;
  28. } else {
  29. if ((_timestamp - other._timestamp) > std::min(_timestampMaxDelta,other._timestampMaxDelta))
  30. return false;
  31. }
  32. // us <> them
  33. for(FCV<_Qualifier,ZT_CERTIFICATEOFMEMBERSHIP_MAX_ADDITIONAL_QUALIFIERS>::const_iterator i(_additionalQualifiers.begin());i != _additionalQualifiers.end();++i) {
  34. if (i->delta != 0xffffffffffffffffULL) {
  35. const uint64_t *v2 = nullptr;
  36. for(FCV<_Qualifier,ZT_CERTIFICATEOFMEMBERSHIP_MAX_ADDITIONAL_QUALIFIERS>::const_iterator j(other._additionalQualifiers.begin());j != other._additionalQualifiers.end();++i) {
  37. if (j->id == i->id) {
  38. v2 = &(j->value);
  39. break;
  40. }
  41. }
  42. if (!v2)
  43. return false;
  44. if (*v2 > i->value) {
  45. if ((*v2 - i->value) > i->delta)
  46. return false;
  47. } else {
  48. if ((i->value - *v2) > i->delta)
  49. return false;
  50. }
  51. }
  52. }
  53. // them <> us (we need a second pass in case they have qualifiers we don't or vice versa)
  54. for(FCV<_Qualifier,ZT_CERTIFICATEOFMEMBERSHIP_MAX_ADDITIONAL_QUALIFIERS>::const_iterator i(other._additionalQualifiers.begin());i != other._additionalQualifiers.end();++i) {
  55. if (i->delta != 0xffffffffffffffffULL) {
  56. const uint64_t *v2 = nullptr;
  57. for(FCV<_Qualifier,ZT_CERTIFICATEOFMEMBERSHIP_MAX_ADDITIONAL_QUALIFIERS>::const_iterator j(_additionalQualifiers.begin());j != _additionalQualifiers.end();++i) {
  58. if (j->id == i->id) {
  59. v2 = &(j->value);
  60. break;
  61. }
  62. }
  63. if (!v2)
  64. return false;
  65. if (*v2 > i->value) {
  66. if ((*v2 - i->value) > i->delta)
  67. return false;
  68. } else {
  69. if ((i->value - *v2) > i->delta)
  70. return false;
  71. }
  72. }
  73. }
  74. // SECURITY: check for issued-to inequality is a sanity check. This should be impossible since elsewhere
  75. // in the code COMs are checked to ensure that they do in fact belong to their issued-to identities.
  76. return (other._networkId == _networkId) && (_networkId != 0) && (other._issuedTo.address() != _issuedTo.address());
  77. }
  78. bool CertificateOfMembership::sign(const Identity &with) noexcept
  79. {
  80. _signedBy = with.address();
  81. uint64_t buf[ZT_CERTIFICATEOFMEMBERSHIP_MARSHAL_SIZE_MAX / 8];
  82. const unsigned int bufSize = _fillSigningBuf(buf);
  83. _signatureLength = with.sign(buf,bufSize,_signature,sizeof(_signature));
  84. return _signatureLength > 0;
  85. }
  86. int CertificateOfMembership::marshal(uint8_t data[ZT_CERTIFICATEOFMEMBERSHIP_MARSHAL_SIZE_MAX],const bool v2) const noexcept
  87. {
  88. data[0] = v2 ? 2 : 1;
  89. // All formats start with the standard three qualifiers: timestamp with delta, network ID as a strict
  90. // equality compare, and the address of the issued-to node as an informational tuple.
  91. int p = 3;
  92. Utils::storeBigEndian<uint64_t>(data + p,0); p += 8;
  93. Utils::storeBigEndian<uint64_t>(data + p,(uint64_t)_timestamp); p += 8;
  94. Utils::storeBigEndian<uint64_t>(data + p,(uint64_t)_timestampMaxDelta); p += 8;
  95. Utils::storeBigEndian<uint64_t>(data + p,1); p += 8;
  96. Utils::storeBigEndian<uint64_t>(data + p,_networkId); p += 8;
  97. Utils::storeBigEndian<uint64_t>(data + p,0); p += 8;
  98. Utils::storeBigEndian<uint64_t>(data + p,2); p += 8;
  99. Utils::storeBigEndian<uint64_t>(data + p,_issuedTo.address().toInt()); p += 8;
  100. Utils::storeAsIsEndian<uint64_t>(data + p,0xffffffffffffffffULL); p += 8;
  101. if (v2) {
  102. // V2 marshal format will have three tuples followed by the fingerprint hash.
  103. Utils::storeBigEndian<uint16_t>(data + 1,3);
  104. Utils::copy<48>(data + p,_issuedTo.hash());
  105. p += 48;
  106. } else {
  107. // V1 marshal format must shove everything into tuples, resulting in nine.
  108. Utils::storeBigEndian<uint16_t>(data + 1,9);
  109. for(int k=0;k<6;++k) {
  110. Utils::storeBigEndian<uint64_t>(data + p,(uint64_t)k + 3); p += 8;
  111. Utils::storeAsIsEndian<uint64_t>(data + p,Utils::loadAsIsEndian<uint64_t>(_issuedTo.hash() + (k * 8))); p += 8;
  112. Utils::storeAsIsEndian<uint64_t>(data + p,0xffffffffffffffffULL); p += 8;
  113. }
  114. }
  115. _signedBy.copyTo(data + p); p += 5;
  116. if (v2) {
  117. // V2 marshal format prefixes signatures with a 16-bit length to support future signature types.
  118. Utils::storeBigEndian<uint16_t>(data + p,(uint16_t)_signatureLength); p += 2;
  119. Utils::copy(data + p,_signature,_signatureLength);
  120. p += (int)_signatureLength;
  121. } else {
  122. // V1 only supports 96-byte signature fields.
  123. Utils::copy<96>(data + p,_signature);
  124. p += 96;
  125. }
  126. return p;
  127. }
  128. int CertificateOfMembership::unmarshal(const uint8_t *data,int len) noexcept
  129. {
  130. if (len < (1 + 2 + 72))
  131. return -1;
  132. TriviallyCopyable::memoryZero(this);
  133. const unsigned int numq = Utils::loadBigEndian<uint16_t>(data + 1);
  134. if ((numq < 3)||(numq > (ZT_CERTIFICATEOFMEMBERSHIP_MAX_ADDITIONAL_QUALIFIERS + 3)))
  135. return -1;
  136. int p = 3;
  137. for(unsigned int q=0;q<numq;++q) {
  138. if ((p + 24) > len)
  139. return -1;
  140. const uint64_t id = Utils::loadBigEndian<uint64_t>(data + p); p += 8;
  141. const uint64_t value = Utils::loadBigEndian<uint64_t>(data + p); p += 8;
  142. const uint64_t delta = Utils::loadBigEndian<uint64_t>(data + p); p += 8;
  143. switch(id) {
  144. case 0:
  145. _timestamp = (int64_t)value;
  146. _timestampMaxDelta = (int64_t)delta;
  147. break;
  148. case 1:
  149. _networkId = value;
  150. break;
  151. case 2:
  152. _issuedTo.apiFingerprint()->address = value;
  153. break;
  154. // V1 nodes will pack the hash into qualifier tuples.
  155. case 3:
  156. Utils::storeBigEndian<uint64_t>(_issuedTo.apiFingerprint()->hash,value);
  157. break;
  158. case 4:
  159. Utils::storeBigEndian<uint64_t>(_issuedTo.apiFingerprint()->hash + 8,value);
  160. break;
  161. case 5:
  162. Utils::storeBigEndian<uint64_t>(_issuedTo.apiFingerprint()->hash + 16,value);
  163. break;
  164. case 6:
  165. Utils::storeBigEndian<uint64_t>(_issuedTo.apiFingerprint()->hash + 24,value);
  166. break;
  167. case 7:
  168. Utils::storeBigEndian<uint64_t>(_issuedTo.apiFingerprint()->hash + 32,value);
  169. break;
  170. case 8:
  171. Utils::storeBigEndian<uint64_t>(_issuedTo.apiFingerprint()->hash + 40,value);
  172. break;
  173. default:
  174. if (_additionalQualifiers.size() == _additionalQualifiers.capacity())
  175. return -1;
  176. _additionalQualifiers.push_back(_Qualifier(id,value,delta));
  177. break;
  178. }
  179. }
  180. std::sort(_additionalQualifiers.begin(),_additionalQualifiers.end());
  181. if (data[0] == 1) {
  182. if ((p + 96) > len)
  183. return -1;
  184. _signatureLength = 96;
  185. Utils::copy<96>(_signature,data + p);
  186. return p + 96;
  187. } else if (data[0] == 2) {
  188. if ((p + 48) > len)
  189. return -1;
  190. Utils::copy<48>(_issuedTo.apiFingerprint()->hash,data + p);
  191. p += 48;
  192. if ((p + 2) > len)
  193. return -1;
  194. _signatureLength = Utils::loadBigEndian<uint16_t>(data + p);
  195. if ((_signatureLength > (unsigned int)sizeof(_signature))||((p + (int)_signatureLength) > len))
  196. return -1;
  197. Utils::copy(_signature,data + p,_signatureLength);
  198. return p + (int)_signatureLength;
  199. }
  200. return -1;
  201. }
  202. unsigned int CertificateOfMembership::_fillSigningBuf(uint64_t buf[ZT_CERTIFICATEOFMEMBERSHIP_MARSHAL_SIZE_MAX / 8]) const noexcept
  203. {
  204. const uint64_t informational = 0xffffffffffffffffULL;
  205. /*
  206. * Signing always embeds all data to be signed in qualifier tuple format for
  207. * backward compatibility with V1 nodes, since otherwise we'd need a signature
  208. * for v1 nodes to verify and another for v2 nodes to verify.
  209. */
  210. // The standard three tuples that must begin every COM.
  211. buf[0] = 0;
  212. buf[1] = Utils::hton((uint64_t)_timestamp);
  213. buf[2] = Utils::hton((uint64_t)_timestampMaxDelta);
  214. buf[3] = ZT_CONST_TO_BE_UINT64(1);
  215. buf[4] = Utils::hton(_networkId);
  216. buf[5] = 0;
  217. buf[6] = ZT_CONST_TO_BE_UINT64(2);
  218. buf[7] = Utils::hton(_issuedTo.address().toInt());
  219. buf[8] = informational;
  220. unsigned int p = 9;
  221. // The full identity fingerprint of the peer to whom the COM was issued,
  222. // embeded as a series of informational tuples.
  223. if (_issuedTo.haveHash()) {
  224. buf[p++] = ZT_CONST_TO_BE_UINT64(3);
  225. buf[p++] = Utils::loadAsIsEndian<uint64_t>(_issuedTo.hash());
  226. buf[p++] = informational;
  227. buf[p++] = ZT_CONST_TO_BE_UINT64(4);
  228. buf[p++] = Utils::loadAsIsEndian<uint64_t>(_issuedTo.hash() + 8);
  229. buf[p++] = informational;
  230. buf[p++] = ZT_CONST_TO_BE_UINT64(5);
  231. buf[p++] = Utils::loadAsIsEndian<uint64_t>(_issuedTo.hash() + 16);
  232. buf[p++] = informational;
  233. buf[p++] = ZT_CONST_TO_BE_UINT64(6);
  234. buf[p++] = Utils::loadAsIsEndian<uint64_t>(_issuedTo.hash() + 24);
  235. buf[p++] = informational;
  236. buf[p++] = ZT_CONST_TO_BE_UINT64(7);
  237. buf[p++] = Utils::loadAsIsEndian<uint64_t>(_issuedTo.hash() + 32);
  238. buf[p++] = informational;
  239. buf[p++] = ZT_CONST_TO_BE_UINT64(8);
  240. buf[p++] = Utils::loadAsIsEndian<uint64_t>(_issuedTo.hash() + 40);
  241. buf[p++] = informational;
  242. }
  243. for(FCV<_Qualifier,ZT_CERTIFICATEOFMEMBERSHIP_MAX_ADDITIONAL_QUALIFIERS>::const_iterator i(_additionalQualifiers.begin());i != _additionalQualifiers.end();++i) {
  244. buf[p++] = Utils::hton(i->id);
  245. buf[p++] = Utils::hton(i->value);
  246. buf[p++] = Utils::hton(i->delta);
  247. }
  248. return p * 8;
  249. }
  250. } // namespace ZeroTier