diff options
author | Russ Housley <housley@vigilsec.com> | 2019-06-27 16:32:22 -0400 |
---|---|---|
committer | Ilya Etingof <etingof@gmail.com> | 2019-06-27 22:32:22 +0200 |
commit | 86f7ad492e0dd2eda1bbc2f98fc00ecc3837114e (patch) | |
tree | b9a0724a2f74b412ea4ef87f44813c2f2e484b3f | |
parent | 118dccfd5a18d7b7e48be830fecb0d30ee8b1b7b (diff) | |
download | pyasn1-modules-86f7ad492e0dd2eda1bbc2f98fc00ecc3837114e.tar.gz |
Add support for RFC 5035 (#48)
-rw-r--r-- | CHANGES.txt | 2 | ||||
-rw-r--r-- | pyasn1_modules/rfc5035.py | 189 | ||||
-rw-r--r-- | tests/__main__.py | 1 | ||||
-rwxr-xr-x | tests/test_rfc5035.py | 151 |
4 files changed, 343 insertions, 0 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 48f9f92..36948aa 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -24,6 +24,8 @@ Revision 0.2.6, released XX-05-2019 Types - Added openType support for ORAddress Extension Attributes in the RFC5280 module +- Added RFC5035 providing Update to Enhanced Security Services for + S/MIME Revision 0.2.5, released 24-04-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc5035.py b/pyasn1_modules/rfc5035.py new file mode 100644 index 0000000..5cf0d0d --- /dev/null +++ b/pyasn1_modules/rfc5035.py @@ -0,0 +1,189 @@ +# +# This file is part of pyasn1-modules software. +# +# Created by Russ Housley with assistance from asn1ate v.0.6.0. +# +# Copyright (c) 2019, Vigil Security, LLC +# License: http://snmplabs.com/pyasn1/license.html +# +# Update to Enhanced Security Services for S/MIME +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc5035.txt +# + +from pyasn1.codec.der.encoder import encode as der_encode + +from pyasn1.type import namedtype +from pyasn1.type import univ + +from pyasn1_modules import rfc2634 +from pyasn1_modules import rfc4055 +from pyasn1_modules import rfc5652 +from pyasn1_modules import rfc5280 + +ContentType = rfc5652.ContentType + +IssuerAndSerialNumber = rfc5652.IssuerAndSerialNumber + +SubjectKeyIdentifier = rfc5652.SubjectKeyIdentifier + +AlgorithmIdentifier = rfc5280.AlgorithmIdentifier + +PolicyInformation = rfc5280.PolicyInformation + +GeneralNames = rfc5280.GeneralNames + +CertificateSerialNumber = rfc5280.CertificateSerialNumber + + +# Signing Certificate Attribute V1 and V2 + +id_aa_signingCertificate = rfc2634.id_aa_signingCertificate + +id_aa_signingCertificateV2 = univ.ObjectIdentifier('1.2.840.113549.1.9.16.2.47') + +Hash = rfc2634.Hash + +IssuerSerial = rfc2634.IssuerSerial + +ESSCertID = rfc2634.ESSCertID + +SigningCertificate = rfc2634.SigningCertificate + + +sha256AlgId = AlgorithmIdentifier() +sha256AlgId['algorithm'] = rfc4055.id_sha256 +# A non-schema object for sha256AlgId['parameters'] as absent +sha256AlgId['parameters'] = der_encode(univ.OctetString('')) + + +class ESSCertIDv2(univ.Sequence): + pass + +ESSCertIDv2.componentType = namedtype.NamedTypes( + namedtype.DefaultedNamedType('hashAlgorithm', sha256AlgId), + namedtype.NamedType('certHash', Hash()), + namedtype.OptionalNamedType('issuerSerial', IssuerSerial()) +) + + +class SigningCertificateV2(univ.Sequence): + pass + +SigningCertificateV2.componentType = namedtype.NamedTypes( + namedtype.NamedType('certs', univ.SequenceOf( + componentType=ESSCertIDv2())), + namedtype.OptionalNamedType('policies', univ.SequenceOf( + componentType=PolicyInformation())) +) + + +# Mail List Expansion History Attribute + +id_aa_mlExpandHistory = rfc2634.id_aa_mlExpandHistory + +ub_ml_expansion_history = rfc2634.ub_ml_expansion_history + +EntityIdentifier = rfc2634.EntityIdentifier + +MLReceiptPolicy = rfc2634.MLReceiptPolicy + +MLData = rfc2634.MLData + +MLExpansionHistory = rfc2634.MLExpansionHistory + + +# ESS Security Label Attribute + +id_aa_securityLabel = rfc2634.id_aa_securityLabel + +ub_privacy_mark_length = rfc2634.ub_privacy_mark_length + +ub_security_categories = rfc2634.ub_security_categories + +ub_integer_options = rfc2634.ub_integer_options + +ESSPrivacyMark = rfc2634.ESSPrivacyMark + +SecurityClassification = rfc2634.SecurityClassification + +SecurityPolicyIdentifier = rfc2634.SecurityPolicyIdentifier + +SecurityCategory = rfc2634.SecurityCategory + +SecurityCategories = rfc2634.SecurityCategories + +ESSSecurityLabel = rfc2634.ESSSecurityLabel + + +# Equivalent Labels Attribute + +id_aa_equivalentLabels = rfc2634.id_aa_equivalentLabels + +EquivalentLabels = rfc2634.EquivalentLabels + + +# Content Identifier Attribute + +id_aa_contentIdentifier = rfc2634.id_aa_contentIdentifier + +ContentIdentifier = rfc2634.ContentIdentifier + + +# Content Reference Attribute + +id_aa_contentReference = rfc2634.id_aa_contentReference + +ContentReference = rfc2634.ContentReference + + +# Message Signature Digest Attribute + +id_aa_msgSigDigest = rfc2634.id_aa_msgSigDigest + +MsgSigDigest = rfc2634.MsgSigDigest + + +# Content Hints Attribute + +id_aa_contentHint = rfc2634.id_aa_contentHint + +ContentHints = rfc2634.ContentHints + + +# Receipt Request Attribute + +AllOrFirstTier = rfc2634.AllOrFirstTier + +ReceiptsFrom = rfc2634.ReceiptsFrom + +id_aa_receiptRequest = rfc2634.id_aa_receiptRequest + +ub_receiptsTo = rfc2634.ub_receiptsTo + +ReceiptRequest = rfc2634.ReceiptRequest + + +# Receipt Content Type + +ESSVersion = rfc2634.ESSVersion + +id_ct_receipt = rfc2634.id_ct_receipt + +Receipt = rfc2634.Receipt + +ub_receiptsTo = rfc2634.ub_receiptsTo + +ReceiptRequest = rfc2634.ReceiptRequest + + +# Map of Attribute Type to the Attribute structure + +ESSAttributeMap = rfc2634.ESSAttributeMap + +_ESSAttributeMapAddition = { + id_aa_signingCertificateV2: SigningCertificateV2(), +} + +ESSAttributeMap.update(_ESSAttributeMapAddition) diff --git a/tests/__main__.py b/tests/__main__.py index 879981e..08b0424 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -29,6 +29,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc4073.suite', 'tests.test_rfc4108.suite', 'tests.test_rfc4210.suite', + 'tests.test_rfc5035.suite', 'tests.test_rfc5083.suite', 'tests.test_rfc5084.suite', 'tests.test_rfc5208.suite', diff --git a/tests/test_rfc5035.py b/tests/test_rfc5035.py new file mode 100755 index 0000000..022d149 --- /dev/null +++ b/tests/test_rfc5035.py @@ -0,0 +1,151 @@ +# +# This file is part of pyasn1-modules software. +# +# Created by Russ Housley +# Copyright (c) 2019, Vigil Security, LLC +# License: http://snmplabs.com/pyasn1/license.html +# + +import sys + +from pyasn1.codec.der.decoder import decode as der_decode +from pyasn1.codec.der.encoder import encode as der_encode + +from pyasn1_modules import pem +from pyasn1_modules import rfc5652 +from pyasn1_modules import rfc5035 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class SignedMessageTestCase(unittest.TestCase): + signed_message_pem_text = """\ +MIIFzAYJKoZIhvcNAQcCoIIFvTCCBbkCAQExDTALBglghkgBZQMEAgIwUQYJKoZI +hvcNAQcBoEQEQkNvbnRlbnQtVHlwZTogdGV4dC9wbGFpbg0KDQpXYXRzb24sIGNv +bWUgaGVyZSAtIEkgd2FudCB0byBzZWUgeW91LqCCAnwwggJ4MIIB/qADAgECAgkA +pbNUKBuwbjswCgYIKoZIzj0EAwMwPzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAlZB +MRAwDgYDVQQHDAdIZXJuZG9uMREwDwYDVQQKDAhCb2d1cyBDQTAeFw0xOTA1Mjkx +NDQ1NDFaFw0yMDA1MjgxNDQ1NDFaMHAxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJW +QTEQMA4GA1UEBxMHSGVybmRvbjEQMA4GA1UEChMHRXhhbXBsZTEOMAwGA1UEAxMF +QWxpY2UxIDAeBgkqhkiG9w0BCQEWEWFsaWNlQGV4YW1wbGUuY29tMHYwEAYHKoZI +zj0CAQYFK4EEACIDYgAE+M2fBy/sRA6V1pKFqecRTE8+LuAHtZxes1wmJZrBBg+b +z7uYZfYQxI3dVB0YCSD6Mt3yXFlnmfBRwoqyArbjIBYrDbHBv2k8Csg2DhQ7qs/w +to8hMKoFgkcscqIbiV7Zo4GUMIGRMAsGA1UdDwQEAwIHgDBCBglghkgBhvhCAQ0E +NRYzVGhpcyBjZXJ0aWZpY2F0ZSBjYW5ub3QgYmUgdHJ1c3RlZCBmb3IgYW55IHB1 +cnBvc2UuMB0GA1UdDgQWBBTEuloOPnrjPIGw9AKqaLsW4JYONTAfBgNVHSMEGDAW +gBTyNds0BNqlVfK9aQOZsGLs4hUIwTAKBggqhkjOPQQDAwNoADBlAjBjuR/RNbgL +3kRhmn+PJTeKaL9sh/oQgHOYTgLmSnv3+NDCkhfKuMNoo/tHrkmihYgCMQC94Mae +rDIrQpi0IDh+v0QSAv9rMife8tClafXWtDwwL8MS7oAh0ymT446Uizxx3PUxggLQ +MIICzAIBATBMMD8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJWQTEQMA4GA1UEBwwH +SGVybmRvbjERMA8GA1UECgwIQm9ndXMgQ0ECCQCls1QoG7BuOzALBglghkgBZQME +AgKgggH1MBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8X +DTE5MDUyOTE4MjMxOVowJQYLKoZIhvcNAQkQAgcxFgQUAbWZQYhLO5wtUgsOCGtT +4V3aNhUwLwYLKoZIhvcNAQkQAgQxIDAeDBFXYXRzb24sIGNvbWUgaGVyZQYJKoZI +hvcNAQcBMDUGCyqGSIb3DQEJEAICMSYxJAIBAQYKKwYBBAGBrGABARMTQm9hZ3Vz +IFByaXZhY3kgTWFyazA/BgkqhkiG9w0BCQQxMgQwtuQipP2CZx7U96rGbUT06LC5 +jVFYccZW5/CaNvpcrOPiChDm2vI3m4k300z5mSZsME0GCyqGSIb3DQEJEAIBMT4w +PAQgx08hD2QnVwj1DoeRELNtdZ0PffW4BQIvcwwVc/goU6OAAQEwFTATgRFhbGlj +ZUBleGFtcGxlLmNvbTCBmwYLKoZIhvcNAQkQAi8xgYswgYgwdjB0BCACcp04gyM2 +dTDg+0ydCwlucr6Mg8Wd3J3c9V+iLHsnZzBQMEOkQTA/MQswCQYDVQQGEwJVUzEL +MAkGA1UECAwCVkExEDAOBgNVBAcMB0hlcm5kb24xETAPBgNVBAoMCEJvZ3VzIENB +AgkApbNUKBuwbjswDjAMBgorBgEEAYGsYAEBMAoGCCqGSM49BAMDBGcwZQIxAO3K +D9YjFTKE3p383VVw/ol79WTVoMea4H1+7xn+3E1XO4oyb7qwQz0KmsGfdqWptgIw +T9yMtRLN5ZDU14y+Phzq9NKpSw/x5KyXoUKjCMc3Ru6dIW+CgcRQees+dhnvuD5U +""" + + def setUp(self): + self.asn1Spec = rfc5652.ContentInfo() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.signed_message_pem_text) + asn1Object, rest = der_decode (substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + assert asn1Object['contentType'] == rfc5652.id_signedData + sd, rest = der_decode(asn1Object['content'], asn1Spec=rfc5652.SignedData()) + assert not rest + assert sd.prettyPrint() + assert der_encode(sd) == asn1Object['content'] + + for sa in sd['signerInfos'][0]['signedAttrs']: + sat = sa['attrType'] + sav0 = sa['attrValues'][0] + + if sat in rfc5035.ESSAttributeMap.keys(): + sav, rest = der_decode(sav0, asn1Spec=rfc5035.ESSAttributeMap[sat]) + assert not rest + assert sav.prettyPrint() + assert der_encode(sav) == sav0 + + +class SignedReceiptTestCase(unittest.TestCase): + signed_receipt_pem_text = """\ +MIIE3gYJKoZIhvcNAQcCoIIEzzCCBMsCAQMxDTALBglghkgBZQMEAgEwga4GCyqGSIb3DQEJ +EAEBoIGeBIGbMIGYAgEBBgkqhkiG9w0BBwEEIMdPIQ9kJ1cI9Q6HkRCzbXWdD331uAUCL3MM +FXP4KFOjBGYwZAIwOLV5WCbYjy5HLHE69IqXQQHVDJQzmo18WwkFrEYH3EMsvpXEIGqsFTFN +6NV4VBe9AjA5fGOCP5IhI32YqmGfs+zDlqZyb2xSX6Gr/IfCIm0angfOI39g7lAZDyivjh5H +/oSgggJ3MIICczCCAfqgAwIBAgIJAKWzVCgbsG48MAoGCCqGSM49BAMDMD8xCzAJBgNVBAYT +AlVTMQswCQYDVQQIDAJWQTEQMA4GA1UEBwwHSGVybmRvbjERMA8GA1UECgwIQm9ndXMgQ0Ew +HhcNMTkwNTI5MTkyMDEzWhcNMjAwNTI4MTkyMDEzWjBsMQswCQYDVQQGEwJVUzELMAkGA1UE +CBMCVkExEDAOBgNVBAcTB0hlcm5kb24xEDAOBgNVBAoTB0V4YW1wbGUxDDAKBgNVBAMTA0Jv +YjEeMBwGCSqGSIb3DQEJARYPYm9iQGV4YW1wbGUuY29tMHYwEAYHKoZIzj0CAQYFK4EEACID +YgAEMaRiVS8WvN8Ycmpfq75jBbOMUukNfXAg6AL0JJBXtIFAuIJcZVlkLn/xbywkcMLHK/O+ +w9RWUQa2Cjw+h8b/1Cl+gIpqLtE558bD5PfM2aYpJ/YE6yZ9nBfTQs7z1TH5o4GUMIGRMAsG +A1UdDwQEAwIHgDBCBglghkgBhvhCAQ0ENRYzVGhpcyBjZXJ0aWZpY2F0ZSBjYW5ub3QgYmUg +dHJ1c3RlZCBmb3IgYW55IHB1cnBvc2UuMB0GA1UdDgQWBBTKa2Zy3iybV3+YjuLDKtNmjsIa +pTAfBgNVHSMEGDAWgBTyNds0BNqlVfK9aQOZsGLs4hUIwTAKBggqhkjOPQQDAwNnADBkAjAV +boS6OfEYQomLDi2RUkd71hzwwiQZztbxNbosahIzjR8ZQaHhjdjJlrP/T6aXBwsCMDfRweYz +3Ce4E4wPfoqQnvqpM7ZlfhstjQQGOsWAtIIfqW/l+TgCO8ux3XLV6fj36zGCAYkwggGFAgEB +MEwwPzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAlZBMRAwDgYDVQQHDAdIZXJuZG9uMREwDwYD +VQQKDAhCb2d1cyBDQQIJAKWzVCgbsG48MAsGCWCGSAFlAwQCAaCBrjAaBgkqhkiG9w0BCQMx +DQYLKoZIhvcNAQkQAQEwHAYJKoZIhvcNAQkFMQ8XDTE5MDUyOTE5MzU1NVowLwYJKoZIhvcN +AQkEMSIEIGb9Hm2kCnM0CYNpZU4Uj7dN0AzOieIn9sDqZMcIcZrEMEEGCyqGSIb3DQEJEAIF +MTIEMBZzeHVja7fQ62ywyh8rtKzBP1WJooMdZ+8c6pRqfIESYIU5bQnH99OPA51QCwdOdjAK +BggqhkjOPQQDAgRoMGYCMQDZiT22xgab6RFMAPvN4fhWwzx017EzttD4VaYrpbolropBdPJ6 +jIXiZQgCwxbGTCwCMQClaQ9K+L5LTeuW50ZKSIbmBZQ5dxjtnK3OlS7hYRi6U0JKZmWbbuS8 +vFIgX7eIkd8= +""" + + def setUp(self): + self.asn1Spec = rfc5652.ContentInfo() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.signed_receipt_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + assert asn1Object['contentType'] == rfc5652.id_signedData + sd, rest = der_decode (asn1Object['content'], asn1Spec=rfc5652.SignedData()) + assert not rest + assert sd.prettyPrint() + assert der_encode(sd) == asn1Object['content'] + + assert sd['encapContentInfo']['eContentType'] == rfc5035.id_ct_receipt + receipt, rest = der_decode(sd['encapContentInfo']['eContent'], + asn1Spec=rfc5035.Receipt()) + assert not rest + assert receipt.prettyPrint() + assert der_encode(receipt) == sd['encapContentInfo']['eContent'] + + for sa in sd['signerInfos'][0]['signedAttrs']: + sat = sa['attrType'] + sav0 = sa['attrValues'][0] + + if sat in rfc5035.ESSAttributeMap.keys(): + sav, rest = der_decode(sav0, asn1Spec=rfc5035.ESSAttributeMap[sat]) + assert not rest + assert sav.prettyPrint() + assert der_encode(sav) == sav0 + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) |