PDFiumPasは、SaveAsEncryptedを通じてISO/TS 32003の暗号化を書き込みます。RevisionをerR7に設定すると、すべての文字列とストリームが、PDF 2.0が2023年に獲得した認証付き暗号であるGCMモードのAES-256で保護されます。EnableIntegrityProtectionも設定すると、ドキュメントは独立したPDF MACトークンも運ぶようになり、これは読み込み側でValidatePdfMacによってチェックされます
これらは、日常的に混同されがちな2つの異なる保護です。GCMは、暗号化された各値を認証します。MACは、ドキュメント全体を認証します。異なる理由から、その両方が必要になります
GCMが加えるもので、CBCが決して提供しなかったものは何か
暗号文の認証です。ISO 32000-2のAESV3方式であるCBCモードのAES-256は、コンテンツを機密に保ちますが、それが改変されずに届いたかどうかについては何も語りません。CBCは、特定の、よく研究された方法において展性を持ちます。暗号文のビットを反転できる攻撃者は、次のブロックの平文に予測可能な変化を生じさせることができ、フォーマットの側は何も気づきません
GCMはその隙間を塞ぎます。暗号化された各値は、ISO/TS 32003が要求する通り完全にシリアライズされた16バイトの認証タグを運び、そのタグが一致しない場合、復号は改変された平文を返すのではなく失敗します。PDFの用語で言えば、AESV4ドキュメント内の改ざんされた文字列やストリームは、自分のアプリケーションへ伝播する奇妙な値ではなく、使用時点でのハードエラーになります。Encrypt辞書はこれを/CFM /AESV4とV 6 / R 7で示し、あわせて/ExtensionLevel 32003と/ExtensionRevision (:2023)を宣言する拡張エントリも持ちます
3つのリビジョン、3つのエコシステム
TPdfEncryptionRevisionはerR5、erR6、erR7を提供しており、その選択は暗号学的な判断というよりも互換性の判断です。R5は、PDF 1.7拡張として公開された、単一のSHA-256パスワードハッシュを使うオリジナルのAES-256方式であり、この15年間のほぼあらゆるものでも開けます。R6は、ISO 32000-2で標準化された、アルゴリズム2.Bの反復SHA-256/384/512構成を使う強化された鍵導出であり、現在のPDF 2.0やPDF/A-4のワークフローが期待するものです。R7はISO/TS 32003で、同じ2.Bの導出を使いながら、暗号にAES-GCMを用います
リーダー側の対応状況もまさにその順序で並んでおり、R7への対応は現行の主流ビューア以外ではまだ薄いままです。これは、あらゆるPDF 2.0機能を支配するのと同じトレードオフです。最新の選択肢は最良の技術でありながら、最も狭い対象読者しか持ちません。誰がそのファイルを開かなければならないかによって判断してください。その答えが「2019年以降誰も更新していない記録システム」であれば、セキュリティポリシーが何を望んでいようと、答えはR5になります
uses
PDFium, FPdfEncrypt;
var
Pdf: TPdf;
Opts: TPdfEncryptOptions;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'quarterly-report.pdf';
Pdf.LoadDocument;
Opts := TPdfEncryptOptions.Default;
Opts.UserPassword := 'open-secret';
Opts.OwnerPassword := 'admin-secret';
Opts.EncryptMetadata := True;
Opts.Revision := erR7; // ISO/TS 32003 AESV4-GCM
Opts.EnableIntegrityProtection := True; // 独立したPDF MACトークン
if not Pdf.SaveAsEncrypted('quarterly-report.enc.pdf', Opts) then
raise Exception.Create('Encrypted save failed');
finally
Pdf.Free;
end;
end;
認証付き暗号の上に、さらにドキュメントMACが必要なのはなぜか
なぜなら、GCMタグが保護するのは値そのものであって、値の配置ではないからです。AESV4ドキュメント内のすべての文字列とストリームは個別に認証されますが、クロスリファレンステーブル、オブジェクト番号、トレーラーは構造であって暗号化されたコンテンツではありません。攻撃者はストリームを偽造することはできませんが、暗号だけでは、ドキュメントがどのオブジェクトを指しているかを並べ替えたり、同じファイルの以前のリビジョンからオブジェクトを継ぎ接ぎしたりすることを止められません
独立したPDF MACトークンは、その層に対処します。PDFiumPasは、Encrypt辞書に記録された専用の32バイトの/KDFSaltを使ってファイル暗号鍵からこれを導出するため、リーダーがそのトークンを確認できるかどうかは、パスワードを持っているかどうかにかかっています。この結果、1つの問いに対する1つの答えが得られます。このドキュメントは全体として、書き込まれたそのドキュメントであるか、というものです
var
Pdf: TPdf;
Mac: TPdfMacValidationResult;
begin
Pdf := TPdf.Create(nil);
try
Pdf.Password := 'open-secret';
Pdf.FileName := 'quarterly-report.enc.pdf';
Pdf.LoadDocument;
Mac := Pdf.ValidatePdfMac('open-secret');
case Mac.Status of
pmvsValid: ProcessDocument(Pdf);
pmvsNotPresent: ProcessWithWarning(Pdf); // このファイルにはトークンがない
pmvsInvalid: Quarantine(Mac.MessageText); // 改ざんまたは切り詰められている
pmvsUnsupported: RouteForManualReview(Mac.MessageText);
end;
finally
Pdf.Free;
end;
end;
4つのステータス値はそれぞれ異なる対応を必要とし、これを1つのブール値に潰してしまうと、重要な区別が失われます。pmvsNotPresentは、そのファイルに単にトークンがないことを意味し、これは2024年より前に書かれたほとんどすべての暗号化PDFに当てはまるものであって、何かの証拠にはなりません。pmvsInvalidは、トークンは存在するが検証できないことを意味し、これは正真正銘の発見であり処理を止めるべきです。pmvsUnsupportedは、このビルドが実装していない形でトークンが存在することを意味し、これは攻撃ではなく互換性のギャップです。「存在しない」を「無効」として扱ってしまえば、初日で自分の過去のファイル資産すべてを隔離することになってしまいます
暗号化がそれでも成し遂げないこと
権限フラグは、これまでもずっとそうであったものと変わりません。それは制御ではなく、準拠ソフトウェアに対する要望です。印刷や抽出を禁止するISO 32000-1表22の/Pビットは、行儀の良いビューアには尊重されますが、それ以外のあらゆるものには無視され、そしてユーザーパスワードを持っている者は誰であれ、すでに復号済みのコンテンツを手にしています。暗号化が境界であり、権限はその内側での意図を記述するものです
運用上、計画しておく価値のある2つの詳細があります。1つ目は、暗号化とその後の変更が絡み合うという点です。暗号化されたドキュメントへの増分更新の追加には独自の規則があり、これは暗号化PDFに対する増分更新で扱っています。MACトークンはドキュメントレベルの主張であり、不用意な追記はそれを無効にしてしまいます。2つ目は、GCM構成は決定論的なIVカウンターを使っており、そのカウンター空間が万が一枯渇した場合、PDFiumPasはカウンター値を使い回すのではなく例外を発生させるという点です。GCMにおけるノンスの再利用は、静かな折り返しが覆い隠してしまうような形で破滅的な結果を招くからです
実務における3つのリビジョンの選び方
誰がそのファイルを開くのかを書き出してから選んでください。すべての読み手が自分の管理下にある現行ビューアである社内配布であれば、整合性保護付きのR7が利用可能な中で最も強力な選択肢であり、それを使わない理由はありません。組織の外へ出ていくドキュメントであれば、R6が擁護しやすいデフォルトです。その上に載る技術仕様ではなくISO 32000-2そのもので標準化されており、対応状況も広範です。アーカイブやレガシーな読み手にとっては、確実に開けるのはR5だけであり、その理由は、保持ポリシーの他の部分を記録している場所と同じ場所に記録しておくべきです
どれを選んだとしても、呼び出しが成功したことを信じるのではなく、出力を検証してください。暗号化されたファイルを開き直し、ValidatePdfMacを確認し、正確なPDFバージョン準拠にあるバージョン準拠チェックを使って、宣言されているバージョンが期待どおりであることを確かめてください。信頼できないドキュメントに対するより広範な取り込みチェックリストはPDFセキュリティリスクの監査にあります
PDFiumPasは、PDFiumエンジンを取り巻くDelphi・Lazarus向けコンポーネントであり、PDF 2.0の暗号化スタックはPascalでネイティブに実装されているため、AES-256、GCM、MACトークンのいずれも外部の暗号DLLを必要としません。暗号化APIはPDFium Delphiコンポーネントページで説明されています