diff options
author | Russ Housley <housley@vigilsec.com> | 2019-09-08 16:57:38 -0400 |
---|---|---|
committer | Ilya Etingof <etingof@gmail.com> | 2019-09-08 22:57:38 +0200 |
commit | f3106317179d2f3ed73721514916018c46894f44 (patch) | |
tree | 3772ddcb1019f3db93579cc644da50c3ee949302 | |
parent | 15bc77911a8394c1814456947d6f0ddd85b0e82e (diff) | |
download | pyasn1-modules-f3106317179d2f3ed73721514916018c46894f44.tar.gz |
Add support for RFC 5990 (#63)
-rw-r--r-- | CHANGES.txt | 1 | ||||
-rw-r--r-- | pyasn1_modules/rfc5990.py | 237 | ||||
-rw-r--r-- | tests/__main__.py | 1 | ||||
-rwxr-xr-x | tests/test_rfc5990.py | 82 |
4 files changed, 321 insertions, 0 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 5dba621..ed3181c 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -26,6 +26,7 @@ Revision 0.2.7, released XX-08-2019 an import of the modules is enough in most situations; updates to RFC 2634, RFC 3274, RFC 3779, RFC 4073, RFC 4108, RFC 5035, RFC 5083, RFC 5084, RFC 5480, RFC 5940, RFC 5958, RFC 6019, and RFC 8520 +- Added RFC5990 providing RSA-KEM Key Transport Algorithm in the CMS Revision 0.2.6, released 31-07-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc5990.py b/pyasn1_modules/rfc5990.py new file mode 100644 index 0000000..281316f --- /dev/null +++ b/pyasn1_modules/rfc5990.py @@ -0,0 +1,237 @@ +# +# 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 +# +# Use of the RSA-KEM Key Transport Algorithm in the CMS +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc5990.txt +# + +from pyasn1.type import constraint +from pyasn1.type import namedtype +from pyasn1.type import univ + +from pyasn1_modules import rfc5280 + +MAX = float('inf') + +def _OID(*components): + output = [] + for x in tuple(components): + if isinstance(x, univ.ObjectIdentifier): + output.extend(list(x)) + else: + output.append(int(x)) + return univ.ObjectIdentifier(output) + + +# Imports from RFC 5280 + +AlgorithmIdentifier = rfc5280.AlgorithmIdentifier + + +# Useful types and definitions + +class NullParms(univ.Null): + pass + + +# Object identifier arcs + +is18033_2 = _OID(1, 0, 18033, 2) + +nistAlgorithm = _OID(2, 16, 840, 1, 101, 3, 4) + +pkcs_1 = _OID(1, 2, 840, 113549, 1, 1) + +x9_44 = _OID(1, 3, 133, 16, 840, 9, 44) + +x9_44_components = _OID(x9_44, 1) + + +# Types for algorithm identifiers + +class Camellia_KeyWrappingScheme(AlgorithmIdentifier): + pass + +class DataEncapsulationMechanism(AlgorithmIdentifier): + pass + +class KDF2_HashFunction(AlgorithmIdentifier): + pass + +class KDF3_HashFunction(AlgorithmIdentifier): + pass + +class KeyDerivationFunction(AlgorithmIdentifier): + pass + +class KeyEncapsulationMechanism(AlgorithmIdentifier): + pass + +class X9_SymmetricKeyWrappingScheme(AlgorithmIdentifier): + pass + + +# RSA-KEM Key Transport Algorithm + +id_rsa_kem = _OID(1, 2, 840, 113549, 1, 9, 16, 3, 14) + + +class GenericHybridParameters(univ.Sequence): + pass + +GenericHybridParameters.componentType = namedtype.NamedTypes( + namedtype.NamedType('kem', KeyEncapsulationMechanism()), + namedtype.NamedType('dem', DataEncapsulationMechanism()) +) + + +rsa_kem = AlgorithmIdentifier() +rsa_kem['algorithm'] = id_rsa_kem +rsa_kem['parameters'] = GenericHybridParameters() + + +# KEM-RSA Key Encapsulation Mechanism + +id_kem_rsa = _OID(is18033_2, 2, 4) + + +class KeyLength(univ.Integer): + pass + +KeyLength.subtypeSpec = constraint.ValueRangeConstraint(1, MAX) + + +class RsaKemParameters(univ.Sequence): + pass + +RsaKemParameters.componentType = namedtype.NamedTypes( + namedtype.NamedType('keyDerivationFunction', KeyDerivationFunction()), + namedtype.NamedType('keyLength', KeyLength()) +) + + +kem_rsa = AlgorithmIdentifier() +kem_rsa['algorithm'] = id_kem_rsa +kem_rsa['parameters'] = RsaKemParameters() + + +# Key Derivation Functions + +id_kdf_kdf2 = _OID(x9_44_components, 1) + +id_kdf_kdf3 = _OID(x9_44_components, 2) + + +kdf2 = AlgorithmIdentifier() +kdf2['algorithm'] = id_kdf_kdf2 +kdf2['parameters'] = KDF2_HashFunction() + +kdf3 = AlgorithmIdentifier() +kdf3['algorithm'] = id_kdf_kdf3 +kdf3['parameters'] = KDF3_HashFunction() + + +# Hash Functions + +id_sha1 = _OID(1, 3, 14, 3, 2, 26) + +id_sha224 = _OID(2, 16, 840, 1, 101, 3, 4, 2, 4) + +id_sha256 = _OID(2, 16, 840, 1, 101, 3, 4, 2, 1) + +id_sha384 = _OID(2, 16, 840, 1, 101, 3, 4, 2, 2) + +id_sha512 = _OID(2, 16, 840, 1, 101, 3, 4, 2, 3) + + +sha1 = AlgorithmIdentifier() +sha1['algorithm'] = id_sha1 +sha1['parameters'] = univ.Null("") + +sha224 = AlgorithmIdentifier() +sha224['algorithm'] = id_sha224 +sha224['parameters'] = univ.Null("") + +sha256 = AlgorithmIdentifier() +sha256['algorithm'] = id_sha256 +sha256['parameters'] = univ.Null("") + +sha384 = AlgorithmIdentifier() +sha384['algorithm'] = id_sha384 +sha384['parameters'] = univ.Null("") + +sha512 = AlgorithmIdentifier() +sha512['algorithm'] = id_sha512 +sha512['parameters'] = univ.Null("") + + +# Symmetric Key-Wrapping Schemes + +id_aes128_Wrap = _OID(nistAlgorithm, 1, 5) + +id_aes192_Wrap = _OID(nistAlgorithm, 1, 25) + +id_aes256_Wrap = _OID(nistAlgorithm, 1, 45) + +id_alg_CMS3DESwrap = _OID(1, 2, 840, 113549, 1, 9, 16, 3, 6) + +id_camellia128_Wrap = _OID(1, 2, 392, 200011, 61, 1, 1, 3, 2) + +id_camellia192_Wrap = _OID(1, 2, 392, 200011, 61, 1, 1, 3, 3) + +id_camellia256_Wrap = _OID(1, 2, 392, 200011, 61, 1, 1, 3, 4) + + +aes128_Wrap = AlgorithmIdentifier() +aes128_Wrap['algorithm'] = id_aes128_Wrap +# aes128_Wrap['parameters'] are absent + +aes192_Wrap = AlgorithmIdentifier() +aes192_Wrap['algorithm'] = id_aes128_Wrap +# aes192_Wrap['parameters'] are absent + +aes256_Wrap = AlgorithmIdentifier() +aes256_Wrap['algorithm'] = id_sha256 +# aes256_Wrap['parameters'] are absent + +tdes_Wrap = AlgorithmIdentifier() +tdes_Wrap['algorithm'] = id_alg_CMS3DESwrap +tdes_Wrap['parameters'] = univ.Null("") + +camellia128_Wrap = AlgorithmIdentifier() +camellia128_Wrap['algorithm'] = id_camellia128_Wrap +# camellia128_Wrap['parameters'] are absent + +camellia192_Wrap = AlgorithmIdentifier() +camellia192_Wrap['algorithm'] = id_camellia192_Wrap +# camellia192_Wrap['parameters'] are absent + +camellia256_Wrap = AlgorithmIdentifier() +camellia256_Wrap['algorithm'] = id_camellia256_Wrap +# camellia256_Wrap['parameters'] are absent + + +# Update the Algorithm Identifier map in rfc5280.py. +# Note that the ones that must not have parameters are not added to the map. + +_algorithmIdentifierMapUpdate = { + id_rsa_kem: GenericHybridParameters(), + id_kem_rsa: RsaKemParameters(), + id_kdf_kdf2: KDF2_HashFunction(), + id_kdf_kdf3: KDF3_HashFunction(), + id_sha1: univ.Null(), + id_sha224: univ.Null(), + id_sha256: univ.Null(), + id_sha384: univ.Null(), + id_sha512: univ.Null(), + id_alg_CMS3DESwrap: univ.Null(), +} + +rfc5280.algorithmIdentifierMap.update(_algorithmIdentifierMapUpdate) diff --git a/tests/__main__.py b/tests/__main__.py index fd8e71d..5f5c12d 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -44,6 +44,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc5915.suite', 'tests.test_rfc5940.suite', 'tests.test_rfc5958.suite', + 'tests.test_rfc5990.suite', 'tests.test_rfc6010.suite', 'tests.test_rfc6019.suite', 'tests.test_rfc6031.suite', diff --git a/tests/test_rfc5990.py b/tests/test_rfc5990.py new file mode 100755 index 0000000..9f7e45b --- /dev/null +++ b/tests/test_rfc5990.py @@ -0,0 +1,82 @@ +# +# 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 rfc5280 +from pyasn1_modules import rfc5990 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class RSAKEMTestCase(unittest.TestCase): + pem_text = """\ +MEcGCyqGSIb3DQEJEAMOMDgwKQYHKIGMcQICBDAeMBkGCiuBBRCGSAksAQIwCwYJ +YIZIAWUDBAIBAgEQMAsGCWCGSAFlAwQBBQ== +""" + + def setUp(self): + self.asn1Spec = rfc5280.AlgorithmIdentifier() + + 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 + + assert asn1Object['algorithm'] == rfc5990.id_rsa_kem + rsa_kem_p, rest = der_decode(asn1Object['parameters'], + asn1Spec=rfc5280.algorithmIdentifierMap[rfc5990.id_rsa_kem]) + assert not rest + assert rsa_kem_p.prettyPrint() + assert der_encode(rsa_kem_p) == asn1Object['parameters'] + + assert rsa_kem_p['kem']['algorithm'] == rfc5990.id_kem_rsa + kem_rsa_p, rest = der_decode(rsa_kem_p['kem']['parameters'], + asn1Spec=rfc5280.algorithmIdentifierMap[rfc5990.id_kem_rsa]) + assert not rest + assert kem_rsa_p.prettyPrint() + assert der_encode(kem_rsa_p) == rsa_kem_p['kem']['parameters'] + + assert kem_rsa_p['keyLength'] == 16 + assert kem_rsa_p['keyDerivationFunction']['algorithm'] == rfc5990.id_kdf_kdf3 + kdf_p, rest = der_decode(kem_rsa_p['keyDerivationFunction']['parameters'], + asn1Spec=rfc5280.algorithmIdentifierMap[rfc5990.id_kdf_kdf3]) + assert not rest + assert kdf_p.prettyPrint() + assert der_encode(kdf_p) == kem_rsa_p['keyDerivationFunction']['parameters'] + + def testOpenTypes(self): + substrate = pem.readBase64fromText(self.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['algorithm'] == rfc5990.id_rsa_kem + assert asn1Object['parameters']['kem']['algorithm'] == rfc5990.id_kem_rsa + assert asn1Object['parameters']['kem']['parameters']['keyLength'] == 16 + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + import sys + + result = unittest.TextTestRunner(verbosity=2).run(suite) + sys.exit(not result.wasSuccessful()) |