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.
- [[#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)

View File

@ -382,9 +382,15 @@ static void Base64WriteThreeLowOrderBytes(std::vector<uint8_t>::iterator destina
std::vector<uint8_t> Base64Decode(const std::string& text)
{
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;
@ -406,6 +412,10 @@ std::vector<uint8_t> Base64Decode(const std::string& text)
while (sourceIndex + 4 < inputSize)
{
int64_t result = Base64Decode(inputPtr + sourceIndex);
if (result < 0)
{
throw std::runtime_error("Unexpected character in Base64 encoded string");
}
Base64WriteThreeLowOrderBytes(destinationPtr, result);
destinationPtr += 3;
sourceIndex += 4;
@ -436,6 +446,11 @@ std::vector<uint8_t> Base64Decode(const std::string& text)
i0 |= i3;
i0 |= i2;
if (i0 < 0)
{
throw std::runtime_error("Unexpected character in Base64 encoded string");
}
Base64WriteThreeLowOrderBytes(destinationPtr, i0);
destinationPtr += 3;
}
@ -446,6 +461,10 @@ std::vector<uint8_t> Base64Decode(const std::string& text)
i2 <<= 6;
i0 |= i2;
if (i0 < 0)
{
throw std::runtime_error("Unexpected character in Base64 encoded string");
}
destinationPtr[1] = static_cast<uint8_t>(i0 >> 8);
destinationPtr[0] = static_cast<uint8_t>(i0 >> 16);
@ -453,6 +472,11 @@ std::vector<uint8_t> Base64Decode(const std::string& text)
}
else
{
if (i0 < 0)
{
throw std::runtime_error("Unexpected character in Base64 encoded string");
}
destinationPtr[0] = static_cast<uint8_t>(i0 >> 16);
destinationPtr += 1;
}

View File

@ -125,3 +125,49 @@ TEST(Base64, Roundtrip)
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::DateTime(2020, 11, 03, 15, 30, 44));
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::Http::RawResponse r(
1, 1, Azure::Core::Http::HttpStatusCode::Accepted, "phrase"));