Files
Yajbir Singh f1b860b25c
Some checks failed
check / markdownlint (push) Has been cancelled
check / spellchecker (push) Has been cancelled
updated
2025-12-11 19:03:17 +05:30

523 lines
12 KiB
C++

/*
* (c) Copyright Ascensio System SIA 2010-2023
*
* 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. In accordance with
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
* that Ascensio System SIA expressly excludes the warranty of non-infringement
* of any third-party rights.
*
* 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: http://www.gnu.org/licenses/agpl-3.0.html
*
* You can contact Ascensio System SIA at 20A-6 Ernesta Birznieka-Upish
* street, Riga, Latvia, EU, LV-1050.
*
* The interactive user interfaces in modified source and object code versions
* of the Program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU AGPL version 3.
*
* Pursuant to Section 7(b) of the License you must retain the original Product
* logo when distributing the program. Pursuant to Section 7(e) we decline to
* grant you any rights under trademark law for use of our trademarks.
*
* All the Product's GUI elements, including illustrations and icon sets, as
* well as technical writing content are licensed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
*
*/
#include "./../source/ECMACryptFile.h"
#include "./../../DesktopEditor/common/File.h"
#include "./../../Common/3dParty/openssl/common/common_openssl.h"
#include <iostream>
#include <map>
#include <vector>
void string_replace(std::wstring& text, const std::wstring& replaceFrom, const std::wstring& replaceTo)
{
size_t posn = 0;
while (std::wstring::npos != (posn = text.find(replaceFrom, posn)))
{
text.replace(posn, replaceFrom.length(), replaceTo);
posn += replaceTo.length();
}
}
void string_replaceA(std::string& text, const std::string& replaceFrom, const std::string& replaceTo)
{
size_t posn = 0;
while (std::string::npos != (posn = text.find(replaceFrom, posn)))
{
text.replace(posn, replaceFrom.length(), replaceTo);
posn += replaceTo.length();
}
}
enum RecordType
{
rtNone = 0,
rtAdd = 1,
rtRemove = 3,
rtEncrypt = 4,
rtDecrypt = 5,
rtPrint = 6,
rtMaster = 7
};
class CRecord
{
public:
RecordType Type = rtNone;
std::map<std::wstring, std::wstring> Items;
public:
CRecord(RecordType type = rtNone)
{
Type = type;
}
CRecord(const CRecord& rec)
{
Type = rec.Type;
Items = rec.Items;
}
CRecord& operator=(const CRecord& rec)
{
Type = rec.Type;
Items = rec.Items;
return *this;
}
static RecordType getType(const std::wstring& key)
{
if (key == L"--add")
return rtAdd;
if (key == L"--remove")
return rtRemove;
if (key == L"--encrypt")
return rtEncrypt;
if (key == L"--decrypt")
return rtDecrypt;
if (key == L"--print")
return rtPrint;
if (key == L"--master")
return rtMaster;
return rtNone;
}
std::wstring getValue(const std::wstring& key)
{
std::map<std::wstring, std::wstring>::iterator iter = Items.find(key);
if (iter == Items.end())
return L"";
return iter->second;
}
void addItem(std::wstring& key)
{
if (0 == key.find(L"--"))
key = key.substr(2);
std::wstring::size_type pos = key.find(L"=");
if (std::wstring::npos != pos)
{
Items.insert(std::pair<std::wstring, std::wstring>(key.substr(0, pos), key.substr(pos + 1)));
}
}
bool isValid()
{
switch (Type)
{
case rtAdd:
{
if (!getValue(L"user").empty() && (!getValue(L"data").empty() || !getValue(L"key").empty()))
return true;
break;
}
case rtRemove:
{
if (!getValue(L"user").empty())
return true;
break;
}
case rtEncrypt:
{
return true;
}
case rtDecrypt:
{
return true;
}
case rtPrint:
{
return true;
}
case rtMaster:
{
if (!getValue(L"user").empty() && !getValue(L"key").empty())
return true;
return true;
}
default:
break;
}
std::string sCommandName = U_TO_UTF8(getName());
if (sCommandName.empty())
{
std::cout << "unknown command" << std::endl;
}
else
{
std::cout << "bad command: " << sCommandName << std::endl;
}
return false;
}
std::wstring getName()
{
switch (Type)
{
case rtAdd:
{
return L"add";
}
case rtRemove:
{
return L"remove";
}
case rtEncrypt:
{
return L"encrypt";
}
case rtDecrypt:
{
return L"decrypt";
}
case rtPrint:
{
return L"printinfo";
}
case rtMaster:
{
return L"master";
}
default:
break;
}
return L"";
}
};
#ifdef WIN32
int wmain(int argc, wchar_t** argv)
#else
int main(int argc, char** argv)
#endif
{
if (argc <= 0)
return 0;
std::wstring file_path;
std::wstring file_password;
CRecord EncryptRecord;
CRecord DecryptRecord;
CRecord PrintRecord;
CRecord MasterRecord;
std::vector<CRecord> Records;
CRecord CurrentRecord;
for (int i = 0; i < argc; ++i)
{
std::string param;
#ifdef WIN32
param = U_TO_UTF8(std::wstring(argv[i]));
#else
param = std::string(argv[i]);
#endif
if (param == "--help")
{
std::cout << "1) encrypt/decrypt" << std::endl;
std::cout << "decrypt command removes all user info" << std::endl;
std::cout << "ooxml_crypt --file=path_to_document --encrypt --password=password" << std::endl;
std::cout << "ooxml_crypt --file=path_to_document --decrypt --password=password" << std::endl;
std::cout << std::endl;
std::cout << "2) print info" << std::endl;
std::cout << "ooxml_crypt --print" << std::endl;
std::cout << std::endl;
std::cout << "3) add/remove records" << std::endl;
std::cout << "ooxml_crypt --file=path_to_document --add --user=user --data=data" << std::endl;
std::cout << "ooxml_crypt --file=path_to_document --remove --user=user" << std::endl;
std::cout << std::endl;
std::cout << "4) generate record" << std::endl;
std::cout << "ooxml_crypt --file=path_to_document --add --user=user --key=pem_file_public_key --password=password" << std::endl;
std::cout << std::endl;
std::cout << "5) work without password" << std::endl;
std::cout << "ooxml_crypt --file=path_to_document --add --user=user --key=pem_file --master --user=user --key=pem_file_private_key" << std::endl;
std::cout << std::endl;
return 0;
}
}
for (int i = 0; i <= argc; ++i)
{
// чтобы не дублировать код
std::wstring param = L"--print";
if (i < argc)
{
#ifdef WIN32
param = std::wstring(argv[i]);
#else
std::string paramA(argv[i]);
param = UTF8_TO_U(paramA);
#endif
}
std::wstring::size_type len = param.length();
if (2 > len)
continue;
if (0 == param.find(L"--file="))
{
file_path = param.substr(7);
continue;
}
if (0 == param.find(L"--password="))
{
file_password = param.substr(11);
continue;
}
RecordType rtCurrent = CRecord::getType(param);
if (rtNone != rtCurrent)
{
if (CurrentRecord.Type != rtNone && CurrentRecord.isValid())
{
switch (CurrentRecord.Type)
{
case rtDecrypt:
{
DecryptRecord = CurrentRecord;
break;
}
case rtEncrypt:
{
EncryptRecord = CurrentRecord;
break;
}
case rtPrint:
{
PrintRecord = CurrentRecord;
break;
}
case rtAdd:
case rtRemove:
{
Records.push_back(CurrentRecord);
break;
}
case rtMaster:
{
MasterRecord = CurrentRecord;
}
default:
break;
}
}
CurrentRecord.Type = rtCurrent;
CurrentRecord.Items.clear();
}
else if (CurrentRecord.Type != rtNone)
{
CurrentRecord.addItem(param);
}
}
if (file_path.empty() || !NSFile::CFileBinary::Exists(file_path))
{
std::cout << "error: file not exist" << std::endl;
return 1;
}
if (DecryptRecord.Type == rtDecrypt)
{
ECMACryptFile file;
bool bDataIntegrity = false;
bool result = file.DecryptOfficeFile(file_path, file_path, file_password, bDataIntegrity);
if (!result)
{
std::cout << "error: file is not decrypted" << std::endl;
return 1;
}
return 0;
}
if (EncryptRecord.Type == rtEncrypt)
{
ECMACryptFile file;
bool result = file.EncryptOfficeFile(file_path, file_path, file_password, L"ONLYOFFICE CryptoEngine (Version 1)\n\n");
if (!result)
{
std::cout << "error: file is not encrypted" << std::endl;
return 1;
}
}
ECMACryptFile file;
std::string docinfo = file.ReadAdditional(file_path, L"DocumentID");
std::string docinfoOld = docinfo;
// декодируем пароль (если надо)
if (MasterRecord.Type == rtMaster && file_password.empty())
{
std::string user = U_TO_UTF8(MasterRecord.getValue(L"user"));
std::wstring keyW = MasterRecord.getValue(L"key");
std::string::size_type pos = docinfo.find(user);
while (pos != std::string::npos)
{
std::string::size_type posEndUser = docinfo.find("\n", pos);
if (posEndUser == std::string::npos)
break;
std::string userCur = docinfo.substr(pos, posEndUser - pos);
if (user == userCur)
{
std::string::size_type posEndUser2 = docinfo.find("\n", posEndUser + 1);
if (posEndUser2 == std::string::npos)
break;
std::string data = docinfo.substr(posEndUser + 1, posEndUser2 - posEndUser - 1);
std::string private_key_content;
NSFile::CFileBinary::ReadAllTextUtf8A(keyW, private_key_content);
std::string password;
if (NSOpenSSL::RSA_DecryptPrivate_desktop((unsigned char*)private_key_content.c_str(), data, password))
{
file_password = UTF8_TO_U(password);
break;
}
}
pos = docinfo.find(user);
}
}
// сначала удаляем
for (std::vector<CRecord>::iterator iter = Records.begin(); iter != Records.end(); iter++)
{
CRecord& rec = *iter;
if (rec.Type != rtRemove)
continue;
std::string user = U_TO_UTF8(rec.getValue(L"user"));
std::string::size_type pos = docinfo.find(user);
while (pos != std::string::npos)
{
std::string::size_type posEnd = docinfo.find("\n\n", pos);
if (posEnd == std::string::npos)
break;
docinfo = docinfo.substr(0, pos) + docinfo.substr(posEnd + 2);
pos = docinfo.find(user);
}
}
// теперь добавляем
for (std::vector<CRecord>::iterator iter = Records.begin(); iter != Records.end(); iter++)
{
CRecord& rec = *iter;
if (rec.Type != rtAdd)
continue;
std::string user = U_TO_UTF8(rec.getValue(L"user"));
if (user.empty())
continue;
std::string data = U_TO_UTF8(rec.getValue(L"data"));
if (!data.empty())
{
// записи не дублируем
std::string::size_type pos = docinfo.find(user);
while (pos != std::string::npos)
{
std::string::size_type posEnd = docinfo.find("\n\n", pos);
if (posEnd == std::string::npos)
break;
std::string user_record = docinfo.substr(pos, posEnd - pos + 2);
if (std::string::npos != user_record.find(data))
{
data = "";
break;
}
pos = docinfo.find(user);
}
}
if (!data.empty())
{
docinfo += user;
docinfo += "\n";
docinfo += data;
docinfo += "\n\n";
continue;
}
std::wstring keyW = rec.getValue(L"key");
if (NSFile::CFileBinary::Exists(keyW))
{
std::string public_key_content;
NSFile::CFileBinary::ReadAllTextUtf8A(keyW, public_key_content);
if (NSOpenSSL::RSA_EncryptPublic_desktop((unsigned char*)public_key_content.c_str(), U_TO_UTF8(file_password), data))
{
docinfo += user;
docinfo += "\n";
docinfo += data;
docinfo += "\n\n";
}
}
}
if (docinfo != docinfoOld)
{
bool result = file.WriteAdditional(file_path, L"DocumentID", docinfo);
if (!result)
{
std::cout << "error: docinfo not writed" << std::endl;
return 1;
}
}
if (PrintRecord.Type == rtPrint)
{
ECMACryptFile file;
std::string docinfo = file.ReadAdditional(file_path, L"DocumentID");
std::cout << docinfo << std::endl;
}
return 0;
}