aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIlya Etingof <etingof@gmail.com>2019-11-08 23:20:51 +0100
committerGitHub <noreply@github.com>2019-11-08 23:20:51 +0100
commit745eed006e93ae89dc0e6e5c366160d6239490e9 (patch)
tree7224195c6d3c01ef12d9f2c0b8a71a161325bbb9
parent9a3a1dbc0ff6ccfabb56ea27a57a7c287d7b5d8b (diff)
downloadpyasn1-modules-745eed006e93ae89dc0e6e5c366160d6239490e9.tar.gz
Add support for RFC 7508 (#102)
-rw-r--r--CHANGES.txt1
-rw-r--r--pyasn1_modules/rfc7508.py90
-rw-r--r--tests/__main__.py1
-rw-r--r--tests/test_rfc7508.py132
4 files changed, 224 insertions, 0 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index a3660b7..3edd67b 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -24,6 +24,7 @@ Revision 0.2.8, released XX-XX-2019
- Added RFC5924 providing Extended Key Usage for Session Initiation
Protocol (SIP) in X.509 certificates
- Added RFC5916 providing Device Owner Attribute
+- Added RFC7508 providing Securing Header Fields with S/MIME
- Update RFC8226 to use ComponentPresentConstraint() instead of the
previous work around
- Add RFC2631 providing OtherInfo for Diffie-Hellman Key Agreement
diff --git a/pyasn1_modules/rfc7508.py b/pyasn1_modules/rfc7508.py
new file mode 100644
index 0000000..6646024
--- /dev/null
+++ b/pyasn1_modules/rfc7508.py
@@ -0,0 +1,90 @@
+#
+# 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
+#
+# Securing Header Fields with S/MIME
+#
+# ASN.1 source from:
+# https://www.rfc-editor.org/rfc/rfc7508.txt
+# https://www.rfc-editor.org/errata/eid5875
+#
+
+from pyasn1.type import char
+from pyasn1.type import constraint
+from pyasn1.type import namedtype
+from pyasn1.type import namedval
+from pyasn1.type import univ
+
+from pyasn1_modules import rfc5652
+
+import string
+
+MAX = float('inf')
+
+
+class Algorithm(univ.Enumerated):
+ namedValues = namedval.NamedValues(
+ ('canonAlgorithmSimple', 0),
+ ('canonAlgorithmRelaxed', 1)
+ )
+
+
+class HeaderFieldStatus(univ.Integer):
+ namedValues = namedval.NamedValues(
+ ('duplicated', 0),
+ ('deleted', 1),
+ ('modified', 2)
+ )
+
+
+class HeaderFieldName(char.VisibleString):
+ subtypeSpec = (
+ constraint.PermittedAlphabetConstraint(*string.printable) -
+ constraint.PermittedAlphabetConstraint(':')
+ )
+
+
+class HeaderFieldValue(char.UTF8String):
+ pass
+
+
+class HeaderField(univ.Sequence):
+ componentType = namedtype.NamedTypes(
+ namedtype.NamedType('field-Name', HeaderFieldName()),
+ namedtype.NamedType('field-Value', HeaderFieldValue()),
+ namedtype.DefaultedNamedType('field-Status',
+ HeaderFieldStatus().subtype(value='duplicated'))
+ )
+
+
+class HeaderFields(univ.SequenceOf):
+ componentType = HeaderField()
+ subtypeSpec = constraint.ValueSizeConstraint(1, MAX)
+
+
+class SecureHeaderFields(univ.Set):
+ componentType = namedtype.NamedTypes(
+ namedtype.NamedType('canonAlgorithm', Algorithm()),
+ namedtype.NamedType('secHeaderFields', HeaderFields())
+ )
+
+
+id_aa = univ.ObjectIdentifier((1, 2, 840, 113549, 1, 9, 16, 2, ))
+
+id_aa_secureHeaderFieldsIdentifier = id_aa + (55, )
+
+
+
+# Map of Attribute Type OIDs to Attributes added to the
+# ones that are in rfc5652.py
+
+_cmsAttributesMapUpdate = {
+ id_aa_secureHeaderFieldsIdentifier: SecureHeaderFields(),
+}
+
+rfc5652.cmsAttributesMap.update(_cmsAttributesMapUpdate)
+
diff --git a/tests/__main__.py b/tests/__main__.py
index 0af458c..c1fa4f7 100644
--- a/tests/__main__.py
+++ b/tests/__main__.py
@@ -76,6 +76,7 @@ suite = unittest.TestLoader().loadTestsFromNames(
'tests.test_rfc7229.suite',
'tests.test_rfc7292.suite',
'tests.test_rfc7296.suite',
+ 'tests.test_rfc7508.suite',
'tests.test_rfc7633.suite',
'tests.test_rfc7773.suite',
'tests.test_rfc7894.suite',
diff --git a/tests/test_rfc7508.py b/tests/test_rfc7508.py
new file mode 100644
index 0000000..07f0d86
--- /dev/null
+++ b/tests/test_rfc7508.py
@@ -0,0 +1,132 @@
+#
+# 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.type import univ
+
+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 rfc7508
+
+try:
+ import unittest2 as unittest
+except ImportError:
+ import unittest
+
+
+class SignedMessageTestCase(unittest.TestCase):
+ signed_message_pem_text = """\
+MIIE/AYJKoZIhvcNAQcCoIIE7TCCBOkCAQExDTALBglghkgBZQMEAgIwUQYJKoZI
+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+v0QSAv9rMife8tClafXWtDwwL8MS7oAh0ymT446Uizxx3PUxggIA
+MIIB/AIBATBMMD8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJWQTEQMA4GA1UEBwwH
+SGVybmRvbjERMA8GA1UECgwIQm9ndXMgQ0ECCQCls1QoG7BuOzALBglghkgBZQME
+AgKgggElMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8X
+DTE5MDUyOTE4MjMxOVowKAYJKoZIhvcNAQk0MRswGTALBglghkgBZQMEAgKhCgYI
+KoZIzj0EAwMwMQYLKoZIhvcNAQkQAjcxIjEgCgEBMBswGRoERnJvbQwRYWxpY2VA
+ZXhhbXBsZS5jb20wPwYJKoZIhvcNAQkEMTIEMLbkIqT9gmce1Peqxm1E9OiwuY1R
+WHHGVufwmjb6XKzj4goQ5tryN5uJN9NM+ZkmbDBNBgsqhkiG9w0BCRACATE+MDwE
+IMdPIQ9kJ1cI9Q6HkRCzbXWdD331uAUCL3MMFXP4KFOjgAEBMBUwE4ERYWxpY2VA
+ZXhhbXBsZS5jb20wCgYIKoZIzj0EAwMEZzBlAjEAuZ8SebvwMRvLPn9+s3VHFUNU
+bEtkkWCao1uNm5TOzphK0NbxzOsD854aC5ReKPSDAjAm1U0siLQw5p4qzGwyxDw9
+5AI5J8Mvy+icNubmfsd4ofvxdaECdhr4rvsSMwbOsFk=
+"""
+
+ 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
+
+ secure_header_field_attr_found = False
+ assert asn1Object['contentType'] == rfc5652.id_signedData
+ sd, rest = der_decode (asn1Object['content'], asn1Spec=rfc5652.SignedData())
+ for sa in sd['signerInfos'][0]['signedAttrs']:
+ sat = sa['attrType']
+ sav0 = sa['attrValues'][0]
+
+ if sat == rfc7508.id_aa_secureHeaderFieldsIdentifier:
+ assert sat in rfc5652.cmsAttributesMap.keys()
+ sav, rest = der_decode(sav0, asn1Spec=rfc5652.cmsAttributesMap[sat])
+ assert not rest
+ assert sav.prettyPrint()
+ assert der_encode(sav) == sav0
+
+ from_field = rfc7508.HeaderFieldName('From')
+ alice_email = rfc7508.HeaderFieldValue('alice@example.com')
+ for shf in sav['secHeaderFields']:
+ if shf['field-Name'] == from_field:
+ assert shf['field-Value'] == alice_email
+ secure_header_field_attr_found = True
+
+ assert secure_header_field_attr_found
+
+ def testOpenTypes(self):
+ substrate = pem.readBase64fromText(self.signed_message_pem_text)
+ asn1Object, rest = der_decode(substrate,
+ asn1Spec=self.asn1Spec, decodeOpenTypes=True)
+ assert not rest
+ assert asn1Object.prettyPrint()
+ assert der_encode(asn1Object) == substrate
+
+ assert asn1Object['contentType'] in rfc5652.cmsContentTypesMap.keys()
+ assert asn1Object['contentType'] == rfc5652.id_signedData
+
+ sd = asn1Object['content']
+ assert sd['version'] == rfc5652.CMSVersion().subtype(value='v1')
+
+ ect = sd['encapContentInfo']['eContentType']
+ assert ect in rfc5652.cmsContentTypesMap.keys()
+ assert ect == rfc5652.id_data
+
+ for sa in sd['signerInfos'][0]['signedAttrs']:
+ if sa['attrType'] == rfc7508.id_aa_secureHeaderFieldsIdentifier:
+ assert sa['attrType'] in rfc5652.cmsAttributesMap.keys()
+
+ secure_header_field_attr_found = False
+ for sa in sd['signerInfos'][0]['signedAttrs']:
+ if sa['attrType'] == rfc7508.id_aa_secureHeaderFieldsIdentifier:
+ assert sa['attrType'] in rfc5652.cmsAttributesMap.keys()
+ from_field = rfc7508.HeaderFieldName('From')
+ alice_email = rfc7508.HeaderFieldValue('alice@example.com')
+ for shf in sa['attrValues'][0]['secHeaderFields']:
+ if shf['field-Name'] == from_field:
+ assert shf['field-Value'] == alice_email
+ secure_header_field_attr_found = True
+
+ assert secure_header_field_attr_found
+
+
+suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
+
+if __name__ == '__main__':
+ import sys
+
+ result = unittest.TextTestRunner(verbosity=2).run(suite)
+ sys.exit(not result.wasSuccessful())