diff options
Diffstat (limited to 'src/m_email/test/EMailCryptoSdkStubs.cpp')
| -rw-r--r-- | src/m_email/test/EMailCryptoSdkStubs.cpp | 270 |
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"; } |
