| // Copyright 2011 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package x509 |
| |
| import ( |
| "bytes" |
| "crypto/sha256" |
| "encoding/pem" |
| "sync" |
| ) |
| |
| type sum224 [sha256.Size224]byte |
| |
| // CertPool is a set of certificates. |
| type CertPool struct { |
| byName map[string][]int // cert.RawSubject => index into lazyCerts |
| |
| // lazyCerts contains funcs that return a certificate, |
| // lazily parsing/decompressing it as needed. |
| lazyCerts []lazyCert |
| |
| // haveSum maps from sum224(cert.Raw) to true. It's used only |
| // for AddCert duplicate detection, to avoid CertPool.contains |
| // calls in the AddCert path (because the contains method can |
| // call getCert and otherwise negate savings from lazy getCert |
| // funcs). |
| haveSum map[sum224]bool |
| |
| // systemPool indicates whether this is a special pool derived from the |
| // system roots. If it includes additional roots, it requires doing two |
| // verifications, one using the roots provided by the caller, and one using |
| // the system platform verifier. |
| systemPool bool |
| } |
| |
| // lazyCert is minimal metadata about a Cert and a func to retrieve it |
| // in its normal expanded *Certificate form. |
| type lazyCert struct { |
| // rawSubject is the Certificate.RawSubject value. |
| // It's the same as the CertPool.byName key, but in []byte |
| // form to make CertPool.Subjects (as used by crypto/tls) do |
| // fewer allocations. |
| rawSubject []byte |
| |
| // getCert returns the certificate. |
| // |
| // It is not meant to do network operations or anything else |
| // where a failure is likely; the func is meant to lazily |
| // parse/decompress data that is already known to be good. The |
| // error in the signature primarily is meant for use in the |
| // case where a cert file existed on local disk when the program |
| // started up is deleted later before it's read. |
| getCert func() (*Certificate, error) |
| } |
| |
| // NewCertPool returns a new, empty CertPool. |
| func NewCertPool() *CertPool { |
| return &CertPool{ |
| byName: make(map[string][]int), |
| haveSum: make(map[sum224]bool), |
| } |
| } |
| |
| // len returns the number of certs in the set. |
| // A nil set is a valid empty set. |
| func (s *CertPool) len() int { |
| if s == nil { |
| return 0 |
| } |
| return len(s.lazyCerts) |
| } |
| |
| // cert returns cert index n in s. |
| func (s *CertPool) cert(n int) (*Certificate, error) { |
| return s.lazyCerts[n].getCert() |
| } |
| |
| // Clone returns a copy of s. |
| func (s *CertPool) Clone() *CertPool { |
| p := &CertPool{ |
| byName: make(map[string][]int, len(s.byName)), |
| lazyCerts: make([]lazyCert, len(s.lazyCerts)), |
| haveSum: make(map[sum224]bool, len(s.haveSum)), |
| systemPool: s.systemPool, |
| } |
| for k, v := range s.byName { |
| indexes := make([]int, len(v)) |
| copy(indexes, v) |
| p.byName[k] = indexes |
| } |
| for k := range s.haveSum { |
| p.haveSum[k] = true |
| } |
| copy(p.lazyCerts, s.lazyCerts) |
| return p |
| } |
| |
| // SystemCertPool returns a copy of the system cert pool. |
| // |
| // On Unix systems other than macOS the environment variables SSL_CERT_FILE and |
| // SSL_CERT_DIR can be used to override the system default locations for the SSL |
| // certificate file and SSL certificate files directory, respectively. The |
| // latter can be a colon-separated list. |
| // |
| // Any mutations to the returned pool are not written to disk and do not affect |
| // any other pool returned by SystemCertPool. |
| // |
| // New changes in the system cert pool might not be reflected in subsequent calls. |
| func SystemCertPool() (*CertPool, error) { |
| if sysRoots := systemRootsPool(); sysRoots != nil { |
| return sysRoots.Clone(), nil |
| } |
| |
| return loadSystemRoots() |
| } |
| |
| // findPotentialParents returns the indexes of certificates in s which might |
| // have signed cert. |
| func (s *CertPool) findPotentialParents(cert *Certificate) []*Certificate { |
| if s == nil { |
| return nil |
| } |
| |
| // consider all candidates where cert.Issuer matches cert.Subject. |
| // when picking possible candidates the list is built in the order |
| // of match plausibility as to save cycles in buildChains: |
| // AKID and SKID match |
| // AKID present, SKID missing / AKID missing, SKID present |
| // AKID and SKID don't match |
| var matchingKeyID, oneKeyID, mismatchKeyID []*Certificate |
| for _, c := range s.byName[string(cert.RawIssuer)] { |
| candidate, err := s.cert(c) |
| if err != nil { |
| continue |
| } |
| kidMatch := bytes.Equal(candidate.SubjectKeyId, cert.AuthorityKeyId) |
| switch { |
| case kidMatch: |
| matchingKeyID = append(matchingKeyID, candidate) |
| case (len(candidate.SubjectKeyId) == 0 && len(cert.AuthorityKeyId) > 0) || |
| (len(candidate.SubjectKeyId) > 0 && len(cert.AuthorityKeyId) == 0): |
| oneKeyID = append(oneKeyID, candidate) |
| default: |
| mismatchKeyID = append(mismatchKeyID, candidate) |
| } |
| } |
| |
| found := len(matchingKeyID) + len(oneKeyID) + len(mismatchKeyID) |
| if found == 0 { |
| return nil |
| } |
| candidates := make([]*Certificate, 0, found) |
| candidates = append(candidates, matchingKeyID...) |
| candidates = append(candidates, oneKeyID...) |
| candidates = append(candidates, mismatchKeyID...) |
| return candidates |
| } |
| |
| func (s *CertPool) contains(cert *Certificate) bool { |
| if s == nil { |
| return false |
| } |
| return s.haveSum[sha256.Sum224(cert.Raw)] |
| } |
| |
| // AddCert adds a certificate to a pool. |
| func (s *CertPool) AddCert(cert *Certificate) { |
| if cert == nil { |
| panic("adding nil Certificate to CertPool") |
| } |
| s.addCertFunc(sha256.Sum224(cert.Raw), string(cert.RawSubject), func() (*Certificate, error) { |
| return cert, nil |
| }) |
| } |
| |
| // addCertFunc adds metadata about a certificate to a pool, along with |
| // a func to fetch that certificate later when needed. |
| // |
| // The rawSubject is Certificate.RawSubject and must be non-empty. |
| // The getCert func may be called 0 or more times. |
| func (s *CertPool) addCertFunc(rawSum224 sum224, rawSubject string, getCert func() (*Certificate, error)) { |
| if getCert == nil { |
| panic("getCert can't be nil") |
| } |
| |
| // Check that the certificate isn't being added twice. |
| if s.haveSum[rawSum224] { |
| return |
| } |
| |
| s.haveSum[rawSum224] = true |
| s.lazyCerts = append(s.lazyCerts, lazyCert{ |
| rawSubject: []byte(rawSubject), |
| getCert: getCert, |
| }) |
| s.byName[rawSubject] = append(s.byName[rawSubject], len(s.lazyCerts)-1) |
| } |
| |
| // AppendCertsFromPEM attempts to parse a series of PEM encoded certificates. |
| // It appends any certificates found to s and reports whether any certificates |
| // were successfully parsed. |
| // |
| // On many Linux systems, /etc/ssl/cert.pem will contain the system wide set |
| // of root CAs in a format suitable for this function. |
| func (s *CertPool) AppendCertsFromPEM(pemCerts []byte) (ok bool) { |
| for len(pemCerts) > 0 { |
| var block *pem.Block |
| block, pemCerts = pem.Decode(pemCerts) |
| if block == nil { |
| break |
| } |
| if block.Type != "CERTIFICATE" || len(block.Headers) != 0 { |
| continue |
| } |
| |
| certBytes := block.Bytes |
| cert, err := ParseCertificate(certBytes) |
| if err != nil { |
| continue |
| } |
| var lazyCert struct { |
| sync.Once |
| v *Certificate |
| } |
| s.addCertFunc(sha256.Sum224(cert.Raw), string(cert.RawSubject), func() (*Certificate, error) { |
| lazyCert.Do(func() { |
| // This can't fail, as the same bytes already parsed above. |
| lazyCert.v, _ = ParseCertificate(certBytes) |
| certBytes = nil |
| }) |
| return lazyCert.v, nil |
| }) |
| ok = true |
| } |
| |
| return ok |
| } |
| |
| // Subjects returns a list of the DER-encoded subjects of |
| // all of the certificates in the pool. |
| // |
| // Deprecated: if s was returned by SystemCertPool, Subjects |
| // will not include the system roots. |
| func (s *CertPool) Subjects() [][]byte { |
| res := make([][]byte, s.len()) |
| for i, lc := range s.lazyCerts { |
| res[i] = lc.rawSubject |
| } |
| return res |
| } |
| |
| // Equal reports whether s and other are equal. |
| func (s *CertPool) Equal(other *CertPool) bool { |
| if s == nil || other == nil { |
| return s == other |
| } |
| if s.systemPool != other.systemPool || len(s.haveSum) != len(other.haveSum) { |
| return false |
| } |
| for h := range s.haveSum { |
| if !other.haveSum[h] { |
| return false |
| } |
| } |
| return true |
| } |