data card and stuff

anything but device info
This commit is contained in:
2026-05-03 23:30:18 +03:00
parent cb0dcf03ad
commit 4a5f1d4d07
4 changed files with 323 additions and 8 deletions

View File

@@ -501,7 +501,11 @@ int main(int argc, char **argv) {
ncl_mountKeyboard(scrstate, "mainKB");
nn_Computer *c = nn_createComputer(u, NULL, NULL, ramTotal, 256, 256);
nn_setComputerEnvironment(c, (nn_Environment) {.userdata = NULL, .handler = ne_env});
nn_Environment cEnv = {
.userdata = NULL,
.handler = ne_env,
};
nn_setComputerEnvironment(c, cEnv);
nn_setCallBudget(c, 0);
nn_setTotalEnergy(c, allEnergy);
@@ -693,6 +697,8 @@ int main(int argc, char **argv) {
nn_removeEnergy(c, ncl_getScreenEnergyUsage(nn_getComponentState(screen)));
if(noIdle) nn_resetIdleTime(c);
// OC computers consume 0.5W when running, 0.05W when running but idle
nn_removeEnergy(c, nn_isComputerIdle(c) ? 0.05 : 0.5);
nn_Exit e = nn_tick(c);
if(e != NN_OK) {
printf("error: %s\n", nn_getError(c));

View File

@@ -113,11 +113,6 @@ void nn_free(nn_Context *ctx, void *memory, size_t size) {
}
void *nn_realloc(nn_Context *ctx, void *memory, size_t oldSize, size_t newSize) {
// nn_realloc passed memory (which is NULL here) as first argument
// to nn_alloc instead of ctx. nn_alloc dereferences it as a context
// struct to call ctx->alloc(), so this is a NULL pointer dereference.
// Confirmed by test_realloc crashing on nn_realloc(&ctx, NULL, 0, 64).
// Original: if(memory == NULL) return nn_alloc(memory, newSize); if(memory == ctx->alloc) return nn_alloc(memory, newSize);
if(memory == NULL) return nn_alloc(ctx, newSize);
if(memory == ctx->alloc) return nn_alloc(ctx, newSize);
if(newSize == 0) {
@@ -1221,6 +1216,11 @@ nn_Exit nn_startComputer(nn_Computer *computer) {
return err;
}
computer->archState = req.localState;
nn_EnvironmentRequest envreq;
envreq.userdata = computer->env.userdata;
envreq.computer = computer;
envreq.action = NN_ENV_POWERON;
computer->env.handler(&envreq);
return NN_OK;
}
@@ -1234,6 +1234,12 @@ void nn_stopComputer(nn_Computer *computer) {
req.action = NN_ARCH_DEINIT;
computer->arch.handler(&req);
computer->archState = NULL;
nn_EnvironmentRequest envreq;
envreq.userdata = computer->env.userdata;
envreq.computer = computer;
envreq.action = NN_ENV_POWEROFF;
computer->env.handler(&envreq);
}
computer->state = NN_BOOTUP;
for(size_t i = 0; i < computer->signalCount; i++) {
@@ -1609,6 +1615,11 @@ nn_Exit nn_tick(nn_Computer *computer) {
if(err) {
computer->state = NN_CRASHED;
nn_setErrorFromExit(computer, err);
nn_EnvironmentRequest envreq;
envreq.userdata = computer->env.userdata;
envreq.computer = computer;
envreq.action = NN_ENV_CRASHED;
computer->env.handler(&envreq);
return err;
}
return NN_OK;
@@ -1628,6 +1639,11 @@ nn_Exit nn_tickSynchronized(nn_Computer *computer) {
if(err) {
computer->state = NN_CRASHED;
nn_setErrorFromExit(computer, err);
nn_EnvironmentRequest envreq;
envreq.userdata = computer->env.userdata;
envreq.computer = computer;
envreq.action = NN_ENV_CRASHED;
computer->env.handler(&envreq);
return err;
}
return NN_OK;
@@ -5842,6 +5858,197 @@ nn_Component *nn_createGPU(
return c;
}
typedef enum nn_DataNum {
NN_DATANUM_GETLIMIT,
NN_DATANUM_DECODE64,
NN_DATANUM_ENCODE64,
NN_DATANUM_CRC32,
NN_DATANUM_MD5,
NN_DATANUM_SHA256,
NN_DATANUM_DEFLATE,
NN_DATANUM_INFLATE,
NN_DATANUM_ENCRYPT,
NN_DATANUM_DECRYPT,
NN_DATANUM_RANDOM,
NN_DATANUM_MAXRANDOM,
NN_DATANUM_GENKEYPAIR,
NN_DATANUM_ECDSA,
NN_DATANUM_ECDH,
NN_DATANUM_DESERIALIZEKEY,
NN_DATANUM_COUNT,
} nn_DataNum;
typedef struct nn_DataState {
nn_Context *ctx;
nn_DataCard dataCard;
nn_DataCardHandler *handler;
} nn_DataState;
nn_DataCard nn_defaultDataCards[3] = {
NN_INIT(nn_DataCard) {
.limit = NN_MiB,
.maxRandom = NN_KiB,
.canHash = true,
.canEncrypt = false,
.canECDH = false,
.canCompress = true,
.trivialCost = 0.2,
.trivialCostByte = 0.005,
.simpleCost = 1.0,
.simpleCostByte = 0.01,
.complexCost = 6.0,
.complexCostByte = 0.1,
.assymetricCost = 10.0,
},
NN_INIT(nn_DataCard) {
.limit = NN_MiB,
.maxRandom = NN_KiB,
.canHash = true,
.canEncrypt = true,
.canECDH = false,
.canCompress = true,
.trivialCost = 0.2,
.trivialCostByte = 0.005,
.simpleCost = 1.0,
.simpleCostByte = 0.01,
.complexCost = 6.0,
.complexCostByte = 0.1,
.assymetricCost = 10.0,
},
NN_INIT(nn_DataCard) {
.limit = NN_MiB,
.maxRandom = NN_KiB,
.canHash = true,
.canEncrypt = true,
.canECDH = true,
.canCompress = true,
.trivialCost = 0.2,
.trivialCostByte = 0.005,
.simpleCost = 1.0,
.simpleCostByte = 0.01,
.complexCost = 6.0,
.complexCostByte = 0.1,
.assymetricCost = 10.0,
},
};
static nn_Exit nn_dataHandler(nn_ComponentRequest *req) {
if(req->action == NN_COMP_SIGNAL) return NN_OK;
nn_Context *ctx = req->ctx;
nn_DataState *state = req->classState;
nn_Computer *C = req->computer;
nn_DataCard dataCard = state->dataCard;
nn_DataNum method = req->methodIdx;
if(req->action == NN_COMP_CHECKMETHOD) {
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) {
req->methodEnabled = dataCard.canCompress;
}
if(method == NN_DATANUM_ENCRYPT || method == NN_DATANUM_DECRYPT) {
req->methodEnabled = dataCard.canEncrypt;
}
if(method == NN_DATANUM_GENKEYPAIR || method == NN_DATANUM_ECDH || method == NN_DATANUM_ECDSA || method == NN_DATANUM_DESERIALIZEKEY) {
req->methodEnabled = dataCard.canECDH;
}
return NN_OK;
}
nn_DataCardRequest dreq;
dreq.ctx = ctx;
dreq.computer = C;
dreq.state = req->state;
dreq.dataCard = &state->dataCard;
nn_Exit e;
if(req->action == NN_COMP_DROP) {
dreq.action = NN_DATA_DROP;
state->handler(&dreq);
nn_free(ctx, state, sizeof(*state));
return NN_OK;
}
if(method == NN_DATANUM_GETLIMIT) {
req->returnCount = 1;
return nn_pushinteger(C, dataCard.limit);
}
if(method == NN_DATANUM_MAXRANDOM) {
req->returnCount = 1;
return nn_pushinteger(C, dataCard.maxRandom);
}
// TODO: the cool methods
if(C) nn_setError(C, "data: not implemented yet");
return NN_EBADCALL;
}
nn_Component *nn_createDataCard(nn_Universe *universe, const char *address, const nn_DataCard *dataCard, void *state, nn_DataCardHandler *handler) {
nn_Component *c = nn_createComponent(
universe, address, "data");
if(c == NULL) return NULL;
nn_Method methods[NN_DATANUM_COUNT] = {
[NN_DATANUM_GETLIMIT] = {"getLimit", "function(): integer - Get the buffer capacity of the card", NN_DIRECT},
[NN_DATANUM_DECODE64] = {"decode64", "function(data: string): string - Decodes the string as base64 data", NN_DIRECT},
[NN_DATANUM_ENCODE64] = {"encode64", "function(data: string): string - Encodes the string into base64 data", NN_DIRECT},
[NN_DATANUM_CRC32] = {"crc32", "function(data: string): string - Computes the CRC-32 hash of the data", NN_DIRECT},
[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_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},
[NN_DATANUM_MAXRANDOM] = {"getRandomLimit", "function(): integer - Maximum amount of secure random bytes the data card will generate", NN_DIRECT},
[NN_DATANUM_GENKEYPAIR] = {"generateKeyPair", "function(bitLen?: integer): userdata, userdata - Generates a pair of 2 ECDH keys; ec-public and ec-private respectively. Supports 256-bit and 384-bit keys.", NN_DIRECT},
[NN_DATANUM_ECDSA] = {"ecdsa", "function(data: string, key: userdata, sig: string?): string or boolean - Either generates a signature using a private key, or if signature is specified, verifies it with public key", NN_DIRECT},
[NN_DATANUM_ECDH] = {"ecdh", "function(privateKey: userdata, publicKey: userdata): string - Computes a shared secret using a private and public key from different pairs. The rule is, ecdh(a.private, b.public) == ecdh(b.private, a.public)", NN_DIRECT},
[NN_DATANUM_DESERIALIZEKEY] = {"deserializeKey", "function(data: string, type: string): userdata - Deserializes key data assuming a specific type. Public keys are of type ec-public, and private ones of type ec-private.", NN_DIRECT},
};
if(dataCard->canEncrypt && dataCard->canHash) {
methods[NN_DATANUM_SHA256].doc = "function(data: string, hmacKey: string?): string - Computes the SHA256 hash / HMAC";
methods[NN_DATANUM_MD5].doc = "function(data: string, hmacKey: string?): string - Computes the MD5 hash / HMAC";
}
nn_Exit e = nn_setComponentMethodsArray(
c, methods, NN_DATANUM_COUNT);
if(e) { nn_dropComponent(c); return NULL; }
nn_Context *ctx = &universe->ctx;
nn_DataState *cls = nn_alloc(ctx, sizeof(*cls));
if(cls == NULL) {
nn_dropComponent(c);
return NULL;
}
cls->ctx = ctx;
cls->dataCard = *dataCard;
cls->handler = handler;
nn_setComponentState(c, state);
nn_setComponentClassState(c, cls);
nn_setComponentHandler(c, nn_dataHandler);
return c;
}
typedef enum nn_ModemNum {
NN_MODEMNUM_ISWIRED,
NN_MODEMNUM_ISWIRELESS,
@@ -5934,7 +6141,7 @@ static nn_Exit nn_modemHandler(nn_ComponentRequest *req) {
nn_Component *nn_createModem(nn_Universe *universe, const char *address, const nn_Modem *modem, void *state, nn_ModemHandler *handler) {
nn_Component *c = nn_createComponent(
universe, address, "gpu");
universe, address, "modem");
if(c == NULL) return NULL;
const nn_Method methods[NN_MODEMNUM_COUNT] = {

View File

@@ -1594,6 +1594,109 @@ nn_Component *nn_createScreen(
nn_ScreenHandler *handler
);
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.
size_t limit;
// The maximum amount of secure random bytes that can be generated.
// In OC, this was hardcoded to 1024. Here, its configurable.
// Unlike the normal limit, this does not preallocate the maximum capacity,
// as the amount of bytes needed is known perfectly.
size_t maxRandom;
// Capabilities
bool canHash;
bool canEncrypt;
bool canECDH;
bool canCompress;
// Trivial operation cost (CRC32 and base64 encoding/decoding)
double trivialCost;
double trivialCostByte;
// Simple operation cost (MD5 and encryption/decryption)
double simpleCost;
double simpleCostByte;
double complexCost;
double complexCostByte;
// Assymetric operation cost (ECDH, ECDSA). ECDH has no per-byte cost, and ECDSA uses complexCostByte as it relies on SHA256.
double assymetricCost;
} nn_DataCard;
typedef enum nn_DataCardAction {
// Data card destroyed
NN_DATA_DROP,
// If you want to match the behavior of OC, which you should if you want
// data compressed or encrypted in OC to work in your emulators, you should
// aim to match what the JVM, com.google.common.hash.Hashing and javax.crypto do.
// For more details, see https://github.com/MightyPirates/OpenComputers/blob/master-MC1.7.10/src/main/scala/li/cil/oc/server/component/DataCard.scala
// encoding base64
NN_DATA_ENCODE64,
NN_DATA_DECODE64,
// hashing
// CRC32, little endian
NN_DATA_CRC32,
// SHA2-256 hash, optional HMAC key (javax.crypto HmacSHA256)
NN_DATA_SHA256,
// MD5 hash, optional HMAC key (javax.crypto HmacMD5)
NN_DATA_MD5,
// Deflate. To match OC, make it follow the ZLIB format, which has a 2 byte header.
NN_DATA_DEFLATE,
// Inflate. Should support the ZLIB format, as thats what OC uses, and GZIP support is optional.
NN_DATA_INFALTE,
// 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.
NN_DATA_ENCRYPT,
// Decrypt data, also using AES/CBC/PKCS5.
NN_DATA_DECRYPT,
// Meant to be *secure RNG*, and can generate anywhere between 1 and the data card's maxRandom.
NN_DATA_RANDOM,
// Generate an ECDH public/private pair of either 256 or 384 bits each.
NN_DATA_GENKEYS,
// 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
// the shared secret, or SHA256 hashing it and chopping the hash in half.
NN_DATA_ECDH,
// ECDSA algorithm, sign data using an ECDH (private) key.
NN_DATA_ECDSA_SIGN,
// ECDSA algorithm, verify data using an ECDH (public) key and signature.
NN_DATA_ECDSA_VERIFY,
} nn_DataCardAction;
typedef struct nn_DataCardRequest {
nn_Context *ctx;
nn_Computer *computer;
void *state;
const nn_DataCard *dataCard;
nn_DataCardAction action;
// TODO: the fields
} nn_DataCardRequest;
typedef nn_Exit (nn_DataCardHandler)(nn_DataCardRequest *req);
extern nn_DataCard nn_defaultDataCards[3];
nn_Component *nn_createDataCard(nn_Universe *universe, const char *address, const nn_DataCard *dataCard, void *state, nn_DataCardHandler *handler);
typedef struct nn_Modem {
// maximum range. Set to 0 for non-wireless modems
size_t maxRange;