summaryrefslogtreecommitdiffstats
path: root/regress/unittests
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--regress/unittests/Makefile7
-rw-r--r--regress/unittests/Makefile.inc53
-rw-r--r--regress/unittests/authopt/testdata/all_permit.cert1
-rw-r--r--regress/unittests/authopt/testdata/bad_sourceaddr.cert1
-rw-r--r--regress/unittests/authopt/testdata/force_command.cert1
-rw-r--r--regress/unittests/authopt/testdata/host.cert1
-rw-r--r--regress/unittests/authopt/testdata/mktestdata.sh48
-rw-r--r--regress/unittests/authopt/testdata/no_agentfwd.cert1
-rw-r--r--regress/unittests/authopt/testdata/no_permit.cert1
-rw-r--r--regress/unittests/authopt/testdata/no_portfwd.cert1
-rw-r--r--regress/unittests/authopt/testdata/no_pty.cert1
-rw-r--r--regress/unittests/authopt/testdata/no_user_rc.cert1
-rw-r--r--regress/unittests/authopt/testdata/no_x11fwd.cert1
-rw-r--r--regress/unittests/authopt/testdata/only_agentfwd.cert1
-rw-r--r--regress/unittests/authopt/testdata/only_portfwd.cert1
-rw-r--r--regress/unittests/authopt/testdata/only_pty.cert1
-rw-r--r--regress/unittests/authopt/testdata/only_user_rc.cert1
-rw-r--r--regress/unittests/authopt/testdata/only_x11fwd.cert1
-rw-r--r--regress/unittests/authopt/testdata/sourceaddr.cert1
-rw-r--r--regress/unittests/authopt/testdata/unknown_critical.cert1
-rw-r--r--regress/unittests/authopt/tests.c573
-rw-r--r--regress/unittests/bitmap/Makefile14
-rw-r--r--regress/unittests/bitmap/tests.c135
-rw-r--r--regress/unittests/conversion/Makefile15
-rw-r--r--regress/unittests/conversion/tests.c51
-rw-r--r--regress/unittests/hostkeys/Makefile23
-rw-r--r--regress/unittests/hostkeys/mktestdata.sh86
-rw-r--r--regress/unittests/hostkeys/test_iterate.c1047
-rw-r--r--regress/unittests/hostkeys/testdata/dsa_1.pub1
-rw-r--r--regress/unittests/hostkeys/testdata/dsa_2.pub1
-rw-r--r--regress/unittests/hostkeys/testdata/dsa_3.pub1
-rw-r--r--regress/unittests/hostkeys/testdata/dsa_4.pub1
-rw-r--r--regress/unittests/hostkeys/testdata/dsa_5.pub1
-rw-r--r--regress/unittests/hostkeys/testdata/dsa_6.pub1
-rw-r--r--regress/unittests/hostkeys/testdata/ecdsa_1.pub1
-rw-r--r--regress/unittests/hostkeys/testdata/ecdsa_2.pub1
-rw-r--r--regress/unittests/hostkeys/testdata/ecdsa_3.pub1
-rw-r--r--regress/unittests/hostkeys/testdata/ecdsa_4.pub1
-rw-r--r--regress/unittests/hostkeys/testdata/ecdsa_5.pub1
-rw-r--r--regress/unittests/hostkeys/testdata/ecdsa_6.pub1
-rw-r--r--regress/unittests/hostkeys/testdata/ed25519_1.pub1
-rw-r--r--regress/unittests/hostkeys/testdata/ed25519_2.pub1
-rw-r--r--regress/unittests/hostkeys/testdata/ed25519_3.pub1
-rw-r--r--regress/unittests/hostkeys/testdata/ed25519_4.pub1
-rw-r--r--regress/unittests/hostkeys/testdata/ed25519_5.pub1
-rw-r--r--regress/unittests/hostkeys/testdata/ed25519_6.pub1
-rw-r--r--regress/unittests/hostkeys/testdata/known_hosts50
-rw-r--r--regress/unittests/hostkeys/testdata/rsa1_1.pub1
-rw-r--r--regress/unittests/hostkeys/testdata/rsa1_2.pub1
-rw-r--r--regress/unittests/hostkeys/testdata/rsa1_3.pub1
-rw-r--r--regress/unittests/hostkeys/testdata/rsa1_4.pub1
-rw-r--r--regress/unittests/hostkeys/testdata/rsa1_5.pub1
-rw-r--r--regress/unittests/hostkeys/testdata/rsa1_6.pub1
-rw-r--r--regress/unittests/hostkeys/testdata/rsa_1.pub1
-rw-r--r--regress/unittests/hostkeys/testdata/rsa_2.pub1
-rw-r--r--regress/unittests/hostkeys/testdata/rsa_3.pub1
-rw-r--r--regress/unittests/hostkeys/testdata/rsa_4.pub1
-rw-r--r--regress/unittests/hostkeys/testdata/rsa_5.pub1
-rw-r--r--regress/unittests/hostkeys/testdata/rsa_6.pub1
-rw-r--r--regress/unittests/hostkeys/tests.c16
-rw-r--r--regress/unittests/kex/Makefile29
-rw-r--r--regress/unittests/kex/test_kex.c202
-rw-r--r--regress/unittests/kex/tests.c14
-rw-r--r--regress/unittests/match/Makefile16
-rw-r--r--regress/unittests/match/tests.c132
-rw-r--r--regress/unittests/sshbuf/Makefile22
-rw-r--r--regress/unittests/sshbuf/test_sshbuf.c240
-rw-r--r--regress/unittests/sshbuf/test_sshbuf_fixed.c126
-rw-r--r--regress/unittests/sshbuf/test_sshbuf_fuzz.c127
-rw-r--r--regress/unittests/sshbuf/test_sshbuf_getput_basic.c484
-rw-r--r--regress/unittests/sshbuf/test_sshbuf_getput_crypto.c409
-rw-r--r--regress/unittests/sshbuf/test_sshbuf_getput_fuzz.c130
-rw-r--r--regress/unittests/sshbuf/test_sshbuf_misc.c167
-rw-r--r--regress/unittests/sshbuf/tests.c28
-rw-r--r--regress/unittests/sshkey/Makefile24
-rw-r--r--regress/unittests/sshkey/common.c163
-rw-r--r--regress/unittests/sshkey/common.h25
-rwxr-xr-xregress/unittests/sshkey/mktestdata.sh177
-rw-r--r--regress/unittests/sshkey/test_file.c421
-rw-r--r--regress/unittests/sshkey/test_fuzz.c360
-rw-r--r--regress/unittests/sshkey/test_sshkey.c508
-rw-r--r--regress/unittests/sshkey/testdata/dsa_112
-rw-r--r--regress/unittests/sshkey/testdata/dsa_1-cert.fp1
-rw-r--r--regress/unittests/sshkey/testdata/dsa_1-cert.pub1
-rw-r--r--regress/unittests/sshkey/testdata/dsa_1.fp1
-rw-r--r--regress/unittests/sshkey/testdata/dsa_1.fp.bb1
-rw-r--r--regress/unittests/sshkey/testdata/dsa_1.param.g1
-rw-r--r--regress/unittests/sshkey/testdata/dsa_1.param.priv1
-rw-r--r--regress/unittests/sshkey/testdata/dsa_1.param.pub1
-rw-r--r--regress/unittests/sshkey/testdata/dsa_1.pub1
-rw-r--r--regress/unittests/sshkey/testdata/dsa_1_pw15
-rw-r--r--regress/unittests/sshkey/testdata/dsa_212
-rw-r--r--regress/unittests/sshkey/testdata/dsa_2.fp1
-rw-r--r--regress/unittests/sshkey/testdata/dsa_2.fp.bb1
-rw-r--r--regress/unittests/sshkey/testdata/dsa_2.pub1
-rw-r--r--regress/unittests/sshkey/testdata/dsa_n12
-rw-r--r--regress/unittests/sshkey/testdata/dsa_n_pw21
-rw-r--r--regress/unittests/sshkey/testdata/ecdsa_15
-rw-r--r--regress/unittests/sshkey/testdata/ecdsa_1-cert.fp1
-rw-r--r--regress/unittests/sshkey/testdata/ecdsa_1-cert.pub1
-rw-r--r--regress/unittests/sshkey/testdata/ecdsa_1.fp1
-rw-r--r--regress/unittests/sshkey/testdata/ecdsa_1.fp.bb1
-rw-r--r--regress/unittests/sshkey/testdata/ecdsa_1.param.curve1
-rw-r--r--regress/unittests/sshkey/testdata/ecdsa_1.param.priv1
-rw-r--r--regress/unittests/sshkey/testdata/ecdsa_1.param.pub1
-rw-r--r--regress/unittests/sshkey/testdata/ecdsa_1.pub1
-rw-r--r--regress/unittests/sshkey/testdata/ecdsa_1_pw8
-rw-r--r--regress/unittests/sshkey/testdata/ecdsa_27
-rw-r--r--regress/unittests/sshkey/testdata/ecdsa_2.fp1
-rw-r--r--regress/unittests/sshkey/testdata/ecdsa_2.fp.bb1
-rw-r--r--regress/unittests/sshkey/testdata/ecdsa_2.param.curve1
-rw-r--r--regress/unittests/sshkey/testdata/ecdsa_2.param.priv1
-rw-r--r--regress/unittests/sshkey/testdata/ecdsa_2.param.pub1
-rw-r--r--regress/unittests/sshkey/testdata/ecdsa_2.pub1
-rw-r--r--regress/unittests/sshkey/testdata/ecdsa_n5
-rw-r--r--regress/unittests/sshkey/testdata/ecdsa_n_pw9
-rw-r--r--regress/unittests/sshkey/testdata/ed25519_17
-rw-r--r--regress/unittests/sshkey/testdata/ed25519_1-cert.fp1
-rw-r--r--regress/unittests/sshkey/testdata/ed25519_1-cert.pub1
-rw-r--r--regress/unittests/sshkey/testdata/ed25519_1.fp1
-rw-r--r--regress/unittests/sshkey/testdata/ed25519_1.fp.bb1
-rw-r--r--regress/unittests/sshkey/testdata/ed25519_1.pub1
-rw-r--r--regress/unittests/sshkey/testdata/ed25519_1_pw8
-rw-r--r--regress/unittests/sshkey/testdata/ed25519_27
-rw-r--r--regress/unittests/sshkey/testdata/ed25519_2.fp1
-rw-r--r--regress/unittests/sshkey/testdata/ed25519_2.fp.bb1
-rw-r--r--regress/unittests/sshkey/testdata/ed25519_2.pub1
-rw-r--r--regress/unittests/sshkey/testdata/pw1
-rw-r--r--regress/unittests/sshkey/testdata/rsa1_1bin0 -> 533 bytes
-rw-r--r--regress/unittests/sshkey/testdata/rsa1_1.fp1
-rw-r--r--regress/unittests/sshkey/testdata/rsa1_1.fp.bb1
-rw-r--r--regress/unittests/sshkey/testdata/rsa1_1.param.n1
-rw-r--r--regress/unittests/sshkey/testdata/rsa1_1.pub1
-rw-r--r--regress/unittests/sshkey/testdata/rsa1_1_pwbin0 -> 533 bytes
-rw-r--r--regress/unittests/sshkey/testdata/rsa1_2bin0 -> 981 bytes
-rw-r--r--regress/unittests/sshkey/testdata/rsa1_2.fp1
-rw-r--r--regress/unittests/sshkey/testdata/rsa1_2.fp.bb1
-rw-r--r--regress/unittests/sshkey/testdata/rsa1_2.param.n1
-rw-r--r--regress/unittests/sshkey/testdata/rsa1_2.pub1
-rw-r--r--regress/unittests/sshkey/testdata/rsa_115
-rw-r--r--regress/unittests/sshkey/testdata/rsa_1-cert.fp1
-rw-r--r--regress/unittests/sshkey/testdata/rsa_1-cert.pub1
-rw-r--r--regress/unittests/sshkey/testdata/rsa_1.fp1
-rw-r--r--regress/unittests/sshkey/testdata/rsa_1.fp.bb1
-rw-r--r--regress/unittests/sshkey/testdata/rsa_1.param.n1
-rw-r--r--regress/unittests/sshkey/testdata/rsa_1.param.p1
-rw-r--r--regress/unittests/sshkey/testdata/rsa_1.param.q1
-rw-r--r--regress/unittests/sshkey/testdata/rsa_1.pub1
-rw-r--r--regress/unittests/sshkey/testdata/rsa_1_pw18
-rw-r--r--regress/unittests/sshkey/testdata/rsa_1_sha115
-rw-r--r--regress/unittests/sshkey/testdata/rsa_1_sha1-cert.pub1
-rw-r--r--regress/unittests/sshkey/testdata/rsa_1_sha1.pub1
-rw-r--r--regress/unittests/sshkey/testdata/rsa_1_sha51215
-rw-r--r--regress/unittests/sshkey/testdata/rsa_1_sha512-cert.pub1
-rw-r--r--regress/unittests/sshkey/testdata/rsa_1_sha512.pub1
-rw-r--r--regress/unittests/sshkey/testdata/rsa_227
-rw-r--r--regress/unittests/sshkey/testdata/rsa_2.fp1
-rw-r--r--regress/unittests/sshkey/testdata/rsa_2.fp.bb1
-rw-r--r--regress/unittests/sshkey/testdata/rsa_2.param.n1
-rw-r--r--regress/unittests/sshkey/testdata/rsa_2.param.p1
-rw-r--r--regress/unittests/sshkey/testdata/rsa_2.param.q1
-rw-r--r--regress/unittests/sshkey/testdata/rsa_2.pub1
-rw-r--r--regress/unittests/sshkey/testdata/rsa_n15
-rw-r--r--regress/unittests/sshkey/testdata/rsa_n_pw17
-rw-r--r--regress/unittests/sshkey/tests.c27
-rw-r--r--regress/unittests/test_helper/Makefile15
-rw-r--r--regress/unittests/test_helper/fuzz.c438
-rw-r--r--regress/unittests/test_helper/test_helper.c548
-rw-r--r--regress/unittests/test_helper/test_helper.h320
-rw-r--r--regress/unittests/utf8/Makefile14
-rw-r--r--regress/unittests/utf8/tests.c102
171 files changed, 7743 insertions, 0 deletions
diff --git a/regress/unittests/Makefile b/regress/unittests/Makefile
new file mode 100644
index 0000000..e464b08
--- /dev/null
+++ b/regress/unittests/Makefile
@@ -0,0 +1,7 @@
+# $OpenBSD: Makefile,v 1.10 2018/03/03 03:16:17 djm Exp $
+
+REGRESS_FAIL_EARLY?= yes
+SUBDIR= test_helper sshbuf sshkey bitmap kex hostkeys utf8 match conversion
+SUBDIR+=authopt
+
+.include <bsd.subdir.mk>
diff --git a/regress/unittests/Makefile.inc b/regress/unittests/Makefile.inc
new file mode 100644
index 0000000..b509f44
--- /dev/null
+++ b/regress/unittests/Makefile.inc
@@ -0,0 +1,53 @@
+# $OpenBSD: Makefile.inc,v 1.12 2017/12/21 00:41:22 djm Exp $
+
+.include <bsd.own.mk>
+.include <bsd.obj.mk>
+
+# enable warnings
+WARNINGS=Yes
+
+DEBUG=-g
+CFLAGS+= -fstack-protector-all
+CDIAGFLAGS= -Wall
+CDIAGFLAGS+= -Wextra
+CDIAGFLAGS+= -Werror
+CDIAGFLAGS+= -Wchar-subscripts
+CDIAGFLAGS+= -Wcomment
+CDIAGFLAGS+= -Wformat
+CDIAGFLAGS+= -Wformat-security
+CDIAGFLAGS+= -Wimplicit
+CDIAGFLAGS+= -Winline
+CDIAGFLAGS+= -Wmissing-declarations
+CDIAGFLAGS+= -Wmissing-prototypes
+CDIAGFLAGS+= -Wparentheses
+CDIAGFLAGS+= -Wpointer-arith
+CDIAGFLAGS+= -Wreturn-type
+CDIAGFLAGS+= -Wshadow
+CDIAGFLAGS+= -Wsign-compare
+CDIAGFLAGS+= -Wstrict-aliasing
+CDIAGFLAGS+= -Wstrict-prototypes
+CDIAGFLAGS+= -Wswitch
+CDIAGFLAGS+= -Wtrigraphs
+CDIAGFLAGS+= -Wuninitialized
+CDIAGFLAGS+= -Wunused
+CDIAGFLAGS+= -Wno-unused-parameter
+.if ${COMPILER_VERSION:L} != "gcc3"
+CDIAGFLAGS+= -Wold-style-definition
+.endif
+
+SSHREL=../../../../../usr.bin/ssh
+
+CFLAGS+=-I${.CURDIR}/../test_helper -I${.CURDIR}/${SSHREL}
+
+.if exists(${.CURDIR}/../test_helper/${__objdir})
+LDADD+=-L${.CURDIR}/../test_helper/${__objdir} -ltest_helper
+DPADD+=${.CURDIR}/../test_helper/${__objdir}/libtest_helper.a
+.else
+LDADD+=-L${.CURDIR}/../test_helper -ltest_helper
+DPADD+=${.CURDIR}/../test_helper/libtest_helper.a
+.endif
+
+.PATH: ${.CURDIR}/${SSHREL}
+
+LDADD+= -lcrypto
+DPADD+= ${LIBCRYPTO}
diff --git a/regress/unittests/authopt/testdata/all_permit.cert b/regress/unittests/authopt/testdata/all_permit.cert
new file mode 100644
index 0000000..38ac573
--- /dev/null
+++ b/regress/unittests/authopt/testdata/all_permit.cert
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIOv/h7mJS1WkRHukSvqPwKDiNVrcib/VqBLpbHW6xjWCAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAABAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgKFWCzCzQTh9UkoHphbgwaa86Q16Kern0UjqOr7Q+Jk8AAABTAAAAC3NzaC1lZDI1NTE5AAAAQNe1XDN+J4Eb82TH5J5sYypcabocufjTFRfpU57K+csRP41Yo1FCSEWx95ilUuNvK9Iv3yFDOeVPzdqRqzWoHwE= user key
diff --git a/regress/unittests/authopt/testdata/bad_sourceaddr.cert b/regress/unittests/authopt/testdata/bad_sourceaddr.cert
new file mode 100644
index 0000000..9732745
--- /dev/null
+++ b/regress/unittests/authopt/testdata/bad_sourceaddr.cert
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAILFEJyunlz9scYU3mwbOEJoSSkeO1z20uNBw13tEn+lJAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAABAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAAHwAAAA5zb3VyY2UtYWRkcmVzcwAAAAkAAAAFeHh4eHgAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIChVgsws0E4fVJKB6YW4MGmvOkNeinq59FI6jq+0PiZPAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEA5xY/OEAJ3tgg8/KJqaBR5KMdYYRDiMJ6u4VKS9lQOV1HJQvDDvjj3F5k53BIqTJRVQx242YWs+B3C4db/uLgB user key
diff --git a/regress/unittests/authopt/testdata/force_command.cert b/regress/unittests/authopt/testdata/force_command.cert
new file mode 100644
index 0000000..f7af27e
--- /dev/null
+++ b/regress/unittests/authopt/testdata/force_command.cert
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIJkpCeqaVl6qnp7qa90KehAmHFecx3HW8HZQ22KEqeKBAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAABAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAAHAAAAA1mb3JjZS1jb21tYW5kAAAABwAAAANmb28AAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIChVgsws0E4fVJKB6YW4MGmvOkNeinq59FI6jq+0PiZPAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEAxbhjgbXvfEumRP1E7VH8nUfuJyVlDChhCxiPg9Nvb9PFK8cHdDUEybDCzKCsIDieRc3mtLTyEu7Kb52va/B4C user key
diff --git a/regress/unittests/authopt/testdata/host.cert b/regress/unittests/authopt/testdata/host.cert
new file mode 100644
index 0000000..6326d04
--- /dev/null
+++ b/regress/unittests/authopt/testdata/host.cert
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIFWMw3ftP29RSefnxQwdvK1KiE2G9Y7rPRrJ7ZsrDiOeAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAACAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAAAAAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAoVYLMLNBOH1SSgemFuDBprzpDXop6ufRSOo6vtD4mTwAAAFMAAAALc3NoLWVkMjU1MTkAAABAKTMqwPkaBg23RS7/aj347dc2kY4bWt/sHwzREYSrKRqZ5RNBnSvZOQ8m5euMCEuf92bZ8VJEdF653jRiW6VoBA== user key
diff --git a/regress/unittests/authopt/testdata/mktestdata.sh b/regress/unittests/authopt/testdata/mktestdata.sh
new file mode 100644
index 0000000..06a24e3
--- /dev/null
+++ b/regress/unittests/authopt/testdata/mktestdata.sh
@@ -0,0 +1,48 @@
+#/bin/sh
+
+set -xe
+
+rm -f ca_key ca_key.pub
+rm -f user_key user_key.pub
+rm -f *.cert
+
+ssh-keygen -q -f ca_key -t ed25519 -C CA -N ''
+ssh-keygen -q -f user_key -t ed25519 -C "user key" -N ''
+
+sign() {
+ output=$1
+ shift
+ set -xe
+ ssh-keygen -q -s ca_key -I user -n user \
+ -V 19990101:19991231 -z 1 "$@" user_key.pub
+ mv user_key-cert.pub "$output"
+}
+
+sign all_permit.cert -Opermit-agent-forwarding -Opermit-port-forwarding \
+ -Opermit-pty -Opermit-user-rc -Opermit-X11-forwarding
+sign no_permit.cert -Oclear
+
+sign no_agentfwd.cert -Ono-agent-forwarding
+sign no_portfwd.cert -Ono-port-forwarding
+sign no_pty.cert -Ono-pty
+sign no_user_rc.cert -Ono-user-rc
+sign no_x11fwd.cert -Ono-X11-forwarding
+
+sign only_agentfwd.cert -Oclear -Opermit-agent-forwarding
+sign only_portfwd.cert -Oclear -Opermit-port-forwarding
+sign only_pty.cert -Oclear -Opermit-pty
+sign only_user_rc.cert -Oclear -Opermit-user-rc
+sign only_x11fwd.cert -Oclear -Opermit-X11-forwarding
+
+sign force_command.cert -Oforce-command="foo"
+sign sourceaddr.cert -Osource-address="127.0.0.1/32,::1/128"
+
+# ssh-keygen won't permit generation of certs with invalid source-address
+# values, so we do it as a custom extension.
+sign bad_sourceaddr.cert -Ocritical:source-address=xxxxx
+
+sign unknown_critical.cert -Ocritical:blah=foo
+
+sign host.cert -h
+
+rm -f user_key ca_key user_key.pub ca_key.pub
diff --git a/regress/unittests/authopt/testdata/no_agentfwd.cert b/regress/unittests/authopt/testdata/no_agentfwd.cert
new file mode 100644
index 0000000..bfa5c2e
--- /dev/null
+++ b/regress/unittests/authopt/testdata/no_agentfwd.cert
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIL2qEsLCVtKaBkbCrZicxbPUorcHHrQ8yw5h/26krTOlAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAABAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAAAAAAAGMAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAoVYLMLNBOH1SSgemFuDBprzpDXop6ufRSOo6vtD4mTwAAAFMAAAALc3NoLWVkMjU1MTkAAABAdRhISpol01OwV30g39PM/JD1t35muskX4lyCcGpFQ08GQtBuHE/hABOp6apbGBJIC7CZYYF+uHkD7PfGU3NPAQ== user key
diff --git a/regress/unittests/authopt/testdata/no_permit.cert b/regress/unittests/authopt/testdata/no_permit.cert
new file mode 100644
index 0000000..351e138
--- /dev/null
+++ b/regress/unittests/authopt/testdata/no_permit.cert
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIGVQtVgp9sD4sc8esIhVWbZaM8d0NxpX3UbEVzTHm9feAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAABAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAAAAAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAoVYLMLNBOH1SSgemFuDBprzpDXop6ufRSOo6vtD4mTwAAAFMAAAALc3NoLWVkMjU1MTkAAABAIKlI0TqqraKjYTjIuKhwoxAV/XnzWRJHq8lNs4aj5yDb84un2xXDF/0vXoLjPgVcLgEbksBKKn0i4whp+xn9Ag== user key
diff --git a/regress/unittests/authopt/testdata/no_portfwd.cert b/regress/unittests/authopt/testdata/no_portfwd.cert
new file mode 100644
index 0000000..9457dc3
--- /dev/null
+++ b/regress/unittests/authopt/testdata/no_portfwd.cert
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIE6gC/QjjuzGWVDkr8ZyaHhja80V+lKLC/MvmEFa+CEBAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAABAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAAAAAAAGQAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgKFWCzCzQTh9UkoHphbgwaa86Q16Kern0UjqOr7Q+Jk8AAABTAAAAC3NzaC1lZDI1NTE5AAAAQEzpgckYlfc1BK1ir0reDSXo9OIDx4UoDMrNXrFO6I44NXoJJ4TlUUJH07WcKp/Xp5ESCdyVZtqwgHQxZr0+PwI= user key
diff --git a/regress/unittests/authopt/testdata/no_pty.cert b/regress/unittests/authopt/testdata/no_pty.cert
new file mode 100644
index 0000000..e8154ec
--- /dev/null
+++ b/regress/unittests/authopt/testdata/no_pty.cert
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIFFjhISpSDR3blDejuCf2T9Fe4aHW53jG7KOH2PV/E7jAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAABAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAAAAAAAHAAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgKFWCzCzQTh9UkoHphbgwaa86Q16Kern0UjqOr7Q+Jk8AAABTAAAAC3NzaC1lZDI1NTE5AAAAQF5c4BdxVYgqbMGAep414IGFK4deCFBCeNUTOLpKodrfb1M0gS4d2qoeMxZvMv5yMf/viKl/gallHzEmcrEcIQY= user key
diff --git a/regress/unittests/authopt/testdata/no_user_rc.cert b/regress/unittests/authopt/testdata/no_user_rc.cert
new file mode 100644
index 0000000..6676a0c
--- /dev/null
+++ b/regress/unittests/authopt/testdata/no_user_rc.cert
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIFUM0VLATkYh05QeS5uuhB1X50NMom3jTWeQUmrPQ1FwAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAABAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAAAAAAAGwAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAoVYLMLNBOH1SSgemFuDBprzpDXop6ufRSOo6vtD4mTwAAAFMAAAALc3NoLWVkMjU1MTkAAABAcmJ3c2FCKJL9BCLv1Ij+uN1N+NWZmMXYionsSkv42Go4pMZiH3g8UfTd+OKq9Q7GAcCzGXa///6Dr/wqFssoDA== user key
diff --git a/regress/unittests/authopt/testdata/no_x11fwd.cert b/regress/unittests/authopt/testdata/no_x11fwd.cert
new file mode 100644
index 0000000..0aff9e6
--- /dev/null
+++ b/regress/unittests/authopt/testdata/no_x11fwd.cert
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIPRKPAP+b5S+4zihdgoJrYNcMovFBgKZaJupIhN1kUvkAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAABAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAAAAAAAGUAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIChVgsws0E4fVJKB6YW4MGmvOkNeinq59FI6jq+0PiZPAAAAUwAAAAtzc2gtZWQyNTUxOQAAAECMzj6VDfT+BJmIEo1qUKdr8VDLExF92K7KkbNxTH77n7uip7TL24HDfXjYBCvqxSSn9KAGBhnWsIC/GPx6A+cP user key
diff --git a/regress/unittests/authopt/testdata/only_agentfwd.cert b/regress/unittests/authopt/testdata/only_agentfwd.cert
new file mode 100644
index 0000000..3cf64b0
--- /dev/null
+++ b/regress/unittests/authopt/testdata/only_agentfwd.cert
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIOvJ28yW5uvA7yxE3ySuyFvPjcRYKAr03CYr4okGTNIFAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAABAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAAAAAAAB8AAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgKFWCzCzQTh9UkoHphbgwaa86Q16Kern0UjqOr7Q+Jk8AAABTAAAAC3NzaC1lZDI1NTE5AAAAQEG2uTgmOSk9dJ0s/Ol1EIERXFP9PF6AauF9t5jBMSthNyvSANSrC/1EIaf4TV5kMYfhZxJXoS0XHQjGndcq2AE= user key
diff --git a/regress/unittests/authopt/testdata/only_portfwd.cert b/regress/unittests/authopt/testdata/only_portfwd.cert
new file mode 100644
index 0000000..bb09c3a
--- /dev/null
+++ b/regress/unittests/authopt/testdata/only_portfwd.cert
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIGPoYoExiSyHMyDEvOFgoNZXk5z91u7xq/7357X23TotAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAABAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAAAAAAAB4AAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAoVYLMLNBOH1SSgemFuDBprzpDXop6ufRSOo6vtD4mTwAAAFMAAAALc3NoLWVkMjU1MTkAAABAHN3YnwipcbDKVn+PObGSoaT9rwlau+yrPYZ50oetvCKng3RMjGaV+roqlv0vjjLcxE9J4Y0ti+9MXtQ0D7beBA== user key
diff --git a/regress/unittests/authopt/testdata/only_pty.cert b/regress/unittests/authopt/testdata/only_pty.cert
new file mode 100644
index 0000000..520c89f
--- /dev/null
+++ b/regress/unittests/authopt/testdata/only_pty.cert
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAILvocWYto5Lg7P46YLbe7U4/b2h9Lr5rWqMZ4Cj4ra7RAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAABAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAAAAAAABIAAAAKcGVybWl0LXB0eQAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACAoVYLMLNBOH1SSgemFuDBprzpDXop6ufRSOo6vtD4mTwAAAFMAAAALc3NoLWVkMjU1MTkAAABASv2xQvp+Y6E8dCf5pzg3MZaan5bl1ToYXNcmQ3ysGrk9Djkcu8m3TytDpF471KmUejxy/iF4xjs9CDpk7h+SBQ== user key
diff --git a/regress/unittests/authopt/testdata/only_user_rc.cert b/regress/unittests/authopt/testdata/only_user_rc.cert
new file mode 100644
index 0000000..fb49c35
--- /dev/null
+++ b/regress/unittests/authopt/testdata/only_user_rc.cert
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIJwsRZQ7kx4A8AQ0q/G/3i6sHM48kr4TxJtTcyy3lZAPAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAABAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAAAAAAABYAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgKFWCzCzQTh9UkoHphbgwaa86Q16Kern0UjqOr7Q+Jk8AAABTAAAAC3NzaC1lZDI1NTE5AAAAQDhgEXsvoHr21XrxmiZq/sIjWeYapp11XvEVkkTBPVhBnPwtrrUeJbPmGs3gmJkQdv8BYajYpT7TXEX8GvEeLwU= user key
diff --git a/regress/unittests/authopt/testdata/only_x11fwd.cert b/regress/unittests/authopt/testdata/only_x11fwd.cert
new file mode 100644
index 0000000..6715585
--- /dev/null
+++ b/regress/unittests/authopt/testdata/only_x11fwd.cert
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIDAhZFZBl3eu8Qa8I5BaHCz/mpH8xCjaPusBwo1eJ9OGAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAABAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAAAAAAAB0AAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIChVgsws0E4fVJKB6YW4MGmvOkNeinq59FI6jq+0PiZPAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEDysfgbhniX/zdA8576rrDJpaO2D7QtQse2KWIM9XmREPkLKeP6FKiXKKFcPQiMyV28rptfvK8bBXAiOvITSUgL user key
diff --git a/regress/unittests/authopt/testdata/sourceaddr.cert b/regress/unittests/authopt/testdata/sourceaddr.cert
new file mode 100644
index 0000000..0fcf7b1
--- /dev/null
+++ b/regress/unittests/authopt/testdata/sourceaddr.cert
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIJ54qqoPs87gtjN1aJoLUn7ZTYUtcaGxkzLyJvRkYG7nAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAABAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAALgAAAA5zb3VyY2UtYWRkcmVzcwAAABgAAAAUMTI3LjAuMC4xLzMyLDo6MS8xMjgAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIChVgsws0E4fVJKB6YW4MGmvOkNeinq59FI6jq+0PiZPAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEAppSUKQ/a9tw/HgIazWceCO3d48GU7mkV4iQMpWWs2nB1dFryY1GDtZrBggAjMviwmBXyM3jIk5vxJDINZXGQJ user key
diff --git a/regress/unittests/authopt/testdata/unknown_critical.cert b/regress/unittests/authopt/testdata/unknown_critical.cert
new file mode 100644
index 0000000..216960a
--- /dev/null
+++ b/regress/unittests/authopt/testdata/unknown_critical.cert
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIIjs/wRAB/p5QShSfqoU9cWnCLT3lSveUirk61A27KxVAAAAICeF4LbtRqwIRhewXifa5PKpbSU9P/K8CzeVYj8J/iBoAAAAAAAAAAEAAAABAAAABHVzZXIAAAAIAAAABHVzZXIAAAAANouDYAAAAAA4a2VgAAAAEwAAAARibGFoAAAABwAAAANmb28AAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIChVgsws0E4fVJKB6YW4MGmvOkNeinq59FI6jq+0PiZPAAAAUwAAAAtzc2gtZWQyNTUxOQAAAEDix3FV7JIBuHNAwtZOVIqGBq8lqhnEwP51DqPA43qt+Tzynm56EWxuFzgGehBPF3L8gl+fVqxIJmiQ9iHB0LUD user key
diff --git a/regress/unittests/authopt/tests.c b/regress/unittests/authopt/tests.c
new file mode 100644
index 0000000..0e8aacb
--- /dev/null
+++ b/regress/unittests/authopt/tests.c
@@ -0,0 +1,573 @@
+/* $OpenBSD: tests.c,v 1.1 2018/03/03 03:16:17 djm Exp $ */
+
+/*
+ * Regress test for keys options functions.
+ *
+ * Placed in the public domain
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "test_helper.h"
+
+#include "sshkey.h"
+#include "authfile.h"
+#include "auth-options.h"
+#include "misc.h"
+#include "log.h"
+
+static struct sshkey *
+load_key(const char *name)
+{
+ struct sshkey *ret;
+ int r;
+
+ r = sshkey_load_public(test_data_file(name), &ret, NULL);
+ ASSERT_INT_EQ(r, 0);
+ ASSERT_PTR_NE(ret, NULL);
+ return ret;
+}
+
+static struct sshauthopt *
+default_authkey_opts(void)
+{
+ struct sshauthopt *ret = sshauthopt_new();
+
+ ASSERT_PTR_NE(ret, NULL);
+ ret->permit_port_forwarding_flag = 1;
+ ret->permit_agent_forwarding_flag = 1;
+ ret->permit_x11_forwarding_flag = 1;
+ ret->permit_pty_flag = 1;
+ ret->permit_user_rc = 1;
+ return ret;
+}
+
+static struct sshauthopt *
+default_authkey_restrict_opts(void)
+{
+ struct sshauthopt *ret = sshauthopt_new();
+
+ ASSERT_PTR_NE(ret, NULL);
+ ret->permit_port_forwarding_flag = 0;
+ ret->permit_agent_forwarding_flag = 0;
+ ret->permit_x11_forwarding_flag = 0;
+ ret->permit_pty_flag = 0;
+ ret->permit_user_rc = 0;
+ ret->restricted = 1;
+ return ret;
+}
+
+static char **
+commasplit(const char *s, size_t *np)
+{
+ char *ocp, *cp, *cp2, **ret = NULL;
+ size_t n;
+
+ ocp = cp = strdup(s);
+ ASSERT_PTR_NE(cp, NULL);
+ for (n = 0; (cp2 = strsep(&cp, ",")) != NULL;) {
+ ret = recallocarray(ret, n, n + 1, sizeof(*ret));
+ ASSERT_PTR_NE(ret, NULL);
+ cp2 = strdup(cp2);
+ ASSERT_PTR_NE(cp2, NULL);
+ ret[n++] = cp2;
+ }
+ free(ocp);
+ *np = n;
+ return ret;
+}
+
+static void
+compare_opts(const struct sshauthopt *opts,
+ const struct sshauthopt *expected)
+{
+ size_t i;
+
+ ASSERT_PTR_NE(opts, NULL);
+ ASSERT_PTR_NE(expected, NULL);
+ ASSERT_PTR_NE(expected, opts); /* bozo :) */
+
+#define FLAG_EQ(x) ASSERT_INT_EQ(opts->x, expected->x)
+ FLAG_EQ(permit_port_forwarding_flag);
+ FLAG_EQ(permit_agent_forwarding_flag);
+ FLAG_EQ(permit_x11_forwarding_flag);
+ FLAG_EQ(permit_pty_flag);
+ FLAG_EQ(permit_user_rc);
+ FLAG_EQ(restricted);
+ FLAG_EQ(cert_authority);
+#undef FLAG_EQ
+
+#define STR_EQ(x) \
+ do { \
+ if (expected->x == NULL) \
+ ASSERT_PTR_EQ(opts->x, expected->x); \
+ else \
+ ASSERT_STRING_EQ(opts->x, expected->x); \
+ } while (0)
+ STR_EQ(cert_principals);
+ STR_EQ(force_command);
+ STR_EQ(required_from_host_cert);
+ STR_EQ(required_from_host_keys);
+#undef STR_EQ
+
+#define ARRAY_EQ(nx, x) \
+ do { \
+ ASSERT_SIZE_T_EQ(opts->nx, expected->nx); \
+ if (expected->nx == 0) \
+ break; \
+ for (i = 0; i < expected->nx; i++) \
+ ASSERT_STRING_EQ(opts->x[i], expected->x[i]); \
+ } while (0)
+ ARRAY_EQ(nenv, env);
+ ARRAY_EQ(npermitopen, permitopen);
+#undef ARRAY_EQ
+}
+
+static void
+test_authkeys_parse(void)
+{
+ struct sshauthopt *opts, *expected;
+ const char *errstr;
+
+#define FAIL_TEST(label, keywords) \
+ do { \
+ TEST_START("sshauthopt_parse invalid " label); \
+ opts = sshauthopt_parse(keywords, &errstr); \
+ ASSERT_PTR_EQ(opts, NULL); \
+ ASSERT_PTR_NE(errstr, NULL); \
+ TEST_DONE(); \
+ } while (0)
+#define CHECK_SUCCESS_AND_CLEANUP() \
+ do { \
+ if (errstr != NULL) \
+ ASSERT_STRING_EQ(errstr, ""); \
+ compare_opts(opts, expected); \
+ sshauthopt_free(expected); \
+ sshauthopt_free(opts); \
+ } while (0)
+
+ /* Basic tests */
+ TEST_START("sshauthopt_parse empty");
+ expected = default_authkey_opts();
+ opts = sshauthopt_parse("", &errstr);
+ CHECK_SUCCESS_AND_CLEANUP();
+ TEST_DONE();
+
+ TEST_START("sshauthopt_parse trailing whitespace");
+ expected = default_authkey_opts();
+ opts = sshauthopt_parse(" ", &errstr);
+ CHECK_SUCCESS_AND_CLEANUP();
+ TEST_DONE();
+
+ TEST_START("sshauthopt_parse restrict");
+ expected = default_authkey_restrict_opts();
+ opts = sshauthopt_parse("restrict", &errstr);
+ CHECK_SUCCESS_AND_CLEANUP();
+ TEST_DONE();
+
+ /* Invalid syntax */
+ FAIL_TEST("trailing comma", "restrict,");
+ FAIL_TEST("bare comma", ",");
+ FAIL_TEST("unknown option", "BLAH");
+ FAIL_TEST("unknown option with trailing comma", "BLAH,");
+ FAIL_TEST("unknown option with trailing whitespace", "BLAH ");
+
+ /* force_tun_device */
+ TEST_START("sshauthopt_parse tunnel explicit");
+ expected = default_authkey_opts();
+ expected->force_tun_device = 1;
+ opts = sshauthopt_parse("tunnel=\"1\"", &errstr);
+ CHECK_SUCCESS_AND_CLEANUP();
+ TEST_DONE();
+
+ TEST_START("sshauthopt_parse tunnel any");
+ expected = default_authkey_opts();
+ expected->force_tun_device = SSH_TUNID_ANY;
+ opts = sshauthopt_parse("tunnel=\"any\"", &errstr);
+ CHECK_SUCCESS_AND_CLEANUP();
+ TEST_DONE();
+
+ FAIL_TEST("tunnel", "tunnel=\"blah\"");
+
+ /* Flag options */
+#define FLAG_TEST(keyword, var, val) \
+ do { \
+ TEST_START("sshauthopt_parse " keyword); \
+ expected = default_authkey_opts(); \
+ expected->var = val; \
+ opts = sshauthopt_parse(keyword, &errstr); \
+ CHECK_SUCCESS_AND_CLEANUP(); \
+ expected = default_authkey_restrict_opts(); \
+ expected->var = val; \
+ opts = sshauthopt_parse("restrict,"keyword, &errstr); \
+ CHECK_SUCCESS_AND_CLEANUP(); \
+ TEST_DONE(); \
+ } while (0)
+ /* Positive flags */
+ FLAG_TEST("cert-authority", cert_authority, 1);
+ FLAG_TEST("port-forwarding", permit_port_forwarding_flag, 1);
+ FLAG_TEST("agent-forwarding", permit_agent_forwarding_flag, 1);
+ FLAG_TEST("x11-forwarding", permit_x11_forwarding_flag, 1);
+ FLAG_TEST("pty", permit_pty_flag, 1);
+ FLAG_TEST("user-rc", permit_user_rc, 1);
+ /* Negative flags */
+ FLAG_TEST("no-port-forwarding", permit_port_forwarding_flag, 0);
+ FLAG_TEST("no-agent-forwarding", permit_agent_forwarding_flag, 0);
+ FLAG_TEST("no-x11-forwarding", permit_x11_forwarding_flag, 0);
+ FLAG_TEST("no-pty", permit_pty_flag, 0);
+ FLAG_TEST("no-user-rc", permit_user_rc, 0);
+#undef FLAG_TEST
+ FAIL_TEST("no-cert-authority", "no-cert-authority");
+
+ /* String options */
+#define STRING_TEST(keyword, var, val) \
+ do { \
+ TEST_START("sshauthopt_parse " keyword); \
+ expected = default_authkey_opts(); \
+ expected->var = strdup(val); \
+ ASSERT_PTR_NE(expected->var, NULL); \
+ opts = sshauthopt_parse(keyword "=" #val, &errstr); \
+ CHECK_SUCCESS_AND_CLEANUP(); \
+ expected = default_authkey_restrict_opts(); \
+ expected->var = strdup(val); \
+ ASSERT_PTR_NE(expected->var, NULL); \
+ opts = sshauthopt_parse( \
+ "restrict," keyword "=" #val ",restrict", &errstr); \
+ CHECK_SUCCESS_AND_CLEANUP(); \
+ TEST_DONE(); \
+ } while (0)
+ STRING_TEST("command", force_command, "/bin/true");
+ STRING_TEST("principals", cert_principals, "gregor,josef,K");
+ STRING_TEST("from", required_from_host_keys, "127.0.0.0/8");
+#undef STRING_TEST
+ FAIL_TEST("unquoted command", "command=oops");
+ FAIL_TEST("unquoted principals", "principals=estragon");
+ FAIL_TEST("unquoted from", "from=127.0.0.1");
+
+ /* String array option tests */
+#define ARRAY_TEST(label, keywords, var, nvar, val) \
+ do { \
+ TEST_START("sshauthopt_parse " label); \
+ expected = default_authkey_opts(); \
+ expected->var = commasplit(val, &expected->nvar); \
+ ASSERT_PTR_NE(expected->var, NULL); \
+ opts = sshauthopt_parse(keywords, &errstr); \
+ CHECK_SUCCESS_AND_CLEANUP(); \
+ expected = default_authkey_restrict_opts(); \
+ expected->var = commasplit(val, &expected->nvar); \
+ ASSERT_PTR_NE(expected->var, NULL); \
+ opts = sshauthopt_parse( \
+ "restrict," keywords ",restrict", &errstr); \
+ CHECK_SUCCESS_AND_CLEANUP(); \
+ TEST_DONE(); \
+ } while (0)
+ ARRAY_TEST("environment", "environment=\"foo=1\",environment=\"bar=2\"",
+ env, nenv, "foo=1,bar=2");
+ ARRAY_TEST("permitopen", "permitopen=\"foo:123\",permitopen=\"bar:*\"",
+ permitopen, npermitopen, "foo:123,bar:*");
+#undef ARRAY_TEST
+ FAIL_TEST("environment", "environment=\",=bah\"");
+ FAIL_TEST("permitopen port", "foo:bar");
+ FAIL_TEST("permitopen missing port", "foo:");
+ FAIL_TEST("permitopen missing port specification", "foo");
+ FAIL_TEST("permitopen invalid host", "[:");
+
+#undef CHECK_SUCCESS_AND_CLEANUP
+#undef FAIL_TEST
+}
+
+static void
+test_cert_parse(void)
+{
+ struct sshkey *cert;
+ struct sshauthopt *opts, *expected;
+
+#define CHECK_SUCCESS_AND_CLEANUP() \
+ do { \
+ compare_opts(opts, expected); \
+ sshauthopt_free(expected); \
+ sshauthopt_free(opts); \
+ sshkey_free(cert); \
+ } while (0)
+#define FLAG_TEST(keybase, var) \
+ do { \
+ TEST_START("sshauthopt_from_cert no_" keybase); \
+ cert = load_key("no_" keybase ".cert"); \
+ expected = default_authkey_opts(); \
+ expected->var = 0; \
+ opts = sshauthopt_from_cert(cert); \
+ CHECK_SUCCESS_AND_CLEANUP(); \
+ TEST_DONE(); \
+ TEST_START("sshauthopt_from_cert only_" keybase); \
+ cert = load_key("only_" keybase ".cert"); \
+ expected = sshauthopt_new(); \
+ ASSERT_PTR_NE(expected, NULL); \
+ expected->var = 1; \
+ opts = sshauthopt_from_cert(cert); \
+ CHECK_SUCCESS_AND_CLEANUP(); \
+ TEST_DONE(); \
+ } while (0)
+ FLAG_TEST("agentfwd", permit_agent_forwarding_flag);
+ FLAG_TEST("portfwd", permit_port_forwarding_flag);
+ FLAG_TEST("pty", permit_pty_flag);
+ FLAG_TEST("user_rc", permit_user_rc);
+ FLAG_TEST("x11fwd", permit_x11_forwarding_flag);
+#undef FLAG_TEST
+
+ TEST_START("sshauthopt_from_cert all permitted");
+ cert = load_key("all_permit.cert");
+ expected = default_authkey_opts();
+ opts = sshauthopt_from_cert(cert);
+ CHECK_SUCCESS_AND_CLEANUP();
+ TEST_DONE();
+
+ TEST_START("sshauthopt_from_cert nothing permitted");
+ cert = load_key("no_permit.cert");
+ expected = sshauthopt_new();
+ ASSERT_PTR_NE(expected, NULL);
+ opts = sshauthopt_from_cert(cert);
+ CHECK_SUCCESS_AND_CLEANUP();
+ TEST_DONE();
+
+ TEST_START("sshauthopt_from_cert force-command");
+ cert = load_key("force_command.cert");
+ expected = default_authkey_opts();
+ expected->force_command = strdup("foo");
+ ASSERT_PTR_NE(expected->force_command, NULL);
+ opts = sshauthopt_from_cert(cert);
+ CHECK_SUCCESS_AND_CLEANUP();
+ TEST_DONE();
+
+ TEST_START("sshauthopt_from_cert source-address");
+ cert = load_key("sourceaddr.cert");
+ expected = default_authkey_opts();
+ expected->required_from_host_cert = strdup("127.0.0.1/32,::1/128");
+ ASSERT_PTR_NE(expected->required_from_host_cert, NULL);
+ opts = sshauthopt_from_cert(cert);
+ CHECK_SUCCESS_AND_CLEANUP();
+ TEST_DONE();
+#undef CHECK_SUCCESS_AND_CLEANUP
+
+#define FAIL_TEST(keybase) \
+ do { \
+ TEST_START("sshauthopt_from_cert " keybase); \
+ cert = load_key(keybase ".cert"); \
+ opts = sshauthopt_from_cert(cert); \
+ ASSERT_PTR_EQ(opts, NULL); \
+ sshkey_free(cert); \
+ TEST_DONE(); \
+ } while (0)
+ FAIL_TEST("host");
+ FAIL_TEST("bad_sourceaddr");
+ FAIL_TEST("unknown_critical");
+#undef FAIL_TEST
+}
+
+static void
+test_merge(void)
+{
+ struct sshkey *cert;
+ struct sshauthopt *key_opts, *cert_opts, *merge_opts, *expected;
+ const char *errstr;
+
+ /*
+ * Prepare for a test by making some key and cert options and
+ * attempting to merge them.
+ */
+#define PREPARE(label, keyname, keywords) \
+ do { \
+ expected = NULL; \
+ TEST_START("sshauthopt_merge " label); \
+ cert = load_key(keyname ".cert"); \
+ cert_opts = sshauthopt_from_cert(cert); \
+ ASSERT_PTR_NE(cert_opts, NULL); \
+ key_opts = sshauthopt_parse(keywords, &errstr); \
+ if (errstr != NULL) \
+ ASSERT_STRING_EQ(errstr, ""); \
+ ASSERT_PTR_NE(key_opts, NULL); \
+ merge_opts = sshauthopt_merge(key_opts, \
+ cert_opts, &errstr); \
+ } while (0)
+
+ /* Cleanup stuff allocated by PREPARE() */
+#define CLEANUP() \
+ do { \
+ sshauthopt_free(expected); \
+ sshauthopt_free(merge_opts); \
+ sshauthopt_free(key_opts); \
+ sshauthopt_free(cert_opts); \
+ sshkey_free(cert); \
+ } while (0)
+
+ /* Check the results of PREPARE() against expectation; calls CLEANUP */
+#define CHECK_SUCCESS_AND_CLEANUP() \
+ do { \
+ if (errstr != NULL) \
+ ASSERT_STRING_EQ(errstr, ""); \
+ compare_opts(merge_opts, expected); \
+ CLEANUP(); \
+ } while (0)
+
+ /* Check a single case of merging of flag options */
+#define FLAG_CASE(keybase, label, keyname, keywords, mostly_off, var, val) \
+ do { \
+ PREPARE(keybase " " label, keyname, keywords); \
+ expected = mostly_off ? \
+ sshauthopt_new() : default_authkey_opts(); \
+ expected->var = val; \
+ ASSERT_PTR_NE(expected, NULL); \
+ CHECK_SUCCESS_AND_CLEANUP(); \
+ TEST_DONE(); \
+ } while (0)
+
+ /*
+ * Fairly exhaustive exercise of a flag option. Tests
+ * option both set and clear in certificate, set and clear in
+ * authorized_keys and set and cleared via restrict keyword.
+ */
+#define FLAG_TEST(keybase, keyword, var) \
+ do { \
+ FLAG_CASE(keybase, "keys:default,yes cert:default,no", \
+ "no_" keybase, keyword, 0, var, 0); \
+ FLAG_CASE(keybase,"keys:-*,yes cert:default,no", \
+ "no_" keybase, "restrict," keyword, 1, var, 0); \
+ FLAG_CASE(keybase, "keys:default,no cert:default,no", \
+ "no_" keybase, "no-" keyword, 0, var, 0); \
+ FLAG_CASE(keybase, "keys:-*,no cert:default,no", \
+ "no_" keybase, "restrict,no-" keyword, 1, var, 0); \
+ \
+ FLAG_CASE(keybase, "keys:default,yes cert:-*,yes", \
+ "only_" keybase, keyword, 1, var, 1); \
+ FLAG_CASE(keybase,"keys:-*,yes cert:-*,yes", \
+ "only_" keybase, "restrict," keyword, 1, var, 1); \
+ FLAG_CASE(keybase, "keys:default,no cert:-*,yes", \
+ "only_" keybase, "no-" keyword, 1, var, 0); \
+ FLAG_CASE(keybase, "keys:-*,no cert:-*,yes", \
+ "only_" keybase, "restrict,no-" keyword, 1, var, 0); \
+ \
+ FLAG_CASE(keybase, "keys:default,yes cert:-*", \
+ "no_permit", keyword, 1, var, 0); \
+ FLAG_CASE(keybase,"keys:-*,yes cert:-*", \
+ "no_permit", "restrict," keyword, 1, var, 0); \
+ FLAG_CASE(keybase, "keys:default,no cert:-*", \
+ "no_permit", "no-" keyword, 1, var, 0); \
+ FLAG_CASE(keybase, "keys:-*,no cert:-*", \
+ "no_permit", "restrict,no-" keyword, 1, var, 0); \
+ \
+ FLAG_CASE(keybase, "keys:default,yes cert:*", \
+ "all_permit", keyword, 0, var, 1); \
+ FLAG_CASE(keybase,"keys:-*,yes cert:*", \
+ "all_permit", "restrict," keyword, 1, var, 1); \
+ FLAG_CASE(keybase, "keys:default,no cert:*", \
+ "all_permit", "no-" keyword, 0, var, 0); \
+ FLAG_CASE(keybase, "keys:-*,no cert:*", \
+ "all_permit", "restrict,no-" keyword, 1, var, 0); \
+ \
+ } while (0)
+ FLAG_TEST("portfwd", "port-forwarding", permit_port_forwarding_flag);
+ FLAG_TEST("agentfwd", "agent-forwarding", permit_agent_forwarding_flag);
+ FLAG_TEST("pty", "pty", permit_pty_flag);
+ FLAG_TEST("user_rc", "user-rc", permit_user_rc);
+ FLAG_TEST("x11fwd", "x11-forwarding", permit_x11_forwarding_flag);
+#undef FLAG_TEST
+
+ PREPARE("source-address both", "sourceaddr", "from=\"127.0.0.1\"");
+ expected = default_authkey_opts();
+ expected->required_from_host_cert = strdup("127.0.0.1/32,::1/128");
+ ASSERT_PTR_NE(expected->required_from_host_cert, NULL);
+ expected->required_from_host_keys = strdup("127.0.0.1");
+ ASSERT_PTR_NE(expected->required_from_host_keys, NULL);
+ CHECK_SUCCESS_AND_CLEANUP();
+ TEST_DONE();
+
+ PREPARE("source-address none", "all_permit", "");
+ expected = default_authkey_opts();
+ CHECK_SUCCESS_AND_CLEANUP();
+ TEST_DONE();
+
+ PREPARE("source-address keys", "all_permit", "from=\"127.0.0.1\"");
+ expected = default_authkey_opts();
+ expected->required_from_host_keys = strdup("127.0.0.1");
+ ASSERT_PTR_NE(expected->required_from_host_keys, NULL);
+ CHECK_SUCCESS_AND_CLEANUP();
+ TEST_DONE();
+
+ PREPARE("source-address cert", "sourceaddr", "");
+ expected = default_authkey_opts();
+ expected->required_from_host_cert = strdup("127.0.0.1/32,::1/128");
+ ASSERT_PTR_NE(expected->required_from_host_cert, NULL);
+ CHECK_SUCCESS_AND_CLEANUP();
+ TEST_DONE();
+
+ PREPARE("force-command both", "force_command", "command=\"foo\"");
+ expected = default_authkey_opts();
+ expected->force_command = strdup("foo");
+ ASSERT_PTR_NE(expected->force_command, NULL);
+ CHECK_SUCCESS_AND_CLEANUP();
+ TEST_DONE();
+
+ PREPARE("force-command none", "all_permit", "");
+ expected = default_authkey_opts();
+ CHECK_SUCCESS_AND_CLEANUP();
+ TEST_DONE();
+
+ PREPARE("force-command keys", "all_permit", "command=\"bar\"");
+ expected = default_authkey_opts();
+ expected->force_command = strdup("bar");
+ ASSERT_PTR_NE(expected->force_command, NULL);
+ CHECK_SUCCESS_AND_CLEANUP();
+ TEST_DONE();
+
+ PREPARE("force-command cert", "force_command", "");
+ expected = default_authkey_opts();
+ expected->force_command = strdup("foo");
+ ASSERT_PTR_NE(expected->force_command, NULL);
+ CHECK_SUCCESS_AND_CLEANUP();
+ TEST_DONE();
+
+ PREPARE("force-command mismatch", "force_command", "command=\"bar\"");
+ ASSERT_PTR_EQ(merge_opts, NULL);
+ CLEANUP();
+ TEST_DONE();
+
+ PREPARE("tunnel", "all_permit", "tunnel=\"6\"");
+ expected = default_authkey_opts();
+ expected->force_tun_device = 6;
+ CHECK_SUCCESS_AND_CLEANUP();
+ TEST_DONE();
+
+ PREPARE("permitopen", "all_permit",
+ "permitopen=\"127.0.0.1:*\",permitopen=\"127.0.0.1:123\"");
+ expected = default_authkey_opts();
+ expected->permitopen = commasplit("127.0.0.1:*,127.0.0.1:123",
+ &expected->npermitopen);
+ CHECK_SUCCESS_AND_CLEANUP();
+ TEST_DONE();
+
+ PREPARE("environment", "all_permit",
+ "environment=\"foo=a\",environment=\"bar=b\"");
+ expected = default_authkey_opts();
+ expected->env = commasplit("foo=a,bar=b", &expected->nenv);
+ CHECK_SUCCESS_AND_CLEANUP();
+ TEST_DONE();
+}
+
+void
+tests(void)
+{
+ extern char *__progname;
+ LogLevel ll = test_is_verbose() ?
+ SYSLOG_LEVEL_DEBUG3 : SYSLOG_LEVEL_QUIET;
+
+ /* test_cert_parse() are a bit spammy to error() by default... */
+ log_init(__progname, ll, SYSLOG_FACILITY_USER, 1);
+
+ test_authkeys_parse();
+ test_cert_parse();
+ test_merge();
+}
diff --git a/regress/unittests/bitmap/Makefile b/regress/unittests/bitmap/Makefile
new file mode 100644
index 0000000..fe30acc
--- /dev/null
+++ b/regress/unittests/bitmap/Makefile
@@ -0,0 +1,14 @@
+# $OpenBSD: Makefile,v 1.4 2017/12/21 00:41:22 djm Exp $
+
+PROG=test_bitmap
+SRCS=tests.c
+
+# From usr.sbin/ssh
+SRCS+=bitmap.c atomicio.c
+
+REGRESS_TARGETS=run-regress-${PROG}
+
+run-regress-${PROG}: ${PROG}
+ env ${TEST_ENV} ./${PROG}
+
+.include <bsd.regress.mk>
diff --git a/regress/unittests/bitmap/tests.c b/regress/unittests/bitmap/tests.c
new file mode 100644
index 0000000..23025f9
--- /dev/null
+++ b/regress/unittests/bitmap/tests.c
@@ -0,0 +1,135 @@
+/* $OpenBSD: tests.c,v 1.1 2015/01/15 07:36:28 djm Exp $ */
+/*
+ * Regress test for bitmap.h bitmap API
+ *
+ * Placed in the public domain
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <stdio.h>
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+
+#include <openssl/bn.h>
+
+#include "../test_helper/test_helper.h"
+
+#include "bitmap.h"
+
+#define NTESTS 131
+
+void
+tests(void)
+{
+ struct bitmap *b;
+ BIGNUM *bn;
+ size_t len;
+ int i, j, k, n;
+ u_char bbuf[1024], bnbuf[1024];
+ int r;
+
+ TEST_START("bitmap_new");
+ b = bitmap_new();
+ ASSERT_PTR_NE(b, NULL);
+ bn = BN_new();
+ ASSERT_PTR_NE(bn, NULL);
+ TEST_DONE();
+
+ TEST_START("bitmap_set_bit / bitmap_test_bit");
+ for (i = -1; i < NTESTS; i++) {
+ for (j = -1; j < NTESTS; j++) {
+ for (k = -1; k < NTESTS; k++) {
+ bitmap_zero(b);
+ BN_clear(bn);
+
+ test_subtest_info("set %d/%d/%d", i, j, k);
+ /* Set bits */
+ if (i >= 0) {
+ ASSERT_INT_EQ(bitmap_set_bit(b, i), 0);
+ ASSERT_INT_EQ(BN_set_bit(bn, i), 1);
+ }
+ if (j >= 0) {
+ ASSERT_INT_EQ(bitmap_set_bit(b, j), 0);
+ ASSERT_INT_EQ(BN_set_bit(bn, j), 1);
+ }
+ if (k >= 0) {
+ ASSERT_INT_EQ(bitmap_set_bit(b, k), 0);
+ ASSERT_INT_EQ(BN_set_bit(bn, k), 1);
+ }
+
+ /* Check perfect match between bitmap and bn */
+ test_subtest_info("match %d/%d/%d", i, j, k);
+ for (n = 0; n < NTESTS; n++) {
+ ASSERT_INT_EQ(BN_is_bit_set(bn, n),
+ bitmap_test_bit(b, n));
+ }
+
+ /* Test length calculations */
+ test_subtest_info("length %d/%d/%d", i, j, k);
+ ASSERT_INT_EQ(BN_num_bits(bn),
+ (int)bitmap_nbits(b));
+ ASSERT_INT_EQ(BN_num_bytes(bn),
+ (int)bitmap_nbytes(b));
+
+ /* Test serialisation */
+ test_subtest_info("serialise %d/%d/%d",
+ i, j, k);
+ len = bitmap_nbytes(b);
+ memset(bbuf, 0xfc, sizeof(bbuf));
+ ASSERT_INT_EQ(bitmap_to_string(b, bbuf,
+ sizeof(bbuf)), 0);
+ for (n = len; n < (int)sizeof(bbuf); n++)
+ ASSERT_U8_EQ(bbuf[n], 0xfc);
+ r = BN_bn2bin(bn, bnbuf);
+ ASSERT_INT_GE(r, 0);
+ ASSERT_INT_EQ(r, (int)len);
+ ASSERT_MEM_EQ(bbuf, bnbuf, len);
+
+ /* Test deserialisation */
+ test_subtest_info("deserialise %d/%d/%d",
+ i, j, k);
+ bitmap_zero(b);
+ ASSERT_INT_EQ(bitmap_from_string(b, bnbuf,
+ len), 0);
+ for (n = 0; n < NTESTS; n++) {
+ ASSERT_INT_EQ(BN_is_bit_set(bn, n),
+ bitmap_test_bit(b, n));
+ }
+
+ /* Test clearing bits */
+ test_subtest_info("clear %d/%d/%d",
+ i, j, k);
+ for (n = 0; n < NTESTS; n++) {
+ ASSERT_INT_EQ(bitmap_set_bit(b, n), 0);
+ ASSERT_INT_EQ(BN_set_bit(bn, n), 1);
+ }
+ if (i >= 0) {
+ bitmap_clear_bit(b, i);
+ BN_clear_bit(bn, i);
+ }
+ if (j >= 0) {
+ bitmap_clear_bit(b, j);
+ BN_clear_bit(bn, j);
+ }
+ if (k >= 0) {
+ bitmap_clear_bit(b, k);
+ BN_clear_bit(bn, k);
+ }
+ for (n = 0; n < NTESTS; n++) {
+ ASSERT_INT_EQ(BN_is_bit_set(bn, n),
+ bitmap_test_bit(b, n));
+ }
+ }
+ }
+ }
+ bitmap_free(b);
+ BN_free(bn);
+ TEST_DONE();
+}
+
diff --git a/regress/unittests/conversion/Makefile b/regress/unittests/conversion/Makefile
new file mode 100644
index 0000000..8b2a09c
--- /dev/null
+++ b/regress/unittests/conversion/Makefile
@@ -0,0 +1,15 @@
+# $OpenBSD: Makefile,v 1.2 2017/12/21 00:41:22 djm Exp $
+
+PROG=test_conversion
+SRCS=tests.c
+
+# From usr.bin/ssh
+SRCS+=sshbuf-getput-basic.c sshbuf-getput-crypto.c sshbuf-misc.c sshbuf.c
+SRCS+=atomicio.c misc.c xmalloc.c log.c uidswap.c cleanup.c fatal.c ssherr.c
+
+REGRESS_TARGETS=run-regress-${PROG}
+
+run-regress-${PROG}: ${PROG}
+ env ${TEST_ENV} ./${PROG}
+
+.include <bsd.regress.mk>
diff --git a/regress/unittests/conversion/tests.c b/regress/unittests/conversion/tests.c
new file mode 100644
index 0000000..6dd77ef
--- /dev/null
+++ b/regress/unittests/conversion/tests.c
@@ -0,0 +1,51 @@
+/* $OpenBSD: tests.c,v 1.1 2017/03/14 01:20:29 dtucker Exp $ */
+/*
+ * Regress test for conversions
+ *
+ * Placed in the public domain
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <stdio.h>
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+
+#include "../test_helper/test_helper.h"
+
+#include "misc.h"
+
+void
+tests(void)
+{
+ char buf[1024];
+
+ TEST_START("conversion_convtime");
+ ASSERT_LONG_EQ(convtime("0"), 0);
+ ASSERT_LONG_EQ(convtime("1"), 1);
+ ASSERT_LONG_EQ(convtime("1S"), 1);
+ /* from the examples in the comment above the function */
+ ASSERT_LONG_EQ(convtime("90m"), 5400);
+ ASSERT_LONG_EQ(convtime("1h30m"), 5400);
+ ASSERT_LONG_EQ(convtime("2d"), 172800);
+ ASSERT_LONG_EQ(convtime("1w"), 604800);
+
+ /* negative time is not allowed */
+ ASSERT_LONG_EQ(convtime("-7"), -1);
+ ASSERT_LONG_EQ(convtime("-9d"), -1);
+
+ /* overflow */
+ snprintf(buf, sizeof buf, "%llu", (unsigned long long)LONG_MAX + 1);
+ ASSERT_LONG_EQ(convtime(buf), -1);
+
+ /* overflow with multiplier */
+ snprintf(buf, sizeof buf, "%lluM", (unsigned long long)LONG_MAX/60 + 1);
+ ASSERT_LONG_EQ(convtime(buf), -1);
+ ASSERT_LONG_EQ(convtime("1000000000000000000000w"), -1);
+ TEST_DONE();
+}
diff --git a/regress/unittests/hostkeys/Makefile b/regress/unittests/hostkeys/Makefile
new file mode 100644
index 0000000..3368851
--- /dev/null
+++ b/regress/unittests/hostkeys/Makefile
@@ -0,0 +1,23 @@
+# $OpenBSD: Makefile,v 1.4 2017/12/21 00:41:22 djm Exp $
+
+PROG=test_hostkeys
+SRCS=tests.c test_iterate.c
+
+# From usr.bin/ssh
+SRCS+=sshbuf-getput-basic.c sshbuf-getput-crypto.c sshbuf-misc.c sshbuf.c
+SRCS+=atomicio.c sshkey.c authfile.c cipher.c log.c ssh-rsa.c ssh-dss.c
+SRCS+=ssh-ecdsa.c ssh-ed25519.c mac.c umac.c umac128.c hmac.c misc.c
+SRCS+=ssherr.c uidswap.c cleanup.c xmalloc.c match.c krl.c fatal.c
+SRCS+=addrmatch.c bitmap.c hostfile.c
+SRCS+=ed25519.c hash.c ge25519.c fe25519.c sc25519.c verify.c
+SRCS+=cipher-chachapoly.c chacha.c poly1305.c
+
+SRCS+=digest-openssl.c
+#SRCS+=digest-libc.c
+
+REGRESS_TARGETS=run-regress-${PROG}
+
+run-regress-${PROG}: ${PROG}
+ env ${TEST_ENV} ./${PROG} -d ${.CURDIR}/testdata
+
+.include <bsd.regress.mk>
diff --git a/regress/unittests/hostkeys/mktestdata.sh b/regress/unittests/hostkeys/mktestdata.sh
new file mode 100644
index 0000000..5a46de9
--- /dev/null
+++ b/regress/unittests/hostkeys/mktestdata.sh
@@ -0,0 +1,86 @@
+#!/bin/sh
+# $OpenBSD: mktestdata.sh,v 1.2 2017/04/30 23:33:48 djm Exp $
+
+set -ex
+
+cd testdata
+
+rm -f rsa* dsa* ecdsa* ed25519*
+rm -f known_hosts*
+
+gen_all() {
+ _n=$1
+ _ecdsa_bits=256
+ test "x$_n" = "x1" && _ecdsa_bits=384
+ test "x$_n" = "x2" && _ecdsa_bits=521
+ ssh-keygen -qt rsa -b 1024 -C "RSA #$_n" -N "" -f rsa_$_n
+ ssh-keygen -qt dsa -b 1024 -C "DSA #$_n" -N "" -f dsa_$_n
+ ssh-keygen -qt ecdsa -b $_ecdsa_bits -C "ECDSA #$_n" -N "" -f ecdsa_$_n
+ ssh-keygen -qt ed25519 -C "ED25519 #$_n" -N "" -f ed25519_$_n
+ # Don't need private keys
+ rm -f rsa_$_n dsa_$_n ecdsa_$_n ed25519_$_n
+}
+
+hentries() {
+ _preamble=$1
+ _kspec=$2
+ for k in `ls -1 $_kspec | sort` ; do
+ printf "$_preamble "
+ cat $k
+ done
+ echo
+}
+
+gen_all 1
+gen_all 2
+gen_all 3
+gen_all 4
+gen_all 5
+gen_all 6
+
+# A section of known_hosts with hashed hostnames.
+(
+ hentries "sisyphus.example.com" "*_5.pub"
+ hentries "prometheus.example.com,192.0.2.1,2001:db8::1" "*_6.pub"
+) > known_hosts_hash_frag
+ssh-keygen -Hf known_hosts_hash_frag
+rm -f known_hosts_hash_frag.old
+
+# Populated known_hosts, including comments, hashed names and invalid lines
+(
+ echo "# Plain host keys, plain host names"
+ hentries "sisyphus.example.com" "*_1.pub"
+
+ echo "# Plain host keys, hostnames + addresses"
+ hentries "prometheus.example.com,192.0.2.1,2001:db8::1" "*_2.pub"
+
+ echo "# Some hosts with wildcard names / IPs"
+ hentries "*.example.com,192.0.2.*,2001:*" "*_3.pub"
+
+ echo "# Hashed hostname and address entries"
+ cat known_hosts_hash_frag
+ rm -f known_hosts_hash_frag
+ echo
+
+ echo "# Revoked and CA keys"
+ printf "@revoked sisyphus.example.com " ; cat ed25519_4.pub
+ printf "@cert-authority prometheus.example.com " ; cat ecdsa_4.pub
+ printf "@cert-authority *.example.com " ; cat dsa_4.pub
+
+ printf "\n"
+ echo "# Some invalid lines"
+ # Invalid marker
+ printf "@what sisyphus.example.com " ; cat dsa_1.pub
+ # Key missing
+ echo "sisyphus.example.com "
+ # Key blob missing
+ echo "prometheus.example.com ssh-ed25519 "
+ # Key blob truncated
+ echo "sisyphus.example.com ssh-dsa AAAATgAAAAdz"
+ # Invalid type
+ echo "sisyphus.example.com ssh-XXX AAAATgAAAAdzc2gtWFhYAAAAP0ZVQ0tPRkZGVUNLT0ZGRlVDS09GRkZVQ0tPRkZGVUNLT0ZGRlVDS09GRkZVQ0tPRkZGVUNLT0ZGRlVDS09GRg=="
+ # Type mismatch with blob
+ echo "prometheus.example.com ssh-rsa AAAATgAAAAdzc2gtWFhYAAAAP0ZVQ0tPRkZGVUNLT0ZGRlVDS09GRkZVQ0tPRkZGVUNLT0ZGRlVDS09GRkZVQ0tPRkZGVUNLT0ZGRlVDS09GRg=="
+) > known_hosts
+
+echo OK
diff --git a/regress/unittests/hostkeys/test_iterate.c b/regress/unittests/hostkeys/test_iterate.c
new file mode 100644
index 0000000..d6963bd
--- /dev/null
+++ b/regress/unittests/hostkeys/test_iterate.c
@@ -0,0 +1,1047 @@
+/* $OpenBSD: test_iterate.c,v 1.6 2018/07/16 03:09:59 djm Exp $ */
+/*
+ * Regress test for hostfile.h hostkeys_foreach()
+ *
+ * Placed in the public domain
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <stdio.h>
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+
+#include "../test_helper/test_helper.h"
+
+#include "sshkey.h"
+#include "authfile.h"
+#include "hostfile.h"
+
+struct expected {
+ const char *key_file; /* Path for key, NULL for none */
+ int no_parse_status; /* Expected status w/o key parsing */
+ int no_parse_keytype; /* Expected keytype w/o key parsing */
+ int match_host_p; /* Match 'prometheus.example.com' */
+ int match_host_s; /* Match 'sisyphus.example.com' */
+ int match_ipv4; /* Match '192.0.2.1' */
+ int match_ipv6; /* Match '2001:db8::1' */
+ int match_flags; /* Expected flags from match */
+ struct hostkey_foreach_line l; /* Expected line contents */
+};
+
+struct cbctx {
+ const struct expected *expected;
+ size_t nexpected;
+ size_t i;
+ int flags;
+ int match_host_p;
+ int match_host_s;
+ int match_ipv4;
+ int match_ipv6;
+};
+
+/*
+ * hostkeys_foreach() iterator callback that verifies the line passed
+ * against an array of expected entries.
+ */
+static int
+check(struct hostkey_foreach_line *l, void *_ctx)
+{
+ struct cbctx *ctx = (struct cbctx *)_ctx;
+ const struct expected *expected;
+ int parse_key = (ctx->flags & HKF_WANT_PARSE_KEY) != 0;
+ const int matching = (ctx->flags & HKF_WANT_MATCH) != 0;
+ u_int expected_status, expected_match;
+ int expected_keytype;
+
+ test_subtest_info("entry %zu/%zu, file line %ld",
+ ctx->i + 1, ctx->nexpected, l->linenum);
+
+ for (;;) {
+ ASSERT_SIZE_T_LT(ctx->i, ctx->nexpected);
+ expected = ctx->expected + ctx->i++;
+ /* If we are matching host/IP then skip entries that don't */
+ if (!matching)
+ break;
+ if (ctx->match_host_p && expected->match_host_p)
+ break;
+ if (ctx->match_host_s && expected->match_host_s)
+ break;
+ if (ctx->match_ipv4 && expected->match_ipv4)
+ break;
+ if (ctx->match_ipv6 && expected->match_ipv6)
+ break;
+ }
+ expected_status = (parse_key || expected->no_parse_status < 0) ?
+ expected->l.status : (u_int)expected->no_parse_status;
+ expected_match = expected->l.match;
+#define UPDATE_MATCH_STATUS(x) do { \
+ if (ctx->x && expected->x) { \
+ expected_match |= expected->x; \
+ if (expected_status == HKF_STATUS_OK) \
+ expected_status = HKF_STATUS_MATCHED; \
+ } \
+ } while (0)
+ expected_keytype = (parse_key || expected->no_parse_keytype < 0) ?
+ expected->l.keytype : expected->no_parse_keytype;
+
+#ifndef OPENSSL_HAS_ECC
+ if (expected->l.keytype == KEY_ECDSA ||
+ expected->no_parse_keytype == KEY_ECDSA) {
+ expected_status = HKF_STATUS_INVALID;
+ expected_keytype = KEY_UNSPEC;
+ parse_key = 0;
+ }
+#endif
+
+ UPDATE_MATCH_STATUS(match_host_p);
+ UPDATE_MATCH_STATUS(match_host_s);
+ UPDATE_MATCH_STATUS(match_ipv4);
+ UPDATE_MATCH_STATUS(match_ipv6);
+
+ ASSERT_PTR_NE(l->path, NULL); /* Don't care about path */
+ ASSERT_LONG_LONG_EQ(l->linenum, expected->l.linenum);
+ ASSERT_U_INT_EQ(l->status, expected_status);
+ ASSERT_U_INT_EQ(l->match, expected_match);
+ /* Not all test entries contain fulltext */
+ if (expected->l.line != NULL)
+ ASSERT_STRING_EQ(l->line, expected->l.line);
+ ASSERT_INT_EQ(l->marker, expected->l.marker);
+ /* XXX we skip hashed hostnames for now; implement checking */
+ if (expected->l.hosts != NULL)
+ ASSERT_STRING_EQ(l->hosts, expected->l.hosts);
+ /* Not all test entries contain raw keys */
+ if (expected->l.rawkey != NULL)
+ ASSERT_STRING_EQ(l->rawkey, expected->l.rawkey);
+ /* XXX synthesise raw key for cases lacking and compare */
+ ASSERT_INT_EQ(l->keytype, expected_keytype);
+ if (parse_key) {
+ if (expected->l.key == NULL)
+ ASSERT_PTR_EQ(l->key, NULL);
+ if (expected->l.key != NULL) {
+ ASSERT_PTR_NE(l->key, NULL);
+ ASSERT_INT_EQ(sshkey_equal(l->key, expected->l.key), 1);
+ }
+ }
+ if (parse_key && !(l->comment == NULL && expected->l.comment == NULL))
+ ASSERT_STRING_EQ(l->comment, expected->l.comment);
+ return 0;
+}
+
+/* Loads public keys for a set of expected results */
+static void
+prepare_expected(struct expected *expected, size_t n)
+{
+ size_t i;
+
+ for (i = 0; i < n; i++) {
+ if (expected[i].key_file == NULL)
+ continue;
+#ifndef OPENSSL_HAS_ECC
+ if (expected[i].l.keytype == KEY_ECDSA)
+ continue;
+#endif
+ ASSERT_INT_EQ(sshkey_load_public(
+ test_data_file(expected[i].key_file), &expected[i].l.key,
+ NULL), 0);
+ }
+}
+
+static void
+cleanup_expected(struct expected *expected, size_t n)
+{
+ size_t i;
+
+ for (i = 0; i < n; i++) {
+ sshkey_free(expected[i].l.key);
+ expected[i].l.key = NULL;
+ }
+}
+
+struct expected expected_full[] = {
+ { NULL, -1, -1, 0, 0, 0, 0, -1, {
+ NULL, /* path, don't care */
+ 1, /* line number */
+ HKF_STATUS_COMMENT, /* status */
+ 0, /* match flags */
+ "# Plain host keys, plain host names", /* full line, optional */
+ MRK_NONE, /* marker (CA / revoked) */
+ NULL, /* hosts text */
+ NULL, /* raw key, optional */
+ KEY_UNSPEC, /* key type */
+ NULL, /* deserialised key */
+ NULL, /* comment */
+ } },
+ { "dsa_1.pub" , -1, -1, 0, HKF_MATCH_HOST, 0, 0, -1, {
+ NULL,
+ 2,
+ HKF_STATUS_OK,
+ 0,
+ NULL,
+ MRK_NONE,
+ "sisyphus.example.com",
+ NULL,
+ KEY_DSA,
+ NULL, /* filled at runtime */
+ "DSA #1",
+ } },
+ { "ecdsa_1.pub" , -1, -1, 0, HKF_MATCH_HOST, 0, 0, -1, {
+ NULL,
+ 3,
+ HKF_STATUS_OK,
+ 0,
+ NULL,
+ MRK_NONE,
+ "sisyphus.example.com",
+ NULL,
+ KEY_ECDSA,
+ NULL, /* filled at runtime */
+ "ECDSA #1",
+ } },
+ { "ed25519_1.pub" , -1, -1, 0, HKF_MATCH_HOST, 0, 0, -1, {
+ NULL,
+ 4,
+ HKF_STATUS_OK,
+ 0,
+ NULL,
+ MRK_NONE,
+ "sisyphus.example.com",
+ NULL,
+ KEY_ED25519,
+ NULL, /* filled at runtime */
+ "ED25519 #1",
+ } },
+ { "rsa_1.pub" , -1, -1, 0, HKF_MATCH_HOST, 0, 0, -1, {
+ NULL,
+ 5,
+ HKF_STATUS_OK,
+ 0,
+ NULL,
+ MRK_NONE,
+ "sisyphus.example.com",
+ NULL,
+ KEY_RSA,
+ NULL, /* filled at runtime */
+ "RSA #1",
+ } },
+ { NULL, -1, -1, 0, 0, 0, 0, -1, {
+ NULL,
+ 6,
+ HKF_STATUS_COMMENT,
+ 0,
+ "",
+ MRK_NONE,
+ NULL,
+ NULL,
+ KEY_UNSPEC,
+ NULL,
+ NULL,
+ } },
+ { NULL, -1, -1, 0, 0, 0, 0, -1, {
+ NULL,
+ 7,
+ HKF_STATUS_COMMENT,
+ 0,
+ "# Plain host keys, hostnames + addresses",
+ MRK_NONE,
+ NULL,
+ NULL,
+ KEY_UNSPEC,
+ NULL,
+ NULL,
+ } },
+ { "dsa_2.pub" , -1, -1, HKF_MATCH_HOST, 0, HKF_MATCH_IP, HKF_MATCH_IP, -1, {
+ NULL,
+ 8,
+ HKF_STATUS_OK,
+ 0,
+ NULL,
+ MRK_NONE,
+ "prometheus.example.com,192.0.2.1,2001:db8::1",
+ NULL,
+ KEY_DSA,
+ NULL, /* filled at runtime */
+ "DSA #2",
+ } },
+ { "ecdsa_2.pub" , -1, -1, HKF_MATCH_HOST, 0, HKF_MATCH_IP, HKF_MATCH_IP, -1, {
+ NULL,
+ 9,
+ HKF_STATUS_OK,
+ 0,
+ NULL,
+ MRK_NONE,
+ "prometheus.example.com,192.0.2.1,2001:db8::1",
+ NULL,
+ KEY_ECDSA,
+ NULL, /* filled at runtime */
+ "ECDSA #2",
+ } },
+ { "ed25519_2.pub" , -1, -1, HKF_MATCH_HOST, 0, HKF_MATCH_IP, HKF_MATCH_IP, -1, {
+ NULL,
+ 10,
+ HKF_STATUS_OK,
+ 0,
+ NULL,
+ MRK_NONE,
+ "prometheus.example.com,192.0.2.1,2001:db8::1",
+ NULL,
+ KEY_ED25519,
+ NULL, /* filled at runtime */
+ "ED25519 #2",
+ } },
+ { "rsa_2.pub" , -1, -1, HKF_MATCH_HOST, 0, HKF_MATCH_IP, HKF_MATCH_IP, -1, {
+ NULL,
+ 11,
+ HKF_STATUS_OK,
+ 0,
+ NULL,
+ MRK_NONE,
+ "prometheus.example.com,192.0.2.1,2001:db8::1",
+ NULL,
+ KEY_RSA,
+ NULL, /* filled at runtime */
+ "RSA #2",
+ } },
+ { NULL, -1, -1, 0, 0, 0, 0, -1, {
+ NULL,
+ 12,
+ HKF_STATUS_COMMENT,
+ 0,
+ "",
+ MRK_NONE,
+ NULL,
+ NULL,
+ KEY_UNSPEC,
+ NULL,
+ NULL,
+ } },
+ { NULL, -1, -1, 0, 0, 0, 0, -1, {
+ NULL,
+ 13,
+ HKF_STATUS_COMMENT,
+ 0,
+ "# Some hosts with wildcard names / IPs",
+ MRK_NONE,
+ NULL,
+ NULL,
+ KEY_UNSPEC,
+ NULL,
+ NULL,
+ } },
+ { "dsa_3.pub" , -1, -1, HKF_MATCH_HOST, HKF_MATCH_HOST, HKF_MATCH_IP, HKF_MATCH_IP, -1, {
+ NULL,
+ 14,
+ HKF_STATUS_OK,
+ 0,
+ NULL,
+ MRK_NONE,
+ "*.example.com,192.0.2.*,2001:*",
+ NULL,
+ KEY_DSA,
+ NULL, /* filled at runtime */
+ "DSA #3",
+ } },
+ { "ecdsa_3.pub" , -1, -1, HKF_MATCH_HOST, HKF_MATCH_HOST, HKF_MATCH_IP, HKF_MATCH_IP, -1, {
+ NULL,
+ 15,
+ HKF_STATUS_OK,
+ 0,
+ NULL,
+ MRK_NONE,
+ "*.example.com,192.0.2.*,2001:*",
+ NULL,
+ KEY_ECDSA,
+ NULL, /* filled at runtime */
+ "ECDSA #3",
+ } },
+ { "ed25519_3.pub" , -1, -1, HKF_MATCH_HOST, HKF_MATCH_HOST, HKF_MATCH_IP, HKF_MATCH_IP, -1, {
+ NULL,
+ 16,
+ HKF_STATUS_OK,
+ 0,
+ NULL,
+ MRK_NONE,
+ "*.example.com,192.0.2.*,2001:*",
+ NULL,
+ KEY_ED25519,
+ NULL, /* filled at runtime */
+ "ED25519 #3",
+ } },
+ { "rsa_3.pub" , -1, -1, HKF_MATCH_HOST, HKF_MATCH_HOST, HKF_MATCH_IP, HKF_MATCH_IP, -1, {
+ NULL,
+ 17,
+ HKF_STATUS_OK,
+ 0,
+ NULL,
+ MRK_NONE,
+ "*.example.com,192.0.2.*,2001:*",
+ NULL,
+ KEY_RSA,
+ NULL, /* filled at runtime */
+ "RSA #3",
+ } },
+ { NULL, -1, -1, 0, 0, 0, 0, -1, {
+ NULL,
+ 18,
+ HKF_STATUS_COMMENT,
+ 0,
+ "",
+ MRK_NONE,
+ NULL,
+ NULL,
+ KEY_UNSPEC,
+ NULL,
+ NULL,
+ } },
+ { NULL, -1, -1, 0, 0, 0, 0, -1, {
+ NULL,
+ 19,
+ HKF_STATUS_COMMENT,
+ 0,
+ "# Hashed hostname and address entries",
+ MRK_NONE,
+ NULL,
+ NULL,
+ KEY_UNSPEC,
+ NULL,
+ NULL,
+ } },
+ { "dsa_5.pub" , -1, -1, 0, HKF_MATCH_HOST|HKF_MATCH_HOST_HASHED, 0, 0, -1, {
+ NULL,
+ 20,
+ HKF_STATUS_OK,
+ 0,
+ NULL,
+ MRK_NONE,
+ NULL,
+ NULL,
+ KEY_DSA,
+ NULL, /* filled at runtime */
+ "DSA #5",
+ } },
+ { "ecdsa_5.pub" , -1, -1, 0, HKF_MATCH_HOST|HKF_MATCH_HOST_HASHED, 0, 0, -1, {
+ NULL,
+ 21,
+ HKF_STATUS_OK,
+ 0,
+ NULL,
+ MRK_NONE,
+ NULL,
+ NULL,
+ KEY_ECDSA,
+ NULL, /* filled at runtime */
+ "ECDSA #5",
+ } },
+ { "ed25519_5.pub" , -1, -1, 0, HKF_MATCH_HOST|HKF_MATCH_HOST_HASHED, 0, 0, -1, {
+ NULL,
+ 22,
+ HKF_STATUS_OK,
+ 0,
+ NULL,
+ MRK_NONE,
+ NULL,
+ NULL,
+ KEY_ED25519,
+ NULL, /* filled at runtime */
+ "ED25519 #5",
+ } },
+ { "rsa_5.pub" , -1, -1, 0, HKF_MATCH_HOST|HKF_MATCH_HOST_HASHED, 0, 0, -1, {
+ NULL,
+ 23,
+ HKF_STATUS_OK,
+ 0,
+ NULL,
+ MRK_NONE,
+ NULL,
+ NULL,
+ KEY_RSA,
+ NULL, /* filled at runtime */
+ "RSA #5",
+ } },
+ { NULL, -1, -1, 0, 0, 0, 0, -1, {
+ NULL,
+ 24,
+ HKF_STATUS_COMMENT,
+ 0,
+ "",
+ MRK_NONE,
+ NULL,
+ NULL,
+ KEY_UNSPEC,
+ NULL,
+ NULL,
+ } },
+ /*
+ * The next series have each key listed multiple times, as the
+ * hostname and addresses in the pre-hashed known_hosts are split
+ * to separate lines.
+ */
+ { "dsa_6.pub" , -1, -1, HKF_MATCH_HOST|HKF_MATCH_HOST_HASHED, 0, 0, 0, -1, {
+ NULL,
+ 25,
+ HKF_STATUS_OK,
+ 0,
+ NULL,
+ MRK_NONE,
+ NULL,
+ NULL,
+ KEY_DSA,
+ NULL, /* filled at runtime */
+ "DSA #6",
+ } },
+ { "dsa_6.pub" , -1, -1, 0, 0, HKF_MATCH_IP|HKF_MATCH_IP_HASHED, 0, -1, {
+ NULL,
+ 26,
+ HKF_STATUS_OK,
+ 0,
+ NULL,
+ MRK_NONE,
+ NULL,
+ NULL,
+ KEY_DSA,
+ NULL, /* filled at runtime */
+ "DSA #6",
+ } },
+ { "dsa_6.pub" , -1, -1, 0, 0, 0, HKF_MATCH_IP|HKF_MATCH_IP_HASHED, -1, {
+ NULL,
+ 27,
+ HKF_STATUS_OK,
+ 0,
+ NULL,
+ MRK_NONE,
+ NULL,
+ NULL,
+ KEY_DSA,
+ NULL, /* filled at runtime */
+ "DSA #6",
+ } },
+ { "ecdsa_6.pub" , -1, -1, HKF_MATCH_HOST|HKF_MATCH_HOST_HASHED, 0, 0, 0, -1, {
+ NULL,
+ 28,
+ HKF_STATUS_OK,
+ 0,
+ NULL,
+ MRK_NONE,
+ NULL,
+ NULL,
+ KEY_ECDSA,
+ NULL, /* filled at runtime */
+ "ECDSA #6",
+ } },
+ { "ecdsa_6.pub" , -1, -1, 0, 0, HKF_MATCH_IP|HKF_MATCH_IP_HASHED, 0, -1, {
+ NULL,
+ 29,
+ HKF_STATUS_OK,
+ 0,
+ NULL,
+ MRK_NONE,
+ NULL,
+ NULL,
+ KEY_ECDSA,
+ NULL, /* filled at runtime */
+ "ECDSA #6",
+ } },
+ { "ecdsa_6.pub" , -1, -1, 0, 0, 0, HKF_MATCH_IP|HKF_MATCH_IP_HASHED, -1, {
+ NULL,
+ 30,
+ HKF_STATUS_OK,
+ 0,
+ NULL,
+ MRK_NONE,
+ NULL,
+ NULL,
+ KEY_ECDSA,
+ NULL, /* filled at runtime */
+ "ECDSA #6",
+ } },
+ { "ed25519_6.pub" , -1, -1, HKF_MATCH_HOST|HKF_MATCH_HOST_HASHED, 0, 0, 0, -1, {
+ NULL,
+ 31,
+ HKF_STATUS_OK,
+ 0,
+ NULL,
+ MRK_NONE,
+ NULL,
+ NULL,
+ KEY_ED25519,
+ NULL, /* filled at runtime */
+ "ED25519 #6",
+ } },
+ { "ed25519_6.pub" , -1, -1, 0, 0, HKF_MATCH_IP|HKF_MATCH_IP_HASHED, 0, -1, {
+ NULL,
+ 32,
+ HKF_STATUS_OK,
+ 0,
+ NULL,
+ MRK_NONE,
+ NULL,
+ NULL,
+ KEY_ED25519,
+ NULL, /* filled at runtime */
+ "ED25519 #6",
+ } },
+ { "ed25519_6.pub" , -1, -1, 0, 0, 0, HKF_MATCH_IP|HKF_MATCH_IP_HASHED, -1, {
+ NULL,
+ 33,
+ HKF_STATUS_OK,
+ 0,
+ NULL,
+ MRK_NONE,
+ NULL,
+ NULL,
+ KEY_ED25519,
+ NULL, /* filled at runtime */
+ "ED25519 #6",
+ } },
+ { "rsa_6.pub" , -1, -1, HKF_MATCH_HOST|HKF_MATCH_HOST_HASHED, 0, 0, 0, -1, {
+ NULL,
+ 34,
+ HKF_STATUS_OK,
+ 0,
+ NULL,
+ MRK_NONE,
+ NULL,
+ NULL,
+ KEY_RSA,
+ NULL, /* filled at runtime */
+ "RSA #6",
+ } },
+ { "rsa_6.pub" , -1, -1, 0, 0, HKF_MATCH_IP|HKF_MATCH_IP_HASHED, 0, -1, {
+ NULL,
+ 35,
+ HKF_STATUS_OK,
+ 0,
+ NULL,
+ MRK_NONE,
+ NULL,
+ NULL,
+ KEY_RSA,
+ NULL, /* filled at runtime */
+ "RSA #6",
+ } },
+ { "rsa_6.pub" , -1, -1, 0, 0, 0, HKF_MATCH_IP|HKF_MATCH_IP_HASHED, -1, {
+ NULL,
+ 36,
+ HKF_STATUS_OK,
+ 0,
+ NULL,
+ MRK_NONE,
+ NULL,
+ NULL,
+ KEY_RSA,
+ NULL, /* filled at runtime */
+ "RSA #6",
+ } },
+ { NULL, -1, -1, 0, 0, 0, 0, -1, {
+ NULL,
+ 37,
+ HKF_STATUS_COMMENT,
+ 0,
+ "",
+ MRK_NONE,
+ NULL,
+ NULL,
+ KEY_UNSPEC,
+ NULL,
+ NULL,
+ } },
+ { NULL, -1, -1, 0, 0, 0, 0, -1, {
+ NULL,
+ 38,
+ HKF_STATUS_COMMENT,
+ 0,
+ "",
+ MRK_NONE,
+ NULL,
+ NULL,
+ KEY_UNSPEC,
+ NULL,
+ NULL,
+ } },
+ { NULL, -1, -1, 0, 0, 0, 0, -1, {
+ NULL,
+ 39,
+ HKF_STATUS_COMMENT,
+ 0,
+ "# Revoked and CA keys",
+ MRK_NONE,
+ NULL,
+ NULL,
+ KEY_UNSPEC,
+ NULL,
+ NULL,
+ } },
+ { "ed25519_4.pub" , -1, -1, 0, HKF_MATCH_HOST, 0, 0, -1, {
+ NULL,
+ 40,
+ HKF_STATUS_OK,
+ 0,
+ NULL,
+ MRK_REVOKE,
+ "sisyphus.example.com",
+ NULL,
+ KEY_ED25519,
+ NULL, /* filled at runtime */
+ "ED25519 #4",
+ } },
+ { "ecdsa_4.pub" , -1, -1, HKF_MATCH_HOST, 0, 0, 0, -1, {
+ NULL,
+ 41,
+ HKF_STATUS_OK,
+ 0,
+ NULL,
+ MRK_CA,
+ "prometheus.example.com",
+ NULL,
+ KEY_ECDSA,
+ NULL, /* filled at runtime */
+ "ECDSA #4",
+ } },
+ { "dsa_4.pub" , -1, -1, HKF_MATCH_HOST, HKF_MATCH_HOST, 0, 0, -1, {
+ NULL,
+ 42,
+ HKF_STATUS_OK,
+ 0,
+ NULL,
+ MRK_CA,
+ "*.example.com",
+ NULL,
+ KEY_DSA,
+ NULL, /* filled at runtime */
+ "DSA #4",
+ } },
+ { NULL, -1, -1, 0, 0, 0, 0, -1, {
+ NULL,
+ 43,
+ HKF_STATUS_COMMENT,
+ 0,
+ "",
+ MRK_NONE,
+ NULL,
+ NULL,
+ KEY_UNSPEC,
+ NULL,
+ NULL,
+ } },
+ { NULL, -1, -1, 0, 0, 0, 0, -1, {
+ NULL,
+ 44,
+ HKF_STATUS_COMMENT,
+ 0,
+ "# Some invalid lines",
+ MRK_NONE,
+ NULL,
+ NULL,
+ KEY_UNSPEC,
+ NULL,
+ NULL,
+ } },
+ { NULL, -1, -1, 0, 0, 0, 0, -1, {
+ NULL,
+ 45,
+ HKF_STATUS_INVALID,
+ 0,
+ NULL,
+ MRK_ERROR,
+ NULL,
+ NULL,
+ KEY_UNSPEC,
+ NULL,
+ NULL,
+ } },
+ { NULL, -1, -1, 0, HKF_MATCH_HOST, 0, 0, -1, {
+ NULL,
+ 46,
+ HKF_STATUS_INVALID,
+ 0,
+ NULL,
+ MRK_NONE,
+ "sisyphus.example.com",
+ NULL,
+ KEY_UNSPEC,
+ NULL,
+ NULL,
+ } },
+ { NULL, -1, -1, HKF_MATCH_HOST, 0, 0, 0, -1, {
+ NULL,
+ 47,
+ HKF_STATUS_INVALID,
+ 0,
+ NULL,
+ MRK_NONE,
+ "prometheus.example.com",
+ NULL,
+ KEY_UNSPEC,
+ NULL,
+ NULL,
+ } },
+ { NULL, -1, -1, 0, HKF_MATCH_HOST, 0, 0, -1, {
+ NULL,
+ 48,
+ HKF_STATUS_INVALID, /* Would be ok if key not parsed */
+ 0,
+ NULL,
+ MRK_NONE,
+ "sisyphus.example.com",
+ NULL,
+ KEY_UNSPEC,
+ NULL,
+ NULL,
+ } },
+ { NULL, -1, -1, 0, HKF_MATCH_HOST, 0, 0, -1, {
+ NULL,
+ 49,
+ HKF_STATUS_INVALID,
+ 0,
+ NULL,
+ MRK_NONE,
+ "sisyphus.example.com",
+ NULL,
+ KEY_UNSPEC,
+ NULL, /* filled at runtime */
+ NULL,
+ } },
+ { NULL, HKF_STATUS_OK, KEY_RSA, HKF_MATCH_HOST, 0, 0, 0, -1, {
+ NULL,
+ 50,
+ HKF_STATUS_INVALID, /* Would be ok if key not parsed */
+ 0,
+ NULL,
+ MRK_NONE,
+ "prometheus.example.com",
+ NULL,
+ KEY_UNSPEC,
+ NULL, /* filled at runtime */
+ NULL,
+ } },
+};
+
+void test_iterate(void);
+
+void
+test_iterate(void)
+{
+ struct cbctx ctx;
+
+ TEST_START("hostkeys_iterate all with key parse");
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.expected = expected_full;
+ ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full);
+ ctx.flags = HKF_WANT_PARSE_KEY;
+ prepare_expected(expected_full, ctx.nexpected);
+ ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"),
+ check, &ctx, NULL, NULL, ctx.flags), 0);
+ cleanup_expected(expected_full, ctx.nexpected);
+ TEST_DONE();
+
+ TEST_START("hostkeys_iterate all without key parse");
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.expected = expected_full;
+ ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full);
+ ctx.flags = 0;
+ prepare_expected(expected_full, ctx.nexpected);
+ ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"),
+ check, &ctx, NULL, NULL, ctx.flags), 0);
+ cleanup_expected(expected_full, ctx.nexpected);
+ TEST_DONE();
+
+ TEST_START("hostkeys_iterate specify host 1");
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.expected = expected_full;
+ ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full);
+ ctx.flags = 0;
+ ctx.match_host_p = 1;
+ prepare_expected(expected_full, ctx.nexpected);
+ ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"),
+ check, &ctx, "prometheus.example.com", NULL, ctx.flags), 0);
+ cleanup_expected(expected_full, ctx.nexpected);
+ TEST_DONE();
+
+ TEST_START("hostkeys_iterate specify host 2");
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.expected = expected_full;
+ ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full);
+ ctx.flags = 0;
+ ctx.match_host_s = 1;
+ prepare_expected(expected_full, ctx.nexpected);
+ ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"),
+ check, &ctx, "sisyphus.example.com", NULL, ctx.flags), 0);
+ cleanup_expected(expected_full, ctx.nexpected);
+ TEST_DONE();
+
+ TEST_START("hostkeys_iterate match host 1");
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.expected = expected_full;
+ ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full);
+ ctx.flags = HKF_WANT_MATCH;
+ ctx.match_host_p = 1;
+ prepare_expected(expected_full, ctx.nexpected);
+ ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"),
+ check, &ctx, "prometheus.example.com", NULL, ctx.flags), 0);
+ cleanup_expected(expected_full, ctx.nexpected);
+ TEST_DONE();
+
+ TEST_START("hostkeys_iterate match host 2");
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.expected = expected_full;
+ ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full);
+ ctx.flags = HKF_WANT_MATCH;
+ ctx.match_host_s = 1;
+ prepare_expected(expected_full, ctx.nexpected);
+ ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"),
+ check, &ctx, "sisyphus.example.com", NULL, ctx.flags), 0);
+ cleanup_expected(expected_full, ctx.nexpected);
+ TEST_DONE();
+
+ TEST_START("hostkeys_iterate specify host missing");
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.expected = expected_full;
+ ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full);
+ ctx.flags = 0;
+ prepare_expected(expected_full, ctx.nexpected);
+ ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"),
+ check, &ctx, "actaeon.example.org", NULL, ctx.flags), 0);
+ cleanup_expected(expected_full, ctx.nexpected);
+ TEST_DONE();
+
+ TEST_START("hostkeys_iterate match host missing");
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.expected = expected_full;
+ ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full);
+ ctx.flags = HKF_WANT_MATCH;
+ prepare_expected(expected_full, ctx.nexpected);
+ ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"),
+ check, &ctx, "actaeon.example.org", NULL, ctx.flags), 0);
+ cleanup_expected(expected_full, ctx.nexpected);
+ TEST_DONE();
+
+ TEST_START("hostkeys_iterate specify IPv4");
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.expected = expected_full;
+ ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full);
+ ctx.flags = 0;
+ ctx.match_ipv4 = 1;
+ prepare_expected(expected_full, ctx.nexpected);
+ ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"),
+ check, &ctx, "tiresias.example.org", "192.0.2.1", ctx.flags), 0);
+ cleanup_expected(expected_full, ctx.nexpected);
+ TEST_DONE();
+
+ TEST_START("hostkeys_iterate specify IPv6");
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.expected = expected_full;
+ ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full);
+ ctx.flags = 0;
+ ctx.match_ipv6 = 1;
+ prepare_expected(expected_full, ctx.nexpected);
+ ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"),
+ check, &ctx, "tiresias.example.org", "2001:db8::1", ctx.flags), 0);
+ cleanup_expected(expected_full, ctx.nexpected);
+ TEST_DONE();
+
+ TEST_START("hostkeys_iterate match IPv4");
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.expected = expected_full;
+ ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full);
+ ctx.flags = HKF_WANT_MATCH;
+ ctx.match_ipv4 = 1;
+ prepare_expected(expected_full, ctx.nexpected);
+ ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"),
+ check, &ctx, "tiresias.example.org", "192.0.2.1", ctx.flags), 0);
+ cleanup_expected(expected_full, ctx.nexpected);
+ TEST_DONE();
+
+ TEST_START("hostkeys_iterate match IPv6");
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.expected = expected_full;
+ ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full);
+ ctx.flags = HKF_WANT_MATCH;
+ ctx.match_ipv6 = 1;
+ prepare_expected(expected_full, ctx.nexpected);
+ ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"),
+ check, &ctx, "tiresias.example.org", "2001:db8::1", ctx.flags), 0);
+ cleanup_expected(expected_full, ctx.nexpected);
+ TEST_DONE();
+
+ TEST_START("hostkeys_iterate specify addr missing");
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.expected = expected_full;
+ ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full);
+ ctx.flags = 0;
+ prepare_expected(expected_full, ctx.nexpected);
+ ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"),
+ check, &ctx, "tiresias.example.org", "192.168.0.1", ctx.flags), 0);
+ cleanup_expected(expected_full, ctx.nexpected);
+ TEST_DONE();
+
+ TEST_START("hostkeys_iterate match addr missing");
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.expected = expected_full;
+ ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full);
+ ctx.flags = HKF_WANT_MATCH;
+ prepare_expected(expected_full, ctx.nexpected);
+ ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"),
+ check, &ctx, "tiresias.example.org", "::1", ctx.flags), 0);
+ cleanup_expected(expected_full, ctx.nexpected);
+ TEST_DONE();
+
+ TEST_START("hostkeys_iterate specify host 2 and IPv4");
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.expected = expected_full;
+ ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full);
+ ctx.flags = 0;
+ ctx.match_host_s = 1;
+ ctx.match_ipv4 = 1;
+ prepare_expected(expected_full, ctx.nexpected);
+ ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"),
+ check, &ctx, "sisyphus.example.com", "192.0.2.1", ctx.flags), 0);
+ cleanup_expected(expected_full, ctx.nexpected);
+ TEST_DONE();
+
+ TEST_START("hostkeys_iterate match host 1 and IPv6");
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.expected = expected_full;
+ ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full);
+ ctx.flags = HKF_WANT_MATCH;
+ ctx.match_host_p = 1;
+ ctx.match_ipv6 = 1;
+ prepare_expected(expected_full, ctx.nexpected);
+ ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"),
+ check, &ctx, "prometheus.example.com",
+ "2001:db8::1", ctx.flags), 0);
+ cleanup_expected(expected_full, ctx.nexpected);
+ TEST_DONE();
+
+ TEST_START("hostkeys_iterate specify host 2 and IPv4 w/ key parse");
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.expected = expected_full;
+ ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full);
+ ctx.flags = HKF_WANT_PARSE_KEY;
+ ctx.match_host_s = 1;
+ ctx.match_ipv4 = 1;
+ prepare_expected(expected_full, ctx.nexpected);
+ ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"),
+ check, &ctx, "sisyphus.example.com", "192.0.2.1", ctx.flags), 0);
+ cleanup_expected(expected_full, ctx.nexpected);
+ TEST_DONE();
+
+ TEST_START("hostkeys_iterate match host 1 and IPv6 w/ key parse");
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.expected = expected_full;
+ ctx.nexpected = sizeof(expected_full)/sizeof(*expected_full);
+ ctx.flags = HKF_WANT_MATCH|HKF_WANT_PARSE_KEY;
+ ctx.match_host_p = 1;
+ ctx.match_ipv6 = 1;
+ prepare_expected(expected_full, ctx.nexpected);
+ ASSERT_INT_EQ(hostkeys_foreach(test_data_file("known_hosts"),
+ check, &ctx, "prometheus.example.com",
+ "2001:db8::1", ctx.flags), 0);
+ cleanup_expected(expected_full, ctx.nexpected);
+ TEST_DONE();
+}
+
diff --git a/regress/unittests/hostkeys/testdata/dsa_1.pub b/regress/unittests/hostkeys/testdata/dsa_1.pub
new file mode 100644
index 0000000..56e1e37
--- /dev/null
+++ b/regress/unittests/hostkeys/testdata/dsa_1.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAOqffHxEW4c+Z9q/r3l4sYK8F7qrBsU8XF9upGsW62T9InROFFq9IO0x3pQ6mDA0Wtw0sqcDmkPCHPyP4Ok/fU3/drLaZusHoVYu8pBBrWsIDrKgkeX9TEodBsSrYdl4Sqtqq9EZv9+DttV6LStZrgYyUTOKwOF95wGantpLynX5AAAAFQDdt+zjRNlETDsgmxcSYFgREirJrQAAAIBQlrPaiPhR24FhnMLcHH4016vL7AqDDID6Qw7PhbXGa4/XlxWMIigjBKrIPKvnZ6p712LSnCKtcbfdx0MtmJlNa01CYqPaRhgRaf+uGdvTkTUcdaq8R5lLJL+JMNwUhcC8ijm3NqEjXjffuebGe1EzIeiITbA7Nndcd+GytwRDegAAAIEAkRYPjSVcUxfUHhHdpP6V8CuY1+CYSs9EPJ7iiWTDuXWVIBTU32oJLAnrmAcOwtIzEfPvm+rff5FI/Yhon2pB3VTXhPPEBjYzE5qANanAT4e6tzAVc5f3DUhHaDknwRYfDz86GFvuLtDjeE/UZ9t6OofYoEsCBpYozLAprBvNIQY= DSA #1
diff --git a/regress/unittests/hostkeys/testdata/dsa_2.pub b/regress/unittests/hostkeys/testdata/dsa_2.pub
new file mode 100644
index 0000000..394e0bf
--- /dev/null
+++ b/regress/unittests/hostkeys/testdata/dsa_2.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAI38Hy/61/O5Bp6yUG8J5XQCeNjRS0xvjlCdzKLyXCueMa+L+X2L/u9PWUsy5SVbTjGgpB8sF6UkCNsV+va7S8zCCHas2MZ7GPlxP6GZBkRPTIFR0N/Pu7wfBzDQz0t0iL4VmxBfTBQv/SxkGWZg+yHihIQP9fwdSAwD/7aVh6ItAAAAFQDSyihIUlINlswM0PJ8wXSti3yIMwAAAIB+oqzaB6ozqs8YxpN5oQOBa/9HEBQEsp8RSIlQmVubXRNgktp42n+Ii1waU9UUk8DX5ahhIeR6B7ojWkqmDAji4SKpoHf4kmr6HvYo85ZSTSx0W4YK/gJHSpDJwhlT52tAfb1JCbWSObjl09B4STv7KedCHcR5oXQvvrV+XoKOSAAAAIAue/EXrs2INw1RfaKNHC0oqOMxmRitv0BFMuNVPo1VDj39CE5kA7AHjwvS1TNeaHtK5Hhgeb6vsmLmNPTOc8xCob0ilyQbt9O0GbONeF2Ge7D2UJyULA/hxql+tCYFIC6yUrmo35fF9XiNisXLoaflk9fjp7ROWWVwnki/jstaQw== DSA #2
diff --git a/regress/unittests/hostkeys/testdata/dsa_3.pub b/regress/unittests/hostkeys/testdata/dsa_3.pub
new file mode 100644
index 0000000..e506ea4
--- /dev/null
+++ b/regress/unittests/hostkeys/testdata/dsa_3.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAI6lz2Ip9bzE7TGuDD4SjO9S4Ac90gq0h6ai1O06eI8t/Ot2uJ5Jk2QyVr2jvIZHDl/5bwBx7+5oyjlwRoUrAPPD814wf5tU2tSnmdu1Wbf0cBswif5q0r4tevzmopp/AtgH11QHo3u0/pfyJd10qBDLV2FaYSKMmZvyPfZJ0s9pAAAAFQD5Eqjl6Rx2qVePodD9OwAPT0bU6wAAAIAfnDm6csZF0sFaJR3NIJvaYgSGr8s7cqlsk2gLltB/1wOOO2yX+NeEC+B0H93hlMfaUsPa08bwgmYxnavSMqEBpmtPceefJiEd68zwYqXd38f88wyWZ9Z5iwaI/6OVZPHzCbDxOa4ewVTevRNYUKP1xUTZNT8/gSMfZLYPk4T2AQAAAIAUKroozRMyV+3V/rxt0gFnNxRXBKk+9cl3vgsQ7ktkI9cYg7V1T2K0XF21AVMK9gODszy6PBJjV6ruXBV6TRiqIbQauivp3bHHKYsG6wiJNqwdbVwIjfvv8nn1qFoZQLXG3sdONr9NwN8KzrX89OV0BlR2dVM5qqp+YxOXymP9yg== DSA #3
diff --git a/regress/unittests/hostkeys/testdata/dsa_4.pub b/regress/unittests/hostkeys/testdata/dsa_4.pub
new file mode 100644
index 0000000..8552c38
--- /dev/null
+++ b/regress/unittests/hostkeys/testdata/dsa_4.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAKvjnFHm0VvMr5h2Zu3nURsxQKGoxm+DCzYDxRYcilK07Cm5c4XTrFbA2X86+9sGs++W7QRMcTJUYIg0a+UtIMtAjwORd6ZPXM2K5dBW+gh1oHyvKi767tWX7I2c+1ZPJDY95mUUfZQUEfdy9eGDSBmw/pSsveQ1ur6XNUh/MtP/AAAAFQDHnXk/9jBJAdce1pHtLWnbdPSGdQAAAIEAm2OLy8tZBfiEO3c3X1yyB/GTcDwrQCqRMDkhnsmrliec3dWkOfNTzu+MrdvF8ymTWLEqPpbMheYtvNyZ3TF0HO5W7aVBpdGZbOdOAIfB+6skqGbI8A5Up1d7dak/bSsqL2r5NjwbDOdq+1hBzzvbl/qjh+sQarV2zHrpKoQaV28AAACANtkBVedBbqIAdphCrN/LbUi9WlyuF9UZz+tlpVLYrj8GJVwnplV2tvOmUw6yP5/pzCimTsao8dpL5PWxm7fKxLWVxA+lEsA4WeC885CiZn8xhdaJOCN+NyJ2bqkz+4VPI7oDGBm0aFwUqJn+M1PiSgvI50XdF2dBsFRTRNY0wzA= DSA #4
diff --git a/regress/unittests/hostkeys/testdata/dsa_5.pub b/regress/unittests/hostkeys/testdata/dsa_5.pub
new file mode 100644
index 0000000..149e1ef
--- /dev/null
+++ b/regress/unittests/hostkeys/testdata/dsa_5.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBALrFy7w5ihlaOG+qR+6fj+vm5EQaO3qwxgACLcgH+VfShuOG4mkx8qFJmf+OZ3fh5iKngjNZfKtfcqI7zHWdk6378TQfQC52/kbZukjNXOLCpyNkogahcjA00onIoTK1RUDuMW28edAHwPFbpttXDTaqis+8JPMY8hZwsZGENCzTAAAAFQD6+It5vozwGgaN9ROYPMlByhi6jwAAAIBz2mcAC694vNzz9b6614gkX9d9E99PzJYfU1MPkXDziKg7MrjBw7Opd5y1jL09S3iL6lSTlHkKwVKvQ3pOwWRwXXRrKVus4I0STveoApm526jmp6mY0YEtqR98vMJ0v97h1ydt8FikKlihefCsnXVicb8887PXs2Y8C6GuFT3tfQAAAIBbmHtV5tPcrMRDkULhaQ/Whap2VKvT2DUhIHA7lx6oy/KpkltOpxDZOIGUHKqffGbiR7Jh01/y090AY5L2eCf0S2Ytx93+eADwVVpJbFJo6zSwfeey2Gm6L2oA+rCz9zTdmtZoekpD3/RAOQjnJIAPwbs7mXwabZTw4xRtiYIRrw== DSA #5
diff --git a/regress/unittests/hostkeys/testdata/dsa_6.pub b/regress/unittests/hostkeys/testdata/dsa_6.pub
new file mode 100644
index 0000000..edbb976
--- /dev/null
+++ b/regress/unittests/hostkeys/testdata/dsa_6.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAIutigAse65TCW6hHDOEGXenE9L4L0talHbs65hj3UUNtWflKdQeXLofqXgW8AwaDKmnuRPrxRoxVNXj84n45wtBEdt4ztmdAZteAbXSnHqpcxME3jDxh3EtxzGPXLs+RUmKPVguraSgo7W2oN7KFx6VM+AcAtxANSTlvDid3s47AAAAFQCd9Q3kkHSLWe77sW0eRaayI45ovwAAAIAw6srGF6xvFasI44Y3r9JJ2K+3ezozl3ldL3p2+p2HG3iWafC4SdV8pB6ZIxKlYAywiiFb3LzH/JweGFq1jtoFDRM3MlYORBevydU4zPz7b5QLDVB0sY4evYtWmg2BFJvoWRfhLnlZVW7h5N8v4fNIwdVmVsw4Ljes7iF2HRGhHgAAAIBDFT3fww2Oby1xUA6G9pDAcVikrQFqp1sJRylNTUyeyQ37SNAGzYxwHJFgQr8gZLdRQ1UW+idYpqVbVNcYFMOiw/zSqK2OfVwPZ9U+TTKdc992ChSup6vJEKM/ZVIyDWDbJr7igQ4ahy7jo9mFvm8ljN926EnspQzCvs0Dxk6tHA== DSA #6
diff --git a/regress/unittests/hostkeys/testdata/ecdsa_1.pub b/regress/unittests/hostkeys/testdata/ecdsa_1.pub
new file mode 100644
index 0000000..16a535b
--- /dev/null
+++ b/regress/unittests/hostkeys/testdata/ecdsa_1.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBF6yQEtD9yBw9gmDRf477WBBzvWhAa0ioBI3nbA4emKykj0RbuQd5C4XdQAEOZGzE7v//FcCjwB2wi+JH5eKkxCtN6CjohDASZ1huoIV2UVyYIicZJEEOg1IWjjphvaxtw== ECDSA #1
diff --git a/regress/unittests/hostkeys/testdata/ecdsa_2.pub b/regress/unittests/hostkeys/testdata/ecdsa_2.pub
new file mode 100644
index 0000000..d2bad11
--- /dev/null
+++ b/regress/unittests/hostkeys/testdata/ecdsa_2.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAB8qVcXwgBM92NCmReQlPrZAoui4Bz/mW0VUBFOpHXXW1n+15b/Y7Pc6UBd/ITTZmaBciXY+PWaSBGdwc5GdqGdLgFyJ/QAGrFMPNpVutm/82gNQzlxpNwjbMcKyiZEXzSgnjS6DzMQ0WuSMdzIBXq8OW/Kafxg4ZkU6YqALUXxlQMZuQ== ECDSA #2
diff --git a/regress/unittests/hostkeys/testdata/ecdsa_3.pub b/regress/unittests/hostkeys/testdata/ecdsa_3.pub
new file mode 100644
index 0000000..e3ea925
--- /dev/null
+++ b/regress/unittests/hostkeys/testdata/ecdsa_3.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBIb3BhJZk+vUQPg5TQc1koIzuGqloCq7wjr9LjlhG24IBeiFHLsdWw74HDlH4DrOmlxToVYk2lTdnjARleRByjk= ECDSA #3
diff --git a/regress/unittests/hostkeys/testdata/ecdsa_4.pub b/regress/unittests/hostkeys/testdata/ecdsa_4.pub
new file mode 100644
index 0000000..2d616f5
--- /dev/null
+++ b/regress/unittests/hostkeys/testdata/ecdsa_4.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHZd0OXHIWwK3xnjAdMZ1tojxWycdu38pORO/UX5cqsKMgGCKQVBWWO3TFk1ePkGIE9VMWT1hCGqWRRwYlH+dSE= ECDSA #4
diff --git a/regress/unittests/hostkeys/testdata/ecdsa_5.pub b/regress/unittests/hostkeys/testdata/ecdsa_5.pub
new file mode 100644
index 0000000..a3df9b3
--- /dev/null
+++ b/regress/unittests/hostkeys/testdata/ecdsa_5.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPIudcagzq4QPtP1jkpje34+0POLB0jwT64hqrbCqhTH2T800KDZ0h2vwlJYa3OP3Oqru9AB5pnuHsKw7mAhUGY= ECDSA #5
diff --git a/regress/unittests/hostkeys/testdata/ecdsa_6.pub b/regress/unittests/hostkeys/testdata/ecdsa_6.pub
new file mode 100644
index 0000000..139f5a7
--- /dev/null
+++ b/regress/unittests/hostkeys/testdata/ecdsa_6.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBK1wRLyKtvK3Mmhd0XPkKwW4ev1KBVf8J4aG8lESq1TsaqqfOXYGyxMq5pN8fCGiD5UPOqyTYz/ZNzClRhJRHao= ECDSA #6
diff --git a/regress/unittests/hostkeys/testdata/ed25519_1.pub b/regress/unittests/hostkeys/testdata/ed25519_1.pub
new file mode 100644
index 0000000..0b12efe
--- /dev/null
+++ b/regress/unittests/hostkeys/testdata/ed25519_1.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK9ks7jkua5YWIwByRnnnc6UPJQWI75O0e/UJdPYU1JI ED25519 #1
diff --git a/regress/unittests/hostkeys/testdata/ed25519_2.pub b/regress/unittests/hostkeys/testdata/ed25519_2.pub
new file mode 100644
index 0000000..78e262b
--- /dev/null
+++ b/regress/unittests/hostkeys/testdata/ed25519_2.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIBp6PVW0z2o9C4Ukv/JOgmK7QMFe1pD1s3ADFF7IQob ED25519 #2
diff --git a/regress/unittests/hostkeys/testdata/ed25519_3.pub b/regress/unittests/hostkeys/testdata/ed25519_3.pub
new file mode 100644
index 0000000..64e5f12
--- /dev/null
+++ b/regress/unittests/hostkeys/testdata/ed25519_3.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBlYfExtYZAPqYvYdrlpGlSWhh/XNHcH3v3c2JzsVNbB ED25519 #3
diff --git a/regress/unittests/hostkeys/testdata/ed25519_4.pub b/regress/unittests/hostkeys/testdata/ed25519_4.pub
new file mode 100644
index 0000000..47b6724
--- /dev/null
+++ b/regress/unittests/hostkeys/testdata/ed25519_4.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDFP8L9REfN/iYy1KIRtFqSCn3V2+vOCpoZYENFGLdOF ED25519 #4
diff --git a/regress/unittests/hostkeys/testdata/ed25519_5.pub b/regress/unittests/hostkeys/testdata/ed25519_5.pub
new file mode 100644
index 0000000..72ccae6
--- /dev/null
+++ b/regress/unittests/hostkeys/testdata/ed25519_5.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINf63qSV8rD57N+digID8t28WVhd3Yf2K2UhaoG8TsWQ ED25519 #5
diff --git a/regress/unittests/hostkeys/testdata/ed25519_6.pub b/regress/unittests/hostkeys/testdata/ed25519_6.pub
new file mode 100644
index 0000000..0f71973
--- /dev/null
+++ b/regress/unittests/hostkeys/testdata/ed25519_6.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPLW0ZwCkRQldpLa4I5BpwGa/om+WE6OgC8jdVqakt0Z ED25519 #6
diff --git a/regress/unittests/hostkeys/testdata/known_hosts b/regress/unittests/hostkeys/testdata/known_hosts
new file mode 100644
index 0000000..4446f45
--- /dev/null
+++ b/regress/unittests/hostkeys/testdata/known_hosts
@@ -0,0 +1,50 @@
+# Plain host keys, plain host names
+sisyphus.example.com ssh-dss AAAAB3NzaC1kc3MAAACBAOqffHxEW4c+Z9q/r3l4sYK8F7qrBsU8XF9upGsW62T9InROFFq9IO0x3pQ6mDA0Wtw0sqcDmkPCHPyP4Ok/fU3/drLaZusHoVYu8pBBrWsIDrKgkeX9TEodBsSrYdl4Sqtqq9EZv9+DttV6LStZrgYyUTOKwOF95wGantpLynX5AAAAFQDdt+zjRNlETDsgmxcSYFgREirJrQAAAIBQlrPaiPhR24FhnMLcHH4016vL7AqDDID6Qw7PhbXGa4/XlxWMIigjBKrIPKvnZ6p712LSnCKtcbfdx0MtmJlNa01CYqPaRhgRaf+uGdvTkTUcdaq8R5lLJL+JMNwUhcC8ijm3NqEjXjffuebGe1EzIeiITbA7Nndcd+GytwRDegAAAIEAkRYPjSVcUxfUHhHdpP6V8CuY1+CYSs9EPJ7iiWTDuXWVIBTU32oJLAnrmAcOwtIzEfPvm+rff5FI/Yhon2pB3VTXhPPEBjYzE5qANanAT4e6tzAVc5f3DUhHaDknwRYfDz86GFvuLtDjeE/UZ9t6OofYoEsCBpYozLAprBvNIQY= DSA #1
+sisyphus.example.com ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBF6yQEtD9yBw9gmDRf477WBBzvWhAa0ioBI3nbA4emKykj0RbuQd5C4XdQAEOZGzE7v//FcCjwB2wi+JH5eKkxCtN6CjohDASZ1huoIV2UVyYIicZJEEOg1IWjjphvaxtw== ECDSA #1
+sisyphus.example.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK9ks7jkua5YWIwByRnnnc6UPJQWI75O0e/UJdPYU1JI ED25519 #1
+sisyphus.example.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDg4hB4vAZHJ0PVRiJajOv/GlytFWNpv5/9xgB9+5BIbvp8LOrFZ5D9K0Gsmwpd4G4rfaAz8j896DhMArg0vtkilIPPGt/6VzWMERgvaIQPJ/IE99X3+fjcAG56oAWwy29JX10lQMzBPU6XJIaN/zqpkb6qUBiAHBdLpxrFBBU0/w== RSA #1
+
+# Plain host keys, hostnames + addresses
+prometheus.example.com,192.0.2.1,2001:db8::1 ssh-dss AAAAB3NzaC1kc3MAAACBAI38Hy/61/O5Bp6yUG8J5XQCeNjRS0xvjlCdzKLyXCueMa+L+X2L/u9PWUsy5SVbTjGgpB8sF6UkCNsV+va7S8zCCHas2MZ7GPlxP6GZBkRPTIFR0N/Pu7wfBzDQz0t0iL4VmxBfTBQv/SxkGWZg+yHihIQP9fwdSAwD/7aVh6ItAAAAFQDSyihIUlINlswM0PJ8wXSti3yIMwAAAIB+oqzaB6ozqs8YxpN5oQOBa/9HEBQEsp8RSIlQmVubXRNgktp42n+Ii1waU9UUk8DX5ahhIeR6B7ojWkqmDAji4SKpoHf4kmr6HvYo85ZSTSx0W4YK/gJHSpDJwhlT52tAfb1JCbWSObjl09B4STv7KedCHcR5oXQvvrV+XoKOSAAAAIAue/EXrs2INw1RfaKNHC0oqOMxmRitv0BFMuNVPo1VDj39CE5kA7AHjwvS1TNeaHtK5Hhgeb6vsmLmNPTOc8xCob0ilyQbt9O0GbONeF2Ge7D2UJyULA/hxql+tCYFIC6yUrmo35fF9XiNisXLoaflk9fjp7ROWWVwnki/jstaQw== DSA #2
+prometheus.example.com,192.0.2.1,2001:db8::1 ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAB8qVcXwgBM92NCmReQlPrZAoui4Bz/mW0VUBFOpHXXW1n+15b/Y7Pc6UBd/ITTZmaBciXY+PWaSBGdwc5GdqGdLgFyJ/QAGrFMPNpVutm/82gNQzlxpNwjbMcKyiZEXzSgnjS6DzMQ0WuSMdzIBXq8OW/Kafxg4ZkU6YqALUXxlQMZuQ== ECDSA #2
+prometheus.example.com,192.0.2.1,2001:db8::1 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIBp6PVW0z2o9C4Ukv/JOgmK7QMFe1pD1s3ADFF7IQob ED25519 #2
+prometheus.example.com,192.0.2.1,2001:db8::1 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDmbUhNabB5AmBDX6GNHZ3lbn7pRxqfpW+f53QqNGlK0sLV+0gkMIrOfUp1kdE2ZLE6tfzdicatj/RlH6/wuo4yyYb+Pyx3G0vxdmAIiA4aANq38XweDucBC0TZkRWVHK+Gs5V/uV0z7N0axJvkkJujMLvST3CRiiWwlficBc6yVQ== RSA #2
+
+# Some hosts with wildcard names / IPs
+*.example.com,192.0.2.*,2001:* ssh-dss AAAAB3NzaC1kc3MAAACBAI6lz2Ip9bzE7TGuDD4SjO9S4Ac90gq0h6ai1O06eI8t/Ot2uJ5Jk2QyVr2jvIZHDl/5bwBx7+5oyjlwRoUrAPPD814wf5tU2tSnmdu1Wbf0cBswif5q0r4tevzmopp/AtgH11QHo3u0/pfyJd10qBDLV2FaYSKMmZvyPfZJ0s9pAAAAFQD5Eqjl6Rx2qVePodD9OwAPT0bU6wAAAIAfnDm6csZF0sFaJR3NIJvaYgSGr8s7cqlsk2gLltB/1wOOO2yX+NeEC+B0H93hlMfaUsPa08bwgmYxnavSMqEBpmtPceefJiEd68zwYqXd38f88wyWZ9Z5iwaI/6OVZPHzCbDxOa4ewVTevRNYUKP1xUTZNT8/gSMfZLYPk4T2AQAAAIAUKroozRMyV+3V/rxt0gFnNxRXBKk+9cl3vgsQ7ktkI9cYg7V1T2K0XF21AVMK9gODszy6PBJjV6ruXBV6TRiqIbQauivp3bHHKYsG6wiJNqwdbVwIjfvv8nn1qFoZQLXG3sdONr9NwN8KzrX89OV0BlR2dVM5qqp+YxOXymP9yg== DSA #3
+*.example.com,192.0.2.*,2001:* ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBIb3BhJZk+vUQPg5TQc1koIzuGqloCq7wjr9LjlhG24IBeiFHLsdWw74HDlH4DrOmlxToVYk2lTdnjARleRByjk= ECDSA #3
+*.example.com,192.0.2.*,2001:* ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBlYfExtYZAPqYvYdrlpGlSWhh/XNHcH3v3c2JzsVNbB ED25519 #3
+*.example.com,192.0.2.*,2001:* ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDX8F93W3SH4ZSus4XUQ2cw9dqcuyUETTlKEeGv3zlknV3YCoe2Mp04naDhiuwj8sOsytrZSESzLY1ZEyzrjxE6ZFVv8NKgck/AbRjcwlRFOcx9oKUxOrXRa0IoXlTq0kyjKCJfaHBKnGitZThknCPTbVmpATkm5xx6J0WEDozfoQ== RSA #3
+
+# Hashed hostname and address entries
+|1|z3xOIdT5ue3Vuf3MzT67kaioqjw=|GZhhe5uwDOBQrC9N4cCjpbLpSn4= ssh-dss AAAAB3NzaC1kc3MAAACBALrFy7w5ihlaOG+qR+6fj+vm5EQaO3qwxgACLcgH+VfShuOG4mkx8qFJmf+OZ3fh5iKngjNZfKtfcqI7zHWdk6378TQfQC52/kbZukjNXOLCpyNkogahcjA00onIoTK1RUDuMW28edAHwPFbpttXDTaqis+8JPMY8hZwsZGENCzTAAAAFQD6+It5vozwGgaN9ROYPMlByhi6jwAAAIBz2mcAC694vNzz9b6614gkX9d9E99PzJYfU1MPkXDziKg7MrjBw7Opd5y1jL09S3iL6lSTlHkKwVKvQ3pOwWRwXXRrKVus4I0STveoApm526jmp6mY0YEtqR98vMJ0v97h1ydt8FikKlihefCsnXVicb8887PXs2Y8C6GuFT3tfQAAAIBbmHtV5tPcrMRDkULhaQ/Whap2VKvT2DUhIHA7lx6oy/KpkltOpxDZOIGUHKqffGbiR7Jh01/y090AY5L2eCf0S2Ytx93+eADwVVpJbFJo6zSwfeey2Gm6L2oA+rCz9zTdmtZoekpD3/RAOQjnJIAPwbs7mXwabZTw4xRtiYIRrw== DSA #5
+|1|B7t/AYabn8zgwU47Cb4A/Nqt3eI=|arQPZyRphkzisr7w6wwikvhaOyE= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPIudcagzq4QPtP1jkpje34+0POLB0jwT64hqrbCqhTH2T800KDZ0h2vwlJYa3OP3Oqru9AB5pnuHsKw7mAhUGY= ECDSA #5
+|1|JR81WxEocTP5d7goIRkl8fHBbno=|l6sj6FOsoXxgEZMzn/BnOfPKN68= ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINf63qSV8rD57N+digID8t28WVhd3Yf2K2UhaoG8TsWQ ED25519 #5
+|1|W7x4zY6KtTZJgsopyOusJqvVPag=|QauLt7hKezBZFZi2i4Xopho7Nsk= ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC/C15Q4sfnk7BZff1er8bscay+5s51oD4eWArlHWMK/ZfYeeTAccTy+7B7Jv+MS4nKCpflrvJI2RQz4kS8vF0ATdBbi4jeWefStlHNg0HLhnCY7NAfDIlRdaN9lm3Pqm2vmr+CkqwcJaSpycDg8nPN9yNAuD6pv7NDuUnECezojQ== RSA #5
+
+|1|mxnU8luzqWLvfVi5qBm5xVIyCRM=|9Epopft7LBd80Bf6RmWPIpwa8yU= ssh-dss AAAAB3NzaC1kc3MAAACBAIutigAse65TCW6hHDOEGXenE9L4L0talHbs65hj3UUNtWflKdQeXLofqXgW8AwaDKmnuRPrxRoxVNXj84n45wtBEdt4ztmdAZteAbXSnHqpcxME3jDxh3EtxzGPXLs+RUmKPVguraSgo7W2oN7KFx6VM+AcAtxANSTlvDid3s47AAAAFQCd9Q3kkHSLWe77sW0eRaayI45ovwAAAIAw6srGF6xvFasI44Y3r9JJ2K+3ezozl3ldL3p2+p2HG3iWafC4SdV8pB6ZIxKlYAywiiFb3LzH/JweGFq1jtoFDRM3MlYORBevydU4zPz7b5QLDVB0sY4evYtWmg2BFJvoWRfhLnlZVW7h5N8v4fNIwdVmVsw4Ljes7iF2HRGhHgAAAIBDFT3fww2Oby1xUA6G9pDAcVikrQFqp1sJRylNTUyeyQ37SNAGzYxwHJFgQr8gZLdRQ1UW+idYpqVbVNcYFMOiw/zSqK2OfVwPZ9U+TTKdc992ChSup6vJEKM/ZVIyDWDbJr7igQ4ahy7jo9mFvm8ljN926EnspQzCvs0Dxk6tHA== DSA #6
+|1|klvLmvh2vCpkNMDEjVvrE8SJWTg=|e/dqEEBLnbgqmwEesl4cDRu/7TM= ssh-dss AAAAB3NzaC1kc3MAAACBAIutigAse65TCW6hHDOEGXenE9L4L0talHbs65hj3UUNtWflKdQeXLofqXgW8AwaDKmnuRPrxRoxVNXj84n45wtBEdt4ztmdAZteAbXSnHqpcxME3jDxh3EtxzGPXLs+RUmKPVguraSgo7W2oN7KFx6VM+AcAtxANSTlvDid3s47AAAAFQCd9Q3kkHSLWe77sW0eRaayI45ovwAAAIAw6srGF6xvFasI44Y3r9JJ2K+3ezozl3ldL3p2+p2HG3iWafC4SdV8pB6ZIxKlYAywiiFb3LzH/JweGFq1jtoFDRM3MlYORBevydU4zPz7b5QLDVB0sY4evYtWmg2BFJvoWRfhLnlZVW7h5N8v4fNIwdVmVsw4Ljes7iF2HRGhHgAAAIBDFT3fww2Oby1xUA6G9pDAcVikrQFqp1sJRylNTUyeyQ37SNAGzYxwHJFgQr8gZLdRQ1UW+idYpqVbVNcYFMOiw/zSqK2OfVwPZ9U+TTKdc992ChSup6vJEKM/ZVIyDWDbJr7igQ4ahy7jo9mFvm8ljN926EnspQzCvs0Dxk6tHA== DSA #6
+|1|wsk3ddB3UjuxEsoeNCeZjZ6NvZs=|O3O/q2Z/u7DrxoTiIq6kzCevQT0= ssh-dss AAAAB3NzaC1kc3MAAACBAIutigAse65TCW6hHDOEGXenE9L4L0talHbs65hj3UUNtWflKdQeXLofqXgW8AwaDKmnuRPrxRoxVNXj84n45wtBEdt4ztmdAZteAbXSnHqpcxME3jDxh3EtxzGPXLs+RUmKPVguraSgo7W2oN7KFx6VM+AcAtxANSTlvDid3s47AAAAFQCd9Q3kkHSLWe77sW0eRaayI45ovwAAAIAw6srGF6xvFasI44Y3r9JJ2K+3ezozl3ldL3p2+p2HG3iWafC4SdV8pB6ZIxKlYAywiiFb3LzH/JweGFq1jtoFDRM3MlYORBevydU4zPz7b5QLDVB0sY4evYtWmg2BFJvoWRfhLnlZVW7h5N8v4fNIwdVmVsw4Ljes7iF2HRGhHgAAAIBDFT3fww2Oby1xUA6G9pDAcVikrQFqp1sJRylNTUyeyQ37SNAGzYxwHJFgQr8gZLdRQ1UW+idYpqVbVNcYFMOiw/zSqK2OfVwPZ9U+TTKdc992ChSup6vJEKM/ZVIyDWDbJr7igQ4ahy7jo9mFvm8ljN926EnspQzCvs0Dxk6tHA== DSA #6
+|1|B8epmkLSni+vGZDijr/EwxeR2k4=|7ct8yzNOVJhKm3ZD2w0XIT7df8E= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBK1wRLyKtvK3Mmhd0XPkKwW4ev1KBVf8J4aG8lESq1TsaqqfOXYGyxMq5pN8fCGiD5UPOqyTYz/ZNzClRhJRHao= ECDSA #6
+|1|JojD885UhYhbCu571rgyM/5PpYU=|BJaU2aE1FebQZy3B5tzTDRWFRG0= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBK1wRLyKtvK3Mmhd0XPkKwW4ev1KBVf8J4aG8lESq1TsaqqfOXYGyxMq5pN8fCGiD5UPOqyTYz/ZNzClRhJRHao= ECDSA #6
+|1|5t7UDHDybVrDZVQPCpwdnr6nk4k=|EqJ73W/veIL3H2x+YWHcJxI5ETA= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBK1wRLyKtvK3Mmhd0XPkKwW4ev1KBVf8J4aG8lESq1TsaqqfOXYGyxMq5pN8fCGiD5UPOqyTYz/ZNzClRhJRHao= ECDSA #6
+|1|OCcBfGc/b9+ip+W6Gp+3ftdluO4=|VbrKUdzOOtIBOOmEE+jlK4SD3Xc= ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPLW0ZwCkRQldpLa4I5BpwGa/om+WE6OgC8jdVqakt0Z ED25519 #6
+|1|9fLN0YdP+BJ25lKuKvYuOdUo93w=|vZyr0rOiX01hv5XbghhHMW+Zb3U= ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPLW0ZwCkRQldpLa4I5BpwGa/om+WE6OgC8jdVqakt0Z ED25519 #6
+|1|nc9RoaaQ0s5jdPxwlUmluGHU3uk=|un6OsJajokKQ3MgyS9mfDNeyP6U= ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPLW0ZwCkRQldpLa4I5BpwGa/om+WE6OgC8jdVqakt0Z ED25519 #6
+|1|rsHB6juT9q6GOY91qOeOwL6TSJE=|ps/vXF9Izuues5PbOn887Gw/2Dg= ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQClu/3I6GG1Ai89Imnw0vXmWJ2OW0ftQwRrsbIAD0qzLFYpkJ76QWnzpCehvK9u0L5hcw7z2Y6mRLcSBsqONc+HVU73Qi7M4zHRvtjprPs3SOyLpf0J9sL1WiHBDwg2P0miHMCdqHDd5nVXkJB2d4eeecmgezGLa29NOHZjbza5yw== RSA #6
+|1|BsckdLH2aRyWQooRmv+Yo3t4dKg=|Lf3tJc5Iyx0KxNwAG89FsImsfEE= ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQClu/3I6GG1Ai89Imnw0vXmWJ2OW0ftQwRrsbIAD0qzLFYpkJ76QWnzpCehvK9u0L5hcw7z2Y6mRLcSBsqONc+HVU73Qi7M4zHRvtjprPs3SOyLpf0J9sL1WiHBDwg2P0miHMCdqHDd5nVXkJB2d4eeecmgezGLa29NOHZjbza5yw== RSA #6
+|1|plqkBA4hq7UATyd5+/Xl+zL7ghw=|stacofaUed46666mfqxp9gJFjt4= ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQClu/3I6GG1Ai89Imnw0vXmWJ2OW0ftQwRrsbIAD0qzLFYpkJ76QWnzpCehvK9u0L5hcw7z2Y6mRLcSBsqONc+HVU73Qi7M4zHRvtjprPs3SOyLpf0J9sL1WiHBDwg2P0miHMCdqHDd5nVXkJB2d4eeecmgezGLa29NOHZjbza5yw== RSA #6
+
+
+# Revoked and CA keys
+@revoked sisyphus.example.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDFP8L9REfN/iYy1KIRtFqSCn3V2+vOCpoZYENFGLdOF ED25519 #4
+@cert-authority prometheus.example.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHZd0OXHIWwK3xnjAdMZ1tojxWycdu38pORO/UX5cqsKMgGCKQVBWWO3TFk1ePkGIE9VMWT1hCGqWRRwYlH+dSE= ECDSA #4
+@cert-authority *.example.com ssh-dss AAAAB3NzaC1kc3MAAACBAKvjnFHm0VvMr5h2Zu3nURsxQKGoxm+DCzYDxRYcilK07Cm5c4XTrFbA2X86+9sGs++W7QRMcTJUYIg0a+UtIMtAjwORd6ZPXM2K5dBW+gh1oHyvKi767tWX7I2c+1ZPJDY95mUUfZQUEfdy9eGDSBmw/pSsveQ1ur6XNUh/MtP/AAAAFQDHnXk/9jBJAdce1pHtLWnbdPSGdQAAAIEAm2OLy8tZBfiEO3c3X1yyB/GTcDwrQCqRMDkhnsmrliec3dWkOfNTzu+MrdvF8ymTWLEqPpbMheYtvNyZ3TF0HO5W7aVBpdGZbOdOAIfB+6skqGbI8A5Up1d7dak/bSsqL2r5NjwbDOdq+1hBzzvbl/qjh+sQarV2zHrpKoQaV28AAACANtkBVedBbqIAdphCrN/LbUi9WlyuF9UZz+tlpVLYrj8GJVwnplV2tvOmUw6yP5/pzCimTsao8dpL5PWxm7fKxLWVxA+lEsA4WeC885CiZn8xhdaJOCN+NyJ2bqkz+4VPI7oDGBm0aFwUqJn+M1PiSgvI50XdF2dBsFRTRNY0wzA= DSA #4
+
+# Some invalid lines
+@what sisyphus.example.com ssh-dss AAAAB3NzaC1kc3MAAACBAOqffHxEW4c+Z9q/r3l4sYK8F7qrBsU8XF9upGsW62T9InROFFq9IO0x3pQ6mDA0Wtw0sqcDmkPCHPyP4Ok/fU3/drLaZusHoVYu8pBBrWsIDrKgkeX9TEodBsSrYdl4Sqtqq9EZv9+DttV6LStZrgYyUTOKwOF95wGantpLynX5AAAAFQDdt+zjRNlETDsgmxcSYFgREirJrQAAAIBQlrPaiPhR24FhnMLcHH4016vL7AqDDID6Qw7PhbXGa4/XlxWMIigjBKrIPKvnZ6p712LSnCKtcbfdx0MtmJlNa01CYqPaRhgRaf+uGdvTkTUcdaq8R5lLJL+JMNwUhcC8ijm3NqEjXjffuebGe1EzIeiITbA7Nndcd+GytwRDegAAAIEAkRYPjSVcUxfUHhHdpP6V8CuY1+CYSs9EPJ7iiWTDuXWVIBTU32oJLAnrmAcOwtIzEfPvm+rff5FI/Yhon2pB3VTXhPPEBjYzE5qANanAT4e6tzAVc5f3DUhHaDknwRYfDz86GFvuLtDjeE/UZ9t6OofYoEsCBpYozLAprBvNIQY= DSA #1
+sisyphus.example.com
+prometheus.example.com ssh-ed25519
+sisyphus.example.com ssh-dsa AAAATgAAAAdz
+sisyphus.example.com ssh-XXX AAAATgAAAAdzc2gtWFhYAAAAP0ZVQ0tPRkZGVUNLT0ZGRlVDS09GRkZVQ0tPRkZGVUNLT0ZGRlVDS09GRkZVQ0tPRkZGVUNLT0ZGRlVDS09GRg==
+prometheus.example.com ssh-rsa AAAATgAAAAdzc2gtWFhYAAAAP0ZVQ0tPRkZGVUNLT0ZGRlVDS09GRkZVQ0tPRkZGVUNLT0ZGRlVDS09GRkZVQ0tPRkZGVUNLT0ZGRlVDS09GRg==
diff --git a/regress/unittests/hostkeys/testdata/rsa1_1.pub b/regress/unittests/hostkeys/testdata/rsa1_1.pub
new file mode 100644
index 0000000..772ce9c
--- /dev/null
+++ b/regress/unittests/hostkeys/testdata/rsa1_1.pub
@@ -0,0 +1 @@
+1024 65537 153895431603677073925890314548566704948446776958334195280085080329934839226701954473292358821568047724356487621573742372399387931887004184139835510820577359977148363519970774657801798872789118894962853659233045778161859413980935372685480527355016624825696983269800574755126132814333241868538220824608980319407 RSA1 #1
diff --git a/regress/unittests/hostkeys/testdata/rsa1_2.pub b/regress/unittests/hostkeys/testdata/rsa1_2.pub
new file mode 100644
index 0000000..78794b9
--- /dev/null
+++ b/regress/unittests/hostkeys/testdata/rsa1_2.pub
@@ -0,0 +1 @@
+1024 65537 135970715082947442639683969597180728933388298633245835186618852623800675939308729462220235058285909679252157995530180587329132927339620517781785310829060832352381015614725360278571924286986474946772141568893116432268565829418506866604294073334978275702221949783314402806080929601995102334442541344606109853641 RSA1 #2
diff --git a/regress/unittests/hostkeys/testdata/rsa1_3.pub b/regress/unittests/hostkeys/testdata/rsa1_3.pub
new file mode 100644
index 0000000..0c035fe
--- /dev/null
+++ b/regress/unittests/hostkeys/testdata/rsa1_3.pub
@@ -0,0 +1 @@
+1024 65537 125895605498029643697051635076028105429632810811904702876152645261610759866299221305725069141163240694267669117205342283569102183636228981857946763978553664895308762890072813014496700601576921921752482059207749978374872713540759920335553799711267170948655579130584031555334229966603000896364091459595522912269 RSA1 #3
diff --git a/regress/unittests/hostkeys/testdata/rsa1_4.pub b/regress/unittests/hostkeys/testdata/rsa1_4.pub
new file mode 100644
index 0000000..0006442
--- /dev/null
+++ b/regress/unittests/hostkeys/testdata/rsa1_4.pub
@@ -0,0 +1 @@
+1024 65537 174143366122697048196335388217056770310345753698079464367148030836533360510864881734142526411160017107552815906024399248049666856133771656680462456979369587903909343046704480897527203474513676654933090991684252819423129896444427656841613263783484827101210734799449281639493127615902427443211183258155381810593 RSA1 #4
diff --git a/regress/unittests/hostkeys/testdata/rsa1_5.pub b/regress/unittests/hostkeys/testdata/rsa1_5.pub
new file mode 100644
index 0000000..bb53c26
--- /dev/null
+++ b/regress/unittests/hostkeys/testdata/rsa1_5.pub
@@ -0,0 +1 @@
+1024 65537 127931411493401587586867047972295564331543694182352197506125410692673654572057908999642645524647232712160516076508316152810117209181150078352725299319149726341058893406440426414316276977768958023952319602422835879783057966985348561111880658922724668687074412548487722084792283453716871417610020757212399252171 RSA1 #5
diff --git a/regress/unittests/hostkeys/testdata/rsa1_6.pub b/regress/unittests/hostkeys/testdata/rsa1_6.pub
new file mode 100644
index 0000000..85d6576
--- /dev/null
+++ b/regress/unittests/hostkeys/testdata/rsa1_6.pub
@@ -0,0 +1 @@
+1024 65537 140883028436203600354693376066567741282115117509696517282419557936340193768851493584179972504103033755515036493433917203732876685813283050574208967197963391667532902202382549275760997891673884333346000558018002659506756213191532156293935482587878596032743105911487673274674568768638010598205190227631909167257 RSA1 #6
diff --git a/regress/unittests/hostkeys/testdata/rsa_1.pub b/regress/unittests/hostkeys/testdata/rsa_1.pub
new file mode 100644
index 0000000..2b87885
--- /dev/null
+++ b/regress/unittests/hostkeys/testdata/rsa_1.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDg4hB4vAZHJ0PVRiJajOv/GlytFWNpv5/9xgB9+5BIbvp8LOrFZ5D9K0Gsmwpd4G4rfaAz8j896DhMArg0vtkilIPPGt/6VzWMERgvaIQPJ/IE99X3+fjcAG56oAWwy29JX10lQMzBPU6XJIaN/zqpkb6qUBiAHBdLpxrFBBU0/w== RSA #1
diff --git a/regress/unittests/hostkeys/testdata/rsa_2.pub b/regress/unittests/hostkeys/testdata/rsa_2.pub
new file mode 100644
index 0000000..33f1fd9
--- /dev/null
+++ b/regress/unittests/hostkeys/testdata/rsa_2.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDmbUhNabB5AmBDX6GNHZ3lbn7pRxqfpW+f53QqNGlK0sLV+0gkMIrOfUp1kdE2ZLE6tfzdicatj/RlH6/wuo4yyYb+Pyx3G0vxdmAIiA4aANq38XweDucBC0TZkRWVHK+Gs5V/uV0z7N0axJvkkJujMLvST3CRiiWwlficBc6yVQ== RSA #2
diff --git a/regress/unittests/hostkeys/testdata/rsa_3.pub b/regress/unittests/hostkeys/testdata/rsa_3.pub
new file mode 100644
index 0000000..c2f6b20
--- /dev/null
+++ b/regress/unittests/hostkeys/testdata/rsa_3.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDX8F93W3SH4ZSus4XUQ2cw9dqcuyUETTlKEeGv3zlknV3YCoe2Mp04naDhiuwj8sOsytrZSESzLY1ZEyzrjxE6ZFVv8NKgck/AbRjcwlRFOcx9oKUxOrXRa0IoXlTq0kyjKCJfaHBKnGitZThknCPTbVmpATkm5xx6J0WEDozfoQ== RSA #3
diff --git a/regress/unittests/hostkeys/testdata/rsa_4.pub b/regress/unittests/hostkeys/testdata/rsa_4.pub
new file mode 100644
index 0000000..35545a7
--- /dev/null
+++ b/regress/unittests/hostkeys/testdata/rsa_4.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDI8AdjBAozcdRnIikVlt69iyDHKyrtxmpdkbRy9bWaL86OH+PTmLUk5e+T/ufiakpeE2pm0hkE3e4Sh/FsY+rsQdRoraWVNFfchcMeVlKvuy5RZN0ElvmaQebOJUeNeBn2LLw8aL8bJ4CP/bQRKrmrSSqjz3+4H9YNVyyk1OGBPQ== RSA #4
diff --git a/regress/unittests/hostkeys/testdata/rsa_5.pub b/regress/unittests/hostkeys/testdata/rsa_5.pub
new file mode 100644
index 0000000..befbaa7
--- /dev/null
+++ b/regress/unittests/hostkeys/testdata/rsa_5.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC/C15Q4sfnk7BZff1er8bscay+5s51oD4eWArlHWMK/ZfYeeTAccTy+7B7Jv+MS4nKCpflrvJI2RQz4kS8vF0ATdBbi4jeWefStlHNg0HLhnCY7NAfDIlRdaN9lm3Pqm2vmr+CkqwcJaSpycDg8nPN9yNAuD6pv7NDuUnECezojQ== RSA #5
diff --git a/regress/unittests/hostkeys/testdata/rsa_6.pub b/regress/unittests/hostkeys/testdata/rsa_6.pub
new file mode 100644
index 0000000..393e116
--- /dev/null
+++ b/regress/unittests/hostkeys/testdata/rsa_6.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQClu/3I6GG1Ai89Imnw0vXmWJ2OW0ftQwRrsbIAD0qzLFYpkJ76QWnzpCehvK9u0L5hcw7z2Y6mRLcSBsqONc+HVU73Qi7M4zHRvtjprPs3SOyLpf0J9sL1WiHBDwg2P0miHMCdqHDd5nVXkJB2d4eeecmgezGLa29NOHZjbza5yw== RSA #6
diff --git a/regress/unittests/hostkeys/tests.c b/regress/unittests/hostkeys/tests.c
new file mode 100644
index 0000000..92c7646
--- /dev/null
+++ b/regress/unittests/hostkeys/tests.c
@@ -0,0 +1,16 @@
+/* $OpenBSD: tests.c,v 1.1 2015/02/16 22:18:34 djm Exp $ */
+/*
+ * Regress test for known_hosts-related API.
+ *
+ * Placed in the public domain
+ */
+
+void tests(void);
+void test_iterate(void); /* test_iterate.c */
+
+void
+tests(void)
+{
+ test_iterate();
+}
+
diff --git a/regress/unittests/kex/Makefile b/regress/unittests/kex/Makefile
new file mode 100644
index 0000000..5c61307
--- /dev/null
+++ b/regress/unittests/kex/Makefile
@@ -0,0 +1,29 @@
+# $OpenBSD: Makefile,v 1.5 2017/12/21 00:41:22 djm Exp $
+
+PROG=test_kex
+SRCS=tests.c test_kex.c
+
+# From usr.bin/ssh
+SRCS+=sshbuf-getput-basic.c sshbuf-getput-crypto.c sshbuf-misc.c sshbuf.c
+SRCS+=atomicio.c sshkey.c authfile.c cipher.c log.c ssh-rsa.c ssh-dss.c
+SRCS+=ssh-ecdsa.c ssh-ed25519.c mac.c umac.c umac128.c hmac.c misc.c
+SRCS+=ssherr.c uidswap.c cleanup.c xmalloc.c match.c krl.c fatal.c
+SRCS+=addrmatch.c bitmap.c packet.c dispatch.c canohost.c ssh_api.c
+SRCS+=kex.c kexc25519.c kexc25519c.c kexc25519s.c kexdh.c kexdhc.c kexdhs.c
+SRCS+=kexecdh.c kexecdhc.c kexecdhs.c kexgex.c kexgexc.c kexgexs.c
+SRCS+=dh.c compat.c
+SRCS+=ed25519.c hash.c ge25519.c fe25519.c sc25519.c verify.c
+SRCS+=cipher-chachapoly.c chacha.c poly1305.c
+SRCS+=smult_curve25519_ref.c
+
+SRCS+=digest-openssl.c
+#SRCS+=digest-libc.c
+
+REGRESS_TARGETS=run-regress-${PROG}
+
+run-regress-${PROG}: ${PROG}
+ env ${TEST_ENV} ./${PROG}
+
+.include <bsd.regress.mk>
+
+LDADD+=-lz
diff --git a/regress/unittests/kex/test_kex.c b/regress/unittests/kex/test_kex.c
new file mode 100644
index 0000000..6e5999b
--- /dev/null
+++ b/regress/unittests/kex/test_kex.c
@@ -0,0 +1,202 @@
+/* $OpenBSD: test_kex.c,v 1.2 2015/07/10 06:23:25 markus Exp $ */
+/*
+ * Regress test KEX
+ *
+ * Placed in the public domain
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <stdio.h>
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+
+#include "../test_helper/test_helper.h"
+
+#include "ssherr.h"
+#include "ssh_api.h"
+#include "sshbuf.h"
+#include "packet.h"
+#include "myproposal.h"
+
+struct ssh *active_state = NULL; /* XXX - needed for linking */
+
+void kex_tests(void);
+static int do_debug = 0;
+
+static int
+do_send_and_receive(struct ssh *from, struct ssh *to)
+{
+ u_char type;
+ size_t len;
+ const u_char *buf;
+ int r;
+
+ for (;;) {
+ if ((r = ssh_packet_next(from, &type)) != 0) {
+ fprintf(stderr, "ssh_packet_next: %s\n", ssh_err(r));
+ return r;
+ }
+ if (type != 0)
+ return 0;
+ buf = ssh_output_ptr(from, &len);
+ if (do_debug)
+ printf("%zu", len);
+ if (len == 0)
+ return 0;
+ if ((r = ssh_output_consume(from, len)) != 0 ||
+ (r = ssh_input_append(to, buf, len)) != 0)
+ return r;
+ }
+}
+
+static void
+run_kex(struct ssh *client, struct ssh *server)
+{
+ int r = 0;
+
+ while (!server->kex->done || !client->kex->done) {
+ if (do_debug)
+ printf(" S:");
+ if ((r = do_send_and_receive(server, client)))
+ break;
+ if (do_debug)
+ printf(" C:");
+ if ((r = do_send_and_receive(client, server)))
+ break;
+ }
+ if (do_debug)
+ printf("done: %s\n", ssh_err(r));
+ ASSERT_INT_EQ(r, 0);
+ ASSERT_INT_EQ(server->kex->done, 1);
+ ASSERT_INT_EQ(client->kex->done, 1);
+}
+
+static void
+do_kex_with_key(char *kex, int keytype, int bits)
+{
+ struct ssh *client = NULL, *server = NULL, *server2 = NULL;
+ struct sshkey *private, *public;
+ struct sshbuf *state;
+ struct kex_params kex_params;
+ char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT };
+ char *keyname = NULL;
+
+ TEST_START("sshkey_generate");
+ ASSERT_INT_EQ(sshkey_generate(keytype, bits, &private), 0);
+ TEST_DONE();
+
+ TEST_START("sshkey_from_private");
+ ASSERT_INT_EQ(sshkey_from_private(private, &public), 0);
+ TEST_DONE();
+
+ TEST_START("ssh_init");
+ memcpy(kex_params.proposal, myproposal, sizeof(myproposal));
+ if (kex != NULL)
+ kex_params.proposal[PROPOSAL_KEX_ALGS] = kex;
+ keyname = strdup(sshkey_ssh_name(private));
+ ASSERT_PTR_NE(keyname, NULL);
+ kex_params.proposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = keyname;
+ ASSERT_INT_EQ(ssh_init(&client, 0, &kex_params), 0);
+ ASSERT_INT_EQ(ssh_init(&server, 1, &kex_params), 0);
+ ASSERT_PTR_NE(client, NULL);
+ ASSERT_PTR_NE(server, NULL);
+ TEST_DONE();
+
+ TEST_START("ssh_add_hostkey");
+ ASSERT_INT_EQ(ssh_add_hostkey(server, private), 0);
+ ASSERT_INT_EQ(ssh_add_hostkey(client, public), 0);
+ TEST_DONE();
+
+ TEST_START("kex");
+ run_kex(client, server);
+ TEST_DONE();
+
+ TEST_START("rekeying client");
+ ASSERT_INT_EQ(kex_send_kexinit(client), 0);
+ run_kex(client, server);
+ TEST_DONE();
+
+ TEST_START("rekeying server");
+ ASSERT_INT_EQ(kex_send_kexinit(server), 0);
+ run_kex(client, server);
+ TEST_DONE();
+
+ TEST_START("ssh_packet_get_state");
+ state = sshbuf_new();
+ ASSERT_PTR_NE(state, NULL);
+ ASSERT_INT_EQ(ssh_packet_get_state(server, state), 0);
+ ASSERT_INT_GE(sshbuf_len(state), 1);
+ TEST_DONE();
+
+ TEST_START("ssh_packet_set_state");
+ server2 = NULL;
+ ASSERT_INT_EQ(ssh_init(&server2, 1, NULL), 0);
+ ASSERT_PTR_NE(server2, NULL);
+ ASSERT_INT_EQ(ssh_add_hostkey(server2, private), 0);
+ kex_free(server2->kex); /* XXX or should ssh_packet_set_state()? */
+ ASSERT_INT_EQ(ssh_packet_set_state(server2, state), 0);
+ ASSERT_INT_EQ(sshbuf_len(state), 0);
+ sshbuf_free(state);
+ ASSERT_PTR_NE(server2->kex, NULL);
+ /* XXX we need to set the callbacks */
+ server2->kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server;
+ server2->kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server;
+ server2->kex->kex[KEX_DH_GEX_SHA1] = kexgex_server;
+ server2->kex->kex[KEX_DH_GEX_SHA256] = kexgex_server;
+#ifdef OPENSSL_HAS_ECC
+ server2->kex->kex[KEX_ECDH_SHA2] = kexecdh_server;
+#endif
+ server2->kex->kex[KEX_C25519_SHA256] = kexc25519_server;
+ server2->kex->load_host_public_key = server->kex->load_host_public_key;
+ server2->kex->load_host_private_key = server->kex->load_host_private_key;
+ server2->kex->sign = server->kex->sign;
+ TEST_DONE();
+
+ TEST_START("rekeying server2");
+ ASSERT_INT_EQ(kex_send_kexinit(server2), 0);
+ run_kex(client, server2);
+ ASSERT_INT_EQ(kex_send_kexinit(client), 0);
+ run_kex(client, server2);
+ TEST_DONE();
+
+ TEST_START("cleanup");
+ sshkey_free(private);
+ sshkey_free(public);
+ ssh_free(client);
+ ssh_free(server);
+ ssh_free(server2);
+ free(keyname);
+ TEST_DONE();
+}
+
+static void
+do_kex(char *kex)
+{
+ do_kex_with_key(kex, KEY_RSA, 2048);
+ do_kex_with_key(kex, KEY_DSA, 1024);
+#ifdef OPENSSL_HAS_ECC
+ do_kex_with_key(kex, KEY_ECDSA, 256);
+#endif
+ do_kex_with_key(kex, KEY_ED25519, 256);
+}
+
+void
+kex_tests(void)
+{
+ do_kex("curve25519-sha256@libssh.org");
+#ifdef OPENSSL_HAS_ECC
+ do_kex("ecdh-sha2-nistp256");
+ do_kex("ecdh-sha2-nistp384");
+ do_kex("ecdh-sha2-nistp521");
+#endif
+ do_kex("diffie-hellman-group-exchange-sha256");
+ do_kex("diffie-hellman-group-exchange-sha1");
+ do_kex("diffie-hellman-group14-sha1");
+ do_kex("diffie-hellman-group1-sha1");
+}
diff --git a/regress/unittests/kex/tests.c b/regress/unittests/kex/tests.c
new file mode 100644
index 0000000..e7036ec
--- /dev/null
+++ b/regress/unittests/kex/tests.c
@@ -0,0 +1,14 @@
+/* $OpenBSD: tests.c,v 1.1 2015/01/15 23:41:29 markus Exp $ */
+/*
+ * Placed in the public domain
+ */
+
+#include "../test_helper/test_helper.h"
+
+void kex_tests(void);
+
+void
+tests(void)
+{
+ kex_tests();
+}
diff --git a/regress/unittests/match/Makefile b/regress/unittests/match/Makefile
new file mode 100644
index 0000000..87e7582
--- /dev/null
+++ b/regress/unittests/match/Makefile
@@ -0,0 +1,16 @@
+# $OpenBSD: Makefile,v 1.4 2017/12/21 03:01:49 djm Exp $
+
+PROG=test_match
+SRCS=tests.c
+
+# From usr.bin/ssh
+SRCS+=sshbuf-getput-basic.c sshbuf-getput-crypto.c sshbuf-misc.c sshbuf.c
+SRCS+=match.c misc.c log.c uidswap.c fatal.c ssherr.c addrmatch.c xmalloc.c
+SRCS+=cleanup.c atomicio.c
+
+REGRESS_TARGETS=run-regress-${PROG}
+
+run-regress-${PROG}: ${PROG}
+ env ${TEST_ENV} ./${PROG}
+
+.include <bsd.regress.mk>
diff --git a/regress/unittests/match/tests.c b/regress/unittests/match/tests.c
new file mode 100644
index 0000000..3d9af55
--- /dev/null
+++ b/regress/unittests/match/tests.c
@@ -0,0 +1,132 @@
+/* $OpenBSD: tests.c,v 1.5 2018/07/04 13:51:45 djm Exp $ */
+/*
+ * Regress test for matching functions
+ *
+ * Placed in the public domain
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <stdio.h>
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+
+#include "../test_helper/test_helper.h"
+
+#include "match.h"
+
+void
+tests(void)
+{
+ TEST_START("match_pattern");
+ ASSERT_INT_EQ(match_pattern("", ""), 1);
+ ASSERT_INT_EQ(match_pattern("", "aaa"), 0);
+ ASSERT_INT_EQ(match_pattern("aaa", ""), 0);
+ ASSERT_INT_EQ(match_pattern("aaa", "aaaa"), 0);
+ ASSERT_INT_EQ(match_pattern("aaaa", "aaa"), 0);
+ TEST_DONE();
+
+ TEST_START("match_pattern wildcard");
+ ASSERT_INT_EQ(match_pattern("", "*"), 1);
+ ASSERT_INT_EQ(match_pattern("a", "?"), 1);
+ ASSERT_INT_EQ(match_pattern("aa", "a?"), 1);
+ ASSERT_INT_EQ(match_pattern("a", "*"), 1);
+ ASSERT_INT_EQ(match_pattern("aa", "a*"), 1);
+ ASSERT_INT_EQ(match_pattern("aa", "?*"), 1);
+ ASSERT_INT_EQ(match_pattern("aa", "**"), 1);
+ ASSERT_INT_EQ(match_pattern("aa", "?a"), 1);
+ ASSERT_INT_EQ(match_pattern("aa", "*a"), 1);
+ ASSERT_INT_EQ(match_pattern("ba", "a?"), 0);
+ ASSERT_INT_EQ(match_pattern("ba", "a*"), 0);
+ ASSERT_INT_EQ(match_pattern("ab", "?a"), 0);
+ ASSERT_INT_EQ(match_pattern("ab", "*a"), 0);
+ TEST_DONE();
+
+ TEST_START("match_pattern_list");
+ ASSERT_INT_EQ(match_pattern_list("", "", 0), 0); /* no patterns */
+ ASSERT_INT_EQ(match_pattern_list("", "*", 0), 1);
+ ASSERT_INT_EQ(match_pattern_list("", "!*", 0), -1);
+ ASSERT_INT_EQ(match_pattern_list("", "!a,*", 0), 1);
+ ASSERT_INT_EQ(match_pattern_list("", "*,!a", 0), 1);
+ ASSERT_INT_EQ(match_pattern_list("", "a,!*", 0), -1);
+ ASSERT_INT_EQ(match_pattern_list("", "!*,a", 0), -1);
+ ASSERT_INT_EQ(match_pattern_list("a", "", 0), 0);
+ ASSERT_INT_EQ(match_pattern_list("a", "*", 0), 1);
+ ASSERT_INT_EQ(match_pattern_list("a", "!*", 0), -1);
+ ASSERT_INT_EQ(match_pattern_list("a", "!a", 0), -1);
+ /* XXX negated ASSERT_INT_EQ(match_pattern_list("a", "!b", 0), 1); */
+ ASSERT_INT_EQ(match_pattern_list("a", "!a,*", 0), -1);
+ ASSERT_INT_EQ(match_pattern_list("b", "!a,*", 0), 1);
+ ASSERT_INT_EQ(match_pattern_list("a", "*,!a", 0), -1);
+ ASSERT_INT_EQ(match_pattern_list("b", "*,!a", 0), 1);
+ ASSERT_INT_EQ(match_pattern_list("a", "a,!*", 0), -1);
+ ASSERT_INT_EQ(match_pattern_list("b", "a,!*", 0), -1);
+ ASSERT_INT_EQ(match_pattern_list("a", "a,!a", 0), -1);
+ /* XXX negated ASSERT_INT_EQ(match_pattern_list("b", "a,!a", 0), 1); */
+ ASSERT_INT_EQ(match_pattern_list("a", "!*,a", 0), -1);
+ ASSERT_INT_EQ(match_pattern_list("b", "!*,a", 0), -1);
+ TEST_DONE();
+
+ TEST_START("match_pattern_list lowercase");
+ ASSERT_INT_EQ(match_pattern_list("abc", "ABC", 0), 0);
+ ASSERT_INT_EQ(match_pattern_list("ABC", "abc", 0), 0);
+ ASSERT_INT_EQ(match_pattern_list("abc", "ABC", 1), 1);
+ ASSERT_INT_EQ(match_pattern_list("ABC", "abc", 1), 0);
+ TEST_DONE();
+
+ TEST_START("addr_match_list");
+ ASSERT_INT_EQ(addr_match_list("127.0.0.1", "127.0.0.1/44"), -2);
+ ASSERT_INT_EQ(addr_match_list(NULL, "127.0.0.1/44"), -2);
+ ASSERT_INT_EQ(addr_match_list("a", "*"), 0);
+ ASSERT_INT_EQ(addr_match_list("127.0.0.1", "*"), 1);
+ ASSERT_INT_EQ(addr_match_list(NULL, "*"), 0);
+ ASSERT_INT_EQ(addr_match_list("127.0.0.1", "127.0.0.1"), 1);
+ ASSERT_INT_EQ(addr_match_list("127.0.0.1", "127.0.0.2"), 0);
+ ASSERT_INT_EQ(addr_match_list("127.0.0.1", "!127.0.0.1"), -1);
+ /* XXX negated ASSERT_INT_EQ(addr_match_list("127.0.0.1", "!127.0.0.2"), 1); */
+ ASSERT_INT_EQ(addr_match_list("127.0.0.255", "127.0.0.0/24"), 1);
+ ASSERT_INT_EQ(addr_match_list("127.0.1.1", "127.0.0.0/24"), 0);
+ ASSERT_INT_EQ(addr_match_list("127.0.0.1", "127.0.0.0/24"), 1);
+ ASSERT_INT_EQ(addr_match_list("127.0.0.1", "127.0.1.0/24"), 0);
+ ASSERT_INT_EQ(addr_match_list("127.0.0.1", "!127.0.0.0/24"), -1);
+ /* XXX negated ASSERT_INT_EQ(addr_match_list("127.0.0.1", "!127.0.1.0/24"), 1); */
+ ASSERT_INT_EQ(addr_match_list("127.0.0.1", "10.0.0.1,!127.0.0.1"), -1);
+ ASSERT_INT_EQ(addr_match_list("127.0.0.1", "!127.0.0.1,10.0.0.1"), -1);
+ ASSERT_INT_EQ(addr_match_list("127.0.0.1", "10.0.0.1,127.0.0.2"), 0);
+ ASSERT_INT_EQ(addr_match_list("127.0.0.1", "127.0.0.2,10.0.0.1"), 0);
+ /* XXX negated ASSERT_INT_EQ(addr_match_list("127.0.0.1", "10.0.0.1,!127.0.0.2"), 1); */
+ /* XXX negated ASSERT_INT_EQ(addr_match_list("127.0.0.1", "!127.0.0.2,10.0.0.1"), 1); */
+ TEST_DONE();
+
+#define CHECK_FILTER(string,filter,expected) \
+ do { \
+ char *result = match_filter_blacklist((string), (filter)); \
+ ASSERT_STRING_EQ(result, expected); \
+ free(result); \
+ } while (0)
+
+ TEST_START("match_filter_list");
+ CHECK_FILTER("a,b,c", "", "a,b,c");
+ CHECK_FILTER("a,b,c", "a", "b,c");
+ CHECK_FILTER("a,b,c", "b", "a,c");
+ CHECK_FILTER("a,b,c", "c", "a,b");
+ CHECK_FILTER("a,b,c", "a,b", "c");
+ CHECK_FILTER("a,b,c", "a,c", "b");
+ CHECK_FILTER("a,b,c", "b,c", "a");
+ CHECK_FILTER("a,b,c", "a,b,c", "");
+ CHECK_FILTER("a,b,c", "b,c", "a");
+ CHECK_FILTER("", "a,b,c", "");
+ TEST_DONE();
+/*
+ * XXX TODO
+ * int match_host_and_ip(const char *, const char *, const char *);
+ * int match_user(const char *, const char *, const char *, const char *);
+ * char *match_list(const char *, const char *, u_int *);
+ * int addr_match_cidr_list(const char *, const char *);
+ */
+}
diff --git a/regress/unittests/sshbuf/Makefile b/regress/unittests/sshbuf/Makefile
new file mode 100644
index 0000000..81d4f27
--- /dev/null
+++ b/regress/unittests/sshbuf/Makefile
@@ -0,0 +1,22 @@
+# $OpenBSD: Makefile,v 1.6 2017/12/21 00:41:22 djm Exp $
+
+.include <bsd.regress.mk>
+
+PROG=test_sshbuf
+SRCS=tests.c
+SRCS+=test_sshbuf.c
+SRCS+=test_sshbuf_getput_basic.c
+SRCS+=test_sshbuf_getput_crypto.c
+SRCS+=test_sshbuf_misc.c
+SRCS+=test_sshbuf_fuzz.c
+SRCS+=test_sshbuf_getput_fuzz.c
+SRCS+=test_sshbuf_fixed.c
+
+# From usr.bin/ssh
+SRCS+=sshbuf-getput-basic.c sshbuf-getput-crypto.c sshbuf-misc.c sshbuf.c
+SRCS+=atomicio.c
+
+run-regress-${PROG}: ${PROG}
+ env ${TEST_ENV} ./${PROG}
+
+
diff --git a/regress/unittests/sshbuf/test_sshbuf.c b/regress/unittests/sshbuf/test_sshbuf.c
new file mode 100644
index 0000000..ee77d69
--- /dev/null
+++ b/regress/unittests/sshbuf/test_sshbuf.c
@@ -0,0 +1,240 @@
+/* $OpenBSD: test_sshbuf.c,v 1.1 2014/04/30 05:32:00 djm Exp $ */
+/*
+ * Regress test for sshbuf.h buffer API
+ *
+ * Placed in the public domain
+ */
+
+#define SSHBUF_INTERNAL 1 /* access internals for testing */
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <stdio.h>
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+
+#include "../test_helper/test_helper.h"
+
+#include "ssherr.h"
+#include "sshbuf.h"
+
+void sshbuf_tests(void);
+
+void
+sshbuf_tests(void)
+{
+ struct sshbuf *p1;
+ const u_char *cdp;
+ u_char *dp;
+ size_t sz;
+ int r;
+
+ TEST_START("allocate sshbuf");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ TEST_DONE();
+
+ TEST_START("max size on fresh buffer");
+ ASSERT_SIZE_T_GT(sshbuf_max_size(p1), 0);
+ TEST_DONE();
+
+ TEST_START("available on fresh buffer");
+ ASSERT_SIZE_T_GT(sshbuf_avail(p1), 0);
+ TEST_DONE();
+
+ TEST_START("len = 0 on empty buffer");
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0);
+ TEST_DONE();
+
+ TEST_START("set valid max size");
+ ASSERT_INT_EQ(sshbuf_set_max_size(p1, 65536), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_max_size(p1), 65536);
+ TEST_DONE();
+
+ TEST_START("available on limited buffer");
+ ASSERT_SIZE_T_EQ(sshbuf_avail(p1), 65536);
+ TEST_DONE();
+
+ TEST_START("free");
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("consume on empty buffer");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_consume(p1, 0), 0);
+ ASSERT_INT_EQ(sshbuf_consume(p1, 1), SSH_ERR_MESSAGE_INCOMPLETE);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("consume_end on empty buffer");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_consume_end(p1, 0), 0);
+ ASSERT_INT_EQ(sshbuf_consume_end(p1, 1), SSH_ERR_MESSAGE_INCOMPLETE);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("reserve space");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ r = sshbuf_reserve(p1, 1, &dp);
+ ASSERT_INT_EQ(r, 0);
+ ASSERT_PTR_NE(dp, NULL);
+ *dp = 0x11;
+ r = sshbuf_reserve(p1, 3, &dp);
+ ASSERT_INT_EQ(r, 0);
+ ASSERT_PTR_NE(dp, NULL);
+ *dp++ = 0x22;
+ *dp++ = 0x33;
+ *dp++ = 0x44;
+ TEST_DONE();
+
+ TEST_START("sshbuf_len on filled buffer");
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 4);
+ TEST_DONE();
+
+ TEST_START("sshbuf_ptr on filled buffer");
+ cdp = sshbuf_ptr(p1);
+ ASSERT_PTR_NE(cdp, NULL);
+ ASSERT_U8_EQ(cdp[0], 0x11);
+ ASSERT_U8_EQ(cdp[1], 0x22);
+ ASSERT_U8_EQ(cdp[2], 0x33);
+ ASSERT_U8_EQ(cdp[3], 0x44);
+ TEST_DONE();
+
+ TEST_START("consume on filled buffer");
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 4);
+ ASSERT_INT_EQ(sshbuf_consume(p1, 0), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 4);
+ r = sshbuf_consume(p1, 64);
+ ASSERT_INT_EQ(r, SSH_ERR_MESSAGE_INCOMPLETE);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 4);
+ ASSERT_INT_EQ(sshbuf_consume(p1, 1), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 3);
+ cdp = sshbuf_ptr(p1);
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_U8_EQ(cdp[0], 0x22);
+ ASSERT_INT_EQ(sshbuf_consume(p1, 2), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 1);
+ cdp = sshbuf_ptr(p1);
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_U8_EQ(cdp[0], 0x44);
+ r = sshbuf_consume(p1, 2);
+ ASSERT_INT_EQ(r, SSH_ERR_MESSAGE_INCOMPLETE);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 1);
+ ASSERT_INT_EQ(sshbuf_consume(p1, 1), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0);
+ r = sshbuf_consume(p1, 1);
+ ASSERT_INT_EQ(r, SSH_ERR_MESSAGE_INCOMPLETE);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("consume_end on filled buffer");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ r = sshbuf_reserve(p1, 4, &dp);
+ ASSERT_INT_EQ(r, 0);
+ ASSERT_PTR_NE(dp, NULL);
+ *dp++ = 0x11;
+ *dp++ = 0x22;
+ *dp++ = 0x33;
+ *dp++ = 0x44;
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 4);
+ r = sshbuf_consume_end(p1, 5);
+ ASSERT_INT_EQ(r, SSH_ERR_MESSAGE_INCOMPLETE);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 4);
+ ASSERT_INT_EQ(sshbuf_consume_end(p1, 3), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 1);
+ cdp = sshbuf_ptr(p1);
+ ASSERT_PTR_NE(cdp, NULL);
+ ASSERT_U8_EQ(*cdp, 0x11);
+ r = sshbuf_consume_end(p1, 2);
+ ASSERT_INT_EQ(r, SSH_ERR_MESSAGE_INCOMPLETE);
+ ASSERT_INT_EQ(sshbuf_consume_end(p1, 1), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("fill limited buffer");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_set_max_size(p1, 1223), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_max_size(p1), 1223);
+ ASSERT_SIZE_T_EQ(sshbuf_avail(p1), 1223);
+ r = sshbuf_reserve(p1, 1223, &dp);
+ ASSERT_INT_EQ(r, 0);
+ ASSERT_PTR_NE(dp, NULL);
+ memset(dp, 0xd7, 1223);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 1223);
+ ASSERT_SIZE_T_EQ(sshbuf_avail(p1), 0);
+ r = sshbuf_reserve(p1, 1, &dp);
+ ASSERT_INT_EQ(r, SSH_ERR_NO_BUFFER_SPACE);
+ ASSERT_PTR_EQ(dp, NULL);
+ TEST_DONE();
+
+ TEST_START("consume and force compaction");
+ ASSERT_INT_EQ(sshbuf_consume(p1, 223), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 1000);
+ ASSERT_SIZE_T_EQ(sshbuf_avail(p1), 223);
+ r = sshbuf_reserve(p1, 224, &dp);
+ ASSERT_INT_EQ(r, SSH_ERR_NO_BUFFER_SPACE);
+ ASSERT_PTR_EQ(dp, NULL);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 1000);
+ ASSERT_SIZE_T_EQ(sshbuf_avail(p1), 223);
+ r = sshbuf_reserve(p1, 223, &dp);
+ ASSERT_INT_EQ(r, 0);
+ ASSERT_PTR_NE(dp, NULL);
+ memset(dp, 0x7d, 223);
+ cdp = sshbuf_ptr(p1);
+ ASSERT_PTR_NE(cdp, NULL);
+ ASSERT_MEM_FILLED_EQ(cdp, 0xd7, 1000);
+ ASSERT_MEM_FILLED_EQ(cdp + 1000, 0x7d, 223);
+ TEST_DONE();
+
+ TEST_START("resize full buffer");
+ r = sshbuf_set_max_size(p1, 1000);
+ ASSERT_INT_EQ(r, SSH_ERR_NO_BUFFER_SPACE);
+ sz = roundup(1223 + SSHBUF_SIZE_INC * 3, SSHBUF_SIZE_INC);
+ ASSERT_INT_EQ(sshbuf_set_max_size(p1, sz), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_max_size(p1), sz);
+ ASSERT_SIZE_T_EQ(sshbuf_avail(p1), sz - 1223);
+ ASSERT_INT_EQ(sshbuf_len(p1), 1223);
+ TEST_DONE();
+
+ /* NB. uses sshbuf internals */
+ TEST_START("alloc chunking");
+ r = sshbuf_reserve(p1, 1, &dp);
+ ASSERT_INT_EQ(r, 0);
+ ASSERT_PTR_NE(dp, NULL);
+ *dp = 0xff;
+ cdp = sshbuf_ptr(p1);
+ ASSERT_PTR_NE(cdp, NULL);
+ ASSERT_MEM_FILLED_EQ(cdp, 0xd7, 1000);
+ ASSERT_MEM_FILLED_EQ(cdp + 1000, 0x7d, 223);
+ ASSERT_MEM_FILLED_EQ(cdp + 1223, 0xff, 1);
+ ASSERT_SIZE_T_EQ(sshbuf_alloc(p1) % SSHBUF_SIZE_INC, 0);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("reset buffer");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_set_max_size(p1, 1223), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_max_size(p1), 1223);
+ r = sshbuf_reserve(p1, 1223, &dp);
+ ASSERT_INT_EQ(r, 0);
+ ASSERT_PTR_NE(dp, NULL);
+ memset(dp, 0xd7, 1223);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 1223);
+ sshbuf_reset(p1);
+ ASSERT_SIZE_T_EQ(sshbuf_max_size(p1), 1223);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_avail(p1), 1223);
+ sshbuf_free(p1);
+ TEST_DONE();
+}
diff --git a/regress/unittests/sshbuf/test_sshbuf_fixed.c b/regress/unittests/sshbuf/test_sshbuf_fixed.c
new file mode 100644
index 0000000..df4925f
--- /dev/null
+++ b/regress/unittests/sshbuf/test_sshbuf_fixed.c
@@ -0,0 +1,126 @@
+/* $OpenBSD: test_sshbuf_fixed.c,v 1.1 2014/04/30 05:32:00 djm Exp $ */
+/*
+ * Regress test for sshbuf.h buffer API
+ *
+ * Placed in the public domain
+ */
+
+#define SSHBUF_INTERNAL 1 /* access internals for testing */
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <stdio.h>
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+
+#include "../test_helper/test_helper.h"
+
+#include "sshbuf.h"
+#include "ssherr.h"
+
+void sshbuf_fixed(void);
+
+const u_char test_buf[] = "\x01\x12\x34\x56\x78\x00\x00\x00\x05hello";
+
+void
+sshbuf_fixed(void)
+{
+ struct sshbuf *p1, *p2, *p3;
+ u_char c;
+ char *s;
+ u_int i;
+ size_t l;
+
+ TEST_START("sshbuf_from");
+ p1 = sshbuf_from(test_buf, sizeof(test_buf));
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_PTR_EQ(sshbuf_mutable_ptr(p1), NULL);
+ ASSERT_INT_EQ(sshbuf_check_reserve(p1, 1), SSH_ERR_BUFFER_READ_ONLY);
+ ASSERT_INT_EQ(sshbuf_reserve(p1, 1, NULL), SSH_ERR_BUFFER_READ_ONLY);
+ ASSERT_INT_EQ(sshbuf_set_max_size(p1, 200), SSH_ERR_BUFFER_READ_ONLY);
+ ASSERT_INT_EQ(sshbuf_put_u32(p1, 0x12345678), SSH_ERR_BUFFER_READ_ONLY);
+ ASSERT_SIZE_T_EQ(sshbuf_avail(p1), 0);
+ ASSERT_PTR_EQ(sshbuf_ptr(p1), test_buf);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_from data");
+ p1 = sshbuf_from(test_buf, sizeof(test_buf) - 1);
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_PTR_EQ(sshbuf_ptr(p1), test_buf);
+ ASSERT_INT_EQ(sshbuf_get_u8(p1, &c), 0);
+ ASSERT_PTR_EQ(sshbuf_ptr(p1), test_buf + 1);
+ ASSERT_U8_EQ(c, 1);
+ ASSERT_INT_EQ(sshbuf_get_u32(p1, &i), 0);
+ ASSERT_PTR_EQ(sshbuf_ptr(p1), test_buf + 5);
+ ASSERT_U32_EQ(i, 0x12345678);
+ ASSERT_INT_EQ(sshbuf_get_cstring(p1, &s, &l), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0);
+ ASSERT_STRING_EQ(s, "hello");
+ ASSERT_SIZE_T_EQ(l, 5);
+ sshbuf_free(p1);
+ free(s);
+ TEST_DONE();
+
+ TEST_START("sshbuf_fromb ");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_U_INT_EQ(sshbuf_refcount(p1), 1);
+ ASSERT_PTR_EQ(sshbuf_parent(p1), NULL);
+ ASSERT_INT_EQ(sshbuf_put(p1, test_buf, sizeof(test_buf) - 1), 0);
+ p2 = sshbuf_fromb(p1);
+ ASSERT_PTR_NE(p2, NULL);
+ ASSERT_U_INT_EQ(sshbuf_refcount(p1), 2);
+ ASSERT_PTR_EQ(sshbuf_parent(p1), NULL);
+ ASSERT_PTR_EQ(sshbuf_parent(p2), p1);
+ ASSERT_PTR_EQ(sshbuf_ptr(p2), sshbuf_ptr(p1));
+ ASSERT_PTR_NE(sshbuf_ptr(p1), NULL);
+ ASSERT_PTR_NE(sshbuf_ptr(p2), NULL);
+ ASSERT_PTR_EQ(sshbuf_mutable_ptr(p1), NULL);
+ ASSERT_PTR_EQ(sshbuf_mutable_ptr(p2), NULL);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), sshbuf_len(p2));
+ ASSERT_INT_EQ(sshbuf_get_u8(p2, &c), 0);
+ ASSERT_PTR_EQ(sshbuf_ptr(p2), sshbuf_ptr(p1) + 1);
+ ASSERT_U8_EQ(c, 1);
+ ASSERT_INT_EQ(sshbuf_get_u32(p2, &i), 0);
+ ASSERT_PTR_EQ(sshbuf_ptr(p2), sshbuf_ptr(p1) + 5);
+ ASSERT_U32_EQ(i, 0x12345678);
+ ASSERT_INT_EQ(sshbuf_get_cstring(p2, &s, &l), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p2), 0);
+ ASSERT_STRING_EQ(s, "hello");
+ ASSERT_SIZE_T_EQ(l, 5);
+ sshbuf_free(p1);
+ ASSERT_U_INT_EQ(sshbuf_refcount(p1), 1);
+ sshbuf_free(p2);
+ free(s);
+ TEST_DONE();
+
+ TEST_START("sshbuf_froms");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_u8(p1, 0x01), 0);
+ ASSERT_INT_EQ(sshbuf_put_u32(p1, 0x12345678), 0);
+ ASSERT_INT_EQ(sshbuf_put_cstring(p1, "hello"), 0);
+ p2 = sshbuf_new();
+ ASSERT_PTR_NE(p2, NULL);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(test_buf) - 1);
+ ASSERT_INT_EQ(sshbuf_put_stringb(p2, p1), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p2), sizeof(test_buf) + 4 - 1);
+ ASSERT_INT_EQ(sshbuf_froms(p2, &p3), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p2), 0);
+ ASSERT_PTR_NE(p3, NULL);
+ ASSERT_PTR_NE(sshbuf_ptr(p3), NULL);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p3), sizeof(test_buf) - 1);
+ ASSERT_MEM_EQ(sshbuf_ptr(p3), test_buf, sizeof(test_buf) - 1);
+ sshbuf_free(p3);
+ ASSERT_INT_EQ(sshbuf_put_stringb(p2, p1), 0);
+ ASSERT_INT_EQ(sshbuf_consume_end(p2, 1), 0);
+ ASSERT_INT_EQ(sshbuf_froms(p2, &p3), SSH_ERR_MESSAGE_INCOMPLETE);
+ ASSERT_PTR_EQ(p3, NULL);
+ sshbuf_free(p2);
+ sshbuf_free(p1);
+}
diff --git a/regress/unittests/sshbuf/test_sshbuf_fuzz.c b/regress/unittests/sshbuf/test_sshbuf_fuzz.c
new file mode 100644
index 0000000..c52376b
--- /dev/null
+++ b/regress/unittests/sshbuf/test_sshbuf_fuzz.c
@@ -0,0 +1,127 @@
+/* $OpenBSD: test_sshbuf_fuzz.c,v 1.1 2014/04/30 05:32:00 djm Exp $ */
+/*
+ * Regress test for sshbuf.h buffer API
+ *
+ * Placed in the public domain
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <stdio.h>
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+
+#include "../test_helper/test_helper.h"
+
+#include "ssherr.h"
+#include "sshbuf.h"
+
+#define NUM_FUZZ_TESTS (1 << 18)
+
+void sshbuf_fuzz_tests(void);
+
+void
+sshbuf_fuzz_tests(void)
+{
+ struct sshbuf *p1;
+ u_char *dp;
+ size_t sz, sz2, i;
+ u_int32_t r;
+ int ret;
+
+ /* NB. uses sshbuf internals */
+ TEST_START("fuzz alloc/dealloc");
+ p1 = sshbuf_new();
+ ASSERT_INT_EQ(sshbuf_set_max_size(p1, 16 * 1024), 0);
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_PTR_NE(sshbuf_ptr(p1), NULL);
+ ASSERT_MEM_ZERO_NE(sshbuf_ptr(p1), sshbuf_len(p1));
+ for (i = 0; i < NUM_FUZZ_TESTS; i++) {
+ r = arc4random_uniform(10);
+ if (r == 0) {
+ /* 10% chance: small reserve */
+ r = arc4random_uniform(10);
+ fuzz_reserve:
+ sz = sshbuf_avail(p1);
+ sz2 = sshbuf_len(p1);
+ ret = sshbuf_reserve(p1, r, &dp);
+ if (ret < 0) {
+ ASSERT_PTR_EQ(dp, NULL);
+ ASSERT_SIZE_T_LT(sz, r);
+ ASSERT_SIZE_T_EQ(sshbuf_avail(p1), sz);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), sz2);
+ } else {
+ ASSERT_PTR_NE(dp, NULL);
+ ASSERT_SIZE_T_GE(sz, r);
+ ASSERT_SIZE_T_EQ(sshbuf_avail(p1), sz - r);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), sz2 + r);
+ memset(dp, arc4random_uniform(255) + 1, r);
+ }
+ } else if (r < 3) {
+ /* 20% chance: big reserve */
+ r = arc4random_uniform(8 * 1024);
+ goto fuzz_reserve;
+ } else if (r == 3) {
+ /* 10% chance: small consume */
+ r = arc4random_uniform(10);
+ fuzz_consume:
+ sz = sshbuf_avail(p1);
+ sz2 = sshbuf_len(p1);
+ /* 50% change consume from end, otherwise start */
+ ret = ((arc4random() & 1) ?
+ sshbuf_consume : sshbuf_consume_end)(p1, r);
+ if (ret < 0) {
+ ASSERT_SIZE_T_LT(sz2, r);
+ ASSERT_SIZE_T_EQ(sshbuf_avail(p1), sz);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), sz2);
+ } else {
+ ASSERT_SIZE_T_GE(sz2, r);
+ ASSERT_SIZE_T_EQ(sshbuf_avail(p1), sz + r);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), sz2 - r);
+ }
+ } else if (r < 8) {
+ /* 40% chance: big consume */
+ r = arc4random_uniform(2 * 1024);
+ goto fuzz_consume;
+ } else if (r == 8) {
+ /* 10% chance: reset max size */
+ r = arc4random_uniform(16 * 1024);
+ sz = sshbuf_max_size(p1);
+ if (sshbuf_set_max_size(p1, r) < 0)
+ ASSERT_SIZE_T_EQ(sshbuf_max_size(p1), sz);
+ else
+ ASSERT_SIZE_T_EQ(sshbuf_max_size(p1), r);
+ } else {
+ if (arc4random_uniform(8192) == 0) {
+ /* tiny chance: new buffer */
+ ASSERT_PTR_NE(sshbuf_ptr(p1), NULL);
+ ASSERT_MEM_ZERO_NE(sshbuf_ptr(p1), sshbuf_len(p1));
+ sshbuf_free(p1);
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_set_max_size(p1,
+ 16 * 1024), 0);
+ } else {
+ /* Almost 10%: giant reserve */
+ /* use arc4random_buf for r > 2^32 on 64 bit */
+ arc4random_buf(&r, sizeof(r));
+ while (r < SSHBUF_SIZE_MAX / 2) {
+ r <<= 1;
+ r |= arc4random() & 1;
+ }
+ goto fuzz_reserve;
+ }
+ }
+ ASSERT_PTR_NE(sshbuf_ptr(p1), NULL);
+ ASSERT_SIZE_T_LE(sshbuf_max_size(p1), 16 * 1024);
+ }
+ ASSERT_PTR_NE(sshbuf_ptr(p1), NULL);
+ ASSERT_MEM_ZERO_NE(sshbuf_ptr(p1), sshbuf_len(p1));
+ sshbuf_free(p1);
+ TEST_DONE();
+}
diff --git a/regress/unittests/sshbuf/test_sshbuf_getput_basic.c b/regress/unittests/sshbuf/test_sshbuf_getput_basic.c
new file mode 100644
index 0000000..966e843
--- /dev/null
+++ b/regress/unittests/sshbuf/test_sshbuf_getput_basic.c
@@ -0,0 +1,484 @@
+/* $OpenBSD: test_sshbuf_getput_basic.c,v 1.1 2014/04/30 05:32:00 djm Exp $ */
+/*
+ * Regress test for sshbuf.h buffer API
+ *
+ * Placed in the public domain
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <stdio.h>
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+
+#include "../test_helper/test_helper.h"
+#include "ssherr.h"
+#include "sshbuf.h"
+
+void sshbuf_getput_basic_tests(void);
+
+void
+sshbuf_getput_basic_tests(void)
+{
+ struct sshbuf *p1, *p2;
+ const u_char *cd;
+ u_char *d, d2[32], x[] = {
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x00, 0x99
+ };
+ u_int64_t v64;
+ u_int32_t v32;
+ u_int16_t v16;
+ u_char v8;
+ size_t s;
+ char *s2;
+ int r;
+ u_char bn1[] = { 0x00, 0x00, 0x00 };
+ u_char bn2[] = { 0x00, 0x00, 0x01, 0x02 };
+ u_char bn3[] = { 0x00, 0x80, 0x09 };
+ u_char bn_exp1[] = { 0x00, 0x00, 0x00, 0x00 };
+ u_char bn_exp2[] = { 0x00, 0x00, 0x00, 0x02, 0x01, 0x02 };
+ u_char bn_exp3[] = { 0x00, 0x00, 0x00, 0x03, 0x00, 0x80, 0x09 };
+
+ TEST_START("PEEK_U64");
+ ASSERT_U64_EQ(PEEK_U64(x), 0x1122334455667788ULL);
+ TEST_DONE();
+
+ TEST_START("PEEK_U32");
+ ASSERT_U32_EQ(PEEK_U32(x), 0x11223344);
+ TEST_DONE();
+
+ TEST_START("PEEK_U16");
+ ASSERT_U16_EQ(PEEK_U16(x), 0x1122);
+ TEST_DONE();
+
+ TEST_START("POKE_U64");
+ bzero(d2, sizeof(d2));
+ POKE_U64(d2, 0x1122334455667788ULL);
+ ASSERT_MEM_EQ(d2, x, 8);
+ TEST_DONE();
+
+ TEST_START("POKE_U32");
+ bzero(d2, sizeof(d2));
+ POKE_U32(d2, 0x11223344);
+ ASSERT_MEM_EQ(d2, x, 4);
+ TEST_DONE();
+
+ TEST_START("POKE_U16");
+ bzero(d2, sizeof(d2));
+ POKE_U16(d2, 0x1122);
+ ASSERT_MEM_EQ(d2, x, 2);
+ TEST_DONE();
+
+ TEST_START("sshbuf_put");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put(p1, x, 5), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 5);
+ cd = sshbuf_ptr(p1);
+ ASSERT_PTR_NE(cd, NULL);
+ ASSERT_U8_EQ(cd[0], 0x11);
+ ASSERT_U8_EQ(cd[1], 0x22);
+ ASSERT_U8_EQ(cd[2], 0x33);
+ ASSERT_U8_EQ(cd[3], 0x44);
+ ASSERT_U8_EQ(cd[4], 0x55);
+ TEST_DONE();
+
+ TEST_START("sshbuf_get");
+ ASSERT_INT_EQ(sshbuf_get(p1, d2, 4), 0);
+ ASSERT_MEM_EQ(d2, x, 4);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 1);
+ ASSERT_U8_EQ(*(sshbuf_ptr(p1)), 0x55);
+ TEST_DONE();
+
+ TEST_START("sshbuf_get truncated");
+ r = sshbuf_get(p1, d2, 4);
+ ASSERT_INT_EQ(r, SSH_ERR_MESSAGE_INCOMPLETE);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 1);
+ ASSERT_U8_EQ(*(sshbuf_ptr(p1)), 0x55);
+ TEST_DONE();
+
+ TEST_START("sshbuf_put truncated");
+ ASSERT_INT_EQ(sshbuf_set_max_size(p1, 4), 0);
+ r = sshbuf_put(p1, x, 5);
+ ASSERT_INT_EQ(r, SSH_ERR_NO_BUFFER_SPACE);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 1);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_get_u64");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put(p1, x, 10), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 10);
+ ASSERT_INT_EQ(sshbuf_get_u64(p1, &v64), 0);
+ ASSERT_U64_EQ(v64, 0x1122334455667788ULL);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2);
+ TEST_DONE();
+
+ TEST_START("sshbuf_get_u64 truncated");
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2);
+ r = sshbuf_get_u64(p1, &v64);
+ ASSERT_INT_EQ(r, SSH_ERR_MESSAGE_INCOMPLETE);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_get_u32");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put(p1, x, 10), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 10);
+ ASSERT_INT_EQ(sshbuf_get_u32(p1, &v32), 0);
+ ASSERT_U32_EQ(v32, 0x11223344);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 6);
+ ASSERT_INT_EQ(sshbuf_get_u32(p1, &v32), 0);
+ ASSERT_U32_EQ(v32, 0x55667788);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2);
+ TEST_DONE();
+
+ TEST_START("sshbuf_get_u32 truncated");
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2);
+ r = sshbuf_get_u32(p1, &v32);
+ ASSERT_INT_EQ(r, SSH_ERR_MESSAGE_INCOMPLETE);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_get_u16");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put(p1, x, 9), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 9);
+ ASSERT_INT_EQ(sshbuf_get_u16(p1, &v16), 0);
+ ASSERT_U16_EQ(v16, 0x1122);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 7);
+ ASSERT_INT_EQ(sshbuf_get_u16(p1, &v16), 0);
+ ASSERT_U16_EQ(v16, 0x3344);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 5);
+ ASSERT_INT_EQ(sshbuf_get_u16(p1, &v16), 0);
+ ASSERT_U16_EQ(v16, 0x5566);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 3);
+ ASSERT_INT_EQ(sshbuf_get_u16(p1, &v16), 0);
+ ASSERT_U16_EQ(v16, 0x7788);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_get_u16 truncated");
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 1);
+ r = sshbuf_get_u16(p1, &v16);
+ ASSERT_INT_EQ(r, SSH_ERR_MESSAGE_INCOMPLETE);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 1);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_get_u8");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put(p1, x, 2), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2);
+ ASSERT_INT_EQ(sshbuf_get_u8(p1, &v8), 0);
+ ASSERT_U8_EQ(v8, 0x11);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 1);
+ ASSERT_INT_EQ(sshbuf_get_u8(p1, &v8), 0);
+ ASSERT_U8_EQ(v8, 0x22);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0);
+ TEST_DONE();
+
+ TEST_START("sshbuf_get_u8 truncated");
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0);
+ r = sshbuf_get_u8(p1, &v8);
+ ASSERT_INT_EQ(r, SSH_ERR_MESSAGE_INCOMPLETE);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_put_u64");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_u64(p1, 0x1122334455667788ULL), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 8);
+ ASSERT_MEM_EQ(sshbuf_ptr(p1), x, 8);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_put_u64 exact");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_set_max_size(p1, 8), 0);
+ ASSERT_INT_EQ(sshbuf_put_u64(p1, 0x1122334455667788ULL), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 8);
+ ASSERT_MEM_EQ(sshbuf_ptr(p1), x, 8);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_put_u64 limited");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_set_max_size(p1, 7), 0);
+ r = sshbuf_put_u64(p1, 0x1122334455667788ULL);
+ ASSERT_INT_EQ(r, SSH_ERR_NO_BUFFER_SPACE);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_put_u32");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_u32(p1, 0x11223344), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 4);
+ ASSERT_MEM_EQ(sshbuf_ptr(p1), x, 4);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_put_u32 exact");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_set_max_size(p1, 4), 0);
+ ASSERT_INT_EQ(sshbuf_put_u32(p1, 0x11223344), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 4);
+ ASSERT_MEM_EQ(sshbuf_ptr(p1), x, 4);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_put_u32 limited");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_set_max_size(p1, 3), 0);
+ r = sshbuf_put_u32(p1, 0x11223344);
+ ASSERT_INT_EQ(r, SSH_ERR_NO_BUFFER_SPACE);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_put_u16");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_u16(p1, 0x1122), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2);
+ ASSERT_MEM_EQ(sshbuf_ptr(p1), x, 2);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_put_u16");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_set_max_size(p1, 2), 0);
+ ASSERT_INT_EQ(sshbuf_put_u16(p1, 0x1122), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2);
+ ASSERT_MEM_EQ(sshbuf_ptr(p1), x, 2);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_put_u16 limited");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_set_max_size(p1, 1), 0);
+ r = sshbuf_put_u16(p1, 0x1122);
+ ASSERT_INT_EQ(r, SSH_ERR_NO_BUFFER_SPACE);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_get_string");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_u32(p1, sizeof(x)), 0);
+ ASSERT_INT_EQ(sshbuf_put(p1, x, sizeof(x)), 0);
+ ASSERT_INT_EQ(sshbuf_put_u32(p1, sizeof(x)), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(x) + 4 + 4);
+ ASSERT_INT_EQ(sshbuf_get_string(p1, &d, &s), 0);
+ ASSERT_SIZE_T_EQ(s, sizeof(x));
+ ASSERT_MEM_EQ(d, x, sizeof(x));
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 4);
+ free(d);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_get_string exact");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_set_max_size(p1, sizeof(x) + 4), 0);
+ ASSERT_INT_EQ(sshbuf_put_u32(p1, sizeof(x)), 0);
+ ASSERT_INT_EQ(sshbuf_put(p1, x, sizeof(x)), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(x) + 4);
+ ASSERT_INT_EQ(sshbuf_get_string(p1, &d, &s), 0);
+ ASSERT_SIZE_T_EQ(s, sizeof(x));
+ ASSERT_MEM_EQ(d, x, sizeof(x));
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0);
+ free(d);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_get_string truncated");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_u32(p1, sizeof(x)), 0);
+ ASSERT_INT_EQ(sshbuf_put(p1, x, sizeof(x)), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(x) + 4);
+ ASSERT_INT_EQ(sshbuf_consume_end(p1, 1), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(x) + 3);
+ r = sshbuf_get_string(p1, &d, &s);
+ ASSERT_INT_EQ(r, SSH_ERR_MESSAGE_INCOMPLETE);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(x) + 3);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_get_string giant");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_u32(p1, 0xffffffff), 0);
+ ASSERT_INT_EQ(sshbuf_put(p1, x, sizeof(x)), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(x) + 4);
+ r = sshbuf_get_string(p1, &d, &s);
+ ASSERT_INT_EQ(r, SSH_ERR_STRING_TOO_LARGE);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(x) + 4);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_get_cstring giant");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_u32(p1, 0xffffffff), 0);
+ ASSERT_INT_EQ(sshbuf_put(p1, x, sizeof(x)), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(x) + 4);
+ r = sshbuf_get_cstring(p1, &s2, &s);
+ ASSERT_INT_EQ(r, SSH_ERR_STRING_TOO_LARGE);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(x) + 4);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_get_cstring embedded \\0");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_u32(p1, sizeof(x)), 0);
+ ASSERT_INT_EQ(sshbuf_put(p1, x, sizeof(x)), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(x) + 4);
+ r = sshbuf_get_cstring(p1, &s2, NULL);
+ ASSERT_INT_EQ(r, SSH_ERR_INVALID_FORMAT);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_get_cstring trailing \\0");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_u32(p1, sizeof(x) - 1), 0);
+ ASSERT_INT_EQ(sshbuf_put(p1, x, sizeof(x) - 1), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(x) + 4 - 1);
+ ASSERT_INT_EQ(sshbuf_get_cstring(p1, &s2, &s), 0);
+ ASSERT_SIZE_T_EQ(s, sizeof(x) - 1);
+ ASSERT_MEM_EQ(s2, x, s);
+ free(s2);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_put_string");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_string(p1, x, sizeof(x)), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(x) + 4);
+ ASSERT_U32_EQ(PEEK_U32(sshbuf_ptr(p1)), sizeof(x));
+ ASSERT_MEM_EQ(sshbuf_ptr(p1) + 4, x, sizeof(x));
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_put_string limited");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_set_max_size(p1, sizeof(x) + 4 - 1), 0);
+ r = sshbuf_put_string(p1, x, sizeof(x));
+ ASSERT_INT_EQ(r, SSH_ERR_NO_BUFFER_SPACE);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_put_string giant");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ r = sshbuf_put_string(p1, (void *)0x01, 0xfffffffc);
+ ASSERT_INT_EQ(r, SSH_ERR_NO_BUFFER_SPACE);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_putf");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ r = sshbuf_putf(p1, "%s %d %x", "hello", 23, 0x5f);
+ ASSERT_INT_EQ(r, 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 11);
+ ASSERT_MEM_EQ(sshbuf_ptr(p1), "hello 23 5f", 11);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_putb");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ p2 = sshbuf_new();
+ ASSERT_PTR_NE(p2, NULL);
+ ASSERT_INT_EQ(sshbuf_put(p1, "blahblahblah", 12), 0);
+ ASSERT_INT_EQ(sshbuf_putb(p2, p1), 0);
+ sshbuf_free(p1);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p2), 12);
+ ASSERT_MEM_EQ(sshbuf_ptr(p2), "blahblahblah", 12);
+ sshbuf_free(p2);
+ TEST_DONE();
+
+ TEST_START("sshbuf_put_bignum2_bytes empty buf");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_bignum2_bytes(p1, NULL, 0), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(bn_exp1));
+ ASSERT_MEM_EQ(sshbuf_ptr(p1), bn_exp1, sizeof(bn_exp1));
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_put_bignum2_bytes all zeroes");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_bignum2_bytes(p1, bn1, sizeof(bn1)), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(bn_exp1));
+ ASSERT_MEM_EQ(sshbuf_ptr(p1), bn_exp1, sizeof(bn_exp1));
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_put_bignum2_bytes simple");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_bignum2_bytes(p1, bn2+2, sizeof(bn2)-2), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(bn_exp2));
+ ASSERT_MEM_EQ(sshbuf_ptr(p1), bn_exp2, sizeof(bn_exp2));
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_put_bignum2_bytes leading zero");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_bignum2_bytes(p1, bn2, sizeof(bn2)), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(bn_exp2));
+ ASSERT_MEM_EQ(sshbuf_ptr(p1), bn_exp2, sizeof(bn_exp2));
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_put_bignum2_bytes neg");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_bignum2_bytes(p1, bn3+1, sizeof(bn3)-1), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(bn_exp3));
+ ASSERT_MEM_EQ(sshbuf_ptr(p1), bn_exp3, sizeof(bn_exp3));
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_put_bignum2_bytes neg and leading zero");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_bignum2_bytes(p1, bn3, sizeof(bn3)), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(bn_exp3));
+ ASSERT_MEM_EQ(sshbuf_ptr(p1), bn_exp3, sizeof(bn_exp3));
+ sshbuf_free(p1);
+ TEST_DONE();
+}
diff --git a/regress/unittests/sshbuf/test_sshbuf_getput_crypto.c b/regress/unittests/sshbuf/test_sshbuf_getput_crypto.c
new file mode 100644
index 0000000..a68e132
--- /dev/null
+++ b/regress/unittests/sshbuf/test_sshbuf_getput_crypto.c
@@ -0,0 +1,409 @@
+/* $OpenBSD: test_sshbuf_getput_crypto.c,v 1.1 2014/04/30 05:32:00 djm Exp $ */
+/*
+ * Regress test for sshbuf.h buffer API
+ *
+ * Placed in the public domain
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <stdio.h>
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+
+#include <openssl/bn.h>
+#include <openssl/objects.h>
+#ifdef OPENSSL_HAS_NISTP256
+# include <openssl/ec.h>
+#endif
+
+#include "../test_helper/test_helper.h"
+#include "ssherr.h"
+#include "sshbuf.h"
+
+void sshbuf_getput_crypto_tests(void);
+
+void
+sshbuf_getput_crypto_tests(void)
+{
+ struct sshbuf *p1;
+ BIGNUM *bn, *bn2;
+ /* This one has num_bits != num_bytes * 8 to test bignum1 encoding */
+ const char *hexbn1 = "0102030405060708090a0b0c0d0e0f10";
+ /* This one has MSB set to test bignum2 encoding negative-avoidance */
+ const char *hexbn2 = "f0e0d0c0b0a0908070605040302010007fff11";
+ u_char expbn1[] = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+ };
+ u_char expbn2[] = {
+ 0xf0, 0xe0, 0xd0, 0xc0, 0xb0, 0xa0, 0x90, 0x80,
+ 0x70, 0x60, 0x50, 0x40, 0x30, 0x20, 0x10, 0x00,
+ 0x7f, 0xff, 0x11
+ };
+#if defined(OPENSSL_HAS_ECC) && defined(OPENSSL_HAS_NISTP256)
+ const u_char *d;
+ size_t s;
+ BIGNUM *bn_x, *bn_y;
+ int ec256_nid = NID_X9_62_prime256v1;
+ char *ec256_x = "0C828004839D0106AA59575216191357"
+ "34B451459DADB586677EF9DF55784999";
+ char *ec256_y = "4D196B50F0B4E94B3C73E3A9D4CD9DF2"
+ "C8F9A35E42BDD047550F69D80EC23CD4";
+ u_char expec256[] = {
+ 0x04,
+ 0x0c, 0x82, 0x80, 0x04, 0x83, 0x9d, 0x01, 0x06,
+ 0xaa, 0x59, 0x57, 0x52, 0x16, 0x19, 0x13, 0x57,
+ 0x34, 0xb4, 0x51, 0x45, 0x9d, 0xad, 0xb5, 0x86,
+ 0x67, 0x7e, 0xf9, 0xdf, 0x55, 0x78, 0x49, 0x99,
+ 0x4d, 0x19, 0x6b, 0x50, 0xf0, 0xb4, 0xe9, 0x4b,
+ 0x3c, 0x73, 0xe3, 0xa9, 0xd4, 0xcd, 0x9d, 0xf2,
+ 0xc8, 0xf9, 0xa3, 0x5e, 0x42, 0xbd, 0xd0, 0x47,
+ 0x55, 0x0f, 0x69, 0xd8, 0x0e, 0xc2, 0x3c, 0xd4
+ };
+ EC_KEY *eck;
+ EC_POINT *ecp;
+#endif
+ int r;
+
+#define MKBN(b, bnn) \
+ do { \
+ bnn = NULL; \
+ ASSERT_INT_GT(BN_hex2bn(&bnn, b), 0); \
+ } while (0)
+
+ TEST_START("sshbuf_put_bignum1");
+ MKBN(hexbn1, bn);
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_bignum1(p1, bn), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(expbn1) + 2);
+ ASSERT_U16_EQ(PEEK_U16(sshbuf_ptr(p1)), (u_int16_t)BN_num_bits(bn));
+ ASSERT_MEM_EQ(sshbuf_ptr(p1) + 2, expbn1, sizeof(expbn1));
+ BN_free(bn);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_put_bignum1 limited");
+ MKBN(hexbn1, bn);
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_set_max_size(p1, sizeof(expbn1) + 1), 0);
+ r = sshbuf_put_bignum1(p1, bn);
+ ASSERT_INT_EQ(r, SSH_ERR_NO_BUFFER_SPACE);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0);
+ BN_free(bn);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_put_bignum1 bn2");
+ MKBN(hexbn2, bn);
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_bignum1(p1, bn), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(expbn2) + 2);
+ ASSERT_U16_EQ(PEEK_U16(sshbuf_ptr(p1)), (u_int16_t)BN_num_bits(bn));
+ ASSERT_MEM_EQ(sshbuf_ptr(p1) + 2, expbn2, sizeof(expbn2));
+ BN_free(bn);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_put_bignum1 bn2 limited");
+ MKBN(hexbn2, bn);
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_set_max_size(p1, sizeof(expbn1) + 1), 0);
+ r = sshbuf_put_bignum1(p1, bn);
+ ASSERT_INT_EQ(r, SSH_ERR_NO_BUFFER_SPACE);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0);
+ BN_free(bn);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_put_bignum2");
+ MKBN(hexbn1, bn);
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_bignum2(p1, bn), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(expbn1) + 4);
+ ASSERT_U32_EQ(PEEK_U32(sshbuf_ptr(p1)), (u_int32_t)BN_num_bytes(bn));
+ ASSERT_MEM_EQ(sshbuf_ptr(p1) + 4, expbn1, sizeof(expbn1));
+ BN_free(bn);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_put_bignum2 limited");
+ MKBN(hexbn1, bn);
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_set_max_size(p1, sizeof(expbn1) + 3), 0);
+ r = sshbuf_put_bignum2(p1, bn);
+ ASSERT_INT_EQ(r, SSH_ERR_NO_BUFFER_SPACE);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0);
+ BN_free(bn);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_put_bignum2 bn2");
+ MKBN(hexbn2, bn);
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_bignum2(p1, bn), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(expbn2) + 4 + 1); /* MSB */
+ ASSERT_U32_EQ(PEEK_U32(sshbuf_ptr(p1)), (u_int32_t)BN_num_bytes(bn) + 1);
+ ASSERT_U8_EQ(*(sshbuf_ptr(p1) + 4), 0x00);
+ ASSERT_MEM_EQ(sshbuf_ptr(p1) + 5, expbn2, sizeof(expbn2));
+ BN_free(bn);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_put_bignum2 bn2 limited");
+ MKBN(hexbn2, bn);
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_set_max_size(p1, sizeof(expbn2) + 3), 0);
+ r = sshbuf_put_bignum2(p1, bn);
+ ASSERT_INT_EQ(r, SSH_ERR_NO_BUFFER_SPACE);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 0);
+ BN_free(bn);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_get_bignum1");
+ MKBN(hexbn1, bn);
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_u16(p1, BN_num_bits(bn)), 0);
+ ASSERT_INT_EQ(sshbuf_put(p1, expbn1, sizeof(expbn1)), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2 + sizeof(expbn1));
+ ASSERT_INT_EQ(sshbuf_put_u16(p1, 0xd00f), 0);
+ bn2 = BN_new();
+ ASSERT_INT_EQ(sshbuf_get_bignum1(p1, bn2), 0);
+ ASSERT_BIGNUM_EQ(bn, bn2);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2);
+ BN_free(bn);
+ BN_free(bn2);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_get_bignum1 truncated");
+ MKBN(hexbn1, bn);
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_u16(p1, BN_num_bits(bn)), 0);
+ ASSERT_INT_EQ(sshbuf_put(p1, expbn1, sizeof(expbn1) - 1), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2 + sizeof(expbn1) - 1);
+ bn2 = BN_new();
+ r = sshbuf_get_bignum1(p1, bn2);
+ ASSERT_INT_EQ(r, SSH_ERR_MESSAGE_INCOMPLETE);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2 + sizeof(expbn1) - 1);
+ BN_free(bn);
+ BN_free(bn2);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_get_bignum1 giant");
+ MKBN(hexbn1, bn);
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_u16(p1, 0xffff), 0);
+ ASSERT_INT_EQ(sshbuf_reserve(p1, (0xffff + 7) / 8, NULL), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2 + ((0xffff + 7) / 8));
+ bn2 = BN_new();
+ r = sshbuf_get_bignum1(p1, bn2);
+ ASSERT_INT_EQ(r, SSH_ERR_BIGNUM_TOO_LARGE);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2 + ((0xffff + 7) / 8));
+ BN_free(bn);
+ BN_free(bn2);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_get_bignum1 bn2");
+ MKBN(hexbn2, bn);
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_u16(p1, BN_num_bits(bn)), 0);
+ ASSERT_INT_EQ(sshbuf_put(p1, expbn2, sizeof(expbn2)), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2 + sizeof(expbn2));
+ ASSERT_INT_EQ(sshbuf_put_u16(p1, 0xd00f), 0);
+ bn2 = BN_new();
+ ASSERT_INT_EQ(sshbuf_get_bignum1(p1, bn2), 0);
+ ASSERT_BIGNUM_EQ(bn, bn2);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2);
+ BN_free(bn);
+ BN_free(bn2);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_get_bignum1 bn2 truncated");
+ MKBN(hexbn2, bn);
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_u16(p1, BN_num_bits(bn)), 0);
+ ASSERT_INT_EQ(sshbuf_put(p1, expbn2, sizeof(expbn2) - 1), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2 + sizeof(expbn2) - 1);
+ bn2 = BN_new();
+ r = sshbuf_get_bignum1(p1, bn2);
+ ASSERT_INT_EQ(r, SSH_ERR_MESSAGE_INCOMPLETE);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2 + sizeof(expbn2) - 1);
+ BN_free(bn);
+ BN_free(bn2);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_get_bignum2");
+ MKBN(hexbn1, bn);
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_u32(p1, BN_num_bytes(bn)), 0);
+ ASSERT_INT_EQ(sshbuf_put(p1, expbn1, sizeof(expbn1)), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 4 + sizeof(expbn1));
+ ASSERT_INT_EQ(sshbuf_put_u16(p1, 0xd00f), 0);
+ bn2 = BN_new();
+ ASSERT_INT_EQ(sshbuf_get_bignum2(p1, bn2), 0);
+ ASSERT_BIGNUM_EQ(bn, bn2);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2);
+ BN_free(bn);
+ BN_free(bn2);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_get_bignum2 truncated");
+ MKBN(hexbn1, bn);
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_u32(p1, BN_num_bytes(bn)), 0);
+ ASSERT_INT_EQ(sshbuf_put(p1, expbn1, sizeof(expbn1) - 1), 0);
+ bn2 = BN_new();
+ r = sshbuf_get_bignum2(p1, bn2);
+ ASSERT_INT_EQ(r, SSH_ERR_MESSAGE_INCOMPLETE);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(expbn1) + 3);
+ BN_free(bn);
+ BN_free(bn2);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_get_bignum2 giant");
+ MKBN(hexbn1, bn);
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_u32(p1, 65536), 0);
+ ASSERT_INT_EQ(sshbuf_reserve(p1, 65536, NULL), 0);
+ bn2 = BN_new();
+ r = sshbuf_get_bignum2(p1, bn2);
+ ASSERT_INT_EQ(r, SSH_ERR_BIGNUM_TOO_LARGE);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 65536 + 4);
+ BN_free(bn);
+ BN_free(bn2);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_get_bignum2 bn2");
+ MKBN(hexbn2, bn);
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_u32(p1, BN_num_bytes(bn) + 1), 0); /* MSB */
+ ASSERT_INT_EQ(sshbuf_put_u8(p1, 0x00), 0);
+ ASSERT_INT_EQ(sshbuf_put(p1, expbn2, sizeof(expbn2)), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 4 + 1 + sizeof(expbn2));
+ ASSERT_INT_EQ(sshbuf_put_u16(p1, 0xd00f), 0);
+ bn2 = BN_new();
+ ASSERT_INT_EQ(sshbuf_get_bignum2(p1, bn2), 0);
+ ASSERT_BIGNUM_EQ(bn, bn2);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2);
+ BN_free(bn);
+ BN_free(bn2);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_get_bignum2 bn2 truncated");
+ MKBN(hexbn2, bn);
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_u32(p1, BN_num_bytes(bn) + 1), 0);
+ ASSERT_INT_EQ(sshbuf_put_u8(p1, 0x00), 0);
+ ASSERT_INT_EQ(sshbuf_put(p1, expbn2, sizeof(expbn2) - 1), 0);
+ bn2 = BN_new();
+ r = sshbuf_get_bignum2(p1, bn2);
+ ASSERT_INT_EQ(r, SSH_ERR_MESSAGE_INCOMPLETE);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(expbn2) + 1 + 4 - 1);
+ BN_free(bn);
+ BN_free(bn2);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_get_bignum2 bn2 negative");
+ MKBN(hexbn2, bn);
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_u32(p1, BN_num_bytes(bn)), 0);
+ ASSERT_INT_EQ(sshbuf_put(p1, expbn2, sizeof(expbn2)), 0);
+ bn2 = BN_new();
+ r = sshbuf_get_bignum2(p1, bn2);
+ ASSERT_INT_EQ(r, SSH_ERR_BIGNUM_IS_NEGATIVE);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(expbn2) + 4);
+ BN_free(bn);
+ BN_free(bn2);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+#if defined(OPENSSL_HAS_ECC) && defined(OPENSSL_HAS_NISTP256)
+ TEST_START("sshbuf_put_ec");
+ eck = EC_KEY_new_by_curve_name(ec256_nid);
+ ASSERT_PTR_NE(eck, NULL);
+ ecp = EC_POINT_new(EC_KEY_get0_group(eck));
+ ASSERT_PTR_NE(ecp, NULL);
+ MKBN(ec256_x, bn_x);
+ MKBN(ec256_y, bn_y);
+ ASSERT_INT_EQ(EC_POINT_set_affine_coordinates_GFp(
+ EC_KEY_get0_group(eck), ecp, bn_x, bn_y, NULL), 1);
+ ASSERT_INT_EQ(EC_KEY_set_public_key(eck, ecp), 1);
+ BN_free(bn_x);
+ BN_free(bn_y);
+ EC_POINT_free(ecp);
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_eckey(p1, eck), 0);
+ ASSERT_INT_EQ(sshbuf_get_string_direct(p1, &d, &s), 0);
+ ASSERT_SIZE_T_EQ(s, sizeof(expec256));
+ ASSERT_MEM_EQ(d, expec256, sizeof(expec256));
+ sshbuf_free(p1);
+ EC_KEY_free(eck);
+ TEST_DONE();
+
+ TEST_START("sshbuf_get_ec");
+ eck = EC_KEY_new_by_curve_name(ec256_nid);
+ ASSERT_PTR_NE(eck, NULL);
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_string(p1, expec256, sizeof(expec256)), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), sizeof(expec256) + 4);
+ ASSERT_INT_EQ(sshbuf_put_u8(p1, 0x00), 0);
+ ASSERT_INT_EQ(sshbuf_get_eckey(p1, eck), 0);
+ bn_x = BN_new();
+ bn_y = BN_new();
+ ASSERT_PTR_NE(bn_x, NULL);
+ ASSERT_PTR_NE(bn_y, NULL);
+ ASSERT_INT_EQ(EC_POINT_get_affine_coordinates_GFp(
+ EC_KEY_get0_group(eck), EC_KEY_get0_public_key(eck),
+ bn_x, bn_y, NULL), 1);
+ MKBN(ec256_x, bn);
+ MKBN(ec256_y, bn2);
+ ASSERT_INT_EQ(BN_cmp(bn_x, bn), 0);
+ ASSERT_INT_EQ(BN_cmp(bn_y, bn2), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 1);
+ sshbuf_free(p1);
+ EC_KEY_free(eck);
+ BN_free(bn_x);
+ BN_free(bn_y);
+ BN_free(bn);
+ BN_free(bn2);
+ TEST_DONE();
+#endif
+}
+
diff --git a/regress/unittests/sshbuf/test_sshbuf_getput_fuzz.c b/regress/unittests/sshbuf/test_sshbuf_getput_fuzz.c
new file mode 100644
index 0000000..c6b5c29
--- /dev/null
+++ b/regress/unittests/sshbuf/test_sshbuf_getput_fuzz.c
@@ -0,0 +1,130 @@
+/* $OpenBSD: test_sshbuf_getput_fuzz.c,v 1.2 2014/05/02 02:54:00 djm Exp $ */
+/*
+ * Regress test for sshbuf.h buffer API
+ *
+ * Placed in the public domain
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <stdio.h>
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+
+#include <openssl/bn.h>
+#include <openssl/objects.h>
+#ifdef OPENSSL_HAS_NISTP256
+# include <openssl/ec.h>
+#endif
+
+#include "../test_helper/test_helper.h"
+#include "ssherr.h"
+#include "sshbuf.h"
+
+void sshbuf_getput_fuzz_tests(void);
+
+static void
+attempt_parse_blob(u_char *blob, size_t len)
+{
+ struct sshbuf *p1;
+ BIGNUM *bn;
+#if defined(OPENSSL_HAS_ECC) && defined(OPENSSL_HAS_NISTP256)
+ EC_KEY *eck;
+#endif
+ u_char *s;
+ size_t l;
+ u_int8_t u8;
+ u_int16_t u16;
+ u_int32_t u32;
+ u_int64_t u64;
+
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put(p1, blob, len), 0);
+ sshbuf_get_u8(p1, &u8);
+ sshbuf_get_u16(p1, &u16);
+ sshbuf_get_u32(p1, &u32);
+ sshbuf_get_u64(p1, &u64);
+ if (sshbuf_get_string(p1, &s, &l) == 0) {
+ bzero(s, l);
+ free(s);
+ }
+ bn = BN_new();
+ sshbuf_get_bignum1(p1, bn);
+ BN_clear_free(bn);
+ bn = BN_new();
+ sshbuf_get_bignum2(p1, bn);
+ BN_clear_free(bn);
+#if defined(OPENSSL_HAS_ECC) && defined(OPENSSL_HAS_NISTP256)
+ eck = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
+ ASSERT_PTR_NE(eck, NULL);
+ sshbuf_get_eckey(p1, eck);
+ EC_KEY_free(eck);
+#endif
+ sshbuf_free(p1);
+}
+
+
+static void
+onerror(void *fuzz)
+{
+ fprintf(stderr, "Failed during fuzz:\n");
+ fuzz_dump((struct fuzz *)fuzz);
+}
+
+void
+sshbuf_getput_fuzz_tests(void)
+{
+ u_char blob[] = {
+ /* u8 */
+ 0xd0,
+ /* u16 */
+ 0xc0, 0xde,
+ /* u32 */
+ 0xfa, 0xce, 0xde, 0xad,
+ /* u64 */
+ 0xfe, 0xed, 0xac, 0x1d, 0x1f, 0x1c, 0xbe, 0xef,
+ /* string */
+ 0x00, 0x00, 0x00, 0x09,
+ 'O', ' ', 'G', 'o', 'r', 'g', 'o', 'n', '!',
+ /* bignum1 */
+ 0x79,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+ /* bignum2 */
+ 0x00, 0x00, 0x00, 0x14,
+ 0x00,
+ 0xf0, 0xe0, 0xd0, 0xc0, 0xb0, 0xa0, 0x90, 0x80,
+ 0x70, 0x60, 0x50, 0x40, 0x30, 0x20, 0x10, 0x00,
+ 0x7f, 0xff, 0x11,
+ /* EC point (NIST-256 curve) */
+ 0x00, 0x00, 0x00, 0x41,
+ 0x04,
+ 0x0c, 0x82, 0x80, 0x04, 0x83, 0x9d, 0x01, 0x06,
+ 0xaa, 0x59, 0x57, 0x52, 0x16, 0x19, 0x13, 0x57,
+ 0x34, 0xb4, 0x51, 0x45, 0x9d, 0xad, 0xb5, 0x86,
+ 0x67, 0x7e, 0xf9, 0xdf, 0x55, 0x78, 0x49, 0x99,
+ 0x4d, 0x19, 0x6b, 0x50, 0xf0, 0xb4, 0xe9, 0x4b,
+ 0x3c, 0x73, 0xe3, 0xa9, 0xd4, 0xcd, 0x9d, 0xf2,
+ 0xc8, 0xf9, 0xa3, 0x5e, 0x42, 0xbd, 0xd0, 0x47,
+ 0x55, 0x0f, 0x69, 0xd8, 0x0e, 0xc2, 0x3c, 0xd4,
+ };
+ struct fuzz *fuzz;
+
+ TEST_START("fuzz blob parsing");
+ fuzz = fuzz_begin(FUZZ_1_BIT_FLIP | FUZZ_2_BIT_FLIP |
+ FUZZ_1_BYTE_FLIP | FUZZ_2_BYTE_FLIP |
+ FUZZ_TRUNCATE_START | FUZZ_TRUNCATE_END, blob, sizeof(blob));
+ TEST_ONERROR(onerror, fuzz);
+ for(; !fuzz_done(fuzz); fuzz_next(fuzz))
+ attempt_parse_blob(blob, sizeof(blob));
+ fuzz_cleanup(fuzz);
+ TEST_DONE();
+ TEST_ONERROR(NULL, NULL);
+}
+
diff --git a/regress/unittests/sshbuf/test_sshbuf_misc.c b/regress/unittests/sshbuf/test_sshbuf_misc.c
new file mode 100644
index 0000000..762a6c3
--- /dev/null
+++ b/regress/unittests/sshbuf/test_sshbuf_misc.c
@@ -0,0 +1,167 @@
+/* $OpenBSD: test_sshbuf_misc.c,v 1.2 2016/05/03 13:48:33 djm Exp $ */
+/*
+ * Regress test for sshbuf.h buffer API
+ *
+ * Placed in the public domain
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <stdio.h>
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+
+#include "../test_helper/test_helper.h"
+
+#include "sshbuf.h"
+
+void sshbuf_misc_tests(void);
+
+void
+sshbuf_misc_tests(void)
+{
+ struct sshbuf *p1;
+ char tmp[512], *p;
+ FILE *out;
+ size_t sz;
+
+ TEST_START("sshbuf_dump");
+ out = tmpfile();
+ ASSERT_PTR_NE(out, NULL);
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_u32(p1, 0x12345678), 0);
+ sshbuf_dump(p1, out);
+ fflush(out);
+ rewind(out);
+ sz = fread(tmp, 1, sizeof(tmp), out);
+ ASSERT_INT_EQ(ferror(out), 0);
+ ASSERT_INT_NE(feof(out), 0);
+ ASSERT_SIZE_T_GT(sz, 0);
+ tmp[sz] = '\0';
+ ASSERT_PTR_NE(strstr(tmp, "12 34 56 78"), NULL);
+ fclose(out);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_dtob16");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_u32(p1, 0x12345678), 0);
+ p = sshbuf_dtob16(p1);
+ ASSERT_PTR_NE(p, NULL);
+ ASSERT_STRING_EQ(p, "12345678");
+ free(p);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_dtob64 len 1");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_u8(p1, 0x11), 0);
+ p = sshbuf_dtob64(p1);
+ ASSERT_PTR_NE(p, NULL);
+ ASSERT_STRING_EQ(p, "EQ==");
+ free(p);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_dtob64 len 2");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_u8(p1, 0x11), 0);
+ ASSERT_INT_EQ(sshbuf_put_u8(p1, 0x22), 0);
+ p = sshbuf_dtob64(p1);
+ ASSERT_PTR_NE(p, NULL);
+ ASSERT_STRING_EQ(p, "ESI=");
+ free(p);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_dtob64 len 3");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_put_u8(p1, 0x11), 0);
+ ASSERT_INT_EQ(sshbuf_put_u8(p1, 0x22), 0);
+ ASSERT_INT_EQ(sshbuf_put_u8(p1, 0x33), 0);
+ p = sshbuf_dtob64(p1);
+ ASSERT_PTR_NE(p, NULL);
+ ASSERT_STRING_EQ(p, "ESIz");
+ free(p);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_dtob64 len 8191");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_reserve(p1, 8192, NULL), 0);
+ bzero(sshbuf_mutable_ptr(p1), 8192);
+ p = sshbuf_dtob64(p1);
+ ASSERT_PTR_NE(p, NULL);
+ ASSERT_SIZE_T_EQ(strlen(p), ((8191 + 2) / 3) * 4);
+ free(p);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_b64tod len 1");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_b64tod(p1, "0A=="), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 1);
+ ASSERT_U8_EQ(*sshbuf_ptr(p1), 0xd0);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_b64tod len 2");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_b64tod(p1, "0A8="), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 2);
+ ASSERT_U16_EQ(PEEK_U16(sshbuf_ptr(p1)), 0xd00f);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_b64tod len 4");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ ASSERT_INT_EQ(sshbuf_b64tod(p1, "0A/QDw=="), 0);
+ ASSERT_SIZE_T_EQ(sshbuf_len(p1), 4);
+ ASSERT_U32_EQ(PEEK_U32(sshbuf_ptr(p1)), 0xd00fd00f);
+ sshbuf_free(p1);
+ TEST_DONE();
+
+ TEST_START("sshbuf_dup_string");
+ p1 = sshbuf_new();
+ ASSERT_PTR_NE(p1, NULL);
+ /* Check empty buffer */
+ p = sshbuf_dup_string(p1);
+ ASSERT_PTR_NE(p, NULL);
+ ASSERT_SIZE_T_EQ(strlen(p), 0);
+ free(p);
+ /* Check buffer with string */
+ ASSERT_INT_EQ(sshbuf_put(p1, "quad1", strlen("quad1")), 0);
+ p = sshbuf_dup_string(p1);
+ ASSERT_PTR_NE(p, NULL);
+ ASSERT_SIZE_T_EQ(strlen(p), strlen("quad1"));
+ ASSERT_STRING_EQ(p, "quad1");
+ free(p);
+ /* Check buffer with terminating nul */
+ ASSERT_INT_EQ(sshbuf_put(p1, "\0", 1), 0);
+ p = sshbuf_dup_string(p1);
+ ASSERT_PTR_NE(p, NULL);
+ ASSERT_SIZE_T_EQ(strlen(p), strlen("quad1"));
+ ASSERT_STRING_EQ(p, "quad1");
+ free(p);
+ /* Check buffer with data after nul (expect failure) */
+ ASSERT_INT_EQ(sshbuf_put(p1, "quad2", strlen("quad2")), 0);
+ p = sshbuf_dup_string(p1);
+ ASSERT_PTR_EQ(p, NULL);
+ sshbuf_free(p1);
+ TEST_DONE();
+}
+
diff --git a/regress/unittests/sshbuf/tests.c b/regress/unittests/sshbuf/tests.c
new file mode 100644
index 0000000..1557e43
--- /dev/null
+++ b/regress/unittests/sshbuf/tests.c
@@ -0,0 +1,28 @@
+/* $OpenBSD: tests.c,v 1.1 2014/04/30 05:32:00 djm Exp $ */
+/*
+ * Regress test for sshbuf.h buffer API
+ *
+ * Placed in the public domain
+ */
+
+#include "../test_helper/test_helper.h"
+
+void sshbuf_tests(void);
+void sshbuf_getput_basic_tests(void);
+void sshbuf_getput_crypto_tests(void);
+void sshbuf_misc_tests(void);
+void sshbuf_fuzz_tests(void);
+void sshbuf_getput_fuzz_tests(void);
+void sshbuf_fixed(void);
+
+void
+tests(void)
+{
+ sshbuf_tests();
+ sshbuf_getput_basic_tests();
+ sshbuf_getput_crypto_tests();
+ sshbuf_misc_tests();
+ sshbuf_fuzz_tests();
+ sshbuf_getput_fuzz_tests();
+ sshbuf_fixed();
+}
diff --git a/regress/unittests/sshkey/Makefile b/regress/unittests/sshkey/Makefile
new file mode 100644
index 0000000..1c940be
--- /dev/null
+++ b/regress/unittests/sshkey/Makefile
@@ -0,0 +1,24 @@
+# $OpenBSD: Makefile,v 1.5 2017/12/21 00:41:22 djm Exp $
+
+PROG=test_sshkey
+SRCS=tests.c test_sshkey.c test_file.c test_fuzz.c common.c
+
+# From usr.bin/ssh
+SRCS+=sshbuf-getput-basic.c sshbuf-getput-crypto.c sshbuf-misc.c sshbuf.c
+SRCS+=atomicio.c sshkey.c authfile.c cipher.c log.c ssh-rsa.c ssh-dss.c
+SRCS+=ssh-ecdsa.c ssh-ed25519.c mac.c umac.c umac128.c hmac.c misc.c
+SRCS+=ssherr.c uidswap.c cleanup.c xmalloc.c match.c krl.c fatal.c
+SRCS+=addrmatch.c bitmap.c
+SRCS+=ed25519.c hash.c ge25519.c fe25519.c sc25519.c verify.c
+SRCS+=cipher-chachapoly.c chacha.c poly1305.c
+
+SRCS+=digest-openssl.c
+#SRCS+=digest-libc.c
+
+REGRESS_TARGETS=run-regress-${PROG}
+
+run-regress-${PROG}: ${PROG}
+ env ${TEST_ENV} ./${PROG} -d ${.CURDIR}/testdata
+
+.include <bsd.regress.mk>
+
diff --git a/regress/unittests/sshkey/common.c b/regress/unittests/sshkey/common.c
new file mode 100644
index 0000000..e63465c
--- /dev/null
+++ b/regress/unittests/sshkey/common.c
@@ -0,0 +1,163 @@
+/* $OpenBSD: common.c,v 1.3 2018/09/13 09:03:20 djm Exp $ */
+/*
+ * Helpers for key API tests
+ *
+ * Placed in the public domain
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <openssl/bn.h>
+#include <openssl/rsa.h>
+#include <openssl/dsa.h>
+#include <openssl/objects.h>
+#ifdef OPENSSL_HAS_NISTP256
+# include <openssl/ec.h>
+#endif
+
+#include "openbsd-compat/openssl-compat.h"
+
+#include "../test_helper/test_helper.h"
+
+#include "ssherr.h"
+#include "authfile.h"
+#include "sshkey.h"
+#include "sshbuf.h"
+
+#include "common.h"
+
+struct sshbuf *
+load_file(const char *name)
+{
+ int fd;
+ struct sshbuf *ret;
+
+ ASSERT_PTR_NE(ret = sshbuf_new(), NULL);
+ ASSERT_INT_NE(fd = open(test_data_file(name), O_RDONLY), -1);
+ ASSERT_INT_EQ(sshkey_load_file(fd, ret), 0);
+ close(fd);
+ return ret;
+}
+
+struct sshbuf *
+load_text_file(const char *name)
+{
+ struct sshbuf *ret = load_file(name);
+ const u_char *p;
+
+ /* Trim whitespace at EOL */
+ for (p = sshbuf_ptr(ret); sshbuf_len(ret) > 0;) {
+ if (p[sshbuf_len(ret) - 1] == '\r' ||
+ p[sshbuf_len(ret) - 1] == '\t' ||
+ p[sshbuf_len(ret) - 1] == ' ' ||
+ p[sshbuf_len(ret) - 1] == '\n')
+ ASSERT_INT_EQ(sshbuf_consume_end(ret, 1), 0);
+ else
+ break;
+ }
+ /* \0 terminate */
+ ASSERT_INT_EQ(sshbuf_put_u8(ret, 0), 0);
+ return ret;
+}
+
+BIGNUM *
+load_bignum(const char *name)
+{
+ BIGNUM *ret = NULL;
+ struct sshbuf *buf;
+
+ buf = load_text_file(name);
+ ASSERT_INT_NE(BN_hex2bn(&ret, (const char *)sshbuf_ptr(buf)), 0);
+ sshbuf_free(buf);
+ return ret;
+}
+
+const BIGNUM *
+rsa_n(struct sshkey *k)
+{
+ const BIGNUM *n = NULL;
+
+ ASSERT_PTR_NE(k, NULL);
+ ASSERT_PTR_NE(k->rsa, NULL);
+ RSA_get0_key(k->rsa, &n, NULL, NULL);
+ return n;
+}
+
+const BIGNUM *
+rsa_e(struct sshkey *k)
+{
+ const BIGNUM *e = NULL;
+
+ ASSERT_PTR_NE(k, NULL);
+ ASSERT_PTR_NE(k->rsa, NULL);
+ RSA_get0_key(k->rsa, NULL, &e, NULL);
+ return e;
+}
+
+const BIGNUM *
+rsa_p(struct sshkey *k)
+{
+ const BIGNUM *p = NULL;
+
+ ASSERT_PTR_NE(k, NULL);
+ ASSERT_PTR_NE(k->rsa, NULL);
+ RSA_get0_factors(k->rsa, &p, NULL);
+ return p;
+}
+
+const BIGNUM *
+rsa_q(struct sshkey *k)
+{
+ const BIGNUM *q = NULL;
+
+ ASSERT_PTR_NE(k, NULL);
+ ASSERT_PTR_NE(k->rsa, NULL);
+ RSA_get0_factors(k->rsa, NULL, &q);
+ return q;
+}
+
+const BIGNUM *
+dsa_g(struct sshkey *k)
+{
+ const BIGNUM *g = NULL;
+
+ ASSERT_PTR_NE(k, NULL);
+ ASSERT_PTR_NE(k->dsa, NULL);
+ DSA_get0_pqg(k->dsa, NULL, NULL, &g);
+ return g;
+}
+
+const BIGNUM *
+dsa_pub_key(struct sshkey *k)
+{
+ const BIGNUM *pub_key = NULL;
+
+ ASSERT_PTR_NE(k, NULL);
+ ASSERT_PTR_NE(k->dsa, NULL);
+ DSA_get0_key(k->dsa, &pub_key, NULL);
+ return pub_key;
+}
+
+const BIGNUM *
+dsa_priv_key(struct sshkey *k)
+{
+ const BIGNUM *priv_key = NULL;
+
+ ASSERT_PTR_NE(k, NULL);
+ ASSERT_PTR_NE(k->dsa, NULL);
+ DSA_get0_key(k->dsa, NULL, &priv_key);
+ return priv_key;
+}
+
diff --git a/regress/unittests/sshkey/common.h b/regress/unittests/sshkey/common.h
new file mode 100644
index 0000000..7a514fd
--- /dev/null
+++ b/regress/unittests/sshkey/common.h
@@ -0,0 +1,25 @@
+/* $OpenBSD: common.h,v 1.2 2018/09/13 09:03:20 djm Exp $ */
+/*
+ * Helpers for key API tests
+ *
+ * Placed in the public domain
+ */
+
+/* Load a binary file into a buffer */
+struct sshbuf *load_file(const char *name);
+
+/* Load a text file into a buffer */
+struct sshbuf *load_text_file(const char *name);
+
+/* Load a bignum from a file */
+BIGNUM *load_bignum(const char *name);
+
+/* Accessors for key components */
+const BIGNUM *rsa_n(struct sshkey *k);
+const BIGNUM *rsa_e(struct sshkey *k);
+const BIGNUM *rsa_p(struct sshkey *k);
+const BIGNUM *rsa_q(struct sshkey *k);
+const BIGNUM *dsa_g(struct sshkey *k);
+const BIGNUM *dsa_pub_key(struct sshkey *k);
+const BIGNUM *dsa_priv_key(struct sshkey *k);
+
diff --git a/regress/unittests/sshkey/mktestdata.sh b/regress/unittests/sshkey/mktestdata.sh
new file mode 100755
index 0000000..93da34c
--- /dev/null
+++ b/regress/unittests/sshkey/mktestdata.sh
@@ -0,0 +1,177 @@
+#!/bin/sh
+# $OpenBSD: mktestdata.sh,v 1.7 2018/09/12 01:36:45 djm Exp $
+
+PW=mekmitasdigoat
+
+rsa_params() {
+ _in="$1"
+ _outbase="$2"
+ set -e
+ openssl rsa -noout -text -in $_in | \
+ awk '/^modulus:$/,/^publicExponent:/' | \
+ grep -v '^[a-zA-Z]' | tr -d ' \n:' > ${_outbase}.n
+ openssl rsa -noout -text -in $_in | \
+ awk '/^prime1:$/,/^prime2:/' | \
+ grep -v '^[a-zA-Z]' | tr -d ' \n:' > ${_outbase}.p
+ openssl rsa -noout -text -in $_in | \
+ awk '/^prime2:$/,/^exponent1:/' | \
+ grep -v '^[a-zA-Z]' | tr -d ' \n:' > ${_outbase}.q
+ for x in n p q ; do
+ echo "" >> ${_outbase}.$x
+ echo ============ ${_outbase}.$x
+ cat ${_outbase}.$x
+ echo ============
+ done
+}
+
+dsa_params() {
+ _in="$1"
+ _outbase="$2"
+ set -e
+ openssl dsa -noout -text -in $_in | \
+ awk '/^priv:$/,/^pub:/' | \
+ grep -v '^[a-zA-Z]' | tr -d ' \n:' > ${_outbase}.priv
+ openssl dsa -noout -text -in $_in | \
+ awk '/^pub:/,/^P:/' | #\
+ grep -v '^[a-zA-Z]' | tr -d ' \n:' > ${_outbase}.pub
+ openssl dsa -noout -text -in $_in | \
+ awk '/^G:/,0' | \
+ grep -v '^[a-zA-Z]' | tr -d ' \n:' > ${_outbase}.g
+ for x in priv pub g ; do
+ echo "" >> ${_outbase}.$x
+ echo ============ ${_outbase}.$x
+ cat ${_outbase}.$x
+ echo ============
+ done
+}
+
+ecdsa_params() {
+ _in="$1"
+ _outbase="$2"
+ set -e
+ openssl ec -noout -text -in $_in | \
+ awk '/^priv:$/,/^pub:/' | \
+ grep -v '^[a-zA-Z]' | tr -d ' \n:' > ${_outbase}.priv
+ openssl ec -noout -text -in $_in | \
+ awk '/^pub:/,/^ASN1 OID:/' | #\
+ grep -v '^[a-zA-Z]' | tr -d ' \n:' > ${_outbase}.pub
+ openssl ec -noout -text -in $_in | \
+ grep "ASN1 OID:" | tr -d '\n' | \
+ sed 's/.*: //;s/ *$//' > ${_outbase}.curve
+ for x in priv pub curve ; do
+ echo "" >> ${_outbase}.$x
+ echo ============ ${_outbase}.$x
+ cat ${_outbase}.$x
+ echo ============
+ done
+}
+
+set -ex
+
+cd testdata
+
+rm -f rsa_1 dsa_1 ecdsa_1 ed25519_1
+rm -f rsa_2 dsa_2 ecdsa_2 ed25519_2
+rm -f rsa_n dsa_n ecdsa_n # new-format keys
+rm -f rsa_1_pw dsa_1_pw ecdsa_1_pw ed25519_1_pw
+rm -f rsa_n_pw dsa_n_pw ecdsa_n_pw
+rm -f pw *.pub *.bn.* *.param.* *.fp *.fp.bb
+
+ssh-keygen -t rsa -b 1024 -C "RSA test key #1" -N "" -f rsa_1
+ssh-keygen -t dsa -b 1024 -C "DSA test key #1" -N "" -f dsa_1
+ssh-keygen -t ecdsa -b 256 -C "ECDSA test key #1" -N "" -f ecdsa_1
+ssh-keygen -t ed25519 -C "ED25519 test key #1" -N "" -f ed25519_1
+
+ssh-keygen -t rsa -b 2048 -C "RSA test key #2" -N "" -f rsa_2
+ssh-keygen -t dsa -b 1024 -C "DSA test key #2" -N "" -f dsa_2
+ssh-keygen -t ecdsa -b 521 -C "ECDSA test key #2" -N "" -f ecdsa_2
+ssh-keygen -t ed25519 -C "ED25519 test key #1" -N "" -f ed25519_2
+
+cp rsa_1 rsa_n
+cp dsa_1 dsa_n
+cp ecdsa_1 ecdsa_n
+
+cp rsa_1 rsa_1_pw
+cp dsa_1 dsa_1_pw
+cp ecdsa_1 ecdsa_1_pw
+cp ed25519_1 ed25519_1_pw
+cp rsa_1 rsa_n_pw
+cp dsa_1 dsa_n_pw
+cp ecdsa_1 ecdsa_n_pw
+
+ssh-keygen -pf rsa_1_pw -N "$PW"
+ssh-keygen -pf dsa_1_pw -N "$PW"
+ssh-keygen -pf ecdsa_1_pw -N "$PW"
+ssh-keygen -pf ed25519_1_pw -N "$PW"
+ssh-keygen -opf rsa_n_pw -N "$PW"
+ssh-keygen -opf dsa_n_pw -N "$PW"
+ssh-keygen -opf ecdsa_n_pw -N "$PW"
+
+rsa_params rsa_1 rsa_1.param
+rsa_params rsa_2 rsa_2.param
+dsa_params dsa_1 dsa_1.param
+dsa_params dsa_1 dsa_1.param
+ecdsa_params ecdsa_1 ecdsa_1.param
+ecdsa_params ecdsa_2 ecdsa_2.param
+# XXX ed25519 params
+
+ssh-keygen -s rsa_2 -I hugo -n user1,user2 \
+ -Oforce-command=/bin/ls -Ono-port-forwarding -Osource-address=10.0.0.0/8 \
+ -V 19990101:20110101 -z 1 rsa_1.pub
+ssh-keygen -s rsa_2 -I hugo -n user1,user2 \
+ -Oforce-command=/bin/ls -Ono-port-forwarding -Osource-address=10.0.0.0/8 \
+ -V 19990101:20110101 -z 2 dsa_1.pub
+ssh-keygen -s rsa_2 -I hugo -n user1,user2 \
+ -Oforce-command=/bin/ls -Ono-port-forwarding -Osource-address=10.0.0.0/8 \
+ -V 19990101:20110101 -z 3 ecdsa_1.pub
+ssh-keygen -s rsa_2 -I hugo -n user1,user2 \
+ -Oforce-command=/bin/ls -Ono-port-forwarding -Osource-address=10.0.0.0/8 \
+ -V 19990101:20110101 -z 4 ed25519_1.pub
+
+# Make a few RSA variant signature too.
+cp rsa_1 rsa_1_sha1
+cp rsa_1 rsa_1_sha512
+cp rsa_1.pub rsa_1_sha1.pub
+cp rsa_1.pub rsa_1_sha512.pub
+ssh-keygen -s rsa_2 -I hugo -n user1,user2 -t ssh-rsa \
+ -Oforce-command=/bin/ls -Ono-port-forwarding -Osource-address=10.0.0.0/8 \
+ -V 19990101:20110101 -z 1 rsa_1_sha1.pub
+ssh-keygen -s rsa_2 -I hugo -n user1,user2 -t rsa-sha2-512 \
+ -Oforce-command=/bin/ls -Ono-port-forwarding -Osource-address=10.0.0.0/8 \
+ -V 19990101:20110101 -z 1 rsa_1_sha512.pub
+
+ssh-keygen -s ed25519_1 -I julius -n host1,host2 -h \
+ -V 19990101:20110101 -z 5 rsa_1.pub
+ssh-keygen -s ed25519_1 -I julius -n host1,host2 -h \
+ -V 19990101:20110101 -z 6 dsa_1.pub
+ssh-keygen -s ecdsa_1 -I julius -n host1,host2 -h \
+ -V 19990101:20110101 -z 7 ecdsa_1.pub
+ssh-keygen -s ed25519_1 -I julius -n host1,host2 -h \
+ -V 19990101:20110101 -z 8 ed25519_1.pub
+
+ssh-keygen -lf rsa_1 | awk '{print $2}' > rsa_1.fp
+ssh-keygen -lf dsa_1 | awk '{print $2}' > dsa_1.fp
+ssh-keygen -lf ecdsa_1 | awk '{print $2}' > ecdsa_1.fp
+ssh-keygen -lf ed25519_1 | awk '{print $2}' > ed25519_1.fp
+ssh-keygen -lf rsa_2 | awk '{print $2}' > rsa_2.fp
+ssh-keygen -lf dsa_2 | awk '{print $2}' > dsa_2.fp
+ssh-keygen -lf ecdsa_2 | awk '{print $2}' > ecdsa_2.fp
+ssh-keygen -lf ed25519_2 | awk '{print $2}' > ed25519_2.fp
+
+ssh-keygen -lf dsa_1-cert.pub | awk '{print $2}' > dsa_1-cert.fp
+ssh-keygen -lf ecdsa_1-cert.pub | awk '{print $2}' > ecdsa_1-cert.fp
+ssh-keygen -lf ed25519_1-cert.pub | awk '{print $2}' > ed25519_1-cert.fp
+ssh-keygen -lf rsa_1-cert.pub | awk '{print $2}' > rsa_1-cert.fp
+
+ssh-keygen -Bf rsa_1 | awk '{print $2}' > rsa_1.fp.bb
+ssh-keygen -Bf dsa_1 | awk '{print $2}' > dsa_1.fp.bb
+ssh-keygen -Bf ecdsa_1 | awk '{print $2}' > ecdsa_1.fp.bb
+ssh-keygen -Bf ed25519_1 | awk '{print $2}' > ed25519_1.fp.bb
+ssh-keygen -Bf rsa_2 | awk '{print $2}' > rsa_2.fp.bb
+ssh-keygen -Bf dsa_2 | awk '{print $2}' > dsa_2.fp.bb
+ssh-keygen -Bf ecdsa_2 | awk '{print $2}' > ecdsa_2.fp.bb
+ssh-keygen -Bf ed25519_2 | awk '{print $2}' > ed25519_2.fp.bb
+
+# XXX Extend ssh-keygen to do detached signatures (better to test/fuzz against)
+
+echo "$PW" > pw
diff --git a/regress/unittests/sshkey/test_file.c b/regress/unittests/sshkey/test_file.c
new file mode 100644
index 0000000..65610da
--- /dev/null
+++ b/regress/unittests/sshkey/test_file.c
@@ -0,0 +1,421 @@
+/* $OpenBSD: test_file.c,v 1.8 2018/09/13 09:03:20 djm Exp $ */
+/*
+ * Regress test for sshkey.h key management API
+ *
+ * Placed in the public domain
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <openssl/bn.h>
+#include <openssl/rsa.h>
+#include <openssl/dsa.h>
+#include <openssl/objects.h>
+#ifdef OPENSSL_HAS_NISTP256
+# include <openssl/ec.h>
+#endif
+
+#include "../test_helper/test_helper.h"
+
+#include "ssherr.h"
+#include "authfile.h"
+#include "sshkey.h"
+#include "sshbuf.h"
+#include "digest.h"
+
+#include "common.h"
+
+void sshkey_file_tests(void);
+
+void
+sshkey_file_tests(void)
+{
+ struct sshkey *k1, *k2;
+ struct sshbuf *buf, *pw;
+ BIGNUM *a, *b, *c;
+ char *cp;
+
+ TEST_START("load passphrase");
+ pw = load_text_file("pw");
+ TEST_DONE();
+
+
+ TEST_START("parse RSA from private");
+ buf = load_file("rsa_1");
+ ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
+ sshbuf_free(buf);
+ ASSERT_PTR_NE(k1, NULL);
+ a = load_bignum("rsa_1.param.n");
+ b = load_bignum("rsa_1.param.p");
+ c = load_bignum("rsa_1.param.q");
+ ASSERT_BIGNUM_EQ(rsa_n(k1), a);
+ ASSERT_BIGNUM_EQ(rsa_p(k1), b);
+ ASSERT_BIGNUM_EQ(rsa_q(k1), c);
+ BN_free(a);
+ BN_free(b);
+ BN_free(c);
+ TEST_DONE();
+
+ TEST_START("parse RSA from private w/ passphrase");
+ buf = load_file("rsa_1_pw");
+ ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf,
+ (const char *)sshbuf_ptr(pw), &k2, NULL), 0);
+ sshbuf_free(buf);
+ ASSERT_PTR_NE(k2, NULL);
+ ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
+ sshkey_free(k2);
+ TEST_DONE();
+
+ TEST_START("parse RSA from new-format");
+ buf = load_file("rsa_n");
+ ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k2, NULL), 0);
+ sshbuf_free(buf);
+ ASSERT_PTR_NE(k2, NULL);
+ ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
+ sshkey_free(k2);
+ TEST_DONE();
+
+ TEST_START("parse RSA from new-format w/ passphrase");
+ buf = load_file("rsa_n_pw");
+ ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf,
+ (const char *)sshbuf_ptr(pw), &k2, NULL), 0);
+ sshbuf_free(buf);
+ ASSERT_PTR_NE(k2, NULL);
+ ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
+ sshkey_free(k2);
+ TEST_DONE();
+
+ TEST_START("load RSA from public");
+ ASSERT_INT_EQ(sshkey_load_public(test_data_file("rsa_1.pub"), &k2,
+ NULL), 0);
+ ASSERT_PTR_NE(k2, NULL);
+ ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
+ sshkey_free(k2);
+ TEST_DONE();
+
+ TEST_START("load RSA cert with SHA1 signature");
+ ASSERT_INT_EQ(sshkey_load_cert(test_data_file("rsa_1_sha1"), &k2), 0);
+ ASSERT_PTR_NE(k2, NULL);
+ ASSERT_INT_EQ(k2->type, KEY_RSA_CERT);
+ ASSERT_INT_EQ(sshkey_equal_public(k1, k2), 1);
+ ASSERT_STRING_EQ(k2->cert->signature_type, "ssh-rsa");
+ sshkey_free(k2);
+ TEST_DONE();
+
+ TEST_START("load RSA cert with SHA512 signature");
+ ASSERT_INT_EQ(sshkey_load_cert(test_data_file("rsa_1_sha512"), &k2), 0);
+ ASSERT_PTR_NE(k2, NULL);
+ ASSERT_INT_EQ(k2->type, KEY_RSA_CERT);
+ ASSERT_INT_EQ(sshkey_equal_public(k1, k2), 1);
+ ASSERT_STRING_EQ(k2->cert->signature_type, "rsa-sha2-512");
+ sshkey_free(k2);
+ TEST_DONE();
+
+ TEST_START("load RSA cert");
+ ASSERT_INT_EQ(sshkey_load_cert(test_data_file("rsa_1"), &k2), 0);
+ ASSERT_PTR_NE(k2, NULL);
+ ASSERT_INT_EQ(k2->type, KEY_RSA_CERT);
+ ASSERT_INT_EQ(sshkey_equal(k1, k2), 0);
+ ASSERT_INT_EQ(sshkey_equal_public(k1, k2), 1);
+ TEST_DONE();
+
+ TEST_START("RSA key hex fingerprint");
+ buf = load_text_file("rsa_1.fp");
+ cp = sshkey_fingerprint(k1, SSH_DIGEST_SHA256, SSH_FP_BASE64);
+ ASSERT_PTR_NE(cp, NULL);
+ ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
+ sshbuf_free(buf);
+ free(cp);
+ TEST_DONE();
+
+ TEST_START("RSA cert hex fingerprint");
+ buf = load_text_file("rsa_1-cert.fp");
+ cp = sshkey_fingerprint(k2, SSH_DIGEST_SHA256, SSH_FP_BASE64);
+ ASSERT_PTR_NE(cp, NULL);
+ ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
+ sshbuf_free(buf);
+ free(cp);
+ sshkey_free(k2);
+ TEST_DONE();
+
+ TEST_START("RSA key bubblebabble fingerprint");
+ buf = load_text_file("rsa_1.fp.bb");
+ cp = sshkey_fingerprint(k1, SSH_DIGEST_SHA1, SSH_FP_BUBBLEBABBLE);
+ ASSERT_PTR_NE(cp, NULL);
+ ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
+ sshbuf_free(buf);
+ free(cp);
+ TEST_DONE();
+
+ sshkey_free(k1);
+
+ TEST_START("parse DSA from private");
+ buf = load_file("dsa_1");
+ ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
+ sshbuf_free(buf);
+ ASSERT_PTR_NE(k1, NULL);
+ a = load_bignum("dsa_1.param.g");
+ b = load_bignum("dsa_1.param.priv");
+ c = load_bignum("dsa_1.param.pub");
+ ASSERT_BIGNUM_EQ(dsa_g(k1), a);
+ ASSERT_BIGNUM_EQ(dsa_priv_key(k1), b);
+ ASSERT_BIGNUM_EQ(dsa_pub_key(k1), c);
+ BN_free(a);
+ BN_free(b);
+ BN_free(c);
+ TEST_DONE();
+
+ TEST_START("parse DSA from private w/ passphrase");
+ buf = load_file("dsa_1_pw");
+ ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf,
+ (const char *)sshbuf_ptr(pw), &k2, NULL), 0);
+ sshbuf_free(buf);
+ ASSERT_PTR_NE(k2, NULL);
+ ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
+ sshkey_free(k2);
+ TEST_DONE();
+
+ TEST_START("parse DSA from new-format");
+ buf = load_file("dsa_n");
+ ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k2, NULL), 0);
+ sshbuf_free(buf);
+ ASSERT_PTR_NE(k2, NULL);
+ ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
+ sshkey_free(k2);
+ TEST_DONE();
+
+ TEST_START("parse DSA from new-format w/ passphrase");
+ buf = load_file("dsa_n_pw");
+ ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf,
+ (const char *)sshbuf_ptr(pw), &k2, NULL), 0);
+ sshbuf_free(buf);
+ ASSERT_PTR_NE(k2, NULL);
+ ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
+ sshkey_free(k2);
+ TEST_DONE();
+
+ TEST_START("load DSA from public");
+ ASSERT_INT_EQ(sshkey_load_public(test_data_file("dsa_1.pub"), &k2,
+ NULL), 0);
+ ASSERT_PTR_NE(k2, NULL);
+ ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
+ sshkey_free(k2);
+ TEST_DONE();
+
+ TEST_START("load DSA cert");
+ ASSERT_INT_EQ(sshkey_load_cert(test_data_file("dsa_1"), &k2), 0);
+ ASSERT_PTR_NE(k2, NULL);
+ ASSERT_INT_EQ(k2->type, KEY_DSA_CERT);
+ ASSERT_INT_EQ(sshkey_equal(k1, k2), 0);
+ ASSERT_INT_EQ(sshkey_equal_public(k1, k2), 1);
+ TEST_DONE();
+
+ TEST_START("DSA key hex fingerprint");
+ buf = load_text_file("dsa_1.fp");
+ cp = sshkey_fingerprint(k1, SSH_DIGEST_SHA256, SSH_FP_BASE64);
+ ASSERT_PTR_NE(cp, NULL);
+ ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
+ sshbuf_free(buf);
+ free(cp);
+ TEST_DONE();
+
+ TEST_START("DSA cert hex fingerprint");
+ buf = load_text_file("dsa_1-cert.fp");
+ cp = sshkey_fingerprint(k2, SSH_DIGEST_SHA256, SSH_FP_BASE64);
+ ASSERT_PTR_NE(cp, NULL);
+ ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
+ sshbuf_free(buf);
+ free(cp);
+ sshkey_free(k2);
+ TEST_DONE();
+
+ TEST_START("DSA key bubblebabble fingerprint");
+ buf = load_text_file("dsa_1.fp.bb");
+ cp = sshkey_fingerprint(k1, SSH_DIGEST_SHA1, SSH_FP_BUBBLEBABBLE);
+ ASSERT_PTR_NE(cp, NULL);
+ ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
+ sshbuf_free(buf);
+ free(cp);
+ TEST_DONE();
+
+ sshkey_free(k1);
+
+#ifdef OPENSSL_HAS_ECC
+ TEST_START("parse ECDSA from private");
+ buf = load_file("ecdsa_1");
+ ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
+ sshbuf_free(buf);
+ ASSERT_PTR_NE(k1, NULL);
+ buf = load_text_file("ecdsa_1.param.curve");
+ ASSERT_STRING_EQ((const char *)sshbuf_ptr(buf),
+ OBJ_nid2sn(k1->ecdsa_nid));
+ sshbuf_free(buf);
+ a = load_bignum("ecdsa_1.param.priv");
+ b = load_bignum("ecdsa_1.param.pub");
+ c = EC_POINT_point2bn(EC_KEY_get0_group(k1->ecdsa),
+ EC_KEY_get0_public_key(k1->ecdsa), POINT_CONVERSION_UNCOMPRESSED,
+ NULL, NULL);
+ ASSERT_PTR_NE(c, NULL);
+ ASSERT_BIGNUM_EQ(EC_KEY_get0_private_key(k1->ecdsa), a);
+ ASSERT_BIGNUM_EQ(b, c);
+ BN_free(a);
+ BN_free(b);
+ BN_free(c);
+ TEST_DONE();
+
+ TEST_START("parse ECDSA from private w/ passphrase");
+ buf = load_file("ecdsa_1_pw");
+ ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf,
+ (const char *)sshbuf_ptr(pw), &k2, NULL), 0);
+ sshbuf_free(buf);
+ ASSERT_PTR_NE(k2, NULL);
+ ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
+ sshkey_free(k2);
+ TEST_DONE();
+
+ TEST_START("parse ECDSA from new-format");
+ buf = load_file("ecdsa_n");
+ ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k2, NULL), 0);
+ sshbuf_free(buf);
+ ASSERT_PTR_NE(k2, NULL);
+ ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
+ sshkey_free(k2);
+ TEST_DONE();
+
+ TEST_START("parse ECDSA from new-format w/ passphrase");
+ buf = load_file("ecdsa_n_pw");
+ ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf,
+ (const char *)sshbuf_ptr(pw), &k2, NULL), 0);
+ sshbuf_free(buf);
+ ASSERT_PTR_NE(k2, NULL);
+ ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
+ sshkey_free(k2);
+ TEST_DONE();
+
+ TEST_START("load ECDSA from public");
+ ASSERT_INT_EQ(sshkey_load_public(test_data_file("ecdsa_1.pub"), &k2,
+ NULL), 0);
+ ASSERT_PTR_NE(k2, NULL);
+ ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
+ sshkey_free(k2);
+ TEST_DONE();
+
+ TEST_START("load ECDSA cert");
+ ASSERT_INT_EQ(sshkey_load_cert(test_data_file("ecdsa_1"), &k2), 0);
+ ASSERT_PTR_NE(k2, NULL);
+ ASSERT_INT_EQ(k2->type, KEY_ECDSA_CERT);
+ ASSERT_INT_EQ(sshkey_equal(k1, k2), 0);
+ ASSERT_INT_EQ(sshkey_equal_public(k1, k2), 1);
+ TEST_DONE();
+
+ TEST_START("ECDSA key hex fingerprint");
+ buf = load_text_file("ecdsa_1.fp");
+ cp = sshkey_fingerprint(k1, SSH_DIGEST_SHA256, SSH_FP_BASE64);
+ ASSERT_PTR_NE(cp, NULL);
+ ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
+ sshbuf_free(buf);
+ free(cp);
+ TEST_DONE();
+
+ TEST_START("ECDSA cert hex fingerprint");
+ buf = load_text_file("ecdsa_1-cert.fp");
+ cp = sshkey_fingerprint(k2, SSH_DIGEST_SHA256, SSH_FP_BASE64);
+ ASSERT_PTR_NE(cp, NULL);
+ ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
+ sshbuf_free(buf);
+ free(cp);
+ sshkey_free(k2);
+ TEST_DONE();
+
+ TEST_START("ECDSA key bubblebabble fingerprint");
+ buf = load_text_file("ecdsa_1.fp.bb");
+ cp = sshkey_fingerprint(k1, SSH_DIGEST_SHA1, SSH_FP_BUBBLEBABBLE);
+ ASSERT_PTR_NE(cp, NULL);
+ ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
+ sshbuf_free(buf);
+ free(cp);
+ TEST_DONE();
+
+ sshkey_free(k1);
+#endif /* OPENSSL_HAS_ECC */
+
+ TEST_START("parse Ed25519 from private");
+ buf = load_file("ed25519_1");
+ ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
+ sshbuf_free(buf);
+ ASSERT_PTR_NE(k1, NULL);
+ ASSERT_INT_EQ(k1->type, KEY_ED25519);
+ /* XXX check key contents */
+ TEST_DONE();
+
+ TEST_START("parse Ed25519 from private w/ passphrase");
+ buf = load_file("ed25519_1_pw");
+ ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf,
+ (const char *)sshbuf_ptr(pw), &k2, NULL), 0);
+ sshbuf_free(buf);
+ ASSERT_PTR_NE(k2, NULL);
+ ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
+ sshkey_free(k2);
+ TEST_DONE();
+
+ TEST_START("load Ed25519 from public");
+ ASSERT_INT_EQ(sshkey_load_public(test_data_file("ed25519_1.pub"), &k2,
+ NULL), 0);
+ ASSERT_PTR_NE(k2, NULL);
+ ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
+ sshkey_free(k2);
+ TEST_DONE();
+
+ TEST_START("load Ed25519 cert");
+ ASSERT_INT_EQ(sshkey_load_cert(test_data_file("ed25519_1"), &k2), 0);
+ ASSERT_PTR_NE(k2, NULL);
+ ASSERT_INT_EQ(k2->type, KEY_ED25519_CERT);
+ ASSERT_INT_EQ(sshkey_equal(k1, k2), 0);
+ ASSERT_INT_EQ(sshkey_equal_public(k1, k2), 1);
+ TEST_DONE();
+
+ TEST_START("Ed25519 key hex fingerprint");
+ buf = load_text_file("ed25519_1.fp");
+ cp = sshkey_fingerprint(k1, SSH_DIGEST_SHA256, SSH_FP_BASE64);
+ ASSERT_PTR_NE(cp, NULL);
+ ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
+ sshbuf_free(buf);
+ free(cp);
+ TEST_DONE();
+
+ TEST_START("Ed25519 cert hex fingerprint");
+ buf = load_text_file("ed25519_1-cert.fp");
+ cp = sshkey_fingerprint(k2, SSH_DIGEST_SHA256, SSH_FP_BASE64);
+ ASSERT_PTR_NE(cp, NULL);
+ ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
+ sshbuf_free(buf);
+ free(cp);
+ sshkey_free(k2);
+ TEST_DONE();
+
+ TEST_START("Ed25519 key bubblebabble fingerprint");
+ buf = load_text_file("ed25519_1.fp.bb");
+ cp = sshkey_fingerprint(k1, SSH_DIGEST_SHA1, SSH_FP_BUBBLEBABBLE);
+ ASSERT_PTR_NE(cp, NULL);
+ ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
+ sshbuf_free(buf);
+ free(cp);
+ TEST_DONE();
+
+ sshkey_free(k1);
+
+ sshbuf_free(pw);
+
+}
diff --git a/regress/unittests/sshkey/test_fuzz.c b/regress/unittests/sshkey/test_fuzz.c
new file mode 100644
index 0000000..d3b0c92
--- /dev/null
+++ b/regress/unittests/sshkey/test_fuzz.c
@@ -0,0 +1,360 @@
+/* $OpenBSD: test_fuzz.c,v 1.8 2017/12/21 00:41:22 djm Exp $ */
+/*
+ * Fuzz tests for key parsing
+ *
+ * Placed in the public domain
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <openssl/bn.h>
+#include <openssl/rsa.h>
+#include <openssl/dsa.h>
+#include <openssl/objects.h>
+#ifdef OPENSSL_HAS_NISTP256
+# include <openssl/ec.h>
+#endif
+
+#include "../test_helper/test_helper.h"
+
+#include "ssherr.h"
+#include "authfile.h"
+#include "sshkey.h"
+#include "sshbuf.h"
+
+#include "common.h"
+
+void sshkey_fuzz_tests(void);
+
+static void
+onerror(void *fuzz)
+{
+ fprintf(stderr, "Failed during fuzz:\n");
+ fuzz_dump((struct fuzz *)fuzz);
+}
+
+static void
+public_fuzz(struct sshkey *k)
+{
+ struct sshkey *k1;
+ struct sshbuf *buf;
+ struct fuzz *fuzz;
+
+ ASSERT_PTR_NE(buf = sshbuf_new(), NULL);
+ ASSERT_INT_EQ(sshkey_putb(k, buf), 0);
+ /* XXX need a way to run the tests in "slow, but complete" mode */
+ fuzz = fuzz_begin(FUZZ_1_BIT_FLIP | /* XXX too slow FUZZ_2_BIT_FLIP | */
+ FUZZ_1_BYTE_FLIP | /* XXX too slow FUZZ_2_BYTE_FLIP | */
+ FUZZ_TRUNCATE_START | FUZZ_TRUNCATE_END,
+ sshbuf_mutable_ptr(buf), sshbuf_len(buf));
+ ASSERT_INT_EQ(sshkey_from_blob(sshbuf_ptr(buf), sshbuf_len(buf),
+ &k1), 0);
+ sshkey_free(k1);
+ sshbuf_free(buf);
+ TEST_ONERROR(onerror, fuzz);
+ for(; !fuzz_done(fuzz); fuzz_next(fuzz)) {
+ if (sshkey_from_blob(fuzz_ptr(fuzz), fuzz_len(fuzz), &k1) == 0)
+ sshkey_free(k1);
+ }
+ fuzz_cleanup(fuzz);
+}
+
+static void
+sig_fuzz(struct sshkey *k, const char *sig_alg)
+{
+ struct fuzz *fuzz;
+ u_char *sig, c[] = "some junk to be signed";
+ size_t l;
+
+ ASSERT_INT_EQ(sshkey_sign(k, &sig, &l, c, sizeof(c), sig_alg, 0), 0);
+ ASSERT_SIZE_T_GT(l, 0);
+ fuzz = fuzz_begin(FUZZ_1_BIT_FLIP | /* too slow FUZZ_2_BIT_FLIP | */
+ FUZZ_1_BYTE_FLIP | FUZZ_2_BYTE_FLIP |
+ FUZZ_TRUNCATE_START | FUZZ_TRUNCATE_END, sig, l);
+ ASSERT_INT_EQ(sshkey_verify(k, sig, l, c, sizeof(c), NULL, 0), 0);
+ free(sig);
+ TEST_ONERROR(onerror, fuzz);
+ for(; !fuzz_done(fuzz); fuzz_next(fuzz)) {
+ /* Ensure 1-bit difference at least */
+ if (fuzz_matches_original(fuzz))
+ continue;
+ ASSERT_INT_NE(sshkey_verify(k, fuzz_ptr(fuzz), fuzz_len(fuzz),
+ c, sizeof(c), NULL, 0), 0);
+ }
+ fuzz_cleanup(fuzz);
+}
+
+void
+sshkey_fuzz_tests(void)
+{
+ struct sshkey *k1;
+ struct sshbuf *buf, *fuzzed;
+ struct fuzz *fuzz;
+ int r;
+
+
+ TEST_START("fuzz RSA private");
+ buf = load_file("rsa_1");
+ fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf),
+ sshbuf_len(buf));
+ ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
+ sshkey_free(k1);
+ sshbuf_free(buf);
+ ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL);
+ TEST_ONERROR(onerror, fuzz);
+ for(; !fuzz_done(fuzz); fuzz_next(fuzz)) {
+ r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz));
+ ASSERT_INT_EQ(r, 0);
+ if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0)
+ sshkey_free(k1);
+ sshbuf_reset(fuzzed);
+ }
+ sshbuf_free(fuzzed);
+ fuzz_cleanup(fuzz);
+ TEST_DONE();
+
+ TEST_START("fuzz RSA new-format private");
+ buf = load_file("rsa_n");
+ fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf),
+ sshbuf_len(buf));
+ ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
+ sshkey_free(k1);
+ sshbuf_free(buf);
+ ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL);
+ TEST_ONERROR(onerror, fuzz);
+ for(; !fuzz_done(fuzz); fuzz_next(fuzz)) {
+ r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz));
+ ASSERT_INT_EQ(r, 0);
+ if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0)
+ sshkey_free(k1);
+ sshbuf_reset(fuzzed);
+ }
+ sshbuf_free(fuzzed);
+ fuzz_cleanup(fuzz);
+ TEST_DONE();
+
+ TEST_START("fuzz DSA private");
+ buf = load_file("dsa_1");
+ fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf),
+ sshbuf_len(buf));
+ ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
+ sshkey_free(k1);
+ sshbuf_free(buf);
+ ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL);
+ TEST_ONERROR(onerror, fuzz);
+ for(; !fuzz_done(fuzz); fuzz_next(fuzz)) {
+ r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz));
+ ASSERT_INT_EQ(r, 0);
+ if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0)
+ sshkey_free(k1);
+ sshbuf_reset(fuzzed);
+ }
+ sshbuf_free(fuzzed);
+ fuzz_cleanup(fuzz);
+ TEST_DONE();
+
+ TEST_START("fuzz DSA new-format private");
+ buf = load_file("dsa_n");
+ fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf),
+ sshbuf_len(buf));
+ ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
+ sshkey_free(k1);
+ sshbuf_free(buf);
+ ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL);
+ TEST_ONERROR(onerror, fuzz);
+ for(; !fuzz_done(fuzz); fuzz_next(fuzz)) {
+ r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz));
+ ASSERT_INT_EQ(r, 0);
+ if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0)
+ sshkey_free(k1);
+ sshbuf_reset(fuzzed);
+ }
+ sshbuf_free(fuzzed);
+ fuzz_cleanup(fuzz);
+ TEST_DONE();
+
+#ifdef OPENSSL_HAS_ECC
+ TEST_START("fuzz ECDSA private");
+ buf = load_file("ecdsa_1");
+ fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf),
+ sshbuf_len(buf));
+ ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
+ sshkey_free(k1);
+ sshbuf_free(buf);
+ ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL);
+ TEST_ONERROR(onerror, fuzz);
+ for(; !fuzz_done(fuzz); fuzz_next(fuzz)) {
+ r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz));
+ ASSERT_INT_EQ(r, 0);
+ if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0)
+ sshkey_free(k1);
+ sshbuf_reset(fuzzed);
+ }
+ sshbuf_free(fuzzed);
+ fuzz_cleanup(fuzz);
+ TEST_DONE();
+
+ TEST_START("fuzz ECDSA new-format private");
+ buf = load_file("ecdsa_n");
+ fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf),
+ sshbuf_len(buf));
+ ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
+ sshkey_free(k1);
+ sshbuf_free(buf);
+ ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL);
+ TEST_ONERROR(onerror, fuzz);
+ for(; !fuzz_done(fuzz); fuzz_next(fuzz)) {
+ r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz));
+ ASSERT_INT_EQ(r, 0);
+ if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0)
+ sshkey_free(k1);
+ sshbuf_reset(fuzzed);
+ }
+ sshbuf_free(fuzzed);
+ fuzz_cleanup(fuzz);
+ TEST_DONE();
+#endif
+
+ TEST_START("fuzz Ed25519 private");
+ buf = load_file("ed25519_1");
+ fuzz = fuzz_begin(FUZZ_BASE64, sshbuf_mutable_ptr(buf),
+ sshbuf_len(buf));
+ ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
+ sshkey_free(k1);
+ sshbuf_free(buf);
+ ASSERT_PTR_NE(fuzzed = sshbuf_new(), NULL);
+ TEST_ONERROR(onerror, fuzz);
+ for(; !fuzz_done(fuzz); fuzz_next(fuzz)) {
+ r = sshbuf_put(fuzzed, fuzz_ptr(fuzz), fuzz_len(fuzz));
+ ASSERT_INT_EQ(r, 0);
+ if (sshkey_parse_private_fileblob(fuzzed, "", &k1, NULL) == 0)
+ sshkey_free(k1);
+ sshbuf_reset(fuzzed);
+ }
+ sshbuf_free(fuzzed);
+ fuzz_cleanup(fuzz);
+ TEST_DONE();
+
+ TEST_START("fuzz RSA public");
+ buf = load_file("rsa_1");
+ ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
+ sshbuf_free(buf);
+ public_fuzz(k1);
+ sshkey_free(k1);
+ TEST_DONE();
+
+ TEST_START("fuzz RSA cert");
+ ASSERT_INT_EQ(sshkey_load_cert(test_data_file("rsa_1"), &k1), 0);
+ public_fuzz(k1);
+ sshkey_free(k1);
+ TEST_DONE();
+
+ TEST_START("fuzz DSA public");
+ buf = load_file("dsa_1");
+ ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
+ sshbuf_free(buf);
+ public_fuzz(k1);
+ sshkey_free(k1);
+ TEST_DONE();
+
+ TEST_START("fuzz DSA cert");
+ ASSERT_INT_EQ(sshkey_load_cert(test_data_file("dsa_1"), &k1), 0);
+ public_fuzz(k1);
+ sshkey_free(k1);
+ TEST_DONE();
+
+#ifdef OPENSSL_HAS_ECC
+ TEST_START("fuzz ECDSA public");
+ buf = load_file("ecdsa_1");
+ ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
+ sshbuf_free(buf);
+ public_fuzz(k1);
+ sshkey_free(k1);
+ TEST_DONE();
+
+ TEST_START("fuzz ECDSA cert");
+ ASSERT_INT_EQ(sshkey_load_cert(test_data_file("ecdsa_1"), &k1), 0);
+ public_fuzz(k1);
+ sshkey_free(k1);
+ TEST_DONE();
+#endif
+
+ TEST_START("fuzz Ed25519 public");
+ buf = load_file("ed25519_1");
+ ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
+ sshbuf_free(buf);
+ public_fuzz(k1);
+ sshkey_free(k1);
+ TEST_DONE();
+
+ TEST_START("fuzz Ed25519 cert");
+ ASSERT_INT_EQ(sshkey_load_cert(test_data_file("ed25519_1"), &k1), 0);
+ public_fuzz(k1);
+ sshkey_free(k1);
+ TEST_DONE();
+
+ TEST_START("fuzz RSA sig");
+ buf = load_file("rsa_1");
+ ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
+ sshbuf_free(buf);
+ sig_fuzz(k1, "ssh-rsa");
+ sshkey_free(k1);
+ TEST_DONE();
+
+ TEST_START("fuzz RSA SHA256 sig");
+ buf = load_file("rsa_1");
+ ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
+ sshbuf_free(buf);
+ sig_fuzz(k1, "rsa-sha2-256");
+ sshkey_free(k1);
+ TEST_DONE();
+
+ TEST_START("fuzz RSA SHA512 sig");
+ buf = load_file("rsa_1");
+ ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
+ sshbuf_free(buf);
+ sig_fuzz(k1, "rsa-sha2-512");
+ sshkey_free(k1);
+ TEST_DONE();
+
+ TEST_START("fuzz DSA sig");
+ buf = load_file("dsa_1");
+ ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
+ sshbuf_free(buf);
+ sig_fuzz(k1, NULL);
+ sshkey_free(k1);
+ TEST_DONE();
+
+#ifdef OPENSSL_HAS_ECC
+ TEST_START("fuzz ECDSA sig");
+ buf = load_file("ecdsa_1");
+ ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
+ sshbuf_free(buf);
+ sig_fuzz(k1, NULL);
+ sshkey_free(k1);
+ TEST_DONE();
+#endif
+
+ TEST_START("fuzz Ed25519 sig");
+ buf = load_file("ed25519_1");
+ ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", &k1, NULL), 0);
+ sshbuf_free(buf);
+ sig_fuzz(k1, NULL);
+ sshkey_free(k1);
+ TEST_DONE();
+
+/* XXX fuzz decoded new-format blobs too */
+
+}
diff --git a/regress/unittests/sshkey/test_sshkey.c b/regress/unittests/sshkey/test_sshkey.c
new file mode 100644
index 0000000..47a03fa
--- /dev/null
+++ b/regress/unittests/sshkey/test_sshkey.c
@@ -0,0 +1,508 @@
+/* $OpenBSD: test_sshkey.c,v 1.17 2018/09/13 09:03:20 djm Exp $ */
+/*
+ * Regress test for sshkey.h key management API
+ *
+ * Placed in the public domain
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <stdio.h>
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+
+#include <openssl/bn.h>
+#include <openssl/rsa.h>
+#include <openssl/dsa.h>
+#if defined(OPENSSL_HAS_ECC) && defined(OPENSSL_HAS_NISTP256)
+# include <openssl/ec.h>
+#endif
+
+#include "../test_helper/test_helper.h"
+
+#include "ssherr.h"
+#include "sshbuf.h"
+#define SSHBUF_INTERNAL 1 /* access internals for testing */
+#include "sshkey.h"
+
+#include "authfile.h"
+#include "common.h"
+#include "ssh2.h"
+
+void sshkey_tests(void);
+
+static void
+put_opt(struct sshbuf *b, const char *name, const char *value)
+{
+ struct sshbuf *sect;
+
+ sect = sshbuf_new();
+ ASSERT_PTR_NE(sect, NULL);
+ ASSERT_INT_EQ(sshbuf_put_cstring(b, name), 0);
+ if (value != NULL)
+ ASSERT_INT_EQ(sshbuf_put_cstring(sect, value), 0);
+ ASSERT_INT_EQ(sshbuf_put_stringb(b, sect), 0);
+ sshbuf_free(sect);
+}
+
+static void
+build_cert(struct sshbuf *b, const struct sshkey *k, const char *type,
+ const struct sshkey *sign_key, const struct sshkey *ca_key,
+ const char *sig_alg)
+{
+ struct sshbuf *ca_buf, *pk, *principals, *critopts, *exts;
+ u_char *sigblob;
+ size_t siglen;
+
+ ca_buf = sshbuf_new();
+ ASSERT_PTR_NE(ca_buf, NULL);
+ ASSERT_INT_EQ(sshkey_putb(ca_key, ca_buf), 0);
+
+ /*
+ * Get the public key serialisation by rendering the key and skipping
+ * the type string. This is a bit of a hack :/
+ */
+ pk = sshbuf_new();
+ ASSERT_PTR_NE(pk, NULL);
+ ASSERT_INT_EQ(sshkey_putb_plain(k, pk), 0);
+ ASSERT_INT_EQ(sshbuf_skip_string(pk), 0);
+
+ principals = sshbuf_new();
+ ASSERT_PTR_NE(principals, NULL);
+ ASSERT_INT_EQ(sshbuf_put_cstring(principals, "gsamsa"), 0);
+ ASSERT_INT_EQ(sshbuf_put_cstring(principals, "gregor"), 0);
+
+ critopts = sshbuf_new();
+ ASSERT_PTR_NE(critopts, NULL);
+ put_opt(critopts, "force-command", "/usr/local/bin/nethack");
+ put_opt(critopts, "source-address", "192.168.0.0/24,127.0.0.1,::1");
+
+ exts = sshbuf_new();
+ ASSERT_PTR_NE(exts, NULL);
+ put_opt(critopts, "permit-X11-forwarding", NULL);
+
+ ASSERT_INT_EQ(sshbuf_put_cstring(b, type), 0);
+ ASSERT_INT_EQ(sshbuf_put_cstring(b, "noncenoncenonce!"), 0); /* nonce */
+ ASSERT_INT_EQ(sshbuf_putb(b, pk), 0); /* public key serialisation */
+ ASSERT_INT_EQ(sshbuf_put_u64(b, 1234), 0); /* serial */
+ ASSERT_INT_EQ(sshbuf_put_u32(b, SSH2_CERT_TYPE_USER), 0); /* type */
+ ASSERT_INT_EQ(sshbuf_put_cstring(b, "gregor"), 0); /* key ID */
+ ASSERT_INT_EQ(sshbuf_put_stringb(b, principals), 0); /* principals */
+ ASSERT_INT_EQ(sshbuf_put_u64(b, 0), 0); /* start */
+ ASSERT_INT_EQ(sshbuf_put_u64(b, 0xffffffffffffffffULL), 0); /* end */
+ ASSERT_INT_EQ(sshbuf_put_stringb(b, critopts), 0); /* options */
+ ASSERT_INT_EQ(sshbuf_put_stringb(b, exts), 0); /* extensions */
+ ASSERT_INT_EQ(sshbuf_put_string(b, NULL, 0), 0); /* reserved */
+ ASSERT_INT_EQ(sshbuf_put_stringb(b, ca_buf), 0); /* signature key */
+ ASSERT_INT_EQ(sshkey_sign(sign_key, &sigblob, &siglen,
+ sshbuf_ptr(b), sshbuf_len(b), sig_alg, 0), 0);
+ ASSERT_INT_EQ(sshbuf_put_string(b, sigblob, siglen), 0); /* signature */
+
+ free(sigblob);
+ sshbuf_free(ca_buf);
+ sshbuf_free(exts);
+ sshbuf_free(critopts);
+ sshbuf_free(principals);
+ sshbuf_free(pk);
+}
+
+static void
+signature_test(struct sshkey *k, struct sshkey *bad, const char *sig_alg,
+ const u_char *d, size_t l)
+{
+ size_t len;
+ u_char *sig;
+
+ ASSERT_INT_EQ(sshkey_sign(k, &sig, &len, d, l, sig_alg, 0), 0);
+ ASSERT_SIZE_T_GT(len, 8);
+ ASSERT_PTR_NE(sig, NULL);
+ ASSERT_INT_EQ(sshkey_verify(k, sig, len, d, l, NULL, 0), 0);
+ ASSERT_INT_NE(sshkey_verify(bad, sig, len, d, l, NULL, 0), 0);
+ /* Fuzz test is more comprehensive, this is just a smoke test */
+ sig[len - 5] ^= 0x10;
+ ASSERT_INT_NE(sshkey_verify(k, sig, len, d, l, NULL, 0), 0);
+ free(sig);
+}
+
+static void
+banana(u_char *s, size_t l)
+{
+ size_t o;
+ const u_char the_banana[] = { 'b', 'a', 'n', 'a', 'n', 'a' };
+
+ for (o = 0; o < l; o += sizeof(the_banana)) {
+ if (l - o < sizeof(the_banana)) {
+ memcpy(s + o, "nanananana", l - o);
+ break;
+ }
+ memcpy(s + o, banana, sizeof(the_banana));
+ }
+}
+
+static void
+signature_tests(struct sshkey *k, struct sshkey *bad, const char *sig_alg)
+{
+ u_char i, buf[2049];
+ size_t lens[] = {
+ 1, 2, 7, 8, 9, 15, 16, 17, 31, 32, 33, 127, 128, 129,
+ 255, 256, 257, 1023, 1024, 1025, 2047, 2048, 2049
+ };
+
+ for (i = 0; i < (sizeof(lens)/sizeof(lens[0])); i++) {
+ test_subtest_info("%s key, banana length %zu",
+ sshkey_type(k), lens[i]);
+ banana(buf, lens[i]);
+ signature_test(k, bad, sig_alg, buf, lens[i]);
+ }
+}
+
+static struct sshkey *
+get_private(const char *n)
+{
+ struct sshbuf *b;
+ struct sshkey *ret;
+
+ b = load_file(n);
+ ASSERT_INT_EQ(sshkey_parse_private_fileblob(b, "", &ret, NULL), 0);
+ sshbuf_free(b);
+ return ret;
+}
+
+void
+sshkey_tests(void)
+{
+ struct sshkey *k1, *k2, *k3, *k4, *kr, *kd, *kf;
+#ifdef OPENSSL_HAS_ECC
+ struct sshkey *ke;
+#endif
+ struct sshbuf *b;
+
+ TEST_START("new invalid");
+ k1 = sshkey_new(-42);
+ ASSERT_PTR_EQ(k1, NULL);
+ TEST_DONE();
+
+ TEST_START("new/free KEY_UNSPEC");
+ k1 = sshkey_new(KEY_UNSPEC);
+ ASSERT_PTR_NE(k1, NULL);
+ sshkey_free(k1);
+ TEST_DONE();
+
+ TEST_START("new/free KEY_RSA");
+ k1 = sshkey_new(KEY_RSA);
+ ASSERT_PTR_NE(k1, NULL);
+ ASSERT_PTR_NE(k1->rsa, NULL);
+ sshkey_free(k1);
+ TEST_DONE();
+
+ TEST_START("new/free KEY_DSA");
+ k1 = sshkey_new(KEY_DSA);
+ ASSERT_PTR_NE(k1, NULL);
+ ASSERT_PTR_NE(k1->dsa, NULL);
+ sshkey_free(k1);
+ TEST_DONE();
+
+#ifdef OPENSSL_HAS_ECC
+ TEST_START("new/free KEY_ECDSA");
+ k1 = sshkey_new(KEY_ECDSA);
+ ASSERT_PTR_NE(k1, NULL);
+ ASSERT_PTR_EQ(k1->ecdsa, NULL); /* Can't allocate without NID */
+ sshkey_free(k1);
+ TEST_DONE();
+#endif
+
+ TEST_START("new/free KEY_ED25519");
+ k1 = sshkey_new(KEY_ED25519);
+ ASSERT_PTR_NE(k1, NULL);
+ /* These should be blank until key loaded or generated */
+ ASSERT_PTR_EQ(k1->ed25519_sk, NULL);
+ ASSERT_PTR_EQ(k1->ed25519_pk, NULL);
+ sshkey_free(k1);
+ TEST_DONE();
+
+ TEST_START("generate KEY_RSA too small modulus");
+ ASSERT_INT_EQ(sshkey_generate(KEY_RSA, 128, &k1),
+ SSH_ERR_KEY_LENGTH);
+ ASSERT_PTR_EQ(k1, NULL);
+ TEST_DONE();
+
+ TEST_START("generate KEY_RSA too large modulus");
+ ASSERT_INT_EQ(sshkey_generate(KEY_RSA, 1 << 20, &k1),
+ SSH_ERR_KEY_LENGTH);
+ ASSERT_PTR_EQ(k1, NULL);
+ TEST_DONE();
+
+ TEST_START("generate KEY_DSA wrong bits");
+ ASSERT_INT_EQ(sshkey_generate(KEY_DSA, 2048, &k1),
+ SSH_ERR_KEY_LENGTH);
+ ASSERT_PTR_EQ(k1, NULL);
+ sshkey_free(k1);
+ TEST_DONE();
+
+#ifdef OPENSSL_HAS_ECC
+ TEST_START("generate KEY_ECDSA wrong bits");
+ ASSERT_INT_EQ(sshkey_generate(KEY_ECDSA, 42, &k1),
+ SSH_ERR_KEY_LENGTH);
+ ASSERT_PTR_EQ(k1, NULL);
+ sshkey_free(k1);
+ TEST_DONE();
+#endif
+
+ TEST_START("generate KEY_RSA");
+ ASSERT_INT_EQ(sshkey_generate(KEY_RSA, 767, &kr),
+ SSH_ERR_KEY_LENGTH);
+ ASSERT_INT_EQ(sshkey_generate(KEY_RSA, 1024, &kr), 0);
+ ASSERT_PTR_NE(kr, NULL);
+ ASSERT_PTR_NE(kr->rsa, NULL);
+ ASSERT_PTR_NE(rsa_n(kr), NULL);
+ ASSERT_PTR_NE(rsa_e(kr), NULL);
+ ASSERT_PTR_NE(rsa_p(kr), NULL);
+ ASSERT_INT_EQ(BN_num_bits(rsa_n(kr)), 1024);
+ TEST_DONE();
+
+ TEST_START("generate KEY_DSA");
+ ASSERT_INT_EQ(sshkey_generate(KEY_DSA, 1024, &kd), 0);
+ ASSERT_PTR_NE(kd, NULL);
+ ASSERT_PTR_NE(kd->dsa, NULL);
+ ASSERT_PTR_NE(dsa_g(kd), NULL);
+ ASSERT_PTR_NE(dsa_priv_key(kd), NULL);
+ TEST_DONE();
+
+#ifdef OPENSSL_HAS_ECC
+ TEST_START("generate KEY_ECDSA");
+ ASSERT_INT_EQ(sshkey_generate(KEY_ECDSA, 256, &ke), 0);
+ ASSERT_PTR_NE(ke, NULL);
+ ASSERT_PTR_NE(ke->ecdsa, NULL);
+ ASSERT_PTR_NE(EC_KEY_get0_public_key(ke->ecdsa), NULL);
+ ASSERT_PTR_NE(EC_KEY_get0_private_key(ke->ecdsa), NULL);
+ TEST_DONE();
+#endif
+
+ TEST_START("generate KEY_ED25519");
+ ASSERT_INT_EQ(sshkey_generate(KEY_ED25519, 256, &kf), 0);
+ ASSERT_PTR_NE(kf, NULL);
+ ASSERT_INT_EQ(kf->type, KEY_ED25519);
+ ASSERT_PTR_NE(kf->ed25519_pk, NULL);
+ ASSERT_PTR_NE(kf->ed25519_sk, NULL);
+ TEST_DONE();
+
+ TEST_START("demote KEY_RSA");
+ ASSERT_INT_EQ(sshkey_from_private(kr, &k1), 0);
+ ASSERT_PTR_NE(k1, NULL);
+ ASSERT_PTR_NE(kr, k1);
+ ASSERT_INT_EQ(k1->type, KEY_RSA);
+ ASSERT_PTR_NE(k1->rsa, NULL);
+ ASSERT_PTR_NE(rsa_n(k1), NULL);
+ ASSERT_PTR_NE(rsa_e(k1), NULL);
+ ASSERT_PTR_EQ(rsa_p(k1), NULL);
+ TEST_DONE();
+
+ TEST_START("equal KEY_RSA/demoted KEY_RSA");
+ ASSERT_INT_EQ(sshkey_equal(kr, k1), 1);
+ sshkey_free(k1);
+ TEST_DONE();
+
+ TEST_START("demote KEY_DSA");
+ ASSERT_INT_EQ(sshkey_from_private(kd, &k1), 0);
+ ASSERT_PTR_NE(k1, NULL);
+ ASSERT_PTR_NE(kd, k1);
+ ASSERT_INT_EQ(k1->type, KEY_DSA);
+ ASSERT_PTR_NE(k1->dsa, NULL);
+ ASSERT_PTR_NE(dsa_g(k1), NULL);
+ ASSERT_PTR_EQ(dsa_priv_key(k1), NULL);
+ TEST_DONE();
+
+ TEST_START("equal KEY_DSA/demoted KEY_DSA");
+ ASSERT_INT_EQ(sshkey_equal(kd, k1), 1);
+ sshkey_free(k1);
+ TEST_DONE();
+
+#ifdef OPENSSL_HAS_ECC
+ TEST_START("demote KEY_ECDSA");
+ ASSERT_INT_EQ(sshkey_from_private(ke, &k1), 0);
+ ASSERT_PTR_NE(k1, NULL);
+ ASSERT_PTR_NE(ke, k1);
+ ASSERT_INT_EQ(k1->type, KEY_ECDSA);
+ ASSERT_PTR_NE(k1->ecdsa, NULL);
+ ASSERT_INT_EQ(k1->ecdsa_nid, ke->ecdsa_nid);
+ ASSERT_PTR_NE(EC_KEY_get0_public_key(ke->ecdsa), NULL);
+ ASSERT_PTR_EQ(EC_KEY_get0_private_key(k1->ecdsa), NULL);
+ TEST_DONE();
+
+ TEST_START("equal KEY_ECDSA/demoted KEY_ECDSA");
+ ASSERT_INT_EQ(sshkey_equal(ke, k1), 1);
+ sshkey_free(k1);
+ TEST_DONE();
+#endif
+
+ TEST_START("demote KEY_ED25519");
+ ASSERT_INT_EQ(sshkey_from_private(kf, &k1), 0);
+ ASSERT_PTR_NE(k1, NULL);
+ ASSERT_PTR_NE(kf, k1);
+ ASSERT_INT_EQ(k1->type, KEY_ED25519);
+ ASSERT_PTR_NE(k1->ed25519_pk, NULL);
+ ASSERT_PTR_EQ(k1->ed25519_sk, NULL);
+ TEST_DONE();
+
+ TEST_START("equal KEY_ED25519/demoted KEY_ED25519");
+ ASSERT_INT_EQ(sshkey_equal(kf, k1), 1);
+ sshkey_free(k1);
+ TEST_DONE();
+
+ TEST_START("equal mismatched key types");
+ ASSERT_INT_EQ(sshkey_equal(kd, kr), 0);
+#ifdef OPENSSL_HAS_ECC
+ ASSERT_INT_EQ(sshkey_equal(kd, ke), 0);
+ ASSERT_INT_EQ(sshkey_equal(kr, ke), 0);
+ ASSERT_INT_EQ(sshkey_equal(ke, kf), 0);
+#endif
+ ASSERT_INT_EQ(sshkey_equal(kd, kf), 0);
+ TEST_DONE();
+
+ TEST_START("equal different keys");
+ ASSERT_INT_EQ(sshkey_generate(KEY_RSA, 1024, &k1), 0);
+ ASSERT_INT_EQ(sshkey_equal(kr, k1), 0);
+ sshkey_free(k1);
+ ASSERT_INT_EQ(sshkey_generate(KEY_DSA, 1024, &k1), 0);
+ ASSERT_INT_EQ(sshkey_equal(kd, k1), 0);
+ sshkey_free(k1);
+#ifdef OPENSSL_HAS_ECC
+ ASSERT_INT_EQ(sshkey_generate(KEY_ECDSA, 256, &k1), 0);
+ ASSERT_INT_EQ(sshkey_equal(ke, k1), 0);
+ sshkey_free(k1);
+#endif
+ ASSERT_INT_EQ(sshkey_generate(KEY_ED25519, 256, &k1), 0);
+ ASSERT_INT_EQ(sshkey_equal(kf, k1), 0);
+ sshkey_free(k1);
+ TEST_DONE();
+
+ sshkey_free(kr);
+ sshkey_free(kd);
+#ifdef OPENSSL_HAS_ECC
+ sshkey_free(ke);
+#endif
+ sshkey_free(kf);
+
+ TEST_START("certify key");
+ ASSERT_INT_EQ(sshkey_load_public(test_data_file("ed25519_1.pub"),
+ &k1, NULL), 0);
+ k2 = get_private("ed25519_2");
+ ASSERT_INT_EQ(sshkey_to_certified(k1), 0);
+ ASSERT_PTR_NE(k1->cert, NULL);
+ k1->cert->type = SSH2_CERT_TYPE_USER;
+ k1->cert->serial = 1234;
+ k1->cert->key_id = strdup("estragon");
+ ASSERT_PTR_NE(k1->cert->key_id, NULL);
+ k1->cert->principals = calloc(4, sizeof(*k1->cert->principals));
+ ASSERT_PTR_NE(k1->cert->principals, NULL);
+ k1->cert->principals[0] = strdup("estragon");
+ k1->cert->principals[1] = strdup("vladimir");
+ k1->cert->principals[2] = strdup("pozzo");
+ k1->cert->principals[3] = strdup("lucky");
+ ASSERT_PTR_NE(k1->cert->principals[0], NULL);
+ ASSERT_PTR_NE(k1->cert->principals[1], NULL);
+ ASSERT_PTR_NE(k1->cert->principals[2], NULL);
+ ASSERT_PTR_NE(k1->cert->principals[3], NULL);
+ k1->cert->nprincipals = 4;
+ k1->cert->valid_after = 0;
+ k1->cert->valid_before = (u_int64_t)-1;
+ sshbuf_free(k1->cert->critical);
+ k1->cert->critical = sshbuf_new();
+ ASSERT_PTR_NE(k1->cert->critical, NULL);
+ sshbuf_free(k1->cert->extensions);
+ k1->cert->extensions = sshbuf_new();
+ ASSERT_PTR_NE(k1->cert->extensions, NULL);
+ put_opt(k1->cert->critical, "force-command", "/usr/bin/true");
+ put_opt(k1->cert->critical, "source-address", "127.0.0.1");
+ put_opt(k1->cert->extensions, "permit-X11-forwarding", NULL);
+ put_opt(k1->cert->extensions, "permit-agent-forwarding", NULL);
+ ASSERT_INT_EQ(sshkey_from_private(k2, &k1->cert->signature_key), 0);
+ ASSERT_INT_EQ(sshkey_certify(k1, k2, NULL), 0);
+ b = sshbuf_new();
+ ASSERT_PTR_NE(b, NULL);
+ ASSERT_INT_EQ(sshkey_putb(k1, b), 0);
+ ASSERT_INT_EQ(sshkey_from_blob(sshbuf_ptr(b), sshbuf_len(b), &k3), 0);
+
+ sshkey_free(k1);
+ sshkey_free(k2);
+ sshkey_free(k3);
+ sshbuf_reset(b);
+ TEST_DONE();
+
+ TEST_START("sign and verify RSA");
+ k1 = get_private("rsa_1");
+ ASSERT_INT_EQ(sshkey_load_public(test_data_file("rsa_2.pub"), &k2,
+ NULL), 0);
+ signature_tests(k1, k2, "ssh-rsa");
+ sshkey_free(k1);
+ sshkey_free(k2);
+ TEST_DONE();
+
+ TEST_START("sign and verify RSA-SHA256");
+ k1 = get_private("rsa_1");
+ ASSERT_INT_EQ(sshkey_load_public(test_data_file("rsa_2.pub"), &k2,
+ NULL), 0);
+ signature_tests(k1, k2, "rsa-sha2-256");
+ sshkey_free(k1);
+ sshkey_free(k2);
+ TEST_DONE();
+
+ TEST_START("sign and verify RSA-SHA512");
+ k1 = get_private("rsa_1");
+ ASSERT_INT_EQ(sshkey_load_public(test_data_file("rsa_2.pub"), &k2,
+ NULL), 0);
+ signature_tests(k1, k2, "rsa-sha2-512");
+ sshkey_free(k1);
+ sshkey_free(k2);
+ TEST_DONE();
+
+ TEST_START("sign and verify DSA");
+ k1 = get_private("dsa_1");
+ ASSERT_INT_EQ(sshkey_load_public(test_data_file("dsa_2.pub"), &k2,
+ NULL), 0);
+ signature_tests(k1, k2, NULL);
+ sshkey_free(k1);
+ sshkey_free(k2);
+ TEST_DONE();
+
+#ifdef OPENSSL_HAS_ECC
+ TEST_START("sign and verify ECDSA");
+ k1 = get_private("ecdsa_1");
+ ASSERT_INT_EQ(sshkey_load_public(test_data_file("ecdsa_2.pub"), &k2,
+ NULL), 0);
+ signature_tests(k1, k2, NULL);
+ sshkey_free(k1);
+ sshkey_free(k2);
+ TEST_DONE();
+#endif
+
+ TEST_START("sign and verify ED25519");
+ k1 = get_private("ed25519_1");
+ ASSERT_INT_EQ(sshkey_load_public(test_data_file("ed25519_2.pub"), &k2,
+ NULL), 0);
+ signature_tests(k1, k2, NULL);
+ sshkey_free(k1);
+ sshkey_free(k2);
+ TEST_DONE();
+
+ TEST_START("nested certificate");
+ ASSERT_INT_EQ(sshkey_load_cert(test_data_file("rsa_1"), &k1), 0);
+ ASSERT_INT_EQ(sshkey_load_public(test_data_file("rsa_1.pub"), &k2,
+ NULL), 0);
+ k3 = get_private("rsa_1");
+ build_cert(b, k2, "ssh-rsa-cert-v01@openssh.com", k3, k1, NULL);
+ ASSERT_INT_EQ(sshkey_from_blob(sshbuf_ptr(b), sshbuf_len(b), &k4),
+ SSH_ERR_KEY_CERT_INVALID_SIGN_KEY);
+ ASSERT_PTR_EQ(k4, NULL);
+ sshkey_free(k1);
+ sshkey_free(k2);
+ sshkey_free(k3);
+ sshbuf_free(b);
+ TEST_DONE();
+
+}
diff --git a/regress/unittests/sshkey/testdata/dsa_1 b/regress/unittests/sshkey/testdata/dsa_1
new file mode 100644
index 0000000..d3f2482
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/dsa_1
@@ -0,0 +1,12 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIBvAIBAAKBgQD6kutNFRsHTwEAv6d39Lhsqy1apdHBZ9c2HfyRr7WmypyGIy2m
+Ka43vzXI8CNwmRSYs+A6d0vJC7Pl+f9QzJ/04NWOA+MiwfurwrR3CRe61QRYb8Py
+mcHOxueHs95IcjrbIPNn86cjnPP5qvv/guUzCjuww4zBdJOXpligrGt2XwIVAKMD
+/50qQy7j8JaMk+1+Xtg1pK01AoGBAO7l9QVVbSSoy5lq6cOtvpf8UlwOa6+zBwbl
+o4gmFd1RwX1yWkA8kQ7RrhCSg8Hc6mIGnKRgKRli/3LgbSfZ0obFJehkRtEWtN4P
+h8fVUeS74iQbIwFQeKlYHIlNTRoGtAbdi3nHdV+BBkEQc1V3rjqYqhjOoz/yNsgz
+LND26HrdAoGBAOdXpyfmobEBaOqZAuvgj1P0uhjG2P31Ufurv22FWPBU3A9qrkxb
+OXwE0LwvjCvrsQV/lrYhJz/tiys40VeahulWZE5SAHMXGIf95LiLSgaXMjko7joo
+t+LK84ltLymwZ4QMnYjnZSSclf1UuyQMcUtb34+I0u9Ycnyhp2mSFsQtAhRYIbQ5
+KfXsZuBPuWe5FJz3ldaEgw==
+-----END DSA PRIVATE KEY-----
diff --git a/regress/unittests/sshkey/testdata/dsa_1-cert.fp b/regress/unittests/sshkey/testdata/dsa_1-cert.fp
new file mode 100644
index 0000000..75ff0e9
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/dsa_1-cert.fp
@@ -0,0 +1 @@
+SHA256:kOLgXSoAT8O5T6r36n5NJUYigbux1d7gdH/rmWiJm6s
diff --git a/regress/unittests/sshkey/testdata/dsa_1-cert.pub b/regress/unittests/sshkey/testdata/dsa_1-cert.pub
new file mode 100644
index 0000000..e768db1
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/dsa_1-cert.pub
@@ -0,0 +1 @@
+ssh-dss-cert-v01@openssh.com AAAAHHNzaC1kc3MtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgdTlbNU9Hn9Qng3FHxwH971bxCIoq1ern/QWFFDWXgmYAAACBAPqS600VGwdPAQC/p3f0uGyrLVql0cFn1zYd/JGvtabKnIYjLaYprje/NcjwI3CZFJiz4Dp3S8kLs+X5/1DMn/Tg1Y4D4yLB+6vCtHcJF7rVBFhvw/KZwc7G54ez3khyOtsg82fzpyOc8/mq+/+C5TMKO7DDjMF0k5emWKCsa3ZfAAAAFQCjA/+dKkMu4/CWjJPtfl7YNaStNQAAAIEA7uX1BVVtJKjLmWrpw62+l/xSXA5rr7MHBuWjiCYV3VHBfXJaQDyRDtGuEJKDwdzqYgacpGApGWL/cuBtJ9nShsUl6GRG0Ra03g+Hx9VR5LviJBsjAVB4qVgciU1NGga0Bt2Lecd1X4EGQRBzVXeuOpiqGM6jP/I2yDMs0Pboet0AAACBAOdXpyfmobEBaOqZAuvgj1P0uhjG2P31Ufurv22FWPBU3A9qrkxbOXwE0LwvjCvrsQV/lrYhJz/tiys40VeahulWZE5SAHMXGIf95LiLSgaXMjko7joot+LK84ltLymwZ4QMnYjnZSSclf1UuyQMcUtb34+I0u9Ycnyhp2mSFsQtAAAAAAAAAAYAAAACAAAABmp1bGl1cwAAABIAAAAFaG9zdDEAAAAFaG9zdDIAAAAANowB8AAAAABNHmBwAAAAAAAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACBThupGO0X+FLQhbz8CoKPwc7V3JNsQuGtlsgN+F7SMGQAAAFMAAAALc3NoLWVkMjU1MTkAAABAh/z1LIdNL1b66tQ8t9DY9BTB3BQKpTKmc7ezyFKLwl96yaIniZwD9Ticdbe/8i/Li3uCFE3EAt8NAIv9zff8Bg== DSA test key #1
diff --git a/regress/unittests/sshkey/testdata/dsa_1.fp b/regress/unittests/sshkey/testdata/dsa_1.fp
new file mode 100644
index 0000000..75ff0e9
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/dsa_1.fp
@@ -0,0 +1 @@
+SHA256:kOLgXSoAT8O5T6r36n5NJUYigbux1d7gdH/rmWiJm6s
diff --git a/regress/unittests/sshkey/testdata/dsa_1.fp.bb b/regress/unittests/sshkey/testdata/dsa_1.fp.bb
new file mode 100644
index 0000000..ba37776
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/dsa_1.fp.bb
@@ -0,0 +1 @@
+xetag-todiz-mifah-torec-mynyv-cyvit-gopon-pygag-rupic-cenav-bexax
diff --git a/regress/unittests/sshkey/testdata/dsa_1.param.g b/regress/unittests/sshkey/testdata/dsa_1.param.g
new file mode 100644
index 0000000..e51c3f9
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/dsa_1.param.g
@@ -0,0 +1 @@
+00eee5f505556d24a8cb996ae9c3adbe97fc525c0e6bafb30706e5a3882615dd51c17d725a403c910ed1ae109283c1dcea62069ca460291962ff72e06d27d9d286c525e86446d116b4de0f87c7d551e4bbe2241b23015078a9581c894d4d1a06b406dd8b79c7755f81064110735577ae3a98aa18cea33ff236c8332cd0f6e87add
diff --git a/regress/unittests/sshkey/testdata/dsa_1.param.priv b/regress/unittests/sshkey/testdata/dsa_1.param.priv
new file mode 100644
index 0000000..4f74331
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/dsa_1.param.priv
@@ -0,0 +1 @@
+5821b43929f5ec66e04fb967b9149cf795d68483
diff --git a/regress/unittests/sshkey/testdata/dsa_1.param.pub b/regress/unittests/sshkey/testdata/dsa_1.param.pub
new file mode 100644
index 0000000..ba0313b
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/dsa_1.param.pub
@@ -0,0 +1 @@
+00e757a727e6a1b10168ea9902ebe08f53f4ba18c6d8fdf551fbabbf6d8558f054dc0f6aae4c5b397c04d0bc2f8c2bebb1057f96b621273fed8b2b38d1579a86e956644e520073171887fde4b88b4a0697323928ee3a28b7e2caf3896d2f29b067840c9d88e765249c95fd54bb240c714b5bdf8f88d2ef58727ca1a7699216c42d
diff --git a/regress/unittests/sshkey/testdata/dsa_1.pub b/regress/unittests/sshkey/testdata/dsa_1.pub
new file mode 100644
index 0000000..41cae2f
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/dsa_1.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAPqS600VGwdPAQC/p3f0uGyrLVql0cFn1zYd/JGvtabKnIYjLaYprje/NcjwI3CZFJiz4Dp3S8kLs+X5/1DMn/Tg1Y4D4yLB+6vCtHcJF7rVBFhvw/KZwc7G54ez3khyOtsg82fzpyOc8/mq+/+C5TMKO7DDjMF0k5emWKCsa3ZfAAAAFQCjA/+dKkMu4/CWjJPtfl7YNaStNQAAAIEA7uX1BVVtJKjLmWrpw62+l/xSXA5rr7MHBuWjiCYV3VHBfXJaQDyRDtGuEJKDwdzqYgacpGApGWL/cuBtJ9nShsUl6GRG0Ra03g+Hx9VR5LviJBsjAVB4qVgciU1NGga0Bt2Lecd1X4EGQRBzVXeuOpiqGM6jP/I2yDMs0Pboet0AAACBAOdXpyfmobEBaOqZAuvgj1P0uhjG2P31Ufurv22FWPBU3A9qrkxbOXwE0LwvjCvrsQV/lrYhJz/tiys40VeahulWZE5SAHMXGIf95LiLSgaXMjko7joot+LK84ltLymwZ4QMnYjnZSSclf1UuyQMcUtb34+I0u9Ycnyhp2mSFsQt DSA test key #1
diff --git a/regress/unittests/sshkey/testdata/dsa_1_pw b/regress/unittests/sshkey/testdata/dsa_1_pw
new file mode 100644
index 0000000..24c7303
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/dsa_1_pw
@@ -0,0 +1,15 @@
+-----BEGIN DSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,BC8386C373B22EB7F00ADC821D5D8BE9
+
++HDV2DQ09sxrIAeXTz9r3YFuPRa2hk1+NGcr3ETkXbC6KiZ14wpTnGTloKwaQjIW
+eXTa9mpCOWAoohgvsVb+hOuOlP7AfeHu1IXV4EAS+GDpkiV5UxlCXXwqlD75Buu4
+wwDd/p4SWzILH3WGjDk5JIXoxWNY13LHwC7Q6gtGJx4AicUG7YBRTXMIBDa/Kh77
+6o2rFETKmp4VHBvHbakmiETfptdM8bbWxKWeY2vakThyESgeofsLoTOQCIwlEfJC
+s2D/KYL65C8VbHYgIoSLTQnooO45DDyxIuhCqP+H23mhv9vB1Od3nc2atgHj/XFs
+dcOPFkF/msDRYqxY3V0AS6+jpKwFodZ7g/hyGcyPxOkzlJVuKoKuH6P5PyQ69Gx0
+iqri0xEPyABr7kGlXNrjjctojX+B4WwSnjg/2euXXWFXCRalIdA7ErATTiQbGOx7
+Vd6Gn8PZbSy1MkqEDrZRip0pfAFJYI/8GXPC75BpnRsrVlfhtrngbW+kBP35LzaN
+l2K+RQ3gSB3iFoqNb1Kuu6T5MZlyVl5H2dVlJSeb1euQ2OycXdDoFTyJ4AiyWS7w
+Vlh8zeJnso5QRDjMwx99pZilbbuFGSLsahiGEveFc6o=
+-----END DSA PRIVATE KEY-----
diff --git a/regress/unittests/sshkey/testdata/dsa_2 b/regress/unittests/sshkey/testdata/dsa_2
new file mode 100644
index 0000000..3cc9631
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/dsa_2
@@ -0,0 +1,12 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIBvQIBAAKBgQCbyPXNdHeLsjpobPVCMkfagBkt15Zsltqf/PGNP1y1cuz7rsTX
+ZekQwUkSTNm5coqXe+ZOw2O4tjobJDd60I1/VPgaB0NYlQR9Hn87M284WD4f6VY+
+aunHmP134a8ybG5G4NqVNF3ihvxAR2pVITqb7kE46r2uYZNcNlHI8voRCwIVAMcP
+bwqFNsQbH5pJyZW30wj4KVZ3AoGBAIK98BVeKQVf8qDFqx9ovMuNgVSxpd+N0Yta
+5ZEy1OI2ziu5RhjueIM2K7Gq2Mnp38ob1AM53BUxqlcBJaHEDa6rj6yvuMgW9oCJ
+dImBM8sIFxfBbXNbpJiMaDwa6WyT84OkpDE6uuAepTMnWOUWkUVkAiyokHDUGXkG
+GyoQblbXAoGBAIsf7TaZ804sUWwRV0wI8DYx+hxD5QdrfYPYMtL2fHn3lICimGt0
+FTtUZ25jKg0E0DMBPdET6ZEHB3ZZkR8hFoUzZhdnyJMu3UjVtgaV88Ue3PrXxchk
+0W2jHPaAgQU3JIWzo8HFIFqvC/HEL+EyW3rBTY2uXM3XGI+YcWSA4ZrZAhUAsY2f
+bDFNzgZ4DaZ9wLRzTgOswPU=
+-----END DSA PRIVATE KEY-----
diff --git a/regress/unittests/sshkey/testdata/dsa_2.fp b/regress/unittests/sshkey/testdata/dsa_2.fp
new file mode 100644
index 0000000..51fbeb4
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/dsa_2.fp
@@ -0,0 +1 @@
+SHA256:ecwhWcXgpdBxZ2e+OjpRRY7dqXHHCD62BGtoVQQBwCk
diff --git a/regress/unittests/sshkey/testdata/dsa_2.fp.bb b/regress/unittests/sshkey/testdata/dsa_2.fp.bb
new file mode 100644
index 0000000..4d908ee
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/dsa_2.fp.bb
@@ -0,0 +1 @@
+xeser-megad-pocan-rozit-belup-tapoh-fapif-kyvit-vonav-cehab-naxax
diff --git a/regress/unittests/sshkey/testdata/dsa_2.pub b/regress/unittests/sshkey/testdata/dsa_2.pub
new file mode 100644
index 0000000..77bb555
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/dsa_2.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAJvI9c10d4uyOmhs9UIyR9qAGS3XlmyW2p/88Y0/XLVy7PuuxNdl6RDBSRJM2blyipd75k7DY7i2OhskN3rQjX9U+BoHQ1iVBH0efzszbzhYPh/pVj5q6ceY/XfhrzJsbkbg2pU0XeKG/EBHalUhOpvuQTjqva5hk1w2Ucjy+hELAAAAFQDHD28KhTbEGx+aScmVt9MI+ClWdwAAAIEAgr3wFV4pBV/yoMWrH2i8y42BVLGl343Ri1rlkTLU4jbOK7lGGO54gzYrsarYyenfyhvUAzncFTGqVwElocQNrquPrK+4yBb2gIl0iYEzywgXF8Ftc1ukmIxoPBrpbJPzg6SkMTq64B6lMydY5RaRRWQCLKiQcNQZeQYbKhBuVtcAAACBAIsf7TaZ804sUWwRV0wI8DYx+hxD5QdrfYPYMtL2fHn3lICimGt0FTtUZ25jKg0E0DMBPdET6ZEHB3ZZkR8hFoUzZhdnyJMu3UjVtgaV88Ue3PrXxchk0W2jHPaAgQU3JIWzo8HFIFqvC/HEL+EyW3rBTY2uXM3XGI+YcWSA4ZrZ DSA test key #2
diff --git a/regress/unittests/sshkey/testdata/dsa_n b/regress/unittests/sshkey/testdata/dsa_n
new file mode 100644
index 0000000..d3f2482
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/dsa_n
@@ -0,0 +1,12 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIBvAIBAAKBgQD6kutNFRsHTwEAv6d39Lhsqy1apdHBZ9c2HfyRr7WmypyGIy2m
+Ka43vzXI8CNwmRSYs+A6d0vJC7Pl+f9QzJ/04NWOA+MiwfurwrR3CRe61QRYb8Py
+mcHOxueHs95IcjrbIPNn86cjnPP5qvv/guUzCjuww4zBdJOXpligrGt2XwIVAKMD
+/50qQy7j8JaMk+1+Xtg1pK01AoGBAO7l9QVVbSSoy5lq6cOtvpf8UlwOa6+zBwbl
+o4gmFd1RwX1yWkA8kQ7RrhCSg8Hc6mIGnKRgKRli/3LgbSfZ0obFJehkRtEWtN4P
+h8fVUeS74iQbIwFQeKlYHIlNTRoGtAbdi3nHdV+BBkEQc1V3rjqYqhjOoz/yNsgz
+LND26HrdAoGBAOdXpyfmobEBaOqZAuvgj1P0uhjG2P31Ufurv22FWPBU3A9qrkxb
+OXwE0LwvjCvrsQV/lrYhJz/tiys40VeahulWZE5SAHMXGIf95LiLSgaXMjko7joo
+t+LK84ltLymwZ4QMnYjnZSSclf1UuyQMcUtb34+I0u9Ycnyhp2mSFsQtAhRYIbQ5
+KfXsZuBPuWe5FJz3ldaEgw==
+-----END DSA PRIVATE KEY-----
diff --git a/regress/unittests/sshkey/testdata/dsa_n_pw b/regress/unittests/sshkey/testdata/dsa_n_pw
new file mode 100644
index 0000000..24ac299
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/dsa_n_pw
@@ -0,0 +1,21 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jYmMAAAAGYmNyeXB0AAAAGAAAABCVs+LsMJ
+wnB5zM9U9pTXrGAAAAEAAAAAEAAAGzAAAAB3NzaC1kc3MAAACBAPqS600VGwdPAQC/p3f0
+uGyrLVql0cFn1zYd/JGvtabKnIYjLaYprje/NcjwI3CZFJiz4Dp3S8kLs+X5/1DMn/Tg1Y
+4D4yLB+6vCtHcJF7rVBFhvw/KZwc7G54ez3khyOtsg82fzpyOc8/mq+/+C5TMKO7DDjMF0
+k5emWKCsa3ZfAAAAFQCjA/+dKkMu4/CWjJPtfl7YNaStNQAAAIEA7uX1BVVtJKjLmWrpw6
+2+l/xSXA5rr7MHBuWjiCYV3VHBfXJaQDyRDtGuEJKDwdzqYgacpGApGWL/cuBtJ9nShsUl
+6GRG0Ra03g+Hx9VR5LviJBsjAVB4qVgciU1NGga0Bt2Lecd1X4EGQRBzVXeuOpiqGM6jP/
+I2yDMs0Pboet0AAACBAOdXpyfmobEBaOqZAuvgj1P0uhjG2P31Ufurv22FWPBU3A9qrkxb
+OXwE0LwvjCvrsQV/lrYhJz/tiys40VeahulWZE5SAHMXGIf95LiLSgaXMjko7joot+LK84
+ltLymwZ4QMnYjnZSSclf1UuyQMcUtb34+I0u9Ycnyhp2mSFsQtAAAB4HiOcRW4w+sIqBL0
+TPVbf0glN1hUi0rcE63Pqxmvxb8LkldC4IxAUagPrjhNAEW2AY42+CvPrtGB1z7gDADAIW
+xZX6wKwIcXP0Qh+xHE12F4u6mwfasssnAp4t1Ki8uCjMjnimgb3KdWpp0kiUV0oR062TXV
+PAdfrWjaq4fw0KOqbHIAG/v36AqzuqjSTfDbqvLZM3y0gp2Q1RxaQVJA5ZIKKyqRyFX7sr
+BaEIyCgeE3hM0EB7BycY1oIcS/eNxrACBWVJCENl5N7LtEYXNX7TANFniztfXzwaqGTT6A
+fCfbW4gz1UKldLUBzbIrPwMWlirAstbHvOf/2Iay2pNAs/SHhI0aF2jsGfvv5/D6N+r9dG
+B2SgDKBg7pywMH1DTvg6YT3P4GjCx0GUHqRCFLvD1rDdk4KSjvaRMpVq1PJ0/Wv6UGtsMS
+TR0PaEHDRNZqAX4YxqujnWrGKuRJhuz0eUvp7fZvbWHtiAMKV7368kkeUmkOHanb+TS+zs
+KINX8ev8zJZ6WVr8Vl+IQavpv0i2bXwS6QqbEuifpv/+uBb7pqRiU4u8en0eMdX1bZoTPM
+R6xHCnGD/Jpb3zS91Ya57T6CiXZ12KCaL6nWGnCkZVpzkfJ2HjFklWSWBQ6uyaosDQ==
+-----END OPENSSH PRIVATE KEY-----
diff --git a/regress/unittests/sshkey/testdata/ecdsa_1 b/regress/unittests/sshkey/testdata/ecdsa_1
new file mode 100644
index 0000000..80382b6
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/ecdsa_1
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIPPNyUAnjvFr+eT/7t/IyjuQQd/aLFiTY92LB9gIjyrMoAoGCCqGSM49
+AwEHoUQDQgAEDFlblkOrW9ydKVhtM+9AY3c9saBE7SG3lFx38nBavkADDaI9jh3/
+kvG/Jt9vpm22qwoklTCGDfzCkXkIKaWlBw==
+-----END EC PRIVATE KEY-----
diff --git a/regress/unittests/sshkey/testdata/ecdsa_1-cert.fp b/regress/unittests/sshkey/testdata/ecdsa_1-cert.fp
new file mode 100644
index 0000000..e48304f
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/ecdsa_1-cert.fp
@@ -0,0 +1 @@
+SHA256:8ty77fOpABat1y88aNdclQTfU+lVvWe7jYZGw8VYtfg
diff --git a/regress/unittests/sshkey/testdata/ecdsa_1-cert.pub b/regress/unittests/sshkey/testdata/ecdsa_1-cert.pub
new file mode 100644
index 0000000..55e2a25
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/ecdsa_1-cert.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp256-cert-v01@openssh.com AAAAKGVjZHNhLXNoYTItbmlzdHAyNTYtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgOtFRnMigkGliaYfPmX5IidVWfV3tRH6lqRXv0l8bvKoAAAAIbmlzdHAyNTYAAABBBAxZW5ZDq1vcnSlYbTPvQGN3PbGgRO0ht5Rcd/JwWr5AAw2iPY4d/5Lxvybfb6ZttqsKJJUwhg38wpF5CCmlpQcAAAAAAAAABwAAAAIAAAAGanVsaXVzAAAAEgAAAAVob3N0MQAAAAVob3N0MgAAAAA2jAHwAAAAAE0eYHAAAAAAAAAAAAAAAAAAAABoAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBAxZW5ZDq1vcnSlYbTPvQGN3PbGgRO0ht5Rcd/JwWr5AAw2iPY4d/5Lxvybfb6ZttqsKJJUwhg38wpF5CCmlpQcAAABkAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAABJAAAAIHbxGwTnue7KxhHXGFvRcxBnekhQ3Qx84vV/Vs4oVCrpAAAAIQC7vk2+d14aS7td7kVXLQn392oALjEBzMZoDvT1vT/zOA== ECDSA test key #1
diff --git a/regress/unittests/sshkey/testdata/ecdsa_1.fp b/regress/unittests/sshkey/testdata/ecdsa_1.fp
new file mode 100644
index 0000000..e48304f
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/ecdsa_1.fp
@@ -0,0 +1 @@
+SHA256:8ty77fOpABat1y88aNdclQTfU+lVvWe7jYZGw8VYtfg
diff --git a/regress/unittests/sshkey/testdata/ecdsa_1.fp.bb b/regress/unittests/sshkey/testdata/ecdsa_1.fp.bb
new file mode 100644
index 0000000..fa23c33
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/ecdsa_1.fp.bb
@@ -0,0 +1 @@
+xibah-vocun-sogyn-byhen-rivem-hegyh-luneh-dozyr-vatyf-dufid-myxyx
diff --git a/regress/unittests/sshkey/testdata/ecdsa_1.param.curve b/regress/unittests/sshkey/testdata/ecdsa_1.param.curve
new file mode 100644
index 0000000..fa04004
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/ecdsa_1.param.curve
@@ -0,0 +1 @@
+prime256v1
diff --git a/regress/unittests/sshkey/testdata/ecdsa_1.param.priv b/regress/unittests/sshkey/testdata/ecdsa_1.param.priv
new file mode 100644
index 0000000..dc908ad
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/ecdsa_1.param.priv
@@ -0,0 +1 @@
+00f3cdc940278ef16bf9e4ffeedfc8ca3b9041dfda2c589363dd8b07d8088f2acc
diff --git a/regress/unittests/sshkey/testdata/ecdsa_1.param.pub b/regress/unittests/sshkey/testdata/ecdsa_1.param.pub
new file mode 100644
index 0000000..71c9584
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/ecdsa_1.param.pub
@@ -0,0 +1 @@
+040c595b9643ab5bdc9d29586d33ef4063773db1a044ed21b7945c77f2705abe40030da23d8e1dff92f1bf26df6fa66db6ab0a249530860dfcc291790829a5a507
diff --git a/regress/unittests/sshkey/testdata/ecdsa_1.pub b/regress/unittests/sshkey/testdata/ecdsa_1.pub
new file mode 100644
index 0000000..84a71f9
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/ecdsa_1.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBAxZW5ZDq1vcnSlYbTPvQGN3PbGgRO0ht5Rcd/JwWr5AAw2iPY4d/5Lxvybfb6ZttqsKJJUwhg38wpF5CCmlpQc= ECDSA test key #1
diff --git a/regress/unittests/sshkey/testdata/ecdsa_1_pw b/regress/unittests/sshkey/testdata/ecdsa_1_pw
new file mode 100644
index 0000000..5c83a65
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/ecdsa_1_pw
@@ -0,0 +1,8 @@
+-----BEGIN EC PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,7BA38DE00F67851E4207216809C3BB15
+
+8QkFoZHQkj9a2mt032sp+WKaJ1fwteqWDd4RpAW9OzDgqzMx1QO43qJgBDTfhzjt
+M2Q8YfiGjfBEYpg4kCbacfcV68DEV4z6Ll7rIzzzO7OfWUNL++brD64vKx4z6f46
++sn4nbZTXilpkzi/nmPDVzrNmTSywA8T7Yf0QcBUxks=
+-----END EC PRIVATE KEY-----
diff --git a/regress/unittests/sshkey/testdata/ecdsa_2 b/regress/unittests/sshkey/testdata/ecdsa_2
new file mode 100644
index 0000000..0f4e844
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/ecdsa_2
@@ -0,0 +1,7 @@
+-----BEGIN EC PRIVATE KEY-----
+MIHcAgEBBEIBqBtN7e6Essd3dlsgISViPCXXC0atlNkGtoMgSQdBTKVUfeJOi4lc
+RZaXJdXnqWUqI/KEsH8h8QN4YcB8ugmAcc+gBwYFK4EEACOhgYkDgYYABAHZ2VNy
+oDedBwqsdzY+kkNptc9DrtRCVmO6cULLj+691MhItqVqTMJbTFlI4MnAg9PoGTF/
+0KmLJfy8vSffXGKqqwGKcFNtd1XCo+7Qu9tXbxron9g6Dmu7y8jaLkixcwZwnwLs
+6GmA9qZGuiAfOGV0Gf9/u98sr+vikOa4Ow5JFDTw5g==
+-----END EC PRIVATE KEY-----
diff --git a/regress/unittests/sshkey/testdata/ecdsa_2.fp b/regress/unittests/sshkey/testdata/ecdsa_2.fp
new file mode 100644
index 0000000..581e48a
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/ecdsa_2.fp
@@ -0,0 +1 @@
+SHA256:ed8YniRHA6qCrErCRnzrWxPHxYuA62a+CAFYUVxJgaI
diff --git a/regress/unittests/sshkey/testdata/ecdsa_2.fp.bb b/regress/unittests/sshkey/testdata/ecdsa_2.fp.bb
new file mode 100644
index 0000000..e1cc664
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/ecdsa_2.fp.bb
@@ -0,0 +1 @@
+xufag-danul-putub-mokin-pugaz-covid-dofag-nihuz-sysab-genar-zaxyx
diff --git a/regress/unittests/sshkey/testdata/ecdsa_2.param.curve b/regress/unittests/sshkey/testdata/ecdsa_2.param.curve
new file mode 100644
index 0000000..617ea2f
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/ecdsa_2.param.curve
@@ -0,0 +1 @@
+secp521r1
diff --git a/regress/unittests/sshkey/testdata/ecdsa_2.param.priv b/regress/unittests/sshkey/testdata/ecdsa_2.param.priv
new file mode 100644
index 0000000..dd898d9
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/ecdsa_2.param.priv
@@ -0,0 +1 @@
+01a81b4dedee84b2c777765b202125623c25d70b46ad94d906b683204907414ca5547de24e8b895c45969725d5e7a9652a23f284b07f21f1037861c07cba098071cf
diff --git a/regress/unittests/sshkey/testdata/ecdsa_2.param.pub b/regress/unittests/sshkey/testdata/ecdsa_2.param.pub
new file mode 100644
index 0000000..94301c9
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/ecdsa_2.param.pub
@@ -0,0 +1 @@
+0401d9d95372a0379d070aac77363e924369b5cf43aed4425663ba7142cb8feebdd4c848b6a56a4cc25b4c5948e0c9c083d3e819317fd0a98b25fcbcbd27df5c62aaab018a70536d7755c2a3eed0bbdb576f1ae89fd83a0e6bbbcbc8da2e48b17306709f02ece86980f6a646ba201f38657419ff7fbbdf2cafebe290e6b83b0e491434f0e6
diff --git a/regress/unittests/sshkey/testdata/ecdsa_2.pub b/regress/unittests/sshkey/testdata/ecdsa_2.pub
new file mode 100644
index 0000000..be9d84b
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/ecdsa_2.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAHZ2VNyoDedBwqsdzY+kkNptc9DrtRCVmO6cULLj+691MhItqVqTMJbTFlI4MnAg9PoGTF/0KmLJfy8vSffXGKqqwGKcFNtd1XCo+7Qu9tXbxron9g6Dmu7y8jaLkixcwZwnwLs6GmA9qZGuiAfOGV0Gf9/u98sr+vikOa4Ow5JFDTw5g== ECDSA test key #2
diff --git a/regress/unittests/sshkey/testdata/ecdsa_n b/regress/unittests/sshkey/testdata/ecdsa_n
new file mode 100644
index 0000000..80382b6
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/ecdsa_n
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIPPNyUAnjvFr+eT/7t/IyjuQQd/aLFiTY92LB9gIjyrMoAoGCCqGSM49
+AwEHoUQDQgAEDFlblkOrW9ydKVhtM+9AY3c9saBE7SG3lFx38nBavkADDaI9jh3/
+kvG/Jt9vpm22qwoklTCGDfzCkXkIKaWlBw==
+-----END EC PRIVATE KEY-----
diff --git a/regress/unittests/sshkey/testdata/ecdsa_n_pw b/regress/unittests/sshkey/testdata/ecdsa_n_pw
new file mode 100644
index 0000000..36b7fa7
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/ecdsa_n_pw
@@ -0,0 +1,9 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jYmMAAAAGYmNyeXB0AAAAGAAAABC4UwEov5
+z0RrCm7AMCxbuiAAAAEAAAAAEAAABoAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlz
+dHAyNTYAAABBBAxZW5ZDq1vcnSlYbTPvQGN3PbGgRO0ht5Rcd/JwWr5AAw2iPY4d/5Lxvy
+bfb6ZttqsKJJUwhg38wpF5CCmlpQcAAACgbCnAklQTHrf5qiHiMxKYwQJ7k/X9mp4fXD4v
+xUbgNZiXSxN26mn8mC2rH+WA6Lk3CexR/hrtLI2ndpBsYu1h6HhVkOwwm3Kd/PMKArCupW
+l6sYEabrT0EghXR/3aDEZvj79hgKSdu3RpayLvMdbCR8k1cg0/mDmR9hicWfeJ61n/IH05
+tUR268+0BVRW9kDhh/cuv8tVY4L09jCCQ6CpsA==
+-----END OPENSSH PRIVATE KEY-----
diff --git a/regress/unittests/sshkey/testdata/ed25519_1 b/regress/unittests/sshkey/testdata/ed25519_1
new file mode 100644
index 0000000..6b0ae01
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/ed25519_1
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACBThupGO0X+FLQhbz8CoKPwc7V3JNsQuGtlsgN+F7SMGQAAAJjnj4Ao54+A
+KAAAAAtzc2gtZWQyNTUxOQAAACBThupGO0X+FLQhbz8CoKPwc7V3JNsQuGtlsgN+F7SMGQ
+AAAED3KgoDbjR54V7bdNpfKlQY5m20UK1QaHytkCR+6rZEDFOG6kY7Rf4UtCFvPwKgo/Bz
+tXck2xC4a2WyA34XtIwZAAAAE0VEMjU1MTkgdGVzdCBrZXkgIzEBAg==
+-----END OPENSSH PRIVATE KEY-----
diff --git a/regress/unittests/sshkey/testdata/ed25519_1-cert.fp b/regress/unittests/sshkey/testdata/ed25519_1-cert.fp
new file mode 100644
index 0000000..a9674e2
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/ed25519_1-cert.fp
@@ -0,0 +1 @@
+SHA256:L3k/oJubblSY0lB9Ulsl7emDMnRPKm/8udf2ccwk560
diff --git a/regress/unittests/sshkey/testdata/ed25519_1-cert.pub b/regress/unittests/sshkey/testdata/ed25519_1-cert.pub
new file mode 100644
index 0000000..649b4e8
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/ed25519_1-cert.pub
@@ -0,0 +1 @@
+ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIIxzuxl4z3uwAIslne8Huft+1n1IhHAlNbWZkQyyECCGAAAAIFOG6kY7Rf4UtCFvPwKgo/BztXck2xC4a2WyA34XtIwZAAAAAAAAAAgAAAACAAAABmp1bGl1cwAAABIAAAAFaG9zdDEAAAAFaG9zdDIAAAAANowB8AAAAABNHmBwAAAAAAAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACBThupGO0X+FLQhbz8CoKPwc7V3JNsQuGtlsgN+F7SMGQAAAFMAAAALc3NoLWVkMjU1MTkAAABABGTn+Bmz86Ajk+iqKCSdP5NClsYzn4alJd0V5bizhP0Kumc/HbqQfSt684J1WdSzih+EjvnTgBhK9jTBKb90AQ== ED25519 test key #1
diff --git a/regress/unittests/sshkey/testdata/ed25519_1.fp b/regress/unittests/sshkey/testdata/ed25519_1.fp
new file mode 100644
index 0000000..a9674e2
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/ed25519_1.fp
@@ -0,0 +1 @@
+SHA256:L3k/oJubblSY0lB9Ulsl7emDMnRPKm/8udf2ccwk560
diff --git a/regress/unittests/sshkey/testdata/ed25519_1.fp.bb b/regress/unittests/sshkey/testdata/ed25519_1.fp.bb
new file mode 100644
index 0000000..309f2da
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/ed25519_1.fp.bb
@@ -0,0 +1 @@
+xubop-rekyd-bakal-nubuf-pahaf-gicuh-logeb-gocif-petod-galip-fuxux
diff --git a/regress/unittests/sshkey/testdata/ed25519_1.pub b/regress/unittests/sshkey/testdata/ed25519_1.pub
new file mode 100644
index 0000000..e533059
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/ed25519_1.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFOG6kY7Rf4UtCFvPwKgo/BztXck2xC4a2WyA34XtIwZ ED25519 test key #1
diff --git a/regress/unittests/sshkey/testdata/ed25519_1_pw b/regress/unittests/sshkey/testdata/ed25519_1_pw
new file mode 100644
index 0000000..c3b7ae7
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/ed25519_1_pw
@@ -0,0 +1,8 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jYmMAAAAGYmNyeXB0AAAAGAAAABCus+kaow
+AUjHphacvRp98dAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIFOG6kY7Rf4UtCFv
+PwKgo/BztXck2xC4a2WyA34XtIwZAAAAoJaqqgiYQuElraJAmYOm7Tb4nJ3eI4oj9mQ52M
+/Yd+ION2Ur1v8BDewpDX+LHEYgKHo3Mlmcn2UyF+QJ+7xUCW7QCtk/4szrJzw74DlEl6mH
+T8PT/f/av7PpECBD/YD3NoDlB9OWm/Q4sHcxfBEKfTGD7s2Onn71HgrdEOPqd4Sj/IQigR
+drfjtXEMlD32k9n3dd2eS9x7AHWYaGFEMkOcY=
+-----END OPENSSH PRIVATE KEY-----
diff --git a/regress/unittests/sshkey/testdata/ed25519_2 b/regress/unittests/sshkey/testdata/ed25519_2
new file mode 100644
index 0000000..e4aed63
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/ed25519_2
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACDPVKyLnm3eZE0lm0IfM3Uy9AsdGSBtozcoCt21blYBCwAAAJix1mBGsdZg
+RgAAAAtzc2gtZWQyNTUxOQAAACDPVKyLnm3eZE0lm0IfM3Uy9AsdGSBtozcoCt21blYBCw
+AAAECZEQHXs18o3DKjhUYaTyt+bUbhqfMeqmsKjYyFvzGVgs9UrIuebd5kTSWbQh8zdTL0
+Cx0ZIG2jNygK3bVuVgELAAAAE0VEMjU1MTkgdGVzdCBrZXkgIzEBAg==
+-----END OPENSSH PRIVATE KEY-----
diff --git a/regress/unittests/sshkey/testdata/ed25519_2.fp b/regress/unittests/sshkey/testdata/ed25519_2.fp
new file mode 100644
index 0000000..0496626
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/ed25519_2.fp
@@ -0,0 +1 @@
+SHA256:vMbaARqVciRgXyZPNHDo+P5p5WK5yWG1Oo6VC35Bomw
diff --git a/regress/unittests/sshkey/testdata/ed25519_2.fp.bb b/regress/unittests/sshkey/testdata/ed25519_2.fp.bb
new file mode 100644
index 0000000..abba789
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/ed25519_2.fp.bb
@@ -0,0 +1 @@
+xuces-bapyb-vikob-zesyv-budod-nupip-kebon-tacyc-fofed-lezic-soxax
diff --git a/regress/unittests/sshkey/testdata/ed25519_2.pub b/regress/unittests/sshkey/testdata/ed25519_2.pub
new file mode 100644
index 0000000..af34236
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/ed25519_2.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM9UrIuebd5kTSWbQh8zdTL0Cx0ZIG2jNygK3bVuVgEL ED25519 test key #1
diff --git a/regress/unittests/sshkey/testdata/pw b/regress/unittests/sshkey/testdata/pw
new file mode 100644
index 0000000..8a1dff9
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/pw
@@ -0,0 +1 @@
+mekmitasdigoat
diff --git a/regress/unittests/sshkey/testdata/rsa1_1 b/regress/unittests/sshkey/testdata/rsa1_1
new file mode 100644
index 0000000..161cc04
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa1_1
Binary files differ
diff --git a/regress/unittests/sshkey/testdata/rsa1_1.fp b/regress/unittests/sshkey/testdata/rsa1_1.fp
new file mode 100644
index 0000000..21b3d1a
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa1_1.fp
@@ -0,0 +1 @@
+SHA256:/kk7K9S9kwYFiFilnZYFwCsQJweI/SGQVR2nIa8VBhE
diff --git a/regress/unittests/sshkey/testdata/rsa1_1.fp.bb b/regress/unittests/sshkey/testdata/rsa1_1.fp.bb
new file mode 100644
index 0000000..62991b3
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa1_1.fp.bb
@@ -0,0 +1 @@
+xilil-nabyf-gynih-duheb-gokyp-bofet-nekac-bosod-lozin-kuvyh-poxix
diff --git a/regress/unittests/sshkey/testdata/rsa1_1.param.n b/regress/unittests/sshkey/testdata/rsa1_1.param.n
new file mode 100644
index 0000000..9a2549b
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa1_1.param.n
@@ -0,0 +1 @@
+00ce8ca77a556eba887f9a866c084a6402785354a81c10854d343181fa09351223a65f99915f8433d11a9c41677d307c03c3a39865b83e7172d2c1d878333c980438d6e4462106a0065cd75cfea7ca7f21538bf2f43f2af49cacee51b22e3bdcc5e87b59cc691f7c6942a77ef13bfdfb24300777b727348d0ba7900ba06b886729
diff --git a/regress/unittests/sshkey/testdata/rsa1_1.pub b/regress/unittests/sshkey/testdata/rsa1_1.pub
new file mode 100644
index 0000000..f665b0d
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa1_1.pub
@@ -0,0 +1 @@
+1024 65537 145043942670517902781741650890610683756045780348507433188994725700923246927874581962206512480287863636935077725837494808988986557337885675565086448774391442851909709751605441036910145362277967349042489937363543710406342212883803780768870873303921572812138116796733586484633244057911618360651775855949808953129 RSA1 test key #1
diff --git a/regress/unittests/sshkey/testdata/rsa1_1_pw b/regress/unittests/sshkey/testdata/rsa1_1_pw
new file mode 100644
index 0000000..e73c679
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa1_1_pw
Binary files differ
diff --git a/regress/unittests/sshkey/testdata/rsa1_2 b/regress/unittests/sshkey/testdata/rsa1_2
new file mode 100644
index 0000000..1d672dd
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa1_2
Binary files differ
diff --git a/regress/unittests/sshkey/testdata/rsa1_2.fp b/regress/unittests/sshkey/testdata/rsa1_2.fp
new file mode 100644
index 0000000..00516d5
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa1_2.fp
@@ -0,0 +1 @@
+SHA256:JaOeRCnLl/TLe7vn1+aQ4ONyKZCUhK5x3k4VHilmbpE
diff --git a/regress/unittests/sshkey/testdata/rsa1_2.fp.bb b/regress/unittests/sshkey/testdata/rsa1_2.fp.bb
new file mode 100644
index 0000000..b4989a5
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa1_2.fp.bb
@@ -0,0 +1 @@
+xipag-zohut-zepuk-pisyv-kamog-pupus-netud-tudis-melup-cynov-gaxox
diff --git a/regress/unittests/sshkey/testdata/rsa1_2.param.n b/regress/unittests/sshkey/testdata/rsa1_2.param.n
new file mode 100644
index 0000000..25d438d
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa1_2.param.n
@@ -0,0 +1 @@
+00cab091b57a154740c1bb7020f46a21a19dc40f647db2aab1babd30cabe241f0437391e68376ba35e48c624b8eaf6b59424d4c1a848c9fd1ef5cdc7c1b7f5e5df23b7ad513b79021286d38c52fdfae35656659e8649b2bf8bedf7c99664e45534007bd1c5dc3de1dafdf2d34ad087155951aa0f3d500b36d0d804bbccdef15ab31ca3dd40bdf5196065a97f397ef576caffb606be8232f6e0614aea0e979b9584296673fabb1dbd9f3212495c428842a2ab1f1768dd424fb6fdceeeab9126cacdfc834f0a0d09ba73ad8360d183ba85bb1565555cc6a536eb8d06df1a1e841107c021ae28a2d8b3465f9d8b58ef4045aea1c4ad7f8bf639574d6b142af67b4eb3
diff --git a/regress/unittests/sshkey/testdata/rsa1_2.pub b/regress/unittests/sshkey/testdata/rsa1_2.pub
new file mode 100644
index 0000000..acab6dd
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa1_2.pub
@@ -0,0 +1 @@
+2048 65537 25587207108642486834576012232250034427766229965612147538722032399009467293691448851087324679403117563681753304072089087252850866332601294130674473984011813227791089686736237645788471744456489819306046398653719249100878753563464696688916667605969658659855996383142110932332560049231682024775766802333675397528993897914717996946881193454997890776063024953924432026083898531677702536941151535135950834711001926404724453460085864892836473957600610133803037286539329764689125111700732309717375455919436557475211197800228646235077584780367991159670572954337165006813357814232200750568307753718414790655085790471723847208627 RSA1 test key #2
diff --git a/regress/unittests/sshkey/testdata/rsa_1 b/regress/unittests/sshkey/testdata/rsa_1
new file mode 100644
index 0000000..5de3f84
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa_1
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDLV5lUTt7FrADseB/CGhEZzpoojjEW5y8+ePvLppmK3MmMI18u
+d6vxzpK3bwZLYkVSyfJYI0HmIuGhdu7yMrW6wb84gbq8C31Xoe9EORcIUuGSvDKd
+NSM1SjlhDquRblDFB8kToqXyx1lqrXecXylxIUOL0jE+u0rU1967pDJx+wIDAQAB
+AoGAXyj5mpjmbD+YlxGIWz/zrM4hGsWgd4VteKEJxT6MMI4uzCRpkMd0ck8oHiwZ
+GAI/SwUzIsgtONQuH3AXVsUgghW4Ynn+8ksEv0IZ918WDMDwqvqkyrVzsOsZzqYj
+Pf8DUDKCpwFjnlknJ04yvWBZvVhWtY4OiZ8GV0Ttsu3k+GECQQD1YHfvBb5FdJBv
+Uhde2Il+jaFia8mwVVNNaiD2ECxXx6CzGz54ZLEB9NPVfDUZK8lJ4UJDqelWNh3i
+PF3RefWDAkEA1CVBzAFL4mNwpleVPzrfy69xP3gWOa26MxM/GE6zx9jC7HgQ3KPa
+WKdG/FuHs085aTRDaDLmGcZ8IvMuu7NgKQJAcIOKmxR0Gd8IN7NZugjqixggb0Pj
+mLKXXwESGiJyYtHL0zTj4Uqyi6Ya2GJ66o7UXscmnmYz828fJtTtZBdbRwJBALfi
+C2QvA32Zv/0PEXibKXy996WSC4G3ShwXZKtHHKHvCxY5BDSbehk59VesZrVPyG2e
+NYdOBxD0cIlCzJE56/ECQAndVkxvO8hwyEFGGwF3faHIAe/OxVb+MjaU25//Pe1/
+h/e6tlCk4w9CODpyV685gV394eYwMcGDcIkipTNUDZs=
+-----END RSA PRIVATE KEY-----
diff --git a/regress/unittests/sshkey/testdata/rsa_1-cert.fp b/regress/unittests/sshkey/testdata/rsa_1-cert.fp
new file mode 100644
index 0000000..79f380a
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa_1-cert.fp
@@ -0,0 +1 @@
+SHA256:l6itGumSMcRBBAFteCgmjQBIXqLK/jFGUH3viHX1RmE
diff --git a/regress/unittests/sshkey/testdata/rsa_1-cert.pub b/regress/unittests/sshkey/testdata/rsa_1-cert.pub
new file mode 100644
index 0000000..3bacf3c
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa_1-cert.pub
@@ -0,0 +1 @@
+ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAg98LhS2EHxLOWCLopZPwHdg/RJXusnkOqQXSc9R7aITkAAAADAQABAAAAgQDLV5lUTt7FrADseB/CGhEZzpoojjEW5y8+ePvLppmK3MmMI18ud6vxzpK3bwZLYkVSyfJYI0HmIuGhdu7yMrW6wb84gbq8C31Xoe9EORcIUuGSvDKdNSM1SjlhDquRblDFB8kToqXyx1lqrXecXylxIUOL0jE+u0rU1967pDJx+wAAAAAAAAAFAAAAAgAAAAZqdWxpdXMAAAASAAAABWhvc3QxAAAABWhvc3QyAAAAADaMAfAAAAAATR5gcAAAAAAAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgU4bqRjtF/hS0IW8/AqCj8HO1dyTbELhrZbIDfhe0jBkAAABTAAAAC3NzaC1lZDI1NTE5AAAAQI3QGlUCzC07KorupxpDkkGy6tniaZ8EvBflzvv+itXWNchGvfUeHmVT6aX0sRqehdz/lR+GmXRoZBhofwh0qAM= RSA test key #1
diff --git a/regress/unittests/sshkey/testdata/rsa_1.fp b/regress/unittests/sshkey/testdata/rsa_1.fp
new file mode 100644
index 0000000..79f380a
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa_1.fp
@@ -0,0 +1 @@
+SHA256:l6itGumSMcRBBAFteCgmjQBIXqLK/jFGUH3viHX1RmE
diff --git a/regress/unittests/sshkey/testdata/rsa_1.fp.bb b/regress/unittests/sshkey/testdata/rsa_1.fp.bb
new file mode 100644
index 0000000..45bacd5
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa_1.fp.bb
@@ -0,0 +1 @@
+xosis-fodod-votot-dibum-ryvac-rediz-naruf-votun-kevis-halis-gexux
diff --git a/regress/unittests/sshkey/testdata/rsa_1.param.n b/regress/unittests/sshkey/testdata/rsa_1.param.n
new file mode 100644
index 0000000..4933712
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa_1.param.n
@@ -0,0 +1 @@
+00cb5799544edec5ac00ec781fc21a1119ce9a288e3116e72f3e78fbcba6998adcc98c235f2e77abf1ce92b76f064b624552c9f2582341e622e1a176eef232b5bac1bf3881babc0b7d57a1ef4439170852e192bc329d3523354a39610eab916e50c507c913a2a5f2c7596aad779c5f297121438bd2313ebb4ad4d7debba43271fb
diff --git a/regress/unittests/sshkey/testdata/rsa_1.param.p b/regress/unittests/sshkey/testdata/rsa_1.param.p
new file mode 100644
index 0000000..4783d21
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa_1.param.p
@@ -0,0 +1 @@
+00f56077ef05be4574906f52175ed8897e8da1626bc9b055534d6a20f6102c57c7a0b31b3e7864b101f4d3d57c35192bc949e14243a9e956361de23c5dd179f583
diff --git a/regress/unittests/sshkey/testdata/rsa_1.param.q b/regress/unittests/sshkey/testdata/rsa_1.param.q
new file mode 100644
index 0000000..00fc8a2
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa_1.param.q
@@ -0,0 +1 @@
+00d42541cc014be26370a657953f3adfcbaf713f781639adba33133f184eb3c7d8c2ec7810dca3da58a746fc5b87b34f396934436832e619c67c22f32ebbb36029
diff --git a/regress/unittests/sshkey/testdata/rsa_1.pub b/regress/unittests/sshkey/testdata/rsa_1.pub
new file mode 100644
index 0000000..23ef872
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa_1.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDLV5lUTt7FrADseB/CGhEZzpoojjEW5y8+ePvLppmK3MmMI18ud6vxzpK3bwZLYkVSyfJYI0HmIuGhdu7yMrW6wb84gbq8C31Xoe9EORcIUuGSvDKdNSM1SjlhDquRblDFB8kToqXyx1lqrXecXylxIUOL0jE+u0rU1967pDJx+w== RSA test key #1
diff --git a/regress/unittests/sshkey/testdata/rsa_1_pw b/regress/unittests/sshkey/testdata/rsa_1_pw
new file mode 100644
index 0000000..b4c0674
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa_1_pw
@@ -0,0 +1,18 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,0C3F819F6EEA66A471BAEEDDA8171606
+
+AhQNxgw7Z2un3dpm6KPHF1u5qVvOczm0yiTyPK4U11B3TTRhXOHdzPLAcKMX71Xq
+fmLm2/JIZATUbLTaysLKIQlmAgtpmXoKLv9b90R3AXLophgToZzOLpvlQTCt+y9G
+0E3QQZG/LFy9BLNyw6uD5cy0RHT3FQb5VQDwfBvR/I+K3qWBFLlb7Rw9bCujYczu
+D3bimcDj/k6YkrWVsEa81Ch5RF2RClOYufti6bsvc4xIsB0Kd++vokER+kXFuQqf
+Tl0Jz+SG0kr9QtjVvkhBtSxzJ6/olAosoUySQ5hqsB8iECufBgp1KelXqsHFJQXy
+gCvVmGiivFUinX0rKOuWCHTplsSKQ9BnPSwDAAs8A7ZLcTXcLs/hMQ5r6fmOYfNN
+YthhjZyE2ciJO0lydGJUJMb5aJUak0rl+uINRlYCHTRLVwmCOmpfqz9SfcJb1ieU
+4Us8NR+pXJar4U0+C2wVlNJkAdpL6GvYxN6vp7vLa+BiFwIZOQozswacIZk/ScXm
+QL9rmWug51RCmDeenX46WTEZeB0o0+xi60sDEDhhe4+iNYcJu5L0BJ5lqRFe3I5n
+HRRv1mBEjbF2fDcg/ChYfOXsc4gDivH2nObabeASuMFZyadmXfA8tnXRZf+7Wuy/
+LZGYbM2xLeEyV3ss16WBHuIqexDt04OEZvs0jN90zj6Yv7qKCB975bdOcuKkN2Nn
+n9lA11R2pgsCs6COp9rYiWXkXZeDf3sW6kdcEV+/SzkVsv4JlHcsIzgk4WGVF/E/
+ZkU4J9AvSdJPzEQDM+yszp0eeUow4+SAgpuNTqZiUO/2UUVbsr3qvlYMoCixhFAN
+-----END RSA PRIVATE KEY-----
diff --git a/regress/unittests/sshkey/testdata/rsa_1_sha1 b/regress/unittests/sshkey/testdata/rsa_1_sha1
new file mode 100644
index 0000000..5de3f84
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa_1_sha1
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDLV5lUTt7FrADseB/CGhEZzpoojjEW5y8+ePvLppmK3MmMI18u
+d6vxzpK3bwZLYkVSyfJYI0HmIuGhdu7yMrW6wb84gbq8C31Xoe9EORcIUuGSvDKd
+NSM1SjlhDquRblDFB8kToqXyx1lqrXecXylxIUOL0jE+u0rU1967pDJx+wIDAQAB
+AoGAXyj5mpjmbD+YlxGIWz/zrM4hGsWgd4VteKEJxT6MMI4uzCRpkMd0ck8oHiwZ
+GAI/SwUzIsgtONQuH3AXVsUgghW4Ynn+8ksEv0IZ918WDMDwqvqkyrVzsOsZzqYj
+Pf8DUDKCpwFjnlknJ04yvWBZvVhWtY4OiZ8GV0Ttsu3k+GECQQD1YHfvBb5FdJBv
+Uhde2Il+jaFia8mwVVNNaiD2ECxXx6CzGz54ZLEB9NPVfDUZK8lJ4UJDqelWNh3i
+PF3RefWDAkEA1CVBzAFL4mNwpleVPzrfy69xP3gWOa26MxM/GE6zx9jC7HgQ3KPa
+WKdG/FuHs085aTRDaDLmGcZ8IvMuu7NgKQJAcIOKmxR0Gd8IN7NZugjqixggb0Pj
+mLKXXwESGiJyYtHL0zTj4Uqyi6Ya2GJ66o7UXscmnmYz828fJtTtZBdbRwJBALfi
+C2QvA32Zv/0PEXibKXy996WSC4G3ShwXZKtHHKHvCxY5BDSbehk59VesZrVPyG2e
+NYdOBxD0cIlCzJE56/ECQAndVkxvO8hwyEFGGwF3faHIAe/OxVb+MjaU25//Pe1/
+h/e6tlCk4w9CODpyV685gV394eYwMcGDcIkipTNUDZs=
+-----END RSA PRIVATE KEY-----
diff --git a/regress/unittests/sshkey/testdata/rsa_1_sha1-cert.pub b/regress/unittests/sshkey/testdata/rsa_1_sha1-cert.pub
new file mode 100644
index 0000000..ff49d75
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa_1_sha1-cert.pub
@@ -0,0 +1 @@
+ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgy5PGFfSaEuSuXsjvKlMZGXYD0xlnqdZftuW9tMkUYz4AAAADAQABAAAAgQDLV5lUTt7FrADseB/CGhEZzpoojjEW5y8+ePvLppmK3MmMI18ud6vxzpK3bwZLYkVSyfJYI0HmIuGhdu7yMrW6wb84gbq8C31Xoe9EORcIUuGSvDKdNSM1SjlhDquRblDFB8kToqXyx1lqrXecXylxIUOL0jE+u0rU1967pDJx+wAAAAAAAAABAAAAAQAAAARodWdvAAAAEgAAAAV1c2VyMQAAAAV1c2VyMgAAAAA2i4NgAAAAAE0d4eAAAABEAAAADWZvcmNlLWNvbW1hbmQAAAALAAAABy9iaW4vbHMAAAAOc291cmNlLWFkZHJlc3MAAAAOAAAACjEwLjAuMC4wLzgAAABkAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAEXAAAAB3NzaC1yc2EAAAADAQABAAABAQD00RRenvxICSYvj54CPiYHM86OT5xwI9XORNH6Zkl3JPCQkAEdQ3hyfhraROaHsSv43wJcKyKrEg5XUZ8fZ/BoKIGU4Rd5AmL9wyPGv2RVY7gWELqXVSpu89R2tQJRmMVMD38CH0wqCTuoZirlKMTen6yfgYuFEpuqar0uOIeAyaQG6/9rVKWK36tcfM7YXx8fmGSN4eK/JhWDDjlo28YJ7ZFF9umh5baZG2Ai/vL3BJ7C3pqaEQNdKj8XqaSoDvFWKfOujk1620Rcuj3W0D0dvp/rH8xz8YkM1dMqGlYIZ4nrF5acB58Nk5FYBjtj1hu4DGEQlWL1Avk1agU4DQLrAAABDwAAAAdzc2gtcnNhAAABAF5BtPY8FbmIekK/zNq6/Lp5agKT5zEVxqAyZKhp75bLRP+kOMZBVB9ZWrekZk6IAVAOCZGQzTsD4mxIQsxBLl8k5hvEWb90/+w9/BzW9ScOGQe+y0COa0QWWR7L3k1S8WX2oAGvtDWOj7Md85nij4ZSU9/QQQFVDF8VilWPSMxUf/3I1fqyDq7AWcZkGk/bFUN6K6RsCSxIPlGmKt0IauyvSMI2IT0XeRT242RngeeUW8vFrn9TXy9YxJRW+cSeLKCuu8agBYyQMXWQ+q39eZZqVYSoo7nFEEhtaLs8d6jzgGkcE9wGJ9KLgfY/mG2vX3gI4IzncKkVJRoeiDzXFIk= RSA test key #1
diff --git a/regress/unittests/sshkey/testdata/rsa_1_sha1.pub b/regress/unittests/sshkey/testdata/rsa_1_sha1.pub
new file mode 100644
index 0000000..23ef872
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa_1_sha1.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDLV5lUTt7FrADseB/CGhEZzpoojjEW5y8+ePvLppmK3MmMI18ud6vxzpK3bwZLYkVSyfJYI0HmIuGhdu7yMrW6wb84gbq8C31Xoe9EORcIUuGSvDKdNSM1SjlhDquRblDFB8kToqXyx1lqrXecXylxIUOL0jE+u0rU1967pDJx+w== RSA test key #1
diff --git a/regress/unittests/sshkey/testdata/rsa_1_sha512 b/regress/unittests/sshkey/testdata/rsa_1_sha512
new file mode 100644
index 0000000..5de3f84
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa_1_sha512
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDLV5lUTt7FrADseB/CGhEZzpoojjEW5y8+ePvLppmK3MmMI18u
+d6vxzpK3bwZLYkVSyfJYI0HmIuGhdu7yMrW6wb84gbq8C31Xoe9EORcIUuGSvDKd
+NSM1SjlhDquRblDFB8kToqXyx1lqrXecXylxIUOL0jE+u0rU1967pDJx+wIDAQAB
+AoGAXyj5mpjmbD+YlxGIWz/zrM4hGsWgd4VteKEJxT6MMI4uzCRpkMd0ck8oHiwZ
+GAI/SwUzIsgtONQuH3AXVsUgghW4Ynn+8ksEv0IZ918WDMDwqvqkyrVzsOsZzqYj
+Pf8DUDKCpwFjnlknJ04yvWBZvVhWtY4OiZ8GV0Ttsu3k+GECQQD1YHfvBb5FdJBv
+Uhde2Il+jaFia8mwVVNNaiD2ECxXx6CzGz54ZLEB9NPVfDUZK8lJ4UJDqelWNh3i
+PF3RefWDAkEA1CVBzAFL4mNwpleVPzrfy69xP3gWOa26MxM/GE6zx9jC7HgQ3KPa
+WKdG/FuHs085aTRDaDLmGcZ8IvMuu7NgKQJAcIOKmxR0Gd8IN7NZugjqixggb0Pj
+mLKXXwESGiJyYtHL0zTj4Uqyi6Ya2GJ66o7UXscmnmYz828fJtTtZBdbRwJBALfi
+C2QvA32Zv/0PEXibKXy996WSC4G3ShwXZKtHHKHvCxY5BDSbehk59VesZrVPyG2e
+NYdOBxD0cIlCzJE56/ECQAndVkxvO8hwyEFGGwF3faHIAe/OxVb+MjaU25//Pe1/
+h/e6tlCk4w9CODpyV685gV394eYwMcGDcIkipTNUDZs=
+-----END RSA PRIVATE KEY-----
diff --git a/regress/unittests/sshkey/testdata/rsa_1_sha512-cert.pub b/regress/unittests/sshkey/testdata/rsa_1_sha512-cert.pub
new file mode 100644
index 0000000..4745196
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa_1_sha512-cert.pub
@@ -0,0 +1 @@
+ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAg/bUEmnMYHxlv1N7iXvnYPYdzDjlTRKoaIGEPkaQQQDwAAAADAQABAAAAgQDLV5lUTt7FrADseB/CGhEZzpoojjEW5y8+ePvLppmK3MmMI18ud6vxzpK3bwZLYkVSyfJYI0HmIuGhdu7yMrW6wb84gbq8C31Xoe9EORcIUuGSvDKdNSM1SjlhDquRblDFB8kToqXyx1lqrXecXylxIUOL0jE+u0rU1967pDJx+wAAAAAAAAABAAAAAQAAAARodWdvAAAAEgAAAAV1c2VyMQAAAAV1c2VyMgAAAAA2i4NgAAAAAE0d4eAAAABEAAAADWZvcmNlLWNvbW1hbmQAAAALAAAABy9iaW4vbHMAAAAOc291cmNlLWFkZHJlc3MAAAAOAAAACjEwLjAuMC4wLzgAAABkAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAEXAAAAB3NzaC1yc2EAAAADAQABAAABAQD00RRenvxICSYvj54CPiYHM86OT5xwI9XORNH6Zkl3JPCQkAEdQ3hyfhraROaHsSv43wJcKyKrEg5XUZ8fZ/BoKIGU4Rd5AmL9wyPGv2RVY7gWELqXVSpu89R2tQJRmMVMD38CH0wqCTuoZirlKMTen6yfgYuFEpuqar0uOIeAyaQG6/9rVKWK36tcfM7YXx8fmGSN4eK/JhWDDjlo28YJ7ZFF9umh5baZG2Ai/vL3BJ7C3pqaEQNdKj8XqaSoDvFWKfOujk1620Rcuj3W0D0dvp/rH8xz8YkM1dMqGlYIZ4nrF5acB58Nk5FYBjtj1hu4DGEQlWL1Avk1agU4DQLrAAABFAAAAAxyc2Etc2hhMi01MTIAAAEA7/GoZsJqrq4xYotsRbpM8arZDjCzT6kohXeD/GVy26s5E/YWXRYCrOMIzSZxjuN5rAaNRW8ffxq14JyI94566Kg2OeoxQ6rK/dTqkk7I1RyypSXunT3I4++RPs1Q+hu9eS/WBzur0/D3dMejhuc3IBg6iB0481I4pGBGcD8/KjQFfhlCuGVXwB1ALk2zfXFT1HYYrs6bYZuQqjgvArnjYJ0do3fTSDC20/ydV4BHnI3fVAY2THVjX45V2ppPadl/rpczaJqW1ZtpnpJkV8Un316stQSD0xLHUDjp89O6d9Yq5S0kDdfwTRJIPm9f2cGNakJwN5qzmmmdDroRKODYcg== RSA test key #1
diff --git a/regress/unittests/sshkey/testdata/rsa_1_sha512.pub b/regress/unittests/sshkey/testdata/rsa_1_sha512.pub
new file mode 100644
index 0000000..23ef872
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa_1_sha512.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDLV5lUTt7FrADseB/CGhEZzpoojjEW5y8+ePvLppmK3MmMI18ud6vxzpK3bwZLYkVSyfJYI0HmIuGhdu7yMrW6wb84gbq8C31Xoe9EORcIUuGSvDKdNSM1SjlhDquRblDFB8kToqXyx1lqrXecXylxIUOL0jE+u0rU1967pDJx+w== RSA test key #1
diff --git a/regress/unittests/sshkey/testdata/rsa_2 b/regress/unittests/sshkey/testdata/rsa_2
new file mode 100644
index 0000000..2441d52
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa_2
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA9NEUXp78SAkmL4+eAj4mBzPOjk+ccCPVzkTR+mZJdyTwkJAB
+HUN4cn4a2kTmh7Er+N8CXCsiqxIOV1GfH2fwaCiBlOEXeQJi/cMjxr9kVWO4FhC6
+l1UqbvPUdrUCUZjFTA9/Ah9MKgk7qGYq5SjE3p+sn4GLhRKbqmq9LjiHgMmkBuv/
+a1Slit+rXHzO2F8fH5hkjeHivyYVgw45aNvGCe2RRfbpoeW2mRtgIv7y9wSewt6a
+mhEDXSo/F6mkqA7xVinzro5NettEXLo91tA9Hb6f6x/Mc/GJDNXTKhpWCGeJ6xeW
+nAefDZORWAY7Y9YbuAxhEJVi9QL5NWoFOA0C6wIDAQABAoIBAQDtRGVVfwhKWHOl
+zK76xXjdqhwaWJXpKRHiI1jOMawpyKdNtAMgdW+apxUnTXePMurG/HuxEC09VvaH
+MhfhvD6G9BsCS1UQdnuyLRnTWVLIXyjeWcA9QtEpTy8vDSb+Je2xVaNmTybl5qTn
+BH22Mtj6Wg5XWJn7kplDhMdssGTDLsSCMw/rcxe9iT2qOKyltQal23RHzR7SijGp
+QTtBp2SDGhvMZcyGuyMqJ084W8sdJpbyVzdDim2iaZdHlk7uvW2n0HcJ56I6yhIq
+2U8wfgEEwydGVGHgmQNJ/n+SiT/hv6g5ebhDS46X9F9m5CHDwhdr0DrhPBVSsdhl
+1HeJ0+FhAoGBAPuC3uNHToiJis688juKlwc3SQ6ger5ffAg3yaNhEcpHkvOtdZlF
+/CfX94xazMov/YqFwkvpSSdKsX+PeXuaqnb1hPKNYX5t45U9RjB/ox7BIQj/2rPx
+Bfs99UFW9HKP4HsVmLu1xeJg1Pc9iylTK/xrnwfYiZ+H7IGVccizjnqHAoGBAPkv
+n1flAdxBzJH/O0rXoig2EtZsDRMPY51MGDdqVOW14ZOfTVlmu0OSnkSKQm2twfro
+TPDVb2TY3wTRutz8H9yOFW1c1Nz4YOyTb8FmJhE2FWAQ9t8QpwUlhn15if72dS/Y
+22+vP+AYu7wfqGL7QVVEXho5hGjXi053iEvfXBl9AoGAeZISpo1LGphRLgkKlVky
+E1zXxWgwrGB/FYHRx1UeQkZCc+K+Wy4G6kNr9r3VC04TIafx+Lt0jrd+AIibUfG6
+v/GBJ7TLEU+QmAycJskrUaxMiYsSbbPtDjoumDytv8pn2VbhEqqUUg44IqHu6DS5
+qDNlFWfHbgNHgIN6EmcoUXUCgYEAi2G57X4pRjx/4wIy9jAbggaNDuctgQXQoIGZ
+4hVWG49a+CnZKDKweKGgaZI0igjxQhmCQAwC3RP520Y9EbLtV38aOSv93QQJowrt
+Le6nSGVKG4whqrAz3EsbKUA8kiLldbgFNjl+ryjmidnjZEpKRxmQ0XZuu/4k6+Us
+ldQAPjkCgYBwjSm5eDUtK2eEPaBtbJykV05CTv5rn6CKC9L7ZBTkCcdU1hxeqe99
+wb22decnNawGRP1a5cGwqKJPOfkgybJVkdr6aqQW8ClzdFSaenjzs+nVW+T9JTXf
+9lFpIZg5kN/geld3B9B4C99riTM0jg9hbe2RQvpLRTrZbnWMA1XoRw==
+-----END RSA PRIVATE KEY-----
diff --git a/regress/unittests/sshkey/testdata/rsa_2.fp b/regress/unittests/sshkey/testdata/rsa_2.fp
new file mode 100644
index 0000000..4659639
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa_2.fp
@@ -0,0 +1 @@
+SHA256:NoQh0XBUuYUSWqnzOzOBnfpgJTRWLMj7BlWAb8IbjeE
diff --git a/regress/unittests/sshkey/testdata/rsa_2.fp.bb b/regress/unittests/sshkey/testdata/rsa_2.fp.bb
new file mode 100644
index 0000000..e9d1e4a
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa_2.fp.bb
@@ -0,0 +1 @@
+xogit-gupof-mydon-hocep-zuval-feson-rarif-cefar-tobar-ryvap-kuxex
diff --git a/regress/unittests/sshkey/testdata/rsa_2.param.n b/regress/unittests/sshkey/testdata/rsa_2.param.n
new file mode 100644
index 0000000..a669dbf
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa_2.param.n
@@ -0,0 +1 @@
+00f4d1145e9efc4809262f8f9e023e260733ce8e4f9c7023d5ce44d1fa66497724f09090011d4378727e1ada44e687b12bf8df025c2b22ab120e57519f1f67f068288194e117790262fdc323c6bf645563b81610ba97552a6ef3d476b5025198c54c0f7f021f4c2a093ba8662ae528c4de9fac9f818b85129baa6abd2e388780c9a406ebff6b54a58adfab5c7cced85f1f1f98648de1e2bf2615830e3968dbc609ed9145f6e9a1e5b6991b6022fef2f7049ec2de9a9a11035d2a3f17a9a4a80ef15629f3ae8e4d7adb445cba3dd6d03d1dbe9feb1fcc73f1890cd5d32a1a56086789eb17969c079f0d939158063b63d61bb80c61109562f502f9356a05380d02eb
diff --git a/regress/unittests/sshkey/testdata/rsa_2.param.p b/regress/unittests/sshkey/testdata/rsa_2.param.p
new file mode 100644
index 0000000..be7c1c3
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa_2.param.p
@@ -0,0 +1 @@
+00fb82dee3474e88898acebcf23b8a970737490ea07abe5f7c0837c9a36111ca4792f3ad759945fc27d7f78c5accca2ffd8a85c24be949274ab17f8f797b9aaa76f584f28d617e6de3953d46307fa31ec12108ffdab3f105fb3df54156f4728fe07b1598bbb5c5e260d4f73d8b29532bfc6b9f07d8899f87ec819571c8b38e7a87
diff --git a/regress/unittests/sshkey/testdata/rsa_2.param.q b/regress/unittests/sshkey/testdata/rsa_2.param.q
new file mode 100644
index 0000000..6f2c542
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa_2.param.q
@@ -0,0 +1 @@
+00f92f9f57e501dc41cc91ff3b4ad7a2283612d66c0d130f639d4c18376a54e5b5e1939f4d5966bb43929e448a426dadc1fae84cf0d56f64d8df04d1badcfc1fdc8e156d5cd4dcf860ec936fc166261136156010f6df10a70525867d7989fef6752fd8db6faf3fe018bbbc1fa862fb4155445e1a398468d78b4e77884bdf5c197d
diff --git a/regress/unittests/sshkey/testdata/rsa_2.pub b/regress/unittests/sshkey/testdata/rsa_2.pub
new file mode 100644
index 0000000..3322fbc
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa_2.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD00RRenvxICSYvj54CPiYHM86OT5xwI9XORNH6Zkl3JPCQkAEdQ3hyfhraROaHsSv43wJcKyKrEg5XUZ8fZ/BoKIGU4Rd5AmL9wyPGv2RVY7gWELqXVSpu89R2tQJRmMVMD38CH0wqCTuoZirlKMTen6yfgYuFEpuqar0uOIeAyaQG6/9rVKWK36tcfM7YXx8fmGSN4eK/JhWDDjlo28YJ7ZFF9umh5baZG2Ai/vL3BJ7C3pqaEQNdKj8XqaSoDvFWKfOujk1620Rcuj3W0D0dvp/rH8xz8YkM1dMqGlYIZ4nrF5acB58Nk5FYBjtj1hu4DGEQlWL1Avk1agU4DQLr RSA test key #2
diff --git a/regress/unittests/sshkey/testdata/rsa_n b/regress/unittests/sshkey/testdata/rsa_n
new file mode 100644
index 0000000..5de3f84
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa_n
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDLV5lUTt7FrADseB/CGhEZzpoojjEW5y8+ePvLppmK3MmMI18u
+d6vxzpK3bwZLYkVSyfJYI0HmIuGhdu7yMrW6wb84gbq8C31Xoe9EORcIUuGSvDKd
+NSM1SjlhDquRblDFB8kToqXyx1lqrXecXylxIUOL0jE+u0rU1967pDJx+wIDAQAB
+AoGAXyj5mpjmbD+YlxGIWz/zrM4hGsWgd4VteKEJxT6MMI4uzCRpkMd0ck8oHiwZ
+GAI/SwUzIsgtONQuH3AXVsUgghW4Ynn+8ksEv0IZ918WDMDwqvqkyrVzsOsZzqYj
+Pf8DUDKCpwFjnlknJ04yvWBZvVhWtY4OiZ8GV0Ttsu3k+GECQQD1YHfvBb5FdJBv
+Uhde2Il+jaFia8mwVVNNaiD2ECxXx6CzGz54ZLEB9NPVfDUZK8lJ4UJDqelWNh3i
+PF3RefWDAkEA1CVBzAFL4mNwpleVPzrfy69xP3gWOa26MxM/GE6zx9jC7HgQ3KPa
+WKdG/FuHs085aTRDaDLmGcZ8IvMuu7NgKQJAcIOKmxR0Gd8IN7NZugjqixggb0Pj
+mLKXXwESGiJyYtHL0zTj4Uqyi6Ya2GJ66o7UXscmnmYz828fJtTtZBdbRwJBALfi
+C2QvA32Zv/0PEXibKXy996WSC4G3ShwXZKtHHKHvCxY5BDSbehk59VesZrVPyG2e
+NYdOBxD0cIlCzJE56/ECQAndVkxvO8hwyEFGGwF3faHIAe/OxVb+MjaU25//Pe1/
+h/e6tlCk4w9CODpyV685gV394eYwMcGDcIkipTNUDZs=
+-----END RSA PRIVATE KEY-----
diff --git a/regress/unittests/sshkey/testdata/rsa_n_pw b/regress/unittests/sshkey/testdata/rsa_n_pw
new file mode 100644
index 0000000..dc18373
--- /dev/null
+++ b/regress/unittests/sshkey/testdata/rsa_n_pw
@@ -0,0 +1,17 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jYmMAAAAGYmNyeXB0AAAAGAAAABAFw/Wg/V
+I5SAXWj/HJr9qeAAAAEAAAAAEAAACXAAAAB3NzaC1yc2EAAAADAQABAAAAgQDLV5lUTt7F
+rADseB/CGhEZzpoojjEW5y8+ePvLppmK3MmMI18ud6vxzpK3bwZLYkVSyfJYI0HmIuGhdu
+7yMrW6wb84gbq8C31Xoe9EORcIUuGSvDKdNSM1SjlhDquRblDFB8kToqXyx1lqrXecXylx
+IUOL0jE+u0rU1967pDJx+wAAAgD1iSGiMlMJt2VH4kx5yr0wCJS+4UOmX0bxKO7UH5Jcul
+K5eaSe5ZoKE7hTYBaz0K5dRF/0fqLsvVZlE4quDjFLN6Hyavgn2W/QM7SUqBHgRMal9pgH
+LnxX6mFNWJ+4yb7f3bcbVIdgmMm3sT9Xjwaf5xgzNlR2mkUWtFwjyQh6FxUo5apNzqNBwO
+l2Q4xfmyZTp1s++pStQ/su6obXpxnE2Nx5G/D84ZL5iWl+njUy/MvJTazHRbiTSyihU+UA
+mUr5ZNuP3WUYY+h3KVlHpYHJYB7l3AMTKuPMFLhY9V7BJ+DuKPaqBgX4hvRzY0eVQiFr61
+ovjWjvfu1ulx550JqdYCgH2PpP0E89OQne35Cxs9QPThfe8DKojC9YquYh9zmVTvr7kNiE
+Soluk/7oKpQIDaC+/SRk7AJ2e3Cbt1lXyGNn37PuqaaC/apaF/DOD6Yig9aClS7jOUrT96
+56trFAYfHEIKbRCUSMCiM1+x6HOLYf5ROrGE9KxT3kUD9XMsMpTva+cPpHUpbGpXcYE10N
+MyYDz+V5M2/ZoIdEhscJNQ3UnhaZpeEaqcOyNyo90n3Dnaw/WpMDD/kNMGfm8daTaYInnQ
+QnwA2gwlYfpTAqxE71oXgOuGmtA0yqJB4778Xq26Pb+B7/mZZZe6n0FVmiNC+ZG37ZGOw/
+iGL9e2Sxzw==
+-----END OPENSSH PRIVATE KEY-----
diff --git a/regress/unittests/sshkey/tests.c b/regress/unittests/sshkey/tests.c
new file mode 100644
index 0000000..13f265c
--- /dev/null
+++ b/regress/unittests/sshkey/tests.c
@@ -0,0 +1,27 @@
+/* $OpenBSD: tests.c,v 1.1 2014/06/24 01:14:18 djm Exp $ */
+/*
+ * Regress test for sshbuf.h buffer API
+ *
+ * Placed in the public domain
+ */
+
+#include "includes.h"
+
+#include <openssl/evp.h>
+
+#include "../test_helper/test_helper.h"
+
+void sshkey_tests(void);
+void sshkey_file_tests(void);
+void sshkey_fuzz_tests(void);
+
+void
+tests(void)
+{
+ OpenSSL_add_all_algorithms();
+ ERR_load_CRYPTO_strings();
+
+ sshkey_tests();
+ sshkey_file_tests();
+ sshkey_fuzz_tests();
+}
diff --git a/regress/unittests/test_helper/Makefile b/regress/unittests/test_helper/Makefile
new file mode 100644
index 0000000..78026e6
--- /dev/null
+++ b/regress/unittests/test_helper/Makefile
@@ -0,0 +1,15 @@
+# $OpenBSD: Makefile,v 1.3 2016/07/04 18:01:44 guenther Exp $
+
+LIB= test_helper
+SRCS= test_helper.c fuzz.c
+
+NOPROFILE= yes
+NOPIC= yes
+
+# Hack to allow building with SUBDIR in ../../Makefile
+regress: all
+
+install:
+ @echo -n
+
+.include <bsd.lib.mk>
diff --git a/regress/unittests/test_helper/fuzz.c b/regress/unittests/test_helper/fuzz.c
new file mode 100644
index 0000000..78b3665
--- /dev/null
+++ b/regress/unittests/test_helper/fuzz.c
@@ -0,0 +1,438 @@
+/* $OpenBSD: fuzz.c,v 1.8 2015/03/03 20:42:49 djm Exp $ */
+/*
+ * Copyright (c) 2011 Damien Miller <djm@mindrot.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Utility functions/framework for fuzz tests */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdio.h>
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include "test_helper.h"
+#include "atomicio.h"
+
+/* #define FUZZ_DEBUG */
+
+#ifdef FUZZ_DEBUG
+# define FUZZ_DBG(x) do { \
+ printf("%s:%d %s: ", __FILE__, __LINE__, __func__); \
+ printf x; \
+ printf("\n"); \
+ fflush(stdout); \
+ } while (0)
+#else
+# define FUZZ_DBG(x)
+#endif
+
+/* For brevity later */
+typedef unsigned long long fuzz_ullong;
+
+/* For base-64 fuzzing */
+static const char fuzz_b64chars[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+struct fuzz {
+ /* Fuzz method currently in use */
+ int strategy;
+
+ /* Fuzz methods remaining */
+ int strategies;
+
+ /* Original seed data blob */
+ void *seed;
+ size_t slen;
+
+ /* Current working copy of seed with fuzz mutations applied */
+ u_char *fuzzed;
+
+ /* Used by fuzz methods */
+ size_t o1, o2;
+};
+
+static const char *
+fuzz_ntop(u_int n)
+{
+ switch (n) {
+ case 0:
+ return "NONE";
+ case FUZZ_1_BIT_FLIP:
+ return "FUZZ_1_BIT_FLIP";
+ case FUZZ_2_BIT_FLIP:
+ return "FUZZ_2_BIT_FLIP";
+ case FUZZ_1_BYTE_FLIP:
+ return "FUZZ_1_BYTE_FLIP";
+ case FUZZ_2_BYTE_FLIP:
+ return "FUZZ_2_BYTE_FLIP";
+ case FUZZ_TRUNCATE_START:
+ return "FUZZ_TRUNCATE_START";
+ case FUZZ_TRUNCATE_END:
+ return "FUZZ_TRUNCATE_END";
+ case FUZZ_BASE64:
+ return "FUZZ_BASE64";
+ default:
+ abort();
+ }
+}
+
+static int
+fuzz_fmt(struct fuzz *fuzz, char *s, size_t n)
+{
+ if (fuzz == NULL)
+ return -1;
+
+ switch (fuzz->strategy) {
+ case FUZZ_1_BIT_FLIP:
+ snprintf(s, n, "%s case %zu of %zu (bit: %zu)\n",
+ fuzz_ntop(fuzz->strategy),
+ fuzz->o1, fuzz->slen * 8, fuzz->o1);
+ return 0;
+ case FUZZ_2_BIT_FLIP:
+ snprintf(s, n, "%s case %llu of %llu (bits: %zu, %zu)\n",
+ fuzz_ntop(fuzz->strategy),
+ (((fuzz_ullong)fuzz->o2) * fuzz->slen * 8) + fuzz->o1,
+ ((fuzz_ullong)fuzz->slen * 8) * fuzz->slen * 8,
+ fuzz->o1, fuzz->o2);
+ return 0;
+ case FUZZ_1_BYTE_FLIP:
+ snprintf(s, n, "%s case %zu of %zu (byte: %zu)\n",
+ fuzz_ntop(fuzz->strategy),
+ fuzz->o1, fuzz->slen, fuzz->o1);
+ return 0;
+ case FUZZ_2_BYTE_FLIP:
+ snprintf(s, n, "%s case %llu of %llu (bytes: %zu, %zu)\n",
+ fuzz_ntop(fuzz->strategy),
+ (((fuzz_ullong)fuzz->o2) * fuzz->slen) + fuzz->o1,
+ ((fuzz_ullong)fuzz->slen) * fuzz->slen,
+ fuzz->o1, fuzz->o2);
+ return 0;
+ case FUZZ_TRUNCATE_START:
+ snprintf(s, n, "%s case %zu of %zu (offset: %zu)\n",
+ fuzz_ntop(fuzz->strategy),
+ fuzz->o1, fuzz->slen, fuzz->o1);
+ return 0;
+ case FUZZ_TRUNCATE_END:
+ snprintf(s, n, "%s case %zu of %zu (offset: %zu)\n",
+ fuzz_ntop(fuzz->strategy),
+ fuzz->o1, fuzz->slen, fuzz->o1);
+ return 0;
+ case FUZZ_BASE64:
+ assert(fuzz->o2 < sizeof(fuzz_b64chars) - 1);
+ snprintf(s, n, "%s case %llu of %llu (offset: %zu char: %c)\n",
+ fuzz_ntop(fuzz->strategy),
+ (fuzz->o1 * (fuzz_ullong)64) + fuzz->o2,
+ fuzz->slen * (fuzz_ullong)64, fuzz->o1,
+ fuzz_b64chars[fuzz->o2]);
+ return 0;
+ default:
+ return -1;
+ abort();
+ }
+}
+
+static void
+dump(u_char *p, size_t len)
+{
+ size_t i, j;
+
+ for (i = 0; i < len; i += 16) {
+ fprintf(stderr, "%.4zd: ", i);
+ for (j = i; j < i + 16; j++) {
+ if (j < len)
+ fprintf(stderr, "%02x ", p[j]);
+ else
+ fprintf(stderr, " ");
+ }
+ fprintf(stderr, " ");
+ for (j = i; j < i + 16; j++) {
+ if (j < len) {
+ if (isascii(p[j]) && isprint(p[j]))
+ fprintf(stderr, "%c", p[j]);
+ else
+ fprintf(stderr, ".");
+ }
+ }
+ fprintf(stderr, "\n");
+ }
+}
+
+void
+fuzz_dump(struct fuzz *fuzz)
+{
+ char buf[256];
+
+ if (fuzz_fmt(fuzz, buf, sizeof(buf)) != 0) {
+ fprintf(stderr, "%s: fuzz invalid\n", __func__);
+ abort();
+ }
+ fputs(buf, stderr);
+ fprintf(stderr, "fuzz original %p len = %zu\n", fuzz->seed, fuzz->slen);
+ dump(fuzz->seed, fuzz->slen);
+ fprintf(stderr, "fuzz context %p len = %zu\n", fuzz, fuzz_len(fuzz));
+ dump(fuzz_ptr(fuzz), fuzz_len(fuzz));
+}
+
+static struct fuzz *last_fuzz;
+
+static void
+siginfo(int unused __attribute__((__unused__)))
+{
+ char buf[256];
+
+ test_info(buf, sizeof(buf));
+ atomicio(vwrite, STDERR_FILENO, buf, strlen(buf));
+ if (last_fuzz != NULL) {
+ fuzz_fmt(last_fuzz, buf, sizeof(buf));
+ atomicio(vwrite, STDERR_FILENO, buf, strlen(buf));
+ }
+}
+
+struct fuzz *
+fuzz_begin(u_int strategies, const void *p, size_t l)
+{
+ struct fuzz *ret = calloc(sizeof(*ret), 1);
+
+ assert(p != NULL);
+ assert(ret != NULL);
+ ret->seed = malloc(l);
+ assert(ret->seed != NULL);
+ memcpy(ret->seed, p, l);
+ ret->slen = l;
+ ret->strategies = strategies;
+
+ assert(ret->slen < SIZE_MAX / 8);
+ assert(ret->strategies <= (FUZZ_MAX|(FUZZ_MAX-1)));
+
+ FUZZ_DBG(("begin, ret = %p", ret));
+
+ fuzz_next(ret);
+
+ last_fuzz = ret;
+#ifdef SIGINFO
+ signal(SIGINFO, siginfo);
+#endif
+ signal(SIGUSR1, siginfo);
+
+ return ret;
+}
+
+void
+fuzz_cleanup(struct fuzz *fuzz)
+{
+ FUZZ_DBG(("cleanup, fuzz = %p", fuzz));
+ last_fuzz = NULL;
+#ifdef SIGINFO
+ signal(SIGINFO, SIG_DFL);
+#endif
+ signal(SIGUSR1, SIG_DFL);
+ assert(fuzz != NULL);
+ assert(fuzz->seed != NULL);
+ assert(fuzz->fuzzed != NULL);
+ free(fuzz->seed);
+ free(fuzz->fuzzed);
+ free(fuzz);
+}
+
+static int
+fuzz_strategy_done(struct fuzz *fuzz)
+{
+ FUZZ_DBG(("fuzz = %p, strategy = %s, o1 = %zu, o2 = %zu, slen = %zu",
+ fuzz, fuzz_ntop(fuzz->strategy), fuzz->o1, fuzz->o2, fuzz->slen));
+
+ switch (fuzz->strategy) {
+ case FUZZ_1_BIT_FLIP:
+ return fuzz->o1 >= fuzz->slen * 8;
+ case FUZZ_2_BIT_FLIP:
+ return fuzz->o2 >= fuzz->slen * 8;
+ case FUZZ_2_BYTE_FLIP:
+ return fuzz->o2 >= fuzz->slen;
+ case FUZZ_1_BYTE_FLIP:
+ case FUZZ_TRUNCATE_START:
+ case FUZZ_TRUNCATE_END:
+ case FUZZ_BASE64:
+ return fuzz->o1 >= fuzz->slen;
+ default:
+ abort();
+ }
+}
+
+void
+fuzz_next(struct fuzz *fuzz)
+{
+ u_int i;
+
+ FUZZ_DBG(("start, fuzz = %p, strategy = %s, strategies = 0x%lx, "
+ "o1 = %zu, o2 = %zu, slen = %zu", fuzz, fuzz_ntop(fuzz->strategy),
+ (u_long)fuzz->strategies, fuzz->o1, fuzz->o2, fuzz->slen));
+
+ if (fuzz->strategy == 0 || fuzz_strategy_done(fuzz)) {
+ /* If we are just starting out, we need to allocate too */
+ if (fuzz->fuzzed == NULL) {
+ FUZZ_DBG(("alloc"));
+ fuzz->fuzzed = calloc(fuzz->slen, 1);
+ }
+ /* Pick next strategy */
+ FUZZ_DBG(("advance"));
+ for (i = 1; i <= FUZZ_MAX; i <<= 1) {
+ if ((fuzz->strategies & i) != 0) {
+ fuzz->strategy = i;
+ break;
+ }
+ }
+ FUZZ_DBG(("selected = %u", fuzz->strategy));
+ if (fuzz->strategy == 0) {
+ FUZZ_DBG(("done, no more strategies"));
+ return;
+ }
+ fuzz->strategies &= ~(fuzz->strategy);
+ fuzz->o1 = fuzz->o2 = 0;
+ }
+
+ assert(fuzz->fuzzed != NULL);
+
+ switch (fuzz->strategy) {
+ case FUZZ_1_BIT_FLIP:
+ assert(fuzz->o1 / 8 < fuzz->slen);
+ memcpy(fuzz->fuzzed, fuzz->seed, fuzz->slen);
+ fuzz->fuzzed[fuzz->o1 / 8] ^= 1 << (fuzz->o1 % 8);
+ fuzz->o1++;
+ break;
+ case FUZZ_2_BIT_FLIP:
+ assert(fuzz->o1 / 8 < fuzz->slen);
+ assert(fuzz->o2 / 8 < fuzz->slen);
+ memcpy(fuzz->fuzzed, fuzz->seed, fuzz->slen);
+ fuzz->fuzzed[fuzz->o1 / 8] ^= 1 << (fuzz->o1 % 8);
+ fuzz->fuzzed[fuzz->o2 / 8] ^= 1 << (fuzz->o2 % 8);
+ fuzz->o1++;
+ if (fuzz->o1 >= fuzz->slen * 8) {
+ fuzz->o1 = 0;
+ fuzz->o2++;
+ }
+ break;
+ case FUZZ_1_BYTE_FLIP:
+ assert(fuzz->o1 < fuzz->slen);
+ memcpy(fuzz->fuzzed, fuzz->seed, fuzz->slen);
+ fuzz->fuzzed[fuzz->o1] ^= 0xff;
+ fuzz->o1++;
+ break;
+ case FUZZ_2_BYTE_FLIP:
+ assert(fuzz->o1 < fuzz->slen);
+ assert(fuzz->o2 < fuzz->slen);
+ memcpy(fuzz->fuzzed, fuzz->seed, fuzz->slen);
+ fuzz->fuzzed[fuzz->o1] ^= 0xff;
+ fuzz->fuzzed[fuzz->o2] ^= 0xff;
+ fuzz->o1++;
+ if (fuzz->o1 >= fuzz->slen) {
+ fuzz->o1 = 0;
+ fuzz->o2++;
+ }
+ break;
+ case FUZZ_TRUNCATE_START:
+ case FUZZ_TRUNCATE_END:
+ assert(fuzz->o1 < fuzz->slen);
+ memcpy(fuzz->fuzzed, fuzz->seed, fuzz->slen);
+ fuzz->o1++;
+ break;
+ case FUZZ_BASE64:
+ assert(fuzz->o1 < fuzz->slen);
+ assert(fuzz->o2 < sizeof(fuzz_b64chars) - 1);
+ memcpy(fuzz->fuzzed, fuzz->seed, fuzz->slen);
+ fuzz->fuzzed[fuzz->o1] = fuzz_b64chars[fuzz->o2];
+ fuzz->o2++;
+ if (fuzz->o2 >= sizeof(fuzz_b64chars) - 1) {
+ fuzz->o2 = 0;
+ fuzz->o1++;
+ }
+ break;
+ default:
+ abort();
+ }
+
+ FUZZ_DBG(("done, fuzz = %p, strategy = %s, strategies = 0x%lx, "
+ "o1 = %zu, o2 = %zu, slen = %zu", fuzz, fuzz_ntop(fuzz->strategy),
+ (u_long)fuzz->strategies, fuzz->o1, fuzz->o2, fuzz->slen));
+}
+
+int
+fuzz_matches_original(struct fuzz *fuzz)
+{
+ if (fuzz_len(fuzz) != fuzz->slen)
+ return 0;
+ return memcmp(fuzz_ptr(fuzz), fuzz->seed, fuzz->slen) == 0;
+}
+
+int
+fuzz_done(struct fuzz *fuzz)
+{
+ FUZZ_DBG(("fuzz = %p, strategies = 0x%lx", fuzz,
+ (u_long)fuzz->strategies));
+
+ return fuzz_strategy_done(fuzz) && fuzz->strategies == 0;
+}
+
+size_t
+fuzz_len(struct fuzz *fuzz)
+{
+ assert(fuzz->fuzzed != NULL);
+ switch (fuzz->strategy) {
+ case FUZZ_1_BIT_FLIP:
+ case FUZZ_2_BIT_FLIP:
+ case FUZZ_1_BYTE_FLIP:
+ case FUZZ_2_BYTE_FLIP:
+ case FUZZ_BASE64:
+ return fuzz->slen;
+ case FUZZ_TRUNCATE_START:
+ case FUZZ_TRUNCATE_END:
+ assert(fuzz->o1 <= fuzz->slen);
+ return fuzz->slen - fuzz->o1;
+ default:
+ abort();
+ }
+}
+
+u_char *
+fuzz_ptr(struct fuzz *fuzz)
+{
+ assert(fuzz->fuzzed != NULL);
+ switch (fuzz->strategy) {
+ case FUZZ_1_BIT_FLIP:
+ case FUZZ_2_BIT_FLIP:
+ case FUZZ_1_BYTE_FLIP:
+ case FUZZ_2_BYTE_FLIP:
+ case FUZZ_BASE64:
+ return fuzz->fuzzed;
+ case FUZZ_TRUNCATE_START:
+ assert(fuzz->o1 <= fuzz->slen);
+ return fuzz->fuzzed + fuzz->o1;
+ case FUZZ_TRUNCATE_END:
+ assert(fuzz->o1 <= fuzz->slen);
+ return fuzz->fuzzed;
+ default:
+ abort();
+ }
+}
+
diff --git a/regress/unittests/test_helper/test_helper.c b/regress/unittests/test_helper/test_helper.c
new file mode 100644
index 0000000..4cc7085
--- /dev/null
+++ b/regress/unittests/test_helper/test_helper.c
@@ -0,0 +1,548 @@
+/* $OpenBSD: test_helper.c,v 1.8 2018/02/08 08:46:20 djm Exp $ */
+/*
+ * Copyright (c) 2011 Damien Miller <djm@mindrot.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Utility functions/framework for regress tests */
+
+#include "includes.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/uio.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <openssl/bn.h>
+
+#if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS)
+# include <vis.h>
+#endif
+
+#include "test_helper.h"
+#include "atomicio.h"
+
+#define TEST_CHECK_INT(r, pred) do { \
+ switch (pred) { \
+ case TEST_EQ: \
+ if (r == 0) \
+ return; \
+ break; \
+ case TEST_NE: \
+ if (r != 0) \
+ return; \
+ break; \
+ case TEST_LT: \
+ if (r < 0) \
+ return; \
+ break; \
+ case TEST_LE: \
+ if (r <= 0) \
+ return; \
+ break; \
+ case TEST_GT: \
+ if (r > 0) \
+ return; \
+ break; \
+ case TEST_GE: \
+ if (r >= 0) \
+ return; \
+ break; \
+ default: \
+ abort(); \
+ } \
+ } while (0)
+
+#define TEST_CHECK(x1, x2, pred) do { \
+ switch (pred) { \
+ case TEST_EQ: \
+ if (x1 == x2) \
+ return; \
+ break; \
+ case TEST_NE: \
+ if (x1 != x2) \
+ return; \
+ break; \
+ case TEST_LT: \
+ if (x1 < x2) \
+ return; \
+ break; \
+ case TEST_LE: \
+ if (x1 <= x2) \
+ return; \
+ break; \
+ case TEST_GT: \
+ if (x1 > x2) \
+ return; \
+ break; \
+ case TEST_GE: \
+ if (x1 >= x2) \
+ return; \
+ break; \
+ default: \
+ abort(); \
+ } \
+ } while (0)
+
+extern char *__progname;
+
+static int verbose_mode = 0;
+static int quiet_mode = 0;
+static char *active_test_name = NULL;
+static u_int test_number = 0;
+static test_onerror_func_t *test_onerror = NULL;
+static void *onerror_ctx = NULL;
+static const char *data_dir = NULL;
+static char subtest_info[512];
+
+int
+main(int argc, char **argv)
+{
+ int ch;
+
+ /* Handle systems without __progname */
+ if (__progname == NULL) {
+ __progname = strrchr(argv[0], '/');
+ if (__progname == NULL || __progname[1] == '\0')
+ __progname = argv[0];
+ else
+ __progname++;
+ if ((__progname = strdup(__progname)) == NULL) {
+ fprintf(stderr, "strdup failed\n");
+ exit(1);
+ }
+ }
+
+ while ((ch = getopt(argc, argv, "vqd:")) != -1) {
+ switch (ch) {
+ case 'd':
+ data_dir = optarg;
+ break;
+ case 'q':
+ verbose_mode = 0;
+ quiet_mode = 1;
+ break;
+ case 'v':
+ verbose_mode = 1;
+ quiet_mode = 0;
+ break;
+ default:
+ fprintf(stderr, "Unrecognised command line option\n");
+ fprintf(stderr, "Usage: %s [-v]\n", __progname);
+ exit(1);
+ }
+ }
+ setvbuf(stdout, NULL, _IONBF, 0);
+ if (!quiet_mode)
+ printf("%s: ", __progname);
+ if (verbose_mode)
+ printf("\n");
+
+ tests();
+
+ if (!quiet_mode)
+ printf(" %u tests ok\n", test_number);
+ return 0;
+}
+
+int
+test_is_verbose()
+{
+ return verbose_mode;
+}
+
+int
+test_is_quiet()
+{
+ return quiet_mode;
+}
+
+const char *
+test_data_file(const char *name)
+{
+ static char ret[PATH_MAX];
+
+ if (data_dir != NULL)
+ snprintf(ret, sizeof(ret), "%s/%s", data_dir, name);
+ else
+ strlcpy(ret, name, sizeof(ret));
+ if (access(ret, F_OK) != 0) {
+ fprintf(stderr, "Cannot access data file %s: %s\n",
+ ret, strerror(errno));
+ exit(1);
+ }
+ return ret;
+}
+
+void
+test_info(char *s, size_t len)
+{
+ snprintf(s, len, "In test %u: \"%s\"%s%s\n", test_number,
+ active_test_name == NULL ? "<none>" : active_test_name,
+ *subtest_info != '\0' ? " - " : "", subtest_info);
+}
+
+static void
+siginfo(int unused __attribute__((__unused__)))
+{
+ char buf[256];
+
+ test_info(buf, sizeof(buf));
+ atomicio(vwrite, STDERR_FILENO, buf, strlen(buf));
+}
+
+void
+test_start(const char *n)
+{
+ assert(active_test_name == NULL);
+ assert((active_test_name = strdup(n)) != NULL);
+ *subtest_info = '\0';
+ if (verbose_mode)
+ printf("test %u - \"%s\": ", test_number, active_test_name);
+ test_number++;
+#ifdef SIGINFO
+ signal(SIGINFO, siginfo);
+#endif
+ signal(SIGUSR1, siginfo);
+}
+
+void
+set_onerror_func(test_onerror_func_t *f, void *ctx)
+{
+ test_onerror = f;
+ onerror_ctx = ctx;
+}
+
+void
+test_done(void)
+{
+ *subtest_info = '\0';
+ assert(active_test_name != NULL);
+ free(active_test_name);
+ active_test_name = NULL;
+ if (verbose_mode)
+ printf("OK\n");
+ else if (!quiet_mode) {
+ printf(".");
+ fflush(stdout);
+ }
+}
+
+void
+test_subtest_info(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(subtest_info, sizeof(subtest_info), fmt, ap);
+ va_end(ap);
+}
+
+void
+ssl_err_check(const char *file, int line)
+{
+ long openssl_error = ERR_get_error();
+
+ if (openssl_error == 0)
+ return;
+
+ fprintf(stderr, "\n%s:%d: uncaught OpenSSL error: %s",
+ file, line, ERR_error_string(openssl_error, NULL));
+ abort();
+}
+
+static const char *
+pred_name(enum test_predicate p)
+{
+ switch (p) {
+ case TEST_EQ:
+ return "EQ";
+ case TEST_NE:
+ return "NE";
+ case TEST_LT:
+ return "LT";
+ case TEST_LE:
+ return "LE";
+ case TEST_GT:
+ return "GT";
+ case TEST_GE:
+ return "GE";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static void
+test_die(void)
+{
+ if (test_onerror != NULL)
+ test_onerror(onerror_ctx);
+ abort();
+}
+
+static void
+test_header(const char *file, int line, const char *a1, const char *a2,
+ const char *name, enum test_predicate pred)
+{
+ fprintf(stderr, "\n%s:%d test #%u \"%s\"%s%s\n",
+ file, line, test_number, active_test_name,
+ *subtest_info != '\0' ? " - " : "", subtest_info);
+ fprintf(stderr, "ASSERT_%s_%s(%s%s%s) failed:\n",
+ name, pred_name(pred), a1,
+ a2 != NULL ? ", " : "", a2 != NULL ? a2 : "");
+}
+
+void
+assert_bignum(const char *file, int line, const char *a1, const char *a2,
+ const BIGNUM *aa1, const BIGNUM *aa2, enum test_predicate pred)
+{
+ int r = BN_cmp(aa1, aa2);
+
+ TEST_CHECK_INT(r, pred);
+ test_header(file, line, a1, a2, "BIGNUM", pred);
+ fprintf(stderr, "%12s = 0x%s\n", a1, BN_bn2hex(aa1));
+ fprintf(stderr, "%12s = 0x%s\n", a2, BN_bn2hex(aa2));
+ test_die();
+}
+
+void
+assert_string(const char *file, int line, const char *a1, const char *a2,
+ const char *aa1, const char *aa2, enum test_predicate pred)
+{
+ int r;
+
+ /* Verify pointers are not NULL */
+ assert_ptr(file, line, a1, "NULL", aa1, NULL, TEST_NE);
+ assert_ptr(file, line, a2, "NULL", aa2, NULL, TEST_NE);
+
+ r = strcmp(aa1, aa2);
+ TEST_CHECK_INT(r, pred);
+ test_header(file, line, a1, a2, "STRING", pred);
+ fprintf(stderr, "%12s = %s (len %zu)\n", a1, aa1, strlen(aa1));
+ fprintf(stderr, "%12s = %s (len %zu)\n", a2, aa2, strlen(aa2));
+ test_die();
+}
+
+static char *
+tohex(const void *_s, size_t l)
+{
+ u_int8_t *s = (u_int8_t *)_s;
+ size_t i, j;
+ const char *hex = "0123456789abcdef";
+ char *r = malloc((l * 2) + 1);
+
+ assert(r != NULL);
+ for (i = j = 0; i < l; i++) {
+ r[j++] = hex[(s[i] >> 4) & 0xf];
+ r[j++] = hex[s[i] & 0xf];
+ }
+ r[j] = '\0';
+ return r;
+}
+
+void
+assert_mem(const char *file, int line, const char *a1, const char *a2,
+ const void *aa1, const void *aa2, size_t l, enum test_predicate pred)
+{
+ int r;
+
+ if (l == 0)
+ return;
+ /* If length is >0, then verify pointers are not NULL */
+ assert_ptr(file, line, a1, "NULL", aa1, NULL, TEST_NE);
+ assert_ptr(file, line, a2, "NULL", aa2, NULL, TEST_NE);
+
+ r = memcmp(aa1, aa2, l);
+ TEST_CHECK_INT(r, pred);
+ test_header(file, line, a1, a2, "STRING", pred);
+ fprintf(stderr, "%12s = %s (len %zu)\n", a1, tohex(aa1, MIN(l, 256)), l);
+ fprintf(stderr, "%12s = %s (len %zu)\n", a2, tohex(aa2, MIN(l, 256)), l);
+ test_die();
+}
+
+static int
+memvalcmp(const u_int8_t *s, u_char v, size_t l, size_t *where)
+{
+ size_t i;
+
+ for (i = 0; i < l; i++) {
+ if (s[i] != v) {
+ *where = i;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void
+assert_mem_filled(const char *file, int line, const char *a1,
+ const void *aa1, u_char v, size_t l, enum test_predicate pred)
+{
+ size_t where = -1;
+ int r;
+ char tmp[64];
+
+ if (l == 0)
+ return;
+ /* If length is >0, then verify the pointer is not NULL */
+ assert_ptr(file, line, a1, "NULL", aa1, NULL, TEST_NE);
+
+ r = memvalcmp(aa1, v, l, &where);
+ TEST_CHECK_INT(r, pred);
+ test_header(file, line, a1, NULL, "MEM_ZERO", pred);
+ fprintf(stderr, "%20s = %s%s (len %zu)\n", a1,
+ tohex(aa1, MIN(l, 20)), l > 20 ? "..." : "", l);
+ snprintf(tmp, sizeof(tmp), "(%s)[%zu]", a1, where);
+ fprintf(stderr, "%20s = 0x%02x (expected 0x%02x)\n", tmp,
+ ((u_char *)aa1)[where], v);
+ test_die();
+}
+
+void
+assert_int(const char *file, int line, const char *a1, const char *a2,
+ int aa1, int aa2, enum test_predicate pred)
+{
+ TEST_CHECK(aa1, aa2, pred);
+ test_header(file, line, a1, a2, "INT", pred);
+ fprintf(stderr, "%12s = %d\n", a1, aa1);
+ fprintf(stderr, "%12s = %d\n", a2, aa2);
+ test_die();
+}
+
+void
+assert_size_t(const char *file, int line, const char *a1, const char *a2,
+ size_t aa1, size_t aa2, enum test_predicate pred)
+{
+ TEST_CHECK(aa1, aa2, pred);
+ test_header(file, line, a1, a2, "SIZE_T", pred);
+ fprintf(stderr, "%12s = %zu\n", a1, aa1);
+ fprintf(stderr, "%12s = %zu\n", a2, aa2);
+ test_die();
+}
+
+void
+assert_u_int(const char *file, int line, const char *a1, const char *a2,
+ u_int aa1, u_int aa2, enum test_predicate pred)
+{
+ TEST_CHECK(aa1, aa2, pred);
+ test_header(file, line, a1, a2, "U_INT", pred);
+ fprintf(stderr, "%12s = %u / 0x%x\n", a1, aa1, aa1);
+ fprintf(stderr, "%12s = %u / 0x%x\n", a2, aa2, aa2);
+ test_die();
+}
+
+void
+assert_long(const char *file, int line, const char *a1, const char *a2,
+ long aa1, long aa2, enum test_predicate pred)
+{
+ TEST_CHECK(aa1, aa2, pred);
+ test_header(file, line, a1, a2, "LONG", pred);
+ fprintf(stderr, "%12s = %ld / 0x%lx\n", a1, aa1, aa1);
+ fprintf(stderr, "%12s = %ld / 0x%lx\n", a2, aa2, aa2);
+ test_die();
+}
+
+void
+assert_long_long(const char *file, int line, const char *a1, const char *a2,
+ long long aa1, long long aa2, enum test_predicate pred)
+{
+ TEST_CHECK(aa1, aa2, pred);
+ test_header(file, line, a1, a2, "LONG LONG", pred);
+ fprintf(stderr, "%12s = %lld / 0x%llx\n", a1, aa1, aa1);
+ fprintf(stderr, "%12s = %lld / 0x%llx\n", a2, aa2, aa2);
+ test_die();
+}
+
+void
+assert_char(const char *file, int line, const char *a1, const char *a2,
+ char aa1, char aa2, enum test_predicate pred)
+{
+ char buf[8];
+
+ TEST_CHECK(aa1, aa2, pred);
+ test_header(file, line, a1, a2, "CHAR", pred);
+ fprintf(stderr, "%12s = '%s' / 0x02%x\n", a1,
+ vis(buf, aa1, VIS_SAFE|VIS_NL|VIS_TAB|VIS_OCTAL, 0), aa1);
+ fprintf(stderr, "%12s = '%s' / 0x02%x\n", a1,
+ vis(buf, aa2, VIS_SAFE|VIS_NL|VIS_TAB|VIS_OCTAL, 0), aa2);
+ test_die();
+}
+
+void
+assert_u8(const char *file, int line, const char *a1, const char *a2,
+ u_int8_t aa1, u_int8_t aa2, enum test_predicate pred)
+{
+ TEST_CHECK(aa1, aa2, pred);
+ test_header(file, line, a1, a2, "U8", pred);
+ fprintf(stderr, "%12s = 0x%02x %u\n", a1, aa1, aa1);
+ fprintf(stderr, "%12s = 0x%02x %u\n", a2, aa2, aa2);
+ test_die();
+}
+
+void
+assert_u16(const char *file, int line, const char *a1, const char *a2,
+ u_int16_t aa1, u_int16_t aa2, enum test_predicate pred)
+{
+ TEST_CHECK(aa1, aa2, pred);
+ test_header(file, line, a1, a2, "U16", pred);
+ fprintf(stderr, "%12s = 0x%04x %u\n", a1, aa1, aa1);
+ fprintf(stderr, "%12s = 0x%04x %u\n", a2, aa2, aa2);
+ test_die();
+}
+
+void
+assert_u32(const char *file, int line, const char *a1, const char *a2,
+ u_int32_t aa1, u_int32_t aa2, enum test_predicate pred)
+{
+ TEST_CHECK(aa1, aa2, pred);
+ test_header(file, line, a1, a2, "U32", pred);
+ fprintf(stderr, "%12s = 0x%08x %u\n", a1, aa1, aa1);
+ fprintf(stderr, "%12s = 0x%08x %u\n", a2, aa2, aa2);
+ test_die();
+}
+
+void
+assert_u64(const char *file, int line, const char *a1, const char *a2,
+ u_int64_t aa1, u_int64_t aa2, enum test_predicate pred)
+{
+ TEST_CHECK(aa1, aa2, pred);
+ test_header(file, line, a1, a2, "U64", pred);
+ fprintf(stderr, "%12s = 0x%016llx %llu\n", a1,
+ (unsigned long long)aa1, (unsigned long long)aa1);
+ fprintf(stderr, "%12s = 0x%016llx %llu\n", a2,
+ (unsigned long long)aa2, (unsigned long long)aa2);
+ test_die();
+}
+
+void
+assert_ptr(const char *file, int line, const char *a1, const char *a2,
+ const void *aa1, const void *aa2, enum test_predicate pred)
+{
+ TEST_CHECK(aa1, aa2, pred);
+ test_header(file, line, a1, a2, "PTR", pred);
+ fprintf(stderr, "%12s = %p\n", a1, aa1);
+ fprintf(stderr, "%12s = %p\n", a2, aa2);
+ test_die();
+}
+
diff --git a/regress/unittests/test_helper/test_helper.h b/regress/unittests/test_helper/test_helper.h
new file mode 100644
index 0000000..6da0066
--- /dev/null
+++ b/regress/unittests/test_helper/test_helper.h
@@ -0,0 +1,320 @@
+/* $OpenBSD: test_helper.h,v 1.8 2018/02/08 08:46:20 djm Exp $ */
+/*
+ * Copyright (c) 2011 Damien Miller <djm@mindrot.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Utility functions/framework for regress tests */
+
+#ifndef _TEST_HELPER_H
+#define _TEST_HELPER_H
+
+#include "includes.h"
+
+#include <sys/types.h>
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+
+#include <openssl/bn.h>
+#include <openssl/err.h>
+
+enum test_predicate {
+ TEST_EQ, TEST_NE, TEST_LT, TEST_LE, TEST_GT, TEST_GE
+};
+typedef void (test_onerror_func_t)(void *);
+
+/* Supplied by test suite */
+void tests(void);
+
+const char *test_data_file(const char *name);
+void test_start(const char *n);
+void test_info(char *s, size_t len);
+void set_onerror_func(test_onerror_func_t *f, void *ctx);
+void test_done(void);
+int test_is_verbose(void);
+int test_is_quiet(void);
+void test_subtest_info(const char *fmt, ...)
+ __attribute__((format(printf, 1, 2)));
+void ssl_err_check(const char *file, int line);
+void assert_bignum(const char *file, int line,
+ const char *a1, const char *a2,
+ const BIGNUM *aa1, const BIGNUM *aa2, enum test_predicate pred);
+void assert_string(const char *file, int line,
+ const char *a1, const char *a2,
+ const char *aa1, const char *aa2, enum test_predicate pred);
+void assert_mem(const char *file, int line,
+ const char *a1, const char *a2,
+ const void *aa1, const void *aa2, size_t l, enum test_predicate pred);
+void assert_mem_filled(const char *file, int line,
+ const char *a1,
+ const void *aa1, u_char v, size_t l, enum test_predicate pred);
+void assert_int(const char *file, int line,
+ const char *a1, const char *a2,
+ int aa1, int aa2, enum test_predicate pred);
+void assert_size_t(const char *file, int line,
+ const char *a1, const char *a2,
+ size_t aa1, size_t aa2, enum test_predicate pred);
+void assert_u_int(const char *file, int line,
+ const char *a1, const char *a2,
+ u_int aa1, u_int aa2, enum test_predicate pred);
+void assert_long(const char *file, int line,
+ const char *a1, const char *a2,
+ long aa1, long aa2, enum test_predicate pred);
+void assert_long_long(const char *file, int line,
+ const char *a1, const char *a2,
+ long long aa1, long long aa2, enum test_predicate pred);
+void assert_char(const char *file, int line,
+ const char *a1, const char *a2,
+ char aa1, char aa2, enum test_predicate pred);
+void assert_ptr(const char *file, int line,
+ const char *a1, const char *a2,
+ const void *aa1, const void *aa2, enum test_predicate pred);
+void assert_u8(const char *file, int line,
+ const char *a1, const char *a2,
+ u_int8_t aa1, u_int8_t aa2, enum test_predicate pred);
+void assert_u16(const char *file, int line,
+ const char *a1, const char *a2,
+ u_int16_t aa1, u_int16_t aa2, enum test_predicate pred);
+void assert_u32(const char *file, int line,
+ const char *a1, const char *a2,
+ u_int32_t aa1, u_int32_t aa2, enum test_predicate pred);
+void assert_u64(const char *file, int line,
+ const char *a1, const char *a2,
+ u_int64_t aa1, u_int64_t aa2, enum test_predicate pred);
+
+#define TEST_START(n) test_start(n)
+#define TEST_DONE() test_done()
+#define TEST_ONERROR(f, c) set_onerror_func(f, c)
+#define SSL_ERR_CHECK() ssl_err_check(__FILE__, __LINE__)
+
+#define ASSERT_BIGNUM_EQ(a1, a2) \
+ assert_bignum(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ)
+#define ASSERT_STRING_EQ(a1, a2) \
+ assert_string(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ)
+#define ASSERT_MEM_EQ(a1, a2, l) \
+ assert_mem(__FILE__, __LINE__, #a1, #a2, a1, a2, l, TEST_EQ)
+#define ASSERT_MEM_FILLED_EQ(a1, c, l) \
+ assert_mem_filled(__FILE__, __LINE__, #a1, a1, c, l, TEST_EQ)
+#define ASSERT_MEM_ZERO_EQ(a1, l) \
+ assert_mem_filled(__FILE__, __LINE__, #a1, a1, '\0', l, TEST_EQ)
+#define ASSERT_INT_EQ(a1, a2) \
+ assert_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ)
+#define ASSERT_SIZE_T_EQ(a1, a2) \
+ assert_size_t(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ)
+#define ASSERT_U_INT_EQ(a1, a2) \
+ assert_u_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ)
+#define ASSERT_LONG_EQ(a1, a2) \
+ assert_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ)
+#define ASSERT_LONG_LONG_EQ(a1, a2) \
+ assert_long_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ)
+#define ASSERT_CHAR_EQ(a1, a2) \
+ assert_char(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ)
+#define ASSERT_PTR_EQ(a1, a2) \
+ assert_ptr(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ)
+#define ASSERT_U8_EQ(a1, a2) \
+ assert_u8(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ)
+#define ASSERT_U16_EQ(a1, a2) \
+ assert_u16(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ)
+#define ASSERT_U32_EQ(a1, a2) \
+ assert_u32(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ)
+#define ASSERT_U64_EQ(a1, a2) \
+ assert_u64(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ)
+
+#define ASSERT_BIGNUM_NE(a1, a2) \
+ assert_bignum(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE)
+#define ASSERT_STRING_NE(a1, a2) \
+ assert_string(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE)
+#define ASSERT_MEM_NE(a1, a2, l) \
+ assert_mem(__FILE__, __LINE__, #a1, #a2, a1, a2, l, TEST_NE)
+#define ASSERT_MEM_ZERO_NE(a1, l) \
+ assert_mem_filled(__FILE__, __LINE__, #a1, a1, '\0', l, TEST_NE)
+#define ASSERT_INT_NE(a1, a2) \
+ assert_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE)
+#define ASSERT_SIZE_T_NE(a1, a2) \
+ assert_size_t(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE)
+#define ASSERT_U_INT_NE(a1, a2) \
+ assert_u_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE)
+#define ASSERT_LONG_NE(a1, a2) \
+ assert_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE)
+#define ASSERT_LONG_LONG_NE(a1, a2) \
+ assert_long_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE)
+#define ASSERT_CHAR_NE(a1, a2) \
+ assert_char(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE)
+#define ASSERT_PTR_NE(a1, a2) \
+ assert_ptr(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE)
+#define ASSERT_U8_NE(a1, a2) \
+ assert_u8(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE)
+#define ASSERT_U16_NE(a1, a2) \
+ assert_u16(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE)
+#define ASSERT_U32_NE(a1, a2) \
+ assert_u32(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE)
+#define ASSERT_U64_NE(a1, a2) \
+ assert_u64(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE)
+
+#define ASSERT_BIGNUM_LT(a1, a2) \
+ assert_bignum(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT)
+#define ASSERT_STRING_LT(a1, a2) \
+ assert_string(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT)
+#define ASSERT_MEM_LT(a1, a2, l) \
+ assert_mem(__FILE__, __LINE__, #a1, #a2, a1, a2, l, TEST_LT)
+#define ASSERT_INT_LT(a1, a2) \
+ assert_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT)
+#define ASSERT_SIZE_T_LT(a1, a2) \
+ assert_size_t(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT)
+#define ASSERT_U_INT_LT(a1, a2) \
+ assert_u_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT)
+#define ASSERT_LONG_LT(a1, a2) \
+ assert_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT)
+#define ASSERT_LONG_LONG_LT(a1, a2) \
+ assert_long_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT)
+#define ASSERT_CHAR_LT(a1, a2) \
+ assert_char(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT)
+#define ASSERT_PTR_LT(a1, a2) \
+ assert_ptr(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT)
+#define ASSERT_U8_LT(a1, a2) \
+ assert_u8(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT)
+#define ASSERT_U16_LT(a1, a2) \
+ assert_u16(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT)
+#define ASSERT_U32_LT(a1, a2) \
+ assert_u32(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT)
+#define ASSERT_U64_LT(a1, a2) \
+ assert_u64(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT)
+
+#define ASSERT_BIGNUM_LE(a1, a2) \
+ assert_bignum(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE)
+#define ASSERT_STRING_LE(a1, a2) \
+ assert_string(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE)
+#define ASSERT_MEM_LE(a1, a2, l) \
+ assert_mem(__FILE__, __LINE__, #a1, #a2, a1, a2, l, TEST_LE)
+#define ASSERT_INT_LE(a1, a2) \
+ assert_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE)
+#define ASSERT_SIZE_T_LE(a1, a2) \
+ assert_size_t(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE)
+#define ASSERT_U_INT_LE(a1, a2) \
+ assert_u_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE)
+#define ASSERT_LONG_LE(a1, a2) \
+ assert_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE)
+#define ASSERT_LONG_LONG_LE(a1, a2) \
+ assert_long_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE)
+#define ASSERT_CHAR_LE(a1, a2) \
+ assert_char(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE)
+#define ASSERT_PTR_LE(a1, a2) \
+ assert_ptr(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE)
+#define ASSERT_U8_LE(a1, a2) \
+ assert_u8(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE)
+#define ASSERT_U16_LE(a1, a2) \
+ assert_u16(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE)
+#define ASSERT_U32_LE(a1, a2) \
+ assert_u32(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE)
+#define ASSERT_U64_LE(a1, a2) \
+ assert_u64(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE)
+
+#define ASSERT_BIGNUM_GT(a1, a2) \
+ assert_bignum(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT)
+#define ASSERT_STRING_GT(a1, a2) \
+ assert_string(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT)
+#define ASSERT_MEM_GT(a1, a2, l) \
+ assert_mem(__FILE__, __LINE__, #a1, #a2, a1, a2, l, TEST_GT)
+#define ASSERT_INT_GT(a1, a2) \
+ assert_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT)
+#define ASSERT_SIZE_T_GT(a1, a2) \
+ assert_size_t(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT)
+#define ASSERT_U_INT_GT(a1, a2) \
+ assert_u_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT)
+#define ASSERT_LONG_GT(a1, a2) \
+ assert_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT)
+#define ASSERT_LONG_LONG_GT(a1, a2) \
+ assert_long_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT)
+#define ASSERT_CHAR_GT(a1, a2) \
+ assert_char(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT)
+#define ASSERT_PTR_GT(a1, a2) \
+ assert_ptr(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT)
+#define ASSERT_U8_GT(a1, a2) \
+ assert_u8(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT)
+#define ASSERT_U16_GT(a1, a2) \
+ assert_u16(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT)
+#define ASSERT_U32_GT(a1, a2) \
+ assert_u32(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT)
+#define ASSERT_U64_GT(a1, a2) \
+ assert_u64(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT)
+
+#define ASSERT_BIGNUM_GE(a1, a2) \
+ assert_bignum(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE)
+#define ASSERT_STRING_GE(a1, a2) \
+ assert_string(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE)
+#define ASSERT_MEM_GE(a1, a2, l) \
+ assert_mem(__FILE__, __LINE__, #a1, #a2, a1, a2, l, TEST_GE)
+#define ASSERT_INT_GE(a1, a2) \
+ assert_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE)
+#define ASSERT_SIZE_T_GE(a1, a2) \
+ assert_size_t(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE)
+#define ASSERT_U_INT_GE(a1, a2) \
+ assert_u_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE)
+#define ASSERT_LONG_GE(a1, a2) \
+ assert_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE)
+#define ASSERT_LONG_LONG_GE(a1, a2) \
+ assert_long_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE)
+#define ASSERT_CHAR_GE(a1, a2) \
+ assert_char(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE)
+#define ASSERT_PTR_GE(a1, a2) \
+ assert_ptr(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE)
+#define ASSERT_U8_GE(a1, a2) \
+ assert_u8(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE)
+#define ASSERT_U16_GE(a1, a2) \
+ assert_u16(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE)
+#define ASSERT_U32_GE(a1, a2) \
+ assert_u32(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE)
+#define ASSERT_U64_GE(a1, a2) \
+ assert_u64(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE)
+
+/* Fuzzing support */
+
+struct fuzz;
+#define FUZZ_1_BIT_FLIP 0x00000001 /* Flip one bit at a time */
+#define FUZZ_2_BIT_FLIP 0x00000002 /* Flip two bits at a time */
+#define FUZZ_1_BYTE_FLIP 0x00000004 /* Flip one byte at a time */
+#define FUZZ_2_BYTE_FLIP 0x00000008 /* Flip two bytes at a time */
+#define FUZZ_TRUNCATE_START 0x00000010 /* Truncate from beginning */
+#define FUZZ_TRUNCATE_END 0x00000020 /* Truncate from end */
+#define FUZZ_BASE64 0x00000040 /* Try all base64 chars */
+#define FUZZ_MAX FUZZ_BASE64
+
+/* Start fuzzing a blob of data with selected strategies (bitmask) */
+struct fuzz *fuzz_begin(u_int strategies, const void *p, size_t l);
+
+/* Free a fuzz context */
+void fuzz_cleanup(struct fuzz *fuzz);
+
+/* Prepare the next fuzz case in the series */
+void fuzz_next(struct fuzz *fuzz);
+
+/*
+ * Check whether this fuzz case is identical to the original
+ * This is slow, but useful if the caller needs to ensure that all tests
+ * generated change the input (e.g. when fuzzing signatures).
+ */
+int fuzz_matches_original(struct fuzz *fuzz);
+
+/* Determine whether the current fuzz sequence is exhausted (nonzero = yes) */
+int fuzz_done(struct fuzz *fuzz);
+
+/* Return the length and a pointer to the current fuzzed case */
+size_t fuzz_len(struct fuzz *fuzz);
+u_char *fuzz_ptr(struct fuzz *fuzz);
+
+/* Dump the current fuzz case to stderr */
+void fuzz_dump(struct fuzz *fuzz);
+
+#endif /* _TEST_HELPER_H */
diff --git a/regress/unittests/utf8/Makefile b/regress/unittests/utf8/Makefile
new file mode 100644
index 0000000..f8eec04
--- /dev/null
+++ b/regress/unittests/utf8/Makefile
@@ -0,0 +1,14 @@
+# $OpenBSD: Makefile,v 1.5 2017/12/21 00:41:22 djm Exp $
+
+PROG=test_utf8
+SRCS=tests.c
+
+# From usr.bin/ssh
+SRCS+=utf8.c atomicio.c
+
+REGRESS_TARGETS=run-regress-${PROG}
+
+run-regress-${PROG}: ${PROG}
+ env ${TEST_ENV} ./${PROG}
+
+.include <bsd.regress.mk>
diff --git a/regress/unittests/utf8/tests.c b/regress/unittests/utf8/tests.c
new file mode 100644
index 0000000..f0bbca5
--- /dev/null
+++ b/regress/unittests/utf8/tests.c
@@ -0,0 +1,102 @@
+/* $OpenBSD: tests.c,v 1.4 2017/02/19 00:11:29 djm Exp $ */
+/*
+ * Regress test for the utf8.h *mprintf() API
+ *
+ * Written by Ingo Schwarze <schwarze@openbsd.org> in 2016
+ * and placed in the public domain.
+ */
+
+#include "includes.h"
+
+#include <locale.h>
+#include <string.h>
+
+#include "../test_helper/test_helper.h"
+
+#include "utf8.h"
+
+static void
+badarg(void)
+{
+ char buf[16];
+ int len, width;
+
+ width = 1;
+ TEST_START("utf8_badarg");
+ len = snmprintf(buf, sizeof(buf), &width, "\377");
+ ASSERT_INT_EQ(len, -1);
+ ASSERT_STRING_EQ(buf, "");
+ ASSERT_INT_EQ(width, 0);
+ TEST_DONE();
+}
+
+static void
+one(int utf8, const char *name, const char *mbs, int width,
+ int wantwidth, int wantlen, const char *wants)
+{
+ char buf[16];
+ int *wp;
+ int len;
+
+ if (wantlen == -2)
+ wantlen = strlen(wants);
+ (void)strlcpy(buf, utf8 ? "utf8_" : "c_", sizeof(buf));
+ (void)strlcat(buf, name, sizeof(buf));
+ TEST_START(buf);
+ wp = wantwidth == -2 ? NULL : &width;
+ len = snmprintf(buf, sizeof(buf), wp, "%s", mbs);
+ ASSERT_INT_EQ(len, wantlen);
+ ASSERT_STRING_EQ(buf, wants);
+ ASSERT_INT_EQ(width, wantwidth);
+ TEST_DONE();
+}
+
+void
+tests(void)
+{
+ char *loc;
+
+ TEST_START("utf8_setlocale");
+ loc = setlocale(LC_CTYPE, "en_US.UTF-8");
+ ASSERT_PTR_NE(loc, NULL);
+ TEST_DONE();
+
+ badarg();
+ one(1, "empty", "", 2, 0, 0, "");
+ one(1, "ascii", "x", -2, -2, -2, "x");
+ one(1, "newline", "a\nb", -2, -2, -2, "a\nb");
+ one(1, "cr", "a\rb", -2, -2, -2, "a\rb");
+ one(1, "tab", "a\tb", -2, -2, -2, "a\tb");
+ one(1, "esc", "\033x", -2, -2, -2, "\\033x");
+ one(1, "inv_badbyte", "\377x", -2, -2, -2, "\\377x");
+ one(1, "inv_nocont", "\341x", -2, -2, -2, "\\341x");
+ one(1, "inv_nolead", "a\200b", -2, -2, -2, "a\\200b");
+ one(1, "sz_ascii", "1234567890123456", -2, -2, 16, "123456789012345");
+ one(1, "sz_esc", "123456789012\033", -2, -2, 16, "123456789012");
+ one(1, "width_ascii", "123", 2, 2, -1, "12");
+ one(1, "width_double", "a\343\201\201", 2, 1, -1, "a");
+ one(1, "double_fit", "a\343\201\201", 3, 3, 4, "a\343\201\201");
+ one(1, "double_spc", "a\343\201\201", 4, 3, 4, "a\343\201\201");
+
+ TEST_START("C_setlocale");
+ loc = setlocale(LC_CTYPE, "C");
+ ASSERT_PTR_NE(loc, NULL);
+ TEST_DONE();
+
+ badarg();
+ one(0, "empty", "", 2, 0, 0, "");
+ one(0, "ascii", "x", -2, -2, -2, "x");
+ one(0, "newline", "a\nb", -2, -2, -2, "a\nb");
+ one(0, "cr", "a\rb", -2, -2, -2, "a\rb");
+ one(0, "tab", "a\tb", -2, -2, -2, "a\tb");
+ one(0, "esc", "\033x", -2, -2, -2, "\\033x");
+ one(0, "inv_badbyte", "\377x", -2, -2, -2, "\\377x");
+ one(0, "inv_nocont", "\341x", -2, -2, -2, "\\341x");
+ one(0, "inv_nolead", "a\200b", -2, -2, -2, "a\\200b");
+ one(0, "sz_ascii", "1234567890123456", -2, -2, 16, "123456789012345");
+ one(0, "sz_esc", "123456789012\033", -2, -2, 16, "123456789012");
+ one(0, "width_ascii", "123", 2, 2, -1, "12");
+ one(0, "width_double", "a\343\201\201", 2, 1, -1, "a");
+ one(0, "double_fit", "a\343\201\201", 7, 5, -1, "a\\343");
+ one(0, "double_spc", "a\343\201\201", 13, 13, 13, "a\\343\\201\\201");
+}