def compress_message(data_to_compress): """Function compresses data and returns the generated ASN.1 :param data_to_compress: A byte string of the data to be compressed :return: A CMS ASN.1 byte string of the compressed data. """ compressed_content = cms.ParsableOctetString( zlib.compress(data_to_compress)) return cms.ContentInfo({ "content_type": cms.ContentType("compressed_data"), "content": cms.CompressedData({ "version": cms.CMSVersion("v0"), "compression_algorithm": cms.CompressionAlgorithm( {"algorithm": cms.CompressionAlgorithmId("zlib")}), "encap_content_info": cms.EncapsulatedContentInfo({ "content_type": cms.ContentType("data"), "content": compressed_content, }), }), }).dump()
async def test_cms_v3_sign(detached): inner_obj = await FROM_CA.async_sign_general_data( b'Hello world!', 'sha256', detached=False ) signature = await FROM_CA.async_sign_general_data( cms.EncapsulatedContentInfo({ 'content_type': 'signed_data', 'content': inner_obj['content'].untag() }), 'sha256', detached=detached ) # re-parse just to make sure we're starting fresh signature = cms.ContentInfo.load(signature.dump()) content = signature['content'] assert content['version'].native == 'v3' assert isinstance(content, cms.SignedData) eci = content['encap_content_info'] assert eci['content_type'].native == 'signed_data' if detached: raw_digest = hashlib.sha256( inner_obj['content'].untag().dump() ).digest() else: raw_digest = None inner_eci = eci['content'].parsed['encap_content_info'] assert inner_eci['content'].native == b'Hello world!' status = await async_validate_cms_signature( content, raw_digest=raw_digest ) assert status.valid assert status.intact
def compress_message(data_to_compress): """Function compresses data and returns the generated ASN.1 :param data_to_compress: A byte string of the data to be compressed :return: A CMS ASN.1 byte string of the compressed data. """ compressed_content = cms.ParsableOctetString( zlib.compress(data_to_compress)) return cms.ContentInfo({ 'content_type': cms.ContentType('compressed_data'), 'content': cms.CompressedData({ 'version': cms.CMSVersion('v0'), 'compression_algorithm': cms.CompressionAlgorithm( {'algorithm': cms.CompressionAlgorithmId('zlib')}), 'encap_content_info': cms.EncapsulatedContentInfo({ 'content_type': cms.ContentType('data'), 'content': compressed_content }) }) }).dump()
def sign_authpack_native(data, privkey, certificate, wrap_signed=False): """ Creating PKCS7 blob which contains the following things: 1. 'data' blob which is an ASN1 encoded "AuthPack" structure 2. the certificate used to sign the data blob 3. the singed 'signed_attrs' structure (ASN1) which points to the "data" structure (in point 1) """ da = {'algorithm': algos.DigestAlgorithmId('1.3.14.3.2.26')} si = {} si['version'] = 'v1' si['sid'] = cms.IssuerAndSerialNumber({ 'issuer': certificate.issuer, 'serial_number': certificate.serial_number, }) si['digest_algorithm'] = algos.DigestAlgorithm(da) si['signed_attrs'] = [ cms.CMSAttribute({ 'type': 'content_type', 'values': ['1.3.6.1.5.2.3.1'] }), # indicates that the encap_content_info's authdata struct (marked with OID '1.3.6.1.5.2.3.1' is signed ) cms.CMSAttribute({ 'type': 'message_digest', 'values': [hashlib.sha1(data).digest()] }), ### hash of the data, the data itself will not be signed, but this block of data will be. ] si['signature_algorithm'] = algos.SignedDigestAlgorithm( {'algorithm': '1.2.840.113549.1.1.1'}) si['signature'] = rsa_pkcs1v15_sign( privkey, cms.CMSAttributes(si['signed_attrs']).dump(), "sha1") ec = {} ec['content_type'] = '1.3.6.1.5.2.3.1' ec['content'] = data sd = {} sd['version'] = 'v3' sd['digest_algorithms'] = [algos.DigestAlgorithm(da)] # must have only one sd['encap_content_info'] = cms.EncapsulatedContentInfo(ec) sd['certificates'] = [certificate] sd['signer_infos'] = cms.SignerInfos([cms.SignerInfo(si)]) if wrap_signed is True: ci = {} ci['content_type'] = '1.2.840.113549.1.7.2' # signed data OID ci['content'] = cms.SignedData(sd) return cms.ContentInfo(ci).dump() return cms.SignedData(sd).dump()
def compress_message(data_to_compress): compressed_content = cms.ParsableOctetString( zlib.compress(data_to_compress)) return cms.ContentInfo({ 'content_type': cms.ContentType('compressed_data'), 'content': cms.CompressedData({ 'version': cms.CMSVersion(0), 'compression_algorithm': cms.CompressionAlgorithm( {'algorithm': cms.CompressionAlgorithmId('zlib')}), 'encap_content_info': cms.EncapsulatedContentInfo({ 'content_type': cms.ContentType('data'), 'content': compressed_content }) }) })
assert status.valid assert status.intact assert 'CONTENT_TIMESTAMP_TOKEN<INVALID>' in status.summary() assert 'TIMESTAMP_TOKEN<INTACT:UNTRUSTED>' in status.summary() @freeze_time('2020-11-01') @pytest.mark.parametrize('content,detach', [ (b'This is not a TST!', True), (b'This is not a TST!', False), (cms.ContentInfo({ 'content_type': 'data', 'content': b'This is not a TST!' }), False), (cms.EncapsulatedContentInfo({ 'content_type': '2.999', 'content': core.ParsableOctetString( core.OctetString(b'This is not a TST!').dump() ) }), False), (cms.ContentInfo({'content_type': '2.999',}), True), ]) async def test_detached_with_malformed_content_tst(content, detach): class CustomProvider(CMSAttributeProvider): attribute_type = 'content_time_stamp' async def build_attr_value(self, dry_run=False): attr_value = await FROM_CA.async_sign_general_data( content, 'sha256', detached=detach, ) return attr_value
def request_tsa_response(self, req: tsp.TimeStampReq) -> tsp.TimeStampResp: # We pretend that certReq is always true in the request # TODO generalise my detached signature logic to include cases like this # (see § 5.4 in RFC 5652) # TODO does the RFC status = tsp.PKIStatusInfo({'status': tsp.PKIStatus('granted')}) message_imprint: tsp.MessageImprint = req['message_imprint'] md_algorithm = message_imprint['hash_algorithm']['algorithm'].native digest_algorithm_obj = algos.DigestAlgorithm({ 'algorithm': md_algorithm }) dt = self.fixed_dt or datetime.now(tz=tzlocal.get_localzone()) tst_info = { 'version': 'v1', # See http://oidref.com/1.3.6.1.4.1.4146.2.2 # I don't really care too much, this is a testing device anyway 'policy': tsp.ObjectIdentifier('1.3.6.1.4.1.4146.2.2'), 'message_imprint': message_imprint, # should be sufficiently random (again, this is a testing class) 'serial_number': get_nonce(), 'gen_time': dt, 'tsa': x509.GeneralName( name='directory_name', value=self.tsa_cert.subject ) } try: tst_info['nonce'] = req['nonce'] except KeyError: pass tst_info = tsp.TSTInfo(tst_info) tst_info_data = tst_info.dump() message_digest = getattr(hashlib, md_algorithm)(tst_info_data).digest() signed_attrs = cms.CMSAttributes([ simple_cms_attribute('content_type', 'tst_info'), simple_cms_attribute( 'signing_time', cms.Time({'utc_time': core.UTCTime(dt)}) ), simple_cms_attribute( 'signing_certificate', general.as_signing_certificate(self.tsa_cert) ), simple_cms_attribute('message_digest', message_digest), ]) signature = asymmetric.rsa_pkcs1v15_sign( asymmetric.load_private_key(self.tsa_key), signed_attrs.dump(), md_algorithm ) sig_info = cms.SignerInfo({ 'version': 'v1', 'sid': cms.SignerIdentifier({ 'issuer_and_serial_number': cms.IssuerAndSerialNumber({ 'issuer': self.tsa_cert.issuer, 'serial_number': self.tsa_cert.serial_number, }) }), 'digest_algorithm': digest_algorithm_obj, 'signature_algorithm': algos.SignedDigestAlgorithm( {'algorithm': 'rsassa_pkcs1v15'} ), 'signed_attrs': signed_attrs, 'signature': signature }) certs = set(self.certs_to_embed) certs.add(self.tsa_cert) signed_data = { # must use v3 to get access to the EncapsulatedContentInfo construct 'version': 'v3', 'digest_algorithms': cms.DigestAlgorithms((digest_algorithm_obj,)), 'encap_content_info': cms.EncapsulatedContentInfo({ 'content_type': cms.ContentType('tst_info'), 'content': cms.ParsableOctetString(tst_info_data) }), 'certificates': certs, 'signer_infos': [sig_info] } tst = cms.ContentInfo({ 'content_type': cms.ContentType('signed_data'), 'content': cms.SignedData(signed_data) }) return tsp.TimeStampResp({'status': status, 'time_stamp_token': tst})
def request_tsa_response(self, req: tsp.TimeStampReq) -> tsp.TimeStampResp: # We pretend that certReq is always true in the request status = tsp.PKIStatusInfo({'status': tsp.PKIStatus('granted')}) message_imprint: tsp.MessageImprint = req['message_imprint'] md_algorithm = self.md_algorithm digest_algorithm_obj = algos.DigestAlgorithm({ 'algorithm': md_algorithm }) dt = self.fixed_dt or datetime.now(tz=tzlocal.get_localzone()) tst_info = { 'version': 'v1', 'policy': self.policy, 'message_imprint': message_imprint, 'serial_number': get_nonce(), 'gen_time': dt, 'tsa': x509.GeneralName( name='directory_name', value=self.tsa_cert.subject ) } try: tst_info['nonce'] = req['nonce'] except KeyError: pass tst_info = tsp.TSTInfo(tst_info) tst_info_data = tst_info.dump() message_digest = getattr(hashlib, md_algorithm)(tst_info_data).digest() signing_cert_id = tsp.ESSCertID({ 'cert_hash': hashlib.sha1(self.tsa_cert.dump()).digest() }) signed_attrs = cms.CMSAttributes([ simple_cms_attribute('content_type', 'tst_info'), simple_cms_attribute( 'signing_time', cms.Time({'utc_time': core.UTCTime(dt)}) ), simple_cms_attribute( 'signing_certificate', tsp.SigningCertificate({'certs': [signing_cert_id]}) ), simple_cms_attribute('message_digest', message_digest), ]) signature = generic_sign( self.tsa_key, signed_attrs.dump(), self.signature_algo ) sig_info = cms.SignerInfo({ 'version': 'v1', 'sid': cms.SignerIdentifier({ 'issuer_and_serial_number': cms.IssuerAndSerialNumber({ 'issuer': self.tsa_cert.issuer, 'serial_number': self.tsa_cert.serial_number, }) }), 'digest_algorithm': digest_algorithm_obj, 'signature_algorithm': self.signature_algo, 'signed_attrs': signed_attrs, 'signature': signature }) certs = set(self.certs_to_embed) certs.add(self.tsa_cert) signed_data = { # must use v3 to get access to the EncapsulatedContentInfo construct 'version': 'v3', 'digest_algorithms': cms.DigestAlgorithms((digest_algorithm_obj,)), 'encap_content_info': cms.EncapsulatedContentInfo({ 'content_type': cms.ContentType('tst_info'), 'content': cms.ParsableOctetString(tst_info_data) }), 'certificates': certs, 'signer_infos': [sig_info] } tst = cms.ContentInfo({ 'content_type': cms.ContentType('signed_data'), 'content': cms.SignedData(signed_data) }) return tsp.TimeStampResp({'status': status, 'time_stamp_token': tst})
def request_tsa_response(self, req: tsp.TimeStampReq) \ -> tsp.TimeStampResp: # We pretend that certReq is always true in the request # TODO generalise my detached signature logic to include cases like this # (see § 5.4 in RFC 5652) # TODO does the RFC status = tsp.PKIStatusInfo({'status': tsp.PKIStatus('granted')}) message_imprint: tsp.MessageImprint = req['message_imprint'] md_algorithm = self.override_md if md_algorithm is None: md_algorithm = message_imprint['hash_algorithm'][ 'algorithm'].native digest_algorithm_obj = algos.DigestAlgorithm( {'algorithm': md_algorithm}) dt = self.fixed_dt or datetime.now(tz=tzlocal.get_localzone()) tst_info = { 'version': 'v1', # See http://oidref.com/1.3.6.1.4.1.4146.2.2 # I don't really care too much, this is a testing device anyway 'policy': tsp.ObjectIdentifier('1.3.6.1.4.1.4146.2.2'), 'message_imprint': message_imprint, # should be sufficiently random (again, this is a testing class) 'serial_number': get_nonce(), 'gen_time': dt, 'tsa': x509.GeneralName(name='directory_name', value=self.tsa_cert.subject) } if req['nonce'].native is not None: tst_info['nonce'] = req['nonce'] tst_info = tsp.TSTInfo(tst_info) tst_info_data = tst_info.dump() md_spec = get_pyca_cryptography_hash(md_algorithm) md = hashes.Hash(md_spec) md.update(tst_info_data) message_digest_value = md.finalize() signed_attrs = cms.CMSAttributes([ simple_cms_attribute('content_type', 'tst_info'), simple_cms_attribute('signing_time', cms.Time({'utc_time': core.UTCTime(dt)})), simple_cms_attribute('signing_certificate', general.as_signing_certificate( self.tsa_cert)), simple_cms_attribute('message_digest', message_digest_value), ]) priv_key = serialization.load_der_private_key(self.tsa_key.dump(), password=None) if not isinstance(priv_key, RSAPrivateKey): raise NotImplementedError("Dummy timestamper is RSA-only.") signature = priv_key.sign( signed_attrs.dump(), PKCS1v15(), get_pyca_cryptography_hash(md_algorithm.upper())) sig_info = cms.SignerInfo({ 'version': 'v1', 'sid': cms.SignerIdentifier({ 'issuer_and_serial_number': cms.IssuerAndSerialNumber({ 'issuer': self.tsa_cert.issuer, 'serial_number': self.tsa_cert.serial_number, }) }), 'digest_algorithm': digest_algorithm_obj, 'signature_algorithm': algos.SignedDigestAlgorithm({'algorithm': 'rsassa_pkcs1v15'}), 'signed_attrs': signed_attrs, 'signature': signature }) certs = set(self.certs_to_embed) certs.add(self.tsa_cert) signed_data = { # must use v3 to get access to the EncapsulatedContentInfo construct 'version': 'v3', 'digest_algorithms': cms.DigestAlgorithms((digest_algorithm_obj, )), 'encap_content_info': cms.EncapsulatedContentInfo({ 'content_type': cms.ContentType('tst_info'), 'content': cms.ParsableOctetString(tst_info_data) }), 'certificates': certs, 'signer_infos': [sig_info] } tst = cms.ContentInfo({ 'content_type': cms.ContentType('signed_data'), 'content': cms.SignedData(signed_data) }) return tsp.TimeStampResp({'status': status, 'time_stamp_token': tst})