Merge pull request #457 from jagerman/remove-cruft

Remove cruft
This commit is contained in:
Jason Rhinelander 2022-07-21 11:55:18 -03:00 committed by GitHub
commit 20d3d043ef
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 10 additions and 1208 deletions

View file

@ -74,12 +74,6 @@ set(OXEN_LOGGING_SOURCE_ROOT "${PROJECT_SOURCE_DIR}/oxenss" CACHE INTERNAL "")
add_subdirectory(oxen-logging)
add_library(oxen-crypto-ops STATIC
oxen/crypto-ops/keccak.c
oxen/crypto-ops/hash-ops.c)
target_include_directories(oxen-crypto-ops PUBLIC .)
# uSockets doesn't really have a proper build system (just a very simple Makefile) so build it
# ourselves.
if (NOT CMAKE_VERSION VERSION_LESS 3.12)

View file

@ -1,13 +0,0 @@
#include "hash-ops.h"
#include "keccak.h"
#include <string.h>
void hash_process(union hash_state *state, const uint8_t *buf, size_t count) {
keccak1600(buf, count, (uint8_t*)state);
}
void cn_fast_hash(const void *data, size_t length, char *hash) {
union hash_state state;
hash_process(&state, (const uint8_t *)data, length);
memcpy(hash, &state, HASH_SIZE);
}

View file

@ -1,62 +0,0 @@
// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#pragma once
#if !defined(__cplusplus)
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
static inline void *padd(void *p, size_t i) {
return (char *) p + i;
}
#pragma pack(push, 1)
union hash_state {
uint8_t b[200];
uint64_t w[25];
};
#pragma pack(pop)
static_assert(sizeof(union hash_state) == 200, "Invalid structure size");
void hash_permutation(union hash_state *state);
enum {
HASH_SIZE = 32,
HASH_DATA_AREA = 136
};
#endif
void cn_fast_hash(const void*data, size_t length, char *hash);
void hash_process(union hash_state *state, const uint8_t *buf, size_t count);

View file

