OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 // public_key_hashes_check.go runs tests on public_key_hashes.h. It's not run | |
6 // automatically, but rather as part of the process of manually updating | |
7 // public_key_hashes.h | |
8 // | |
9 // It verifies that each hash in the file is correct given the preceeding | |
10 // certificate and that the name of the variable matches the name given in the | |
11 // certifiate. | |
12 // | |
13 // Compile and run with: | |
14 // % cd net/base | |
15 // % 6g public_key_hashes_check.go && 6l public_key_hashes_check.6 && ./6.out ./
public_key_hashes.h | |
16 | |
17 package main | |
18 | |
19 import ( | |
20 "bufio" | |
21 "bytes" | |
22 "crypto/sha1" | |
23 "crypto/x509" | |
24 "encoding/base64" | |
25 "encoding/pem" | |
26 "errors" | |
27 "fmt" | |
28 "io" | |
29 "os" | |
30 "strings" | |
31 ) | |
32 | |
33 const ( | |
34 PRECERT = iota | |
35 INCERT = iota | |
36 POSTCERT = iota | |
37 POSTDECL = iota | |
38 ) | |
39 | |
40 var newLine = []byte("\n") | |
41 var startOfCert = []byte("-----BEGIN CERTIFICATE") | |
42 var endOfCert = []byte("-----END CERTIFICATE") | |
43 var hashDecl = []byte("static const char kSPKIHash_") | |
44 | |
45 // matchNames returns true if the given variable name is a reasonable match for | |
46 // the given CN. | |
47 func matchNames(name, v string) error { | |
48 words := strings.Split(name, " ") | |
49 if len(words) == 0 { | |
50 return errors.New("No words in certificate name") | |
51 } | |
52 firstWord := words[0] | |
53 if strings.HasSuffix(firstWord, ",") { | |
54 firstWord = firstWord[:len(firstWord)-1] | |
55 } | |
56 if pos := strings.Index(firstWord, "."); pos != -1 { | |
57 firstWord = firstWord[:pos] | |
58 } | |
59 if pos := strings.Index(firstWord, "-"); pos != -1 { | |
60 firstWord = firstWord[:pos] | |
61 } | |
62 if !strings.HasPrefix(v, firstWord) { | |
63 return errors.New("The first word of the certificate name isn't
a prefix of the variable name") | |
64 } | |
65 | |
66 for i, word := range words { | |
67 if word == "Class" && i+1 < len(words) { | |
68 if strings.Index(v, word+words[i+1]) == -1 { | |
69 return errors.New("Class specification doesn't a
ppear in the variable name") | |
70 } | |
71 } else if len(word) == 1 && word[0] >= '0' && word[0] <= '9' { | |
72 if strings.Index(v, word) == -1 { | |
73 return errors.New("Number doesn't appear in the
variable name") | |
74 } | |
75 } else if isImportantWordInCertificateName(word) { | |
76 if strings.Index(v, word) == -1 { | |
77 return errors.New(word + " doesn't appear in the
variable name") | |
78 } | |
79 } | |
80 } | |
81 | |
82 return nil | |
83 } | |
84 | |
85 // isImportantWordInCertificateName returns true if w must be found in any | |
86 // corresponding variable name. | |
87 func isImportantWordInCertificateName(w string) bool { | |
88 switch w { | |
89 case "Universal", "Global", "EV", "G1", "G2", "G3", "G4", "G5": | |
90 return true | |
91 } | |
92 return false | |
93 } | |
94 | |
95 func main() { | |
96 if len(os.Args) < 2 { | |
97 fmt.Fprintf(os.Stderr, "Usage: %s <public_key_hashes.h>\n", os.A
rgs[0]) | |
98 return | |
99 } | |
100 | |
101 inFile, err := os.Open(os.Args[1]) | |
102 if err != nil { | |
103 fmt.Fprintf(os.Stderr, "Failed to open input file: %s\n", err.Er
ror()) | |
104 return | |
105 } | |
106 | |
107 in := bufio.NewReader(inFile) | |
108 defer inFile.Close() | |
109 | |
110 lineNo := 0 | |
111 var cert []byte | |
112 state := PRECERT | |
113 var x509Cert *x509.Certificate | |
114 seenHashes := make(map[string]bool) | |
115 | |
116 for { | |
117 lineNo++ | |
118 line, isPrefix, err := in.ReadLine() | |
119 if isPrefix { | |
120 fmt.Fprintf(os.Stderr, "Line %d is too long to process\n
", lineNo) | |
121 return | |
122 } | |
123 if err == io.EOF { | |
124 return | |
125 } | |
126 if err != nil { | |
127 fmt.Fprintf(os.Stderr, "Error reading from input: %s\n",
err.Error()) | |
128 return | |
129 } | |
130 | |
131 switch state { | |
132 case INCERT: | |
133 cert = append(cert, line...) | |
134 cert = append(cert, newLine...) | |
135 case POSTDECL: | |
136 trimmed := bytes.TrimSpace(line) | |
137 if len(trimmed) < 8 || !bytes.HasPrefix(trimmed, []byte(
"\"sha1/")) { | |
138 fmt.Fprintf(os.Stderr, "Line %d is immediately a
fter a declation, but failed to find a hash on it\n", lineNo) | |
139 return | |
140 } | |
141 trimmed = trimmed[6 : len(trimmed)-2] | |
142 h := sha1.New() | |
143 h.Write(x509Cert.RawSubjectPublicKeyInfo) | |
144 shouldBe := base64.StdEncoding.EncodeToString(h.Sum(nil)
) | |
145 if shouldBe != string(trimmed) { | |
146 fmt.Fprintf(os.Stderr, "Line %d: hash should be
%s, but found %s\n", lineNo, shouldBe, trimmed) | |
147 return | |
148 } | |
149 if _, ok := seenHashes[shouldBe]; ok { | |
150 fmt.Fprintf(os.Stderr, "Line %d: duplicated hash
\n", lineNo) | |
151 return | |
152 } | |
153 seenHashes[shouldBe] = true | |
154 state = PRECERT | |
155 x509Cert = nil | |
156 } | |
157 | |
158 if bytes.HasPrefix(line, startOfCert) { | |
159 switch state { | |
160 case PRECERT: | |
161 cert = append(cert, line...) | |
162 cert = append(cert, newLine...) | |
163 state = INCERT | |
164 case INCERT: | |
165 fmt.Fprintf(os.Stderr, "Found start of certifica
te while in certificate at line %d\n", lineNo) | |
166 return | |
167 case POSTCERT: | |
168 fmt.Fprintf(os.Stderr, "Found start of certifica
te while while looking for hash at line %d\n", lineNo) | |
169 return | |
170 case POSTDECL: | |
171 fmt.Fprintf(os.Stderr, "Found start of certifica
te while while looking for hash string at line %d\n", lineNo) | |
172 return | |
173 default: | |
174 panic("bad state") | |
175 } | |
176 } else if bytes.HasPrefix(line, endOfCert) { | |
177 switch state { | |
178 case PRECERT: | |
179 fmt.Fprintf(os.Stderr, "Found end of certificate
without certificate at line %d\n", lineNo) | |
180 return | |
181 case INCERT: | |
182 block, _ := pem.Decode(cert) | |
183 if block == nil { | |
184 fmt.Fprintf(os.Stderr, "Failed to decode
certificate ending on line %d\n", lineNo) | |
185 return | |
186 } | |
187 if x509Cert, err = x509.ParseCertificate(block.B
ytes); err != nil { | |
188 fmt.Fprintf(os.Stderr, "Failed to parse
certificate ending on line %d: %s\n", lineNo, err.Error()) | |
189 return | |
190 } | |
191 cert = nil | |
192 state = POSTCERT | |
193 case POSTCERT: | |
194 fmt.Fprintf(os.Stderr, "Found end of certificate
while while looking for hash at line %d\n", lineNo) | |
195 return | |
196 case POSTDECL: | |
197 fmt.Fprintf(os.Stderr, "Found end of certificate
while while looking for hash string at line %d\n", lineNo) | |
198 return | |
199 default: | |
200 panic("bad state") | |
201 } | |
202 } else if bytes.HasPrefix(line, hashDecl) { | |
203 switch state { | |
204 case PRECERT: | |
205 // No problem here. Not all declations need a ce
rtificate | |
206 case INCERT: | |
207 fmt.Fprintf(os.Stderr, "Found declation at line
%d, but missed the end of the certificate\n", lineNo) | |
208 return | |
209 case POSTCERT: | |
210 varName := line[len(hashDecl):] | |
211 for i, c := range varName { | |
212 if c == '[' { | |
213 varName = varName[:i] | |
214 break | |
215 } | |
216 } | |
217 name := x509Cert.Subject.CommonName | |
218 if len(name) == 0 { | |
219 name = x509Cert.Subject.Organization[0]
+ " " + x509Cert.Subject.OrganizationalUnit[0] | |
220 } | |
221 if err := matchNames(name, string(varName)); err
!= nil { | |
222 fmt.Fprintf(os.Stderr, "Name failure on
line %d: %s\n%s -> %s\n", lineNo, err, name, varName) | |
223 return | |
224 } | |
225 | |
226 state = POSTDECL | |
227 case POSTDECL: | |
228 fmt.Fprintf(os.Stderr, "Found declation at line
%d, but missed the hash value of the previous one\n", lineNo) | |
229 return | |
230 default: | |
231 panic("bad state") | |
232 } | |
233 } | |
234 } | |
235 } | |
OLD | NEW |