Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 22 additions & 4 deletions node/EncryptMessage.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,25 @@
const ModuleFactory = require('./encrypt.js');
const path = require('path');
const ModuleFactory = require('./encrypt_node.js');

let ModulePromise = null;

function getModule() {
if (!ModulePromise) {
ModulePromise = ModuleFactory({
locateFile: (filename) => {
// Should return absolute path to encrypt_node.wasm
if (filename.endsWith('.wasm')) {
return path.join(__dirname, 'encrypt_node.wasm');
}
return path.join(__dirname, filename);
}
});
}
return ModulePromise;
}

async function encryptMessage(txData, publicKey, aadTE = '', aadAES = '') {
const Module = await ModuleFactory();
const Module = await getModule();
return Module.ccall(
'encryptMessage', // Name of the exported C++ function
'string', // Return type
Expand All @@ -12,7 +30,7 @@ async function encryptMessage(txData, publicKey, aadTE = '', aadAES = '') {

async function encryptMessageDualKey(
txData, firstPublicKey, secondPublicKey, aadTE = '', aadAES = '') {
const Module = await ModuleFactory();
const Module = await getModule();
return Module.ccall(
'encryptMessageDualKey', // Name of the exported C++ function
'string', // Return type
Expand All @@ -22,7 +40,7 @@ async function encryptMessageDualKey(
}

async function encryptMessageMockup(txData) {
const Module = await ModuleFactory();
const Module = await getModule();
return Module.ccall(
'encryptMessageMockup', // Name of the exported C++ function
'string', // Return type
Expand Down
115 changes: 111 additions & 4 deletions node/EncryptMessage.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,114 @@
import ModuleFactory from './encrypt.js';
import ModuleFactory from './encrypt_web.js';
import wasmAsset from './encrypt_web.wasm';

let ModulePromise = null;

function getRuntimeBaseUrl() {
if (typeof self !== 'undefined' && self.location && self.location.href) {
return self.location.href;
}
if (typeof document !== 'undefined' && document.baseURI) {
return document.baseURI;
}
return null;
}

function toAbsoluteAssetUrl(filename) {
if (typeof filename !== 'string' || filename.length === 0) {
throw new TypeError('filename must be a non-empty string');
}

const baseUrl = getRuntimeBaseUrl();
if (!baseUrl) {
return filename;
}

try {
return new URL(filename, baseUrl).href;
} catch {
return filename;
}
}

function getWasmModuleCandidate() {
if (typeof WebAssembly === 'undefined' || typeof WebAssembly.Module === 'undefined') {
return null;
}

if (wasmAsset instanceof WebAssembly.Module) {
return wasmAsset;
}

if (wasmAsset && wasmAsset.default instanceof WebAssembly.Module) {
return wasmAsset.default;
}

try {
if (wasmAsset instanceof Uint8Array || wasmAsset instanceof ArrayBuffer) {
return new WebAssembly.Module(wasmAsset);
}

if (wasmAsset && (wasmAsset.default instanceof Uint8Array || wasmAsset.default instanceof ArrayBuffer)) {
return new WebAssembly.Module(wasmAsset.default);
}
} catch {
// Fall through to URL-based loading below.
}

return null;
}

function getWasmUrlCandidate() {
if (typeof wasmAsset === 'string' && wasmAsset.length > 0) {
return wasmAsset;
}

if (wasmAsset && typeof wasmAsset.default === 'string' && wasmAsset.default.length > 0) {
return wasmAsset.default;
}

if (wasmAsset && typeof wasmAsset.href === 'string' && wasmAsset.href.length > 0) {
return wasmAsset.href;
}

return null;
}

function getModule() {
if (!ModulePromise) {
const moduleOptions = {};
const wasmModule = getWasmModuleCandidate();
const wasmUrl = getWasmUrlCandidate();

if (wasmModule) {
moduleOptions.instantiateWasm = (imports, receiveInstance) => {
const instance = new WebAssembly.Instance(wasmModule, imports);
receiveInstance(instance, wasmModule);
return instance.exports;
};
}

if (wasmUrl) {
moduleOptions.locateFile = (filename) => {
if (filename.endsWith('.wasm')) {
return wasmUrl;
}

return toAbsoluteAssetUrl(filename);
};
} else {
moduleOptions.locateFile = (filename) => {
return toAbsoluteAssetUrl(filename);
};
}

ModulePromise = ModuleFactory(moduleOptions);
}
return ModulePromise;
}

export async function encryptMessage(txData, publicKey, aadTE = '', aadAES = '') {
const Module = await ModuleFactory();
const Module = await getModule();
return Module.ccall(
'encryptMessage', // Name of the exported C++ function
'string', // Return type
Expand All @@ -12,7 +119,7 @@ export async function encryptMessage(txData, publicKey, aadTE = '', aadAES = '')

export async function encryptMessageDualKey(
txData, firstPublicKey, secondPublicKey, aadTE = '', aadAES = '') {
const Module = await ModuleFactory();
const Module = await getModule();
return Module.ccall(
'encryptMessageDualKey', // Name of the exported C++ function
'string', // Return type
Expand All @@ -22,7 +129,7 @@ export async function encryptMessageDualKey(
}

export async function encryptMessageMockup(txData) {
const Module = await ModuleFactory();
const Module = await getModule();
return Module.ccall(
'encryptMessageMockup', // Name of the exported C++ function
'string', // Return type
Expand Down
56 changes: 56 additions & 0 deletions node/EncryptMessageNode.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { createRequire } from 'module';
import { fileURLToPath } from 'url';
const require = createRequire(import.meta.url);
const ModuleFactory = require('./encrypt_node.js');
let ModulePromise = null;
function resolveModuleAssetPath(filename) {
if (typeof filename !== 'string' || filename.length === 0) {
throw new TypeError('filename must be a non-empty string');
}
const resolvedPath = fileURLToPath(new URL(filename, import.meta.url));
if (typeof resolvedPath !== 'string' || resolvedPath.length === 0) {
throw new Error('Failed to resolve module asset path');
}
return resolvedPath;
}
function getModule() {
if (!ModulePromise) {
ModulePromise = ModuleFactory({
locateFile: (filename) => {
return resolveModuleAssetPath(filename);
}
});
}
return ModulePromise;
}

export async function encryptMessage(txData, publicKey, aadTE = '', aadAES = '') {
const Module = await getModule();
return Module.ccall(
'encryptMessage',
'string',
['string', 'string', 'string', 'string'],
[txData, publicKey, aadTE, aadAES]
);
}

export async function encryptMessageDualKey(
txData, firstPublicKey, secondPublicKey, aadTE = '', aadAES = '') {
const Module = await getModule();
return Module.ccall(
'encryptMessageDualKey',
'string',
['string', 'string', 'string', 'string', 'string'],
[txData, firstPublicKey, secondPublicKey, aadTE, aadAES]
);
}

export async function encryptMessageMockup(txData) {
const Module = await getModule();
return Module.ccall(
'encryptMessageMockup',
'string',
['string'],
[txData]
);
}
24 changes: 12 additions & 12 deletions node/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@skalenetwork/t-encrypt",
"version": "0.8.0",
"version": "0.9.0",

"keywords": [
"SKALE",
Expand All @@ -11,13 +11,6 @@
"encryption",
"wasm"
],
"browser": {
"fs": false,
"os": false,
"path": false,
"crypto": false,
"child_process": false
},
"homepage": "https://github.com/skalenetwork/libBLS",
"license": "AGPL-3.0",
"author": "SKALE Labs and contributors",
Expand All @@ -33,14 +26,21 @@
"sideEffects": false,
"exports": {
".": {
"import": "./EncryptMessage.mjs",
"require": "./EncryptMessage.js"
"browser": "./EncryptMessage.mjs",
"node": {
"import": "./EncryptMessageNode.mjs",
"require": "./EncryptMessage.js"
},
"default": "./EncryptMessage.mjs"
}
},
"files": [
"EncryptMessage.js",
"EncryptMessage.mjs",
"encrypt.js",
"encrypt.wasm"
"EncryptMessageNode.mjs",
"encrypt_node.js",
"encrypt_node.wasm",
"encrypt_web.js",
"encrypt_web.wasm"
]
}
2 changes: 1 addition & 1 deletion scripts/run_emscripten_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ cp "$ROOT_DIR/tools/generate_bls_keys" "$ABS_BUILD_DIR/"
cp "$ROOT_DIR/tools/decrypt_message" "$ABS_BUILD_DIR/"
cp "$ROOT_DIR/test/test.js" "$ABS_BUILD_DIR/"
cp "$ROOT_DIR/test/test2Keys.js" "$ABS_BUILD_DIR/"
cp "$ABS_BUILD_DIR/threshold_encryption/encrypt."* "$ABS_BUILD_DIR/"
cp "$ABS_BUILD_DIR/threshold_encryption/encrypt_node."* "$ABS_BUILD_DIR/"
cd "$ABS_BUILD_DIR/"

# Number of times to run the test block
Expand Down
2 changes: 1 addition & 1 deletion test/test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const ModuleFactory = require("./encrypt.js");
const ModuleFactory = require("./encrypt_node.js");

const BLS_PUBLIC_KEY = process.argv[2];
const TX_DATA = process.argv[3];
Expand Down
2 changes: 1 addition & 1 deletion test/test2Keys.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const ModuleFactory = require("./encrypt.js");
const ModuleFactory = require("./encrypt_node.js");

const FIRST_BLS_PUBLIC_KEY = process.argv[2];
const SECOND_BLS_PUBLIC_KEY = process.argv[3];
Expand Down
38 changes: 33 additions & 5 deletions threshold_encryption/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,39 @@ if (NOT EMSCRIPTEN)
endif()

if (EMSCRIPTEN)
add_executable(encrypt ../threshold_encryption/encryptMessage.cpp ../tools/utils.cpp)
set_target_properties(encrypt PROPERTIES LINK_FLAGS "-s EXIT_RUNTIME=1 -s USE_PTHREADS=0 -s MODULARIZE -s ALLOW_MEMORY_GROWTH=1 -s NO_DISABLE_EXCEPTION_CATCHING=1 -s EXPORTED_RUNTIME_METHODS='[\"ccall\"]' -s EXPORTED_FUNCTIONS='[\"_encryptMessage\", \"_encryptMessageDualKey\",\"_encryptMessageMockup\"]' -s ENVIRONMENT='node,web' -s SINGLE_FILE=1")
target_include_directories(encrypt SYSTEM PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${THIRD_PARTY_DIR})
target_compile_definitions(encrypt PRIVATE MCL=1)
target_link_libraries(encrypt PRIVATE te)
set(ENCRYPT_SOURCES ../threshold_encryption/encryptMessage.cpp ../tools/utils.cpp)
set(ENCRYPT_LINK_FLAGS "-s EXIT_RUNTIME=1 -s USE_PTHREADS=0 -s MODULARIZE -s ALLOW_MEMORY_GROWTH=1 -s NO_DISABLE_EXCEPTION_CATCHING=1 -s EXPORTED_RUNTIME_METHODS='[\"ccall\"]' -s EXPORTED_FUNCTIONS='[\"_encryptMessage\", \"_encryptMessageDualKey\",\"_encryptMessageMockup\"]'")

# encrypt_node (for Node.js)
add_executable(encrypt_node ${ENCRYPT_SOURCES})
target_include_directories(encrypt_node SYSTEM PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${THIRD_PARTY_DIR})
target_compile_definitions(encrypt_node PRIVATE MCL=1)
target_link_libraries(encrypt_node PRIVATE te)
set_target_properties(encrypt_node PROPERTIES
OUTPUT_NAME "encrypt_node"
LINK_FLAGS "${ENCRYPT_LINK_FLAGS} -s ENVIRONMENT='node'"
)

# encrypt_web (for Browser / Vite / Webpack 5 / native ESM consumers)
add_executable(encrypt_web ${ENCRYPT_SOURCES})
target_include_directories(encrypt_web SYSTEM PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${THIRD_PARTY_DIR})
target_compile_definitions(encrypt_web PRIVATE MCL=1)
target_link_libraries(encrypt_web PRIVATE te)
set_target_properties(encrypt_web PROPERTIES
OUTPUT_NAME "encrypt_web"
LINK_FLAGS "${ENCRYPT_LINK_FLAGS} -s ENVIRONMENT='web' -s EXPORT_ES6=1"
)

add_custom_command(TARGET encrypt_node POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/encrypt_node.js ${CMAKE_SOURCE_DIR}/node/encrypt_node.js
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/encrypt_node.wasm ${CMAKE_SOURCE_DIR}/node/encrypt_node.wasm
COMMENT "Copying encrypt_node to node package folder"
)
add_custom_command(TARGET encrypt_web POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/encrypt_web.js ${CMAKE_SOURCE_DIR}/node/encrypt_web.js
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/encrypt_web.wasm ${CMAKE_SOURCE_DIR}/node/encrypt_web.wasm
COMMENT "Copying encrypt_web to node package folder"
Comment on lines +170 to +178
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The POST_BUILD steps copy generated artifacts into ${CMAKE_SOURCE_DIR}/node, which mutates the source tree and can break read-only source checkouts or multi-config/out-of-tree builds (multiple build dirs racing to overwrite the same files). Prefer copying into an install/package staging directory (e.g., via install(FILES ...) + cpack, or a dedicated package_node target that writes under ${CMAKE_CURRENT_BINARY_DIR}) and have the publish step pull from that output.

Copilot uses AI. Check for mistakes.
Comment on lines +175 to +178
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These POST_BUILD commands write build outputs into ${CMAKE_SOURCE_DIR}/node, which mutates the source tree during a build and can fail in read-only source checkouts (or cause dirty working trees in CI). Consider copying into the build tree and/or making this step opt-in (e.g., behind an option or an explicit install/packaging target).

Copilot uses AI. Check for mistakes.
)

add_executable(encrypt_message ../test/encryptMessageJS.cpp ../tools/utils.cpp)
target_include_directories(encrypt_message SYSTEM PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${THIRD_PARTY_DIR})
Expand Down
Loading