Base64::Decode throws std::runtime_error on failure. (#3305)

* Throw an exception when base64 decode has bogus inputs

Co-authored-by: Ahson Khan <ahkha@microsoft.com>
This commit is contained in:
Larry Osterman 2022-02-08 09:34:00 -08:00 committed by GitHub
parent 16b4670a8c
commit 60c075a64e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 76 additions and 3 deletions

View File

@ -15,6 +15,9 @@
- Fixed `Azure::DateTime::Parse()` validation if the result is going to exceed `9999-12-31T23:59:59.9999999` due to time zone, leap second, or fractional digits rounding up adjustments. - Fixed `Azure::DateTime::Parse()` validation if the result is going to exceed `9999-12-31T23:59:59.9999999` due to time zone, leap second, or fractional digits rounding up adjustments.
- [[#3224]](https://github.com/Azure/azure-sdk-for-cpp/issues/3224) Fixed intermittent crash on macOS when logging is turned on. - [[#3224]](https://github.com/Azure/azure-sdk-for-cpp/issues/3224) Fixed intermittent crash on macOS when logging is turned on.
- The `Base64::Decode` API will throw a `std::runtime_error` exception if presented with invalid inputs.
### Other Changes
## 1.3.1 (2021-11-05) ## 1.3.1 (2021-11-05)

View File

@ -382,9 +382,15 @@ static void Base64WriteThreeLowOrderBytes(std::vector<uint8_t>::iterator destina
std::vector<uint8_t> Base64Decode(const std::string& text) std::vector<uint8_t> Base64Decode(const std::string& text)
{ {
auto inputSize = text.size(); auto inputSize = text.size();
if (inputSize < 4) if (inputSize % 4 != 0)
{ {
return std::vector<uint8_t>(0); throw std::runtime_error("Unexpected end of Base64 encoded string.");
}
// An empty input should result in an empty output.
if (inputSize == 0)
{
return std::vector<uint8_t>();
} }
size_t sourceIndex = 0; size_t sourceIndex = 0;
@ -406,6 +412,10 @@ std::vector<uint8_t> Base64Decode(const std::string& text)
while (sourceIndex + 4 < inputSize) while (sourceIndex + 4 < inputSize)
{ {
int64_t result = Base64Decode(inputPtr + sourceIndex); int64_t result = Base64Decode(inputPtr + sourceIndex);
if (result < 0)
{
throw std::runtime_error("Unexpected character in Base64 encoded string");
}
Base64WriteThreeLowOrderBytes(destinationPtr, result); Base64WriteThreeLowOrderBytes(destinationPtr, result);
destinationPtr += 3; destinationPtr += 3;
sourceIndex += 4; sourceIndex += 4;
@ -436,6 +446,11 @@ std::vector<uint8_t> Base64Decode(const std::string& text)
i0 |= i3; i0 |= i3;
i0 |= i2; i0 |= i2;
if (i0 < 0)
{
throw std::runtime_error("Unexpected character in Base64 encoded string");
}
Base64WriteThreeLowOrderBytes(destinationPtr, i0); Base64WriteThreeLowOrderBytes(destinationPtr, i0);
destinationPtr += 3; destinationPtr += 3;
} }
@ -446,6 +461,10 @@ std::vector<uint8_t> Base64Decode(const std::string& text)
i2 <<= 6; i2 <<= 6;
i0 |= i2; i0 |= i2;
if (i0 < 0)
{
throw std::runtime_error("Unexpected character in Base64 encoded string");
}
destinationPtr[1] = static_cast<uint8_t>(i0 >> 8); destinationPtr[1] = static_cast<uint8_t>(i0 >> 8);
destinationPtr[0] = static_cast<uint8_t>(i0 >> 16); destinationPtr[0] = static_cast<uint8_t>(i0 >> 16);
@ -453,6 +472,11 @@ std::vector<uint8_t> Base64Decode(const std::string& text)
} }
else else
{ {
if (i0 < 0)
{
throw std::runtime_error("Unexpected character in Base64 encoded string");
}
destinationPtr[0] = static_cast<uint8_t>(i0 >> 16); destinationPtr[0] = static_cast<uint8_t>(i0 >> 16);
destinationPtr += 1; destinationPtr += 1;
} }

View File

@ -125,3 +125,49 @@ TEST(Base64, Roundtrip)
EXPECT_EQ(Convert::Base64Decode(Convert::Base64Encode(data)), data); EXPECT_EQ(Convert::Base64Decode(Convert::Base64Encode(data)), data);
} }
} }
TEST(Base64, ValidDecode)
{
// cspell::disable
EXPECT_NO_THROW(Convert::Base64Decode(Convert::Base64Encode(std::vector<uint8_t>{})));
EXPECT_NO_THROW(Convert::Base64Decode(""));
EXPECT_NO_THROW(Convert::Base64Decode("aa=="));
EXPECT_NO_THROW(Convert::Base64Decode("aaa="));
// cspell::enable
}
TEST(Base64, InvalidDecode)
{
// cspell::disable
EXPECT_THROW(Convert::Base64Decode("a"), std::runtime_error);
EXPECT_THROW(Convert::Base64Decode("aa"), std::runtime_error);
EXPECT_THROW(Convert::Base64Decode("aaa"), std::runtime_error);
EXPECT_THROW(Convert::Base64Decode("a==="), std::runtime_error);
EXPECT_THROW(Convert::Base64Decode("===="), std::runtime_error);
EXPECT_THROW(Convert::Base64Decode("@#!%"), std::runtime_error);
EXPECT_THROW(Convert::Base64Decode("ABCD%GA="), std::runtime_error);
EXPECT_THROW(Convert::Base64Decode("ABCDE^A="), std::runtime_error);
EXPECT_THROW(Convert::Base64Decode("ABCDEF&="), std::runtime_error);
EXPECT_THROW(Convert::Base64Decode("ABD%GA=="), std::runtime_error);
EXPECT_THROW(Convert::Base64Decode("ABDE^A=="), std::runtime_error);
EXPECT_THROW(Convert::Base64Decode("ABDEF&=="), std::runtime_error);
EXPECT_THROW(Convert::Base64Decode("AD%GA==="), std::runtime_error);
EXPECT_THROW(Convert::Base64Decode("ADE^A==="), std::runtime_error);
EXPECT_THROW(Convert::Base64Decode("ADEF&==="), std::runtime_error);
EXPECT_THROW(Convert::Base64Decode("ABCD===="), std::runtime_error);
EXPECT_THROW(Convert::Base64Decode("ADEF====="), std::runtime_error);
EXPECT_THROW(Convert::Base64Decode("ADEF======"), std::runtime_error);
EXPECT_THROW(Convert::Base64Decode("QQ======"), std::runtime_error);
EXPECT_THROW(Convert::Base64Decode("AB===CD="), std::runtime_error);
EXPECT_THROW(Convert::Base64Decode("AB==CD=="), std::runtime_error);
EXPECT_THROW(Convert::Base64Decode("AB=CD==="), std::runtime_error);
EXPECT_THROW(Convert::Base64Decode("AB====CD"), std::runtime_error);
EXPECT_THROW(Convert::Base64Decode("AD=====EF"), std::runtime_error);
EXPECT_THROW(Convert::Base64Decode("AD======EF"), std::runtime_error);
// cspell::enable
}

View File

@ -33,7 +33,7 @@ TEST(SimplifiedHeader, core)
EXPECT_NO_THROW(Azure::Core::Context c); EXPECT_NO_THROW(Azure::Core::Context c);
EXPECT_NO_THROW(Azure::DateTime(2020, 11, 03, 15, 30, 44)); EXPECT_NO_THROW(Azure::DateTime(2020, 11, 03, 15, 30, 44));
EXPECT_NO_THROW(Azure::ETag e); EXPECT_NO_THROW(Azure::ETag e);
EXPECT_NO_THROW(Azure::Core::Convert::Base64Decode("foo")); EXPECT_NO_THROW(Azure::Core::Convert::Base64Decode("foo="));
EXPECT_NO_THROW(Azure::Core::Cryptography::Md5Hash m); EXPECT_NO_THROW(Azure::Core::Cryptography::Md5Hash m);
EXPECT_NO_THROW(Azure::Core::Http::RawResponse r( EXPECT_NO_THROW(Azure::Core::Http::RawResponse r(
1, 1, Azure::Core::Http::HttpStatusCode::Accepted, "phrase")); 1, 1, Azure::Core::Http::HttpStatusCode::Accepted, "phrase"));