• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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