// testCrypto.c -- exercises the crypto library: SHA-256/SHA-1 hashes, HMAC-SHA-256, base64 // and hex codecs (with round-trips and binary safety), random bytes, and UUIDs, driven from // a Lua context. #define _POSIX_C_SOURCE 200809L #include "calog.h" #include "calogCrypto.h" #include #include #include #define CHECK(cond, msg) checkImpl((cond), (msg), __FILE__, __LINE__) #define PUMP_LIMIT 4000 #define RESULT_SLOTS 16 static CalogT *calog = NULL; static _Atomic int64_t results[RESULT_SLOTS]; static _Atomic int32_t doneCount = 0; static _Atomic int32_t errorCount = 0; static int32_t testsRun = 0; static int32_t testsFailed = 0; static int64_t asInt(const CalogValueT *value); static void checkImpl(bool condition, const char *message, const char *file, int32_t line); static int32_t nativeDone(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData); static int32_t nativeReport(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData); static void onError(uint64_t contextId, const char *message, void *userData); static void pumpUntilDone(int32_t target); static int64_t asInt(const CalogValueT *value) { if (value->type == calogIntE) { return value->as.i; } return (int64_t)value->as.r; } static void checkImpl(bool condition, const char *message, const char *file, int32_t line) { testsRun++; if (!condition) { testsFailed++; printf("FAIL %s:%d %s\n", file, line, message); } } static int32_t nativeDone(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData) { (void)args; (void)argCount; (void)userData; atomic_fetch_add(&doneCount, 1); calogValueNil(result); return calogOkE; } static int32_t nativeReport(CalogValueT *args, int32_t argCount, CalogValueT *result, void *userData) { int64_t tag; (void)userData; calogValueNil(result); if (argCount != 2 || (args[0].type != calogIntE && args[0].type != calogRealE)) { return calogFail(result, calogErrArgE, "report expects (tag, value)"); } tag = asInt(&args[0]); if (tag < 0 || tag >= RESULT_SLOTS) { return calogFail(result, calogErrArgE, "report: tag out of range"); } atomic_store(&results[tag], asInt(&args[1])); return calogOkE; } static void onError(uint64_t contextId, const char *message, void *userData) { (void)contextId; (void)userData; fprintf(stderr, " [script error] %s\n", (message != NULL) ? message : "(null)"); atomic_fetch_add(&errorCount, 1); } static void pumpUntilDone(int32_t target) { struct timespec ts = { 0, 500000 }; int i; for (i = 0; i < PUMP_LIMIT; i++) { calogPump(calog); if (atomic_load(&doneCount) >= target) { calogPump(calog); return; } nanosleep(&ts, NULL); } } int main(void) { CalogContextT *ctx; int32_t i; calog = calogCreate(); if (calog == NULL) { printf("calog create failed\n"); return 1; } calogSetErrorHandler(calog, onError, NULL); calogRegister(calog, "report", nativeReport, NULL); calogRegister(calog, "done", nativeDone, NULL); calogCryptoRegister(calog); for (i = 0; i < RESULT_SLOTS; i++) { atomic_store(&results[i], -1); } ctx = calogContextOpen(calog, &calogLuaEngine); calogContextEval(ctx, "report(1, #cryptoHashSha256('abc'))\n" // 64 hex chars "report(2, cryptoHashSha256('abc') == 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad' and 1 or 0)\n" // known vector "report(3, cryptoHashSha1('abc') == 'a9993e364706816aba3e25717850c26c9cd0d89d' and 1 or 0)\n" // known vector "report(4, cryptoHmacSha256('key', 'The quick brown fox jumps over the lazy dog') == 'f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8' and 1 or 0)\n" // known vector "report(5, cryptoBase64Encode('abc') == 'YWJj' and 1 or 0)\n" // known encoding "report(6, cryptoBase64Decode(cryptoBase64Encode('hello')) == 'hello' and 1 or 0)\n" // round-trip, one pad byte "report(7, cryptoHexEncode('abc') == '616263' and 1 or 0)\n" // known encoding "report(8, cryptoHexDecode(cryptoHexEncode('Hello!')) == 'Hello!' and 1 or 0)\n" // round-trip "report(9, #cryptoRandomBytes(16))\n" // 16 bytes "local a = cryptoUuid()\n" "local b = cryptoUuid()\n" "report(10, (#a == 36 and #b == 36 and a ~= b) and 1 or 0)\n" // distinct, well-formed "report(11, cryptoBase64Decode(cryptoBase64Encode('a')) == 'a' and 1 or 0)\n" // round-trip, two pad bytes "report(12, cryptoHexEncode('a\\0b') == '610062' and 1 or 0)\n" // binary-safe (embedded NUL) "local ok = pcall(function() cryptoHexDecode('xyz') end)\n" "report(13, ok and 0 or 1)\n" // invalid hex is catchable "report(14, cryptoBase64Decode('YQ==\\n') == 'a' and 1 or 0)\n" // trailing whitespace does not add spurious NULs "done()"); pumpUntilDone(1); CHECK(atomic_load(&results[1]) == 64, "sha256 hex digest is 64 chars"); CHECK(atomic_load(&results[2]) == 1, "sha256('abc') matches the known vector"); CHECK(atomic_load(&results[3]) == 1, "sha1('abc') matches the known vector"); CHECK(atomic_load(&results[4]) == 1, "cryptoHmacSha256 matches the known vector"); CHECK(atomic_load(&results[5]) == 1, "cryptoBase64Encode('abc') is YWJj"); CHECK(atomic_load(&results[6]) == 1, "base64 round-trips a padded value"); CHECK(atomic_load(&results[7]) == 1, "cryptoHexEncode('abc') is 616263"); CHECK(atomic_load(&results[8]) == 1, "hex round-trips a value"); CHECK(atomic_load(&results[9]) == 16, "cryptoRandomBytes(16) yields 16 bytes"); CHECK(atomic_load(&results[10]) == 1, "two uuids are distinct and 36 chars"); CHECK(atomic_load(&results[11]) == 1, "base64 round-trips a double-padded value"); CHECK(atomic_load(&results[12]) == 1, "cryptoHexEncode is binary-safe over embedded NULs"); CHECK(atomic_load(&results[13]) == 1, "invalid hex raises a catchable error"); CHECK(atomic_load(&results[14]) == 1, "cryptoBase64Decode trims trailing whitespace without spurious trailing NULs"); CHECK(atomic_load(&errorCount) == 0, "no uncaught errors"); calogContextClose(ctx); calogDestroy(calog); printf("\n%d checks, %d failed\n", testsRun, testsFailed); fflush(stdout); if (testsFailed != 0) { return 1; } return 0; }