xref: /aosp_15_r20/external/cronet/third_party/boringssl/src/util/convert_wycheproof/convert_wycheproof.go (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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