mirror of
https://github.com/RetroShare/RetroShare.git
synced 2024-12-29 01:16:20 -05:00
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:
parent
55d466f79b
commit
d203f31d0c
@ -22,10 +22,12 @@
|
||||
#include <iomanip>
|
||||
|
||||
#include "util/radix64.h"
|
||||
#include "util/rsbase64.h"
|
||||
#include "util/rsdir.h"
|
||||
#include "retroshare/rsfiles.h"
|
||||
#include "file_sharing_defaults.h"
|
||||
#include "filelist_io.h"
|
||||
#include "serialiser/rstypeserializer.h"
|
||||
|
||||
void RsFileTree::DirData::serial_process(
|
||||
RsGenericSerializer::SerializeJob j,
|
||||
@ -45,17 +47,24 @@ void RsFileTree::FileData::serial_process(
|
||||
RS_SERIAL_PROCESS(hash);
|
||||
}
|
||||
|
||||
/*static*/ std::unique_ptr<RsFileTree> RsFileTree::fromBase64(
|
||||
const std::string& base64 )
|
||||
/*static*/ std::tuple<std::unique_ptr<RsFileTree>, std::error_condition>
|
||||
RsFileTree::fromBase64(const std::string& base64)
|
||||
{
|
||||
std::unique_ptr<RsFileTree> ft(new RsFileTree);
|
||||
std::vector<uint8_t> mem = Radix64::decode(base64);
|
||||
const auto failure = [](std::error_condition ec)
|
||||
{ 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(
|
||||
mem.data(), static_cast<uint32_t>(mem.size()) );
|
||||
std::unique_ptr<RsFileTree> ft(new RsFileTree);
|
||||
ft->serial_process(
|
||||
RsGenericSerializer::SerializeJob::DESERIALIZE, ctx);
|
||||
if(ctx.mOk) return ft;
|
||||
return nullptr;
|
||||
if(ctx.mOk) return std::make_tuple(std::move(ft), std::error_condition());
|
||||
|
||||
return failure(std::errc::invalid_argument);
|
||||
}
|
||||
|
||||
std::string RsFileTree::toBase64() const
|
||||
@ -64,8 +73,6 @@ std::string RsFileTree::toBase64() const
|
||||
RsFileTree* ncThis = const_cast<RsFileTree*>(this);
|
||||
ncThis->serial_process(
|
||||
RsGenericSerializer::SerializeJob::SIZE_ESTIMATE, ctx );
|
||||
RsDbg() << __PRETTY_FUNCTION__ << " ctx.mOffset: " << ctx.mOffset
|
||||
<< std::endl;
|
||||
|
||||
std::vector<uint8_t> buf(ctx.mOffset);
|
||||
ctx.mSize = ctx.mOffset; ctx.mOffset = 0; ctx.mData = buf.data();
|
||||
@ -73,7 +80,7 @@ std::string RsFileTree::toBase64() const
|
||||
ncThis->serial_process(
|
||||
RsGenericSerializer::SerializeJob::SERIALIZE, ctx );
|
||||
std::string result;
|
||||
Radix64::encode(ctx.mData, ctx.mSize, result);
|
||||
RsBase64::encode(ctx.mData, ctx.mSize, result, false, true);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -94,7 +101,7 @@ std::unique_ptr<RsFileTree> RsFileTree::fromRadix64(
|
||||
std::unique_ptr<RsFileTree> ft(new RsFileTree);
|
||||
std::vector<uint8_t> mem = Radix64::decode(radix64_string);
|
||||
if(ft->deserialise(mem.data(), static_cast<uint32_t>(mem.size())))
|
||||
return std::move(ft);
|
||||
return ft;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -353,3 +360,5 @@ bool RsFileTree::serialise(unsigned char *& buffer,uint32_t& buffer_size) const
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
RsFileTree::~RsFileTree() = default;
|
||||
|
@ -72,6 +72,8 @@
|
||||
"retroshare:///files";
|
||||
/*static*/ const std::string RsFiles::FILES_URL_COUNT_FIELD = "filesCount";
|
||||
/*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_SIZE_FIELD = "filesSize";
|
||||
|
||||
@ -2118,8 +2120,7 @@ const noexcept
|
||||
{
|
||||
switch(static_cast<RsFilesErrorNum>(ev))
|
||||
{
|
||||
case RsFilesErrorNum::FILES_HANDLE_NOT_FOUND: // [[fallthrough]];
|
||||
case RsFilesErrorNum::INVALID_FILES_URL:
|
||||
case RsFilesErrorNum::FILES_HANDLE_NOT_FOUND:
|
||||
return std::errc::invalid_argument;
|
||||
default:
|
||||
return std::error_condition(ev, *this);
|
||||
@ -2127,7 +2128,8 @@ const noexcept
|
||||
}
|
||||
|
||||
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;
|
||||
if(!requestDirDetails(tDirDet, handle))
|
||||
@ -2142,10 +2144,13 @@ std::error_condition ftServer::exportFilesLink(
|
||||
RsUrl tUrl(baseUrl);
|
||||
tUrl.setQueryKV(FILES_URL_COUNT_FIELD,
|
||||
std::to_string(tFileTree->mTotalFiles) )
|
||||
.setQueryKV(FILES_URL_DATA_FIELD, link)
|
||||
.setQueryKV(FILES_URL_NAME_FIELD, tDirDet.name)
|
||||
.setQueryKV( FILES_URL_SIZE_FIELD,
|
||||
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();
|
||||
}
|
||||
|
||||
@ -2159,14 +2164,10 @@ std::error_condition ftServer::parseFilesLink(
|
||||
rs_view_ptr<const std::string> radixPtr =
|
||||
tUrl.getQueryV(FILES_URL_DATA_FIELD);
|
||||
if(!radixPtr) radixPtr = &link;
|
||||
else if(*radixPtr == FILES_URL_FAGMENT_FORWARD) radixPtr = &tUrl.fragment();
|
||||
|
||||
std::unique_ptr<RsFileTree> tft =
|
||||
RsFileTree::fromBase64(*radixPtr);
|
||||
if(tft)
|
||||
{
|
||||
collection = *tft;
|
||||
return std::error_condition();
|
||||
}
|
||||
|
||||
return RsFilesErrorNum::INVALID_FILES_URL;
|
||||
std::unique_ptr<RsFileTree> tft; std::error_condition ec;
|
||||
std::tie(tft, ec) = RsFileTree::fromBase64(*radixPtr);
|
||||
if(tft) collection = *tft;
|
||||
return ec;
|
||||
}
|
||||
|
@ -217,7 +217,7 @@ public:
|
||||
|
||||
/// @see RsFiles
|
||||
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
|
||||
) override;
|
||||
|
||||
|
@ -480,6 +480,7 @@ HEADERS += util/folderiterator.h \
|
||||
util/dnsresolver.h \
|
||||
util/radix32.h \
|
||||
util/radix64.h \
|
||||
util/rsbase64.h \
|
||||
util/rsinitedptr.h \
|
||||
util/rsprint.h \
|
||||
util/rsstring.h \
|
||||
@ -636,6 +637,7 @@ SOURCES += util/folderiterator.cc \
|
||||
util/rsrecogn.cc \
|
||||
util/rstime.cc \
|
||||
util/rsurl.cc \
|
||||
util/rsbase64.cc \
|
||||
util/rserrno.cc
|
||||
|
||||
equals(RS_UPNP_LIB, miniupnpc) {
|
||||
|
@ -4,7 +4,8 @@
|
||||
* libretroshare: retroshare core library *
|
||||
* *
|
||||
* 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 *
|
||||
* it under the terms of the GNU Lesser General Public License as *
|
||||
@ -48,7 +49,6 @@ extern RsFiles* rsFiles;
|
||||
enum class RsFilesErrorNum : int32_t
|
||||
{
|
||||
FILES_HANDLE_NOT_FOUND = 2004,
|
||||
INVALID_FILES_URL = 2005
|
||||
};
|
||||
|
||||
struct RsFilesErrorCategory: std::error_category
|
||||
@ -62,8 +62,6 @@ struct RsFilesErrorCategory: std::error_category
|
||||
{
|
||||
case RsFilesErrorNum::FILES_HANDLE_NOT_FOUND:
|
||||
return "Files handle not found";
|
||||
case RsFilesErrorNum::INVALID_FILES_URL:
|
||||
return "Invalid files url";
|
||||
default:
|
||||
return "Error message for error: " + std::to_string(ev) +
|
||||
" not available in category: " + name();
|
||||
@ -286,21 +284,22 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Create a RsFileTree from directory details
|
||||
* @param dd
|
||||
* @param dd directory or file details
|
||||
* @param remote
|
||||
* @param remove_top_dirs
|
||||
* @return
|
||||
* @return pointer to the created file-tree
|
||||
*/
|
||||
static std::unique_ptr<RsFileTree> fromDirDetails(
|
||||
const DirDetails& dd, bool remote, bool remove_top_dirs = true );
|
||||
|
||||
/**
|
||||
* @brief Create a RsFileTree from Radix64 representation
|
||||
* @param base64
|
||||
* @return nullptr if on failure, pointer to the created FileTree on success
|
||||
* @brief Create a RsFileTree from Base64 representation
|
||||
* @param base64 base64 or base64url string representation of the file-tree
|
||||
* @return pointer to the parsed file-tree on success, nullptr plus error
|
||||
* details on failure
|
||||
*/
|
||||
static std::unique_ptr<RsFileTree> fromBase64(
|
||||
const std::string& base64 );
|
||||
static std::tuple<std::unique_ptr<RsFileTree>, std::error_condition>
|
||||
fromBase64(const std::string& base64);
|
||||
|
||||
/** @brief Convert to base64 representetion */
|
||||
std::string toBase64() const;
|
||||
@ -357,24 +356,25 @@ public:
|
||||
uint32_t mTotalFiles;
|
||||
uint64_t mTotalSize;
|
||||
|
||||
~RsFileTree() = default;
|
||||
virtual ~RsFileTree();
|
||||
|
||||
/**
|
||||
* @brief Create a RsFileTree from Radix64 representation
|
||||
* @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
|
||||
* @return nullptr if on failure, pointer to the created FileTree on success
|
||||
*/
|
||||
RS_DEPRECATED
|
||||
RS_DEPRECATED_FOR(fromBase64)
|
||||
static std::unique_ptr<RsFileTree> fromRadix64(
|
||||
const std::string& radix64_string );
|
||||
|
||||
/** @brief Convert to radix64 representetion
|
||||
* @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:
|
||||
/** @deprecated kept for retrocompatibility with RetroShare-gui */
|
||||
@ -885,6 +885,11 @@ public:
|
||||
/// Link query field used to store collection data @see exportFilesLink
|
||||
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
|
||||
static const std::string FILES_URL_NAME_FIELD;
|
||||
|
||||
@ -896,15 +901,21 @@ public:
|
||||
* @jsonapi{development}
|
||||
* @param[out] link storage for the generated link
|
||||
* @param[in] handle file/directory RetroShare handle
|
||||
* @param[in] baseUrl URL into which to sneak in the RetroShare link
|
||||
* radix, 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.
|
||||
* @param[in] fragSneak when true the file data is sneaked into fragment
|
||||
* instead of FILES_URL_DATA_FIELD query field, this way if using an
|
||||
* http[s] link to pass around a disguised file link a misconfigured host
|
||||
* attempting to visit that link with a web browser will not send the file
|
||||
* 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
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
|
@ -26,7 +26,10 @@
|
||||
#include <vector>
|
||||
#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:
|
||||
static std::vector<uint8_t> decode(const std::string& buffer)
|
||||
@ -195,5 +198,3 @@ again:
|
||||
return true ;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
191
libretroshare/src/util/rsbase64.cc
Normal file
191
libretroshare/src/util/rsbase64.cc
Normal 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;
|
||||
}
|
139
libretroshare/src/util/rsbase64.h
Normal file
139
libretroshare/src/util/rsbase64.h
Normal 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; }
|
||||
};
|
Loading…
Reference in New Issue
Block a user