1// Copyright 2017 The BoringSSL Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// https://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15// The make_cavp utility generates cipher_test input files from NIST CAVP Known 16// Answer Test response (.rsp) files. 17package main 18 19import ( 20 "bufio" 21 "flag" 22 "fmt" 23 "io" 24 "log" 25 "os" 26 "strings" 27) 28 29var ( 30 cipher = flag.String("cipher", "", "The name of the cipher/mode (supported: aes, tdes, gcm). Required.") 31 cmdLineLabelStr = flag.String("extra-labels", "", "Comma-separated list of additional label pairs to add (e.g. 'Cipher=AES-128-CBC,Operation=ENCRYPT')") 32 swapIVAndPlaintext = flag.Bool("swap-iv-plaintext", false, "When processing CBC vector files for CTR mode, swap IV and plaintext.") 33) 34 35type kvPair struct { 36 key, value string 37} 38 39// Test generates a FileTest file from a CAVP response file. 40type Test struct { 41 translations map[kvPair]kvPair 42 transform func(k, v string) []kvPair 43 defaults map[string]string 44 // kvDelim is the rune that delimits key-value pairs throughout the file ('=' or ':'). 45 kvDelim rune 46} 47 48func (t *Test) parseKeyValue(s string) (key, value string) { 49 if t.kvDelim == 0 { 50 i := strings.IndexAny(s, "=:") 51 if i != -1 { 52 t.kvDelim = rune(s[i]) 53 } 54 } 55 if i := strings.IndexRune(s, t.kvDelim); t.kvDelim != 0 && i != -1 { 56 key, value = s[:i], s[i+1:] 57 if trimmed := strings.TrimSpace(value); len(trimmed) != 0 { 58 value = trimmed 59 } else { 60 value = " " 61 } 62 } else { 63 key = s 64 } 65 return strings.TrimSpace(key), value 66} 67 68func (t *Test) translateKeyValue(key, value string) (string, string) { 69 if kv, ok := t.translations[kvPair{key, ""}]; ok { 70 if len(kv.value) == 0 && len(value) != 0 { 71 return kv.key, value 72 } 73 return kv.key, kv.value 74 } 75 if kv, ok := t.translations[kvPair{key, value}]; ok { 76 return kv.key, kv.value 77 } 78 return key, value 79} 80 81func printKeyValue(key, value string) { 82 if len(value) == 0 { 83 fmt.Println(key) 84 } else { 85 // Omit the value if it is " ", i.e. print "key: ", not "key: ". 86 value = strings.TrimSpace(value) 87 fmt.Printf("%s: %s\n", key, value) 88 } 89} 90 91func (t *Test) generate(r io.Reader, cmdLineLabelStr string) { 92 s := bufio.NewScanner(r) 93 94 // Label blocks consist of lines of the form "[key]" or "[key = 95 // value]". |labels| holds keys and values of the most recent block 96 // of labels. 97 var labels map[string]string 98 99 // Auxiliary labels passed as a flag. 100 cmdLineLabels := make(map[string]string) 101 if len(cmdLineLabelStr) != 0 { 102 pairs := strings.Split(cmdLineLabelStr, ",") 103 for _, p := range pairs { 104 key, value := t.parseKeyValue(p) 105 cmdLineLabels[key] = value 106 } 107 } 108 109 t.kvDelim = 0 // Reset kvDelim for scanning the file. 110 111 // Whether we are in a test or a label section. 112 inLabels := false 113 inTest := false 114 115 n := 0 116 var currentKv map[string]string 117 for s.Scan() { 118 n++ 119 line := s.Text() 120 l := strings.TrimSpace(line) 121 l = strings.SplitN(l, "#", 2)[0] // Trim trailing comments. 122 123 // Blank line. 124 if len(l) == 0 { 125 if inTest { 126 // Fill in missing defaults at the end of a test case. 127 for k, v := range t.defaults { 128 if _, ok := currentKv[k]; !ok { 129 printKeyValue(k, v) 130 } 131 } 132 fmt.Println() 133 } 134 inTest = false 135 currentKv = make(map[string]string) 136 inLabels = false 137 continue 138 } 139 140 // Label section. 141 if l[0] == '[' { 142 if l[len(l)-1] != ']' { 143 log.Fatalf("line #%d invalid: %q", n, line) 144 } 145 if !inLabels { 146 labels = make(map[string]string) 147 inLabels = true 148 } 149 150 k, v := t.parseKeyValue(l[1 : len(l)-1]) 151 k, v = t.translateKeyValue(k, v) 152 if len(k) != 0 { 153 labels[k] = v 154 } 155 156 continue 157 } 158 159 // Repeat the label map at the beginning of each test section. 160 if !inTest { 161 inTest = true 162 for k, v := range cmdLineLabels { 163 printKeyValue(k, v) 164 currentKv[k] = v 165 } 166 for k, v := range labels { 167 printKeyValue(k, v) 168 currentKv[k] = v 169 } 170 } 171 172 // Look up translation and apply transformation (if any). 173 k, v := t.parseKeyValue(l) 174 k, v = t.translateKeyValue(k, v) 175 kvPairs := []kvPair{{k, v}} 176 if t.transform != nil { 177 kvPairs = t.transform(k, v) 178 } 179 180 for _, kv := range kvPairs { 181 k, v := kv.key, kv.value 182 if *cipher == "tdes" && k == "Key" { 183 v += v + v // Key1=Key2=Key3 184 } 185 if len(k) != 0 { 186 printKeyValue(k, v) 187 currentKv[k] = v 188 } 189 } 190 } 191} 192 193func usage() { 194 fmt.Fprintln(os.Stderr, "usage: make_cavp <file 1> [<file 2> ...]") 195 flag.PrintDefaults() 196} 197 198func maybeSwapIVAndPlaintext(k, v string) []kvPair { 199 if *swapIVAndPlaintext { 200 if k == "Plaintext" { 201 return []kvPair{{"IV", v}} 202 } else if k == "IV" { 203 return []kvPair{{"Plaintext", v}} 204 } 205 } 206 return []kvPair{{k, v}} 207} 208 209// Test generator for different values of the -cipher flag. 210var testMap = map[string]Test{ 211 // Generate cipher_test input file from AESVS .rsp file. 212 "aes": Test{ 213 translations: map[kvPair]kvPair{ 214 {"ENCRYPT", ""}: {"Operation", "ENCRYPT"}, 215 {"DECRYPT", ""}: {"Operation", "DECRYPT"}, 216 {"COUNT", ""}: {"Count", ""}, 217 {"KEY", ""}: {"Key", ""}, 218 {"PLAINTEXT", ""}: {"Plaintext", ""}, 219 {"CIPHERTEXT", ""}: {"Ciphertext", ""}, 220 {"COUNT", ""}: {"", ""}, // delete 221 }, 222 transform: maybeSwapIVAndPlaintext, 223 }, 224 // Generate cipher_test input file from TMOVS .rsp file. 225 "tdes": Test{ 226 translations: map[kvPair]kvPair{ 227 {"ENCRYPT", ""}: {"Operation", "ENCRYPT"}, 228 {"DECRYPT", ""}: {"Operation", "DECRYPT"}, 229 {"COUNT", ""}: {"Count", ""}, 230 {"KEYs", ""}: {"Key", ""}, 231 {"PLAINTEXT", ""}: {"Plaintext", ""}, 232 {"CIPHERTEXT", ""}: {"Ciphertext", ""}, 233 {"COUNT", ""}: {"", ""}, // delete 234 }, 235 transform: maybeSwapIVAndPlaintext, 236 }, 237 // Generate aead_test input file from GCMVS .rsp file. 238 "gcm": Test{ 239 translations: map[kvPair]kvPair{ 240 {"Keylen", ""}: {"", ""}, // delete 241 {"IVlen", ""}: {"", ""}, // delete 242 {"PTlen", ""}: {"", ""}, // delete 243 {"AADlen", ""}: {"", ""}, // delete 244 {"Taglen", ""}: {"", ""}, // delete 245 {"Count", ""}: {"", ""}, // delete 246 {"Key", ""}: {"KEY", ""}, 247 {"IV", ""}: {"NONCE", ""}, 248 {"PT", ""}: {"IN", ""}, 249 {"AAD", ""}: {"AD", ""}, 250 {"Tag", ""}: {"TAG", ""}, 251 {"FAIL", ""}: {"FAILS", " "}, 252 }, 253 transform: func(k, v string) []kvPair { 254 if k == "FAILS" { 255 // FAIL cases only appear in the decrypt rsp files. Skip encryption for 256 // these. 257 return []kvPair{{"FAILS", " "}, {"NO_SEAL", " "}} 258 } 259 return []kvPair{{k, v}} 260 }, 261 defaults: map[string]string{ 262 "IN": " ", // FAIL tests don't have IN 263 }, 264 }, 265} 266 267func main() { 268 flag.Usage = usage 269 flag.Parse() 270 271 if len(flag.Args()) == 0 { 272 fmt.Fprintf(os.Stderr, "no input files\n\n") 273 flag.Usage() 274 os.Exit(1) 275 } 276 277 test, ok := testMap[*cipher] 278 if !ok { 279 fmt.Fprintf(os.Stderr, "invalid cipher: %q\n\n", *cipher) 280 flag.Usage() 281 os.Exit(1) 282 } 283 284 args := append([]string{"make_cavp"}, os.Args[1:]...) 285 fmt.Printf("# Generated by %q\n\n", strings.Join(args, " ")) 286 287 for i, p := range flag.Args() { 288 f, err := os.Open(p) 289 if err != nil { 290 log.Fatal(err) 291 } 292 defer f.Close() 293 294 fmt.Printf("# File %d: %s\n\n", i+1, p) 295 test.generate(f, *cmdLineLabelStr) 296 } 297} 298