std/secure

Standard Library documentation

Secure runtime services and capability inspection.

Module

Name
std/secure
Area
Standard Library
Source
modules/std/secure.zzm

NAME

std/secure - Secure runtime services and capability inspection.

SYNOPSIS

  from std/secure import
      Secure,
      SecureRandom,
      PasswordHash,
      KeyDerivation,
      Cipher,
      KeyAgreement,
      SigningKey,
      Certificate,
      PrivateKey,
      PublicKey,
      SealedBox,
      TlsIdentity;

  let caps := Secure.capabilities();
  Secure.require( "random", "bytes" );

IMPLEMENTATION SUPPORT

This module is supported by all implementations of ZuzuScript, though the ciphers, hashes, and other algorithms supported by the module vary from platform to platform. Many synchronous operations are unsupported by zuzu-js on the browser; using async variants is recommended if you need to support that platform.

DESCRIPTION

This runtime-supported module is the public namespace for secure random generation, key derivation, authenticated encryption, password hashing, signing, key agreement, certificate handling, and TLS identity objects.

It implements secure random bytes, URL-safe random tokens, unbiased random integers, host identification, capability queries, HKDF-SHA256 key derivation, AES-256-GCM authenticated encryption, password hashing, Ed25519 and ECDSA signing, and X25519 key agreement.

EXPORTS

Classes

  • Secure

    Module-level inspection and policy namespace. It currently exposes capabilities(), has(area, name), and require(area, name).

  • SecureRandom

    Namespace for CSPRNG byte, token, and integer helpers.

  • PasswordHash

    Namespace for password hashing, verification, rehash detection, and passphrase key derivation.

  • KeyDerivation

    Namespace for future high-entropy key derivation helpers such as HKDF-SHA256.

  • Cipher

    Namespace for authenticated symmetric encryption helpers. It currently supports AES-256-GCM envelopes.

  • KeyAgreement

    Namespace and object type for X25519 key agreement.

  • SigningKey and PublicKey

    Namespaces and object types for Ed25519 and ECDSA asymmetric signing, verification, and key material handling.

  • PrivateKey

    Placeholder object type for future non-signing private-key handling.

  • Certificate and TlsIdentity

    Certificate is the X.509 parsing and inspection object type. TlsIdentity stores client certificate identity material for future TLS integrations.

  • SealedBox

    Namespace for future public-key sealed-box encryption recipes.

Capability Reporting

Secure.capabilities() returns a Dict with stable keys:

  {
      host: "perl",
      random: true,
      password_hash: [ "argon2id", "crypt", "pbkdf2-sha256", "scrypt" ],
      kdf: [ "hkdf-sha256" ],
      cipher: [
          "aes-128-gcm",
          "aes-192-gcm",
          "aes-256-gcm",
          "chacha20-poly1305",
      ],
      key_agreement: [ "x25519" ],
      signing: [
          "ed25519",
          "ecdsa-p256-sha256",
          "ecdsa-p384-sha384",
          "ecdsa-p521-sha512",
      ],
      certificate: [
          "fingerprint-sha256",
          "fingerprint-sha384",
          "fingerprint-sha512",
          "parse-x509",
          "parse-x509-der",
          "public-key",
          "verify-chain",
      ],
      tls_identity: [ "pem", "pkcs12" ],
      async_required: {
          cipher: false,
          kdf: false,
          password_hash: false,
          signing: false,
          key_agreement: false,
      },
  }

host identifies the runtime host that produced the report. The current host names are perl, rust, node, and browser.

Later phases will turn on additional capabilities only after the corresponding runtime has a real implementation.

Browser hosts do not advertise ed25519 in signing and reject it in this phase even when a specific Web Crypto implementation exposes it. Browser hosts advertise ECDSA signing and report async_required.signing as true. Browser hosts also report async_required.key_agreement as true when X25519 is available through Web Crypto.

Capability Vocabulary

Capability areas and names are stable strings. random currently supports bytes, token, and int. password_hash always supports pbkdf2-sha256; hosts may additionally report argon2id, scrypt, or legacy crypt. kdf currently supports hkdf-sha256. cipher always supports aes-256-gcm; hosts may add aes-128-gcm, aes-192-gcm, or chacha20-poly1305. key_agreement currently supports x25519. signing may include ed25519, ecdsa-p256-sha256, ecdsa-p384-sha384, and ecdsa-p521-sha512. The certificate area reports X.509 parsing, fingerprinting, public-key extraction, and chain verification support. tls_identity reports pem and, where available, pkcs12.

  • Secure.has(String area, String name)

    Returns true if the current host supports name within area. Unknown, missing, or null values return false.

  • Secure.require(String area, String name)

    Returns true if the current host supports name within area. Otherwise it throws a runtime exception with the unsupported area/name pair and the current host name in the message.

