소스 검색

Add proof of work request for future DDOS mitigation use.

Adam Ierymenko 9 년 전
부모
커밋
e5f168f599
5개의 변경된 파일240개의 추가작업 그리고 21개의 파일을 삭제
  1. 121 0
      node/IncomingPacket.cpp
  2. 22 0
      node/IncomingPacket.hpp
  3. 1 1
      node/Packet.cpp
  4. 82 20
      node/Packet.hpp
  5. 14 0
      selftest.cpp

+ 121 - 0
node/IncomingPacket.cpp

@@ -41,6 +41,8 @@
 #include "Peer.hpp"
 #include "Peer.hpp"
 #include "NetworkController.hpp"
 #include "NetworkController.hpp"
 #include "SelfAwareness.hpp"
 #include "SelfAwareness.hpp"
+#include "Salsa20.hpp"
+#include "SHA512.hpp"
 
 
 namespace ZeroTier {
 namespace ZeroTier {
 
 
@@ -90,6 +92,7 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR)
 				case Packet::VERB_PUSH_DIRECT_PATHS:              return _doPUSH_DIRECT_PATHS(RR,peer);
 				case Packet::VERB_PUSH_DIRECT_PATHS:              return _doPUSH_DIRECT_PATHS(RR,peer);
 				case Packet::VERB_CIRCUIT_TEST:                   return _doCIRCUIT_TEST(RR,peer);
 				case Packet::VERB_CIRCUIT_TEST:                   return _doCIRCUIT_TEST(RR,peer);
 				case Packet::VERB_CIRCUIT_TEST_REPORT:            return _doCIRCUIT_TEST_REPORT(RR,peer);
 				case Packet::VERB_CIRCUIT_TEST_REPORT:            return _doCIRCUIT_TEST_REPORT(RR,peer);
+				case Packet::VERB_REQUEST_PROOF_OF_WORK:          return _doREQUEST_PROOF_OF_WORK(RR,peer);
 			}
 			}
 		} else {
 		} else {
 			RR->sw->requestWhois(sourceAddress);
 			RR->sw->requestWhois(sourceAddress);
@@ -1042,6 +1045,124 @@ bool IncomingPacket::_doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,const S
 	return true;
 	return true;
 }
 }
 
 
