Improve files links generation parsing and errors

Implement URL safe base64 for file links
Implement sneaking file data into URL fragment
Deprecate Radix65 in favore of RsBase64 which supports also URL safe encoding
This commit is contained in:
Gioacchino Mazzurco 2020-03-12 18:57:07 +01:00
parent 55d466f79b
commit d203f31d0c
No known key found for this signature in database
GPG Key ID: A1FBCA3872E87051
8 changed files with 403 additions and 49 deletions

View File

@ -22,10 +22,12 @@
#include <iomanip> #include <iomanip>
#include "util/radix64.h" #include "util/radix64.h"
#include "util/rsbase64.h"
#include "util/rsdir.h" #include "util/rsdir.h"
#include "retroshare/rsfiles.h" #include "retroshare/rsfiles.h"
#include "file_sharing_defaults.h" #include "file_sharing_defaults.h"
#include "filelist_io.h" #include "filelist_io.h"
#include "serialiser/rstypeserializer.h"
void RsFileTree::DirData::serial_process( void RsFileTree::DirData::serial_process(
RsGenericSerializer::SerializeJob j, RsGenericSerializer::SerializeJob j,
@ -45,17 +47,24 @@ void RsFileTree::FileData::serial_process(
RS_SERIAL_PROCESS(hash); RS_SERIAL_PROCESS(hash);
} }
/*static*/ std::unique_ptr<RsFileTree> RsFileTree::fromBase64( /*static*/ std::tuple<std::unique_ptr<RsFileTree>, std::error_condition>
const std::string& base64 ) RsFileTree::fromBase64(const std::string& base64)
{ {
std::unique_ptr<RsFileTree> ft(new RsFileTree); const auto failure = [](std::error_condition ec)
std::vector<uint8_t> mem = Radix64::decode(base64); { return std::make_tuple(nullptr, ec); };
std::error_condition ec;
std::vector<uint8_t> mem;
if( (ec = RsBase64::decode(base64, mem)) ) return failure(ec);
RsGenericSerializer::SerializeContext ctx( RsGenericSerializer::SerializeContext ctx(
mem.data(), static_cast<uint32_t>(mem.size()) ); mem.data(), static_cast<uint32_t>(mem.size()) );
std::unique_ptr<RsFileTree> ft(new RsFileTree);
ft->serial_process( ft->serial_process(
RsGenericSerializer::SerializeJob::DESERIALIZE, ctx); RsGenericSerializer::SerializeJob::DESERIALIZE, ctx);
if(ctx.mOk) return ft; if(ctx.mOk) return std::make_tuple(std::move(ft), std::error_condition());
return nullptr;
return failure(std::errc::invalid_argument);
} }
std::string RsFileTree::toBase64() const std::string RsFileTree::toBase64() const
@ -64,8 +73,6 @@ std::string RsFileTree::toBase64() const
RsFileTree* ncThis = const_cast<RsFileTree*>(this); RsFileTree* ncThis = const_cast<RsFileTree*>(this);
ncThis->serial_process( ncThis->serial_process(
RsGenericSerializer::SerializeJob::SIZE_ESTIMATE, ctx ); RsGenericSerializer::SerializeJob::SIZE_ESTIMATE, ctx );
RsDbg() << __PRETTY_FUNCTION__ << " ctx.mOffset: " << ctx.mOffset
<< std::endl;
std::vector<uint8_t> buf(ctx.mOffset); std::vector<uint8_t> buf(ctx.mOffset);
ctx.mSize = ctx.mOffset; ctx.mOffset = 0; ctx.mData = buf.data(); ctx.mSize = ctx.mOffset; ctx.mOffset = 0; ctx.mData = buf.data();
@ -73,7 +80,7 @@ std::string RsFileTree::toBase64() const
ncThis->serial_process( ncThis->serial_process(
RsGenericSerializer::SerializeJob::SERIALIZE, ctx ); RsGenericSerializer::SerializeJob::SERIALIZE, ctx );
std::string result; std::string result;
Radix64::encode(ctx.mData, ctx.mSize, result); RsBase64::encode(ctx.mData, ctx.mSize, result, false, true);
return result; return result;
} }
@ -94,7 +101,7 @@ std::unique_ptr<RsFileTree> RsFileTree::fromRadix64(
std::unique_ptr<RsFileTree> ft(new RsFileTree); std::unique_ptr<RsFileTree> ft(new RsFileTree);
std::vector<uint8_t> mem = Radix64::decode(radix64_string); std::vector<uint8_t> mem = Radix64::decode(radix64_string);
if(ft->deserialise(mem.data(), static_cast<uint32_t>(mem.size()))) if(ft->deserialise(mem.data(), static_cast<uint32_t>(mem.size())))
return std::move(ft); return ft;
return nullptr; return nullptr;
} }
@ -353,3 +360,5 @@ bool RsFileTree::serialise(unsigned char *& buffer,uint32_t& buffer_size) const
return false; return false;
} }
} }
RsFileTree::~RsFileTree() = default;

