Crypto — version history
A non-visual object for hashes, HMAC, password-based or key-based encryption, RSA, ECDSA and Ed25519 signatures, JWT, salted password hashes, two-factor TOTP and DPAPI — with nothing to install. Introduced in 3.0.
4.0 — October 2026 #
New #
- Keys given as bytes: constants
KEYFORMAT_TEXT,KEYFORMAT_HEX,KEYFORMAT_BASE64and an overload ofof_hmac,of_jwt_signandof_jwt_verifythat takes them (a secret supplied encoded, AWS SigV4 signing). of_hmac_verify(algo, key, text, expected {, key_format})compares a received HMAC (a webhook signature, hex or base64) in constant time.of_jwt_header(token)reads a token's header, and an overload ofof_jwt_signadds members to the header (kid).of_totp_verify(secret, code, ref al_step)returns the accepted time step, so that a code already used can be refused.il_password_hash_rounds: the rounds ofof_password_hash, 600,000 by default (the current OWASP figure); values already stored carry their own rounds and still verify.- An optional entropy for
of_protectandof_unprotect(DPAPI), and the documentation says who can read a protected value back. of_hashis computed natively for every algorithm, without opening a hidden page.- The documentation describes the
of_encryptcontainer byte by byte (with a Python example to read it) and the RSA-OAEP settings to use with openssl, Java or .NET.
Fixed #
of_jwt_signwithJWT_EDDSAwrites"alg":"EdDSA", the registered name that other libraries (jose, PyJWT, jjwt) expect; a token signed by 3.0 still verifies.of_generate_keypairwith an unknown kind or size ("ec_p256",1024) returns-5with empty PEMs andis_last_errornaming the acceptedKEY_*(it silently produced an RSA 2048 pair).of_hash_fileaccepts the algorithm namesof_hashaccepts (SHA-256=HASH_SHA256, case and dashes ignored); an unknown algorithm is reported as such, distinct from an unreadable file.of_base64_encode_fileon a file too large for the process (over 64 MB in 32-bit, 512 MB in 64-bit) says so (-7) instead of "unreadable file"; a missing file is reported as unreadable.is_last_erroris emptied by a successful native call (of_crc32,of_hashin MD5,of_hash_file…); it kept the previous error.- An empty HMAC key, a non-numeric
exp/nbfin a JWT, anof_password_verifyof a value claiming more than 10,000,000 rounds, an unreadable instant for TOTP and an unknown character set forof_random_passwordare refused at once, with the reason inis_last_error. of_base64_decode_to_file("", path)writes an empty file and returns0(it returned-5).- The CRC32 of a file that fails to read is an error, no longer a wrong value.
- Engine messages ("Invalid character", "Invalid keyData") are replaced by messages that say what was wrong.
- The JWT example of the documentation uses a neutral subject (
"sub":"jdoe").
Behavior changes #
of_jwt_signandof_jwt_verify: the algorithm is required and compared exactly (pass it as a constant,n_pbt_crypto.JWT_RS256); a PEM key is never accepted as an HMAC secret (of_hmacincluded); a header withcritis refused.audandissremain for the caller to check.of_jwt_signrefuses anexpin the claims together with a durational_expires_seconds(the duration silently replaced it).of_encryptandof_decryptrefuse an empty password;of_password_hash("")is still allowed.il_pbkdf2_roundsoutside 1,000 to 10,000,000 makes the call fail (empty string for a text,-5for a file, bounds inis_last_error) instead of silently falling back to 100,000.of_base64_decode,of_base64url_decode,of_hex_decode,of_decrypt… on bytes that are not UTF-8 text (a PDF, an image) return an empty string andis_last_errorpointing toof_base64_decode_to_fileorof_decrypt_file, instead of replacement characters.of_signwith an EC key returns a DER signature, the form openssl and Java verify;of_verifyreads both DER and the former 64-byte form, and a JWT ES256 stays in its standard form.of_totp_codeandof_totp_verifyrefuse a secret with a character outside base32 (spaces and=tolerated) — a0typed for anOgave another secret.of_encrypt_fileaccepts files up to 64 GB, the limit of an AES-GCM container; beyond,-7.- Relative paths are read from the current folder, then the folder where the application started, then next to the EXE, and written to the folder where the application started — the same in the IDE and compiled.
of_openreturns-2when the hidden page cannot start, with the reason inis_last_error.- See the 3 → 4 migration guide, sections 11.3, 11.5 and 11.8.
3.0 — September 2026 #
New #
- First release, with nothing to install: hashes SHA-1/256/384/512 and MD5 (
of_hash, andof_hash_filefor a file of any size), HMAC, CRC32, UUIDs and random values, Base64 (text, file, base64url) and hex. - Password-based encryption (
of_encrypt/of_decrypt: PBKDF2 + AES-256-GCM in a single base64 string,il_pbkdf2_rounds), for text and for files (of_encrypt_file/of_decrypt_file, same container either way). - Explicit keys (
of_generate_key,of_encrypt_with_key/of_decrypt_with_key), RSA-OAEP (of_rsa_encrypt/of_rsa_decrypt) and key pairs in PEM (of_generate_keypair:KEY_RSA_2048/3072/4096,KEY_EC_P256,KEY_ED25519) withof_sign/of_verify. - JWT (
of_jwt_sign,of_jwt_verify,of_jwt_claims: HS256, RS256, ES256, EdDSA, withiat/expand one minute of tolerance). - Salted password hashes (
of_password_hash/of_password_verify, constant-time comparisonof_equals_constant_time) andof_random_password(CHARSET_*). - Two-factor TOTP (RFC 6238:
of_totp_secret,of_totp_code,of_totp_verify,of_totp_urifor an authenticator app's QR code). - DPAPI (
of_protect/of_unprotect, per user or per machine): what goes into an INI file. ipo_ownernames the host class;is_last_errorsays why a call failed.