pyhanko.sign.diff_analysis.rules package

Submodules

pyhanko.sign.diff_analysis.rules.file_structure_rules module

class pyhanko.sign.diff_analysis.rules.file_structure_rules.CatalogModificationRule(ignored_keys=None)

Bases: QualifiedWhitelistRule

Rule that adjudicates modifications to the document catalog.

Parameters:

ignored_keys

Values in the document catalog that may change between revisions. The default ones are /AcroForm, /DSS, /Extensions, /Metadata, /MarkInfo and /Version.

Checking for /AcroForm, /DSS and /Metadata is delegated to FormUpdatingRule, DSSCompareRule and MetadataUpdateRule, respectively.

apply_qualified(old: HistoricalResolver, new: HistoricalResolver) Iterable[Tuple[ModificationLevel, ReferenceUpdate]]

Apply the rule to the changes between two revisions.

Parameters:
  • old – The older, base revision.

  • new – The newer revision to be vetted.

class pyhanko.sign.diff_analysis.rules.file_structure_rules.ObjectStreamRule

Bases: WhitelistRule

Rule that allows object streams to be added.

Note that this rule only whitelists the object streams themselves (provided they do not override any existing objects, obviously), not the objects in them.

apply(old: HistoricalResolver, new: HistoricalResolver) Iterable[ReferenceUpdate]

Apply the rule to the changes between two revisions.

Parameters:
  • old – The older, base revision.

  • new – The newer revision to be vetted.

class pyhanko.sign.diff_analysis.rules.file_structure_rules.XrefStreamRule

Bases: WhitelistRule

Rule that allows new cross-reference streams to be defined.

apply(old: HistoricalResolver, new: HistoricalResolver) Iterable[ReferenceUpdate]

Apply the rule to the changes between two revisions.

Parameters:
  • old – The older, base revision.

  • new – The newer revision to be vetted.

pyhanko.sign.diff_analysis.rules.form_field_rules module

class pyhanko.sign.diff_analysis.rules.form_field_rules.DSSCompareRule

Bases: WhitelistRule

Rule that allows changes to the document security store (DSS).

This rule will validate the structure of the DSS quite rigidly, and will raise SuspiciousModification whenever it encounters structural problems with the DSS. Similarly, modifications that remove structural items from the DSS also count as suspicious. However, merely removing individual OCSP responses, CRLs or certificates when they become irrelevant is permitted. This is also allowed by PAdES.

apply(old: HistoricalResolver, new: HistoricalResolver) Iterable[ReferenceUpdate]

Apply the rule to the changes between two revisions.

Parameters:
  • old – The older, base revision.

  • new – The newer revision to be vetted.

class pyhanko.sign.diff_analysis.rules.form_field_rules.SigFieldCreationRule(approve_widget_bindings=True, allow_new_visible_after_certify=False)

Bases: FieldMDPRule

This rule allows signature fields to be created at the root of the form hierarchy, but disallows the creation of other types of fields. It also disallows field deletion.

In addition, this rule will allow newly created signature fields to attach themselves as widget annotations to pages.

The creation of invisible signature fields is considered a modification at level ModificationLevel.LTA_UPDATES, but appearance-related changes will be qualified with ModificationLevel.FORM_FILLING.

Parameters:
  • allow_new_visible_after_certify – Creating new visible signature fields is disallowed after certification signatures by default; this is stricter than Acrobat. Set this parameter to True to disable this check.

  • approve_widget_bindings – Set to False to reject new widget annotation registrations associated with approved new fields.

apply(context: FieldComparisonContext) Iterable[Tuple[ModificationLevel, FormUpdate]]

Apply the rule to the given FieldComparisonContext.

Parameters:

context – The context of this form revision evaluation, given as an instance of FieldComparisonContext.

class pyhanko.sign.diff_analysis.rules.form_field_rules.SigFieldModificationRule(allow_in_place_appearance_stream_changes: bool = True, always_modifiable=None, value_update_keys=None)

Bases: BaseFieldModificationRule

This rule allows signature fields to be filled in, and set an appearance if desired. Deleting values from signature fields is disallowed, as is modifying signature fields that already contain a signature.

This rule will take field locks into account if the FieldComparisonContext includes a FieldMDPSpec.

For (invisible) document timestamps, this is allowed at ModificationLevel.LTA_UPDATES, but in all other cases the modification level will be bumped to ModificationLevel.FORM_FILLING.

check_form_field(fq_name: str, spec: FieldComparisonSpec, context: FieldComparisonContext) Iterable[Tuple[ModificationLevel, FormUpdate]]

Investigate updates to a particular form field. This function is called by apply() for every form field in the new revision.

Parameters:
  • fq_name – The fully qualified name of the form field.j

  • spec – The FieldComparisonSpec object describing the old state of the field in relation to the new state.

  • context – The full FieldComparisonContext that is currently being evaluated.

