mirror of https://github.com/djcb/mu.git
message: add basic support for encrypted parts
This commit is contained in:
parent
72c0f82b41
commit
de8dd048e8
|
@ -143,3 +143,9 @@ MessagePart::is_signed() const noexcept
|
|||
{
|
||||
return mime_object().is_multipart_signed();
|
||||
}
|
||||
|
||||
bool
|
||||
MessagePart::is_encrypted() const noexcept
|
||||
{
|
||||
return mime_object().is_multipart_encrypted();
|
||||
}
|
||||
|
|
|
@ -111,6 +111,15 @@ public:
|
|||
*/
|
||||
bool is_signed() const noexcept;
|
||||
|
||||
|
||||
/**
|
||||
* Is this part encrypted?
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
bool is_encrypted() const noexcept;
|
||||
|
||||
|
||||
/**
|
||||
* Write (decoded) mime-part contents to string
|
||||
*
|
||||
|
|
|
@ -170,7 +170,7 @@ MimeCryptoContext::setup_gpg_test(const std::string& testpath)
|
|||
g_unsetenv ("DISPLAY");
|
||||
g_unsetenv ("GPG_TTY");
|
||||
|
||||
if (g_mkdir_with_parents((testpath + "/.gnupg").c_str(), 700) != 0)
|
||||
if (g_mkdir_with_parents((testpath + "/.gnupg").c_str(), 0700) != 0)
|
||||
return Err(Error::Code::File,
|
||||
"failed to create gnupg dir; err=%d", errno);
|
||||
|
||||
|
@ -461,7 +461,6 @@ MimeMultipartSigned::verify(VerifyFlags vflags) const noexcept
|
|||
std::vector<MimeSignature> sigs;
|
||||
for (auto i = 0; i != g_mime_signature_list_length(siglist); ++i) {
|
||||
GMimeSignature *sig = g_mime_signature_list_get_signature(siglist, i);
|
||||
g_object_ref(sig);
|
||||
sigs.emplace_back(MimeSignature(sig));
|
||||
}
|
||||
|
||||
|
@ -470,3 +469,160 @@ MimeMultipartSigned::verify(VerifyFlags vflags) const noexcept
|
|||
return sigs;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* we need to be able to pass a crypto-context to the verify(), but
|
||||
* g_mime_multipart_signed_verify() doesn't offer that anymore in GMime 3.x.
|
||||
*
|
||||
* So, add that by reimplementing it a bit (follow the upstream impl)
|
||||
*/
|
||||
|
||||
|
||||
static bool
|
||||
mime_types_equal (const std::string& mime_type, const std::string& official_type)
|
||||
{
|
||||
if (g_ascii_strcasecmp(mime_type.c_str(), official_type.c_str()))
|
||||
return true;
|
||||
|
||||
const auto slash_pos = official_type.find("/");
|
||||
if (slash_pos == std::string::npos || slash_pos == 0)
|
||||
return false;
|
||||
|
||||
/* If the official mime-type's subtype already begins with "x-", then there's
|
||||
* nothing else to check. */
|
||||
const auto subtype{official_type.substr(slash_pos + 1)};
|
||||
if (g_ascii_strncasecmp (subtype.c_str(), "x-", 2) == 0)
|
||||
return false;
|
||||
const auto supertype{official_type.substr(0, slash_pos - 1)};
|
||||
const auto xtype{official_type.substr(0, slash_pos - 1) + "x-" + subtype};
|
||||
|
||||
/* Check if the "x-" version of the official mime-type matches the
|
||||
* supplied mime-type. For example, if the official mime-type is
|
||||
* "application/pkcs7-signature", then we also want to match
|
||||
* "application/x-pkcs7-signature". */
|
||||
return g_ascii_strcasecmp(mime_type.c_str(), xtype.c_str()) == 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Result<std::vector<MimeSignature>>
|
||||
// MimeMultipartSigned::verify(VerifyFlags vflags, const CryptoContext& ctx) const noexcept
|
||||
// {
|
||||
// if (g_mime_multipart_get_count(self()) < 2)
|
||||
// return Err(Error::Code::Crypto, "cannot verify, not enough subparts");
|
||||
|
||||
// const auto proto{content_type_parameter("protocol")};
|
||||
// const auto sign_proto{ctx.signature_prototol().value_or("<unknown>")};
|
||||
|
||||
// if (!proto || !sign_proto || !mime_types_equal(*proto, sign_proto))
|
||||
// return Err(Error::Code::Crypto, "unsupported protocol");
|
||||
|
||||
// const auto sig{MimeObject(g_mime_multipart_get_part(self(), GMIME_MULTIPART_SIGNED_SIGNATURE))};
|
||||
// if (!sig || !mime_types_equal(sig.mime_type().value_or("<none>"), sign_proto))
|
||||
// return Err(Error::Code::Crypto, "failed to find matching signature part");
|
||||
|
||||
// MimeObject content{g_mime_multipart_get_part(self(), GMIME_MULTIPART_SIGNED_CONTENT)};
|
||||
// if (!content)
|
||||
// return Err(Error::Code::Crypto, "cannot find content part");
|
||||
|
||||
// MimeFormatOptions fopts{format_options_new()};
|
||||
// g_mime_format_options_set_newline_format(*fopts, GMIME_NEWLINE_FORMAT_DOS);
|
||||
|
||||
// MimeStream stream{g_mime_stream_mem_new()};
|
||||
// g_mime_object_write_to_stream (content, *fopts, stream);
|
||||
// g_mime_stream_reset (stream);
|
||||
|
||||
// GMimeDataWrapper *wrapper = g_mime_part_get_content(static_cast<GMimePart*>(sig));
|
||||
|
||||
|
||||
|
||||
|
||||
// GError *err{};
|
||||
// GMimeSignatureList *siglist = g_mime_multipart_signed_verify(
|
||||
// self(),
|
||||
// static_cast<GMimeVerifyFlags>(vflags),
|
||||
// &err);
|
||||
|
||||
// if (!siglist)
|
||||
// return Err(Error::Code::Crypto, &err, "failed to verify");
|
||||
|
||||
// std::vector<MimeSignature> sigs;
|
||||
// for (auto i = 0; i != g_mime_signature_list_length(siglist); ++i) {
|
||||
// GMimeSignature *sig = g_mime_signature_list_get_signature(siglist, i);
|
||||
// sigs.emplace_back(MimeSignature(sig));
|
||||
// }
|
||||
|
||||
// g_object_unref(siglist);
|
||||
|
||||
// return sigs;
|
||||
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
std::vector<MimeCertificate>
|
||||
MimeDecryptResult::recipients() const noexcept
|
||||
{
|
||||
GMimeCertificateList *lst{g_mime_decrypt_result_get_recipients(self())};
|
||||
if (!lst)
|
||||
return {};
|
||||
|
||||
std::vector<MimeCertificate> certs;
|
||||
for (int i = 0; i != g_mime_certificate_list_length(lst); ++i)
|
||||
certs.emplace_back(
|
||||
MimeCertificate(
|
||||
g_mime_certificate_list_get_certificate(lst, i)));
|
||||
|
||||
return certs;
|
||||
}
|
||||
|
||||
std::vector<MimeSignature>
|
||||
MimeDecryptResult::signatures() const noexcept
|
||||
{
|
||||
GMimeSignatureList *lst{g_mime_decrypt_result_get_signatures(self())};
|
||||
if (!lst)
|
||||
return {};
|
||||
|
||||
std::vector<MimeSignature> sigs;
|
||||
for (auto i = 0; i != g_mime_signature_list_length(lst); ++i) {
|
||||
GMimeSignature *sig = g_mime_signature_list_get_signature(lst, i);
|
||||
sigs.emplace_back(MimeSignature(sig));
|
||||
}
|
||||
|
||||
return sigs;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Mu::Result<MimeMultipartEncrypted::Decrypted>
|
||||
MimeMultipartEncrypted::decrypt(DecryptFlags flags,
|
||||
const std::string& session_key) const noexcept
|
||||
{
|
||||
GError *err{};
|
||||
GMimeDecryptResult *dres{};
|
||||
GMimeObject *obj = g_mime_multipart_encrypted_decrypt(
|
||||
self(),
|
||||
static_cast<GMimeDecryptFlags>(flags),
|
||||
session_key.empty() ? NULL : session_key.c_str(),
|
||||
&dres,
|
||||
&err);
|
||||
|
||||
if (!obj)
|
||||
return Err(Error::Code::Crypto, &err, "failed to decrypt");
|
||||
|
||||
return Ok(Decrypted{MimeObject{obj}, MimeDecryptResult(dres)});
|
||||
}
|
||||
|
|
|
@ -101,8 +101,7 @@ public:
|
|||
*/
|
||||
Object& operator=(Object&& other) noexcept {
|
||||
|
||||
if (this != &other) {
|
||||
auto oldself = self_;
|
||||
if (this != &other) { auto oldself = self_;
|
||||
self_ = other.self_;
|
||||
other.self_ = nullptr;
|
||||
if (oldself)
|
||||
|
@ -130,13 +129,6 @@ public:
|
|||
protected:
|
||||
GObject* object() const { return self(); }
|
||||
|
||||
static Option<std::string> maybe_string(const char *str) noexcept {
|
||||
if (!str)
|
||||
return Nothing;
|
||||
else
|
||||
return std::string(str);
|
||||
}
|
||||
|
||||
private:
|
||||
GObject *self() const { return self_; }
|
||||
mutable GObject *self_{};
|
||||
|
@ -153,12 +145,17 @@ struct MimeContentType: public Object {
|
|||
if (!GMIME_IS_CONTENT_TYPE(self()))
|
||||
throw std::runtime_error("not a content-type");
|
||||
}
|
||||
std::string media_type() const {
|
||||
std::string media_type() const noexcept {
|
||||
return g_mime_content_type_get_media_type(self());
|
||||
}
|
||||
std::string media_subtype() const {
|
||||
std::string media_subtype() const noexcept {
|
||||
return g_mime_content_type_get_media_subtype(self());
|
||||
}
|
||||
|
||||
Option<std::string> mime_type() const noexcept {
|
||||
return to_option_string(g_mime_content_type_get_mime_type(self()));
|
||||
}
|
||||
|
||||
bool is_type(const std::string& type, const std::string& subtype) const {
|
||||
return g_mime_content_type_is_type(self(), type.c_str(),
|
||||
subtype.c_str());
|
||||
|
@ -170,7 +167,6 @@ private:
|
|||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Thin wrapper around a GMimeStream
|
||||
|
@ -186,6 +182,11 @@ struct MimeStream: public Object {
|
|||
return g_mime_stream_write(self(), buf, size);
|
||||
}
|
||||
|
||||
bool reset() {
|
||||
return g_mime_stream_reset(self()) < 0 ? false : true;
|
||||
}
|
||||
|
||||
|
||||
GMimeStream* self() const {
|
||||
return reinterpret_cast<GMimeStream*>(object());
|
||||
}
|
||||
|
@ -282,27 +283,27 @@ struct MimeCertificate: public Object {
|
|||
}
|
||||
|
||||
Option<std::string> issuer_serial() const {
|
||||
return maybe_string(g_mime_certificate_get_issuer_serial(self()));
|
||||
return to_option_string(g_mime_certificate_get_issuer_serial(self()));
|
||||
}
|
||||
Option<std::string> issuer_name() const {
|
||||
return maybe_string(g_mime_certificate_get_issuer_name(self()));
|
||||
return to_option_string(g_mime_certificate_get_issuer_name(self()));
|
||||
}
|
||||
|
||||
Option<std::string> fingerprint() const {
|
||||
return maybe_string(g_mime_certificate_get_fingerprint(self()));
|
||||
return to_option_string(g_mime_certificate_get_fingerprint(self()));
|
||||
}
|
||||
|
||||
Option<std::string> key_id() const {
|
||||
return maybe_string(g_mime_certificate_get_key_id(self()));
|
||||
return to_option_string(g_mime_certificate_get_key_id(self()));
|
||||
}
|
||||
|
||||
|
||||
Option<std::string> name() const {
|
||||
return maybe_string(g_mime_certificate_get_name(self()));
|
||||
return to_option_string(g_mime_certificate_get_name(self()));
|
||||
}
|
||||
|
||||
Option<std::string> user_id() const {
|
||||
return maybe_string(g_mime_certificate_get_user_id(self()));
|
||||
return to_option_string(g_mime_certificate_get_user_id(self()));
|
||||
}
|
||||
|
||||
Option<::time_t> created() const {
|
||||
|
@ -477,6 +478,78 @@ static inline std::string to_string(MimeSignature::Status status) {
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Thin wrapper around a GMimeDecryptResult
|
||||
*
|
||||
*/
|
||||
struct MimeDecryptResult: public Object {
|
||||
MimeDecryptResult (GMimeDecryptResult *decres) : Object{G_OBJECT(decres)} {
|
||||
if (!GMIME_IS_DECRYPT_RESULT(self()))
|
||||
throw std::runtime_error("not a decrypt-result");
|
||||
}
|
||||
|
||||
std::vector<MimeCertificate> recipients() const noexcept;
|
||||
std::vector<MimeSignature> signatures() const noexcept;
|
||||
|
||||
enum struct CipherAlgo {
|
||||
Default = GMIME_CIPHER_ALGO_DEFAULT,
|
||||
Idea = GMIME_CIPHER_ALGO_IDEA,
|
||||
Des3 = GMIME_CIPHER_ALGO_3DES,
|
||||
Cast5 = GMIME_CIPHER_ALGO_CAST5,
|
||||
Blowfish = GMIME_CIPHER_ALGO_BLOWFISH,
|
||||
Aes = GMIME_CIPHER_ALGO_AES,
|
||||
Aes192 = GMIME_CIPHER_ALGO_AES192,
|
||||
Aes256 = GMIME_CIPHER_ALGO_AES256,
|
||||
TwoFish = GMIME_CIPHER_ALGO_TWOFISH,
|
||||
Camellia128 = GMIME_CIPHER_ALGO_CAMELLIA128,
|
||||
Camellia192 = GMIME_CIPHER_ALGO_CAMELLIA192,
|
||||
Camellia256 = GMIME_CIPHER_ALGO_CAMELLIA256
|
||||
};
|
||||
|
||||
CipherAlgo cipher() const noexcept {
|
||||
return static_cast<CipherAlgo>(
|
||||
g_mime_decrypt_result_get_cipher(self()));
|
||||
}
|
||||
|
||||
using DigestAlgo = MimeCertificate::DigestAlgo;
|
||||
DigestAlgo mdc() const noexcept {
|
||||
return static_cast<DigestAlgo>(
|
||||
g_mime_decrypt_result_get_mdc(self()));
|
||||
}
|
||||
|
||||
Option<std::string> session_key() const noexcept {
|
||||
return to_option_string(g_mime_decrypt_result_get_session_key(self()));
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
GMimeDecryptResult* self() const {
|
||||
return reinterpret_cast<GMimeDecryptResult*>(object());
|
||||
}
|
||||
};
|
||||
|
||||
constexpr std::array<std::pair<MimeDecryptResult::CipherAlgo, std::string_view>, 12>
|
||||
AllCipherAlgos= {{
|
||||
{ MimeDecryptResult::CipherAlgo::Default , "default"},
|
||||
{ MimeDecryptResult::CipherAlgo::Idea , "idea"},
|
||||
{ MimeDecryptResult::CipherAlgo::Des3 , "3des"},
|
||||
{ MimeDecryptResult::CipherAlgo::Cast5 , "cast5"},
|
||||
{ MimeDecryptResult::CipherAlgo::Blowfish , "blowfish"},
|
||||
{ MimeDecryptResult::CipherAlgo::Aes , "aes"},
|
||||
{ MimeDecryptResult::CipherAlgo::Aes192 , "aes192"},
|
||||
{ MimeDecryptResult::CipherAlgo::Aes256 , "aes256"},
|
||||
{ MimeDecryptResult::CipherAlgo::TwoFish , "twofish"},
|
||||
{ MimeDecryptResult::CipherAlgo::Camellia128, "camellia128"},
|
||||
{ MimeDecryptResult::CipherAlgo::Camellia192, "camellia192"},
|
||||
{ MimeDecryptResult::CipherAlgo::Camellia256, "camellia256"},
|
||||
}};
|
||||
|
||||
constexpr Option<std::string_view> to_string_view(MimeDecryptResult::CipherAlgo algo) {
|
||||
return to_string_view(AllCipherAlgos, algo);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Thin wrapper around a GMimeCryptoContext
|
||||
|
@ -506,6 +579,17 @@ struct MimeCryptoContext : public Object {
|
|||
return Err(Error::Code::Crypto, "failed to create crypto context");
|
||||
}
|
||||
|
||||
|
||||
Option<std::string> encryption_protocol() {
|
||||
return to_option_string(g_mime_crypto_context_get_encryption_protocol(self()));
|
||||
}
|
||||
Option<std::string> signature_protocol() {
|
||||
return to_option_string(g_mime_crypto_context_get_signature_protocol(self()));
|
||||
}
|
||||
Option<std::string> key_exchange_protocol() {
|
||||
return to_option_string(g_mime_crypto_context_get_key_exchange_protocol(self()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports a stream of keys/certificates contained within stream into
|
||||
* the key/certificate database controlled by @this.
|
||||
|
@ -574,7 +658,7 @@ public:
|
|||
throw std::runtime_error("not a mime-object");
|
||||
}
|
||||
MimeObject(GMimeObject *mobj): Object{G_OBJECT(mobj)} {
|
||||
if (!GMIME_IS_OBJECT(self()))
|
||||
if (mobj && !GMIME_IS_OBJECT(self()))
|
||||
throw std::runtime_error("not a mime-object");
|
||||
}
|
||||
|
||||
|
@ -600,6 +684,24 @@ public:
|
|||
return MimeContentType(ct);
|
||||
}
|
||||
|
||||
Option<std::string> content_type_parameter(const std::string& param) const noexcept {
|
||||
return to_option_string(
|
||||
g_mime_object_get_content_type_parameter(self(), param.c_str()));
|
||||
}
|
||||
|
||||
|
||||
using MimeFormatOptions =
|
||||
deletable_unique_ptr<GMimeFormatOptions, g_mime_format_options_free>;
|
||||
|
||||
|
||||
Option<size_t> write_to_stream(const MimeFormatOptions& f_opts,
|
||||
MimeStream& stream) {
|
||||
auto written = g_mime_object_write_to_stream(self(), f_opts.get(), stream.self());
|
||||
if (written < 0)
|
||||
return Nothing;
|
||||
else
|
||||
return static_cast<size_t>(written);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the object to a string.
|
||||
|
@ -730,7 +832,7 @@ public:
|
|||
* @return string or nullopt
|
||||
*/
|
||||
Option<std::string> message_id() const noexcept {
|
||||
return maybe_string(g_mime_message_get_message_id(self()));
|
||||
return to_option_string(g_mime_message_get_message_id(self()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -739,7 +841,7 @@ public:
|
|||
* @return string or nullopt
|
||||
*/
|
||||
Option<std::string> subject() const noexcept {
|
||||
return maybe_string(g_mime_message_get_subject(self()));
|
||||
return to_option_string(g_mime_message_get_subject(self()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -764,7 +866,7 @@ public:
|
|||
*
|
||||
*/
|
||||
using ForEachFunc = std::function<void(const MimeObject& parent,
|
||||
const MimeObject& part)>;
|
||||
const MimeObject& part)>;
|
||||
|
||||
/**
|
||||
* Recursively apply func tol all parts of this message
|
||||
|
@ -812,7 +914,7 @@ public:
|
|||
* @return string or nullopt
|
||||
*/
|
||||
Option<std::string> content_description() const noexcept {
|
||||
return maybe_string(g_mime_part_get_content_description(self()));
|
||||
return to_option_string(g_mime_part_get_content_description(self()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -822,7 +924,7 @@ public:
|
|||
* @return string or nullopt
|
||||
*/
|
||||
Option<std::string> content_id() const noexcept {
|
||||
return maybe_string(g_mime_part_get_content_id(self()));
|
||||
return to_option_string(g_mime_part_get_content_id(self()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -832,7 +934,7 @@ public:
|
|||
* @return string or nullopt
|
||||
*/
|
||||
Option<std::string> content_md5() const noexcept {
|
||||
return maybe_string(g_mime_part_get_content_md5(self()));
|
||||
return to_option_string(g_mime_part_get_content_md5(self()));
|
||||
|
||||
}
|
||||
|
||||
|
@ -853,7 +955,7 @@ public:
|
|||
* @return string or nullopt
|
||||
*/
|
||||
Option<std::string> content_location() const noexcept {
|
||||
return maybe_string(g_mime_part_get_content_location(self()));
|
||||
return to_option_string(g_mime_part_get_content_location(self()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -863,7 +965,7 @@ public:
|
|||
* @return string or nullopt
|
||||
*/
|
||||
Option<std::string> filename() const noexcept {
|
||||
return maybe_string(g_mime_part_get_filename(self()));
|
||||
return to_option_string(g_mime_part_get_filename(self()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1058,12 +1160,30 @@ public:
|
|||
throw std::runtime_error("not a mime-multipart-encrypted");
|
||||
}
|
||||
|
||||
enum struct DecryptFlags {
|
||||
None = GMIME_DECRYPT_NONE,
|
||||
ExportSessionKey = GMIME_DECRYPT_EXPORT_SESSION_KEY,
|
||||
NoVerify = GMIME_DECRYPT_NO_VERIFY,
|
||||
EnableKeyserverLookups = GMIME_DECRYPT_ENABLE_KEYSERVER_LOOKUPS,
|
||||
EnableOnlineCertificateChecks = GMIME_DECRYPT_ENABLE_ONLINE_CERTIFICATE_CHECKS
|
||||
};
|
||||
|
||||
struct Decrypted {
|
||||
MimeObject decrypted_object;
|
||||
MimeDecryptResult decrypte_result;
|
||||
};
|
||||
|
||||
Result<Decrypted> decrypt(DecryptFlags flags=DecryptFlags::None,
|
||||
const std::string& session_key = {}) const noexcept;
|
||||
|
||||
private:
|
||||
GMimeMultipartEncrypted* self() const {
|
||||
return reinterpret_cast<GMimeMultipartEncrypted*>(object());
|
||||
}
|
||||
};
|
||||
|
||||
MU_ENABLE_BITOPS(MimeMultipartEncrypted::DecryptFlags);
|
||||
|
||||
|
||||
/**
|
||||
* Thin wrapper around a GMimeMultiPartSigned
|
||||
|
@ -1096,6 +1216,9 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
MU_ENABLE_BITOPS(MimeMultipartSigned::VerifyFlags);
|
||||
|
||||
} // namespace Mu
|
||||
|
||||
|
||||
|
|
|
@ -247,48 +247,50 @@ World!
|
|||
constexpr std::string_view pub_key =
|
||||
R"(-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mDMEYkycYxYJKwYBBAHaRw8BAQdAiE6rRtXjh1u8ZNVB02k1d3divvp0qrifJSe2
|
||||
/vcCLDm0HE11IFRlc3QgPG11QGRqY2Jzb2Z0d2FyZS5ubD6IlAQTFgoAPAIbAwUL
|
||||
CQgHAgMiAgEGFQoJCAsCBBYCAwECHgcCF4AWIQT0f3WQZJn/X54Jqiz74qC0xbNs
|
||||
qgUCYkysVAAKCRD74qC0xbNsquJ0AP95o557wp8GoCn4Fn6RA4mX8QOVv9lGrGRu
|
||||
D42pFNyFmgD+LinWJR973YEGaGSamGwjc0vKY8nXiuY21noN+RMBYQm4OARiTJxj
|
||||
EgorBgEEAZdVAQUBAQdAdfnXbQAAgQ/2zDKh1kn0EeTZnzEgC0y+VCz+VQOhXDMD
|
||||
AQgHiHgEGBYKACACGwwWIQT0f3WQZJn/X54Jqiz74qC0xbNsqgUCYkysvgAKCRD7
|
||||
4qC0xbNsqsQOAPkBi4cDuf0Yk6PmDb10ARuL4E8plQTO8Ehqp/+O5JeIFQD/f3mi
|
||||
KTUVweCNFi/1aZ/ViQ4umui3RTmCi+M91A7bRQg=
|
||||
=3Xa7
|
||||
mDMEYlbaNhYJKwYBBAHaRw8BAQdAEgxZnlN3mIwqV89zchjFlEby8OgrbrkT+yRN
|
||||
hQhc+A+0LU11IFRlc3QgKG11IHRlc3Rpbmcga2V5KSA8bXVAZGpjYnNvZnR3YXJl
|
||||
Lm5sPoiUBBMWCgA8FiEE/HZRT+2bPjARz29Cw7FsU49t3vAFAmJW2jYCGwMFCwkI
|
||||
BwIDIgIBBhUKCQgLAgQWAgMBAh4HAheAAAoJEMOxbFOPbd7wJ2kBAIGmUDWYEPtn
|
||||
qYTwhZIdZtTa4KJ3UdtTqey9AnxJ9mzAAQDRJOoVppj5wW2xRhgYP+ysN2iBUYGE
|
||||
MhahOcNgxodbCLg4BGJW2jYSCisGAQQBl1UBBQEBB0D4Sp+GTVre7Cx5a8D3SwLJ
|
||||
/bRAVGDwqI7PL9B/cMmCTwMBCAeIeAQYFgoAIBYhBPx2UU/tmz4wEc9vQsOxbFOP
|
||||
bd7wBQJiVto2AhsMAAoJEMOxbFOPbd7w1tYA+wdfYCcwOP0QoNZZz2Yk12YkDk2R
|
||||
FsRrZZpb0GKC/a2VAP4qFceeSegcUCBTQaoeFE9vq9XiUVOO98QI8r9C8QwvBw==
|
||||
=jM/g
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
)";
|
||||
|
||||
constexpr std::string_view priv_key =
|
||||
constexpr std::string_view priv_key = // "test1234"
|
||||
R"(-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||
|
||||
lIYEYkycYxYJKwYBBAHaRw8BAQdAiE6rRtXjh1u8ZNVB02k1d3divvp0qrifJSe2
|
||||
/vcCLDn+BwMCMFZr+icelQr/nHyufC4ON2PZG1WTURyap1CAXvV8Jgg8KAtG2olp
|
||||
Ftp22lSko5JL791GuWe5SnQaIT2I0FNVYPJiuwtcoLxT6vCutam4GLQcTXUgVGVz
|
||||
dCA8bXVAZGpjYnNvZnR3YXJlLm5sPoiUBBMWCgA8AhsDBQsJCAcCAyICAQYVCgkI
|
||||
CwIEFgIDAQIeBwIXgBYhBPR/dZBkmf9fngmqLPvioLTFs2yqBQJiTKxUAAoJEPvi
|
||||
oLTFs2yq4nQA/3mjnnvCnwagKfgWfpEDiZfxA5W/2UasZG4PjakU3IWaAP4uKdYl
|
||||
H3vdgQZoZJqYbCNzS8pjydeK5jbWeg35EwFhCZyLBGJMnGMSCisGAQQBl1UBBQEB
|
||||
B0B1+ddtAACBD/bMMqHWSfQR5NmfMSALTL5ULP5VA6FcMwMBCAf+BwMCEaDNUrOs
|
||||
FLX/HOOPlvFb4zh7IkWYnpCRX1HEWheJIlhYAtzS/EU+Ebc11ricUleyM3mKIeYb
|
||||
st5PE8NNcm40ep3RtBwNNMt/TGht4/iLfIh4BBgWCgAgAhsMFiEE9H91kGSZ/1+e
|
||||
Caos++KgtMWzbKoFAmJMrL4ACgkQ++KgtMWzbKrEDgD5AYuHA7n9GJOj5g29dAEb
|
||||
i+BPKZUEzvBIaqf/juSXiBUA/395oik1FcHgjRYv9Wmf1YkOLprot0U5govjPdQO
|
||||
20UI
|
||||
=hlnL
|
||||
lIYEYlbaNhYJKwYBBAHaRw8BAQdAEgxZnlN3mIwqV89zchjFlEby8OgrbrkT+yRN
|
||||
hQhc+A/+BwMCz6T2uBpk6a7/rXyE7C1bRbGjP6YSFcyRFz8VRV3Xlm7z6rdbdKZr
|
||||
8R15AtLvXA4DOK5GiZRB2VbIxi8B9CtZ9qQx6YbQPkAmRzISGAjECrQtTXUgVGVz
|
||||
dCAobXUgdGVzdGluZyBrZXkpIDxtdUBkamNic29mdHdhcmUubmw+iJQEExYKADwW
|
||||
IQT8dlFP7Zs+MBHPb0LDsWxTj23e8AUCYlbaNgIbAwULCQgHAgMiAgEGFQoJCAsC
|
||||
BBYCAwECHgcCF4AACgkQw7FsU49t3vAnaQEAgaZQNZgQ+2ephPCFkh1m1NrgondR
|
||||
21Op7L0CfEn2bMABANEk6hWmmPnBbbFGGBg/7Kw3aIFRgYQyFqE5w2DGh1sInIsE
|
||||
YlbaNhIKKwYBBAGXVQEFAQEHQPhKn4ZNWt7sLHlrwPdLAsn9tEBUYPCojs8v0H9w
|
||||
yYJPAwEIB/4HAwI9MZDWcsoiJ/9oV5DRiAedeo3Ta/1M+aKfeNV36Ch1VGLwQF3E
|
||||
V77qIrJlsT8CwOZHWUksUBENvG3ak3vd84awHHaHoTmoFwtISfvQrFK0iHgEGBYK
|
||||
ACAWIQT8dlFP7Zs+MBHPb0LDsWxTj23e8AUCYlbaNgIbDAAKCRDDsWxTj23e8NbW
|
||||
APsHX2AnMDj9EKDWWc9mJNdmJA5NkRbEa2WaW9Bigv2tlQD+KhXHnknoHFAgU0Gq
|
||||
HhRPb6vV4lFTjvfECPK/QvEMLwc=
|
||||
=w1Nc
|
||||
-----END PGP PRIVATE KEY BLOCK-----
|
||||
)";
|
||||
|
||||
|
||||
static void
|
||||
test_message_signed(void)
|
||||
{
|
||||
constexpr const char *msgtext =
|
||||
R"(From: Mu Test <mu@djcbsoftware.nl>
|
||||
To: boo@example.com
|
||||
Subject: object
|
||||
Date: Thu, 07 Apr 2022 00:04:26 +0300
|
||||
Message-ID: <87bkxdyl8i.fsf@djcbsoftware.nl>
|
||||
R"(Return-Path: <diggler@gmail.com>
|
||||
From: Mu Test <mu@djcbsoftware.nl>
|
||||
To: Mu Test <mu@djcbsoftware.nl>
|
||||
Subject: boo
|
||||
Date: Wed, 13 Apr 2022 17:19:08 +0300
|
||||
Message-ID: <878rs9ysin.fsf@djcbsoftware.nl>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: multipart/signed; boundary="=-=-=";
|
||||
micalg=pgp-sha512; protocol="application/pgp-signature"
|
||||
|
@ -303,13 +305,24 @@ Content-Type: application/pgp-signature; name="signature.asc"
|
|||
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
iIkEARYKADEWIQT0f3WQZJn/X54Jqiz74qC0xbNsqgUCYk4BBRMcbXVAZGpjYnNv
|
||||
ZnR3YXJlLm5sAAoJEPvioLTFs2yqhuwBANzT0Lrex/1ohZ5t3GrAfykkbZPZUHDW
|
||||
1fhWrQ9GIP+8AQCqlgXEteQjQC0VLPNuV4Iz1wOq/e+Hn0KEBNr230v9AQ==
|
||||
=PeYV
|
||||
iIkEARYKADEWIQT8dlFP7Zs+MBHPb0LDsWxTj23e8AUCYlbcLhMcbXVAZGpjYnNv
|
||||
ZnR3YXJlLm5sAAoJEMOxbFOPbd7waIkA/jK1oY7OL8vrDoubNYxamy8HHmwtvO01
|
||||
Q46aYjxe0As6AP90bcAZ3dcn5RcTJaM0UhZssguawZ+tnriD3+5DPkMMCg==
|
||||
=e32+
|
||||
-----END PGP SIGNATURE-----
|
||||
--=-=-=--
|
||||
)";
|
||||
TempDir tempdir;
|
||||
auto ctx{MimeCryptoContext::make_gpg(tempdir.path())};
|
||||
g_assert_true(!!ctx);
|
||||
|
||||
MimeStream stream{g_mime_stream_mem_new()};
|
||||
stream.write(pub_key.data(), pub_key.size());
|
||||
stream.reset();
|
||||
|
||||
auto imported = ctx->import_keys(stream);
|
||||
g_assert_cmpuint(*imported, ==, 1);
|
||||
|
||||
auto message{Message::make_from_text(
|
||||
msgtext,
|
||||
"/home/test/Maildir/inbox/cur/1649279777.107710_1.mindcrime:2,RS",
|
||||
|
@ -331,13 +344,99 @@ ZnR3YXJlLm5sAAoJEPvioLTFs2yqhuwBANzT0Lrex/1ohZ5t3GrAfykkbZPZUHDW
|
|||
|
||||
const auto mpart{MimeMultipartSigned(mobj)};
|
||||
const auto sigs{mpart.verify()};
|
||||
|
||||
g_assert_true(!!sigs && sigs->size() == 1);
|
||||
|
||||
g_print("status: %s\n", to_string(sigs->at(0).status()).c_str());
|
||||
|
||||
++n;
|
||||
}
|
||||
|
||||
g_assert_cmpuint(n, ==, 1);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_message_signed_encrypted(void)
|
||||
{
|
||||
constexpr const char *msgtext =
|
||||
R"(From: "Mu Test" <mu@djcbsoftware.nl>
|
||||
To: mu@djcbsoftware.nl
|
||||
Subject: encrypted and signed
|
||||
Date: Wed, 13 Apr 2022 17:32:30 +0300
|
||||
Message-ID: <87lew9xddt.fsf@djcbsoftware.nl>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: multipart/encrypted; boundary="=-=-=";
|
||||
protocol="application/pgp-encrypted"
|
||||
|
||||
--=-=-=
|
||||
Content-Type: application/pgp-encrypted
|
||||
|
||||
Version: 1
|
||||
|
||||
--=-=-=
|
||||
Content-Type: application/octet-stream
|
||||
|
||||
-----BEGIN PGP MESSAGE-----
|
||||
|
||||
hF4DeEerj6WhdZASAQdAKdZwmugAlQA8c06Q5iQw4rwSADgfEWBTWlI6tDw7hEAw
|
||||
0qSSeeQbA802qjG5TesaDVbFoPp1gOESt67HkJBABj9niwZLnjbzVRXKFoPTYabu
|
||||
1MBWAQkCEO6kS0N73XQeJ9+nDkUacRX6sSgVM0j+nRdCGcrCQ8MOfLd9KUUBxpXy
|
||||
r/rIBMpZGOIpKJnoZ2x75VsQIp/ADHLe9zzXVe0tkahXJqvLo26w3gn4NSEIEDp6
|
||||
4T/zMZImqGrENaixNmRiRSAnwPkLt95qJGOIqYhuW3X6hMRZyU4zDNwkAvnK+2Fv
|
||||
Wjd+EmiFzh5tvCmPOSj556YFMV7UpFWO9VznXX/T5+f4i+95Lsm9Uotv/SiNtNQG
|
||||
DPU3wiL347SzmPFXckknjlzSzDL1XbdbHdmoJs0uNnbaZxRwhkuTYbLHdpBZrBgR
|
||||
C0bdoCx44QVU8HaZ2x91h3GoM/0q5bqM/rvCauwbokiJgAUrznecNPY=
|
||||
=Ado7
|
||||
-----END PGP MESSAGE-----
|
||||
--=-=-=--
|
||||
)";
|
||||
TempDir tempdir;
|
||||
auto ctx{MimeCryptoContext::make_gpg(tempdir.path())};
|
||||
g_assert_true(!!ctx);
|
||||
|
||||
/// test1234
|
||||
ctx->set_password_request_function(
|
||||
[](const MimeCryptoContext& ctx,
|
||||
const std::string& user_id,
|
||||
const std::string& prompt,
|
||||
bool reprompt,
|
||||
MimeStream& response)->Result<void> {
|
||||
|
||||
return Err(Error::Code::Internal, "boo");
|
||||
|
||||
g_warning("boo!");
|
||||
|
||||
return Ok();
|
||||
});
|
||||
|
||||
{
|
||||
MimeStream stream{g_mime_stream_mem_new()};
|
||||
stream.write(priv_key.data(), priv_key.size());
|
||||
stream.reset();
|
||||
g_assert_cmpint(ctx->import_keys(stream).value_or(-1),==,1);
|
||||
}
|
||||
|
||||
auto message{Message::make_from_text(
|
||||
msgtext,
|
||||
"/home/test/Maildir/inbox/cur/1649279888.107710_1.mindcrime:2,FS",
|
||||
"/archive")};
|
||||
g_assert_true(!!message);
|
||||
g_assert_true(message->flags() == (Flags::Encrypted|Flags::Seen|Flags::Flagged));
|
||||
|
||||
size_t n{};
|
||||
for (auto&& part: message->parts()) {
|
||||
|
||||
if (!part.is_encrypted())
|
||||
continue;
|
||||
|
||||
const auto& mobj{part.mime_object()};
|
||||
if (!mobj.is_multipart_encrypted())
|
||||
continue;
|
||||
|
||||
const auto mpart{MimeMultipartEncrypted(mobj)};
|
||||
const auto decres = mpart.decrypt();
|
||||
g_assert_true(!!decres);
|
||||
|
||||
++n;
|
||||
}
|
||||
|
||||
g_assert_cmpuint(n, ==, 1);
|
||||
}
|
||||
|
@ -355,6 +454,8 @@ main(int argc, char* argv[])
|
|||
test_message_attachments);
|
||||
g_test_add_func("/message/message/signed",
|
||||
test_message_signed);
|
||||
g_test_add_func("/message/message/signed-encrypted",
|
||||
test_message_signed_encrypted);
|
||||
|
||||
return g_test_run();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue