Base64: Implement the "base64url" encoding and the stripping of '='

The "base64url" encoding is defined in RFC 4648, which is the newest
version of Base64. There are also a few situations where the ending
'=' is not desired.

Change-Id: I9bb9fa55817b57d125189e4e795d6fde97caea6d
Reviewed-by: Richard J. Moore <rich@kde.org>
bb10
Thiago Macieira 2013-07-13 12:04:40 +02:00 committed by The Qt Project
parent 3154e1ccaa
commit cc74159702
5 changed files with 151 additions and 14 deletions

View File

@ -352,6 +352,14 @@ QByteArray text("Qt is great!");
text.toBase64(); // returns "UXQgaXMgZ3JlYXQh"
//! [39]
//! [39bis]
QByteArray text("<p>Hello?</p>");
text.toBase64(QByteArray::Base64 | QByteArray::OmitTrailingEquals); // returns "PHA+SGVsbG8/PC9wPg"
text.toBase64(QByteArray::Base64); // returns "PHA+SGVsbG8/PC9wPg=="
text.toBase64(QByteArray::Base64Url); // returns "PHA-SGVsbG8_PC9wPg=="
text.toBase64(QByteArray::Base64Url | QByteArray::OmitTrailingEquals); // returns "PHA-SGVsbG8_PC9wPg"
//! [39bis]
//! [40]
QByteArray ba;
@ -394,6 +402,11 @@ QByteArray text = QByteArray::fromBase64("UXQgaXMgZ3JlYXQh");
text.data(); // returns "Qt is great!"
//! [44]
//! [44bis]
QByteArray::fromBase64("PHA+SGVsbG8/PC9wPg==", QByteArray::Base64Encoding); // returns "<p>Hello?</p>"
QByteArray::fromBase64("PHA-SGVsbG8_PC9wPg==", QByteArray::Base64UrlEncoding); // returns "<p>Hello?</p>"
//! [44bis]
//! [45]
QByteArray text = QByteArray::fromHex("517420697320677265617421");

View File

