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 
20 namespace oox::crypto {
21 
22 /* =========================================================================== */
23 /* Kudos to Caolan McNamara who provided the core decryption implementations. */
24 /* =========================================================================== */
25 namespace
26 {
27 
28 void 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 
35 constexpr OUStringLiteral lclCspName = u"Microsoft Enhanced RSA and AES Cryptographic Provider";
36 constexpr 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 
70 bool Standard2007Engine::calculateEncryptionKey(const OUString& rPassword)
71 {
72  sal_uInt32 saltSize = mInfo.verifier.saltSize;
73  sal_uInt32 passwordByteLength = rPassword.getLength() * 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 (sal_Int32 i = 0; i != rPassword.getLength(); ++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 
118 bool 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 
189 bool 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);
228  rStream.WriteUInt32(mInfo.header.keyBits);
232  rStream.writeUnicodeArray(lclCspName);
233  rStream.WriteUInt16(0);
234 
236  rStream.writeMemory(&mInfo.verifier.salt, sizeof mInfo.verifier.salt);
239  rStream.writeMemory(
241 }
242 
243 void 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 
275 bool 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: */
const sal_uInt32 ENCRYPTINFO_CRYPTOAPI
Wraps a UNO input stream and provides convenient access functions.
sal_uInt32 update(std::vector< sal_uInt8 > &output, std::vector< sal_uInt8 > &input, sal_uInt32 inputLength=0)
Definition: CryptTools.cxx:421
const sal_uInt32 ENCRYPTINFO_AES
virtual void writeEncryptionInfo(BinaryXOutputStream &rStream) override
virtual void writeMemory(const void *pMem, sal_Int32 nBytes, size_t nAtomSize=1) override
Write nBytes bytes from the (preallocated!) buffer pMem.
const sal_uInt32 VERSION_INFO_2007_FORMAT
sal_uInt32 update(std::vector< sal_uInt8 > &output, std::vector< sal_uInt8 > &input, sal_uInt32 inputLength=0)
Definition: CryptTools.cxx:369
sal_uInt8 encryptedVerifier[ENCRYPTED_VERIFIER_LENGTH]
void encrypt(const css::uno::Reference< css::io::XInputStream > &rxInputStream, css::uno::Reference< css::io::XOutputStream > &rxOutputStream, sal_uInt32 nSize) override
Wraps a UNO output stream and provides convenient access functions.
BinaryOutputStream & WriteUInt32(sal_uInt32 x)
msfilter::StandardEncryptionInfo mInfo
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:135
#define SAL_N_ELEMENTS(arr)
bool calculateEncryptionKey(const OUString &rPassword)
const sal_uInt32 ENCRYPTINFO_EXTERNAL
sal_uInt8 encryptedVerifierHash[comphelper::SHA256_HASH_LENGTH]
const sal_uInt32 ENCRYPT_HASH_SHA1
const sal_uInt32 ENCRYPTED_VERIFIER_LENGTH
T roundUp(T input, T multiple)
Rounds up the input to the nearest multiple.
Definition: CryptTools.hxx:40
float u
virtual bool setupEncryption(OUString const &rPassword) override
const sal_uInt32 SHA1_HASH_LENGTH
EncryptionStandardHeader header
static void writeLittleEndian(void *pDstBuffer, Type nValue)
Writes a value to memory, while converting it to little-endian.
Definition: helper.hxx:262
exports com.sun.star.chart2. data
sal_uInt8 salt[SALT_LENGTH]
BinaryOutputStream & WriteUInt16(sal_uInt16 x)
const sal_uInt32 SHA256_HASH_LENGTH
bool readEncryptionInfo(css::uno::Reference< css::io::XInputStream > &rxInputStream) override
void * rtlRandomPool
virtual bool decrypt(BinaryXInputStream &aInputStream, BinaryXOutputStream &aOutputStream) override
EncryptionVerifierAES verifier
unsigned char sal_uInt8
const sal_uInt32 ENCRYPT_KEY_SIZE_AES_128
sal_Int32 readArray(Type *opnArray, sal_Int32 nElemCount)
Reads a (preallocated!) C array of values from the stream.
void * p
static std::vector< unsigned char > calculateHash(const unsigned char *pInput, size_t length, HashType eType)
const sal_uInt32 ENCRYPT_PROVIDER_TYPE_AES
std::vector< sal_uInt8 > mKey
bool isEof() const
Returns true, if the stream position is invalid (EOF).
static sal_uInt32 aes128ecb(std::vector< sal_uInt8 > &output, std::vector< sal_uInt8 > &input, std::vector< sal_uInt8 > &key)
Definition: CryptTools.cxx:394
const sal_uInt32 ENCRYPT_ALGO_AES128
virtual bool generateEncryptionKey(OUString const &rPassword) override
virtual sal_Int32 readMemory(void *opMem, sal_Int32 nBytes, size_t nAtomSize=1) override
Reads nBytes bytes to the (existing) buffer opMem.
void writeUnicodeArray(const OUString &rString)
virtual void skip(sal_Int32 nBytes, size_t nAtomSize=1) override
Seeks the stream forward by the passed number of bytes.