xref: /freebsd/crypto/openssh/regress/cert-hostkey.sh (revision 2574974648c68c738aec3ff96644d888d7913a37)
1#	$OpenBSD: cert-hostkey.sh,v 1.31 2026/02/11 22:58:23 djm Exp $
2#	Placed in the Public Domain.
3
4tid="certified host keys"
5
6rm -f $OBJ/known_hosts-cert* $OBJ/host_ca_key* $OBJ/host_revoked_*
7rm -f $OBJ/cert_host_key* $OBJ/host_krl_*
8
9# Allow all hostkey/pubkey types, prefer certs for the client
10rsa=0
11types=""
12for i in `$SSH -Q key | maybe_filter_sk`; do
13	if [ -z "$types" ]; then
14		types="$i"
15		continue
16	fi
17	case "$i" in
18	# Special treatment for RSA keys.
19	*rsa*cert*)
20		types="rsa-sha2-256-cert-v01@openssh.com,$i,$types"
21		types="rsa-sha2-512-cert-v01@openssh.com,$types";;
22	*rsa*)
23		rsa=1
24		types="$types,rsa-sha2-512,rsa-sha2-256,$i";;
25	# Prefer certificate to plain keys.
26	*cert*)	types="$i,$types";;
27	*)	types="$types,$i";;
28	esac
29done
30(
31	echo "HostKeyAlgorithms ${types}"
32	echo "PubkeyAcceptedAlgorithms *"
33) >> $OBJ/ssh_proxy
34cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak
35(
36	echo "HostKeyAlgorithms *"
37	echo "PubkeyAcceptedAlgorithms *"
38) >> $OBJ/sshd_proxy_bak
39
40HOSTS='localhost-with-alias,127.0.0.1,::1'
41
42kh_ca() {
43	for k in "$@" ; do
44		printf "@cert-authority $HOSTS "
45		cat $OBJ/$k || fatal "couldn't cat $k"
46	done
47}
48kh_revoke() {
49	for k in "$@" ; do
50		printf "@revoked * "
51		cat $OBJ/$k || fatal "couldn't cat $k"
52	done
53}
54
55# Create a CA key and add it to known hosts. Ed25519 chosen for speed.
56# RSA for testing RSA/SHA2 signatures if supported.
57ktype2=ed25519
58[ "x$rsa" = "x1" ] && ktype2=rsa
59${SSHKEYGEN} -q -N '' -t ed25519  -f $OBJ/host_ca_key ||\
60	fail "ssh-keygen of host_ca_key failed"
61${SSHKEYGEN} -q -N '' -t $ktype2  -f $OBJ/host_ca_key2 ||\
62	fail "ssh-keygen of host_ca_key failed"
63
64kh_ca host_ca_key.pub host_ca_key2.pub > $OBJ/known_hosts-cert.orig
65cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert
66
67# Plain text revocation files
68touch $OBJ/host_revoked_empty
69touch $OBJ/host_revoked_plain
70touch $OBJ/host_revoked_cert
71cat $OBJ/host_ca_key.pub $OBJ/host_ca_key2.pub > $OBJ/host_revoked_ca
72
73PLAIN_TYPES=`echo "$SSH_KEYTYPES" | sed 's/^ssh-//'`
74
75if echo "$PLAIN_TYPES" | grep '^rsa$' >/dev/null 2>&1 ; then
76	PLAIN_TYPES="$PLAIN_TYPES rsa-sha2-256 rsa-sha2-512"
77fi
78
79# Prepare certificate, plain key and CA KRLs
80${SSHKEYGEN} -kf $OBJ/host_krl_empty || fatal "KRL init failed"
81${SSHKEYGEN} -kf $OBJ/host_krl_plain || fatal "KRL init failed"
82${SSHKEYGEN} -kf $OBJ/host_krl_cert || fatal "KRL init failed"
83${SSHKEYGEN} -kf $OBJ/host_krl_ca $OBJ/host_ca_key.pub $OBJ/host_ca_key2.pub \
84	|| fatal "KRL init failed"
85
86# Generate and sign host keys
87serial=1
88for ktype in $PLAIN_TYPES ; do
89	verbose "$tid: sign host ${ktype} cert"
90	# Generate and sign a host key
91	${SSHKEYGEN} -q -N '' -t ${ktype} \
92	    -f $OBJ/cert_host_key_${ktype} || \
93		fatal "ssh-keygen of cert_host_key_${ktype} failed"
94	${SSHKEYGEN} -ukf $OBJ/host_krl_plain \
95	    $OBJ/cert_host_key_${ktype}.pub || fatal "KRL update failed"
96	cat $OBJ/cert_host_key_${ktype}.pub >> $OBJ/host_revoked_plain
97	case $ktype in
98	rsa-sha2-*)	tflag="-t $ktype"; ca="$OBJ/host_ca_key2" ;;
99	*)		tflag=""; ca="$OBJ/host_ca_key" ;;
100	esac
101	${SSHKEYGEN} -h -q -s $ca -z $serial $tflag \
102	    -I "regress host key for $USER" \
103	    -n $HOSTS $OBJ/cert_host_key_${ktype} ||
104		fatal "couldn't sign cert_host_key_${ktype}"
105	${SSHKEYGEN} -ukf $OBJ/host_krl_cert \
106	    $OBJ/cert_host_key_${ktype}-cert.pub || \
107		fatal "KRL update failed"
108	cat $OBJ/cert_host_key_${ktype}-cert.pub >> $OBJ/host_revoked_cert
109	serial=`expr $serial + 1`
110done
111
112attempt_connect() {
113	_ident="$1"
114	_expect_success="$2"
115	shift; shift
116	verbose "$tid: $_ident expect success $_expect_success"
117	cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert
118	${SSH} -oUserKnownHostsFile=$OBJ/known_hosts-cert \
119	    -oGlobalKnownHostsFile=$OBJ/known_hosts-cert \
120	    "$@" -F $OBJ/ssh_proxy somehost true
121	_r=$?
122	if [ "x$_expect_success" = "xyes" ] ; then
123		if [ $_r -ne 0 ]; then
124			fail "ssh cert connect $_ident failed"
125		fi
126	else
127		if [ $_r -eq 0 ]; then
128			fail "ssh cert connect $_ident succeeded unexpectedly"
129		fi
130	fi
131}
132
133# Basic connect and revocation tests.
134for ktype in $PLAIN_TYPES ; do
135	verbose "$tid: host ${ktype} cert connect"
136	(
137		cat $OBJ/sshd_proxy_bak
138		echo HostKey $OBJ/cert_host_key_${ktype}
139		echo HostCertificate $OBJ/cert_host_key_${ktype}-cert.pub
140	) > $OBJ/sshd_proxy
141
142	#               test name                         expect success
143	attempt_connect "$ktype basic connect"			"yes"
144	attempt_connect "$ktype empty KRL"			"yes" \
145	    -oRevokedHostKeys=$OBJ/host_krl_empty
146	attempt_connect "$ktype multiple KRL files"		"no" \
147	    -oRevokedHostKeys="/dev/null $OBJ/host_krl_plain"
148	attempt_connect "$ktype KRL w/ plain key revoked"	"no" \
149	    -oRevokedHostKeys=$OBJ/host_krl_plain
150	attempt_connect "$ktype KRL w/ cert revoked"		"no" \
151	    -oRevokedHostKeys=$OBJ/host_krl_cert
152	attempt_connect "$ktype KRL w/ CA revoked"		"no" \
153	    -oRevokedHostKeys=$OBJ/host_krl_ca
154	attempt_connect "$ktype empty plaintext revocation"	"yes" \
155	    -oRevokedHostKeys=$OBJ/host_revoked_empty
156	attempt_connect "$ktype plain key plaintext revocation"	"no" \
157	    -oRevokedHostKeys=$OBJ/host_revoked_plain
158	attempt_connect "$ktype cert plaintext revocation"	"no" \
159	    -oRevokedHostKeys=$OBJ/host_revoked_cert
160	attempt_connect "$ktype CA plaintext revocation"	"no" \
161	    -oRevokedHostKeys=$OBJ/host_revoked_ca
162done
163
164# Revoked certificates with key present
165kh_ca host_ca_key.pub host_ca_key2.pub > $OBJ/known_hosts-cert.orig
166for ktype in $PLAIN_TYPES ; do
167	test -f "$OBJ/cert_host_key_${ktype}.pub" || fatal "no pubkey"
168	kh_revoke cert_host_key_${ktype}.pub >> $OBJ/known_hosts-cert.orig
169done
170cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert
171for ktype in $PLAIN_TYPES ; do
172	verbose "$tid: host ${ktype} revoked cert"
173	(
174		cat $OBJ/sshd_proxy_bak
175		echo HostKey $OBJ/cert_host_key_${ktype}
176		echo HostCertificate $OBJ/cert_host_key_${ktype}-cert.pub
177	) > $OBJ/sshd_proxy
178
179	cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert
180	${SSH} -oUserKnownHostsFile=$OBJ/known_hosts-cert \
181	    -oGlobalKnownHostsFile=$OBJ/known_hosts-cert \
182		-F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
183	if [ $? -eq 0 ]; then
184		fail "ssh cert connect succeeded unexpectedly"
185	fi
186done
187
188# Revoked CA
189kh_ca host_ca_key.pub host_ca_key2.pub > $OBJ/known_hosts-cert.orig
190kh_revoke host_ca_key.pub host_ca_key2.pub >> $OBJ/known_hosts-cert.orig
191cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert
192for ktype in $PLAIN_TYPES ; do
193	verbose "$tid: host ${ktype} revoked cert"
194	(
195		cat $OBJ/sshd_proxy_bak
196		echo HostKey $OBJ/cert_host_key_${ktype}
197		echo HostCertificate $OBJ/cert_host_key_${ktype}-cert.pub
198	) > $OBJ/sshd_proxy
199	cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert
200	${SSH} -oUserKnownHostsFile=$OBJ/known_hosts-cert \
201	    -oGlobalKnownHostsFile=$OBJ/known_hosts-cert \
202		-F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
203	if [ $? -eq 0 ]; then
204		fail "ssh cert connect succeeded unexpectedly"
205	fi
206done
207
208# Create a CA key and add it to known hosts
209kh_ca host_ca_key.pub host_ca_key2.pub > $OBJ/known_hosts-cert.orig
210cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert
211
212test_one() {
213	ident="$1"
214	result="$2"
215	hosts="$3"
216	sign_opts="$4"
217
218	test -z "$hosts" || sign_opts="$sign_opts -n $hosts"
219
220	for kt in $PLAIN_TYPES; do
221		case $ktype in
222		rsa-sha2-*)	tflag="-t $ktype"; ca="$OBJ/host_ca_key2" ;;
223		*)		tflag=""; ca="$OBJ/host_ca_key" ;;
224		esac
225		if test -z "$hosts" ; then
226			# Empty principals section.
227			${SSHKEYGEN} -q -s $ca $tflag $sign_opts \
228			    -I "regress host key for $USER" \
229			    $OBJ/cert_host_key_${kt} 2>/dev/null ||
230				fatal "couldn't sign cert_host_key_${kt}"
231		else
232			# Be careful with quoting principals, which may contain
233			# wilcards.
234			${SSHKEYGEN} -q -s $ca $tflag $sign_opts \
235			    -I "regress host key for $USER" -n "$hosts" \
236			    $OBJ/cert_host_key_${kt} ||
237				fatal "couldn't sign cert_host_key_${kt}"
238		fi
239		(
240			cat $OBJ/sshd_proxy_bak
241			echo HostKey $OBJ/cert_host_key_${kt}
242			echo HostCertificate $OBJ/cert_host_key_${kt}-cert.pub
243		) > $OBJ/sshd_proxy
244
245		cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert
246		${SSH} -oUserKnownHostsFile=$OBJ/known_hosts-cert \
247		    -oGlobalKnownHostsFile=$OBJ/known_hosts-cert \
248		    -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
249		rc=$?
250		if [ "x$result" = "xsuccess" ] ; then
251			if [ $rc -ne 0 ]; then
252				fail "ssh cert connect $ident failed unexpectedly"
253			fi
254		else
255			if [ $rc -eq 0 ]; then
256				fail "ssh cert connect $ident succeeded unexpectedly"
257			fi
258		fi
259	done
260}
261
262test_one "simple"		success $HOSTS	"-h"
263test_one "wildcard"		success "loc*"	"-h"
264test_one "user-certificate"	failure $HOSTS
265test_one "wildcard user"	failure "local*"
266test_one "empty principals"	failure ""	"-h"
267test_one "wrong principals"	failure foo	"-h"
268test_one "cert not yet valid"	failure $HOSTS	"-h -V20300101:20320101"
269test_one "cert expired"		failure $HOSTS	"-h -V19800101:19900101"
270test_one "cert valid interval"	success $HOSTS	"-h -V-1w:+2w"
271test_one "cert has constraints"	failure $HOSTS	"-h -Oforce-command=false"
272
273# Check downgrade of cert to raw key when no CA found
274for ktype in $PLAIN_TYPES ; do
275	rm -f $OBJ/known_hosts-cert $OBJ/cert_host_key*
276	verbose "$tid: host ${ktype} ${v} cert downgrade to raw key"
277	# Generate and sign a host key
278	${SSHKEYGEN} -q -N '' -t ${ktype} -f $OBJ/cert_host_key_${ktype} || \
279		fail "ssh-keygen of cert_host_key_${ktype} failed"
280	case $ktype in
281	rsa-sha2-*)	tflag="-t $ktype"; ca="$OBJ/host_ca_key2" ;;
282	*)		tflag=""; ca="$OBJ/host_ca_key" ;;
283	esac
284	${SSHKEYGEN} -h -q $tflag -s $ca $tflag \
285	    -I "regress host key for $USER" \
286	    -n $HOSTS $OBJ/cert_host_key_${ktype} ||
287		fatal "couldn't sign cert_host_key_${ktype}"
288	(
289		printf "$HOSTS "
290		cat $OBJ/cert_host_key_${ktype}.pub
291	) > $OBJ/known_hosts-cert
292	(
293		cat $OBJ/sshd_proxy_bak
294		echo HostKey $OBJ/cert_host_key_${ktype}
295		echo HostCertificate $OBJ/cert_host_key_${ktype}-cert.pub
296	) > $OBJ/sshd_proxy
297
298	${SSH} -oUserKnownHostsFile=$OBJ/known_hosts-cert \
299	    -oGlobalKnownHostsFile=none -F $OBJ/ssh_proxy somehost true
300	if [ $? -ne 0 ]; then
301		fail "ssh cert connect failed"
302	fi
303	# Also check that it works when the known_hosts file is not in the
304	# first array position.
305	${SSH} -oUserKnownHostsFile="/dev/null $OBJ/known_hosts-cert" \
306	    -oGlobalKnownHostsFile=none -F $OBJ/ssh_proxy somehost true
307	if [ $? -ne 0 ]; then
308		fail "ssh cert connect failed known_hosts 2nd"
309	fi
310done
311
312# Wrong certificate
313kh_ca host_ca_key.pub host_ca_key2.pub > $OBJ/known_hosts-cert.orig
314cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert
315for kt in $PLAIN_TYPES ; do
316	verbose "$tid: host ${kt} connect wrong cert"
317	rm -f $OBJ/cert_host_key*
318	# Self-sign key
319	${SSHKEYGEN} -q -N '' -t ${kt} -f $OBJ/cert_host_key_${kt} || \
320		fail "ssh-keygen of cert_host_key_${kt} failed"
321	case $kt in
322	rsa-sha2-*)	tflag="-t $kt" ;;
323	*)		tflag="" ;;
324	esac
325	${SSHKEYGEN} $tflag -h -q -s $OBJ/cert_host_key_${kt} \
326	    -I "regress host key for $USER" \
327	    -n $HOSTS $OBJ/cert_host_key_${kt} ||
328		fatal "couldn't sign cert_host_key_${kt}"
329	(
330		cat $OBJ/sshd_proxy_bak
331		echo HostKey $OBJ/cert_host_key_${kt}
332		echo HostCertificate $OBJ/cert_host_key_${kt}-cert.pub
333	) > $OBJ/sshd_proxy
334
335	cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert
336	${SSH} -oUserKnownHostsFile=$OBJ/known_hosts-cert \
337	    -oGlobalKnownHostsFile=$OBJ/known_hosts-cert \
338		-F $OBJ/ssh_proxy -q somehost true >/dev/null 2>&1
339	if [ $? -eq 0 ]; then
340		fail "ssh cert connect $ident succeeded unexpectedly"
341	fi
342done
343
344rm -f $OBJ/known_hosts-cert* $OBJ/host_ca_key* $OBJ/cert_host_key*
345