1#!/bin/ksh
2
3#
4# Copyright (c) 2020 Joel Sing <jsing@openbsd.org>
5#
6# Permission to use, copy, modify, and distribute this software for any
7# purpose with or without fee is hereby granted, provided that the above
8# copyright notice and this permission notice appear in all copies.
9#
10# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17#
18
19set -e
20set -u
21set -x
22
23readonly SUBJECT="/CN=LibreSSL Test"
24
25readonly TMPDIR=$(mktemp -d)
26
27cleanup() {
28        rm -rf "${TMPDIR}"
29}
30
31trap cleanup EXIT INT
32
33reset() {
34	echo '100001' > ${TMPDIR}/certserial
35	cat /dev/null > ${TMPDIR}/certindex
36}
37
38setup() {
39	reset
40
41	cat > ${TMPDIR}/openssl.cnf <<EOF
42[ca]
43default_ca = test_ca
44
45[test_ca]
46new_certs_dir = ${TMPDIR}/
47database = ${TMPDIR}/certindex
48default_days = 365
49default_md = sha256
50policy = test_policy
51serial = ${TMPDIR}/certserial
52
53[test_policy]
54countryName             = optional
55stateOrProvinceName     = optional
56localityName            = optional
57organizationName        = optional
58organizationalUnitName  = optional
59commonName              = supplied
60emailAddress            = optional
61
62[v3_ca_root]
63subjectKeyIdentifier = hash
64authorityKeyIdentifier = keyid:always,issuer
65basicConstraints = critical, CA:true
66keyUsage = critical, cRLSign, keyCertSign
67
68[v3_ca_int]
69subjectKeyIdentifier = hash
70authorityKeyIdentifier = keyid:always,issuer
71basicConstraints = critical, CA:true
72keyUsage = critical, cRLSign, keyCertSign
73
74[v3_other]
75subjectKeyIdentifier = hash
76authorityKeyIdentifier = keyid:always,issuer
77basicConstraints = critical, CA:false
78keyUsage = critical, digitalSignature
79
80[req]
81distinguished_name      = req_distinguished_name
82
83[ req_distinguished_name ]
84EOF
85}
86
87create_root() {
88	local name=$1 file=$2
89
90	openssl req -new -days 3650 -nodes -newkey rsa:2048 -sha256 -x509 \
91	    -subj "${SUBJECT} ${name}" -keyout "${TMPDIR}/${file}.key" \
92	    -config ${TMPDIR}/openssl.cnf -extensions v3_ca_root \
93	    -out "${TMPDIR}/${file}.crt"
94}
95
96create_expired_root() {
97	local name=$1 file=$2
98
99	openssl req -new -days 3650 -nodes -newkey rsa:2048 -sha256 \
100	    -subj "${SUBJECT} ${name}" -keyout "${TMPDIR}/${file}.key" \
101	    -config ${TMPDIR}/openssl.cnf -extensions v3_ca_root \
102	    -out "${TMPDIR}/${file}.csr"
103	openssl ca -batch -notext -selfsign \
104	    -keyfile "${TMPDIR}/${file}.key" \
105	    -startdate 20100101000000Z -enddate 20200101000000Z \
106	    -config ${TMPDIR}/openssl.cnf -extensions v3_ca_root \
107	    -in "${TMPDIR}/${file}.csr" -out "${TMPDIR}/${file}.crt"
108}
109
110create_intermediate() {
111	local name=$1 file=$2 issuer_file=$3
112
113	openssl req -new -days 3650 -nodes -newkey rsa:2048 -sha256 \
114	    -subj "${SUBJECT} ${name}" -keyout "${TMPDIR}/${file}.key" \
115	    -out "${TMPDIR}/${file}.csr"
116	openssl x509 -req -days 3650 -CA "${TMPDIR}/${issuer_file}.crt" \
117	    -CAkey "${TMPDIR}/${issuer_file}.key" -CAcreateserial \
118	    -extfile ${TMPDIR}/openssl.cnf -extensions v3_ca_int \
119	    -in "${TMPDIR}/${file}.csr" -out "${TMPDIR}/${file}.crt"
120}
121
122create_expired_intermediate() {
123	local name=$1 file=$2 issuer_file=$3
124
125	openssl req -new -days 3650 -nodes -newkey rsa:2048 -sha256 \
126	    -subj "${SUBJECT} ${name}" -keyout "${TMPDIR}/${file}.key" \
127	    -out "${TMPDIR}/${file}.csr"
128	openssl ca -batch -notext -cert "${TMPDIR}/${issuer_file}.crt" \
129	    -keyfile "${TMPDIR}/${issuer_file}.key" \
130	    -startdate 20100101000000Z -enddate 20200101000000Z \
131	    -config ${TMPDIR}/openssl.cnf -extensions v3_ca_int \
132	    -in "${TMPDIR}/${file}.csr" -out "${TMPDIR}/${file}.crt"
133}
134
135create_leaf() {
136	local name=$1 file=$2 issuer_file=$3
137
138	openssl req -new -days 3650 -nodes -newkey rsa:2048 -sha256 \
139	    -subj "${SUBJECT} ${name}" -keyout "${TMPDIR}/${file}.key" \
140	    -out "${TMPDIR}/${file}.csr"
141	openssl x509 -req -days 3650 -CA "${TMPDIR}/${issuer_file}.crt" \
142	    -CAkey "${TMPDIR}/${issuer_file}.key" -CAcreateserial \
143	    -extfile ${TMPDIR}/openssl.cnf -extensions v3_other \
144	    -in "${TMPDIR}/${file}.csr" -out "${TMPDIR}/${file}.crt"
145}
146
147create_expired_leaf() {
148	local name=$1 file=$2 issuer_file=$3
149
150	openssl req -new -days 3650 -nodes -newkey rsa:2048 -sha256 \
151	    -subj "${SUBJECT} ${name}" -keyout "${TMPDIR}/${file}.key" \
152	    -out "${TMPDIR}/${file}.csr"
153	openssl ca -batch -notext -cert "${TMPDIR}/${issuer_file}.crt" \
154	    -keyfile "${TMPDIR}/${issuer_file}.key" \
155	    -config ${TMPDIR}/openssl.cnf -extensions v3_other \
156	    -startdate 20100101000000Z -enddate 20200101000000Z \
157	    -in "${TMPDIR}/${file}.csr" -out "${TMPDIR}/${file}.crt"
158}
159
160create_cross_signed() {
161	local file=$1 csr_file=$2 issuer_file=$3
162
163	openssl x509 -req -days 3650 -CA "${TMPDIR}/${issuer_file}.crt" \
164	    -CAkey "${TMPDIR}/${issuer_file}.key" -CAcreateserial \
165	    -extfile ${TMPDIR}/openssl.cnf -extensions v3_ca_int \
166	    -in "${TMPDIR}/${csr_file}.csr" -out "${TMPDIR}/${file}.crt"
167}
168
169create_expired_cross_signed() {
170	local file=$1 csr_file=$2 issuer_file=$3
171
172	openssl ca -batch -notext -cert "${TMPDIR}/${issuer_file}.crt" \
173	    -keyfile "${TMPDIR}/${issuer_file}.key" \
174	    -startdate 20100101000000Z -enddate 20200101000000Z \
175	    -config ${TMPDIR}/openssl.cnf -extensions v3_ca_int \
176	    -in "${TMPDIR}/${csr_file}.csr" -out "${TMPDIR}/${file}.crt"
177}
178
179create_bundle() {
180	local bundle_file=$1
181	shift
182
183	mkdir -p $(dirname ${bundle_file})
184	cat /dev/null > ${bundle_file}
185
186	for _cert_file in $@; do
187		openssl x509 -nameopt oneline -subject -issuer -noout \
188		    -in "${TMPDIR}/${_cert_file}.crt" >> ${bundle_file}
189	done
190	for _cert_file in $@; do
191		cat "${TMPDIR}/${_cert_file}.crt" >> ${bundle_file}
192	done
193}
194
195create_root_bundle() {
196	local bundle_file=$1
197	shift
198
199	mkdir -p $(dirname ${bundle_file})
200	cat /dev/null > ${bundle_file}
201
202	for _cert_file in $@; do
203		openssl x509 -nameopt oneline -subject -issuer \
204		    -in "${TMPDIR}/${_cert_file}.crt" >> ${bundle_file}
205	done
206}
207
208setup
209
210# Scenario 1a.
211reset
212create_root "Root CA 1" "ca-root"
213create_leaf "Server 1" "server-1" "ca-root"
214create_root_bundle "./1a/roots.pem" "ca-root"
215create_bundle "./1a/bundle.pem" "server-1"
216
217# Scenarios 2a and 2b.
218reset
219create_root "Root CA 1" "ca-root"
220create_intermediate "Intermediate CA 1" "ca-int-1" "ca-root"
221create_leaf "Server 1" "server-1" "ca-int-1"
222create_root_bundle "./2a/roots.pem" "ca-root"
223create_bundle "./2a/bundle.pem" "server-1" "ca-int-1"
224create_root_bundle "./2b/roots.pem" "ca-root"
225create_bundle "./2b/bundle.pem" "server-1"
226
227# Scenarios 3a, 3b, 3c, 3d and 3e.
228reset
229create_root "Root CA 1" "ca-root"
230create_intermediate "Intermediate CA 1" "ca-int-1" "ca-root"
231create_intermediate "Intermediate CA 2" "ca-int-2" "ca-int-1"
232create_intermediate "Intermediate CA 3" "ca-int-3" "ca-int-2"
233create_leaf "Server 1" "server-1" "ca-int-3"
234create_root_bundle "./3a/roots.pem" "ca-root"
235create_bundle "./3a/bundle.pem" "server-1" "ca-int-3" "ca-int-2" "ca-int-1"
236create_root_bundle "./3b/roots.pem" "ca-root"
237create_bundle "./3b/bundle.pem" "server-1" "ca-int-3" "ca-int-2"
238create_root_bundle "./3c/roots.pem" "ca-root"
239create_bundle "./3c/bundle.pem" "server-1" "ca-int-3" "ca-int-1"
240create_root_bundle "./3d/roots.pem" "ca-root"
241create_bundle "./3d/bundle.pem" "server-1" "ca-int-2" "ca-int-1"
242create_root_bundle "./3e/roots.pem" "ca-root"
243create_bundle "./3e/bundle.pem" "server-1" "ca-int-1" "ca-int-2" "ca-int-3"
244
245# Scenarios 4a, 4b, 4c, 4d, 4e, 4f, 4g and 4h.
246reset
247create_root "Root CA 1" "ca-root-1"
248create_root "Root CA 2" "ca-root-2"
249create_intermediate "Intermediate CA 1" "ca-int-1" "ca-root-1"
250create_cross_signed "ca-int-1-xs" "ca-int-1" "ca-root-2"
251create_leaf "Server 1" "server-1" "ca-int-1"
252create_root_bundle "./4a/roots.pem" "ca-root-1" "ca-root-2"
253create_bundle "./4a/bundle.pem" "server-1" "ca-int-1" "ca-int-1-xs"
254create_root_bundle "./4b/roots.pem" "ca-root-1"
255create_bundle "./4b/bundle.pem" "server-1" "ca-int-1" "ca-int-1-xs"
256create_root_bundle "./4c/roots.pem" "ca-root-2"
257create_bundle "./4c/bundle.pem" "server-1" "ca-int-1" "ca-int-1-xs"
258create_root_bundle "./4d/roots.pem" "ca-root-1" "ca-root-2"
259create_bundle "./4d/bundle.pem" "server-1" "ca-int-1"
260create_root_bundle "./4e/roots.pem" "ca-root-1" "ca-root-2"
261create_bundle "./4e/bundle.pem" "server-1" "ca-int-1-xs"
262create_root_bundle "./4f/roots.pem" "ca-root-1" "ca-root-2"
263create_bundle "./4f/bundle.pem" "server-1" "ca-int-1-xs" "ca-int-1"
264create_root_bundle "./4g/roots.pem" "ca-root-1"
265create_bundle "./4g/bundle.pem" "server-1" "ca-int-1-xs" "ca-int-1"
266create_root_bundle "./4h/roots.pem" "ca-root-2"
267create_bundle "./4h/bundle.pem" "server-1" "ca-int-1-xs" "ca-int-1"
268
269# Scenario 5a, 5b, 5c, 5d, 5e, 5f, 5g, 5h and 5i.
270reset
271create_root "Root CA 1" "ca-root-1"
272create_intermediate "Intermediate CA 1" "ca-int-1" "ca-root-1"
273create_intermediate "Intermediate CA 2" "ca-int-2" "ca-int-1"
274create_root "Root CA 2" "ca-root-2"
275create_cross_signed "ca-int-2-xs" "ca-int-2" "ca-root-2"
276create_leaf "Server 1" "server-1" "ca-int-2"
277create_root_bundle "./5a/roots.pem" "ca-root-1" "ca-root-2"
278create_bundle "./5a/bundle.pem" "server-1" "ca-int-2" "ca-int-2-xs" "ca-int-1"
279create_root_bundle "./5b/roots.pem" "ca-root-2"
280create_bundle "./5b/bundle.pem" "server-1" "ca-int-2" "ca-int-2-xs" "ca-int-1"
281create_root_bundle "./5c/roots.pem" "ca-root-1"
282create_bundle "./5c/bundle.pem" "server-1" "ca-int-2" "ca-int-2-xs" "ca-int-1"
283create_root_bundle "./5d/roots.pem" "ca-root-1" "ca-root-2"
284create_bundle "./5d/bundle.pem" "server-1" "ca-int-2-xs" "ca-int-1"
285create_root_bundle "./5e/roots.pem" "ca-root-1" "ca-root-2"
286create_bundle "./5e/bundle.pem" "server-1" "ca-int-2" "ca-int-2-xs"
287create_root_bundle "./5f/roots.pem" "ca-root-1" "ca-root-2"
288create_bundle "./5f/bundle.pem" "server-1" "ca-int-2" "ca-int-1"
289create_root_bundle "./5g/roots.pem" "ca-root-1" "ca-root-2"
290create_bundle "./5g/bundle.pem" "server-1" "ca-int-2-xs" "ca-int-2" "ca-int-1"
291create_root_bundle "./5h/roots.pem" "ca-root-2"
292create_bundle "./5h/bundle.pem" "server-1" "ca-int-2-xs" "ca-int-2" "ca-int-1"
293create_root_bundle "./5i/roots.pem" "ca-root-1"
294create_bundle "./5i/bundle.pem" "server-1" "ca-int-2-xs" "ca-int-2" "ca-int-1"
295
296# Scenarios 6a and 6b.
297reset
298create_root "Root CA 1" "ca-root-1"
299create_intermediate "Intermediate CA 1" "ca-int-1" "ca-root-1"
300create_intermediate "Intermediate CA 2" "ca-int-2" "ca-int-1"
301create_expired_root "Root CA 2" "ca-root-2"
302create_cross_signed "ca-int-2-xs" "ca-int-2" "ca-root-2"
303create_leaf "Server 1" "server-1" "ca-int-2"
304create_root_bundle "./6a/roots.pem" "ca-root-1" "ca-root-2"
305create_bundle "./6a/bundle.pem" "server-1" "ca-int-2" "ca-int-2-xs" "ca-int-1"
306create_root_bundle "./6b/roots.pem" "ca-root-1" "ca-root-2"
307create_bundle "./6b/bundle.pem" "server-1" "ca-int-2-xs" "ca-int-2" "ca-int-1"
308
309# Scenarios 7a and 7b.
310reset
311create_expired_root "Root CA 1" "ca-root-1"
312create_intermediate "Intermediate CA 1" "ca-int-1" "ca-root-1"
313create_intermediate "Intermediate CA 2" "ca-int-2" "ca-int-1"
314create_root "Root CA 2" "ca-root-2"
315create_cross_signed "ca-int-2-xs" "ca-int-2" "ca-root-2"
316create_leaf "Server 1" "server-1" "ca-int-2"
317create_root_bundle "./7a/roots.pem" "ca-root-1" "ca-root-2"
318create_bundle "./7a/bundle.pem" "server-1" "ca-int-2" "ca-int-2-xs" "ca-int-1"
319create_root_bundle "./7b/roots.pem" "ca-root-1" "ca-root-2"
320create_bundle "./7b/bundle.pem" "server-1" "ca-int-2-xs" "ca-int-2" "ca-int-1"
321
322# Scenario 8a.
323reset
324create_root "Root CA 1" "ca-root"
325create_intermediate "Intermediate CA 1" "ca-int-1" "ca-root"
326create_expired_leaf "Server 1" "server-1" "ca-int-1"
327create_root_bundle "./8a/roots.pem" "ca-root"
328create_bundle "./8a/bundle.pem" "server-1" "ca-int-1"
329
330# Scenario 9a.
331reset
332create_root "Root CA 1" "ca-root"
333create_expired_intermediate "Intermediate CA 1" "ca-int-1" "ca-root"
334create_leaf "Server 1" "server-1" "ca-int-1"
335create_root_bundle "./9a/roots.pem" "ca-root"
336create_bundle "./9a/bundle.pem" "server-1" "ca-int-1"
337
338# Scenarios 10a and 10b.
339reset
340create_root "Root CA 1" "ca-root-1"
341create_root "Root CA 2" "ca-root-2"
342create_expired_intermediate "Intermediate CA 1" "ca-int-1" "ca-root-1"
343create_cross_signed "ca-int-1-xs" "ca-int-1" "ca-root-2"
344create_leaf "Server 1" "server-1" "ca-int-1"
345create_root_bundle "./10a/roots.pem" "ca-root-1" "ca-root-2"
346create_bundle "./10a/bundle.pem" "server-1" "ca-int-1" "ca-int-1-xs"
347create_root_bundle "./10b/roots.pem" "ca-root-1" "ca-root-2"
348create_bundle "./10b/bundle.pem" "server-1" "ca-int-1-xs" "ca-int-1"
349
350# Scenarios 11a and 11b.
351reset
352create_root "Root CA 1" "ca-root-1"
353create_expired_intermediate "Intermediate CA 1" "ca-int-1" "ca-root-1"
354create_intermediate "Intermediate CA 2" "ca-int-2" "ca-int-1"
355create_root "Root CA 2" "ca-root-2"
356create_cross_signed "ca-int-2-xs" "ca-int-2" "ca-root-2"
357create_leaf "Server 1" "server-1" "ca-int-2"
358create_root_bundle "./11a/roots.pem" "ca-root-1" "ca-root-2"
359create_bundle "./11a/bundle.pem" "server-1" "ca-int-2" "ca-int-2-xs" "ca-int-1"
360create_root_bundle "./11b/roots.pem" "ca-root-1" "ca-root-2"
361create_bundle "./11b/bundle.pem" "server-1" "ca-int-2-xs" "ca-int-2" "ca-int-1"
362
363# Scenario 12a.
364reset
365create_root "Root CA 1" "ca-root-1"
366create_expired_intermediate "Intermediate CA 1" "ca-int-1" "ca-root-1"
367create_cross_signed "ca-root-2" "ca-int-1" "ca-root-1"
368create_intermediate "Intermediate CA 2" "ca-int-2" "ca-int-1"
369create_leaf "Server 1" "server-1" "ca-int-2"
370create_root_bundle "./12a/roots.pem" "ca-root-1" "ca-root-2"
371create_bundle "./12a/bundle.pem" "server-1" "ca-int-2" "ca-int-1"
372
373# Scenario 13a.
374reset
375create_root "Root CA 1" "ca-root-1"
376create_intermediate "Intermediate CA 1" "ca-int-1" "ca-root-1"
377create_expired_cross_signed "ca-root-2" "ca-int-1" "ca-root-1"
378create_intermediate "Intermediate CA 2" "ca-int-2" "ca-int-1"
379create_leaf "Server 1" "server-1" "ca-int-2"
380create_root_bundle "./13a/roots.pem" "ca-root-1" "ca-root-2"
381create_bundle "./13a/bundle.pem" "server-1" "ca-int-2" "ca-int-1"
382