Skip to main content

oxidize_pdf/forms/
signature_field.rs

1//! Digital signature fields implementation according to ISO 32000-1 Section 12.7.4.5
2//!
3//! This module provides signature field support including visual representation,
4//! signature metadata, and lock fields after signing.
5
6use crate::error::PdfError;
7use crate::graphics::Color;
8use crate::objects::{Dictionary, Object};
9use crate::text::Font;
10use chrono::{DateTime, Utc};
11
12/// Signature field for digital signatures
13#[derive(Debug, Clone)]
14pub struct SignatureField {
15    /// Field name (unique identifier)
16    pub name: String,
17    /// Signer information
18    pub signer: Option<SignerInfo>,
19    /// Signature value (placeholder for actual signature)
20    pub signature_value: Option<SignatureValue>,
21    /// Fields to lock after signing
22    pub lock_fields: Vec<String>,
23    /// Whether signature is required
24    pub required: bool,
25    /// Signature reason
26    pub reason: Option<String>,
27    /// Signature location
28    pub location: Option<String>,
29    /// Contact information
30    pub contact_info: Option<String>,
31    /// Visual appearance settings
32    pub appearance: SignatureAppearance,
33}
34
35/// Information about the signer
36#[derive(Debug, Clone)]
37pub struct SignerInfo {
38    /// Name of the signer
39    pub name: String,
40    /// Distinguished name (DN)
41    pub distinguished_name: Option<String>,
42    /// Email address
43    pub email: Option<String>,
44    /// Organization
45    pub organization: Option<String>,
46    /// Organizational unit
47    pub organizational_unit: Option<String>,
48}
49
50/// Signature value and metadata
51#[derive(Debug, Clone)]
52pub struct SignatureValue {
53    /// Timestamp of signature
54    pub timestamp: DateTime<Utc>,
55    /// Hash of the document
56    pub document_hash: Vec<u8>,
57    /// Signature algorithm
58    pub algorithm: SignatureAlgorithm,
59    /// Certificate chain (placeholder)
60    pub certificates: Vec<Certificate>,
61    /// Actual signature bytes (placeholder)
62    pub signature_bytes: Vec<u8>,
63}
64
65/// Signature algorithms
66#[derive(Debug, Clone, Copy, PartialEq)]
67pub enum SignatureAlgorithm {
68    /// RSA with SHA-256
69    RsaSha256,
70    /// RSA with SHA-384
71    RsaSha384,
72    /// RSA with SHA-512
73    RsaSha512,
74    /// ECDSA with SHA-256
75    EcdsaSha256,
76    /// DSA with SHA-256
77    DsaSha256,
78}
79
80/// Certificate placeholder
81#[derive(Debug, Clone)]
82pub struct Certificate {
83    /// Subject name
84    pub subject: String,
85    /// Issuer name
86    pub issuer: String,
87    /// Serial number
88    pub serial_number: String,
89    /// Not before date
90    pub not_before: DateTime<Utc>,
91    /// Not after date
92    pub not_after: DateTime<Utc>,
93    /// Public key info
94    pub public_key_info: String,
95}
96
97/// Visual appearance settings for signature field
98#[derive(Debug, Clone)]
99pub struct SignatureAppearance {
100    /// Show signer name
101    pub show_name: bool,
102    /// Show date/time
103    pub show_date: bool,
104    /// Show reason
105    pub show_reason: bool,
106    /// Show location
107    pub show_location: bool,
108    /// Show distinguished name
109    pub show_dn: bool,
110    /// Show labels
111    pub show_labels: bool,
112    /// Background color
113    pub background_color: Option<Color>,
114    /// Border color
115    pub border_color: Color,
116    /// Border width
117    pub border_width: f64,
118    /// Text color
119    pub text_color: Color,
120    /// Font for text
121    pub font: Font,
122    /// Font size
123    pub font_size: f64,
124    /// Custom logo/image
125    pub logo_data: Option<Vec<u8>>,
126}
127
128impl Default for SignatureAppearance {
129    fn default() -> Self {
130        Self {
131            show_name: true,
132            show_date: true,
133            show_reason: true,
134            show_location: false,
135            show_dn: false,
136            show_labels: true,
137            background_color: Some(Color::gray(0.95)),
138            border_color: Color::black(),
139            border_width: 1.0,
140            text_color: Color::black(),
141            font: Font::Helvetica,
142            font_size: 10.0,
143            logo_data: None,
144        }
145    }
146}
147
148impl SignatureField {
149    /// Create a new signature field
150    pub fn new(name: impl Into<String>) -> Self {
151        Self {
152            name: name.into(),
153            signer: None,
154            signature_value: None,
155            lock_fields: Vec::new(),
156            required: false,
157            reason: None,
158            location: None,
159            contact_info: None,
160            appearance: SignatureAppearance::default(),
161        }
162    }
163
164    /// Set the signer information
165    pub fn with_signer(mut self, signer: SignerInfo) -> Self {
166        self.signer = Some(signer);
167        self
168    }
169
170    /// Set the signature reason
171    pub fn with_reason(mut self, reason: impl Into<String>) -> Self {
172        self.reason = Some(reason.into());
173        self
174    }
175
176    /// Set the signature location
177    pub fn with_location(mut self, location: impl Into<String>) -> Self {
178        self.location = Some(location.into());
179        self
180    }
181
182    /// Set contact information
183    pub fn with_contact(mut self, contact: impl Into<String>) -> Self {
184        self.contact_info = Some(contact.into());
185        self
186    }
187
188    /// Add fields to lock after signing
189    pub fn lock_fields_after_signing(mut self, fields: Vec<String>) -> Self {
190        self.lock_fields = fields;
191        self
192    }
193
194    /// Mark field as required
195    pub fn required(mut self) -> Self {
196        self.required = true;
197        self
198    }
199
200    /// Customize appearance
201    pub fn with_appearance(mut self, appearance: SignatureAppearance) -> Self {
202        self.appearance = appearance;
203        self
204    }
205
206    /// Check if field is signed
207    pub fn is_signed(&self) -> bool {
208        self.signature_value.is_some()
209    }
210
211    /// Sign the field (placeholder implementation)
212    pub fn sign(&mut self, signer: SignerInfo, reason: Option<String>) -> Result<(), PdfError> {
213        if self.is_signed() {
214            return Err(PdfError::InvalidOperation(
215                "Field is already signed".to_string(),
216            ));
217        }
218
219        // Create signature value (placeholder)
220        let signature_value = SignatureValue {
221            timestamp: Utc::now(),
222            document_hash: vec![0; 32], // Placeholder hash
223            algorithm: SignatureAlgorithm::RsaSha256,
224            certificates: vec![],
225            signature_bytes: vec![0; 256], // Placeholder signature
226        };
227
228        self.signer = Some(signer);
229        if let Some(r) = reason {
230            self.reason = Some(r);
231        }
232        self.signature_value = Some(signature_value);
233
234        Ok(())
235    }
236
237    /// Verify signature (placeholder implementation)
238    pub fn verify(&self) -> Result<bool, PdfError> {
239        if !self.is_signed() {
240            return Ok(false);
241        }
242
243        // Placeholder verification - always returns true for now
244        // In a real implementation, this would verify the signature
245        // against the document hash and certificate chain
246        Ok(true)
247    }
248
249    /// Generate appearance stream for the signature field
250    pub fn generate_appearance(&self, width: f64, height: f64) -> Result<Vec<u8>, PdfError> {
251        let mut stream = Vec::new();
252
253        // Background
254        if let Some(bg_color) = self.appearance.background_color {
255            match bg_color {
256                Color::Rgb(r, g, b) => {
257                    stream.extend(format!("{} {} {} rg\n", r, g, b).as_bytes());
258                }
259                Color::Gray(v) => {
260                    stream.extend(format!("{} g\n", v).as_bytes());
261                }
262                Color::Cmyk(c, m, y, k) => {
263                    stream.extend(format!("{} {} {} {} k\n", c, m, y, k).as_bytes());
264                }
265            }
266            stream.extend(format!("0 0 {} {} re f\n", width, height).as_bytes());
267        }
268
269        // Border
270        match self.appearance.border_color {
271            Color::Rgb(r, g, b) => {
272                stream.extend(format!("{} {} {} RG\n", r, g, b).as_bytes());
273            }
274            Color::Gray(v) => {
275                stream.extend(format!("{} G\n", v).as_bytes());
276            }
277            Color::Cmyk(c, m, y, k) => {
278                stream.extend(format!("{} {} {} {} K\n", c, m, y, k).as_bytes());
279            }
280        }
281        stream.extend(format!("{} w\n", self.appearance.border_width).as_bytes());
282        stream.extend(format!("0 0 {} {} re S\n", width, height).as_bytes());
283
284        // Text content
285        stream.extend(b"BT\n");
286        stream.extend(
287            format!(
288                "/{} {} Tf\n",
289                self.appearance.font.pdf_name(),
290                self.appearance.font_size
291            )
292            .as_bytes(),
293        );
294        match self.appearance.text_color {
295            Color::Rgb(r, g, b) => {
296                stream.extend(format!("{} {} {} rg\n", r, g, b).as_bytes());
297            }
298            Color::Gray(v) => {
299                stream.extend(format!("{} g\n", v).as_bytes());
300            }
301            Color::Cmyk(c, m, y, k) => {
302                stream.extend(format!("{} {} {} {} k\n", c, m, y, k).as_bytes());
303            }
304        }
305
306        let mut y_pos = height - self.appearance.font_size - 5.0;
307        let x_pos = 5.0;
308
309        if self.is_signed() {
310            // Signed appearance
311            if let Some(ref signer) = self.signer {
312                if self.appearance.show_name {
313                    let label = if self.appearance.show_labels {
314                        "Digitally signed by: "
315                    } else {
316                        ""
317                    };
318                    stream.extend(format!("{} {} Td\n", x_pos, y_pos).as_bytes());
319                    stream.extend(format!("({}{}) Tj\n", label, signer.name).as_bytes());
320                    y_pos -= self.appearance.font_size + 2.0;
321                }
322
323                if self.appearance.show_dn {
324                    if let Some(ref dn) = signer.distinguished_name {
325                        stream.extend(format!("{} {} Td\n", x_pos, y_pos).as_bytes());
326                        stream.extend(format!("(DN: {}) Tj\n", dn).as_bytes());
327                        y_pos -= self.appearance.font_size + 2.0;
328                    }
329                }
330            }
331
332            if self.appearance.show_date {
333                if let Some(ref sig_value) = self.signature_value {
334                    let label = if self.appearance.show_labels {
335                        "Date: "
336                    } else {
337                        ""
338                    };
339                    let date_str = sig_value
340                        .timestamp
341                        .format("%Y-%m-%d %H:%M:%S UTC")
342                        .to_string();
343                    stream.extend(format!("{} {} Td\n", x_pos, y_pos).as_bytes());
344                    stream.extend(format!("({}{}) Tj\n", label, date_str).as_bytes());
345                    y_pos -= self.appearance.font_size + 2.0;
346                }
347            }
348
349            if self.appearance.show_reason {
350                if let Some(ref reason) = self.reason {
351                    let label = if self.appearance.show_labels {
352                        "Reason: "
353                    } else {
354                        ""
355                    };
356                    stream.extend(format!("{} {} Td\n", x_pos, y_pos).as_bytes());
357                    stream.extend(format!("({}{}) Tj\n", label, reason).as_bytes());
358                    y_pos -= self.appearance.font_size + 2.0;
359                }
360            }
361
362            if self.appearance.show_location {
363                if let Some(ref location) = self.location {
364                    let label = if self.appearance.show_labels {
365                        "Location: "
366                    } else {
367                        ""
368                    };
369                    stream.extend(format!("{} {} Td\n", x_pos, y_pos).as_bytes());
370                    stream.extend(format!("({}{}) Tj\n", label, location).as_bytes());
371                }
372            }
373        } else {
374            // Unsigned appearance - show placeholder
375            stream.extend(format!("{} {} Td\n", x_pos, y_pos).as_bytes());
376            stream.extend(b"(Click to sign) Tj\n");
377        }
378
379        stream.extend(b"ET\n");
380
381        Ok(stream)
382    }
383
384    /// Convert to PDF dictionary
385    pub fn to_dict(&self) -> Dictionary {
386        let mut dict = Dictionary::new();
387
388        dict.set("Type", Object::Name("Annot".to_string()));
389        dict.set("Subtype", Object::Name("Widget".to_string()));
390        dict.set("FT", Object::Name("Sig".to_string()));
391        dict.set("T", Object::String(self.name.clone()));
392
393        // Field flags
394        let mut flags = 0;
395        if self.required {
396            flags |= 2; // Required flag
397        }
398        dict.set("Ff", Object::Integer(flags));
399
400        // Signature dictionary
401        if self.is_signed() {
402            let mut sig_dict = Dictionary::new();
403            sig_dict.set("Type", Object::Name("Sig".to_string()));
404
405            if let Some(ref signer) = self.signer {
406                sig_dict.set("Name", Object::String(signer.name.clone()));
407                if let Some(ref email) = signer.email {
408                    sig_dict.set("ContactInfo", Object::String(email.clone()));
409                }
410            }
411
412            if let Some(ref reason) = self.reason {
413                sig_dict.set("Reason", Object::String(reason.clone()));
414            }
415
416            if let Some(ref location) = self.location {
417                sig_dict.set("Location", Object::String(location.clone()));
418            }
419
420            if let Some(ref sig_value) = self.signature_value {
421                sig_dict.set(
422                    "M",
423                    Object::String(sig_value.timestamp.format("%Y%m%d%H%M%S%z").to_string()),
424                );
425            }
426
427            dict.set("V", Object::Dictionary(sig_dict));
428        }
429
430        // Lock dictionary for fields to lock after signing
431        if !self.lock_fields.is_empty() {
432            let mut lock_dict = Dictionary::new();
433            lock_dict.set("Type", Object::Name("SigFieldLock".to_string()));
434
435            let fields: Vec<Object> = self
436                .lock_fields
437                .iter()
438                .map(|f| Object::String(f.clone()))
439                .collect();
440            lock_dict.set("Fields", Object::Array(fields));
441
442            dict.set("Lock", Object::Dictionary(lock_dict));
443        }
444
445        dict
446    }
447}
448
449impl SignerInfo {
450    /// Create new signer info
451    pub fn new(name: impl Into<String>) -> Self {
452        Self {
453            name: name.into(),
454            distinguished_name: None,
455            email: None,
456            organization: None,
457            organizational_unit: None,
458        }
459    }
460
461    /// Set email
462    pub fn with_email(mut self, email: impl Into<String>) -> Self {
463        self.email = Some(email.into());
464        self
465    }
466
467    /// Set organization
468    pub fn with_organization(mut self, org: impl Into<String>) -> Self {
469        self.organization = Some(org.into());
470        self
471    }
472
473    /// Build distinguished name
474    pub fn build_dn(&mut self) {
475        let mut dn_parts = vec![format!("CN={}", self.name)];
476
477        if let Some(ref email) = self.email {
478            dn_parts.push(format!("emailAddress={}", email));
479        }
480
481        if let Some(ref org) = self.organization {
482            dn_parts.push(format!("O={}", org));
483        }
484
485        if let Some(ref ou) = self.organizational_unit {
486            dn_parts.push(format!("OU={}", ou));
487        }
488
489        self.distinguished_name = Some(dn_parts.join(", "));
490    }
491}
492
493#[cfg(test)]
494mod tests {
495    use super::*;
496
497    #[test]
498    fn test_signature_field_creation() {
499        let field = SignatureField::new("sig1");
500        assert_eq!(field.name, "sig1");
501        assert!(!field.is_signed());
502        assert!(!field.required);
503    }
504
505    #[test]
506    fn test_signer_info() {
507        let mut signer = SignerInfo::new("John Doe")
508            .with_email("john@example.com")
509            .with_organization("ACME Corp");
510
511        signer.build_dn();
512        assert!(signer.distinguished_name.is_some());
513        assert!(signer.distinguished_name.unwrap().contains("CN=John Doe"));
514    }
515
516    #[test]
517    fn test_sign_field() {
518        let mut field = SignatureField::new("sig1");
519        let signer = SignerInfo::new("Jane Smith");
520
521        assert!(field
522            .sign(signer.clone(), Some("Approval".to_string()))
523            .is_ok());
524        assert!(field.is_signed());
525        assert_eq!(field.reason, Some("Approval".to_string()));
526
527        // Cannot sign twice
528        assert!(field.sign(signer, None).is_err());
529    }
530
531    #[test]
532    fn test_signature_appearance() {
533        let field = SignatureField::new("sig1");
534        let appearance = field.generate_appearance(200.0, 50.0);
535
536        assert!(appearance.is_ok());
537        let stream = appearance.unwrap();
538        assert!(!stream.is_empty());
539    }
540
541    #[test]
542    fn test_lock_fields() {
543        let field = SignatureField::new("sig1")
544            .lock_fields_after_signing(vec!["field1".to_string(), "field2".to_string()]);
545
546        assert_eq!(field.lock_fields.len(), 2);
547    }
548
549    #[test]
550    fn test_required_field() {
551        let field = SignatureField::new("sig1").required();
552        assert!(field.required);
553
554        let dict = field.to_dict();
555        // Check that required flag is set
556        if let Some(Object::Integer(flags)) = dict.get("Ff") {
557            assert_eq!(flags & 2, 2);
558        }
559    }
560}