luajitos

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

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:
DSAFEFS_EXAMPLE.lua | 205-------------------------------------------------------------------------------
Mbuild.sh | 7+++----
Mcrypto/HKDF.c | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mcrypto/Hash_Lua.c | 21+++++++++++++++++++++
Mcrypto/Hash_Lua.h | 1+
Mcrypto/RSA.c | 283+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcrypto/crypto.c | 4++--
Mcrypto/stubs.c | 35++++++++---------------------------
Aiso_includes/apps/com.luajitos.installer/icon.png | 0
Diso_includes/apps/com.luajitos.lunareditor/src/editor.lua | 375-------------------------------------------------------------------------------
Miso_includes/apps/com.luajitos.shell/manifest.lua | 3++-
Miso_includes/apps/com.luajitos.shell/src/shell.lua | 24++++++++++++++++--------
Miso_includes/apps/com.luajitos.taskbar/src/init.lua | 29+++++++++--------------------
Miso_includes/os/init.lua | 6++++++
Miso_includes/os/libs/Run.lua | 42+++++++++++++++++++++++++++---------------
Miso_includes/os/postinit.lua | 9---------
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")