Returns:

An iterable yielding FormUpdate objects qualified with an appropriate ModificationLevel.

class pyhanko.sign.diff_analysis.rules.form_field_rules.GenericFieldModificationRule(allow_in_place_appearance_stream_changes: bool = True, always_modifiable=None, value_update_keys=None)

Bases: BaseFieldModificationRule

This rule allows non-signature form fields to be modified at ModificationLevel.FORM_FILLING.

This rule will take field locks into account if the FieldComparisonContext includes a FieldMDPSpec.

check_form_field(fq_name: str, spec: FieldComparisonSpec, context: FieldComparisonContext) Iterable[Tuple[ModificationLevel, FormUpdate]]

Investigate updates to a particular form field. This function is called by apply() for every form field in the new revision.

Parameters:
  • fq_name – The fully qualified name of the form field.j

  • spec – The FieldComparisonSpec object describing the old state of the field in relation to the new state.

  • context – The full FieldComparisonContext that is currently being evaluated.

Returns:

An iterable yielding FormUpdate objects qualified with an appropriate ModificationLevel.

class pyhanko.sign.diff_analysis.rules.form_field_rules.BaseFieldModificationRule(allow_in_place_appearance_stream_changes: bool = True, always_modifiable=None, value_update_keys=None)

Bases: FieldMDPRule

Base class that implements some boilerplate to validate modifications to individual form fields.

compare_fields(spec: FieldComparisonSpec) bool

Helper method to compare field dictionaries.

Parameters:

spec – The current FieldComparisonSpec.

Returns:

True if the modifications are permissible even when the field is locked, False otherwise. If keys beyond those in value_update_keys are changed, a SuspiciousModification is raised.

apply(context: FieldComparisonContext) Iterable[Tuple[ModificationLevel, FormUpdate]]

Apply the rule to the given FieldComparisonContext.

Parameters:

context – The context of this form revision evaluation, given as an instance of FieldComparisonContext.

check_form_field(fq_name: str, spec: FieldComparisonSpec, context: FieldComparisonContext) Iterable[Tuple[ModificationLevel, FormUpdate]]

Investigate updates to a particular form field. This function is called by apply() for every form field in the new revision.

Parameters:
  • fq_name – The fully qualified name of the form field.j

  • spec – The FieldComparisonSpec object describing the old state of the field in relation to the new state.

  • context – The full FieldComparisonContext that is currently being evaluated.

Returns:

An iterable yielding FormUpdate objects qualified with an appropriate ModificationLevel.

pyhanko.sign.diff_analysis.rules.metadata_rules module

class pyhanko.sign.diff_analysis.rules.metadata_rules.DocInfoRule

Bases: WhitelistRule

Rule that allows the /Info dictionary in the trailer to be updated.

apply(old: HistoricalResolver, new: HistoricalResolver) Iterable[ReferenceUpdate]

Apply the rule to the changes between two revisions.

Parameters:
  • old – The older, base revision.

  • new – The newer revision to be vetted.

class pyhanko.sign.diff_analysis.rules.metadata_rules.MetadataUpdateRule(check_xml_syntax=True, always_refuse_stream_override=False)

Bases: WhitelistRule

Rule to adjudicate updates to the XMP metadata stream.

The content of the metadata isn’t actually validated in any significant way; this class only checks whether the XML is well-formed.

Parameters:
  • check_xml_syntax – Do a well-formedness check on the XML syntax. Default True.

  • always_refuse_stream_override

    Always refuse to override the metadata stream if its object ID existed in a prior revision, including if the new stream overrides the old metadata stream and the syntax check passes. Default False.

    Note

    In other situations, pyHanko will reject stream overrides on general principle, since combined with the fault-tolerance of some PDF readers, these can allow an attacker to manipulate parts of the signed content in subtle but significant ways.

    In case of the metadata stream, the risk is significantly mitigated thanks to the XML syntax check on both versions of the stream, but if you’re feeling extra paranoid, you can turn the default behaviour back on by setting always_refuse_stream_override to True.

static is_well_formed_xml(metadata_ref: Reference)

Checks whether the provided stream consists of well-formed XML data. Note that this does not perform any more advanced XML or XMP validation, the check is purely syntactic.

Parameters:

metadata_ref – A reference to a (purported) metadata stream.

Raises:

SuspiciousModification – if there are indications that the reference doesn’t point to an XML stream.

apply(old: HistoricalResolver, new: HistoricalResolver) Iterable[ReferenceUpdate]

Apply the rule to the changes between two revisions.

Parameters:
  • old – The older, base revision.

  • new – The newer revision to be vetted.

Module contents

Rule implementations for the standard difference analysis policy.