From cfc044568ad1d2958bcb037d827a3c58a1099d52 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Thu, 1 Aug 2019 08:04:17 +0200 Subject: Prepere for 0.2.7 --- CHANGES.txt | 5 +++++ pyasn1_modules/__init__.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index fd564f8..141822d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,9 @@ +Revision 0.2.7, released XX-08-2019 +----------------------------------- + +No changes yet + Revision 0.2.6, released 31-07-2019 ----------------------------------- diff --git a/pyasn1_modules/__init__.py b/pyasn1_modules/__init__.py index 7bb503a..023d7f4 100644 --- a/pyasn1_modules/__init__.py +++ b/pyasn1_modules/__init__.py @@ -1,2 +1,2 @@ # http://www.python.org/dev/peps/pep-0396/ -__version__ = '0.2.6' +__version__ = '0.2.7' -- cgit v1.2.3 From 283db3c96dfbbbc8bb9459e5923e01c04752f65e Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Tue, 13 Aug 2019 17:17:52 -0400 Subject: Add map for opentype to RFC 3565 (#55) --- CHANGES.txt | 2 +- pyasn1_modules/rfc3565.py | 23 ++++++++++++++++++++++- tests/test_rfc3565.py | 19 +++++++++++++++---- 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 141822d..4127ee3 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -2,7 +2,7 @@ Revision 0.2.7, released XX-08-2019 ----------------------------------- -No changes yet +- Added maps for use with openType to RFC 3565 Revision 0.2.6, released 31-07-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc3565.py b/pyasn1_modules/rfc3565.py index c4b742d..ec75e23 100644 --- a/pyasn1_modules/rfc3565.py +++ b/pyasn1_modules/rfc3565.py @@ -1,3 +1,8 @@ +# This file is being contributed to pyasn1-modules software. +# +# Created by Russ Housley. +# Modified by Russ Housley to add maps for use with opentypes. +# # Copyright (c) 2019, Vigil Security, LLC # License: http://snmplabs.com/pyasn1/license.html # @@ -7,6 +12,7 @@ # ASN.1 source from: # https://www.rfc-editor.org/rfc/rfc3565.txt + from pyasn1.type import constraint from pyasn1.type import univ @@ -20,17 +26,32 @@ class AlgorithmIdentifier(rfc5280.AlgorithmIdentifier): class AES_IV(univ.OctetString): pass - AES_IV.subtypeSpec = constraint.ValueSizeConstraint(16, 16) + id_aes128_CBC = univ.ObjectIdentifier('2.16.840.1.101.3.4.1.2') id_aes192_CBC = univ.ObjectIdentifier('2.16.840.1.101.3.4.1.22') id_aes256_CBC = univ.ObjectIdentifier('2.16.840.1.101.3.4.1.42') + id_aes128_wrap = univ.ObjectIdentifier('2.16.840.1.101.3.4.1.5') id_aes192_wrap = univ.ObjectIdentifier('2.16.840.1.101.3.4.1.25') id_aes256_wrap = univ.ObjectIdentifier('2.16.840.1.101.3.4.1.45') + + +# Update the Algorithm Identifier map + +_algorithmIdentifierMapUpdate = { + id_aes128_CBC: AES_IV(), + id_aes192_CBC: AES_IV(), + id_aes256_CBC: AES_IV(), + id_aes128_wrap: univ.Null(), + id_aes192_wrap: univ.Null(), + id_aes256_wrap: univ.Null(), +} + +rfc5280.algorithmIdentifierMap.update(_algorithmIdentifierMapUpdate) diff --git a/tests/test_rfc3565.py b/tests/test_rfc3565.py index 99cb567..f69e392 100644 --- a/tests/test_rfc3565.py +++ b/tests/test_rfc3565.py @@ -11,6 +11,8 @@ import sys from pyasn1.codec.der import decoder as der_decoder from pyasn1.codec.der import encoder as der_encoder +from pyasn1.type import univ + from pyasn1_modules import pem from pyasn1_modules import rfc3565 @@ -50,11 +52,20 @@ class AESCBCTestCase(unittest.TestCase): assert asn1Object[1].isValue assert der_encoder.encode(asn1Object) == substrate + def testOpenTypes(self): + substrate = pem.readBase64fromText(self.aes_alg_id_pem_text) + asn1Object, rest = der_decoder.decode(substrate, + asn1Spec=self.asn1Spec, + decodeOpenTypes=True) + assert not rest + assert asn1Object.prettyPrint() + assert asn1Object[0] == rfc3565.id_aes256_CBC + aes_iv = univ.OctetString(hexValue='108996ba850e3f0339993bb5878a0e37') + assert asn1Object[1] == aes_iv + assert der_encoder.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()) + unittest.TextTestRunner(verbosity=2).run(suite) -- cgit v1.2.3 From 999c682a3e9c2d7770b7032fcf67fde0bac153c3 Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Sat, 17 Aug 2019 02:21:47 -0400 Subject: Add more RFCs Added ASN.1 modules for RFC298, RFC3770, RFC5914, RFC 6010, RFC6031, RFC6032, RFC7030, RFC7292, and RFC8018 --- CHANGES.txt | 12 + pyasn1_modules/rfc2985.py | 583 ++++++++++++++++++++++++++++++++++++++++++++++ pyasn1_modules/rfc3770.py | 63 +++++ pyasn1_modules/rfc5914.py | 119 ++++++++++ pyasn1_modules/rfc6010.py | 88 +++++++ pyasn1_modules/rfc6031.py | 469 +++++++++++++++++++++++++++++++++++++ pyasn1_modules/rfc6032.py | 68 ++++++ pyasn1_modules/rfc7030.py | 66 ++++++ pyasn1_modules/rfc7292.py | 357 ++++++++++++++++++++++++++++ pyasn1_modules/rfc8018.py | 260 +++++++++++++++++++++ tests/__main__.py | 9 + tests/test_rfc2985.py | 293 +++++++++++++++++++++++ tests/test_rfc3770.py | 90 +++++++ tests/test_rfc4073.py | 2 +- tests/test_rfc5914.py | 83 +++++++ tests/test_rfc6010.py | 95 ++++++++ tests/test_rfc6031.py | 89 +++++++ tests/test_rfc6032.py | 88 +++++++ tests/test_rfc6402.py | 163 +++++++++++++ tests/test_rfc7030.py | 87 +++++++ tests/test_rfc7292.py | 172 ++++++++++++++ tests/test_rfc7296.py | 5 +- tests/test_rfc8018.py | 56 +++++ 23 files changed, 3312 insertions(+), 5 deletions(-) create mode 100644 pyasn1_modules/rfc2985.py create mode 100644 pyasn1_modules/rfc3770.py create mode 100644 pyasn1_modules/rfc5914.py create mode 100644 pyasn1_modules/rfc6010.py create mode 100644 pyasn1_modules/rfc6031.py create mode 100644 pyasn1_modules/rfc6032.py create mode 100644 pyasn1_modules/rfc7030.py create mode 100644 pyasn1_modules/rfc7292.py create mode 100644 pyasn1_modules/rfc8018.py create mode 100644 tests/test_rfc2985.py create mode 100644 tests/test_rfc3770.py create mode 100644 tests/test_rfc5914.py create mode 100644 tests/test_rfc6010.py create mode 100644 tests/test_rfc6031.py create mode 100644 tests/test_rfc6032.py create mode 100755 tests/test_rfc6402.py create mode 100644 tests/test_rfc7030.py create mode 100644 tests/test_rfc7292.py create mode 100644 tests/test_rfc8018.py diff --git a/CHANGES.txt b/CHANGES.txt index 4127ee3..60cb3e4 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -3,6 +3,18 @@ Revision 0.2.7, released XX-08-2019 ----------------------------------- - Added maps for use with openType to RFC 3565 +- Added RFC2985 providing PKCS#9 Attributes +- Added RFC3770 providing Certificate Extensions and Attributes for + Authentication in PPP and Wireless LAN Networks +- Added RFC5914 providing Trust Anchor Format +- Added RFC6010 providing CMS Content Constraints (CCC) Extension +- Added RFC6031 providing CMS Symmetric Key Package Content Type +- Added RFC6032 providing CMS Encrypted Key Package Content Type +- Added RFC7030 providing Enrollment over Secure Transport (EST) +- Added RFC7292 providing PKCS #12, which is the Personal Information + Exchange Syntax v1.1 +- Added RFC8018 providing PKCS #5, which is the Password-Based + Cryptography Specification, Version 2.1 Revision 0.2.6, released 31-07-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc2985.py b/pyasn1_modules/rfc2985.py new file mode 100644 index 0000000..591d27d --- /dev/null +++ b/pyasn1_modules/rfc2985.py @@ -0,0 +1,583 @@ +# +# 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 +# +# PKCS#9: Selected Attribute Types (Version 2.0) +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc2985.txt +# + +from pyasn1.type import char +from pyasn1.type import constraint +from pyasn1.type import namedtype +from pyasn1.type import namedval +from pyasn1.type import opentype +from pyasn1.type import tag +from pyasn1.type import univ +from pyasn1.type import useful + +from pyasn1_modules import rfc7292 +from pyasn1_modules import rfc5958 +from pyasn1_modules import rfc5652 +from pyasn1_modules import rfc5280 + + +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) + + +MAX = float('inf') + + +# Imports from RFC 5280 + +AlgorithmIdentifier = rfc5280.AlgorithmIdentifier + +Attribute = rfc5280.Attribute + +EmailAddress = rfc5280.EmailAddress + +Extensions = rfc5280.Extensions + +Time = rfc5280.Time + +X520countryName = rfc5280.X520countryName + +X520SerialNumber = rfc5280.X520SerialNumber + + +# Imports from RFC 5652 + +ContentInfo = rfc5652.ContentInfo + +ContentType = rfc5652.ContentType + +Countersignature = rfc5652.Countersignature + +MessageDigest = rfc5652.MessageDigest + +SignerInfo = rfc5652.SignerInfo + +SigningTime = rfc5652.SigningTime + + +# Imports from RFC 5958 + +EncryptedPrivateKeyInfo = rfc5958.EncryptedPrivateKeyInfo + + +# Imports from RFC 7292 + +PFX = rfc7292.PFX + + +# TODO: +# Need a place to import PKCS15Token; it does not yet appear in an RFC + + +# SingleAttribute is the same as Attribute in RFC 5280, except that the +# attrValues SET must have one and only one member + +class AttributeType(univ.ObjectIdentifier): + pass + + +class AttributeValue(univ.Any): + pass + + +class AttributeValues(univ.SetOf): + pass + +AttributeValues.componentType = AttributeValue() + + +class SingleAttributeValues(univ.SetOf): + pass + +SingleAttributeValues.componentType = AttributeValue() + + +class SingleAttribute(univ.Sequence): + pass + +SingleAttribute.componentType = namedtype.NamedTypes( + namedtype.NamedType('type', AttributeType()), + namedtype.NamedType('values', + AttributeValues().subtype(sizeSpec=constraint.ValueSizeConstraint(1, 1)), + openType=opentype.OpenType('type', rfc5280.certificateAttributesMap) + ) +) + + +# CMSAttribute is the same as Attribute in RFC 5652, and CMSSingleAttribute +# is the companion where the attrValues SET must have one and only one member + +CMSAttribute = rfc5652.Attribute + + +class CMSSingleAttribute(univ.Sequence): + pass + +CMSSingleAttribute.componentType = namedtype.NamedTypes( + namedtype.NamedType('attrType', AttributeType()), + namedtype.NamedType('attrValues', + AttributeValues().subtype(sizeSpec=constraint.ValueSizeConstraint(1, 1)), + openType=opentype.OpenType('attrType', rfc5652.cmsAttributesMap) + ) +) + + +# DirectoryString is the same as RFC 5280, except the length is limited to 255 + +class DirectoryString(univ.Choice): + pass + +DirectoryString.componentType = namedtype.NamedTypes( + namedtype.NamedType('teletexString', char.TeletexString().subtype( + subtypeSpec=constraint.ValueSizeConstraint(1, 255))), + namedtype.NamedType('printableString', char.PrintableString().subtype( + subtypeSpec=constraint.ValueSizeConstraint(1, 255))), + namedtype.NamedType('universalString', char.UniversalString().subtype( + subtypeSpec=constraint.ValueSizeConstraint(1, 255))), + namedtype.NamedType('utf8String', char.UTF8String().subtype( + subtypeSpec=constraint.ValueSizeConstraint(1, 255))), + namedtype.NamedType('bmpString', char.BMPString().subtype( + subtypeSpec=constraint.ValueSizeConstraint(1, 255))) +) + + +# PKCS9String is DirectoryString with an additional choice of IA5String, +# and the SIZE is limited to 255 + +class PKCS9String(univ.Choice): + pass + +PKCS9String.componentType = namedtype.NamedTypes( + namedtype.NamedType('ia5String', char.IA5String().subtype( + subtypeSpec=constraint.ValueSizeConstraint(1, 255))), + namedtype.NamedType('directoryString', DirectoryString()) +) + + +# Upper Bounds + +pkcs_9_ub_pkcs9String = univ.Integer(255) + +pkcs_9_ub_challengePassword = univ.Integer(pkcs_9_ub_pkcs9String) + +pkcs_9_ub_emailAddress = univ.Integer(pkcs_9_ub_pkcs9String) + +pkcs_9_ub_friendlyName = univ.Integer(pkcs_9_ub_pkcs9String) + +pkcs_9_ub_match = univ.Integer(pkcs_9_ub_pkcs9String) + +pkcs_9_ub_signingDescription = univ.Integer(pkcs_9_ub_pkcs9String) + +pkcs_9_ub_unstructuredAddress = univ.Integer(pkcs_9_ub_pkcs9String) + +pkcs_9_ub_unstructuredName = univ.Integer(pkcs_9_ub_pkcs9String) + + +ub_name = univ.Integer(32768) + +pkcs_9_ub_placeOfBirth = univ.Integer(ub_name) + +pkcs_9_ub_pseudonym = univ.Integer(ub_name) + + +# Object Identifier Arcs + +ietf_at = _OID(1, 3, 6, 1, 5, 5, 7, 9) + +id_at = _OID(2, 5, 4) + +pkcs_9 = _OID(1, 2, 840, 113549, 1, 9) + +pkcs_9_mo = _OID(pkcs_9, 0) + +smime = _OID(pkcs_9, 16) + +certTypes = _OID(pkcs_9, 22) + +crlTypes = _OID(pkcs_9, 23) + +pkcs_9_oc = _OID(pkcs_9, 24) + +pkcs_9_at = _OID(pkcs_9, 25) + +pkcs_9_sx = _OID(pkcs_9, 26) + +pkcs_9_mr = _OID(pkcs_9, 27) + + +# Object Identifiers for Syntaxes for use with LDAP-accessible directories + +pkcs_9_sx_pkcs9String = _OID(pkcs_9_sx, 1) + +pkcs_9_sx_signingTime = _OID(pkcs_9_sx, 2) + + +# Object Identifiers for object classes + +pkcs_9_oc_pkcsEntity = _OID(pkcs_9_oc, 1) + +pkcs_9_oc_naturalPerson = _OID(pkcs_9_oc, 2) + + +# Object Identifiers for matching rules + +pkcs_9_mr_caseIgnoreMatch = _OID(pkcs_9_mr, 1) + +pkcs_9_mr_signingTimeMatch = _OID(pkcs_9_mr, 2) + + +# PKCS #7 PDU + +pkcs_9_at_pkcs7PDU = _OID(pkcs_9_at, 5) + +pKCS7PDU = Attribute() +pKCS7PDU['type'] = pkcs_9_at_pkcs7PDU +pKCS7PDU['values'][0] = ContentInfo() + + +# PKCS #12 token + +pkcs_9_at_userPKCS12 = _OID(2, 16, 840, 1, 113730, 3, 1, 216) + +userPKCS12 = Attribute() +userPKCS12['type'] = pkcs_9_at_userPKCS12 +userPKCS12['values'][0] = PFX() + + +# PKCS #15 token + +pkcs_9_at_pkcs15Token = _OID(pkcs_9_at, 1) + +# TODO: Once PKCS15Token can be imported, this can be included +# +# pKCS15Token = Attribute() +# userPKCS12['type'] = pkcs_9_at_pkcs15Token +# userPKCS12['values'][0] = PKCS15Token() + + +# PKCS #8 encrypted private key information + +pkcs_9_at_encryptedPrivateKeyInfo = _OID(pkcs_9_at, 2) + +encryptedPrivateKeyInfo = Attribute() +encryptedPrivateKeyInfo['type'] = pkcs_9_at_encryptedPrivateKeyInfo +encryptedPrivateKeyInfo['values'][0] = EncryptedPrivateKeyInfo() + + +# Electronic-mail address + +pkcs_9_at_emailAddress = rfc5280.id_emailAddress + +emailAddress = Attribute() +emailAddress['type'] = pkcs_9_at_emailAddress +emailAddress['values'][0] = EmailAddress() + + +# Unstructured name + +pkcs_9_at_unstructuredName = _OID(pkcs_9, 2) + +unstructuredName = Attribute() +unstructuredName['type'] = pkcs_9_at_unstructuredName +unstructuredName['values'][0] = PKCS9String() + + +# Unstructured address + +pkcs_9_at_unstructuredAddress = _OID(pkcs_9, 8) + +unstructuredAddress = Attribute() +unstructuredAddress['type'] = pkcs_9_at_unstructuredAddress +unstructuredAddress['values'][0] = DirectoryString() + + +# Date of birth + +pkcs_9_at_dateOfBirth = _OID(ietf_at, 1) + +dateOfBirth = SingleAttribute() +dateOfBirth['type'] = pkcs_9_at_dateOfBirth +dateOfBirth['values'][0] = useful.GeneralizedTime() + + +# Place of birth + +pkcs_9_at_placeOfBirth = _OID(ietf_at, 2) + +placeOfBirth = SingleAttribute() +placeOfBirth['type'] = pkcs_9_at_placeOfBirth +placeOfBirth['values'][0] = DirectoryString() + + +# Gender + +class GenderString(char.PrintableString): + pass + +GenderString.subtypeSpec = constraint.ValueSizeConstraint(1, 1) +GenderString.subtypeSpec = constraint.SingleValueConstraint("M", "F", "m", "f") + + +pkcs_9_at_gender = _OID(ietf_at, 3) + +gender = SingleAttribute() +gender['type'] = pkcs_9_at_gender +gender['values'][0] = GenderString() + + +# Country of citizenship + +pkcs_9_at_countryOfCitizenship = _OID(ietf_at, 4) + +countryOfCitizenship = Attribute() +countryOfCitizenship['type'] = pkcs_9_at_countryOfCitizenship +countryOfCitizenship['values'][0] = X520countryName() + + +# Country of residence + +pkcs_9_at_countryOfResidence = _OID(ietf_at, 5) + +countryOfResidence = Attribute() +countryOfResidence['type'] = pkcs_9_at_countryOfResidence +countryOfResidence['values'][0] = X520countryName() + + +# Pseudonym + +id_at_pseudonym = _OID(2, 5, 4, 65) + +pseudonym = Attribute() +pseudonym['type'] = id_at_pseudonym +pseudonym['values'][0] = DirectoryString() + + +# Serial number + +id_at_serialNumber = rfc5280.id_at_serialNumber + +serialNumber = Attribute() +serialNumber['type'] = id_at_serialNumber +serialNumber['values'][0] = X520SerialNumber() + + +# Content type + +pkcs_9_at_contentType = rfc5652.id_contentType + +contentType = CMSSingleAttribute() +contentType['attrType'] = pkcs_9_at_contentType +contentType['attrValues'][0] = ContentType() + + +# Message digest + +pkcs_9_at_messageDigest = rfc5652.id_messageDigest + +messageDigest = CMSSingleAttribute() +messageDigest['attrType'] = pkcs_9_at_messageDigest +messageDigest['attrValues'][0] = MessageDigest() + + +# Signing time + +pkcs_9_at_signingTime = rfc5652.id_signingTime + +signingTime = CMSSingleAttribute() +signingTime['attrType'] = pkcs_9_at_signingTime +signingTime['attrValues'][0] = SigningTime() + + +# Random nonce + +class RandomNonce(univ.OctetString): + pass + +RandomNonce.subtypeSpec = constraint.ValueSizeConstraint(4, MAX) + + +pkcs_9_at_randomNonce = _OID(pkcs_9_at, 3) + +randomNonce = CMSSingleAttribute() +randomNonce['attrType'] = pkcs_9_at_randomNonce +randomNonce['attrValues'][0] = RandomNonce() + + +# Sequence number + +class SequenceNumber(univ.Integer): + pass + +SequenceNumber.subtypeSpec = constraint.ValueRangeConstraint(1, MAX) + + +pkcs_9_at_sequenceNumber = _OID(pkcs_9_at, 4) + +sequenceNumber = CMSSingleAttribute() +sequenceNumber['attrType'] = pkcs_9_at_sequenceNumber +sequenceNumber['attrValues'][0] = SequenceNumber() + + +# Countersignature + +pkcs_9_at_counterSignature = rfc5652.id_countersignature + +counterSignature = CMSAttribute() +counterSignature['attrType'] = pkcs_9_at_counterSignature +counterSignature['attrValues'][0] = Countersignature() + + +# Challenge password + +pkcs_9_at_challengePassword = _OID(pkcs_9, 7) + +challengePassword = SingleAttribute() +challengePassword['type'] = pkcs_9_at_challengePassword +challengePassword['values'][0] = DirectoryString() + + +# Extension request + +class ExtensionRequest(Extensions): + pass + + +pkcs_9_at_extensionRequest = _OID(pkcs_9, 14) + +extensionRequest = SingleAttribute() +extensionRequest['type'] = pkcs_9_at_extensionRequest +extensionRequest['values'][0] = ExtensionRequest() + + +# Extended-certificate attributes (deprecated) + +class AttributeSet(univ.SetOf): + pass + +AttributeSet.componentType = Attribute() + + +pkcs_9_at_extendedCertificateAttributes = _OID(pkcs_9, 9) + +extendedCertificateAttributes = SingleAttribute() +extendedCertificateAttributes['type'] = pkcs_9_at_extendedCertificateAttributes +extendedCertificateAttributes['values'][0] = AttributeSet() + + +# Friendly name + +class FriendlyName(char.BMPString): + pass + +FriendlyName.subtypeSpec = constraint.ValueSizeConstraint(1, pkcs_9_ub_friendlyName) + + +pkcs_9_at_friendlyName = _OID(pkcs_9, 20) + +friendlyName = SingleAttribute() +friendlyName['type'] = pkcs_9_at_friendlyName +friendlyName['values'][0] = FriendlyName() + + +# Local key identifier + +pkcs_9_at_localKeyId = _OID(pkcs_9, 21) + +localKeyId = SingleAttribute() +localKeyId['type'] = pkcs_9_at_localKeyId +localKeyId['values'][0] = univ.OctetString() + + +# Signing description + +pkcs_9_at_signingDescription = _OID(pkcs_9, 13) + +signingDescription = CMSSingleAttribute() +signingDescription['attrType'] = pkcs_9_at_signingDescription +signingDescription['attrValues'][0] = DirectoryString() + + +# S/MIME capabilities + +class SMIMECapability(AlgorithmIdentifier): + pass + + +class SMIMECapabilities(univ.SequenceOf): + pass + +SMIMECapabilities.componentType = SMIMECapability() + + +pkcs_9_at_smimeCapabilities = _OID(pkcs_9, 15) + +smimeCapabilities = CMSSingleAttribute() +smimeCapabilities['attrType'] = pkcs_9_at_smimeCapabilities +smimeCapabilities['attrValues'][0] = SMIMECapabilities() + + +# Certificate Attribute Map + +certificateAttributesMapUpdate = { + # Attribute types for use with the "pkcsEntity" object class + pkcs_9_at_pkcs7PDU: ContentInfo(), + pkcs_9_at_userPKCS12: PFX(), + # TODO: Once PKCS15Token can be imported, this can be included + # pkcs_9_at_pkcs15Token: PKCS15Token(), + pkcs_9_at_encryptedPrivateKeyInfo: EncryptedPrivateKeyInfo(), + # Attribute types for use with the "naturalPerson" object class + pkcs_9_at_emailAddress: EmailAddress(), + pkcs_9_at_unstructuredName: PKCS9String(), + pkcs_9_at_unstructuredAddress: DirectoryString(), + pkcs_9_at_dateOfBirth: useful.GeneralizedTime(), + pkcs_9_at_placeOfBirth: DirectoryString(), + pkcs_9_at_gender: GenderString(), + pkcs_9_at_countryOfCitizenship: X520countryName(), + pkcs_9_at_countryOfResidence: X520countryName(), + id_at_pseudonym: DirectoryString(), + id_at_serialNumber: X520SerialNumber(), + # Attribute types for use with PKCS #10 certificate requests + pkcs_9_at_challengePassword: DirectoryString(), + pkcs_9_at_extensionRequest: ExtensionRequest(), + pkcs_9_at_extendedCertificateAttributes: AttributeSet(), +} + +rfc5280.certificateAttributesMap.update(certificateAttributesMapUpdate) + + +# CMS Attribute Map + +cmsAttributesMapUpdate = { + # Attribute types for use in PKCS #7 data (a.k.a. CMS) + pkcs_9_at_contentType: ContentType(), + pkcs_9_at_messageDigest: MessageDigest(), + pkcs_9_at_signingTime: SigningTime(), + pkcs_9_at_randomNonce: RandomNonce(), + pkcs_9_at_sequenceNumber: SequenceNumber(), + pkcs_9_at_counterSignature: Countersignature(), + # Attributes for use in PKCS #12 "PFX" PDUs or PKCS #15 tokens + pkcs_9_at_friendlyName: FriendlyName(), + pkcs_9_at_localKeyId: univ.OctetString(), + pkcs_9_at_signingDescription: DirectoryString(), + pkcs_9_at_smimeCapabilities: SMIMECapabilities(), +} + +rfc5652.cmsAttributesMap.update(cmsAttributesMapUpdate) diff --git a/pyasn1_modules/rfc3770.py b/pyasn1_modules/rfc3770.py new file mode 100644 index 0000000..a347fb3 --- /dev/null +++ b/pyasn1_modules/rfc3770.py @@ -0,0 +1,63 @@ +# +# 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 +# +# Certificate Extensions and Attributes Supporting Authentication +# in PPP and Wireless LAN Networks +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc3770.txt +# + +from pyasn1.type import constraint +from pyasn1.type import univ + +from pyasn1_modules import rfc5280 + + +MAX = float('inf') + + +# Extended Key Usage Values + +id_kp_eapOverLAN = univ.ObjectIdentifier('1.3.6.1.5.5.7.3.14') + +id_kp_eapOverPPP = univ.ObjectIdentifier('1.3.6.1.5.5.7.3.13') + + +# Wireless LAN SSID Extension + +id_pe_wlanSSID = univ.ObjectIdentifier('1.3.6.1.5.5.7.1.13') + + +class SSID(univ.OctetString): + pass + +SSID.subtypeSpec = constraint.ValueSizeConstraint(1, 32) + + +class SSIDList(univ.SequenceOf): + pass + +SSIDList.componentType = SSID() +SSIDList.subtypeSpec=constraint.ValueSizeConstraint(1, MAX) + + +# Wireless LAN SSID Attribute Certificate Attribute +# Uses same syntax as the certificate extension: SSIDList + +id_aca_wlanSSID = univ.ObjectIdentifier('1.3.6.1.5.5.7.10.6') + + +# Map of Certificate Extension OIDs to Extensions +# To be added to the ones that are in rfc5280.py + +_certificateExtensionsMap = { + id_pe_wlanSSID: SSIDList(), +} + +rfc5280.certificateExtensionsMap.update(_certificateExtensionsMap) diff --git a/pyasn1_modules/rfc5914.py b/pyasn1_modules/rfc5914.py new file mode 100644 index 0000000..d125ea2 --- /dev/null +++ b/pyasn1_modules/rfc5914.py @@ -0,0 +1,119 @@ +# This file is being contributed to 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 +# +# Trust Anchor Format +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc5914.txt + +from pyasn1.type import char +from pyasn1.type import constraint +from pyasn1.type import namedtype +from pyasn1.type import namedval +from pyasn1.type import tag +from pyasn1.type import univ + +from pyasn1_modules import rfc5280 + + +MAX = float('inf') + +Certificate = rfc5280.Certificate + +Name = rfc5280.Name + +Extensions = rfc5280.Extensions + +SubjectPublicKeyInfo = rfc5280.SubjectPublicKeyInfo + +TBSCertificate = rfc5280.TBSCertificate + +CertificatePolicies = rfc5280.CertificatePolicies + +KeyIdentifier = rfc5280.KeyIdentifier + +NameConstraints = rfc5280.NameConstraints + + +class CertPolicyFlags(univ.BitString): + pass + +CertPolicyFlags.namedValues = namedval.NamedValues( + ('inhibitPolicyMapping', 0), + ('requireExplicitPolicy', 1), + ('inhibitAnyPolicy', 2) +) + + +class CertPathControls(univ.Sequence): + pass + +CertPathControls.componentType = namedtype.NamedTypes( + namedtype.NamedType('taName', Name()), + namedtype.OptionalNamedType('certificate', Certificate().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.OptionalNamedType('policySet', CertificatePolicies().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.OptionalNamedType('policyFlags', CertPolicyFlags().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))), + namedtype.OptionalNamedType('nameConstr', NameConstraints().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))), + namedtype.OptionalNamedType('pathLenConstraint', univ.Integer().subtype( + subtypeSpec=constraint.ValueRangeConstraint(0, MAX)).subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4))) +) + + +class TrustAnchorTitle(char.UTF8String): + pass + +TrustAnchorTitle.subtypeSpec = constraint.ValueSizeConstraint(1, 64) + + +class TrustAnchorInfoVersion(univ.Integer): + pass + +TrustAnchorInfoVersion.namedValues = namedval.NamedValues( + ('v1', 1) +) + + +class TrustAnchorInfo(univ.Sequence): + pass + +TrustAnchorInfo.componentType = namedtype.NamedTypes( + namedtype.DefaultedNamedType('version', TrustAnchorInfoVersion().subtype(value='v1')), + namedtype.NamedType('pubKey', SubjectPublicKeyInfo()), + namedtype.NamedType('keyId', KeyIdentifier()), + namedtype.OptionalNamedType('taTitle', TrustAnchorTitle()), + namedtype.OptionalNamedType('certPath', CertPathControls()), + namedtype.OptionalNamedType('exts', Extensions().subtype(explicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.OptionalNamedType('taTitleLangTag', char.UTF8String().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))) +) + + +class TrustAnchorChoice(univ.Choice): + pass + +TrustAnchorChoice.componentType = namedtype.NamedTypes( + namedtype.NamedType('certificate', Certificate()), + namedtype.NamedType('tbsCert', TBSCertificate().subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.NamedType('taInfo', TrustAnchorInfo().subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2))) +) + + +id_ct_trustAnchorList = univ.ObjectIdentifier('1.2.840.113549.1.9.16.1.34') + +class TrustAnchorList(univ.SequenceOf): + pass + +TrustAnchorList.componentType = TrustAnchorChoice() +TrustAnchorList.subtypeSpec=constraint.ValueSizeConstraint(1, MAX) diff --git a/pyasn1_modules/rfc6010.py b/pyasn1_modules/rfc6010.py new file mode 100644 index 0000000..250e207 --- /dev/null +++ b/pyasn1_modules/rfc6010.py @@ -0,0 +1,88 @@ +# +# This file is part of pyasn1-modules software. +# +# Created by Russ Housley with assistance from asn1ate v.0.6.0. +# Modified by Russ Housley to add maps for use with opentypes. +# +# Copyright (c) 2019, Vigil Security, LLC +# License: http://snmplabs.com/pyasn1/license.html +# +# Certificate Extension for CMS Content Constraints (CCC) +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc6010.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 rfc5280 + +MAX = float('inf') + + +AttributeType = rfc5280.AttributeType + +AttributeValue = rfc5280.AttributeValue + + +id_ct_anyContentType = univ.ObjectIdentifier('1.2.840.113549.1.9.16.1.0') + + +class AttrConstraint(univ.Sequence): + pass + +AttrConstraint.componentType = namedtype.NamedTypes( + namedtype.NamedType('attrType', AttributeType()), + namedtype.NamedType('attrValues', univ.SetOf( + componentType=AttributeValue()).subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))) +) + + +class AttrConstraintList(univ.SequenceOf): + pass + +AttrConstraintList.componentType = AttrConstraint() +AttrConstraintList.subtypeSpec=constraint.ValueSizeConstraint(1, MAX) + + +class ContentTypeGeneration(univ.Enumerated): + pass + +ContentTypeGeneration.namedValues = namedval.NamedValues( + ('canSource', 0), + ('cannotSource', 1) +) + + +class ContentTypeConstraint(univ.Sequence): + pass + +ContentTypeConstraint.componentType = namedtype.NamedTypes( + namedtype.NamedType('contentType', univ.ObjectIdentifier()), + namedtype.DefaultedNamedType('canSource', ContentTypeGeneration().subtype(value='canSource')), + namedtype.OptionalNamedType('attrConstraints', AttrConstraintList()) +) + + +# CMS Content Constraints (CCC) Extension and Object Identifier + +id_pe_cmsContentConstraints = univ.ObjectIdentifier('1.3.6.1.5.5.7.1.18') + +class CMSContentConstraints(univ.SequenceOf): + pass + +CMSContentConstraints.componentType = ContentTypeConstraint() +CMSContentConstraints.subtypeSpec=constraint.ValueSizeConstraint(1, MAX) + + +# Map of Certificate Extension OIDs to Extensions +# To be added to the ones that are in rfc5280.py + +_certificateExtensionsMap = { + id_pe_cmsContentConstraints: CMSContentConstraints(), +} + +rfc5280.certificateExtensionsMap.update(_certificateExtensionsMap) diff --git a/pyasn1_modules/rfc6031.py b/pyasn1_modules/rfc6031.py new file mode 100644 index 0000000..6e1bb22 --- /dev/null +++ b/pyasn1_modules/rfc6031.py @@ -0,0 +1,469 @@ +# +# 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 Symmetric Key Package Content Type +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc6031.txt +# + +from pyasn1.type import char +from pyasn1.type import constraint +from pyasn1.type import namedtype +from pyasn1.type import namedval +from pyasn1.type import opentype +from pyasn1.type import tag +from pyasn1.type import univ +from pyasn1.type import useful + +from pyasn1_modules import rfc5652 +from pyasn1_modules import rfc6019 + + +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) + + +MAX = float('inf') + +id_pskc = univ.ObjectIdentifier('1.2.840.113549.1.9.16.12') + + +# Symmetric Key Package Attributes + +id_pskc_manufacturer = _OID(id_pskc, 1) + +class at_pskc_manufacturer(char.UTF8String): + pass + + +id_pskc_serialNo = _OID(id_pskc, 2) + +class at_pskc_serialNo(char.UTF8String): + pass + + +id_pskc_model = _OID(id_pskc, 3) + +class at_pskc_model(char.UTF8String): + pass + + +id_pskc_issueNo = _OID(id_pskc, 4) + +class at_pskc_issueNo(char.UTF8String): + pass + + +id_pskc_deviceBinding = _OID(id_pskc, 5) + +class at_pskc_deviceBinding(char.UTF8String): + pass + + +id_pskc_deviceStartDate = _OID(id_pskc, 6) + +class at_pskc_deviceStartDate(useful.GeneralizedTime): + pass + + +id_pskc_deviceExpiryDate = _OID(id_pskc, 7) + +class at_pskc_deviceExpiryDate(useful.GeneralizedTime): + pass + + +id_pskc_moduleId = _OID(id_pskc, 8) + +class at_pskc_moduleId(char.UTF8String): + pass + + +id_pskc_deviceUserId = _OID(id_pskc, 26) + +class at_pskc_deviceUserId(char.UTF8String): + pass + + +# Symmetric Key Attributes + +id_pskc_keyId = _OID(id_pskc, 9) + +class at_pskc_keyUserId(char.UTF8String): + pass + + +id_pskc_algorithm = _OID(id_pskc, 10) + +class at_pskc_algorithm(char.UTF8String): + pass + + +id_pskc_issuer = _OID(id_pskc, 11) + +class at_pskc_issuer(char.UTF8String): + pass + + +id_pskc_keyProfileId = _OID(id_pskc, 12) + +class at_pskc_keyProfileId(char.UTF8String): + pass + + +id_pskc_keyReference = _OID(id_pskc, 13) + +class at_pskc_keyReference(char.UTF8String): + pass + + +id_pskc_friendlyName = _OID(id_pskc, 14) + +class FriendlyName(univ.Sequence): + pass + +FriendlyName.componentType = namedtype.NamedTypes( + namedtype.NamedType('friendlyName', char.UTF8String()), + namedtype.OptionalNamedType('friendlyNameLangTag', char.UTF8String()) +) + +class at_pskc_friendlyName(FriendlyName): + pass + + +id_pskc_algorithmParameters = _OID(id_pskc, 15) + +class Encoding(char.UTF8String): + pass + +Encoding.namedValues = namedval.NamedValues( + ('dec', "DECIMAL"), + ('hex', "HEXADECIMAL"), + ('alpha', "ALPHANUMERIC"), + ('b64', "BASE64"), + ('bin', "BINARY") +) + +Encoding.subtypeSpec = constraint.SingleValueConstraint( + "DECIMAL", "HEXADECIMAL", "ALPHANUMERIC", "BASE64", "BINARY" ) + +class ChallengeFormat(univ.Sequence): + pass + +ChallengeFormat.componentType = namedtype.NamedTypes( + namedtype.NamedType('encoding', Encoding()), + namedtype.DefaultedNamedType('checkDigit', + univ.Boolean().subtype(value=0)), + namedtype.NamedType('min', univ.Integer().subtype( + subtypeSpec=constraint.ValueRangeConstraint(0, MAX))), + namedtype.NamedType('max', univ.Integer().subtype( + subtypeSpec=constraint.ValueRangeConstraint(0, MAX))) +) + +class ResponseFormat(univ.Sequence): + pass + +ResponseFormat.componentType = namedtype.NamedTypes( + namedtype.NamedType('encoding', Encoding()), + namedtype.NamedType('length', univ.Integer().subtype( + subtypeSpec=constraint.ValueRangeConstraint(0, MAX))), + namedtype.DefaultedNamedType('checkDigit', + univ.Boolean().subtype(value=0)) +) + +class PSKCAlgorithmParameters(univ.Choice): + pass + +PSKCAlgorithmParameters.componentType = namedtype.NamedTypes( + namedtype.NamedType('suite', char.UTF8String()), + namedtype.NamedType('challengeFormat', ChallengeFormat().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))), + namedtype.NamedType('responseFormat', ResponseFormat().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))) +) + +class at_pskc_algorithmParameters(PSKCAlgorithmParameters): + pass + + +id_pskc_counter = _OID(id_pskc, 16) + +class at_pskc_counter(univ.Integer): + pass + +at_pskc_counter.subtypeSpec = constraint.ValueRangeConstraint(0, MAX) + + +id_pskc_time = _OID(id_pskc, 17) + +class at_pskc_time(rfc6019.BinaryTime): + pass + + +id_pskc_timeInterval = _OID(id_pskc, 18) + +class at_pskc_timeInterval(univ.Integer): + pass + +at_pskc_timeInterval.subtypeSpec = constraint.ValueRangeConstraint(0, MAX) + + +id_pskc_timeDrift = _OID(id_pskc, 19) + +class at_pskc_timeDrift(univ.Integer): + pass + +at_pskc_timeDrift.subtypeSpec = constraint.ValueRangeConstraint(0, MAX) + + +id_pskc_valueMAC = _OID(id_pskc, 20) + +class ValueMac(univ.Sequence): + pass + +ValueMac.componentType = namedtype.NamedTypes( + namedtype.NamedType('macAlgorithm', char.UTF8String()), + namedtype.NamedType('mac', char.UTF8String()) +) + +class at_pskc_valueMAC(ValueMac): + pass + + +id_pskc_keyUserId = _OID(id_pskc, 27) + +class at_pskc_keyId(char.UTF8String): + pass + + +id_pskc_keyStartDate = _OID(id_pskc, 21) + +class at_pskc_keyStartDate(useful.GeneralizedTime): + pass + + +id_pskc_keyExpiryDate = _OID(id_pskc, 22) + +class at_pskc_keyExpiryDate(useful.GeneralizedTime): + pass + + +id_pskc_numberOfTransactions = _OID(id_pskc, 23) + +class at_pskc_numberOfTransactions(univ.Integer): + pass + +at_pskc_numberOfTransactions.subtypeSpec = constraint.ValueRangeConstraint(0, MAX) + + +id_pskc_keyUsages = _OID(id_pskc, 24) + +class PSKCKeyUsage(char.UTF8String): + pass + +PSKCKeyUsage.namedValues = namedval.NamedValues( + ('otp', "OTP"), + ('cr', "CR"), + ('encrypt', "Encrypt"), + ('integrity', "Integrity"), + ('verify', "Verify"), + ('unlock', "Unlock"), + ('decrypt', "Decrypt"), + ('keywrap', "KeyWrap"), + ('unwrap', "Unwrap"), + ('derive', "Derive"), + ('generate', "Generate") +) + +PSKCKeyUsage.subtypeSpec = constraint.SingleValueConstraint( + "OTP", "CR", "Encrypt", "Integrity", "Verify", "Unlock", + "Decrypt", "KeyWrap", "Unwrap", "Derive", "Generate" ) + +class PSKCKeyUsages(univ.SequenceOf): + pass + +PSKCKeyUsages.componentType = PSKCKeyUsage() + +class at_pskc_keyUsage(PSKCKeyUsages): + pass + + +id_pskc_pinPolicy = _OID(id_pskc, 25) + +class PINUsageMode(char.UTF8String): + pass + +PINUsageMode.namedValues = namedval.NamedValues( + ("local", "Local"), + ("prepend", "Prepend"), + ("append", "Append"), + ("algorithmic", "Algorithmic") +) + +PINUsageMode.subtypeSpec = constraint.SingleValueConstraint( + "Local", "Prepend", "Append", "Algorithmic" ) + +class PINPolicy(univ.Sequence): + pass + +PINPolicy.componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('pinKeyId', char.UTF8String().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.NamedType('pinUsageMode', PINUsageMode().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.OptionalNamedType('maxFailedAttempts', univ.Integer().subtype( + subtypeSpec=constraint.ValueRangeConstraint(0, MAX)).subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))), + namedtype.OptionalNamedType('minLength', univ.Integer().subtype( + subtypeSpec=constraint.ValueRangeConstraint(0, MAX)).subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))), + namedtype.OptionalNamedType('maxLength', univ.Integer().subtype( + subtypeSpec=constraint.ValueRangeConstraint(0, MAX)).subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4))), + namedtype.OptionalNamedType('pinEncoding', Encoding().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 5))) +) + +class at_pskc_pinPolicy(PINPolicy): + pass + + +# Map of Symmetric Key Package Attribute OIDs to Attributes + +sKeyPkgAttributesMap = { + id_pskc_manufacturer: at_pskc_manufacturer(), + id_pskc_serialNo: at_pskc_serialNo(), + id_pskc_model: at_pskc_model(), + id_pskc_issueNo: at_pskc_issueNo(), + id_pskc_deviceBinding: at_pskc_deviceBinding(), + id_pskc_deviceStartDate: at_pskc_deviceStartDate(), + id_pskc_deviceExpiryDate: at_pskc_deviceExpiryDate(), + id_pskc_moduleId: at_pskc_moduleId(), + id_pskc_deviceUserId: at_pskc_deviceUserId(), +} + + +# Map of Symmetric Key Attribute OIDs to Attributes + +sKeyAttributesMap = { + id_pskc_keyId: at_pskc_keyId(), + id_pskc_algorithm: at_pskc_algorithm(), + id_pskc_issuer: at_pskc_issuer(), + id_pskc_keyProfileId: at_pskc_keyProfileId(), + id_pskc_keyReference: at_pskc_keyReference(), + id_pskc_friendlyName: at_pskc_friendlyName(), + id_pskc_algorithmParameters: at_pskc_algorithmParameters(), + id_pskc_counter: at_pskc_counter(), + id_pskc_time: at_pskc_time(), + id_pskc_timeInterval: at_pskc_timeInterval(), + id_pskc_timeDrift: at_pskc_timeDrift(), + id_pskc_valueMAC: at_pskc_valueMAC(), + id_pskc_keyUserId: at_pskc_keyUserId(), + id_pskc_keyStartDate: at_pskc_keyStartDate(), + id_pskc_keyExpiryDate: at_pskc_keyExpiryDate(), + id_pskc_numberOfTransactions: at_pskc_numberOfTransactions(), + id_pskc_keyUsages: at_pskc_keyUsage(), + id_pskc_pinPolicy: at_pskc_pinPolicy(), +} + + +# This definition replaces Attribute() from rfc5652.py; it is the same except +# that opentype is added with sKeyPkgAttributesMap and sKeyAttributesMap + +class AttributeType(univ.ObjectIdentifier): + pass + + +class AttributeValue(univ.Any): + pass + + +class SKeyAttribute(univ.Sequence): + pass + +SKeyAttribute.componentType = namedtype.NamedTypes( + namedtype.NamedType('attrType', AttributeType()), + namedtype.NamedType('attrValues', + univ.SetOf(componentType=AttributeValue()), + openType=opentype.OpenType('attrType', sKeyAttributesMap) + ) +) + + +class SKeyPkgAttribute(univ.Sequence): + pass + +SKeyPkgAttribute.componentType = namedtype.NamedTypes( + namedtype.NamedType('attrType', AttributeType()), + namedtype.NamedType('attrValues', + univ.SetOf(componentType=AttributeValue()), + openType=opentype.OpenType('attrType', sKeyPkgAttributesMap) + ) +) + + +# Symmetric Key Package Content Type + +id_ct_KP_sKeyPackage = univ.ObjectIdentifier('1.2.840.113549.1.9.16.1.25') + + +class KeyPkgVersion(univ.Integer): + pass + +KeyPkgVersion.namedValues = namedval.NamedValues( + ('v1', 1) +) + + +class OneSymmetricKey(univ.Sequence): + pass + +OneSymmetricKey.componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('sKeyAttrs', + univ.SequenceOf(componentType=SKeyAttribute()).subtype( + subtypeSpec=constraint.ValueSizeConstraint(1, MAX))), + namedtype.OptionalNamedType('sKey', univ.OctetString()) +) + +OneSymmetricKey.sizeSpec = univ.Sequence.sizeSpec + constraint.ValueSizeConstraint(1, 2) + + +class SymmetricKeys(univ.SequenceOf): + pass + +SymmetricKeys.componentType = OneSymmetricKey() +SymmetricKeys.subtypeSpec=constraint.ValueSizeConstraint(1, MAX) + + +class SymmetricKeyPackage(univ.Sequence): + pass + +SymmetricKeyPackage.componentType = namedtype.NamedTypes( + namedtype.DefaultedNamedType('version', KeyPkgVersion().subtype(value='v1')), + namedtype.OptionalNamedType('sKeyPkgAttrs', + univ.SequenceOf(componentType=SKeyPkgAttribute()).subtype( + subtypeSpec=constraint.ValueSizeConstraint(1, MAX), + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.NamedType('sKeys', SymmetricKeys()) +) + + +# Map of Content Type OIDs to Content Types are +# added to the ones that are in rfc5652.py + +_cmsContentTypesMapUpdate = { + id_ct_KP_sKeyPackage: SymmetricKeyPackage(), +} + +rfc5652.cmsContentTypesMap.update(_cmsContentTypesMapUpdate) diff --git a/pyasn1_modules/rfc6032.py b/pyasn1_modules/rfc6032.py new file mode 100644 index 0000000..563639a --- /dev/null +++ b/pyasn1_modules/rfc6032.py @@ -0,0 +1,68 @@ +# +# 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 Encrypted Key Package Content Type +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc6032.txt +# + +from pyasn1.type import namedtype +from pyasn1.type import tag +from pyasn1.type import univ + +from pyasn1_modules import rfc5652 +from pyasn1_modules import rfc5083 + + +# Content Decryption Key Identifier attribute + +id_aa_KP_contentDecryptKeyID = univ.ObjectIdentifier('2.16.840.1.101.2.1.5.66') + +class ContentDecryptKeyID(univ.OctetString): + pass + +aa_content_decrypt_key_identifier = rfc5652.Attribute() +aa_content_decrypt_key_identifier['attrType'] = id_aa_KP_contentDecryptKeyID +aa_content_decrypt_key_identifier['attrValues'][0] = ContentDecryptKeyID() + + +# Encrypted Key Package Content Type + +id_ct_KP_encryptedKeyPkg = univ.ObjectIdentifier('2.16.840.1.101.2.1.2.78.2') + +class EncryptedKeyPackage(univ.Choice): + pass + +EncryptedKeyPackage.componentType = namedtype.NamedTypes( + namedtype.NamedType('encrypted', rfc5652.EncryptedData()), + namedtype.NamedType('enveloped', rfc5652.EnvelopedData().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.NamedType('authEnveloped', rfc5083.AuthEnvelopedData().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))) +) + + +# Map of Attribute Type OIDs to Attributes are +# added to the ones that are in rfc5652.py + +_cmsAttributesMapUpdate = { + id_aa_KP_contentDecryptKeyID: ContentDecryptKeyID(), +} + +rfc5652.cmsAttributesMap.update(_cmsAttributesMapUpdate) + + +# Map of Content Type OIDs to Content Types are +# added to the ones that are in rfc5652.py + +_cmsContentTypesMapUpdate = { + id_ct_KP_encryptedKeyPkg: EncryptedKeyPackage(), +} + +rfc5652.cmsContentTypesMap.update(_cmsContentTypesMapUpdate) diff --git a/pyasn1_modules/rfc7030.py b/pyasn1_modules/rfc7030.py new file mode 100644 index 0000000..84b6dc5 --- /dev/null +++ b/pyasn1_modules/rfc7030.py @@ -0,0 +1,66 @@ +# +# 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 +# +# Enrollment over Secure Transport (EST) +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc7030.txt +# + +from pyasn1.type import constraint +from pyasn1.type import namedtype +from pyasn1.type import univ + +from pyasn1_modules import rfc5652 + +MAX = float('inf') + + +# Imports from RFC 5652 + +Attribute = rfc5652.Attribute + + +# Asymmetric Decrypt Key Identifier Attribute + +id_aa_asymmDecryptKeyID = univ.ObjectIdentifier('1.2.840.113549.1.9.16.2.54') + +class AsymmetricDecryptKeyIdentifier(univ.OctetString): + pass + + +aa_asymmDecryptKeyID = Attribute() +aa_asymmDecryptKeyID['attrType'] = id_aa_asymmDecryptKeyID +aa_asymmDecryptKeyID['attrValues'][0] = AsymmetricDecryptKeyIdentifier() + + +# CSR Attributes + +class AttrOrOID(univ.Choice): + pass + +AttrOrOID.componentType = namedtype.NamedTypes( + namedtype.NamedType('oid', univ.ObjectIdentifier()), + namedtype.NamedType('attribute', Attribute()) +) + + +class CsrAttrs(univ.SequenceOf): + pass + +CsrAttrs.componentType = AttrOrOID() +CsrAttrs.subtypeSpec=constraint.ValueSizeConstraint(0, MAX) + + +# Update CMS Attribute Map + +_cmsAttributesMapUpdate = { + id_aa_asymmDecryptKeyID: AsymmetricDecryptKeyIdentifier(), +} + +rfc5652.cmsAttributesMap.update(_cmsAttributesMapUpdate) diff --git a/pyasn1_modules/rfc7292.py b/pyasn1_modules/rfc7292.py new file mode 100644 index 0000000..1c9f319 --- /dev/null +++ b/pyasn1_modules/rfc7292.py @@ -0,0 +1,357 @@ +# This file is being contributed to pyasn1-modules software. +# +# Created by Russ Housley with assistance from the asn1ate tool. +# +# Copyright (c) 2019, Vigil Security, LLC +# License: http://snmplabs.com/pyasn1/license.html +# +# PKCS #12: Personal Information Exchange Syntax v1.1 +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc7292.txt +# https://www.rfc-editor.org/errata_search.php?rfc=7292 + +from pyasn1.type import char +from pyasn1.type import constraint +from pyasn1.type import namedtype +from pyasn1.type import namedval +from pyasn1.type import opentype +from pyasn1.type import tag +from pyasn1.type import univ + +from pyasn1_modules import rfc2315 +from pyasn1_modules import rfc5652 +from pyasn1_modules import rfc5280 +from pyasn1_modules import rfc5958 + + +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) + + +# Initialize the maps used in PKCS#12 + +pkcs12BagTypeMap = { } + +pkcs12CertBagMap = { } + +pkcs12CRLBagMap = { } + +pkcs12SecretBagMap = { } + + +# Imports from RFC 2315, RFC 5652, and RFC 5958 + +DigestInfo = rfc2315.DigestInfo + + +ContentInfo = rfc5652.ContentInfo + +PKCS12Attribute = rfc5652.Attribute + + +EncryptedPrivateKeyInfo = rfc5958.EncryptedPrivateKeyInfo + +PrivateKeyInfo = rfc5958.PrivateKeyInfo + + +# CMSSingleAttribute is the same as Attribute in RFC 5652 except the attrValues +# SET must have one and only one member + +class AttributeType(univ.ObjectIdentifier): + pass + + +class AttributeValue(univ.Any): + pass + + +class AttributeValues(univ.SetOf): + pass + +AttributeValues.componentType = AttributeValue() + + +class CMSSingleAttribute(univ.Sequence): + pass + +CMSSingleAttribute.componentType = namedtype.NamedTypes( + namedtype.NamedType('attrType', AttributeType()), + namedtype.NamedType('attrValues', + AttributeValues().subtype(sizeSpec=constraint.ValueSizeConstraint(1, 1)), + openType=opentype.OpenType('attrType', rfc5652.cmsAttributesMap) + ) +) + + +# Object identifier arcs + +rsadsi = _OID(1, 2, 840, 113549) + +pkcs = _OID(rsadsi, 1) + +pkcs_9 = _OID(pkcs, 9) + +certTypes = _OID(pkcs_9, 22) + +crlTypes = _OID(pkcs_9, 23) + +pkcs_12 = _OID(pkcs, 12) + + +# PBE Algorithm Identifiers and Parameters Structure + +pkcs_12PbeIds = _OID(pkcs_12, 1) + +pbeWithSHAAnd128BitRC4 = _OID(pkcs_12PbeIds, 1) + +pbeWithSHAAnd40BitRC4 = _OID(pkcs_12PbeIds, 2) + +pbeWithSHAAnd3_KeyTripleDES_CBC = _OID(pkcs_12PbeIds, 3) + +pbeWithSHAAnd2_KeyTripleDES_CBC = _OID(pkcs_12PbeIds, 4) + +pbeWithSHAAnd128BitRC2_CBC = _OID(pkcs_12PbeIds, 5) + +pbeWithSHAAnd40BitRC2_CBC = _OID(pkcs_12PbeIds, 6) + + +class Pkcs_12PbeParams(univ.Sequence): + pass + +Pkcs_12PbeParams.componentType = namedtype.NamedTypes( + namedtype.NamedType('salt', univ.OctetString()), + namedtype.NamedType('iterations', univ.Integer()) +) + + +# Bag types + +bagtypes = _OID(pkcs_12, 10, 1) + +class BAG_TYPE(univ.Sequence): + pass + +BAG_TYPE.componentType = namedtype.NamedTypes( + namedtype.NamedType('id', univ.ObjectIdentifier()), + namedtype.NamedType('unnamed1', univ.Any(), + openType=opentype.OpenType('attrType', pkcs12BagTypeMap) + ) +) + + +id_keyBag = _OID(bagtypes, 1) + +class KeyBag(PrivateKeyInfo): + pass + + +id_pkcs8ShroudedKeyBag = _OID(bagtypes, 2) + +class PKCS8ShroudedKeyBag(EncryptedPrivateKeyInfo): + pass + + +id_certBag = _OID(bagtypes, 3) + +class CertBag(univ.Sequence): + pass + +CertBag.componentType = namedtype.NamedTypes( + namedtype.NamedType('certId', univ.ObjectIdentifier()), + namedtype.NamedType('certValue', + univ.Any().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)), + openType=opentype.OpenType('certId', pkcs12CertBagMap) + ) +) + + +x509Certificate = CertBag() +x509Certificate['certId'] = _OID(certTypes, 1) +x509Certificate['certValue'] = univ.OctetString() +# DER-encoded X.509 certificate stored in OCTET STRING + + +sdsiCertificate = CertBag() +sdsiCertificate['certId'] = _OID(certTypes, 2) +sdsiCertificate['certValue'] = char.IA5String() +# Base64-encoded SDSI certificate stored in IA5String + + +id_CRLBag = _OID(bagtypes, 4) + +class CRLBag(univ.Sequence): + pass + +CRLBag.componentType = namedtype.NamedTypes( + namedtype.NamedType('crlId', univ.ObjectIdentifier()), + namedtype.NamedType('crlValue', + univ.Any().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)), + openType=opentype.OpenType('crlId', pkcs12CRLBagMap) + ) +) + + +x509CRL = CRLBag() +x509CRL['crlId'] = _OID(crlTypes, 1) +x509CRL['crlValue'] = univ.OctetString() +# DER-encoded X.509 CRL stored in OCTET STRING + + +id_secretBag = _OID(bagtypes, 5) + +class SecretBag(univ.Sequence): + pass + +SecretBag.componentType = namedtype.NamedTypes( + namedtype.NamedType('secretTypeId', univ.ObjectIdentifier()), + namedtype.NamedType('secretValue', + univ.Any().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)), + openType=opentype.OpenType('secretTypeId', pkcs12SecretBagMap) + ) +) + + +id_safeContentsBag = _OID(bagtypes, 6) + +class SafeBag(univ.Sequence): + pass + +SafeBag.componentType = namedtype.NamedTypes( + namedtype.NamedType('bagId', univ.ObjectIdentifier()), + namedtype.NamedType('bagValue', + univ.Any().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)), + openType=opentype.OpenType('bagId', pkcs12BagTypeMap) + ), + namedtype.OptionalNamedType('bagAttributes', + univ.SetOf(componentType=PKCS12Attribute()) + ) +) + + +class SafeContents(univ.SequenceOf): + pass + +SafeContents.componentType = SafeBag() + + +# The PFX PDU + +class AuthenticatedSafe(univ.SequenceOf): + pass + +AuthenticatedSafe.componentType = ContentInfo() +# Data if unencrypted +# EncryptedData if password-encrypted +# EnvelopedData if public key-encrypted + + +class MacData(univ.Sequence): + pass + +MacData.componentType = namedtype.NamedTypes( + namedtype.NamedType('mac', DigestInfo()), + namedtype.NamedType('macSalt', univ.OctetString()), + namedtype.DefaultedNamedType('iterations', univ.Integer().subtype(value=1)) + # Note: The default is for historical reasons and its use is deprecated +) + + +class PFX(univ.Sequence): + pass + +PFX.componentType = namedtype.NamedTypes( + namedtype.NamedType('version', + univ.Integer(namedValues=namedval.NamedValues(('v3', 3))) + ), + namedtype.NamedType('authSafe', ContentInfo()), + namedtype.OptionalNamedType('macData', MacData()) +) + + +# Local key identifier (also defined as certificateAttribute in rfc2985.py) + +pkcs_9_at_localKeyId = _OID(pkcs_9, 21) + +localKeyId = CMSSingleAttribute() +localKeyId['attrType'] = pkcs_9_at_localKeyId +localKeyId['attrValues'][0] = univ.OctetString() + + +# Friendly name (also defined as certificateAttribute in rfc2985.py) + +pkcs_9_ub_pkcs9String = univ.Integer(255) + +pkcs_9_ub_friendlyName = univ.Integer(pkcs_9_ub_pkcs9String) + +pkcs_9_at_friendlyName = _OID(pkcs_9, 20) + +class FriendlyName(char.BMPString): + pass + +FriendlyName.subtypeSpec = constraint.ValueSizeConstraint(1, pkcs_9_ub_friendlyName) + + +friendlyName = CMSSingleAttribute() +friendlyName['attrType'] = pkcs_9_at_friendlyName +friendlyName['attrValues'][0] = FriendlyName() + + +# Update the PKCS#12 maps + +_pkcs12BagTypeMap = { + id_keyBag: KeyBag(), + id_pkcs8ShroudedKeyBag: PKCS8ShroudedKeyBag(), + id_certBag: CertBag(), + id_CRLBag: CRLBag(), + id_secretBag: SecretBag(), + id_safeContentsBag: SafeBag(), +} + +pkcs12BagTypeMap.update(_pkcs12BagTypeMap) + + +_pkcs12CertBagMap = { + _OID(certTypes, 1): univ.OctetString(), + _OID(certTypes, 2): char.IA5String(), +} + +pkcs12CertBagMap.update(_pkcs12CertBagMap) + + +_pkcs12CRLBagMap = { + _OID(crlTypes, 1): univ.OctetString(), +} + +pkcs12CRLBagMap.update(_pkcs12CRLBagMap) + + +# Update the Algorithm Identifier map + +_algorithmIdentifierMapUpdate = { + pbeWithSHAAnd128BitRC4: Pkcs_12PbeParams(), + pbeWithSHAAnd40BitRC4: Pkcs_12PbeParams(), + pbeWithSHAAnd3_KeyTripleDES_CBC: Pkcs_12PbeParams(), + pbeWithSHAAnd2_KeyTripleDES_CBC: Pkcs_12PbeParams(), + pbeWithSHAAnd128BitRC2_CBC: Pkcs_12PbeParams(), + pbeWithSHAAnd40BitRC2_CBC: Pkcs_12PbeParams(), +} + +rfc5280.algorithmIdentifierMap.update(_algorithmIdentifierMapUpdate) + + +# Update the CMS Attribute map + +_cmsAttributesMapUpdate = { + pkcs_9_at_friendlyName: FriendlyName(), + pkcs_9_at_localKeyId: univ.OctetString(), +} + +rfc5652.cmsAttributesMap.update(_cmsAttributesMapUpdate) diff --git a/pyasn1_modules/rfc8018.py b/pyasn1_modules/rfc8018.py new file mode 100644 index 0000000..7a44eea --- /dev/null +++ b/pyasn1_modules/rfc8018.py @@ -0,0 +1,260 @@ +# +# 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 #5: Password-Based Cryptography Specification, Version 2.1 +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc8018.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 rfc3565 +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) + + +# Import from RFC 3565 + +AES_IV = rfc3565.AES_IV + + +# Import from RFC 5280 + +AlgorithmIdentifier = rfc5280.AlgorithmIdentifier + + +# Basic object identifiers + +nistAlgorithms = _OID(2, 16, 840, 1, 101, 3, 4) + +aes = _OID(nistAlgorithms, 1) + +oiw = _OID(1, 3, 14) + +rsadsi = _OID(1, 2, 840, 113549) + +pkcs = _OID(rsadsi, 1) + +digestAlgorithm = _OID(rsadsi, 2) + +encryptionAlgorithm = _OID(rsadsi, 3) + +pkcs_5 = _OID(pkcs, 5) + + + +# HMAC object identifiers + +id_hmacWithSHA1 = _OID(digestAlgorithm, 7) + +id_hmacWithSHA224 = _OID(digestAlgorithm, 8) + +id_hmacWithSHA256 = _OID(digestAlgorithm, 9) + +id_hmacWithSHA384 = _OID(digestAlgorithm, 10) + +id_hmacWithSHA512 = _OID(digestAlgorithm, 11) + +id_hmacWithSHA512_224 = _OID(digestAlgorithm, 12) + +id_hmacWithSHA512_256 = _OID(digestAlgorithm, 13) + + +# PBES1 object identifiers + +pbeWithMD2AndDES_CBC = _OID(pkcs_5, 1) + +pbeWithMD2AndRC2_CBC = _OID(pkcs_5, 4) + +pbeWithMD5AndDES_CBC = _OID(pkcs_5, 3) + +pbeWithMD5AndRC2_CBC = _OID(pkcs_5, 6) + +pbeWithSHA1AndDES_CBC = _OID(pkcs_5, 10) + +pbeWithSHA1AndRC2_CBC = _OID(pkcs_5, 11) + + +# Supporting techniques object identifiers + +desCBC = _OID(oiw, 3, 2, 7) + +des_EDE3_CBC = _OID(encryptionAlgorithm, 7) + +rc2CBC = _OID(encryptionAlgorithm, 2) + +rc5_CBC_PAD = _OID(encryptionAlgorithm, 9) + +aes128_CBC_PAD = _OID(aes, 2) + +aes192_CBC_PAD = _OID(aes, 22) + +aes256_CBC_PAD = _OID(aes, 42) + + +# PBES1 + +class PBEParameter(univ.Sequence): + pass + +PBEParameter.componentType = namedtype.NamedTypes( + namedtype.NamedType('salt', univ.OctetString().subtype( + subtypeSpec=constraint.ValueSizeConstraint(8, 8))), + namedtype.NamedType('iterationCount', univ.Integer()) +) + + +# PBES2 + +id_PBES2 = _OID(pkcs_5, 13) + + +class PBES2_params(univ.Sequence): + pass + +PBES2_params.componentType = namedtype.NamedTypes( + namedtype.NamedType('keyDerivationFunc', AlgorithmIdentifier()), + namedtype.NamedType('encryptionScheme', AlgorithmIdentifier()) +) + + +# PBMAC1 + +id_PBMAC1 = _OID(pkcs_5, 14) + + +class PBMAC1_params(univ.Sequence): + pass + +PBMAC1_params.componentType = namedtype.NamedTypes( + namedtype.NamedType('keyDerivationFunc', AlgorithmIdentifier()), + namedtype.NamedType('messageAuthScheme', AlgorithmIdentifier()) +) + + +# PBKDF2 + +id_PBKDF2 = _OID(pkcs_5, 12) + + +algid_hmacWithSHA1 = AlgorithmIdentifier() +algid_hmacWithSHA1['algorithm'] = id_hmacWithSHA1 +algid_hmacWithSHA1['parameters'] = univ.Null("") + + +class PBKDF2_params(univ.Sequence): + pass + +PBKDF2_params.componentType = namedtype.NamedTypes( + namedtype.NamedType('salt', univ.Choice(componentType=namedtype.NamedTypes( + namedtype.NamedType('specified', univ.OctetString()), + namedtype.NamedType('otherSource', AlgorithmIdentifier()) + ))), + namedtype.NamedType('iterationCount', univ.Integer().subtype( + subtypeSpec=constraint.ValueRangeConstraint(1, MAX))), + namedtype.OptionalNamedType('keyLength', univ.Integer().subtype( + subtypeSpec=constraint.ValueRangeConstraint(1, MAX))), + namedtype.DefaultedNamedType('prf', algid_hmacWithSHA1) +) + + +# RC2 CBC algorithm parameter + +class RC2_CBC_Parameter(univ.Sequence): + pass + +RC2_CBC_Parameter.componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('rc2ParameterVersion', univ.Integer()), + namedtype.NamedType('iv', univ.OctetString().subtype( + subtypeSpec=constraint.ValueSizeConstraint(8, 8))) +) + + +# RC5 CBC algorithm parameter + +class RC5_CBC_Parameters(univ.Sequence): + pass + +RC5_CBC_Parameters.componentType = namedtype.NamedTypes( + namedtype.NamedType('version', + univ.Integer(namedValues=namedval.NamedValues(('v1_0', 16))).subtype( + subtypeSpec=constraint.SingleValueConstraint(16))), + namedtype.NamedType('rounds', + univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(8, 127))), + namedtype.NamedType('blockSizeInBits', + univ.Integer().subtype(subtypeSpec=constraint.SingleValueConstraint(64, 128))), + namedtype.OptionalNamedType('iv', univ.OctetString()) +) + + +# Initialization Vector for AES: OCTET STRING (SIZE(16)) + +class AES_IV(univ.OctetString): + pass + +AES_IV.subtypeSpec = constraint.ValueSizeConstraint(16, 16) + + +# Initialization Vector for DES: OCTET STRING (SIZE(8)) + +class DES_IV(univ.OctetString): + pass + +DES_IV.subtypeSpec = constraint.ValueSizeConstraint(8, 8) + + +# Update the Algorithm Identifier map + +_algorithmIdentifierMapUpdate = { + # PBKDF2-PRFs + id_hmacWithSHA1: univ.Null(), + id_hmacWithSHA224: univ.Null(), + id_hmacWithSHA256: univ.Null(), + id_hmacWithSHA384: univ.Null(), + id_hmacWithSHA512: univ.Null(), + id_hmacWithSHA512_224: univ.Null(), + id_hmacWithSHA512_256: univ.Null(), + # PBES1Algorithms + pbeWithMD2AndDES_CBC: PBEParameter(), + pbeWithMD2AndRC2_CBC: PBEParameter(), + pbeWithMD5AndDES_CBC: PBEParameter(), + pbeWithMD5AndRC2_CBC: PBEParameter(), + pbeWithSHA1AndDES_CBC: PBEParameter(), + pbeWithSHA1AndRC2_CBC: PBEParameter(), + # PBES2Algorithms + id_PBES2: PBES2_params(), + # PBES2-KDFs + id_PBKDF2: PBKDF2_params(), + # PBMAC1Algorithms + id_PBMAC1: PBMAC1_params(), + # SupportingAlgorithms + desCBC: DES_IV(), + des_EDE3_CBC: DES_IV(), + rc2CBC: RC2_CBC_Parameter(), + rc5_CBC_PAD: RC5_CBC_Parameters(), + aes128_CBC_PAD: AES_IV(), + aes192_CBC_PAD: AES_IV(), + aes256_CBC_PAD: AES_IV(), +} + +rfc5280.algorithmIdentifierMap.update(_algorithmIdentifierMapUpdate) diff --git a/tests/__main__.py b/tests/__main__.py index 08300c2..93ae011 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -19,12 +19,14 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc2511.suite', 'tests.test_rfc2560.suite', 'tests.test_rfc2634.suite', + 'tests.test_rfc2985.suite', 'tests.test_rfc2986.suite', 'tests.test_rfc3161.suite', 'tests.test_rfc3274.suite', 'tests.test_rfc3560.suite', 'tests.test_rfc3565.suite', 'tests.test_rfc3709.suite', + 'tests.test_rfc3770.suite', 'tests.test_rfc3779.suite', 'tests.test_rfc4055.suite', 'tests.test_rfc4073.suite', @@ -38,12 +40,19 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc5480.suite', 'tests.test_rfc5649.suite', 'tests.test_rfc5652.suite', + 'tests.test_rfc5914.suite', 'tests.test_rfc5915.suite', 'tests.test_rfc5940.suite', 'tests.test_rfc5958.suite', + 'tests.test_rfc6010.suite', 'tests.test_rfc6019.suite', + 'tests.test_rfc6031.suite', + 'tests.test_rfc6032.suite', + 'tests.test_rfc7030.suite', 'tests.test_rfc7191.suite', + 'tests.test_rfc7292.suite', 'tests.test_rfc7296.suite', + 'tests.test_rfc8018.suite', 'tests.test_rfc8103.suite', 'tests.test_rfc8226.suite', 'tests.test_rfc8410.suite', diff --git a/tests/test_rfc2985.py b/tests/test_rfc2985.py new file mode 100644 index 0000000..31110a8 --- /dev/null +++ b/tests/test_rfc2985.py @@ -0,0 +1,293 @@ +# +# 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 rfc2985 +from pyasn1_modules import rfc5280 +from pyasn1_modules import rfc5652 +from pyasn1_modules import rfc7292 + + +try: + import unittest2 as unittest + +except ImportError: + import unittest + + +class PKCS9AttrsTestCase(unittest.TestCase): + pem_text = """\ +MYIQjzAOBgNVBEExBwwFQWxpY2UwDwYIKwYBBQUHCQMxAxMBTTAQBgNVBAUxCRMH +QjQ4LTAwNzAQBggrBgEFBQcJBDEEEwJVUzAQBggrBgEFBQcJBTEEEwJVUzARBgoq +hkiG9w0BCRkEMQMCATAwFAYJKoZIhvcNAQkCMQcWBUFsaWNlMBgGCiqGSIb3DQEJ +GQMxCgQIUTeqnHYky4AwHAYJKoZIhvcNAQkPMQ8wDTALBglghkgBZQMEAS0wHQYI +KwYBBQUHCQExERgPMjAxOTA4MDMxMjAwMDBaMB0GCCsGAQUFBwkCMREMD0hlcm5k +b24sIFZBLCBVUzApBgkqhkiG9w0BCRQxHB4aAEYAcgBpAGUAbgBkAGwAeQAgAE4A +YQBtAGUwLwYJKoZIhvcNAQkIMSITIDEyMyBVbmtub3duIFdheSwgTm93aGVyZSwg +VkEsIFVTMIGZBgoqhkiG9w0BCRkCMYGKMIGHMAsGCWCGSAFlAwQBLQR4VsJb7t4l +IqjJCT54rqkbCJsBPE17YQJeEYvyA4M1aDIUU5GnCgEhctgMiDPWGMvaSziixdIg +aU/0zvWvYCm8UwPvBBwMtm9X5NDvk9p4nXbGAT8E/OsV1SYWVvwRJwYak0yWWexM +HSixw1Ljh2nb0fIbqwLOeMmIMIIEsQYKKoZIhvcNAQkZBTGCBKEwggSdBgkqhkiG +9w0BBwKgggSOMIIEigIBATENMAsGCWCGSAFlAwQCAjBRBgkqhkiG9w0BBwGgRARC +Q29udGVudC1UeXBlOiB0ZXh0L3BsYWluDQoNCldhdHNvbiwgY29tZSBoZXJlIC0g +SSB3YW50IHRvIHNlZSB5b3UuoIICfDCCAngwggH+oAMCAQICCQCls1QoG7BuOzAK +BggqhkjOPQQDAzA/MQswCQYDVQQGEwJVUzELMAkGA1UECAwCVkExEDAOBgNVBAcM +B0hlcm5kb24xETAPBgNVBAoMCEJvZ3VzIENBMB4XDTE5MDUyOTE0NDU0MVoXDTIw +MDUyODE0NDU0MVowcDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlZBMRAwDgYDVQQH +EwdIZXJuZG9uMRAwDgYDVQQKEwdFeGFtcGxlMQ4wDAYDVQQDEwVBbGljZTEgMB4G +CSqGSIb3DQEJARYRYWxpY2VAZXhhbXBsZS5jb20wdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAT4zZ8HL+xEDpXWkoWp5xFMTz4u4Ae1nF6zXCYlmsEGD5vPu5hl9hDEjd1U +HRgJIPoy3fJcWWeZ8FHCirICtuMgFisNscG/aTwKyDYOFDuqz/C2jyEwqgWCRyxy +ohuJXtmjgZQwgZEwCwYDVR0PBAQDAgeAMEIGCWCGSAGG+EIBDQQ1FjNUaGlzIGNl +cnRpZmljYXRlIGNhbm5vdCBiZSB0cnVzdGVkIGZvciBhbnkgcHVycG9zZS4wHQYD +VR0OBBYEFMS6Wg4+euM8gbD0Aqpouxbglg41MB8GA1UdIwQYMBaAFPI12zQE2qVV +8r1pA5mwYuziFQjBMAoGCCqGSM49BAMDA2gAMGUCMGO5H9E1uAveRGGaf48lN4po +v2yH+hCAc5hOAuZKe/f40MKSF8q4w2ij+0euSaKFiAIxAL3gxp6sMitCmLQgOH6/ +RBIC/2syJ97y0KVp9da0PDAvwxLugCHTKZPjjpSLPHHc9TGCAaEwggGdAgEBMEww +PzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAlZBMRAwDgYDVQQHDAdIZXJuZG9uMREw +DwYDVQQKDAhCb2d1cyBDQQIJAKWzVCgbsG47MAsGCWCGSAFlAwQCAqCByDAYBgkq +hkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0xOTA1MjkxODIz +MTlaMD8GCSqGSIb3DQEJBDEyBDC25CKk/YJnHtT3qsZtRPTosLmNUVhxxlbn8Jo2 ++lys4+IKEOba8jebiTfTTPmZJmwwTQYLKoZIhvcNAQkQAgExPjA8BCDHTyEPZCdX +CPUOh5EQs211nQ999bgFAi9zDBVz+ChTo4ABATAVMBOBEWFsaWNlQGV4YW1wbGUu +Y29tMAoGCCqGSM49BAMDBGYwZAIwOLV5WCbYjy5HLHE69IqXQQHVDJQzmo18WwkF +rEYH3EMsvpXEIGqsFTFN6NV4VBe9AjA5fGOCP5IhI32YqmGfs+zDlqZyb2xSX6Gr +/IfCIm0angfOI39g7lAZDyivjh5H/oQwggnoBgtghkgBhvhCAwGBWDGCCdcwggnT +AgEDMIIJjwYJKoZIhvcNAQcBoIIJgASCCXwwggl4MIIGCAYJKoZIhvcNAQcBoIIF ++QSCBfUwggXxMIIF7QYLKoZIhvcNAQwKAQKgggT+MIIE+jAcBgoqhkiG9w0BDAED +MA4ECO6rT/7SnK61AgIH0ASCBNhl7+ZgGmaQO8qy97gTAhXCjVM2/iV3LHWodlbY +iHqpAJj42/Uye/3B7TNROXine1DMI9ZeetIDzYiA52i0sh7PhjBeuCIqFwiRJIv7 +bIKYCgz6qSOIAgqr6XdQnpeFp97YqDgST/RGQel7obCNO115+SlelmBxwwSik60p +AwslawMzunvvH9qafrIiTa2myQqpRj/ifxjESJNZxG1O2FiplAi36r3icotim3Sj +zzRJU5+90SqnkogjtxODrQYkv6fqg3qGY/RuwAy+eT3V/z+UUoyL22w1T8qdSFsN +WmMnAFCSGBuoHHoZ22ipItKVg09UzTCWe3CbUmEfjJuJDmw3Oo7sWVYLltxjCS86 +XHWAauyFjmMr9aNsDiloGnFKSChslF6Ktj0F6ohOe+iReW5vi16EeEzbQiTjakpr +eQZoeajC/N+XGoT6jKxbk5r1dtnEEJ+Q4wnvSjiGpr6frr4T+4pw301sptOjfO3f +F23rKk7Advvi3k5xZobHcRmzDSfT9X5agtKlc4HCnHTz7XKHstXb1o1DSgTNVWQX +phhFBm10gx6zfEHaLqyMtqXbWe2TuIHMwnBWiLnbhIBn+hbxK4MCfVz3cBZbApks +Au/lXcVnakOJBcCtx/MMfZ3kcnI3Hs6W8rM2ASeDBLIQLVduOc6xlVSoYUQ24NNr +9usfigQkcSTJZPIO52vPyIIQ7zR7U8TiqonkKWU3QJJVarPgLEYMUhBfNHqiGfx/ +d1Hf4MBoti8CMFUwsmOTv6d+cHYvQelqeFMXP0DE88gN/mkFBDAzXiXzAqMQcjJ+ +pyW6l4o2iQFSvXKSKg/IKved/hGp7RngQohjg4KlbqeGuRYea8Xs4pH5ue5KTeOc +HGNI3Qi/Lmr2rd+e1iuGxwwYZHve6Z+Lxnb20zW9I/2MFm+KsCiB4Z/+x84jR7BG +8l//lpuc2D/vxnKTxaaUAdUXM0Zwze7e+Gc2lMhVG5TJWR1KY51vN5J+apDYc8IR +0L0c2bbkom3WkPq/po/dPDuoaX61nKmztUHaL5r5QZzBBwKVyhdw9J0btnWAFPNK +vzgy5U9iV4+6jXH5TCmlIreszwRPoqqEaYRIfmUpp2+zy91PpzjTs98tx/HIAbOM +fT3WmuTahEnEHehABhwq+S4xwzoVIskLbrcOP6l7UYYR7GTUCjKxh7ru0rSwHrqG +9t33YdzJaFbz+8jb88xtf454Rvur66Cew/4GYX9u1Zef0DF9So1ay3IicpOf5emo +VWIwg4bh7bELi78i/MbdWtNZQcXimykfeTsYH8Q4u+1uxHS5pwEWWwKiUnLQVpZP +2ut255TdgSIhEILwsaLVelRrx/lp14EpY355FOusXiju6g14aWfBnt5udvuTXxDQ +ZHPPNNk+gwzgvvTey98T941hYUctjg0NApJiB66bfrlYB9mkc5ftg5zqhEasYH5C +4ajKKRNMM7zGlwSZvy8PPhnAeE3Q9LTnos0l4ygjQD/kMlvd7XSLW3GUzjyxtkG4 +gQh6LGvnafAbgu7GpcapKEppN86sXEePHiQjj92n103+TxMYWwtaO4iAwkjqdEdt +avEHcXRcpdqC0st6nUwPAPAC4LKJbZgLQnNG+wlWIiCMMD56IdfQ7r/zGIr13MxC +kjNNUdISoWWE5GnQMYHbMBMGCSqGSIb3DQEJFTEGBAQBAAAAMFcGCSqGSIb3DQEJ +FDFKHkgAMwBmADcAMQBhAGYANgA1AC0AMQA2ADgANwAtADQANAA0AGEALQA5AGYA +NAA2AC0AYwA4AGIAZQAxADkANABjADMAZQA4AGUwawYJKwYBBAGCNxEBMV4eXABN +AGkAYwByAG8AcwBvAGYAdAAgAEUAbgBoAGEAbgBjAGUAZAAgAEMAcgB5AHAAdABv +AGcAcgBhAHAAaABpAGMAIABQAHIAbwB2AGkAZABlAHIAIAB2ADEALgAwMIIDaAYJ +KoZIhvcNAQcBoIIDWQSCA1UwggNRMIIDTQYLKoZIhvcNAQwKAQOgggMlMIIDIQYK +KoZIhvcNAQkWAaCCAxEEggMNMIIDCTCCAfGgAwIBAgIQNu32hzqhCKdHATXzboyI +ETANBgkqhkiG9w0BAQUFADAUMRIwEAYDVQQDEwlhbm9ueW1vdXMwIBcNMTYwNzE5 +MjIwMDAxWhgPMjExNjA2MjUyMjAwMDFaMBQxEjAQBgNVBAMTCWFub255bW91czCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALy2sEJMGNdcDg6BI7mdFM5T +lPzo5sKBzvUnagK5SKBJ11xMPN5toPTBzICB/XTWEB3AwpD0O+srSca+bsUAyedS +5V4BNp8qCyEu5RNRR8qPHheJ/guhLT96/gGI4jlrUyUhFntPkLKODxu+7KanMy6K +dD+PVE8shXRUZTYe4PG64/c7z3wapnf4XoCXkJRzCY5f3MKz3Ul039kVnTlJcikd +C7I9I9RflXLwXVl4nxUbeeRt6Z8WVWS4pCq+14v2aVPvP3mtVmAYHedRkvS04Hrx +4xx98D3NSSw6Z5OLkzqOcFw15fYmH2NLdhh34gSWJmaaCBAbuQ+1rx/42p7MvvsC +AwEAAaNVMFMwFQYDVR0lBA4wDAYKKwYBBAGCNwoDBDAvBgNVHREEKDAmoCQGCisG +AQQBgjcUAgOgFgwUYW5vbnltb3VzQHdpbmRvd3MteAAwCQYDVR0TBAIwADANBgkq +hkiG9w0BAQUFAAOCAQEAuH7iqY0/MLozwFb39ILYAJDHE+HToZBQbHQP4YtienrU +Stk60rIp0WH65lam7m/JhgAcItc/tV1L8mEnLrvvKcA+NeIL8sDOtM28azvgcOi0 +P3roeLLLRCuiykUaKmUcZEDm9cDYKIpJf7QetWQ3uuGTk9iRzpH79x2ix35BnyWQ +Rr3INZzmX/+9YRvPBXKYl/89F/w1ORYArpI9XtjfuPWaGQmM4f1WRHE2t3qRyKFF +ri7QiZdpcSx5zvsRHSyjfUMoKs+b6upk+P01lIhg/ewwYngGab+fZhF15pTNN2hx +8PdNGcrGzrkNKCmJKrWCa2xczuMA+z8SCuC1tYTKmDEVMBMGCSqGSIb3DQEJFTEG +BAQBAAAAMDswHzAHBgUrDgMCGgQUpWCP/fZR0TK5BwGuqvTd0+duiKcEFJTubF2k +HktMK+isIjxOTk4yJTOOAgIH0A== +""" + + def setUp(self): + self.asn1Spec = rfc2985.AttributeSet() + + 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 + + rfc5280.certificateAttributesMap.update(rfc2985.certificateAttributesMapUpdate) + rfc5280.certificateAttributesMap.update(rfc2985.cmsAttributesMapUpdate) + + for attr in asn1Object: + assert attr['type'] in rfc5280.certificateAttributesMap + av, rest = der_decode(attr['values'][0], + asn1Spec=rfc5280.certificateAttributesMap[attr['type']]) + assert not rest + assert av.prettyPrint() + assert der_encode(av) == attr['values'][0] + + if attr['type'] == rfc2985.pkcs_9_at_userPKCS12: + assert av['version'] == univ.Integer(3) + assert av['authSafe']['contentType'] == rfc5652.id_data + outdata, rest = der_decode(av['authSafe']['content'], + asn1Spec=univ.OctetString()) + assert not rest + authsafe, rest = der_decode(outdata, + asn1Spec=rfc7292.AuthenticatedSafe()) + assert not rest + + for ci in authsafe: + assert ci['contentType'] == rfc5652.id_data + indata, rest = der_decode(ci['content'], + asn1Spec=univ.OctetString()) + assert not rest + sc, rest = der_decode(indata, + asn1Spec=rfc7292.SafeContents()) + assert not rest + + for sb in sc: + if sb['bagId'] in rfc7292.pkcs12BagTypeMap: + bv, rest = der_decode(sb['bagValue'], + asn1Spec=rfc7292.pkcs12BagTypeMap[sb['bagId']]) + assert not rest + + for bagattr in sb['bagAttributes']: + if bagattr['attrType'] in rfc5280.certificateAttributesMap: + inav, rest = der_decode(bagattr['attrValues'][0], + asn1Spec=rfc5280.certificateAttributesMap[bagattr['attrType']]) + assert not rest + + if bagattr['attrType'] == rfc2985.pkcs_9_at_friendlyName: + assert inav == "3f71af65-1687-444a-9f46-c8be194c3e8e" + + if bagattr['attrType'] == rfc2985.pkcs_9_at_localKeyId: + assert inav == univ.OctetString(hexValue='01000000') + + if attr['type'] == rfc2985.pkcs_9_at_pkcs7PDU: + ci, rest = der_decode(attr['values'][0], + asn1Spec=rfc5652.ContentInfo()) + assert not rest + assert ci['contentType'] == rfc5652.id_signedData + + sd, rest = der_decode(ci['content'], + asn1Spec=rfc5652.SignedData()) + assert not rest + assert sd['version'] == 1 + + for si in sd['signerInfos']: + assert si['version'] == 1 + + for siattr in si['signedAttrs']: + if siattr['attrType'] in rfc5280.certificateAttributesMap: + siav, rest = der_decode(siattr['attrValues'][0], + asn1Spec=rfc5280.certificateAttributesMap[siattr['attrType']]) + assert not rest + + if siattr['attrType'] == rfc2985.pkcs_9_at_contentType: + assert siav == rfc5652.id_data + + if siattr['attrType'] == rfc2985.pkcs_9_at_messageDigest: + assert siav.prettyPrint()[2:10] == 'b6e422a4' + + if siattr['attrType'] == rfc2985.pkcs_9_at_signingTime: + assert siav['utcTime'] == '190529182319Z' + + for choices in sd['certificates']: + for rdn in choices[0]['tbsCertificate']['subject']['rdnSequence']: + if rdn[0]['type'] in rfc5280.certificateAttributesMap: + nv, rest = der_decode(rdn[0]['value'], + asn1Spec=rfc5280.certificateAttributesMap[rdn[0]['type']]) + assert not rest + + if rdn[0]['type'] == rfc2985.pkcs_9_at_emailAddress: + assert nv == 'alice@example.com' + + def testOpenTypes(self): + rfc5280.certificateAttributesMap.update(rfc2985.certificateAttributesMapUpdate) + rfc5280.certificateAttributesMap.update(rfc2985.cmsAttributesMapUpdate) + + 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 + + for attr in asn1Object: + assert attr['type'] in rfc5280.certificateAttributesMap + + if attr['type'] == rfc2985.pkcs_9_at_userPKCS12: + assert attr['values'][0]['version'] == univ.Integer(3) + assert attr['values'][0]['authSafe']['contentType'] == rfc5652.id_data + authsafe, rest = der_decode(attr['values'][0]['authSafe']['content'], + asn1Spec=rfc7292.AuthenticatedSafe()) + assert not rest + + for ci in authsafe: + assert ci['contentType'] == rfc5652.id_data + indata, rest = der_decode(ci['content'], + asn1Spec=univ.OctetString()) + assert not rest + + sc, rest = der_decode(indata, + asn1Spec=rfc7292.SafeContents(), + decodeOpenTypes=True) + assert not rest + + for sb in sc: + if sb['bagId'] in rfc7292.pkcs12BagTypeMap: + for bagattr in sb['bagAttributes']: + if bagattr['attrType'] in rfc5280.certificateAttributesMap: + + if bagattr['attrType'] == rfc2985.pkcs_9_at_friendlyName: + assert bagattr['attrValues'][0] == "3f71af65-1687-444a-9f46-c8be194c3e8e" + + if bagattr['attrType'] == rfc2985.pkcs_9_at_localKeyId: + assert bagattr['attrValues'][0] == univ.OctetString(hexValue='01000000') + + if attr['type'] == rfc2985.pkcs_9_at_pkcs7PDU: + assert attr['values'][0]['contentType'] == rfc5652.id_signedData + assert attr['values'][0]['content']['version'] == 1 + + for si in attr['values'][0]['content']['signerInfos']: + assert si['version'] == 1 + + for siattr in si['signedAttrs']: + if siattr['attrType'] in rfc5280.certificateAttributesMap: + + if siattr['attrType'] == rfc2985.pkcs_9_at_contentType: + assert siattr['attrValues'][0] == rfc5652.id_data + + if siattr['attrType'] == rfc2985.pkcs_9_at_messageDigest: + assert siattr['attrValues'][0].prettyPrint()[2:10] == 'b6e422a4' + + if siattr['attrType'] == rfc2985.pkcs_9_at_signingTime: + assert siattr['attrValues'][0]['utcTime'] == '190529182319Z' + + for choices in attr['values'][0]['content']['certificates']: + for rdn in choices[0]['tbsCertificate']['subject']['rdnSequence']: + if rdn[0]['type'] in rfc5280.certificateAttributesMap: + + if rdn[0]['type'] == rfc2985.pkcs_9_at_emailAddress: + assert rdn[0]['value'] == 'alice@example.com' + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/tests/test_rfc3770.py b/tests/test_rfc3770.py new file mode 100644 index 0000000..e8093f0 --- /dev/null +++ b/tests/test_rfc3770.py @@ -0,0 +1,90 @@ +# +# 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.compat.octets import str2octs + +from pyasn1_modules import pem +from pyasn1_modules import rfc5480 +from pyasn1_modules import rfc5280 +from pyasn1_modules import rfc3770 + +try: + import unittest2 as unittest +except ImportError: + import unittest + +class CertificateTestCase(unittest.TestCase): + cert_pem_text = """\ +MIICqzCCAjCgAwIBAgIJAKWzVCgbsG4/MAoGCCqGSM49BAMDMD8xCzAJBgNVBAYT +AlVTMQswCQYDVQQIDAJWQTEQMA4GA1UEBwwHSGVybmRvbjERMA8GA1UECgwIQm9n +dXMgQ0EwHhcNMTkwNzE5MTk0MjQ3WhcNMjAwNzE4MTk0MjQ3WjBjMQswCQYDVQQG +EwJVUzELMAkGA1UECBMCVkExEDAOBgNVBAcTB0hlcm5kb24xGzAZBgNVBAoTElZp +Z2lsIFNlY3VyaXR5IExMQzEYMBYGA1UEAxMPZWFwLmV4YW1wbGUuY29tMHYwEAYH +KoZIzj0CAQYFK4EEACIDYgAEMMbnIp2BUbuyMgH9HhNHrh7VBy7ql2lBjGRSsefR +Wa7+vCWs4uviW6On4eem5YoP9/UdO7DaIL+/J9/3DJHERI17oFxn+YWiE4JwXofy +QwfSu3cncVNMqpiDjEkUGGvBo4HTMIHQMAsGA1UdDwQEAwIHgDBCBglghkgBhvhC +AQ0ENRYzVGhpcyBjZXJ0aWZpY2F0ZSBjYW5ub3QgYmUgdHJ1c3RlZCBmb3IgYW55 +IHB1cnBvc2UuMB0GA1UdDgQWBBSDjPGr7M742rsE4oQGwBvGvllZ+zAfBgNVHSME +GDAWgBTyNds0BNqlVfK9aQOZsGLs4hUIwTAeBggrBgEFBQcBDQQSMBAEB0V4YW1w +bGUEBUJvZ3VzMB0GA1UdJQQWMBQGCCsGAQUFBwMOBggrBgEFBQcDDTAKBggqhkjO +PQQDAwNpADBmAjEAmCPZnnlUQOKlcOIIOgFrRCkOqO0ESs+dobYwAc2rFCBtQyP7 +C3N00xkX8WZZpiAZAjEAi1Z5+nGbJg5eJTc8fwudutN/HNwJEIS6mHds9kfcy26x +DAlVlhox680Jxy5J8Pkx +""" + + def setUp(self): + self.asn1Spec = rfc5280.Certificate() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.cert_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + def testOpenTypes(self): + substrate = pem.readBase64fromText(self.cert_pem_text) + rfc5280.algorithmIdentifierMap.update(rfc5480.algorithmIdentifierMapUpdate) + asn1Object, rest = der_decode(substrate, + asn1Spec=self.asn1Spec, + decodeOpenTypes=True) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + sig_alg = asn1Object['tbsCertificate']['signature'] + assert sig_alg['algorithm'] == rfc5480.ecdsa_with_SHA384 + assert not sig_alg['parameters'].hasValue() + + spki_alg = asn1Object['tbsCertificate']['subjectPublicKeyInfo']['algorithm'] + assert spki_alg['algorithm'] == rfc5480.id_ecPublicKey + assert spki_alg['parameters']['namedCurve'] == rfc5480.secp384r1 + + for extn in asn1Object['tbsCertificate']['extensions']: + if extn['extnID'] in rfc5280.certificateExtensionsMap.keys(): + extnValue, rest = der_decode(extn['extnValue'], + asn1Spec=rfc5280.certificateExtensionsMap[extn['extnID']]) + assert der_encode(extnValue) == extn['extnValue'] + + if extn['extnID'] == rfc3770.id_pe_wlanSSID: + assert str2octs('Example') in extnValue + + if extn['extnID'] == rfc5280.id_ce_extKeyUsage: + assert rfc3770.id_kp_eapOverLAN in extnValue + assert rfc3770.id_kp_eapOverPPP in extnValue + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/tests/test_rfc4073.py b/tests/test_rfc4073.py index 34cebea..5a3b7f8 100644 --- a/tests/test_rfc4073.py +++ b/tests/test_rfc4073.py @@ -153,4 +153,4 @@ if __name__ == '__main__': import sys result = unittest.TextTestRunner(verbosity=2).run(suite) - sys.exit(not result.wasSuccessful()) + sys.exit(not result.wasSuccessful()) \ No newline at end of file diff --git a/tests/test_rfc5914.py b/tests/test_rfc5914.py new file mode 100644 index 0000000..3fc5df9 --- /dev/null +++ b/tests/test_rfc5914.py @@ -0,0 +1,83 @@ +# +# 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 rfc5914 +from pyasn1_modules import rfc5652 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class TrustAnchorListTestCase(unittest.TestCase): + trust_anchor_list_pem_text = """\ +MIIGGQYLKoZIhvcNAQkQASKgggYIMIIGBKGCAvYwggLyoAMCAQICAgDJMA0GCSqG +SIb3DQEBCwUAMBYxFDASBgNVBAMTC3JpcGUtbmNjLXRhMCAXDTE3MTEyODE0Mzk1 +NVoYDzIxMTcxMTI4MTQzOTU1WjAWMRQwEgYDVQQDEwtyaXBlLW5jYy10YTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANFEWEhqlM9psgbDs3ltY0OjbMTb +5SzMoVpJ755fDYgQrP0/0tl7jSkDWfsAWcSIDz1dqRQRXkAL6B/1ivNx8ANuldrI +sJvzGNpymfjpcPsJac5WdadyKY9njXCq5orfAcAQvMSJs7ghmldI5EQdBmdIaB+j +JdN7pi6a0bJ+r9MTj9PpekHNWRzBVRW9/OSEOxUEE3FSMa3XjLKMiavXjJBOg6HJ +R4RfzZUpZV7mwEkPSlFqidPjrd0Al6+C1xAjH5KZFUdk2U/r+b+ufGx1bOmcUQ9W ++lJNbkCgMh1G5/7V7z/Ja4wImxs1bFw09i9MeBHcfkHYsT4Do4t4ATMi9lcCAwEA +AaOCAV4wggFaMB0GA1UdDgQWBBToVSsf1tGk9+QExtjlaA0evBY/wzAPBgNVHRMB +Af8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjCBsQYIKwYBBQUHAQsEgaQwgaEwPAYI +KwYBBQUHMAqGMHJzeW5jOi8vcnBraS5yaXBlLm5ldC9yZXBvc2l0b3J5L3JpcGUt +bmNjLXRhLm1mdDAyBggrBgEFBQcwDYYmaHR0cHM6Ly9ycmRwLnJpcGUubmV0L25v +dGlmaWNhdGlvbi54bWwwLQYIKwYBBQUHMAWGIXJzeW5jOi8vcnBraS5yaXBlLm5l +dC9yZXBvc2l0b3J5LzAYBgNVHSABAf8EDjAMMAoGCCsGAQUFBw4CMCcGCCsGAQUF +BwEHAQH/BBgwFjAJBAIAATADAwEAMAkEAgACMAMDAQAwIQYIKwYBBQUHAQgBAf8E +EjAQoA4wDDAKAgEAAgUA/////zCCAgIwggGIoAMCAQICCQDokdYGkU/O8jAKBggq +hkjOPQQDAzA/MQswCQYDVQQGEwJVUzELMAkGA1UECAwCVkExEDAOBgNVBAcMB0hl +cm5kb24xETAPBgNVBAoMCEJvZ3VzIENBMB4XDTE5MDUxNDA4NTgxMVoXDTIxMDUx +MzA4NTgxMVowPzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAlZBMRAwDgYDVQQHDAdI +ZXJuZG9uMREwDwYDVQQKDAhCb2d1cyBDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IA +BPBRdlSx6I5qpZ2sKUMIxun1gUAzzstOYWKvKCnMoNT1x+pIKDvMEMimFcLAxxL3 +NVYOhK0Jty83SPDkKWMdx9/Okdhf3U/zxJlEnXDiFrAeM6xbG8zcCRiBnmd92Uvs +RqNQME4wHQYDVR0OBBYEFPI12zQE2qVV8r1pA5mwYuziFQjBMB8GA1UdIwQYMBaA +FPI12zQE2qVV8r1pA5mwYuziFQjBMAwGA1UdEwQFMAMBAf8wCgYIKoZIzj0EAwMD +aAAwZQIwWlGNjb9NyqJSzUSdsEqDSvMZb8yFkxYCIbAVqQ9UqScUUb9tpJKGsPWw +bZsnLVvmAjEAt/ypozbUhQw4dSPpWzrn5BQ0kKbDM3DQJcBABEUBoIOol1/jYQPm +xajQuxcheFlkooIBADCB/TB2MBAGByqGSM49AgEGBSuBBAAiA2IABOIIQup32CTe +oCxkpBPOQJwjcqkCCg43PyE2uI1TFPbVkZVL85YCjXEexNjLp59e76Dmf1qSEZZT +b+vAyz+u/Vs/RyTnmgculr6oL7tXGK9xpL14Oh7oWzxrZBErzDQrjAQUo53mH/na +OU/AbuiRy5Wl2jHiCp8MFURpZ2lDZXJ0IFRydXN0IEFuY2hvcjBSMEwxCzAJBgNV +BAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxJjAkBgNVBAMTHURpZ2lDZXJ0 +IEVDQyBTZWN1cmUgU2VydmVyIENBggIFIIICZW4= +""" + + def setUp(self): + self.asn1Spec = rfc5652.ContentInfo() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.trust_anchor_list_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + assert asn1Object['contentType'] == rfc5914.id_ct_trustAnchorList + tal, rest = der_decode(asn1Object['content'], rfc5914.TrustAnchorList()) + assert not rest + assert tal.prettyPrint() + assert der_encode(tal) == asn1Object['content'] + + assert sum (1 for _ in tal) == 3 + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/tests/test_rfc6010.py b/tests/test_rfc6010.py new file mode 100644 index 0000000..072c1ac --- /dev/null +++ b/tests/test_rfc6010.py @@ -0,0 +1,95 @@ +# +# 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 rfc6010 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class UnconstrainedCCCExtensionTestCase(unittest.TestCase): + unconstrained_pem_text = "MB0GCCsGAQUFBwESBBEwDzANBgsqhkiG9w0BCRABAA==" + + def setUp(self): + self.asn1Spec = rfc5280.Extension() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.unconstrained_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + assert asn1Object['extnID'] == rfc6010.id_pe_cmsContentConstraints + evalue, rest = der_decode(asn1Object['extnValue'], + asn1Spec=rfc6010.CMSContentConstraints()) + assert not rest + assert evalue.prettyPrint() + assert der_encode(evalue) == asn1Object['extnValue'] + + assert evalue[0]['contentType'] == rfc6010.id_ct_anyContentType + + +class ConstrainedCCCExtensionTestCase(unittest.TestCase): + constrained_pem_text = """\ +MIG7BggrBgEFBQcBEgSBrjCBqzA0BgsqhkiG9w0BCRABEDAlMCMGCyqGSIb3DQEJ +EAwBMRQMElZpZ2lsIFNlY3VyaXR5IExMQzAwBgpghkgBZQIBAk4CMCIwIAYLKoZI +hvcNAQkQDAsxEQwPa3RhLmV4YW1wbGUuY29tMDEGCyqGSIb3DQEJEAEZMCIwIAYL +KoZIhvcNAQkQDAsxEQwPa3RhLmV4YW1wbGUuY29tMA4GCSqGSIb3DQEHAQoBAQ== +""" + + def setUp(self): + self.asn1Spec = rfc5280.Extension() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.constrained_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + assert asn1Object['extnID'] == rfc6010.id_pe_cmsContentConstraints + evalue, rest = der_decode(asn1Object['extnValue'], + asn1Spec=rfc6010.CMSContentConstraints()) + assert not rest + assert evalue.prettyPrint() + assert der_encode(evalue) == asn1Object['extnValue'] + + constraint_count = 0 + attribute_count = 0 + cannot_count = 0 + for ccc in evalue: + constraint_count += 1 + if ccc['canSource'] == 1: + cannot_count += 1 + if ccc['attrConstraints'].hasValue(): + for attr in ccc['attrConstraints']: + attribute_count += 1 + assert constraint_count == 4 + assert attribute_count == 3 + assert cannot_count == 1 + + def testExtensionsMap(self): + substrate = pem.readBase64fromText(self.constrained_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert asn1Object['extnID'] in rfc5280.certificateExtensionsMap.keys() + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/tests/test_rfc6031.py b/tests/test_rfc6031.py new file mode 100644 index 0000000..176285f --- /dev/null +++ b/tests/test_rfc6031.py @@ -0,0 +1,89 @@ +# +# 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 rfc5652 +from pyasn1_modules import rfc6031 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class SymmetricKeyPkgTestCase(unittest.TestCase): + key_pkg_pem_text = """\ +MIG7BgsqhkiG9w0BCRABGaCBqzCBqKBEMCMGCyqGSIb3DQEJEAwBMRQMElZpZ2ls +IFNlY3VyaXR5IExMQzAdBgsqhkiG9w0BCRAMAzEODAxQcmV0ZW5kIDA0OEEwYDBe +MFYwGwYLKoZIhvcNAQkQDBsxDAwKZXhhbXBsZUlEMTAVBgsqhkiG9w0BCRAMCjEG +DARIT1RQMCAGCyqGSIb3DQEJEAwLMREMD2t0YS5leGFtcGxlLmNvbQQEMTIzNA== +""" + + def setUp(self): + self.asn1Spec = rfc5652.ContentInfo() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.key_pkg_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + assert asn1Object['contentType'] in rfc5652.cmsContentTypesMap + asn1Spec = rfc5652.cmsContentTypesMap[asn1Object['contentType']] + skp, rest = der_decode(asn1Object['content'], asn1Spec=asn1Spec) + assert not rest + assert skp.prettyPrint() + assert der_encode(skp) == asn1Object['content'] + + for attr in skp['sKeyPkgAttrs']: + assert attr['attrType'] in rfc6031.sKeyPkgAttributesMap.keys() + + for osk in skp['sKeys']: + for attr in osk['sKeyAttrs']: + assert attr['attrType'] in rfc6031.sKeyAttributesMap.keys() + + def testOpenTypes(self): + substrate = pem.readBase64fromText(self.key_pkg_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 + assert asn1Object['content'].hasValue() + keypkg = asn1Object['content'] + assert keypkg['version'] == rfc6031.KeyPkgVersion().subtype(value='v1') + + for attr in keypkg['sKeyPkgAttrs']: + assert attr['attrType'] in rfc6031.sKeyPkgAttributesMap.keys() + assert attr['attrValues'][0].prettyPrint()[:2] != '0x' + # decodeOpenTypes=True did not decode if the value is shown in hex ... + if attr['attrType'] == rfc6031.id_pskc_manufacturer: + attr['attrValues'][0] == 'Vigil Security LLC' + + for osk in keypkg['sKeys']: + for attr in osk['sKeyAttrs']: + assert attr['attrType'] in rfc6031.sKeyAttributesMap.keys() + assert attr['attrValues'][0].prettyPrint()[:2] != '0x' + # decodeOpenTypes=True did not decode if the value is shown in hex ... + if attr['attrType'] == rfc6031.id_pskc_issuer: + attr['attrValues'][0] == 'kta.example.com' + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/tests/test_rfc6032.py b/tests/test_rfc6032.py new file mode 100644 index 0000000..ad56823 --- /dev/null +++ b/tests/test_rfc6032.py @@ -0,0 +1,88 @@ +# +# 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.compat.octets import str2octs + +from pyasn1_modules import pem +from pyasn1_modules import rfc5652 +from pyasn1_modules import rfc6032 +from pyasn1_modules import rfc3565 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class EncryptedKeyPkgTestCase(unittest.TestCase): + encrypted_key_pkg_pem_text = """\ +MIIBBwYKYIZIAWUCAQJOAqCB+DCB9QIBAjCBzgYKYIZIAWUCAQJOAjAdBglghkgB +ZQMEASoEEN6HFteHMZ3DyeO35xIwWQOAgaCKTs0D0HguNzMhsLgiwG/Kw8OwX+GF +9/cZ1YVNesUTW/VsbXJcbTmFmWyfqZsM4DLBegIbrUEHQZnQRq6/NO4ricQdHApD +B/ip6RRqeN1yxMJLv1YN0zUOOIDBS2iMEjTLXZLWw3w22GN2JK7G+Lr4OH1NhMgU +ILJyh/RePmPseMwxvcJs7liEfkiSNMtDfEcpjtzA9bDe95GjhQRsiSByoR8wHQYJ +YIZIAWUCAQVCMRAEDnB0Zi1rZGMtODEyMzc0 +""" + + def setUp(self): + self.asn1Spec = rfc5652.ContentInfo() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.encrypted_key_pkg_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + assert asn1Object['contentType'] == rfc6032.id_ct_KP_encryptedKeyPkg + + content, rest = der_decode(asn1Object['content'], rfc6032.EncryptedKeyPackage()) + assert not rest + assert content.prettyPrint() + assert der_encode(content) == asn1Object['content'] + assert content.getName() == 'encrypted' + eci = content['encrypted']['encryptedContentInfo'] + assert eci['contentType'] == rfc6032.id_ct_KP_encryptedKeyPkg + attrType = content['encrypted']['unprotectedAttrs'][0]['attrType'] + assert attrType == rfc6032.id_aa_KP_contentDecryptKeyID + + attrVal0 = content['encrypted']['unprotectedAttrs'][0]['attrValues'][0] + keyid, rest = der_decode(attrVal0, rfc6032.ContentDecryptKeyID()) + assert not rest + assert keyid.prettyPrint() + assert der_encode(keyid) == attrVal0 + assert keyid == b'ptf-kdc-812374' + + def testOpenTypes(self): + substrate = pem.readBase64fromText(self.encrypted_key_pkg_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 + eci = asn1Object['content']['encrypted']['encryptedContentInfo'] + assert eci['contentType'] in rfc5652.cmsContentTypesMap + + for attr in asn1Object['content']['encrypted']['unprotectedAttrs']: + assert attr['attrType'] in rfc5652.cmsAttributesMap.keys() + assert attr['attrValues'][0].prettyPrint()[:2] != '0x' + if attr['attrType'] == rfc6032.id_aa_KP_contentDecryptKeyID: + assert attr['attrValues'][0] == str2octs('ptf-kdc-812374') + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/tests/test_rfc6402.py b/tests/test_rfc6402.py new file mode 100755 index 0000000..65d5ea9 --- /dev/null +++ b/tests/test_rfc6402.py @@ -0,0 +1,163 @@ +# +# 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 char +from pyasn1.type import namedtype +from pyasn1.type import univ + +from pyasn1_modules import pem +from pyasn1_modules import rfc5280 +from pyasn1_modules import rfc5652 +from pyasn1_modules import rfc6402 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class BackwardCompatibilityTestCase(unittest.TestCase): + pem_text = """\ +MIIEJQYJKoZIhvcNAQcCoIIEFjCCBBICAQMxCzAJBgUrDgMCGgUAMIIDAgYIKwYBBQUHDAKgggL0 +BIIC8DCCAuwweDB2AgECBgorBgEEAYI3CgoBMWUwYwIBADADAgEBMVkwVwYJKwYBBAGCNxUUMUow +SAIBBQwZcGl0dWNoYTEuZW1lYS5ocHFjb3JwLm5ldAwMRU1FQVxwaXR1Y2hhDBpDTUNSZXFHZW5l +cmF0b3IudnNob3N0LmV4ZTCCAmqgggJmAgEBMIICXzCCAcgCAQAwADCBnzANBgkqhkiG9w0BAQEF +AAOBjQAwgYkCgYEA0jm7SSSm2wyEAzuNKtFZFJKo91SrJq9wQwEhEKHDavZwMQOm1rZ2PF8NWCEb +PqrhToQ7rtiGLSZa4dF4bzgmBqQ9aoSfEX4jISt31Vy+skHidXjHHpbsjT24NPhrZgANivL7CxD6 +Ft+s7qS1gL4HRm2twQkqSwOLrE/q2QeXl2UCAwEAAaCCAR0wGgYKKwYBBAGCNw0CAzEMFgo2LjIu +OTIwMC4yMD4GCSqGSIb3DQEJDjExMC8wHQYDVR0OBBYEFMW2skn88gxhONWZQA4sWGBDb68yMA4G +A1UdDwEB/wQEAwIHgDBXBgkrBgEEAYI3FRQxSjBIAgEFDBlwaXR1Y2hhMS5lbWVhLmhwcWNvcnAu +bmV0DAxFTUVBXHBpdHVjaGEMGkNNQ1JlcUdlbmVyYXRvci52c2hvc3QuZXhlMGYGCisGAQQBgjcN +AgIxWDBWAgECHk4ATQBpAGMAcgBvAHMAbwBmAHQAIABTAHQAcgBvAG4AZwAgAEMAcgB5AHAAdABv +AGcAcgBhAHAAaABpAGMAIABQAHIAbwB2AGkAZABlAHIDAQAwDQYJKoZIhvcNAQEFBQADgYEAJZlu +mxjtCxSOQi27jsVdd3y8NSIlzNv0b3LqmzvAly6L+CstXcnuG2MPQqPH9R7tbJonGUniBQO9sQ7C +KhYWj2gfhiEkSID82lV5chINVUFKoUlSiEhWr0tPGgvOaqdsKQcrHfzrsBbFkhDqrFSVy7Yivbnh +qYszKrOjJKiiCPMwADAAMYH5MIH2AgEDgBTFtrJJ/PIMYTjVmUAOLFhgQ2+vMjAJBgUrDgMCGgUA +oD4wFwYJKoZIhvcNAQkDMQoGCCsGAQUFBwwCMCMGCSqGSIb3DQEJBDEWBBTFTkK/OifaFjwqHiJu +xM7qXcg/VzANBgkqhkiG9w0BAQEFAASBgKfC6jOi1Wgy4xxDCQVK9+e5tktL8wE/j2cb9JSqq+aU +5UxEgXEw7q7BoYZCAzcxMRriGzakXr8aXHcgkRJ7XcFvLPUjpmGg9SOZ2sGW4zQdWAwImN/i8loc +xicQmJP+VoMHo/ZpjFY9fYCjNZUArgKsEwK/s+p9yrVVeB1Nf8Mn +""" + + def testDerCodec(self): + + rfc5652.cmsContentTypesMap.update(rfc6402.cmsContentTypesMapUpdate) + layers = rfc5652.cmsContentTypesMap + + getNextLayer = { + rfc5652.id_ct_contentInfo: lambda x: x['contentType'], + rfc5652.id_signedData: lambda x: x['encapContentInfo']['eContentType'], + rfc6402.id_cct_PKIData: lambda x: None + } + + getNextSubstrate = { + rfc5652.id_ct_contentInfo: lambda x: x['content'], + rfc5652.id_signedData: lambda x: x['encapContentInfo']['eContent'], + rfc6402.id_cct_PKIData: lambda x: None + } + + substrate = pem.readBase64fromText(self.pem_text) + + next_layer = rfc5652.id_ct_contentInfo + while next_layer: + asn1Object, rest = der_decode(substrate, asn1Spec=layers[next_layer]) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + substrate = getNextSubstrate[next_layer](asn1Object) + next_layer = getNextLayer[next_layer](asn1Object) + + + def testOpenTypes(self): + class ClientInformation(univ.Sequence): + pass + + ClientInformation.componentType = namedtype.NamedTypes( + namedtype.NamedType('clientId', univ.Integer()), + namedtype.NamedType('MachineName', char.UTF8String()), + namedtype.NamedType('UserName', char.UTF8String()), + namedtype.NamedType('ProcessName', char.UTF8String()) + ) + + class EnrollmentCSP(univ.Sequence): + pass + + EnrollmentCSP.componentType = namedtype.NamedTypes( + namedtype.NamedType('KeySpec', univ.Integer()), + namedtype.NamedType('Name', char.BMPString()), + namedtype.NamedType('Signature', univ.BitString()) + ) + + attributesMapUpdate = { + univ.ObjectIdentifier('1.3.6.1.4.1.311.13.2.3'): char.IA5String(), + univ.ObjectIdentifier('1.3.6.1.4.1.311.13.2.2'): EnrollmentCSP(), + univ.ObjectIdentifier('1.3.6.1.4.1.311.21.20'): ClientInformation(), + } + + rfc5652.cmsAttributesMap.update(rfc6402.cmcControlAttributesMap) + rfc5652.cmsAttributesMap.update(attributesMapUpdate) + + algorithmIdentifierMapUpdate = { + univ.ObjectIdentifier('1.2.840.113549.1.1.1'): univ.Null(""), + univ.ObjectIdentifier('1.2.840.113549.1.1.5'): univ.Null(""), + univ.ObjectIdentifier('1.2.840.113549.1.1.11'): univ.Null(""), + } + + rfc5280.algorithmIdentifierMap.update(algorithmIdentifierMapUpdate) + + rfc5652.cmsContentTypesMap.update(rfc6402.cmsContentTypesMapUpdate) + + substrate = pem.readBase64fromText(self.pem_text) + asn1Object, rest = der_decode(substrate, + asn1Spec=rfc5652.ContentInfo(), + decodeOpenTypes=True) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + eci = asn1Object['content']['encapContentInfo'] + assert eci['eContentType'] == rfc6402.id_cct_PKIData + substrate = eci['eContent'] + asn1Object, rest = der_decode(substrate, + asn1Spec=rfc6402.PKIData(), + decodeOpenTypes=True) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + for req in asn1Object['reqSequence']: + cr = req['tcr']['certificationRequest'] + + sig_alg = cr['signatureAlgorithm'] + assert sig_alg['algorithm'] in rfc5280.algorithmIdentifierMap.keys() + assert sig_alg['parameters'] == univ.Null("") + + cri = cr['certificationRequestInfo'] + spki_alg = cri['subjectPublicKeyInfo']['algorithm'] + assert spki_alg['algorithm'] in rfc5280.algorithmIdentifierMap.keys() + assert spki_alg['parameters'] == univ.Null("") + + attrs = cr['certificationRequestInfo']['attributes'] + for attr in attrs: + assert attr['attrType'] in rfc5652.cmsAttributesMap.keys() + if attr['attrType'] == univ.ObjectIdentifier('1.3.6.1.4.1.311.13.2.3'): + assert attr['attrValues'][0] == "6.2.9200.2" + else: + assert attr['attrValues'][0].hasValue() + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/tests/test_rfc7030.py b/tests/test_rfc7030.py new file mode 100644 index 0000000..5708203 --- /dev/null +++ b/tests/test_rfc7030.py @@ -0,0 +1,87 @@ +# +# 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 rfc5652 +from pyasn1_modules import rfc7030 + +from pyasn1.type import univ + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class CSRAttrsTestCase(unittest.TestCase): + pem_text = """\ +MEEGCSqGSIb3DQEJBzASBgcqhkjOPQIBMQcGBSuBBAAiMBYGCSqGSIb3DQEJDjEJ +BgcrBgEBAQEWBggqhkjOPQQDAw== +""" + + the_oids = (univ.ObjectIdentifier('1.2.840.113549.1.9.7'), + univ.ObjectIdentifier('1.2.840.10045.4.3.3'), + ) + + the_attrTypes = (univ.ObjectIdentifier('1.2.840.10045.2.1'), + univ.ObjectIdentifier('1.2.840.113549.1.9.14'), + ) + + the_attrVals = ('1.3.132.0.34', + '1.3.6.1.1.1.1.22', + ) + + + def setUp(self): + self.asn1Spec = rfc7030.CsrAttrs() + + 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 + + for attr_or_oid in asn1Object: + if attr_or_oid.getName() == 'oid': + assert attr_or_oid['oid'] in self.the_oids + + if attr_or_oid.getName() == 'attribute': + assert attr_or_oid['attribute']['attrType'] in self.the_attrTypes + + def testOpenTypes(self): + for at in self.the_attrTypes: + rfc5652.cmsAttributesMap.update({ at: univ.ObjectIdentifier(), }) + + 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 + + for attr_or_oid in asn1Object: + if attr_or_oid.getName() == 'attribute': + valString = attr_or_oid['attribute']['attrValues'][0].prettyPrint() + + if attr_or_oid['attribute']['attrType'] == self.the_attrTypes[0]: + assert valString == self.the_attrVals[0] + + if attr_or_oid['attribute']['attrType'] == self.the_attrTypes[1]: + assert valString == self.the_attrVals[1] + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/tests/test_rfc7292.py b/tests/test_rfc7292.py new file mode 100644 index 0000000..8bebef3 --- /dev/null +++ b/tests/test_rfc7292.py @@ -0,0 +1,172 @@ +# +# 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 rfc5652 +from pyasn1_modules import rfc7292 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class PKCS12TestCase(unittest.TestCase): + pfx_pem_text = """\ +MIIJ0wIBAzCCCY8GCSqGSIb3DQEHAaCCCYAEggl8MIIJeDCCBggGCSqGSIb3DQEHAaCCBfkE +ggX1MIIF8TCCBe0GCyqGSIb3DQEMCgECoIIE/jCCBPowHAYKKoZIhvcNAQwBAzAOBAjuq0/+ +0pyutQICB9AEggTYZe/mYBpmkDvKsve4EwIVwo1TNv4ldyx1qHZW2Ih6qQCY+Nv1Mnv9we0z +UTl4p3tQzCPWXnrSA82IgOdotLIez4YwXrgiKhcIkSSL+2yCmAoM+qkjiAIKq+l3UJ6Xhafe +2Kg4Ek/0RkHpe6GwjTtdefkpXpZgccMEopOtKQMLJWsDM7p77x/amn6yIk2tpskKqUY/4n8Y +xEiTWcRtTthYqZQIt+q94nKLYpt0o880SVOfvdEqp5KII7cTg60GJL+n6oN6hmP0bsAMvnk9 +1f8/lFKMi9tsNU/KnUhbDVpjJwBQkhgbqBx6GdtoqSLSlYNPVM0wlntwm1JhH4ybiQ5sNzqO +7FlWC5bcYwkvOlx1gGrshY5jK/WjbA4paBpxSkgobJReirY9BeqITnvokXlub4tehHhM20Ik +42pKa3kGaHmowvzflxqE+oysW5Oa9XbZxBCfkOMJ70o4hqa+n66+E/uKcN9NbKbTo3zt3xdt +6ypOwHb74t5OcWaGx3EZsw0n0/V+WoLSpXOBwpx08+1yh7LV29aNQ0oEzVVkF6YYRQZtdIMe +s3xB2i6sjLal21ntk7iBzMJwVoi524SAZ/oW8SuDAn1c93AWWwKZLALv5V3FZ2pDiQXArcfz +DH2d5HJyNx7OlvKzNgEngwSyEC1XbjnOsZVUqGFENuDTa/brH4oEJHEkyWTyDudrz8iCEO80 +e1PE4qqJ5CllN0CSVWqz4CxGDFIQXzR6ohn8f3dR3+DAaLYvAjBVMLJjk7+nfnB2L0HpanhT +Fz9AxPPIDf5pBQQwM14l8wKjEHIyfqclupeKNokBUr1ykioPyCr3nf4Rqe0Z4EKIY4OCpW6n +hrkWHmvF7OKR+bnuSk3jnBxjSN0Ivy5q9q3fntYrhscMGGR73umfi8Z29tM1vSP9jBZvirAo +geGf/sfOI0ewRvJf/5abnNg/78Zyk8WmlAHVFzNGcM3u3vhnNpTIVRuUyVkdSmOdbzeSfmqQ +2HPCEdC9HNm25KJt1pD6v6aP3Tw7qGl+tZyps7VB2i+a+UGcwQcClcoXcPSdG7Z1gBTzSr84 +MuVPYlePuo1x+UwppSK3rM8ET6KqhGmESH5lKadvs8vdT6c407PfLcfxyAGzjH091prk2oRJ +xB3oQAYcKvkuMcM6FSLJC263Dj+pe1GGEexk1AoysYe67tK0sB66hvbd92HcyWhW8/vI2/PM +bX+OeEb7q+ugnsP+BmF/btWXn9AxfUqNWstyInKTn+XpqFViMIOG4e2xC4u/IvzG3VrTWUHF +4pspH3k7GB/EOLvtbsR0uacBFlsColJy0FaWT9rrdueU3YEiIRCC8LGi1XpUa8f5adeBKWN+ +eRTrrF4o7uoNeGlnwZ7ebnb7k18Q0GRzzzTZPoMM4L703svfE/eNYWFHLY4NDQKSYgeum365 +WAfZpHOX7YOc6oRGrGB+QuGoyikTTDO8xpcEmb8vDz4ZwHhN0PS056LNJeMoI0A/5DJb3e10 +i1txlM48sbZBuIEIeixr52nwG4LuxqXGqShKaTfOrFxHjx4kI4/dp9dN/k8TGFsLWjuIgMJI +6nRHbWrxB3F0XKXagtLLep1MDwDwAuCyiW2YC0JzRvsJViIgjDA+eiHX0O6/8xiK9dzMQpIz +TVHSEqFlhORp0DGB2zATBgkqhkiG9w0BCRUxBgQEAQAAADBXBgkqhkiG9w0BCRQxSh5IADMA +ZgA3ADEAYQBmADYANQAtADEANgA4ADcALQA0ADQANABhAC0AOQBmADQANgAtAGMAOABiAGUA +MQA5ADQAYwAzAGUAOABlMGsGCSsGAQQBgjcRATFeHlwATQBpAGMAcgBvAHMAbwBmAHQAIABF +AG4AaABhAG4AYwBlAGQAIABDAHIAeQBwAHQAbwBnAHIAYQBwAGgAaQBjACAAUAByAG8AdgBp +AGQAZQByACAAdgAxAC4AMDCCA2gGCSqGSIb3DQEHAaCCA1kEggNVMIIDUTCCA00GCyqGSIb3 +DQEMCgEDoIIDJTCCAyEGCiqGSIb3DQEJFgGgggMRBIIDDTCCAwkwggHxoAMCAQICEDbt9oc6 +oQinRwE1826MiBEwDQYJKoZIhvcNAQEFBQAwFDESMBAGA1UEAxMJYW5vbnltb3VzMCAXDTE2 +MDcxOTIyMDAwMVoYDzIxMTYwNjI1MjIwMDAxWjAUMRIwEAYDVQQDEwlhbm9ueW1vdXMwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC8trBCTBjXXA4OgSO5nRTOU5T86ObCgc71 +J2oCuUigSddcTDzebaD0wcyAgf101hAdwMKQ9DvrK0nGvm7FAMnnUuVeATafKgshLuUTUUfK +jx4Xif4LoS0/ev4BiOI5a1MlIRZ7T5Cyjg8bvuympzMuinQ/j1RPLIV0VGU2HuDxuuP3O898 +GqZ3+F6Al5CUcwmOX9zCs91JdN/ZFZ05SXIpHQuyPSPUX5Vy8F1ZeJ8VG3nkbemfFlVkuKQq +vteL9mlT7z95rVZgGB3nUZL0tOB68eMcffA9zUksOmeTi5M6jnBcNeX2Jh9jS3YYd+IEliZm +mggQG7kPta8f+NqezL77AgMBAAGjVTBTMBUGA1UdJQQOMAwGCisGAQQBgjcKAwQwLwYDVR0R +BCgwJqAkBgorBgEEAYI3FAIDoBYMFGFub255bW91c0B3aW5kb3dzLXgAMAkGA1UdEwQCMAAw +DQYJKoZIhvcNAQEFBQADggEBALh+4qmNPzC6M8BW9/SC2ACQxxPh06GQUGx0D+GLYnp61ErZ +OtKyKdFh+uZWpu5vyYYAHCLXP7VdS/JhJy677ynAPjXiC/LAzrTNvGs74HDotD966Hiyy0Qr +ospFGiplHGRA5vXA2CiKSX+0HrVkN7rhk5PYkc6R+/cdosd+QZ8lkEa9yDWc5l//vWEbzwVy +mJf/PRf8NTkWAK6SPV7Y37j1mhkJjOH9VkRxNrd6kcihRa4u0ImXaXEsec77ER0so31DKCrP +m+rqZPj9NZSIYP3sMGJ4Bmm/n2YRdeaUzTdocfD3TRnKxs65DSgpiSq1gmtsXM7jAPs/Egrg +tbWEypgxFTATBgkqhkiG9w0BCRUxBgQEAQAAADA7MB8wBwYFKw4DAhoEFKVgj/32UdEyuQcB +rqr03dPnboinBBSU7mxdpB5LTCvorCI8Tk5OMiUzjgICB9A= +""" + + def setUp(self): + self.asn1Spec = rfc7292.PFX() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.pfx_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + assert asn1Object['version'] == univ.Integer(3) + oid = asn1Object['macData']['mac']['digestAlgorithm']['algorithm'] + assert oid == univ.ObjectIdentifier('1.3.14.3.2.26') + md_hex = asn1Object['macData']['mac']['digest'].prettyPrint() + assert md_hex == '0xa5608ffdf651d132b90701aeaaf4ddd3e76e88a7' + + assert asn1Object['authSafe']['contentType'] == rfc5652.id_data + data, rest = der_decode(asn1Object['authSafe']['content'], + asn1Spec=univ.OctetString()) + assert not rest + + authsafe, rest = der_decode(data, asn1Spec=rfc7292.AuthenticatedSafe()) + assert not rest + assert authsafe.prettyPrint() + assert der_encode(authsafe) == data + + for ci in authsafe: + assert ci['contentType'] == rfc5652.id_data + data, rest = der_decode(ci['content'], asn1Spec=univ.OctetString()) + assert not rest + + sc, rest = der_decode(data, asn1Spec=rfc7292.SafeContents()) + assert not rest + assert sc.prettyPrint() + assert der_encode(sc) == data + + for sb in sc: + if sb['bagId'] in rfc7292.pkcs12BagTypeMap: + bv, rest = der_decode(sb['bagValue'], + asn1Spec=rfc7292.pkcs12BagTypeMap[sb['bagId']]) + assert not rest + assert bv.prettyPrint() + assert der_encode(bv) == sb['bagValue'] + + for attr in sb['bagAttributes']: + if attr['attrType'] in rfc5652.cmsAttributesMap: + av, rest = der_decode(attr['attrValues'][0], + asn1Spec=rfc5652.cmsAttributesMap[attr['attrType']]) + assert not rest + assert av.prettyPrint() + assert der_encode(av) == attr['attrValues'][0] + + def testOpenTypes(self): + substrate = pem.readBase64fromText(self.pfx_pem_text) + asn1Object, rest = der_decode(substrate, + asn1Spec=self.asn1Spec, + decodeOpenTypes=True + ) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + digest_alg = asn1Object['macData']['mac']['digestAlgorithm'] + assert not digest_alg['parameters'].hasValue() + + authsafe, rest = der_decode(asn1Object['authSafe']['content'], + asn1Spec=rfc7292.AuthenticatedSafe(), + decodeOpenTypes=True + ) + assert not rest + assert authsafe.prettyPrint() + assert der_encode(authsafe) == asn1Object['authSafe']['content'] + + for ci in authsafe: + assert ci['contentType'] == rfc5652.id_data + sc, rest = der_decode(ci['content'], + asn1Spec=rfc7292.SafeContents(), + decodeOpenTypes=True + ) + assert not rest + assert sc.prettyPrint() + assert der_encode(sc) == ci['content'] + + for sb in sc: + if sb['bagId'] == rfc7292.id_pkcs8ShroudedKeyBag: + bv = sb['bagValue'] + enc_alg = bv['encryptionAlgorithm']['algorithm'] + assert enc_alg == rfc7292.pbeWithSHAAnd3_KeyTripleDES_CBC + enc_alg_param = bv['encryptionAlgorithm']['parameters'] + assert enc_alg_param['iterations'] == 2000 + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/tests/test_rfc7296.py b/tests/test_rfc7296.py index bcd28ee..90dc0a3 100644 --- a/tests/test_rfc7296.py +++ b/tests/test_rfc7296.py @@ -161,7 +161,4 @@ m9Y= suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) if __name__ == '__main__': - import sys - - result = unittest.TextTestRunner(verbosity=2).run(suite) - sys.exit(not result.wasSuccessful()) + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/tests/test_rfc8018.py b/tests/test_rfc8018.py new file mode 100644 index 0000000..f899b54 --- /dev/null +++ b/tests/test_rfc8018.py @@ -0,0 +1,56 @@ +# +# 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 rfc5652 +from pyasn1_modules import rfc8018 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class PWRITestCase(unittest.TestCase): + rfc3211_ex1_pem_text = """\ +o1MCAQCgGgYJKoZIhvcNAQUMMA0ECBI0Vnh4VjQSAgEFMCAGCyqGSIb3DQEJEAMJMBEGBSsO +AwIHBAjv5ZjvIbM9bQQQuBslZe43PKbe3KJqF4sMEA== +""" + + def setUp(self): + self.asn1Spec = rfc5652.RecipientInfo() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.rfc3211_ex1_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + alg_oid = asn1Object['pwri']['keyDerivationAlgorithm']['algorithm'] + assert alg_oid == rfc8018.id_PBKDF2 + + def testOpenTypes(self): + substrate = pem.readBase64fromText(self.rfc3211_ex1_pem_text) + asn1Object, rest = der_decode(substrate, + asn1Spec=self.asn1Spec, + decodeOpenTypes=True) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + icount = asn1Object['pwri']['keyDerivationAlgorithm']['parameters']['iterationCount'] + assert icount == 5 + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) -- cgit v1.2.3 From fdd5c3033d124ad159b9784e3e7fe304f777dbb8 Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Sun, 18 Aug 2019 02:12:09 -0400 Subject: Improve opentypes maps for RFC3709, RFC6402, RFC7191, and RFC8226 (#57) --- CHANGES.txt | 2 ++ pyasn1_modules/rfc3709.py | 8 +++++--- pyasn1_modules/rfc6402.py | 11 +++++++---- pyasn1_modules/rfc7191.py | 16 ++++++++++------ pyasn1_modules/rfc8226.py | 8 +++++--- tests/test_rfc3709.py | 1 - tests/test_rfc5652.py | 2 -- tests/test_rfc6402.py | 4 ---- tests/test_rfc7191.py | 5 ----- 9 files changed, 29 insertions(+), 28 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 60cb3e4..9f99e8a 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -15,6 +15,8 @@ Revision 0.2.7, released XX-08-2019 Exchange Syntax v1.1 - Added RFC8018 providing PKCS #5, which is the Password-Based Cryptography Specification, Version 2.1 +- Automatically update the maps for use with openType for RFC3709, + RFC6402, RFC7191, and RFC8226 when the module is imported Revision 0.2.6, released 31-07-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc3709.py b/pyasn1_modules/rfc3709.py index a52f99e..f26ba33 100644 --- a/pyasn1_modules/rfc3709.py +++ b/pyasn1_modules/rfc3709.py @@ -194,9 +194,11 @@ LogotypeExtn.componentType = namedtype.NamedTypes( ) -# Map of Certificate Extension OIDs to Extensions -# To be added to the ones that are in rfc5280.py +# Map of Certificate Extension OIDs to Extensions added to the +# ones that are in rfc5280.py -certificateExtensionsMapUpdate = { +_certificateExtensionsMapUpdate = { id_pe_logotype: LogotypeExtn(), } + +rfc5280.certificateExtensionsMap.update(_certificateExtensionsMapUpdate) \ No newline at end of file diff --git a/pyasn1_modules/rfc6402.py b/pyasn1_modules/rfc6402.py index dbb699a..322e35e 100644 --- a/pyasn1_modules/rfc6402.py +++ b/pyasn1_modules/rfc6402.py @@ -615,10 +615,13 @@ _cmcControlAttributesMapUpdate = { cmcControlAttributesMap.update(_cmcControlAttributesMapUpdate) -# Map of CMC Content Type OIDs to CMC Content Types -# To be added to the ones that are in rfc5652.py +# Map of CMC Content Type OIDs to CMC Content Types are added to +# the ones that are in rfc5652.py -cmsContentTypesMapUpdate = { +_cmsContentTypesMapUpdate = { id_cct_PKIData: PKIData(), id_cct_PKIResponse: PKIResponse(), -} \ No newline at end of file +} + +rfc5652.cmsContentTypesMap.update(_cmsContentTypesMapUpdate) + diff --git a/pyasn1_modules/rfc7191.py b/pyasn1_modules/rfc7191.py index 4f90be1..7c2be11 100644 --- a/pyasn1_modules/rfc7191.py +++ b/pyasn1_modules/rfc7191.py @@ -240,18 +240,22 @@ KeyPkgIdentifierAndReceiptReq.componentType = namedtype.NamedTypes( ) -# Map of Attribute Type OIDs to Attributes -# To be added to the ones that are in rfc5652.py +# Map of Attribute Type OIDs to Attributes are added to +# the ones that are in rfc5652.py -cmsAttributesMapUpdate = { +_cmsAttributesMapUpdate = { id_aa_KP_keyPkgIdAndReceiptReq: KeyPkgIdentifierAndReceiptReq(), } +rfc5652.cmsAttributesMap.update(_cmsAttributesMapUpdate) -# Map of Content Type OIDs to Content Types -# To be added to the ones that are in rfc5652.py -cmsContentTypesMapUpdate = { +# Map of CMC Content Type OIDs to CMC Content Types are added to +# the ones that are in rfc5652.py + +_cmsContentTypesMapUpdate = { id_ct_KP_keyPackageError: KeyPackageError(), id_ct_KP_keyPackageReceipt: KeyPackageReceipt(), } + +rfc5652.cmsContentTypesMap.update(_cmsContentTypesMapUpdate) diff --git a/pyasn1_modules/rfc8226.py b/pyasn1_modules/rfc8226.py index 0c3dc21..a5eb211 100644 --- a/pyasn1_modules/rfc8226.py +++ b/pyasn1_modules/rfc8226.py @@ -18,6 +18,7 @@ from pyasn1.type import namedtype from pyasn1.type import tag from pyasn1.type import univ +from pyasn1_modules import rfc5280 MAX = float('inf') @@ -133,11 +134,12 @@ id_pe_TNAuthList = _OID(1, 3, 6, 1, 5, 5, 7, 1, 26) id_ad_stirTNList = _OID(1, 3, 6, 1, 5, 5, 7, 48, 14) -# Map of Certificate Extension OIDs to Extensions -# To be added to the ones that are in rfc5280.py +# Map of Certificate Extension OIDs to Extensions added to the +# ones that are in rfc5280.py -certificateExtensionsMapUpdate = { +_certificateExtensionsMapUpdate = { id_pe_TNAuthList: TNAuthorizationList(), id_pe_JWTClaimConstraints: JWTClaimConstraints(), } +rfc5280.certificateExtensionsMap.update(_certificateExtensionsMapUpdate) diff --git a/tests/test_rfc3709.py b/tests/test_rfc3709.py index a7d0ef8..56753cc 100644 --- a/tests/test_rfc3709.py +++ b/tests/test_rfc3709.py @@ -66,7 +66,6 @@ Pj22pmfmQi5w21UljqoTj/+lQLkU3wfy5BdVKBwI0GfEA+YL3ctSzPNqAA== def testExtensionsMap(self): substrate = pem.readBase64fromText(self.pem_text) - rfc5280.certificateExtensionsMap.update(rfc3709.certificateExtensionsMapUpdate) asn1Object, rest = der_decoder.decode(substrate, asn1Spec=self.asn1Spec) assert not rest assert asn1Object.prettyPrint() diff --git a/tests/test_rfc5652.py b/tests/test_rfc5652.py index eac4262..b34627b 100644 --- a/tests/test_rfc5652.py +++ b/tests/test_rfc5652.py @@ -125,8 +125,6 @@ xicQmJP+VoMHo/ZpjFY9fYCjNZUArgKsEwK/s+p9yrVVeB1Nf8Mn rfc5280.algorithmIdentifierMap.update(algorithmIdentifierMapUpdate) - rfc5652.cmsContentTypesMap.update(rfc6402.cmsContentTypesMapUpdate) - substrate = pem.readBase64fromText(self.pem_text) asn1Object, rest = der_decoder.decode(substrate, diff --git a/tests/test_rfc6402.py b/tests/test_rfc6402.py index 65d5ea9..191078c 100755 --- a/tests/test_rfc6402.py +++ b/tests/test_rfc6402.py @@ -50,8 +50,6 @@ xicQmJP+VoMHo/ZpjFY9fYCjNZUArgKsEwK/s+p9yrVVeB1Nf8Mn """ def testDerCodec(self): - - rfc5652.cmsContentTypesMap.update(rfc6402.cmsContentTypesMapUpdate) layers = rfc5652.cmsContentTypesMap getNextLayer = { @@ -116,8 +114,6 @@ xicQmJP+VoMHo/ZpjFY9fYCjNZUArgKsEwK/s+p9yrVVeB1Nf8Mn rfc5280.algorithmIdentifierMap.update(algorithmIdentifierMapUpdate) - rfc5652.cmsContentTypesMap.update(rfc6402.cmsContentTypesMapUpdate) - substrate = pem.readBase64fromText(self.pem_text) asn1Object, rest = der_decode(substrate, asn1Spec=rfc5652.ContentInfo(), diff --git a/tests/test_rfc7191.py b/tests/test_rfc7191.py index 337bd44..9352c1f 100644 --- a/tests/test_rfc7191.py +++ b/tests/test_rfc7191.py @@ -89,7 +89,6 @@ goRV+bq4fdgOOj25JFqa80xnXGtQqjm/7NSII5SbdJk+DT7KCkSbkElkbgQ= def testOpenTypes(self): substrate = pem.readBase64fromText(self.message1_pem_text) - rfc5652.cmsAttributesMap.update(rfc7191.cmsAttributesMapUpdate) asn1Object, rest = der_decode (substrate, asn1Spec=self.asn1Spec, decodeOpenTypes=True) @@ -165,8 +164,6 @@ bUcOYuCdivgxVuhlAgIxAPR9JavxziwCbVyBUWOAiKKYfglTgG3AwNmrKDj0NtXUQ9qDmGAc def testOpenTypes(self): substrate = pem.readBase64fromText(self.message2_pem_text) - rfc5652.cmsContentTypesMap.update(rfc7191.cmsContentTypesMapUpdate) - rfc5652.cmsAttributesMap.update(rfc7191.cmsAttributesMapUpdate) asn1Object, rest = der_decode (substrate, asn1Spec=self.asn1Spec, decodeOpenTypes=True) @@ -254,8 +251,6 @@ iNF8uKtW/lk0AjA7z2q40N0lamXkSU7ECasiWOYV1X4cWGiQwMZDKknBPDqXqB6Es6p4J+qe def testOpenTypes(self): substrate = pem.readBase64fromText(self.message3_pem_text) - rfc5652.cmsContentTypesMap.update(rfc7191.cmsContentTypesMapUpdate) - rfc5652.cmsAttributesMap.update(rfc7191.cmsAttributesMapUpdate) asn1Object, rest = der_decode (substrate, asn1Spec=self.asn1Spec, decodeOpenTypes=True) -- cgit v1.2.3 From b67a4d7d5a2f78dacb4fa211da3a342160edf901 Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Wed, 28 Aug 2019 18:06:42 -0400 Subject: Add support for RFC 6211 and RFC 8649 (#59) --- CHANGES.txt | 2 + pyasn1_modules/rfc6211.py | 74 +++++++++++++++++++++++++++++ pyasn1_modules/rfc8649.py | 40 ++++++++++++++++ tests/__main__.py | 4 +- tests/test_rfc6211.py | 117 ++++++++++++++++++++++++++++++++++++++++++++++ tests/test_rfc8649.py | 64 +++++++++++++++++++++++++ 6 files changed, 300 insertions(+), 1 deletion(-) create mode 100644 pyasn1_modules/rfc6211.py create mode 100644 pyasn1_modules/rfc8649.py create mode 100644 tests/test_rfc6211.py create mode 100644 tests/test_rfc8649.py 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()) -- cgit v1.2.3 From 1c6cf63a2d8767879dc6e1ca0862bb4815111aca Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sun, 1 Sep 2019 18:19:55 +0200 Subject: Push pyasn1 requirement to 0.4.7 To support upcoming `SET ... WITH COMPONENTS` constraint. --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index cb1feb0..68daca2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -pyasn1>=0.4.6,<0.5.0 +pyasn1>=0.4.7,<0.5.0 -- cgit v1.2.3 From 3f4a9e498ccd2c2998adbf2c9e7ddda87e87d7aa Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Sun, 1 Sep 2019 12:21:48 -0400 Subject: Updates to RFC2459, RFC5280, and RFC6211 (#60) Add `WithComponentsConstraint` where applicable. --- CHANGES.txt | 1 + pyasn1_modules/rfc2459.py | 6 ++++-- pyasn1_modules/rfc5280.py | 2 +- pyasn1_modules/rfc6211.py | 18 ++++++++---------- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index de550b9..d5b7902 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -19,6 +19,7 @@ Revision 0.2.7, released XX-08-2019 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 +- Updated RFC2459 and RFC5280 for TODO in the certificate extension map Revision 0.2.6, released 31-07-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc2459.py b/pyasn1_modules/rfc2459.py index 2a2e696..9f35787 100644 --- a/pyasn1_modules/rfc2459.py +++ b/pyasn1_modules/rfc2459.py @@ -1,6 +1,9 @@ # # This file is part of pyasn1-modules software. # +# Updated by Russ Housley to resolve the TODO regarding the Certificate +# Policies Certificate Extension. +# # Copyright (c) 2005-2019, Ilya Etingof # License: http://snmplabs.com/pyasn1/license.html # @@ -1312,8 +1315,7 @@ _certificateExtensionsMapUpdate = { id_ce_subjectKeyIdentifier: SubjectKeyIdentifier(), id_ce_keyUsage: KeyUsage(), id_ce_privateKeyUsagePeriod: PrivateKeyUsagePeriod(), -# TODO -# id_ce_certificatePolicies: PolicyInformation(), # could be a sequence of concat'ed objects? + id_ce_certificatePolicies: CertificatePolicies(), id_ce_policyMappings: PolicyMappings(), id_ce_subjectAltName: SubjectAltName(), id_ce_issuerAltName: IssuerAltName(), diff --git a/pyasn1_modules/rfc5280.py b/pyasn1_modules/rfc5280.py index 181584c..f2b52b2 100644 --- a/pyasn1_modules/rfc5280.py +++ b/pyasn1_modules/rfc5280.py @@ -1635,7 +1635,7 @@ _certificateExtensionsMap = { id_ce_subjectKeyIdentifier: SubjectKeyIdentifier(), id_ce_keyUsage: KeyUsage(), id_ce_privateKeyUsagePeriod: PrivateKeyUsagePeriod(), - id_ce_certificatePolicies: PolicyInformation(), # could be a sequence of concat'ed objects? + id_ce_certificatePolicies: CertificatePolicies(), id_ce_policyMappings: PolicyMappings(), id_ce_subjectAltName: SubjectAltName(), id_ce_issuerAltName: IssuerAltName(), diff --git a/pyasn1_modules/rfc6211.py b/pyasn1_modules/rfc6211.py index 3853817..abd7a86 100644 --- a/pyasn1_modules/rfc6211.py +++ b/pyasn1_modules/rfc6211.py @@ -47,16 +47,14 @@ CMSAlgorithmProtection.componentType = namedtype.NamedTypes( 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())) -# ) +CMSAlgorithmProtection.subtypeSpec = constraint.ConstraintsUnion( + constraint.WithComponentsConstraint( + ('signatureAlgorithm', constraint.ComponentPresentConstraint()), + ('macAlgorithm', constraint.ComponentAbsentConstraint())), + constraint.WithComponentsConstraint( + ('signatureAlgorithm', constraint.ComponentAbsentConstraint()), + ('macAlgorithm', constraint.ComponentPresentConstraint())) +) aa_cmsAlgorithmProtection = rfc5652.Attribute() -- cgit v1.2.3 From 674015081b7a2d157a832f1ba6a0f7400b859dc4 Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Tue, 3 Sep 2019 03:06:48 -0400 Subject: Add support for RFC 7906 (#61) --- CHANGES.txt | 1 + pyasn1_modules/rfc7906.py | 736 ++++++++++++++++++++++++++++++++++++++++++++++ tests/__main__.py | 1 + tests/test_rfc7906.py | 167 +++++++++++ 4 files changed, 905 insertions(+) create mode 100644 pyasn1_modules/rfc7906.py create mode 100644 tests/test_rfc7906.py diff --git a/CHANGES.txt b/CHANGES.txt index d5b7902..0a4778b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -20,6 +20,7 @@ Revision 0.2.7, released XX-08-2019 - Added RFC6211 providing CMS Algorithm Identifier Protection Attribute - Added RFC8449 providing Certificate Extension for Hash Of Root Key - Updated RFC2459 and RFC5280 for TODO in the certificate extension map +- Added RFC7906 providing NSA's CMS Key Management Attributes Revision 0.2.6, released 31-07-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc7906.py b/pyasn1_modules/rfc7906.py new file mode 100644 index 0000000..fa5f6b0 --- /dev/null +++ b/pyasn1_modules/rfc7906.py @@ -0,0 +1,736 @@ +# +# 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 +# +# NSA's CMS Key Management Attributes +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc7906.txt +# https://www.rfc-editor.org/errata/eid5850 +# + +from pyasn1.type import char +from pyasn1.type import constraint +from pyasn1.type import namedtype +from pyasn1.type import namedval +from pyasn1.type import tag +from pyasn1.type import univ + +from pyasn1_modules import rfc2634 +from pyasn1_modules import rfc4108 +from pyasn1_modules import rfc5280 +from pyasn1_modules import rfc5652 +from pyasn1_modules import rfc6010 +from pyasn1_modules import rfc6019 +from pyasn1_modules import rfc7191 + +MAX = float('inf') + + +# Imports From RFC 2634 + +id_aa_contentHint = rfc2634.id_aa_contentHint + +ContentHints = rfc2634.ContentHints + +id_aa_securityLabel = rfc2634.id_aa_securityLabel + +SecurityPolicyIdentifier = rfc2634.SecurityPolicyIdentifier + +SecurityClassification = rfc2634.SecurityClassification + +ESSPrivacyMark = rfc2634.ESSPrivacyMark + +SecurityCategories= rfc2634.SecurityCategories + +ESSSecurityLabel = rfc2634.ESSSecurityLabel + + +# Imports From RFC 4108 + +id_aa_communityIdentifiers = rfc4108.id_aa_communityIdentifiers + +CommunityIdentifier = rfc4108.CommunityIdentifier + +CommunityIdentifiers = rfc4108.CommunityIdentifiers + + +# Imports From RFC 5280 + +AlgorithmIdentifier = rfc5280.AlgorithmIdentifier + +Name = rfc5280.Name + +Certificate = rfc5280.Certificate + +GeneralNames = rfc5280.GeneralNames + +GeneralName = rfc5280.GeneralName + + +SubjectInfoAccessSyntax = rfc5280.SubjectInfoAccessSyntax + +id_pkix = rfc5280.id_pkix + +id_pe = rfc5280.id_pe + +id_pe_subjectInfoAccess = rfc5280.id_pe_subjectInfoAccess + + +# Imports From RFC 6010 + +CMSContentConstraints = rfc6010.CMSContentConstraints + + +# Imports From RFC 6019 + +BinaryTime = rfc6019.BinaryTime + +id_aa_binarySigningTime = rfc6019.id_aa_binarySigningTime + +BinarySigningTime = rfc6019.BinarySigningTime + + +# Imports From RFC 5652 + +Attribute = rfc5652.Attribute + +CertificateSet = rfc5652.CertificateSet + +CertificateChoices = rfc5652.CertificateChoices + +id_contentType = rfc5652.id_contentType + +ContentType = rfc5652.ContentType + +id_messageDigest = rfc5652.id_messageDigest + +MessageDigest = rfc5652.MessageDigest + + +# Imports From RFC 7191 + +SIREntityName = rfc7191.SIREntityName + +id_aa_KP_keyPkgIdAndReceiptReq = rfc7191.id_aa_KP_keyPkgIdAndReceiptReq + +KeyPkgIdentifierAndReceiptReq = rfc7191.KeyPkgIdentifierAndReceiptReq + + +# Key Province Attribute + +id_aa_KP_keyProvinceV2 = univ.ObjectIdentifier('2.16.840.1.101.2.1.5.71') + + +class KeyProvinceV2(univ.ObjectIdentifier): + pass + + +aa_keyProvince_v2 = Attribute() +aa_keyProvince_v2['attrType'] = id_aa_KP_keyProvinceV2 +aa_keyProvince_v2['attrValues'][0] = KeyProvinceV2() + + +# Manifest Attribute + +id_aa_KP_manifest = univ.ObjectIdentifier('2.16.840.1.101.2.1.5.72') + + +class ShortTitle(char.PrintableString): + pass + + +class Manifest(univ.SequenceOf): + pass + +Manifest.componentType = ShortTitle() +Manifest.subtypeSpec=constraint.ValueSizeConstraint(1, MAX) + + +aa_manifest = Attribute() +aa_manifest['attrType'] = id_aa_KP_manifest +aa_manifest['attrValues'][0] = Manifest() + + +# Key Algorithm Attribute + +id_kma_keyAlgorithm = univ.ObjectIdentifier('2.16.840.1.101.2.1.13.1') + + +class KeyAlgorithm(univ.Sequence): + pass + +KeyAlgorithm.componentType = namedtype.NamedTypes( + namedtype.NamedType('keyAlg', univ.ObjectIdentifier()), + namedtype.OptionalNamedType('checkWordAlg', univ.ObjectIdentifier().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.OptionalNamedType('crcAlg', univ.ObjectIdentifier().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))) +) + + +aa_keyAlgorithm = Attribute() +aa_keyAlgorithm['attrType'] = id_kma_keyAlgorithm +aa_keyAlgorithm['attrValues'][0] = KeyAlgorithm() + + +# User Certificate Attribute + +id_at_userCertificate = univ.ObjectIdentifier('2.5.4.36') + + +aa_userCertificate = Attribute() +aa_userCertificate['attrType'] = id_at_userCertificate +aa_userCertificate['attrValues'][0] = Certificate() + + +# Key Package Receivers Attribute + +id_kma_keyPkgReceiversV2 = univ.ObjectIdentifier('2.16.840.1.101.2.1.13.16') + + +class KeyPkgReceiver(univ.Choice): + pass + +KeyPkgReceiver.componentType = namedtype.NamedTypes( + namedtype.NamedType('sirEntity', SIREntityName().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.NamedType('community', CommunityIdentifier().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))) +) + + +class KeyPkgReceiversV2(univ.SequenceOf): + pass + +KeyPkgReceiversV2.componentType = KeyPkgReceiver() +KeyPkgReceiversV2.subtypeSpec=constraint.ValueSizeConstraint(1, MAX) + + +aa_keyPackageReceivers_v2 = Attribute() +aa_keyPackageReceivers_v2['attrType'] = id_kma_keyPkgReceiversV2 +aa_keyPackageReceivers_v2['attrValues'][0] = KeyPkgReceiversV2() + + +# TSEC Nomenclature Attribute + +id_kma_TSECNomenclature = univ.ObjectIdentifier('2.16.840.1.101.2.1.13.3') + + +class CharEdition(char.PrintableString): + pass + + +class CharEditionRange(univ.Sequence): + pass + +CharEditionRange.componentType = namedtype.NamedTypes( + namedtype.NamedType('firstCharEdition', CharEdition()), + namedtype.NamedType('lastCharEdition', CharEdition()) +) + + +class NumEdition(univ.Integer): + pass + +NumEdition.subtypeSpec = constraint.ValueRangeConstraint(0, 308915776) + + +class NumEditionRange(univ.Sequence): + pass + +NumEditionRange.componentType = namedtype.NamedTypes( + namedtype.NamedType('firstNumEdition', NumEdition()), + namedtype.NamedType('lastNumEdition', NumEdition()) +) + + +class EditionID(univ.Choice): + pass + +EditionID.componentType = namedtype.NamedTypes( + namedtype.NamedType('char', univ.Choice(componentType=namedtype.NamedTypes( + namedtype.NamedType('charEdition', CharEdition().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.NamedType('charEditionRange', CharEditionRange().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2))) + )) + ), + namedtype.NamedType('num', univ.Choice(componentType=namedtype.NamedTypes( + namedtype.NamedType('numEdition', NumEdition().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))), + namedtype.NamedType('numEditionRange', NumEditionRange().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 4))) + )) + ) +) + + +class Register(univ.Integer): + pass + +Register.subtypeSpec = constraint.ValueRangeConstraint(0, 2147483647) + + +class RegisterRange(univ.Sequence): + pass + +RegisterRange.componentType = namedtype.NamedTypes( + namedtype.NamedType('firstRegister', Register()), + namedtype.NamedType('lastRegister', Register()) +) + + +class RegisterID(univ.Choice): + pass + +RegisterID.componentType = namedtype.NamedTypes( + namedtype.NamedType('register', Register().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 5))), + namedtype.NamedType('registerRange', RegisterRange().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 6))) +) + + +class SegmentNumber(univ.Integer): + pass + +SegmentNumber.subtypeSpec = constraint.ValueRangeConstraint(1, 127) + + +class SegmentRange(univ.Sequence): + pass + +SegmentRange.componentType = namedtype.NamedTypes( + namedtype.NamedType('firstSegment', SegmentNumber()), + namedtype.NamedType('lastSegment', SegmentNumber()) +) + + +class SegmentID(univ.Choice): + pass + +SegmentID.componentType = namedtype.NamedTypes( + namedtype.NamedType('segmentNumber', SegmentNumber().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 7))), + namedtype.NamedType('segmentRange', SegmentRange().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 8))) +) + + +class TSECNomenclature(univ.Sequence): + pass + +TSECNomenclature.componentType = namedtype.NamedTypes( + namedtype.NamedType('shortTitle', ShortTitle()), + namedtype.OptionalNamedType('editionID', EditionID()), + namedtype.OptionalNamedType('registerID', RegisterID()), + namedtype.OptionalNamedType('segmentID', SegmentID()) +) + + +aa_tsecNomenclature = Attribute() +aa_tsecNomenclature['attrType'] = id_kma_TSECNomenclature +aa_tsecNomenclature['attrValues'][0] = TSECNomenclature() + + +# Key Purpose Attribute + +id_kma_keyPurpose = univ.ObjectIdentifier('2.16.840.1.101.2.1.13.13') + + +class KeyPurpose(univ.Enumerated): + pass + +KeyPurpose.namedValues = namedval.NamedValues( + ('n-a', 0), + ('a', 65), + ('b', 66), + ('l', 76), + ('m', 77), + ('r', 82), + ('s', 83), + ('t', 84), + ('v', 86), + ('x', 88), + ('z', 90) +) + + +aa_keyPurpose = Attribute() +aa_keyPurpose['attrType'] = id_kma_keyPurpose +aa_keyPurpose['attrValues'][0] = KeyPurpose() + + +# Key Use Attribute + +id_kma_keyUse = univ.ObjectIdentifier('2.16.840.1.101.2.1.13.14') + + +class KeyUse(univ.Enumerated): + pass + +KeyUse.namedValues = namedval.NamedValues( + ('n-a', 0), + ('ffk', 1), + ('kek', 2), + ('kpk', 3), + ('msk', 4), + ('qkek', 5), + ('tek', 6), + ('tsk', 7), + ('trkek', 8), + ('nfk', 9), + ('effk', 10), + ('ebfk', 11), + ('aek', 12), + ('wod', 13), + ('kesk', 246), + ('eik', 247), + ('ask', 248), + ('kmk', 249), + ('rsk', 250), + ('csk', 251), + ('sak', 252), + ('rgk', 253), + ('cek', 254), + ('exk', 255) +) + + +aa_keyUse = Attribute() +aa_keyPurpose['attrType'] = id_kma_keyUse +aa_keyPurpose['attrValues'][0] = KeyUse() + + +# Transport Key Attribute + +id_kma_transportKey = univ.ObjectIdentifier('2.16.840.1.101.2.1.13.15') + + +class TransOp(univ.Enumerated): + pass + +TransOp.namedValues = namedval.NamedValues( + ('transport', 1), + ('operational', 2) +) + + +aa_transportKey = Attribute() +aa_transportKey['attrType'] = id_kma_transportKey +aa_transportKey['attrValues'][0] = TransOp() + + +# Key Distribution Period Attribute + +id_kma_keyDistPeriod = univ.ObjectIdentifier('2.16.840.1.101.2.1.13.5') + + +class KeyDistPeriod(univ.Sequence): + pass + +KeyDistPeriod.componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('doNotDistBefore', BinaryTime().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.NamedType('doNotDistAfter', BinaryTime()) +) + + +aa_keyDistributionPeriod = Attribute() +aa_keyDistributionPeriod['attrType'] = id_kma_keyDistPeriod +aa_keyDistributionPeriod['attrValues'][0] = KeyDistPeriod() + + +# Key Validity Period Attribute + +id_kma_keyValidityPeriod = univ.ObjectIdentifier('2.16.840.1.101.2.1.13.6') + + +class KeyValidityPeriod(univ.Sequence): + pass + +KeyValidityPeriod.componentType = namedtype.NamedTypes( + namedtype.NamedType('doNotUseBefore', BinaryTime()), + namedtype.OptionalNamedType('doNotUseAfter', BinaryTime()) +) + + +aa_keyValidityPeriod = Attribute() +aa_keyValidityPeriod['attrType'] = id_kma_keyValidityPeriod +aa_keyValidityPeriod['attrValues'][0] = KeyValidityPeriod() + + +# Key Duration Attribute + +id_kma_keyDuration = univ.ObjectIdentifier('2.16.840.1.101.2.1.13.7') + + +ub_KeyDuration_months = univ.Integer(72) + +ub_KeyDuration_hours = univ.Integer(96) + +ub_KeyDuration_days = univ.Integer(732) + +ub_KeyDuration_weeks = univ.Integer(104) + +ub_KeyDuration_years = univ.Integer(100) + + +class KeyDuration(univ.Choice): + pass + +KeyDuration.componentType = namedtype.NamedTypes( + namedtype.NamedType('hours', univ.Integer().subtype( + subtypeSpec=constraint.ValueRangeConstraint(1, ub_KeyDuration_hours)).subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.NamedType('days', univ.Integer().subtype( + subtypeSpec=constraint.ValueRangeConstraint(1, ub_KeyDuration_days))), + namedtype.NamedType('weeks', univ.Integer().subtype( + subtypeSpec=constraint.ValueRangeConstraint(1, ub_KeyDuration_weeks)).subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.NamedType('months', univ.Integer().subtype( + subtypeSpec=constraint.ValueRangeConstraint(1, ub_KeyDuration_months)).subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))), + namedtype.NamedType('years', univ.Integer().subtype( + subtypeSpec=constraint.ValueRangeConstraint(1, ub_KeyDuration_years)).subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))) +) + + +aa_keyDurationPeriod = Attribute() +aa_keyDurationPeriod['attrType'] = id_kma_keyDuration +aa_keyDurationPeriod['attrValues'][0] = KeyDuration() + + +# Classification Attribute + +id_aa_KP_classification = univ.ObjectIdentifier(id_aa_securityLabel) + + +id_enumeratedPermissiveAttributes = univ.ObjectIdentifier('2.16.840.1.101.2.1.8.3.1') + +id_enumeratedRestrictiveAttributes = univ.ObjectIdentifier('2.16.840.1.101.2.1.8.3.4') + +id_informativeAttributes = univ.ObjectIdentifier('2.16.840.1.101.2.1.8.3.3') + + +class SecurityAttribute(univ.Integer): + pass + +SecurityAttribute.subtypeSpec = constraint.ValueRangeConstraint(0, MAX) + + +class EnumeratedTag(univ.Sequence): + pass + +EnumeratedTag.componentType = namedtype.NamedTypes( + namedtype.NamedType('tagName', univ.ObjectIdentifier()), + namedtype.NamedType('attributeList', univ.SetOf(componentType=SecurityAttribute())) +) + + +class FreeFormField(univ.Choice): + pass + +FreeFormField.componentType = namedtype.NamedTypes( + namedtype.NamedType('bitSetAttributes', univ.BitString()), # Not permitted in RFC 7906 + namedtype.NamedType('securityAttributes', univ.SetOf(componentType=SecurityAttribute())) +) + + +class InformativeTag(univ.Sequence): + pass + +InformativeTag.componentType = namedtype.NamedTypes( + namedtype.NamedType('tagName', univ.ObjectIdentifier()), + namedtype.NamedType('attributes', FreeFormField()) +) + + +class Classification(ESSSecurityLabel): + pass + + +aa_classification = Attribute() +aa_classification['attrType'] = id_aa_KP_classification +aa_classification['attrValues'][0] = Classification() + + +# Split Identifier Attribute + +id_kma_splitID = univ.ObjectIdentifier('2.16.840.1.101.2.1.13.11') + + +class SplitID(univ.Sequence): + pass + +SplitID.componentType = namedtype.NamedTypes( + namedtype.NamedType('half', univ.Enumerated( + namedValues=namedval.NamedValues(('a', 0), ('b', 1)))), + namedtype.OptionalNamedType('combineAlg', AlgorithmIdentifier()) +) + + +aa_splitIdentifier = Attribute() +aa_splitIdentifier['attrType'] = id_kma_splitID +aa_splitIdentifier['attrValues'][0] = SplitID() + + +# Key Package Type Attribute + +id_kma_keyPkgType = univ.ObjectIdentifier('2.16.840.1.101.2.1.13.12') + + +class KeyPkgType(univ.ObjectIdentifier): + pass + + +aa_keyPackageType = Attribute() +aa_keyPackageType['attrType'] = id_kma_keyPkgType +aa_keyPackageType['attrValues'][0] = KeyPkgType() + + +# Signature Usage Attribute + +id_kma_sigUsageV3 = univ.ObjectIdentifier('2.16.840.1.101.2.1.13.22') + + +class SignatureUsage(CMSContentConstraints): + pass + + +aa_signatureUsage_v3 = Attribute() +aa_signatureUsage_v3['attrType'] = id_kma_sigUsageV3 +aa_signatureUsage_v3['attrValues'][0] = SignatureUsage() + + +# Other Certificate Format Attribute + +id_kma_otherCertFormats = univ.ObjectIdentifier('2.16.840.1.101.2.1.13.19') + + +aa_otherCertificateFormats = Attribute() +aa_signatureUsage_v3['attrType'] = id_kma_otherCertFormats +aa_signatureUsage_v3['attrValues'][0] = CertificateChoices() + + +# PKI Path Attribute + +id_at_pkiPath = univ.ObjectIdentifier('2.5.4.70') + + +class PkiPath(univ.SequenceOf): + pass + +PkiPath.componentType = Certificate() +PkiPath.subtypeSpec=constraint.ValueSizeConstraint(1, MAX) + + +aa_pkiPath = Attribute() +aa_pkiPath['attrType'] = id_at_pkiPath +aa_pkiPath['attrValues'][0] = PkiPath() + + +# Useful Certificates Attribute + +id_kma_usefulCerts = univ.ObjectIdentifier('2.16.840.1.101.2.1.13.20') + + +aa_usefulCertificates = Attribute() +aa_usefulCertificates['attrType'] = id_kma_usefulCerts +aa_usefulCertificates['attrValues'][0] = CertificateSet() + + +# Key Wrap Attribute + +id_kma_keyWrapAlgorithm = univ.ObjectIdentifier('2.16.840.1.101.2.1.13.21') + + +aa_keyWrapAlgorithm = Attribute() +aa_keyWrapAlgorithm['attrType'] = id_kma_keyWrapAlgorithm +aa_keyWrapAlgorithm['attrValues'][0] = AlgorithmIdentifier() + + +# Content Decryption Key Identifier Attribute + +id_aa_KP_contentDecryptKeyID = univ.ObjectIdentifier('2.16.840.1.101.2.1.5.66') + + +class ContentDecryptKeyID(univ.OctetString): + pass + + +aa_contentDecryptKeyIdentifier = Attribute() +aa_contentDecryptKeyIdentifier['attrType'] = id_aa_KP_contentDecryptKeyID +aa_contentDecryptKeyIdentifier['attrValues'][0] = ContentDecryptKeyID() + + +# Certificate Pointers Attribute + +aa_certificatePointers = Attribute() +aa_certificatePointers['attrType'] = id_pe_subjectInfoAccess +aa_certificatePointers['attrValues'][0] = SubjectInfoAccessSyntax() + + +# CRL Pointers Attribute + +id_aa_KP_crlPointers = univ.ObjectIdentifier('2.16.840.1.101.2.1.5.70') + + +aa_cRLDistributionPoints = Attribute() +aa_cRLDistributionPoints['attrType'] = id_aa_KP_crlPointers +aa_cRLDistributionPoints['attrValues'][0] = GeneralNames() + + +# Extended Error Codes + +id_errorCodes = univ.ObjectIdentifier('2.16.840.1.101.2.1.22') + +id_missingKeyType = univ.ObjectIdentifier('2.16.840.1.101.2.1.22.1') + +id_privacyMarkTooLong = univ.ObjectIdentifier('2.16.840.1.101.2.1.22.2') + +id_unrecognizedSecurityPolicy = univ.ObjectIdentifier('2.16.840.1.101.2.1.22.3') + + +# Map of Attribute Type OIDs to Attributes added to the +# ones that are in rfc5652.py + +_cmsAttributesMapUpdate = { + id_aa_contentHint: ContentHints(), + id_aa_communityIdentifiers: CommunityIdentifiers(), + id_aa_binarySigningTime: BinarySigningTime(), + id_contentType: ContentType(), + id_messageDigest: MessageDigest(), + id_aa_KP_keyPkgIdAndReceiptReq: KeyPkgIdentifierAndReceiptReq(), + id_aa_KP_keyProvinceV2: KeyProvinceV2(), + id_aa_KP_manifest: Manifest(), + id_kma_keyAlgorithm: KeyAlgorithm(), + id_at_userCertificate: Certificate(), + id_kma_keyPkgReceiversV2: KeyPkgReceiversV2(), + id_kma_TSECNomenclature: TSECNomenclature(), + id_kma_keyPurpose: KeyPurpose(), + id_kma_keyUse: KeyUse(), + id_kma_transportKey: TransOp(), + id_kma_keyDistPeriod: KeyDistPeriod(), + id_kma_keyValidityPeriod: KeyValidityPeriod(), + id_kma_keyDuration: KeyDuration(), + id_aa_KP_classification: Classification(), + id_kma_splitID: SplitID(), + id_kma_keyPkgType: KeyPkgType(), + id_kma_sigUsageV3: SignatureUsage(), + id_kma_otherCertFormats: CertificateChoices(), + id_at_pkiPath: PkiPath(), + id_kma_usefulCerts: CertificateSet(), + id_kma_keyWrapAlgorithm: AlgorithmIdentifier(), + id_aa_KP_contentDecryptKeyID: ContentDecryptKeyID(), + id_pe_subjectInfoAccess: SubjectInfoAccessSyntax(), + id_aa_KP_crlPointers: GeneralNames(), +} + +rfc5652.cmsAttributesMap.update(_cmsAttributesMapUpdate) diff --git a/tests/__main__.py b/tests/__main__.py index a1f7000..539f7ba 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -53,6 +53,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc7191.suite', 'tests.test_rfc7292.suite', 'tests.test_rfc7296.suite', + 'tests.test_rfc7906.suite', 'tests.test_rfc8018.suite', 'tests.test_rfc8103.suite', 'tests.test_rfc8226.suite', diff --git a/tests/test_rfc7906.py b/tests/test_rfc7906.py new file mode 100644 index 0000000..e843bdd --- /dev/null +++ b/tests/test_rfc7906.py @@ -0,0 +1,167 @@ +# +# 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 rfc2985 +from pyasn1_modules import rfc5652 +from pyasn1_modules import rfc5280 +from pyasn1_modules import rfc7906 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class AttributeSetTestCase(unittest.TestCase): + attr_set_pem_text = """\ +MYIRmDAQBglghkgBZQIBDQcxA4IBATAQBglghkgBZQIBDQ0xAwoBUzAQBglghkgB +ZQIBDQ4xAwoBAjAQBglghkgBZQIBDQ8xAwoBATARBglghkgBZQIBBUIxBAQCeQYw +EgYJYIZIAWUCAQ0LMQUwAwoBATAVBglghkgBZQIBDQUxCDAGAgReAA//MBUGCyqG +SIb3DQEJEAIuMQYCBF1qowYwGQYJYIZIAWUCAQVHMQwGCisGAQQBgaxgME0wGgYJ +YIZIAWUCAQ0BMQ0wCwYJYIZIAWUDBAEtMBoGCWCGSAFlAgENDDENBgsqhkiG9w0B +CRABGTAaBglghkgBZQIBDRUxDTALBglghkgBZQMEAS0wGwYJYIZIAWUCAQ0GMQ4w +DAIEXQAAAAIEXwAP/zAdBgsqhkiG9w0BCRACKDEOMAwGCisGAQQBgaxgMDAwLQYJ +YIZIAWUCAQVGMSAwHoYcaHR0cDovL3JlcG8uZXhhbXBsZS5jb20vcGtpLzAvBglg +hkgBZQIBDQMxIjAgExFCb2d1cyBTaG9ydCBUaXRsZYEFQm9ndXOFATCHAU0wNAYJ +YIZIAWUCAQVIMScwJRMRQm9ndXMgU2hvcnQgVGl0bGUTEEZha2UgU2hvcnQgVGl0 +bGUwOAYIKwYBBQUHAQsxLDAqMCgGCCsGAQUFBzAFhhxodHRwOi8vcmVwby5leGFt +cGxlLmNvbS9wa2kvMEEGCyqGSIb3DQEJEAIEMTIwMAwjVGhlc2UgUkZDIDc5MDYg +YXR0cmlidXRlcyBhcmUgYm9ndXMGCSqGSIb3DQEHATCBggYLKoZIhvcNAQkQAgIx +czFxAgEBBgorBgEEAYGsYAEBMUwwJIAKYIZIAWUCAQgDA4EWMBQGCisGAQQBgaxg +MEkxBgIBMAIBSTAkgApghkgBZQIBCAMEgRYwFAYKKwYBBAGBrGAwRTEGAgEwAgFF +ExJCb2d1cyBQcml2YWN5IE1hcmswgYQGCWCGSAFlAgENFjF3MHUwMAYKYIZIAWUC +AQJOAjAiMCAGCyqGSIb3DQEJEAwLMREMD2t0YS5leGFtcGxlLmNvbTAxBgsqhkiG +9w0BCRABGTAiMCAGCyqGSIb3DQEJEAwLMREMD2t0YS5leGFtcGxlLmNvbTAOBgkq +hkiG9w0BBwEKAQEwgaAGCWCGSAFlAgENEDGBkjCBj6EMBgorBgEEAYGsYDAwoH8G +CWCGSAFlAgEQAARyMHAxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJWQTEQMA4GA1UE +BxMHSGVybmRvbjEQMA4GA1UEChMHRXhhbXBsZTEOMAwGA1UEAxMFQWxpY2UxIDAe +BgkqhkiG9w0BCQEWEWFsaWNlQGV4YW1wbGUuY29tMIIBvwYJYIZIAWUCAQVBMYIB +sDCCAawEFO1lDTbJmd4voc2GDuaMzYO+XJSmMIIBkqCB/jB/BglghkgBZQIBEAAE +cjBwMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVkExEDAOBgNVBAcTB0hlcm5kb24x +EDAOBgNVBAoTB0V4YW1wbGUxDjAMBgNVBAMTBUFsaWNlMSAwHgYJKoZIhvcNAQkB +FhFhbGljZUBleGFtcGxlLmNvbTB7BglghkgBZQIBEAAEbjBsMQswCQYDVQQGEwJV +UzELMAkGA1UECBMCVkExEDAOBgNVBAcTB0hlcm5kb24xEDAOBgNVBAoTB0V4YW1w +bGUxDDAKBgNVBAMTA0JvYjEeMBwGCSqGSIb3DQEJARYPYm9iQGV4YW1wbGUuY29t +MIGOMIGLBglghkgBZQIBEAAEfjB8MQswCQYDVQQGEwJVUzELMAkGA1UECBMCVkEx +EDAOBgNVBAcTB0hlcm5kb24xGzAZBgNVBAoTElZpZ2lsIFNlY3VyaXR5IExMQzEX +MBUGA1UECxMOS2V5IE1hbmFnZW1lbnQxGDAWBgNVBAMTD2t0YS5leGFtcGxlLmNv +bTCCAoUGA1UEJDGCAnwwggJ4MIIB/qADAgECAgkApbNUKBuwbjswCgYIKoZIzj0E +AwMwPzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAlZBMRAwDgYDVQQHDAdIZXJuZG9u +MREwDwYDVQQKDAhCb2d1cyBDQTAeFw0xOTA1MjkxNDQ1NDFaFw0yMDA1MjgxNDQ1 +NDFaMHAxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJWQTEQMA4GA1UEBxMHSGVybmRv +bjEQMA4GA1UEChMHRXhhbXBsZTEOMAwGA1UEAxMFQWxpY2UxIDAeBgkqhkiG9w0B +CQEWEWFsaWNlQGV4YW1wbGUuY29tMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE+M2f +By/sRA6V1pKFqecRTE8+LuAHtZxes1wmJZrBBg+bz7uYZfYQxI3dVB0YCSD6Mt3y +XFlnmfBRwoqyArbjIBYrDbHBv2k8Csg2DhQ7qs/wto8hMKoFgkcscqIbiV7Zo4GU +MIGRMAsGA1UdDwQEAwIHgDBCBglghkgBhvhCAQ0ENRYzVGhpcyBjZXJ0aWZpY2F0 +ZSBjYW5ub3QgYmUgdHJ1c3RlZCBmb3IgYW55IHB1cnBvc2UuMB0GA1UdDgQWBBTE +uloOPnrjPIGw9AKqaLsW4JYONTAfBgNVHSMEGDAWgBTyNds0BNqlVfK9aQOZsGLs +4hUIwTAKBggqhkjOPQQDAwNoADBlAjBjuR/RNbgL3kRhmn+PJTeKaL9sh/oQgHOY +TgLmSnv3+NDCkhfKuMNoo/tHrkmihYgCMQC94MaerDIrQpi0IDh+v0QSAv9rMife +8tClafXWtDwwL8MS7oAh0ymT446Uizxx3PUwggSaBgNVBEYxggSRMIIEjTCCAgIw +ggGIoAMCAQICCQDokdYGkU/O8jAKBggqhkjOPQQDAzA/MQswCQYDVQQGEwJVUzEL +MAkGA1UECAwCVkExEDAOBgNVBAcMB0hlcm5kb24xETAPBgNVBAoMCEJvZ3VzIENB +MB4XDTE5MDUxNDA4NTgxMVoXDTIxMDUxMzA4NTgxMVowPzELMAkGA1UEBhMCVVMx +CzAJBgNVBAgMAlZBMRAwDgYDVQQHDAdIZXJuZG9uMREwDwYDVQQKDAhCb2d1cyBD +QTB2MBAGByqGSM49AgEGBSuBBAAiA2IABPBRdlSx6I5qpZ2sKUMIxun1gUAzzstO +YWKvKCnMoNT1x+pIKDvMEMimFcLAxxL3NVYOhK0Jty83SPDkKWMdx9/Okdhf3U/z +xJlEnXDiFrAeM6xbG8zcCRiBnmd92UvsRqNQME4wHQYDVR0OBBYEFPI12zQE2qVV +8r1pA5mwYuziFQjBMB8GA1UdIwQYMBaAFPI12zQE2qVV8r1pA5mwYuziFQjBMAwG +A1UdEwQFMAMBAf8wCgYIKoZIzj0EAwMDaAAwZQIwWlGNjb9NyqJSzUSdsEqDSvMZ +b8yFkxYCIbAVqQ9UqScUUb9tpJKGsPWwbZsnLVvmAjEAt/ypozbUhQw4dSPpWzrn +5BQ0kKbDM3DQJcBABEUBoIOol1/jYQPmxajQuxcheFlkMIICgzCCAgqgAwIBAgIJ +AKWzVCgbsG49MAoGCCqGSM49BAMDMD8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJW +QTEQMA4GA1UEBwwHSGVybmRvbjERMA8GA1UECgwIQm9ndXMgQ0EwHhcNMTkwNjEy +MTQzMTA0WhcNMjAwNjExMTQzMTA0WjB8MQswCQYDVQQGEwJVUzELMAkGA1UECBMC +VkExEDAOBgNVBAcTB0hlcm5kb24xGzAZBgNVBAoTElZpZ2lsIFNlY3VyaXR5IExM +QzEXMBUGA1UECxMOS2V5IE1hbmFnZW1lbnQxGDAWBgNVBAMTD2t0YS5leGFtcGxl +LmNvbTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJf2XsTdVLcYASKJGtWjOAIFB8sX +rsiE7G1tC+IP+iOEdJCZ+UvJ9Enx7v6dtaU4uy1FzuWCar45BVpKVK2TNWT8E7XA +TkGBTIXGN76yJ5S09FdWp+hVkIkmyCJJujXzV6OBlDCBkTALBgNVHQ8EBAMCB4Aw +QgYJYIZIAYb4QgENBDUWM1RoaXMgY2VydGlmaWNhdGUgY2Fubm90IGJlIHRydXN0 +ZWQgZm9yIGFueSBwdXJwb3NlLjAdBgNVHQ4EFgQUbZtc/QOvtbnVi/FknxpW4LWt +TQ8wHwYDVR0jBBgwFoAU8jXbNATapVXyvWkDmbBi7OIVCMEwCgYIKoZIzj0EAwMD +ZwAwZAIwBniWpO11toMsV8fLBpBjA5YGQvd3TAcSw1lNbWpArL+hje1dzQ7pxsln +kklv3CTxAjBuVebz4mN0Qkew2NK/itwlmi7i+QxPs/MSZ7YFsyTA5Z4h2GbLW+zN +3xNCC91vfpcwggSgBglghkgBZQIBDRQxggSRMYIEjTCCAgIwggGIoAMCAQICCQDo +kdYGkU/O8jAKBggqhkjOPQQDAzA/MQswCQYDVQQGEwJVUzELMAkGA1UECAwCVkEx +EDAOBgNVBAcMB0hlcm5kb24xETAPBgNVBAoMCEJvZ3VzIENBMB4XDTE5MDUxNDA4 +NTgxMVoXDTIxMDUxMzA4NTgxMVowPzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAlZB +MRAwDgYDVQQHDAdIZXJuZG9uMREwDwYDVQQKDAhCb2d1cyBDQTB2MBAGByqGSM49 +AgEGBSuBBAAiA2IABPBRdlSx6I5qpZ2sKUMIxun1gUAzzstOYWKvKCnMoNT1x+pI +KDvMEMimFcLAxxL3NVYOhK0Jty83SPDkKWMdx9/Okdhf3U/zxJlEnXDiFrAeM6xb +G8zcCRiBnmd92UvsRqNQME4wHQYDVR0OBBYEFPI12zQE2qVV8r1pA5mwYuziFQjB +MB8GA1UdIwQYMBaAFPI12zQE2qVV8r1pA5mwYuziFQjBMAwGA1UdEwQFMAMBAf8w +CgYIKoZIzj0EAwMDaAAwZQIwWlGNjb9NyqJSzUSdsEqDSvMZb8yFkxYCIbAVqQ9U +qScUUb9tpJKGsPWwbZsnLVvmAjEAt/ypozbUhQw4dSPpWzrn5BQ0kKbDM3DQJcBA +BEUBoIOol1/jYQPmxajQuxcheFlkMIICgzCCAgqgAwIBAgIJAKWzVCgbsG49MAoG +CCqGSM49BAMDMD8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJWQTEQMA4GA1UEBwwH +SGVybmRvbjERMA8GA1UECgwIQm9ndXMgQ0EwHhcNMTkwNjEyMTQzMTA0WhcNMjAw +NjExMTQzMTA0WjB8MQswCQYDVQQGEwJVUzELMAkGA1UECBMCVkExEDAOBgNVBAcT +B0hlcm5kb24xGzAZBgNVBAoTElZpZ2lsIFNlY3VyaXR5IExMQzEXMBUGA1UECxMO +S2V5IE1hbmFnZW1lbnQxGDAWBgNVBAMTD2t0YS5leGFtcGxlLmNvbTB2MBAGByqG +SM49AgEGBSuBBAAiA2IABJf2XsTdVLcYASKJGtWjOAIFB8sXrsiE7G1tC+IP+iOE +dJCZ+UvJ9Enx7v6dtaU4uy1FzuWCar45BVpKVK2TNWT8E7XATkGBTIXGN76yJ5S0 +9FdWp+hVkIkmyCJJujXzV6OBlDCBkTALBgNVHQ8EBAMCB4AwQgYJYIZIAYb4QgEN +BDUWM1RoaXMgY2VydGlmaWNhdGUgY2Fubm90IGJlIHRydXN0ZWQgZm9yIGFueSBw +dXJwb3NlLjAdBgNVHQ4EFgQUbZtc/QOvtbnVi/FknxpW4LWtTQ8wHwYDVR0jBBgw +FoAU8jXbNATapVXyvWkDmbBi7OIVCMEwCgYIKoZIzj0EAwMDZwAwZAIwBniWpO11 +toMsV8fLBpBjA5YGQvd3TAcSw1lNbWpArL+hje1dzQ7pxslnkklv3CTxAjBuVebz +4mN0Qkew2NK/itwlmi7i+QxPs/MSZ7YFsyTA5Z4h2GbLW+zN3xNCC91vfpc= +""" + + def setUp(self): + self.asn1Spec = rfc2985.AttributeSet() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.attr_set_pem_text) + asn1Object, rest = der_decode (substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + for attr in asn1Object: + assert attr['type'] in rfc5652.cmsAttributesMap.keys() + av, rest = der_decode(attr['values'][0], + asn1Spec=rfc5652.cmsAttributesMap[attr['type']]) + assert not rest + assert av.prettyPrint() + assert der_encode(av) == attr['values'][0] + + if attr['type'] == rfc7906.id_aa_KP_contentDecryptKeyID: + assert av == univ.OctetString(hexValue='7906') + + def testOpenTypes(self): + substrate = pem.readBase64fromText(self.attr_set_pem_text) + rfc5280.certificateAttributesMap.update(rfc5652.cmsAttributesMap) + asn1Object, rest = der_decode (substrate, + asn1Spec=self.asn1Spec, decodeOpenTypes=True) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + for attr in asn1Object: + if attr['type'] == rfc7906.id_aa_KP_contentDecryptKeyID: + assert attr['values'][0] == univ.OctetString(hexValue='7906') + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + import sys + + result = unittest.TextTestRunner(verbosity=2).run(suite) + sys.exit(not result.wasSuccessful()) -- cgit v1.2.3 From 15bc77911a8394c1814456947d6f0ddd85b0e82e Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Sun, 8 Sep 2019 09:41:53 -0400 Subject: Add support for RFC 3894 and openTypes map cleanup (#62) --- CHANGES.txt | 5 +++ pyasn1_modules/rfc2634.py | 17 +++++---- pyasn1_modules/rfc2985.py | 8 ++--- pyasn1_modules/rfc3274.py | 10 +++--- pyasn1_modules/rfc3779.py | 10 ++++-- pyasn1_modules/rfc4073.py | 10 +++--- pyasn1_modules/rfc4108.py | 23 +++++++----- pyasn1_modules/rfc5035.py | 19 +++++----- pyasn1_modules/rfc5083.py | 10 +++--- pyasn1_modules/rfc5084.py | 17 +++++++++ pyasn1_modules/rfc5480.py | 10 ++++-- pyasn1_modules/rfc5940.py | 9 +++-- pyasn1_modules/rfc5958.py | 9 +++-- pyasn1_modules/rfc6019.py | 10 ++++-- pyasn1_modules/rfc6402.py | 3 +- pyasn1_modules/rfc7894.py | 92 +++++++++++++++++++++++++++++++++++++++++++++++ pyasn1_modules/rfc8520.py | 23 +++++++----- tests/__main__.py | 1 + tests/test_rfc2634.py | 13 ++++--- tests/test_rfc2985.py | 36 ++++++++++--------- tests/test_rfc2986.py | 16 ++++----- tests/test_rfc3274.py | 6 ++-- tests/test_rfc3770.py | 5 ++- tests/test_rfc3779.py | 1 - tests/test_rfc4073.py | 10 ++---- tests/test_rfc4108.py | 9 ++--- tests/test_rfc5035.py | 12 +++---- tests/test_rfc5083.py | 20 +++++------ tests/test_rfc5084.py | 78 ++++++++++++++++++++++++++++++++++++---- tests/test_rfc5280.py | 74 +++++++++++++++++++------------------- tests/test_rfc5480.py | 20 +++++------ tests/test_rfc5652.py | 21 +++++------ tests/test_rfc5940.py | 10 +++--- tests/test_rfc5958.py | 16 ++++----- tests/test_rfc6019.py | 2 -- tests/test_rfc6402.py | 31 ++++++++-------- tests/test_rfc7030.py | 8 +++-- tests/test_rfc7894.py | 86 ++++++++++++++++++++++++++++++++++++++++++++ tests/test_rfc7906.py | 11 ++++-- tests/test_rfc8520.py | 1 - 40 files changed, 531 insertions(+), 241 deletions(-) create mode 100644 pyasn1_modules/rfc7894.py create mode 100644 tests/test_rfc7894.py diff --git a/CHANGES.txt b/CHANGES.txt index 0a4778b..5dba621 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -21,6 +21,11 @@ Revision 0.2.7, released XX-08-2019 - Added RFC8449 providing Certificate Extension for Hash Of Root Key - Updated RFC2459 and RFC5280 for TODO in the certificate extension map - Added RFC7906 providing NSA's CMS Key Management Attributes +- Added RFC7894 providing EST Alternative Challenge Password Attributes +- Updated the handling of maps for use with openType so that just doing + 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 Revision 0.2.6, released 31-07-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc2634.py b/pyasn1_modules/rfc2634.py index 4b4e5bb..2099a4b 100644 --- a/pyasn1_modules/rfc2634.py +++ b/pyasn1_modules/rfc2634.py @@ -308,9 +308,10 @@ Receipt.componentType = namedtype.NamedTypes( ) -# Map of Attribute Type to the Attribute structure +# Map of Attribute Type to the Attribute structure is added to the +# ones that are in rfc5652.py -ESSAttributeMap = { +_cmsAttributesMapUpdate = { id_aa_signingCertificate: SigningCertificate(), id_aa_mlExpandHistory: MLExpansionHistory(), id_aa_securityLabel: ESSSecurityLabel(), @@ -322,10 +323,14 @@ ESSAttributeMap = { id_aa_receiptRequest: ReceiptRequest(), } +rfc5652.cmsAttributesMap.update(_cmsAttributesMapUpdate) -# Map of Content Type OIDs to Content Types -# To be added to the ones that are in rfc5652.py -cmsContentTypesMapUpdate = { +# Map of Content Type OIDs to Content Types is added to the +# ones that are in rfc5652.py + +_cmsContentTypesMapUpdate = { id_ct_receipt: Receipt(), -} \ No newline at end of file +} + +rfc5652.cmsContentTypesMap.update(_cmsContentTypesMapUpdate) diff --git a/pyasn1_modules/rfc2985.py b/pyasn1_modules/rfc2985.py index 591d27d..c8ebb0c 100644 --- a/pyasn1_modules/rfc2985.py +++ b/pyasn1_modules/rfc2985.py @@ -536,7 +536,7 @@ smimeCapabilities['attrValues'][0] = SMIMECapabilities() # Certificate Attribute Map -certificateAttributesMapUpdate = { +_certificateAttributesMapUpdate = { # Attribute types for use with the "pkcsEntity" object class pkcs_9_at_pkcs7PDU: ContentInfo(), pkcs_9_at_userPKCS12: PFX(), @@ -560,12 +560,12 @@ certificateAttributesMapUpdate = { pkcs_9_at_extendedCertificateAttributes: AttributeSet(), } -rfc5280.certificateAttributesMap.update(certificateAttributesMapUpdate) +rfc5280.certificateAttributesMap.update(_certificateAttributesMapUpdate) # CMS Attribute Map -cmsAttributesMapUpdate = { +_cmsAttributesMapUpdate = { # Attribute types for use in PKCS #7 data (a.k.a. CMS) pkcs_9_at_contentType: ContentType(), pkcs_9_at_messageDigest: MessageDigest(), @@ -580,4 +580,4 @@ cmsAttributesMapUpdate = { pkcs_9_at_smimeCapabilities: SMIMECapabilities(), } -rfc5652.cmsAttributesMap.update(cmsAttributesMapUpdate) +rfc5652.cmsAttributesMap.update(_cmsAttributesMapUpdate) diff --git a/pyasn1_modules/rfc3274.py b/pyasn1_modules/rfc3274.py index 8d8541c..425e006 100644 --- a/pyasn1_modules/rfc3274.py +++ b/pyasn1_modules/rfc3274.py @@ -49,9 +49,11 @@ cpa_zlibCompress['algorithm'] = id_alg_zlibCompress # cpa_zlibCompress['parameters'] are absent -# Map of Content Type OIDs to Content Types -# To be added to the ones that are in rfc5652.py +# Map of Content Type OIDs to Content Types is added to thr +# ones that are in rfc5652.py -cmsContentTypesMapUpdate = { +_cmsContentTypesMapUpdate = { id_ct_compressedData: CompressedData(), -} \ No newline at end of file +} + +rfc5652.cmsContentTypesMap.update(_cmsContentTypesMapUpdate) diff --git a/pyasn1_modules/rfc3779.py b/pyasn1_modules/rfc3779.py index 125351e..8e6eaa3 100644 --- a/pyasn1_modules/rfc3779.py +++ b/pyasn1_modules/rfc3779.py @@ -18,6 +18,8 @@ from pyasn1.type import namedtype from pyasn1.type import tag from pyasn1.type import univ +from pyasn1_modules import rfc5280 + # IP Address Delegation Extension @@ -124,10 +126,12 @@ ASIdentifiers.componentType = namedtype.NamedTypes( ) -# Map of Certificate Extension OIDs to Extensions -# To be added to the ones that are in rfc5280.py +# Map of Certificate Extension OIDs to Extensions is added to the +# ones that are in rfc5280.py -certificateExtensionsMapUpdate = { +_certificateExtensionsMapUpdate = { id_pe_ipAddrBlocks: IPAddrBlocks(), id_pe_autonomousSysIds: ASIdentifiers(), } + +rfc5280.certificateExtensionsMap.update(_certificateExtensionsMapUpdate) diff --git a/pyasn1_modules/rfc4073.py b/pyasn1_modules/rfc4073.py index 12a9ec0..3f425b2 100644 --- a/pyasn1_modules/rfc4073.py +++ b/pyasn1_modules/rfc4073.py @@ -48,10 +48,12 @@ ContentWithAttributes.componentType = namedtype.NamedTypes( ) -# Map of Content Type OIDs to Content Types -# To be added to the ones that are in rfc5652.py +# Map of Content Type OIDs to Content Types is added to the +# ones that are in rfc5652.py -cmsContentTypesMapUpdate = { +_cmsContentTypesMapUpdate = { id_ct_contentCollection: ContentCollection(), id_ct_contentWithAttrs: ContentWithAttributes(), -} \ No newline at end of file +} + +rfc5652.cmsContentTypesMap.update(_cmsContentTypesMapUpdate) diff --git a/pyasn1_modules/rfc4108.py b/pyasn1_modules/rfc4108.py index c5cdfe9..ecace9e 100644 --- a/pyasn1_modules/rfc4108.py +++ b/pyasn1_modules/rfc4108.py @@ -310,10 +310,10 @@ HardwareModuleName.componentType = namedtype.NamedTypes( ) -# Map of Attribute Type OIDs to Attributes -# To be added to the ones that are in rfc5652.py +# Map of Attribute Type OIDs to Attributes is added to the +# ones that are in rfc5652.py -cmsAttributesMapUpdate = { +_cmsAttributesMapUpdate = { id_aa_wrappedFirmwareKey: WrappedFirmwareKey(), id_aa_firmwarePackageInfo: FirmwarePackageInfo(), id_aa_communityIdentifiers: CommunityIdentifiers(), @@ -325,21 +325,26 @@ cmsAttributesMapUpdate = { id_aa_fwPkgMessageDigest: FirmwarePackageMessageDigest(), } +rfc5652.cmsAttributesMap.update(_cmsAttributesMapUpdate) -# Map of Content Type OIDs to Content Types -# To be added to the ones that are in rfc5652.py -cmsContentTypesMapUpdate = { +# Map of Content Type OIDs to Content Types is added to the +# ones that are in rfc5652.py + +_cmsContentTypesMapUpdate = { id_ct_firmwareLoadError: FirmwarePackageLoadError(), id_ct_firmwareLoadReceipt: FirmwarePackageLoadReceipt(), id_ct_firmwarePackage: FirmwarePkgData(), } +rfc5652.cmsContentTypesMap.update(_cmsContentTypesMapUpdate) + -# Map of Other Name OIDs to Other Name -# To be added to the ones that are in rfc5280.py +# Map of Other Name OIDs to Other Name is added to the +# ones that are in rfc5280.py -anotherNameMapUpdate = { +_anotherNameMapUpdate = { id_on_hardwareModuleName: HardwareModuleName(), } +rfc5280.anotherNameMap.update(_anotherNameMapUpdate) diff --git a/pyasn1_modules/rfc5035.py b/pyasn1_modules/rfc5035.py index 4a70bb0..1cec982 100644 --- a/pyasn1_modules/rfc5035.py +++ b/pyasn1_modules/rfc5035.py @@ -179,20 +179,21 @@ ub_receiptsTo = rfc2634.ub_receiptsTo ReceiptRequest = rfc2634.ReceiptRequest -# Map of Attribute Type to the Attribute structure +# Map of Attribute Type to the Attribute structure is added to the +# ones that are in rfc5652.py -ESSAttributeMap = rfc2634.ESSAttributeMap - -_ESSAttributeMapAddition = { +_cmsAttributesMapUpdate = { id_aa_signingCertificateV2: SigningCertificateV2(), } -ESSAttributeMap.update(_ESSAttributeMapAddition) +rfc5652.cmsAttributesMap.update(_cmsAttributesMapUpdate) -# Map of Content Type OIDs to Content Types -# To be added to the ones that are in rfc5652.py +# Map of Content Type OIDs to Content Types is added to the +# ones that are in rfc5652.py -cmsContentTypesMapUpdate = { +_cmsContentTypesMapUpdate = { id_ct_receipt: Receipt(), -} \ No newline at end of file +} + +rfc5652.cmsContentTypesMap.update(_cmsContentTypesMapUpdate) diff --git a/pyasn1_modules/rfc5083.py b/pyasn1_modules/rfc5083.py index e3df086..26ef550 100644 --- a/pyasn1_modules/rfc5083.py +++ b/pyasn1_modules/rfc5083.py @@ -42,9 +42,11 @@ AuthEnvelopedData.componentType = namedtype.NamedTypes( ) -# Map of Content Type OIDs to Content Types -# To be added to the ones that are in rfc5652.py +# Map of Content Type OIDs to Content Types is added to the +# ones that are in rfc5652.py -cmsContentTypesMapUpdate = { +_cmsContentTypesMapUpdate = { id_ct_authEnvelopedData: AuthEnvelopedData(), -} \ No newline at end of file +} + +rfc5652.cmsContentTypesMap.update(_cmsContentTypesMapUpdate) diff --git a/pyasn1_modules/rfc5084.py b/pyasn1_modules/rfc5084.py index 18abec8..7686839 100644 --- a/pyasn1_modules/rfc5084.py +++ b/pyasn1_modules/rfc5084.py @@ -16,6 +16,8 @@ from pyasn1.type import constraint from pyasn1.type import namedtype from pyasn1.type import univ +from pyasn1_modules import rfc5280 + def _OID(*components): output = [] @@ -78,3 +80,18 @@ id_aes192_GCM = _OID(aes, 26) id_aes256_CCM = _OID(aes, 47) id_aes256_GCM = _OID(aes, 46) + + +# Map of Algorithm Identifier OIDs to Parameters is added to the +# ones in rfc5280.py + +_algorithmIdentifierMapUpdate = { + id_aes128_CCM: CCMParameters(), + id_aes128_GCM: GCMParameters(), + id_aes192_CCM: CCMParameters(), + id_aes192_GCM: GCMParameters(), + id_aes256_CCM: CCMParameters(), + id_aes256_GCM: GCMParameters(), +} + +rfc5280.algorithmIdentifierMap.update(_algorithmIdentifierMapUpdate) diff --git a/pyasn1_modules/rfc5480.py b/pyasn1_modules/rfc5480.py index 0113826..84c0c11 100644 --- a/pyasn1_modules/rfc5480.py +++ b/pyasn1_modules/rfc5480.py @@ -18,6 +18,7 @@ from pyasn1.type import namedtype from pyasn1.type import univ from pyasn1_modules import rfc3279 +from pyasn1_modules import rfc5280 # These structures are the same as RFC 3279. @@ -170,7 +171,7 @@ sect571r1 = univ.ObjectIdentifier('1.3.132.0.39') # Map of Algorithm Identifier OIDs to Parameters # The algorithm is not included if the parameters MUST be absent -algorithmIdentifierMapUpdate = { +_algorithmIdentifierMapUpdate = { rsaEncryption: univ.Null(), md2WithRSAEncryption: univ.Null(), md5WithRSAEncryption: univ.Null(), @@ -181,4 +182,9 @@ algorithmIdentifierMapUpdate = { id_ecPublicKey: ECParameters(), id_ecDH: ECParameters(), id_ecMQV: ECParameters(), -} \ No newline at end of file +} + + +# Add these Algorithm Identifier map entries to the ones in rfc5280.py + +rfc5280.algorithmIdentifierMap.update(_algorithmIdentifierMapUpdate) diff --git a/pyasn1_modules/rfc5940.py b/pyasn1_modules/rfc5940.py index c5ae0c8..e105923 100644 --- a/pyasn1_modules/rfc5940.py +++ b/pyasn1_modules/rfc5940.py @@ -42,15 +42,18 @@ class SCVPReqRes(univ.Sequence): pass SCVPReqRes.componentType = namedtype.NamedTypes( - namedtype.OptionalNamedType('request', ContentInfo().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.OptionalNamedType('request', + ContentInfo().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), namedtype.NamedType('response', ContentInfo()) ) # Map of Revocation Info Format OIDs to Revocation Info Format -# To be added to the ones that are in rfc5652.py +# is added to the ones that are in rfc5652.py -otherRevInfoFormatMapUpdate = { +_otherRevInfoFormatMapUpdate = { id_ri_ocsp_response: OCSPResponse(), id_ri_scvp: SCVPReqRes(), } + +rfc5652.otherRevInfoFormatMap.update(_otherRevInfoFormatMapUpdate) diff --git a/pyasn1_modules/rfc5958.py b/pyasn1_modules/rfc5958.py index d8c7c90..82c6556 100644 --- a/pyasn1_modules/rfc5958.py +++ b/pyasn1_modules/rfc5958.py @@ -16,6 +16,7 @@ from pyasn1.type import univ, constraint, namedtype, namedval, tag from pyasn1_modules import rfc5280 +from pyasn1_modules import rfc5652 MAX = float('inf') @@ -87,9 +88,11 @@ AsymmetricKeyPackage.componentType = OneAsymmetricKey() AsymmetricKeyPackage.sizeSpec=constraint.ValueSizeConstraint(1, MAX) -# Map of Content Type OIDs to Content Types -# To be added to the ones that are in rfc5652.py +# Map of Content Type OIDs to Content Types is added to the +# ones that are in rfc5652.py -cmsContentTypesMapUpdate = { +_cmsContentTypesMapUpdate = { id_ct_KP_aKeyPackage: AsymmetricKeyPackage(), } + +rfc5652.cmsContentTypesMap.update(_cmsContentTypesMapUpdate) diff --git a/pyasn1_modules/rfc6019.py b/pyasn1_modules/rfc6019.py index 7816593..c6872c7 100644 --- a/pyasn1_modules/rfc6019.py +++ b/pyasn1_modules/rfc6019.py @@ -14,6 +14,8 @@ from pyasn1.type import constraint from pyasn1.type import univ +from pyasn1_modules import rfc5652 + MAX = float('inf') @@ -33,9 +35,11 @@ class BinarySigningTime(BinaryTime): pass -# Map of Attribute Type OIDs to Attributes -# To be added to the ones that are in rfc5652.py +# Map of Attribute Type OIDs to Attributes ia added to the +# ones that are in rfc5652.py -cmsAttributesMapUpdate = { +_cmsAttributesMapUpdate = { id_aa_binarySigningTime: BinarySigningTime(), } + +rfc5652.cmsAttributesMap.update(_cmsAttributesMapUpdate) diff --git a/pyasn1_modules/rfc6402.py b/pyasn1_modules/rfc6402.py index 322e35e..b5f0d48 100644 --- a/pyasn1_modules/rfc6402.py +++ b/pyasn1_modules/rfc6402.py @@ -41,7 +41,8 @@ def _buildOid(*components): return univ.ObjectIdentifier(output) -cmcControlAttributesMap = { } +# Since CMS Attributes and CMC Controls both use 'attrType', one map is used +cmcControlAttributesMap = rfc5652.cmsAttributesMap class ChangeSubjectName(univ.Sequence): diff --git a/pyasn1_modules/rfc7894.py b/pyasn1_modules/rfc7894.py new file mode 100644 index 0000000..4193643 --- /dev/null +++ b/pyasn1_modules/rfc7894.py @@ -0,0 +1,92 @@ +# +# 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 +# +# Alternative Challenge Password Attributes for EST +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc7894.txt +# + +from pyasn1.type import char +from pyasn1.type import constraint +from pyasn1.type import namedtype +from pyasn1.type import univ + +from pyasn1_modules import rfc5652 +from pyasn1_modules import rfc6402 +from pyasn1_modules import rfc7191 + + +# SingleAttribute is the same as Attribute in RFC 5652, except that the +# attrValues SET must have one and only one member + +Attribute = rfc7191.SingleAttribute + + +# DirectoryString is the same as RFC 5280, except the length is limited to 255 + +class DirectoryString(univ.Choice): + pass + +DirectoryString.componentType = namedtype.NamedTypes( + namedtype.NamedType('teletexString', char.TeletexString().subtype( + subtypeSpec=constraint.ValueSizeConstraint(1, 255))), + namedtype.NamedType('printableString', char.PrintableString().subtype( + subtypeSpec=constraint.ValueSizeConstraint(1, 255))), + namedtype.NamedType('universalString', char.UniversalString().subtype( + subtypeSpec=constraint.ValueSizeConstraint(1, 255))), + namedtype.NamedType('utf8String', char.UTF8String().subtype( + subtypeSpec=constraint.ValueSizeConstraint(1, 255))), + namedtype.NamedType('bmpString', char.BMPString().subtype( + subtypeSpec=constraint.ValueSizeConstraint(1, 255))) +) + + +# OTP Challenge Attribute + +id_aa_otpChallenge = univ.ObjectIdentifier('1.2.840.113549.1.9.16.2.56') + +ub_aa_otpChallenge = univ.Integer(255) + +otpChallenge = Attribute() +otpChallenge['attrType'] = id_aa_otpChallenge +otpChallenge['attrValues'][0] = DirectoryString() + + +# Revocation Challenge Attribute + +id_aa_revocationChallenge = univ.ObjectIdentifier('1.2.840.113549.1.9.16.2.57') + +ub_aa_revocationChallenge = univ.Integer(255) + +revocationChallenge = Attribute() +revocationChallenge['attrType'] = id_aa_revocationChallenge +revocationChallenge['attrValues'][0] = DirectoryString() + + +# EST Identity Linking Attribute + +id_aa_estIdentityLinking = univ.ObjectIdentifier('1.2.840.113549.1.9.16.2.58') + +ub_aa_est_identity_linking = univ.Integer(255) + +estIdentityLinking = Attribute() +estIdentityLinking['attrType'] = id_aa_estIdentityLinking +estIdentityLinking['attrValues'][0] = DirectoryString() + + +# Map of Attribute Type OIDs to Attributes added to the +# ones that are in rfc6402.py + +_cmcControlAttributesMapUpdate = { + id_aa_otpChallenge: DirectoryString(), + id_aa_revocationChallenge: DirectoryString(), + id_aa_estIdentityLinking: DirectoryString(), +} + +rfc6402.cmcControlAttributesMap.update(_cmcControlAttributesMapUpdate) diff --git a/pyasn1_modules/rfc8520.py b/pyasn1_modules/rfc8520.py index 8e4adf3..b9eb6e9 100644 --- a/pyasn1_modules/rfc8520.py +++ b/pyasn1_modules/rfc8520.py @@ -14,8 +14,11 @@ # https://www.rfc-editor.org/rfc/rfc8520.txt # -from pyasn1.type import univ, char, namedtype, namedval, tag, constraint, useful +from pyasn1.type import char +from pyasn1.type import univ + from pyasn1_modules import rfc5280 +from pyasn1_modules import rfc5652 # X.509 Extension for MUD URL @@ -39,18 +42,22 @@ class MUDsignerSyntax(rfc5280.Name): id_ct_mudtype = univ.ObjectIdentifier('1.2.840.113549.1.9.16.1.41') -# Map of Certificate Extension OIDs to Extensions -# To be added to the ones that are in rfc5280.py +# Map of Certificate Extension OIDs to Extensions added to the +# ones that are in rfc5280.py -certificateExtensionsMapUpdate = { +_certificateExtensionsMapUpdate = { id_pe_mud_url: MUDURLSyntax(), id_pe_mudsigner: MUDsignerSyntax(), } +rfc5280.certificateExtensionsMap.update(_certificateExtensionsMapUpdate) + -# Map of Content Type OIDs to Content Types -# To be added to the ones that are in rfc5652.py +# Map of Content Type OIDs to Content Types added to the +# ones that are in rfc5652.py -cmsContentTypesMapUpdate = { +_cmsContentTypesMapUpdate = { id_ct_mudtype: univ.OctetString(), -} \ No newline at end of file +} + +rfc5652.cmsContentTypesMap.update(_cmsContentTypesMapUpdate) diff --git a/tests/__main__.py b/tests/__main__.py index 539f7ba..fd8e71d 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -53,6 +53,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc7191.suite', 'tests.test_rfc7292.suite', 'tests.test_rfc7296.suite', + 'tests.test_rfc7894.suite', 'tests.test_rfc7906.suite', 'tests.test_rfc8018.suite', 'tests.test_rfc8103.suite', diff --git a/tests/test_rfc2634.py b/tests/test_rfc2634.py index d57315b..18c8a3d 100644 --- a/tests/test_rfc2634.py +++ b/tests/test_rfc2634.py @@ -73,8 +73,8 @@ mNTr0mjYeUWRe/15IsWNx+kuFcLDr71DFHvMFY5M3sdfMA== sat = sa['attrType'] sav0 = sa['attrValues'][0] - if sat in rfc2634.ESSAttributeMap.keys(): - sav, rest = der_decode(sav0, asn1Spec=rfc2634.ESSAttributeMap[sat]) + if 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 @@ -139,18 +139,17 @@ lropBdPJ6jIXiZQgCwxbGTCwCMQClaQ9K+L5LTeuW50ZKSIbmBZQ5dxjtnK3OlS sat = sa['attrType'] sav0 = sa['attrValues'][0] - if sat in rfc2634.ESSAttributeMap.keys(): - sav, rest = der_decode(sav0, asn1Spec=rfc2634.ESSAttributeMap[sat]) + if 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 def testOpenTypes(self): substrate = pem.readBase64fromText(self.signed_receipt_pem_text) - rfc5652.cmsContentTypesMap.update(rfc2634.cmsContentTypesMapUpdate) - rfc5652.cmsAttributesMap.update(rfc2634.ESSAttributeMap) asn1Object, rest = der_decode(substrate, - asn1Spec=self.asn1Spec, decodeOpenTypes=True) + asn1Spec=self.asn1Spec, + decodeOpenTypes=True) assert not rest assert asn1Object.prettyPrint() assert der_encode(asn1Object) == substrate diff --git a/tests/test_rfc2985.py b/tests/test_rfc2985.py index 31110a8..466d50b 100644 --- a/tests/test_rfc2985.py +++ b/tests/test_rfc2985.py @@ -129,13 +129,14 @@ HktMK+isIjxOTk4yJTOOAgIH0A== assert asn1Object.prettyPrint() assert der_encode(asn1Object) == substrate - rfc5280.certificateAttributesMap.update(rfc2985.certificateAttributesMapUpdate) - rfc5280.certificateAttributesMap.update(rfc2985.cmsAttributesMapUpdate) + openTypesMap = { } + openTypesMap.update(rfc5280.certificateAttributesMap) + openTypesMap.update(rfc5652.cmsAttributesMap) for attr in asn1Object: - assert attr['type'] in rfc5280.certificateAttributesMap + assert attr['type'] in openTypesMap.keys() av, rest = der_decode(attr['values'][0], - asn1Spec=rfc5280.certificateAttributesMap[attr['type']]) + asn1Spec=openTypesMap[attr['type']]) assert not rest assert av.prettyPrint() assert der_encode(av) == attr['values'][0] @@ -166,9 +167,9 @@ HktMK+isIjxOTk4yJTOOAgIH0A== assert not rest for bagattr in sb['bagAttributes']: - if bagattr['attrType'] in rfc5280.certificateAttributesMap: + if bagattr['attrType'] in openTypesMap: inav, rest = der_decode(bagattr['attrValues'][0], - asn1Spec=rfc5280.certificateAttributesMap[bagattr['attrType']]) + asn1Spec=openTypesMap[bagattr['attrType']]) assert not rest if bagattr['attrType'] == rfc2985.pkcs_9_at_friendlyName: @@ -192,9 +193,9 @@ HktMK+isIjxOTk4yJTOOAgIH0A== assert si['version'] == 1 for siattr in si['signedAttrs']: - if siattr['attrType'] in rfc5280.certificateAttributesMap: + if siattr['attrType'] in openTypesMap: siav, rest = der_decode(siattr['attrValues'][0], - asn1Spec=rfc5280.certificateAttributesMap[siattr['attrType']]) + asn1Spec=openTypesMap[siattr['attrType']]) assert not rest if siattr['attrType'] == rfc2985.pkcs_9_at_contentType: @@ -208,28 +209,30 @@ HktMK+isIjxOTk4yJTOOAgIH0A== for choices in sd['certificates']: for rdn in choices[0]['tbsCertificate']['subject']['rdnSequence']: - if rdn[0]['type'] in rfc5280.certificateAttributesMap: + if rdn[0]['type'] in openTypesMap: nv, rest = der_decode(rdn[0]['value'], - asn1Spec=rfc5280.certificateAttributesMap[rdn[0]['type']]) + asn1Spec=openTypesMap[rdn[0]['type']]) assert not rest if rdn[0]['type'] == rfc2985.pkcs_9_at_emailAddress: assert nv == 'alice@example.com' def testOpenTypes(self): - rfc5280.certificateAttributesMap.update(rfc2985.certificateAttributesMapUpdate) - rfc5280.certificateAttributesMap.update(rfc2985.cmsAttributesMapUpdate) + openTypesMap = { } + openTypesMap.update(rfc5280.certificateAttributesMap) + openTypesMap.update(rfc5652.cmsAttributesMap) substrate = pem.readBase64fromText(self.pem_text) asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec, + openTypes=openTypesMap, decodeOpenTypes=True) assert not rest assert asn1Object.prettyPrint() assert der_encode(asn1Object) == substrate for attr in asn1Object: - assert attr['type'] in rfc5280.certificateAttributesMap + assert attr['type'] in openTypesMap.keys() if attr['type'] == rfc2985.pkcs_9_at_userPKCS12: assert attr['values'][0]['version'] == univ.Integer(3) @@ -252,7 +255,7 @@ HktMK+isIjxOTk4yJTOOAgIH0A== for sb in sc: if sb['bagId'] in rfc7292.pkcs12BagTypeMap: for bagattr in sb['bagAttributes']: - if bagattr['attrType'] in rfc5280.certificateAttributesMap: + if bagattr['attrType'] in openTypesMap: if bagattr['attrType'] == rfc2985.pkcs_9_at_friendlyName: assert bagattr['attrValues'][0] == "3f71af65-1687-444a-9f46-c8be194c3e8e" @@ -268,7 +271,7 @@ HktMK+isIjxOTk4yJTOOAgIH0A== assert si['version'] == 1 for siattr in si['signedAttrs']: - if siattr['attrType'] in rfc5280.certificateAttributesMap: + if siattr['attrType'] in openTypesMap: if siattr['attrType'] == rfc2985.pkcs_9_at_contentType: assert siattr['attrValues'][0] == rfc5652.id_data @@ -281,8 +284,7 @@ HktMK+isIjxOTk4yJTOOAgIH0A== for choices in attr['values'][0]['content']['certificates']: for rdn in choices[0]['tbsCertificate']['subject']['rdnSequence']: - if rdn[0]['type'] in rfc5280.certificateAttributesMap: - + if rdn[0]['type'] in openTypesMap: if rdn[0]['type'] == rfc2985.pkcs_9_at_emailAddress: assert rdn[0]['value'] == 'alice@example.com' diff --git a/tests/test_rfc2986.py b/tests/test_rfc2986.py index 92834ee..fa1370d 100644 --- a/tests/test_rfc2986.py +++ b/tests/test_rfc2986.py @@ -6,8 +6,8 @@ # import sys -from pyasn1.codec.der import decoder as der_decoder -from pyasn1.codec.der import encoder as der_encoder +from pyasn1.codec.der.decoder import decode as der_decode +from pyasn1.codec.der.encoder import encode as der_encode from pyasn1.type import char from pyasn1.type import univ @@ -50,28 +50,28 @@ fi6h7i9VVAZpslaKFfkNg12gLbbsCB1q36l5VXjHY/qe0FIUa9ogRrOi substrate = pem.readBase64fromText(self.pem_text) - asn1Object, rest = der_decoder.decode(substrate, asn1Spec=self.asn1Spec) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) assert not rest assert asn1Object.prettyPrint() - assert der_encoder.encode(asn1Object) == substrate + assert der_encode(asn1Object) == substrate def testOpenTypes(self): - algorithmIdentifierMapUpdate = { + openTypesMap = { univ.ObjectIdentifier('1.2.840.113549.1.1.1'): univ.Null(""), univ.ObjectIdentifier('1.2.840.113549.1.1.5'): univ.Null(""), univ.ObjectIdentifier('1.2.840.113549.1.1.11'): univ.Null(""), } - rfc5280.algorithmIdentifierMap.update(algorithmIdentifierMapUpdate) substrate = pem.readBase64fromText(self.pem_text) - asn1Object, rest = der_decoder.decode(substrate, + asn1Object, rest = der_decode(substrate, asn1Spec=rfc2986.CertificationRequest(), + openTypes=openTypesMap, decodeOpenTypes=True) assert not rest assert asn1Object.prettyPrint() - assert der_encoder.encode(asn1Object) == substrate + assert der_encode(asn1Object) == substrate for rdn in asn1Object['certificationRequestInfo']['subject']['rdnSequence']: for atv in rdn: diff --git a/tests/test_rfc3274.py b/tests/test_rfc3274.py index a8137fd..a024b77 100644 --- a/tests/test_rfc3274.py +++ b/tests/test_rfc3274.py @@ -57,11 +57,9 @@ XQ7u2qbaKFtZ7V96NH8ApkUFkg== def testOpenTypes(self): substrate = pem.readBase64fromText(self.compressed_data_pem_text) - - rfc5652.cmsContentTypesMap.update(rfc3274.cmsContentTypesMapUpdate) asn1Object, rest = der_decode(substrate, - asn1Spec=self.asn1Spec, - decodeOpenTypes=True) + asn1Spec=self.asn1Spec, + decodeOpenTypes=True) assert not rest assert asn1Object.prettyPrint() assert der_encode(asn1Object) == substrate diff --git a/tests/test_rfc3770.py b/tests/test_rfc3770.py index e8093f0..775dc75 100644 --- a/tests/test_rfc3770.py +++ b/tests/test_rfc3770.py @@ -54,10 +54,9 @@ DAlVlhox680Jxy5J8Pkx def testOpenTypes(self): substrate = pem.readBase64fromText(self.cert_pem_text) - rfc5280.algorithmIdentifierMap.update(rfc5480.algorithmIdentifierMapUpdate) asn1Object, rest = der_decode(substrate, - asn1Spec=self.asn1Spec, - decodeOpenTypes=True) + asn1Spec=self.asn1Spec, + decodeOpenTypes=True) assert not rest assert asn1Object.prettyPrint() assert der_encode(asn1Object) == substrate diff --git a/tests/test_rfc3779.py b/tests/test_rfc3779.py index 081f69b..32f0ca3 100644 --- a/tests/test_rfc3779.py +++ b/tests/test_rfc3779.py @@ -76,7 +76,6 @@ V+vo2L72yerdbsP9xjqvhZrLKfsLZjYK4SdYYthi def testExtensionsMap(self): substrate = pem.readBase64fromText(self.pem_text) - rfc5280.certificateExtensionsMap.update(rfc3779.certificateExtensionsMapUpdate) asn1Object, rest = der_decoder.decode(substrate, asn1Spec=self.asn1Spec) assert not rest assert asn1Object.prettyPrint() diff --git a/tests/test_rfc4073.py b/tests/test_rfc4073.py index 5a3b7f8..576cc7b 100644 --- a/tests/test_rfc4073.py +++ b/tests/test_rfc4073.py @@ -83,8 +83,6 @@ buWO3egPDL8Kf7tBhzjIKLw= return asn1Object - rfc5652.cmsAttributesMap.update(rfc2634.ESSAttributeMap) - rfc5652.cmsContentTypesMap.update(rfc4073.cmsContentTypesMapUpdate) layers = rfc5652.cmsContentTypesMap getNextLayer = { @@ -120,14 +118,10 @@ buWO3egPDL8Kf7tBhzjIKLw= this_layer = getNextLayer[this_layer](asn1Object) def testOpenTypes(self): - substrate = pem.readBase64fromText(self.pem_text) - - rfc5652.cmsAttributesMap.update(rfc2634.ESSAttributeMap) - rfc5652.cmsContentTypesMap.update(rfc4073.cmsContentTypesMapUpdate) asn1Object, rest = der_decode(substrate, - asn1Spec=rfc5652.ContentInfo(), - decodeOpenTypes=True) + asn1Spec=rfc5652.ContentInfo(), + decodeOpenTypes=True) assert not rest assert asn1Object.prettyPrint() assert der_encode(asn1Object) == substrate diff --git a/tests/test_rfc4108.py b/tests/test_rfc4108.py index aa5dfd2..272558c 100644 --- a/tests/test_rfc4108.py +++ b/tests/test_rfc4108.py @@ -74,7 +74,7 @@ dsnhVtIdkPtfJIvcYteYJg== attribute_list.append(attr['attrType']) if attr['attrType'] == rfc4108.id_aa_targetHardwareIDs: av, rest = der_decode(attr['attrValues'][0], - asn1Spec=rfc4108.TargetHardwareIdentifiers()) + asn1Spec=rfc4108.TargetHardwareIdentifiers()) assert len(av) == 2 for oid in av: assert '1.3.6.1.4.1.221121.1.1.' in oid.prettyPrint() @@ -86,12 +86,9 @@ dsnhVtIdkPtfJIvcYteYJg== def testOpenTypes(self): substrate = pem.readBase64fromText(self.pem_text) - - rfc5652.cmsContentTypesMap.update(rfc4108.cmsContentTypesMapUpdate) - rfc5652.cmsAttributesMap.update(rfc4108.cmsAttributesMapUpdate) asn1Object, rest = der_decode(substrate, - asn1Spec=self.asn1Spec, - decodeOpenTypes=True) + asn1Spec=self.asn1Spec, + decodeOpenTypes=True) assert not rest assert asn1Object.prettyPrint() assert der_encode(asn1Object) == substrate diff --git a/tests/test_rfc5035.py b/tests/test_rfc5035.py index b031c1b..22f0f36 100644 --- a/tests/test_rfc5035.py +++ b/tests/test_rfc5035.py @@ -76,8 +76,8 @@ T9yMtRLN5ZDU14y+Phzq9NKpSw/x5KyXoUKjCMc3Ru6dIW+CgcRQees+dhnvuD5U sat = sa['attrType'] sav0 = sa['attrValues'][0] - if sat in rfc5035.ESSAttributeMap.keys(): - sav, rest = der_decode(sav0, asn1Spec=rfc5035.ESSAttributeMap[sat]) + if 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 @@ -138,16 +138,14 @@ vFIgX7eIkd8= sat = sa['attrType'] sav0 = sa['attrValues'][0] - if sat in rfc5035.ESSAttributeMap.keys(): - sav, rest = der_decode(sav0, asn1Spec=rfc5035.ESSAttributeMap[sat]) + if 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 def testOpenTypes(self): substrate = pem.readBase64fromText(self.signed_receipt_pem_text) - rfc5652.cmsContentTypesMap.update(rfc5035.cmsContentTypesMapUpdate) - rfc5652.cmsAttributesMap.update(rfc5035.ESSAttributeMap) asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec, decodeOpenTypes=True) assert not rest @@ -171,7 +169,7 @@ vFIgX7eIkd8= # automatically decode it receipt, rest = der_decode(sd['encapContentInfo']['eContent'], asn1Spec=rfc5652.cmsContentTypesMap[sd['encapContentInfo']['eContentType']]) - assert receipt['version'] == rfc5035.ESSVersion().subtype(value='v1') + assert receipt['version'] == 1 suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) diff --git a/tests/test_rfc5083.py b/tests/test_rfc5083.py index a1222f9..4dcfa96 100644 --- a/tests/test_rfc5083.py +++ b/tests/test_rfc5083.py @@ -8,8 +8,8 @@ import sys -from pyasn1.codec.der import decoder as der_decoder -from pyasn1.codec.der import encoder as der_encoder +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 @@ -43,10 +43,10 @@ ur76ztut3sr4iIANmvLRbyFUf87+2bPvLQQMoOWSXMGE4BckY8RM def testDerCodec(self): substrate = pem.readBase64fromText(self.pem_text) - asn1Object, rest = der_decoder.decode(substrate, asn1Spec=self.asn1Spec) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) assert not rest assert asn1Object.prettyPrint() - assert der_encoder.encode(asn1Object) == substrate + assert der_encode(asn1Object) == substrate class AuthEnvelopedDataOpenTypesTestCase(unittest.TestCase): @@ -73,19 +73,17 @@ IDAeDBFXYXRzb24sIGNvbWUgaGVyZQYJKoZIhvcNAQcB def testDerCodec(self): substrate = pem.readBase64fromText(self.pem_text) - rfc5652.cmsAttributesMap.update(rfc5035.ESSAttributeMap) - rfc5652.cmsContentTypesMap.update(rfc5083.cmsContentTypesMapUpdate) - asn1Object, rest = der_decoder.decode(substrate, - asn1Spec=self.asn1Spec, - decodeOpenTypes=True) + asn1Object, rest = der_decode(substrate, + asn1Spec=self.asn1Spec, + decodeOpenTypes=True) assert not rest assert asn1Object.prettyPrint() - assert der_encoder.encode(asn1Object) == substrate + assert der_encode(asn1Object) == substrate assert asn1Object['contentType'] in rfc5652.cmsContentTypesMap assert asn1Object['contentType'] == rfc5083.id_ct_authEnvelopedData authenv = asn1Object['content'] - assert authenv['version'] == rfc5652.CMSVersion().subtype(value='v0') + assert authenv['version'] == 0 for attr in authenv['unauthAttrs']: assert attr['attrType'] in rfc5652.cmsAttributesMap diff --git a/tests/test_rfc5084.py b/tests/test_rfc5084.py index 8356f90..e685922 100644 --- a/tests/test_rfc5084.py +++ b/tests/test_rfc5084.py @@ -8,11 +8,13 @@ import sys -from pyasn1.codec.der import decoder as der_decoder -from pyasn1.codec.der import encoder as der_encoder +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 rfc5083 from pyasn1_modules import rfc5084 +from pyasn1_modules import rfc5652 try: import unittest2 as unittest @@ -28,10 +30,10 @@ class CCMParametersTestCase(unittest.TestCase): def testDerCodec(self): substrate = pem.readBase64fromText(self.ccm_pem_text) - asn1Object, rest = der_decoder.decode(substrate, asn1Spec=self.asn1Spec) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) assert not rest assert asn1Object.prettyPrint() - assert der_encoder.encode(asn1Object) == substrate + assert der_encode(asn1Object) == substrate class GCMParametersTestCase(unittest.TestCase): @@ -42,10 +44,74 @@ class GCMParametersTestCase(unittest.TestCase): def testDerCodec(self): substrate = pem.readBase64fromText(self.gcm_pem_text) - asn1Object, rest = der_decoder.decode(substrate, asn1Spec=self.asn1Spec) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) assert not rest assert asn1Object.prettyPrint() - assert der_encoder.encode(asn1Object) == substrate + assert der_encode(asn1Object) == substrate + + +class GCMOpenTypesTestCase(unittest.TestCase): + rfc8591_pem_pext = """\ +MIIHkAYLKoZIhvcNAQkQARegggd/MIIHewIBADGCAk8wggJLAgEAMDMwJjEUMBIGA1UECgwL +ZXhhbXBsZS5jb20xDjAMBgNVBAMMBUFsaWNlAgkAg/ULtwvVxA4wDQYJKoZIhvcNAQEBBQAE +ggIAdZphtN3x8a8kZoAFY15HYRD6JyPBueRUhLbTPoOH3pZ9xeDK+zVXGlahl1y1UOe+McEx +2oD7cxAkhFuruNZMrCYEBCTZMwVhyEOZlBXdZEs8rZUHL3FFE5PJnygsSIO9DMxd1UuTFGTg +Cm5V5ZLFGmjeEGJRbsfTyo52S7iseJqIN3dl743DbApu0+yuUoXKxqKdUFlEVxmhvc+Qbg/z +fiwu8PTsYiUQDMBi4cdIlju8iLjj389xQHNyndXHWD51is89GG8vpBe+IsN8mnbGtCcpqtJ/ +c65ErJhHTR7rSJSMEqQD0LPOCKIY1q9FaSSJfMXJZk9t/rPxgUEVjfw7hAkKpgOAqoZRN+Fp +nFyBl0FnnXo8kLp55tfVyNibtUpmdCPkOwt9b3jAtKtnvDQ2YqY1/llfEUnFOVDKwuC6MYwi +fm92qNlAQA/T0+ocjs6gA9zOLx+wD1zqM13hMD/L+T2OHL/WgvGb62JLrNHXuPWA8RShO4kI +lPtARKXap2S3+MX/kpSUUrNa65Y5uK1jwFFclczG+CPCIBBn6iJiQT/vOX1I97YUP4Qq6OGk +jK064Bq6o8+e5+NmIOBcygYRv6wA7vGkmPLSWbnw99qD728bBh84fC3EjItdusqGIwjzL0eS +UWXJ5eu0Z3mYhJGN1pe0R/TEB5ibiJsMLpWAr3gwggUPBgkqhkiG9w0BBwEwHgYJYIZIAWUD +BAEGMBEEDE2HVyIurFKUEX8MEgIBEICCBOD+L7PeC/BpmMOb9KlS+r+LD+49fi6FGBrs8aie +Gi7ezZQEiFYS38aYQzTYYCt3SbJQTkX1fDsGZiaw/HRiNh7sJnxWATm+XNKGoq+Wls9RhSJ4 +5Sw4GMqwpoxZjeT84UozOITk3l3fV+3XiGcCejHkp8DAKZFExd5rrjlpnnAOBX6w8NrXO4s2 +n0LrMhtBU4eB2YKhGgs5Q6wQyXtU7rc7OOwTGvxWEONzSHJ01pyvqVQZAohsZPaWLULrM/kE +GkrhG4jcaVjVPfULi7Uqo14imYhdCq5Ba4bwqI0Ot6mB27KD6LlOnVC/YmXCNIoYoWmqy1o3 +pSm9ovnLEO/dzxQjEJXYeWRje9M/sTxotM/5oZBpYMHqIwHTJbehXFgp8+oDjyTfayMYA3fT +cTH3XbGPQfnYW2U9+ka/JhcSYybM8cuDNFd1I1LIQXoJRITXtkvPUbJqm+s6DtS5yvG9I8aQ +xlT365zphS4vbQaO74ujO8bE3dynrvTTV0c318TcHpN3DY9PIt6mHXMIPDLEA4wes90zg6ia +h5XiQcLtfLaAdYwEEGlImGD8n0kOhSNgclSLMklpj5mVOs8exli3qoXlVMRJcBptSwOe0QPc +RY30spywS4zt1UDIQ0jaecGGVtUYj586nkubhAxwZkuQKWxgt6yYTpGNSKCdvd+ygfyGJRDb +Wdn6nck/EPnG1773KTHRhMrXrBPBpSlfyJ/ju3644CCFqCjFoTh4bmB63k9ejUEVkJIJuoeK +eTBaUxbCIinkK4htBkgchHP51RJp4q9jQbziD3aOhg13hO1GFQ4E/1DNIJxbEnURNp/ga8Sq +mnLY8f5Pzwhm1mSzZf+obowbQ+epISrswWyjUKKO+uJfrAVN2TS/5+X6T3U6pBWWjH6+xDng +rAJwtIdKBo0iSEwJ2eir4X8TcrSy9l8RSOiTPtqS5dF3RWSWOzkcO72fHCf/42+DLgUVX8Oe +5mUvp7QYiXXsXGezLJ8hPIrGuOEypafDv3TwFkBc2MIB0QUhk+GG1ENY3jiNcyEbovF5Lzz+ +ubvechHSb1arBuEczJzN4riM2Dc3c+r8N/2Ft6eivK7HUuYX1uAcArhunZpA8yBGLF1m+DUX +FtzWAUvfMKYPdfwGMckghF7YwLrTXd8ZhPIkHNO1KdwQKIRfgIlUPfTxRB7eNrG/Ma9a/Iwr +cI1QtkXU59uIZIw+7+FHZRWPsOjTu1Pdy+JtcSTG4dmS+DIwqpUzdu6MaBCVaOhXHwybvaSP +TfMG/nR/NxF1FI8xgydnzXZs8HtFDL9iytKnvXHx+IIz8Rahp/PK8S80vPQNIeef/JgnIhto +sID/A614LW1tB4cWdveYlD5U8T/XXInAtCY78Q9WJD+ecu87OJmlOdmjrFvitpQAo8+NGWxc +7Wl7LtgDuYel7oXFCVtI2npbA7R+K5/kzUvDCY6GTgzn1Gfamc1/Op6Ue17qd/emvhbIx+ng +3swf8TJVnCNDIXucKVA4boXSlCEhCGzfoZZYGVvm1/hrypiBtpUIKWTxLnz4AQJdZ5LGiCQJ +QU1wMyHsg6vWmNaJVhGHE6D/EnKsvJptFIkAx0wWkh35s48p7EbU8QBg//5eNru6yvLRutfd +BX7T4w681pCD+dOiom75C3UdahrfoFkNsZ2hB88+qNsEEPb/xuGu8ZzSPZhakhl2NS0= +""" + + def setUp(self): + self.asn1Spec = rfc5652.ContentInfo() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.rfc8591_pem_pext) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + assert asn1Object['contentType'] == rfc5083.id_ct_authEnvelopedData + aed, rest = der_decode(asn1Object['content'], + asn1Spec=rfc5083.AuthEnvelopedData(), + decodeOpenTypes=True) + assert not rest + assert aed.prettyPrint() + assert der_encode(aed) == asn1Object['content'] + + assert aed['version'] == 0 + cea = aed['authEncryptedContentInfo']['contentEncryptionAlgorithm'] + assert cea['algorithm'] == rfc5084.id_aes128_GCM + assert cea['parameters']['aes-ICVlen'] == 16 suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) diff --git a/tests/test_rfc5280.py b/tests/test_rfc5280.py index 5abff3e..ce9ec8d 100644 --- a/tests/test_rfc5280.py +++ b/tests/test_rfc5280.py @@ -6,8 +6,8 @@ # import sys -from pyasn1.codec.der import decoder as der_decoder -from pyasn1.codec.der import encoder as der_encoder +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 @@ -48,11 +48,11 @@ PhmcGcwTTYJBtYze4D1gCCAPRX5ron+jjBXu substrate = pem.readBase64fromText(self.pem_text) - asn1Object, rest = der_decoder.decode(substrate, asn1Spec=self.asn1Spec) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) assert not rest assert asn1Object.prettyPrint() - assert der_encoder.encode(asn1Object) == substrate + assert der_encode(asn1Object) == substrate class CertificateListTestCase(unittest.TestCase): @@ -74,11 +74,11 @@ vjnIhxTFoCb5vA== substrate = pem.readBase64fromText(self.pem_text) - asn1Object, rest = der_decoder.decode(substrate, asn1Spec=self.asn1Spec) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) assert not rest assert asn1Object.prettyPrint() - assert der_encoder.encode(asn1Object) == substrate + assert der_encode(asn1Object) == substrate class CertificateOpenTypeTestCase(unittest.TestCase): @@ -108,19 +108,25 @@ PhmcGcwTTYJBtYze4D1gCCAPRX5ron+jjBXu substrate = pem.readBase64fromText(self.pem_text) - algorithmIdentifierMapUpdate = { + openTypesMap = { univ.ObjectIdentifier('1.2.840.113549.1.1.1'): univ.Null(""), univ.ObjectIdentifier('1.2.840.113549.1.1.5'): univ.Null(""), univ.ObjectIdentifier('1.2.840.113549.1.1.11'): univ.Null(""), } - rfc5280.algorithmIdentifierMap.update(algorithmIdentifierMapUpdate) - - asn1Object, rest = der_decoder.decode(substrate, - asn1Spec=self.asn1Spec, decodeOpenTypes=True) + asn1Object, rest = der_decode(substrate, + asn1Spec=self.asn1Spec, + openTypes=openTypesMap, + decodeOpenTypes=True) assert not rest assert asn1Object.prettyPrint() - assert der_encoder.encode(asn1Object) == substrate + assert der_encode(asn1Object) == substrate + + sig_alg = asn1Object['tbsCertificate']['signature'] + assert sig_alg['parameters'] == univ.Null("") + + spki_alg = asn1Object['tbsCertificate']['subjectPublicKeyInfo']['algorithm'] + assert spki_alg['parameters'] == univ.Null("") for rdn in asn1Object['tbsCertificate']['subject']['rdnSequence']: for atv in rdn: @@ -130,12 +136,6 @@ PhmcGcwTTYJBtYze4D1gCCAPRX5ron+jjBXu atv_ps = str(atv['value']['printableString']) assert "valicert" in atv_ps.lower() - sig_alg = asn1Object['tbsCertificate']['signature'] - assert sig_alg['parameters'] == univ.Null("") - - spki_alg = asn1Object['tbsCertificate']['subjectPublicKeyInfo']['algorithm'] - assert spki_alg['parameters'] == univ.Null("") - class CertificateListOpenTypeTestCase(unittest.TestCase): pem_text = """\ @@ -156,19 +156,22 @@ vjnIhxTFoCb5vA== substrate = pem.readBase64fromText(self.pem_text) - algorithmIdentifierMapUpdate = { + openTypesMap = { univ.ObjectIdentifier('1.2.840.113549.1.1.1'): univ.Null(""), univ.ObjectIdentifier('1.2.840.113549.1.1.5'): univ.Null(""), univ.ObjectIdentifier('1.2.840.113549.1.1.11'): univ.Null(""), } - rfc5280.algorithmIdentifierMap.update(algorithmIdentifierMapUpdate) - - asn1Object, rest = der_decoder.decode(substrate, - asn1Spec=self.asn1Spec, decodeOpenTypes=True) + asn1Object, rest = der_decode(substrate, + asn1Spec=self.asn1Spec, + openTypes=openTypesMap, + decodeOpenTypes=True) assert not rest assert asn1Object.prettyPrint() - assert der_encoder.encode(asn1Object) == substrate + assert der_encode(asn1Object) == substrate + + sig_alg = asn1Object['tbsCertList']['signature'] + assert sig_alg['parameters'] == univ.Null("") for rdn in asn1Object['tbsCertList']['issuer']['rdnSequence']: for atv in rdn: @@ -181,27 +184,24 @@ vjnIhxTFoCb5vA== for extn in asn1Object['tbsCertList']['crlExtensions']: if extn['extnID'] in rfc5280.certificateExtensionsMap.keys(): - ev, rest = der_decoder.decode(extn['extnValue'], + ev, rest = der_decode(extn['extnValue'], asn1Spec=rfc5280.certificateExtensionsMap[extn['extnID']]) assert not rest assert ev.prettyPrint() - assert der_encoder.encode(ev) == extn['extnValue'] - - sig_alg = asn1Object['tbsCertList']['signature'] - assert sig_alg['parameters'] == univ.Null("") + assert der_encode(ev) == extn['extnValue'] def testExtensionsMap(self): substrate = pem.readBase64fromText(self.pem_text) - asn1Object, rest = der_decoder.decode(substrate, asn1Spec=self.asn1Spec) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) assert not rest assert asn1Object.prettyPrint() - assert der_encoder.encode(asn1Object) == substrate + assert der_encode(asn1Object) == substrate for extn in asn1Object['tbsCertList']['crlExtensions']: if extn['extnID'] in rfc5280.certificateExtensionsMap.keys(): - extnValue, rest = der_decoder.decode(extn['extnValue'], + extnValue, rest = der_decode(extn['extnValue'], asn1Spec=rfc5280.certificateExtensionsMap[extn['extnID']]) - assert der_encoder.encode(extnValue) == extn['extnValue'] + assert der_encode(extnValue) == extn['extnValue'] class ORAddressOpenTypeTestCase(unittest.TestCase): @@ -214,14 +214,14 @@ FDASgAEBoQ0TC1N0ZXZlIEtpbGxl self.asn1Spec = rfc5280.ORAddress() def testDecodeOpenTypes(self): - substrate = pem.readBase64fromText(self.oraddress_pem_text) - asn1Object, rest = der_decoder.decode(substrate, - asn1Spec=self.asn1Spec, decodeOpenTypes=True) + asn1Object, rest = der_decode(substrate, + asn1Spec=self.asn1Spec, + decodeOpenTypes=True) assert not rest assert asn1Object.prettyPrint() - assert der_encoder.encode(asn1Object) == substrate + assert der_encode(asn1Object) == substrate ea0 = asn1Object['extension-attributes'][0] assert ea0['extension-attribute-type'] == rfc5280.common_name diff --git a/tests/test_rfc5480.py b/tests/test_rfc5480.py index 06e336d..fa93553 100755 --- a/tests/test_rfc5480.py +++ b/tests/test_rfc5480.py @@ -8,13 +8,12 @@ import sys -from pyasn1.codec.der import decoder as der_decoder -from pyasn1.codec.der import encoder as der_encoder +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 rfc5480 -from pyasn1_modules import rfc4055 try: import unittest2 as unittest @@ -51,27 +50,24 @@ Ea8/B6hPatJ0ES8q/HO3X8IVQwVs1n3aAr0im0/T+Xc= def testDerCodec(self): substrate = pem.readBase64fromText(self.digicert_ec_cert_pem_text) - asn1Object, rest = der_decoder.decode(substrate, asn1Spec=self.asn1Spec) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) assert not rest assert asn1Object.prettyPrint() - assert der_encoder.encode(asn1Object) == substrate - assert substrate == der_encoder.encode(asn1Object) + assert der_encode(asn1Object) == substrate algid = asn1Object['tbsCertificate']['subjectPublicKeyInfo']['algorithm'] assert algid['algorithm'] == rfc5480.id_ecPublicKey - param, rest = der_decoder.decode(algid['parameters'], asn1Spec=rfc5480.ECParameters()) + param, rest = der_decode(algid['parameters'], asn1Spec=rfc5480.ECParameters()) assert param.prettyPrint() assert param['namedCurve'] == rfc5480.secp384r1 def testOpenTypes(self): substrate = pem.readBase64fromText(self.digicert_ec_cert_pem_text) - rfc5280.algorithmIdentifierMap.update(rfc5480.algorithmIdentifierMapUpdate) - asn1Object, rest = der_decoder.decode(substrate, - asn1Spec=self.asn1Spec, - decodeOpenTypes=True) + asn1Object, rest = der_decode(substrate, + asn1Spec=self.asn1Spec, decodeOpenTypes=True) assert not rest assert asn1Object.prettyPrint() - assert der_encoder.encode(asn1Object) == substrate + assert der_encode(asn1Object) == substrate spki_alg = asn1Object['tbsCertificate']['subjectPublicKeyInfo']['algorithm'] assert spki_alg['algorithm'] == rfc5480.id_ecPublicKey diff --git a/tests/test_rfc5652.py b/tests/test_rfc5652.py index b34627b..be18914 100644 --- a/tests/test_rfc5652.py +++ b/tests/test_rfc5652.py @@ -108,25 +108,21 @@ xicQmJP+VoMHo/ZpjFY9fYCjNZUArgKsEwK/s+p9yrVVeB1Nf8Mn namedtype.NamedType('Signature', univ.BitString()) ) - attributesMapUpdate = { + openTypeMap = { + # attributes univ.ObjectIdentifier('1.3.6.1.4.1.311.13.2.3'): char.IA5String(), univ.ObjectIdentifier('1.3.6.1.4.1.311.13.2.2'): EnrollmentCSP(), univ.ObjectIdentifier('1.3.6.1.4.1.311.21.20'): ClientInformation(), - } - - rfc5652.cmsAttributesMap.update(rfc6402.cmcControlAttributesMap) - rfc5652.cmsAttributesMap.update(attributesMapUpdate) - - algorithmIdentifierMapUpdate = { + # algorithm identifier parameters univ.ObjectIdentifier('1.2.840.113549.1.1.1'): univ.Null(""), univ.ObjectIdentifier('1.2.840.113549.1.1.5'): univ.Null(""), univ.ObjectIdentifier('1.2.840.113549.1.1.11'): univ.Null(""), } - rfc5280.algorithmIdentifierMap.update(algorithmIdentifierMapUpdate) + openTypeMap.update(rfc5652.cmsAttributesMap) + openTypeMap.update(rfc6402.cmcControlAttributesMap) substrate = pem.readBase64fromText(self.pem_text) - asn1Object, rest = der_decoder.decode(substrate, asn1Spec=rfc5652.ContentInfo(), decodeOpenTypes=True) assert not rest @@ -138,6 +134,7 @@ xicQmJP+VoMHo/ZpjFY9fYCjNZUArgKsEwK/s+p9yrVVeB1Nf8Mn assert eci['eContentType'] == rfc6402.id_cct_PKIData pkid, rest = der_decoder.decode(eci['eContent'], asn1Spec=rfc5652.cmsContentTypesMap[eci['eContentType']], + openTypes=openTypeMap, decodeOpenTypes=True) assert not rest assert pkid.prettyPrint() @@ -147,17 +144,17 @@ xicQmJP+VoMHo/ZpjFY9fYCjNZUArgKsEwK/s+p9yrVVeB1Nf8Mn cr = req['tcr']['certificationRequest'] sig_alg = cr['signatureAlgorithm'] - assert sig_alg['algorithm'] in rfc5280.algorithmIdentifierMap.keys() + assert sig_alg['algorithm'] in openTypeMap.keys() assert sig_alg['parameters'] == univ.Null("") cri = cr['certificationRequestInfo'] spki_alg = cri['subjectPublicKeyInfo']['algorithm'] - assert spki_alg['algorithm'] in rfc5280.algorithmIdentifierMap.keys() + assert spki_alg['algorithm'] in openTypeMap.keys() assert spki_alg['parameters'] == univ.Null("") attrs = cr['certificationRequestInfo']['attributes'] for attr in attrs: - assert attr['attrType'] in rfc5652.cmsAttributesMap.keys() + assert attr['attrType'] in openTypeMap.keys() if attr['attrType'] == univ.ObjectIdentifier('1.3.6.1.4.1.311.13.2.3'): assert attr['attrValues'][0] == "6.2.9200.2" else: diff --git a/tests/test_rfc5940.py b/tests/test_rfc5940.py index d33a1c7..293b565 100644 --- a/tests/test_rfc5940.py +++ b/tests/test_rfc5940.py @@ -81,7 +81,7 @@ ttTMEpl2prH8bbwo1g== assert asn1Object['contentType'] == rfc5652.id_signedData sd, rest = der_decode(asn1Object['content'], - asn1Spec=rfc5652.SignedData()) + asn1Spec=rfc5652.SignedData()) assert sd.prettyPrint() assert sd['encapContentInfo']['eContentType'] == rfc5652.id_data @@ -92,18 +92,16 @@ ttTMEpl2prH8bbwo1g== assert sd['crls'][1]['other']['otherRevInfoFormat'] == ocspr_oid ocspr, rest = der_decode(sd['crls'][1]['other']['otherRevInfo'], - asn1Spec=rfc5940.OCSPResponse()) + asn1Spec=rfc5940.OCSPResponse()) assert ocspr.prettyPrint() success = rfc2560.OCSPResponseStatus(value='successful') assert ocspr['responseStatus'] == success def testOpenTypes(self): substrate = pem.readBase64fromText(self.pem_text) - - rfc5652.otherRevInfoFormatMap.update(rfc5940.otherRevInfoFormatMapUpdate) asn1Object, rest = der_decode(substrate, - asn1Spec=self.asn1Spec, - decodeOpenTypes=True) + asn1Spec=self.asn1Spec, + decodeOpenTypes=True) assert not rest assert asn1Object.prettyPrint() assert der_encode(asn1Object) == substrate diff --git a/tests/test_rfc5958.py b/tests/test_rfc5958.py index febec78..0b859ed 100644 --- a/tests/test_rfc5958.py +++ b/tests/test_rfc5958.py @@ -8,8 +8,8 @@ import sys -from pyasn1.codec.der import decoder as der_decoder -from pyasn1.codec.der import encoder as der_encoder +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 @@ -36,7 +36,7 @@ Z9w7lshQhqowtrbLDFw4rXAxZuE= def testDerCodec(self): substrate = pem.readBase64fromText(self.priv_key_pem_text) - asn1Object, rest = der_decoder.decode(substrate, asn1Spec=self.asn1Spec) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) assert not rest assert asn1Object.prettyPrint() assert asn1Object['privateKeyAlgorithm']['algorithm'] == rfc8410.id_Ed25519 @@ -44,7 +44,7 @@ Z9w7lshQhqowtrbLDFw4rXAxZuE= assert asn1Object['privateKey'].prettyPrint()[0:10] == "0x0420d4ee" assert asn1Object['publicKey'].isValue assert asn1Object['publicKey'].prettyPrint()[0:10] == "1164575857" - assert der_encoder.encode(asn1Object) == substrate + assert der_encode(asn1Object) == substrate class PrivateKeyOpenTypesTestCase(unittest.TestCase): @@ -59,13 +59,11 @@ YWlyc4EhABm/RAlphM3+hUG6wWfcO5bIUIaqMLa2ywxcOK1wMWbh def testOpenTypes(self): substrate = pem.readBase64fromText(self.asymmetric_key_pkg_pem_text) - rfc5652.cmsContentTypesMap.update(rfc5958.cmsContentTypesMapUpdate) - asn1Object, rest = der_decoder.decode(substrate, - asn1Spec=self.asn1Spec, - decodeOpenTypes=True) + asn1Object, rest = der_decode(substrate, + asn1Spec=self.asn1Spec, decodeOpenTypes=True) assert not rest assert asn1Object.prettyPrint() - assert der_encoder.encode(asn1Object) == substrate + assert der_encode(asn1Object) == substrate assert rfc5958.id_ct_KP_aKeyPackage in rfc5652.cmsContentTypesMap.keys() oneKey = asn1Object['content'][0] diff --git a/tests/test_rfc6019.py b/tests/test_rfc6019.py index 311e49f..46cd75d 100644 --- a/tests/test_rfc6019.py +++ b/tests/test_rfc6019.py @@ -43,8 +43,6 @@ class BinarySigningTimeTestCase(unittest.TestCase): def testOpenTypes(self): substrate = pem.readBase64fromText(self.pem_text) - - rfc5652.cmsAttributesMap.update(rfc6019.cmsAttributesMapUpdate) asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec, decodeOpenTypes=True) diff --git a/tests/test_rfc6402.py b/tests/test_rfc6402.py index 191078c..1532b38 100755 --- a/tests/test_rfc6402.py +++ b/tests/test_rfc6402.py @@ -50,7 +50,8 @@ xicQmJP+VoMHo/ZpjFY9fYCjNZUArgKsEwK/s+p9yrVVeB1Nf8Mn """ def testDerCodec(self): - layers = rfc5652.cmsContentTypesMap + layers = { } + layers.update(rfc5652.cmsContentTypesMap) getNextLayer = { rfc5652.id_ct_contentInfo: lambda x: x['contentType'], @@ -97,27 +98,24 @@ xicQmJP+VoMHo/ZpjFY9fYCjNZUArgKsEwK/s+p9yrVVeB1Nf8Mn namedtype.NamedType('Signature', univ.BitString()) ) - attributesMapUpdate = { + openTypeMap = { + # attributes univ.ObjectIdentifier('1.3.6.1.4.1.311.13.2.3'): char.IA5String(), univ.ObjectIdentifier('1.3.6.1.4.1.311.13.2.2'): EnrollmentCSP(), univ.ObjectIdentifier('1.3.6.1.4.1.311.21.20'): ClientInformation(), - } - - rfc5652.cmsAttributesMap.update(rfc6402.cmcControlAttributesMap) - rfc5652.cmsAttributesMap.update(attributesMapUpdate) - - algorithmIdentifierMapUpdate = { + # algorithm identifier parameters univ.ObjectIdentifier('1.2.840.113549.1.1.1'): univ.Null(""), univ.ObjectIdentifier('1.2.840.113549.1.1.5'): univ.Null(""), univ.ObjectIdentifier('1.2.840.113549.1.1.11'): univ.Null(""), } - rfc5280.algorithmIdentifierMap.update(algorithmIdentifierMapUpdate) + openTypeMap.update(rfc5652.cmsAttributesMap) + openTypeMap.update(rfc6402.cmcControlAttributesMap) substrate = pem.readBase64fromText(self.pem_text) asn1Object, rest = der_decode(substrate, - asn1Spec=rfc5652.ContentInfo(), - decodeOpenTypes=True) + asn1Spec=rfc5652.ContentInfo(), + decodeOpenTypes=True) assert not rest assert asn1Object.prettyPrint() assert der_encode(asn1Object) == substrate @@ -126,8 +124,9 @@ xicQmJP+VoMHo/ZpjFY9fYCjNZUArgKsEwK/s+p9yrVVeB1Nf8Mn assert eci['eContentType'] == rfc6402.id_cct_PKIData substrate = eci['eContent'] asn1Object, rest = der_decode(substrate, - asn1Spec=rfc6402.PKIData(), - decodeOpenTypes=True) + asn1Spec=rfc6402.PKIData(), + openTypes=openTypeMap, + decodeOpenTypes=True) assert not rest assert asn1Object.prettyPrint() assert der_encode(asn1Object) == substrate @@ -136,17 +135,17 @@ xicQmJP+VoMHo/ZpjFY9fYCjNZUArgKsEwK/s+p9yrVVeB1Nf8Mn cr = req['tcr']['certificationRequest'] sig_alg = cr['signatureAlgorithm'] - assert sig_alg['algorithm'] in rfc5280.algorithmIdentifierMap.keys() + assert sig_alg['algorithm'] in openTypeMap.keys() assert sig_alg['parameters'] == univ.Null("") cri = cr['certificationRequestInfo'] spki_alg = cri['subjectPublicKeyInfo']['algorithm'] - assert spki_alg['algorithm'] in rfc5280.algorithmIdentifierMap.keys() + assert spki_alg['algorithm'] in openTypeMap.keys() assert spki_alg['parameters'] == univ.Null("") attrs = cr['certificationRequestInfo']['attributes'] for attr in attrs: - assert attr['attrType'] in rfc5652.cmsAttributesMap.keys() + assert attr['attrType'] in openTypeMap.keys() if attr['attrType'] == univ.ObjectIdentifier('1.3.6.1.4.1.311.13.2.3'): assert attr['attrValues'][0] == "6.2.9200.2" else: diff --git a/tests/test_rfc7030.py b/tests/test_rfc7030.py index 5708203..c351922 100644 --- a/tests/test_rfc7030.py +++ b/tests/test_rfc7030.py @@ -60,12 +60,15 @@ BgcrBgEBAQEWBggqhkjOPQQDAw== assert attr_or_oid['attribute']['attrType'] in self.the_attrTypes def testOpenTypes(self): + openTypesMap = { } + openTypesMap.update(rfc5652.cmsAttributesMap) for at in self.the_attrTypes: - rfc5652.cmsAttributesMap.update({ at: univ.ObjectIdentifier(), }) - + openTypesMap.update({ at: univ.ObjectIdentifier(), }) + substrate = pem.readBase64fromText(self.pem_text) asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec, + openTypes=openTypesMap, decodeOpenTypes=True) assert not rest assert asn1Object.prettyPrint() @@ -81,6 +84,7 @@ BgcrBgEBAQEWBggqhkjOPQQDAw== if attr_or_oid['attribute']['attrType'] == self.the_attrTypes[1]: assert valString == self.the_attrVals[1] + suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) if __name__ == '__main__': diff --git a/tests/test_rfc7894.py b/tests/test_rfc7894.py new file mode 100644 index 0000000..243b4c9 --- /dev/null +++ b/tests/test_rfc7894.py @@ -0,0 +1,86 @@ +# +# 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 rfc6402 +from pyasn1_modules import rfc7894 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class AlternativeChallengePasswordTestCase(unittest.TestCase): + otp_pem_text = """\ +MIICsjCCAZwCAQAwJDELMAkGA1UEBhMCVVMxFTATBgNVBAMTDDRUUzJWMk5MWEE2 +WjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKmF0oUj5+1rBB+pUO8X +7FPxer+1BhWOa54RTSucJmBaLx0H95qNaBCcctNDl1kcmIro/a0zMcEvj5Do29vQ +lStJdTeJ/B3X4qzOGShupxJcAhCreRZjN6Yz3T9z0zJ8OPnRvJOzcSiIzlubc9lK +Cpq4U0UsCLLfymOgL9NH4lZi96J+PFuJr0J+rTY38076U2jcPqNq5/L/d6NV9Sz2 +IVOvCK1kqP/nElJVibIQZvj9YESLUKyVAfTNxLj3+IpioOOv2dT3kB9wdi4plAVi +UFEUvED1okRrI29+LdPV1UXglOCksyJIIw+DgDtutDE5Co6QkTNURFEdKIV9Sg13 +zEECAwEAAaBLMBkGCyqGSIb3DQEJEAI4MQoTCDkwNTAzODQ2MC4GCSqGSIb3DQEJ +DjEhMB8wHQYDVR0OBBYEFBj12LVowM16Ed0D+AmoElKNYP/kMAsGCSqGSIb3DQEB +CwOCAQEAZZdDWKejs3UVfgZI3R9cMWGijmscVeZrjwFVkn7MI9pEDZ2aS1QaRYjY +1cu9j3i+LQp9LWPIW/ztYk11e/OcZp3fo8pZ+MT66n7YTWfDXNkqqA5xmI84DMEx +/cqenyzOBZWqpZGx7eyM9BtnrdeJ0r2qSc7LYU25FbIQFJJf8IvgMAXWMs50fvs2 +Gzns447x952se2ReQ3vYhXdHvYYcgAZfSJZvK+nCmhzzqowv5p15Y5S+IHpBSXTO +a1qhNW4cjdicQZUeQ2R5kiuwZ+8vHaq9jKxAEk0hBeqG6RQaxvNOBQhHtTLNGw/C +NmaF8Y2Sl/MgvC5tjs0Ck0/r3lsoLQ== +""" + + def setUp(self): + self.asn1Spec = rfc6402.CertificationRequest() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.otp_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + assert asn1Object['certificationRequestInfo']['version'] == 0 + + for attr in asn1Object['certificationRequestInfo']['attributes']: + assert attr['attrType'] in rfc6402.cmcControlAttributesMap.keys() + av, rest = der_decode(attr['attrValues'][0], + rfc6402.cmcControlAttributesMap[attr['attrType']]) + assert not rest + assert der_encode(av) == attr['attrValues'][0] + + if attr['attrType'] == rfc7894.id_aa_otpChallenge: + assert av['printableString'] == '90503846' + + def testOpenTypes(self): + substrate = pem.readBase64fromText(self.otp_pem_text) + asn1Object, rest = der_decode(substrate, + asn1Spec=self.asn1Spec, + decodeOpenTypes=True) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + for attr in asn1Object['certificationRequestInfo']['attributes']: + assert attr['attrType'] in rfc6402.cmcControlAttributesMap.keys() + if attr['attrType'] == rfc7894.id_aa_otpChallenge: + assert attr['attrValues'][0]['printableString'] == '90503846' + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + import sys + + result = unittest.TextTestRunner(verbosity=2).run(suite) + sys.exit(not result.wasSuccessful()) \ No newline at end of file diff --git a/tests/test_rfc7906.py b/tests/test_rfc7906.py index e843bdd..8e4a6ee 100644 --- a/tests/test_rfc7906.py +++ b/tests/test_rfc7906.py @@ -145,10 +145,15 @@ toMsV8fLBpBjA5YGQvd3TAcSw1lNbWpArL+hje1dzQ7pxslnkklv3CTxAjBuVebz assert av == univ.OctetString(hexValue='7906') def testOpenTypes(self): + openTypesMap = { } + openTypesMap.update(rfc5280.certificateAttributesMap) + openTypesMap.update(rfc5652.cmsAttributesMap) + substrate = pem.readBase64fromText(self.attr_set_pem_text) - rfc5280.certificateAttributesMap.update(rfc5652.cmsAttributesMap) - asn1Object, rest = der_decode (substrate, - asn1Spec=self.asn1Spec, decodeOpenTypes=True) + asn1Object, rest = der_decode(substrate, + asn1Spec=self.asn1Spec, + openTypes=openTypesMap, + decodeOpenTypes=True) assert not rest assert asn1Object.prettyPrint() assert der_encode(asn1Object) == substrate diff --git a/tests/test_rfc8520.py b/tests/test_rfc8520.py index 483b81a..4612f3a 100644 --- a/tests/test_rfc8520.py +++ b/tests/test_rfc8520.py @@ -84,7 +84,6 @@ izaUuU1EEwgOMELjeFL62Ssvq8X+x6hZFCLygI7GNeitlblNhCXhFFurqMs= def testExtensionsMap(self): substrate = pem.readBase64fromText(self.mud_cert_pem_text) - rfc5280.certificateExtensionsMap.update(rfc8520.certificateExtensionsMapUpdate) asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) assert not rest assert asn1Object.prettyPrint() -- cgit v1.2.3 From f3106317179d2f3ed73721514916018c46894f44 Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Sun, 8 Sep 2019 16:57:38 -0400 Subject: Add support for RFC 5990 (#63) --- CHANGES.txt | 1 + pyasn1_modules/rfc5990.py | 237 ++++++++++++++++++++++++++++++++++++++++++++++ tests/__main__.py | 1 + tests/test_rfc5990.py | 82 ++++++++++++++++ 4 files changed, 321 insertions(+) create mode 100644 pyasn1_modules/rfc5990.py create mode 100755 tests/test_rfc5990.py 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()) -- cgit v1.2.3 From 5ef413b279d9c78688f926604840012687e8960b Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sun, 8 Sep 2019 23:05:07 +0200 Subject: Fix malformed `rfc6402.RevRepContent` layout --- CHANGES.txt | 1 + pyasn1_modules/rfc4210.py | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index ed3181c..bacb11e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -27,6 +27,7 @@ Revision 0.2.7, released XX-08-2019 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 +- Fixed malformed `rfc6402.RevRepContent` data structure layout Revision 0.2.6, released 31-07-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc4210.py b/pyasn1_modules/rfc4210.py index fd3b256..45926d3 100644 --- a/pyasn1_modules/rfc4210.py +++ b/pyasn1_modules/rfc4210.py @@ -354,8 +354,12 @@ class RevRepContent(univ.Sequence): OPTIONAL """ componentType = namedtype.NamedTypes( - namedtype.NamedType('status', PKIStatusInfo( - sizeSpec=constraint.ValueSizeConstraint(1, MAX))), + namedtype.NamedType( + 'status', univ.SequenceOf( + componentType=PKIStatusInfo(), + sizeSpec=constraint.ValueSizeConstraint(1, MAX) + ) + ), namedtype.OptionalNamedType( 'revCerts', univ.SequenceOf(componentType=rfc2511.CertId()).subtype( sizeSpec=constraint.ValueSizeConstraint(1, MAX), -- cgit v1.2.3 From 837615d54a430db7a9fc707d3bd5bf36f420e4a1 Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Thu, 19 Sep 2019 00:45:41 -0400 Subject: Add support for RFC 5934 (#64) --- CHANGES.txt | 3 +- pyasn1_modules/rfc5934.py | 786 ++++++++++++++++++++++++++++++++++++++++++++++ tests/__main__.py | 1 + tests/test_rfc5934.py | 304 ++++++++++++++++++ 4 files changed, 1093 insertions(+), 1 deletion(-) create mode 100644 pyasn1_modules/rfc5934.py create mode 100644 tests/test_rfc5934.py diff --git a/CHANGES.txt b/CHANGES.txt index bacb11e..055163d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,5 @@ -Revision 0.2.7, released XX-08-2019 +Revision 0.2.7, released XX-09-2019 ----------------------------------- - Added maps for use with openType to RFC 3565 @@ -28,6 +28,7 @@ Revision 0.2.7, released XX-08-2019 RFC 5084, RFC 5480, RFC 5940, RFC 5958, RFC 6019, and RFC 8520 - Added RFC5990 providing RSA-KEM Key Transport Algorithm in the CMS - Fixed malformed `rfc6402.RevRepContent` data structure layout +- Added RFC5934 providing Trust Anchor Management Protocol (TAMP) Revision 0.2.6, released 31-07-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc5934.py b/pyasn1_modules/rfc5934.py new file mode 100644 index 0000000..e3ad247 --- /dev/null +++ b/pyasn1_modules/rfc5934.py @@ -0,0 +1,786 @@ +# This file is being contributed to 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 +# +# Trust Anchor Format +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc5934.txt + +from pyasn1.type import univ, char, namedtype, namedval, tag, constraint, useful + +from pyasn1_modules import rfc2985 +from pyasn1_modules import rfc5280 +from pyasn1_modules import rfc5652 +from pyasn1_modules import rfc5914 + +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 2985 + +SingleAttribute = rfc2985.SingleAttribute + + +# Imports from RFC5914 + +CertPathControls = rfc5914.CertPathControls + +TrustAnchorChoice = rfc5914.TrustAnchorChoice + +TrustAnchorTitle = rfc5914.TrustAnchorTitle + + +# Imports from RFC 5280 + +AlgorithmIdentifier = rfc5280.AlgorithmIdentifier + +AnotherName = rfc5280.AnotherName + +Attribute = rfc5280.Attribute + +Certificate = rfc5280.Certificate + +CertificateSerialNumber = rfc5280.CertificateSerialNumber + +Extension = rfc5280.Extension + +Extensions = rfc5280.Extensions + +KeyIdentifier = rfc5280.KeyIdentifier + +Name = rfc5280.Name + +SubjectPublicKeyInfo = rfc5280.SubjectPublicKeyInfo + +TBSCertificate = rfc5280.TBSCertificate + +Validity = rfc5280.Validity + + +# Object Identifier Arc for TAMP Message Content Types + +id_tamp = univ.ObjectIdentifier('2.16.840.1.101.2.1.2.77') + + +# TAMP Status Query Message + +id_ct_TAMP_statusQuery = _OID(id_tamp, 1) + + +class TAMPVersion(univ.Integer): + pass + +TAMPVersion.namedValues = namedval.NamedValues( + ('v1', 1), + ('v2', 2) +) + + +class TerseOrVerbose(univ.Enumerated): + pass + +TerseOrVerbose.namedValues = namedval.NamedValues( + ('terse', 1), + ('verbose', 2) +) + + +class HardwareSerialEntry(univ.Choice): + pass + +HardwareSerialEntry.componentType = namedtype.NamedTypes( + namedtype.NamedType('all', univ.Null()), + namedtype.NamedType('single', univ.OctetString()), + namedtype.NamedType('block', univ.Sequence(componentType=namedtype.NamedTypes( + namedtype.NamedType('low', univ.OctetString()), + namedtype.NamedType('high', univ.OctetString()) + )) + ) +) + + +class HardwareModules(univ.Sequence): + pass + +HardwareModules.componentType = namedtype.NamedTypes( + namedtype.NamedType('hwType', univ.ObjectIdentifier()), + namedtype.NamedType('hwSerialEntries', univ.SequenceOf( + componentType=HardwareSerialEntry()).subtype( + subtypeSpec=constraint.ValueSizeConstraint(1, MAX))) +) + + +class HardwareModuleIdentifierList(univ.SequenceOf): + pass + +HardwareModuleIdentifierList.componentType = HardwareModules() +HardwareModuleIdentifierList.subtypeSpec=constraint.ValueSizeConstraint(1, MAX) + + +class Community(univ.ObjectIdentifier): + pass + + +class CommunityIdentifierList(univ.SequenceOf): + pass + +CommunityIdentifierList.componentType = Community() +CommunityIdentifierList.subtypeSpec=constraint.ValueSizeConstraint(0, MAX) + + +class TargetIdentifier(univ.Choice): + pass + +TargetIdentifier.componentType = namedtype.NamedTypes( + namedtype.NamedType('hwModules', HardwareModuleIdentifierList().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.NamedType('communities', CommunityIdentifierList().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))), + namedtype.NamedType('allModules', univ.Null().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))), + namedtype.NamedType('uri', char.IA5String().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4))), + namedtype.NamedType('otherName', AnotherName().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 5))) +) + + +class SeqNumber(univ.Integer): + pass + +SeqNumber.subtypeSpec = constraint.ValueRangeConstraint(0, 9223372036854775807) + + +class TAMPMsgRef(univ.Sequence): + pass + +TAMPMsgRef.componentType = namedtype.NamedTypes( + namedtype.NamedType('target', TargetIdentifier()), + namedtype.NamedType('seqNum', SeqNumber()) +) + + +class TAMPStatusQuery(univ.Sequence): + pass + +TAMPStatusQuery.componentType = namedtype.NamedTypes( + namedtype.DefaultedNamedType('version', TAMPVersion().subtype( + implicitTag=tag.Tag(tag.tagClassContext, + tag.tagFormatSimple, 0)).subtype(value='v2')), + namedtype.DefaultedNamedType('terse', TerseOrVerbose().subtype( + implicitTag=tag.Tag(tag.tagClassContext, + tag.tagFormatSimple, 1)).subtype(value='verbose')), + namedtype.NamedType('query', TAMPMsgRef()) +) + + +tamp_status_query = rfc5652.ContentInfo() +tamp_status_query['contentType'] = id_ct_TAMP_statusQuery +tamp_status_query['content'] = TAMPStatusQuery() + + +# TAMP Status Response Message + +id_ct_TAMP_statusResponse = _OID(id_tamp, 2) + + +class KeyIdentifiers(univ.SequenceOf): + pass + +KeyIdentifiers.componentType = KeyIdentifier() +KeyIdentifiers.subtypeSpec=constraint.ValueSizeConstraint(1, MAX) + + +class TrustAnchorChoiceList(univ.SequenceOf): + pass + +TrustAnchorChoiceList.componentType = TrustAnchorChoice() +TrustAnchorChoiceList.subtypeSpec=constraint.ValueSizeConstraint(1, MAX) + + +class TAMPSequenceNumber(univ.Sequence): + pass + +TAMPSequenceNumber.componentType = namedtype.NamedTypes( + namedtype.NamedType('keyId', KeyIdentifier()), + namedtype.NamedType('seqNumber', SeqNumber()) +) + + +class TAMPSequenceNumbers(univ.SequenceOf): + pass + +TAMPSequenceNumbers.componentType = TAMPSequenceNumber() +TAMPSequenceNumbers.subtypeSpec=constraint.ValueSizeConstraint(1, MAX) + + +class TerseStatusResponse(univ.Sequence): + pass + +TerseStatusResponse.componentType = namedtype.NamedTypes( + namedtype.NamedType('taKeyIds', KeyIdentifiers()), + namedtype.OptionalNamedType('communities', CommunityIdentifierList()) +) + + +class VerboseStatusResponse(univ.Sequence): + pass + +VerboseStatusResponse.componentType = namedtype.NamedTypes( + namedtype.NamedType('taInfo', TrustAnchorChoiceList()), + namedtype.OptionalNamedType('continPubKeyDecryptAlg', + AlgorithmIdentifier().subtype(implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.OptionalNamedType('communities', + CommunityIdentifierList().subtype(implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.OptionalNamedType('tampSeqNumbers', + TAMPSequenceNumbers().subtype(implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 2))) +) + + +class StatusResponse(univ.Choice): + pass + +StatusResponse.componentType = namedtype.NamedTypes( + namedtype.NamedType('terseResponse', TerseStatusResponse().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))), + namedtype.NamedType('verboseResponse', VerboseStatusResponse().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))) +) + + +class TAMPStatusResponse(univ.Sequence): + pass + +TAMPStatusResponse.componentType = namedtype.NamedTypes( + namedtype.DefaultedNamedType('version', TAMPVersion().subtype( + implicitTag=tag.Tag(tag.tagClassContext, + tag.tagFormatSimple, 0)).subtype(value='v2')), + namedtype.NamedType('query', TAMPMsgRef()), + namedtype.NamedType('response', StatusResponse()), + namedtype.DefaultedNamedType('usesApex', univ.Boolean().subtype(value=1)) +) + + +tamp_status_response = rfc5652.ContentInfo() +tamp_status_response['contentType'] = id_ct_TAMP_statusResponse +tamp_status_response['content'] = TAMPStatusResponse() + + +# Trust Anchor Update Message + +id_ct_TAMP_update = _OID(id_tamp, 3) + + +class TBSCertificateChangeInfo(univ.Sequence): + pass + +TBSCertificateChangeInfo.componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('serialNumber', CertificateSerialNumber()), + namedtype.OptionalNamedType('signature', AlgorithmIdentifier().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.OptionalNamedType('issuer', Name().subtype(implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.OptionalNamedType('validity', Validity().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))), + namedtype.OptionalNamedType('subject', Name().subtype(implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 3))), + namedtype.NamedType('subjectPublicKeyInfo', SubjectPublicKeyInfo().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4))), + namedtype.OptionalNamedType('exts', Extensions().subtype(explicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 5))) +) + + +class TrustAnchorChangeInfo(univ.Sequence): + pass + +TrustAnchorChangeInfo.componentType = namedtype.NamedTypes( + namedtype.NamedType('pubKey', SubjectPublicKeyInfo()), + namedtype.OptionalNamedType('keyId', KeyIdentifier()), + namedtype.OptionalNamedType('taTitle', TrustAnchorTitle()), + namedtype.OptionalNamedType('certPath', CertPathControls()), + namedtype.OptionalNamedType('exts', Extensions().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))) +) + + +class TrustAnchorChangeInfoChoice(univ.Choice): + pass + +TrustAnchorChangeInfoChoice.componentType = namedtype.NamedTypes( + namedtype.NamedType('tbsCertChange', TBSCertificateChangeInfo().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))), + namedtype.NamedType('taChange', TrustAnchorChangeInfo().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))) +) + + +class TrustAnchorUpdate(univ.Choice): + pass + +TrustAnchorUpdate.componentType = namedtype.NamedTypes( + namedtype.NamedType('add', TrustAnchorChoice().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.NamedType('remove', SubjectPublicKeyInfo().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))), + namedtype.NamedType('change', TrustAnchorChangeInfoChoice().subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3))) +) + + +class TAMPUpdate(univ.Sequence): + pass + +TAMPUpdate.componentType = namedtype.NamedTypes( + namedtype.DefaultedNamedType('version', + TAMPVersion().subtype(implicitTag=tag.Tag(tag.tagClassContext, + tag.tagFormatSimple, 0)).subtype(value='v2')), + namedtype.DefaultedNamedType('terse', + TerseOrVerbose().subtype(implicitTag=tag.Tag(tag.tagClassContext, + tag.tagFormatSimple, 1)).subtype(value='verbose')), + namedtype.NamedType('msgRef', TAMPMsgRef()), + namedtype.NamedType('updates', + univ.SequenceOf(componentType=TrustAnchorUpdate()).subtype( + subtypeSpec=constraint.ValueSizeConstraint(1, MAX))), + namedtype.OptionalNamedType('tampSeqNumbers', + TAMPSequenceNumbers().subtype(implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 2))) +) + + +tamp_update = rfc5652.ContentInfo() +tamp_update['contentType'] = id_ct_TAMP_update +tamp_update['content'] = TAMPUpdate() + + +# Trust Anchor Update Confirm Message + +id_ct_TAMP_updateConfirm = _OID(id_tamp, 4) + + +class StatusCode(univ.Enumerated): + pass + +StatusCode.namedValues = namedval.NamedValues( + ('success', 0), + ('decodeFailure', 1), + ('badContentInfo', 2), + ('badSignedData', 3), + ('badEncapContent', 4), + ('badCertificate', 5), + ('badSignerInfo', 6), + ('badSignedAttrs', 7), + ('badUnsignedAttrs', 8), + ('missingContent', 9), + ('noTrustAnchor', 10), + ('notAuthorized', 11), + ('badDigestAlgorithm', 12), + ('badSignatureAlgorithm', 13), + ('unsupportedKeySize', 14), + ('unsupportedParameters', 15), + ('signatureFailure', 16), + ('insufficientMemory', 17), + ('unsupportedTAMPMsgType', 18), + ('apexTAMPAnchor', 19), + ('improperTAAddition', 20), + ('seqNumFailure', 21), + ('contingencyPublicKeyDecrypt', 22), + ('incorrectTarget', 23), + ('communityUpdateFailed', 24), + ('trustAnchorNotFound', 25), + ('unsupportedTAAlgorithm', 26), + ('unsupportedTAKeySize', 27), + ('unsupportedContinPubKeyDecryptAlg', 28), + ('missingSignature', 29), + ('resourcesBusy', 30), + ('versionNumberMismatch', 31), + ('missingPolicySet', 32), + ('revokedCertificate', 33), + ('unsupportedTrustAnchorFormat', 34), + ('improperTAChange', 35), + ('malformed', 36), + ('cmsError', 37), + ('unsupportedTargetIdentifier', 38), + ('other', 127) +) + + +class StatusCodeList(univ.SequenceOf): + pass + +StatusCodeList.componentType = StatusCode() +StatusCodeList.subtypeSpec=constraint.ValueSizeConstraint(1, MAX) + + +class TerseUpdateConfirm(StatusCodeList): + pass + + +class VerboseUpdateConfirm(univ.Sequence): + pass + +VerboseUpdateConfirm.componentType = namedtype.NamedTypes( + namedtype.NamedType('status', StatusCodeList()), + namedtype.NamedType('taInfo', TrustAnchorChoiceList()), + namedtype.OptionalNamedType('tampSeqNumbers', TAMPSequenceNumbers()), + namedtype.DefaultedNamedType('usesApex', univ.Boolean().subtype(value=1)) +) + + +class UpdateConfirm(univ.Choice): + pass + +UpdateConfirm.componentType = namedtype.NamedTypes( + namedtype.NamedType('terseConfirm', TerseUpdateConfirm().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.NamedType('verboseConfirm', VerboseUpdateConfirm().subtype( + implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))) +) + + +class TAMPUpdateConfirm(univ.Sequence): + pass + +TAMPUpdateConfirm.componentType = namedtype.NamedTypes( + namedtype.DefaultedNamedType('version', TAMPVersion().subtype( + implicitTag=tag.Tag(tag.tagClassContext, + tag.tagFormatSimple, 0)).subtype(value='v2')), + namedtype.NamedType('update', TAMPMsgRef()), + namedtype.NamedType('confirm', UpdateConfirm()) +) + + +tamp_update_confirm = rfc5652.ContentInfo() +tamp_update_confirm['contentType'] = id_ct_TAMP_updateConfirm +tamp_update_confirm['content'] = TAMPUpdateConfirm() + + +# Apex Trust Anchor Update Message + +id_ct_TAMP_apexUpdate = _OID(id_tamp, 5) + + +class TAMPApexUpdate(univ.Sequence): + pass + +TAMPApexUpdate.componentType = namedtype.NamedTypes( + namedtype.DefaultedNamedType('version', + TAMPVersion().subtype(implicitTag=tag.Tag(tag.tagClassContext, + tag.tagFormatSimple, 0)).subtype(value='v2')), + namedtype.DefaultedNamedType('terse', + TerseOrVerbose().subtype(implicitTag=tag.Tag(tag.tagClassContext, + tag.tagFormatSimple, 1)).subtype(value='verbose')), + namedtype.NamedType('msgRef', TAMPMsgRef()), + namedtype.NamedType('clearTrustAnchors', univ.Boolean()), + namedtype.NamedType('clearCommunities', univ.Boolean()), + namedtype.OptionalNamedType('seqNumber', SeqNumber()), + namedtype.NamedType('apexTA', TrustAnchorChoice()) +) + + +tamp_apex_update = rfc5652.ContentInfo() +tamp_apex_update['contentType'] = id_ct_TAMP_apexUpdate +tamp_apex_update['content'] = TAMPApexUpdate() + + +# Apex Trust Anchor Update Confirm Message + +id_ct_TAMP_apexUpdateConfirm = _OID(id_tamp, 6) + + +class TerseApexUpdateConfirm(StatusCode): + pass + + +class VerboseApexUpdateConfirm(univ.Sequence): + pass + +VerboseApexUpdateConfirm.componentType = namedtype.NamedTypes( + namedtype.NamedType('status', StatusCode()), + namedtype.NamedType('taInfo', TrustAnchorChoiceList()), + namedtype.OptionalNamedType('communities', + CommunityIdentifierList().subtype(implicitTag=tag.Tag(tag.tagClassContext, + tag.tagFormatSimple, 0))), + namedtype.OptionalNamedType('tampSeqNumbers', + TAMPSequenceNumbers().subtype(implicitTag=tag.Tag(tag.tagClassContext, + tag.tagFormatSimple, 1))) +) + + +class ApexUpdateConfirm(univ.Choice): + pass + +ApexUpdateConfirm.componentType = namedtype.NamedTypes( + namedtype.NamedType('terseApexConfirm', + TerseApexUpdateConfirm().subtype(implicitTag=tag.Tag(tag.tagClassContext, + tag.tagFormatSimple, 0))), + namedtype.NamedType('verboseApexConfirm', + VerboseApexUpdateConfirm().subtype(implicitTag=tag.Tag(tag.tagClassContext, + tag.tagFormatConstructed, 1))) +) + + +class TAMPApexUpdateConfirm(univ.Sequence): + pass + +TAMPApexUpdateConfirm.componentType = namedtype.NamedTypes( + namedtype.DefaultedNamedType('version', + TAMPVersion().subtype(implicitTag=tag.Tag(tag.tagClassContext, + tag.tagFormatSimple, 0)).subtype(value='v2')), + namedtype.NamedType('apexReplace', TAMPMsgRef()), + namedtype.NamedType('apexConfirm', ApexUpdateConfirm()) +) + + +tamp_apex_update_confirm = rfc5652.ContentInfo() +tamp_apex_update_confirm['contentType'] = id_ct_TAMP_apexUpdateConfirm +tamp_apex_update_confirm['content'] = TAMPApexUpdateConfirm() + + +# Community Update Message + +id_ct_TAMP_communityUpdate = _OID(id_tamp, 7) + + +class CommunityUpdates(univ.Sequence): + pass + +CommunityUpdates.componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('remove', + CommunityIdentifierList().subtype(implicitTag=tag.Tag(tag.tagClassContext, + tag.tagFormatSimple, 1))), + namedtype.OptionalNamedType('add', + CommunityIdentifierList().subtype(implicitTag=tag.Tag(tag.tagClassContext, + tag.tagFormatSimple, 2))) +) + + +class TAMPCommunityUpdate(univ.Sequence): + pass + +TAMPCommunityUpdate.componentType = namedtype.NamedTypes( + namedtype.DefaultedNamedType('version', + TAMPVersion().subtype(implicitTag=tag.Tag(tag.tagClassContext, + tag.tagFormatSimple, 0)).subtype(value='v2')), + namedtype.DefaultedNamedType('terse', + TerseOrVerbose().subtype(implicitTag=tag.Tag(tag.tagClassContext, + tag.tagFormatSimple, 1)).subtype(value='verbose')), + namedtype.NamedType('msgRef', TAMPMsgRef()), + namedtype.NamedType('updates', CommunityUpdates()) +) + + +tamp_community_update = rfc5652.ContentInfo() +tamp_community_update['contentType'] = id_ct_TAMP_communityUpdate +tamp_community_update['content'] = TAMPCommunityUpdate() + + +# Community Update Confirm Message + +id_ct_TAMP_communityUpdateConfirm = _OID(id_tamp, 8) + + +class TerseCommunityConfirm(StatusCode): + pass + + +class VerboseCommunityConfirm(univ.Sequence): + pass + +VerboseCommunityConfirm.componentType = namedtype.NamedTypes( + namedtype.NamedType('status', StatusCode()), + namedtype.OptionalNamedType('communities', CommunityIdentifierList()) +) + + +class CommunityConfirm(univ.Choice): + pass + +CommunityConfirm.componentType = namedtype.NamedTypes( + namedtype.NamedType('terseCommConfirm', + TerseCommunityConfirm().subtype(implicitTag=tag.Tag(tag.tagClassContext, + tag.tagFormatSimple, 0))), + namedtype.NamedType('verboseCommConfirm', + VerboseCommunityConfirm().subtype(implicitTag=tag.Tag(tag.tagClassContext, + tag.tagFormatConstructed, 1))) +) + + +class TAMPCommunityUpdateConfirm(univ.Sequence): + pass + +TAMPCommunityUpdateConfirm.componentType = namedtype.NamedTypes( + namedtype.DefaultedNamedType('version', + TAMPVersion().subtype(implicitTag=tag.Tag(tag.tagClassContext, + tag.tagFormatSimple, 0)).subtype(value='v2')), + namedtype.NamedType('update', TAMPMsgRef()), + namedtype.NamedType('commConfirm', CommunityConfirm()) +) + + +tamp_community_update_confirm = rfc5652.ContentInfo() +tamp_community_update_confirm['contentType'] = id_ct_TAMP_communityUpdateConfirm +tamp_community_update_confirm['content'] = TAMPCommunityUpdateConfirm() + + +# Sequence Number Adjust Message + +id_ct_TAMP_seqNumAdjust = _OID(id_tamp, 10) + + + +class SequenceNumberAdjust(univ.Sequence): + pass + +SequenceNumberAdjust.componentType = namedtype.NamedTypes( + namedtype.DefaultedNamedType('version', + TAMPVersion().subtype(implicitTag=tag.Tag(tag.tagClassContext, + tag.tagFormatSimple, 0)).subtype(value='v2')), + namedtype.NamedType('msgRef', TAMPMsgRef()) +) + + +tamp_sequence_number_adjust = rfc5652.ContentInfo() +tamp_sequence_number_adjust['contentType'] = id_ct_TAMP_seqNumAdjust +tamp_sequence_number_adjust['content'] = SequenceNumberAdjust() + + +# Sequence Number Adjust Confirm Message + +id_ct_TAMP_seqNumAdjustConfirm = _OID(id_tamp, 11) + + +class SequenceNumberAdjustConfirm(univ.Sequence): + pass + +SequenceNumberAdjustConfirm.componentType = namedtype.NamedTypes( + namedtype.DefaultedNamedType('version', + TAMPVersion().subtype(implicitTag=tag.Tag(tag.tagClassContext, + tag.tagFormatSimple, 0)).subtype(value='v2')), + namedtype.NamedType('adjust', TAMPMsgRef()), + namedtype.NamedType('status', StatusCode()) +) + + +tamp_sequence_number_adjust_confirm = rfc5652.ContentInfo() +tamp_sequence_number_adjust_confirm['contentType'] = id_ct_TAMP_seqNumAdjustConfirm +tamp_sequence_number_adjust_confirm['content'] = SequenceNumberAdjustConfirm() + + +# TAMP Error Message + +id_ct_TAMP_error = _OID(id_tamp, 9) + + +class TAMPError(univ.Sequence): + pass + +TAMPError.componentType = namedtype.NamedTypes( + namedtype.DefaultedNamedType('version', + TAMPVersion().subtype(implicitTag=tag.Tag(tag.tagClassContext, + tag.tagFormatSimple, 0)).subtype(value='v2')), + namedtype.NamedType('msgType', univ.ObjectIdentifier()), + namedtype.NamedType('status', StatusCode()), + namedtype.OptionalNamedType('msgRef', TAMPMsgRef()) +) + + +tamp_error = rfc5652.ContentInfo() +tamp_error['contentType'] = id_ct_TAMP_error +tamp_error['content'] = TAMPError() + + +# Object Identifier Arc for Attributes + +id_attributes = univ.ObjectIdentifier('2.16.840.1.101.2.1.5') + + +# contingency-public-key-decrypt-key unsigned attribute + +id_aa_TAMP_contingencyPublicKeyDecryptKey = _OID(id_attributes, 63) + + +class PlaintextSymmetricKey(univ.OctetString): + pass + + +contingency_public_key_decrypt_key = Attribute() +contingency_public_key_decrypt_key['type'] = id_aa_TAMP_contingencyPublicKeyDecryptKey +contingency_public_key_decrypt_key['values'][0] = PlaintextSymmetricKey() + + +# id-pe-wrappedApexContinKey extension + +id_pe_wrappedApexContinKey =univ.ObjectIdentifier('1.3.6.1.5.5.7.1.20') + + +class ApexContingencyKey(univ.Sequence): + pass + +ApexContingencyKey.componentType = namedtype.NamedTypes( + namedtype.NamedType('wrapAlgorithm', AlgorithmIdentifier()), + namedtype.NamedType('wrappedContinPubKey', univ.OctetString()) +) + + +wrappedApexContinKey = Extension() +wrappedApexContinKey['extnID'] = id_pe_wrappedApexContinKey +wrappedApexContinKey['critical'] = 0 +wrappedApexContinKey['extnValue'] = univ.OctetString() + + +# Add to the map of CMS Content Type OIDs to Content Types in +# rfc5652.py + +_cmsContentTypesMapUpdate = { + id_ct_TAMP_statusQuery: TAMPStatusQuery(), + id_ct_TAMP_statusResponse: TAMPStatusResponse(), + id_ct_TAMP_update: TAMPUpdate(), + id_ct_TAMP_updateConfirm: TAMPUpdateConfirm(), + id_ct_TAMP_apexUpdate: TAMPApexUpdate(), + id_ct_TAMP_apexUpdateConfirm: TAMPApexUpdateConfirm(), + id_ct_TAMP_communityUpdate: TAMPCommunityUpdate(), + id_ct_TAMP_communityUpdateConfirm: TAMPCommunityUpdateConfirm(), + id_ct_TAMP_seqNumAdjust: SequenceNumberAdjust(), + id_ct_TAMP_seqNumAdjustConfirm: SequenceNumberAdjustConfirm(), + id_ct_TAMP_error: TAMPError(), +} + +rfc5652.cmsContentTypesMap.update(_cmsContentTypesMapUpdate) + + +# Add to the map of CMS Attribute OIDs to Attribute Values in +# rfc5652.py + +_cmsAttributesMapUpdate = { + id_aa_TAMP_contingencyPublicKeyDecryptKey: PlaintextSymmetricKey(), +} + +rfc5652.cmsAttributesMap.update(_cmsAttributesMapUpdate) + + +# Add to the map of Certificate Extension OIDs to Extensions in +# rfc5280.py + +_certificateExtensionsMap = { + id_pe_wrappedApexContinKey: ApexContingencyKey(), +} + +rfc5280.certificateExtensionsMap.update(_certificateExtensionsMap) diff --git a/tests/__main__.py b/tests/__main__.py index 5f5c12d..c85b6d4 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -42,6 +42,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc5652.suite', 'tests.test_rfc5914.suite', 'tests.test_rfc5915.suite', + 'tests.test_rfc5934.suite', 'tests.test_rfc5940.suite', 'tests.test_rfc5958.suite', 'tests.test_rfc5990.suite', diff --git a/tests/test_rfc5934.py b/tests/test_rfc5934.py new file mode 100644 index 0000000..662ccf1 --- /dev/null +++ b/tests/test_rfc5934.py @@ -0,0 +1,304 @@ +# +# This file is part of pyasn1-modules software. +# +# Created by Russ Housley +# Acknowledgement to Carl Wallace for the test messages. +# 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 rfc5652 +from pyasn1_modules import rfc5934 + +try: + import unittest2 as unittest + +except ImportError: + import unittest + + +class TAMPStatusResponseTestCase(unittest.TestCase): + tsr_pem_text = """\ +MIIU/QYJKoZIhvcNAQcCoIIU7jCCFOoCAQMxDTALBglghkgBZQMEAgEwgg/GBgpghkgBZQIB +Ak0CoIIPtgSCD7Iwgg+uMAiDAAIEXXp3f6GCD50wgg+ZooIFFTCCBREwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQDALMH2jTus/z881nG+uHQiB+xwQRX8q0DjB6rBw9if/tpM +Or8/yNgoe0s2AcCsRSXD0g4Kj4UYZBA9GhNwKm+O19yNk7NBDzghza2rwj0qBdNXETcNzYxR ++ZPjzEZJIY4UtM3LFD44zXIx7qsS8mXqNC5WXf/uY3XLbbqRNPye8/QtHL5QxELfWYj/arP6 +qGw9y1ZxcQWWu5+A5YBFWWdBsOvDrWCkgHUGF5wO9EPgmQ4b+3/1s8yygYKx/TLBuL5BpGS1 +YDpaUTCMzt5BLBlHXEkQZLl0qYdBr31uusG4ob9lMToEZ/m1u46SigBjuLHmjDhfg/9Q1Tui +XWuyEMxjAgMBAAEEFEl0uwxeunr+AlTve6DGlcYJgHCWMIID0TBbMQswCQYDVQQGEwJVUzEY +MBYGA1UEChMPVS5TLiBHb3Zlcm5tZW50MQwwCgYDVQQLEwNEb0QxDDAKBgNVBAsTA1BLSTEW +MBQGA1UEAxMNRG9EIFJvb3QgQ0EgMqCCA3AwggJYoAMCAQICAQUwDQYJKoZIhvcNAQEFBQAw +WzELMAkGA1UEBhMCVVMxGDAWBgNVBAoTD1UuUy4gR292ZXJubWVudDEMMAoGA1UECxMDRG9E +MQwwCgYDVQQLEwNQS0kxFjAUBgNVBAMTDURvRCBSb290IENBIDIwHhcNMDQxMjEzMTUwMDEw +WhcNMjkxMjA1MTUwMDEwWjBbMQswCQYDVQQGEwJVUzEYMBYGA1UEChMPVS5TLiBHb3Zlcm5t +ZW50MQwwCgYDVQQLEwNEb0QxDDAKBgNVBAsTA1BLSTEWMBQGA1UEAxMNRG9EIFJvb3QgQ0Eg +MjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMAswfaNO6z/PzzWcb64dCIH7HBB +FfyrQOMHqsHD2J/+2kw6vz/I2Ch7SzYBwKxFJcPSDgqPhRhkED0aE3Aqb47X3I2Ts0EPOCHN +ravCPSoF01cRNw3NjFH5k+PMRkkhjhS0zcsUPjjNcjHuqxLyZeo0LlZd/+5jdcttupE0/J7z +9C0cvlDEQt9ZiP9qs/qobD3LVnFxBZa7n4DlgEVZZ0Gw68OtYKSAdQYXnA70Q+CZDhv7f/Wz +zLKBgrH9MsG4vkGkZLVgOlpRMIzO3kEsGUdcSRBkuXSph0GvfW66wbihv2UxOgRn+bW7jpKK +AGO4seaMOF+D/1DVO6Jda7IQzGMCAwEAAaM/MD0wHQYDVR0OBBYEFEl0uwxeunr+AlTve6DG +lcYJgHCWMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IB +AQCYkY0/ici79cBpcyk7Nay6swh2PXAJkumERCEBfRR2G+5RbB2NFTctezFp9JpEuK9GzDT6 +I8sDJxnSgyF1K+fgG5km3IRAleio0sz2WFxm7z9KlxCCHboKot1bBiudp2RO6y4BNaS0PxOt +VeTVc6hpmxHxmPIxHm9A1Ph4n46RoG9wBJBmqgYrzuF6krV94eDRluehOi3MsZ0fBUTth5nT +TRpwOcEEDOV+2fGv1yAO8SJ6JaRzmcw/pAcnlqiile2CuRbTnguHwsHyiPVi32jfx7xpUe2x +XNxUVCkPCTmarAPB2wxNrm8KehZJ8b+R0jiU0/aVLLdsyUK2jcqQjYXZooIFGDCCBRQwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCp7BRyiuhLcKPaEAOEpvunNg0qOlIWvzAV +UoYFRyDPqqbNdcRkbu/xYCPLCmZArrTIaCoAUWhJN+lZMk2VvEMn6UCNOhDOFLxDGKH53szn +hXZzXhgaI1u9Px/y7Y0ZzAPRQKSPpyACTCdaeTb2ozchjgBaBhbK01WWbzEpu3IOy+JIUfLU +N6Q11m/uF7OxBqsLGYboI20xGyh4ZcXeYlK8wX3r7qBdVAT7sssrsiNUkYJM8L+6dEA7DARF +gGdcxeuiV8MafwotvX+53MGZsMgH5AyGNpQ6JS/yfeaXPBuUtJdZBsk65AvZ6un8O3M0b/3n +mOTzocKQXxz1Py7XGdN/AgMBAAEEFGyKlKJ3sYByHYF6Fqry3M5m7kXAMIID1DBbMQswCQYD +VQQGEwJVUzEYMBYGA1UEChMPVS5TLiBHb3Zlcm5tZW50MQwwCgYDVQQLEwNEb0QxDDAKBgNV +BAsTA1BLSTEWMBQGA1UEAxMNRG9EIFJvb3QgQ0EgM6CCA3MwggJboAMCAQICAQEwDQYJKoZI +hvcNAQELBQAwWzELMAkGA1UEBhMCVVMxGDAWBgNVBAoTD1UuUy4gR292ZXJubWVudDEMMAoG +A1UECxMDRG9EMQwwCgYDVQQLEwNQS0kxFjAUBgNVBAMTDURvRCBSb290IENBIDMwHhcNMTIw +MzIwMTg0NjQxWhcNMjkxMjMwMTg0NjQxWjBbMQswCQYDVQQGEwJVUzEYMBYGA1UEChMPVS5T +LiBHb3Zlcm5tZW50MQwwCgYDVQQLEwNEb0QxDDAKBgNVBAsTA1BLSTEWMBQGA1UEAxMNRG9E +IFJvb3QgQ0EgMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKnsFHKK6Etwo9oQ +A4Sm+6c2DSo6Uha/MBVShgVHIM+qps11xGRu7/FgI8sKZkCutMhoKgBRaEk36VkyTZW8Qyfp +QI06EM4UvEMYofnezOeFdnNeGBojW70/H/LtjRnMA9FApI+nIAJMJ1p5NvajNyGOAFoGFsrT +VZZvMSm7cg7L4khR8tQ3pDXWb+4Xs7EGqwsZhugjbTEbKHhlxd5iUrzBfevuoF1UBPuyyyuy +I1SRgkzwv7p0QDsMBEWAZ1zF66JXwxp/Ci29f7ncwZmwyAfkDIY2lDolL/J95pc8G5S0l1kG +yTrkC9nq6fw7czRv/eeY5POhwpBfHPU/LtcZ038CAwEAAaNCMEAwHQYDVR0OBBYEFGyKlKJ3 +sYByHYF6Fqry3M5m7kXAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MA0GCSqG +SIb3DQEBCwUAA4IBAQCfcaTAtpbSgEOgSOkfdgT5xTytZhhYY5vDtuhoioVaQmYStNLmi4h/ +h/SY9ajGCckf8Cwf7IK49KVHOMEzK99Mfpq+Cwuxyw98UCgQz4qNoum6rIbX1LGTXyKPlgW0 +Tgx1kX3T8ueUwpQUdk+PDKsQh1gyhQd1hhILXupTtArITISSH+voQYY8uvROQUrRbFhHQcOG +WvLu6fKYJ4LqLjbW+AZegvGgUpNECbrSqRlaWKOoXSBtT2T4MIcbkBNIgc3KkMcNwdSYP47y +DldoMxKOmQmx8OT2EPQ28km96qM4yFZBI4Oa36EbNXzrP0Gz9W9LOl6ub5N2mNLxmZ1FxI5y +ooIFYDCCBVwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ3HcYEBAYYEH753gQ +D/iEd3DvLW5VOxGmmVI/bfS9oZf6Nh5uREIRyFP+dYabXjcSiKJ92XEI1Ek1cc5Gz1vQWY5l +H+tCPcoO3EyQ2FRpz144siBg3YNRLt/b1Vs4kVotz5oztG+WkOV2FGJDaYQQz1RB+TXqntRa +l51eEFm94OTDWYnX3vJ5sIdrAsBZoSoAghVvaxERAFM0dD304cxWYqLkZegjsYMdWFMIsjMt +lr7lfTOeEFonc1PdXZjiSxFTWJGP6nIR7LuU8g0PUK3yFrUaACQx5RW9FwaQqiSxrN0MUh7w +i2qruPft32O0zpRov16W0ESW8fj0ejoKeRVTAgMBAAEEFKg8CZ1n9thHuqLQ/BhyVohAbZWV +MIID0jBTMQswCQYDVQQGEwJVUzEfMB0GA1UEChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEj +MCEGA1UEAxMaVmFsaWQgRUUgQ2VydGlmaWNhdGUgVGVzdDGgggN5MIICYaADAgECAgEBMA0G +CSqGSIb3DQEBCwUAMEAxCzAJBgNVBAYTAlVTMR8wHQYDVQQKExZUZXN0IENlcnRpZmljYXRl +cyAyMDExMRAwDgYDVQQDEwdHb29kIENBMB4XDTEwMDEwMTA4MzAwMFoXDTMwMTIzMTA4MzAw +MFowUzELMAkGA1UEBhMCVVMxHzAdBgNVBAoTFlRlc3QgQ2VydGlmaWNhdGVzIDIwMTExIzAh +BgNVBAMTGlZhbGlkIEVFIENlcnRpZmljYXRlIFRlc3QxMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA2dx3GBAQGGBB++d4EA/4hHdw7y1uVTsRpplSP230vaGX+jYebkRCEchT +/nWGm143EoiifdlxCNRJNXHORs9b0FmOZR/rQj3KDtxMkNhUac9eOLIgYN2DUS7f29VbOJFa +Lc+aM7RvlpDldhRiQ2mEEM9UQfk16p7UWpedXhBZveDkw1mJ197yebCHawLAWaEqAIIVb2sR +EQBTNHQ99OHMVmKi5GXoI7GDHVhTCLIzLZa+5X0znhBaJ3NT3V2Y4ksRU1iRj+pyEey7lPIN +D1Ct8ha1GgAkMeUVvRcGkKoksazdDFIe8Itqq7j37d9jtM6UaL9eltBElvH49Ho6CnkVUwID +AQABo2swaTAfBgNVHSMEGDAWgBRYAYQkG7wrUpRKPaUQchRR9a86yTAdBgNVHQ4EFgQUqDwJ +nWf22Ee6otD8GHJWiEBtlZUwDgYDVR0PAQH/BAQDAgTwMBcGA1UdIAQQMA4wDAYKYIZIAWUD +AgEwATANBgkqhkiG9w0BAQsFAAOCAQEAHlrZD69ipblSvLzsDGGIEwGqCg8NR6OeqbIXG/ij +2SzSjTi+O7LP1DGIz85p9I7HuXAFUcAGh8aVtPZq+jGeLcQXs+3lehlhGG6M0eQO2pttbI0G +kO4s0XlY2ITNm0HTGOL+kcZfACcUZXsS+i+9qL80ji3PF0xYWzAPLmlmRSYmIZjT85CuKYda +Tsa96Ch+D6CU5v9ctVxP3YphWQ4F0v/FacDTiUrRwuXI9MgIw/0qI0+EAFwsRC2DisI9Isc8 +YPKKeOMbRmXamY/4Y8HUeqBwpnqnEJudrH++FPBEI4dYrBAV6POgvx4lyzarAmlarv/AbrBD +ngieGTynMG6NwqFIMEYwRAYIKwYBBQUHARIBAf8ENTAzMA8GCmCGSAFlAgECTQMKAQEwDwYK +YIZIAWUCAQJNAQoBATAPBgpghkgBZQIBAk0CCgEBAQEAoIIDfTCCA3kwggJhoAMCAQICAQEw +DQYJKoZIhvcNAQELBQAwQDELMAkGA1UEBhMCVVMxHzAdBgNVBAoTFlRlc3QgQ2VydGlmaWNh +dGVzIDIwMTExEDAOBgNVBAMTB0dvb2QgQ0EwHhcNMTAwMTAxMDgzMDAwWhcNMzAxMjMxMDgz +MDAwWjBTMQswCQYDVQQGEwJVUzEfMB0GA1UEChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEj +MCEGA1UEAxMaVmFsaWQgRUUgQ2VydGlmaWNhdGUgVGVzdDEwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQDZ3HcYEBAYYEH753gQD/iEd3DvLW5VOxGmmVI/bfS9oZf6Nh5uREIR +yFP+dYabXjcSiKJ92XEI1Ek1cc5Gz1vQWY5lH+tCPcoO3EyQ2FRpz144siBg3YNRLt/b1Vs4 +kVotz5oztG+WkOV2FGJDaYQQz1RB+TXqntRal51eEFm94OTDWYnX3vJ5sIdrAsBZoSoAghVv +axERAFM0dD304cxWYqLkZegjsYMdWFMIsjMtlr7lfTOeEFonc1PdXZjiSxFTWJGP6nIR7LuU +8g0PUK3yFrUaACQx5RW9FwaQqiSxrN0MUh7wi2qruPft32O0zpRov16W0ESW8fj0ejoKeRVT +AgMBAAGjazBpMB8GA1UdIwQYMBaAFFgBhCQbvCtSlEo9pRByFFH1rzrJMB0GA1UdDgQWBBSo +PAmdZ/bYR7qi0PwYclaIQG2VlTAOBgNVHQ8BAf8EBAMCBPAwFwYDVR0gBBAwDjAMBgpghkgB +ZQMCATABMA0GCSqGSIb3DQEBCwUAA4IBAQAeWtkPr2KluVK8vOwMYYgTAaoKDw1Ho56pshcb ++KPZLNKNOL47ss/UMYjPzmn0jse5cAVRwAaHxpW09mr6MZ4txBez7eV6GWEYbozR5A7am21s +jQaQ7izReVjYhM2bQdMY4v6Rxl8AJxRlexL6L72ovzSOLc8XTFhbMA8uaWZFJiYhmNPzkK4p +h1pOxr3oKH4PoJTm/1y1XE/dimFZDgXS/8VpwNOJStHC5cj0yAjD/SojT4QAXCxELYOKwj0i +xzxg8op44xtGZdqZj/hjwdR6oHCmeqcQm52sf74U8EQjh1isEBXo86C/HiXLNqsCaVqu/8Bu +sEOeCJ4ZPKcwbo3CMYIBiTCCAYUCAQOAFKg8CZ1n9thHuqLQ/BhyVohAbZWVMAsGCWCGSAFl +AwQCAaBMMBkGCSqGSIb3DQEJAzEMBgpghkgBZQIBAk0CMC8GCSqGSIb3DQEJBDEiBCAiPyBP +FFwHJbHgGmoz+54OEJ/ppMyfSoZmbS/nkWfxxjALBgkqhkiG9w0BAQsEggEAHllTg+TMT2ll +zVvrvRDwOwrzr6YIJSt96sLANqOXiqqnvrHDDWTdVMcRX/LccVbm9JP4sGSfGDdwbm3FqB+l +kgSBlejFgjWfF/YVK5OpaVcPGg4DB3oAOwxtn0GVQtKgGkiGQF0r5389mTHYlQzS6BVDG2Oi +sKIe4SBazrBGjnKANf9LEunpWPt15y6QCxiEKnJfPlAqiMuiIhHmXPIHi+d3sYkC+iu+5I68 +2oeLdtBWCDcGh4+DdS6Qqzkpp14MpvzBMdfD3lKcI3NRmY+GmRYaGAiEalh83vggslF7N4SS +iPxQyqz7LIQe9/5ynJV5/CPUDBL9QK2vSCOQaihWCg== +""" + + def setUp(self): + self.asn1Spec = rfc5652.ContentInfo() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.tsr_pem_text) + + layers = { + rfc5652.id_ct_contentInfo: rfc5652.ContentInfo(), + rfc5652.id_signedData: rfc5652.SignedData(), + rfc5934.id_ct_TAMP_statusResponse: rfc5934.TAMPStatusResponse() + } + + getNextLayer = { + rfc5652.id_ct_contentInfo: lambda x: x['contentType'], + rfc5652.id_signedData: lambda x: x['encapContentInfo']['eContentType'], + rfc5934.id_ct_TAMP_statusResponse: lambda x: None + } + + getNextSubstrate = { + rfc5652.id_ct_contentInfo: lambda x: x['content'], + rfc5652.id_signedData: lambda x: x['encapContentInfo']['eContent'], + rfc5934.id_ct_TAMP_statusResponse: lambda x: None + } + + next_layer = rfc5652.id_ct_contentInfo + + while next_layer: + asn1Object, rest = der_decode(substrate, asn1Spec=layers[next_layer]) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + substrate = getNextSubstrate[next_layer](asn1Object) + next_layer = getNextLayer[next_layer](asn1Object) + + def testOpenTypes(self): + substrate = pem.readBase64fromText(self.tsr_pem_text) + asn1Object, rest = der_decode(substrate, + asn1Spec=rfc5652.ContentInfo(), + decodeOpenTypes=True) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + eci = asn1Object['content']['encapContentInfo'] + assert eci['eContentType'] in rfc5652.cmsContentTypesMap.keys() + assert eci['eContentType'] == rfc5934.id_ct_TAMP_statusResponse + tsr, rest = der_decode(eci['eContent'], + asn1Spec=rfc5652.cmsContentTypesMap[eci['eContentType']], + decodeOpenTypes=True) + assert not rest + assert tsr.prettyPrint() + assert der_encode(tsr) == eci['eContent'] + + assert tsr['version'] == 2 + assert tsr['query']['target'] == univ.Null("") + assert tsr['query']['seqNum'] == 1568307071 + assert tsr['usesApex'] == False + + count = 0 + for tai in tsr['response']['verboseResponse']['taInfo']: + count += 1 + assert tai['taInfo']['version'] == 1 + + assert count == 3 + + +class TrustAnchorUpdateTestCase(unittest.TestCase): + tau_pem_text = """\ +MIIGgwYJKoZIhvcNAQcCoIIGdDCCBnACAQMxDTALBglghkgBZQMEAgEwggFMBgpghkgBZQIB +Ak0DoIIBPASCATgwggE0MAiDAAIEXXp3kDCCASaiggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDALMH2jTus/z881nG+uHQiB+xwQRX8q0DjB6rBw9if/tpMOr8/yNgoe0s2AcCs +RSXD0g4Kj4UYZBA9GhNwKm+O19yNk7NBDzghza2rwj0qBdNXETcNzYxR+ZPjzEZJIY4UtM3L +FD44zXIx7qsS8mXqNC5WXf/uY3XLbbqRNPye8/QtHL5QxELfWYj/arP6qGw9y1ZxcQWWu5+A +5YBFWWdBsOvDrWCkgHUGF5wO9EPgmQ4b+3/1s8yygYKx/TLBuL5BpGS1YDpaUTCMzt5BLBlH +XEkQZLl0qYdBr31uusG4ob9lMToEZ/m1u46SigBjuLHmjDhfg/9Q1TuiXWuyEMxjAgMBAAGg +ggN9MIIDeTCCAmGgAwIBAgIBATANBgkqhkiG9w0BAQsFADBAMQswCQYDVQQGEwJVUzEfMB0G +A1UEChMWVGVzdCBDZXJ0aWZpY2F0ZXMgMjAxMTEQMA4GA1UEAxMHR29vZCBDQTAeFw0xMDAx +MDEwODMwMDBaFw0zMDEyMzEwODMwMDBaMFMxCzAJBgNVBAYTAlVTMR8wHQYDVQQKExZUZXN0 +IENlcnRpZmljYXRlcyAyMDExMSMwIQYDVQQDExpWYWxpZCBFRSBDZXJ0aWZpY2F0ZSBUZXN0 +MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANncdxgQEBhgQfvneBAP+IR3cO8t +blU7EaaZUj9t9L2hl/o2Hm5EQhHIU/51hpteNxKIon3ZcQjUSTVxzkbPW9BZjmUf60I9yg7c +TJDYVGnPXjiyIGDdg1Eu39vVWziRWi3PmjO0b5aQ5XYUYkNphBDPVEH5Neqe1FqXnV4QWb3g +5MNZidfe8nmwh2sCwFmhKgCCFW9rEREAUzR0PfThzFZiouRl6COxgx1YUwiyMy2WvuV9M54Q +WidzU91dmOJLEVNYkY/qchHsu5TyDQ9QrfIWtRoAJDHlFb0XBpCqJLGs3QxSHvCLaqu49+3f +Y7TOlGi/XpbQRJbx+PR6Ogp5FVMCAwEAAaNrMGkwHwYDVR0jBBgwFoAUWAGEJBu8K1KUSj2l +EHIUUfWvOskwHQYDVR0OBBYEFKg8CZ1n9thHuqLQ/BhyVohAbZWVMA4GA1UdDwEB/wQEAwIE +8DAXBgNVHSAEEDAOMAwGCmCGSAFlAwIBMAEwDQYJKoZIhvcNAQELBQADggEBAB5a2Q+vYqW5 +Ury87AxhiBMBqgoPDUejnqmyFxv4o9ks0o04vjuyz9QxiM/OafSOx7lwBVHABofGlbT2avox +ni3EF7Pt5XoZYRhujNHkDtqbbWyNBpDuLNF5WNiEzZtB0xji/pHGXwAnFGV7Evovvai/NI4t +zxdMWFswDy5pZkUmJiGY0/OQrimHWk7Gvegofg+glOb/XLVcT92KYVkOBdL/xWnA04lK0cLl +yPTICMP9KiNPhABcLEQtg4rCPSLHPGDyinjjG0Zl2pmP+GPB1HqgcKZ6pxCbnax/vhTwRCOH +WKwQFejzoL8eJcs2qwJpWq7/wG6wQ54Inhk8pzBujcIxggGJMIIBhQIBA4AUqDwJnWf22Ee6 +otD8GHJWiEBtlZUwCwYJYIZIAWUDBAIBoEwwGQYJKoZIhvcNAQkDMQwGCmCGSAFlAgECTQMw +LwYJKoZIhvcNAQkEMSIEINq+nldSoCoJuEe/lhrRhfx0ArygsPJ7mCMbOFrpr1dFMAsGCSqG +SIb3DQEBCwSCAQBTeRE1DzwF2dnv2yJAOYOxNnAtTs72ZG8mv5Ad4M/9n1+MPiAykLcBslW8 +7D1KjBdwB3oxIT4sjwGh0kxKLe4G+VuvQuPwtT8MqMl3hounnFOM5nMSj1TSbfHVPs3dhEyk +Wu1gQ5g9gxLF3MpwEJGJKvhRtK17LGElJWvGPniRMChAJZJWoLjFBMe5JMzpqu2za50S1K3t +YtkTOx/2FQdVApkTY1qMQooljDiuvSvOuSDXcyAA15uIypQJvfrBNqe6Ush+j7yS5UQyTm0o +ZidB8vj4jIZT3S2gqWhtBLMUc11j+kWlXEZEigSL8WgCbAu7lqhItMwz2dy4C5aAWq8r""" + + def setUp(self): + self.asn1Spec = rfc5652.ContentInfo() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.tau_pem_text) + + layers = { + rfc5652.id_ct_contentInfo: rfc5652.ContentInfo(), + rfc5652.id_signedData: rfc5652.SignedData(), + rfc5934.id_ct_TAMP_update: rfc5934.TAMPUpdate() + } + + getNextLayer = { + rfc5652.id_ct_contentInfo: lambda x: x['contentType'], + rfc5652.id_signedData: lambda x: x['encapContentInfo']['eContentType'], + rfc5934.id_ct_TAMP_update: lambda x: None + } + + getNextSubstrate = { + rfc5652.id_ct_contentInfo: lambda x: x['content'], + rfc5652.id_signedData: lambda x: x['encapContentInfo']['eContent'], + rfc5934.id_ct_TAMP_update: lambda x: None + } + + next_layer = rfc5652.id_ct_contentInfo + + while next_layer: + asn1Object, rest = der_decode(substrate, asn1Spec=layers[next_layer]) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + substrate = getNextSubstrate[next_layer](asn1Object) + next_layer = getNextLayer[next_layer](asn1Object) + + def testOpenTypes(self): + substrate = pem.readBase64fromText(self.tau_pem_text) + asn1Object, rest = der_decode(substrate, + asn1Spec=rfc5652.ContentInfo(), + decodeOpenTypes=True) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + eci = asn1Object['content']['encapContentInfo'] + assert eci['eContentType'] in rfc5652.cmsContentTypesMap.keys() + assert eci['eContentType'] == rfc5934.id_ct_TAMP_update + tau, rest = der_decode(eci['eContent'], + asn1Spec=rfc5652.cmsContentTypesMap[eci['eContentType']], + decodeOpenTypes=True) + assert not rest + assert tau.prettyPrint() + assert der_encode(tau) == eci['eContent'] + + assert tau['version'] == 2 + assert tau['msgRef']['target'] == univ.Null("") + assert tau['msgRef']['seqNum'] == 1568307088 + + count = 0 + for u in tau['updates']: + count += 1 + + assert count == 1 + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + import sys + + result = unittest.TextTestRunner(verbosity=2).run(suite) + sys.exit(not result.wasSuccessful()) -- cgit v1.2.3 From 3c6eb1928887dbb19aea72e36a256002fe3b07d1 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Thu, 19 Sep 2019 06:46:59 +0200 Subject: Fix typo in CHANGES --- CHANGES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 055163d..f7ad85c 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -27,7 +27,7 @@ Revision 0.2.7, released XX-09-2019 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 -- Fixed malformed `rfc6402.RevRepContent` data structure layout +- Fixed malformed `rfc4210.RevRepContent` data structure layout - Added RFC5934 providing Trust Anchor Management Protocol (TAMP) Revision 0.2.6, released 31-07-2019 -- cgit v1.2.3 From 38da5f02063131f6149f3e0fc798a304a41d7831 Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Mon, 23 Sep 2019 16:57:41 -0400 Subject: Add support for RFC 6210 (#65) --- CHANGES.txt | 1 + pyasn1_modules/rfc6210.py | 42 ++++++++++++++++++++++++++ tests/__main__.py | 1 + tests/test_rfc6210.py | 75 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 119 insertions(+) create mode 100644 pyasn1_modules/rfc6210.py create mode 100644 tests/test_rfc6210.py diff --git a/CHANGES.txt b/CHANGES.txt index f7ad85c..bf7e8ad 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -29,6 +29,7 @@ Revision 0.2.7, released XX-09-2019 - Added RFC5990 providing RSA-KEM Key Transport Algorithm in the CMS - Fixed malformed `rfc4210.RevRepContent` data structure layout - Added RFC5934 providing Trust Anchor Management Protocol (TAMP) +- Added RFC6210 providing Experiment for Hash Functions with Parameters Revision 0.2.6, released 31-07-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc6210.py b/pyasn1_modules/rfc6210.py new file mode 100644 index 0000000..28587b9 --- /dev/null +++ b/pyasn1_modules/rfc6210.py @@ -0,0 +1,42 @@ +# +# 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 +# +# Experiment for Hash Functions with Parameters in the CMS +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc6210.txt +# + +from pyasn1.type import constraint +from pyasn1.type import univ + +from pyasn1_modules import rfc5280 + + +id_alg_MD5_XOR_EXPERIMENT = univ.ObjectIdentifier('1.2.840.113549.1.9.16.3.13') + + +class MD5_XOR_EXPERIMENT(univ.OctetString): + pass + +MD5_XOR_EXPERIMENT.subtypeSpec = constraint.ValueSizeConstraint(64, 64) + + +mda_xor_md5_EXPERIMENT = rfc5280.AlgorithmIdentifier() +mda_xor_md5_EXPERIMENT['algorithm'] = id_alg_MD5_XOR_EXPERIMENT +mda_xor_md5_EXPERIMENT['parameters'] = MD5_XOR_EXPERIMENT() + + +# Map of Algorithm Identifier OIDs to Parameters added to the +# ones that are in rfc5280.py. + +_algorithmIdentifierMapUpdate = { + id_alg_MD5_XOR_EXPERIMENT: MD5_XOR_EXPERIMENT(), +} + +rfc5280.algorithmIdentifierMap.update(_algorithmIdentifierMapUpdate) diff --git a/tests/__main__.py b/tests/__main__.py index c85b6d4..7ee3fb7 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -50,6 +50,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc6019.suite', 'tests.test_rfc6031.suite', 'tests.test_rfc6032.suite', + 'tests.test_rfc6210.suite', 'tests.test_rfc6211.suite', 'tests.test_rfc7030.suite', 'tests.test_rfc7191.suite', diff --git a/tests/test_rfc6210.py b/tests/test_rfc6210.py new file mode 100644 index 0000000..a04423a --- /dev/null +++ b/tests/test_rfc6210.py @@ -0,0 +1,75 @@ +# +# 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 rfc5652 +from pyasn1_modules import rfc6210 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class AuthenticatedDataTestCase(unittest.TestCase): + pem_text = """\ +MIICRQYLKoZIhvcNAQkQAQKgggI0MIICMAIBADGBwDCBvQIBADAmMBIxEDAOBgNVBAMMB0 +NhcmxSU0ECEEY0a8eAAFa8EdNuLs1dcdAwDQYJKoZIhvcNAQEBBQAEgYCH70EpEikY7deb +859YJRAWfFondQv1D4NFltw6C1ceheWnlAU0C2WEXr3LUBXZp1/PSte29FnJxu5bXCTn1g +elMm6zNlZNWNd0KadVBcaxi1n8L52tVM5sWFGJPO5cStOyAka2ucuZM6iAnCSkn1Ju7fgU +5j2g3bZ/IM8nHTcygjAKBggrBgEFBQgBAqFPBgsqhkiG9w0BCRADDQRAAQIDBAUGBwgJCg +sMDQ4PEBESEwQVFhcYGRobHB0eHyAhIiMEJSYnKCkqKywtLi8wMTIzBDU2Nzg5Ojs8PT4/ +QDArBgkqhkiG9w0BBwGgHgQcVGhpcyBpcyBzb21lIHNhbXBsZSBjb250ZW50LqKBxzAYBg +kqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0wOTEyMTAyMzI1MDBa +MB8GCSqGSIb3DQEJBDESBBCWaa5hG1eeg+oQK2tJ3cD5MGwGCSqGSIb3DQEJNDFfMF0wTw +YLKoZIhvcNAQkQAw0EQAECAwQFBgcICQoLDA0ODxAREhMEFRYXGBkaGxwdHh8gISIjBCUm +JygpKissLS4vMDEyMwQ1Njc4OTo7PD0+P0CiCgYIKwYBBQUIAQIEFLjUxQ9PJFzFnWraxb +EIbVbg2xql +""" + + def setUp(self): + self.asn1Spec = rfc5652.ContentInfo() + + 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['contentType'] == rfc5652.id_ct_authData + ad, rest = der_decode (asn1Object['content'], asn1Spec=rfc5652.AuthenticatedData()) + assert not rest + assert ad.prettyPrint() + assert der_encode(ad) == asn1Object['content'] + + assert ad['version'] == 0 + assert ad['digestAlgorithm']['algorithm'] == rfc6210.id_alg_MD5_XOR_EXPERIMENT + + mac_alg_p, rest = der_decode (ad['digestAlgorithm']['parameters'], + asn1Spec=rfc5280.algorithmIdentifierMap[ad['digestAlgorithm']['algorithm']]) + assert not rest + assert mac_alg_p.prettyPrint() + assert der_encode(mac_alg_p) == ad['digestAlgorithm']['parameters'] + + assert mac_alg_p.prettyPrint()[:10] == "0x01020304" + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + import sys + + result = unittest.TextTestRunner(verbosity=2).run(suite) + sys.exit(not result.wasSuccessful()) -- cgit v1.2.3 From aa1b0c65c1ffc4ec4c0a9fdc037012b457154af7 Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Fri, 27 Sep 2019 18:13:46 -0400 Subject: Add support for RFC 5751 (#66) * Add support for RFC 5751 --- CHANGES.txt | 1 + pyasn1_modules/rfc2985.py | 7 ++- pyasn1_modules/rfc5751.py | 124 ++++++++++++++++++++++++++++++++++++++++++++++ tests/__main__.py | 1 + tests/test_rfc2985.py | 8 ++- tests/test_rfc5751.py | 109 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 247 insertions(+), 3 deletions(-) create mode 100644 pyasn1_modules/rfc5751.py create mode 100644 tests/test_rfc5751.py diff --git a/CHANGES.txt b/CHANGES.txt index bf7e8ad..1e37a2b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -30,6 +30,7 @@ Revision 0.2.7, released XX-09-2019 - Fixed malformed `rfc4210.RevRepContent` data structure layout - Added RFC5934 providing Trust Anchor Management Protocol (TAMP) - Added RFC6210 providing Experiment for Hash Functions with Parameters +- Added RFC5751 providing S/MIME Version 3.2 Message Specification Revision 0.2.6, released 31-07-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc2985.py b/pyasn1_modules/rfc2985.py index c8ebb0c..75bccf0 100644 --- a/pyasn1_modules/rfc2985.py +++ b/pyasn1_modules/rfc2985.py @@ -565,6 +565,11 @@ rfc5280.certificateAttributesMap.update(_certificateAttributesMapUpdate) # CMS Attribute Map +# Note: pkcs_9_at_smimeCapabilities is not included in the map because +# the definition in RFC 5751 is preferred, which produces the same +# encoding, but it allows different parameters for SMIMECapability +# and AlgorithmIdentifier. + _cmsAttributesMapUpdate = { # Attribute types for use in PKCS #7 data (a.k.a. CMS) pkcs_9_at_contentType: ContentType(), @@ -577,7 +582,7 @@ _cmsAttributesMapUpdate = { pkcs_9_at_friendlyName: FriendlyName(), pkcs_9_at_localKeyId: univ.OctetString(), pkcs_9_at_signingDescription: DirectoryString(), - pkcs_9_at_smimeCapabilities: SMIMECapabilities(), + # pkcs_9_at_smimeCapabilities: SMIMECapabilities(), } rfc5652.cmsAttributesMap.update(_cmsAttributesMapUpdate) diff --git a/pyasn1_modules/rfc5751.py b/pyasn1_modules/rfc5751.py new file mode 100644 index 0000000..7e20001 --- /dev/null +++ b/pyasn1_modules/rfc5751.py @@ -0,0 +1,124 @@ +# This file is being contributed to 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 +# +# S/MIME Version 3.2 Message Specification +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc5751.txt + +from pyasn1.type import namedtype +from pyasn1.type import opentype +from pyasn1.type import tag +from pyasn1.type import univ + +from pyasn1_modules import rfc5652 +from pyasn1_modules import rfc8018 + + +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 5652 and RFC 8018 + +IssuerAndSerialNumber = rfc5652.IssuerAndSerialNumber + +RecipientKeyIdentifier = rfc5652.RecipientKeyIdentifier + +SubjectKeyIdentifier = rfc5652.SubjectKeyIdentifier + +rc2CBC = rfc8018.rc2CBC + + +# S/MIME Capabilities Attribute + +smimeCapabilities = univ.ObjectIdentifier('1.2.840.113549.1.9.15') + + +smimeCapabilityMap = { } + + +class SMIMECapability(univ.Sequence): + pass + +SMIMECapability.componentType = namedtype.NamedTypes( + namedtype.NamedType('capabilityID', univ.ObjectIdentifier()), + namedtype.OptionalNamedType('parameters', univ.Any(), + openType=opentype.OpenType('capabilityID', smimeCapabilityMap)) +) + + +class SMIMECapabilities(univ.SequenceOf): + pass + +SMIMECapabilities.componentType = SMIMECapability() + + +class SMIMECapabilitiesParametersForRC2CBC(univ.Integer): + # which carries the RC2 Key Length (number of bits) + pass + + +# S/MIME Encryption Key Preference Attribute + +id_smime = univ.ObjectIdentifier('1.2.840.113549.1.9.16') + +id_aa = _OID(id_smime, 2) + +id_aa_encrypKeyPref = _OID(id_aa, 11) + + +class SMIMEEncryptionKeyPreference(univ.Choice): + pass + +SMIMEEncryptionKeyPreference.componentType = namedtype.NamedTypes( + namedtype.NamedType('issuerAndSerialNumber', + IssuerAndSerialNumber().subtype(implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.NamedType('receipentKeyId', + # Yes, 'receipentKeyId' is spelled incorrectly, but kept + # this way for alignment with the ASN.1 module in the RFC. + RecipientKeyIdentifier().subtype(implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.NamedType('subjectAltKeyIdentifier', + SubjectKeyIdentifier().subtype(implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 2))) +) + + +# The Prefer Binary Inside SMIMECapabilities attribute + +id_cap = _OID(id_smime, 11) + +id_cap_preferBinaryInside = _OID(id_cap, 1) + + +# CMS Attribute Map + +_cmsAttributesMapUpdate = { + smimeCapabilities: SMIMECapabilities(), + id_aa_encrypKeyPref: SMIMEEncryptionKeyPreference(), +} + +rfc5652.cmsAttributesMap.update(_cmsAttributesMapUpdate) + + +# SMIMECapabilities Attribute Map +# +# Do not include OIDs in the dictionary when the parameters are absent. + +_smimeCapabilityMapUpdate = { + rc2CBC: SMIMECapabilitiesParametersForRC2CBC(), +} + +smimeCapabilityMap.update(_smimeCapabilityMapUpdate) diff --git a/tests/__main__.py b/tests/__main__.py index 7ee3fb7..56c2fd4 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -40,6 +40,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc5480.suite', 'tests.test_rfc5649.suite', 'tests.test_rfc5652.suite', + 'tests.test_rfc5751.suite', 'tests.test_rfc5914.suite', 'tests.test_rfc5915.suite', 'tests.test_rfc5934.suite', diff --git a/tests/test_rfc2985.py b/tests/test_rfc2985.py index 466d50b..f95ede8 100644 --- a/tests/test_rfc2985.py +++ b/tests/test_rfc2985.py @@ -129,7 +129,9 @@ HktMK+isIjxOTk4yJTOOAgIH0A== assert asn1Object.prettyPrint() assert der_encode(asn1Object) == substrate - openTypesMap = { } + openTypesMap = { + rfc2985.pkcs_9_at_smimeCapabilities: rfc2985.SMIMECapabilities(), + } openTypesMap.update(rfc5280.certificateAttributesMap) openTypesMap.update(rfc5652.cmsAttributesMap) @@ -218,7 +220,9 @@ HktMK+isIjxOTk4yJTOOAgIH0A== assert nv == 'alice@example.com' def testOpenTypes(self): - openTypesMap = { } + openTypesMap = { + rfc2985.pkcs_9_at_smimeCapabilities: rfc2985.SMIMECapabilities(), + } openTypesMap.update(rfc5280.certificateAttributesMap) openTypesMap.update(rfc5652.cmsAttributesMap) diff --git a/tests/test_rfc5751.py b/tests/test_rfc5751.py new file mode 100644 index 0000000..a89457a --- /dev/null +++ b/tests/test_rfc5751.py @@ -0,0 +1,109 @@ +# +# 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 rfc5652 +from pyasn1_modules import rfc5751 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class SignedMessageTestCase(unittest.TestCase): + pem_text = """\ +MIIGigYJKoZIhvcNAQcCoIIGezCCBncCAQExCTAHBgUrDgMCGjArBgkqhkiG9w0B +BwGgHgQcVGhpcyBpcyBzb21lIHNhbXBsZSBjb250ZW50LqCCAuAwggLcMIICm6AD +AgECAgIAyDAJBgcqhkjOOAQDMBIxEDAOBgNVBAMTB0NhcmxEU1MwHhcNOTkwODE3 +MDExMDQ5WhcNMzkxMjMxMjM1OTU5WjATMREwDwYDVQQDEwhBbGljZURTUzCCAbYw +ggErBgcqhkjOOAQBMIIBHgKBgQCBjc3tg+oKnjk+wkgoo+RHk90O16gO7FPFq4QI +T/+U4XNIfgzW80RI0f6fr6ShiS/h2TDINt4/m7+3TNxfaYrkddA3DJEIlZvep175 +/PSfL91DqItU8T+wBwhHTV2Iw8O1s+NVCHXVOXYQxHi9/52whJc38uRRG7XkCZZc +835b2wIVAOJHphpFZrgTxtqPuDchK2KL95PNAoGAJjjQFIkyqjn7Pm3ZS1lqTHYj +OQQCNVzyyxowwx5QXd2bWeLNqgU9WMB7oja4bgevfYpCJaf0dc9KCF5LPpD4beqc +ySGKO3YU6c4uXaMHzSOFuC8wAXxtSYkRiTZEvfjIlUpTVrXi+XPsGmE2HxF/wr3t +0VD/mHTC0YFKYDm6NjkDgYQAAoGAXOO5WnUUlgupet3jP6nsrF7cvbcTETSmFoko +ESPZNIZndXUTEj1DW2/lUb/6ifKiGz4kfT0HjVtjyLtFpaBK44XWzgaAP+gjfhry +JKtTGrgnDR7vCL9mFIBcYqxl+hWL8bs01NKWN/ZhR7LEMoTwfkFA/UanY04z8qXi +9PKD5bijgYEwfzAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIGwDAfBgNVHSME +GDAWgBRwRD6CLm+H3krTdeM9ILxDK5PxHzAdBgNVHQ4EFgQUvmyhs+PB9+1DcKTO +EwHi/eOX/s0wHwYDVR0RBBgwFoEUQWxpY2VEU1NAZXhhbXBsZS5jb20wCQYHKoZI +zjgEAwMwADAtAhRVDKQZH0IriXEiM42DarU9Z2u/RQIVAJ9hU1JUC1yy3drndh3i +EFJbQ169MYIDVDCCA1ACAQEwGDASMRAwDgYDVQQDEwdDYXJsRFNTAgIAyDAHBgUr +DgMCGqCCAuowGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAjBgkqhkiG9w0BCQQx +FgQUQGrsCFJ5um4WAi2eBinAIpaH3UgwOAYDKqszMTEEL1RoaXMgaXMgYSB0ZXN0 +IEdlbmVyYWwgQVNOIEF0dHJpYnV0ZSwgbnVtYmVyIDEuMD4GCyqGSIb3DQEJEAIE +MS8wLQwgQ29udGVudCBIaW50cyBEZXNjcmlwdGlvbiBCdWZmZXIGCSqGSIb3DQEH +ATBKBgkqhkiG9w0BCQ8xPTA7MAcGBSoDBAUGMDAGBioDBAUGTQQmU21pbWUgQ2Fw +YWJpbGl0aWVzIHBhcmFtZXRlcnMgYnVmZmVyIDIwbwYLKoZIhvcNAQkQAgoxYDBe +BgUqAwQFBgQrQ29udGVudCBSZWZlcmVuY2UgQ29udGVudCBJZGVudGlmaWVyIEJ1 +ZmZlcgQoQ29udGVudCBSZWZlcmVuY2UgU2lnbmF0dXJlIFZhbHVlIEJ1ZmZlcjBz +BgsqhkiG9w0BCRACCzFkoGIwWjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDVVTIEdv +dmVybm1lbnQxETAPBgNVBAsTCFZEQSBTaXRlMQwwCgYDVQQLEwNWREExEjAQBgNV +BAMTCURhaXN5IFJTQQIEClVEMzCB/AYLKoZIhvcNAQkQAgMxgewwgekwgeYEBzU3 +MzgyOTkYDzE5OTkwMzExMTA0NDMzWqGByTCBxqRhMF8xCzAJBgNVBAYTAlVTMRYw +FAYDVQQKEw1VUyBHb3Zlcm5tZW50MREwDwYDVQQLEwhWREEgU2l0ZTEMMAoGA1UE +CxMDVkRBMRcwFQYDVQQDEw5CdWdzIEJ1bm55IERTQaRhMF8xCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1VUyBHb3Zlcm5tZW50MREwDwYDVQQLEwhWREEgU2l0ZTEMMAoG +A1UECxMDVkRBMRcwFQYDVQQDEw5FbG1lciBGdWRkIERTQTAJBgcqhkjOOAQDBC8w +LQIVALwzN2XE93BcF0kTqkyFyrtSkUhZAhRjlqIUi89X3rBIX2xk3YQESV8cyg== +""" + + def setUp(self): + self.asn1Spec = rfc5652.ContentInfo() + + def testDerCodec(self): + smimeCapMap = { + univ.ObjectIdentifier('1.2.3.4.5.6.77'): univ.OctetString(), + } + smimeCapMap.update(rfc5751.smimeCapabilityMap) + + 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['contentType'] == rfc5652.id_signedData + assert asn1Object['content']['version'] == 1 + + for si in asn1Object['content']['signerInfos']: + assert si['version'] == 1 + for attr in si['signedAttrs']: + + if attr['attrType'] == rfc5751.smimeCapabilities: + for scap in attr['attrValues'][0]: + if scap['capabilityID'] in smimeCapMap.keys(): + scap_p, rest = der_decode(scap['parameters'], + asn1Spec=smimeCapMap[scap['capabilityID']]) + assert not rest + assert der_encode(scap_p) == scap['parameters'] + assert 'parameters' in scap_p.prettyPrint() + + if attr['attrType'] == rfc5751.id_aa_encrypKeyPref: + ekp_issuer_serial = attr['attrValues'][0]['issuerAndSerialNumber'] + assert ekp_issuer_serial['serialNumber'] == 173360179 + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + import sys + + result = unittest.TextTestRunner(verbosity=2).run(suite) + sys.exit(not result.wasSuccessful()) -- cgit v1.2.3 From fbfc0567412b3ba3882a7bedeb982849e9d49d6a Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Tue, 1 Oct 2019 16:52:04 -0400 Subject: Add support for RFC 8494 (#67) --- CHANGES.txt | 1 + pyasn1_modules/rfc8494.py | 80 +++++++++++++++++++++++++++++++++++++++++++++++ tests/__main__.py | 1 + tests/test_rfc8494.py | 55 ++++++++++++++++++++++++++++++++ 4 files changed, 137 insertions(+) create mode 100644 pyasn1_modules/rfc8494.py create mode 100644 tests/test_rfc8494.py diff --git a/CHANGES.txt b/CHANGES.txt index 1e37a2b..0ce6478 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -31,6 +31,7 @@ Revision 0.2.7, released XX-09-2019 - Added RFC5934 providing Trust Anchor Management Protocol (TAMP) - Added RFC6210 providing Experiment for Hash Functions with Parameters - Added RFC5751 providing S/MIME Version 3.2 Message Specification +- Added RFC8494 providing Multicast Email (MULE) over ACP 142 Revision 0.2.6, released 31-07-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc8494.py b/pyasn1_modules/rfc8494.py new file mode 100644 index 0000000..fe349e1 --- /dev/null +++ b/pyasn1_modules/rfc8494.py @@ -0,0 +1,80 @@ +# This file is being contributed to 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 +# +# Multicast Email (MULE) over Allied Communications Publication 142 +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc8494.txt + +from pyasn1.type import namedtype +from pyasn1.type import namedval +from pyasn1.type import tag +from pyasn1.type import univ + + +id_mmhs_CDT = univ.ObjectIdentifier('1.3.26.0.4406.0.4.2') + + +class AlgorithmID_ShortForm(univ.Integer): + pass + +AlgorithmID_ShortForm.namedValues = namedval.NamedValues( + ('zlibCompress', 0) +) + + +class ContentType_ShortForm(univ.Integer): + pass + +ContentType_ShortForm.namedValues = namedval.NamedValues( + ('unidentified', 0), + ('external', 1), + ('p1', 2), + ('p3', 3), + ('p7', 4), + ('mule', 25) +) + + +class CompressedContentInfo(univ.Sequence): + pass + +CompressedContentInfo.componentType = namedtype.NamedTypes( + namedtype.NamedType('unnamed', univ.Choice(componentType=namedtype.NamedTypes( + namedtype.NamedType('contentType-ShortForm', + ContentType_ShortForm().subtype(explicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.NamedType('contentType-OID', + univ.ObjectIdentifier().subtype(explicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 1))) + ))), + namedtype.NamedType('compressedContent', + univ.OctetString().subtype(explicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 0))) +) + + +class CompressionAlgorithmIdentifier(univ.Choice): + pass + +CompressionAlgorithmIdentifier.componentType = namedtype.NamedTypes( + namedtype.NamedType('algorithmID-ShortForm', + AlgorithmID_ShortForm().subtype(explicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.NamedType('algorithmID-OID', + univ.ObjectIdentifier().subtype(explicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 1))) +) + + +class CompressedData(univ.Sequence): + pass + +CompressedData.componentType = namedtype.NamedTypes( + namedtype.NamedType('compressionAlgorithm', CompressionAlgorithmIdentifier()), + namedtype.NamedType('compressedContentInfo', CompressedContentInfo()) +) diff --git a/tests/__main__.py b/tests/__main__.py index 56c2fd4..391a23d 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -64,6 +64,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc8226.suite', 'tests.test_rfc8410.suite', 'tests.test_rfc8418.suite', + 'tests.test_rfc8494.suite', 'tests.test_rfc8520.suite', 'tests.test_rfc8619.suite', 'tests.test_rfc8649.suite'] diff --git a/tests/test_rfc8494.py b/tests/test_rfc8494.py new file mode 100644 index 0000000..a2db6a0 --- /dev/null +++ b/tests/test_rfc8494.py @@ -0,0 +1,55 @@ +# +# 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 rfc8494 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class CompresssedDataTestCase(unittest.TestCase): + pem_text = """\ +MIIBNqADAgEAMIIBLaADAgEZoIIBJASCASB4nG2P0U7CQBBF3/cr5l2K3YpSF5YA +bYmbWArtQsJjKVuogd1mO0T8e0ti1IjJZB4md07OHZbWnMbqkp/qo+oW5jSCWDqL +VCSpkBveg2kSbrg/FTIWcQRpJPlLmGYQzdci5MvlA+3Rx2cyREO/KVrhCOaJFLMN +n03E6yqNIEmDheS2LHzPG0zNdqw0dn89XAnev4RsFQRRlnW+SITMWmMGf72JNAyk +oXCj0mnPHtzwSZijYuD1YVJb8FzaB/rE2n3nUtcl2Xn7pgpkkAOqBsm1vrNWtqmM +ZkC7LgmMxraFgx91y0F1wfv6mFd6AMUht41CfsbS8X9yNtdNqayjdGF2ld4z8LcV +EiIPVQPtvBuLBxjW5qx3TbXXo6vHJ1OhhLY= + +""" + + def setUp(self): + self.asn1Spec = rfc8494.CompressedData() + + 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['compressionAlgorithm']['algorithmID-ShortForm'] == 0 + cci = asn1Object['compressedContentInfo'] + assert cci['unnamed']['contentType-ShortForm'] == 25 + assert cci['compressedContent'].prettyPrint()[:12] == '0x789c6d8fd1' + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + import sys + + result = unittest.TextTestRunner(verbosity=2).run(suite) + sys.exit(not result.wasSuccessful()) -- cgit v1.2.3 From fd7d16fdde4f47727f70944df8bf5e8f4701d1f4 Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Wed, 2 Oct 2019 00:49:57 -0400 Subject: Update map used for OpenTypes in RFC 5958 (#68) --- CHANGES.txt | 2 ++ pyasn1_modules/rfc5958.py | 2 +- tests/test_rfc5958.py | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 0ce6478..bbf2dc7 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -26,6 +26,8 @@ Revision 0.2.7, released XX-09-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 +- Updated the handling of attribute maps for use with openType in + RFC 5958 to use the rfc5652.cmsAttributesMap - Added RFC5990 providing RSA-KEM Key Transport Algorithm in the CMS - Fixed malformed `rfc4210.RevRepContent` data structure layout - Added RFC5934 providing Trust Anchor Management Protocol (TAMP) diff --git a/pyasn1_modules/rfc5958.py b/pyasn1_modules/rfc5958.py index 82c6556..1aaa928 100644 --- a/pyasn1_modules/rfc5958.py +++ b/pyasn1_modules/rfc5958.py @@ -50,7 +50,7 @@ class PrivateKey(univ.OctetString): class Attributes(univ.SetOf): - componentType = rfc5280.Attribute() + componentType = rfc5652.Attribute() class PublicKey(univ.BitString): diff --git a/tests/test_rfc5958.py b/tests/test_rfc5958.py index 0b859ed..a38a696 100644 --- a/tests/test_rfc5958.py +++ b/tests/test_rfc5958.py @@ -69,7 +69,7 @@ YWlyc4EhABm/RAlphM3+hUG6wWfcO5bIUIaqMLa2ywxcOK1wMWbh oneKey = asn1Object['content'][0] assert oneKey['privateKeyAlgorithm']['algorithm'] == rfc8410.id_Ed25519 pkcs_9_at_friendlyName = univ.ObjectIdentifier('1.2.840.113549.1.9.9.20') - assert oneKey['attributes'][0]['type'] == pkcs_9_at_friendlyName + assert oneKey['attributes'][0]['attrType'] == pkcs_9_at_friendlyName suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) -- cgit v1.2.3 From 6b3ecead6ee42ce57986ac00d4e6639337aed7ba Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Wed, 2 Oct 2019 16:41:03 -0400 Subject: Add support for RFC 8398 (#69) --- CHANGES.txt | 2 ++ pyasn1_modules/rfc8398.py | 52 ++++++++++++++++++++++++++++++++++++ tests/__main__.py | 1 + tests/test_rfc8398.py | 67 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 122 insertions(+) create mode 100644 pyasn1_modules/rfc8398.py create mode 100644 tests/test_rfc8398.py diff --git a/CHANGES.txt b/CHANGES.txt index bbf2dc7..3d013c4 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -34,6 +34,8 @@ Revision 0.2.7, released XX-09-2019 - Added RFC6210 providing Experiment for Hash Functions with Parameters - Added RFC5751 providing S/MIME Version 3.2 Message Specification - Added RFC8494 providing Multicast Email (MULE) over ACP 142 +- Added RFC8398 providing Internationalized Email Addresses in + X.509 Certificates Revision 0.2.6, released 31-07-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc8398.py b/pyasn1_modules/rfc8398.py new file mode 100644 index 0000000..151b632 --- /dev/null +++ b/pyasn1_modules/rfc8398.py @@ -0,0 +1,52 @@ +# +# This file is part of pyasn1-modules software. +# +# Created by Russ Housley with some assistance from asn1ate v.0.6.0. +# +# Copyright (c) 2019, Vigil Security, LLC +# License: http://snmplabs.com/pyasn1/license.html +# +# Internationalized Email Addresses in X.509 Certificates +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc8398.txt +# https://www.rfc-editor.org/errata/eid5418 +# + +from pyasn1.type import char +from pyasn1.type import constraint +from pyasn1.type import univ + +from pyasn1_modules import rfc5280 + +MAX = float('inf') + + +# SmtpUTF8Mailbox contains Mailbox as specified in Section 3.3 of RFC 6531 + +id_pkix = rfc5280.id_pkix + +id_on = id_pkix + (8, ) + +id_on_SmtpUTF8Mailbox = id_on + (9, ) + + +class SmtpUTF8Mailbox(char.UTF8String): + pass + +SmtpUTF8Mailbox.subtypeSpec = constraint.ValueSizeConstraint(1, MAX) + + +on_SmtpUTF8Mailbox = rfc5280.AnotherName() +on_SmtpUTF8Mailbox['type-id'] = id_on_SmtpUTF8Mailbox +on_SmtpUTF8Mailbox['value'] = SmtpUTF8Mailbox() + + +# Map of Other Name OIDs to Other Name is added to the +# ones that are in rfc5280.py + +_anotherNameMapUpdate = { + id_on_SmtpUTF8Mailbox: SmtpUTF8Mailbox(), +} + +rfc5280.anotherNameMap.update(_anotherNameMapUpdate) diff --git a/tests/__main__.py b/tests/__main__.py index 391a23d..c90b2e2 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -62,6 +62,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc8018.suite', 'tests.test_rfc8103.suite', 'tests.test_rfc8226.suite', + 'tests.test_rfc8398.suite', 'tests.test_rfc8410.suite', 'tests.test_rfc8418.suite', 'tests.test_rfc8494.suite', diff --git a/tests/test_rfc8398.py b/tests/test_rfc8398.py new file mode 100644 index 0000000..0b27d4a --- /dev/null +++ b/tests/test_rfc8398.py @@ -0,0 +1,67 @@ +# +# 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 rfc5280 +from pyasn1_modules import rfc8398 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class EAITestCase(unittest.TestCase): + pem_text = "oCAGCCsGAQUFBwgJoBQMEuiAgeW4q0BleGFtcGxlLmNvbQ==" + + def setUp(self): + self.asn1Spec = rfc5280.GeneralName() + + 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['otherName']['type-id'] in rfc5280.anotherNameMap.keys() + assert asn1Object['otherName']['type-id'] == rfc8398.id_on_SmtpUTF8Mailbox + + eai, rest = der_decode(asn1Object['otherName']['value'], + asn1Spec=rfc5280.anotherNameMap[asn1Object['otherName']['type-id']]) + assert not rest + assert eai.prettyPrint() + assert der_encode(eai) == asn1Object['otherName']['value'] + + assert eai[0] == u'\u8001' + assert eai[1] == u'\u5E2B' + + 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['otherName']['type-id'] == rfc8398.id_on_SmtpUTF8Mailbox + assert asn1Object['otherName']['value'][0] == u'\u8001' + assert asn1Object['otherName']['value'][1] == u'\u5E2B' + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + import sys + + result = unittest.TextTestRunner(verbosity=2).run(suite) + sys.exit(not result.wasSuccessful()) -- cgit v1.2.3 From fe4e942cddbf9b5d788fe2fa34f5a3ac6a0a46ac Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Thu, 3 Oct 2019 11:22:08 -0400 Subject: Add support for RFC 8419 (#70) --- CHANGES.txt | 2 + pyasn1_modules/rfc8419.py | 68 ++++++++++++++++++++++++ tests/__main__.py | 1 + tests/test_rfc8419.py | 128 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 199 insertions(+) create mode 100644 pyasn1_modules/rfc8419.py create mode 100644 tests/test_rfc8419.py diff --git a/CHANGES.txt b/CHANGES.txt index 3d013c4..fb53d21 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -36,6 +36,8 @@ Revision 0.2.7, released XX-09-2019 - Added RFC8494 providing Multicast Email (MULE) over ACP 142 - Added RFC8398 providing Internationalized Email Addresses in X.509 Certificates +- Added RFC8419 providing Edwards-Curve Digital Signature Algorithm + (EdDSA) Signatures in the CMS Revision 0.2.6, released 31-07-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc8419.py b/pyasn1_modules/rfc8419.py new file mode 100644 index 0000000..f10994b --- /dev/null +++ b/pyasn1_modules/rfc8419.py @@ -0,0 +1,68 @@ +# This file is being contributed to pyasn1-modules software. +# +# Created by Russ Housley. +# +# Copyright (c) 2019, Vigil Security, LLC +# License: http://snmplabs.com/pyasn1/license.html +# +# Edwards-Curve Digital Signature Algorithm (EdDSA) Signatures in the CMS +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc8419.txt +# https://www.rfc-editor.org/errata/eid5869 + + +from pyasn1.type import univ + +from pyasn1_modules import rfc5280 + + +class ShakeOutputLen(univ.Integer): + pass + + +id_Ed25519 = univ.ObjectIdentifier('1.3.101.112') + +sigAlg_Ed25519 = rfc5280.AlgorithmIdentifier() +sigAlg_Ed25519['algorithm'] = id_Ed25519 +# sigAlg_Ed25519['parameters'] is absent + + +id_Ed448 = univ.ObjectIdentifier('1.3.101.113') + +sigAlg_Ed448 = rfc5280.AlgorithmIdentifier() +sigAlg_Ed448['algorithm'] = id_Ed448 +# sigAlg_Ed448['parameters'] is absent + + +hashAlgs = univ.ObjectIdentifier('2.16.840.1.101.3.4.2') + +id_sha512 = hashAlgs + (3, ) + +hashAlg_SHA_512 = rfc5280.AlgorithmIdentifier() +hashAlg_SHA_512['algorithm'] = id_sha512 +# hashAlg_SHA_512['parameters'] is absent + + +id_shake256 = hashAlgs + (12, ) + +hashAlg_SHAKE256 = rfc5280.AlgorithmIdentifier() +hashAlg_SHAKE256['algorithm'] = id_shake256 +# hashAlg_SHAKE256['parameters']is absent + + +id_shake256_len = hashAlgs + (18, ) + +hashAlg_SHAKE256_LEN = rfc5280.AlgorithmIdentifier() +hashAlg_SHAKE256_LEN['algorithm'] = id_shake256_len +hashAlg_SHAKE256_LEN['parameters'] = ShakeOutputLen() + + +# Map of Algorithm Identifier OIDs to Parameters added to the +# ones in rfc5280.py. Do not add OIDs with absent paramaters. + +_algorithmIdentifierMapUpdate = { + id_shake256_len: ShakeOutputLen(), +} + +rfc5280.algorithmIdentifierMap.update(_algorithmIdentifierMapUpdate) diff --git a/tests/__main__.py b/tests/__main__.py index c90b2e2..251aca6 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -65,6 +65,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc8398.suite', 'tests.test_rfc8410.suite', 'tests.test_rfc8418.suite', + 'tests.test_rfc8419.suite', 'tests.test_rfc8494.suite', 'tests.test_rfc8520.suite', 'tests.test_rfc8619.suite', diff --git a/tests/test_rfc8419.py b/tests/test_rfc8419.py new file mode 100644 index 0000000..ee566c5 --- /dev/null +++ b/tests/test_rfc8419.py @@ -0,0 +1,128 @@ +# +# 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 rfc8419 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class Ed25519TestCase(unittest.TestCase): + alg_id_1_pem_text = "MAUGAytlcA==" + + def setUp(self): + self.asn1Spec = rfc5280.AlgorithmIdentifier() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.alg_id_1_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert asn1Object['algorithm'] == rfc8419.id_Ed25519 + assert not asn1Object['parameters'].isValue + assert der_encode(asn1Object) == substrate + + +class Ed448TestCase(unittest.TestCase): + alg_id_2_pem_text = "MAUGAytlcQ==" + + def setUp(self): + self.asn1Spec = rfc5280.AlgorithmIdentifier() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.alg_id_2_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert asn1Object['algorithm'] == rfc8419.id_Ed448 + assert not asn1Object['parameters'].isValue + assert der_encode(asn1Object) == substrate + + +class SHA512TestCase(unittest.TestCase): + alg_id_3_pem_text = "MAsGCWCGSAFlAwQCAw==" + + def setUp(self): + self.asn1Spec = rfc5280.AlgorithmIdentifier() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.alg_id_3_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert asn1Object['algorithm'] == rfc8419.id_sha512 + assert not asn1Object['parameters'].isValue + assert der_encode(asn1Object) == substrate + + +class SHAKE256TestCase(unittest.TestCase): + alg_id_4_pem_text = "MAsGCWCGSAFlAwQCDA==" + + def setUp(self): + self.asn1Spec = rfc5280.AlgorithmIdentifier() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.alg_id_4_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert asn1Object['algorithm'] == rfc8419.id_shake256 + assert not asn1Object['parameters'].isValue + assert der_encode(asn1Object) == substrate + + +class SHAKE256LENTestCase(unittest.TestCase): + alg_id_5_pem_text = "MA8GCWCGSAFlAwQCEgICAgA=" + + def setUp(self): + self.asn1Spec = rfc5280.AlgorithmIdentifier() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.alg_id_5_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert asn1Object['algorithm'] == rfc8419.id_shake256_len + assert asn1Object['parameters'].isValue + assert der_encode(asn1Object) == substrate + + param, rest = der_decode(asn1Object['parameters'], + asn1Spec=rfc5280.algorithmIdentifierMap[asn1Object['algorithm']]) + assert not rest + assert param.prettyPrint() + assert der_encode(param) == asn1Object['parameters'] + assert param == 512 + + def testOpenTypes(self): + substrate = pem.readBase64fromText(self.alg_id_5_pem_text) + asn1Object, rest = der_decode(substrate, + asn1Spec=self.asn1Spec, + decodeOpenTypes=True) + assert not rest + assert asn1Object.prettyPrint() + assert asn1Object['algorithm'] == rfc8419.id_shake256_len + assert asn1Object['parameters'] == 512 + 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()) -- cgit v1.2.3 From c8f7dce25078099e00f75a3351a6294294624687 Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Fri, 4 Oct 2019 17:36:33 -0400 Subject: Add support for RFC 8479 (#71) Added support for RFC 8479 --- CHANGES.txt | 1 + pyasn1_modules/rfc8479.py | 45 ++++++++++++++++++++ tests/__main__.py | 1 + tests/test_rfc8479.py | 104 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 151 insertions(+) create mode 100644 pyasn1_modules/rfc8479.py create mode 100644 tests/test_rfc8479.py diff --git a/CHANGES.txt b/CHANGES.txt index fb53d21..881296e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -38,6 +38,7 @@ Revision 0.2.7, released XX-09-2019 X.509 Certificates - Added RFC8419 providing Edwards-Curve Digital Signature Algorithm (EdDSA) Signatures in the CMS +- Added RFC8479 providing Storing Validation Parameters in PKCS#8 Revision 0.2.6, released 31-07-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc8479.py b/pyasn1_modules/rfc8479.py new file mode 100644 index 0000000..57f78b6 --- /dev/null +++ b/pyasn1_modules/rfc8479.py @@ -0,0 +1,45 @@ +# +# 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 +# +# Storing Validation Parameters in PKCS#8 +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc8479.txt +# + +from pyasn1.type import namedtype +from pyasn1.type import univ + +from pyasn1_modules import rfc5652 + + +id_attr_validation_parameters = univ.ObjectIdentifier('1.3.6.1.4.1.2312.18.8.1') + + +class ValidationParams(univ.Sequence): + pass + +ValidationParams.componentType = namedtype.NamedTypes( + namedtype.NamedType('hashAlg', univ.ObjectIdentifier()), + namedtype.NamedType('seed', univ.OctetString()) +) + + +at_validation_parameters = rfc5652.Attribute() +at_validation_parameters['attrType'] = id_attr_validation_parameters +at_validation_parameters['attrValues'][0] = ValidationParams() + + +# Map of Attribute Type OIDs to Attributes added to the +# ones that are in rfc5652.py + +_cmsAttributesMapUpdate = { + id_attr_validation_parameters: ValidationParams(), +} + +rfc5652.cmsAttributesMap.update(_cmsAttributesMapUpdate) diff --git a/tests/__main__.py b/tests/__main__.py index 251aca6..a266c7f 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -66,6 +66,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc8410.suite', 'tests.test_rfc8418.suite', 'tests.test_rfc8419.suite', + 'tests.test_rfc8479.suite', 'tests.test_rfc8494.suite', 'tests.test_rfc8520.suite', 'tests.test_rfc8619.suite', diff --git a/tests/test_rfc8479.py b/tests/test_rfc8479.py new file mode 100644 index 0000000..60e0ea2 --- /dev/null +++ b/tests/test_rfc8479.py @@ -0,0 +1,104 @@ +# +# 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.type import univ + +from pyasn1_modules import pem +from pyasn1_modules import rfc4055 +from pyasn1_modules import rfc5652 +from pyasn1_modules import rfc5958 +from pyasn1_modules import rfc8479 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class ValidationParmTestCase(unittest.TestCase): + pem_text = """\ +MIIE/gIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCpPwXwfhDsWA3q +jN2BWg1xfDjvZDVNfgTV/b95g304Aty3z13xPXAhHZ3ROW3pgPxTj9fiq7ZMy4Ua +gMpPK81v3pHX1uokC2KcGXbgbAq2Q8ClxSXgEJllRwDENufjEdV10gArt8NlIP0N +lota1kQUuI1DMsqc5DTIa35Nq4j1GW+KmLtP0kCrGq9fMGwjDbPEpSp9DTquEMHJ +o7kyJIjB+93ikLvBUTgbxr+jcnTLXuhA8rC8r+KXre4NPPNPRyefRcALLt/URvfA +rTvFOQfi3vIjNhBZL5FdC+FVAr5QnF3r2+cuDPbnczr4/rr81kzFGWrwyAgF5FWu +pFtB5IYDAgMBAAECggEAHZ88vGNsNdmRkfhWupGW4cKCuo+Y7re8Q/H2Jd/4Nin2 +FKvUPuloaztiSGDbVm+vejama/Nu5FEIumNJRYMeoVJcx2DDuUxO1ZB1aIEwfMct +/DWd0/JDzuCXB0Cu5GTWLhlz0zMGHXihIdQ0DtGKt++3Ncg5gy1D+cIqqJB515/z +jYdZmb0Wqmz7H3DisuxvnhiCAOuNrjcDau80hpMA9TQlb+XKNGHIBgKpJe6lnB0P +MsS/AjDiDoEpP9GG9mv9+96rAga4Nos6avYlwWwbC6d+hHIWvWEWsmrDfcJlm2gN +tjvG8omj00t5dAt7qGhfOoNDGr5tvJVo/g96O/0I8QKBgQDdzytVRulo9aKVdAYW +/Nj04thtnRaqsTyFH+7ibEVwNIUuld/Bp6NnuGrY+K1siX8+zA9f8mKxuXXV9KK4 +O89Ypw9js2BxM7VYO9Gmp6e1RY3Rrd8w7pG7/KqoPWXkuixTay9eybrJMWu3TT36 +q7NheNmBHqcFmSQQuUwEmvp3MQKBgQDDVaisMJkc/sIyQh3XrlfzmMLK+GlPDucD +w5e50fHl8Q5PmTcP20zVLhTevffCqeItSyeAno94Xdzc9vZ/rt69410kJEHyBO9L +CmhtYz94wvSdRhbqf4VzAl2WU184sIYiIZDGsnGScgIYvo6v6mITjRhc8AMdYoPR +rL6xp6frcwKBgFi1+avCj6mFzD+fxqu89nyCmXLFiAI+nmjTy7PM/7yPlNB76qDG +Dil2bW1Xj+y/1R9ld6S1CVnxRbqLe+TZLuVS82m5nRHJT3b5fbD8jquGJOE+e+xT +DgA0XoCpBa6D8yRt0uVDIyxCUsVd5DL0JusN7VehzcUEaZMyuL+CyDeRAoGBAImB +qH6mq3Kc6Komnwlw4ttJ436sxr1vuTKOIyYdZBNB0Zg5PGi+MWU0zl5LDroLi3vl +FwbVGBxcvxkSBU63FHhKMQw7Ne0gii+iQQcYQdtKKpb4ezNS1+exd55WTIcExTgL +tvYZMhgsh8tRgfLWpXor7kWmdBrgeflFiOxZIL1/AoGAeBP7sdE+gzsh8jqFnVRj +7nOg+YllJAlWsf7cTH4pLIy2Eo9D+cNjhL9LK6RaAd7PSZ1adm8HfaROA2cfCm84 +RI4c7Ue0G+N6LZiFvC0Bfi5SaPVAExXOty8UqjOCoZavSaXBPuNcTXZuzswcgbxI +G5/kaJNHoEcdlVsPsYWKRNKgPzA9BgorBgEEAZIIEggBMS8wLQYJYIZIAWUDBAIC +BCCK9DKMh7687DHjA7j1U37/y2qR2UcITZmjaYI7NvAUYg== +""" + + def setUp(self): + self.asn1Spec = rfc5958.OneAsymmetricKey() + + 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 + + for attr in asn1Object['attributes']: + assert attr['attrType'] in rfc5652.cmsAttributesMap.keys() + if attr['attrType'] == rfc8479.id_attr_validation_parameters: + av, rest = der_decode(attr['attrValues'][0], + asn1Spec=rfc5652.cmsAttributesMap[attr['attrType']]) + assert not rest + assert av.prettyPrint() + assert der_encode(av) == attr['attrValues'][0] + + assert av['hashAlg'] == rfc4055.id_sha384 + seed = univ.OctetString(hexValue='8af4328c87bebcec31e303b8f5537effcb6a91d947084d99a369823b36f01462') + assert av['seed'] == seed + + 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 + + for attr in asn1Object['attributes']: + assert attr['attrType'] in rfc5652.cmsAttributesMap.keys() + if attr['attrType'] == rfc8479.id_attr_validation_parameters: + av = attr['attrValues'][0] + assert av['hashAlg'] == rfc4055.id_sha384 + seed = univ.OctetString(hexValue='8af4328c87bebcec31e303b8f5537effcb6a91d947084d99a369823b36f01462') + assert av['seed'] == seed + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + import sys + + result = unittest.TextTestRunner(verbosity=2).run(suite) + sys.exit(not result.wasSuccessful()) -- cgit v1.2.3 From b0a87d502f46542e65ad81ad5c8fe5474764b995 Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Sat, 5 Oct 2019 13:36:52 -0400 Subject: Add support for RFC 8360 (#72) --- CHANGES.txt | 2 + pyasn1_modules/rfc8360.py | 44 +++++ tests/__main__.py | 1 + tests/test_rfc8360.py | 468 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 515 insertions(+) create mode 100644 pyasn1_modules/rfc8360.py create mode 100644 tests/test_rfc8360.py diff --git a/CHANGES.txt b/CHANGES.txt index 881296e..3aa63bf 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -39,6 +39,8 @@ Revision 0.2.7, released XX-09-2019 - Added RFC8419 providing Edwards-Curve Digital Signature Algorithm (EdDSA) Signatures in the CMS - Added RFC8479 providing Storing Validation Parameters in PKCS#8 +- Added RFC8360 providing Resource Public Key Infrastructure (RPKI) + Validation Reconsidered Revision 0.2.6, released 31-07-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc8360.py b/pyasn1_modules/rfc8360.py new file mode 100644 index 0000000..ca180c1 --- /dev/null +++ b/pyasn1_modules/rfc8360.py @@ -0,0 +1,44 @@ +# +# 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 +# +# Resource Public Key Infrastructure (RPKI) Validation Reconsidered +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc8360.txt +# https://www.rfc-editor.org/errata/eid5870 +# + +from pyasn1.type import univ + +from pyasn1_modules import rfc3779 +from pyasn1_modules import rfc5280 + + +# IP Address Delegation Extension V2 + +id_pe_ipAddrBlocks_v2 = univ.ObjectIdentifier('1.3.6.1.5.5.7.1.28') + +IPAddrBlocks = rfc3779.IPAddrBlocks + + +# Autonomous System Identifier Delegation Extension V2 + +id_pe_autonomousSysIds_v2 = univ.ObjectIdentifier('1.3.6.1.5.5.7.1.29') + +ASIdentifiers = rfc3779.ASIdentifiers + + +# Map of Certificate Extension OIDs to Extensions is added to the +# ones that are in rfc5280.py + +_certificateExtensionsMapUpdate = { + id_pe_ipAddrBlocks_v2: IPAddrBlocks(), + id_pe_autonomousSysIds_v2: ASIdentifiers(), +} + +rfc5280.certificateExtensionsMap.update(_certificateExtensionsMapUpdate) diff --git a/tests/__main__.py b/tests/__main__.py index a266c7f..94801f1 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -62,6 +62,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc8018.suite', 'tests.test_rfc8103.suite', 'tests.test_rfc8226.suite', + 'tests.test_rfc8360.suite', 'tests.test_rfc8398.suite', 'tests.test_rfc8410.suite', 'tests.test_rfc8418.suite', diff --git a/tests/test_rfc8360.py b/tests/test_rfc8360.py new file mode 100644 index 0000000..95e2c80 --- /dev/null +++ b/tests/test_rfc8360.py @@ -0,0 +1,468 @@ +# +# 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 rfc8360 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class CertExtnsTestCase(unittest.TestCase): + extns_pem_text = """\ +MIJM7TCCRkUGCCsGAQUFBwEcBIJGNzCCRjMwgkXJBAIAATCCRcEDAgACMAoDAgAF +AwQFBRwAMAoDBAMFHCgDAgEEMAoDAwINdAMDAg14AwMCDYwwCgMDAw2oAwMDDbAD +AwIN8AMEAhcTMAMEAxcTOAMEAxdTQAMEBBdTcAMEBRdp4DAMAwQDF2poAwQDF2pw +AwQEF2zQAwMAF20DBAMXbwAwDAMEBBdvEAMEBhdvADAMAwQEF29QAwQHF28AMAwD +BAYXb8ADBAMXb/ADBAUX74ADBAMX+egwDAMEARf8QgMEABf8RDAMAwQAF/xHAwQA +F/xIMAwDBAAX/EsDBAAX/EwDBAAX/E8DAwIYhAMEARjrFgMCABkwCwMDARtuAwQC +G24QMAkDAgAfAwMAH84wDAMEAx/PCAMEAx/PMDAKAwQCH888AwIFADAJAwIAJQMD +ACVuMAkDAwQlcAMCASQDBAIr4XADBAIr+6ADAwMtCAMEBS0rQAMEBS044AMEBi1B +QAMDAS1CAwQHLUuAAwMELVADAwUtgAMEAi36GDAKAwIBLgMEBi6VgDAKAwQELpXQ +AwIALgMDATEMAwMBMjwDAwAydzAJAwIAMwMDADNOAwMAM1AwCQMDATNSAwICMAME +BTSQQAMCADUwCgMDAjYkAwMANiYDAgA5AwQCPQ7gMAoDAgE+AwQGPggAMAwDBAU+ +CGADBAU+DEAwDAMEBz4MgAMEBT4YQDAMAwQHPhiAAwQGPj2AMAsDAwE+PgMEBT5E +ADAMAwQGPkRAAwQFPkTAMAoDAwA+RQMDAT5wMAsDAwA+cwMEBT51ADALAwQGPnVA +AwMAPoYwCwMEBz6HgAMDAD6KAwQGPowAMAwDBAc+jIADBAU+rQAwDAMEBj6tQAME +Bj7BAAMEBT7BgDALAwQGPsHAAwMBPugwCwMDAD7rAwQFPvAAAwQFPvBAMAwDBAc+ +8IADBAc+8QAwDAMEBT7xoAMEBz77AAMDAj78MAwDBAE/jSIDBAE/jSQDBAQ/jgAD +BAVAEMADBAVAHAADBAdAKwADBAZAQUADBAZAicADBAJA7+wDBAVA/SADBAZBEoAD +BARBEtADBAVBJ0ADBAZBY4ADBAVCT2ADBARCVEADBAZCdoADBAVCzUADBAVC1GAD +BAJC+MwwDAMEBUMWIAMEAkMWOAMEAEOePgMEBUPRgAMEBERGwAMEBkUGAAMEBEVe +QAMEBEVecAMEBUWsYAMEBkXCQDAJAwIATQMDAk3YMAwDBAVN3CADBAdQQwAwDAME +BFBDkAMEBVBHADAMAwQEUEcwAwQFUEhAMAwDBARQSHADBAVQS4AwDAMEBlBLwAME +BFBWADAMAwQFUFYgAwQGUFcAMAsDBAVQV2ADAwNQUDALAwQEUFgQAwMAUF4wDAME +BFBfEAMEBlDwgDALAwQEUPDQAwMBUPQwCwMEBFD2EAMDA1DwMAwDBARQ+BADBAZQ ++AAwDAMEBFD4UAMEBlD5ADAMAwQEUPlQAwQFUPoAMAsDBARQ+jADAwJRADALAwQG +UQRAAwMBUQgwDAMEB1EKgAMEBVEVQDAMAwQEURVwAwQGURYAMAwDBAVRFmADBARR +F8AwDAMEBVEX4AMEBlEaADAMAwQEURpQAwQFUR1AMAsDBARRHXADAwJRQDALAwMD +UUgDBAVRW8AwCwMEBFFb8AMDBlGAMAsDAwBRwQMEB1JlADALAwQGUmXAAwMHUgAw +DAMEB1KAgAMEB1KBADALAwMBUoIDBAZSlwAwCwMEBVKXYAMDAlKYMAsDAwFSngME +B1LJADALAwMBUsoDBANTjxAwCwMEBVOPIAMDAlQgMAsDAwBUJQMEBVTNQDALAwQH +VM2AAwMAVOgwDAMEB1TpgAMEBVf3gDAMAwQDV/eoAwQFV/9AMAwDBAdX/4ADBARZ +0OAwCwMDAFnRAwQEWe8gMAwDBAZZ70ADBAVb4QAwDAMEAVvhIgMEAlvrADALAwQB +W+sGAwMBXMgwCwMDAlzMAwQFXbNAAwQDXbNoAwQCXbN4MAwDBAdds4ADBABeGhow +DAMEAl4aHAMEAF4aHjAMAwQFXhogAwQCXhooAwQAXhotMAwDBAReGjADBABeGkIw +DAMEAl4aRAMEAF4aSDAMAwQBXhpKAwQCXhpQMAwDBAFeGlYDBAJeZwAwDAMEA15n +CAMEBF6+wAMEBF6+8DAMAwQHXr+AAwQGX6OAMAsDBARfo9ADAwBfqDAMAwQFX6kg +AwQCX7W4MAoDBAZftcADAgVAAwQFYAmAAwQEYH2QAwMAYoADAwNlOAMEAmfhyAME +AmfniAMEAmfxbAMEAmf7pAMEAmf8WAMEA2glIAMEAmiZVAMEAWjoJAMEBmjpQAME +Amjz2AMEAmj0CDAMAwQBa5aiAwQCa5awAwQEa6GgAwQFa7WAAwQAa79EMAkDAgBt +AwMCbegwCwMEBG3sEAMDAm3wMAkDAwBt9QMCAWwwCwMDAHHLAwQGccuAAwMBdMoD +BAJ9PkgDAwCAAAMDAIAHAwMAgBAwCgMDAIAnAwMBgCgDAwCALTAKAwMAgEEDAwSA +QAMDAYBWAwMAgF0DAwCAYgMEB4B0gAMDAIB8AwMAgH8DAwGAgjAKAwMAgIsDAwCA +jgMDAICoAwMAgLADAwGAsgMDAIDHAwMAgMwDAwCA1gMDAIDoAwMAgOoDAwCA8AMD +AIDzAwMAgPYwCgMDAIELAwMBgQwDAwCBEAMDAIEUAwMBgRoDAwCBHwMDAIFDMAoD +AwCBRQMDAIFGAwMAgUkDAwCBWDAKAwMBgWYDAwCBaAMDAIF9AwMAgYEDAwCBhAMD +AYGOAwMAgakDAwCBrzAKAwMAgbEDAwCBsjAKAwMAgbUDAwGBuAMDAIG7AwMBgcID +AwCBxwMDAIHOAwMAgdADAwGB1gMDAIHZMAoDAwCB6QMDAIHqMAoDAwSB8AMDAIHy +AwMAgfcDAwCCAAMDAYIYAwMAghwDAwCCIAMDAIIlAwMAgiswCgMDAII7AwMAgjwD +AwGCQgMDAIJJAwMAgksDAwGCTjAKAwMBglIDAwCCVAMDAYJYAwMBglwDAwCCYgMD +AIJkAwMAgmgDAwCCcAMDAIJzAwMAgngDAwCCfQMDAIKFAwMAgogwCgMDAYKKAwMB +gpQDAwCCnwMDAIKhAwQHgqSAAwMAgrQDAwCCtzAKAwMAgrkDAwCCugMDAIK8AwMA +gr4DAwGCwAMDAILGAwMAgswDAwCCzgMDAYLQAwMAgt8wCgMDAILhAwMAgvQDAwCC +9gMEA4L4MDAMAwQBgvg6AwQGgvgAMAwDBAKC+EQDBAKC+GgDAwCC+wMDAIL/AwMA +gwEDAwCDYQMDAINjAwMAg2YDAwCDbzAKAwMBg3IDAwGDdAMDAIOCAwMAg5gDAwGD +mjAKAwMAg58DAwCDoDAKAwMCg6QDAwCDpgMDAIOpMAoDAwCDrQMDAYOwAwMAg7QD +AwCDvAMDAIPNAwMAg88DAwCD0wMDAIPcAwMAg+AwCgMDAIPjAwMAg+QDAwCD5wMD +AIPqAwMAg+0DAwCD9gMDAIP7AwMAg/4wCgMDBoRAAwMAhE4DAwCEkjAKAwMAhJUD +AwCElgMDAISZAwMAhJswCgMDAISlAwMBhKgDAwCEqwMDAISwAwMAhLQwCgMDAIS5 +AwMChLgwCgMDAITDAwMAhMQDAwCExwMDAITjMAoDAwCE5QMDA4TgAwMAhPQDAwCE +/AMDAoYAAwMAhhEDAwCGEwMDAIYVAwMAhhkwCgMDAIYbAwMAhhwDAwCGHjAKAwMB +hiIDAwGGJAMDAIYvAwMAhjYwCgMDAYY6AwMBhjwDAwCGTAMDAIZRAwMAhlMwCgMD +AYZaAwMAhm4DAwCGdwMDAIaCAwMAhooDAwGGjjAKAwMAhpEDAwKGkAMDAIaXAwMA +hpswCgMDAIadAwMAhp4DAwCGqQMDAIarAwMAhrAwCgMDAIa3AwMAhrgDAwCGvAMD +AIa/AwMAhssDAwCGzjAKAwMChtQDAwCG1jAKAwMAhtsDAwCG3jAKAwMAhuEDAwKG +4AMDAIbvMAoDAwCG9QMDA4bwAwMAhvkDAwCG/wMEAodUNAMEAodUlAMDAIdaAwMA +h8QDBAeIjwAwCwMEBIiQEAMDAIiQAwMAiJQwCgMDAIibAwMBiJwDBAeInoAwCgMD +AIijAwMAiKQwCgMDAIipAwMAiKoDAwGIrAMDAIjHAwMAiMkDAwCIzgMDAIjhAwMB +iOYDAwCI8wMDAIj/AwMAiREwCgMDAIkhAwMAiSIwCgMDAIkrAwMAiSwDAwCJLwMD +AIkyMAoDAwCJNwMDAIk4AwMAiTowCgMDAok8AwMAiT4wCgMDAIlJAwMAiUoDAwCJ +XQMDAIlgAwMAiWUDAwCJaQMDAIlsAwMBiXgDAwCJgQMDAImFAwMAiYoDAwCJnAMD +AImjAwMAia4DAwCJvzAKAwMAicEDAwKJwAMDAYnMAwMAidADAwCJ1TAKAwMAidkD +AwCJ2jAKAwMAid0DAwCJ4AMDAIniAwMAifgDAwGJ+gMDAIn9MAoDAwCKAwMDAIoE +AwMAigYDAwCKDjAKAwMAihUDAwCKFjAKAwMAiiUDAwCKJgMDAYooAwMAijADAwGK +PgMDAIpCAwMAikYDAwCKUQMDAIpgAwMAimQwCgMDAYpmAwMAimgDAwCKagMDAIp8 +AwQDioCIMAoDAwCKgwMDAIqEAwMAioYwCgMDAIq7AwMGioADAwCKwzAKAwMBisYD +AwGKyAMDAIrLMAoDAwCKzQMDAIrOMAoDAwCK1wMDAIrYMAoDAwCK3QMDAIrkMAoD +AwCK5wMDAYroMAoDAwKK9AMDAIr2AwMCivgDAwCK/TAKAwMAiwEDAwCLBDAKAwMB +iwYDAwCLCDAKAwMBiwoDAwGLGDAKAwMAixsDAwCLHgMDAIstAwMAiy8DAwCLMgMD +AIs2AwMAizowCwMDAIs/AwQHi0AAAwMAi0IDAwGLSgMDAItPAwMAi1MwCgMDAItZ +AwMAi1wwCgMDBYtgAwMAi2IDAwCLZDAKAwMAi2kDAwCLeDAKAwMBi3oDAwCLfAMD +AIuAAwMAi4UDBAWLisADAwCLjQMDAIuPAwMAi5EDAwCLlQMDAIuZMAoDAwKLnAMD +AIueAwMAi6ADAwCLojAKAwMCi6QDAwCLpgMDAIuuAwMBi7IDAwCLuAMDAIu/AwMA +i94wCgMDAIxNAwMAjE4wCgMDAoxUAwMAjFYwCgMDAIxdAwMAjF4DAwCMYQMDAIxp +AwMBjJYDAwCMpAMDAIymAwMAjLUDAwCMywMDAIznMAkDAgCNAwMBjVQwCgMDAI1X +AwMBjWQDAwCNaQMDAI1sAwMAjXEDAwCNcwMDAI19AwMAjYIwCgMDAY2GAwMAjYow +CgMDAI2PAwMAjZQDAwCNowMEB42kgDAKAwMAjakDAwCNrDAKAwMAja8DAwCNsAMD +AI3AAwQAjcEUAwQCjcFsAwQBjcHWAwMAjcIDAwCNxAMDAY3IAwMAjcsDAwCN0DAL +AwMBjeIDBAON4oADBACN4oswCwMEBI3ikAMDAI3kAwMAjecDAwCN7QMDAI3xAwMB +jfQwCgMDAI35AwMAjfoDAwGN/AMDAI3/AwQDjlsIAwQDjlt4AwQDjluYAwQHjpoA +AwQDjuogAwMAjvcDAwCPKQMDAI8vMAoDAwGPMgMDAY80AwMAj0EwCwMEB49cgAMD +AY9cAwMAj2EDAwCPYzAKAwMAj3UDAwCPdgMDAI95AwMAj34wCgMDAI+BAwMAj4ID +AwCPoTAKAwMAj6MDAwCPpAMDAI+nAwMAj6kwCgMDBI+wAwMAj7QDAwCPxAMDAI/N +AwMAj9IDAwCP2QMDAY/gMAoDAwCP6QMDAI/qAwMAj+0DAwCP7wMDAI/1AwMAj/wD +AwCQAgMDAJAEAwMBkBQDAwCQGAMDAJAbAwMAkCADAwCQKTAKAwMAkCsDAwCQLAME +ApAwPAMDAJA2AwMBkDgwCgMDAJA/AwMBkEADAwCQTAMDAJBSMAsDAwKQVAMEB5BW +AAMDAJBXAwMAkF8DAwCQYgMDAJB3AwMAkHoDAwCQfAMDAJB/AwMAkJEDAwGQpAMD +AJCtMAoDAwSQsAMDAZC0AwMAkMEDAwCQyAMDAJDMAwMAkM4DBAaQ0AAwCwMEB5DQ +gAMDAZDQAwMAkPgwCQMCAJEDAwCSADAKAwMBkgIDAwCSBAMDAJITAwMAkhUDAwCS +MAMDAJIyAwMAkjQwCgMDAJI7AwMAkjwDAwGSQgMDAJJGAwQBkkdeAwMAkkgDAwCS +SwMDAJJNAwMBklADAwCSVwMEA5JY6AMDAJJaAwMAkmEwCgMDApJkAwMAkm4DAwCS +cDAKAwMAkncDAwCSeAMDAJJ8AwMAkoUDAwCSiAMDAJKMAwMBkp4wCgMDAJKhAwMA +kqIDAwCSqQMDAZKsMAoDAwCSrwMDAZKwAwMAkrMDAwCSuQMDAJK8MAoDAwCSvwMD +AJLCMAoDAwGS0gMDAZLUAwMAktgwCgMDAJLbAwMAktwDAwCS4DAKAwMAkuMDAwCS +5AMDAJLqAwMAkvEwCgMDAJL3AwMBkvgDAwCS+zAJAwMAkv0DAgCSAwMAkwcwCgMD +ApMMAwMAkw4wCgMDAJMbAwMAkx4DAwGTIAMDAZMsAwMAkzQDAwCTNgMDAJM8MAoD +AwCTQwMDAJNEAwMAk0swCgMDAJNNAwMAk04wCgMDAZNSAwMAk1QwCgMDAZNWAwMB +k1gDAwCTWzAKAwMAk10DAwCTYDAKAwMBk2IDAwCTZAMDAJNmMAoDAwCTbwMDAJNw +AwMAk3cDAwGTegMDAJN9AwMAk38DBAeTh4ADAwGTjjAKAwMAk5MDAwCTmAMDAJOc +MAoDAwCToQMDApOgAwMAk6cwCgMDAJOrAwMBk6wDAwCTrwMDAZO0AwMAk7gDAwCT +ugMDAZO8AwMAk8EDAwGTxAMDAJPJAwMAk8wDAwCT0jAKAwMAk9UDAwOT0AMDAJPc +MAoDAwKT5AMDAZPsAwMAk/MwCgMDAZP6AwMAk/wDAwGUAgMDAJQGAwMAlDYDAwCU +OAMEAJQ7cwMDAJQ8AwMAlD8DBAKUQDgDAwCURQMDAJRHAwQFlEzgAwMAlE8wCgMD +AJRRAwMClFADAwCUWAMDAZRuMAoDAwGUdgMDApR4MAoDAwCUhwMDAJSIAwMAlIoD +AwCUjAMDAJSPAwMAlJQDAwCUlwMDAJSgAwMAlKkDAwCUsAMDAJS1AwMAlLkDAwCU +uzAKAwMClMQDAwCUxgMDAJTIMAoDAwCU+wMDAZT8AwMAlQADAwCVAwMDAJUbAwMA +lTEDAwCVOwMDAJU+AwMAlVEDAwCVWgMDAJVtAwMAlX4wCgMDApWEAwMAlYYwCgMD +AJWLAwMAlYwwCgMDAZWSAwMAlZQwCgMDAJWZAwMBlZwDAwCVqgMDAJWsMAoDAwCV +sQMDAZXEMAoDAwOVyAMDAZXoMAwDBAOV6ggDBAeV6gAwCgMDAJXrAwMDlfAwCgMD +AJX5AwMClfgDAwGV/gMDAJZqAwQClmvIAwMAlnADAwCWgAMEApaBCAMDAJaEAwMA +lowwCgMDAJaRAwMAlpIDAwCWrwMDAJayAwMBlswwCgMDAJbVAwMAltYDAwCW2QMD +AJbjAwMBluwDAwCW8QMDAJb0AwMAlvsDAwCW/jAJAwIAlwMDAJdkMAsDAwGXagME +BZdqgDAMAwQGl2rAAwQEl2rgAwMAl3MDAwCXeAMDAJd/AwMAl4EDAwCXhTAKAwMA +l4cDAwCXiAMDAZecAwMAl6oDAwCXrTAKAwMAl68DAwGXsAMDAJe0MAoDAwGXtgMD +AJe4AwMAl7sDAwCXvTAKAwMAl9MDAwCX1DAJAwMDl9gDAgOQAwMAmEIDAwCYRwMD +AJhJMAoDAwCYTQMDAJhOAwMAmFEwCgMDA5hYAwMAmFowCgMDAJhdAwMAmGADAwCY +aQMDAZhyAwMAmIYDAwCYjwMDAJiWAwMAmJgDAwCZAQMDAJkFAwMAmQ8DAwCZEQMD +AJkTAwMAmVgwCgMDAplcAwMAmWIDAwCZZDAKAwMCmWwDAwCZbgMDAJlwMAsDAwOa +CAMEBJoIIAMEBpoIQAMDAZoOAwMAmiADAwCbBAMDAJstMAoDAwGbNgMDAJs4AwMA +m0IDAwCbSQMDAJtpMAoDAwCbgwMDAZuEAwMBm4gDBAabikADAwCbjAMDAJuRAwMA +m54DAwCbuQMDAJvAAwMAm8YDAwCbygMDAJvMAwMAm88wCgMDAJvRAwMAm9IDAwCb +3zAKAwMAm+MDAwCb5AMDAJvnAwMAm/UwCgMDAJv5AwMAm/oDAwCb/QMEBZv+IAMD +AJwKAwMAnA4wCgMDAJwRAwMAnBIDAwCcGQMDAJwcAwMAnCMDAwCcKwMDAZwwMAoD +AwCcMwMDAJw0AwMAnDYDAwCcOgMDAJw9AwMAnEMDAwCcUwMDAJxqMAoDAwGccgMD +AJx0AwMAnHYDAwCchQMDAJyHAwMAnJQDAwCcljAKAwMAnRcDAwGdHDAKAwMAnVMD +AwCdVAMDAJ1YAwMAnV4DAwGdYAMDAJ1jAwQDnXjgMAsDBAKdeOwDAwCdeAMDAZ18 +AwMAnYEDAwCdiAMDAJ2KAwMAnYwDAwCdkDAKAwMAnZ0DAwWdgDAKAwMAnaEDAwCd +pDAKAwMAnacDAwGdqDAKAwMAnasDAwGdrAMDAJ2xAwMBnbQDAwCdugMDAJ2+AwMA +ncEDAwCdyAMDAJ3LAwMAneQDAwCd5wMDAZ3sAwMAnfMDAwCd9wMDAJ35AwMCniQw +CgMDAJ4pAwMCnigDAwGeLjAKAwMAnjEDAwCeMgMDAJ46AwMAnjwDAwCeQAMDAZ5C +AwMAnksDAwCeWgMDAJ5cAwMAnl4DAwCeYwMDAJ5mAwMAnmkwCgMDAJ5tAwMAnm4D +AwCecAMDAJ53AwMCnnwDAwCegQMDAJ6DAwMAnoUDBASejBADBAaejEADAwCejwME +BJ6SgDAKAwMCnpQDAwCelgMDAJ6YAwMAnpwDAwCeojAKAwMBnqYDAwGeqDAKAwMC +nqwDAwGetDAKAwMBnr4DAwGexAMDAJ7YAwMAntoDAwCe3AMDAJ7fMAoDAwCe4QMD +Ap7gMAoDAwGe5gMDAJ7qMAoDAwOe+AMDAJ76MAoDAwCe/wMDAJ8AMAoDAwCfBwMD +AJ8IAwMAnwwDAwCfDwMDAJ8UAwMBnxYDAwCfGTAKAwMAnx0DAwWfAAMDAJ8iAwMA +nyYDAwCfLgMDAZ8yAwMAnzoDAwCfPAMDAJ9FAwMAn0gDAwCfUQMDAJ9UAwMAn1YD +AwGfXAMDAJ9fAwMBn2QwCgMDAJ9nAwMAn2gDAwCfawMDAJ9tAwMAn3IDBAOfdcAw +DAMEAZ91ygMEBJ91wAMDAJ96AwMAn4IDAwCfhgMEBZ+HgAMDAJ+QMAoDAwGfkgMD +AZ+UMAoDAwCflwMDAJ+YAwMAn5oDAwCfnAMDAJ+gAwMAn6IwCgMDAJ+nAwMAn6gD +AwGfqgMDAJ+tMAoDAwCfswMDAJ+0AwMAn74wCgMDAJ/BAwMCn8ADAwCfxQMDAJ/I +AwMAn80DAwCf0gMDAJ/VAwMBn9gDAwCf2wMDAJ/gAwMAn+gDAwGf7AMDAJ/vAwMA +n/EDBAOf8ggDBAaf8kADBASf8uADAwGf9DAJAwMAn/0DAgWAMAoDAwCgBQMDAKAG +AwMBoAgDBAKgE1wDBAKgE7QDBAOgFGADBAKgFGwDBASgFJADBAGgFNYDBACgFOUD +BAGgFPgDAwCgJgMDAKAoMAoDAwKgLAMDBqAAMAoDAwGgQgMDAKBEAwMBoEYDAwCg +SwMDAKBOAwMAoFADAwCgVQMDAKBcMAoDAwCgYQMDAKBkMAoDAwCgZwMDAKBoAwMA +oHIDAwCgtAMEBKDKEAMDAaDSMAoDAwCg1QMDAKDWMAoDAwOg2AMDAaDcAwMAoOQD +BAag50ADBACg7hUDBAKg7iQDBAKg7jQDBAGg7jwDBAKg7mADBASg7nAwCgMDAaEC +AwMBoQQDAwGhCAMDAKEMAwMAoREDAwChFAMDAKEXAwMAoRsDAwChHgMDAKElMAoD +AwChKQMDAKEqAwMAoTAwCgMDAqE0AwMAoTYDAwChOwMDAKE+AwMAoUMwCgMDAaFG +AwMAoUoDAwChTAMDAKFOMAoDAwChUwMDAKFcMAoDAwOhaAMDAKFqMAoDAwGhbgMD +AKFwAwMAoXQDAwChhgMDAKGTAwMAoZwDAwChngMDAKHKAwMAodoDAwCh5jAKAwMA +ofsDAwCh/AMDAKILAwQDogzIAwMAog0DAwCiFQMDAKIXMAoDAwCiGQMDAKIaAwMA +oiYDAwCiVgMDAKKFAwQHoo6AAwQBotiKAwQCotn4AwQDotpYAwQCotqwAwQDotzw +MAwDBAOi3lgDBACi3loDBAKi9DQDBAKi9cwDBAKi+MQDBAKi+tgDBAOi+yADBAKi +/8QDAwCjAQMDAKMDAwMAowUDAwCjCQMDAKMiMAoDAwGjPgMDAaN0AwMAo3cwCgMD +AqOcAwMEo6ADAwCj8jAJAwICpAMDAKQoAwMApDAwCgMDAKQ7AwMBpDwDAwCkUQME +B6RdgDAKAwMBpH4DAwSkgAMDAKSxAwMApNcDAwClTgMEBKVU0AMDAKVyAwMApcAD +AwCl2gMDAKXeAwMDpggDBAemMYADAwCmVwMDAKdRMAsDBAGnVkIDAwOnUAMDAKdi +AwQHp2SAAwMAp28DBAWnoAADAwCnqAMDAKesAwMAp8sDAwCn6QMDAKgBAwMAqIsD +BAeolQADBAGolfgDAwCouwMEAaj1xAMDBakgAwQHqZQAAwQEqgpwAwQHqhGAMAwD +BACqJcsDBACqJc4DAwCqPAMEB6plgAMEB6pmgAMEB6qFAAMDAarsAwMAqv8wCgMD +BKsQAwMBqyADBAesUIADBAOsZ1gDBAOs8QAwDAMEA6zxKAMEA6zxMAMEA6zxQAME +AqzxVAMEBKz/AAMEBKz/YAMEA6z/iAMEBKz/wAMEBKz/4AMEA6z/+AMEBq3UwAME +Aq3WyAMEA63qgAMEBK30kAMEBq35AAMEA63/kAMEBq6MAAMEB69ugDAKAwIEsAME +A7BvMDAMAwQAsG85AwQEsHOgMAwDBAOwc7gDBAewegAwCgMEBrB6wAMCALAwCgMC +AbIDBASy7kAwCgMEBbLuYAMCALIDAwC06jAKAwIAuQMEArkIYDAMAwQDuQhoAwQC +uRTYMAwDBAW5FOADBAO5HkAwDAMEArkeTAMEAbkm0DAMAwQCuSbUAwQCuVrwMAwD +BAO5WvgDBAO5ZCAwDAMEArlkLAMEALlpBjAMAwQDuWkIAwQFuZIAMAwDBAK5kiQD +BAK5qWgwDAMEBLmpcAMEBLmrwDAMAwQCuavUAwQCubAwMAwDBAO5sDgDBAS5soAw +CgMEArmylAMCAbgwCgMCArwDBAe8gwAwCwMDAryEAwQEvNGAMAwDBAO80ZgDBAa8 +1gAwCgMEBLzWUAMCALwwDAMEAsAFHAMEAMAFHgMEAMAFJAMEAMAFMgMEAMAFOzAM +AwQAwAU9AwQAwAU+AwQAwAVhAwQAwAWOAwQAwAWRAwQAwAWiAwQAwAXvAwQAwAX+ +AwQAwAwBAwQAwAwvAwQAwAw2AwQBwAxIAwQAwAxNAwQAwAxRAwQAwAxgAwQAwAxj +MAwDBAbADMADBADADMIwDAMEAMAM2QMEAMAM2jAMAwQAwAznAwQAwAzoAwQAwAzr +AwQAwAz3AwMAwA8wDAMEAMAQewMEAMAQpjAMAwQAwBC3AwQAwBDKAwQAwBLDMAwD +BALAGhwDBATAGiAwDAMEAMAaaQMEAcAabDAMAwQAwBpvAwQAwBqAMAwDBADAGoUD +BAPAGoAwDAMEAcAamgMEAcAawAMEAMAa5wMEAMAa6gMEAsAa7DAMAwQCwBx8AwQC +wByAAwQAwB8OAwQAwB8XAwQBwB8aAwQAwB8fAwQAwB8oAwQAwB8+AwQAwB9mMAwD +BAHAH6YDBALAH6gDBADAH9MDBADAH+cDBADAH/wwDAMEAMAhDwMEAMAhEAMEAMAh +JDAMAwQAwCFXAwQEwCFgMAwDBADAIXEDBADAIXIwDAMEAcAhdgMEB8AhAAMEAcAh +gjAMAwQAwCGPAwQBwCGQMAwDBADAIZMDBADAIaYwDAMEAMAhqQMEAsAhqDAMAwQE +wCGwAwQAwCG2MAwDBAbAIcADBAPAIeAwDAMEAMAh6QMEBMAh4AMEAMAh/gMEAMAi +EwMEAMAiMgMEAMAiawMEAMAidAMEAMAiszALAwMAwCMDBALAIxAwDAMEAMAjPwME +AMAjSAMEAMAjWgMEAMAjXgMEAMAjbDAMAwQBwCOCAwQBwCOEAwQAwCOKAwQAwCOS +MAwDBADAI5UDBAHAI5gDBADAI6wwDAMEAMAjtwMEAMAjwDAMAwQAwCPFAwQAwCPG +MAwDBADAI80DBATAI8ADBADAI+UwDAMEBMAj8AMEAMAj9AMEAcAj9jAKAwMCwCQD +AwDAJjAMAwQAwChFAwQAwChQAwQAwCjkMAwDBADAKWcDBADAKYgwDAMEAsApjAME +AcApkAMEAMApkzAMAwQAwCmVAwQAwCmgAwQAwCnSAwQAwCnYAwQAwCnaAwQAwCnj +AwQAwCoBMAwDBAHAKioDBATAKiADBADAKjUwDAMEAMAqPwMEAcAqQAMEAMAqVwME +AMAqYQMEAMAqZAMEAMAqZjAMAwQAwCpxAwQAwCqEAwQAwCqPMAwDBALAKrQDBAHA +KsgDBADAKv0wDAMEAcArogMEAsArqDAMAwQGwCvAAwQAwCvEAwQAwCvSAwQAwCvU +AwQAwCvqMAsDAwLALAMEAsAsQDAMAwQAwCxHAwQAwCxaMAwDBATALPADBALALPgw +DAMEAsAv9AMEAcAv+AMEAMAwHwMEAMAwawMEAMAwkQMEAMAw4AMEAMAw5wMDAMAx +AwQEwDMAMAsDAwLANAMEAMA0MgMEAMA0mDAMAwQAwDSfAwQBwDSgMAwDBADANN0D +BAXANMAwDAMEAMA0/QMEAMA0/jAMAwQAwDVnAwQAwDVoMAwDBADANh8DBADANjQw +DAMEAcA2NgMEAMA2UAMEAMA2aAMEAMA2cTAMAwQAwDZzAwQAwDZ4AwQAwDZ6MAwD +BADANn0DBADANoADBADANoQDBADANoswDAMEAMA2jQMEAcA23AMEAMA24QMEAcA2 +9AMEAMA2/gMEAMA3VAMEAMA3WQMEAMA3ZQMEAMA3aQMEAMA3bQMEAMA3cwMEAMA3 +gQMEAMA3hAMEAMA3vAMEAMA3wQMEAMA3xQMEAMA31AMEAMA39DAMAwQCwDocAwQC +wDogMAwDBADAOikDBAHAOlgDBADAOsUDBADAOtowDAMEAcA64gMEAcA65AMEAMBA +HAMEAcBALAMEAsBAZAMEAMBAfQMEAMBAyjAMAwQAwEEzAwQAwEFGMAwDBALAQVwD +BADAQV4DBADAQWAwDAMEAMBBgwMEAMBBhAMEAMBBizAMAwQEwEGQAwQAwEGSAwQA +wEGZMAwDBADAQbcDBAHAQcQwDAMEAMBB2wMEAMBB5AMDAMBCMAwDBADAQwMDBADA +QwQDBADAQycDBADAQysDBADAQy8DBADAQzIDBADAQzQDBADAQzcDBADAQzoDBADA +Q0wDBADAQ08DBADAQ1cDBADAQ14wDAMEAsBDZAMEAMBDaAMEAMBDhwMEAMBDpwME +AMBDqjAMAwQAwEO9AwQAwEPQAwQAwEPaAwQBwEPcAwQAwEPfAwQAwEP5MAsDAwLA +RAMEAsBEEAMEAMBEFzAMAwQAwEQfAwQCwEQwMAwDBALAREwDBALARGgwDAMEAMBE +lwMEAcBEmDAMAwQAwESlAwQAwESqAwQAwESuAwQAwESwAwQAwES2AwQAwES6AwQA +wETRMAwDBADARNMDBADARNgDBADARN0DBADAROADBADAROYwDAMEAcBE+gMEAMBE +/AMEAMBE/jALAwMBwEYDBAPARnAwDAMEAMBGhQMEAMBGhgMEAMBGiDAMAwQCwEaM +AwQFwEaAAwQDwEbAAwQAwEbyAwMAwEcwDAMEAMBJEwMEAMBJFDAMAwQBwEkiAwQA +wEksAwQAwEniAwQAwEnlMAwDBAHATAYDBALATCAwDAMEAMBMewMEAMBMhDAMAwQB +wEyGAwQAwEysAwQAwEywMAwDBADATPEDBADATPgDBADATQsDBAHATXIDBALATXgw +DAMEAsBNhAMEAMBNigMEAcBNjAMEAcBQFDAMAwQAwFAfAwQAwFAqAwQAwFAuAwQA +wFAzAwQAwFE7MAwDBADAUT0DBADAUT4DBADAUW0DBADAUXkDBADAUXsDBADAUaAw +DAMEAcBRtgMEAMBRuAMEAMBRwgMEAMBR5gMEAMBR6gMEAMBSeQMEAMBSfAMEAMBS +fwMEAMBSmTAMAwQAwFKdAwQAwFKeAwQAwFLWAwQBwFLcAwQAwFLxMAsDAwDAUwME +AMBTZAMEAMBTZgMEAMBToAMEAMBTpQMEAMBTyAMEAMBTygMEAMBT2AMEAMBT3zAM +AwQAwFPlAwQAwFPmAwQAwFQFAwQAwFQNAwQAwFQPAwQAwFQbAwQAwFQeAwQBwFQg +AwQAwFQ+MAwDBADAVEsDBADAVFQDBADAVFcwDAMEAcBUWgMEAsBUYDAMAwQAwFRl +AwQBwFRsMAwDBADAVH8DBADAVJwDBADAVKYDBADAVK0wDAMEBMBUsAMEBMBUwAME +AMBU1AMEAcBU3DAMAwQBwFTiAwQBwFTkMAwDBADAVPUDBAPAVPADBADAVgsDBADA +Vg4DBADAVhIDBADAVhkDBADAVhsDBADAVlkwDAMEAMBWfQMEB8BWAAMEAMBWhjAM +AwQAwFaJAwQAwFaKAwQAwFajAwQBwFamAwQAwFapAwQAwFb+AwMAwFcDBADAWAED +BADAWAQwDAMEAMBYCQMEAMBYCgMEAMBYETAMAwQAwFgXAwQAwFgYMAwDBADAWFMD +BADAWFQDBADAWFYwDAMEAMBYYQMEAMBYYgMEAMBYbAMEAMBYdgMEAMBYewMEAMBY +gAMEAMBYggMEAMBYhQMEAMBYxAMEAMBYzAMEAcBY7jAMAwQBwFj6AwQAwFj+AwMA +wFkDBAHAW4wDBADAW7EDBADAW7oDBADAW70DBADAW78DBADAW8cDBADAW8kDBADA +W9MwDAMEAcBb1gMEAcBb6DAMAwQCwFvsAwQDwFvwAwQAwFxWAwQAwFxeMAwDBAPA +XGgDBADAXGoDBAHAXGwDBADAXHQwDAMEAMBcfQMEAsBciDAMAwQAwFyNAwQBwFyY +MAwDBADAXJsDBADAXJwDBADAXNgDAwDAXQMEAMBeGAMEAMBeHDAMAwQAwF45AwQA +wF46MAwDBADAXkMDBADAXkQDBADAXkwDBADAXk4wDAMEAMBebwMEAcBedDAMAwQC +wF6cAwQCwF6gAwQAwF6sMAwDBADAXq8DBAPAXsADBADAXtQDBADAXt0DBADAXuID +BADAXukDBADAXusDBADAXu8DAwDAYgMEAMBkEjAMAwQAwGQXAwQBwGQYAwQAwGQ0 +AwQAwGQ9AwQAwGQ/AwQAwGROAwQCwGRgMAwDBAHAZGYDBAHAZIQwDAMEAMBkhwME +AMBkjAMEAMBkkAMEAMBkmgMEAMBlAQMEAMBlBAMEAMBlCAMEAMBlCwMEAMBlHAME +AMBlIgMEAMBlSzAMAwQAwGVRAwQAwGVaMAwDBADAZW8DBADAZXIDBADAZXYDBADA +ZYkwDAMEAMBloQMEAMBlqAMEAMBlqgMEAMBlsDAMAwQAwGWzAwQAwGW0AwQAwGXA +MAwDBADAZcUDBADAZcYDBADAZfwDBADAZgEwDAMEAcBmBgMEAMBmCDAMAwQAwGYR +AwQBwGZQAwQAwGZZAwQAwGZfMAwDBAHAZpIDBAHAZrADBADAZtYDBAHAZuAwDAME +AMBm4wMEAcBm5AMEAMBnAgMEAMBnBwMEAMBnDgMEAMBnFAMEAMBnFzAMAwQAwGcb +AwQAwGcoMAwDBADAZ1UDBADAZ3QwDAMEAMBniQMEAsBniAMEAMBnkwMEAMBoFwME +AcBoHDAMAwQAwGgjAwQBwGgkAwQAwGgpAwQAwGgwAwQAwGg1MAwDBADAaDcDBADA +aDoDBADAaEgDBADAaE0DBADAaFIDBADAaIwDBADAaI4DBADAaJMDBAHAaJowDAME +AMBopwMEAMBoqAMEAMBo7gMEAMBo9QMEAMBo+AMEAMBo+wMEAMBpSwMDAMBqAwQA +wGsCAwQBwGsEMAwDBADAawsDBAHAawwwDAMEAMBrMwMEAMBrZAMEAMBrbgMEAMBr +cjAMAwQBwGt6AwQBwGuAAwQAwGuEAwQAwGuoAwQAwGuuMAwDBADAa7EDBADAa7ID +BADAa7swDAMEA8BryAMEAcBr6DAMAwQAwGvrAwQAwGvsMAwDBADAbBcDBAHAbDAw +DAMEAMBsMwMEAMBsXAMEAcBsZDAMAwQAwGxrAwQAwGxsMAwDBAHAbHIDBAHAbHgw +DAMEAMBsfQMEAMBsfjAMAwQHwGyAAwQAwGyuMAwDBADAbMMDBADAbNYDBADAbOoD +BADAbO4wCwMDAMBtAwQAwG0sMAwDBAHAbS4DBADAbUowDAMEAsBtTAMEAsBtWDAM +AwQBwG1eAwQAwG1iMAwDBALAbWQDBADAbWYwDAMEAMBtaQMEA8BtcDAMAwQAwG15 +AwQBwG3wMAsDBADAbfMDAwHAbAMEAMBvIQMEAMBvJwMEAMBvLDAMAwQAwG8vAwQA +wG8wAwQAwG9YAwQAwG9lMAwDBADAb2cDBADAb2gDBAHAb3wDBADAb38DBADAb/ww +DAMEAcBwHgMEAMBwIAMEAMBwLQMEAMBwMQMEAMBwPTAMAwQBwHBGAwQEwHBAMAwD +BAHAcGIDBADAcGQDBADAcMwDBADAcM4DBADAcNAwDAMEAMBw1QMEAMBw1gMEAMBw +9wMEAMBw/jAKAwMAwHEDAwDAdgMDAMB5MAwDBADAegEDBADAeoIwDAMEAMB6jQME +AMB6kjAMAwQAwHqXAwQAwHqqAwQAwHrWMAwDBAPAetgDBADAeuowDAMEAcB67gME +AMB68gMEAMB6/jAMAwQAwHwZAwQAwHwcAwQAwHwgAwQAwHwnAwQAwHwuAwQAwHxw +MAwDBADAfHMDBADAfHQDBADAfJswDAMEAcB8qgMEAsB82AMEAMB86zAMAwQAwHzt +AwQBwHz0MAwDBADAfPcDBADAfPgwDAMEAcB8+gMEAMB8/gMDAMB9MAwDBADAfgED +BADAfkAwDAMEAMCBAQMEAcCBPAMEAMCBUAMEAMCBVwMEAMCBYgMDAMCCAwQAwIMU +MAwDBADAgxkDBADAgxoDBADAg08DBADAg1kDBADAg2ADBADAg2wDBADAg4QwDAME +AMCECQMEAsCECAMEAMCEIgMEAMCENQMEAMCENwMEAMCEYwMEAMCE7wMEAcCE9AME +AMCE/AMEAMCFDwMEAMCFHAMEAMCFIAMEAMCFJDAMAwQAwIU1AwQDwIUwAwQAwIU6 +AwQAwIVAAwQCwIVsAwQAwIV5AwQAwIWDAwQAwIX0AwMAwIYwDAMEAMCHBwMEAcCH +JAMEAMCHLjAMAwQAwIczAwQBwIc0AwQAwIc/AwQAwIdCAwQAwIdEAwQAwIdSAwQA +wIdkAwQAwIeBAwQAwIeFAwQAwIePMAwDBADAh5EDBADAh6gDBADAh68DBADAh7sD +BADAh9sDBADAh+EwDAMEAMCH5wMEAMCH6jAMAwQAwIf9AwQAwIf+AwQAwIgHAwQA +wIgJAwQBwIgSAwQAwIgXMAwDBADAiB0DBAXAiAADBAHAiCgDBADAiDEwDAMEAMCI +MwMEAMCINAMEAMCIPTAMAwQAwIhHAwQAwIhmMAwDBAHAiJoDBADAiJwwDAMEAMCK +AQMEAMCKCAMEAMCKVjAMAwQAwIppAwQBwIp0MAwDBADAipsDBADAip4DBADAircD +BADAisADBADAiswwDAMEAsCK5AMEAMCK6AMEAMCK+AMEAMCLTgMEAcCMAgMEBsCQ +ADAMAwQBwJBKAwQBwJBMAwQHwJEAAwQCwJHgAwQAwJJ1AwQAwJJ3MAwDBADAknsD +BADAkn4DBADAkoQwDAMEAcCShgMEAcCSjAMEAMCSmDAMAwQAwJKjAwQAwJKqMAwD +BALAkqwDBADAkrYDBADAkrkDBADAkrsDBADAksEDBADAkswwDAMEAMCS4wMEAMCS +5DAMAwQAwJLpAwQAwJLqAwQBwJLuAwQAwJLyAwQAwJMXAwQAwJMiAwQAwJMkAwQA +wJMqAwQCwJNMAwQAwJONAwQAwJOWAwQAwJObAwQAwJPUMAwDBADAk9cDBAHAk9gD +BADAk9sDBADAk+QDBADAk/cDBADAk/swDAMEAMCUIQMEAMCUXAMEAMCUZwMEAcCU +pjAMAwQAwJSxAwQAwJS6MAwDBAbAlMADBADAlMIwDAMEAcCUxgMEAMCU2AMEAMCU +3AMEAMCVAwMEAMCVBQMEAMCVDwMEAMCVEwMEAMCVGzAMAwQAwJUdAwQBwJUgAwQA +wJUjAwQAwJUpAwQAwJU5MAwDBADAlTsDBADAlTwwDAMEAMCVTQMEBMCVQAMEAMCV +YgMEAMCVZAMEAMCVZgMEAcCVbjAMAwQAwJV1AwQBwJV4AwQAwJV+AwQAwJXjAwQA +wJXoAwQAwJXuAwQAwJYUMAwDBAHAljoDBADAlkgwDAMEAMCWSwMEAMCWTDAMAwQB +wJZOAwQAwJZUAwQAwJZZAwQAwJZcAwQAwJZeAwQAwJZoAwQAwJZqAwQAwJZ8AwQA +wJaMAwQAwJaSMAwDBADAlrEDBADAlrgwDAMEAsCWvAMEAMCWvjAMAwQGwJbAAwQA +wJbGMAwDBADAlssDBAHAlswDBAHAltADBADAlt8wDAMEAMCW4wMEAcCW6AMEAMCW +7gMEAMCW+AMEAMCW/AMEAMCW/gMEAMCYBgMEAMCYDgMEAMCYEQMEAcCYGgMEAMCY +KgMEAMCYLDAMAwQAwJgvAwQCwJgwAwQAwJg2MAwDBADAmD0DBAbAmAADBADAmEQD +BADAmFIDBADAmGIwDAMEAMCYbwMEAMCYcAMEAMCYegMEAMCYfAMEAMCYjQMEAMCY +lwMEAMCYnAMEAcCYpgMEAMCYrgMEAsCYuAMEAMCY8QMEAMCY9DAMAwQAwJj9AwQA +wJj+AwQBwJkCAwQAwJkNAwQBwJkSAwQAwJlZAwQAwJl0AwQAwJl/AwQAwJmZAwQA +wJmmAwQAwJmoAwQAwJmrMAwDBADAma0DBADAmbYDBAHAmbwDBADAmcIDBADAmdUw +DAMEAMCbAQMEAMCbBgMEAMCchAMEAMCcogMEAMCcpwMEAMCc0gMEAMCc1QMEAMCc +2QMEAMCc4wMEAMCc7wMEAMCc+DAMAwQAwJ0BAwQCwJ0AMAwDBAPAnQgDBAHAnRAD +BADAnYEwDAMEAMCdpQMEAMCdrAMEAMCdrgMEAMCdsAMEAMCduQMEAMCduwMEAMCd +vQMEAcCfJgMEAMCfRgMEAMCfSQMEAMCfTQMEAcCfVAMEAMCfWgMEAMCfXzAMAwQA +wJ9jAwQDwJ9gAwQAwJ9pAwQBwJ9sAwQAwJ92MAwDBADAn3kDBADAn3oDBADAoAoD +BADAoA8wDAMEAMCgFQMEA8CgEAMEAMCgGwMEAMCgIQMEAMCgJQMEAMCgQQMEAMCg +QzAMAwQAwKBfAwQAwKBgAwQAwKBqMAwDBADAoG0DBADAoG4DBADAoHsDBADAoH4D +BAHAoI4DBADAoJgDBADAoJwDBADAoKADBADAoKwwDAMEAMCgsQMEAsCgsAMEAcCg +wgMEAcCg4DAMAwQAwKDnAwQBwKDoMAwDBADAoPUDBAHAoPgwDAMEAMCg+wMEAMCg +/AMEAcChBgMEAcChQAMDAMCiMAwDBAXAoyADBAXAo4ADAwLApDAMAwQAwKsBAwQB +wKsEMAwDBAfAq4ADBAPAq8ADBADArOgDBADArP0wDAMEAMCtAQMEAMCtBAMEA8Ct +gDAMAwQGwK5AAwQAwK5EAwQBwK8OAwQEwK8gAwMAwLAwDAMEBMC7EAMEAcC7GAME +AMC8CjAMAwQAwLw/AwQBwLxAAwQAwLxFAwQAwLxgAwQAwLxpAwQBwLx0MAwDBADA +vHkDBADAvHoDBADAvH0DBADAvH8DBADAvIEDBADAvIQDBADAvIgDBADAvJEwDAME +AMC8nQMEAMC8ngMEAMC8uwMEAMC8vTAMAwQAwLzpAwQBwLzsMAwDBAHAvPIDBADA +vPgDBADAvQEDBALAvQgDBADAvQ4DBADAvRcDBADAvSkwDAMEAMC9MwMEAMC9NAME +AMC9NwMEAMC9QjAMAwQAwL1FAwQAwL1GAwQAwL1JAwQAwL1MAwQAwL13AwQAwL2X +AwQAwL2aAwQAwL2dAwQAwL2gMAwDBAHAvaYDBADAvaoDBAHAvcoDBADAvfsDBADA +viwDBAHAvjoDBADAvkADBADAvkMDBADAvkUDBADAvl8wDAMEAMC+gQMEAMC+hDAM +AwQAwL6tAwQAwL6uAwQBwL62MAwDBAHAvr4DBAHAvsAwDAMEAMC+yQMEAMC+yjAM +AwQAwL7pAwQAwL7sAwQAwL7wAwQAwL7yMAwDBADAvvcDBAHAvvgDAwDAwgMEAMDD +AQMEAMDDCAMEAcDDKgMEAMDDSAMEAMDDYjAMAwQAwMNpAwQAwMNqAwQAwMNuMAwD +BALAw3QDBADAw3YwDAMEAcDDhgMEAMDDlDAMAwQAwMO3AwQAwMO4AwQAwMPDAwQA +wMPsMAwDBADAxAEDBALAxJgDBATAxxADBADAy1ADBAHAy2wDBADAy+MwDAMEAMDO +TQMEAcDOUAMEAMDOVjAMAwQBwM7eAwQAwM7iAwQAwM8OAwQAwM8fMAwDBADAz40D +BADAz44DBADAz8QDBADA50MDBADA51IwDAMEAMDuAQMEAMDuCgMEAMD1mAMEAMD1 +qQMEAMD14TAMAwQAwPcBAwQAwPcKMAwDBADA+z0DBAHA+0ADBADA++IDBADA++Yw +CgMCAMEDBADBEdYwDAMEA8ER2AMEA8ET4DAMAwQCwRPsAwQBwSmQMAwDBALBKZQD +BAbBUgAwDAMEBcFSYAMEBcFSwDAKAwMAwVMDAwDBXjAMAwQHwV+AAwQAwWwWAwQC +wWwYMAwDBADBbB0DBAHBbNQwDAMEAMFs1wMEAsFs+DALAwMAwW0DBAHBbUAwDAME +AsFtRAMEA8FuYDALAwQBwW5qAwMEwWADAwDBcTAMAwQFwXIgAwQFwXJAMAwDBAXB +cqADBAXBcwAwDAMEB8FzgAMEBcF0AAMEBsF0gAMEBsF1ADALAwQFwXVgAwMAwXYw +CwMEB8F3gAMDAcF4MAsDAwLBfAMEAMG8BjALAwQDwbwIAwMAwbwwDAMEAcG9QgME +B8G9ADAMAwQAwb2BAwQAwcIAMAwDBAHBwgYDBAXBwgADBAXBwmAwDAMEAMHCgQME +BcHCgDAMAwQGwcLAAwQBwd3YMAsDBADB3dsDAwDB4gMEBsHjQDAMAwQAweOBAwQF +wgbAMAwDBADCBuEDBAbCCQAwDAMEAcIJQgMEAcIJUDAMAwQCwglUAwQAwiO+MAwD +BAbCI8ADBAXCT0AwCwMEB8JPgAMDAMJaMAsDAwLCXAMEBcLBAAMEBsLBQDAMAwQF +wsHgAwQGwsyAMAoDAwDCzQMDAMLeMAwDBAXC32ADBAXC34AwDAMEBsLfwAMEBMMY +QDAMAwQDwxhYAwQGwxiAMAwDBAXDGOADBAHDJ9gwCwMEAsMn3AMDAMMqMAwDBAXD +KyADBALDgAAwDAMEA8OACAMEBcOmwDALAwMAw6cDBAXDp4AwDAMEBMOnsAMEBsPK +ADAMAwQFw8pgAwQDw+pwMAwDBALD6nwDBAPD6qAwDAMEAMPqqQMEAMPquDAMAwQB +w+q6AwQCw+r4MAsDAwDD6wMEBcP2ADAKAwQGw/ZAAwICwAMEAMQBAzAMAwQAxAEF +AwQAxAEGAwQCxAFAMAwDBADEAUUDBADEAUYDBALEAgQwDAMEAcQDQgMEA8QDQAME +AMQDWwMEBcQPIAMEAsYLAAMEAMYRTQMEAMYRdTAMAwQCxhG0AwQAxhG2AwQAxhYz +MAwDBADGFl0DBAHGFmAwDAMEBcYkIAMEAMYkLgMEAsYtdAMEAMYzDDAMAwQAxjOP +AwQAxjOSAwQCxjQsAwQCxjccAwQDxllYAwQAxmOUAwQAxmPeAwQFxmlgMAwDBATG +hVADBAHGhVQDBADGhYwDBADGhc4DBADGheIwDAMEAMaHiQMEAMaHigMEAMaHpwME +AMaToDAMAwQAxpSxAwQAxpSyMAwDBAHGtJYDBAHGtJgDBADGzsUwDAMEA8bOyAME +AMbOygMEB8bwgAMEAccr9gMEAccw5gMDAMc1AwQDx1jQAwQEx1sQMAwDBAPHZwgD +BADHZwwDBAXH9wADBAPH9zgDBAXH+oADBADKAE0DBATLn1ADBAPLvjgDBALMCwAD +AwDMEgMEAcwwIAMEAMzh2gMEAM3JNwMEAM3TUwMEAc3c2AMEAM6nIQMEAs6+3AME +Bc7DIAMEBc784AMEBs9ZQAMEBc+WoAMEAs+u2AMEBc+yQAMEBs+0wAMEBM+9wDAM +AwQEz+VwAwQAz+V0MAwDBAHP5XYDBADP5XgwDAMEAc/legMEB8/lAAMEAtBSSAME +BtEqwAMEBdGigAMEBdHOAAMEAdHOJgMEBNHVMAMEBdH64DAMAwQA0fvDAwQB0fvE +AwQB0fv8AwQH1AAAMAwDBAXUAKADBAHUCOQwDAMEA9QI6AMEANQI8DAMAwQB1Ajy +AwQB1Aj8MAsDAwDUCQMEBdQMwDALAwMA1A0DBAXUFoAwDAMEBtQWwAMEBtQxADAM +AwQF1DFgAwQH1DQAMAwDBAXUNKADBAbUPAAwCwMEBdQ8YAMDBtQAMAwDBAfUQIAD +BATURYAwDAMEBdRFoAMEBtRVgDAMAwQF1FXgAwQF1FhAMAwDBAfUWIADBAHUXGQw +DAMEA9RcaAMEB9RfADALAwQF1F+gAwMF1EAwDAMEBdRgIAMEBtRkADAMAwQF1GRg +AwQF1GeAMAwDBAbUZ8ADBATUdSAwDAMEBtR1QAMEBdR6wDALAwMA1HsDBAfUgQAw +CgMDAdSCAwMA1NgwDAMEB9TZgAMEBtU3ADAMAwQH1TeAAwQG1YMAMAwDBAXVg2AD +BAXViEAwDAMEB9WIgAMEBtWTADAMAwQF1ZNgAwQF1ZZAAwQF1ZaAMAwDBAXVluAD +BAbVmAAwDAMEBdWYYAMEBdWaADAMAwQF1ZpgAwQF1Z6AMAwDBAbVnsADBAHVnwww +DAMEBNWfEAMEAtWfgDAMAwQD1Z+IAwQH1awAMAwDBAXVrKADBAXVs4AwDAMEBtWz +wAMEBdW1wDALAwMB1bYDBAXVwQAwDAMEBtXBQAMEBtXUgDAKAwMA1dUDAwDV9jAM +AwQF1fcgAwQH1f8AMAoDBAXV/6ADAgHUAwQB2C58AwQA2GPeAwQE2J5gAwQE2KxA +AwQG2NWAAwQF2PGAMAoDAgDZAwQE2Q5AMAwDBAXZDmADBAXZFMAwDAMEBNkU8AME +BNkVYDAMAwQH2RWAAwQH2R0AMAwDBATZHZADBATZHcAwCwMEBdkd4AMDAtkwMAsD +AwPZOAMEBdlAQDAMAwQE2UBwAwQG2U0AMAwDBATZTVADBAbZTgAwCwMEBNlOUAMD +ANl0MAsDBATZdRADAwDZijALAwMC2YwDBAPZk7AwDAMEBtmTwAMEBNmqgDALAwQF +2aqgAwMB2bAwCwMDANmzAwQE2ceAMAoDBAXZx6ADAgHYAwQC3J7EMGQEAgACMF4D +BQAgAQAFMA0DBAEgAQYDBQEgAQf4MA0DBQAgAQf7AwQCIAEIAwQCIAEUMAwDBAEg +ARoDBAEgAUADBAEgAUYwDAMEASABSgMEASABTAMEBCABUAMEBiADAAMDBCoAMIIG +oAYIKwYBBQUHAR0EggaSMIIGjqCCBoowggaGAgEHAgEcAgIAiQICAOAwCAICAPgC +AgD7AgIBBQICAR4CAgEgAgIBJgICAXcCAgF6AgICAQICAgUwCAICAhACAgIRAgIC +IAICAikCAgIvAgICNQICAk4CAgJRAgICnTAIAgICpwICAqgwCAICArcCAgK5MAgC +AgLFAgICxgICAsgCAgLPMAgCAgL4AgIC+QICAvwCAgL+MAgCAgMGAgIDDwICAxIw +CAICAxUCAgMWMAgCAgRNAgIEsAICBLMCAgS1AgIEvTAIAgIE0gICBNMCAgTZAgIE +4AICBOUCAgTpMAgCAgTzAgIE+wICBP8CAgUKAgIFETAIAgIFEwICBR0CAgUmAgIF +PjAIAgIFSAICBUkCAgYLMAgCAgZ1AgIGdgICBn8CAgaQMAgCAgarAgIGvgICBsEC +AgbEMAgCAgbKAgIGywICBs0CAgbUAgIG2DAIAgIG2gICBtwCAgbfAgIG5DAIAgIG +6gICBusCAgbuAgIG8AICBvQCAgcpMAgCAgcrAgIHLQICBzEwCAICBzkCAgc6MAgC +Agc9AgIHPjAIAgIHVQICB28wCAICB4ECAgeDAgIHhgICB4owCAICB48CAgejMAgC +AgeoAgIHqgICB68CAgfUAgIH3DAIAgIH4AICB+EwCAICB+oCAgftAgIH9DAIAgIH +9gICB/gCAgf7AgIH/QICB/8CAggBMAgCAggJAgIIWDAIAgIIYwICCGQwCAICCH4C +AgjhMAgCAgjmAgIJSQICCUwwCAICCVMCAgm4AgIJvjAIAgIJ4QICCeICAgntMAgC +AgnyAgIJ8wICChIwCAICChkCAgo2AgIKUwICClcCAgp7AgIKzjAIAgIK1QICCwYw +CAICCw4CAgs/AgILTwICC2UCAgtpAgIL8jAIAgIMCwICDCUCAgxPMAgCAgxSAgIM +hzAIAgIMiQICDRkwCAICDVQCAg1XAgIOKAICDwMwCAICD00CAg9OAgIQNDAIAgIR +NQICEU4wCAICEWkCAhFqAgIRrDAIAgIR7AICEe0CAhNuAgIT4TAIAgIVAQICFZ8w +CAICFaECAhX/AgIXswICF8UCAhgYAgIYsAICGQwwCAICGgACAho4MAgCAho6AgIa +3jAIAgIa4AICGv8CAh+dMAgCAiAAAgIhSzAIAgIhTQICIkEwCAICIkMCAiOoMAgC +AiOqAgIj/wICLE0CAi2MAgIvDjAIAgIwAAICMKYwCAICMKgCAjELMAgCAjENAgIz +pzAIAgIzqQICM/8CAjY3MAgCAjwAAgI8JjAIAgI8KAICPHIwCAICPHQCAj1ZMAgC +Aj1bAgI9uzAIAgI9vQICPdAwCAICPdICAj3ZMAgCAj3bAgI+WzAIAgI+XQICPrkw +CAICPrsCAj9VMAgCAj9XAgI/mzAIAgI/nQICP/8CAkksAgJK6gICS7ACAkvHMAgC +AlAAAgJQAzAIAgJQBQICUXkwCAICUXsCAlG/MAgCAlHBAgJSCjAIAgJSDAICUp8w +CAICUqECAlL5MAgCAlL7AgJTFjAIAgJTGAICUx0CAlMfMAgCAlMhAgJTjjAIAgJT +kAICU8swCAICU80CAlP/AgJWXAICWGMCAlibAgJayjAIAgJgAAICYJ8wCAICYKEC +AmC0MAgCAmC2AgJg0zAIAgJg1QICYOAwCAICYOICAmECMAgCAmEEAgJhHjAIAgJh +IAICYS0wCAICYS8CAmGaMAgCAmGcAgJiSjAIAgJiTAICYqEwCAICYqMCAmMRAgJj +EzAIAgJjFQICY8YwCAICY8gCAmPfMAgCAmPhAgJj5zAIAgJj6QICY/8CAmUYMAgC +AnAAAgJwCjAIAgJwDAICcBkwCAICcBsCAnDwMAgCAnDyAgJxojAIAgJxpAICcpkC +AnKbMAgCAnKdAgJy8zAIAgJy9QICczYwCAICczgCAnNnMAgCAnNpAgJzgjAIAgJz +hAICc60wCAICc68CAnPpMAgCAnPrAgJz/zAIAgJ4AAICeK8wCAICeLECAnkDAgJ5 +BTAIAgJ5GAICeVgwCAICeVoCAnoMMAgCAnoOAgJ7gjAIAgJ7hAICe/8wCgIDAIQA +AgMAi/8wCgIDAJgAAgMAm/8wCgIDAKAAAgMApY0wCgIDAKWPAgMAr/8wCgIDALgA +AgMAy/8wCgIDANwAAgMA4/8wCgIDAOgAAgMA7/8wCgIDAPIAAgMA8/8wCgIDAPuM +AgMA++8wCgIDAwAAAgMDNZs= +""" + + def setUp(self): + self.asn1Spec = rfc5280.Extensions() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.extns_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + oids = [ ] + for extn in asn1Object: + oids.append(extn['extnID']) + extn_value, rest = der_decode(extn['extnValue'], + rfc5280.certificateExtensionsMap[extn['extnID']]) + assert not rest + assert extn_value.prettyPrint() + assert der_encode(extn_value) == extn['extnValue'] + + assert rfc8360.id_pe_ipAddrBlocks_v2 in oids + assert rfc8360.id_pe_autonomousSysIds_v2 in oids + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + import sys + + result = unittest.TextTestRunner(verbosity=2).run(suite) + sys.exit(not result.wasSuccessful()) -- cgit v1.2.3 From 4dfe517a355d939902d223decf1efc0a4409a96b Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Sat, 5 Oct 2019 17:45:52 -0400 Subject: Add support for RFC 8358 (#73) --- CHANGES.txt | 1 + pyasn1_modules/rfc8358.py | 50 ++++++++++++ tests/__main__.py | 1 + tests/test_rfc8358.py | 200 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 252 insertions(+) create mode 100644 pyasn1_modules/rfc8358.py create mode 100644 tests/test_rfc8358.py diff --git a/CHANGES.txt b/CHANGES.txt index 3aa63bf..352175e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -41,6 +41,7 @@ Revision 0.2.7, released XX-09-2019 - Added RFC8479 providing Storing Validation Parameters in PKCS#8 - Added RFC8360 providing Resource Public Key Infrastructure (RPKI) Validation Reconsidered +- Added RFC8358 providing Digital Signatures on Internet-Draft Documents Revision 0.2.6, released 31-07-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc8358.py b/pyasn1_modules/rfc8358.py new file mode 100644 index 0000000..647a366 --- /dev/null +++ b/pyasn1_modules/rfc8358.py @@ -0,0 +1,50 @@ +# +# 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 +# +# Digital Signatures on Internet-Draft Documents +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc8358.txt +# + +from pyasn1.type import univ + +from pyasn1_modules import rfc5652 + + +id_ct = univ.ObjectIdentifier('1.2.840.113549.1.9.16.1') + +id_ct_asciiTextWithCRLF = id_ct + (27, ) + +id_ct_epub = id_ct + (39, ) + +id_ct_htmlWithCRLF = id_ct + (38, ) + +id_ct_pdf = id_ct + (29, ) + +id_ct_postscript = id_ct + (30, ) + +id_ct_utf8TextWithCRLF = id_ct + (37, ) + +id_ct_xml = id_ct + (28, ) + + +# Map of Content Type OIDs to Content Types is added to the +# ones that are in rfc5652.py + +_cmsContentTypesMapUpdate = { + id_ct_asciiTextWithCRLF: univ.OctetString(), + id_ct_epub: univ.OctetString(), + id_ct_htmlWithCRLF: univ.OctetString(), + id_ct_pdf: univ.OctetString(), + id_ct_postscript: univ.OctetString(), + id_ct_utf8TextWithCRLF: univ.OctetString(), + id_ct_xml: univ.OctetString(), +} + +rfc5652.cmsContentTypesMap.update(_cmsContentTypesMapUpdate) diff --git a/tests/__main__.py b/tests/__main__.py index 94801f1..979b1ba 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -62,6 +62,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc8018.suite', 'tests.test_rfc8103.suite', 'tests.test_rfc8226.suite', + 'tests.test_rfc8358.suite', 'tests.test_rfc8360.suite', 'tests.test_rfc8398.suite', 'tests.test_rfc8410.suite', diff --git a/tests/test_rfc8358.py b/tests/test_rfc8358.py new file mode 100644 index 0000000..bbe37e0 --- /dev/null +++ b/tests/test_rfc8358.py @@ -0,0 +1,200 @@ +# +# 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 rfc5652 +from pyasn1_modules import rfc8358 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class P7STestCase(unittest.TestCase): + pem_text_list = ( +"""\ +MIIJWgYJKoZIhvcNAQcCoIIJSzCCCUcCAQMxDTALBglghkgBZQMEAgEwDQYLKoZIhvcNAQkQ +ARugggZ0MIIGcDCCBVigAwIBAgIRANa58hQvZ26svTWQaGtqo/YwDQYJKoZIhvcNAQELBQAw +gZcxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcT +B1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMT0wOwYDVQQDEzRDT01PRE8g +UlNBIENsaWVudCBBdXRoZW50aWNhdGlvbiBhbmQgU2VjdXJlIEVtYWlsIENBMB4XDTE1MDIx +MjAwMDAwMFoXDTIwMDIxMjIzNTk1OVowgZUxCzAJBgNVBAYTAlVTMREwDwYDVQQIEwhWaXJn +aW5pYTEPMA0GA1UEBxMGUmVzdG9uMRMwEQYDVQQKEwpJRVRGIFRydXN0MRkwFwYDVQQLExBT +ZWNyZXRhcmlhdCBXZXN0MQ0wCwYDVQQDEwRJRVRGMSMwIQYJKoZIhvcNAQkBFhRpZXRmLWFj +dGlvbkBpZXRmLm9yZzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMUkKtMPP1RA +FU6sxMezYJKCt4rw30RDieB8/P67TMhA6j8Um4a2Xo+CP9Ce1oMri2bwaaQPYWB4ciEL32za +0NUE0B0iCjZZl36hon6wW6mJw1NGD/AFxnKWzhkSWG6BHMoeOAzu/ye8sHu4Jp5nazpGptK7 +30SjTS3JJFU9pHwQY6JlcmwVv0j2rsT3gj92Cbj5S+U5wCSE6+mZbCC+VPFeeI1kFITwyaIm +uK9kSYHr15OXua/jrYNrHNRfqtexGKSgnUT96KkTh9TVvkMETB1WJS4WuEIP6GITvwVTp0lA +qS3oNO4SM4tgFVdYqppcvZBg52kHY9y7IdR156c99zzZDBfWBduqjs/AXa0uol0EJd7qFLUs +xEJ96XN3tPgR/Cwq18ec29pZQH6kO81Kato/RsQrj6A05TFx/J0MYE0R1MZqvIDUu55vlicb +wT2lpXMiz1szKuvjTZRR9H/IgbKPNpt/kDUSgXLYwuKBm+nBoJXgybEyJ+A4arb60d9Uiusu +UA8/h6s1rDMuTnIYMbIii4Y+KgevBWPawqk0xioilEMJ0RBaBVrDreuFlK8aYh+Jo2piruBA +QnB9ZaPmEM1HPNArJxqL6XcUJTkFxNSksOATDFV5sEoBWYIe6qv2nV2r/HWDAEaa4WH2h3o/ +kASarXk3SxPXmfjOOr1XgpKjAgMBAAGjggG1MIIBsTAfBgNVHSMEGDAWgBSCr2yM+MX+lmF8 +6B89K3FIXsSLwDAdBgNVHQ4EFgQU7Olc92Oy6nkCvpv6jCj6qN8YPtUwDgYDVR0PAQH/BAQD +AgeAMAwGA1UdEwEB/wQCMAAwRgYDVR0gBD8wPTA7BgwrBgEEAbIxAQIBAwUwKzApBggrBgEF +BQcCARYdaHR0cHM6Ly9zZWN1cmUuY29tb2RvLmNvbS9DUFMwWgYDVR0fBFMwUTBPoE2gS4ZJ +aHR0cDovL2NybC5jb21vZG9jYS5jb20vQ09NT0RPUlNBQ2xpZW50QXV0aGVudGljYXRpb25h +bmRTZWN1cmVFbWFpbENBLmNybDCBiwYIKwYBBQUHAQEEfzB9MFUGCCsGAQUFBzAChklodHRw +Oi8vY3J0LmNvbW9kb2NhLmNvbS9DT01PRE9SU0FDbGllbnRBdXRoZW50aWNhdGlvbmFuZFNl +Y3VyZUVtYWlsQ0EuY3J0MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20w +HwYDVR0RBBgwFoEUaWV0Zi1hY3Rpb25AaWV0Zi5vcmcwDQYJKoZIhvcNAQELBQADggEBAGPm +QUKHxkEQ9vk69vIP68anHc8UsTv5powtLSXLqUw3rAoKAdoWkKjb7ZByHCuFiNk1BvTnhQPh +LAZm5dI8dYWKp3zgWVxsCXOQv2K4XbaQpIk8KKdLycHWsOq2OD4xBdhfTQqDj9EidhxaLf4B +bRUePOuWVvwNqHI6OQ9FbRllUsTsSH3XK7z9Ru/0Ub07uEzmWyrIpeFRLJUg9EqQj25pw8j7 +N9ym8ItpfEQvK4Nrzt9KnGwFDaNOUjYAvejig9iUNdOXEQKVzbq8fC25HrXPQisq8u2jrP38 +cRqzwgGHZ1bJrQa8+LPBfADZ4ZHeqlEe6IqZhS/wDSuDNCIZHtkxggKqMIICpgIBA4AU7Olc +92Oy6nkCvpv6jCj6qN8YPtUwCwYJYIZIAWUDBAIBoGswGgYJKoZIhvcNAQkDMQ0GCyqGSIb3 +DQEJEAEbMBwGCSqGSIb3DQEJBTEPFw0xOTA2MDkxNjU3NTdaMC8GCSqGSIb3DQEJBDEiBCDx +ACvH9u26K1BdX+IPp6vguUAtA9k0lp9JMNunvXTuQzANBgkqhkiG9w0BAQEFAASCAgBY8kFl +SxQIvU4n6LaVoAV6ibHrlCqOp9KrUc9DmwXtDifsgoGfhDHb6i5k9BSHmerjTGF6mLlquPUV +Z2EHSUuVpk8rX//ki6vngq91+f+ufrzEpvO6BLc2aO/zOat0W3U2hiq3zJSLMYMNZhX484Nq +9+ImsU0S5f32ZpEXH0lFINUaZFo0eRAOZywqNuY57stjWBxTI6MA00S0+eMuWlmkMy0C2LL9 +BQvWW01/ri2UDEprAiKo2sXLcScgHimEVYHuWsrnP+sJ3XVWRsWGRW2i5qIalu2ZGmmIU/vg +bdBzQnAjCoS2xC5Kwv+cqtUiiyLI0nnuu1aKKi4hivmt1n+hSIWWgGNwTFn3S4+mYDDNSH0u +ocOr0uDFVv/SH9QPQuGh9rpSz3cd3hlA4R63Rylm46Tt6DnXiovu0mDoos68UQjIAPXWj1ES +Peeubp+wSbuqN8Rh+koZU+HK7YpsR2bB4hL0GIwMA9lQjGSCxPCt1ViRL6zAWECzQC1YgLyc ++f1Fe8pkaWUbZz+18H/rJoKsXiNWH8yhfAyk+JGTxc4qxWJ/BuF0vzSyuVEffuxIHrOMZTpO ++xfAaJVDqFjxT5yKj3dCfy6XSDZq39AeX/w26/WfH+0ALRiViAAaMHSldbawVR/W3isecDWF +tlU4NSJMLi/tTohe0QN1fjOaFryAvw== +""", +"""\ +MIIJWgYJKoZIhvcNAQcCoIIJSzCCCUcCAQMxDTALBglghkgBZQMEAgEwDQYLKoZIhvcNAQkQ +ARygggZ0MIIGcDCCBVigAwIBAgIRANa58hQvZ26svTWQaGtqo/YwDQYJKoZIhvcNAQELBQAw +gZcxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcT +B1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMT0wOwYDVQQDEzRDT01PRE8g +UlNBIENsaWVudCBBdXRoZW50aWNhdGlvbiBhbmQgU2VjdXJlIEVtYWlsIENBMB4XDTE1MDIx +MjAwMDAwMFoXDTIwMDIxMjIzNTk1OVowgZUxCzAJBgNVBAYTAlVTMREwDwYDVQQIEwhWaXJn +aW5pYTEPMA0GA1UEBxMGUmVzdG9uMRMwEQYDVQQKEwpJRVRGIFRydXN0MRkwFwYDVQQLExBT +ZWNyZXRhcmlhdCBXZXN0MQ0wCwYDVQQDEwRJRVRGMSMwIQYJKoZIhvcNAQkBFhRpZXRmLWFj +dGlvbkBpZXRmLm9yZzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMUkKtMPP1RA +FU6sxMezYJKCt4rw30RDieB8/P67TMhA6j8Um4a2Xo+CP9Ce1oMri2bwaaQPYWB4ciEL32za +0NUE0B0iCjZZl36hon6wW6mJw1NGD/AFxnKWzhkSWG6BHMoeOAzu/ye8sHu4Jp5nazpGptK7 +30SjTS3JJFU9pHwQY6JlcmwVv0j2rsT3gj92Cbj5S+U5wCSE6+mZbCC+VPFeeI1kFITwyaIm +uK9kSYHr15OXua/jrYNrHNRfqtexGKSgnUT96KkTh9TVvkMETB1WJS4WuEIP6GITvwVTp0lA +qS3oNO4SM4tgFVdYqppcvZBg52kHY9y7IdR156c99zzZDBfWBduqjs/AXa0uol0EJd7qFLUs +xEJ96XN3tPgR/Cwq18ec29pZQH6kO81Kato/RsQrj6A05TFx/J0MYE0R1MZqvIDUu55vlicb +wT2lpXMiz1szKuvjTZRR9H/IgbKPNpt/kDUSgXLYwuKBm+nBoJXgybEyJ+A4arb60d9Uiusu +UA8/h6s1rDMuTnIYMbIii4Y+KgevBWPawqk0xioilEMJ0RBaBVrDreuFlK8aYh+Jo2piruBA +QnB9ZaPmEM1HPNArJxqL6XcUJTkFxNSksOATDFV5sEoBWYIe6qv2nV2r/HWDAEaa4WH2h3o/ +kASarXk3SxPXmfjOOr1XgpKjAgMBAAGjggG1MIIBsTAfBgNVHSMEGDAWgBSCr2yM+MX+lmF8 +6B89K3FIXsSLwDAdBgNVHQ4EFgQU7Olc92Oy6nkCvpv6jCj6qN8YPtUwDgYDVR0PAQH/BAQD +AgeAMAwGA1UdEwEB/wQCMAAwRgYDVR0gBD8wPTA7BgwrBgEEAbIxAQIBAwUwKzApBggrBgEF +BQcCARYdaHR0cHM6Ly9zZWN1cmUuY29tb2RvLmNvbS9DUFMwWgYDVR0fBFMwUTBPoE2gS4ZJ +aHR0cDovL2NybC5jb21vZG9jYS5jb20vQ09NT0RPUlNBQ2xpZW50QXV0aGVudGljYXRpb25h +bmRTZWN1cmVFbWFpbENBLmNybDCBiwYIKwYBBQUHAQEEfzB9MFUGCCsGAQUFBzAChklodHRw +Oi8vY3J0LmNvbW9kb2NhLmNvbS9DT01PRE9SU0FDbGllbnRBdXRoZW50aWNhdGlvbmFuZFNl +Y3VyZUVtYWlsQ0EuY3J0MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20w +HwYDVR0RBBgwFoEUaWV0Zi1hY3Rpb25AaWV0Zi5vcmcwDQYJKoZIhvcNAQELBQADggEBAGPm +QUKHxkEQ9vk69vIP68anHc8UsTv5powtLSXLqUw3rAoKAdoWkKjb7ZByHCuFiNk1BvTnhQPh +LAZm5dI8dYWKp3zgWVxsCXOQv2K4XbaQpIk8KKdLycHWsOq2OD4xBdhfTQqDj9EidhxaLf4B +bRUePOuWVvwNqHI6OQ9FbRllUsTsSH3XK7z9Ru/0Ub07uEzmWyrIpeFRLJUg9EqQj25pw8j7 +N9ym8ItpfEQvK4Nrzt9KnGwFDaNOUjYAvejig9iUNdOXEQKVzbq8fC25HrXPQisq8u2jrP38 +cRqzwgGHZ1bJrQa8+LPBfADZ4ZHeqlEe6IqZhS/wDSuDNCIZHtkxggKqMIICpgIBA4AU7Olc +92Oy6nkCvpv6jCj6qN8YPtUwCwYJYIZIAWUDBAIBoGswGgYJKoZIhvcNAQkDMQ0GCyqGSIb3 +DQEJEAEcMBwGCSqGSIb3DQEJBTEPFw0xOTA2MDkxNjU3NTdaMC8GCSqGSIb3DQEJBDEiBCBg +ifxBsUb2E8RicFvqZB+NJEs1FOG4hFFU1bPqV2UwGzANBgkqhkiG9w0BAQEFAASCAgCApFAS +4+cYrnkMebrANXw7/TGn6Qx01p9fuOugQb6lcfE5CysIKGLJJogs0BXwHK4jTeJRdt/lutuz +bACg1bakABxuCiLWMu3pKCKS94qAgElYgWru+pAxPhuslz5MwAU0qFW3KnaNq3f5wXlVQ+h2 +l9spSiLhAQ+vLTLfotn6tCmUfjaaYsoNIUGg6b/2vH75QGYaXDq9YGoCrrkDbaRS4eDenSL5 +S2fBTZ5VMJE/1VQY1D5CWqt2CTfzRkNkU7mkarPy6SPvguDlqKJJnFaZJmeIYbGOpDt6KxWc +DLFD9+J6CH492QwlHxDtM94nK1oIaqdu9TTV94t0ToGezElOZZuVA2DVkov5DzrYQLI5GjMw +7iHXW1ewCaGF38DdOopqBYp7jcCCZpruKBWDq/uz40MzSBrffYTP/dg4//8Awvt/JomvTUoH +E18Pt/G2cqdw0NqOE7YEcFpsLGfikTWmGhnrcYUkt8odDDAv/vqZRt8DLkB56waQeQw0TLit +2M3gbTSHJ1KFsBM/kqHanVapGtnClkY7hYh8DVpgJymJpupkNFs8lDNbN4C42DhQ6Oz9P2qu +8a/ybEb5gMZ3fsVLvvp6LhbJfqIvYgZO2uKXeKg3eLASD5nVY/Tuhnn2plhx+weKULGys0Ov +zPKZ+N96KLerIBr3FmGByqhr3jNrBw== +""", +"""\ +MIIJWgYJKoZIhvcNAQcCoIIJSzCCCUcCAQMxDTALBglghkgBZQMEAgEwDQYLKoZIhvcNAQkQ +AR2gggZ0MIIGcDCCBVigAwIBAgIRANa58hQvZ26svTWQaGtqo/YwDQYJKoZIhvcNAQELBQAw +gZcxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcT +B1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMT0wOwYDVQQDEzRDT01PRE8g +UlNBIENsaWVudCBBdXRoZW50aWNhdGlvbiBhbmQgU2VjdXJlIEVtYWlsIENBMB4XDTE1MDIx +MjAwMDAwMFoXDTIwMDIxMjIzNTk1OVowgZUxCzAJBgNVBAYTAlVTMREwDwYDVQQIEwhWaXJn +aW5pYTEPMA0GA1UEBxMGUmVzdG9uMRMwEQYDVQQKEwpJRVRGIFRydXN0MRkwFwYDVQQLExBT +ZWNyZXRhcmlhdCBXZXN0MQ0wCwYDVQQDEwRJRVRGMSMwIQYJKoZIhvcNAQkBFhRpZXRmLWFj +dGlvbkBpZXRmLm9yZzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMUkKtMPP1RA +FU6sxMezYJKCt4rw30RDieB8/P67TMhA6j8Um4a2Xo+CP9Ce1oMri2bwaaQPYWB4ciEL32za +0NUE0B0iCjZZl36hon6wW6mJw1NGD/AFxnKWzhkSWG6BHMoeOAzu/ye8sHu4Jp5nazpGptK7 +30SjTS3JJFU9pHwQY6JlcmwVv0j2rsT3gj92Cbj5S+U5wCSE6+mZbCC+VPFeeI1kFITwyaIm +uK9kSYHr15OXua/jrYNrHNRfqtexGKSgnUT96KkTh9TVvkMETB1WJS4WuEIP6GITvwVTp0lA +qS3oNO4SM4tgFVdYqppcvZBg52kHY9y7IdR156c99zzZDBfWBduqjs/AXa0uol0EJd7qFLUs +xEJ96XN3tPgR/Cwq18ec29pZQH6kO81Kato/RsQrj6A05TFx/J0MYE0R1MZqvIDUu55vlicb +wT2lpXMiz1szKuvjTZRR9H/IgbKPNpt/kDUSgXLYwuKBm+nBoJXgybEyJ+A4arb60d9Uiusu +UA8/h6s1rDMuTnIYMbIii4Y+KgevBWPawqk0xioilEMJ0RBaBVrDreuFlK8aYh+Jo2piruBA +QnB9ZaPmEM1HPNArJxqL6XcUJTkFxNSksOATDFV5sEoBWYIe6qv2nV2r/HWDAEaa4WH2h3o/ +kASarXk3SxPXmfjOOr1XgpKjAgMBAAGjggG1MIIBsTAfBgNVHSMEGDAWgBSCr2yM+MX+lmF8 +6B89K3FIXsSLwDAdBgNVHQ4EFgQU7Olc92Oy6nkCvpv6jCj6qN8YPtUwDgYDVR0PAQH/BAQD +AgeAMAwGA1UdEwEB/wQCMAAwRgYDVR0gBD8wPTA7BgwrBgEEAbIxAQIBAwUwKzApBggrBgEF +BQcCARYdaHR0cHM6Ly9zZWN1cmUuY29tb2RvLmNvbS9DUFMwWgYDVR0fBFMwUTBPoE2gS4ZJ +aHR0cDovL2NybC5jb21vZG9jYS5jb20vQ09NT0RPUlNBQ2xpZW50QXV0aGVudGljYXRpb25h +bmRTZWN1cmVFbWFpbENBLmNybDCBiwYIKwYBBQUHAQEEfzB9MFUGCCsGAQUFBzAChklodHRw +Oi8vY3J0LmNvbW9kb2NhLmNvbS9DT01PRE9SU0FDbGllbnRBdXRoZW50aWNhdGlvbmFuZFNl +Y3VyZUVtYWlsQ0EuY3J0MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20w +HwYDVR0RBBgwFoEUaWV0Zi1hY3Rpb25AaWV0Zi5vcmcwDQYJKoZIhvcNAQELBQADggEBAGPm +QUKHxkEQ9vk69vIP68anHc8UsTv5powtLSXLqUw3rAoKAdoWkKjb7ZByHCuFiNk1BvTnhQPh +LAZm5dI8dYWKp3zgWVxsCXOQv2K4XbaQpIk8KKdLycHWsOq2OD4xBdhfTQqDj9EidhxaLf4B +bRUePOuWVvwNqHI6OQ9FbRllUsTsSH3XK7z9Ru/0Ub07uEzmWyrIpeFRLJUg9EqQj25pw8j7 +N9ym8ItpfEQvK4Nrzt9KnGwFDaNOUjYAvejig9iUNdOXEQKVzbq8fC25HrXPQisq8u2jrP38 +cRqzwgGHZ1bJrQa8+LPBfADZ4ZHeqlEe6IqZhS/wDSuDNCIZHtkxggKqMIICpgIBA4AU7Olc +92Oy6nkCvpv6jCj6qN8YPtUwCwYJYIZIAWUDBAIBoGswGgYJKoZIhvcNAQkDMQ0GCyqGSIb3 +DQEJEAEdMBwGCSqGSIb3DQEJBTEPFw0xOTA3MTQwMTMyMTdaMC8GCSqGSIb3DQEJBDEiBCAJ +zK6u0RRfrSQ2ebn+GOxnbovlG3Raul/1zOOGmTaIPzANBgkqhkiG9w0BAQEFAASCAgBlKYNd +euVzPDqEa13k4nQthmyJUUqjWlAVolgohXioYok8Z5BkKmkp8ANLbvkJl0hV1Al1hutTRNeF +a5ZeWyS6nAWyPFKfRSNqwWLMIi1dX+rO7Vhf15Lz944ZYsqO+O2f7rjWUJmi8/uJKD7cFDiW +uKkPMgvqyIMnnC3ya/sC1vU+0Feqr5JcIMs2AHQeNVe8hzN4T9Pthyax7gqbxTkg3Gyt7Mwy +WLZeK84oJmkl9ANeVgzq+P/cmqUaqtfkBFDSxaTag/eoYM3QfHNisr/jHCazqCh88VMgwhvk +cl6NS9hdH+aOWqQ3FE1c7VJNoQRDT7ztyKCrRJFPc4wZL8tsGkKp1lP4WcaStcbUJ65AdWPb +3CZonLY4UOBotAUpG/PObMCmWBEpr8MN0Q+kuEO2oAe9kBoFsv7MtNfyHE4CuOANRqGLRgOL +72hN8Cy0sGWYUy+2chH3i50cT8XkDV5Rz2Z5xW3SfyAuW53j2WKLFsKkZjfkZBopSJM20V4E +8pPnQQ/ByFwYPyS/xJZc24vsRxgogbrf11JU8hKVkfSsq3JXxUxe5w+Sh1XGTmO5tXDKFfyi +S+VljWVifzXaR3pmTEQPhXH4nBa4K/HYytxofDP3EMli+imil2fFBbBedZkb5CIQ/Ly3soHZ +dZlmZDkyeXJLpkNjRAsG6V82raZd9g== +""", +) + + def setUp(self): + self.asn1Spec = rfc5652.ContentInfo() + + def testDerCodec(self): + oids = [ ] + for pem_text in self.pem_text_list: + substrate = pem.readBase64fromText(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'] + + oids.append(sd['encapContentInfo']['eContentType']) + + assert rfc8358.id_ct_asciiTextWithCRLF in oids + assert rfc8358.id_ct_pdf in oids + assert rfc8358.id_ct_xml in oids + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + import sys + + result = unittest.TextTestRunner(verbosity=2).run(suite) + sys.exit(not result.wasSuccessful()) -- cgit v1.2.3 From 7da04e58044fbd2e0f43b441dad1a37b98286cf9 Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Sun, 6 Oct 2019 04:20:25 -0400 Subject: Add support for RFC 8209 (#74) * Add support for RFC 8209 --- CHANGES.txt | 1 + pyasn1_modules/rfc8209.py | 20 ++++++++++++++++ tests/__main__.py | 1 + tests/test_rfc8209.py | 59 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+) create mode 100644 pyasn1_modules/rfc8209.py create mode 100644 tests/test_rfc8209.py diff --git a/CHANGES.txt b/CHANGES.txt index 352175e..da4c06f 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -42,6 +42,7 @@ Revision 0.2.7, released XX-09-2019 - Added RFC8360 providing Resource Public Key Infrastructure (RPKI) Validation Reconsidered - Added RFC8358 providing Digital Signatures on Internet-Draft Documents +- Added RFC8209 providing BGPsec Router PKI Profile Revision 0.2.6, released 31-07-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc8209.py b/pyasn1_modules/rfc8209.py new file mode 100644 index 0000000..7d70f51 --- /dev/null +++ b/pyasn1_modules/rfc8209.py @@ -0,0 +1,20 @@ +# +# 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 +# +# BGPsec Router PKI Profile +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc8209.txt +# + +from pyasn1.type import univ + + +id_kp = univ.ObjectIdentifier('1.3.6.1.5.5.7.3') + +id_kp_bgpsec_router = id_kp + (30, ) diff --git a/tests/__main__.py b/tests/__main__.py index 979b1ba..9b4cb18 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -61,6 +61,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc7906.suite', 'tests.test_rfc8018.suite', 'tests.test_rfc8103.suite', + 'tests.test_rfc8209.suite', 'tests.test_rfc8226.suite', 'tests.test_rfc8358.suite', 'tests.test_rfc8360.suite', diff --git a/tests/test_rfc8209.py b/tests/test_rfc8209.py new file mode 100644 index 0000000..1eb2efd --- /dev/null +++ b/tests/test_rfc8209.py @@ -0,0 +1,59 @@ +# +# 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 rfc8209 + +try: + import unittest2 as unittest +except ImportError: + import unittest + +class CertificateTestCase(unittest.TestCase): + cert_pem_text = """\ +MIIBiDCCAS+gAwIBAgIEAk3WfDAKBggqhkjOPQQDAjAaMRgwFgYDVQQDDA9ST1VU +RVItMDAwMEZCRjAwHhcNMTcwMTAxMDUwMDAwWhcNMTgwNzAxMDUwMDAwWjAaMRgw +FgYDVQQDDA9ST1VURVItMDAwMEZCRjAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC +AARzkbq7kqDLO+EOWbGev/shTgSpHgy6GxOafTjZD3flWqBbjmlWeOD6FpBLVdnU +9cDfxYiV7lC8T3XSBaJb02/1o2MwYTALBgNVHQ8EBAMCB4AwHQYDVR0OBBYEFKtN +kQ9VyucaIV7zyv46zEW17sFUMBMGA1UdJQQMMAoGCCsGAQUFBwMeMB4GCCsGAQUF +BwEIAQH/BA8wDaAHMAUCAwD78KECBQAwCgYIKoZIzj0EAwIDRwAwRAIgB7e0al+k +8cxoNjkDpIPsfIAC0vYInUay7Cp75pKzb7ECIACRBUqh9bAYnSck6LQi/dEc8D2x +OCRdZCk1KI3uDDgp +""" + + def setUp(self): + self.asn1Spec = rfc5280.Certificate() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.cert_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + for extn in asn1Object['tbsCertificate']['extensions']: + if extn['extnID'] in rfc5280.certificateExtensionsMap.keys(): + extnValue, rest = der_decode(extn['extnValue'], + asn1Spec=rfc5280.certificateExtensionsMap[extn['extnID']]) + assert der_encode(extnValue) == extn['extnValue'] + + if extn['extnID'] == rfc5280.id_ce_extKeyUsage: + assert rfc8209.id_kp_bgpsec_router in extnValue + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) -- cgit v1.2.3 From e2f88827355c7dae440a27bf9d281abcf7d8fc5c Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Mon, 7 Oct 2019 13:02:45 -0400 Subject: Add test for RFC 3447 (#75) --- tests/__main__.py | 1 + tests/test_rfc3447.py | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 tests/test_rfc3447.py diff --git a/tests/__main__.py b/tests/__main__.py index 9b4cb18..511ef87 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -23,6 +23,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc2986.suite', 'tests.test_rfc3161.suite', 'tests.test_rfc3274.suite', + 'tests.test_rfc3447.suite', 'tests.test_rfc3560.suite', 'tests.test_rfc3565.suite', 'tests.test_rfc3709.suite', diff --git a/tests/test_rfc3447.py b/tests/test_rfc3447.py new file mode 100644 index 0000000..d4114c7 --- /dev/null +++ b/tests/test_rfc3447.py @@ -0,0 +1,73 @@ +# +# 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 rfc3447 + +try: + import unittest2 as unittest + +except ImportError: + import unittest + + +# openssl genrsa -primes 3 -f4 -out multiprime.key + +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 = rfc3447.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()) -- cgit v1.2.3 From d3a9ae35fb63cb2d8372a352da26e53a9009edfb Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Mon, 7 Oct 2019 16:44:32 -0400 Subject: Add support for RFC 8017 (#76) --- CHANGES.txt | 1 + pyasn1_modules/rfc8017.py | 153 ++++++++++++++++++++++++++++++++++++++++++++++ tests/__main__.py | 1 + tests/test_rfc8017.py | 124 +++++++++++++++++++++++++++++++++++++ 4 files changed, 279 insertions(+) create mode 100644 pyasn1_modules/rfc8017.py create mode 100644 tests/test_rfc8017.py 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()) -- cgit v1.2.3 From e33d2ac356fa13ac1f5c5622d4c7bd00202161d2 Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Tue, 8 Oct 2019 16:28:53 -0400 Subject: Add support for RFC 7914 (#77) --- CHANGES.txt | 1 + pyasn1_modules/rfc7914.py | 49 ++++++++++++++++++++++++ tests/__main__.py | 1 + tests/test_rfc7914.py | 95 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 146 insertions(+) create mode 100644 pyasn1_modules/rfc7914.py create mode 100644 tests/test_rfc7914.py diff --git a/CHANGES.txt b/CHANGES.txt index 4dbe1fa..9797e24 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -44,6 +44,7 @@ Revision 0.2.7, released XX-09-2019 - Added RFC8358 providing Digital Signatures on Internet-Draft Documents - Added RFC8209 providing BGPsec Router PKI Profile - Added RFC8017 providing PKCS #1 Version 2.2 +- Added RFC7914 providing scrypt Password-Based Key Derivation Function Revision 0.2.6, released 31-07-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc7914.py b/pyasn1_modules/rfc7914.py new file mode 100644 index 0000000..99e9551 --- /dev/null +++ b/pyasn1_modules/rfc7914.py @@ -0,0 +1,49 @@ +# +# 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 +# +#The scrypt Password-Based Key Derivation Function +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc8520.txt +# https://www.rfc-editor.org/errata/eid5871 +# + +from pyasn1.type import constraint +from pyasn1.type import namedtype +from pyasn1.type import univ + +from pyasn1_modules import rfc5280 + +MAX = float('inf') + + +id_scrypt = univ.ObjectIdentifier('1.3.6.1.4.1.11591.4.11') + + +class Scrypt_params(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('salt', + univ.OctetString()), + namedtype.NamedType('costParameter', + univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(1, MAX))), + namedtype.NamedType('blockSize', + univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(1, MAX))), + namedtype.NamedType('parallelizationParameter', + univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(1, MAX))), + namedtype.OptionalNamedType('keyLength', + univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(1, MAX))) + ) + + +# Update the Algorithm Identifier map in rfc5280.py + +_algorithmIdentifierMapUpdate = { + id_scrypt: Scrypt_params(), +} + +rfc5280.algorithmIdentifierMap.update(_algorithmIdentifierMapUpdate) diff --git a/tests/__main__.py b/tests/__main__.py index 36290c6..b79752f 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_rfc7914.suite', 'tests.test_rfc8017.suite', 'tests.test_rfc8018.suite', 'tests.test_rfc8103.suite', diff --git a/tests/test_rfc7914.py b/tests/test_rfc7914.py new file mode 100644 index 0000000..1ab8b8f --- /dev/null +++ b/tests/test_rfc7914.py @@ -0,0 +1,95 @@ +# +# 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 rfc5958 +from pyasn1_modules import rfc7914 +from pyasn1_modules import rfc8018 + +try: + import unittest2 as unittest + +except ImportError: + import unittest + + +# From RFC 7914, Section 13 + +class MultiprimeRSAPrivateKeyTestCase(unittest.TestCase): + pem_text = """\ +MIHiME0GCSqGSIb3DQEFDTBAMB8GCSsGAQQB2kcECzASBAVNb3VzZQIDEAAAAgEI +AgEBMB0GCWCGSAFlAwQBKgQQyYmguHMsOwzGMPoyObk/JgSBkJb47EWd5iAqJlyy ++ni5ftd6gZgOPaLQClL7mEZc2KQay0VhjZm/7MbBUNbqOAXNM6OGebXxVp6sHUAL +iBGY/Dls7B1TsWeGObE0sS1MXEpuREuloZjcsNVcNXWPlLdZtkSH6uwWzR0PyG/Z ++ZXfNodZtd/voKlvLOw5B3opGIFaLkbtLZQwMiGtl42AS89lZg== +""" + + def setUp(self): + self.asn1Spec = rfc5958.EncryptedPrivateKeyInfo() + + 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 + + ea = asn1Object['encryptionAlgorithm'] + assert ea['algorithm'] == rfc8018.id_PBES2 + assert ea['algorithm'] in rfc5280.algorithmIdentifierMap.keys() + + params, rest = der_decode(ea['parameters'], + asn1Spec=rfc5280.algorithmIdentifierMap[ea['algorithm']]) + assert not rest + assert params.prettyPrint() + assert der_encode(params) == ea['parameters'] + + kdf = params['keyDerivationFunc'] + assert kdf['algorithm'] == rfc7914.id_scrypt + assert kdf['algorithm'] in rfc5280.algorithmIdentifierMap.keys() + + kdfp, rest = der_decode(kdf['parameters'], + asn1Spec=rfc5280.algorithmIdentifierMap[kdf['algorithm']]) + assert not rest + assert kdfp.prettyPrint() + assert der_encode(kdfp) == kdf['parameters'] + + assert kdfp['costParameter'] == 1048576 + + 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 + + ea = asn1Object['encryptionAlgorithm'] + assert ea['algorithm'] == rfc8018.id_PBES2 + + params = asn1Object['encryptionAlgorithm']['parameters'] + assert params['keyDerivationFunc']['algorithm'] == rfc7914.id_scrypt + + kdfp = params['keyDerivationFunc']['parameters'] + assert kdfp['costParameter'] == 1048576 + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + import sys + + result = unittest.TextTestRunner(verbosity=2).run(suite) + sys.exit(not result.wasSuccessful()) -- cgit v1.2.3 From 96d522c2f7e88f13fd17fdf344d0cf966fe57214 Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Wed, 9 Oct 2019 11:26:29 -0400 Subject: Add support for RFC 7773 (#78) --- CHANGES.txt | 1 + pyasn1_modules/rfc7773.py | 52 +++++++++++++++++++++ tests/__main__.py | 1 + tests/test_rfc7773.py | 114 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 168 insertions(+) create mode 100644 pyasn1_modules/rfc7773.py create mode 100644 tests/test_rfc7773.py diff --git a/CHANGES.txt b/CHANGES.txt index 9797e24..61b240b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -45,6 +45,7 @@ Revision 0.2.7, released XX-09-2019 - Added RFC8209 providing BGPsec Router PKI Profile - Added RFC8017 providing PKCS #1 Version 2.2 - Added RFC7914 providing scrypt Password-Based Key Derivation Function +- Added RFC7773 providing Authentication Context Certificate Extension Revision 0.2.6, released 31-07-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc7773.py b/pyasn1_modules/rfc7773.py new file mode 100644 index 0000000..0fee2aa --- /dev/null +++ b/pyasn1_modules/rfc7773.py @@ -0,0 +1,52 @@ +# +# This file is part of pyasn1-modules software. +# +# Created by Russ Housley with some assistance from asn1ate v.0.6.0. +# +# Copyright (c) 2019, Vigil Security, LLC +# License: http://snmplabs.com/pyasn1/license.html +# +# Authentication Context Certificate Extension +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc7773.txt +# + +from pyasn1.type import char +from pyasn1.type import constraint +from pyasn1.type import namedtype +from pyasn1.type import univ + +from pyasn1_modules import rfc5280 + +MAX = float('inf') + + +# Authentication Context Extension + +e_legnamnden = univ.ObjectIdentifier('1.2.752.201') + +id_eleg_ce = e_legnamnden + (5, ) + +id_ce_authContext = id_eleg_ce + (1, ) + + +class AuthenticationContext(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('contextType', char.UTF8String()), + namedtype.OptionalNamedType('contextInfo', char.UTF8String()) + ) + +class AuthenticationContexts(univ.SequenceOf): + componentType = AuthenticationContext() + subtypeSpec=constraint.ValueSizeConstraint(1, MAX) + + +# Map of Certificate Extension OIDs to Extensions added to the +# ones that are in rfc5280.py + +_certificateExtensionsMapUpdate = { + id_ce_authContext: AuthenticationContexts(), +} + +rfc5280.certificateExtensionsMap.update(_certificateExtensionsMapUpdate) diff --git a/tests/__main__.py b/tests/__main__.py index b79752f..f828272 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -58,6 +58,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc7191.suite', 'tests.test_rfc7292.suite', 'tests.test_rfc7296.suite', + 'tests.test_rfc7773.suite', 'tests.test_rfc7894.suite', 'tests.test_rfc7906.suite', 'tests.test_rfc7914.suite', diff --git a/tests/test_rfc7773.py b/tests/test_rfc7773.py new file mode 100644 index 0000000..393187f --- /dev/null +++ b/tests/test_rfc7773.py @@ -0,0 +1,114 @@ +# +# 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 rfc7773 + +try: + import unittest2 as unittest + +except ImportError: + import unittest + + +class AuthenticationContextExtnTestCase(unittest.TestCase): + pem_text = """\ +MIIMUjCCCzqgAwIBAgIQevDaX+wRYAlpUgjTYjCCRjANBgkqhkiG9w0BAQsFADCBuDELMAkGA1UE +BhMCU0UxNTAzBgNVBAoTLERldiBURVNUIENBIG9yZyBBQiAoTk9UIEEgUkVBTCBPUkdBTklaQVRJ +T04pMSAwHgYDVQQLExdDZW50cmFsIFNpZ25pbmcgU2VydmljZTEVMBMGA1UEBRMMQTEyMzQ1Ni03 +ODkwMTkwNwYDVQQDEzBDZW50cmFsIFNpZ25pbmcgQ0EwMDEgLSBFSUQgMi4wIERldiBURVNUIFNl +cnZpY2UwHhcNMTkxMDA5MDc0ODI2WhcNMjAxMDA5MDc0ODI2WjBgMRUwEwYDVQQFEwwxODg4MDMw +OTkzNjgxCzAJBgNVBAYTAlNFMQ0wCwYDVQQqEwRBZ2RhMRcwFQYDVQQDEw5BZ2RhIEFuZGVyc3Nv +bjESMBAGA1UEBBMJQW5kZXJzc29uMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjl1H +7vveI/EUaF9z6EiL/AmTHDbpLAKoWh9JJjpRlb8lU0TseYOzZp6ySiAO8St2a/HxxhrNuAAELUwZ +3oICkmxM/NeYgI7EEaLVPUwBAWfGZrRWb/+h8C6SrivWc73M/LI1A0B9tcEpUuh0CHTSVIBZsH+L +IDyKW6n3T8YeI7+0CX391I/j3iyEBNFcfDaHaFChzkPxgPg6Xh1D1JWs+mUj1rOoTLxsyusWiIQk +IkjDgFNUCpS1+NUvkTU1uFewvluxjOzRVqzYZWesOL+V/lGnyVPw4o1INEKYpOurYii2TXElTmXO +iQdIG20S96uFH6vFFJ2cPwgYjWpory/K+QIDAQABo4IIrTCCCKkwCwYDVR0PBAQDAgZAMB0GA1Ud +DgQWBBQo71oFnxX2kapLl3ZoYOylnJo01TATBgNVHSAEDDAKMAgGBgQAizABATBLBgNVHR8ERDBC +MECgPqA8hjpodHRwczovL2VpZDJjc2lnLmtvbmtpLnNlL3B1Ymxpc2gvY3JsLzE4MTRiMGFiYzEx +NGM3YmEuY3JsMIIH6wYHKoVwgUkFAQSCB94wggfaMIIH1gwraHR0cDovL2lkLmVsZWduYW1uZGVu +LnNlL2F1dGgtY29udC8xLjAvc2FjaQyCB6U8c2FjaTpTQU1MQXV0aENvbnRleHQgeG1sbnM6c2Fj +aT0iaHR0cDovL2lkLmVsZWduYW1uZGVuLnNlL2F1dGgtY29udC8xLjAvc2FjaSI+PHNhY2k6QXV0 +aENvbnRleHRJbmZvIElkZW50aXR5UHJvdmlkZXI9Imh0dHA6Ly9kZXYudGVzdC5zd2VkZW5jb25u +ZWN0LnNlL2lkcCIgQXV0aGVudGljYXRpb25JbnN0YW50PSIyMDE5LTEwLTA5VDA3OjU4OjI2LjAw +MFoiIFNlcnZpY2VJRD0iRmVkU2lnbmluZyIgQXV0aG5Db250ZXh0Q2xhc3NSZWY9Imh0dHA6Ly9p +ZC5lbGVnbmFtbmRlbi5zZS9sb2EvMS4wL2xvYTMtc2lnbWVzc2FnZSIgQXNzZXJ0aW9uUmVmPSJf +ZGM5MjM0Y2Y3Zjc5OWQwMDlmMjUwNWVhMzVlMWU0NmUiLz48c2FjaTpJZEF0dHJpYnV0ZXM+PHNh +Y2k6QXR0cmlidXRlTWFwcGluZyBUeXBlPSJyZG4iIFJlZj0iMi41LjQuNSI+PHNhbWw6QXR0cmli +dXRlIEZyaWVuZGx5TmFtZT0iU3dlZGlzaCBQZXJzb25udW1tZXIiIE5hbWU9InVybjpvaWQ6MS4y +Ljc1Mi4yOS40LjEzIiB4bWxuczpzYW1sPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNz +ZXJ0aW9uIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIiB4bWxuczp4 +cz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3 +dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiPjE4ODgwMzA5OTM2ODwvc2FtbDpBdHRy +aWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjwvc2FjaTpBdHRyaWJ1dGVNYXBwaW5nPjxzYWNp +OkF0dHJpYnV0ZU1hcHBpbmcgVHlwZT0icmRuIiBSZWY9IjIuNS40LjQyIj48c2FtbDpBdHRyaWJ1 +dGUgRnJpZW5kbHlOYW1lPSJHaXZlbiBOYW1lIiBOYW1lPSJ1cm46b2lkOjIuNS40LjQyIiB4bWxu +czpzYW1sPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj48c2FtbDpBdHRy +aWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIiB4bWxuczp4cz0iaHR0cDovL3d3dy53My5v +cmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxT +Y2hlbWEtaW5zdGFuY2UiPkFnZGE8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0 +ZT48L3NhY2k6QXR0cmlidXRlTWFwcGluZz48c2FjaTpBdHRyaWJ1dGVNYXBwaW5nIFR5cGU9InJk +biIgUmVmPSIyLjUuNC4zIj48c2FtbDpBdHRyaWJ1dGUgRnJpZW5kbHlOYW1lPSJEaXNwbGF5IE5h +bWUiIE5hbWU9InVybjpvaWQ6Mi4xNi44NDAuMS4xMTM3MzAuMy4xLjI0MSIgeG1sbnM6c2FtbD0i +dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiI+PHNhbWw6QXR0cmlidXRlVmFs +dWUgeHNpOnR5cGU9InhzOnN0cmluZyIgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEv +WE1MU2NoZW1hIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWlu +c3RhbmNlIj5BZ2RhIEFuZGVyc3Nvbjwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmli +dXRlPjwvc2FjaTpBdHRyaWJ1dGVNYXBwaW5nPjxzYWNpOkF0dHJpYnV0ZU1hcHBpbmcgVHlwZT0i +cmRuIiBSZWY9IjIuNS40LjQiPjxzYW1sOkF0dHJpYnV0ZSBGcmllbmRseU5hbWU9IlN1cm5hbWUi +IE5hbWU9InVybjpvaWQ6Mi41LjQuNCIgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNB +TUw6Mi4wOmFzc2VydGlvbiI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmlu +ZyIgeG1sbnM6eHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczp4c2k9 +Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIj5BbmRlcnNzb248L3Nh +bWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48L3NhY2k6QXR0cmlidXRlTWFwcGlu +Zz48L3NhY2k6SWRBdHRyaWJ1dGVzPjwvc2FjaTpTQU1MQXV0aENvbnRleHQ+MAkGA1UdEwQCMAAw +HwYDVR0jBBgwFoAUqKv0QPwAYcLfcD/Vy1A2deHtiqcwDQYJKoZIhvcNAQELBQADggEBAETlZOIL +NknxlMiYHCxoYypyzYuza2l3M4+YWakT0vFPgXpCk+l0dNst7h9nWvKKHCboSj+YP5dUCSsuUXhb +7xTei/F2nj7q1oCPuVJGThZqhWgF/JkqOy34hHEM5VniJiQu2W9TjzRMSOSFzRlQsHcOuXzdTkhr +CQpD1TWxYL9sCy4YoCdE4edfgBGBMujxoijl3/xJ5uI1FjhlSPVP88p8Wsi8i7GdMYuxqjZMwrt2 +PHIPgop3BNN9/BzW0cmdyNvFgcD9qR8Rv5aFBYuQbyg6fST8JdAOrbMrCST6v2U41OOXH5MC/kL6 +tAGXsYdcuQpglUngmo/FV4Z9qjIDkYQ= +""" + + def setUp(self): + self.asn1Spec = rfc5280.Certificate() + + 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 + + for extn in asn1Object['tbsCertificate']['extensions']: + if extn['extnID'] == rfc7773.id_ce_authContext: + s = extn['extnValue'] + acs, rest = der_decode(s, + asn1Spec=rfc5280.certificateExtensionsMap[extn['extnID']]) + assert not rest + assert acs.prettyPrint() + assert s == der_encode(acs) + + assert u'id.elegnamnden.se' in acs[0]['contextType'] + assert u'AuthContextInfo IdentityProvider' in acs[0]['contextInfo'] + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + import sys + + result = unittest.TextTestRunner(verbosity=2).run(suite) + sys.exit(not result.wasSuccessful()) -- cgit v1.2.3 From 1daa0559f9da2d1d2304a6df99a4ec899d5b84d8 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Wed, 9 Oct 2019 22:23:35 +0200 Subject: Release 0.2.7 --- CHANGES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 61b240b..1edabde 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,5 @@ -Revision 0.2.7, released XX-09-2019 +Revision 0.2.7, released 09-10-2019 ----------------------------------- - Added maps for use with openType to RFC 3565 -- cgit v1.2.3 From 71123fb26b001938f339903a4fe62cbe2c8315ef Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Wed, 9 Oct 2019 22:31:04 +0200 Subject: Prepare for 0.2.8 --- CHANGES.txt | 5 +++++ pyasn1_modules/__init__.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 1edabde..ad46d7c 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,9 @@ +Revision 0.2.8, released XX-XX-2019 +----------------------------------- + +No changes yet + Revision 0.2.7, released 09-10-2019 ----------------------------------- diff --git a/pyasn1_modules/__init__.py b/pyasn1_modules/__init__.py index 023d7f4..917ac12 100644 --- a/pyasn1_modules/__init__.py +++ b/pyasn1_modules/__init__.py @@ -1,2 +1,2 @@ # http://www.python.org/dev/peps/pep-0396/ -__version__ = '0.2.7' +__version__ = '0.2.8' -- cgit v1.2.3 From a9ba285df7e5973a28b23d48bbb597980ca9ad94 Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Sat, 12 Oct 2019 02:04:45 -0400 Subject: Add support for RFC 7633 (#79) --- CHANGES.txt | 2 +- pyasn1_modules/rfc7633.py | 38 ++++++++++++++++++++++ tests/__main__.py | 1 + tests/test_rfc7633.py | 82 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 pyasn1_modules/rfc7633.py create mode 100644 tests/test_rfc7633.py diff --git a/CHANGES.txt b/CHANGES.txt index ad46d7c..1b4c178 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -2,7 +2,7 @@ Revision 0.2.8, released XX-XX-2019 ----------------------------------- -No changes yet +- Added RFC7633 providing TLS Features Certificate Extension Revision 0.2.7, released 09-10-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc7633.py b/pyasn1_modules/rfc7633.py new file mode 100644 index 0000000..f518440 --- /dev/null +++ b/pyasn1_modules/rfc7633.py @@ -0,0 +1,38 @@ +# +# This file is part of pyasn1-modules software. +# +# Created by Russ Housley with some assistance from asn1ate v.0.6.0. +# +# Copyright (c) 2019, Vigil Security, LLC +# License: http://snmplabs.com/pyasn1/license.html +# +# Transport Layer Security (TLS) Feature Certificate Extension +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc7633.txt +# + +from pyasn1.type import univ + +from pyasn1_modules import rfc5280 + + +# TLS Features Extension + +id_pe = univ.ObjectIdentifier('1.3.6.1.5.5.7.1') + +id_pe_tlsfeature = id_pe + (24, ) + + +class Features(univ.SequenceOf): + componentType = univ.Integer() + + +# Map of Certificate Extension OIDs to Extensions added to the +# ones that are in rfc5280.py + +_certificateExtensionsMapUpdate = { + id_pe_tlsfeature: Features(), +} + +rfc5280.certificateExtensionsMap.update(_certificateExtensionsMapUpdate) diff --git a/tests/__main__.py b/tests/__main__.py index f828272..4e9bee7 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -58,6 +58,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc7191.suite', 'tests.test_rfc7292.suite', 'tests.test_rfc7296.suite', + 'tests.test_rfc7633.suite', 'tests.test_rfc7773.suite', 'tests.test_rfc7894.suite', 'tests.test_rfc7906.suite', diff --git a/tests/test_rfc7633.py b/tests/test_rfc7633.py new file mode 100644 index 0000000..aca90c2 --- /dev/null +++ b/tests/test_rfc7633.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 rfc7633 + +try: + import unittest2 as unittest + +except ImportError: + import unittest + + +class TLSFeaturesExtnTestCase(unittest.TestCase): + pem_text = """\ +MIIEbTCCBBOgAwIBAgIRAO5f2N8q74GBATjTMXQCjlgwCgYIKoZIzj0EAwIwgZYx +CzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNV +BAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMTwwOgYDVQQD +EzNDT01PRE8gRUNDIE9yZ2FuaXphdGlvbiBWYWxpZGF0aW9uIFNlY3VyZSBTZXJ2 +ZXIgQ0EwHhcNMTYwMTE1MDAwMDAwWhcNMTgwMTE0MjM1OTU5WjCBwjELMAkGA1UE +BhMCUlUxDzANBgNVBBETBjExNzY0NzEUMBIGA1UECBMLTW9zY293IENpdHkxDzAN +BgNVBAcTBk1vc2NvdzE4MDYGA1UECRMvQWthZGVtaWthIEthcGljeSBzdHJlZXQs +IGhvdXNlIDQsIGFwYXJ0bWVudCAxNjYxGDAWBgNVBAoTD0FuZHJleSBDaHVyYW5v +djETMBEGA1UECxMKSW5zdGFudFNTTDESMBAGA1UEAxMJYWRtc2VsLmVjMHYwEAYH +KoZIzj0CAQYFK4EEACIDYgAEwrPPzgBO1vDNmV0UVvYSBnys9B7LVkGLiIBbKYf2 +nNFRuJKo1gzNurI8pv4CbvqjkCX4Je/aSeYFHSCR9y82+zTwYQuJFt5LIL5f+Syp +xZ7aLH56bOiQ+QhCtIvWP4YWo4IB9TCCAfEwHwYDVR0jBBgwFoAUdr4iSO4/PvZG +A9mHGNBlfiKcC+EwHQYDVR0OBBYEFHTFQqV+H5a7+RVL+70Z6zqCbqq9MA4GA1Ud +DwEB/wQEAwIFgDAMBgNVHRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr +BgEFBQcDAjBQBgNVHSAESTBHMDsGDCsGAQQBsjEBAgEDBDArMCkGCCsGAQUFBwIB +Fh1odHRwczovL3NlY3VyZS5jb21vZG8uY29tL0NQUzAIBgZngQwBAgIwWgYDVR0f +BFMwUTBPoE2gS4ZJaHR0cDovL2NybC5jb21vZG9jYS5jb20vQ09NT0RPRUNDT3Jn +YW5pemF0aW9uVmFsaWRhdGlvblNlY3VyZVNlcnZlckNBLmNybDCBiwYIKwYBBQUH +AQEEfzB9MFUGCCsGAQUFBzAChklodHRwOi8vY3J0LmNvbW9kb2NhLmNvbS9DT01P +RE9FQ0NPcmdhbml6YXRpb25WYWxpZGF0aW9uU2VjdXJlU2VydmVyQ0EuY3J0MCQG +CCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wEQYIKwYBBQUHARgE +BTADAgEFMCMGA1UdEQQcMBqCCWFkbXNlbC5lY4INd3d3LmFkbXNlbC5lYzAKBggq +hkjOPQQDAgNIADBFAiAi6TXl76FTKPP1AhqtEjU5BjAj9Ju7CSKChHZSmzxeXQIh +AOQSxhs011emVxyBIXT0ZGbmBY8LFRh6eGIOCAJbkM5T +""" + + def setUp(self): + self.asn1Spec = rfc5280.Certificate() + + 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 + + for extn in asn1Object['tbsCertificate']['extensions']: + if extn['extnID'] == rfc7633.id_pe_tlsfeature: + s = extn['extnValue'] + features, rest = der_decode(s, + rfc5280.certificateExtensionsMap[extn['extnID']]) + assert not rest + assert features.prettyPrint() + assert s == der_encode(features) + + assert len(features) == 1 + assert features[0] == 5 + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + import sys + + result = unittest.TextTestRunner(verbosity=2).run(suite) + sys.exit(not result.wasSuccessful()) -- cgit v1.2.3 From 86fafcc8923b1094197f20ccf794a9c66731fa34 Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Sat, 12 Oct 2019 11:48:35 -0400 Subject: Improve test routines for modules that use certificate extensions (#80) Improve test routines for modules that use certificate extensions --- CHANGES.txt | 1 + tests/test_rfc3709.py | 4 ++++ tests/test_rfc3770.py | 5 +++++ tests/test_rfc3779.py | 6 ++++++ tests/test_rfc5280.py | 7 ++++++- tests/test_rfc7633.py | 4 ++++ tests/test_rfc7773.py | 5 +++++ tests/test_rfc8209.py | 4 ++++ tests/test_rfc8226.py | 3 +++ tests/test_rfc8520.py | 6 ++++++ 10 files changed, 44 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 1b4c178..d542559 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -2,6 +2,7 @@ Revision 0.2.8, released XX-XX-2019 ----------------------------------- +- Improve test routines for modules that use certificate extensions - Added RFC7633 providing TLS Features Certificate Extension Revision 0.2.7, released 09-10-2019 diff --git a/tests/test_rfc3709.py b/tests/test_rfc3709.py index 56753cc..e115391 100644 --- a/tests/test_rfc3709.py +++ b/tests/test_rfc3709.py @@ -52,7 +52,9 @@ Pj22pmfmQi5w21UljqoTj/+lQLkU3wfy5BdVKBwI0GfEA+YL3ctSzPNqAA== assert asn1Object.prettyPrint() assert der_encoder.encode(asn1Object) == substrate + extn_list = [ ] for extn in asn1Object['tbsCertificate']['extensions']: + extn_list.append(extn['extnID']) if extn['extnID'] == rfc3709.id_pe_logotype: s = extn['extnValue'] @@ -64,6 +66,8 @@ Pj22pmfmQi5w21UljqoTj/+lQLkU3wfy5BdVKBwI0GfEA+YL3ctSzPNqAA== assert ids['mediaType'] == "image/png" assert ids['logotypeURI'][0] == "http://www.vigilsec.com/vigilsec_logo.png" + assert rfc3709.id_pe_logotype in extn_list + def testExtensionsMap(self): substrate = pem.readBase64fromText(self.pem_text) asn1Object, rest = der_decoder.decode(substrate, asn1Spec=self.asn1Spec) diff --git a/tests/test_rfc3770.py b/tests/test_rfc3770.py index 775dc75..df6e0dc 100644 --- a/tests/test_rfc3770.py +++ b/tests/test_rfc3770.py @@ -69,7 +69,9 @@ DAlVlhox680Jxy5J8Pkx assert spki_alg['algorithm'] == rfc5480.id_ecPublicKey assert spki_alg['parameters']['namedCurve'] == rfc5480.secp384r1 + extn_list = [ ] for extn in asn1Object['tbsCertificate']['extensions']: + extn_list.append(extn['extnID']) if extn['extnID'] in rfc5280.certificateExtensionsMap.keys(): extnValue, rest = der_decode(extn['extnValue'], asn1Spec=rfc5280.certificateExtensionsMap[extn['extnID']]) @@ -82,6 +84,9 @@ DAlVlhox680Jxy5J8Pkx assert rfc3770.id_kp_eapOverLAN in extnValue assert rfc3770.id_kp_eapOverPPP in extnValue + assert rfc3770.id_pe_wlanSSID in extn_list + assert rfc5280.id_ce_extKeyUsage in extn_list + suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) diff --git a/tests/test_rfc3779.py b/tests/test_rfc3779.py index 32f0ca3..af90376 100644 --- a/tests/test_rfc3779.py +++ b/tests/test_rfc3779.py @@ -58,7 +58,9 @@ V+vo2L72yerdbsP9xjqvhZrLKfsLZjYK4SdYYthi assert asn1Object.prettyPrint() assert der_encoder.encode(asn1Object) == substrate + extn_list = [ ] for extn in asn1Object['tbsCertificate']['extensions']: + extn_list.append(extn['extnID']) if extn['extnID'] == rfc3779.id_pe_ipAddrBlocks: s = extn['extnValue'] @@ -74,6 +76,10 @@ V+vo2L72yerdbsP9xjqvhZrLKfsLZjYK4SdYYthi assert as_ids.prettyPrint() assert der_encoder.encode(as_ids) == s + assert rfc3779.id_pe_ipAddrBlocks in extn_list + assert rfc3779.id_pe_autonomousSysIds in extn_list + + def testExtensionsMap(self): substrate = pem.readBase64fromText(self.pem_text) asn1Object, rest = der_decoder.decode(substrate, asn1Spec=self.asn1Spec) diff --git a/tests/test_rfc5280.py b/tests/test_rfc5280.py index ce9ec8d..9802ef9 100644 --- a/tests/test_rfc5280.py +++ b/tests/test_rfc5280.py @@ -182,6 +182,7 @@ vjnIhxTFoCb5vA== else: assert len(atv['value']['printableString']) > 9 + crl_extn_count = 0 for extn in asn1Object['tbsCertList']['crlExtensions']: if extn['extnID'] in rfc5280.certificateExtensionsMap.keys(): ev, rest = der_decode(extn['extnValue'], @@ -189,6 +190,8 @@ vjnIhxTFoCb5vA== assert not rest assert ev.prettyPrint() assert der_encode(ev) == extn['extnValue'] + crl_extn_count += 1 + assert crl_extn_count == 1 def testExtensionsMap(self): substrate = pem.readBase64fromText(self.pem_text) @@ -197,12 +200,14 @@ vjnIhxTFoCb5vA== assert asn1Object.prettyPrint() assert der_encode(asn1Object) == substrate + cert_extn_count = 0 for extn in asn1Object['tbsCertList']['crlExtensions']: if extn['extnID'] in rfc5280.certificateExtensionsMap.keys(): extnValue, rest = der_decode(extn['extnValue'], asn1Spec=rfc5280.certificateExtensionsMap[extn['extnID']]) assert der_encode(extnValue) == extn['extnValue'] - + cert_extn_count += 1 + assert cert_extn_count == 1 class ORAddressOpenTypeTestCase(unittest.TestCase): oraddress_pem_text = """\ diff --git a/tests/test_rfc7633.py b/tests/test_rfc7633.py index aca90c2..de17cda 100644 --- a/tests/test_rfc7633.py +++ b/tests/test_rfc7633.py @@ -60,7 +60,9 @@ AOQSxhs011emVxyBIXT0ZGbmBY8LFRh6eGIOCAJbkM5T assert asn1Object.prettyPrint() assert der_encode(asn1Object) == substrate + extn_list = [ ] for extn in asn1Object['tbsCertificate']['extensions']: + extn_list.append(extn['extnID']) if extn['extnID'] == rfc7633.id_pe_tlsfeature: s = extn['extnValue'] features, rest = der_decode(s, @@ -72,6 +74,8 @@ AOQSxhs011emVxyBIXT0ZGbmBY8LFRh6eGIOCAJbkM5T assert len(features) == 1 assert features[0] == 5 + assert rfc7633.id_pe_tlsfeature in extn_list + suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) diff --git a/tests/test_rfc7773.py b/tests/test_rfc7773.py index 393187f..37765f0 100644 --- a/tests/test_rfc7773.py +++ b/tests/test_rfc7773.py @@ -92,7 +92,10 @@ tAGXsYdcuQpglUngmo/FV4Z9qjIDkYQ= assert asn1Object.prettyPrint() assert der_encode(asn1Object) == substrate + extn_list = [ ] for extn in asn1Object['tbsCertificate']['extensions']: + extn_list.append(extn['extnID']) + if extn['extnID'] == rfc7773.id_ce_authContext: s = extn['extnValue'] acs, rest = der_decode(s, @@ -104,6 +107,8 @@ tAGXsYdcuQpglUngmo/FV4Z9qjIDkYQ= assert u'id.elegnamnden.se' in acs[0]['contextType'] assert u'AuthContextInfo IdentityProvider' in acs[0]['contextInfo'] + assert rfc7773.id_ce_authContext in extn_list + suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) diff --git a/tests/test_rfc8209.py b/tests/test_rfc8209.py index 1eb2efd..4c72f76 100644 --- a/tests/test_rfc8209.py +++ b/tests/test_rfc8209.py @@ -43,7 +43,9 @@ OCRdZCk1KI3uDDgp assert asn1Object.prettyPrint() assert der_encode(asn1Object) == substrate + extn_list = [ ] for extn in asn1Object['tbsCertificate']['extensions']: + extn_list.append(extn['extnID']) if extn['extnID'] in rfc5280.certificateExtensionsMap.keys(): extnValue, rest = der_decode(extn['extnValue'], asn1Spec=rfc5280.certificateExtensionsMap[extn['extnID']]) @@ -52,6 +54,8 @@ OCRdZCk1KI3uDDgp if extn['extnID'] == rfc5280.id_ce_extKeyUsage: assert rfc8209.id_kp_bgpsec_router in extnValue + assert rfc5280.id_ce_extKeyUsage in extn_list + suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) diff --git a/tests/test_rfc8226.py b/tests/test_rfc8226.py index 69fd438..8993ae1 100644 --- a/tests/test_rfc8226.py +++ b/tests/test_rfc8226.py @@ -77,7 +77,9 @@ yEFWA6G95b/HbtPMTjLpPKtrOjhofc4LyVCDYhFhKzpvHh1qeA== assert asn1Object.prettyPrint() assert der_encoder.encode(asn1Object) == substrate + extn_list = [ ] for extn in asn1Object['tbsCertificate']['extensions']: + extn_list.append(extn['extnID']) if extn['extnID'] in rfc5280.certificateExtensionsMap.keys(): extnValue, rest = der_decoder.decode(extn['extnValue'], asn1Spec=rfc5280.certificateExtensionsMap[extn['extnID']]) @@ -86,6 +88,7 @@ yEFWA6G95b/HbtPMTjLpPKtrOjhofc4LyVCDYhFhKzpvHh1qeA== if extn['extnID'] == rfc8226.id_pe_TNAuthList: assert extnValue[0]['spc'] == 'fake' + assert rfc8226.id_pe_TNAuthList in extn_list suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) diff --git a/tests/test_rfc8520.py b/tests/test_rfc8520.py index 4612f3a..884ef09 100644 --- a/tests/test_rfc8520.py +++ b/tests/test_rfc8520.py @@ -63,7 +63,10 @@ izaUuU1EEwgOMELjeFL62Ssvq8X+x6hZFCLygI7GNeitlblNhCXhFFurqMs= assert asn1Object.prettyPrint() assert der_encode(asn1Object) == substrate + extn_list = [ ] for extn in asn1Object['tbsCertificate']['extensions']: + extn_list.append(extn['extnID']) + if extn['extnID'] == rfc8520.id_pe_mudsigner: mudsigner, rest = der_decode(extn['extnValue'], rfc8520.MUDsignerSyntax()) assert der_encode(mudsigner) == extn['extnValue'] @@ -82,6 +85,9 @@ izaUuU1EEwgOMELjeFL62Ssvq8X+x6hZFCLygI7GNeitlblNhCXhFFurqMs= assert mudurl[-5:] == ".json" + assert rfc8520.id_pe_mudsigner in extn_list + assert rfc8520.id_pe_mud_url in extn_list + def testExtensionsMap(self): substrate = pem.readBase64fromText(self.mud_cert_pem_text) asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) -- cgit v1.2.3 From 8f74c07702b0941d70335c517aa2ac88c0b9c95e Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Sun, 13 Oct 2019 15:54:21 -0400 Subject: Add support for RFC 7229 (#81) --- CHANGES.txt | 1 + pyasn1_modules/rfc7229.py | 29 ++++++++++++++ tests/__main__.py | 1 + tests/test_rfc7229.py | 97 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 128 insertions(+) create mode 100644 pyasn1_modules/rfc7229.py create mode 100644 tests/test_rfc7229.py diff --git a/CHANGES.txt b/CHANGES.txt index d542559..311d43c 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -4,6 +4,7 @@ Revision 0.2.8, released XX-XX-2019 - Improve test routines for modules that use certificate extensions - Added RFC7633 providing TLS Features Certificate Extension +- Added RFC7229 providing OIDs for Test Certificate Policies Revision 0.2.7, released 09-10-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc7229.py b/pyasn1_modules/rfc7229.py new file mode 100644 index 0000000..e9bce2d --- /dev/null +++ b/pyasn1_modules/rfc7229.py @@ -0,0 +1,29 @@ +# +# 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 +# +# Object Identifiers for Test Certificate Policies +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc7229.txt +# + +from pyasn1.type import univ + + +id_pkix = univ.ObjectIdentifier('1.3.6.1.5.5.7') + +id_TEST = id_pkix + (13, ) + +id_TEST_certPolicyOne = id_TEST + (1, ) +id_TEST_certPolicyTwo = id_TEST + (2, ) +id_TEST_certPolicyThree = id_TEST + (3, ) +id_TEST_certPolicyFour = id_TEST + (4, ) +id_TEST_certPolicyFive = id_TEST + (5, ) +id_TEST_certPolicySix = id_TEST + (6, ) +id_TEST_certPolicySeven = id_TEST + (7, ) +id_TEST_certPolicyEight = id_TEST + (8, ) diff --git a/tests/__main__.py b/tests/__main__.py index 4e9bee7..08c2635 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -56,6 +56,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc6211.suite', 'tests.test_rfc7030.suite', 'tests.test_rfc7191.suite', + 'tests.test_rfc7229.suite', 'tests.test_rfc7292.suite', 'tests.test_rfc7296.suite', 'tests.test_rfc7633.suite', diff --git a/tests/test_rfc7229.py b/tests/test_rfc7229.py new file mode 100644 index 0000000..6d1c578 --- /dev/null +++ b/tests/test_rfc7229.py @@ -0,0 +1,97 @@ +# +# 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 rfc7229 + +try: + import unittest2 as unittest + +except ImportError: + import unittest + + +class CertificatePolicyTestCase(unittest.TestCase): + pem_text = """\ +MIIDJDCCAqqgAwIBAgIJAKWzVCgbsG5AMAoGCCqGSM49BAMDMD8xCzAJBgNVBAYT +AlVTMQswCQYDVQQIDAJWQTEQMA4GA1UEBwwHSGVybmRvbjERMA8GA1UECgwIQm9n +dXMgQ0EwHhcNMTkxMDEzMTkwNTUzWhcNMjAxMDEyMTkwNTUzWjBTMQswCQYDVQQG +EwJVUzELMAkGA1UECBMCVkExEDAOBgNVBAcTB0hlcm5kb24xJTAjBgNVBAoTHFRF +U1QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AATwUXZUseiOaqWdrClDCMbp9YFAM87LTmFirygpzKDU9cfqSCg7zBDIphXCwMcS +9zVWDoStCbcvN0jw5CljHcffzpHYX91P88SZRJ1w4hawHjOsWxvM3AkYgZ5nfdlL +7EajggFcMIIBWDAdBgNVHQ4EFgQU8jXbNATapVXyvWkDmbBi7OIVCMEwbwYDVR0j +BGgwZoAU8jXbNATapVXyvWkDmbBi7OIVCMGhQ6RBMD8xCzAJBgNVBAYTAlVTMQsw +CQYDVQQIDAJWQTEQMA4GA1UEBwwHSGVybmRvbjERMA8GA1UECgwIQm9ndXMgQ0GC +CQDokdYGkU/O8jAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBhjBCBglghkgB +hvhCAQ0ENRYzVGhpcyBjZXJ0aWZpY2F0ZSBjYW5ub3QgYmUgdHJ1c3RlZCBmb3Ig +YW55IHB1cnBvc2UuMCEGA1UdIAQaMBgwCgYIKwYBBQUHDQEwCgYIKwYBBQUHDQIw +CgYDVR02BAMCAQIwNQYDVR0hBC4wLDAUBggrBgEFBQcNAQYIKwYBBQUHDQcwFAYI +KwYBBQUHDQIGCCsGAQUFBw0IMAoGCCqGSM49BAMDA2gAMGUCMHaWskjS7MKQCMcn +zEKFOV3LWK8pL57vrECJd8ywKdwBJUNw9HhvSKkfUwL6rjlLpQIxAL2QO3CNoZRP +PZs8K3IjUA5+U73pA8lpaTOPscLY22WL9pAGmyVUyEJ8lM7E+r4iDg== +""" + + def setUp(self): + self.asn1Spec = rfc5280.Certificate() + + def testDerCodec(self): + test_oids = [ + rfc7229.id_TEST_certPolicyOne, + rfc7229.id_TEST_certPolicyTwo, + rfc7229.id_TEST_certPolicyThree, + rfc7229.id_TEST_certPolicyFour, + rfc7229.id_TEST_certPolicyFive, + rfc7229.id_TEST_certPolicySix, + rfc7229.id_TEST_certPolicySeven, + rfc7229.id_TEST_certPolicyEight, + ] + + 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 + + count = 0 + for extn in asn1Object['tbsCertificate']['extensions']: + if extn['extnID'] in rfc5280.certificateExtensionsMap.keys(): + s = extn['extnValue'] + ev, rest = der_decode(s, rfc5280.certificateExtensionsMap[extn['extnID']]) + assert not rest + assert ev.prettyPrint() + assert s == der_encode(ev) + + if extn['extnID'] == rfc5280.id_ce_certificatePolicies: + for pol in ev: + if pol['policyIdentifier'] in test_oids: + count += 1 + + if extn['extnID'] == rfc5280.id_ce_policyMappings: + for pmap in ev: + if pmap['issuerDomainPolicy'] in test_oids: + count += 1 + if pmap['subjectDomainPolicy'] in test_oids: + count += 1 + + assert count == 6 + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + import sys + + result = unittest.TextTestRunner(verbosity=2).run(suite) + sys.exit(not result.wasSuccessful()) -- cgit v1.2.3 From 5fde2a4f538ea8dafd83852be175ea8c9fcf1c15 Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Mon, 14 Oct 2019 12:22:53 -0400 Subject: Add tests for RFC 3280 and RFC 3281 (#82) --- CHANGES.txt | 1 + tests/__main__.py | 2 ++ tests/test_rfc3280.py | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++ tests/test_rfc3281.py | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 171 insertions(+) create mode 100644 tests/test_rfc3280.py create mode 100644 tests/test_rfc3281.py diff --git a/CHANGES.txt b/CHANGES.txt index 311d43c..60267b8 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -5,6 +5,7 @@ Revision 0.2.8, released XX-XX-2019 - Improve test routines for modules that use certificate extensions - Added RFC7633 providing TLS Features Certificate Extension - Added RFC7229 providing OIDs for Test Certificate Policies +- Added test for RFC3280 and RFC3281 Revision 0.2.7, released 09-10-2019 ----------------------------------- diff --git a/tests/__main__.py b/tests/__main__.py index 08c2635..f892330 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -23,6 +23,8 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc2986.suite', 'tests.test_rfc3161.suite', 'tests.test_rfc3274.suite', + 'tests.test_rfc3280.suite', + 'tests.test_rfc3281.suite', 'tests.test_rfc3447.suite', 'tests.test_rfc3560.suite', 'tests.test_rfc3565.suite', diff --git a/tests/test_rfc3280.py b/tests/test_rfc3280.py new file mode 100644 index 0000000..0a4dd02 --- /dev/null +++ b/tests/test_rfc3280.py @@ -0,0 +1,84 @@ +# +# 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 rfc3280 + +try: + import unittest2 as unittest + +except ImportError: + import unittest + + +class CertificateTestCase(unittest.TestCase): + pem_text = """\ +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0 +IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz +BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y +aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG +9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMjIzM1oXDTE5MDYy +NjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y +azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw +Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl +cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjmFGWHOjVsQaBalfD +cnWTq8+epvzzFlLWLU2fNUSoLgRNB0mKOCn1dzfnt6td3zZxFJmP3MKS8edgkpfs +2Ejcv8ECIMYkpChMMFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89HBFx1cQqY +JJgpp0lZpd34t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliE +Zwgs3x/be0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJ +n0WuPIqpsHEzXcjFV9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/A +PhmcGcwTTYJBtYze4D1gCCAPRX5ron+jjBXu +""" + + def setUp(self): + self.asn1Spec = rfc3280.Certificate() + + 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 + + +class CertificateListTestCase(unittest.TestCase): + pem_text = """\ +MIIBVjCBwAIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJBVTETMBEGA1UE +CBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRk +MRUwEwYDVQQDEwxzbm1wbGFicy5jb20xIDAeBgkqhkiG9w0BCQEWEWluZm9Ac25t +cGxhYnMuY29tFw0xMjA0MTExMzQwNTlaFw0xMjA1MTExMzQwNTlaoA4wDDAKBgNV +HRQEAwIBATANBgkqhkiG9w0BAQUFAAOBgQC1D/wwnrcY/uFBHGc6SyoYss2kn+nY +RTwzXmmldbNTCQ03x5vkWGGIaRJdN8QeCzbEi7gpgxgpxAx6Y5WkxkMQ1UPjNM5n +DGVDOtR0dskFrrbHuNpWqWrDaBN0/ryZiWKjr9JRbrpkHgVY29I1gLooQ6IHuKHY +vjnIhxTFoCb5vA== +""" + + def setUp(self): + self.asn1Spec = rfc3280.CertificateList() + + 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()) diff --git a/tests/test_rfc3281.py b/tests/test_rfc3281.py new file mode 100644 index 0000000..915f791 --- /dev/null +++ b/tests/test_rfc3281.py @@ -0,0 +1,84 @@ +# +# 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 rfc3281 + +try: + import unittest2 as unittest + +except ImportError: + import unittest + + +class AttributeCertificateTestCase(unittest.TestCase): + pem_text = """\ +MIIDBTCCAm4CAQEwgY+gUTBKpEgwRjEjMCEGA1UEAwwaQUNNRSBJbnRlcm1lZGlh +dGUgRUNEU0EgQ0ExCzAJBgNVBAYTAkZJMRIwEAYDVQQKDAlBQ01FIEx0ZC4CAx7N +WqE6pDgwNjETMBEGA1UEAwwKQUNNRSBFQ0RTQTELMAkGA1UEBhMCRkkxEjAQBgNV +BAoMCUFDTUUgTHRkLqA9MDukOTA3MRQwEgYDVQQDDAtleGFtcGxlLmNvbTELMAkG +A1UEBhMCRkkxEjAQBgNVBAoMCUFDTUUgTHRkLjANBgkqhkiG9w0BAQsFAAIEC63K +/jAiGA8yMDE2MDEwMTEyMDAwMFoYDzIwMTYwMzAxMTIwMDAwWjCB8jA8BggrBgEF +BQcKATEwMC6GC3VybjpzZXJ2aWNlpBUwEzERMA8GA1UEAwwIdXNlcm5hbWUECHBh +c3N3b3JkMDIGCCsGAQUFBwoCMSYwJIYLdXJuOnNlcnZpY2WkFTATMREwDwYDVQQD +DAh1c2VybmFtZTA1BggrBgEFBQcKAzEpMCegGKQWMBQxEjAQBgNVBAMMCUFDTUUg +THRkLjALDAlBQ01FIEx0ZC4wIAYIKwYBBQUHCgQxFDASMBAMBmdyb3VwMQwGZ3Jv +dXAyMCUGA1UESDEeMA2hC4YJdXJuOnJvbGUxMA2hC4YJdXJuOnJvbGUyMGowHwYD +VR0jBBgwFoAUgJCMhskAsEBzvklAX8yJBOXO500wCQYDVR04BAIFADA8BgNVHTcB +Af8EMjAwMB2gCoYIdXJuOnRlc3SgD4INKi5leGFtcGxlLmNvbTAPoA2GC3Vybjph +bm90aGVyMA0GCSqGSIb3DQEBCwUAA4GBACygfTs6TkPurZQTLufcE3B1H2707OXK +sJlwRpuodR2oJbunSHZ94jcJHs5dfbzFs6vNfVLlBiDBRieX4p+4JcQ2P44bkgyi +UTJu7g1b6C1liB3vO6yH5hOZicOAaKd+c/myuGb9uJ4n6y2oLNxnk/fDzpuZUe2h +Q4eikPk4LQey +""" + + def setUp(self): + self.asn1Spec = rfc3281.AttributeCertificate() + + 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['acinfo']['version'] == 1 + + attributeMap = { + rfc3281.id_at_role: rfc3281.RoleSyntax(), + rfc3281.id_aca_authenticationInfo: rfc3281.SvceAuthInfo(), + rfc3281.id_aca_accessIdentity: rfc3281.SvceAuthInfo(), + rfc3281.id_aca_chargingIdentity: rfc3281.IetfAttrSyntax(), + rfc3281.id_aca_group: rfc3281.IetfAttrSyntax(), + } + + count = 0 + for attr in asn1Object['acinfo']['attributes']: + assert attr['type'] in attributeMap + av, rest = der_decode(attr['values'][0], + asn1Spec=attributeMap[attr['type']]) + assert not rest + assert av.prettyPrint() + assert der_encode(av) == attr['values'][0] + count += 1 + + assert count == 5 + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + import sys + + result = unittest.TextTestRunner(verbosity=2).run(suite) + sys.exit(not result.wasSuccessful()) -- cgit v1.2.3 From 7fafbf8550ab9e8727274903ed96ae849cd24594 Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Tue, 15 Oct 2019 10:54:03 -0400 Subject: Add tests for RFC 3852 and RFC 4211 (#83) --- CHANGES.txt | 2 +- tests/__main__.py | 2 + tests/test_rfc3852.py | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++ tests/test_rfc4211.py | 61 +++++++++++++++++++++++ 4 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 tests/test_rfc3852.py create mode 100644 tests/test_rfc4211.py diff --git a/CHANGES.txt b/CHANGES.txt index 60267b8..346e2c8 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -5,7 +5,7 @@ Revision 0.2.8, released XX-XX-2019 - Improve test routines for modules that use certificate extensions - Added RFC7633 providing TLS Features Certificate Extension - Added RFC7229 providing OIDs for Test Certificate Policies -- Added test for RFC3280 and RFC3281 +- Added tests for RFC3280, RFC3281, RFC3852, and RFC4211 Revision 0.2.7, released 09-10-2019 ----------------------------------- diff --git a/tests/__main__.py b/tests/__main__.py index f892330..6c004b5 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -31,10 +31,12 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc3709.suite', 'tests.test_rfc3770.suite', 'tests.test_rfc3779.suite', + 'tests.test_rfc3852.suite', 'tests.test_rfc4055.suite', 'tests.test_rfc4073.suite', 'tests.test_rfc4108.suite', 'tests.test_rfc4210.suite', + 'tests.test_rfc4211.suite', 'tests.test_rfc5035.suite', 'tests.test_rfc5083.suite', 'tests.test_rfc5084.suite', diff --git a/tests/test_rfc3852.py b/tests/test_rfc3852.py new file mode 100644 index 0000000..94a618c --- /dev/null +++ b/tests/test_rfc3852.py @@ -0,0 +1,131 @@ +# +# 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 rfc3852 +from pyasn1_modules import rfc6402 + +try: + import unittest2 as unittest + +except ImportError: + import unittest + + +class ContentInfoTestCase(unittest.TestCase): + pem_text = """\ +MIIEJQYJKoZIhvcNAQcCoIIEFjCCBBICAQMxCzAJBgUrDgMCGgUAMIIDAgYIKwYBBQUHDAKgggL0 +BIIC8DCCAuwweDB2AgECBgorBgEEAYI3CgoBMWUwYwIBADADAgEBMVkwVwYJKwYBBAGCNxUUMUow +SAIBBQwZcGl0dWNoYTEuZW1lYS5ocHFjb3JwLm5ldAwMRU1FQVxwaXR1Y2hhDBpDTUNSZXFHZW5l +cmF0b3IudnNob3N0LmV4ZTCCAmqgggJmAgEBMIICXzCCAcgCAQAwADCBnzANBgkqhkiG9w0BAQEF +AAOBjQAwgYkCgYEA0jm7SSSm2wyEAzuNKtFZFJKo91SrJq9wQwEhEKHDavZwMQOm1rZ2PF8NWCEb +PqrhToQ7rtiGLSZa4dF4bzgmBqQ9aoSfEX4jISt31Vy+skHidXjHHpbsjT24NPhrZgANivL7CxD6 +Ft+s7qS1gL4HRm2twQkqSwOLrE/q2QeXl2UCAwEAAaCCAR0wGgYKKwYBBAGCNw0CAzEMFgo2LjIu +OTIwMC4yMD4GCSqGSIb3DQEJDjExMC8wHQYDVR0OBBYEFMW2skn88gxhONWZQA4sWGBDb68yMA4G +A1UdDwEB/wQEAwIHgDBXBgkrBgEEAYI3FRQxSjBIAgEFDBlwaXR1Y2hhMS5lbWVhLmhwcWNvcnAu +bmV0DAxFTUVBXHBpdHVjaGEMGkNNQ1JlcUdlbmVyYXRvci52c2hvc3QuZXhlMGYGCisGAQQBgjcN +AgIxWDBWAgECHk4ATQBpAGMAcgBvAHMAbwBmAHQAIABTAHQAcgBvAG4AZwAgAEMAcgB5AHAAdABv +AGcAcgBhAHAAaABpAGMAIABQAHIAbwB2AGkAZABlAHIDAQAwDQYJKoZIhvcNAQEFBQADgYEAJZlu +mxjtCxSOQi27jsVdd3y8NSIlzNv0b3LqmzvAly6L+CstXcnuG2MPQqPH9R7tbJonGUniBQO9sQ7C +KhYWj2gfhiEkSID82lV5chINVUFKoUlSiEhWr0tPGgvOaqdsKQcrHfzrsBbFkhDqrFSVy7Yivbnh +qYszKrOjJKiiCPMwADAAMYH5MIH2AgEDgBTFtrJJ/PIMYTjVmUAOLFhgQ2+vMjAJBgUrDgMCGgUA +oD4wFwYJKoZIhvcNAQkDMQoGCCsGAQUFBwwCMCMGCSqGSIb3DQEJBDEWBBTFTkK/OifaFjwqHiJu +xM7qXcg/VzANBgkqhkiG9w0BAQEFAASBgKfC6jOi1Wgy4xxDCQVK9+e5tktL8wE/j2cb9JSqq+aU +5UxEgXEw7q7BoYZCAzcxMRriGzakXr8aXHcgkRJ7XcFvLPUjpmGg9SOZ2sGW4zQdWAwImN/i8loc +xicQmJP+VoMHo/ZpjFY9fYCjNZUArgKsEwK/s+p9yrVVeB1Nf8Mn +""" + + def setUp(self): + self.asn1Spec = rfc3852.ContentInfo() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.pem_text) + + layers = { + rfc3852.id_ct_contentInfo: rfc3852.ContentInfo(), + rfc3852.id_signedData: rfc3852.SignedData(), + rfc6402.id_cct_PKIData: rfc6402.PKIData() + } + + getNextLayer = { + rfc3852.id_ct_contentInfo: lambda x: x['contentType'], + rfc3852.id_signedData: lambda x: x['encapContentInfo']['eContentType'], + rfc6402.id_cct_PKIData: lambda x: None + } + + getNextSubstrate = { + rfc3852.id_ct_contentInfo: lambda x: x['content'], + rfc3852.id_signedData: lambda x: x['encapContentInfo']['eContent'], + rfc6402.id_cct_PKIData: lambda x: None + } + + alg_oids = ( + univ.ObjectIdentifier('1.3.14.3.2.26'), + univ.ObjectIdentifier('1.2.840.113549.1.1.1'), + univ.ObjectIdentifier('1.2.840.113549.1.1.5'), + univ.ObjectIdentifier('1.2.840.113549.1.1.11'), + ) + + encoded_null = der_encode(univ.Null("")) + + next_layer = rfc3852.id_ct_contentInfo + + count = 0 + while next_layer: + asn1Object, rest = der_decode(substrate, asn1Spec=layers[next_layer]) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + if next_layer == rfc3852.id_signedData: + for d in asn1Object['digestAlgorithms']: + assert d['algorithm'] in alg_oids + assert d['parameters'] == encoded_null + count += 1 + + for si in asn1Object['signerInfos']: + assert si['digestAlgorithm']['algorithm'] in alg_oids + assert si['digestAlgorithm']['parameters'] == encoded_null + count += 1 + + assert si['signatureAlgorithm']['algorithm'] in alg_oids + assert si['signatureAlgorithm']['parameters'] == encoded_null + count += 1 + + if next_layer == rfc6402.id_cct_PKIData: + for req in asn1Object['reqSequence']: + cr = req['tcr']['certificationRequest'] + assert cr['signatureAlgorithm']['algorithm'] in alg_oids + assert cr['signatureAlgorithm']['parameters'] == encoded_null + count += 1 + + cri_spki = cr['certificationRequestInfo']['subjectPublicKeyInfo'] + assert cri_spki['algorithm']['algorithm'] in alg_oids + assert cri_spki['algorithm']['parameters'] == encoded_null + count += 1 + + substrate = getNextSubstrate[next_layer](asn1Object) + next_layer = getNextLayer[next_layer](asn1Object) + + assert count == 5 + + +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_rfc4211.py b/tests/test_rfc4211.py new file mode 100644 index 0000000..f96cdcf --- /dev/null +++ b/tests/test_rfc4211.py @@ -0,0 +1,61 @@ +# +# 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 rfc4211 + +try: + import unittest2 as unittest + +except ImportError: + import unittest + + +class CertificateReqTestCase(unittest.TestCase): + pem_text = """\ +MIIBozCCAZ8wggEFAgUAwTnj2jCByoABAqURMA8xDTALBgNVBAMTBHVzZXKmgZ8w +DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJ6ZQ2cYbn/lFsmBOlRltbRbFQUvvE0Q +nbopOu1kC7Bmaaz7QTx8nxeiHi4m7uxCbGGxHNoGCt7EmdG8eZUBNAcHyGlXrJdm +0z3/uNEGiBHq+xB8FnFJCA5EIJ3RWFnlbu9otSITLxWK7c5+/NHmWM+yaeHD/f/h +rp01c/8qXZfZAgMBAAGpEDAOBgNVHQ8BAf8EBAMCBeAwLzASBgkrBgEFBQcFAQEM +BTExMTExMBkGCSsGAQUFBwUBAgwMc2VydmVyX21hZ2ljoYGTMA0GCSqGSIb3DQEB +BQUAA4GBAEI3KNEvTq/n1kNVhNhPkovk1AZxyJrN1u1+7Gkc4PLjWwjLOjcEVWt4 +AajUk/gkIJ6bbeO+fZlMjHfPSDKcD6AV2hN+n72QZwfzcw3icNvBG1el9EU4XfIm +xfu5YVWi81/fw8QQ6X6YGHFQkomLd7jxakVyjxSng9BhO6GpjJNF +""" + + def setUp(self): + self.asn1Spec = rfc4211.CertReqMessages() + + 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 + + count = 0 + for crm in asn1Object: + assert crm['certReq']['certTemplate']['version'] == 2 + count += 1 + + assert count == 1 + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + import sys + + result = unittest.TextTestRunner(verbosity=2).run(suite) + sys.exit(not result.wasSuccessful()) -- cgit v1.2.3 From a9139ec7641167d88c8ac418c3ec8bc5c407d588 Mon Sep 17 00:00:00 2001 From: Aliaksei Urbanski Date: Thu, 17 Oct 2019 07:57:05 +0300 Subject: Declare support for Python 3.8 (#86) Python 3.8 is there, so I believe that it would be nice to declare support and add tests for it on CI. Python 3.8.0 release announcement: https://discuss.python.org/t/python-3-8-0-is-now-available/2478 These changes also: - add .venv/ to .gitignore --- .gitignore | 3 +++ .travis.yml | 3 +++ setup.py | 1 + 3 files changed, 7 insertions(+) diff --git a/.gitignore b/.gitignore index 380d0ff..4654707 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,9 @@ __pycache__ # vim swapfiles *.sw? +# virtual environment +.venv/ + # python packaging MANIFEST dist/ diff --git a/.travis.yml b/.travis.yml index 07294be..94d7ad5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,6 +27,9 @@ matrix: - os: linux dist: xenial python: '3.7' + - os: linux + dist: bionic + python: '3.8' - os: linux dist: trusty python: 'nightly' diff --git a/setup.py b/setup.py index 2579e1f..7a0ecd0 100644 --- a/setup.py +++ b/setup.py @@ -40,6 +40,7 @@ Programming Language :: Python :: 3.4 Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 +Programming Language :: Python :: 3.8 Topic :: Communications Topic :: System :: Monitoring Topic :: System :: Networking :: Monitoring -- cgit v1.2.3 From 790b52983a59f35f2e6d3b33da4d3b3dbc9e38e3 Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Thu, 17 Oct 2019 00:57:59 -0400 Subject: Add support for RFC 6960 (#85) --- CHANGES.txt | 1 + pyasn1_modules/rfc6960.py | 223 ++++++++++++++++++++++++++++++++++++++++++++++ tests/__main__.py | 1 + tests/test_rfc6960.py | 169 +++++++++++++++++++++++++++++++++++ 4 files changed, 394 insertions(+) create mode 100644 pyasn1_modules/rfc6960.py create mode 100644 tests/test_rfc6960.py diff --git a/CHANGES.txt b/CHANGES.txt index 346e2c8..bb62b9e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -6,6 +6,7 @@ Revision 0.2.8, released XX-XX-2019 - Added RFC7633 providing TLS Features Certificate Extension - Added RFC7229 providing OIDs for Test Certificate Policies - Added tests for RFC3280, RFC3281, RFC3852, and RFC4211 +- Added RFC6960 providing Online Certificate Status Protocol (OCSP) Revision 0.2.7, released 09-10-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc6960.py b/pyasn1_modules/rfc6960.py new file mode 100644 index 0000000..e5f1305 --- /dev/null +++ b/pyasn1_modules/rfc6960.py @@ -0,0 +1,223 @@ +# +# 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 +# +# Online Certificate Status Protocol (OCSP) +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc6960.txt +# + +from pyasn1.type import univ, char, namedtype, namedval, tag, constraint, useful + +from pyasn1_modules import rfc2560 +from pyasn1_modules import rfc5280 + +MAX = float('inf') + + +# Imports from RFC 5280 + +AlgorithmIdentifier = rfc5280.AlgorithmIdentifier +AuthorityInfoAccessSyntax = rfc5280.AuthorityInfoAccessSyntax +Certificate = rfc5280.Certificate +CertificateSerialNumber = rfc5280.CertificateSerialNumber +CRLReason = rfc5280.CRLReason +Extensions = rfc5280.Extensions +GeneralName = rfc5280.GeneralName +Name = rfc5280.Name + +id_kp = rfc5280.id_kp + +id_ad_ocsp = rfc5280.id_ad_ocsp + + +# Imports from the original OCSP module in RFC 2560 + +AcceptableResponses = rfc2560.AcceptableResponses +ArchiveCutoff = rfc2560.ArchiveCutoff +CertStatus = rfc2560.CertStatus +KeyHash = rfc2560.KeyHash +OCSPResponse = rfc2560.OCSPResponse +OCSPResponseStatus = rfc2560.OCSPResponseStatus +ResponseBytes = rfc2560.ResponseBytes +RevokedInfo = rfc2560.RevokedInfo +UnknownInfo = rfc2560.UnknownInfo +Version = rfc2560.Version + +id_kp_OCSPSigning = rfc2560.id_kp_OCSPSigning + +id_pkix_ocsp = rfc2560.id_pkix_ocsp +id_pkix_ocsp_archive_cutoff = rfc2560.id_pkix_ocsp_archive_cutoff +id_pkix_ocsp_basic = rfc2560.id_pkix_ocsp_basic +id_pkix_ocsp_crl = rfc2560.id_pkix_ocsp_crl +id_pkix_ocsp_nocheck = rfc2560.id_pkix_ocsp_nocheck +id_pkix_ocsp_nonce = rfc2560.id_pkix_ocsp_nonce +id_pkix_ocsp_response = rfc2560.id_pkix_ocsp_response +id_pkix_ocsp_service_locator = rfc2560.id_pkix_ocsp_service_locator + + +# Additional object identifiers + +id_pkix_ocsp_pref_sig_algs = id_pkix_ocsp + (8, ) +id_pkix_ocsp_extended_revoke = id_pkix_ocsp + (9, ) + + +# Updated structures (mostly to improve openTypes support) + +class CertID(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('hashAlgorithm', AlgorithmIdentifier()), + namedtype.NamedType('issuerNameHash', univ.OctetString()), + namedtype.NamedType('issuerKeyHash', univ.OctetString()), + namedtype.NamedType('serialNumber', CertificateSerialNumber()) + ) + + +class SingleResponse(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('certID', CertID()), + namedtype.NamedType('certStatus', CertStatus()), + namedtype.NamedType('thisUpdate', useful.GeneralizedTime()), + namedtype.OptionalNamedType('nextUpdate', useful.GeneralizedTime().subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.OptionalNamedType('singleExtensions', Extensions().subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))) + ) + + +class ResponderID(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('byName', Name().subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.NamedType('byKey', KeyHash().subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))) + ) + + +class ResponseData(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.DefaultedNamedType('version', Version('v1').subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.NamedType('responderID', ResponderID()), + namedtype.NamedType('producedAt', useful.GeneralizedTime()), + namedtype.NamedType('responses', univ.SequenceOf( + componentType=SingleResponse())), + namedtype.OptionalNamedType('responseExtensions', Extensions().subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))) + ) + + +class BasicOCSPResponse(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('tbsResponseData', ResponseData()), + namedtype.NamedType('signatureAlgorithm', AlgorithmIdentifier()), + namedtype.NamedType('signature', univ.BitString()), + namedtype.OptionalNamedType('certs', univ.SequenceOf( + componentType=Certificate()).subtype(explicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 0))) + ) + + +class Request(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('reqCert', CertID()), + namedtype.OptionalNamedType('singleRequestExtensions', Extensions().subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))) + ) + + +class Signature(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('signatureAlgorithm', AlgorithmIdentifier()), + namedtype.NamedType('signature', univ.BitString()), + namedtype.OptionalNamedType('certs', univ.SequenceOf( + componentType=Certificate()).subtype(explicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 0))) + ) + + +class TBSRequest(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.DefaultedNamedType('version', Version('v1').subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.OptionalNamedType('requestorName', GeneralName().subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.NamedType('requestList', univ.SequenceOf( + componentType=Request())), + namedtype.OptionalNamedType('requestExtensions', Extensions().subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))) + ) + + +class OCSPRequest(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('tbsRequest', TBSRequest()), + namedtype.OptionalNamedType('optionalSignature', Signature().subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))) + ) + + +# Previously omitted structure + +class ServiceLocator(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('issuer', Name()), + namedtype.NamedType('locator', AuthorityInfoAccessSyntax()) + ) + + +# Additional structures + +class CrlID(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('crlUrl', char.IA5String().subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.OptionalNamedType('crlNum', univ.Integer().subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.OptionalNamedType('crlTime', useful.GeneralizedTime().subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))) + ) + + +class PreferredSignatureAlgorithm(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('sigIdentifier', AlgorithmIdentifier()), + namedtype.OptionalNamedType('certIdentifier', AlgorithmIdentifier()) + ) + + +class PreferredSignatureAlgorithms(univ.SequenceOf): + componentType = PreferredSignatureAlgorithm() + + + +# Response Type OID to Response Map + +ocspResponseMap = { + id_pkix_ocsp_basic: BasicOCSPResponse(), +} + + +# Map of Extension OIDs to Extensions added to the ones +# that are in rfc5280.py + +_certificateExtensionsMapUpdate = { + # Certificate Extension + id_pkix_ocsp_nocheck: univ.Null(""), + # OCSP Request Extensions + id_pkix_ocsp_nonce: univ.OctetString(), + id_pkix_ocsp_response: AcceptableResponses(), + id_pkix_ocsp_service_locator: ServiceLocator(), + id_pkix_ocsp_pref_sig_algs: PreferredSignatureAlgorithms(), + # OCSP Response Extensions + id_pkix_ocsp_crl: CrlID(), + id_pkix_ocsp_archive_cutoff: ArchiveCutoff(), + id_pkix_ocsp_extended_revoke: univ.Null(""), +} + +rfc5280.certificateExtensionsMap.update(_certificateExtensionsMapUpdate) diff --git a/tests/__main__.py b/tests/__main__.py index 6c004b5..10bfeee 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -58,6 +58,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc6032.suite', 'tests.test_rfc6210.suite', 'tests.test_rfc6211.suite', + 'tests.test_rfc6960.suite', 'tests.test_rfc7030.suite', 'tests.test_rfc7191.suite', 'tests.test_rfc7229.suite', diff --git a/tests/test_rfc6960.py b/tests/test_rfc6960.py new file mode 100644 index 0000000..33acbb9 --- /dev/null +++ b/tests/test_rfc6960.py @@ -0,0 +1,169 @@ +# +# 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 rfc4055 +from pyasn1_modules import rfc6960 + +try: + import unittest2 as unittest + +except ImportError: + import unittest + + +class OCSPRequestTestCase(unittest.TestCase): + ocsp_req_pem_text = """\ +MGowaDBBMD8wPTAJBgUrDgMCGgUABBS3ZrMV9C5Dko03aH13cEZeppg3wgQUkqR1LKSevoFE63n8 +isWVpesQdXMCBDXe9M+iIzAhMB8GCSsGAQUFBzABAgQSBBBjdJOiIW9EKJGELNNf/rdA +""" + + def setUp(self): + self.asn1Spec = rfc6960.OCSPRequest() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.ocsp_req_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + assert asn1Object['tbsRequest']['version'] == 0 + + count = 0 + for extn in asn1Object['tbsRequest']['requestExtensions']: + assert extn['extnID'] in rfc5280.certificateExtensionsMap.keys() + ev, rest = der_decode(extn['extnValue'], + asn1Spec=rfc5280.certificateExtensionsMap[extn['extnID']]) + assert not rest + assert ev.prettyPrint() + assert der_encode(ev) == extn['extnValue'] + count += 1 + + assert count == 1 + + def testOpenTypes(self): + substrate = pem.readBase64fromText(self.ocsp_req_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['tbsRequest']['version'] == 0 + for req in asn1Object['tbsRequest']['requestList']: + ha = req['reqCert']['hashAlgorithm'] + assert ha['algorithm'] == rfc4055.id_sha1 + assert ha['parameters'] == univ.Null("") + + +class OCSPResponseTestCase(unittest.TestCase): + ocsp_resp_pem_text = """\ +MIIEvQoBAKCCBLYwggSyBgkrBgEFBQcwAQEEggSjMIIEnzCCAQ+hgYAwfjELMAkGA1UEBhMCQVUx +EzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEV +MBMGA1UEAxMMc25tcGxhYnMuY29tMSAwHgYJKoZIhvcNAQkBFhFpbmZvQHNubXBsYWJzLmNvbRgP +MjAxMjA0MTExNDA5MjJaMFQwUjA9MAkGBSsOAwIaBQAEFLdmsxX0LkOSjTdofXdwRl6mmDfCBBSS +pHUspJ6+gUTrefyKxZWl6xB1cwIENd70z4IAGA8yMDEyMDQxMTE0MDkyMlqhIzAhMB8GCSsGAQUF +BzABAgQSBBBjdJOiIW9EKJGELNNf/rdAMA0GCSqGSIb3DQEBBQUAA4GBADk7oRiCy4ew1u0N52QL +RFpW+tdb0NfkV2Xyu+HChKiTThZPr9ZXalIgkJ1w3BAnzhbB0JX/zq7Pf8yEz/OrQ4GGH7HyD3Vg +PkMu+J6I3A2An+bUQo99AmCbZ5/tSHtDYQMQt3iNbv1fk0yvDmh7UdKuXUNSyJdHeg27dMNy4k8A +oIIC9TCCAvEwggLtMIICVqADAgECAgEBMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAkFVMRMw +EQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxFTAT +BgNVBAMTDHNubXBsYWJzLmNvbTEgMB4GCSqGSIb3DQEJARYRaW5mb0Bzbm1wbGFicy5jb20wHhcN +MTIwNDExMTMyNTM1WhcNMTMwNDExMTMyNTM1WjB+MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29t +ZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRUwEwYDVQQDEwxzbm1w +bGFicy5jb20xIDAeBgkqhkiG9w0BCQEWEWluZm9Ac25tcGxhYnMuY29tMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQDDDU5HOnNV8I2CojxB8ilIWRHYQuaAjnjrETMOprouDHFXnwWqQo/I3m0b +XYmocrh9kDefb+cgc7+eJKvAvBqrqXRnU38DmQU/zhypCftGGfP8xjuBZ1n23lR3hplN1yYA0J2X +SgBaAg6e8OsKf1vcX8Es09rDo8mQpt4G2zR56wIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG ++EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQU8Ys2dpJFLMHl +yY57D4BNmlqnEcYwHwYDVR0jBBgwFoAU8Ys2dpJFLMHlyY57D4BNmlqnEcYwDQYJKoZIhvcNAQEF +BQADgYEAWR0uFJVlQId6hVpUbgXFTpywtNitNXFiYYkRRv77McSJqLCa/c1wnuLmqcFcuRUK0oN6 +8ZJDP2HDDKe8MCZ8+sx+CF54eM8VCgN9uQ9XyE7x9XrXDd3Uw9RJVaWSIezkNKNeBE0lDM2jUjC4 +HAESdf7nebz1wtqAOXE1jWF/y8g= +""" + + def setUp(self): + self.asn1Spec = rfc6960.OCSPResponse() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.ocsp_resp_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + assert asn1Object['responseStatus'] == 0 + rb = asn1Object['responseBytes'] + assert rb['responseType'] in rfc6960.ocspResponseMap.keys() + resp, rest = der_decode(rb['response'], + asn1Spec=rfc6960.ocspResponseMap[rb['responseType']]) + assert not rest + assert resp.prettyPrint() + assert der_encode(resp) == rb['response'] + + resp['tbsResponseData']['version'] == 0 + count = 0 + for extn in resp['tbsResponseData']['responseExtensions']: + assert extn['extnID'] in rfc5280.certificateExtensionsMap.keys() + ev, rest = der_decode(extn['extnValue'], + asn1Spec=rfc5280.certificateExtensionsMap[extn['extnID']]) + assert not rest + assert ev.prettyPrint() + assert der_encode(ev) == extn['extnValue'] + count += 1 + + assert count == 1 + + def testOpenTypes(self): + substrate = pem.readBase64fromText(self.ocsp_resp_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['responseStatus'] == 0 + rb = asn1Object['responseBytes'] + assert rb['responseType'] in rfc6960.ocspResponseMap.keys() + resp, rest = der_decode(rb['response'], + asn1Spec=rfc6960.ocspResponseMap[rb['responseType']], + decodeOpenTypes=True) + assert not rest + assert resp.prettyPrint() + assert der_encode(resp) == rb['response'] + + resp['tbsResponseData']['version'] == 0 + for rdn in resp['tbsResponseData']['responderID']['byName']['rdnSequence']: + for attr in rdn: + if attr['type'] == rfc5280.id_emailAddress: + assert attr['value'] == 'info@snmplabs.com' + + for r in resp['tbsResponseData']['responses']: + ha = r['certID']['hashAlgorithm'] + assert ha['algorithm'] == rfc4055.id_sha1 + assert ha['parameters'] == univ.Null("") + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + import sys + + result = unittest.TextTestRunner(verbosity=2).run(suite) + sys.exit(not result.wasSuccessful()) -- cgit v1.2.3 From aee190c5031fba3d6742b2a46091401ca5d1f544 Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Sat, 19 Oct 2019 13:55:20 -0400 Subject: Improve test for RFC3709 with a real world certificate (#87) --- CHANGES.txt | 1 + tests/test_rfc3709.py | 116 ++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 104 insertions(+), 13 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index bb62b9e..6b60f37 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -3,6 +3,7 @@ Revision 0.2.8, released XX-XX-2019 ----------------------------------- - Improve test routines for modules that use certificate extensions +- Improve test for RFC3709 with a real world certificate - Added RFC7633 providing TLS Features Certificate Extension - Added RFC7229 providing OIDs for Test Certificate Policies - Added tests for RFC3280, RFC3281, RFC3852, and RFC4211 diff --git a/tests/test_rfc3709.py b/tests/test_rfc3709.py index e115391..8313129 100644 --- a/tests/test_rfc3709.py +++ b/tests/test_rfc3709.py @@ -6,8 +6,8 @@ # import sys -from pyasn1.codec.der import decoder as der_decoder -from pyasn1.codec.der import encoder as der_encoder +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 @@ -19,7 +19,7 @@ except ImportError: import unittest -class CertificateExtnTestCase(unittest.TestCase): +class CertificateExtnWithUrlTestCase(unittest.TestCase): pem_text = """\ MIIC9zCCAn2gAwIBAgIJAKWzVCgbsG46MAoGCCqGSM49BAMDMD8xCzAJBgNVBAYT AlVTMQswCQYDVQQIDAJWQTEQMA4GA1UEBwwHSGVybmRvbjERMA8GA1UECgwIQm9n @@ -43,14 +43,104 @@ Pj22pmfmQi5w21UljqoTj/+lQLkU3wfy5BdVKBwI0GfEA+YL3ctSzPNqAA== self.asn1Spec = rfc5280.Certificate() 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 + + extn_list = [ ] + for extn in asn1Object['tbsCertificate']['extensions']: + extn_list.append(extn['extnID']) + + if extn['extnID'] == rfc3709.id_pe_logotype: + s = extn['extnValue'] + logotype, rest = der_decode(s, rfc3709.LogotypeExtn()) + assert not rest + assert logotype.prettyPrint() + assert der_encode(logotype) == s + ids = logotype['subjectLogo']['direct']['image'][0]['imageDetails'] + assert ids['mediaType'] == "image/png" + assert ids['logotypeURI'][0] == "http://www.vigilsec.com/vigilsec_logo.png" + + assert rfc3709.id_pe_logotype in extn_list + def testExtensionsMap(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 + + for extn in asn1Object['tbsCertificate']['extensions']: + if extn['extnID'] in rfc5280.certificateExtensionsMap.keys(): + extnValue, rest = der_decode(extn['extnValue'], + asn1Spec=rfc5280.certificateExtensionsMap[extn['extnID']]) + assert der_encode(extnValue) == extn['extnValue'] - asn1Object, rest = der_decoder.decode(substrate, asn1Spec=self.asn1Spec) +class CertificateExtnWithDataTestCase(unittest.TestCase): + pem_text = """\ +MIIJJDCCCAygAwIBAgIRAPIGo/5ScWbpAAAAAFwQBqkwDQYJKoZIhvcNAQELBQAw +gbkxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQL +Ex9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykg +MjAxOCBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxLTAr +BgNVBAMTJEVudHJ1c3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gVk1DMTAeFw0x +OTA4MzAxNDMyMzlaFw0yMDAyMjUxNTAyMzZaMIIBjTEOMAwGA1UEERMFMTAwMTcx +CzAJBgNVBAYTAlVTMREwDwYDVQQIEwhOZXcgWW9yazERMA8GA1UEBxMITmV3IFlv +cmsxGDAWBgNVBAkTDzI3MCBQYXJrIEF2ZW51ZTETMBEGCysGAQQBgjc8AgEDEwJV +UzEZMBcGCysGAQQBgjc8AgECEwhEZWxhd2FyZTEfMB0GA1UEChMWSlBNb3JnYW4g +Q2hhc2UgYW5kIENvLjEdMBsGA1UEDxMUUHJpdmF0ZSBPcmdhbml6YXRpb24xNzA1 +BgNVBAsTLkpQTUMgRmlyc3QgVmVyaWZpZWQgTWFyayBDZXJ0aWZpY2F0ZSBXb3Js +ZHdpZGUxDzANBgNVBAUTBjY5MTAxMTEXMBUGCisGAQQBg55fAQQTBzIwMTUzODkx +EjAQBgorBgEEAYOeXwEDEwJVUzEmMCQGCisGAQQBg55fAQITFmh0dHBzOi8vd3d3 +LnVzcHRvLmdvdi8xHzAdBgNVBAMTFkpQTW9yZ2FuIENoYXNlIGFuZCBDby4wggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCNLY+etlX06q1MxA1VT/P20h1i +eFGTzX4fqSQNG+ypmjNfLa8YXraO1v1hahenkRUWrVPW0Hq3zKNJcCDmosox6+tB +59u0b1xgN8y8D05AEC7qoVVdbaWKENMxCN4CDfST6d3YOqApjqEFAGZ71s39tRRG +kmWGJb4jKXcUX8FWV8w/vjKrpipZ8JsX2tuOp2uxFLkmi+V7gvN8tpbHUipP5K7L +190VOBytSWPudXefnYG3UWRfwah7Fq1bKYT/cCwStUm8XlfA8nUumeVsAiyC6phs +adn26MYiSddsBU08TGthmunLAO0+shaBy6jHYZxMa37S67vVlDpxbeF+TPVXAgMB +AAGjggROMIIESjATBgorBgEEAdZ5AgQDAQH/BAIFADCCArAGCCsGAQUFBwEMBIIC +ojCCAp6iggKaoIICljCCApIwggKOMIICihYNaW1hZ2Uvc3ZnK3htbDAzMDEwDQYJ +YIZIAWUDBAIBBQAEIBnwW6ChGgWWIRn3qn/xGAOlhDflA3z5jhZcZTNDlxF5MIIC +QhaCAj5kYXRhOmltYWdlL3N2Zyt4bWw7YmFzZTY0LEg0c0lBQUFBQUFBQUFJV1Iz +V3JqTUJCR3I1dW5tR3F2Rml4NUpQODBObkZLRTVhbTRFSmhJYmVMazZpT1dhOXRa +TWQyOXVrN2NsTG9SV25CMHNENGNPYVR0TGdmLzVYUWE5TVdkWlV3S1pDQnJ2YjFv +YWp5aEoyNlZ6NW45OHZaNHBaemVOU1ZObGxYbXhnZUR2Vk93MU5abnRwdWFvRlNB +b1YwNFBmMkVYNk5UVzA2ZUNsUE9YK3FRRXpON1dWR0RLRkFoTldwS0ErQVB3RTRK +MzNiNXg5REtBYTdyTlV2cG40dFNwMndycWpPRElwRHd0THNyTTBmeVlCaVYyM0Nq +bDNYeEs0N0RJTVlQRkdiM0ZXSTZKTHZpc1JqV1ZSL1B3TmxGRVh1OUpmTmJtQk1H +RFlqZy9PMTlvVWVWclh0QWtJWTBEY0o0N2JKOXBTb01iclZwdGVNd3VmTDJjMml5 +Ym9qVU5veVlUOFFnL1VxWWtCNW41VW5QQWZYU2pub0tPbEl1eW5oOVRJVTh1Z3JF +YVMrVC9lRzZRWDh6OXl2YkdIZ0VLZjJ5S1h3dU9Sa2VsOGJQeFJoUHhtSnN0TDBT +bi9qOUtXWU8yR3dsM2EremNhbmhOYTV0YzZORkdHcVVFUUVwVmY0R3lVNnhOMnRx +WGgwWXQrM1BpcEhlK2l0cElRMGg0VHBoWnRrQ3plM0d6M2NjdllHbkp0cjZKVUNB +QUE9MCIGA1UdEQQbMBmCF2V4Y2hhZGRldi5sYWJtb3JnYW4uY29tMBMGA1UdJQQM +MAoGCCsGAQUFBwMfMA4GA1UdDwEB/wQEAwIHgDBmBggrBgEFBQcBAQRaMFgwIwYI +KwYBBQUHMAGGF2h0dHA6Ly9vY3NwLmVudHJ1c3QubmV0MDEGCCsGAQUFBzAChiVo +dHRwOi8vYWlhLmVudHJ1c3QubmV0L3ZtYzEtY2hhaW4uY2VyMDIGA1UdHwQrMCkw +J6AloCOGIWh0dHA6Ly9jcmwuZW50cnVzdC5uZXQvdm1jMWNhLmNybDBPBgNVHSAE +SDBGMDYGCmCGSAGG+mwKAQswKDAmBggrBgEFBQcCARYaaHR0cDovL3d3dy5lbnRy +dXN0Lm5ldC9ycGEwDAYKKwYBBAGDnl8BATAfBgNVHSMEGDAWgBSLtjl20DSQpj9i +4WTqPrz0fEahczAdBgNVHQ4EFgQUxAJ+yoDhzpPUzAPWKBYxg108dU0wCQYDVR0T +BAIwADANBgkqhkiG9w0BAQsFAAOCAQEAnqdB/vcwxFcxAlyCK0W5HOthXUdXRg9a +GwPDupqmLq2rKfyysZXonJJfr8jqO0f3l6TWTTJlXHljAwwXMtg3T3ngLyEzip5p +g0zH7s5eXjmWRhOeuHt21o611bXDbUNFTF0IpbYBTgOwAz/+k3XLVehf8dW7Y0Lr +VkzxJ6U82NxmqjaAnkm+H127x5/jPAr4LLD4gZfqFaHzw/ZLoS+fXFGs+dpuYE4s +n+xe0msYMu8qWABiMGA+MCKl45Dp5di+c2fyXtKyQ3rKI8XXZ0nN4bXK7DZd+3E3 +kbpmR6cDliloU808Bi/erMkrfUHRoZ2d586lkmwkLcoDkJ/yPD+Jhw== +""" + + def setUp(self): + self.asn1Spec = rfc5280.Certificate() + + 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_encoder.encode(asn1Object) == substrate + assert der_encode(asn1Object) == substrate extn_list = [ ] for extn in asn1Object['tbsCertificate']['extensions']: @@ -58,28 +148,28 @@ Pj22pmfmQi5w21UljqoTj/+lQLkU3wfy5BdVKBwI0GfEA+YL3ctSzPNqAA== if extn['extnID'] == rfc3709.id_pe_logotype: s = extn['extnValue'] - logotype, rest = der_decoder.decode(s, rfc3709.LogotypeExtn()) + logotype, rest = der_decode(s, rfc3709.LogotypeExtn()) assert not rest assert logotype.prettyPrint() - assert der_encoder.encode(logotype) == s + assert der_encode(logotype) == s ids = logotype['subjectLogo']['direct']['image'][0]['imageDetails'] - assert ids['mediaType'] == "image/png" - assert ids['logotypeURI'][0] == "http://www.vigilsec.com/vigilsec_logo.png" + assert ids['mediaType'] == "image/svg+xml" + assert ids['logotypeURI'][0][0:25] == "data:image/svg+xml;base64" assert rfc3709.id_pe_logotype in extn_list def testExtensionsMap(self): substrate = pem.readBase64fromText(self.pem_text) - asn1Object, rest = der_decoder.decode(substrate, asn1Spec=self.asn1Spec) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) assert not rest assert asn1Object.prettyPrint() - assert der_encoder.encode(asn1Object) == substrate + assert der_encode(asn1Object) == substrate for extn in asn1Object['tbsCertificate']['extensions']: if extn['extnID'] in rfc5280.certificateExtensionsMap.keys(): - extnValue, rest = der_decoder.decode(extn['extnValue'], + extnValue, rest = der_decode(extn['extnValue'], asn1Spec=rfc5280.certificateExtensionsMap[extn['extnID']]) - assert der_encoder.encode(extnValue) == extn['extnValue'] + assert der_encode(extnValue) == extn['extnValue'] suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) -- cgit v1.2.3 From da35e37e01f12711f00e41a46f2cbe79311285e7 Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Sun, 20 Oct 2019 02:00:29 -0400 Subject: Add support for RFC 6955 (#88) --- CHANGES.txt | 1 + pyasn1_modules/rfc6955.py | 108 ++++++++++++++++++++++++++++++++++++++++++++++ tests/__main__.py | 1 + tests/test_rfc6955.py | 99 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 209 insertions(+) create mode 100644 pyasn1_modules/rfc6955.py create mode 100644 tests/test_rfc6955.py diff --git a/CHANGES.txt b/CHANGES.txt index 6b60f37..f4f03f7 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -8,6 +8,7 @@ Revision 0.2.8, released XX-XX-2019 - Added RFC7229 providing OIDs for Test Certificate Policies - Added tests for RFC3280, RFC3281, RFC3852, and RFC4211 - Added RFC6960 providing Online Certificate Status Protocol (OCSP) +- Added RFC6955 providing Diffie-Hellman Proof-of-Possession Algorithms Revision 0.2.7, released 09-10-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc6955.py b/pyasn1_modules/rfc6955.py new file mode 100644 index 0000000..09f2d65 --- /dev/null +++ b/pyasn1_modules/rfc6955.py @@ -0,0 +1,108 @@ +# +# 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 +# +# Diffie-Hellman Proof-of-Possession Algorithms +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc6955.txt +# + +from pyasn1.type import namedtype +from pyasn1.type import univ + +from pyasn1_modules import rfc3279 +from pyasn1_modules import rfc5280 +from pyasn1_modules import rfc5652 + + +# Imports from RFC 5652 + +MessageDigest = rfc5652.MessageDigest + +IssuerAndSerialNumber = rfc5652.IssuerAndSerialNumber + + +# Imports from RFC 5280 + +id_pkix = rfc5280.id_pkix + + +# Imports from RFC 3279 + +Dss_Sig_Value = rfc3279.Dss_Sig_Value + +DomainParameters = rfc3279.DomainParameters + + +# Static DH Proof-of-Possession + +class DhSigStatic(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('issuerAndSerial', IssuerAndSerialNumber()), + namedtype.NamedType('hashValue', MessageDigest()) + ) + + +# Object Identifiers + +id_dh_sig_hmac_sha1 = id_pkix + (6, 3, ) + +id_dhPop_static_sha1_hmac_sha1 = univ.ObjectIdentifier(id_dh_sig_hmac_sha1) + + +id_alg_dh_pop = id_pkix + (6, 4, ) + +id_alg_dhPop_sha1 = univ.ObjectIdentifier(id_alg_dh_pop) + +id_alg_dhPop_sha224 = id_pkix + (6, 5, ) + +id_alg_dhPop_sha256 = id_pkix + (6, 6, ) + +id_alg_dhPop_sha384 = id_pkix + (6, 7, ) + +id_alg_dhPop_sha512 = id_pkix + (6, 8, ) + + +id_alg_dhPop_static_sha224_hmac_sha224 = id_pkix + (6, 15, ) + +id_alg_dhPop_static_sha256_hmac_sha256 = id_pkix + (6, 16, ) + +id_alg_dhPop_static_sha384_hmac_sha384 = id_pkix + (6, 17, ) + +id_alg_dhPop_static_sha512_hmac_sha512 = id_pkix + (6, 18, ) + + +id_alg_ecdhPop_static_sha224_hmac_sha224 = id_pkix + (6, 25, ) + +id_alg_ecdhPop_static_sha256_hmac_sha256 = id_pkix + (6, 26, ) + +id_alg_ecdhPop_static_sha384_hmac_sha384 = id_pkix + (6, 27, ) + +id_alg_ecdhPop_static_sha512_hmac_sha512 = id_pkix + (6, 28, ) + + +# Update the Algorithm Identifier map in rfc5280.py + +_algorithmIdentifierMapUpdate = { + id_alg_dh_pop: DomainParameters(), + id_alg_dhPop_sha224: DomainParameters(), + id_alg_dhPop_sha256: DomainParameters(), + id_alg_dhPop_sha384: DomainParameters(), + id_alg_dhPop_sha512: DomainParameters(), + id_dh_sig_hmac_sha1: univ.Null(""), + id_alg_dhPop_static_sha224_hmac_sha224: univ.Null(""), + id_alg_dhPop_static_sha256_hmac_sha256: univ.Null(""), + id_alg_dhPop_static_sha384_hmac_sha384: univ.Null(""), + id_alg_dhPop_static_sha512_hmac_sha512: univ.Null(""), + id_alg_ecdhPop_static_sha224_hmac_sha224: univ.Null(""), + id_alg_ecdhPop_static_sha256_hmac_sha256: univ.Null(""), + id_alg_ecdhPop_static_sha384_hmac_sha384: univ.Null(""), + id_alg_ecdhPop_static_sha512_hmac_sha512: univ.Null(""), +} + +rfc5280.algorithmIdentifierMap.update(_algorithmIdentifierMapUpdate) diff --git a/tests/__main__.py b/tests/__main__.py index 10bfeee..b9e7070 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -58,6 +58,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc6032.suite', 'tests.test_rfc6210.suite', 'tests.test_rfc6211.suite', + 'tests.test_rfc6955.suite', 'tests.test_rfc6960.suite', 'tests.test_rfc7030.suite', 'tests.test_rfc7191.suite', diff --git a/tests/test_rfc6955.py b/tests/test_rfc6955.py new file mode 100644 index 0000000..eb4d5cf --- /dev/null +++ b/tests/test_rfc6955.py @@ -0,0 +1,99 @@ +# +# 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 rfc5480 +from pyasn1_modules import rfc6402 +from pyasn1_modules import rfc6955 + +try: + import unittest2 as unittest + +except ImportError: + import unittest + + +class CertificationRequestTestCase(unittest.TestCase): + pem_text = """\ +MIIDPDCCArsCAQAwTjELMAkGA1UEBhMCVVMxETAPBgNVBAoTCFhFVEkgSW5jMRAw +DgYDVQQLEwdUZXN0aW5nMRowGAYDVQQDExFQS0lYIEV4YW1wbGUgVXNlcjCCAkEw +ggG2BgcqhkjOPgIBMIIBqQKBgQCUhOBFbH9pUWI+VoB8aOfFqZ6edHSU7ZCMHcTh +ShSC9dKUDBnjuRC7EbnlpfuOIVFjAoaqBrghNrZ/Nt/R1mhbeXwdWhR1H2qTdZPO +u5dyivAPI51H9tSzx/D05vYrwjLhiWe+fgau+NABa4sq9QLXtqhjlIOwGzF9Uhre +5QOFJwKBgCamMixaK9QzK1zcBodTP5AGYVA4PtK5fYEcEhDFDFPUZNGOMAcIjN0/ +Ci8s1ht/V4bQ2rtuNioY6NO8cDF6SLZOGG7dHyIG6z/q1EFp2ZveR5V6cpHSCX9J +XDsDM1HI8Tma/wTVbn6UPQO49jEVJkiVqFzeR4i0aToAp4ae2tHNAiEA6HL6lvAR +QPXy3P07XXiUsYUB5Wk3IfclubpxSvxgMPsCYQCjkQHAqG6kTaBW/Gz+H6ewzQ+U +hwwlvpd2jevlpAldq4PNgAs1Z38MjqcxmDKFOUCdEZjY3rh/hpuvjWc9tna0YS8h +4UsOaP9TPofd2HFWaEfc9yBjSzxfeHGD5nCe4pIwGgMVABzVOg0Xgm0KgXWBRhCO +PtsJ5Jg0AgE3A4GEAAKBgBNjoYUEjEaoiOv0XqiTdK79rp6WJxJlxEwHBj4Y/pS4 +qHlIvS40tkfKBDCh7DP9GgstnlDJeA+uauy1a2q+slzasp94LLl34nkrJb8uC1lK +k0v4s+yBNK6XR1LgqCmY7NGwyitveovbTo2lFX5+rzNiCZ4PEUSMwY2iEZ5T77Lo +oCEwHwYJKoZIhvcNAQkOMRIwEDAOBgNVHQ8BAf8EBAMCAwgwDAYIKwYBBQUHBgMF +AANtADBqMFIwSDELMAkGA1UEBhMCVVMxETAPBgNVBAoTCFhFVEkgSW5jMRAwDgYD +VQQLEwdUZXN0aW5nMRQwEgYDVQQDEwtSb290IERTQSBDQQIGANo5tuLLBBQtBXf+ +Xo9l9a+tyVybAsCoiClhYw== +""" + + def setUp(self): + self.asn1Spec = rfc6402.CertificationRequest() + + 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 + + spki_a = asn1Object['certificationRequestInfo']['subjectPublicKeyInfo']['algorithm'] + assert spki_a['algorithm'] == rfc5480.dhpublicnumber + assert spki_a['algorithm'] in rfc5280.algorithmIdentifierMap.keys() + params, rest = der_decode(spki_a['parameters'], asn1Spec=rfc6955.DomainParameters()) + assert not rest + assert params.prettyPrint() + assert der_encode(params) == spki_a['parameters'] + assert params['validationParms']['pgenCounter'] == 55 + + sig_a = asn1Object['signatureAlgorithm'] + assert sig_a['algorithm'] == rfc6955.id_dhPop_static_sha1_hmac_sha1 + assert sig_a['algorithm'] in rfc5280.algorithmIdentifierMap.keys() + assert sig_a['parameters'] == der_encode(univ.Null("")) + + 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 + + spki_a = asn1Object['certificationRequestInfo']['subjectPublicKeyInfo']['algorithm'] + assert spki_a['algorithm'] == rfc5480.dhpublicnumber + assert spki_a['parameters']['validationParms']['pgenCounter'] == 55 + + sig_a = asn1Object['signatureAlgorithm'] + assert sig_a['algorithm'] == rfc6955.id_dhPop_static_sha1_hmac_sha1 + assert sig_a['parameters'] == univ.Null("") + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + import sys + + result = unittest.TextTestRunner(verbosity=2).run(suite) + sys.exit(not result.wasSuccessful()) -- cgit v1.2.3 From a080f0bfbcd43a84a95e6118e0b258dd2184f28d Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Mon, 21 Oct 2019 16:13:24 -0400 Subject: Updated the handling of maps for use with openType for RFC 3279 (#89) --- CHANGES.txt | 1 + pyasn1_modules/rfc3279.py | 29 +++- tests/__main__.py | 1 + tests/test_rfc3279.py | 356 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 386 insertions(+), 1 deletion(-) create mode 100644 tests/test_rfc3279.py diff --git a/CHANGES.txt b/CHANGES.txt index f4f03f7..2b942e6 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -9,6 +9,7 @@ Revision 0.2.8, released XX-XX-2019 - Added tests for RFC3280, RFC3281, RFC3852, and RFC4211 - Added RFC6960 providing Online Certificate Status Protocol (OCSP) - Added RFC6955 providing Diffie-Hellman Proof-of-Possession Algorithms +- Updated the handling of maps for use with openType for RFC 3279 Revision 0.2.7, released 09-10-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc3279.py b/pyasn1_modules/rfc3279.py index 428c0e8..f6e24de 100644 --- a/pyasn1_modules/rfc3279.py +++ b/pyasn1_modules/rfc3279.py @@ -4,12 +4,19 @@ # Copyright (c) 2017, Danielle Madeley # License: http://snmplabs.com/pyasn1/license.html # -# Derived from RFC 3279 +# Modified by Russ Housley to add maps for use with opentypes. +# +# Algorithms and Identifiers for Internet X.509 Certificates and CRLs +# +# Derived from RFC 3279: +# https://www.rfc-editor.org/rfc/rfc3279.txt # from pyasn1.type import namedtype from pyasn1.type import namedval from pyasn1.type import univ +from pyasn1_modules import rfc5280 + def _OID(*components): output = [] @@ -231,3 +238,23 @@ prime239v1 = _OID(primeCurve, 4) prime239v2 = _OID(primeCurve, 5) prime239v3 = _OID(primeCurve, 6) prime256v1 = _OID(primeCurve, 7) + + +# Map of Algorithm Identifier OIDs to Parameters added to the +# ones in rfc5280.py. Do not add OIDs with absent paramaters. + +_algorithmIdentifierMapUpdate = { + md2: univ.Null(""), + md5: univ.Null(""), + id_sha1: univ.Null(""), + id_dsa: Dss_Parms(), + rsaEncryption: univ.Null(""), + md2WithRSAEncryption: univ.Null(""), + md5WithRSAEncryption: univ.Null(""), + sha1WithRSAEncryption: univ.Null(""), + dhpublicnumber: DomainParameters(), + id_keyExchangeAlgorithm: KEA_Parms_Id(), + id_ecPublicKey: EcpkParameters(), +} + +rfc5280.algorithmIdentifierMap.update(_algorithmIdentifierMapUpdate) diff --git a/tests/__main__.py b/tests/__main__.py index b9e7070..c852b56 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -23,6 +23,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc2986.suite', 'tests.test_rfc3161.suite', 'tests.test_rfc3274.suite', + 'tests.test_rfc3279.suite', 'tests.test_rfc3280.suite', 'tests.test_rfc3281.suite', 'tests.test_rfc3447.suite', diff --git a/tests/test_rfc3279.py b/tests/test_rfc3279.py new file mode 100644 index 0000000..28d5cce --- /dev/null +++ b/tests/test_rfc3279.py @@ -0,0 +1,356 @@ +# +# 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 rfc3279 + +try: + import unittest2 as unittest +except ImportError: + import unittest + +class RSACertificateTestCase(unittest.TestCase): + rsa_cert_pem_text = """\ +MIIE8TCCA9mgAwIBAgIQbyXcFa/fXqMIVgw7ek/H+DANBgkqhkiG9w0BAQUFADBv +MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk +ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF +eHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFow +gYExCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO +BgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMScwJQYD +VQQDEx5DT01PRE8gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQDQQIuLcuORG/dRwRtUBJjTqb/B5opdO4f7u4jO +DeMvPwaW8KIpUJmu2zuhV7B0UXHN7UKRTUH+qcjYaoZ3RLtZZpdQXrTULHBEz9o3 +lUJpPDDEcbNS8CFNodi6OXwcnqMknfKDFpiqFnxDmxVbt640kf7UYiYYRpo/68H5 +8ZBX66x6DYvbcjBqZtXgRqNw3GjZ/wRIiXfeten7Z21B6bw5vTLZYgLxsag9bjec +4i/i06Imi8a4VUOI4SM+pdIkOWpHqwDUobOpJf4NP6cdutNRwQuk2qw471VQJAVl +RpM0Ty2NrcbUIRnSjsoFYXEHc0flihkSvQRNzk6cpUisuyb3AgMBAAGjggF0MIIB +cDAfBgNVHSMEGDAWgBStvZh6NLQm9/rEJlTvA73gJMtUGjAdBgNVHQ4EFgQUC1jl +i8ZMFTekQKkwqSG+RzZaVv8wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMB +Af8wEQYDVR0gBAowCDAGBgRVHSAAMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9j +cmwudXNlcnRydXN0LmNvbS9BZGRUcnVzdEV4dGVybmFsQ0FSb290LmNybDCBswYI +KwYBBQUHAQEEgaYwgaMwPwYIKwYBBQUHMAKGM2h0dHA6Ly9jcnQudXNlcnRydXN0 +LmNvbS9BZGRUcnVzdEV4dGVybmFsQ0FSb290LnA3YzA5BggrBgEFBQcwAoYtaHR0 +cDovL2NydC51c2VydHJ1c3QuY29tL0FkZFRydXN0VVROU0dDQ0EuY3J0MCUGCCsG +AQUFBzABhhlodHRwOi8vb2NzcC51c2VydHJ1c3QuY29tMA0GCSqGSIb3DQEBBQUA +A4IBAQAHYJOZqs7Q00fQNzPeP2S35S6jJQzVMx0Njav2fkZ7WQaS44LE5/X289kF +z0k0LTdf9CXH8PtrI3fx8UDXTLtJRTHdAChntylMdagfeTHJNjcPyjVPjPF+3vxG +q79om3AjMC63xVx7ivsYE3lLkkKM3CyrbCK3KFOzGkrOG/soDrc6pNoN90AyT99v +uwFQ/IfTdtn8+7aEA8rJNhj33Wzbu7qBHKat/ij5z7micV0ZBepKRtxzQe+JlEKx +Q4hvNRevHmCDrHqMEHufyfaDbZ76iO4+3e6esL/garnQnweyCROa9aTlyFt5p0c1 +M2jlVZ6qW8swC53HD79oRIGXi1FK +""" + + def setUp(self): + self.asn1Spec = rfc5280.Certificate() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.rsa_cert_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + spki_a = asn1Object['tbsCertificate']['subjectPublicKeyInfo']['algorithm'] + assert spki_a['algorithm'] == rfc3279.rsaEncryption + + spki_pk = asn1Object['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey'].asOctets() + pk, rest = der_decode(spki_pk, asn1Spec=rfc3279.RSAPublicKey()) + assert not rest + assert pk.prettyPrint() + assert der_encode(pk) == spki_pk + assert pk['publicExponent'] == 65537 + + assert asn1Object['tbsCertificate']['signature']['algorithm'] == rfc3279.sha1WithRSAEncryption + assert asn1Object['signatureAlgorithm']['algorithm'] == rfc3279.sha1WithRSAEncryption + + def testOpenTypes(self): + substrate = pem.readBase64fromText(self.rsa_cert_pem_text) + asn1Object, rest = der_decode(substrate, + asn1Spec=self.asn1Spec, + decodeOpenTypes=True) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + spki_a = asn1Object['tbsCertificate']['subjectPublicKeyInfo']['algorithm'] + assert spki_a['algorithm'] == rfc3279.rsaEncryption + assert spki_a['parameters'] == univ.Null("") + + +class ECCertificateTestCase(unittest.TestCase): + ec_cert_pem_text = """\ +MIIDrDCCApSgAwIBAgIQCssoukZe5TkIdnRw883GEjANBgkqhkiG9w0BAQwFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD +QTAeFw0xMzAzMDgxMjAwMDBaFw0yMzAzMDgxMjAwMDBaMEwxCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxJjAkBgNVBAMTHURpZ2lDZXJ0IEVDQyBT +ZWN1cmUgU2VydmVyIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE4ghC6nfYJN6g +LGSkE85AnCNyqQIKDjc/ITa4jVMU9tWRlUvzlgKNcR7E2Munn17voOZ/WpIRllNv +68DLP679Wz9HJOeaBy6Wvqgvu1cYr3GkvXg6HuhbPGtkESvMNCuMo4IBITCCAR0w +EgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwNAYIKwYBBQUHAQEE +KDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQgYDVR0f +BDswOTA3oDWgM4YxaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0R2xv +YmFsUm9vdENBLmNybDA9BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYc +aHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAdBgNVHQ4EFgQUo53mH/naOU/A +buiRy5Wl2jHiCp8wHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJ +KoZIhvcNAQEMBQADggEBAMeKoENL7HTJxavVHzA1Nm6YVntIrAVjrnuaVyRXzG/6 +3qttnMe2uuzO58pzZNvfBDcKAEmzP58mrZGMIOgfiA4q+2Y3yDDo0sIkp0VILeoB +UEoxlBPfjV/aKrtJPGHzecicZpIalir0ezZYoyxBEHQa0+1IttK7igZFcTMQMHp6 +mCHdJLnsnLWSB62DxsRq+HfmNb4TDydkskO/g+l3VtsIh5RHFPVfKK+jaEyDj2D3 +loB5hWp2Jp2VDCADjT7ueihlZGak2YPqmXTNbk19HOuNssWvFhtOyPNV6og4ETQd +Ea8/B6hPatJ0ES8q/HO3X8IVQwVs1n3aAr0im0/T+Xc= +""" + + def setUp(self): + self.asn1Spec = rfc5280.Certificate() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.ec_cert_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + spki_a = asn1Object['tbsCertificate']['subjectPublicKeyInfo']['algorithm'] + assert spki_a['algorithm'] == rfc3279.id_ecPublicKey + + spki_a_p, rest = der_decode(spki_a['parameters'], + asn1Spec=rfc3279.EcpkParameters()) + assert not rest + assert spki_a_p.prettyPrint() + assert der_encode(spki_a_p) == spki_a['parameters'] + assert spki_a_p['namedCurve'] == univ.ObjectIdentifier('1.3.132.0.34') + + def testOpenTypes(self): + asn1Spec = rfc5280.Certificate() + substrate = pem.readBase64fromText(self.ec_cert_pem_text) + asn1Object, rest = der_decode(substrate, + asn1Spec=self.asn1Spec, + decodeOpenTypes=True) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + spki_a = asn1Object['tbsCertificate']['subjectPublicKeyInfo']['algorithm'] + assert spki_a['algorithm'] == rfc3279.id_ecPublicKey + assert spki_a['parameters']['namedCurve'] == univ.ObjectIdentifier('1.3.132.0.34') + + +class DSACertificateTestCase(unittest.TestCase): + dsa_cert_pem_text = """\ +MIIDpjCCA0ygAwIBAgIUY8xt3l0B9nIPWSpjs0hDJUJZmCkwCwYJYIZIAWUDBAMC +MD8xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJWQTEQMA4GA1UEBxMHSGVybmRvbjER +MA8GA1UEChMIQm9ndXMgQ0EwHhcNMTkxMDIwMjAxMjMwWhcNMjAxMDE5MjAxMjMw +WjBwMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVkExEDAOBgNVBAcTB0hlcm5kb24x +EDAOBgNVBAoTB0V4YW1wbGUxDjAMBgNVBAsTBUFsaWNlMSAwHgYJKoZIhvcNAQkB +FhFhbGljZUBleGFtcGxlLmNvbTCCAbYwggErBgcqhkjOOAQBMIIBHgKBgQCLpR53 +xHfe+SiknAK/L9lm/ZO1109c9iYkriPIW/5MMlM+qc/tdRkKpG6ELIpfXTPtKCJm +zqqVIyTmAJryyE8Xw0Ie2mzYPU5ULvKmllQkjTsWgPGgQBkciZ0AW9ggD9VwZilg +4qh3iSO7T97hVQFnpCh6vm8pOH6UP/5kpr9ZJQIVANzdbztBJlJfqCB1t4h/NvSu +wCFvAoGAITP+jhYk9Rngd98l+5ccgauQ+cLEUBgNG2Wq56zBXQbLou6eKkQi7ecL +NiRmExq3IU3LOj426wSxL72Kw6FPyOEv3edIFkJJEHL4Z+ZJeVe//dzya0ddOJ7k +k6qNF2ic+viD/5Vm8yRyKiig2uHH/MgIesLdZnvbzvX+f/P0z50DgYQAAoGALAUl +jkOi1PxjjFVvhGfK95yIsrfbfcIEKUBaTs9NR2rbGWUeP+93paoXwP39X9wrJx2M +SWeHWhWKszNgoiyqYT0k4R9mem3WClotxOvB5fHfwIp2kQYvE7H0/TPdGhfUpHQG +YpyLQgT6L80meSKMFnu4VXGzOANhWDxu3JxiADCjgZQwgZEwCwYDVR0PBAQDAgeA +MEIGCWCGSAGG+EIBDQQ1FjNUaGlzIGNlcnRpZmljYXRlIGNhbm5vdCBiZSB0cnVz +dGVkIGZvciBhbnkgcHVycG9zZS4wHQYDVR0OBBYEFO37wHcauyc03rDc6cDRRsHz +gcK+MB8GA1UdIwQYMBaAFM1IZQGDsqYHWwb+I4EMxHPk0bU4MAsGCWCGSAFlAwQD +AgNHADBEAiBBRbfMzLi7+SVyO8SM3xxwUsMf/k1B+Nkvf1kBTfCfGwIgSAx/6mI+ +pNqdXqZZGESXy1MT1aBc4ynPGLFUr2r7cPY= +""" + + def setUp(self): + self.asn1Spec = rfc5280.Certificate() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.dsa_cert_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + spki_a = asn1Object['tbsCertificate']['subjectPublicKeyInfo']['algorithm'] + assert spki_a['algorithm'] == rfc3279.id_dsa + + spki_a_p, rest = der_decode(spki_a['parameters'], + asn1Spec=rfc3279.Dss_Parms()) + assert not rest + assert spki_a_p.prettyPrint() + assert der_encode(spki_a_p) == spki_a['parameters'] + q_value = 1260916123897116834511257683105158021801897369967 + assert spki_a_p['q'] == q_value + + sig_value, rest = der_decode(asn1Object['signature'].asOctets(), + asn1Spec=rfc3279.Dss_Sig_Value()) + assert not rest + assert sig_value.prettyPrint() + assert der_encode(sig_value) == asn1Object['signature'].asOctets() + assert sig_value['r'].hasValue() + assert sig_value['s'].hasValue() + + def testOpenTypes(self): + substrate = pem.readBase64fromText(self.dsa_cert_pem_text) + asn1Object, rest = der_decode(substrate, + asn1Spec=self.asn1Spec, + decodeOpenTypes=True) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + spki_a = asn1Object['tbsCertificate']['subjectPublicKeyInfo']['algorithm'] + assert spki_a['algorithm'] == rfc3279.id_dsa + q_value = 1260916123897116834511257683105158021801897369967 + assert spki_a['parameters']['q'] == q_value + + +class KEACertificateTestCase(unittest.TestCase): + kea_cert_pem_text = """\ +MIICizCCAjOgAwIBAgIUY8xt3l0B9nIPWSpjs0hDJUJZmCgwCQYHKoZIzjgEAzA/ +MQswCQYDVQQGEwJVUzELMAkGA1UECBMCVkExEDAOBgNVBAcTB0hlcm5kb24xETAP +BgNVBAoTCEJvZ3VzIENBMB4XDTE5MTAyMDIwMDkyMVoXDTIwMTAxOTIwMDkyMVow +cDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlZBMRAwDgYDVQQHEwdIZXJuZG9uMRAw +DgYDVQQKEwdFeGFtcGxlMQ4wDAYDVQQDEwVBbGljZTEgMB4GCSqGSIb3DQEJARYR +YWxpY2VAZXhhbXBsZS5jb20wgaAwFwYJYIZIAWUCAQEWBApc+PEn5ladbYizA4GE +AAKBgB9Lc2QcoSW0E9/VnQ2xGBtpYh9MaDUBzIixbN8rhDwh0BBesD2TwHjzBpDM +2PJ6DD1ZbBcz2M3vJaIKoZ8hA2EUtbbHX1BSnVfAdeqr5St5gfnuxSdloUjLQlWO +rOYfpFVEp6hJoKAZiYfiXz0fohNXn8+fiU5k214byxlCPlU0o4GUMIGRMAsGA1Ud +DwQEAwIDCDBCBglghkgBhvhCAQ0ENRYzVGhpcyBjZXJ0aWZpY2F0ZSBjYW5ub3Qg +YmUgdHJ1c3RlZCBmb3IgYW55IHB1cnBvc2UuMB0GA1UdDgQWBBSE49bkPB9sQm27 +Rs2jgAPMyY6UCDAfBgNVHSMEGDAWgBTNSGUBg7KmB1sG/iOBDMRz5NG1ODAJBgcq +hkjOOAQDA0cAMEQCIE9PWhUbnJVdNQcVYSc36BMZ+23uk2ITLsgSXtkScF6TAiAf +TPnJ5Wym0hv2fOpnPPsWTgqvLFYfX27GGTquuOd/6A== +""" + + def setUp(self): + self.asn1Spec = rfc5280.Certificate() + + def testDerCodec(self): + asn1Spec = rfc5280.Certificate() + substrate = pem.readBase64fromText(self.kea_cert_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + spki_a = asn1Object['tbsCertificate']['subjectPublicKeyInfo']['algorithm'] + assert spki_a['algorithm'] == rfc3279.id_keyExchangeAlgorithm + + spki_a_p, rest = der_decode(spki_a['parameters'], + asn1Spec=rfc3279.KEA_Parms_Id()) + assert not rest + assert spki_a_p.prettyPrint() + assert der_encode(spki_a_p) == spki_a['parameters'] + assert spki_a_p == univ.OctetString(hexValue='5cf8f127e6569d6d88b3') + + assert asn1Object['tbsCertificate']['signature']['algorithm'] == rfc3279.id_dsa_with_sha1 + assert asn1Object['signatureAlgorithm']['algorithm'] == rfc3279.id_dsa_with_sha1 + + sig_value, rest = der_decode(asn1Object['signature'].asOctets(), + asn1Spec=rfc3279.Dss_Sig_Value()) + assert not rest + assert sig_value.prettyPrint() + assert der_encode(sig_value) == asn1Object['signature'].asOctets() + assert sig_value['r'].hasValue() + assert sig_value['s'].hasValue() + + def testOpenTypes(self): + substrate = pem.readBase64fromText(self.kea_cert_pem_text) + asn1Object, rest = der_decode(substrate, + asn1Spec=self.asn1Spec, + decodeOpenTypes=True) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + spki_a = asn1Object['tbsCertificate']['subjectPublicKeyInfo']['algorithm'] + assert spki_a['algorithm'] == rfc3279.id_keyExchangeAlgorithm + assert spki_a['parameters'] == univ.OctetString(hexValue='5cf8f127e6569d6d88b3') + + assert asn1Object['tbsCertificate']['signature']['algorithm'] == rfc3279.id_dsa_with_sha1 + assert asn1Object['signatureAlgorithm']['algorithm'] == rfc3279.id_dsa_with_sha1 + + +class DHCertificateTestCase(unittest.TestCase): + dh_cert_pem_text = """\ +MIIEtDCCBFqgAwIBAgIUY8xt3l0B9nIPWSpjs0hDJUJZmCkwCwYJYIZIAWUDBAMC +MD8xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJWQTEQMA4GA1UEBxMHSGVybmRvbjER +MA8GA1UEChMIQm9ndXMgQ0EwHhcNMTkxMDIwMjAxMjMwWhcNMjAxMDE5MjAxMjMw +WjBwMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVkExEDAOBgNVBAcTB0hlcm5kb24x +EDAOBgNVBAoTB0V4YW1wbGUxDjAMBgNVBAsTBUFsaWNlMSAwHgYJKoZIhvcNAQkB +FhFhbGljZUBleGFtcGxlLmNvbTCCAsQwggI5BgcqhkjOPgIBMIICLAKCAQEAt9x/ +0iwGww3k19h+wbODVK1yqjFzEY2pyfXthHcn+nEw+DpURJ+iOhYPr68E3XO5sB48 +r5xTZhPN5+YejD3T8qhnDtiq4qrrSH7BOaEzqCDpHE2Bpoy3SodQ5Obaiu9Kx1ix +BRk/oRZUH+F+ATZmF0rPKrZGZOnmsh0IZm3dlmRR9FRGn0aJlZKXveqp+hZ97/r0 +cbSo6wdT47APfocgweZMvgWu1IQBs6FiunRgaeX3RyLr4fnkvCzUM7TmxpRJYtL6 +myAp007QvtgQ0AdEwVfNl3jQ0IIW7TtpXVxDDQaKZZe9yYrY4GV3etlYk8a4cpjN +rBxBCCTMASE4+iVtPQKCAQAg3m19vWc1TlHmkeqLwgvHN0Ufdyw5axWtc8qIJGZ1 +MezhyLyD4RU0VFCSocJCCe2k2kS2P2vQERZZYcn/nCYuiswCjOCbnwKozfaTZ3Fc +1KOCtb4EEcuk/th5XNhWCYJJ7Hasym8zuPaqh5TLcsHXp0/lQUiOV2uVHnAt503A +HY1v4PhlZ3G0CRZMenafU0Ky7a6zhrqFvWgtSdo+vN0S9xS/KJuTaWsYgOAt4r2I +K1uwuWuvA5L1Qrdj8pDzMLkdlyHU1Jgjzk0rNQDTbUkZX9CAi/xKUGZysjWfOn1F +HC1vJ1sbP9nTXpWRain1/6yatB2RxLTvWYyAq9IsL/8PAiEAkY8lGryvcZI/pxXt +XwSaXEL2d77GSGICMGZa1wOJtdEDgYQAAoGALAUljkOi1PxjjFVvhGfK95yIsrfb +fcIEKUBaTs9NR2rbGWUeP+93paoXwP39X9wrJx2MSWeHWhWKszNgoiyqYT0k4R9m +em3WClotxOvB5fHfwIp2kQYvE7H0/TPdGhfUpHQGYpyLQgT6L80meSKMFnu4VXGz +OANhWDxu3JxiADCjgZQwgZEwCwYDVR0PBAQDAgMIMEIGCWCGSAGG+EIBDQQ1FjNU +aGlzIGNlcnRpZmljYXRlIGNhbm5vdCBiZSB0cnVzdGVkIGZvciBhbnkgcHVycG9z +ZS4wHQYDVR0OBBYEFO37wHcauyc03rDc6cDRRsHzgcK+MB8GA1UdIwQYMBaAFM1I +ZQGDsqYHWwb+I4EMxHPk0bU4MAsGCWCGSAFlAwQDAgNHADBEAiB1LU0esRdHDvSj +kqAm+3viU2a+hl66sLrK5lYBOYqGYAIgWG7bDxqFVP6/stHfdbeMovLejquEl9tr +iPEBA+EDHjk= +""" + + def setUp(self): + self.asn1Spec = rfc5280.Certificate() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.dh_cert_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + spki_a = asn1Object['tbsCertificate']['subjectPublicKeyInfo']['algorithm'] + assert spki_a['algorithm'] == rfc3279.dhpublicnumber + + spki_a_p, rest = der_decode(spki_a['parameters'], + asn1Spec=rfc3279.DomainParameters()) + assert not rest + assert spki_a_p.prettyPrint() + assert der_encode(spki_a_p) == spki_a['parameters'] + q_value = 65838278260281264030127352144753816831178774189428428256716126077244217603537 + assert spki_a_p['q'] == q_value + + def testOpenTypes(self): + substrate = pem.readBase64fromText(self.dh_cert_pem_text) + asn1Object, rest = der_decode(substrate, + asn1Spec=self.asn1Spec, + decodeOpenTypes=True) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + spki_a = asn1Object['tbsCertificate']['subjectPublicKeyInfo']['algorithm'] + assert spki_a['algorithm'] == rfc3279.dhpublicnumber + q_value = 65838278260281264030127352144753816831178774189428428256716126077244217603537 + assert spki_a['parameters']['q'] == q_value + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) -- cgit v1.2.3 From e6aca617f808e2ae1ddcc0c1221a41eef73644b4 Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Tue, 22 Oct 2019 16:50:27 -0400 Subject: Add support for RFC 6486 (#90) --- CHANGES.txt | 1 + pyasn1_modules/rfc6486.py | 68 +++++++++++++++++++++++++ tests/__main__.py | 1 + tests/test_rfc6486.py | 123 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 193 insertions(+) create mode 100644 pyasn1_modules/rfc6486.py create mode 100644 tests/test_rfc6486.py diff --git a/CHANGES.txt b/CHANGES.txt index 2b942e6..006a913 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -10,6 +10,7 @@ Revision 0.2.8, released XX-XX-2019 - Added RFC6960 providing Online Certificate Status Protocol (OCSP) - Added RFC6955 providing Diffie-Hellman Proof-of-Possession Algorithms - Updated the handling of maps for use with openType for RFC 3279 +- Added RFC6486 providing RPKI Manifests Revision 0.2.7, released 09-10-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc6486.py b/pyasn1_modules/rfc6486.py new file mode 100644 index 0000000..31c936a --- /dev/null +++ b/pyasn1_modules/rfc6486.py @@ -0,0 +1,68 @@ +# +# 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 +# +# RPKI Manifests +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc6486.txt +# + +from pyasn1.type import char +from pyasn1.type import constraint +from pyasn1.type import namedtype +from pyasn1.type import tag +from pyasn1.type import useful +from pyasn1.type import univ + +from pyasn1_modules import rfc5652 + +MAX = float('inf') + + +id_smime = univ.ObjectIdentifier('1.2.840.113549.1.9.16') + +id_ct = id_smime + (1, ) + +id_ct_rpkiManifest = id_ct + (26, ) + + +class FileAndHash(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('file', char.IA5String()), + namedtype.NamedType('hash', univ.BitString()) + ) + + +class Manifest(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.DefaultedNamedType('version', + univ.Integer().subtype(explicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 0)).subtype(value=0)), + namedtype.NamedType('manifestNumber', + univ.Integer().subtype( + subtypeSpec=constraint.ValueRangeConstraint(0, MAX))), + namedtype.NamedType('thisUpdate', + useful.GeneralizedTime()), + namedtype.NamedType('nextUpdate', + useful.GeneralizedTime()), + namedtype.NamedType('fileHashAlg', + univ.ObjectIdentifier()), + namedtype.NamedType('fileList', + univ.SequenceOf(componentType=FileAndHash()).subtype( + subtypeSpec=constraint.ValueSizeConstraint(0, MAX))) + ) + + +# Map of Content Type OIDs to Content Types added to the +# ones that are in rfc5652.py + +_cmsContentTypesMapUpdate = { + id_ct_rpkiManifest: Manifest(), +} + +rfc5652.cmsContentTypesMap.update(_cmsContentTypesMapUpdate) diff --git a/tests/__main__.py b/tests/__main__.py index c852b56..48e674d 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -59,6 +59,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc6032.suite', 'tests.test_rfc6210.suite', 'tests.test_rfc6211.suite', + 'tests.test_rfc6486.suite', 'tests.test_rfc6955.suite', 'tests.test_rfc6960.suite', 'tests.test_rfc7030.suite', diff --git a/tests/test_rfc6486.py b/tests/test_rfc6486.py new file mode 100644 index 0000000..70e9160 --- /dev/null +++ b/tests/test_rfc6486.py @@ -0,0 +1,123 @@ +# +# 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 rfc5652 +from pyasn1_modules import rfc6486 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class SignedManifestTestCase(unittest.TestCase): + manifest_pem_text = """\ +MIIHVAYJKoZIhvcNAQcCoIIHRTCCB0ECAQMxDTALBglghkgBZQMEAgEwgYwGCyqGSIb3DQEJ +EAEaoH0EezB5AgIK5xgPMjAxMjEwMjMyMjI2MDNaGA8yMDEyMTAyNTIyMjYwM1oGCWCGSAFl +AwQCATBGMEQWH1pYU0dCREJrTDgyVEZHSHVFNFZPWXRKUC1FNC5jcmwDIQCzTdC3GsuONsRq +RFnYf8+AJ2NnCIgmnc3O8PyfGvn18aCCBO4wggTqMIID0qADAgECAgIK5zANBgkqhkiG9w0B +AQsFADATMREwDwYDVQQDEwhBOTE5OTg4NTAeFw0xMjEwMjMyMjI2MDNaFw0xMjEwMjUyMjI2 +MDNaMBgxFjAUBgNVBAMTDTUwODcxOTdjLTIwZjcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDEl4R4LiCs6zyR/IAeaRCfz0O0mXXAUKt8bmG6DXzaDYNG8dnBjbrsM1L05sb4 +2Ti4TyE1UXtwFFEwatsFQ2uRBn9gsKmDGOjW8TH1AYObmZW+hZlEN7OLSz2bmPLtxIMwiCq/ +vqmBJlMWPyCSym4iPnjzwWbJechqHSiTMOYGICF1QSW5xjJDAhRfeZG3nRY7TqfW8R2KJXeN +cKSYSGNKzv79B8GCswmwU8J8kcuryIiqb7WtcK2B6VBsROIQHGXM0UV4Zbnvv9m9Fl0SjvZJ +XyrzRjGzV2C00hM0f4jAplD9nJhAJ7nOTe8OnadrFABRga+Ge1HooeDQJGmTekLXAgMBAAGj +ggJBMIICPTAdBgNVHQ4EFgQUbcbOyNBHkRXXDaMq51jC7vOSHFUwHwYDVR0jBBgwFoAUZXSG +BDBkL82TFGHuE4VOYtJP+E4wDgYDVR0PAQH/BAQDAgeAMIGDBgNVHR8EfDB6MHigdqB0hnJy +c3luYzovL3Jwa2kuYXBuaWMubmV0L21lbWJlcl9yZXBvc2l0b3J5L0E5MTk5ODg1LzY1RkQ0 +M0FBNUJFRjExREZBQjYxQjNFNzU1QUZFN0NGL1pYU0dCREJrTDgyVEZHSHVFNFZPWXRKUC1F +NC5jcmwwfgYIKwYBBQUHAQEEcjBwMG4GCCsGAQUFBzAChmJyc3luYzovL3Jwa2kuYXBuaWMu +bmV0L3JlcG9zaXRvcnkvQTNDMzhBMjRENjAzMTFEQ0FCMDhGMzE5NzlCREJFMzkvWlhTR0JE +QmtMODJURkdIdUU0Vk9ZdEpQLUU0LmNlcjAYBgNVHSABAf8EDjAMMAoGCCsGAQUFBw4CMIGQ +BggrBgEFBQcBCwSBgzCBgDB+BggrBgEFBQcwC4ZycnN5bmM6Ly9ycGtpLmFwbmljLm5ldC9t +ZW1iZXJfcmVwb3NpdG9yeS9BOTE5OTg4NS82NUZENDNBQTVCRUYxMURGQUI2MUIzRTc1NUFG +RTdDRi9aWFNHQkRCa0w4MlRGR0h1RTRWT1l0SlAtRTQubWZ0MBUGCCsGAQUFBwEIAQH/BAYw +BKACBQAwIQYIKwYBBQUHAQcBAf8EEjAQMAYEAgABBQAwBgQCAAIFADANBgkqhkiG9w0BAQsF +AAOCAQEAyBl1J+ql1O3d6JiaQEG2UAjDSKHSMVau++QcB6/yd4RuWv2KpQxk1cp+awf4Ttoh +GYakbUZQl7lJaXzbluG5siRSv6AowEWxf99iLhDx+pE1htklRfmmTE9oFpKnITAYZAUjarNC +sYGCZ00vSwRu27OdpSQbZQ7WdyDAhyHS0Sun0pkImVSqPO11gqyKV9ZCwCJUa5U/zsWDMNrj +MSZl1I3VoPs2rx997rLoiQiMqwGeoqfl7snpsL9OR/CazPmepuq3SyZNWcCrUGcGRhRdGScj +Tm2EHne1GiRHapn46HWQ3am8jumEKv5u0gLT4Mi9CyZwkDyhotGTJZmdAmN7zzGCAaowggGm +AgEDgBRtxs7I0EeRFdcNoyrnWMLu85IcVTALBglghkgBZQMEAgGgazAaBgkqhkiG9w0BCQMx +DQYLKoZIhvcNAQkQARowHAYJKoZIhvcNAQkFMQ8XDTEyMTAyMzIyMjYwNFowLwYJKoZIhvcN +AQkEMSIEIIu2XV8dT+rqQy5Cbpm3Tv5I1dwkLK8n2GesMGOr6/pEMA0GCSqGSIb3DQEBAQUA +BIIBAFsd0zkl4dIHrqZts441T+w/5/ekymDLFwftk6W+Mi35Htjvm2IHOthnKHQsK5h6dnEh +6DfNfc6tACmzLnM+UG7ve+uAhfpA+CUJIoVhpQvDH7Ntql0cD1X3d9ng484jpkVoHhbUIYNR +TyxvV4DV5EBbLYpx2HYf6wWa8TCobxUXNtw53OVA24ceavS+KvuDa0JQPFpbYUCS0UPMt/Im +mtKrWTmRUr8sYWdIQn+SStUh8iAR5rmSVr+Pe7aFbe2ju2FPf08gnIjH/SdCrJuFK8q7Z5MT +C9ijmXiajracUe+7eCluqgXRE8yRtnscWoA/9fVFz1lPwgEeNHLoaK7Sqew= +""" + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.manifest_pem_text) + + layers = { } + layers.update(rfc5652.cmsContentTypesMap) + + getNextLayer = { + rfc5652.id_ct_contentInfo: lambda x: x['contentType'], + rfc5652.id_signedData: lambda x: x['encapContentInfo']['eContentType'], + rfc6486.id_ct_rpkiManifest: lambda x: None + } + + getNextSubstrate = { + rfc5652.id_ct_contentInfo: lambda x: x['content'], + rfc5652.id_signedData: lambda x: x['encapContentInfo']['eContent'], + rfc6486.id_ct_rpkiManifest: lambda x: None + } + + next_layer = rfc5652.id_ct_contentInfo + while next_layer: + asn1Object, rest = der_decode(substrate, asn1Spec=layers[next_layer]) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + substrate = getNextSubstrate[next_layer](asn1Object) + next_layer = getNextLayer[next_layer](asn1Object) + + assert asn1Object['version'] == 0 + for f in asn1Object['fileList']: + assert f['file'] == 'ZXSGBDBkL82TFGHuE4VOYtJP-E4.crl' + + def testOpenTypes(self): + substrate = pem.readBase64fromText(self.manifest_pem_text) + asn1Object, rest = der_decode(substrate, + asn1Spec=rfc5652.ContentInfo(), + decodeOpenTypes=True) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + oid = asn1Object['content']['encapContentInfo']['eContentType'] + substrate = asn1Object['content']['encapContentInfo']['eContent'] + assert oid in rfc5652.cmsContentTypesMap.keys() + asn1Object, rest = der_decode(substrate, + asn1Spec=rfc5652.cmsContentTypesMap[oid], + decodeOpenTypes=True) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + assert asn1Object['version'] == 0 + for f in asn1Object['fileList']: + assert f['file'] == 'ZXSGBDBkL82TFGHuE4VOYtJP-E4.crl' + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + import sys + + result = unittest.TextTestRunner(verbosity=2).run(suite) + sys.exit(not result.wasSuccessful()) -- cgit v1.2.3 From a3c47259f60631905af419159deeb43452fe8959 Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Wed, 23 Oct 2019 12:19:49 -0400 Subject: Add support for RFC 6487 (#91) --- CHANGES.txt | 1 + pyasn1_modules/rfc6487.py | 22 ++++++++++++ tests/__main__.py | 1 + tests/test_rfc6487.py | 90 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 114 insertions(+) create mode 100644 pyasn1_modules/rfc6487.py create mode 100644 tests/test_rfc6487.py diff --git a/CHANGES.txt b/CHANGES.txt index 006a913..4d9074d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -11,6 +11,7 @@ Revision 0.2.8, released XX-XX-2019 - Added RFC6955 providing Diffie-Hellman Proof-of-Possession Algorithms - Updated the handling of maps for use with openType for RFC 3279 - Added RFC6486 providing RPKI Manifests +- Added RFC6487 providing Profile for X.509 PKIX Resource Certificates Revision 0.2.7, released 09-10-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc6487.py b/pyasn1_modules/rfc6487.py new file mode 100644 index 0000000..d8c2f87 --- /dev/null +++ b/pyasn1_modules/rfc6487.py @@ -0,0 +1,22 @@ +# +# 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 +# +# Profile for X.509 PKIX Resource Certificates +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc6487.txt +# + +from pyasn1.type import univ + +id_pkix = univ.ObjectIdentifier('1.3.6.1.5.5.7') + +id_ad = id_pkix + (48, ) + +id_ad_rpkiManifest = id_ad + (10, ) +id_ad_signedObject = id_ad + (11, ) diff --git a/tests/__main__.py b/tests/__main__.py index 48e674d..b89faae 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -60,6 +60,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc6210.suite', 'tests.test_rfc6211.suite', 'tests.test_rfc6486.suite', + 'tests.test_rfc6487.suite', 'tests.test_rfc6955.suite', 'tests.test_rfc6960.suite', 'tests.test_rfc7030.suite', diff --git a/tests/test_rfc6487.py b/tests/test_rfc6487.py new file mode 100644 index 0000000..87445ba --- /dev/null +++ b/tests/test_rfc6487.py @@ -0,0 +1,90 @@ +# +# 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 rfc5280 +from pyasn1_modules import rfc6487 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class RPKICertificateTestCase(unittest.TestCase): + rpki_cert_pem_text = """\ +MIIGCTCCBPGgAwIBAgICKJgwDQYJKoZIhvcNAQELBQAwRjERMA8GA1UEAxMIQTkwREM1QkUx +MTAvBgNVBAUTKDBDRkNFNzc4NTdGQ0YwMUYzOUQ5OUE2MkI0QUE2MkU2MTU5RTc2RjgwHhcN +MTkwODA2MDQwMzIyWhcNMjAxMDMxMDAwMDAwWjBGMREwDwYDVQQDEwhBOTFEMTY5MTExMC8G +A1UEBRMoREMwNEFGMTk4Qzk3RjI1ODJGMTVBRERFRUU3QzY4MjYxMUNBREE1MTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMksR6bPbZFpxlXID/2dhYFuS11agb6ACDUFJpII +41uw65tFIPT+Y4laccnYRcWPWMTvHLyj0ggU+bc2zJCTYfmGD/GW/Q3WW0A3niBCdXDfkrp2 +DXvSTASJ5+wtVb+AE74C4Mr3UiMOXhJre1rRd5Lq7o6+TEKbVkmUrmTlbsz2Vs2F4//t5sCr +WjAVP9D5jUBGH2MInbleBP1Bwf+kIxD16OKftRb/vGLzk1UhLsbq22GGE0vZ2hnJP3CbyXkN +dLBraErzvyCnqYF7/yA0JL0KWRDwr7a9y37s8O3xOxhA/dL8hLZXllzJmoxvxHmq8D+5CjHv +2/EmH8ODGm2aAzcCAwEAAaOCAv8wggL7MB0GA1UdDgQWBBTcBK8ZjJfyWC8Vrd7ufGgmEcra +UTAfBgNVHSMEGDAWgBQM/Od4V/zwHznZmmK0qmLmFZ52+DAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zBzBgNVHR8EbDBqMGigZqBkhmJyc3luYzovL3Jwa2kuYXBuaWMubmV0 +L3JlcG9zaXRvcnkvQjMyMkE1RjQxRDY2MTFFMkEzRjI3RjdDNzJGRDFGRjIvRFB6bmVGZjg4 +Qjg1MlpwaXRLcGk1aFdlZHZnLmNybDB+BggrBgEFBQcBAQRyMHAwbgYIKwYBBQUHMAKGYnJz +eW5jOi8vcnBraS5hcG5pYy5uZXQvcmVwb3NpdG9yeS85ODA2NTJFMEI3N0UxMUU3QTk2QTM5 +NTIxQTRGNEZCNC9EUHpuZUZmODhCODUyWnBpdEtwaTVoV2VkdmcuY2VyMEoGA1UdIAEB/wRA +MD4wPAYIKwYBBQUHDgIwMDAuBggrBgEFBQcCARYiaHR0cHM6Ly93d3cuYXBuaWMubmV0L1JQ +S0kvQ1BTLnBkZjCCASgGCCsGAQUFBwELBIIBGjCCARYwXwYIKwYBBQUHMAWGU3JzeW5jOi8v +cnBraS5hcG5pYy5uZXQvbWVtYmVyX3JlcG9zaXRvcnkvQTkxRDE2OTEvNTBDNjkyOTI5RDI0 +MTFFNzg2MUEyMjZCQzRGOUFFMDIvMH4GCCsGAQUFBzAKhnJyc3luYzovL3Jwa2kuYXBuaWMu +bmV0L21lbWJlcl9yZXBvc2l0b3J5L0E5MUQxNjkxLzUwQzY5MjkyOUQyNDExRTc4NjFBMjI2 +QkM0RjlBRTAyLzNBU3ZHWXlYOGxndkZhM2U3bnhvSmhISzJsRS5tZnQwMwYIKwYBBQUHMA2G +J2h0dHBzOi8vcnJkcC5hcG5pYy5uZXQvbm90aWZpY2F0aW9uLnhtbDArBggrBgEFBQcBBwEB +/wQcMBowGAQCAAEwEgMEAdQI5gMEAdQI/gMEAdRcZjANBgkqhkiG9w0BAQsFAAOCAQEAGvJ+ +s7VgIZk8LDSz6uvsyX80KzZgaqMF7sMsqln0eo5KiGGBHjwvZuiDf46xbNseWW2nwAHmjLda +osCbcTGVu0JzFYBdkimgyHiq2l8yEchh5BUXr8x4CQIxwGEZEOlEp5mRa/AfHVEfDeMm7mob +eiCfyTC8q8KH9Tb/rY192kBe+n9MuRyn7TkimV5eYMdwWMyT/VSBCQzzfJ0r+S9o0rBYWH9k +HDFd3u1ztO8WGjH/LOehoO30xsm52kbxZjc4SJWubgBgxTMIWyjPHbKqCF44NwYev/6eFcOC ++KTEQ/hydcURm3YtX7EZLDtksWB2me576J8opeLsbNeNgzfJpg== +""" + + def setUp(self): + self.asn1Spec = rfc5280.Certificate() + + def testDerCodec(self): + access_methods = [ + rfc6487.id_ad_rpkiManifest, + rfc6487.id_ad_signedObject, + ] + + substrate = pem.readBase64fromText(self.rpki_cert_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + count = 0 + for extn in asn1Object['tbsCertificate']['extensions']: + if extn['extnID'] == rfc5280.id_pe_subjectInfoAccess: + extnValue, rest = der_decode(extn['extnValue'], + asn1Spec=rfc5280.SubjectInfoAccessSyntax()) + for ad in extnValue: + if ad['accessMethod'] in access_methods: + uri = ad['accessLocation']['uniformResourceIdentifier'] + assert 'rpki.apnic.net' in uri + count += 1 + + assert count == 1 + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + import sys + + result = unittest.TextTestRunner(verbosity=2).run(suite) + sys.exit(not result.wasSuccessful()) -- cgit v1.2.3 From 03eb527270dea65311ef9a00cd7b351590769263 Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Wed, 23 Oct 2019 15:41:56 -0400 Subject: Improbe test for RFC 6487 (#92) --- tests/test_rfc6487.py | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/tests/test_rfc6487.py b/tests/test_rfc6487.py index 87445ba..f9f30a2 100644 --- a/tests/test_rfc6487.py +++ b/tests/test_rfc6487.py @@ -19,7 +19,7 @@ except ImportError: import unittest -class RPKICertificateTestCase(unittest.TestCase): +class CertificateWithManifestTestCase(unittest.TestCase): rpki_cert_pem_text = """\ MIIGCTCCBPGgAwIBAgICKJgwDQYJKoZIhvcNAQELBQAwRjERMA8GA1UEAxMIQTkwREM1QkUx MTAvBgNVBAUTKDBDRkNFNzc4NTdGQ0YwMUYzOUQ5OUE2MkI0QUE2MkU2MTU5RTc2RjgwHhcN @@ -80,6 +80,64 @@ HDFd3u1ztO8WGjH/LOehoO30xsm52kbxZjc4SJWubgBgxTMIWyjPHbKqCF44NwYev/6eFcOC assert count == 1 +class CertificateWithSignedObjectTestCase(unittest.TestCase): + rpki_cert_pem_text = """\ +MIIEuDCCA6CgAwIBAgICBhgwDQYJKoZIhvcNAQELBQAwMzExMC8GA1UEAxMoNmQ2 +ZmJmYTk3NTNkYjhkODQ2NDMzZGI1MzUxZDlhOWVjMDdjOTZiZDAeFw0xOTA4MjAw +MDQ5MjlaFw0yMDA3MDEwMDAwMDBaMDMxMTAvBgNVBAMTKDVCODNERDg3REU5QUM3 +QzZFMzRCODc3REY1MDFBMkIxMjMwQTgxQjQwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQCXJw4ElLYkHnpRkLE8djruXIJn6uij0hIDM/18ma0c48HKlEPw +8jGB8kSSvLpimY3zLvqyLaD0pBNulovT3Ep37mPkskvXIUwLTThjBEujG7R4zU5g +RV2q+QhRjPLXiyER5QnEnHrIEke6tnioEPnC4i02Z6JdDKZrn9jSLPlet2OB5/0+ +0DqLnYPZ1LZrId9YVDeIyBDRFxbzQ6L5K2stua5fWqhX1vnbZKDbXSY6d+u5zVwn +adxnRP989EiKk/MJ4Reu7YEdtpsM3sd7prXkAcJjPokdvL7hy+BOY8ESgaIhIBj2 +Kqu4G35HKBbUdwFekBikitmiVJlIvvVYXku/AgMBAAGjggHUMIIB0DAdBgNVHQ4E +FgQUW4Pdh96ax8bjS4d99QGisSMKgbQwHwYDVR0jBBgwFoAUbW+/qXU9uNhGQz21 +NR2ansB8lr0wGAYDVR0gAQH/BA4wDDAKBggrBgEFBQcOAjBQBgNVHR8ESTBHMEWg +Q6BBhj9yc3luYzovL2NhLnJnLm5ldC9ycGtpL1JHbmV0LU9VL2JXLV9xWFU5dU5o +R1F6MjFOUjJhbnNCOGxyMC5jcmwwZAYIKwYBBQUHAQEEWDBWMFQGCCsGAQUFBzAC +hkhyc3luYzovL3Jwa2kucmlwZS5uZXQvcmVwb3NpdG9yeS9ERUZBVUxUL2JXLV9x +WFU5dU5oR1F6MjFOUjJhbnNCOGxyMC5jZXIwDgYDVR0PAQH/BAQDAgeAMIGKBggr +BgEFBQcBCwR+MHwwSwYIKwYBBQUHMAuGP3JzeW5jOi8vY2EucmcubmV0L3Jwa2kv +UkduZXQtT1UvVzRQZGg5NmF4OGJqUzRkOTlRR2lzU01LZ2JRLnJvYTAtBggrBgEF +BQcwDYYhaHR0cHM6Ly9jYS5yZy5uZXQvcnJkcC9ub3RpZnkueG1sMB8GCCsGAQUF +BwEHAQH/BBAwDjAMBAIAATAGAwQAkxwtMA0GCSqGSIb3DQEBCwUAA4IBAQCoYaCd +17R3o7xul5BWgk8SXItdIDoDb7zxVqs/gnzl9i5gdDd0IWIy4gGW32EjsTUXsi+G +1gyv7aWYFQNlR7kvBgfHyPPp2rkFIj9/KK1VygG3FFMaO1JBDB8UOU+tRbV6xGcf +IYCk5bH6H9BtkPm2kiczVdjCFIB5krMy+DMf3x1F7/G+5f+ZmUG3b93GfUmzgxw9 +IjlQMyt35h3rgOK6EjpOJgUA1jUWNTpPsR/xzA0HaDlbW38ue02SNluztrsXxJSr +8XwXhHPUzmlqg89Mb5iem3WZ5lkbr6lteO+ZocYtLPyOHhNmXWgop764K4JQaf46 +WYtY4rWNeHcfgNTz +""" + + def setUp(self): + self.asn1Spec = rfc5280.Certificate() + + def testDerCodec(self): + access_methods = [ + rfc6487.id_ad_rpkiManifest, + rfc6487.id_ad_signedObject, + ] + + substrate = pem.readBase64fromText(self.rpki_cert_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + count = 0 + for extn in asn1Object['tbsCertificate']['extensions']: + if extn['extnID'] == rfc5280.id_pe_subjectInfoAccess: + extnValue, rest = der_decode(extn['extnValue'], + asn1Spec=rfc5280.SubjectInfoAccessSyntax()) + for ad in extnValue: + if ad['accessMethod'] in access_methods: + uri = ad['accessLocation']['uniformResourceIdentifier'] + assert 'ca.rg.net' in uri + count += 1 + + assert count == 1 + suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) -- cgit v1.2.3 From 13343c7a40a06f20bdf59bbc1ff2b83f6a26db97 Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Thu, 24 Oct 2019 14:50:08 -0400 Subject: Add support for RFC 6170 (#93) --- CHANGES.txt | 2 ++ pyasn1_modules/rfc3709.py | 5 ++++- pyasn1_modules/rfc6170.py | 17 +++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 pyasn1_modules/rfc6170.py diff --git a/CHANGES.txt b/CHANGES.txt index 4d9074d..1bc631f 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -12,6 +12,8 @@ Revision 0.2.8, released XX-XX-2019 - Updated the handling of maps for use with openType for RFC 3279 - Added RFC6486 providing RPKI Manifests - Added RFC6487 providing Profile for X.509 PKIX Resource Certificates +- Added RFC6170 providing Certificate Image in the Internet X.509 Public + Key Infrastructure, and import the object identifier into RFC3709. Revision 0.2.7, released 09-10-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc3709.py b/pyasn1_modules/rfc3709.py index f26ba33..aa1d5b6 100644 --- a/pyasn1_modules/rfc3709.py +++ b/pyasn1_modules/rfc3709.py @@ -21,6 +21,7 @@ from pyasn1.type import tag from pyasn1.type import univ from pyasn1_modules import rfc5280 +from pyasn1_modules import rfc6170 MAX = float('inf') @@ -162,6 +163,8 @@ id_logo_background = univ.ObjectIdentifier('1.3.6.1.5.5.7.20.2') id_logo_loyalty = univ.ObjectIdentifier('1.3.6.1.5.5.7.20.1') +id_logo_certImage = rfc6170.id_logo_certImage + class OtherLogotypeInfo(univ.Sequence): pass @@ -201,4 +204,4 @@ _certificateExtensionsMapUpdate = { id_pe_logotype: LogotypeExtn(), } -rfc5280.certificateExtensionsMap.update(_certificateExtensionsMapUpdate) \ No newline at end of file +rfc5280.certificateExtensionsMap.update(_certificateExtensionsMapUpdate) diff --git a/pyasn1_modules/rfc6170.py b/pyasn1_modules/rfc6170.py new file mode 100644 index 0000000..e287616 --- /dev/null +++ b/pyasn1_modules/rfc6170.py @@ -0,0 +1,17 @@ +# +# 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 +# +# Certificate Image in the Internet X.509 Public Key Infrastructure +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc6170.txt +# + +from pyasn1.type import univ + +id_logo_certImage = univ.ObjectIdentifier('1.3.6.1.5.5.7.20.3') -- cgit v1.2.3 From a8c0d3aa252e275af4ebd20217a5ee9704fce0e2 Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Fri, 25 Oct 2019 16:32:21 -0400 Subject: Add support for RFC 6187, RFC 6482, and RFC 6664 (#94) --- CHANGES.txt | 3 + pyasn1_modules/rfc6187.py | 22 +++++++ pyasn1_modules/rfc6482.py | 74 +++++++++++++++++++++++ pyasn1_modules/rfc6664.py | 147 ++++++++++++++++++++++++++++++++++++++++++++++ tests/__main__.py | 3 + tests/test_rfc6187.py | 73 +++++++++++++++++++++++ tests/test_rfc6482.py | 119 +++++++++++++++++++++++++++++++++++++ tests/test_rfc6664.py | 109 ++++++++++++++++++++++++++++++++++ 8 files changed, 550 insertions(+) create mode 100644 pyasn1_modules/rfc6187.py create mode 100644 pyasn1_modules/rfc6482.py create mode 100644 pyasn1_modules/rfc6664.py create mode 100644 tests/test_rfc6187.py create mode 100644 tests/test_rfc6482.py create mode 100644 tests/test_rfc6664.py diff --git a/CHANGES.txt b/CHANGES.txt index 1bc631f..633bfb1 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -14,6 +14,9 @@ Revision 0.2.8, released XX-XX-2019 - Added RFC6487 providing Profile for X.509 PKIX Resource Certificates - Added RFC6170 providing Certificate Image in the Internet X.509 Public Key Infrastructure, and import the object identifier into RFC3709. +- Added RFC6187 providing Certificates for Secure Shell Authentication +- Added RFC6482 providing RPKI Route Origin Authorizations (ROAs) +- Added RFC6664 providing S/MIME Capabilities for Public Keys Revision 0.2.7, released 09-10-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc6187.py b/pyasn1_modules/rfc6187.py new file mode 100644 index 0000000..4be0054 --- /dev/null +++ b/pyasn1_modules/rfc6187.py @@ -0,0 +1,22 @@ +# +# 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 +# +# X.509v3 Certificates for Secure Shell Authentication +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc6187.txt +# + +from pyasn1.type import univ + +id_pkix = univ.ObjectIdentifier('1.3.6.1.5.5.7') + +id_kp = id_pkix + (3, ) + +id_kp_secureShellClient = id_kp + (21, ) +id_kp_secureShellServer = id_kp + (22, ) diff --git a/pyasn1_modules/rfc6482.py b/pyasn1_modules/rfc6482.py new file mode 100644 index 0000000..d213a46 --- /dev/null +++ b/pyasn1_modules/rfc6482.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 +# +# RPKI Route Origin Authorizations (ROAs) +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc6482.txt +# https://www.rfc-editor.org/errata/eid5881 +# + +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 + +MAX = float('inf') + + +id_ct_routeOriginAuthz = univ.ObjectIdentifier('1.2.840.113549.1.9.16.1.24') + + +class ASID(univ.Integer): + pass + + +class IPAddress(univ.BitString): + pass + + +class ROAIPAddress(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('address', IPAddress()), + namedtype.OptionalNamedType('maxLength', univ.Integer()) + ) + + +class ROAIPAddressFamily(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('addressFamily', + univ.OctetString().subtype( + subtypeSpec=constraint.ValueSizeConstraint(2, 3))), + namedtype.NamedType('addresses', + univ.SequenceOf(componentType=ROAIPAddress()).subtype( + subtypeSpec=constraint.ValueSizeConstraint(1, MAX))) + ) + + +class RouteOriginAttestation(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.DefaultedNamedType('version', + univ.Integer().subtype(explicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 0)).subtype(value=0)), + namedtype.NamedType('asID', ASID()), + namedtype.NamedType('ipAddrBlocks', + univ.SequenceOf(componentType=ROAIPAddressFamily()).subtype( + subtypeSpec=constraint.ValueSizeConstraint(1, MAX))) + ) + + +# Map of Content Type OIDs to Content Types added to the +# ones that are in rfc5652.py + +_cmsContentTypesMapUpdate = { + id_ct_routeOriginAuthz: RouteOriginAttestation(), +} + +rfc5652.cmsContentTypesMap.update(_cmsContentTypesMapUpdate) diff --git a/pyasn1_modules/rfc6664.py b/pyasn1_modules/rfc6664.py new file mode 100644 index 0000000..41629d8 --- /dev/null +++ b/pyasn1_modules/rfc6664.py @@ -0,0 +1,147 @@ +# +# This file is part of pyasn1-modules software. +# +# Created by Russ Housley with some assistance from asn1ate v.0.6.0. +# +# Copyright (c) 2019, Vigil Security, LLC +# License: http://snmplabs.com/pyasn1/license.html +# +# S/MIME Capabilities for Public Key Definitions +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc6664.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 rfc5280 +from pyasn1_modules import rfc5751 +from pyasn1_modules import rfc5480 +from pyasn1_modules import rfc4055 +from pyasn1_modules import rfc3279 + +MAX = float('inf') + + +# Imports from RFC 5280 + +AlgorithmIdentifier = rfc5280.AlgorithmIdentifier + + +# Imports from RFC 3279 + +dhpublicnumber = rfc3279.dhpublicnumber + +Dss_Parms = rfc3279.Dss_Parms + +id_dsa = rfc3279.id_dsa + +id_ecPublicKey = rfc3279.id_ecPublicKey + +rsaEncryption = rfc3279.rsaEncryption + + +# Imports from RFC 4055 + +id_mgf1 = rfc4055.id_mgf1 + +id_RSAES_OAEP = rfc4055.id_RSAES_OAEP + +id_RSASSA_PSS = rfc4055.id_RSASSA_PSS + + +# Imports from RFC 5480 + +ECParameters = rfc5480.ECParameters + +id_ecDH = rfc5480.id_ecDH + +id_ecMQV = rfc5480.id_ecMQV + + +# RSA + +class RSAKeySize(univ.Integer): + # suggested values are 1024, 2048, 3072, 4096, 7680, 8192, and 15360; + # however, the integer value is not limited to these suggestions + pass + + +class RSAKeyCapabilities(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('minKeySize', RSAKeySize()), + namedtype.OptionalNamedType('maxKeySize', RSAKeySize()) + ) + + +class RsaSsa_Pss_sig_caps(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('hashAlg', AlgorithmIdentifier()), + namedtype.OptionalNamedType('maskAlg', AlgorithmIdentifier()), + namedtype.DefaultedNamedType('trailerField', univ.Integer().subtype(value=1)) + ) + + +# Diffie-Hellman and DSA + +class DSAKeySize(univ.Integer): + subtypeSpec = constraint.SingleValueConstraint(1024, 2048, 3072, 7680, 15360) + + +class DSAKeyCapabilities(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('keySizes', univ.Sequence(componentType=namedtype.NamedTypes( + namedtype.NamedType('minKeySize', + DSAKeySize()), + namedtype.OptionalNamedType('maxKeySize', + DSAKeySize()), + namedtype.OptionalNamedType('maxSizeP', + univ.Integer().subtype(explicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.OptionalNamedType('maxSizeQ', + univ.Integer().subtype(explicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 2))), + namedtype.OptionalNamedType('maxSizeG', + univ.Integer().subtype(explicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 3))) + )).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))), + namedtype.NamedType('keyParams', + Dss_Parms().subtype(explicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatConstructed, 1))) + ) + + +# Elliptic Curve + +class EC_SMimeCaps(univ.SequenceOf): + componentType = ECParameters() + subtypeSpec=constraint.ValueSizeConstraint(1, MAX) + + +# Update the SMIMECapabilities Attribute Map in rfc5751.py +# +# The map can either include an entry for scap-sa-rsaSSA-PSS or +# scap-pk-rsaSSA-PSS, but not both. One is associated with the +# public key and the other is associated with the signature +# algorithm; however, they use the same OID. If you need the +# other one in your application, copy the map into a local dict, +# adjust as needed, and pass the local dict to the decoder with +# openTypes=your_local_map. + +_smimeCapabilityMapUpdate = { + rsaEncryption: RSAKeyCapabilities(), + id_RSASSA_PSS: RSAKeyCapabilities(), + # id_RSASSA_PSS: RsaSsa_Pss_sig_caps(), + id_RSAES_OAEP: RSAKeyCapabilities(), + id_dsa: DSAKeyCapabilities(), + dhpublicnumber: DSAKeyCapabilities(), + id_ecPublicKey: EC_SMimeCaps(), + id_ecDH: EC_SMimeCaps(), + id_ecMQV: EC_SMimeCaps(), + id_mgf1: AlgorithmIdentifier(), +} + +rfc5751.smimeCapabilityMap.update(_smimeCapabilityMapUpdate) diff --git a/tests/__main__.py b/tests/__main__.py index b89faae..d7f9102 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -57,10 +57,13 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc6019.suite', 'tests.test_rfc6031.suite', 'tests.test_rfc6032.suite', + 'tests.test_rfc6187.suite', 'tests.test_rfc6210.suite', 'tests.test_rfc6211.suite', + 'tests.test_rfc6482.suite', 'tests.test_rfc6486.suite', 'tests.test_rfc6487.suite', + 'tests.test_rfc6664.suite', 'tests.test_rfc6955.suite', 'tests.test_rfc6960.suite', 'tests.test_rfc7030.suite', diff --git a/tests/test_rfc6187.py b/tests/test_rfc6187.py new file mode 100644 index 0000000..e821d77 --- /dev/null +++ b/tests/test_rfc6187.py @@ -0,0 +1,73 @@ +# +# 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 rfc5280 +from pyasn1_modules import rfc6187 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class SSHClientCertificateTestCase(unittest.TestCase): + cert_pem_text = """\ +MIICkDCCAhegAwIBAgIJAKWzVCgbsG5BMAoGCCqGSM49BAMDMD8xCzAJBgNVBAYT +AlVTMQswCQYDVQQIDAJWQTEQMA4GA1UEBwwHSGVybmRvbjERMA8GA1UECgwIQm9n +dXMgQ0EwHhcNMTkxMDI0MTgyNjA3WhcNMjAxMDIzMTgyNjA3WjB0MQswCQYDVQQG +EwJVUzELMAkGA1UECBMCVkExEDAOBgNVBAcTB0hlcm5kb24xEDAOBgNVBAoTB0V4 +YW1wbGUxEDAOBgNVBAMTB0NoYXJsaWUxIjAgBgkqhkiG9w0BCQEWE2NoYXJsaWVA +ZXhhbXBsZS5jb20wdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARfr1XPl5S0A/BwTOm4 +/rO7mGVt2Tmfr3yvYnfN/ggMvyS3RiIXSsdzcAwzeqc907Jp7Dggab0PpaOKDOxD +WoK0g6B8+kC/VMsU23mfShlb9et8qcR3A8gdU6g8uvSMahWjgakwgaYwCwYDVR0P +BAQDAgeAMB0GA1UdDgQWBBQfwm5u0GoxiDcjhDt33UJYlvMPFTAfBgNVHSMEGDAW +gBTyNds0BNqlVfK9aQOZsGLs4hUIwTATBgNVHSUEDDAKBggrBgEFBQcDFTBCBglg +hkgBhvhCAQ0ENRYzVGhpcyBjZXJ0aWZpY2F0ZSBjYW5ub3QgYmUgdHJ1c3RlZCBm +b3IgYW55IHB1cnBvc2UuMAoGCCqGSM49BAMDA2cAMGQCMGEme38A3k8q4RGSEs2D +ThQQOQz3TBJrIW8zr92S8e8BNPkRcQDR+C72TEhL/qoPCQIwGpGaC4ERiUypETkC +voNP0ODFhhlpFo6lwVHd8Gu+6hShC2PKdAfs4QFDS9ZKgQeZ +""" + + def setUp(self): + self.asn1Spec = rfc5280.Certificate() + + def testDerCodec(self): + ssh_eku_oids = [ + rfc6187.id_kp_secureShellClient, + rfc6187.id_kp_secureShellServer, + ] + + substrate = pem.readBase64fromText(self.cert_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + count = 0 + for extn in asn1Object['tbsCertificate']['extensions']: + if extn['extnID'] == rfc5280.id_ce_extKeyUsage: + extnValue, rest = der_decode(extn['extnValue'], + asn1Spec=rfc5280.ExtKeyUsageSyntax()) + for oid in extnValue: + if oid in ssh_eku_oids: + count += 1 + + assert count == 1 + + +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_rfc6482.py b/tests/test_rfc6482.py new file mode 100644 index 0000000..731cbd9 --- /dev/null +++ b/tests/test_rfc6482.py @@ -0,0 +1,119 @@ +# +# 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 rfc5652 +from pyasn1_modules import rfc6482 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class RPKIROATestCase(unittest.TestCase): + roa_pem_text = """\ +MIIGvwYJKoZIhvcNAQcCoIIGsDCCBqwCAQMxDTALBglghkgBZQMEAgEwKgYLKoZIhvcNAQkQ +ARigGwQZMBcCAwDj+zAQMA4EAgABMAgwBgMEAJMcLaCCBLwwggS4MIIDoKADAgECAgIGGDAN +BgkqhkiG9w0BAQsFADAzMTEwLwYDVQQDEyg2ZDZmYmZhOTc1M2RiOGQ4NDY0MzNkYjUzNTFk +OWE5ZWMwN2M5NmJkMB4XDTE5MDgyMDAwNDkyOVoXDTIwMDcwMTAwMDAwMFowMzExMC8GA1UE +AxMoNUI4M0REODdERTlBQzdDNkUzNEI4NzdERjUwMUEyQjEyMzBBODFCNDCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJcnDgSUtiQeelGQsTx2Ou5cgmfq6KPSEgMz/XyZrRzj +wcqUQ/DyMYHyRJK8umKZjfMu+rItoPSkE26Wi9PcSnfuY+SyS9chTAtNOGMES6MbtHjNTmBF +Xar5CFGM8teLIRHlCcScesgSR7q2eKgQ+cLiLTZnol0Mpmuf2NIs+V63Y4Hn/T7QOoudg9nU +tmsh31hUN4jIENEXFvNDovkray25rl9aqFfW+dtkoNtdJjp367nNXCdp3GdE/3z0SIqT8wnh +F67tgR22mwzex3umteQBwmM+iR28vuHL4E5jwRKBoiEgGPYqq7gbfkcoFtR3AV6QGKSK2aJU +mUi+9VheS78CAwEAAaOCAdQwggHQMB0GA1UdDgQWBBRbg92H3prHxuNLh331AaKxIwqBtDAf +BgNVHSMEGDAWgBRtb7+pdT242EZDPbU1HZqewHyWvTAYBgNVHSABAf8EDjAMMAoGCCsGAQUF +Bw4CMFAGA1UdHwRJMEcwRaBDoEGGP3JzeW5jOi8vY2EucmcubmV0L3Jwa2kvUkduZXQtT1Uv +YlctX3FYVTl1TmhHUXoyMU5SMmFuc0I4bHIwLmNybDBkBggrBgEFBQcBAQRYMFYwVAYIKwYB +BQUHMAKGSHJzeW5jOi8vcnBraS5yaXBlLm5ldC9yZXBvc2l0b3J5L0RFRkFVTFQvYlctX3FY +VTl1TmhHUXoyMU5SMmFuc0I4bHIwLmNlcjAOBgNVHQ8BAf8EBAMCB4AwgYoGCCsGAQUFBwEL +BH4wfDBLBggrBgEFBQcwC4Y/cnN5bmM6Ly9jYS5yZy5uZXQvcnBraS9SR25ldC1PVS9XNFBk +aDk2YXg4YmpTNGQ5OVFHaXNTTUtnYlEucm9hMC0GCCsGAQUFBzANhiFodHRwczovL2NhLnJn +Lm5ldC9ycmRwL25vdGlmeS54bWwwHwYIKwYBBQUHAQcBAf8EEDAOMAwEAgABMAYDBACTHC0w +DQYJKoZIhvcNAQELBQADggEBAKhhoJ3XtHejvG6XkFaCTxJci10gOgNvvPFWqz+CfOX2LmB0 +N3QhYjLiAZbfYSOxNReyL4bWDK/tpZgVA2VHuS8GB8fI8+nauQUiP38orVXKAbcUUxo7UkEM +HxQ5T61FtXrEZx8hgKTlsfof0G2Q+baSJzNV2MIUgHmSszL4Mx/fHUXv8b7l/5mZQbdv3cZ9 +SbODHD0iOVAzK3fmHeuA4roSOk4mBQDWNRY1Ok+xH/HMDQdoOVtbfy57TZI2W7O2uxfElKvx +fBeEc9TOaWqDz0xvmJ6bdZnmWRuvqW1475mhxi0s/I4eE2ZdaCinvrgrglBp/jpZi1jitY14 +dx+A1PMxggGqMIIBpgIBA4AUW4Pdh96ax8bjS4d99QGisSMKgbQwCwYJYIZIAWUDBAIBoGsw +GgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEYMBwGCSqGSIb3DQEJBTEPFw0xOTA4MjAwMDQ5 +MjlaMC8GCSqGSIb3DQEJBDEiBCCfuHnOmhF2iBF3JXMOnoZCJzmE+Tcf8b+zObvDUpUddzAN +BgkqhkiG9w0BAQEFAASCAQBDlJIMKCqWsFV/tQj/XvpSJUxJybG+zwjrUKm4yTKv8QEGOzOD +aIL6irSOhhXeax6Lw0P2J7x+L3jGW1we1qWslumEDTr9kTE+kN/6rZuptUhwdrXcu3p9G6gJ +mAUQtzqe2jRN1T3eSBfz1CNU3C7+jSHXOc+4Tea5mKiVddsjotYHXX0PbSCS/ZZ1yzdeES0o +KWhXhW9ogS0bwtXWVTrciSekaRpp2n/pqcVEDxWg/5NpPiDlPNrRL/9eTEHFp940RAUfhbBh +pbC2J02N0KgxUJxIJnGnpZ7rXKpG4jMiTVry7XB9bnFxCvZGBdjQW1Hagrfpl2TiVxQFvJWl +IzU1 +""" + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.roa_pem_text) + + layers = { } + layers.update(rfc5652.cmsContentTypesMap) + + getNextLayer = { + rfc5652.id_ct_contentInfo: lambda x: x['contentType'], + rfc5652.id_signedData: lambda x: x['encapContentInfo']['eContentType'], + rfc6482.id_ct_routeOriginAuthz: lambda x: None + } + + getNextSubstrate = { + rfc5652.id_ct_contentInfo: lambda x: x['content'], + rfc5652.id_signedData: lambda x: x['encapContentInfo']['eContent'], + rfc6482.id_ct_routeOriginAuthz: lambda x: None + } + + next_layer = rfc5652.id_ct_contentInfo + while next_layer: + asn1Object, rest = der_decode(substrate, asn1Spec=layers[next_layer]) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + substrate = getNextSubstrate[next_layer](asn1Object) + next_layer = getNextLayer[next_layer](asn1Object) + + assert asn1Object['version'] == 0 + assert asn1Object['asID'] == 58363 + + def testOpenTypes(self): + substrate = pem.readBase64fromText(self.roa_pem_text) + asn1Object, rest = der_decode(substrate, + asn1Spec=rfc5652.ContentInfo(), + decodeOpenTypes=True) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + oid = asn1Object['content']['encapContentInfo']['eContentType'] + substrate = asn1Object['content']['encapContentInfo']['eContent'] + assert oid in rfc5652.cmsContentTypesMap.keys() + asn1Object, rest = der_decode(substrate, + asn1Spec=rfc5652.cmsContentTypesMap[oid], + decodeOpenTypes=True) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + assert asn1Object['version'] == 0 + assert asn1Object['asID'] == 58363 + + +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_rfc6664.py b/tests/test_rfc6664.py new file mode 100644 index 0000000..38d0a54 --- /dev/null +++ b/tests/test_rfc6664.py @@ -0,0 +1,109 @@ +# +# 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 rfc5480 +from pyasn1_modules import rfc5652 +from pyasn1_modules import rfc5751 +from pyasn1_modules import rfc6664 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class SMIMECapabilitiesTestCase(unittest.TestCase): + smime_capabilities_pem_text = """\ +MIICOjAJBgUrDgMCGgUAMA0GCWCGSAFlAwQCBAUAMA0GCWCGSAFlAwQCAQUAMA0G +CWCGSAFlAwQCAgUAMA0GCWCGSAFlAwQCAwUAMBUGCSqGSIb3DQEBATAIAgIEAAIC +EAAwFQYJKoZIhvcNAQEHMAgCAgQAAgIQADAVBgkqhkiG9w0BAQowCAICBAACAhAA +MBUGByqGSM44BAGgCjAIAgIEAAICDAAwggEvBgcqhkjOPgIBoYIBIjCCAR4CgYEA +i6Ued8R33vkopJwCvy/ZZv2TtddPXPYmJK4jyFv+TDJTPqnP7XUZCqRuhCyKX10z +7SgiZs6qlSMk5gCa8shPF8NCHtps2D1OVC7yppZUJI07FoDxoEAZHImdAFvYIA/V +cGYpYOKod4kju0/e4VUBZ6Qoer5vKTh+lD/+ZKa/WSUCFQDc3W87QSZSX6ggdbeI +fzb0rsAhbwKBgCEz/o4WJPUZ4HffJfuXHIGrkPnCxFAYDRtlqueswV0Gy6LunipE +Iu3nCzYkZhMatyFNyzo+NusEsS+9isOhT8jhL93nSBZCSRBy+GfmSXlXv/3c8mtH +XTie5JOqjRdonPr4g/+VZvMkcioooNrhx/zICHrC3WZ72871/n/z9M+dMCMGByqG +SM49AgEwGAYIKoZIzj0DAQcGBSuBBAAiBgUrgQQAIzAhBgUrgQQBDTAYBggqhkjO +PQMBBwYFK4EEACIGBSuBBAAjMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAgEFAA== +""" + + def setUp(self): + self.asn1Spec = rfc5751.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 + + count = 0 + for cap in asn1Object: + if cap['capabilityID'] in rfc5751.smimeCapabilityMap.keys(): + substrate = cap['parameters'] + cap_p, rest = der_decode (substrate, + asn1Spec=rfc5751.smimeCapabilityMap[cap['capabilityID']]) + assert not rest + assert cap_p.prettyPrint() + assert der_encode(cap_p) == substrate + count += 1 + + assert count == 8 + + def testOpenTypes(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 + + parameterValue = { + rfc6664.rsaEncryption: lambda x: x['maxKeySize'], + rfc6664.id_RSAES_OAEP: lambda x: x['maxKeySize'], + rfc6664.id_RSASSA_PSS: lambda x: x['minKeySize'], + rfc6664.id_dsa: lambda x: x['keySizes']['maxKeySize'], + rfc6664.dhpublicnumber: lambda x: x['keyParams']['q'] % 1023, + rfc6664.id_ecPublicKey: lambda x: x[0]['namedCurve'], + rfc6664.id_ecMQV: lambda x: x[1]['namedCurve'], + } + + expectedValue = { + rfc6664.rsaEncryption: 4096, + rfc6664.id_RSAES_OAEP: 4096, + rfc6664.id_RSASSA_PSS: 1024, + rfc6664.id_dsa: 3072, + rfc6664.dhpublicnumber: 257, + rfc6664.id_ecPublicKey: rfc5480.secp256r1, + rfc6664.id_ecMQV: rfc5480.secp384r1, + } + + count = 0 + for cap in asn1Object: + if cap['capabilityID'] in parameterValue.keys(): + pValue = parameterValue[cap['capabilityID']](cap['parameters']) + eValue = expectedValue[cap['capabilityID']] + assert pValue == eValue + count += 1 + + assert count == 7 + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + import sys + + result = unittest.TextTestRunner(verbosity=2).run(suite) + sys.exit(not result.wasSuccessful()) -- cgit v1.2.3 From 6f9a139a806df8589bb36d25805f5cdd31c8fa54 Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Wed, 30 Oct 2019 14:21:22 -0400 Subject: Add support for RFC 6120 (#95) --- CHANGES.txt | 2 + pyasn1_modules/rfc6120.py | 43 ++++++++++++++++++ tests/__main__.py | 1 + tests/test_rfc6120.py | 109 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 155 insertions(+) create mode 100644 pyasn1_modules/rfc6120.py create mode 100644 tests/test_rfc6120.py diff --git a/CHANGES.txt b/CHANGES.txt index 633bfb1..ab45ec6 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -17,6 +17,8 @@ Revision 0.2.8, released XX-XX-2019 - Added RFC6187 providing Certificates for Secure Shell Authentication - Added RFC6482 providing RPKI Route Origin Authorizations (ROAs) - Added RFC6664 providing S/MIME Capabilities for Public Keys +- Added RFC6120 providing Extensible Messaging and Presence Protocol + names in certificates Revision 0.2.7, released 09-10-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc6120.py b/pyasn1_modules/rfc6120.py new file mode 100644 index 0000000..ab25620 --- /dev/null +++ b/pyasn1_modules/rfc6120.py @@ -0,0 +1,43 @@ +# +# 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 +# +# Extensible Messaging and Presence Protocol (XMPP) +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc6120.txt +# + +from pyasn1.type import char +from pyasn1.type import univ + +from pyasn1_modules import rfc5280 + +MAX = float('inf') + + +# XmppAddr Identifier Type as specified in Section 13.7.1.4. of RFC 6120 + +id_pkix = rfc5280.id_pkix + +id_on = id_pkix + (8, ) + +id_on_xmppAddr = id_on + (5, ) + + +class XmppAddr(char.UTF8String): + pass + + +# Map of Other Name OIDs to Other Name is added to the +# ones that are in rfc5280.py + +_anotherNameMapUpdate = { + id_on_xmppAddr: XmppAddr(), +} + +rfc5280.anotherNameMap.update(_anotherNameMapUpdate) diff --git a/tests/__main__.py b/tests/__main__.py index d7f9102..9bc9f0a 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -57,6 +57,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc6019.suite', 'tests.test_rfc6031.suite', 'tests.test_rfc6032.suite', + 'tests.test_rfc6120.suite', 'tests.test_rfc6187.suite', 'tests.test_rfc6210.suite', 'tests.test_rfc6211.suite', diff --git a/tests/test_rfc6120.py b/tests/test_rfc6120.py new file mode 100644 index 0000000..820f7a3 --- /dev/null +++ b/tests/test_rfc6120.py @@ -0,0 +1,109 @@ +# +# 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 rfc6120 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class XMPPCertificateTestCase(unittest.TestCase): + xmpp_server_cert_pem_text = """\ +MIIC6DCCAm+gAwIBAgIJAKWzVCgbsG5DMAoGCCqGSM49BAMDMD8xCzAJBgNVBAYT +AlVTMQswCQYDVQQIDAJWQTEQMA4GA1UEBwwHSGVybmRvbjERMA8GA1UECgwIQm9n +dXMgQ0EwHhcNMTkxMDI0MjMxNjA0WhcNMjAxMDIzMjMxNjA0WjBNMQswCQYDVQQG +EwJVUzELMAkGA1UECBMCVkExEDAOBgNVBAcTB0hlcm5kb24xHzAdBgNVBAoTFkV4 +YW1wbGUgUHJvZHVjdHMsIEluYy4wdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQZzQlk +03nJRPF6+w1NxFELmQ5vJTjTRz3eu03CRtahK4Wnwd4GwbDe8NVHAEG2qTzBXFDu +p6RZugsBdf9GcEZHG42rThYYOzIYzVFnI7tQgA+nTWSWZN6eoU/EXcknhgijggEn +MIIBIzAdBgNVHQ4EFgQUkQpUMYcbUesEn5buI03POFnktJgwHwYDVR0jBBgwFoAU +8jXbNATapVXyvWkDmbBi7OIVCMEwCwYDVR0PBAQDAgeAMIGPBgNVHREEgYcwgYSg +KQYIKwYBBQUHCAegHRYbX3htcHAtY2xpZW50LmltLmV4YW1wbGUuY29toCkGCCsG +AQUFBwgHoB0WG194bXBwLXNlcnZlci5pbS5leGFtcGxlLmNvbaAcBggrBgEFBQcI +BaAQDA5pbS5leGFtcGxlLmNvbYIOaW0uZXhhbXBsZS5jb20wQgYJYIZIAYb4QgEN +BDUWM1RoaXMgY2VydGlmaWNhdGUgY2Fubm90IGJlIHRydXN0ZWQgZm9yIGFueSBw +dXJwb3NlLjAKBggqhkjOPQQDAwNnADBkAjAEo4mhDGC6/R39HyNgzLseNAp36qBH +yQJ/AWsBojN0av8akeVv9IuM45yqLKdiCzcCMDCjh1lFnCvurahwp5D1j9pAZMsg +nOzhcMpnHs2U/eN0lHl/JNgnbftl6Dvnt59xdA== +""" + + def setUp(self): + self.asn1Spec = rfc5280.Certificate() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.xmpp_server_cert_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + count = 0 + for extn in asn1Object['tbsCertificate']['extensions']: + if extn['extnID'] == rfc5280.id_ce_subjectAltName: + extnValue, rest = der_decode(extn['extnValue'], + asn1Spec=rfc5280.SubjectAltName()) + assert not rest + assert extnValue.prettyPrint() + assert der_encode(extnValue) == extn['extnValue'] + for gn in extnValue: + if gn['otherName'].hasValue(): + gn_on = gn['otherName'] + if gn_on['type-id'] == rfc6120.id_on_xmppAddr: + assert gn_on['type-id'] in rfc5280.anotherNameMap.keys() + spec = rfc5280.anotherNameMap[gn['otherName']['type-id']] + on, rest = der_decode(gn_on['value'], asn1Spec=spec) + assert not rest + assert on.prettyPrint() + assert der_encode(on) == gn_on['value'] + assert on == u'im.example.com' + count += 1 + + assert count == 1 + + def testOpenTypes(self): + substrate = pem.readBase64fromText(self.xmpp_server_cert_pem_text) + asn1Object, rest = der_decode(substrate, + asn1Spec=self.asn1Spec, + decodeOpenTypes=True) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + count = 0 + for extn in asn1Object['tbsCertificate']['extensions']: + if extn['extnID'] == rfc5280.id_ce_subjectAltName: + extnValue, rest = der_decode(extn['extnValue'], + asn1Spec=rfc5280.SubjectAltName(), decodeOpenTypes=True) + assert not rest + assert extnValue.prettyPrint() + assert der_encode(extnValue) == extn['extnValue'] + for gn in extnValue: + if gn['otherName'].hasValue(): + if gn['otherName']['type-id'] == rfc6120.id_on_xmppAddr: + assert gn['otherName']['value'] == u'im.example.com' + count += 1 + + assert count == 1 + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + import sys + + result = unittest.TextTestRunner(verbosity=2).run(suite) + sys.exit(not result.wasSuccessful()) -- cgit v1.2.3 From ca9a0541d1fda5581a07f36b4fd46a0161f67f7a Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Wed, 30 Oct 2019 18:16:26 -0400 Subject: Add support for RFC 4985 (#96) --- CHANGES.txt | 2 + pyasn1_modules/rfc4985.py | 49 +++++++++++++++++++++ tests/__main__.py | 1 + tests/test_rfc4985.py | 109 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 161 insertions(+) create mode 100644 pyasn1_modules/rfc4985.py create mode 100644 tests/test_rfc4985.py diff --git a/CHANGES.txt b/CHANGES.txt index ab45ec6..487c0b8 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -19,6 +19,8 @@ Revision 0.2.8, released XX-XX-2019 - Added RFC6664 providing S/MIME Capabilities for Public Keys - Added RFC6120 providing Extensible Messaging and Presence Protocol names in certificates +- Added RFC4985 providing Subject Alternative Name for expression of + service names in certificates Revision 0.2.7, released 09-10-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc4985.py b/pyasn1_modules/rfc4985.py new file mode 100644 index 0000000..318e412 --- /dev/null +++ b/pyasn1_modules/rfc4985.py @@ -0,0 +1,49 @@ +# +# 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 +# +# Expression of Service Names in X.509 Certificates +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc4985.txt +# + +from pyasn1.type import char +from pyasn1.type import constraint +from pyasn1.type import univ + +from pyasn1_modules import rfc5280 + +MAX = float('inf') + + +# As specified in Appendix A.2 of RFC 4985 + +id_pkix = rfc5280.id_pkix + +id_on = id_pkix + (8, ) + +id_on_dnsSRV = id_on + (7, ) + + +class SRVName(char.IA5String): + subtypeSpec = constraint.ValueSizeConstraint(1, MAX) + + +srvName = rfc5280.AnotherName() +srvName['type-id'] = id_on_dnsSRV +srvName['value'] = SRVName() + + +# Map of Other Name OIDs to Other Name is added to the +# ones that are in rfc5280.py + +_anotherNameMapUpdate = { + id_on_dnsSRV: SRVName(), +} + +rfc5280.anotherNameMap.update(_anotherNameMapUpdate) diff --git a/tests/__main__.py b/tests/__main__.py index 9bc9f0a..27e279d 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -38,6 +38,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc4108.suite', 'tests.test_rfc4210.suite', 'tests.test_rfc4211.suite', + 'tests.test_rfc4985.suite', 'tests.test_rfc5035.suite', 'tests.test_rfc5083.suite', 'tests.test_rfc5084.suite', diff --git a/tests/test_rfc4985.py b/tests/test_rfc4985.py new file mode 100644 index 0000000..132b12b --- /dev/null +++ b/tests/test_rfc4985.py @@ -0,0 +1,109 @@ +# +# 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 rfc4985 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class XMPPCertificateTestCase(unittest.TestCase): + xmpp_server_cert_pem_text = """\ +MIIC6DCCAm+gAwIBAgIJAKWzVCgbsG5DMAoGCCqGSM49BAMDMD8xCzAJBgNVBAYT +AlVTMQswCQYDVQQIDAJWQTEQMA4GA1UEBwwHSGVybmRvbjERMA8GA1UECgwIQm9n +dXMgQ0EwHhcNMTkxMDI0MjMxNjA0WhcNMjAxMDIzMjMxNjA0WjBNMQswCQYDVQQG +EwJVUzELMAkGA1UECBMCVkExEDAOBgNVBAcTB0hlcm5kb24xHzAdBgNVBAoTFkV4 +YW1wbGUgUHJvZHVjdHMsIEluYy4wdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQZzQlk +03nJRPF6+w1NxFELmQ5vJTjTRz3eu03CRtahK4Wnwd4GwbDe8NVHAEG2qTzBXFDu +p6RZugsBdf9GcEZHG42rThYYOzIYzVFnI7tQgA+nTWSWZN6eoU/EXcknhgijggEn +MIIBIzAdBgNVHQ4EFgQUkQpUMYcbUesEn5buI03POFnktJgwHwYDVR0jBBgwFoAU +8jXbNATapVXyvWkDmbBi7OIVCMEwCwYDVR0PBAQDAgeAMIGPBgNVHREEgYcwgYSg +KQYIKwYBBQUHCAegHRYbX3htcHAtY2xpZW50LmltLmV4YW1wbGUuY29toCkGCCsG +AQUFBwgHoB0WG194bXBwLXNlcnZlci5pbS5leGFtcGxlLmNvbaAcBggrBgEFBQcI +BaAQDA5pbS5leGFtcGxlLmNvbYIOaW0uZXhhbXBsZS5jb20wQgYJYIZIAYb4QgEN +BDUWM1RoaXMgY2VydGlmaWNhdGUgY2Fubm90IGJlIHRydXN0ZWQgZm9yIGFueSBw +dXJwb3NlLjAKBggqhkjOPQQDAwNnADBkAjAEo4mhDGC6/R39HyNgzLseNAp36qBH +yQJ/AWsBojN0av8akeVv9IuM45yqLKdiCzcCMDCjh1lFnCvurahwp5D1j9pAZMsg +nOzhcMpnHs2U/eN0lHl/JNgnbftl6Dvnt59xdA== +""" + + def setUp(self): + self.asn1Spec = rfc5280.Certificate() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.xmpp_server_cert_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + count = 0 + for extn in asn1Object['tbsCertificate']['extensions']: + if extn['extnID'] == rfc5280.id_ce_subjectAltName: + extnValue, rest = der_decode(extn['extnValue'], + asn1Spec=rfc5280.SubjectAltName()) + assert not rest + assert extnValue.prettyPrint() + assert der_encode(extnValue) == extn['extnValue'] + for gn in extnValue: + if gn['otherName'].hasValue(): + gn_on = gn['otherName'] + if gn_on['type-id'] == rfc4985.id_on_dnsSRV: + assert gn_on['type-id'] in rfc5280.anotherNameMap.keys() + spec = rfc5280.anotherNameMap[gn['otherName']['type-id']] + on, rest = der_decode(gn_on['value'], asn1Spec=spec) + assert not rest + assert on.prettyPrint() + assert der_encode(on) == gn_on['value'] + assert 'im.example.com' in on + count += 1 + + assert count == 2 + + def testOpenTypes(self): + substrate = pem.readBase64fromText(self.xmpp_server_cert_pem_text) + asn1Object, rest = der_decode(substrate, + asn1Spec=self.asn1Spec, + decodeOpenTypes=True) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + count = 0 + for extn in asn1Object['tbsCertificate']['extensions']: + if extn['extnID'] == rfc5280.id_ce_subjectAltName: + extnValue, rest = der_decode(extn['extnValue'], + asn1Spec=rfc5280.SubjectAltName(), decodeOpenTypes=True) + assert not rest + assert extnValue.prettyPrint() + assert der_encode(extnValue) == extn['extnValue'] + for gn in extnValue: + if gn['otherName'].hasValue(): + if gn['otherName']['type-id'] == rfc4985.id_on_dnsSRV: + assert 'im.example.com' in gn['otherName']['value'] + count += 1 + + assert count == 2 + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + import sys + + result = unittest.TextTestRunner(verbosity=2).run(suite) + sys.exit(not result.wasSuccessful()) -- cgit v1.2.3 From 2e6acd120f89a18945b5ae49c9710f9cc90c1bda Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Sat, 2 Nov 2019 04:37:20 -0400 Subject: Add support for RFC 5924 (#97) --- CHANGES.txt | 2 ++ pyasn1_modules/rfc5924.py | 19 ++++++++++++ tests/__main__.py | 1 + tests/test_rfc5924.py | 73 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+) create mode 100644 pyasn1_modules/rfc5924.py create mode 100644 tests/test_rfc5924.py diff --git a/CHANGES.txt b/CHANGES.txt index 487c0b8..c898fa5 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -21,6 +21,8 @@ Revision 0.2.8, released XX-XX-2019 names in certificates - Added RFC4985 providing Subject Alternative Name for expression of service names in certificates +- Added RFC5924 providing Extended Key Usage for Session Initiation + Protocol (SIP) in X.509 certificates Revision 0.2.7, released 09-10-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc5924.py b/pyasn1_modules/rfc5924.py new file mode 100644 index 0000000..4358e4f --- /dev/null +++ b/pyasn1_modules/rfc5924.py @@ -0,0 +1,19 @@ +# +# 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 +# +# Extended Key Usage (EKU) for Session Initiation Protocol (SIP) +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc5924.txt +# + +from pyasn1.type import univ + +id_kp = univ.ObjectIdentifier('1.3.6.1.5.5.7.3') + +id_kp_sipDomain = id_kp + (20, ) diff --git a/tests/__main__.py b/tests/__main__.py index 27e279d..2d0337c 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -50,6 +50,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc5751.suite', 'tests.test_rfc5914.suite', 'tests.test_rfc5915.suite', + 'tests.test_rfc5924.suite', 'tests.test_rfc5934.suite', 'tests.test_rfc5940.suite', 'tests.test_rfc5958.suite', diff --git a/tests/test_rfc5924.py b/tests/test_rfc5924.py new file mode 100644 index 0000000..ca623fa --- /dev/null +++ b/tests/test_rfc5924.py @@ -0,0 +1,73 @@ +# +# 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 rfc5924 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class SIPDomainCertTestCase(unittest.TestCase): + cert_pem_text = """\ +MIICiTCCAg+gAwIBAgIJAKWzVCgbsG5EMAoGCCqGSM49BAMDMD8xCzAJBgNVBAYT +AlVTMQswCQYDVQQIDAJWQTEQMA4GA1UEBwwHSGVybmRvbjERMA8GA1UECgwIQm9n +dXMgQ0EwHhcNMTkxMDMwMjEwMDM0WhcNMjAxMDI5MjEwMDM0WjBsMQswCQYDVQQG +EwJVUzELMAkGA1UECBMCVkExEDAOBgNVBAcTB0hlcm5kb24xEDAOBgNVBAoTB0V4 +YW1wbGUxEjAQBgNVBAsTCVNJUCBQcm94eTEYMBYGA1UEAxMPc2lwLmV4YW1wbGUu +Y29tMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEcY3ckttSa6z3CfOFwZvPmZY8C9Ml +D1XOydz00+Vqifh1lydhDuulHrJaQ+QgVjG1TzlTAssD9GeABit/M98DPS/IC3wi +TsTMSyQ9/Oz4hKAw7x7lYEvufvycsZ7pJGRso4GpMIGmMEIGCWCGSAGG+EIBDQQ1 +FjNUaGlzIGNlcnRpZmljYXRlIGNhbm5vdCBiZSB0cnVzdGVkIGZvciBhbnkgcHVy +cG9zZS4wHQYDVR0OBBYEFEcJ8iFWmJOl3Hg/44UFgFWNbe7FMB8GA1UdIwQYMBaA +FPI12zQE2qVV8r1pA5mwYuziFQjBMAsGA1UdDwQEAwIHgDATBgNVHSUEDDAKBggr +BgEFBQcDFDAKBggqhkjOPQQDAwNoADBlAjAXEPPNyXBUj40dzy+ZOqafuM3/6Fy6 +bkgiIObcQImra96X10fe6qacanrbu4uU6d8CMQCQ+BCjCnOP4dBbNC3vB0WypxLo +UwZ6TjS0Rfr+dRvlyilVjP+hPVwbyb7ZOSZR6zk= +""" + + def setUp(self): + self.asn1Spec = rfc5280.Certificate() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.cert_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + found_kp_sipDomain = False + for extn in asn1Object['tbsCertificate']['extensions']: + if extn['extnID'] == rfc5280.id_ce_extKeyUsage: + assert extn['extnID'] in rfc5280.certificateExtensionsMap.keys() + ev, rest = der_decode(extn['extnValue'], + asn1Spec=rfc5280.certificateExtensionsMap[extn['extnID']]) + assert not rest + assert ev.prettyPrint() + assert der_encode(ev) == extn['extnValue'] + assert rfc5924.id_kp_sipDomain in ev + found_kp_sipDomain = True + + assert found_kp_sipDomain + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + import sys + + result = unittest.TextTestRunner(verbosity=2).run(suite) + sys.exit(not result.wasSuccessful()) -- cgit v1.2.3 From a99e2ecd831fbbea1f2869b68d708fb5ddf60fd7 Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Sun, 3 Nov 2019 16:43:39 -0500 Subject: Add support for RFC 5916 (#98) --- CHANGES.txt | 1 + pyasn1_modules/rfc5916.py | 35 +++++++++++++++ tests/__main__.py | 1 + tests/test_rfc5916.py | 110 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 147 insertions(+) create mode 100644 pyasn1_modules/rfc5916.py create mode 100644 tests/test_rfc5916.py diff --git a/CHANGES.txt b/CHANGES.txt index c898fa5..9b38f43 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -23,6 +23,7 @@ Revision 0.2.8, released XX-XX-2019 service names in certificates - Added RFC5924 providing Extended Key Usage for Session Initiation Protocol (SIP) in X.509 certificates +- Added RFC5916 providing Device Owner Attribute Revision 0.2.7, released 09-10-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc5916.py b/pyasn1_modules/rfc5916.py new file mode 100644 index 0000000..ac23c86 --- /dev/null +++ b/pyasn1_modules/rfc5916.py @@ -0,0 +1,35 @@ +# +# 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 +# +# Device Owner Attribute +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc5916.txt +# + +from pyasn1.type import univ + +from pyasn1_modules import rfc5280 + + +# Device Owner Attribute + +id_deviceOwner = univ.ObjectIdentifier((2, 16, 840, 1, 101, 2, 1, 5, 69)) + +at_deviceOwner = rfc5280.Attribute() +at_deviceOwner['type'] = id_deviceOwner +at_deviceOwner['values'][0] = univ.ObjectIdentifier() + + +# Add to the map of Attribute Type OIDs to Attributes in rfc5280.py. + +_certificateAttributesMapUpdate = { + id_deviceOwner: univ.ObjectIdentifier(), +} + +rfc5280.certificateAttributesMap.update(_certificateAttributesMapUpdate) diff --git a/tests/__main__.py b/tests/__main__.py index 2d0337c..9b75c65 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -50,6 +50,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc5751.suite', 'tests.test_rfc5914.suite', 'tests.test_rfc5915.suite', + 'tests.test_rfc5916.suite', 'tests.test_rfc5924.suite', 'tests.test_rfc5934.suite', 'tests.test_rfc5940.suite', diff --git a/tests/test_rfc5916.py b/tests/test_rfc5916.py new file mode 100644 index 0000000..2e7ba7f --- /dev/null +++ b/tests/test_rfc5916.py @@ -0,0 +1,110 @@ +# +# 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 rfc5916 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class DeviceCertTestCase(unittest.TestCase): + cert_pem_text = """\ +MIICpzCCAiygAwIBAgIJAKWzVCgbsG5FMAoGCCqGSM49BAMDMD8xCzAJBgNVBAYT +AlVTMQswCQYDVQQIDAJWQTEQMA4GA1UEBwwHSGVybmRvbjERMA8GA1UECgwIQm9n +dXMgQ0EwHhcNMTkxMDMxMTQwMDE1WhcNMjAxMDMwMTQwMDE1WjB4MQswCQYDVQQG +EwJVUzELMAkGA1UECBMCVkExEDAOBgNVBAcTB0hlcm5kb24xEDAOBgNVBAoTB0V4 +YW1wbGUxGjAYBgNVBAsTEURldmljZSBPcGVyYXRpb25zMRwwGgYDVQQDExNleDEy +MzQ1LmV4YW1wbGUuY29tMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE7Lje3glS2qYl +5x6N9TOlD4CbnzfFeJQfbDaCa3vexEiwE0apuAP+4L5fqOsYeZC970iNW+z3PdUs +GzkKDC2cCVy8nIxQ3mWhNQDvavT3iz5OGSwa1GjSXRFbGn2x9QjNo4G6MIG3MEIG +CWCGSAGG+EIBDQQ1FjNUaGlzIGNlcnRpZmljYXRlIGNhbm5vdCBiZSB0cnVzdGVk +IGZvciBhbnkgcHVycG9zZS4wHQYDVR0OBBYEFPTQN1kXEM5Rd4hNvQL5HyA+o2No +MB8GA1UdIwQYMBaAFPI12zQE2qVV8r1pA5mwYuziFQjBMAsGA1UdDwQEAwIHgDAk +BgNVHQkEHTAbMBkGCWCGSAFlAgEFRTEMBgorBgEEAYGsYDAYMAoGCCqGSM49BAMD +A2kAMGYCMQCt6AceOEIwXFKFHIV8+wTK/vgs7ZYSA6jhXUpzNtzZw1xh9NxVUhmx +pogu5Q9Vp28CMQC5YVF8dShC1tk9YImRftiVl8C6pbj//1K/+MwmR6nRk/WU+hKl ++Qsc5Goi6At471s= +""" + + def setUp(self): + self.asn1Spec = rfc5280.Certificate() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.cert_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + found_dev_owner = False + der_dev_own_oid = der_encode(univ.ObjectIdentifier('1.3.6.1.4.1.22112.48.24')) + + for extn in asn1Object['tbsCertificate']['extensions']: + if extn['extnID'] == rfc5280.id_ce_subjectDirectoryAttributes: + assert extn['extnID'] in rfc5280.certificateExtensionsMap.keys() + ev, rest = der_decode(extn['extnValue'], + asn1Spec=rfc5280.certificateExtensionsMap[extn['extnID']]) + assert not rest + assert ev.prettyPrint() + assert der_encode(ev) == extn['extnValue'] + + for attr in ev: + if attr['type'] == rfc5916.id_deviceOwner: + assert attr['values'][0] == der_dev_own_oid + found_dev_owner = True + + assert found_dev_owner + + def testOpenTypes(self): + substrate = pem.readBase64fromText(self.cert_pem_text) + asn1Object, rest = der_decode(substrate, + asn1Spec=self.asn1Spec, + decodeOpenTypes=True) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + found_dev_owner = False + dev_own_oid = univ.ObjectIdentifier('1.3.6.1.4.1.22112.48.24') + + for extn in asn1Object['tbsCertificate']['extensions']: + if extn['extnID'] == rfc5280.id_ce_subjectDirectoryAttributes: + assert extn['extnID'] in rfc5280.certificateExtensionsMap.keys() + ev, rest = der_decode(extn['extnValue'], + asn1Spec=rfc5280.certificateExtensionsMap[extn['extnID']], + decodeOpenTypes=True) + assert not rest + assert ev.prettyPrint() + assert der_encode(ev) == extn['extnValue'] + + for attr in ev: + if attr['type'] == rfc5916.id_deviceOwner: + assert attr['values'][0] == dev_own_oid + found_dev_owner = True + + assert found_dev_owner + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + import sys + + result = unittest.TextTestRunner(verbosity=2).run(suite) + sys.exit(not result.wasSuccessful()) -- cgit v1.2.3 From 0db0db806489e1fe968b0c22bf614b76ff55361a Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Fri, 8 Nov 2019 18:46:23 +0100 Subject: Temporarily depend on git master To test the upcoming PRs against unreleased pyasn1 containing new features related to permitted alphabet constraint. --- requirements.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 68daca2..a56cbc1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,3 @@ -pyasn1>=0.4.7,<0.5.0 +#pyasn1>=0.4.7,<0.5.0 +# temporarily depend on git master to test bew constraints +-e git://github.com/etingof/pyasn1.git#egg=pyasn1 -- cgit v1.2.3 From 883def9ff53b949f76014a0b1dc61594e813009b Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Fri, 8 Nov 2019 12:59:50 -0500 Subject: Update to RFC 8226 for ComponentPresentConstraint (#100) --- CHANGES.txt | 2 ++ pyasn1_modules/rfc8226.py | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 9b38f43..a20bcc2 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -24,6 +24,8 @@ 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 +- Update RFC8226 to use ComponentPresentConstraint() instead of the + previous work around Revision 0.2.7, released 09-10-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc8226.py b/pyasn1_modules/rfc8226.py index a5eb211..e7fe946 100644 --- a/pyasn1_modules/rfc8226.py +++ b/pyasn1_modules/rfc8226.py @@ -75,8 +75,12 @@ JWTClaimConstraints.componentType = namedtype.NamedTypes( tag.tagFormatSimple, 1))) ) - -JWTClaimConstraints.sizeSpec = univ.Sequence.sizeSpec + constraint.ValueSizeConstraint(1, 2) +JWTClaimConstraints.subtypeSpec = constraint.ConstraintsUnion( + constraint.WithComponentsConstraint( + ('mustInclude', constraint.ComponentPresentConstraint())), + constraint.WithComponentsConstraint( + ('permittedValues', constraint.ComponentPresentConstraint())) +) id_pe_JWTClaimConstraints = _OID(1, 3, 6, 1, 5, 5, 7, 1, 27) -- cgit v1.2.3 From 9a3a1dbc0ff6ccfabb56ea27a57a7c287d7b5d8b Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Fri, 8 Nov 2019 14:40:33 -0500 Subject: Add support for RFC 2631 (#101) --- CHANGES.txt | 1 + pyasn1_modules/rfc2631.py | 37 ++++++++++++++++++++++++++++++++++++ tests/__main__.py | 1 + tests/test_rfc2631.py | 48 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 87 insertions(+) create mode 100644 pyasn1_modules/rfc2631.py create mode 100644 tests/test_rfc2631.py diff --git a/CHANGES.txt b/CHANGES.txt index a20bcc2..a3660b7 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -26,6 +26,7 @@ Revision 0.2.8, released XX-XX-2019 - Added RFC5916 providing Device Owner Attribute - Update RFC8226 to use ComponentPresentConstraint() instead of the previous work around +- Add RFC2631 providing OtherInfo for Diffie-Hellman Key Agreement Revision 0.2.7, released 09-10-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc2631.py b/pyasn1_modules/rfc2631.py new file mode 100644 index 0000000..44e5371 --- /dev/null +++ b/pyasn1_modules/rfc2631.py @@ -0,0 +1,37 @@ +# +# 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 +# +# Diffie-Hellman Key Agreement +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc2631.txt +# https://www.rfc-editor.org/errata/eid5897 +# + +from pyasn1.type import constraint +from pyasn1.type import namedtype +from pyasn1.type import tag +from pyasn1.type import univ + + +class KeySpecificInfo(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('algorithm', univ.ObjectIdentifier()), + namedtype.NamedType('counter', univ.OctetString().subtype( + subtypeSpec=constraint.ValueSizeConstraint(4, 4))) + ) + + +class OtherInfo(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('keyInfo', KeySpecificInfo()), + namedtype.OptionalNamedType('partyAInfo', univ.OctetString().subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.NamedType('suppPubInfo', univ.OctetString().subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))) + ) diff --git a/tests/__main__.py b/tests/__main__.py index 9b75c65..0af458c 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -18,6 +18,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc2459.suite', 'tests.test_rfc2511.suite', 'tests.test_rfc2560.suite', + 'tests.test_rfc2631.suite', 'tests.test_rfc2634.suite', 'tests.test_rfc2985.suite', 'tests.test_rfc2986.suite', diff --git a/tests/test_rfc2631.py b/tests/test_rfc2631.py new file mode 100644 index 0000000..7abdcd7 --- /dev/null +++ b/tests/test_rfc2631.py @@ -0,0 +1,48 @@ +# +# 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 rfc2631 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class OtherInfoTestCase(unittest.TestCase): + pem_text = "MB0wEwYLKoZIhvcNAQkQAwYEBAAAAAGiBgQEAAAAwA==" + + def setUp(self): + self.asn1Spec = rfc2631.OtherInfo() + + 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 + + hex1 = univ.OctetString(hexValue='00000001') + assert asn1Object['keyInfo']['counter'] == hex1 + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + import sys + + result = unittest.TextTestRunner(verbosity=2).run(suite) + sys.exit(not result.wasSuccessful()) -- cgit v1.2.3 From 745eed006e93ae89dc0e6e5c366160d6239490e9 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Fri, 8 Nov 2019 23:20:51 +0100 Subject: Add support for RFC 7508 (#102) --- CHANGES.txt | 1 + pyasn1_modules/rfc7508.py | 90 +++++++++++++++++++++++++++++++ tests/__main__.py | 1 + tests/test_rfc7508.py | 132 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 224 insertions(+) create mode 100644 pyasn1_modules/rfc7508.py create mode 100644 tests/test_rfc7508.py 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()) -- cgit v1.2.3 From 5032fad108757efdf3f646cee1fe9c94e09b5004 Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Sun, 10 Nov 2019 03:39:55 -0500 Subject: Add support for RFC 3114, RFC 5755, RFC 5913, and RFC 5917 (#103) Adds support for RFC 3114, RFC 5755, RFC 5913, and RFC 5917 --- CHANGES.txt | 5 + pyasn1_modules/rfc3114.py | 77 +++++++++ pyasn1_modules/rfc5755.py | 398 ++++++++++++++++++++++++++++++++++++++++++++++ pyasn1_modules/rfc5913.py | 44 +++++ pyasn1_modules/rfc5917.py | 55 +++++++ tests/__main__.py | 4 + tests/test_rfc3114.py | 235 +++++++++++++++++++++++++++ tests/test_rfc5755.py | 190 ++++++++++++++++++++++ tests/test_rfc5913.py | 114 +++++++++++++ tests/test_rfc5917.py | 112 +++++++++++++ 10 files changed, 1234 insertions(+) create mode 100644 pyasn1_modules/rfc3114.py create mode 100644 pyasn1_modules/rfc5755.py create mode 100644 pyasn1_modules/rfc5913.py create mode 100644 pyasn1_modules/rfc5917.py create mode 100644 tests/test_rfc3114.py create mode 100644 tests/test_rfc5755.py create mode 100644 tests/test_rfc5913.py create mode 100644 tests/test_rfc5917.py diff --git a/CHANGES.txt b/CHANGES.txt index 3edd67b..49172ae 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -28,6 +28,11 @@ Revision 0.2.8, released XX-XX-2019 - Update RFC8226 to use ComponentPresentConstraint() instead of the previous work around - Add RFC2631 providing OtherInfo for Diffie-Hellman Key Agreement +- Add RFC3114 providing test values for the S/MIME Security Label +- Add RFC5755 providing Attribute Certificate Profile for Authorization +- Add RFC5913 providing Clearance Attribute and Authority Clearance + Constraints Certificate Extension +- Add RFC5917 providing Clearance Sponsor Attribute Revision 0.2.7, released 09-10-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc3114.py b/pyasn1_modules/rfc3114.py new file mode 100644 index 0000000..badcb1f --- /dev/null +++ b/pyasn1_modules/rfc3114.py @@ -0,0 +1,77 @@ +# +# 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 +# +# TEST Company Classification Policies +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc3114.txt +# + +from pyasn1.type import char +from pyasn1.type import namedval +from pyasn1.type import univ + +from pyasn1_modules import rfc5755 + + +id_smime = univ.ObjectIdentifier((1, 2, 840, 113549, 1, 9, 16, )) + +id_tsp = id_smime + (7, ) + +id_tsp_TEST_Amoco = id_tsp + (1, ) + +class Amoco_SecurityClassification(univ.Integer): + namedValues = namedval.NamedValues( + ('amoco-general', 6), + ('amoco-confidential', 7), + ('amoco-highly-confidential', 8) + ) + + +id_tsp_TEST_Caterpillar = id_tsp + (2, ) + +class Caterpillar_SecurityClassification(univ.Integer): + namedValues = namedval.NamedValues( + ('caterpillar-public', 6), + ('caterpillar-green', 7), + ('caterpillar-yellow', 8), + ('caterpillar-red', 9) + ) + + +id_tsp_TEST_Whirlpool = id_tsp + (3, ) + +class Whirlpool_SecurityClassification(univ.Integer): + namedValues = namedval.NamedValues( + ('whirlpool-public', 6), + ('whirlpool-internal', 7), + ('whirlpool-confidential', 8) + ) + + +id_tsp_TEST_Whirlpool_Categories = id_tsp + (4, ) + +class SecurityCategoryValues(univ.SequenceOf): + componentType = char.UTF8String() + +# Example SecurityCategoryValues: "LAW DEPARTMENT USE ONLY" +# Example SecurityCategoryValues: "HUMAN RESOURCES USE ONLY" + + +# Also, the privacy mark in the security label can contain a string, +# such as: "ATTORNEY-CLIENT PRIVILEGED INFORMATION" + + +# Map of security category type OIDs to security category added +# to the ones that are in rfc5755.py + +_securityCategoryMapUpdate = { + id_tsp_TEST_Whirlpool_Categories: SecurityCategoryValues(), +} + +rfc5755.securityCategoryMap.update(_securityCategoryMapUpdate) diff --git a/pyasn1_modules/rfc5755.py b/pyasn1_modules/rfc5755.py new file mode 100644 index 0000000..14f56fc --- /dev/null +++ b/pyasn1_modules/rfc5755.py @@ -0,0 +1,398 @@ +# +# 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 +# +# An Internet Attribute Certificate Profile for Authorization +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc5755.txt +# https://www.rfc-editor.org/rfc/rfc5912.txt (see Section 13) +# + +from pyasn1.type import char +from pyasn1.type import constraint +from pyasn1.type import namedtype +from pyasn1.type import namedval +from pyasn1.type import opentype +from pyasn1.type import tag +from pyasn1.type import univ +from pyasn1.type import useful + +from pyasn1_modules import rfc5280 +from pyasn1_modules import rfc5652 + +MAX = float('inf') + +# Map for Security Category type to value + +securityCategoryMap = { } + + +# Imports from RFC 5652 + +ContentInfo = rfc5652.ContentInfo + + +# Imports from RFC 5280 + +AlgorithmIdentifier = rfc5280.AlgorithmIdentifier + +Attribute = rfc5280.Attribute + +AuthorityInfoAccessSyntax = rfc5280.AuthorityInfoAccessSyntax + +AuthorityKeyIdentifier = rfc5280.AuthorityKeyIdentifier + +CertificateSerialNumber = rfc5280.CertificateSerialNumber + +CRLDistributionPoints = rfc5280.CRLDistributionPoints + +Extensions = rfc5280.Extensions + +Extension = rfc5280.Extension + +GeneralNames = rfc5280.GeneralNames + +GeneralName = rfc5280.GeneralName + +UniqueIdentifier = rfc5280.UniqueIdentifier + + +# Object Identifier arcs + +id_pkix = univ.ObjectIdentifier((1, 3, 6, 1, 5, 5, 7, )) + +id_pe = id_pkix + (1, ) + +id_kp = id_pkix + (3, ) + +id_aca = id_pkix + (10, ) + +id_ad = id_pkix + (48, ) + +id_at = univ.ObjectIdentifier((2, 5, 4, )) + +id_ce = univ.ObjectIdentifier((2, 5, 29, )) + + +# Attribute Certificate + +class AttCertVersion(univ.Integer): + namedValues = namedval.NamedValues( + ('v2', 1) + ) + + +class IssuerSerial(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('issuer', GeneralNames()), + namedtype.NamedType('serial', CertificateSerialNumber()), + namedtype.OptionalNamedType('issuerUID', UniqueIdentifier()) + ) + + +class ObjectDigestInfo(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('digestedObjectType', + univ.Enumerated(namedValues=namedval.NamedValues( + ('publicKey', 0), + ('publicKeyCert', 1), + ('otherObjectTypes', 2)))), + namedtype.OptionalNamedType('otherObjectTypeID', + univ.ObjectIdentifier()), + namedtype.NamedType('digestAlgorithm', + AlgorithmIdentifier()), + namedtype.NamedType('objectDigest', + univ.BitString()) + ) + + +class Holder(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('baseCertificateID', + IssuerSerial().subtype(implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatConstructed, 0))), + namedtype.OptionalNamedType('entityName', + GeneralNames().subtype(implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.OptionalNamedType('objectDigestInfo', + ObjectDigestInfo().subtype(implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatConstructed, 2))) +) + + +class V2Form(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('issuerName', + GeneralNames()), + namedtype.OptionalNamedType('baseCertificateID', + IssuerSerial().subtype(implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatConstructed, 0))), + namedtype.OptionalNamedType('objectDigestInfo', + ObjectDigestInfo().subtype(implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatConstructed, 1))) + ) + + +class AttCertIssuer(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('v1Form', GeneralNames()), + namedtype.NamedType('v2Form', V2Form().subtype(implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatConstructed, 0))) + ) + + +class AttCertValidityPeriod(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('notBeforeTime', useful.GeneralizedTime()), + namedtype.NamedType('notAfterTime', useful.GeneralizedTime()) + ) + + +class AttributeCertificateInfo(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('version', + AttCertVersion()), + namedtype.NamedType('holder', + Holder()), + namedtype.NamedType('issuer', + AttCertIssuer()), + namedtype.NamedType('signature', + AlgorithmIdentifier()), + namedtype.NamedType('serialNumber', + CertificateSerialNumber()), + namedtype.NamedType('attrCertValidityPeriod', + AttCertValidityPeriod()), + namedtype.NamedType('attributes', + univ.SequenceOf(componentType=Attribute())), + namedtype.OptionalNamedType('issuerUniqueID', + UniqueIdentifier()), + namedtype.OptionalNamedType('extensions', + Extensions()) + ) + + +class AttributeCertificate(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('acinfo', AttributeCertificateInfo()), + namedtype.NamedType('signatureAlgorithm', AlgorithmIdentifier()), + namedtype.NamedType('signatureValue', univ.BitString()) + ) + + +# Attribute Certificate Extensions + +id_pe_ac_auditIdentity = id_pe + (4, ) + +id_ce_noRevAvail = id_ce + (56, ) + +id_ce_targetInformation = id_ce + (55, ) + + +class TargetCert(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('targetCertificate', IssuerSerial()), + namedtype.OptionalNamedType('targetName', GeneralName()), + namedtype.OptionalNamedType('certDigestInfo', ObjectDigestInfo()) + ) + + +class Target(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('targetName', + GeneralName().subtype(implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.NamedType('targetGroup', + GeneralName().subtype(implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.NamedType('targetCert', + TargetCert().subtype(implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatConstructed, 2))) + ) + + +class Targets(univ.SequenceOf): + componentType = Target() + + +id_pe_ac_proxying = id_pe + (10, ) + + +class ProxyInfo(univ.SequenceOf): + componentType = Targets() + + +id_pe_aaControls = id_pe + (6, ) + + +class AttrSpec(univ.SequenceOf): + componentType = univ.ObjectIdentifier() + + +class AAControls(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('pathLenConstraint', + univ.Integer().subtype( + subtypeSpec=constraint.ValueRangeConstraint(0, MAX))), + namedtype.OptionalNamedType('permittedAttrs', + AttrSpec().subtype(implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.OptionalNamedType('excludedAttrs', + AttrSpec().subtype(implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.DefaultedNamedType('permitUnSpecified', + univ.Boolean().subtype(value=1)) + ) + + +# Attribute Certificate Attributes + +id_aca_authenticationInfo = id_aca + (1, ) + + +id_aca_accessIdentity = id_aca + (2, ) + + +class SvceAuthInfo(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('service', GeneralName()), + namedtype.NamedType('ident', GeneralName()), + namedtype.OptionalNamedType('authInfo', univ.OctetString()) + ) + + +id_aca_chargingIdentity = id_aca + (3, ) + + +id_aca_group = id_aca + (4, ) + + +class IetfAttrSyntax(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('policyAuthority', + GeneralNames().subtype(implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.NamedType('values', univ.SequenceOf( + componentType=univ.Choice(componentType=namedtype.NamedTypes( + namedtype.NamedType('octets', univ.OctetString()), + namedtype.NamedType('oid', univ.ObjectIdentifier()), + namedtype.NamedType('string', char.UTF8String()) + )) + )) + ) + + +id_at_role = id_at + (72,) + + +class RoleSyntax(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('roleAuthority', + GeneralNames().subtype(implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.NamedType('roleName', + GeneralName().subtype(implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 1))) + ) + + +class ClassList(univ.BitString): + namedValues = namedval.NamedValues( + ('unmarked', 0), + ('unclassified', 1), + ('restricted', 2), + ('confidential', 3), + ('secret', 4), + ('topSecret', 5) + ) + + +class SecurityCategory(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('type', + univ.ObjectIdentifier().subtype(implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.NamedType('value', + univ.Any().subtype(implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 1)), + openType=opentype.OpenType('type', securityCategoryMap)) + ) + + +id_at_clearance = univ.ObjectIdentifier((2, 5, 4, 55, )) + + +class Clearance(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('policyId', + univ.ObjectIdentifier()), + namedtype.DefaultedNamedType('classList', + ClassList().subtype(value='unclassified')), + namedtype.OptionalNamedType('securityCategories', + univ.SetOf(componentType=SecurityCategory())) + ) + + +id_at_clearance_rfc3281 = univ.ObjectIdentifier((2, 5, 1, 5, 55, )) + + +class Clearance_rfc3281(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('policyId', + univ.ObjectIdentifier().subtype(implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.DefaultedNamedType('classList', + ClassList().subtype(implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 1)).subtype( + value='unclassified')), + namedtype.OptionalNamedType('securityCategories', + univ.SetOf(componentType=SecurityCategory()).subtype( + implicitTag=tag.Tag( + tag.tagClassContext, tag.tagFormatSimple, 2))) + ) + + +id_aca_encAttrs = id_aca + (6, ) + + +class ACClearAttrs(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('acIssuer', GeneralName()), + namedtype.NamedType('acSerial', univ.Integer()), + namedtype.NamedType('attrs', univ.SequenceOf(componentType=Attribute())) + ) + + +# Map of Certificate Extension OIDs to Extensions added to the +# ones that are in rfc5280.py + +_certificateExtensionsMapUpdate = { + id_pe_ac_auditIdentity: univ.OctetString(), + id_ce_noRevAvail: univ.Null(), + id_ce_targetInformation: Targets(), + id_pe_ac_proxying: ProxyInfo(), + id_pe_aaControls: AAControls(), +} + +rfc5280.certificateExtensionsMap.update(_certificateExtensionsMapUpdate) + + +# Map of AttributeType OIDs to AttributeValue added to the +# ones that are in rfc5280.py + +_certificateAttributesMapUpdate = { + id_aca_authenticationInfo: SvceAuthInfo(), + id_aca_accessIdentity: SvceAuthInfo(), + id_aca_chargingIdentity: IetfAttrSyntax(), + id_aca_group: IetfAttrSyntax(), + id_at_role: RoleSyntax(), + id_at_clearance: Clearance(), + id_at_clearance_rfc3281: Clearance_rfc3281(), + id_aca_encAttrs: ContentInfo(), +} + +rfc5280.certificateAttributesMap.update(_certificateAttributesMapUpdate) diff --git a/pyasn1_modules/rfc5913.py b/pyasn1_modules/rfc5913.py new file mode 100644 index 0000000..0bd0653 --- /dev/null +++ b/pyasn1_modules/rfc5913.py @@ -0,0 +1,44 @@ +# +# 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 +# +# Authority Clearance Constraints Certificate Extension +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc5913.txt +# https://www.rfc-editor.org/errata/eid5890 +# + +from pyasn1.type import constraint +from pyasn1.type import univ + +from pyasn1_modules import rfc5280 +from pyasn1_modules import rfc5755 + +MAX = float('inf') + + +# Authority Clearance Constraints Certificate Extension + +id_pe_clearanceConstraints = univ.ObjectIdentifier('1.3.6.1.5.5.7.1.21') + +id_pe_authorityClearanceConstraints = id_pe_clearanceConstraints + + +class AuthorityClearanceConstraints(univ.SequenceOf): + componentType = rfc5755.Clearance() + subtypeSpec=constraint.ValueSizeConstraint(1, MAX) + + +# Map of Certificate Extension OIDs to Extensions added to the +# ones that are in rfc5280.py + +_certificateExtensionsMapUpdate = { + id_pe_clearanceConstraints: AuthorityClearanceConstraints(), +} + +rfc5280.certificateExtensionsMap.update(_certificateExtensionsMapUpdate) diff --git a/pyasn1_modules/rfc5917.py b/pyasn1_modules/rfc5917.py new file mode 100644 index 0000000..ed9af98 --- /dev/null +++ b/pyasn1_modules/rfc5917.py @@ -0,0 +1,55 @@ +# +# 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 +# +# Clearance Sponsor Attribute +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc5917.txt +# https://www.rfc-editor.org/errata/eid4558 +# https://www.rfc-editor.org/errata/eid5883 +# + +from pyasn1.type import char +from pyasn1.type import constraint +from pyasn1.type import namedtype +from pyasn1.type import univ + +from pyasn1_modules import rfc5280 + + +# DirectoryString is the same as RFC 5280, except for two things: +# 1. the length is limited to 64; +# 2. only the 'utf8String' choice remains because the ASN.1 +# specification says: ( WITH COMPONENTS { utf8String PRESENT } ) + +class DirectoryString(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('utf8String', char.UTF8String().subtype( + subtypeSpec=constraint.ValueSizeConstraint(1, 64))), + ) + + +# Clearance Sponsor Attribute + +id_clearanceSponsor = univ.ObjectIdentifier((2, 16, 840, 1, 101, 2, 1, 5, 68)) + +ub_clearance_sponsor = univ.Integer(64) + + +at_clearanceSponsor = rfc5280.Attribute() +at_clearanceSponsor['type'] = id_clearanceSponsor +at_clearanceSponsor['values'][0] = DirectoryString() + + +# Add to the map of Attribute Type OIDs to Attributes in rfc5280.py. + +_certificateAttributesMapUpdate = { + id_clearanceSponsor: DirectoryString(), +} + +rfc5280.certificateAttributesMap.update(_certificateAttributesMapUpdate) diff --git a/tests/__main__.py b/tests/__main__.py index c1fa4f7..479cd09 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -22,6 +22,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc2634.suite', 'tests.test_rfc2985.suite', 'tests.test_rfc2986.suite', + 'tests.test_rfc3114.suite', 'tests.test_rfc3161.suite', 'tests.test_rfc3274.suite', 'tests.test_rfc3279.suite', @@ -49,9 +50,12 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc5649.suite', 'tests.test_rfc5652.suite', 'tests.test_rfc5751.suite', + 'tests.test_rfc5755.suite', + 'tests.test_rfc5913.suite', 'tests.test_rfc5914.suite', 'tests.test_rfc5915.suite', 'tests.test_rfc5916.suite', + 'tests.test_rfc5917.suite', 'tests.test_rfc5924.suite', 'tests.test_rfc5934.suite', 'tests.test_rfc5940.suite', diff --git a/tests/test_rfc3114.py b/tests/test_rfc3114.py new file mode 100644 index 0000000..537fe9a --- /dev/null +++ b/tests/test_rfc3114.py @@ -0,0 +1,235 @@ +# +# 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 rfc3114 +from pyasn1_modules import rfc5035 +from pyasn1_modules import rfc5083 +from pyasn1_modules import rfc5084 +from pyasn1_modules import rfc5280 +from pyasn1_modules import rfc5652 +from pyasn1_modules import rfc5755 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class SecurityLabelTestCase(unittest.TestCase): + pem_text = """\ +MIITHAYJKoZIhvcNAQcCoIITDTCCEwkCAQMxDTALBglghkgBZQMEAgIwggeUBgsq +hkiG9w0BCRABF6CCB4MEggd/MIIHewIBADGCAk8wggJLAgEAMDMwJjEUMBIGA1UE +CgwLZXhhbXBsZS5jb20xDjAMBgNVBAMMBUFsaWNlAgkAg/ULtwvVxA4wDQYJKoZI +hvcNAQEBBQAEggIAdZphtN3x8a8kZoAFY15HYRD6JyPBueRUhLbTPoOH3pZ9xeDK ++zVXGlahl1y1UOe+McEx2oD7cxAkhFuruNZMrCYEBCTZMwVhyEOZlBXdZEs8rZUH +L3FFE5PJnygsSIO9DMxd1UuTFGTgCm5V5ZLFGmjeEGJRbsfTyo52S7iseJqIN3dl +743DbApu0+yuUoXKxqKdUFlEVxmhvc+Qbg/zfiwu8PTsYiUQDMBi4cdIlju8iLjj +389xQHNyndXHWD51is89GG8vpBe+IsN8mnbGtCcpqtJ/c65ErJhHTR7rSJSMEqQD +0LPOCKIY1q9FaSSJfMXJZk9t/rPxgUEVjfw7hAkKpgOAqoZRN+FpnFyBl0FnnXo8 +kLp55tfVyNibtUpmdCPkOwt9b3jAtKtnvDQ2YqY1/llfEUnFOVDKwuC6MYwifm92 +qNlAQA/T0+ocjs6gA9zOLx+wD1zqM13hMD/L+T2OHL/WgvGb62JLrNHXuPWA8RSh +O4kIlPtARKXap2S3+MX/kpSUUrNa65Y5uK1jwFFclczG+CPCIBBn6iJiQT/vOX1I +97YUP4Qq6OGkjK064Bq6o8+e5+NmIOBcygYRv6wA7vGkmPLSWbnw99qD728bBh84 +fC3EjItdusqGIwjzL0eSUWXJ5eu0Z3mYhJGN1pe0R/TEB5ibiJsMLpWAr3gwggUP +BgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEGMBEEDE2HVyIurFKUEX8MEgIBEICCBOD+ +L7PeC/BpmMOb9KlS+r+LD+49fi6FGBrs8aieGi7ezZQEiFYS38aYQzTYYCt3SbJQ +TkX1fDsGZiaw/HRiNh7sJnxWATm+XNKGoq+Wls9RhSJ45Sw4GMqwpoxZjeT84Uoz +OITk3l3fV+3XiGcCejHkp8DAKZFExd5rrjlpnnAOBX6w8NrXO4s2n0LrMhtBU4eB +2YKhGgs5Q6wQyXtU7rc7OOwTGvxWEONzSHJ01pyvqVQZAohsZPaWLULrM/kEGkrh +G4jcaVjVPfULi7Uqo14imYhdCq5Ba4bwqI0Ot6mB27KD6LlOnVC/YmXCNIoYoWmq +y1o3pSm9ovnLEO/dzxQjEJXYeWRje9M/sTxotM/5oZBpYMHqIwHTJbehXFgp8+oD +jyTfayMYA3fTcTH3XbGPQfnYW2U9+ka/JhcSYybM8cuDNFd1I1LIQXoJRITXtkvP +UbJqm+s6DtS5yvG9I8aQxlT365zphS4vbQaO74ujO8bE3dynrvTTV0c318TcHpN3 +DY9PIt6mHXMIPDLEA4wes90zg6iah5XiQcLtfLaAdYwEEGlImGD8n0kOhSNgclSL +Mklpj5mVOs8exli3qoXlVMRJcBptSwOe0QPcRY30spywS4zt1UDIQ0jaecGGVtUY +j586nkubhAxwZkuQKWxgt6yYTpGNSKCdvd+ygfyGJRDbWdn6nck/EPnG1773KTHR +hMrXrBPBpSlfyJ/ju3644CCFqCjFoTh4bmB63k9ejUEVkJIJuoeKeTBaUxbCIink +K4htBkgchHP51RJp4q9jQbziD3aOhg13hO1GFQ4E/1DNIJxbEnURNp/ga8SqmnLY +8f5Pzwhm1mSzZf+obowbQ+epISrswWyjUKKO+uJfrAVN2TS/5+X6T3U6pBWWjH6+ +xDngrAJwtIdKBo0iSEwJ2eir4X8TcrSy9l8RSOiTPtqS5dF3RWSWOzkcO72fHCf/ +42+DLgUVX8Oe5mUvp7QYiXXsXGezLJ8hPIrGuOEypafDv3TwFkBc2MIB0QUhk+GG +1ENY3jiNcyEbovF5Lzz+ubvechHSb1arBuEczJzN4riM2Dc3c+r8N/2Ft6eivK7H +UuYX1uAcArhunZpA8yBGLF1m+DUXFtzWAUvfMKYPdfwGMckghF7YwLrTXd8ZhPIk +HNO1KdwQKIRfgIlUPfTxRB7eNrG/Ma9a/IwrcI1QtkXU59uIZIw+7+FHZRWPsOjT +u1Pdy+JtcSTG4dmS+DIwqpUzdu6MaBCVaOhXHwybvaSPTfMG/nR/NxF1FI8xgydn +zXZs8HtFDL9iytKnvXHx+IIz8Rahp/PK8S80vPQNIeef/JgnIhtosID/A614LW1t +B4cWdveYlD5U8T/XXInAtCY78Q9WJD+ecu87OJmlOdmjrFvitpQAo8+NGWxc7Wl7 +LtgDuYel7oXFCVtI2npbA7R+K5/kzUvDCY6GTgzn1Gfamc1/Op6Ue17qd/emvhbI +x+ng3swf8TJVnCNDIXucKVA4boXSlCEhCGzfoZZYGVvm1/hrypiBtpUIKWTxLnz4 +AQJdZ5LGiCQJQU1wMyHsg6vWmNaJVhGHE6D/EnKsvJptFIkAx0wWkh35s48p7EbU +8QBg//5eNru6yvLRutfdBX7T4w681pCD+dOiom75C3UdahrfoFkNsZ2hB88+qNsE +EPb/xuGu8ZzSPZhakhl2NS2ggglpMIICAjCCAYigAwIBAgIJAOiR1gaRT87yMAoG +CCqGSM49BAMDMD8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJWQTEQMA4GA1UEBwwH +SGVybmRvbjERMA8GA1UECgwIQm9ndXMgQ0EwHhcNMTkwNTE0MDg1ODExWhcNMjEw +NTEzMDg1ODExWjA/MQswCQYDVQQGEwJVUzELMAkGA1UECAwCVkExEDAOBgNVBAcM +B0hlcm5kb24xETAPBgNVBAoMCEJvZ3VzIENBMHYwEAYHKoZIzj0CAQYFK4EEACID +YgAE8FF2VLHojmqlnawpQwjG6fWBQDPOy05hYq8oKcyg1PXH6kgoO8wQyKYVwsDH +Evc1Vg6ErQm3LzdI8OQpYx3H386R2F/dT/PEmUSdcOIWsB4zrFsbzNwJGIGeZ33Z +S+xGo1AwTjAdBgNVHQ4EFgQU8jXbNATapVXyvWkDmbBi7OIVCMEwHwYDVR0jBBgw +FoAU8jXbNATapVXyvWkDmbBi7OIVCMEwDAYDVR0TBAUwAwEB/zAKBggqhkjOPQQD +AwNoADBlAjBaUY2Nv03KolLNRJ2wSoNK8xlvzIWTFgIhsBWpD1SpJxRRv22kkoaw +9bBtmyctW+YCMQC3/KmjNtSFDDh1I+lbOufkFDSQpsMzcNAlwEAERQGgg6iXX+Nh +A+bFqNC7FyF4WWQwggOHMIIDDqADAgECAgkApbNUKBuwbkYwCgYIKoZIzj0EAwMw +PzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAlZBMRAwDgYDVQQHDAdIZXJuZG9uMREw +DwYDVQQKDAhCb2d1cyBDQTAeFw0xOTExMDIxODQyMThaFw0yMDExMDExODQyMTha +MGYxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJWQTEQMA4GA1UEBxMHSGVybmRvbjEQ +MA4GA1UEChMHRXhhbXBsZTEMMAoGA1UECxMDUENBMRgwFgYDVQQDEw9wY2EuZXhh +bXBsZS5jb20wdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQ9/m9uACpsTl2frBuILHiw +IJyfUEpKseYJ+JYL1AtIZU0YeJ9DA+32h0ZeNGJDtDClnbBEPpn3W/5+TzldcsTe +QlAJB08gcVRjkQym9LtPq7rGubCeVWlRRE9M7F9znk6jggGtMIIBqTAdBgNVHQ4E +FgQUJuolDwsyICik11oKjf8t3L1/VGUwbwYDVR0jBGgwZoAU8jXbNATapVXyvWkD +mbBi7OIVCMGhQ6RBMD8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJWQTEQMA4GA1UE +BwwHSGVybmRvbjERMA8GA1UECgwIQm9ndXMgQ0GCCQDokdYGkU/O8jAPBgNVHRMB +Af8EBTADAQH/MAsGA1UdDwQEAwIBhjBCBglghkgBhvhCAQ0ENRYzVGhpcyBjZXJ0 +aWZpY2F0ZSBjYW5ub3QgYmUgdHJ1c3RlZCBmb3IgYW55IHB1cnBvc2UuMBUGA1Ud +IAQOMAwwCgYIKwYBBQUHDQIwCgYDVR02BAMCAQIwgZEGCCsGAQUFBwEVBIGEMIGB +MFkGCyqGSIb3DQEJEAcDAwIF4DFGMESACyqGSIb3DQEJEAcEgTUwMwwXTEFXIERF +UEFSVE1FTlQgVVNFIE9OTFkMGEhVTUFOIFJFU09VUkNFUyBVU0UgT05MWTARBgsq +hkiG9w0BCRAHAgMCBPAwEQYLKoZIhvcNAQkQBwEDAgXgMAoGCCqGSM49BAMDA2cA +MGQCMBlIP4FWrNzWXR8OgfcvCLGPG+110EdsmwznIF6ThT1vbJYvYoSbBXTZ9OCh +/cCMMQIwJOySybHl/eLkNJh971DWF4mUQkt3WGBmZ+9Rg2cJTdat2ZjPKg101NuD +tkUyjGxfMIID1DCCA1qgAwIBAgIUUc1IQGJpeYQ0XwOS2ZmVEb3aeZ0wCgYIKoZI +zj0EAwMwZjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlZBMRAwDgYDVQQHEwdIZXJu +ZG9uMRAwDgYDVQQKEwdFeGFtcGxlMQwwCgYDVQQLEwNQQ0ExGDAWBgNVBAMTD3Bj +YS5leGFtcGxlLmNvbTAeFw0xOTExMDUyMjIwNDZaFw0yMDExMDQyMjIwNDZaMIGS +MQswCQYDVQQGEwJVUzELMAkGA1UECBMCVkExEDAOBgNVBAcTB0hlcm5kb24xEDAO +BgNVBAoTB0V4YW1wbGUxIjAgBgNVBAsTGUh1bWFuIFJlc291cmNlIERlcGFydG1l +bnQxDTALBgNVBAMTBEZyZWQxHzAdBgkqhkiG9w0BCQEWEGZyZWRAZXhhbXBsZS5j +b20wdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQObFslQ2EBP0xlDJ3sRnsNaqm/woQg +KpBispSxXxK5bWUVpfnWsZnjLWhtDuPcu1BcBlM2g7gwL/aw8nUSIK3D8Ja9rTUQ +QXc3zxnkcl8+8znNXHMGByRjPUH87C+TOrqjggGaMIIBljAdBgNVHQ4EFgQU5m71 +1OqFDNGRSWMOSzTXjpTLIFUwbwYDVR0jBGgwZoAUJuolDwsyICik11oKjf8t3L1/ +VGWhQ6RBMD8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJWQTEQMA4GA1UEBwwHSGVy +bmRvbjERMA8GA1UECgwIQm9ndXMgQ0GCCQCls1QoG7BuRjAPBgNVHRMBAf8EBTAD +AQH/MAsGA1UdDwQEAwIBhjBCBglghkgBhvhCAQ0ENRYzVGhpcyBjZXJ0aWZpY2F0 +ZSBjYW5ub3QgYmUgdHJ1c3RlZCBmb3IgYW55IHB1cnBvc2UuMBUGA1UdIAQOMAww +CgYIKwYBBQUHDQIwCgYDVR02BAMCAQIwfwYDVR0JBHgwdjBJBgNVBDcxQjBABgsq +hkiG9w0BCRAHAwMCBeAxLTArgAsqhkiG9w0BCRAHBIEcMBoMGEhVTUFOIFJFU09V +UkNFUyBVU0UgT05MWTApBglghkgBZQIBBUQxHAwaSHVtYW4gUmVzb3VyY2VzIERl +cGFydG1lbnQwCgYIKoZIzj0EAwMDaAAwZQIwVh/RypULFgPpAN0I7OvuMomRWnm/ +Hea3Hk8PtTRz2Zai8iYat7oeAmGVgMhSXy2jAjEAuJW4l/CFatBy4W/lZ7gS3weB +dBa5WEDIFFMC7GjGtCeLtXYqWfBnRdK26dOaHLB2MYIB7jCCAeoCAQEwfjBmMQsw +CQYDVQQGEwJVUzELMAkGA1UECBMCVkExEDAOBgNVBAcTB0hlcm5kb24xEDAOBgNV +BAoTB0V4YW1wbGUxDDAKBgNVBAsTA1BDQTEYMBYGA1UEAxMPcGNhLmV4YW1wbGUu +Y29tAhRRzUhAYml5hDRfA5LZmZURvdp5nTALBglghkgBZQMEAgKggeIwGgYJKoZI +hvcNAQkDMQ0GCyqGSIb3DQEJEAEXMBwGCSqGSIb3DQEJBTEPFw0xOTExMDgyMDA4 +MzFaMD8GCSqGSIb3DQEJBDEyBDCd5WyvIB0VdXgPBWPtI152MIJLg5o68IRimCXx +bVY0j3YyAKbi0egiZ/UunkyCfv0wZQYLKoZIhvcNAQkQAgIxVjFUAgEIBgsqhkiG +9w0BCRAHAzEtMCuACyqGSIb3DQEJEAcEgRwwGgwYSFVNQU4gUkVTT1VSQ0VTIFVT +RSBPTkxZExNCb2FndXMgUHJpdmFjeSBNYXJrMAoGCCqGSM49BAMDBGcwZQIwWkD7 +03QoNrKL5HJnuGJqvML1KlUXZDHnFpnJ+QMzXi8gocyfpRXWm6h0NjXieE0XAjEA +uuDSOoaUIz+G9aemAE0ldpo1c0avNGa7BtynUTHmwosD6Sjfj0epAg9OnMedOjbr +""" + + def testDerCodec(self): + layers = { } + layers.update(rfc5652.cmsContentTypesMap) + + getNextLayer = { + rfc5652.id_ct_contentInfo: lambda x: x['contentType'], + rfc5652.id_signedData: lambda x: x['encapContentInfo']['eContentType'], + rfc5083.id_ct_authEnvelopedData: lambda x: None + } + + getNextSubstrate = { + rfc5652.id_ct_contentInfo: lambda x: x['content'], + rfc5652.id_signedData: lambda x: x['encapContentInfo']['eContent'], + rfc5083.id_ct_authEnvelopedData: lambda x: None + } + + substrate = pem.readBase64fromText(self.pem_text) + + next_layer = rfc5652.id_ct_contentInfo + while next_layer: + asn1Object, rest = der_decode(substrate, asn1Spec=layers[next_layer]) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + if next_layer == rfc5652.id_signedData: + attrs = asn1Object['signerInfos'][0]['signedAttrs'] + certs = asn1Object['certificates'] + + substrate = getNextSubstrate[next_layer](asn1Object) + next_layer = getNextLayer[next_layer](asn1Object) + + spid = rfc3114.id_tsp_TEST_Whirlpool + catid = rfc3114.id_tsp_TEST_Whirlpool_Categories + conf = rfc3114.Whirlpool_SecurityClassification(value='whirlpool-confidential') + + assert catid in rfc5755.securityCategoryMap.keys() + assert rfc5755.id_at_clearance in rfc5280.certificateAttributesMap.keys() + assert rfc5280.id_ce_subjectDirectoryAttributes in rfc5280.certificateExtensionsMap.keys() + + security_label_okay = False + for attr in attrs: + if attr['attrType'] == rfc5035.id_aa_securityLabel: + esssl, rest = der_decode(attr['attrValues'][0], + asn1Spec=rfc5035.ESSSecurityLabel()) + assert not rest + assert esssl.prettyPrint() + assert der_encode(esssl) == attr['attrValues'][0] + + assert esssl['security-policy-identifier'] == spid + assert esssl['security-classification'] == conf + + for cat in esssl['security-categories']: + if cat['type'] == catid: + scv, rest = der_decode(cat['value'], + asn1Spec=rfc3114.SecurityCategoryValues()) + assert not rest + assert scv.prettyPrint() + assert der_encode(scv) == cat['value'] + for scv_str in scv: + assert 'USE ONLY' in scv_str + security_label_okay = True + + assert security_label_okay + + clearance_okay = False + for cert_choice in certs: + for extn in cert_choice['certificate']['tbsCertificate']['extensions']: + if extn['extnID'] == rfc5280.id_ce_subjectDirectoryAttributes: + ev, rest = der_decode(extn['extnValue'], + asn1Spec=rfc5280.certificateExtensionsMap[extn['extnID']]) + assert not rest + assert ev.prettyPrint() + assert der_encode(ev) == extn['extnValue'] + + for attr in ev: + if attr['type'] == rfc5755.id_at_clearance: + av, rest = der_decode(attr['values'][0], + asn1Spec=rfc5280.certificateAttributesMap[attr['type']]) + assert av['policyId'] == spid + for cat in av['securityCategories']: + assert cat['type'] == catid + scv, rest = der_decode(cat['value'], + asn1Spec=rfc5755.securityCategoryMap[cat['type']]) + assert not rest + assert scv.prettyPrint() + assert der_encode(scv) == cat['value'] + for scv_str in scv: + assert 'USE ONLY' in scv_str + clearance_okay = True + + assert clearance_okay + + +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_rfc5755.py b/tests/test_rfc5755.py new file mode 100644 index 0000000..a41f0f9 --- /dev/null +++ b/tests/test_rfc5755.py @@ -0,0 +1,190 @@ +# +# 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.compat.octets import str2octs + +from pyasn1_modules import pem +from pyasn1_modules import rfc5280 +from pyasn1_modules import rfc5755 +from pyasn1_modules import rfc3114 + +try: + import unittest2 as unittest + +except ImportError: + import unittest + + +class AttributeCertificateTestCase(unittest.TestCase): + pem_text = """\ +MIIDBTCCAm4CAQEwgY+gUTBKpEgwRjEjMCEGA1UEAwwaQUNNRSBJbnRlcm1lZGlh +dGUgRUNEU0EgQ0ExCzAJBgNVBAYTAkZJMRIwEAYDVQQKDAlBQ01FIEx0ZC4CAx7N +WqE6pDgwNjETMBEGA1UEAwwKQUNNRSBFQ0RTQTELMAkGA1UEBhMCRkkxEjAQBgNV +BAoMCUFDTUUgTHRkLqA9MDukOTA3MRQwEgYDVQQDDAtleGFtcGxlLmNvbTELMAkG +A1UEBhMCRkkxEjAQBgNVBAoMCUFDTUUgTHRkLjANBgkqhkiG9w0BAQsFAAIEC63K +/jAiGA8yMDE2MDEwMTEyMDAwMFoYDzIwMTYwMzAxMTIwMDAwWjCB8jA8BggrBgEF +BQcKATEwMC6GC3VybjpzZXJ2aWNlpBUwEzERMA8GA1UEAwwIdXNlcm5hbWUECHBh +c3N3b3JkMDIGCCsGAQUFBwoCMSYwJIYLdXJuOnNlcnZpY2WkFTATMREwDwYDVQQD +DAh1c2VybmFtZTA1BggrBgEFBQcKAzEpMCegGKQWMBQxEjAQBgNVBAMMCUFDTUUg +THRkLjALDAlBQ01FIEx0ZC4wIAYIKwYBBQUHCgQxFDASMBAMBmdyb3VwMQwGZ3Jv +dXAyMCUGA1UESDEeMA2hC4YJdXJuOnJvbGUxMA2hC4YJdXJuOnJvbGUyMGowHwYD +VR0jBBgwFoAUgJCMhskAsEBzvklAX8yJBOXO500wCQYDVR04BAIFADA8BgNVHTcB +Af8EMjAwMB2gCoYIdXJuOnRlc3SgD4INKi5leGFtcGxlLmNvbTAPoA2GC3Vybjph +bm90aGVyMA0GCSqGSIb3DQEBCwUAA4GBACygfTs6TkPurZQTLufcE3B1H2707OXK +sJlwRpuodR2oJbunSHZ94jcJHs5dfbzFs6vNfVLlBiDBRieX4p+4JcQ2P44bkgyi +UTJu7g1b6C1liB3vO6yH5hOZicOAaKd+c/myuGb9uJ4n6y2oLNxnk/fDzpuZUe2h +Q4eikPk4LQey +""" + + def setUp(self): + self.asn1Spec = rfc5755.AttributeCertificate() + + 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['acinfo']['version'] == 1 + + count = 0 + for attr in asn1Object['acinfo']['attributes']: + assert attr['type'] in rfc5280.certificateAttributesMap.keys() + av, rest = der_decode(attr['values'][0], + asn1Spec=rfc5280.certificateAttributesMap[attr['type']]) + assert not rest + assert av.prettyPrint() + assert der_encode(av) == attr['values'][0] + count += 1 + + assert count == 5 + + 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['acinfo']['version'] == 1 + + count = 0 + for attr in asn1Object['acinfo']['attributes']: + assert attr['type'] in rfc5280.certificateAttributesMap.keys() + count += 1 + if attr['type'] == rfc5755.id_aca_authenticationInfo: + assert attr['values'][0]['authInfo'] == str2octs('password') + + assert count == 5 + +class CertificateWithClearanceTestCase(unittest.TestCase): + cert_pem_text = """\ +MIID1DCCA1qgAwIBAgIUUc1IQGJpeYQ0XwOS2ZmVEb3aeZ0wCgYIKoZIzj0EAwMw +ZjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlZBMRAwDgYDVQQHEwdIZXJuZG9uMRAw +DgYDVQQKEwdFeGFtcGxlMQwwCgYDVQQLEwNQQ0ExGDAWBgNVBAMTD3BjYS5leGFt +cGxlLmNvbTAeFw0xOTExMDUyMjIwNDZaFw0yMDExMDQyMjIwNDZaMIGSMQswCQYD +VQQGEwJVUzELMAkGA1UECBMCVkExEDAOBgNVBAcTB0hlcm5kb24xEDAOBgNVBAoT +B0V4YW1wbGUxIjAgBgNVBAsTGUh1bWFuIFJlc291cmNlIERlcGFydG1lbnQxDTAL +BgNVBAMTBEZyZWQxHzAdBgkqhkiG9w0BCQEWEGZyZWRAZXhhbXBsZS5jb20wdjAQ +BgcqhkjOPQIBBgUrgQQAIgNiAAQObFslQ2EBP0xlDJ3sRnsNaqm/woQgKpBispSx +XxK5bWUVpfnWsZnjLWhtDuPcu1BcBlM2g7gwL/aw8nUSIK3D8Ja9rTUQQXc3zxnk +cl8+8znNXHMGByRjPUH87C+TOrqjggGaMIIBljAdBgNVHQ4EFgQU5m711OqFDNGR +SWMOSzTXjpTLIFUwbwYDVR0jBGgwZoAUJuolDwsyICik11oKjf8t3L1/VGWhQ6RB +MD8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJWQTEQMA4GA1UEBwwHSGVybmRvbjER +MA8GA1UECgwIQm9ndXMgQ0GCCQCls1QoG7BuRjAPBgNVHRMBAf8EBTADAQH/MAsG +A1UdDwQEAwIBhjBCBglghkgBhvhCAQ0ENRYzVGhpcyBjZXJ0aWZpY2F0ZSBjYW5u +b3QgYmUgdHJ1c3RlZCBmb3IgYW55IHB1cnBvc2UuMBUGA1UdIAQOMAwwCgYIKwYB +BQUHDQIwCgYDVR02BAMCAQIwfwYDVR0JBHgwdjBJBgNVBDcxQjBABgsqhkiG9w0B +CRAHAwMCBeAxLTArgAsqhkiG9w0BCRAHBIEcMBoMGEhVTUFOIFJFU09VUkNFUyBV +U0UgT05MWTApBglghkgBZQIBBUQxHAwaSHVtYW4gUmVzb3VyY2VzIERlcGFydG1l +bnQwCgYIKoZIzj0EAwMDaAAwZQIwVh/RypULFgPpAN0I7OvuMomRWnm/Hea3Hk8P +tTRz2Zai8iYat7oeAmGVgMhSXy2jAjEAuJW4l/CFatBy4W/lZ7gS3weBdBa5WEDI +FFMC7GjGtCeLtXYqWfBnRdK26dOaHLB2 +""" + + def setUp(self): + self.asn1Spec = rfc5280.Certificate() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.cert_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + clearance_found = False + for extn in asn1Object['tbsCertificate']['extensions']: + if extn['extnID'] == rfc5280.id_ce_subjectDirectoryAttributes: + assert extn['extnID'] in rfc5280.certificateExtensionsMap.keys() + ev, rest = der_decode(extn['extnValue'], + asn1Spec=rfc5280.certificateExtensionsMap[extn['extnID']]) + assert not rest + assert ev.prettyPrint() + assert der_encode(ev) == extn['extnValue'] + + for attr in ev: + if attr['type'] == rfc5755.id_at_clearance: + assert attr['type'] in rfc5280.certificateAttributesMap.keys() + av, rest = der_decode(attr['values'][0], + asn1Spec=rfc5280.certificateAttributesMap[attr['type']]) + assert av['policyId'] == rfc3114.id_tsp_TEST_Whirlpool + for cat in av['securityCategories']: + assert cat['type'] == rfc3114.id_tsp_TEST_Whirlpool_Categories + assert cat['type'] in rfc5755.securityCategoryMap.keys() + catv, rest = der_decode(cat['value'], + asn1Spec=rfc5755.securityCategoryMap[cat['type']]) + assert u'USE ONLY' in catv[0] + clearance_found = True + + assert clearance_found + + def testOpenTypes(self): + substrate = pem.readBase64fromText(self.cert_pem_text) + asn1Object, rest = der_decode(substrate, + asn1Spec=self.asn1Spec, + decodeOpenTypes=True) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + clearance_found = False + for extn in asn1Object['tbsCertificate']['extensions']: + if extn['extnID'] == rfc5280.id_ce_subjectDirectoryAttributes: + assert extn['extnID'] in rfc5280.certificateExtensionsMap.keys() + ev, rest = der_decode(extn['extnValue'], + asn1Spec=rfc5280.certificateExtensionsMap[extn['extnID']], + decodeOpenTypes=True) + assert not rest + assert ev.prettyPrint() + assert der_encode(ev) == extn['extnValue'] + + for attr in ev: + if attr['type'] == rfc5755.id_at_clearance: + spid = rfc3114.id_tsp_TEST_Whirlpool + catid = rfc3114.id_tsp_TEST_Whirlpool_Categories + assert attr['values'][0]['policyId'] == spid + for cat in attr['values'][0]['securityCategories']: + assert cat['type'] == catid + assert u'USE ONLY' in cat['value'][0] + clearance_found = True + + assert clearance_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()) diff --git a/tests/test_rfc5913.py b/tests/test_rfc5913.py new file mode 100644 index 0000000..f140d92 --- /dev/null +++ b/tests/test_rfc5913.py @@ -0,0 +1,114 @@ +# +# 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 rfc5913 +from pyasn1_modules import rfc5755 +from pyasn1_modules import rfc3114 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class ClearanceTestCase(unittest.TestCase): + cert_pem_text = """\ +MIIDhzCCAw6gAwIBAgIJAKWzVCgbsG5GMAoGCCqGSM49BAMDMD8xCzAJBgNVBAYT +AlVTMQswCQYDVQQIDAJWQTEQMA4GA1UEBwwHSGVybmRvbjERMA8GA1UECgwIQm9n +dXMgQ0EwHhcNMTkxMTAyMTg0MjE4WhcNMjAxMTAxMTg0MjE4WjBmMQswCQYDVQQG +EwJVUzELMAkGA1UECBMCVkExEDAOBgNVBAcTB0hlcm5kb24xEDAOBgNVBAoTB0V4 +YW1wbGUxDDAKBgNVBAsTA1BDQTEYMBYGA1UEAxMPcGNhLmV4YW1wbGUuY29tMHYw +EAYHKoZIzj0CAQYFK4EEACIDYgAEPf5vbgAqbE5dn6wbiCx4sCCcn1BKSrHmCfiW +C9QLSGVNGHifQwPt9odGXjRiQ7QwpZ2wRD6Z91v+fk85XXLE3kJQCQdPIHFUY5EM +pvS7T6u6xrmwnlVpUURPTOxfc55Oo4IBrTCCAakwHQYDVR0OBBYEFCbqJQ8LMiAo +pNdaCo3/Ldy9f1RlMG8GA1UdIwRoMGaAFPI12zQE2qVV8r1pA5mwYuziFQjBoUOk +QTA/MQswCQYDVQQGEwJVUzELMAkGA1UECAwCVkExEDAOBgNVBAcMB0hlcm5kb24x +ETAPBgNVBAoMCEJvZ3VzIENBggkA6JHWBpFPzvIwDwYDVR0TAQH/BAUwAwEB/zAL +BgNVHQ8EBAMCAYYwQgYJYIZIAYb4QgENBDUWM1RoaXMgY2VydGlmaWNhdGUgY2Fu +bm90IGJlIHRydXN0ZWQgZm9yIGFueSBwdXJwb3NlLjAVBgNVHSAEDjAMMAoGCCsG +AQUFBw0CMAoGA1UdNgQDAgECMIGRBggrBgEFBQcBFQSBhDCBgTBZBgsqhkiG9w0B +CRAHAwMCBeAxRjBEgAsqhkiG9w0BCRAHBIE1MDMMF0xBVyBERVBBUlRNRU5UIFVT +RSBPTkxZDBhIVU1BTiBSRVNPVVJDRVMgVVNFIE9OTFkwEQYLKoZIhvcNAQkQBwID +AgTwMBEGCyqGSIb3DQEJEAcBAwIF4DAKBggqhkjOPQQDAwNnADBkAjAZSD+BVqzc +1l0fDoH3LwixjxvtddBHbJsM5yBek4U9b2yWL2KEmwV02fTgof3AjDECMCTsksmx +5f3i5DSYfe9Q1heJlEJLd1hgZmfvUYNnCU3WrdmYzyoNdNTbg7ZFMoxsXw== +""" + + def setUp(self): + self.asn1Spec = rfc5280.Certificate() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.cert_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + cat_value_found = False + for extn in asn1Object['tbsCertificate']['extensions']: + if extn['extnID'] == rfc5913.id_pe_clearanceConstraints: + assert extn['extnID'] in rfc5280.certificateExtensionsMap.keys() + ev, rest = der_decode(extn['extnValue'], + asn1Spec=rfc5280.certificateExtensionsMap[extn['extnID']]) + assert not rest + assert ev.prettyPrint() + assert der_encode(ev) == extn['extnValue'] + + for c in ev: + if c['policyId'] == rfc3114.id_tsp_TEST_Whirlpool: + for sc in c['securityCategories']: + assert sc['type'] in rfc5755.securityCategoryMap.keys() + scv, rest = der_decode(sc['value'], + asn1Spec=rfc5755.securityCategoryMap[sc['type']]) + for cat in scv: + assert u'USE ONLY' in cat + cat_value_found = True + + assert cat_value_found + + def testOpenTypes(self): + substrate = pem.readBase64fromText(self.cert_pem_text) + asn1Object, rest = der_decode(substrate, + asn1Spec=self.asn1Spec, + decodeOpenTypes=True) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + cat_value_found = False + for extn in asn1Object['tbsCertificate']['extensions']: + if extn['extnID'] == rfc5913.id_pe_clearanceConstraints: + assert extn['extnID'] in rfc5280.certificateExtensionsMap.keys() + ev, rest = der_decode(extn['extnValue'], + asn1Spec=rfc5280.certificateExtensionsMap[extn['extnID']], + decodeOpenTypes=True) + assert not rest + assert ev.prettyPrint() + assert der_encode(ev) == extn['extnValue'] + + for c in ev: + if c['policyId'] == rfc3114.id_tsp_TEST_Whirlpool: + for sc in c['securityCategories']: + assert sc['type'] in rfc5755.securityCategoryMap.keys() + for cat in sc['value']: + assert u'USE ONLY' in cat + cat_value_found = True + + assert cat_value_found + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/tests/test_rfc5917.py b/tests/test_rfc5917.py new file mode 100644 index 0000000..459895d --- /dev/null +++ b/tests/test_rfc5917.py @@ -0,0 +1,112 @@ +# +# 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 rfc5917 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class ClearanceSponsorTestCase(unittest.TestCase): + cert_pem_text = """\ +MIID1DCCA1qgAwIBAgIUUc1IQGJpeYQ0XwOS2ZmVEb3aeZ0wCgYIKoZIzj0EAwMw +ZjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlZBMRAwDgYDVQQHEwdIZXJuZG9uMRAw +DgYDVQQKEwdFeGFtcGxlMQwwCgYDVQQLEwNQQ0ExGDAWBgNVBAMTD3BjYS5leGFt +cGxlLmNvbTAeFw0xOTExMDUyMjIwNDZaFw0yMDExMDQyMjIwNDZaMIGSMQswCQYD +VQQGEwJVUzELMAkGA1UECBMCVkExEDAOBgNVBAcTB0hlcm5kb24xEDAOBgNVBAoT +B0V4YW1wbGUxIjAgBgNVBAsTGUh1bWFuIFJlc291cmNlIERlcGFydG1lbnQxDTAL +BgNVBAMTBEZyZWQxHzAdBgkqhkiG9w0BCQEWEGZyZWRAZXhhbXBsZS5jb20wdjAQ +BgcqhkjOPQIBBgUrgQQAIgNiAAQObFslQ2EBP0xlDJ3sRnsNaqm/woQgKpBispSx +XxK5bWUVpfnWsZnjLWhtDuPcu1BcBlM2g7gwL/aw8nUSIK3D8Ja9rTUQQXc3zxnk +cl8+8znNXHMGByRjPUH87C+TOrqjggGaMIIBljAdBgNVHQ4EFgQU5m711OqFDNGR +SWMOSzTXjpTLIFUwbwYDVR0jBGgwZoAUJuolDwsyICik11oKjf8t3L1/VGWhQ6RB +MD8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJWQTEQMA4GA1UEBwwHSGVybmRvbjER +MA8GA1UECgwIQm9ndXMgQ0GCCQCls1QoG7BuRjAPBgNVHRMBAf8EBTADAQH/MAsG +A1UdDwQEAwIBhjBCBglghkgBhvhCAQ0ENRYzVGhpcyBjZXJ0aWZpY2F0ZSBjYW5u +b3QgYmUgdHJ1c3RlZCBmb3IgYW55IHB1cnBvc2UuMBUGA1UdIAQOMAwwCgYIKwYB +BQUHDQIwCgYDVR02BAMCAQIwfwYDVR0JBHgwdjBJBgNVBDcxQjBABgsqhkiG9w0B +CRAHAwMCBeAxLTArgAsqhkiG9w0BCRAHBIEcMBoMGEhVTUFOIFJFU09VUkNFUyBV +U0UgT05MWTApBglghkgBZQIBBUQxHAwaSHVtYW4gUmVzb3VyY2VzIERlcGFydG1l +bnQwCgYIKoZIzj0EAwMDaAAwZQIwVh/RypULFgPpAN0I7OvuMomRWnm/Hea3Hk8P +tTRz2Zai8iYat7oeAmGVgMhSXy2jAjEAuJW4l/CFatBy4W/lZ7gS3weBdBa5WEDI +FFMC7GjGtCeLtXYqWfBnRdK26dOaHLB2 +""" + + def setUp(self): + self.asn1Spec = rfc5280.Certificate() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.cert_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + cs = rfc5917.DirectoryString() + cs['utf8String'] = u'Human Resources Department' + encoded_cs = der_encode(cs) + + clearance_sponsor_found = False + for extn in asn1Object['tbsCertificate']['extensions']: + if extn['extnID'] == rfc5280.id_ce_subjectDirectoryAttributes: + assert extn['extnID'] in rfc5280.certificateExtensionsMap.keys() + ev, rest = der_decode(extn['extnValue'], + asn1Spec=rfc5280.certificateExtensionsMap[extn['extnID']]) + assert not rest + assert ev.prettyPrint() + assert der_encode(ev) == extn['extnValue'] + + for attr in ev: + if attr['type'] == rfc5917.id_clearanceSponsor: + assert attr['values'][0] == encoded_cs + clearance_sponsor_found = True + + assert clearance_sponsor_found + + def testOpenTypes(self): + substrate = pem.readBase64fromText(self.cert_pem_text) + asn1Object, rest = der_decode(substrate, + asn1Spec=self.asn1Spec, + decodeOpenTypes=True) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + clearance_sponsor_found = False + for extn in asn1Object['tbsCertificate']['extensions']: + if extn['extnID'] == rfc5280.id_ce_subjectDirectoryAttributes: + assert extn['extnID'] in rfc5280.certificateExtensionsMap.keys() + ev, rest = der_decode(extn['extnValue'], + asn1Spec=rfc5280.certificateExtensionsMap[extn['extnID']], + decodeOpenTypes=True) + assert not rest + assert ev.prettyPrint() + assert der_encode(ev) == extn['extnValue'] + + for attr in ev: + if attr['type'] == rfc5917.id_clearanceSponsor: + hrd = u'Human Resources Department' + assert attr['values'][0]['utf8String'] == hrd + clearance_sponsor_found = True + + assert clearance_sponsor_found + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) -- cgit v1.2.3 From b61bbfac575cfaa2a58812f66670fd0c04063e7c Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Mon, 11 Nov 2019 20:19:25 +0100 Subject: Fix `rfc4210.PKIHeader.explicitTag` tagging --- pyasn1_modules/rfc4210.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyasn1_modules/rfc4210.py b/pyasn1_modules/rfc4210.py index 45926d3..4d01a33 100644 --- a/pyasn1_modules/rfc4210.py +++ b/pyasn1_modules/rfc4210.py @@ -743,11 +743,11 @@ class PKIHeader(univ.Sequence): namedtype.OptionalNamedType('generalInfo', univ.SequenceOf( componentType=InfoTypeAndValue().subtype( - sizeSpec=constraint.ValueSizeConstraint(1, MAX), - explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 8) + sizeSpec=constraint.ValueSizeConstraint(1, MAX) ) - ) - ) + ).subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 8)) + ) ) -- cgit v1.2.3 From bab198ce0c1dd61c46c112684b0ebb0688ea9eec Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Mon, 11 Nov 2019 16:09:38 -0500 Subject: Add support for RFC 4043 and RFC 7585 (#104) --- CHANGES.txt | 3 ++ pyasn1_modules/rfc4043.py | 43 ++++++++++++++++ pyasn1_modules/rfc7585.py | 50 +++++++++++++++++++ tests/__main__.py | 2 + tests/test_rfc4043.py | 120 ++++++++++++++++++++++++++++++++++++++++++++ tests/test_rfc7585.py | 124 ++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 342 insertions(+) create mode 100644 pyasn1_modules/rfc4043.py create mode 100644 pyasn1_modules/rfc7585.py create mode 100644 tests/test_rfc4043.py create mode 100644 tests/test_rfc7585.py diff --git a/CHANGES.txt b/CHANGES.txt index 49172ae..0838373 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -33,6 +33,9 @@ Revision 0.2.8, released XX-XX-2019 - Add RFC5913 providing Clearance Attribute and Authority Clearance Constraints Certificate Extension - Add RFC5917 providing Clearance Sponsor Attribute +- Add RFC4043 providing Internet X.509 PKI Permanent Identifier +- Add RFC7585 providing Network Access Identifier (NAI) Realm Name + for Certificates Revision 0.2.7, released 09-10-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc4043.py b/pyasn1_modules/rfc4043.py new file mode 100644 index 0000000..cf0a801 --- /dev/null +++ b/pyasn1_modules/rfc4043.py @@ -0,0 +1,43 @@ +# +# 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 +# +# Internet X.509 Public Key Infrastructure Permanent Identifier +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc4043.txt +# + +from pyasn1.type import char +from pyasn1.type import namedtype +from pyasn1.type import univ + +from pyasn1_modules import rfc5280 + + +id_pkix = univ.ObjectIdentifier((1, 3, 6, 1, 5, 5, 7, )) + +id_on = id_pkix + (8, ) + +id_on_permanentIdentifier = id_on + (3, ) + + +class PermanentIdentifier(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('identifierValue', char.UTF8String()), + namedtype.OptionalNamedType('assigner', univ.ObjectIdentifier()) + ) + + +# Map of Other Name OIDs to Other Name is added to the +# ones that are in rfc5280.py + +_anotherNameMapUpdate = { + id_on_permanentIdentifier: PermanentIdentifier(), +} + +rfc5280.anotherNameMap.update(_anotherNameMapUpdate) diff --git a/pyasn1_modules/rfc7585.py b/pyasn1_modules/rfc7585.py new file mode 100644 index 0000000..b3fd4a5 --- /dev/null +++ b/pyasn1_modules/rfc7585.py @@ -0,0 +1,50 @@ +# +# This file is part of pyasn1-modules software. +# +# Created by Russ Housley with some assistance from asn1ate v.0.6.0. +# +# Copyright (c) 2019, Vigil Security, LLC +# License: http://snmplabs.com/pyasn1/license.html +# +# Network Access Identifier (NAI) Realm Name for Certificates +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc7585.txt +# + +from pyasn1.type import char +from pyasn1.type import constraint +from pyasn1.type import univ + +from pyasn1_modules import rfc5280 + + +# NAI Realm Name for Certificates + +id_pkix = univ.ObjectIdentifier('1.3.6.1.5.5.7') + +id_on = id_pkix + (8, ) + +id_on_naiRealm = id_on + (8, ) + + +ub_naiRealm_length = univ.Integer(255) + + +class NAIRealm(char.UTF8String): + subtypeSpec = constraint.ValueSizeConstraint(1, ub_naiRealm_length) + + +naiRealm = rfc5280.AnotherName() +naiRealm['type-id'] = id_on_naiRealm +naiRealm['value'] = NAIRealm() + + +# Map of Other Name OIDs to Other Name is added to the +# ones that are in rfc5280.py + +_anotherNameMapUpdate = { + id_on_naiRealm: NAIRealm(), +} + +rfc5280.anotherNameMap.update(_anotherNameMapUpdate) diff --git a/tests/__main__.py b/tests/__main__.py index 479cd09..a019541 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -35,6 +35,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc3770.suite', 'tests.test_rfc3779.suite', 'tests.test_rfc3852.suite', + 'tests.test_rfc4043.suite', 'tests.test_rfc4055.suite', 'tests.test_rfc4073.suite', 'tests.test_rfc4108.suite', @@ -81,6 +82,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc7292.suite', 'tests.test_rfc7296.suite', 'tests.test_rfc7508.suite', + 'tests.test_rfc7585.suite', 'tests.test_rfc7633.suite', 'tests.test_rfc7773.suite', 'tests.test_rfc7894.suite', diff --git a/tests/test_rfc4043.py b/tests/test_rfc4043.py new file mode 100644 index 0000000..0637b87 --- /dev/null +++ b/tests/test_rfc4043.py @@ -0,0 +1,120 @@ +# +# 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 rfc4043 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class PermIdCertTestCase(unittest.TestCase): + cert_pem_text = """\ +MIIDDTCCApOgAwIBAgIJAKWzVCgbsG5HMAoGCCqGSM49BAMDMD8xCzAJBgNVBAYT +AlVTMQswCQYDVQQIDAJWQTEQMA4GA1UEBwwHSGVybmRvbjERMA8GA1UECgwIQm9n +dXMgQ0EwHhcNMTkxMTEwMDA0MDIyWhcNMjAxMTA5MDA0MDIyWjBNMQswCQYDVQQG +EwJVUzELMAkGA1UECBMCVkExEDAOBgNVBAcTB0hlcm5kb24xEDAOBgNVBAoTB0V4 +YW1wbGUxDTALBgNVBAMTBEdhaWwwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQBoktg +/68xL+uEQaWBoHyOjw8EMLeMEng3R2H7yiEzTGoaMJgPOKvSfzB2P0paHYPL+B5y +Gc0CK5EHRujMl9ljH+Wydpk57rKBLo1ZzpWUS6anLGIkWs1sOakcgGGr7hGjggFL +MIIBRzAdBgNVHQ4EFgQU1pCNZuMzfEaJ9GGhH7RKy6Mvz+cwbwYDVR0jBGgwZoAU +8jXbNATapVXyvWkDmbBi7OIVCMGhQ6RBMD8xCzAJBgNVBAYTAlVTMQswCQYDVQQI +DAJWQTEQMA4GA1UEBwwHSGVybmRvbjERMA8GA1UECgwIQm9ndXMgQ0GCCQDokdYG +kU/O8jAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBhjBCBglghkgBhvhCAQ0E +NRYzVGhpcyBjZXJ0aWZpY2F0ZSBjYW5ub3QgYmUgdHJ1c3RlZCBmb3IgYW55IHB1 +cnBvc2UuMFMGA1UdEQRMMEqgNgYIKwYBBQUHCAOgKjAoDBs4MjYyMDgtNDE3MDI4 +LTU0ODE5NS0yMTUyMzMGCSsGAQQBgaxgMIEQZ2FpbEBleGFtcGxlLmNvbTAKBggq +hkjOPQQDAwNoADBlAjBT+36Y/LPaGSu+61P7kR97M8jAjtH5DtUwrWR02ChshvYJ +x0bpZq3PJaO0WlBgFicCMQCf+67wSvjxxtjI/OAg4t8NQIJW1LcehSXizlPDc772 +/FC5OiUAxO+iFaSVMeDFsCo= +""" + + def setUp(self): + self.asn1Spec = rfc5280.Certificate() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.cert_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + perm_id_oid = rfc4043.id_on_permanentIdentifier + assigner_oid = univ.ObjectIdentifier('1.3.6.1.4.1.22112.48') + permanent_identifier_found = False + + for extn in asn1Object['tbsCertificate']['extensions']: + if extn['extnID'] == rfc5280.id_ce_subjectAltName: + extnValue, rest = der_decode(extn['extnValue'], + asn1Spec=rfc5280.SubjectAltName()) + assert not rest + assert extnValue.prettyPrint() + assert der_encode(extnValue) == extn['extnValue'] + + for gn in extnValue: + if gn['otherName'].hasValue(): + assert gn['otherName']['type-id'] == perm_id_oid + onValue, rest = der_decode(gn['otherName']['value'], + asn1Spec=rfc4043.PermanentIdentifier()) + assert not rest + assert onValue.prettyPrint() + assert der_encode(onValue) == gn['otherName']['value'] + assert onValue['assigner'] == assigner_oid + permanent_identifier_found = True + + assert permanent_identifier_found + + def testOpenTypes(self): + substrate = pem.readBase64fromText(self.cert_pem_text) + asn1Object, rest = der_decode(substrate, + asn1Spec=self.asn1Spec, + decodeOpenTypes=True) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + perm_id_oid = rfc4043.id_on_permanentIdentifier + assigner_oid = univ.ObjectIdentifier('1.3.6.1.4.1.22112.48') + permanent_identifier_found = False + + for extn in asn1Object['tbsCertificate']['extensions']: + if extn['extnID'] == rfc5280.id_ce_subjectAltName: + extnValue, rest = der_decode(extn['extnValue'], + asn1Spec=rfc5280.SubjectAltName(), + decodeOpenTypes=True) + assert not rest + assert extnValue.prettyPrint() + assert der_encode(extnValue) == extn['extnValue'] + + for gn in extnValue: + if gn['otherName'].hasValue(): + on = gn['otherName'] + assert on['type-id'] == perm_id_oid + assert on['value']['assigner'] == assigner_oid + permanent_identifier_found = True + + assert permanent_identifier_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()) diff --git a/tests/test_rfc7585.py b/tests/test_rfc7585.py new file mode 100644 index 0000000..ecbd72a --- /dev/null +++ b/tests/test_rfc7585.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 rfc7585 + +try: + import unittest2 as unittest +except ImportError: + import unittest + + +class NAIRealmCertTestCase(unittest.TestCase): + cert_pem_text = """\ +MIIEZzCCA0+gAwIBAgIBBzANBgkqhkiG9w0BAQsFADCBkjELMAkGA1UEBhMCRlIx +DzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRQwEgYDVQQKDAtF +eGFtcGxlIEluYzEgMB4GCSqGSIb3DQEJARYRYWRtaW5AZXhhbXBsZS5vcmcxJjAk +BgNVBAMMHUV4YW1wbGUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTE5MTExMTE4 +MDQyMVoXDTIwMDExMDE4MDQyMVowezELMAkGA1UEBhMCRlIxDzANBgNVBAgMBlJh +ZGl1czEUMBIGA1UECgwLRXhhbXBsZSBJbmMxIzAhBgNVBAMMGkV4YW1wbGUgU2Vy +dmVyIENlcnRpZmljYXRlMSAwHgYJKoZIhvcNAQkBFhFhZG1pbkBleGFtcGxlLm9y +ZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM9HqbuyWpsTMKo739Dm +DwmQo2HUkNdQYbvsB+e7ILsw8fWa2qnsF1CoRr/1bcZqXUR1To/QbHse7xSMZH9t +F7rdlDMc7QtgdwVfn8TiL3hCg5LSE8iaBzfJUjrts/V5WOByP1DwJVM7W3Va/5dN +oOiceVeC7ThghMlwIx/wN5cy78a8fPYV2FvPR6e+U2HG35zaIv2PizYcliF/QmZG +gnw4Q9dYC1Lw/ogVBZBALlv+/MuGheb/xIuL8lu1PFZ0YbW65WLD9Cx4wvytAke7 +tKlhL/Kd4OBSeOY3OYmpxbc1gEUmFoLTlZesY2NP9Jyl5mGsIHtPdvVkh/tSBy8o +VLUCAwEAAaOB3TCB2jAJBgNVHRMEAjAAMAsGA1UdDwQEAwIF4DATBgNVHSUEDDAK +BggrBgEFBQcDATA2BgNVHR8ELzAtMCugKaAnhiVodHRwOi8vd3d3LmV4YW1wbGUu +Y29tL2V4YW1wbGVfY2EuY3JsMDcGCCsGAQUFBwEBBCswKTAnBggrBgEFBQcwAYYb +aHR0cDovL3d3dy5leGFtcGxlLm9yZy9vY3NwMDoGA1UdEQQzMDGCEnJhZGl1cy5l +eGFtcGxlLm9yZ6AbBggrBgEFBQcICKAPDA0qLmV4YW1wbGUuY29tMA0GCSqGSIb3 +DQEBCwUAA4IBAQBOhtH2Jpi0b0MZ8FBKTqDl44rIHL1rHG2mW/YYmRI4jZo8kFhA +yWm/T8ZpdaotJgRqbQbeXvTXIg4/JNFheyLG4yLOzS1esdMAYDD5EN9/dXE++jND +/wrfPU+QtTgzAjkgFDKuqO7gr1/vSizxLYTWLKBPRHhiQo7GGlEC6/CPb38x4mfQ +5Y9DsKCp6BEZu+LByCho/HMDzcIPCdtXRX7Fs8rtX4/zRpVIdm6D+vebuo6CwRKp +mIljfssCvZjb9YIxSVDmA/6Lapqsfsfo922kb+MTXvPrq2ynPx8LrPDrxKc8maYc +Jiw8B0yjkokwojxyRGftMT8uxNjWQVsMDbxl +""" + + def setUp(self): + self.asn1Spec = rfc5280.Certificate() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.cert_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + nai_realm_oid = rfc7585.id_on_naiRealm + nai_realm_found = False + + for extn in asn1Object['tbsCertificate']['extensions']: + if extn['extnID'] == rfc5280.id_ce_subjectAltName: + extnValue, rest = der_decode(extn['extnValue'], + asn1Spec=rfc5280.SubjectAltName()) + assert not rest + assert extnValue.prettyPrint() + assert der_encode(extnValue) == extn['extnValue'] + + for gn in extnValue: + if gn['otherName'].hasValue(): + assert gn['otherName']['type-id'] == nai_realm_oid + onValue, rest = der_decode(gn['otherName']['value'], + asn1Spec=rfc7585.NAIRealm()) + assert not rest + assert onValue.prettyPrint() + assert der_encode(onValue) == gn['otherName']['value'] + assert 'example' in onValue + nai_realm_found = True + + assert nai_realm_found + + def testOpenTypes(self): + substrate = pem.readBase64fromText(self.cert_pem_text) + asn1Object, rest = der_decode(substrate, + asn1Spec=self.asn1Spec, + decodeOpenTypes=True) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + nai_realm_oid = rfc7585.id_on_naiRealm + nai_realm_found = False + + for extn in asn1Object['tbsCertificate']['extensions']: + if extn['extnID'] == rfc5280.id_ce_subjectAltName: + extnValue, rest = der_decode(extn['extnValue'], + asn1Spec=rfc5280.SubjectAltName(), + decodeOpenTypes=True) + assert not rest + assert extnValue.prettyPrint() + assert der_encode(extnValue) == extn['extnValue'] + + for gn in extnValue: + if gn['otherName'].hasValue(): + assert gn['otherName']['type-id'] == nai_realm_oid + assert 'example' in gn['otherName']['value'] + nai_realm_found = True + + assert nai_realm_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()) -- cgit v1.2.3 From f2c70a401ccb1af92267f285f43c09ea9cd093e7 Mon Sep 17 00:00:00 2001 From: Russ Housley Date: Fri, 15 Nov 2019 13:13:04 -0500 Subject: Add support for RFC 4334 and fix errata for RFC 3770 (#105) --- CHANGES.txt | 3 ++ pyasn1_modules/rfc3770.py | 14 +++++++- pyasn1_modules/rfc4334.py | 75 +++++++++++++++++++++++++++++++++++++++++ tests/__main__.py | 1 + tests/test_rfc4334.py | 85 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 pyasn1_modules/rfc4334.py create mode 100644 tests/test_rfc4334.py diff --git a/CHANGES.txt b/CHANGES.txt index 0838373..1b5e1ac 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -36,6 +36,9 @@ Revision 0.2.8, released XX-XX-2019 - Add RFC4043 providing Internet X.509 PKI Permanent Identifier - Add RFC7585 providing Network Access Identifier (NAI) Realm Name for Certificates +- Update RFC3770 to support openType for attributes and reported errata +- Add RFC4334 providing Certificate Extensions and Attributes for + Authentication in PPP and Wireless LAN Networks Revision 0.2.7, released 09-10-2019 ----------------------------------- diff --git a/pyasn1_modules/rfc3770.py b/pyasn1_modules/rfc3770.py index a347fb3..3fefe1d 100644 --- a/pyasn1_modules/rfc3770.py +++ b/pyasn1_modules/rfc3770.py @@ -11,6 +11,7 @@ # # ASN.1 source from: # https://www.rfc-editor.org/rfc/rfc3770.txt +# https://www.rfc-editor.org/errata/eid234 # from pyasn1.type import constraint @@ -49,8 +50,9 @@ SSIDList.subtypeSpec=constraint.ValueSizeConstraint(1, MAX) # Wireless LAN SSID Attribute Certificate Attribute # Uses same syntax as the certificate extension: SSIDList +# Correction for https://www.rfc-editor.org/errata/eid234 -id_aca_wlanSSID = univ.ObjectIdentifier('1.3.6.1.5.5.7.10.6') +id_aca_wlanSSID = univ.ObjectIdentifier('1.3.6.1.5.5.7.10.7') # Map of Certificate Extension OIDs to Extensions @@ -61,3 +63,13 @@ _certificateExtensionsMap = { } rfc5280.certificateExtensionsMap.update(_certificateExtensionsMap) + + +# Map of AttributeType OIDs to AttributeValue added to the +# ones that are in rfc5280.py + +_certificateAttributesMapUpdate = { + id_aca_wlanSSID: SSIDList(), +} + +rfc5280.certificateAttributesMap.update(_certificateAttributesMapUpdate) diff --git a/pyasn1_modules/rfc4334.py b/pyasn1_modules/rfc4334.py new file mode 100644 index 0000000..44cd31b --- /dev/null +++ b/pyasn1_modules/rfc4334.py @@ -0,0 +1,75 @@ +# +# 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 +# +# Certificate Extensions and Attributes Supporting Authentication +# in PPP and Wireless LAN Networks +# +# ASN.1 source from: +# https://www.rfc-editor.org/rfc/rfc4334.txt +# + +from pyasn1.type import constraint +from pyasn1.type import univ + +from pyasn1_modules import rfc5280 + +MAX = float('inf') + + +# OID Arcs + +id_pe = univ.ObjectIdentifier('1.3.6.1.5.5.7.1') + +id_kp = univ.ObjectIdentifier('1.3.6.1.5.5.7.3') + +id_aca = univ.ObjectIdentifier('1.3.6.1.5.5.7.10') + + +# Extended Key Usage Values + +id_kp_eapOverPPP = id_kp + (13, ) + +id_kp_eapOverLAN = id_kp + (14, ) + + +# Wireless LAN SSID Extension + +id_pe_wlanSSID = id_pe + (13, ) + +class SSID(univ.OctetString): + constraint.ValueSizeConstraint(1, 32) + + +class SSIDList(univ.SequenceOf): + componentType = SSID() + subtypeSpec=constraint.ValueSizeConstraint(1, MAX) + + +# Wireless LAN SSID Attribute Certificate Attribute + +id_aca_wlanSSID = id_aca + (7, ) + + +# Map of Certificate Extension OIDs to Extensions +# To be added to the ones that are in rfc5280.py + +_certificateExtensionsMap = { + id_pe_wlanSSID: SSIDList(), +} + +rfc5280.certificateExtensionsMap.update(_certificateExtensionsMap) + + +# Map of AttributeType OIDs to AttributeValue added to the +# ones that are in rfc5280.py + +_certificateAttributesMapUpdate = { + id_aca_wlanSSID: SSIDList(), +} + +rfc5280.certificateAttributesMap.update(_certificateAttributesMapUpdate) diff --git a/tests/__main__.py b/tests/__main__.py index a019541..dd0bc32 100644 --- a/tests/__main__.py +++ b/tests/__main__.py @@ -41,6 +41,7 @@ suite = unittest.TestLoader().loadTestsFromNames( 'tests.test_rfc4108.suite', 'tests.test_rfc4210.suite', 'tests.test_rfc4211.suite', + 'tests.test_rfc4334.suite', 'tests.test_rfc4985.suite', 'tests.test_rfc5035.suite', 'tests.test_rfc5083.suite', diff --git a/tests/test_rfc4334.py b/tests/test_rfc4334.py new file mode 100644 index 0000000..ff4b252 --- /dev/null +++ b/tests/test_rfc4334.py @@ -0,0 +1,85 @@ +# +# 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.compat.octets import str2octs + +from pyasn1_modules import pem +from pyasn1_modules import rfc5280 +from pyasn1_modules import rfc4334 + +try: + import unittest2 as unittest +except ImportError: + import unittest + +class CertificateTestCase(unittest.TestCase): + cert_pem_text = """\ +MIICqzCCAjCgAwIBAgIJAKWzVCgbsG4/MAoGCCqGSM49BAMDMD8xCzAJBgNVBAYT +AlVTMQswCQYDVQQIDAJWQTEQMA4GA1UEBwwHSGVybmRvbjERMA8GA1UECgwIQm9n +dXMgQ0EwHhcNMTkwNzE5MTk0MjQ3WhcNMjAwNzE4MTk0MjQ3WjBjMQswCQYDVQQG +EwJVUzELMAkGA1UECBMCVkExEDAOBgNVBAcTB0hlcm5kb24xGzAZBgNVBAoTElZp +Z2lsIFNlY3VyaXR5IExMQzEYMBYGA1UEAxMPZWFwLmV4YW1wbGUuY29tMHYwEAYH +KoZIzj0CAQYFK4EEACIDYgAEMMbnIp2BUbuyMgH9HhNHrh7VBy7ql2lBjGRSsefR +Wa7+vCWs4uviW6On4eem5YoP9/UdO7DaIL+/J9/3DJHERI17oFxn+YWiE4JwXofy +QwfSu3cncVNMqpiDjEkUGGvBo4HTMIHQMAsGA1UdDwQEAwIHgDBCBglghkgBhvhC +AQ0ENRYzVGhpcyBjZXJ0aWZpY2F0ZSBjYW5ub3QgYmUgdHJ1c3RlZCBmb3IgYW55 +IHB1cnBvc2UuMB0GA1UdDgQWBBSDjPGr7M742rsE4oQGwBvGvllZ+zAfBgNVHSME +GDAWgBTyNds0BNqlVfK9aQOZsGLs4hUIwTAeBggrBgEFBQcBDQQSMBAEB0V4YW1w +bGUEBUJvZ3VzMB0GA1UdJQQWMBQGCCsGAQUFBwMOBggrBgEFBQcDDTAKBggqhkjO +PQQDAwNpADBmAjEAmCPZnnlUQOKlcOIIOgFrRCkOqO0ESs+dobYwAc2rFCBtQyP7 +C3N00xkX8WZZpiAZAjEAi1Z5+nGbJg5eJTc8fwudutN/HNwJEIS6mHds9kfcy26x +DAlVlhox680Jxy5J8Pkx +""" + + def setUp(self): + self.asn1Spec = rfc5280.Certificate() + + def testDerCodec(self): + substrate = pem.readBase64fromText(self.cert_pem_text) + asn1Object, rest = der_decode(substrate, asn1Spec=self.asn1Spec) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + def testOpenTypes(self): + substrate = pem.readBase64fromText(self.cert_pem_text) + asn1Object, rest = der_decode(substrate, + asn1Spec=self.asn1Spec, + decodeOpenTypes=True) + assert not rest + assert asn1Object.prettyPrint() + assert der_encode(asn1Object) == substrate + + extn_list = [ ] + for extn in asn1Object['tbsCertificate']['extensions']: + extn_list.append(extn['extnID']) + if extn['extnID'] in rfc5280.certificateExtensionsMap.keys(): + extnValue, rest = der_decode(extn['extnValue'], + asn1Spec=rfc5280.certificateExtensionsMap[extn['extnID']]) + assert der_encode(extnValue) == extn['extnValue'] + + if extn['extnID'] == rfc4334.id_pe_wlanSSID: + assert str2octs('Example') in extnValue + + if extn['extnID'] == rfc5280.id_ce_extKeyUsage: + assert rfc4334.id_kp_eapOverLAN in extnValue + assert rfc4334.id_kp_eapOverPPP in extnValue + + assert rfc4334.id_pe_wlanSSID in extn_list + assert rfc5280.id_ce_extKeyUsage in extn_list + + +suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite) -- cgit v1.2.3 From db4a4cb5fe87ae4cd6dd486b08a825406c4b5520 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sat, 16 Nov 2019 18:28:57 +0100 Subject: Remove pyasn1 Github master dependency Since pyasn1 0.4.8 is release, master dependency is no longer required. Also, bump pyasn1 upper constraint to <0.6.0 in anticipation of 0.5.x release which should remain reasonably backward compatible. --- requirements.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index a56cbc1..943e6cd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1 @@ -#pyasn1>=0.4.7,<0.5.0 -# temporarily depend on git master to test bew constraints --e git://github.com/etingof/pyasn1.git#egg=pyasn1 +pyasn1>=0.4.7,<0.6.0 -- cgit v1.2.3 From 87e78d03b7b7f9f35afdf7fe85fe55410e5be4ab Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Sat, 16 Nov 2019 18:33:13 +0100 Subject: Release 0.2.8 --- CHANGES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 1b5e1ac..bb82416 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,5 @@ -Revision 0.2.8, released XX-XX-2019 +Revision 0.2.8, released 16-11-2019 ----------------------------------- - Improve test routines for modules that use certificate extensions -- cgit v1.2.3