lints/lint_ext_tor_service_descriptor_hash_invalid.go
func (l *torServiceDescHashInvalid) Execute(c *x509.Certificate) *LintResult {
	// If the BRTorServiceDescriptor extension is missing return a lint error. We
	// know the cert contains one or more `.onion` subjects because of
	// `CheckApplies` and all such certs are expected to have this extension after
	// util.CABV201Date.
	if ext := util.GetExtFromCert(c, util.BRTorServiceDescriptor); ext == nil {
		return failResult(
			"certificate contained a %s domain but is missing a TorServiceDescriptor "+
				"extension (oid %s)",
			onionTLD, util.BRTorServiceDescriptor.String())
	}

	// The certificate should have at least one TorServiceDescriptorHash in the
	// TorServiceDescriptor extension.
	descriptors := c.TorServiceDescriptors
	if len(descriptors) == 0 {
		return failResult(
			"certificate contained a %s domain but TorServiceDescriptor "+
				"extension (oid %s) had no TorServiceDescriptorHash objects",
			onionTLD, util.BRTorServiceDescriptor.String())
	}

	// Build a map of all the eTLD+1 onion subjects in the cert to compare against
	// the service descriptors.
	onionETLDPlusOneMap := make(map[string]string)
	for _, subj := range append(c.DNSNames, c.Subject.CommonName) {
		if !strings.HasSuffix(subj, onionTLD) {
			continue
		}
		labels := strings.Split(subj, ".")
		if len(labels) < 2 {
			return failResult("certificate contained a %s domain with too few "+
				"labels: %q",
				onionTLD, subj)
		}
		eTLDPlusOne := strings.Join(labels[len(labels)-2:], ".")
		onionETLDPlusOneMap[eTLDPlusOne] = subj
	}

	expectedHashBits := map[string]int{
		"SHA256": 256,
		"SHA384": 384,
		"SHA512": 512,
	}

	// Build a map of onion hostname -> TorServiceDescriptorHash using the parsed
	// TorServiceDescriptors from zcrypto.
	descriptorMap := make(map[string]*x509.TorServiceDescriptorHash)
	for _, descriptor := range descriptors {
		// each descriptor's Onion URL must be valid
		if errResult := lintOnionURL(descriptor.Onion); errResult != nil {
			return errResult
		}
		// each descriptor should have a known hash algorithm and the correct
		// corresponding size of hash.
		if expectedBits, found := expectedHashBits[descriptor.AlgorithmName]; !found {
			return failResult(
				"%s contained a TorServiceDescriptorHash for Onion URI %q with an "+
					"unknown hash algorithm",
				torServiceDescExtName, descriptor.Onion)
		} else if expectedBits != descriptor.HashBits {
			return failResult(
				"%s contained a TorServiceDescriptorHash with hash algorithm %q but "+
					"only %d bits of hash not %d",
				torServiceDescExtName, descriptor.AlgorithmName,
				descriptor.HashBits, expectedBits)
		}
		// NOTE(@cpu): Throwing out the err result here because lintOnionURL already
		//             ensured the URL is valid.
		url, _ := url.Parse(descriptor.Onion)
		hostname := url.Hostname()
		// there should only be one TorServiceDescriptorHash for each Onion hostname.
		if _, exists := descriptorMap[hostname]; exists {
			return failResult(
				"%s contained more than one TorServiceDescriptorHash for base "+
					"Onion URI %q",
				torServiceDescExtName, descriptor.Onion)
		}
		// there shouldn't be a TorServiceDescriptorHash for a Onion hostname that
		// isn't an eTLD+1 in the certificate's subjects.
		if _, found := onionETLDPlusOneMap[hostname]; !found {
			return failResult(
				"%s contained a TorServiceDescriptorHash with a hostname (%q) not "+
					"present as a subject in the certificate",
				torServiceDescExtName, hostname)
		}
		descriptorMap[hostname] = descriptor
	}

	// Check if any of the onion subjects in the certificate don't have
	// a TorServiceDescriptorHash for the eTLD+1 in the descriptorMap.
	for eTLDPlusOne, subjDomain := range onionETLDPlusOneMap {
		if _, found := descriptorMap[eTLDPlusOne]; !found {
			return failResult(
				"%s subject domain name %q does not have a corresponding "+
					"TorServiceDescriptorHash for its eTLD+1",
				onionTLD, subjDomain)
		}
	}

	// Everything checks out!
	return &LintResult{
		Status: Pass,
	}
}
func (l *torServiceDescHashInvalid) Execute(c *x509.Certificate) *LintResult {
	// If the BRTorServiceDescriptor extension is missing return a lint error. We
	// know the cert contains one or more `.onion` subjects because of
	// `CheckApplies` and all such certs are expected to have this extension after
	// util.CABV201Date.
	if ext := util.GetExtFromCert(c, util.BRTorServiceDescriptor); ext == nil {
		return failResult(
			"certificate contained a %s domain but is missing a TorServiceDescriptor "+
				"extension (oid %s)",
			onionTLD, util.BRTorServiceDescriptor.String())
	}

	// The certificate should have at least one TorServiceDescriptorHash in the
	// TorServiceDescriptor extension.
	descriptors := c.TorServiceDescriptors
	if len(descriptors) == 0 {
		return failResult(
			"certificate contained a %s domain but TorServiceDescriptor "+
				"extension (oid %s) had no TorServiceDescriptorHash objects",
			onionTLD, util.BRTorServiceDescriptor.String())
	}

	// Build a map of all the eTLD+1 onion subjects in the cert to compare against
	// the service descriptors.
	onionETLDPlusOneMap := make(map[string]string)
	for _, subj := range append(c.DNSNames, c.Subject.CommonName) {
		if !strings.HasSuffix(subj, onionTLD) {
			continue
		}
		labels := strings.Split(subj, ".")
		if len(labels) < 2 {
			return failResult("certificate contained a %s domain with too few "+
				"labels: %q",
				onionTLD, subj)
		}
		eTLDPlusOne := strings.Join(labels[len(labels)-2:], ".")
		onionETLDPlusOneMap[eTLDPlusOne] = subj
	}

	expectedHashBits := map[string]int{
		"SHA256": 256,
		"SHA384": 384,
		"SHA512": 512,
	}

	// Build a map of onion hostname -> TorServiceDescriptorHash using the parsed
	// TorServiceDescriptors from zcrypto.
	descriptorMap := make(map[string]*x509.TorServiceDescriptorHash)
	for _, descriptor := range descriptors {
		// each descriptor's Onion URL must be valid
		if errResult := lintOnionURL(descriptor.Onion); errResult != nil {
			return errResult
		}
		// each descriptor should have a known hash algorithm and the correct
		// corresponding size of hash.
		if expectedBits, found := expectedHashBits[descriptor.AlgorithmName]; !found {
			return failResult(
				"%s contained a TorServiceDescriptorHash for Onion URI %q with an "+
					"unknown hash algorithm",
				torServiceDescExtName, descriptor.Onion)
		} else if expectedBits != descriptor.HashBits {
			return failResult(
				"%s contained a TorServiceDescriptorHash with hash algorithm %q but "+
					"only %d bits of hash not %d",
				torServiceDescExtName, descriptor.AlgorithmName,
				descriptor.HashBits, expectedBits)
		}
		// NOTE(@cpu): Throwing out the err result here because lintOnionURL already
		//             ensured the URL is valid.
		url, _ := url.Parse(descriptor.Onion)
		hostname := url.Hostname()
		// there should only be one TorServiceDescriptorHash for each Onion hostname.
		if _, exists := descriptorMap[hostname]; exists {
			return failResult(
				"%s contained more than one TorServiceDescriptorHash for base "+
					"Onion URI %q",
				torServiceDescExtName, descriptor.Onion)
		}
		// there shouldn't be a TorServiceDescriptorHash for a Onion hostname that
		// isn't an eTLD+1 in the certificate's subjects.
		if _, found := onionETLDPlusOneMap[hostname]; !found {
			return failResult(
				"%s contained a TorServiceDescriptorHash with a hostname (%q) not "+
					"present as a subject in the certificate",
				torServiceDescExtName, hostname)
		}
		descriptorMap[hostname] = descriptor
	}

	// Check if any of the onion subjects in the certificate don't have
	// a TorServiceDescriptorHash for the eTLD+1 in the descriptorMap.
	for eTLDPlusOne, subjDomain := range onionETLDPlusOneMap {
		if _, found := descriptorMap[eTLDPlusOne]; !found {
			return failResult(
				"%s subject domain name %q does not have a corresponding "+
					"TorServiceDescriptorHash for its eTLD+1",
				onionTLD, subjDomain)
		}
	}

	// Everything checks out!
	return &LintResult{
		Status: Pass,
	}
}
func ( l * torServiceDescHashInvalid ) Execute ( c * x509 . Certificate ) * LintResult { // If the BRTorServiceDescriptor extension is missing return a lint error. We // know the cert contains one or more `.onion` subjects because of // `CheckApplies` and all such certs are expected to have this extension after // util.CABV201Date. if ext := util . GetExtFromCert ( c , util . BRTorServiceDescriptor ) ; ext == nil { return failResult ( " " + " " , onionTLD , util . BRTorServiceDescriptor . String ( ) ) } // The certificate should have at least one TorServiceDescriptorHash in the // TorServiceDescriptor extension. descriptors := c . TorServiceDescriptors if len ( descriptors ) == 0 { return failResult ( " " + " " , onionTLD , util . BRTorServiceDescriptor . String ( ) ) } // Build a map of all the eTLD+1 onion subjects in the cert to compare against // the service descriptors. onionETLDPlusOneMap := make ( map [ string ] string ) for _ , subj := range append ( c . DNSNames , c . Subject . CommonName ) { if ! strings . HasSuffix ( subj , onionTLD ) { continue } labels := strings . Split ( subj , " " ) if len ( labels ) < 2 { return failResult ( " " + " " , onionTLD , subj ) } eTLDPlusOne := strings . Join ( labels [ len ( labels ) - 2 : ] , " " ) onionETLDPlusOneMap [ eTLDPlusOne ] = subj } expectedHashBits := map [ string ] int { " " : 256 , " " : 384 , " " : 512 , } // Build a map of onion hostname -> TorServiceDescriptorHash using the parsed // TorServiceDescriptors from zcrypto. descriptorMap := make ( map [ string ] * x509 . TorServiceDescriptorHash ) for _ , descriptor := range descriptors { // each descriptor's Onion URL must be valid if errResult := lintOnionURL ( descriptor . Onion ) ; errResult != nil { return errResult } // each descriptor should have a known hash algorithm and the correct // corresponding size of hash. if expectedBits , found := expectedHashBits [ descriptor . AlgorithmName ] ; ! found { return failResult ( " " + " " , torServiceDescExtName , descriptor . Onion ) } else if expectedBits != descriptor . HashBits { return failResult ( " " + " " , torServiceDescExtName , descriptor . AlgorithmName , descriptor . HashBits , expectedBits ) } // NOTE(@cpu): Throwing out the err result here because lintOnionURL already //             ensured the URL is valid. url , _ := url . Parse ( descriptor . Onion ) hostname := url . Hostname ( ) // there should only be one TorServiceDescriptorHash for each Onion hostname. if _ , exists := descriptorMap [ hostname ] ; exists { return failResult ( " " + " " , torServiceDescExtName , descriptor . Onion ) } // there shouldn't be a TorServiceDescriptorHash for a Onion hostname that // isn't an eTLD+1 in the certificate's subjects. if _ , found := onionETLDPlusOneMap [ hostname ] ; ! found { return failResult ( " " + " " , torServiceDescExtName , hostname ) } descriptorMap [ hostname ] = descriptor } // Check if any of the onion subjects in the certificate don't have // a TorServiceDescriptorHash for the eTLD+1 in the descriptorMap. for eTLDPlusOne , subjDomain := range onionETLDPlusOneMap { if _ , found := descriptorMap [ eTLDPlusOne ] ; ! found { return failResult ( " " + " " , onionTLD , subjDomain ) } } // Everything checks out! return & LintResult { Status : Pass , } }
// Execute will lint the provided certificate. An Error LintResult will be
// returned if:
//
//   1) There is no TorServiceDescriptor extension present.
//   2) There were no TorServiceDescriptors parsed by zcrypto
//   3) There are TorServiceDescriptorHash entries with an invalid Onion URL.
//   4) There are TorServiceDescriptorHash entries with an unknown hash
//      algorithm or incorrect hash bit length.
//   5) There is a TorServiceDescriptorHash entry that doesn't correspond to
//      an onion subject in the cert.
//   6) There is an onion subject in the cert that doesn't correspond to
//      a TorServiceDescriptorHash.
Execute will lint the provided certificate . An Error LintResult will be returned if : 1 ) There is no TorServiceDescriptor extension present . 2 ) There were no TorServiceDescriptors parsed by zcrypto 3 ) There are TorServiceDescriptorHash entries with an invalid Onion URL . 4 ) There are TorServiceDescriptorHash entries with an unknown hash algorithm or incorrect hash bit length . 5 ) There is a TorServiceDescriptorHash entry that doesn t correspond to an onion subject in the cert . 6 ) There is an onion subject in the cert that doesn t correspond to a TorServiceDescriptorHash .