Example #1
0
def test_append_sig_field_acro_update():
    # test different configurations of the AcroForm
    w = PdfFileWriter()
    w.root['/AcroForm'] = generic.DictionaryObject(
        {pdf_name('/Fields'): generic.ArrayObject()})
    w.insert_page(simple_page(w, 'Hello world'))
    out = BytesIO()
    w.write(out)
    out.seek(0)

    sp = fields.SigFieldSpec('InvisibleSig')
    w = IncrementalPdfFileWriter(out)
    fields.append_signature_field(w, sp)
    assert len(w.root['/AcroForm']['/Fields']) == 1

    w = PdfFileWriter()
    # Technically, this is not standards-compliant, but our routine
    # shouldn't care
    w.root['/AcroForm'] = generic.DictionaryObject()
    w.insert_page(simple_page(w, 'Hello world'))
    out = BytesIO()
    w.write(out)
    out.seek(0)

    sp = fields.SigFieldSpec('InvisibleSig')
    w = IncrementalPdfFileWriter(out)
    with pytest.raises(ValueError):
        fields.append_signature_field(w, sp)
Example #2
0
def test_sign_with_new_field_spec():

    w = IncrementalPdfFileWriter(BytesIO(MINIMAL))
    spec = fields.SigFieldSpec(sig_field_name='Sig1', box=(20, 20, 80, 40))
    out = signers.sign_pdf(w,
                           signers.PdfSignatureMetadata(field_name='Sig1'),
                           signer=FROM_CA,
                           new_field_spec=spec)
    r = PdfFileReader(out)
    s = r.embedded_signatures[0]
    assert s.field_name == 'Sig1'
    assert '/AP' in s.sig_field

    w = IncrementalPdfFileWriter(BytesIO(MINIMAL))
    spec = fields.SigFieldSpec(sig_field_name='Sig1', box=(20, 20, 80, 40))

    with pytest.raises(SigningError):
        signers.sign_pdf(w,
                         signers.PdfSignatureMetadata(field_name='Sig2'),
                         signer=FROM_CA,
                         new_field_spec=spec)

    with pytest.raises(SigningError):
        signers.sign_pdf(w,
                         signers.PdfSignatureMetadata(field_name='Sig1'),
                         signer=FROM_CA,
                         new_field_spec=spec,
                         existing_fields_only=True)
Example #3
0
def test_rogue_backreferences():
    w = IncrementalPdfFileWriter(BytesIO(MINIMAL))
    # intentionally refer back to the contents of the first page
    w.root['/DSS'] = w.root['/Pages']['/Kids'][0].get_object().raw_get(
        '/Contents')
    w.update_root()
    meta = signers.PdfSignatureMetadata(field_name='Sig1', )
    out = signers.sign_pdf(w, meta, signer=FROM_CA)

    # pretend to add a new form field, but actually secretly do a page
    #  tree modification.
    sp = fields.SigFieldSpec('SigNew',
                             box=(10, 74, 140, 134),
                             doc_mdp_update_value=fields.MDPPerm.FILL_FORMS)
    w = IncrementalPdfFileWriter(out)
    fields.append_signature_field(w, sp)
    w.write_in_place()

    w = IncrementalPdfFileWriter(out)
    contents_ref = w.root['/Pages']['/Kids'][0].get_object().raw_get(
        '/Contents')
    content_stream: generic.StreamObject = contents_ref.get_object()
    content_stream._data = content_stream.data + b"q Q"
    content_stream._encoded_data = None
    w.mark_update(contents_ref)
    w.write_in_place()

    r = PdfFileReader(out)
    emb = r.embedded_signatures[0]
    emb.compute_integrity_info()
    assert isinstance(emb.diff_result, SuspiciousModification)
Example #4
0
def test_pages_kids_tamper(bogus_kids, indirectify):
    w = IncrementalPdfFileWriter(BytesIO(MINIMAL))

    # sign, then fill
    meta = signers.PdfSignatureMetadata(field_name='Sig1')
    out = signers.sign_pdf(w, meta, signer=FROM_CA)
    w = IncrementalPdfFileWriter(out)

    # add an empty sig field to trigger the annotation parsing logic
    # in the difference analysis tool
    fields.append_signature_field(
        w, sig_field_spec=fields.SigFieldSpec(sig_field_name="Extra"))
    page_root = w.root['/Pages']
    if indirectify:
        bogus_kids = generic.ArrayObject(map(w.add_object, bogus_kids))
    if bogus_kids is not None:
        page_root['/Kids'] = bogus_kids
    else:
        del page_root['/Kids']
    w.update_container(page_root)
    out = BytesIO()
    w.write(out)

    r = PdfFileReader(out)
    s = r.embedded_signatures[0]
    assert s.field_name == 'Sig1'
    val_trusted_but_modified(s)
