Ejemplo n.º 1
0
 def decode_qualifier(cls, oid, qualifier_data):
     decoded_qualifier = None
     if oid == OIDDB.X509ExtensionCertificatePolicyQualifierOIDs.inverse(
             "id-qt-cps"):
         decoded_qualifier = ASN1Tools.safe_decode(
             qualifier_data,
             asn1_spec=(rfc5280.CPSuri(), ASN1Models.RelaxedCPSuri()))
     elif oid == OIDDB.X509ExtensionCertificatePolicyQualifierOIDs.inverse(
             "id-qt-unotice"):
         decoded_qualifier = ASN1Tools.safe_decode(
             qualifier_data,
             asn1_spec=(rfc5280.UserNotice(),
                        ASN1Models.RelaxedUserNotice()))
     return decoded_qualifier
Ejemplo n.º 2
0
    def from_subject_pubkey_info(cls, pk_alg, params_asn1, pubkey_data):
        params = ASN1Tools.safe_decode(params_asn1, asn1_spec=ECParameters())

        accessible_parameters = {}
        if params.asn1 is not None:
            accessible_parameters["curve_source"] = params.asn1.getName()
            if accessible_parameters["curve_source"] == "namedCurve":
                # Named curve
                curve_oid = OID.from_asn1(params.asn1.getComponent())
                curve = CurveDB().instantiate(oid=curve_oid)
                accessible_parameters.update({
                    "curve_oid": curve_oid,
                    "curve": curve,
                })
            elif accessible_parameters["curve_source"] == "specifiedCurve":
                # Explicit curve or implicit curve
                curve = EllipticCurve.from_asn1(params.asn1.getComponent())
                accessible_parameters.update({
                    "curve": curve,
                })
            else:
                # Implicit curve
                pass

        if accessible_parameters.get("curve") is not None:
            pk_point = curve.decode_point(pubkey_data)
            accessible_parameters.update({
                "x": pk_point.x,
                "y": pk_point.y,
            })
        else:
            pk_point = None

        return cls(accessible_parameters=accessible_parameters,
                   decoding_details=[params, pk_point])
Ejemplo n.º 3
0
 def test_safedecode_ok_nomodel(self):
     der_data = bytes.fromhex("02 01   7b")  # 123
     asn1_details = ASN1Tools.safe_decode(der_data)
     self.assertEqual(asn1_details.flags, set())
     self.assertEqual(asn1_details.tail, bytes())
     self.assertEqual(asn1_details.asn1, Integer(123))
     self.assertEqual(asn1_details.generic_asn1, None)
     self.assertEqual(asn1_details.encoded_der, None)
     self.assertEqual(asn1_details.original_der, der_data)
     self.assertEqual(asn1_details.model_index, 0)
Ejemplo n.º 4
0
 def test_safedecode_undecodable(self):
     der_data = bytes.fromhex("aa bb cc")
     asn1_details = ASN1Tools.safe_decode(der_data)
     self.assertEqual(asn1_details.flags, set(["undecodable"]))
     self.assertEqual(asn1_details.tail, None)
     self.assertEqual(asn1_details.asn1, None)
     self.assertEqual(asn1_details.generic_asn1, None)
     self.assertEqual(asn1_details.encoded_der, None)
     self.assertEqual(asn1_details.original_der, der_data)
     self.assertEqual(asn1_details.model_index, None)
Ejemplo n.º 5
0
 def __init__(self, oid, critical, data):
     assert (isinstance(oid, OID))
     assert (isinstance(critical, bool))
     assert (isinstance(data, bytes))
     self._oid = oid
     self._critical = critical
     self._data = data
     self._detailed_asn1 = None
     spec = self._ASN1_MODEL() if (self._ASN1_MODEL is not None) else None
     self._detailed_asn1 = ASN1Tools.safe_decode(self.data, asn1_spec=spec)
     self._decode_hook()
Ejemplo n.º 6
0
 def test_safedecode_ok_nonder_trailing_data(self):
     der_data = bytes.fromhex("02 02 007b aabb")  # 123 non-DER
     asn1_details = ASN1Tools.safe_decode(der_data,
                                          asn1_spec=RestrictiveASN1Model())
     self.assertEqual(asn1_details.flags, set(["non_der", "trailing_data"]))
     self.assertEqual(asn1_details.asn1, Integer(123))
     self.assertEqual(asn1_details.tail, bytes.fromhex("aabb"))
     self.assertEqual(asn1_details.generic_asn1, None)
     self.assertEqual(asn1_details.encoded_der, bytes.fromhex("02 01 7b"))
     self.assertEqual(asn1_details.original_der, der_data)
     self.assertEqual(asn1_details.model_index, 0)
