initial try at seif handshake example
Some checks failed
Build and Deploy / build-macos (push) Failing after 5s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
Some checks failed
Build and Deploy / build-macos (push) Failing after 5s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
This commit is contained in:
50
examples/seif_client.js
Normal file
50
examples/seif_client.js
Normal file
@@ -0,0 +1,50 @@
|
||||
// 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);
|
||||
});
|
||||
83
examples/seif_handshake_README.md
Normal file
83
examples/seif_handshake_README.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# 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
|
||||
85
examples/seif_server.js
Normal file
85
examples/seif_server.js
Normal file
@@ -0,0 +1,85 @@
|
||||
// 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");
|
||||
118
examples/seif_simple.js
Normal file
118
examples/seif_simple.js
Normal file
@@ -0,0 +1,118 @@
|
||||
// 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 ===");
|
||||
@@ -199,10 +199,240 @@ 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)
|
||||
|
||||
Reference in New Issue
Block a user