@ -1,284 +0,0 @@
// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#pragma once
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#ifndef _MSC_VER
#include <sys/param.h>
#endif
#if defined(__ANDROID__)
#include <byteswap.h>
#endif
#if defined(__sun) && defined(__SVR4)
#include <endian.h>
#endif
#if defined(_MSC_VER)
#include <stdlib.h>
static inline uint32_t rol32(uint32_t x, int r) {
static_assert(sizeof(uint32_t) == sizeof(unsigned int), "this code assumes 32-bit integers");
return _rotl(x, r);
}
static inline uint64_t rol64(uint64_t x, int r) {
return _rotl64(x, r);
}
#else
static inline uint32_t rol32(uint32_t x, int r) {
return (x << (r & 31)) | (x >> (-r & 31));
}
static inline uint64_t rol64(uint64_t x, int r) {
return (x << (r & 63)) | (x >> (-r & 63));
}
#endif
static inline uint64_t hi_dword(uint64_t val) {
return val >> 32;
}
static inline uint64_t lo_dword(uint64_t val) {
return val & 0xFFFFFFFF;
}
static inline uint64_t mul128(uint64_t multiplier, uint64_t multiplicand, uint64_t* product_hi) {
// multiplier = ab = a * 2^32 + b
// multiplicand = cd = c * 2^32 + d
// ab * cd = a * c * 2^64 + (a * d + b * c) * 2^32 + b * d
uint64_t a = hi_dword(multiplier);
uint64_t b = lo_dword(multiplier);
uint64_t c = hi_dword(multiplicand);
uint64_t d = lo_dword(multiplicand);
uint64_t ac = a * c;
uint64_t ad = a * d;
uint64_t bc = b * c;
uint64_t bd = b * d;
uint64_t adbc = ad + bc;
uint64_t adbc_carry = adbc < ad ? 1 : 0;
// multiplier * multiplicand = product_hi * 2^64 + product_lo
uint64_t product_lo = bd + (adbc << 32);
uint64_t product_lo_carry = product_lo < bd ? 1 : 0;
*product_hi = ac + (adbc >> 32) + (adbc_carry << 32) + product_lo_carry;
assert(ac <= *product_hi);
return product_lo;
}
static inline uint64_t div_with_reminder(uint64_t dividend, uint32_t divisor, uint32_t* remainder) {
dividend |= ((uint64_t)*remainder) << 32;
*remainder = dividend % divisor;
return dividend / divisor;
}
// Long division with 2^32 base
static inline uint32_t div128_32(uint64_t dividend_hi, uint64_t dividend_lo, uint32_t divisor, uint64_t* quotient_hi, uint64_t* quotient_lo) {
uint64_t dividend_dwords[4];
uint32_t remainder = 0;
dividend_dwords[3] = hi_dword(dividend_hi);
dividend_dwords[2] = lo_dword(dividend_hi);
dividend_dwords[1] = hi_dword(dividend_lo);
dividend_dwords[0] = lo_dword(dividend_lo);
*quotient_hi = div_with_reminder(dividend_dwords[3], divisor, &remainder) << 32;
*quotient_hi |= div_with_reminder(dividend_dwords[2], divisor, &remainder);
*quotient_lo = div_with_reminder(dividend_dwords[1], divisor, &remainder) << 32;
*quotient_lo |= div_with_reminder(dividend_dwords[0], divisor, &remainder);
return remainder;
}
static inline bool shl128(uint64_t* hi, uint64_t* lo) {
bool carry = ((*hi) >> 63);
*hi <<= 1;
*hi += ((*lo) >> 63);
*lo <<= 1;
return carry;
}
// Long division with 2^64 base
static inline uint64_t div128_64(uint64_t dividend_hi, uint64_t dividend_lo, uint64_t divisor, uint64_t* quotient_hi, uint64_t* quotient_lo) {
uint64_t remainder = 0;
for (size_t i = 0; i < 128; i++) {
bool carry = remainder >> 63;
remainder <<= 1;
if (shl128(&dividend_hi, &dividend_lo))
remainder |= 1;
if (carry || remainder >= divisor) {
remainder -= divisor;
dividend_lo |= 1;
}
}
*quotient_hi = dividend_hi;
*quotient_lo = dividend_lo;
return remainder;
}
#define IDENT32(x) ((uint32_t) (x))
#define IDENT64(x) ((uint64_t) (x))
#define SWAP32(x) ((((uint32_t) (x) & 0x000000ff) << 24) | \
(((uint32_t) (x) & 0x0000ff00) << 8) | \
(((uint32_t) (x) & 0x00ff0000) >> 8) | \
(((uint32_t) (x) & 0xff000000) >> 24))
#define SWAP64(x) ((((uint64_t) (x) & 0x00000000000000ff) << 56) | \
(((uint64_t) (x) & 0x000000000000ff00) << 40) | \
(((uint64_t) (x) & 0x0000000000ff0000) << 24) | \
(((uint64_t) (x) & 0x00000000ff000000) << 8) | \
(((uint64_t) (x) & 0x000000ff00000000) >> 8) | \
(((uint64_t) (x) & 0x0000ff0000000000) >> 24) | \
(((uint64_t) (x) & 0x00ff000000000000) >> 40) | \
(((uint64_t) (x) & 0xff00000000000000) >> 56))
static inline uint32_t ident32(uint32_t x) { return x; }
static inline uint64_t ident64(uint64_t x) { return x; }
#ifndef __OpenBSD__
# if defined(__ANDROID__) && defined(__swap32) && !defined(swap32)
# define swap32 __swap32
# elif !defined(swap32)
static inline uint32_t swap32(uint32_t x) {
x = ((x & 0x00ff00ff) << 8) | ((x & 0xff00ff00) >> 8);
return (x << 16) | (x >> 16);
}
# endif
# if defined(__ANDROID__) && defined(__swap64) && !defined(swap64)
# define swap64 __swap64
# elif !defined(swap64)
static inline uint64_t swap64(uint64_t x) {
x = ((x & 0x00ff00ff00ff00ff) << 8) | ((x & 0xff00ff00ff00ff00) >> 8);
x = ((x & 0x0000ffff0000ffff) << 16) | ((x & 0xffff0000ffff0000) >> 16);
return (x << 32) | (x >> 32);
}
# endif
#endif /* __OpenBSD__ */
#if defined(__GNUC__)
#define UNUSED __attribute__((unused))
#else
#define UNUSED
#endif
static inline void mem_inplace_ident(void *mem UNUSED, size_t n UNUSED) { }
#undef UNUSED
static inline void mem_inplace_swap32(void *mem, size_t n) {
size_t i;
for (i = 0; i < n; i++) {
((uint32_t *) mem)[i] = swap32(((const uint32_t *) mem)[i]);
}
}
static inline void mem_inplace_swap64(void *mem, size_t n) {
size_t i;
for (i = 0; i < n; i++) {
((uint64_t *) mem)[i] = swap64(((const uint64_t *) mem)[i]);
}
}
static inline void memcpy_ident32(void *dst, const void *src, size_t n) {
memcpy(dst, src, 4 * n);
}
static inline void memcpy_ident64(void *dst, const void *src, size_t n) {
memcpy(dst, src, 8 * n);
}
static inline void memcpy_swap32(void *dst, const void *src, size_t n) {
size_t i;
for (i = 0; i < n; i++) {
((uint32_t *) dst)[i] = swap32(((const uint32_t *) src)[i]);
}
}
static inline void memcpy_swap64(void *dst, const void *src, size_t n) {
size_t i;
for (i = 0; i < n; i++) {
((uint64_t *) dst)[i] = swap64(((const uint64_t *) src)[i]);
}
}
#ifdef _MSC_VER
# define LITTLE_ENDIAN 1234
# define BIG_ENDIAN 4321
# define BYTE_ORDER LITTLE_ENDIAN
#endif
#if !defined(BYTE_ORDER) || !defined(LITTLE_ENDIAN) || !defined(BIG_ENDIAN)
static_assert(false, "BYTE_ORDER is undefined. Perhaps, GNU extensions are not enabled");
#endif
#if BYTE_ORDER == LITTLE_ENDIAN
#define SWAP32LE IDENT32
#define SWAP32BE SWAP32
#define swap32le ident32
#define swap32be swap32
#define mem_inplace_swap32le mem_inplace_ident
#define mem_inplace_swap32be mem_inplace_swap32
#define memcpy_swap32le memcpy_ident32
#define memcpy_swap32be memcpy_swap32
#define SWAP64LE IDENT64
#define SWAP64BE SWAP64
#define swap64le ident64
#define swap64be swap64
#define mem_inplace_swap64le mem_inplace_ident
#define mem_inplace_swap64be mem_inplace_swap64
#define memcpy_swap64le memcpy_ident64
#define memcpy_swap64be memcpy_swap64
#endif
#if BYTE_ORDER == BIG_ENDIAN
#define SWAP32BE IDENT32
#define SWAP32LE SWAP32
#define swap32be ident32
#define swap32le swap32
#define mem_inplace_swap32be mem_inplace_ident
#define mem_inplace_swap32le mem_inplace_swap32
#define memcpy_swap32be memcpy_ident32
#define memcpy_swap32le memcpy_swap32
#define SWAP64BE IDENT64
#define SWAP64LE SWAP64
#define swap64be ident64
#define swap64le swap64
#define mem_inplace_swap64be mem_inplace_ident
#define mem_inplace_swap64le mem_inplace_swap64
#define memcpy_swap64be memcpy_ident64
#define memcpy_swap64le memcpy_swap64
#endif

View file

