From a471807e2ef67f0d24d18ab153a941f2cd213f2b Mon Sep 17 00:00:00 2001 From: Ana Gelez Date: Wed, 14 Aug 2024 09:10:12 +0200 Subject: [PATCH] More stuff Still not working :( --- Cargo.toml | 2 +- crates/typst-pdf/src/catalog.rs | 12 +++++- crates/typst-pdf/src/lib.rs | 4 ++ crates/typst-pdf/src/page.rs | 3 +- crates/typst-pdf/src/signature.rs | 64 ++++++++++++++++++++++++++----- 5 files changed, 72 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 474e56c6e..957256c71 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -99,7 +99,7 @@ semver = "1" serde = { version = "1.0.184", features = ["derive"] } serde_json = "1" serde_yaml = "0.9" -sha2 = { version = "0.10.8", featurs = ["oid"] } +sha2 = { version = "0.10.8", features = ["oid"] } shell-escape = "0.1.5" siphasher = "1" smallvec = { version = "1.11.1", features = ["union", "const_generics", "const_new"] } diff --git a/crates/typst-pdf/src/catalog.rs b/crates/typst-pdf/src/catalog.rs index 517d9ee18..07ac6e138 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 ea8f6648f..1da8ffaa5 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 1001d8992..a84fa474f 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 266d028a5..125c79b67 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, Ref) { - let form_ref = alloc.bump(); - let signature_field_ref = alloc.bump(); +pub fn alloc_signature_annotation(_: &WithGlobalRefs) -> (PdfChunk, Ref) { + let mut chunk = PdfChunk::new(); + let r = chunk.alloc(); + (chunk, r) +} - let mut signature_field = pdf.indirect(signature_field_ref).dict(); +pub fn prepare( + alloc: &mut Ref, + pdf: &mut Pdf, + signature_annotation_ref: Ref, + last_page_ref: Ref, +) -> (Range, Ref) { + let form_ref = alloc.bump(); + let field_lock_ref = alloc.bump(); + + 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, 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, mut bytes: Vec) -> Vec { let doc_size_range = doc_size_start..(doc_size_start + needle.len()); dbg!(&range, &doc_size_range); let mut actual_size = Vec::new(); - ::write(bytes.len() as i32, &mut actual_size); + ::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,