2013-08-24 15:40:54 -05:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
|
|
/*
|
|
|
|
* This file is part of the LibreOffice project.
|
|
|
|
*
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "oox/crypto/Standard2007Engine.hxx"
|
|
|
|
|
|
|
|
#include <osl/time.h>
|
|
|
|
#include <rtl/random.h>
|
|
|
|
|
|
|
|
namespace oox {
|
|
|
|
namespace core {
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
/* =========================================================================== */
|
|
|
|
/* Kudos to Caolan McNamara who provided the core decryption implementations. */
|
|
|
|
/* =========================================================================== */
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
|
|
|
|
void lclRandomGenerateValues(sal_uInt8* aArray, sal_uInt32 aSize)
|
|
|
|
{
|
|
|
|
TimeValue aTime;
|
|
|
|
osl_getSystemTime( &aTime );
|
|
|
|
rtlRandomPool aRandomPool = rtl_random_createPool ();
|
|
|
|
rtl_random_addBytes ( aRandomPool, &aTime, 8 );
|
|
|
|
rtl_random_getBytes ( aRandomPool, aArray, aSize );
|
|
|
|
rtl_random_destroyPool ( aRandomPool );
|
|
|
|
}
|
|
|
|
|
|
|
|
static const OUString lclCspName = "Microsoft Enhanced RSA and AES Cryptographic Provider";
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
EncryptionStandardHeader::EncryptionStandardHeader()
|
|
|
|
{
|
|
|
|
flags = 0;
|
|
|
|
sizeExtra = 0;
|
|
|
|
algId = 0;
|
|
|
|
algIdHash = 0;
|
|
|
|
keyBits = 0;
|
|
|
|
providedType = 0;
|
|
|
|
reserved1 = 0;
|
|
|
|
reserved2 = 0;
|
|
|
|
}
|
|
|
|
|
2014-03-23 12:27:11 -05:00
|
|
|
EncryptionVerifierAES::EncryptionVerifierAES() :
|
|
|
|
saltSize(SALT_LENGTH),
|
|
|
|
encryptedVerifierHashSize(Digest::DIGEST_LENGTH_SHA1)
|
2013-08-24 15:40:54 -05:00
|
|
|
{
|
|
|
|
memset(salt, 0, sizeof(salt));
|
|
|
|
memset(encryptedVerifier, 0, sizeof(encryptedVerifier));
|
|
|
|
memset(encryptedVerifierHash, 0, sizeof(encryptedVerifierHash));
|
|
|
|
}
|
|
|
|
|
|
|
|
Standard2007Engine::Standard2007Engine() :
|
|
|
|
CryptoEngine()
|
|
|
|
{}
|
|
|
|
|
|
|
|
Standard2007Engine::~Standard2007Engine()
|
|
|
|
{}
|
|
|
|
|
|
|
|
bool Standard2007Engine::generateVerifier()
|
|
|
|
{
|
|
|
|
// only support key of size 128 bit (16 byte)
|
|
|
|
if (mKey.size() != 16)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
sal_uInt32 outputLength;
|
|
|
|
vector<sal_uInt8> verifier(ENCRYPTED_VERIFIER_LENGTH);
|
|
|
|
vector<sal_uInt8> encryptedVerifier(ENCRYPTED_VERIFIER_LENGTH);
|
|
|
|
|
|
|
|
lclRandomGenerateValues(&verifier[0], verifier.size());
|
|
|
|
|
|
|
|
vector<sal_uInt8> iv;
|
|
|
|
Encrypt aEncryptorVerifier(mKey, iv, Crypto::AES_128_ECB);
|
|
|
|
outputLength = aEncryptorVerifier.update(encryptedVerifier, verifier);
|
|
|
|
if (outputLength != ENCRYPTED_VERIFIER_LENGTH)
|
|
|
|
return false;
|
|
|
|
std::copy(encryptedVerifier.begin(), encryptedVerifier.end(), mInfo.verifier.encryptedVerifier);
|
|
|
|
|
|
|
|
vector<sal_uInt8> hash(RTL_DIGEST_LENGTH_SHA1, 0);
|
|
|
|
mInfo.verifier.encryptedVerifierHashSize = RTL_DIGEST_LENGTH_SHA1;
|
2014-03-23 12:27:11 -05:00
|
|
|
Digest::sha1(hash, verifier);
|
2013-08-24 15:40:54 -05:00
|
|
|
hash.resize(ENCRYPTED_VERIFIER_HASH_LENGTH, 0);
|
|
|
|
|
|
|
|
vector<sal_uInt8> encryptedHash(ENCRYPTED_VERIFIER_HASH_LENGTH, 0);
|
|
|
|
|
|
|
|
Encrypt aEncryptorHash(mKey, iv, Crypto::AES_128_ECB);
|
|
|
|
outputLength = aEncryptorHash.update(encryptedHash, hash, hash.size());
|
|
|
|
std::copy(encryptedHash.begin(), encryptedHash.end(), mInfo.verifier.encryptedVerifierHash);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Standard2007Engine::calculateEncryptionKey(const OUString& rPassword)
|
|
|
|
{
|
|
|
|
sal_uInt32 saltSize = mInfo.verifier.saltSize;
|
|
|
|
sal_uInt32 passwordByteLength = rPassword.getLength() * 2;
|
|
|
|
const sal_uInt8* saltArray = mInfo.verifier.salt;
|
|
|
|
|
|
|
|
// Prepare initial data -> salt + password (in 16-bit chars)
|
|
|
|
vector<sal_uInt8> initialData(saltSize + passwordByteLength);
|
|
|
|
std::copy(saltArray, saltArray + saltSize, initialData.begin());
|
|
|
|
|
|
|
|
const sal_uInt8* passwordByteArray = reinterpret_cast<const sal_uInt8*>(rPassword.getStr());
|
|
|
|
|
|
|
|
std::copy(
|
|
|
|
passwordByteArray,
|
|
|
|
passwordByteArray + passwordByteLength,
|
|
|
|
initialData.begin() + saltSize);
|
|
|
|
|
|
|
|
// use "hash" vector for result of sha1 hashing
|
2014-03-23 12:27:11 -05:00
|
|
|
vector<sal_uInt8> hash(Digest::DIGEST_LENGTH_SHA1, 0);
|
2013-08-24 15:40:54 -05:00
|
|
|
|
|
|
|
// calculate SHA1 hash of initialData
|
2014-03-23 12:27:11 -05:00
|
|
|
Digest::sha1(hash, initialData);
|
2013-08-24 15:40:54 -05:00
|
|
|
|
|
|
|
// data = iterator (4bytes) + hash
|
2014-03-23 12:27:11 -05:00
|
|
|
vector<sal_uInt8> data(Digest::DIGEST_LENGTH_SHA1 + 4, 0);
|
2013-08-24 15:40:54 -05:00
|
|
|
|
2014-01-01 06:05:33 -06:00
|
|
|
for (sal_Int32 i = 0; i < 50000; ++i)
|
2013-08-24 15:40:54 -05:00
|
|
|
{
|
|
|
|
ByteOrderConverter::writeLittleEndian( &data[0], i );
|
|
|
|
std::copy(hash.begin(), hash.end(), data.begin() + 4);
|
2014-03-23 12:27:11 -05:00
|
|
|
Digest::sha1(hash, data);
|
2013-08-24 15:40:54 -05:00
|
|
|
}
|
|
|
|
std::copy(hash.begin(), hash.end(), data.begin() );
|
2014-03-23 12:27:11 -05:00
|
|
|
std::fill(data.begin() + Digest::DIGEST_LENGTH_SHA1, data.end(), 0 );
|
2013-08-24 15:40:54 -05:00
|
|
|
|
2014-03-23 12:27:11 -05:00
|
|
|
Digest::sha1(hash, data);
|
2013-08-24 15:40:54 -05:00
|
|
|
|
|
|
|
// derive key
|
|
|
|
vector<sal_uInt8> buffer(64, 0x36);
|
|
|
|
for( sal_uInt32 i = 0; i < hash.size(); ++i )
|
|
|
|
buffer[i] ^= hash[i];
|
|
|
|
|
2014-03-23 12:27:11 -05:00
|
|
|
Digest::sha1(hash, buffer);
|
2013-08-24 15:40:54 -05:00
|
|
|
std::copy(hash.begin(), hash.begin() + mKey.size(), mKey.begin());
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Standard2007Engine::generateEncryptionKey(const OUString& password)
|
|
|
|
{
|
|
|
|
mKey.clear();
|
|
|
|
mKey.resize(mInfo.header.keyBits / 8, 0);
|
|
|
|
|
|
|
|
calculateEncryptionKey(password);
|
|
|
|
|
|
|
|
vector<sal_uInt8> encryptedVerifier(ENCRYPTED_VERIFIER_LENGTH);
|
|
|
|
std::copy(
|
|
|
|
mInfo.verifier.encryptedVerifier,
|
|
|
|
mInfo.verifier.encryptedVerifier + ENCRYPTED_VERIFIER_LENGTH,
|
|
|
|
encryptedVerifier.begin());
|
|
|
|
|
2013-09-14 06:52:58 -05:00
|
|
|
vector<sal_uInt8> encryptedHash(ENCRYPTED_VERIFIER_HASH_LENGTH);
|
2013-08-24 15:40:54 -05:00
|
|
|
std::copy(
|
|
|
|
mInfo.verifier.encryptedVerifierHash,
|
|
|
|
mInfo.verifier.encryptedVerifierHash + ENCRYPTED_VERIFIER_HASH_LENGTH,
|
2013-09-14 06:52:58 -05:00
|
|
|
encryptedHash.begin());
|
2013-08-24 15:40:54 -05:00
|
|
|
|
2013-09-14 06:52:58 -05:00
|
|
|
vector<sal_uInt8> verifier(encryptedVerifier.size(), 0);
|
|
|
|
Decrypt::aes128ecb(verifier, encryptedVerifier, mKey);
|
|
|
|
|
|
|
|
vector<sal_uInt8> verifierHash(encryptedHash.size(), 0);
|
|
|
|
Decrypt::aes128ecb(verifierHash, encryptedHash, mKey);
|
|
|
|
|
|
|
|
vector<sal_uInt8> hash(RTL_DIGEST_LENGTH_SHA1, 0);
|
2014-03-23 12:27:11 -05:00
|
|
|
Digest::sha1(hash, verifier);
|
2013-09-14 06:52:58 -05:00
|
|
|
|
|
|
|
return std::equal( hash.begin(), hash.end(), verifierHash.begin() );
|
2013-08-24 15:40:54 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Standard2007Engine::decrypt(
|
|
|
|
BinaryXInputStream& aInputStream,
|
|
|
|
BinaryXOutputStream& aOutputStream)
|
|
|
|
{
|
|
|
|
sal_uInt32 totalSize;
|
|
|
|
aInputStream >> totalSize; // Document unencrypted size - 4 bytes
|
|
|
|
aInputStream.skip( 4 ); // Reserved 4 Bytes
|
|
|
|
|
|
|
|
vector<sal_uInt8> iv;
|
|
|
|
Decrypt aDecryptor(mKey, iv, Crypto::AES_128_ECB);
|
|
|
|
vector<sal_uInt8> inputBuffer (4096);
|
|
|
|
vector<sal_uInt8> outputBuffer(4096);
|
|
|
|
sal_uInt32 inputLength;
|
|
|
|
sal_uInt32 outputLength;
|
|
|
|
|
|
|
|
while( (inputLength = aInputStream.readMemory( &inputBuffer[0], inputBuffer.size() )) > 0 )
|
|
|
|
{
|
|
|
|
outputLength = aDecryptor.update(outputBuffer, inputBuffer, inputLength);
|
|
|
|
aOutputStream.writeMemory( &outputBuffer[0], outputLength );
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Standard2007Engine::writeEncryptionInfo(const OUString& password, BinaryXOutputStream& rStream)
|
|
|
|
{
|
|
|
|
mInfo.header.flags = ENCRYPTINFO_AES | ENCRYPTINFO_CRYPTOAPI;
|
|
|
|
mInfo.header.algId = ENCRYPT_ALGO_AES128;
|
|
|
|
mInfo.header.algIdHash = ENCRYPT_HASH_SHA1;
|
|
|
|
mInfo.header.keyBits = ENCRYPT_KEY_SIZE_AES_128;
|
|
|
|
mInfo.header.providedType = ENCRYPT_PROVIDER_TYPE_AES;
|
|
|
|
|
|
|
|
lclRandomGenerateValues(mInfo.verifier.salt, mInfo.verifier.saltSize);
|
|
|
|
const sal_Int32 keyLength = mInfo.header.keyBits / 8;
|
|
|
|
|
|
|
|
mKey.clear();
|
|
|
|
mKey.resize(keyLength, 0);
|
|
|
|
|
|
|
|
if (!calculateEncryptionKey(password))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!generateVerifier())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
rStream.writeValue(VERSION_INFO_2007_FORMAT);
|
|
|
|
|
|
|
|
sal_uInt32 cspNameSize = (lclCspName.getLength() * 2) + 2;
|
|
|
|
|
|
|
|
sal_uInt32 encryptionHeaderSize = static_cast<sal_uInt32>(sizeof(EncryptionStandardHeader));
|
|
|
|
|
|
|
|
rStream << mInfo.header.flags;
|
|
|
|
sal_uInt32 headerSize = encryptionHeaderSize + cspNameSize;
|
|
|
|
rStream << headerSize;
|
|
|
|
|
|
|
|
rStream.writeMemory(&mInfo.header, encryptionHeaderSize);
|
|
|
|
rStream.writeUnicodeArray(lclCspName);
|
|
|
|
rStream.writeValue<sal_uInt16>(0);
|
|
|
|
|
|
|
|
sal_uInt32 encryptionVerifierSize = static_cast<sal_uInt32>(sizeof(EncryptionVerifierAES));
|
|
|
|
rStream.writeMemory(&mInfo.verifier, encryptionVerifierSize);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Standard2007Engine::encrypt(
|
|
|
|
BinaryXInputStream& aInputStream,
|
|
|
|
BinaryXOutputStream& aOutputStream)
|
|
|
|
{
|
|
|
|
vector<sal_uInt8> inputBuffer(1024);
|
|
|
|
vector<sal_uInt8> outputBuffer(1024);
|
|
|
|
|
|
|
|
sal_uInt32 inputLength;
|
|
|
|
sal_uInt32 outputLength;
|
|
|
|
|
|
|
|
vector<sal_uInt8> iv;
|
|
|
|
Encrypt aEncryptor(mKey, iv, Crypto::AES_128_ECB);
|
|
|
|
|
|
|
|
while( (inputLength = aInputStream.readMemory( &inputBuffer[0], inputBuffer.size() )) > 0 )
|
|
|
|
{
|
|
|
|
inputLength = inputLength % 16 == 0 ? inputLength : ((inputLength / 16) * 16) + 16;
|
|
|
|
outputLength = aEncryptor.update(outputBuffer, inputBuffer, inputLength);
|
|
|
|
aOutputStream.writeMemory( &outputBuffer[0], outputLength );
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace core
|
|
|
|
} // namespace oox
|
|
|
|
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|