@ -66,6 +66,11 @@
\title RFC 2045
*/
/*!
\externalpage http://www.ietf.org/rfc/rfc4648.txt
\title RFC 4648
*/
/*!
\externalpage http://www.iana.org/assignments/character-sets/character-sets.xml
\title IANA character-sets encoding file

View File

@ -795,6 +795,27 @@ static inline char qToLower(char c)
\sa QString, QBitArray
*/
/*!
\enum QByteArray::Base64Option
\since 5.2
This enum contains the options available for encoding and decoding Base64.
Base64 is defined by \l{RFC 4648}, with the following options:
\value Base64Encoding (default) The regular Base64 alphabet, called simply "base64"
\value Base64UrlEncoding An alternate alphabet, called "base64url", which replaces two
characters in the alphabet to be more friendly to URLs.
\value KeepTrailingEquals (default) Keeps the trailing padding equal signs at the end
of the encoded data, so the data is always a size multiple of
four.
\value OmitTrailingEquals Omits adding the padding equal signs at the end of the encoded
data.
QByteArray::fromBase64() ignores the KeepTrailingEquals and
OmitTrailingEquals options and will not flag errors in case they are
missing or if there are too many of them.
*/
/*! \fn QByteArray::iterator QByteArray::begin()
\internal
@ -3539,14 +3560,34 @@ float QByteArray::toFloat(bool *ok) const
\snippet code/src_corelib_tools_qbytearray.cpp 39
The algorithm used to encode Base64-encoded data is defined in \l{RFC 2045}.
The algorithm used to encode Base64-encoded data is defined in \l{RFC 4648}.
\sa fromBase64()
*/
QByteArray QByteArray::toBase64() const
{
const char alphabet[] = "ABCDEFGH" "IJKLMNOP" "QRSTUVWX" "YZabcdef"
"ghijklmn" "opqrstuv" "wxyz0123" "456789+/";
return toBase64(Base64Encoding);
}
/*!
\since 5.2
\overload
Returns a copy of the byte array, encoded using the options \a options.
\snippet code/src_corelib_tools_qbytearray.cpp 39bis
The algorithm used to encode Base64-encoded data is defined in \l{RFC 4648}.
\sa fromBase64()
*/
QByteArray QByteArray::toBase64(Base64Options options) const
{
const char alphabet_base64[] = "ABCDEFGH" "IJKLMNOP" "QRSTUVWX" "YZabcdef"
"ghijklmn" "opqrstuv" "wxyz0123" "456789+/";
const char alphabet_base64url[] = "ABCDEFGH" "IJKLMNOP" "QRSTUVWX" "YZabcdef"
"ghijklmn" "opqrstuv" "wxyz0123" "456789-_";
const char *const alphabet = options & Base64UrlEncoding ? alphabet_base64url : alphabet_base64;
const char padchar = '=';
int padlen = 0;
@ -3575,14 +3616,18 @@ QByteArray QByteArray::toBase64() const
*out++ = alphabet[j];
*out++ = alphabet[k];
if (padlen > 1)
*out++ = padchar;
else
if (padlen > 1) {
if ((options & OmitTrailingEquals) == 0)
*out++ = padchar;
} else {
*out++ = alphabet[l];
if (padlen > 0)
*out++ = padchar;
else
}
if (padlen > 0) {
if ((options & OmitTrailingEquals) == 0)
*out++ = padchar;
} else {
*out++ = alphabet[m];
}
}
tmp.truncate(out - tmp.data());
@ -3941,11 +3986,33 @@ QByteArray &QByteArray::setRawData(const char *data, uint size)
\snippet code/src_corelib_tools_qbytearray.cpp 44
The algorithm used to decode Base64-encoded data is defined in \l{RFC 2045}.
The algorithm used to decode Base64-encoded data is defined in \l{RFC 4648}.
\sa toBase64()
*/
QByteArray QByteArray::fromBase64(const QByteArray &base64)
{
return fromBase64(base64, Base64Encoding);
}
/*!
\since 5.2
\overload
Returns a decoded copy of the Base64 array \a base64, using the alphabet
defined by \a options. Input is not checked for validity; invalid
characters in the input are skipped, enabling the decoding process to
continue with subsequent characters.
For example:
\snippet code/src_corelib_tools_qbytearray.cpp 44bis
The algorithm used to decode Base64-encoded data is defined in \l{RFC 4648}.
\sa toBase64()
*/
QByteArray QByteArray::fromBase64(const QByteArray &base64, Base64Options options)
{
unsigned int buf = 0;
int nbits = 0;
@ -3962,9 +4029,13 @@ QByteArray QByteArray::fromBase64(const QByteArray &base64)
d = ch - 'a' + 26;
else if (ch >= '0' && ch <= '9')
d = ch - '0' + 52;
else if (ch == '+')
else if (ch == '+' && (options & Base64UrlEncoding) == 0)
d = 62;
else if (ch == '/')
else if (ch == '-' && (options & Base64UrlEncoding) != 0)
d = 62;
else if (ch == '/' && (options & Base64UrlEncoding) == 0)
d = 63;
else if (ch == '_' && (options & Base64UrlEncoding) != 0)
d = 63;
else
d = -1;

View File

@ -173,6 +173,15 @@ private:
typedef QTypedArrayData<char> Data;
public:
enum Base64Option {
Base64Encoding = 0,
Base64UrlEncoding = 1,
KeepTrailingEquals = 0,
OmitTrailingEquals = 2
};
Q_DECLARE_FLAGS(Base64Options, Base64Option)
inline QByteArray();
QByteArray(const char *, int size = -1);
QByteArray(int size, char c);
@ -317,7 +326,8 @@ public:
qulonglong toULongLong(bool *ok = 0, int base = 10) const;
float toFloat(bool *ok = 0) const;
double toDouble(bool *ok = 0) const;
QByteArray toBase64() const;
QByteArray toBase64(Base64Options options) const;
QByteArray toBase64() const; // ### Qt6 merge with previous
QByteArray toHex() const;
QByteArray toPercentEncoding(const QByteArray &exclude = QByteArray(),
const QByteArray &include = QByteArray(),
@ -339,7 +349,8 @@ public:
static QByteArray number(qulonglong, int base = 10);
static QByteArray number(double, char f = 'g', int prec = 6);
static QByteArray fromRawData(const char *, int size);
static QByteArray fromBase64(const QByteArray &base64);
static QByteArray fromBase64(const QByteArray &base64, Base64Options options);
static QByteArray fromBase64(const QByteArray &base64); // ### Qt6 merge with previous
static QByteArray fromHex(const QByteArray &hexEncoded);
static QByteArray fromPercentEncoding(const QByteArray &pctEncoded, char percent = '%');
@ -392,6 +403,8 @@ public:
inline DataPtr &data_ptr() { return d; }
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QByteArray::Base64Options)
inline QByteArray::QByteArray(): d(Data::sharedNull()) { }
inline QByteArray::~QByteArray() { if (!d->ref.deref()) Data::deallocate(d); }
inline int QByteArray::size() const

View File

@ -614,6 +614,24 @@ void tst_QByteArray::base64()
QByteArray arr64 = rawdata.toBase64();
QCOMPARE(arr64, base64);
arr64 = rawdata.toBase64(QByteArray::Base64Encoding);
QCOMPARE(arr64, base64);
QByteArray base64noequals = base64;
base64noequals.replace('=', "");
arr64 = rawdata.toBase64(QByteArray::Base64Encoding | QByteArray::OmitTrailingEquals);
QCOMPARE(arr64, base64noequals);
QByteArray base64url = base64;
base64url.replace('/', '_').replace('+', '-');
arr64 = rawdata.toBase64(QByteArray::Base64UrlEncoding);
QCOMPARE(arr64, base64url);
QByteArray base64urlnoequals = base64url;
base64urlnoequals.replace('=', "");
arr64 = rawdata.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
QCOMPARE(arr64, base64urlnoequals);
}
//different from the previous test as the input are invalid
@ -662,6 +680,23 @@ void tst_QByteArray::fromBase64()
QByteArray arr = QByteArray::fromBase64(base64);
QCOMPARE(arr, rawdata);
arr = QByteArray::fromBase64(base64, QByteArray::Base64Encoding);
QCOMPARE(arr, rawdata);
// try "base64url" encoding
QByteArray base64url = base64;
base64url.replace('/', '_').replace('+', '-');
arr = QByteArray::fromBase64(base64url, QByteArray::Base64UrlEncoding);
QCOMPARE(arr, rawdata);
if (base64 != base64url) {
// check that the invalid decodings fail
arr = QByteArray::fromBase64(base64, QByteArray::Base64UrlEncoding);
QVERIFY(arr != rawdata);
arr = QByteArray::fromBase64(base64url, QByteArray::Base64Encoding);
QVERIFY(arr != rawdata);
}
}
void tst_QByteArray::qvsnprintf()