mirror of
https://github.com/oxen-io/lokinet
synced 2023-12-14 06:53:00 +01:00
base32 encoding and unit tests
This commit is contained in:
parent
21a2ffdeaf
commit
6cb31ecb1a
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -60,6 +60,7 @@
|
|||
"valarray": "cpp",
|
||||
"*.ipp": "cpp",
|
||||
"csignal": "cpp",
|
||||
"future": "cpp"
|
||||
"future": "cpp",
|
||||
"map": "cpp"
|
||||
}
|
||||
}
|
|
@ -201,8 +201,10 @@ set(LIB_SRC
|
|||
set(TEST_SRC
|
||||
test/main.cpp
|
||||
test/api_unittest.cpp
|
||||
test/base32_unittest.cpp
|
||||
test/dht_unittest.cpp
|
||||
test/encrypted_frame_unittest.cpp
|
||||
test/hiddenservice_unittest.cpp
|
||||
)
|
||||
set(TEST_EXE testAll)
|
||||
set(GTEST_DIR test/gtest)
|
||||
|
|
|
@ -153,6 +153,8 @@ the "network address" of a hidden service, which is computed as the blake2b
|
|||
|
||||
HS(BE(SI))
|
||||
|
||||
when in string form it's encoded with z-base32 and uses the .loki tld
|
||||
|
||||
introduction (I)
|
||||
|
||||
a descriptor annoucing a path to a hidden service
|
||||
|
|
|
@ -1,10 +1,112 @@
|
|||
#ifndef LLARP_ENCODE_HPP
|
||||
#define LLARP_ENCODE_HPP
|
||||
#include <stdint.h>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
// from https://en.wikipedia.org/wiki/Base32#z-base-32
|
||||
static const char zbase32_alpha[] = {'y', 'b', 'n', 'd', 'r', 'f', 'g', '8',
|
||||
'e', 'j', 'k', 'm', 'c', 'p', 'q', 'x',
|
||||
'o', 't', '1', 'u', 'w', 'i', 's', 'z',
|
||||
'a', '3', '4', '5', 'h', '7', '6', '9'};
|
||||
|
||||
static const std::unordered_map< char, uint8_t > zbase32_reverse_alpha = {
|
||||
{'y', 0}, {'b', 1}, {'n', 2}, {'d', 3}, {'r', 4}, {'f', 5},
|
||||
{'g', 6}, {'8', 7}, {'e', 8}, {'j', 9}, {'k', 10}, {'m', 11},
|
||||
{'c', 12}, {'p', 13}, {'q', 14}, {'x', 15}, {'o', 16}, {'t', 17},
|
||||
{'1', 18}, {'u', 19}, {'w', 20}, {'i', 21}, {'s', 22}, {'z', 23},
|
||||
{'a', 24}, {'3', 25}, {'4', 26}, {'5', 27}, {'h', 28}, {'7', 29},
|
||||
{'6', 30}, {'9', 31}};
|
||||
|
||||
template < int a, int b >
|
||||
static size_t
|
||||
DecodeSize(size_t sz)
|
||||
{
|
||||
auto d = div(sz, a);
|
||||
if(d.rem)
|
||||
d.quot++;
|
||||
return b * d.quot;
|
||||
}
|
||||
|
||||
template < typename Stack, typename V >
|
||||
bool
|
||||
Base32Decode(const Stack& stack, V& value)
|
||||
{
|
||||
int tmp = 0, bits = 0;
|
||||
size_t ret = 0;
|
||||
size_t len = DecodeSize< 5, 8 >(value.size());
|
||||
size_t outLen = value.size();
|
||||
for(size_t i = 0; i < len; i++)
|
||||
{
|
||||
char ch = stack[i];
|
||||
if(ch)
|
||||
{
|
||||
auto itr = zbase32_reverse_alpha.find(ch);
|
||||
if(itr == zbase32_reverse_alpha.end())
|
||||
return false;
|
||||
ch = itr->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ret == outLen;
|
||||
}
|
||||
tmp |= ch;
|
||||
bits += 5;
|
||||
if(bits >= 8)
|
||||
{
|
||||
if(ret >= outLen)
|
||||
return false;
|
||||
value[ret] = tmp >> (bits - 8);
|
||||
bits -= 8;
|
||||
ret++;
|
||||
}
|
||||
tmp <<= 5;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// adapted from i2pd
|
||||
template < typename V, typename Stack >
|
||||
const char*
|
||||
Base32Encode(const V& value, Stack& stack)
|
||||
{
|
||||
size_t ret = 0, pos = 1;
|
||||
int bits = 8, tmp = value[0];
|
||||
size_t len = value.size();
|
||||
while(ret < sizeof(stack) && (bits > 0 || pos < len))
|
||||
{
|
||||
if(bits < 5)
|
||||
{
|
||||
if(pos < len)
|
||||
{
|
||||
tmp <<= 8;
|
||||
tmp |= value[pos] & 0xFF;
|
||||
pos++;
|
||||
bits += 8;
|
||||
}
|
||||
else // last byte
|
||||
{
|
||||
tmp <<= (5 - bits);
|
||||
bits = 5;
|
||||
}
|
||||
}
|
||||
|
||||
bits -= 5;
|
||||
int ind = (tmp >> bits) & 0x1F;
|
||||
if(ret < sizeof(stack))
|
||||
{
|
||||
stack[ret] = zbase32_alpha[ind];
|
||||
ret++;
|
||||
}
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
return &stack[0];
|
||||
}
|
||||
|
||||
/// encode V as hex to stack
|
||||
/// null terminate
|
||||
/// return pointer to base of stack buffer on success otherwise returns
|
||||
|
@ -30,8 +132,9 @@ namespace llarp
|
|||
|
||||
int
|
||||
char2int(char input);
|
||||
|
||||
void
|
||||
HexDecode(const char* src, uint8_t* target);
|
||||
}
|
||||
} // namespace llarp
|
||||
|
||||
#endif
|
||||
|
|
|
@ -4,16 +4,27 @@
|
|||
#include <llarp/bencode.hpp>
|
||||
#include <llarp/crypto.hpp>
|
||||
#include <llarp/path_types.hpp>
|
||||
#include <llarp/pow.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
namespace service
|
||||
{
|
||||
constexpr std::size_t MAX_INTROSET_SIZE = 1024;
|
||||
|
||||
// forward declare
|
||||
struct IntroSet;
|
||||
|
||||
/// hidden service address
|
||||
typedef llarp::AlignedBuffer< 32 > Address;
|
||||
|
||||
std::string
|
||||
AddressToString(const Address& addr);
|
||||
|
||||
typedef llarp::AlignedBuffer< 16 > VanityNonce;
|
||||
|
||||
struct ServiceInfo : public llarp::IBEncodeMessage
|
||||
|
@ -23,10 +34,29 @@ namespace llarp
|
|||
uint64_t version = 0;
|
||||
VanityNonce vanity;
|
||||
|
||||
ServiceInfo();
|
||||
|
||||
~ServiceInfo();
|
||||
|
||||
ServiceInfo&
|
||||
operator=(const ServiceInfo& other)
|
||||
{
|
||||
enckey = other.enckey;
|
||||
signkey = other.signkey;
|
||||
version = other.version;
|
||||
vanity = other.vanity;
|
||||
return *this;
|
||||
};
|
||||
|
||||
friend std::ostream&
|
||||
operator<<(std::ostream& out, const ServiceInfo& i)
|
||||
{
|
||||
return out << "[e=" << i.enckey << " s=" << i.signkey
|
||||
<< " v=" << i.version << " x=" << i.vanity << "]";
|
||||
}
|
||||
|
||||
/// calculate our address
|
||||
void
|
||||
bool
|
||||
CalculateAddress(Address& addr) const;
|
||||
|
||||
bool
|
||||
|
@ -47,6 +77,8 @@ namespace llarp
|
|||
// public service info
|
||||
ServiceInfo pub;
|
||||
|
||||
~Identity();
|
||||
|
||||
// regenerate secret keys
|
||||
void
|
||||
RegenerateKeys(llarp_crypto* c);
|
||||
|
@ -60,6 +92,9 @@ namespace llarp
|
|||
|
||||
bool
|
||||
DecodeKey(llarp_buffer_t key, llarp_buffer_t* buf);
|
||||
|
||||
bool
|
||||
SignIntroSet(IntroSet& i, llarp_crypto* c) const;
|
||||
};
|
||||
|
||||
struct Introduction : public llarp::IBEncodeMessage
|
||||
|
@ -69,8 +104,13 @@ namespace llarp
|
|||
uint64_t version = 0;
|
||||
uint64_t expiresAt;
|
||||
|
||||
~Introduction()
|
||||
~Introduction();
|
||||
|
||||
friend std::ostream&
|
||||
operator<<(std::ostream& out, const Introduction& i)
|
||||
{
|
||||
return out << "k=" << i.router << " p=" << i.pathID
|
||||
<< " v=" << i.version << " x=" << i.expiresAt;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -78,16 +118,48 @@ namespace llarp
|
|||
|
||||
bool
|
||||
DecodeKey(llarp_buffer_t key, llarp_buffer_t* buf);
|
||||
|
||||
bool
|
||||
operator<(const Introduction& other) const
|
||||
{
|
||||
return expiresAt < other.expiresAt || pathID < other.pathID;
|
||||
}
|
||||
};
|
||||
|
||||
struct IntroSet : public llarp::IBEncodeMessage
|
||||
{
|
||||
ServiceInfo A;
|
||||
std::set< Introduction > I;
|
||||
uint64_t V = 0;
|
||||
llarp::PoW* W = nullptr;
|
||||
llarp::Signature Z;
|
||||
|
||||
~IntroSet();
|
||||
|
||||
IntroSet&
|
||||
operator=(const IntroSet& other)
|
||||
{
|
||||
A = other.A;
|
||||
I = other.I;
|
||||
V = other.V;
|
||||
if(W)
|
||||
delete W;
|
||||
W = other.W;
|
||||
Z = other.Z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend std::ostream&
|
||||
operator<<(std::ostream& out, const IntroSet& i)
|
||||
{
|
||||
out << "A=[" << i.A << "] I=[";
|
||||
for(const auto& intro : i.I)
|
||||
{
|
||||
out << intro << ",";
|
||||
}
|
||||
return out << "] V=" << i.V << " Z=" << i.Z;
|
||||
}
|
||||
|
||||
bool
|
||||
BDecode(llarp_buffer_t* buf);
|
||||
|
||||
|
@ -96,6 +168,9 @@ namespace llarp
|
|||
|
||||
bool
|
||||
DecodeKey(llarp_buffer_t key, llarp_buffer_t* buf);
|
||||
|
||||
bool
|
||||
VerifySignature(llarp_crypto* crypto) const;
|
||||
};
|
||||
|
||||
}; // namespace service
|
||||
|
|
|
@ -205,7 +205,12 @@ namespace llarp
|
|||
PublishIntroMessage::HandleMessage(llarp_dht_context *ctx,
|
||||
std::vector< IMessage * > &replies) const
|
||||
{
|
||||
// TODO: implement me
|
||||
auto &dht = ctx->impl;
|
||||
if(!I.VerifySignature(&dht.router->crypto))
|
||||
{
|
||||
llarp::LogWarn("invalid introset signature");
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,23 @@
|
|||
#include <llarp/service.hpp>
|
||||
#include "buffer.hpp"
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
namespace service
|
||||
{
|
||||
std::string
|
||||
AddressToString(const Address& addr)
|
||||
{
|
||||
char tmp[(1 + 32) * 2] = {0};
|
||||
std::string str = Base32Encode(addr, tmp);
|
||||
return str + ".loki";
|
||||
}
|
||||
|
||||
ServiceInfo::ServiceInfo()
|
||||
{
|
||||
vanity.Zero();
|
||||
}
|
||||
|
||||
ServiceInfo::~ServiceInfo()
|
||||
{
|
||||
}
|
||||
|
@ -42,8 +56,22 @@ namespace llarp
|
|||
return bencode_end(buf);
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceInfo::CalculateAddress(Address& addr) const
|
||||
{
|
||||
byte_t tmp[128];
|
||||
auto buf = llarp::StackBuffer< decltype(tmp) >(tmp);
|
||||
if(!BEncode(&buf))
|
||||
return false;
|
||||
return crypto_generichash(addr, addr.size(), buf.base, buf.cur - buf.base,
|
||||
nullptr, 0)
|
||||
!= -1;
|
||||
}
|
||||
|
||||
IntroSet::~IntroSet()
|
||||
{
|
||||
if(W)
|
||||
delete W;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -77,9 +105,116 @@ namespace llarp
|
|||
{
|
||||
if(!bencode_start_dict(buf))
|
||||
return false;
|
||||
if(!BEncodeWriteDictEntry("a", A, buf))
|
||||
return false;
|
||||
// start introduction list
|
||||
if(!bencode_write_bytestring(buf, "i", 1))
|
||||
return false;
|
||||
if(!BEncodeWriteList(I.begin(), I.end(), buf))
|
||||
return false;
|
||||
// end introduction list
|
||||
|
||||
// write version
|
||||
if(!BEncodeWriteDictInt(buf, "v", V))
|
||||
return false;
|
||||
if(W)
|
||||
{
|
||||
if(!BEncodeWriteDictEntry("w", *W, buf))
|
||||
return false;
|
||||
}
|
||||
if(!BEncodeWriteDictEntry("z", Z, buf))
|
||||
return false;
|
||||
|
||||
return bencode_end(buf);
|
||||
}
|
||||
|
||||
Introduction::~Introduction()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
Introduction::DecodeKey(llarp_buffer_t key, llarp_buffer_t* val)
|
||||
{
|
||||
// TODO: implement me
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
Introduction::BEncode(llarp_buffer_t* buf) const
|
||||
{
|
||||
if(!bencode_start_dict(buf))
|
||||
return false;
|
||||
if(!BEncodeWriteDictEntry("k", router, buf))
|
||||
return false;
|
||||
if(!BEncodeWriteDictEntry("p", pathID, buf))
|
||||
return false;
|
||||
if(!BEncodeWriteDictInt(buf, "v", version))
|
||||
return false;
|
||||
if(!BEncodeWriteDictInt(buf, "x", expiresAt))
|
||||
return false;
|
||||
return bencode_end(buf);
|
||||
}
|
||||
|
||||
Identity::~Identity()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
Identity::BEncode(llarp_buffer_t* buf) const
|
||||
{
|
||||
/// TODO: implement me
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
Identity::DecodeKey(llarp_buffer_t key, llarp_buffer_t* buf)
|
||||
{
|
||||
/// TODO: implement me
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
Identity::RegenerateKeys(llarp_crypto* crypto)
|
||||
{
|
||||
crypto->encryption_keygen(enckey);
|
||||
crypto->identity_keygen(signkey);
|
||||
pub.enckey = llarp::seckey_topublic(enckey);
|
||||
pub.signkey = llarp::seckey_topublic(signkey);
|
||||
}
|
||||
|
||||
bool
|
||||
Identity::SignIntroSet(IntroSet& i, llarp_crypto* crypto) const
|
||||
{
|
||||
if(i.I.size() == 0)
|
||||
return false;
|
||||
i.A = pub;
|
||||
// zero out signature for signing process
|
||||
i.Z.Zero();
|
||||
byte_t tmp[MAX_INTROSET_SIZE];
|
||||
auto buf = llarp::StackBuffer< decltype(tmp) >(tmp);
|
||||
if(!i.BEncode(&buf))
|
||||
return false;
|
||||
// rewind and resize buffer
|
||||
buf.sz = buf.cur - buf.base;
|
||||
buf.cur = buf.base;
|
||||
return crypto->sign(i.Z, signkey, buf);
|
||||
}
|
||||
|
||||
bool
|
||||
IntroSet::VerifySignature(llarp_crypto* crypto) const
|
||||
{
|
||||
byte_t tmp[MAX_INTROSET_SIZE];
|
||||
auto buf = llarp::StackBuffer< decltype(tmp) >(tmp);
|
||||
IntroSet copy;
|
||||
copy = *this;
|
||||
copy.Z.Zero();
|
||||
if(!copy.BEncode(&buf))
|
||||
return false;
|
||||
// rewind and resize buffer
|
||||
buf.sz = buf.cur - buf.base;
|
||||
buf.cur = buf.base;
|
||||
return crypto->verify(A.signkey, buf, Z);
|
||||
}
|
||||
|
||||
} // namespace service
|
||||
} // namespace llarp
|
||||
|
|
|
@ -22,6 +22,5 @@ TEST_F(APITest, TestMessageWellFormed)
|
|||
msg.msgID = 0;
|
||||
msg.sessionID = 12345;
|
||||
msg.CalculateHash(&crypto, apiPassword);
|
||||
llarp::LogInfo("msghash=", msg.hash);
|
||||
ASSERT_TRUE(msg.IsWellFormed(&crypto, apiPassword));
|
||||
};
|
24
test/base32_unittest.cpp
Normal file
24
test/base32_unittest.cpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#include <gtest/gtest.h>
|
||||
#include <llarp/aligned.hpp>
|
||||
#include <llarp/encode.hpp>
|
||||
#include <llarp/logger.hpp>
|
||||
|
||||
struct Base32Test : public ::testing::Test
|
||||
{
|
||||
Base32Test()
|
||||
{
|
||||
llarp_crypto_libsodium_init(&crypto);
|
||||
}
|
||||
|
||||
llarp_crypto crypto;
|
||||
};
|
||||
|
||||
TEST_F(Base32Test, Serialize)
|
||||
{
|
||||
llarp::AlignedBuffer< 32 > addr, otherAddr;
|
||||
addr.Randomize();
|
||||
char tmp[64] = {0};
|
||||
std::string encoded = llarp::Base32Encode(addr, tmp);
|
||||
ASSERT_TRUE(llarp::Base32Decode(tmp, otherAddr));
|
||||
ASSERT_TRUE(otherAddr == addr);
|
||||
};
|
43
test/hiddenservice_unittest.cpp
Normal file
43
test/hiddenservice_unittest.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
#include <gtest/gtest.h>
|
||||
#include <llarp/service.hpp>
|
||||
|
||||
struct HiddenServiceTest : public ::testing::Test
|
||||
{
|
||||
llarp_crypto crypto;
|
||||
llarp::service::Identity ident;
|
||||
|
||||
HiddenServiceTest()
|
||||
{
|
||||
llarp_crypto_libsodium_init(&crypto);
|
||||
}
|
||||
|
||||
llarp_crypto*
|
||||
Crypto()
|
||||
{
|
||||
return &crypto;
|
||||
}
|
||||
|
||||
void
|
||||
SetUp()
|
||||
{
|
||||
ident.RegenerateKeys(Crypto());
|
||||
ident.pub.vanity.Randomize();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(HiddenServiceTest, TestGenerateIntroSet)
|
||||
{
|
||||
llarp::service::Address addr;
|
||||
ASSERT_TRUE(ident.pub.CalculateAddress(addr));
|
||||
llarp::service::IntroSet I;
|
||||
while(I.I.size() < 10)
|
||||
{
|
||||
llarp::service::Introduction intro;
|
||||
intro.expiresAt = 1000;
|
||||
intro.router.Randomize();
|
||||
intro.pathID.Randomize();
|
||||
I.I.insert(intro);
|
||||
}
|
||||
ASSERT_TRUE(ident.SignIntroSet(I, Crypto()));
|
||||
ASSERT_TRUE(I.VerifySignature(Crypto()));
|
||||
};
|
Loading…
Reference in a new issue