This module describes and implements the low-level
protocol for embedding CMS payloads into PDF signature objects.
- class pyhanko.sign.signers.cms_embedder.PdfCMSEmbedder(new_field_spec: Optional[pyhanko.sign.fields.SigFieldSpec] = None)¶
Low-level class that handles embedding CMS objects into PDF signature fields.
It also takes care of appearance generation and DocMDP configuration, but does not otherwise offer any of the conveniences of
SigFieldSpecto use when creating new fields on-the-fly.
- write_cms(field_name: str, writer: pyhanko.pdf_utils.writer.BasePdfFileWriter, existing_fields_only=False)¶
New in version 0.3.0.
Changed in version 0.7.0: Digest wrapped in
PreparedByteRangeDigestin step 3;
outputreturned in step 3 instead of step 4.
This method returns a generator coroutine that controls the process of embedding CMS data into a PDF signature field. Can be used for both timestamps and regular signatures.
This is a very low-level interface that performs virtually no error checking, and is intended to be used in situations where the construction of the CMS object to be embedded is not under the caller’s control (e.g. a remote signer that produces full-fledged CMS objects).
The coroutine follows the following specific protocol.
First, it retrieves or creates the signature field to embed the CMS object in, and yields a reference to said field.
The caller should then send in a
SigObjSetupobject, which is subsequently processed by the coroutine. For convenience, the coroutine will then yield a reference to the signature dictionary (as embedded in the PDF writer).
Next, the caller should send a
SigIOSetupobject, describing how the resulting document should be hashed and written to the output. The coroutine will write the entire document with a placeholder region reserved for the signature and compute the document’s hash and yield it to the caller. It will then yield a
prepared_digest, outputtuple, where
PreparedByteRangeDigestobject containing the document digest and the relevant offsets, and
outputis the output stream to which the document to be signed was written.
From this point onwards, no objects may be changed or added to the
IncrementalPdfFileWritercurrently in use.
Finally, the caller should pass in a CMS object to place inside the signature dictionary. The CMS object can be supplied as a raw
bytesobject, or an
asn1crypto-style object. The coroutine’s final yield is the value of the signature dictionary’s
/Contentsentry, given as a hexadecimal string.
It is the caller’s own responsibility to ensure that enough room is available in the placeholder signature object to contain the final CMS object.
field_name – The name of the field to fill in. This should be a field of type
writer – An
IncrementalPdfFileWritercontaining the document to sign.
existing_fields_only – If
True, never create a new empty signature field to contain the signature. If
False, a new field may be created if no field matching
A generator coroutine implementing the protocol described above.
- class pyhanko.sign.signers.cms_embedder.SigMDPSetup(md_algorithm: str, certify: bool = False, field_lock: Union[pyhanko.sign.fields.FieldMDPSpec, NoneType] = None, docmdp_perms: Union[pyhanko.sign.fields.MDPPerm, NoneType] = None)¶
- md_algorithm: str¶
Message digest algorithm to write into the signature reference dictionary, if one is written at all.
It is the caller’s responsibility to make sure that this value agrees with the value embedded into the CMS object, and with the algorithm used to hash the document. The low-level
PdfCMSEmbedderAPI will simply take it at face value.
- certify: bool = False¶
Sign with an author (certification) signature, as opposed to an approval signature. A document can contain at most one such signature, and it must be the first one.
- field_lock: Optional[pyhanko.sign.fields.FieldMDPSpec] = None¶
Field lock information to write to the signature reference dictionary.
- docmdp_perms: Optional[pyhanko.sign.fields.MDPPerm] = None¶
DocMDP permissions to write to the signature reference dictionary.
- apply(sig_obj_ref, writer)¶
Apply the settings to a signature object.
This method is internal API.
- class pyhanko.sign.signers.cms_embedder.SigObjSetup(sig_placeholder: pyhanko.sign.signers.pdf_byterange.PdfSignedData, mdp_setup: Optional[pyhanko.sign.signers.cms_embedder.SigMDPSetup] = None, appearance_setup: Optional[pyhanko.sign.signers.cms_embedder.SigAppearanceSetup] = None)¶
Describes the signature dictionary to be embedded as the form field’s value.
- sig_placeholder: pyhanko.sign.signers.pdf_byterange.PdfSignedData¶
In particular, this determines the number of bytes to allocate for the CMS object.
- mdp_setup: Optional[pyhanko.sign.signers.cms_embedder.SigMDPSetup] = None¶
Optional DocMDP settings, see
- appearance_setup: Optional[pyhanko.sign.signers.cms_embedder.SigAppearanceSetup] = None¶
Optional appearance settings, see
- class pyhanko.sign.signers.cms_embedder.SigAppearanceSetup(style: pyhanko.stamp.BaseStampStyle, timestamp: datetime.datetime, name: str, text_params: Optional[dict] = None)¶
Signature appearance configuration.
- timestamp: datetime.datetime¶
Timestamp to show in the signature appearance.
- name: str¶
Signer name to show in the signature appearance.
- text_params: dict = None¶
Additional text interpolation parameters to pass to the underlying stamp style.
- apply(sig_annot, writer)¶
Apply the settings to an annotation.
This method is internal API.
- class pyhanko.sign.signers.cms_embedder.SigIOSetup(md_algorithm: str, in_place: bool = False, chunk_size: int = 4096, output: Optional[IO] = None)¶
I/O settings for writing signed PDF documents.
Objects of this type are used in the penultimate phase of the
- md_algorithm: str¶
Message digest algorithm to use to compute the document hash. It should be supported by pyca/cryptography.
This is also the message digest algorithm that should appear in the corresponding
signerInfoentry in the CMS object that ends up being embedded in the signature field.
- in_place: bool = False¶
Sign the input in-place. If
False, write output to a
outputif the latter is not
- chunk_size: int = 4096¶
Size of the internal buffer (in bytes) used to feed data to the message digest function if the input stream does not support
- output: Optional[IO] = None¶
Write the output to the specified output stream. If
None, write to a new
BytesIOobject. Default is