@ -1,214 +0,0 @@
// keccak.c
// 19-Nov-11 Markku-Juhani O. Saarinen <mjos@iki.fi>
// A baseline Keccak (3rd round) implementation.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "int-util.h"
#include "hash-ops.h"
#include "keccak.h"
static void local_abort(const char *msg)
{
fprintf(stderr, "%s\n", msg);
#ifdef NDEBUG
_exit(1);
#else
abort();
#endif
}
const uint64_t keccakf_rndc[24] =
{
0x0000000000000001, 0x0000000000008082, 0x800000000000808a,
0x8000000080008000, 0x000000000000808b, 0x0000000080000001,
0x8000000080008081, 0x8000000000008009, 0x000000000000008a,
0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
0x000000008000808b, 0x800000000000008b, 0x8000000000008089,
0x8000000000008003, 0x8000000000008002, 0x8000000000000080,
0x000000000000800a, 0x800000008000000a, 0x8000000080008081,
0x8000000000008080, 0x0000000080000001, 0x8000000080008008
};
const int keccakf_rotc[24] =
{
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14,
27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44
};
const int keccakf_piln[24] =
{
10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4,
15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1
};
// update the state with given number of rounds
void keccakf(uint64_t st[25], int rounds)
{
int i, j, round;
uint64_t t, bc[5];
for (round = 0; round < rounds; round++) {
// Theta
for (i = 0; i < 5; i++)
bc[i] = st[i] ^ st[i + 5] ^ st[i + 10] ^ st[i + 15] ^ st[i + 20];
for (i = 0; i < 5; i++) {
t = bc[(i + 4) % 5] ^ ROTL64(bc[(i + 1) % 5], 1);
for (j = 0; j < 25; j += 5)
st[j + i] ^= t;
}
// Rho Pi
t = st[1];
for (i = 0; i < 24; i++) {
j = keccakf_piln[i];
bc[0] = st[j];
st[j] = ROTL64(t, keccakf_rotc[i]);
t = bc[0];
}
// Chi
for (j = 0; j < 25; j += 5) {
for (i = 0; i < 5; i++)
bc[i] = st[j + i];
for (i = 0; i < 5; i++)
st[j + i] ^= (~bc[(i + 1) % 5]) & bc[(i + 2) % 5];
}
// Iota
st[0] ^= keccakf_rndc[round];
}
}
// compute a keccak hash (md) of given byte length from "in"
typedef uint64_t state_t[25];
void keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen)
{
state_t st;
uint8_t temp[144];
size_t i, rsiz, rsizw;
static_assert(HASH_DATA_AREA <= sizeof(temp), "Bad keccak preconditions");
if (mdlen <= 0 || (mdlen > 100 && sizeof(st) != (size_t)mdlen))
{
local_abort("Bad keccak use");
}
rsiz = sizeof(state_t) == mdlen ? HASH_DATA_AREA : 200 - 2 * mdlen;
rsizw = rsiz / 8;
memset(st, 0, sizeof(st));
for ( ; inlen >= rsiz; inlen -= rsiz, in += rsiz) {
for (i = 0; i < rsizw; i++)
st[i] ^= swap64le(((uint64_t *) in)[i]);
keccakf(st, KECCAK_ROUNDS);
}
// last block and padding
if (inlen + 1 >= sizeof(temp) || inlen > rsiz || rsiz - inlen + inlen + 1 >= sizeof(temp) || rsiz == 0 || rsiz - 1 >= sizeof(temp) || rsizw * 8 > sizeof(temp))
{
local_abort("Bad keccak use");
}
memcpy(temp, in, inlen);
temp[inlen++] = 1;
memset(temp + inlen, 0, rsiz - inlen);
temp[rsiz - 1] |= 0x80;
for (i = 0; i < rsizw; i++)
st[i] ^= swap64le(((uint64_t *) temp)[i]);
keccakf(st, KECCAK_ROUNDS);
if (((size_t)mdlen % sizeof(uint64_t)) != 0)
{
local_abort("Bad keccak use");
}
memcpy_swap64le(md, st, mdlen/sizeof(uint64_t));
}
void keccak1600(const uint8_t *in, size_t inlen, uint8_t *md)
{
keccak(in, inlen, md, sizeof(state_t));
}
#define KECCAK_FINALIZED 0x80000000
#define KECCAK_BLOCKLEN 136
#define KECCAK_WORDS 17
#define KECCAK_DIGESTSIZE 32
#define IS_ALIGNED_64(p) (0 == (7 & ((const char*)(p) - (const char*)0)))
#define KECCAK_PROCESS_BLOCK(st, block) { \
for (int i_ = 0; i_ < KECCAK_WORDS; i_++){ \
((st))[i_] ^= swap64le(((block))[i_]); \
}; \
keccakf(st, KECCAK_ROUNDS); }
void keccak_init(KECCAK_CTX * ctx){
memset(ctx, 0, sizeof(KECCAK_CTX));
}
void keccak_update(KECCAK_CTX * ctx, const uint8_t *in, size_t inlen){
if (ctx->rest & KECCAK_FINALIZED) {
local_abort("Bad keccak use");
}
const size_t idx = ctx->rest;
ctx->rest = (ctx->rest + inlen) % KECCAK_BLOCKLEN;
// fill partial block
if (idx) {
size_t left = KECCAK_BLOCKLEN - idx;
memcpy((char*)ctx->message + idx, in, (inlen < left ? inlen : left));
if (inlen < left) return;
KECCAK_PROCESS_BLOCK(ctx->hash, ctx->message);
in += left;
inlen -= left;
}
const bool is_aligned = IS_ALIGNED_64(in);
while (inlen >= KECCAK_BLOCKLEN) {
const uint64_t* aligned_message_block;
if (is_aligned) {
aligned_message_block = (uint64_t*)in;
} else {
memcpy(ctx->message, in, KECCAK_BLOCKLEN);
aligned_message_block = ctx->message;
}
KECCAK_PROCESS_BLOCK(ctx->hash, aligned_message_block);
in += KECCAK_BLOCKLEN;
inlen -= KECCAK_BLOCKLEN;
}
if (inlen) {
memcpy(ctx->message, in, inlen);
}
}
void keccak_finish(KECCAK_CTX * ctx, uint8_t *md){
if (!(ctx->rest & KECCAK_FINALIZED))
{
// clear the rest of the data queue
memset((char*)ctx->message + ctx->rest, 0, KECCAK_BLOCKLEN - ctx->rest);
((char*)ctx->message)[ctx->rest] |= 0x01;
((char*)ctx->message)[KECCAK_BLOCKLEN - 1] |= 0x80;
// process final block
KECCAK_PROCESS_BLOCK(ctx->hash, ctx->message);
ctx->rest = KECCAK_FINALIZED; // mark context as finalized
}
static_assert(KECCAK_BLOCKLEN > KECCAK_DIGESTSIZE, "");
static_assert(KECCAK_DIGESTSIZE % sizeof(uint64_t) == 0, "");
if (md) {
memcpy_swap64le(md, ctx->hash, KECCAK_DIGESTSIZE / sizeof(uint64_t));
}
}

