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 include keys in multiple formats. Skip PEM and 148 // JWK formats. We process DER more easily. PEM has newlines and 149 // JWK is a JSON object. 150 if k == "type" || k == "tests" || strings.HasSuffix(k, "Pem") || strings.HasSuffix(k, "Jwk") || k == "jwk" { 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 flagsI, ok := test["flags"]; ok { 178 var flags []string 179 for _, flagI := range flagsI.([]interface{}) { 180 flag := flagI.(string) 181 flags = append(flags, flag) 182 } 183 if len(flags) != 0 { 184 if err := printAttribute(f, "flags", strings.Join(flags, ","), false); err != nil { 185 return err 186 } 187 } 188 } 189 if _, err := fmt.Fprintf(f, "\n"); err != nil { 190 return err 191 } 192 } 193 } 194 return nil 195} 196 197var defaultInputs = []string{ 198 "aes_cbc_pkcs5_test.json", 199 "aes_cmac_test.json", 200 "aes_gcm_siv_test.json", 201 "aes_gcm_test.json", 202 "chacha20_poly1305_test.json", 203 "dsa_test.json", 204 "ecdh_secp224r1_test.json", 205 "ecdh_secp256r1_test.json", 206 "ecdh_secp384r1_test.json", 207 "ecdh_secp521r1_test.json", 208 "ecdsa_secp224r1_sha224_test.json", 209 "ecdsa_secp224r1_sha256_test.json", 210 "ecdsa_secp224r1_sha512_test.json", 211 "ecdsa_secp256r1_sha256_test.json", 212 "ecdsa_secp256r1_sha512_test.json", 213 "ecdsa_secp384r1_sha384_test.json", 214 "ecdsa_secp384r1_sha512_test.json", 215 "ecdsa_secp521r1_sha512_test.json", 216 "eddsa_test.json", 217 "hkdf_sha1_test.json", 218 "hkdf_sha256_test.json", 219 "hkdf_sha384_test.json", 220 "hkdf_sha512_test.json", 221 "hmac_sha1_test.json", 222 "hmac_sha224_test.json", 223 "hmac_sha256_test.json", 224 "hmac_sha384_test.json", 225 "hmac_sha512_test.json", 226 "kw_test.json", 227 "kwp_test.json", 228 "primality_test.json", 229 "rsa_oaep_2048_sha1_mgf1sha1_test.json", 230 "rsa_oaep_2048_sha224_mgf1sha1_test.json", 231 "rsa_oaep_2048_sha224_mgf1sha224_test.json", 232 "rsa_oaep_2048_sha256_mgf1sha1_test.json", 233 "rsa_oaep_2048_sha256_mgf1sha256_test.json", 234 "rsa_oaep_2048_sha384_mgf1sha1_test.json", 235 "rsa_oaep_2048_sha384_mgf1sha384_test.json", 236 "rsa_oaep_2048_sha512_mgf1sha1_test.json", 237 "rsa_oaep_2048_sha512_mgf1sha512_test.json", 238 "rsa_oaep_3072_sha256_mgf1sha1_test.json", 239 "rsa_oaep_3072_sha256_mgf1sha256_test.json", 240 "rsa_oaep_3072_sha512_mgf1sha1_test.json", 241 "rsa_oaep_3072_sha512_mgf1sha512_test.json", 242 "rsa_oaep_4096_sha256_mgf1sha1_test.json", 243 "rsa_oaep_4096_sha256_mgf1sha256_test.json", 244 "rsa_oaep_4096_sha512_mgf1sha1_test.json", 245 "rsa_oaep_4096_sha512_mgf1sha512_test.json", 246 "rsa_oaep_misc_test.json", 247 "rsa_pkcs1_2048_test.json", 248 "rsa_pkcs1_3072_test.json", 249 "rsa_pkcs1_4096_test.json", 250 "rsa_pss_2048_sha1_mgf1_20_test.json", 251 "rsa_pss_2048_sha256_mgf1_0_test.json", 252 "rsa_pss_2048_sha256_mgf1_32_test.json", 253 "rsa_pss_3072_sha256_mgf1_32_test.json", 254 "rsa_pss_4096_sha256_mgf1_32_test.json", 255 "rsa_pss_4096_sha512_mgf1_32_test.json", 256 "rsa_pss_misc_test.json", 257 "rsa_sig_gen_misc_test.json", 258 "rsa_signature_2048_sha224_test.json", 259 "rsa_signature_2048_sha256_test.json", 260 "rsa_signature_2048_sha384_test.json", 261 "rsa_signature_2048_sha512_test.json", 262 "rsa_signature_3072_sha256_test.json", 263 "rsa_signature_3072_sha384_test.json", 264 "rsa_signature_3072_sha512_test.json", 265 "rsa_signature_4096_sha384_test.json", 266 "rsa_signature_4096_sha512_test.json", 267 "rsa_signature_test.json", 268 "x25519_test.json", 269 "xchacha20_poly1305_test.json", 270} 271 272func main() { 273 switch len(os.Args) { 274 case 1: 275 for _, jsonPath := range defaultInputs { 276 if !strings.HasSuffix(jsonPath, ".json") { 277 panic(jsonPath) 278 } 279 280 txtPath := jsonPath[:len(jsonPath)-len(".json")] + ".txt" 281 out, err := os.Create(txtPath) 282 if err != nil { 283 fmt.Fprintf(os.Stderr, "Error opening output %s: %s\n", txtPath, err) 284 os.Exit(1) 285 } 286 defer out.Close() 287 288 if err := convertWycheproof(out, jsonPath); err != nil { 289 fmt.Fprintf(os.Stderr, "Error converting %s: %s\n", jsonPath, err) 290 os.Exit(1) 291 } 292 } 293 294 case 2: 295 if err := convertWycheproof(os.Stdout, os.Args[1]); err != nil { 296 fmt.Fprintf(os.Stderr, "Error converting %s: %s\n", os.Args[1], err) 297 os.Exit(1) 298 } 299 300 default: 301 fmt.Fprintf(os.Stderr, "Usage: %s [input JSON]\n", os.Args[0]) 302 os.Exit(1) 303 } 304} 305