Crypto API

Cryptographic primitives backed by mbedTLS: random byte generation, RSA key pairs, self-signed X.509 certificates, signing and verification, asymmetric encryption, HMAC digests, and constant-time byte comparison.

Overview

The global identifier Crypto is a constructor namespace. Calling one of its constructors returns a Crypto, CryptoKey, or X509Certificate handle whose methods are invoked as h.method(...). A Crypto instance from Crypto.create() is the entry point for everything that needs a working RNG: random bytes, RSA generation, signing, encryption, and HMAC. The CryptoKey and X509Certificate constructors produce empty handles for parsing PEM material received from elsewhere. For symmetric encryption, see the AES API.

Conventions

Byte payloads

All binary inputs and outputs are exchanged through the Zym Buffer native: random bytes, hashes, signatures, ciphertexts, and keys for HMAC. Use Buffer.fromString(...) to lift script strings into buffers, and b.size() / b[i] to inspect results.

Hash identifiers

Methods that name a hash take a string identifier. The lookup is case-insensitive.

IdentifierDigest sizesign / verifyhmacDigest
"sha256"32 bytessupportedsupported
"sha1"20 bytessupportedsupported
"md5"16 bytessupportedraises a runtime error

PEM strings vs. file paths

Keys and certificates serialize either to a PEM string (saveToString / loadFromString) or to a file path (save / load). The string form is convenient when piping through script values; the file form is convenient when interoperating with tools that read PEM from disk.

The publicOnly flag

CryptoKey.load, CryptoKey.save, CryptoKey.loadFromString, and CryptoKey.saveToString take an optional publicOnly boolean (default false). When true, only the public component of the key is written or expected. That is the form used to verify signatures or perform RSA encryption.

Errors

Wrong-type arguments raise a Zym runtime error of the form Crypto.method(args) .... Operations the engine handles gracefully, such as parsing a malformed PEM, return false or null instead of raising; engine-level diagnostic messages may still be printed to stderr in those cases.

Aliasing: plain assignment (k2 = k1) makes k2 refer to the same underlying key as k1. Loading a new PEM through one name is visible through the other.

Construction

Crypto.create()

Creates a Crypto instance backed by mbedTLS. This is the entry point for everything that needs a working RNG: random bytes, RSA generation, signing, encryption, and HMAC.

Returns: A Crypto instance, or null if the engine refuses to create one.

Crypto.CryptoKey()

Creates an empty CryptoKey ready for load / loadFromString. Useful for parsing a PEM received from somewhere else, such as a file, the network, or user input.

Returns: An empty CryptoKey instance.

Crypto.X509Certificate()

Creates an empty X509Certificate ready for load / loadFromString.

Returns: An empty X509Certificate instance.

Random Bytes

c.generateRandomBytes(n)

Generates n cryptographically random bytes.

Returns: A Buffer of n random bytes.

random token
var c = Crypto.create()
var token = c.generateRandomBytes(32)   // Buffer of 32 random bytes

RSA Keys & Certificates

c.generateRsa(bits)

Generates a fresh RSA key pair of size bits. Common sizes: 2048 for production, 1024 for tests.

Returns: A CryptoKey holding the new pair, or null on failure.

c.generateSelfSignedCertificate(key, issuer, notBefore, notAfter)

Issues a self-signed X.509 certificate using key.

Returns: An X509Certificate, or null on failure.

self-signed certificate
var c = Crypto.create()
var key = c.generateRsa(2048)
var cert = c.generateSelfSignedCertificate(
    key, "CN=zym example", "20230101000000", "20330101000000")
cert.save("/tmp/zym.pem")

Signing & Verification

sign and verify operate on a digest, not on the raw message. Use hmacDigest (with an empty / public key value) or a separate hashing step to produce the digest before signing.

c.sign(hashType, hash, key)

Signs the precomputed hash using the private key. The hash must be the exact length expected by the hash algorithm: 32 bytes for "sha256", 20 for "sha1", 16 for "md5".

