feat: support decrypt & verify email

This commit is contained in:
saturneric 2024-11-28 17:19:32 +01:00
parent 0a88a9a46c
commit 3d5d465219
5 changed files with 807 additions and 380 deletions

View File

@ -323,8 +323,12 @@ auto GetEMLMetaData(vmime::shared_ptr<vmime::message>& message,
meta_data.from = from_field_value_text; meta_data.from = from_field_value_text;
meta_data.to = to_field_value_text.split(','); meta_data.to = to_field_value_text.split(',');
meta_data.cc = cc_field_value_text.split(','); meta_data.cc = cc_field_value_text.trimmed().isEmpty()
meta_data.bcc = bcc_field_value_text.split(','); ? QStringList()
: cc_field_value_text.split(',');
meta_data.bcc = bcc_field_value_text.trimmed().isEmpty()
? QStringList()
: bcc_field_value_text.split(',');
meta_data.subject = subject_field_value_text; meta_data.subject = subject_field_value_text;
return 0; return 0;
} }

View File

@ -36,9 +36,10 @@
#include "EMailHelper.h" #include "EMailHelper.h"
#include "GFModuleCommonUtils.hpp" #include "GFModuleCommonUtils.hpp"
auto EncryptEMLData(int channel, const QStringList& keys, auto EncryptPlainText(int channel, const QStringList& keys,
const EMailMetaData& meta_data, const QByteArray& body_data, const EMailMetaData& meta_data,
QString& eml_data) -> int { const QByteArray& body_data, QString& eml_data,
QString& capsule_id) -> int {
auto from = meta_data.from; auto from = meta_data.from;
auto recipient_list = meta_data.to; auto recipient_list = meta_data.to;
auto cc_list = meta_data.cc; auto cc_list = meta_data.cc;
@ -49,17 +50,21 @@ auto EncryptEMLData(int channel, const QStringList& keys,
QString email; QString email;
try { try {
GFGpgEncryptionResult* enc_result = nullptr; GFGpgEncryptionResult* s = nullptr;
auto ret = GFGpgEncryptData(channel, QListToCharArray(keys), keys.size(), auto ret = GFGpgEncryptData(channel, QListToCharArray(keys), keys.size(),
QDUP(body_data), 1, &enc_result); QDUP(body_data), 1, &s);
if (ret != 0) { if (ret != 0) {
eml_data = "Encryption Failed"; eml_data = "Encryption Failed";
return -1; return -1;
} }
auto encrypted_data = UDUP(enc_result->encrypted_data); auto encrypted_data = UDUP(s->encrypted_data);
GFFreeMemory(enc_result);
capsule_id = UDUP(s->capsule_id);
FLOG_DEBUG("got capsule id: %1", capsule_id);
GFFreeMemory(s);
vmime::messageBuilder msg_builder; vmime::messageBuilder msg_builder;
@ -193,9 +198,177 @@ auto EncryptEMLData(int channel, const QStringList& keys,
return -1; return -1;
} }
auto SignEMLData(int channel, const QString& key, auto EncryptEMLData(int channel, const QStringList& keys,
const EMailMetaData& meta_data, const QByteArray& body_data, const vmime::shared_ptr<vmime::message>& message,
QString& eml_data) -> int { const QByteArray& body_data, QString& eml_data,
QString& capsule_id) -> int {
try {
auto header = message->getHeader();
auto body = message->getBody();
auto body_offset = body->getParsedOffset();
auto body_len = body->getParsedLength();
auto plain_body_signed_raw_data = body_data.mid(
static_cast<qsizetype>(body_offset), static_cast<qsizetype>(body_len));
auto backup_content_type_header_field_component =
header->getField<vmime::headerField>(vmime::fields::CONTENT_TYPE)
->clone();
std::shared_ptr<vmime::headerField> backup_content_type_header_field =
std::static_pointer_cast<vmime::headerField>(
backup_content_type_header_field_component);
auto backup_from_field_component =
header->getField<vmime::headerField>(vmime::fields::FROM)->clone();
std::shared_ptr<vmime::headerField> backup_from_field =
std::static_pointer_cast<vmime::headerField>(
backup_from_field_component);
auto backup_to_field_component =
header->getField<vmime::headerField>(vmime::fields::TO)->clone();
std::shared_ptr<vmime::headerField> backup_to_field =
std::static_pointer_cast<vmime::headerField>(backup_to_field_component);
auto backup_message_id_field_component =
header->getField<vmime::headerField>(vmime::fields::MESSAGE_ID)
->clone();
std::shared_ptr<vmime::headerField> backup_message_id_field =
std::static_pointer_cast<vmime::headerField>(
backup_message_id_field_component);
auto backup_subject_field_component =
header->getField<vmime::headerField>(vmime::fields::SUBJECT)->clone();
std::shared_ptr<vmime::headerField> backup_subject_field =
std::static_pointer_cast<vmime::headerField>(
backup_subject_field_component);
auto plain_part = vmime::make_shared<vmime::bodyPart>();
auto plain_part_header = plain_part->getHeader();
plain_part_header->appendField(backup_content_type_header_field);
plain_part_header->appendField(backup_subject_field);
plain_part_header->appendField(backup_from_field);
plain_part_header->appendField(backup_to_field);
plain_part_header->appendField(backup_message_id_field);
auto plain_header_raw_data =
Q_SC(plain_part_header->generate(vmime::lineLengthLimits::convenient));
auto plain_raw_data =
plain_header_raw_data + "\r\n" + plain_body_signed_raw_data;
plain_raw_data.replace("\r\n", "\n");
plain_raw_data.replace("\n", "\r\n");
GFGpgEncryptionResult* enc_result = nullptr;
auto ret = GFGpgEncryptData(channel, QListToCharArray(keys), keys.size(),
QDUP(plain_raw_data), 1, &enc_result);
if (ret != 0) {
eml_data = "Encryption Failed";
return -1;
}
auto encrypted_data = UDUP(enc_result->encrypted_data);
capsule_id = UDUP(enc_result->capsule_id);
FLOG_DEBUG("got capsule id: %1", capsule_id);
GFFreeMemory(enc_result);
// no Content-Transfer-Encoding
header->removeField(
header->getField(vmime::fields::CONTENT_TRANSFER_ENCODING));
auto content_type_header_field =
header->getField<vmime::contentTypeField>(vmime::fields::CONTENT_TYPE);
content_type_header_field->setValue("multipart/encrypted");
content_type_header_field->appendParameter(
vmime::make_shared<vmime::parameter>("protocol",
"application/pgp-encrypted"));
// hide subject
header->Subject()->setValue("...");
auto root_part_boundary = vmime::body::generateRandomBoundaryString();
content_type_header_field->setBoundary(root_part_boundary);
auto root_body_part = vmime::make_shared<vmime::body>();
auto control_info_part = vmime::make_shared<vmime::bodyPart>();
auto encrypted_data_part = vmime::make_shared<vmime::bodyPart>();
root_body_part->appendPart(control_info_part);
root_body_part->appendPart(encrypted_data_part);
message->setBody(root_body_part);
root_body_part->setPrologText(
"This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)");
auto control_info_part_header = control_info_part->getHeader();
auto control_info_content_type_field =
control_info_part_header->getField<vmime::contentTypeField>(
vmime::fields::CONTENT_TYPE);
control_info_content_type_field->setValue("application/pgp-encrypted");
auto control_info_part_content_desc_header_field =
control_info_part_header->getField(vmime::fields::CONTENT_DESCRIPTION);
control_info_part_content_desc_header_field->setValue(
"PGP/MIME version identification");
auto control_info_body = control_info_part->getBody();
auto control_info_content =
vmime::make_shared<vmime::stringContentHandler>("Version: 1");
control_info_body->setContents(control_info_content);
auto encrypted_data_part_header = encrypted_data_part->getHeader();
auto encrypted_data_content_type_field =
encrypted_data_part_header->getField<vmime::contentTypeField>(
vmime::fields::CONTENT_TYPE);
encrypted_data_content_type_field->setValue("application/octet-stream");
encrypted_data_content_type_field->appendParameter(
vmime::make_shared<vmime::parameter>("name", "encrypted.asc"));
auto encrypted_data_content_desc_header_field =
encrypted_data_part_header->getField(
vmime::fields::CONTENT_DESCRIPTION);
encrypted_data_content_desc_header_field->setValue(
"OpenPGP encrypted message");
auto encrypted_data_content_disp_header_field =
encrypted_data_part_header->getField<vmime::contentDispositionField>(
vmime::fields::CONTENT_DISPOSITION);
encrypted_data_content_disp_header_field->setValue("inline");
encrypted_data_content_disp_header_field->setFilename(
vmime::word({"encrypted.asc"}));
auto encrypted_data_body = encrypted_data_part->getBody();
auto encrypted_data_content =
vmime::make_shared<vmime::stringContentHandler>(
encrypted_data.toStdString());
encrypted_data_body->setContents(encrypted_data_content);
eml_data = Q_SC(message->generate(vmime::lineLengthLimits::convenient));
FLOG_DEBUG("EML Data: %1", eml_data);
return 0;
} catch (const vmime::exception& e) {
eml_data = QString("VMIME Error: %1").arg(e.what());
return -1;
}
eml_data = QString("Unknown Error: %1");
return -1;
}
auto SignPlainText(int channel, const QString& key,
const EMailMetaData& meta_data, const QByteArray& body_data,
QString& eml_data, QString& capsule_id) -> int {
auto from = meta_data.from; auto from = meta_data.from;
auto recipient_list = meta_data.to; auto recipient_list = meta_data.to;
auto cc_list = meta_data.cc; auto cc_list = meta_data.cc;
@ -408,6 +581,9 @@ auto SignEMLData(int channel, const QString& key,
auto signature = UDUP(s->signature); auto signature = UDUP(s->signature);
auto hash_algo = UDUP(s->hash_algo); auto hash_algo = UDUP(s->hash_algo);
capsule_id = UDUP(s->capsule_id);
FLOG_DEBUG("got capsule id: %1", capsule_id);
GFFreeMemory(s); GFFreeMemory(s);
FLOG_DEBUG("Hash Algo: %1 Signature Data: %2", hash_algo, signature); FLOG_DEBUG("Hash Algo: %1 Signature Data: %2", hash_algo, signature);
@ -437,9 +613,9 @@ auto SignEMLData(int channel, const QString& key,
return -1; return -1;
} }
auto AppendSignToEMLData(int channel, const QString& key, auto SignEMLData(int channel, const QString& key,
const vmime::shared_ptr<vmime::message>& message, const vmime::shared_ptr<vmime::message>& message,
QString& eml_data) -> int { QString& eml_data, QString& capsule_id) -> int {
try { try {
auto header = message->getHeader(); auto header = message->getHeader();
@ -619,6 +795,9 @@ auto AppendSignToEMLData(int channel, const QString& key,
auto signature = UDUP(s->signature); auto signature = UDUP(s->signature);
auto hash_algo = UDUP(s->hash_algo); auto hash_algo = UDUP(s->hash_algo);
capsule_id = UDUP(s->capsule_id);
FLOG_DEBUG("got capsule id: %1", capsule_id);
GFFreeMemory(s); GFFreeMemory(s);
FLOG_DEBUG("Hash Algo: %1 Signature Data: %2", hash_algo, signature); FLOG_DEBUG("Hash Algo: %1 Signature Data: %2", hash_algo, signature);
@ -647,3 +826,384 @@ auto AppendSignToEMLData(int channel, const QString& key,
eml_data = QString("Unknown Error: %1"); eml_data = QString("Unknown Error: %1");
return -1; return -1;
} }
auto VerifyEMLData(int channel, const QByteArray& data,
EMailMetaData& meta_data, QString& error_string,
QString& capsule_id) -> int {
vmime::string vmime_data(data.constData(), data.size());
auto message = vmime::make_shared<vmime::message>();
try {
message->parse(vmime_data);
} catch (const vmime::exception& e) {
FLOG_DEBUG("error when parsing vmime data: %1", e.what());
error_string = "Error when parsing eml raw data";
return -2;
}
auto header = message->getHeader();
auto content_type_field =
header->getField<vmime::contentTypeField>(vmime::fields::CONTENT_TYPE);
if (!content_type_field) {
error_string = "Cannot get 'Content-Type' Field from header";
return -2;
}
auto content_type_value =
Q_SC(content_type_field->getValue()->generate()).trimmed();
auto prm_protocol = content_type_field->getParameter("protocol");
if (!prm_protocol) {
error_string = "Cannot get 'protocol' from 'Content-Type'";
return -2;
}
/*
* OpenPGP signed messages are denoted by the "multipart/signed" content
* type.
*/
if (content_type_value != "multipart/signed") {
error_string =
"OpenPGP signed messages are denoted by the 'multipart/signed' "
"content type";
return -2;
}
/*
* with a "protocol" parameter which MUST have a value of
* "application/pgp-signature"
*/
auto prm_protocol_value = Q_SC(prm_protocol->getValue().generate());
if (prm_protocol_value != "application/pgp-signature") {
error_string =
"The 'protocol' parameter which MUST have a value of "
"'application/pgp-signature' (MUST be quoted)";
return -2;
}
auto prm_micalg = content_type_field->getParameter("micalg");
if (!prm_micalg) {
error_string = "cannot get 'micalg' from 'Content-Type'";
return -2;
}
/*
* The "micalg" parameter for the "application/pgp-signature" protocol
* MUST contain exactly one hash-symbol of the format "pgp-<hash-
* identifier>", where <hash-identifier> identifies the Message
* Integrity Check (MIC) algorithm used to generate the signature.
*/
auto prm_micalg_value = Q_SC(prm_micalg->getValue().generate());
FLOG_DEBUG("micalg value: %1", prm_micalg_value);
if (!IsValidMicalgFormat(prm_micalg_value)) {
error_string =
"The 'micalg' parameter MUST contain exactly one hash-symbol of the "
"format 'pgp-<hash-identifier>'";
return -2;
}
auto from_field_value_text =
ExtractFieldValueMailBox(header, vmime::fields::FROM);
auto to_field_value_text =
ExtractFieldValueAddressList(header, vmime::fields::TO);
auto cc_field_value_text =
ExtractFieldValueAddressList(header, vmime::fields::CC);
auto bcc_field_value_text =
ExtractFieldValueAddressList(header, vmime::fields::BCC);
auto date_field_value =
ExtractFieldValueDateTime(header, vmime::fields::DATE);
auto subject_field_value_text =
ExtractFieldValueText(header, vmime::fields::SUBJECT);
auto reply_to_field_value_text =
ExtractFieldValueMailBox(header, vmime::fields::REPLY_TO);
auto organization_text =
ExtractFieldValueText(header, vmime::fields::ORGANIZATION);
auto body = message->getBody();
auto content_type = body->getContentType();
auto part_count = body->getPartCount();
FLOG_DEBUG("body page count: %1", part_count);
/*
* The multipart/signed body MUST consist of exactly two parts.
*/
if (part_count != 2) {
error_string =
"The multipart/signed body MUST consist of exactly two parts";
return -2;
}
/*
The first part contains the signed data in MIME canonical format,
including a set of appropriate content headers describing the data.
*/
auto part_mime = body->getPartAt(0);
auto part_mime_parse_offset = part_mime->getParsedOffset();
auto part_mime_parse_length = part_mime->getParsedLength();
auto part_mime_content_text = QByteArray::fromStdString(
vmime_data.substr(part_mime_parse_offset, part_mime_parse_length));
FLOG_DEBUG("mime part info, raw offset: %1, length: %2",
part_mime_parse_offset, part_mime_parse_length);
auto part_mime_content_hash = QCryptographicHash::hash(
part_mime_content_text, QCryptographicHash::Sha1);
FLOG_DEBUG("mime part of raw content hash: %1",
part_mime_content_hash.toHex());
FLOG_DEBUG("mime part of raw content: %1", part_mime_content_text);
qDebug().noquote() << "\n" << part_mime_content_text;
if (part_mime_content_text.isEmpty()) {
error_string = "Mime raw data part is empty";
return -2;
}
auto attachments =
vmime::attachmentHelper::findAttachmentsInBodyPart(part_mime);
FLOG_DEBUG("mime part info, attachment count: %1", attachments.size());
QStringList public_keys_buffer;
for (const auto& att : attachments) {
auto att_type = Q_SC(att->getType().generate()).trimmed();
FLOG_DEBUG("mime part info, attachment type: %1", att_type);
if (att_type != "application/pgp-keys") continue;
std::ostringstream oss;
vmime::utility::outputStreamAdapter osa(oss);
att->getData()->extract(osa);
public_keys_buffer.append(Q_SC(oss.str()));
}
FLOG_DEBUG("mime part info, attached public keys: ",
public_keys_buffer.join("\n"));
/*
* The second body MUST contain the OpenPGP digital signature. It MUST
* be labeled with a content type of "application/pgp-signature"
*/
auto part_sign = body->getPartAt(1);
auto part_sign_header = part_sign->getHeader();
auto part_sign_content_type = part_sign_header->ContentType();
auto part_sign_content_type_value =
Q_SC(part_sign_content_type->getValue()->generate());
if (part_sign_content_type_value != "application/pgp-signature") {
error_string =
"The second body MUST be labeled with a content type of "
"'application/pgp-signature'";
return -2;
}
auto part_sign_body_content =
QByteArray::fromStdString(part_sign->getBody()->generate());
if (part_sign_body_content.trimmed().isEmpty()) {
error_string = "The signature part is empty";
return -2;
}
FLOG_DEBUG("body part of signature content: %1", part_sign_body_content);
GFGpgVerifyResult* s;
auto ret = GFGpgVerifyData(channel, QDUP(part_mime_content_text),
QDUP(part_sign_body_content), &s);
if (ret != 0) {
error_string = "Verify Failed";
return -1;
}
capsule_id = UDUP(s->capsule_id);
FLOG_DEBUG("got capsule id: %1", capsule_id);
GFFreeMemory(s);
meta_data.from = from_field_value_text;
meta_data.to = to_field_value_text.split(',');
meta_data.cc = cc_field_value_text.split(',');
meta_data.bcc = bcc_field_value_text.split(',');
meta_data.subject = subject_field_value_text;
meta_data.datetime = date_field_value;
meta_data.micalg = prm_micalg_value;
meta_data.public_keys = public_keys_buffer.join("\n");
meta_data.mime = {};
meta_data.mime_hash = part_mime_content_hash.toHex();
meta_data.signature = {};
return 0;
}
auto DecryptEMLData(int channel, const QByteArray& data,
EMailMetaData& meta_data, QString& eml_data,
QString& capsule_id) -> int {
vmime::string vmime_data(data.constData(), data.size());
auto message = vmime::make_shared<vmime::message>();
try {
message->parse(vmime_data);
} catch (const vmime::exception& e) {
FLOG_DEBUG("error when parsing vmime data: %1", e.what());
eml_data = "Error when parsing EML Data";
return -2;
}
auto header = message->getHeader();
auto content_type_field =
header->getField<vmime::contentTypeField>(vmime::fields::CONTENT_TYPE);
if (!content_type_field) {
eml_data = "cannot get 'Content-Type' Field from header";
return -2;
}
auto content_type_value =
Q_SC(content_type_field->getValue()->generate()).trimmed();
auto prm_protocol = content_type_field->getParameter("protocol");
if (!prm_protocol) {
eml_data = "cannot get 'protocol' from 'Content-Type'";
return -2;
}
/*
* OpenPGP encrypted data is denoted by the "multipart/encrypted"
* content type
*/
if (content_type_value != "multipart/encrypted") {
eml_data =
"OpenPGP encrypted data is denoted by the 'multipart/encrypted' "
"content type";
return -2;
}
/*
* MUST have a "protocol" parameter value of "application/pgp-encrypted"
*/
auto prm_protocol_value = Q_SC(prm_protocol->getValue().generate());
if (prm_protocol_value != "application/pgp-encrypted") {
eml_data =
"'protocol' parameter which MUST have a value of "
"'application/pgp-encrypted' (MUST be quoted)";
return -2;
}
auto from_field_value_text =
ExtractFieldValueMailBox(header, vmime::fields::FROM);
auto to_field_value_text =
ExtractFieldValueAddressList(header, vmime::fields::TO);
auto cc_field_value_text =
ExtractFieldValueAddressList(header, vmime::fields::CC);
auto bcc_field_value_text =
ExtractFieldValueAddressList(header, vmime::fields::BCC);
auto date_field_value =
ExtractFieldValueDateTime(header, vmime::fields::DATE);
auto subject_field_value_text =
ExtractFieldValueText(header, vmime::fields::SUBJECT);
auto reply_to_field_value_text =
ExtractFieldValueMailBox(header, vmime::fields::REPLY_TO);
auto organization_text =
ExtractFieldValueText(header, vmime::fields::ORGANIZATION);
auto body = message->getBody();
auto content_type = body->getContentType();
auto part_count = body->getPartCount();
FLOG_DEBUG("body page count: %1", part_count);
/*
* The multipart/encrypted body MUST consist of exactly two parts.
*/
if (part_count != 2) {
eml_data = "The multipart/signed body MUST consist of exactly two parts";
return -2;
}
/*
* The multipart/encrypted MIME body MUST consist of exactly two body
* parts, the first with content type "application/pgp-encrypted". This
* body contains the control information.
*/
auto part_mime = body->getPartAt(0);
std::ostringstream oss;
vmime::utility::outputStreamAdapter osa(oss);
auto part_mime_body = part_mime->getBody();
auto part_mime_body_content = part_mime_body->getContents();
if (!part_mime_body_content) {
eml_data = "Cannot get the content of the first part's body";
return -2;
}
part_mime_body_content->extractRaw(osa);
osa.flush();
auto part_mime_body_content_text = Q_SC(oss.str());
FLOG_DEBUG("body part of raw content text: %1", part_mime_body_content_text);
/*
* A message complying with this
* standard MUST contain a "Version: 1" field in this body.
*/
if (!part_mime_body_content_text.contains("Version: 1")) {
eml_data =
"The first part MUST contain a 'Version: 1' field in this "
"body.";
return -2;
}
/*
* The second MIME body part MUST contain the actual encrypted data. It
* MUST be labeled with a content type of "application/octet-stream".
*/
auto part_sign = body->getPartAt(1);
auto part_sign_header = part_sign->getHeader();
auto part_sign_content_type = part_sign_header->ContentType();
auto part_sign_content_type_value =
Q_SC(part_sign_content_type->getValue()->generate());
if (part_sign_content_type_value != "application/octet-stream") {
eml_data =
"The second part MUST be labeled with a content type of "
"'application/octet-stream'";
return -2;
}
auto part_encr_body_content =
QByteArray::fromStdString(part_sign->getBody()->generate());
if (part_encr_body_content.trimmed().isEmpty()) {
eml_data = "The second part is empty";
return -2;
}
FLOG_DEBUG("body part of encrypt content: %1", part_encr_body_content);
GFGpgDecryptResult* s;
auto ret = GFGpgDecryptData(channel, QDUP(part_encr_body_content), &s);
if (ret != 0) {
eml_data = "Ddecrypt Failed";
return -1;
}
eml_data = UDUP(s->decrypted_data);
capsule_id = UDUP(s->capsule_id);
FLOG_DEBUG("got capsule id: %1", capsule_id);
GFFreeMemory(s);
// callback
meta_data.from = from_field_value_text;
meta_data.to = to_field_value_text.split(',');
meta_data.cc = cc_field_value_text.split(',');
meta_data.bcc = bcc_field_value_text.split(',');
meta_data.subject = subject_field_value_text;
meta_data.datetime = date_field_value;
meta_data.encrypted_data = part_encr_body_content;
return 0;
}

View File

@ -40,9 +40,24 @@
* @param eml_data * @param eml_data
* @return int * @return int
*/ */
auto EncryptPlainText(int channel, const QStringList& keys,
const EMailMetaData& meta_data,
const QByteArray& body_data, QString& eml_data,
QString& capsule_id) -> int;
/**
* @brief
*
* @param channel
* @param keys
* @param message
* @param eml_data
* @return int
*/
auto EncryptEMLData(int channel, const QStringList& keys, auto EncryptEMLData(int channel, const QStringList& keys,
const EMailMetaData& meta_data, const QByteArray& body_data, const vmime::shared_ptr<vmime::message>& message,
QString& eml_data) -> int; const QByteArray& body_data, QString& eml_data,
QString& capsule_id) -> int;
/** /**
* @brief * @brief
@ -54,9 +69,9 @@ auto EncryptEMLData(int channel, const QStringList& keys,
* @param eml_data * @param eml_data
* @return int * @return int
*/ */
auto SignEMLData(int channel, const QString& key, auto SignPlainText(int channel, const QString& key,
const EMailMetaData& meta_data, const QByteArray& body_data, const EMailMetaData& meta_data, const QByteArray& body_data,
QString& eml_data) -> int; QString& eml_data, QString& capsule_id) -> int;
/** /**
* @brief * @brief
@ -67,6 +82,28 @@ auto SignEMLData(int channel, const QString& key,
* @param eml_data * @param eml_data
* @return int * @return int
*/ */
auto AppendSignToEMLData(int channel, const QString& key, auto SignEMLData(int channel, const QString& key,
const vmime::shared_ptr<vmime::message>& message, const vmime::shared_ptr<vmime::message>& message,
QString& eml_data) -> int; QString& eml_data, QString& capsule_id) -> int;
/**
* @brief
*
* @param data
* @param error_string
* @return int
*/
auto VerifyEMLData(int channel, const QByteArray& data,
EMailMetaData& meta_data, QString& error_string,
QString& capsule_id) -> int;
/**
* @brief
*
* @param data
* @param error_string
* @return int
*/
auto DecryptEMLData(int channel, const QByteArray& data,
EMailMetaData& meta_data, QString& eml_data,
QString& capsule_id) -> int;

View File

@ -28,6 +28,7 @@
#pragma once #pragma once
#include <QDateTime>
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
@ -39,9 +40,19 @@
#include <vmime/contentTypeField.hpp> #include <vmime/contentTypeField.hpp>
struct EMailMetaData { struct EMailMetaData {
// Basic MetaData
QString from; QString from;
QStringList to; QStringList to;
QStringList cc; QStringList cc;
QStringList bcc; QStringList bcc;
QString subject; QString subject;
QDateTime datetime;
QString micalg;
// OpenPGP MetaData
QString public_keys;
QByteArray mime;
QString mime_hash;
QByteArray signature;
QByteArray encrypted_data;
}; };

View File

@ -68,340 +68,72 @@ auto GFRegisterModule() -> int {
LISTEN("EMAIL_DECRYPT_EML_DATA"); LISTEN("EMAIL_DECRYPT_EML_DATA");
LISTEN("EMAIL_SIGN_EML_DATA"); LISTEN("EMAIL_SIGN_EML_DATA");
LISTEN("EMAIL_ENCRYPT_EML_DATA"); LISTEN("EMAIL_ENCRYPT_EML_DATA");
LISTEN("EMAIL_ENCRYPT_SIGN_EML_DATA");
LISTEN("EMAIL_DECRYPT_VERIFY_EML_DATA");
return 0; return 0;
} }
auto GFActiveModule() -> int { return 0; } auto GFActiveModule() -> int { return 0; }
REGISTER_EVENT_HANDLER(EMAIL_VERIFY_EML_DATA, [](const MEvent& event) -> int { REGISTER_EVENT_HANDLER(EMAIL_VERIFY_EML_DATA, [](const MEvent& event) -> int {
if (event["channel"].isEmpty()) CB_ERR(event, -1, "channel is empty");
if (event["eml_data"].isEmpty()) CB_ERR(event, -1, "eml_data is empty"); if (event["eml_data"].isEmpty()) CB_ERR(event, -1, "eml_data is empty");
auto channel = event.value("channel", "0").toInt();
auto data = QByteArray::fromBase64(QString(event["eml_data"]).toLatin1()); auto data = QByteArray::fromBase64(QString(event["eml_data"]).toLatin1());
vmime::string vmime_data(data.constData(), data.size());
auto message = vmime::make_shared<vmime::message>(); EMailMetaData meta_data;
try { QString error_string;
message->parse(vmime_data); QString capsule_id;
} catch (const vmime::exception& e) { auto ret = VerifyEMLData(channel, data, meta_data, error_string, capsule_id);
FLOG_DEBUG("error when parsing vmime data: %1", e.what()); if (ret != 0) CB_ERR(event, ret, error_string);
CB_ERR(event, -2, "Error when parsing eml raw data");
}
auto header = message->getHeader();
auto content_type_field =
header->getField<vmime::contentTypeField>(vmime::fields::CONTENT_TYPE);
if (!content_type_field) {
CB_ERR(event, -2, "Cannot get 'Content-Type' Field from header");
}
auto content_type_value =
Q_SC(content_type_field->getValue()->generate()).trimmed();
auto prm_protocol = content_type_field->getParameter("protocol");
if (!prm_protocol) {
CB_ERR(event, -2, "Cannot get 'protocol' from 'Content-Type'");
}
/*
* OpenPGP signed messages are denoted by the "multipart/signed" content
* type.
*/
if (content_type_value != "multipart/signed")
CB_ERR(event, -2,
"OpenPGP signed messages are denoted by the 'multipart/signed' "
"content type");
/*
* with a "protocol" parameter which MUST have a value of
* "application/pgp-signature"
*/
auto prm_protocol_value = Q_SC(prm_protocol->getValue().generate());
if (prm_protocol_value != "application/pgp-signature")
CB_ERR(event, -2,
"The 'protocol' parameter which MUST have a value of "
"'application/pgp-signature' (MUST be quoted)");
auto prm_micalg = content_type_field->getParameter("micalg");
if (!prm_micalg) {
CB_ERR(event, -2, "cannot get 'micalg' from 'Content-Type'");
}
/*
* The "micalg" parameter for the "application/pgp-signature" protocol
* MUST contain exactly one hash-symbol of the format "pgp-<hash-
* identifier>", where <hash-identifier> identifies the Message
* Integrity Check (MIC) algorithm used to generate the signature.
*/
auto prm_micalg_value = Q_SC(prm_micalg->getValue().generate());
FLOG_DEBUG("micalg value: %1", prm_micalg_value);
if (!IsValidMicalgFormat(prm_micalg_value)) {
CB_ERR(event, -2,
"The 'micalg' parameter MUST contain exactly one hash-symbol of the "
"format 'pgp-<hash-identifier>'");
}
auto from_field_value_text =
ExtractFieldValueMailBox(header, vmime::fields::FROM);
auto to_field_value_text =
ExtractFieldValueAddressList(header, vmime::fields::TO);
auto cc_field_value_text =
ExtractFieldValueAddressList(header, vmime::fields::CC);
auto bcc_field_value_text =
ExtractFieldValueAddressList(header, vmime::fields::BCC);
auto date_field_value =
ExtractFieldValueDateTime(header, vmime::fields::DATE);
auto subject_field_value_text =
ExtractFieldValueText(header, vmime::fields::SUBJECT);
auto reply_to_field_value_text =
ExtractFieldValueMailBox(header, vmime::fields::REPLY_TO);
auto organization_text =
ExtractFieldValueText(header, vmime::fields::ORGANIZATION);
auto body = message->getBody();
auto content_type = body->getContentType();
auto part_count = body->getPartCount();
FLOG_DEBUG("body page count: %1", part_count);
/*
* The multipart/signed body MUST consist of exactly two parts.
*/
if (part_count != 2)
CB_ERR(event, -2,
"The multipart/signed body MUST consist of exactly two parts");
/*
The first part contains the signed data in MIME canonical format,
including a set of appropriate content headers describing the data.
*/
auto part_mime = body->getPartAt(0);
auto part_mime_parse_offset = part_mime->getParsedOffset();
auto part_mime_parse_length = part_mime->getParsedLength();
auto part_mime_content_text = QByteArray::fromStdString(
vmime_data.substr(part_mime_parse_offset, part_mime_parse_length));
FLOG_DEBUG("mime part info, raw offset: %1, length: %2",
part_mime_parse_offset, part_mime_parse_length);
auto part_mime_content_hash = QCryptographicHash::hash(
part_mime_content_text, QCryptographicHash::Sha1);
FLOG_DEBUG("mime part of raw content hash: %1",
part_mime_content_hash.toHex());
FLOG_DEBUG("mime part of raw content: %1", part_mime_content_text);
qDebug().noquote() << "\n" << part_mime_content_text;
if (part_mime_content_text.isEmpty())
CB_ERR(event, -2, "mime raw data part is empty");
auto attachments =
vmime::attachmentHelper::findAttachmentsInBodyPart(part_mime);
FLOG_DEBUG("mime part info, attachment count: %1", attachments.size());
QStringList public_keys_buffer;
for (const auto& att : attachments) {
auto att_type = Q_SC(att->getType().generate()).trimmed();
FLOG_DEBUG("mime part info, attachment type: %1", att_type);
if (att_type != "application/pgp-keys") continue;
std::ostringstream oss;
vmime::utility::outputStreamAdapter osa(oss);
att->getData()->extract(osa);
public_keys_buffer.append(Q_SC(oss.str()));
}
FLOG_DEBUG("mime part info, attached public keys: ",
public_keys_buffer.join("\n"));
/*
* The second body MUST contain the OpenPGP digital signature. It MUST
* be labeled with a content type of "application/pgp-signature"
*/
auto part_sign = body->getPartAt(1);
auto part_sign_header = part_sign->getHeader();
auto part_sign_content_type = part_sign_header->ContentType();
auto part_sign_content_type_value =
Q_SC(part_sign_content_type->getValue()->generate());
if (part_sign_content_type_value != "application/pgp-signature")
CB_ERR(event, -2,
"The second body MUST be labeled with a content type of "
"'application/pgp-signature'");
auto part_sign_body_content =
QByteArray::fromStdString(part_sign->getBody()->generate());
if (part_sign_body_content.trimmed().isEmpty())
CB_ERR(event, -2, "The signature part is empty");
FLOG_DEBUG("body part of signature content: %1", part_sign_body_content);
// callback // callback
CB(event, GFGetModuleID(), CB(event, GFGetModuleID(),
{ {
{"ret", QString::number(0)}, {"ret", QString::number(0)},
{"mime", QString::fromLatin1(part_mime_content_text.toBase64())}, {"mime", QString::fromLatin1(meta_data.mime.toBase64())},
{"mime_hash", part_mime_content_hash.toHex()}, {"mime_hash", meta_data.mime_hash},
{"signature", QString::fromLatin1(part_sign_body_content.toBase64())}, {"signature", QString::fromLatin1(meta_data.signature.toBase64())},
{"from", from_field_value_text}, {"from", meta_data.from},
{"to", to_field_value_text}, {"to", meta_data.to.join("; ")},
{"cc", cc_field_value_text}, {"cc", meta_data.cc.join("; ")},
{"bcc", bcc_field_value_text}, {"bcc", meta_data.bcc.join("; ")},
{"subject", subject_field_value_text}, {"subject", meta_data.subject},
{"datetime", QString::number(date_field_value.toMSecsSinceEpoch())}, {"datetime", QString::number(meta_data.datetime.toMSecsSinceEpoch())},
{"micalg", prm_micalg_value}, {"micalg", meta_data.micalg},
{"public_keys", public_keys_buffer.join("\n")}, {"public_keys", meta_data.public_keys},
{"capsule_id", capsule_id},
}); });
return 0; return 0;
}); });
REGISTER_EVENT_HANDLER(EMAIL_DECRYPT_EML_DATA, [](const MEvent& event) -> int { REGISTER_EVENT_HANDLER(EMAIL_DECRYPT_EML_DATA, [](const MEvent& event) -> int {
if (event["channel"].isEmpty()) CB_ERR(event, -1, "channel is empty");
if (event["eml_data"].isEmpty()) CB_ERR(event, -1, "eml_data is empty"); if (event["eml_data"].isEmpty()) CB_ERR(event, -1, "eml_data is empty");
auto channel = event.value("channel", "0").toInt();
auto data = QByteArray::fromBase64(QString(event["eml_data"]).toLatin1()); auto data = QByteArray::fromBase64(QString(event["eml_data"]).toLatin1());
vmime::string vmime_data(data.constData(), data.size());
auto message = vmime::make_shared<vmime::message>(); EMailMetaData meta_data;
try { QString eml_data;
message->parse(vmime_data); QString capsule_id;
} catch (const vmime::exception& e) { auto ret = DecryptEMLData(channel, data, meta_data, eml_data, capsule_id);
FLOG_DEBUG("error when parsing vmime data: %1", e.what()); if (ret != 0) CB_ERR(event, ret, eml_data);
CB_ERR(event, -2, "error when parsing vmime data");
}
auto header = message->getHeader();
auto content_type_field =
header->getField<vmime::contentTypeField>(vmime::fields::CONTENT_TYPE);
if (!content_type_field) {
CB_ERR(event, -2, "cannot get 'Content-Type' Field from header");
}
auto content_type_value =
Q_SC(content_type_field->getValue()->generate()).trimmed();
auto prm_protocol = content_type_field->getParameter("protocol");
if (!prm_protocol) {
CB_ERR(event, -2, "cannot get 'protocol' from 'Content-Type'");
}
/*
* OpenPGP encrypted data is denoted by the "multipart/encrypted"
* content type
*/
if (content_type_value != "multipart/encrypted")
CB_ERR(event, -2,
"OpenPGP encrypted data is denoted by the 'multipart/encrypted' "
"content type");
/*
* MUST have a "protocol" parameter value of "application/pgp-encrypted"
*/
auto prm_protocol_value = Q_SC(prm_protocol->getValue().generate());
if (prm_protocol_value != "application/pgp-encrypted")
CB_ERR(event, -2,
"'protocol' parameter which MUST have a value of "
"'application/pgp-encrypted' (MUST be quoted)");
auto from_field_value_text =
ExtractFieldValueMailBox(header, vmime::fields::FROM);
auto to_field_value_text =
ExtractFieldValueAddressList(header, vmime::fields::TO);
auto cc_field_value_text =
ExtractFieldValueAddressList(header, vmime::fields::CC);
auto bcc_field_value_text =
ExtractFieldValueAddressList(header, vmime::fields::BCC);
auto date_field_value =
ExtractFieldValueDateTime(header, vmime::fields::DATE);
auto subject_field_value_text =
ExtractFieldValueText(header, vmime::fields::SUBJECT);
auto reply_to_field_value_text =
ExtractFieldValueMailBox(header, vmime::fields::REPLY_TO);
auto organization_text =
ExtractFieldValueText(header, vmime::fields::ORGANIZATION);
auto body = message->getBody();
auto content_type = body->getContentType();
auto part_count = body->getPartCount();
FLOG_DEBUG("body page count: %1", part_count);
/*
* The multipart/encrypted body MUST consist of exactly two parts.
*/
if (part_count != 2)
CB_ERR(event, -2,
"The multipart/signed body MUST consist of exactly two parts");
/*
* The multipart/encrypted MIME body MUST consist of exactly two body
* parts, the first with content type "application/pgp-encrypted". This
* body contains the control information.
*/
auto part_mime = body->getPartAt(0);
std::ostringstream oss;
vmime::utility::outputStreamAdapter osa(oss);
auto part_mime_body = part_mime->getBody();
auto part_mime_body_content = part_mime_body->getContents();
if (!part_mime_body_content) {
CB_ERR(event, -2, "Cannot get the content of the first part's body");
}
part_mime_body_content->extractRaw(osa);
osa.flush();
auto part_mime_body_content_text = Q_SC(oss.str());
FLOG_DEBUG("body part of raw content text: %1", part_mime_body_content_text);
/*
* A message complying with this
* standard MUST contain a "Version: 1" field in this body.
*/
if (!part_mime_body_content_text.contains("Version: 1")) {
CB_ERR(event, -2,
"The first part MUST contain a 'Version: 1' field in this body.");
}
/*
* The second MIME body part MUST contain the actual encrypted data. It
* MUST be labeled with a content type of "application/octet-stream".
*/
auto part_sign = body->getPartAt(1);
auto part_sign_header = part_sign->getHeader();
auto part_sign_content_type = part_sign_header->ContentType();
auto part_sign_content_type_value =
Q_SC(part_sign_content_type->getValue()->generate());
if (part_sign_content_type_value != "application/octet-stream")
CB_ERR(event, -2,
"The second part MUST be labeled with a content type of "
"'application/octet-stream'");
auto part_encr_body_content =
QByteArray::fromStdString(part_sign->getBody()->generate());
if (part_encr_body_content.trimmed().isEmpty())
CB_ERR(event, -2, "The second part is empty");
FLOG_DEBUG("body part of encrypt content: %1", part_encr_body_content);
// callback // callback
CB(event, GFGetModuleID(), CB(event, GFGetModuleID(),
{ {
{"ret", QString::number(0)}, {"ret", QString::number(0)},
{"encrypted", QString::fromLatin1(part_encr_body_content.toBase64())}, {"eml_data", QString::fromLatin1(eml_data.toLatin1().toBase64())},
{"from", from_field_value_text}, {"from", meta_data.from},
{"to", to_field_value_text}, {"to", meta_data.to.join("; ")},
{"cc", cc_field_value_text}, {"cc", meta_data.cc.join("; ")},
{"bcc", bcc_field_value_text}, {"bcc", meta_data.bcc.join("; ")},
{"subject", subject_field_value_text}, {"subject", meta_data.subject},
{"datetime", QString::number(date_field_value.toMSecsSinceEpoch())}, {"datetime", QString::number(meta_data.datetime.toMSecsSinceEpoch())},
{"capsule_id", capsule_id},
}); });
return 0; return 0;
@ -440,7 +172,8 @@ REGISTER_EVENT_HANDLER(EMAIL_SIGN_EML_DATA, [](const MEvent& event) -> int {
} }
QString eml_data; QString eml_data;
ret = AppendSignToEMLData(channel, sign_key, message, eml_data); QString capsule_id;
ret = SignEMLData(channel, sign_key, message, eml_data, capsule_id);
if (ret != 0) { if (ret != 0) {
CB_ERR(event, -2, eml_data); CB_ERR(event, -2, eml_data);
} }
@ -449,6 +182,7 @@ REGISTER_EVENT_HANDLER(EMAIL_SIGN_EML_DATA, [](const MEvent& event) -> int {
{ {
{"ret", QString::number(0)}, {"ret", QString::number(0)},
{"eml_data", eml_data}, {"eml_data", eml_data},
{"capsule_id", capsule_id},
}); });
return 0; return 0;
} }
@ -457,8 +191,9 @@ REGISTER_EVENT_HANDLER(EMAIL_SIGN_EML_DATA, [](const MEvent& event) -> int {
QObject::connect(r_dialog, &EMailMetaDataDialog::SignalEMLMetaData, r_dialog, QObject::connect(r_dialog, &EMailMetaDataDialog::SignalEMLMetaData, r_dialog,
[=](const EMailMetaData& meta_data) { [=](const EMailMetaData& meta_data) {
QString eml_data; QString eml_data;
auto ret = SignEMLData(channel, sign_key, meta_data, QString capsule_id;
body_data, eml_data); auto ret = SignPlainText(channel, sign_key, meta_data,
body_data, eml_data, capsule_id);
if (ret != 0) { if (ret != 0) {
CB_ERR(event, -2, eml_data); CB_ERR(event, -2, eml_data);
} }
@ -467,6 +202,7 @@ REGISTER_EVENT_HANDLER(EMAIL_SIGN_EML_DATA, [](const MEvent& event) -> int {
{ {
{"ret", QString::number(0)}, {"ret", QString::number(0)},
{"eml_data", eml_data}, {"eml_data", eml_data},
{"capsule_id", capsule_id},
}); });
return 0; return 0;
}); });
@ -494,15 +230,10 @@ REGISTER_EVENT_HANDLER(EMAIL_ENCRYPT_EML_DATA, [](const MEvent& event) -> int {
vmime::shared_ptr<vmime::message> message; vmime::shared_ptr<vmime::message> message;
if (CheckIfEMLMessage(body_data, message)) { if (CheckIfEMLMessage(body_data, message)) {
EMailMetaData meta_data;
auto ret = GetEMLMetaData(message, meta_data);
if (ret != 0) {
CB_ERR(event, -1, "Get MetaData From EML Data Failed");
}
QString eml_data; QString eml_data;
ret = EncryptEMLData(channel, encrypt_keys, meta_data, body_data, eml_data); QString capsule_id;
auto ret = EncryptEMLData(channel, encrypt_keys, message, body_data,
eml_data, capsule_id);
if (ret != 0) { if (ret != 0) {
CB_ERR(event, -2, eml_data); CB_ERR(event, -2, eml_data);
} }
@ -511,6 +242,7 @@ REGISTER_EVENT_HANDLER(EMAIL_ENCRYPT_EML_DATA, [](const MEvent& event) -> int {
{ {
{"ret", QString::number(0)}, {"ret", QString::number(0)},
{"eml_data", eml_data}, {"eml_data", eml_data},
{"capsule_id", capsule_id},
}); });
return 0; return 0;
} }
@ -527,31 +259,34 @@ REGISTER_EVENT_HANDLER(EMAIL_ENCRYPT_EML_DATA, [](const MEvent& event) -> int {
GFUIShowDialog(dialog, nullptr); GFUIShowDialog(dialog, nullptr);
QObject::connect( QObject::connect(r_dialog, &EMailMetaDataDialog::SignalEMLMetaData, r_dialog,
r_dialog, &EMailMetaDataDialog::SignalEMLMetaData, r_dialog, [=](const EMailMetaData& meta_data) {
[=](const EMailMetaData& meta_data) { QString eml_data;
QString eml_data; QString capsule_id;
QString plain_text_eml_data; QString plain_text_eml_data;
auto ret = BuildPlainTextEML(meta_data, body_data, plain_text_eml_data); auto ret = BuildPlainTextEML(meta_data, body_data,
plain_text_eml_data);
if (ret != 0) { if (ret != 0) {
CB_ERR(event, -1, "Build PlainText EML Data Failed"); CB_ERR(event, -1, "Build PlainText EML Data Failed");
} }
ret = EncryptEMLData(channel, encrypt_keys, meta_data, ret = EncryptPlainText(channel, encrypt_keys, meta_data,
plain_text_eml_data.toLatin1(), eml_data); plain_text_eml_data.toLatin1(),
if (ret != 0) { eml_data, capsule_id);
CB_ERR(event, -2, eml_data); if (ret != 0) {
} CB_ERR(event, -2, eml_data);
}
CB(event, GFGetModuleID(), CB(event, GFGetModuleID(),
{ {
{"ret", QString::number(0)}, {"ret", QString::number(0)},
{"eml_data", eml_data}, {"eml_data", eml_data},
}); {"capsule_id", capsule_id},
return 0; });
}); return 0;
});
QObject::connect( QObject::connect(
r_dialog, &EMailMetaDataDialog::SignalNoEMLMetaData, r_dialog, r_dialog, &EMailMetaDataDialog::SignalNoEMLMetaData, r_dialog,
@ -573,11 +308,46 @@ REGISTER_EVENT_HANDLER(
auto encrypt_keys = event.value("encrypt_keys", "").split(';'); auto encrypt_keys = event.value("encrypt_keys", "").split(';');
FLOG_DEBUG("eml encrypt keys: %1", encrypt_keys.join(';')); FLOG_DEBUG("eml encrypt keys: %1", encrypt_keys.join(';'));
FLOG_DEBUG("eml sign key: %1", sign_key);
auto body_data = auto body_data =
QByteArray::fromBase64(QString(event["body_data"]).toLatin1()); QByteArray::fromBase64(QString(event["body_data"]).toLatin1());
vmime::shared_ptr<vmime::message> message;
if (CheckIfEMLMessage(body_data, message)) {
QString eml_data;
QString sign_capsule_id;
auto ret =
SignEMLData(channel, sign_key, message, eml_data, sign_capsule_id);
if (ret != 0) {
CB_ERR(event, -2, eml_data);
}
QByteArray body_data = eml_data.toLatin1();
eml_data.clear();
vmime::shared_ptr<vmime::message> signed_message;
bool r = CheckIfEMLMessage(body_data, signed_message);
if (!r) {
CB_ERR(event, -1, "Parse Signed Message Failed");
}
QString encr_capsule_id;
ret = EncryptEMLData(channel, encrypt_keys, signed_message, body_data,
eml_data, encr_capsule_id);
if (ret != 0) {
CB_ERR(event, -2, eml_data);
}
CB(event, GFGetModuleID(),
{
{"ret", QString::number(0)},
{"eml_data", eml_data},
{"sign_capsule_id", sign_capsule_id},
{"encr_capsule_id", encr_capsule_id},
});
return 0;
}
auto* dialog = GUI_OBJECT(CreateEMailMetaDataDialog, 1); auto* dialog = GUI_OBJECT(CreateEMailMetaDataDialog, 1);
auto* r_dialog = auto* r_dialog =
qobject_cast<EMailMetaDataDialog*>(static_cast<QObject*>(dialog)); qobject_cast<EMailMetaDataDialog*>(static_cast<QObject*>(dialog));
@ -590,28 +360,36 @@ REGISTER_EVENT_HANDLER(
GFUIShowDialog(dialog, nullptr); GFUIShowDialog(dialog, nullptr);
QObject::connect(r_dialog, &EMailMetaDataDialog::SignalEMLMetaData, QObject::connect(
r_dialog, [=](const EMailMetaData& meta_data) { r_dialog, &EMailMetaDataDialog::SignalEMLMetaData, r_dialog,
QString eml_data; [=](const EMailMetaData& meta_data) {
auto ret = SignEMLData(channel, sign_key, meta_data, QString eml_data;
body_data, eml_data); QString sign_capsule_id;
if (ret != 0) { QString encr_capsule_id;
CB_ERR(event, -2, eml_data); auto ret = SignPlainText(channel, sign_key, meta_data, body_data,
} eml_data, sign_capsule_id);
if (ret != 0) {
CB_ERR(event, -2, eml_data);
}
ret = EncryptEMLData(channel, encrypt_keys, meta_data, QByteArray body_data = eml_data.toLatin1();
body_data, eml_data); eml_data.clear();
if (ret != 0) {
CB_ERR(event, -2, eml_data);
}
CB(event, GFGetModuleID(), ret = EncryptPlainText(channel, encrypt_keys, meta_data, body_data,
{ eml_data, encr_capsule_id);
{"ret", QString::number(0)}, if (ret != 0) {
{"eml_data", eml_data}, CB_ERR(event, -2, eml_data);
}); }
return 0;
}); CB(event, GFGetModuleID(),
{
{"ret", QString::number(0)},
{"eml_data", eml_data},
{"sign_capsule_id", sign_capsule_id},
{"encr_capsule_id", encr_capsule_id},
});
return 0;
});
QObject::connect(r_dialog, &EMailMetaDataDialog::SignalNoEMLMetaData, QObject::connect(r_dialog, &EMailMetaDataDialog::SignalNoEMLMetaData,
r_dialog, [=](const QString& error_string) { r_dialog, [=](const QString& error_string) {
@ -621,6 +399,43 @@ REGISTER_EVENT_HANDLER(
return 0; return 0;
}); });
REGISTER_EVENT_HANDLER(
EMAIL_DECRYPT_VERIFY_EML_DATA, [](const MEvent& event) -> int {
if (event["channel"].isEmpty()) CB_ERR(event, -1, "channel is empty");
if (event["eml_data"].isEmpty()) CB_ERR(event, -1, "eml_data is empty");
auto channel = event.value("channel", "0").toInt();
auto data = QByteArray::fromBase64(QString(event["eml_data"]).toLatin1());
auto body_data =
QByteArray::fromBase64(QString(event["body_data"]).toLatin1());
QString eml_data;
QString decr_capsule_id;
EMailMetaData meta_data;
auto ret =
DecryptEMLData(channel, data, meta_data, eml_data, decr_capsule_id);
if (ret != 0) {
CB_ERR(event, -2, eml_data);
}
QString verify_capsule_id;
ret = VerifyEMLData(channel, eml_data.toLatin1(), meta_data, eml_data,
verify_capsule_id);
if (ret != 0) {
CB_ERR(event, -2, eml_data);
}
CB(event, GFGetModuleID(),
{
{"ret", QString::number(0)},
{"eml_data", eml_data},
{"decr_capsule_id", decr_capsule_id},
{"verify_capsule_id", verify_capsule_id},
});
return 0;
});
auto GFDeactivateModule() -> int { return 0; } auto GFDeactivateModule() -> int { return 0; }
auto GFUnregisterModule() -> int { auto GFUnregisterModule() -> int {