1/*
2 * Copyright 2010-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License").
5 * You may not use this file except in compliance with the License.
6 * A copy of the License is located at
7 *
8 * http://aws.amazon.com/apache2.0
9 *
10 * or in the "license" file accompanying this file. This file is distributed
11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 * express or implied. See the License for the specific language governing
13 * permissions and limitations under the License.
14 */
15
16#include <aws/core/utils/base64/Base64.h>
17#include <cstring>
18
19using namespace Aws::Utils::Base64;
20
21static const uint8_t SENTINEL_VALUE = 255;
22static const char BASE64_ENCODING_TABLE_MIME[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
23
24namespace Aws
25{
26namespace Utils
27{
28namespace Base64
29{
30
31Base64::Base64(const char *encodingTable)
32{
33 if(encodingTable == nullptr)
34 {
35 encodingTable = BASE64_ENCODING_TABLE_MIME;
36 }
37
38 size_t encodingTableLength = strlen(encodingTable);
39 if(encodingTableLength != 64)
40 {
41 encodingTable = BASE64_ENCODING_TABLE_MIME;
42 encodingTableLength = 64;
43 }
44
45 memcpy(m_mimeBase64EncodingTable, encodingTable, encodingTableLength);
46
47 memset((void *)m_mimeBase64DecodingTable, 0, 256);
48
49 for(uint32_t i = 0; i < encodingTableLength; ++i)
50 {
51 uint32_t index = static_cast<uint32_t>(m_mimeBase64EncodingTable[i]);
52 m_mimeBase64DecodingTable[index] = static_cast<uint8_t>(i);
53 }
54
55 m_mimeBase64DecodingTable[(uint32_t)'='] = SENTINEL_VALUE;
56}
57
58Aws::String Base64::Encode(const Aws::Utils::ByteBuffer& buffer) const
59{
60 size_t bufferLength = buffer.GetLength();
61 size_t blockCount = (bufferLength + 2) / 3;
62 size_t remainderCount = (bufferLength % 3);
63
64 Aws::String outputString;
65 outputString.reserve(CalculateBase64EncodedLength(buffer));
66
67 for(size_t i = 0; i < bufferLength; i += 3 )
68 {
69 uint32_t block = buffer[ i ];
70
71 block <<= 8;
72 if (i + 1 < bufferLength)
73 {
74 block = block | buffer[ i + 1 ];
75 }
76
77 block <<= 8;
78 if (i + 2 < bufferLength)
79 {
80 block = block | buffer[ i + 2 ];
81 }
82
83 outputString.push_back(m_mimeBase64EncodingTable[(block >> 18) & 0x3F]);
84 outputString.push_back(m_mimeBase64EncodingTable[(block >> 12) & 0x3F]);
85 outputString.push_back(m_mimeBase64EncodingTable[(block >> 6) & 0x3F]);
86 outputString.push_back(m_mimeBase64EncodingTable[block & 0x3F]);
87 }
88
89 if(remainderCount > 0)
90 {
91 outputString[blockCount * 4 - 1] = '=';
92 if(remainderCount == 1)
93 {
94 outputString[blockCount * 4 - 2] = '=';
95 }
96 }
97
98 return outputString;
99}
100
101Aws::Utils::ByteBuffer Base64::Decode(const Aws::String& str) const
102{
103 size_t decodedLength = CalculateBase64DecodedLength(str);
104
105 Aws::Utils::ByteBuffer buffer(decodedLength);
106
107 const char* rawString = str.c_str();
108 size_t blockCount = str.length() / 4;
109 for(size_t i = 0; i < blockCount; ++i)
110 {
111 size_t stringIndex = i * 4;
112
113 uint32_t value1 = m_mimeBase64DecodingTable[uint32_t(rawString[stringIndex])];
114 uint32_t value2 = m_mimeBase64DecodingTable[uint32_t(rawString[++stringIndex])];
115 uint32_t value3 = m_mimeBase64DecodingTable[uint32_t(rawString[++stringIndex])];
116 uint32_t value4 = m_mimeBase64DecodingTable[uint32_t(rawString[++stringIndex])];
117
118 size_t bufferIndex = i * 3;
119 buffer[bufferIndex] = static_cast<uint8_t>((value1 << 2) | ((value2 >> 4) & 0x03));
120 if(value3 != SENTINEL_VALUE)
121 {
122 buffer[++bufferIndex] = static_cast<uint8_t>(((value2 << 4) & 0xF0) | ((value3 >> 2) & 0x0F));
123 if(value4 != SENTINEL_VALUE)
124 {
125 buffer[++bufferIndex] = static_cast<uint8_t>((value3 & 0x03) << 6 | value4);
126 }
127 }
128 }
129
130 return buffer;
131}
132
133size_t Base64::CalculateBase64DecodedLength(const Aws::String& b64input)
134{
135 const size_t len = b64input.length();
136 if(len < 2)
137 {
138 return 0;
139 }
140
141 size_t padding = 0;
142
143 if (b64input[len - 1] == '=' && b64input[len - 2] == '=') //last two chars are =
144 padding = 2;
145 else if (b64input[len - 1] == '=') //last char is =
146 padding = 1;
147
148 return (len * 3 / 4 - padding);
149}
150
151size_t Base64::CalculateBase64EncodedLength(const Aws::Utils::ByteBuffer& buffer)
152{
153 return 4 * ((buffer.GetLength() + 2) / 3);
154}
155
156} // namespace Base64
157} // namespace Utils
158} // namespace Aws