diff options
Diffstat (limited to 'regress/unittests/sshsig')
26 files changed, 1064 insertions, 0 deletions
diff --git a/regress/unittests/sshsig/Makefile b/regress/unittests/sshsig/Makefile new file mode 100644 index 000000000..4b607df45 --- /dev/null +++ b/regress/unittests/sshsig/Makefile | |||
@@ -0,0 +1,25 @@ | |||
1 | # $OpenBSD: Makefile,v 1.1 2020/06/19 04:32:09 djm Exp $ | ||
2 | |||
3 | PROG=test_sshsig | ||
4 | SRCS=tests.c | ||
5 | |||
6 | # From usr.bin/ssh | ||
7 | SRCS+=sshbuf-getput-basic.c sshbuf-getput-crypto.c sshbuf-misc.c sshbuf.c | ||
8 | SRCS+=sshbuf-io.c atomicio.c sshkey.c authfile.c cipher.c log.c ssh-rsa.c | ||
9 | SRCS+=ssh-dss.c ssh-ecdsa.c ssh-ed25519.c mac.c umac.c umac128.c hmac.c misc.c | ||
10 | SRCS+=ssherr.c uidswap.c cleanup.c xmalloc.c match.c krl.c fatal.c | ||
11 | SRCS+=addrmatch.c bitmap.c sshsig.c | ||
12 | SRCS+=ed25519.c hash.c ge25519.c fe25519.c sc25519.c verify.c | ||
13 | SRCS+=cipher-chachapoly.c chacha.c poly1305.c ssh-ecdsa-sk.c ssh-sk.c | ||
14 | SRCS+=ssh-ed25519-sk.c sk-usbhid.c | ||
15 | |||
16 | SRCS+=digest-openssl.c | ||
17 | #SRCS+=digest-libc.c | ||
18 | SRCS+=utf8.c | ||
19 | |||
20 | REGRESS_TARGETS=run-regress-${PROG} | ||
21 | |||
22 | run-regress-${PROG}: ${PROG} | ||
23 | env ${TEST_ENV} ./${PROG} ${UNITTEST_ARGS} -d ${.CURDIR}/testdata | ||
24 | |||
25 | .include <bsd.regress.mk> | ||
diff --git a/regress/unittests/sshsig/mktestdata.sh b/regress/unittests/sshsig/mktestdata.sh new file mode 100755 index 000000000..d2300f9c6 --- /dev/null +++ b/regress/unittests/sshsig/mktestdata.sh | |||
@@ -0,0 +1,42 @@ | |||
1 | #!/bin/sh | ||
2 | # $OpenBSD: mktestdata.sh,v 1.1 2020/06/19 04:32:09 djm Exp $ | ||
3 | |||
4 | NAMESPACE=unittest | ||
5 | |||
6 | set -ex | ||
7 | |||
8 | cd testdata | ||
9 | |||
10 | if [ -f ../../../misc/sk-dummy/sk-dummy.so ] ; then | ||
11 | SK_DUMMY=../../../misc/sk-dummy/sk-dummy.so | ||
12 | elif [ -f ../../../misc/sk-dummy/obj/sk-dummy.so ] ; then | ||
13 | SK_DUMMY=../../../misc/sk-dummy/obj/sk-dummy.so | ||
14 | else | ||
15 | echo "Can't find sk-dummy.so" 1>&2 | ||
16 | exit 1 | ||
17 | fi | ||
18 | |||
19 | rm -f signed-data namespace | ||
20 | rm -f rsa dsa ecdsa ed25519 ecdsa_sk ed25519_sk | ||
21 | rm -f rsa.sig dsa.sig ecdsa.sig ed25519.sig ecdsa_sk.sig ed25519_sk.sig | ||
22 | |||
23 | printf "This is a test, this is only a test" > signed-data | ||
24 | printf "$NAMESPACE" > namespace | ||
25 | |||
26 | ssh-keygen -t rsa -C "RSA test" -N "" -f rsa -m PEM | ||
27 | ssh-keygen -t dsa -C "DSA test" -N "" -f dsa -m PEM | ||
28 | ssh-keygen -t ecdsa -C "ECDSA test" -N "" -f ecdsa -m PEM | ||
29 | ssh-keygen -t ed25519 -C "ED25519 test key" -N "" -f ed25519 | ||
30 | ssh-keygen -w "$SK_DUMMY" -t ecdsa-sk -C "ECDSA-SK test key" \ | ||
31 | -N "" -f ecdsa_sk | ||
32 | ssh-keygen -w "$SK_DUMMY" -t ed25519-sk -C "ED25519-SK test key" \ | ||
33 | -N "" -f ed25519_sk | ||
34 | |||
35 | ssh-keygen -Y sign -f rsa -n $NAMESPACE - < signed-data > rsa.sig | ||
36 | ssh-keygen -Y sign -f dsa -n $NAMESPACE - < signed-data > dsa.sig | ||
37 | ssh-keygen -Y sign -f ecdsa -n $NAMESPACE - < signed-data > ecdsa.sig | ||
38 | ssh-keygen -Y sign -f ed25519 -n $NAMESPACE - < signed-data > ed25519.sig | ||
39 | ssh-keygen -w "$SK_DUMMY" \ | ||
40 | -Y sign -f ecdsa_sk -n $NAMESPACE - < signed-data > ecdsa_sk.sig | ||
41 | ssh-keygen -w "$SK_DUMMY" \ | ||
42 | -Y sign -f ed25519_sk -n $NAMESPACE - < signed-data > ed25519_sk.sig | ||
diff --git a/regress/unittests/sshsig/testdata/dsa b/regress/unittests/sshsig/testdata/dsa new file mode 100644 index 000000000..7c0063efc --- /dev/null +++ b/regress/unittests/sshsig/testdata/dsa | |||
@@ -0,0 +1,12 @@ | |||
1 | -----BEGIN DSA PRIVATE KEY----- | ||
2 | MIIBuwIBAAKBgQCXpndQdz2mQVnk+lYOF3nxDT+h6SiJmUvBFhnFWBv8tG4pTOkb | ||
3 | EwGufLEzGpzjTj+3bjVau7LFt37AFrqs4Num272BWNsYNIjOlGPgq7Xjv32FN00x | ||
4 | JYh1DoRs1cGGnvohlsWEamGGhTHD1a9ipctPEBV+NrxtZMrl+pO/ZZg8vQIVAKJB | ||
5 | P3iNYSpSuW74+q4WxLCuK8O3AoGAQldE+BIuxlvoG1IFiWesx0CU+H2KO0SEZc9A | ||
6 | SX/qjOabh0Fb78ofTlEf9gWHFfat8SvSJQIOPMVlb76Lio8AAMT8Eaa/qQKKYmQL | ||
7 | dNq4MLhhjxx5KLGt6J2JyFPExCv+qnHYHD59ngtLwKyqGjpSC8LPLktdXn8W/Aad | ||
8 | Ly1K7+MCgYBsMHBczhSeUh8w7i20CVg4OlNTmfJRVU2tO6OpMxZ/quitRm3hLKSN | ||
9 | u4xRkvHJwi4LhQtv1SXvLI5gs5P3gCG8tsIAiyCqLinHha63iBdJpqhnV/x/j7dB | ||
10 | yJr3xJbnmLdWLkkCtNk1Ir1/CuEz+ufAyLGdKWksEAu1UUlb501BkwIVAILIa3Rg | ||
11 | 0h7J9lQpHJphvF3K0M1T | ||
12 | -----END DSA PRIVATE KEY----- | ||
diff --git a/regress/unittests/sshsig/testdata/dsa.pub b/regress/unittests/sshsig/testdata/dsa.pub new file mode 100644 index 000000000..e77aa7ef4 --- /dev/null +++ b/regress/unittests/sshsig/testdata/dsa.pub | |||
@@ -0,0 +1 @@ | |||
ssh-dss AAAAB3NzaC1kc3MAAACBAJemd1B3PaZBWeT6Vg4XefENP6HpKImZS8EWGcVYG/y0bilM6RsTAa58sTManONOP7duNVq7ssW3fsAWuqzg26bbvYFY2xg0iM6UY+CrteO/fYU3TTEliHUOhGzVwYae+iGWxYRqYYaFMcPVr2Kly08QFX42vG1kyuX6k79lmDy9AAAAFQCiQT94jWEqUrlu+PquFsSwrivDtwAAAIBCV0T4Ei7GW+gbUgWJZ6zHQJT4fYo7RIRlz0BJf+qM5puHQVvvyh9OUR/2BYcV9q3xK9IlAg48xWVvvouKjwAAxPwRpr+pAopiZAt02rgwuGGPHHkosa3onYnIU8TEK/6qcdgcPn2eC0vArKoaOlILws8uS11efxb8Bp0vLUrv4wAAAIBsMHBczhSeUh8w7i20CVg4OlNTmfJRVU2tO6OpMxZ/quitRm3hLKSNu4xRkvHJwi4LhQtv1SXvLI5gs5P3gCG8tsIAiyCqLinHha63iBdJpqhnV/x/j7dByJr3xJbnmLdWLkkCtNk1Ir1/CuEz+ufAyLGdKWksEAu1UUlb501Bkw== DSA test | |||
diff --git a/regress/unittests/sshsig/testdata/dsa.sig b/regress/unittests/sshsig/testdata/dsa.sig new file mode 100644 index 000000000..0b14ad6b8 --- /dev/null +++ b/regress/unittests/sshsig/testdata/dsa.sig | |||
@@ -0,0 +1,13 @@ | |||
1 | -----BEGIN SSH SIGNATURE----- | ||
2 | U1NIU0lHAAAAAQAAAbEAAAAHc3NoLWRzcwAAAIEAl6Z3UHc9pkFZ5PpWDhd58Q0/oekoiZ | ||
3 | lLwRYZxVgb/LRuKUzpGxMBrnyxMxqc404/t241Wruyxbd+wBa6rODbptu9gVjbGDSIzpRj | ||
4 | 4Ku14799hTdNMSWIdQ6EbNXBhp76IZbFhGphhoUxw9WvYqXLTxAVfja8bWTK5fqTv2WYPL | ||
5 | 0AAAAVAKJBP3iNYSpSuW74+q4WxLCuK8O3AAAAgEJXRPgSLsZb6BtSBYlnrMdAlPh9ijtE | ||
6 | hGXPQEl/6ozmm4dBW+/KH05RH/YFhxX2rfEr0iUCDjzFZW++i4qPAADE/BGmv6kCimJkC3 | ||
7 | TauDC4YY8ceSixreidichTxMQr/qpx2Bw+fZ4LS8Csqho6UgvCzy5LXV5/FvwGnS8tSu/j | ||
8 | AAAAgGwwcFzOFJ5SHzDuLbQJWDg6U1OZ8lFVTa07o6kzFn+q6K1GbeEspI27jFGS8cnCLg | ||
9 | uFC2/VJe8sjmCzk/eAIby2wgCLIKouKceFrreIF0mmqGdX/H+Pt0HImvfElueYt1YuSQK0 | ||
10 | 2TUivX8K4TP658DIsZ0paSwQC7VRSVvnTUGTAAAACHVuaXR0ZXN0AAAAAAAAAAZzaGE1MT | ||
11 | IAAAA3AAAAB3NzaC1kc3MAAAAodi5lr0pqBpO76OY4N1CtfR85BCgZ95qfVjP/e9lToj0q | ||
12 | lwjSJJXUjw== | ||
13 | -----END SSH SIGNATURE----- | ||
diff --git a/regress/unittests/sshsig/testdata/ecdsa b/regress/unittests/sshsig/testdata/ecdsa new file mode 100644 index 000000000..55fb440e0 --- /dev/null +++ b/regress/unittests/sshsig/testdata/ecdsa | |||
@@ -0,0 +1,5 @@ | |||
1 | -----BEGIN EC PRIVATE KEY----- | ||
2 | MHcCAQEEIFg0ZCSEB5LNeLsXYL25g3kqEWsqh52DR+yNOjyQJqyZoAoGCCqGSM49 | ||
3 | AwEHoUQDQgAE3sud88FV0N8FPspZSV7LWqj6uPPLRZiSsenNuEYAteWPyDgrZsWb | ||
4 | LzXBuUJucepaCNuW/QWgHBRbrjWj3ERm3A== | ||
5 | -----END EC PRIVATE KEY----- | ||
diff --git a/regress/unittests/sshsig/testdata/ecdsa.pub b/regress/unittests/sshsig/testdata/ecdsa.pub new file mode 100644 index 000000000..14ec6cf12 --- /dev/null +++ b/regress/unittests/sshsig/testdata/ecdsa.pub | |||
@@ -0,0 +1 @@ | |||
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBN7LnfPBVdDfBT7KWUley1qo+rjzy0WYkrHpzbhGALXlj8g4K2bFmy81wblCbnHqWgjblv0FoBwUW641o9xEZtw= ECDSA test | |||
diff --git a/regress/unittests/sshsig/testdata/ecdsa.sig b/regress/unittests/sshsig/testdata/ecdsa.sig new file mode 100644 index 000000000..79781570c --- /dev/null +++ b/regress/unittests/sshsig/testdata/ecdsa.sig | |||
@@ -0,0 +1,7 @@ | |||
1 | -----BEGIN SSH SIGNATURE----- | ||
2 | U1NIU0lHAAAAAQAAAGgAAAATZWNkc2Etc2hhMi1uaXN0cDI1NgAAAAhuaXN0cDI1NgAAAE | ||
3 | EE3sud88FV0N8FPspZSV7LWqj6uPPLRZiSsenNuEYAteWPyDgrZsWbLzXBuUJucepaCNuW | ||
4 | /QWgHBRbrjWj3ERm3AAAAAh1bml0dGVzdAAAAAAAAAAGc2hhNTEyAAAAZQAAABNlY2RzYS | ||
5 | 1zaGEyLW5pc3RwMjU2AAAASgAAACEAycVNsTlE+XEZYyYiDxWZlliruf/pPMhEEMR/XLdQ | ||
6 | a4MAAAAhALQt+5gES7L3uKGptHB6UZQMuZ2WyI0C6FJs4v6AtMIU | ||
7 | -----END SSH SIGNATURE----- | ||
diff --git a/regress/unittests/sshsig/testdata/ecdsa_sk b/regress/unittests/sshsig/testdata/ecdsa_sk new file mode 100644 index 000000000..62ae44cb0 --- /dev/null +++ b/regress/unittests/sshsig/testdata/ecdsa_sk | |||
@@ -0,0 +1,13 @@ | |||
1 | -----BEGIN OPENSSH PRIVATE KEY----- | ||
2 | b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAfwAAACJzay1lY2 | ||
3 | RzYS1zaGEyLW5pc3RwMjU2QG9wZW5zc2guY29tAAAACG5pc3RwMjU2AAAAQQSg1WuY0XE+ | ||
4 | VexOsrJsFYuxyVoe6eQ/oXmyz2pEHKZw9moyWehv+Fs7oZWFp3JVmOtybKQ6dvfUZYauQE | ||
5 | /Ov4PAAAAABHNzaDoAAAGI6iV41+oleNcAAAAic2stZWNkc2Etc2hhMi1uaXN0cDI1NkBv | ||
6 | cGVuc3NoLmNvbQAAAAhuaXN0cDI1NgAAAEEEoNVrmNFxPlXsTrKybBWLsclaHunkP6F5ss | ||
7 | 9qRBymcPZqMlnob/hbO6GVhadyVZjrcmykOnb31GWGrkBPzr+DwAAAAARzc2g6AQAAAOMt | ||
8 | LS0tLUJFR0lOIEVDIFBSSVZBVEUgS0VZLS0tLS0KTUhjQ0FRRUVJQm9oeW54M2tpTFVEeS | ||
9 | t5UjU3WXBXSU5KektnU1p6WnV2VTljYXFla3JGcW9Bb0dDQ3FHU000OQpBd0VIb1VRRFFn | ||
10 | QUVvTlZybU5GeFBsWHNUckt5YkJXTHNjbGFIdW5rUDZGNXNzOXFSQnltY1BacU1sbm9iL2 | ||
11 | hiCk82R1ZoYWR5VlpqcmNteWtPbmIzMUdXR3JrQlB6citEd0E9PQotLS0tLUVORCBFQyBQ | ||
12 | UklWQVRFIEtFWS0tLS0tCgAAAAAAAAARRUNEU0EtU0sgdGVzdCBrZXk= | ||
13 | -----END OPENSSH PRIVATE KEY----- | ||
diff --git a/regress/unittests/sshsig/testdata/ecdsa_sk.pub b/regress/unittests/sshsig/testdata/ecdsa_sk.pub new file mode 100644 index 000000000..385ebf15b --- /dev/null +++ b/regress/unittests/sshsig/testdata/ecdsa_sk.pub | |||
@@ -0,0 +1 @@ | |||
sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBKDVa5jRcT5V7E6ysmwVi7HJWh7p5D+hebLPakQcpnD2ajJZ6G/4WzuhlYWnclWY63JspDp299Rlhq5AT86/g8AAAAAEc3NoOg== ECDSA-SK test key | |||
diff --git a/regress/unittests/sshsig/testdata/ecdsa_sk.sig b/regress/unittests/sshsig/testdata/ecdsa_sk.sig new file mode 100644 index 000000000..86de36063 --- /dev/null +++ b/regress/unittests/sshsig/testdata/ecdsa_sk.sig | |||
@@ -0,0 +1,8 @@ | |||
1 | -----BEGIN SSH SIGNATURE----- | ||
2 | U1NIU0lHAAAAAQAAAH8AAAAic2stZWNkc2Etc2hhMi1uaXN0cDI1NkBvcGVuc3NoLmNvbQ | ||
3 | AAAAhuaXN0cDI1NgAAAEEEoNVrmNFxPlXsTrKybBWLsclaHunkP6F5ss9qRBymcPZqMlno | ||
4 | b/hbO6GVhadyVZjrcmykOnb31GWGrkBPzr+DwAAAAARzc2g6AAAACHVuaXR0ZXN0AAAAAA | ||
5 | AAAAZzaGE1MTIAAAB3AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20A | ||
6 | AABIAAAAIHohGwyy8iKT3zwd1TYA9V/Ioo7h/3zCJUtyq/Qigt/HAAAAIGzidTwq7D/kFa | ||
7 | 7Xjcp/KkdbIs4MfQpfAW/0OciajlpzARI0Vng= | ||
8 | -----END SSH SIGNATURE----- | ||
diff --git a/regress/unittests/sshsig/testdata/ecdsa_sk_webauthn.pub b/regress/unittests/sshsig/testdata/ecdsa_sk_webauthn.pub new file mode 100644 index 000000000..1597302ce --- /dev/null +++ b/regress/unittests/sshsig/testdata/ecdsa_sk_webauthn.pub | |||
@@ -0,0 +1 @@ | |||
sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBBRGwDjs4HhJFcn4tJ5Gr72KcmRmCS1OirETxaXvnsNApgoOLF1a/7rxldfSMHm73eT1nhHe97W8qicPPEAKDJQAAAALbWluZHJvdC5vcmc= | |||
diff --git a/regress/unittests/sshsig/testdata/ecdsa_sk_webauthn.sig b/regress/unittests/sshsig/testdata/ecdsa_sk_webauthn.sig new file mode 100644 index 000000000..4bdd8edc6 --- /dev/null +++ b/regress/unittests/sshsig/testdata/ecdsa_sk_webauthn.sig | |||
@@ -0,0 +1,13 @@ | |||
1 | -----BEGIN SSH SIGNATURE----- | ||
2 | U1NIU0lHAAAAAQAAAIYAAAAic2stZWNkc2Etc2hhMi1uaXN0cDI1NkBvcGVuc3NoLmNvbQ | ||
3 | AAAAhuaXN0cDI1NgAAAEEEFEbAOOzgeEkVyfi0nkavvYpyZGYJLU6KsRPFpe+ew0CmCg4s | ||
4 | XVr/uvGV19Iwebvd5PWeEd73tbyqJw88QAoMlAAAAAttaW5kcm90Lm9yZwAAAAh1bml0dG | ||
5 | VzdAAAAAAAAAAGc2hhNTEyAAABhwAAACt3ZWJhdXRobi1zay1lY2RzYS1zaGEyLW5pc3Rw | ||
6 | MjU2QG9wZW5zc2guY29tAAAASQAAACBj2oMT9tb5wRXe6mdmf4/lgAO8wrgr95ouozwNg4 | ||
7 | itnQAAACEAtU9g5wz3HchUiLfLD6plr9T4TiJ32lVCrATSjpiy0SMBAAADHwAAABdodHRw | ||
8 | czovL3d3dy5taW5kcm90Lm9yZwAAAON7InR5cGUiOiJ3ZWJhdXRobi5nZXQiLCJjaGFsbG | ||
9 | VuZ2UiOiJVMU5JVTBsSEFBQUFDSFZ1YVhSMFpYTjBBQUFBQUFBQUFBWnphR0UxTVRJQUFB | ||
10 | QkFMTHU4WmdjU3h0Nk1zRlV6dWlaZ0c2R3dNZEo5ZDd4ZUU3WW9SSXcwZzlpSEpfd3NGRD | ||
11 | cxbzRXbHllenZGV0VqYnFRMHFDN0Z3R3Bqa2pVUVAtTmQ2dyIsIm9yaWdpbiI6Imh0dHBz | ||
12 | Oi8vd3d3Lm1pbmRyb3Qub3JnIiwiY3Jvc3NPcmlnaW4iOmZhbHNlfQAAAAA= | ||
13 | -----END SSH SIGNATURE----- | ||
diff --git a/regress/unittests/sshsig/testdata/ed25519 b/regress/unittests/sshsig/testdata/ed25519 new file mode 100644 index 000000000..b44a63d3e --- /dev/null +++ b/regress/unittests/sshsig/testdata/ed25519 | |||
@@ -0,0 +1,7 @@ | |||
1 | -----BEGIN OPENSSH PRIVATE KEY----- | ||
2 | b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW | ||
3 | QyNTUxOQAAACCJYs0iDdw0Fe/FTzY1b78I4H/j+R6mz2AmLtwTjHYwBAAAAJjpGas/6Rmr | ||
4 | PwAAAAtzc2gtZWQyNTUxOQAAACCJYs0iDdw0Fe/FTzY1b78I4H/j+R6mz2AmLtwTjHYwBA | ||
5 | AAAEDpSKRA1QKW6kYiQftGRWh+H0fNekzYLG6c3bzseoCpEolizSIN3DQV78VPNjVvvwjg | ||
6 | f+P5HqbPYCYu3BOMdjAEAAAAEEVEMjU1MTkgdGVzdCBrZXkBAgMEBQ== | ||
7 | -----END OPENSSH PRIVATE KEY----- | ||
diff --git a/regress/unittests/sshsig/testdata/ed25519.pub b/regress/unittests/sshsig/testdata/ed25519.pub new file mode 100644 index 000000000..b078e4516 --- /dev/null +++ b/regress/unittests/sshsig/testdata/ed25519.pub | |||
@@ -0,0 +1 @@ | |||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIlizSIN3DQV78VPNjVvvwjgf+P5HqbPYCYu3BOMdjAE ED25519 test key | |||
diff --git a/regress/unittests/sshsig/testdata/ed25519.sig b/regress/unittests/sshsig/testdata/ed25519.sig new file mode 100644 index 000000000..8e8ff2a8a --- /dev/null +++ b/regress/unittests/sshsig/testdata/ed25519.sig | |||
@@ -0,0 +1,6 @@ | |||
1 | -----BEGIN SSH SIGNATURE----- | ||
2 | U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgiWLNIg3cNBXvxU82NW+/COB/4/ | ||
3 | keps9gJi7cE4x2MAQAAAAIdW5pdHRlc3QAAAAAAAAABnNoYTUxMgAAAFMAAAALc3NoLWVk | ||
4 | MjU1MTkAAABAihQsbUzuNEFflk5Tw1+H9aLS7tZQk0RG8KW1DtOmDYYnWe3D3UKiG3fcJa | ||
5 | DNg4vBWp1j1gLRiBMOF+gwYNegDg== | ||
6 | -----END SSH SIGNATURE----- | ||
diff --git a/regress/unittests/sshsig/testdata/ed25519_sk b/regress/unittests/sshsig/testdata/ed25519_sk new file mode 100644 index 000000000..3a434ecb9 --- /dev/null +++ b/regress/unittests/sshsig/testdata/ed25519_sk | |||
@@ -0,0 +1,8 @@ | |||
1 | -----BEGIN OPENSSH PRIVATE KEY----- | ||
2 | b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAASgAAABpzay1zc2 | ||
3 | gtZWQyNTUxOUBvcGVuc3NoLmNvbQAAACCbGg2F0GK7nOm4pQmAyCuGEjnhvs5q0TtjPbdN | ||
4 | //+yxwAAAARzc2g6AAAAuBw56jAcOeowAAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY2 | ||
5 | 9tAAAAIJsaDYXQYruc6bilCYDIK4YSOeG+zmrRO2M9t03//7LHAAAABHNzaDoBAAAAQFXc | ||
6 | 6dCwWewIk1EBofAouGZApW8+s0XekXenxtb78+x0mxoNhdBiu5zpuKUJgMgrhhI54b7Oat | ||
7 | E7Yz23Tf//sscAAAAAAAAAE0VEMjU1MTktU0sgdGVzdCBrZXkBAgMEBQY= | ||
8 | -----END OPENSSH PRIVATE KEY----- | ||
diff --git a/regress/unittests/sshsig/testdata/ed25519_sk.pub b/regress/unittests/sshsig/testdata/ed25519_sk.pub new file mode 100644 index 000000000..71051ec3b --- /dev/null +++ b/regress/unittests/sshsig/testdata/ed25519_sk.pub | |||
@@ -0,0 +1 @@ | |||
sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIJsaDYXQYruc6bilCYDIK4YSOeG+zmrRO2M9t03//7LHAAAABHNzaDo= ED25519-SK test key | |||
diff --git a/regress/unittests/sshsig/testdata/ed25519_sk.sig b/regress/unittests/sshsig/testdata/ed25519_sk.sig new file mode 100644 index 000000000..49b6818da --- /dev/null +++ b/regress/unittests/sshsig/testdata/ed25519_sk.sig | |||
@@ -0,0 +1,7 @@ | |||
1 | -----BEGIN SSH SIGNATURE----- | ||
2 | U1NIU0lHAAAAAQAAAEoAAAAac2stc3NoLWVkMjU1MTlAb3BlbnNzaC5jb20AAAAgmxoNhd | ||
3 | Biu5zpuKUJgMgrhhI54b7OatE7Yz23Tf//sscAAAAEc3NoOgAAAAh1bml0dGVzdAAAAAAA | ||
4 | AAAGc2hhNTEyAAAAZwAAABpzay1zc2gtZWQyNTUxOUBvcGVuc3NoLmNvbQAAAEAi+7eTjW | ||
5 | /+LQ2M+sCD+KFtH1n7VFFJon/SZFsxODyV8cWTlFKj617Ys1Ur5TV6uaEXQhck8rBA2oQI | ||
6 | HTPANLIPARI0Vng= | ||
7 | -----END SSH SIGNATURE----- | ||
diff --git a/regress/unittests/sshsig/testdata/namespace b/regress/unittests/sshsig/testdata/namespace new file mode 100644 index 000000000..1570cd548 --- /dev/null +++ b/regress/unittests/sshsig/testdata/namespace | |||
@@ -0,0 +1 @@ | |||
unittest \ No newline at end of file | |||
diff --git a/regress/unittests/sshsig/testdata/rsa b/regress/unittests/sshsig/testdata/rsa new file mode 100644 index 000000000..228fad797 --- /dev/null +++ b/regress/unittests/sshsig/testdata/rsa | |||
@@ -0,0 +1,39 @@ | |||
1 | -----BEGIN RSA PRIVATE KEY----- | ||
2 | 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 | ||
39 | -----END RSA PRIVATE KEY----- | ||
diff --git a/regress/unittests/sshsig/testdata/rsa.pub b/regress/unittests/sshsig/testdata/rsa.pub new file mode 100644 index 000000000..30142ac0a --- /dev/null +++ b/regress/unittests/sshsig/testdata/rsa.pub | |||
@@ -0,0 +1 @@ | |||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDfzqWaNEe0mnKPzsGtL6ywya29z/VI83SBdM+z2NnWFgCr3w9xrf9FhUzgdpK7oI7O1AnZmdkPdN79d4GchTaZ+69ASX4OLCKwZ42CVXJEKLvvzt88UV9zHNl8NrsQHapjxLehpIZkrxCrO4bCYYAiaOVBfPon4cJ394P/nwagJJKKsgQIVYIC3642s4L8SaADOxt9x9ohttXMDjS2WBTeE7YnjaR13PwKKwNO0MpAAEVw5pBnJxJ+fscM/5aBctdcFQj3BUzVOJ/V/70xfTzEiy8K8bOjvIuBttMUtIavlKQD606ti6gDtXx+B7SJ3/emdW0bVSLcju96N4DB/Qdmq0V4ESc7CQgkWZl4y0V45V/7iSLiHWsOoC9Kxw28y0uno5elsABMBMC4RYSQhAGXWueCcR7SIL2JT2K0oxbqongW0hvgAIfUShTXHy+qsV5yDaG+p+TkJkEkhtXbwkW4aNpzc7EFnXsWpvyWOiZSKvUasRPbPro0yBvlOMMPsFU= RSA test | |||
diff --git a/regress/unittests/sshsig/testdata/rsa.sig b/regress/unittests/sshsig/testdata/rsa.sig new file mode 100644 index 000000000..15a032e01 --- /dev/null +++ b/regress/unittests/sshsig/testdata/rsa.sig | |||
@@ -0,0 +1,19 @@ | |||
1 | -----BEGIN SSH SIGNATURE----- | ||
2 | U1NIU0lHAAAAAQAAAZcAAAAHc3NoLXJzYQAAAAMBAAEAAAGBAN/OpZo0R7Saco/Owa0vrL | ||
3 | DJrb3P9UjzdIF0z7PY2dYWAKvfD3Gt/0WFTOB2krugjs7UCdmZ2Q903v13gZyFNpn7r0BJ | ||
4 | fg4sIrBnjYJVckQou+/O3zxRX3Mc2Xw2uxAdqmPEt6GkhmSvEKs7hsJhgCJo5UF8+ifhwn | ||
5 | f3g/+fBqAkkoqyBAhVggLfrjazgvxJoAM7G33H2iG21cwONLZYFN4TtieNpHXc/AorA07Q | ||
6 | ykAARXDmkGcnEn5+xwz/loFy11wVCPcFTNU4n9X/vTF9PMSLLwrxs6O8i4G20xS0hq+UpA | ||
7 | PrTq2LqAO1fH4HtInf96Z1bRtVItyO73o3gMH9B2arRXgRJzsJCCRZmXjLRXjlX/uJIuId | ||
8 | aw6gL0rHDbzLS6ejl6WwAEwEwLhFhJCEAZda54JxHtIgvYlPYrSjFuqieBbSG+AAh9RKFN | ||
9 | cfL6qxXnINob6n5OQmQSSG1dvCRbho2nNzsQWdexam/JY6JlIq9RqxE9s+ujTIG+U4ww+w | ||
10 | VQAAAAh1bml0dGVzdAAAAAAAAAAGc2hhNTEyAAABlAAAAAxyc2Etc2hhMi01MTIAAAGACi | ||
11 | nEpBrQxZi0yOrrT6h98JFfZh0XXioih4fzmvtoV0yOReWClS+otGgXoJyZHcbaKNOjDwSM | ||
12 | rIkUoX6OUJmtHYP0HRELnKw35m33LdBPXpFGS4tRS7NeSpvc04KtjT6jYXY9FjWy5hcn17 | ||
13 | Sxc/3DnJqLgJBur8acY7FeIzpWmKixPd/dGkEjdWoD9gO6szLczGuQgrOdYmSRL4yKadTJ | ||
14 | lVjz5OSeKSYYGQy33US2XQassRRNYf4e9byTA3DKvHa/OcTt7lFerea0kZdDpAboqffz7T | ||
15 | Yaw/hFskAYLIEdTW3aoXBGHSOvu8AkDOtb7qwuxGSQ27pjkDLDNsp1ceCFaCaQ6X83RZuK | ||
16 | ACv9JUBI5KaSf81e0bs0KezJKkhB9czeZ6dk96qISbgayEBnvhYgXvUDKtHn7HzNlCJKfK | ||
17 | 5ABhNxfGG2CD+NKqcrndwFgS1sQO3hbA84zPQb26ShBovT8ytHBmW1F8ZK4O9Bz61Q6EZK | ||
18 | vs/u6xP6LUean/so5daa | ||
19 | -----END SSH SIGNATURE----- | ||
diff --git a/regress/unittests/sshsig/testdata/signed-data b/regress/unittests/sshsig/testdata/signed-data new file mode 100644 index 000000000..7df4bedd1 --- /dev/null +++ b/regress/unittests/sshsig/testdata/signed-data | |||
@@ -0,0 +1 @@ | |||
This is a test, this is only a test \ No newline at end of file | |||
diff --git a/regress/unittests/sshsig/tests.c b/regress/unittests/sshsig/tests.c new file mode 100644 index 000000000..bf59d58d1 --- /dev/null +++ b/regress/unittests/sshsig/tests.c | |||
@@ -0,0 +1,139 @@ | |||
1 | /* $OpenBSD: tests.c,v 1.2 2020/06/22 06:00:06 djm Exp $ */ | ||
2 | /* | ||
3 | * Regress test for sshbuf.h buffer API | ||
4 | * | ||
5 | * Placed in the public domain | ||
6 | */ | ||
7 | |||
8 | #include "includes.h" | ||
9 | |||
10 | #include <sys/types.h> | ||
11 | #include <sys/param.h> | ||
12 | #include <sys/stat.h> | ||
13 | #include <fcntl.h> | ||
14 | #include <stdio.h> | ||
15 | #ifdef HAVE_STDINT_H | ||
16 | #include <stdint.h> | ||
17 | #endif | ||
18 | #include <stdlib.h> | ||
19 | #include <string.h> | ||
20 | #include <unistd.h> | ||
21 | |||
22 | #include <openssl/evp.h> | ||
23 | #include <openssl/crypto.h> | ||
24 | |||
25 | #include "ssherr.h" | ||
26 | #include "authfile.h" | ||
27 | #include "sshkey.h" | ||
28 | #include "sshbuf.h" | ||
29 | #include "sshsig.h" | ||
30 | #include "log.h" | ||
31 | |||
32 | #include "../test_helper/test_helper.h" | ||
33 | |||
34 | static struct sshbuf * | ||
35 | load_file(const char *name) | ||
36 | { | ||
37 | struct sshbuf *ret = NULL; | ||
38 | |||
39 | ASSERT_INT_EQ(sshbuf_load_file(test_data_file(name), &ret), 0); | ||
40 | ASSERT_PTR_NE(ret, NULL); | ||
41 | return ret; | ||
42 | } | ||
43 | |||
44 | static struct sshkey * | ||
45 | load_key(const char *name) | ||
46 | { | ||
47 | struct sshkey *ret = NULL; | ||
48 | ASSERT_INT_EQ(sshkey_load_public(test_data_file(name), &ret, NULL), 0); | ||
49 | ASSERT_PTR_NE(ret, NULL); | ||
50 | return ret; | ||
51 | } | ||
52 | |||
53 | static void | ||
54 | check_sig(const char *keyname, const char *signame, const struct sshbuf *msg, | ||
55 | const char *namespace) | ||
56 | { | ||
57 | struct sshkey *k, *sign_key; | ||
58 | struct sshbuf *sig, *rawsig; | ||
59 | struct sshkey_sig_details *sig_details; | ||
60 | |||
61 | k = load_key(keyname); | ||
62 | sig = load_file(signame); | ||
63 | sign_key = NULL; | ||
64 | sig_details = NULL; | ||
65 | rawsig = NULL; | ||
66 | ASSERT_INT_EQ(sshsig_dearmor(sig, &rawsig), 0); | ||
67 | ASSERT_INT_EQ(sshsig_verifyb(rawsig, msg, namespace, | ||
68 | &sign_key, &sig_details), 0); | ||
69 | ASSERT_INT_EQ(sshkey_equal(k, sign_key), 1); | ||
70 | sshkey_free(k); | ||
71 | sshkey_free(sign_key); | ||
72 | sshkey_sig_details_free(sig_details); | ||
73 | sshbuf_free(sig); | ||
74 | sshbuf_free(rawsig); | ||
75 | } | ||
76 | |||
77 | void | ||
78 | tests(void) | ||
79 | { | ||
80 | struct sshbuf *msg; | ||
81 | char *namespace; | ||
82 | |||
83 | #if 0 | ||
84 | log_init("test_sshsig", SYSLOG_LEVEL_DEBUG3, SYSLOG_FACILITY_AUTH, 1); | ||
85 | #endif | ||
86 | |||
87 | #ifdef WITH_OPENSSL | ||
88 | OpenSSL_add_all_algorithms(); | ||
89 | ERR_load_CRYPTO_strings(); | ||
90 | #endif | ||
91 | |||
92 | TEST_START("load data"); | ||
93 | msg = load_file("namespace"); | ||
94 | namespace = sshbuf_dup_string(msg); | ||
95 | ASSERT_PTR_NE(namespace, NULL); | ||
96 | sshbuf_free(msg); | ||
97 | msg = load_file("signed-data"); | ||
98 | TEST_DONE(); | ||
99 | |||
100 | #ifdef WITH_OPENSSL | ||
101 | TEST_START("check RSA signature"); | ||
102 | check_sig("rsa.pub", "rsa.sig", msg, namespace); | ||
103 | TEST_DONE(); | ||
104 | |||
105 | TEST_START("check DSA signature"); | ||
106 | check_sig("dsa.pub", "dsa.sig", msg, namespace); | ||
107 | TEST_DONE(); | ||
108 | |||
109 | #ifdef OPENSSL_HAS_ECC | ||
110 | TEST_START("check ECDSA signature"); | ||
111 | check_sig("ecdsa.pub", "ecdsa.sig", msg, namespace); | ||
112 | TEST_DONE(); | ||
113 | #endif | ||
114 | #endif | ||
115 | |||
116 | TEST_START("check ED25519 signature"); | ||
117 | check_sig("ed25519.pub", "ed25519.sig", msg, namespace); | ||
118 | TEST_DONE(); | ||
119 | |||
120 | #if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) | ||
121 | TEST_START("check ECDSA-SK signature"); | ||
122 | check_sig("ecdsa_sk.pub", "ecdsa_sk.sig", msg, namespace); | ||
123 | TEST_DONE(); | ||
124 | #endif | ||
125 | |||
126 | TEST_START("check ED25519-SK signature"); | ||
127 | check_sig("ed25519_sk.pub", "ed25519_sk.sig", msg, namespace); | ||
128 | TEST_DONE(); | ||
129 | |||
130 | #if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) | ||
131 | TEST_START("check ECDSA-SK webauthn signature"); | ||
132 | check_sig("ecdsa_sk_webauthn.pub", "ecdsa_sk_webauthn.sig", | ||
133 | msg, namespace); | ||
134 | TEST_DONE(); | ||
135 | #endif | ||
136 | |||
137 | sshbuf_free(msg); | ||
138 | free(namespace); | ||
139 | } | ||
diff --git a/regress/unittests/sshsig/webauthn.html b/regress/unittests/sshsig/webauthn.html new file mode 100644 index 000000000..953041e61 --- /dev/null +++ b/regress/unittests/sshsig/webauthn.html | |||
@@ -0,0 +1,692 @@ | |||
1 | <html> | ||
2 | <head> | ||
3 | <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> | ||
4 | <title>webauthn test</title> | ||
5 | </head> | ||
6 | <body onload="init()"> | ||
7 | <h1>webauthn test</h1> | ||
8 | <p> | ||
9 | This is a demo/test page for generating FIDO keys and signatures in SSH | ||
10 | formats. The page initially displays a form to generate a FIDO key and | ||
11 | convert it to a SSH public key. | ||
12 | </p> | ||
13 | <p> | ||
14 | Once a key has been generated, an additional form will be displayed to | ||
15 | allow signing of data using the just-generated key. The data may be signed | ||
16 | as either a raw SSH signature or wrapped in a sshsig message (the latter is | ||
17 | easier to test using command-line tools. | ||
18 | </p> | ||
19 | <p> | ||
20 | Lots of debugging is printed along the way. | ||
21 | </p> | ||
22 | <h2>Enroll</h2> | ||
23 | <span id="error" style="color: #800; font-weight: bold; font-size: 150%;"></span> | ||
24 | <form id="enrollform"> | ||
25 | <table> | ||
26 | <tr> | ||
27 | <td><b>Username:</b></td> | ||
28 | <td><input id="username" type="text" size="20" name="user" value="test" /></td> | ||
29 | </tr> | ||
30 | <tr><td></td><td><input id="assertsubmit" type="submit" value="submit" /></td></tr> | ||
31 | </table> | ||
32 | </form> | ||
33 | <span id="enrollresult" style="visibility: hidden;"> | ||
34 | <h2>clientData</h2> | ||
35 | <pre id="enrollresultjson" style="color: #008; font-family: monospace;"></pre> | ||
36 | <h2>attestationObject raw</h2> | ||
37 | <pre id="enrollresultraw" style="color: #008; font-family: monospace;"></pre> | ||
38 | <h2>attestationObject</h2> | ||
39 | <pre id="enrollresultattestobj" style="color: #008; font-family: monospace;"></pre> | ||
40 | <h2>authData raw</h2> | ||
41 | <pre id="enrollresultauthdataraw" style="color: #008; font-family: monospace;"></pre> | ||
42 | <h2>authData</h2> | ||
43 | <pre id="enrollresultauthdata" style="color: #008; font-family: monospace;"></pre> | ||
44 | <h2>SSH pubkey blob</h2> | ||
45 | <pre id="enrollresultpkblob" style="color: #008; font-family: monospace;"></pre> | ||
46 | <h2>SSH pubkey string</h2> | ||
47 | <pre id="enrollresultpk" style="color: #008; font-family: monospace;"></pre> | ||
48 | </span> | ||
49 | <span id="assertsection" style="visibility: hidden;"> | ||
50 | <h2>Assert</h2> | ||
51 | <form id="assertform"> | ||
52 | <span id="asserterror" style="color: #800; font-weight: bold;"></span> | ||
53 | <table> | ||
54 | <tr> | ||
55 | <td><b>Data to sign:</b></td> | ||
56 | <td><input id="message" type="text" size="20" name="message" value="test" /></td> | ||
57 | </tr> | ||
58 | <tr> | ||
59 | <td><input id="message_sshsig" type="checkbox" checked /> use sshsig format</td> | ||
60 | </tr> | ||
61 | <tr> | ||
62 | <td><b>Signature namespace:</b></td> | ||
63 | <td><input id="message_namespace" type="text" size="20" name="namespace" value="test" /></td> | ||
64 | </tr> | ||
65 | <tr><td></td><td><input type="submit" value="submit" /></td></tr> | ||
66 | </table> | ||
67 | </form> | ||
68 | </span> | ||
69 | <span id="assertresult" style="visibility: hidden;"> | ||
70 | <h2>clientData</h2> | ||
71 | <pre id="assertresultjson" style="color: #008; font-family: monospace;"></pre> | ||
72 | <h2>signature raw</h2> | ||
73 | <pre id="assertresultsigraw" style="color: #008; font-family: monospace;"></pre> | ||
74 | <h2>authenticatorData raw</h2> | ||
75 | <pre id="assertresultauthdataraw" style="color: #008; font-family: monospace;"></pre> | ||
76 | <h2>authenticatorData</h2> | ||
77 | <pre id="assertresultauthdata" style="color: #008; font-family: monospace;"></pre> | ||
78 | <h2>signature in SSH format</h2> | ||
79 | <pre id="assertresultsshsigraw" style="color: #008; font-family: monospace;"></pre> | ||
80 | <h2>signature in SSH format (base64 encoded)</h2> | ||
81 | <pre id="assertresultsshsigb64" style="color: #008; font-family: monospace;"></pre> | ||
82 | </span> | ||
83 | </body> | ||
84 | <script> | ||
85 | // ------------------------------------------------------------------ | ||
86 | // a crappy CBOR decoder - 20200401 djm@openbsd.org | ||
87 | |||
88 | var CBORDecode = function(buffer) { | ||
89 | this.buf = buffer | ||
90 | this.v = new DataView(buffer) | ||
91 | this.offset = 0 | ||
92 | } | ||
93 | |||
94 | CBORDecode.prototype.empty = function() { | ||
95 | return this.offset >= this.buf.byteLength | ||
96 | } | ||
97 | |||
98 | CBORDecode.prototype.getU8 = function() { | ||
99 | let r = this.v.getUint8(this.offset) | ||
100 | this.offset += 1 | ||
101 | return r | ||
102 | } | ||
103 | |||
104 | CBORDecode.prototype.getU16 = function() { | ||
105 | let r = this.v.getUint16(this.offset) | ||
106 | this.offset += 2 | ||
107 | return r | ||
108 | } | ||
109 | |||
110 | CBORDecode.prototype.getU32 = function() { | ||
111 | let r = this.v.getUint32(this.offset) | ||
112 | this.offset += 4 | ||
113 | return r | ||
114 | } | ||
115 | |||
116 | CBORDecode.prototype.getU64 = function() { | ||
117 | let r = this.v.getUint64(this.offset) | ||
118 | this.offset += 8 | ||
119 | return r | ||
120 | } | ||
121 | |||
122 | CBORDecode.prototype.getCBORTypeLen = function() { | ||
123 | let tl, t, l | ||
124 | tl = this.getU8() | ||
125 | t = (tl & 0xe0) >> 5 | ||
126 | l = tl & 0x1f | ||
127 | return [t, this.decodeInteger(l)] | ||
128 | } | ||
129 | |||
130 | CBORDecode.prototype.decodeInteger = function(len) { | ||
131 | switch (len) { | ||
132 | case 0x18: return this.getU8() | ||
133 | case 0x19: return this.getU16() | ||
134 | case 0x20: return this.getU32() | ||
135 | case 0x21: return this.getU64() | ||
136 | default: | ||
137 | if (len <= 23) { | ||
138 | return len | ||
139 | } | ||
140 | throw new Error("Unsupported int type 0x" + len.toString(16)) | ||
141 | } | ||
142 | } | ||
143 | |||
144 | CBORDecode.prototype.decodeNegint = function(len) { | ||
145 | let r = -(this.decodeInteger(len) + 1) | ||
146 | return r | ||
147 | } | ||
148 | |||
149 | CBORDecode.prototype.decodeByteString = function(len) { | ||
150 | let r = this.buf.slice(this.offset, this.offset + len) | ||
151 | this.offset += len | ||
152 | return r | ||
153 | } | ||
154 | |||
155 | CBORDecode.prototype.decodeTextString = function(len) { | ||
156 | let u8dec = new TextDecoder('utf-8') | ||
157 | r = u8dec.decode(this.decodeByteString(len)) | ||
158 | return r | ||
159 | } | ||
160 | |||
161 | CBORDecode.prototype.decodeArray = function(len, level) { | ||
162 | let r = [] | ||
163 | for (let i = 0; i < len; i++) { | ||
164 | let v = this.decodeInternal(level) | ||
165 | r.push(v) | ||
166 | // console.log("decodeArray level " + level.toString() + " index " + i.toString() + " value " + JSON.stringify(v)) | ||
167 | } | ||
168 | return r | ||
169 | } | ||
170 | |||
171 | CBORDecode.prototype.decodeMap = function(len, level) { | ||
172 | let r = {} | ||
173 | for (let i = 0; i < len; i++) { | ||
174 | let k = this.decodeInternal(level) | ||
175 | let v = this.decodeInternal(level) | ||
176 | r[k] = v | ||
177 | // console.log("decodeMap level " + level.toString() + " key " + k.toString() + " value " + JSON.stringify(v)) | ||
178 | // XXX check string keys, duplicates | ||
179 | } | ||
180 | return r | ||
181 | } | ||
182 | |||
183 | CBORDecode.prototype.decodePrimitive = function(t) { | ||
184 | switch (t) { | ||
185 | case 20: return false | ||
186 | case 21: return true | ||
187 | case 22: return null | ||
188 | case 23: return undefined | ||
189 | default: | ||
190 | throw new Error("Unsupported primitive 0x" + t.toString(2)) | ||
191 | } | ||
192 | } | ||
193 | |||
194 | CBORDecode.prototype.decodeInternal = function(level) { | ||
195 | if (level > 256) { | ||
196 | throw new Error("CBOR nesting too deep") | ||
197 | } | ||
198 | let t, l, r | ||
199 | [t, l] = this.getCBORTypeLen() | ||
200 | // console.log("decode level " + level.toString() + " type " + t.toString() + " len " + l.toString()) | ||
201 | switch (t) { | ||
202 | case 0: | ||
203 | r = this.decodeInteger(l) | ||
204 | break | ||
205 | case 1: | ||
206 | r = this.decodeNegint(l) | ||
207 | break | ||
208 | case 2: | ||
209 | r = this.decodeByteString(l) | ||
210 | break | ||
211 | case 3: | ||
212 | r = this.decodeTextString(l) | ||
213 | break | ||
214 | case 4: | ||
215 | r = this.decodeArray(l, level + 1) | ||
216 | break | ||
217 | case 5: | ||
218 | r = this.decodeMap(l, level + 1) | ||
219 | break | ||
220 | case 6: | ||
221 | console.log("XXX ignored semantic tag " + this.decodeInteger(l).toString()) | ||
222 | break; | ||
223 | case 7: | ||
224 | r = this.decodePrimitive(l) | ||
225 | break | ||
226 | default: | ||
227 | throw new Error("Unsupported type 0x" + t.toString(2) + " len " + l.toString()) | ||
228 | } | ||
229 | // console.log("decode level " + level.toString() + " value " + JSON.stringify(r)) | ||
230 | return r | ||
231 | } | ||
232 | |||
233 | CBORDecode.prototype.decode = function() { | ||
234 | return this.decodeInternal(0) | ||
235 | } | ||
236 | |||
237 | // ------------------------------------------------------------------ | ||
238 | // a crappy SSH message packer - 20200401 djm@openbsd.org | ||
239 | |||
240 | var SSHMSG = function() { | ||
241 | this.r = [] | ||
242 | } | ||
243 | |||
244 | SSHMSG.prototype.serialise = function() { | ||
245 | let len = 0 | ||
246 | for (buf of this.r) { | ||
247 | len += buf.length | ||
248 | } | ||
249 | let r = new ArrayBuffer(len) | ||
250 | let v = new Uint8Array(r) | ||
251 | let offset = 0 | ||
252 | for (buf of this.r) { | ||
253 | v.set(buf, offset) | ||
254 | offset += buf.length | ||
255 | } | ||
256 | if (offset != r.byteLength) { | ||
257 | throw new Error("djm can't count") | ||
258 | } | ||
259 | return r | ||
260 | } | ||
261 | |||
262 | SSHMSG.prototype.serialiseBase64 = function(v) { | ||
263 | let b = this.serialise() | ||
264 | return btoa(String.fromCharCode(...new Uint8Array(b))); | ||
265 | } | ||
266 | |||
267 | SSHMSG.prototype.putU8 = function(v) { | ||
268 | this.r.push(new Uint8Array([v])) | ||
269 | } | ||
270 | |||
271 | SSHMSG.prototype.putU32 = function(v) { | ||
272 | this.r.push(new Uint8Array([ | ||
273 | (v >> 24) & 0xff, | ||
274 | (v >> 16) & 0xff, | ||
275 | (v >> 8) & 0xff, | ||
276 | (v & 0xff) | ||
277 | ])) | ||
278 | } | ||
279 | |||
280 | SSHMSG.prototype.put = function(v) { | ||
281 | this.r.push(new Uint8Array(v)) | ||
282 | } | ||
283 | |||
284 | SSHMSG.prototype.putString = function(v) { | ||
285 | let enc = new TextEncoder(); | ||
286 | let venc = enc.encode(v) | ||
287 | this.putU32(venc.length) | ||
288 | this.put(venc) | ||
289 | } | ||
290 | |||
291 | SSHMSG.prototype.putSSHMSG = function(v) { | ||
292 | let msg = v.serialise() | ||
293 | this.putU32(msg.byteLength) | ||
294 | this.put(msg) | ||
295 | } | ||
296 | |||
297 | SSHMSG.prototype.putBytes = function(v) { | ||
298 | this.putU32(v.byteLength) | ||
299 | this.put(v) | ||
300 | } | ||
301 | |||
302 | SSHMSG.prototype.putECPoint = function(x, y) { | ||
303 | let x8 = new Uint8Array(x) | ||
304 | let y8 = new Uint8Array(y) | ||
305 | this.putU32(1 + x8.length + y8.length) | ||
306 | this.putU8(0x04) // Uncompressed point format. | ||
307 | this.put(x8) | ||
308 | this.put(y8) | ||
309 | } | ||
310 | |||
311 | // ------------------------------------------------------------------ | ||
312 | // webauthn to SSH glue - djm@openbsd.org 20200408 | ||
313 | |||
314 | function error(msg, ...args) { | ||
315 | document.getElementById("error").innerText = msg | ||
316 | console.log(msg) | ||
317 | for (const arg of args) { | ||
318 | console.dir(arg) | ||
319 | } | ||
320 | } | ||
321 | function hexdump(buf) { | ||
322 | const hex = Array.from(new Uint8Array(buf)).map( | ||
323 | b => b.toString(16).padStart(2, "0")) | ||
324 | const fmt = new Array() | ||
325 | for (let i = 0; i < hex.length; i++) { | ||
326 | if ((i % 16) == 0) { | ||
327 | // Prepend length every 16 bytes. | ||
328 | fmt.push(i.toString(16).padStart(4, "0")) | ||
329 | fmt.push(" ") | ||
330 | } | ||
331 | fmt.push(hex[i]) | ||
332 | fmt.push(" ") | ||
333 | if ((i % 16) == 15) { | ||
334 | fmt.push("\n") | ||
335 | } | ||
336 | } | ||
337 | return fmt.join("") | ||
338 | } | ||
339 | function enrollform_submit(event) { | ||
340 | event.preventDefault(); | ||
341 | console.log("submitted") | ||
342 | username = event.target.elements.username.value | ||
343 | if (username === "") { | ||
344 | error("no username specified") | ||
345 | return false | ||
346 | } | ||
347 | enrollStart(username) | ||
348 | } | ||
349 | function enrollStart(username) { | ||
350 | let challenge = new Uint8Array(32) | ||
351 | window.crypto.getRandomValues(challenge) | ||
352 | let userid = new Uint8Array(8) | ||
353 | window.crypto.getRandomValues(userid) | ||
354 | |||
355 | console.log("challenge:" + btoa(challenge)) | ||
356 | console.log("userid:" + btoa(userid)) | ||
357 | |||
358 | let pkopts = { | ||
359 | challenge: challenge, | ||
360 | rp: { | ||
361 | name: "mindrot.org", | ||
362 | id: "mindrot.org", | ||
363 | }, | ||
364 | user: { | ||
365 | id: userid, | ||
366 | name: username, | ||
367 | displayName: username, | ||
368 | }, | ||
369 | authenticatorSelection: { | ||
370 | authenticatorAttachment: "cross-platform", | ||
371 | userVerification: "discouraged", | ||
372 | }, | ||
373 | pubKeyCredParams: [{alg: -7, type: "public-key"}], // ES256 | ||
374 | timeout: 30 * 1000, | ||
375 | }; | ||
376 | console.dir(pkopts) | ||
377 | window.enrollOpts = pkopts | ||
378 | let credpromise = navigator.credentials.create({ publicKey: pkopts }); | ||
379 | credpromise.then(enrollSuccess, enrollFailure) | ||
380 | } | ||
381 | function enrollFailure(result) { | ||
382 | error("Enroll failed", result) | ||
383 | } | ||
384 | function enrollSuccess(result) { | ||
385 | console.log("Enroll succeeded") | ||
386 | console.dir(result) | ||
387 | window.enrollResult = result | ||
388 | document.getElementById("enrollresult").style.visibility = "visible" | ||
389 | |||
390 | // Show the clientData | ||
391 | let u8dec = new TextDecoder('utf-8') | ||
392 | clientData = u8dec.decode(result.response.clientDataJSON) | ||
393 | document.getElementById("enrollresultjson").innerText = clientData | ||
394 | |||
395 | // Decode and show the attestationObject | ||
396 | document.getElementById("enrollresultraw").innerText = hexdump(result.response.attestationObject) | ||
397 | let aod = new CBORDecode(result.response.attestationObject) | ||
398 | let attestationObject = aod.decode() | ||
399 | console.log("attestationObject") | ||
400 | console.dir(attestationObject) | ||
401 | document.getElementById("enrollresultattestobj").innerText = JSON.stringify(attestationObject) | ||
402 | |||
403 | // Decode and show the authData | ||
404 | document.getElementById("enrollresultauthdataraw").innerText = hexdump(attestationObject.authData) | ||
405 | let authData = decodeAuthenticatorData(attestationObject.authData, true) | ||
406 | console.log("authData") | ||
407 | console.dir(authData) | ||
408 | window.enrollAuthData = authData | ||
409 | document.getElementById("enrollresultauthdata").innerText = JSON.stringify(authData) | ||
410 | |||
411 | // Reformat the pubkey as a SSH key for easy verification | ||
412 | window.rawKey = reformatPubkey(authData.attestedCredentialData.credentialPublicKey, window.enrollOpts.rp.id) | ||
413 | console.log("SSH pubkey blob") | ||
414 | console.dir(window.rawKey) | ||
415 | document.getElementById("enrollresultpkblob").innerText = hexdump(window.rawKey) | ||
416 | let pk64 = btoa(String.fromCharCode(...new Uint8Array(window.rawKey))); | ||
417 | let pk = "sk-ecdsa-sha2-nistp256@openssh.com " + pk64 | ||
418 | document.getElementById("enrollresultpk").innerText = pk | ||
419 | |||
420 | // Success: show the assertion form. | ||
421 | document.getElementById("assertsection").style.visibility = "visible" | ||
422 | } | ||
423 | |||
424 | function decodeAuthenticatorData(authData, expectCred) { | ||
425 | let r = new Object() | ||
426 | let v = new DataView(authData) | ||
427 | |||
428 | r.rpIdHash = authData.slice(0, 32) | ||
429 | r.flags = v.getUint8(32) | ||
430 | r.signCount = v.getUint32(33) | ||
431 | |||
432 | // Decode attestedCredentialData if present. | ||
433 | let offset = 37 | ||
434 | let acd = new Object() | ||
435 | if (expectCred) { | ||
436 | acd.aaguid = authData.slice(offset, offset+16) | ||
437 | offset += 16 | ||
438 | let credentialIdLength = v.getUint16(offset) | ||
439 | offset += 2 | ||
440 | acd.credentialIdLength = credentialIdLength | ||
441 | acd.credentialId = authData.slice(offset, offset+credentialIdLength) | ||
442 | offset += credentialIdLength | ||
443 | r.attestedCredentialData = acd | ||
444 | } | ||
445 | console.log("XXXXX " + offset.toString()) | ||
446 | let pubkeyrest = authData.slice(offset, authData.byteLength) | ||
447 | let pkdecode = new CBORDecode(pubkeyrest) | ||
448 | if (expectCred) { | ||
449 | // XXX unsafe: doesn't mandate COSE canonical format. | ||
450 | acd.credentialPublicKey = pkdecode.decode() | ||
451 | } | ||
452 | if (!pkdecode.empty()) { | ||
453 | // Decode extensions if present. | ||
454 | r.extensions = pkdecode.decode() | ||
455 | } | ||
456 | return r | ||
457 | } | ||
458 | |||
459 | function reformatPubkey(pk, rpid) { | ||
460 | // pk is in COSE format. We only care about a tiny subset. | ||
461 | if (pk[1] != 2) { | ||
462 | console.dir(pk) | ||
463 | throw new Error("pubkey is not EC") | ||
464 | } | ||
465 | if (pk[-1] != 1) { | ||
466 | throw new Error("pubkey is not in P256") | ||
467 | } | ||
468 | if (pk[3] != -7) { | ||
469 | throw new Error("pubkey is not ES256") | ||
470 | } | ||
471 | if (pk[-2].byteLength != 32 || pk[-3].byteLength != 32) { | ||
472 | throw new Error("pubkey EC coords have bad length") | ||
473 | } | ||
474 | let msg = new SSHMSG() | ||
475 | msg.putString("sk-ecdsa-sha2-nistp256@openssh.com") // Key type | ||
476 | msg.putString("nistp256") // Key curve | ||
477 | msg.putECPoint(pk[-2], pk[-3]) // EC key | ||
478 | msg.putString(rpid) // RP ID | ||
479 | return msg.serialise() | ||
480 | } | ||
481 | |||
482 | async function assertform_submit(event) { | ||
483 | event.preventDefault(); | ||
484 | console.log("submitted") | ||
485 | message = event.target.elements.message.value | ||
486 | if (message === "") { | ||
487 | error("no message specified") | ||
488 | return false | ||
489 | } | ||
490 | let enc = new TextEncoder() | ||
491 | let encmsg = enc.encode(message) | ||
492 | window.assertSignRaw = !event.target.elements.message_sshsig.checked | ||
493 | console.log("using sshsig ", !window.assertSignRaw) | ||
494 | if (window.assertSignRaw) { | ||
495 | assertStart(encmsg) | ||
496 | return | ||
497 | } | ||
498 | // Format a sshsig-style message. | ||
499 | window.sigHashAlg = "sha512" | ||
500 | let msghash = await crypto.subtle.digest("SHA-512", encmsg); | ||
501 | console.log("raw message hash") | ||
502 | console.dir(msghash) | ||
503 | window.sigNamespace = event.target.elements.message_namespace.value | ||
504 | let sigbuf = new SSHMSG() | ||
505 | sigbuf.put(enc.encode("SSHSIG")) | ||
506 | sigbuf.putString(window.sigNamespace) | ||
507 | sigbuf.putU32(0) // Reserved string | ||
508 | sigbuf.putString(window.sigHashAlg) | ||
509 | sigbuf.putBytes(msghash) | ||
510 | let msg = sigbuf.serialise() | ||
511 | console.log("sigbuf") | ||
512 | console.dir(msg) | ||
513 | assertStart(msg) | ||
514 | } | ||
515 | |||
516 | function assertStart(message) { | ||
517 | let assertReqOpts = { | ||
518 | challenge: message, | ||
519 | rpId: "mindrot.org", | ||
520 | allowCredentials: [{ | ||
521 | type: 'public-key', | ||
522 | id: window.enrollResult.rawId, | ||
523 | }], | ||
524 | userVerification: "discouraged", | ||
525 | timeout: (30 * 1000), | ||
526 | } | ||
527 | console.log("assertReqOpts") | ||
528 | console.dir(assertReqOpts) | ||
529 | window.assertReqOpts = assertReqOpts | ||
530 | let assertpromise = navigator.credentials.get({ | ||
531 | publicKey: assertReqOpts | ||
532 | }); | ||
533 | assertpromise.then(assertSuccess, assertFailure) | ||
534 | } | ||
535 | function assertFailure(result) { | ||
536 | error("Assertion failed", result) | ||
537 | } | ||
538 | function linewrap(s) { | ||
539 | const linelen = 70 | ||
540 | let ret = "" | ||
541 | for (let i = 0; i < s.length; i += linelen) { | ||
542 | end = i + linelen | ||
543 | if (end > s.length) { | ||
544 | end = s.length | ||
545 | } | ||
546 | if (i > 0) { | ||
547 | ret += "\n" | ||
548 | } | ||
549 | ret += s.slice(i, end) | ||
550 | } | ||
551 | return ret + "\n" | ||
552 | } | ||
553 | function assertSuccess(result) { | ||
554 | console.log("Assertion succeeded") | ||
555 | console.dir(result) | ||
556 | window.assertResult = result | ||
557 | document.getElementById("assertresult").style.visibility = "visible" | ||
558 | |||
559 | // show the clientData. | ||
560 | let u8dec = new TextDecoder('utf-8') | ||
561 | clientData = u8dec.decode(result.response.clientDataJSON) | ||
562 | document.getElementById("assertresultjson").innerText = clientData | ||
563 | |||
564 | // show the signature. | ||
565 | document.getElementById("assertresultsigraw").innerText = hexdump(result.response.signature) | ||
566 | |||
567 | // decode and show the authData. | ||
568 | document.getElementById("assertresultauthdataraw").innerText = hexdump(result.response.authenticatorData) | ||
569 | authData = decodeAuthenticatorData(result.response.authenticatorData, false) | ||
570 | document.getElementById("assertresultauthdata").innerText = JSON.stringify(authData) | ||
571 | |||
572 | // Parse and reformat the signature to an SSH style signature. | ||
573 | let sshsig = reformatSignature(result.response.signature, clientData, authData) | ||
574 | document.getElementById("assertresultsshsigraw").innerText = hexdump(sshsig) | ||
575 | let sig64 = btoa(String.fromCharCode(...new Uint8Array(sshsig))); | ||
576 | if (window.assertSignRaw) { | ||
577 | document.getElementById("assertresultsshsigb64").innerText = sig64 | ||
578 | } else { | ||
579 | document.getElementById("assertresultsshsigb64").innerText = | ||
580 | "-----BEGIN SSH SIGNATURE-----\n" + linewrap(sig64) + | ||
581 | "-----END SSH SIGNATURE-----\n"; | ||
582 | } | ||
583 | } | ||
584 | |||
585 | function reformatSignature(sig, clientData, authData) { | ||
586 | if (sig.byteLength < 2) { | ||
587 | throw new Error("signature is too short") | ||
588 | } | ||
589 | let offset = 0 | ||
590 | let v = new DataView(sig) | ||
591 | // Expect an ASN.1 SEQUENCE that exactly spans the signature. | ||
592 | if (v.getUint8(offset) != 0x30) { | ||
593 | throw new Error("signature not an ASN.1 sequence") | ||
594 | } | ||
595 | offset++ | ||
596 | let seqlen = v.getUint8(offset) | ||
597 | offset++ | ||
598 | if ((seqlen & 0x80) != 0 || seqlen != sig.byteLength - offset) { | ||
599 | throw new Error("signature has unexpected length " + seqlen.toString() + " vs expected " + (sig.byteLength - offset).toString()) | ||
600 | } | ||
601 | |||
602 | // Parse 'r' INTEGER value. | ||
603 | if (v.getUint8(offset) != 0x02) { | ||
604 | throw new Error("signature r not an ASN.1 integer") | ||
605 | } | ||
606 | offset++ | ||
607 | let rlen = v.getUint8(offset) | ||
608 | offset++ | ||
609 | if ((rlen & 0x80) != 0 || rlen > sig.byteLength - offset) { | ||
610 | throw new Error("signature r has unexpected length " + rlen.toString() + " vs buffer " + (sig.byteLength - offset).toString()) | ||
611 | } | ||
612 | let r = sig.slice(offset, offset + rlen) | ||
613 | offset += rlen | ||
614 | console.log("sig_r") | ||
615 | console.dir(r) | ||
616 | |||
617 | // Parse 's' INTEGER value. | ||
618 | if (v.getUint8(offset) != 0x02) { | ||
619 | throw new Error("signature r not an ASN.1 integer") | ||
620 | } | ||
621 | offset++ | ||
622 | let slen = v.getUint8(offset) | ||
623 | offset++ | ||
624 | if ((slen & 0x80) != 0 || slen > sig.byteLength - offset) { | ||
625 | throw new Error("signature s has unexpected length " + slen.toString() + " vs buffer " + (sig.byteLength - offset).toString()) | ||
626 | } | ||
627 | let s = sig.slice(offset, offset + slen) | ||
628 | console.log("sig_s") | ||
629 | console.dir(s) | ||
630 | offset += slen | ||
631 | |||
632 | if (offset != sig.byteLength) { | ||
633 | throw new Error("unexpected final offset during signature parsing " + offset.toString() + " expected " + sig.byteLength.toString()) | ||
634 | } | ||
635 | |||
636 | // Reformat as an SSH signature. | ||
637 | let clientDataParsed = JSON.parse(clientData) | ||
638 | let innersig = new SSHMSG() | ||
639 | innersig.putBytes(r) | ||
640 | innersig.putBytes(s) | ||
641 | |||
642 | let rawsshsig = new SSHMSG() | ||
643 | rawsshsig.putString("webauthn-sk-ecdsa-sha2-nistp256@openssh.com") | ||
644 | rawsshsig.putSSHMSG(innersig) | ||
645 | rawsshsig.putU8(authData.flags) | ||
646 | rawsshsig.putU32(authData.signCount) | ||
647 | rawsshsig.putString(clientDataParsed.origin) | ||
648 | rawsshsig.putString(clientData) | ||
649 | if (authData.extensions == undefined) { | ||
650 | rawsshsig.putU32(0) | ||
651 | } else { | ||
652 | rawsshsig.putBytes(authData.extensions) | ||
653 | } | ||
654 | |||
655 | if (window.assertSignRaw) { | ||
656 | return rawsshsig.serialise() | ||
657 | } | ||
658 | // Format as SSHSIG. | ||
659 | let enc = new TextEncoder() | ||
660 | let sshsig = new SSHMSG() | ||
661 | sshsig.put(enc.encode("SSHSIG")) | ||
662 | sshsig.putU32(0x01) // Signature version. | ||
663 | sshsig.putBytes(window.rawKey) | ||
664 | sshsig.putString(window.sigNamespace) | ||
665 | sshsig.putU32(0) // Reserved string | ||
666 | sshsig.putString(window.sigHashAlg) | ||
667 | sshsig.putBytes(rawsshsig.serialise()) | ||
668 | return sshsig.serialise() | ||
669 | } | ||
670 | |||
671 | function toggleNamespaceVisibility() { | ||
672 | const assertsigtype = document.getElementById('message_sshsig'); | ||
673 | const assertsignamespace = document.getElementById('message_namespace'); | ||
674 | assertsignamespace.disabled = !assertsigtype.checked; | ||
675 | } | ||
676 | |||
677 | function init() { | ||
678 | if (document.location.protocol != "https:") { | ||
679 | error("This page must be loaded via https") | ||
680 | const assertsubmit = document.getElementById('assertsubmit') | ||
681 | assertsubmit.disabled = true | ||
682 | } | ||
683 | const enrollform = document.getElementById('enrollform'); | ||
684 | enrollform.addEventListener('submit', enrollform_submit); | ||
685 | const assertform = document.getElementById('assertform'); | ||
686 | assertform.addEventListener('submit', assertform_submit); | ||
687 | const assertsigtype = document.getElementById('message_sshsig'); | ||
688 | assertsigtype.onclick = toggleNamespaceVisibility; | ||
689 | } | ||
690 | </script> | ||
691 | |||
692 | </html> | ||