use NTRU for introset end to end encryption

This commit is contained in:
Jeff Becker 2018-08-13 19:22:31 -04:00
parent 98c275ab87
commit 186bd7d573
No known key found for this signature in database
GPG Key ID: F357B3B42F6F9B05
41 changed files with 569 additions and 344 deletions

View File

@ -294,12 +294,8 @@ set(NTRU_SRC
crypto/libntrup/src/ntru.c
)
set(NTRU_LIB sntrup)
add_library(${NTRU_LIB} ${NTRU_SRC})
target_link_libraries(${NTRU_LIB} ${SODIUM_LIB})
set(LIB_SRC
set(LIB_SRC
${NTRU_SRC}
llarp/address_info.cpp
llarp/bencode.cpp
llarp/buffer.cpp
@ -377,6 +373,7 @@ set(TEST_SRC
test/dht_unittest.cpp
test/encrypted_frame_unittest.cpp
test/hiddenservice_unittest.cpp
test/pq_unittest.cpp
)
@ -423,14 +420,10 @@ else()
if(WITH_TESTS)
enable_testing()
add_subdirectory(${GTEST_DIR})
include_directories(${GTEST_DIR}/include ${GTEST_DIR})
add_executable(${TEST_EXE} ${TEST_SRC})
add_test(runAllTests ${TEST_EXE})
target_link_libraries(${TEST_EXE} ${STATIC_LINK_LIBS} gtest_main ${STATIC_LIB})
endif()
add_subdirectory(${GTEST_DIR})
include_directories(${GTEST_DIR}/include ${GTEST_DIR})
add_executable(${TEST_EXE} ${TEST_SRC})
target_link_libraries(${TEST_EXE} ${STATIC_LINK_LIBS} gtest_main ${STATIC_LIB})
if(WITH_STATIC)
add_library(${STATIC_LIB} STATIC ${LIB_SRC})

View File

@ -1,5 +1,5 @@
all: debug
all: test
SIGN = gpg --sign --detach
@ -27,6 +27,7 @@ TESTNET_CONF=$(TESTNET_ROOT)/supervisor.conf
TESTNET_LOG=$(TESTNET_ROOT)/testnet.log
EXE = $(REPO)/lokinet
TEST_EXE = $(REPO)/testAll
TESTNET_EXE=$(REPO)/lokinet-testnet
TESTNET_CLIENTS ?= 50
@ -42,18 +43,16 @@ clean:
rm -f *.a *.so
debug-configure:
cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DWITH_TESTS=ON -DCMAKE_C_COMPILER=$(CC) -DCMAKE_CXX_COMPILER=$(CXX) -DTUNTAP=ON
cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=$(CC) -DCMAKE_CXX_COMPILER=$(CXX) -DTUNTAP=ON
release-configure: clean
cmake -GNinja -DSTATIC_LINK=ON -DCMAKE_BUILD_TYPE=Release -DRELEASE_MOTTO="$(shell cat motto.txt)" -DCMAKE_C_COMPILER=$(CC) -DCMAKE_CXX_COMPILER=$(CXX) -DTUNTAP=ON
debug: debug-configure
ninja
ninja test
release-compile: release-configure
ninja
cp llarpd lokinet
strip $(TARGETS)
$(TARGETS): release-compile
@ -101,9 +100,8 @@ testnet:
python3 contrib/testnet/genconf.py --bin=$(TESTNET_EXE) --svc=$(TESTNET_SERVERS) --clients=$(TESTNET_CLIENTS) --dir=$(TESTNET_ROOT) --out $(TESTNET_CONF)
LLARP_DEBUG=$(TESTNET_DEBUG) supervisord -n -d $(TESTNET_ROOT) -l $(TESTNET_LOG) -c $(TESTNET_CONF)
test: debug-configure
ninja
ninja test
test: debug
$(TEST_EXE)
format:
clang-format -i $$(find daemon llarp include | grep -E '\.[h,c](pp)?$$')

View File

@ -18,6 +18,12 @@ int crypto_kem_keypair(unsigned char *pk, unsigned char * sk);
#define crypto_kem_SECRETKEYBYTES 1600
#define crypto_kem_PUBLICKEYBYTES 1218
#define crypto_kem_CIPHERTEXTBYTES 1047
#define NTRU_SECRETKEYBYTES CRYPTO_SECRETKEYBYTES
#define NTRU_PUBLICKEYBYTES CRYPTO_PUBLICKEYBYTES
#define NTRU_CIPHERTEXTBYTES CRYPTO_CIPHERTEXTBYTES
#define CRYPTO_BYTES 32
#ifdef __cplusplus

View File

@ -1,9 +1,4 @@
#define NTRU_SECRETKEYBYTES CRYPTO_SECRETKEYBYTES
#define NTRU_PUBLICKEYBYTES CRYPTO_PUBLICKEYBYTES
#define NTRU_CIPHERTEXTBYTES CRYPTO_CIPHERTEXTBYTES
int crypto_kem_enc_ref(unsigned char *cstr, unsigned char *k, const unsigned char *pk);
int crypto_kem_dec_ref(unsigned char *k, const unsigned char *cstr, const unsigned char *sk);

View File

@ -13,7 +13,7 @@
#error "crypto_kem_SECRETKEYBYTES must match rq_encode_len + 2 * small_encode_len"
#endif
int crypto_kem_keypair_ax2(unsigned char *pk,unsigned char *sk)
int crypto_kem_keypair_avx2(unsigned char *pk,unsigned char *sk)
{
small g[768];
small grecip[768];

View File

@ -1,4 +1,4 @@
#ifdef __AVX2__
#if __AVX2__
#include <string.h>
#include <immintrin.h>
#include "mod3.h"

View File

@ -1,4 +1,4 @@
#ifdef __AVX2__
#if __AVX2__
#include <immintrin.h>
#include "params.h"
#include "mod3.h"

View File

@ -1,4 +1,4 @@
#ifdef __AVX2__
#if __AVX2__
#include <immintrin.h>
#include "params.h"
#include "crypto_uint32.h"

View File

@ -1,4 +1,4 @@
#ifdef __AVX2__
#if __AVX2__
#include <immintrin.h>
#include "mod3.h"
#include "rq.h"

View File

@ -1,4 +1,4 @@
#ifdef __AVX2__
#if __AVX2__
#include <immintrin.h>
#include "params.h"
#include "swap.h"

View File

@ -1,4 +1,4 @@
#ifdef __AVX__
#if __AVX2__
#include <immintrin.h>
#include "params.h"
#include "rq.h"

View File

@ -1,4 +1,4 @@
#ifdef __AVX__
#if __AVX2__
#include <immintrin.h>
#include "params.h"
#include "crypto_uint32.h"

View File

@ -1,4 +1,4 @@
#ifdef __AVX2__
#if __AVX2__
#include <immintrin.h>
#include "swap.h"

View File

@ -1,4 +1,4 @@
#ifdef __AVX2__
#if __AVX2__
#include <immintrin.h>
#include "params.h"
#include "r3.h"

View File

@ -2,7 +2,7 @@
#include <stdbool.h>
#if defined(__x86_64__) || defined(__i386__)
#if __AVX__
#include <cpuid.h>
static bool supports_avx2()
@ -36,16 +36,19 @@ void ntru_init()
{
if(supports_avx2())
{
__crypto_kem_dec = crypto_kem_dec_avx2;
__crypto_kem_enc = crypto_kem_enc_avx2;
__crypto_kem_dec = crypto_kem_dec_avx2;
__crypto_kem_dec = &crypto_kem_dec_avx2;
__crypto_kem_enc = &crypto_kem_enc_avx2;
__crypto_kem_dec = &crypto_kem_dec_avx2;
__crypto_kem_keypair = &crypto_kem_keypair_avx2;
}
else
{
__crypto_kem_dec = crypto_kem_dec_ref;
__crypto_kem_enc = crypto_kem_enc_ref;
__crypto_kem_dec = crypto_kem_dec_ref;
__crypto_kem_dec = &crypto_kem_dec_ref;
__crypto_kem_enc = &crypto_kem_enc_ref;
__crypto_kem_dec = &crypto_kem_dec_ref;
__crypto_kem_keypair = &crypto_kem_keypair_ref;
}
}

View File

@ -319,7 +319,7 @@ main(int argc, char *argv[])
llarp_findOrCreateIdentity(&crypt, ident_keyfile.string().c_str(),
identity);
// get identity public key
uint8_t *pubkey = llarp::seckey_topublic(identity);
const uint8_t *pubkey = llarp::seckey_topublic(identity);
llarp_rc_set_pubsigkey(&rc, pubkey);
llarp_rc_sign(&crypt, identity, &rc);

View File

@ -9,19 +9,18 @@ MDS(x, k) is 256 bit blake2b hmac of x with secret value k
SE(k, n, x) is chacha20 encrypt data x using symettric key k and nounce n
SD(k, n, x) is chacha20 dectypt data x using symettric key k and nounce n
S(k, x) is sign x with ed25519 using secret key k
EDKG() is generate ec keypair (p, s) public key p (32 bytes), secret key s (643 bytes)
EDKG() is generate ec keypair (p, s) public key p (32 bytes), secret key s (64 bytes)
V(k, x, sig) is verify x data using signature sig using public key k
EDDH(a, b) is curve25519 scalar multiplication of a and b
HKE(a, b, x) is hashed key exchange between a and b using a secret key x HS(a + b + EDDH(x, b))
TKE(a, b, sk, n) is a transport shared secret kdf using MDS(n, HKE(a, b, sk))
TKE(a, b, x, n) is a transport shared secret kdf using MDS(n, HKE(a, b, x))
when A is client and B is server where n is a 32 bytes shared random
client computes TKE(A.pk, B.pk, A.sk, n)
server computes TKE(A.pk, B.pk, B.sk, n)
PDH(a, b, x) is path shared secret generation HS(a + b + curve41417_scalar_mult(x, b))
PDH(a, b, x) is path shared secret generation HS(a + b + EDDH(x, b))
PKE(a, b, x, n) is a path shared secret kdf using MDS(n, PDH(a, b, x))
@ -34,4 +33,7 @@ S_a is equal to S_b
RAND(n) is n random bytes
PQKG() is generate a sntrup4591761 key pair (sk, pk)
PQKE_A(pk) is alice generating (x, k) where x is sntrup4591761 ciphertext block and k is the session key
PQKE_B(x, sk) is bob calculating k where x is sntrup4591761 ciphertext block, sk is bob's sntrup4591761 secretkey and k is the session key

View File

@ -176,8 +176,9 @@ x is the timestamp seconds since epoch that this introduction expires at
introduction set (IS)
a signed set of introductions for a hidden service
a is the service info
a is the service info of the publisher
i is the list of introductions that this service is advertising with
k is the public key to use when doing encryption to this hidden service
n is a 16 byte null padded utf-8 encoded string tagging the hidden service in
a topic searchable via a lookup (optional)
v is the protocol version
@ -188,6 +189,7 @@ service's signing key.
{
a: SI,
i: [ I, I, I, ... ],
k: "<1218 bytes sntrup4591761 public key block>",
n: "<16 bytes service topic (optional)>",
v: 0,
w: optional proof of work,
@ -528,54 +530,6 @@ B is set to a backoff value.
R contains additional metadata text describing why the exit was rejected.
hidden service frame (HSF)
TODO: document this better
intro message (variant 1)
start a new session
{
A: "H",
D: "<N bytes encrypted HSD>",
H: "<32 bytes ephemeral public encryption key>",
N: "<32 bytes nonce for key exchange>",
S: 0,
V: 0,
Z: "<64 bytes signature of entire message using sender's signing key>"
}
D is encrypted with session key K which is derived by
K = PKE(H, SI.enckey, N)
ordered data message (variant 2)
{
A: "H",
D: "<N bytes encrypted HSD>",
N: "<32 bytes nonce for symettric cipher>",
S: sequence_number_uint64,
T: "<16 bytes converstation tag>",
V: 0,
Z: "<64 bytes signature using sender's signing key>"
}
hidden service data (HSD)
data sent anonymously over the network to a recipiant from a sender.
sent inside a HSFM encrypted with a shared secret.
{
a: protocol_number_uint,
d: "<N bytes payload>",
i: Introduction for reply,
s: SI of sender,
t: "<16 bytes converstation tag present only in message 0>",
v: 0
}
transfer data fragment message (TDFM)
transfer data between paths.
@ -592,6 +546,132 @@ transfer data to another path with id P on the local router place a random 32 by
into y and z values into a LRDM message (respectively) and send it in the
downstream direction.
hidden service data (HSD)
data sent anonymously over the network to a recipiant from a sender.
sent inside a HSFM encrypted with a shared secret.
{
a: protocol_number_uint,
d: "<N bytes payload>",
i: Introduction for reply,
n: uint_message_sequence_number,
o: N seconds until this converstation plans terminate,
s: SI of sender,
t: "<16 bytes converstation tag present only when n is 0>",
v: 0
}
hidden service frame (HSF)
TODO: document this better
intro message (variant 1)
start a new session
{
A: "H",
C: "<1048 bytes ciphertext block>",
D: "<N bytes encrypted HSD>",
N: "<32 bytes nonce for key exchange>",
V: 0,
Z: "<64 bytes signature of entire message using sender's signing key>"
}
alice (A) wants to talk to bob (B) over the network, both have hidden services
set up and are online on the network.
A and B are both SI.
A_sk is alice's private signing key.
for alice (A) to send the string "beep" to bob (B), alice picks an introduction
to use on one of her paths (I_A) such that I_A is aligning with one of bobs's
paths (I_B)
alice generates:
T = RAND(16)
m = {
a: 0,
d: "beep",
i: I_A,
n: 0,
s: A,
t: T,
v: 0
}
X = BE(m)
C, K = PQKE_A(I_B.k)
N = RAND(32)
D = SE(X, K, N)
M = {
A: "T",
P: I_B.P,
S: uint64_sequence_number,
T: {
A: "H",
C: C,
D: D,
N: N,
V: 0,
Z: "\x00" * 32
},
V: 0
}
Z = S(A_sk, BE(M))
alice transmits a TDFM to router with public key I_B.K via her path that ends
with router with public key I_B.k
{
A: "T",
P: I_B.P,
S: uint64_sequence_number,
T: {
A: "H",
C: C,
D: D,
N: N,
V: 0,
Z: Z
},
V: 0
}
the shared secret (S) for further message encryption is:
S = HS(K + PKE(A, B, sk, N))
given sk is the local secret encryption key used by the current hidden service
please note:
signature verification can only be done after decryption
TODO: explain bob's side too (it's invsere of alice's process)
data from a previously made session (variant 2)
transfer data on a session previously made
{
A: "H",
D: "<N bytes encrypted HSD>",
N: "<32 bytes nonce for symettric cipher>",
T: "<16 bytes converstation tag>",
V: 0,
Z: "<64 bytes signature using sender's signing key>"
}
transfer ip traffic message (TITM)
transfer ip traffic for exit

View File

@ -11,12 +11,10 @@
namespace llarp
{
/// aligned buffer, sz must be multiple of 8 bytes
/// aligned buffer, aligns to the nears 8 bytes
template < size_t sz, bool randomize = false >
struct AlignedBuffer
{
static_assert(sz % 8 == 0, "aligned buffer size is not a multiple of 8");
AlignedBuffer()
{
if(randomize)
@ -212,7 +210,7 @@ namespace llarp
protected:
union {
byte_t b[sz];
uint64_t l[sz / 8];
uint64_t l[(sz / 8) + (sz % 8)];
};
};

View File

@ -2,8 +2,9 @@
#define LLARP_BENCODE_HPP
#include <llarp/bencode.h>
#include <llarp/buffer.hpp>
#include <llarp/logger.hpp>
#include <llarp/mem.hpp>
#include <set>
namespace llarp
@ -197,6 +198,16 @@ namespace llarp
r->buffer);
return true;
}
template < size_t bufsz, size_t align = 128 >
void
Dump() const
{
byte_t tmp[bufsz] = {0};
auto buf = llarp::StackBuffer< decltype(tmp) >(tmp);
if(BEncode(&buf))
llarp::DumpBuffer< decltype(buf), align >(buf);
}
};
} // namespace llarp

44
include/llarp/buffer.hpp Normal file
View File

@ -0,0 +1,44 @@
#ifndef LLARP_BUFFER_HPP
#define LLARP_BUFFER_HPP
#include <llarp/buffer.h>
namespace llarp
{
template < typename T >
llarp_buffer_t
StackBuffer(T& stack)
{
llarp_buffer_t buff;
buff.base = &stack[0];
buff.cur = buff.base;
buff.sz = sizeof(stack);
return buff;
}
/** initialize llarp_buffer_t from container */
template < typename T >
llarp_buffer_t
Buffer(T& t)
{
llarp_buffer_t buff;
buff.base = &t[0];
buff.cur = buff.base;
buff.sz = t.size();
return buff;
}
template < typename T >
llarp_buffer_t
ConstBuffer(const T& t)
{
llarp_buffer_t buff;
buff.base = (byte_t*)&t[0];
buff.cur = buff.base;
buff.sz = t.size();
return buff;
}
} // namespace llarp
#endif

View File

@ -24,26 +24,22 @@
#define HMACSIZE 32
#define PATHIDSIZE 16
/*
typedef byte_t llarp_pubkey_t[PUBKEYSIZE];
typedef byte_t llarp_seckey_t[SECKEYSIZE];
typedef byte_t llarp_nonce_t[NONCESIZE];
typedef byte_t llarp_sharedkey_t[SHAREDKEYSIZE];
typedef byte_t llarp_hash_t[HASHSIZE];
typedef byte_t llarp_shorthash_t[SHORTHASHSIZE];
typedef byte_t llarp_hmac_t[HMACSIZE];
typedef byte_t llarp_hmacsec_t[HMACSECSIZE];
typedef byte_t llarp_sig_t[SIGSIZE];
typedef byte_t llarp_tunnel_nonce_t[TUNNONCESIZE];
*/
#include <libntrup/ntru.h>
#define PQ_CIPHERTEXTSIZE crypto_kem_CIPHERTEXTBYTES
#define PQ_PUBKEYSIZE crypto_kem_PUBLICKEYBYTES
#define PQ_SECRETKEYSIZE crypto_kem_SECRETKEYBYTES
#define PQ_KEYPAIRSIZE (PQ_SECRETKEYSIZE + PQ_SECRETKEYSIZE)
/// label functors
/// PKE(result, publickey, secretkey, nonce)
typedef bool (*llarp_path_dh_func)(byte_t *, byte_t *, byte_t *, byte_t *);
typedef bool (*llarp_path_dh_func)(byte_t *, const byte_t *, const byte_t *,
const byte_t *);
/// TKE(result, publickey, secretkey, nonce)
typedef bool (*llarp_transport_dh_func)(byte_t *, byte_t *, byte_t *, byte_t *);
typedef bool (*llarp_transport_dh_func)(byte_t *, const byte_t *,
const byte_t *, const byte_t *);
/// SD/SE(buffer, key, nonce)
typedef bool (*llarp_sym_cipher_func)(llarp_buffer_t, const byte_t *,
@ -96,6 +92,12 @@ struct llarp_crypto
void (*identity_keygen)(byte_t *);
/// generate encryption keypair
void (*encryption_keygen)(byte_t *);
/// generate post quantum encrytion key
void (*pqe_keygen)(byte_t *);
/// post quantum decrypt (buffer, sharedkey_dst, sec)
bool (*pqe_decrypt)(const byte_t *, byte_t *, const byte_t *);
/// post quantum encrypt (buffer, sharedkey_dst, pub)
bool (*pqe_encrypt)(byte_t *, byte_t *, const byte_t *);
};
/// set crypto function pointers to use libsodium

View File

@ -11,36 +11,26 @@ namespace llarp
const byte_t*
seckey_topublic(const byte_t* secret);
byte_t*
seckey_topublic(byte_t* secret);
const byte_t*
pq_keypair_to_public(const byte_t* keypair);
typedef AlignedBuffer< 32 > SharedSecret;
const byte_t*
pq_keypair_to_secret(const byte_t* keypair);
typedef AlignedBuffer< SHAREDKEYSIZE > SharedSecret;
typedef AlignedBuffer< 32 > KeyExchangeNonce;
typedef AlignedBuffer< PUBKEYSIZE > PubKey;
struct PubKeyHash
{
std::size_t
operator()(PubKey const& a) const noexcept
{
size_t sz = 0;
memcpy(&sz, a.data(), sizeof(size_t));
return sz;
}
};
typedef AlignedBuffer< SECKEYSIZE > SecretKey;
typedef AlignedBuffer< SHORTHASHSIZE > ShortHash;
typedef AlignedBuffer< SIGSIZE > Signature;
typedef AlignedBuffer< TUNNONCESIZE > TunnelNonce;
typedef AlignedBuffer< 24 > SymmNonce;
typedef AlignedBuffer< NONCESIZE > SymmNonce;
typedef AlignedBuffer< 32 > SymmKey;
typedef AlignedBuffer< PQ_CIPHERTEXTSIZE + 1 > PQCipherBlock;
typedef AlignedBuffer< PQ_PUBKEYSIZE > PQPubKey;
typedef AlignedBuffer< PQ_KEYPAIRSIZE > PQKeyPair;
} // namespace llarp
#endif

View File

@ -22,7 +22,7 @@ namespace llarp
{
}
EncryptedFrame(byte_t* buf, size_t sz) : Encrypted(buf, sz)
EncryptedFrame(const byte_t* buf, size_t sz) : Encrypted(buf, sz)
{
}
EncryptedFrame(size_t sz)
@ -42,10 +42,11 @@ namespace llarp
}
bool
DecryptInPlace(byte_t* seckey, llarp_crypto* crypto);
DecryptInPlace(const byte_t* seckey, llarp_crypto* crypto);
bool
EncryptInPlace(byte_t* seckey, byte_t* other, llarp_crypto* crypto);
EncryptInPlace(const byte_t* seckey, const byte_t* other,
llarp_crypto* crypto);
};
/// TOOD: can only handle 1 frame at a time
@ -116,7 +117,8 @@ namespace llarp
ctx->result(nullptr, ctx->context);
}
AsyncFrameDecrypter(llarp_crypto* c, byte_t* secretkey, DecryptHandler h)
AsyncFrameDecrypter(llarp_crypto* c, const byte_t* secretkey,
DecryptHandler h)
: result(h), crypto(c), seckey(secretkey)
{
}
@ -124,7 +126,7 @@ namespace llarp
DecryptHandler result;
User* context;
llarp_crypto* crypto;
byte_t* seckey;
const byte_t* seckey;
EncryptedFrame* target;
void

