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 converts Wycheproof test vectors into a format more easily 16// consumed by BoringSSL. 17package main 18 19import ( 20 "encoding/json" 21 "fmt" 22 "io" 23 "os" 24 "sort" 25 "strings" 26) 27 28type wycheproofTest struct { 29 Algorithm string `json:"algorithm"` 30 GeneratorVersion string `json:"generatorVersion"` 31 NumberOfTests int `json:"numberOfTests"` 32 Notes map[string]string `json:"notes"` 33 Header []string `json:"header"` 34 // encoding/json does not support collecting unused keys, so we leave 35 // everything past this point as generic. 36 TestGroups []map[string]any `json:"testGroups"` 37} 38 39func sortedKeys(m map[string]any) []string { 40 keys := make([]string, 0, len(m)) 41 for k, _ := range m { 42 keys = append(keys, k) 43 } 44 sort.Strings(keys) 45 return keys 46} 47 48func printAttribute(w io.Writer, key string, valueAny any, isInstruction bool) error { 49 switch value := valueAny.(type) { 50 case float64: 51 if float64(int(value)) != value { 52 panic(key + "was not an integer.") 53 } 54 if isInstruction { 55 if _, err := fmt.Fprintf(w, "[%s = %d]\n", key, int(value)); err != nil { 56 return err 57 } 58 } else { 59 if _, err := fmt.Fprintf(w, "%s = %d\n", key, int(value)); err != nil { 60 return err 61 } 62 } 63 case string: 64 if strings.Contains(value, "\n") { 65 panic(key + " contained a newline.") 66 } 67 if isInstruction { 68 if _, err := fmt.Fprintf(w, "[%s = %s]\n", key, value); err != nil { 69 return err 70 } 71 } else { 72 if _, err := fmt.Fprintf(w, "%s = %s\n", key, value); err != nil { 73 return err 74 } 75 } 76 case map[string]any: 77 for _, k := range sortedKeys(value) { 78 if err := printAttribute(w, key+"."+k, value[k], isInstruction); err != nil { 79 return err 80 } 81 } 82 default: 83 panic(fmt.Sprintf("Unknown type for %q: %T", key, valueAny)) 84 } 85 return nil 86} 87 88func printComment(w io.Writer, in string) error { 89 const width = 80 - 2 90 lines := strings.Split(in, "\n") 91 for _, line := range lines { 92 for { 93 if len(line) <= width { 94 if _, err := fmt.Fprintf(w, "# %s\n", line); err != nil { 95 return err 96 } 97 break 98 } 99 100 // Find the last space we can break at. 101 n := strings.LastIndexByte(line[:width+1], ' ') 102 if n < 0 { 103 // The next word is too long. Wrap as soon as that word ends. 104 n = strings.IndexByte(line[width+1:], ' ') 105 if n < 0 { 106 // This was the last word. 107 if _, err := fmt.Fprintf(w, "# %s\n", line); err != nil { 108 return nil 109 } 110 break 111 } 112 n += width + 1 113 } 114 if _, err := fmt.Fprintf(w, "# %s\n", line[:n]); err != nil { 115 return err 116 } 117 line = line[n+1:] // Ignore the space. 118 } 119 } 120 return nil 121} 122 123func convertWycheproof(f io.Writer, jsonPath string) error { 124 jsonData, err := os.ReadFile(jsonPath) 125 if err != nil { 126 return err 127 } 128 129 var w wycheproofTest 130 if err := json.Unmarshal(jsonData, &w); err != nil { 131 return err 132 } 133 134 if _, err := fmt.Fprintf(f, `# Imported from Wycheproof's %s. 135# This file is generated by convert_wycheproof.go. Do not edit by hand. 136# 137# Algorithm: %s 138# Generator version: %s 139 140`, jsonPath, w.Algorithm, w.GeneratorVersion); err != nil { 141 return err 142 } 143 144 for _, group := range w.TestGroups { 145 for _, k := range sortedKeys(group) { 146 // Wycheproof files include keys in multiple formats. Skip PEM and 147 // JWK formats. We process DER more easily. PEM has newlines and 148 // JWK is a JSON object. 149 if k == "type" || k == "tests" || strings.HasSuffix(k, "Pem") || strings.HasSuffix(k, "Jwk") || k == "jwk" { 150 continue 151 } 152 if err := printAttribute(f, k, group[k], true); err != nil { 153 return err 154 } 155 } 156 fmt.Fprintf(f, "\n") 157 tests := group["tests"].([]any) 158 for _, testAny := range tests { 159 test := testAny.(map[string]any) 160 if _, err := fmt.Fprintf(f, "# tcId = %d\n", int(test["tcId"].(float64))); err != nil { 161 return err 162 } 163 if comment, ok := test["comment"]; ok && len(comment.(string)) != 0 { 164 if err := printComment(f, comment.(string)); err != nil { 165 return err 166 } 167 } 168 for _, k := range sortedKeys(test) { 169 if k == "comment" || k == "flags" || k == "tcId" { 170 continue 171 } 172 if err := printAttribute(f, k, test[k], false); err != nil { 173 return err 174 } 175 } 176 if flagsAny, ok := test["flags"]; ok { 177 var flags []string 178 for _, flagAny := range flagsAny.([]any) { 179 flag := flagAny.(string) 180 flags = append(flags, flag) 181 } 182 if len(flags) != 0 { 183 if err := printAttribute(f, "flags", strings.Join(flags, ","), false); err != nil { 184 return err 185 } 186 } 187 } 188 if _, err := fmt.Fprintf(f, "\n"); err != nil { 189 return err 190 } 191 } 192 } 193 return nil 194} 195 196var defaultInputs = []string{ 197 "aes_cbc_pkcs5_test.json", 198 "aes_cmac_test.json", 199 "aes_gcm_siv_test.json", 200 "aes_gcm_test.json", 201 "chacha20_poly1305_test.json", 202 "dsa_test.json", 203 "ecdh_secp224r1_test.json", 204 "ecdh_secp256r1_test.json", 205 "ecdh_secp384r1_test.json", 206 "ecdh_secp521r1_test.json", 207 "ecdsa_secp224r1_sha224_test.json", 208 "ecdsa_secp224r1_sha256_test.json", 209 "ecdsa_secp224r1_sha512_test.json", 210 "ecdsa_secp256r1_sha256_test.json", 211 "ecdsa_secp256r1_sha512_test.json", 212 "ecdsa_secp384r1_sha384_test.json", 213 "ecdsa_secp384r1_sha512_test.json", 214 "ecdsa_secp521r1_sha512_test.json", 215 "eddsa_test.json", 216 "hkdf_sha1_test.json", 217 "hkdf_sha256_test.json", 218 "hkdf_sha384_test.json", 219 "hkdf_sha512_test.json", 220 "hmac_sha1_test.json", 221 "hmac_sha224_test.json", 222 "hmac_sha256_test.json", 223 "hmac_sha384_test.json", 224 "hmac_sha512_test.json", 225 "kw_test.json", 226 "kwp_test.json", 227 "primality_test.json", 228 "rsa_oaep_2048_sha1_mgf1sha1_test.json", 229 "rsa_oaep_2048_sha224_mgf1sha1_test.json", 230 "rsa_oaep_2048_sha224_mgf1sha224_test.json", 231 "rsa_oaep_2048_sha256_mgf1sha1_test.json", 232 "rsa_oaep_2048_sha256_mgf1sha256_test.json", 233 "rsa_oaep_2048_sha384_mgf1sha1_test.json", 234 "rsa_oaep_2048_sha384_mgf1sha384_test.json", 235 "rsa_oaep_2048_sha512_mgf1sha1_test.json", 236 "rsa_oaep_2048_sha512_mgf1sha512_test.json", 237 "rsa_oaep_3072_sha256_mgf1sha1_test.json", 238 "rsa_oaep_3072_sha256_mgf1sha256_test.json", 239 "rsa_oaep_3072_sha512_mgf1sha1_test.json", 240 "rsa_oaep_3072_sha512_mgf1sha512_test.json", 241 "rsa_oaep_4096_sha256_mgf1sha1_test.json", 242 "rsa_oaep_4096_sha256_mgf1sha256_test.json", 243 "rsa_oaep_4096_sha512_mgf1sha1_test.json", 244 "rsa_oaep_4096_sha512_mgf1sha512_test.json", 245 "rsa_oaep_misc_test.json", 246 "rsa_pkcs1_2048_test.json", 247 "rsa_pkcs1_3072_test.json", 248 "rsa_pkcs1_4096_test.json", 249 "rsa_pss_2048_sha1_mgf1_20_test.json", 250 "rsa_pss_2048_sha256_mgf1_0_test.json", 251 "rsa_pss_2048_sha256_mgf1_32_test.json", 252 "rsa_pss_3072_sha256_mgf1_32_test.json", 253 "rsa_pss_4096_sha256_mgf1_32_test.json", 254 "rsa_pss_4096_sha512_mgf1_32_test.json", 255 "rsa_pss_misc_test.json", 256 "rsa_sig_gen_misc_test.json", 257 "rsa_signature_2048_sha224_test.json", 258 "rsa_signature_2048_sha256_test.json", 259 "rsa_signature_2048_sha384_test.json", 260 "rsa_signature_2048_sha512_test.json", 261 "rsa_signature_3072_sha256_test.json", 262 "rsa_signature_3072_sha384_test.json", 263 "rsa_signature_3072_sha512_test.json", 264 "rsa_signature_4096_sha384_test.json", 265 "rsa_signature_4096_sha512_test.json", 266 "rsa_signature_test.json", 267 "x25519_test.json", 268 "xchacha20_poly1305_test.json", 269} 270 271func main() { 272 switch len(os.Args) { 273 case 1: 274 for _, jsonPath := range defaultInputs { 275 if !strings.HasSuffix(jsonPath, ".json") { 276 panic(jsonPath) 277 } 278 279 txtPath := jsonPath[:len(jsonPath)-len(".json")] + ".txt" 280 out, err := os.Create(txtPath) 281 if err != nil { 282 fmt.Fprintf(os.Stderr, "Error opening output %s: %s\n", txtPath, err) 283 os.Exit(1) 284 } 285 defer out.Close() 286 287 if err := convertWycheproof(out, jsonPath); err != nil { 288 fmt.Fprintf(os.Stderr, "Error converting %s: %s\n", jsonPath, err) 289 os.Exit(1) 290 } 291 } 292 293 case 2: 294 if err := convertWycheproof(os.Stdout, os.Args[1]); err != nil { 295 fmt.Fprintf(os.Stderr, "Error converting %s: %s\n", os.Args[1], err) 296 os.Exit(1) 297 } 298 299 default: 300 fmt.Fprintf(os.Stderr, "Usage: %s [input JSON]\n", os.Args[0]) 301 os.Exit(1) 302 } 303} 304