Ejemplo n.º 7
0
 def test_safedecode_ok_trailing_data(self):
     der_data = bytes.fromhex("02 02 01c8 aabb")  # 456
     asn1_details = ASN1Tools.safe_decode(der_data,
                                          asn1_spec=BasicASN1Model())
     self.assertEqual(asn1_details.flags, set(["trailing_data"]))
     self.assertEqual(asn1_details.asn1, Integer(456))
     self.assertEqual(asn1_details.tail, bytes.fromhex("aa bb"))
     self.assertEqual(asn1_details.generic_asn1, None)
     self.assertEqual(asn1_details.encoded_der, None)
     self.assertEqual(asn1_details.original_der, der_data)
     self.assertEqual(asn1_details.model_index, 0)
Ejemplo n.º 8
0
 def test_safedecode_fail_model_generic(self):
     der_data = bytes.fromhex("02 02 01c8")  # 456
     asn1_details = ASN1Tools.safe_decode(der_data,
                                          asn1_spec=RestrictiveASN1Model())
     self.assertEqual(asn1_details.flags, set(["unexpected_type"]))
     self.assertEqual(asn1_details.tail, bytes())
     self.assertEqual(asn1_details.asn1, None)
     self.assertEqual(asn1_details.generic_asn1, Integer(456))
     self.assertEqual(asn1_details.encoded_der, None)
     self.assertEqual(asn1_details.original_der, der_data)
     self.assertEqual(asn1_details.model_index, None)
Ejemplo n.º 9
0
 def _decode_hook(self):
     self._attributes = []
     if self.asn1 is not None:
         for attribute in self.asn1:
             self._attributes.append(
                 self._Attribute(attribute_type=OID.from_asn1(
                     attribute["type"]),
                                 values=[
                                     ASN1Tools.safe_decode(value)
                                     for value in attribute["values"]
                                 ]))
Ejemplo n.º 10
0
 def test_safedecode_wrong_type(self):
     der_data = bytes.fromhex("04 06 666f6f626172")  # "foobar" OctetString
     asn1_details = ASN1Tools.safe_decode(der_data,
                                          asn1_spec=RestrictiveASN1Model())
     self.assertEqual(asn1_details.flags, set(["unexpected_type"]))
     self.assertEqual(asn1_details.asn1, None)
     self.assertEqual(asn1_details.tail, bytes())
     self.assertEqual(asn1_details.generic_asn1, OctetString(b"foobar"))
     self.assertEqual(asn1_details.encoded_der, None)
     self.assertEqual(asn1_details.original_der, der_data)
     self.assertEqual(asn1_details.model_index, None)
Ejemplo n.º 11
0
 def test_safedecode_non_encodable(self):
     der_data = bytes.fromhex(
         "17 11 31 36 30 32 31 37 31 30 32 37 30 30 2b 30 31 30 30"
     )  # UTCTime("160217102700+0100")
     asn1_details = ASN1Tools.safe_decode(der_data)
     self.assertEqual(asn1_details.flags, set(["non_encodable"]))
     self.assertEqual(asn1_details.asn1, UTCTime("160217102700+0100"))
     self.assertEqual(asn1_details.tail, bytes())
     self.assertEqual(asn1_details.generic_asn1, None)
     self.assertEqual(asn1_details.encoded_der, None)
     self.assertEqual(asn1_details.original_der, der_data)
     self.assertEqual(asn1_details.model_index, 0)
Ejemplo n.º 12
0
 def test_safedecode_ok_model_fallback(self):
     der_data = bytes.fromhex("02 02 01c8")  # 456
     asn1_details = ASN1Tools.safe_decode(der_data,
                                          asn1_spec=(RestrictiveASN1Model(),
                                                     BasicASN1Model()))
     self.assertEqual(asn1_details.flags, set(["fallback"]))
     self.assertEqual(asn1_details.tail, bytes())
     self.assertEqual(asn1_details.asn1, Integer(456))
     self.assertEqual(asn1_details.generic_asn1, None)
     self.assertEqual(asn1_details.encoded_der, None)
     self.assertEqual(asn1_details.original_der, der_data)
     self.assertEqual(asn1_details.model_index, 1)
