2026-01-17 12:02:08 +01:00
|
|
|
/**
|
2026-03-04 11:30:44 +01:00
|
|
|
* Node.js secure memory implementation.
|
|
|
|
|
*
|
|
|
|
|
* When the optional native addon (nomyo-native) is installed and built,
|
|
|
|
|
* this implementation provides true OS-level memory locking (mlock) and
|
|
|
|
|
* secure zeroing via explicit_bzero / SecureZeroMemory.
|
|
|
|
|
*
|
|
|
|
|
* Without the native addon it falls back to pure-JS zeroing only.
|
|
|
|
|
*
|
|
|
|
|
* To build the native addon:
|
|
|
|
|
* cd native && npm install && npm run build
|
2026-01-17 12:02:08 +01:00
|
|
|
*/
|
|
|
|
|
|
2026-03-04 11:30:44 +01:00
|
|
|
import { SecureMemory } from './secure';
|
|
|
|
|
import { ProtectionInfo } from '../../types/crypto';
|
|
|
|
|
|
|
|
|
|
interface NativeAddon {
|
|
|
|
|
mlockBuffer(buf: Buffer): boolean;
|
|
|
|
|
munlockBuffer(buf: Buffer): boolean;
|
|
|
|
|
secureZeroBuffer(buf: Buffer): void;
|
|
|
|
|
getPageSize(): number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Try to load the optional native addon once at module init time.
|
|
|
|
|
let nativeAddon: NativeAddon | null = null;
|
|
|
|
|
try {
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
|
|
|
const loaded = require('nomyo-native') as NativeAddon | null;
|
|
|
|
|
if (loaded && typeof loaded.mlockBuffer === 'function') {
|
|
|
|
|
nativeAddon = loaded;
|
|
|
|
|
}
|
|
|
|
|
} catch (_e) {
|
|
|
|
|
// Native addon not installed — degrade gracefully
|
|
|
|
|
}
|
2026-01-17 12:02:08 +01:00
|
|
|
|
|
|
|
|
export class NodeSecureMemory implements SecureMemory {
|
2026-03-04 11:30:44 +01:00
|
|
|
private readonly hasNative: boolean;
|
|
|
|
|
|
|
|
|
|
constructor() {
|
|
|
|
|
this.hasNative = nativeAddon !== null;
|
|
|
|
|
if (this.hasNative) {
|
|
|
|
|
console.log('nomyo-native addon loaded: mlock + secure-zero available');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-17 12:02:08 +01:00
|
|
|
/**
|
2026-03-04 11:30:44 +01:00
|
|
|
* Zero memory.
|
|
|
|
|
* With native addon: uses explicit_bzero / SecureZeroMemory (not optimized away).
|
|
|
|
|
* Without native addon: fills ArrayBuffer with zeros via Uint8Array (best effort).
|
2026-01-17 12:02:08 +01:00
|
|
|
*/
|
|
|
|
|
zeroMemory(data: ArrayBuffer): void {
|
2026-03-04 11:30:44 +01:00
|
|
|
if (this.hasNative && nativeAddon) {
|
|
|
|
|
// Buffer.from(arrayBuffer) shares the underlying memory (no copy)
|
|
|
|
|
const buf = Buffer.from(data);
|
|
|
|
|
nativeAddon.secureZeroBuffer(buf);
|
|
|
|
|
}
|
|
|
|
|
// Always also zero via JS for defence-in-depth
|
2026-01-17 12:02:08 +01:00
|
|
|
const view = new Uint8Array(data);
|
|
|
|
|
view.fill(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2026-03-04 11:30:44 +01:00
|
|
|
* Attempt to lock an ArrayBuffer in physical memory (prevent swapping).
|
|
|
|
|
* Best-effort: fails gracefully without CAP_IPC_LOCK / elevated privileges.
|
|
|
|
|
*/
|
|
|
|
|
lockMemory(data: ArrayBuffer): boolean {
|
|
|
|
|
if (!this.hasNative || !nativeAddon) return false;
|
|
|
|
|
const buf = Buffer.from(data);
|
|
|
|
|
return nativeAddon.mlockBuffer(buf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Unlock previously locked memory.
|
|
|
|
|
*/
|
|
|
|
|
unlockMemory(data: ArrayBuffer): boolean {
|
|
|
|
|
if (!this.hasNative || !nativeAddon) return false;
|
|
|
|
|
const buf = Buffer.from(data);
|
|
|
|
|
return nativeAddon.munlockBuffer(buf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get memory protection information.
|
2026-01-17 12:02:08 +01:00
|
|
|
*/
|
|
|
|
|
getProtectionInfo(): ProtectionInfo {
|
2026-03-04 11:30:44 +01:00
|
|
|
if (this.hasNative) {
|
|
|
|
|
return {
|
|
|
|
|
canLock: true,
|
|
|
|
|
isPlatformSecure: true,
|
|
|
|
|
method: 'mlock',
|
|
|
|
|
details:
|
|
|
|
|
'Node.js with nomyo-native addon: mlock + explicit_bzero/SecureZeroMemory available.',
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-17 12:02:08 +01:00
|
|
|
return {
|
|
|
|
|
canLock: false,
|
|
|
|
|
isPlatformSecure: false,
|
|
|
|
|
method: 'zero-only',
|
|
|
|
|
details:
|
2026-03-04 11:30:44 +01:00
|
|
|
'Node.js (pure JS): memory locking not available without native addon. ' +
|
2026-01-17 12:02:08 +01:00
|
|
|
'Using immediate zeroing only. ' +
|
2026-03-04 11:30:44 +01:00
|
|
|
'Build the optional native addon for mlock support: cd native && npm install && npm run build',
|
2026-01-17 12:02:08 +01:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|