You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lokinet/llarp/crypto/crypto_libsodium.cpp

472 lines
14 KiB
C++

#include <crypto/crypto_libsodium.hpp>
6 years ago
#include <sodium/crypto_generichash.h>
#include <sodium/crypto_sign.h>
#include <sodium/crypto_scalarmult.h>
4 years ago
#include <sodium/crypto_scalarmult_ed25519.h>
#include <sodium/crypto_scalarmult_ristretto255.h>
#include <sodium/crypto_stream_xchacha20.h>
#include <sodium/crypto_core_ed25519.h>
4 years ago
#include <sodium/crypto_core_ristretto255.h>
#include <sodium/randombytes.h>
#include <sodium/utils.h>
#include <util/mem.hpp>
#include <util/endian.hpp>
#include <cassert>
#include <cstring>
7 years ago
6 years ago
extern "C"
{
extern int
sodium_init(void);
}
namespace llarp
{
namespace sodium
{
static bool
dh(llarp::SharedSecret &out, const PubKey &client_pk,
const PubKey &server_pk, const uint8_t *themPub, const SecretKey &usSec)
{
llarp::SharedSecret shared;
crypto_generichash_state h;
if(crypto_scalarmult_curve25519(shared.data(), usSec.data(), themPub))
{
return false;
}
crypto_generichash_blake2b_init(&h, nullptr, 0U, shared.size());
crypto_generichash_blake2b_update(&h, client_pk.data(), 32);
crypto_generichash_blake2b_update(&h, server_pk.data(), 32);
crypto_generichash_blake2b_update(&h, shared.data(), 32);
crypto_generichash_blake2b_final(&h, out.data(), shared.size());
return true;
}
7 years ago
static bool
dh_client_priv(llarp::SharedSecret &shared, const PubKey &pk,
const SecretKey &sk, const TunnelNonce &n)
{
llarp::SharedSecret dh_result;
6 years ago
if(dh(dh_result, sk.toPublic(), pk, pk.data(), sk))
{
return crypto_generichash_blake2b(shared.data(), 32, n.data(), 32,
dh_result.data(), 32)
!= -1;
}
6 years ago
llarp::LogWarn("crypto::dh_client - dh failed");
return false;
}
6 years ago
static bool
dh_server_priv(llarp::SharedSecret &shared, const PubKey &pk,
const SecretKey &sk, const TunnelNonce &n)
{
llarp::SharedSecret dh_result;
if(dh(dh_result, pk, sk.toPublic(), pk.data(), sk))
{
return crypto_generichash_blake2b(shared.data(), 32, n.data(), 32,
dh_result.data(), 32)
!= -1;
}
6 years ago
llarp::LogWarn("crypto::dh_server - dh failed");
return false;
}
6 years ago
CryptoLibSodium::CryptoLibSodium()
{
if(sodium_init() == -1)
{
throw std::runtime_error("sodium_init() returned -1");
}
char *avx2 = std::getenv("AVX2_FORCE_DISABLE");
if(avx2 && std::string(avx2) == "1")
{
ntru_init(1);
}
else
{
ntru_init(0);
}
int seed = 0;
randombytes(reinterpret_cast< unsigned char * >(&seed), sizeof(seed));
srand(seed);
}
bool
CryptoLibSodium::xchacha20(const llarp_buffer_t &buff,
const SharedSecret &k, const TunnelNonce &n)
{
return crypto_stream_xchacha20_xor(buff.base, buff.base, buff.sz,
n.data(), k.data())
== 0;
}
bool
CryptoLibSodium::xchacha20_alt(const llarp_buffer_t &out,
const llarp_buffer_t &in,
const SharedSecret &k, const byte_t *n)
{
if(in.sz > out.sz)
return false;
return crypto_stream_xchacha20_xor(out.base, in.base, in.sz, n, k.data())
== 0;
}
bool
CryptoLibSodium::dh_client(llarp::SharedSecret &shared, const PubKey &pk,
const SecretKey &sk, const TunnelNonce &n)
{
return dh_client_priv(shared, pk, sk, n);
}
/// path dh relay side
bool
CryptoLibSodium::dh_server(llarp::SharedSecret &shared, const PubKey &pk,
const SecretKey &sk, const TunnelNonce &n)
{
return dh_server_priv(shared, pk, sk, n);
}
/// transport dh client side
bool
CryptoLibSodium::transport_dh_client(llarp::SharedSecret &shared,
const PubKey &pk, const SecretKey &sk,
const TunnelNonce &n)
{
return dh_client_priv(shared, pk, sk, n);
}
/// transport dh server side
bool
CryptoLibSodium::transport_dh_server(llarp::SharedSecret &shared,
const PubKey &pk, const SecretKey &sk,
const TunnelNonce &n)
{
return dh_server_priv(shared, pk, sk, n);
}
bool
CryptoLibSodium::shorthash(ShortHash &result, const llarp_buffer_t &buff)
{
return crypto_generichash_blake2b(result.data(), ShortHash::SIZE,
buff.base, buff.sz, nullptr, 0)
!= -1;
}
7 years ago
bool
CryptoLibSodium::hmac(byte_t *result, const llarp_buffer_t &buff,
const SharedSecret &secret)
{
return crypto_generichash_blake2b(result, HMACSIZE, buff.base, buff.sz,
secret.data(), HMACSECSIZE)
!= -1;
}
7 years ago
static bool
hash(uint8_t *result, const llarp_buffer_t &buff)
{
return crypto_generichash_blake2b(result, HASHSIZE, buff.base, buff.sz,
nullptr, 0)
!= -1;
}
bool
CryptoLibSodium::sign(Signature &sig, const SecretKey &secret,
const llarp_buffer_t &buf)
{
return crypto_sign_detached(sig.data(), nullptr, buf.base, buf.sz,
secret.data())
!= -1;
}
7 years ago
bool
CryptoLibSodium::sign(Signature &sig, const PrivateKey& privkey,
const llarp_buffer_t &buf)
{
PubKey pubkey;
privkey.toPublic(pubkey);
crypto_hash_sha512_state hs;
unsigned char nonce[64];
unsigned char hram[64];
unsigned char mulres[32];
// r = H(s || M) where here s is pseudorandom bytes typically generated as part of hashing the
// seed (i.e. [a,s] = H(k)), but for derived PrivateKeys will come from a hash of the root
// key's s concatenated with the derivation hash.
crypto_hash_sha512_init(&hs);
crypto_hash_sha512_update(&hs, privkey.data() + 32, 32);
crypto_hash_sha512_update(&hs, buf.base, buf.sz);
crypto_hash_sha512_final(&hs, nonce);
crypto_core_ed25519_scalar_reduce(nonce, nonce);
// copy pubkey into sig to make (for now) sig = (R || A)
memmove(sig.data() + 32, pubkey.data(), 32);
// R = r * B
crypto_scalarmult_ed25519_base_noclamp(sig.data(), nonce);
// hram = H(R || A || M)
crypto_hash_sha512_init(&hs);
crypto_hash_sha512_update(&hs, sig.data(), 64);
crypto_hash_sha512_update(&hs, buf.base, buf.sz);
crypto_hash_sha512_final(&hs, hram);
// S = r + H(R || A || M) * s, so sig = (R || S)
crypto_core_ed25519_scalar_reduce(hram, hram);
crypto_core_ed25519_scalar_mul(mulres, hram, privkey.data());
crypto_core_ed25519_scalar_add(sig.data() + 32, mulres, nonce);
sodium_memzero(nonce, sizeof nonce);
return true;
}
bool
CryptoLibSodium::verify(const PubKey &pub, const llarp_buffer_t &buf,
const Signature &sig)
{
return crypto_sign_verify_detached(sig.data(), buf.base, buf.sz,
pub.data())
!= -1;
}
6 years ago
4 years ago
/// clamp a 32 byte ec point
static void
clamp_ed25519(byte_t *out)
{
out[0] &= 248;
out[31] &= 127;
out[31] |= 64;
}
template < typename K >
static K
clamp(const K &p)
{
K out = p;
clamp_ed25519(out);
return out;
}
template < typename K >
static bool
is_clamped(const K &key)
{
K other(key);
clamp_ed25519(other.data());
return other == key;
}
constexpr static char derived_key_hash_str[161] = "just imagine what would happen if we all decided to understand. you can't in the and by be or then before so just face it this text hurts to read? lokinet yolo!";
template < typename K >
static bool
make_scalar(AlignedBuffer<32>& out, const K &k, uint64_t i)
{
// b = BLIND-STRING || k || i
std::array< byte_t, 160 + K::SIZE + sizeof(uint64_t) > buf;
std::copy(derived_key_hash_str, derived_key_hash_str + 160, buf.begin());
std::copy(k.begin(), k.end(), buf.begin() + 160);
htole64buf(buf.data() + 160 + K::SIZE, i);
// n = H(b)
// h = make_point(n)
ShortHash n;
return -1 != crypto_generichash_blake2b(n.data(), ShortHash::SIZE,
buf.data(), buf.size(), nullptr, 0)
&& -1 != crypto_core_ed25519_from_uniform(out.data(), n.data());
}
4 years ago
static AlignedBuffer< 32 > zero;
bool
CryptoLibSodium::derive_subkey(PubKey &out_pubkey, const PubKey &root_pubkey,
uint64_t key_n, const AlignedBuffer<32>* hash)
{
// scalar h = H( BLIND-STRING || root_pubkey || key_n )
AlignedBuffer< 32 > h;
if (hash)
h = *hash;
else if(not make_scalar(h, root_pubkey, key_n))
{
LogError("cannot make scalar");
return false;
}
return 0 == crypto_scalarmult_ed25519(out_pubkey.data(), h.data(), root_pubkey.data());
}
bool
CryptoLibSodium::derive_subkey_private(PrivateKey &out_key,
const SecretKey &root_key,
uint64_t key_n,
const AlignedBuffer<32>* hash
)
{
// Derives a private subkey from a root key.
//
// The basic idea is:
//
// h = H( BLIND-STRING || A || key_n )
// a - private key
// A = aB - public key
// s - signing hash
// a' = ah - derived private key
// A' = a'B = (ah)B - derived public key
// s' = H(h || s) - derived signing hash
//
// libsodium throws some wrenches in the mechanics which are a nuisance, the biggest of which
// is that sodium's secret key is *not* `a`; rather it is the seed. If you want to get the
// private key (i.e. "a"), you need to SHA-512 hash it and then clamp that.
//
// This also makes signature verification harder: we can't just use sodium's sign function
// because it wants to be given the seed rather than the private key, and moreover we can't
// actually *get* the seed to make libsodium happy because we only have `ah` above; thus we
// reimplemented most of sodium's detached signing function but without the hash step.
//
// Lastly, for the signing hash s', we need some value that is both different from the root s
// but also unknowable from the public key (since otherwise `r` in the signing function would
// be known), so we generate it from a hash of `h` and the root key's (psuedorandom) signing
// hash, `s`.
//
const auto root_pubkey = root_key.toPublic();
AlignedBuffer< 32 > h;
if (hash)
h = *hash;
else if(not make_scalar(h, root_pubkey, key_n))
4 years ago
{
LogError("cannot make scalar");
return false;
}
h[0] &= 248;
h[31] &= 63;
h[31] |= 64;
PrivateKey a;
if (!root_key.toPrivate(a))
return false;
// a' = ha
crypto_core_ed25519_scalar_mul(out_key.data(), h.data(), a.data());
// s' = H(h || s)
std::array<byte_t, 64> buf;
std::copy(h.begin(), h.end(), buf.begin());
std::copy(a.begin() + 32, a.end(), buf.begin() + 32);
return -1 != crypto_generichash_blake2b(out_key.data() + 32, 32,
buf.data(), buf.size(), nullptr, 0);
4 years ago
return true;
}
bool
CryptoLibSodium::seed_to_secretkey(llarp::SecretKey &secret,
const llarp::IdentitySecret &seed)
{
4 years ago
return crypto_sign_ed25519_seed_keypair(secret.data() + 32, secret.data(),
seed.data())
!= -1;
}
void
CryptoLibSodium::randomize(const llarp_buffer_t &buff)
{
randombytes((unsigned char *)buff.base, buff.sz);
}
6 years ago
void
CryptoLibSodium::randbytes(byte_t *ptr, size_t sz)
{
randombytes((unsigned char *)ptr, sz);
}
6 years ago
void
CryptoLibSodium::identity_keygen(llarp::SecretKey &keys)
{
PubKey pk;
int result = crypto_sign_keypair(pk.data(), keys.data());
assert(result != -1);
const PubKey sk_pk = keys.toPublic();
assert(pk == sk_pk);
(void)result;
(void)sk_pk;
4 years ago
// encryption_keygen(keys);
}
bool
CryptoLibSodium::check_identity_privkey(const llarp::SecretKey &keys)
{
AlignedBuffer< crypto_sign_SEEDBYTES > seed;
llarp::PubKey pk;
llarp::SecretKey sk;
if(crypto_sign_ed25519_sk_to_seed(seed.data(), keys.data()) == -1)
return false;
if(crypto_sign_seed_keypair(pk.data(), sk.data(), seed.data()) == -1)
return false;
return keys.toPublic() == pk && sk == keys;
}
void
CryptoLibSodium::encryption_keygen(llarp::SecretKey &keys)
{
auto d = keys.data();
randbytes(d, 32);
crypto_scalarmult_curve25519_base(d + 32, d);
}
bool
CryptoLibSodium::pqe_encrypt(PQCipherBlock &ciphertext,
SharedSecret &sharedkey,
const PQPubKey &pubkey)
{
return crypto_kem_enc(ciphertext.data(), sharedkey.data(), pubkey.data())
!= -1;
}
bool
CryptoLibSodium::pqe_decrypt(const PQCipherBlock &ciphertext,
SharedSecret &sharedkey,
const byte_t *secretkey)
{
return crypto_kem_dec(sharedkey.data(), ciphertext.data(), secretkey)
!= -1;
}
void
CryptoLibSodium::pqe_keygen(PQKeyPair &keypair)
{
auto d = keypair.data();
crypto_kem_keypair(d + PQ_SECRETKEYSIZE, d);
}
} // namespace sodium
const byte_t *
seckey_topublic(const SecretKey &sec)
{
return sec.data() + 32;
}
const byte_t *
pq_keypair_to_public(const PQKeyPair &k)
{
return k.data() + PQ_SECRETKEYSIZE;
}
const byte_t *
pq_keypair_to_secret(const PQKeyPair &k)
{
return k.data();
}
7 years ago
uint64_t
randint()
{
uint64_t i;
randombytes((byte_t *)&i, sizeof(i));
return i;
}
} // namespace llarp