diff --git a/pyhanko/cli/commands/signing/plugin.py b/pyhanko/cli/commands/signing/plugin.py index d3b76324..af8caf3e 100644 --- a/pyhanko/cli/commands/signing/plugin.py +++ b/pyhanko/cli/commands/signing/plugin.py @@ -118,6 +118,7 @@ def _callback_logic( w, existing_fields_only=cli_ctx.existing_fields_only, appearance_text_params=get_text_params(ctx), + strict=not cli_ctx.lenient, ) with open(outfile, 'wb') as outf: diff --git a/pyhanko/sign/signers/functions.py b/pyhanko/sign/signers/functions.py index f7c66293..f2582560 100644 --- a/pyhanko/sign/signers/functions.py +++ b/pyhanko/sign/signers/functions.py @@ -30,6 +30,7 @@ def sign_pdf( bytes_reserved=None, in_place=False, output=None, + strict=True, ): """ Thin convenience wrapper around :meth:`.PdfSigner.sign_pdf`. @@ -63,6 +64,9 @@ def sign_pdf( Write the output to the specified output stream. If ``None``, write to a new :class:`.BytesIO` object. Default is ``None``. + :param strict: + If ``True``, enforce stricter validation of the input PDF. + Default is ``True``. :return: The output stream containing the signed output. """ @@ -85,6 +89,7 @@ def sign_pdf( bytes_reserved=bytes_reserved, in_place=in_place, output=output, + strict=strict, ) @@ -98,6 +103,7 @@ async def async_sign_pdf( bytes_reserved=None, in_place=False, output=None, + strict=True, ): """ Thin convenience wrapper around :meth:`.PdfSigner.async_sign_pdf`. @@ -131,6 +137,9 @@ async def async_sign_pdf( Write the output to the specified output stream. If ``None``, write to a new :class:`.BytesIO` object. Default is ``None``. + :param strict: + If ``True``, enforce stricter validation of the input PDF. + Default is ``True``. :return: The output stream containing the signed output. """ @@ -153,6 +162,7 @@ async def async_sign_pdf( bytes_reserved=bytes_reserved, in_place=in_place, output=output, + strict=strict, ) diff --git a/pyhanko/sign/signers/pdf_signer.py b/pyhanko/sign/signers/pdf_signer.py index cb81750e..363a2e8c 100644 --- a/pyhanko/sign/signers/pdf_signer.py +++ b/pyhanko/sign/signers/pdf_signer.py @@ -1486,6 +1486,7 @@ def sign_pdf( in_place=False, output=None, chunk_size=misc.DEFAULT_CHUNK_SIZE, + strict=True, ): """ .. versionchanged:: 0.9.0 @@ -1518,6 +1519,9 @@ def sign_pdf( Size of the internal buffer (in bytes) used to feed data to the message digest function if the input stream does not support ``memoryview``. + :param strict: + If ``True``, enforce stricter validation of the input PDF file. + Default is ``True``. :return: The output stream containing the signed data. """ @@ -1530,6 +1534,7 @@ def sign_pdf( in_place=in_place, output=output, chunk_size=chunk_size, + strict=strict, ) ) return result @@ -1544,6 +1549,7 @@ async def async_sign_pdf( in_place=False, output=None, chunk_size=misc.DEFAULT_CHUNK_SIZE, + strict=True, ): """ .. versionadded:: 0.9.0 @@ -1575,6 +1581,9 @@ async def async_sign_pdf( Size of the internal buffer (in bytes) used to feed data to the message digest function if the input stream does not support ``memoryview``. + :param strict: + If ``True``, enforce stricter validation of the input PDF file. + Default is ``True``. :return: The output stream containing the signed data. """ @@ -1614,7 +1623,7 @@ async def async_sign_pdf( ) await post_signing_doc.post_signature_processing( - res_output, chunk_size=chunk_size + res_output, chunk_size=chunk_size, strict=strict ) # we put the finalisation step after the DSS manipulations, since # otherwise we'd also run into issues with non-seekable output buffers @@ -2734,6 +2743,7 @@ async def async_finish_signing( post_sign_instr: Optional[PostSignInstructions] = None, validation_context: Optional[ValidationContext] = None, chunk_size=misc.DEFAULT_CHUNK_SIZE, + strict=True, ): """ Finish signing after obtaining a CMS object from an external source, and @@ -2760,6 +2770,9 @@ async def async_finish_signing( Size of the internal buffer (in bytes) used to feed data to the message digest function if the input stream does not support ``memoryview``. + :param strict: + If ``True``, enforce strict validation of the input stream. + Default is ``True``. """ # TODO at this point, the output stream no longer needs to be readable, # just seekable, unless there's a timestamp requirement. @@ -2773,7 +2786,7 @@ async def async_finish_signing( validation_context=validation_context, ) await post_sign.post_signature_processing( - rw_output, chunk_size=chunk_size + rw_output, chunk_size=chunk_size, strict=strict ) @@ -2795,7 +2808,7 @@ def __init__( self.validation_context = validation_context async def post_signature_processing( - self, output: IO, chunk_size=misc.DEFAULT_CHUNK_SIZE + self, output: IO, chunk_size=misc.DEFAULT_CHUNK_SIZE, strict=True ): """ Handle DSS updates and LTA timestamps, if applicable. @@ -2851,10 +2864,11 @@ async def post_signature_processing( output_stream=output, **dss_op_kwargs, file_credential=instr.file_credential, + strict=strict, ) if timestamper is not None: # append a document timestamp after the DSS update - w = IncrementalPdfFileWriter(output) + w = IncrementalPdfFileWriter(output, strict=strict) if ( w.security_handler is not None and instr.file_credential is not None diff --git a/pyhanko/sign/validation/dss.py b/pyhanko/sign/validation/dss.py index d33eae1a..b566bed8 100644 --- a/pyhanko/sign/validation/dss.py +++ b/pyhanko/sign/validation/dss.py @@ -490,6 +490,7 @@ def add_dss( force_write: bool = False, embed_roots: bool = True, file_credential: Optional[SerialisedCredential] = None, + strict: bool = True, ): """ Wrapper around :meth:`supply_dss_in_writer`. @@ -535,8 +536,11 @@ def add_dss( .. versionadded:: 0.13.0 Serialised file credential, to update encrypted files. + :param strict: + If ``True``, enforce strict validation of the input stream. + Default is ``True``. """ - pdf_out = IncrementalPdfFileWriter(output_stream) + pdf_out = IncrementalPdfFileWriter(output_stream, strict=strict) if pdf_out.security_handler is not None and file_credential is not None: pdf_out.security_handler.authenticate(file_credential) dss = cls.supply_dss_in_writer(