Password Hashing

  • PasswordHash.default_algorithm()

    Returns pbkdf2-sha256. This is the portable password-hash baseline for every host. Hosts may report stronger additional password-hash algorithms through Secure.capabilities(){password_hash}.

    scrypt and especially crypt are exposed for compatibility only and are not preferred for new password storage. Treat crypt as a legacy, inadequate password-hash mechanism.

  • PasswordHash.hash(String password, Dict options = {})

    Returns an encoded password hash. options.algorithm defaults to pbkdf2-sha256. options.salt may provide a BinaryString salt for tests and deterministic fixtures; ordinary callers should omit it.

    The portable encoded PBKDF2 form is:

      $zuzu-pbkdf2-sha256$v=1$i=600000,l=32$base64url_salt$base64url_hash

    Argon2id uses PHC-style Argon2id encoding:

      $argon2id$v=19$m=19456,t=2,p=1$base64salt$base64hash

    Scrypt uses:

      $scrypt$ln=17,r=8,p=1,l=32$base64url_salt$base64url_hash

    Legacy crypt hashes use a host-specific format and are only for compatibility with existing password stores.

  • PasswordHash.hash_async(...)

    Returns a Task resolving to the same encoded hash. Browser hosts require this method for password hashing and currently support pbkdf2-sha256 only.

  • PasswordHash.verify(String password, String encoded_hash)

    Returns true when password matches encoded_hash. Unknown, malformed, or unsupported encoded hashes return false.

  • PasswordHash.verify_async(...)

    Returns a Task resolving to the same boolean result. Browser hosts require this method for password verification.

  • PasswordHash.needs_rehash(String encoded_hash, Dict options = {})

    Returns true if encoded_hash is malformed, uses a different algorithm from options.algorithm, uses weaker parameters than the current options, or uses crypt.

  • PasswordHash.derive_key(String password, Dict options)

    Derives raw key material from a passphrase and returns it as a BinaryString. options.salt is required and must be a BinaryString. This method supports PBKDF2, Argon2id, and scrypt where the host reports those capabilities; it does not support crypt.

  • PasswordHash.derive_key_async(...)

    Returns a Task resolving to the same BinaryString. Browser hosts require this method for passphrase key derivation.

Key Derivation

  • KeyDerivation.hkdf_sha256(input_key_material, length, salt, info)

    Returns length bytes of HKDF-SHA256 output as a BinaryString. input_key_material must already be high-entropy key material. This is not the password-hashing API.

    length must be an integer from 0 through 8160. salt and info must be BinaryString values or null. Null salt and info are treated as empty values.

  • KeyDerivation.hkdf_sha256_async(...)

    Returns a Task resolving to the same BinaryString output. Browser hosts may use Web Crypto promises internally; CLI hosts may return an already fulfilled task.

Cipher

  • Cipher.generate_key(String algorithm = "aes-256-gcm")

    Returns a BinaryString key for the requested cipher algorithm. aes-128-gcm uses 16-byte keys, aes-192-gcm uses 24-byte keys, and aes-256-gcm and chacha20-poly1305 use 32-byte keys. Check Secure.capabilities() before relying on optional cipher algorithms.

  • Cipher.encrypt(BinaryString plaintext, BinaryString key, Dict options = {})

    Encrypts and authenticates plaintext. key must match the selected algorithm length. options.algorithm defaults to aes-256-gcm; hosts may advertise additional algorithms through Secure.capabilities(). options.aad may provide additional authenticated data as a BinaryString. The runtime generates a fresh 12-byte nonce for each call.

    The returned envelope is a Dict:

      {
          version: 1,
          algorithm: "aes-256-gcm",
          nonce: b"...",
          ciphertext: b"...",
          tag: b"...",
      }

    nonce is 12 bytes and tag is 16 bytes for the currently supported AEAD ciphers.

  • Cipher.decrypt(Dict envelope, BinaryString key, Dict options = {})

    Verifies and decrypts a cipher envelope. If options.algorithm is provided, it must match envelope.algorithm. Authentication failure, including a wrong key, wrong AAD, or tampered envelope, throws an exception.

  • Cipher.encrypt_async(...)
  • Cipher.decrypt_async(...)

    Return Task values resolving to the corresponding synchronous result. Browser hosts use Web Crypto and require the async methods for cipher operations. CLI hosts may return already fulfilled tasks.

