AnCH Framework  0.1
Another C++ Hack Framework
All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Pages
bcModOp.hpp
1 /*
2  ANCH Framework: ANother C++ Hack is a C++ framework based on C++11 standard
3  Copyright (C) 2012 Vincent Lachenal
4 
5  This file is part of ANCH Framework.
6 
7  ANCH Framework is free software: you can redistribute it and/or modify
8  it under the terms of the GNU Lesser General Public License as published by
9  the Free Software Foundation, either version 3 of the License, or
10  (at your option) any later version.
11 
12  ANCH Framework is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU Lesser General Public License for more details.
16 
17  You should have received a copy of the GNU Lesser General Public License
18  along with ANCH Framework. If not, see <http://www.gnu.org/licenses/>.
19 */
20 #ifndef _ANCH_CRYPTO_BC_MODOP_H_
21 #define _ANCH_CRYPTO_BC_MODOP_H_
22 
23 #include "crypto/cipher/invalidBlockException.hpp"
24 
25 #include <istream>
26 #include <ostream>
27 #include <array>
28 #include <thread>
29 #include <condition_variable>
30 #include <cstring>
31 #include <map>
32 
33 #include "threadPool.hpp"
34 
35 
36 namespace anch {
37  namespace crypto {
38 
48  template<typename Derived, typename Cipher>
50 
51  friend class anch::ThreadPool;
52 
53  // Attributes +
54  private:
56  bool _cipherParallelizable;
57 
59  bool _decipherParallelizable;
60 
62  unsigned int _nbThread;
63 
65  std::mutex _streamMutex;
66 
68  std::condition_variable _writeBlock;
69 
71  std::atomic<uint32_t> _endIdx;
72 
74  std::atomic<uint32_t> _writeIdx;
75 
77  std::atomic<bool> _error;
78 
80  std::ostream* _stream;
81  // Attributes -
82 
83 
84  // Constructors +
85  protected:
94  BlockCipherModeOfOperation(bool cipherParallelizable,
95  bool decipherParallelizable,
96  unsigned int nbThread = 1):
97  _cipherParallelizable(cipherParallelizable),
98  _decipherParallelizable(decipherParallelizable),
99  _nbThread(nbThread),
100  _writeBlock(),
101  _endIdx(UINT32_MAX),
102  _writeIdx(0),
103  _error(false) {
104  if(nbThread == 0 && (cipherParallelizable || decipherParallelizable)) {
105  _nbThread = std::thread::hardware_concurrency();
106  if(_nbThread == 0) { // Keep at least 1 thread running
107  _nbThread = 1;
108  }
109  }
110  }
111  // Constructors -
112 
113 
114  // Destructor +
119  // Nothing to do
120  }
121  // Destructor -
122 
123 
124  // Methods +
125  public:
133  void cipher(std::istream& input,
134  std::ostream& output,
135  const std::string& key) {
136  if(input && output) {
137  reset();
138  if(!_cipherParallelizable || _nbThread == 1) {
139  cipherSequentially(input, output, key);
140 
141  } else {
142  cipherInParallel(input, output, key);
143  }
144  } else {
145  // error
146  }
147  }
148 
156  void decipher(std::istream& input,
157  std::ostream& output,
158  const std::string& key) {
159  if(input && output) {
160  std::array<uint8_t,Cipher::getBlockSize()> prevData = reset();
161  if(!_decipherParallelizable || _nbThread == 1) {
162  decipherSequentially(input, output, key, prevData);
163 
164  } else {
165  decipherInParallel(input, output, key, prevData);
166  }
167  } else {
168  // error
169  }
170  }
171 
172  protected:
185  virtual std::size_t cipherBlock(std::array<uint8_t,Cipher::getBlockSize()>& input,
186  std::streamsize nbRead,
187  std::array<uint8_t,Cipher::getBlockSize()>& output,
188  uint32_t index,
189  Cipher& cipher) = 0;
190 
205  virtual std::size_t decipherBlock(std::array<uint8_t,Cipher::getBlockSize()>& input,
206  std::array<uint8_t,Cipher::getBlockSize()>& prevInput,
207  std::streamsize nbRead,
208  bool lastBlock,
209  std::array<uint8_t,Cipher::getBlockSize()>& output,
210  uint32_t index,
211  Cipher& cipher) = 0;
212 
218  virtual const std::array<uint8_t,Cipher::getBlockSize()>& reset() = 0;
219 
220  private:
228  inline void cipherSequentially(std::istream& input,
229  std::ostream& output,
230  const std::string& key) {
231  Cipher cipher(reinterpret_cast<const uint8_t*>(key.data()));
232  std::array<uint8_t, Cipher::getBlockSize()> data;
233  std::array<uint8_t, Cipher::getBlockSize()> out;
234  uint32_t index = 0;
235  while(!input.eof()) {
236  input.read(reinterpret_cast<char*>(data.data()), Cipher::getBlockSize());
237  std::streamsize nbRead = input.gcount();
238  if(nbRead == 0) {
239  break;
240  }
241  std::size_t nbBlocks = cipherBlock(data, nbRead, out, index, cipher);
242  for(std::size_t i = 0 ; i < nbBlocks ; ++i) {
243  output << out[i];
244  }
245  index++;
246  }
247  output.flush();
248  }
249 
257  inline void cipherInParallel(std::istream& input,
258  std::ostream& output,
259  const std::string& key) {
260  std::array<uint8_t,Cipher::getBlockSize()>* data = new std::array<uint8_t,Cipher::getBlockSize()>[_nbThread];
261  std::array<uint8_t,Cipher::getBlockSize()>* result = new std::array<uint8_t,Cipher::getBlockSize()>[_nbThread];
262  std::size_t* nbWrite = new std::size_t[_nbThread];
263  uint32_t index = 0;
264  std::deque<std::thread> threads;
265  std::vector<Cipher> ciph;
266  ciph.push_back(Cipher(reinterpret_cast<const uint8_t*>(key.data())));
267  for(std::size_t i = 1 ; i < _nbThread ; ++i) {
268  ciph.push_back(Cipher(ciph[0]));
269  }
270  while(!input.eof()) {
271  for(std::size_t i = 0 ; i < _nbThread ; ++i) {
272  input.read(reinterpret_cast<char*>(data[i].data()), Cipher::getBlockSize());
273  std::streamsize nbRead = input.gcount();
274  if(nbRead == 0) {
275  break;
276  }
277  threads.push_back(std::thread(&Derived::deferredCipherBlock,
278  this,
279  index++,
280  std::ref(data[i]),
281  nbRead,
282  std::ref(result[i]),
283  std::ref(ciph[i]),
284  std::ref(nbWrite[i])));
285  if(nbRead != Cipher::getBlockSize()) {
286  break;
287  }
288  }
289  for(uint32_t i = 0 ; !threads.empty() ; ++i) {
290  std::thread& th = threads.front();
291  th.join();
292  for(uint32_t j = 0 ; j < nbWrite[i] ; ++j) {
293  output << result[i][j];
294  }
295  threads.pop_front();
296  }
297  }
298 
299  output.flush();
300  delete[] data;
301  delete[] result;
302  delete[] nbWrite;
303  }
304 
313  void decipherSequentially(std::istream& input,
314  std::ostream& output,
315  const std::string& key,
316  std::array<uint8_t,Cipher::getBlockSize()>& prevData) {
317  Cipher cipher(reinterpret_cast<const uint8_t*>(key.data()));
318  std::array<uint8_t,Cipher::getBlockSize()> out;
319  std::array<uint8_t,Cipher::getBlockSize()> data;
320  std::array<uint8_t,Cipher::getBlockSize()> cipherData;
321  input.read(reinterpret_cast<char*>(data.data()), Cipher::getBlockSize());
322  std::streamsize nbRead = input.gcount();
323  uint32_t index = 0;
324  do {
325  cipherData = data;
326  std::size_t read = nbRead;
327  input.read(reinterpret_cast<char*>(data.data()), Cipher::getBlockSize());
328 
329  std::size_t end = decipherBlock(cipherData, prevData, read, input.eof(), out, index, cipher);
330  for(std::size_t i = 0 ; i < end ; ++i) {
331  output << out[i];
332  }
333 
334  prevData = cipherData;
335  nbRead = input.gcount();
336  index++;
337 
338  } while(nbRead != 0);
339  output.flush();
340  }
341 
350  inline void decipherInParallel(std::istream& input,
351  std::ostream& output,
352  const std::string& key,
353  std::array<uint8_t,Cipher::getBlockSize()>& prevData) {
354  std::array<uint8_t,Cipher::getBlockSize()>* data = new std::array<uint8_t,Cipher::getBlockSize()>[_nbThread + 1];
355  std::array<uint8_t,Cipher::getBlockSize()>* result = new std::array<uint8_t,Cipher::getBlockSize()>[_nbThread];
356  std::size_t* nbWrite = new std::size_t[_nbThread];
357  uint32_t index = 0;
358  std::deque<std::thread> threads;
359  std::vector<Cipher> ciph;
360  ciph.push_back(Cipher(reinterpret_cast<const uint8_t*>(key.data())));
361  for(std::size_t i = 1 ; i < _nbThread ; ++i) {
362  ciph.push_back(Cipher(ciph[0]));
363  }
364  bool lastBlock = false;
365  input.read(reinterpret_cast<char*>(data[0].data()), Cipher::getBlockSize());
366  std::size_t nbRead = input.gcount();
367  do {
368  for(std::size_t i = 0, idx = 1 ; i < _nbThread ; ++i, ++idx) {
369  input.read(reinterpret_cast<char*>(data[idx].data()), Cipher::getBlockSize());
370  std::streamsize nextCount = input.gcount();
371  if(nextCount == 0) {
372  lastBlock = true;
373  }
374  threads.push_back(std::thread(&Derived::deferredDecipherBlock,
375  this,
376  index++,
377  std::ref(data[i]),
378  prevData,
379  nbRead,
380  lastBlock,
381  std::ref(result[i]),
382  std::ref(ciph[i]),
383  std::ref(nbWrite[i])));
384  if(lastBlock) {
385  break;
386  }
387  nbRead = nextCount;
388  prevData = data[i];
389  }
390  for(uint32_t i = 0 ; !threads.empty() ; ++i) {
391  std::thread& th = threads.front();
392  th.join();
393  for(uint32_t j = 0 ; j < nbWrite[i] ; ++j) {
394  output << result[i][j];
395  }
396  threads.pop_front();
397  }
398  if(_error) {
399  throw InvalidBlockException("Error while decipher stream");
400  }
401  data[0] = data[_nbThread];
402  } while(!input.eof() && !lastBlock);
403 
404  output.flush();
405  delete[] data;
406  delete[] result;
407  delete[] nbWrite;
408  }
409 
418  void deferredCipherBlock(uint32_t index,
419  std::array<uint8_t,Cipher::getBlockSize()>& input,
420  std::streamsize nbRead,
421  std::array<uint8_t,Cipher::getBlockSize()>& output,
422  Cipher& cipher,
423  std::size_t& nbWrite) {
424  nbWrite = cipherBlock(input, nbRead, output, index, cipher);
425  }
426 
436  virtual void deferredDecipherBlock(uint32_t index,
437  std::array<uint8_t, Cipher::getBlockSize()>& input,
438  std::array<uint8_t, Cipher::getBlockSize()> prevInput,
439  std::streamsize nbRead,
440  bool lastBlock,
441  std::array<uint8_t, Cipher::getBlockSize()>& output,
442  Cipher& cipher,
443  std::size_t& nbWrite) {
444  try {
445  nbWrite = decipherBlock(input, prevInput, nbRead, lastBlock, output, index, cipher);
446  } catch(const InvalidBlockException& e) {
447  _error = true;
448  }
449  }
450  // Methods -
451 
452 
453  // Accessors +
454  public:
460  inline void setNbThread(unsigned int nbThread) {
461  _nbThread = nbThread;
462  }
463  // Accessors -
464 
465  };
466 
467  }
468 }
469 
470 #endif // _ANCH_CRYPTO_BC_MODOP_H_
void setNbThread(unsigned int nbThread)
Definition: bcModOp.hpp:460
virtual std::size_t cipherBlock(std::array< uint8_t, Cipher::getBlockSize()> &input, std::streamsize nbRead, std::array< uint8_t, Cipher::getBlockSize()> &output, uint32_t index, Cipher &cipher)=0
BlockCipherModeOfOperation(bool cipherParallelizable, bool decipherParallelizable, unsigned int nbThread=1)
Definition: bcModOp.hpp:94
Exception on receiving an invalid block.
Definition: invalidBlockException.hpp:39
virtual std::size_t decipherBlock(std::array< uint8_t, Cipher::getBlockSize()> &input, std::array< uint8_t, Cipher::getBlockSize()> &prevInput, std::streamsize nbRead, bool lastBlock, std::array< uint8_t, Cipher::getBlockSize()> &output, uint32_t index, Cipher &cipher)=0
Thread pool utility class.
Definition: threadPool.hpp:45
AnCH framework base namespace.
Definition: base64.hpp:28
void cipher(std::istream &input, std::ostream &output, const std::string &key)
Definition: bcModOp.hpp:133
Block cipher mode of operation interface.
Definition: bcModOp.hpp:49
void decipher(std::istream &input, std::ostream &output, const std::string &key)
Definition: bcModOp.hpp:156
virtual const std::array< uint8_t, Cipher::getBlockSize()> & reset()=0
virtual ~BlockCipherModeOfOperation()
Definition: bcModOp.hpp:118