nx.v2-native
v2.0.1
Published
Native Node addon for nx.v2 cryptographic library
Readme
nx.v2-native
A high-performance native Node.js addon for authenticated encryption, key management, and token-based secret handling. All cryptographic operations run in C++ via OpenSSL — zero crypto in JavaScript.
Features
- ChaCha20-Poly1305 authenticated encryption with AAD
- Deterministic encryption (SIV-style) for indexable ciphertext
- Key derivation via scrypt (password) and HKDF-SHA256 (subkeys)
- Token format — self-contained, versioned, base64url-encoded binary tokens
- Key rotation — KeyRing with multiple keys + seamless migration
- Replay protection — built-in TTL-based replay guard
- No JS crypto — all sensitive operations execute in native code
Install
npm install nx.v2-nativeNote: Prebuilt binaries are included for supported platforms. No compiler or OpenSSL installation required for end users.
Quick Start
const {
generateKey,
encrypt,
decrypt,
KeyRing,
} = require('nx.v2-native');
// Create a key ring and add a key
const ring = new KeyRing();
ring.add('key-2026', generateKey());
// Encrypt
const token = encrypt('sensitive data', { keyRing: ring });
// → "nx.v2.AgAHa2V5LTIwMjY..."
// Decrypt
const result = decrypt(token, { keyRing: ring });
console.log(result.data); // "sensitive data"API
Encryption & Decryption
encrypt(plaintext, options)
Encrypts a value and returns an nx.v2. token string.
- plaintext —
string,Buffer, or plain object (auto-serialized to JSON) - options.keyRing —
KeyRinginstance (required) - options.aad — additional authenticated data (
string,object, ornull) - options.metadata —
objectof string key-value pairs embedded in the token
const token = encrypt('hello', {
keyRing: ring,
aad: 'user:123',
metadata: { purpose: 'greeting' },
});encryptDeterministic(plaintext, options)
Same as encrypt, but produces identical tokens for identical inputs. Useful for indexing or deduplication.
const a = encryptDeterministic('hello', { keyRing: ring });
const b = encryptDeterministic('hello', { keyRing: ring });
// a === bdecrypt(token, options)
Decrypts a token. Throws on failure.
- options.keyRing —
KeyRinginstance - options.globalDecryptor —
GlobalDecryptorinstance (alternative) - options.aad — must match the AAD used during encryption
Returns:
{
data: 'hello', // decrypted plaintext as string
metadata: {}, // embedded metadata
keyId: 'key-2026', // which key was used
deterministic: false // whether deterministic mode was used
}Key Management
generateKey()
Returns a cryptographically random 32-byte Buffer.
const key = generateKey();deriveKeyFromPassword(password, options?)
Derives a key from a password using scrypt.
- password —
stringorBuffer - options.saltHex — hex-encoded salt (random if omitted)
- options.N — cost parameter (default
2^17, minimum2^14, must be power of 2) - options.r — block size (default
8) - options.p — parallelization (default
1)
const { key, saltHex } = deriveKeyFromPassword('my-password');
// Deterministic re-derivation
const { key: same } = deriveKeyFromPassword('my-password', { saltHex });deriveSubkey(masterKey, context, salt?)
Derives a subkey using HKDF-SHA256.
const master = generateKey();
const dbKey = deriveSubkey(master, 'database');
const apiKey = deriveSubkey(master, 'api-tokens');KeyRing
Manages multiple encryption keys with a designated current key.
const { KeyRing, generateKey } = require('nx.v2-native');
const ring = new KeyRing();
ring.add('v1', generateKey());
ring.add('v2', generateKey());
ring.setCurrent('v2');
ring.currentId; // 'v2'
ring.size; // 2
ring.has('v1'); // true
ring.resolve('v1'); // Buffer
ring.keyIds; // ['v2', 'v1'] — current first
ring.remove('v1'); // removes and wipes the buffer
ring.wipeAll(); // zeroes all keys, clears the ringGlobalDecryptor
Resolves keys across multiple KeyRings. Useful for multi-tenant or multi-service decryption.
const { GlobalDecryptor } = require('nx.v2-native');
const serviceA = new KeyRing();
serviceA.add('a-key', generateKey());
const serviceB = new KeyRing();
serviceB.add('b-key', generateKey());
const gd = new GlobalDecryptor();
gd.addRing(serviceA).addRing(serviceB);
// Decrypts tokens from either service
const result = decrypt(token, { globalDecryptor: gd });ReplayGuard
Prevents token reuse with an in-memory TTL store.
const { ReplayGuard } = require('nx.v2-native');
const guard = new ReplayGuard({ ttlMs: 300_000 }); // 5 minutes
// First use — succeeds
const result = guard.decrypt(token, { keyRing: ring });
// Second use — throws "nx: token has already been used."
guard.decrypt(token, { keyRing: ring });Custom store (e.g. Redis):
const guard = new ReplayGuard({
store: {
has: (id) => redis.exists(id),
add: (id, ttlMs) => redis.set(id, '1', 'PX', ttlMs),
},
});Key Rotation
migrate(token, options)
Re-encrypts a token under the current key if it was encrypted with an older key. Returns the original token unchanged if already current.
const ring = new KeyRing();
ring.add('v1', oldKey);
ring.add('v2', newKey);
ring.setCurrent('v2');
const migrated = migrate(oldToken, { keyRing: ring });
// migrated is now encrypted under 'v2'Utilities
wipeBuffer(buf)
Securely zeroes a Buffer using OpenSSL's OPENSSL_cleanse.
const key = generateKey();
wipeBuffer(key); // all bytes are now 0x00withBuffer(buf, fn)
Executes a function with a buffer, then wipes it regardless of success or failure.
const result = withBuffer(generateKey(), (key) => {
return encrypt('data', { keyRing: ring });
});
// key is wiped after useToken Format
Tokens are prefixed with nx.v2. followed by a base64url-encoded binary payload:
| Field | Size | Description |
|---|---|---|
| version | 1 byte | 0x02 |
| flags | 1 byte | 0x01 = deterministic |
| keyId length | 1 byte | 1–255 |
| keyId | variable | UTF-8 encoded |
| nonce | 12 bytes | Random or SIV-derived |
| auth tag | 16 bytes | Poly1305 tag |
| metadata length | 2 bytes | Big-endian uint16 |
| metadata | variable | Canonical JSON (sorted keys) |
| ciphertext | remaining | Encrypted payload |
Security
- ChaCha20-Poly1305 AEAD with 12-byte nonce and 16-byte authentication tag
- HMAC-SHA256 for deterministic (SIV) nonce derivation
- scrypt for password-based key derivation with configurable cost
- HKDF-SHA256 for domain-separated subkey derivation
- OPENSSL_cleanse for secure memory wiping
- AAD is cryptographically bound — tampering with keyId, metadata, or context is detected
- All crypto runs in C++ via OpenSSL — no JavaScript crypto code
Platform Support
| Platform | Architecture | Status | |---|---|---| | Windows | x64 | ✅ Prebuilt | | macOS | arm64 | 🔜 Coming soon | | macOS | x64 | 🔜 Coming soon | | Linux | x64 | 🔜 Coming soon |
Requirements
- Node.js >= 18
License
MIT
