Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e98abcdda7 |
20
.moth/log
Normal file
20
.moth/log
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"console": {
|
||||
"outputs": ["console"]
|
||||
},
|
||||
"info": {
|
||||
"outputs": ["file:logs/info.log"]
|
||||
},
|
||||
"warning": {
|
||||
"outputs": ["console", "file:logs/warnings.log"]
|
||||
},
|
||||
"error": {
|
||||
"outputs": ["console", "file:logs/errors.log", "actor:localhost:5678"]
|
||||
},
|
||||
"debug": {
|
||||
"outputs": ["file:logs/debug.log"]
|
||||
},
|
||||
"audit": {
|
||||
"outputs": ["file:logs/audit.log", "actor:audit-server:9000"]
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
// Seif Handshake Client Example
|
||||
// Implements the Seif Protocol handshake client side
|
||||
|
||||
var crypto = use('crypto');
|
||||
var json = use('json');
|
||||
var io = use('io');
|
||||
|
||||
// Alice's key pair
|
||||
var alice_keys = crypto.keypair();
|
||||
console.log("Alice public key:", alice_keys.public);
|
||||
console.log("Alice private key:", alice_keys.private);
|
||||
|
||||
// Bob's public key (in real usage, this would be obtained separately)
|
||||
// For this example, we'll use a hardcoded key or read from file
|
||||
var bob_public_key = null;
|
||||
|
||||
// Try to read Bob's public key from file if it exists
|
||||
if (io.exists('bob_public.key')) {
|
||||
bob_public_key = io.slurp('bob_public.key').trim();
|
||||
console.log("Loaded Bob's public key from file:", bob_public_key);
|
||||
} else {
|
||||
// For testing, use the server's public key printed by seif_server.js
|
||||
console.log("Please create bob_public.key with the server's public key");
|
||||
console.log("Run seif_server.js first to get the public key");
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate random handshake key
|
||||
var handshake_key = crypto.keypair().public; // Using public key as random 32-byte value
|
||||
console.log("Generated handshake key:", handshake_key);
|
||||
|
||||
console.log("Sending handshake to server...");
|
||||
|
||||
// Contact the server
|
||||
$_.contact((actor, reason) => {
|
||||
if (!actor) {
|
||||
console.error("Could not establish connection:", reason);
|
||||
return;
|
||||
}
|
||||
}, {
|
||||
address: "localhost",
|
||||
port: 5678,
|
||||
seif: 1,
|
||||
handshake: crypto.encrypt_pk(bob_public_key, handshake_key),
|
||||
payload: crypto.encrypt(handshake_key, alice_keys.public)
|
||||
});
|
||||
|
||||
$_.receiver(e => {
|
||||
console.log("Received message:", e);
|
||||
});
|
||||
@@ -1,83 +0,0 @@
|
||||
# Seif Handshake Examples
|
||||
|
||||
This directory contains examples demonstrating the Seif Protocol handshake implementation in Prosperon.
|
||||
|
||||
## Files
|
||||
|
||||
- `seif_simple.js` - A standalone demonstration of the Seif handshake cryptographic operations
|
||||
- `seif_server.js` - A server that accepts Seif handshake connections
|
||||
- `seif_client.js` - A client that initiates Seif handshake with a server
|
||||
|
||||
## Running the Examples
|
||||
|
||||
### Simple Demo
|
||||
To see the cryptographic operations in action:
|
||||
```bash
|
||||
./prosperon examples/seif_simple.js
|
||||
```
|
||||
|
||||
### Client-Server Demo
|
||||
|
||||
1. First, start the server:
|
||||
```bash
|
||||
./prosperon examples/seif_server.js
|
||||
```
|
||||
|
||||
2. Note the server's public key that is printed
|
||||
|
||||
3. Create a file `bob_public.key` with the server's public key:
|
||||
```bash
|
||||
echo "SERVER_PUBLIC_KEY_HERE" > bob_public.key
|
||||
```
|
||||
|
||||
4. In another terminal, run the client:
|
||||
```bash
|
||||
./prosperon examples/seif_client.js
|
||||
```
|
||||
|
||||
## The Seif Protocol
|
||||
|
||||
The Seif handshake establishes a secure session in one round trip:
|
||||
|
||||
1. **Alice's Message**:
|
||||
- Generates random `handshake_key`
|
||||
- Sends: `{seif: 1, handshake: encrypt_pk(bob_public, handshake_key), payload: encrypt(handshake_key, alice_public)}`
|
||||
|
||||
2. **Bob's Response**:
|
||||
- Decrypts `handshake_key` using his private key
|
||||
- Decrypts Alice's public key from payload
|
||||
- Generates `session_key`
|
||||
- Sends: `encrypt(handshake_key, {session: encrypt_pk(alice_public, session_key)})`
|
||||
|
||||
3. **Result**: Both parties share `session_key` for symmetric encryption
|
||||
|
||||
## Actor System Integration
|
||||
|
||||
In Prosperon's actor system:
|
||||
- Actor objects can serve as public key identifiers (they contain unique IDs)
|
||||
- The `$_.portal()` function creates a listening endpoint
|
||||
- The `$_.contact()` function initiates connections
|
||||
- Messages are automatically routed through the actor system
|
||||
|
||||
## Security Properties
|
||||
|
||||
- **Authentication**: Both parties prove possession of their private keys
|
||||
- **Forward Secrecy**: Session keys are ephemeral
|
||||
- **Man-in-the-Middle Protection**: Requires knowledge of both private keys
|
||||
- **One Round Trip**: Efficient session establishment
|
||||
|
||||
## Notes on Implementation
|
||||
|
||||
The current implementation uses the actor object's ID as part of the identity system. In a production system, you might want to:
|
||||
|
||||
1. Store the public key as part of the actor's data
|
||||
2. Use a proper key derivation function for session keys
|
||||
3. Add additional metadata in the handshake (timestamps, nonces, etc.)
|
||||
4. Implement key rotation and session management
|
||||
|
||||
The crypto module provides:
|
||||
- `crypto.keypair()` - Generate X25519 key pairs
|
||||
- `crypto.encrypt_pk(public_key, data)` - Public key encryption
|
||||
- `crypto.decrypt_pk(private_key, data)` - Public key decryption
|
||||
- `crypto.encrypt(key, data)` - Symmetric encryption
|
||||
- `crypto.decrypt(key, data)` - Symmetric decryption
|
||||
@@ -1,85 +0,0 @@
|
||||
// Seif Handshake Server Example
|
||||
// Implements the Seif Protocol handshake as described in the documentation
|
||||
|
||||
var crypto = use('crypto');
|
||||
var json = use('json');
|
||||
var io = use('io');
|
||||
|
||||
// Server's key pair
|
||||
var server_keys = crypto.keypair();
|
||||
console.log("Server public key:", server_keys.public);
|
||||
console.log("Server private key:", server_keys.private);
|
||||
|
||||
// Store connected clients
|
||||
var clients = {};
|
||||
|
||||
$_.portal(e => {
|
||||
// Verify the handshake message format
|
||||
if (!e.seif || !e.handshake || !e.payload) {
|
||||
send(e, {error:"Invalid Seif handshake format"});
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.seif !== 1) {
|
||||
send(e, {error:"Unsupported Seif protocol version:", e.seif});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Decrypt the handshake key with server's private key
|
||||
var handshake_key_encrypted = e.handshake;
|
||||
var handshake_key_hex = crypto.decrypt_pk(server_keys.private, handshake_key_encrypted);
|
||||
|
||||
// Convert ArrayBuffer to hex string
|
||||
var handshake_key_bytes = new Uint8Array(handshake_key_hex);
|
||||
var handshake_key = '';
|
||||
for (var i = 0; i < handshake_key_bytes.length; i++) {
|
||||
var hex = handshake_key_bytes[i].toString(16);
|
||||
handshake_key += (hex.length === 1 ? '0' : '') + hex;
|
||||
}
|
||||
|
||||
console.log("Decrypted handshake key:", handshake_key);
|
||||
|
||||
// Decrypt the payload (client's public key) with handshake key
|
||||
var client_public_encrypted = e.payload;
|
||||
var client_public_buffer = crypto.decrypt(handshake_key, client_public_encrypted);
|
||||
|
||||
// Convert decrypted buffer to string
|
||||
var client_public_bytes = new Uint8Array(client_public_buffer);
|
||||
var client_public = '';
|
||||
for (var i = 0; i < client_public_bytes.length; i++) {
|
||||
client_public += String.fromCharCode(client_public_bytes[i]);
|
||||
}
|
||||
|
||||
console.log("Client's public key:", client_public);
|
||||
|
||||
// Generate session key
|
||||
var session_key = crypto.keypair();
|
||||
console.log("Generated session key:", session_key.public);
|
||||
|
||||
// Create response encrypted with handshake key
|
||||
var response = {
|
||||
session: crypto.encrypt_pk(client_public, session_key.public)
|
||||
};
|
||||
|
||||
var response_encrypted = crypto.encrypt(handshake_key, json.encode(response));
|
||||
|
||||
// Send encrypted response
|
||||
send(e, response_encrypted);
|
||||
|
||||
console.log("Handshake complete with client:", client_public);
|
||||
|
||||
} catch (err) {
|
||||
send(e, {error:err})
|
||||
}
|
||||
}, 5678);
|
||||
|
||||
// Handle messages from connected clients
|
||||
$_.receiver(e => {
|
||||
if (e.type === 'encrypted_message') {
|
||||
console.log("Received encrypted message");
|
||||
// In a real implementation, decrypt with session key and process
|
||||
}
|
||||
});
|
||||
|
||||
console.log("Seif server listening on port 5678");
|
||||
@@ -1,118 +0,0 @@
|
||||
// Simplified Seif Handshake Example
|
||||
// This demonstrates the core cryptographic operations of the Seif handshake
|
||||
|
||||
var crypto = use('crypto');
|
||||
var json = use('json');
|
||||
|
||||
console.log("=== Seif Handshake Demo ===\n");
|
||||
|
||||
// Step 1: Generate key pairs for Alice and Bob
|
||||
console.log("1. Generating key pairs...");
|
||||
var alice_keys = crypto.keypair();
|
||||
var bob_keys = crypto.keypair();
|
||||
|
||||
console.log("Alice's public key:", alice_keys.public);
|
||||
console.log("Bob's public key:", bob_keys.public);
|
||||
|
||||
// Step 2: Alice initiates handshake
|
||||
console.log("\n2. Alice initiates handshake...");
|
||||
|
||||
// Alice generates a random handshake key
|
||||
var handshake_key = crypto.keypair().public; // Using public key generation for random 32 bytes
|
||||
console.log("Handshake key:", handshake_key);
|
||||
|
||||
// Alice creates the handshake message
|
||||
var alice_message = {
|
||||
seif: 1,
|
||||
handshake: crypto.encrypt_pk(bob_keys.public, handshake_key),
|
||||
payload: crypto.encrypt(handshake_key, alice_keys.public)
|
||||
};
|
||||
|
||||
console.log("Alice's message created (encrypted components)");
|
||||
|
||||
// Step 3: Bob processes the handshake
|
||||
console.log("\n3. Bob processes the handshake...");
|
||||
|
||||
// Bob decrypts the handshake key
|
||||
var decrypted_handshake_key_buffer = crypto.decrypt_pk(bob_keys.private, alice_message.handshake);
|
||||
|
||||
// Convert buffer to hex string
|
||||
var handshake_key_bytes = new Uint8Array(decrypted_handshake_key_buffer);
|
||||
var recovered_handshake_key = '';
|
||||
for (var i = 0; i < handshake_key_bytes.length; i++) {
|
||||
var hex = handshake_key_bytes[i].toString(16);
|
||||
recovered_handshake_key += (hex.length === 1 ? '0' : '') + hex;
|
||||
}
|
||||
|
||||
console.log("Bob recovered handshake key:", recovered_handshake_key);
|
||||
console.log("Keys match:", recovered_handshake_key === handshake_key);
|
||||
|
||||
// Bob decrypts Alice's public key
|
||||
var alice_public_buffer = crypto.decrypt(recovered_handshake_key, alice_message.payload);
|
||||
var alice_public_bytes = new Uint8Array(alice_public_buffer);
|
||||
var recovered_alice_public = '';
|
||||
for (var i = 0; i < alice_public_bytes.length; i++) {
|
||||
recovered_alice_public += String.fromCharCode(alice_public_bytes[i]);
|
||||
}
|
||||
|
||||
console.log("Bob recovered Alice's public key:", recovered_alice_public);
|
||||
console.log("Public keys match:", recovered_alice_public === alice_keys.public);
|
||||
|
||||
// Step 4: Bob generates session key and responds
|
||||
console.log("\n4. Bob generates session key and responds...");
|
||||
|
||||
// Generate a random session key
|
||||
var session_key = crypto.keypair().public;
|
||||
console.log("Session key:", session_key);
|
||||
|
||||
// Bob encrypts the session key with Alice's public key
|
||||
var bob_response = {
|
||||
session: crypto.encrypt_pk(alice_keys.public, session_key)
|
||||
};
|
||||
|
||||
// Encrypt the entire response with the handshake key
|
||||
var encrypted_response = crypto.encrypt(handshake_key, json.encode(bob_response));
|
||||
console.log("Bob's encrypted response created");
|
||||
|
||||
// Step 5: Alice processes Bob's response
|
||||
console.log("\n5. Alice processes Bob's response...");
|
||||
|
||||
// Alice decrypts the response
|
||||
var decrypted_response_buffer = crypto.decrypt(handshake_key, encrypted_response);
|
||||
var response_json = '';
|
||||
var response_bytes = new Uint8Array(decrypted_response_buffer);
|
||||
for (var i = 0; i < response_bytes.length; i++) {
|
||||
response_json += String.fromCharCode(response_bytes[i]);
|
||||
}
|
||||
var response_data = json.decode(response_json);
|
||||
|
||||
// Alice decrypts the session key
|
||||
var session_key_buffer = crypto.decrypt_pk(alice_keys.private, response_data.session);
|
||||
var session_key_bytes = new Uint8Array(session_key_buffer);
|
||||
var recovered_session_key = '';
|
||||
for (var i = 0; i < session_key_bytes.length; i++) {
|
||||
var hex = session_key_bytes[i].toString(16);
|
||||
recovered_session_key += (hex.length === 1 ? '0' : '') + hex;
|
||||
}
|
||||
|
||||
console.log("Alice recovered session key:", recovered_session_key);
|
||||
console.log("Session keys match:", recovered_session_key === session_key);
|
||||
|
||||
// Step 6: Demonstrate secure communication
|
||||
console.log("\n6. Secure communication established!");
|
||||
console.log("Both parties now share the session key and can communicate securely.");
|
||||
|
||||
// Example encrypted message
|
||||
var message = "Hello, this is a secret message!";
|
||||
var encrypted_msg = crypto.encrypt(session_key, message);
|
||||
console.log("\nAlice encrypts:", message);
|
||||
|
||||
var decrypted_msg_buffer = crypto.decrypt(session_key, encrypted_msg);
|
||||
var decrypted_msg = '';
|
||||
var msg_bytes = new Uint8Array(decrypted_msg_buffer);
|
||||
for (var i = 0; i < msg_bytes.length; i++) {
|
||||
decrypted_msg += String.fromCharCode(msg_bytes[i]);
|
||||
}
|
||||
console.log("Bob decrypts:", decrypted_msg);
|
||||
|
||||
console.log("\n=== Seif Handshake Complete ===");
|
||||
@@ -166,6 +166,96 @@ console[prosperon.DOC] = {
|
||||
clear: "Clear console."
|
||||
}
|
||||
|
||||
// Initialize log system
|
||||
var logConfig = {}
|
||||
var logPath = '.moth/log'
|
||||
|
||||
// Default configuration if no .moth/log exists
|
||||
var defaultLogConfig = {
|
||||
console: { outputs: ['console'] }
|
||||
}
|
||||
|
||||
// Try to load log configuration
|
||||
if (io.exists(logPath)) {
|
||||
try {
|
||||
var logContent = io.slurp(logPath)
|
||||
logConfig = JSON.parse(logContent)
|
||||
} catch (e) {
|
||||
console.warn(`Failed to parse ${logPath}, using default configuration: ${e.message}`)
|
||||
logConfig = defaultLogConfig
|
||||
}
|
||||
} else {
|
||||
logConfig = defaultLogConfig
|
||||
}
|
||||
|
||||
// Create global log object
|
||||
globalThis.log = {}
|
||||
|
||||
// Helper function to create a log function for a specific log level
|
||||
function createLogFunction(logName, config) {
|
||||
return function(expr) {
|
||||
// If logging is not enabled for this level, do nothing
|
||||
if (!config || !config.outputs || config.outputs.length === 0) return
|
||||
|
||||
var timestamp = time.text(time.now(), "mb d yyyy h:nn:ss")
|
||||
var message = `[${logName}] ${timestamp}: ${String(expr)}`
|
||||
|
||||
// Process each output destination
|
||||
for (var output of config.outputs) {
|
||||
if (output === 'console') {
|
||||
// Output to console
|
||||
console.print(message + '\n')
|
||||
} else if (output.startsWith('file:')) {
|
||||
// Output to file
|
||||
var filename = output.substring(5)
|
||||
try {
|
||||
var existing = io.exists(filename) ? io.slurp(filename) : ''
|
||||
io.slurpwrite(filename, existing + message + '\n')
|
||||
} catch (e) {
|
||||
console.error(`Failed to write to log file ${filename}: ${e.message}`)
|
||||
}
|
||||
} else if (output.startsWith('actor:')) {
|
||||
// Send to actor
|
||||
var parts = output.substring(6).split(':')
|
||||
var address = parts[0]
|
||||
var port = parseInt(parts[1])
|
||||
|
||||
// Create an actor reference with the address and port
|
||||
var logActor = create_actor({
|
||||
address: address,
|
||||
port: port
|
||||
})
|
||||
|
||||
// Send log message to the actor (defer if send not available yet)
|
||||
try {
|
||||
// Check if send is available
|
||||
if (typeof send !== 'undefined') {
|
||||
send(logActor, {
|
||||
type: 'log',
|
||||
level: logName,
|
||||
message: expr,
|
||||
timestamp: timestamp,
|
||||
source: prosperon.id
|
||||
})
|
||||
} else {
|
||||
// If send is not available yet, just print to console as fallback
|
||||
console.print(`[${logName}] (actor output pending): ${expr}\n`)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`Failed to send log to actor ${address}:${port}: ${e.message}`)
|
||||
}
|
||||
} else {
|
||||
console.warn(`Unknown log output type: ${output}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create log functions for each configured log level
|
||||
for (var logName in logConfig) {
|
||||
log[logName] = createLogFunction(logName, logConfig[logName])
|
||||
}
|
||||
|
||||
var BASEPATH = 'scripts/core/base.js'
|
||||
var script = io.slurp(BASEPATH)
|
||||
var fnname = "base"
|
||||
|
||||
@@ -406,10 +406,9 @@ var image_info = {
|
||||
flip_x: false,
|
||||
flip_y: false,
|
||||
color: Color.white,
|
||||
mode: 'linear'
|
||||
}
|
||||
|
||||
draw.image = function image(image, rect = [0,0], rotation = 0, anchor = [0,0], shear = [0,0], info = {}, pipeline) {
|
||||
draw.image = function image(image, rect = [0,0], rotation = 0, anchor = [0,0], shear = [0,0], info, pipeline) {
|
||||
if (!image) throw Error('Need an image to render.')
|
||||
if (typeof image === "string")
|
||||
image = graphics.texture(image)
|
||||
|
||||
@@ -28,10 +28,8 @@ render.initialize = function(config)
|
||||
url: "https://prosperon.dev"
|
||||
}
|
||||
|
||||
config.__proto__ = default_conf
|
||||
prosperon.window = prosperon.engine_start(config)
|
||||
context = prosperon.window.make_renderer()
|
||||
context.logical_size([config.resolution_x, config.resolution_y], config.mode)
|
||||
prosperon.window = prosperon.engine_start({width:500,height:500})
|
||||
context = prosperon.window.make_renderer("vulkan")
|
||||
}
|
||||
|
||||
render.sprite = function(sprite)
|
||||
@@ -51,7 +49,6 @@ render.image = function(image, rect, rotation, anchor, shear, info)
|
||||
{
|
||||
// rect.width = image.rect_px.width;
|
||||
// rect.height = image.rect_px.height;
|
||||
image.texture.mode(info.mode)
|
||||
context.texture(image.texture, image.rect_px, rect, rotation, anchor);
|
||||
}
|
||||
|
||||
|
||||
@@ -1466,41 +1466,10 @@ JSC_CCALL(renderer_geometry2,
|
||||
JS_FreeValue(js, idx_v);
|
||||
)
|
||||
|
||||
/* logical presentation helpers */
|
||||
typedef struct { const char *name; SDL_RendererLogicalPresentation pres; } pres_entry;
|
||||
|
||||
static const pres_entry k_pres_table[] = {
|
||||
{ "stretch", SDL_LOGICAL_PRESENTATION_STRETCH },
|
||||
{ "letterbox", SDL_LOGICAL_PRESENTATION_LETTERBOX },
|
||||
{ "overscan", SDL_LOGICAL_PRESENTATION_OVERSCAN },
|
||||
{ "integer", SDL_LOGICAL_PRESENTATION_INTEGER_SCALE },
|
||||
{ NULL, SDL_LOGICAL_PRESENTATION_DISABLED } /* fallback */
|
||||
};
|
||||
|
||||
static JSValue logicalpresentation2js(JSContext *js,
|
||||
SDL_RendererLogicalPresentation pres){
|
||||
const pres_entry *it;
|
||||
for(it = k_pres_table; it->name; ++it)
|
||||
if(it->pres == pres) break;
|
||||
return JS_NewString(js, it->name ? it->name : "disabled");
|
||||
}
|
||||
|
||||
static SDL_RendererLogicalPresentation
|
||||
js2SDL_LogicalPresentation(JSContext *js, JSValue v){
|
||||
if(JS_IsUndefined(v)) return SDL_LOGICAL_PRESENTATION_DISABLED;
|
||||
const char *s = JS_ToCString(js, v);
|
||||
if(!s) return SDL_LOGICAL_PRESENTATION_DISABLED;
|
||||
const pres_entry *it;
|
||||
for(it = k_pres_table; it->name; ++it)
|
||||
if(!strcmp(it->name, s)) break;
|
||||
JS_FreeCString(js, s);
|
||||
return it->pres;
|
||||
}
|
||||
|
||||
JSC_CCALL(renderer_logical_size,
|
||||
SDL_Renderer *r = js2renderer_ctx(js,self)->sdl;
|
||||
HMM_Vec2 v = js2vec2(js,argv[0]);
|
||||
SDL_SetRenderLogicalPresentation(r,v.x,v.y,js2SDL_LogicalPresentation(js, argv[1]));
|
||||
SDL_SetRenderLogicalPresentation(r,v.x,v.y,SDL_LOGICAL_PRESENTATION_INTEGER_SCALE);
|
||||
)
|
||||
|
||||
JSC_CCALL(renderer_viewport,
|
||||
@@ -1694,7 +1663,7 @@ static const JSCFunctionListEntry js_renderer_ctx_funcs[] = {
|
||||
MIST_FUNC_DEF(renderer, get_image, 1),
|
||||
|
||||
MIST_FUNC_DEF(renderer, scale, 1),
|
||||
MIST_FUNC_DEF(renderer, logical_size,2),
|
||||
MIST_FUNC_DEF(renderer, logical_size,1),
|
||||
MIST_FUNC_DEF(renderer, viewport,1),
|
||||
MIST_FUNC_DEF(renderer, clip,1),
|
||||
MIST_FUNC_DEF(renderer, vsync,1),
|
||||
@@ -1749,7 +1718,7 @@ static SDL_PixelFormat js2pixelformat(JSContext *js, JSValue v)
|
||||
|
||||
JS_FreeCString(js,s);
|
||||
return it->fmt;
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct { const char *name; SDL_ScaleMode mode; } scale_entry;
|
||||
|
||||
@@ -1925,8 +1894,7 @@ static const JSCFunctionListEntry js_SDL_Surface_funcs[] = {
|
||||
|
||||
JSC_CCALL(texture_mode,
|
||||
SDL_Texture *tex = js2SDL_Texture(js,self);
|
||||
SDL_ScaleMode mode = js2SDL_ScaleMode(js,argv[0]);
|
||||
SDL_SetTextureScaleMode(tex,mode);
|
||||
SDL_SetTextureScaleMode(tex,js2number(js,argv[0]));
|
||||
)
|
||||
|
||||
static const JSCFunctionListEntry js_SDL_Texture_funcs[] = {
|
||||
|
||||
@@ -199,240 +199,10 @@ JSValue js_crypto_random(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
return JS_NewFloat64(js, val);
|
||||
}
|
||||
|
||||
JSValue js_crypto_encrypt_pk(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
{
|
||||
if (argc < 2) {
|
||||
return JS_ThrowTypeError(js, "crypto.encrypt_pk: expected 2 arguments (public_key, plaintext)");
|
||||
}
|
||||
|
||||
uint8_t public_key[32];
|
||||
js2crypto(js, argv[0], public_key);
|
||||
|
||||
size_t plaintext_len;
|
||||
uint8_t *plaintext;
|
||||
|
||||
if (JS_IsString(argv[1])) {
|
||||
const char *str = JS_ToCStringLen(js, &plaintext_len, argv[1]);
|
||||
if (!str) return JS_EXCEPTION;
|
||||
plaintext = (uint8_t *)str;
|
||||
} else {
|
||||
plaintext = JS_GetArrayBuffer(js, &plaintext_len, argv[1]);
|
||||
if (!plaintext) {
|
||||
return JS_ThrowTypeError(js, "crypto.encrypt_pk: plaintext must be string or ArrayBuffer");
|
||||
}
|
||||
}
|
||||
|
||||
// Generate ephemeral keypair
|
||||
uint8_t ephemeral_secret[32];
|
||||
uint8_t ephemeral_public[32];
|
||||
randombytes(ephemeral_secret, 32);
|
||||
ephemeral_secret[0] &= 248;
|
||||
ephemeral_secret[31] &= 127;
|
||||
ephemeral_secret[31] |= 64;
|
||||
crypto_x25519_public_key(ephemeral_public, ephemeral_secret);
|
||||
|
||||
// Compute shared secret
|
||||
uint8_t shared[32];
|
||||
crypto_x25519(shared, ephemeral_secret, public_key);
|
||||
|
||||
// Derive encryption key using BLAKE2b
|
||||
uint8_t key[32];
|
||||
crypto_blake2b(key, 32, shared, 32);
|
||||
|
||||
// Generate random nonce
|
||||
uint8_t nonce[24];
|
||||
randombytes(nonce, 24);
|
||||
|
||||
// Allocate output buffer: ephemeral_public(32) + nonce(24) + ciphertext + mac(16)
|
||||
size_t output_size = 32 + 24 + plaintext_len + 16;
|
||||
uint8_t *output = js_malloc(js, output_size);
|
||||
if (!output) {
|
||||
if (JS_IsString(argv[1])) JS_FreeCString(js, (const char *)plaintext);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
// Copy ephemeral public key and nonce to output
|
||||
memcpy(output, ephemeral_public, 32);
|
||||
memcpy(output + 32, nonce, 24);
|
||||
|
||||
// Encrypt
|
||||
crypto_aead_lock(output + 32 + 24, output + 32 + 24 + plaintext_len,
|
||||
key, nonce, NULL, 0, plaintext, plaintext_len);
|
||||
|
||||
if (JS_IsString(argv[1])) JS_FreeCString(js, (const char *)plaintext);
|
||||
|
||||
// Wipe sensitive data
|
||||
crypto_wipe(ephemeral_secret, 32);
|
||||
crypto_wipe(shared, 32);
|
||||
crypto_wipe(key, 32);
|
||||
|
||||
JSValue result = JS_NewArrayBufferCopy(js, output, output_size);
|
||||
js_free(js, output);
|
||||
return result;
|
||||
}
|
||||
|
||||
JSValue js_crypto_decrypt_pk(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
{
|
||||
if (argc < 2) {
|
||||
return JS_ThrowTypeError(js, "crypto.decrypt_pk: expected 2 arguments (private_key, ciphertext)");
|
||||
}
|
||||
|
||||
uint8_t private_key[32];
|
||||
js2crypto(js, argv[0], private_key);
|
||||
|
||||
size_t ciphertext_len;
|
||||
uint8_t *ciphertext = JS_GetArrayBuffer(js, &ciphertext_len, argv[1]);
|
||||
if (!ciphertext) {
|
||||
return JS_ThrowTypeError(js, "crypto.decrypt_pk: ciphertext must be ArrayBuffer");
|
||||
}
|
||||
|
||||
if (ciphertext_len < 32 + 24 + 16) {
|
||||
return JS_ThrowTypeError(js, "crypto.decrypt_pk: ciphertext too short");
|
||||
}
|
||||
|
||||
// Extract ephemeral public key and nonce
|
||||
uint8_t ephemeral_public[32];
|
||||
uint8_t nonce[24];
|
||||
memcpy(ephemeral_public, ciphertext, 32);
|
||||
memcpy(nonce, ciphertext + 32, 24);
|
||||
|
||||
// Compute shared secret
|
||||
uint8_t shared[32];
|
||||
crypto_x25519(shared, private_key, ephemeral_public);
|
||||
|
||||
// Derive decryption key
|
||||
uint8_t key[32];
|
||||
crypto_blake2b(key, 32, shared, 32);
|
||||
|
||||
// Decrypt
|
||||
size_t plaintext_len = ciphertext_len - 32 - 24 - 16;
|
||||
uint8_t *plaintext = js_malloc(js, plaintext_len);
|
||||
if (!plaintext) {
|
||||
crypto_wipe(shared, 32);
|
||||
crypto_wipe(key, 32);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
int result = crypto_aead_unlock(plaintext,
|
||||
ciphertext + ciphertext_len - 16,
|
||||
key, nonce, NULL, 0,
|
||||
ciphertext + 32 + 24, plaintext_len);
|
||||
|
||||
crypto_wipe(shared, 32);
|
||||
crypto_wipe(key, 32);
|
||||
|
||||
if (result != 0) {
|
||||
js_free(js, plaintext);
|
||||
return JS_ThrowTypeError(js, "crypto.decrypt_pk: decryption failed");
|
||||
}
|
||||
|
||||
JSValue ret = JS_NewArrayBufferCopy(js, plaintext, plaintext_len);
|
||||
js_free(js, plaintext);
|
||||
return ret;
|
||||
}
|
||||
|
||||
JSValue js_crypto_encrypt(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
{
|
||||
if (argc < 2) {
|
||||
return JS_ThrowTypeError(js, "crypto.encrypt: expected 2 arguments (key, plaintext)");
|
||||
}
|
||||
|
||||
uint8_t key[32];
|
||||
js2crypto(js, argv[0], key);
|
||||
|
||||
size_t plaintext_len;
|
||||
uint8_t *plaintext;
|
||||
|
||||
if (JS_IsString(argv[1])) {
|
||||
const char *str = JS_ToCStringLen(js, &plaintext_len, argv[1]);
|
||||
if (!str) return JS_EXCEPTION;
|
||||
plaintext = (uint8_t *)str;
|
||||
} else {
|
||||
plaintext = JS_GetArrayBuffer(js, &plaintext_len, argv[1]);
|
||||
if (!plaintext) {
|
||||
return JS_ThrowTypeError(js, "crypto.encrypt: plaintext must be string or ArrayBuffer");
|
||||
}
|
||||
}
|
||||
|
||||
// Generate random nonce
|
||||
uint8_t nonce[24];
|
||||
randombytes(nonce, 24);
|
||||
|
||||
// Allocate output buffer: nonce(24) + ciphertext + mac(16)
|
||||
size_t output_size = 24 + plaintext_len + 16;
|
||||
uint8_t *output = js_malloc(js, output_size);
|
||||
if (!output) {
|
||||
if (JS_IsString(argv[1])) JS_FreeCString(js, (const char *)plaintext);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
// Copy nonce to output
|
||||
memcpy(output, nonce, 24);
|
||||
|
||||
// Encrypt
|
||||
crypto_aead_lock(output + 24, output + 24 + plaintext_len,
|
||||
key, nonce, NULL, 0, plaintext, plaintext_len);
|
||||
|
||||
if (JS_IsString(argv[1])) JS_FreeCString(js, (const char *)plaintext);
|
||||
|
||||
JSValue result = JS_NewArrayBufferCopy(js, output, output_size);
|
||||
js_free(js, output);
|
||||
return result;
|
||||
}
|
||||
|
||||
JSValue js_crypto_decrypt(JSContext *js, JSValue self, int argc, JSValue *argv)
|
||||
{
|
||||
if (argc < 2) {
|
||||
return JS_ThrowTypeError(js, "crypto.decrypt: expected 2 arguments (key, ciphertext)");
|
||||
}
|
||||
|
||||
uint8_t key[32];
|
||||
js2crypto(js, argv[0], key);
|
||||
|
||||
size_t ciphertext_len;
|
||||
uint8_t *ciphertext = JS_GetArrayBuffer(js, &ciphertext_len, argv[1]);
|
||||
if (!ciphertext) {
|
||||
return JS_ThrowTypeError(js, "crypto.decrypt: ciphertext must be ArrayBuffer");
|
||||
}
|
||||
|
||||
if (ciphertext_len < 24 + 16) {
|
||||
return JS_ThrowTypeError(js, "crypto.decrypt: ciphertext too short");
|
||||
}
|
||||
|
||||
// Extract nonce
|
||||
uint8_t nonce[24];
|
||||
memcpy(nonce, ciphertext, 24);
|
||||
|
||||
// Decrypt
|
||||
size_t plaintext_len = ciphertext_len - 24 - 16;
|
||||
uint8_t *plaintext = js_malloc(js, plaintext_len);
|
||||
if (!plaintext) {
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
int result = crypto_aead_unlock(plaintext,
|
||||
ciphertext + ciphertext_len - 16,
|
||||
key, nonce, NULL, 0,
|
||||
ciphertext + 24, plaintext_len);
|
||||
|
||||
if (result != 0) {
|
||||
js_free(js, plaintext);
|
||||
return JS_ThrowTypeError(js, "crypto.decrypt: decryption failed");
|
||||
}
|
||||
|
||||
JSValue ret = JS_NewArrayBufferCopy(js, plaintext, plaintext_len);
|
||||
js_free(js, plaintext);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const JSCFunctionListEntry js_crypto_funcs[] = {
|
||||
JS_CFUNC_DEF("keypair", 0, js_crypto_keypair),
|
||||
JS_CFUNC_DEF("shared", 1, js_crypto_shared),
|
||||
JS_CFUNC_DEF("random", 0, js_crypto_random),
|
||||
JS_CFUNC_DEF("encrypt_pk", 2, js_crypto_encrypt_pk),
|
||||
JS_CFUNC_DEF("decrypt_pk", 2, js_crypto_decrypt_pk),
|
||||
JS_CFUNC_DEF("encrypt", 2, js_crypto_encrypt),
|
||||
JS_CFUNC_DEF("decrypt", 2, js_crypto_decrypt),
|
||||
};
|
||||
|
||||
JSValue js_crypto_use(JSContext *js)
|
||||
|
||||
29
test_log.js
Normal file
29
test_log.js
Normal file
@@ -0,0 +1,29 @@
|
||||
// Test script for the log statement system
|
||||
|
||||
// Test console logging
|
||||
log.console("This is a console log message");
|
||||
|
||||
// Test info logging (goes to file)
|
||||
log.info("Application started");
|
||||
log.info("User logged in: user123");
|
||||
|
||||
// Test warning logging (goes to console and file)
|
||||
log.warning("Memory usage is high");
|
||||
|
||||
// Test error logging (goes to console, file, and actor)
|
||||
log.error("Failed to connect to database");
|
||||
|
||||
// Test debug logging (goes to file only)
|
||||
log.debug("index: " + 42);
|
||||
log.debug("Processing item: " + JSON.stringify({id: 1, name: "test"}));
|
||||
|
||||
// Test audit logging (goes to file and actor)
|
||||
if (log.audit) {
|
||||
log.audit("User performed sensitive action");
|
||||
}
|
||||
|
||||
// Test with expressions as per the spec
|
||||
var index = 5;
|
||||
log.debug("index value is: " + index);
|
||||
|
||||
console.log("Log test completed. Check console output and log files.");
|
||||
Reference in New Issue
Block a user