blob: 587ba0701b723be52c37d42a03c08af27542c660 [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 Sloan0db7f542018-01-16 15:48:33 -080024 niap = flag.Bool("niap", false, "Perform NIAP tests rather than FIPS tests")
Robert Sloan8542c082018-02-05 09:07:34 -080025 android = flag.Bool("android", false, "Run tests via ADB")
26)
27
28const (
29 androidTmpPath = "/data/local/tmp/"
30 androidCAVPPath = androidTmpPath + "cavp"
31 androidLibCryptoPath = androidTmpPath + "libcrypto.so"
Robert Sloan8ff03552017-06-14 12:40:58 -070032)
33
34// test describes a single request file.
35type test struct {
36 // inFile is the base of the filename without an extension, i.e.
37 // “ECBMCT128”.
38 inFile string
39 // args are the arguments (not including the input filename) to the
40 // oracle binary.
41 args []string
42 // noFAX, if true, indicates that the output cannot be compared against
43 // the FAX file. (E.g. because the primitive is non-deterministic.)
44 noFAX bool
45}
46
Robert Sloan0db7f542018-01-16 15:48:33 -080047// nextLineState can be used by FAX next-line function to store state.
48type nextLineState struct {
49 // State used by the KAS test.
50 nextIsIUTHash bool
51}
52
Robert Sloan8ff03552017-06-14 12:40:58 -070053// testSuite describes a series of tests that are handled by a single oracle
54// binary.
55type testSuite struct {
56 // directory is the name of the directory in the CAVP input, i.e. “AES”.
57 directory string
58 // suite names the test suite to pass as the first command-line argument.
59 suite string
Robert Sloan0db7f542018-01-16 15:48:33 -080060 // nextLineFunc, if not nil, is the function used to read the next line
61 // from the FAX file. This can be used to skip lines and/or mutate them
62 // as needed. The second argument can be used by the scanner to store
63 // state, if needed. If isWildcard is true on return then line is not
64 // meaningful and any line from the response file should be accepted.
65 nextLineFunc func(*bufio.Scanner, *nextLineState) (line string, isWildcard, ok bool)
66 tests []test
Robert Sloan8ff03552017-06-14 12:40:58 -070067}
68
69func (t *testSuite) getDirectory() string {
70 return filepath.Join(*suiteDir, t.directory)
71}
72
73var aesGCMTests = testSuite{
74 "AES_GCM",
75 "aes_gcm",
76 nil,
77 []test{
78 {"gcmDecrypt128", []string{"dec", "aes-128-gcm"}, false},
79 {"gcmDecrypt256", []string{"dec", "aes-256-gcm"}, false},
80 {"gcmEncryptExtIV128", []string{"enc", "aes-128-gcm"}, false},
81 {"gcmEncryptExtIV256", []string{"enc", "aes-256-gcm"}, false},
82 },
83}
84
85var aesTests = testSuite{
86 "AES",
87 "aes",
88 nil,
89 []test{
90 {"CBCGFSbox128", []string{"kat", "aes-128-cbc"}, false},
91 {"CBCGFSbox192", []string{"kat", "aes-192-cbc"}, false},
92 {"CBCGFSbox256", []string{"kat", "aes-256-cbc"}, false},
93 {"CBCKeySbox128", []string{"kat", "aes-128-cbc"}, false},
94 {"CBCKeySbox192", []string{"kat", "aes-192-cbc"}, false},
95 {"CBCKeySbox256", []string{"kat", "aes-256-cbc"}, false},
96 {"CBCMMT128", []string{"kat", "aes-128-cbc"}, false},
97 {"CBCMMT192", []string{"kat", "aes-192-cbc"}, false},
98 {"CBCMMT256", []string{"kat", "aes-256-cbc"}, false},
99 {"CBCVarKey128", []string{"kat", "aes-128-cbc"}, false},
100 {"CBCVarKey192", []string{"kat", "aes-192-cbc"}, false},
101 {"CBCVarKey256", []string{"kat", "aes-256-cbc"}, false},
102 {"CBCVarTxt128", []string{"kat", "aes-128-cbc"}, false},
103 {"CBCVarTxt192", []string{"kat", "aes-192-cbc"}, false},
104 {"CBCVarTxt256", []string{"kat", "aes-256-cbc"}, false},
105 {"ECBGFSbox128", []string{"kat", "aes-128-ecb"}, false},
106 {"ECBGFSbox192", []string{"kat", "aes-192-ecb"}, false},
107 {"ECBGFSbox256", []string{"kat", "aes-256-ecb"}, false},
108 {"ECBKeySbox128", []string{"kat", "aes-128-ecb"}, false},
109 {"ECBKeySbox192", []string{"kat", "aes-192-ecb"}, false},
110 {"ECBKeySbox256", []string{"kat", "aes-256-ecb"}, false},
111 {"ECBMMT128", []string{"kat", "aes-128-ecb"}, false},
112 {"ECBMMT192", []string{"kat", "aes-192-ecb"}, false},
113 {"ECBMMT256", []string{"kat", "aes-256-ecb"}, false},
114 {"ECBVarKey128", []string{"kat", "aes-128-ecb"}, false},
115 {"ECBVarKey192", []string{"kat", "aes-192-ecb"}, false},
116 {"ECBVarKey256", []string{"kat", "aes-256-ecb"}, false},
117 {"ECBVarTxt128", []string{"kat", "aes-128-ecb"}, false},
118 {"ECBVarTxt192", []string{"kat", "aes-192-ecb"}, false},
119 {"ECBVarTxt256", []string{"kat", "aes-256-ecb"}, false},
120 // AES Monte-Carlo tests
121 {"ECBMCT128", []string{"mct", "aes-128-ecb"}, false},
122 {"ECBMCT192", []string{"mct", "aes-192-ecb"}, false},
123 {"ECBMCT256", []string{"mct", "aes-256-ecb"}, false},
124 {"CBCMCT128", []string{"mct", "aes-128-cbc"}, false},
125 {"CBCMCT192", []string{"mct", "aes-192-cbc"}, false},
126 {"CBCMCT256", []string{"mct", "aes-256-cbc"}, false},
127 },
128}
129
130var ecdsa2KeyPairTests = testSuite{
131 "ECDSA2",
132 "ecdsa2_keypair",
133 nil,
134 []test{{"KeyPair", nil, true}},
135}
136
137var ecdsa2PKVTests = testSuite{
138 "ECDSA2",
139 "ecdsa2_pkv",
140 nil,
141 []test{{"PKV", nil, false}},
142}
143
144var ecdsa2SigGenTests = testSuite{
145 "ECDSA2",
146 "ecdsa2_siggen",
147 nil,
148 []test{
149 {"SigGen", []string{"SigGen"}, true},
150 {"SigGenComponent", []string{"SigGenComponent"}, true},
151 },
152}
153
154var ecdsa2SigVerTests = testSuite{
155 "ECDSA2",
156 "ecdsa2_sigver",
157 nil,
158 []test{{"SigVer", nil, false}},
159}
160
161var rsa2KeyGenTests = testSuite{
162 "RSA2",
163 "rsa2_keygen",
164 nil,
165 []test{
166 {"KeyGen_RandomProbablyPrime3_3", nil, true},
167 },
168}
169
170var rsa2SigGenTests = testSuite{
171 "RSA2",
172 "rsa2_siggen",
173 nil,
174 []test{
175 {"SigGen15_186-3", []string{"pkcs15"}, true},
176 {"SigGenPSS_186-3", []string{"pss"}, true},
177 },
178}
179
180var rsa2SigVerTests = testSuite{
181 "RSA2",
182 "rsa2_sigver",
Robert Sloan0db7f542018-01-16 15:48:33 -0800183 func(s *bufio.Scanner, state *nextLineState) (string, bool, bool) {
Robert Sloan8ff03552017-06-14 12:40:58 -0700184 for {
185 if !s.Scan() {
Robert Sloan0db7f542018-01-16 15:48:33 -0800186 return "", false, false
Robert Sloan8ff03552017-06-14 12:40:58 -0700187 }
188
189 line := s.Text()
190 if strings.HasPrefix(line, "p = ") || strings.HasPrefix(line, "d = ") || strings.HasPrefix(line, "SaltVal = ") || strings.HasPrefix(line, "EM with ") {
191 continue
192 }
193 if strings.HasPrefix(line, "q = ") {
194 // Skip the "q = " line and an additional blank line.
Robert Sloan0db7f542018-01-16 15:48:33 -0800195 if !s.Scan() ||
196 len(strings.TrimSpace(s.Text())) > 0 {
197 return "", false, false
Robert Sloan8ff03552017-06-14 12:40:58 -0700198 }
199 continue
200 }
Robert Sloan0db7f542018-01-16 15:48:33 -0800201 return line, false, true
Robert Sloan8ff03552017-06-14 12:40:58 -0700202 }
203 },
204 []test{
205 {"SigVer15_186-3", []string{"pkcs15"}, false},
206 {"SigVerPSS_186-3", []string{"pss"}, false},
207 },
208}
209
210var hmacTests = testSuite{
211 "HMAC",
212 "hmac",
213 nil,
214 []test{{"HMAC", nil, false}},
215}
216
217var shaTests = testSuite{
218 "SHA",
219 "sha",
220 nil,
221 []test{
222 {"SHA1LongMsg", []string{"SHA1"}, false},
223 {"SHA1ShortMsg", []string{"SHA1"}, false},
224 {"SHA224LongMsg", []string{"SHA224"}, false},
225 {"SHA224ShortMsg", []string{"SHA224"}, false},
226 {"SHA256LongMsg", []string{"SHA256"}, false},
227 {"SHA256ShortMsg", []string{"SHA256"}, false},
228 {"SHA384LongMsg", []string{"SHA384"}, false},
229 {"SHA384ShortMsg", []string{"SHA384"}, false},
230 {"SHA512LongMsg", []string{"SHA512"}, false},
231 {"SHA512ShortMsg", []string{"SHA512"}, false},
232 },
233}
234
235var shaMonteTests = testSuite{
236 "SHA",
237 "sha_monte",
238 nil,
239 []test{
240 {"SHA1Monte", []string{"SHA1"}, false},
241 {"SHA224Monte", []string{"SHA224"}, false},
242 {"SHA256Monte", []string{"SHA256"}, false},
243 {"SHA384Monte", []string{"SHA384"}, false},
244 {"SHA512Monte", []string{"SHA512"}, false},
245 },
246}
247
248var ctrDRBGTests = testSuite{
249 "DRBG800-90A",
250 "ctr_drbg",
251 nil,
252 []test{{"CTR_DRBG", nil, false}},
253}
254
255var tdesTests = testSuite{
256 "TDES",
257 "tdes",
258 nil,
259 []test{
260 {"TCBCMMT2", []string{"kat", "des-ede-cbc"}, false},
261 {"TCBCMMT3", []string{"kat", "des-ede3-cbc"}, false},
262 {"TCBCMonte2", []string{"mct", "des-ede3-cbc"}, false},
263 {"TCBCMonte3", []string{"mct", "des-ede3-cbc"}, false},
264 {"TCBCinvperm", []string{"kat", "des-ede3-cbc"}, false},
265 {"TCBCpermop", []string{"kat", "des-ede3-cbc"}, false},
266 {"TCBCsubtab", []string{"kat", "des-ede3-cbc"}, false},
267 {"TCBCvarkey", []string{"kat", "des-ede3-cbc"}, false},
268 {"TCBCvartext", []string{"kat", "des-ede3-cbc"}, false},
269 {"TECBMMT2", []string{"kat", "des-ede"}, false},
270 {"TECBMMT3", []string{"kat", "des-ede3"}, false},
271 {"TECBMonte2", []string{"mct", "des-ede3"}, false},
272 {"TECBMonte3", []string{"mct", "des-ede3"}, false},
273 {"TECBinvperm", []string{"kat", "des-ede3"}, false},
274 {"TECBpermop", []string{"kat", "des-ede3"}, false},
275 {"TECBsubtab", []string{"kat", "des-ede3"}, false},
276 {"TECBvarkey", []string{"kat", "des-ede3"}, false},
277 {"TECBvartext", []string{"kat", "des-ede3"}, false},
278 },
279}
280
281var keyWrapTests = testSuite{
282 "KeyWrap38F",
283 "keywrap",
284 nil,
285 []test{
286 {"KW_AD_128", []string{"dec", "128"}, false},
287 {"KW_AD_256", []string{"dec", "256"}, false},
288 {"KW_AE_128", []string{"enc", "128"}, false},
289 {"KW_AE_256", []string{"enc", "256"}, false},
290 },
291}
292
Robert Sloan0db7f542018-01-16 15:48:33 -0800293var kasTests = testSuite{
294 "KAS",
295 "kas",
296 func(s *bufio.Scanner, state *nextLineState) (line string, isWildcard, ok bool) {
297 for {
298 // If the response file will include the IUT hash next,
299 // return a wildcard signal because this cannot be
300 // matched against the FAX file.
301 if state.nextIsIUTHash {
302 state.nextIsIUTHash = false
303 return "", true, true
304 }
305
306 if !s.Scan() {
307 return "", false, false
308 }
309
310 line := s.Text()
311 if strings.HasPrefix(line, "deCAVS = ") || strings.HasPrefix(line, "Z = ") {
312 continue
313 }
314 if strings.HasPrefix(line, "CAVSHashZZ = ") {
315 state.nextIsIUTHash = true
316 }
317 return line, false, true
318 }
319 },
320 []test{
321 {"KASFunctionTest_ECCEphemeralUnified_NOKC_ZZOnly_init", []string{"function"}, true},
322 {"KASFunctionTest_ECCEphemeralUnified_NOKC_ZZOnly_resp", []string{"function"}, true},
323 {"KASValidityTest_ECCEphemeralUnified_NOKC_ZZOnly_init", []string{"validity"}, false},
324 {"KASValidityTest_ECCEphemeralUnified_NOKC_ZZOnly_resp", []string{"validity"}, false},
325 },
326}
327
328var tlsKDFTests = testSuite{
329 "KDF135",
330 "tlskdf",
331 nil,
332 []test{
333 {"tls", nil, false},
334 },
335}
336
337var fipsTestSuites = []*testSuite{
Robert Sloan8ff03552017-06-14 12:40:58 -0700338 &aesGCMTests,
339 &aesTests,
340 &ctrDRBGTests,
341 &ecdsa2KeyPairTests,
342 &ecdsa2PKVTests,
343 &ecdsa2SigGenTests,
344 &ecdsa2SigVerTests,
345 &hmacTests,
346 &keyWrapTests,
347 &rsa2KeyGenTests,
348 &rsa2SigGenTests,
349 &rsa2SigVerTests,
350 &shaTests,
351 &shaMonteTests,
352 &tdesTests,
353}
354
Robert Sloan0db7f542018-01-16 15:48:33 -0800355var niapTestSuites = []*testSuite{
356 &kasTests,
357 &tlsKDFTests,
358}
359
Robert Sloan8ff03552017-06-14 12:40:58 -0700360// testInstance represents a specific test in a testSuite.
361type testInstance struct {
362 suite *testSuite
363 testIndex int
364}
365
366func worker(wg *sync.WaitGroup, work <-chan testInstance) {
367 defer wg.Done()
368
369 for ti := range work {
370 test := ti.suite.tests[ti.testIndex]
371
372 if err := doTest(ti.suite, test); err != nil {
373 fmt.Fprintf(os.Stderr, "%s\n", err)
374 os.Exit(2)
375 }
376
377 if !*noFAX && !test.noFAX {
378 if err := compareFAX(ti.suite, test); err != nil {
379 fmt.Fprintf(os.Stderr, "%s\n", err)
380 os.Exit(3)
381 }
382 }
383 }
384}
385
Robert Sloan8542c082018-02-05 09:07:34 -0800386func checkAndroidPrereqs() error {
387 // The cavp binary, and a matching libcrypto.so, are required to be placed
388 // in /data/local/tmp before running this script.
389 if err := exec.Command("adb", "shell", "ls", androidCAVPPath).Run(); err != nil {
390 return errors.New("failed to list cavp binary; ensure that adb works and cavp binary is in place: " + err.Error())
391 }
392 if err := exec.Command("adb", "shell", "ls", androidLibCryptoPath).Run(); err != nil {
393 return errors.New("failed to list libcrypto.so; ensure that library is in place: " + err.Error())
394 }
395 return nil
396}
397
Robert Sloan8ff03552017-06-14 12:40:58 -0700398func main() {
399 flag.Parse()
400
Robert Sloan8542c082018-02-05 09:07:34 -0800401 if *android {
402 if err := checkAndroidPrereqs(); err != nil {
403 fmt.Fprintf(os.Stderr, "%s\n", err)
404 os.Exit(1)
405 }
406 } else if len(*oraclePath) == 0 {
Robert Sloan8ff03552017-06-14 12:40:58 -0700407 fmt.Fprintf(os.Stderr, "Must give -oracle-bin\n")
408 os.Exit(1)
409 }
410
411 work := make(chan testInstance)
412 var wg sync.WaitGroup
413
Robert Sloan8542c082018-02-05 09:07:34 -0800414 numWorkers := runtime.NumCPU()
415 if *android {
416 numWorkers = 1
417 }
418
419 for i := 0; i < numWorkers; i++ {
Robert Sloan8ff03552017-06-14 12:40:58 -0700420 wg.Add(1)
421 go worker(&wg, work)
422 }
423
Robert Sloan0db7f542018-01-16 15:48:33 -0800424 testSuites := fipsTestSuites
425 if *niap {
426 testSuites = niapTestSuites
427 }
428
429 for _, suite := range testSuites {
Robert Sloan8ff03552017-06-14 12:40:58 -0700430 for i := range suite.tests {
431 work <- testInstance{suite, i}
432 }
433 }
434
435 close(work)
436 wg.Wait()
437}
438
439func doTest(suite *testSuite, test test) error {
Robert Sloan8542c082018-02-05 09:07:34 -0800440 bin := *oraclePath
441 var args []string
442
443 if *android {
444 bin = "adb"
445 args = []string{"shell", "LD_LIBRARY_PATH=" + androidTmpPath, androidCAVPPath}
446 }
447
448 args = append(args, suite.suite)
Robert Sloan8ff03552017-06-14 12:40:58 -0700449 args = append(args, test.args...)
Robert Sloan8542c082018-02-05 09:07:34 -0800450 reqPath := filepath.Join(suite.getDirectory(), "req", test.inFile+".req")
451 var reqPathOnDevice string
452
453 if *android {
454 reqPathOnDevice = path.Join(androidTmpPath, test.inFile+".req")
455 if err := exec.Command("adb", "push", reqPath, reqPathOnDevice).Run(); err != nil {
456 return errors.New("failed to push request file: " + err.Error())
457 }
458 args = append(args, reqPathOnDevice)
459 } else {
460 args = append(args, reqPath)
461 }
Robert Sloan8ff03552017-06-14 12:40:58 -0700462
463 respDir := filepath.Join(suite.getDirectory(), "resp")
464 if err := os.Mkdir(respDir, 0755); err != nil && !os.IsExist(err) {
465 return fmt.Errorf("cannot create resp directory: %s", err)
466 }
467 outPath := filepath.Join(respDir, test.inFile+".rsp")
468 outFile, err := os.OpenFile(outPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
469 if err != nil {
470 return fmt.Errorf("cannot open output file for %q %q: %s", suite.getDirectory(), test.inFile, err)
471 }
472 defer outFile.Close()
473
Robert Sloan8542c082018-02-05 09:07:34 -0800474 cmd := exec.Command(bin, args...)
Robert Sloan8ff03552017-06-14 12:40:58 -0700475 cmd.Stdout = outFile
476 cmd.Stderr = os.Stderr
477
Robert Sloan8542c082018-02-05 09:07:34 -0800478 cmdLine := strings.Join(append([]string{bin}, args...), " ")
Robert Sloan8ff03552017-06-14 12:40:58 -0700479 startTime := time.Now()
480 if err := cmd.Run(); err != nil {
481 return fmt.Errorf("cannot run command for %q %q (%s): %s", suite.getDirectory(), test.inFile, cmdLine, err)
482 }
483
484 fmt.Printf("%s (%ds)\n", cmdLine, int(time.Since(startTime).Seconds()))
485
Robert Sloan8542c082018-02-05 09:07:34 -0800486 if *android {
487 exec.Command("adb", "shell", "rm", reqPathOnDevice).Run()
488 }
489
Robert Sloan8ff03552017-06-14 12:40:58 -0700490 return nil
491}
492
493func canonicalizeLine(in string) string {
494 if strings.HasPrefix(in, "Result = P (") {
495 return "Result = P"
496 }
497 if strings.HasPrefix(in, "Result = F (") {
498 return "Result = F"
499 }
500 return in
501}
502
503func compareFAX(suite *testSuite, test test) error {
Robert Sloan0db7f542018-01-16 15:48:33 -0800504 nextLineFunc := suite.nextLineFunc
505 if nextLineFunc == nil {
506 nextLineFunc = func(s *bufio.Scanner, state *nextLineState) (string, bool, bool) {
507 if !s.Scan() {
508 return "", false, false
509 }
510 return s.Text(), false, true
511 }
Robert Sloan8ff03552017-06-14 12:40:58 -0700512 }
513
514 respPath := filepath.Join(suite.getDirectory(), "resp", test.inFile+".rsp")
515 respFile, err := os.Open(respPath)
516 if err != nil {
517 return fmt.Errorf("cannot read output of %q %q: %s", suite.getDirectory(), test.inFile, err)
518 }
519 defer respFile.Close()
520
521 faxPath := filepath.Join(suite.getDirectory(), "fax", test.inFile+".fax")
522 faxFile, err := os.Open(faxPath)
523 if err != nil {
524 return fmt.Errorf("cannot open fax file for %q %q: %s", suite.getDirectory(), test.inFile, err)
525 }
526 defer faxFile.Close()
527
528 respScanner := bufio.NewScanner(respFile)
529 faxScanner := bufio.NewScanner(faxFile)
Robert Sloan0db7f542018-01-16 15:48:33 -0800530 var nextLineState nextLineState
Robert Sloan8ff03552017-06-14 12:40:58 -0700531
532 lineNo := 0
533 inHeader := true
534
535 for respScanner.Scan() {
536 lineNo++
537 respLine := respScanner.Text()
538 var faxLine string
Robert Sloan0db7f542018-01-16 15:48:33 -0800539 var isWildcard, ok bool
Robert Sloan8ff03552017-06-14 12:40:58 -0700540
541 if inHeader && (len(respLine) == 0 || respLine[0] == '#') {
542 continue
543 }
544
545 for {
546 haveFaxLine := false
547
548 if inHeader {
Robert Sloan0db7f542018-01-16 15:48:33 -0800549 for {
550 if faxLine, isWildcard, ok = nextLineFunc(faxScanner, &nextLineState); !ok {
551 break
552 }
Robert Sloan8ff03552017-06-14 12:40:58 -0700553 if len(faxLine) != 0 && faxLine[0] != '#' {
554 haveFaxLine = true
555 break
556 }
557 }
558
559 inHeader = false
560 } else {
Robert Sloan0db7f542018-01-16 15:48:33 -0800561 faxLine, isWildcard, haveFaxLine = nextLineFunc(faxScanner, &nextLineState)
Robert Sloan8ff03552017-06-14 12:40:58 -0700562 }
563
564 if !haveFaxLine {
565 // Ignore blank lines at the end of the generated file.
566 if len(respLine) == 0 {
567 break
568 }
569 return fmt.Errorf("resp file is longer than fax for %q %q", suite.getDirectory(), test.inFile)
570 }
571
572 if strings.HasPrefix(faxLine, " (Reason: ") {
573 continue
574 }
575
576 break
577 }
578
Robert Sloan0db7f542018-01-16 15:48:33 -0800579 if isWildcard || canonicalizeLine(faxLine) == canonicalizeLine(respLine) {
Robert Sloan8ff03552017-06-14 12:40:58 -0700580 continue
581 }
582
583 return fmt.Errorf("resp and fax differ at line %d for %q %q: %q vs %q", lineNo, suite.getDirectory(), test.inFile, respLine, faxLine)
584 }
585
Robert Sloan0db7f542018-01-16 15:48:33 -0800586 if _, _, ok := nextLineFunc(faxScanner, &nextLineState); ok {
Robert Sloan8ff03552017-06-14 12:40:58 -0700587 return fmt.Errorf("fax file is longer than resp for %q %q", suite.getDirectory(), test.inFile)
588 }
589
590 return nil
591}