Skip to content
getnextpdf.com

Enterprise edition

FIPS 140 — Deep Reference

This page is the deep reference for the NextPDF Enterprise FIPS 140 module. The module is a policy and self-test capability. It restricts cryptographic choices to a FIPS-aligned allow-list, runs a power-on known-answer-test battery, and inhibits cryptographic output when the battery fails. A FIPS-compatible deployment additionally requires a FIPS-validated cryptographic provider supplied by the operator.

This capability ships in NextPDF Enterprise (nextpdf/enterprise) and activates with an Enterprise-tier license envelope. A deployment without that entitlement does not load the capability’s classes. Compare editions and get a license.

All symbols live in NextPDF\Enterprise\Security\Fips, except FipsBootstrap in NextPDF\Enterprise\Bootstrap.

SymbolParametersDefault behaviorReturnsThrows or fails withNotes
FipsBootstrap::boot()?CryptoPolicyInterface $policy, ?FipsSelfTest $selfTest, ?LoggerInterface $auditLogger (all default null)Runs the power-on battery once at start-up; defaults to the strict policyFipsModeGuardFipsModuleErrorStateException on any power-on test failureComposition root; audit trail off when $auditLogger is null
FipsBootstrap::lazy()Same as boot()Defers the battery to the first boundary assertionFipsModeGuardNone at call time; the first assertion can throw FipsModuleErrorStateExceptionModule stays PRE_OPERATIONAL until first assertion
FipsBootstrap::signatureEnforcer()?CryptoPolicyInterface $policy = null, ?FipsSelfTest $selfTest = nullBoots the module, then wraps the guard for signer chokepointsFipsSignatureEnforcerFipsModuleErrorStateException on any power-on test failureDelegates to boot()
FipsBootstrap::selfTestReport()?FipsSelfTest $selfTest = nullRuns the battery on demand and summarizes itarray{status: FipsSelfTestStatus, operational: bool, failed: list<string>}Does not throw; failures appear in failedFor admin health endpoints and CLI use
FipsCryptoPolicy::strict()?FipsSelfTest $selfTest = nullFIPS 140-3 preset: SHA-256/384/512; approved RSA, RSASSA-PSS, ECDSA OIDs; aes-256-cbc, aes-256-gcm; RSA >= 2048, EC >= 256selfNoneConstructor is private; presets are the only entry
FipsCryptoPolicy::standard()?FipsSelfTest $selfTest = nullFIPS 140-2 preset: strict set plus aes-128-cbcselfNoneLegacy interoperability only
FipsCryptoPolicy::assertPreOperational()NoneRuns, or replays the latched result of, the power-on batteryvoidFipsModuleErrorStateException when any power-on test failsDriven by the Core enforcement seam before the first operation
FipsCryptoPolicy query surfacestring / int inputsAllow-list membership checks; unknown key type is deniedbool / stringNoneisHashAlgorithmAllowed, isSignatureAlgorithmAllowed, isEncryptionAlgorithmAllowed, isKeyStrengthAllowed, getPreferredHashAlgorithm, getName
FipsModeGuard::__construct()CryptoPolicyInterface $policy, ?FipsBootGuard $bootGuard = null, ?FipsAuditLogger $auditLogger = nullWraps a policy with assert-style boundariesNoneWithout $bootGuard there is no self-test gate; production composition supplies it
FipsModeGuard::assertHashAllowed()string $algorithmDeny catalogue first, then the allow-listvoidFipsViolationException; FipsModuleErrorStateException when a boot guard is wiredAudit record precedes any FipsViolationException
FipsModeGuard::assertSignatureAlgorithmAllowed()string $oidDeny catalogue first, then the allow-listvoidSame as aboveOIDs are matched exactly
FipsModeGuard::assertEncryptionAllowed()string $algorithmDeny catalogue first, then the allow-listvoidSame as aboveNames are compared lowercase
FipsModeGuard::assertKeyStrengthAllowed()string $keyType, int $bitLengthDeny catalogue first, then the policy minimumsvoidSame as aboveUnknown key type is denied
FipsModeGuard::getPolicy()NoneReturns the wrapped policyCryptoPolicyInterfaceNone
FipsBootGuard::__construct()FipsSelfTest $selfTestHolds the battery; does not run itNoneOne power-on cycle per instance
FipsBootGuard::report()NoneRuns the battery once, caches the report, latches on errorFipsSelfTestReportNoneFirst call runs the tests
FipsBootGuard::rerun()NoneForces a fresh run; an error run latches the processFipsSelfTestReportNoneOn-demand self-test; not an error-recovery path
FipsBootGuard::assertOperational()NoneAsserts the module is operational; sticky across the processvoidFipsModuleErrorStateExceptionA clean instance still throws when the process latched an error
FipsBootGuard::status()NoneReports the cached statusFipsSelfTestStatusNoneERROR when latched; PRE_OPERATIONAL when never run
FipsSelfTest::__construct()?callable $randomBytesProvider = null, ?callable $hashProvider = nullUses platform hash() and random_bytes()NoneOverrides exist for deterministic failure tests
FipsSelfTest::run()NoneExecutes the full battery; never short-circuitsFipsSelfTestReportNoneFailures land in the report, not in exceptions
FipsSelfTestReportFipsSelfTestStatus $status, array $results, array $failedResultsImmutable aggregate of one runassertOperational() throws FipsModuleErrorStateException in the error stateAlso isOperational(), isError()
FipsSelfTestReport::assertOperational()NoneReturns unless the report status is ERRORvoidFipsModuleErrorStateException carrying failedResultsThe canonical start-up abort for production code (ISO/IEC 19790 §7.10 error state)
FipsSelfTestResultstring $algorithm, string $kind, bool $passed, string $message = ''Immutable per-test outcomeNonekind is KAT, PWCT, or HEALTH; also isPassed(), isFailed()
FipsSelfTestStatusString-backed enumNoneCases PRE_OPERATIONAL, OPERATIONAL, ERROR
FipsSignatureEnforcer::__construct()FipsModeGuard $guardWraps a boot-gated guard for signing chokepointsNone
FipsSignatureEnforcer::assertSignatureGenerationAllowed()string $algorithm, string $certificatePemResolves the OID and key strength, then delegates to the guardvoidFipsViolationException (unknown algorithm, unapproved PSS digest, unprovable key, or policy denial); FipsModuleErrorStateException via the guardGeneration path only; verification never routes here
FipsAuditLogger::__construct()CryptoPolicyInterface $policy, LoggerInterface $loggerWraps a PSR-3 logger and the same policy the guard enforcesNoneRecorded decisions cannot diverge from enforced ones
FipsAuditLogger::logHashOperation() / logSignatureOperation() / logEncryptionOperation() / logKeyStrengthCheck()string / int inputs per decisionLogs ALLOW at INFO and DENY at WARNINGbool (true when allowed)NoneStructured context: policy name, item, decision
FipsTransitioningAlgorithmsstring / int inputsStatic SP 800-131A Rev.2 deny cataloguebool / list<string>NoneisHashDisallowed, isSignatureOidDisallowed, isEncryptionDisallowed, isKeyStrengthDisallowed, plus disallowedHashes, disallowedSignatureOids, disallowedEncryption
FipsTransitioningAlgorithms::isHashDisallowed()string $algorithmLowercases the name, then tests membership in the disallowed-hash catalogueboolNonestatic; true for MD2, MD4, MD5, SHA-1, and RIPEMD-160 per SP 800-131A Rev.2
FipsViolationExceptionstring $policyName, string $violatingItem, string $reasonTyped policy violation with public readonly fieldsSubtype of NextPDF\Exception\NextPdfException
FipsModuleErrorStateExceptionarray $failedResults, ?string $message = nullError-state refusal carrying the failed test resultsSubtype of NextPDF\Exception\NextPdfException
public static function boot(?CryptoPolicyInterface $policy = null, ?FipsSelfTest $selfTest = null, ?LoggerInterface $auditLogger = null): FipsModeGuard
public static function lazy(?CryptoPolicyInterface $policy = null, ?FipsSelfTest $selfTest = null, ?LoggerInterface $auditLogger = null): FipsModeGuard
public static function signatureEnforcer(?CryptoPolicyInterface $policy = null, ?FipsSelfTest $selfTest = null): FipsSignatureEnforcer
public static function selfTestReport(?FipsSelfTest $selfTest = null): array
public static function strict(?FipsSelfTest $selfTest = null): self
public static function standard(?FipsSelfTest $selfTest = null): self
public function assertPreOperational(): void
public function isHashAlgorithmAllowed(string $algorithm): bool
public function isSignatureAlgorithmAllowed(string $oid): bool
public function isEncryptionAlgorithmAllowed(string $algorithm): bool
public function isKeyStrengthAllowed(string $keyType, int $bitLength): bool
public function getPreferredHashAlgorithm(): string
public function getName(): string
public function __construct(private CryptoPolicyInterface $policy, private ?FipsBootGuard $bootGuard = null, private ?FipsAuditLogger $auditLogger = null)
public function assertHashAllowed(string $algorithm): void
public function assertSignatureAlgorithmAllowed(string $oid): void
public function assertEncryptionAllowed(string $algorithm): void
public function assertKeyStrengthAllowed(string $keyType, int $bitLength): void
public function getPolicy(): CryptoPolicyInterface
public function __construct(private readonly FipsSelfTest $selfTest)
public function report(): FipsSelfTestReport
public function rerun(): FipsSelfTestReport
public function assertOperational(): void
public function status(): FipsSelfTestStatus
public function __construct(?callable $randomBytesProvider = null, ?callable $hashProvider = null)
public function run(): FipsSelfTestReport
public function __construct(private readonly FipsModeGuard $guard)
public function assertSignatureGenerationAllowed(string $algorithm, string $certificatePem): void
public function __construct(private CryptoPolicyInterface $policy, private LoggerInterface $logger)
public function logHashOperation(string $algorithm): bool
public function logSignatureOperation(string $oid): bool
public function logEncryptionOperation(string $algorithm): bool
public function logKeyStrengthCheck(string $keyType, int $bitLength): bool
public static function isHashDisallowed(string $algorithm): bool
public static function isSignatureOidDisallowed(string $oid): bool
public static function isEncryptionDisallowed(string $algorithm): bool
public static function isKeyStrengthDisallowed(string $keyType, int $bitLength): bool
public static function disallowedHashes(): array
public static function disallowedSignatureOids(): array
public static function disallowedEncryption(): array
public function __construct(public FipsSelfTestStatus $status, public array $results, public array $failedResults)
public function isOperational(): bool
public function isError(): bool
public function assertOperational(): void
// FipsSelfTestResult
public function __construct(public string $algorithm, public string $kind, public bool $passed, public string $message = '')
public function isPassed(): bool
public function isFailed(): bool
// FipsSelfTestStatus
enum FipsSelfTestStatus: string
{
case PRE_OPERATIONAL = 'pre_operational';
case OPERATIONAL = 'operational';
case ERROR = 'error';
}
  • The policy allow-list is the authoritative decision. FipsTransitioningAlgorithms adds an explicit SP 800-131A Rev.2 deny layer above it, for audit-clear rejection messages. The deny layer never widens or overrides the allow-list.
  • FipsCryptoPolicy::strict() permits SHA-256, SHA-384, and SHA-512; RSA PKCS#1 v1.5, RSASSA-PSS, and ECDSA signature OIDs bound to those hashes; aes-256-cbc and aes-256-gcm; and key floors of RSA 2048, EC 256, Ed25519 256. standard() additionally permits aes-128-cbc.
  • The power-on battery covers: SHA-256/384/512 digest KATs, an HMAC-SHA-256 KAT, an AES-256-CBC encrypt-and-decrypt KAT, an AES-256-GCM tag KAT, an ECDSA P-256 pair-wise consistency test, and a DRBG health check. FipsSelfTest::run() always executes every test and never short-circuits, so the report is complete for audit evidence.
  • The DRBG row is a continuous health test (draw length plus distinct successive draws), not a known-answer test. The DRBG known-answer obligation is delegated to the underlying FIPS-validated provider the operator supplies.
  • The error state is process-sticky. The first ERROR report observed by any FipsBootGuard latches the whole process. A fresh guard or policy instance cannot launder the error, and a later passing rerun does not clear it. Only a process restart (a true power cycle) resets the state.
  • FipsCryptoPolicy implements NextPDF\Contracts\CryptoPolicyInterface and NextPDF\Contracts\PreOperationalSelfTestInterface. When configured as the Core crypto policy, the Core enforcement seam drives assertPreOperational() before the first signature or ciphertext is produced.
  • FipsSignatureEnforcer gates the generation path only. Verification of already-generated signatures is legacy use under SP 800-131A Rev.2 and never routes through the enforcer.
  • When an audit logger is wired, every assert boundary emits one ALLOW (INFO) or DENY (WARNING) record before any policy-violation throw. Every FipsViolationException denial is therefore evidenced in the trail. The boot-guard gate runs first, so an error-state refusal is raised before the audit record. The FipsAuditLogger consults the same policy the guard enforces, so recorded decisions cannot diverge from enforced ones.
  • An unknown key type is denied by both layers: the policy returns false and the catalogue treats it as disallowed.
  • A signing-algorithm identifier the enforcer cannot map to a signature OID is refused fail-closed with FipsViolationException.
  • Every RSASSA-PSS variant shares the OID 1.2.840.113549.1.1.10, so the OID alone cannot prove the digest. The enforcer binds the effective digest explicitly and denies any PSS token whose digest is not SHA-256/384/512.
  • A certificate that cannot be parsed, or whose public-key bit length is unavailable, is denied as key:unprovable.
  • On a runtime without OpenSSL asymmetric primitives, the ECDSA pair-wise consistency test records a failure, not a skip, and the module enters ERROR.
  • Two identical successive 32-byte random draws fail the DRBG health check (stuck-output detection) and force ERROR.
  • A FipsModeGuard constructed without a boot guard performs policy checks only and has no self-test gate. Production composition goes through FipsBootstrap, which always wires the gate.
  • Hash and cipher names are compared lowercase; signature OIDs are matched exactly, with no normalization.
  • After FipsBootstrap::lazy(), the module stays PRE_OPERATIONAL until the first assert boundary runs the battery. PRE_OPERATIONAL is treated as not operational at assertion time.

