177 lines
4.9 KiB
C
177 lines
4.9 KiB
C
#include "include/conf.h"
|
|
|
|
#include <unistd.h>
|
|
#include <sys/random.h>
|
|
|
|
#include "include/def.h"
|
|
|
|
#include "include/pow.h"
|
|
#include "include/proto.h"
|
|
#include "include/blake2s.h"
|
|
|
|
#define DP_POW_XOR_ONES_RANGE 32
|
|
|
|
static const uint8_t dp_pow_difficulty = 20; /* Primary difficulty */
|
|
static const uint8_t dp_pow_ones = 8; /* Secondary difficulty */
|
|
|
|
bool dp_pow_has_leading_zero_bits(
|
|
const uint8_t digest[DP_BLAKE2S_OUTPUT_SIZE_MAX], uint8_t difficulty) {
|
|
|
|
if (!digest || difficulty == 0) {
|
|
return false;
|
|
}
|
|
|
|
const uint8_t full_bytes = difficulty / 8;
|
|
const uint8_t remainder_bytes = difficulty % 8;
|
|
|
|
for (uint8_t idx = 0; idx < full_bytes; ++idx) {
|
|
if (digest[idx] != '\x00') {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (remainder_bytes > 0) {
|
|
const uint8_t next_byte = digest[full_bytes];
|
|
const uint8_t mask =
|
|
((uint8_t)((uint8_t)0xff << (uint8_t)(8 - remainder_bytes)) &
|
|
(uint8_t)0xff);
|
|
|
|
if ((next_byte & mask) != 0x00) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static inline uint8_t dp_count_ones(uint8_t num) {
|
|
/* Fast bit-counting using Hacker's Delight method */
|
|
num = (uint8_t)(num - ((uint8_t)(num >> (uint8_t)1) & (uint8_t)0x55));
|
|
num = (uint8_t)((num & (uint8_t)0x33) +
|
|
((uint8_t)(num >> (uint8_t)2) & (uint8_t)0x33));
|
|
return (uint8_t)(((uint8_t)(num + (num >> (uint8_t)4)) & (uint8_t)0x0F));
|
|
}
|
|
|
|
bool dp_pow_has_xor_ones(const uint8_t digest[DP_BLAKE2S_OUTPUT_SIZE_MAX],
|
|
const uint8_t challenge[DP_POW_CHALLENGE_SIZE],
|
|
uint8_t ones) {
|
|
if (!digest || !challenge || ones == 0) {
|
|
return false;
|
|
}
|
|
|
|
uint8_t ok_ones = 0;
|
|
|
|
for (uint8_t idx = 0; idx < DP_POW_XOR_ONES_RANGE; ++idx) {
|
|
const uint8_t xored = (digest[idx % DP_BLAKE2S_OUTPUT_SIZE_MAX] ^
|
|
challenge[idx % DP_POW_CHALLENGE_SIZE]);
|
|
|
|
if (dp_count_ones(xored) >= 6) {
|
|
++ok_ones;
|
|
}
|
|
|
|
if (ok_ones == ones) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool dp_pow_protect(SSL *ssl, dp_Logger *logger) {
|
|
if (!ssl) {
|
|
return false;
|
|
}
|
|
|
|
uint8_t buf[DP_POW_CHALLENGE_SIZE + 1 + 1] = {0};
|
|
|
|
if (getrandom(buf, DP_POW_CHALLENGE_SIZE, 0) != DP_POW_CHALLENGE_SIZE) {
|
|
return false;
|
|
}
|
|
buf[DP_POW_CHALLENGE_SIZE] = dp_pow_difficulty;
|
|
buf[DP_POW_CHALLENGE_SIZE + 1] = dp_pow_ones;
|
|
|
|
if (SSL_write(ssl, buf, sizeof(buf)) != sizeof(buf)) {
|
|
return false;
|
|
}
|
|
|
|
uint8_t sol_buf[8] = {0};
|
|
uint8_t pings = 0;
|
|
|
|
dp_log(logger, DP_LOG_INFO, DP_POW_LOG "/protect",
|
|
"Protecting route using PoW");
|
|
|
|
while (pings < 64) {
|
|
if (SSL_read(ssl, sol_buf, 1) != 1) {
|
|
return false;
|
|
}
|
|
|
|
if (sol_buf[0] == dp_PacketType_ping) {
|
|
if (pings == 64) {
|
|
dp_proto_error(ssl, dp_PacketError_pow_too_many_pings,
|
|
"Too many PoW pings", logger);
|
|
return false;
|
|
}
|
|
|
|
if (!dp_proto_ping_reply(ssl)) {
|
|
return false;
|
|
}
|
|
|
|
++pings;
|
|
sleep(1);
|
|
continue;
|
|
}
|
|
|
|
if (sol_buf[0] != dp_PacketType_ready) {
|
|
dp_proto_skip(ssl, sol_buf[0]);
|
|
dp_proto_error(ssl, dp_PacketError_status,
|
|
"Invalid status packet", logger);
|
|
return false;
|
|
}
|
|
|
|
if (SSL_read(ssl, sol_buf, 8) != 8) {
|
|
dp_proto_error(ssl, dp_PacketError_proto_packet_too_short,
|
|
"Packet too short for PoW nonce", logger);
|
|
return false;
|
|
}
|
|
|
|
/* rest - nonce bytes */
|
|
|
|
char str[21] = {0};
|
|
const int len = dp_u642str(str, dp_buf2u64_le(sol_buf));
|
|
if (len < 1) {
|
|
return false;
|
|
}
|
|
|
|
dp_Blake2sCtx ctx = {0};
|
|
|
|
if (!dp_Blake2sCtx_init(&ctx, DP_BLAKE2S_OUTPUT_SIZE_MAX, buf,
|
|
DP_POW_CHALLENGE_SIZE)) {
|
|
return false;
|
|
}
|
|
if (!dp_Blake2sCtx_update(&ctx, str, (size_t)len)) {
|
|
return false;
|
|
}
|
|
if (!dp_Blake2sCtx_final(&ctx)) {
|
|
return false;
|
|
}
|
|
|
|
uint8_t digest[DP_BLAKE2S_OUTPUT_SIZE_MAX] = {0};
|
|
if (!dp_Blake2sCtx_to_digest(&ctx, digest)) {
|
|
return false;
|
|
}
|
|
|
|
if (dp_pow_has_leading_zero_bits(digest, dp_pow_difficulty) &&
|
|
dp_pow_has_xor_ones(digest, buf, dp_pow_ones)) {
|
|
static const uint8_t allowed = dp_PacketType_allowed;
|
|
SSL_write(ssl, &allowed, 1);
|
|
return true;
|
|
}
|
|
|
|
dp_proto_error(ssl, dp_PacketError_pow_bad_solution,
|
|
"Bad solution provided", logger);
|
|
}
|
|
|
|
dp_proto_error(ssl, dp_PacketError_pow_bad_solution, "No solution provided",
|
|
logger);
|
|
return false;
|
|
}
|