Docs
/
applied cryptography adapter pack

crypto

Crypto adapter — applied cryptography over node:crypto, plus the parameterised LEUC1 container lane from @leumas/crypt. Loaded by the adapter registry (POST /api/adapters/crypto/<fn>), and doubles as...

crypto — applied cryptography adapter pack

Real, modern cryptography for the Leumas ecosystem. The original half is built entirely on Node's native node:crypto with no npm dependency at all; the container lane added for encrypt.leumas.tech is the adapter face of @leumas/crypt, the shared engine that also runs in a browser.

This is the "do actual crypto" pack: encrypt sensitive data, generate keypairs, sign & verify payloads, mint and validate JSON Web Tokens, derive keys from passwords, and generate secure secrets — plus encrypt any file with any suite, any KDF and every cost knob exposed.

[critical] encrypt and decrypt are frozen. Seeded chatbot functioncalls depend on their exact argument and result shapes, and the failure mode of changing them is silent — an agent keeps calling the old shape and gets a result that no longer means what it did. The parameterised lane is encryptData/decryptData, sitting beside them.

Tools

ToolWhat it does
encryptAES-256-GCM authenticated encryption of text with a password. Derives a 32-byte key via scrypt from the password + a random salt, encrypts under a random IV. Returns { salt, iv, authTag, ciphertext } (all base64).
decryptReverses encrypt. Needs the password + the salt/iv/authTag/ciphertext. Throws on wrong password or tampered data (GCM is authenticated).
generateKeyPairGenerate an rsa (default 2048-bit), ec (default P-256), ed25519, or ed448 keypair. Returns PEM publicKey + privateKey.
publicKeyFromPrivateDerive/export the PEM public key from a private key.
signDigital signature of data with a PEM private key (RSA/EC use a hash, default sha256; Ed keys sign raw).
verifyVerify a signature against data with the matching public key → { valid }.
jwtSignSign a JWT. HS use a shared secret; RS/PS/ES use a PEM privateKey. Auto-adds iat/exp (expiresIn accepts 3600, "15m", "2h", "7d").
jwtVerifyVerify a JWT signature + exp/nbf. Throws status:401 on invalid/expired. Supports an algorithms allow-list.
jwtDecodeDecode header + payload without verifying (inspection only — never trust for auth).
hmacKeyed MAC of arbitrary data (raw or encoded key, choose hash + output encoding).
hashOne-shot cryptographic hash of arbitrary data/bytes (sha256 default).
pbkdf2PBKDF2 key derivation (iterations + salt). Auto-generates + returns a salt if none given.
scryptscrypt memory-hard key derivation (N/r/p). Auto-generates + returns a salt if none given.
randomBytesCSPRNG bytes in the chosen encoding (base64 default).
randomTokenURL-safe random token (base64url) for API keys, nonces, reset tokens.
uuidAn RFC-4122 v4 UUID from the CSPRNG.
timingSafeEqualConstant-time comparison of two secrets (resists timing attacks).
listHashAlgorithmsHash algorithms available on this Node build.

The container lane — @leumas/crypt

ToolWhat it does
encryptDataEncrypt bytes into a LEUC1 container with a chosen suite (AES-128/192/256 GCM · CBC · CTR, ChaCha20-Poly1305) and KDF (scrypt · PBKDF2 · Argon2id), every cost knob exposed. The parameters are recorded IN the file, so it opens with the passphrase alone.
decryptDataOpen a LEUC1 container — or a legacy LEUENC file, which is still read and reported as format: 'leuenc'.
inspectContainerRead what a container says about itself with no passphrase: suite, KDF and costs, whether it is authenticated, whether it needs AAD, and its overhead.
listCipherSuitesThe whole catalogue with each knob's type and range, and — given a lane — which entries that lane can actually run, keeping the ones it cannot with a note saying why.
sealEnvelopeHybrid public-key encryption: a random content key wrapped with RSA-OAEP or ECDH-P256. No passphrase anywhere.
openEnvelopeOpen an envelope with the matching private key.
classicalCaesar, ROT13, Atbash, Vigenère. Not encryption, and every result says so.
breakCaesarBreaks Caesar with no key, and returns all 26 shifts scored — the size of the keyspace is the lesson.

Usage

Every tool takes ONE args object (an HTTP POST body maps 1:1). Through the registry: registry.run('crypto', '<tool>', args).

