use string/string_view for base64
Some checks are pending
Build Debian packages / bookworm (push) Waiting to run
Build Debian packages / bullseye (push) Waiting to run
Build Debian packages / buster (push) Waiting to run
Build on FreeBSD / with UPnP (push) Waiting to run
Build on OSX / With USE_UPNP=no (push) Waiting to run
Build on OSX / With USE_UPNP=yes (push) Waiting to run
Build on Windows / clang-x86_64 (push) Waiting to run
Build on Windows / i686 (push) Waiting to run
Build on Windows / ucrt-x86_64 (push) Waiting to run
Build on Windows / x86_64 (push) Waiting to run
Build on Windows / CMake clang-x86_64 (push) Waiting to run
Build on Windows / CMake i686 (push) Waiting to run
Build on Windows / CMake ucrt-x86_64 (push) Waiting to run
Build on Windows / CMake x86_64 (push) Waiting to run
Build on Windows / XP (push) Waiting to run
Build on Ubuntu / Make with USE_UPNP=no (push) Waiting to run
Build on Ubuntu / Make with USE_UPNP=yes (push) Waiting to run
Build on Ubuntu / CMake with -DWITH_UPNP=OFF (push) Waiting to run
Build on Ubuntu / CMake with -DWITH_UPNP=ON (push) Waiting to run
Build containers / Building container for linux/amd64 (push) Waiting to run
Build containers / Building container for linux/arm64 (push) Waiting to run
Build containers / Building container for linux/arm/v7 (push) Waiting to run
Build containers / Building container for linux/386 (push) Waiting to run
Build containers / Pushing merged manifest (push) Blocked by required conditions

This commit is contained in:
orignal 2025-03-17 09:06:11 -04:00
parent e0a21cf702
commit 93cc810f29
9 changed files with 142 additions and 120 deletions

View file

