def test_sign_crypt_aes256(password): w = IncrementalPdfFileWriter(BytesIO(MINIMAL_ONE_FIELD_AES256)) w.encrypt(password) out = signers.sign_pdf(w, signers.PdfSignatureMetadata(), signer=FROM_CA, existing_fields_only=True) r = PdfFileReader(out) r.decrypt(password) s = r.embedded_signatures[0] val_trusted(s)
def test_sign_crypt_pubkey_rc4(): w = IncrementalPdfFileWriter(BytesIO(MINIMAL_PUBKEY_ONE_FIELD_RC4)) w.encrypt_pubkey(PUBKEY_SELFSIGNED_DECRYPTER) out = signers.sign_pdf(w, signers.PdfSignatureMetadata(), signer=FROM_CA, existing_fields_only=True) r = PdfFileReader(out) r.decrypt_pubkey(PUBKEY_SELFSIGNED_DECRYPTER) s = r.embedded_signatures[0] val_trusted(s)
def test_circular_form_tree_sign(): fname = os.path.join(PDF_DATA_DIR, 'form-tree-circular-ref-input.pdf') with open(fname, 'rb') as inf: w = IncrementalPdfFileWriter(inf) out = signers.sign_pdf( w, signature_meta=signers.PdfSignatureMetadata( field_name='Sig' ), signer=FROM_CA ) r = PdfFileReader(out) with pytest.raises(PdfReadError, match='Circular.*form tree'): list(r.embedded_signatures)
def test_sign_with_trust_pkcs12(): w = IncrementalPdfFileWriter(BytesIO(MINIMAL)) out = signers.sign_pdf( w, signers.PdfSignatureMetadata(field_name='Sig1'), signer=FROM_CA_PKCS12 ) r = PdfFileReader(out) s = r.embedded_signatures[0] assert s.field_name == 'Sig1' status = val_untrusted(s) assert not status.trusted val_trusted(s)
def test_timestamp_wrong_type(): w = IncrementalPdfFileWriter(BytesIO(MINIMAL)) meta = signers.PdfSignatureMetadata(field_name='Sig1') out = signers.sign_pdf(w, meta, signer=SELF_SIGN) r = PdfFileReader(out) emb = r.embedded_signatures[0] # Again: # (yes, obviously this also isn't a valid timestamp token, hence the # match=... rule here) with pytest.raises(SignatureValidationError, match='.*must be /DocTimeStamp.*'): validate_pdf_timestamp(emb, validation_context=SIMPLE_V_CONTEXT())
def test_sign_reject_freed(forbid_freeing): w = IncrementalPdfFileWriter(BytesIO(MINIMAL_ONE_FIELD)) out = signers.sign_pdf( w, signature_meta=signers.PdfSignatureMetadata(field_name='Sig1'), signer=FROM_CA) # free the ref containing the /Info dictionary # since we don't have support for freeing objects in the writer (yet), # do it manually r = PdfFileReader(out) last_startxref = r.last_startxref # NOTE the linked list offsets are dummied out, but our Xref parser # doesn't care len_out = out.seek(0, os.SEEK_END) out.write(b'\n'.join([ b'xref', b'0 1', b'0000000000 65535 f ', b'2 1', b'0000000000 00001 f ', b'trailer<</Prev %d>>' % last_startxref, b'startxref', b'%d' % len_out, b'%%EOF' ])) r = PdfFileReader(out) last_rev = r.xrefs.xref_sections - 1 some_ref = generic.Reference(2, 0) assert some_ref in r.xrefs.refs_freed_in_revision(last_rev) sig = r.embedded_signatures[0] assert sig.signed_revision == 2 # make a dummy rule that whitelists our freed object ref class AdHocRule(QualifiedWhitelistRule): def apply_qualified(self, old: HistoricalResolver, new: HistoricalResolver): yield ModificationLevel.LTA_UPDATES, ReferenceUpdate( some_ref, paths_checked=RawPdfPath('/Root', '/Pages')) val_status = validate_pdf_signature( sig, SIMPLE_V_CONTEXT(), diff_policy=StandardDiffPolicy(DEFAULT_DIFF_POLICY.global_rules + [AdHocRule()], DEFAULT_DIFF_POLICY.form_rule, reject_object_freeing=forbid_freeing)) if forbid_freeing: assert val_status.modification_level == ModificationLevel.OTHER else: assert val_status.modification_level == ModificationLevel.LTA_UPDATES
def test_add_revinfo_timestamp_separate_no_dss(requests_mock, with_vri): buf = BytesIO(MINIMAL) w = IncrementalPdfFileWriter(buf) # create signature & timestamp without revocation info with freeze_time('2020-11-01'): signers.sign_pdf( w, signers.PdfSignatureMetadata(field_name='Sig1'), signer=FROM_CA, in_place=True ) signers.PdfTimeStamper(timestamper=DUMMY_TS).timestamp_pdf( IncrementalPdfFileWriter(buf), 'sha256', in_place=True ) # fast forward 1 month with freeze_time('2020-12-01'): vc = live_testing_vc(requests_mock) r = PdfFileReader(buf) emb_sig = r.embedded_signatures[0] add_validation_info(emb_sig, vc, in_place=True, add_vri_entry=with_vri) r = PdfFileReader(buf) emb_sig = r.embedded_signatures[0] # without retroactive revinfo, the validation should fail status = validate_pdf_ltv_signature( emb_sig, RevocationInfoValidationType.PADES_LT, {'trust_roots': TRUST_ROOTS} ) assert status.valid and not status.trusted # with retroactive revinfo, it should be OK status = validate_pdf_ltv_signature( emb_sig, RevocationInfoValidationType.PADES_LT, {'trust_roots': TRUST_ROOTS, 'retroactive_revinfo': True} ) assert status.valid and status.trusted assert status.modification_level == ModificationLevel.LTA_UPDATES
def test_simple_sign_legacy_signer_upgrade(): w = IncrementalPdfFileWriter(BytesIO(MINIMAL)) meta = signers.PdfSignatureMetadata(field_name='Sig1') legacy_signer = LegacyRSASigner( signing_cert=SELF_SIGN.signing_cert, signing_key=SELF_SIGN.signing_key, cert_registry=SELF_SIGN.cert_registry, ) out = signers.sign_pdf(w, meta, signer=legacy_signer) r = PdfFileReader(out) emb = r.embedded_signatures[0] assert emb.field_name == 'Sig1' val_untrusted(emb)
def test_adobe_revinfo_live(requests_mock): w = IncrementalPdfFileWriter(BytesIO(MINIMAL_ONE_FIELD)) vc = live_testing_vc(requests_mock) out = signers.sign_pdf( w, signers.PdfSignatureMetadata( field_name='Sig1', validation_context=vc, subfilter=fields.SigSeedSubFilter.ADOBE_PKCS7_DETACHED, embed_validation_info=True ), signer=FROM_CA, timestamper=DUMMY_TS ) r = PdfFileReader(out) rivt_adobe = RevocationInfoValidationType.ADOBE_STYLE status = validate_pdf_ltv_signature(r.embedded_signatures[0], rivt_adobe, {'trust_roots': TRUST_ROOTS}) assert status.valid and status.trusted
def test_sign_crypt_rc4_new(password, file): w = IncrementalPdfFileWriter(BytesIO(sign_crypt_rc4_files[file])) w.encrypt(password) out = signers.sign_pdf( w, signers.PdfSignatureMetadata(field_name='SigNew'), signer=FROM_CA, ) out.seek(0) r = PdfFileReader(out) r.decrypt(password) s = r.embedded_signatures[0] val_trusted(s)
def test_sign_with_indir_annots(): with open(PDF_DATA_DIR + '/minimal-one-field-indir-annots.pdf', 'rb') as f: w = IncrementalPdfFileWriter(f) out = signers.sign_pdf( w, signers.PdfSignatureMetadata(field_name='SigNew'), signer=FROM_CA ) r = PdfFileReader(out) e = r.embedded_signatures[0] assert e.field_name == 'SigNew' val_trusted(e) annots_ref = r.root['/Pages']['/Kids'][0].raw_get('/Annots') assert isinstance(annots_ref, generic.IndirectObject) assert len(annots_ref.get_object()) == 2
def test_sign_weak_digest(): w = IncrementalPdfFileWriter(BytesIO(MINIMAL)) meta = signers.PdfSignatureMetadata(field_name='Sig1', md_algorithm='md5') out = signers.sign_pdf(w, meta, signer=FROM_CA) r = PdfFileReader(out) emb = r.embedded_signatures[0] assert emb.field_name == 'Sig1' with pytest.raises(WeakHashAlgorithmError): val_trusted(emb) lenient_vc = ValidationContext(trust_roots=[ROOT_CERT], weak_hash_algos=set()) val_trusted(emb, vc=lenient_vc)
def test_dummy_timestamp(): w = IncrementalPdfFileWriter(BytesIO(MINIMAL_ONE_FIELD)) out = signers.sign_pdf( w, signers.PdfSignatureMetadata(), signer=FROM_CA, timestamper=DUMMY_TS, existing_fields_only=True, ) r = PdfFileReader(out) s = r.embedded_signatures[0] assert s.field_name == 'Sig1' validity = val_trusted(s) assert validity.timestamp_validity is not None assert validity.timestamp_validity.trusted
def test_double_sign_lock_second(): # test if the difference analysis correctly processes /Reference # on a newly added signature object w = IncrementalPdfFileWriter(BytesIO(MINIMAL)) fields.append_signature_field(w, field_with_lock_sp(True)) out = signers.sign_pdf( w, signers.PdfSignatureMetadata(field_name='SigFirst'), signer=FROM_CA, ) w = IncrementalPdfFileWriter(out) # now sign the locked field out = signers.sign_pdf( w, signers.PdfSignatureMetadata(field_name='SigNew'), signer=FROM_CA, ) r = PdfFileReader(out) s = r.embedded_signatures[0] val_trusted(s, extd=True) s = r.embedded_signatures[1] assert len(s.sig_object.get_object()['/Reference']) == 1 val_trusted(s)
def test_sign_without_annot(): with open(PDF_DATA_DIR + '/minimal-annotless.pdf', 'rb') as f: w = IncrementalPdfFileWriter(f) meta = signers.PdfSignatureMetadata(field_name='Sig1') out = signers.sign_pdf(w, meta, signer=FROM_CA) r = PdfFileReader(out) emb = r.embedded_signatures[0] assert emb.field_name == 'Sig1' assert '/AP' not in emb.sig_field assert '/Rect' not in emb.sig_field assert '/Kids' not in emb.sig_field assert '/Type' not in emb.sig_field val_trusted(emb)
def test_form_field_postsign_fill(): w = IncrementalPdfFileWriter(BytesIO(SIMPLE_FORM)) # sign, then fill meta = signers.PdfSignatureMetadata(field_name='Sig1') out = signers.sign_pdf(w, meta, signer=FROM_CA) w = IncrementalPdfFileWriter(out) set_text_field(w, "Some text") out = BytesIO() w.write(out) r = PdfFileReader(out) s = r.embedded_signatures[0] assert s.field_name == 'Sig1' val_trusted(s, extd=True)
def test_simple_sign_ecdsa(bulk_fetch): w = IncrementalPdfFileWriter(BytesIO(MINIMAL)) meta = signers.PdfSignatureMetadata(field_name='Sig1', md_algorithm='sha1') with _simple_sess(token='testecdsa') as sess: signer = pkcs11.PKCS11Signer(sess, 'signer', other_certs_to_pull=default_other_certs, bulk_fetch=bulk_fetch) out = signers.sign_pdf(w, meta, signer=signer) r = PdfFileReader(out) emb = r.embedded_signatures[0] assert emb.field_name == 'Sig1' val_trusted(emb, vc=SIMPLE_ECC_V_CONTEXT())
def test_form_field_in_group_postsign_fill(variant): w = IncrementalPdfFileWriter(BytesIO(GROUP_VARIANTS[variant])) # sign, then fill meta = signers.PdfSignatureMetadata(field_name='Sig1') out = signers.sign_pdf(w, meta, signer=FROM_CA) w = IncrementalPdfFileWriter(out) set_text_field_in_group(w, 0, "Some text") out = BytesIO() w.write(out) r = PdfFileReader(out) s = r.embedded_signatures[0] assert s.field_name == 'Sig1' val_trusted(s, extd=True)
def test_sign_field_infer(): w = IncrementalPdfFileWriter(BytesIO(MINIMAL_ONE_FIELD)) with pytest.raises(SigningError): signers.sign_pdf(w, signers.PdfSignatureMetadata(), signer=FROM_CA) out = signers.sign_pdf(w, signers.PdfSignatureMetadata(), signer=FROM_CA, existing_fields_only=True) r = PdfFileReader(out) s = r.embedded_signatures[0] assert s.field_name == 'Sig1' val_trusted(s) w = IncrementalPdfFileWriter(out) # shouldn't work now since all fields are taken with pytest.raises(SigningError): signers.sign_pdf(w, signers.PdfSignatureMetadata(), signer=FROM_CA, existing_fields_only=True)
def test_name_location(): w = IncrementalPdfFileWriter(BytesIO(MINIMAL)) meta = signers.PdfSignatureMetadata( field_name='Sig1', name='Bleh', location='Bluh' ) out = signers.sign_pdf(w, meta, signer=SELF_SIGN) r = PdfFileReader(out) emb = r.embedded_signatures[0] assert emb.field_name == 'Sig1' val_untrusted(emb) assert emb.sig_object['/Name'] == 'Bleh' assert emb.sig_object['/Location'] == 'Bluh'
def test_simple_sign(bulk_fetch, pss): w = IncrementalPdfFileWriter(BytesIO(MINIMAL)) meta = signers.PdfSignatureMetadata(field_name='Sig1') with _simple_sess() as sess: signer = pkcs11.PKCS11Signer(sess, 'signer', other_certs_to_pull=default_other_certs, bulk_fetch=bulk_fetch, prefer_pss=pss) out = signers.sign_pdf(w, meta, signer=signer) r = PdfFileReader(out) emb = r.embedded_signatures[0] assert emb.field_name == 'Sig1' val_trusted(emb)
def test_simple_sign_from_config(): w = IncrementalPdfFileWriter(BytesIO(MINIMAL)) meta = signers.PdfSignatureMetadata(field_name='Sig1') config = PKCS11SignatureConfig(module_path=pkcs11_test_module, token_label='testrsa', cert_label='signer', user_pin='1234', other_certs_to_pull=None) with PKCS11SigningContext(config) as signer: out = signers.sign_pdf(w, meta, signer=signer) r = PdfFileReader(out) emb = r.embedded_signatures[0] assert emb.field_name == 'Sig1' val_trusted(emb)
def test_sign_pss(): signer = signers.SimpleSigner.load( CRYPTO_DATA_DIR + '/selfsigned.key.pem', CRYPTO_DATA_DIR + '/selfsigned.cert.pem', ca_chain_files=(CRYPTO_DATA_DIR + '/selfsigned.cert.pem',), key_passphrase=b'secret', prefer_pss=True ) w = IncrementalPdfFileWriter(BytesIO(MINIMAL)) meta = signers.PdfSignatureMetadata(field_name='Sig1') out = signers.sign_pdf(w, meta, signer=signer) r = PdfFileReader(out) emb = r.embedded_signatures[0] assert emb.field_name == 'Sig1' sda: SignedDigestAlgorithm = emb.signer_info['signature_algorithm'] assert sda.signature_algo == 'rsassa_pss' val_untrusted(emb)
def test_form_field_ft_tamper(): w = IncrementalPdfFileWriter(BytesIO(SIMPLE_FORM)) # sign, then fill meta = signers.PdfSignatureMetadata(field_name='Sig1') out = signers.sign_pdf(w, meta, signer=FROM_CA) w = IncrementalPdfFileWriter(out) tf = w.root['/AcroForm']['/Fields'][1].get_object() tf['/FT'] = pdf_name('/Sig') w.update_container(tf) out = BytesIO() w.write(out) r = PdfFileReader(out) s = r.embedded_signatures[0] assert s.field_name == 'Sig1' val_trusted_but_modified(s)
def test_no_diff_summary(): w = IncrementalPdfFileWriter(BytesIO(MINIMAL)) meta = signers.PdfSignatureMetadata(field_name='Sig1') out = signers.sign_pdf(w, meta, signer=SELF_SIGN) # just do an incremental DSS update DocumentSecurityStore.add_dss( out, sig_contents=None, certs=(SELF_SIGN.signing_cert,) ) r = PdfFileReader(out) emb = r.embedded_signatures[0] status = validate_pdf_signature(emb, skip_diff=True) assert emb.diff_result is None assert status.modification_level is None assert not status.docmdp_ok assert status.coverage == SignatureCoverageLevel.ENTIRE_REVISION assert 'EXTENDED' in status.summary()
def test_sign_tight_container(): w = IncrementalPdfFileWriter(BytesIO(MINIMAL)) meta = signers.PdfSignatureMetadata( field_name='Sig1', tight_size_estimates=True ) out = signers.sign_pdf(w, meta, signer=SELF_SIGN) r = PdfFileReader(out) emb = r.embedded_signatures[0] assert emb.field_name == 'Sig1' val_untrusted(emb) contents_str = emb.pkcs7_content ci = cms.ContentInfo({ 'content_type': 'signed_data', 'content': emb.signed_data }) assert ci.dump() == contents_str
def test_sign_multiple_cert_sources(bulk_fetch): w = IncrementalPdfFileWriter(BytesIO(MINIMAL)) meta = signers.PdfSignatureMetadata(field_name='Sig1') with _simple_sess() as sess: signer = pkcs11.PKCS11Signer(sess, 'signer', other_certs_to_pull=('root', ), ca_chain=(TESTING_CA.get_cert( CertLabel('interm')), ), bulk_fetch=bulk_fetch) assert isinstance(signer.cert_registry, SimpleCertificateStore) assert len(list(signer.cert_registry)) == 2 out = signers.sign_pdf(w, meta, signer=signer) r = PdfFileReader(out) emb = r.embedded_signatures[0] assert emb.field_name == 'Sig1' val_trusted(emb)
def test_signer_provided_others_pulled(bulk_fetch): w = IncrementalPdfFileWriter(BytesIO(MINIMAL)) meta = signers.PdfSignatureMetadata(field_name='Sig1') with _simple_sess() as sess: signer = pkcs11.PKCS11Signer( sess, 'signer', ca_chain={ TESTING_CA.get_cert(CertLabel('root')), TESTING_CA.get_cert(CertLabel('interm')), }, ) out = signers.sign_pdf(w, meta, signer=signer) r = PdfFileReader(out) emb = r.embedded_signatures[0] assert emb.field_name == 'Sig1' val_trusted(emb)
def test_sign_with_explicit_dsa_implied_hash(): signer = signers.SimpleSigner( signing_cert=TESTING_CA_DSA.get_cert(CertLabel('signer1')), signing_key=TESTING_CA_DSA.key_set.get_private_key( KeyLabel('signer1')), cert_registry=SimpleCertificateStore.from_certs( [DSA_ROOT_CERT, DSA_INTERM_CERT]), # this is not allowed, but the validator should accept it anyway signature_mechanism=SignedDigestAlgorithm({'algorithm': 'dsa'})) w = IncrementalPdfFileWriter(BytesIO(MINIMAL)) out = signers.sign_pdf(w, signers.PdfSignatureMetadata(field_name='Sig1'), signer=signer) r = PdfFileReader(out) s = r.embedded_signatures[0] si = s.signer_info assert si['signature_algorithm']['algorithm'].native == 'dsa' assert s.field_name == 'Sig1' val_trusted(s, vc=SIMPLE_DSA_V_CONTEXT())
def test_signer_pulled_others_provided(bulk_fetch): w = IncrementalPdfFileWriter(BytesIO(MINIMAL)) meta = signers.PdfSignatureMetadata(field_name='Sig1') signer_cert = TESTING_CA.get_cert(CertLabel('signer1')) with _simple_sess() as sess: signer = pkcs11.PKCS11Signer(sess, key_label='signer', signing_cert=signer_cert, bulk_fetch=bulk_fetch, other_certs_to_pull=default_other_certs) out = signers.sign_pdf(w, meta, signer=signer) r = PdfFileReader(out) emb = r.embedded_signatures[0] assert emb.field_name == 'Sig1' assert emb.signer_cert.dump() == signer_cert.dump() # this will fail if the intermediate cert is not present val_trusted(emb)