View File

@ -112,7 +112,7 @@ struct llarp_link
void
RemoveSession(llarp_link_session *s);
uint8_t *
const uint8_t *
pubkey();
bool

60
include/llarp/mem.hpp Normal file
View File

@ -0,0 +1,60 @@
#ifndef LLARP_MEM_HPP
#define LLARP_MEM_HPP
#include <llarp/buffer.h>
#include <llarp/mem.h>
#include <cctype>
#include <cstdio>
namespace llarp
{
void
Zero(void *ptr, size_t sz);
template < typename T >
void
dumphex(const uint8_t *t)
{
size_t idx = 0;
while(idx < sizeof(T))
{
printf("%.2x ", t[idx++]);
if(idx % 8 == 0)
printf("\n");
}
}
template < typename T, size_t align = 128 >
void
DumpBuffer(const T &buff)
{
size_t idx = 0;
printf("buffer of size %zu\n", buff.sz);
while(idx < buff.sz)
{
if(buff.base + idx == buff.cur)
{
printf("%c[1;31m", 27);
}
if(std::isprint(buff.base[idx]))
{
printf("%c", buff.base[idx]);
}
else
{
printf("X");
}
if(buff.base + idx == buff.cur)
{
printf("%c[0;0m", 27);
}
++idx;
if(idx % align == 0)
printf("\n");
}
printf("\n");
fflush(stdout);
}
} // namespace llarp
#endif