Ejemplo n.º 13
0
    def from_subject_pubkey_info(cls, pk_alg, params_asn1, pubkey_data):
        params = ASN1Tools.safe_decode(params_asn1,
                                       asn1_spec=rfc3279.Dss_Parms())
        pubkey = ASN1Tools.safe_decode(pubkey_data,
                                       asn1_spec=rfc3279.DSAPublicKey())

        accessible_parameters = {}
        if params.asn1 is not None:
            accessible_parameters.update({
                "p": int(params.asn1["p"]),
                "q": int(params.asn1["q"]),
                "g": int(params.asn1["g"]),
            })

        if pubkey.asn1 is not None:
            accessible_parameters.update({
                "pubkey": int(pubkey.asn1),
            })

        return cls(accessible_parameters=accessible_parameters,
                   decoding_details=[params, pubkey])
Ejemplo n.º 14
0
 def __init__(self, der_data, source=None):
     assert (isinstance(der_data, bytes))
     self._der_data = der_data
     self._asn1_details = ASN1Tools.safe_decode(
         der_data, asn1_spec=self._ASN1_MODEL())
     if self._asn1_details.asn1 is None:
         raise UnexpectedFileContentException(
             "Could not decode ASN.1 blob of length %d as %s." %
             (len(der_data), self.__class__.__name__))
     self._hashval = hashlib.sha256(self._der_data).digest()
     self._source = source
     self._post_decode_hook()
Ejemplo n.º 15
0
    def from_subject_pubkey_info(cls, pk_alg, params_asn1, pubkey_data):
        pubkey = ASN1Tools.safe_decode(pubkey_data,
                                       asn1_spec=rfc2437.RSAPublicKey())

        if pubkey.asn1 is not None:
            accessible_parameters = {
                "n": int(pubkey.asn1["modulus"]),
                "e": int(pubkey.asn1["publicExponent"]),
                "params": params_asn1 if params_asn1.hasValue() else None,
            }
        else:
            accessible_parameters = None
        return cls(accessible_parameters=accessible_parameters,
                   decoding_details=pubkey)
Ejemplo n.º 16
0
    def _analyze_rsa_pss_signature_params(self, signature_alg_params):
        judgements = SecurityJudgements()
        asn1_details = ASN1Tools.safe_decode(
            signature_alg_params, asn1_spec=ASN1Models.RSASSA_PSS_Params())
        judgements += self._DER_VALIDATOR_RSAPSS_PARAMETERS.validate(
            asn1_details)
        if asn1_details.asn1 is not None:
            rsapss = RSAPSSParameters.from_asn1(asn1_details.asn1)
            if rsapss.hash_algorithm is None:
                judgements += SecurityJudgement(
                    JudgementCode.X509Cert_Signature_HashFunction_Unknown,
                    "Certificate has unknown hash function for use in RSA-PSS, OID %s. Cannot make security determination for that part."
                    % (rsapss.hash_algorithm_oid),
                    commonness=Commonness.HIGHLY_UNUSUAL,
                    compatibility=Compatibility.LIMITED_SUPPORT)

            if rsapss.mask_algorithm is None:
                judgements += SecurityJudgement(
                    JudgementCode.
                    X509Cert_PublicKey_RSA_RSA_PSS_UnknownMaskFunction,
                    "Certificate has unknown mask function for use in RSA-PSS, OID %s."
                    % (rsapss.mask_algorithm_oid),
                    commonness=Commonness.HIGHLY_UNUSUAL,
                    compatibility=Compatibility.LIMITED_SUPPORT)

            if rsapss.mask_hash_algorithm is None:
                judgements += SecurityJudgement(
                    JudgementCode.X509Cert_Signature_HashFunction_Unknown,
                    "Certificate has unknown mask hash function for use in RSA-PSS, OID %s."
                    % (rsapss.mask_hash_algorithm_oid),
                    commonness=Commonness.HIGHLY_UNUSUAL,
                    compatibility=Compatibility.LIMITED_SUPPORT)

            if rsapss.trailer_field_value is None:
                judgements += SecurityJudgement(
                    JudgementCode.
                    X509Cert_PublicKey_RSA_RSA_PSS_UnknownTrailerField,
                    "Certificate has unknown trailer field for use in RSA-PSS, trailer field ID %d."
                    % (rsapss.trailer_field),
                    commonness=Commonness.HIGHLY_UNUSUAL,
                    compatibility=Compatibility.LIMITED_SUPPORT)

            if (rsapss.hash_algorithm is not None) and (
                    rsapss.mask_hash_algorithm is not None) and (
                        rsapss.hash_algorithm != rsapss.mask_hash_algorithm):
                standard = RFCReference(
                    rfcno=3447,
                    sect="8.1",
                    verb="RECOMMEND",
                    text=
                    "Therefore, it is recommended that the EMSA-PSS mask generation function be based on the same hash function."
                )
                judgements += SecurityJudgement(
                    JudgementCode.
                    X509Cert_PublicKey_RSA_RSA_PSS_MultipleHashFunctions,
                    "RSA-PSS uses hash function %s for hashing, but %s for masking. This is discouraged."
                    % (rsapss.hash_algorithm.name,
                       rsapss.mask_hash_algorithm.name),
                    commonness=Commonness.HIGHLY_UNUSUAL,
                    compatibility=Compatibility.LIMITED_SUPPORT,
                    standard=standard)

            if rsapss.salt_length < 0:
                judgements += SecurityJudgement(
                    JudgementCode.
                    X509Cert_PublicKey_RSA_RSAPSS_InvalidSaltLength,
                    "Certificate has negative salt length for use in RSA-PSS, %d bytes specified."
                    % (rsapss.salt_length),
                    commonness=Commonness.HIGHLY_UNUSUAL,
                    compatibility=Compatibility.STANDARDS_DEVIATION,
                    bits=0)
            elif rsapss.salt_length == 0:
                judgements += SecurityJudgement(
                    JudgementCode.X509Cert_PublicKey_RSA_RSA_PSS_NoSaltUsed,
                    "RSA-PSS does not use any salt.",
                    commonness=Commonness.HIGHLY_UNUSUAL)
            elif rsapss.salt_length < 16:
                judgements += SecurityJudgement(
                    JudgementCode.X509Cert_PublicKey_RSA_RSA_PSS_ShortSaltUsed,
                    "RSA-PSS uses a comparatively short salt value of %d bits."
                    % (rsapss.salt_length * 8),
                    commonness=Commonness.UNUSUAL)
            else:
                judgements += SecurityJudgement(
                    JudgementCode.
                    X509Cert_PublicKey_RSA_RSA_PSS_SaltLengthInBytes,
                    "RSA-PSS uses a salt of %d bits." %
                    (rsapss.salt_length * 8))
            return (rsapss.hash_algorithm, judgements)
        else:
            return (None, judgements)
