| // Copyright (c) 2017, Google Inc. |
| // |
| // Permission to use, copy, modify, and/or distribute this software for any |
| // purpose with or without fee is hereby granted, provided that the above |
| // copyright notice and this permission notice appear in all copies. |
| // |
| // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
| // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
| // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
| // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| |
| // The make_cavp utility generates cipher_test input files from NIST CAVP Known |
| // Answer Test response (.rsp) files. |
| package main |
| |
| import ( |
| "bufio" |
| "flag" |
| "fmt" |
| "io" |
| "log" |
| "os" |
| "strings" |
| ) |
| |
| var ( |
| cipher = flag.String("cipher", "", "The name of the cipher/mode (supported: aes, tdes, gcm). Required.") |
| cmdLineLabelStr = flag.String("extra-labels", "", "Comma-separated list of additional label pairs to add (e.g. 'Cipher=AES-128-CBC,Operation=ENCRYPT')") |
| swapIVAndPlaintext = flag.Bool("swap-iv-plaintext", false, "When processing CBC vector files for CTR mode, swap IV and plaintext.") |
| ) |
| |
| type kvPair struct { |
| key, value string |
| } |
| |
| // Test generates a FileTest file from a CAVP response file. |
| type Test struct { |
| translations map[kvPair]kvPair |
| transform func(k, v string) []kvPair |
| defaults map[string]string |
| // kvDelim is the rune that delimits key-value pairs throughout the file ('=' or ':'). |
| kvDelim rune |
| } |
| |
| func (t *Test) parseKeyValue(s string) (key, value string) { |
| if t.kvDelim == 0 { |
| i := strings.IndexAny(s, "=:") |
| if i != -1 { |
| t.kvDelim = rune(s[i]) |
| } |
| } |
| if i := strings.IndexRune(s, t.kvDelim); t.kvDelim != 0 && i != -1 { |
| key, value = s[:i], s[i+1:] |
| if trimmed := strings.TrimSpace(value); len(trimmed) != 0 { |
| value = trimmed |
| } else { |
| value = " " |
| } |
| } else { |
| key = s |
| } |
| return strings.TrimSpace(key), value |
| } |
| |
| func (t *Test) translateKeyValue(key, value string) (string, string) { |
| if kv, ok := t.translations[kvPair{key, ""}]; ok { |
| if len(kv.value) == 0 && len(value) != 0 { |
| return kv.key, value |
| } |
| return kv.key, kv.value |
| } |
| if kv, ok := t.translations[kvPair{key, value}]; ok { |
| return kv.key, kv.value |
| } |
| return key, value |
| } |
| |
| func printKeyValue(key, value string) { |
| if len(value) == 0 { |
| fmt.Println(key) |
| } else { |
| // Omit the value if it is " ", i.e. print "key: ", not "key: ". |
| value = strings.TrimSpace(value) |
| fmt.Printf("%s: %s\n", key, value) |
| } |
| } |
| |
| func (t *Test) generate(r io.Reader, cmdLineLabelStr string) { |
| s := bufio.NewScanner(r) |
| |
| // Label blocks consist of lines of the form "[key]" or "[key = |
| // value]". |labels| holds keys and values of the most recent block |
| // of labels. |
| var labels map[string]string |
| |
| // Auxiliary labels passed as a flag. |
| cmdLineLabels := make(map[string]string) |
| if len(cmdLineLabelStr) != 0 { |
| pairs := strings.Split(cmdLineLabelStr, ",") |
| for _, p := range pairs { |
| key, value := t.parseKeyValue(p) |
| cmdLineLabels[key] = value |
| } |
| } |
| |
| t.kvDelim = 0 // Reset kvDelim for scanning the file. |
| |
| // Whether we are in a test or a label section. |
| inLabels := false |
| inTest := false |
| |
| n := 0 |
| var currentKv map[string]string |
| for s.Scan() { |
| n++ |
| line := s.Text() |
| l := strings.TrimSpace(line) |
| l = strings.SplitN(l, "#", 2)[0] // Trim trailing comments. |
| |
| // Blank line. |
| if len(l) == 0 { |
| if inTest { |
| // Fill in missing defaults at the end of a test case. |
| for k, v := range t.defaults { |
| if _, ok := currentKv[k]; !ok { |
| printKeyValue(k, v) |
| } |
| } |
| fmt.Println() |
| } |
| inTest = false |
| currentKv = make(map[string]string) |
| inLabels = false |
| continue |
| } |
| |
| // Label section. |
| if l[0] == '[' { |
| if l[len(l)-1] != ']' { |
| log.Fatalf("line #%d invalid: %q", n, line) |
| } |
| if !inLabels { |
| labels = make(map[string]string) |
| inLabels = true |
| } |
| |
| k, v := t.parseKeyValue(l[1 : len(l)-1]) |
| k, v = t.translateKeyValue(k, v) |
| if len(k) != 0 { |
| labels[k] = v |
| } |
| |
| continue |
| } |
| |
| // Repeat the label map at the beginning of each test section. |
| if !inTest { |
| inTest = true |
| for k, v := range cmdLineLabels { |
| printKeyValue(k, v) |
| currentKv[k] = v |
| } |
| for k, v := range labels { |
| printKeyValue(k, v) |
| currentKv[k] = v |
| } |
| } |
| |
| // Look up translation and apply transformation (if any). |
| k, v := t.parseKeyValue(l) |
| k, v = t.translateKeyValue(k, v) |
| kvPairs := []kvPair{{k, v}} |
| if t.transform != nil { |
| kvPairs = t.transform(k, v) |
| } |
| |
| for _, kv := range kvPairs { |
| k, v := kv.key, kv.value |
| if *cipher == "tdes" && k == "Key" { |
| v += v + v // Key1=Key2=Key3 |
| } |
| if len(k) != 0 { |
| printKeyValue(k, v) |
| currentKv[k] = v |
| } |
| } |
| } |
| } |
| |
| func usage() { |
| fmt.Fprintln(os.Stderr, "usage: make_cavp <file 1> [<file 2> ...]") |
| flag.PrintDefaults() |
| } |
| |
| func maybeSwapIVAndPlaintext(k, v string) []kvPair { |
| if *swapIVAndPlaintext { |
| if k == "Plaintext" { |
| return []kvPair{{"IV", v}} |
| } else if k == "IV" { |
| return []kvPair{{"Plaintext", v}} |
| } |
| } |
| return []kvPair{{k, v}} |
| } |
| |
| // Test generator for different values of the -cipher flag. |
| var testMap = map[string]Test{ |
| // Generate cipher_test input file from AESVS .rsp file. |
| "aes": Test{ |
| translations: map[kvPair]kvPair{ |
| {"ENCRYPT", ""}: {"Operation", "ENCRYPT"}, |
| {"DECRYPT", ""}: {"Operation", "DECRYPT"}, |
| {"COUNT", ""}: {"Count", ""}, |
| {"KEY", ""}: {"Key", ""}, |
| {"PLAINTEXT", ""}: {"Plaintext", ""}, |
| {"CIPHERTEXT", ""}: {"Ciphertext", ""}, |
| {"COUNT", ""}: {"", ""}, // delete |
| }, |
| transform: maybeSwapIVAndPlaintext, |
| }, |
| // Generate cipher_test input file from TMOVS .rsp file. |
| "tdes": Test{ |
| translations: map[kvPair]kvPair{ |
| {"ENCRYPT", ""}: {"Operation", "ENCRYPT"}, |
| {"DECRYPT", ""}: {"Operation", "DECRYPT"}, |
| {"COUNT", ""}: {"Count", ""}, |
| {"KEYs", ""}: {"Key", ""}, |
| {"PLAINTEXT", ""}: {"Plaintext", ""}, |
| {"CIPHERTEXT", ""}: {"Ciphertext", ""}, |
| {"COUNT", ""}: {"", ""}, // delete |
| }, |
| transform: maybeSwapIVAndPlaintext, |
| }, |
| // Generate aead_test input file from GCMVS .rsp file. |
| "gcm": Test{ |
| translations: map[kvPair]kvPair{ |
| {"Keylen", ""}: {"", ""}, // delete |
| {"IVlen", ""}: {"", ""}, // delete |
| {"PTlen", ""}: {"", ""}, // delete |
| {"AADlen", ""}: {"", ""}, // delete |
| {"Taglen", ""}: {"", ""}, // delete |
| {"Count", ""}: {"", ""}, // delete |
| {"Key", ""}: {"KEY", ""}, |
| {"IV", ""}: {"NONCE", ""}, |
| {"PT", ""}: {"IN", ""}, |
| {"AAD", ""}: {"AD", ""}, |
| {"Tag", ""}: {"TAG", ""}, |
| {"FAIL", ""}: {"FAILS", " "}, |
| }, |
| transform: func(k, v string) []kvPair { |
| if k == "FAILS" { |
| // FAIL cases only appear in the decrypt rsp files. Skip encryption for |
| // these. |
| return []kvPair{{"FAILS", " "}, {"NO_SEAL", " "}} |
| } |
| return []kvPair{{k, v}} |
| }, |
| defaults: map[string]string{ |
| "IN": " ", // FAIL tests don't have IN |
| }, |
| }, |
| } |
| |
| func main() { |
| flag.Usage = usage |
| flag.Parse() |
| |
| if len(flag.Args()) == 0 { |
| fmt.Fprintf(os.Stderr, "no input files\n\n") |
| flag.Usage() |
| os.Exit(1) |
| } |
| |
| test, ok := testMap[*cipher] |
| if !ok { |
| fmt.Fprintf(os.Stderr, "invalid cipher: %q\n\n", *cipher) |
| flag.Usage() |
| os.Exit(1) |
| } |
| |
| args := append([]string{"make_cavp"}, os.Args[1:]...) |
| fmt.Printf("# Generated by %q\n\n", strings.Join(args, " ")) |
| |
| for i, p := range flag.Args() { |
| f, err := os.Open(p) |
| if err != nil { |
| log.Fatal(err) |
| } |
| defer f.Close() |
| |
| fmt.Printf("# File %d: %s\n\n", i+1, p) |
| test.generate(f, *cmdLineLabelStr) |
| } |
| } |