#include
#include
#include
namespace llarp
{
namespace iwp
{
std::array< byte_t, 6 > OuterMessage::obtain_flow_id_magic =
std::array< byte_t, 6 >{{'n', 'e', 't', 'i', 'd', '?'}};
std::array< byte_t, 6 > OuterMessage::give_flow_id_magic =
std::array< byte_t, 6 >{{'n', 'e', 't', 'i', 'd', '!'}};
OuterMessage::OuterMessage()
{
Clear();
}
OuterMessage::~OuterMessage()
{
}
void
OuterMessage::Clear()
{
command = 0;
flow.Zero();
netid.Zero();
reject.fill(0);
N.Zero();
X.Zero();
Xsize = 0;
Zsig.Zero();
Zhash.Zero();
pubkey.Zero();
magic.fill(0);
uinteger = 0;
A.reset();
}
void
OuterMessage::CreateReject(const char* msg, llarp_time_t now,
const PubKey& pk)
{
Clear();
std::copy_n(msg, std::min(strlen(msg), reject.size()), reject.begin());
uinteger = now;
pubkey = pk;
}
bool
OuterMessage::Encode(llarp_buffer_t* buf) const
{
if(buf->size_left() < 2)
return false;
*buf->cur = command;
buf->cur++;
*buf->cur = '=';
buf->cur++;
switch(command)
{
case eOCMD_ObtainFlowID:
case eOCMD_GiveFlowID:
if(!buf->write(reject.begin(), reject.end()))
return false;
if(!buf->write(give_flow_id_magic.begin(), give_flow_id_magic.end()))
return false;
if(!buf->write(flow.begin(), flow.end()))
return false;
if(!buf->write(pubkey.begin(), pubkey.end()))
return false;
return buf->write(Zsig.begin(), Zsig.end());
default:
return false;
}
}
bool
OuterMessage::Decode(llarp_buffer_t* buf)
{
static constexpr size_t header_size = 2;
if(buf->size_left() < header_size)
return false;
command = *buf->cur;
++buf->cur;
if(*buf->cur != '=')
return false;
++buf->cur;
switch(command)
{
case eOCMD_ObtainFlowID:
if(!buf->read_into(magic.begin(), magic.end()))
return false;
if(!buf->read_into(netid.begin(), netid.end()))
return false;
if(!buf->read_uint64(uinteger))
return false;
if(!buf->read_into(pubkey.begin(), pubkey.end()))
return false;
if(buf->size_left() <= Zsig.size())
return false;
Xsize = buf->size_left() - Zsig.size();
if(!buf->read_into(X.begin(), X.begin() + Xsize))
return false;
return buf->read_into(Zsig.begin(), Zsig.end());
case eOCMD_GiveFlowID:
if(!buf->read_into(magic.begin(), magic.end()))
return false;
if(!buf->read_into(flow.begin(), flow.end()))
return false;
if(!buf->read_into(pubkey.begin(), pubkey.end()))
return false;
buf->cur += buf->size_left() - Zsig.size();
return buf->read_into(Zsig.begin(), Zsig.end());
case eOCMD_Reject:
if(!buf->read_into(reject.begin(), reject.end()))
return false;
if(!buf->read_uint64(uinteger))
return false;
if(!buf->read_into(pubkey.begin(), pubkey.end()))
return false;
buf->cur += buf->size_left() - Zsig.size();
return buf->read_into(Zsig.begin(), Zsig.end());
case eOCMD_SessionNegotiate:
if(!buf->read_into(flow.begin(), flow.end()))
return false;
if(!buf->read_into(pubkey.begin(), pubkey.end()))
return false;
if(!buf->read_uint64(uinteger))
return false;
if(buf->size_left() == Zsig.size() + 32)
{
A.reset(new AlignedBuffer< 32 >());
if(!buf->read_into(A->begin(), A->end()))
return false;
}
return buf->read_into(Zsig.begin(), Zsig.end());
case eOCMD_TransmitData:
if(!buf->read_into(flow.begin(), flow.end()))
return false;
if(!buf->read_into(N.begin(), N.end()))
return false;
if(buf->size_left() <= Zhash.size())
return false;
Xsize = buf->size_left() - Zhash.size();
if(!buf->read_into(X.begin(), X.begin() + Xsize))
return false;
return buf->read_into(Zhash.begin(), Zhash.end());
default:
return false;
}
}
LinkLayer::LinkLayer(Crypto* c, const SecretKey& enckey, GetRCFunc getrc,
LinkMessageHandler h, SessionEstablishedHandler est,
SessionRenegotiateHandler reneg, SignBufferFunc sign,
TimeoutHandler t, SessionClosedHandler closed)
: ILinkLayer(enckey, getrc, h, sign, est, reneg, t, closed), crypto(c)
{
m_FlowCookie.Randomize();
}
LinkLayer::~LinkLayer()
{
}
void
LinkLayer::Pump()
{
ILinkLayer::Pump();
}
const char*
LinkLayer::Name() const
{
return "iwp";
}
bool
LinkLayer::KeyGen(SecretKey& k)
{
k.Zero();
crypto->encryption_keygen(k);
return !k.IsZero();
}
uint16_t
LinkLayer::Rank() const
{
return 2;
}
bool
LinkLayer::Start(Logic* l)
{
if(!ILinkLayer::Start(l))
return false;
/// TODO: change me to true when done
return false;
}
void
LinkLayer::RecvFrom(const Addr& from, const void* pkt, size_t sz)
{
m_OuterMsg.Clear();
llarp_buffer_t sigbuf(pkt, sz);
llarp_buffer_t decodebuf(pkt, sz);
if(!m_OuterMsg.Decode(&decodebuf))
{
LogError("failed to decode outer message");
return;
}
NetID ourNetID;
switch(m_OuterMsg.command)
{
case eOCMD_ObtainFlowID:
sigbuf.sz -= m_OuterMsg.Zsig.size();
if(!crypto->verify(m_OuterMsg.pubkey, sigbuf, m_OuterMsg.Zsig))
{
LogError("failed to verify signature on '",
(char)m_OuterMsg.command, "' message from ", from);
return;
}
if(!ShouldSendFlowID(from))
{
SendReject(from, "no flo 4u :^)");
return;
}
if(m_OuterMsg.netid == ourNetID)
{
if(GenFlowIDFor(m_OuterMsg.pubkey, from, m_OuterMsg.flow))
SendFlowID(from, m_OuterMsg.flow);
else
SendReject(from, "genflow fail");
}
else
SendReject(from, "bad netid");
}
}
ILinkSession*
LinkLayer::NewOutboundSession(const RouterContact& rc,
const AddressInfo& ai)
{
(void)rc;
(void)ai;
// TODO: implement me
return nullptr;
}
void
LinkLayer::SendFlowID(const Addr& to, const FlowID_t& flow)
{
// TODO: implement me
(void)to;
(void)flow;
}
bool
LinkLayer::VerifyFlowID(const PubKey& pk, const Addr& from,
const FlowID_t& flow) const
{
FlowID_t expected;
if(!GenFlowIDFor(pk, from, expected))
return false;
return expected == flow;
}
bool
LinkLayer::GenFlowIDFor(const PubKey& pk, const Addr& from,
FlowID_t& flow) const
{
std::array< byte_t, 128 > tmp = {{0}};
if(inet_ntop(AF_INET6, from.addr6(), (char*)tmp.data(), tmp.size())
== nullptr)
return false;
std::copy_n(pk.begin(), pk.size(), tmp.begin() + 64);
std::copy_n(m_FlowCookie.begin(), m_FlowCookie.size(),
tmp.begin() + 64 + pk.size());
llarp_buffer_t buf(tmp);
ShortHash h;
if(!crypto->shorthash(h, buf))
return false;
std::copy_n(h.begin(), flow.size(), flow.begin());
return true;
}
bool
LinkLayer::ShouldSendFlowID(const Addr& to) const
{
(void)to;
// TODO: implement me
return false;
}
void
LinkLayer::SendReject(const Addr& to, const char* msg)
{
if(strlen(msg) > 14)
{
throw std::logic_error("reject message too big");
}
std::array< byte_t, 120 > pkt;
auto now = Now();
PubKey pk = GetOurRC().pubkey;
OuterMessage m;
m.CreateReject(msg, now, pk);
llarp_buffer_t encodebuf(pkt);
if(!m.Encode(&encodebuf))
{
LogError("failed to encode reject message to ", to);
return;
}
llarp_buffer_t signbuf(pkt.data(), pkt.size() - m.Zsig.size());
if(!Sign(m.Zsig, signbuf))
{
LogError("failed to sign reject messsage to ", to);
return;
}
std::copy_n(m.Zsig.begin(), m.Zsig.size(),
pkt.begin() + (pkt.size() - m.Zsig.size()));
llarp_buffer_t pktbuf(pkt);
SendTo_LL(to, pktbuf);
}
std::unique_ptr< ILinkLayer >
NewServerFromRouter(AbstractRouter* r)
{
using namespace std::placeholders;
return NewServer(
r->crypto(), r->encryption(), std::bind(&AbstractRouter::rc, r),
std::bind(&AbstractRouter::HandleRecvLinkMessageBuffer, r, _1, _2),
std::bind(&AbstractRouter::OnSessionEstablished, r, _1),
std::bind(&AbstractRouter::CheckRenegotiateValid, r, _1, _2),
std::bind(&AbstractRouter::Sign, r, _1, _2),
std::bind(&AbstractRouter::OnConnectTimeout, r, _1),
std::bind(&AbstractRouter::SessionClosed, r, _1));
}
std::unique_ptr< ILinkLayer >
NewServer(Crypto* c, const SecretKey& enckey, GetRCFunc getrc,
LinkMessageHandler h, SessionEstablishedHandler est,
SessionRenegotiateHandler reneg, SignBufferFunc sign,
TimeoutHandler t, SessionClosedHandler closed)
{
(void)c;
(void)enckey;
(void)getrc;
(void)h;
(void)est;
(void)reneg;
(void)sign;
(void)t;
(void)closed;
// TODO: implement me
return nullptr;
}
} // namespace iwp
} // namespace llarp