diff options
author | Russ Housley <housley@vigilsec.com> | 2019-10-07 16:44:32 -0400 |
---|---|---|
committer | Ilya Etingof <etingof@gmail.com> | 2019-10-07 22:44:32 +0200 |
commit | d3a9ae35fb63cb2d8372a352da26e53a9009edfb (patch) | |
tree | fe15e0ed2ca4a0d97af8d37061833c7c910bb92d | |
parent | e2f88827355c7dae440a27bf9d281abcf7d8fc5c (diff) | |
download | pyasn1-modules-d3a9ae35fb63cb2d8372a352da26e53a9009edfb.tar.gz |
Add support for RFC 8017 (#76)
-rw-r--r-- | CHANGES.txt | 1 | ||||
-rw-r--r-- | pyasn1_modules/rfc8017.py | 153 | ||||
-rw-r--r-- | tests/__main__.py | 1 | ||||
-rw-r--r-- | tests/test_rfc8017.py | 124 |
4 files changed, 279 insertions, 0 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index da4c06f..4dbe1fa 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -43,6 +43,7 @@ Revision 0.2.7, released XX-09-2019 Validation Reconsidered - Added RFC8358 providing Digital Signatures on Internet-Draft Documents - Added RFC8209 providing BGPsec Router PKI Profile +- Added RFC8017 providing PKCS #1 Version 2.2 Revision 0.2.6, released 31-07-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc8017.py b/pyasn1_modules/rfc8017.py new file mode 100644 index 0000000..fefed1d --- /dev/null +++ b/pyasn1_modules/rfc8017.py @@ -0,0 +1,153 @@ +# +# 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 +# +# PKCS #1: RSA Cryptography Specifications Version 2.2 +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc8017.txt +# + +from pyasn1.type import constraint +from pyasn1.type import namedtype +from pyasn1.type import namedval +from pyasn1.type import univ + +from pyasn1_modules import rfc2437 +from pyasn1_modules import rfc3447 +from pyasn1_modules import rfc4055 +from pyasn1_modules import rfc5280 + +MAX = float('inf') + + +# Import Algorithm Identifier from RFC 5280 + +AlgorithmIdentifier = rfc5280.AlgorithmIdentifier + +class DigestAlgorithm(AlgorithmIdentifier): + pass + +class HashAlgorithm(AlgorithmIdentifier): + pass + +class MaskGenAlgorithm(AlgorithmIdentifier): + pass + +class PSourceAlgorithm(AlgorithmIdentifier): + pass + + +# Object identifiers from NIST SHA2 + +hashAlgs = univ.ObjectIdentifier('2.16.840.1.101.3.4.2') +id_sha256 = rfc4055.id_sha256 +id_sha384 = rfc4055.id_sha384 +id_sha512 = rfc4055.id_sha512 +id_sha224 = rfc4055.id_sha224 +id_sha512_224 = hashAlgs + (5, ) +id_sha512_256 = hashAlgs + (6, ) + + +# Basic object identifiers + +pkcs_1 = univ.ObjectIdentifier('1.2.840.113549.1.1') +rsaEncryption = rfc2437.rsaEncryption +id_RSAES_OAEP = rfc2437.id_RSAES_OAEP +id_pSpecified = rfc2437.id_pSpecified +id_RSASSA_PSS = rfc4055.id_RSASSA_PSS +md2WithRSAEncryption = rfc2437.md2WithRSAEncryption +md5WithRSAEncryption = rfc2437.md5WithRSAEncryption +sha1WithRSAEncryption = rfc2437.sha1WithRSAEncryption +sha224WithRSAEncryption = rfc4055.sha224WithRSAEncryption +sha256WithRSAEncryption = rfc4055.sha256WithRSAEncryption +sha384WithRSAEncryption = rfc4055.sha384WithRSAEncryption +sha512WithRSAEncryption = rfc4055.sha512WithRSAEncryption +sha512_224WithRSAEncryption = pkcs_1 + (15, ) +sha512_256WithRSAEncryption = pkcs_1 + (16, ) +id_sha1 = rfc2437.id_sha1 +id_md2 = univ.ObjectIdentifier('1.2.840.113549.2.2') +id_md5 = univ.ObjectIdentifier('1.2.840.113549.2.5') +id_mgf1 = rfc2437.id_mgf1 + + +# Default parameter values + +sha1 = rfc4055.sha1Identifier +SHA1Parameters = univ.Null("") + +mgf1SHA1 = rfc4055.mgf1SHA1Identifier + +class EncodingParameters(univ.OctetString): + subtypeSpec = constraint.ValueSizeConstraint(0, MAX) + +pSpecifiedEmpty = rfc4055.pSpecifiedEmptyIdentifier + +emptyString = EncodingParameters(value='') + + +# Main structures + +class Version(univ.Integer): + namedValues = namedval.NamedValues( + ('two-prime', 0), + ('multi', 1) + ) + +class TrailerField(univ.Integer): + namedValues = namedval.NamedValues( + ('trailerFieldBC', 1) + ) + +RSAPublicKey = rfc2437.RSAPublicKey + +OtherPrimeInfo = rfc3447.OtherPrimeInfo +OtherPrimeInfos = rfc3447.OtherPrimeInfos +RSAPrivateKey = rfc3447.RSAPrivateKey + +RSAES_OAEP_params = rfc4055.RSAES_OAEP_params +rSAES_OAEP_Default_Identifier = rfc4055.rSAES_OAEP_Default_Identifier + +RSASSA_PSS_params = rfc4055.RSASSA_PSS_params +rSASSA_PSS_Default_Identifier = rfc4055.rSASSA_PSS_Default_Identifier + + +# Syntax for the EMSA-PKCS1-v1_5 hash identifier + +class DigestInfo(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('digestAlgorithm', DigestAlgorithm()), + namedtype.NamedType('digest', univ.OctetString()) + ) + + +# Update the Algorithm Identifier map + +_algorithmIdentifierMapUpdate = { + id_sha1: univ.Null(), + id_sha224: univ.Null(), + id_sha256: univ.Null(), + id_sha384: univ.Null(), + id_sha512: univ.Null(), + id_sha512_224: univ.Null(), + id_sha512_256: univ.Null(), + id_mgf1: AlgorithmIdentifier(), + id_pSpecified: univ.OctetString(), + id_RSAES_OAEP: RSAES_OAEP_params(), + id_RSASSA_PSS: RSASSA_PSS_params(), + md2WithRSAEncryption: univ.Null(), + md5WithRSAEncryption: univ.Null(), + sha1WithRSAEncryption: univ.Null(), + sha224WithRSAEncryption: univ.Null(), + sha256WithRSAEncryption: univ.Null(), + sha384WithRSAEncryption: univ.Null(), + sha512WithRSAEncryption: univ.Null(), + sha512_224WithRSAEncryption: univ.Null(), + sha512_256WithRSAEncryption: univ.Null(), +} + +rfc5280.algorithmIdentifierMap.update(_algorithmIdentifierMapUpdate) diff --git a/tests/__main__.py b/tests/__main__.py index 511ef87..36290c6 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -60,6 +60,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc7296.suite', 'tests.test_rfc7894.suite', 'tests.test_rfc7906.suite', + 'tests.test_rfc8017.suite', 'tests.test_rfc8018.suite', 'tests.test_rfc8103.suite', 'tests.test_rfc8209.suite', diff --git a/tests/test_rfc8017.py b/tests/test_rfc8017.py new file mode 100644 index 0000000..f05706e --- /dev/null +++ b/tests/test_rfc8017.py @@ -0,0 +1,124 @@ +# +# 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.type import univ + +from pyasn1_modules import pem +from pyasn1_modules import rfc5280 +from pyasn1_modules import rfc8017 +from pyasn1_modules import rfc2985 + +try: + import unittest2 as unittest + +except ImportError: + import unittest + +class SMIMECapabilitiesTestCase(unittest.TestCase): + smime_capabilities_pem_text = """\ +MIIBAzA8BgkqhkiG9w0BAQcwL6APMA0GCWCGSAFlAwQCAgUAoRwwGgYJKoZIhvcN +AQEIMA0GCWCGSAFlAwQCAgUAMDwGCSqGSIb3DQEBCjAvoA8wDQYJYIZIAWUDBAIC +BQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAICBQAwDQYJKoZIhvcNAQECBQAw +DQYJKoZIhvcNAQEEBQAwDQYJKoZIhvcNAQEFBQAwDQYJKoZIhvcNAQEOBQAwDQYJ +KoZIhvcNAQELBQAwDQYJKoZIhvcNAQEMBQAwDQYJKoZIhvcNAQENBQAwDQYJKoZI +hvcNAQEPBQAwDQYJKoZIhvcNAQEQBQA= +""" + + def setUp(self): + self.asn1Spec = rfc2985.SMIMECapabilities() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.smime_capabilities_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + for cap in asn1Object: + assert cap['algorithm'] in rfc5280.algorithmIdentifierMap.keys() + if cap['parameters'].hasValue(): + p, rest = der_decode(cap['parameters'], + asn1Spec=rfc5280.algorithmIdentifierMap[cap['algorithm']]) + assert not rest + if not p == univ.Null(""): + assert p.prettyPrint() + assert der_encode(p) == cap['parameters'] + + if cap['algorithm'] == rfc8017.id_RSAES_OAEP: + assert p['hashFunc']['algorithm'] == rfc8017.id_sha384 + assert p['maskGenFunc']['algorithm'] == rfc8017.id_mgf1 + + def OpenTypesCodec(self): + substrate = pem.readBase64fromText(self.smime_capabilities_pem_text) + asn1Object, rest = der_decode(substrate, + asn1Spec=self.asn1Spec, + decodeOpenTypes=True) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + for cap in asn1Object: + if cap['algorithm'] == rfc8017.id_RSAES_OAEP: + p = cap['parameters'] + assert p['hashFunc']['algorithm'] == rfc8017.id_sha384 + assert p['maskGenFunc']['algorithm'] == rfc8017.id_mgf1 + + +class MultiprimeRSAPrivateKeyTestCase(unittest.TestCase): + pem_text = """\ +MIIE2QIBAQKCAQEAn82EqwXasE2TFNSmZucB8LNza2mOWLHF3nxpxKXalPMDvezc +5Dq7Ytcv/k9jJL4j4jYfvR4yyZdU9iHLaD6hOINZ8E6hVpx/4c96ZUSOLzD2g+u+ +jIuoNfG+zygSBGYCS6BLCAIsZ+2wUyxYpLJknHJld9/jy+aLmmyrilhH9dH5AUiV +3NeWht/68++dMXf4ZI/gV4bMSlWhggxkz2WJJpiQdCdJatGkwNDkHmLA9X0tC6OH +SPE7qYdxG38cYS5F445SgnhDpiK7BodSqYLwgehaDjoOYdEgHVnOcpBCDI5zCJSL +b1c/z8uhrB1xxlECR44wCLcKsIIYQxaEErRJ/wIDAQABAoIBAD+Ra5L0szeqxDVn +GgKZJkZvvBwgU0WpAgMtDo3xQ/A4c2ab0IrhaiU5YJgTUGcPVArqaNm8J4vVrTBz +5QxEzbFDXwWe4cMoYh6bgB7ElKLlIUr8/kGZUfgc7kI29luEjcAIEAC2/RQHesVn +DHkL5OzqZL+4fIwckAMh0tXdflsPgZ/jgIaKca4OqKu4KGnczm3UvqtlvwisAjkx +zMyfZXOLn0vEwP2bfbhQrCVrP7n6a+CV+Kqm8NBWnbiS6x2rWemVVssNTbfXQztq +wC6ZJZCLK7plciDBWvHcS6vxdcsS9DUxuqSV6o/stCGTl1D+9tDx8Od0Eunna2B2 +wAoRHZECVgbNO1bqwfYpp5aFuySWoP+KZz8f/5ZkHjLwiNGpQcqVd4+7Ql2R4qgF +NgSoQQOZFhKtiOeLVU0HYfp6doI4waSINZdF/fJDHD6fY3AMOc/IIMDHHIzbAlYG +vKOocLXWj/2+gcyQ1XoAmrE70aIFUBLSvd7RCi8GI74zYWp5lCSvO850Z4GsWSZT +41iF13sTDDJPm3+BbzMvEu2GuACi/8/IpbUr24/FP9Cp1Rf7kwJWAgMxfoshbrNu +ebQB5laHNnT+DYhrOFVRNiNDaD2bUNSetrFidosWtD4ueHxMGENwa4BbFJ9+UrdP +fyxC6k7exM7khGjaNZczwTep1VpYtKjzP/bp9KcCVgYoj9s9HZ1FCAsNEPodjGfd +AcPTQS9mIa7wzy19B7uvFQJXPURi/p4KKBMVQ99Pp8/r9lJzxxiEf8FyPr8N7lZM +EUKkFkDrZQDhKpsrHWSNj6yRFlltAlYC7dYR8KLEWoOUATLosxQhwgypv+23r+d4 +ZdPOdDv9n8Kmj+NFy/oISFfdXzlOU4RWQtMx3hEwAabwct7vjiJEej/kmiTqco02 +17tt13VvvQ5ZXF73dDCCAQwwggEIAlYDfMpM1WNfxcLLOgkRZ+0S9OvIrEOi0ALV +SquTdi/thhCuCsK3lMD4miN9te8j16YtqEFVWXC3a6DWwIJ6m/xZ50bBwPqM8RsI +6FWhZw4Dr5VqjYXUvwJWAvapRk9SydDYri/cAtGIkUJVlspkE1emALAaSw30vmfd +hrgYLT6YGOmK3UmcNJ4NVeET275MXWF1ZOhkOGKTN6aj5wPhJaHBMnmUQrq7GwC6 +/LfUkSsCVgMCDTV9gbFW8u6TcTVW85dBIeUGxZh1T2pbU3dkGO3IOxOhzJUplH4/ +EeEs9dusHakg1ERXAg4Vo1YowPW8kuVbZ9faxeVrmuER5NcCuZzS5X/obGUw +""" + + def setUp(self): + self.asn1Spec = rfc8017.RSAPrivateKey() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + import sys + + result = unittest.TextTestRunner(verbosity=2).run(suite) + sys.exit(not result.wasSuccessful()) |