1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  */
10 
11 #include <oox/crypto/Standard2007Engine.hxx>
12 
13 #include <oox/crypto/CryptTools.hxx>
14 #include <oox/helper/binaryinputstream.hxx>
15 #include <oox/helper/binaryoutputstream.hxx>
16 #include <rtl/random.h>
17 
18 #include <comphelper/hash.hxx>
19 
20 namespace oox::crypto {
21 
22 /* =========================================================================== */
23 /*  Kudos to Caolan McNamara who provided the core decryption implementations. */
24 /* =========================================================================== */
25 namespace
26 {
27 
lclRandomGenerateValues(sal_uInt8 * aArray,sal_uInt32 aSize)28 void lclRandomGenerateValues(sal_uInt8* aArray, sal_uInt32 aSize)
29 {
30     rtlRandomPool aRandomPool = rtl_random_createPool();
31     rtl_random_getBytes(aRandomPool, aArray, aSize);
32     rtl_random_destroyPool(aRandomPool);
33 }
34 
35 constexpr OUStringLiteral lclCspName = u"Microsoft Enhanced RSA and AES Cryptographic Provider";
36 constexpr const sal_uInt32 AES128Size = 16;
37 
38 } // end anonymous namespace
39 
generateVerifier()40 bool Standard2007Engine::generateVerifier()
41 {
42     // only support key of size 128 bit (16 byte)
43     if (mKey.size() != 16)
44         return false;
45 
46     std::vector<sal_uInt8> verifier(msfilter::ENCRYPTED_VERIFIER_LENGTH);
47     std::vector<sal_uInt8> encryptedVerifier(msfilter::ENCRYPTED_VERIFIER_LENGTH);
48 
49     lclRandomGenerateValues(verifier.data(), verifier.size());
50 
51     std::vector<sal_uInt8> iv;
52     Encrypt aEncryptorVerifier(mKey, iv, Crypto::AES_128_ECB);
53     if (aEncryptorVerifier.update(encryptedVerifier, verifier) != msfilter::ENCRYPTED_VERIFIER_LENGTH)
54         return false;
55     std::copy(encryptedVerifier.begin(), encryptedVerifier.end(), mInfo.verifier.encryptedVerifier);
56 
57     mInfo.verifier.encryptedVerifierHashSize = comphelper::SHA1_HASH_LENGTH;
58     std::vector<sal_uInt8> hash = comphelper::Hash::calculateHash(verifier.data(), verifier.size(), comphelper::HashType::SHA1);
59     hash.resize(comphelper::SHA256_HASH_LENGTH, 0);
60 
61     std::vector<sal_uInt8> encryptedHash(comphelper::SHA256_HASH_LENGTH, 0);
62 
63     Encrypt aEncryptorHash(mKey, iv, Crypto::AES_128_ECB);
64     aEncryptorHash.update(encryptedHash, hash, hash.size());
65     std::copy(encryptedHash.begin(), encryptedHash.end(), mInfo.verifier.encryptedVerifierHash);
66 
67     return true;
68 }
69 
calculateEncryptionKey(const OUString & rPassword)70 bool Standard2007Engine::calculateEncryptionKey(const OUString& rPassword)
71 {
72     sal_uInt32 saltSize = mInfo.verifier.saltSize;
73     sal_uInt32 passwordByteLength = rPassword.getLength() * 2;
74     const sal_uInt8* saltArray = mInfo.verifier.salt;
75 
76     // Prepare initial data -> salt + password (in 16-bit chars)
77     std::vector<sal_uInt8> initialData(saltSize + passwordByteLength);
78     std::copy(saltArray, saltArray + saltSize, initialData.begin());
79 
80     auto p = initialData.begin() + saltSize;
81     for (sal_Int32 i = 0; i != rPassword.getLength(); ++i) {
82         auto c = rPassword[i];
83         *p++ = c & 0xFF;
84         *p++ = c >> 8;
85     }
86 
87     // use "hash" vector for result of sha1 hashing
88     // calculate SHA1 hash of initialData
89     std::vector<sal_uInt8> hash = comphelper::Hash::calculateHash(initialData.data(), initialData.size(), comphelper::HashType::SHA1);
90 
91     // data = iterator (4bytes) + hash
92     std::vector<sal_uInt8> data(comphelper::SHA1_HASH_LENGTH + 4, 0);
93 
94     for (sal_Int32 i = 0; i < 50000; ++i)
95     {
96         ByteOrderConverter::writeLittleEndian(data.data(), i);
97         std::copy(hash.begin(), hash.end(), data.begin() + 4);
98         hash = comphelper::Hash::calculateHash(data.data(), data.size(), comphelper::HashType::SHA1);
99     }
100     std::copy(hash.begin(), hash.end(), data.begin() );
101     std::fill(data.begin() + comphelper::SHA1_HASH_LENGTH, data.end(), 0 );
102 
103     hash = comphelper::Hash::calculateHash(data.data(), data.size(), comphelper::HashType::SHA1);
104 
105     // derive key
106     std::vector<sal_uInt8> buffer(64, 0x36);
107     for (size_t i = 0; i < hash.size(); ++i)
108         buffer[i] ^= hash[i];
109 
110     hash = comphelper::Hash::calculateHash(buffer.data(), buffer.size(), comphelper::HashType::SHA1);
111     if (mKey.size() > hash.size())
112         return false;
113     std::copy(hash.begin(), hash.begin() + mKey.size(), mKey.begin());
114 
115     return true;
116 }
117 
generateEncryptionKey(const OUString & password)118 bool Standard2007Engine::generateEncryptionKey(const OUString& password)
119 {
120     mKey.clear();
121     /*
122         KeySize (4 bytes): An unsigned integer that specifies the number of bits in the encryption key.
123         MUST be a multiple of 8. MUST be one of the values in the following table:
124         Algorithm   Value                               Comment
125         Any         0x00000000                          Determined by Flags
126         RC4         0x00000028 – 0x00000080             (inclusive) 8-bit increments.
127         AES         0x00000080, 0x000000C0, 0x00000100  128, 192 or 256-bit
128     */
129     if (mInfo.header.keyBits > 8192) // should we strictly enforce the above 256 bit limit ?
130         return false;
131     mKey.resize(mInfo.header.keyBits / 8, 0);
132     if (mKey.empty())
133         return false;
134 
135     calculateEncryptionKey(password);
136 
137     std::vector<sal_uInt8> encryptedVerifier(msfilter::ENCRYPTED_VERIFIER_LENGTH);
138     std::copy(
139         mInfo.verifier.encryptedVerifier,
140         mInfo.verifier.encryptedVerifier + msfilter::ENCRYPTED_VERIFIER_LENGTH,
141         encryptedVerifier.begin());
142 
143     std::vector<sal_uInt8> encryptedHash(comphelper::SHA256_HASH_LENGTH);
144     std::copy(
145         mInfo.verifier.encryptedVerifierHash,
146         mInfo.verifier.encryptedVerifierHash + comphelper::SHA256_HASH_LENGTH,
147         encryptedHash.begin());
148 
149     std::vector<sal_uInt8> verifier(encryptedVerifier.size(), 0);
150     Decrypt::aes128ecb(verifier, encryptedVerifier, mKey);
151 
152     std::vector<sal_uInt8> verifierHash(encryptedHash.size(), 0);
153     Decrypt::aes128ecb(verifierHash, encryptedHash, mKey);
154 
155     std::vector<sal_uInt8> hash = comphelper::Hash::calculateHash(verifier.data(), verifier.size(), comphelper::HashType::SHA1);
156 
157     return std::equal(hash.begin(), hash.end(), verifierHash.begin());
158 }
159 
decrypt(BinaryXInputStream & aInputStream,BinaryXOutputStream & aOutputStream)160 bool Standard2007Engine::decrypt(BinaryXInputStream& aInputStream,
161                                  BinaryXOutputStream& aOutputStream)
162 {
163     sal_uInt32 totalSize = aInputStream.readuInt32(); // Document unencrypted size - 4 bytes
164     aInputStream.skip(4); // Reserved 4 Bytes
165 
166     std::vector<sal_uInt8> iv;
167     Decrypt aDecryptor(mKey, iv, Crypto::AES_128_ECB);
168     std::vector<sal_uInt8> inputBuffer (4096);
169     std::vector<sal_uInt8> outputBuffer(4096);
170     sal_uInt32 inputLength;
171     sal_uInt32 outputLength;
172     sal_uInt32 remaining = totalSize;
173 
174     while ((inputLength = aInputStream.readMemory(inputBuffer.data(), inputBuffer.size())) > 0)
175     {
176         outputLength = aDecryptor.update(outputBuffer, inputBuffer, inputLength);
177         sal_uInt32 writeLength = std::min(outputLength, remaining);
178         aOutputStream.writeMemory(outputBuffer.data(), writeLength);
179         remaining -= outputLength;
180     }
181     return true;
182 }
183 
checkDataIntegrity()184 bool Standard2007Engine::checkDataIntegrity()
185 {
186     return true;
187 }
188 
setupEncryption(OUString const & password)189 bool Standard2007Engine::setupEncryption(OUString const & password)
190 {
191     mInfo.header.flags        = msfilter::ENCRYPTINFO_AES | msfilter::ENCRYPTINFO_CRYPTOAPI;
192     mInfo.header.algId        = msfilter::ENCRYPT_ALGO_AES128;
193     mInfo.header.algIdHash    = msfilter::ENCRYPT_HASH_SHA1;
194     mInfo.header.keyBits      = msfilter::ENCRYPT_KEY_SIZE_AES_128;
195     mInfo.header.providedType = msfilter::ENCRYPT_PROVIDER_TYPE_AES;
196 
197     lclRandomGenerateValues(mInfo.verifier.salt, mInfo.verifier.saltSize);
198     const sal_Int32 keyLength = mInfo.header.keyBits / 8;
199 
200     mKey.clear();
201     mKey.resize(keyLength, 0);
202 
203     if (!calculateEncryptionKey(password))
204         return false;
205 
206     if (!generateVerifier())
207         return false;
208 
209     return true;
210 }
211 
writeEncryptionInfo(BinaryXOutputStream & rStream)212 void Standard2007Engine::writeEncryptionInfo(BinaryXOutputStream& rStream)
213 {
214     rStream.WriteUInt32(msfilter::VERSION_INFO_2007_FORMAT);
215 
216     sal_uInt32 cspNameSize = (lclCspName.getLength() * 2) + 2;
217 
218     sal_uInt32 encryptionHeaderSize = static_cast<sal_uInt32>(sizeof(msfilter::EncryptionStandardHeader));
219 
220     rStream.WriteUInt32(mInfo.header.flags);
221     sal_uInt32 headerSize = encryptionHeaderSize + cspNameSize;
222     rStream.WriteUInt32(headerSize);
223 
224     rStream.WriteUInt32(mInfo.header.flags);
225     rStream.WriteUInt32(mInfo.header.sizeExtra);
226     rStream.WriteUInt32(mInfo.header.algId);
227     rStream.WriteUInt32(mInfo.header.algIdHash);
228     rStream.WriteUInt32(mInfo.header.keyBits);
229     rStream.WriteUInt32(mInfo.header.providedType);
230     rStream.WriteUInt32(mInfo.header.reserved1);
231     rStream.WriteUInt32(mInfo.header.reserved2);
232     rStream.writeUnicodeArray(lclCspName);
233     rStream.WriteUInt16(0);
234 
235     rStream.WriteUInt32(mInfo.verifier.saltSize);
236     rStream.writeMemory(&mInfo.verifier.salt, sizeof mInfo.verifier.salt);
237     rStream.writeMemory(&mInfo.verifier.encryptedVerifier, sizeof mInfo.verifier.encryptedVerifier);
238     rStream.WriteUInt32(mInfo.verifier.encryptedVerifierHashSize);
239     rStream.writeMemory(
240         &mInfo.verifier.encryptedVerifierHash, sizeof mInfo.verifier.encryptedVerifierHash);
241 }
242 
encrypt(const css::uno::Reference<css::io::XInputStream> & rxInputStream,css::uno::Reference<css::io::XOutputStream> & rxOutputStream,sal_uInt32 nSize)243 void Standard2007Engine::encrypt(const css::uno::Reference<css::io::XInputStream> &  rxInputStream,
244                                  css::uno::Reference<css::io::XOutputStream> & rxOutputStream,
245                                  sal_uInt32 nSize)
246 {
247     if (mKey.empty())
248         return;
249 
250     BinaryXOutputStream aBinaryOutputStream(rxOutputStream, false);
251     BinaryXInputStream aBinaryInputStream(rxInputStream, false);
252 
253     aBinaryOutputStream.WriteUInt32(nSize); // size
254     aBinaryOutputStream.WriteUInt32(0U);    // reserved
255 
256     std::vector<sal_uInt8> inputBuffer(1024);
257     std::vector<sal_uInt8> outputBuffer(1024);
258 
259     sal_uInt32 inputLength;
260     sal_uInt32 outputLength;
261 
262     std::vector<sal_uInt8> iv;
263     Encrypt aEncryptor(mKey, iv, Crypto::AES_128_ECB);
264 
265     while ((inputLength = aBinaryInputStream.readMemory(inputBuffer.data(), inputBuffer.size())) > 0)
266     {
267         // increase size to multiple of 16 (size of mKey) if necessary
268         inputLength = inputLength % AES128Size == 0 ?
269                             inputLength : roundUp(inputLength, AES128Size);
270         outputLength = aEncryptor.update(outputBuffer, inputBuffer, inputLength);
271         aBinaryOutputStream.writeMemory(outputBuffer.data(), outputLength);
272     }
273 }
274 
readEncryptionInfo(css::uno::Reference<css::io::XInputStream> & rxInputStream)275 bool Standard2007Engine::readEncryptionInfo(css::uno::Reference<css::io::XInputStream> & rxInputStream)
276 {
277     BinaryXInputStream aBinaryStream(rxInputStream, false);
278 
279     mInfo.header.flags = aBinaryStream.readuInt32();
280     if (getFlag(mInfo.header.flags, msfilter::ENCRYPTINFO_EXTERNAL))
281         return false;
282 
283     sal_uInt32 nHeaderSize = aBinaryStream.readuInt32();
284 
285     sal_uInt32 actualHeaderSize = sizeof(mInfo.header);
286 
287     if (nHeaderSize < actualHeaderSize)
288         return false;
289 
290     mInfo.header.flags = aBinaryStream.readuInt32();
291     mInfo.header.sizeExtra = aBinaryStream.readuInt32();
292     mInfo.header.algId = aBinaryStream.readuInt32();
293     mInfo.header.algIdHash = aBinaryStream.readuInt32();
294     mInfo.header.keyBits = aBinaryStream.readuInt32();
295     mInfo.header.providedType = aBinaryStream.readuInt32();
296     mInfo.header.reserved1 = aBinaryStream.readuInt32();
297     mInfo.header.reserved2 = aBinaryStream.readuInt32();
298 
299     aBinaryStream.skip(nHeaderSize - actualHeaderSize);
300 
301     mInfo.verifier.saltSize = aBinaryStream.readuInt32();
302     aBinaryStream.readArray(mInfo.verifier.salt, SAL_N_ELEMENTS(mInfo.verifier.salt));
303     aBinaryStream.readArray(mInfo.verifier.encryptedVerifier, SAL_N_ELEMENTS(mInfo.verifier.encryptedVerifier));
304     mInfo.verifier.encryptedVerifierHashSize = aBinaryStream.readuInt32();
305     aBinaryStream.readArray(mInfo.verifier.encryptedVerifierHash, SAL_N_ELEMENTS(mInfo.verifier.encryptedVerifierHash));
306 
307     if (mInfo.verifier.saltSize != 16)
308         return false;
309 
310     // check flags and algorithm IDs, required are AES128 and SHA-1
311     if (!getFlag(mInfo.header.flags, msfilter::ENCRYPTINFO_CRYPTOAPI))
312         return false;
313 
314     if (!getFlag(mInfo.header.flags, msfilter::ENCRYPTINFO_AES))
315         return false;
316 
317     // algorithm ID 0 defaults to AES128 too, if ENCRYPTINFO_AES flag is set
318     if (mInfo.header.algId != 0 && mInfo.header.algId != msfilter::ENCRYPT_ALGO_AES128)
319         return false;
320 
321     // hash algorithm ID 0 defaults to SHA-1 too
322     if (mInfo.header.algIdHash != 0 && mInfo.header.algIdHash != msfilter::ENCRYPT_HASH_SHA1)
323         return false;
324 
325     if (mInfo.verifier.encryptedVerifierHashSize != 20)
326         return false;
327 
328     return !aBinaryStream.isEof();
329 }
330 
331 } // namespace oox::crypto
332 
333 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
334