CyberChef/src/core/operations/LibHydrogenCurve25519Sign.mjs

300 lines
9.1 KiB
JavaScript
Raw Normal View History

2025-01-15 22:31:47 +00:00
/**
* @author Configured Things Ltd. [getconfigured@configuredthings.com]
2025-01-29 15:19:19 +02:00
* @copyright Crown Copyright 2025
2025-01-15 22:31:47 +00:00
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
2025-01-27 19:43:01 +02:00
import * as hydro from "@configuredthings/libhydrogen-wasm/libHydrogen.js";
2025-01-15 22:31:47 +00:00
/**
* LibHydrogen Curve25519 Sign operation
*/
class LibHydrogenCurve25519Signing extends Operation {
/**
* LibHydrogenCurve25519Sign constructor
*/
constructor() {
super();
this.name = "LibHydrogen Curve25519 Sign";
this.module = "Crypto";
this.description = "Computes a signature for a message using the lightweight LibHydrogen cryptography library";
this.infoURL = "https://libhydrogen.org/";
2025-01-29 21:28:13 +02:00
this.inputType = "ArrayBuffer";
2025-01-15 22:31:47 +00:00
this.outputType = "JSON";
this.args = [
{
2025-01-27 19:43:01 +02:00
name: "Context",
2025-01-15 22:31:47 +00:00
type: "string",
2025-01-27 19:43:01 +02:00
value: ""
2025-01-15 22:31:47 +00:00
},
{
2025-01-27 19:43:01 +02:00
name: "Sender's private key",
type: "byteArray",
value: ""
2025-01-15 22:31:47 +00:00
}
];
}
2025-01-29 15:19:19 +02:00
/* eslint-disable camelcase */
2025-01-15 22:31:47 +00:00
/**
2025-01-29 21:28:13 +02:00
* @param {ArrayBuffer} input
2025-01-15 22:31:47 +00:00
* @param {Object[]} args
* @returns {JSON}
*/
2025-01-27 19:43:01 +02:00
async run(input, args) {
const [context, privateKey] = args;
2025-01-29 15:19:19 +02:00
const wasmSrc = await fetch(new URL(`${self.docURL}/assets/libhydrogen-wasm/libhydrogen.wasm`));
const wasm = await WebAssembly.compileStreaming(wasmSrc);
2025-01-27 19:43:01 +02:00
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);
2025-01-15 22:31:47 +00:00
}
2025-01-29 15:19:19 +02:00
/* eslint-enable camelcase */
2025-01-15 22:31:47 +00:00
}
let instance, dataview;
2025-01-29 15:19:19 +02:00
/**
* Helper function to reserve space in the buffer used
* as a stack between js and the wasm.
* @param {Object} offset - Must be an an object so we can update it's value
* @param {number} offset.value
* @param {number} length
* @returns {Uint8Array} Returns a UInt8Array representation of the buffer
*
* @example
* // Designed to allow a sequence of reservations such as
*
* let offset = {value: 0};
* buf1 = reserve (offset, 100);
* buf2 = reserve (offset, 590);
* ...
*/
2025-01-15 22:31:47 +00:00
function reserve(offset, length) {
const a = new Uint8Array(dataview.buffer, offset.value, length);
const newOffset = a.byteOffset + a.byteLength;
offset.value = newOffset;
return a;
}
2025-01-29 15:19:19 +02:00
/**
* A signed JSON object.
* @typedef {Object} SignedJSON
* @property {Uint8Array} context - A buffer representing the context used to define the context of the signing operation,
see {@link https://github.com/jedisct1/libhydrogen/wiki/Contexts}
* @property {Uint8Array} input - A buffer representing the stringified JSON object used as the input to the signing operation
* @property {Uint8Array} signature - A buffer representing the digital signature of the signed JSON object
*/
/**
2025-01-29 21:28:13 +02:00
* Digital signing of an ArrayBuffer's contents
* @param {ArrayBuffer} input - An ArrayBuffer to be signed
2025-01-29 15:19:19 +02:00
* @param {string} context - A string used to define the context of the signing operation,
* see {@link https://github.com/jedisct1/libhydrogen/wiki/Contexts}
* @param {Uint8Array} privateKey - The private key to use for the digital signing operation
* @returns {SignedJSON} A signed JSON object
*/
2025-01-27 19:43:01 +02:00
async function sign(input, context, privateKey) {
2025-01-15 22:31:47 +00:00
// Importing libhydrogen's signing keygen and signing and verification functions
2025-01-29 15:19:19 +02:00
// eslint-disable-next-line camelcase
const { hydro_sign_create } = instance.exports;
2025-01-15 22:31:47 +00:00
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 };
2025-01-29 15:19:19 +02:00
const contextArr = reserve(offset, hydro.hash_CONTEXTBYTES);
const contextAb = textEncoder.encode(context);
2025-01-15 22:31:47 +00:00
for (let i = 0; i < hydro.hash_CONTEXTBYTES; i++) {
2025-01-29 15:19:19 +02:00
contextArr.set([contextAb.at(i)], i);
2025-01-15 22:31:47 +00:00
}
2025-01-29 21:28:13 +02:00
const messageTypedArr = new Uint8Array(input);
const messageArr = reserve(offset, messageTypedArr.length);
2025-01-15 22:31:47 +00:00
2025-01-29 21:28:13 +02:00
for (let i = 0; i < input.length; i++) {
messageArr.set([messageTypedArr.at(i)], i);
2025-01-15 22:31:47 +00:00
}
// Generate a key pair
2025-01-29 15:19:19 +02:00
const privateKeyArr = reserve(offset, hydro.sign_SECRETKEYBYTES);
2025-01-27 19:43:01 +02:00
for (let i = 0; i < privateKey.length; i++) {
2025-01-29 15:19:19 +02:00
privateKeyArr.set([privateKey.at(i)], i);
2025-01-27 19:43:01 +02:00
}
2025-01-15 22:31:47 +00:00
// Reserving memory for the signature
const signature = reserve(offset, hydro.sign_BYTES);
// Creating signature of message with secret key
hydro_sign_create(
signature.byteOffset,
2025-01-29 15:19:19 +02:00
messageArr.byteOffset,
messageArr.byteLength,
contextArr.byteOffset,
privateKeyArr.byteOffset,
2025-01-15 22:31:47 +00:00
);
2025-01-27 19:43:01 +02:00
return {
2025-01-29 15:19:19 +02:00
context: contextArr,
input: messageArr,
signature
2025-01-15 22:31:47 +00:00
};
}
export default LibHydrogenCurve25519Signing;