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
29using namespace Aws::Utils;
30using namespace Aws::Utils::Base64;
31using namespace Aws::Utils::Crypto;
32
33// internal buffers are fixed-size arrays, so this is harmless memory-management wise
34static Aws::Utils::Base64::Base64 s_base64;
35
36// Aws Glacier Tree Hash calculates hash value for each 1MB data
37const static size_t TREE_HASH_ONE_MB = 1024 * 1024;
38
39Aws::String HashingUtils::Base64Encode(const ByteBuffer& message)
40{
41 return s_base64.Encode(message);
42}
43
44ByteBuffer HashingUtils::Base64Decode(const Aws::String& encodedMessage)
45{
46 return s_base64.Decode(encodedMessage);
47}
48
49ByteBuffer HashingUtils::CalculateSHA256HMAC(const ByteBuffer& toSign, const ByteBuffer& secret)
50{
51 Sha256HMAC hash;
52 return hash.Calculate(toSign, secret).GetResult();
53}
54
55ByteBuffer HashingUtils::CalculateSHA256(const Aws::String& str)
56{
57 Sha256 hash;
58 return hash.Calculate(str).GetResult();
59}
60
61ByteBuffer 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 */
72static 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
100ByteBuffer 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
119ByteBuffer 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
150Aws::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
164ByteBuffer 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
222ByteBuffer HashingUtils::CalculateMD5(const Aws::String& str)
223{
224 MD5 hash;
225 return hash.Calculate(str).GetResult();
226}
227
228ByteBuffer HashingUtils::CalculateMD5(Aws::IOStream& stream)
229{
230 MD5 hash;
231 return hash.Calculate(stream).GetResult();
232}
233
234int 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