3 This code implements Brian Gladman's scheme
\r
4 specified in password Based File Encryption Utility.
\r
6 Note: you must include MyAes.cpp to project to initialize AES tables
\r
11 #include "../Common/StreamObjects.h"
\r
12 #include "../Common/StreamUtils.h"
\r
14 #include "Pbkdf2HmacSha1.h"
\r
15 #include "RandGen.h"
\r
18 // define it if you don't want to use speed-optimized version of Pbkdf2HmacSha1
\r
19 // #define _NO_WZAES_OPTIMIZATIONS
\r
24 const unsigned kAesKeySizeMax = 32;
\r
26 static const UInt32 kNumKeyGenIterations = 1000;
\r
28 STDMETHODIMP CBaseCoder::CryptoSetPassword(const Byte *data, UInt32 size)
\r
30 if(size > kPasswordSizeMax)
\r
31 return E_INVALIDARG;
\r
32 _key.Password.SetCapacity(size);
\r
33 memcpy(_key.Password, data, size);
\r
37 #ifndef _NO_WZAES_OPTIMIZATIONS
\r
39 static void BytesToBeUInt32s(const Byte *src, UInt32 *dest, unsigned destSize)
\r
41 for (unsigned i = 0; i < destSize; i++)
\r
43 ((UInt32)(src[i * 4 + 0]) << 24) |
\r
44 ((UInt32)(src[i * 4 + 1]) << 16) |
\r
45 ((UInt32)(src[i * 4 + 2]) << 8) |
\r
46 ((UInt32)(src[i * 4 + 3]));
\r
51 STDMETHODIMP CBaseCoder::Init()
\r
53 UInt32 keySize = _key.GetKeySize();
\r
54 UInt32 keysTotalSize = 2 * keySize + kPwdVerifCodeSize;
\r
55 Byte buf[2 * kAesKeySizeMax + kPwdVerifCodeSize];
\r
57 // for (unsigned ii = 0; ii < 1000; ii++)
\r
59 #ifdef _NO_WZAES_OPTIMIZATIONS
\r
62 _key.Password, _key.Password.GetCapacity(),
\r
63 _key.Salt, _key.GetSaltSize(),
\r
64 kNumKeyGenIterations,
\r
65 buf, keysTotalSize);
\r
69 UInt32 buf32[(2 * kAesKeySizeMax + kPwdVerifCodeSize + 3) / 4];
\r
70 UInt32 key32SizeTotal = (keysTotalSize + 3) / 4;
\r
71 UInt32 salt[kSaltSizeMax * 4];
\r
72 UInt32 saltSizeInWords = _key.GetSaltSize() / 4;
\r
73 BytesToBeUInt32s(_key.Salt, salt, saltSizeInWords);
\r
74 NSha1::Pbkdf2Hmac32(
\r
75 _key.Password, _key.Password.GetCapacity(),
\r
76 salt, saltSizeInWords,
\r
77 kNumKeyGenIterations,
\r
78 buf32, key32SizeTotal);
\r
79 for (UInt32 j = 0; j < keysTotalSize; j++)
\r
80 buf[j] = (Byte)(buf32[j / 4] >> (24 - 8 * (j & 3)));
\r
85 _hmac.SetKey(buf + keySize, keySize);
\r
86 memcpy(_key.PwdVerifComputed, buf + 2 * keySize, kPwdVerifCodeSize);
\r
88 AesCtr2_Init(&_aes);
\r
89 Aes_SetKey_Enc(_aes.aes + _aes.offset + 8, buf, keySize);
\r
93 HRESULT CEncoder::WriteHeader(ISequentialOutStream *outStream)
\r
95 UInt32 saltSize = _key.GetSaltSize();
\r
96 g_RandomGenerator.Generate(_key.Salt, saltSize);
\r
98 RINOK(WriteStream(outStream, _key.Salt, saltSize));
\r
99 return WriteStream(outStream, _key.PwdVerifComputed, kPwdVerifCodeSize);
\r
102 HRESULT CEncoder::WriteFooter(ISequentialOutStream *outStream)
\r
104 Byte mac[kMacSize];
\r
105 _hmac.Final(mac, kMacSize);
\r
106 return WriteStream(outStream, mac, kMacSize);
\r
109 STDMETHODIMP CDecoder::SetDecoderProperties2(const Byte *data, UInt32 size)
\r
112 return E_INVALIDARG;
\r
114 return SetKeyMode(data[0]) ? S_OK : E_INVALIDARG;
\r
117 HRESULT CDecoder::ReadHeader(ISequentialInStream *inStream)
\r
119 UInt32 saltSize = _key.GetSaltSize();
\r
120 UInt32 extraSize = saltSize + kPwdVerifCodeSize;
\r
121 Byte temp[kSaltSizeMax + kPwdVerifCodeSize];
\r
122 RINOK(ReadStream_FAIL(inStream, temp, extraSize));
\r
124 for (i = 0; i < saltSize; i++)
\r
125 _key.Salt[i] = temp[i];
\r
126 for (i = 0; i < kPwdVerifCodeSize; i++)
\r
127 _pwdVerifFromArchive[i] = temp[saltSize + i];
\r
131 static bool CompareArrays(const Byte *p1, const Byte *p2, UInt32 size)
\r
133 for (UInt32 i = 0; i < size; i++)
\r
134 if (p1[i] != p2[i])
\r
139 bool CDecoder::CheckPasswordVerifyCode()
\r
141 return CompareArrays(_key.PwdVerifComputed, _pwdVerifFromArchive, kPwdVerifCodeSize);
\r
144 HRESULT CDecoder::CheckMac(ISequentialInStream *inStream, bool &isOK)
\r
147 Byte mac1[kMacSize];
\r
148 RINOK(ReadStream_FAIL(inStream, mac1, kMacSize));
\r
149 Byte mac2[kMacSize];
\r
150 _hmac.Final(mac2, kMacSize);
\r
151 isOK = CompareArrays(mac1, mac2, kMacSize);
\r
155 CAesCtr2::CAesCtr2()
\r
157 offset = ((0 - (unsigned)(ptrdiff_t)aes) & 0xF) / sizeof(UInt32);
\r
160 void AesCtr2_Init(CAesCtr2 *p)
\r
162 UInt32 *ctr = p->aes + p->offset + 4;
\r
164 for (i = 0; i < 4; i++)
\r
166 p->pos = AES_BLOCK_SIZE;
\r
169 void AesCtr2_Code(CAesCtr2 *p, Byte *data, SizeT size)
\r
171 unsigned pos = p->pos;
\r
172 UInt32 *buf32 = p->aes + p->offset;
\r
175 if (pos != AES_BLOCK_SIZE)
\r
177 const Byte *buf = (const Byte *)buf32;
\r
179 *data++ ^= buf[pos++];
\r
180 while (--size != 0 && pos != AES_BLOCK_SIZE);
\r
184 SizeT size2 = size >> 4;
\r
185 g_AesCtr_Code(buf32 + 4, data, size2);
\r
189 pos = AES_BLOCK_SIZE;
\r
195 for (j = 0; j < 4; j++)
\r
197 g_AesCtr_Code(buf32 + 4, (Byte *)buf32, 1);
\r
198 buf = (const Byte *)buf32;
\r
201 *data++ ^= buf[pos++];
\r
202 while (--size != 0 && pos != AES_BLOCK_SIZE);
\r
207 STDMETHODIMP_(UInt32) CEncoder::Filter(Byte *data, UInt32 size)
\r
209 AesCtr2_Code(&_aes, data, size);
\r
210 _hmac.Update(data, size);
\r
214 STDMETHODIMP_(UInt32) CDecoder::Filter(Byte *data, UInt32 size)
\r
216 _hmac.Update(data, size);
\r
217 AesCtr2_Code(&_aes, data, size);
\r