PDF-Kryptorichtlinie, FIPS & CBOM
PDF Oxide abstrahiert jede Verschlüsselungs- und Signaturoperation hinter einem austauschbaren Krypto-Provider, legt darüber eine fail-closed arbeitende Laufzeit-Richtlinie und erfasst pro Prozess ein Inventar der tatsächlich genutzten Algorithmen. Dieses Inventar wird als CycloneDX-1.6-Kryptografie-Stückliste (CBOM) exportiert – das maschinenlesbare Artefakt, das regulierte Käufer und Prüfer verlangen.
Diese Seite behandelt die Provider-Auswahl, den FIPS-140-3-Modus, die Grammatik der Laufzeitrichtlinie, das Krypto-Inventar und den CBOM-Export.
Binding-Abdeckung. Die gesamte Krypto-Governance-Oberfläche ist prozessglobal (sie nimmt kein Dokument-Handle entgegen) und wird von Rust, Python, Go, Node.js / TypeScript und C# bereitgestellt. Die Methodennamen unterscheiden sich je nach Binding (idiomatische Schreibweise). Die Provider-Auswahl-Hälfte (
active_provider/fips_available/use_fips) ist die FIPS-Oberfläche aus Issue #236; die Richtlinien-Hälfte (set_policy/policy/inventory/cbom) ist die Laufzeit-Governance-Oberfläche aus Issue #230. Der FIPS-Modus selbst bewirkt nur dann etwas, wenn die native Bibliothek mit--features fipskompiliert wurde – andernfalls gibtuse_fipseinen Fehler zurück bzw. löst ihn aus undfips_availableist false.
Die zwei Provider
| Provider | Build | Algorithmen | Verwendung |
|---|---|---|---|
rust-crypto (Standard) |
immer vorhanden | jeder Algorithmus, auf den die PDF-Spezifikation verweist, einschließlich des veralteten MD5-±RC4-Pfads für ISO-32000-1-R≤4-Dokumente | allgemein |
aws-lc-rs |
optional, --features fips |
FIPS-140-3-validiert; verweigert MD5, RC4 und SHA-1-Signaturen | FIPS-Konformität |
Der Standard-Provider wird verzögert (lazy) installiert – die erste Kryptooperation (Öffnen eines verschlüsselten Dokuments, Prüfen einer Signatur) legt ihn fest. Deshalb müssen Sie vor jeder Kryptoarbeit im Prozess auf FIPS umschalten.
Wie prüfe ich den aktiven Krypto-Provider?
active_provider ist nicht initialisierend: Wenn noch kein Provider festgelegt wurde, gibt es "rust-crypto (default, lazy)" zurück, ohne den Standard festzuschreiben, sodass ein späterer use_fips-Aufruf weiterhin gelingen kann. Sobald ein Provider festgelegt ist (explizit oder verzögert), gibt es den echten Namen zurück ("rust-crypto" oder "aws-lc-rs").
import pdf_oxide
# Safe to call first — does not commit a provider.
print(pdf_oxide.crypto_active_provider()) # "rust-crypto (default, lazy)"
print(pdf_oxide.crypto_available_providers()) # ["rust-crypto"] (+ "aws-lc-rs" on a FIPS wheel)
use pdf_oxide::crypto;
// Non-initializing read for display/audit.
let name = if crypto::is_set() {
crypto::active().name().to_string()
} else {
format!("{} (default, lazy)", crypto::RustCryptoProvider.name())
};
println!("active crypto provider: {name}");
package main
import (
"fmt"
pdfoxide "github.com/yfedoseev/pdf_oxide/go"
)
func main() {
fmt.Println(pdfoxide.ActiveCryptoProvider()) // "rust-crypto"
fmt.Println(pdfoxide.IsFipsCryptoAvailable()) // false unless built with FIPS
}
const pdf_oxide = require("pdf-oxide");
console.log(pdf_oxide.getActiveCryptoProvider()); // "rust-crypto (default, lazy)"
console.log(pdf_oxide.isFipsCryptoAvailable()); // false unless the -fips package
using PdfOxide;
Console.WriteLine(PdfDocument.GetActiveCryptoProvider()); // "rust-crypto"
Console.WriteLine(PdfDocument.IsFipsCryptoAvailable()); // False unless FIPS lib
Signaturen
| Operation | Rust | Python | Go | Node.js | C# |
|---|---|---|---|---|---|
| Aktiver Provider | crypto::active().name() -> &'static str |
crypto_active_provider() -> str |
ActiveCryptoProvider() -> string |
getActiveCryptoProvider(): string |
GetActiveCryptoProvider() -> string |
| FIPS verfügbar? | cfg(feature = "fips") |
crypto_available_providers() -> list[str] |
IsFipsCryptoAvailable() -> bool |
isFipsCryptoAvailable(): boolean |
IsFipsCryptoAvailable() -> bool |
Die C-ABI, die sie umhüllen: char *pdf_oxide_crypto_active_provider(void) und int32_t pdf_oxide_crypto_fips_available(void) (gibt 1 zurück, wenn der FIPS-Provider einkompiliert wurde, andernfalls 0).
Wie aktiviere ich den FIPS-Modus für ein PDF?
Rufen Sie use_fips (oder in Rust set_provider) vor jeder Kryptooperation auf. Es ist einmalig setzbar: Ein zweiter Aufruf – oder jeder Aufruf, nachdem der Standard-Provider verzögert installiert wurde – schlägt fehl.
C-ABI-Fehlercodes für int32_t pdf_oxide_crypto_use_fips(void):
| Code | Bedeutung |
|---|---|
0 |
Erfolg |
1 |
FIPS-Feature nicht einkompiliert |
2 |
ein Provider ist bereits gesetzt |
import pdf_oxide
try:
pdf_oxide.crypto_use_fips() # raises RuntimeError if not compiled in / already set
print("FIPS provider active:", pdf_oxide.crypto_active_provider()) # "aws-lc-rs"
except RuntimeError as e:
print("FIPS unavailable:", e)
# Now every encrypted-PDF open / signature verify routes through aws-lc-rs.
doc = pdf_oxide.PdfDocument("encrypted-r6.pdf")
print(doc.page_count())
use std::sync::Arc;
use pdf_oxide::PdfDocument;
use pdf_oxide::crypto::{set_provider, AwsLcProvider};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Must run before any pdf_oxide operation that uses crypto.
set_provider(Arc::new(AwsLcProvider::new()))
.expect("crypto provider already installed");
// Every PDF open / signature verify now routes through aws-lc-rs.
let doc = PdfDocument::open("encrypted-r6.pdf")?;
println!("pages: {}", doc.page_count());
Ok(())
}
package main
import (
"log"
pdfoxide "github.com/yfedoseev/pdf_oxide/go"
)
func main() {
// Before any crypto operation.
if err := pdfoxide.UseFipsCryptoProvider(); err != nil {
log.Fatalf("FIPS unavailable: %v", err) // ErrFipsNotCompiled / ErrCryptoProviderAlreadySet
}
log.Println("active:", pdfoxide.ActiveCryptoProvider()) // "aws-lc-rs"
}
const pdf_oxide = require("pdf-oxide");
try {
pdf_oxide.useFipsCryptoProvider();
console.log("active:", pdf_oxide.getActiveCryptoProvider()); // "aws-lc-rs"
} catch (e) {
console.error("FIPS unavailable:", e.message);
}
using PdfOxide;
try
{
PdfDocument.UseFipsCryptoProvider();
Console.WriteLine(PdfDocument.GetActiveCryptoProvider()); // "aws-lc-rs"
}
catch (InvalidOperationException e)
{
Console.WriteLine($"FIPS unavailable: {e.Message}");
}
Was lehnt FIPS ab?
PDF Standard Security R≤4 (Passwortverschlüsselung von PDF 1.4–1.6, ISO 32000-1 §7.6.3) verwendet MD5 zur Schlüsselableitung und RC4/AES-128 als Chiffre. FIPS 140-3 verbietet MD5 und RC4, daher schlägt das Öffnen eines solchen Dokuments unter aws-lc-rs mit einer Behebungsmeldung fehl:
active CryptoProvider 'aws-lc-rs' rejects PDF Standard Security R=4
(R≤4 requires MD5; FIPS 140-3 forbids MD5).
Re-encrypt the document at R=6 (AES-256) or build pdf_oxide
without the 'fips' feature so the default 'rust-crypto'
provider stays active.
Die Lösung: das Dokument in einer nicht FIPS-beschränkten Umgebung mit R=6 (PDF 2.0 AES-256, ISO 32000-2 §7.6.4) neu verschlüsseln. R=6-Dokumente lassen sich unter aws-lc-rs problemlos öffnen.
Installation der FIPS-Variante. Die
-fips-Distributionen liefern Single-Provider-Binärdateien (Prüfer verlangen dies in der Regel). Verwenden Siepip install pdf_oxide_fips,npm install pdf-oxide-fips,dotnet add package PdfOxide.Fips,go get github.com/yfedoseev/pdf_oxide/go-fipsoder bauen Sie Rust mitcargo build --features fips. Standard- und FIPS-Variante desselben Release-Tags sind auf allen Nicht-Krypto-Pfaden byteweise identisch.
Wie lege ich eine Laufzeit-Kryptorichtlinie fest?
Eine Richtlinie schränkt ein, welche Algorithmen erlaubt sind – unabhängig davon, welcher Provider aktiv ist. Sie ist einmalig setzbar (ein Laufzeit-Downgrade ist ein Angriffsvektor) und fail-closed (eine nicht parsbare Spezifikation wird abgelehnt und es wird keine Richtlinie installiert). Der Standard – nie gesetzt – ist "compat", was byteweise identisch zum Verhalten vor Einführung der Richtlinien ist.
Grammatik: mode[;clause]*
mode∈compat|strict|fips-strictclause=(allow|deny):<alg>@<read|write>– z. B."compat;deny:rc4@write;deny:md5@write"oder"fips-strict"
C-ABI-Fehlercodes für int32_t pdf_oxide_crypto_set_policy(const char *spec):
| Code | Bedeutung |
|---|---|
0 |
Erfolg |
1 |
ungültiges Argument (null / Nicht-UTF-8-Spezifikation) |
2 |
Parse-Fehler – Spezifikation abgelehnt, Richtlinie nicht installiert |
3 |
eine Richtlinie ist bereits gesetzt |
import pdf_oxide
# Deny the legacy write paths while staying compatible for reads.
pdf_oxide.crypto_set_policy("compat;deny:rc4@write;deny:md5@write")
print(pdf_oxide.crypto_policy()) # canonical grammar string
use pdf_oxide::crypto::{self, SecurityPolicy};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Parse fail-closed, then install set-once.
let policy: SecurityPolicy = "fips-strict".parse()?;
crypto::set_policy(policy).expect("a crypto policy is already set");
println!("active policy: {}", crypto::active_policy());
Ok(())
}
package main
import (
"log"
pdfoxide "github.com/yfedoseev/pdf_oxide/go"
)
func main() {
// Treat any error as fatal — the policy is not installed on failure.
if err := pdfoxide.SetCryptoPolicy("strict;deny:md5@write"); err != nil {
log.Fatalf("policy rejected: %v", err)
}
log.Println("active policy:", pdfoxide.CryptoPolicy())
}
const pdf_oxide = require("pdf-oxide");
// Throws on an unparseable spec (fail-closed) or if already set.
pdf_oxide.setCryptoPolicy("fips-strict");
console.log(pdf_oxide.cryptoPolicy()); // "fips-strict"
using PdfOxide;
// Throws ArgumentException (rejected) or InvalidOperationException (already set).
PdfDocument.SetCryptoPolicy("compat;deny:rc4@write");
Console.WriteLine(PdfDocument.CryptoPolicy());
Signaturen
| Operation | Rust | Python | Go | Node.js | C# |
|---|---|---|---|---|---|
| Richtlinie festlegen | crypto::set_policy(SecurityPolicy) -> Result<(), SetPolicyError> |
crypto_set_policy(spec: str) -> None |
SetCryptoPolicy(spec string) -> error |
setCryptoPolicy(spec: string): void |
SetCryptoPolicy(string spec) -> void |
| Richtlinie lesen | crypto::active_policy() -> &'static SecurityPolicy |
crypto_policy() -> str |
CryptoPolicy() -> string |
cryptoPolicy(): string |
CryptoPolicy() -> string |
Wie lese ich das Krypto-Inventar aus?
Das Inventar ist die Menge der Algorithmus-Tokens, die in diesem Prozess bisher tatsächlich genutzt wurden – ein sperrfreies atomares Bitset, das an jeder Durchsetzungsgrenze erfasst wird. Es ist der minimale Bericht „welche Kryptografie hat dieser Lauf verwendet?". Tokens sind stabile Kleinbuchstaben-Strings wie md5, aes256, rc4, sha256.
Die C-ABI gibt einen kommaseparierten String zurück (char *pdf_oxide_crypto_inventory(void), "", wenn nichts genutzt wurde); die High-Level-Bindings teilen ihn für Sie in eine Liste/Slice/ein Array auf.
import pdf_oxide
doc = pdf_oxide.PdfDocument("signed.pdf")
doc.verify_signatures() # exercises some algorithms
print(pdf_oxide.crypto_inventory()) # e.g. ["sha256", "aes256"] (list[str])
use pdf_oxide::crypto;
// `inventory()` returns the exercised algorithms in declaration order.
let used: Vec<&'static str> = crypto::inventory()
.into_iter()
.map(|a| a.token())
.collect();
println!("exercised: {used:?}"); // e.g. ["md5", "aes256"]
package main
import (
"fmt"
pdfoxide "github.com/yfedoseev/pdf_oxide/go"
)
func main() {
inv := pdfoxide.CryptoInventory() // []string, empty when nothing exercised
fmt.Println("exercised:", inv)
}
const pdf_oxide = require("pdf-oxide");
console.log(pdf_oxide.cryptoInventory()); // string[], e.g. ["sha256"]
using PdfOxide;
foreach (var token in PdfDocument.CryptoInventory()) // string[]
Console.WriteLine(token);
Wie exportiere ich eine CBOM (CycloneDX) für ein PDF?
crypto_cbom serialisiert das Inventar in eine CycloneDX-1.6-cryptographic-asset-Stückliste als JSON-String. Das Dokument hat stets bomFormat: "CycloneDX", specVersion: "1.6", einen metadata-Block (RFC-3339-Zeitstempel + die pdf_oxide-Tool-Komponente) und eine cryptographic-asset-Komponente pro genutztem Algorithmus. Ein leeres Inventar ergibt eine gültige BOM ohne Komponenten.
import json
import pdf_oxide
# Run your workload first so the inventory reflects what was used.
doc = pdf_oxide.PdfDocument("signed.pdf")
doc.verify_signatures()
cbom = pdf_oxide.crypto_cbom() # JSON string
report = json.loads(cbom)
assert report["bomFormat"] == "CycloneDX"
assert report["specVersion"] == "1.6"
with open("pdf-cbom.json", "w") as f:
f.write(cbom)
use std::fs;
use pdf_oxide::crypto;
fn main() -> std::io::Result<()> {
// ... run the crypto workload first ...
let cbom: String = crypto::cbom_json(); // CycloneDX 1.6 JSON
fs::write("pdf-cbom.json", cbom)?;
Ok(())
}
package main
import (
"os"
pdfoxide "github.com/yfedoseev/pdf_oxide/go"
)
func main() {
cbom := pdfoxide.CryptoCBOM() // CycloneDX 1.6 JSON string
_ = os.WriteFile("pdf-cbom.json", []byte(cbom), 0o644)
}
const fs = require("fs");
const pdf_oxide = require("pdf-oxide");
const cbom = pdf_oxide.cryptoCbom(); // CycloneDX 1.6 JSON string
fs.writeFileSync("pdf-cbom.json", cbom);
using System.IO;
using PdfOxide;
string cbom = PdfDocument.CryptoCbom(); // CycloneDX 1.6 JSON string
File.WriteAllText("pdf-cbom.json", cbom);
CBOM-Struktur
Jeder genutzte Algorithmus wird zu einer Komponente:
{
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"metadata": {
"timestamp": "2026-06-22T12:00:00+00:00",
"tools": { "components": [{ "type": "application", "name": "pdf_oxide", "version": "0.3.69" }] }
},
"components": [
{
"type": "cryptographic-asset",
"name": "aes256",
"bom-ref": "crypto/algorithm/aes256",
"cryptoProperties": {
"assetType": "algorithm",
"algorithmProperties": {
"primitive": "block-cipher",
"classicalSecurityLevel": 256,
"nistQuantumSecurityLevel": 0,
"certificationLevel": ["fips140-3"]
}
}
}
]
}
Das primitive wird aus der Algorithmusart abgeleitet (hash, block-cipher, stream-cipher für RC4, signature, kdf, drbg); certificationLevel ist ["fips140-3"] für FIPS-zugelassene Algorithmen und andernfalls ["none"].
Signaturen
| Operation | Rust | Python | Go | Node.js | C# |
|---|---|---|---|---|---|
| Inventar | crypto::inventory() -> Vec<AlgorithmId> |
crypto_inventory() -> list[str] |
CryptoInventory() -> []string |
cryptoInventory(): string[] |
CryptoInventory() -> string[] |
| CBOM | crypto::cbom_json() -> String |
crypto_cbom() -> str |
CryptoCBOM() -> string |
cryptoCbom(): string |
CryptoCbom() -> string |
Empfohlene Startsequenz
Da Provider und Richtlinien beide einmalig setzbar sind und der Standard-Provider verzögert installiert wird, konfigurieren Sie die Krypto-Governance als allererstes, was Ihr Prozess tut:
import pdf_oxide
# 1. (optional) switch to FIPS — must precede any crypto operation.
if "aws-lc-rs" in pdf_oxide.crypto_available_providers():
pdf_oxide.crypto_use_fips()
# 2. install the governance policy (fail-closed, set-once).
pdf_oxide.crypto_set_policy("fips-strict")
# 3. ... run your PDF workload ...
doc = pdf_oxide.PdfDocument("encrypted-r6.pdf")
doc.verify_signatures()
# 4. emit governance artifacts.
print("policy: ", pdf_oxide.crypto_policy())
print("inventory:", pdf_oxide.crypto_inventory())
with open("pdf-cbom.json", "w") as f:
f.write(pdf_oxide.crypto_cbom())
FAQ
Kann ich den Krypto-Provider nach dem Festlegen wieder zurückschalten? Nein. Sowohl use_fips (Provider) als auch set_policy (Richtlinie) sind per Design einmalig setzbar – ein Austausch mitten im Prozess, während laufender Kryptozustand existiert (FIPS-Selbsttest, HSM-Sitzung), ist ein Korrektheitsrisiko, und ein Laufzeit-Richtlinien-Downgrade ist ein Angriffsvektor. Tests, die einen frischen Provider benötigen, müssen in separaten Prozess-Namespaces laufen.
Warum sagt crypto_active_provider() "rust-crypto (default, lazy)", bevor ich irgendetwas tue? Diese Auslesung ist bewusst nicht initialisierend: Sie meldet den Standard, ohne ihn festzuschreiben, sodass ein späteres crypto_use_fips() weiterhin gelingen kann. Der echte Name erscheint, sobald ein Provider festgelegt ist.
Ist FIPS in jedem Paket verfügbar? Nur, wenn die native Bibliothek mit --features fips gebaut wurde. Verwenden Sie die parallelen -fips-Distributionen (pdf_oxide_fips, pdf-oxide-fips, PdfOxide.Fips, go-fips), die Single-Provider-FIPS-Binärdateien liefern. In einem Nicht-FIPS-Build ist fips_available false und use_fips liefert einen Fehler.
Ist die CBOM standardkonform? Ja – es handelt sich um ein gültiges CycloneDX-1.6-Dokument (bomFormat: "CycloneDX", specVersion: "1.6"), sodass es sich direkt in SBOM-/CBOM-Werkzeuge und Lieferketten-Audits einfügt.
Verlangsamt das Festlegen einer Richtlinie die PDF-Verarbeitung? Nein. PDF Oxide bleibt bei seinem Extraktionsprofil von 0,8 ms im Mittel / 100 % Bestehensquote; das Inventar ist ein sperrfreies atomares Bitset und Richtlinienprüfungen finden nur an Krypto-Durchsetzungsgrenzen statt.
Nächste Schritte
- Digitale Signaturen – PDFs signieren und prüfen (der Hauptnutzer der Kryptorichtlinie)
- PDF/A-Validierung – Archivierungskonformität (lehnt Verschlüsselung ab)
- API-Referenz – vollständige Rust-API