blob: b37f45326e898955a92f188dfcdb75a6736fc2b5 [file] [log] [blame]
Robert Sloan8ff03552017-06-14 12:40:58 -07001// run_cavp.go processes CAVP input files and generates suitable response
2// files, optionally comparing the results against the provided FAX files.
3package main
4
5import (
6 "bufio"
Robert Sloan8542c082018-02-05 09:07:34 -08007 "errors"
Robert Sloan8ff03552017-06-14 12:40:58 -07008 "flag"
9 "fmt"
10 "os"
11 "os/exec"
Robert Sloan8542c082018-02-05 09:07:34 -080012 "path"
Robert Sloan8ff03552017-06-14 12:40:58 -070013 "path/filepath"
14 "runtime"
15 "strings"
16 "sync"
17 "time"
18)
19
20var (
21 oraclePath = flag.String("oracle-bin", "", "Path to the oracle binary")
22 suiteDir = flag.String("suite-dir", "", "Base directory containing the CAVP test suite")
23 noFAX = flag.Bool("no-fax", false, "Skip comparing against FAX files")
Robert Sloan8542c082018-02-05 09:07:34 -080024 android = flag.Bool("android", false, "Run tests via ADB")
25)
26
27const (
28 androidTmpPath = "/data/local/tmp/"
29 androidCAVPPath = androidTmpPath + "cavp"
30 androidLibCryptoPath = androidTmpPath + "libcrypto.so"
Robert Sloan8ff03552017-06-14 12:40:58 -070031)
32
33// test describes a single request file.
34type test struct {
35 // inFile is the base of the filename without an extension, i.e.
36 // “ECBMCT128”.
37 inFile string
38 // args are the arguments (not including the input filename) to the
39 // oracle binary.
40 args []string
41 // noFAX, if true, indicates that the output cannot be compared against
42 // the FAX file. (E.g. because the primitive is non-deterministic.)
43 noFAX bool
44}
45
Robert Sloan0db7f542018-01-16 15:48:33 -080046// nextLineState can be used by FAX next-line function to store state.
47type nextLineState struct {
48 // State used by the KAS test.
49 nextIsIUTHash bool
50}
51
Robert Sloan8ff03552017-06-14 12:40:58 -070052// testSuite describes a series of tests that are handled by a single oracle
53// binary.
54type testSuite struct {
55 // directory is the name of the directory in the CAVP input, i.e. “AES”.
56 directory string
57 // suite names the test suite to pass as the first command-line argument.
58 suite string
Robert Sloan0db7f542018-01-16 15:48:33 -080059 // nextLineFunc, if not nil, is the function used to read the next line
60 // from the FAX file. This can be used to skip lines and/or mutate them
61 // as needed. The second argument can be used by the scanner to store
62 // state, if needed. If isWildcard is true on return then line is not
63 // meaningful and any line from the response file should be accepted.
64 nextLineFunc func(*bufio.Scanner, *nextLineState) (line string, isWildcard, ok bool)
65 tests []test
Robert Sloan8ff03552017-06-14 12:40:58 -070066}
67
68func (t *testSuite) getDirectory() string {
69 return filepath.Join(*suiteDir, t.directory)
70}
71
72var aesGCMTests = testSuite{
73 "AES_GCM",
74 "aes_gcm",
75 nil,
76 []test{
77 {"gcmDecrypt128", []string{"dec", "aes-128-gcm"}, false},
78 {"gcmDecrypt256", []string{"dec", "aes-256-gcm"}, false},
79 {"gcmEncryptExtIV128", []string{"enc", "aes-128-gcm"}, false},
80 {"gcmEncryptExtIV256", []string{"enc", "aes-256-gcm"}, false},
81 },
82}
83
84var aesTests = testSuite{
85 "AES",
86 "aes",
87 nil,
88 []test{
89 {"CBCGFSbox128", []string{"kat", "aes-128-cbc"}, false},
90 {"CBCGFSbox192", []string{"kat", "aes-192-cbc"}, false},
91 {"CBCGFSbox256", []string{"kat", "aes-256-cbc"}, false},
92 {"CBCKeySbox128", []string{"kat", "aes-128-cbc"}, false},
93 {"CBCKeySbox192", []string{"kat", "aes-192-cbc"}, false},
94 {"CBCKeySbox256", []string{"kat", "aes-256-cbc"}, false},
95 {"CBCMMT128", []string{"kat", "aes-128-cbc"}, false},
96 {"CBCMMT192", []string{"kat", "aes-192-cbc"}, false},
97 {"CBCMMT256", []string{"kat", "aes-256-cbc"}, false},
98 {"CBCVarKey128", []string{"kat", "aes-128-cbc"}, false},
99 {"CBCVarKey192", []string{"kat", "aes-192-cbc"}, false},
100 {"CBCVarKey256", []string{"kat", "aes-256-cbc"}, false},
101 {"CBCVarTxt128", []string{"kat", "aes-128-cbc"}, false},
102 {"CBCVarTxt192", []string{"kat", "aes-192-cbc"}, false},
103 {"CBCVarTxt256", []string{"kat", "aes-256-cbc"}, false},
104 {"ECBGFSbox128", []string{"kat", "aes-128-ecb"}, false},
105 {"ECBGFSbox192", []string{"kat", "aes-192-ecb"}, false},
106 {"ECBGFSbox256", []string{"kat", "aes-256-ecb"}, false},
107 {"ECBKeySbox128", []string{"kat", "aes-128-ecb"}, false},
108 {"ECBKeySbox192", []string{"kat", "aes-192-ecb"}, false},
109 {"ECBKeySbox256", []string{"kat", "aes-256-ecb"}, false},
110 {"ECBMMT128", []string{"kat", "aes-128-ecb"}, false},
111 {"ECBMMT192", []string{"kat", "aes-192-ecb"}, false},
112 {"ECBMMT256", []string{"kat", "aes-256-ecb"}, false},
113 {"ECBVarKey128", []string{"kat", "aes-128-ecb"}, false},
114 {"ECBVarKey192", []string{"kat", "aes-192-ecb"}, false},
115 {"ECBVarKey256", []string{"kat", "aes-256-ecb"}, false},
116 {"ECBVarTxt128", []string{"kat", "aes-128-ecb"}, false},
117 {"ECBVarTxt192", []string{"kat", "aes-192-ecb"}, false},
118 {"ECBVarTxt256", []string{"kat", "aes-256-ecb"}, false},
119 // AES Monte-Carlo tests
120 {"ECBMCT128", []string{"mct", "aes-128-ecb"}, false},
121 {"ECBMCT192", []string{"mct", "aes-192-ecb"}, false},
122 {"ECBMCT256", []string{"mct", "aes-256-ecb"}, false},
123 {"CBCMCT128", []string{"mct", "aes-128-cbc"}, false},
124 {"CBCMCT192", []string{"mct", "aes-192-cbc"}, false},
125 {"CBCMCT256", []string{"mct", "aes-256-cbc"}, false},
126 },
127}
128
129var ecdsa2KeyPairTests = testSuite{
130 "ECDSA2",
131 "ecdsa2_keypair",
132 nil,
133 []test{{"KeyPair", nil, true}},
134}
135
136var ecdsa2PKVTests = testSuite{
137 "ECDSA2",
138 "ecdsa2_pkv",
139 nil,
140 []test{{"PKV", nil, false}},
141}
142
143var ecdsa2SigGenTests = testSuite{
144 "ECDSA2",
145 "ecdsa2_siggen",
146 nil,
147 []test{
148 {"SigGen", []string{"SigGen"}, true},
149 {"SigGenComponent", []string{"SigGenComponent"}, true},
150 },
151}
152
153var ecdsa2SigVerTests = testSuite{
154 "ECDSA2",
155 "ecdsa2_sigver",
156 nil,
157 []test{{"SigVer", nil, false}},
158}
159
160var rsa2KeyGenTests = testSuite{
161 "RSA2",
162 "rsa2_keygen",
163 nil,
164 []test{
165 {"KeyGen_RandomProbablyPrime3_3", nil, true},
166 },
167}
168
169var rsa2SigGenTests = testSuite{
170 "RSA2",
171 "rsa2_siggen",
172 nil,
173 []test{
174 {"SigGen15_186-3", []string{"pkcs15"}, true},
175 {"SigGenPSS_186-3", []string{"pss"}, true},
176 },
177}
178
179var rsa2SigVerTests = testSuite{
180 "RSA2",
181 "rsa2_sigver",
Robert Sloan0db7f542018-01-16 15:48:33 -0800182 func(s *bufio.Scanner, state *nextLineState) (string, bool, bool) {
Robert Sloan8ff03552017-06-14 12:40:58 -0700183 for {
184 if !s.Scan() {
Robert Sloan0db7f542018-01-16 15:48:33 -0800185 return "", false, false
Robert Sloan8ff03552017-06-14 12:40:58 -0700186 }
187
188 line := s.Text()
189 if strings.HasPrefix(line, "p = ") || strings.HasPrefix(line, "d = ") || strings.HasPrefix(line, "SaltVal = ") || strings.HasPrefix(line, "EM with ") {
190 continue
191 }
192 if strings.HasPrefix(line, "q = ") {
193 // Skip the "q = " line and an additional blank line.
Robert Sloan0db7f542018-01-16 15:48:33 -0800194 if !s.Scan() ||
195 len(strings.TrimSpace(s.Text())) > 0 {
196 return "", false, false
Robert Sloan8ff03552017-06-14 12:40:58 -0700197 }
198 continue
199 }
Robert Sloan0db7f542018-01-16 15:48:33 -0800200 return line, false, true
Robert Sloan8ff03552017-06-14 12:40:58 -0700201 }
202 },
203 []test{
204 {"SigVer15_186-3", []string{"pkcs15"}, false},
205 {"SigVerPSS_186-3", []string{"pss"}, false},
206 },
207}
208
209var hmacTests = testSuite{
210 "HMAC",
211 "hmac",
212 nil,
213 []test{{"HMAC", nil, false}},
214}
215
216var shaTests = testSuite{
217 "SHA",
218 "sha",
219 nil,
220 []test{
221 {"SHA1LongMsg", []string{"SHA1"}, false},
222 {"SHA1ShortMsg", []string{"SHA1"}, false},
223 {"SHA224LongMsg", []string{"SHA224"}, false},
224 {"SHA224ShortMsg", []string{"SHA224"}, false},
225 {"SHA256LongMsg", []string{"SHA256"}, false},
226 {"SHA256ShortMsg", []string{"SHA256"}, false},
227 {"SHA384LongMsg", []string{"SHA384"}, false},
228 {"SHA384ShortMsg", []string{"SHA384"}, false},
229 {"SHA512LongMsg", []string{"SHA512"}, false},
230 {"SHA512ShortMsg", []string{"SHA512"}, false},
231 },
232}
233
234var shaMonteTests = testSuite{
235 "SHA",
236 "sha_monte",
237 nil,
238 []test{
239 {"SHA1Monte", []string{"SHA1"}, false},
240 {"SHA224Monte", []string{"SHA224"}, false},
241 {"SHA256Monte", []string{"SHA256"}, false},
242 {"SHA384Monte", []string{"SHA384"}, false},
243 {"SHA512Monte", []string{"SHA512"}, false},
244 },
245}
246
247var ctrDRBGTests = testSuite{
248 "DRBG800-90A",
249 "ctr_drbg",
250 nil,
251 []test{{"CTR_DRBG", nil, false}},
252}
253
254var tdesTests = testSuite{
255 "TDES",
256 "tdes",
257 nil,
258 []test{
259 {"TCBCMMT2", []string{"kat", "des-ede-cbc"}, false},
260 {"TCBCMMT3", []string{"kat", "des-ede3-cbc"}, false},
261 {"TCBCMonte2", []string{"mct", "des-ede3-cbc"}, false},
262 {"TCBCMonte3", []string{"mct", "des-ede3-cbc"}, false},
263 {"TCBCinvperm", []string{"kat", "des-ede3-cbc"}, false},
264 {"TCBCpermop", []string{"kat", "des-ede3-cbc"}, false},
265 {"TCBCsubtab", []string{"kat", "des-ede3-cbc"}, false},
266 {"TCBCvarkey", []string{"kat", "des-ede3-cbc"}, false},
267 {"TCBCvartext", []string{"kat", "des-ede3-cbc"}, false},
268 {"TECBMMT2", []string{"kat", "des-ede"}, false},
269 {"TECBMMT3", []string{"kat", "des-ede3"}, false},
270 {"TECBMonte2", []string{"mct", "des-ede3"}, false},
271 {"TECBMonte3", []string{"mct", "des-ede3"}, false},
272 {"TECBinvperm", []string{"kat", "des-ede3"}, false},
273 {"TECBpermop", []string{"kat", "des-ede3"}, false},
274 {"TECBsubtab", []string{"kat", "des-ede3"}, false},
275 {"TECBvarkey", []string{"kat", "des-ede3"}, false},
276 {"TECBvartext", []string{"kat", "des-ede3"}, false},
277 },
278}
279
280var keyWrapTests = testSuite{
281 "KeyWrap38F",
282 "keywrap",
283 nil,
284 []test{
285 {"KW_AD_128", []string{"dec", "128"}, false},
Pete Bentley0c61efe2019-08-13 09:32:23 +0100286 {"KW_AD_192", []string{"dec", "192"}, false},
Robert Sloan8ff03552017-06-14 12:40:58 -0700287 {"KW_AD_256", []string{"dec", "256"}, false},
288 {"KW_AE_128", []string{"enc", "128"}, false},
Pete Bentley0c61efe2019-08-13 09:32:23 +0100289 {"KW_AE_192", []string{"enc", "192"}, false},
Robert Sloan8ff03552017-06-14 12:40:58 -0700290 {"KW_AE_256", []string{"enc", "256"}, false},
Pete Bentley0c61efe2019-08-13 09:32:23 +0100291 {"KWP_AD_128", []string{"dec-pad", "128"}, false},
292 {"KWP_AD_192", []string{"dec-pad", "192"}, false},
293 {"KWP_AD_256", []string{"dec-pad", "256"}, false},
294 {"KWP_AE_128", []string{"enc-pad", "128"}, false},
295 {"KWP_AE_192", []string{"enc-pad", "192"}, false},
296 {"KWP_AE_256", []string{"enc-pad", "256"}, false},
Robert Sloan8ff03552017-06-14 12:40:58 -0700297 },
298}
299
Robert Sloan0db7f542018-01-16 15:48:33 -0800300var kasTests = testSuite{
301 "KAS",
302 "kas",
303 func(s *bufio.Scanner, state *nextLineState) (line string, isWildcard, ok bool) {
304 for {
305 // If the response file will include the IUT hash next,
306 // return a wildcard signal because this cannot be
307 // matched against the FAX file.
308 if state.nextIsIUTHash {
309 state.nextIsIUTHash = false
310 return "", true, true
311 }
312
313 if !s.Scan() {
314 return "", false, false
315 }
316
317 line := s.Text()
318 if strings.HasPrefix(line, "deCAVS = ") || strings.HasPrefix(line, "Z = ") {
319 continue
320 }
321 if strings.HasPrefix(line, "CAVSHashZZ = ") {
322 state.nextIsIUTHash = true
323 }
324 return line, false, true
325 }
326 },
327 []test{
328 {"KASFunctionTest_ECCEphemeralUnified_NOKC_ZZOnly_init", []string{"function"}, true},
329 {"KASFunctionTest_ECCEphemeralUnified_NOKC_ZZOnly_resp", []string{"function"}, true},
330 {"KASValidityTest_ECCEphemeralUnified_NOKC_ZZOnly_init", []string{"validity"}, false},
331 {"KASValidityTest_ECCEphemeralUnified_NOKC_ZZOnly_resp", []string{"validity"}, false},
332 },
333}
334
335var tlsKDFTests = testSuite{
336 "KDF135",
337 "tlskdf",
338 nil,
339 []test{
340 {"tls", nil, false},
341 },
342}
343
Robert Sloanab8b8882018-03-26 11:39:51 -0700344var testSuites = []*testSuite{
Robert Sloan8ff03552017-06-14 12:40:58 -0700345 &aesGCMTests,
346 &aesTests,
347 &ctrDRBGTests,
348 &ecdsa2KeyPairTests,
349 &ecdsa2PKVTests,
350 &ecdsa2SigGenTests,
351 &ecdsa2SigVerTests,
352 &hmacTests,
353 &keyWrapTests,
354 &rsa2KeyGenTests,
355 &rsa2SigGenTests,
356 &rsa2SigVerTests,
357 &shaTests,
358 &shaMonteTests,
359 &tdesTests,
Robert Sloan0db7f542018-01-16 15:48:33 -0800360 &kasTests,
361 &tlsKDFTests,
362}
363
Robert Sloan8ff03552017-06-14 12:40:58 -0700364// testInstance represents a specific test in a testSuite.
365type testInstance struct {
366 suite *testSuite
367 testIndex int
368}
369
370func worker(wg *sync.WaitGroup, work <-chan testInstance) {
371 defer wg.Done()
372
373 for ti := range work {
374 test := ti.suite.tests[ti.testIndex]
375
376 if err := doTest(ti.suite, test); err != nil {
377 fmt.Fprintf(os.Stderr, "%s\n", err)
378 os.Exit(2)
379 }
380
381 if !*noFAX && !test.noFAX {
382 if err := compareFAX(ti.suite, test); err != nil {
383 fmt.Fprintf(os.Stderr, "%s\n", err)
384 os.Exit(3)
385 }
386 }
387 }
388}
389
Robert Sloan8542c082018-02-05 09:07:34 -0800390func checkAndroidPrereqs() error {
391 // The cavp binary, and a matching libcrypto.so, are required to be placed
392 // in /data/local/tmp before running this script.
393 if err := exec.Command("adb", "shell", "ls", androidCAVPPath).Run(); err != nil {
394 return errors.New("failed to list cavp binary; ensure that adb works and cavp binary is in place: " + err.Error())
395 }
396 if err := exec.Command("adb", "shell", "ls", androidLibCryptoPath).Run(); err != nil {
397 return errors.New("failed to list libcrypto.so; ensure that library is in place: " + err.Error())
398 }
399 return nil
400}
401
Robert Sloan8ff03552017-06-14 12:40:58 -0700402func main() {
403 flag.Parse()
404
Robert Sloan8542c082018-02-05 09:07:34 -0800405 if *android {
406 if err := checkAndroidPrereqs(); err != nil {
407 fmt.Fprintf(os.Stderr, "%s\n", err)
408 os.Exit(1)
409 }
410 } else if len(*oraclePath) == 0 {
Robert Sloan8ff03552017-06-14 12:40:58 -0700411 fmt.Fprintf(os.Stderr, "Must give -oracle-bin\n")
412 os.Exit(1)
413 }
414
415 work := make(chan testInstance)
416 var wg sync.WaitGroup
417
Robert Sloan8542c082018-02-05 09:07:34 -0800418 numWorkers := runtime.NumCPU()
419 if *android {
420 numWorkers = 1
421 }
422
423 for i := 0; i < numWorkers; i++ {
Robert Sloan8ff03552017-06-14 12:40:58 -0700424 wg.Add(1)
425 go worker(&wg, work)
426 }
427
Robert Sloan0db7f542018-01-16 15:48:33 -0800428 for _, suite := range testSuites {
Robert Sloan8ff03552017-06-14 12:40:58 -0700429 for i := range suite.tests {
430 work <- testInstance{suite, i}
431 }
432 }
433
434 close(work)
435 wg.Wait()
436}
437
438func doTest(suite *testSuite, test test) error {
Robert Sloan8542c082018-02-05 09:07:34 -0800439 bin := *oraclePath
440 var args []string
441
442 if *android {
443 bin = "adb"
444 args = []string{"shell", "LD_LIBRARY_PATH=" + androidTmpPath, androidCAVPPath}
445 }
446
447 args = append(args, suite.suite)
Robert Sloan8ff03552017-06-14 12:40:58 -0700448 args = append(args, test.args...)
Robert Sloan8542c082018-02-05 09:07:34 -0800449 reqPath := filepath.Join(suite.getDirectory(), "req", test.inFile+".req")
450 var reqPathOnDevice string
451
452 if *android {
453 reqPathOnDevice = path.Join(androidTmpPath, test.inFile+".req")
454 if err := exec.Command("adb", "push", reqPath, reqPathOnDevice).Run(); err != nil {
455 return errors.New("failed to push request file: " + err.Error())
456 }
457 args = append(args, reqPathOnDevice)
458 } else {
459 args = append(args, reqPath)
460 }
Robert Sloan8ff03552017-06-14 12:40:58 -0700461
462 respDir := filepath.Join(suite.getDirectory(), "resp")
463 if err := os.Mkdir(respDir, 0755); err != nil && !os.IsExist(err) {
464 return fmt.Errorf("cannot create resp directory: %s", err)
465 }
466 outPath := filepath.Join(respDir, test.inFile+".rsp")
467 outFile, err := os.OpenFile(outPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
468 if err != nil {
469 return fmt.Errorf("cannot open output file for %q %q: %s", suite.getDirectory(), test.inFile, err)
470 }
471 defer outFile.Close()
472
Robert Sloan8542c082018-02-05 09:07:34 -0800473 cmd := exec.Command(bin, args...)
Robert Sloan8ff03552017-06-14 12:40:58 -0700474 cmd.Stdout = outFile
475 cmd.Stderr = os.Stderr
476
Robert Sloan8542c082018-02-05 09:07:34 -0800477 cmdLine := strings.Join(append([]string{bin}, args...), " ")
Robert Sloan8ff03552017-06-14 12:40:58 -0700478 startTime := time.Now()
479 if err := cmd.Run(); err != nil {
480 return fmt.Errorf("cannot run command for %q %q (%s): %s", suite.getDirectory(), test.inFile, cmdLine, err)
481 }
482
483 fmt.Printf("%s (%ds)\n", cmdLine, int(time.Since(startTime).Seconds()))
484
Robert Sloan8542c082018-02-05 09:07:34 -0800485 if *android {
486 exec.Command("adb", "shell", "rm", reqPathOnDevice).Run()
487 }
488
Robert Sloan8ff03552017-06-14 12:40:58 -0700489 return nil
490}
491
492func canonicalizeLine(in string) string {
493 if strings.HasPrefix(in, "Result = P (") {
494 return "Result = P"
495 }
496 if strings.HasPrefix(in, "Result = F (") {
497 return "Result = F"
498 }
499 return in
500}
501
502func compareFAX(suite *testSuite, test test) error {
Robert Sloan0db7f542018-01-16 15:48:33 -0800503 nextLineFunc := suite.nextLineFunc
504 if nextLineFunc == nil {
505 nextLineFunc = func(s *bufio.Scanner, state *nextLineState) (string, bool, bool) {
506 if !s.Scan() {
507 return "", false, false
508 }
509 return s.Text(), false, true
510 }
Robert Sloan8ff03552017-06-14 12:40:58 -0700511 }
512
513 respPath := filepath.Join(suite.getDirectory(), "resp", test.inFile+".rsp")
514 respFile, err := os.Open(respPath)
515 if err != nil {
516 return fmt.Errorf("cannot read output of %q %q: %s", suite.getDirectory(), test.inFile, err)
517 }
518 defer respFile.Close()
519
520 faxPath := filepath.Join(suite.getDirectory(), "fax", test.inFile+".fax")
521 faxFile, err := os.Open(faxPath)
522 if err != nil {
523 return fmt.Errorf("cannot open fax file for %q %q: %s", suite.getDirectory(), test.inFile, err)
524 }
525 defer faxFile.Close()
526
527 respScanner := bufio.NewScanner(respFile)
528 faxScanner := bufio.NewScanner(faxFile)
Robert Sloan0db7f542018-01-16 15:48:33 -0800529 var nextLineState nextLineState
Robert Sloan8ff03552017-06-14 12:40:58 -0700530
531 lineNo := 0
532 inHeader := true
533
534 for respScanner.Scan() {
535 lineNo++
536 respLine := respScanner.Text()
537 var faxLine string
Robert Sloan0db7f542018-01-16 15:48:33 -0800538 var isWildcard, ok bool
Robert Sloan8ff03552017-06-14 12:40:58 -0700539
540 if inHeader && (len(respLine) == 0 || respLine[0] == '#') {
541 continue
542 }
543
544 for {
545 haveFaxLine := false
546
547 if inHeader {
Robert Sloan0db7f542018-01-16 15:48:33 -0800548 for {
549 if faxLine, isWildcard, ok = nextLineFunc(faxScanner, &nextLineState); !ok {
550 break
551 }
Robert Sloan8ff03552017-06-14 12:40:58 -0700552 if len(faxLine) != 0 && faxLine[0] != '#' {
553 haveFaxLine = true
554 break
555 }
556 }
557
558 inHeader = false
559 } else {
Robert Sloan0db7f542018-01-16 15:48:33 -0800560 faxLine, isWildcard, haveFaxLine = nextLineFunc(faxScanner, &nextLineState)
Robert Sloan8ff03552017-06-14 12:40:58 -0700561 }
562
563 if !haveFaxLine {
564 // Ignore blank lines at the end of the generated file.
565 if len(respLine) == 0 {
566 break
567 }
568 return fmt.Errorf("resp file is longer than fax for %q %q", suite.getDirectory(), test.inFile)
569 }
570
571 if strings.HasPrefix(faxLine, " (Reason: ") {
572 continue
573 }
574
575 break
576 }
577
Robert Sloan0db7f542018-01-16 15:48:33 -0800578 if isWildcard || canonicalizeLine(faxLine) == canonicalizeLine(respLine) {
Robert Sloan8ff03552017-06-14 12:40:58 -0700579 continue
580 }
581
582 return fmt.Errorf("resp and fax differ at line %d for %q %q: %q vs %q", lineNo, suite.getDirectory(), test.inFile, respLine, faxLine)
583 }
584
Robert Sloan0db7f542018-01-16 15:48:33 -0800585 if _, _, ok := nextLineFunc(faxScanner, &nextLineState); ok {
Robert Sloan8ff03552017-06-14 12:40:58 -0700586 return fmt.Errorf("fax file is longer than resp for %q %q", suite.getDirectory(), test.inFile)
587 }
588
589 return nil
590}