add helper for i2p related functions

This commit is contained in:
sehraf 2020-05-08 16:10:17 +02:00
parent b49dfaead0
commit 462f52585b
No known key found for this signature in database
GPG Key ID: DF09F6EAE356B2C6
3 changed files with 397 additions and 1 deletions

View File

@ -154,6 +154,7 @@ rs_webui {
HEADERS += plugins/pluginmanager.h \
plugins/dlfcn_win32.h \
rsitems/rspluginitems.h \
util/i2pcommon.h \
util/rsinitedptr.h
HEADERS += $$PUBLIC_HEADERS
@ -517,7 +518,8 @@ SOURCES += ft/ftchunkmap.cc \
ft/ftfilesearch.cc \
ft/ftserver.cc \
ft/fttransfermodule.cc \
ft/ftturtlefiletransferitem.cc
ft/ftturtlefiletransferitem.cc \
util/i2pcommon.cpp
SOURCES += crypto/chacha20.cpp \
crypto/hashstream.cc\

View File

@ -0,0 +1,172 @@
#include "i2pcommon.h"
#include "util/rsbase64.h"
#include "util/rsdebug.h"
namespace i2p {
const std::string generateNameSuffix(const size_t len) {
std::vector<uint8_t> tmp(len);
RsRandom::random_bytes(tmp.data(), len);
const std::string location = Radix32::encode(tmp.data(), len);
return location;
}
std::string keyToBase32Addr(const std::string &key)
{
std::string copy(key);
// replace I2P specific chars
std::replace(copy.begin(), copy.end(), '~', '/');
std::replace(copy.begin(), copy.end(), '-', '+');
// decode
std::vector<uint8_t> bin;
RsBase64::decode(copy, bin);
// hash
std::vector<uint8_t> sha256 = RsUtil::BinToSha256(bin);
// encode
std::string out = Radix32::encode(sha256);
// i2p uses lowercase
std::transform(out.begin(), out.end(), out.begin(), ::tolower);
out.append(".b32.i2p");
return out;
}
const std::string makeOption(const std::string &lhs, const int8_t &rhs) {
// convert number to int
std::ostringstream oss;
oss << (int)rhs;
return lhs + "=" + oss.str();
}
uint16_t readTwoBytesBE(std::vector<uint8_t>::const_iterator &p)
{
uint16_t val = 0;
val += *p++;
val <<= 8;
val += *p++;
return val;
}
std::string publicKeyFromPrivate(std::string const &priv)
{
/*
* https://geti2p.net/spec/common-structures#destination
* https://geti2p.net/spec/common-structures#keysandcert
* https://geti2p.net/spec/common-structures#certificate
*/
if (priv.length() < 884) // base64 ( = 663 bytes = KeyCert + priv Keys)
return std::string();
// creat a copy to work on, need to convert it to standard base64
auto priv_copy(priv);
std::replace(priv_copy.begin(), priv_copy.end(), '~', '/');
std::replace(priv_copy.begin(), priv_copy.end(), '-', '+');
// get raw data
std::vector<uint8_t> dataPriv;
RsBase64::decode(priv_copy, dataPriv);
auto p = dataPriv.cbegin();
RsDbg() << __PRETTY_FUNCTION__ << " dataPriv.size " << dataPriv.size() << std::endl;
size_t publicKeyLen = 256 + 128; // default length (bytes)
uint8_t certType = 0;
uint16_t len = 0;
uint16_t signingKeyType = 0;
uint16_t cryptKey = 0;
// only used for easy break
do {
try {
// jump to certificate
p += publicKeyLen;
// try to read type and length
certType = *p++;
len = readTwoBytesBE(p);
// only 0 and 5 are used / valid at this point
// check for == 0
if (certType == static_cast<typename std::underlying_type<CertType>::type>(CertType::Null)) {
/*
* CertType.Null
* type null is followed by 0x00 0x00 <END>
* so has to be 0!
*/
RsDbg() << __PRETTY_FUNCTION__ << " cert is CertType.Null" << std::endl;
publicKeyLen += 3; // add 0x00 0x00 0x00
if (len != 0)
// weird
RsDbg() << __PRETTY_FUNCTION__ << " cert is CertType.Null but len != 0" << std::endl;
break;
}
// check for != 5
if (certType != static_cast<typename std::underlying_type<CertType>::type>(CertType::Key)) {
// unsupported
RsDbg() << __PRETTY_FUNCTION__ << " cert type " << certType << " is unsupported" << std::endl;
return std::string();
}
RsDbg() << __PRETTY_FUNCTION__ << " cert is CertType.Key" << std::endl;
publicKeyLen += 7; // <type 1B> <len 2B> <keyType1 2B> <keyType2 2B> = 1 + 2 + 2 + 2 = 7 bytes
/*
* "Key certificates were introduced in release 0.9.12. Prior to that release, all PublicKeys were 256-byte ElGamal keys, and all SigningPublicKeys were 128-byte DSA-SHA1 keys."
* --> there is space for 256+128 bytes, longer keys are splitted and appended to the certificate
* We don't need to bother with the splitting here as only the lenght is important!
*/
// Signing Public Key
// likely 7
signingKeyType = readTwoBytesBE(p);
RsDbg() << __PRETTY_FUNCTION__ << " signing pubkey type " << certType << std::endl;
if (signingKeyType >= 3 && signingKeyType <= 6) {
RsDbg() << __PRETTY_FUNCTION__ << " signing pubkey type " << certType << " has oversize" << std::endl;
// calculate oversize
auto it = signingKeyLengths.find(static_cast<SigningKeyType>(signingKeyType));
if (it == signingKeyLengths.end()) {
// just in case
RsDbg() << __PRETTY_FUNCTION__ << " signing pubkey type " << certType << " cannot be found in size map!" << std::endl;
return std::string();
}
if (it->second.first <= 128) {
// just in case, it's supposed to be larger!
RsDbg() << __PRETTY_FUNCTION__ << " signing pubkey type " << certType << " is oversize but size calculation would underflow!" << std::endl;
return std::string();
}
publicKeyLen += it->second.first - 128; // 128 = default DSA key length = the space than can be used before the key must be splitted
}
// Crypto Public Key
// likely 0
cryptKey = readTwoBytesBE(p);
RsDbg() << __PRETTY_FUNCTION__ << " crypto pubkey type " << cryptKey << std::endl;
// info: these are all smaller than the default 256 bytes, so no oversize calculation is needed
break;
} catch (const std::out_of_range &e) {
RsDbg() << __PRETTY_FUNCTION__ << " hit exception!" << e.what() << std::endl;
return std::string();
}
} while(false);
std::string pub;
auto data2 = std::vector<uint8_t>(dataPriv.cbegin(), dataPriv.cbegin() + publicKeyLen);
RsBase64::encode(data2.data(), data2.size(), pub, false, false);
return pub;
}
} // namespace i2p

View File

@ -0,0 +1,222 @@
#ifndef I2PCOMMON_H
#define I2PCOMMON_H
#include <algorithm>
#include <map>
#include "util/rsrandom.h"
#include "util/radix32.h"
#include "util/rsbase64.h"
#include "util/rsprint.h"
#include "util/rsdebug.h"
/*
* This header provides common code for i2p related code, namely BOB and SAM3 support.
*/
namespace i2p {
static constexpr int8_t kDefaultLength = 3; // i2p default
static constexpr int8_t kDefaultQuantity = 3; // i2p default + 1
static constexpr int8_t kDefaultVariance = 0;
static constexpr int8_t kDefaultBackupQuantity = 0;
/**
* @brief The address struct
* This structure is a container for any i2p address/key. The public key is used for addressing and can be (optionally) hashed to generate the .b32.i2p address.
*/
struct address {
std::string base32;
std::string publicKey;
std::string privateKey;
void clear() {
base32.clear();
publicKey.clear();
privateKey.clear();
}
};
/**
* @brief The settings struct
* Common structure with all settings that are shared between any i2p backends
*/
struct settings {
bool enable;
struct address address;
// connection parameter
int8_t inLength;
int8_t inQuantity;
int8_t inVariance;
int8_t inBackupQuantity;
int8_t outLength;
int8_t outQuantity;
int8_t outVariance;
int8_t outBackupQuantity;
void initDefault() {
enable = false;
address.clear();
inLength = kDefaultLength;
inQuantity = kDefaultQuantity;
inVariance = kDefaultVariance;
inBackupQuantity = kDefaultBackupQuantity;
outLength = kDefaultLength;
outQuantity = kDefaultQuantity;
outVariance = kDefaultVariance;
outBackupQuantity = kDefaultBackupQuantity;
}
};
/*
Type Type Code Payload Length Total Length Notes
Null 0 0 3
HashCash 1 varies varies Experimental, unused. Payload contains an ASCII colon-separated hashcash string.
Hidden 2 0 3 Experimental, unused. Hidden routers generally do not announce that they are hidden.
Signed 3 40 or 72 43 or 75 Experimental, unused. Payload contains a 40-byte DSA signature, optionally followed by the 32-byte Hash of the signing Destination.
Multiple 4 varies varies Experimental, unused. Payload contains multiple certificates.
Key 5 4+ 7+ Since 0.9.12. See below for details.
*/
enum class CertType : uint8_t {
Null = 0,
HashCash = 1,
Hidden = 2,
Signed = 3,
Multiple = 4,
Key = 5
};
/*
* public
Type Type Code Total Public Key Length Since Usage
DSA_SHA1 0 128 0.9.12 Legacy Router Identities and Destinations, never explicitly set
ECDSA_SHA256_P256 1 64 0.9.12 Older Destinations
ECDSA_SHA384_P384 2 96 0.9.12 Rarely if ever used for Destinations
ECDSA_SHA512_P521 3 132 0.9.12 Rarely if ever used for Destinations
RSA_SHA256_2048 4 256 0.9.12 Offline only; never used in Key Certificates for Router Identities or Destinations
RSA_SHA384_3072 5 384 0.9.12 Offline only; never used in Key Certificates for Router Identities or Destinations
RSA_SHA512_4096 6 512 0.9.12 Offline only; never used in Key Certificates for Router Identities or Destinations
EdDSA_SHA512_Ed25519 7 32 0.9.15 Recent Router Identities and Destinations
EdDSA_SHA512_Ed25519ph 8 32 0.9.25 Offline only; never used in Key Certificates for Router Identities or Destinations
reserved (GOST) 9 64 Reserved, see proposal 134
reserved (GOST) 10 128 Reserved, see proposal 134
RedDSA_SHA512_Ed25519 11 32 0.9.39 For Destinations and encrypted leasesets only; never used for Router Identities
reserved 65280-65534 Reserved for experimental use
reserved 65535 Reserved for future expansion
* private
Type Length (bytes) Since Usage
DSA_SHA1 20 Legacy Router Identities and Destinations
ECDSA_SHA256_P256 32 0.9.12 Recent Destinations
ECDSA_SHA384_P384 48 0.9.12 Rarely used for Destinations
ECDSA_SHA512_P521 66 0.9.12 Rarely used for Destinations
RSA_SHA256_2048 512 0.9.12 Offline signing, never used for Router Identities or Destinations
RSA_SHA384_3072 768 0.9.12 Offline signing, never used for Router Identities or Destinations
RSA_SHA512_4096 1024 0.9.12 Offline signing, never used for Router Identities or Destinations
EdDSA_SHA512_Ed25519 32 0.9.15 Recent Router Identities and Destinations
EdDSA_SHA512_Ed25519ph 32 0.9.25 Offline signing, never used for Router Identities or Destinations
RedDSA_SHA512_Ed25519 32 0.9.39 For Destinations and encrypted leasesets only, never used for Router Identities
*/
enum class SigningKeyType : uint16_t {
DSA_SHA1 = 0,
ECDSA_SHA256_P256 = 1,
ECDSA_SHA384_P384 = 2,
ECDSA_SHA512_P521 = 3,
RSA_SHA256_2048 = 4,
RSA_SHA384_3072 = 5,
RSA_SHA512_4096 = 6,
EdDSA_SHA512_Ed25519 = 7,
EdDSA_SHA512_Ed25519ph = 8,
RedDSA_SHA512_Ed25519 = 11
};
/*
* public
Type Type Code Total Public Key Length Usage
ElGamal 0 256 All Router Identities and Destinations
P256 1 64 Reserved, see proposal 145
P384 2 96 Reserved, see proposal 145
P521 3 132 Reserved, see proposal 145
X25519 4 32 Not for use in key certs. See proposal 144
reserved 65280-65534 Reserved for experimental use
reserved 65535 Reserved for future expansion
* private
Type Length (bytes) Since Usage
ElGamal 256 All Router Identities and Destinations
P256 32 TBD Reserved, see proposal 145
P384 48 TBD Reserved, see proposal 145
P521 66 TBD Reserved, see proposal 145
X25519 32 0.9.38 Little-endian. See proposal 144
*/
enum class CryptoKeyType : uint16_t {
ElGamal = 0,
P256 = 1,
P384 = 2,
P521 = 3,
X25519 = 4
};
static const std::map<CryptoKeyType, std::pair<uint16_t, uint16_t>> cryptoKeyLengths {
{CryptoKeyType::ElGamal, {256, 256}},
{CryptoKeyType::P256, { 64, 32}},
{CryptoKeyType::P384, { 96, 48}},
{CryptoKeyType::P521, {132, 66}},
{CryptoKeyType::X25519, { 32, 32}},
};
static const std::map<SigningKeyType, std::pair<uint16_t, uint16_t>> signingKeyLengths {
{SigningKeyType::DSA_SHA1, {128, 128}},
{SigningKeyType::ECDSA_SHA256_P256, { 64, 32}},
{SigningKeyType::ECDSA_SHA384_P384, { 96, 48}},
{SigningKeyType::ECDSA_SHA512_P521, {132, 66}},
{SigningKeyType::RSA_SHA256_2048, {256, 512}},
{SigningKeyType::RSA_SHA384_3072, {384, 768}},
{SigningKeyType::RSA_SHA512_4096, {512,1024}},
{SigningKeyType::EdDSA_SHA512_Ed25519, { 32, 32}},
{SigningKeyType::EdDSA_SHA512_Ed25519ph,{ 32, 32}},
{SigningKeyType::RedDSA_SHA512_Ed25519, { 32, 32}},
};
/**
* @brief generateNameSuffix Generates a base32 name suffix for tunnel identification
* @param len lenght of random bytes, will be expanded by base32 encoding
* @return base32 string
*
* RSRandom::random_alphaNumericString can return very weird looking strings like: ,,@z+M
* -> so use base32 instead
*
* 5 characters = 8 base32 symbols
*/
const std::string generateNameSuffix(const size_t len = 5);
/**
* @brief makeOption Creates the string "lhs=rhs" used by BOB and SAM. Converts rhs
* @param lhs option to set
* @param rhs value to set
* @return concatenated string
*/
const std::string makeOption(const std::string &lhs, const int8_t &rhs);
/**
* @brief keyToBase32Addr generated a base32 address (.b32.i2p) from a given public key
* @param key public key
* @return generated base32 address
*/
std::string keyToBase32Addr(const std::string &key);
/**
* @brief publicKeyFromPrivate parses the private key and calculates the lenght of the public key
* @param priv private key (which includes the public key) to read
* @return public key used for addressing
*/
std::string publicKeyFromPrivate(const std::string &priv);
} // namespace i2p
#endif // I2PCOMMON_H