Skip to content

Commit

Permalink
Add timestamp CLI command
Browse files Browse the repository at this point in the history
Command for simply adding a document timestamp outside an LTA context
  • Loading branch information
MatthiasValvekens committed Dec 8, 2023
1 parent 9daa3e4 commit 34df1d6
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 1 deletion.
1 change: 0 additions & 1 deletion pyhanko/cli/_trust.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from typing import Iterable, Optional, TypeVar, Union

import click
from pyhanko_certvalidator import ValidationContext

from pyhanko.cli.config import CLIConfig
from pyhanko.cli.utils import logger, readable_file
Expand Down
52 changes: 52 additions & 0 deletions pyhanko/cli/commands/signing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,18 @@
from pyhanko.cli.commands.signing.plugin import command_from_plugin
from pyhanko.cli.commands.stamp import select_style
from pyhanko.cli.utils import parse_field_location_spec
from pyhanko.pdf_utils.incremental_writer import IncrementalPdfFileWriter
from pyhanko.sign import DEFAULT_SIGNER_KEY_USAGE, fields, signers
from pyhanko.sign.signers.pdf_byterange import BuildProps
from pyhanko.sign.timestamps import HTTPTimeStamper

from ..._ctx import CLIContext
from ...plugin_api import SigningCommandPlugin

__all__ = ['signing', 'addsig', 'register']

from ...runtime import pyhanko_exception_manager


@cli_root.group(help='sign PDFs and other files', name='sign')
def signing():
Expand Down Expand Up @@ -270,3 +274,51 @@ def _unavailable():
callback=_unavailable,
)
)


readable_file = click.Path(exists=True, readable=True, dir_okay=False)
writable_file = click.Path(writable=True, dir_okay=False)


@trust_options
@signing.command(name='timestamp', help='add timestamp to PDF')
@click.argument('infile', type=readable_file)
@click.argument('outfile', type=writable_file)
@click.option(
'--timestamp-url',
help='URL for timestamp server',
required=True,
type=str,
default=None,
)
@click.pass_context
def timestamp(
ctx,
infile,
outfile,
validation_context,
trust,
trust_replace,
other_certs,
timestamp_url,
):
with pyhanko_exception_manager():
vc_kwargs = build_vc_kwargs(
ctx.obj.config,
validation_context,
trust,
trust_replace,
other_certs,
retroactive_revinfo=True,
)
timestamper = HTTPTimeStamper(timestamp_url)
with open(infile, 'rb') as inf:
w = IncrementalPdfFileWriter(inf)
pdf_timestamper = signers.PdfTimeStamper(timestamper)
with open(outfile, 'wb') as outf:
pdf_timestamper.timestamp_pdf(
w,
'sha256',
validation_context=ValidationContext(**vc_kwargs),
output=outf,
)
29 changes: 29 additions & 0 deletions pyhanko_tests/cli_tests/test_cli_signing.py
Original file line number Diff line number Diff line change
Expand Up @@ -1205,3 +1205,32 @@ def test_cli_with_signature_dictionary_entries(cli_runner):

assert last_sign['/ContactInfo'] == 'www.pyhanko.com/verify'
assert last_sign['/Location'] == 'THIS-COMPUTER'


def test_cli_timestamp(pki_arch_name, timestamp_url, cli_runner, root_cert):
if pki_arch_name == 'ed448':
# FIXME deal with this bug on the Certomancer end
pytest.skip("ed448 timestamping in Certomancer doesn't work")
cfg = {
'validation-contexts': {
'test': {
'trust': root_cert,
}
},
}

_write_config(cfg)
result = cli_runner.invoke(
cli_root,
[
'sign',
'timestamp',
'--validation-context',
'test',
'--timestamp-url',
timestamp_url,
INPUT_PATH,
SIGNED_OUTPUT_PATH,
],
)
assert not result.exception, result.output

0 comments on commit 34df1d6

Please sign in to comment.