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/logging/LogMacros.h> |
17 | #include <aws/core/utils/HashingUtils.h> |
18 | #include <aws/core/utils/StringUtils.h> |
19 | #include <aws/core/utils/base64/Base64.h> |
20 | #include <aws/core/utils/crypto/Sha256.h> |
21 | #include <aws/core/utils/crypto/Sha256HMAC.h> |
22 | #include <aws/core/utils/crypto/MD5.h> |
23 | #include <aws/core/utils/Outcome.h> |
24 | #include <aws/core/utils/memory/stl/AWSStringStream.h> |
25 | #include <aws/core/utils/memory/stl/AWSList.h> |
26 | |
27 | #include <iomanip> |
28 | |
29 | using namespace Aws::Utils; |
30 | using namespace Aws::Utils::Base64; |
31 | using namespace Aws::Utils::Crypto; |
32 | |
33 | // internal buffers are fixed-size arrays, so this is harmless memory-management wise |
34 | static Aws::Utils::Base64::Base64 s_base64; |
35 | |
36 | // Aws Glacier Tree Hash calculates hash value for each 1MB data |
37 | const static size_t TREE_HASH_ONE_MB = 1024 * 1024; |
38 | |
39 | Aws::String HashingUtils::Base64Encode(const ByteBuffer& message) |
40 | { |
41 | return s_base64.Encode(message); |
42 | } |
43 | |
44 | ByteBuffer HashingUtils::Base64Decode(const Aws::String& encodedMessage) |
45 | { |
46 | return s_base64.Decode(encodedMessage); |
47 | } |
48 | |
49 | ByteBuffer HashingUtils::CalculateSHA256HMAC(const ByteBuffer& toSign, const ByteBuffer& secret) |
50 | { |
51 | Sha256HMAC hash; |
52 | return hash.Calculate(toSign, secret).GetResult(); |
53 | } |
54 | |
55 | ByteBuffer HashingUtils::CalculateSHA256(const Aws::String& str) |
56 | { |
57 | Sha256 hash; |
58 | return hash.Calculate(str).GetResult(); |
59 | } |
60 | |
61 | ByteBuffer HashingUtils::CalculateSHA256(Aws::IOStream& stream) |
62 | { |
63 | Sha256 hash; |
64 | return hash.Calculate(stream).GetResult(); |
65 | } |
66 | |
67 | /** |
68 | * This function is only used by HashingUtils::CalculateSHA256TreeHash() in this cpp file |
69 | * It's a helper function be used to compute the TreeHash defined at: |
70 | * http://docs.aws.amazon.com/amazonglacier/latest/dev/checksum-calculations.html |
71 | */ |
72 | static ByteBuffer TreeHashFinalCompute(Aws::List<ByteBuffer>& input) |
73 | { |
74 | Sha256 hash; |
75 | assert(input.size() != 0); |
76 | |
77 | // O(n) time complexity of merging (n + n/2 + n/4 + n/8 +...+ 1) |
78 | while (input.size() > 1) |
79 | { |
80 | auto iter = input.begin(); |
81 | // if only one element left, just left it there |
82 | while (std::next(iter) != input.end()) |
83 | { |
84 | // if >= two elements |
85 | Aws::String str(reinterpret_cast<char*>(iter->GetUnderlyingData()), iter->GetLength()); |
86 | // list erase returns iterator of next element next to the erased element or end() if erased the last one |
87 | // list insert inserts element before pos, here we erase two elements, and insert a new element |
88 | iter = input.erase(iter); |
89 | str.append(reinterpret_cast<char*>(iter->GetUnderlyingData()), iter->GetLength()); |
90 | iter = input.erase(iter); |
91 | input.insert(iter, hash.Calculate(str).GetResult()); |
92 | |
93 | if (iter == input.end()) break; |
94 | } // while process to the last element |
95 | } // while the list has only one element left |
96 | |
97 | return *(input.begin()); |
98 | } |
99 | |
100 | ByteBuffer HashingUtils::CalculateSHA256TreeHash(const Aws::String& str) |
101 | { |
102 | Sha256 hash; |
103 | if (str.size() == 0) |
104 | { |
105 | return hash.Calculate(str).GetResult(); |
106 | } |
107 | |
108 | Aws::List<ByteBuffer> input; |
109 | size_t pos = 0; |
110 | while (pos < str.size()) |
111 | { |
112 | input.push_back(hash.Calculate(Aws::String(str, pos, TREE_HASH_ONE_MB)).GetResult()); |
113 | pos += TREE_HASH_ONE_MB; |
114 | } |
115 | |
116 | return TreeHashFinalCompute(input); |
117 | } |
118 | |
119 | ByteBuffer HashingUtils::CalculateSHA256TreeHash(Aws::IOStream& stream) |
120 | { |
121 | Sha256 hash; |
122 | Aws::List<ByteBuffer> input; |
123 | auto currentPos = stream.tellg(); |
124 | if (currentPos == std::ios::pos_type(-1)) |
125 | { |
126 | currentPos = 0; |
127 | stream.clear(); |
128 | } |
129 | stream.seekg(0, stream.beg); |
130 | Array<char> streamBuffer(TREE_HASH_ONE_MB); |
131 | while (stream.good()) |
132 | { |
133 | stream.read(streamBuffer.GetUnderlyingData(), TREE_HASH_ONE_MB); |
134 | auto bytesRead = stream.gcount(); |
135 | if (bytesRead > 0) |
136 | { |
137 | input.push_back(hash.Calculate(Aws::String(reinterpret_cast<char*>(streamBuffer.GetUnderlyingData()), static_cast<size_t>(bytesRead))).GetResult()); |
138 | } |
139 | } |
140 | stream.clear(); |
141 | stream.seekg(currentPos, stream.beg); |
142 | |
143 | if (input.size() == 0) |
144 | { |
145 | return hash.Calculate("" ).GetResult(); |
146 | } |
147 | return TreeHashFinalCompute(input); |
148 | } |
149 | |
150 | Aws::String HashingUtils::HexEncode(const ByteBuffer& message) |
151 | { |
152 | Aws::String encoded; |
153 | encoded.reserve(2 * message.GetLength()); |
154 | |
155 | for (unsigned i = 0; i < message.GetLength(); ++i) |
156 | { |
157 | encoded.push_back("0123456789abcdef" [message[i] >> 4]); |
158 | encoded.push_back("0123456789abcdef" [message[i] & 0x0f]); |
159 | } |
160 | |
161 | return encoded; |
162 | } |
163 | |
164 | ByteBuffer HashingUtils::HexDecode(const Aws::String& str) |
165 | { |
166 | //number of characters should be even |
167 | assert(str.length() % 2 == 0); |
168 | assert(str.length() >= 2); |
169 | |
170 | if(str.length() < 2 || str.length() % 2 != 0) |
171 | { |
172 | return ByteBuffer(); |
173 | } |
174 | |
175 | size_t strLength = str.length(); |
176 | size_t readIndex = 0; |
177 | |
178 | if(str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) |
179 | { |
180 | strLength -= 2; |
181 | readIndex = 2; |
182 | } |
183 | |
184 | ByteBuffer hexBuffer(strLength / 2); |
185 | size_t bufferIndex = 0; |
186 | |
187 | for (size_t i = readIndex; i < str.length(); i += 2) |
188 | { |
189 | if(!StringUtils::IsAlnum(str[i]) || !StringUtils::IsAlnum(str[i + 1])) |
190 | { |
191 | //contains non-hex characters |
192 | assert(0); |
193 | } |
194 | |
195 | char firstChar = str[i]; |
196 | uint8_t distance = firstChar - '0'; |
197 | |
198 | if(isalpha(firstChar)) |
199 | { |
200 | firstChar = static_cast<char>(toupper(firstChar)); |
201 | distance = firstChar - 'A' + 10; |
202 | } |
203 | |
204 | unsigned char val = distance * 16; |
205 | |
206 | char secondChar = str[i + 1]; |
207 | distance = secondChar - '0'; |
208 | |
209 | if(isalpha(secondChar)) |
210 | { |
211 | secondChar = static_cast<char>(toupper(secondChar)); |
212 | distance = secondChar - 'A' + 10; |
213 | } |
214 | |
215 | val += distance; |
216 | hexBuffer[bufferIndex++] = val; |
217 | } |
218 | |
219 | return hexBuffer; |
220 | } |
221 | |
222 | ByteBuffer HashingUtils::CalculateMD5(const Aws::String& str) |
223 | { |
224 | MD5 hash; |
225 | return hash.Calculate(str).GetResult(); |
226 | } |
227 | |
228 | ByteBuffer HashingUtils::CalculateMD5(Aws::IOStream& stream) |
229 | { |
230 | MD5 hash; |
231 | return hash.Calculate(stream).GetResult(); |
232 | } |
233 | |
234 | int HashingUtils::HashString(const char* strToHash) |
235 | { |
236 | if (!strToHash) |
237 | return 0; |
238 | |
239 | unsigned hash = 0; |
240 | while (char charValue = *strToHash++) |
241 | { |
242 | hash = charValue + 31 * hash; |
243 | } |
244 | |
245 | return hash; |
246 | } |
247 | |