Files
pdfcraft/crates/sign/Cargo.toml
56f85f76df M9: more signature validation algorithms (SHA-3, RIPEMD-160, Ed25519, P-521, brainpool, PSS, rsa_sha1) (#386)
* M9: more signature validation algorithms

Validation now reads what real signers write, and says when it is weak:

- Digests SHA-224, SHA-512/224, SHA-512/256, SHA3-224/256/384/512 and
  RIPEMD-160, beside SHA-1 and SHA-2.
- RSA PKCS#1 v1.5 for every digest, in `rsa_pad`: the one expected encoding
  is built and compared byte for byte, with or without the DigestInfo's NULL
  (RFC 8017 App. B.1). A signature over the bare digest, which doesn't say
  which hash it uses, is refused. The signature value must be below the
  modulus (RFC 8017 §5.2.2); s and s + n both verified before.
- RSA-PSS with the hash, MGF1 hash and salt length the signature declares.
- ECDSA as DER, non-minimal DER or raw r||s on P-256, P-384, P-521 and
  brainpoolP256r1/P384r1/P512r1 (BSI "plain" ECDSA OIDs too); brainpoolP512r1
  is hand-written on crypto-bigint because no RustCrypto crate has it.
- Ed25519 (RFC 8419), and the legacy adbe.x509.rsa_sha1 format.
- An algorithm PdfCraft can't check is Unknown, never Invalid.
- SHA-1 and RIPEMD-160 signatures validate, but the signature's details say
  the algorithm is weak (wording pinned by tests).
- Encoding irregularities that were tolerated are listed in the details
  (`verify_noting`; `verify` keeps its signature).

Found while testing, each with a regression test that failed before the fix:
a brainpoolP512r1 public key with a coordinate >= p panicked inside
crypto-bigint's `sub_mod` (the certificate comes from the PDF); it is refused
now, and the point at infinity is an invalid signature, not an error.

Vectors were made with OpenSSL (tests/data/README.md has the commands).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Update comment to clarify MAC digest restrictions

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* M9: a legacy x509.rsa_sha1 signature whose key the verifier rejects is Invalid; only unsupported keys are "can't check yet"

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-09 18:44:38 -04:00

56 lines
2.3 KiB
TOML

[package]
name = "pdfcraft-sign"
description = "Digital signatures (ISO 32000-2 §12.8, PAdES): X.509, CMS, PKCS #12, signing and validation"
version.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true
repository.workspace = true
[lints]
workspace = true
[dependencies]
pdfcraft-cos = { workspace = true }
pdfcraft-annot = { workspace = true }
pdfcraft-fonts = { workspace = true }
thiserror.workspace = true
base64 = "0.22"
sha1 = "0.11"
sha2 = "0.11"
p256 = { version = "0.14", default-features = false, features = ["ecdsa", "std"] }
p384 = { version = "0.14", default-features = false, features = ["ecdsa", "std"] }
# Verification only: P-521 and the Brainpool curves German qualified-signature cards use.
p521 = { version = "0.14", default-features = false, features = ["ecdsa", "std"] }
bp256 = { version = "0.14", default-features = false, features = ["arithmetic", "ecdsa", "std"] }
bp384 = { version = "0.14", default-features = false, features = ["arithmetic", "ecdsa", "std"] }
ecdsa = { version = "0.17", default-features = false, features = ["algorithm", "der"] }
# Verification only on native targets (RSA private-key operations use aws-lc-rs, ADR-0009).
crypto-bigint = { version = "0.7", default-features = false, features = ["alloc"] }
rsa = { version = "=0.10.0-rc.18", default-features = false, features = ["std", "hazmat"] }
pbkdf2 = { version = "0.13", default-features = false, features = ["hmac"] }
hmac = "0.13"
aes = "0.9"
cbc = { version = "0.2", features = ["alloc", "block-padding"] }
des = "0.9"
rc2 = "0.9"
sha3 = "0.12.0"
ripemd = "0.2.0"
ed25519-dalek = "3.0.0"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
aws-lc-rs = { version = "1.18", default-features = false, features = ["aws-lc-sys", "prebuilt-nasm"] }
getrandom = "0.4"
[target.'cfg(target_os = "windows")'.dependencies]
# Safe Windows certificate store and CNG signing wrappers (MIT OR Apache-2.0).
rustls-cng = { version = "0.7.1", default-features = false }
[target.'cfg(target_os = "macos")'.dependencies]
# Signing with Keychain identities (MIT OR Apache-2.0).
security-framework = "3.7"
[target.'cfg(target_arch = "wasm32")'.dependencies]
# crypto-bigint asks for randomness; in the browser it comes from Web Crypto.
getrandom = { version = "0.4", features = ["wasm_js"] }