lokinet/llarp/link/dtls.cpp
2019-01-03 16:10:40 -05:00

315 lines
9.3 KiB
C++

#include <link/dtls_internal.hpp>
#include <crypto.hpp>
#include <router.hpp>
#include <endian.hpp>
namespace llarp
{
namespace dtls
{
const mbedtls_ecp_group_id LinkLayer::AllowedCurve[2] = {
MBEDTLS_ECP_DP_CURVE25519, MBEDTLS_ECP_DP_NONE};
const mbedtls_md_type_t LinkLayer::AllowedHash[2] = {MBEDTLS_MD_SHA256,
MBEDTLS_MD_NONE};
const int LinkLayer::CipherSuite[2] = {
MBEDTLS_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 0};
const mbedtls_x509_crt_profile LinkLayer::X509Profile = {
MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA256),
MBEDTLS_X509_ID_FLAG(MBEDTLS_PK_ECDSA),
MBEDTLS_X509_ID_FLAG(MBEDTLS_ECP_DP_CURVE25519), 0};
static int
Random(void *ctx, unsigned char *buf, size_t sz)
{
static_cast< llarp::Crypto * >(ctx)->randbytes(buf, sz);
return 0;
}
static int
WriteCookie(void *ctx, unsigned char **p, unsigned char *,
const unsigned char *info, size_t ilen)
{
Session *self = static_cast< Session * >(ctx);
if(!self->crypto->hmac(*p, llarp::InitBuffer(info, ilen),
self->m_Parent->CookieSec()))
return -1;
*p += 32;
return 0;
}
static int
VerifyCookie(void *ctx, const unsigned char *cookie, size_t clen,
const unsigned char *info, size_t ilen)
{
if(clen != 32)
return -1;
Session *self = static_cast< Session * >(ctx);
ShortHash check;
if(!self->crypto->hmac(check.data(), llarp::InitBuffer(info, ilen),
self->m_Parent->CookieSec()))
return -1;
if(memcmp(check.data(), cookie, clen) == 0)
return 0;
return -1;
}
Session::Session(LinkLayer *parent) : ILinkSession(), crypto(parent->crypto)
{
m_Parent = parent;
mbedtls_ssl_config_init(&m_config);
mbedtls_ssl_conf_transport(&m_config, MBEDTLS_SSL_TRANSPORT_DATAGRAM);
m_config.p_vrfy = this;
m_config.key_cert = &m_Parent->ourKeys;
m_config.p_cookie = this;
m_config.f_cookie_write = &WriteCookie;
m_config.f_cookie_check = &VerifyCookie;
}
Session::Session(LinkLayer *parent, const llarp::Addr &from)
: Session(parent)
{
remoteAddr = from;
m_config.f_vrfy = &InboundVerifyCert;
byte_t buf[20] = {0};
parent->crypto->randbytes(buf, sizeof(buf));
htobe16buf(buf, from.port());
memcpy(buf + 2, from.addr6()->s6_addr, 16);
mbedtls_ssl_set_client_transport_id(&m_ctx, buf, sizeof(buf));
}
Session::Session(LinkLayer *parent, const RouterContact &rc,
const AddressInfo &ai)
: Session(parent)
{
remoteRC = rc;
remoteAddr = ai;
m_config.f_vrfy = &OutboundVerifyCert;
}
Session::~Session()
{
mbedtls_ssl_session_free(&m_session);
mbedtls_ssl_free(&m_ctx);
mbedtls_ssl_config_free(&m_config);
}
void
Session::Connect()
{
mbedtls_ssl_conf_endpoint(&m_config, MBEDTLS_SSL_IS_CLIENT);
Configure();
}
void
Session::Accept()
{
mbedtls_ssl_conf_endpoint(&m_config, MBEDTLS_SSL_IS_SERVER);
Configure();
}
void
Session::Configure()
{
m_config.ciphersuite_list[0] = LinkLayer::CipherSuite;
m_config.ciphersuite_list[1] = LinkLayer::CipherSuite;
m_config.ciphersuite_list[2] = LinkLayer::CipherSuite;
m_config.ciphersuite_list[3] = LinkLayer::CipherSuite;
m_config.p_dbg = nullptr;
m_config.f_dbg = &Session::Debug;
m_config.p_rng = m_Parent->crypto;
m_config.f_rng = &Random;
const auto *conf = &m_config;
mbedtls_ssl_setup(&m_ctx, conf);
}
void
Session::Recv_ll(const void *buf, size_t sz)
{
ll_recv.emplace_back(sz);
auto &back = ll_recv.back();
memcpy(back.data(), buf, sz);
}
void
Session::PumpIO()
{
llarp_time_t now = m_Parent->Now();
if(m_ctx.state == MBEDTLS_SSL_HANDSHAKE_OVER)
{
// pump inbound acks
{
auto itr = m_Inbound.begin();
while(itr != m_Inbound.end())
{
if(!itr->second.IsExpired(now))
{
if(itr->second.IsDone())
{
m_Parent->HandleMessage(this, itr->second.msg.as_buffer());
itr = m_Inbound.erase(itr);
continue;
}
else if(itr->second.ShouldRetransmit(now))
{
itr->second.TransmitAcks(&m_ctx);
}
++itr;
}
else
itr = m_Inbound.erase(itr);
}
}
// pump outbound fragments
{
auto itr = m_Outbound.begin();
while(itr != m_Outbound.end())
{
if(itr->second.IsExpired(now) || itr->second.IsDone())
{
itr = m_Outbound.erase(itr);
continue;
}
else if(itr->second.ShouldRetransmit(now))
itr->second.TransmitUnacked(&m_ctx);
++itr;
}
}
}
else
{
/// step the handshake
int res = mbedtls_ssl_handshake_step(&m_ctx);
switch(res)
{
case MBEDTLS_ERR_SSL_WANT_READ:
case MBEDTLS_ERR_SSL_WANT_WRITE:
case MBEDTLS_ERR_SSL_ASYNC_IN_PROGRESS:
case MBEDTLS_ERR_SSL_CRYPTO_IN_PROGRESS:
break;
default:
// drop send queue
ll_send.clear();
// drop recv queue
ll_recv.clear();
// reset session
mbedtls_ssl_session_reset(&m_ctx);
return;
}
}
// low level sendto
while(ll_send.size())
{
m_Parent->SendTo_LL(remoteAddr, llarp::ConstBuffer(ll_send.front()));
ll_send.pop_front();
}
}
void
Session::Debug(void *, int, const char *fname, int lineno, const char *msg)
{
llarp::_Log(llarp::eLogInfo, fname, lineno, msg);
}
LinkLayer::LinkLayer(llarp::Crypto *c, const byte_t *encryptionSecretKey,
const byte_t *identitySecretKey,
llarp::GetRCFunc getrc, llarp::LinkMessageHandler h,
llarp::SessionEstablishedHandler established,
llarp::SessionRenegotiateHandler reneg,
llarp::TimeoutHandler timeout,
llarp::SessionClosedHandler closed)
: ILinkLayer(encryptionSecretKey, getrc, h,
std::bind(&LinkLayer::SignBuffer, this,
std::placeholders::_1, std::placeholders::_2),
established, reneg, timeout, closed)
, crypto(c)
, m_IdentityKey(identitySecretKey)
{
}
static int
Random(void *ctx, unsigned char *buf, size_t sz)
{
static_cast< llarp::Crypto * >(ctx)->randbytes(buf, sz);
return 0;
}
bool
LinkLayer::Start(llarp::Logic *l)
{
if(!ILinkLayer::Start(l))
return false;
return crypto->shrothash(m_CookieSec, m_IdentityKey.toBuffer());
}
void
LinkLayer::RecvFrom(const llarp::Addr &from, const void *buf, size_t sz)
{
auto itr = m_Pending.find(from);
if(itr == m_Pending.end())
{
itr = m_Pending.insert(std::make_pair(from, new Session(this, from)))
.first;
itr->second->Start();
}
static_cast< Session * >(itr->second.get())->Recv_ll(buf, sz);
}
ILinkSession *
LinkLayer::NewOutboundSession(const llarp::RouterContact &rc,
const llarp::AddressInfo &ai)
{
return new Session(this, rc, ai);
}
void
LinkLayer::Pump()
{
std::set< RouterID > sessions;
{
Lock l(m_AuthedLinksMutex);
auto itr = m_AuthedLinks.begin();
while(itr != m_AuthedLinks.end())
{
sessions.insert(itr->first);
++itr;
}
}
ILinkLayer::Pump();
{
Lock l(m_AuthedLinksMutex);
for(const auto &pk : sessions)
{
if(m_AuthedLinks.count(pk) == 0)
{
// all sessions were removed
SessionClosed(pk);
}
}
}
}
std::unique_ptr< ILinkLayer >
NewServerFromRouter(llarp::Router *r)
{
return std::unique_ptr< LinkLayer >(new LinkLayer(
&r->crypto, r->encryption, r->identity,
std::bind(&llarp::Router::rc, r),
std::bind(&llarp::Router::HandleRecvLinkMessageBuffer, r,
std::placeholders::_1, std::placeholders::_2),
std::bind(&llarp::Router::OnSessionEstablished, r,
std::placeholders::_1),
std::bind(&llarp::Router::CheckRenegotiateValid, r,
std::placeholders::_1, std::placeholders::_2),
std::bind(&llarp::Router::Sign, r, std::placeholders::_1,
std::placeholders::_2),
std::bind(&llarp::Router::OnConnectTimeout, r, std::placeholders::_1),
std::bind(&llarp::Router::SessionClosed, r, std::placeholders::_1)));
}
} // namespace dtls
} // namespace llarp