pyhanko.sign.fields module

Utilities to deal with signature form fields and their properties in PDF files.

class pyhanko.sign.fields.SigFieldSpec(sig_field_name: str, on_page: int = 0, box: (<class 'int'>, <class 'int'>, <class 'int'>, <class 'int'>) = None, seed_value_dict: ~pyhanko.sign.fields.SigSeedValueSpec = None, field_mdp_spec: ~pyhanko.sign.fields.FieldMDPSpec = None, doc_mdp_update_value: ~pyhanko.sign.fields.MDPPerm = None, combine_annotation: bool = True, empty_field_appearance: bool = False, invis_sig_settings: ~pyhanko.sign.fields.InvisSigSettings = InvisSigSettings(set_print_flag=True, set_hidden_flag=False, box_out_of_bounds=False))

Bases: object

Description of a signature field to be created.

sig_field_name: str

Name of the signature field.

on_page: int = 0

Index of the page on which the signature field should be included (starting at 0). A negative number counts pages from the back of the document, with index -1 referring to the last page.

Note

This is essentially only relevant for visible signature fields, i.e. those that have a widget associated with them.

box: (<class 'int'>, <class 'int'>, <class 'int'>, <class 'int'>) = None

Bounding box of the signature field, if applicable.

Typically specified in ll_x, ll_y, ur_x, ur_y format, where ll_* refers to the lower left and ur_* to the upper right corner.

seed_value_dict: SigSeedValueSpec = None

Specification for the seed value dictionary, if applicable.

field_mdp_spec: FieldMDPSpec = None

Specification for the field lock dictionary, if applicable.

doc_mdp_update_value: MDPPerm = None

Value to use for the document modification policy associated with the signature in this field.

This value will be embedded into the field lock dictionary if specified, and is meaningless if field_mdp_spec is not specified.

Warning

DocMDP entries for approval signatures are a PDF 2.0 feature. Older PDF software will likely ignore this part of the field lock dictionary.

combine_annotation: bool = True

Flag controlling whether the field should be combined with its annotation dictionary; True by default.

empty_field_appearance: bool = False

Generate a neutral appearance stream for empty, visible signature fields. If False, an empty appearance stream will be put in.

Note

We use an empty appearance stream to satisfy the appearance requirements for widget annotations in ISO 32000-2. However, even when a nontrivial appearance stream is present on an empty signature field, many viewers will not use it to render the appearance of the empty field on-screen.

Instead, these viewers typically substitute their own native widget.

invis_sig_settings: InvisSigSettings = InvisSigSettings(set_print_flag=True, set_hidden_flag=False, box_out_of_bounds=False)

Advanced settings to control invisible signature field generation.

format_lock_dictionary() Optional[DictionaryObject]
class pyhanko.sign.fields.SigSeedValFlags(value)

Bases: Flag

Flags for the /Ff entry in the seed value dictionary for a signature field. These mark which of the constraints are to be strictly enforced, as opposed to optional ones.

Warning

The flags LEGAL_ATTESTATION and APPEARANCE_FILTER are processed in accordance with the specification when creating a signature, but support is nevertheless limited.

  • PyHanko does not support legal attestations at all, so given that the LEGAL_ATTESTATION requirement flag only restricts the legal attestations that can be used by the signer, pyHanko can safely ignore it when signing.

    On the other hand, since the validator is not aware of legal attestations either, it cannot validate signatures that make legal_attestations a mandatory constraint.

  • Since pyHanko does not define any named appearances, setting the APPEARANCE_FILTER flag and the appearance entry in the seed value dictionary will make pyHanko refuse to sign the document.

    When validating, the situation is different: since pyHanko has no way of knowing whether the signer used the named appearance imposed by the seed value dictionary, it will simply emit a warning and continue validating the signature.

FILTER = 1

Makes the signature handler setting mandatory. PyHanko only supports /Adobe.PPKLite.

SUBFILTER = 2

See subfilters.

V = 4

See sv_dict_version.

REASONS = 8

See reasons.

LEGAL_ATTESTATION = 16

