From 754a04de3ca99442d44e8c451e29f9ec3d1dc525 Mon Sep 17 00:00:00 2001 From: ionut Date: Sat, 9 May 2026 02:22:58 +0300 Subject: [PATCH] yipee data cards are done --- src/machine.lua | 3 +- src/main.c | 26 +++++++ src/neonucleus.c | 199 +++++++++++++++++++++++++++++++++++++++++------ src/neonucleus.h | 34 ++++++++ 4 files changed, 235 insertions(+), 27 deletions(-) diff --git a/src/machine.lua b/src/machine.lua index 20292e4..e732893 100644 --- a/src/machine.lua +++ b/src/machine.lua @@ -80,7 +80,6 @@ local function sandboxValue(val) return nt end if type(val) == "userdata" then - -- Light userdata shall never escape our shi! -- This matches how OC wraps it -- TODO: just make our own shi with fields and stuff -- like a component proxy, because this sucks @@ -90,7 +89,7 @@ local function sandboxValue(val) end, } - local wrapped = {type = "userdata"} + local wrapped = {type = "userdata", userdata = val} for name, m in pairs(userdata.methods(val)) do wrapped[name] = { name = name, diff --git a/src/main.c b/src/main.c index dca28c3..089ddb0 100644 --- a/src/main.c +++ b/src/main.c @@ -123,6 +123,32 @@ static nn_Exit ne_dataBullshit(nn_DataCardRequest *req) { return nn_pushlstring(C, req->data, req->datalen); } + if(action == NN_DATA_VALIDATEKEY) { + // its valid, trust + return NN_OK; + } + + if(action == NN_DATA_GENKEYS) { + char a = 'A' + rand() % 26; + char b = 'A' + rand() % 26; + nn_pushlstring(C, &a, 1); + return nn_pushlstring(C, &b, 1); + } + + if(action == NN_DATA_ECDH) { + char buf[32]; + memset(buf, 'e', 32); + return nn_pushlstring(C, buf, 32); + } + + if(action == NN_DATA_ECDSA_SIGN) { + return nn_pushstring(C, "epic signature bro"); + } + if(action == NN_DATA_ECDSA_VERIFY) { + req->checksig.sigpassed = strcmp(req->checksig.signature, "epic signature bro"); + return NN_OK; + } + if(C) nn_setError(C, "ne: data method not implemented"); return NN_EBADCALL; } diff --git a/src/neonucleus.c b/src/neonucleus.c index a986343..0224986 100644 --- a/src/neonucleus.c +++ b/src/neonucleus.c @@ -6318,13 +6318,6 @@ nn_DataCard nn_defaultDataCards[3] = { }, }; -typedef struct nn_DataKey { - bool isPublic; - unsigned short bitlen; - size_t bytelen; - char bytes[]; -} nn_DataKey; - static nn_Exit nn_dataHandler(nn_ComponentRequest *req) { nn_Context *ctx = req->ctx; nn_DataState *state = req->classState; @@ -6360,26 +6353,22 @@ static nn_Exit nn_dataHandler(nn_ComponentRequest *req) { if(user->action == NN_USER_SERIALIZE) { // its assumed bytelen is not ridiculous - size_t serlen = 2 + key->bytelen; + size_t serlen = 1 + key->bytelen; NN_VLA(unsigned char, ser, serlen); - unsigned short bitsAndPub = (key->bitlen * 2) + (key->isPublic ? 1 : 0); - ser[0] = (bitsAndPub >> 0) & 0xFF; - ser[1] = (bitsAndPub >> 8) & 0xFF; - nn_memcpy(ser + 2, key->bytes, key->bytelen); + ser[0] = key->isPublic ? 1 : 0; + nn_memcpy(ser + 1, key->bytes, key->bytelen); return nn_pushlstring(C, (const char *)ser, serlen); } if(user->action == NN_USER_DESERIALIZE) { size_t serlen = user->deserialize.len; const unsigned char *ser = (const unsigned char *)user->deserialize.data; - size_t bytelen = serlen - 2; - unsigned short bitsAndPub = ((unsigned short)ser[0]) + (((unsigned short)ser[1]) << 8); + size_t bytelen = serlen - 1; key = nn_alloc(ctx, sizeof(*key) + bytelen); if(key == NULL) return NN_ENOMEM; - key->isPublic = (bitsAndPub&1) != 0; - key->bitlen = bitsAndPub/2; + key->isPublic = ser[0] != 0; key->bytelen = bytelen; - nn_memcpy(key->bytes, ser + 2, key->bytelen); + nn_memcpy(key->bytes, ser + 1, key->bytelen); user->state = key; return NN_OK; } @@ -6618,15 +6607,175 @@ static nn_Exit nn_dataHandler(nn_ComponentRequest *req) { nn_setError(C, "invalid bit length (only 256-bit and 384-bit are supported)"); return NN_EBADCALL; } - // TODO: this is test code, no OOM check. Implement the correct version and make sure it handles OOMs correctly - size_t keylen = bitLen / 8; - nn_DataKey *key = nn_alloc(ctx, sizeof(*key) + keylen); - key->isPublic = true; - key->bitlen = bitLen; - key->bytelen = keylen; - nn_memset(key->bytes, 'A', key->bytelen); + dreq.action = NN_DATA_GENKEYS; + dreq.genkeybitsize = bitLen; + + e = state->handler(&dreq); + if(e) return e; + + // the keygen DARED succeed which means we must actually + // create those keys smh. Darn you crypto bros! + + size_t pubIdx = nn_getstacksize(C) - 2; + + size_t publen = 0, privlen = 0; + int pubUser = -1, privUser = -1; + nn_DataKey *pubKey = NULL, *privKey = NULL; + + const char *pubdata = nn_tolstring(C, pubIdx, &publen); + const char *privdata = nn_tolstring(C, pubIdx + 1, &privlen); + + pubKey = nn_alloc(ctx, sizeof(nn_DataKey) + publen); + if(pubKey == NULL) { + e = NN_ENOMEM; + goto considered_harmful; + } + pubKey->isPublic = true; + pubKey->bytelen = publen; + nn_memcpy(pubKey->bytes, pubdata, publen); + + privKey = nn_alloc(ctx, sizeof(nn_DataKey) + privlen); + if(privKey == NULL) { + e = NN_ENOMEM; + goto considered_harmful; + } + privKey->isPublic = false; + privKey->bytelen = privlen; + nn_memcpy(privKey->bytes, privdata, privlen); + + pubUser = nn_allocUserdata(C, pubKey, req->compAddress); + if(pubUser < 0) { + e = NN_ELIMIT; + goto considered_harmful; + } + + privUser = nn_allocUserdata(C, privKey, req->compAddress); + if(privUser < 0) { + e = NN_ELIMIT; + goto considered_harmful; + } + + e = nn_pushuserdata(C, pubUser); + if(e) goto considered_harmful; + + e = nn_pushuserdata(C, privUser); + if(e) goto considered_harmful; + + req->returnCount = 2; + return NN_OK; + + considered_harmful: + nn_free(ctx, pubKey, sizeof(*pubKey) + publen); + nn_free(ctx, privKey, sizeof(*privKey) + privlen); + if(pubUser >= 0) nn_freeUserdata(C, pubUser); + if(privUser >= 0) nn_freeUserdata(C, privUser); + return e; + } + + if(method == NN_DATANUM_DESERIALIZEKEY) { + if(nn_checkstring(C, 0, "bad argument #1 (string expected)")) return NN_EBADCALL; + if(nn_checkboolean(C, 1, "bad argument #2 (boolean expected)")) return NN_EBADCALL; + dreq.action = NN_DATA_VALIDATEKEY; + dreq.validatekey.buf = nn_tolstring(C, 0, &dreq.validatekey.len); + dreq.validatekey.isPublic = nn_toboolean(C, 1); + e = state->handler(&dreq); + if(e) return e; req->returnCount = 1; - return nn_pushuserdata(C, nn_allocUserdata(C, key, req->compAddress)); + size_t keylen = dreq.validatekey.len; + nn_DataKey *key = nn_alloc(ctx, sizeof(*key) + keylen); + if(key == NULL) return NN_ENOMEM; + key->isPublic = dreq.validatekey.isPublic; + key->bytelen = keylen; + nn_memcpy(key->bytes, dreq.validatekey.buf, key->bytelen); + int u = nn_allocUserdata(C, key, req->compAddress); + if(u < 0) { + nn_free(ctx, key, sizeof(*key) + keylen); + return NN_ELIMIT; + } + e = nn_pushuserdata(C, u); + if(e) { + nn_free(ctx, key, sizeof(*key) + keylen); + return e; + } + req->returnCount = 1; + return NN_OK; + } + + if(method == NN_DATANUM_ECDH) { + if(nn_checkuserdata(C, 0, "bad argument #1 (userdata expected)")) return NN_EBADCALL; + if(nn_checkuserdata(C, 1, "bad argument #2 (userdata expected)")) return NN_EBADCALL; + + const nn_DataKey *privateKey = nn_unwrapUserdata(C, nn_touserdata(C, 0), req->compAddress); + if(privateKey == NULL) { + nn_setError(C, "invalid or foreign private key"); + return NN_EBADCALL; + } + const nn_DataKey *publicKey = nn_unwrapUserdata(C, nn_touserdata(C, 1), req->compAddress); + if(publicKey == NULL) { + nn_setError(C, "invalid or foreign public key"); + return NN_EBADCALL; + } + + if(privateKey->isPublic == publicKey->isPublic) { + nn_setError(C, "illogical key pair"); + return NN_EBADCALL; + } + if(privateKey->isPublic) { + const nn_DataKey *tmp = privateKey; + privateKey = publicKey; + publicKey = tmp; + } + + dreq.action = NN_DATA_ECDH; + dreq.ecdh.privateKey = privateKey; + dreq.ecdh.publicKey = publicKey; + req->returnCount = 1; + return state->handler(&dreq); + } + + if(method == NN_DATANUM_ECDSA) { + if(nn_getstacksize(C) == 2) { + // we gotta generate a signature cuz some dingus + // wanted everyone to know it was them who wrote + // this text smh + if(nn_checkstring(C, 0, "bad argument #1 (string expected)")) return NN_EBADCALL; + if(nn_checkuserdata(C, 1, "bad argument #2 (userdata expected)")) return NN_EBADCALL; + + dreq.action = NN_DATA_ECDSA_SIGN; + dreq.sign.data = nn_tolstring(C, 0, &dreq.sign.len); + dreq.sign.privateKey = nn_unwrapUserdata(C, nn_touserdata(C, 1), req->compAddress); + if(dreq.sign.privateKey == NULL) { + nn_setError(C, "invalid or foreign private key"); + return NN_EBADCALL; + } + if(dreq.sign.privateKey->isPublic) { + nn_setError(C, "signatures require a private key"); + return NN_EBADCALL; + } + req->returnCount = 1; + return state->handler(&dreq); + } + // signature verification, cuz we do not trust the author label + if(nn_checkstring(C, 0, "bad argument #1 (string expected)")) return NN_EBADCALL; + if(nn_checkuserdata(C, 1, "bad argument #2 (userdata expected)")) return NN_EBADCALL; + if(nn_checkstring(C, 2, "bad argument #3 (string expected)")) return NN_EBADCALL; + dreq.action = NN_DATA_ECDSA_VERIFY; + dreq.checksig.data = nn_tolstring(C, 0, &dreq.checksig.datalen); + dreq.checksig.publicKey = nn_unwrapUserdata(C, nn_touserdata(C, 1), req->compAddress); + dreq.checksig.signature = nn_tolstring(C, 0, &dreq.checksig.siglen); + dreq.checksig.sigpassed = false; + if(dreq.checksig.publicKey == NULL) { + nn_setError(C, "invalid or foreign private key"); + return NN_EBADCALL; + } + if(!dreq.checksig.publicKey->isPublic) { + nn_setError(C, "validaing signatures require a public key"); + return NN_EBADCALL; + } + e = state->handler(&dreq); + if(e) return e; + req->returnCount = 1; + return nn_pushbool(C, dreq.checksig.sigpassed); } if(C) nn_setError(C, "data: not implemented yet"); diff --git a/src/neonucleus.h b/src/neonucleus.h index 8c565af..2066772 100644 --- a/src/neonucleus.h +++ b/src/neonucleus.h @@ -1809,6 +1809,9 @@ typedef enum nn_DataCardAction { // Generate an ECDH public/private pair of either 256 or 384 bits each. NN_DATA_GENKEYS, + // validate key, cuz we feel like it + NN_DATA_VALIDATEKEY, + // Does an ECDH pass, matching javax.crypto.KeyAgreement. // This generates a shared secret as binary data. // This is not an AES key as output, the AES key is often computed by either MD5 hashing @@ -1821,6 +1824,13 @@ typedef enum nn_DataCardAction { NN_DATA_ECDSA_VERIFY, } nn_DataCardAction; +// The representation of datacard key userdata +typedef struct nn_DataKey { + bool isPublic; + unsigned short bytelen; + char bytes[]; +} nn_DataKey; + typedef struct nn_DataCardRequest { nn_Context *ctx; nn_Computer *computer; @@ -1860,6 +1870,30 @@ typedef struct nn_DataCardRequest { const char *data; size_t datalen; }; + struct { + const char *buf; + size_t len; + bool isPublic; + } validatekey; + struct { + const nn_DataKey *publicKey; + const nn_DataKey *privateKey; + } ecdh; + struct { + const char *data; + size_t len; + const nn_DataKey *privateKey; + } sign; + struct { + const char *data; + size_t datalen; + const nn_DataKey *publicKey; + const char *signature; + size_t siglen; + // returns whether the signature actually passed + bool sigpassed; + } checksig; + int genkeybitsize; }; } nn_DataCardRequest;