+bool IncomingPacket::_doREQUEST_PROOF_OF_WORK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+{
+	try {
+		// Right now this is only allowed from root servers -- may be allowed from controllers and relays later.
+		if (RR->topology->isRoot(peer->identity())) {
+			const unsigned int difficulty = (*this)[ZT_PACKET_IDX_PAYLOAD + 1];
+			const unsigned int challengeLength = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 2);
+			if (challengeLength > ZT_PROTO_MAX_PACKET_LENGTH)
+				return true; // sanity check, drop invalid size
+			const unsigned char *challenge = field(ZT_PACKET_IDX_PAYLOAD + 4,challengeLength);
+
+			switch((*this)[ZT_PACKET_IDX_PAYLOAD]) {
+
+				// Salsa20/12+SHA512 hashcash
+				case 0x01: {
+					unsigned char result[16];
+					computeSalsa2012Sha512ProofOfWork(difficulty,challenge,challengeLength,result);
+
+					Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
+					outp.append((unsigned char)Packet::VERB_REQUEST_PROOF_OF_WORK);
+					outp.append(packetId());
+					outp.append((uint16_t)sizeof(result));
+					outp.append(result,sizeof(result));
+					outp.armor(peer->key(),true);
+					RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
+				}	break;
+
+				default:
+					TRACE("dropped REQUEST_PROO_OF_WORK from %s(%s): unrecognized proof of work type",peer->address().toString().c_str(),_remoteAddress.toString().c_str());
+					break;
+			}
+		} else {
+			TRACE("dropped REQUEST_PROO_OF_WORK from %s(%s): not trusted enough",peer->address().toString().c_str(),_remoteAddress.toString().c_str());
+		}
+	} catch ( ... ) {
+		TRACE("dropped REQUEST_PROOF_OF_WORK from %s(%s): unexpected exception",peer->address().toString().c_str(),_remoteAddress.toString().c_str());
+	}
+	return true;
+}
+
+void IncomingPacket::computeSalsa2012Sha512ProofOfWork(unsigned int difficulty,const void *challenge,unsigned int challengeLength,unsigned char result[16])
+{
+	unsigned char salsabuf[131072]; // 131072 == protocol constant, size of memory buffer for this proof of work function
+	char candidatebuf[ZT_PROTO_MAX_PACKET_LENGTH + 256];
+	unsigned char shabuf[ZT_SHA512_DIGEST_LEN];
+	const uint64_t s20iv = 0; // zero IV for Salsa20
+	char *const candidate = (char *)(( ((uintptr_t)&(candidatebuf[0])) | 0xf ) + 1); // align to 16-byte boundary to ensure that uint64_t type punning of initial nonce is okay
+	Salsa20 s20;
+	unsigned int d;
+	unsigned char *p;
+
+	Utils::getSecureRandom(candidate,16);
+	memcpy(candidate + 16,challenge,challengeLength);
+
+	if (difficulty > 512)
+		difficulty = 512; // sanity check
+
+try_salsa2012sha512_again:
+	++*(reinterpret_cast<volatile uint64_t *>(candidate));
+
+	SHA512::hash(shabuf,candidate,16 + challengeLength);
+	s20.init(shabuf,256,&s20iv,12);
+	memset(salsabuf,0,sizeof(salsabuf));
+	s20.encrypt(salsabuf,salsabuf,sizeof(salsabuf));
+	SHA512::hash(shabuf,salsabuf,sizeof(salsabuf));
+
+	d = difficulty;
+	p = shabuf;
+	while (d >= 8) {
+		if (*(p++))
+			goto try_salsa2012sha512_again;
+		d -= 8;
+	}
+	if (d > 0) {
+		if ( ((((unsigned int)*p) << d) & 0xff00) != 0 )
+			goto try_salsa2012sha512_again;
+	}
+
+	memcpy(result,candidate,16);
+}
+
+bool IncomingPacket::testSalsa2012Sha512ProofOfWorkResult(unsigned int difficulty,const void *challenge,unsigned int challengeLength,const unsigned char proposedResult[16])
+{
+	unsigned char salsabuf[131072]; // 131072 == protocol constant, size of memory buffer for this proof of work function
+	char candidate[ZT_PROTO_MAX_PACKET_LENGTH + 256];
+	unsigned char shabuf[ZT_SHA512_DIGEST_LEN];
+	const uint64_t s20iv = 0; // zero IV for Salsa20
+	Salsa20 s20;
+	unsigned int d;
+	unsigned char *p;
+
+	if (difficulty > 512)
+		difficulty = 512; // sanity check
+
+	memcpy(candidate,proposedResult,16);
+	memcpy(candidate + 16,challenge,challengeLength);
+
+	SHA512::hash(shabuf,candidate,16 + challengeLength);
+	s20.init(shabuf,256,&s20iv,12);
+	memset(salsabuf,0,sizeof(salsabuf));
+	s20.encrypt(salsabuf,salsabuf,sizeof(salsabuf));
+	SHA512::hash(shabuf,salsabuf,sizeof(salsabuf));
+
+	d = difficulty;
+	p = shabuf;
+	while (d >= 8) {
+		if (*(p++))
+			return false;
+		d -= 8;
+	}
+	if (d > 0) {
+		if ( ((((unsigned int)*p) << d) & 0xff00) != 0 )
+			return false;
+	}
+
+	return true;
+}
+
 void IncomingPacket::_sendErrorNeedCertificate(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer,uint64_t nwid)
 void IncomingPacket::_sendErrorNeedCertificate(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer,uint64_t nwid)
 {
 {
 	Packet outp(source(),RR->identity.address(),Packet::VERB_ERROR);
 	Packet outp(source(),RR->identity.address(),Packet::VERB_ERROR);

+ 22 - 0
node/IncomingPacket.hpp

@@ -107,6 +107,27 @@ public:
 	 */
 	 */
 	inline uint64_t receiveTime() const throw() { return _receiveTime; }
 	inline uint64_t receiveTime() const throw() { return _receiveTime; }
 
 
+	/**
+	 * Compute the Salsa20/12+SHA512 proof of work function
+	 *
+	 * @param difficulty Difficulty in bits (max: 64)
+	 * @param challenge Challenge string
+	 * @param challengeLength Length of challenge in bytes (max allowed: ZT_PROTO_MAX_PACKET_LENGTH)
+	 * @param result Buffer to fill with 16-byte result
+	 */
+	static void computeSalsa2012Sha512ProofOfWork(unsigned int difficulty,const void *challenge,unsigned int challengeLength,unsigned char result[16]);
+
+	/**
+	 * Verify the result of Salsa20/12+SHA512 proof of work
+	 *
+	 * @param difficulty Difficulty in bits (max: 64)
+	 * @param challenge Challenge bytes
+	 * @param challengeLength Length of challenge in bytes (max allowed: ZT_PROTO_MAX_PACKET_LENGTH)
+	 * @param proposedResult Result supplied by client
+	 * @return True if result is valid
+	 */
+	static bool testSalsa2012Sha512ProofOfWorkResult(unsigned int difficulty,const void *challenge,unsigned int challengeLength,const unsigned char proposedResult[16]);
+
 private:
 private:
 	// These are called internally to handle packet contents once it has
 	// These are called internally to handle packet contents once it has
 	// been authenticated, decrypted, decompressed, and classified.
 	// been authenticated, decrypted, decompressed, and classified.
@@ -126,6 +147,7 @@ private:
 	bool _doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
 	bool _doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
 	bool _doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
 	bool _doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
 	bool _doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
 	bool _doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
+	bool _doREQUEST_PROOF_OF_WORK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
 
 
 	// Send an ERROR_NEED_MEMBERSHIP_CERTIFICATE to a peer indicating that an updated cert is needed to communicate
 	// Send an ERROR_NEED_MEMBERSHIP_CERTIFICATE to a peer indicating that an updated cert is needed to communicate
 	void _sendErrorNeedCertificate(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer,uint64_t nwid);
 	void _sendErrorNeedCertificate(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer,uint64_t nwid);

+ 1 - 1
node/Packet.cpp

@@ -45,7 +45,6 @@ const char *Packet::verbString(Verb v)
 		case VERB_RENDEZVOUS: return "RENDEZVOUS";
 		case VERB_RENDEZVOUS: return "RENDEZVOUS";
 		case VERB_FRAME: return "FRAME";
 		case VERB_FRAME: return "FRAME";
 		case VERB_EXT_FRAME: return "EXT_FRAME";
 		case VERB_EXT_FRAME: return "EXT_FRAME";
-		case VERB_P5_MULTICAST_FRAME: return "P5_MULTICAST_FRAME";
 		case VERB_MULTICAST_LIKE: return "MULTICAST_LIKE";
 		case VERB_MULTICAST_LIKE: return "MULTICAST_LIKE";
 		case VERB_NETWORK_MEMBERSHIP_CERTIFICATE: return "NETWORK_MEMBERSHIP_CERTIFICATE";
 		case VERB_NETWORK_MEMBERSHIP_CERTIFICATE: return "NETWORK_MEMBERSHIP_CERTIFICATE";
 		case VERB_NETWORK_CONFIG_REQUEST: return "NETWORK_CONFIG_REQUEST";
 		case VERB_NETWORK_CONFIG_REQUEST: return "NETWORK_CONFIG_REQUEST";
@@ -56,6 +55,7 @@ const char *Packet::verbString(Verb v)
 		case VERB_PUSH_DIRECT_PATHS: return "PUSH_DIRECT_PATHS";
 		case VERB_PUSH_DIRECT_PATHS: return "PUSH_DIRECT_PATHS";
 		case VERB_CIRCUIT_TEST: return "CIRCUIT_TEST";
 		case VERB_CIRCUIT_TEST: return "CIRCUIT_TEST";
 		case VERB_CIRCUIT_TEST_REPORT: return "CIRCUIT_TEST_REPORT";
 		case VERB_CIRCUIT_TEST_REPORT: return "CIRCUIT_TEST_REPORT";
+		case VERB_REQUEST_PROOF_OF_WORK: return "REQUEST_PROOF_OF_WORK";
 	}
 	}
 	return "(unknown)";
 	return "(unknown)";
 }
 }

