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
18namespace 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