PictureCrypt  1.4.1
An image-steganography project
qaesencryption.cpp
Go to the documentation of this file.
1 #include "qaesencryption.h"
2 
3 /*
4  * Static Functions
5  * */
6 QByteArray QAESEncryption::Crypt(QAESEncryption::Aes level, QAESEncryption::Mode mode, const QByteArray &rawText,
7  const QByteArray &key, const QByteArray &iv, QAESEncryption::Padding padding)
8 {
9  return QAESEncryption(level, mode, padding).encode(rawText, key, iv);
10 }
11 
12 QByteArray QAESEncryption::Decrypt(QAESEncryption::Aes level, QAESEncryption::Mode mode, const QByteArray &rawText,
13  const QByteArray &key, const QByteArray &iv, QAESEncryption::Padding padding)
14 {
15  return QAESEncryption(level, mode, padding).decode(rawText, key, iv);
16 }
17 
18 QByteArray QAESEncryption::ExpandKey(QAESEncryption::Aes level, QAESEncryption::Mode mode, const QByteArray &key)
19 {
20  return QAESEncryption(level, mode).expandKey(key);
21 }
22 
23 QByteArray QAESEncryption::RemovePadding(const QByteArray &rawText, QAESEncryption::Padding padding)
24 {
25  QByteArray ret(rawText);
26  switch (padding)
27  {
28  case Padding::ZERO:
29  //Works only if the last byte of the decoded array is not zero
30  while (ret.at(ret.length()-1) == 0x00)
31  ret.remove(ret.length()-1, 1);
32  break;
33  case Padding::PKCS7:
34  ret.remove(ret.length() - ret.at(ret.length()-1), ret.at(ret.length()-1));
35  break;
36  case Padding::ISO:
37  ret.truncate(ret.lastIndexOf(0x80));
38  break;
39  default:
40  //do nothing
41  break;
42  }
43  return ret;
44 }
45 /*
46  * End Static function declarations
47  * */
48 
49 /*
50  * Inline Functions
51  * */
52 
53 inline quint8 xTime(quint8 x){
54  return ((x<<1) ^ (((x>>7) & 1) * 0x1b));
55 }
56 
57 inline quint8 multiply(quint8 x, quint8 y){
58  return (((y & 1) * x) ^ ((y>>1 & 1) * xTime(x)) ^ ((y>>2 & 1) * xTime(xTime(x))) ^ ((y>>3 & 1)
59  * xTime(xTime(xTime(x)))) ^ ((y>>4 & 1) * xTime(xTime(xTime(xTime(x))))));
60 }
61 
62 /*
63  * End Inline functions
64  * */
65 
66 
68  Padding padding)
69  : m_nb(4), m_blocklen(16), m_level(level), m_mode(mode), m_padding(padding)
70 {
71  m_state = NULL;
72 
73  switch (level)
74  {
75  case AES_128: {
76  AES128 aes;
77  m_nk = aes.nk;
78  m_keyLen = aes.keylen;
79  m_nr = aes.nr;
80  m_expandedKey = aes.expandedKey;
81  }
82  break;
83  case AES_192: {
84  AES192 aes;
85  m_nk = aes.nk;
86  m_keyLen = aes.keylen;
87  m_nr = aes.nr;
88  m_expandedKey = aes.expandedKey;
89  }
90  break;
91  case AES_256: {
92  AES256 aes;
93  m_nk = aes.nk;
94  m_keyLen = aes.keylen;
95  m_nr = aes.nr;
96  m_expandedKey = aes.expandedKey;
97  }
98  break;
99  default: {
100  AES128 aes;
101  m_nk = aes.nk;
102  m_keyLen = aes.keylen;
103  m_nr = aes.nr;
104  m_expandedKey = aes.expandedKey;
105  }
106  break;
107  }
108 
109 }
110 QByteArray QAESEncryption::getPadding(int currSize, int alignment)
111 {
112  int size = (alignment - currSize % alignment) % alignment;
113  if (size == 0) return QByteArray();
114  switch(m_padding)
115  {
116  case Padding::ZERO:
117  return QByteArray(size, 0x00);
118  break;
119  case Padding::PKCS7:
120  return QByteArray(size,size);
121  break;
122  case Padding::ISO:
123  return QByteArray (size-1, 0x00).prepend(0x80);
124  break;
125  default:
126  return QByteArray(size, 0x00);
127  break;
128  }
129  return QByteArray(size, 0x00);
130 }
131 
132 QByteArray QAESEncryption::expandKey(const QByteArray &key)
133 {
134  int i, k;
135  quint8 tempa[4]; // Used for the column/row operations
136  QByteArray roundKey(key);
137 
138  // The first round key is the key itself.
139  // ...
140 
141  // All other round keys are found from the previous round keys.
142  //i == Nk
143  for(i = m_nk; i < m_nb * (m_nr + 1); i++)
144  {
145  tempa[0] = (quint8) roundKey.at((i-1) * 4 + 0);
146  tempa[1] = (quint8) roundKey.at((i-1) * 4 + 1);
147  tempa[2] = (quint8) roundKey.at((i-1) * 4 + 2);
148  tempa[3] = (quint8) roundKey.at((i-1) * 4 + 3);
149 
150  if (i % m_nk == 0)
151  {
152  // This function shifts the 4 bytes in a word to the left once.
153  // [a0,a1,a2,a3] becomes [a1,a2,a3,a0]
154 
155  // Function RotWord()
156  k = tempa[0];
157  tempa[0] = tempa[1];
158  tempa[1] = tempa[2];
159  tempa[2] = tempa[3];
160  tempa[3] = k;
161 
162  // Function Subword()
163  tempa[0] = getSBoxValue(tempa[0]);
164  tempa[1] = getSBoxValue(tempa[1]);
165  tempa[2] = getSBoxValue(tempa[2]);
166  tempa[3] = getSBoxValue(tempa[3]);
167 
168  tempa[0] = tempa[0] ^ Rcon[i/m_nk];
169  }
170  if (m_level == AES_256 && i % m_nk == 4)
171  {
172  // Function Subword()
173  tempa[0] = getSBoxValue(tempa[0]);
174  tempa[1] = getSBoxValue(tempa[1]);
175  tempa[2] = getSBoxValue(tempa[2]);
176  tempa[3] = getSBoxValue(tempa[3]);
177  }
178  roundKey.insert(i * 4 + 0, (quint8) roundKey.at((i - m_nk) * 4 + 0) ^ tempa[0]);
179  roundKey.insert(i * 4 + 1, (quint8) roundKey.at((i - m_nk) * 4 + 1) ^ tempa[1]);
180  roundKey.insert(i * 4 + 2, (quint8) roundKey.at((i - m_nk) * 4 + 2) ^ tempa[2]);
181  roundKey.insert(i * 4 + 3, (quint8) roundKey.at((i - m_nk) * 4 + 3) ^ tempa[3]);
182  }
183  return roundKey;
184 }
185 
186 // This function adds the round key to state.
187 // The round key is added to the state by an XOR function.
188 void QAESEncryption::addRoundKey(const quint8 round, const QByteArray expKey)
189 {
190  QByteArray::iterator it = m_state->begin();
191  for(int i=0; i < 16; ++i)
192  it[i] = (quint8) it[i] ^ (quint8) expKey.at(round * m_nb * 4 + (i/4) * m_nb + (i%4));
193 }
194 
195 // The SubBytes Function Substitutes the values in the
196 // state matrix with values in an S-box.
197 void QAESEncryption::subBytes()
198 {
199  QByteArray::iterator it = m_state->begin();
200  for(int i = 0; i < 16; i++)
201  it[i] = getSBoxValue((quint8) it[i]);
202 }
203 
204 // The ShiftRows() function shifts the rows in the state to the left.
205 // Each row is shifted with different offset.
206 // Offset = Row number. So the first row is not shifted.
207 void QAESEncryption::shiftRows()
208 {
209  QByteArray::iterator it = m_state->begin();
210  quint8 temp;
211  //Keep in mind that QByteArray is column-driven!!
212 
213  //Shift 1 to left
214  temp = (quint8)it[1];
215  it[1] = (quint8)it[5];
216  it[5] = (quint8)it[9];
217  it[9] = (quint8)it[13];
218  it[13] = (quint8)temp;
219 
220  //Shift 2 to left
221  temp = (quint8)it[2];
222  it[2] = (quint8)it[10];
223  it[10] = (quint8)temp;
224  temp = (quint8)it[6];
225  it[6] = (quint8)it[14];
226  it[14] = (quint8)temp;
227 
228  //Shift 3 to left
229  temp = (quint8)it[3];
230  it[3] = (quint8)it[15];
231  it[15] = (quint8)it[11];
232  it[11] = (quint8)it[7];
233  it[7] = (quint8)temp;
234 }
235 
236 // MixColumns function mixes the columns of the state matrix
237 //optimized!!
238 void QAESEncryption::mixColumns()
239 {
240  QByteArray::iterator it = m_state->begin();
241  quint8 tmp, tm, t;
242 
243  for(int i = 0; i < 16; i += 4){
244  t = (quint8)it[i];
245  tmp = (quint8)it[i] ^ (quint8)it[i+1] ^ (quint8)it[i+2] ^ (quint8)it[i+3] ;
246 
247  tm = xTime( (quint8)it[i] ^ (quint8)it[i+1] );
248  it[i] = (quint8)it[i] ^ (quint8)tm ^ (quint8)tmp;
249 
250  tm = xTime( (quint8)it[i+1] ^ (quint8)it[i+2]);
251  it[i+1] = (quint8)it[i+1] ^ (quint8)tm ^ (quint8)tmp;
252 
253  tm = xTime( (quint8)it[i+2] ^ (quint8)it[i+3]);
254  it[i+2] =(quint8)it[i+2] ^ (quint8)tm ^ (quint8)tmp;
255 
256  tm = xTime((quint8)it[i+3] ^ (quint8)t);
257  it[i+3] =(quint8)it[i+3] ^ (quint8)tm ^ (quint8)tmp;
258  }
259 }
260 
261 // MixColumns function mixes the columns of the state matrix.
262 // The method used to multiply may be difficult to understand for the inexperienced.
263 // Please use the references to gain more information.
264 void QAESEncryption::invMixColumns()
265 {
266  QByteArray::iterator it = m_state->begin();
267  quint8 a,b,c,d;
268  for(int i = 0; i < 16; i+=4){
269  a = (quint8) it[i];
270  b = (quint8) it[i+1];
271  c = (quint8) it[i+2];
272  d = (quint8) it[i+3];
273 
274  it[i] = (quint8) (multiply(a, 0x0e) ^ multiply(b, 0x0b) ^ multiply(c, 0x0d) ^ multiply(d, 0x09));
275  it[i+1] = (quint8) (multiply(a, 0x09) ^ multiply(b, 0x0e) ^ multiply(c, 0x0b) ^ multiply(d, 0x0d));
276  it[i+2] = (quint8) (multiply(a, 0x0d) ^ multiply(b, 0x09) ^ multiply(c, 0x0e) ^ multiply(d, 0x0b));
277  it[i+3] = (quint8) (multiply(a, 0x0b) ^ multiply(b, 0x0d) ^ multiply(c, 0x09) ^ multiply(d, 0x0e));
278  }
279 }
280 
281 // The SubBytes Function Substitutes the values in the
282 // state matrix with values in an S-box.
283 void QAESEncryption::invSubBytes()
284 {
285  QByteArray::iterator it = m_state->begin();
286  for(int i = 0; i < 16; ++i)
287  it[i] = getSBoxInvert((quint8) it[i]);
288 }
289 
290 void QAESEncryption::invShiftRows()
291 {
292  QByteArray::iterator it = m_state->begin();
293  uint8_t temp;
294 
295  //Keep in mind that QByteArray is column-driven!!
296 
297  //Shift 1 to right
298  temp = (quint8)it[13];
299  it[13] = (quint8)it[9];
300  it[9] = (quint8)it[5];
301  it[5] = (quint8)it[1];
302  it[1] = (quint8)temp;
303 
304  //Shift 2
305  temp = (quint8)it[10];
306  it[10] = (quint8)it[2];
307  it[2] = (quint8)temp;
308  temp = (quint8)it[14];
309  it[14] = (quint8)it[6];
310  it[6] = (quint8)temp;
311 
312  //Shift 3
313  temp = (quint8)it[15];
314  it[15] = (quint8)it[3];
315  it[3] = (quint8)it[7];
316  it[7] = (quint8)it[11];
317  it[11] = (quint8)temp;
318 }
319 
320 QByteArray QAESEncryption::byteXor(const QByteArray &a, const QByteArray &b)
321 {
322  QByteArray::const_iterator it_a = a.begin();
323  QByteArray::const_iterator it_b = b.begin();
324  QByteArray ret;
325 
326  //for(int i = 0; i < m_blocklen; i++)
327  for(int i = 0; i < std::min(a.size(), b.size()); i++)
328  ret.insert(i,it_a[i] ^ it_b[i]);
329 
330  return ret;
331 }
332 
333 // Cipher is the main function that encrypts the PlainText.
334 QByteArray QAESEncryption::cipher(const QByteArray &expKey, const QByteArray &in)
335 {
336 
337  //m_state is the input buffer...
338  QByteArray output(in);
339  m_state = &output;
340 
341  // Add the First round key to the state before starting the rounds.
342  addRoundKey(0, expKey);
343 
344  // There will be Nr rounds.
345  // The first Nr-1 rounds are identical.
346  // These Nr-1 rounds are executed in the loop below.
347  for(quint8 round = 1; round < m_nr; ++round){
348  subBytes();
349  shiftRows();
350  mixColumns();
351  addRoundKey(round, expKey);
352  }
353 
354  // The last round is given below.
355  // The MixColumns function is not here in the last round.
356  subBytes();
357  shiftRows();
358  addRoundKey(m_nr, expKey);
359 
360  return output;
361 }
362 
363 QByteArray QAESEncryption::invCipher(const QByteArray &expKey, const QByteArray &in)
364 {
365  //m_state is the input buffer.... handle it!
366  QByteArray output(in);
367  m_state = &output;
368 
369  // Add the First round key to the state before starting the rounds.
370  addRoundKey(m_nr, expKey);
371 
372  // There will be Nr rounds.
373  // The first Nr-1 rounds are identical.
374  // These Nr-1 rounds are executed in the loop below.
375  for(quint8 round=m_nr-1; round>0 ; round--){
376  invShiftRows();
377  invSubBytes();
378  addRoundKey(round, expKey);
379  invMixColumns();
380  }
381 
382  // The last round is given below.
383  // The MixColumns function is not here in the last round.
384  invShiftRows();
385  invSubBytes();
386  addRoundKey(0, expKey);
387 
388  return output;
389 }
390 
391 QByteArray QAESEncryption::encode(const QByteArray &rawText, const QByteArray &key, const QByteArray &iv)
392 {
393  if (m_mode >= CBC && (iv.isNull() || iv.size() != m_blocklen))
394  return QByteArray();
395 
396  QByteArray ret;
397  QByteArray expandedKey = expandKey(key);
398  QByteArray alignedText(rawText);
399 
400  //Fill array with padding
401  alignedText.append(getPadding(rawText.size(), m_blocklen));
402 
403  switch(m_mode)
404  {
405  case ECB:
406  for(int i=0; i < alignedText.size(); i+= m_blocklen)
407  ret.append(cipher(expandedKey, alignedText.mid(i, m_blocklen)));
408  break;
409  case CBC: {
410  QByteArray ivTemp(iv);
411  for(int i=0; i < alignedText.size(); i+= m_blocklen) {
412  alignedText.replace(i, m_blocklen, byteXor(alignedText.mid(i, m_blocklen),ivTemp));
413  ret.append(cipher(expandedKey, alignedText.mid(i, m_blocklen)));
414  ivTemp = ret.mid(i, m_blocklen);
415  }
416  }
417  break;
418  case CFB: {
419  ret.append(byteXor(alignedText.left(m_blocklen), cipher(expandedKey, iv)));
420  for(int i=0; i < alignedText.size(); i+= m_blocklen) {
421  if (i+m_blocklen < alignedText.size())
422  ret.append(byteXor(alignedText.mid(i+m_blocklen, m_blocklen),
423  cipher(expandedKey, ret.mid(i, m_blocklen))));
424  }
425  }
426  break;
427  case OFB: {
428  QByteArray ofbTemp;
429  ofbTemp.append(cipher(expandedKey, iv));
430  for (int i=m_blocklen; i < alignedText.size(); i += m_blocklen){
431  ofbTemp.append(cipher(expandedKey, ofbTemp.right(m_blocklen)));
432  }
433  ret.append(byteXor(alignedText, ofbTemp));
434  }
435  break;
436  default: break;
437  }
438  return ret;
439 }
440 
441 QByteArray QAESEncryption::decode(const QByteArray &rawText, const QByteArray &key, const QByteArray &iv)
442 {
443  if (m_mode >= CBC && (iv.isNull() || iv.size() != m_blocklen))
444  return QByteArray();
445 
446  QByteArray ret;
447  QByteArray expandedKey = expandKey(key);
448 
449  switch(m_mode)
450  {
451  case ECB:
452  for(int i=0; i < rawText.size(); i+= m_blocklen)
453  ret.append(invCipher(expandedKey, rawText.mid(i, m_blocklen)));
454  break;
455  case CBC: {
456  QByteArray ivTemp(iv);
457  for(int i=0; i < rawText.size(); i+= m_blocklen){
458  ret.append(invCipher(expandedKey, rawText.mid(i, m_blocklen)));
459  ret.replace(i, m_blocklen, byteXor(ret.mid(i, m_blocklen),ivTemp));
460  ivTemp = rawText.mid(i, m_blocklen);
461  }
462  }
463  break;
464  case CFB: {
465  ret.append(byteXor(rawText.mid(0, m_blocklen), cipher(expandedKey, iv)));
466  for(int i=0; i < rawText.size(); i+= m_blocklen){
467  if (i+m_blocklen < rawText.size()) {
468  ret.append(byteXor(rawText.mid(i+m_blocklen, m_blocklen),
469  cipher(expandedKey, rawText.mid(i, m_blocklen))));
470  }
471  }
472  }
473  break;
474  case OFB: {
475  QByteArray ofbTemp;
476  ofbTemp.append(cipher(expandedKey, iv));
477  for (int i=m_blocklen; i < rawText.size(); i += m_blocklen){
478  ofbTemp.append(cipher(expandedKey, ofbTemp.right(m_blocklen)));
479  }
480  ret.append(byteXor(rawText, ofbTemp));
481  }
482  break;
483  default:
484  //do nothing
485  break;
486  }
487  return ret;
488 }
489 
490 QByteArray QAESEncryption::removePadding(const QByteArray &rawText)
491 {
492  QByteArray ret(rawText);
493  switch (m_padding)
494  {
495  case Padding::ZERO:
496  //Works only if the last byte of the decoded array is not zero
497  while (ret.at(ret.length()-1) == 0x00)
498  ret.remove(ret.length()-1, 1);
499  break;
500  case Padding::PKCS7:
501  ret.remove(ret.length() - ret.at(ret.length()-1), ret.at(ret.length()-1));
502  break;
503  case Padding::ISO:
504  ret.truncate(ret.lastIndexOf(0x80));
505  break;
506  default:
507  //do nothing
508  break;
509  }
510  return ret;
511 }
Aes
The Aes enum AES Level AES Levels The class supports all AES key lenghts.
static QByteArray Crypt(QAESEncryption::Aes level, QAESEncryption::Mode mode, const QByteArray &rawText, const QByteArray &key, const QByteArray &iv=NULL, QAESEncryption::Padding padding=QAESEncryption::ISO)
Crypt Static encode function.
Mode
The Mode enum AES Mode The class supports the following operating modes ECB CBC CFB OFB...
QByteArray decode(const QByteArray &rawText, const QByteArray &key, const QByteArray &iv=NULL)
decode Decodes data with AES
static QByteArray Decrypt(QAESEncryption::Aes level, QAESEncryption::Mode mode, const QByteArray &rawText, const QByteArray &key, const QByteArray &iv=NULL, QAESEncryption::Padding padding=QAESEncryption::ISO)
Decrypt Static decode function.
QByteArray removePadding(const QByteArray &rawText)
RemovePadding Removes padding.
quint8 multiply(quint8 x, quint8 y)
quint8 xTime(quint8 x)
QByteArray encode(const QByteArray &rawText, const QByteArray &key, const QByteArray &iv=NULL)
encode Encodes data with AES
QAESEncryption(QAESEncryption::Aes level, QAESEncryption::Mode mode, QAESEncryption::Padding padding=QAESEncryption::ISO)
Padding
The Padding enum Padding By default the padding method is ISO, however, the class supports: ...
static QByteArray ExpandKey(QAESEncryption::Aes level, QAESEncryption::Mode mode, const QByteArray &key)
ExpandKey Expands the key.
QByteArray expandKey(const QByteArray &key)
ExpandKey Expands the key.
static QByteArray RemovePadding(const QByteArray &rawText, QAESEncryption::Padding padding)
RemovePadding Removes padding.