See legal_attestations.

ADD_REV_INFO = 32

See add_rev_info.

DIGEST_METHOD = 64

See digest_method.

LOCK_DOCUMENT = 128

See lock_document.

APPEARANCE_FILTER = 256

See appearance.

class pyhanko.sign.fields.SigCertConstraints(flags: ~pyhanko.sign.fields.SigCertConstraintFlags = SigCertConstraintFlags.None, subjects: ~typing.Optional[~typing.List[~asn1crypto.x509.Certificate]] = None, subject_dn: ~typing.Optional[~asn1crypto.x509.Name] = None, issuers: ~typing.Optional[~typing.List[~asn1crypto.x509.Certificate]] = None, info_url: ~typing.Optional[str] = None, url_type: ~pyhanko.pdf_utils.generic.NameObject = '/Browser', key_usage: ~typing.Optional[~typing.List[~pyhanko.sign.fields.SigCertKeyUsage]] = None)

Bases: object

This part of the seed value dictionary allows the document author to set constraints on the signer’s certificate.

See Table 235 in ISO 32000-1.

flags: SigCertConstraintFlags = 0

Enforcement flags. By default, all entries are optional.

subjects: List[Certificate] = None

Explicit list of certificates that can be used to sign a signature field.

subject_dn: Name = None

Certificate subject names that can be used to sign a signature field. Subject DN entries that are not mentioned are unconstrained.

issuers: List[Certificate] = None

List of issuer certificates that the signer certificate can be issued by. Note that these issuers do not need to be the direct issuer of the signer’s certificate; any descendant relationship will do.

info_url: str = None

Informational URL that should be opened when an appropriate certificate cannot be found (if url_type is /Browser, that is).

Note

PyHanko ignores this value, but we include it for compatibility.

url_type: NameObject = '/Browser'

Handler that should be used to open info_url. /Browser is the only implementation-independent value.

key_usage: List[SigCertKeyUsage] = None

Specify the key usage extensions that should (or should not) be present on the signer’s certificate.

classmethod from_pdf_object(pdf_dict)

Read a PDF dictionary into a SigCertConstraints object.

Parameters

pdf_dict – A DictionaryObject.

Returns

A SigCertConstraints object.

as_pdf_object()

Render this SigCertConstraints object to a PDF dictionary.

Returns

A DictionaryObject.

satisfied_by(signer: Certificate, validation_path: Optional[ValidationPath])

Evaluate whether a signing certificate satisfies the required constraints of this SigCertConstraints object.

Parameters
  • signer – The candidate signer’s certificate.

  • validation_path – Validation path of the signer’s certificate.

Raises

UnacceptableSignerError – Raised if the conditions are not met.

class pyhanko.sign.fields.SigSeedValueSpec(flags: ~pyhanko.sign.fields.SigSeedValFlags = SigSeedValFlags.None, reasons: ~typing.Optional[~typing.List[str]] = None, timestamp_server_url: ~typing.Optional[str] = None, timestamp_required: bool = False, cert: ~typing.Optional[~pyhanko.sign.fields.SigCertConstraints] = None, subfilters: ~typing.Optional[~typing.List[~pyhanko.sign.fields.SigSeedSubFilter]] = None, digest_methods: ~typing.Optional[~typing.List[str]] = None, add_rev_info: ~typing.Optional[bool] = None, seed_signature_type: ~typing.Optional[~pyhanko.sign.fields.SeedSignatureType] = None, sv_dict_version: ~typing.Optional[~typing.Union[~pyhanko.sign.fields.SeedValueDictVersion, int]] = None, legal_attestations: ~typing.Optional[~typing.List[str]] = None, lock_document: ~typing.Optional[~pyhanko.sign.fields.SeedLockDocument] = None, appearance: ~typing.Optional[str] = None)

Bases: object

Python representation of a PDF seed value dictionary.

flags: SigSeedValFlags = 0

Enforcement flags. By default, all entries are optional.

reasons: List[str] = None

Acceptable reasons for signing.

timestamp_server_url: str = None

RFC 3161 timestamp server endpoint suggestion.

