mirror of
https://github.com/gchq/CyberChef.git
synced 2025-04-21 07:16:17 -04:00
checkpoint
This commit is contained in:
parent
00184ff68a
commit
af465dfcfa
2 changed files with 179 additions and 701 deletions
|
@ -10,7 +10,7 @@
|
|||
|
||||
import Operation from "../Operation.mjs";
|
||||
|
||||
import * as hydro from "libhydrogen/libHydrogen.js";
|
||||
import * as hydro from "@configuredthings/libhydrogen-wasm/libHydrogen.js";
|
||||
|
||||
/**
|
||||
* LibHydrogen Curve25519 Sign operation
|
||||
|
@ -27,190 +27,182 @@ class LibHydrogenCurve25519Signing extends Operation {
|
|||
this.module = "Crypto";
|
||||
this.description = "Computes a signature for a message using the lightweight LibHydrogen cryptography library";
|
||||
this.infoURL = "https://libhydrogen.org/";
|
||||
this.inputType = "byteArray";
|
||||
this.inputType = "JSON";
|
||||
this.outputType = "JSON";
|
||||
this.args = [
|
||||
{
|
||||
name: "First arg",
|
||||
name: "Context",
|
||||
type: "string",
|
||||
value: "Don't Panic"
|
||||
value: ""
|
||||
},
|
||||
{
|
||||
name: "Second arg",
|
||||
type: "number",
|
||||
value: 42
|
||||
name: "Sender's private key",
|
||||
type: "byteArray",
|
||||
value: ""
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {byteArray} input
|
||||
* @param {JSON} input
|
||||
* @param {Object[]} args
|
||||
* @returns {JSON}
|
||||
*/
|
||||
run(input, args) {
|
||||
const [firstArg, secondArg] = args;
|
||||
(async () => {
|
||||
const wasm_src = await fetch(new URL(`${self.docURL}/assets/libhydrogen/libhydrogen.wasm`));
|
||||
const wasm = await WebAssembly.compileStreaming(wasm_src);
|
||||
const imports = {
|
||||
"wasi_snapshot_preview1": {
|
||||
args_get() {
|
||||
return 0;
|
||||
},
|
||||
args_sizes_get() {
|
||||
return 0;
|
||||
},
|
||||
clock_res_get() {
|
||||
return 0;
|
||||
},
|
||||
clock_time_get() {
|
||||
return 0;
|
||||
},
|
||||
environ_sizes_get() {
|
||||
return 0;
|
||||
},
|
||||
environ_get() {
|
||||
return 0;
|
||||
},
|
||||
proc_exit() {
|
||||
return 0;
|
||||
},
|
||||
fd_write() {
|
||||
return 0;
|
||||
},
|
||||
fd_advise() {
|
||||
return 0;
|
||||
},
|
||||
fd_allocate() {
|
||||
return 0;
|
||||
},
|
||||
fd_close() {
|
||||
return 0;
|
||||
},
|
||||
fd_datasync() {
|
||||
return 0;
|
||||
},
|
||||
fd_fdstat_get() {
|
||||
return 0;
|
||||
},
|
||||
fd_fdstat_set_flags() {
|
||||
return 0;
|
||||
},
|
||||
fd_fdstat_set_rights() {
|
||||
return 0;
|
||||
},
|
||||
fd_filestat_get() {
|
||||
return 0;
|
||||
},
|
||||
fd_filestat_set_size() {
|
||||
return 0;
|
||||
},
|
||||
fd_filestat_set_times() {
|
||||
return 0;
|
||||
},
|
||||
fd_pread() {
|
||||
return 0;
|
||||
},
|
||||
fd_prestat_get() {
|
||||
return 0;
|
||||
},
|
||||
fd_prestat_dir_name() {
|
||||
return 0;
|
||||
},
|
||||
fd_pwrite() {
|
||||
return 0;
|
||||
},
|
||||
fd_read() {
|
||||
return 0;
|
||||
},
|
||||
fd_readdir() {
|
||||
return 0;
|
||||
},
|
||||
fd_renumber() {
|
||||
return 0;
|
||||
},
|
||||
fd_seek() {
|
||||
return 0;
|
||||
},
|
||||
fd_sync() {
|
||||
return 0;
|
||||
},
|
||||
fd_tell() {
|
||||
return 0;
|
||||
},
|
||||
path_create_directory() {
|
||||
return 0;
|
||||
},
|
||||
path_filestat_get() {
|
||||
return 0;
|
||||
},
|
||||
path_filestat_set_times() {
|
||||
return 0;
|
||||
},
|
||||
path_link() {
|
||||
return 0;
|
||||
},
|
||||
path_open() {
|
||||
return 0;
|
||||
},
|
||||
path_readlink() {
|
||||
return 0;
|
||||
},
|
||||
path_remove_directory() {
|
||||
return 0;
|
||||
},
|
||||
path_rename() {
|
||||
return 0;
|
||||
},
|
||||
path_symlink() {
|
||||
return 0;
|
||||
},
|
||||
path_unlink_file() {
|
||||
return 0;
|
||||
},
|
||||
poll_oneoff() {
|
||||
return 0;
|
||||
},
|
||||
sched_yield() {
|
||||
return 0;
|
||||
},
|
||||
random_get(buf, buf_len) {
|
||||
const random_arr = new Uint8Array(dataview.buffer, buf, buf_len);
|
||||
crypto.getRandomValues(random_arr);
|
||||
return 0;
|
||||
},
|
||||
sock_accept() {
|
||||
return 0;
|
||||
},
|
||||
sock_recv() {
|
||||
return 0;
|
||||
},
|
||||
sock_send() {
|
||||
return 0;
|
||||
},
|
||||
sock_shutdown() {
|
||||
return 0;
|
||||
},
|
||||
}
|
||||
};
|
||||
instance = await WebAssembly.instantiate(wasm, imports);
|
||||
// Get the memory are used as a stack when calling into the WASI
|
||||
const memory = instance.exports.memory;
|
||||
// DataView takes care of our platform specific endian conversions
|
||||
dataview = new DataView(memory.buffer);
|
||||
// We must call a start method per WASI specification
|
||||
// Libhydrogen's main method is one we have patched to initialise it
|
||||
instance.exports._start();
|
||||
// Run the various examples
|
||||
random_uniform();
|
||||
hash();
|
||||
keyed_hash();
|
||||
public_key_signing();
|
||||
symmetric_encryption();
|
||||
symmetric_encryption_via_asymmetric_key_exchange();
|
||||
})();
|
||||
return {};
|
||||
async run(input, args) {
|
||||
const [context, privateKey] = args;
|
||||
const wasm_src = await fetch(new URL(`${self.docURL}/assets/libhydrogen-wasm/libhydrogen.wasm`));
|
||||
const wasm = await WebAssembly.compileStreaming(wasm_src);
|
||||
const imports = {
|
||||
"wasi_snapshot_preview1": {
|
||||
args_get() {
|
||||
return 0;
|
||||
},
|
||||
args_sizes_get() {
|
||||
return 0;
|
||||
},
|
||||
clock_res_get() {
|
||||
return 0;
|
||||
},
|
||||
clock_time_get() {
|
||||
return 0;
|
||||
},
|
||||
environ_sizes_get() {
|
||||
return 0;
|
||||
},
|
||||
environ_get() {
|
||||
return 0;
|
||||
},
|
||||
proc_exit() {
|
||||
return 0;
|
||||
},
|
||||
fd_write() {
|
||||
return 0;
|
||||
},
|
||||
fd_advise() {
|
||||
return 0;
|
||||
},
|
||||
fd_allocate() {
|
||||
return 0;
|
||||
},
|
||||
fd_close() {
|
||||
return 0;
|
||||
},
|
||||
fd_datasync() {
|
||||
return 0;
|
||||
},
|
||||
fd_fdstat_get() {
|
||||
return 0;
|
||||
},
|
||||
fd_fdstat_set_flags() {
|
||||
return 0;
|
||||
},
|
||||
fd_fdstat_set_rights() {
|
||||
return 0;
|
||||
},
|
||||
fd_filestat_get() {
|
||||
return 0;
|
||||
},
|
||||
fd_filestat_set_size() {
|
||||
return 0;
|
||||
},
|
||||
fd_filestat_set_times() {
|
||||
return 0;
|
||||
},
|
||||
fd_pread() {
|
||||
return 0;
|
||||
},
|
||||
fd_prestat_get() {
|
||||
return 0;
|
||||
},
|
||||
fd_prestat_dir_name() {
|
||||
return 0;
|
||||
},
|
||||
fd_pwrite() {
|
||||
return 0;
|
||||
},
|
||||
fd_read() {
|
||||
return 0;
|
||||
},
|
||||
fd_readdir() {
|
||||
return 0;
|
||||
},
|
||||
fd_renumber() {
|
||||
return 0;
|
||||
},
|
||||
fd_seek() {
|
||||
return 0;
|
||||
},
|
||||
fd_sync() {
|
||||
return 0;
|
||||
},
|
||||
fd_tell() {
|
||||
return 0;
|
||||
},
|
||||
path_create_directory() {
|
||||
return 0;
|
||||
},
|
||||
path_filestat_get() {
|
||||
return 0;
|
||||
},
|
||||
path_filestat_set_times() {
|
||||
return 0;
|
||||
},
|
||||
path_link() {
|
||||
return 0;
|
||||
},
|
||||
path_open() {
|
||||
return 0;
|
||||
},
|
||||
path_readlink() {
|
||||
return 0;
|
||||
},
|
||||
path_remove_directory() {
|
||||
return 0;
|
||||
},
|
||||
path_rename() {
|
||||
return 0;
|
||||
},
|
||||
path_symlink() {
|
||||
return 0;
|
||||
},
|
||||
path_unlink_file() {
|
||||
return 0;
|
||||
},
|
||||
poll_oneoff() {
|
||||
return 0;
|
||||
},
|
||||
sched_yield() {
|
||||
return 0;
|
||||
},
|
||||
random_get(buf, buf_len) {
|
||||
const random_arr = new Uint8Array(dataview.buffer, buf, buf_len);
|
||||
crypto.getRandomValues(random_arr);
|
||||
return 0;
|
||||
},
|
||||
sock_accept() {
|
||||
return 0;
|
||||
},
|
||||
sock_recv() {
|
||||
return 0;
|
||||
},
|
||||
sock_send() {
|
||||
return 0;
|
||||
},
|
||||
sock_shutdown() {
|
||||
return 0;
|
||||
},
|
||||
}
|
||||
};
|
||||
instance = await WebAssembly.instantiate(wasm, imports);
|
||||
// Get the memory are used as a stack when calling into the WASI
|
||||
const memory = instance.exports.memory;
|
||||
// DataView takes care of our platform specific endian conversions
|
||||
dataview = new DataView(memory.buffer);
|
||||
// We must call a start method per WASI specification
|
||||
// Libhydrogen's main method is one we have patched to initialise it
|
||||
instance.exports._start();
|
||||
// Generated signature for JSON input
|
||||
return await sign(input, context, privateKey);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -219,7 +211,7 @@ let instance, dataview;
|
|||
|
||||
//
|
||||
// Helper function to reserve space in the buffer used
|
||||
// as a stack between Node and the wasm.
|
||||
// as a stack between js and the wasm.
|
||||
//
|
||||
// Offset must be an an object so we can update it's value
|
||||
// {value: n}
|
||||
|
@ -238,182 +230,10 @@ function reserve(offset, length) {
|
|||
return a;
|
||||
}
|
||||
|
||||
//
|
||||
// Generate a series of random numbers
|
||||
//
|
||||
function random_uniform() {
|
||||
console.log("\n=== random_uniform ===\n");
|
||||
const { hydro_random_uniform } = instance.exports;
|
||||
// Testing a simple case of passing integers and fetching integers
|
||||
console.log(`generated random - ${hydro_random_uniform(20)}`);
|
||||
console.log(`generated random - ${hydro_random_uniform(20)}`);
|
||||
console.log(`generated random - ${hydro_random_uniform(20)}`);
|
||||
console.log(`generated random - ${hydro_random_uniform(20)}`);
|
||||
console.log(`generated random - ${hydro_random_uniform(20)}`);
|
||||
}
|
||||
|
||||
//
|
||||
// Hash Generation
|
||||
//
|
||||
function hash() {
|
||||
console.log("\n=== hash ===\n");
|
||||
const { hydro_hash_hash } = instance.exports;
|
||||
const textEncoder = new TextEncoder();
|
||||
|
||||
// We have to create the stack frame to pass to libHydrogen
|
||||
// in the dataview Buffer, and then pass in pointers to
|
||||
// that buffer
|
||||
|
||||
const offset = { value: 0 };
|
||||
const context_str = "Examples";
|
||||
const context_arr = reserve(offset, hydro.hash_CONTEXTBYTES);
|
||||
const examples_ab = textEncoder.encode(context_str);
|
||||
|
||||
for (let i = 0; i < hydro.hash_CONTEXTBYTES; i++) {
|
||||
context_arr.set([examples_ab.at(i)], i);
|
||||
}
|
||||
|
||||
// Our message to be hashed
|
||||
const message_str = "Arbitrary data to hash";
|
||||
const message_ab = textEncoder.encode(message_str);
|
||||
const message_arr = reserve(offset, message_ab.length);
|
||||
|
||||
for (let i = 0; i < message_ab.length; i++) {
|
||||
message_arr.set([message_ab.at(i)], i);
|
||||
}
|
||||
|
||||
// Buffer for libHydrogen to write the hash into
|
||||
const hash = reserve(offset, hydro.hash_BYTES);
|
||||
|
||||
// Call the imported function
|
||||
hydro_hash_hash(
|
||||
hash.byteOffset,
|
||||
hash.length,
|
||||
message_arr.byteOffset,
|
||||
message_arr.byteLength,
|
||||
context_arr.byteOffset,
|
||||
null,
|
||||
);
|
||||
console.log(`generated hash - ${Buffer.from(hash).toString("hex")}`);
|
||||
}
|
||||
|
||||
//
|
||||
// Hash generation with a key
|
||||
//
|
||||
function keyed_hash() {
|
||||
console.log("\n=== keyed_hash ===\n");
|
||||
|
||||
// Importing libhydrogen's hashing keygen and hash functions
|
||||
const { hydro_hash_keygen, hydro_hash_hash } = instance.exports;
|
||||
const textEncoder = new TextEncoder();
|
||||
|
||||
// We have to create the stack frame to pass to libHydrogen
|
||||
// in the dataview Buffer, and then pass in pointers to
|
||||
// that buffer
|
||||
const offset = { value: 0 };
|
||||
const context_str = "Examples";
|
||||
const context_arr = reserve(offset, hydro.hash_CONTEXTBYTES);
|
||||
const examples_ab = textEncoder.encode(context_str);
|
||||
|
||||
for (let i = 0; i < hydro.hash_CONTEXTBYTES; i++) {
|
||||
context_arr.set([examples_ab.at(i)], i);
|
||||
}
|
||||
|
||||
const message_str = "Arbitrary data to hash";
|
||||
const message_ab = textEncoder.encode(message_str);
|
||||
const message_arr = reserve(offset, message_ab.length);
|
||||
|
||||
for (let i = 0; i < message_ab.length; i++) {
|
||||
message_arr.set([message_ab.at(i)], i);
|
||||
}
|
||||
|
||||
// Reserve buffer space for the returned key
|
||||
const key = reserve(offset, hydro.hash_KEYBYTES);
|
||||
|
||||
// Reserve space for the hash result
|
||||
const keyedhash = reserve(offset, hydro.hash_BYTES);
|
||||
|
||||
// Generate hashing key
|
||||
hydro_hash_keygen(key.byteOffset);
|
||||
console.log(`generated hash key - ${key}`);
|
||||
|
||||
// Create a hash with the key
|
||||
hydro_hash_hash(
|
||||
keyedhash.byteOffset,
|
||||
keyedhash.length,
|
||||
message_arr.byteOffset,
|
||||
message_arr.byteLength,
|
||||
context_arr.byteOffset,
|
||||
key.byteOffset,
|
||||
);
|
||||
const khash1 = keyedhash.toString("hex");
|
||||
console.log(`khash1 - ${khash1}`);
|
||||
|
||||
keyedhash.fill(0); // Resetting output buffer (seems to pollute state otherwise)
|
||||
|
||||
// Hashing message with key again
|
||||
hydro_hash_hash(
|
||||
keyedhash.byteOffset,
|
||||
keyedhash.length,
|
||||
message_arr.byteOffset,
|
||||
message_arr.byteLength,
|
||||
context_arr.byteOffset,
|
||||
key.byteOffset,
|
||||
);
|
||||
const khash2 = keyedhash.toString("hex");
|
||||
console.log(`khash2 - ${khash2}`);
|
||||
keyedhash.fill(0);
|
||||
console.log(`${keyedhash.toString("hex")}`);
|
||||
|
||||
// Check that the same hash is generated
|
||||
if (khash1 === khash2) console.log("khash1 equals khash2");
|
||||
|
||||
// Testing whether we can load a key and generate a matching hash created previously
|
||||
const presetKey = "f539065185b3ce774b0c748564e804a3717ca7d0c08231076e8b7920814f0bba";
|
||||
console.log(`presetKey - ${presetKey}`);
|
||||
|
||||
const historicHash = "4004516ceff97883804dbdb221baeb7283256e60165d0715d0152e4d6a6cbad4";
|
||||
console.log(`historicHash - ${historicHash}`);
|
||||
|
||||
const loadedKey = new Uint8Array(presetKey.match(/../g).map(h=>parseInt(h, 16)));
|
||||
for (let i = 0; i < loadedKey.length; i++) {
|
||||
key.set([loadedKey.at(i)], i);
|
||||
}
|
||||
|
||||
// Hashing message with presetKey
|
||||
hydro_hash_hash(
|
||||
keyedhash.byteOffset,
|
||||
keyedhash.length,
|
||||
message_arr.byteOffset,
|
||||
message_arr.byteLength,
|
||||
context_arr.byteOffset,
|
||||
key.byteOffset,
|
||||
);
|
||||
|
||||
const khash3 = keyedhash.toString("hex");
|
||||
console.log(`khash3 - ${khash3}`);
|
||||
|
||||
// Testing hash matches historicHash
|
||||
if (khash3 === historicHash)
|
||||
console.log("khash3 using old key, matches historicHash");
|
||||
|
||||
// ...and doesn't match the hashes with the latest key
|
||||
if (khash1 !== khash3)
|
||||
console.log("khash3 does not equal khash1");
|
||||
|
||||
// clear the buffer for the next example
|
||||
context_arr.fill(0);
|
||||
message_arr.fill(0);
|
||||
keyedhash.fill(0);
|
||||
key.fill(0);
|
||||
}
|
||||
|
||||
//
|
||||
// Public key signing
|
||||
//
|
||||
function public_key_signing() {
|
||||
console.log("\n=== public_key_signing ===\n");
|
||||
|
||||
async function sign(input, context, privateKey) {
|
||||
// Importing libhydrogen's signing keygen and signing and verification functions
|
||||
const { hydro_sign_keygen, hydro_sign_create, hydro_sign_verify } = instance.exports;
|
||||
const textEncoder = new TextEncoder();
|
||||
|
@ -422,16 +242,14 @@ function public_key_signing() {
|
|||
// in the dataview Buffer, and then pass in pointers to
|
||||
// that buffer
|
||||
const offset = { value: 0 };
|
||||
const context_str = "Examples";
|
||||
const context_arr = reserve(offset, hydro.hash_CONTEXTBYTES);
|
||||
const examples_ab = textEncoder.encode(context_str);
|
||||
const context_ab = textEncoder.encode(context);
|
||||
|
||||
for (let i = 0; i < hydro.hash_CONTEXTBYTES; i++) {
|
||||
context_arr.set([examples_ab.at(i)], i);
|
||||
context_arr.set([context_ab.at(i)], i);
|
||||
}
|
||||
|
||||
const message_str = "Arbitrary data to hash";
|
||||
const message_ab = textEncoder.encode(message_str);
|
||||
const message_ab = textEncoder.encode(JSON.stringify(input));
|
||||
const message_arr = reserve(offset, message_ab.length);
|
||||
|
||||
for (let i = 0; i < message_ab.length; i++) {
|
||||
|
@ -439,10 +257,10 @@ function public_key_signing() {
|
|||
}
|
||||
|
||||
// Generate a key pair
|
||||
const keypair = reserve(offset, hydro.sign_PUBLICKEYBYTES + hydro.sign_SECRETKEYBYTES);
|
||||
const publicKeyOffset = keypair.byteOffset;
|
||||
const privateKeyOffset = keypair.byteOffset + hydro.sign_PUBLICKEYBYTES;
|
||||
hydro_sign_keygen(keypair.byteOffset);
|
||||
const privateKey_arr = reserve(offset, hydro.sign_SECRETKEYBYTES);
|
||||
for (let i = 0; i < privateKey.length; i++) {
|
||||
privateKey_arr.set([privateKey.at(i)], i);
|
||||
}
|
||||
|
||||
// Reserving memory for the signature
|
||||
const signature = reserve(offset, hydro.sign_BYTES);
|
||||
|
@ -453,356 +271,16 @@ function public_key_signing() {
|
|||
message_arr.byteOffset,
|
||||
message_arr.byteLength,
|
||||
context_arr.byteOffset,
|
||||
privateKeyOffset,
|
||||
privateKey_arr.byteOffset,
|
||||
);
|
||||
|
||||
console.log(`generated signature - ${Buffer.from(signature).toString("hex")}`);
|
||||
|
||||
// Verifying signature with public key
|
||||
const res = hydro_sign_verify(
|
||||
signature.byteOffset,
|
||||
message_arr.byteOffset,
|
||||
message_arr.byteLength,
|
||||
context_arr.byteOffset,
|
||||
publicKeyOffset,
|
||||
);
|
||||
|
||||
if (res === 0) console.log("Signature is correctly valid");
|
||||
signature.set([0]); // Modifying signature
|
||||
console.log(`modified signature - ${Buffer.from(signature).toString("hex")}`);
|
||||
|
||||
const manipulatedRes = hydro_sign_verify(
|
||||
signature.byteOffset,
|
||||
message_arr.byteOffset, // Reverifying signature
|
||||
message_arr.byteLength,
|
||||
context_arr.byteOffset,
|
||||
publicKeyOffset,
|
||||
);
|
||||
if (manipulatedRes !== 0) console.log("Signature is correctly invalid");
|
||||
|
||||
message_arr.fill(0);
|
||||
keypair.fill(0);
|
||||
signature.fill(0);
|
||||
}
|
||||
|
||||
//
|
||||
// Secret Key Encryption
|
||||
//
|
||||
function symmetric_encryption() {
|
||||
console.log("\n=== symmetric_encryption ===\n");
|
||||
|
||||
// Importing libhydrogen's secretbox keygen and encrypt and decrypt functions
|
||||
const { hydro_secretbox_keygen, hydro_secretbox_encrypt, hydro_secretbox_decrypt } =
|
||||
instance.exports;
|
||||
const textEncoder = new TextEncoder();
|
||||
|
||||
// We have to create the stack frame to pass to libHydrogen
|
||||
// in the dataview Buffer, and then pass in pointers to
|
||||
// that buffer
|
||||
const offset = { value: 0 };
|
||||
const context_str = "Examples";
|
||||
const context_arr = reserve(offset, hydro.hash_CONTEXTBYTES);
|
||||
const examples_ab = textEncoder.encode(context_str);
|
||||
|
||||
for (let i = 0; i < hydro.hash_CONTEXTBYTES; i++) {
|
||||
context_arr.set([examples_ab.at(i)], i);
|
||||
}
|
||||
|
||||
// Reserving buffer for the message
|
||||
const message_str = "Arbitrary data to hash";
|
||||
const message_ab = textEncoder.encode(message_str);
|
||||
const message_arr = reserve(offset, message_ab.length);
|
||||
|
||||
for (let i = 0; i < message_ab.length; i++) {
|
||||
message_arr.set([message_ab.at(i)], i);
|
||||
}
|
||||
console.log(`message - ${message_arr}`);
|
||||
|
||||
// Reserving buffer for the key
|
||||
const key = reserve(offset, hydro.secretbox_KEYBYTES);
|
||||
hydro_secretbox_keygen(key.byteOffset);
|
||||
console.log(`generated key - ${key.toString("hex")}`);
|
||||
|
||||
// Reserving buffer for the cipher text
|
||||
const cipherTextLength = hydro.secretbox_HEADERBYTES + message_arr.length;
|
||||
const ciphertext = reserve(offset, cipherTextLength);
|
||||
|
||||
// Enciphering single message (thus use of msg_id 0n -- 'n' as libhydrogen expects i64)
|
||||
hydro_secretbox_encrypt(
|
||||
ciphertext.byteOffset,
|
||||
message_arr.byteOffset,
|
||||
message_arr.byteLength,
|
||||
0n,
|
||||
context_arr.byteOffset,
|
||||
key.byteOffset,
|
||||
);
|
||||
|
||||
// Reserving buffer for the decrypted plain text
|
||||
const decryptedPlaintextLength = ciphertext.byteLength - hydro.secretbox_HEADERBYTES;
|
||||
const decryptedPlaintext = reserve(offset, decryptedPlaintextLength);
|
||||
|
||||
// Deciphering single message (thus use of msg_id 0n -- 'n' as libhydrogen expects i64)
|
||||
const res = hydro_secretbox_decrypt(
|
||||
decryptedPlaintext.byteOffset,
|
||||
ciphertext.byteOffset,
|
||||
ciphertext.byteLength,
|
||||
0n,
|
||||
context_arr.byteOffset,
|
||||
key.byteOffset,
|
||||
);
|
||||
if (res === 0) {
|
||||
// As secretbox is an authenticated encryption (AEAD) algorithm
|
||||
// we check that the ciphertext was authentic
|
||||
console.log("cipherText not forged");
|
||||
// Decoding Uint8 encoded string
|
||||
const textDecoder = new TextDecoder();
|
||||
console.log(`decryptedPlaintext - ${textDecoder.decode(decryptedPlaintext)}`);
|
||||
}
|
||||
|
||||
message_arr.fill(0);
|
||||
key.fill(0);
|
||||
ciphertext.fill(0);
|
||||
decryptedPlaintext.fill(0);
|
||||
}
|
||||
|
||||
//
|
||||
// Symmetric Encryption using a Key Generated
|
||||
// via Asymmetric key exchange
|
||||
//
|
||||
function symmetric_encryption_via_asymmetric_key_exchange() {
|
||||
console.log("\n=== symmetric_encryption_via_asymmetric_key_exchange ===\n");
|
||||
|
||||
const { hydro_kx_keygen, hydro_kx_kk_1, hydro_kx_kk_2, hydro_kx_kk_3 } = instance.exports;
|
||||
const textEncoder = new TextEncoder();
|
||||
|
||||
//
|
||||
// Reserve space in the buffer for the keypair exchange
|
||||
//
|
||||
// Both Alice and Bob need a keypair
|
||||
//
|
||||
// Both Alice and bob need a pair of session keys
|
||||
// tx to encrpyt
|
||||
// rx to decrypt
|
||||
//
|
||||
// Alice initiates the key exchange, so she also needs a kx state
|
||||
// buffer
|
||||
//
|
||||
|
||||
const offset = { value: 0 };
|
||||
const alice = {
|
||||
static: {
|
||||
pk: reserve(offset, hydro.kx_PUBLICKEYBYTES),
|
||||
sk: reserve(offset, hydro.kx_SECRETKEYBYTES),
|
||||
},
|
||||
session: {
|
||||
rx: reserve(offset, hydro.kx_SESSIONKEYBYTES),
|
||||
tx: reserve(offset, hydro.kx_SESSIONKEYBYTES),
|
||||
},
|
||||
return {
|
||||
context,
|
||||
input,
|
||||
signature: Buffer.from(signature).toString("hex")
|
||||
};
|
||||
|
||||
const bob = {
|
||||
static: {
|
||||
pk: reserve(offset, hydro.kx_PUBLICKEYBYTES),
|
||||
sk: reserve(offset, hydro.kx_SECRETKEYBYTES),
|
||||
},
|
||||
session: {
|
||||
rx: reserve(offset, hydro.kx_SESSIONKEYBYTES),
|
||||
tx: reserve(offset, hydro.kx_SESSIONKEYBYTES),
|
||||
},
|
||||
};
|
||||
|
||||
const keysOffset = offset.value;
|
||||
alice.state = reserve(offset, hydro.kx_SESSIONKEYBYTES);
|
||||
|
||||
// We need two "packets" for the messages exchanged between
|
||||
// Alice and Bob during the key exchange
|
||||
const packets = {
|
||||
1: reserve(offset, hydro.kx_KK_PACKET1BYTES),
|
||||
2: reserve(offset, hydro.kx_KK_PACKET2BYTES),
|
||||
};
|
||||
|
||||
console.log("\n=== symmetric_encryption_via_asymmetric_key_exchange ===\n");
|
||||
//
|
||||
// Generate the static keys
|
||||
//
|
||||
console.log("------------ALICEKEYGEN------------");
|
||||
hydro_kx_keygen(alice.static.pk.byteOffset);
|
||||
console.log("---------------ALICE---------------");
|
||||
console.log(alice);
|
||||
|
||||
console.log("-------------BOBKEYGEN-------------");
|
||||
hydro_kx_keygen(bob.static.pk.byteOffset);
|
||||
console.log("----------------BOB----------------");
|
||||
console.log(bob);
|
||||
|
||||
//
|
||||
// Key exchange
|
||||
//
|
||||
|
||||
// Performing kk_1 (Generating alice's ephemeral keypair and packet 1)
|
||||
console.log("-----------hydro_kx_kk_1-----------");
|
||||
hydro_kx_kk_1(
|
||||
alice.state.byteOffset,
|
||||
packets[1].byteOffset,
|
||||
bob.static.pk.byteOffset,
|
||||
alice.static.pk.byteOffset,
|
||||
);
|
||||
console.log("---------------ALICE---------------");
|
||||
console.dir(alice, { maxArrayLength: null });
|
||||
console.log("--------------packets--------------");
|
||||
console.log(packets);
|
||||
|
||||
// Performing kk_2 (Generating bob's response packet 2 and his copy of session keys)
|
||||
console.log("-----------hydro_kx_kk_2-----------");
|
||||
hydro_kx_kk_2(
|
||||
bob.session.rx.byteOffset,
|
||||
packets[2].byteOffset,
|
||||
packets[1].byteOffset,
|
||||
alice.static.pk.byteOffset,
|
||||
bob.static.pk.byteOffset,
|
||||
);
|
||||
console.log("----------------BOB----------------");
|
||||
console.dir(bob, { maxArrayLength: null });
|
||||
console.log("--------------packets--------------");
|
||||
console.log(packets);
|
||||
|
||||
// Performing kk_3 (Generating Alice's copy of session keys)
|
||||
console.log("-----------hydro_kx_kk_3-----------");
|
||||
hydro_kx_kk_3(
|
||||
alice.state.byteOffset,
|
||||
alice.session.rx.byteOffset,
|
||||
packets[2].byteOffset,
|
||||
alice.static.pk.byteOffset,
|
||||
);
|
||||
console.log("---------------ALICE---------------");
|
||||
console.dir(alice, { maxArrayLength: null });
|
||||
|
||||
console.log("-----------KEYS EXCHANGED----------");
|
||||
|
||||
// Tidy up the buffer space used in the key exchange
|
||||
alice.state.fill(0);
|
||||
packets[1].fill(0);
|
||||
packets[2].fill(0);
|
||||
offset.value = keysOffset;
|
||||
|
||||
//
|
||||
// Use the session keys to exchange messages
|
||||
//
|
||||
const { hydro_secretbox_encrypt, hydro_secretbox_decrypt } = instance.exports;
|
||||
|
||||
const context_str = "Examples";
|
||||
const context_arr = reserve(offset, hydro.hash_CONTEXTBYTES);
|
||||
const examples_ab = textEncoder.encode(context_str);
|
||||
|
||||
for (let i = 0; i < hydro.hash_CONTEXTBYTES; i++) {
|
||||
context_arr.set([examples_ab.at(i)], i);
|
||||
}
|
||||
|
||||
const message1_str = "Hello Bob";
|
||||
const message1_ab = textEncoder.encode(message1_str);
|
||||
const message1_arr = reserve(offset, message1_ab.length);
|
||||
|
||||
for (let i = 0; i < message1_ab.length; i++) {
|
||||
message1_arr.set([message1_ab.at(i)], i);
|
||||
}
|
||||
|
||||
console.log(`message1_arr - ${message1_arr}`);
|
||||
|
||||
const ciphertext1_length = hydro.secretbox_HEADERBYTES + message1_arr.length;
|
||||
const ciphertext1_arr = reserve(offset, ciphertext1_length);
|
||||
|
||||
//
|
||||
// Alice Encypts the message with her session tx key
|
||||
//
|
||||
// Enciphering single message (thus use of msg_id 0n -- 'n' as libhydrogen expects i64)
|
||||
//
|
||||
hydro_secretbox_encrypt(
|
||||
ciphertext1_arr.byteOffset,
|
||||
message1_arr.byteOffset,
|
||||
message1_arr.byteLength,
|
||||
0n,
|
||||
context_arr.byteOffset,
|
||||
alice.session.tx.byteOffset,
|
||||
);
|
||||
|
||||
//
|
||||
// Bob Decrypts the mesaage with his session rx key
|
||||
//
|
||||
// Deciphering single message (thus use of msg_id 0n -- 'n' as libhydrogen expects i64)
|
||||
//
|
||||
const decryptedPlaintext1Length = ciphertext1_arr.byteLength - hydro.secretbox_HEADERBYTES;
|
||||
const decryptedPlaintext1 = reserve(offset, decryptedPlaintext1Length);
|
||||
|
||||
const res1 = hydro_secretbox_decrypt(
|
||||
decryptedPlaintext1.byteOffset,
|
||||
ciphertext1_arr.byteOffset,
|
||||
ciphertext1_arr.byteLength,
|
||||
0n,
|
||||
context_arr.byteOffset,
|
||||
bob.session.rx.byteOffset,
|
||||
);
|
||||
|
||||
// As secretbox is an authenticated encryption (AEAD) algorithm
|
||||
// we check that the ciphertext was authentic
|
||||
if (res1 === 0) {
|
||||
console.log("ciphertext1_arr not forged");
|
||||
// Decoding Uint8 encoded string
|
||||
const textDecoder = new TextDecoder();
|
||||
console.log(`decryptedPlaintext1 - ${textDecoder.decode(decryptedPlaintext1)}`);
|
||||
}
|
||||
|
||||
//
|
||||
// Bob sends a reply to Alice
|
||||
//
|
||||
const message2_str = "Hello Alice";
|
||||
const message2_ab = textEncoder.encode(message2_str);
|
||||
const message2_arr = reserve(offset, message2_ab.length);
|
||||
|
||||
for (let i = 0; i < message2_ab.length; i++) {
|
||||
message2_arr.set([message2_ab.at(i)], i);
|
||||
}
|
||||
|
||||
console.log(`message2_arr - ${message2_arr}`);
|
||||
|
||||
const ciphertext2Length = hydro.secretbox_HEADERBYTES + message2_arr.length;
|
||||
const ciphertext2_arr = reserve(offset, ciphertext2Length);
|
||||
|
||||
//
|
||||
// Bob encrypts the message with his session tx key
|
||||
//
|
||||
hydro_secretbox_encrypt(
|
||||
ciphertext2_arr.byteOffset,
|
||||
message2_arr.byteOffset,
|
||||
message2_arr.byteLength,
|
||||
0n,
|
||||
context_arr.byteOffset,
|
||||
bob.session.tx.byteOffset,
|
||||
);
|
||||
|
||||
//
|
||||
// Alice decrypts the message with her session rx key
|
||||
//
|
||||
const decryptedPlaintext2Length = ciphertext2_arr.byteLength - hydro.secretbox_HEADERBYTES;
|
||||
const decryptedPlaintext2_arr = reserve(offset, decryptedPlaintext2Length);
|
||||
|
||||
const res2 = hydro_secretbox_decrypt(
|
||||
decryptedPlaintext2_arr.byteOffset,
|
||||
ciphertext2_arr.byteOffset,
|
||||
ciphertext2_arr.byteLength,
|
||||
0n,
|
||||
context_arr.byteOffset,
|
||||
alice.session.rx.byteOffset,
|
||||
);
|
||||
|
||||
// As secretbox is an authenticated encryption (AEAD) algorithm
|
||||
// we check that the ciphertext was authentic
|
||||
if (res2 === 0) {
|
||||
console.log("ciphertext2 not forged");
|
||||
const textDecoder = new TextDecoder();
|
||||
// Decoding Uint8 encoded string
|
||||
console.log(`decryptedPlaintext2 - ${textDecoder.decode(decryptedPlaintext2_arr)}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default LibHydrogenCurve25519Signing;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue