mirror of
https://github.com/recrof/map.meshcore.dev-uploader.git
synced 2026-03-28 17:42:45 +01:00
230 lines
7.7 KiB
JavaScript
230 lines
7.7 KiB
JavaScript
import fs from 'node:fs';
|
|
|
|
const Module = (async () => {
|
|
const memory = new WebAssembly.Memory({ initial: 4 });
|
|
const imports = { env: { memory } };
|
|
const program = await WebAssembly.instantiate(fs.readFileSync('./supercop/supercop.wasm'), imports);
|
|
|
|
return {
|
|
memory: memory,
|
|
instance: program.instance,
|
|
exports: program.instance.exports,
|
|
};
|
|
})();
|
|
|
|
function toBuffer(data) {
|
|
return data instanceof Buffer ? data : Buffer.from(data);
|
|
}
|
|
|
|
function randomBytes(length) {
|
|
return Buffer.from(new Array(length).fill(0).map(() => Math.floor(Math.random() * 256)));
|
|
}
|
|
|
|
export function createSeed() {
|
|
return randomBytes(32);
|
|
}
|
|
|
|
export function isSeed(data) {
|
|
return data.length === 32;
|
|
}
|
|
|
|
export function isPublicKey(data) {
|
|
return data.length === 32;
|
|
}
|
|
|
|
export function isSignature(data) {
|
|
return data.length === 64;
|
|
}
|
|
|
|
export function isSecretKey(data) {
|
|
return data.length === 64;
|
|
}
|
|
|
|
export class KeyPair {
|
|
publicKey;
|
|
secretKey;
|
|
constructor() {
|
|
// Intentionally empty
|
|
}
|
|
// Passes signing on to the exported stand-alone method
|
|
// Async, so the error = promise rejection
|
|
async sign(message) {
|
|
if (!isSecretKey(this.secretKey))
|
|
throw new Error('No secret key on this keypair, only verification is possible');
|
|
if (!isPublicKey(this.publicKey))
|
|
throw new Error('Invalid public key');
|
|
return sign(message, this.publicKey, this.secretKey);
|
|
}
|
|
// Passes verification on to the exported stand-alone method
|
|
verify(signature, message) {
|
|
if (!isPublicKey(this.publicKey))
|
|
throw new Error('Invalid public key');
|
|
return verify(signature, message, this.publicKey);
|
|
}
|
|
keyExchange(theirPublicKey) {
|
|
if (!isSecretKey(this.secretKey))
|
|
throw new Error('Invalid secret key');
|
|
return keyExchange(theirPublicKey, this.secretKey);
|
|
}
|
|
toJSON() {
|
|
return {
|
|
publicKey: this.publicKey ? [...this.publicKey] : undefined,
|
|
secretKey: this.secretKey ? [...this.secretKey] : undefined,
|
|
};
|
|
}
|
|
static create(seed) {
|
|
return createKeyPair(seed);
|
|
}
|
|
static from(data) {
|
|
return keyPairFrom(data);
|
|
}
|
|
}
|
|
|
|
export function keyPairFrom(data) {
|
|
if (typeof data !== 'object')
|
|
throw new Error('Invalid input data');
|
|
// Sanitization and sanity checking
|
|
data = {
|
|
publicKey: toBuffer(data.publicKey),
|
|
secretKey: toBuffer(data.secretKey)
|
|
};
|
|
|
|
if (!isPublicKey(data.publicKey))
|
|
throw new Error('Invalid public key');
|
|
// Not checking the secretKey, allowed to be missing
|
|
const keypair = new KeyPair();
|
|
Object.assign(keypair, data);
|
|
|
|
return keypair;
|
|
}
|
|
|
|
export async function createKeyPair(seed) {
|
|
// Pre-fetch module components
|
|
const fn = (await Module).exports;
|
|
const mem = (await Module).memory;
|
|
// Ensure we have a valid seed
|
|
if (Array.isArray(seed))
|
|
seed = Buffer.from(seed);
|
|
if (!isSeed(seed))
|
|
throw new Error('Invalid seed');
|
|
// Reserve wasm-side memory
|
|
const seedPtr = fn._malloc(32);
|
|
const publicKeyPtr = fn._malloc(32);
|
|
const secretKeyPtr = fn._malloc(64);
|
|
const seedBuf = new Uint8Array(mem.buffer, seedPtr, 32);
|
|
const publicKey = new Uint8Array(mem.buffer, publicKeyPtr, 32);
|
|
const secretKey = new Uint8Array(mem.buffer, secretKeyPtr, 64);
|
|
seedBuf.set(seed);
|
|
fn.create_keypair(publicKeyPtr, secretKeyPtr, seedPtr);
|
|
fn._free(seedPtr);
|
|
fn._free(publicKeyPtr);
|
|
fn._free(secretKeyPtr);
|
|
|
|
return keyPairFrom({
|
|
publicKey: Buffer.from(publicKey),
|
|
secretKey: Buffer.from(secretKey),
|
|
});
|
|
}
|
|
|
|
export async function sign(message, publicKey, secretKey) {
|
|
// Pre-fetch module components
|
|
const fn = (await Module).exports;
|
|
const mem = (await Module).memory;
|
|
|
|
// Sanitization and sanity checking
|
|
if (Array.isArray(publicKey))
|
|
publicKey = Buffer.from(publicKey);
|
|
if (Array.isArray(secretKey))
|
|
secretKey = Buffer.from(secretKey);
|
|
if (!isPublicKey(publicKey))
|
|
throw new Error('Invalid public key');
|
|
if (!isSecretKey(secretKey))
|
|
throw new Error('Invalid secret key');
|
|
if ('string' === typeof message)
|
|
message = Buffer.from(message);
|
|
// Allocate memory on the wasm side to transfer variables
|
|
const messageLen = message.length;
|
|
const messageArrPtr = fn._malloc(messageLen);
|
|
const messageArr = new Uint8Array(mem.buffer, messageArrPtr, messageLen);
|
|
const publicKeyArrPtr = fn._malloc(32);
|
|
const publicKeyArr = new Uint8Array(mem.buffer, publicKeyArrPtr, 32);
|
|
const secretKeyArrPtr = fn._malloc(64);
|
|
const secretKeyArr = new Uint8Array(mem.buffer, secretKeyArrPtr, 64);
|
|
const sigPtr = fn._malloc(64);
|
|
const sig = new Uint8Array(mem.buffer, sigPtr, 64);
|
|
messageArr.set(message);
|
|
publicKeyArr.set(publicKey);
|
|
secretKeyArr.set(secretKey);
|
|
await fn.sign(sigPtr, messageArrPtr, messageLen, publicKeyArrPtr, secretKeyArrPtr);
|
|
// Free used memory on wasm side
|
|
fn._free(messageArrPtr);
|
|
fn._free(publicKeyArrPtr);
|
|
fn._free(secretKeyArrPtr);
|
|
fn._free(sigPtr);
|
|
|
|
return Buffer.from(sig);
|
|
}
|
|
export async function verify(signature, message, publicKey) {
|
|
const fn = (await Module).exports;
|
|
const mem = (await Module).memory;
|
|
|
|
// Sanitization and sanity checking
|
|
if (Array.isArray(signature))
|
|
signature = Buffer.from(signature);
|
|
if (Array.isArray(publicKey))
|
|
publicKey = Buffer.from(publicKey);
|
|
if (!isPublicKey(publicKey))
|
|
throw new Error('Invalid public key');
|
|
if (!isSignature(signature))
|
|
throw new Error('Invalid signature');
|
|
if ('string' === typeof message)
|
|
message = Buffer.from(message);
|
|
// Allocate memory on the wasm side to transfer variables
|
|
const messageLen = message.length;
|
|
const messageArrPtr = fn._malloc(messageLen);
|
|
const messageArr = new Uint8Array(mem.buffer, messageArrPtr, messageLen);
|
|
const signatureArrPtr = fn._malloc(64);
|
|
const signatureArr = new Uint8Array(mem.buffer, signatureArrPtr, 64);
|
|
const publicKeyArrPtr = fn._malloc(32);
|
|
const publicKeyArr = new Uint8Array(mem.buffer, publicKeyArrPtr, 32);
|
|
messageArr.set(message);
|
|
signatureArr.set(signature);
|
|
publicKeyArr.set(publicKey);
|
|
const res = fn.verify(signatureArrPtr, messageArrPtr, messageLen, publicKeyArrPtr) === 1;
|
|
// Free used memory on wasm side
|
|
fn._free(messageArrPtr);
|
|
fn._free(signatureArrPtr);
|
|
fn._free(publicKeyArrPtr);
|
|
|
|
return res;
|
|
}
|
|
export async function keyExchange(theirPublicKey, ourSecretKey) {
|
|
const fn = (await Module).exports;
|
|
const mem = (await Module).memory;
|
|
|
|
// Sanitization and sanity checking
|
|
if (Array.isArray(theirPublicKey))
|
|
theirPublicKey = Buffer.from(theirPublicKey);
|
|
if (Array.isArray(ourSecretKey))
|
|
ourSecretKey = Buffer.from(ourSecretKey);
|
|
if (!isPublicKey(theirPublicKey))
|
|
throw new Error('Invalid public key');
|
|
if (!isSecretKey(ourSecretKey))
|
|
throw new Error('Invalid secret key');
|
|
// Allocate memory on the wasm side to transfer variables
|
|
const sharedSecretArrPtr = fn._malloc(32);
|
|
const sharedSecretArr = new Uint8Array(mem.buffer, sharedSecretArrPtr, 32);
|
|
const publicKeyArrPtr = fn._malloc(32);
|
|
const publicKeyArr = new Uint8Array(mem.buffer, publicKeyArrPtr, 32);
|
|
const secretKeyArrPtr = fn._malloc(64);
|
|
const secretKeyArr = new Uint8Array(mem.buffer, secretKeyArrPtr, 64);
|
|
publicKeyArr.set(theirPublicKey);
|
|
secretKeyArr.set(ourSecretKey);
|
|
fn.key_exchange(sharedSecretArrPtr, publicKeyArrPtr, secretKeyArrPtr);
|
|
// Free used memory on wasm side
|
|
fn._free(sharedSecretArrPtr);
|
|
fn._free(publicKeyArrPtr);
|
|
fn._free(secretKeyArrPtr);
|
|
return Buffer.from(sharedSecretArr);
|
|
}
|