diff options
author | Russ Housley <housley@vigilsec.com> | 2019-08-28 18:06:42 -0400 |
---|---|---|
committer | Ilya Etingof <etingof@gmail.com> | 2019-08-29 00:06:42 +0200 |
commit | b67a4d7d5a2f78dacb4fa211da3a342160edf901 (patch) | |
tree | fee95923c29cd11ed17a1f8a7825e95a9ba5857c | |
parent | fdd5c3033d124ad159b9784e3e7fe304f777dbb8 (diff) | |
download | pyasn1-modules-b67a4d7d5a2f78dacb4fa211da3a342160edf901.tar.gz |
Add support for RFC 6211 and RFC 8649 (#59)
-rw-r--r-- | CHANGES.txt | 2 | ||||
-rw-r--r-- | pyasn1_modules/rfc6211.py | 74 | ||||
-rw-r--r-- | pyasn1_modules/rfc8649.py | 40 | ||||
-rw-r--r-- | tests/__main__.py | 4 | ||||
-rw-r--r-- | tests/test_rfc6211.py | 117 | ||||
-rw-r--r-- | tests/test_rfc8649.py | 64 |
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()) |