/* * Copyright (C) Ascensio System SIA, 2009-2026 * * This program is a free software product. You can redistribute it and/or * modify it under the terms of the GNU Affero General Public License (AGPL) * version 3 as published by the Free Software Foundation, together with the * additional terms provided in the LICENSE file. * * This program is distributed WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For * details, see the GNU AGPL at: https://www.gnu.org/licenses/agpl-3.0.html * * You can contact Ascensio System SIA by email at info@onlyoffice.com * or by postal mail at 20A-6 Ernesta Birznieka-Upisha Street, Riga, * LV-1050, Latvia, European Union. * * The interactive user interfaces in modified versions of the Program * are required to display Appropriate Legal Notices in accordance with * Section 5 of the GNU AGPL version 3. * * No trademark rights are granted under this License. * * All non-code elements of the Product, including illustrations, * icon sets, and technical writing content, are licensed under the * Creative Commons Attribution-ShareAlike 4.0 International License: * https://creativecommons.org/licenses/by-sa/4.0/legalcode * * This license applies only to such non-code elements and does not * modify or replace the licensing terms applicable to the Program's * source code, which remains licensed under the GNU Affero General * Public License v3. * * SPDX-License-Identifier: AGPL-3.0-only */ #include "XORCrypt.h" #include typedef unsigned char SVBT16[2]; inline void ShortToSVBT16( unsigned short n, SVBT16 p ) { p[0] = (unsigned char) n; p[1] = (unsigned char)(n >> 8); } template< typename Type > inline void lclRotateLeft( Type& rnValue, int nBits ) { //OSL_ASSERT( // nBits >= 0 && // sal::static_int_cast< unsigned int >(nBits) < sizeof( Type ) * 8 ); rnValue = static_cast< Type >( (rnValue << nBits) | (rnValue >> (sizeof( Type ) * 8 - nBits)) ); } template< typename Type > inline void lclRotateLeft( Type& rnValue, unsigned char nBits, unsigned char nWidth ) { //OSL_ASSERT( (nBits < nWidth) && (nWidth < sizeof( Type ) * 8) ); Type nMask = static_cast< Type >( (1UL << nWidth) - 1 ); rnValue = static_cast< Type >( ((rnValue << nBits) | ((rnValue & nMask) >> (nWidth - nBits))) & nMask ); } namespace CRYPT { size_t lclGetLen( const unsigned char* pnPassData, size_t nBufferSize ) { size_t nLen = 0; while( (nLen < nBufferSize) && pnPassData[ nLen ] ) ++nLen; return nLen; } unsigned short lclGetKey( const unsigned char* pnPassData, size_t nBufferSize ) { size_t nLen = lclGetLen( pnPassData, nBufferSize ); if( !nLen ) return 0; unsigned short nKey = 0; unsigned short nKeyBase = 0x8000; unsigned short nKeyEnd = 0xFFFF; const unsigned char* pnChar = pnPassData + nLen - 1; for( size_t nIndex = 0; nIndex < nLen; ++nIndex, --pnChar ) { unsigned char cChar = *pnChar & 0x7F; for( unsigned char nBit = 0; nBit < 8; ++nBit ) { lclRotateLeft( nKeyBase, 1 ); if( nKeyBase & 1 ) nKeyBase ^= 0x1020; if( cChar & 1 ) nKey ^= nKeyBase; cChar >>= 1; lclRotateLeft( nKeyEnd, 1 ); if( nKeyEnd & 1 ) nKeyEnd ^= 0x1020; } } return nKey ^ nKeyEnd; } unsigned short lclGetHash( const unsigned char* pnPassData, size_t nBufferSize ) { size_t nLen = lclGetLen( pnPassData, nBufferSize ); unsigned short nHash = static_cast< unsigned short >( nLen ); if( nLen ) nHash ^= 0xCE4B; const unsigned char* pnChar = pnPassData; for( size_t nIndex = 0; nIndex < nLen; ++nIndex, ++pnChar ) { unsigned short cChar = *pnChar; unsigned char nRot = static_cast< unsigned char >( (nIndex + 1) % 15 ); lclRotateLeft( cChar, nRot, 15 ); nHash ^= cChar; } return nHash; } XORCrypt::XORCrypt(int type, unsigned short key, unsigned short hash, std::wstring password) : m_nOffset(0), m_nKey(0), m_nHash(0) { m_VerifyPassword = false; if (password.empty()) return; if (type == 1) m_nRotateDistance = 7; //doc else if (type == 2) m_nRotateDistance = 2; //xls memset( m_pnKey, 0, sizeof( m_pnKey ) ); size_t nLen = password.length(); if (nLen > 16) return; m_sPassword = std::string(password.begin(), password.end()); unsigned char* pnPassData = (unsigned char*)m_sPassword.c_str(); m_nKey = lclGetKey( pnPassData, 16 ); m_nHash = lclGetHash( pnPassData, 16 ); memcpy( m_pnKey, pnPassData, 16 ); static const unsigned char spnFillChars[] = { 0xBB, 0xFF, 0xFF, 0xBA, 0xFF, 0xFF, 0xB9, 0x80, 0x00, 0xBE, 0x0F, 0x00, 0xBF, 0x0F, 0x00 }; nLen = lclGetLen( pnPassData, 16 ); const unsigned char* pnFillChar = spnFillChars; for ( size_t nIndex = nLen; nIndex < sizeof( m_pnKey ); ++nIndex, ++pnFillChar ) m_pnKey[ nIndex ] = *pnFillChar; SVBT16 pnOrigKey; ShortToSVBT16( m_nKey, pnOrigKey ); unsigned char* pnKeyChar = m_pnKey; for ( size_t nIndex = 0; nIndex < sizeof( m_pnKey ); ++nIndex, ++pnKeyChar ) { *pnKeyChar ^= pnOrigKey[ nIndex & 0x01 ]; lclRotateLeft( *pnKeyChar, m_nRotateDistance ); } m_VerifyPassword = (key == m_nKey) && (hash == m_nHash); } bool XORCrypt::IsVerify() { return m_VerifyPassword; } void XORCrypt::Init(const unsigned long val) { m_nOffset = val & 0x0F; } void XORCrypt::Decrypt(char* data, const size_t size, const unsigned long block_index) { unsigned char* pnData = (unsigned char*)data; const unsigned char* pnCurrKey = m_pnKey + m_nOffset; const unsigned char* pnKeyLast = m_pnKey + 0x0F; if (m_nRotateDistance == 7) { for( const unsigned char* pnDataEnd = pnData + size; pnData < pnDataEnd; ++pnData ) { const unsigned char cChar = *pnData ^ *pnCurrKey; if (*pnData && cChar) *pnData = cChar; if( pnCurrKey < pnKeyLast ) ++pnCurrKey; else pnCurrKey = m_pnKey; } } if (m_nRotateDistance == 2) { for( const unsigned char* pnDataEnd = pnData + size; pnData < pnDataEnd; ++pnData ) { lclRotateLeft( *pnData, 3 ); *pnData ^= *pnCurrKey; if( pnCurrKey < pnKeyLast ) ++pnCurrKey; else pnCurrKey = m_pnKey; } } Skip(size); } void XORCrypt::Decrypt(char* data, const size_t size, const unsigned long stream_pos, const size_t block_size) { Init(stream_pos + size); unsigned char* pnData = (unsigned char*)data; const unsigned char* pnCurrKey = m_pnKey + m_nOffset; const unsigned char* pnKeyLast = m_pnKey + 0x0F; if (m_nRotateDistance == 7) { for( const unsigned char* pnDataEnd = pnData + size; pnData < pnDataEnd; ++pnData ) { const unsigned char cChar = *pnData ^ *pnCurrKey; if (*pnData && cChar) *pnData = cChar; if( pnCurrKey < pnKeyLast ) ++pnCurrKey; else pnCurrKey = m_pnKey; } } if (m_nRotateDistance == 2) { for( const unsigned char* pnDataEnd = pnData + size; pnData < pnDataEnd; ++pnData ) { lclRotateLeft( *pnData, 3 ); *pnData ^= *pnCurrKey; if( pnCurrKey < pnKeyLast ) ++pnCurrKey; else pnCurrKey = m_pnKey; } } } void XORCrypt::Skip( size_t size ) { m_nOffset = (m_nOffset + size) & 0x0F; } };