View File

@ -15,6 +15,7 @@ namespace llarp
{
llarp::SecretKey enckey;
llarp::SecretKey signkey;
llarp::PQKeyPair pq;
uint64_t version = 0;
VanityNonce vanity;
@ -37,11 +38,18 @@ namespace llarp
bool
EnsureKeys(const std::string& fpath, llarp_crypto* c);
bool
KeyExchange(llarp_path_dh_func dh, byte_t* sharedkey,
const ServiceInfo& other, const byte_t* N) const;
bool
DecodeKey(llarp_buffer_t key, llarp_buffer_t* buf);
bool
SignIntroSet(IntroSet& i, llarp_crypto* c) const;
bool
Sign(llarp_crypto*, byte_t* sig, llarp_buffer_t buf) const;
};
} // namespace service
} // namespace llarp

View File

@ -49,8 +49,8 @@ namespace llarp
return crypto->verify(signkey, payload, sig);
}
byte_t*
EncryptionPublicKey()
const byte_t*
EncryptionPublicKey() const
{
return enckey;
}

View File

@ -15,12 +15,13 @@ namespace llarp
{
namespace service
{
constexpr std::size_t MAX_INTROSET_SIZE = 1024;
constexpr std::size_t MAX_INTROSET_SIZE = 4096;
struct IntroSet : public llarp::IBEncodeMessage
{
ServiceInfo A;
std::vector< Introduction > I;
PQPubKey K;
Tag topic;
llarp::PoW* W = nullptr;
llarp::Signature Z;
@ -31,6 +32,7 @@ namespace llarp
{
A = std::move(other.A);
I = std::move(other.I);
K = std::move(other.K);
version = std::move(other.version);
topic = std::move(other.topic);
W = std::move(other.W);
@ -41,6 +43,7 @@ namespace llarp
{
A = other.A;
I = other.I;
K = other.K;
version = other.version;
topic = other.topic;
if(other.W)
@ -55,6 +58,7 @@ namespace llarp
{
A = other.A;
I = other.I;
K = other.K;
version = other.version;
topic = other.topic;
if(W)
@ -80,6 +84,7 @@ namespace llarp
out << intro << ",";
}
out << "]";
out << "K=" << i.K;
auto topic = i.topic.ToString();
if(topic.size())
{

View File

@ -97,10 +97,10 @@ namespace llarp
void
EnsureRouterIsKnown(const RouterID& router);
Identity*
const Identity&
GetIdentity()
{
return &m_Identity;
return m_Identity;
}
void
@ -194,7 +194,7 @@ namespace llarp
bool
GetCachedSessionKeyFor(const ConvoTag& remote,
SharedSecret& secret) const;
const byte_t*& secret) const;
void
PutCachedSessionKeyFor(const ConvoTag& remote,
const SharedSecret& secret);

View File

@ -17,7 +17,7 @@ namespace llarp
virtual bool
GetCachedSessionKeyFor(const ConvoTag& remote,
SharedSecret& secret) const = 0;
const byte_t*& secret) const = 0;
virtual void
PutCachedSessionKeyFor(const ConvoTag& remote,
const SharedSecret& secret) = 0;

View File

@ -5,6 +5,7 @@
#include <llarp/crypto.hpp>
#include <llarp/encrypted.hpp>
#include <llarp/routing/message.hpp>
#include <llarp/service/Identity.hpp>
#include <llarp/service/Info.hpp>
#include <llarp/service/Intro.hpp>
#include <llarp/service/handler.hpp>
@ -51,8 +52,8 @@ namespace llarp
/// outer message
struct ProtocolFrame : public llarp::routing::IMessage
{
llarp::PQCipherBlock C;
llarp::Encrypted D;
llarp::PubKey H;
llarp::KeyExchangeNonce N;
llarp::Signature Z;
llarp::service::ConvoTag T;
@ -63,17 +64,18 @@ namespace llarp
~ProtocolFrame();
bool
EncryptAndSign(llarp_crypto* c, const ProtocolMessage* msg,
byte_t* sharedkey, byte_t* signingkey);
EncryptAndSign(llarp_crypto* c, const ProtocolMessage& msg,
const byte_t* sharedkey, const Identity& localIdent);
bool
AsyncDecryptAndVerify(llarp_logic* logic, llarp_crypto* c,
llarp_threadpool* worker, byte_t* localSecret,
llarp_threadpool* worker,
const Identity& localIdent,
IDataHandler* handler) const;
bool
DecryptPayloadInto(llarp_crypto* c, byte_t* sharedkey,
ProtocolMessage* into) const;
DecryptPayloadInto(llarp_crypto* c, const byte_t* sharedkey,
ProtocolMessage& into) const;
bool
DecodeKey(llarp_buffer_t key, llarp_buffer_t* val);

View File

@ -1,44 +1 @@
#ifndef LLARP_BUFFER_HPP
#define LLARP_BUFFER_HPP
#include <llarp/buffer.h>
namespace llarp
{
template < typename T >
llarp_buffer_t
StackBuffer(T& stack)
{
llarp_buffer_t buff;
buff.base = &stack[0];
buff.cur = buff.base;
buff.sz = sizeof(stack);
return buff;
}
/** initialize llarp_buffer_t from container */
template < typename T >
llarp_buffer_t
Buffer(T& t)
{
llarp_buffer_t buff;
buff.base = &t[0];
buff.cur = buff.base;
buff.sz = t.size();
return buff;
}
template < typename T >
llarp_buffer_t
ConstBuffer(const T& t)
{
llarp_buffer_t buff;
buff.base = (byte_t*)&t[0];
buff.cur = buff.base;
buff.sz = t.size();
return buff;
}
} // namespace llarp
#endif
#include <llarp/buffer.hpp>

View File

@ -17,8 +17,8 @@ namespace llarp
}
static bool
dh(uint8_t *out, uint8_t *client_pk, uint8_t *server_pk, uint8_t *themPub,
uint8_t *usSec)
dh(uint8_t *out, const uint8_t *client_pk, const uint8_t *server_pk,
const uint8_t *themPub, const uint8_t *usSec)
{
llarp::SharedSecret shared;
crypto_generichash_state h;
@ -35,7 +35,8 @@ namespace llarp
}
static bool
dh_client(uint8_t *shared, uint8_t *pk, uint8_t *sk, uint8_t *n)
dh_client(uint8_t *shared, const uint8_t *pk, const uint8_t *sk,
const uint8_t *n)
{
llarp::SharedSecret dh_result;
if(dh(dh_result, llarp::seckey_topublic(sk), pk, pk, sk))
@ -46,7 +47,8 @@ namespace llarp
}
static bool
dh_server(uint8_t *shared, uint8_t *pk, uint8_t *sk, uint8_t *n)
dh_server(uint8_t *shared, const uint8_t *pk, const uint8_t *sk,
const uint8_t *n)
{
llarp::SharedSecret dh_result;
if(dh(dh_result, pk, llarp::seckey_topublic(sk), pk, sk))
@ -124,24 +126,46 @@ namespace llarp
return sec + 32;
}
byte_t *
seckey_topublic(byte_t *sec)
namespace pq
{
return sec + 32;
bool
encrypt(byte_t *ciphertext, byte_t *sharedkey, const byte_t *pubkey)
{
return crypto_kem_enc(ciphertext, sharedkey, pubkey) != -1;
}
bool
decrypt(const byte_t *ciphertext, byte_t *sharedkey,
const byte_t *secretkey)
{
return crypto_kem_dec(sharedkey, ciphertext, secretkey) != -1;
}
void
keygen(byte_t *keypair)
{
crypto_kem_keypair(keypair + PQ_SECRETKEYSIZE, keypair);
}
} // namespace pq
const byte_t *
pq_keypair_to_public(const byte_t *k)
{
return k + PQ_SECRETKEYSIZE;
}
const byte_t *
pq_keypair_to_secret(const byte_t *k)
{
return k;
}
} // namespace llarp
const byte_t *
llarp_seckey_topublic(const byte_t *secret)
{
return secret + 32;
}
void
llarp_crypto_libsodium_init(struct llarp_crypto *c)
{
assert(sodium_init() != -1);
ntru_init();
c->xchacha20 = llarp::sodium::xchacha20;
c->dh_client = llarp::sodium::dh_client;
c->dh_server = llarp::sodium::dh_server;
@ -156,6 +180,9 @@ llarp_crypto_libsodium_init(struct llarp_crypto *c)
c->randbytes = llarp::sodium::randbytes;
c->identity_keygen = llarp::sodium::sigkeygen;
c->encryption_keygen = llarp::sodium::enckeygen;
c->pqe_encrypt = llarp::pq::encrypt;
c->pqe_decrypt = llarp::pq::decrypt;
c->pqe_keygen = llarp::pq::keygen;
int seed;
c->randbytes(&seed, sizeof(seed));
srand(seed);

View File

@ -35,7 +35,8 @@ namespace llarp
}
bool
EncryptedFrame::EncryptInPlace(byte_t* ourSecretKey, byte_t* otherPubkey,
EncryptedFrame::EncryptInPlace(const byte_t* ourSecretKey,
const byte_t* otherPubkey,
llarp_crypto* crypto)
{
// format of frame is
@ -93,7 +94,8 @@ namespace llarp
}
bool
EncryptedFrame::DecryptInPlace(byte_t* ourSecretKey, llarp_crypto* crypto)
EncryptedFrame::DecryptInPlace(const byte_t* ourSecretKey,
llarp_crypto* crypto)
{
if(size() <= size_t(EncryptedFrame::OverheadSize))
{

View File

@ -225,7 +225,7 @@ llarp_link::RemoveSession(llarp_link_session* s)
delete s;
}
uint8_t*
const uint8_t*
llarp_link::pubkey()
{
return llarp::seckey_topublic(seckey);

View File

@ -1,60 +1 @@
#ifndef LLARP_MEM_HPP
#define LLARP_MEM_HPP
#include <llarp/buffer.h>
#include <llarp/mem.h>
#include <cctype>
#include <cstdio>
namespace llarp
{
void
Zero(void *ptr, size_t sz);
template < typename T >
void
dumphex(const uint8_t *t)
{
size_t idx = 0;
while(idx < sizeof(T))
{
printf("%.2x ", t[idx++]);
if(idx % 8 == 0)
printf("\n");
}
}
template < typename T, size_t align = 128 >
void
DumpBuffer(const T &buff)
{
size_t idx = 0;
printf("buffer of size %zu\n", buff.sz);
while(idx < buff.sz)
{
if(buff.base + idx == buff.cur)
{
printf("%c[1;31m", 27);
}
if(std::isprint(buff.base[idx]))
{
printf("%c", buff.base[idx]);
}
else
{
printf("X");
}
if(buff.base + idx == buff.cur)
{
printf("%c[0;0m", 27);
}
++idx;
if(idx % align == 0)
printf("\n");
}
printf("\n");
fflush(stdout);
}
} // namespace llarp
#endif
#include <llarp/mem.hpp>

View File

@ -29,6 +29,8 @@ namespace llarp
{
return BEncodeReadList(I, buf);
}
if(!BEncodeMaybeReadDictEntry("k", K, read, key, buf))
return false;
if(!BEncodeMaybeReadDictEntry("n", topic, read, key, buf))
return false;
@ -64,6 +66,10 @@ namespace llarp
return false;
// end introduction list
// pq pubkey
if(!BEncodeWriteDictEntry("k", K, buf))
return false;
// topic tag
if(topic.ToString().size())
{
@ -165,6 +171,8 @@ namespace llarp
return false;
if(!BEncodeWriteDictEntry("e", enckey, buf))
return false;
if(!BEncodeWriteDictEntry("q", pq, buf))
return false;
if(!BEncodeWriteDictEntry("s", signkey, buf))
return false;
if(!BEncodeWriteDictInt("v", version, buf))
@ -180,6 +188,8 @@ namespace llarp
bool read = false;
if(!BEncodeMaybeReadDictEntry("e", enckey, read, key, buf))
return false;
if(!BEncodeMaybeReadDictEntry("q", pq, read, key, buf))
return false;
if(!BEncodeMaybeReadDictEntry("s", signkey, read, key, buf))
return false;
if(!BEncodeMaybeReadDictInt("v", version, read, key, buf))
@ -196,12 +206,26 @@ namespace llarp
crypto->identity_keygen(signkey);
pub.Update(llarp::seckey_topublic(enckey),
llarp::seckey_topublic(signkey));
crypto->pqe_keygen(pq);
}
bool
Identity::KeyExchange(llarp_path_dh_func dh, byte_t* result,
const ServiceInfo& other, const byte_t* N) const
{
return dh(result, other.EncryptionPublicKey(), enckey, N);
}
bool
Identity::Sign(llarp_crypto* c, byte_t* sig, llarp_buffer_t buf) const
{
return c->sign(sig, signkey, buf);
}
bool
Identity::EnsureKeys(const std::string& fname, llarp_crypto* c)
{
byte_t tmp[256];
byte_t tmp[4096];
auto buf = llarp::StackBuffer< decltype(tmp) >(tmp);
std::error_code ec;
// check for file
@ -244,7 +268,10 @@ namespace llarp
{
if(i.I.size() == 0)
return false;
// set service info
i.A = pub;
// set public encryption key
i.K = pq_keypair_to_public(pq);
// zero out signature for signing process
i.Z.Zero();
byte_t tmp[MAX_INTROSET_SIZE];
@ -254,7 +281,7 @@ namespace llarp
// rewind and resize buffer
buf.sz = buf.cur - buf.base;
buf.cur = buf.base;
return crypto->sign(i.Z, signkey, buf);
return Sign(crypto, i.Z, buf);
}
bool

View File

@ -344,12 +344,12 @@ namespace llarp
bool
Endpoint::GetCachedSessionKeyFor(const ConvoTag& tag,
SharedSecret& secret) const
const byte_t*& secret) const
{
auto itr = m_Sessions.find(tag);
if(itr == m_Sessions.end())
return false;
secret = itr->second.sharedKey;
secret = itr->second.sharedKey.data();
return true;
}
@ -626,7 +626,7 @@ namespace llarp
Endpoint::HandleHiddenServiceFrame(const ProtocolFrame* frame)
{
return frame->AsyncDecryptAndVerify(EndpointLogic(), Crypto(), Worker(),
m_Identity.enckey, m_DataHandler);
m_Identity, m_DataHandler);
}
void
@ -760,15 +760,16 @@ namespace llarp
llarp_crypto* crypto;
byte_t* sharedKey;
ServiceInfo remote;
Identity* m_LocalIdentity;
const Identity& m_LocalIdentity;
ProtocolMessage msg;
ProtocolFrame frame;
Introduction intro;
PQPubKey introPubKey;
std::function< void(ProtocolFrame&) > hook;
IDataHandler* handler;
AsyncIntroGen(llarp_logic* l, llarp_crypto* c, byte_t* key,
const ServiceInfo& r, Identity* localident,
const ServiceInfo& r, const Identity& localident,
const Introduction& us, IDataHandler* h)
: logic(l)
, crypto(c)
@ -796,28 +797,34 @@ namespace llarp
Work(void* user)
{
AsyncIntroGen* self = static_cast< AsyncIntroGen* >(user);
// derive ntru session key component
SharedSecret K;
self->crypto->pqe_encrypt(self->frame.C, K, self->introPubKey);
// randomize Nounce
self->frame.N.Randomize();
// ephemeral public key
SecretKey ephem;
self->crypto->encryption_keygen(ephem);
self->frame.H = llarp::seckey_topublic(ephem);
// compure post handshake session key
byte_t tmp[64];
// K
memcpy(tmp, K, 32);
// PKE (A, B, N)
if(!self->m_LocalIdentity.KeyExchange(self->crypto->dh_client, tmp + 64,
self->remote, self->frame.N))
llarp::LogError("failed to derive x25519 shared key component");
// H (K + PKE(A, B, N))
self->crypto->shorthash(self->sharedKey,
llarp::StackBuffer< decltype(tmp) >(tmp));
// randomize tag
self->msg.tag.Randomize();
// set sender
self->msg.sender = self->m_LocalIdentity->pub;
self->msg.sender = self->m_LocalIdentity.pub;
// set our introduction
self->msg.introReply = self->intro;
// derive session key
self->crypto->dh_server(self->sharedKey,
self->remote.EncryptionPublicKey(), ephem,
self->frame.N);
// encrypt and sign
self->frame.EncryptAndSign(self->crypto, &self->msg, self->sharedKey,
self->m_LocalIdentity->signkey);
// inform result
llarp_logic_queue_job(self->logic, {self, &Result});
if(self->frame.EncryptAndSign(self->crypto, self->msg, K,
self->m_LocalIdentity))
llarp_logic_queue_job(self->logic, {self, &Result});
else
llarp::LogError("failed to encrypt and sign");
}
};
@ -955,8 +962,8 @@ namespace llarp
llarp::LogError("no open converstations with remote endpoint?");
return;
}
auto crypto = m_Parent->Crypto();
SharedSecret shared;
auto crypto = m_Parent->Crypto();
const byte_t* shared = nullptr;
routing::PathTransferMessage msg;
ProtocolFrame& f = msg.T;
f.N.Randomize();
@ -970,8 +977,7 @@ namespace llarp
m.sender = m_Parent->m_Identity.pub;
m.PutBuffer(payload);
if(!f.EncryptAndSign(crypto, &m, shared,
m_Parent->m_Identity.signkey))
if(!f.EncryptAndSign(crypto, m, shared, m_Parent->m_Identity))
{
llarp::LogError("failed to sign");
return;

View File

@ -95,20 +95,20 @@ namespace llarp
{
if(!bencode_start_dict(buf))
return false;
if(!BEncodeWriteDictMsgType(buf, "A", "H"))
return false;
if(!BEncodeWriteDictEntry("D", D, buf))
return false;
if(S == 0)
if(!C.IsZero())
{
if(!BEncodeWriteDictEntry("H", H, buf))
if(!BEncodeWriteDictEntry("C", C, buf))
return false;
}
if(!BEncodeWriteDictEntry("D", D, buf))
return false;
if(!BEncodeWriteDictEntry("N", N, buf))
return false;
if(!BEncodeWriteDictInt("S", S, buf))
return false;
if(S == 0)
if(!T.IsZero())
{
if(!BEncodeWriteDictEntry("T", T, buf))
return false;
@ -135,7 +135,7 @@ namespace llarp
}
if(!BEncodeMaybeReadDictEntry("D", D, read, key, val))
return false;
if(!BEncodeMaybeReadDictEntry("H", H, read, key, val))
if(!BEncodeMaybeReadDictEntry("C", C, read, key, val))
return false;
if(!BEncodeMaybeReadDictEntry("N", N, read, key, val))
return false;
@ -152,23 +152,25 @@ namespace llarp
}
bool
ProtocolFrame::DecryptPayloadInto(llarp_crypto* crypto, byte_t* sharedkey,
ProtocolMessage* msg) const
ProtocolFrame::DecryptPayloadInto(llarp_crypto* crypto,
const byte_t* sharedkey,
ProtocolMessage& msg) const
{
auto buf = D.Buffer();
crypto->xchacha20(buf, sharedkey, N);
msg->PutBuffer(buf);
msg.PutBuffer(buf);
return true;
}
bool
ProtocolFrame::EncryptAndSign(llarp_crypto* crypto,
const ProtocolMessage* msg,
byte_t* sessionKey, byte_t* signingkey)
const ProtocolMessage& msg,
const byte_t* sessionKey,
const Identity& localIdent)
{
// put payload and encrypt
D = llarp::ConstBuffer(msg->payload);
memcpy(D.data(), msg->payload.data(), D.size());
D = llarp::ConstBuffer(msg.payload);
memcpy(D.data(), msg.payload.data(), D.size());
auto dbuf = D.Buffer();
crypto->xchacha20(*dbuf, sessionKey, N);
// zero out signature
@ -182,7 +184,7 @@ namespace llarp
buf.sz = buf.cur - buf.base;
buf.cur = buf.base;
// sign
return crypto->sign(Z, signingkey, buf);
return localIdent.Sign(crypto, Z, buf);
}
struct AsyncFrameDH
@ -190,24 +192,22 @@ namespace llarp
llarp_crypto* crypto;
llarp_logic* logic;
ProtocolMessage* msg;
byte_t* localSecret;
PubKey H;
KeyExchangeNonce N;
const Identity& m_LocalIdentity;
PQPubKey introPubKey;
IDataHandler* handler;
Address remote;
Encrypted D;
const ProtocolFrame* frame;
AsyncFrameDH(llarp_logic* l, llarp_crypto* c, byte_t* sec,
IDataHandler* h, ProtocolMessage* m,
const ProtocolFrame* frame)
AsyncFrameDH(llarp_logic* l, llarp_crypto* c, const Identity& localIdent,
IDataHandler* h, ProtocolMessage* m, const ProtocolFrame* f)
: crypto(c)
, logic(l)
, msg(m)
, localSecret(sec)
, H(frame->H)
, N(frame->N)
, m_LocalIdentity(localIdent)
, handler(h)
, D(frame->D)
, D(f->D)
, frame(f)
{
}
@ -216,18 +216,20 @@ namespace llarp
{
AsyncFrameDH* self = static_cast< AsyncFrameDH* >(user);
auto crypto = self->crypto;
SharedSecret shared;
if(!crypto->dh_client(shared, self->H, self->localSecret, self->N))
SharedSecret K;
SharedSecret sharedKey;
// copy
ProtocolFrame frame = *self->frame;
if(!crypto->pqe_decrypt(self->frame->C, K,
pq_keypair_to_secret(self->m_LocalIdentity.pq)))
{
llarp::LogError(
"Failed to derive shared secret for initial message H=", self->H,
" N=", self->N);
llarp::LogError("pqke failed");
delete self->msg;
delete self;
return;
}
auto buf = self->D.Buffer();
crypto->xchacha20(*buf, shared, self->N);
crypto->xchacha20(*buf, K, self->frame->N);
if(!self->msg->BDecode(buf))
{
llarp::LogError("failed to decode inner protocol message");
@ -236,9 +238,35 @@ namespace llarp
delete self;
return;
}
// verify signature of outer message after we parsed the inner message
if(!frame.Verify(crypto, self->msg->sender))
{
llarp::LogError("intro frame has invalid signature");
frame.Dump< MAX_PROTOCOL_MESSAGE_SIZE >();
delete self->msg;
delete self;
return;
}
byte_t tmp[64];
// K
memcpy(tmp, K, 32);
// PKE (A, B, N)
if(!self->m_LocalIdentity.KeyExchange(
crypto->dh_server, tmp + 32, self->msg->sender, self->frame->N))
{
llarp::LogError("x25519 key exchange failed");
frame.Dump< MAX_PROTOCOL_MESSAGE_SIZE >();
delete self->msg;
delete self;
return;
}
// S = HS( K + PKE( A, B, N))
crypto->shorthash(sharedKey, StackBuffer< decltype(tmp) >(tmp));
self->handler->PutIntroFor(self->msg->tag, self->msg->introReply);
self->handler->PutSenderFor(self->msg->tag, self->msg->sender);
self->handler->PutCachedSessionKeyFor(self->msg->tag, shared);
self->handler->PutCachedSessionKeyFor(self->msg->tag, sharedKey);
self->msg->handler = self->handler;
llarp_logic_queue_job(self->logic,
{self->msg, &ProtocolMessage::ProcessAsync});
@ -247,22 +275,20 @@ namespace llarp
};
bool
ProtocolFrame::AsyncDecryptAndVerify(llarp_logic* logic,
llarp_crypto* crypto,
ProtocolFrame::AsyncDecryptAndVerify(llarp_logic* logic, llarp_crypto* c,
llarp_threadpool* worker,
byte_t* localSecret,
const Identity& localIdent,
IDataHandler* handler) const
{
if(S == 0)
{
ProtocolMessage* msg = new ProtocolMessage();
// we need to dh
auto dh =
new AsyncFrameDH(logic, crypto, localSecret, handler, msg, this);
auto dh = new AsyncFrameDH(logic, c, localIdent, handler, msg, this);
llarp_threadpool_queue_job(worker, {dh, &AsyncFrameDH::Work});
return true;
}
SharedSecret shared;
const byte_t* shared = nullptr;
if(!handler->GetCachedSessionKeyFor(T, shared))
{
llarp::LogError("No cached session for T=", T);
@ -274,13 +300,13 @@ namespace llarp
llarp::LogError("No sender for T=", T);
return false;
}
if(!Verify(crypto, si))
if(!Verify(c, si))
{
llarp::LogError("Signature failure");
return false;
}
ProtocolMessage* msg = new ProtocolMessage();
if(!DecryptPayloadInto(crypto, shared, msg))
if(!DecryptPayloadInto(c, shared, *msg))
{
llarp::LogError("failed to decrypt message");
delete msg;
@ -297,7 +323,7 @@ namespace llarp
}
ProtocolFrame::ProtocolFrame(const ProtocolFrame& other)
: D(other.D), H(other.H), N(other.N), Z(other.Z), T(other.T)
: C(other.C), D(other.D), N(other.N), Z(other.Z), T(other.T)
{
}

40
test/pq_unittest.cpp Normal file
View File

@ -0,0 +1,40 @@
#include <gtest/gtest.h>
#include <llarp/crypto.hpp>
#include <iostream>
namespace llarp
{
struct PQCryptoTest : public ::testing::Test
{
llarp_crypto crypto;
PQKeyPair keys;
PQCryptoTest()
{
llarp_crypto_libsodium_init(&crypto);
}
llarp_crypto * Crypto()
{
return &crypto;
}
void SetUp()
{
crypto.pqe_keygen(keys);
}
};
TEST_F(PQCryptoTest, TestCrypto)
{
PQCipherBlock block;
SharedSecret shared, otherShared;
auto c = Crypto();
ASSERT_TRUE(keys.size() == PQ_KEYPAIRSIZE);
ASSERT_TRUE(c->pqe_encrypt(block, shared, pq_keypair_to_public(keys)));
ASSERT_TRUE(c->pqe_decrypt(block, otherShared, pq_keypair_to_secret(keys)));
ASSERT_TRUE(otherShared == shared);
}
}