PDF 暗号ポリシー、FIPS、CBOM
PDF Oxide は、すべての暗号化および署名操作を差し替え可能な暗号プロバイダの背後に抽象化し、その上にフェイルクローズなランタイムポリシーを重ね、実際に行使したアルゴリズムをプロセス単位のインベントリとして記録します。このインベントリは CycloneDX 1.6 暗号部品表 (CBOM) としてエクスポートできます。これは規制対象の購入者や監査人が求める、機械可読な成果物です。
このページでは、プロバイダの選択、FIPS 140-3 モード、ランタイムポリシーの文法、暗号インベントリ、CBOM のエクスポートを扱います。
バインディングの対応状況。 暗号ガバナンスの面全体はプロセスグローバル(ドキュメントハンドルを取りません)であり、Rust、Python、Go、Node.js / TypeScript、C# で公開されています。メソッド名はバインディングごとに異なります(各言語の慣用的な命名規則)。プロバイダ選択の側(
active_provider/fips_available/use_fips)は issue #236 の FIPS サーフェスであり、ポリシーの側(set_policy/policy/inventory/cbom)は issue #230 のランタイムガバナンスサーフェスです。FIPS モードそのものは、ネイティブライブラリが--features fips付きでコンパイルされている場合にのみ機能します。それ以外の場合、use_fipsはエラーを返す/送出し、fips_availableは false になります。
2 つのプロバイダ
| プロバイダ | ビルド | アルゴリズム | 用途 |
|---|---|---|---|
rust-crypto(デフォルト) |
常に存在 | PDF 仕様が参照するすべてのアルゴリズム。ISO 32000-1 R≤4 ドキュメント向けのレガシー MD5 + RC4 パスを含む | 汎用 |
aws-lc-rs |
オプトイン、--features fips |
FIPS 140-3 検証済み。MD5、RC4、SHA-1 署名を拒否 | FIPS 準拠 |
デフォルトプロバイダは遅延的にインストールされます。最初の暗号操作(暗号化ドキュメントのオープン、署名の検証)がそれを確定させます。そのため、プロセス内で暗号処理が発生する前に FIPS へ切り替える必要があります。
アクティブな暗号プロバイダを確認するには?
active_provider は初期化を伴いません。まだプロバイダが確定していない場合、デフォルトを確定させずに "rust-crypto (default, lazy)" を返すため、後続の use_fips 呼び出しが依然として成功し得ます。プロバイダが確定すると(明示的または遅延的に)、実際の名前("rust-crypto" または "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
シグネチャ
| 操作 | Rust | Python | Go | Node.js | C# |
|---|---|---|---|---|---|
| アクティブなプロバイダ | crypto::active().name() -> &'static str |
crypto_active_provider() -> str |
ActiveCryptoProvider() -> string |
getActiveCryptoProvider(): string |
GetActiveCryptoProvider() -> string |
| FIPS は利用可能か? | cfg(feature = "fips") |
crypto_available_providers() -> list[str] |
IsFipsCryptoAvailable() -> bool |
isFipsCryptoAvailable(): boolean |
IsFipsCryptoAvailable() -> bool |
これらがラップする C ABI: char *pdf_oxide_crypto_active_provider(void) と int32_t pdf_oxide_crypto_fips_available(void)(FIPS プロバイダがコンパイル時に組み込まれていれば 1、そうでなければ 0 を返します)。
PDF で FIPS モードを有効にするには?
いかなる暗号操作よりも前に use_fips(Rust では set_provider)を呼び出してください。これは一度限りの設定です。2 回目の呼び出し、またはデフォルトプロバイダが遅延的にインストールされた後の任意の呼び出しは失敗します。
int32_t pdf_oxide_crypto_use_fips(void) の C ABI エラーコード:
| コード | 意味 |
|---|---|
0 |
成功 |
1 |
FIPS 機能がコンパイルされていない |
2 |
プロバイダが既に設定されている |
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}");
}
FIPS は何を拒否するか?
PDF Standard Security R≤4(PDF 1.4〜1.6 のパスワード暗号化、ISO 32000-1 §7.6.3)は、鍵導出に MD5 を、暗号方式に RC4/AES-128 を使用します。FIPS 140-3 は MD5 と RC4 を禁止しているため、そのようなドキュメントを aws-lc-rs の下で開くと、是正メッセージとともに失敗します:
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.
解決策: FIPS 制限のない環境で R=6(PDF 2.0 AES-256、ISO 32000-2 §7.6.4)で再暗号化します。R=6 ドキュメントは aws-lc-rs の下で問題なく開けます。
FIPS バリアントのインストール。
-fipsディストリビューションは単一プロバイダのバイナリを提供します(監査人は通常これを要求します)。pip install pdf_oxide_fips、npm install pdf-oxide-fips、dotnet add package PdfOxide.Fips、go get github.com/yfedoseev/pdf_oxide/go-fipsを使用するか、Rust をcargo build --features fipsでビルドしてください。同じリリースタグのデフォルトバリアントと FIPS バリアントは、暗号以外のすべてのパスでバイト単位で同一です。
ランタイム暗号ポリシーを設定するには?
ポリシーは、どのプロバイダがアクティブかとは独立に、許可されるアルゴリズムを絞り込みます。これは一度限りの設定(ランタイムでのダウングレードは攻撃ベクトル)であり、フェイルクローズ(解析不能な仕様は拒否され、ポリシーは一切インストールされません)です。デフォルト(設定しない場合)は "compat" で、ポリシー導入前の挙動とバイト単位で同一です。
文法: mode[;clause]*
mode∈compat|strict|fips-strictclause=(allow|deny):<alg>@<read|write>— 例:"compat;deny:rc4@write;deny:md5@write"または"fips-strict"
int32_t pdf_oxide_crypto_set_policy(const char *spec) の C ABI エラーコード:
| コード | 意味 |
|---|---|
0 |
成功 |
1 |
無効な引数(null / 非 UTF-8 の仕様) |
2 |
解析エラー — 仕様は拒否され、ポリシーはインストールされません |
3 |
ポリシーが既に設定されている |
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());
シグネチャ
| 操作 | Rust | Python | Go | Node.js | C# |
|---|---|---|---|---|---|
| ポリシーの設定 | crypto::set_policy(SecurityPolicy) -> Result<(), SetPolicyError> |
crypto_set_policy(spec: str) -> None |
SetCryptoPolicy(spec string) -> error |
setCryptoPolicy(spec: string): void |
SetCryptoPolicy(string spec) -> void |
| ポリシーの読み取り | crypto::active_policy() -> &'static SecurityPolicy |
crypto_policy() -> str |
CryptoPolicy() -> string |
cryptoPolicy(): string |
CryptoPolicy() -> string |
暗号インベントリを読み取るには?
インベントリは、このプロセスでこれまでに実際に行使されたアルゴリズムトークンの集合です。各強制境界で記録されるロックフリーのアトミックなビットセットです。「この実行ではどの暗号処理が使われたか?」を示す最小限のレポートです。トークンは md5、aes256、rc4、sha256 のような安定した小文字の文字列です。
C ABI はカンマ区切りの文字列を返します(char *pdf_oxide_crypto_inventory(void)、何も行使されていない場合は "")。高レベルのバインディングはこれをリスト/スライス/配列に分割してくれます。
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);
PDF 向けに CBOM (CycloneDX) をエクスポートするには?
crypto_cbom は、インベントリを CycloneDX 1.6 の cryptographic-asset 部品表として JSON 文字列にシリアライズします。ドキュメントには常に bomFormat: "CycloneDX"、specVersion: "1.6"、metadata ブロック(RFC 3339 タイムスタンプ + pdf_oxide ツールコンポーネント)、そして行使されたアルゴリズムごとに 1 つの cryptographic-asset コンポーネントが含まれます。空のインベントリは、コンポーネントを持たない有効な BOM を生成します。
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 の構造
行使された各アルゴリズムは 1 つのコンポーネントになります:
{
"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"]
}
}
}
]
}
primitive はアルゴリズムの種別(hash、block-cipher、RC4 向けの stream-cipher、signature、kdf、drbg)からマッピングされます。certificationLevel は FIPS 承認アルゴリズムの場合 ["fips140-3"]、それ以外の場合 ["none"] です。
シグネチャ
| 操作 | Rust | Python | Go | Node.js | C# |
|---|---|---|---|---|---|
| インベントリ | 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 |
推奨される起動シーケンス
プロバイダとポリシーはいずれも一度限りの設定であり、デフォルトプロバイダは遅延的にインストールされるため、暗号ガバナンスはプロセスが最初に行うこととして構成してください:
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
確定した後で暗号プロバイダを元に戻せますか? いいえ。use_fips(プロバイダ)も set_policy(ポリシー)も、設計上一度限りの設定です。処理中の暗号状態(FIPS 自己テスト、HSM セッション)が存在する状態でプロセスの途中で入れ替えるのは健全性の危険であり、ランタイムでのポリシーのダウングレードは攻撃ベクトルです。新しいプロバイダを必要とするテストは、別々のプロセス名前空間で実行する必要があります。
何もしていないのに、なぜ crypto_active_provider() は "rust-crypto (default, lazy)" と表示するのですか? その読み取りは意図的に初期化を伴いません。デフォルトを確定させずに報告するため、後続の crypto_use_fips() が依然として成功し得ます。実際の名前は、プロバイダが確定すると表示されます。
FIPS はすべてのパッケージで利用できますか? ネイティブライブラリが --features fips でビルドされている場合に限ります。単一プロバイダの FIPS バイナリを提供する並行の -fips ディストリビューション(pdf_oxide_fips、pdf-oxide-fips、PdfOxide.Fips、go-fips)を使用してください。非 FIPS ビルドでは fips_available は false で、use_fips はエラーになります。
CBOM は標準ですか? はい。これは有効な CycloneDX 1.6 ドキュメント(bomFormat: "CycloneDX"、specVersion: "1.6")であるため、SBOM/CBOM ツールやサプライチェーン監査にそのまま組み込めます。
ポリシーを設定すると PDF 処理が遅くなりますか? いいえ。PDF Oxide は 0.8ms 平均 / 100% パスの抽出プロファイルを維持します。インベントリはロックフリーのアトミックなビットセットであり、ポリシーチェックは暗号の強制境界でのみ行われます。
次のステップ
- デジタル署名 — PDF への署名と検証(暗号ポリシーの主な利用者)
- PDF/A 検証 — アーカイブ準拠(暗号化を拒否)
- API リファレンス — 完全な Rust API