aboutsummaryrefslogtreecommitdiffstats
path: root/src/m_email/test/EMailCryptoSdkStubs.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/m_email/test/EMailCryptoSdkStubs.cpp')
-rw-r--r--src/m_email/test/EMailCryptoSdkStubs.cpp270
1 files changed, 200 insertions, 70 deletions
diff --git a/src/m_email/test/EMailCryptoSdkStubs.cpp b/src/m_email/test/EMailCryptoSdkStubs.cpp
index 0e322ef..2553b7f 100644
--- a/src/m_email/test/EMailCryptoSdkStubs.cpp
+++ b/src/m_email/test/EMailCryptoSdkStubs.cpp
@@ -41,7 +41,10 @@
#include "EMailCryptoRecorder.h"
//
+#include <GFSDKBuildInfo.h>
+#include <GFSDKContext.h>
#include <GFSDKGpg.h>
+#include <GFSDKHostApi.h>
#include <QByteArray>
#include <cstdint>
@@ -79,15 +82,15 @@ auto Get() -> Recording& { return g_recording; }
using crypto_recorder::g_recording;
-extern "C" {
+namespace {
-void* GFAllocateMemory(unsigned int n) {
+void* MemAllocNormal(unsigned int n) {
void* p = std::malloc(n);
NormalArena().insert(p);
return p;
}
-void GFFreeMemory(void* p) {
+void MemFreeNormal(void* p) {
if (p == nullptr) return;
if (NormalArena().erase(p) == 0) {
std::printf("[FAIL] GFFreeMemory on a pointer it does not own\n");
@@ -97,13 +100,13 @@ void GFFreeMemory(void* p) {
std::free(p);
}
-void* GFSecAllocateMemory(unsigned int n) {
+void* MemAllocSecure(unsigned int n) {
void* p = std::malloc(n);
SecureArena().insert(p);
return p;
}
-void GFSecFreeMemory(void* p) {
+void MemFreeSecure(void* p) {
if (p == nullptr) return;
if (SecureArena().erase(p) == 0) {
std::printf("[FAIL] GFSecFreeMemory on a pointer it does not own\n");
@@ -114,42 +117,29 @@ void GFSecFreeMemory(void* p) {
}
// Deliberately identical to the shipping implementation, truncation and all.
-char* GFModuleStrDup(const char* s) {
+char* StrDupNormal(const char* s) {
if (s == nullptr) return nullptr;
auto n = std::strlen(s) + 1;
- char* d = static_cast<char*>(GFAllocateMemory(n));
+ char* d = static_cast<char*>(MemAllocNormal(n));
std::memcpy(d, s, n);
return d;
}
-char* GFModuleSecStrDup(const char* s) {
+char* StrDupSecure(const char* s) {
if (s == nullptr) return nullptr;
auto n = std::strlen(s) + 1;
- char* d = static_cast<char*>(GFSecAllocateMemory(n));
+ char* d = static_cast<char*>(MemAllocSecure(n));
std::memcpy(d, s, n);
return d;
}
-void GFModuleLogTrace(const char*) {}
-void GFModuleLogDebug(const char*) {}
-void GFModuleLogInfo(const char*) {}
-void GFModuleLogWarn(const char*) {}
-void GFModuleLogError(const char*) {}
-void GFModuleLogAt(const char*, int, const char*, int, const char*,
- const char*) {}
-int GFModuleLogEnabled(const char*, int) { return 1; }
-
-char* GFAppActiveLocale() { return GFModuleStrDup("en_US"); }
+void LogWrite(GFHostContextRef, int, const char*, int, const char*,
+ const char*) {}
+auto LogEnabled(GFHostContextRef, int) -> int { return 1; }
-char* GFGpgPublicKey(int, const char* key_id, int) {
- // Borrowed, not consumed: the real entry point stopped freeing its
- // arguments, and a stub that still frees would report a bogus allocator
- // violation for every correct call.
- (void)key_id;
- return GFModuleStrDup(
- "-----BEGIN PGP PUBLIC KEY BLOCK-----\nstub\n"
- "-----END PGP PUBLIC KEY BLOCK-----\n");
-}
+auto AppText(GFHostContextRef) -> const char* { return "test"; }
+auto AppLocale(GFHostContextRef) -> char* { return StrDupNormal("en_US"); }
+auto AppZero(GFHostContextRef) -> int { return 0; }
// --- the four crypto entry points -------------------------------------------
//
@@ -161,7 +151,7 @@ namespace {
// Mirrors GFBytesDup on the host side: byte-exact, NUL-terminated one past the
// end, with the true length reported separately.
auto BytesOut(const QByteArray& b, size_t* size) -> char* {
- auto* d = static_cast<char*>(GFAllocateMemory(b.size() + 1));
+ auto* d = static_cast<char*>(MemAllocNormal(b.size() + 1));
std::memcpy(d, b.constData(), b.size());
d[b.size()] = '\0';
if (size != nullptr) *size = static_cast<size_t>(b.size());
@@ -169,12 +159,18 @@ auto BytesOut(const QByteArray& b, size_t* size) -> char* {
}
} // namespace
-int GFAnalyseVerifyResultInfoByCapsule(int, uint32_t, const char* capsule_id,
- const char** analyse, const char** cards,
- const char** info_json) {
+auto GpgAnalyse(GFHostContextRef, int, int, uint32_t, const char* capsule_id,
+ uint32_t, GFGpgAnalysis* out) -> int {
(void)capsule_id; // borrowed, like every SDK argument
- if (analyse != nullptr) *analyse = GFModuleStrDup("");
- if (cards != nullptr) *cards = GFModuleStrDup("[]");
+ if (out == nullptr) return -1;
+ out->struct_size = sizeof(GFGpgAnalysis);
+
+ // Nine entry points became one filling a struct, so the fake fills it.
+ const char** analyse = const_cast<const char**>(&out->report);
+ const char** cards = const_cast<const char**>(&out->cards);
+ const char** info_json = const_cast<const char**>(&out->info_json);
+ if (analyse != nullptr) *analyse = StrDupNormal("");
+ if (cards != nullptr) *cards = StrDupNormal("[]");
// One good signature, so ParseSignatureResults has something to walk --
// unless the test queued an answer for this call. Regions are analysed in
@@ -183,9 +179,9 @@ int GFAnalyseVerifyResultInfoByCapsule(int, uint32_t, const char* capsule_id,
auto& recording = crypto_recorder::Get();
if (!recording.verify_info_json.isEmpty()) {
const auto queued = recording.verify_info_json.takeFirst();
- *info_json = GFModuleStrDup(queued.constData());
+ *info_json = StrDupNormal(queued.constData());
} else {
- *info_json = GFModuleStrDup(
+ *info_json = StrDupNormal(
R"({"signatures":[{"status":"good","fingerprint":"DEADBEEF",)"
R"("hash_algo":"SHA256","uid":"Test <[email protected]>"}]})");
}
@@ -193,7 +189,7 @@ int GFAnalyseVerifyResultInfoByCapsule(int, uint32_t, const char* capsule_id,
return 0;
}
-} // extern "C"
+} // namespace
/* ===================================================================== *
* Handle-based SDK: buffers and results
@@ -282,7 +278,7 @@ auto KeyIdCount(const char* const* key_ids, size_t n) -> int {
extern "C" {
-GFBufferRef GFBufferNewFromBytes(const void* data, size_t size) {
+auto BufNew(GFHostContextRef, const void* data, size_t size) -> GFBufferRef {
if (data == nullptr && size != 0) return nullptr;
auto* b = new StubBuffer{
QByteArray(static_cast<const char*>(data), static_cast<int>(size))};
@@ -290,22 +286,34 @@ GFBufferRef GFBufferNewFromBytes(const void* data, size_t size) {
return reinterpret_cast<GFBufferRef>(b);
}
-const void* GFBufferData(GFBufferView buf) {
+auto GpgPublicKey(GFHostContextRef ctx, int, const char* key_id, int)
+ -> GFBufferRef {
+ // Borrowed, not consumed: the real entry point stopped freeing its
+ // arguments, and a stub that still frees would report a bogus allocator
+ // violation for every correct call.
+ (void)key_id;
+ static constexpr char kBlock[] =
+ "-----BEGIN PGP PUBLIC KEY BLOCK-----\nstub\n"
+ "-----END PGP PUBLIC KEY BLOCK-----\n";
+ return BufNew(ctx, kBlock, sizeof(kBlock) - 1);
+}
+
+auto BufData(GFHostContextRef, GFBufferView buf) -> const void* {
auto* impl = LiveBuf(buf);
return impl == nullptr ? nullptr : impl->bytes.constData();
}
-size_t GFBufferSize(GFBufferView buf) {
+auto BufSize(GFHostContextRef, GFBufferView buf) -> size_t {
auto* impl = LiveBuf(buf);
return impl == nullptr ? 0 : static_cast<size_t>(impl->bytes.size());
}
-void GFBufferZeroize(GFBufferRef buf) {
+void BufZeroize(GFHostContextRef, GFBufferRef buf) {
auto* impl = LiveBuf(buf);
if (impl != nullptr) impl->bytes.fill('\0');
}
-void GFBufferRelease(GFBufferRef buf) {
+void BufRelease(GFHostContextRef, GFBufferRef buf) {
if (buf == nullptr) return;
if (LiveBuffers().erase(buf) == 0) {
std::printf("[FAIL] GFBufferRelease on a handle it does not own\n");
@@ -315,10 +323,11 @@ void GFBufferRelease(GFBufferRef buf) {
delete reinterpret_cast<StubBuffer*>(buf);
}
-size_t GFBufferOutstandingCount(const char*) { return LiveBuffers().size(); }
+auto BufOutstanding(GFHostContextRef) -> size_t { return LiveBuffers().size(); }
-int GFGpgSign(int, const char* const* key_ids, size_t key_ids_size,
- GFBufferView in, int sign_mode, int, GFGpgResultRef* out) {
+auto GpgSign(GFHostContextRef, int, const char* const* key_ids,
+ size_t key_ids_size, GFBufferView in, int sign_mode, int,
+ GFGpgResultRef* out) -> int {
if (out == nullptr) return -1;
g_recording.sign.append({BufferBytes(in), sign_mode});
(void)KeyIdCount(key_ids, key_ids_size);
@@ -331,8 +340,8 @@ int GFGpgSign(int, const char* const* key_ids, size_t key_ids_size,
return GF_GPG_OP_FAILED;
}
- r->data = GFBufferNewFromBytes(g_recording.sign_output.constData(),
- g_recording.sign_output.size());
+ r->data = BufNew(nullptr, g_recording.sign_output.constData(),
+ g_recording.sign_output.size());
r->hash_algo = "SHA256";
r->capsule_id = "stub-capsule";
r->error_string = "Success";
@@ -340,8 +349,9 @@ int GFGpgSign(int, const char* const* key_ids, size_t key_ids_size,
return GF_GPG_OK;
}
-int GFGpgEncrypt(int, const char* const* key_ids, size_t key_ids_size,
- GFBufferView in, int, GFGpgResultRef* out) {
+auto GpgEncrypt(GFHostContextRef, int, const char* const* key_ids,
+ size_t key_ids_size, GFBufferView in, int, GFGpgResultRef* out)
+ -> int {
if (out == nullptr) return -1;
g_recording.encrypt.append({BufferBytes(in)});
(void)KeyIdCount(key_ids, key_ids_size);
@@ -354,15 +364,16 @@ int GFGpgEncrypt(int, const char* const* key_ids, size_t key_ids_size,
return GF_GPG_OP_FAILED;
}
- r->data = GFBufferNewFromBytes(g_recording.encrypt_output.constData(),
- g_recording.encrypt_output.size());
+ r->data = BufNew(nullptr, g_recording.encrypt_output.constData(),
+ g_recording.encrypt_output.size());
r->capsule_id = "stub-capsule";
r->error_string = "Success";
*out = reinterpret_cast<GFGpgResultRef>(r);
return GF_GPG_OK;
}
-int GFGpgDecrypt(int, GFBufferView in, GFGpgResultRef* out) {
+auto GpgDecrypt(GFHostContextRef, int, GFBufferView in, GFGpgResultRef* out)
+ -> int {
if (out == nullptr) return -1;
g_recording.decrypt.append({BufferBytes(in)});
@@ -374,16 +385,16 @@ int GFGpgDecrypt(int, GFBufferView in, GFGpgResultRef* out) {
return GF_GPG_OP_FAILED;
}
- r->data = GFBufferNewFromBytes(g_recording.decrypt_output.constData(),
- g_recording.decrypt_output.size());
+ r->data = BufNew(nullptr, g_recording.decrypt_output.constData(),
+ g_recording.decrypt_output.size());
r->capsule_id = "stub-capsule";
r->error_string = "Success";
*out = reinterpret_cast<GFGpgResultRef>(r);
return GF_GPG_OK;
}
-int GFGpgVerify(int, GFBufferView in, GFBufferView signature,
- GFGpgResultRef* out) {
+auto GpgVerify(GFHostContextRef, int, GFBufferView in, GFBufferView signature,
+ GFGpgResultRef* out) -> int {
if (out == nullptr) return -1;
g_recording.verify.append({BufferBytes(in), BufferBytes(signature)});
@@ -401,37 +412,37 @@ int GFGpgVerify(int, GFBufferView in, GFBufferView signature,
return GF_GPG_OK;
}
-int GFGpgResultStatusOf(GFGpgResultRef r) {
+auto GpgResultStatus(GFHostContextRef, GFGpgResultRef r) -> int {
auto* impl = LiveRes(r);
return impl == nullptr ? GF_GPG_BAD_REQUEST : impl->status;
}
-uint32_t GFGpgResultError(GFGpgResultRef r) {
+auto GpgResultError(GFHostContextRef, GFGpgResultRef r) -> uint32_t {
auto* impl = LiveRes(r);
return impl == nullptr ? 0 : impl->error;
}
-GFBufferView GFGpgResultData(GFGpgResultRef r) {
+auto GpgResultData(GFHostContextRef, GFGpgResultRef r) -> GFBufferView {
auto* impl = LiveRes(r);
return impl == nullptr ? nullptr : impl->data;
}
-const char* GFGpgResultCapsuleId(GFGpgResultRef r) {
+auto ResultCapsuleId(GFGpgResultRef r) -> const char* {
auto* impl = LiveRes(r);
return impl == nullptr ? "" : impl->capsule_id.constData();
}
-const char* GFGpgResultErrorString(GFGpgResultRef r) {
+auto ResultErrorString(GFGpgResultRef r) -> const char* {
auto* impl = LiveRes(r);
return impl == nullptr ? "" : impl->error_string.constData();
}
-const char* GFGpgResultHashAlgo(GFGpgResultRef r) {
+auto ResultHashAlgo(GFGpgResultRef r) -> const char* {
auto* impl = LiveRes(r);
return impl == nullptr ? "" : impl->hash_algo.constData();
}
-GFBufferRef GFGpgResultTakeData(GFGpgResultRef r) {
+auto GpgResultTakeData(GFHostContextRef, GFGpgResultRef r) -> GFBufferRef {
auto* impl = LiveRes(r);
if (impl == nullptr) return nullptr;
auto* taken = impl->data;
@@ -439,7 +450,7 @@ GFBufferRef GFGpgResultTakeData(GFGpgResultRef r) {
return taken;
}
-void GFGpgResultRelease(GFGpgResultRef r) {
+void GpgResultRelease(GFHostContextRef, GFGpgResultRef r) {
if (r == nullptr) return;
if (LiveResults().erase(r) == 0) {
std::printf("[FAIL] GFGpgResultRelease on a handle it does not own\n");
@@ -447,15 +458,134 @@ void GFGpgResultRelease(GFGpgResultRef r) {
return;
}
auto* impl = reinterpret_cast<StubResult*>(r);
- GFBufferRelease(impl->data);
+ BufRelease(nullptr, impl->data);
delete impl;
}
-size_t GFGpgResultOutstandingCount(const char*) { return LiveResults().size(); }
+auto GpgResultOutstanding(GFHostContextRef) -> size_t {
+ return LiveResults().size();
+}
+
+/* --- the tables ----------------------------------------------------------- */
-} // extern "C"
+auto MemAlloc(GFHostContextRef, int arena, uint32_t n) -> void* {
+ return arena == GF_ARENA_SECURE ? MemAllocSecure(n) : MemAllocNormal(n);
+}
+auto MemRealloc(GFHostContextRef, int, void* p, uint32_t n) -> void* {
+ return std::realloc(p, n);
+}
+void MemFree(GFHostContextRef, int arena, void* p) {
+ if (arena == GF_ARENA_SECURE) {
+ MemFreeSecure(p);
+ } else {
+ MemFreeNormal(p);
+ }
+}
+auto MemStrDup(GFHostContextRef, int arena, const char* s) -> char* {
+ return arena == GF_ARENA_SECURE ? StrDupSecure(s) : StrDupNormal(s);
+}
+
+/// Three named getters became one taking a field, so the fake follows.
+auto GpgResultText(GFHostContextRef, GFGpgResultRef r, int field) -> const
+ char* {
+ switch (field) {
+ case GF_GPG_RESULT_TEXT_ERROR_STRING:
+ return ResultErrorString(r);
+ case GF_GPG_RESULT_TEXT_HASH_ALGO:
+ return ResultHashAlgo(r);
+ default:
+ return ResultCapsuleId(r);
+ }
+}
+
+const GFHostBufferApi kBuffer = {
+ sizeof(GFHostBufferApi),
+ &BufNew,
+ &BufData,
+ &BufSize,
+ &BufZeroize,
+ &BufRelease,
+ &BufOutstanding,
+ &MemAlloc,
+ &MemRealloc,
+ &MemFree,
+ &MemStrDup,
+};
+
+const GFHostLogApi kLog = {sizeof(GFHostLogApi), &LogWrite, &LogEnabled};
+
+const GFHostAppApi kApp = {
+ sizeof(GFHostAppApi), &AppText, &AppText, &AppText, &AppText,
+ &AppLocale, &AppZero, &AppZero,
+};
+
+auto NotImplementedInt(GFHostContextRef, ...) -> int { return -1; }
+
+const GFHostGpgApi kGpg = {
+ sizeof(GFHostGpgApi),
+ nullptr, // current_channel: these tests always name the channel
+ &GpgSign,
+ &GpgEncrypt,
+ &GpgDecrypt,
+ &GpgVerify,
+ &GpgResultStatus,
+ &GpgResultError,
+ &GpgResultData,
+ &GpgResultText,
+ &GpgResultTakeData,
+ &GpgResultRelease,
+ &GpgResultOutstanding,
+ &GpgAnalyse,
+ &GpgPublicKey,
+ nullptr, // key_primary_uid
+ nullptr, // export_key
+ nullptr, // import_keys
+ nullptr, // find_keys
+ nullptr, // key_brief_count
+ nullptr, // key_brief_at
+ nullptr, // key_brief_release
+ nullptr, // sniff_recipients
+ nullptr, // recipient_count
+ nullptr, // recipient_at
+ nullptr, // recipient_release
+ nullptr, // list_addresses
+};
+
+struct FakeHostContext {
+ int marker = 0;
+};
+
+auto TheTable() -> GFHostApi& {
+ static FakeHostContext host_context;
+ static GFHostApi host = [&]() {
+ GFHostApi t{};
+ t.struct_size = sizeof(GFHostApi);
+ t.abi_version = GF_SDK_ABI_VERSION;
+ t.module_id = "harness";
+ t.context = reinterpret_cast<GFHostContextRef>(&host_context);
+ t.buffer = &kBuffer;
+ t.log = &kLog;
+ t.app = &kApp;
+ t.gpg = &kGpg;
+ return t;
+ }();
+ return host;
+}
+
+} // namespace
+
+/// The module's SDK context, which the runtime would normally own.
+auto GFModuleSdkContext() -> GFSDKContext* {
+ static GFSDKContext context = []() {
+ GFSDKContext c{};
+ c.struct_size = sizeof(GFSDKContext);
+ c.abi_version = GF_SDK_ABI_VERSION;
+ c.host = &TheTable();
+ c.module_id = "harness";
+ return c;
+ }();
+ return &context;
+}
-// C++ linkage, deliberately outside the extern "C" block above: GFModule.h
-// declares it as an ordinary C++ function, and the log macros call it on every
-// line. Defined here with C linkage it compiles cleanly and fails to link.
+/// Identity, for the log macros. C++ linkage, as GFModule.h declares it.
const char* GFGetModuleID() { return "harness"; }