View file

@ -1,40 +0,0 @@
// keccak.h
// 19-Nov-11 Markku-Juhani O. Saarinen <mjos@iki.fi>
#ifndef KECCAK_H
#define KECCAK_H
#include <stdint.h>
#include <string.h>
#ifndef KECCAK_ROUNDS
#define KECCAK_ROUNDS 24
#endif
#ifndef ROTL64
#define ROTL64(x, y) (((x) << (y)) | ((x) >> (64 - (y))))
#endif
// SHA3 Algorithm context.
typedef struct KECCAK_CTX
{
// 1600 bits algorithm hashing state
uint64_t hash[25];
// 1088-bit buffer for leftovers, block size = 136 B for 256-bit keccak
uint64_t message[17];
// count of bytes in the message[] buffer
size_t rest;
} KECCAK_CTX;
// compute a keccak hash (md) of given byte length from "in"
void keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen);
// update the state
void keccakf(uint64_t st[25], int norounds);
void keccak1600(const uint8_t *in, size_t inlen, uint8_t *md);
void keccak_init(KECCAK_CTX * ctx);
void keccak_update(KECCAK_CTX * ctx, const uint8_t *in, size_t inlen);
void keccak_finish(KECCAK_CTX * ctx, uint8_t *md);
#endif

View file

@ -1,8 +1,7 @@
add_library(crypto STATIC
keys.cpp
channel_encryption.cpp
signature.cpp)
channel_encryption.cpp)
find_package(Threads)
@ -10,6 +9,5 @@ target_link_libraries(crypto
PRIVATE
common
logging
oxen-crypto-ops
OpenSSL::SSL
sodium)

View file

@ -1,182 +0,0 @@
#include "signature.h"
extern "C" {
#include "oxen/crypto-ops/hash-ops.h"
}
#include <oxenc/base64.h>
#include <sodium/crypto_core_ed25519.h>
#include <sodium/crypto_generichash.h>
#include <sodium/crypto_scalarmult_ed25519.h>
#include <sodium/randombytes.h>
#include <sodium/utils.h>
#include <algorithm>
#include <cstdint>
#include <cstring>
#include <iterator>
#include <string>
static_assert(crypto_generichash_BYTES == oxen::crypto::HASH_SIZE, "Wrong hash size!");
namespace oxen::crypto {
using ec_point = std::array<unsigned char, 32>;
struct s_comm {
unsigned char h[32];
unsigned char key[32];
unsigned char comm[32];
};
static ec_scalar monero_hash_to_scalar(const void* input, size_t size) {
// We're trying to be backwards compatible with Monero's approach here, which is to
// calculate H mod L from a 32-byte H. sodium reduces a 64-byte value, however, so will
// fill it with 0s to get the same result. (This is crappy, but to use a 64-byte hash would
// break backwards compatibility).
unsigned char hash[64] = {0};
cn_fast_hash(input, size, reinterpret_cast<char*>(hash));
ec_scalar result;
crypto_core_ed25519_scalar_reduce(result.data(), hash);
return result;
}
hash hash_data(std::string_view data) {
hash hash;
crypto_generichash(
hash.data(),
hash.size(),
reinterpret_cast<const unsigned char*>(data.data()),
data.size(),
nullptr,
0);
return hash;
}
// Concatenate a bunch of trivial types together into a std::array
template <typename... T, typename Array = std::array<unsigned char, (sizeof(T) + ...)>>
static Array concatenate(const T&... v) {
static_assert((std::is_trivial_v<T> && ...));
Array result;
unsigned char* ptr = result.data();
((std::memcpy(ptr, reinterpret_cast<const void*>(&v), sizeof(T)), ptr += sizeof(T)), ...);
return result;
}
// Blake2B hash some trivial types together
template <size_t S, typename... T>
static std::array<unsigned char, S> hash_concatenate(const T&... v) {
std::array<unsigned char, S> h;
crypto_generichash_state state;
crypto_generichash_init(&state, nullptr, 0, h.size());
(crypto_generichash_update(&state, reinterpret_cast<const unsigned char*>(&v), sizeof(T)), ...);
crypto_generichash_final(&state, h.data(), h.size());
return h;
}
// With straight Ed25519 sigs this wouldn't be necessary (because sodium clamps things for us),
// but Monero sigs require things to be able to be multiplied *without* clamping (because Monero
// crypto is screwed up by design), so the onus is on the signer to clamp scalars directly.
void clamp(ec_scalar& s) {
s[0] &= 248;
s[31] &= 63;
s[31] |= 64;
}
signature generate_signature(const hash& prefix_hash, const legacy_keypair& keys) {
/* Generate a non-standard Monero-compatible signature. This is different from an standard
* Ed25519 signature for no good reason (i.e. Monero NIH), but we can't change it because we
* would break backwards compatibility.
*
* Monero generation is as follows:
*
* Given M = H(msg)
* x = random scalar
* X = xG
* c = H(M || A || X)
*
* where H is cn_fast_hash. The signature for this is computed as:
*
* r = x - ac
*
* with final signature: (c, r)
*
* But this relies on a random scalar x, which is undesirable; Ed25519, in contrast, gets x
* from H(S
* || M) where S is the second half of the SHA512 hash of the seed. (Monero keys throw away
* the seed and only keep a just because NIH again).
*
* So we aim to be Ed25519-like in terms of a hash of a deterministic value rather than a
* random number, but keeping things Monero compatible, so we do:
*
* x = H[512](H[256](a) || A || M)
*
* where H[N] is a N-bit Blake2b hash; we reduce and clamp the outer hash to get x, and
* otherwise keep things the same to remain Monero sig compatible. (Effectively we are
* replacing the RNG with the deterministic hash function).
*/
signature sig;
const auto& [pubkey, seckey] = keys;
crypto_generichash(
sig.r.data(),
sig.r.size(),
seckey.data(),
seckey.size(),
nullptr,
0); // use r as tmp storage
auto xH = hash_concatenate<64>(sig.r /*H(a)*/, pubkey /*A*/, prefix_hash /*M*/);
ec_scalar x;
crypto_core_ed25519_scalar_reduce(x.data(), xH.data());
clamp(x);
ec_point X;
crypto_scalarmult_ed25519_base_noclamp(X.data(), x.data());
auto M_A_X = concatenate(prefix_hash, pubkey, X);
sig.c = monero_hash_to_scalar(M_A_X.data(), M_A_X.size());
crypto_core_ed25519_scalar_mul(sig.r.data(), seckey.data(), sig.c.data()); // r == ac
crypto_core_ed25519_scalar_sub(sig.r.data(), x.data(), sig.r.data()); // r = x - r (= x - ac)
return sig;
}
bool check_signature(const signature& sig, const hash& prefix_hash, const legacy_pubkey& pub) {
/* Monero-style signature verification (which is different from Ed25519 because Monero NIH).
*
* given signature (c, r), message hash M, pubkey A, and basepoint G:
* X = rG + cA
* Check: H(M||A||X) == c
*/
ec_point X, cA;
if (0 != crypto_scalarmult_ed25519_base_noclamp(X.data(), sig.r.data()))
return false;
if (0 != crypto_scalarmult_ed25519_noclamp(cA.data(), sig.c.data(), pub.data()))
return false;
if (0 != crypto_core_ed25519_add(X.data(), X.data(), cA.data()))
return false;
if (1 != crypto_core_ed25519_is_valid_point(X.data()))
return false;
// H(M||A||X):
auto M_A_X = concatenate(prefix_hash, pub, X);
auto expected_c = monero_hash_to_scalar(M_A_X.data(), M_A_X.size());
return 0 == sodium_memcmp(expected_c.data(), sig.c.data(), expected_c.size());
}
signature signature::from_base64(std::string_view signature_b64) {
if (!oxenc::is_base64(signature_b64))
throw std::runtime_error{"Invalid data: not base64-encoded"};
// 64 bytes bytes -> 86/88 base64 encoded bytes with/without padding
if (!(signature_b64.size() == 86 ||
(signature_b64.size() == 88 && signature_b64.substr(86) == "==")))
throw std::runtime_error{"Invalid data: b64 data size does not match signature size"};
// convert signature
signature sig;
static_assert(sizeof(sig) == 64);
oxenc::from_base64(
signature_b64.begin(), signature_b64.end(), reinterpret_cast<unsigned char*>(&sig));
return sig;
}
} // namespace oxen::crypto

