aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuss Housley <housley@vigilsec.com>2019-08-28 18:06:42 -0400
committerIlya Etingof <etingof@gmail.com>2019-08-29 00:06:42 +0200
commitb67a4d7d5a2f78dacb4fa211da3a342160edf901 (patch)
treefee95923c29cd11ed17a1f8a7825e95a9ba5857c
parentfdd5c3033d124ad159b9784e3e7fe304f777dbb8 (diff)
downloadpyasn1-modules-b67a4d7d5a2f78dacb4fa211da3a342160edf901.tar.gz
Add support for RFC 6211 and RFC 8649 (#59)
-rw-r--r--CHANGES.txt2
-rw-r--r--pyasn1_modules/rfc6211.py74
-rw-r--r--pyasn1_modules/rfc8649.py40
-rw-r--r--tests/__main__.py4
-rw-r--r--tests/test_rfc6211.py117
-rw-r--r--tests/test_rfc8649.py64
6 files changed, 300 insertions, 1 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 9f99e8a..de550b9 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -17,6 +17,8 @@ Revision 0.2.7, released XX-08-2019
Cryptography Specification, Version 2.1
- Automatically update the maps for use with openType for RFC3709,
RFC6402, RFC7191, and RFC8226 when the module is imported
+- Added RFC6211 providing CMS Algorithm Identifier Protection Attribute
+- Added RFC8449 providing Certificate Extension for Hash Of Root Key
Revision 0.2.6, released 31-07-2019
-----------------------------------
diff --git a/pyasn1_modules/rfc6211.py b/pyasn1_modules/rfc6211.py
new file mode 100644
index 0000000..3853817
--- /dev/null
+++ b/pyasn1_modules/rfc6211.py
@@ -0,0 +1,74 @@
+#
+# 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
+#
+# CMS Algorithm Identifier Protection Attribute
+#
+# ASN.1 source from:
+# https://www.rfc-editor.org/rfc/rfc6211.txt
+#
+
+from pyasn1.type import constraint
+from pyasn1.type import namedtype
+from pyasn1.type import tag
+from pyasn1.type import univ
+
+from pyasn1_modules import rfc5652
+
+
+# Imports from RFC 5652
+
+DigestAlgorithmIdentifier = rfc5652.DigestAlgorithmIdentifier
+
+MessageAuthenticationCodeAlgorithm = rfc5652.MessageAuthenticationCodeAlgorithm
+
+SignatureAlgorithmIdentifier = rfc5652.SignatureAlgorithmIdentifier
+
+
+# CMS Algorithm Protection attribute
+
+id_aa_cmsAlgorithmProtect = univ.ObjectIdentifier('1.2.840.113549.1.9.52')
+
+
+class CMSAlgorithmProtection(univ.Sequence):
+ pass
+
+CMSAlgorithmProtection.componentType = namedtype.NamedTypes(
+ namedtype.NamedType('digestAlgorithm', DigestAlgorithmIdentifier()),
+ namedtype.OptionalNamedType('signatureAlgorithm',
+ SignatureAlgorithmIdentifier().subtype(
+ implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))),
+ namedtype.OptionalNamedType('macAlgorithm',
+ MessageAuthenticationCodeAlgorithm().subtype(
+ implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2)))
+)
+
+# TODO: Add constraints to implement the WITH COMPONENTS part of the ASN.1:
+#
+# CMSAlgorithmProtection.subtypeSpec = constraint.ConstraintsIntersection(
+# constraint.WithComponentsConstraint(
+# ('signatureAlgorithm', constraint.ComponentPresentConstraint()),
+# ('macAlgorithm', constraint.ComponentAbsentConstraint())),
+# constraint.WithComponentsConstraint(
+# ('signatureAlgorithm', constraint.ComponentAbsentConstraint()),
+# ('macAlgorithm', constraint.ComponentPresentConstraint()))
+# )
+
+
+aa_cmsAlgorithmProtection = rfc5652.Attribute()
+aa_cmsAlgorithmProtection['attrType'] = id_aa_cmsAlgorithmProtect
+aa_cmsAlgorithmProtection['attrValues'][0] = CMSAlgorithmProtection()
+
+
+# Map of Attribute Type OIDs to Attributes are
+# added to the ones that are in rfc5652.py
+
+_cmsAttributesMapUpdate = {
+ id_aa_cmsAlgorithmProtect: CMSAlgorithmProtection(),
+}
+
+rfc5652.cmsAttributesMap.update(_cmsAttributesMapUpdate) \ No newline at end of file
diff --git a/pyasn1_modules/rfc8649.py b/pyasn1_modules/rfc8649.py
new file mode 100644
index 0000000..c405f05
--- /dev/null
+++ b/pyasn1_modules/rfc8649.py
@@ -0,0 +1,40 @@
+#
+# 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
+#
+# X.509 Certificate Extension for Hash Of Root Key
+#
+# ASN.1 source from:
+# https://www.rfc-editor.org/rfc/rfc8649.txt
+#
+
+from pyasn1.type import namedtype
+from pyasn1.type import univ
+
+from pyasn1_modules import rfc5280
+
+
+id_ce_hashOfRootKey = univ.ObjectIdentifier('1.3.6.1.4.1.51483.2.1')
+
+
+class HashedRootKey(univ.Sequence):
+ pass
+
+HashedRootKey.componentType = namedtype.NamedTypes(
+ namedtype.NamedType('hashAlg', rfc5280.AlgorithmIdentifier()),
+ namedtype.NamedType('hashValue', univ.OctetString())
+)
+
+
+# Map of Certificate Extension OIDs to Extensions added to the
+# ones that are in rfc5280.py
+
+_certificateExtensionsMapUpdate = {
+ id_ce_hashOfRootKey: HashedRootKey(),
+}
+
+rfc5280.certificateExtensionsMap.update(_certificateExtensionsMapUpdate)
diff --git a/tests/__main__.py b/tests/__main__.py
index 93ae011..a1f7000 100644
--- a/tests/__main__.py
+++ b/tests/__main__.py
@@ -48,6 +48,7 @@ suite = unittest.TestLoader().loadTestsFromNames(
'tests.test_rfc6019.suite',
'tests.test_rfc6031.suite',
'tests.test_rfc6032.suite',
+ 'tests.test_rfc6211.suite',
'tests.test_rfc7030.suite',
'tests.test_rfc7191.suite',
'tests.test_rfc7292.suite',
@@ -58,7 +59,8 @@ suite = unittest.TestLoader().loadTestsFromNames(
'tests.test_rfc8410.suite',
'tests.test_rfc8418.suite',
'tests.test_rfc8520.suite',
- 'tests.test_rfc8619.suite']
+ 'tests.test_rfc8619.suite',
+ 'tests.test_rfc8649.suite']
)
diff --git a/tests/test_rfc6211.py b/tests/test_rfc6211.py
new file mode 100644
index 0000000..103838a
--- /dev/null
+++ b/tests/test_rfc6211.py
@@ -0,0 +1,117 @@
+#
+# 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 rfc6211
+
+try:
+ import unittest2 as unittest
+except ImportError:
+ import unittest
+
+
+class SignedMessageTestCase(unittest.TestCase):
+ signed_message_pem_text = """\
+MIIEyAYJKoZIhvcNAQcCoIIEuTCCBLUCAQExDTALBglghkgBZQMEAgIwUQYJKoZI
+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+v0QSAv9rMife8tClafXWtDwwL8MS7oAh0ymT446Uizxx3PUxggHM
+MIIByAIBATBMMD8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJWQTEQMA4GA1UEBwwH
+SGVybmRvbjERMA8GA1UECgwIQm9ndXMgQ0ECCQCls1QoG7BuOzALBglghkgBZQME
+AgKggfIwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcN
+MTkwNTI5MTgyMzE5WjAoBgkqhkiG9w0BCTQxGzAZMAsGCWCGSAFlAwQCAqEKBggq
+hkjOPQQDAzA/BgkqhkiG9w0BCQQxMgQwtuQipP2CZx7U96rGbUT06LC5jVFYccZW
+5/CaNvpcrOPiChDm2vI3m4k300z5mSZsME0GCyqGSIb3DQEJEAIBMT4wPAQgx08h
+D2QnVwj1DoeRELNtdZ0PffW4BQIvcwwVc/goU6OAAQEwFTATgRFhbGljZUBleGFt
+cGxlLmNvbTAKBggqhkjOPQQDAwRnMGUCMQChIMyN1nTN+LLQcYJuhWT297vSKMDK
+fIUedSwWYrcSnSa1pq2s3Wue+pNBfecEjYECMGrUNu1UpWdafEJulP9Vz76qOPMa
+5V/AnTEV5zkmzRle8sffN+nQ+SGkoos5zpI1kA==
+"""
+
+ 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 rfc6211.id_aa_cmsAlgorithmProtect:
+ sav, rest = der_decode(sav0, asn1Spec=rfc6211.CMSAlgorithmProtection())
+ assert not rest
+ assert sav.prettyPrint()
+ assert der_encode(sav) == sav0
+
+ 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'] == rfc6211.id_aa_cmsAlgorithmProtect:
+ assert sa['attrType'] in rfc5652.cmsAttributesMap.keys()
+
+ sav0 = sa['attrValues'][0]
+ digest_oid = univ.ObjectIdentifier('2.16.840.1.101.3.4.2.2')
+ sig_oid = univ.ObjectIdentifier('1.2.840.10045.4.3.3')
+ assert sav0['digestAlgorithm']['algorithm'] == digest_oid
+ assert sav0['signatureAlgorithm']['algorithm'] == sig_oid
+
+
+suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
+
+if __name__ == '__main__':
+ import sys
+
+ result = unittest.TextTestRunner(verbosity=2).run(suite)
+ sys.exit(not result.wasSuccessful())
diff --git a/tests/test_rfc8649.py b/tests/test_rfc8649.py
new file mode 100644
index 0000000..e7ca72b
--- /dev/null
+++ b/tests/test_rfc8649.py
@@ -0,0 +1,64 @@
+#
+# This file is part of pyasn1-modules software.
+#
+# 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 rfc4055
+from pyasn1_modules import rfc5280
+from pyasn1_modules import rfc8649
+
+try:
+ import unittest2 as unittest
+except ImportError:
+ import unittest
+
+
+class RootCertificateExtnTestCase(unittest.TestCase):
+ extn_pem_text = """\
+MGEGCisGAQQBg5IbAgEEUzBRMA0GCWCGSAFlAwQCAwUABEBxId+rK+WVDLOda2Yk
+FFRbqQAztXhs91j/RxHjYJIv/3gleQg3Qix/yQy2rIg3xysjCvHWw8AuYOGVh/sL
+GANG
+"""
+
+ def setUp(self):
+ self.asn1Spec = rfc5280.Extension()
+
+ def testDerCodec(self):
+ substrate = pem.readBase64fromText(self.extn_pem_text)
+ asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec)
+ assert not rest
+ assert asn1Object.prettyPrint()
+ assert der_encode(asn1Object) == substrate
+
+ assert asn1Object['extnID'] == rfc8649.id_ce_hashOfRootKey
+ hashed_root_key, rest = der_decode(asn1Object['extnValue'],
+ rfc8649.HashedRootKey())
+ assert not rest
+ assert hashed_root_key.prettyPrint()
+ assert der_encode(hashed_root_key) == asn1Object['extnValue']
+
+ assert hashed_root_key['hashAlg']['algorithm'] == rfc4055.id_sha512
+
+ def testExtensionsMap(self):
+ substrate = pem.readBase64fromText(self.extn_pem_text)
+ asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec)
+ assert not rest
+
+ assert asn1Object['extnID'] == rfc8649.id_ce_hashOfRootKey
+ assert asn1Object['extnID'] in rfc5280.certificateExtensionsMap.keys()
+
+
+suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
+
+if __name__ == '__main__':
+ import sys
+
+ result = unittest.TextTestRunner(verbosity=2).run(suite)
+ sys.exit(not result.wasSuccessful())