Microtoll Engine pre-1.0 · the locks, pre-built

@microtoll/crypto-core

The primitives and wire formats of the Microtoll Engine. Web Crypto (SubtleCrypto) only; no runtime dependencies; runs in current browsers and Node ≥ 24.

Status: pre-release; not published. Every format is pinned by frozen fixtures (test/fixtures/frozen-v1.json) that each later version must open.

Five-minute quickstart

import { createCryptoCore } from '@microtoll/crypto-core';

// One instance per app. The namespace prefixes every derivation label, so no
// two apps ever share a key derivation by accident. It is required.
const cc = createCryptoCore({ namespace: 'myapp' });

// A 32-byte root secret, and keys derived from it under labelled purposes.
const root = cc.generateSymmetricKey();
const routingSeed = await cc.deriveBits(root, 'routing');          // HKDF, label "myapp/routing/v1"
const signingKey  = await cc.importEd25519PrivateKeyFromSeed(routingSeed);
const ownDataKey  = await cc.deriveAesKey(root, 'symm');           // AES-256-GCM, non-extractable

// Symmetric sealing: [0x01][12-byte IV][ciphertext ‖ tag], optional bound context.
const sealed = await cc.sealSymmetric(ownDataKey, new TextEncoder().encode('hello'));
const opened = await cc.openSymmetric(ownDataKey, sealed);

// Sealing to another person: they hold a P-256 key pair stored as a JWK.
const alice = await cc.generateSealingKeyPair();                    // { privateKey, publicKeyRaw, jwk }
const forAlice = await cc.sealToRecipient(alice.publicKeyRaw, opened);
const back = await cc.openWithPrivateKey(alice, forAlice);          // needs the pair, not a bare key

// A recovery code a person can write down: 128 bits, Crockford base32, checksum.
const { secretBytes, displayString } = await cc.generateRecoveryCode(); // "ABCD-EFGH-…-XYZ"
const unwrapKey = await cc.deriveAesKeyFromSecret(secretBytes, cc.randomBytes(16)); // PBKDF2, 310,000 iterations

Stateless primitives are also exported directly (hkdfDeriveBits, sealSymmetric, verifyBytes, the encoders); everything derived under a label lives on the instance.

What is here

Tests

npm test at the repository root. Published vectors run through this package's own API: RFC 5869 (test cases 1 and 3), RFC 8032 (tests 1, 2, 3, SHA(abc)), RFC 5903 §8.1, RFC 7914 §11, NIST CAVP AES-256-GCM, and X-Wing draft-10 Appendix C plus the working-group MLKEM768-X25519 vector. Then property and tamper tests, and the frozen fixtures.

Threat model

See THREATMODEL.md §3 at the repository root. In one line: this package protects sealed bytes against the server, the network and strangers; it protects nothing against a compromised device or page, and its classical seal is not quantum-safe.

Notes on the API