This module is the FIPS-mode surface itself. While the module is in the error state, and while pre-operational self-tests run, cryptographic output is inhibited: every assert boundary throws FipsModuleErrorStateException before any signature or ciphertext is produced (ISO/IEC 19790:2025 §7.3.3 b), AS03.07). FipsBootGuard::status() and FipsBootstrap::selfTestReport() expose the state so an operator can determine that the module entered the error state (ISO/IEC 19790:2025 §7.10.3). These behaviors are capability claims about NextPDF code: the module boundary that FIPS 140 validates is the operator-supplied cryptographic provider.

ClaimStandardClause
FIPS 140-3 is based on ISO/IEC 19790 and ISO/IEC 24759; this page therefore cites ISO/IEC 19790 clauses.FIPS 140-3Introduction (fips_140_3#x26.x2)
Output is inhibited in the error state and during pre-operational self-tests.ISO/IEC 19790:2025§7.3.3 b) [AS03.07]
A known-answer test compares a computed result against a known expected output; the battery implements this shape.ISO/IEC 19790:2025§7.10.4
The operator can determine the error state through a status output.ISO/IEC 19790:2025§7.10.3 [AS10.10]
Operators can initiate the self-tests on demand for periodic testing; rerun() and selfTestReport() provide this.ISO/IEC 19790:2025§7.10.5 [AS10.54]
SHA-1 is disallowed for new digital-signature generation; the deny catalogue rejects it.NIST SP 800-131A Rev.2§9
Signature generation below 112-bit strength (RSA < 2048, ECDSA order < 224) is disallowed; the key floors enforce this.NIST SP 800-131A Rev.2§3 Table 2
Verification of already-generated SHA-1 signatures is legacy use; it does not route through the generation gate.NIST SP 800-131A Rev.2Change summary (9.x4.p12)

All clauses are paraphrased; no normative text is reproduced. The module aligns its behavior with the cited clauses as a compliance-assistance capability. Whether a deployment is FIPS-compliant depends on the operator’s validated provider, module boundary definition, and compliance program.

  • Deterministic failure tests inject broken providers through the FipsSelfTest constructor, or through the $selfTest parameters on FipsCryptoPolicy::strict(), standard(), and the FipsBootstrap methods.
  • The process-sticky error latch has an internal, test-only reset hook. It is not part of the supported API, and production code must not call it.
  • Each PHP worker process runs its own power-on battery. The report is cached per instance, so hot-path assertions are constant-time status checks.
  • Do not catch FipsModuleErrorStateException and continue. The exception means the module refuses cryptographic services; the correct response is to stop and restart the process after remediation.
  • FipsBootstrap::selfTestReport() serves on-demand and periodic self-test needs, such as health endpoints. A passing on-demand run never clears a latched error.

This page documents externally observable behavior and the supported public API surface only. Internal namespace paths, helper classes, mechanism tables, runbook filenames, and ticket prefixes are out of scope.