def _decode_hook(self): self._mappings = [] if self.asn1 is not None: for mapping in self.asn1: self._mappings.append( self._PolicyMapping(issuer_policy=OID.from_asn1( mapping["issuerDomainPolicy"]), subject_policy=OID.from_asn1( mapping["subjectDomainPolicy"])))
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])
def _decode_hook(self): self._methods = [] if self.asn1 is not None: for item in self.asn1: oid = OID.from_asn1(item["accessMethod"]) location = item["accessLocation"] self._methods.append((oid, location))
def from_asn1(cls, asn1): rdn_elements = [] for attribute_type_and_value in asn1: attribute_type_oid = OID.from_asn1(attribute_type_and_value[0]) attribute_value = bytes(attribute_type_and_value[1]) rdn_elements.append((attribute_type_oid, attribute_value)) return cls(rdn_elements)
def signature_algorithm(self): algorithm_oid = OID.from_asn1( self.asn1["signatureAlgorithm"]["algorithm"]) sig_alg = SignatureAlgorithms.lookup("oid", algorithm_oid) if sig_alg is None: raise UnknownAlgorithmException("Unknown signature OID %s." % (algorithm_oid)) return sig_alg
def _get_extensions(self): result = [ ] if self.asn1["tbsCertificate"]["extensions"] is not None: for extension in self.asn1["tbsCertificate"]["extensions"]: oid = OID.from_asn1(extension["extnID"]) critical = bool(extension["critical"]) value = bytes(extension["extnValue"]) result.append(X509ExtensionRegistry.create(oid, critical, value)) return X509Extensions(result)
def _load_db_data(): json_data = JSONTools.load_internal("x509sak.data.ecc", "curves.json") result = { OID.from_str(oid): parameters for (oid, parameters) in json_data.items() } for (oid, parameters) in result.items(): parameters["oid"] = oid return result
def _decode_hook(self): self._description = [] if self.asn1 is not None: for access_description in self.asn1: self._description.append( self._AccessDescription( method=OID.from_asn1( access_description["accessMethod"]), location=ASN1GeneralNameWrapper.from_asn1( access_description["accessLocation"])))
def _decode_hook(self): self._policies = [] if self.asn1 is None: return for item in self.asn1: policy_oid = OID.from_asn1(item["policyIdentifier"]) qualifiers = [] for qualifier in item["policyQualifiers"]: qualifier_oid = OID.from_asn1(qualifier["policyQualifierId"]) qualifier_data = bytes(qualifier["qualifier"]) qualifier = self._CertificatePolicyQualifier( oid=qualifier_oid, qualifier_data=qualifier_data, decoded_qualifier=self.decode_qualifier( qualifier_oid, qualifier_data)) qualifiers.append(qualifier) policy = self._CertificatePolicy(oid=policy_oid, qualifiers=tuple(qualifiers)) self._policies.append(policy)
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"] ]))
def from_asn1(cls, asn1): constructor_arguments = {} if asn1["hashAlgorithm"].hasValue(): constructor_arguments["hash_algorithm_oid"] = OID.from_asn1( asn1["hashAlgorithm"]["algorithm"]) else: # Default for RSASSA-PSS is SHA-1 constructor_arguments[ "hash_algorithm_oid"] = OIDDB.HashFunctions.inverse("sha1") if asn1["maskGenAlgorithm"].hasValue(): constructor_arguments["mask_algorithm_oid"] = OID.from_asn1( asn1["maskGenAlgorithm"]["algorithm"]) (decoded_mask_parameters, tail) = pyasn1.codec.der.decoder.decode( asn1["maskGenAlgorithm"]["parameters"], asn1Spec=ASN1Models.HashAlgorithm()) constructor_arguments["mask_hash_algorithm_oid"] = OID.from_asn1( decoded_mask_parameters["algorithm"]) else: # Default for RSASSA-PSS is mgf1 with SHA-1 constructor_arguments[ "mask_algorithm_oid"] = OIDDB.RSAPSSMaskGenerationAlgorithm.inverse( "mgf1") constructor_arguments[ "mask_hash_algorithm_oid"] = OIDDB.HashFunctions.inverse( "sha1") if asn1["saltLength"].hasValue(): constructor_arguments["salt_length"] = int(asn1["saltLength"]) else: constructor_arguments["salt_length"] = 20 if asn1["trailerField"].hasValue(): constructor_arguments["trailer_field"] = int(asn1["trailerField"]) else: constructor_arguments["trailer_field"] = 1 return cls(**constructor_arguments)
def _post_decode_hook(self): if self.asn1["parameters"] is None: raise InvalidInputException( "ECC private key does not contain curve OID. Cannot proceed.") if self.asn1["publicKey"] is None: raise InvalidInputException( "ECC private key does not contain public key. Cannot proceed.") curve_oid = OID.from_asn1(self.asn1["parameters"]) self._curve = CurveDB().instantiate(oid=curve_oid) self._d = int.from_bytes(self.asn1["privateKey"], byteorder="big") (self._x, self._y) = ECCTools.decode_enc_pubkey( ASN1Tools.bitstring2bytes(self.asn1["publicKey"]))
def _post_decode_hook(self): alg_oid = OID.from_asn1(self.asn1["algorithm"]["algorithm"]) self._pk_alg = PublicKeyAlgorithms.lookup("oid", alg_oid) if self._pk_alg is None: self._key_decoding_error = "Unable to determine public key algorithm for OID %s." % ( alg_oid) return if self._pk_alg not in self._HANDLERS_BY_PK_ALG: self._key_decoding_error = "Unable to determine public key handler for public key algorithm %s." % ( self._pk_alg.name) return handler_class = self._HANDLERS_BY_PK_ALG[self._pk_alg] key_data = ASN1Tools.bitstring2bytes(self.asn1["subjectPublicKey"]) self._key = handler_class.from_subject_pubkey_info( self._pk_alg, self.asn1["algorithm"]["parameters"], key_data) self._key_decoding_error = None
def _post_decode_hook(self): if self.asn1["privateKeyAlgorithm"]["algorithm"] is None: raise InvalidInputException( "EdDSA private key does not contain curve OID. Cannot proceed." ) curve_oid = OID.from_asn1( self.asn1["privateKeyAlgorithm"]["algorithm"]) pk_alg = PublicKeyAlgorithms.lookup("oid", curve_oid) self._curve = CurveDB().instantiate(oid=curve_oid) self._prehash = pk_alg.value.fixed_params["prehash"] private_key = bytes(self.asn1["privateKey"]) if (private_key[0] != 0x04) or (private_key[1] != self.curve.element_octet_cnt): raise InvalidInputException( "EdDSA private key does start with 04 %02x, but with %02x %02x." % (self.curve.element_octet_cnt, private_key[0], private_key[1])) if len(private_key) != self.curve.element_octet_cnt + 2: raise InvalidInputException( "EdDSA private key length expected to be %d octets, but was %d octets." % (self.curve.element_octet_cnt + 2, len(private_key[0]))) self._priv = private_key[2:]
def _decode_hook(self): if self.asn1 is not None: self._oids = [OID.from_str(str(oid)) for oid in self.asn1] else: self._oids = []
def extension_oid_abbreviation(cls, oid): if oid is None: return None return cls._Exported_Extension_OIDs.get(OID.from_str(oid))
def registered_id(self): assert (self.name == "registeredID") if self._cached is None: self._cached = OID.from_asn1(self.asn1_value) return self._cached
def signature_alg_oid(self): signature_alg_oid = OID.from_str( str(self.asn1["signatureAlgorithm"]["algorithm"])) return signature_alg_oid
def from_asn1(cls, asn1): """Decode explicitly encoded elliptic curve domain parameters, given as a Sequence (SpecifiedECDomain).""" version = int(asn1["version"]) if version != 1: raise InvalidInputException( "Attempted to decode the excplicit EC domain and saw unknown version %d." % (version)) field_type = OID.from_asn1(asn1["fieldID"]["fieldType"]) field_type_id = OIDDB.ECFieldType.get(field_type) if field_type_id is None: raise InvalidInputException( "Encountered explicit EC domain parameters in unknown field with OID %s." % (str(field_type))) domain_parameters = { "a": int.from_bytes(bytes(asn1["curve"]["a"]), byteorder="big"), "b": int.from_bytes(bytes(asn1["curve"]["b"]), byteorder="big"), "n": int(asn1["order"]), } if asn1["cofactor"].hasValue(): domain_parameters["h"] = int(asn1["cofactor"]) base_point = bytes(asn1["base"]) if field_type_id == "prime-field": (field_params, tail) = pyasn1.codec.der.decoder.decode( bytes(asn1["fieldID"]["parameters"]), asn1Spec=ECFieldParametersPrimeField()) if len(tail) != 0: raise InvalidInputException( "Attempted to decode the excplicit EC domain and encountered %d bytes of trailing data of the prime basis Integer." % (len(tail))) domain_parameters.update({ "p": int(field_params), }) return cls.get_class_for_curvetype("prime").instantiate( domain_parameters, base_point) elif field_type_id == "characteristic-two-field": (field_params, tail) = pyasn1.codec.der.decoder.decode( bytes(asn1["fieldID"]["parameters"]), asn1Spec=ECFieldParametersCharacteristicTwoField()) if len(tail) != 0: raise InvalidInputException( "Attempted to decode the excplicit EC domain and encountered %d bytes of trailing data of the characteristic two field Sequence." % (len(tail))) basis_type = OID.from_asn1(field_params["basis"]) basis_type_id = OIDDB.ECTwoFieldBasistype.get(basis_type) if basis_type_id is None: raise InvalidInputException( "Unknown two-field basis type with OID %s found in public key." % (str(basis_type))) # Field width is common to all two-fields domain_parameters.update({ "m": int(field_params["m"]), "basis": basis_type_id, }) if basis_type_id == "gnBasis": raise InvalidInputException( "Binary field explicit domain parameters with Gaussian polynomial basis is not implemented." ) elif basis_type_id == "tpBasis": (params, tail) = ASN1Tools.redecode( field_params["parameters"], ECFieldParametersCharacteristicTwoFieldTrinomial()) if len(tail) != 0: raise InvalidInputException( "Attempted to decode the excplicit EC domain and encountered %d bytes of trailing data of the characteristic two field trinomial basis." % (len(tail))) poly = [domain_parameters["m"], int(params), 0] elif basis_type_id == "ppBasis": (params, tail) = ASN1Tools.redecode( field_params["parameters"], ECFieldParametersCharacteristicTwoFieldPentanomial()) if len(tail) != 0: raise InvalidInputException( "Attempted to decode the excplicit EC domain and encountered %d bytes of trailing data of the characteristic two field pentanomial basis." % (len(tail))) poly = [ domain_parameters["m"], int(params["k1"]), int(params["k2"]), int(params["k3"]), 0 ] else: raise NotImplementedError("Binary field basis", basis_type_id) domain_parameters.update({ "m": int(field_params["m"]), "poly": poly, }) return cls.get_class_for_curvetype("binary").instantiate( domain_parameters, base_point) else: raise NotImplementedError( "Explicit EC domain parameter encoding for field type \"%s\" is not implemented." % (field_type_id))
def _analyze_certificate_general_issues(self, certificate): judgements = SecurityJudgements() if certificate.version != 3: judgements += SecurityJudgement( JudgementCode.X509Cert_Body_Version_Not3, "Certificate version is v%d, usually would expect a v3 certificate." % (certificate.version), commonness=Commonness.HIGHLY_UNUSUAL) if certificate.serial < 0: standard = RFCReference( rfcno=5280, sect="4.1.2.2", verb="MUST", text= "The serial number MUST be a positive integer assigned by the CA to each certificate." ) judgements += SecurityJudgement( JudgementCode.X509Cert_Body_SerialNumber_BasicChecks_Negative, "Certificate serial number is a negative value.", compatibility=Compatibility.STANDARDS_DEVIATION, standard=standard) elif certificate.serial == 0: standard = RFCReference( rfcno=5280, sect="4.1.2.2", verb="MUST", text= "The serial number MUST be a positive integer assigned by the CA to each certificate." ) judgements += SecurityJudgement( JudgementCode.X509Cert_Body_SerialNumber_BasicChecks_Zero, "Certificate serial number is zero.", compatibility=Compatibility.STANDARDS_DEVIATION, standard=standard) elif certificate.serial >= (2**(8 * 20)): standard = RFCReference( rfcno=5280, sect="4.1.2.2", verb="MUST", text= "Conforming CAs MUST NOT use serialNumber values longer than 20 octets." ) judgements += SecurityJudgement( JudgementCode.X509Cert_Body_SerialNumber_BasicChecks_Large, "Certificate serial number is too large.", compatibility=Compatibility.STANDARDS_DEVIATION, standard=standard) if "non_der" in certificate.asn1_details.flags: judgements += SecurityJudgement( JudgementCode.X509Cert_Malformed_NonDEREncoding, "Certificate uses invalid DER encoding. Original certificate is %d bytes; DER would be %d bytes." % (len(certificate.asn1_details.original_der), len(certificate.asn1_details.encoded_der)), compatibility=Compatibility.STANDARDS_DEVIATION) if "trailing_data" in certificate.asn1_details.flags: judgements += SecurityJudgement( JudgementCode.X509Cert_TrailingData, "Certificate contains %d bytes of trailing data." % (len(certificate.asn1_details.tail)), compatibility=Compatibility.STANDARDS_DEVIATION) standard = RFCReference( rfcno=5280, sect=["4.1.1.2", "4.1.2.3"], verb="MUST", text= "This field MUST contain the same algorithm identifier as the signature field in the sequence tbsCertificate (Section 4.1.2.3)." ) oid_header = OID.from_asn1( certificate.asn1["tbsCertificate"]["signature"]["algorithm"]) oid_sig = OID.from_asn1( certificate.asn1["signatureAlgorithm"]["algorithm"]) if oid_header != oid_sig: name_header = OIDDB.SignatureAlgorithms.get( oid_header, str(oid_header)) name_sig = OIDDB.SignatureAlgorithms.get(oid_sig, str(oid_sig)) judgements += SecurityJudgement( JudgementCode.X509Cert_Signature_Function_BodyMismatch, "Certificate indicates signature algorithm %s in header section and %s in signature section." % (name_header, name_sig), compatibility=Compatibility.STANDARDS_DEVIATION, standard=standard) else: # OIDs might be same, but parameters could differ (e.g., for RSA-PSS) header_hasvalue = certificate.asn1["tbsCertificate"]["signature"][ "parameters"].hasValue() signature_hasvalue = certificate.asn1["signatureAlgorithm"][ "parameters"].hasValue() if header_hasvalue and signature_hasvalue: name_header = OIDDB.SignatureAlgorithms.get( oid_header, str(oid_header)) parameters_header = bytes(certificate.asn1["tbsCertificate"] ["signature"]["parameters"]) parameters_signature = bytes( certificate.asn1["signatureAlgorithm"]["parameters"]) if parameters_header != parameters_signature: judgements += SecurityJudgement( JudgementCode.X509Cert_Signature_Function_BodyMismatch, "Certificate indicates same signature algorithm in both header section and signature section (%s), but parameterization of each differ." % (name_header), compatibility=Compatibility.STANDARDS_DEVIATION, standard=standard) elif header_hasvalue != signature_hasvalue: judgements += SecurityJudgement( JudgementCode.X509Cert_Signature_Function_BodyMismatch, "Certificate indicates same signature algorithm in both header section and signature section, but header %s while signature section %s." % ("has parameters" if header_hasvalue else "has no parameters", "does" if signature_hasvalue else "does not"), compatibility=Compatibility.STANDARDS_DEVIATION, standard=standard) return judgements
class ActionTestcaseGen(BaseAction): _EXPECT_PRESENT_CODEPOINT_OID = OID.from_str("1.13.99.127.41") _TCDefinition = collections.namedtuple( "TCDefinition", ["full_name", "filename", "expect_present"]) def __init__(self, cmdname, args): BaseAction.__init__(self, cmdname, args) sys.setrecursionlimit(10000) self._cg = CertGenerator.instantiate(self._args.tcname + ".ader") if self._args.list_parameters: for (name, values) in sorted(self._cg.parameters): print("%-30s %s" % (name, ", ".join(sorted(values)))) sys.exit(0) template_parameters = {} for parameter in self._args.parameter: (key, value) = parameter.split("=", maxsplit=1) if value == "*": template_parameters[key] = self._cg.get_choices(key) else: template_parameters[key] = [value] for (name, values) in self._cg.parameters: if name not in template_parameters: template_parameters[name] = values with contextlib.suppress(FileExistsError): os.makedirs(self._args.output_dir) template_parameters = list(template_parameters.items()) keys = [key for (key, values) in template_parameters] values = [values for (key, values) in template_parameters] self._tcs = [] for concrete_values in itertools.product(*values): concrete_values = dict(zip(keys, concrete_values)) try: self._render(concrete_values) except subprocess.CalledProcessError as e: print("Failed: %s (%s)" % (str(concrete_values), str(e))) self._tcs.sort() self._print_tcs() @staticmethod def testcase_codepoint_string(codepoints): if len(codepoints) == 1: return "\"%s\"" % (codepoints[0]) else: return "[ " + ", ".join("\"%s\"" % (codepoint) for codepoint in codepoints) + " ]" def _print_tcs(self): with open(self._args.output_dir + "/tcs.txt", "w") as f: print("# " + ("=" * 70) + " Begin of %s " % (self._args.tcname) + ("=" * 70), file=f) for tc in self._tcs: print("\tdef %s(self):" % (tc.full_name), file=f) print( "\t\tself._test_examine_x509test_resultcode(\"%s\", expect_present = %s)" % (tc.filename, self.testcase_codepoint_string(tc.expect_present)), file=f) print(file=f) print("# " + ("=" * 70) + " End of %s " % (self._args.tcname) + ("=" * 70), file=f) def _extract_codepoints(self, filename_prefix, pem_file): x509cert = X509Certificate.from_pem_data(pem_file)[0] present_codepoints = [ rdn.get_value(self._EXPECT_PRESENT_CODEPOINT_OID).printable_value for rdn in x509cert.subject.get_all( self._EXPECT_PRESENT_CODEPOINT_OID) ] present_codepoints += [ rdn.get_value(self._EXPECT_PRESENT_CODEPOINT_OID).printable_value for rdn in x509cert.issuer.get_all( self._EXPECT_PRESENT_CODEPOINT_OID) ] codepoints = [] for codepoint_name in present_codepoints: if "#" in codepoint_name: continue try: getattr(JudgementCode, codepoint_name) except AttributeError: print("No such codepoint: %s (in %s)" % (codepoint_name, filename_prefix)) continue codepoints.append(codepoint_name) return codepoints def _render(self, concrete_values): if self._args.verbose >= 1: print(concrete_values) (filename_prefix, ascii_der_source) = self._cg.render(concrete_values) if self._args.no_pem: self._write_raw_ader_file(filename_prefix, ascii_der_source) else: self._write_pem_file(filename_prefix, ascii_der_source) def _write_raw_ader_file(self, filename_prefix, ascii_der_source): outfile = self._args.output_dir + "/" + basename + ".ader" with open(outfile, "w") as f: f.write(ascii_der_source) print(ascii_der_source) def _write_pem_file(self, filename_prefix, ascii_der_source): der_data = subprocess.check_output(["ascii2der"], input=ascii_der_source.encode()) try: pem_cert = subprocess.check_output( ["openssl", "x509", "-inform", "der", "-text"], input=der_data) pem_cert = pem_cert.decode() pem_cert = [line.rstrip("\t ") for line in pem_cert.split("\n")] pem_cert = "\n".join(pem_cert) except subprocess.CalledProcessError: # OpenSSL cannot encode the certificate pem_cert = PEMDataTools.data2pem(der_data, marker="CERTIFICATE") codepoints = self._extract_codepoints(filename_prefix, pem_cert) if len(codepoints) == 0: return outfile = self._args.output_dir + "/" + filename_prefix + ".pem" with open(outfile, "w") as f: f.write(pem_cert) tc_filename = "certs/generated/%s/%s.pem" % (self._args.tcname, filename_prefix) tc = self._TCDefinition(full_name="test_generated_%s_%s" % (self._args.tcname, filename_prefix), filename=tc_filename, expect_present=codepoints) self._tcs.append(tc)