import crypto from './index.js';
const { adapters } = crypto;

// Encrypt then decrypt
const box = adapters.encrypt({ text: 'top secret', password: 'hunter2' });
const { text } = adapters.decrypt({ password: 'hunter2', ...box });
// text === 'top secret'

// Keypair + sign + verify
const { publicKey, privateKey } = adapters.generateKeyPair({ type: 'ec' });
const { signature } = adapters.sign({ data: 'invoice#42', privateKey });
const { valid } = adapters.verify({ data: 'invoice#42', signature, publicKey }); // valid === true

// JWT (HS256)
const { token } = adapters.jwtSign({ payload: { sub: 'u_1', role: 'admin' }, secret: 's3cr3t', expiresIn: '2h' });
const { payload } = adapters.jwtVerify({ token, secret: 's3cr3t' });

// Key derivation + secure token
const kdf = adapters.pbkdf2({ password: 'hunter2' }); // returns salt + key
const { token: apiKey } = adapters.randomToken({ length: 32 });

// The container lane — a file, a chosen suite, a chosen KDF cost
const { container } = await adapters.encryptData({
  data: pngBytesBase64, dataEncoding: 'base64',
  password: 'hunter2',
  cipher: 'chacha20-poly1305',
  kdf: 'argon2id', kdfParams: { memoryCost: 131072, timeCost: 4 },
});
adapters.inspectContainer({ container });   // suite + costs, no passphrase needed
const { data } = await adapters.decryptData({ container, password: 'hunter2', encoding: 'base64' });

DRY boundary — what does NOT live here

This pack is deliberately scoped so it does not overlap its neighbours:

  • a-text owns string→string primitives on text: MD5Hash, SHA256Hash, SHA512Hash,

a hex-digest HMAC, Base64Encode/Base64Decode, and classical ciphers (CaesarCipher, Rot13, VigenereEncode/Decode). Those are deterministic display/obfuscation helpers with no key management. Reach for a-text when you just want the hex hash of a string.

  • a-file-actions owns AES encryption of files on disk, at fixed parameters, writing the

older LEUENC format. This pack reads those files and never writes them: LEUENC records no algorithm and no KDF cost, which is fine for a fixed backup job and fatal once the parameters are the user's to choose.

  • @leumas/crypt owns the LEUC1 format, the suite/KDF catalogue and the browser lane. This

adapter imports it and does not fork it — the same engine runs in the tab and on the server, and a cross-lane test compares their bytes.

  • crypto (this pack) owns real applied cryptography on data and keys: password-based

AES-256-GCM of arbitrary text (authenticated, salted, random-IV), RSA/EC/Ed keypairs, digital signatures, JWTs, KDFs (PBKDF2/scrypt), and CSPRNG token/byte/compare primitives.

The small overlaps are intentional and distinct: crypto.hmac/crypto.hash are the general data/bytes primitives (any key encoding, any output encoding) used to build signing flows, whereas a-text's HMAC/*Hash are hex-only convenience wrappers for text. Use a-text for quick text digests; use this pack when you're actually securing data, keys, or tokens.

Notes

  • Binary outputs default to base64 (base64url for randomToken and JWT segments). All results are

plain JSON-serializable objects.

  • encrypt uses scrypt (N=16384, r=8, p=1) → AES-256-GCM; the returned salt/iv/authTag

make each ciphertext self-describing for decrypt.

  • JWT algorithms: HS256/384/512, RS256/384/512, PS256/384/512, ES256/384/512. ES* use

IEEE-P1363 (r‖s) signatures per the JWT spec.

  • Binary crosses this boundary as base64, because an adapter result is JSON-serialisable by

contract. Pass encoding: 'base64' to decryptData for anything that is not text — utf8 on binary yields replacement characters and loses the file, silently. The HTTP lane at /api/encrypt streams real bytes instead and has no such cost.

  • argon2id is server-only and its native binding (@node-rs/argon2) is an optional

dependency: a missing binary degrades that one KDF at call time, with a sentence naming it, rather than taking the module down at import.

  • integrity: false on a non-AEAD suite is a teaching mode, not a configuration. It produces a

file that decrypts silently wrong when modified, which is the only demonstration on the site of why authentication is not optional.

Source shared/engines/adapters/domain/crypto/README.md (no-git)markdownjson
Generated from the Leumas repository. Every page cites the file it came from.leumas.techllms.txt