Example #5
0
def test_double_sig_add_visible_field():
    w = IncrementalPdfFileWriter(BytesIO(MINIMAL_ONE_FIELD))
    out = signers.sign_pdf(w,
                           signers.PdfSignatureMetadata(
                               field_name='Sig1',
                               certify=True,
                               docmdp_permissions=fields.MDPPerm.FILL_FORMS),
                           signer=FROM_CA)

    # create a new signature field after signing
    w = IncrementalPdfFileWriter(out)

    sp = fields.SigFieldSpec('SigNew', box=(10, 74, 140, 134))
    fields.append_signature_field(w, sp)
    out = signers.sign_pdf(
        w,
        signers.PdfSignatureMetadata(field_name='SigNew'),
        signer=FROM_CA,
    )
    r = PdfFileReader(out)
    s = r.embedded_signatures[0]
    assert s.field_name == 'Sig1'
    status = val_trusted(s, extd=True)
    assert status.modification_level == ModificationLevel.FORM_FILLING
    assert status.docmdp_ok

    s = r.embedded_signatures[1]
    assert s.field_name == 'SigNew'
    val_trusted(s)
Example #6
0
def test_double_sig_add_field_annots_indirect():
    w = IncrementalPdfFileWriter(BytesIO(MINIMAL_ONE_FIELD))
    out = signers.sign_pdf(
        w,
        signers.PdfSignatureMetadata(
            field_name='Sig1',
            certify=True,
            docmdp_permissions=fields.MDPPerm.FILL_FORMS),
        signer=FROM_CA,
    )

    # create a new signature field after signing
    w = IncrementalPdfFileWriter(out)
    # ... but first make the /Annots entry of the first page an indirect one
    first_page = w.root['/Pages']['/Kids'][0]
    annots_copy = generic.ArrayObject(first_page['/Annots'])
    first_page['/Annots'] = annots_ref = w.add_object(annots_copy)
    annots_copy.container_ref = annots_ref
    w.update_container(first_page)
    out = signers.sign_pdf(w,
                           signers.PdfSignatureMetadata(field_name='SigNew'),
                           signer=FROM_CA,
                           new_field_spec=fields.SigFieldSpec(
                               sig_field_name='SigNew', box=(10, 10, 10, 10)))

    r = PdfFileReader(out)
    s = r.embedded_signatures[0]
    assert s.field_name == 'Sig1'
    status = val_trusted(s, extd=True)
    assert status.modification_level == ModificationLevel.FORM_FILLING
    assert status.docmdp_ok

    s = r.embedded_signatures[1]
    assert s.field_name == 'SigNew'
    val_trusted(s)
Example #7
0
def parse_field_location_spec(spec, require_full_spec=True):
    if spec is None:
        if require_full_spec:
            raise click.ClickException(
                "A signature field spec was not provided.")
        return None, None
    try:
        page, box, name = spec.split('/')
    except ValueError:
        if require_full_spec:
            raise click.ClickException(
                "Sig field spec should be of the form PAGE/X1,Y1,X2,Y2/NAME.")
        else:
            # interpret the entire string as a field name
            return spec, None

    page_ix = _index_page(page)

    try:
        x1, y1, x2, y2 = map(int, box.split(','))
    except ValueError:
        raise click.ClickException(
            "Sig field parameters X1,Y1,X2,Y2 should be four integers.")

    return name, fields.SigFieldSpec(sig_field_name=name,
                                     on_page=page_ix,
                                     box=(x1, y1, x2, y2))
Example #8
0
def field_with_lock_sp(include_docmdp):
    return fields.SigFieldSpec(
        'SigNew',
        box=(10, 74, 140, 134),
        field_mdp_spec=fields.FieldMDPSpec(fields.FieldMDPAction.INCLUDE,
                                           fields=['blah']),
        doc_mdp_update_value=(fields.MDPPerm.NO_CHANGES
                              if include_docmdp else None))
Example #9
0
def prepare_sv_field(sv_spec, add_field_lock=False):
    w = IncrementalPdfFileWriter(BytesIO(MINIMAL))

    if add_field_lock:
        sp = fields.SigFieldSpec(
            'Sig',
            seed_value_dict=sv_spec,
            field_mdp_spec=fields.FieldMDPSpec(fields.FieldMDPAction.INCLUDE,
                                               fields=['blah']),
            doc_mdp_update_value=fields.MDPPerm.NO_CHANGES)
    else:
        sp = fields.SigFieldSpec('Sig', seed_value_dict=sv_spec)
    fields.append_signature_field(w, sp)
    out = BytesIO()
    w.write(out)
    out.seek(0)
    return out
