AES-GCM (SP 800-38D) — authenticated AEAD + GMAC, IV-reuse catastrophic.
Module gcm | Source packages/front/fw/src/crypto/mode/gcm.js | Deps bitArray, aes | Worker-safe yes
Recommended AEAD mode for TLS / file encryption. IV reuse under the same key is catastrophic: loss of the GHASH authentication key + leak of the XOR of plaintexts.
Resolve
const gcm = runtime.resolve('gcm');
// Returns: { encrypt, decrypt, gmac, gmacVerify, nonceTracker, _internal }
API
| Method | Signature | Returns |
|---|---|---|
encrypt(prf, ptBa, ivBa, aadBa, tagLenBits) |
— | {ct, tag} | false |
decrypt(prf, ctBa, ivBa, aadBa, tagBa, tagLenBits) |
— | Plaintext or false (invalid tag) |
gmac(prf, aadBa, ivBa, tagLenBits) |
— | Tag (no PT) |
gmacVerify(prf, aadBa, ivBa, tagBa, tagLenBits) |
— | boolean |
nonceTracker(prf) |
({encrypt}) => {encrypt, decrypt, seenNonces} |
Anti-IV-reuse wrapper (in-process) |
_internal.{ghash, gctr} |
— | Debug primitives |
tagLenBits
32 to 128 (in steps of 32). 128 recommended for general use. Shorter tag = linear security loss.
Examples
const { aes, gcm, random, bitArray } = fw.runtime.resolveAll(['aes', 'gcm', 'random', 'bitArray']);
const key = Array.from(bitArray.ui8_to_ba(random.bytes(32)));
const iv = bitArray.ui8_to_ba(random.bytes(12)); // 96-bit recommended
const aad = bitArray.ui8_to_ba(new TextEncoder().encode('header'));
const pt = bitArray.ui8_to_ba(new TextEncoder().encode('Hello, GCM!'));
const cipher = aes.fn(key);
const out = gcm.encrypt(cipher, pt, iv, aad, 128); // {ct, tag}
const dec = gcm.decrypt(cipher, out.ct, iv, aad, out.tag, 128);
// dec === pt (or false on invalid tag)
Nonce tracker (anti-IV-reuse)
const tracker = gcm.nonceTracker(cipher);
tracker.encrypt(pt1, iv1, aad, 128); // OK
tracker.encrypt(pt2, iv1, aad, 128); // false + console.error('CORRUPT: nonce reuse')
GMAC only (no plaintext)
const tag = gcm.gmac(cipher, aad, iv, 128);
const ok = gcm.gmacVerify(cipher, aad, iv, tag, 128);
Worker Usage
const worker = fw.createWorker(
function ({ libs, args }) {
const cipher = libs.aes.fn(args[0]);
self.postMessage(libs.gcm.encrypt(cipher, args[1], args[2], args[3], 128));
},
{ dependencies: ['aes', 'gcm'], args: [key, pt, iv, aad] }
);
Notes
- Unique IV per (key, message): use
random.bytes(12)or a monotonic counter.nonceTrackerdetects reuse in-process. - Tag mismatch →
false+console.error('CORRUPT: gcm: authentication tag mismatch'). Plaintext is never released on an invalid tag. - AES-128/192/256:
ciphercan be scheduled on any of the 3 key sizes (key-size-agnostic viaaes.fn).
See also
- aes, chacha20poly1305 — alternative AEAD
- cmac — MAC without AEAD
- Conformance gcm.acvp.md