Fail closed by default

Every verification path in this toolkit throws on failure. No parse, verify, or validation API returns false, null, zero, or a partial result in place of a real verdict -- an accept-on-error default is treated as a bug in the library, not an ergonomic for the caller.

What a failure looks like

Malformed input is a permanent verdict, so it throws a typed error: every error the toolkit raises extends PkiError and carries a stable, greppable code of the form domain/reason -- asn1/trailing-bytes, cms/bad-version, x509/not-a-certificate. A consumer needs a single err instanceof pki.errors.PkiError check and can switch on err.code without parsing prose. The full code inventory is on the Error catalog reference page, harvested from the source at boot.

Why throwing beats returning false

A boolean verify result composes badly: one forgotten if and a failed signature check becomes a successful code path. A thrown, typed error inverts the failure mode -- ignoring it stops the program instead of accepting the forgery. The same posture applies inside the library: internal helpers never swallow a verification error into a default value on the way up.

Bounded before it walks

Fail-closed includes resource exhaustion. The DER decoder enforces size and depth caps before it walks a byte, so a hostile input can fail fast with a typed error instead of exhausting memory or the stack. Parsing hostile bytes may only succeed or throw a PkiError -- that contract is fuzzed continuously.