Skip to main content

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