Robert Sloan | c6ebb28 | 2018-04-30 10:10:26 -0700 | [diff] [blame] | 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. |
| 17 | package main |
| 18 | |
| 19 | import ( |
| 20 | "encoding/json" |
| 21 | "fmt" |
| 22 | "io" |
| 23 | "io/ioutil" |
| 24 | "os" |
| 25 | "sort" |
| 26 | "strings" |
| 27 | ) |
| 28 | |
| 29 | type 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 | |
| 40 | func 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 | |
| 49 | func 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 | |
| 89 | func 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 | |
Adam Vartanian | bfcf3a7 | 2018-08-10 14:55:24 +0100 | [diff] [blame] | 124 | func convertWycheproof(f io.Writer, jsonPath string) error { |
Robert Sloan | c6ebb28 | 2018-04-30 10:10:26 -0700 | [diff] [blame] | 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 | |
Robert Sloan | c6ebb28 | 2018-04-30 10:10:26 -0700 | [diff] [blame] | 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 { |
Robert Sloan | c6ebb28 | 2018-04-30 10:10:26 -0700 | [diff] [blame] | 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{}) |
Adam Vartanian | bfcf3a7 | 2018-08-10 14:55:24 +0100 | [diff] [blame] | 161 | if _, err := fmt.Fprintf(f, "# tcId = %d\n", int(test["tcId"].(float64))); err != nil { |
| 162 | return err |
| 163 | } |
Robert Sloan | c6ebb28 | 2018-04-30 10:10:26 -0700 | [diff] [blame] | 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 | |
Adam Vartanian | bfcf3a7 | 2018-08-10 14:55:24 +0100 | [diff] [blame] | 194 | var 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", |
Robert Sloan | d9e572d | 2018-08-27 12:27:00 -0700 | [diff] [blame] | 201 | "ecdh_secp224r1_test.json", |
| 202 | "ecdh_secp256r1_test.json", |
| 203 | "ecdh_secp384r1_test.json", |
| 204 | "ecdh_secp521r1_test.json", |
Adam Vartanian | bfcf3a7 | 2018-08-10 14:55:24 +0100 | [diff] [blame] | 205 | "ecdsa_secp224r1_sha224_test.json", |
| 206 | "ecdsa_secp224r1_sha256_test.json", |
Robert Sloan | d9e572d | 2018-08-27 12:27:00 -0700 | [diff] [blame] | 207 | "ecdsa_secp224r1_sha512_test.json", |
Adam Vartanian | bfcf3a7 | 2018-08-10 14:55:24 +0100 | [diff] [blame] | 208 | "ecdsa_secp256r1_sha256_test.json", |
Robert Sloan | d9e572d | 2018-08-27 12:27:00 -0700 | [diff] [blame] | 209 | "ecdsa_secp256r1_sha512_test.json", |
Adam Vartanian | bfcf3a7 | 2018-08-10 14:55:24 +0100 | [diff] [blame] | 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", |
Robert Sloan | d9e572d | 2018-08-27 12:27:00 -0700 | [diff] [blame] | 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", |
Adam Vartanian | bfcf3a7 | 2018-08-10 14:55:24 +0100 | [diff] [blame] | 222 | "rsa_signature_test.json", |
| 223 | "x25519_test.json", |
| 224 | } |
Robert Sloan | c6ebb28 | 2018-04-30 10:10:26 -0700 | [diff] [blame] | 225 | |
Adam Vartanian | bfcf3a7 | 2018-08-10 14:55:24 +0100 | [diff] [blame] | 226 | func 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 | } |
Robert Sloan | c6ebb28 | 2018-04-30 10:10:26 -0700 | [diff] [blame] | 246 | } |
Adam Vartanian | bfcf3a7 | 2018-08-10 14:55:24 +0100 | [diff] [blame] | 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) |
Robert Sloan | c6ebb28 | 2018-04-30 10:10:26 -0700 | [diff] [blame] | 251 | os.Exit(1) |
| 252 | } |
Adam Vartanian | bfcf3a7 | 2018-08-10 14:55:24 +0100 | [diff] [blame] | 253 | |
| 254 | default: |
| 255 | fmt.Fprintf(os.Stderr, "Usage: %s [input JSON]\n", os.Args[0]) |
| 256 | os.Exit(1) |
Robert Sloan | c6ebb28 | 2018-04-30 10:10:26 -0700 | [diff] [blame] | 257 | } |
| 258 | } |