convert_wycheproof.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  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. // convert_wycheproof.go converts Wycheproof test vectors into a format more
  15. // easily consumed by BoringSSL.
  16. package main
  17. import (
  18. "encoding/json"
  19. "fmt"
  20. "io"
  21. "io/ioutil"
  22. "os"
  23. "sort"
  24. "strings"
  25. )
  26. type wycheproofTest struct {
  27. Algorithm string `json:"algorithm"`
  28. GeneratorVersion string `json:"generatorVersion"`
  29. NumberOfTests int `json:"numberOfTests"`
  30. Notes map[string]string `json:"notes"`
  31. Header []string `json:"header"`
  32. // encoding/json does not support collecting unused keys, so we leave
  33. // everything past this point as generic.
  34. TestGroups []map[string]interface{} `json:"testGroups"`
  35. }
  36. func sortedKeys(m map[string]interface{}) []string {
  37. keys := make([]string, 0, len(m))
  38. for k, _ := range m {
  39. keys = append(keys, k)
  40. }
  41. sort.Strings(keys)
  42. return keys
  43. }
  44. func printAttribute(w io.Writer, key string, valueI interface{}, isInstruction bool) error {
  45. switch value := valueI.(type) {
  46. case float64:
  47. if float64(int(value)) != value {
  48. panic(key + "was not an integer.")
  49. }
  50. if isInstruction {
  51. if _, err := fmt.Fprintf(w, "[%s = %d]\n", key, int(value)); err != nil {
  52. return err
  53. }
  54. } else {
  55. if _, err := fmt.Fprintf(w, "%s = %d\n", key, int(value)); err != nil {
  56. return err
  57. }
  58. }
  59. case string:
  60. if strings.Contains(value, "\n") {
  61. panic(key + " contained a newline.")
  62. }
  63. if isInstruction {
  64. if _, err := fmt.Fprintf(w, "[%s = %s]\n", key, value); err != nil {
  65. return err
  66. }
  67. } else {
  68. if _, err := fmt.Fprintf(w, "%s = %s\n", key, value); err != nil {
  69. return err
  70. }
  71. }
  72. case map[string]interface{}:
  73. for _, k := range sortedKeys(value) {
  74. if err := printAttribute(w, key+"."+k, value[k], isInstruction); err != nil {
  75. return err
  76. }
  77. }
  78. default:
  79. panic(fmt.Sprintf("Unknown type for %q: %T", key, valueI))
  80. }
  81. return nil
  82. }
  83. func printComment(w io.Writer, in string) error {
  84. const width = 80 - 2
  85. lines := strings.Split(in, "\n")
  86. for _, line := range lines {
  87. for {
  88. if len(line) <= width {
  89. if _, err := fmt.Fprintf(w, "# %s\n", line); err != nil {
  90. return err
  91. }
  92. break
  93. }
  94. // Find the last space we can break at.
  95. n := strings.LastIndexByte(line[:width+1], ' ')
  96. if n < 0 {
  97. // The next word is too long. Wrap as soon as that word ends.
  98. n = strings.IndexByte(line[width+1:], ' ')
  99. if n < 0 {
  100. // This was the last word.
  101. if _, err := fmt.Fprintf(w, "# %s\n", line); err != nil {
  102. return nil
  103. }
  104. break
  105. }
  106. n += width + 1
  107. }
  108. if _, err := fmt.Fprintf(w, "# %s\n", line[:n]); err != nil {
  109. return err
  110. }
  111. line = line[n+1:] // Ignore the space.
  112. }
  113. }
  114. return nil
  115. }
  116. func convertWycheproof(f io.Writer, jsonPath string) error {
  117. jsonData, err := ioutil.ReadFile(jsonPath)
  118. if err != nil {
  119. return err
  120. }
  121. var w wycheproofTest
  122. if err := json.Unmarshal(jsonData, &w); err != nil {
  123. return err
  124. }
  125. if _, err := fmt.Fprintf(f, `# Imported from Wycheproof's %s.
  126. # This file is generated by convert_wycheproof.go. Do not edit by hand.
  127. #
  128. # Algorithm: %s
  129. # Generator version: %s
  130. `, jsonPath, w.Algorithm, w.GeneratorVersion); err != nil {
  131. return err
  132. }
  133. for _, group := range w.TestGroups {
  134. for _, k := range sortedKeys(group) {
  135. // Wycheproof files always include both keyPem and
  136. // keyDer. Skip keyPem as they contain newlines. We
  137. // process keyDer more easily.
  138. if k == "type" || k == "tests" || k == "keyPem" {
  139. continue
  140. }
  141. if err := printAttribute(f, k, group[k], true); err != nil {
  142. return err
  143. }
  144. }
  145. fmt.Fprintf(f, "\n")
  146. tests := group["tests"].([]interface{})
  147. for _, testI := range tests {
  148. test := testI.(map[string]interface{})
  149. if _, err := fmt.Fprintf(f, "# tcId = %d\n", int(test["tcId"].(float64))); err != nil {
  150. return err
  151. }
  152. if comment, ok := test["comment"]; ok && len(comment.(string)) != 0 {
  153. if err := printComment(f, comment.(string)); err != nil {
  154. return err
  155. }
  156. }
  157. for _, k := range sortedKeys(test) {
  158. if k == "comment" || k == "flags" || k == "tcId" {
  159. continue
  160. }
  161. if err := printAttribute(f, k, test[k], false); err != nil {
  162. return err
  163. }
  164. }
  165. if flags, ok := test["flags"]; ok {
  166. for _, flag := range flags.([]interface{}) {
  167. if note, ok := w.Notes[flag.(string)]; ok {
  168. if err := printComment(f, note); err != nil {
  169. return err
  170. }
  171. }
  172. }
  173. }
  174. if _, err := fmt.Fprintf(f, "\n"); err != nil {
  175. return err
  176. }
  177. }
  178. }
  179. return nil
  180. }
  181. var defaultInputs = []string{
  182. "aes_cbc_pkcs5_test.json",
  183. "aes_cmac_test.json",
  184. "aes_gcm_siv_test.json",
  185. "aes_gcm_test.json",
  186. "chacha20_poly1305_test.json",
  187. "dsa_test.json",
  188. "ecdh_secp224r1_test.json",
  189. "ecdh_secp256r1_test.json",
  190. "ecdh_secp384r1_test.json",
  191. "ecdh_secp521r1_test.json",
  192. "ecdsa_secp224r1_sha224_test.json",
  193. "ecdsa_secp224r1_sha256_test.json",
  194. "ecdsa_secp224r1_sha512_test.json",
  195. "ecdsa_secp256r1_sha256_test.json",
  196. "ecdsa_secp256r1_sha512_test.json",
  197. "ecdsa_secp384r1_sha384_test.json",
  198. "ecdsa_secp384r1_sha512_test.json",
  199. "ecdsa_secp521r1_sha512_test.json",
  200. "eddsa_test.json",
  201. "kw_test.json",
  202. "kwp_test.json",
  203. "rsa_pss_2048_sha1_mgf1_20_test.json",
  204. "rsa_pss_2048_sha256_mgf1_0_test.json",
  205. "rsa_pss_2048_sha256_mgf1_32_test.json",
  206. "rsa_pss_3072_sha256_mgf1_32_test.json",
  207. "rsa_pss_4096_sha256_mgf1_32_test.json",
  208. "rsa_pss_4096_sha512_mgf1_32_test.json",
  209. "rsa_pss_misc_test.json",
  210. "rsa_signature_test.json",
  211. "x25519_test.json",
  212. }
  213. func main() {
  214. switch len(os.Args) {
  215. case 1:
  216. for _, jsonPath := range defaultInputs {
  217. if !strings.HasSuffix(jsonPath, ".json") {
  218. panic(jsonPath)
  219. }
  220. txtPath := jsonPath[:len(jsonPath)-len(".json")] + ".txt"
  221. out, err := os.Create(txtPath)
  222. if err != nil {
  223. fmt.Fprintf(os.Stderr, "Error opening output %s: %s\n", txtPath, err)
  224. os.Exit(1)
  225. }
  226. defer out.Close()
  227. if err := convertWycheproof(out, jsonPath); err != nil {
  228. fmt.Fprintf(os.Stderr, "Error converting %s: %s\n", jsonPath, err)
  229. os.Exit(1)
  230. }
  231. }
  232. case 2:
  233. if err := convertWycheproof(os.Stdout, os.Args[1]); err != nil {
  234. fmt.Fprintf(os.Stderr, "Error converting %s: %s\n", os.Args[1], err)
  235. os.Exit(1)
  236. }
  237. default:
  238. fmt.Fprintf(os.Stderr, "Usage: %s [input JSON]\n", os.Args[0])
  239. os.Exit(1)
  240. }
  241. }