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/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 /* =========================================================================== */
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)
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
)
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
);
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
];
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
);
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())
114 std::copy(hash
.begin(), hash
.begin() + mKey
.size(), mKey
.begin());
119 bool Standard2007Engine::generateEncryptionKey(const OUString
& password
)
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 ?
132 mKey
.resize(mInfo
.header
.keyBits
/ 8, 0);
136 calculateEncryptionKey(password
);
138 std::vector
<sal_uInt8
> encryptedVerifier(msfilter::ENCRYPTED_VERIFIER_LENGTH
);
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
);
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
;
185 bool Standard2007Engine::checkDataIntegrity()
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;
202 mKey
.resize(keyLength
, 0);
204 if (!calculateEncryptionKey(password
))
207 if (!generateVerifier())
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
);
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
,
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
))
284 sal_uInt32 nHeaderSize
= aBinaryStream
.readuInt32();
286 sal_uInt32 actualHeaderSize
= sizeof(mInfo
.header
);
288 if (nHeaderSize
< actualHeaderSize
)
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)
311 // check flags and algorithm IDs, required are AES128 and SHA-1
312 if (!getFlag(mInfo
.header
.flags
, msfilter::ENCRYPTINFO_CRYPTOAPI
))
315 if (!getFlag(mInfo
.header
.flags
, msfilter::ENCRYPTINFO_AES
))
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
)
322 // hash algorithm ID 0 defaults to SHA-1 too
323 if (mInfo
.header
.algIdHash
!= 0 && mInfo
.header
.algIdHash
!= msfilter::ENCRYPT_HASH_SHA1
)
326 if (mInfo
.verifier
.encryptedVerifierHashSize
!= 20)
329 return !aBinaryStream
.isEof();
332 } // namespace oox::crypto
334 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */