async def test_detached_cms_with_content_tst(): signed_attr_settings = PdfCMSSignedAttributes( cades_signed_attrs=CAdESSignedAttrSpec(timestamp_content=True) ) signature = await FROM_CA.async_sign_general_data( b'Hello world!', 'sha256', detached=False, timestamper=DUMMY_TS, signed_attr_settings=signed_attr_settings ) signature = cms.ContentInfo.load(signature.dump()) status = await async_validate_detached_cms( b'Hello world!', signature['content'] ) assert status.signer_reported_dt is None assert status.timestamp_validity.intact assert status.timestamp_validity.valid assert status.timestamp_validity.timestamp == datetime.now(tz=pytz.utc) assert status.content_timestamp_validity assert status.content_timestamp_validity.intact assert status.content_timestamp_validity.valid assert status.content_timestamp_validity.timestamp == datetime.now(tz=pytz.utc) pretty_print = status.pretty_print_details() assert 'The TSA certificate is untrusted' in pretty_print assert 'Content timestamp' in pretty_print assert 'Signature timestamp' in pretty_print assert status.valid assert status.intact assert 'CONTENT_TIMESTAMP_TOKEN<INTACT:UNTRUSTED>' in status.summary() assert ',TIMESTAMP_TOKEN<INTACT:UNTRUSTED>' in status.summary()
async def test_sign_weak_sig_digest(): # We have to jump through some hoops to put together a signature # where the signing method's digest is not the same as the "external" # digest. This is intentional, since it's bad practice. input_buf = BytesIO(MINIMAL) w = IncrementalPdfFileWriter(input_buf) cms_writer = cms_embedder.PdfCMSEmbedder().write_cms( field_name='Signature', writer=w ) next(cms_writer) timestamp = datetime.now(tz=tzlocal.get_localzone()) sig_obj = signers.SignatureObject(timestamp=timestamp, bytes_reserved=8192) external_md_algorithm = 'sha256' cms_writer.send(cms_embedder.SigObjSetup(sig_placeholder=sig_obj)) prep_digest, output = cms_writer.send( cms_embedder.SigIOSetup(md_algorithm=external_md_algorithm, in_place=True) ) signer = signers.SimpleSigner( signing_cert=TESTING_CA.get_cert(CertLabel('signer1')), signing_key=TESTING_CA.key_set.get_private_key(KeyLabel('signer1')), cert_registry=SimpleCertificateStore.from_certs([ROOT_CERT, INTERM_CERT]) ) cms_obj = await signer.async_sign( data_digest=prep_digest.document_digest, digest_algorithm=external_md_algorithm, signed_attr_settings=PdfCMSSignedAttributes(signing_time=timestamp) ) si_obj: cms.SignerInfo = cms_obj['content']['signer_infos'][0] bad_algo = SignedDigestAlgorithm({'algorithm': 'md5_rsa'}) si_obj['signature_algorithm'] = signer.signature_mechanism = bad_algo attrs = si_obj['signed_attrs'] cms_prot = find_cms_attribute(attrs, 'cms_algorithm_protection')[0] cms_prot['signature_algorithm'] = bad_algo # recompute the signature si_obj['signature'] = signer.sign_raw(attrs.untag().dump(), 'md5') sig_contents = cms_writer.send(cms_obj) # we requested in-place output assert output is input_buf r = PdfFileReader(input_buf) emb = r.embedded_signatures[0] with pytest.raises(WeakHashAlgorithmError): await async_val_trusted(emb) lenient_vc = ValidationContext( trust_roots=[ROOT_CERT], weak_hash_algos=set() ) await async_val_trusted(emb, vc=lenient_vc)
async def test_detached_cms_with_self_reported_timestamp(): dt = datetime.fromisoformat('2020-11-01T05:00:00+00:00') signature = await FROM_CA.async_sign_general_data( b'Hello world!', 'sha256', detached=False, signed_attr_settings=PdfCMSSignedAttributes(signing_time=dt) ) signature = cms.ContentInfo.load(signature.dump()) status = await async_validate_detached_cms( b'Hello world!', signature['content'] ) assert status.signer_reported_dt == dt assert status.timestamp_validity is None assert 'reported by signer' in status.pretty_print_details() assert status.valid assert status.intact
async def test_embed_signed_attachment(): dt = datetime.fromisoformat('2020-11-01T05:00:00+00:00') signature = await FROM_CA.async_sign_general_data( VECTOR_IMAGE_PDF, 'sha256', PdfCMSSignedAttributes(signing_time=dt) ) w = IncrementalPdfFileWriter(BytesIO(MINIMAL)) signers.embed_payload_with_cms( w, file_spec_string='attachment.pdf', payload=embed.EmbeddedFileObject.from_file_data( w, data=VECTOR_IMAGE_PDF, mime_type='application/pdf', params=embed.EmbeddedFileParams( creation_date=dt, modification_date=dt ) ), cms_obj=signature, file_name='添付ファイル.pdf', file_spec_kwargs={'description': "Signed attachment test"} ) out = BytesIO() w.write(out) r = PdfFileReader(out) emb_lst = r.root['/Names']['/EmbeddedFiles']['/Names'] assert len(emb_lst) == 4 assert emb_lst[0] == 'attachment.pdf' spec_obj = emb_lst[1] assert spec_obj['/UF'] == '添付ファイル.pdf' stream = spec_obj['/EF']['/F'] assert stream.data == VECTOR_IMAGE_PDF assert spec_obj['/RF']['/F'][0] == 'attachment.sig' assert spec_obj['/RF']['/UF'][0] == '添付ファイル.sig' rel_file_ref = spec_obj['/RF']['/F'].raw_get(1).reference assert emb_lst[2] == 'attachment.sig' spec_obj = emb_lst[3] assert spec_obj['/UF'] == '添付ファイル.sig' stream = spec_obj['/EF']['/F'] assert stream.data == signature.dump() assert stream.container_ref == rel_file_ref
async def test_validate_with_malformed_claimed_attrs(bad_attr, requests_mock): # This should parse up to the first level and be reencoded by asn1crypto # without asking any questions. cms_sig = await FROM_CA.async_sign_general_data( b'Hello world', digest_algorithm='sha256', signed_attr_settings=PdfCMSSignedAttributes( cades_signed_attrs=CAdESSignedAttrSpec( signer_attributes=SignerAttrSpec( claimed_attrs=[bad_attr, SAMPLE_GROUP_ATTR], certified_attrs=[] ) ) ) ) status = await async_validate_detached_cms( input_data=b'Hello world', signed_data=cms_sig['content'], signer_validation_context=live_testing_vc(requests_mock) ) assert isinstance(status, StandardCMSSignatureStatus) # The malformed attribute shouldn't have been processed, # but the other attrs should've assert len(status.cades_signer_attrs.claimed_attrs) == 1
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
async def test_parse_ac_with_malformed_attribute(requests_mock): attr_cert_cfg = f''' test-ac: holder: name: signer1 # this needs to match against something from a totally different PKI # arch, so make the coupling as loose as possible include-base-cert-id: false include-entity-name: true issuer: root attributes: - id: charging_identity smart-value: schema: ietf-attribute params: ["Big Corp Inc."] validity: valid-from: "2000-01-01T00:00:00+0000" valid-to: "2100-01-01T00:00:00+0000" ''' pki_arch = PKIArchitecture( arch_label=ArchLabel('test'), key_set=TESTING_CA.key_set, entities=TESTING_CA.entities, cert_spec_config=yaml.safe_load(BASIC_AC_ISSUER_SETUP), ac_spec_config=yaml.safe_load(attr_cert_cfg), service_config={}, external_url_prefix='http://test.test', ) spec = pki_arch.get_attr_cert_spec(CertLabel('test-ac')) # we have to get a bit creative to get Certomancer to output invalid asn1 # in exactly the way we want class FakeAttrSpec: # noinspection PyUnusedLocal def to_asn1(self, arch): return NONSENSICAL_ATTR # noinspection PyTypeChecker spec.attributes.append(FakeAttrSpec()) cms_sig = await FROM_CA.async_sign_general_data( b'Hello world', digest_algorithm='sha256', signed_attr_settings=PdfCMSSignedAttributes( cades_signed_attrs=CAdESSignedAttrSpec( signer_attributes=SignerAttrSpec( certified_attrs=[ pki_arch.get_attr_cert(CertLabel('test-ac')) ], claimed_attrs=[] ) ) ) ) vc = live_testing_vc(requests_mock) ac_vc = ValidationContext( trust_roots=[pki_arch.get_cert(CertLabel('ac-issuer'))], allow_fetching=False, ) status = await async_validate_detached_cms( input_data=b'Hello world', signed_data=cms_sig['content'], signer_validation_context=vc, ac_validation_context=ac_vc ) assert isinstance(status, StandardCMSSignatureStatus) # The malformed attribute shouldn't have been processed, # but the other attrs should've assert len(status.cades_signer_attrs.certified_attrs) == 1