blob: 438bb785669e17dd7529342b62e4be49df5c51cd [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},
286 {"KW_AD_256", []string{"dec", "256"}, false},
287 {"KW_AE_128", []string{"enc", "128"}, false},
288 {"KW_AE_256", []string{"enc", "256"}, false},
289 },
290}
291
Robert Sloan0db7f542018-01-16 15:48:33 -0800292var kasTests = testSuite{
293 "KAS",
294 "kas",
295 func(s *bufio.Scanner, state *nextLineState) (line string, isWildcard, ok bool) {
296 for {
297 // If the response file will include the IUT hash next,
298 // return a wildcard signal because this cannot be
299 // matched against the FAX file.
300 if state.nextIsIUTHash {
301 state.nextIsIUTHash = false
302 return "", true, true
303 }
304
305 if !s.Scan() {
306 return "", false, false
307 }
308
309 line := s.Text()
310 if strings.HasPrefix(line, "deCAVS = ") || strings.HasPrefix(line, "Z = ") {
311 continue
312 }
313 if strings.HasPrefix(line, "CAVSHashZZ = ") {
314 state.nextIsIUTHash = true
315 }
316 return line, false, true
317 }
318 },
319 []test{
320 {"KASFunctionTest_ECCEphemeralUnified_NOKC_ZZOnly_init", []string{"function"}, true},
321 {"KASFunctionTest_ECCEphemeralUnified_NOKC_ZZOnly_resp", []string{"function"}, true},
322 {"KASValidityTest_ECCEphemeralUnified_NOKC_ZZOnly_init", []string{"validity"}, false},
323 {"KASValidityTest_ECCEphemeralUnified_NOKC_ZZOnly_resp", []string{"validity"}, false},
324 },
325}
326
327var tlsKDFTests = testSuite{
328 "KDF135",
329 "tlskdf",
330 nil,
331 []test{
332 {"tls", nil, false},
333 },
334}
335
Robert Sloanab8b8882018-03-26 11:39:51 -0700336var testSuites = []*testSuite{
Robert Sloan8ff03552017-06-14 12:40:58 -0700337 &aesGCMTests,
338 &aesTests,
339 &ctrDRBGTests,
340 &ecdsa2KeyPairTests,
341 &ecdsa2PKVTests,
342 &ecdsa2SigGenTests,
343 &ecdsa2SigVerTests,
344 &hmacTests,
345 &keyWrapTests,
346 &rsa2KeyGenTests,
347 &rsa2SigGenTests,
348 &rsa2SigVerTests,
349 &shaTests,
350 &shaMonteTests,
351 &tdesTests,
Robert Sloan0db7f542018-01-16 15:48:33 -0800352 &kasTests,
353 &tlsKDFTests,
354}
355
Robert Sloan8ff03552017-06-14 12:40:58 -0700356// testInstance represents a specific test in a testSuite.
357type testInstance struct {
358 suite *testSuite
359 testIndex int
360}
361
362func worker(wg *sync.WaitGroup, work <-chan testInstance) {
363 defer wg.Done()
364
365 for ti := range work {
366 test := ti.suite.tests[ti.testIndex]
367
368 if err := doTest(ti.suite, test); err != nil {
369 fmt.Fprintf(os.Stderr, "%s\n", err)
370 os.Exit(2)
371 }
372
373 if !*noFAX && !test.noFAX {
374 if err := compareFAX(ti.suite, test); err != nil {
375 fmt.Fprintf(os.Stderr, "%s\n", err)
376 os.Exit(3)
377 }
378 }
379 }
380}
381
Robert Sloan8542c082018-02-05 09:07:34 -0800382func checkAndroidPrereqs() error {
383 // The cavp binary, and a matching libcrypto.so, are required to be placed
384 // in /data/local/tmp before running this script.
385 if err := exec.Command("adb", "shell", "ls", androidCAVPPath).Run(); err != nil {
386 return errors.New("failed to list cavp binary; ensure that adb works and cavp binary is in place: " + err.Error())
387 }
388 if err := exec.Command("adb", "shell", "ls", androidLibCryptoPath).Run(); err != nil {
389 return errors.New("failed to list libcrypto.so; ensure that library is in place: " + err.Error())
390 }
391 return nil
392}
393
Robert Sloan8ff03552017-06-14 12:40:58 -0700394func main() {
395 flag.Parse()
396
Robert Sloan8542c082018-02-05 09:07:34 -0800397 if *android {
398 if err := checkAndroidPrereqs(); err != nil {
399 fmt.Fprintf(os.Stderr, "%s\n", err)
400 os.Exit(1)
401 }
402 } else if len(*oraclePath) == 0 {
Robert Sloan8ff03552017-06-14 12:40:58 -0700403 fmt.Fprintf(os.Stderr, "Must give -oracle-bin\n")
404 os.Exit(1)
405 }
406
407 work := make(chan testInstance)
408 var wg sync.WaitGroup
409
Robert Sloan8542c082018-02-05 09:07:34 -0800410 numWorkers := runtime.NumCPU()
411 if *android {
412 numWorkers = 1
413 }
414
415 for i := 0; i < numWorkers; i++ {
Robert Sloan8ff03552017-06-14 12:40:58 -0700416 wg.Add(1)
417 go worker(&wg, work)
418 }
419
Robert Sloan0db7f542018-01-16 15:48:33 -0800420 for _, suite := range testSuites {
Robert Sloan8ff03552017-06-14 12:40:58 -0700421 for i := range suite.tests {
422 work <- testInstance{suite, i}
423 }
424 }
425
426 close(work)
427 wg.Wait()
428}
429
430func doTest(suite *testSuite, test test) error {
Robert Sloan8542c082018-02-05 09:07:34 -0800431 bin := *oraclePath
432 var args []string
433
434 if *android {
435 bin = "adb"
436 args = []string{"shell", "LD_LIBRARY_PATH=" + androidTmpPath, androidCAVPPath}
437 }
438
439 args = append(args, suite.suite)
Robert Sloan8ff03552017-06-14 12:40:58 -0700440 args = append(args, test.args...)
Robert Sloan8542c082018-02-05 09:07:34 -0800441 reqPath := filepath.Join(suite.getDirectory(), "req", test.inFile+".req")
442 var reqPathOnDevice string
443
444 if *android {
445 reqPathOnDevice = path.Join(androidTmpPath, test.inFile+".req")
446 if err := exec.Command("adb", "push", reqPath, reqPathOnDevice).Run(); err != nil {
447 return errors.New("failed to push request file: " + err.Error())
448 }
449 args = append(args, reqPathOnDevice)
450 } else {
451 args = append(args, reqPath)
452 }
Robert Sloan8ff03552017-06-14 12:40:58 -0700453
454 respDir := filepath.Join(suite.getDirectory(), "resp")
455 if err := os.Mkdir(respDir, 0755); err != nil && !os.IsExist(err) {
456 return fmt.Errorf("cannot create resp directory: %s", err)
457 }
458 outPath := filepath.Join(respDir, test.inFile+".rsp")
459 outFile, err := os.OpenFile(outPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
460 if err != nil {
461 return fmt.Errorf("cannot open output file for %q %q: %s", suite.getDirectory(), test.inFile, err)
462 }
463 defer outFile.Close()
464
Robert Sloan8542c082018-02-05 09:07:34 -0800465 cmd := exec.Command(bin, args...)
Robert Sloan8ff03552017-06-14 12:40:58 -0700466 cmd.Stdout = outFile
467 cmd.Stderr = os.Stderr
468
Robert Sloan8542c082018-02-05 09:07:34 -0800469 cmdLine := strings.Join(append([]string{bin}, args...), " ")
Robert Sloan8ff03552017-06-14 12:40:58 -0700470 startTime := time.Now()
471 if err := cmd.Run(); err != nil {
472 return fmt.Errorf("cannot run command for %q %q (%s): %s", suite.getDirectory(), test.inFile, cmdLine, err)
473 }
474
475 fmt.Printf("%s (%ds)\n", cmdLine, int(time.Since(startTime).Seconds()))
476
Robert Sloan8542c082018-02-05 09:07:34 -0800477 if *android {
478 exec.Command("adb", "shell", "rm", reqPathOnDevice).Run()
479 }
480
Robert Sloan8ff03552017-06-14 12:40:58 -0700481 return nil
482}
483
484func canonicalizeLine(in string) string {
485 if strings.HasPrefix(in, "Result = P (") {
486 return "Result = P"
487 }
488 if strings.HasPrefix(in, "Result = F (") {
489 return "Result = F"
490 }
491 return in
492}
493
494func compareFAX(suite *testSuite, test test) error {
Robert Sloan0db7f542018-01-16 15:48:33 -0800495 nextLineFunc := suite.nextLineFunc
496 if nextLineFunc == nil {
497 nextLineFunc = func(s *bufio.Scanner, state *nextLineState) (string, bool, bool) {
498 if !s.Scan() {
499 return "", false, false
500 }
501 return s.Text(), false, true
502 }
Robert Sloan8ff03552017-06-14 12:40:58 -0700503 }
504
505 respPath := filepath.Join(suite.getDirectory(), "resp", test.inFile+".rsp")
506 respFile, err := os.Open(respPath)
507 if err != nil {
508 return fmt.Errorf("cannot read output of %q %q: %s", suite.getDirectory(), test.inFile, err)
509 }
510 defer respFile.Close()
511
512 faxPath := filepath.Join(suite.getDirectory(), "fax", test.inFile+".fax")
513 faxFile, err := os.Open(faxPath)
514 if err != nil {
515 return fmt.Errorf("cannot open fax file for %q %q: %s", suite.getDirectory(), test.inFile, err)
516 }
517 defer faxFile.Close()
518
519 respScanner := bufio.NewScanner(respFile)
520 faxScanner := bufio.NewScanner(faxFile)
Robert Sloan0db7f542018-01-16 15:48:33 -0800521 var nextLineState nextLineState
Robert Sloan8ff03552017-06-14 12:40:58 -0700522
523 lineNo := 0
524 inHeader := true
525
526 for respScanner.Scan() {
527 lineNo++
528 respLine := respScanner.Text()
529 var faxLine string
Robert Sloan0db7f542018-01-16 15:48:33 -0800530 var isWildcard, ok bool
Robert Sloan8ff03552017-06-14 12:40:58 -0700531
532 if inHeader && (len(respLine) == 0 || respLine[0] == '#') {
533 continue
534 }
535
536 for {
537 haveFaxLine := false
538
539 if inHeader {
Robert Sloan0db7f542018-01-16 15:48:33 -0800540 for {
541 if faxLine, isWildcard, ok = nextLineFunc(faxScanner, &nextLineState); !ok {
542 break
543 }
Robert Sloan8ff03552017-06-14 12:40:58 -0700544 if len(faxLine) != 0 && faxLine[0] != '#' {
545 haveFaxLine = true
546 break
547 }
548 }
549
550 inHeader = false
551 } else {
Robert Sloan0db7f542018-01-16 15:48:33 -0800552 faxLine, isWildcard, haveFaxLine = nextLineFunc(faxScanner, &nextLineState)
Robert Sloan8ff03552017-06-14 12:40:58 -0700553 }
554
555 if !haveFaxLine {
556 // Ignore blank lines at the end of the generated file.
557 if len(respLine) == 0 {
558 break
559 }
560 return fmt.Errorf("resp file is longer than fax for %q %q", suite.getDirectory(), test.inFile)
561 }
562
563 if strings.HasPrefix(faxLine, " (Reason: ") {
564 continue
565 }
566
567 break
568 }
569
Robert Sloan0db7f542018-01-16 15:48:33 -0800570 if isWildcard || canonicalizeLine(faxLine) == canonicalizeLine(respLine) {
Robert Sloan8ff03552017-06-14 12:40:58 -0700571 continue
572 }
573
574 return fmt.Errorf("resp and fax differ at line %d for %q %q: %q vs %q", lineNo, suite.getDirectory(), test.inFile, respLine, faxLine)
575 }
576
Robert Sloan0db7f542018-01-16 15:48:33 -0800577 if _, _, ok := nextLineFunc(faxScanner, &nextLineState); ok {
Robert Sloan8ff03552017-06-14 12:40:58 -0700578 return fmt.Errorf("fax file is longer than resp for %q %q", suite.getDirectory(), test.inFile)
579 }
580
581 return nil
582}