@ -15,7 +15,7 @@ namespace i2p
{ {
namespace data namespace data
{ {
static const char T32[32] = static constexpr char T32[32] =
{ {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
@ -38,7 +38,7 @@ namespace data
* Direct Substitution Table * Direct Substitution Table
*/ */
static const char T64[64] = static constexpr char T64[64] =
{ {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
@ -77,11 +77,11 @@ namespace data
* *
*/ */
size_t ByteStreamToBase64 ( /* Number of bytes in the encoded buffer */ size_t ByteStreamToBase64 ( // Number of bytes in the encoded buffer
const uint8_t * InBuffer, /* Input buffer, binary data */ const uint8_t * InBuffer, // Input buffer, binary data
size_t InCount, /* Number of bytes in the input buffer */ size_t InCount, // Number of bytes in the input buffer
char * OutBuffer, /* output buffer */ char * OutBuffer, // output buffer
size_t len /* length of output buffer */ size_t len // length of output buffer
) )
{ {
unsigned char * ps; unsigned char * ps;
@ -108,24 +108,24 @@ namespace data
{ {
acc_1 = *ps++; acc_1 = *ps++;
acc_2 = (acc_1 << 4) & 0x30; acc_2 = (acc_1 << 4) & 0x30;
acc_1 >>= 2; /* base64 digit #1 */ acc_1 >>= 2; // base64 digit #1
*pd++ = T64[acc_1]; *pd++ = T64[acc_1];
acc_1 = *ps++; acc_1 = *ps++;
acc_2 |= acc_1 >> 4; /* base64 digit #2 */ acc_2 |= acc_1 >> 4; // base64 digit #2
*pd++ = T64[acc_2]; *pd++ = T64[acc_2];
acc_1 &= 0x0f; acc_1 &= 0x0f;
acc_1 <<= 2; acc_1 <<= 2;
acc_2 = *ps++; acc_2 = *ps++;
acc_1 |= acc_2 >> 6; /* base64 digit #3 */ acc_1 |= acc_2 >> 6; // base64 digit #3
*pd++ = T64[acc_1]; *pd++ = T64[acc_1];
acc_2 &= 0x3f; /* base64 digit #4 */ acc_2 &= 0x3f; // base64 digit #4
*pd++ = T64[acc_2]; *pd++ = T64[acc_2];
} }
if ( m == 1 ) if ( m == 1 )
{ {
acc_1 = *ps++; acc_1 = *ps++;
acc_2 = (acc_1 << 4) & 0x3f; /* base64 digit #2 */ acc_2 = (acc_1 << 4) & 0x3f; // base64 digit #2
acc_1 >>= 2; /* base64 digit #1 */ acc_1 >>= 2; // base64 digit #1
*pd++ = T64[acc_1]; *pd++ = T64[acc_1];
*pd++ = T64[acc_2]; *pd++ = T64[acc_2];
*pd++ = P64; *pd++ = P64;
@ -136,13 +136,13 @@ namespace data
{ {
acc_1 = *ps++; acc_1 = *ps++;
acc_2 = (acc_1 << 4) & 0x3f; acc_2 = (acc_1 << 4) & 0x3f;
acc_1 >>= 2; /* base64 digit #1 */ acc_1 >>= 2; // base64 digit #1
*pd++ = T64[acc_1]; *pd++ = T64[acc_1];
acc_1 = *ps++; acc_1 = *ps++;
acc_2 |= acc_1 >> 4; /* base64 digit #2 */ acc_2 |= acc_1 >> 4; // base64 digit #2
*pd++ = T64[acc_2]; *pd++ = T64[acc_2];
acc_1 &= 0x0f; acc_1 &= 0x0f;
acc_1 <<= 2; /* base64 digit #3 */ acc_1 <<= 2; // base64 digit #3
*pd++ = T64[acc_1]; *pd++ = T64[acc_1];
*pd++ = P64; *pd++ = P64;
} }
@ -150,60 +150,112 @@ namespace data
return outCount; return outCount;
} }
/* std::string ByteStreamToBase64 (// base64 encoded string
* const uint8_t * InBuffer, // Input buffer, binary data
* Base64ToByteStream size_t InCount // Number of bytes in the input buffer
* ------------------
*
* Converts BASE64 encoded data to binary format. If input buffer is
* not properly padded, buffer of negative length is returned
*
*/
size_t Base64ToByteStream ( /* Number of output bytes */
const char * InBuffer, /* BASE64 encoded buffer */
size_t InCount, /* Number of input bytes */
uint8_t * OutBuffer, /* output buffer length */
size_t len /* length of output buffer */
) )
{ {
unsigned char * ps; unsigned char * ps;
unsigned char * pd;
unsigned char acc_1; unsigned char acc_1;
unsigned char acc_2; unsigned char acc_2;
int i; int i;
int n; int n;
int m; int m;
ps = (unsigned char *)InBuffer;
n = InCount / 3;
m = InCount % 3;
size_t outCount = m ? (4 * (n + 1)) : (4 * n);
std::string out;
out.reserve (outCount);
for ( i = 0; i < n; i++ )
{
acc_1 = *ps++;
acc_2 = (acc_1 << 4) & 0x30;
acc_1 >>= 2; // base64 digit #1
out.push_back (T64[acc_1]);
acc_1 = *ps++;
acc_2 |= acc_1 >> 4; // base64 digit #2
out.push_back (T64[acc_2]);
acc_1 &= 0x0f;
acc_1 <<= 2;
acc_2 = *ps++;
acc_1 |= acc_2 >> 6; // base64 digit #3
out.push_back (T64[acc_1]);
acc_2 &= 0x3f; // base64 digit #4
out.push_back (T64[acc_2]);
}
if ( m == 1 )
{
acc_1 = *ps++;
acc_2 = (acc_1 << 4) & 0x3f; // base64 digit #2
acc_1 >>= 2; // base64 digit #1
out.push_back (T64[acc_1]);
out.push_back (T64[acc_2]);
out.push_back (P64);
out.push_back (P64);
}
else if ( m == 2 )
{
acc_1 = *ps++;
acc_2 = (acc_1 << 4) & 0x3f;
acc_1 >>= 2; // base64 digit #1
out.push_back (T64[acc_1]);
acc_1 = *ps++;
acc_2 |= acc_1 >> 4; // base64 digit #2
out.push_back (T64[acc_2]);
acc_1 &= 0x0f;
acc_1 <<= 2; // base64 digit #3
out.push_back (T64[acc_1]);
out.push_back (P64);
}
return out;
}
/*
*
* Base64ToByteStream
* ------------------
*
* Converts BASE64 encoded string to binary format. If input buffer is
* not properly padded, buffer of negative length is returned
*
*/
size_t Base64ToByteStream ( // Number of output bytes
std::string_view base64Str, // BASE64 encoded string
uint8_t * OutBuffer, // output buffer length
size_t len // length of output buffer
)
{
unsigned char * pd;
unsigned char acc_1;
unsigned char acc_2;
size_t outCount; size_t outCount;
if (isFirstTime) if (base64Str.empty () || base64Str[0] == P64) return 0;
iT64Build(); auto d = std::div (base64Str.length (), 4);
if (!d.rem)
n = InCount / 4; outCount = 3 * d.quot;
m = InCount % 4;
if (InCount && !m)
outCount = 3 * n;
else else
return 0; return 0;
if(*InBuffer == P64) if (isFirstTime) iT64Build();
return 0;
ps = (unsigned char *)(InBuffer + InCount - 1);
while ( *ps-- == P64 )
outCount--;
ps = (unsigned char *)InBuffer;
if (outCount > len)
return 0;
auto pos = base64Str.find_last_not_of (P64);
if (pos == base64Str.npos) return 0;
outCount -= (base64Str.length () - pos - 1);
if (outCount > len) return 0;
auto ps = base64Str.begin ();
pd = OutBuffer; pd = OutBuffer;
auto endOfOutBuffer = OutBuffer + outCount; auto endOfOutBuffer = OutBuffer + outCount;
for ( i = 0; i < n; i++ ) for (int i = 0; i < d.quot; i++)
{ {
acc_1 = iT64[*ps++]; acc_1 = iT64[int(*ps++)];
acc_2 = iT64[*ps++]; acc_2 = iT64[int(*ps++)];
acc_1 <<= 2; acc_1 <<= 2;
acc_1 |= acc_2 >> 4; acc_1 |= acc_2 >> 4;
*pd++ = acc_1; *pd++ = acc_1;
@ -211,36 +263,30 @@ namespace data
break; break;
acc_2 <<= 4; acc_2 <<= 4;
acc_1 = iT64[*ps++]; acc_1 = iT64[int(*ps++)];
acc_2 |= acc_1 >> 2; acc_2 |= acc_1 >> 2;
*pd++ = acc_2; *pd++ = acc_2;
if (pd >= endOfOutBuffer) if (pd >= endOfOutBuffer)
break; break;
acc_2 = iT64[*ps++]; acc_2 = iT64[int(*ps++)];
acc_2 |= acc_1 << 6; acc_2 |= acc_1 << 6;
*pd++ = acc_2; *pd++ = acc_2;
} }
return outCount; return outCount;
} }
std::string ToBase64Standard (std::string_view in) std::string ToBase64Standard (std::string_view in)
{ {
auto len = Base64EncodingBufferSize (in.length ()); auto str = ByteStreamToBase64 ((const uint8_t *)in.data (), in.length ());
char * str = new char[len + 1];
auto l = ByteStreamToBase64 ((const uint8_t *)in.data (), in.length (), str, len);
str[l] = 0;
// replace '-' by '+' and '~' by '/' // replace '-' by '+' and '~' by '/'
for (size_t i = 0; i < l; i++) for (auto& ch: str)
if (str[i] == '-') if (ch == '-')
str[i] = '+'; ch = '+';
else if (str[i] == '~') else if (ch == '~')
str[i] = '/'; ch = '/';
return str;
std::string s(str);
delete[] str;
return s;
} }
/* /*

View file

@ -18,8 +18,10 @@ namespace i2p
{ {
namespace data namespace data
{ {
size_t ByteStreamToBase64 (const uint8_t * InBuffer, size_t InCount, char * OutBuffer, size_t len); size_t ByteStreamToBase64 (const uint8_t * InBuffer, size_t InCount, char * OutBuffer, size_t len); // called from SAM TODO: rewrite
size_t Base64ToByteStream (const char * InBuffer, size_t InCount, uint8_t * OutBuffer, size_t len ); std::string ByteStreamToBase64 (const uint8_t * InBuffer, size_t InCount);
size_t Base64ToByteStream (std::string_view base64Str, uint8_t * OutBuffer, size_t len);
const char * GetBase32SubstitutionTable (); const char * GetBase32SubstitutionTable ();
const char * GetBase64SubstitutionTable (); const char * GetBase64SubstitutionTable ();
constexpr bool IsBase64 (char ch) constexpr bool IsBase64 (char ch)
@ -28,8 +30,7 @@ namespace data
} }
size_t Base32ToByteStream (std::string_view base32Str, uint8_t * outBuf, size_t outLen); size_t Base32ToByteStream (std::string_view base32Str, uint8_t * outBuf, size_t outLen);
std::string ByteStreamToBase32 (const uint8_t * inBuf, size_t len); std::string ByteStreamToBase32 (const uint8_t * inBuf, size_t len);
constexpr bool IsBase32 (char ch) constexpr bool IsBase32 (char ch)
{ {
return (ch >= 'a' && ch <= 'z') || (ch >= '2' && ch <= '7'); return (ch >= 'a' && ch <= 'z') || (ch >= '2' && ch <= '7');

View file

@ -105,7 +105,7 @@ namespace data
memcpy (buf, family.c_str (), len); memcpy (buf, family.c_str (), len);
memcpy (buf + len, (const uint8_t *)ident, 32); memcpy (buf + len, (const uint8_t *)ident, 32);
len += 32; len += 32;
auto signatureBufLen = Base64ToByteStream (signature.data (), signature.length (), signatureBuf, 64); auto signatureBufLen = Base64ToByteStream (signature, signatureBuf, 64);
if (signatureBufLen) if (signatureBufLen)
{ {
EVP_MD_CTX * ctx = EVP_MD_CTX_create (); EVP_MD_CTX * ctx = EVP_MD_CTX_create ();
@ -154,12 +154,7 @@ namespace data
memcpy (buf + len, (const uint8_t *)ident, 32); memcpy (buf + len, (const uint8_t *)ident, 32);
len += 32; len += 32;
signer.Sign (buf, len, signature); signer.Sign (buf, len, signature);
len = Base64EncodingBufferSize (64); sig = ByteStreamToBase64 (signature, 64);
char * b64 = new char[len+1];
len = ByteStreamToBase64 (signature, 64, b64, len);
b64[len] = 0;
sig = b64;
delete[] b64;
} }
else else
LogPrint (eLogWarning, "Family: elliptic curve ", curve, " is not supported"); LogPrint (eLogWarning, "Family: elliptic curve ", curve, " is not supported");

View file

@ -271,21 +271,17 @@ namespace data
size_t IdentityEx::FromBase64(std::string_view s) size_t IdentityEx::FromBase64(std::string_view s)
{ {
const size_t slen = s.length(); std::vector<uint8_t> buf(s.length ()); // binary data can't exceed base64
std::vector<uint8_t> buf(slen); // binary data can't exceed base64 auto len = Base64ToByteStream (s, buf.data(), buf.size ());
const size_t len = Base64ToByteStream (s.data(), slen, buf.data(), slen);
return FromBuffer (buf.data(), len); return FromBuffer (buf.data(), len);
} }
std::string IdentityEx::ToBase64 () const std::string IdentityEx::ToBase64 () const
{ {
const size_t bufLen = GetFullLen(); const size_t bufLen = GetFullLen();
const size_t strLen = Base64EncodingBufferSize(bufLen);
std::vector<uint8_t> buf(bufLen); std::vector<uint8_t> buf(bufLen);
std::vector<char> str(strLen);
size_t l = ToBuffer (buf.data(), bufLen); size_t l = ToBuffer (buf.data(), bufLen);
size_t l1 = i2p::data::ByteStreamToBase64 (buf.data(), l, str.data(), strLen); return i2p::data::ByteStreamToBase64 (buf.data(), l);
return std::string (str.data(), l1);
} }
size_t IdentityEx::GetSigningPublicKeyLen () const size_t IdentityEx::GetSigningPublicKeyLen () const
@ -570,26 +566,18 @@ namespace data
return ret; return ret;
} }
size_t PrivateKeys::FromBase64(const std::string& s) size_t PrivateKeys::FromBase64(std::string_view s)
{ {
uint8_t * buf = new uint8_t[s.length ()]; std::vector<uint8_t> buf(s.length ());
size_t l = i2p::data::Base64ToByteStream (s.c_str (), s.length (), buf, s.length ()); size_t l = i2p::data::Base64ToByteStream (s, buf.data (), buf.size ());
size_t ret = FromBuffer (buf, l); return FromBuffer (buf.data (), l);
delete[] buf;
return ret;
} }
std::string PrivateKeys::ToBase64 () const std::string PrivateKeys::ToBase64 () const
{ {
uint8_t * buf = new uint8_t[GetFullLen ()]; std::vector<uint8_t> buf(GetFullLen ());
char * str = new char[GetFullLen ()*2]; size_t l = ToBuffer (buf.data (), buf.size ());
size_t l = ToBuffer (buf, GetFullLen ()); return i2p::data::ByteStreamToBase64 (buf.data (), l);
size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, str, GetFullLen ()*2);
str[l1] = 0;
delete[] buf;
std::string ret(str);
delete[] str;
return ret;
} }
void PrivateKeys::Sign (const uint8_t * buf, int len, uint8_t * signature) const void PrivateKeys::Sign (const uint8_t * buf, int len, uint8_t * signature) const

View file

@ -164,7 +164,7 @@ namespace data
size_t FromBuffer (const uint8_t * buf, size_t len); size_t FromBuffer (const uint8_t * buf, size_t len);
size_t ToBuffer (uint8_t * buf, size_t len) const; size_t ToBuffer (uint8_t * buf, size_t len) const;
size_t FromBase64(const std::string& s); size_t FromBase64(std::string_view s);
std::string ToBase64 () const; std::string ToBase64 () const;
std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> CreateDecryptor (const uint8_t * key) const; std::shared_ptr<i2p::crypto::CryptoKeyDecryptor> CreateDecryptor (const uint8_t * key) const;

View file

@ -951,9 +951,7 @@ namespace data
LogPrint (eLogError, "NetDb: DatabaseLookup for zero ident. Ignored"); LogPrint (eLogError, "NetDb: DatabaseLookup for zero ident. Ignored");
return; return;
} }
char key[48]; auto key = i2p::data::ByteStreamToBase64 (buf, 32);
int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48);
key[l] = 0;
IdentHash replyIdent(buf + 32); IdentHash replyIdent(buf + 32);
uint8_t flag = buf[64]; uint8_t flag = buf[64];

View file

@ -360,9 +360,7 @@ namespace data
void NetDbRequests::HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg) void NetDbRequests::HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg)
{ {
const uint8_t * buf = msg->GetPayload (); const uint8_t * buf = msg->GetPayload ();
char key[48]; auto key = i2p::data::ByteStreamToBase64 (buf, 32);
int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48);
key[l] = 0;
size_t num = buf[32]; // num size_t num = buf[32]; // num
LogPrint (eLogDebug, "NetDbReq: DatabaseSearchReply for ", key, " num=", num); LogPrint (eLogDebug, "NetDbReq: DatabaseSearchReply for ", key, " num=", num);
IdentHash ident (buf); IdentHash ident (buf);

View file

@ -281,7 +281,7 @@ namespace data
address->caps = ExtractAddressCaps (value); address->caps = ExtractAddressCaps (value);
else if (key == "s") // ntcp2 or ssu2 static key else if (key == "s") // ntcp2 or ssu2 static key
{ {
if (Base64ToByteStream (value.data (), value.length (), address->s, 32) == 32 && if (Base64ToByteStream (value, address->s, 32) == 32 &&
!(address->s[31] & 0x80)) // check if x25519 public key !(address->s[31] & 0x80)) // check if x25519 public key
isStaticKey = true; isStaticKey = true;
else else
@ -291,14 +291,14 @@ namespace data
{ {
if (address->IsNTCP2 ()) if (address->IsNTCP2 ())
{ {
if (Base64ToByteStream (value.data (), value.length (), address->i, 16) == 16) if (Base64ToByteStream (value, address->i, 16) == 16)
address->published = true; // presence of "i" means "published" NTCP2 address->published = true; // presence of "i" means "published" NTCP2
else else
address->transportStyle = eTransportUnknown; // invalid address address->transportStyle = eTransportUnknown; // invalid address
} }
else if (address->IsSSU2 ()) else if (address->IsSSU2 ())
{ {
if (Base64ToByteStream (value.data (), value.length (), address->i, 32) == 32) if (Base64ToByteStream (value, address->i, 32) == 32)
isIntroKey = true; isIntroKey = true;
else else
address->transportStyle = eTransportUnknown; // invalid address address->transportStyle = eTransportUnknown; // invalid address
@ -343,7 +343,7 @@ namespace data
LogPrint (eLogWarning, "RouterInfo: 'itag' conversion error: ", std::make_error_code (res.ec).message ()); LogPrint (eLogWarning, "RouterInfo: 'itag' conversion error: ", std::make_error_code (res.ec).message ());
} }
else if (key1 == "ih") else if (key1 == "ih")
Base64ToByteStream (value.data (), value.length (), introducer.iH, 32); Base64ToByteStream (value, introducer.iH, 32);
else if (key1 == "iexp") else if (key1 == "iexp")
{ {
auto res = std::from_chars(value.data(), value.data() + value.size(), introducer.iExp); auto res = std::from_chars(value.data(), value.data() + value.size(), introducer.iExp);
@ -1394,9 +1394,7 @@ namespace data
if (!introducer.iTag) continue; if (!introducer.iTag) continue;
WriteString ("ih" + std::to_string(i), properties); WriteString ("ih" + std::to_string(i), properties);
properties << '='; properties << '=';
char value[64]; auto value = ByteStreamToBase64 (introducer.iH, 32);
size_t l = ByteStreamToBase64 (introducer.iH, 32, value, 64);
value[l] = 0;
WriteString (value, properties); WriteString (value, properties);
properties << ';'; properties << ';';
i++; i++;

View file

@ -62,9 +62,7 @@ namespace data
std::string ToBase64 (size_t len = sz) const std::string ToBase64 (size_t len = sz) const
{ {
char str[sz*2]; return i2p::data::ByteStreamToBase64 (m_Buf, len);
size_t l = i2p::data::ByteStreamToBase64 (m_Buf, len, str, sz*2);
return std::string (str, str + l);
} }
std::string ToBase32 (size_t len = sz) const std::string ToBase32 (size_t len = sz) const
@ -79,7 +77,7 @@ namespace data
size_t FromBase64 (std::string_view s) size_t FromBase64 (std::string_view s)
{ {
return i2p::data::Base64ToByteStream (s.data (), s.length (), m_Buf, sz); return i2p::data::Base64ToByteStream (s, m_Buf, sz);
} }
uint8_t GetBit (int i) const uint8_t GetBit (int i) const