View file

@ -1,32 +0,0 @@
#pragma once
#include "keys.h"
#include <array>
namespace oxen::crypto {
constexpr size_t HASH_SIZE = 32;
constexpr size_t EC_SCALAR_SIZE = 32;
using hash = std::array<unsigned char, HASH_SIZE>;
using ec_scalar = std::array<unsigned char, EC_SCALAR_SIZE>;
struct signature {
ec_scalar c, r;
// Decodes a base64 signature into a `signature`. Throws on invalid input.
static signature from_base64(std::string_view b64);
};
// Returns a 32-byte blake2b hash of the given data
hash hash_data(std::string_view data);
// Generates a not-proper-Ed25519 Monero signature for the given legacy monero pubkey.
// TODO: start using proper Ed25519 signatures instead.
signature generate_signature(const hash& prefix_hash, const legacy_keypair& keys);
// Verifies the not-proper-Ed25519 Monero signature against the given public key
bool check_signature(const signature& sig, const hash& prefix_hash, const legacy_pubkey& pub);
} // namespace oxen::crypto

View file

@ -1,74 +0,0 @@
#pragma once
#include <iostream>
#include <mutex>
#include <spdlog/sinks/base_sink.h>
#include <vector>
// A sink used to store most important logs for developers
namespace oxen::logging {
template <typename Mutex>
class dev_sink : public spdlog::sinks::base_sink<Mutex> {
using Base = spdlog::sinks::base_sink<Mutex>;
// Potentially all entries will be returned in a
// single message, so we should keep the limit
// relatively small
static constexpr size_t BUFFER_SIZE = 100;
static constexpr size_t MAX_ENTRIES = 2 * BUFFER_SIZE;
std::vector<std::string> primary_buffer_;
std::vector<std::string> secondary_buffer_;
// size_t log_entires
protected:
void sink_it_(const spdlog::details::log_msg& msg) override {
spdlog::memory_buf_t formatted;
Base::formatter_->format(msg, formatted);
if (primary_buffer_.size() >= BUFFER_SIZE) {
secondary_buffer_ = std::move(primary_buffer_);
primary_buffer_.clear();
}
primary_buffer_.push_back(fmt::to_string(formatted));
}
void flush_() override {
// no op
}
public:
dev_sink() : spdlog::sinks::base_sink<Mutex>() {
primary_buffer_.reserve(BUFFER_SIZE);
secondary_buffer_.reserve(BUFFER_SIZE);
}
std::vector<std::string> peek() {
std::lock_guard<Mutex> lock{this->mutex_};
std::vector<std::string> result;
result.reserve(MAX_ENTRIES);
for (auto it = primary_buffer_.end() - 1;
it >= primary_buffer_.begin() && result.size() < MAX_ENTRIES;
--it) {
result.push_back(*it);
}
for (auto it = secondary_buffer_.end() - 1;
it >= secondary_buffer_.begin() && result.size() < MAX_ENTRIES;
--it) {
result.push_back(*it);
}
return result;
}
};
#include <mutex>
using dev_sink_mt = dev_sink<std::mutex>;
} // namespace oxen::logging

View file

