message: add basic support for encrypted parts

This commit is contained in:
Dirk-Jan C. Binnema 2022-04-10 13:18:55 +03:00
parent 72c0f82b41
commit de8dd048e8
5 changed files with 461 additions and 66 deletions

View File

@ -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();
}

View File

@ -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
*

View File

@ -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)});
}

View File

@ -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

View File

@ -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();
}