Ejemplo n.º 17
0
    def analyze(self,
                signature_alg_oid,
                signature_alg_params,
                signature,
                root_cert=None):
        judgements = SecurityJudgements()
        signature_alg = SignatureAlgorithms.lookup("oid", signature_alg_oid)
        if signature_alg is None:
            judgements += SecurityJudgement(
                JudgementCode.X509Cert_Signature_Function_Unknown,
                "Certificate has unknown signature algorithm with OID %s. Cannot make security determination."
                % (signature_alg_oid),
                commonness=Commonness.HIGHLY_UNUSUAL,
                compatibility=Compatibility.LIMITED_SUPPORT)
            result = {
                "name": str(signature_alg_oid),
                "pretty": str(signature_alg_oid),
                "security": judgements,
            }
            return result

        if isinstance(signature_alg.value.oid, (tuple, list)):
            # Have more than one OID for this
            if signature_alg.value.oid[0] != signature_alg_oid:
                judgements += SecurityJudgement(
                    JudgementCode.X509Cert_Signature_Function_DeprecatedOID,
                    "Signature algorithm uses alternate OID %s for algorithm %s. Preferred OID would be %s."
                    % (signature_alg_oid, signature_alg.name,
                       signature_alg.value.oid[0]),
                    commonness=Commonness.HIGHLY_UNUSUAL,
                    compatibility=Compatibility.LIMITED_SUPPORT)

        if signature_alg.value.hash_fnc is not None:
            # Signature algorithm already implies a concrete hash function, already done.
            hash_fnc = signature_alg.value.hash_fnc
        else:
            # Signature algorithms depends and is not implied.
            (hash_fnc, new_judgements) = self._determine_hash_function(
                signature_alg, signature_alg_params)
            judgements += new_judgements

        if signature_alg.value.sig_fnc == SignatureFunctions.ecdsa:
            # Decode ECDSA signature
            asn1_details = ASN1Tools.safe_decode(
                signature, asn1_spec=rfc3279.ECDSA_Sig_Value())
            judgements += self._DER_VALIDATOR_ECDSA_SIGNATURE.validate(
                asn1_details)
            if (asn1_details.asn1 is not None) and (root_cert is not None):
                if root_cert.pubkey.pk_alg.value.cryptosystem == Cryptosystems.ECC_ECDSA:
                    # Check that this is really a potential parent CA certificate
                    ca_curve = root_cert.pubkey.curve
                    hweight_analysis = NumberTheory.hamming_weight_analysis(
                        int(asn1_details.asn1["r"]),
                        min_bit_length=ca_curve.field_bits)
                    if not hweight_analysis.plausibly_random:
                        judgements += SecurityJudgement(
                            JudgementCode.
                            X509Cert_Signature_ECDSA_R_BitBiasPresent,
                            "Hamming weight of ECDSA signature R parameter is %d at bitlength %d, but expected a weight between %d and %d when randomly chosen; this is likely not coincidential."
                            %
                            (hweight_analysis.hweight, hweight_analysis.bitlen,
                             hweight_analysis.rnd_min_hweight,
                             hweight_analysis.rnd_max_hweight),
                            commonness=Commonness.HIGHLY_UNUSUAL)
                    hweight_analysis = NumberTheory.hamming_weight_analysis(
                        int(asn1_details.asn1["s"]),
                        min_bit_length=ca_curve.field_bits)
                    if not hweight_analysis.plausibly_random:
                        judgements += SecurityJudgement(
                            JudgementCode.
                            X509Cert_Signature_ECDSA_S_BitBiasPresent,
                            "Hamming weight of ECDSA signature S parameter is %d at bitlength %d, but expected a weight between %d and %d when randomly chosen; this is likely not coincidential."
                            %
                            (hweight_analysis.hweight, hweight_analysis.bitlen,
                             hweight_analysis.rnd_min_hweight,
                             hweight_analysis.rnd_max_hweight),
                            commonness=Commonness.HIGHLY_UNUSUAL)

        elif signature_alg.value.sig_fnc == SignatureFunctions.dsa:
            # Decode DSA signature
            asn1_details = ASN1Tools.safe_decode(
                signature, asn1_spec=rfc3279.Dss_Sig_Value())
            judgements += self._DER_VALIDATOR_DSA_SIGNATURE.validate(
                asn1_details)
            if (asn1_details.asn1 is not None) and (root_cert is not None):
                if root_cert is not None:
                    if root_cert.pubkey.pk_alg.value.cryptosystem == Cryptosystems.DSA:
                        field_width = root_cert.pubkey.q.bit_length()

                        hweight_analysis = NumberTheory.hamming_weight_analysis(
                            int(asn1_details.asn1["r"]),
                            min_bit_length=field_width)
                        if not hweight_analysis.plausibly_random:
                            judgements += SecurityJudgement(
                                JudgementCode.
                                X509Cert_Signature_DSA_R_BitBiasPresent,
                                "Hamming weight of DSA signature R parameter is %d at bitlength %d, but expected a weight between %d and %d when randomly chosen; this is likely not coincidential."
                                % (hweight_analysis.hweight,
                                   hweight_analysis.bitlen,
                                   hweight_analysis.rnd_min_hweight,
                                   hweight_analysis.rnd_max_hweight),
                                commonness=Commonness.HIGHLY_UNUSUAL)

                        hweight_analysis = NumberTheory.hamming_weight_analysis(
                            int(asn1_details.asn1["s"]),
                            min_bit_length=field_width)
                        if not hweight_analysis.plausibly_random:
                            judgements += SecurityJudgement(
                                JudgementCode.
                                X509Cert_Signature_DSA_S_BitBiasPresent,
                                "Hamming weight of DSA signature S parameter is %d at bitlength %d, but expected a weight between %d and %d when randomly chosen; this is likely not coincidential."
                                % (hweight_analysis.hweight,
                                   hweight_analysis.bitlen,
                                   hweight_analysis.rnd_min_hweight,
                                   hweight_analysis.rnd_max_hweight),
                                commonness=Commonness.HIGHLY_UNUSUAL)

        result = {
            "name":
            signature_alg.name,
            "sig_fnc":
            self.algorithm("sig_fnc").analyze(signature_alg.value.sig_fnc),
            "security":
            judgements,
        }
        if hash_fnc is not None:
            result.update({
                "pretty":
                signature_alg.value.sig_fnc.value.pretty_name + " with " +
                hash_fnc.value.pretty_name,
                "hash_fnc":
                self.algorithm("hash_fnc").analyze(hash_fnc),
            })
        else:
            result.update({
                "pretty":
                "%s with undetermined hash function" %
                (signature_alg.value.sig_fnc.value.pretty_name)
            })
        return result