@ -98,9 +98,7 @@ namespace {
/// 76800 bytes (== 102400 in b64 encoding). For OMQ RPC requests the value is bytes.
/// - `namespace` (optional) a non-zero integer namespace (from -32768 to 32767) in which to store
/// this message. Messages in different namespaces are treated as separate storage boxes from
/// untagged messages. (Note that before the Oxen 10 hardfork (HF 19) this field will be ignored
/// and the message will end up in the default namespace (i.e. namespace 0) regardless of what was
/// specified here.)
/// untagged messages.
/// - `subkey` (optional) if provided this is a 32-byte subkey value, encoded base64 or hex (for
/// json requests; bytes, for bt-encoded requests), to use for subkey signature verification
/// instead of using `pubkey` directly. Denoting this value as `c` and `pubkey` as `A`, the
@ -447,7 +445,7 @@ struct delete_before final : recursive {
/// - "swarms" dict mapping ed25519 pubkeys (in hex) of swarm members to dict values of:
/// - "failed" and other failure keys -- see `recursive`.
/// - "updated":
/// - if deleting from a single namespace then this is a list of (ascii-sorted) hashes that
/// - if expiring from a single namespace then this is a list of (ascii-sorted) hashes that
/// had their expiries updated to `expiry`; messages that did not exist or that already
/// had an expiry <= the given expiry are not included.
/// - otherwise (i.e. namespace="all") this is a dict of `{ namespace => [sorted hashes] }`
@ -483,10 +481,10 @@ struct expire_all final : recursive {
/// to the given `pubkey` value (without the `05` prefix).
/// - messages -- array of message hash strings (as provided by the storage server) to update.
/// Messages can be from any namespace(s).
/// - expiry -- the new expiry timestamp (milliseconds since unix epoch). Must be >= 60s ago. As
/// of HF19 this can be used to extend expiries instead of just shortening them. The expiry can
/// be extended to at most the maximum TTL (14 days) from now; specifying a later timestamp will
/// be truncated to the maximum.
/// - expiry -- the new expiry timestamp (milliseconds since unix epoch). Must be >= 60s ago. This
/// can be used to extend expiries instead of just shortening them. The expiry can be extended to
/// at most the maximum TTL (14 days) from now; specifying a later timestamp will be truncated to
/// the maximum.
/// - signature -- Ed25519 signature of:
/// ("expire" || expiry || messages[0] || ... || messages[N])
/// where `expiry` is the expiry timestamp expressed as a string. The signature must be base64
@ -496,9 +494,7 @@ struct expire_all final : recursive {
/// Returns dict of:
/// - "swarms" dict mapping ed25519 pubkeys (in hex) of swarm members to dict values of:
/// - "failed" and other failure keys -- see `recursive`.
/// - "updated": ascii-sorted list of hashes of messages that had their expiries updated. As of
/// HF19, this includes messages that have had their expiries extended (before HF19 expiries
/// could only be shortened but not extended).
/// - "updated": ascii-sorted list of hashes of messages that had their expiries updated.
/// - "expiry": the expiry timestamp that was applied (which might be different from the request
/// expiry, e.g. if the requested value exceeded the permitted TTL).
/// - "signature": signature of:

View file

@ -6,7 +6,6 @@
#include <oxenss/server/omq.h>
#include <oxenss/logging/oxen_logger.h>
#include <oxenss/snode/service_node.h>
#include <oxenss/crypto/signature.h>
#include <oxenss/utils/string_utils.hpp>
#include <oxenss/utils/time.hpp>
#include <oxenss/version.h>
@ -500,11 +499,6 @@ void RequestHandler::process_client_req(rpc::store&& req, std::function<void(Res
return cb(Response{http::NOT_ACCEPTABLE, "Timestamp error: check your clock"sv});
}
// TODO: remove after HF 19
if (!service_node_.hf_at_least(snode::HARDFORK_NAMESPACES) &&
req.msg_namespace != namespace_id::Default)
req.msg_namespace = namespace_id::Default;
if (!is_public_namespace(req.msg_namespace)) {
if (!req.signature) {
auto err = fmt::format(
@ -667,10 +661,9 @@ void RequestHandler::process_client_req(
// At HF19 start requiring authentication for all retrievals (except legacy closed groups, which
// can't be authenticated for technical reasons).
if (service_node_.hf_at_least(snode::HARDFORK_RETRIEVE_AUTH) &&
req.msg_namespace != namespace_id::LegacyClosed) {
if (req.msg_namespace != namespace_id::LegacyClosed) {
if (!req.check_signature) {
log::debug(logcat, "retrieve: request signature required as of HF19.1");
log::debug(logcat, "retrieve: request signature required");
return cb(Response{http::UNAUTHORIZED, "retrieve: request signature required"sv});
}
}

View file

@ -6,7 +6,6 @@
#include <oxenss/logging/oxen_logger.h>
#include <oxenss/rpc/request_handler.h>
#include <oxenss/snode/service_node.h>
#include <oxenss/crypto/signature.h>
#include <oxenss/utils/string_utils.hpp>
#include <oxenss/utils/file.hpp>
@ -419,15 +418,6 @@ namespace {
} // anonymous namespace
void HTTPS::create_endpoints(uWS::SSLApp& https) {
// Legacy target, can be removed post-HF18.1:
https.post("/swarms/ping_test/v1", [this](HttpResponse* res, HttpRequest* /*req*/) {
log::trace(logcat, "Received (old) https ping_test");
service_node_.update_last_ping(snode::ReachType::HTTPS);
rpc::Response resp{http::OK};
resp.headers.emplace_back(http::SNODE_SIGNATURE_HEADER, cert_signature_);
queue_response_internal(*this, *res, std::move(resp));
});
https.post("/ping_test/v1", [this](HttpResponse* res, HttpRequest* /*req*/) {
log::trace(logcat, "Received https ping_test");
service_node_.update_last_ping(snode::ReachType::HTTPS);
@ -437,12 +427,6 @@ void HTTPS::create_endpoints(uWS::SSLApp& https) {
queue_response_internal(*this, *res, std::move(resp));
});
// Legacy storage testing over HTTPS; can be removed after HF18.1
https.post("/swarms/storage_test/v1", [this](HttpResponse* res, HttpRequest* req) {
if (!check_ready(*res))
return;
process_storage_test_req(*req, *res);
});
https.post("/storage_rpc/v1", [this](HttpResponse* res, HttpRequest* req) {
if (!check_ready(*res))
return;
@ -478,177 +462,6 @@ void HTTPS::create_endpoints(uWS::SSLApp& https) {
});
}
/// Verifies snode pubkey and signature values in a request; returns the sender pubkey on
/// success or a filled-out error Response if verification fails.
///
/// `prevalidate` - if true, do a "pre-validation": check that the required header values
/// (pubkey, signature) are present and valid (including verifying that the pubkey is a valid
/// snode) but don't actually verify the signature against the body (note that this is *not*
/// signature verification but is used as a pre-check before reading a body to ensure the
///
/// Deprecated; can be removed after HF19
static std::variant<crypto::legacy_pubkey, rpc::Response> validate_snode_signature(
snode::ServiceNode& sn, const Request& r, bool prevalidate = false) {
crypto::legacy_pubkey pubkey;
if (auto it = r.headers.find(http::SNODE_SENDER_HEADER); it != r.headers.end())
pubkey = crypto::parse_legacy_pubkey(it->second);
if (!pubkey) {
log::debug(logcat, "Missing or invalid pubkey header for request");
return rpc::Response{http::BAD_REQUEST, "missing/invalid pubkey header"sv};
}
crypto::signature sig;
if (auto it = r.headers.find(http::SNODE_SIGNATURE_HEADER); it != r.headers.end()) {
try {
sig = crypto::signature::from_base64(it->second);
} catch (...) {
log::warning(logcat, "invalid signature (not b64) found in header from {}", pubkey);
return rpc::Response{http::BAD_REQUEST, "Invalid signature"sv};
}
} else {
log::debug(logcat, "Missing required signature header for request");
return rpc::Response{http::BAD_REQUEST, "missing signature header"sv};
}
if (!sn.find_node(pubkey)) {
log::debug(logcat, "Rejecting signature from unknown service node: {}", pubkey);
return rpc::Response{http::UNAUTHORIZED, "Unknown service node"sv};
}
if (!prevalidate) {
if (!check_signature(sig, crypto::hash_data(r.body), pubkey)) {
log::debug(logcat, "snode signature verification failed for pubkey {}", pubkey);
return rpc::Response{http::UNAUTHORIZED, "snode signature verification failed"sv};
}
}
return pubkey;
}
void HTTPS::process_storage_test_req(HttpRequest& req, HttpResponse& res) {
auto check_snode_headers = [this, &res](call_data& data) {
// Before we read the body make sure we have the required headers (so that we can reject
// bad requests earlier).
if (auto prevalidate = validate_snode_signature(service_node_, data.request, true);
std::holds_alternative<rpc::Response>(prevalidate)) {
queue_response_internal(*this, res, std::move(std::get<rpc::Response>(prevalidate)));
data.replied = true;
} else {
assert(std::holds_alternative<crypto::legacy_pubkey>(prevalidate));
if (rate_limiter_.should_rate_limit(std::get<crypto::legacy_pubkey>(prevalidate))) {
queue_response_internal(
*this,
res,
rpc::Response{
http::TOO_MANY_REQUESTS, "too many requests from this snode"sv});
data.replied = true;
}
}
};
handle_request(
*this,
omq_,
req,
res,
[this](std::shared_ptr<call_data> data) mutable {
// Now that we have the body, fully validate the snode signature:
if (auto validate = validate_snode_signature(service_node_, data->request);
std::holds_alternative<rpc::Response>(validate))
return queue_response(
std::move(data), std::move(std::get<rpc::Response>(validate)));
auto& omq = data->omq;
auto& request = data->request;
omq.inject_task(
"https",
"https:" + request.uri,
request.remote_addr,
[this, data = std::move(data)]() mutable {
if (data->replied || data->aborted)
return;
auto& req = data->request;
rpc::Response resp{http::BAD_REQUEST};
resp.headers.emplace_back(
http::SNODE_SIGNATURE_HEADER, cert_signature_);
crypto::legacy_pubkey tester_pk;
if (auto it = req.headers.find(http::SNODE_SENDER_HEADER);
it != req.headers.end()) {
if (tester_pk = crypto::parse_legacy_pubkey(it->second);
!tester_pk) {
log::debug(logcat, "Invalid test request: invalid pubkey");
resp.body = "invalid tester pubkey header"sv;
return queue_response(std::move(data), std::move(resp));
}
} else {
log::debug(logcat, "Invalid test request: missing pubkey");
resp.body = "missing tester pubkey header"sv;
return queue_response(std::move(data), std::move(resp));
}
auto body = json::parse(data->request.body, nullptr, false);
if (body.is_discarded()) {
log::debug(logcat, "Bad snode test request: invalid json");
resp.body = "invalid json"sv;
return queue_response(std::move(data), std::move(resp));
}
uint64_t height;
std::string msg_hash;
try {
height = body.at("height").get<uint64_t>();
msg_hash = body.at("hash").get<std::string>();
} catch (...) {
resp.body = "Bad snode test request: missing fields in json"sv;
log::debug(logcat, std::get<std::string_view>(resp.body));
return queue_response(std::move(data), std::move(resp));
}
request_handler_.process_storage_test_req(
height,
tester_pk,
msg_hash,
[data = std::move(data), resp = std::move(resp)](
snode::MessageTestStatus status,
std::string answer,
std::chrono::steady_clock::duration elapsed) mutable {
resp.status = http::OK;
switch (status) {
case snode::MessageTestStatus::SUCCESS:
log::debug(
logcat,
"Storage test success after {}",
util::friendly_duration(elapsed));
resp.body =
json{{"status", "OK"},
{"value", oxenc::to_base64(answer)}};
return queue_response(
std::move(data), std::move(resp));
case snode::MessageTestStatus::WRONG_REQ:
resp.body = json{{"status", "wrong request"}};
return queue_response(
std::move(data), std::move(resp));
case snode::MessageTestStatus::RETRY:
[[fallthrough]]; // If we're getting called then a
// retry ran out of time
case snode::MessageTestStatus::ERROR:
// Promote this to `error` once we enforce storage
// testing
log::debug(
logcat,
"Failed storage test, tried for {}",
util::friendly_duration(elapsed));
resp.body = json{{"status", "other"}};
return queue_response(
std::move(data), std::move(resp));
}
});
});
},
std::move(check_snode_headers));
}
bool HTTPS::should_rate_limit_client(std::string_view addr) {
if (addr.size() != 4)
return true;

View file

@ -92,8 +92,6 @@ class HTTPS {
bool should_rate_limit_client(std::string_view addr);
// Deprecated storage test over HTTPS; can be removed after HF19
void process_storage_test_req(HttpRequest& req, HttpResponse& res);
void process_storage_rpc_req(HttpRequest& req, HttpResponse& res);
void process_onion_req_v2(HttpRequest& req, HttpResponse& res);
@ -136,12 +134,6 @@ class HTTPS {
rpc::RateLimiter& rate_limiter_;
// Keys for signing responses
crypto::legacy_keypair legacy_keys_;
// Certificate signature of the cert.pem so that the client can verify who they are
// receiving a reply from (deprecated, to be removed after HF19). This was a mistake: it
// doesn't provide any assurance *before* sending data, and is almost impossible to verify
// without rolling your own low-level SSL sockets. Everything that needs encrypted data is
// now done over encrypted, authenticated zmq or onion requests.
std::string cert_signature_;
friend void queue_response_internal(
HTTPS& https, HttpResponse& r, rpc::Response res, bool force_close);

View file

@ -61,12 +61,6 @@ namespace detail {
using headers =
std::unordered_map<std::string, std::string, detail::ascii_lc_hash, detail::ascii_lc_equal>;
// Deprecated headers; these can be removed after HF19
constexpr auto SNODE_SENDER_HEADER = "X-Loki-Snode-PubKey";
constexpr auto SNODE_TARGET_HEADER = "X-Target-Snode-Key";
constexpr auto SNODE_SIGNATURE_HEADER = "X-Loki-Snode-Signature";
constexpr auto SENDER_KEY_HEADER = "X-Sender-Public-Key";
// Returned in a HF19+ ping_test to include the remote's pubkey in the response
constexpr auto SNODE_PUBKEY_HEADER = "X-Oxen-Snode-Pubkey";

View file

@ -3,7 +3,6 @@
#include "serialization.h"
#include <oxenss/version.h>
#include <oxenss/common/mainnet.h>
#include <oxenss/crypto/signature.h>
#include <oxenss/rpc/request_handler.h>
#include <oxenss/server/omq.h>
#include <oxenss/logging/oxen_logger.h>
@ -714,18 +713,6 @@ void ServiceNode::ping_peers() {
test_reachability(sn, prev_fails);
}
std::vector<std::pair<std::string, std::string>> ServiceNode::sign_request(
std::string_view body) const {
std::vector<std::pair<std::string, std::string>> headers;
const auto signature = crypto::generate_signature(
crypto::hash_data(body), {our_address_.pubkey_legacy, our_seckey_});
headers.emplace_back(
http::SNODE_SIGNATURE_HEADER, oxenc::to_base64(util::view_guts(signature)));
headers.emplace_back(
http::SNODE_SENDER_HEADER, oxenc::to_base32z(our_address_.pubkey_legacy.view()));
return headers;
}
void ServiceNode::test_reachability(const sn_record& sn, int previous_failures) {
log::debug(
logcat,

View file

@ -173,10 +173,6 @@ class ServiceNode {
// retesting.
void report_reachability(const sn_record& sn, bool reachable, int previous_failures);
/// Deprecated; can be removed after HF19
/// Returns headers to add to the request containing signature info for the given body
std::vector<std::pair<std::string, std::string>> sign_request(std::string_view body) const;
public:
ServiceNode(
sn_record address,

View file

@ -9,7 +9,6 @@ add_executable(Test
rate_limiter.cpp
serialization.cpp
service_node.cpp
signature.cpp
storage.cpp
)

View file

@ -1,59 +0,0 @@
#include <oxenss/crypto/signature.h>
#include <catch2/catch.hpp>
#include <oxenc/base64.h>
#include <vector>
using namespace oxen;
using namespace oxen::crypto;
TEST_CASE("signatures - hash generation", "[signature][hash]") {
std::vector<hash> hashes;
const std::string inputs[] = {
"L", "", "FOO", "FOO_", "FO0", "FFO", "FFFFFFFFFFFFFFFFFFFFOOOOOOOOOOOOOO"};
for (const auto& str : inputs) {
const auto hash = hash_data(str);
CHECK(std::find(hashes.begin(), hashes.end(), hash) == hashes.end());
hashes.push_back(hash);
}
}
static const auto public_key =
legacy_pubkey::from_hex("e35b7cf5057845284740af496ec323148db68ac2553a05e4677b96f3afdabcd1");
static const auto secret_key =
legacy_seckey::from_hex("97fe49c2d436e5a39f8aa2e3374d19b532eecfb2b0367eaa6f703279e34ec102");
TEST_CASE("signatures - it_signs_and_verifies", "[signature][...]") {
const auto hash = hash_data("This is the payload");
REQUIRE(secret_key.pubkey() == public_key);
const auto sig = generate_signature(hash, {public_key, secret_key});
CHECK(check_signature(sig, hash, public_key));
}
TEST_CASE("signatures - it_signs_and_verifies_encoded_inputs", "[signature][...]") {
const auto hash = hash_data("This is the payload");
const auto sig = generate_signature(hash, {public_key, secret_key});
// convert signature to base64
std::string raw_sig;
raw_sig.reserve(sig.c.size() + sig.r.size());
raw_sig.insert(raw_sig.begin(), sig.c.begin(), sig.c.end());
raw_sig.insert(raw_sig.end(), sig.r.begin(), sig.r.end());
const std::string sig_b64 = oxenc::to_base64(raw_sig);
CHECK(check_signature(signature::from_base64(sig_b64), hash, public_key));
}
TEST_CASE("signatures - it_rejects_wrong_signature", "[signature][...]") {
const auto hash = hash_data("This is the payload");
auto sig = generate_signature(hash, {public_key, secret_key});
// amend signature
sig.c[4]++;
CHECK_FALSE(check_signature(sig, hash, public_key));
}