Skip to content

Commit 641a0ec

Browse files
committed
swap base64 implementation to CRT
1 parent faaeb6b commit 641a0ec

4 files changed

Lines changed: 59 additions & 132 deletions

File tree

src/aws-cpp-sdk-core/include/aws/core/utils/base64/Base64.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,6 @@ namespace Aws
5757
* Calculates the length of an encoded base64 string based on the buffer being encoded
5858
*/
5959
static size_t CalculateBase64EncodedLength(const ByteBuffer& buffer);
60-
61-
private:
62-
char m_mimeBase64EncodingTable[64];
63-
uint8_t m_mimeBase64DecodingTable[256];
64-
6560
};
6661

6762
} // namespace Base64

src/aws-cpp-sdk-core/source/utils/base64/Base64.cpp

Lines changed: 26 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -3,146 +3,46 @@
33
* SPDX-License-Identifier: Apache-2.0.
44
*/
55

6+
#include <aws/core/utils/UnreferencedParam.h>
67
#include <aws/core/utils/base64/Base64.h>
7-
#include <cstring>
8+
#include <aws/crt/Types.h>
89

910
using namespace Aws::Utils::Base64;
1011

11-
static const uint8_t SENTINEL_VALUE = 255;
12-
static const char BASE64_ENCODING_TABLE_MIME[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
12+
namespace Aws {
13+
namespace Utils {
14+
namespace Base64 {
1315

14-
namespace Aws
15-
{
16-
namespace Utils
17-
{
18-
namespace Base64
19-
{
20-
21-
Base64::Base64(const char *encodingTable)
22-
{
23-
if(encodingTable == nullptr)
24-
{
25-
encodingTable = BASE64_ENCODING_TABLE_MIME;
26-
}
27-
28-
size_t encodingTableLength = strlen(encodingTable);
29-
if(encodingTableLength != 64)
30-
{
31-
encodingTable = BASE64_ENCODING_TABLE_MIME;
32-
encodingTableLength = 64;
33-
}
34-
35-
memcpy(m_mimeBase64EncodingTable, encodingTable, encodingTableLength);
36-
37-
memset((void *)m_mimeBase64DecodingTable, 0, 256);
38-
39-
for(uint32_t i = 0; i < encodingTableLength; ++i)
40-
{
41-
uint32_t index = static_cast<uint32_t>(m_mimeBase64EncodingTable[i]);
42-
m_mimeBase64DecodingTable[index] = static_cast<uint8_t>(i);
43-
}
44-
45-
m_mimeBase64DecodingTable[(uint32_t)'='] = SENTINEL_VALUE;
16+
namespace {
17+
Aws::Crt::ByteCursor AsCursor(const Aws::Utils::ByteBuffer& buffer) {
18+
return Aws::Crt::ByteCursorFromArray(buffer.GetUnderlyingData(), buffer.GetLength());
4619
}
4720

48-
Aws::String Base64::Encode(const Aws::Utils::ByteBuffer& buffer) const
49-
{
50-
size_t bufferLength = buffer.GetLength();
51-
size_t blockCount = (bufferLength + 2) / 3;
52-
size_t remainderCount = (bufferLength % 3);
53-
54-
Aws::String outputString;
55-
outputString.reserve(CalculateBase64EncodedLength(buffer));
56-
57-
for(size_t i = 0; i < bufferLength; i += 3 )
58-
{
59-
uint32_t block = buffer[ i ];
60-
61-
block <<= 8;
62-
if (i + 1 < bufferLength)
63-
{
64-
block = block | buffer[ i + 1 ];
65-
}
66-
67-
block <<= 8;
68-
if (i + 2 < bufferLength)
69-
{
70-
block = block | buffer[ i + 2 ];
71-
}
72-
73-
outputString.push_back(m_mimeBase64EncodingTable[(block >> 18) & 0x3F]);
74-
outputString.push_back(m_mimeBase64EncodingTable[(block >> 12) & 0x3F]);
75-
outputString.push_back(m_mimeBase64EncodingTable[(block >> 6) & 0x3F]);
76-
outputString.push_back(m_mimeBase64EncodingTable[block & 0x3F]);
77-
}
78-
79-
if(remainderCount > 0)
80-
{
81-
outputString[blockCount * 4 - 1] = '=';
82-
if(remainderCount == 1)
83-
{
84-
outputString[blockCount * 4 - 2] = '=';
85-
}
86-
}
87-
88-
return outputString;
21+
Aws::Crt::ByteCursor AsCursor(const Aws::String& str) {
22+
return Aws::Crt::ByteCursorFromArray(reinterpret_cast<const uint8_t*>(str.data()), str.length());
8923
}
24+
} // namespace
9025

91-
Aws::Utils::ByteBuffer Base64::Decode(const Aws::String& str) const
92-
{
93-
size_t decodedLength = CalculateBase64DecodedLength(str);
94-
95-
Aws::Utils::ByteBuffer buffer(decodedLength);
96-
97-
const char* rawString = str.c_str();
98-
size_t blockCount = str.length() / 4;
99-
for(size_t i = 0; i < blockCount; ++i)
100-
{
101-
size_t stringIndex = i * 4;
26+
Base64::Base64(const char* encodingTable) { AWS_UNREFERENCED_PARAM(encodingTable); }
10227

103-
uint32_t value1 = m_mimeBase64DecodingTable[uint32_t(rawString[stringIndex])];
104-
uint32_t value2 = m_mimeBase64DecodingTable[uint32_t(rawString[++stringIndex])];
105-
uint32_t value3 = m_mimeBase64DecodingTable[uint32_t(rawString[++stringIndex])];
106-
uint32_t value4 = m_mimeBase64DecodingTable[uint32_t(rawString[++stringIndex])];
107-
108-
size_t bufferIndex = i * 3;
109-
buffer[bufferIndex] = static_cast<uint8_t>((value1 << 2) | ((value2 >> 4) & 0x03));
110-
if(value3 != SENTINEL_VALUE)
111-
{
112-
buffer[++bufferIndex] = static_cast<uint8_t>(((value2 << 4) & 0xF0) | ((value3 >> 2) & 0x0F));
113-
if(value4 != SENTINEL_VALUE)
114-
{
115-
buffer[++bufferIndex] = static_cast<uint8_t>((value3 & 0x03) << 6 | value4);
116-
}
117-
}
118-
}
119-
120-
return buffer;
28+
Aws::String Base64::Encode(const Aws::Utils::ByteBuffer& buffer) const {
29+
const auto encoded = Aws::Crt::Base64Encode(AsCursor(buffer));
30+
return {encoded.data(), encoded.size()};
12131
}
12232

123-
size_t Base64::CalculateBase64DecodedLength(const Aws::String& b64input)
124-
{
125-
const size_t len = b64input.length();
126-
if(len < 2)
127-
{
128-
return 0;
129-
}
130-
131-
size_t padding = 0;
132-
133-
if (b64input[len - 1] == '=' && b64input[len - 2] == '=') //last two chars are =
134-
padding = 2;
135-
else if (b64input[len - 1] == '=') //last char is =
136-
padding = 1;
33+
Aws::Utils::ByteBuffer Base64::Decode(const Aws::String& str) const {
34+
const auto decoded = Aws::Crt::Base64Decode(AsCursor(str));
35+
return {decoded.data(), decoded.size()};
36+
}
13737

138-
return (len * 3 / 4 - padding);
38+
size_t Base64::CalculateBase64EncodedLength(const Aws::Utils::ByteBuffer& buffer) {
39+
return Aws::Crt::Base64EncodedLength(AsCursor(buffer));
13940
}
14041

141-
size_t Base64::CalculateBase64EncodedLength(const Aws::Utils::ByteBuffer& buffer)
142-
{
143-
return 4 * ((buffer.GetLength() + 2) / 3);
42+
size_t Base64::CalculateBase64DecodedLength(const Aws::String& b64input) {
43+
return Aws::Crt::Base64DecodedLength(AsCursor(b64input));
14444
}
14545

146-
} // namespace Base64
147-
} // namespace Utils
148-
} // namespace Aws
46+
} // namespace Base64
47+
} // namespace Utils
48+
} // namespace Aws

tests/aws-cpp-sdk-core-tests/utils/HashingUtilsTest.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <aws/testing/AwsCppSdkGTestSuite.h>
77

88
#include <aws/core/utils/HashingUtils.h>
9+
#include <aws/core/utils/base64/Base64.h>
910
#include <aws/core/utils/memory/stl/AWSStringStream.h>
1011

1112

@@ -68,6 +69,37 @@ TEST_F(HashingUtilsTest, TestBase64Decoding)
6869
ASSERT_EQ(ByteBuffer((unsigned char*) "foobar", 6), test7);
6970
}
7071

72+
TEST_F(HashingUtilsTest, TestBase64DecodeRejectsMisplacedPadding)
73+
{
74+
for (const auto* encoded : {"AAAA=", "AAAAA=", "AAAAAA=", "AB=D", "="})
75+
{
76+
const Aws::String input(encoded);
77+
ASSERT_EQ(0u, HashingUtils::Base64Decode(input).GetLength()) << "input: " << encoded;
78+
}
79+
}
80+
81+
TEST_F(HashingUtilsTest, TestBase64DecodeRejectsNonAsciiBytes)
82+
{
83+
for (int byte = 0x80; byte <= 0xFF; ++byte)
84+
{
85+
const char raw[] = {static_cast<char>(byte), 'A', 'A', 'A'};
86+
const Aws::String input(raw, sizeof(raw));
87+
ASSERT_EQ(0u, HashingUtils::Base64Decode(input).GetLength()) << "byte: " << byte;
88+
}
89+
}
90+
91+
TEST_F(HashingUtilsTest, TestBase64DecodedLengthNeverUnderAllocates)
92+
{
93+
const Aws::Utils::Base64::Base64 base64;
94+
for (const auto* encoded : {"", "X", "Zg==", "Zm8=", "Zm9v", "Zm9vYg==", "Zm9vYmE=", "Zm9vYmFy",
95+
"AAAA=", "AAAAA=", "AB=D", "=", "====", "A===", "//++", "AA=="})
96+
{
97+
const Aws::String input(encoded);
98+
ASSERT_LE(base64.Decode(input).GetLength(), Aws::Utils::Base64::Base64::CalculateBase64DecodedLength(input))
99+
<< "input: " << encoded;
100+
}
101+
}
102+
71103
TEST_F(HashingUtilsTest, TestHexEncodingDecoding)
72104
{
73105
unsigned char beforeHexEncoding[32] =

0 commit comments

Comments
 (0)