+ 82 - 20
node/Packet.hpp

@@ -523,10 +523,13 @@ public:
 	 */
 	 */
 	enum Verb /* Max value: 32 (5 bits) */
 	enum Verb /* Max value: 32 (5 bits) */
 	{
 	{
-		/* No operation, payload ignored, no reply */
+		/**
+		 * No operation (ignored, no reply)
+		 */
 		VERB_NOP = 0,
 		VERB_NOP = 0,
 
 
-		/* Announcement of a node's existence:
+		/**
+		 * Announcement of a node's existence:
 		 *   <[1] protocol version>
 		 *   <[1] protocol version>
 		 *   <[1] software major version>
 		 *   <[1] software major version>
 		 *   <[1] software minor version>
 		 *   <[1] software minor version>
@@ -564,7 +567,8 @@ public:
 		 */
 		 */
 		VERB_HELLO = 1,
 		VERB_HELLO = 1,
 
 
-		/* Error response:
+		/**
+		 * Error response:
 		 *   <[1] in-re verb>
 		 *   <[1] in-re verb>
 		 *   <[8] in-re packet ID>
 		 *   <[8] in-re packet ID>
 		 *   <[1] error code>
 		 *   <[1] error code>
@@ -572,14 +576,16 @@ public:
 		 */
 		 */
 		VERB_ERROR = 2,
 		VERB_ERROR = 2,
 
 
-		/* Success response:
+		/**
+		 * Success response:
 		 *   <[1] in-re verb>
 		 *   <[1] in-re verb>
 		 *   <[8] in-re packet ID>
 		 *   <[8] in-re packet ID>
 		 *   <[...] request-specific payload>
 		 *   <[...] request-specific payload>
 		 */
 		 */
 		VERB_OK = 3,
 		VERB_OK = 3,
 
 
-		/* Query an identity by address:
+		/**
+		 * Query an identity by address:
 		 *   <[5] address to look up>
 		 *   <[5] address to look up>
 		 *
 		 *
 		 * OK response payload:
 		 * OK response payload:
@@ -590,7 +596,8 @@ public:
 		 */
 		 */
 		VERB_WHOIS = 4,
 		VERB_WHOIS = 4,
 
 
-		/* Meet another node at a given protocol address:
+		/**
+		 * Meet another node at a given protocol address:
 		 *   <[1] flags (unused, currently 0)>
 		 *   <[1] flags (unused, currently 0)>
 		 *   <[5] ZeroTier address of peer that might be found at this address>
 		 *   <[5] ZeroTier address of peer that might be found at this address>
 		 *   <[2] 16-bit protocol address port>
 		 *   <[2] 16-bit protocol address port>
@@ -613,7 +620,8 @@ public:
 		 */
 		 */
 		VERB_RENDEZVOUS = 5,
 		VERB_RENDEZVOUS = 5,
 
 
-		/* ZT-to-ZT unicast ethernet frame (shortened EXT_FRAME):
+		/**
+		 * ZT-to-ZT unicast ethernet frame (shortened EXT_FRAME):
 		 *   <[8] 64-bit network ID>
 		 *   <[8] 64-bit network ID>
 		 *   <[2] 16-bit ethertype>
 		 *   <[2] 16-bit ethertype>
 		 *   <[...] ethernet payload>
 		 *   <[...] ethernet payload>
@@ -628,7 +636,8 @@ public:
 		 */
 		 */
 		VERB_FRAME = 6,
 		VERB_FRAME = 6,
 
 
-		/* Full Ethernet frame with MAC addressing and optional fields:
+		/**
+		 * Full Ethernet frame with MAC addressing and optional fields:
 		 *   <[8] 64-bit network ID>
 		 *   <[8] 64-bit network ID>
 		 *   <[1] flags>
 		 *   <[1] flags>
 		 *  [<[...] certificate of network membership>]
 		 *  [<[...] certificate of network membership>]
@@ -652,9 +661,10 @@ public:
 		VERB_EXT_FRAME = 7,
 		VERB_EXT_FRAME = 7,
 
 
 		/* DEPRECATED */
 		/* DEPRECATED */
-		VERB_P5_MULTICAST_FRAME = 8,
+		//VERB_P5_MULTICAST_FRAME = 8,
 
 
-		/* Announce interest in multicast group(s):
+		/**
+		 * Announce interest in multicast group(s):
 		 *   <[8] 64-bit network ID>
 		 *   <[8] 64-bit network ID>
 		 *   <[6] multicast Ethernet address>
 		 *   <[6] multicast Ethernet address>
 		 *   <[4] multicast additional distinguishing information (ADI)>
 		 *   <[4] multicast additional distinguishing information (ADI)>
@@ -667,7 +677,8 @@ public:
 		 */
 		 */
 		VERB_MULTICAST_LIKE = 9,
 		VERB_MULTICAST_LIKE = 9,
 
 
-		/* Network member certificate replication/push:
+		/**
+		 * Network member certificate replication/push:
 		 *   <[...] serialized certificate of membership>
 		 *   <[...] serialized certificate of membership>
 		 *   [ ... additional certificates may follow ...]
 		 *   [ ... additional certificates may follow ...]
 		 *
 		 *
@@ -678,7 +689,8 @@ public:
 		 */
 		 */
 		VERB_NETWORK_MEMBERSHIP_CERTIFICATE = 10,
 		VERB_NETWORK_MEMBERSHIP_CERTIFICATE = 10,
 
 
-		/* Network configuration request:
+		/**
+		 * Network configuration request:
 		 *   <[8] 64-bit network ID>
 		 *   <[8] 64-bit network ID>
 		 *   <[2] 16-bit length of request meta-data dictionary>
 		 *   <[2] 16-bit length of request meta-data dictionary>
 		 *   <[...] string-serialized request meta-data>
 		 *   <[...] string-serialized request meta-data>
@@ -713,7 +725,8 @@ public:
 		 */
 		 */
 		VERB_NETWORK_CONFIG_REQUEST = 11,
 		VERB_NETWORK_CONFIG_REQUEST = 11,
 
 
-		/* Network configuration refresh request:
+		/**
+		 * Network configuration refresh request:
 		 *   <[...] array of 64-bit network IDs>
 		 *   <[...] array of 64-bit network IDs>
 		 *
 		 *
 		 * This can be sent by the network controller to inform a node that it
 		 * This can be sent by the network controller to inform a node that it
@@ -724,7 +737,8 @@ public:
 		 */
 		 */
 		VERB_NETWORK_CONFIG_REFRESH = 12,
 		VERB_NETWORK_CONFIG_REFRESH = 12,
 
 
-		/* Request endpoints for multicast distribution:
+		/**
+		 * Request endpoints for multicast distribution:
 		 *   <[8] 64-bit network ID>
 		 *   <[8] 64-bit network ID>
 		 *   <[1] flags>
 		 *   <[1] flags>
 		 *   <[6] MAC address of multicast group being queried>
 		 *   <[6] MAC address of multicast group being queried>
@@ -762,7 +776,8 @@ public:
 		 */
 		 */
 		VERB_MULTICAST_GATHER = 13,
 		VERB_MULTICAST_GATHER = 13,
 
 
-		/* Multicast frame:
+		/**
+		 * Multicast frame:
 		 *   <[8] 64-bit network ID>
 		 *   <[8] 64-bit network ID>
 		 *   <[1] flags>
 		 *   <[1] flags>
 		 *  [<[...] network certificate of membership>]
 		 *  [<[...] network certificate of membership>]
@@ -803,7 +818,8 @@ public:
 		 */
 		 */
 		VERB_MULTICAST_FRAME = 14,
 		VERB_MULTICAST_FRAME = 14,
 
 
-		/* Ephemeral (PFS) key push: (UNFINISHED, NOT IMPLEMENTED YET)
+		/**
+		 * Ephemeral (PFS) key push: (UNFINISHED, NOT IMPLEMENTED YET)
 		 *   <[2] flags (unused and reserved, must be 0)>
 		 *   <[2] flags (unused and reserved, must be 0)>
 		 *   <[2] length of padding / extra field section>
 		 *   <[2] length of padding / extra field section>
 		 *   <[...] padding / extra field section>
 		 *   <[...] padding / extra field section>
@@ -859,7 +875,8 @@ public:
 		 */
 		 */
 		VERB_SET_EPHEMERAL_KEY = 15,
 		VERB_SET_EPHEMERAL_KEY = 15,
 
 
-		/* Push of potential endpoints for direct communication:
+		/**
+		 * Push of potential endpoints for direct communication:
 		 *   <[2] 16-bit number of paths>
 		 *   <[2] 16-bit number of paths>
 		 *   <[...] paths>
 		 *   <[...] paths>
 		 *
 		 *
@@ -899,7 +916,8 @@ public:
 		 */
 		 */
 		VERB_PUSH_DIRECT_PATHS = 16,
 		VERB_PUSH_DIRECT_PATHS = 16,
 
 
-		/* Source-routed circuit test message:
+		/**
+		 * Source-routed circuit test message:
 		 *   <[5] address of originator of circuit test>
 		 *   <[5] address of originator of circuit test>
 		 *   <[2] 16-bit flags>
 		 *   <[2] 16-bit flags>
 		 *   <[8] 64-bit timestamp>
 		 *   <[8] 64-bit timestamp>
@@ -977,7 +995,8 @@ public:
 		 */
 		 */
 		VERB_CIRCUIT_TEST = 17,
 		VERB_CIRCUIT_TEST = 17,
 
 
-		/* Circuit test hop report:
+		/**
+		 * Circuit test hop report:
 		 *   <[8] 64-bit timestamp (from original test)>
 		 *   <[8] 64-bit timestamp (from original test)>
 		 *   <[8] 64-bit test ID (from original test)>
 		 *   <[8] 64-bit test ID (from original test)>
 		 *   <[8] 64-bit reporter timestamp (reporter's clock, 0 if unspec)>
 		 *   <[8] 64-bit reporter timestamp (reporter's clock, 0 if unspec)>
@@ -1010,7 +1029,50 @@ public:
 		 * If a test report is received and no circuit test was sent, it should be
 		 * If a test report is received and no circuit test was sent, it should be
 		 * ignored. This message generates no OK or ERROR response.
 		 * ignored. This message generates no OK or ERROR response.
 		 */
 		 */
-		VERB_CIRCUIT_TEST_REPORT = 18
+		VERB_CIRCUIT_TEST_REPORT = 18,
+
+		/**
+		 * Request proof of work:
+		 *   <[1] 8-bit proof of work type>
+		 *   <[1] 8-bit proof of work difficulty>
+		 *   <[2] 16-bit length of proof of work challenge>
+		 *   <[...] proof of work challenge>
+		 *
+		 * This requests that a peer perform a proof of work calucation. It can be
+		 * sent by highly trusted peers (e.g. root servers, network controllers)
+		 * under suspected denial of service conditions in an attempt to filter
+		 * out "non-serious" peers and remain responsive to those proving their
+		 * intent to actually communicate.
+		 *
+		 * If the peer obliges to perform the work, it does so and responds with
+		 * an OK containing the result. Otherwise it may ignore the message or
+		 * response with an ERROR_INVALID_REQUEST or ERROR_UNSUPPORTED_OPERATION.
+		 *
+		 * Proof of work type IDs:
+		 *   0x01 - Salsa20/12+SHA512 hashcash function
+		 *
+		 * Salsa20/12+SHA512 is based on the following composite hash function:
+		 *
+		 * (1) Compute SHA512(candidate)
+		 * (2) Use the first 256 bits of the result of #1 as a key to encrypt
+		 *     131072 zero bytes with Salsa20/12 (with a zero IV).
+		 * (3) Compute SHA512(the result of step #2)
+		 * (4) Accept this candiate if the first [difficulty] bits of the result
+		 *     from step #3 are zero. Otherwise generate a new candidate and try
+		 *     again.
+		 *
+		 * This is performed repeatedly on candidates generated by appending the
+		 * supplied challenge to an arbitrary nonce until a valid candidate
+		 * is found. This chosen prepended nonce is then returned as the result
+		 * in OK.
+		 *
+		 * OK payload:
+		 *   <[2] 16-bit length of result>
+		 *   <[...] computed proof of work>
+		 *
+		 * ERROR has no payload.
+		 */
+		VERB_REQUEST_PROOF_OF_WORK = 19
 	};
 	};
 
 
 	/**
 	/**

+ 14 - 0
selftest.cpp

@@ -52,6 +52,7 @@
 #include "node/CertificateOfMembership.hpp"
 #include "node/CertificateOfMembership.hpp"
 #include "node/Defaults.hpp"
 #include "node/Defaults.hpp"
 #include "node/Node.hpp"
 #include "node/Node.hpp"
+#include "node/IncomingPacket.hpp"
 
 
 #include "osdep/OSUtils.hpp"
 #include "osdep/OSUtils.hpp"
 #include "osdep/Phy.hpp"
 #include "osdep/Phy.hpp"
@@ -285,6 +286,19 @@ static int testCrypto()
 		::free((void *)bb);
 		::free((void *)bb);
 	}
 	}
 
 
+	/*
+	for(unsigned int d=8;d<=10;++d) {
+		for(int k=0;k<8;++k) {
+			std::cout << "[crypto] computeSalsa2012Sha512ProofOfWork(" << d << ",\"foobarbaz\",9) == "; std::cout.flush();
+			unsigned char result[16];
+			uint64_t start = OSUtils::now();
+			IncomingPacket::computeSalsa2012Sha512ProofOfWork(d,"foobarbaz",9,result);
+			uint64_t end = OSUtils::now();
+			std::cout << Utils::hex(result,16) << " -- valid: " << IncomingPacket::testSalsa2012Sha512ProofOfWorkResult(d,"foobarbaz",9,result) << ", " << (end - start) << "ms" << std::endl;
+		}
+	}
+	*/
+
 	std::cout << "[crypto] Testing C25519 and Ed25519 against test vectors... "; std::cout.flush();
 	std::cout << "[crypto] Testing C25519 and Ed25519 against test vectors... "; std::cout.flush();
 	for(int k=0;k<ZT_NUM_C25519_TEST_VECTORS;++k) {
 	for(int k=0;k<ZT_NUM_C25519_TEST_VECTORS;++k) {
 		C25519::Pair p1,p2;
 		C25519::Pair p1,p2;