def test_sv_sign_addrevinfo_subfilter_conflict(): sv = fields.SigSeedValueSpec(flags=fields.SigSeedValFlags.ADD_REV_INFO, subfilters=[PADES], add_rev_info=True) with pytest.raises(SigningError): meta = signers.PdfSignatureMetadata(field_name='Sig', validation_context=dummy_ocsp_vc(), embed_validation_info=True) sign_with_sv(sv, meta) revinfo_and_subfilter = (fields.SigSeedValFlags.ADD_REV_INFO | fields.SigSeedValFlags.SUBFILTER) sv = fields.SigSeedValueSpec(flags=revinfo_and_subfilter, subfilters=[PADES], add_rev_info=True) meta = signers.PdfSignatureMetadata(field_name='Sig', validation_context=dummy_ocsp_vc(), embed_validation_info=True) with pytest.raises(SigningError): sign_with_sv(sv, meta) sign_with_sv(sv, meta, test_violation=True) sv = fields.SigSeedValueSpec(flags=revinfo_and_subfilter, subfilters=[PADES], add_rev_info=False) meta = signers.PdfSignatureMetadata( field_name='Sig', validation_context=dummy_ocsp_vc(), ) sign_with_sv(sv, meta)
def test_sv_version(): fields.SigSeedValueSpec.from_pdf_object( generic.DictionaryObject({'/Ff': fields.SigSeedValFlags.V})) fields.SigSeedValueSpec.from_pdf_object( fields.SigSeedValueSpec(flags=fields.SigSeedValFlags.V, sv_dict_version=fields.SeedValueDictVersion. PDF_2_0).as_pdf_object()) with pytest.raises(SigningError): fields.SigSeedValueSpec.from_pdf_object( fields.SigSeedValueSpec(flags=fields.SigSeedValFlags.V, sv_dict_version=4).as_pdf_object())
def test_field_lock_compat(): sv = fields.SigSeedValueSpec(flags=fields.SigSeedValFlags.LOCK_DOCUMENT, lock_document=fields.SeedLockDocument.LOCK) meta = signers.PdfSignatureMetadata(field_name='Sig') sign_with_sv(sv, meta, add_field_lock=True) sv = fields.SigSeedValueSpec( flags=fields.SigSeedValFlags.LOCK_DOCUMENT, lock_document=fields.SeedLockDocument.DO_NOT_LOCK) meta = signers.PdfSignatureMetadata(field_name='Sig') with pytest.raises(SigningError): sign_with_sv(sv, meta, add_field_lock=True)
def test_sv_mdp_type(): sv_dict = fields.SigSeedValueSpec().as_pdf_object() assert '/MDP' not in sv_dict sv_dict = fields.SigSeedValueSpec( seed_signature_type=fields.SeedSignatureType(None)).as_pdf_object() assert sv_dict['/MDP'] == generic.DictionaryObject( {pdf_name('/P'): generic.NumberObject(0)}) sv_dict = fields.SigSeedValueSpec( seed_signature_type=fields.SeedSignatureType( fields.MDPPerm.NO_CHANGES)).as_pdf_object() assert sv_dict['/MDP'] == generic.DictionaryObject( {pdf_name('/P'): generic.NumberObject(1)}) sv_spec = fields.SigSeedValueSpec.from_pdf_object( generic.DictionaryObject({ pdf_name('/MDP'): generic.DictionaryObject({pdf_name('/P'): generic.NumberObject(0)}) })) assert sv_spec.seed_signature_type == fields.SeedSignatureType(None) sv_spec = fields.SigSeedValueSpec.from_pdf_object( generic.DictionaryObject({ pdf_name('/MDP'): generic.DictionaryObject({pdf_name('/P'): generic.NumberObject(2)}) })) assert sv_spec.seed_signature_type == fields.SeedSignatureType( fields.MDPPerm.FILL_FORMS) with pytest.raises(SigningError): fields.SigSeedValueSpec.from_pdf_object( generic.DictionaryObject({ pdf_name('/MDP'): generic.DictionaryObject( {pdf_name('/P'): generic.NumberObject(5)}) })) with pytest.raises(SigningError): fields.SigSeedValueSpec.from_pdf_object( generic.DictionaryObject({ pdf_name('/MDP'): generic.DictionaryObject( {pdf_name('/P'): generic.NullObject()}) })) with pytest.raises(SigningError): fields.SigSeedValueSpec.from_pdf_object( generic.DictionaryObject( {pdf_name('/MDP'): generic.DictionaryObject()})) with pytest.raises(SigningError): fields.SigSeedValueSpec.from_pdf_object( generic.DictionaryObject({pdf_name('/MDP'): generic.NullObject()}))
def test_sv_sign_md_req(): sv = fields.SigSeedValueSpec( flags=fields.SigSeedValFlags.DIGEST_METHOD, digest_methods=['sha256', 'sha512'], ) with pytest.raises(SigningError): sign_with_sv( sv, signers.PdfSignatureMetadata( md_algorithm='sha1', field_name='Sig' ) ) sign_with_sv( sv, signers.PdfSignatureMetadata(md_algorithm='sha1', field_name='Sig'), test_violation=True ) emb_sig = sign_with_sv( sv, signers.PdfSignatureMetadata(field_name='Sig') ) assert emb_sig.md_algorithm == 'sha256' emb_sig = sign_with_sv( sv, signers.PdfSignatureMetadata( md_algorithm='sha512', field_name='Sig' ) ) assert emb_sig.md_algorithm == 'sha512'
def test_sv_no_lock_certify(): sv = fields.SigSeedValueSpec( flags=fields.SigSeedValFlags.LOCK_DOCUMENT, lock_document=fields.SeedLockDocument.DO_NOT_LOCK) meta = signers.PdfSignatureMetadata( field_name='Sig', certify=True, docmdp_permissions=fields.MDPPerm.FILL_FORMS) sign_with_sv(sv, meta) meta = signers.PdfSignatureMetadata( field_name='Sig', certify=True, docmdp_permissions=fields.MDPPerm.ANNOTATE) sign_with_sv(sv, meta) meta = signers.PdfSignatureMetadata( field_name='Sig', certify=True, ) sign_with_sv(sv, meta) meta = signers.PdfSignatureMetadata(field_name='Sig') sign_with_sv(sv, meta) meta = signers.PdfSignatureMetadata( field_name='Sig', certify=True, docmdp_permissions=fields.MDPPerm.NO_CHANGES) sign_with_sv(sv, meta, test_violation=True)
def test_sv_sign_addrevinfo_req(requests_mock): sv = fields.SigSeedValueSpec(flags=fields.SigSeedValFlags.ADD_REV_INFO, add_rev_info=True) vc = live_testing_vc(requests_mock) meta = signers.PdfSignatureMetadata( field_name='Sig', validation_context=vc, subfilter=fields.SigSeedSubFilter.ADOBE_PKCS7_DETACHED, embed_validation_info=True) emb_sig = sign_with_sv(sv, meta) status = validate_pdf_ltv_signature( emb_sig, RevocationInfoValidationType.ADOBE_STYLE, {'trust_roots': TRUST_ROOTS}) assert status.valid and status.trusted assert emb_sig.sig_object['/SubFilter'] == '/adbe.pkcs7.detached' meta = signers.PdfSignatureMetadata( field_name='Sig', validation_context=vc, subfilter=fields.SigSeedSubFilter.ADOBE_PKCS7_DETACHED, embed_validation_info=False) with pytest.raises(SigningError): sign_with_sv(sv, meta) sign_with_sv(sv, meta, test_violation=True) meta = signers.PdfSignatureMetadata( field_name='Sig', validation_context=vc, subfilter=fields.SigSeedSubFilter.PADES, embed_validation_info=True) # this shouldn't work with PAdES with pytest.raises(SigningError): sign_with_sv(sv, meta) sign_with_sv(sv, meta, test_violation=True)
def test_sv_subfilter_unsupported_partial(): sv_spec = fields.SigSeedValueSpec( flags=fields.SigSeedValFlags.SUBFILTER, subfilters=[fields.SigSeedSubFilter.ADOBE_PKCS7_DETACHED, PADES]) w = IncrementalPdfFileWriter(prepare_sv_field(sv_spec)) field_name, _, sig_field = next(fields.enumerate_sig_fields(w)) sig_field = sig_field.get_object() sv_ref = sig_field.raw_get('/SV') w.mark_update(sv_ref) sv_ref.get_object()['/SubFilter'][0] = pdf_name('/this.doesnt.exist') out = BytesIO() w.write(out) out.seek(0) frozen = out.getvalue() signers.sign_pdf(IncrementalPdfFileWriter(BytesIO(frozen)), signers.PdfSignatureMetadata(field_name='Sig'), signer=FROM_CA, timestamper=DUMMY_TS) with pytest.raises(SigningError): signers.sign_pdf( IncrementalPdfFileWriter(BytesIO(frozen)), signers.PdfSignatureMetadata( field_name='Sig', subfilter=fields.SigSeedSubFilter.ADOBE_PKCS7_DETACHED), signer=FROM_CA, timestamper=DUMMY_TS)
def test_sv_flag_appearance_required(): sv = fields.SigSeedValueSpec( flags=fields.SigSeedValFlags.APPEARANCE_FILTER, appearance='test' ) meta = signers.PdfSignatureMetadata(field_name='Sig') with pytest.raises(SigningError): sign_with_sv(sv, meta)
def test_sv_cert_flag_unsupported(): sv = fields.SigSeedValueSpec( cert=fields.SigCertConstraints( flags=fields.SigCertConstraintFlags.RESERVED, ) ) meta = signers.PdfSignatureMetadata(field_name='Sig') with pytest.raises(NotImplementedError): sign_with_sv(sv, meta)
def test_sv_sign_cert_constraint(): # this is more thoroughly unit tested at a lower level (see further up), # so we simply try two basic scenarios here for now from asn1crypto import x509 sv = fields.SigSeedValueSpec(cert=fields.SigCertConstraints( flags=fields.SigCertConstraintFlags.SUBJECT_DN, subject_dn=x509.Name.build({'common_name': 'Lord Testerino'}), )) sign_with_sv(sv, signers.PdfSignatureMetadata(field_name='Sig')) sv = fields.SigSeedValueSpec(cert=fields.SigCertConstraints( flags=fields.SigCertConstraintFlags.SUBJECT_DN, subject_dn=x509.Name.build({'common_name': 'Not Lord Testerino'}), )) with pytest.raises(SigningError): sign_with_sv(sv, signers.PdfSignatureMetadata(field_name='Sig')) sign_with_sv(sv, signers.PdfSignatureMetadata(field_name='Sig'), test_violation=True)
def test_sv_flag_unsupported(): sv = fields.SigSeedValueSpec( flags=(fields.SigSeedValFlags.LEGAL_ATTESTATION | fields.SigSeedValFlags.APPEARANCE_FILTER), legal_attestations=['abc'], appearance='test') meta = signers.PdfSignatureMetadata(field_name='Sig') with pytest.raises(NotImplementedError): sign_with_sv(sv, meta, test_violation=True)
def test_sv_mdp_must_certify_wrong_docmdp(): sv = fields.SigSeedValueSpec(seed_signature_type=fields.SeedSignatureType( fields.MDPPerm.FILL_FORMS), ) meta = signers.PdfSignatureMetadata( field_name='Sig', certify=True, docmdp_permissions=fields.MDPPerm.NO_CHANGES) with pytest.raises(SigningError): sign_with_sv(sv, meta)
def test_sv_sign_subfilter_hint(): sv = fields.SigSeedValueSpec(subfilters=[PADES]) emb_sig = sign_with_sv( sv, signers.PdfSignatureMetadata( md_algorithm='sha1', field_name='Sig', subfilter=fields.SigSeedSubFilter.ADOBE_PKCS7_DETACHED)) assert emb_sig.sig_object['/SubFilter'] == '/adbe.pkcs7.detached' emb_sig = sign_with_sv(sv, signers.PdfSignatureMetadata(field_name='Sig')) assert emb_sig.sig_object['/SubFilter'] == PADES.value
def test_sv_sign_md_hint(): sv = fields.SigSeedValueSpec(digest_methods=['sha256', 'sha512']) emb_sig = sign_with_sv( sv, signers.PdfSignatureMetadata(md_algorithm='sha1', field_name='Sig')) assert emb_sig.md_algorithm == 'sha1' emb_sig = sign_with_sv(sv, signers.PdfSignatureMetadata(field_name='Sig')) assert emb_sig.md_algorithm == 'sha256' emb_sig = sign_with_sv( sv, signers.PdfSignatureMetadata(md_algorithm='sha512', field_name='Sig')) assert emb_sig.md_algorithm == 'sha512'
def test_sv_mdp_no_certify(): sv = fields.SigSeedValueSpec( seed_signature_type=fields.SeedSignatureType(), ) meta = signers.PdfSignatureMetadata(field_name='Sig') sign_with_sv(sv, meta) meta = signers.PdfSignatureMetadata(field_name='Sig', certify=True) with pytest.raises(SigningError): sign_with_sv(sv, meta) meta = signers.PdfSignatureMetadata(field_name='Sig', certify=True) sign_with_sv(sv, meta, test_violation=True)
def test_sv_sign_reason_prohibited(reasons_param): sv = fields.SigSeedValueSpec(flags=fields.SigSeedValFlags.REASONS, reasons=reasons_param) aw_yiss = signers.PdfSignatureMetadata(reason='Aw yiss', field_name='Sig') with pytest.raises(SigningError): sign_with_sv(sv, aw_yiss) sign_with_sv(sv, aw_yiss, test_violation=True) dot = signers.PdfSignatureMetadata(reason='.', field_name='Sig') with pytest.raises(SigningError): sign_with_sv(sv, dot) sign_with_sv(sv, dot, test_violation=True) emb_sig = sign_with_sv(sv, signers.PdfSignatureMetadata(field_name='Sig')) assert pdf_name('/Reason') not in emb_sig.sig_object
def test_sv_sign_reason_req(): sv = fields.SigSeedValueSpec(flags=fields.SigSeedValFlags.REASONS, reasons=['I agree', 'Works for me']) aw_yiss = signers.PdfSignatureMetadata(reason='Aw yiss', field_name='Sig') with pytest.raises(SigningError): sign_with_sv(sv, aw_yiss) sign_with_sv(sv, aw_yiss, test_violation=True) with pytest.raises(SigningError): sign_with_sv(sv, signers.PdfSignatureMetadata(field_name='Sig')) sign_with_sv(sv, signers.PdfSignatureMetadata(field_name='Sig'), test_violation=True) emb_sig = sign_with_sv( sv, signers.PdfSignatureMetadata(field_name='Sig', reason='I agree')) assert emb_sig.sig_object['/Reason'] == 'I agree'
def test_sv_timestamp_url(requests_mock): # state issues (see comment in signers.py), so create a fresh signer sv = fields.SigSeedValueSpec(timestamp_server_url=DUMMY_HTTP_TS.url, timestamp_required=True) meta = signers.PdfSignatureMetadata(field_name='Sig') ts_requested = False def ts_callback(*args, **kwargs): nonlocal ts_requested ts_requested = True return ts_response_callback(*args, **kwargs) requests_mock.post(DUMMY_HTTP_TS.url, content=ts_callback, headers={'Content-Type': 'application/timestamp-reply'}) # noinspection PyTypeChecker sign_with_sv(sv, meta, timestamper=None) assert ts_requested
def test_sv_sign_subfilter_req(): sv = fields.SigSeedValueSpec(flags=fields.SigSeedValFlags.SUBFILTER, subfilters=[PADES]) with pytest.raises(SigningError): sign_with_sv( sv, signers.PdfSignatureMetadata( md_algorithm='sha1', field_name='Sig', subfilter=fields.SigSeedSubFilter.ADOBE_PKCS7_DETACHED)) sign_with_sv(sv, signers.PdfSignatureMetadata( md_algorithm='sha1', field_name='Sig', subfilter=fields.SigSeedSubFilter.ADOBE_PKCS7_DETACHED), test_violation=True) emb_sig = sign_with_sv(sv, signers.PdfSignatureMetadata(field_name='Sig')) assert emb_sig.sig_object['/SubFilter'] == PADES.value
def test_append_sig_field_with_simple_sv(): w = IncrementalPdfFileWriter(BytesIO(MINIMAL)) sv = fields.SigSeedValueSpec( reasons=['a', 'b', 'c'], cert=fields.SigCertConstraints( subject_dn=FROM_CA.signing_cert.subject, issuers=[INTERM_CERT], subjects=[FROM_CA.signing_cert], key_usage=[ fields.SigCertKeyUsage.from_sets( {'digital_signature', 'non_repudiation'}, {'key_agreement'}) ]), digest_methods=['ssh256'], add_rev_info=True, subfilters=[fields.SigSeedSubFilter.ADOBE_PKCS7_DETACHED], timestamp_server_url='https://tsa.example.com', ) sp = fields.SigFieldSpec('InvisibleSig', seed_value_dict=sv) fields.append_signature_field(w, sp) out = BytesIO() w.write(out) out.seek(0) r = PdfFileReader(out) _, _, sig_field_ref = next(fields.enumerate_sig_fields(r)) sv_dict = sig_field_ref.get_object()['/SV'] assert sv_dict['/V'] == generic.NumberObject(2) del sv_dict['/V'] recovered_sv = fields.SigSeedValueSpec.from_pdf_object(sv_dict) # x509.Certificate doesn't have an __eq__ implementation apparently, # so for the purposes of the test, we replace them by byte dumps issuers1 = recovered_sv.cert.issuers issuers2 = sv.cert.issuers issuers1[0] = issuers1[0].dump() issuers2[0] = issuers2[0].dump() subjects1 = recovered_sv.cert.subjects subjects2 = sv.cert.subjects subjects1[0] = subjects1[0].dump() subjects2[0] = subjects2[0].dump() assert recovered_sv == sv
async def test_add_revinfo_wrong_subfilter(): sv = fields.SigSeedValueSpec(flags=fields.SigSeedValFlags.ADD_REV_INFO, add_rev_info=True) sig_field_spec = fields.SigFieldSpec( sig_field_name='Sig', seed_value_dict=sv, ) w = IncrementalPdfFileWriter(BytesIO(MINIMAL)) fields.append_signature_field(w, sig_field_spec) cms_writer = cms_embedder.PdfCMSEmbedder().write_cms(field_name='Sig', writer=w) next(cms_writer) # wrong subfilter: PAdES # but we do embed an (empty) RevInfoArchival attribute sig_obj = signers.SignatureObject(bytes_reserved=8192, subfilter=PADES) cms_writer.send(cms_embedder.SigObjSetup(sig_placeholder=sig_obj)) prep_digest, output = cms_writer.send( cms_embedder.SigIOSetup(md_algorithm='sha256', in_place=True)) cms_obj = await FROM_CA.async_sign( data_digest=prep_digest.document_digest, digest_algorithm='sha256', signed_attr_settings=PdfCMSSignedAttributes( # empty adobe_revinfo_attr=asn1_pdf.RevocationInfoArchival({'ocsp': []}))) await PdfTBSDocument.async_finish_signing(output, prep_digest, cms_obj) r = PdfFileReader(output) s = r.embedded_signatures[0] status = await async_validate_pdf_signature(s, dummy_ocsp_vc()) summary = status.pretty_print_details() assert status.intact and status.valid assert 'not satisfy the SV constraints' in summary assert 'requires subfilter' in status.seed_value_constraint_error.args[0] assert not status.seed_value_ok