Returns: A Buffer holding the signature, or null if signing fails.

c.verify(hashType, hash, signature, key)

Verifies that signature was produced for hash by the private counterpart of key. The public component of key is sufficient.

Returns: true if the signature checks out, false otherwise.

Asymmetric Encryption

c.encrypt(key, plaintext)

RSA-encrypts plaintext using key's public component. The ciphertext is randomized (OAEP), so two encryptions of the same plaintext differ. The plaintext must be small enough for the key size.

Returns: A Buffer holding the ciphertext, or null on failure.

c.decrypt(key, ciphertext)

Decrypts ciphertext using key's private component.

Returns: A Buffer holding the plaintext, or null on failure (e.g. wrong key, corrupted ciphertext).

HMAC

c.hmacDigest(hashType, key, msg)

Computes the HMAC of msg keyed with key, using the named hash. Output is 20 bytes for "sha1" and 32 bytes for "sha256". "md5" is not supported by the underlying engine and raises a runtime error.

Returns: A Buffer holding the digest.

Constant-Time Comparison

c.constantTimeCompare(trusted, received)

Returns true only when trusted and received are byte-for-byte equal. The implementation does not short-circuit on the first mismatch, making it safe for comparing MACs and tokens against attacker-supplied input. Buffers of different lengths compare false.

CryptoKey

A CryptoKey is returned by c.generateRsa(...) and created empty by Crypto.CryptoKey(). PEMs round-trip both through file paths and through string values: a key saved to pem and re-parsed via loadFromString(pem, ...) produces an equivalent handle, and re-saving it yields the same PEM.

k.load(path, publicOnly?)

Reads a PEM-encoded key from path.

Returns: true on success, false if the file cannot be read or parsed.

k.save(path, publicOnly?)

Writes the key to path as PEM. With publicOnly = true only the public component is written.

Returns: true on success, false otherwise.

k.saveToString(publicOnly?)

Returns the PEM representation of the key. With publicOnly = true only the public component is included.

Returns: The PEM string.

k.loadFromString(pem, publicOnly?)

Parses a PEM string.

Returns: true on success, false on invalid input.

k.isPublicOnly()

Returns true when only the public component is loaded, false when private material is present.

PEM round-trip
var c = Crypto.create()
var key = c.generateRsa(2048)
var pem = key.saveToString(false)       // private PEM
var loaded = Crypto.CryptoKey()
loaded.loadFromString(pem, false)       // true

X509Certificate

An X509Certificate is returned by c.generateSelfSignedCertificate(...) and created empty by Crypto.X509Certificate().

x.load(path)

Parses a PEM-encoded certificate (or chain) from path.

Returns: true on success, false otherwise.

x.save(path)

Writes the certificate to path as PEM.

Returns: true on success, false otherwise.

x.saveToString()

Returns the PEM representation of the certificate.

x.loadFromString(pem)

Parses a PEM string.

Returns: true on success, false otherwise.

Examples

Sign and Verify a Message

var c = Crypto.create()
var key = c.generateRsa(2048)

// Crypto.sign expects a precomputed digest, not the raw message.
var hashKey = Buffer.fromString("")
var digest = c.hmacDigest("sha256", hashKey, Buffer.fromString("hello"))

var sig = c.sign("sha256", digest, key)
print(c.verify("sha256", digest, sig, key))   // true

HMAC for Token Comparison

var c = Crypto.create()
var hmacKey = Buffer.fromString("server-secret")
var expected = c.hmacDigest("sha256", hmacKey, Buffer.fromString("payload"))
var received = c.hmacDigest("sha256", hmacKey, Buffer.fromString("payload"))
if (c.constantTimeCompare(expected, received)) {
    print("token ok")
}

RSA Encrypt / Decrypt

var c = Crypto.create()
var key = c.generateRsa(2048)
var ct = c.encrypt(key, Buffer.fromString("secret payload"))
var pt = c.decrypt(key, ct)
// pt is a Buffer holding the original bytes.