mirror of
https://github.com/oxen-io/oxen-storage-server.git
synced 2023-12-13 21:00:26 +01:00
commit
20d3d043ef
19 changed files with 10 additions and 1208 deletions
6
external/CMakeLists.txt
vendored
6
external/CMakeLists.txt
vendored
|
@ -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)
|
||||
|
|
13
external/oxen/crypto-ops/hash-ops.c
vendored
13
external/oxen/crypto-ops/hash-ops.c
vendored
|
@ -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);
|
||||
}
|
62
external/oxen/crypto-ops/hash-ops.h
vendored
62
external/oxen/crypto-ops/hash-ops.h
vendored
|
@ -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);
|
284
external/oxen/crypto-ops/int-util.h
vendored
284
external/oxen/crypto-ops/int-util.h
vendored
|
@ -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(÷nd_hi, ÷nd_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
|
214
external/oxen/crypto-ops/keccak.c
vendored
214
external/oxen/crypto-ops/keccak.c
vendored
|
@ -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));
|
||||
}
|
||||
}
|
40
external/oxen/crypto-ops/keccak.h
vendored
40
external/oxen/crypto-ops/keccak.h
vendored
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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:
|
||||
|
|
|
@ -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});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -9,7 +9,6 @@ add_executable(Test
|
|||
rate_limiter.cpp
|
||||
serialization.cpp
|
||||
service_node.cpp
|
||||
signature.cpp
|
||||
storage.cpp
|
||||
)
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
Loading…
Reference in a new issue