165 lines
7.5 KiB
C
165 lines
7.5 KiB
C
// 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 <stdatomic.h>
|
|
#include <stdio.h>
|
|
#include <time.h>
|
|
|
|
#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, #hashSha256('abc'))\n" // 64 hex chars
|
|
"report(2, hashSha256('abc') == 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad' and 1 or 0)\n" // known vector
|
|
"report(3, hashSha1('abc') == 'a9993e364706816aba3e25717850c26c9cd0d89d' and 1 or 0)\n" // known vector
|
|
"report(4, hmacSha256('key', 'The quick brown fox jumps over the lazy dog') == 'f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8' and 1 or 0)\n" // known vector
|
|
"report(5, base64Encode('abc') == 'YWJj' and 1 or 0)\n" // known encoding
|
|
"report(6, base64Decode(base64Encode('hello')) == 'hello' and 1 or 0)\n" // round-trip, one pad byte
|
|
"report(7, hexEncode('abc') == '616263' and 1 or 0)\n" // known encoding
|
|
"report(8, hexDecode(hexEncode('Hello!')) == 'Hello!' and 1 or 0)\n" // round-trip
|
|
"report(9, #randomBytes(16))\n" // 16 bytes
|
|
"local a = uuid()\n"
|
|
"local b = uuid()\n"
|
|
"report(10, (#a == 36 and #b == 36 and a ~= b) and 1 or 0)\n" // distinct, well-formed
|
|
"report(11, base64Decode(base64Encode('a')) == 'a' and 1 or 0)\n" // round-trip, two pad bytes
|
|
"report(12, hexEncode('a\\0b') == '610062' and 1 or 0)\n" // binary-safe (embedded NUL)
|
|
"local ok = pcall(function() hexDecode('xyz') end)\n"
|
|
"report(13, ok and 0 or 1)\n" // invalid hex is catchable
|
|
"report(14, base64Decode('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, "hmacSha256 matches the known vector");
|
|
CHECK(atomic_load(&results[5]) == 1, "base64Encode('abc') is YWJj");
|
|
CHECK(atomic_load(&results[6]) == 1, "base64 round-trips a padded value");
|
|
CHECK(atomic_load(&results[7]) == 1, "hexEncode('abc') is 616263");
|
|
CHECK(atomic_load(&results[8]) == 1, "hex round-trips a value");
|
|
CHECK(atomic_load(&results[9]) == 16, "randomBytes(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, "hexEncode is binary-safe over embedded NULs");
|
|
CHECK(atomic_load(&results[13]) == 1, "invalid hex raises a catchable error");
|
|
CHECK(atomic_load(&results[14]) == 1, "base64Decode 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;
|
|
}
|