diff --git a/sdk/core/azure-core/CHANGELOG.md b/sdk/core/azure-core/CHANGELOG.md index 829264ecf..63e8ae5e9 100644 --- a/sdk/core/azure-core/CHANGELOG.md +++ b/sdk/core/azure-core/CHANGELOG.md @@ -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) diff --git a/sdk/core/azure-core/src/base64.cpp b/sdk/core/azure-core/src/base64.cpp index c2aa06d67..867e796bc 100644 --- a/sdk/core/azure-core/src/base64.cpp +++ b/sdk/core/azure-core/src/base64.cpp @@ -382,9 +382,15 @@ static void Base64WriteThreeLowOrderBytes(std::vector::iterator destina std::vector Base64Decode(const std::string& text) { auto inputSize = text.size(); - if (inputSize < 4) + if (inputSize % 4 != 0) { - return std::vector(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(); } size_t sourceIndex = 0; @@ -406,6 +412,10 @@ std::vector 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 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 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(i0 >> 8); destinationPtr[0] = static_cast(i0 >> 16); @@ -453,6 +472,11 @@ std::vector Base64Decode(const std::string& text) } else { + if (i0 < 0) + { + throw std::runtime_error("Unexpected character in Base64 encoded string"); + } + destinationPtr[0] = static_cast(i0 >> 16); destinationPtr += 1; } diff --git a/sdk/core/azure-core/test/ut/base64_test.cpp b/sdk/core/azure-core/test/ut/base64_test.cpp index efb860596..5272273c0 100644 --- a/sdk/core/azure-core/test/ut/base64_test.cpp +++ b/sdk/core/azure-core/test/ut/base64_test.cpp @@ -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{}))); + 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 +} diff --git a/sdk/core/azure-core/test/ut/simplified_header_test.cpp b/sdk/core/azure-core/test/ut/simplified_header_test.cpp index 610d3606b..e5db88367 100644 --- a/sdk/core/azure-core/test/ut/simplified_header_test.cpp +++ b/sdk/core/azure-core/test/ut/simplified_header_test.cpp @@ -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"));