Bài viết kỹ thuật

PDFiumPas: Mã hóa AES-GCM PDF 2.0 theo ISO/TS 32003

PDFiumPas ghi mã hóa ISO/TS 32003 thông qua SaveAsEncrypted: đặt Revision thành erR7 và mọi chuỗi cùng luồng đều được bảo vệ bằng AES-256 ở chế độ GCM, mật mã có xác thực mà PDF 2.0 có được vào năm 2023. Đặt thêm EnableIntegrityProtection thì tài liệu cũng mang theo một token PDF MAC độc lập, được ValidatePdfMac kiểm tra ở phía đọc

Đó là hai lớp bảo vệ khác nhau mà người dùng thường xuyên nhầm lẫn với nhau. GCM xác thực từng giá trị đã mã hóa. MAC xác thực toàn bộ tài liệu. Bạn cần cả hai, vì những lý do khác nhau

GCM bổ sung điều gì mà CBC chưa từng cung cấp?

Xác thực bản mã. AES-256 ở chế độ CBC, cơ chế AESV3 trong ISO 32000-2, giữ nội dung bí mật và không nói gì về việc liệu nó có đến nguyên vẹn hay không. CBC có tính dễ uốn (malleable) theo những cách cụ thể, đã được nghiên cứu kỹ: một kẻ tấn công có thể đảo bit trong bản mã sẽ tạo ra những thay đổi có thể dự đoán được trong bản rõ của khối tiếp theo, và không gì trong định dạng này nhận ra điều đó

GCM khép kín lỗ hổng đó lại. Mỗi giá trị đã mã hóa mang theo một thẻ xác thực 16 byte, được tuần tự hóa đầy đủ theo yêu cầu của ISO/TS 32003, và việc giải mã sẽ thất bại thay vì trả về một bản rõ đã bị thay đổi khi thẻ đó không khớp. Theo thuật ngữ PDF, một chuỗi hoặc luồng bị can thiệp trong một tài liệu AESV4 là một lỗi cứng ngay tại thời điểm sử dụng, không phải một giá trị kỳ lạ lan truyền vào ứng dụng của bạn. Từ điển Encrypt đánh dấu điều này bằng /CFM /AESV4V 6 / R 7, cùng một mục extension khai báo /ExtensionLevel 32003/ExtensionRevision (:2023)

Ba phiên bản, ba hệ sinh thái

TPdfEncryptionRevision cung cấp erR5, erR6erR7, và lựa chọn ở đây là một quyết định tương thích nhiều hơn là một quyết định mật mã học. R5 là cơ chế AES-256 gốc được công bố như một phần mở rộng của PDF 1.7, với một băm mật khẩu SHA-256 đơn, và nó mở được trên hầu như mọi thứ trong mười lăm năm qua. R6 là cơ chế dẫn xuất khóa được gia cố, chuẩn hóa trong ISO 32000-2, dùng cấu trúc lặp SHA-256/384/512 của thuật toán 2.B, và đó là điều một quy trình PDF 2.0 hoặc PDF/A-4 hiện đại mong đợi. R7 là ISO/TS 32003, dùng cùng cơ chế dẫn xuất 2.B với AES-GCM làm mật mã

Mức hỗ trợ của trình đọc đi theo đúng thứ tự đó, và hỗ trợ R7 vẫn còn mỏng ngoài các trình xem chủ đạo hiện nay. Đây là cùng một sự đánh đổi chi phối bất kỳ tính năng PDF 2.0 nào: lựa chọn mới nhất là kỹ thuật tốt nhất nhưng có đối tượng hẹp nhất. Hãy quyết định dựa trên ai sẽ mở tệp, và nếu câu trả lời là "một hệ thống lưu trữ không ai cập nhật từ năm 2019", câu trả lời là R5 bất kể chính sách bảo mật ưa thích điều gì

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;   // token PDF MAC độc lập

    if not Pdf.SaveAsEncrypted('quarterly-report.enc.pdf', Opts) then
      raise Exception.Create('Encrypted save failed');
  finally
    Pdf.Free;
  end;
end;

Vì sao cần một MAC tài liệu bên trên một mật mã đã xác thực?

Vì các thẻ GCM bảo vệ các giá trị, không bảo vệ cách sắp xếp của các giá trị đó. Mỗi chuỗi và luồng trong một tài liệu AESV4 đều được xác thực riêng lẻ, nhưng bảng tham chiếu chéo, đánh số đối tượng và trailer là cấu trúc, không phải nội dung đã mã hóa. Kẻ tấn công không thể giả mạo một luồng, nhưng không gì trong bản thân mật mã ngăn được chúng sắp xếp lại các đối tượng mà tài liệu trỏ tới, hoặc ghép các đối tượng từ một phiên bản trước đó của cùng tệp

