A lightweight Rust crate for verifying digital signatures in PDF documents. Designed for zero-knowledge environments with minimal dependencies and comprehensive PKCS#7/CMS support.
The signature-validator crate provides robust verification of digital signatures embedded in PDF files. It focuses on signatures within PKCS#7/CMS SignedData structures and performs both content integrity and signature authenticity checks.
use signature_validator::verify_pdf_signature;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Read signed PDF file
let pdf_bytes = std::fs::read("signed_document.pdf")?;
// Verify the signature
let result = verify_pdf_signature(&pdf_bytes)?;
if result.is_valid {
println!("✅ Signature is valid!");
println!("Signer: {}", result.signer_info);
println!("Signing time: {}", result.signing_time);
} else {
println!("❌ Signature verification failed");
}
Ok(())
}pub fn verify_pdf_signature(pdf_bytes: &[u8]) -> Result<PdfSignatureResult, String>Parameters:
pdf_bytes: Raw PDF file bytes
Returns:
Ok(PdfSignatureResult): Detailed signature verification resultErr(String): Error if verification fails
pub struct PdfSignatureResult {
pub is_valid: bool, // Overall verification result
pub message_digest: Vec<u8>, // Extracted message digest
pub public_key: Vec<u8>, // Signer's public key
pub signer_info: String, // Signer information
pub signing_time: Option<String>, // Signing timestamp
pub signature_algorithm: String, // Used signature algorithm
}The verify_pdf_signature function performs two critical checks:
Verifies that the PDF content hasn't been tampered with since signing.
Process:
- Extract
signed_bytesfrom PDF using theByteRange - Calculate cryptographic hash using the specified algorithm
- Compare with stored
MessageDigestvalue
Mathematical Verification:
Hash(signed_bytes) == MessageDigest
Verifies that the signature was created by the claimed signer.
Process:
- Extract
signed_attributes(ASN.1 structure) - Hash the encoded signed attributes
- Verify signature using signer's public key
Mathematical Verification:
Verify(PublicKey, Hash(signed_attributes), Signature) == true
PDF signatures don't cover the entire file. Instead, they sign specific byte ranges:
ByteRangedefines which parts of the PDF were signed- Typically includes document content and metadata
- Excludes the signature field itself and later additions
- Allows incremental updates without invalidating signatures
SET {
OBJECT IDENTIFIER (messageDigest)
OCTET STRING (hash value)
OBJECT IDENTIFIER (signingTime)
UTCTime (time value)
OBJECT IDENTIFIER (contentType)
OBJECT IDENTIFIER (signingCertificate)
...
}| Algorithm | Hash Function | Encryption | Support |
|---|---|---|---|
| RSA-SHA1 | SHA-1 | RSA | ✅ |
| RSA-SHA256 | SHA-256 | RSA | ✅ |
| RSA-SHA384 | SHA-384 | RSA | ✅ |
| RSA-SHA512 | SHA-512 | RSA | ✅ |
- Hash Functions: SHA-1, SHA-256, SHA-384, SHA-512
- Encryption: RSA with PKCS#1 v1.5 padding
- Signature Format: PKCS#7/CMS SignedData
- ASN.1 Encoding: DER (Distinguished Encoding Rules)
use signature_validator::verify_pdf_signature;
let pdf_bytes = include_bytes!("document.pdf");
let result = verify_pdf_signature(pdf_bytes)?;
if result.is_valid {
println!("Document is authentic and unmodified");
} else {
println!("Document verification failed");
}let result = verify_pdf_signature(&pdf_bytes)?;
println!("Signature Valid: {}", result.is_valid);
println!("Algorithm: {}", result.signature_algorithm);
println!("Signer: {}", result.signer_info);
if let Some(time) = result.signing_time {
println!("Signed at: {}", time);
}
// Access raw cryptographic data
println!("Message Digest: {}", hex::encode(&result.message_digest));
println!("Public Key Length: {} bytes", result.public_key.len());- PDF Parser – Extracts signature fields and ByteRange
- PKCS#7 Parser – Parses ASN.1 SignedData structures
- Crypto Engine – Performs hash and signature verification
- Certificate Handler – Processes signer certificates
rsa– RSA signature verificationsha1,sha2– Hash function implementationssimple_asn1– ASN.1 parsinghex– Hexadecimal encoding/decodingnum-bigint– Big integer arithmetic for RSA
Run the basic test suite:
cargo test -p signature-validatorRun tests with sample signed PDFs:
cargo test -p signature-validator --features private_tests- ✅ PKCS#7/CMS SignedData structures
- ✅ RSA signatures with SHA-1/256/384/512
- ✅ Standard PDF signature fields
- ✅ ByteRange-based content verification
- ✅ ASN.1 DER encoding
- ❌ ECDSA signatures
- ❌ Timestamp verification
- ❌ Certificate chain validation and Multiple signatures
When contributing to the signature validator:
- Keep dependencies minimal
- Ensure ZK-VM compatibility
- Add tests for new algorithms
- Document security considerations
- Maintain compatibility with existing signatures
This crate is licensed under the same terms as the parent repository.