commit fd71fd8fd418e13dc702656a1221e4a9db3da638
parent 39106a9d7c9a4f1f79ebe1bf3ce6c76a29aaff61
Author: luajitos <bbhbb2094@gmail.com>
Date: Sun, 30 Nov 2025 12:37:53 +0000
Fixed start menu, fixed crypto stubs, set-path permission
Diffstat:
16 files changed, 458 insertions(+), 668 deletions(-)
diff --git a/SAFEFS_EXAMPLE.lua b/SAFEFS_EXAMPLE.lua
@@ -1,205 +0,0 @@
--- SafeFS Usage Examples
--- Demonstrates how to use the SafeFS sandboxed filesystem
-
-local SafeFS = require("os.libs.safefs")
-
--- Assume we have a root filesystem node
--- local root_fs = ... (your RamDisk root node)
-
--- Example 1: Create SafeFS instance with allowed directories
-local sfs = SafeFS.new(root_fs, {
- "/tmp/*", -- Allow all of /tmp
- "/apps/com.dev.app/data/*", -- Allow app data directory
- "~/documents/*" -- Allow user documents (expands to /home/username/documents)
-}, "alice") -- Current user is "alice"
-
--- Example 2: Writing files
-local success, err = sfs:write("/tmp/test.txt", "Hello, SafeFS!")
-if success then
- print("File written successfully")
-else
- print("Error: " .. err)
-end
-
--- Example 3: Reading files
-local content, err = sfs:read("/tmp/test.txt")
-if content then
- print("Content: " .. content)
-else
- print("Error: " .. err)
-end
-
--- Example 4: Opening files with handles
-local file, err = sfs:open("/tmp/data.txt", "w")
-if file then
- file:write("Line 1\n")
- file:write("Line 2\n")
- file:write("Line 3\n")
- file:close()
- print("File written with handle")
-end
-
--- Read with handle
-local file, err = sfs:open("/tmp/data.txt", "r")
-if file then
- local line = file:read("*l")
- while line do
- print("Line: " .. line)
- line = file:read("*l")
- end
- file:close()
-end
-
--- Example 5: Directory listing
-local dirs, err = sfs:dirs("/tmp")
-if dirs then
- print("Directories in /tmp:")
- for _, dir in ipairs(dirs) do
- print(" " .. dir)
- end
-end
-
-local files, err = sfs:files("/tmp")
-if files then
- print("Files in /tmp:")
- for _, file in ipairs(files) do
- print(" " .. file)
- end
-end
-
--- Example 6: Path traversal protection
--- This will NOT work - trying to escape the sandbox
-local content, err = sfs:read("/tmp/../etc/passwd")
-if not content then
- print("Correctly blocked: " .. err)
-end
-
--- Example 7: Relative path within sandbox (this DOES work)
-sfs:mkdir("/tmp/subdir")
-sfs:write("/tmp/subdir/file.txt", "test")
--- Navigate using ..
-local content, err = sfs:read("/tmp/subdir/../test.txt")
-if content then
- print("Relative path worked: " .. content)
-end
-
--- Example 8: Tilde expansion
-local success, err = sfs:write("~/documents/note.txt", "My personal note")
--- This writes to /home/alice/documents/note.txt
-
--- Example 9: Check existence
-if sfs:exists("/tmp/test.txt") then
- print("File exists")
-end
-
-local fileType = sfs:getType("/tmp/test.txt")
-print("Type: " .. (fileType or "not found"))
-
--- Example 10: Delete files
-local success, err = sfs:delete("/tmp/test.txt")
-if success then
- print("File deleted")
-end
-
--- Example 11: Create nested directories
-sfs:mkdir("/tmp/a/b/c") -- Creates entire path
-sfs:write("/tmp/a/b/c/deep.txt", "Deep file")
-
--- Example 12: Append mode
-local file = sfs:open("/tmp/log.txt", "w")
-file:write("Initial log entry\n")
-file:close()
-
-local file = sfs:open("/tmp/log.txt", "a")
-file:write("Second log entry\n")
-file:write("Third log entry\n")
-file:close()
-
--- Example 13: Read entire file
-local content = sfs:read("/tmp/log.txt")
-print("Full log:\n" .. content)
-
--- Example 14: Use in LPM app sandbox
--- In your LPM implementation, create SafeFS for each app:
---[[
-function LPM:run(app_name)
- local app_dir = "/apps/com.example.myapp"
-
- -- Create SafeFS with only app's directories
- local safeFS = SafeFS.new(root_fs, {
- app_dir .. "/data/*",
- app_dir .. "/tmp/*",
- app_dir .. "/settings/*",
- "/tmp/*" -- Optional: allow system tmp
- })
-
- -- Pass to sandbox
- local sandbox = {
- fs = safeFS,
- -- ... other sandbox globals
- }
-
- -- App can now use:
- -- fs:write("$/data/save.txt", data) -- After resolving $ to app_dir
- -- fs:read("$/settings/config.txt")
-end
-]]--
-
--- Example 15: Multiple isolated instances
-local app1_fs = SafeFS.new(root_fs, {"/apps/com.app1/data/*"})
-local app2_fs = SafeFS.new(root_fs, {"/apps/com.app2/data/*"})
-
--- App 1 can only access its own data
-app1_fs:write("/apps/com.app1/data/secret.txt", "App 1 secret")
-
--- App 2 cannot access App 1's data
-local content, err = app2_fs:read("/apps/com.app1/data/secret.txt")
--- This will fail: "Path not in SafeFS"
-
--- Example 16: Path manipulation with fileName() and parentDir()
-local filename = sfs:fileName("/tmp/subdir/data.txt")
-print("Filename: " .. filename) -- Output: "data.txt"
-
-local parent = sfs:parentDir("/tmp/subdir/data.txt")
-print("Parent: " .. parent) -- Output: "/tmp/subdir"
-
--- Get grandparent
-local grandparent = sfs:parentDir(parent)
-print("Grandparent: " .. grandparent) -- Output: "/tmp"
-
--- Example 17: List all files with their full paths
-local function listFilesRecursive(safeFS, path, prefix)
- prefix = prefix or ""
- local files = safeFS:files(path)
- local dirs = safeFS:dirs(path)
-
- if files then
- for _, file in ipairs(files) do
- print(prefix .. path .. "/" .. file)
- end
- end
-
- if dirs then
- for _, dir in ipairs(dirs) do
- listFilesRecursive(safeFS, path .. "/" .. dir, prefix .. " ")
- end
- end
-end
-
-print("\nAll files in /tmp:")
-listFilesRecursive(sfs, "/tmp")
-
--- Example 18: Check if dirs() and files() return names only
-local dirs = sfs:dirs("/tmp")
-print("\nDirectories in /tmp (names only):")
-for _, dir in ipairs(dirs) do
- print(" " .. dir) -- Just the name, not a node object
-end
-
-local files = sfs:files("/tmp")
-print("\nFiles in /tmp (names only):")
-for _, file in ipairs(files) do
- print(" " .. file) -- Just the name, not a node object
-end
-
-print("\nSafeFS examples complete!")
diff --git a/build.sh b/build.sh
@@ -228,8 +228,7 @@ ${CC} ${CFLAGS} -DBARE_METAL -I./crypto ${LUAJIT_INCLUDE} -c crypto/Salsa20-Poly
# Key derivation
${CC} ${CFLAGS} -DBARE_METAL -I./crypto ${LUAJIT_INCLUDE} -c crypto/PBKDF2.c -o build/PBKDF2.o
${CC} ${CFLAGS} -DBARE_METAL -I./crypto ${LUAJIT_INCLUDE} -c crypto/Argon2.c -o build/Argon2.o
-# HKDF disabled - needs HMAC functions exposed from PBKDF2
-# ${CC} ${CFLAGS} -DBARE_METAL -I./crypto ${LUAJIT_INCLUDE} -c crypto/HKDF.c -o build/HKDF.o
+${CC} ${CFLAGS} -DBARE_METAL -I./crypto ${LUAJIT_INCLUDE} -c crypto/HKDF.c -o build/HKDF.o
# Stubs for unimplemented functions
${CC} ${CFLAGS} -DBARE_METAL -I./crypto ${LUAJIT_INCLUDE} -c crypto/stubs.c -o build/stubs.o
@@ -294,7 +293,7 @@ if [ -f "$LIBGCC_PATH" ]; then
build/crypto_baremetal.o build/CSPRNG.o build/Ed25519.o build/X25519.o build/Curve25519.o \
build/AES-256-GCM.o build/AES-128-GCM.o build/ChaCha20-Poly1305.o build/XChaCha20-Poly1305.o \
build/Serpent-256-GCM.o build/Twofish-256-GCM.o build/Salsa20-Poly1305.o \
- build/PBKDF2.o build/Argon2.o build/stubs.o build/RSA.o build/P256.o \
+ build/PBKDF2.o build/Argon2.o build/HKDF.o build/stubs.o build/RSA.o build/P256.o \
build/Kyber.o build/Dilithium.o \
build/hash.o build/MD5.o build/SHA1.o build/SHA256.o build/SHA3.o build/BLAKE2.o \
build/CSPRNG_Lua.o build/Ed25519_Lua.o build/X25519_Lua.o build/Hash_Lua.o \
@@ -312,7 +311,7 @@ else
build/crypto_baremetal.o build/CSPRNG.o build/Ed25519.o build/X25519.o build/Curve25519.o \
build/AES-256-GCM.o build/AES-128-GCM.o build/ChaCha20-Poly1305.o build/XChaCha20-Poly1305.o \
build/Serpent-256-GCM.o build/Twofish-256-GCM.o build/Salsa20-Poly1305.o \
- build/PBKDF2.o build/Argon2.o build/stubs.o build/RSA.o build/P256.o \
+ build/PBKDF2.o build/Argon2.o build/HKDF.o build/stubs.o build/RSA.o build/P256.o \
build/Kyber.o build/Dilithium.o \
build/hash.o build/MD5.o build/SHA1.o build/SHA256.o build/SHA3.o build/BLAKE2.o \
build/CSPRNG_Lua.o build/Ed25519_Lua.o build/X25519_Lua.o build/Hash_Lua.o \
diff --git a/crypto/HKDF.c b/crypto/HKDF.c
@@ -1,12 +1,90 @@
/*
* HKDF Implementation
- * RFC 5869
+ * RFC 5869 - HMAC-based Extract-and-Expand Key Derivation Function
*/
#include "HKDF.h"
-#include "PBKDF2.h" /* For HMAC-SHA256 */
#include <string.h>
+/* Use existing SHA-256 implementation */
+typedef struct {
+ uint32_t state[8];
+ uint64_t count;
+ uint8_t buffer[64];
+} sha256_context;
+
+extern void sha256_init(sha256_context *ctx);
+extern void sha256_update(sha256_context *ctx, const uint8_t *data, size_t len);
+extern void sha256_final(sha256_context *ctx, uint8_t digest[32]);
+extern void sha256(const uint8_t *data, size_t len, uint8_t digest[32]);
+
+#define SHA256_BLOCK_SIZE 64
+#define SHA256_DIGEST_SIZE 32
+
+/* HMAC-SHA256 context for incremental updates */
+typedef struct {
+ sha256_context inner;
+ uint8_t o_key_pad[SHA256_BLOCK_SIZE];
+} hmac_sha256_context;
+
+static void hmac_sha256_init(hmac_sha256_context *ctx, const uint8_t *key, size_t key_len) {
+ uint8_t k_pad[SHA256_BLOCK_SIZE];
+ uint8_t tk[SHA256_DIGEST_SIZE];
+
+ /* If key is longer than block size, hash it first */
+ if (key_len > SHA256_BLOCK_SIZE) {
+ sha256(key, key_len, tk);
+ key = tk;
+ key_len = SHA256_DIGEST_SIZE;
+ }
+
+ /* Prepare padded key */
+ memset(k_pad, 0, sizeof(k_pad));
+ memcpy(k_pad, key, key_len);
+
+ /* Store outer key pad for finalization */
+ for (int i = 0; i < SHA256_BLOCK_SIZE; i++) {
+ ctx->o_key_pad[i] = k_pad[i] ^ 0x5c;
+ k_pad[i] ^= 0x36;
+ }
+
+ /* Initialize inner hash with i_key_pad */
+ sha256_init(&ctx->inner);
+ sha256_update(&ctx->inner, k_pad, SHA256_BLOCK_SIZE);
+
+ memset(k_pad, 0, sizeof(k_pad));
+}
+
+static void hmac_sha256_update(hmac_sha256_context *ctx, const uint8_t *data, size_t len) {
+ sha256_update(&ctx->inner, data, len);
+}
+
+static void hmac_sha256_final(hmac_sha256_context *ctx, uint8_t output[32]) {
+ uint8_t inner_hash[SHA256_DIGEST_SIZE];
+
+ /* Finalize inner hash */
+ sha256_final(&ctx->inner, inner_hash);
+
+ /* Compute outer hash: H(o_key_pad || inner_hash) */
+ sha256_context outer;
+ sha256_init(&outer);
+ sha256_update(&outer, ctx->o_key_pad, SHA256_BLOCK_SIZE);
+ sha256_update(&outer, inner_hash, SHA256_DIGEST_SIZE);
+ sha256_final(&outer, output);
+
+ memset(inner_hash, 0, sizeof(inner_hash));
+}
+
+/* Single-shot HMAC-SHA256 */
+static void hmac_sha256(const uint8_t *key, size_t key_len,
+ const uint8_t *data, size_t data_len,
+ uint8_t output[32]) {
+ hmac_sha256_context ctx;
+ hmac_sha256_init(&ctx, key, key_len);
+ hmac_sha256_update(&ctx, data, data_len);
+ hmac_sha256_final(&ctx, output);
+}
+
/* HKDF-Extract using HMAC-SHA256 */
void hkdf_sha256_extract(const uint8_t *salt, size_t salt_len,
const uint8_t *ikm, size_t ikm_len,
diff --git a/crypto/Hash_Lua.c b/crypto/Hash_Lua.c
@@ -242,6 +242,27 @@ int l_hash_sha3_512(lua_State *L) {
return 1;
}
+/* hash(string) via __call metamethod - SHA3-256 hash (default)
+ * When called as crypto.hash("data"), the table is arg 1, data is arg 2 */
+int l_hash_call(lua_State *L) {
+ size_t input_len;
+ /* Argument 2 because arg 1 is the hash table itself when using __call */
+ const char *input = luaL_checklstring(L, 2, &input_len);
+
+ uint8_t digest[32];
+ sha3_256((const uint8_t*)input, input_len, digest);
+
+ size_t b64_len;
+ char *b64 = base64_encode(digest, 32, &b64_len);
+ if (!b64) {
+ return luaL_error(L, "Base64 encoding failed");
+ }
+
+ lua_pushlstring(L, b64, b64_len);
+ free(b64);
+ return 1;
+}
+
/* hash.BLAKE2b(string) or hash.BLAKE2b_256(string) - BLAKE2b-256 hash */
int l_hash_blake2b(lua_State *L) {
size_t input_len;
diff --git a/crypto/Hash_Lua.h b/crypto/Hash_Lua.h
@@ -15,6 +15,7 @@ char* base64_encode(const uint8_t *data, size_t len, size_t *out_len);
/* Hash function bindings */
int l_hash(lua_State *L); /* SHA-256 hash returning base64 */
+int l_hash_call(lua_State *L); /* __call metamethod: SHA3-256 (default) */
int l_hash_sha512(lua_State *L); /* SHA-512 hash */
int l_hash_sha1(lua_State *L); /* SHA-1 hash (legacy) */
int l_hash_md5(lua_State *L); /* MD5 hash (binary or base64 input) */
diff --git a/crypto/RSA.c b/crypto/RSA.c
@@ -509,3 +509,286 @@ void rsa_print_public_key(const rsa_public_key *key) {
printf("\n");
}
+/* ============================================================================
+ * RSA-PSS (Probabilistic Signature Scheme) Implementation
+ * RFC 8017 - PKCS #1: RSA Cryptography Specifications Version 2.2
+ * ========================================================================= */
+
+/* SHA-256 function (from hashing/SHA256.c) */
+extern void sha256(const uint8_t *data, size_t len, uint8_t digest[32]);
+
+/**
+ * MGF1 - Mask Generation Function based on SHA-256
+ * RFC 8017 Section B.2.1
+ */
+static void mgf1_sha256(const uint8_t *seed, size_t seed_len,
+ uint8_t *mask, size_t mask_len) {
+ uint8_t counter[4];
+ size_t offset = 0;
+ uint32_t count = 0;
+ uint8_t hash_input[256 + 4]; /* Max seed length + counter */
+ uint8_t hash[32];
+
+ if (seed_len > 256) seed_len = 256; /* Safety limit */
+
+ while (offset < mask_len) {
+ /* Convert counter to big-endian bytes */
+ counter[0] = (count >> 24) & 0xFF;
+ counter[1] = (count >> 16) & 0xFF;
+ counter[2] = (count >> 8) & 0xFF;
+ counter[3] = count & 0xFF;
+
+ /* Hash seed || counter */
+ memcpy(hash_input, seed, seed_len);
+ memcpy(hash_input + seed_len, counter, 4);
+
+ sha256(hash_input, seed_len + 4, hash);
+
+ /* Copy to mask */
+ size_t to_copy = (mask_len - offset < 32) ? (mask_len - offset) : 32;
+ memcpy(mask + offset, hash, to_copy);
+
+ offset += to_copy;
+ count++;
+ }
+}
+
+/**
+ * EMSA-PSS-ENCODE operation
+ * RFC 8017 Section 9.1.1
+ */
+static int emsa_pss_encode(const uint8_t *m_hash, size_t h_len,
+ size_t em_bits, size_t s_len,
+ uint8_t *em, size_t em_len) {
+ /* PSS encoding requires em_len >= h_len + s_len + 2 */
+ if (em_len < h_len + s_len + 2) {
+ return -1;
+ }
+
+ /* Generate random salt */
+ uint8_t salt[64];
+ if (s_len > 64) s_len = 64; /* Safety limit */
+ get_random_bytes(salt, s_len);
+
+ /* M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt */
+ uint8_t m_prime[8 + 64 + 64]; /* 8 padding + max hash + max salt */
+ memset(m_prime, 0, 8);
+ memcpy(m_prime + 8, m_hash, h_len);
+ memcpy(m_prime + 8 + h_len, salt, s_len);
+
+ /* H = Hash(M') */
+ uint8_t h[32];
+ sha256(m_prime, 8 + h_len + s_len, h);
+
+ /* Generate DB = PS || 0x01 || salt */
+ size_t ps_len = em_len - s_len - h_len - 2;
+ uint8_t *db = calloc(em_len - h_len - 1, 1); /* Zero-initialized PS */
+ if (!db) return -1;
+
+ db[ps_len] = 0x01;
+ memcpy(db + ps_len + 1, salt, s_len);
+
+ /* dbMask = MGF(H, emLen - hLen - 1) */
+ size_t db_len = em_len - h_len - 1;
+ uint8_t *db_mask = malloc(db_len);
+ if (!db_mask) {
+ free(db);
+ return -1;
+ }
+
+ mgf1_sha256(h, h_len, db_mask, db_len);
+
+ /* maskedDB = DB xor dbMask */
+ for (size_t i = 0; i < db_len; i++) {
+ db[i] ^= db_mask[i];
+ }
+
+ /* Set leftmost bits of maskedDB to zero (8*emLen - emBits bits) */
+ size_t mask_bits = 8 * em_len - em_bits;
+ if (mask_bits > 0 && mask_bits < 8) {
+ db[0] &= (0xFF >> mask_bits);
+ }
+
+ /* EM = maskedDB || H || 0xbc */
+ memcpy(em, db, db_len);
+ memcpy(em + db_len, h, h_len);
+ em[em_len - 1] = 0xbc;
+
+ free(db);
+ free(db_mask);
+ return 0;
+}
+
+/**
+ * EMSA-PSS-VERIFY operation
+ * RFC 8017 Section 9.1.2
+ */
+static int emsa_pss_verify(const uint8_t *m_hash, size_t h_len,
+ const uint8_t *em, size_t em_len,
+ size_t em_bits, size_t s_len) {
+ /* Check minimum length */
+ if (em_len < h_len + s_len + 2) {
+ return -1;
+ }
+
+ /* Check trailer field (0xbc) */
+ if (em[em_len - 1] != 0xbc) {
+ return -1;
+ }
+
+ /* Split EM = maskedDB || H */
+ size_t db_len = em_len - h_len - 1;
+ const uint8_t *masked_db = em;
+ const uint8_t *h = em + db_len;
+
+ /* Check leftmost bits of maskedDB are zero */
+ size_t mask_bits = 8 * em_len - em_bits;
+ if (mask_bits > 0 && mask_bits < 8) {
+ uint8_t mask = (0xFF << (8 - mask_bits)) & 0xFF;
+ if ((masked_db[0] & mask) != 0) {
+ return -1;
+ }
+ }
+
+ /* dbMask = MGF(H, emLen - hLen - 1) */
+ uint8_t *db_mask = malloc(db_len);
+ if (!db_mask) return -1;
+
+ mgf1_sha256(h, h_len, db_mask, db_len);
+
+ /* DB = maskedDB xor dbMask */
+ uint8_t *db = malloc(db_len);
+ if (!db) {
+ free(db_mask);
+ return -1;
+ }
+
+ for (size_t i = 0; i < db_len; i++) {
+ db[i] = masked_db[i] ^ db_mask[i];
+ }
+
+ /* Set leftmost bits to zero */
+ if (mask_bits > 0 && mask_bits < 8) {
+ db[0] &= (0xFF >> mask_bits);
+ }
+
+ /* Verify DB = PS || 0x01 || salt */
+ size_t ps_len = em_len - s_len - h_len - 2;
+
+ /* Check PS is all zeros */
+ for (size_t i = 0; i < ps_len; i++) {
+ if (db[i] != 0x00) {
+ free(db);
+ free(db_mask);
+ return -1;
+ }
+ }
+
+ /* Check 0x01 separator */
+ if (db[ps_len] != 0x01) {
+ free(db);
+ free(db_mask);
+ return -1;
+ }
+
+ /* Extract salt */
+ uint8_t *salt = db + ps_len + 1;
+
+ /* M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt */
+ uint8_t m_prime[8 + 64 + 64];
+ memset(m_prime, 0, 8);
+ memcpy(m_prime + 8, m_hash, h_len);
+ memcpy(m_prime + 8 + h_len, salt, s_len);
+
+ /* H' = Hash(M') */
+ uint8_t h_prime[32];
+ sha256(m_prime, 8 + h_len + s_len, h_prime);
+
+ /* Verify H == H' */
+ int result = (memcmp(h, h_prime, h_len) == 0) ? 0 : -1;
+
+ free(db);
+ free(db_mask);
+ return result;
+}
+
+/**
+ * RSA-PSS Sign
+ * RFC 8017 Section 8.1.1
+ */
+int rsa_sign_pss(const rsa_private_key *key,
+ const uint8_t *message, size_t msg_len,
+ uint8_t *signature, size_t *sig_len) {
+ /* PSS parameters (using SHA-256) */
+ const size_t h_len = 32; /* SHA-256 output length */
+ const size_t s_len = 32; /* Salt length (same as hash length) */
+
+ /* Message must be a SHA-256 hash */
+ if (msg_len != h_len) {
+ fprintf(stderr, "RSA-PSS: Message must be a SHA-256 hash (32 bytes)\n");
+ return -1;
+ }
+
+ size_t em_len = key->n_len;
+ size_t em_bits = em_len * 8 - 1; /* One less than modulus bits */
+
+ if (em_len < h_len + s_len + 2) {
+ fprintf(stderr, "RSA-PSS: Key too short for PSS padding\n");
+ return -1;
+ }
+
+ /* EMSA-PSS encoding */
+ uint8_t *em = malloc(em_len);
+ if (!em) return -1;
+
+ if (emsa_pss_encode(message, h_len, em_bits, s_len, em, em_len) != 0) {
+ free(em);
+ return -1;
+ }
+
+ /* RSA signature primitive (decrypt with private key) */
+ int result = rsa_decrypt_raw(key, em, em_len, signature, sig_len);
+ free(em);
+ return result;
+}
+
+/**
+ * RSA-PSS Verify
+ * RFC 8017 Section 8.1.2
+ */
+int rsa_verify_pss(const rsa_public_key *key,
+ const uint8_t *message, size_t msg_len,
+ const uint8_t *signature, size_t sig_len) {
+ /* PSS parameters (using SHA-256) */
+ const size_t h_len = 32; /* SHA-256 output length */
+ const size_t s_len = 32; /* Salt length (same as hash length) */
+
+ /* Message must be a SHA-256 hash */
+ if (msg_len != h_len) {
+ fprintf(stderr, "RSA-PSS: Message must be a SHA-256 hash (32 bytes)\n");
+ return -1;
+ }
+
+ if (sig_len != key->n_len) {
+ return -1;
+ }
+
+ size_t em_len = key->n_len;
+ size_t em_bits = em_len * 8 - 1;
+
+ /* RSA verification primitive (encrypt with public key) */
+ uint8_t *em = malloc(em_len);
+ if (!em) return -1;
+
+ size_t recovered_len;
+ if (rsa_encrypt_raw(key, signature, sig_len, em, &recovered_len) != 0) {
+ free(em);
+ return -1;
+ }
+
+ /* EMSA-PSS verification */
+ int result = emsa_pss_verify(message, h_len, em, em_len, em_bits, s_len);
+ free(em);
+ return result;
+}
+
diff --git a/crypto/crypto.c b/crypto/crypto.c
@@ -1261,9 +1261,9 @@ int luaopen_crypto(lua_State *L) {
lua_pushcfunction(L, l_hash_blake2b_512);
lua_setfield(L, -2, "BLAKE2b_512");
- /* Create metatable with __call for SHA-256 */
+ /* Create metatable with __call for SHA3-256 (default hash) */
lua_newtable(L); /* metatable */
- lua_pushcfunction(L, l_hash);
+ lua_pushcfunction(L, l_hash_call);
lua_setfield(L, -2, "__call");
lua_setmetatable(L, -2); /* Set metatable on hash table */
diff --git a/crypto/stubs.c b/crypto/stubs.c
@@ -1,34 +1,15 @@
/*
* Stubs for unimplemented crypto functions
- * These allow the build to succeed while the full implementations are completed
+ *
+ * Note: The following functions are now fully implemented:
+ * - hkdf_sha256() - in HKDF.c
+ * - rsa_sign_pss() - in RSA.c
+ * - rsa_verify_pss() - in RSA.c
+ *
+ * This file is kept for any future stub functions that may be needed.
*/
#include <stdint.h>
#include <stddef.h>
-/* HKDF stub - returns error */
-int hkdf_sha256(const uint8_t *salt, size_t salt_len,
- const uint8_t *ikm, size_t ikm_len,
- const uint8_t *info, size_t info_len,
- uint8_t *okm, size_t okm_len) {
- (void)salt; (void)salt_len; (void)ikm; (void)ikm_len;
- (void)info; (void)info_len; (void)okm; (void)okm_len;
- return -1; /* Not implemented */
-}
-
-/* RSA PSS stubs - return error */
-int rsa_sign_pss(const uint8_t *private_key, size_t key_len,
- const uint8_t *message, size_t message_len,
- uint8_t *signature, size_t *signature_len) {
- (void)private_key; (void)key_len; (void)message; (void)message_len;
- (void)signature; (void)signature_len;
- return -1; /* Not implemented */
-}
-
-int rsa_verify_pss(const uint8_t *public_key, size_t key_len,
- const uint8_t *message, size_t message_len,
- const uint8_t *signature, size_t signature_len) {
- (void)public_key; (void)key_len; (void)message; (void)message_len;
- (void)signature; (void)signature_len;
- return -1; /* Not implemented */
-}
+/* No stubs currently needed - all crypto functions are implemented */
diff --git a/iso_includes/apps/com.luajitos.installer/icon.png b/iso_includes/apps/com.luajitos.installer/icon.png
Binary files differ.
diff --git a/iso_includes/apps/com.luajitos.lunareditor/src/editor.lua b/iso_includes/apps/com.luajitos.lunareditor/src/editor.lua
@@ -1,375 +0,0 @@
--- Lunar Editor - Simple Text Editor for LuajitOS
-
-local WINDOW_WIDTH = 600
-local WINDOW_HEIGHT = 400
-local CHAR_WIDTH = 8
-local CHAR_HEIGHT = 16
-local PADDING = 4
-local LINE_NUMBER_WIDTH = 40
-
--- Editor state
-local state = {
- lines = {""}, -- Array of text lines
- cursorLine = 1, -- Current line (1-indexed)
- cursorCol = 1, -- Current column (1-indexed)
- scrollY = 0, -- Vertical scroll offset (lines)
- scrollX = 0, -- Horizontal scroll offset (chars)
- filename = nil, -- Current file path
- modified = false, -- Has unsaved changes
- selecting = false, -- Is selecting text
- selStartLine = nil,
- selStartCol = nil
-}
-
--- Colors
-local COLORS = {
- background = 0x1E1E2E, -- Dark background
- text = 0xCDD6F4, -- Light text
- lineNumbers = 0x6C7086, -- Muted line numbers
- lineNumberBg = 0x181825, -- Darker bg for line numbers
- cursor = 0xF5E0DC, -- Cursor color
- cursorLine = 0x313244, -- Current line highlight
- selection = 0x45475A, -- Selection background
- statusBar = 0x313244, -- Status bar background
- statusText = 0xBAC2DE -- Status bar text
-}
-
--- Create main window
-local window = app:newWindow("Lunar Editor", WINDOW_WIDTH, WINDOW_HEIGHT, true)
-
--- Calculate visible area
-local function getVisibleLines()
- local contentHeight = window.height - CHAR_HEIGHT - PADDING * 2 -- Subtract status bar
- return math.floor(contentHeight / CHAR_HEIGHT)
-end
-
-local function getVisibleCols()
- local contentWidth = window.width - LINE_NUMBER_WIDTH - PADDING * 2
- return math.floor(contentWidth / CHAR_WIDTH)
-end
-
--- Ensure cursor is visible
-local function ensureCursorVisible()
- local visibleLines = getVisibleLines()
- local visibleCols = getVisibleCols()
-
- -- Vertical scroll
- if state.cursorLine <= state.scrollY then
- state.scrollY = state.cursorLine - 1
- elseif state.cursorLine > state.scrollY + visibleLines then
- state.scrollY = state.cursorLine - visibleLines
- end
-
- -- Horizontal scroll
- if state.cursorCol <= state.scrollX then
- state.scrollX = state.cursorCol - 1
- elseif state.cursorCol > state.scrollX + visibleCols then
- state.scrollX = state.cursorCol - visibleCols
- end
-
- if state.scrollY < 0 then state.scrollY = 0 end
- if state.scrollX < 0 then state.scrollX = 0 end
-end
-
--- Get current line text
-local function getCurrentLine()
- return state.lines[state.cursorLine] or ""
-end
-
--- Set current line text
-local function setCurrentLine(text)
- state.lines[state.cursorLine] = text
- state.modified = true
-end
-
--- Insert character at cursor
-local function insertChar(char)
- local line = getCurrentLine()
- local before = string.sub(line, 1, state.cursorCol - 1)
- local after = string.sub(line, state.cursorCol)
- setCurrentLine(before .. char .. after)
- state.cursorCol = state.cursorCol + 1
-end
-
--- Delete character before cursor (backspace)
-local function deleteCharBefore()
- if state.cursorCol > 1 then
- local line = getCurrentLine()
- local before = string.sub(line, 1, state.cursorCol - 2)
- local after = string.sub(line, state.cursorCol)
- setCurrentLine(before .. after)
- state.cursorCol = state.cursorCol - 1
- elseif state.cursorLine > 1 then
- -- Join with previous line
- local currentLine = getCurrentLine()
- local prevLine = state.lines[state.cursorLine - 1]
- state.cursorCol = #prevLine + 1
- state.lines[state.cursorLine - 1] = prevLine .. currentLine
- table.remove(state.lines, state.cursorLine)
- state.cursorLine = state.cursorLine - 1
- state.modified = true
- end
-end
-
--- Delete character at cursor (delete key)
-local function deleteCharAt()
- local line = getCurrentLine()
- if state.cursorCol <= #line then
- local before = string.sub(line, 1, state.cursorCol - 1)
- local after = string.sub(line, state.cursorCol + 1)
- setCurrentLine(before .. after)
- elseif state.cursorLine < #state.lines then
- -- Join with next line
- state.lines[state.cursorLine] = line .. state.lines[state.cursorLine + 1]
- table.remove(state.lines, state.cursorLine + 1)
- state.modified = true
- end
-end
-
--- Insert new line (enter key)
-local function insertNewLine()
- local line = getCurrentLine()
- local before = string.sub(line, 1, state.cursorCol - 1)
- local after = string.sub(line, state.cursorCol)
- setCurrentLine(before)
- table.insert(state.lines, state.cursorLine + 1, after)
- state.cursorLine = state.cursorLine + 1
- state.cursorCol = 1
- state.modified = true
-end
-
--- Move cursor
-local function moveCursor(dLine, dCol)
- state.cursorLine = state.cursorLine + dLine
- state.cursorCol = state.cursorCol + dCol
-
- -- Clamp line
- if state.cursorLine < 1 then state.cursorLine = 1 end
- if state.cursorLine > #state.lines then state.cursorLine = #state.lines end
-
- -- Clamp column
- local lineLen = #getCurrentLine()
- if state.cursorCol < 1 then state.cursorCol = 1 end
- if state.cursorCol > lineLen + 1 then state.cursorCol = lineLen + 1 end
-
- ensureCursorVisible()
-end
-
--- Move to start/end of line
-local function moveToLineStart()
- state.cursorCol = 1
- ensureCursorVisible()
-end
-
-local function moveToLineEnd()
- state.cursorCol = #getCurrentLine() + 1
- ensureCursorVisible()
-end
-
--- Load file
-local function loadFile(path)
- local content = nil
- if fs and fs.read then
- content = fs:read(path)
- elseif CRamdiskOpen then
- local handle = CRamdiskOpen(path, "r")
- if handle then
- content = CRamdiskRead(handle)
- CRamdiskClose(handle)
- end
- end
-
- if content then
- state.lines = {}
- for line in (content .. "\n"):gmatch("([^\n]*)\n") do
- table.insert(state.lines, line)
- end
- if #state.lines == 0 then
- state.lines = {""}
- end
- state.filename = path
- state.modified = false
- state.cursorLine = 1
- state.cursorCol = 1
- state.scrollX = 0
- state.scrollY = 0
- window:markDirty()
- return true
- end
- return false
-end
-
--- Save file
-local function saveFile(path)
- path = path or state.filename
- if not path then
- -- Show save dialog
- Dialog.fileSave("/home", "untitled.txt"):onSuccess(function(filepath)
- saveFile(filepath)
- end):show()
- return false
- end
-
- local content = table.concat(state.lines, "\n")
- local success = false
-
- if fs and fs.write then
- success = fs:write(path, content)
- elseif CRamdiskOpen then
- local handle = CRamdiskOpen(path, "w")
- if handle then
- CRamdiskWrite(handle, content)
- CRamdiskClose(handle)
- success = true
- end
- end
-
- if success then
- state.filename = path
- state.modified = false
- window:markDirty()
- end
- return success
-end
-
--- Open file dialog
-local function openFileDialog()
- Dialog.fileOpen("/home"):onSuccess(function(filepath)
- loadFile(filepath)
- end):show()
-end
-
--- Draw callback
-window.onDraw = function(gfx)
- local w = gfx:getWidth()
- local h = gfx:getHeight()
-
- -- Background
- gfx:fillRect(0, 0, w, h, COLORS.background)
-
- -- Line number background
- gfx:fillRect(0, 0, LINE_NUMBER_WIDTH, h - CHAR_HEIGHT, COLORS.lineNumberBg)
-
- -- Calculate visible range
- local visibleLines = getVisibleLines()
- local startLine = state.scrollY + 1
- local endLine = math.min(startLine + visibleLines - 1, #state.lines)
-
- -- Draw lines
- local y = PADDING
- for i = startLine, endLine do
- local line = state.lines[i] or ""
- local lineY = y + (i - startLine) * CHAR_HEIGHT
-
- -- Current line highlight
- if i == state.cursorLine then
- gfx:fillRect(LINE_NUMBER_WIDTH, lineY, w - LINE_NUMBER_WIDTH, CHAR_HEIGHT, COLORS.cursorLine)
- end
-
- -- Line number
- local lineNum = tostring(i)
- local numX = LINE_NUMBER_WIDTH - #lineNum * CHAR_WIDTH - 4
- gfx:drawText(numX, lineY, lineNum, COLORS.lineNumbers)
-
- -- Line text (with horizontal scroll)
- local visibleText = string.sub(line, state.scrollX + 1)
- gfx:drawText(LINE_NUMBER_WIDTH + PADDING, lineY, visibleText, COLORS.text)
-
- -- Cursor (on current line)
- if i == state.cursorLine then
- local cursorX = LINE_NUMBER_WIDTH + PADDING + (state.cursorCol - state.scrollX - 1) * CHAR_WIDTH
- if cursorX >= LINE_NUMBER_WIDTH then
- gfx:fillRect(cursorX, lineY, 2, CHAR_HEIGHT, COLORS.cursor)
- end
- end
- end
-
- -- Status bar
- local statusY = h - CHAR_HEIGHT
- gfx:fillRect(0, statusY, w, CHAR_HEIGHT, COLORS.statusBar)
-
- -- Status text
- local filename = state.filename or "[New File]"
- if state.modified then filename = filename .. " *" end
- local status = string.format(" %s | Ln %d, Col %d | %d lines",
- filename, state.cursorLine, state.cursorCol, #state.lines)
- gfx:drawText(4, statusY + 2, status, COLORS.statusText)
-
- -- Help hint on right side
- local help = "Ctrl+O: Open Ctrl+S: Save "
- local helpX = w - #help * CHAR_WIDTH - 4
- gfx:drawText(helpX, statusY + 2, help, COLORS.lineNumbers)
-end
-
--- Input handler
-window:onInput(function(key, scancode)
- local ctrl = false
- -- Check for Ctrl modifier (scancode 0x1D is left ctrl)
- -- For now we'll use simple key detection
-
- if scancode == 0x48 then -- Up arrow
- moveCursor(-1, 0)
- elseif scancode == 0x50 then -- Down arrow
- moveCursor(1, 0)
- elseif scancode == 0x4B then -- Left arrow
- moveCursor(0, -1)
- elseif scancode == 0x4D then -- Right arrow
- moveCursor(0, 1)
- elseif scancode == 0x47 then -- Home
- moveToLineStart()
- elseif scancode == 0x4F then -- End
- moveToLineEnd()
- elseif scancode == 0x49 then -- Page Up
- moveCursor(-getVisibleLines(), 0)
- elseif scancode == 0x51 then -- Page Down
- moveCursor(getVisibleLines(), 0)
- elseif scancode == 0x0E then -- Backspace
- deleteCharBefore()
- elseif scancode == 0x53 then -- Delete
- deleteCharAt()
- elseif scancode == 0x1C then -- Enter
- insertNewLine()
- elseif scancode == 0x0F then -- Tab
- insertChar(" ") -- 4 spaces
- state.cursorCol = state.cursorCol + 3 -- We already added 1 in insertChar
- elseif key and #key == 1 then
- -- Regular character
- local byte = string.byte(key)
- if byte >= 32 and byte < 127 then
- insertChar(key)
- end
- end
-
- ensureCursorVisible()
- window:markDirty()
-end)
-
--- Click handler for cursor positioning
-window.onClick = function(x, y, button)
- if y < window.height - CHAR_HEIGHT and x > LINE_NUMBER_WIDTH then
- -- Calculate clicked line and column
- local clickedLine = math.floor((y - PADDING) / CHAR_HEIGHT) + state.scrollY + 1
- local clickedCol = math.floor((x - LINE_NUMBER_WIDTH - PADDING) / CHAR_WIDTH) + state.scrollX + 1
-
- -- Clamp to valid range
- if clickedLine >= 1 and clickedLine <= #state.lines then
- state.cursorLine = clickedLine
- local lineLen = #state.lines[clickedLine]
- if clickedCol < 1 then clickedCol = 1 end
- if clickedCol > lineLen + 1 then clickedCol = lineLen + 1 end
- state.cursorCol = clickedCol
- window:markDirty()
- end
- end
-end
-
--- Handle keyboard shortcuts via global input
--- Note: In a full implementation, we'd have modifier key tracking
-
--- Check for file argument
-if args and args[1] then
- loadFile(args[1])
-end
-
--- Initial draw
-window:markDirty()
diff --git a/iso_includes/apps/com.luajitos.shell/manifest.lua b/iso_includes/apps/com.luajitos.shell/manifest.lua
@@ -17,7 +17,8 @@ return {
"run",
"ramdisk",
"load",
- "imaging"
+ "imaging",
+ "set-path"
},
allowedPaths = {
"/apps/*",
diff --git a/iso_includes/apps/com.luajitos.shell/src/shell.lua b/iso_includes/apps/com.luajitos.shell/src/shell.lua
@@ -295,11 +295,11 @@ local function executeCommand(cmd)
if manifest then
-- App exists, run it with arguments
- local pcall_success, run_success, app_instance = pcall(run, appName, argString)
- if pcall_success and run_success and app_instance then
+ local pcall_success, run_success, run_result = pcall(run, appName, argString)
+ if pcall_success and run_success and run_result then
-- Check if app has CLI output
- if app_instance.cli and app_instance.cli.getText then
- local cli_output = app_instance.cli.getText()
+ if run_result.cli and run_result.cli.getText then
+ local cli_output = run_result.cli.getText()
if cli_output and cli_output ~= "" then
return cli_output
end
@@ -307,7 +307,11 @@ local function executeCommand(cmd)
return "Started: " .. appName
else
if not pcall_success then
+ -- pcall itself failed
return "Error: " .. tostring(run_success)
+ elseif not run_success then
+ -- run returned false with error message
+ return "Error: " .. tostring(run_result)
else
return "Error: Failed to run " .. appName
end
@@ -351,10 +355,10 @@ local function executeCommand(cmd)
end
if run then
- local pcall_success, run_success, app_instance = pcall(run, tmpScriptPath)
- if pcall_success and run_success and app_instance then
- if app_instance.cli and app_instance.cli.getText then
- local cli_output = app_instance.cli.getText()
+ local pcall_success, run_success, run_result = pcall(run, tmpScriptPath)
+ if pcall_success and run_success and run_result then
+ if run_result.cli and run_result.cli.getText then
+ local cli_output = run_result.cli.getText()
if cli_output and cli_output ~= "" then
return cli_output
end
@@ -362,7 +366,11 @@ local function executeCommand(cmd)
return "Script executed: " .. scriptName
else
if not pcall_success then
+ -- pcall itself failed
return "Error: " .. tostring(run_success)
+ elseif not run_success then
+ -- run returned false with error message
+ return "Error: " .. tostring(run_result)
else
return "Error: Failed to run script"
end
diff --git a/iso_includes/apps/com.luajitos.taskbar/src/init.lua b/iso_includes/apps/com.luajitos.taskbar/src/init.lua
@@ -18,6 +18,7 @@ taskbar.noTaskbar = true -- Don't show in taskbar
-- Start menu window (initially hidden)
local startMenuWindow = nil
local startMenuVisible = false
+local startMenuJustClosed = false -- Track if menu was just closed by focus lost
-- Window popup for multi-window apps
local windowPopup = nil
@@ -368,7 +369,7 @@ local function showStartMenu()
if not startMenuWindow then
-- Position menu just above the start button
local menuWidth = 300
- local menuHeight = 400
+ local menuHeight = 500
local menuX = startButtonX
local menuY = taskbarY - menuHeight
@@ -524,6 +525,7 @@ local function showStartMenu()
startMenuWindow.onFocusLost = function()
startMenuWindow:hide()
startMenuVisible = false
+ startMenuJustClosed = true -- Mark that we just closed via focus lost
end
else
-- Show existing window
@@ -549,6 +551,12 @@ taskbar.onClick = function(mx, my)
local startClickableBottom = taskbarHeight -- Extend to bottom edge
if mx >= 0 and mx < startClickableRight and my >= 0 and my < startClickableBottom then
closeWindowPopup() -- Close any window popup
+ -- If menu was just closed by focus lost (clicking start button), don't reopen
+ if startMenuJustClosed then
+ startMenuJustClosed = false
+ taskbar:markDirty()
+ return
+ end
if startMenuVisible then
hideStartMenu()
else
@@ -694,33 +702,22 @@ end
-- Register hooks to redraw taskbar when windows are created or closed
if sys and sys.hook then
sys.hook:add("WindowCreated", "taskbar-redraw", function(window)
- -- Redraw taskbar when a new window is created
taskbar:markDirty()
- print("Taskbar: Window created, redrawing taskbar")
end)
sys.hook:add("WindowClosed", "taskbar-redraw", function(window)
- -- Redraw taskbar when a window is closed
taskbar:markDirty()
- print("Taskbar: Window closed, redrawing taskbar")
end)
sys.hook:add("WindowMinimized", "taskbar-redraw", function(window)
- -- Redraw taskbar when a window is minimized
taskbar:markDirty()
- print("Taskbar: Window minimized, redrawing taskbar")
end)
sys.hook:add("WindowRestored", "taskbar-redraw", function(window)
- -- Redraw taskbar when a window is restored
taskbar:markDirty()
- print("Taskbar: Window restored, redrawing taskbar")
end)
sys.hook:add("ScreenResolutionChanged", "taskbar-reposition", function(screen)
- -- Update taskbar position and size when screen resolution changes
- print("Taskbar: Screen resolution changed to " .. screen.width .. "x" .. screen.height)
-
-- Update local screen dimensions
screenWidth = screen.width
screenHeight = screen.height
@@ -732,8 +729,6 @@ if sys and sys.hook then
-- Mark for redraw
taskbar:markDirty()
-
- print("Taskbar: Repositioned to bottom of new resolution")
end)
-- Close start menu when background is clicked
@@ -743,10 +738,4 @@ if sys and sys.hook then
taskbar:markDirty()
end
end)
-
- print("Taskbar: Registered WindowCreated, WindowClosed, ScreenResolutionChanged, and BackgroundFocused hooks")
end
-
-print("Taskbar loaded")
-print("Window dimensions: " .. screenWidth .. "x" .. taskbarHeight)
-print("Position: bottom of screen")
diff --git a/iso_includes/os/init.lua b/iso_includes/os/init.lua
@@ -1320,6 +1320,12 @@ function MainDraw()
end
end
end
+ else
+ -- Clicked on background (no window was clicked)
+ -- Fire BackgroundFocused hook so apps like taskbar can close menus
+ if _G.sys and _G.sys.hook then
+ _G.sys.hook:run("BackgroundFocused")
+ end
end
end
diff --git a/iso_includes/os/libs/Run.lua b/iso_includes/os/libs/Run.lua
@@ -435,6 +435,7 @@ function run.execute(app_name, fsRoot)
local selected_app, init_content, init_file, app_dir, data_dir
local permissions = {}
local manifest = {}
+ local app_instance = nil
if is_lua_script then
-- Direct .lua file execution
@@ -452,8 +453,9 @@ function run.execute(app_name, fsRoot)
init_content = read_from_ramdisk(fsRoot, init_file)
if not init_content then
- run_print("Error: Script file not found: " .. init_file .. "\n")
- return false
+ local err_msg = "Script file not found: " .. init_file
+ run_print("Error: " .. err_msg .. "\n")
+ return false, err_msg
end
-- Extract embedded manifest
@@ -506,8 +508,9 @@ function run.execute(app_name, fsRoot)
local matches = find_apps(app_name, fsRoot)
if #matches == 0 then
- run_print("Error: No app found matching '" .. app_name .. "'\n")
- return false
+ local err_msg = "No app found matching '" .. app_name .. "'"
+ run_print("Error: " .. err_msg .. "\n")
+ return false, err_msg
end
if #matches > 1 then
@@ -519,8 +522,9 @@ function run.execute(app_name, fsRoot)
local choice = tonumber(io.read())
if not choice or choice < 1 or choice > #matches then
- run_print("Invalid selection\n")
- return false
+ local err_msg = "Invalid selection"
+ run_print(err_msg .. "\n")
+ return false, err_msg
end
selected_app = matches[choice]
@@ -571,8 +575,9 @@ function run.execute(app_name, fsRoot)
-- Check if entry file exists
init_content = read_from_ramdisk(fsRoot, init_file)
if not init_content then
- run_print("Error: Entry file not found in " .. init_file .. "\n")
- return false
+ local err_msg = "Entry file not found: " .. init_file
+ run_print("Error: " .. err_msg .. "\n")
+ return false, err_msg
end
end
@@ -812,7 +817,7 @@ function run.execute(app_name, fsRoot)
if osprint then
osprint("[run] Creating Application instance for " .. selected_app .. "\n")
end
- local app_instance = Application.new(app_dir, {
+ app_instance = Application.new(app_dir, {
name = selected_app
})
-- Set status to running
@@ -967,7 +972,7 @@ function run.execute(app_name, fsRoot)
}
-- Also expose these read-only properties
local safe_app_properties = {
- "appName", "pid", "startTime", "status"
+ "appName", "pid", "startTime", "status", "path"
}
local app_proxy = {}
for _, method in ipairs(safe_app_methods) do
@@ -988,7 +993,12 @@ function run.execute(app_name, fsRoot)
error("app." .. tostring(k) .. " is not accessible", 2)
end,
__newindex = function(t, k, v)
- error("Cannot modify app object", 2)
+ -- Allow setting path if app has set-path permission
+ if k == "path" and permissions["set-path"] then
+ app_instance.path = v
+ return
+ end
+ error("Cannot modify app." .. tostring(k), 2)
end,
__metatable = false
})
@@ -2177,8 +2187,9 @@ function run.execute(app_name, fsRoot)
if osprint then
osprint("ERROR: Failed to create HTML window: " .. tostring(htmlErr) .. "\n")
end
- print("Error: Failed to create HTML window: " .. tostring(htmlErr))
- return false
+ local err_msg = "Failed to create HTML window: " .. tostring(htmlErr)
+ print("Error: " .. err_msg)
+ return false, err_msg
end
if osprint then
@@ -2290,7 +2301,8 @@ function run.execute(app_name, fsRoot)
if osprint then
osprint("ERROR: Failed to load app: " .. err .. "\n")
end
- return false
+ print("Error: " .. tostring(err))
+ return false, tostring(err)
end
if osprint then
@@ -2344,7 +2356,7 @@ function run.execute(app_name, fsRoot)
end
end
- return false, nil
+ return false, tostring(result)
end
print("App completed successfully")
diff --git a/iso_includes/os/postinit.lua b/iso_includes/os/postinit.lua
@@ -354,13 +354,4 @@ else
osprint("ERROR: Failed to launch taskbar app\n")
end
--- Launch installer for testing
-osprint("Launching installer...\n")
-local inst_ok, inst_app = _G.run.execute("com.luajitos.installer", _G.fsRoot)
-if inst_ok then
- osprint("Installer launched with PID: " .. tostring(inst_app.pid) .. "\n")
-else
- osprint("ERROR: Failed to launch installer\n")
-end
-
osprint("Post-init complete\n")