pdfium_render/pdf/document/
signature.rs

1//! Defines the [PdfSignature] struct, exposing functionality related to a single
2//! digital signature in a `PdfSignatures` collection.
3
4use crate::bindgen::FPDF_SIGNATURE;
5use crate::bindings::PdfiumLibraryBindings;
6use crate::error::{PdfiumError, PdfiumInternalError};
7use crate::utils::mem::create_byte_buffer;
8use crate::utils::utf16le::get_string_from_pdfium_utf16le_bytes;
9use std::ffi::{c_uint, CString};
10use std::os::raw::{c_char, c_void};
11
12/// The modification detection permission (MDP) applicable to a single digital signature
13/// in a `PdfDocument`.
14///
15/// For more information on MDP, refer to "DocMDP" in Section 8.7.1 on page 731 of
16/// The PDF Reference, Sixth Edition. The permission levels in this enumeration
17/// correspond to those listed in table 8.104 on page 733.
18#[derive(Copy, Clone, Debug, PartialEq)]
19pub enum PdfSignatureModificationDetectionPermission {
20    /// MDP access permission level 1: no changes to the document are permitted;
21    /// any change to the document invalidates the signature.
22    Mdp1,
23
24    /// MDP access permission level 2: permitted changes are filling in forms,
25    /// instantiating page templates, and signing; other changes invalidate the signature.
26    Mdp2,
27
28    /// MDP access permission level 3: permitted changes are the same as for level 2,
29    /// as well as annotation creation, deletion, and modification; other changes
30    /// invalidate the signature.
31    Mdp3,
32}
33
34impl PdfSignatureModificationDetectionPermission {
35    #[inline]
36    pub(crate) fn from_pdfium(raw: c_uint) -> Result<Self, PdfiumError> {
37        match raw {
38            0 => Err(PdfiumError::PdfiumLibraryInternalError(
39                PdfiumInternalError::Unknown,
40            )),
41            1 => Ok(PdfSignatureModificationDetectionPermission::Mdp1),
42            2 => Ok(PdfSignatureModificationDetectionPermission::Mdp2),
43            3 => Ok(PdfSignatureModificationDetectionPermission::Mdp3),
44            _ => Err(PdfiumError::UnknownPdfSignatureModificationDetectionPermissionLevel),
45        }
46    }
47}
48
49/// A single digital signature in a `PdfDocument`.
50pub struct PdfSignature<'a> {
51    handle: FPDF_SIGNATURE,
52    bindings: &'a dyn PdfiumLibraryBindings,
53}
54
55impl<'a> PdfSignature<'a> {
56    #[inline]
57    pub(crate) fn from_pdfium(
58        handle: FPDF_SIGNATURE,
59        bindings: &'a dyn PdfiumLibraryBindings,
60    ) -> Self {
61        PdfSignature { handle, bindings }
62    }
63
64    /// Returns the [PdfiumLibraryBindings] used by this [PdfSignature].
65    #[inline]
66    pub fn bindings(&self) -> &'a dyn PdfiumLibraryBindings {
67        self.bindings
68    }
69
70    /// Returns the raw byte data for this [PdfSignature].
71    ///
72    /// For public key signatures, the byte data is either a DER-encoded PKCS#1 binary or
73    /// a DER-encoded PKCS#7 binary.
74    pub fn bytes(&self) -> Vec<u8> {
75        // Retrieving the byte data from Pdfium is a two-step operation. First, we call
76        // FPDFSignatureObj_GetContents() with a null buffer; this will retrieve the length of
77        // the reason text in bytes. If the length is zero, then there is no reason associated
78        // with this signature.
79
80        // If the length is non-zero, then we reserve a byte buffer of the given
81        // length and call FPDFSignatureObj_GetContents() again with a pointer to the buffer;
82        // this will write the reason text to the buffer in UTF16-LE format.
83
84        let buffer_length =
85            self.bindings
86                .FPDFSignatureObj_GetContents(self.handle, std::ptr::null_mut(), 0);
87
88        if buffer_length == 0 {
89            // The signature is empty.
90
91            return Vec::new();
92        }
93
94        let mut buffer = create_byte_buffer(buffer_length as usize);
95
96        let result = self.bindings.FPDFSignatureObj_GetContents(
97            self.handle,
98            buffer.as_mut_ptr() as *mut c_void,
99            buffer_length,
100        );
101
102        assert_eq!(result, buffer_length);
103
104        buffer
105    }
106
107    /// Returns the reason for the signing, if any, as a plain text description provided by the
108    /// creator of this [PdfSignature].
109    pub fn reason(&self) -> Option<String> {
110        // Retrieving the reason from Pdfium is a two-step operation. First, we call
111        // FPDFSignatureObj_GetReason() with a null buffer; this will retrieve the length of
112        // the reason text in bytes. If the length is zero, then there is no reason associated
113        // with this signature.
114
115        // If the length is non-zero, then we reserve a byte buffer of the given
116        // length and call FPDFSignatureObj_GetReason() again with a pointer to the buffer;
117        // this will write the reason text to the buffer in UTF16-LE format.
118
119        let buffer_length =
120            self.bindings
121                .FPDFSignatureObj_GetReason(self.handle, std::ptr::null_mut(), 0);
122
123        if buffer_length == 0 {
124            // There is no reason given for this signature.
125
126            return None;
127        }
128
129        let mut buffer = create_byte_buffer(buffer_length as usize);
130
131        let result = self.bindings.FPDFSignatureObj_GetReason(
132            self.handle,
133            buffer.as_mut_ptr() as *mut c_void,
134            buffer_length,
135        );
136
137        assert_eq!(result, buffer_length);
138
139        get_string_from_pdfium_utf16le_bytes(buffer)
140    }
141
142    /// Returns the date, if any, in plain text format as specified by the creator of this [PdfSignature].
143    /// The format of the returned value is expected to be `D:YYYYMMDDHHMMSS+XX'YY'`, with precision
144    /// to the second and timezone information included.
145    ///
146    /// This value should only be used if the date of signing is not available in the
147    /// PKCS#7 digital signature.
148    pub fn signing_date(&self) -> Option<String> {
149        // Retrieving the signing date from Pdfium is a two-step operation. First, we call
150        // FPDFSignatureObj_GetTime() with a null buffer; this will retrieve the length of
151        // the timestamp in bytes. If the length is zero, then there is no timestamp associated
152        // with this signature.
153
154        // If the length is non-zero, then we reserve a byte buffer of the given
155        // length and call FPDFSignatureObj_GetTime() again with a pointer to the buffer;
156        // this will write the timestamp to the buffer as an array of 7-bit ASCII characters.
157
158        let buffer_length =
159            self.bindings
160                .FPDFSignatureObj_GetTime(self.handle, std::ptr::null_mut(), 0);
161
162        if buffer_length == 0 {
163            // There is no timestamp given for this signature.
164
165            return None;
166        }
167
168        let mut buffer = create_byte_buffer(buffer_length as usize);
169
170        let result = self.bindings.FPDFSignatureObj_GetTime(
171            self.handle,
172            buffer.as_mut_ptr() as *mut c_char,
173            buffer_length,
174        );
175
176        assert_eq!(result, buffer_length);
177
178        if let Ok(result) = CString::from_vec_with_nul(buffer) {
179            result.into_string().ok()
180        } else {
181            None
182        }
183    }
184
185    /// Returns the modification detection permission (MDP) applicable to this [PdfSignature],
186    /// if available.
187    ///
188    /// For more information on MDP, refer to "DocMDP" in Section 8.7.1 on page 731 of
189    /// The PDF Reference, Sixth Edition.
190    pub fn modification_detection_permission(
191        &self,
192    ) -> Result<PdfSignatureModificationDetectionPermission, PdfiumError> {
193        PdfSignatureModificationDetectionPermission::from_pdfium(
194            self.bindings
195                .FPDFSignatureObj_GetDocMDPPermission(self.handle),
196        )
197    }
198}