Encryption, hashing, CSRF protection, and secure operations.

Uses the Web Crypto API for:

  • Hybrid RSA-OAEP + AES-256-GCM encryption for PII at rest
  • HMAC-SHA256 for webhooks and CSRF tokens
  • Argon2-style password hashing
  • Constant-time comparison for timing-safe checks

Functions

f
aesGcmDecryptRaw(
iv: Uint8Array,
ciphertext: Uint8Array,
key: CryptoKey
): Promise<ArrayBuffer>

AES-GCM decrypt raw data, returning the decrypted ArrayBuffer

f
base64ToBase64Url(b64: string): string

Convert standard base64 to base64url (no padding). Works on both strings and Uint8Array (bytes are first encoded to base64).

f
computeHmacSha256(
data: Uint8Array,
secret: string
): Promise<ArrayBuffer>

Compute HMAC-SHA256 using Web Crypto API, returning raw ArrayBuffer

f
computeTicketTokenIndex(token: string): Promise<string>

Compute ticket token index using HMAC for blind lookups Similar to slug_index for events - allows lookup without decrypting

f
constantTimeEqual(
a: string,
b: string
): boolean

Constant-time string comparison to prevent timing attacks Always iterates over the longer string and XORs the lengths so that different-length inputs don't leak via an early return.

f
decrypt(encrypted: string): Promise<string>

Decrypt a string value encrypted with encrypt() Expects format: enc:1:$base64iv:$base64ciphertext

f
decryptAttendeePII(
encrypted: string,
privateKey: CryptoKey
): Promise<string>

Decrypt attendee PII using the private key Used in admin views after obtaining private key from session

f
decryptBytes(encrypted: Uint8Array): Promise<Uint8Array>

Decrypt binary data encrypted with encryptBytes(). Expects ENCB binary format: magic + version + IV + ciphertext.

f
deriveKEK(passwordHash: string): Promise<CryptoKey>

Derive a Key Encryption Key (KEK) from password hash and DB_ENCRYPTION_KEY Uses PBKDF2 with the password hash as input and DB_ENCRYPTION_KEY as salt

f
encrypt(plaintext: string): Promise<string>

Encrypt a string value using AES-256-GCM via Web Crypto API Returns format: enc:1:$base64iv:$base64ciphertext Note: ciphertext includes auth tag appended (Web Crypto API does this automatically)

f
encryptAttendeePII(
plaintext: string,
publicKeyJwk: string
): Promise<string>

Encrypt attendee PII using the public key from settings This can be called without authentication (for public ticket forms)

f
encryptBytes(data: Uint8Array): Promise<Uint8Array>

Encrypt binary data with AES-256-GCM using compact binary format. Output: ENCB + version byte + 12-byte IV + ciphertext (with GCM auth tag). Overhead is only 33 bytes (vs ~76% bloat in the legacy text format).

f
encryptWithKey(
plaintext: string,
key: CryptoKey
): Promise<string>

Encrypt data with a symmetric key (for wrapping private key with DATA_KEY)

f
formatPrefixed(
prefix: string,
...parts: Uint8Array[]
): string

Format IV + ciphertext as a prefixed base64 string

f
fromBase64(base64: string): Uint8Array

Convert base64 string to Uint8Array

f
generateDataKey(): Promise<CryptoKey>

Generate a random 256-bit symmetric key for data encryption

f
generateKeyPair(): Promise<{ publicKey: string; privateKey: string; }>

Generate an RSA key pair for asymmetric encryption Returns { publicKey, privateKey } as exportable JWK strings

f
generateSecureToken(): string

Generate a cryptographically secure random token Uses Web Crypto API getRandomValues

f
generateTicketToken(): string

Generate a 5-byte uppercase hex ticket token for public ticket URLs

f
getCurrentCsrfToken(): string

Get the most recently generated CSRF token (for synchronous JSX rendering)

f
getEncryptionKeyString(): string

Get the encryption key bytes from environment variable (sync validation only) Expects DB_ENCRYPTION_KEY to be a base64-encoded 256-bit (32 byte) key

f
getPrivateKeyFromSession(
sessionToken: string,
wrappedDataKey: string,
wrappedPrivateKey: string
): Promise<CryptoKey>