timestamp_required: bool = False

Flags whether a timestamp is required. This flag is only meaningful if timestamp_server_url is specified.

cert: SigCertConstraints = None

Constraints on the signer’s certificate.

subfilters: List[SigSeedSubFilter] = None

Acceptable /SubFilter values.

digest_methods: List[str] = None

Acceptable digest methods.

add_rev_info: bool = None

Indicates whether revocation information should be embedded.

Warning

This flag exclusively refers to the Adobe-style revocation information embedded within the CMS object that is written to the signature field. PAdES-style revocation information that is saved to the document security store (DSS) does not satisfy the requirement. Additionally, the standard mandates that /SubFilter be equal to /adbe.pkcs7.detached if this flag is True.

seed_signature_type: SeedSignatureType = None

Specifies the type of signature that should occupy a signature field; this represents the /MDP entry in the seed value dictionary. See SeedSignatureType for details.

Caution

Since a certification-type signature is by definition the first signature applied to a document, compliance with this requirement cannot be cryptographically enforced.

sv_dict_version: Union[SeedValueDictVersion, int] = None

Specifies the compliance level required of a seed value dictionary processor. If None, pyHanko will compute an appropriate value.

Note

You may also specify this value directly as an integer. This covers potential future versions of the standard that pyHanko does not support out of the box.

legal_attestations: List[str] = None

Specifies the possible legal attestations that a certification signature occupying this signature field can supply. The corresponding flag in flags indicates whether this is a mandatory constraint.

Caution

Since legal_attestations is only relevant for certification signatures, compliance with this requirement cannot be reliably enforced. Regardless, since pyHanko’s validator is also unaware of legal attestation settings, it will refuse to validate signatures where this seed value constitutes a mandatory constraint.

Additionally, since pyHanko does not support legal attestation specifications at all, it vacuously satisfies the requirements of this entry no matter what, and will therefore ignore it when signing.

lock_document: SeedLockDocument = None

Tell the signer whether or not the document should be locked after signing this field; see SeedLockDocument for details.

The corresponding flag in flags indicates whether this constraint is mandatory.

appearance: str = None

Specify a named appearance to use when generating the signature. The corresponding flag in flags indicates whether this constraint is mandatory.

Caution

There is no standard registry of named appearances, so these constraints are not portable, and cannot be validated.

PyHanko currently does not define any named appearances.

as_pdf_object()

Render this SigSeedValueSpec object to a PDF dictionary.

Returns

A DictionaryObject.

classmethod from_pdf_object(pdf_dict)

Read from a seed value dictionary.

Parameters

pdf_dict – A DictionaryObject.

Returns

A SigSeedValueSpec object.

build_timestamper()

Return a timestamper object based on the timestamp_server_url attribute of this SigSeedValueSpec object.

Returns

A HTTPTimeStamper.

class pyhanko.sign.fields.SigCertConstraintFlags(value)

Bases: Flag

Flags for the /Ff entry in the certificate seed value dictionary for a dictionary field. These mark which of the constraints are to be strictly enforced, as opposed to optional ones.

Warning

While this enum records values for all flags, not all corresponding constraint types have been implemented yet.

SUBJECT = 1

See SigCertConstraints.subjects.

ISSUER = 2

See SigCertConstraints.issuers.

OID = 4

Currently not supported.

SUBJECT_DN = 8

See SigCertConstraints.subject_dn.

RESERVED = 16

Currently not supported (reserved).

KEY_USAGE = 32

See SigCertConstraints.key_usage.

URL = 64

See SigCertConstraints.info_url.

Note

As specified in the standard, this enforcement bit is supposed to be ignored by default. We include it for compatibility reasons.

UNSUPPORTED = 20

Flags for which the corresponding constraint is unsupported.

class pyhanko.sign.fields.SigSeedSubFilter(value)

Bases: Enum

Enum declaring all supported /SubFilter values.

ADOBE_PKCS7_DETACHED = '/adbe.pkcs7.detached'
PADES = '/ETSI.CAdES.detached'
ETSI_RFC3161 = '/ETSI.RFC3161'
class pyhanko.sign.fields.SeedValueDictVersion(value)

