summaryrefslogtreecommitdiff
path: root/crates/typst-pdf/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/typst-pdf/src')
-rw-r--r--crates/typst-pdf/src/catalog.rs12
-rw-r--r--crates/typst-pdf/src/lib.rs4
-rw-r--r--crates/typst-pdf/src/page.rs3
-rw-r--r--crates/typst-pdf/src/signature.rs62
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,