Derive the private key from session credentials Used to decrypt attendee PII in admin views Results are cached per session token for 10 seconds

f
getRandomBytes(length: number): Uint8Array

Generate random bytes using Web Crypto API

f
hashPassword(password: string): Promise<string>

Hash a password using PBKDF2 Returns format: pbkdf2:iterations:$base64salt:$base64hash

f
hashSessionToken(token: string): Promise<string>

Hash a session token using SHA-256 Used to store session lookups without exposing the actual token

f
hmacHash(value: string): Promise<string>

HMAC-SHA256 hash using DB_ENCRYPTION_KEY Used for blind indexes and hashing limited keyspace values Returns deterministic output for same input (unlike encrypt)

f
hmacToBase64(buf: ArrayBuffer): string

Convert ArrayBuffer to base64 string

f
hmacToHex(buf: ArrayBuffer): string

Convert ArrayBuffer to hex string

f
hybridDecrypt(
encrypted: string,
privateKey: CryptoKey
): Promise<string>

Decrypt data using hybrid encryption Expects format: hyb:1:$base64WrappedKey:$base64iv:$base64ciphertext Results are cached in a bounded LRU (ciphertext -> plaintext)

f
hybridEncrypt(
plaintext: string,
publicKey: CryptoKey
): Promise<string>

Encrypt data using hybrid encryption (RSA + AES)

f
f
f
isSignedCsrfToken(token: string): boolean

Check whether a token uses the signed format

f
parseEncryptedPayload(
encrypted: string,
prefix: string,
label: string
): { iv: Uint8Array; ciphertext: Uint8Array; }

Parse a prefixed encrypted payload into IV and ciphertext bytes. Validates the prefix and separator; throws on invalid format.

f
secureCompare(
a: string,
b: string
): boolean

Constant-time string comparison to prevent timing attacks

f
setEncryptionKeyForTest(key: string | null): void

Explicitly set or clear the encryption key for testing. Bypasses Deno.env to avoid races between parallel test workers. Automatically clears all crypto caches (encryption, HMAC, and any registered via onEncryptionKeyChange).

f
setFastPbkdf2ForTest(fast: boolean | null): void

Explicitly enable/disable fast PBKDF2 for testing without env var races

f
setRsaKeySizeForTest(size: number | null): void

Explicitly set RSA key size for testing without env var races

f
signCsrfToken(): Promise<string>

Create a signed CSRF token: s1.{timestamp}.{nonce}.{hmac}

f
symmetricDecrypt(
encrypted: string,
key: CryptoKey
): Promise<string>

Decrypt a prefixed AES-GCM payload with the given key.

f
symmetricEncrypt(
plaintext: string,
key: CryptoKey
): Promise<string>

Encrypt plaintext with an AES-GCM key, returning prefixed format: enc:1:$base64iv:$base64ciphertext

f
toBase64(bytes: Uint8Array): string

Convert Uint8Array to base64 string

f
unwrapKey(
wrapped: string,
unwrappingKey: CryptoKey
): Promise<CryptoKey>

Unwrap a symmetric key Expects format: wk:1:$base64iv:$base64wrapped

f
validateEncryptionKey(): void

Validate encryption key is present and valid Call this on startup to fail fast if key is missing

f
verifyPassword(
password: string,
storedHash: string
): Promise<boolean>

Verify a password against a hash Uses constant-time comparison to prevent timing attacks

f
verifySignedCsrfToken(
token: string,
maxAge?
): Promise<boolean>

Verify a signed CSRF token's signature and expiry

f
wrapKey(
keyToWrap: CryptoKey,
wrappingKey: CryptoKey
): Promise<string>

Wrap a symmetric key with another key using AES-GCM Returns format: wk:1:$base64iv:$base64wrapped

f
wrapKeyWithToken(
keyToWrap: CryptoKey,
sessionToken: string
): Promise<string>

Wrap a key using a session token (derives a wrapping key from the token)

Variables

v
CSRF_INVALID_FORM_MESSAGE: "Invalid or expired form. Please try again."

Default message for invalid/expired CSRF form submissions