1#! /bin/bash
2
3set -e
4
5DOMAIN=example.com
6HOST=mail.${DOMAIN}
7TEST=./tls_dane
8
9key() {
10    local key=$1; shift
11
12    if [ ! -f "${key}.pem" ]; then
13    	openssl genpkey 2>/dev/null \
14	    -paramfile <(openssl ecparam -name prime256v1) \
15	    -out "${key}.pem"
16    fi
17}
18
19req() {
20    local key=$1; shift
21    local cn=$1; shift
22
23    key "$key"
24    openssl req -new -sha256 -key "${key}.pem" 2>/dev/null \
25	-config <(printf "[req]\n%s\n%s\n[dn]\nCN=%s\n" \
26		   "prompt = no" "distinguished_name = dn" "${cn}") 
27}
28
29req_nocn() {
30    local key=$1; shift
31
32    key "$key"
33    openssl req -new -sha256 -subj / -key "${key}.pem" 2>/dev/null \
34	-config <(printf "[req]\n%s\n[dn]\nCN_default =\n" \
35		   "distinguished_name = dn") 
36}
37
38cert() {
39    local cert=$1; shift
40    local exts=$1; shift
41
42    openssl x509 -req -sha256 -out "${cert}.pem" 2>/dev/null \
43	-extfile <(printf "%s\n" "$exts") "$@"
44}
45
46genroot() {
47    local cn=$1; shift
48    local key=$1; shift
49    local cert=$1; shift
50    local skid=$1; shift
51    local akid=$1; shift
52
53    exts=$(printf "%s\n%s\n%s\n" "$skid" "$akid" "basicConstraints = CA:true")
54    req "$key" "$cn" |
55    	cert "$cert" "$exts" -signkey "${key}.pem" -set_serial 1 -days 30
56}
57
58genca() {
59    local cn=$1; shift
60    local key=$1; shift
61    local cert=$1; shift
62    local skid=$1; shift
63    local akid=$1; shift
64    local ca=$1; shift
65    local cakey=$1; shift
66
67    exts=$(printf "%s\n%s\n%s\n" "$skid" "$akid" "basicConstraints = CA:true")
68    req "$key" "$cn" |
69    	cert "$cert" "$exts" -CA "${ca}.pem" -CAkey "${cakey}.pem" \
70	    -set_serial 2 -days 30 "$@"
71}
72
73genee() {
74    local cn=$1; shift
75    local key=$1; shift
76    local cert=$1; shift
77    local ca=$1; shift
78    local cakey=$1; shift
79
80    exts=$(printf "%s\n%s\n%s\n%s\n%s\n[alts]\n%s\n" \
81	    "subjectKeyIdentifier = hash" \
82	    "authorityKeyIdentifier = keyid, issuer" \
83	    "basicConstraints = CA:false" \
84	    "extendedKeyUsage = serverAuth" \
85	    "subjectAltName = @alts" "DNS=${cn}")
86    req "$key" "$cn" |
87    	cert "$cert" "$exts" -CA "${ca}.pem" -CAkey "${cakey}.pem" \
88	    -set_serial 2 -days 30 "$@"
89}
90
91genss() {
92    local cn=$1; shift
93    local key=$1; shift
94    local cert=$1; shift
95
96    exts=$(printf "%s\n%s\n%s\n%s\n%s\n[alts]\n%s\n" \
97	    "subjectKeyIdentifier   = hash" \
98	    "authorityKeyIdentifier = keyid, issuer" \
99	    "basicConstraints = CA:true" \
100	    "extendedKeyUsage = serverAuth" \
101	    "subjectAltName = @alts" "DNS=${cn}")
102    req "$key" "$cn" |
103	cert "$cert" "$exts" -set_serial 1 -days 30 -signkey "${key}.pem" "$@"
104}
105
106gennocn() {
107    local key=$1; shift
108    local cert=$1; shift
109
110    req_nocn "$key" |
111	cert "$cert" "" -signkey "${key}.pem" -set_serial 1 -days -1 "$@"
112}
113
114runtest() {
115    local desc=$1; shift
116    local usage=$1; shift
117    local selector=$1; shift
118    local mtype=$1; shift
119    local tlsa=$1; shift
120    local ca=$1; shift
121    local chain=$1; shift
122    local digest
123
124    case $mtype in
125    0) digest="";;
126    1) digest=sha256;;
127    2) digest=sha512;;
128    *) echo "bad mtype: $mtype"; exit 1;;
129    esac
130
131    printf "%d %d %d %-24s %s: " "$usage" "$selector" "$mtype" "$tlsa" "$desc"
132
133    if [ -n "$ca" ]; then ca="$ca.pem"; fi
134    "$TEST" "$usage" "$selector" "$digest" "$tlsa.pem" "$ca" "$chain.pem" \
135    	"$@" > /dev/null
136}
137
138checkpass() { runtest "$@" && { echo pass; } || { echo fail; exit 1; }; }
139checkfail() { runtest "$@" && { echo fail; exit 1; } || { echo pass; }; }
140
141#---------
142
143genss "$HOST" sskey sscert
144gennocn akey acert
145
146# Tests that might depend on akid/skid chaining
147#
148for rakid in "" \
149	"authorityKeyIdentifier  = keyid,issuer" \
150	"authorityKeyIdentifier  = issuer" \
151	"authorityKeyIdentifier  = keyid"
152do
153for cakid in "" \
154	"authorityKeyIdentifier  = keyid,issuer" \
155	"authorityKeyIdentifier  = issuer" \
156	"authorityKeyIdentifier  = keyid"
157do
158for rskid in "" "subjectKeyIdentifier = hash"
159do
160for caskid in "" "subjectKeyIdentifier = hash"
161do
162
163genroot "Root CA" rootkey rootcert "$rskid" "$rakid"
164genca "CA 1" cakey1 cacert1 "$caskid" "$cakid" rootcert rootkey
165genca "CA 2" cakey2 cacert2 "$caskid" "$cakid" cacert1 cakey1
166genee "$HOST" eekey eecert cacert2 cakey2
167cat eecert.pem cacert2.pem cacert1.pem rootcert.pem > chain.pem
168cat eecert.pem cacert2.pem cacert1.pem > chain1.pem
169
170for s in 0 1
171do
172  checkpass "OOB root TA" 2 "$s" 0 rootcert "" chain1 "$HOST"
173  checkpass "OOB TA" 2 "$s" 0 cacert2 "" eecert "$HOST"
174  checkpass "in-chain root TA" 2 "$s" 1 rootcert "" chain "$HOST"
175
176  for m in 0 1 2
177  do
178    checkpass "valid TA" 2 "$s" "$m" rootcert "" chain "$HOST"
179    for ca in "cacert1" "cacert2"; do
180	checkpass "valid TA" 2 "$s" "$m" "$ca" "" chain "$HOST"
181	checkpass "valid TA" 2 "$s" "$m" "$ca" "" chain1 "$HOST"
182	checkpass "valid TA+CA" 2 "$s" "$m" "$ca" rootcert chain1 "$HOST"
183	checkpass "sub-domain" 2 "$s" "$m" "$ca" "" chain1 whatever ".$DOMAIN"
184	checkfail "wrong name" 2 "$s" "$m" "$ca" "" chain1 "whatever"
185    done
186  done
187done
188
189done
190done
191done
192done
193
194# These tests don't depend in the akid/skid chaining:
195#
196for s in 0 1
197do
198  checkfail "missing TA" 2 "$s" 1 rootcert "" chain1 "$HOST"
199  for m in 0 1 2
200  do
201    checkpass "depth 0 TA" 2 "$s" "$m" sscert "" sscert "$HOST" 
202    checkfail "non-TA" 2 "$s" "$m" eecert rootcert chain "$HOST"
203    checkfail "depth 0 TA namecheck" 2 "$s" "$m" sscert sscert sscert whatever
204
205    checkpass "valid EE" 3 "$s" "$m" eecert "" chain whatever
206    checkpass "key-only EE" 3 "$s" "$m" acert "" acert whatever
207    checkfail "wrong EE" 3 "$s" "$m" cacert2 "" chain whatever
208  done
209done
210
211rm -f *.pem
212