bug 1413336 - (3/7) fix pycert.py and pykey.py with respect to pyasn1/pyasn1-modules updates r?Cykesiopka draft
authorDavid Keeler <dkeeler@mozilla.com>
Wed, 08 Nov 2017 13:23:17 -0800
changeset 699147 f1c3a9415723fa27724cc480df48511e72b9c3d7
parent 699146 0369c1eca34f1061c80e1148c867972b629fa23d
child 699148 4a77588ca6343a73f2338283bad252c3ec21a1b4
push id89479
push userbmo:dkeeler@mozilla.com
push dateThu, 16 Nov 2017 18:28:32 +0000
reviewersCykesiopka
bugs1413336
milestone59.0a1
bug 1413336 - (3/7) fix pycert.py and pykey.py with respect to pyasn1/pyasn1-modules updates r?Cykesiopka MozReview-Commit-ID: CsxOF7LdEHB
security/manager/ssl/tests/unit/pycert.py
security/manager/ssl/tests/unit/pycms.py
security/manager/ssl/tests/unit/pykey.py
security/manager/ssl/tests/unit/requirements.txt
--- a/security/manager/ssl/tests/unit/pycert.py
+++ b/security/manager/ssl/tests/unit/pycert.py
@@ -80,52 +80,28 @@ TLSFeature values can either consist of 
 feature value (see rfc7633 for more information).
 
 If a serial number is not explicitly specified, it is automatically
 generated based on the contents of the certificate.
 """
 
 from pyasn1.codec.der import decoder
 from pyasn1.codec.der import encoder
-from pyasn1.type import constraint, namedtype, tag, univ, useful
+from pyasn1.type import constraint, tag, univ, useful
 from pyasn1_modules import rfc2459
 from struct import pack
 import base64
 import datetime
 import hashlib
 import re
 import sys
 
 import pyct
 import pykey
 
-# The GeneralSubtree definition in pyasn1_modules.rfc2459 is incorrect.
-# Where this definition uses a DefaultedNamedType, pyasn1_modules uses
-# a NamedType, which results in the default value being explicitly
-# encoded, which is incorrect for DER.
-class GeneralSubtree(univ.Sequence):
-    componentType = namedtype.NamedTypes(
-        namedtype.NamedType('base', rfc2459.GeneralName()),
-        namedtype.DefaultedNamedType('minimum', rfc2459.BaseDistance(0).subtype(
-            implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
-        namedtype.OptionalNamedType('maximum', rfc2459.BaseDistance().subtype(
-            implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1)))
-    )
-
-
-# The NameConstraints definition in pyasn1_modules.rfc2459 is incorrect.
-# excludedSubtrees has a tag value of 1, not 0.
-class NameConstraints(univ.Sequence):
-    componentType = namedtype.NamedTypes(
-        namedtype.OptionalNamedType('permittedSubtrees', rfc2459.GeneralSubtrees().subtype(
-            implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))),
-        namedtype.OptionalNamedType('excludedSubtrees', rfc2459.GeneralSubtrees().subtype(
-            implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1)))
-    )
-
 
 class Error(Exception):
     """Base class for exceptions in this module."""
     pass
 
 
 class UnknownBaseError(Error):
     """Base class for handling unexpected input in this module."""
@@ -239,17 +215,17 @@ class InvalidSerialNumber(Error):
 
     def __str__(self):
         return repr(self.value)
 
 
 def getASN1Tag(asn1Type):
     """Helper function for returning the base tag value of a given
     type from the pyasn1 package"""
-    return asn1Type.baseTagSet.getBaseTag().asTuple()[2]
+    return asn1Type.tagSet.baseTag.tagId
 
 def stringToAccessDescription(string):
     """Helper function that takes a string representing a URI
     presumably identifying an OCSP authority information access
     location. Returns an AccessDescription usable by pyasn1."""
     accessMethod = rfc2459.id_ad_ocsp
     accessLocation = rfc2459.GeneralName()
     accessLocation.setComponentByName('uniformResourceIdentifier', string)
@@ -317,37 +293,46 @@ def stringToDN(string, tag=None):
 
 def stringToAlgorithmIdentifiers(string):
     """Helper function that converts a description of an algorithm
     to a representation usable by the pyasn1 package and a hash
     algorithm constant for use by pykey."""
     algorithmIdentifier = rfc2459.AlgorithmIdentifier()
     algorithmType = None
     algorithm = None
+    # We add Null parameters for RSA only
+    addParameters = False
     if string == 'sha1WithRSAEncryption':
         algorithmType = pykey.HASH_SHA1
         algorithm = rfc2459.sha1WithRSAEncryption
+        addParameters = True
     elif string == 'sha256WithRSAEncryption':
         algorithmType = pykey.HASH_SHA256
         algorithm = univ.ObjectIdentifier('1.2.840.113549.1.1.11')
+        addParameters = True
     elif string == 'md5WithRSAEncryption':
         algorithmType = pykey.HASH_MD5
         algorithm = rfc2459.md5WithRSAEncryption
+        addParameters = True
     elif string == 'ecdsaWithSHA256':
         algorithmType = pykey.HASH_SHA256
         algorithm = univ.ObjectIdentifier('1.2.840.10045.4.3.2')
     elif string == 'ecdsaWithSHA384':
         algorithmType = pykey.HASH_SHA384
         algorithm = univ.ObjectIdentifier('1.2.840.10045.4.3.3')
     elif string == 'ecdsaWithSHA512':
         algorithmType = pykey.HASH_SHA512
         algorithm = univ.ObjectIdentifier('1.2.840.10045.4.3.4')
     else:
         raise UnknownAlgorithmTypeError(string)
-    algorithmIdentifier.setComponentByName('algorithm', algorithm)
+    algorithmIdentifier['algorithm'] = algorithm
+    if addParameters:
+        # Directly setting parameters to univ.Null doesn't currently work.
+        nullEncapsulated = encoder.encode(univ.Null())
+        algorithmIdentifier['parameters'] = univ.Any(nullEncapsulated)
     return (algorithmIdentifier, algorithmType)
 
 def datetimeToTime(dt):
     """Takes a datetime object and returns an rfc2459.Time object with
     that time as its value as a GeneralizedTime"""
     time = rfc2459.Time()
     time.setComponentByName('generalTime', useful.GeneralizedTime(dt.strftime('%Y%m%d%H%M%SZ')))
     return time
@@ -528,30 +513,28 @@ class Certificate(object):
     def addBasicConstraints(self, basicConstraints, critical):
         cA = basicConstraints.split(',')[0]
         pathLenConstraint = basicConstraints.split(',')[1]
         basicConstraintsExtension = rfc2459.BasicConstraints()
         basicConstraintsExtension.setComponentByName('cA', cA == 'cA')
         if pathLenConstraint:
             pathLenConstraintValue = \
                 univ.Integer(int(pathLenConstraint)).subtype(
-                    subtypeSpec=constraint.ValueRangeConstraint(0, 64))
+                    subtypeSpec=constraint.ValueRangeConstraint(0, float('inf')))
             basicConstraintsExtension.setComponentByName('pathLenConstraint',
                                                          pathLenConstraintValue)
         self.addExtension(rfc2459.id_ce_basicConstraints, basicConstraintsExtension, critical)
 
     def addKeyUsage(self, keyUsage, critical):
         keyUsageExtension = rfc2459.KeyUsage(keyUsage)
         self.addExtension(rfc2459.id_ce_keyUsage, keyUsageExtension, critical)
 
     def keyPurposeToOID(self, keyPurpose):
         if keyPurpose == 'serverAuth':
-            # the OID for id_kp_serverAuth is incorrect in the
-            # pyasn1-modules implementation
-            return univ.ObjectIdentifier('1.3.6.1.5.5.7.3.1')
+            return rfc2459.id_kp_serverAuth
         if keyPurpose == 'clientAuth':
             return rfc2459.id_kp_clientAuth
         if keyPurpose == 'codeSigning':
             return rfc2459.id_kp_codeSigning
         if keyPurpose == 'emailProtection':
             return rfc2459.id_kp_emailProtection
         if keyPurpose == 'nsSGC':
             return univ.ObjectIdentifier('2.16.840.1.113730.4.1')
@@ -596,17 +579,17 @@ class Certificate(object):
                 policyOID = '2.5.29.32.0'
             policy = rfc2459.PolicyInformation()
             policyIdentifier = rfc2459.CertPolicyId(policyOID)
             policy.setComponentByName('policyIdentifier', policyIdentifier)
             policies.setComponentByPosition(pos, policy)
         self.addExtension(rfc2459.id_ce_certificatePolicies, policies, critical)
 
     def addNameConstraints(self, constraints, critical):
-        nameConstraints = NameConstraints()
+        nameConstraints = rfc2459.NameConstraints()
         if constraints.startswith('permitted:'):
             (subtreesType, subtreesTag) = ('permittedSubtrees', 0)
         elif constraints.startswith('excluded:'):
             (subtreesType, subtreesTag) = ('excludedSubtrees', 1)
         else:
             raise UnknownNameConstraintsSpecificationError(constraints)
         generalSubtrees = rfc2459.GeneralSubtrees().subtype(
             implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, subtreesTag))
@@ -614,17 +597,17 @@ class Certificate(object):
         for pos, name in enumerate(subtrees.split(',')):
             generalName = rfc2459.GeneralName()
             if '/' in name:
                 directoryName = stringToDN(name,
                                            tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4))
                 generalName.setComponentByName('directoryName', directoryName)
             else:
                 generalName.setComponentByName('dNSName', name)
-            generalSubtree = GeneralSubtree()
+            generalSubtree = rfc2459.GeneralSubtree()
             generalSubtree.setComponentByName('base', generalName)
             generalSubtrees.setComponentByPosition(pos, generalSubtree)
         nameConstraints.setComponentByName(subtreesType, generalSubtrees)
         self.addExtension(rfc2459.id_ce_nameConstraints, nameConstraints, critical)
 
     def addNSCertType(self, certType, critical):
         if certType != 'sslServer':
             raise UnknownNSCertTypeError(certType)
--- a/security/manager/ssl/tests/unit/pycms.py
+++ b/security/manager/ssl/tests/unit/pycms.py
@@ -108,17 +108,19 @@ class CMS(object):
         if pykeyHash == pykey.HASH_SHA1:
             oidString = '1.3.14.3.2.26'
         elif pykeyHash == pykey.HASH_SHA256:
             oidString = '2.16.840.1.101.3.4.2.1'
         else:
             raise pykey.UnknownHashAlgorithmError(pykeyHash)
         algorithmIdentifier = rfc2459.AlgorithmIdentifier()
         algorithmIdentifier['algorithm'] = univ.ObjectIdentifier(oidString)
-        algorithmIdentifier['parameters'] = univ.Null()
+        # Directly setting parameters to univ.Null doesn't currently work.
+        nullEncapsulated = encoder.encode(univ.Null())
+        algorithmIdentifier['parameters'] = univ.Any(nullEncapsulated)
         return algorithmIdentifier
 
     def buildSignerInfo(self, certificate, pykeyHash, digestValue):
         """Given a pyasn1 certificate, a pykey hash identifier
         and a hash value, creates a SignerInfo with the
         appropriate values."""
         signerInfo = rfc2315.SignerInfo()
         signerInfo['version'] = 1
--- a/security/manager/ssl/tests/unit/pykey.py
+++ b/security/manager/ssl/tests/unit/pykey.py
@@ -533,17 +533,19 @@ class RSAKey(object):
         else:
             raise UnknownKeySpecificationError(specification)
 
     def toDER(self):
         privateKeyInfo = PrivateKeyInfo()
         privateKeyInfo.setComponentByName('version', 0)
         algorithmIdentifier = rfc2459.AlgorithmIdentifier()
         algorithmIdentifier.setComponentByName('algorithm', rfc2459.rsaEncryption)
-        algorithmIdentifier.setComponentByName('parameters', univ.Null())
+        # Directly setting parameters to univ.Null doesn't currently work.
+        nullEncapsulated = encoder.encode(univ.Null())
+        algorithmIdentifier['parameters'] = univ.Any(nullEncapsulated)
         privateKeyInfo.setComponentByName('privateKeyAlgorithm', algorithmIdentifier)
         rsaPrivateKey = RSAPrivateKey()
         rsaPrivateKey.setComponentByName('version', 0)
         rsaPrivateKey.setComponentByName('modulus', self.RSA_N)
         rsaPrivateKey.setComponentByName('publicExponent', self.RSA_E)
         rsaPrivateKey.setComponentByName('privateExponent', self.RSA_D)
         rsaPrivateKey.setComponentByName('prime1', self.RSA_P)
         rsaPrivateKey.setComponentByName('prime2', self.RSA_Q)
@@ -564,17 +566,19 @@ class RSAKey(object):
         output += '\n-----END PRIVATE KEY-----'
         return output
 
     def asSubjectPublicKeyInfo(self):
         """Returns a subject public key info representing
         this key for use by pyasn1."""
         algorithmIdentifier = rfc2459.AlgorithmIdentifier()
         algorithmIdentifier.setComponentByName('algorithm', rfc2459.rsaEncryption)
-        algorithmIdentifier.setComponentByName('parameters', univ.Null())
+        # Directly setting parameters to univ.Null doesn't currently work.
+        nullEncapsulated = encoder.encode(univ.Null())
+        algorithmIdentifier['parameters'] = univ.Any(nullEncapsulated)
         spki = rfc2459.SubjectPublicKeyInfo()
         spki.setComponentByName('algorithm', algorithmIdentifier)
         rsaKey = RSAPublicKey()
         rsaKey.setComponentByName('N', univ.Integer(self.RSA_N))
         rsaKey.setComponentByName('E', univ.Integer(self.RSA_E))
         subjectPublicKey = univ.BitString(byteStringToHexifiedBitString(encoder.encode(rsaKey)))
         spki.setComponentByName('subjectPublicKey', subjectPublicKey)
         return spki
--- a/security/manager/ssl/tests/unit/requirements.txt
+++ b/security/manager/ssl/tests/unit/requirements.txt
@@ -1,6 +1,6 @@
 lxml
-pyasn1 == 0.1.7
-pyasn1_modules == 0.0.5
+pyasn1 == 0.3.7
+pyasn1_modules == 0.1.5
 ecc
 mock
 rsa