blob: e6eab95c259531d37926bdc8ac65fc82f73c0399 [file] [log] [blame]
Robert Sloanc6ebb282018-04-30 10:10:26 -07001/* 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
Adam Vartanianbfcf3a72018-08-10 14:55:24 +0100124func convertWycheproof(f io.Writer, jsonPath string) error {
Robert Sloanc6ebb282018-04-30 10:10:26 -0700125 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 Sloanc6ebb282018-04-30 10:10:26 -0700135 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 Sloanc6ebb282018-04-30 10:10:26 -0700146 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 Vartanianbfcf3a72018-08-10 14:55:24 +0100161 if _, err := fmt.Fprintf(f, "# tcId = %d\n", int(test["tcId"].(float64))); err != nil {
162 return err
163 }
Robert Sloanc6ebb282018-04-30 10:10:26 -0700164 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 Vartanianbfcf3a72018-08-10 14:55:24 +0100194var 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 Sloand9e572d2018-08-27 12:27:00 -0700201 "ecdh_secp224r1_test.json",
202 "ecdh_secp256r1_test.json",
203 "ecdh_secp384r1_test.json",
204 "ecdh_secp521r1_test.json",
Adam Vartanianbfcf3a72018-08-10 14:55:24 +0100205 "ecdsa_secp224r1_sha224_test.json",
206 "ecdsa_secp224r1_sha256_test.json",
Robert Sloand9e572d2018-08-27 12:27:00 -0700207 "ecdsa_secp224r1_sha512_test.json",
Adam Vartanianbfcf3a72018-08-10 14:55:24 +0100208 "ecdsa_secp256r1_sha256_test.json",
Robert Sloand9e572d2018-08-27 12:27:00 -0700209 "ecdsa_secp256r1_sha512_test.json",
Adam Vartanianbfcf3a72018-08-10 14:55:24 +0100210 "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",
Pete Bentley0c61efe2019-08-13 09:32:23 +0100215 "kwp_test.json",
Robert Sloand9e572d2018-08-27 12:27:00 -0700216 "rsa_pss_2048_sha1_mgf1_20_test.json",
217 "rsa_pss_2048_sha256_mgf1_0_test.json",
218 "rsa_pss_2048_sha256_mgf1_32_test.json",
219 "rsa_pss_3072_sha256_mgf1_32_test.json",
220 "rsa_pss_4096_sha256_mgf1_32_test.json",
221 "rsa_pss_4096_sha512_mgf1_32_test.json",
222 "rsa_pss_misc_test.json",
Adam Vartanianbfcf3a72018-08-10 14:55:24 +0100223 "rsa_signature_test.json",
224 "x25519_test.json",
225}
Robert Sloanc6ebb282018-04-30 10:10:26 -0700226
Adam Vartanianbfcf3a72018-08-10 14:55:24 +0100227func main() {
228 switch len(os.Args) {
229 case 1:
230 for _, jsonPath := range defaultInputs {
231 if !strings.HasSuffix(jsonPath, ".json") {
232 panic(jsonPath)
233 }
234
235 txtPath := jsonPath[:len(jsonPath)-len(".json")] + ".txt"
236 out, err := os.Create(txtPath)
237 if err != nil {
238 fmt.Fprintf(os.Stderr, "Error opening output %s: %s\n", txtPath, err)
239 os.Exit(1)
240 }
241 defer out.Close()
242
243 if err := convertWycheproof(out, jsonPath); err != nil {
244 fmt.Fprintf(os.Stderr, "Error converting %s: %s\n", jsonPath, err)
245 os.Exit(1)
246 }
Robert Sloanc6ebb282018-04-30 10:10:26 -0700247 }
Adam Vartanianbfcf3a72018-08-10 14:55:24 +0100248
249 case 2:
250 if err := convertWycheproof(os.Stdout, os.Args[1]); err != nil {
251 fmt.Fprintf(os.Stderr, "Error converting %s: %s\n", os.Args[1], err)
Robert Sloanc6ebb282018-04-30 10:10:26 -0700252 os.Exit(1)
253 }
Adam Vartanianbfcf3a72018-08-10 14:55:24 +0100254
255 default:
256 fmt.Fprintf(os.Stderr, "Usage: %s [input JSON]\n", os.Args[0])
257 os.Exit(1)
Robert Sloanc6ebb282018-04-30 10:10:26 -0700258 }
259}