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_delete_signature(): w = IncrementalPdfFileWriter(BytesIO(MINIMAL_TWO_FIELDS)) # first, we simply sign the two fields out = signers.sign_pdf(w, signers.PdfSignatureMetadata(field_name='Sig1'), signer=FROM_CA, existing_fields_only=True) w = IncrementalPdfFileWriter(out) out = signers.sign_pdf(w, signers.PdfSignatureMetadata(field_name='Sig2'), signer=FROM_CA, existing_fields_only=True) # after that, we add an incremental update that deletes the first signature # This should invalidate the remaining one. w = IncrementalPdfFileWriter(out) sig_fields = fields.enumerate_sig_fields(w) field_name, sig_obj, sig_field = next(sig_fields) assert field_name == 'Sig1' del sig_field.get_object()['/V'] w.mark_update(sig_field) out = BytesIO() w.write(out) r = PdfFileReader(out) s = r.embedded_signatures[0] assert s.field_name == 'Sig2' val_trusted_but_modified(s)
def list_sigfields(infile, skip_status): with pyhanko_exception_manager(): r = PdfFileReader(infile) field_info = fields.enumerate_sig_fields(r) for ix, (name, value, field_ref) in enumerate(field_info): if skip_status: print(name) continue print(f"{name}:{'EMPTY' if value is None else 'FILLED'}")
def _get_or_create_sigfield(field_name, pdf_out: BasePdfFileWriter, existing_fields_only, new_field_spec: Optional[SigFieldSpec] = None): root = pdf_out.root if field_name is None: if not existing_fields_only: raise SigningError('Not specifying a field name is only allowed ' 'when existing_fields_only=True') # most of the logic in prepare_sig_field has to do with preparing # for the potential addition of a new field. That is completely # irrelevant in this special case, so we might as well short circuit # things. field_created = False empty_fields = enumerate_sig_fields(pdf_out, filled_status=False) try: found_field_name, _, sig_field_ref = next(empty_fields) except StopIteration: raise SigningError('There are no empty signature fields.') others = ', '.join(fn for fn, _, _ in empty_fields if fn is not None) if others: raise SigningError( 'There are several empty signature fields. Please specify ' 'a field name. The options are %s, %s.' % (found_field_name, others)) else: # grab or create a sig field if new_field_spec is not None: sig_field_kwargs = { 'box': new_field_spec.box, 'include_on_page': pdf_out.find_page_for_modification(new_field_spec.on_page)[0], 'combine_annotation': new_field_spec.combine_annotation } else: sig_field_kwargs = {} field_created, sig_field_ref = prepare_sig_field( field_name, root, update_writer=pdf_out, existing_fields_only=existing_fields_only, **sig_field_kwargs) ensure_sig_flags(writer=pdf_out, lock_sig_flags=True) return field_created, sig_field_ref
def embedded_signatures(self): """ :return: The signatures embedded in this document, in signing order; see :class:`~pyhanko.sign.validation.EmbeddedPdfSignature`. """ if self._embedded_signatures is not None: return self._embedded_signatures from pyhanko.sign.fields import enumerate_sig_fields from pyhanko.sign.validation import EmbeddedPdfSignature sig_fields = enumerate_sig_fields(self, filled_status=True) result = sorted((EmbeddedPdfSignature(self, sig_field) for _, sig_obj, sig_field in sig_fields), key=lambda emb: emb.signed_revision) self._embedded_signatures = result return result
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
def test_enumerate_empty(): with pytest.raises(StopIteration): next(fields.enumerate_sig_fields(PdfFileReader(BytesIO(MINIMAL))))