View File

@ -72,6 +72,8 @@
"retroshare:///files"; "retroshare:///files";
/*static*/ const std::string RsFiles::FILES_URL_COUNT_FIELD = "filesCount"; /*static*/ const std::string RsFiles::FILES_URL_COUNT_FIELD = "filesCount";
/*static*/ const std::string RsFiles::FILES_URL_DATA_FIELD = "filesData"; /*static*/ const std::string RsFiles::FILES_URL_DATA_FIELD = "filesData";
// Use a 5 character word so there is no mistake it isn't base64 as 5%4=1
/*static*/ const std::string RsFiles::FILES_URL_FAGMENT_FORWARD = "fragm";
/*static*/ const std::string RsFiles::FILES_URL_NAME_FIELD = "filesName"; /*static*/ const std::string RsFiles::FILES_URL_NAME_FIELD = "filesName";
/*static*/ const std::string RsFiles::FILES_URL_SIZE_FIELD = "filesSize"; /*static*/ const std::string RsFiles::FILES_URL_SIZE_FIELD = "filesSize";
@ -2118,8 +2120,7 @@ const noexcept
{ {
switch(static_cast<RsFilesErrorNum>(ev)) switch(static_cast<RsFilesErrorNum>(ev))
{ {
case RsFilesErrorNum::FILES_HANDLE_NOT_FOUND: // [[fallthrough]]; case RsFilesErrorNum::FILES_HANDLE_NOT_FOUND:
case RsFilesErrorNum::INVALID_FILES_URL:
return std::errc::invalid_argument; return std::errc::invalid_argument;
default: default:
return std::error_condition(ev, *this); return std::error_condition(ev, *this);
@ -2127,7 +2128,8 @@ const noexcept
} }
std::error_condition ftServer::exportFilesLink( std::error_condition ftServer::exportFilesLink(
std::string& link, std::uintptr_t handle, const std::string& baseUrl ) std::string& link, std::uintptr_t handle, bool fragSneak,
const std::string& baseUrl )
{ {
DirDetails tDirDet; DirDetails tDirDet;
if(!requestDirDetails(tDirDet, handle)) if(!requestDirDetails(tDirDet, handle))
@ -2142,10 +2144,13 @@ std::error_condition ftServer::exportFilesLink(
RsUrl tUrl(baseUrl); RsUrl tUrl(baseUrl);
tUrl.setQueryKV(FILES_URL_COUNT_FIELD, tUrl.setQueryKV(FILES_URL_COUNT_FIELD,
std::to_string(tFileTree->mTotalFiles) ) std::to_string(tFileTree->mTotalFiles) )
.setQueryKV(FILES_URL_DATA_FIELD, link)
.setQueryKV(FILES_URL_NAME_FIELD, tDirDet.name) .setQueryKV(FILES_URL_NAME_FIELD, tDirDet.name)
.setQueryKV( FILES_URL_SIZE_FIELD, .setQueryKV( FILES_URL_SIZE_FIELD,
std::to_string(tFileTree->mTotalSize) ); std::to_string(tFileTree->mTotalSize) );
if(fragSneak)
tUrl.setQueryKV(FILES_URL_DATA_FIELD, FILES_URL_FAGMENT_FORWARD)
.setFragment(link);
else tUrl.setQueryKV(FILES_URL_DATA_FIELD, link);
link = tUrl.toString(); link = tUrl.toString();
} }
@ -2159,14 +2164,10 @@ std::error_condition ftServer::parseFilesLink(
rs_view_ptr<const std::string> radixPtr = rs_view_ptr<const std::string> radixPtr =
tUrl.getQueryV(FILES_URL_DATA_FIELD); tUrl.getQueryV(FILES_URL_DATA_FIELD);
if(!radixPtr) radixPtr = &link; if(!radixPtr) radixPtr = &link;
else if(*radixPtr == FILES_URL_FAGMENT_FORWARD) radixPtr = &tUrl.fragment();
std::unique_ptr<RsFileTree> tft = std::unique_ptr<RsFileTree> tft; std::error_condition ec;
RsFileTree::fromBase64(*radixPtr); std::tie(tft, ec) = RsFileTree::fromBase64(*radixPtr);
if(tft) if(tft) collection = *tft;
{ return ec;
collection = *tft;
return std::error_condition();
}
return RsFilesErrorNum::INVALID_FILES_URL;
} }

View File

@ -217,7 +217,7 @@ public:
/// @see RsFiles /// @see RsFiles
std::error_condition exportFilesLink( std::error_condition exportFilesLink(
std::string& link, std::uintptr_t handle, std::string& link, std::uintptr_t handle, bool fragSneak = false,
const std::string& baseUrl = RsFiles::DEFAULT_FILES_BASE_URL const std::string& baseUrl = RsFiles::DEFAULT_FILES_BASE_URL
) override; ) override;

View File

@ -480,6 +480,7 @@ HEADERS += util/folderiterator.h \
util/dnsresolver.h \ util/dnsresolver.h \
util/radix32.h \ util/radix32.h \
util/radix64.h \ util/radix64.h \
util/rsbase64.h \
util/rsinitedptr.h \ util/rsinitedptr.h \
util/rsprint.h \ util/rsprint.h \
util/rsstring.h \ util/rsstring.h \
@ -636,6 +637,7 @@ SOURCES += util/folderiterator.cc \
util/rsrecogn.cc \ util/rsrecogn.cc \
util/rstime.cc \ util/rstime.cc \
util/rsurl.cc \ util/rsurl.cc \
util/rsbase64.cc \
util/rserrno.cc util/rserrno.cc
equals(RS_UPNP_LIB, miniupnpc) { equals(RS_UPNP_LIB, miniupnpc) {

View File

@ -4,7 +4,8 @@
* libretroshare: retroshare core library * * libretroshare: retroshare core library *
* * * *
* Copyright (C) 2008 Robert Fernie <retroshare@lunamutt.com> * * Copyright (C) 2008 Robert Fernie <retroshare@lunamutt.com> *
* Copyright (C) 2018-2019 Gioacchino Mazzurco <gio@eigenlab.org> * * Copyright (C) 2018-2020 Gioacchino Mazzurco <gio@eigenlab.org> *
* Copyright (C) 2019-2020 Asociación Civil Altermundi <info@altermundi.net> *
* * * *
* This program is free software: you can redistribute it and/or modify * * This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License as * * it under the terms of the GNU Lesser General Public License as *
@ -48,7 +49,6 @@ extern RsFiles* rsFiles;
enum class RsFilesErrorNum : int32_t enum class RsFilesErrorNum : int32_t
{ {
FILES_HANDLE_NOT_FOUND = 2004, FILES_HANDLE_NOT_FOUND = 2004,
INVALID_FILES_URL = 2005
}; };
struct RsFilesErrorCategory: std::error_category struct RsFilesErrorCategory: std::error_category
@ -62,8 +62,6 @@ struct RsFilesErrorCategory: std::error_category
{ {
case RsFilesErrorNum::FILES_HANDLE_NOT_FOUND: case RsFilesErrorNum::FILES_HANDLE_NOT_FOUND:
return "Files handle not found"; return "Files handle not found";
case RsFilesErrorNum::INVALID_FILES_URL:
return "Invalid files url";
default: default:
return "Error message for error: " + std::to_string(ev) + return "Error message for error: " + std::to_string(ev) +
" not available in category: " + name(); " not available in category: " + name();
@ -286,21 +284,22 @@ public:
/** /**
* @brief Create a RsFileTree from directory details * @brief Create a RsFileTree from directory details
* @param dd * @param dd directory or file details
* @param remote * @param remote
* @param remove_top_dirs * @param remove_top_dirs
* @return * @return pointer to the created file-tree
*/ */
static std::unique_ptr<RsFileTree> fromDirDetails( static std::unique_ptr<RsFileTree> fromDirDetails(
const DirDetails& dd, bool remote, bool remove_top_dirs = true ); const DirDetails& dd, bool remote, bool remove_top_dirs = true );
/** /**
* @brief Create a RsFileTree from Radix64 representation * @brief Create a RsFileTree from Base64 representation
* @param base64 * @param base64 base64 or base64url string representation of the file-tree
* @return nullptr if on failure, pointer to the created FileTree on success * @return pointer to the parsed file-tree on success, nullptr plus error
* details on failure
*/ */
static std::unique_ptr<RsFileTree> fromBase64( static std::tuple<std::unique_ptr<RsFileTree>, std::error_condition>
const std::string& base64 ); fromBase64(const std::string& base64);
/** @brief Convert to base64 representetion */ /** @brief Convert to base64 representetion */
std::string toBase64() const; std::string toBase64() const;
@ -357,24 +356,25 @@ public:
uint32_t mTotalFiles; uint32_t mTotalFiles;
uint64_t mTotalSize; uint64_t mTotalSize;
~RsFileTree() = default; virtual ~RsFileTree();
/** /**
* @brief Create a RsFileTree from Radix64 representation * @brief Create a RsFileTree from Radix64 representation
* @deprecated kept for retrocompatibility with RetroShare-gui * @deprecated kept for retrocompatibility with RetroShare-gui
* The format is not guardanted to be compatible with the new methods * The format is not compatible with the new methods
* @param radix64_string * @param radix64_string
* @return nullptr if on failure, pointer to the created FileTree on success * @return nullptr if on failure, pointer to the created FileTree on success
*/ */
RS_DEPRECATED RS_DEPRECATED_FOR(fromBase64)
static std::unique_ptr<RsFileTree> fromRadix64( static std::unique_ptr<RsFileTree> fromRadix64(
const std::string& radix64_string ); const std::string& radix64_string );
/** @brief Convert to radix64 representetion /** @brief Convert to radix64 representetion
* @deprecated kept for retrocompatibility with RetroShare-gui * @deprecated kept for retrocompatibility with RetroShare-gui
* The format is not guardanted to be compatible with the new methods * The format is not compatible with the new methods
*/ */
RS_DEPRECATED std::string toRadix64() const; RS_DEPRECATED_FOR(toBase64)
std::string toRadix64() const;
private: private:
/** @deprecated kept for retrocompatibility with RetroShare-gui */ /** @deprecated kept for retrocompatibility with RetroShare-gui */
@ -885,6 +885,11 @@ public:
/// Link query field used to store collection data @see exportFilesLink /// Link query field used to store collection data @see exportFilesLink
static const std::string FILES_URL_DATA_FIELD; static const std::string FILES_URL_DATA_FIELD;
/** Link query FILES_URL_DATA_FIELD field value used to instruct the parser
* to look for the data into the link fragment
* @see exportFilesLink and parseFilesLink */
static const std::string FILES_URL_FAGMENT_FORWARD;
/// Link query field used to store collection name @see exportFilesLink /// Link query field used to store collection name @see exportFilesLink
static const std::string FILES_URL_NAME_FIELD; static const std::string FILES_URL_NAME_FIELD;
@ -896,15 +901,21 @@ public:
* @jsonapi{development} * @jsonapi{development}
* @param[out] link storage for the generated link * @param[out] link storage for the generated link
* @param[in] handle file/directory RetroShare handle * @param[in] handle file/directory RetroShare handle
* @param[in] baseUrl URL into which to sneak in the RetroShare link * @param[in] fragSneak when true the file data is sneaked into fragment
* radix, this is primarly useful to induce applications into making the * instead of FILES_URL_DATA_FIELD query field, this way if using an
* link clickable, or to disguise the RetroShare link into a * http[s] link to pass around a disguised file link a misconfigured host
* "normal" looking web link. If empty the collection data link will be * attempting to visit that link with a web browser will not send the file
* outputted in plain base64 format. * data to the server thus protecting at least some of the privacy of the
* user even in a misconfiguration scenario.
* @param[in] baseUrl URL into which to sneak in the RetroShare file link
* base64, this is primarly useful to induce applications into making the
* link clickable, or to disguise the RetroShare link into a "normal"
* looking web link. If empty the collection data link will be outputted in
* plain base64 format.
* @return error information if some error occurred, 0/SUCCESS otherwise * @return error information if some error occurred, 0/SUCCESS otherwise
*/ */
virtual std::error_condition exportFilesLink( virtual std::error_condition exportFilesLink(
std::string& link, std::uintptr_t handle, std::string& link, std::uintptr_t handle, bool fragSneak = false,
const std::string& baseUrl = RsFiles::DEFAULT_FILES_BASE_URL ) = 0; const std::string& baseUrl = RsFiles::DEFAULT_FILES_BASE_URL ) = 0;
/** /**

View File

@ -26,7 +26,10 @@
#include <vector> #include <vector>
#include <stdint.h> #include <stdint.h>
class Radix64 #include "util/rsdeprecate.h"
/** @deprecated use RsBase64 instead which supports also URL safe encoding */
class RS_DEPRECATED_FOR(RsBase64) Radix64
{ {
public: public:
static std::vector<uint8_t> decode(const std::string& buffer) static std::vector<uint8_t> decode(const std::string& buffer)
@ -195,5 +198,3 @@ again:
return true ; return true ;
} }
}; };

View File

@ -0,0 +1,191 @@
/*******************************************************************************
* *
* libretroshare base64 encoding utilities *
* *
* Copyright (C) 2020 Gioacchino Mazzurco <gio@eigenlab.org> *
* Copyright (C) 2020 Asociación Civil Altermundi <info@altermundi.net> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
#include <cmath>
#include "util/rsbase64.h"
#include "util/rsdebug.h"
#if __cplusplus < 201703L
/* Solve weird undefined reference error with C++ < 17 see:
* https://stackoverflow.com/questions/8016780/undefined-reference-to-static-constexpr-char
*/
/*static*/ decltype(RsBase64::bDict) constexpr RsBase64::bDict;
/*static*/ decltype(RsBase64::uDict) constexpr RsBase64::uDict;
/*static*/ decltype(RsBase64::rDict) constexpr RsBase64::rDict;
/*static*/ decltype(RsBase64::sPad) constexpr RsBase64::sPad;
#endif
/*static*/ void RsBase64::encode(
rs_view_ptr<const uint8_t> data, size_t len, std::string& outString,
bool padding, bool urlSafe )
{
const char* sDict = urlSafe ? uDict : bDict;
// Workaround if input and output are the same buffer.
bool inplace = (outString.data() == reinterpret_cast<const char*>(data));
std::string tBuff;
std::string& outStr = inplace ? tBuff : outString;
auto encSize = encodedSize(len, padding);
outStr.resize(encSize);
char* p = &outStr[0];
for (; len >= 3; len -= 3, data += 3)
{
*p++ = sDict[ (data[0] >> 2) & 077 ];
*p++ = sDict[
(((data[0] << 4) & 060) | ((data[1] >> 4) & 017)) & 077 ];
*p++ = sDict[
(((data[1] << 2) & 074) | ((data[2] >> 6) & 03)) & 077 ];
*p++ = sDict[ data[2] & 077 ];
}
if (len == 2)
{
*p++ = sDict[ (data[0] >> 2) & 077 ];
*p++ = sDict[
(((data[0] << 4) & 060) | ((data[1] >> 4) & 017)) & 077 ];
*p++ = sDict[ ((data[1] << 2) & 074) ];
if(padding) *p++ = sPad;
}
else if (len == 1)
{
*p++ = sDict[ (data[0] >> 2) & 077 ];
*p++ = sDict[ (data[0] << 4) & 060 ];
if(padding) { *p++ = sPad; *p++ = sPad; }
}
if(inplace) outString = tBuff;
}
/*static*/ std::error_condition RsBase64::decode(
const std::string& encoded, std::vector<uint8_t>& decoded )
{
size_t decSize; std::error_condition ec;
std::tie(decSize, ec) = decodedSize(encoded);
if(!decSize || ec) return ec;
size_t encSize = encoded.size();
decoded.resize(decSize);
for (size_t i = 0, o = 0; i < encSize; i += 4, o += 3)
{
char input0 = encoded[i + 0];
char input1 = encoded[i + 1];
/* At the end of the string, missing bytes 2 and 3 are considered
* padding '=' */
char input2 = i + 2 < encoded.size() ? encoded[i + 2] : sPad;
char input3 = i + 3 < encSize ? encoded[i + 3] : sPad;
// If any unknown characters appear, it's an error.
if(!( isBase64Char(input0) && isBase64Char(input1) &&
isBase64Char(input2) && isBase64Char(input3) ))
return std::errc::argument_out_of_domain;
/* If padding appears anywhere but the last 1 or 2 characters, or if
* it appears but encoded.size() % 4 != 0, it's an error. */
bool at_end = (i + 4 >= encSize);
if ( (input0 == sPad) || (input1 == sPad) ||
( input2 == sPad && !at_end ) ||
( input2 == sPad && input3 != sPad ) ||
( input3 == sPad && !at_end) )
return std::errc::illegal_byte_sequence;
uint32_t b0 = rDict[static_cast<uint8_t>(input0)] & 0x3f;
uint32_t b1 = rDict[static_cast<uint8_t>(input1)] & 0x3f;
uint32_t b2 = rDict[static_cast<uint8_t>(input2)] & 0x3f;
uint32_t b3 = rDict[static_cast<uint8_t>(input3)] & 0x3f;
uint32_t stream = (b0 << 18) | (b1 << 12) | (b2 << 6) | b3;
decoded[o + 0] = (stream >> 16) & 0xFF;
if (input2 != sPad) decoded[o + 1] = (stream >> 8) & 0xFF;
/* If there are any stale bits in this from input1, the text is
* malformed. */
else if (((stream >> 8) & 0xFF) != 0)
return std::errc::invalid_argument;
if (input3 != sPad) decoded[o + 2] = (stream >> 0) & 0xFF;
/* If there are any stale bits in this from input2, the text is
* malformed. */
else if (((stream >> 0) & 0xFF) != 0)
return std::errc::invalid_argument;
}
return std::error_condition();
}
/*static*/ size_t RsBase64::encodedSize(size_t decodedSize, bool padding)
{
if(padding) return 4 * (decodedSize + 2) / 3;
return static_cast<size_t>(
std::ceil(4L * static_cast<double>(decodedSize) / 3L) );
}
/*static*/ std::tuple<size_t, std::error_condition> RsBase64::decodedSize(
const std::string& input )
{
const auto success = [](size_t val)
{ return std::make_tuple(val, std::error_condition()); };
if(input.empty()) return success(0);
auto mod = input.size() % 4;
if(mod == 1) std::make_tuple(0, std::errc::invalid_argument);
size_t padded_size = ((input.size() + 3) / 4) * 3;
if (mod >= 2 || (mod == 0 && input[input.size() - 1] == sPad))
{
/* If the last byte is '=', or the input size % 4 is 2 or 3 (thus
* there are implied '='s), then the actual size is 1-2 bytes
* smaller. */
if ( mod == 2 || (mod == 0 && input[input.size() - 2] == sPad) )
{
/* If the second-to-last byte is also '=', or the input
* size % 4 is 2 (implying a second '='), then the actual size
* is 2 bytes smaller. */
return success(padded_size - 2);
}
else
{
/* Otherwise it's just the last character and the actual size is
* 1 byte smaller. */
return success(padded_size - 1);
}
}
return success(padded_size);
}
/*static*/ size_t RsBase64::stripInvalid(
const std::string& in, std::string& out )
{
size_t strippedCnt = 0;
auto inSize = in.size();
out.resize(inSize);
for(size_t i = 0; i < inSize; ++i)
{
if(isBase64Char(in[i])) out[i-strippedCnt] = in[i];
else ++strippedCnt;
}
out.resize(inSize-strippedCnt);
return strippedCnt;
}

View File

@ -0,0 +1,139 @@
/*******************************************************************************
* *
* libretroshare base64 encoding utilities *
* *
* Copyright (C) 2020 Gioacchino Mazzurco <gio@eigenlab.org> *
* Copyright (C) 2020 Asociación Civil Altermundi <info@altermundi.net> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
#pragma once
#include <string>
#include <vector>
#include <cstdint>
#include <system_error>
#include <tuple>
#include "util/rsmemory.h"
/**
* Implement methods to encode e decode to base64 format as per RFC 4648
* This implementation support also the file name and URL safe base64url format
* @see https://tools.ietf.org/html/rfc4648#section-5
*/
class RsBase64
{
public:
/// Enable base64url by default
static constexpr bool DEFAULT_URL_SAFE = true;
/// Disable padding by default
static constexpr bool DEFAULT_PADDING = false;
/**
* @brief Encode arbitrary data to base64
* @param[in] data pointer to the input data buffer
* @param[in] len lenght of the input buffer
* @param[out] outString storage for the resulting base64 encoded string
* @param[in] padding set to true to enable padding to 32 bits
* @param[in] urlSafe pass true for base64url format, false for base64 format
*/
static void encode(
rs_view_ptr<const uint8_t> data, size_t len,
std::string& outString,
bool padding = DEFAULT_PADDING, bool urlSafe = DEFAULT_URL_SAFE );
/**
* @brief Decode data from a base64 encoded string
* @param[in] encoded encoded string
* @param[out] decoded storage for decoded data
* @return success or error details
*/
static std::error_condition decode(
const std::string& encoded, std::vector<uint8_t>& decoded );
/**
* Remove invalid characters from base64 encoded string.
* Often when copy and pasting from one progam to another long base64
* strings, new lines, spaces or other characters end up polluting the
* original text. This function is useful to cleanup the pollution before
* attempting to decode the message.
* @param in input string
* @param out storage for cleaned string. In-place operation in supported so
* the same input string may be passed.
* @return count of stripped invalid characters
*/
static size_t stripInvalid(const std::string& in, std::string& out);
/**
* Calculate how much bytes are needed to store the base64 encoded version
* of some data.
* @param decodedSize size of the original decoded data
* @param padding true to enable base64 padding
* @return how much bytes would take to store the encoded version
*/
static size_t encodedSize(
size_t decodedSize, bool padding = DEFAULT_PADDING );
/**
* @brief Calculate how much space is needed to store the decoded version of
* a base64 encoded string
* @param input encoded string
* @return decoded size, plus error information on failure
*/
static std::tuple<size_t, std::error_condition> decodedSize(
const std::string& input );
private:
/// base64 conversion table
static constexpr char bDict[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/// base64url conversion table
static constexpr char uDict[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
/// This reverse table supports both base64 and base64url
static constexpr int8_t rDict[256] = {
/* index +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 */
/* 0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* 16 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* 32 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63,
/* 48 */ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, 0, -1, -1,
/* 64 */ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
/* 80 */ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63,
/* 96 */ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
/* 112 */ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
/* 128 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* 144 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* 160 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* 176 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* 192 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* 208 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* 224 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* 240 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
};
/// base64 padding character
static constexpr char sPad = '=';
/** Check if given character is valid either for base64 or for base64url
* @param c character to check
* @return true if valid false otherwise
*/
static inline bool isBase64Char(char c)
{ return rDict[static_cast<uint8_t>(c)] >= 0; }
};