Next: (bewaking cli), Up: API
Return #t when the loaded libgcrypt provides Argon2 (1.11 or newer).
Decode the hexadecimal string value into a string. Upper and lower case digits are both accepted. Raises an error when the input has an odd number of digits or contains a non-hexadecimal character.
Return the lowercase hexadecimal encoding of the string value, one byte per two digits.
Safely reopen a string produced by ‘encrypt-object‘. Wraps ‘unsafe-decrypt-object‘, catching errors and returning #f instead of raising.
See ‘unsafe-decrypt-object‘ for the keyword arguments.
Decrypt a payload previously produced by ‘encrypt-payload‘, by
openssl enc -aes256 -pbkdf2 -a -A, or by any other implementation
of that format. Which key derivation to use is read from the payload
itself, so no options are needed to open data sealed with either KDF.
Raises an error when the input is not a sealed payload or when its
padding is invalid, which is how a wrong key is reported.
#:args payload: The Base64-encoded, AES256-encrypted string to decrypt. k: The decryption key (a string) that was used to encrypt the payload.
#:returns The original, decrypted data as a string.
#:example (decrypt-payload "U2FsdGVkX1+..." "mysecretkey")
Safely attempts to decrypt a given token. This function acts as a wrapper around ‘unsafe-decrypt-token‘, catching any errors that occur during the decryption process.
Args: token: The token string to be decrypted. encryption-key: The key used for encryption. signing-key: The key whose derived AES-256-GCM layer authenticates the envelope. keyring: (Optional) alist of key-id -> key entry for key rotation. enforce-expiry?: (Optional) when #t (the default) expired envelopes are rejected. max-age-seconds: (Optional) reject envelopes sealed longer ago than this.
Returns: The decrypted token payload if successful, otherwise ‘#f‘ (false) and prints an error message to the console.
Safely seal an arbitrary Scheme datum. Wraps ‘unsafe-encrypt-object‘, catching errors and returning #f instead of raising.
See ‘unsafe-encrypt-object‘ for the keyword arguments.
Encrypt payload with AES-256-CBC under the passphrase k,
deriving the AES key and IV with kdf (see kdf-spec in
(bewaking crypto)). The result records which derivation was
used, so it can always be opened again without being told.
The output uses exactly the layout of openssl enc -aes256 -pbkdf2
-a -A -k KEY, so data produced by either implementation can be read by
the other:
- an 8-byte random salt, stored in an OpenSSL Salted__ header; -
a 32-byte key and a 16-byte IV derived from the passphrase and that salt
with PBKDF2-HMAC-SHA256 over 10000 iterations; - AES-256-CBC with PKCS#7
padding, Base64-encoded on a single line.
The work happens inside this process; nothing is passed to a shell.
#:args payload: The string to encrypt. k: The encryption key (a string) to use.
#:returns A Base64 string such as "U2FsdGVkX1+...".
#:key kdf: the key-derivation specification; 'pbkdf2 (the
default) keeps the OpenSSL-compatible layout, 'argon2id selects
Argon2id with its default parameters.
#:example (encrypt-payload "mysecretdata" "mysecretkey") (encrypt-payload "mysecretdata" "mysecretkey" #:kdf ’argon2id)
Return #t when sealed is an envelope carrying an expires-at
in the past relative to now, and #f otherwise — including
envelopes with no expiry and non-envelope values.
This is an unauthenticated audit helper that reads the header only. Use ‘open-envelope‘, which verifies the cipher tag, to enforce expiry during decryption.
Return an alist of envelope metadata without needing any key:
version, key-id, created-at and expires-at.
expires-at is #f when the data never expires. Returns #f when
sealed is not a well-formed envelope.
Reading this needs no key, which is what makes it useful for auditing; the metadata is authenticated by the envelope’s cipher tag, so ‘open-envelope‘ will not act on it before that protection has been accepted. Treat this function’s result as a *claim* to log, never as a decision: only ‘open-envelope‘ can say whether the envelope is genuine.
Safely attempts to generate a new token. This function wraps ‘unsafe-generate-token‘ to gracefully handle any potential errors during token creation.
Args: encryption-key: The key to be used for encrypting the token’s payload. signing-key: The key to be used for signing the token. keyring: (Optional) alist of key-id -> key entry for key rotation. key-id: (Optional) label embedded in the envelope (default "default"). get-owner-details: A thunk that returns a list of details about the token’s owner. token-validity-seconds: (Optional) The payload validity duration in seconds. ttl-seconds: (Optional) Envelope lifetime in seconds; seals an expiry into the token. expires-at: (Optional) Explicit Unix timestamp for the envelope expiry. kdf: (Optional) Key-derivation specification; ’pbkdf2 (the default) or ’argon2id, or an alist of parameters. Recorded in the token itself.
Returns: A newly generated token string if successful, otherwise ‘#f‘ (false) and prints an error message to the console.
Check if needed permissions are a subset of the present (existing) permissions.
Return #t when the token’s token-expires-at timestamp is in the
past, and #f while the token is still valid. Raises an error when the
payload is missing a well-formed token-expires-at field.
#:key token: A decrypted token payload (association list).
#:example (is-token-expired? #:token (decrypt-token #:token t #:encryption-key k1 #:signing-key k2))
Open a sealed envelope and return its plaintext string.
The envelope is opened with AES-256-GCM, whose tag also covers the rotation metadata (key id, created-at, expires-at): no plaintext is returned unless the tag verifies, so the metadata cannot be read as authentic before the cipher has accepted it, and tampering is refused rather than decrypted and then distrusted.
A refusal is deliberately unspecific. An unknown key id, a retired key, a key that is not active yet, a wrong key and altered data all raise the same error, and the attempt is made with a placeholder key rather than abandoned early, so the work done is comparable too. See ‘envelope-refusal-message’. Expiry is the one distinguishable case, because it is only reached once the tag has proved the envelope genuine, and an operator needs to know that live data merely went stale.
#:key enforce-expiry?: when #t (the default) an envelope whose
expires-at is in the past is rejected. #:key now: current Unix
timestamp, for deterministic checks. #:key max-age-seconds: optional;
reject envelopes created longer ago than this. #:key keyring: optional
alist of key id to key entry; the entry’s not-before /
not-after window is enforced.
Anything that is not a well-formed BWK3 envelope — an envelope of an earlier format version, a pre-versioning bare Base16 token, or arbitrary text — is refused.
Seal a plaintext string into a versioned, authenticated envelope.
The output is BWK3.<key-id>.<created-at>.<expires-at>.<body>,
where the body is a sealed payload whose authenticity comes from
AES-256-GCM keyed by the signing key. An encryption key and a signing
key are all that is required: there is no MAC key, because the cipher
tag is the integrity check.
#:key created-at: Unix timestamp to record (default: now). #:key
expires-at: Unix timestamp after which the data must not be opened.
#:key ttl-seconds: convenience; sets expires-at to created-at +
ttl-seconds. Give at most one of expires-at / ttl-seconds. #:key kdf:
how the AES keys are derived from the two keys, either a symbol such as
'pbkdf2 (the default) or 'argon2id, or an alist of
parameters as accepted by kdf-spec in (bewaking crypto).
The choice is recorded in the sealed body, so opening needs no matching
option.
#:example (seal-envelope #:plaintext some-plaintext #:encryption-key k1 #:signing-key k2 #:ttl-seconds (* 24 3600)) (seal-envelope #:plaintext some-plaintext #:encryption-key k1 #:signing-key k2 #:kdf ’argon2id)
Return #t when sealed is a bewaking envelope of any readable format version.
Garbles a string by splitting it into two halves, reversing each half, and then concatenating the reversed second half with the reversed first half. If the string has an odd length, the first half will be shorter by one character.
#:args s: The input string to garble.
#:return A new string that is the garbled version of the input string.
Example: (string-garble "abcdefg") ; Returns "gfedcba" (string-garble "hello world") ; Returns "dlrow olleh"
Ungarbeles a string that was previously garbled by the ‘string-garble‘ function. It reconstructs the original string by reversing the process of ‘string-garble‘.
Args: s: The garbled string to ungarble.
Returns: The original string before it was garbled.
Example: (string-ungarble "gfedcba") ; Returns "abcdefg" (string-ungarble "dlrow olleh") ; Returns "hello world"
Verify the validity of a token payload, by doing checks on every field, and returning either #t or #f representing validity status.
Return the number of seconds left before the token expires.
The value is positive while the token is still valid, zero at the exact
expiry instant, and negative once the token has expired. Raises an
error when token-expires-at is missing or is not a number.
#:key token: A decrypted token payload (association list).
Return #t when the decrypted token payload is structurally valid (‘token-payload-valid?‘) and has not expired yet (‘is-token-expired?‘).
#:key token: A decrypted token payload (association list).
Reopen a string produced by ‘unsafe-encrypt-object‘, returning the original Scheme datum. The envelope’s authentication tag is checked as it is opened, so tampering is reported explicitly and no plaintext exists before that check. Raises an error if the object cannot be decrypted.
#:key object: The sealed string to decrypt. #:key encryption-key: The inner AES-256 key (string). #:key signing-key: The key whose derived AES-256-GCM layer authenticates the envelope. #:key keyring: Optional alist of key-id -> key entry. When supplied, keys are selected using the sealed data’s own key id. #:key enforce-expiry?: When #t (the default), an object whose envelope expiry has passed is rejected. #:key max-age-seconds: Optional rotation policy; reject objects sealed longer ago than this. #:key now: Current Unix timestamp, for deterministic checks. #:returns: The original Scheme datum.
Decodes and validates a secure token created by ‘generate-token‘.
Tokens carry a version tag, a key id and an authentication tag inside the sealed body. The tag is checked *while* the token is opened, so tampering is reported explicitly and no plaintext exists before authenticity is established. Anything that is not a current-version envelope is refused.
All cryptography is performed in this process (see the module ‘(bewaking crypto)‘); no external command-line tools are involved.
#:key token: The token string to decrypt. #:key encryption-key: The key used for the inner AES-256 encryption. #:key signing-key: The key whose derived AES-256-GCM layer authenticates the envelope. #:key keyring: Optional alist of key-id -> key entry. When supplied, keys are selected using the token’s own key id. #:key enforce-expiry?: When #t (the default), an envelope whose expiry has passed is rejected. #:key max-age-seconds: Optional rotation policy; reject envelopes sealed longer ago than this. #:key now: Current Unix timestamp, for deterministic checks.
#:returns: A parsed association list representing the token’s payload if the token is valid and successfully decoded. Otherwise, it raises an error.
Seal an arbitrary Scheme datum (not just a token payload) into a portable, versioned, authenticated string. Any value that ‘write‘ can serialize is supported: alists, lists, vectors, numbers, strings, booleans, etc.
#:key payload: The Scheme datum to protect. #:key encryption-key: The inner AES-256 key (string). Required unless a keyring is supplied. #:key signing-key: The key (string) whose derived AES-256-GCM layer authenticates the envelope. #:key keyring: Optional alist of key-id -> key entry for key rotation. #:key key-id: Key identifier embedded in the envelope (default "default"). #:key ttl-seconds: Optional envelope lifetime in seconds. The envelope then records an expiry and refuses to reopen afterwards. #:key expires-at: Optional Unix timestamp for the envelope expiry; mutually exclusive with ttl-seconds. #:key kdf: How the AES keys are derived from the two keys: ’pbkdf2 (the default) or ’argon2id, or an alist of parameters as accepted by ‘kdf-spec‘. The choice travels inside the sealed body. #:returns: A versioned, authenticated envelope string.
#:example (unsafe-encrypt-object #:payload ’((user . "joe") (role . admin)) #:encryption-key "..." #:signing-key "...")
Generates a secure, versioned, authenticated token.
#:key encryption-key: The secret key (string) used for AES-256
encryption. #:key signing-key: The signing key (string) used to derive
the key of the AES-256-GCM layer that authenticates the token. #:key
keyring: Optional alist of key-id -> key entry used to select keys
instead of the explicit keys above. #:key key-id: Key identifier
embedded in the envelope (default "default"). Enables key rotation.
#:key get-owner-details: A thunk that returns an the owner’s details.
#:key token-validity-seconds: The token’s payload lifetime in seconds.
#:key ttl-seconds: Optional envelope lifetime in seconds. When given,
the sealed envelope records an expiry and ‘decrypt-token‘ refuses to
open it afterwards (default: no expiry). #:key expires-at: Optional
Unix timestamp for the envelope expiry; mutually exclusive with
ttl-seconds. #:key kdf: How the AES keys are derived from the two keys.
Use 'argon2id for a memory-hard derivation; the default is
PBKDF2, which is what older tokens use. #:returns: A versioned,
authenticated token string.
Next: (bewaking cli), Up: API