diff options
-rw-r--r-- | .github/workflows/windows.yml | 14 | ||||
-rw-r--r-- | .gitignore | 6 | ||||
-rw-r--r-- | .travis.yml | 86 | ||||
-rw-r--r-- | .travis/build-linux-clang | 22 | ||||
-rw-r--r-- | .travis/build-linux-gcc | 19 | ||||
-rw-r--r-- | .travis/build-linux-mingw | 47 | ||||
-rw-r--r-- | .travis/build-osx-clang | 24 | ||||
-rw-r--r-- | .travis/fuzz-linux-asan | 58 | ||||
-rw-r--r-- | .travis/fuzz-linux-msan | 58 | ||||
-rw-r--r-- | CMakeLists.txt | 4 | ||||
-rw-r--r-- | NEWS | 4 | ||||
-rw-r--r-- | README.adoc | 17 | ||||
-rw-r--r-- | debian/changelog | 14 | ||||
-rw-r--r-- | fuzz/report.html.gz | bin | 0 -> 206440 bytes | |||
-rw-r--r-- | openbsd-compat/openbsd-compat.h | 2 | ||||
-rw-r--r-- | src/iso7816.c | 2 | ||||
-rw-r--r-- | src/u2f.c | 2 | ||||
-rw-r--r-- | tools/CMakeLists.txt | 7 | ||||
-rw-r--r-- | tools/sk-libfido2.c | 784 | ||||
-rw-r--r-- | udev/70-u2f.rules | 7 |
20 files changed, 359 insertions, 818 deletions
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml new file mode 100644 index 0000000..eb953db --- /dev/null +++ b/.github/workflows/windows.yml | |||
@@ -0,0 +1,14 @@ | |||
1 | name: windows | ||
2 | |||
3 | on: [push] | ||
4 | |||
5 | jobs: | ||
6 | build: | ||
7 | |||
8 | runs-on: windows-latest | ||
9 | |||
10 | steps: | ||
11 | - uses: actions/checkout@v1 | ||
12 | - name: build | ||
13 | run: .\windows\build.ps1 | ||
14 | |||
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..329c184 --- /dev/null +++ b/.gitignore | |||
@@ -0,0 +1,6 @@ | |||
1 | build/ | ||
2 | cscope.out | ||
3 | fuzz/build/ | ||
4 | fuzz/obj/ | ||
5 | fuzz/*.so | ||
6 | output/ | ||
diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..5ddf36f --- /dev/null +++ b/.travis.yml | |||
@@ -0,0 +1,86 @@ | |||
1 | language: c | ||
2 | |||
3 | matrix: | ||
4 | include: | ||
5 | - os: linux | ||
6 | compiler: clang-7 | ||
7 | dist: xenial | ||
8 | sudo: required | ||
9 | addons: | ||
10 | apt: | ||
11 | sources: | ||
12 | - ubuntu-toolchain-r-test | ||
13 | - llvm-toolchain-xenial-7 | ||
14 | packages: | ||
15 | - clang-7 | ||
16 | - cmake | ||
17 | - libssl-dev | ||
18 | - libudev-dev | ||
19 | script: /bin/sh -eux .travis/build-linux-clang | ||
20 | - os: linux | ||
21 | compiler: gcc-7 | ||
22 | dist: xenial | ||
23 | sudo: required | ||
24 | addons: | ||
25 | apt: | ||
26 | sources: | ||
27 | - ubuntu-toolchain-r-test | ||
28 | packages: | ||
29 | - gcc-7 | ||
30 | - cmake | ||
31 | - libssl-dev | ||
32 | - libudev-dev | ||
33 | script: /bin/sh -eux .travis/build-linux-gcc | ||
34 | - os: linux | ||
35 | compiler: i686-w64-mingw32-gcc-4.8 | ||
36 | dist: xenial | ||
37 | sudo: required | ||
38 | addons: | ||
39 | apt: | ||
40 | sources: | ||
41 | - ubuntu-toolchain-r-test | ||
42 | packages: | ||
43 | - binutils-mingw-w64-i686 | ||
44 | - gcc-mingw-w64 | ||
45 | - g++-mingw-w64 | ||
46 | - mingw-w64-i686-dev | ||
47 | - cmake | ||
48 | script: /bin/sh -eux .travis/build-linux-mingw | ||
49 | - os: osx | ||
50 | osx_image: xcode10.2 | ||
51 | compiler: clang | ||
52 | sudo: required | ||
53 | script: /bin/sh -eux .travis/build-osx-clang | ||
54 | - os: linux | ||
55 | compiler: clang-7 | ||
56 | dist: xenial | ||
57 | sudo: required | ||
58 | addons: | ||
59 | apt: | ||
60 | sources: | ||
61 | - ubuntu-toolchain-r-test | ||
62 | - llvm-toolchain-xenial-7 | ||
63 | packages: | ||
64 | - clang-7 | ||
65 | - cmake | ||
66 | - libssl-dev | ||
67 | - libudev-dev | ||
68 | script: /bin/sh -eux .travis/fuzz-linux-asan | ||
69 | - os: linux | ||
70 | compiler: clang-7 | ||
71 | dist: xenial | ||
72 | sudo: required | ||
73 | addons: | ||
74 | apt: | ||
75 | sources: | ||
76 | - ubuntu-toolchain-r-test | ||
77 | - llvm-toolchain-xenial-7 | ||
78 | packages: | ||
79 | - clang-7 | ||
80 | - cmake | ||
81 | - libssl-dev | ||
82 | - libudev-dev | ||
83 | script: /bin/sh -eux .travis/fuzz-linux-msan | ||
84 | |||
85 | notifications: | ||
86 | email: false | ||
diff --git a/.travis/build-linux-clang b/.travis/build-linux-clang new file mode 100644 index 0000000..8938461 --- /dev/null +++ b/.travis/build-linux-clang | |||
@@ -0,0 +1,22 @@ | |||
1 | #!/bin/sh -eux | ||
2 | |||
3 | ${CC} --version | ||
4 | |||
5 | # Check exports. | ||
6 | (cd src && ./diff_exports.sh) | ||
7 | |||
8 | # Build and install libcbor. | ||
9 | git clone git://github.com/pjk/libcbor | ||
10 | cd libcbor | ||
11 | git checkout v0.5.0 | ||
12 | mkdir build | ||
13 | (cd build && cmake ..) | ||
14 | make -C build | ||
15 | sudo make -C build install | ||
16 | cd .. | ||
17 | |||
18 | # Build, analyze, and install libfido2. | ||
19 | mkdir build | ||
20 | (cd build && scan-build cmake -DCMAKE_BUILD_TYPE=Debug ..) | ||
21 | scan-build --status-bugs make -C build | ||
22 | sudo make -C build install | ||
diff --git a/.travis/build-linux-gcc b/.travis/build-linux-gcc new file mode 100644 index 0000000..be1e0a9 --- /dev/null +++ b/.travis/build-linux-gcc | |||
@@ -0,0 +1,19 @@ | |||
1 | #!/bin/sh -eux | ||
2 | |||
3 | ${CC} --version | ||
4 | |||
5 | # Build and install libcbor. | ||
6 | git clone git://github.com/pjk/libcbor | ||
7 | cd libcbor | ||
8 | git checkout v0.5.0 | ||
9 | mkdir build | ||
10 | (cd build && cmake ..) | ||
11 | make -C build | ||
12 | sudo make -C build install | ||
13 | cd .. | ||
14 | |||
15 | # Build and install libfido2. | ||
16 | mkdir build | ||
17 | (cd build && cmake -DCMAKE_BUILD_TYPE=Debug ..) | ||
18 | make -C build | ||
19 | sudo make -C build install | ||
diff --git a/.travis/build-linux-mingw b/.travis/build-linux-mingw new file mode 100644 index 0000000..c88ddca --- /dev/null +++ b/.travis/build-linux-mingw | |||
@@ -0,0 +1,47 @@ | |||
1 | #!/bin/sh -eux | ||
2 | |||
3 | # XXX defining CC and cross-compiling confuses OpenSSL's build. | ||
4 | unset CC | ||
5 | |||
6 | sudo mkdir /fakeroot | ||
7 | sudo chmod 755 /fakeroot | ||
8 | |||
9 | cat << EOF > /tmp/mingw.cmake | ||
10 | SET(CMAKE_SYSTEM_NAME Windows) | ||
11 | SET(CMAKE_C_COMPILER i686-w64-mingw32-gcc) | ||
12 | SET(CMAKE_CXX_COMPILER i686-w64-mingw32-g++) | ||
13 | SET(CMAKE_RC_COMPILER i686-w64-mingw32-windres) | ||
14 | SET(CMAKE_FIND_ROOT_PATH /fakeroot) | ||
15 | SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) | ||
16 | SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) | ||
17 | SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) | ||
18 | EOF | ||
19 | |||
20 | # Build and install libcbor. | ||
21 | git clone git://github.com/pjk/libcbor | ||
22 | cd libcbor | ||
23 | git checkout v0.5.0 | ||
24 | mkdir build | ||
25 | (cd build && cmake -DCMAKE_TOOLCHAIN_FILE=/tmp/mingw.cmake \ | ||
26 | -DCMAKE_INSTALL_PREFIX=/fakeroot ..) | ||
27 | make -C build | ||
28 | sudo make -C build install | ||
29 | cd .. | ||
30 | |||
31 | # Build and install OpenSSL 1.1.0j. | ||
32 | git clone git://github.com/openssl/openssl | ||
33 | cd openssl | ||
34 | git checkout OpenSSL_1_1_0j | ||
35 | ./Configure mingw --prefix=/fakeroot --openssldir=/fakeroot/openssl \ | ||
36 | --cross-compile-prefix=i686-w64-mingw32- | ||
37 | make | ||
38 | sudo make install_sw | ||
39 | cd .. | ||
40 | |||
41 | # Build and install libfido2. | ||
42 | export PKG_CONFIG_PATH=/fakeroot/lib/pkgconfig | ||
43 | mkdir build | ||
44 | (cd build && cmake -DCMAKE_TOOLCHAIN_FILE=/tmp/mingw.cmake \ | ||
45 | -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=/fakeroot ..) | ||
46 | make -C build | ||
47 | sudo make -C build install | ||
diff --git a/.travis/build-osx-clang b/.travis/build-osx-clang new file mode 100644 index 0000000..69a784c --- /dev/null +++ b/.travis/build-osx-clang | |||
@@ -0,0 +1,24 @@ | |||
1 | #!/bin/sh -eux | ||
2 | |||
3 | ${CC} --version | ||
4 | |||
5 | # Build and install libcbor. | ||
6 | git clone git://github.com/pjk/libcbor | ||
7 | cd libcbor | ||
8 | git checkout v0.5.0 | ||
9 | mkdir build | ||
10 | (cd build && cmake ..) | ||
11 | make -C build | ||
12 | sudo make -C build install | ||
13 | cd .. | ||
14 | |||
15 | # Install mandoc from Homebrew. | ||
16 | brew install mandoc | ||
17 | |||
18 | # Build and install libfido2. | ||
19 | export PKG_CONFIG_PATH=/usr/local/opt/openssl@1.1/lib/pkgconfig | ||
20 | mkdir build | ||
21 | (cd build && cmake -DCMAKE_BUILD_TYPE=Debug ..) | ||
22 | make -C build | ||
23 | make -C build man_symlink_html | ||
24 | sudo make -C build install | ||
diff --git a/.travis/fuzz-linux-asan b/.travis/fuzz-linux-asan new file mode 100644 index 0000000..0a0aebb --- /dev/null +++ b/.travis/fuzz-linux-asan | |||
@@ -0,0 +1,58 @@ | |||
1 | #!/bin/sh -eux | ||
2 | |||
3 | ${CC} --version | ||
4 | |||
5 | FAKEROOT=/fakeroot | ||
6 | sudo mkdir ${FAKEROOT} | ||
7 | sudo chmod 755 ${FAKEROOT} | ||
8 | |||
9 | # Build and install libcbor. | ||
10 | git clone git://github.com/pjk/libcbor | ||
11 | cd libcbor | ||
12 | patch -p0 < ../fuzz/README | ||
13 | mkdir build | ||
14 | cd build | ||
15 | cmake -DCMAKE_C_FLAGS_DEBUG="-g2 -fno-omit-frame-pointer" \ | ||
16 | -DCMAKE_C_COMPILER=clang -DCMAKE_BUILD_TYPE=Debug \ | ||
17 | -DCMAKE_INSTALL_PREFIX=${FAKEROOT} -DSANITIZE=ON \ | ||
18 | -DCMAKE_INSTALL_LIBDIR=lib .. | ||
19 | make | ||
20 | sudo make install | ||
21 | cd ../.. | ||
22 | |||
23 | # Build and install OpenSSL 1.1.1b. | ||
24 | git clone git://github.com/openssl/openssl | ||
25 | cd openssl | ||
26 | git checkout OpenSSL_1_1_1b | ||
27 | ./Configure linux-x86_64-clang enable-asan --prefix=${FAKEROOT} \ | ||
28 | --openssldir=${FAKEROOT}/openssl | ||
29 | make clean | ||
30 | make | ||
31 | sudo make install_sw | ||
32 | cd .. | ||
33 | |||
34 | # Build libfido2. | ||
35 | mkdir build | ||
36 | cd build | ||
37 | export PKG_CONFIG_PATH=/fakeroot/lib/pkgconfig | ||
38 | cmake -DFUZZ=1 -DLIBFUZZER=1 -DASAN=1 -DUBSAN=1 -DCMAKE_C_COMPILER=clang \ | ||
39 | -DCRYPTO_INCLUDE_DIRS=${FAKEROOT}/include \ | ||
40 | -DCRYPTO_LIBRARY_DIRS=${FAKEROOT}/lib \ | ||
41 | -DCBOR_INCLUDE_DIRS=${FAKEROOT}/include \ | ||
42 | -DCBOR_LIBRARY_DIRS=${FAKEROOT}/lib \ | ||
43 | -DCMAKE_BUILD_TYPE=Debug .. | ||
44 | make | ||
45 | |||
46 | # Fuzz with ASAN. | ||
47 | mkdir corpus | ||
48 | tar -C corpus -zxf ../fuzz/corpus.tgz | ||
49 | fuzz/fuzz_cred -use_value_profile=1 -reload=30 -print_pcs=1 \ | ||
50 | -print_funcs=30 -timeout=10 -max_len=17408 -runs=1 corpus/fuzz_cred | ||
51 | fuzz/fuzz_assert -use_value_profile=1 -reload=30 -print_pcs=1 \ | ||
52 | -print_funcs=30 -timeout=10 -max_len=17408 -runs=1 corpus/fuzz_assert | ||
53 | fuzz/fuzz_credman -use_value_profile=1 -reload=30 -print_pcs=1 \ | ||
54 | -print_funcs=30 -timeout=10 -max_len=17408 -runs=1 corpus/fuzz_credman | ||
55 | fuzz/fuzz_mgmt -use_value_profile=1 -reload=30 -print_pcs=1 \ | ||
56 | -print_funcs=30 -timeout=10 -max_len=17408 -runs=1 corpus/fuzz_mgmt | ||
57 | fuzz/fuzz_bio -use_value_profile=1 -reload=30 -print_pcs=1 \ | ||
58 | -print_funcs=30 -timeout=10 -max_len=17408 -runs=1 corpus/fuzz_bio | ||
diff --git a/.travis/fuzz-linux-msan b/.travis/fuzz-linux-msan new file mode 100644 index 0000000..fd7b2ac --- /dev/null +++ b/.travis/fuzz-linux-msan | |||
@@ -0,0 +1,58 @@ | |||
1 | #!/bin/sh -eux | ||
2 | |||
3 | ${CC} --version | ||
4 | |||
5 | FAKEROOT=/fakeroot | ||
6 | sudo mkdir ${FAKEROOT} | ||
7 | sudo chmod 755 ${FAKEROOT} | ||
8 | |||
9 | # Build and install libcbor. | ||
10 | git clone git://github.com/pjk/libcbor | ||
11 | cd libcbor | ||
12 | patch -p0 < ../fuzz/README | ||
13 | mkdir build | ||
14 | cd build | ||
15 | cmake -DCMAKE_C_FLAGS_DEBUG="-fsanitize=memory,undefined -g2 -fno-omit-frame-pointer" \ | ||
16 | -DCMAKE_C_COMPILER=clang -DCMAKE_BUILD_TYPE=Debug \ | ||
17 | -DCMAKE_INSTALL_PREFIX=${FAKEROOT} -DSANITIZE=OFF \ | ||
18 | -DCMAKE_INSTALL_LIBDIR=lib .. | ||
19 | make | ||
20 | sudo make install | ||
21 | cd ../.. | ||
22 | |||
23 | # Build and install OpenSSL 1.1.1b. | ||
24 | git clone git://github.com/openssl/openssl | ||
25 | cd openssl | ||
26 | git checkout OpenSSL_1_1_1b | ||
27 | ./Configure linux-x86_64-clang enable-msan --prefix=${FAKEROOT} \ | ||
28 | --openssldir=${FAKEROOT}/openssl | ||
29 | make clean | ||
30 | make | ||
31 | sudo make install_sw | ||
32 | cd .. | ||
33 | |||
34 | # Build libfido2. | ||
35 | mkdir build | ||
36 | cd build | ||
37 | export PKG_CONFIG_PATH=/fakeroot/lib/pkgconfig | ||
38 | cmake -DFUZZ=1 -DLIBFUZZER=1 -DMSAN=1 -DUBSAN=1 -DCMAKE_C_COMPILER=clang \ | ||
39 | -DCRYPTO_INCLUDE_DIRS=${FAKEROOT}/include \ | ||
40 | -DCRYPTO_LIBRARY_DIRS=${FAKEROOT}/lib \ | ||
41 | -DCBOR_INCLUDE_DIRS=${FAKEROOT}/include \ | ||
42 | -DCBOR_LIBRARY_DIRS=${FAKEROOT}/lib \ | ||
43 | -DCMAKE_BUILD_TYPE=Debug .. | ||
44 | make | ||
45 | |||
46 | # Fuzz with MSAN. | ||
47 | mkdir corpus | ||
48 | tar -C corpus -zxf ../fuzz/corpus.tgz | ||
49 | fuzz/fuzz_cred -use_value_profile=1 -reload=30 -print_pcs=1 \ | ||
50 | -print_funcs=30 -timeout=10 -max_len=17408 -runs=1 corpus/fuzz_cred | ||
51 | fuzz/fuzz_assert -use_value_profile=1 -reload=30 -print_pcs=1 \ | ||
52 | -print_funcs=30 -timeout=10 -max_len=17408 -runs=1 corpus/fuzz_assert | ||
53 | fuzz/fuzz_credman -use_value_profile=1 -reload=30 -print_pcs=1 \ | ||
54 | -print_funcs=30 -timeout=10 -max_len=17408 -runs=1 corpus/fuzz_credman | ||
55 | fuzz/fuzz_mgmt -use_value_profile=1 -reload=30 -print_pcs=1 \ | ||
56 | -print_funcs=30 -timeout=10 -max_len=17408 -runs=1 corpus/fuzz_mgmt | ||
57 | fuzz/fuzz_bio -use_value_profile=1 -reload=30 -print_pcs=1 \ | ||
58 | -print_funcs=30 -timeout=10 -max_len=17408 -runs=1 corpus/fuzz_bio | ||
diff --git a/CMakeLists.txt b/CMakeLists.txt index c7c5991..9481c46 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
@@ -20,7 +20,7 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON) | |||
20 | 20 | ||
21 | set(FIDO_MAJOR "1") | 21 | set(FIDO_MAJOR "1") |
22 | set(FIDO_MINOR "3") | 22 | set(FIDO_MINOR "3") |
23 | set(FIDO_PATCH "0") | 23 | set(FIDO_PATCH "1") |
24 | set(FIDO_VERSION ${FIDO_MAJOR}.${FIDO_MINOR}.${FIDO_PATCH}) | 24 | set(FIDO_VERSION ${FIDO_MAJOR}.${FIDO_MINOR}.${FIDO_PATCH}) |
25 | 25 | ||
26 | add_definitions(-D_FIDO_MAJOR=${FIDO_MAJOR}) | 26 | add_definitions(-D_FIDO_MAJOR=${FIDO_MAJOR}) |
@@ -322,7 +322,7 @@ elseif(NOT MSVC) | |||
322 | endif() | 322 | endif() |
323 | else() | 323 | else() |
324 | string(CONCAT CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} | 324 | string(CONCAT CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} |
325 | " /def:${CMAKE_CURRENT_SOURCE_DIR}/src/export.msvc") | 325 | " /def:\"${CMAKE_CURRENT_SOURCE_DIR}/src/export.msvc\"") |
326 | endif() | 326 | endif() |
327 | 327 | ||
328 | include_directories(${CMAKE_SOURCE_DIR}/src) | 328 | include_directories(${CMAKE_SOURCE_DIR}/src) |
@@ -1,3 +1,7 @@ | |||
1 | * Version 1.3.1 (2020-02-19) | ||
2 | ** fix zero-ing of le1 and le2 when talking to a U2F device. | ||
3 | ** dropping sk-libfido2 middleware, please find it in the openssh tree. | ||
4 | |||
1 | * Version 1.3.0 (2019-11-28) | 5 | * Version 1.3.0 (2019-11-28) |
2 | ** assert/hmac: encode public key as per spec, gh#60. | 6 | ** assert/hmac: encode public key as per spec, gh#60. |
3 | ** fido2-cred: fix creation of resident keys. | 7 | ** fido2-cred: fix creation of resident keys. |
diff --git a/README.adoc b/README.adoc index 8693417..ce8ac1e 100644 --- a/README.adoc +++ b/README.adoc | |||
@@ -31,7 +31,7 @@ is also available. | |||
31 | 31 | ||
32 | ==== Releases | 32 | ==== Releases |
33 | 33 | ||
34 | The current release of *libfido2* is 1.3.0. Please consult Yubico's | 34 | The current release of *libfido2* is 1.3.1. Please consult Yubico's |
35 | https://developers.yubico.com/libfido2/Releases[release page] for source | 35 | https://developers.yubico.com/libfido2/Releases[release page] for source |
36 | and binary releases. | 36 | and binary releases. |
37 | 37 | ||
@@ -71,18 +71,3 @@ KERNEL=="hidraw*", SUBSYSTEM=="hidraw", \ | |||
71 | On Windows 1903 and newer versions, access to FIDO devices has been restricted | 71 | On Windows 1903 and newer versions, access to FIDO devices has been restricted |
72 | to applications using the operating system's native API. Use of *libfido2* | 72 | to applications using the operating system's native API. Use of *libfido2* |
73 | is still possible in privileged applications. | 73 | is still possible in privileged applications. |
74 | |||
75 | === OpenSSH Integration | ||
76 | |||
77 | *libfido2* includes middleware allowing https://www.openssh.com[OpenSSH] to | ||
78 | talk to U2F/FIDO2 devices. Note that server support is required for | ||
79 | authentication. In a nutshell: | ||
80 | |||
81 | ==== Key Generation | ||
82 | |||
83 | $ ssh-keygen -t [ecdsa-sk|ed25519-sk] -w /path/to/libsk-libfido2.so | ||
84 | |||
85 | ==== Authentication | ||
86 | |||
87 | $ ssh-agent -P /path/to/libsk-libfido2.so | ||
88 | $ ssh-add -S /path/to/libsk-libfido2.so | ||
diff --git a/debian/changelog b/debian/changelog index 3cf1a4d..af5ccc1 100644 --- a/debian/changelog +++ b/debian/changelog | |||
@@ -1,14 +1,18 @@ | |||
1 | libfido2 (1.3.0-2) UNRELEASED; urgency=medium | 1 | libfido2 (1.3.1-0.1) UNRELEASED; urgency=medium |
2 | |||
3 | [ Colin Watson ] | ||
4 | * Fix pattern in debian/watch. | ||
5 | 2 | ||
6 | [ nicoo ] | 3 | [ nicoo ] |
4 | * New upstream version 1.3.1 (released 2020-02-19) | ||
5 | Correctly zero part of the APDU when communicating with U2F devices | ||
6 | |||
7 | * Fix non-determinism in the build | 7 | * Fix non-determinism in the build |
8 | Set CMAKE_BUILD_RPATH_USE_ORIGIN, require cmake >= 3.14 | 8 | Set CMAKE_BUILD_RPATH_USE_ORIGIN, require cmake >= 3.14 |
9 | * Add CI, based on salsa-ci-team's pipeline | 9 | * Add CI, based on salsa-ci-team's pipeline |
10 | 10 | ||
11 | -- nicoo <nicoo@debian.org> Sat, 15 Feb 2020 17:47:54 +0100 | 11 | [ Colin Watson ] |
12 | * Fix pattern in debian/watch. | ||
13 | * Only use -fstack-protector-all if it is supported | ||
14 | |||
15 | -- nicoo <nicoo@debian.org> Sun, 23 Feb 2020 15:33:59 +0100 | ||
12 | 16 | ||
13 | libfido2 (1.3.0-1) unstable; urgency=low | 17 | libfido2 (1.3.0-1) unstable; urgency=low |
14 | 18 | ||
diff --git a/fuzz/report.html.gz b/fuzz/report.html.gz new file mode 100644 index 0000000..9f07223 --- /dev/null +++ b/fuzz/report.html.gz | |||
Binary files differ | |||
diff --git a/openbsd-compat/openbsd-compat.h b/openbsd-compat/openbsd-compat.h index d1d8652..bee126c 100644 --- a/openbsd-compat/openbsd-compat.h +++ b/openbsd-compat/openbsd-compat.h | |||
@@ -18,6 +18,7 @@ | |||
18 | #if defined(__APPLE__) && !defined(HAVE_ENDIAN_H) | 18 | #if defined(__APPLE__) && !defined(HAVE_ENDIAN_H) |
19 | #include <libkern/OSByteOrder.h> | 19 | #include <libkern/OSByteOrder.h> |
20 | #define be16toh(x) OSSwapBigToHostInt16((x)) | 20 | #define be16toh(x) OSSwapBigToHostInt16((x)) |
21 | #define htobe16(x) OSSwapHostToBigInt16((x)) | ||
21 | #define be32toh(x) OSSwapBigToHostInt32((x)) | 22 | #define be32toh(x) OSSwapBigToHostInt32((x)) |
22 | #endif /* __APPLE__ && !HAVE_ENDIAN_H */ | 23 | #endif /* __APPLE__ && !HAVE_ENDIAN_H */ |
23 | 24 | ||
@@ -27,6 +28,7 @@ | |||
27 | #include <sys/param.h> | 28 | #include <sys/param.h> |
28 | #endif | 29 | #endif |
29 | #define be16toh(x) ntohs((x)) | 30 | #define be16toh(x) ntohs((x)) |
31 | #define htobe16(x) htons((x)) | ||
30 | #define be32toh(x) ntohl((x)) | 32 | #define be32toh(x) ntohl((x)) |
31 | #endif /* _WIN32 && !HAVE_ENDIAN_H */ | 33 | #endif /* _WIN32 && !HAVE_ENDIAN_H */ |
32 | 34 | ||
diff --git a/src/iso7816.c b/src/iso7816.c index e2ea281..a3fd280 100644 --- a/src/iso7816.c +++ b/src/iso7816.c | |||
@@ -13,7 +13,7 @@ iso7816_new(uint8_t ins, uint8_t p1, uint16_t payload_len) | |||
13 | iso7816_apdu_t *apdu; | 13 | iso7816_apdu_t *apdu; |
14 | size_t alloc_len; | 14 | size_t alloc_len; |
15 | 15 | ||
16 | alloc_len = sizeof(iso7816_apdu_t) + payload_len; | 16 | alloc_len = sizeof(iso7816_apdu_t) + payload_len + 2; /* le1 le2 */ |
17 | 17 | ||
18 | if ((apdu = calloc(1, alloc_len)) == NULL) | 18 | if ((apdu = calloc(1, alloc_len)) == NULL) |
19 | return (NULL); | 19 | return (NULL); |
@@ -439,7 +439,7 @@ encode_cred_authdata(const char *rp_id, const uint8_t *kh, uint8_t kh_len, | |||
439 | authdata.sigcount = 0; | 439 | authdata.sigcount = 0; |
440 | 440 | ||
441 | memset(&attcred_raw.aaguid, 0, sizeof(attcred_raw.aaguid)); | 441 | memset(&attcred_raw.aaguid, 0, sizeof(attcred_raw.aaguid)); |
442 | attcred_raw.id_len = (uint16_t)(kh_len << 8); /* XXX */ | 442 | attcred_raw.id_len = htobe16(kh_len); |
443 | 443 | ||
444 | len = authdata_blob.len = sizeof(authdata) + sizeof(attcred_raw) + | 444 | len = authdata_blob.len = sizeof(authdata) + sizeof(attcred_raw) + |
445 | kh_len + pk_blob.len; | 445 | kh_len + pk_blob.len; |
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 5f27e88..4b8ef32 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt | |||
@@ -49,17 +49,10 @@ add_executable(fido2-token | |||
49 | ${COMPAT_SOURCES} | 49 | ${COMPAT_SOURCES} |
50 | ) | 50 | ) |
51 | 51 | ||
52 | add_library(sk-libfido2 MODULE sk-libfido2.c) | ||
53 | set_target_properties(sk-libfido2 PROPERTIES | ||
54 | COMPILE_FLAGS "-DSK_STANDALONE -DWITH_OPENSSL" | ||
55 | OUTPUT_NAME sk-libfido2 | ||
56 | ) | ||
57 | 52 | ||
58 | target_link_libraries(fido2-cred ${CRYPTO_LIBRARIES} fido2_shared) | 53 | target_link_libraries(fido2-cred ${CRYPTO_LIBRARIES} fido2_shared) |
59 | target_link_libraries(fido2-assert ${CRYPTO_LIBRARIES} fido2_shared) | 54 | target_link_libraries(fido2-assert ${CRYPTO_LIBRARIES} fido2_shared) |
60 | target_link_libraries(fido2-token ${CRYPTO_LIBRARIES} fido2_shared) | 55 | target_link_libraries(fido2-token ${CRYPTO_LIBRARIES} fido2_shared) |
61 | target_link_libraries(sk-libfido2 ${CRYPTO_LIBRARIES} fido2_shared) | ||
62 | 56 | ||
63 | install(TARGETS fido2-cred fido2-assert fido2-token | 57 | install(TARGETS fido2-cred fido2-assert fido2-token |
64 | DESTINATION ${CMAKE_INSTALL_BINDIR}) | 58 | DESTINATION ${CMAKE_INSTALL_BINDIR}) |
65 | install(TARGETS sk-libfido2 DESTINATION ${CMAKE_INSTALL_LIBDIR}) | ||
diff --git a/tools/sk-libfido2.c b/tools/sk-libfido2.c deleted file mode 100644 index 15aa813..0000000 --- a/tools/sk-libfido2.c +++ /dev/null | |||
@@ -1,784 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2019 Markus Friedl | ||
3 | * | ||
4 | * Permission to use, copy, modify, and distribute this software for any | ||
5 | * purpose with or without fee is hereby granted, provided that the above | ||
6 | * copyright notice and this permission notice appear in all copies. | ||
7 | * | ||
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
15 | */ | ||
16 | |||
17 | #include <fcntl.h> | ||
18 | #include <stdint.h> | ||
19 | #include <stdlib.h> | ||
20 | #include <string.h> | ||
21 | #include <stdio.h> | ||
22 | #include <stddef.h> | ||
23 | #include <stdarg.h> | ||
24 | #ifdef HAVE_UNISTD_H | ||
25 | #include <unistd.h> | ||
26 | #endif | ||
27 | |||
28 | #ifdef WITH_OPENSSL | ||
29 | #include <openssl/opensslv.h> | ||
30 | #include <openssl/crypto.h> | ||
31 | #include <openssl/bn.h> | ||
32 | #include <openssl/ec.h> | ||
33 | #include <openssl/ecdsa.h> | ||
34 | #endif /* WITH_OPENSSL */ | ||
35 | |||
36 | #include <fido.h> | ||
37 | |||
38 | #ifndef SK_STANDALONE | ||
39 | #include "log.h" | ||
40 | #include "xmalloc.h" | ||
41 | #endif | ||
42 | |||
43 | /* #define SK_DEBUG 1 */ | ||
44 | |||
45 | #if defined(_WIN32) | ||
46 | #include <windows.h> | ||
47 | #include <winternl.h> | ||
48 | #include <winerror.h> | ||
49 | #include <bcrypt.h> | ||
50 | #include <sal.h> | ||
51 | #endif | ||
52 | |||
53 | #define MAX_FIDO_DEVICES 256 | ||
54 | |||
55 | /* Compatibility with OpenSSL 1.0.x */ | ||
56 | #if (OPENSSL_VERSION_NUMBER < 0x10100000L) | ||
57 | #define ECDSA_SIG_get0(sig, pr, ps) \ | ||
58 | do { \ | ||
59 | (*pr) = sig->r; \ | ||
60 | (*ps) = sig->s; \ | ||
61 | } while (0) | ||
62 | #endif | ||
63 | |||
64 | #define SK_VERSION_MAJOR 0x00020000 /* current API version */ | ||
65 | |||
66 | /* Flags */ | ||
67 | #define SK_USER_PRESENCE_REQD 0x01 | ||
68 | |||
69 | /* Algs */ | ||
70 | #define SK_ECDSA 0x00 | ||
71 | #define SK_ED25519 0x01 | ||
72 | |||
73 | struct sk_enroll_response { | ||
74 | uint8_t *public_key; | ||
75 | size_t public_key_len; | ||
76 | uint8_t *key_handle; | ||
77 | size_t key_handle_len; | ||
78 | uint8_t *signature; | ||
79 | size_t signature_len; | ||
80 | uint8_t *attestation_cert; | ||
81 | size_t attestation_cert_len; | ||
82 | }; | ||
83 | |||
84 | struct sk_sign_response { | ||
85 | uint8_t flags; | ||
86 | uint32_t counter; | ||
87 | uint8_t *sig_r; | ||
88 | size_t sig_r_len; | ||
89 | uint8_t *sig_s; | ||
90 | size_t sig_s_len; | ||
91 | }; | ||
92 | |||
93 | /* If building as part of OpenSSH, then rename exported functions */ | ||
94 | #if !defined(SK_STANDALONE) | ||
95 | #define sk_api_version ssh_sk_api_version | ||
96 | #define sk_enroll ssh_sk_enroll | ||
97 | #define sk_sign ssh_sk_sign | ||
98 | #endif | ||
99 | |||
100 | /* Return the version of the middleware API */ | ||
101 | uint32_t sk_api_version(void); | ||
102 | |||
103 | /* Enroll a U2F key (private key generation) */ | ||
104 | int sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, | ||
105 | const char *application, uint8_t flags, | ||
106 | struct sk_enroll_response **enroll_response); | ||
107 | |||
108 | /* Sign a challenge */ | ||
109 | int sk_sign(int alg, const uint8_t *message, size_t message_len, | ||
110 | const char *application, const uint8_t *key_handle, size_t key_handle_len, | ||
111 | uint8_t flags, struct sk_sign_response **sign_response); | ||
112 | |||
113 | #ifdef SK_DEBUG | ||
114 | static void skdebug(const char *func, const char *fmt, ...) | ||
115 | __attribute__((__format__ (printf, 2, 3))); | ||
116 | |||
117 | static void | ||
118 | skdebug(const char *func, const char *fmt, ...) | ||
119 | { | ||
120 | #if !defined(SK_STANDALONE) | ||
121 | char *msg; | ||
122 | va_list ap; | ||
123 | |||
124 | va_start(ap, fmt); | ||
125 | xvasprintf(&msg, fmt, ap); | ||
126 | va_end(ap); | ||
127 | debug("%s: %s", func, msg); | ||
128 | free(msg); | ||
129 | #else | ||
130 | va_list ap; | ||
131 | |||
132 | va_start(ap, fmt); | ||
133 | fprintf(stderr, "%s: ", func); | ||
134 | vfprintf(stderr, fmt, ap); | ||
135 | fputc('\n', stderr); | ||
136 | va_end(ap); | ||
137 | #endif /* !SK_STANDALONE */ | ||
138 | } | ||
139 | #else | ||
140 | #define skdebug(...) do { /* nothing */ } while (0) | ||
141 | #endif /* SK_DEBUG */ | ||
142 | |||
143 | uint32_t | ||
144 | sk_api_version(void) | ||
145 | { | ||
146 | return SK_VERSION_MAJOR; | ||
147 | } | ||
148 | |||
149 | /* Select the first identified FIDO device attached to the system */ | ||
150 | static char * | ||
151 | pick_first_device(void) | ||
152 | { | ||
153 | char *ret = NULL; | ||
154 | fido_dev_info_t *devlist = NULL; | ||
155 | size_t olen = 0; | ||
156 | int r; | ||
157 | const fido_dev_info_t *di; | ||
158 | |||
159 | if ((devlist = fido_dev_info_new(1)) == NULL) { | ||
160 | skdebug(__func__, "fido_dev_info_new failed"); | ||
161 | goto out; | ||
162 | } | ||
163 | if ((r = fido_dev_info_manifest(devlist, 1, &olen)) != FIDO_OK) { | ||
164 | skdebug(__func__, "fido_dev_info_manifest failed: %s", | ||
165 | fido_strerr(r)); | ||
166 | goto out; | ||
167 | } | ||
168 | if (olen != 1) { | ||
169 | skdebug(__func__, "fido_dev_info_manifest bad len %zu", olen); | ||
170 | goto out; | ||
171 | } | ||
172 | di = fido_dev_info_ptr(devlist, 0); | ||
173 | if ((ret = strdup(fido_dev_info_path(di))) == NULL) { | ||
174 | skdebug(__func__, "fido_dev_info_path failed"); | ||
175 | goto out; | ||
176 | } | ||
177 | out: | ||
178 | fido_dev_info_free(&devlist, 1); | ||
179 | return ret; | ||
180 | } | ||
181 | |||
182 | #if defined(HAVE_ARC4RANDOM_BUF) | ||
183 | static int | ||
184 | get_random_challenge(uint8_t *ptr, size_t len) | ||
185 | { | ||
186 | arc4random_buf(ptr, len); | ||
187 | |||
188 | return 0; | ||
189 | } | ||
190 | #elif defined(HAVE_GETENTROPY) | ||
191 | static int | ||
192 | get_random_challenge(uint8_t *ptr, size_t len) | ||
193 | { | ||
194 | if (getentropy(ptr, len) == -1) { | ||
195 | skdebug(__func__, "getentropy failed"); | ||
196 | return -1; | ||
197 | } | ||
198 | |||
199 | return 0; | ||
200 | } | ||
201 | #elif defined(HAS_DEV_URANDOM) | ||
202 | static int | ||
203 | get_random_challenge(uint8_t *ptr, size_t len) | ||
204 | { | ||
205 | int fd; | ||
206 | ssize_t n; | ||
207 | |||
208 | if ((fd = open(FIDO_RANDOM_DEV, O_RDONLY)) < 0) { | ||
209 | skdebug(__func__, "open %s failed", FIDO_RANDOM_DEV); | ||
210 | return -1; | ||
211 | } | ||
212 | |||
213 | n = read(fd, ptr, len); | ||
214 | close(fd); | ||
215 | |||
216 | if (n < 0 || (size_t)n != len) { | ||
217 | skdebug(__func__, "read from %s failed", FIDO_RANDOM_DEV); | ||
218 | return -1; | ||
219 | } | ||
220 | |||
221 | return 0; | ||
222 | } | ||
223 | #elif defined(_WIN32) | ||
224 | static int | ||
225 | get_random_challenge(uint8_t *ptr, size_t len) | ||
226 | { | ||
227 | NTSTATUS status; | ||
228 | |||
229 | status = BCryptGenRandom(NULL, ptr, len, | ||
230 | BCRYPT_USE_SYSTEM_PREFERRED_RNG); | ||
231 | if (!NT_SUCCESS(status)) | ||
232 | return -1; | ||
233 | |||
234 | return 0; | ||
235 | } | ||
236 | #else | ||
237 | #error "please provide an implementation of get_random_challenge() for your platform" | ||
238 | #endif | ||
239 | |||
240 | /* Check if the specified key handle exists on a given device. */ | ||
241 | static int | ||
242 | try_device(fido_dev_t *dev, const char *application, | ||
243 | const uint8_t *key_handle, size_t key_handle_len) | ||
244 | { | ||
245 | fido_assert_t *assert = NULL; | ||
246 | uint8_t challenge[32]; | ||
247 | int r = FIDO_ERR_INTERNAL; | ||
248 | |||
249 | if (get_random_challenge(challenge, sizeof(challenge)) == -1) { | ||
250 | skdebug(__func__, "get_random_challenge failed"); | ||
251 | goto out; | ||
252 | } | ||
253 | |||
254 | if ((assert = fido_assert_new()) == NULL) { | ||
255 | skdebug(__func__, "fido_assert_new failed"); | ||
256 | goto out; | ||
257 | } | ||
258 | if ((r = fido_assert_set_clientdata_hash(assert, challenge, | ||
259 | sizeof(challenge))) != FIDO_OK) { | ||
260 | skdebug(__func__, "fido_assert_set_clientdata_hash: %s", | ||
261 | fido_strerr(r)); | ||
262 | goto out; | ||
263 | } | ||
264 | if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) { | ||
265 | skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r)); | ||
266 | goto out; | ||
267 | } | ||
268 | if ((r = fido_assert_allow_cred(assert, key_handle, | ||
269 | key_handle_len)) != FIDO_OK) { | ||
270 | skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r)); | ||
271 | goto out; | ||
272 | } | ||
273 | if ((r = fido_assert_set_up(assert, FIDO_OPT_FALSE)) != FIDO_OK) { | ||
274 | skdebug(__func__, "fido_assert_up: %s", fido_strerr(r)); | ||
275 | goto out; | ||
276 | } | ||
277 | r = fido_dev_get_assert(dev, assert, NULL); | ||
278 | skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r)); | ||
279 | if (r == FIDO_ERR_USER_PRESENCE_REQUIRED) { | ||
280 | /* U2F tokens may return this */ | ||
281 | r = FIDO_OK; | ||
282 | } | ||
283 | out: | ||
284 | fido_assert_free(&assert); | ||
285 | |||
286 | return r != FIDO_OK ? -1 : 0; | ||
287 | } | ||
288 | |||
289 | /* Iterate over configured devices looking for a specific key handle */ | ||
290 | static fido_dev_t * | ||
291 | find_device(const char *application, const uint8_t *key_handle, | ||
292 | size_t key_handle_len) | ||
293 | { | ||
294 | fido_dev_info_t *devlist = NULL; | ||
295 | fido_dev_t *dev = NULL; | ||
296 | size_t devlist_len = 0, i; | ||
297 | const char *path; | ||
298 | int r; | ||
299 | |||
300 | if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) { | ||
301 | skdebug(__func__, "fido_dev_info_new failed"); | ||
302 | goto out; | ||
303 | } | ||
304 | if ((r = fido_dev_info_manifest(devlist, MAX_FIDO_DEVICES, | ||
305 | &devlist_len)) != FIDO_OK) { | ||
306 | skdebug(__func__, "fido_dev_info_manifest: %s", fido_strerr(r)); | ||
307 | goto out; | ||
308 | } | ||
309 | |||
310 | skdebug(__func__, "found %zu device(s)", devlist_len); | ||
311 | |||
312 | for (i = 0; i < devlist_len; i++) { | ||
313 | const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i); | ||
314 | |||
315 | if (di == NULL) { | ||
316 | skdebug(__func__, "fido_dev_info_ptr %zu failed", i); | ||
317 | continue; | ||
318 | } | ||
319 | if ((path = fido_dev_info_path(di)) == NULL) { | ||
320 | skdebug(__func__, "fido_dev_info_path %zu failed", i); | ||
321 | continue; | ||
322 | } | ||
323 | skdebug(__func__, "trying device %zu: %s", i, path); | ||
324 | if ((dev = fido_dev_new()) == NULL) { | ||
325 | skdebug(__func__, "fido_dev_new failed"); | ||
326 | continue; | ||
327 | } | ||
328 | if ((r = fido_dev_open(dev, path)) != FIDO_OK) { | ||
329 | skdebug(__func__, "fido_dev_open failed"); | ||
330 | fido_dev_free(&dev); | ||
331 | continue; | ||
332 | } | ||
333 | if (try_device(dev, application, key_handle, | ||
334 | key_handle_len) == 0) { | ||
335 | skdebug(__func__, "found key"); | ||
336 | break; | ||
337 | } | ||
338 | fido_dev_close(dev); | ||
339 | fido_dev_free(&dev); | ||
340 | } | ||
341 | |||
342 | out: | ||
343 | if (devlist != NULL) | ||
344 | fido_dev_info_free(&devlist, MAX_FIDO_DEVICES); | ||
345 | |||
346 | return dev; | ||
347 | } | ||
348 | |||
349 | #ifdef WITH_OPENSSL | ||
350 | /* | ||
351 | * The key returned via fido_cred_pubkey_ptr() is in affine coordinates, | ||
352 | * but the API expects a SEC1 octet string. | ||
353 | */ | ||
354 | static int | ||
355 | pack_public_key_ecdsa(fido_cred_t *cred, struct sk_enroll_response *response) | ||
356 | { | ||
357 | const uint8_t *ptr; | ||
358 | BIGNUM *x = NULL, *y = NULL; | ||
359 | EC_POINT *q = NULL; | ||
360 | EC_GROUP *g = NULL; | ||
361 | int ret = -1; | ||
362 | |||
363 | response->public_key = NULL; | ||
364 | response->public_key_len = 0; | ||
365 | |||
366 | if ((x = BN_new()) == NULL || | ||
367 | (y = BN_new()) == NULL || | ||
368 | (g = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)) == NULL || | ||
369 | (q = EC_POINT_new(g)) == NULL) { | ||
370 | skdebug(__func__, "libcrypto setup failed"); | ||
371 | goto out; | ||
372 | } | ||
373 | if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) { | ||
374 | skdebug(__func__, "fido_cred_pubkey_ptr failed"); | ||
375 | goto out; | ||
376 | } | ||
377 | if (fido_cred_pubkey_len(cred) != 64) { | ||
378 | skdebug(__func__, "bad fido_cred_pubkey_len %zu", | ||
379 | fido_cred_pubkey_len(cred)); | ||
380 | goto out; | ||
381 | } | ||
382 | |||
383 | if (BN_bin2bn(ptr, 32, x) == NULL || | ||
384 | BN_bin2bn(ptr + 32, 32, y) == NULL) { | ||
385 | skdebug(__func__, "BN_bin2bn failed"); | ||
386 | goto out; | ||
387 | } | ||
388 | if (EC_POINT_set_affine_coordinates_GFp(g, q, x, y, NULL) != 1) { | ||
389 | skdebug(__func__, "EC_POINT_set_affine_coordinates_GFp failed"); | ||
390 | goto out; | ||
391 | } | ||
392 | response->public_key_len = EC_POINT_point2oct(g, q, | ||
393 | POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL); | ||
394 | if (response->public_key_len == 0 || response->public_key_len > 2048) { | ||
395 | skdebug(__func__, "bad pubkey length %zu", | ||
396 | response->public_key_len); | ||
397 | goto out; | ||
398 | } | ||
399 | if ((response->public_key = malloc(response->public_key_len)) == NULL) { | ||
400 | skdebug(__func__, "malloc pubkey failed"); | ||
401 | goto out; | ||
402 | } | ||
403 | if (EC_POINT_point2oct(g, q, POINT_CONVERSION_UNCOMPRESSED, | ||
404 | response->public_key, response->public_key_len, NULL) == 0) { | ||
405 | skdebug(__func__, "EC_POINT_point2oct failed"); | ||
406 | goto out; | ||
407 | } | ||
408 | /* success */ | ||
409 | ret = 0; | ||
410 | out: | ||
411 | if (ret != 0 && response->public_key != NULL) { | ||
412 | memset(response->public_key, 0, response->public_key_len); | ||
413 | free(response->public_key); | ||
414 | response->public_key = NULL; | ||
415 | } | ||
416 | EC_POINT_free(q); | ||
417 | EC_GROUP_free(g); | ||
418 | BN_clear_free(x); | ||
419 | BN_clear_free(y); | ||
420 | return ret; | ||
421 | } | ||
422 | #endif /* WITH_OPENSSL */ | ||
423 | |||
424 | static int | ||
425 | pack_public_key_ed25519(fido_cred_t *cred, struct sk_enroll_response *response) | ||
426 | { | ||
427 | const uint8_t *ptr; | ||
428 | size_t len; | ||
429 | int ret = -1; | ||
430 | |||
431 | response->public_key = NULL; | ||
432 | response->public_key_len = 0; | ||
433 | |||
434 | if ((len = fido_cred_pubkey_len(cred)) != 32) { | ||
435 | skdebug(__func__, "bad fido_cred_pubkey_len len %zu", len); | ||
436 | goto out; | ||
437 | } | ||
438 | if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) { | ||
439 | skdebug(__func__, "fido_cred_pubkey_ptr failed"); | ||
440 | goto out; | ||
441 | } | ||
442 | response->public_key_len = len; | ||
443 | if ((response->public_key = malloc(response->public_key_len)) == NULL) { | ||
444 | skdebug(__func__, "malloc pubkey failed"); | ||
445 | goto out; | ||
446 | } | ||
447 | memcpy(response->public_key, ptr, len); | ||
448 | ret = 0; | ||
449 | out: | ||
450 | if (ret != 0) | ||
451 | free(response->public_key); | ||
452 | return ret; | ||
453 | } | ||
454 | |||
455 | static int | ||
456 | pack_public_key(int alg, fido_cred_t *cred, struct sk_enroll_response *response) | ||
457 | { | ||
458 | switch(alg) { | ||
459 | #ifdef WITH_OPENSSL | ||
460 | case SK_ECDSA: | ||
461 | return pack_public_key_ecdsa(cred, response); | ||
462 | #endif /* WITH_OPENSSL */ | ||
463 | case SK_ED25519: | ||
464 | return pack_public_key_ed25519(cred, response); | ||
465 | default: | ||
466 | return -1; | ||
467 | } | ||
468 | } | ||
469 | |||
470 | int | ||
471 | sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len, | ||
472 | const char *application, uint8_t flags, | ||
473 | struct sk_enroll_response **enroll_response) | ||
474 | { | ||
475 | fido_cred_t *cred = NULL; | ||
476 | fido_dev_t *dev = NULL; | ||
477 | const uint8_t *ptr; | ||
478 | uint8_t user_id[32]; | ||
479 | struct sk_enroll_response *response = NULL; | ||
480 | size_t len; | ||
481 | int cose_alg; | ||
482 | int ret = -1; | ||
483 | int r; | ||
484 | char *device = NULL; | ||
485 | |||
486 | (void)flags; /* XXX; unused */ | ||
487 | #ifdef SK_DEBUG | ||
488 | fido_init(FIDO_DEBUG); | ||
489 | #endif | ||
490 | if (enroll_response == NULL) { | ||
491 | skdebug(__func__, "enroll_response == NULL"); | ||
492 | goto out; | ||
493 | } | ||
494 | *enroll_response = NULL; | ||
495 | switch(alg) { | ||
496 | #ifdef WITH_OPENSSL | ||
497 | case SK_ECDSA: | ||
498 | cose_alg = COSE_ES256; | ||
499 | break; | ||
500 | #endif /* WITH_OPENSSL */ | ||
501 | case SK_ED25519: | ||
502 | cose_alg = COSE_EDDSA; | ||
503 | break; | ||
504 | default: | ||
505 | skdebug(__func__, "unsupported key type %d", alg); | ||
506 | goto out; | ||
507 | } | ||
508 | if ((device = pick_first_device()) == NULL) { | ||
509 | skdebug(__func__, "pick_first_device failed"); | ||
510 | goto out; | ||
511 | } | ||
512 | skdebug(__func__, "using device %s", device); | ||
513 | if ((cred = fido_cred_new()) == NULL) { | ||
514 | skdebug(__func__, "fido_cred_new failed"); | ||
515 | goto out; | ||
516 | } | ||
517 | memset(user_id, 0, sizeof(user_id)); | ||
518 | if ((r = fido_cred_set_type(cred, cose_alg)) != FIDO_OK) { | ||
519 | skdebug(__func__, "fido_cred_set_type: %s", fido_strerr(r)); | ||
520 | goto out; | ||
521 | } | ||
522 | if ((r = fido_cred_set_clientdata_hash(cred, challenge, | ||
523 | challenge_len)) != FIDO_OK) { | ||
524 | skdebug(__func__, "fido_cred_set_clientdata_hash: %s", | ||
525 | fido_strerr(r)); | ||
526 | goto out; | ||
527 | } | ||
528 | if ((r = fido_cred_set_user(cred, user_id, sizeof(user_id), | ||
529 | "openssh", "openssh", NULL)) != FIDO_OK) { | ||
530 | skdebug(__func__, "fido_cred_set_user: %s", fido_strerr(r)); | ||
531 | goto out; | ||
532 | } | ||
533 | if ((r = fido_cred_set_rp(cred, application, NULL)) != FIDO_OK) { | ||
534 | skdebug(__func__, "fido_cred_set_rp: %s", fido_strerr(r)); | ||
535 | goto out; | ||
536 | } | ||
537 | if ((dev = fido_dev_new()) == NULL) { | ||
538 | skdebug(__func__, "fido_dev_new failed"); | ||
539 | goto out; | ||
540 | } | ||
541 | if ((r = fido_dev_open(dev, device)) != FIDO_OK) { | ||
542 | skdebug(__func__, "fido_dev_open: %s", fido_strerr(r)); | ||
543 | goto out; | ||
544 | } | ||
545 | if ((r = fido_dev_make_cred(dev, cred, NULL)) != FIDO_OK) { | ||
546 | skdebug(__func__, "fido_dev_make_cred: %s", fido_strerr(r)); | ||
547 | goto out; | ||
548 | } | ||
549 | if (fido_cred_x5c_ptr(cred) != NULL) { | ||
550 | if ((r = fido_cred_verify(cred)) != FIDO_OK) { | ||
551 | skdebug(__func__, "fido_cred_verify: %s", | ||
552 | fido_strerr(r)); | ||
553 | goto out; | ||
554 | } | ||
555 | } else { | ||
556 | skdebug(__func__, "self-attested credential"); | ||
557 | if ((r = fido_cred_verify_self(cred)) != FIDO_OK) { | ||
558 | skdebug(__func__, "fido_cred_verify_self: %s", | ||
559 | fido_strerr(r)); | ||
560 | goto out; | ||
561 | } | ||
562 | } | ||
563 | if ((response = calloc(1, sizeof(*response))) == NULL) { | ||
564 | skdebug(__func__, "calloc response failed"); | ||
565 | goto out; | ||
566 | } | ||
567 | if (pack_public_key(alg, cred, response) != 0) { | ||
568 | skdebug(__func__, "pack_public_key failed"); | ||
569 | goto out; | ||
570 | } | ||
571 | if ((ptr = fido_cred_id_ptr(cred)) != NULL) { | ||
572 | len = fido_cred_id_len(cred); | ||
573 | if ((response->key_handle = calloc(1, len)) == NULL) { | ||
574 | skdebug(__func__, "calloc key handle failed"); | ||
575 | goto out; | ||
576 | } | ||
577 | memcpy(response->key_handle, ptr, len); | ||
578 | response->key_handle_len = len; | ||
579 | } | ||
580 | if ((ptr = fido_cred_sig_ptr(cred)) != NULL) { | ||
581 | len = fido_cred_sig_len(cred); | ||
582 | if ((response->signature = calloc(1, len)) == NULL) { | ||
583 | skdebug(__func__, "calloc signature failed"); | ||
584 | goto out; | ||
585 | } | ||
586 | memcpy(response->signature, ptr, len); | ||
587 | response->signature_len = len; | ||
588 | } | ||
589 | if ((ptr = fido_cred_x5c_ptr(cred)) != NULL) { | ||
590 | len = fido_cred_x5c_len(cred); | ||
591 | if ((response->attestation_cert = calloc(1, len)) == NULL) { | ||
592 | skdebug(__func__, "calloc attestation cert failed"); | ||
593 | goto out; | ||
594 | } | ||
595 | memcpy(response->attestation_cert, ptr, len); | ||
596 | response->attestation_cert_len = len; | ||
597 | } | ||
598 | *enroll_response = response; | ||
599 | response = NULL; | ||
600 | ret = 0; | ||
601 | out: | ||
602 | free(device); | ||
603 | if (response != NULL) { | ||
604 | free(response->public_key); | ||
605 | free(response->key_handle); | ||
606 | free(response->signature); | ||
607 | free(response->attestation_cert); | ||
608 | free(response); | ||
609 | } | ||
610 | if (dev != NULL) { | ||
611 | fido_dev_close(dev); | ||
612 | fido_dev_free(&dev); | ||
613 | } | ||
614 | if (cred != NULL) { | ||
615 | fido_cred_free(&cred); | ||
616 | } | ||
617 | return ret; | ||
618 | } | ||
619 | |||
620 | #ifdef WITH_OPENSSL | ||
621 | static int | ||
622 | pack_sig_ecdsa(fido_assert_t *assert, struct sk_sign_response *response) | ||
623 | { | ||
624 | ECDSA_SIG *sig = NULL; | ||
625 | const BIGNUM *sig_r, *sig_s; | ||
626 | const unsigned char *cp; | ||
627 | size_t sig_len; | ||
628 | int ret = -1; | ||
629 | |||
630 | cp = fido_assert_sig_ptr(assert, 0); | ||
631 | sig_len = fido_assert_sig_len(assert, 0); | ||
632 | if ((sig = d2i_ECDSA_SIG(NULL, &cp, sig_len)) == NULL) { | ||
633 | skdebug(__func__, "d2i_ECDSA_SIG failed"); | ||
634 | goto out; | ||
635 | } | ||
636 | ECDSA_SIG_get0(sig, &sig_r, &sig_s); | ||
637 | response->sig_r_len = BN_num_bytes(sig_r); | ||
638 | response->sig_s_len = BN_num_bytes(sig_s); | ||
639 | if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL || | ||
640 | (response->sig_s = calloc(1, response->sig_s_len)) == NULL) { | ||
641 | skdebug(__func__, "calloc signature failed"); | ||
642 | goto out; | ||
643 | } | ||
644 | BN_bn2bin(sig_r, response->sig_r); | ||
645 | BN_bn2bin(sig_s, response->sig_s); | ||
646 | ret = 0; | ||
647 | out: | ||
648 | ECDSA_SIG_free(sig); | ||
649 | if (ret != 0) { | ||
650 | free(response->sig_r); | ||
651 | free(response->sig_s); | ||
652 | response->sig_r = NULL; | ||
653 | response->sig_s = NULL; | ||
654 | } | ||
655 | return ret; | ||
656 | } | ||
657 | #endif /* WITH_OPENSSL */ | ||
658 | |||
659 | static int | ||
660 | pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response) | ||
661 | { | ||
662 | const unsigned char *ptr; | ||
663 | size_t len; | ||
664 | int ret = -1; | ||
665 | |||
666 | ptr = fido_assert_sig_ptr(assert, 0); | ||
667 | len = fido_assert_sig_len(assert, 0); | ||
668 | if (len != 64) { | ||
669 | skdebug(__func__, "bad length %zu", len); | ||
670 | goto out; | ||
671 | } | ||
672 | response->sig_r_len = len; | ||
673 | if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) { | ||
674 | skdebug(__func__, "calloc signature failed"); | ||
675 | goto out; | ||
676 | } | ||
677 | memcpy(response->sig_r, ptr, len); | ||
678 | ret = 0; | ||
679 | out: | ||
680 | if (ret != 0) { | ||
681 | free(response->sig_r); | ||
682 | response->sig_r = NULL; | ||
683 | } | ||
684 | return ret; | ||
685 | } | ||
686 | |||
687 | static int | ||
688 | pack_sig(int alg, fido_assert_t *assert, struct sk_sign_response *response) | ||
689 | { | ||
690 | switch(alg) { | ||
691 | #ifdef WITH_OPENSSL | ||
692 | case SK_ECDSA: | ||
693 | return pack_sig_ecdsa(assert, response); | ||
694 | #endif /* WITH_OPENSSL */ | ||
695 | case SK_ED25519: | ||
696 | return pack_sig_ed25519(assert, response); | ||
697 | default: | ||
698 | return -1; | ||
699 | } | ||
700 | } | ||
701 | |||
702 | int | ||
703 | sk_sign(int alg, const uint8_t *message, size_t message_len, | ||
704 | const char *application, | ||
705 | const uint8_t *key_handle, size_t key_handle_len, | ||
706 | uint8_t flags, struct sk_sign_response **sign_response) | ||
707 | { | ||
708 | fido_assert_t *assert = NULL; | ||
709 | fido_dev_t *dev = NULL; | ||
710 | struct sk_sign_response *response = NULL; | ||
711 | int ret = -1; | ||
712 | int r; | ||
713 | |||
714 | #ifdef SK_DEBUG | ||
715 | fido_init(FIDO_DEBUG); | ||
716 | #endif | ||
717 | |||
718 | if (sign_response == NULL) { | ||
719 | skdebug(__func__, "sign_response == NULL"); | ||
720 | goto out; | ||
721 | } | ||
722 | *sign_response = NULL; | ||
723 | if ((dev = find_device(application, key_handle, | ||
724 | key_handle_len)) == NULL) { | ||
725 | skdebug(__func__, "couldn't find device for key handle"); | ||
726 | goto out; | ||
727 | } | ||
728 | if ((assert = fido_assert_new()) == NULL) { | ||
729 | skdebug(__func__, "fido_assert_new failed"); | ||
730 | goto out; | ||
731 | } | ||
732 | if ((r = fido_assert_set_clientdata_hash(assert, message, | ||
733 | message_len)) != FIDO_OK) { | ||
734 | skdebug(__func__, "fido_assert_set_clientdata_hash: %s", | ||
735 | fido_strerr(r)); | ||
736 | goto out; | ||
737 | } | ||
738 | if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) { | ||
739 | skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r)); | ||
740 | goto out; | ||
741 | } | ||
742 | if ((r = fido_assert_allow_cred(assert, key_handle, | ||
743 | key_handle_len)) != FIDO_OK) { | ||
744 | skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r)); | ||
745 | goto out; | ||
746 | } | ||
747 | if ((r = fido_assert_set_up(assert, | ||
748 | (flags & SK_USER_PRESENCE_REQD) ? | ||
749 | FIDO_OPT_TRUE : FIDO_OPT_FALSE)) != FIDO_OK) { | ||
750 | skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r)); | ||
751 | goto out; | ||
752 | } | ||
753 | if ((r = fido_dev_get_assert(dev, assert, NULL)) != FIDO_OK) { | ||
754 | skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r)); | ||
755 | goto out; | ||
756 | } | ||
757 | if ((response = calloc(1, sizeof(*response))) == NULL) { | ||
758 | skdebug(__func__, "calloc response failed"); | ||
759 | goto out; | ||
760 | } | ||
761 | response->flags = fido_assert_flags(assert, 0); | ||
762 | response->counter = fido_assert_sigcount(assert, 0); | ||
763 | if (pack_sig(alg, assert, response) != 0) { | ||
764 | skdebug(__func__, "pack_sig failed"); | ||
765 | goto out; | ||
766 | } | ||
767 | *sign_response = response; | ||
768 | response = NULL; | ||
769 | ret = 0; | ||
770 | out: | ||
771 | if (response != NULL) { | ||
772 | free(response->sig_r); | ||
773 | free(response->sig_s); | ||
774 | free(response); | ||
775 | } | ||
776 | if (dev != NULL) { | ||
777 | fido_dev_close(dev); | ||
778 | fido_dev_free(&dev); | ||
779 | } | ||
780 | if (assert != NULL) { | ||
781 | fido_assert_free(&assert); | ||
782 | } | ||
783 | return ret; | ||
784 | } | ||
diff --git a/udev/70-u2f.rules b/udev/70-u2f.rules index 8dc20a1..3932270 100644 --- a/udev/70-u2f.rules +++ b/udev/70-u2f.rules | |||
@@ -35,8 +35,11 @@ KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2abe", ATTRS{idProduct | |||
35 | # Thetis Key | 35 | # Thetis Key |
36 | KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1ea8", ATTRS{idProduct}=="f025", TAG+="uaccess", GROUP="plugdev", MODE="0660" | 36 | KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1ea8", ATTRS{idProduct}=="f025", TAG+="uaccess", GROUP="plugdev", MODE="0660" |
37 | 37 | ||
38 | # Nitrokey FIDO U2F | 38 | # Nitrokey FIDO U2F, Nitrokey FIDO2 |
39 | KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="4287", TAG+="uaccess", GROUP="plugdev", MODE="0660" | 39 | KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="4287|42b1", TAG+="uaccess", GROUP="plugdev", MODE="0660" |
40 | |||
41 | # Safetech SafeKey | ||
42 | KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="42b3", TAG+="uaccess", GROUP="plugdev", MODE="0660" | ||
40 | 43 | ||
41 | # Google Titan U2F | 44 | # Google Titan U2F |
42 | KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="18d1", ATTRS{idProduct}=="5026", TAG+="uaccess", GROUP="plugdev", MODE="0660" | 45 | KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="18d1", ATTRS{idProduct}=="5026", TAG+="uaccess", GROUP="plugdev", MODE="0660" |