1/* Copyright (c) 2018, Google Inc. 2 * 3 * Permission to use, copy, modify, and/or distribute this software for any 4 * purpose with or without fee is hereby granted, provided that the above 5 * copyright notice and this permission notice appear in all copies. 6 * 7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 10 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 12 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 13 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ 14 15// convert_wycheproof.go converts Wycheproof test vectors into a format more 16// easily consumed by BoringSSL. 17package main 18 19import ( 20 "encoding/json" 21 "fmt" 22 "io" 23 "io/ioutil" 24 "os" 25 "sort" 26 "strings" 27) 28 29type wycheproofTest struct { 30 Algorithm string `json:"algorithm"` 31 GeneratorVersion string `json:"generatorVersion"` 32 NumberOfTests int `json:"numberOfTests"` 33 Notes map[string]string `json:"notes"` 34 Header []string `json:"header"` 35 // encoding/json does not support collecting unused keys, so we leave 36 // everything past this point as generic. 37 TestGroups []map[string]interface{} `json:"testGroups"` 38} 39 40func sortedKeys(m map[string]interface{}) []string { 41 keys := make([]string, 0, len(m)) 42 for k, _ := range m { 43 keys = append(keys, k) 44 } 45 sort.Strings(keys) 46 return keys 47} 48 49func printAttribute(w io.Writer, key string, valueI interface{}, isInstruction bool) error { 50 switch value := valueI.(type) { 51 case float64: 52 if float64(int(value)) != value { 53 panic(key + "was not an integer.") 54 } 55 if isInstruction { 56 if _, err := fmt.Fprintf(w, "[%s = %d]\n", key, int(value)); err != nil { 57 return err 58 } 59 } else { 60 if _, err := fmt.Fprintf(w, "%s = %d\n", key, int(value)); err != nil { 61 return err 62 } 63 } 64 case string: 65 if strings.Contains(value, "\n") { 66 panic(key + " contained a newline.") 67 } 68 if isInstruction { 69 if _, err := fmt.Fprintf(w, "[%s = %s]\n", key, value); err != nil { 70 return err 71 } 72 } else { 73 if _, err := fmt.Fprintf(w, "%s = %s\n", key, value); err != nil { 74 return err 75 } 76 } 77 case map[string]interface{}: 78 for _, k := range sortedKeys(value) { 79 if err := printAttribute(w, key+"."+k, value[k], isInstruction); err != nil { 80 return err 81 } 82 } 83 default: 84 panic(fmt.Sprintf("Unknown type for %q: %T", key, valueI)) 85 } 86 return nil 87} 88 89func printComment(w io.Writer, in string) error { 90 const width = 80 - 2 91 lines := strings.Split(in, "\n") 92 for _, line := range lines { 93 for { 94 if len(line) <= width { 95 if _, err := fmt.Fprintf(w, "# %s\n", line); err != nil { 96 return err 97 } 98 break 99 } 100 101 // Find the last space we can break at. 102 n := strings.LastIndexByte(line[:width+1], ' ') 103 if n < 0 { 104 // The next word is too long. Wrap as soon as that word ends. 105 n = strings.IndexByte(line[width+1:], ' ') 106 if n < 0 { 107 // This was the last word. 108 if _, err := fmt.Fprintf(w, "# %s\n", line); err != nil { 109 return nil 110 } 111 break 112 } 113 n += width + 1 114 } 115 if _, err := fmt.Fprintf(w, "# %s\n", line[:n]); err != nil { 116 return err 117 } 118 line = line[n+1:] // Ignore the space. 119 } 120 } 121 return nil 122} 123 124func convertWycheproof(f io.Writer, jsonPath string) error { 125 jsonData, err := ioutil.ReadFile(jsonPath) 126 if err != nil { 127 return err 128 } 129 130 var w wycheproofTest 131 if err := json.Unmarshal(jsonData, &w); err != nil { 132 return err 133 } 134 135 if _, err := fmt.Fprintf(f, `# Imported from Wycheproof's %s. 136# This file is generated by convert_wycheproof.go. Do not edit by hand. 137# 138# Algorithm: %s 139# Generator version: %s 140 141`, jsonPath, w.Algorithm, w.GeneratorVersion); err != nil { 142 return err 143 } 144 145 for _, group := range w.TestGroups { 146 for _, k := range sortedKeys(group) { 147 // Wycheproof files always include both keyPem and 148 // keyDer. Skip keyPem as they contain newlines. We 149 // process keyDer more easily. 150 if k == "type" || k == "tests" || k == "keyPem" { 151 continue 152 } 153 if err := printAttribute(f, k, group[k], true); err != nil { 154 return err 155 } 156 } 157 fmt.Fprintf(f, "\n") 158 tests := group["tests"].([]interface{}) 159 for _, testI := range tests { 160 test := testI.(map[string]interface{}) 161 if _, err := fmt.Fprintf(f, "# tcId = %d\n", int(test["tcId"].(float64))); err != nil { 162 return err 163 } 164 if comment, ok := test["comment"]; ok && len(comment.(string)) != 0 { 165 if err := printComment(f, comment.(string)); err != nil { 166 return err 167 } 168 } 169 for _, k := range sortedKeys(test) { 170 if k == "comment" || k == "flags" || k == "tcId" { 171 continue 172 } 173 if err := printAttribute(f, k, test[k], false); err != nil { 174 return err 175 } 176 } 177 if flags, ok := test["flags"]; ok { 178 for _, flag := range flags.([]interface{}) { 179 if note, ok := w.Notes[flag.(string)]; ok { 180 if err := printComment(f, note); err != nil { 181 return err 182 } 183 } 184 } 185 } 186 if _, err := fmt.Fprintf(f, "\n"); err != nil { 187 return err 188 } 189 } 190 } 191 return nil 192} 193 194var defaultInputs = []string{ 195 "aes_cbc_pkcs5_test.json", 196 "aes_cmac_test.json", 197 "aes_gcm_siv_test.json", 198 "aes_gcm_test.json", 199 "chacha20_poly1305_test.json", 200 "dsa_test.json", 201 "ecdh_secp224r1_test.json", 202 "ecdh_secp256r1_test.json", 203 "ecdh_secp384r1_test.json", 204 "ecdh_secp521r1_test.json", 205 "ecdsa_secp224r1_sha224_test.json", 206 "ecdsa_secp224r1_sha256_test.json", 207 "ecdsa_secp224r1_sha512_test.json", 208 "ecdsa_secp256r1_sha256_test.json", 209 "ecdsa_secp256r1_sha512_test.json", 210 "ecdsa_secp384r1_sha384_test.json", 211 "ecdsa_secp384r1_sha512_test.json", 212 "ecdsa_secp521r1_sha512_test.json", 213 "eddsa_test.json", 214 "kw_test.json", 215 "rsa_pss_2048_sha1_mgf1_20_test.json", 216 "rsa_pss_2048_sha256_mgf1_0_test.json", 217 "rsa_pss_2048_sha256_mgf1_32_test.json", 218 "rsa_pss_3072_sha256_mgf1_32_test.json", 219 "rsa_pss_4096_sha256_mgf1_32_test.json", 220 "rsa_pss_4096_sha512_mgf1_32_test.json", 221 "rsa_pss_misc_test.json", 222 "rsa_signature_test.json", 223 "x25519_test.json", 224} 225 226func main() { 227 switch len(os.Args) { 228 case 1: 229 for _, jsonPath := range defaultInputs { 230 if !strings.HasSuffix(jsonPath, ".json") { 231 panic(jsonPath) 232 } 233 234 txtPath := jsonPath[:len(jsonPath)-len(".json")] + ".txt" 235 out, err := os.Create(txtPath) 236 if err != nil { 237 fmt.Fprintf(os.Stderr, "Error opening output %s: %s\n", txtPath, err) 238 os.Exit(1) 239 } 240 defer out.Close() 241 242 if err := convertWycheproof(out, jsonPath); err != nil { 243 fmt.Fprintf(os.Stderr, "Error converting %s: %s\n", jsonPath, err) 244 os.Exit(1) 245 } 246 } 247 248 case 2: 249 if err := convertWycheproof(os.Stdout, os.Args[1]); err != nil { 250 fmt.Fprintf(os.Stderr, "Error converting %s: %s\n", os.Args[1], err) 251 os.Exit(1) 252 } 253 254 default: 255 fmt.Fprintf(os.Stderr, "Usage: %s [input JSON]\n", os.Args[0]) 256 os.Exit(1) 257 } 258} 259