1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
11 #include <oox/crypto/Standard2007Engine.hxx>
13 #include <oox/crypto/CryptTools.hxx>
14 #include <oox/helper/binaryinputstream.hxx>
15 #include <oox/helper/binaryoutputstream.hxx>
16 #include <rtl/random.h>
18 #include <comphelper/hash.hxx>
20 namespace oox::crypto
{
22 /* =========================================================================== */
23 /* Kudos to Caolan McNamara who provided the core decryption implementations. */
24 /* =========================================================================== */
28 void lclRandomGenerateValues(sal_uInt8
* aArray
, sal_uInt32 aSize
)
30 rtlRandomPool aRandomPool
= rtl_random_createPool();
31 rtl_random_getBytes(aRandomPool
, aArray
, aSize
);
32 rtl_random_destroyPool(aRandomPool
);
35 constexpr OUStringLiteral lclCspName
= u
"Microsoft Enhanced RSA and AES Cryptographic Provider";
36 constexpr const sal_uInt32 AES128Size
= 16;
38 } // end anonymous namespace
40 bool Standard2007Engine::generateVerifier()
42 // only support key of size 128 bit (16 byte)
43 if (mKey
.size() != 16)
46 std::vector
<sal_uInt8
> verifier(msfilter::ENCRYPTED_VERIFIER_LENGTH
);
47 std::vector
<sal_uInt8
> encryptedVerifier(msfilter::ENCRYPTED_VERIFIER_LENGTH
);
49 lclRandomGenerateValues(verifier
.data(), verifier
.size());
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
)
55 std::copy(encryptedVerifier
.begin(), encryptedVerifier
.end(), mInfo
.verifier
.encryptedVerifier
);
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);
61 std::vector
<sal_uInt8
> encryptedHash(comphelper::SHA256_HASH_LENGTH
, 0);
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
);
70 bool Standard2007Engine::calculateEncryptionKey(std::u16string_view rPassword
)
72 sal_uInt32 saltSize
= mInfo
.verifier
.saltSize
;
73 size_t passwordByteLength
= rPassword
.size() * 2;
74 const sal_uInt8
* saltArray
= mInfo
.verifier
.salt
;
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());
80 auto p
= initialData
.begin() + saltSize
;
81 for (size_t i
= 0; i
!= rPassword
.size(); ++i
) {
82 auto c
= rPassword
[i
];
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
);
91 // data = iterator (4bytes) + hash
92 std::vector
<sal_uInt8
> data(comphelper::SHA1_HASH_LENGTH
+ 4, 0);
94 for (sal_Int32 i
= 0; i
< 50000; ++i
)
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
);
100 std::copy(hash
.begin(), hash
.end(), data
.begin() );
101 std::fill(data
.begin() + comphelper::SHA1_HASH_LENGTH
, data
.end(), 0 );
103 hash
= comphelper::Hash::calculateHash(data
.data(), data
.size(), comphelper::HashType::SHA1
);
106 std::vector
<sal_uInt8
> buffer(64, 0x36);
107 for (size_t i
= 0; i
< hash
.size(); ++i
)
108 buffer
[i
] ^= hash
[i
];
110 hash
= comphelper::Hash::calculateHash(buffer
.data(), buffer
.size(), comphelper::HashType::SHA1
);
111 if (mKey
.size() > hash
.size())
113 std::copy(hash
.begin(), hash
.begin() + mKey
.size(), mKey
.begin());
118 bool Standard2007Engine::generateEncryptionKey(const OUString
& password
)
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
129 if (mInfo
.header
.keyBits
> 8192) // should we strictly enforce the above 256 bit limit ?
131 mKey
.resize(mInfo
.header
.keyBits
/ 8, 0);
135 calculateEncryptionKey(password
);
137 std::vector
<sal_uInt8
> encryptedVerifier(msfilter::ENCRYPTED_VERIFIER_LENGTH
);
139 mInfo
.verifier
.encryptedVerifier
,
140 mInfo
.verifier
.encryptedVerifier
+ msfilter::ENCRYPTED_VERIFIER_LENGTH
,
141 encryptedVerifier
.begin());
143 std::vector
<sal_uInt8
> encryptedHash(comphelper::SHA256_HASH_LENGTH
);
145 mInfo
.verifier
.encryptedVerifierHash
,
146 mInfo
.verifier
.encryptedVerifierHash
+ comphelper::SHA256_HASH_LENGTH
,
147 encryptedHash
.begin());
149 std::vector
<sal_uInt8
> verifier(encryptedVerifier
.size(), 0);
150 Decrypt::aes128ecb(verifier
, encryptedVerifier
, mKey
);
152 std::vector
<sal_uInt8
> verifierHash(encryptedHash
.size(), 0);
153 Decrypt::aes128ecb(verifierHash
, encryptedHash
, mKey
);
155 std::vector
<sal_uInt8
> hash
= comphelper::Hash::calculateHash(verifier
.data(), verifier
.size(), comphelper::HashType::SHA1
);
157 return std::equal(hash
.begin(), hash
.end(), verifierHash
.begin());
160 bool Standard2007Engine::decrypt(BinaryXInputStream
& aInputStream
,
161 BinaryXOutputStream
& aOutputStream
)
163 sal_uInt32 totalSize
= aInputStream
.readuInt32(); // Document unencrypted size - 4 bytes
164 aInputStream
.skip(4); // Reserved 4 Bytes
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
;
174 while ((inputLength
= aInputStream
.readMemory(inputBuffer
.data(), inputBuffer
.size())) > 0)
176 outputLength
= aDecryptor
.update(outputBuffer
, inputBuffer
, inputLength
);
177 sal_uInt32 writeLength
= std::min(outputLength
, remaining
);
178 aOutputStream
.writeMemory(outputBuffer
.data(), writeLength
);
179 remaining
-= outputLength
;
184 bool Standard2007Engine::checkDataIntegrity()
189 bool Standard2007Engine::setupEncryption(OUString
const & password
)
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
;
197 lclRandomGenerateValues(mInfo
.verifier
.salt
, mInfo
.verifier
.saltSize
);
198 const sal_Int32 keyLength
= mInfo
.header
.keyBits
/ 8;
201 mKey
.resize(keyLength
, 0);
203 if (!calculateEncryptionKey(password
))
206 if (!generateVerifier())
212 void Standard2007Engine::writeEncryptionInfo(BinaryXOutputStream
& rStream
)
214 rStream
.WriteUInt32(msfilter::VERSION_INFO_2007_FORMAT
);
216 sal_uInt32 cspNameSize
= (lclCspName
.getLength() * 2) + 2;
218 sal_uInt32 encryptionHeaderSize
= static_cast<sal_uInt32
>(sizeof(msfilter::EncryptionStandardHeader
));
220 rStream
.WriteUInt32(mInfo
.header
.flags
);
221 sal_uInt32 headerSize
= encryptionHeaderSize
+ cspNameSize
;
222 rStream
.WriteUInt32(headerSize
);
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);
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
);
240 &mInfo
.verifier
.encryptedVerifierHash
, sizeof mInfo
.verifier
.encryptedVerifierHash
);
243 void Standard2007Engine::encrypt(const css::uno::Reference
<css::io::XInputStream
> & rxInputStream
,
244 css::uno::Reference
<css::io::XOutputStream
> & rxOutputStream
,
250 BinaryXOutputStream
aBinaryOutputStream(rxOutputStream
, false);
251 BinaryXInputStream
aBinaryInputStream(rxInputStream
, false);
253 aBinaryOutputStream
.WriteUInt32(nSize
); // size
254 aBinaryOutputStream
.WriteUInt32(0U); // reserved
256 std::vector
<sal_uInt8
> inputBuffer(1024);
257 std::vector
<sal_uInt8
> outputBuffer(1024);
259 sal_uInt32 inputLength
;
260 sal_uInt32 outputLength
;
262 std::vector
<sal_uInt8
> iv
;
263 Encrypt
aEncryptor(mKey
, iv
, Crypto::AES_128_ECB
);
265 while ((inputLength
= aBinaryInputStream
.readMemory(inputBuffer
.data(), inputBuffer
.size())) > 0)
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
);
275 bool Standard2007Engine::readEncryptionInfo(css::uno::Reference
<css::io::XInputStream
> & rxInputStream
)
277 BinaryXInputStream
aBinaryStream(rxInputStream
, false);
279 mInfo
.header
.flags
= aBinaryStream
.readuInt32();
280 if (getFlag(mInfo
.header
.flags
, msfilter::ENCRYPTINFO_EXTERNAL
))
283 sal_uInt32 nHeaderSize
= aBinaryStream
.readuInt32();
285 sal_uInt32 actualHeaderSize
= sizeof(mInfo
.header
);
287 if (nHeaderSize
< actualHeaderSize
)
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();
299 aBinaryStream
.skip(nHeaderSize
- actualHeaderSize
);
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
));
307 if (mInfo
.verifier
.saltSize
!= 16)
310 // check flags and algorithm IDs, required are AES128 and SHA-1
311 if (!getFlag(mInfo
.header
.flags
, msfilter::ENCRYPTINFO_CRYPTOAPI
))
314 if (!getFlag(mInfo
.header
.flags
, msfilter::ENCRYPTINFO_AES
))
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
)
321 // hash algorithm ID 0 defaults to SHA-1 too
322 if (mInfo
.header
.algIdHash
!= 0 && mInfo
.header
.algIdHash
!= msfilter::ENCRYPT_HASH_SHA1
)
325 if (mInfo
.verifier
.encryptedVerifierHashSize
!= 20)
328 return !aBinaryStream
.isEof();
331 } // namespace oox::crypto
333 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */