github.com/zmap/zcrypto@v0.0.0-20240512203510-0fef58d9a9db/verifier/graph.go (about)

     1  /*
     2   * ZCrypto Copyright 2017 Regents of the University of Michigan
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License"); you may not
     5   * use this file except in compliance with the License. You may obtain a copy
     6   * of the License at http://www.apache.org/licenses/LICENSE-2.0
     7   *
     8   * Unless required by applicable law or agreed to in writing, software
     9   * distributed under the License is distributed on an "AS IS" BASIS,
    10   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    11   * implied. See the License for the specific language governing
    12   * permissions and limitations under the License.
    13   */
    14  
    15  package verifier
    16  
    17  import (
    18  	"bufio"
    19  	"encoding/pem"
    20  	"fmt"
    21  	"io"
    22  
    23  	"github.com/zmap/zcertificate"
    24  	"github.com/zmap/zcrypto/x509"
    25  )
    26  
    27  // subjectAndKeyFingerprint is a SHA256 fingerprint of (public key, subject).
    28  // This is used a key in maps.
    29  type subjectAndKeyFingerprint string
    30  
    31  // Graph represents signing relationships between SubjectAndKey tuples. A node
    32  // in the graph is a SubjectAndKey. An edge in the graph is a certificate issued
    33  // by the direct predecessor (tail) to the direct successor (head).
    34  type Graph struct {
    35  	nodes                []*GraphNode
    36  	edges                *GraphEdgeSet
    37  	nodesBySubjectAndKey map[subjectAndKeyFingerprint]*GraphNode
    38  	nodesBySubject       map[string][]*GraphNode  // indexed by RawSubject
    39  	missingIssuerNode    map[string]*GraphEdgeSet // indexed by RawIssuer
    40  }
    41  
    42  // A GraphNode is a SubjectAndKey
    43  type GraphNode struct {
    44  	SubjectAndKey           *x509.SubjectAndKey
    45  	childrenBySubjectAndKey map[subjectAndKeyFingerprint]*GraphEdgeSet
    46  	parentsBySubjectAndKey  map[subjectAndKeyFingerprint]*GraphEdgeSet
    47  }
    48  
    49  // A GraphEdge is a certificate that joins two SubjectAndKeys.
    50  type GraphEdge struct {
    51  	Certificate *x509.Certificate
    52  	issuer      *GraphNode // this might not always be filled out
    53  	child       *GraphNode
    54  	root        bool
    55  }
    56  
    57  // A GraphEdgeSet represents a set of edges. Edges are deduplicated by
    58  // certificate fingerprint.
    59  type GraphEdgeSet struct {
    60  	edges map[string]*GraphEdge
    61  }
    62  
    63  // NewGraph initializes an empty Graph.
    64  func NewGraph() (g *Graph) {
    65  	g = new(Graph)
    66  	g.edges = NewGraphEdgeSet()
    67  	g.nodesBySubjectAndKey = make(map[subjectAndKeyFingerprint]*GraphNode)
    68  	g.nodesBySubject = make(map[string][]*GraphNode)
    69  	g.missingIssuerNode = make(map[string]*GraphEdgeSet)
    70  	return
    71  }
    72  
    73  // Nodes returns a slice of all nodes in the graph.
    74  func (g *Graph) Nodes() (out []*GraphNode) {
    75  	if g.nodes == nil {
    76  		return
    77  	}
    78  	out = make([]*GraphNode, len(g.nodes))
    79  	copy(out, g.nodes)
    80  	return
    81  }
    82  
    83  // Edges returns all edges in the graph as a slice.
    84  func (g *Graph) Edges() []*GraphEdge {
    85  	return g.edges.Edges()
    86  }
    87  
    88  // FindEdge returns an edge with a certificate matching the given SHA256
    89  // fingerprint, if it exists. If it does not exist, FindEdge returns nil.
    90  func (g *Graph) FindEdge(fp x509.CertificateFingerprint) *GraphEdge {
    91  	return g.edges.FindEdge(fp)
    92  }
    93  
    94  // FindNode returns a node with a matching spki_subject_fingerprint to fp, if it
    95  // exists. If it does not exist, FindNode returns nil.
    96  func (g *Graph) FindNode(fp x509.CertificateFingerprint) *GraphNode {
    97  	node := g.nodesBySubjectAndKey[subjectAndKeyFingerprint(fp)]
    98  	return node
    99  }
   100  
   101  // AddCert inserts an edge for c into the graph, and creates nodes as needed.
   102  func (g *Graph) AddCert(c *x509.Certificate) {
   103  	sk := c.SubjectAndKey()
   104  	skfp := subjectAndKeyFingerprint(sk.Fingerprint)
   105  	isNewNode := false
   106  
   107  	if g.edges.ContainsCertificate(c) {
   108  		// This certificate is already represented in the graph.
   109  		return
   110  	}
   111  
   112  	// Create a new edge for this certificate.
   113  	edge := new(GraphEdge)
   114  	edge.Certificate = c
   115  	g.edges.addOrPanic(edge)
   116  
   117  	// Make the node based on this certificates subject (or find it). Connect the
   118  	// node to the edge as the successor / head.
   119  	node := g.nodesBySubjectAndKey[skfp]
   120  	if node == nil {
   121  		node = new(GraphNode)
   122  		node.SubjectAndKey = sk
   123  		node.childrenBySubjectAndKey = make(map[subjectAndKeyFingerprint]*GraphEdgeSet)
   124  		node.parentsBySubjectAndKey = make(map[subjectAndKeyFingerprint]*GraphEdgeSet)
   125  		g.nodes = append(g.nodes, node)
   126  		g.nodesBySubjectAndKey[skfp] = node
   127  
   128  		s := string(c.RawSubject)
   129  		g.nodesBySubject[s] = append(g.nodesBySubject[s], node)
   130  		isNewNode = true
   131  	}
   132  	edge.child = node
   133  
   134  	// Connect the edge to the graph
   135  	//fmt.Fprintf(os.Stderr, "by subject %v\n", g.nodesBySubject)
   136  	potentialIssuers, _ := g.nodesBySubject[string(c.RawIssuer)]
   137  	for _, potentialIssuerNode := range potentialIssuers {
   138  		issuerIdentity := potentialIssuerNode.SubjectAndKey
   139  		if err := x509.CheckSignatureFromKey(issuerIdentity.PublicKey, c.SignatureAlgorithm, c.RawTBSCertificate, c.Signature); err != nil {
   140  			// If the signature was not valid, this is not an issuer.
   141  			continue
   142  		}
   143  		// The signature from the potential issuer was valid, and the subjects
   144  		// match. Therefore, we have found an issuer. We now need to add the edge to
   145  		// the graph. However, the graph is actually a multigraph. Two
   146  		// SubjectAndKeys can be joined in the same direction multiple times, by
   147  		// issuing multiple certificates to the same subject. Find all edges
   148  		// corresponding to this issuer signing a certificate with the target
   149  		// subject.
   150  		edgeSet := potentialIssuerNode.childrenBySubjectAndKey[skfp]
   151  		if edgeSet == nil {
   152  			edgeSet = NewGraphEdgeSet()
   153  			potentialIssuerNode.childrenBySubjectAndKey[skfp] = edgeSet
   154  		}
   155  
   156  		// Add this edge in. It should not already exist due to earlier checks. If
   157  		// it prexists, the graph is corrupted.
   158  		edge.issuer = potentialIssuerNode
   159  		edgeSet.addOrPanic(edge)
   160  
   161  		// Update the parents of this node
   162  		parentSkpf := subjectAndKeyFingerprint(potentialIssuerNode.SubjectAndKey.Fingerprint)
   163  		parentSet := node.parentsBySubjectAndKey[parentSkpf]
   164  		if parentSet == nil {
   165  			parentSet = NewGraphEdgeSet()
   166  			node.parentsBySubjectAndKey[parentSkpf] = parentSet
   167  		}
   168  		parentSet.addOrPanic(edge)
   169  
   170  		// A certificate can only be one edge. We found it already, so break out of
   171  		// the loop.
   172  		break
   173  	}
   174  
   175  	// Check if we have a reverse-dangling edge. This might be "patchable" as we
   176  	// add more certificates.
   177  	if edge.issuer == nil {
   178  		rawIssuer := string(c.RawIssuer)
   179  		missingIssuerSet := g.missingIssuerNode[rawIssuer]
   180  		if missingIssuerSet == nil {
   181  			missingIssuerSet = NewGraphEdgeSet()
   182  			g.missingIssuerNode[rawIssuer] = missingIssuerSet
   183  		}
   184  		missingIssuerSet.addOrPanic(edge)
   185  	}
   186  
   187  	// If we added a new node, check if it issued an existing dangling edge.
   188  	if !isNewNode {
   189  		return
   190  	}
   191  	potentialOutgoingEdges := g.missingIssuerNode[string(c.RawSubject)]
   192  	if potentialOutgoingEdges == nil {
   193  		return
   194  	}
   195  
   196  	// Check every edge in the set to see if this node is an issuer.
   197  	var fixedUpEdges []*GraphEdge
   198  	for _, candidateEdge := range potentialOutgoingEdges.Edges() {
   199  		pk := node.SubjectAndKey.PublicKey
   200  		candidateCert := candidateEdge.Certificate
   201  		if err := x509.CheckSignatureFromKey(pk, candidateCert.SignatureAlgorithm, candidateCert.RawTBSCertificate, candidateCert.Signature); err != nil {
   202  			// If the signature was not valid, this node is not an issuer
   203  			continue
   204  		}
   205  
   206  		// The signature was valid, so fixup this edge.
   207  		candidateEdge.issuer = node
   208  		childSubjectAndKeyFingerprint := subjectAndKeyFingerprint(candidateEdge.child.SubjectAndKey.Fingerprint)
   209  		edgeSet := node.childrenBySubjectAndKey[childSubjectAndKeyFingerprint]
   210  		if edgeSet == nil {
   211  			edgeSet = NewGraphEdgeSet()
   212  			node.childrenBySubjectAndKey[childSubjectAndKeyFingerprint] = edgeSet
   213  		}
   214  		edgeSet.addOrPanic(candidateEdge)
   215  
   216  		// Set the parents of the node
   217  		parentSkpf := subjectAndKeyFingerprint(node.SubjectAndKey.Fingerprint)
   218  		parentSet := candidateEdge.child.parentsBySubjectAndKey[parentSkpf]
   219  		if parentSet == nil {
   220  			parentSet = NewGraphEdgeSet()
   221  			candidateEdge.child.parentsBySubjectAndKey[parentSkpf] = parentSet
   222  		}
   223  		parentSet.addOrPanic(candidateEdge)
   224  
   225  		// Record the edge as fixed so we can remove it from the missingIssuerNode
   226  		// map.
   227  		fixedUpEdges = append(fixedUpEdges, candidateEdge)
   228  	}
   229  
   230  	// Remove any fixed-up edges from the missingIssuerNode map.
   231  	for _, fixedEdge := range fixedUpEdges {
   232  		potentialOutgoingEdges.removeEdge(fixedEdge.Certificate.FingerprintSHA256)
   233  	}
   234  	if potentialOutgoingEdges.Size() == 0 {
   235  		potentialOutgoingEdges = nil
   236  		delete(g.missingIssuerNode, string(c.RawSubject))
   237  	}
   238  }
   239  
   240  // AddRoot adges an edge for certificate c, and marks it as a root.
   241  func (g *Graph) AddRoot(c *x509.Certificate) {
   242  	g.AddCert(c)
   243  	edge := g.edges.FindEdge(c.FingerprintSHA256)
   244  	edge.root = true
   245  }
   246  
   247  // IsRoot returns true if c is a root in the graph.
   248  func (g *Graph) IsRoot(c *x509.Certificate) bool {
   249  	edge := g.FindEdge(c.FingerprintSHA256)
   250  	if edge == nil {
   251  		return false
   252  	}
   253  	return edge.root
   254  }
   255  
   256  // AppendFromPEM adds any certificates encoded as PEM from r to the graph. If
   257  // root is true, it marks them as roots. It returns the number of certificates
   258  // parsed.
   259  func (g *Graph) AppendFromPEM(r io.Reader, root bool) int {
   260  	count := 0
   261  	scanner := bufio.NewScanner(r)
   262  	scanner.Split(zcertificate.ScannerSplitPEM)
   263  	for scanner.Scan() {
   264  		p, _ := pem.Decode(scanner.Bytes())
   265  		if p == nil {
   266  			continue
   267  		}
   268  		c, err := x509.ParseCertificate(p.Bytes)
   269  		if err != nil {
   270  			continue
   271  		}
   272  		g.AddCert(c)
   273  		if root {
   274  			g.AddRoot(c)
   275  		}
   276  		count++
   277  	}
   278  	return count
   279  }
   280  
   281  // NewGraphEdgeSet initializes an empty GraphEdgeSet.
   282  func NewGraphEdgeSet() (es *GraphEdgeSet) {
   283  	es = new(GraphEdgeSet)
   284  	es.edges = make(map[string]*GraphEdge)
   285  	return
   286  }
   287  
   288  // Edges returns all edges in the set as a slice.
   289  func (es *GraphEdgeSet) Edges() (out []*GraphEdge) {
   290  	for _, edge := range es.edges {
   291  		out = append(out, edge)
   292  	}
   293  	return
   294  }
   295  
   296  // ContainsCertificate returns true if c is contained in the GraphEdgeSet.
   297  func (es *GraphEdgeSet) ContainsCertificate(c *x509.Certificate) bool {
   298  	fp := string(c.FingerprintSHA256)
   299  	_, ok := es.edges[fp]
   300  	return ok
   301  }
   302  
   303  // ContainsEdge returns true if the edge is contained in the GraphEdgeSet.
   304  func (es *GraphEdgeSet) ContainsEdge(edge *GraphEdge) bool {
   305  	return es.ContainsCertificate(edge.Certificate)
   306  }
   307  
   308  // Size returns the number of edges in the GraphEdgeSet.
   309  func (es *GraphEdgeSet) Size() int {
   310  	return len(es.edges)
   311  }
   312  
   313  // FindEdge returns an edge matching the certificate fingerprint, if it exists.
   314  // If it does not exist, FindEdge returns nil.
   315  func (es *GraphEdgeSet) FindEdge(fp x509.CertificateFingerprint) *GraphEdge {
   316  	edge, _ := es.edges[string(fp)]
   317  	return edge
   318  }
   319  
   320  // RemoveEdge removes an edge matching the certificate fingerprint, if it
   321  // exists. If it exists, RemoveEdge returns a point to the removed edge. If no
   322  // such edge exists, RemoveEdge does nothing and returns nil.
   323  func (es *GraphEdgeSet) removeEdge(fp x509.CertificateFingerprint) *GraphEdge {
   324  	edge, ok := es.edges[string(fp)]
   325  	if !ok {
   326  		return nil
   327  	}
   328  	delete(es.edges, string(fp))
   329  	return edge
   330  }
   331  
   332  // addOrPanic adds the edge to the set, and panics if there is a fingerprint
   333  // collision.
   334  func (es *GraphEdgeSet) addOrPanic(edge *GraphEdge) {
   335  	fp := string(edge.Certificate.FingerprintSHA256)
   336  	if _, ok := es.edges[fp]; ok {
   337  		panicStr := fmt.Sprintf("adding duplicate edge to set: %s", edge.Certificate.FingerprintSHA256.Hex())
   338  		panic(panicStr)
   339  	}
   340  	es.edges[fp] = edge
   341  }