Signing

Signing algorithms are reported through Secure.capabilities(). ECDSA P-256 with SHA-256 and ECDSA P-384 with SHA-384 are the portable ECDSA names. Some hosts also support Ed25519 or ECDSA P-521 with SHA-512. Browser hosts may require async signing methods.

  • SigningKey.generate(String algorithm = "ed25519")

    Generates a new signing key. Common algorithms are ed25519, ecdsa-p256-sha256, ecdsa-p384-sha384, and ecdsa-p521-sha512. The default is ed25519. SigningKey.generate_async(...) returns a Task resolving to the same kind of key.

  • SigningKey.import_private(key, Dict options = {})

    Imports a private signing key. options.format may be raw or pem; when it is omitted, BinaryString keys default to raw and String keys default to pem. Raw private keys are 32-byte Ed25519 seeds, 32-byte P-256 scalars, 48-byte P-384 scalars, or 66-byte P-521 scalars where supported. Raw ECDSA private-key import should pass options.algorithm to avoid ambiguity with Ed25519. PEM private keys are importable as Ed25519 PKCS#8, ECDSA PKCS#8, or traditional EC private-key PEM where the host supports it.

    SigningKey.import_private_async(...) returns a Task resolving to the same kind of key.

  • SigningKey.import_public(key, Dict options = {})

    Imports a public key. Raw Ed25519 public keys are 32 bytes. Raw P-256 public keys are 65-byte SEC1 uncompressed points, and raw P-384 public keys are 97-byte SEC1 uncompressed points. Compressed EC points are not supported. PEM public keys use SPKI. options.format follows the same rules as import_private.

    SigningKey.import_public_async(...) returns a Task resolving to a PublicKey.

  • SigningKey.public_key()

    Returns the matching PublicKey.

  • SigningKey.sign(BinaryString message)

    Signs message and returns a BinaryString signature. Ed25519 signatures are 64 bytes. ECDSA signatures are DER encoded. SigningKey.sign_async(...) returns a Task resolving to the same signature.

  • SigningKey.export_private(Dict options = {})

    Exports private signing-key material. The default raw format returns a 32-byte Ed25519 seed, 32-byte P-256 scalar, or 48-byte P-384 scalar as a BinaryString. format: "pem" returns PEM text where supported.

  • PublicKey.verify(BinaryString message, BinaryString signature)

    Returns true when signature is a valid signature for message. Wrong messages, wrong keys, malformed DER, invalid ECDSA points, and algorithm mismatches return false. PublicKey.verify_async(...) returns a Task resolving to the same boolean result.

  • PublicKey.export(Dict options = {})

    Exports public key material. The default raw format returns a 32-byte Ed25519 public key, a 65-byte P-256 SEC1 uncompressed point, or a 97-byte P-384 SEC1 uncompressed point. For X25519 public keys, it returns the 32-byte Montgomery public key. format: "pem" returns SPKI PEM as a String for signing keys; X25519 public keys only support raw in this phase.

Key Agreement

X25519 key agreement is available as the key_agreement/x25519 capability. It produces a raw 32-byte shared secret, which applications should normally pass through KeyDerivation.hkdf_sha256 with protocol-specific info before using it as an encryption key.

Browser hosts require the async key-agreement methods when X25519 is available through Web Crypto.

  • KeyAgreement.generate(String algorithm = "x25519")

    Generates a new X25519 key-agreement object. The only supported algorithm is x25519. KeyAgreement.generate_async(...) returns a Task resolving to the same kind of object.

  • KeyAgreement.import_private(BinaryString key, Dict options = {})

    Imports a raw 32-byte X25519 private key. options.algorithm may be x25519, and options.format may be raw. PEM import is not part of this phase.

    KeyAgreement.import_private_async(...) returns a Task resolving to the same kind of object.

  • KeyAgreement.import_public(BinaryString key, Dict options = {})

    Imports a raw 32-byte X25519 public key and returns a PublicKey. KeyAgreement.import_public_async(...) returns a Task resolving to a PublicKey.

  • KeyAgreement.public_key()

    Returns the matching X25519 PublicKey.

  • KeyAgreement.export_private(Dict options = {})

    Exports the raw 32-byte X25519 private key as a BinaryString. Only format: "raw" is supported.

  • KeyAgreement.derive(PublicKey peer)

    Derives a raw 32-byte shared secret using a peer X25519 PublicKey and returns it as a BinaryString. Signing public keys and other algorithms are rejected. KeyAgreement.derive_async(...) returns a Task resolving to the same shared secret.

