1use std::fmt;
4
5pub type SignatureResult<T> = Result<T, SignatureError>;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum SignatureError {
11 MissingField {
13 field: String,
15 },
16
17 InvalidByteRange {
19 details: String,
21 },
22
23 InvalidSignatureDict {
25 details: String,
27 },
28
29 ContentsExtractionFailed {
31 details: String,
33 },
34
35 AcroFormNotFound,
37
38 NoSignatureFields,
40
41 ParseError {
43 message: String,
45 },
46
47 CmsParsingFailed {
49 details: String,
51 },
52
53 UnsupportedAlgorithm {
55 algorithm: String,
57 },
58
59 ByteRangeExceedsDocument {
61 offset: u64,
63 length: u64,
65 document_size: u64,
67 },
68
69 HashVerificationFailed {
71 details: String,
73 },
74
75 SignatureVerificationFailed {
77 details: String,
79 },
80
81 CertificateExtractionFailed {
83 details: String,
85 },
86
87 CertificateValidationFailed {
89 details: String,
91 },
92}
93
94impl fmt::Display for SignatureError {
95 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96 match self {
97 Self::MissingField { field } => {
98 write!(f, "Missing required signature field: {}", field)
99 }
100 Self::InvalidByteRange { details } => {
101 write!(f, "Invalid ByteRange format: {}", details)
102 }
103 Self::InvalidSignatureDict { details } => {
104 write!(f, "Invalid signature dictionary: {}", details)
105 }
106 Self::ContentsExtractionFailed { details } => {
107 write!(f, "Failed to extract signature contents: {}", details)
108 }
109 Self::AcroFormNotFound => {
110 write!(f, "Document does not contain an AcroForm dictionary")
111 }
112 Self::NoSignatureFields => {
113 write!(f, "No signature fields found in document")
114 }
115 Self::ParseError { message } => {
116 write!(f, "PDF parsing error: {}", message)
117 }
118 Self::CmsParsingFailed { details } => {
119 write!(f, "CMS/PKCS#7 parsing failed: {}", details)
120 }
121 Self::UnsupportedAlgorithm { algorithm } => {
122 write!(f, "Unsupported algorithm: {}", algorithm)
123 }
124 Self::ByteRangeExceedsDocument {
125 offset,
126 length,
127 document_size,
128 } => {
129 write!(
130 f,
131 "ByteRange exceeds document: offset {} + length {} > document size {}",
132 offset, length, document_size
133 )
134 }
135 Self::HashVerificationFailed { details } => {
136 write!(f, "Hash verification failed: {}", details)
137 }
138 Self::SignatureVerificationFailed { details } => {
139 write!(f, "Signature verification failed: {}", details)
140 }
141 Self::CertificateExtractionFailed { details } => {
142 write!(f, "Certificate extraction failed: {}", details)
143 }
144 Self::CertificateValidationFailed { details } => {
145 write!(f, "Certificate validation failed: {}", details)
146 }
147 }
148 }
149}
150
151impl std::error::Error for SignatureError {}
152
153impl From<crate::error::PdfError> for SignatureError {
154 fn from(err: crate::error::PdfError) -> Self {
155 SignatureError::ParseError {
156 message: err.to_string(),
157 }
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164
165 #[test]
166 fn test_missing_field_error_display() {
167 let err = SignatureError::MissingField {
168 field: "Filter".to_string(),
169 };
170 assert!(err.to_string().contains("Filter"));
171 assert!(err.to_string().contains("Missing"));
172 }
173
174 #[test]
175 fn test_invalid_byterange_error_display() {
176 let err = SignatureError::InvalidByteRange {
177 details: "expected 4 elements".to_string(),
178 };
179 assert!(err.to_string().contains("ByteRange"));
180 assert!(err.to_string().contains("4 elements"));
181 }
182
183 #[test]
184 fn test_acroform_not_found_error_display() {
185 let err = SignatureError::AcroFormNotFound;
186 assert!(err.to_string().contains("AcroForm"));
187 }
188
189 #[test]
190 fn test_error_is_std_error() {
191 fn assert_error<E: std::error::Error>() {}
192 assert_error::<SignatureError>();
193 }
194
195 #[test]
196 fn test_error_clone_eq() {
197 let err1 = SignatureError::NoSignatureFields;
198 let err2 = err1.clone();
199 assert_eq!(err1, err2);
200 }
201
202 #[test]
203 fn test_all_error_variants_display() {
204 let errors = vec![
205 SignatureError::MissingField {
206 field: "SubFilter".to_string(),
207 },
208 SignatureError::InvalidByteRange {
209 details: "negative value".to_string(),
210 },
211 SignatureError::InvalidSignatureDict {
212 details: "not a dictionary".to_string(),
213 },
214 SignatureError::ContentsExtractionFailed {
215 details: "hex decode failed".to_string(),
216 },
217 SignatureError::AcroFormNotFound,
218 SignatureError::NoSignatureFields,
219 SignatureError::ParseError {
220 message: "unexpected EOF".to_string(),
221 },
222 SignatureError::CmsParsingFailed {
223 details: "invalid DER".to_string(),
224 },
225 SignatureError::UnsupportedAlgorithm {
226 algorithm: "MD5".to_string(),
227 },
228 SignatureError::ByteRangeExceedsDocument {
229 offset: 1000,
230 length: 500,
231 document_size: 800,
232 },
233 SignatureError::HashVerificationFailed {
234 details: "hash mismatch".to_string(),
235 },
236 SignatureError::SignatureVerificationFailed {
237 details: "invalid signature".to_string(),
238 },
239 SignatureError::CertificateExtractionFailed {
240 details: "no certificate".to_string(),
241 },
242 SignatureError::CertificateValidationFailed {
243 details: "certificate expired".to_string(),
244 },
245 ];
246
247 for err in errors {
248 let display = err.to_string();
249 assert!(!display.is_empty(), "Error display should not be empty");
250 }
251 }
252
253 #[test]
254 fn test_cms_parsing_failed_error_display() {
255 let err = SignatureError::CmsParsingFailed {
256 details: "invalid ContentInfo".to_string(),
257 };
258 assert!(err.to_string().contains("CMS"));
259 assert!(err.to_string().contains("invalid ContentInfo"));
260 }
261
262 #[test]
263 fn test_unsupported_algorithm_error_display() {
264 let err = SignatureError::UnsupportedAlgorithm {
265 algorithm: "MD5".to_string(),
266 };
267 assert!(err.to_string().contains("algorithm"));
268 assert!(err.to_string().contains("MD5"));
269 }
270
271 #[test]
272 fn test_byterange_exceeds_document_error_display() {
273 let err = SignatureError::ByteRangeExceedsDocument {
274 offset: 1000,
275 length: 500,
276 document_size: 800,
277 };
278 let display = err.to_string();
279 assert!(display.contains("1000"));
280 assert!(display.contains("500"));
281 assert!(display.contains("800"));
282 assert!(display.contains("exceeds"));
283 }
284
285 #[test]
286 fn test_hash_verification_failed_error_display() {
287 let err = SignatureError::HashVerificationFailed {
288 details: "hash mismatch".to_string(),
289 };
290 assert!(err.to_string().contains("Hash verification failed"));
291 assert!(err.to_string().contains("hash mismatch"));
292 }
293
294 #[test]
295 fn test_signature_verification_failed_error_display() {
296 let err = SignatureError::SignatureVerificationFailed {
297 details: "invalid RSA signature".to_string(),
298 };
299 assert!(err.to_string().contains("Signature verification failed"));
300 assert!(err.to_string().contains("invalid RSA signature"));
301 }
302
303 #[test]
304 fn test_certificate_extraction_failed_error_display() {
305 let err = SignatureError::CertificateExtractionFailed {
306 details: "no certificate found".to_string(),
307 };
308 assert!(err.to_string().contains("Certificate extraction failed"));
309 assert!(err.to_string().contains("no certificate found"));
310 }
311
312 #[test]
313 fn test_certificate_validation_failed_error_display() {
314 let err = SignatureError::CertificateValidationFailed {
315 details: "certificate expired".to_string(),
316 };
317 assert!(err.to_string().contains("Certificate validation failed"));
318 assert!(err.to_string().contains("certificate expired"));
319 }
320}