Black Lives Matter. Support the Equal Justice Initiative.

Source file src/crypto/x509/root_ios_gen.go

Documentation: crypto/x509

     1  // Copyright 2015 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build ignore
     6  // +build ignore
     7  
     8  // Generates root_ios.go.
     9  //
    10  // As of iOS 13, there is no API for querying the system trusted X.509 root
    11  // certificates.
    12  //
    13  // Apple publishes the trusted root certificates for iOS and macOS on
    14  // opensource.apple.com so we embed them into the x509 package.
    15  //
    16  // Note that this ignores distrusted and revoked certificates.
    17  package main
    18  
    19  import (
    20  	"archive/tar"
    21  	"bytes"
    22  	"compress/gzip"
    23  	"crypto/sha256"
    24  	"crypto/tls"
    25  	"crypto/x509"
    26  	"encoding/pem"
    27  	"flag"
    28  	"fmt"
    29  	"go/format"
    30  	"io"
    31  	"log"
    32  	"net/http"
    33  	"os"
    34  	"path"
    35  	"sort"
    36  	"strings"
    37  	"time"
    38  )
    39  
    40  func main() {
    41  	var output = flag.String("output", "root_ios.go", "file name to write")
    42  	var version = flag.String("version", "", "security_certificates version")
    43  	flag.Parse()
    44  	if *version == "" {
    45  		log.Fatal("Select the latest security_certificates version from " +
    46  			"https://opensource.apple.com/source/security_certificates/")
    47  	}
    48  
    49  	url := "https://opensource.apple.com/tarballs/security_certificates/security_certificates-%s.tar.gz"
    50  	hc := &http.Client{Timeout: 1 * time.Minute}
    51  	resp, err := hc.Get(fmt.Sprintf(url, *version))
    52  	if err != nil {
    53  		log.Fatal(err)
    54  	}
    55  	defer resp.Body.Close()
    56  	if resp.StatusCode != http.StatusOK {
    57  		log.Fatalf("HTTP status not OK: %s", resp.Status)
    58  	}
    59  
    60  	zr, err := gzip.NewReader(resp.Body)
    61  	if err != nil {
    62  		log.Fatal(err)
    63  	}
    64  	defer zr.Close()
    65  
    66  	var certs []*x509.Certificate
    67  	pool := x509.NewCertPool()
    68  
    69  	tr := tar.NewReader(zr)
    70  	for {
    71  		hdr, err := tr.Next()
    72  		if err == io.EOF {
    73  			break
    74  		}
    75  		if err != nil {
    76  			log.Fatal(err)
    77  		}
    78  
    79  		rootsDirectory := fmt.Sprintf("security_certificates-%s/certificates/roots/", *version)
    80  		if dir, file := path.Split(hdr.Name); hdr.Typeflag != tar.TypeReg ||
    81  			dir != rootsDirectory || strings.HasPrefix(file, ".") {
    82  			continue
    83  		}
    84  
    85  		der, err := io.ReadAll(tr)
    86  		if err != nil {
    87  			log.Fatal(err)
    88  		}
    89  
    90  		c, err := x509.ParseCertificate(der)
    91  		if err != nil {
    92  			log.Printf("Failed to parse certificate %q: %v", hdr.Name, err)
    93  			continue
    94  		}
    95  
    96  		certs = append(certs, c)
    97  		pool.AddCert(c)
    98  	}
    99  
   100  	// Quick smoke test to check the pool is well formed, and that we didn't end
   101  	// up trusting roots in the removed folder.
   102  	for _, c := range certs {
   103  		if c.Subject.CommonName == "Symantec Class 2 Public Primary Certification Authority - G4" {
   104  			log.Fatal("The pool includes a removed root!")
   105  		}
   106  	}
   107  	conn, err := tls.Dial("tcp", "mail.google.com:443", &tls.Config{
   108  		RootCAs: pool,
   109  	})
   110  	if err != nil {
   111  		log.Fatal(err)
   112  	}
   113  	conn.Close()
   114  
   115  	certName := func(c *x509.Certificate) string {
   116  		if c.Subject.CommonName != "" {
   117  			return c.Subject.CommonName
   118  		}
   119  		if len(c.Subject.OrganizationalUnit) > 0 {
   120  			return c.Subject.OrganizationalUnit[0]
   121  		}
   122  		return c.Subject.Organization[0]
   123  	}
   124  	sort.Slice(certs, func(i, j int) bool {
   125  		if strings.ToLower(certName(certs[i])) != strings.ToLower(certName(certs[j])) {
   126  			return strings.ToLower(certName(certs[i])) < strings.ToLower(certName(certs[j]))
   127  		}
   128  		if !certs[i].NotBefore.Equal(certs[j].NotBefore) {
   129  			return certs[i].NotBefore.Before(certs[j].NotBefore)
   130  		}
   131  		fi, fj := sha256.Sum256(certs[i].Raw), sha256.Sum256(certs[j].Raw)
   132  		return bytes.Compare(fi[:], fj[:]) < 0
   133  	})
   134  
   135  	out := new(bytes.Buffer)
   136  	fmt.Fprintf(out, header, *version)
   137  	fmt.Fprintf(out, "const systemRootsPEM = `\n")
   138  
   139  	for _, c := range certs {
   140  		fmt.Fprintf(out, "# %q\n", certName(c))
   141  		h := sha256.Sum256(c.Raw)
   142  		fmt.Fprintf(out, "# % X\n", h[:len(h)/2])
   143  		fmt.Fprintf(out, "# % X\n", h[len(h)/2:])
   144  		b := &pem.Block{
   145  			Type:  "CERTIFICATE",
   146  			Bytes: c.Raw,
   147  		}
   148  		if err := pem.Encode(out, b); err != nil {
   149  			log.Fatal(err)
   150  		}
   151  	}
   152  
   153  	fmt.Fprintf(out, "`")
   154  
   155  	source, err := format.Source(out.Bytes())
   156  	if err != nil {
   157  		log.Fatal(err)
   158  	}
   159  	if err := os.WriteFile(*output, source, 0644); err != nil {
   160  		log.Fatal(err)
   161  	}
   162  }
   163  
   164  const header = `// Code generated by root_ios_gen.go -version %s; DO NOT EDIT.
   165  // Update the version in root.go and regenerate with "go generate".
   166  
   167  // +build ios
   168  // +build !x509omitbundledroots
   169  
   170  package x509
   171  
   172  func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
   173  	return nil, nil
   174  }
   175  
   176  func loadSystemRoots() (*CertPool, error) {
   177  	p := NewCertPool()
   178  	p.AppendCertsFromPEM([]byte(systemRootsPEM))
   179  	return p, nil
   180  }
   181  `
   182  

View as plain text