nomyo-js/src/core/memory/node.ts

106 lines
3.4 KiB
TypeScript
Raw Normal View History

2026-01-17 12:02:08 +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
*/
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 {
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
/**
* 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 {
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);
}
/**
* 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 {
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:
'Node.js (pure JS): memory locking not available without native addon. ' +
2026-01-17 12:02:08 +01:00
'Using immediate zeroing only. ' +
'Build the optional native addon for mlock support: cd native && npm install && npm run build',
2026-01-17 12:02:08 +01:00
};
}
}