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}