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> {}