Bases: OrderedEnum

Specify the minimal compliance level for a seed value dictionary processor.

PDF_1_5 = 1

Require the reader to understand all keys defined in PDF 1.5.

PDF_1_7 = 2

Require the reader to understand all keys defined in PDF 1.7.

PDF_2_0 = 3

Require the reader to understand all keys defined in PDF 2.0.

class pyhanko.sign.fields.SeedLockDocument(value)

Bases: Enum

Provides a recommendation to the signer as to whether the document should be locked after signing. The corresponding flag in SigSeedValueSpec.flags determines whether this constraint is a required constraint.

LOCK = '/true'

Lock the document after signing.

DO_NOT_LOCK = '/false'

Lock the document after signing.

SIGNER_DISCRETION = '/auto'

Leave the decision up to the signer.

Note

This is functionally equivalent to not specifying any value.

class pyhanko.sign.fields.SigCertKeyUsage(must_have: Optional[KeyUsage] = None, forbidden: Optional[KeyUsage] = None)

Bases: object

Encodes the key usage bits that must (resp. must not) be active on the signer’s certificate.

Note

See § 4.2.1.3 in RFC 5280 and KeyUsage for more information on key usage extensions.

Note

The human-readable names of the key usage extensions are recorded in camelCase in RFC 5280, but this class uses the naming convention of KeyUsage in asn1crypto. The conversion is done by replacing camelCase with snake_case. For example, nonRepudiation becomes non_repudiation, and digitalSignature turns into digital_signature.

Note

This class is intended to closely replicate the definition of the KeyUsage entry Table 235 in ISO 32000-1. In particular, it does not provide a mechanism to deal with extended key usage extensions (cf. § 4.2.1.12 in RFC 5280).

Parameters
  • must_have – The KeyUsage object encoding the key usage extensions that must be present on the signer’s certificate.

  • forbidden – The KeyUsage object encoding the key usage extensions that must not be present on the signer’s certificate.

encode_to_sv_string()

Encode the key usage requirements in the format specified in the PDF specification.

Returns

A string.

classmethod read_from_sv_string(ku_str)

Parse a PDF KeyUsage string into an instance of SigCertKeyUsage. See Table 235 in ISO 32000-1.

Parameters

ku_str – A PDF KeyUsage string.

Returns

An instance of SigCertKeyUsage.

classmethod from_sets(must_have: Optional[Set[str]] = None, forbidden: Optional[Set[str]] = None)

Initialise a SigCertKeyUsage object from two sets.

Parameters
  • must_have – The key usage extensions that must be present on the signer’s certificate.

  • forbidden – The key usage extensions that must not be present on the signer’s certificate.

Returns

A SigCertKeyUsage object encoding these.

must_have_set() Set[str]

Return the set of key usage extensions that must be present on the signer’s certificate.

forbidden_set() Set[str]

Return the set of key usage extensions that must not be present on the signer’s certificate.

class pyhanko.sign.fields.MDPPerm(value)

Bases: OrderedEnum

Indicates a /DocMDP level.

Cf. Table 254 in ISO 32000-1.

NO_CHANGES = 1

No changes to the document are allowed.

Warning

This does not apply to DSS updates and the addition of document time stamps.

FILL_FORMS = 2

Form filling & signing is allowed.

ANNOTATE = 3

Form filling, signing and commenting are allowed.

Warning

Validating this /DocMDP level is not currently supported, but included in the list for completeness.

class pyhanko.sign.fields.FieldMDPAction(value)

Bases: Enum

Marker for the scope of a /FieldMDP policy.

ALL = '/All'

The policy locks all form fields.

INCLUDE = '/Include'

The policy locks all fields in the list (see FieldMDPSpec.fields).

EXCLUDE = '/Exclude'

The policy locks all fields except those specified in the list (see FieldMDPSpec.fields).

class pyhanko.sign.fields.FieldMDPSpec(action: FieldMDPAction, fields: Optional[List[str]] = None)