Example #10
0
def test_sign_with_empty_kids():
    w = IncrementalPdfFileWriter(BytesIO(MINIMAL))
    fields.append_signature_field(
        w, fields.SigFieldSpec(
            sig_field_name='Sig1', combine_annotation=False,
            box=(20, 20, 80, 40)
        )
    )
    w.root['/AcroForm']['/Fields'][0]['/Kids'] = generic.ArrayObject()
    meta = signers.PdfSignatureMetadata(field_name='Sig1')

    with pytest.raises(SigningError, match="Failed to access.*annot.*"):
        signers.sign_pdf(w, meta, signer=FROM_CA)
Example #11
0
def test_append_acroform_no_fields():
    w = PdfFileWriter()
    # Technically, this is not standards-compliant, but our routine
    # shouldn't care
    w.root['/AcroForm'] = generic.DictionaryObject()
    w.insert_page(simple_page(w, 'Hello world'))
    out = BytesIO()
    w.write(out)
    out.seek(0)

    sp = fields.SigFieldSpec('InvisibleSig')
    w = IncrementalPdfFileWriter(out)
    with pytest.raises(PdfError, match="has no /Fields"):
        fields.append_signature_field(w, sp)
Example #12
0
def test_append_sig_field_acro_update():
    # test different configurations of the AcroForm
    w = PdfFileWriter()
    w.root['/AcroForm'] = generic.DictionaryObject({
        pdf_name('/Fields'): generic.ArrayObject()
    })
    w.insert_page(simple_page(w, 'Hello world'))
    out = BytesIO()
    w.write(out)
    out.seek(0)

    sp = fields.SigFieldSpec('InvisibleSig')
    w = IncrementalPdfFileWriter(out)
    fields.append_signature_field(w, sp)
    assert len(w.root['/AcroForm']['/Fields']) == 1
Example #13
0
def test_append_sigfield_trivial_ap():
    buf = BytesIO(MINIMAL)
    w = IncrementalPdfFileWriter(buf)
    spec = fields.SigFieldSpec(sig_field_name='Sig1', box=(20, 20, 80, 40))
    fields.append_signature_field(w, sig_field_spec=spec)
    w.write_in_place()

    r = PdfFileReader(buf)

    pg1 = r.root['/Pages']['/Kids'][0]
    assert '/Annots' in pg1
    assert len(pg1['/Annots']) == 1
    annot = pg1['/Annots'][0]
    assert '/AP' in annot
    assert annot['/AP']['/N'].data == b''
Example #14
0
def test_append_sigfield_second_page():
    buf = BytesIO(MINIMAL_TWO_PAGES)
    w = IncrementalPdfFileWriter(buf)
    fields.append_signature_field(w, fields.SigFieldSpec('Sig1', on_page=1))
    w.write_in_place()

    r = PdfFileReader(buf)

    pg1 = r.root['/Pages']['/Kids'][0]
    assert '/Annots' not in pg1

    pg2 = r.root['/Pages']['/Kids'][1]
    assert '/Annots' in pg2
    assert len(pg2['/Annots']) == 1
    assert pg2['/Annots'][0]['/T'] == 'Sig1'
Example #15
0
def test_append_visible_sig_field():
    w = IncrementalPdfFileWriter(BytesIO(MINIMAL))

    sp = fields.SigFieldSpec('VisibleSig', box=(10, 0, 50, 8))
    fields.append_signature_field(w, sp)
    assert len(w.root['/AcroForm']['/Fields']) == 1
    out = BytesIO()
    w.write(out)
    out.seek(0)
    w = IncrementalPdfFileWriter(out)
    with pytest.raises(PdfWriteError):
        fields.append_signature_field(w, sp)

    w = IncrementalPdfFileWriter(BytesIO(MINIMAL_TWO_FIELDS))
    fields.append_signature_field(w, sp)
    assert len(w.root['/AcroForm']['/Fields']) == 3
Example #16
0
def test_append_sig_field_with_simple_sv():
    w = IncrementalPdfFileWriter(BytesIO(MINIMAL))

    sv = fields.SigSeedValueSpec(
        reasons=['a', 'b', 'c'],
        cert=fields.SigCertConstraints(
            subject_dn=FROM_CA.signing_cert.subject,
            issuers=[INTERM_CERT],
            subjects=[FROM_CA.signing_cert],
            key_usage=[
                fields.SigCertKeyUsage.from_sets(
                    {'digital_signature', 'non_repudiation'},
                    {'key_agreement'})
            ]),
        digest_methods=['ssh256'],
        add_rev_info=True,
        subfilters=[fields.SigSeedSubFilter.ADOBE_PKCS7_DETACHED],
        timestamp_server_url='https://tsa.example.com',
    )
    sp = fields.SigFieldSpec('InvisibleSig', seed_value_dict=sv)
    fields.append_signature_field(w, sp)
    out = BytesIO()
    w.write(out)
    out.seek(0)
    r = PdfFileReader(out)
    _, _, sig_field_ref = next(fields.enumerate_sig_fields(r))
    sv_dict = sig_field_ref.get_object()['/SV']
    assert sv_dict['/V'] == generic.NumberObject(2)
    del sv_dict['/V']
    recovered_sv = fields.SigSeedValueSpec.from_pdf_object(sv_dict)
    # x509.Certificate doesn't have an __eq__ implementation apparently,
    # so for the purposes of the test, we replace them by byte dumps
    issuers1 = recovered_sv.cert.issuers
    issuers2 = sv.cert.issuers
    issuers1[0] = issuers1[0].dump()
    issuers2[0] = issuers2[0].dump()

    subjects1 = recovered_sv.cert.subjects
    subjects2 = sv.cert.subjects
    subjects1[0] = subjects1[0].dump()
    subjects2[0] = subjects2[0].dump()
    assert recovered_sv == sv
