1 // 7zAes.cpp 2 3 #include "StdAfx.h" 4 5 #include "../../../C/Sha256.h" 6 7 #include "../../Common/ComTry.h" 8 9 #ifndef _7ZIP_ST 10 #include "../../Windows/Synchronization.h" 11 #endif 12 13 #include "../Common/StreamUtils.h" 14 15 #include "7zAes.h" 16 #include "MyAes.h" 17 18 #ifndef EXTRACT_ONLY 19 #include "RandGen.h" 20 #endif 21 22 namespace NCrypto { 23 namespace N7z { 24 25 static const unsigned k_NumCyclesPower_Supported_MAX = 24; 26 27 bool CKeyInfo::IsEqualTo(const CKeyInfo &a) const 28 { 29 if (SaltSize != a.SaltSize || NumCyclesPower != a.NumCyclesPower) 30 return false; 31 for (unsigned i = 0; i < SaltSize; i++) 32 if (Salt[i] != a.Salt[i]) 33 return false; 34 return (Password == a.Password); 35 } 36 37 void CKeyInfo::CalcKey() 38 { 39 if (NumCyclesPower == 0x3F) 40 { 41 unsigned pos; 42 for (pos = 0; pos < SaltSize; pos++) 43 Key[pos] = Salt[pos]; 44 for (unsigned i = 0; i < Password.Size() && pos < kKeySize; i++) 45 Key[pos++] = Password[i]; 46 for (; pos < kKeySize; pos++) 47 Key[pos] = 0; 48 } 49 else 50 { 51 size_t bufSize = 8 + SaltSize + Password.Size(); 52 CObjArray<Byte> buf(bufSize); 53 memcpy(buf, Salt, SaltSize); 54 memcpy(buf + SaltSize, Password, Password.Size()); 55 56 CSha256 sha; 57 Sha256_Init(&sha); 58 59 Byte *ctr = buf + SaltSize + Password.Size(); 60 61 for (unsigned i = 0; i < 8; i++) 62 ctr[i] = 0; 63 64 UInt64 numRounds = (UInt64)1 << NumCyclesPower; 65 66 do 67 { 68 Sha256_Update(&sha, buf, bufSize); 69 for (unsigned i = 0; i < 8; i++) 70 if (++(ctr[i]) != 0) 71 break; 72 } 73 while (--numRounds != 0); 74 75 Sha256_Final(&sha, Key); 76 } 77 } 78 79 bool CKeyInfoCache::GetKey(CKeyInfo &key) 80 { 81 FOR_VECTOR (i, Keys) 82 { 83 const CKeyInfo &cached = Keys[i]; 84 if (key.IsEqualTo(cached)) 85 { 86 for (unsigned j = 0; j < kKeySize; j++) 87 key.Key[j] = cached.Key[j]; 88 if (i != 0) 89 Keys.MoveToFront(i); 90 return true; 91 } 92 } 93 return false; 94 } 95 96 void CKeyInfoCache::FindAndAdd(const CKeyInfo &key) 97 { 98 FOR_VECTOR (i, Keys) 99 { 100 const CKeyInfo &cached = Keys[i]; 101 if (key.IsEqualTo(cached)) 102 { 103 if (i != 0) 104 Keys.MoveToFront(i); 105 return; 106 } 107 } 108 Add(key); 109 } 110 111 void CKeyInfoCache::Add(const CKeyInfo &key) 112 { 113 if (Keys.Size() >= Size) 114 Keys.DeleteBack(); 115 Keys.Insert(0, key); 116 } 117 118 static CKeyInfoCache g_GlobalKeyCache(32); 119 120 #ifndef _7ZIP_ST 121 static NWindows::NSynchronization::CCriticalSection g_GlobalKeyCacheCriticalSection; 122 #define MT_LOCK NWindows::NSynchronization::CCriticalSectionLock lock(g_GlobalKeyCacheCriticalSection); 123 #else 124 #define MT_LOCK 125 #endif 126 127 CBase::CBase(): 128 _cachedKeys(16), 129 _ivSize(0) 130 { 131 for (unsigned i = 0; i < sizeof(_iv); i++) 132 _iv[i] = 0; 133 } 134 135 void CBase::PrepareKey() 136 { 137 // BCJ2 threads use same password. So we use long lock. 138 MT_LOCK 139 140 bool finded = false; 141 if (!_cachedKeys.GetKey(_key)) 142 { 143 finded = g_GlobalKeyCache.GetKey(_key); 144 if (!finded) 145 _key.CalcKey(); 146 _cachedKeys.Add(_key); 147 } 148 if (!finded) 149 g_GlobalKeyCache.FindAndAdd(_key); 150 } 151 152 #ifndef EXTRACT_ONLY 153 154 /* 155 STDMETHODIMP CEncoder::ResetSalt() 156 { 157 _key.SaltSize = 4; 158 g_RandomGenerator.Generate(_key.Salt, _key.SaltSize); 159 return S_OK; 160 } 161 */ 162 163 STDMETHODIMP CEncoder::ResetInitVector() 164 { 165 for (unsigned i = 0; i < sizeof(_iv); i++) 166 _iv[i] = 0; 167 _ivSize = 8; 168 g_RandomGenerator.Generate(_iv, _ivSize); 169 return S_OK; 170 } 171 172 STDMETHODIMP CEncoder::WriteCoderProperties(ISequentialOutStream *outStream) 173 { 174 Byte props[2 + sizeof(_key.Salt) + sizeof(_iv)]; 175 unsigned propsSize = 1; 176 177 props[0] = (Byte)(_key.NumCyclesPower 178 | (_key.SaltSize == 0 ? 0 : (1 << 7)) 179 | (_ivSize == 0 ? 0 : (1 << 6))); 180 181 if (_key.SaltSize != 0 || _ivSize != 0) 182 { 183 props[1] = (Byte)( 184 ((_key.SaltSize == 0 ? 0 : _key.SaltSize - 1) << 4) 185 | (_ivSize == 0 ? 0 : _ivSize - 1)); 186 memcpy(props + 2, _key.Salt, _key.SaltSize); 187 propsSize = 2 + _key.SaltSize; 188 memcpy(props + propsSize, _iv, _ivSize); 189 propsSize += _ivSize; 190 } 191 192 return WriteStream(outStream, props, propsSize); 193 } 194 195 CEncoder::CEncoder() 196 { 197 // _key.SaltSize = 4; g_RandomGenerator.Generate(_key.Salt, _key.SaltSize); 198 // _key.NumCyclesPower = 0x3F; 199 _key.NumCyclesPower = 19; 200 _aesFilter = new CAesCbcEncoder(kKeySize); 201 } 202 203 #endif 204 205 CDecoder::CDecoder() 206 { 207 _aesFilter = new CAesCbcDecoder(kKeySize); 208 } 209 210 STDMETHODIMP CDecoder::SetDecoderProperties2(const Byte *data, UInt32 size) 211 { 212 _key.ClearProps(); 213 214 _ivSize = 0; 215 unsigned i; 216 for (i = 0; i < sizeof(_iv); i++) 217 _iv[i] = 0; 218 219 if (size == 0) 220 return S_OK; 221 222 Byte b0 = data[0]; 223 224 _key.NumCyclesPower = b0 & 0x3F; 225 if ((b0 & 0xC0) == 0) 226 return size == 1 ? S_OK : E_INVALIDARG; 227 228 if (size <= 1) 229 return E_INVALIDARG; 230 231 Byte b1 = data[1]; 232 233 unsigned saltSize = ((b0 >> 7) & 1) + (b1 >> 4); 234 unsigned ivSize = ((b0 >> 6) & 1) + (b1 & 0x0F); 235 236 if (size != 2 + saltSize + ivSize) 237 return E_INVALIDARG; 238 _key.SaltSize = saltSize; 239 data += 2; 240 for (i = 0; i < saltSize; i++) 241 _key.Salt[i] = *data++; 242 for (i = 0; i < ivSize; i++) 243 _iv[i] = *data++; 244 return (_key.NumCyclesPower <= k_NumCyclesPower_Supported_MAX 245 || _key.NumCyclesPower == 0x3F) ? S_OK : E_NOTIMPL; 246 } 247 248 249 STDMETHODIMP CBaseCoder::CryptoSetPassword(const Byte *data, UInt32 size) 250 { 251 COM_TRY_BEGIN 252 253 _key.Password.CopyFrom(data, (size_t)size); 254 return S_OK; 255 256 COM_TRY_END 257 } 258 259 STDMETHODIMP CBaseCoder::Init() 260 { 261 COM_TRY_BEGIN 262 263 PrepareKey(); 264 CMyComPtr<ICryptoProperties> cp; 265 RINOK(_aesFilter.QueryInterface(IID_ICryptoProperties, &cp)); 266 if (!cp) 267 return E_FAIL; 268 RINOK(cp->SetKey(_key.Key, kKeySize)); 269 RINOK(cp->SetInitVector(_iv, sizeof(_iv))); 270 return _aesFilter->Init(); 271 272 COM_TRY_END 273 } 274 275 STDMETHODIMP_(UInt32) CBaseCoder::Filter(Byte *data, UInt32 size) 276 { 277 return _aesFilter->Filter(data, size); 278 } 279 280 }} 281