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_external_certs(bulk_fetch): # Test to see if unnecessary fetches for intermediate certs are skipped 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('interm')), ), bulk_fetch=bulk_fetch) orig_fetcher = pkcs11._pull_cert try: def _trap_pull(session, *, label=None, cert_id=None): if label != 'signer': raise RuntimeError return orig_fetcher(session, label=label, cert_id=cert_id) pkcs11._pull_cert = _trap_pull assert isinstance(signer.cert_registry, SimpleCertificateStore) assert len(list(signer.cert_registry)) == 1 out = signers.sign_pdf(w, meta, signer=signer) finally: pkcs11._pull_cert = orig_fetcher r = PdfFileReader(out) emb = r.embedded_signatures[0] assert emb.field_name == 'Sig1' val_trusted(emb)
def val2(out_buf): r = PdfFileReader(out_buf) s = r.embedded_signatures[0] assert s.field_name == 'Sig1' val_trusted(s, extd=True) s = r.embedded_signatures[1] assert s.field_name == 'Sig2' val_trusted(s)
def test_sign_new(file): w = IncrementalPdfFileWriter(BytesIO(sign_test_files[file])) 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)
def test_old_style_signing_cert_attr_ok(with_issser): if with_issser: fname = 'pades-with-old-style-signing-cert-attr-issser.pdf' else: # this file has an old-style signing cert attr without issuerSerial fname = 'pades-with-old-style-signing-cert-attr.pdf' with open(os.path.join(PDF_DATA_DIR, fname), 'rb') as f: r = PdfFileReader(f) s = r.embedded_signatures[0] assert s.field_name == 'Sig1' val_trusted(s)
def test_sign_with_dsa_trust(): w = IncrementalPdfFileWriter(BytesIO(MINIMAL)) out = signers.sign_pdf( w, signers.PdfSignatureMetadata(field_name='Sig1'), signer=FROM_DSA_CA ) r = PdfFileReader(out) s = r.embedded_signatures[0] assert s.field_name == 'Sig1' si = s.signer_info assert si['signature_algorithm']['algorithm'].native == 'sha256_dsa' val_trusted(s, vc=SIMPLE_DSA_V_CONTEXT())
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_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_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_http_timestamp(requests_mock): w = IncrementalPdfFileWriter(BytesIO(MINIMAL_ONE_FIELD)) # bad content-type requests_mock.post(DUMMY_HTTP_TS.url, content=ts_response_callback) from pyhanko.sign.timestamps import TimestampRequestError with pytest.raises(TimestampRequestError): signers.sign_pdf( w, signers.PdfSignatureMetadata(), signer=FROM_CA, timestamper=DUMMY_HTTP_TS, existing_fields_only=True, ) requests_mock.post(DUMMY_HTTP_TS.url, content=ts_response_callback, headers={'Content-Type': 'application/timestamp-reply'}) w = IncrementalPdfFileWriter(BytesIO(MINIMAL_ONE_FIELD)) out = signers.sign_pdf( w, signers.PdfSignatureMetadata(), signer=FROM_CA, timestamper=DUMMY_HTTP_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_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_dsa(bulk_fetch): w = IncrementalPdfFileWriter(BytesIO(MINIMAL)) meta = signers.PdfSignatureMetadata(field_name='Sig1', md_algorithm='sha256') with _simple_sess(token='testdsa') 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_DSA_V_CONTEXT())
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_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_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_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)
def test_add_sigfield_with_lock(include_docmdp): w = IncrementalPdfFileWriter(BytesIO(MINIMAL)) fields.append_signature_field(w, field_with_lock_sp(include_docmdp)) out = signers.sign_pdf( w, signers.PdfSignatureMetadata(field_name='SigNew'), signer=FROM_CA, ) r = PdfFileReader(out) s = r.embedded_signatures[0] assert s.field_name == 'SigNew' refs = s.sig_object.get_object()['/Reference'] assert len(refs) == 1 ref = refs[0] assert ref['/TransformMethod'] == '/FieldMDP' assert ref['/TransformParams']['/Fields'] == generic.ArrayObject(['blah']) assert ref.raw_get('/Data').reference == r.root_ref assert '/Perms' not in r.root if include_docmdp: # test if the Acrobat-compatibility hack was included assert ref['/TransformParams']['/P'] == 1 val_trusted(s)
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_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_direct_pdfcmsembedder_usage(): # CMS-agnostic signing example # # write an in-place certification signature using the PdfCMSEmbedder # low-level API directly. input_buf = BytesIO(MINIMAL) w = IncrementalPdfFileWriter(input_buf) # Phase 1: coroutine sets up the form field cms_writer = cms_embedder.PdfCMSEmbedder().write_cms( field_name='Signature', writer=w ) sig_field_ref = next(cms_writer) # just for kicks, let's check assert sig_field_ref.get_object()['/T'] == 'Signature' # Phase 2: make a placeholder signature object, # wrap it up together with the MDP config we want, and send that # on to cms_writer timestamp = datetime.now(tz=tzlocal.get_localzone()) sig_obj = signers.SignatureObject(timestamp=timestamp, bytes_reserved=8192) md_algorithm = 'sha256' cms_writer.send( cms_embedder.SigObjSetup( sig_placeholder=sig_obj, mdp_setup=cms_embedder.SigMDPSetup( md_algorithm=md_algorithm, certify=True, docmdp_perms=fields.MDPPerm.NO_CHANGES ) ) ) # Phase 3: write & hash the document (with placeholder) prep_digest, output = cms_writer.send( cms_embedder.SigIOSetup(md_algorithm=md_algorithm, in_place=True) ) # Phase 4: construct CMS signature object, and pass it on to cms_writer # NOTE: I'm using a regular SimpleSigner here, but you can substitute # whatever CMS supplier you want. signer: signers.SimpleSigner = FROM_CA # let's supply the CMS object as a raw bytestring with pytest.deprecated_call(): # noinspection PyDeprecation cms_bytes = signer.sign( data_digest=prep_digest.document_digest, digest_algorithm=md_algorithm, timestamp=timestamp ).dump() sig_contents = cms_writer.send(cms_bytes) # we requested in-place output assert output is input_buf r = PdfFileReader(input_buf) val_trusted(r.embedded_signatures[0]) # add some stuff to the DSS for kicks DocumentSecurityStore.add_dss( output, sig_contents, certs=FROM_CA.cert_registry, ocsps=(FIXED_OCSP,) ) r = PdfFileReader(input_buf) dss = DocumentSecurityStore.read_dss(handler=r) val_trusted(r.embedded_signatures[0], extd=True) assert dss is not None assert len(dss.certs) == 3 assert len(dss.ocsps) == 1