base32 encoding and unit tests

pull/5/head
Jeff Becker 6 years ago
parent 21a2ffdeaf
commit 6cb31ecb1a
No known key found for this signature in database
GPG Key ID: F357B3B42F6F9B05

@ -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));
};

@ -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);
};

@ -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…
Cancel
Save