LibreOffice Module oox (master) 1
Standard2007Engine.cxx
Go to the documentation of this file.
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
12
16#include <rtl/random.h>
17
18#include <comphelper/hash.hxx>
19
20namespace oox::crypto {
21
22/* =========================================================================== */
23/* Kudos to Caolan McNamara who provided the core decryption implementations. */
24/* =========================================================================== */
25namespace
26{
27
28void 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
35constexpr OUStringLiteral lclCspName = u"Microsoft Enhanced RSA and AES Cryptographic Provider";
36constexpr const sal_uInt32 AES128Size = 16;
37
38} // end anonymous namespace
39
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
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
70bool Standard2007Engine::calculateEncryptionKey(std::u16string_view rPassword)
71{
72 sal_uInt32 saltSize = mInfo.verifier.saltSize;
73 size_t passwordByteLength = rPassword.size() * 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 (size_t i = 0; i != rPassword.size(); ++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 {
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
118bool 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(
141 encryptedVerifier.begin());
142
143 std::vector<sal_uInt8> encryptedHash(comphelper::SHA256_HASH_LENGTH);
144 std::copy(
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
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
185{
186 return true;
187}
188
189bool Standard2007Engine::setupEncryption(OUString const & password)
190{
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
213{
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);
226 rStream.WriteUInt32(mInfo.header.algId);
232 rStream.writeUnicodeArray(lclCspName);
233 rStream.WriteUInt16(0);
234
236 rStream.writeMemory(&mInfo.verifier.salt, sizeof mInfo.verifier.salt);
239 rStream.writeMemory(
241}
242
243void 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
275bool Standard2007Engine::readEncryptionInfo(css::uno::Reference<css::io::XInputStream> & rxInputStream)
276{
277 BinaryXInputStream aBinaryStream(rxInputStream, false);
278
279 mInfo.header.flags = aBinaryStream.readuInt32();
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();
306
307 if (mInfo.verifier.saltSize != 16)
308 return false;
309
310 // check flags and algorithm IDs, required are AES128 and SHA-1
312 return false;
313
315 return false;
316
317 // algorithm ID 0 defaults to AES128 too, if ENCRYPTINFO_AES flag is set
319 return false;
320
321 // hash algorithm ID 0 defaults to SHA-1 too
323 return false;
324
326 return false;
327
328 return !aBinaryStream.isEof();
329}
330
331} // namespace oox::crypto
332
333/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void * rtlRandomPool
static std::vector< unsigned char > calculateHash(const unsigned char *pInput, size_t length, HashType eType)
sal_Int32 readArray(Type *opnArray, sal_Int32 nElemCount)
Reads a (preallocated!) C array of values from the stream.
BinaryOutputStream & WriteUInt32(sal_uInt32 x)
BinaryOutputStream & WriteUInt16(sal_uInt16 x)
void writeUnicodeArray(const OUString &rString)
bool isEof() const
Returns true, if the stream position is invalid (EOF).
Wraps a UNO input stream and provides convenient access functions.
virtual void skip(sal_Int32 nBytes, size_t nAtomSize=1) override
Seeks the stream forward by the passed number of bytes.
virtual sal_Int32 readMemory(void *opMem, sal_Int32 nBytes, size_t nAtomSize=1) override
Reads nBytes bytes to the (existing) buffer opMem.
Wraps a UNO output stream and provides convenient access functions.
virtual void writeMemory(const void *pMem, sal_Int32 nBytes, size_t nAtomSize=1) override
Write nBytes bytes from the (preallocated!) buffer pMem.
static void writeLittleEndian(void *pDstBuffer, Type nValue)
Writes a value to memory, while converting it to little-endian.
Definition: helper.hxx:239
std::vector< sal_uInt8 > mKey
sal_uInt32 update(std::vector< sal_uInt8 > &output, std::vector< sal_uInt8 > &input, sal_uInt32 inputLength=0)
Definition: CryptTools.cxx:378
static sal_uInt32 aes128ecb(std::vector< sal_uInt8 > &output, std::vector< sal_uInt8 > &input, std::vector< sal_uInt8 > &key)
Definition: CryptTools.cxx:403
sal_uInt32 update(std::vector< sal_uInt8 > &output, std::vector< sal_uInt8 > &input, sal_uInt32 inputLength=0)
Definition: CryptTools.cxx:429
virtual bool setupEncryption(OUString const &rPassword) override
virtual bool generateEncryptionKey(OUString const &rPassword) override
void encrypt(const css::uno::Reference< css::io::XInputStream > &rxInputStream, css::uno::Reference< css::io::XOutputStream > &rxOutputStream, sal_uInt32 nSize) override
msfilter::StandardEncryptionInfo mInfo
virtual void writeEncryptionInfo(BinaryXOutputStream &rStream) override
bool calculateEncryptionKey(std::u16string_view rPassword)
bool readEncryptionInfo(css::uno::Reference< css::io::XInputStream > &rxInputStream) override
virtual bool decrypt(BinaryXInputStream &aInputStream, BinaryXOutputStream &aOutputStream) override
float u
void * p
#define SAL_N_ELEMENTS(arr)
const sal_uInt32 SHA256_HASH_LENGTH
const sal_uInt32 SHA1_HASH_LENGTH
const sal_uInt32 ENCRYPT_PROVIDER_TYPE_AES
const sal_uInt32 VERSION_INFO_2007_FORMAT
const sal_uInt32 ENCRYPT_KEY_SIZE_AES_128
const sal_uInt32 ENCRYPT_ALGO_AES128
const sal_uInt32 ENCRYPT_HASH_SHA1
const sal_uInt32 ENCRYPTINFO_AES
const sal_uInt32 ENCRYPTED_VERIFIER_LENGTH
const sal_uInt32 ENCRYPTINFO_CRYPTOAPI
const sal_uInt32 ENCRYPTINFO_EXTERNAL
T roundUp(T input, T multiple)
Rounds up the input to the nearest multiple.
Definition: CryptTools.hxx:40
bool getFlag(Type nBitField, Type nMask)
Returns true, if at least one of the bits set in nMask is set in nBitField.
Definition: helper.hxx:137
sal_uInt8 salt[SALT_LENGTH]
sal_uInt8 encryptedVerifierHash[comphelper::SHA256_HASH_LENGTH]
sal_uInt8 encryptedVerifier[ENCRYPTED_VERIFIER_LENGTH]
EncryptionVerifierAES verifier
EncryptionStandardHeader header
unsigned char sal_uInt8