From edca90341677a207b39c4845e0c5e0d5bf67f6b4 Mon Sep 17 00:00:00 2001 From: ionut Date: Tue, 5 May 2026 19:08:31 +0300 Subject: [PATCH] progress on modem and data card --- src/main.c | 105 ++++++++++++++++++- src/neonucleus.c | 265 ++++++++++++++++++++++++++++++++++++++++++++++- src/neonucleus.h | 65 +++++++++++- 3 files changed, 427 insertions(+), 8 deletions(-) diff --git a/src/main.c b/src/main.c index a7d003b..e4d267a 100644 --- a/src/main.c +++ b/src/main.c @@ -48,6 +48,100 @@ static nn_Exit sandbox_handler(nn_ComponentRequest *req) { return NN_OK; } +static nn_Exit ne_dataBullshit(nn_DataCardRequest *req) { + nn_Computer *C = req->computer; + nn_DataCardAction action = req->action; + nn_Exit e; + + if(action == NN_DATA_DROP) { + return NN_OK; + } + if(action == NN_DATA_ENCODE64) { + int outSize = 0; + char *out = EncodeDataBase64((const unsigned char *)req->data, req->datalen, &outSize); + if(out == NULL) return NN_ENOMEM; + // -1 because raylib includes the NUL terminator?? + e = nn_pushlstring(C, out, outSize-1); + MemFree(out); + return e; + } + if(action == NN_DATA_DECODE64) { + int outSize = 0; + char *out = (char *)DecodeDataBase64(req->data, &outSize); + if(out == NULL) return NN_ENOMEM; + e = nn_pushlstring(C, out, outSize); + MemFree(out); + return e; + } + if(action == NN_DATA_DEFLATE) { + int outSize = 0; + char *out = (char *)CompressData((const unsigned char *)req->data, req->datalen, &outSize); + if(out == NULL) return NN_ENOMEM; + e = nn_pushlstring(C, out, outSize); + MemFree(out); + return e; + } + if(action == NN_DATA_INFLATE) { + int outSize = 0; + char *out = (char *)DecompressData((unsigned char *)req->data, req->datalen, &outSize); + if(out == NULL) return NN_ENOMEM; + e = nn_pushlstring(C, out, outSize); + MemFree(out); + return e; + } + if(action == NN_DATA_CRC32) { + unsigned int check = nn_computeCRC32(req->crc32.data, req->crc32.datalen); + req->crc32.checksum[0] = (check >> 0) & 0xFF; + req->crc32.checksum[1] = (check >> 8) & 0xFF; + req->crc32.checksum[2] = (check >> 16) & 0xFF; + req->crc32.checksum[3] = (check >> 24) & 0xFF; + return NN_OK; + } + if(action == NN_DATA_MD5) { + unsigned int *out = ComputeMD5((unsigned char *)req->md5.data, req->md5.datalen); + if(out == NULL) return NN_ENOMEM; + memcpy(req->md5.checksum, out, 16); + return NN_OK; + } + if(action == NN_DATA_SHA256) { + // does not match OC, dunno why + unsigned int *out = ComputeSHA256((unsigned char *)req->sha256.data, req->sha256.datalen); + if(out == NULL) return NN_ENOMEM; + memcpy(req->sha256.checksum, out, 32); + return NN_OK; + } + if(action == NN_DATA_RANDOM) { + for(size_t i = 0; i < req->randbuf.buflen; i++) { + req->randbuf.buf[i] = rand(); + } + return NN_OK; + } + if(action == NN_DATA_ENCRYPT) { + return nn_pushlstring(C, req->data, req->datalen); + } + if(action == NN_DATA_DECRYPT) { + return nn_pushlstring(C, req->data, req->datalen); + } + + if(C) nn_setError(C, "ne: data method not implemented"); + return NN_EBADCALL; +} + +static nn_Exit ne_modemBullshit(nn_ModemRequest *req) { + nn_Computer *C = req->computer; + + if(req->action == NN_MODEM_DROP) { + return NN_OK; + } + if(req->action == NN_MODEM_SEND) { + printf("Transmission from %s to %s (port %zu) of %zu bytes (%zu values)\n", req->localAddress, req->send.address == NULL ? "*" : req->send.address, req->send.port, req->send.contents->buflen, req->send.contents->valueCount); + return nn_pushModemMessage(C, req->localAddress, nn_getComputerAddress(C), req->send.port, 0, req->send.contents); + } + + if(C) nn_setError(C, "ne: modem method not implemented"); + return NN_EBADCALL; +} + static unsigned char ne_processColorPart(unsigned char channel, double brightness) { double n = (double)channel / 255; n *= brightness; @@ -392,6 +486,9 @@ int main(int argc, char **argv) { nn_setComponentMethods(ocelotCard, sandboxMethods); nn_setComponentHandler(ocelotCard, sandbox_handler); + nn_Component *dataCard = nn_createDataCard(u, NULL, &nn_defaultDataCards[2], NULL, ne_dataBullshit); + nn_Component *modem = nn_createModem(u, NULL, &nn_defaultWiredModem, NULL, ne_modemBullshit); + char *eepromCode = (char *)minBIOS; size_t eepromSize = strlen(minBIOS); const char *eepromPath = getenv("NN_EEPROM"); @@ -492,8 +589,8 @@ int main(int argc, char **argv) { double nextSecond = 0; double wattage = 0; - nn_Component *screen = ncl_createScreen(u, NULL, &nn_defaultScreens[3]); - nn_Component *gpuCard = ncl_createGPU(u, NULL, &nn_defaultGPUs[3]); + nn_Component *screen = ncl_createScreen(u, NULL, &nn_defaultScreens[2]); + nn_Component *gpuCard = ncl_createGPU(u, NULL, &nn_defaultGPUs[2]); nn_Component *keyboard = nn_createComponent( u, "mainKB", "keyboard"); @@ -536,6 +633,8 @@ int main(int argc, char **argv) { nn_mountComponent(c, testingfs, 3, false); nn_mountComponent(c, testDrive, 4, false); nn_mountComponent(c, testFlash, 5, false); + nn_mountComponent(c, dataCard, 6, false); + nn_mountComponent(c, modem, 7, false); int ltx = 0, lty = 0; double scrollBuf = 0; SetTargetFPS(60); @@ -759,6 +858,8 @@ cleanup:; nn_dropComponent(screen); nn_dropComponent(gpuCard); nn_dropComponent(keyboard); + nn_dropComponent(dataCard); + nn_dropComponent(modem); // rip the universe nn_destroyUniverse(u); ncl_destroyGlyphCache(gc); diff --git a/src/neonucleus.c b/src/neonucleus.c index babdb4e..1235b1a 100644 --- a/src/neonucleus.c +++ b/src/neonucleus.c @@ -1752,7 +1752,11 @@ nn_Exit nn_tick(nn_Computer *computer) { err = nn_startComputer(computer); if(err) return err; } else if(computer->state != NN_RUNNING) { - if(computer->state != NN_CRASHED) nn_setErrorFromExit(computer, NN_EBADSTATE); + if(computer->state == NN_BLACKOUT) { + nn_setError(computer, "out of energy"); + } else if(computer->state != NN_CRASHED) { + nn_setErrorFromExit(computer, NN_EBADSTATE); + } return NN_EBADSTATE; } computer->state = NN_RUNNING; @@ -2187,6 +2191,11 @@ nn_Exit nn_invokeComponent(nn_Computer *computer, const char *compAddress, const } computer->stackSize = req.returnCount; + if(nn_getEnergy(computer) <= 0) { + nn_setError(computer, "out of energy"); + return NN_EBADCALL; + } + return NN_OK; } @@ -6055,6 +6064,17 @@ nn_DataCard nn_defaultDataCards[3] = { NN_INIT(nn_DataCard) { .limit = NN_MiB, .maxRandom = NN_KiB, + .base64PerTick = 32, + .deflatingPerTick = 4, + .crc32PerTick = 32, + .md5PerTick = 8, + .sha256PerTick = 4, + .encryptPerTick = 4, + .genPerTick = 1, + .deserializePerTick = 8, + .ecdhPerTick = 1, + .ecdsaPerTick = 1, + .randomPerTick = 4, .canHash = true, .canEncrypt = false, .canECDH = false, @@ -6070,6 +6090,17 @@ nn_DataCard nn_defaultDataCards[3] = { NN_INIT(nn_DataCard) { .limit = NN_MiB, .maxRandom = NN_KiB, + .base64PerTick = 32, + .deflatingPerTick = 4, + .crc32PerTick = 32, + .md5PerTick = 8, + .sha256PerTick = 4, + .encryptPerTick = 4, + .genPerTick = 1, + .deserializePerTick = 8, + .ecdhPerTick = 1, + .ecdsaPerTick = 1, + .randomPerTick = 4, .canHash = true, .canEncrypt = true, .canECDH = false, @@ -6085,6 +6116,17 @@ nn_DataCard nn_defaultDataCards[3] = { NN_INIT(nn_DataCard) { .limit = NN_MiB, .maxRandom = NN_KiB, + .base64PerTick = 32, + .deflatingPerTick = 4, + .crc32PerTick = 32, + .md5PerTick = 8, + .sha256PerTick = 4, + .encryptPerTick = 4, + .genPerTick = 1, + .deserializePerTick = 8, + .ecdhPerTick = 1, + .ecdsaPerTick = 1, + .randomPerTick = 4, .canHash = true, .canEncrypt = true, .canECDH = true, @@ -6111,10 +6153,10 @@ static nn_Exit nn_dataHandler(nn_ComponentRequest *req) { if(method == NN_DATANUM_SHA256 || method == NN_DATANUM_MD5) { req->methodEnabled = dataCard.canHash; } - if(method == NN_DATANUM_DEFLATE || method == NN_DATANUM_INFLATE || method == NN_DATANUM_RANDOM || method == NN_DATANUM_MAXRANDOM) { + if(method == NN_DATANUM_DEFLATE || method == NN_DATANUM_INFLATE) { req->methodEnabled = dataCard.canCompress; } - if(method == NN_DATANUM_ENCRYPT || method == NN_DATANUM_DECRYPT) { + if(method == NN_DATANUM_ENCRYPT || method == NN_DATANUM_DECRYPT || method == NN_DATANUM_RANDOM || method == NN_DATANUM_MAXRANDOM) { req->methodEnabled = dataCard.canEncrypt; } if(method == NN_DATANUM_GENKEYPAIR || method == NN_DATANUM_ECDH || method == NN_DATANUM_ECDSA || method == NN_DATANUM_DESERIALIZEKEY) { @@ -6147,6 +6189,153 @@ static nn_Exit nn_dataHandler(nn_ComponentRequest *req) { } // TODO: the cool methods + if(method == NN_DATANUM_ENCODE64) { + nn_costComponent(C, req->compAddress, dataCard.base64PerTick); + if(nn_checkstring(C, 0, "bad argument #1 (string expected)")) return NN_EBADCALL; + dreq.action = NN_DATA_ENCODE64; + dreq.data = nn_tolstring(C, 0, &dreq.datalen); + if(dreq.datalen > dataCard.limit) return NN_ELIMIT; + e = state->handler(&dreq); + if(e) return e; + req->returnCount = 1; + return NN_OK; + } + if(method == NN_DATANUM_DECODE64) { + nn_costComponent(C, req->compAddress, dataCard.base64PerTick); + if(nn_checkstring(C, 0, "bad argument #1 (string expected)")) return NN_EBADCALL; + dreq.action = NN_DATA_DECODE64; + dreq.data = nn_tolstring(C, 0, &dreq.datalen); + if(dreq.datalen > dataCard.limit) return NN_ELIMIT; + e = state->handler(&dreq); + if(e) return e; + req->returnCount = 1; + return NN_OK; + } + if(method == NN_DATANUM_DEFLATE) { + nn_costComponent(C, req->compAddress, dataCard.deflatingPerTick); + if(nn_checkstring(C, 0, "bad argument #1 (string expected)")) return NN_EBADCALL; + dreq.action = NN_DATA_DEFLATE; + dreq.data = nn_tolstring(C, 0, &dreq.datalen); + if(dreq.datalen > dataCard.limit) return NN_ELIMIT; + e = state->handler(&dreq); + if(e) return e; + req->returnCount = 1; + return NN_OK; + } + if(method == NN_DATANUM_INFLATE) { + nn_costComponent(C, req->compAddress, dataCard.deflatingPerTick); + if(nn_checkstring(C, 0, "bad argument #1 (string expected)")) return NN_EBADCALL; + dreq.action = NN_DATA_INFLATE; + dreq.data = nn_tolstring(C, 0, &dreq.datalen); + if(dreq.datalen > dataCard.limit) return NN_ELIMIT; + e = state->handler(&dreq); + if(e) return e; + req->returnCount = 1; + return NN_OK; + } + if(method == NN_DATANUM_CRC32) { + nn_costComponent(C, req->compAddress, dataCard.crc32PerTick); + if(nn_checkstring(C, 0, "bad argument #1 (string expected)")) return NN_EBADCALL; + dreq.action = NN_DATA_CRC32; + dreq.crc32.data = nn_tolstring(C, 0, &dreq.crc32.datalen); + if(dreq.crc32.datalen > dataCard.limit) return NN_ELIMIT; + e = state->handler(&dreq); + if(e) return e; + req->returnCount = 1; + return nn_pushlstring(C, dreq.crc32.checksum, 4); + } + if(method == NN_DATANUM_MD5) { + nn_costComponent(C, req->compAddress, dataCard.md5PerTick); + if(nn_checkstring(C, 0, "bad argument #1 (string expected)")) return NN_EBADCALL; + dreq.action = NN_DATA_MD5; + dreq.md5.data = nn_tolstring(C, 0, &dreq.md5.datalen); + if(dreq.md5.datalen > dataCard.limit) return NN_ELIMIT; + e = state->handler(&dreq); + if(e) return e; + req->returnCount = 1; + return nn_pushlstring(C, dreq.md5.checksum, 16); + } + if(method == NN_DATANUM_SHA256) { + nn_costComponent(C, req->compAddress, dataCard.sha256PerTick); + if(nn_checkstring(C, 0, "bad argument #1 (string expected)")) return NN_EBADCALL; + dreq.action = NN_DATA_SHA256; + dreq.sha256.data = nn_tolstring(C, 0, &dreq.sha256.datalen); + if(dreq.sha256.datalen > dataCard.limit) return NN_ELIMIT; + e = state->handler(&dreq); + if(e) return e; + req->returnCount = 1; + return nn_pushlstring(C, dreq.sha256.checksum, 32); + } + if(method == NN_DATANUM_RANDOM) { + nn_costComponent(C, req->compAddress, dataCard.randomPerTick); + if(nn_checkinteger(C, 0, "bad argument #1 (integer expected)")) return NN_EBADCALL; + intptr_t n = nn_tointeger(C, 0); + if(n <= 0) { + n = 1; + } + if(n > dataCard.maxRandom) return NN_ELIMIT; + char *buf = nn_alloc(ctx, n); + dreq.action = NN_DATA_RANDOM; + dreq.randbuf.buf = buf; + dreq.randbuf.buflen = n; + e = state->handler(&dreq); + if(e) { + nn_free(ctx, buf, n); + return e; + } + req->returnCount = 1; + e = nn_pushlstring(C, buf, n); + nn_free(ctx, buf, n); + return e; + } + if(method == NN_DATANUM_ENCRYPT) { + nn_costComponent(C, req->compAddress, dataCard.encryptPerTick); + if(nn_checkstring(C, 0, "bad argument #1 (string expected)")) return NN_EBADCALL; + if(nn_checkstring(C, 1, "bad argument #2 (string expected)")) return NN_EBADCALL; + if(nn_checkstring(C, 2, "bad argument #3 (string expected)")) return NN_EBADCALL; + dreq.action = NN_DATA_ENCRYPT; + dreq.encrypt.data = nn_tolstring(C, 0, &dreq.encrypt.datalen); + if(dreq.encrypt.datalen > dataCard.limit) return NN_ELIMIT; + size_t len; + dreq.encrypt.key = nn_tolstring(C, 1, &len); + if(len != 16) { + nn_setError(C, "invalid key"); + return NN_EBADCALL; + } + dreq.encrypt.iv = nn_tolstring(C, 2, &len); + if(len != 16) { + nn_setError(C, "invalid IV"); + return NN_EBADCALL; + } + e = state->handler(&dreq); + if(e) return e; + req->returnCount = 1; + return NN_OK; + } + if(method == NN_DATANUM_DECRYPT) { + nn_costComponent(C, req->compAddress, dataCard.encryptPerTick); + if(nn_checkstring(C, 0, "bad argument #1 (string expected)")) return NN_EBADCALL; + if(nn_checkstring(C, 1, "bad argument #2 (string expected)")) return NN_EBADCALL; + if(nn_checkstring(C, 2, "bad argument #3 (string expected)")) return NN_EBADCALL; + dreq.action = NN_DATA_ENCRYPT; + dreq.encrypt.data = nn_tolstring(C, 0, &dreq.encrypt.datalen); + if(dreq.encrypt.datalen > dataCard.limit) return NN_ELIMIT; + size_t len; + dreq.encrypt.key = nn_tolstring(C, 1, &len); + if(len != 16) { + nn_setError(C, "invalid key"); + return NN_EBADCALL; + } + dreq.encrypt.iv = nn_tolstring(C, 2, &len); + if(len != 16) { + nn_setError(C, "invalid IV"); + return NN_EBADCALL; + } + e = state->handler(&dreq); + if(e) return e; + req->returnCount = 1; + return NN_OK; + } if(C) nn_setError(C, "data: not implemented yet"); return NN_EBADCALL; @@ -6165,7 +6354,7 @@ nn_Component *nn_createDataCard(nn_Universe *universe, const char *address, cons [NN_DATANUM_MD5] = {"md5", "function(data: string): string - Computes the MD5 hash of the data", NN_DIRECT}, [NN_DATANUM_SHA256] = {"sha256", "function(data: string): string - Computes the SHA256 hash of the data", NN_DIRECT}, [NN_DATANUM_DEFLATE] = {"deflate", "function(data: string): string - Compresses the data", NN_DIRECT}, - [NN_DATANUM_INFLATE] = {"deflate", "function(data: string): string - Decompresses the compressed data", NN_DIRECT}, + [NN_DATANUM_INFLATE] = {"inflate", "function(data: string): string - Decompresses the compressed data", NN_DIRECT}, [NN_DATANUM_ENCRYPT] = {"encrypt", "function(data: string, key: string, iv: string): string - Encrypts the data", NN_DIRECT}, [NN_DATANUM_DECRYPT] = {"decrypt", "function(data: string, key: string, iv: string): string - Decrypts the data", NN_DIRECT}, [NN_DATANUM_RANDOM] = {"random", "function(size: integer): string - Generates an amount of secure random bytes", NN_DIRECT}, @@ -6264,15 +6453,19 @@ static nn_Exit nn_modemHandler(nn_ComponentRequest *req) { } if(method == NN_MODEMNUM_ISWIRED) { + req->returnCount = 1; return nn_pushbool(C, isWired); } if(method == NN_MODEMNUM_ISWIRELESS) { + req->returnCount = 1; return nn_pushbool(C, isWireless); } if(method == NN_MODEMNUM_MAXPACKETSIZE) { + req->returnCount = 1; return nn_pushinteger(C, state->modem.maxPacketSize); } if(method == NN_MODEMNUM_MAXVALUES) { + req->returnCount = 1; return nn_pushinteger(C, state->modem.maxValues); } if(method == NN_MODEMNUM_GETSTRENGTH) { @@ -6283,9 +6476,73 @@ static nn_Exit nn_modemHandler(nn_ComponentRequest *req) { return nn_pushinteger(C, mreq.strength); } if(method == NN_MODEMNUM_MAXSTRENGTH) { + req->returnCount = 1; return nn_pushinteger(C, state->modem.maxRange); } + if(method == NN_MODEMNUM_BROADCAST) { + if(nn_checkinteger(C, 0, "bad argument #1 (integer expected)")) return NN_EBADCALL; + intptr_t port = nn_tointeger(C, 0); + if(port < 1 || port > NN_MAX_PORT) { + nn_setError(C, "invalid port"); + return NN_EBADCALL; + } + size_t valcount = nn_getstacksize(C) - 1; + if(valcount > state->modem.maxValues) return NN_EBADCALL; + int cost = nn_countValueCost(C, valcount); + if(cost < 0) { + nn_setError(C, "invalid contents"); + return NN_EBADCALL; + } + if(cost > state->modem.maxPacketSize) return NN_ELIMIT; + nn_EncodedNetworkContents data; + e = nn_encodeNetworkContents(C, &data, valcount); + if(e) return e; + mreq.action = NN_MODEM_SEND; + mreq.send.address = NULL; + mreq.send.port = port; + mreq.send.contents = &data; + e = state->handler(&mreq); + nn_dropNetworkContents(&data); + if(!e) { + req->returnCount = 1; + e = nn_pushbool(C, true); + } + return e; + } + if(method == NN_MODEMNUM_SEND) { + if(nn_checkstring(C, 0, "bad argument #1 (string expected)")) return NN_EBADCALL; + if(nn_checkinteger(C, 1, "bad argument #2 (integer expected)")) return NN_EBADCALL; + const char *addr = nn_tostring(C, 0); + intptr_t port = nn_tointeger(C, 1); + if(port < 1 || port > NN_MAX_PORT) { + nn_setError(C, "invalid port"); + return NN_EBADCALL; + } + size_t valcount = nn_getstacksize(C) - 1; + if(valcount > state->modem.maxValues) return NN_EBADCALL; + int cost = nn_countValueCost(C, valcount); + if(cost < 0) { + nn_setError(C, "invalid contents"); + return NN_EBADCALL; + } + if(cost > state->modem.maxPacketSize) return NN_ELIMIT; + nn_EncodedNetworkContents data; + e = nn_encodeNetworkContents(C, &data, valcount); + if(e) return e; + mreq.action = NN_MODEM_SEND; + mreq.send.address = addr; + mreq.send.port = port; + mreq.send.contents = &data; + e = state->handler(&mreq); + nn_dropNetworkContents(&data); + if(!e) { + req->returnCount = 1; + e = nn_pushbool(C, true); + } + return e; + } + if(C) nn_setError(C, "modem: not implemented yet"); return NN_EBADCALL; } diff --git a/src/neonucleus.h b/src/neonucleus.h index 830d476..2336e31 100644 --- a/src/neonucleus.h +++ b/src/neonucleus.h @@ -1657,10 +1657,15 @@ nn_Component *nn_createScreen( nn_ScreenHandler *handler ); +// Computes a CRC32 checksum +unsigned int nn_computeCRC32(const char *data, size_t datalen); + typedef struct nn_DataCard { // The buffer size of the data card, limit in both input and output. - // This buffer is allocated on the heap before the call, thus setting it to be very large will lead to huge *spikes* in memory usage. // In OC, this value is 1MiB regardless of tier. + // As there is no out buffer and you are expected to push strings, + // the buffer is not pre-allocated. This is intentional, as memory + // would be wasted and it could be a potential attack vector otherwise. size_t limit; // The maximum amount of secure random bytes that can be generated. @@ -1669,6 +1674,29 @@ typedef struct nn_DataCard { // as the amount of bytes needed is known perfectly. size_t maxRandom; + // for encoding/decoding. OC defaulted to 32 + size_t base64PerTick; + // for deflate/inflate. OC defaulted to 4 + size_t deflatingPerTick; + // OC defaulted to 32 + size_t crc32PerTick; + // OC defaulted to 8 + size_t md5PerTick; + // OC defaulted to 4 + size_t sha256PerTick; + // for encrypt/decrypt. OC defaulted to 4 + size_t encryptPerTick; + // for generateKeyPair. OC defaulted to 1 + size_t genPerTick; + // for deserializeKey. OC defaulted to 8 + size_t deserializePerTick; + // OC defaulted to 1 + size_t ecdhPerTick; + // OC defaulted to 1 + size_t ecdsaPerTick; + // OC defaults to 4 + size_t randomPerTick; + // Capabilities bool canHash; bool canEncrypt; @@ -1718,7 +1746,7 @@ typedef enum nn_DataCardAction { NN_DATA_DEFLATE, // Inflate. Should support the ZLIB format, as thats what OC uses, and GZIP support is optional. - NN_DATA_INFALTE, + NN_DATA_INFLATE, // Encrypt data with AES-128. The full algorithm is AES/CBC/PKCS5, as is use PKCS5 for padding, CBC for block sequences, and AES-128 for encrypting blocks. // It does also receive a 128-bit AES Initialization Vector, for better security. @@ -1752,6 +1780,39 @@ typedef struct nn_DataCardRequest { const nn_DataCard *dataCard; nn_DataCardAction action; // TODO: the fields + union { + struct { + const char *data; + size_t datalen; + char checksum[4]; + } crc32; + struct { + const char *data; + size_t datalen; + char checksum[16]; + } md5; + struct { + const char *data; + size_t datalen; + char checksum[32]; + } sha256; + // for encrypt/decrypt + struct { + const char *data; + size_t datalen; + const char *key; + const char *iv; + } encrypt; + struct { + char *buf; + size_t buflen; + } randbuf; + // for deflate, inflate, encode64 and decode64 + struct { + const char *data; + size_t datalen; + }; + }; } nn_DataCardRequest; typedef nn_Exit (nn_DataCardHandler)(nn_DataCardRequest *req);