WIP: digital signatures

This commit is contained in:
Ana Gelez 2024-08-08 15:11:15 +02:00
parent c4dd6fa062
commit 28905fedc1
6 changed files with 565 additions and 11 deletions

369
Cargo.lock generated
View File

@ -8,6 +8,17 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "aes"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
]
[[package]]
name = "ahash"
version = "0.8.11"
@ -141,6 +152,12 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "base64ct"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]]
name = "biblatex"
version = "0.9.3"
@ -205,6 +222,24 @@ dependencies = [
"wyz",
]
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "block-padding"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93"
dependencies = [
"generic-array",
]
[[package]]
name = "bumpalo"
version = "3.16.0"
@ -229,6 +264,15 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cbc"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6"
dependencies = [
"cipher",
]
[[package]]
name = "cc"
version = "1.0.106"
@ -303,6 +347,16 @@ dependencies = [
"half",
]
[[package]]
name = "cipher"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
dependencies = [
"crypto-common",
"inout",
]
[[package]]
name = "citationberg"
version = "0.3.1"
@ -372,6 +426,18 @@ dependencies = [
"roff",
]
[[package]]
name = "cms"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b77c319abfd5219629c45c34c89ba945ed3c5e49fcde9d16b6c3885f118a730"
dependencies = [
"const-oid",
"der",
"spki",
"x509-cert",
]
[[package]]
name = "cobs"
version = "0.2.3"
@ -423,6 +489,12 @@ dependencies = [
"syn",
]
[[package]]
name = "const-oid"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
[[package]]
name = "core-foundation"
version = "0.9.4"
@ -448,6 +520,15 @@ dependencies = [
"libm",
]
[[package]]
name = "cpufeatures"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
dependencies = [
"libc",
]
[[package]]
name = "crc32fast"
version = "1.4.2"
@ -497,6 +578,16 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "csv"
version = "1.3.0"
@ -524,6 +615,30 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a"
[[package]]
name = "der"
version = "0.7.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0"
dependencies = [
"const-oid",
"der_derive",
"flagset",
"pem-rfc7468",
"zeroize",
]
[[package]]
name = "der_derive"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "deranged"
version = "0.3.11"
@ -544,6 +659,18 @@ dependencies = [
"syn",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"const-oid",
"crypto-common",
"subtle",
]
[[package]]
name = "dirs"
version = "5.0.1"
@ -701,6 +828,12 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "flagset"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3ea1ec5f8307826a5b71094dd91fc04d4ae75d5709b20ad351c7fb4815c86ec"
[[package]]
name = "flate2"
version = "1.0.30"
@ -791,6 +924,16 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getopts"
version = "0.2.21"
@ -873,6 +1016,15 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hmac"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
dependencies = [
"digest",
]
[[package]]
name = "hypher"
version = "0.1.5"
@ -1136,6 +1288,16 @@ dependencies = [
"libc",
]
[[package]]
name = "inout"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
dependencies = [
"block-padding",
"generic-array",
]
[[package]]
name = "instant"
version = "0.1.13"
@ -1239,6 +1401,15 @@ dependencies = [
"smallvec",
]
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
dependencies = [
"spin",
]
[[package]]
name = "libc"
version = "0.2.155"
@ -1449,6 +1620,23 @@ dependencies = [
"num-traits",
]
[[package]]
name = "num-bigint-dig"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151"
dependencies = [
"byteorder",
"lazy_static",
"libm",
"num-integer",
"num-iter",
"num-traits",
"rand",
"smallvec",
"zeroize",
]
[[package]]
name = "num-conv"
version = "0.1.0"
@ -1475,6 +1663,17 @@ dependencies = [
"num-traits",
]
[[package]]
name = "num-iter"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.19"
@ -1482,6 +1681,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
"libm",
]
[[package]]
@ -1647,11 +1847,19 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
[[package]]
name = "pbkdf2"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
dependencies = [
"digest",
"hmac",
]
[[package]]
name = "pdf-writer"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af6a7882fda7808481d43c51cadfc3ec934c6af72612a1fe6985ce329a2f0469"
dependencies = [
"bitflags 2.6.0",
"itoa",
@ -1659,6 +1867,15 @@ dependencies = [
"ryu",
]
[[package]]
name = "pem-rfc7468"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
dependencies = [
"base64ct",
]
[[package]]
name = "percent-encoding"
version = "2.3.1"
@ -1722,6 +1939,44 @@ dependencies = [
"ttf-parser",
]
[[package]]
name = "pkcs1"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f"
dependencies = [
"der",
"pkcs8",
"spki",
]
[[package]]
name = "pkcs5"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e847e2c91a18bfa887dd028ec33f2fe6f25db77db3619024764914affe8b69a6"
dependencies = [
"aes",
"cbc",
"der",
"pbkdf2",
"scrypt",
"sha2",
"spki",
]
[[package]]
name = "pkcs8"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
dependencies = [
"der",
"pkcs5",
"rand_core",
"spki",
]
[[package]]
name = "pkg-config"
version = "0.3.30"
@ -1859,6 +2114,8 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
@ -1877,6 +2134,9 @@ name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "rayon"
@ -1999,6 +2259,26 @@ version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
[[package]]
name = "rsa"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc"
dependencies = [
"const-oid",
"digest",
"num-bigint-dig",
"num-integer",
"num-traits",
"pkcs1",
"pkcs8",
"rand_core",
"signature",
"spki",
"subtle",
"zeroize",
]
[[package]]
name = "rustc-hash"
version = "1.1.0"
@ -2055,6 +2335,15 @@ version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "salsa20"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213"
dependencies = [
"cipher",
]
[[package]]
name = "same-file"
version = "1.0.6"
@ -2079,6 +2368,17 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "scrypt"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f"
dependencies = [
"pbkdf2",
"salsa20",
"sha2",
]
[[package]]
name = "security-framework"
version = "2.11.0"
@ -2184,12 +2484,33 @@ dependencies = [
"unsafe-libyaml",
]
[[package]]
name = "sha2"
version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "shell-escape"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f"
[[package]]
name = "signature"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
dependencies = [
"digest",
"rand_core",
]
[[package]]
name = "simd-adler32"
version = "0.3.7"
@ -2238,6 +2559,16 @@ version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]]
name = "spki"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
dependencies = [
"base64ct",
"der",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
@ -2310,6 +2641,12 @@ name = "subsetter"
version = "0.11.0"
source = "git+https://github.com/typst/subsetter?rev=4e0058b#4e0058b4b9a0948a5f79894111948d95e59ba350"
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "svg2pdf"
version = "0.11.0"
@ -2588,6 +2925,12 @@ version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
[[package]]
name = "typenum"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "typst"
version = "0.11.0"
@ -2777,6 +3120,7 @@ dependencies = [
"arrayvec",
"base64",
"bytemuck",
"cms",
"comemo",
"ecow",
"image",
@ -2784,6 +3128,10 @@ dependencies = [
"miniz_oxide",
"once_cell",
"pdf-writer",
"pkcs8",
"rand",
"rsa",
"sha2",
"subsetter",
"svg2pdf",
"ttf-parser",
@ -3406,6 +3754,17 @@ dependencies = [
"tap",
]
[[package]]
name = "x509-cert"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94"
dependencies = [
"const-oid",
"der",
"spki",
]
[[package]]
name = "xattr"
version = "1.3.1"
@ -3528,6 +3887,12 @@ dependencies = [
"synstructure",
]
[[package]]
name = "zeroize"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
[[package]]
name = "zerotrie"
version = "0.1.3"

View File

@ -39,6 +39,7 @@ ciborium = "0.2.1"
clap = { version = "4.4", features = ["derive", "env"] }
clap_complete = "4.2.1"
clap_mangen = "0.2.10"
cms = "0.2.3"
ctrlc = "3.4.1"
codespan-reporting = "0.11"
comemo = "0.4"
@ -78,16 +79,19 @@ pathdiff = "0.2"
pdf-writer = "0.10.0"
phf = { version = "0.11", features = ["macros"] }
pixglyph = "0.4"
pkcs8 = { version = "0.10.2", features = ["pkcs5", "encryption"] }
png = "0.17"
portable-atomic = "1.6"
proc-macro2 = "1"
pulldown-cmark = "0.9"
quote = "1"
qcms = "0.3.0"
rand = "0.8.5"
rayon = "1.7.0"
regex = "1"
resvg = { version = "0.42", default-features = false, features = ["raster-images"] }
roxmltree = "0.20"
rsa = "0.9.6"
rustybuzz = "0.14"
same-file = "1"
self-replace = "1.3.7"
@ -95,6 +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"] }
shell-escape = "0.1.5"
siphasher = "1"
smallvec = { version = "1.11.1", features = ["union", "const_generics", "const_new"] }
@ -126,6 +131,7 @@ xmlparser = "0.13.5"
xmlwriter = "0.1.0"
xmp-writer = "0.2"
xz2 = { version = "0.1", features = ["static"] }
x509-cert = "0.2.5"
yaml-front-matter = "0.1"
zip = { version = "2", default-features = false, features = ["deflate"] }
@ -142,3 +148,6 @@ strip = true
[workspace.lints.clippy]
uninlined_format_args = "warn"
blocks_in_conditions = "allow"
[patch.crates-io]
pdf-writer = { path = "../pdf-writer" }

View File

@ -17,8 +17,10 @@ typst = { workspace = true }
typst-assets = { workspace = true }
typst-macros = { workspace = true }
typst-timing = { workspace = true }
arrayvec = { workspace = true }
base64 = { workspace = true }
bytemuck = { workspace = true }
cms = { workspace = true }
comemo = { workspace = true }
ecow = { workspace = true }
image = { workspace = true }
@ -26,7 +28,10 @@ indexmap = { workspace = true }
miniz_oxide = { workspace = true }
once_cell = { workspace = true }
pdf-writer = { workspace = true }
arrayvec = { workspace = true }
pkcs8 = { workspace = true, features = ["pkcs5", "encryption"] }
rand = { workspace = true }
rsa = { workspace = true }
sha2 = { workspace = true, features = ["oid"] }
subsetter = { workspace = true }
svg2pdf = { workspace = true }
ttf-parser = { workspace = true }

View File

@ -10,17 +10,17 @@ use typst::foundations::{Datetime, Smart};
use typst::layout::Dir;
use typst::text::Lang;
use crate::WithEverything;
use crate::{hash_base64, outline, page::PdfPageLabel};
use crate::{signature, WithEverything};
/// Write the document catalog.
pub fn write_catalog(
ctx: WithEverything,
ident: Smart<&str>,
timestamp: Option<Datetime>,
pdf: &mut Pdf,
mut pdf: Pdf,
alloc: &mut Ref,
) {
) -> Vec<u8> {
let lang = ctx
.resources
.languages
@ -35,10 +35,10 @@ pub fn write_catalog(
};
// Write the outline tree.
let outline_root_id = outline::write_outline(pdf, alloc, &ctx);
let outline_root_id = outline::write_outline(&mut pdf, alloc, &ctx);
// Write the page labels.
let page_labels = write_page_labels(pdf, alloc, &ctx);
let page_labels = write_page_labels(&mut pdf, alloc, &ctx);
// Write the document information.
let info_ref = alloc.bump();
@ -132,6 +132,9 @@ pub fn write_catalog(
.pair(Name(b"Type"), Name(b"Metadata"))
.pair(Name(b"Subtype"), Name(b"XML"));
// Prepare digital signatures
let (signature_range, signature_form_ref) = signature::prepare(alloc, &mut pdf);
// Write the document catalog.
let catalog_ref = alloc.bump();
let mut catalog = pdf.catalog(catalog_ref);
@ -167,7 +170,11 @@ pub fn write_catalog(
catalog.lang(TextStr(lang.as_str()));
}
catalog.insert(Name(b"AcroForm")).primitive(signature_form_ref);
catalog.finish();
signature::write(signature_range, pdf.finish())
}
/// Write the page labels.

View File

@ -13,6 +13,7 @@ mod outline;
mod page;
mod pattern;
mod resources;
mod signature;
use std::collections::HashMap;
use std::hash::Hash;
@ -348,10 +349,9 @@ impl<S> PdfBuilder<S> {
process: P,
) -> Vec<u8>
where
P: Fn(S, Smart<&str>, Option<Datetime>, &mut Pdf, &mut Ref),
P: Fn(S, Smart<&str>, Option<Datetime>, Pdf, &mut Ref) -> Vec<u8>,
{
process(self.state, ident, timestamp, &mut self.pdf, &mut self.alloc);
self.pdf.finish()
process(self.state, ident, timestamp, self.pdf, &mut self.alloc)
}
}

View File

@ -0,0 +1,168 @@
use pkcs8::DecodePrivateKey;
use sha2::{
digest::const_oid::db::rfc5912::{ID_SHA_512, SHA_512_WITH_RSA_ENCRYPTION},
Digest,
};
use std::ops::Range;
use cms::{
cert::{
x509::{
der::{
asn1::{OctetString, SetOfVec},
oid::db::rfc5911::{ID_DATA, ID_SIGNED_DATA},
Any, AnyRef, Encode,
},
spki::AlgorithmIdentifier,
Certificate,
},
CertificateChoices, IssuerAndSerialNumber,
},
content_info::{CmsVersion, ContentInfo},
signed_data::{
CertificateSet, EncapsulatedContentInfo, SignedData, SignerIdentifier,
SignerInfo, SignerInfos,
},
};
use pdf_writer::{
types::{FieldType, SigFlags},
writers::{Field, Form},
Finish, Name, Pdf, Primitive, Ref, Str,
};
use rsa::{traits::SignatureScheme, Pkcs1v15Sign, RsaPrivateKey};
use sha2::Sha512;
const SIG_SIZE: usize = 1024 * 4;
pub fn prepare(alloc: &mut Ref, pdf: &mut Pdf) -> (Range<usize>, Ref) {
let form_ref = alloc.bump();
let signature_field_ref = alloc.bump();
let mut signature_field: Field = pdf.indirect(signature_field_ref).start();
signature_field.field_type(FieldType::Signature);
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"));
let mut placeholder = [0; SIG_SIZE];
placeholder[0] = 255; // Make sure pdf-writer writes this array as binary
let sig_end = signature_dict
.pair(Name(b"Contents"), Str(&placeholder))
.current_len();
let sig_start = sig_end
- SIG_SIZE * 2 // 2 chars to write each byte
- 2; // take < and > into account;
signature_dict
.insert(Name(b"ByteRange"))
.array()
.items([0, sig_start as i32, sig_end as i32])
.item(Str(b"typst-document-size"));
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);
(sig_start..sig_end, form_ref)
}
pub fn write(range: Range<usize>, mut bytes: Vec<u8>) -> Vec<u8> {
let needle = b"(typst-document-size)";
let doc_size_start = bytes[range.end..]
.windows(needle.len())
.position(|x| x == needle)
.unwrap();
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);
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,
actual_size,
);
let mut hasher = Sha512::new();
hasher.update(&bytes[0..range.start]);
hasher.update(&bytes[range.end..]);
let hashed = hasher.finalize();
let priv_key =
RsaPrivateKey::from_pkcs8_encrypted_pem(include_str!("../../../key.pem"), "abcd")
.unwrap();
let signer = Pkcs1v15Sign::new::<Sha512>();
let signature =
signer.sign(Some(&mut rand::rngs::OsRng), &priv_key, &hashed).unwrap();
let pem_chain =
Certificate::load_pem_chain(include_bytes!("../../../cert.pem")).unwrap();
let sig_data = ContentInfo {
content_type: ID_SIGNED_DATA,
content: Any::from(
AnyRef::try_from(
SignedData {
version: CmsVersion::V0,
digest_algorithms: SetOfVec::try_from(vec![AlgorithmIdentifier {
oid: SHA_512_WITH_RSA_ENCRYPTION,
parameters: None,
}])
.unwrap(),
encap_content_info: EncapsulatedContentInfo {
econtent_type: ID_DATA,
econtent: None,
},
certificates: Some(CertificateSet(
SetOfVec::from_iter(
pem_chain
.clone()
.into_iter()
.map(CertificateChoices::Certificate),
)
.unwrap(),
)),
crls: None,
signer_infos: SignerInfos(
SetOfVec::from_iter(pem_chain.into_iter().map(|cert| {
SignerInfo {
version: CmsVersion::V1,
sid: SignerIdentifier::IssuerAndSerialNumber(
IssuerAndSerialNumber {
issuer: cert.tbs_certificate.issuer,
serial_number: cert.tbs_certificate.serial_number,
},
),
digest_alg: AlgorithmIdentifier {
oid: ID_SHA_512,
parameters: None,
},
signed_attrs: None, // TODO: should contain revocation information (see section 12.8.3.3.2)
signature_algorithm: AlgorithmIdentifier {
oid: SHA_512_WITH_RSA_ENCRYPTION,
parameters: None,
},
signature: OctetString::new(&signature[..]).unwrap(),
unsigned_attrs: None, // TODO: should contain timestamp
}
}))
.unwrap(),
),
}
.to_der()
.unwrap()
.as_slice(),
)
.unwrap(),
),
};
let mut sig = sig_data.to_der().unwrap();
// pad with 0 to keep the ranges correct
sig.extend(std::iter::repeat(0).take(SIG_SIZE - sig.len()));
let mut encoded_sig = Vec::with_capacity(sig.len() * 2);
Str(&sig).write(&mut encoded_sig);
dbg!(range.len(), encoded_sig.len());
bytes.splice(range, encoded_sig);
bytes
}