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/crypto/CryptoBuf.h> |
17 | |
18 | namespace Aws |
19 | { |
20 | namespace Utils |
21 | { |
22 | namespace Crypto |
23 | { |
24 | SymmetricCryptoBufSrc::SymmetricCryptoBufSrc(Aws::IStream& stream, SymmetricCipher& cipher, CipherMode cipherMode, size_t bufferSize) |
25 | : |
26 | m_isBuf(PUT_BACK_SIZE), m_cipher(cipher), m_stream(stream), m_cipherMode(cipherMode), m_isFinalized(false), |
27 | m_bufferSize(bufferSize), m_putBack(PUT_BACK_SIZE) |
28 | { |
29 | char* end = reinterpret_cast<char*>(m_isBuf.GetUnderlyingData() + m_isBuf.GetLength()); |
30 | setg(end, end, end); |
31 | } |
32 | |
33 | SymmetricCryptoBufSrc::pos_type SymmetricCryptoBufSrc::seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode which) |
34 | { |
35 | if(which == std::ios_base::in) |
36 | { |
37 | auto curPos = m_stream.tellg(); |
38 | //error on seek we may have read past the end already. Try resetting and seeking to the end first |
39 | if (curPos == pos_type(-1)) |
40 | { |
41 | m_stream.clear(); |
42 | m_stream.seekg(0, std::ios_base::end); |
43 | curPos = m_stream.tellg(); |
44 | } |
45 | |
46 | auto absPosition = ComputeAbsSeekPosition(off, dir, curPos); |
47 | size_t seekTo = static_cast<size_t>(absPosition); |
48 | size_t index = static_cast<size_t>(curPos); |
49 | |
50 | if(index == seekTo) |
51 | { |
52 | return curPos; |
53 | } |
54 | else if (seekTo < index) |
55 | { |
56 | m_cipher.Reset(); |
57 | m_stream.clear(); |
58 | m_stream.seekg(0); |
59 | m_isFinalized = false; |
60 | index = 0; |
61 | } |
62 | |
63 | CryptoBuffer cryptoBuffer; |
64 | while (m_cipher && index < seekTo && !m_isFinalized) |
65 | { |
66 | size_t max_read = std::min<size_t>(static_cast<size_t>(seekTo - index), m_bufferSize); |
67 | |
68 | Aws::Utils::Array<char> buf(max_read); |
69 | size_t readSize(0); |
70 | if(m_stream) |
71 | { |
72 | m_stream.read(buf.GetUnderlyingData(), max_read); |
73 | readSize = static_cast<size_t>(m_stream.gcount()); |
74 | } |
75 | |
76 | if (readSize > 0) |
77 | { |
78 | if (m_cipherMode == CipherMode::Encrypt) |
79 | { |
80 | cryptoBuffer = m_cipher.EncryptBuffer(CryptoBuffer(reinterpret_cast<unsigned char*>(buf.GetUnderlyingData()), readSize)); |
81 | } |
82 | else |
83 | { |
84 | cryptoBuffer = m_cipher.DecryptBuffer(CryptoBuffer(reinterpret_cast<unsigned char*>(buf.GetUnderlyingData()), readSize)); |
85 | } |
86 | } |
87 | else |
88 | { |
89 | if (m_cipherMode == CipherMode::Encrypt) |
90 | { |
91 | cryptoBuffer = m_cipher.FinalizeEncryption(); |
92 | } |
93 | else |
94 | { |
95 | cryptoBuffer = m_cipher.FinalizeDecryption(); |
96 | } |
97 | |
98 | m_isFinalized = true; |
99 | } |
100 | |
101 | index += cryptoBuffer.GetLength(); |
102 | } |
103 | |
104 | if (cryptoBuffer.GetLength() && m_cipher) |
105 | { |
106 | CryptoBuffer putBackArea(m_putBack); |
107 | |
108 | m_isBuf = CryptoBuffer({&putBackArea, &cryptoBuffer}); |
109 | //in the very unlikely case that the cipher had less output than the source stream. |
110 | assert(seekTo <= index); |
111 | size_t newBufferPos = index > seekTo ? cryptoBuffer.GetLength() - (index - seekTo) : cryptoBuffer.GetLength(); |
112 | char* baseBufPtr = reinterpret_cast<char*>(m_isBuf.GetUnderlyingData()); |
113 | setg(baseBufPtr, baseBufPtr + m_putBack + newBufferPos, baseBufPtr + m_isBuf.GetLength()); |
114 | |
115 | return pos_type(seekTo); |
116 | } |
117 | else if (seekTo == 0) |
118 | { |
119 | m_isBuf = CryptoBuffer(m_putBack); |
120 | char* end = reinterpret_cast<char*>(m_isBuf.GetUnderlyingData() + m_isBuf.GetLength()); |
121 | setg(end, end, end); |
122 | return pos_type(seekTo); |
123 | } |
124 | } |
125 | |
126 | return pos_type(off_type(-1)); |
127 | } |
128 | |
129 | SymmetricCryptoBufSrc::pos_type SymmetricCryptoBufSrc::seekpos(pos_type pos, std::ios_base::openmode which) |
130 | { |
131 | return seekoff(pos, std::ios_base::beg, which); |
132 | } |
133 | |
134 | SymmetricCryptoBufSrc::int_type SymmetricCryptoBufSrc::underflow() |
135 | { |
136 | if (!m_cipher || (m_isFinalized && gptr() >= egptr())) |
137 | { |
138 | return traits_type::eof(); |
139 | } |
140 | |
141 | if (gptr() < egptr()) |
142 | { |
143 | return traits_type::to_int_type(*gptr()); |
144 | } |
145 | |
146 | char* baseBufPtr = reinterpret_cast<char*>(m_isBuf.GetUnderlyingData()); |
147 | CryptoBuffer putBackArea(m_putBack); |
148 | |
149 | //eback is properly set after the first fill. So this guarantees we are on the second or later fill. |
150 | if (eback() == baseBufPtr) |
151 | { |
152 | //just fill in the last bit of the previous buffer into the put back area so that it has some data in it |
153 | memcpy(putBackArea.GetUnderlyingData(), egptr() - m_putBack, m_putBack); |
154 | } |
155 | |
156 | CryptoBuffer newDataBuf; |
157 | |
158 | while(!newDataBuf.GetLength() && !m_isFinalized) |
159 | { |
160 | Aws::Utils::Array<char> buf(m_bufferSize); |
161 | m_stream.read(buf.GetUnderlyingData(), m_bufferSize); |
162 | size_t readSize = static_cast<size_t>(m_stream.gcount()); |
163 | |
164 | if (readSize > 0) |
165 | { |
166 | if (m_cipherMode == CipherMode::Encrypt) |
167 | { |
168 | newDataBuf = m_cipher.EncryptBuffer(CryptoBuffer(reinterpret_cast<unsigned char*>(buf.GetUnderlyingData()), readSize)); |
169 | } |
170 | else |
171 | { |
172 | newDataBuf = m_cipher.DecryptBuffer(CryptoBuffer(reinterpret_cast<unsigned char*>(buf.GetUnderlyingData()), readSize)); |
173 | } |
174 | } |
175 | else |
176 | { |
177 | if (m_cipherMode == CipherMode::Encrypt) |
178 | { |
179 | newDataBuf = m_cipher.FinalizeEncryption(); |
180 | } |
181 | else |
182 | { |
183 | newDataBuf = m_cipher.FinalizeDecryption(); |
184 | } |
185 | |
186 | m_isFinalized = true; |
187 | } |
188 | } |
189 | |
190 | |
191 | if(newDataBuf.GetLength() > 0) |
192 | { |
193 | m_isBuf = CryptoBuffer({&putBackArea, &newDataBuf}); |
194 | |
195 | baseBufPtr = reinterpret_cast<char*>(m_isBuf.GetUnderlyingData()); |
196 | setg(baseBufPtr, baseBufPtr + m_putBack, baseBufPtr + m_isBuf.GetLength()); |
197 | |
198 | return traits_type::to_int_type(*gptr()); |
199 | } |
200 | |
201 | return traits_type::eof(); |
202 | } |
203 | |
204 | SymmetricCryptoBufSrc::off_type SymmetricCryptoBufSrc::ComputeAbsSeekPosition(off_type pos, std::ios_base::seekdir dir, std::fpos<FPOS_TYPE> curPos) |
205 | { |
206 | switch(dir) |
207 | { |
208 | case std::ios_base::beg: |
209 | return pos; |
210 | case std::ios_base::cur: |
211 | return m_stream.tellg() + pos; |
212 | case std::ios_base::end: |
213 | { |
214 | off_type absPos = m_stream.seekg(0, std::ios_base::end).tellg() - pos; |
215 | m_stream.seekg(curPos); |
216 | return absPos; |
217 | } |
218 | default: |
219 | assert(0); |
220 | return off_type(-1); |
221 | } |
222 | } |
223 | |
224 | void SymmetricCryptoBufSrc::FinalizeCipher() |
225 | { |
226 | if(m_cipher && !m_isFinalized) |
227 | { |
228 | if(m_cipherMode == CipherMode::Encrypt) |
229 | { |
230 | m_cipher.FinalizeEncryption(); |
231 | } |
232 | else |
233 | { |
234 | m_cipher.FinalizeDecryption(); |
235 | } |
236 | } |
237 | } |
238 | |
239 | SymmetricCryptoBufSink::SymmetricCryptoBufSink(Aws::OStream& stream, SymmetricCipher& cipher, CipherMode cipherMode, size_t bufferSize, int16_t blockOffset) |
240 | : |
241 | m_osBuf(bufferSize), m_cipher(cipher), m_stream(stream), m_cipherMode(cipherMode), m_isFinalized(false), m_blockOffset(blockOffset) |
242 | { |
243 | assert(m_blockOffset < 16 && m_blockOffset >= 0); |
244 | char* outputBase = reinterpret_cast<char*>(m_osBuf.GetUnderlyingData()); |
245 | setp(outputBase, outputBase + bufferSize - 1); |
246 | } |
247 | |
248 | SymmetricCryptoBufSink::~SymmetricCryptoBufSink() |
249 | { |
250 | FinalizeCiphersAndFlushSink(); |
251 | } |
252 | |
253 | void SymmetricCryptoBufSink::FinalizeCiphersAndFlushSink() |
254 | { |
255 | if(m_cipher && !m_isFinalized) |
256 | { |
257 | writeOutput(true); |
258 | } |
259 | } |
260 | |
261 | bool SymmetricCryptoBufSink::writeOutput(bool finalize) |
262 | { |
263 | if(!m_isFinalized) |
264 | { |
265 | CryptoBuffer cryptoBuf; |
266 | if (pptr() > pbase()) |
267 | { |
268 | if (m_cipherMode == CipherMode::Encrypt) |
269 | { |
270 | cryptoBuf = m_cipher.EncryptBuffer(CryptoBuffer(reinterpret_cast<unsigned char*>(pbase()), pptr() - pbase())); |
271 | } |
272 | else |
273 | { |
274 | cryptoBuf = m_cipher.DecryptBuffer(CryptoBuffer(reinterpret_cast<unsigned char*>(pbase()), pptr() - pbase())); |
275 | } |
276 | |
277 | pbump(-(static_cast<int>(pptr() - pbase()))); |
278 | } |
279 | if(finalize) |
280 | { |
281 | CryptoBuffer finalBuffer; |
282 | if (m_cipherMode == CipherMode::Encrypt) |
283 | { |
284 | finalBuffer = m_cipher.FinalizeEncryption(); |
285 | } |
286 | else |
287 | { |
288 | finalBuffer = m_cipher.FinalizeDecryption(); |
289 | } |
290 | if(cryptoBuf.GetLength()) |
291 | { |
292 | cryptoBuf = CryptoBuffer({&cryptoBuf, &finalBuffer}); |
293 | } |
294 | else |
295 | { |
296 | cryptoBuf = std::move(finalBuffer); |
297 | } |
298 | |
299 | m_isFinalized = true; |
300 | } |
301 | |
302 | if (m_cipher) |
303 | { |
304 | if(cryptoBuf.GetLength()) |
305 | { |
306 | //allow mid block decryption. We have to decrypt it, but we don't have to write it to the stream. |
307 | //the assumption here is that tellp() will always be 0 or >= 16 bytes. The block offset should only |
308 | //be the offset of the first block read. |
309 | auto blockOffset = m_stream.tellp() > m_blockOffset ? 0 : m_blockOffset; |
310 | m_stream.write(reinterpret_cast<char*>(cryptoBuf.GetUnderlyingData() + blockOffset), cryptoBuf.GetLength() - blockOffset); |
311 | } |
312 | return true; |
313 | } |
314 | } |
315 | |
316 | return false; |
317 | } |
318 | |
319 | SymmetricCryptoBufSink::int_type SymmetricCryptoBufSink::overflow(int_type ch) |
320 | { |
321 | if(m_cipher && m_stream) |
322 | { |
323 | if(ch != traits_type::eof()) |
324 | { |
325 | *pptr() = (char)ch; |
326 | pbump(1); |
327 | } |
328 | |
329 | if(writeOutput(ch == traits_type::eof())) |
330 | { |
331 | return ch; |
332 | } |
333 | } |
334 | |
335 | return traits_type::eof(); |
336 | } |
337 | |
338 | int SymmetricCryptoBufSink::sync() |
339 | { |
340 | if(m_cipher && m_stream) |
341 | { |
342 | return writeOutput(false) ? 0 : -1; |
343 | } |
344 | |
345 | return -1; |
346 | } |
347 | } |
348 | } |
349 | } |
350 | |