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 }