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/digest.h>
17 #include <rtl/random.h>
19 #include <comphelper/hash.hxx>
24 /* =========================================================================== */
25 /* Kudos to Caolan McNamara who provided the core decryption implementations. */
26 /* =========================================================================== */
30 void lclRandomGenerateValues(sal_uInt8
* aArray
, sal_uInt32 aSize
)
32 rtlRandomPool aRandomPool
= rtl_random_createPool();
33 rtl_random_getBytes(aRandomPool
, aArray
, aSize
);
34 rtl_random_destroyPool(aRandomPool
);
37 static const OUString lclCspName
= "Microsoft Enhanced RSA and AES Cryptographic Provider";
38 constexpr const sal_uInt32 AES128Size
= 16;
40 } // end anonymous namespace
42 bool Standard2007Engine::generateVerifier()
44 // only support key of size 128 bit (16 byte)
45 if (mKey
.size() != 16)
48 std::vector
<sal_uInt8
> verifier(msfilter::ENCRYPTED_VERIFIER_LENGTH
);
49 std::vector
<sal_uInt8
> encryptedVerifier(msfilter::ENCRYPTED_VERIFIER_LENGTH
);
51 lclRandomGenerateValues(verifier
.data(), verifier
.size());
53 std::vector
<sal_uInt8
> iv
;
54 Encrypt
aEncryptorVerifier(mKey
, iv
, Crypto::AES_128_ECB
);
55 if (aEncryptorVerifier
.update(encryptedVerifier
, verifier
) != msfilter::ENCRYPTED_VERIFIER_LENGTH
)
57 std::copy(encryptedVerifier
.begin(), encryptedVerifier
.end(), mInfo
.verifier
.encryptedVerifier
);
59 mInfo
.verifier
.encryptedVerifierHashSize
= msfilter::SHA1_HASH_LENGTH
;
60 std::vector
<sal_uInt8
> hash
= comphelper::Hash::calculateHash(verifier
.data(), verifier
.size(), comphelper::HashType::SHA1
);
61 hash
.resize(msfilter::SHA256_HASH_LENGTH
, 0);
63 std::vector
<sal_uInt8
> encryptedHash(msfilter::SHA256_HASH_LENGTH
, 0);
65 Encrypt
aEncryptorHash(mKey
, iv
, Crypto::AES_128_ECB
);
66 aEncryptorHash
.update(encryptedHash
, hash
, hash
.size());
67 std::copy(encryptedHash
.begin(), encryptedHash
.end(), mInfo
.verifier
.encryptedVerifierHash
);
72 bool Standard2007Engine::calculateEncryptionKey(const OUString
& rPassword
)
74 sal_uInt32 saltSize
= mInfo
.verifier
.saltSize
;
75 sal_uInt32 passwordByteLength
= rPassword
.getLength() * 2;
76 const sal_uInt8
* saltArray
= mInfo
.verifier
.salt
;
78 // Prepare initial data -> salt + password (in 16-bit chars)
79 std::vector
<sal_uInt8
> initialData(saltSize
+ passwordByteLength
);
80 std::copy(saltArray
, saltArray
+ saltSize
, initialData
.begin());
82 const sal_uInt8
* passwordByteArray
= reinterpret_cast<const sal_uInt8
*>(rPassword
.getStr());
86 passwordByteArray
+ passwordByteLength
,
87 initialData
.begin() + saltSize
);
89 // use "hash" vector for result of sha1 hashing
90 // calculate SHA1 hash of initialData
91 std::vector
<sal_uInt8
> hash
= comphelper::Hash::calculateHash(initialData
.data(), initialData
.size(), comphelper::HashType::SHA1
);
93 // data = iterator (4bytes) + hash
94 std::vector
<sal_uInt8
> data(msfilter::SHA1_HASH_LENGTH
+ 4, 0);
96 for (sal_Int32 i
= 0; i
< 50000; ++i
)
98 ByteOrderConverter::writeLittleEndian(data
.data(), i
);
99 std::copy(hash
.begin(), hash
.end(), data
.begin() + 4);
100 hash
= comphelper::Hash::calculateHash(data
.data(), data
.size(), comphelper::HashType::SHA1
);
102 std::copy(hash
.begin(), hash
.end(), data
.begin() );
103 std::fill(data
.begin() + msfilter::SHA1_HASH_LENGTH
, data
.end(), 0 );
105 hash
= comphelper::Hash::calculateHash(data
.data(), data
.size(), comphelper::HashType::SHA1
);
108 std::vector
<sal_uInt8
> buffer(64, 0x36);
109 for (size_t i
= 0; i
< hash
.size(); ++i
)
110 buffer
[i
] ^= hash
[i
];
112 hash
= comphelper::Hash::calculateHash(buffer
.data(), buffer
.size(), comphelper::HashType::SHA1
);
113 if (mKey
.size() > hash
.size())
115 std::copy(hash
.begin(), hash
.begin() + mKey
.size(), mKey
.begin());
120 bool Standard2007Engine::generateEncryptionKey(const OUString
& password
)
124 KeySize (4 bytes): An unsigned integer that specifies the number of bits in the encryption key.
125 MUST be a multiple of 8. MUST be one of the values in the following table:
126 Algorithm Value Comment
127 Any 0x00000000 Determined by Flags
128 RC4 0x00000028 – 0x00000080 (inclusive) 8-bit increments.
129 AES 0x00000080, 0x000000C0, 0x00000100 128, 192 or 256-bit
131 if (mInfo
.header
.keyBits
> 8192) // should we strictly enforce the above 256 bit limit ?
133 mKey
.resize(mInfo
.header
.keyBits
/ 8, 0);
137 calculateEncryptionKey(password
);
139 std::vector
<sal_uInt8
> encryptedVerifier(msfilter::ENCRYPTED_VERIFIER_LENGTH
);
141 mInfo
.verifier
.encryptedVerifier
,
142 mInfo
.verifier
.encryptedVerifier
+ msfilter::ENCRYPTED_VERIFIER_LENGTH
,
143 encryptedVerifier
.begin());
145 std::vector
<sal_uInt8
> encryptedHash(msfilter::SHA256_HASH_LENGTH
);
147 mInfo
.verifier
.encryptedVerifierHash
,
148 mInfo
.verifier
.encryptedVerifierHash
+ msfilter::SHA256_HASH_LENGTH
,
149 encryptedHash
.begin());
151 std::vector
<sal_uInt8
> verifier(encryptedVerifier
.size(), 0);
152 Decrypt::aes128ecb(verifier
, encryptedVerifier
, mKey
);
154 std::vector
<sal_uInt8
> verifierHash(encryptedHash
.size(), 0);
155 Decrypt::aes128ecb(verifierHash
, encryptedHash
, mKey
);
157 std::vector
<sal_uInt8
> hash
= comphelper::Hash::calculateHash(verifier
.data(), verifier
.size(), comphelper::HashType::SHA1
);
159 return std::equal(hash
.begin(), hash
.end(), verifierHash
.begin());
162 bool Standard2007Engine::decrypt(BinaryXInputStream
& aInputStream
,
163 BinaryXOutputStream
& aOutputStream
)
165 sal_uInt32 totalSize
= aInputStream
.readuInt32(); // Document unencrypted size - 4 bytes
166 aInputStream
.skip(4); // Reserved 4 Bytes
168 std::vector
<sal_uInt8
> iv
;
169 Decrypt
aDecryptor(mKey
, iv
, Crypto::AES_128_ECB
);
170 std::vector
<sal_uInt8
> inputBuffer (4096);
171 std::vector
<sal_uInt8
> outputBuffer(4096);
172 sal_uInt32 inputLength
;
173 sal_uInt32 outputLength
;
174 sal_uInt32 remaining
= totalSize
;
176 while ((inputLength
= aInputStream
.readMemory(inputBuffer
.data(), inputBuffer
.size())) > 0)
178 outputLength
= aDecryptor
.update(outputBuffer
, inputBuffer
, inputLength
);
179 sal_uInt32 writeLength
= std::min(outputLength
, remaining
);
180 aOutputStream
.writeMemory(outputBuffer
.data(), writeLength
);
181 remaining
-= outputLength
;
186 bool Standard2007Engine::checkDataIntegrity()
191 bool Standard2007Engine::setupEncryption(OUString
const & password
)
193 mInfo
.header
.flags
= msfilter::ENCRYPTINFO_AES
| msfilter::ENCRYPTINFO_CRYPTOAPI
;
194 mInfo
.header
.algId
= msfilter::ENCRYPT_ALGO_AES128
;
195 mInfo
.header
.algIdHash
= msfilter::ENCRYPT_HASH_SHA1
;
196 mInfo
.header
.keyBits
= msfilter::ENCRYPT_KEY_SIZE_AES_128
;
197 mInfo
.header
.providedType
= msfilter::ENCRYPT_PROVIDER_TYPE_AES
;
199 lclRandomGenerateValues(mInfo
.verifier
.salt
, mInfo
.verifier
.saltSize
);
200 const sal_Int32 keyLength
= mInfo
.header
.keyBits
/ 8;
203 mKey
.resize(keyLength
, 0);
205 if (!calculateEncryptionKey(password
))
208 if (!generateVerifier())
214 void Standard2007Engine::writeEncryptionInfo(BinaryXOutputStream
& rStream
)
216 rStream
.WriteUInt32(msfilter::VERSION_INFO_2007_FORMAT
);
218 sal_uInt32 cspNameSize
= (lclCspName
.getLength() * 2) + 2;
220 sal_uInt32 encryptionHeaderSize
= static_cast<sal_uInt32
>(sizeof(msfilter::EncryptionStandardHeader
));
222 rStream
.WriteUInt32(mInfo
.header
.flags
);
223 sal_uInt32 headerSize
= encryptionHeaderSize
+ cspNameSize
;
224 rStream
.WriteUInt32(headerSize
);
226 rStream
.writeMemory(&mInfo
.header
, encryptionHeaderSize
);
227 rStream
.writeUnicodeArray(lclCspName
);
228 rStream
.WriteUInt16(0);
230 rStream
.writeMemory(&mInfo
.verifier
, sizeof(msfilter::EncryptionVerifierAES
));
233 void Standard2007Engine::encrypt(css::uno::Reference
<css::io::XInputStream
> & rxInputStream
,
234 css::uno::Reference
<css::io::XOutputStream
> & rxOutputStream
,
240 BinaryXOutputStream
aBinaryOutputStream(rxOutputStream
, false);
241 BinaryXInputStream
aBinaryInputStream(rxInputStream
, false);
243 aBinaryOutputStream
.WriteUInt32(nSize
); // size
244 aBinaryOutputStream
.WriteUInt32(0U); // reserved
246 std::vector
<sal_uInt8
> inputBuffer(1024);
247 std::vector
<sal_uInt8
> outputBuffer(1024);
249 sal_uInt32 inputLength
;
250 sal_uInt32 outputLength
;
252 std::vector
<sal_uInt8
> iv
;
253 Encrypt
aEncryptor(mKey
, iv
, Crypto::AES_128_ECB
);
255 while ((inputLength
= aBinaryInputStream
.readMemory(inputBuffer
.data(), inputBuffer
.size())) > 0)
257 // increase size to multiple of 16 (size of mKey) if necessary
258 inputLength
= inputLength
% AES128Size
== 0 ?
259 inputLength
: roundUp(inputLength
, AES128Size
);
260 outputLength
= aEncryptor
.update(outputBuffer
, inputBuffer
, inputLength
);
261 aBinaryOutputStream
.writeMemory(outputBuffer
.data(), outputLength
);
265 bool Standard2007Engine::readEncryptionInfo(css::uno::Reference
<css::io::XInputStream
> & rxInputStream
)
267 BinaryXInputStream
aBinaryStream(rxInputStream
, false);
269 mInfo
.header
.flags
= aBinaryStream
.readuInt32();
270 if (getFlag(mInfo
.header
.flags
, msfilter::ENCRYPTINFO_EXTERNAL
))
273 sal_uInt32 nHeaderSize
= aBinaryStream
.readuInt32();
275 sal_uInt32 actualHeaderSize
= sizeof(mInfo
.header
);
277 if (nHeaderSize
< actualHeaderSize
)
280 mInfo
.header
.flags
= aBinaryStream
.readuInt32();
281 mInfo
.header
.sizeExtra
= aBinaryStream
.readuInt32();
282 mInfo
.header
.algId
= aBinaryStream
.readuInt32();
283 mInfo
.header
.algIdHash
= aBinaryStream
.readuInt32();
284 mInfo
.header
.keyBits
= aBinaryStream
.readuInt32();
285 mInfo
.header
.providedType
= aBinaryStream
.readuInt32();
286 mInfo
.header
.reserved1
= aBinaryStream
.readuInt32();
287 mInfo
.header
.reserved2
= aBinaryStream
.readuInt32();
289 aBinaryStream
.skip(nHeaderSize
- actualHeaderSize
);
291 mInfo
.verifier
.saltSize
= aBinaryStream
.readuInt32();
292 aBinaryStream
.readArray(mInfo
.verifier
.salt
, SAL_N_ELEMENTS(mInfo
.verifier
.salt
));
293 aBinaryStream
.readArray(mInfo
.verifier
.encryptedVerifier
, SAL_N_ELEMENTS(mInfo
.verifier
.encryptedVerifier
));
294 mInfo
.verifier
.encryptedVerifierHashSize
= aBinaryStream
.readuInt32();
295 aBinaryStream
.readArray(mInfo
.verifier
.encryptedVerifierHash
, SAL_N_ELEMENTS(mInfo
.verifier
.encryptedVerifierHash
));
297 if (mInfo
.verifier
.saltSize
!= 16)
300 // check flags and algorithm IDs, required are AES128 and SHA-1
301 if (!getFlag(mInfo
.header
.flags
, msfilter::ENCRYPTINFO_CRYPTOAPI
))
304 if (!getFlag(mInfo
.header
.flags
, msfilter::ENCRYPTINFO_AES
))
307 // algorithm ID 0 defaults to AES128 too, if ENCRYPTINFO_AES flag is set
308 if (mInfo
.header
.algId
!= 0 && mInfo
.header
.algId
!= msfilter::ENCRYPT_ALGO_AES128
)
311 // hash algorithm ID 0 defaults to SHA-1 too
312 if (mInfo
.header
.algIdHash
!= 0 && mInfo
.header
.algIdHash
!= msfilter::ENCRYPT_HASH_SHA1
)
315 if (mInfo
.verifier
.encryptedVerifierHashSize
!= 20)
318 return !aBinaryStream
.isEof();
324 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */