tdf#130857 qt weld: Support mail merge "Server Auth" dialog
[LibreOffice.git] / oox / source / crypto / Standard2007Engine.cxx
bloba9fb8d3c2339dbd2c906239b6cba2546ac3aee3c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 */
11 #include <oox/crypto/Standard2007Engine.hxx>
13 #include <oox/helper/binaryinputstream.hxx>
14 #include <oox/helper/binaryoutputstream.hxx>
15 #include <rtl/random.h>
17 #include <comphelper/crypto/Crypto.hxx>
18 #include <comphelper/hash.hxx>
20 namespace oox::crypto {
22 /* =========================================================================== */
23 /* Kudos to Caolan McNamara who provided the core decryption implementations. */
24 /* =========================================================================== */
25 namespace
28 void lclRandomGenerateValues(sal_uInt8* aArray, sal_uInt32 aSize)
30 if (rtl_random_getBytes(nullptr, aArray, aSize) != rtl_Random_E_None)
32 throw css::uno::RuntimeException(u"rtl_random_getBytes failed"_ustr);
36 constexpr OUString lclCspName = u"Microsoft Enhanced RSA and AES Cryptographic Provider"_ustr;
37 constexpr const sal_uInt32 AES128Size = 16;
39 } // end anonymous namespace
41 bool Standard2007Engine::generateVerifier()
43 // only support key of size 128 bit (16 byte)
44 if (mKey.size() != 16)
45 return false;
47 std::vector<sal_uInt8> verifier(msfilter::ENCRYPTED_VERIFIER_LENGTH);
48 std::vector<sal_uInt8> encryptedVerifier(msfilter::ENCRYPTED_VERIFIER_LENGTH);
50 lclRandomGenerateValues(verifier.data(), verifier.size());
52 std::vector<sal_uInt8> iv;
53 comphelper::Encrypt aEncryptorVerifier(mKey, iv, comphelper::CryptoType::AES_128_ECB);
54 if (aEncryptorVerifier.update(encryptedVerifier, verifier) != msfilter::ENCRYPTED_VERIFIER_LENGTH)
55 return false;
56 std::copy(encryptedVerifier.begin(), encryptedVerifier.end(), mInfo.verifier.encryptedVerifier);
58 mInfo.verifier.encryptedVerifierHashSize = comphelper::SHA1_HASH_LENGTH;
59 std::vector<sal_uInt8> hash = comphelper::Hash::calculateHash(verifier.data(), verifier.size(), comphelper::HashType::SHA1);
60 hash.resize(comphelper::SHA256_HASH_LENGTH, 0);
62 std::vector<sal_uInt8> encryptedHash(comphelper::SHA256_HASH_LENGTH, 0);
64 comphelper::Encrypt aEncryptorHash(mKey, iv, comphelper::CryptoType::AES_128_ECB);
65 aEncryptorHash.update(encryptedHash, hash, hash.size());
66 std::copy(encryptedHash.begin(), encryptedHash.end(), mInfo.verifier.encryptedVerifierHash);
68 return true;
71 bool Standard2007Engine::calculateEncryptionKey(std::u16string_view rPassword)
73 sal_uInt32 saltSize = mInfo.verifier.saltSize;
74 size_t passwordByteLength = rPassword.size() * 2;
75 const sal_uInt8* saltArray = mInfo.verifier.salt;
77 // Prepare initial data -> salt + password (in 16-bit chars)
78 std::vector<sal_uInt8> initialData(saltSize + passwordByteLength);
79 std::copy(saltArray, saltArray + saltSize, initialData.begin());
81 auto p = initialData.begin() + saltSize;
82 for (size_t i = 0; i != rPassword.size(); ++i) {
83 auto c = rPassword[i];
84 *p++ = c & 0xFF;
85 *p++ = c >> 8;
88 // use "hash" vector for result of sha1 hashing
89 // calculate SHA1 hash of initialData
90 std::vector<sal_uInt8> hash = comphelper::Hash::calculateHash(initialData.data(), initialData.size(), comphelper::HashType::SHA1);
92 // data = iterator (4bytes) + hash
93 std::vector<sal_uInt8> data(comphelper::SHA1_HASH_LENGTH + 4, 0);
95 for (sal_Int32 i = 0; i < 50000; ++i)
97 ByteOrderConverter::writeLittleEndian(data.data(), i);
98 std::copy(hash.begin(), hash.end(), data.begin() + 4);
99 hash = comphelper::Hash::calculateHash(data.data(), data.size(), comphelper::HashType::SHA1);
101 std::copy(hash.begin(), hash.end(), data.begin() );
102 std::fill(data.begin() + comphelper::SHA1_HASH_LENGTH, data.end(), 0 );
104 hash = comphelper::Hash::calculateHash(data.data(), data.size(), comphelper::HashType::SHA1);
106 // derive key
107 std::vector<sal_uInt8> buffer(64, 0x36);
108 for (size_t i = 0; i < hash.size(); ++i)
109 buffer[i] ^= hash[i];
111 hash = comphelper::Hash::calculateHash(buffer.data(), buffer.size(), comphelper::HashType::SHA1);
112 if (mKey.size() > hash.size())
113 return false;
114 std::copy(hash.begin(), hash.begin() + mKey.size(), mKey.begin());
116 return true;
119 bool Standard2007Engine::generateEncryptionKey(const OUString& password)
121 mKey.clear();
123 KeySize (4 bytes): An unsigned integer that specifies the number of bits in the encryption key.
124 MUST be a multiple of 8. MUST be one of the values in the following table:
125 Algorithm Value Comment
126 Any 0x00000000 Determined by Flags
127 RC4 0x00000028 – 0x00000080 (inclusive) 8-bit increments.
128 AES 0x00000080, 0x000000C0, 0x00000100 128, 192 or 256-bit
130 if (mInfo.header.keyBits > 8192) // should we strictly enforce the above 256 bit limit ?
131 return false;
132 mKey.resize(mInfo.header.keyBits / 8, 0);
133 if (mKey.empty())
134 return false;
136 calculateEncryptionKey(password);
138 std::vector<sal_uInt8> encryptedVerifier(msfilter::ENCRYPTED_VERIFIER_LENGTH);
139 std::copy(
140 mInfo.verifier.encryptedVerifier,
141 mInfo.verifier.encryptedVerifier + msfilter::ENCRYPTED_VERIFIER_LENGTH,
142 encryptedVerifier.begin());
144 std::vector<sal_uInt8> encryptedHash(comphelper::SHA256_HASH_LENGTH);
145 std::copy(
146 mInfo.verifier.encryptedVerifierHash,
147 mInfo.verifier.encryptedVerifierHash + comphelper::SHA256_HASH_LENGTH,
148 encryptedHash.begin());
150 std::vector<sal_uInt8> verifier(encryptedVerifier.size(), 0);
151 comphelper::Decrypt::aes128ecb(verifier, encryptedVerifier, mKey);
153 std::vector<sal_uInt8> verifierHash(encryptedHash.size(), 0);
154 comphelper::Decrypt::aes128ecb(verifierHash, encryptedHash, mKey);
156 std::vector<sal_uInt8> hash = comphelper::Hash::calculateHash(verifier.data(), verifier.size(), comphelper::HashType::SHA1);
158 return std::equal(hash.begin(), hash.end(), verifierHash.begin());
161 bool Standard2007Engine::decrypt(BinaryXInputStream& aInputStream,
162 BinaryXOutputStream& aOutputStream)
164 sal_uInt32 totalSize = aInputStream.readuInt32(); // Document unencrypted size - 4 bytes
165 aInputStream.skip(4); // Reserved 4 Bytes
167 std::vector<sal_uInt8> iv;
168 comphelper::Decrypt aDecryptor(mKey, iv, comphelper::CryptoType::AES_128_ECB);
169 std::vector<sal_uInt8> inputBuffer (4096);
170 std::vector<sal_uInt8> outputBuffer(4096);
171 sal_uInt32 inputLength;
172 sal_uInt32 outputLength;
173 sal_uInt32 remaining = totalSize;
175 while ((inputLength = aInputStream.readMemory(inputBuffer.data(), inputBuffer.size())) > 0)
177 outputLength = aDecryptor.update(outputBuffer, inputBuffer, inputLength);
178 sal_uInt32 writeLength = std::min(outputLength, remaining);
179 aOutputStream.writeMemory(outputBuffer.data(), writeLength);
180 remaining -= outputLength;
182 return true;
185 bool Standard2007Engine::checkDataIntegrity()
187 return true;
190 bool Standard2007Engine::setupEncryption(OUString const & password)
192 mInfo.header.flags = msfilter::ENCRYPTINFO_AES | msfilter::ENCRYPTINFO_CRYPTOAPI;
193 mInfo.header.algId = msfilter::ENCRYPT_ALGO_AES128;
194 mInfo.header.algIdHash = msfilter::ENCRYPT_HASH_SHA1;
195 mInfo.header.keyBits = msfilter::ENCRYPT_KEY_SIZE_AES_128;
196 mInfo.header.providedType = msfilter::ENCRYPT_PROVIDER_TYPE_AES;
198 lclRandomGenerateValues(mInfo.verifier.salt, mInfo.verifier.saltSize);
199 const sal_Int32 keyLength = mInfo.header.keyBits / 8;
201 mKey.clear();
202 mKey.resize(keyLength, 0);
204 if (!calculateEncryptionKey(password))
205 return false;
207 if (!generateVerifier())
208 return false;
210 return true;
213 void Standard2007Engine::writeEncryptionInfo(BinaryXOutputStream& rStream)
215 rStream.WriteUInt32(msfilter::VERSION_INFO_2007_FORMAT);
217 sal_uInt32 cspNameSize = (lclCspName.getLength() * 2) + 2;
219 sal_uInt32 encryptionHeaderSize = static_cast<sal_uInt32>(sizeof(msfilter::EncryptionStandardHeader));
221 rStream.WriteUInt32(mInfo.header.flags);
222 sal_uInt32 headerSize = encryptionHeaderSize + cspNameSize;
223 rStream.WriteUInt32(headerSize);
225 rStream.WriteUInt32(mInfo.header.flags);
226 rStream.WriteUInt32(mInfo.header.sizeExtra);
227 rStream.WriteUInt32(mInfo.header.algId);
228 rStream.WriteUInt32(mInfo.header.algIdHash);
229 rStream.WriteUInt32(mInfo.header.keyBits);
230 rStream.WriteUInt32(mInfo.header.providedType);
231 rStream.WriteUInt32(mInfo.header.reserved1);
232 rStream.WriteUInt32(mInfo.header.reserved2);
233 rStream.writeUnicodeArray(lclCspName);
234 rStream.WriteUInt16(0);
236 rStream.WriteUInt32(mInfo.verifier.saltSize);
237 rStream.writeMemory(&mInfo.verifier.salt, sizeof mInfo.verifier.salt);
238 rStream.writeMemory(&mInfo.verifier.encryptedVerifier, sizeof mInfo.verifier.encryptedVerifier);
239 rStream.WriteUInt32(mInfo.verifier.encryptedVerifierHashSize);
240 rStream.writeMemory(
241 &mInfo.verifier.encryptedVerifierHash, sizeof mInfo.verifier.encryptedVerifierHash);
244 void Standard2007Engine::encrypt(const css::uno::Reference<css::io::XInputStream> & rxInputStream,
245 css::uno::Reference<css::io::XOutputStream> & rxOutputStream,
246 sal_uInt32 nSize)
248 if (mKey.empty())
249 return;
251 BinaryXOutputStream aBinaryOutputStream(rxOutputStream, false);
252 BinaryXInputStream aBinaryInputStream(rxInputStream, false);
254 aBinaryOutputStream.WriteUInt32(nSize); // size
255 aBinaryOutputStream.WriteUInt32(0U); // reserved
257 std::vector<sal_uInt8> inputBuffer(1024);
258 std::vector<sal_uInt8> outputBuffer(1024);
260 sal_uInt32 inputLength;
261 sal_uInt32 outputLength;
263 std::vector<sal_uInt8> iv;
264 comphelper::Encrypt aEncryptor(mKey, iv, comphelper::CryptoType::AES_128_ECB);
266 while ((inputLength = aBinaryInputStream.readMemory(inputBuffer.data(), inputBuffer.size())) > 0)
268 // increase size to multiple of 16 (size of mKey) if necessary
269 inputLength = inputLength % AES128Size == 0 ?
270 inputLength : comphelper::roundUp(inputLength, AES128Size);
271 outputLength = aEncryptor.update(outputBuffer, inputBuffer, inputLength);
272 aBinaryOutputStream.writeMemory(outputBuffer.data(), outputLength);
276 bool Standard2007Engine::readEncryptionInfo(css::uno::Reference<css::io::XInputStream> & rxInputStream)
278 BinaryXInputStream aBinaryStream(rxInputStream, false);
280 mInfo.header.flags = aBinaryStream.readuInt32();
281 if (getFlag(mInfo.header.flags, msfilter::ENCRYPTINFO_EXTERNAL))
282 return false;
284 sal_uInt32 nHeaderSize = aBinaryStream.readuInt32();
286 sal_uInt32 actualHeaderSize = sizeof(mInfo.header);
288 if (nHeaderSize < actualHeaderSize)
289 return false;
291 mInfo.header.flags = aBinaryStream.readuInt32();
292 mInfo.header.sizeExtra = aBinaryStream.readuInt32();
293 mInfo.header.algId = aBinaryStream.readuInt32();
294 mInfo.header.algIdHash = aBinaryStream.readuInt32();
295 mInfo.header.keyBits = aBinaryStream.readuInt32();
296 mInfo.header.providedType = aBinaryStream.readuInt32();
297 mInfo.header.reserved1 = aBinaryStream.readuInt32();
298 mInfo.header.reserved2 = aBinaryStream.readuInt32();
300 aBinaryStream.skip(nHeaderSize - actualHeaderSize);
302 mInfo.verifier.saltSize = aBinaryStream.readuInt32();
303 aBinaryStream.readArray(mInfo.verifier.salt, SAL_N_ELEMENTS(mInfo.verifier.salt));
304 aBinaryStream.readArray(mInfo.verifier.encryptedVerifier, SAL_N_ELEMENTS(mInfo.verifier.encryptedVerifier));
305 mInfo.verifier.encryptedVerifierHashSize = aBinaryStream.readuInt32();
306 aBinaryStream.readArray(mInfo.verifier.encryptedVerifierHash, SAL_N_ELEMENTS(mInfo.verifier.encryptedVerifierHash));
308 if (mInfo.verifier.saltSize != 16)
309 return false;
311 // check flags and algorithm IDs, required are AES128 and SHA-1
312 if (!getFlag(mInfo.header.flags, msfilter::ENCRYPTINFO_CRYPTOAPI))
313 return false;
315 if (!getFlag(mInfo.header.flags, msfilter::ENCRYPTINFO_AES))
316 return false;
318 // algorithm ID 0 defaults to AES128 too, if ENCRYPTINFO_AES flag is set
319 if (mInfo.header.algId != 0 && mInfo.header.algId != msfilter::ENCRYPT_ALGO_AES128)
320 return false;
322 // hash algorithm ID 0 defaults to SHA-1 too
323 if (mInfo.header.algIdHash != 0 && mInfo.header.algIdHash != msfilter::ENCRYPT_HASH_SHA1)
324 return false;
326 if (mInfo.verifier.encryptedVerifierHashSize != 20)
327 return false;
329 return !aBinaryStream.isEof();
332 } // namespace oox::crypto
334 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */