diff options
Diffstat (limited to 'crates/typst-pdf')
| -rw-r--r-- | crates/typst-pdf/src/catalog.rs | 12 | ||||
| -rw-r--r-- | crates/typst-pdf/src/lib.rs | 4 | ||||
| -rw-r--r-- | crates/typst-pdf/src/page.rs | 3 | ||||
| -rw-r--r-- | crates/typst-pdf/src/signature.rs | 62 |
4 files changed, 70 insertions, 11 deletions
diff --git a/crates/typst-pdf/src/catalog.rs b/crates/typst-pdf/src/catalog.rs index 517d9ee1..07ac6e13 100644 --- a/crates/typst-pdf/src/catalog.rs +++ b/crates/typst-pdf/src/catalog.rs @@ -133,7 +133,17 @@ pub fn write_catalog( .pair(Name(b"Subtype"), Name(b"XML")); // Prepare digital signatures - let (signature_range, signature_form_ref) = signature::prepare(alloc, &mut pdf); + let (signature_range, signature_form_ref) = signature::prepare( + alloc, + &mut pdf, + ctx.references.signature_annotation, + ctx.globals + .pages + .iter() + .filter_map(|p| *p) + .last() + .expect("Can't sign a doc with no pages"), + ); // Write the document catalog. let catalog_ref = alloc.bump(); diff --git a/crates/typst-pdf/src/lib.rs b/crates/typst-pdf/src/lib.rs index ea8f6648..1da8ffaa 100644 --- a/crates/typst-pdf/src/lib.rs +++ b/crates/typst-pdf/src/lib.rs @@ -21,6 +21,7 @@ use std::ops::{Deref, DerefMut}; use base64::Engine; use pdf_writer::{Chunk, Pdf, Ref}; +use signature::alloc_signature_annotation; use typst::foundations::{Datetime, Smart}; use typst::layout::{Abs, Em, PageRanges, Transform}; use typst::model::Document; @@ -79,6 +80,7 @@ pub fn pdf( resources: builder.run(alloc_resources_refs), }) .phase(|builder| References { + signature_annotation: builder.run(alloc_signature_annotation), named_destinations: builder.run(write_named_destinations), fonts: builder.run(write_fonts), color_fonts: builder.run(write_color_fonts), @@ -207,6 +209,8 @@ impl<'a> From<(WithResources<'a>, GlobalRefs)> for WithGlobalRefs<'a> { /// The references that have been assigned to each object. struct References { + /// Reference for the digital signature annotation + signature_annotation: Ref, /// List of named destinations, each with an ID. named_destinations: NamedDestinations, /// The IDs of written fonts. diff --git a/crates/typst-pdf/src/page.rs b/crates/typst-pdf/src/page.rs index 1001d899..a84fa474 100644 --- a/crates/typst-pdf/src/page.rs +++ b/crates/typst-pdf/src/page.rs @@ -112,7 +112,8 @@ fn write_page( return; }; - let mut annotations = Vec::with_capacity(page.content.links.len()); + let mut annotations = Vec::with_capacity(page.content.links.len() + 1); + annotations.push(ctx.references.signature_annotation); for (dest, rect) in &page.content.links { let id = chunk.alloc(); annotations.push(id); diff --git a/crates/typst-pdf/src/signature.rs b/crates/typst-pdf/src/signature.rs index 266d028a..125c79b6 100644 --- a/crates/typst-pdf/src/signature.rs +++ b/crates/typst-pdf/src/signature.rs @@ -25,26 +25,54 @@ use cms::{ }, }; use pdf_writer::{ - types::SigFlags, writers::Form, Finish, Name, Pdf, Primitive, Ref, Str, + types::SigFlags, writers::Form, Date, Finish, Name, Pdf, Primitive, Ref, Str, }; use rsa::{traits::SignatureScheme, Pkcs1v15Sign, RsaPrivateKey}; use sha2::Sha512; +use crate::{PdfChunk, WithGlobalRefs}; + const SIG_SIZE: usize = 1024 * 4; -pub fn prepare(alloc: &mut Ref, pdf: &mut Pdf) -> (Range<usize>, Ref) { +pub fn alloc_signature_annotation(_: &WithGlobalRefs) -> (PdfChunk, Ref) { + let mut chunk = PdfChunk::new(); + let r = chunk.alloc(); + (chunk, r) +} + +pub fn prepare( + alloc: &mut Ref, + pdf: &mut Pdf, + signature_annotation_ref: Ref, + last_page_ref: Ref, +) -> (Range<usize>, Ref) { let form_ref = alloc.bump(); - let signature_field_ref = alloc.bump(); + let field_lock_ref = alloc.bump(); - let mut signature_field = pdf.indirect(signature_field_ref).dict(); + let mut lock = pdf.indirect(field_lock_ref).dict(); + lock.pair(Name(b"Type"), Name(b"SigFieldLock")); + lock.pair(Name(b"Action"), Name(b"All")); + lock.finish(); + + let mut signature_field = pdf.indirect(signature_annotation_ref).dict(); signature_field.pair(Name(b"Type"), Name(b"Annot")); signature_field.pair(Name(b"Subtype"), Name(b"Widget")); - signature_field.pair(Name(b"FieldType"), Name(b"Sig")); - signature_field.insert(Name(b"Rect")).array().items([0, 0, 0, 0]); + signature_field.pair(Name(b"FT"), Name(b"Sig")); + signature_field.pair(Name(b"F"), 132); + signature_field.pair(Name(b"T"), Str(b"Signature")); + signature_field.pair(Name(b"P"), last_page_ref); + signature_field.pair(Name(b"Lock"), field_lock_ref); + signature_field + .insert(Name(b"Rect")) + .array() + .items([0.0, 0.0, 0.0, 0.0]); let mut signature_dict = signature_field.insert(Name(b"V")).dict(); signature_dict.pair(Name(b"Type"), Name(b"Sig")); signature_dict.pair(Name(b"Filter"), Name(b"Adobe.PPKLite")); signature_dict.pair(Name(b"SubFilter"), Name(b"adbe.pkcs7.detached")); + signature_dict.pair(Name(b"Name"), Str(b"Ana Gelez")); + signature_dict + .pair(Name(b"M"), Date::new(2024).month(08).day(12).hour(15).minute(55)); let mut placeholder = [0; SIG_SIZE]; placeholder[0] = 255; // Make sure pdf-writer writes this array as binary let sig_end = signature_dict @@ -58,12 +86,25 @@ pub fn prepare(alloc: &mut Ref, pdf: &mut Pdf) -> (Range<usize>, Ref) { .array() .items([0, sig_start as i32, sig_end as i32]) .item(Str(b"typst-document-size")); + + let mut sig_refs = signature_dict.insert(Name(b"Reference")).array(); + let mut sig_ref = sig_refs.push().dict(); + sig_ref.pair(Name(b"Type"), Name(b"SigRef")); + sig_ref.pair(Name(b"TransformMethod"), Name(b"DocMDP")); + let mut params = sig_ref.insert(Name(b"TransformParams")).dict(); + params.pair(Name(b"Type"), Name(b"TransformParams")); + params.pair(Name(b"P"), 1); + params.finish(); + sig_ref.pair(Name(b"DigestMethod"), Name(b"SHA1")); + sig_ref.finish(); + sig_refs.finish(); + signature_dict.finish(); signature_field.finish(); let mut form: Form = pdf.indirect(form_ref).start(); - form.fields([signature_field_ref]); - form.sig_flags(SigFlags::SIGNATURES_EXIST); + form.fields([signature_annotation_ref]); + form.sig_flags(SigFlags::SIGNATURES_EXIST | SigFlags::APPEND_ONLY); (sig_start..sig_end, form_ref) } @@ -77,7 +118,10 @@ pub fn write(range: Range<usize>, mut bytes: Vec<u8>) -> Vec<u8> { let doc_size_range = doc_size_start..(doc_size_start + needle.len()); dbg!(&range, &doc_size_range); let mut actual_size = Vec::new(); - <i32 as pdf_writer::Primitive>::write(bytes.len() as i32, &mut actual_size); + <i32 as pdf_writer::Primitive>::write( + (bytes.len() - range.end) as i32, + &mut actual_size, + ); actual_size.extend(std::iter::repeat(b' ').take(needle.len() - actual_size.len())); bytes.splice( doc_size_range.start + range.end..doc_size_range.end + range.end, |