Bases: object

/FieldMDP policy description.

This class models both field lock dictionaries and /FieldMDP transformation parameters.

action: FieldMDPAction

Indicates the scope of the policy.

fields: Optional[List[str]] = None

Indicates the fields subject to the policy, unless action is FieldMDPAction.ALL.

as_pdf_object() DictionaryObject

Render this /FieldMDP policy description as a PDF dictionary.

Returns

A DictionaryObject.

as_transform_params() DictionaryObject

Render this /FieldMDP policy description as a PDF dictionary, ready for inclusion into the /TransformParams entry of a /FieldMDP dictionary associated with a signature object.

Returns

A DictionaryObject.

as_sig_field_lock() DictionaryObject

Render this /FieldMDP policy description as a PDF dictionary, ready for inclusion into the /Lock dictionary of a signature field.

Returns

A DictionaryObject.

classmethod from_pdf_object(pdf_dict) FieldMDPSpec

Read a PDF dictionary into a FieldMDPSpec object.

Parameters

pdf_dict – A DictionaryObject.

Returns

A FieldMDPSpec object.

is_locked(field_name: str) bool

Adjudicate whether a field should be locked by the policy described by this FieldMDPSpec object.

Parameters

field_name – The name of a form field.

Returns

True if the field should be locked, False otherwise.

class pyhanko.sign.fields.SignatureFormField(field_name, *, box=None, include_on_page=None, combine_annotation=True, invis_settings: InvisSigSettings = InvisSigSettings(set_print_flag=True, set_hidden_flag=False, box_out_of_bounds=False), annot_flags=None)

Bases: DictionaryObject

register_widget_annotation(writer: BasePdfFileWriter, sig_field_ref)
class pyhanko.sign.fields.InvisSigSettings(set_print_flag: bool = True, set_hidden_flag: bool = False, box_out_of_bounds: bool = False)

Bases: object

Invisible signature widget generation settings.

These settings exist because there is no real way of including an untagged invisible signature in a document that complies with the requirements of both PDF/A-2 (or -3) and PDF/UA-1.

Compatibility with PDF/A (the default) requires the print flag to be set. Compatibility with PDF/UA requires the hidden flag to be set (which is banned in PDF/A) or the box to be outside the crop box.

set_print_flag: bool = True

Set the print flag. Required in PDF/A.

set_hidden_flag: bool = False

Set the hidden flag. Required in PDF/UA.

box_out_of_bounds: bool = False

Put the box out of bounds (technically, this just makes the box zero-sized with large negative coordinates).

This is a hack to get around the fact that PDF/UA requires the hidden flag to be set on all in-bounds untagged annotations, and some validators consider [0, 0, 0, 0] to be an in-bounds rectangle if (0, 0) is a point that falls within the crop box.

pyhanko.sign.fields.enumerate_sig_fields(handler: PdfHandler, filled_status=None)

Enumerate signature fields.

Parameters
  • handler – The PdfHandler to operate on.

  • filled_status – Optional boolean. If True (resp. False) then all filled (resp. empty) fields are returned. If left None (the default), then all fields are returned.

Returns

A generator producing signature fields.

pyhanko.sign.fields.append_signature_field(pdf_out: BasePdfFileWriter, sig_field_spec: SigFieldSpec)

Append signature fields to a PDF file.

Parameters
  • pdf_out – Incremental writer to house the objects.

  • sig_field_spec – A SigFieldSpec object describing the signature field to add.

pyhanko.sign.fields.ensure_sig_flags(writer: BasePdfFileWriter, lock_sig_flags: bool = True)

Ensure the SigFlags setting is present in the AcroForm dictionary.

Parameters
  • writer – A PDF writer.

  • lock_sig_flags – Whether to flag the document as append-only.

pyhanko.sign.fields.prepare_sig_field(sig_field_name, root, update_writer: BasePdfFileWriter, existing_fields_only=False, **kwargs)

Returns a tuple of a boolean and a reference to a signature field. The boolean is True if the field was created, and False otherwise.

Danger

This function is internal API.