1// Copyright 2015 The BoringSSL Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package main
16
17import (
18	"bufio"
19	"bytes"
20	"errors"
21	"fmt"
22	"io"
23	"os"
24	"path"
25	"sort"
26	"strconv"
27)
28
29// libraryNames must be kept in sync with the enum in err.h. The generated code
30// will contain static assertions to enforce this.
31var libraryNames = []string{
32	"NONE",
33	"SYS",
34	"BN",
35	"RSA",
36	"DH",
37	"EVP",
38	"BUF",
39	"OBJ",
40	"PEM",
41	"DSA",
42	"X509",
43	"ASN1",
44	"CONF",
45	"CRYPTO",
46	"EC",
47	"SSL",
48	"BIO",
49	"PKCS7",
50	"PKCS8",
51	"X509V3",
52	"RAND",
53	"ENGINE",
54	"OCSP",
55	"UI",
56	"COMP",
57	"ECDSA",
58	"ECDH",
59	"HMAC",
60	"DIGEST",
61	"CIPHER",
62	"HKDF",
63	"TRUST_TOKEN",
64	"CMS",
65	"USER",
66}
67
68// stringList is a map from uint32 -> string which can output data for a sorted
69// list as C literals.
70type stringList struct {
71	// entries is an array of keys and offsets into |stringData|. The
72	// offsets are in the bottom 15 bits of each uint32 and the key is the
73	// top 17 bits.
74	entries []uint32
75	// internedStrings contains the same strings as are in |stringData|,
76	// but allows for easy deduplication. It maps a string to its offset in
77	// |stringData|.
78	internedStrings map[string]uint32
79	stringData      []byte
80}
81
82func newStringList() *stringList {
83	return &stringList{
84		internedStrings: make(map[string]uint32),
85	}
86}
87
88// offsetMask is the bottom 15 bits. It's a mask that selects the offset from a
89// uint32 in entries.
90const offsetMask = 0x7fff
91
92func (st *stringList) Add(key uint32, value string) error {
93	if key&offsetMask != 0 {
94		return errors.New("need bottom 15 bits of the key for the offset")
95	}
96	offset, ok := st.internedStrings[value]
97	if !ok {
98		offset = uint32(len(st.stringData))
99		if offset&offsetMask != offset {
100			return errors.New("stringList overflow")
101		}
102		st.stringData = append(st.stringData, []byte(value)...)
103		st.stringData = append(st.stringData, 0)
104		st.internedStrings[value] = offset
105	}
106
107	for _, existing := range st.entries {
108		if existing>>15 == key>>15 {
109			panic("duplicate entry")
110		}
111	}
112	st.entries = append(st.entries, key|offset)
113	return nil
114}
115
116func (st *stringList) buildList() []uint32 {
117	sort.Slice(st.entries, func(i, j int) bool { return (st.entries[i] >> 15) < (st.entries[j] >> 15) })
118	return st.entries
119}
120
121type stringWriter interface {
122	io.Writer
123	WriteString(string) (int, error)
124}
125
126func (st *stringList) WriteTo(out stringWriter, name string) {
127	list := st.buildList()
128	values := "kOpenSSL" + name + "Values"
129	out.WriteString("extern const uint32_t " + values + "[];\n")
130	out.WriteString("const uint32_t " + values + "[] = {\n")
131	for _, v := range list {
132		fmt.Fprintf(out, "    0x%x,\n", v)
133	}
134	out.WriteString("};\n\n")
135	out.WriteString("extern const size_t " + values + "Len;\n")
136	out.WriteString("const size_t " + values + "Len = sizeof(" + values + ") / sizeof(" + values + "[0]);\n\n")
137
138	stringData := "kOpenSSL" + name + "StringData"
139	out.WriteString("extern const char " + stringData + "[];\n")
140	out.WriteString("const char " + stringData + "[] =\n    \"")
141	for i, c := range st.stringData {
142		if c == 0 {
143			out.WriteString("\\0\"\n    \"")
144			continue
145		}
146		out.Write(st.stringData[i : i+1])
147	}
148	out.WriteString("\";\n\n")
149}
150
151type errorData struct {
152	reasons    *stringList
153	libraryMap map[string]uint32
154}
155
156func (e *errorData) readErrorDataFile(filename string) error {
157	inFile, err := os.Open(filename)
158	if err != nil {
159		return err
160	}
161	defer inFile.Close()
162
163	scanner := bufio.NewScanner(inFile)
164	comma := []byte(",")
165
166	lineNo := 0
167	for scanner.Scan() {
168		lineNo++
169
170		line := scanner.Bytes()
171		if len(line) == 0 {
172			continue
173		}
174		parts := bytes.Split(line, comma)
175		if len(parts) != 3 {
176			return fmt.Errorf("bad line %d in %s: found %d values but want 3", lineNo, filename, len(parts))
177		}
178		libNum, ok := e.libraryMap[string(parts[0])]
179		if !ok {
180			return fmt.Errorf("bad line %d in %s: unknown library", lineNo, filename)
181		}
182		if libNum >= 64 {
183			return fmt.Errorf("bad line %d in %s: library value too large", lineNo, filename)
184		}
185		key, err := strconv.ParseUint(string(parts[1]), 10 /* base */, 32 /* bit size */)
186		if err != nil {
187			return fmt.Errorf("bad line %d in %s: %s", lineNo, filename, err)
188		}
189		if key >= 2048 {
190			return fmt.Errorf("bad line %d in %s: key too large", lineNo, filename)
191		}
192		value := string(parts[2])
193
194		listKey := libNum<<26 | uint32(key)<<15
195
196		err = e.reasons.Add(listKey, value)
197		if err != nil {
198			return err
199		}
200	}
201
202	return scanner.Err()
203}
204
205type ErrDataTask struct {
206	TargetName string
207	Inputs     []string
208}
209
210func (t *ErrDataTask) Destination() string {
211	return path.Join("gen", t.TargetName, "err_data.cc")
212}
213
214func (t *ErrDataTask) Run() ([]byte, error) {
215	e := &errorData{
216		reasons:    newStringList(),
217		libraryMap: make(map[string]uint32),
218	}
219	for i, name := range libraryNames {
220		e.libraryMap[name] = uint32(i) + 1
221	}
222
223	for _, input := range t.Inputs {
224		if err := e.readErrorDataFile(input); err != nil {
225			return nil, err
226		}
227	}
228
229	var out bytes.Buffer
230	out.WriteString(`// Copyright 2015 The BoringSSL Authors
231//
232// Licensed under the Apache License, Version 2.0 (the "License");
233// you may not use this file except in compliance with the License.
234// You may obtain a copy of the License at
235//
236//     https://www.apache.org/licenses/LICENSE-2.0
237//
238// Unless required by applicable law or agreed to in writing, software
239// distributed under the License is distributed on an "AS IS" BASIS,
240// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
241// See the License for the specific language governing permissions and
242// limitations under the License.
243
244// This file was generated by go run ./util/pregenerate.
245
246#include <openssl/base.h>
247#include <openssl/err.h>
248
249#include <assert.h>
250
251`)
252
253	for i, name := range libraryNames {
254		fmt.Fprintf(&out, "static_assert(ERR_LIB_%s == %d, \"library value changed\");\n", name, i+1)
255	}
256	fmt.Fprintf(&out, "static_assert(ERR_NUM_LIBS == %d, \"number of libraries changed\");\n", len(libraryNames)+1)
257	out.WriteString("\n")
258
259	e.reasons.WriteTo(&out, "Reason")
260	return out.Bytes(), nil
261}
262