Token PDF MAC độc lập giải quyết lớp đó. PDFiumPas dẫn xuất nó từ khóa mã hóa tệp bằng một /KDFSalt 32 byte riêng được ghi trong từ điển Encrypt, nên việc sở hữu mật khẩu chính là điều cho phép một trình đọc xác nhận token đó. Kết quả là một câu trả lời duy nhất cho một câu hỏi duy nhất: liệu tài liệu này, xét như một tổng thể, có đúng là tài liệu đã được ghi ra hay không

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);       // không có token trong tệp này
      pmvsInvalid:     Quarantine(Mac.MessageText);   // bị can thiệp hoặc cắt cụt
      pmvsUnsupported: RouteForManualReview(Mac.MessageText);
    end;
  finally
    Pdf.Free;
  end;
end;

Bốn giá trị trạng thái đó cần bốn phản ứng khác nhau, và việc gộp chúng thành một boolean sẽ làm mất đi sự phân biệt quan trọng đó. pmvsNotPresent nghĩa là tệp đơn giản là không có token, điều này mô tả gần như mọi PDF đã mã hóa được viết trước năm 2024 và không phải bằng chứng của điều gì cả. pmvsInvalid nghĩa là một token có mặt nhưng không xác minh được, đây là một phát hiện thực sự và nên dừng xử lý lại. pmvsUnsupported nghĩa là token tồn tại dưới một dạng mà bản build này chưa triển khai, đây là một khoảng trống tương thích, không phải một cuộc tấn công. Coi "không có mặt" là "không hợp lệ" sẽ đưa cả kho lưu trữ cũ của bạn vào diện cách ly ngay trong ngày đầu tiên

Mã hóa vẫn chưa làm được điều gì

Các cờ quyền vẫn giữ nguyên như chúng luôn vậy: một yêu cầu gửi tới phần mềm tuân thủ, không phải một cơ chế kiểm soát. Các bit /P từ Bảng 22 của ISO 32000-1 không cho phép in hoặc trích xuất được các trình xem cư xử đúng mực tôn trọng và bị mọi thứ khác phớt lờ, và bất kỳ ai nắm giữ mật khẩu người dùng đã nắm giữ nội dung đã giải mã rồi. Mã hóa là ranh giới; quyền hạn mô tả ý định bên trong ranh giới đó

Có hai chi tiết vận hành đáng để lên kế hoạch trước. Thứ nhất, mã hóa và việc sửa đổi sau này tương tác với nhau: nối thêm một bản cập nhật tăng dần vào một tài liệu đã mã hóa có các quy tắc riêng, được trình bày trong cập nhật tăng dần trên PDF đã mã hóa, và một token MAC là một phát biểu ở cấp tài liệu mà một lượt nối thêm bất cẩn sẽ làm mất hiệu lực. Thứ hai, cấu trúc GCM dùng một bộ đếm IV tất định, và PDFiumPas sẽ raise thay vì tái sử dụng một giá trị bộ đếm nếu không gian đó từng bị cạn kiệt, vì việc tái sử dụng nonce trong GCM gây hậu quả thảm khốc theo cách mà một sự tràn số âm thầm sẽ che giấu mất

Chọn giữa ba phiên bản trong thực tế

Hãy ghi ra ai sẽ mở tệp, rồi mới chọn. Với việc phân phối nội bộ nơi mọi trình đọc đều là một trình xem hiện đại dưới sự kiểm soát của bạn, R7 kèm bảo vệ toàn vẹn là lựa chọn mạnh nhất hiện có và không có lý do gì để không dùng nó. Với các tài liệu rời khỏi tổ chức, R6 là mặc định hợp lý: nó được chuẩn hóa trong ISO 32000-2 thay vì trong một đặc tả kỹ thuật đặt trên nó, và hỗ trợ rộng rãi. Với kho lưu trữ và các bên tiêu thụ cũ, R5 là lựa chọn duy nhất mở được một cách đáng tin cậy, và bạn nên ghi lại lý do tại cùng nơi bạn ghi lại phần còn lại của chính sách lưu trữ

Dù chọn phiên bản nào, hãy xác minh đầu ra thay vì tin rằng lệnh gọi đã thành công. Mở lại tệp đã mã hóa, kiểm tra ValidatePdfMac, và xác nhận phiên bản đã khai báo đúng như bạn mong đợi, dùng các kiểm tra tuân thủ phiên bản trong tuân thủ chính xác phiên bản PDF. Một danh sách kiểm tra rộng hơn cho tài liệu không đáng tin nằm trong rà soát các rủi ro bảo mật PDF

PDFiumPas là một thành phần Delphi và Lazarus xây quanh engine PDFium với ngăn xếp mã hóa PDF 2.0 được triển khai nguyên bản bằng Pascal, nên AES-256, GCM và token MAC không cần bất kỳ DLL mật mã bên ngoài nào. API mã hóa được tài liệu hóa tại trang thành phần PDFium cho Delphi