Certificates

X.509 certificate parsing is available as certificate capabilities. Hosts may support PEM text, DER bytes, or both. Unsupported formats throw clear errors.

  • Certificate.parse(String pem)

    Parses one PEM certificate and returns a Certificate. This is not available on browser hosts.

  • Certificate.parse(BinaryString der)

    Parses one DER certificate and returns a Certificate.

  • Certificate.parse_chain(String pem)

    Parses one or more PEM certificates and returns an Array of Certificate objects. This is not available on browser hosts.

  • Certificate.parse_chain(BinaryString der)

    Parses one DER certificate and returns a one-element Array.

  • Certificate.subject() and Certificate.issuer()

    Return string renderings of the certificate subject and issuer names. Structured distinguished-name dictionaries are deferred.

  • Certificate.serial_number()

    Returns the serial number as uppercase hexadecimal text with no separators.

  • Certificate.not_before() and Certificate.not_after()

    Return std/time Time objects for the certificate validity bounds.

  • Certificate.fingerprint(String algorithm = "sha256")

    Returns the certificate fingerprint as a BinaryString. sha256 is the portable baseline; hosts may also support sha384 and sha512.

  • Certificate.to_der()

    Returns the original DER certificate bytes as a BinaryString.

  • Certificate.to_pem()

    Returns canonical CERTIFICATE PEM text.

  • Certificate.public_key()

    Returns a PublicKey for supported certificate public-key algorithms. Unsupported algorithms throw clearly.

  • Certificate.verify_chain(Array chain, Dict options = {})

    Verifies an X.509 chain where the host supports chain verification. chain[0] is the leaf certificate; later entries are intermediates. Trust roots are supplied through options.roots, which may be a Certificate, PEM String, Array of those values, or null. options.use_system_roots defaults to false; when true, CLI hosts also use the host system trust store.

    options.hostname may be a String or null. options.time may be a std/time Time, numeric epoch seconds, or null; omitted or null values use the current time.

    The method returns a Dict with valid, reason, error, hostname, verified_at, and chain_length. Validation failures return valid: false with a stable reason such as untrusted-root, expired, not-yet-valid, hostname-mismatch, or invalid-chain. Bad argument types throw.

TLS Identities

TLS identity objects are parsed and inspected by std/secure and can be supplied to std/net/http UserAgent or Request objects for mutual-TLS client authentication. PEM certificate input may contain a chain; the first certificate is treated as the leaf certificate. The full chain is retained internally for HTTP TLS use, but this phase does not expose a public chain accessor.

  • TlsIdentity.from_pem(String certificate_pem, String private_key_pem, String password = null)

    Parses PEM identity material and returns a TlsIdentity. password null means an empty passphrase. Browser hosts accept PEM identities, but the identity is inert for signing-key extraction.

  • TlsIdentity.from_pkcs12(BinaryString bytes, String password = null)

    Parses PKCS#12 identity material where supported. password null means an empty passphrase.

  • TlsIdentity.certificate()

    Returns the leaf certificate as a Certificate.

  • TlsIdentity.private_key()

    Returns a SigningKey for supported private-key algorithms. Unsupported key algorithms throw clearly.

Secure Random

  • SecureRandom.bytes(Number length)

    Returns a BinaryString of exactly length bytes from the host CSPRNG. length must be a non-negative integer.

  • SecureRandom.token(Number bytes = 32)

    Returns URL-safe Base64 text with no padding, generated from bytes random bytes. bytes must be a non-negative integer.

  • SecureRandom.int(Number max)

    Returns a number in the range 0 to max - 1. max must be a positive integer no greater than 2^53. The implementation uses rejection sampling to avoid modulo bias.

Async Contract

Every supported asynchronous secure operation will expose an async method on every runtime. Browser implementations may need async methods for Web Crypto promises. CLI implementations may return already completed tasks when an operation is naturally synchronous.

async_required reports which areas need asynchronous APIs on the current host. The keys are stable even before those areas are implemented.

COPYRIGHT AND LICENCE

std/secure is copyright Toby Inkster.

It is free software; you may redistribute it and/or modify it under the terms of either the Artistic License 1.0 or the GNU General Public License version 2.