/* -*- 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/AgileEngine.hxx" namespace oox { namespace core { using namespace std; namespace { const sal_uInt8 constBlock1[] = { 0xfe, 0xa7, 0xd2, 0x76, 0x3b, 0x4b, 0x9e, 0x79 }; const sal_uInt8 constBlock2[] = { 0xd7, 0xaa, 0x0f, 0x6d, 0x30, 0x61, 0x34, 0x4e }; const sal_uInt8 constBlock3[] = { 0x14, 0x6e, 0x0b, 0xe7, 0xab, 0xac, 0xd0, 0xd6 }; bool hashCalc( std::vector& output, std::vector& input, const OUString& algorithm ) { if (algorithm == "SHA1") return Digest::sha1(output, input); else if (algorithm == "SHA512") return Digest::sha512(output, input); return false; } } // namespace AgileEngine::AgileEngine() : CryptoEngine() {} AgileEngine::~AgileEngine() {} Crypto::CryptoType AgileEngine::cryptoType(const AgileEncryptionInfo& rInfo) { if (rInfo.keyBits == 128 && rInfo.cipherAlgorithm == "AES" && rInfo.cipherChaining == "ChainingModeCBC") return Crypto::AES_128_CBC; else if (rInfo.keyBits == 256 && rInfo.cipherAlgorithm == "AES" && rInfo.cipherChaining == "ChainingModeCBC") return Crypto::AES_256_CBC; return Crypto::UNKNOWN; } bool AgileEngine::calculateBlock( const sal_uInt8* rBlock, sal_uInt32 aBlockSize, vector& rHashFinal, vector& rInput, vector& rOutput) { vector hash(mInfo.hashSize, 0); vector salt = mInfo.saltValue; vector dataFinal(mInfo.hashSize + aBlockSize, 0); std::copy(rHashFinal.begin(), rHashFinal.end(), dataFinal.begin()); std::copy( rBlock, rBlock + aBlockSize, dataFinal.begin() + mInfo.hashSize); hashCalc(hash, dataFinal, mInfo.hashAlgorithm); sal_Int32 keySize = mInfo.keyBits / 8; vector key(keySize, 0); std::copy(hash.begin(), hash.begin() + keySize, key.begin()); Decrypt aDecryptor(key, salt, cryptoType(mInfo)); aDecryptor.update(rOutput, rInput); return true; } bool AgileEngine::calculateHashFinal(const OUString& rPassword, vector& aHashFinal) { sal_Int32 saltSize = mInfo.saltSize; vector salt = mInfo.saltValue; sal_uInt32 passwordByteLength = rPassword.getLength() * 2; vector initialData(saltSize + passwordByteLength); std::copy(salt.begin(), salt.end(), initialData.begin()); const sal_uInt8* passwordByteArray = reinterpret_cast(rPassword.getStr()); std::copy( passwordByteArray, passwordByteArray + passwordByteLength, initialData.begin() + saltSize); vector hash(mInfo.hashSize, 0); hashCalc(hash, initialData, mInfo.hashAlgorithm); vector data(mInfo.hashSize + 4, 0); for (sal_Int32 i = 0; i < mInfo.spinCount; i++) { ByteOrderConverter::writeLittleEndian( &data[0], i ); std::copy(hash.begin(), hash.end(), data.begin() + 4); hashCalc(hash, data, mInfo.hashAlgorithm); } std::copy(hash.begin(), hash.end(), aHashFinal.begin()); return true; } bool AgileEngine::generateEncryptionKey(const OUString& rPassword) { mKey.clear(); mKey.resize(mInfo.keyBits / 8, 0); vector hashFinal(mInfo.hashSize, 0); calculateHashFinal(rPassword, hashFinal); vector encryptedHashInput = mInfo.encryptedVerifierHashInput; vector hashInput(mInfo.saltSize, 0); calculateBlock(constBlock1, sizeof(constBlock1), hashFinal, encryptedHashInput, hashInput); vector encryptedHashValue = mInfo.encryptedVerifierHashValue; vector hashValue(encryptedHashValue.size(), 0); calculateBlock(constBlock2, sizeof(constBlock2), hashFinal, encryptedHashValue, hashValue); vector hash(mInfo.hashSize, 0); hashCalc(hash, hashInput, mInfo.hashAlgorithm); if (std::equal (hash.begin(), hash.end(), hashValue.begin()) ) { vector encryptedKeyValue = mInfo.encryptedKeyValue; calculateBlock(constBlock3, sizeof(constBlock3), hashFinal, encryptedKeyValue, mKey); return true; } return false; } bool AgileEngine::decrypt( BinaryXInputStream& aInputStream, BinaryXOutputStream& aOutputStream) { sal_uInt32 totalSize; aInputStream >> totalSize; // Document unencrypted size - 4 bytes aInputStream.skip( 4 ); // Reserved 4 Bytes vector keyDataSalt = mInfo.keyDataSalt; sal_uInt32 saltSize = mInfo.saltSize; sal_uInt32 keySize = mInfo.keyBits / 8; sal_uInt32 segment = 0; vector saltWithBlockKey(saltSize + sizeof(segment), 0); std::copy(keyDataSalt.begin(), keyDataSalt.end(), saltWithBlockKey.begin()); vector hash(mInfo.hashSize, 0); vector iv(keySize, 0); vector inputBuffer (SEGMENT_LENGTH); vector outputBuffer(SEGMENT_LENGTH); sal_uInt32 inputLength; sal_uInt32 outputLength; sal_uInt32 remaining = totalSize; while( (inputLength = aInputStream.readMemory( &inputBuffer[0], SEGMENT_LENGTH )) > 0 ) { sal_uInt8* segmentBegin = reinterpret_cast(&segment); sal_uInt8* segmentEnd = segmentBegin + sizeof(segment); std::copy(segmentBegin, segmentEnd, saltWithBlockKey.begin() + saltSize); hashCalc(hash, saltWithBlockKey, mInfo.hashAlgorithm); // Only if hash > keySize std::copy(hash.begin(), hash.begin() + keySize, iv.begin()); Decrypt aDecryptor(mKey, iv, AgileEngine::cryptoType(mInfo)); outputLength = aDecryptor.update(outputBuffer, inputBuffer, inputLength); sal_uInt32 writeLength = outputLength > remaining ? remaining : outputLength; aOutputStream.writeMemory( &outputBuffer[0], writeLength ); remaining -= outputLength; segment++; } return true; } bool AgileEngine::writeEncryptionInfo( const OUString& /*aPassword*/, BinaryXOutputStream& /*rStream*/) { return false; // Agile encrypting is not supported for now } bool AgileEngine::encrypt( BinaryXInputStream& /*aInputStream*/, BinaryXOutputStream& /*aOutputStream*/) { return false; // Agile encrypting is not supported for now } } // namespace core } // namespace oox /* vim:set shiftwidth=4 softtabstop=4 expandtab: */