Example #17
0
async def test_add_revinfo_wrong_subfilter():
    sv = fields.SigSeedValueSpec(flags=fields.SigSeedValFlags.ADD_REV_INFO,
                                 add_rev_info=True)

    sig_field_spec = fields.SigFieldSpec(
        sig_field_name='Sig',
        seed_value_dict=sv,
    )

    w = IncrementalPdfFileWriter(BytesIO(MINIMAL))
    fields.append_signature_field(w, sig_field_spec)

    cms_writer = cms_embedder.PdfCMSEmbedder().write_cms(field_name='Sig',
                                                         writer=w)
    next(cms_writer)
    # wrong subfilter: PAdES
    #  but we do embed an (empty) RevInfoArchival attribute
    sig_obj = signers.SignatureObject(bytes_reserved=8192, subfilter=PADES)

    cms_writer.send(cms_embedder.SigObjSetup(sig_placeholder=sig_obj))

    prep_digest, output = cms_writer.send(
        cms_embedder.SigIOSetup(md_algorithm='sha256', in_place=True))
    cms_obj = await FROM_CA.async_sign(
        data_digest=prep_digest.document_digest,
        digest_algorithm='sha256',
        signed_attr_settings=PdfCMSSignedAttributes(
            # empty
            adobe_revinfo_attr=asn1_pdf.RevocationInfoArchival({'ocsp': []})))
    await PdfTBSDocument.async_finish_signing(output, prep_digest, cms_obj)
    r = PdfFileReader(output)
    s = r.embedded_signatures[0]
    status = await async_validate_pdf_signature(s, dummy_ocsp_vc())
    summary = status.pretty_print_details()
    assert status.intact and status.valid
    assert 'not satisfy the SV constraints' in summary
    assert 'requires subfilter' in status.seed_value_constraint_error.args[0]
    assert not status.seed_value_ok
Example #18
0
def test_form_field_in_group_locked_postsign_modify_success(
        field_filled, fieldmdp_spec):
    # the field that is filled in after signing is always the same,
    # but the initial one varies
    w = IncrementalPdfFileWriter(BytesIO(GROUP_VARIANTS[0]))

    sp = fields.SigFieldSpec('SigNew',
                             box=(10, 74, 140, 134),
                             field_mdp_spec=fieldmdp_spec,
                             doc_mdp_update_value=fields.MDPPerm.FILL_FORMS)
    fields.append_signature_field(w, sp)
    set_text_field_in_group(w, field_filled, "Some text")
    meta = signers.PdfSignatureMetadata(field_name='SigNew')
    out = signers.sign_pdf(w, meta, signer=FROM_CA)
    w = IncrementalPdfFileWriter(out)
    set_text_field_in_group(w, 1, "Some other text")
    out = BytesIO()
    w.write(out)

    r = PdfFileReader(out)
    s = r.embedded_signatures[0]
    assert s.field_name == 'SigNew'
    val_trusted(s, extd=True)
Example #19
0
def test_fieldmdp_all_pades_lta(requests_mock):

    w = IncrementalPdfFileWriter(BytesIO(MINIMAL))
    vc = live_testing_vc(requests_mock)
    sp = fields.SigFieldSpec(
        'SigNew',
        box=(10, 74, 140, 134),
        field_mdp_spec=fields.FieldMDPSpec(action=fields.FieldMDPAction.ALL),
        doc_mdp_update_value=fields.MDPPerm.FILL_FORMS)
    fields.append_signature_field(w, sp)
    meta = signers.PdfSignatureMetadata(
        field_name='SigNew',
        validation_context=vc,
        embed_validation_info=True,
        subfilter=fields.SigSeedSubFilter.PADES,
        use_pades_lta=True)

    out = signers.sign_pdf(w, meta, signer=FROM_CA, timestamper=DUMMY_TS)

    r = PdfFileReader(out)
    s = r.embedded_signatures[0]
    assert s.field_name == 'SigNew'
    status = val_trusted(s, extd=True)
    assert status.modification_level == ModificationLevel.LTA_UPDATES