├── .github └── workflows │ └── scan.yml ├── .gitignore ├── .travis.yml ├── AUTHORS ├── COPYING ├── Makefile.am ├── NEWS ├── README ├── README.adoc ├── THANKS ├── autogen.sh ├── build-aux └── travis ├── configure.ac ├── doc └── Make_Release.adoc ├── m4 ├── ld-version-script.m4 ├── manywarnings.m4 └── warnings.m4 ├── man └── u2f-server.1.txt ├── src ├── Makefile.am ├── cmdline.ggo └── u2f-server.c ├── tests ├── Makefile.am ├── basic.c ├── core.c ├── openssl.c └── u2f-server-test.sh └── u2f-server ├── Makefile.am ├── b64 ├── cdecode.h └── cencode.h ├── cdecode.c ├── cencode.c ├── core.c ├── crypto.h ├── error.c ├── global.c ├── internal.h ├── openssl.c ├── sha256.c ├── sha256.h ├── u2f-server-version.h.in ├── u2f-server.h ├── u2f-server.map ├── u2f-server.pc.in └── version.c /.github/workflows/scan.yml: -------------------------------------------------------------------------------- 1 | name: static code analysis 2 | # Documentation: https://github.com/Yubico/yes-static-code-analysis 3 | 4 | on: 5 | push: 6 | schedule: 7 | - cron: '0 0 * * 1' 8 | 9 | env: 10 | SCAN_IMG: 11 | yubico-yes-docker-local.jfrog.io/static-code-analysis/c:v1 12 | SECRET: ${{ secrets.ARTIFACTORY_READER_TOKEN }} 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - uses: actions/checkout@master 20 | 21 | - name: Scan and fail on warnings 22 | run: | 23 | if [ "${SECRET}" != "" ]; then 24 | docker login yubico-yes-docker-local.jfrog.io/ \ 25 | -u svc-static-code-analysis-reader -p ${SECRET} 26 | docker pull ${SCAN_IMG} 27 | docker run -v${PWD}:/k -e COMPILE_DEPS="${COMPILE_DEPS}" \ 28 | -e PROJECT_NAME=${GITHUB_REPOSITORY#Yubico/} \ 29 | -e PVS_IGNORE_WARNINGS=${PVS_IGNORE_WARNINGS} -t ${SCAN_IMG} 30 | else 31 | echo "No docker registry credentials, not scanning" 32 | fi 33 | 34 | - uses: actions/upload-artifact@master 35 | if: failure() 36 | with: 37 | name: suppression_files 38 | path: suppression_files 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.bak 2 | *.lo 3 | *.log 4 | *.o 5 | *.trs 6 | *~ 7 | .deps 8 | .libs 9 | ChangeLog 10 | INSTALL 11 | Makefile 12 | Makefile.in 13 | aclocal.m4 14 | autom4te.cache/ 15 | build-aux/ar-lib 16 | build-aux/compile 17 | build-aux/config.guess 18 | build-aux/config.sub 19 | build-aux/depcomp 20 | build-aux/install-sh 21 | build-aux/ltmain.sh 22 | build-aux/missing 23 | build-aux/test-driver 24 | config.log 25 | config.status 26 | configure 27 | gtk-doc/html-build.stamp 28 | gtk-doc/html.stamp 29 | gtk-doc/html/ 30 | gtk-doc/scan-build.stamp 31 | gtk-doc/setup-build.stamp 32 | gtk-doc/sgml-build.stamp 33 | gtk-doc/sgml.stamp 34 | gtk-doc/tmpl-build.stamp 35 | gtk-doc/tmpl.stamp 36 | gtk-doc/tmpl/ 37 | gtk-doc/u2f-server-decl-list.txt 38 | gtk-doc/u2f-server-decl.txt 39 | gtk-doc/u2f-server-overrides.txt 40 | gtk-doc/u2f-server-sections.txt 41 | gtk-doc/u2f-server-undeclared.txt 42 | gtk-doc/u2f-server-undocumented.txt 43 | gtk-doc/u2f-server-unused.txt 44 | gtk-doc/u2f-server.args 45 | gtk-doc/u2f-server.hierarchy 46 | gtk-doc/u2f-server.interfaces 47 | gtk-doc/u2f-server.prerequisites 48 | gtk-doc/u2f-server.signals 49 | gtk-doc/u2f-server.types 50 | gtk-doc/xml/ 51 | libtool 52 | libu2f-server-*.tar.xz* 53 | m4/libtool.m4 54 | m4/ltoptions.m4 55 | m4/ltsugar.m4 56 | m4/ltversion.m4 57 | m4/lt~obsolete.m4 58 | src/.gdbinit 59 | src/cmdline.c 60 | src/cmdline.h 61 | src/u2f-server 62 | man/u2f-server.1 63 | tests/.deps/ 64 | tests/Makefile 65 | tests/Makefile.in 66 | tests/basic 67 | tests/core 68 | tests/openssl 69 | u2f-server/libu2f-server.la 70 | u2f-server/u2f-server-version.h 71 | u2f-server/u2f-server.pc 72 | coverage/ 73 | src/cmdline.gcda 74 | src/cmdline.gcno 75 | src/u2f-server.gcda 76 | src/u2f-server.gcno 77 | u2f-server/cdecode.gcno 78 | u2f-server/cencode.gcno 79 | u2f-server/core.gcno 80 | u2f-server/error.gcno 81 | u2f-server/global.gcno 82 | u2f-server/openssl.gcno 83 | u2f-server/sha256.gcno 84 | u2f-server/version.gcno 85 | tests/*.gcno 86 | tests/*.gcda -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | os: 3 | - linux 4 | - osx 5 | compiler: 6 | - gcc 7 | - clang 8 | script: ./build-aux/travis 9 | matrix: 10 | include: 11 | - compiler: gcc 12 | env: COVERAGE="--enable-coverage" 13 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Alessio Di Mauro 2 | Implementation of U2Fv2. 3 | 4 | Simon Josefsson 5 | Initial Framework. -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Yubico AB 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the 14 | distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Yubico AB 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are 6 | # met: 7 | # 8 | # # Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # 11 | # # Redistributions in binary form must reproduce the above 12 | # copyright notice, this list of conditions and the following 13 | # disclaimer in the documentation and/or other materials provided 14 | # with the distribution. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | SUBDIRS = u2f-server src 29 | 30 | if ENABLE_TESTS 31 | SUBDIRS+=tests 32 | endif 33 | 34 | ACLOCAL_AMFLAGS = -I m4 35 | 36 | DISTCHECK_CONFIGURE_FLAGS = --enable-tests 37 | 38 | if ENABLE_COV 39 | cov-reset: 40 | rm -fr coverage 41 | find . -name "*.gcda" -exec rm {} \; 42 | lcov --directory . --zerocounters 43 | 44 | cov-report: 45 | mkdir -p coverage 46 | lcov --compat-libtool --directory . --capture --output-file coverage/app.info 47 | lcov --extract coverage/app.info '*.c' --output-file coverage/app2.info 48 | lcov --remove coverage/app2.info cmdline.c --output-file coverage/app3.info 49 | genhtml -o coverage/ coverage/app3.info 50 | 51 | cov: 52 | make cov-report 53 | 54 | clean-local: 55 | make cov-reset 56 | 57 | check: 58 | make cov 59 | endif 60 | 61 | # Release 62 | 63 | indent: 64 | indent -kr -nut -i2 */*.c */*.h */*.h.in 65 | indent -kr -nut -i2 */*.c */*.h */*.h.in 66 | 67 | ChangeLog: 68 | cd $(srcdir) && git2cl > ChangeLog 69 | 70 | release: 71 | @if test ! -d "$(YUBICO_WWW_REPO)"; then \ 72 | echo "www repo not found!"; \ 73 | echo "Make sure that YUBICO_WWW_REPO is set"; \ 74 | exit 1; \ 75 | fi 76 | @if test -z "$(KEYID)"; then \ 77 | echo "Try this instead:"; \ 78 | echo " make release KEYID=[PGPKEYID]"; \ 79 | echo "For example:"; \ 80 | echo " make release KEYID=2117364A"; \ 81 | exit 1; \ 82 | fi 83 | @head -3 $(srcdir)/NEWS | \ 84 | grep -q "Version $(VERSION) .released `date -I`" || \ 85 | (echo 'error: You need to update date/version in $(srcdir)/NEWS'; exit 1) 86 | rm -f $(srcdir)/ChangeLog 87 | make ChangeLog distcheck 88 | gpg --detach-sign --default-key $(KEYID) $(PACKAGE)-$(VERSION).tar.xz 89 | gpg --verify $(PACKAGE)-$(VERSION).tar.xz.sig 90 | cd $(srcdir) && git push 91 | cd $(srcdir) && git tag -u $(KEYID) -m $(VERSION) $(PACKAGE)-$(VERSION) 92 | cd $(srcdir) && git push --tags 93 | $(YUBICO_WWW_REPO)/publish $(PACKAGE) $(VERSION) $(PACKAGE)-$(VERSION).tar.xz* 94 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | libu2f-server NEWS -- History of user-visible changes. -*- outline -*- 2 | 3 | * Version 1.1.1 (unreleased) 4 | 5 | * Version 1.1.0 (released 2018-01-04) 6 | ** Add the possibility to dump the attestation certificate. 7 | ** Correctly handle the refcount of json-c objects. 8 | ** General fixes and improvements. 9 | 10 | * Version 1.0.1 (released 2015-07-09) 11 | ** General fixes and improvements. 12 | ** Changed pkg-config settings to accommodate changes in OpenSSL. 13 | 14 | * Version 1.0.0 (released 2015-07-01) 15 | ** Improved compatibility for other architectures. 16 | 17 | * Version 0.0.0 (released 2014-11-25) 18 | ** Initial release. 19 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Yubico Universal 2nd Factor (U2F) Server C Library 2 | ================================================== 3 | 4 | image:https://img.shields.io/badge/License-BSD%202--Clause-orange.svg["License, link=https://opensource.org/licenses/BSD-2-Clause"] 5 | image:https://travis-ci.org/Yubico/libu2f-server.svg?branch=master["Build Status", link="https://travis-ci.org/Yubico/libu2f-server"] 6 | image:https://coveralls.io/repos/Yubico/libu2f-server/badge.svg?branch=master["Coverage Status", link="https://coveralls.io/r/Yubico/libu2f-server?branch=master"] 7 | image:https://scan.coverity.com/projects/5685/badge.svg["Coverity Status", link=https://scan.coverity.com/projects/5685] 8 | 9 | Introduction 10 | ------------ 11 | 12 | This is a C library that implements the server-side of the U2F 13 | protocol. More precisely, it provides an API for generating the JSON 14 | blobs required by U2F devices to perform the U2F Registration and U2F 15 | Authentication operations, and functionality for verifying the 16 | cryptographic operations. For the host-side aspect, see our 17 | https://developers.yubico.com/libu2f-host/[libu2f-host project]. 18 | 19 | NOTE: This project is in maintenance mode, 20 | https://developers.yubico.com/libfido2/[libfido2] is a new project with 21 | support for U2F and FIDO2. 22 | 23 | Warning and Known limitation: Attestation Certificate validation 24 | ---------------------------------------------------------------- 25 | 26 | At registration time, an X.509 attestation certificate is provided. 27 | Ideally the whole certificate chain should be validated. However, the 28 | current version of this library does not do so. This is a known 29 | limitation, and we hope to address this as soon as possible. Please 30 | be sure to understand the implication of this before using the 31 | library. 32 | 33 | Versioning 34 | ---------- 35 | 36 | The version numbers for this project follows the principles of 37 | Semantic Versioning -- see http://semver.org/[http://semver.org] -- which, 38 | briefly, uses the +MAJOR.MINOR.PATCH+ scheme where +MAJOR+ is bumped 39 | in API-incompatible changes, +MINOR+ is bumped for new features that are 40 | backwards compatible, and +PATCH+ is incremented when minor changes are done. 41 | 42 | Usage 43 | ----- 44 | 45 | There is a command line utility that is useful for debugging or 46 | testing. We describe how you can use it here. 47 | 48 | In order to perform a *REGISTRATION* operation, run the application 49 | as follows: 50 | 51 | $ u2f-server -aregister -ohttp://demo.yubico.com \ 52 | -i http://demo.yubico.com -k keyhandle.dat -p userkey.dat 53 | 54 | This will generate a JSON registration blob like the following: 55 | 56 | ...... 57 | { "challenge": "cOQ-TBjhsbiAss7_hg2LhNNWxqjrRPWoSlThnQD6e2I",\ 58 | "version":"U2F_V2", "appId": "http:\/\/demo.yubico.com" } 59 | ...... 60 | 61 | This can be used together with a client tool in order to 62 | generate a registration response. One such tool is included in the 63 | https://developers.yubico.com/libu2f-host/[libu2f-host project]. 64 | 65 | 66 | The tool can be invoked as follows: 67 | 68 | $ u2f-host -aregister -o http://demo.yubico.com 69 | 70 | The previous JSON blob should now be pasted into the standard input of the 71 | u2f-host application and an +EOF+ character should be sent (+Ctrl-D+ on Linux). 72 | 73 | At this point your device should start flashing, waiting for an input. 74 | Touching the gold disk will allow you to proceed. 75 | 76 | A JSON registration response will now be generated by u2f-host. 77 | It will look something like this: 78 | 79 | ...... 80 | { "registrationData": "BQQcsmA1brUvrNgntjvKrCzUIIN92Y61ee\ 81 | DI7xnEVliksvO8l0aVY0HcASqf5dExipOVHTfxYVImZ_M3U4eAjt-OQCt\ 82 | h1BOT0gr3HLkLJcid1Ahks8NyjeyfkoLLES-i4Fn650vkgE7jTOnCGiuC\ 83 | nZzpkZ2exkwLWhNW2QZA1nAaS4owggIbMIIBBaADAgECAgR1o_Z1MAsGC\ 84 | SqGSIb3DQEBCzAuMSwwKgYDVQQDEyNZdWJpY28gVTJGIFJvb3QgQ0EgU2\ 85 | VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAwMDBaGA8yMDUwMDkwNDA\ 86 | wMDAwMFowKjEoMCYGA1UEAwwfWXViaWNvIFUyRiBFRSBTZXJpYWwgMTk3\ 87 | MzY3OTczMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABBmjfkNqa2mXz\ 88 | Vh2ZxuES5coCvvENxDMDLmfd-0ACG0Fu7wR4ZTjKd9KAuidySpfona5cs\ 89 | GmlM0Te_Zu35h_wwujEjAQMA4GCisGAQQBgsQKAQIEADALBgkqhkiG9w0\ 90 | BAQsDggEBAb0tuI0-CzSxBg4cAlyD6UyT4cKyJZGVhWdtPgj_mWepT3Tu\ 91 | 9jXtdgA5F3jfZtTc2eGxuS-PPvqRAkZd40AXgM8A0YaXPwlT4s0RUTY9Y\ 92 | 8aAQzQZeAHuZk3lKKd_LUCg5077dzdt90lC5eVTEduj6cOnHEqnOr2Cv7\ 93 | 5FuiQXX7QkGQxtoD-otgvhZ2Fjk29o7Iy9ik7ewHGXOfoVw_ruGWi0YfX\ 94 | BTuqEJ6H666vvMN4BZWHtzhC0k5ceQslB9Xdntky-GQgDqNkkBf32GKwA\ 95 | FT9JJrkO2BfsB-wfBrTiHr0AABYNTNKTceA5dtR3UVpI492VUWQbY3YmW\ 96 | UUfKTI7fM4wRQIhAJNrBRxUWPwdVWFpuutWd78ESaoZFXvK2yvUzm14cP\ 97 | vQAiBFSn-5J6LvJAstgSFLD-1vWXANqrJ7-5yoIqT6fOj9JQ==", "cli\ 98 | entData": "eyAiY2hhbGxlbmdlIjogImNPUS1UQmpoc2JpQXNzN19oZz\ 99 | JMaE5OV3hxanJSUFdvU2xUaG5RRDZlMkkiLCAib3JpZ2luIjogImh0dHA\ 100 | 6XC9cL2RlbW8ueXViaWNvLmNvbSIsICJ0eXAiOiAibmF2aWdhdG9yLmlk\ 101 | LmZpbmlzaEVucm9sbG1lbnQiIH0=" } 102 | ...... 103 | 104 | This should now be pasted back into the u2f-server application 105 | which will report either a successful or a failed registration. 106 | In the first case the user public key and the key-handle 107 | associated with the operation will be written in the two files 108 | specified at invocation time (userkey.dat and keyhandle.dat 109 | respectively in this example). 110 | 111 | 112 | In order to perform an *AUTHENTICATION* operation, run the application 113 | as follows: 114 | 115 | $ u2f-server -aauthenticate -ohttp://demo.yubico.com \ 116 | -i http://demo.yubico.com -k keyhandle.dat -p userkey.dat 117 | 118 | The two files keyhandle.dat and userkey.dat must contain a 119 | key-handle and user public key respectively. These can be obtained 120 | by performing the registration process described above. 121 | 122 | The tool will then generate a JSON authentication blob like the following: 123 | 124 | ...... 125 | { "keyHandle": "K2HUE5PSCvccuQslyJ3UCGSzw3KN7J-SgssRL6LgWfrnS-SATu\ 126 | NM6cIaK4KdnOmRnZ7GTAtaE1bZBkDWcBpLig", "version": "U2F_V2", "chall\ 127 | enge": "NRQNFRTLNLKtJzUsIPe12Aw1uzjIdSBotm0j_gYbpXQ", "appId": "ht\ 128 | tp:\/\/demo.yubico.com" } 129 | ...... 130 | 131 | This can be used together with a client tool in order to 132 | generate an authentication response. One such tool is included in the 133 | https://developers.yubico.com/libu2f-host/[libu2f-host project]. 134 | 135 | The tool can be invoked as follows: 136 | 137 | $ u2f-host -aauthenticate -o http://demo.yubico.com 138 | 139 | The previous JSON blob should now be pasted into the standard input of the 140 | u2f-host application and an +EOF+ character should be sent (+Ctrl-D+ on Linux). 141 | 142 | At this point your device should start flashing, waiting for an input. 143 | Touching the gold disk will allow you to proceed. 144 | 145 | A JSON authentication response will now be generated by u2f-host. 146 | It will look something like this: 147 | 148 | ...... 149 | { "signatureData": "AQAAAC0wRQIgc3fteZpmsA0AbNDMIglup1b5jRPVUUr0\ 150 | PDz_ZTq7lD4CIQCAGCkREjVFRVIOGIs43dspMxgVjENGE3gm8G3VNdc61w==", "\ 151 | clientData": "eyAiY2hhbGxlbmdlIjogIk5SUU5GUlRMTkxLdEp6VXNJUGUxMk\ 152 | F3MXV6aklkU0JvdG0wal9nWWJwWFEiLCAib3JpZ2luIjogImh0dHA6XC9cL2RlbW\ 153 | 8ueXViaWNvLmNvbSIsICJ0eXAiOiAibmF2aWdhdG9yLmlkLmdldEFzc2VydGlvbi\ 154 | IgfQ==", "keyHandle": "K2HUE5PSCvccuQslyJ3UCGSzw3KN7J-SgssRL6LgW\ 155 | frnS-SATuNM6cIaK4KdnOmRnZ7GTAtaE1bZBkDWcBpLig" } 156 | ...... 157 | 158 | This should now be pasted back into the u2f-server application 159 | which will report either a successful or a failed authentication. 160 | For successful authentication the counter value and the user 161 | presence value will be printed as well. 162 | 163 | Building 164 | -------- 165 | 166 | This project has a handful of Dependencies that must be satisfied prior to 167 | build -- consult `build-aux/travis` for insights if you run into issues. 168 | 169 | Autotools are required, this includes `autoconf`, `automake` and `libtool`, 170 | `check` is only required for tests, and the library will build with out. 171 | 172 | Debian: 173 | ----------- 174 | # apt-get install check gengetopt help2man libssl-dev libjson-c-dev 175 | ----------- 176 | 177 | RHEL: 178 | ----------- 179 | # yum install check-devel gengetopt help2man openssl-devel json-c-devel 180 | ----------- 181 | 182 | macOS: 183 | ----------- 184 | $ brew install check gengetopt help2man json-c openssl pkg-config 185 | $ export PKG_CONFIG_PATH=$(brew --prefix openssl)/lib/pkgconfig 186 | ----------- 187 | 188 | Building from a release tarball: 189 | 190 | ----------- 191 | $ ./configure --enable-tests 192 | $ make && make check 193 | # make install 194 | ----------- 195 | 196 | From source: 197 | 198 | ----------- 199 | $ ./autogen.sh 200 | $ ./configure --enable-tests 201 | $ make && make check 202 | # make install 203 | ----------- 204 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | README -------------------------------------------------------------------------------- /THANKS: -------------------------------------------------------------------------------- 1 | Some public-domain Base64 code is taken from the libb64 project. 2 | 3 | Some public domain SHA256 code taken from LibTomCrypt. 4 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 5 | 6 | main() { 7 | autoreconf --install 8 | } 9 | 10 | pushd "$DIR" &>/dev/null 11 | main "$@" 12 | popd &>/dev/null 13 | -------------------------------------------------------------------------------- /build-aux/travis: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -exv 3 | env | sort 4 | 5 | if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then 6 | sudo apt-get update -qq 7 | sudo apt-get autoremove -qq 8 | sudo apt-get install -qq -y gengetopt help2man libjson0-dev libssl-dev check lcov 9 | elif [[ "$TRAVIS_OS_NAME" == "osx" ]]; then 10 | brew update 11 | brew install gengetopt help2man pkg-config json-c openssl check 12 | export PKG_CONFIG_PATH=/usr/local/opt/openssl/lib/pkgconfig 13 | else 14 | echo "Unsupported OS: $TRAVIS_OS_NAME" 15 | exit 1 16 | fi 17 | 18 | ./autogen.sh 19 | ./configure $COVERAGE --enable-tests 20 | make check 21 | 22 | if [[ ! -z "$COVERAGE" ]]; then 23 | gem install coveralls-lcov 24 | coveralls-lcov coverage/app3.info 25 | fi 26 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Yubico AB 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are 6 | # met: 7 | # 8 | # # Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # 11 | # # Redistributions in binary form must reproduce the above 12 | # copyright notice, this list of conditions and the following 13 | # disclaimer in the documentation and/or other materials provided 14 | # with the distribution. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | AC_PREQ([2.69]) 29 | 30 | AC_INIT([libu2f-server], [1.1.1], 31 | [https://github.com/Yubico/libu2f-server/issues], 32 | [libu2f-server], 33 | [https://developers.yubico.com/libu2f-server]) 34 | 35 | AC_CONFIG_MACRO_DIR([m4]) 36 | AC_CONFIG_AUX_DIR([build-aux]) 37 | 38 | # http://www.gnu.org/s/libtool/manual/html_node/Updating-version-info.html 39 | AC_SUBST(LT_CURRENT, 1) 40 | AC_SUBST(LT_REVISION, 1) 41 | AC_SUBST(LT_AGE, 1) 42 | 43 | AM_INIT_AUTOMAKE([gnits dist-xz no-dist-gzip std-options -Wall]) 44 | AM_SILENT_RULES([yes]) 45 | AC_PROG_CC 46 | AC_USE_SYSTEM_EXTENSIONS 47 | 48 | m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) 49 | 50 | LT_INIT([win32-dll]) 51 | 52 | AM_MISSING_PROG(HELP2ADOC, help2adoc, $missing_dir) 53 | AM_MISSING_PROG(HELP2MAN, help2man, $missing_dir) 54 | if ! which help2man >/dev/null ; then 55 | AC_MSG_ERROR([help2man missing]) 56 | fi 57 | if ! which gengetopt >/dev/null ; then 58 | AC_MSG_ERROR([gengetopt missing]) 59 | fi 60 | 61 | gl_LD_VERSION_SCRIPT 62 | 63 | PKG_CHECK_MODULES([LIBJSON], [json-c], [], [ 64 | PKG_CHECK_MODULES([LIBJSON], [json])]) 65 | 66 | # Check for json_object_object_get_ext 67 | am_save_CFLAGS="$CFLAGS" 68 | am_save_LIBS="$LIBS" 69 | CFLAGS="$CFLAGS $LIBJSON_CFLAGS" 70 | LIBS="$LIBS $LIBJSON_LIBS" 71 | AC_CHECK_FUNCS([json_object_object_get_ex]) 72 | CFLAGS=$am_save_CFLAGS 73 | LIBS=$am_save_LIBS 74 | 75 | PKG_CHECK_MODULES([LIBSSL], [libssl], [], []) 76 | 77 | PKG_CHECK_MODULES([LIBCRYPTO], [libcrypto], [], []) 78 | 79 | AC_ARG_ENABLE([tests], 80 | [AS_HELP_STRING([--enable-tests], 81 | [use check to run the unit tests])], 82 | [enable_tests=$enableval], 83 | [enable_tests=no]) 84 | AM_CONDITIONAL([ENABLE_TESTS],[test '!' "$enable_tests" = no]) 85 | if test '!' "$enable_tests" = no; then 86 | PKG_CHECK_MODULES([CHECK], [check], [], [AC_MSG_ERROR(`check` not found)]) 87 | fi 88 | 89 | 90 | AC_ARG_ENABLE([coverage], 91 | [AS_HELP_STRING([--enable-coverage], 92 | [use Gcov to test the test suite])], 93 | [], 94 | [enable_cov=no]) 95 | AM_CONDITIONAL([ENABLE_COV],[test '!' "$enable_cov" = no]) 96 | 97 | AC_ARG_ENABLE([h2a], 98 | [AS_HELP_STRING([--enable-h2a], 99 | [generate asciidoc manpages])], 100 | [enable_h2a=$enableval], 101 | [enable_h2a=no]) 102 | AM_CONDITIONAL([ENABLE_H2A],[test '!' "$enable_h2a" = no]) 103 | 104 | AC_ARG_ENABLE([gcc-warnings], 105 | [AS_HELP_STRING([--enable-gcc-warnings], 106 | [turn on lots of GCC warnings (for developers)])], 107 | [case $enableval in 108 | yes|no) ;; 109 | *) AC_MSG_ERROR([bad value $enableval for gcc-warnings option]) ;; 110 | esac 111 | gl_gcc_warnings=$enableval], 112 | [gl_gcc_warnings=no] 113 | ) 114 | 115 | if test "$gl_gcc_warnings" = yes; then 116 | nw="$nw -Wsystem-headers" # Don't let system headers trigger warnings 117 | nw="$nw -Wpadded" # Struct's arenot padded 118 | nw="$nw -Wc++-compat" # We don't care strongly about C++ compilers 119 | nw="$nw -Wtraditional" # Warns on #elif which we use often 120 | nw="$nw -Wtraditional-conversion" # Too many warnings for now 121 | nw="$nw -Wconversion" # Too many warnings for now 122 | nw="$nw -Wsuggest-attribute=pure" # Is it worth using attributes? 123 | nw="$nw -Wsuggest-attribute=const" # Is it worth using attributes? 124 | 125 | gl_MANYWARN_ALL_GCC([ws]) 126 | gl_MANYWARN_COMPLEMENT(ws, [$ws], [$nw]) 127 | for w in $ws; do 128 | gl_WARN_ADD([$w]) 129 | done 130 | 131 | gl_WARN_ADD([-fdiagnostics-show-option]) 132 | fi 133 | 134 | AC_SUBST([U2FS_VERSION_MAJOR], 135 | `echo $PACKAGE_VERSION | sed 's/\(.*\)\..*\..*/\1/g'`) 136 | AC_SUBST([U2FS_VERSION_MINOR], 137 | `echo $PACKAGE_VERSION | sed 's/.*\.\(.*\)\..*/\1/g'`) 138 | AC_SUBST([U2FS_VERSION_PATCH], 139 | `echo $PACKAGE_VERSION | sed 's/.*\..*\.\(.*\)/\1/g'`) 140 | AC_SUBST([U2FS_VERSION_NUMBER], 141 | `printf "0x%02x%02x%02x" $U2FS_VERSION_MAJOR \ 142 | $U2FS_VERSION_MINOR $U2FS_VERSION_PATCH`) 143 | 144 | AC_CONFIG_FILES([ 145 | Makefile 146 | src/Makefile 147 | u2f-server/Makefile 148 | u2f-server/u2f-server-version.h 149 | u2f-server/u2f-server.pc 150 | ]) 151 | if test '!' "$enable_tests" = no; then 152 | AC_CONFIG_FILES([ 153 | tests/Makefile 154 | ]) 155 | fi 156 | AC_OUTPUT 157 | 158 | AC_MSG_NOTICE([summary of build options: 159 | 160 | Version: ${VERSION} shared $LT_CURRENT:$LT_REVISION:$LT_AGE major $U2FS_VERSION_MAJOR minor $U2FS_VERSION_MINOR patch $U2FS_VERSION_PATCH number $U2FS_VERSION_NUMBER 161 | Host type: ${host} 162 | Install prefix: ${prefix} 163 | Compiler: ${CC} 164 | Shared library: ${enable_shared} 165 | Static library: ${enable_static} 166 | CFLAGS: ${CFLAGS} 167 | CPPFLAGS: ${CPPFLAGS} 168 | JSON CFLAGS: $LIBJSON_CFLAGS 169 | JSON LIBS: $LIBJSON_LIBS 170 | OPENSSL CFLAGS: $LIBSSL_CFLAGS 171 | OPENSSL LIBS: $LIBSSL_LIBS 172 | LIBCRYPTO CFLAGS: $LIBCRYPTO_CFLAGS 173 | LIBCRYPTO LIBS: $LIBCRYPTO_LIBS 174 | CHECK CFLAGS: $CHECK_CFLAGS 175 | CHECK LIBS: $CHECK_LIBS 176 | ]) 177 | -------------------------------------------------------------------------------- /doc/Make_Release.adoc: -------------------------------------------------------------------------------- 1 | Maintainer instructions for making releases 2 | =========================================== 3 | 4 | Introduction 5 | ------------ 6 | 7 | The point of this document is to describe all steps required to make a 8 | proper release of the libu2f-server project. 9 | 10 | Details 11 | ------- 12 | 13 | * Make sure your working environment is up to date. Confirm with: 14 | +git checkout master && git pull && git diff+. 15 | 16 | * Make sure the version number in +configure.ac+ has been incremented in +AC_INIT+. 17 | 18 | * Make sure the libtool shared library version has been incremented properly, see http://www.gnu.org/software/libtool/manual/html_node/Versioning.html Always increment +LT_REVISION+ on every release -- it makes it possible to have multiple releases installed concurrently which helps testing. 19 | 20 | * Make sure +NEWS+ describes all changes since the last release. Use https://github.com/Yubico/libu2f-server/commits/master to review. Running +make distcheck+ will also produce a Changelog file. 21 | 22 | * Change the '(unreleased)' part in NEWS to '(released 20XX-YY-ZZ)' and commit that with a note 'Version Q.P'. 23 | 24 | * Run +make release+. 25 | 26 | * Increment version number in +configure.ac+ and add a +NEWS+ template for the next release. 27 | -------------------------------------------------------------------------------- /m4/ld-version-script.m4: -------------------------------------------------------------------------------- 1 | # ld-version-script.m4 serial 3 2 | dnl Copyright (C) 2008-2013 Free Software Foundation, Inc. 3 | dnl This file is free software; the Free Software Foundation 4 | dnl gives unlimited permission to copy and/or distribute it, 5 | dnl with or without modifications, as long as this notice is preserved. 6 | 7 | dnl From Simon Josefsson 8 | 9 | # FIXME: The test below returns a false positive for mingw 10 | # cross-compiles, 'local:' statements does not reduce number of 11 | # exported symbols in a DLL. Use --disable-ld-version-script to work 12 | # around the problem. 13 | 14 | # gl_LD_VERSION_SCRIPT 15 | # -------------------- 16 | # Check if LD supports linker scripts, and define automake conditional 17 | # HAVE_LD_VERSION_SCRIPT if so. 18 | AC_DEFUN([gl_LD_VERSION_SCRIPT], 19 | [ 20 | AC_ARG_ENABLE([ld-version-script], 21 | AS_HELP_STRING([--enable-ld-version-script], 22 | [enable linker version script (default is enabled when possible)]), 23 | [have_ld_version_script=$enableval], []) 24 | if test -z "$have_ld_version_script"; then 25 | AC_MSG_CHECKING([if LD -Wl,--version-script works]) 26 | save_LDFLAGS="$LDFLAGS" 27 | LDFLAGS="$LDFLAGS -Wl,--version-script=conftest.map" 28 | cat > conftest.map < conftest.map </dev/null` in 227 | 'gcc (GCC) '[[0-3]].* | \ 228 | 'gcc (GCC) '4.[[0-7]].*) 229 | gl_manywarn_set="$gl_manywarn_set -fdiagnostics-show-option" 230 | gl_manywarn_set="$gl_manywarn_set -funit-at-a-time" 231 | ;; 232 | esac 233 | fi 234 | 235 | # Disable specific options as needed. 236 | if test "$gl_cv_cc_nomfi_needed" = yes; then 237 | gl_manywarn_set="$gl_manywarn_set -Wno-missing-field-initializers" 238 | fi 239 | 240 | if test "$gl_cv_cc_uninitialized_supported" = no; then 241 | gl_manywarn_set="$gl_manywarn_set -Wno-uninitialized" 242 | fi 243 | 244 | $1=$gl_manywarn_set 245 | ]) 246 | -------------------------------------------------------------------------------- /m4/warnings.m4: -------------------------------------------------------------------------------- 1 | # warnings.m4 serial 8 2 | dnl Copyright (C) 2008-2013 Free Software Foundation, Inc. 3 | dnl This file is free software; the Free Software Foundation 4 | dnl gives unlimited permission to copy and/or distribute it, 5 | dnl with or without modifications, as long as this notice is preserved. 6 | 7 | dnl From Simon Josefsson 8 | 9 | # gl_AS_VAR_APPEND(VAR, VALUE) 10 | # ---------------------------- 11 | # Provide the functionality of AS_VAR_APPEND if Autoconf does not have it. 12 | m4_ifdef([AS_VAR_APPEND], 13 | [m4_copy([AS_VAR_APPEND], [gl_AS_VAR_APPEND])], 14 | [m4_define([gl_AS_VAR_APPEND], 15 | [AS_VAR_SET([$1], [AS_VAR_GET([$1])$2])])]) 16 | 17 | 18 | # gl_COMPILER_OPTION_IF(OPTION, [IF-SUPPORTED], [IF-NOT-SUPPORTED], 19 | # [PROGRAM = AC_LANG_PROGRAM()]) 20 | # ----------------------------------------------------------------- 21 | # Check if the compiler supports OPTION when compiling PROGRAM. 22 | # 23 | # FIXME: gl_Warn must be used unquoted until we can assume Autoconf 24 | # 2.64 or newer. 25 | AC_DEFUN([gl_COMPILER_OPTION_IF], 26 | [AS_VAR_PUSHDEF([gl_Warn], [gl_cv_warn_[]_AC_LANG_ABBREV[]_$1])dnl 27 | AS_VAR_PUSHDEF([gl_Flags], [_AC_LANG_PREFIX[]FLAGS])dnl 28 | AC_CACHE_CHECK([whether _AC_LANG compiler handles $1], m4_defn([gl_Warn]), [ 29 | gl_save_compiler_FLAGS="$gl_Flags" 30 | gl_AS_VAR_APPEND(m4_defn([gl_Flags]), [" $gl_unknown_warnings_are_errors $1"]) 31 | AC_COMPILE_IFELSE([m4_default([$4], [AC_LANG_PROGRAM([])])], 32 | [AS_VAR_SET(gl_Warn, [yes])], 33 | [AS_VAR_SET(gl_Warn, [no])]) 34 | gl_Flags="$gl_save_compiler_FLAGS" 35 | ]) 36 | AS_VAR_IF(gl_Warn, [yes], [$2], [$3]) 37 | AS_VAR_POPDEF([gl_Flags])dnl 38 | AS_VAR_POPDEF([gl_Warn])dnl 39 | ]) 40 | 41 | # gl_UNKNOWN_WARNINGS_ARE_ERRORS 42 | # ------------------------------ 43 | # Clang doesn't complain about unknown warning options unless one also 44 | # specifies -Wunknown-warning-option -Werror. Detect this. 45 | AC_DEFUN([gl_UNKNOWN_WARNINGS_ARE_ERRORS], 46 | [gl_COMPILER_OPTION_IF([-Werror -Wunknown-warning-option], 47 | [gl_unknown_warnings_are_errors='-Wunknown-warning-option -Werror'], 48 | [gl_unknown_warnings_are_errors=])]) 49 | 50 | # gl_WARN_ADD(OPTION, [VARIABLE = WARN_CFLAGS], 51 | # [PROGRAM = AC_LANG_PROGRAM()]) 52 | # --------------------------------------------- 53 | # Adds parameter to WARN_CFLAGS if the compiler supports it when 54 | # compiling PROGRAM. For example, gl_WARN_ADD([-Wparentheses]). 55 | # 56 | # If VARIABLE is a variable name, AC_SUBST it. 57 | AC_DEFUN([gl_WARN_ADD], 58 | [AC_REQUIRE([gl_UNKNOWN_WARNINGS_ARE_ERRORS]) 59 | gl_COMPILER_OPTION_IF([$1], 60 | [gl_AS_VAR_APPEND(m4_if([$2], [], [[WARN_CFLAGS]], [[$2]]), [" $1"])], 61 | [], 62 | [$3]) 63 | m4_ifval([$2], 64 | [AS_LITERAL_IF([$2], [AC_SUBST([$2])])], 65 | [AC_SUBST([WARN_CFLAGS])])dnl 66 | ]) 67 | 68 | # Local Variables: 69 | # mode: autoconf 70 | # End: 71 | -------------------------------------------------------------------------------- /man/u2f-server.1.txt: -------------------------------------------------------------------------------- 1 | = LIBU2F-SERVER(1) 2 | :doctype: manpage 3 | :man source: libu2f-server 4 | :man version: 1.0.2 5 | 6 | == NAME 7 | libu2f-server - Yubico Universal 2nd Factor (U2F) Server Tool 8 | 9 | == SYNOPSIS 10 | *libu2f-server* [OPTIONS]... 11 | 12 | == OPTIONS 13 | *-h, --help*:: 14 | Print help and exit 15 | 16 | *-V, --version*:: 17 | Print version and exit 18 | 19 | *-o, --origin=STRING*:: 20 | Origin URL to use. 21 | 22 | *-i, --appid=STRING*:: 23 | Application ID to use. 24 | 25 | *-c, --challenge=STRING*:: 26 | Challenge (Base64-URL encoded) string to use. 27 | 28 | *-a, --action=ENUM*:: 29 | Action to take. (possible values="register", 30 | "authenticate") 31 | 32 | *-k, --key-handle=STRING*:: 33 | A file containing a key-handle 34 | 35 | *-p, --user-key=STRING*:: 36 | A file containing the public user-key 37 | 38 | *-d, --debug*:: 39 | Print debug information to standard error 40 | (default=off) 41 | 42 | == REPORTING BUGS 43 | Report bugs at . 44 | 45 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Yubico AB 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are 6 | # met: 7 | # 8 | # # Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # 11 | # # Redistributions in binary form must reproduce the above 12 | # copyright notice, this list of conditions and the following 13 | # disclaimer in the documentation and/or other materials provided 14 | # with the distribution. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | AM_CFLAGS = $(WARN_CFLAGS) 29 | AM_CPPFLAGS = -I$(srcdir)/.. -I$(builddir)/.. -I$(builddir)/../u2f-server 30 | 31 | bin_PROGRAMS = u2f-server 32 | 33 | u2f_server_SOURCES = u2f-server.c 34 | u2f_server_SOURCES += cmdline.ggo cmdline.c cmdline.h 35 | u2f_server_LDADD = ../u2f-server/libu2f-server.la 36 | 37 | cmdline.c cmdline.h: cmdline.ggo Makefile.am 38 | gengetopt --no-handle-help --input $^ 39 | 40 | BUILT_SOURCES = cmdline.c cmdline.h 41 | MAINTAINERCLEANFILES = $(BUILT_SOURCES) 42 | 43 | dist_man_MANS = $(top_srcdir)/man/u2f-server.1 44 | DISTCLEANFILES = $(dist_man_MANS) 45 | 46 | $(top_srcdir)/man/u2f-server.1: $(srcdir)/u2f-server.c $(srcdir)/cmdline.ggo $(top_srcdir)/configure.ac | $(builddir)/u2f-server$(EXEEXT) 47 | $(HELP2MAN) \ 48 | --output=$(top_builddir)/man/$@ $(builddir)/u2f-server$(EXEEXT) \ 49 | --name="Yubico Universal 2nd Factor (U2F) Server Tool" \ 50 | --no-info 51 | if ENABLE_H2A 52 | $(HELP2ADOC) \ 53 | -e $(builddir)/u2f-server$(EXEEXT) \ 54 | --name="Yubico Universal 2nd Factor (U2F) Server Tool" \ 55 | > $(top_builddir)/man/$@.txt 56 | endif 57 | 58 | clean-local: 59 | rm -f $(top_builddir)/man/*.[1-9] 60 | 61 | if ENABLE_COV 62 | AM_CFLAGS += --coverage 63 | AM_LDFLAGS = --coverage 64 | endif 65 | -------------------------------------------------------------------------------- /src/cmdline.ggo: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Yubico AB 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are 6 | # met: 7 | # 8 | # # Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # 11 | # # Redistributions in binary form must reproduce the above 12 | # copyright notice, this list of conditions and the following 13 | # disclaimer in the documentation and/or other materials provided 14 | # with the distribution. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | purpose "Perform U2F server-side operations on the command line. Writes challenge to standard output and read responses from standard input." 29 | 30 | option "origin" o "Origin URL to use." string optional 31 | option "appid" i "Application ID to use." string optional 32 | option "challenge" c "Challenge (Base64-URL encoded) string to use." string optional 33 | option "action" a "Action to take." values="register","authenticate" enum 34 | option "key-handle" k "A file containing a key-handle" string optional 35 | option "user-key" p "A file containing the public user-key" string optional 36 | option "debug" d "Print debug information to standard error" flag off 37 | option "x509cert" x "A file to write the registration attestation certificate to" string optional 38 | -------------------------------------------------------------------------------- /src/u2f-server.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Yubico AB 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above 13 | * copyright notice, this list of conditions and the following 14 | * disclaimer in the documentation and/or other materials provided 15 | * with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include "cmdline.h" 40 | 41 | int main(int argc, char *argv[]) 42 | { 43 | int exit_code = EXIT_FAILURE; 44 | struct gengetopt_args_info args_info; 45 | char buf[8192]; 46 | char *p; 47 | u2fs_ctx_t *ctx; 48 | u2fs_reg_res_t *reg_result; 49 | u2fs_auth_res_t *auth_result; 50 | u2fs_rc rc; 51 | int len; 52 | if (cmdline_parser(argc, argv, &args_info) != 0) 53 | exit(EXIT_FAILURE); 54 | if (args_info.help_given) { 55 | cmdline_parser_print_help(); 56 | printf 57 | ("\nReport bugs at .\n"); 58 | exit(EXIT_SUCCESS); 59 | } 60 | 61 | if (!args_info.origin_given) { 62 | fprintf(stderr, "error: An origin must be specified with -o\n"); 63 | exit(EXIT_FAILURE); 64 | } else if (strncmp("http://", args_info.origin_arg, 7) != 0 && 65 | strncmp("https://", args_info.origin_arg, 8) != 0 && 66 | strncmp("pam://", args_info.origin_arg, 6) != 0) { 67 | fprintf(stderr, "error: origin must be http, https or pam\n"); 68 | exit(EXIT_FAILURE); 69 | } 70 | 71 | if (!args_info.appid_given) { 72 | fprintf(stderr, "error: An appId must be specified with -i\n"); 73 | exit(EXIT_FAILURE); 74 | } 75 | 76 | if (args_info.challenge_arg 77 | && strlen(args_info.challenge_arg) != U2FS_CHALLENGE_B64U_LEN) { 78 | fprintf(stderr, "warning: challenge should be %d characters long\n", 79 | U2FS_CHALLENGE_B64U_LEN); 80 | exit(EXIT_FAILURE); 81 | } 82 | rc = u2fs_global_init(args_info.debug_flag ? U2FS_DEBUG : 0); 83 | if (rc != U2FS_OK) { 84 | fprintf(stderr, "error: u2fs_global_init (%d): %s\n", rc, 85 | u2fs_strerror(rc)); 86 | exit(EXIT_FAILURE); 87 | } 88 | rc = u2fs_init(&ctx); 89 | if (rc != U2FS_OK) { 90 | fprintf(stderr, "error: u2fs_init (%d): %s\n", rc, u2fs_strerror(rc)); 91 | exit(EXIT_FAILURE); 92 | } 93 | if (args_info.action_arg == action_arg_authenticate) { 94 | if (!args_info.key_handle_given) { 95 | fprintf(stderr, 96 | "error: Authentication action requires a key-handle\n"); 97 | exit(EXIT_FAILURE); 98 | } else { 99 | FILE *fp; 100 | if ((fp = fopen(args_info.key_handle_arg, "rb")) == NULL) { 101 | perror("open"); 102 | exit(EXIT_FAILURE); 103 | } 104 | 105 | len = fread(buf, sizeof(char), sizeof(buf) - 1, fp); 106 | buf[len] = '\0'; 107 | if (len == 0 && !feof(stdin) 108 | && ferror(stdin)) { 109 | perror("read"); 110 | exit(EXIT_FAILURE); 111 | } 112 | fclose(fp); 113 | } 114 | rc = u2fs_set_keyHandle(ctx, buf); 115 | if (rc != U2FS_OK) { 116 | fprintf(stderr, "error: u2fs_set_keyHandle (%d): %s\n", rc, 117 | u2fs_strerror(rc)); 118 | exit(EXIT_FAILURE); 119 | } 120 | } 121 | 122 | if (args_info.action_arg == action_arg_authenticate) { 123 | if (!args_info.user_key_given) { 124 | fprintf(stderr, 125 | "error: Authentication action requires a user-key\n"); 126 | exit(EXIT_FAILURE); 127 | } else { 128 | FILE *fp; 129 | if ((fp = fopen(args_info.user_key_arg, "rb")) == NULL) { 130 | perror("open"); 131 | exit(EXIT_FAILURE); 132 | } 133 | 134 | len = fread(buf, sizeof(char), sizeof(buf) - 1, fp); 135 | buf[len] = '\0'; 136 | if (len == 0 && !feof(stdin) 137 | && ferror(stdin)) { 138 | perror("read"); 139 | exit(EXIT_FAILURE); 140 | } 141 | fclose(fp); 142 | } 143 | 144 | rc = u2fs_set_publicKey(ctx, (unsigned char *) buf); 145 | if (rc != U2FS_OK) { 146 | fprintf(stderr, "error: u2fs_set_publicKey (%d): %s\n", rc, 147 | u2fs_strerror(rc)); 148 | exit(EXIT_FAILURE); 149 | } 150 | } 151 | 152 | rc = u2fs_set_origin(ctx, args_info.origin_arg); 153 | if (rc != U2FS_OK) { 154 | printf("error: u2fs_set_origin (%d): %s\n", rc, u2fs_strerror(rc)); 155 | exit(EXIT_FAILURE); 156 | } 157 | 158 | rc = u2fs_set_appid(ctx, args_info.appid_arg); 159 | if (rc != U2FS_OK) { 160 | fprintf(stderr, "error: u2fs_set_appid (%d): %s\n", rc, 161 | u2fs_strerror(rc)); 162 | exit(EXIT_FAILURE); 163 | } 164 | 165 | if (args_info.challenge_arg) { 166 | rc = u2fs_set_challenge(ctx, args_info.challenge_arg); 167 | if (rc != U2FS_OK) { 168 | fprintf(stderr, "error: u2fs_set_challenge (%d): %s\n", rc, 169 | u2fs_strerror(rc)); 170 | exit(EXIT_FAILURE); 171 | } 172 | } 173 | switch (args_info.action_arg) { 174 | case action_arg_register: 175 | rc = u2fs_registration_challenge(ctx, &p); 176 | break; 177 | case action_arg_authenticate: 178 | rc = u2fs_authentication_challenge(ctx, &p); 179 | break; 180 | case action__NULL: 181 | default: 182 | fprintf(stderr, "error: unknown action.\n"); 183 | goto done; 184 | } 185 | if (rc != U2FS_OK) { 186 | fprintf(stderr, "error (%d): %s\n", rc, u2fs_strerror(rc)); 187 | goto done; 188 | } 189 | printf("%s\n", p); 190 | len = fread(buf, 1, sizeof(buf) - 1, stdin); 191 | buf[len] = '\0'; 192 | if (len == 0 && !feof(stdin) 193 | && ferror(stdin)) { 194 | perror("read"); 195 | exit(EXIT_FAILURE); 196 | } 197 | switch (args_info.action_arg) { 198 | case action_arg_register: 199 | rc = u2fs_registration_verify(ctx, buf, ®_result); 200 | 201 | if (rc == U2FS_OK) 202 | printf("Registration successful\n"); 203 | else { 204 | fprintf(stderr, "error: (%d) %s\n", rc, u2fs_strerror(rc)); 205 | exit(EXIT_FAILURE); 206 | } 207 | 208 | if (args_info.x509cert_given) { 209 | const char *pem = u2fs_get_registration_attestation(reg_result); 210 | FILE *fp = fopen(args_info.x509cert_arg, "w"); 211 | if (fp == NULL) { 212 | perror("fopen"); 213 | exit(EXIT_FAILURE); 214 | } 215 | size_t fwlen = fwrite(pem, 1, strlen(pem), fp); 216 | if (fwlen != strlen(pem)) { 217 | perror("fwrite"); 218 | exit(EXIT_FAILURE); 219 | } 220 | fclose(fp); 221 | } 222 | 223 | if (args_info.key_handle_given) { 224 | FILE *fp; 225 | if ((fp = fopen(args_info.key_handle_arg, "wb")) == NULL) { 226 | perror("open"); 227 | exit(EXIT_FAILURE); 228 | } 229 | if (fwrite 230 | (u2fs_get_registration_keyHandle(reg_result), sizeof(char), 231 | strlen(u2fs_get_registration_keyHandle(reg_result)), 232 | fp) != strlen(u2fs_get_registration_keyHandle(reg_result))) { 233 | perror("write"); 234 | exit(EXIT_FAILURE); 235 | } 236 | fclose(fp); 237 | } else { 238 | fprintf(stderr, "KeyHandle not saved!. Rerun with -k\n"); 239 | } 240 | 241 | if (rc == U2FS_OK && args_info.user_key_given) { 242 | FILE *fp; 243 | const char *k = u2fs_get_registration_publicKey(reg_result); 244 | 245 | if ((fp = fopen(args_info.user_key_arg, "wb")) == NULL) { 246 | perror("open"); 247 | exit(EXIT_FAILURE); 248 | } 249 | 250 | if (fwrite(k, sizeof(unsigned char), U2FS_PUBLIC_KEY_LEN, fp) != 251 | U2FS_PUBLIC_KEY_LEN) { 252 | perror("write"); 253 | exit(EXIT_FAILURE); 254 | } 255 | 256 | fclose(fp); 257 | } else { 258 | fprintf(stderr, "User key not saved!. Rerun with -p\n"); 259 | } 260 | break; 261 | case action_arg_authenticate: 262 | rc = u2fs_authentication_verify(ctx, buf, &auth_result); 263 | if (rc == U2FS_OK) { 264 | u2fs_rc verified; 265 | uint32_t counter; 266 | uint8_t user_presence; 267 | rc = u2fs_get_authentication_result(auth_result, &verified, &counter, 268 | &user_presence); 269 | if (verified == U2FS_OK) { 270 | printf 271 | ("Successful authentication, counter: %d, user presence %d\n", 272 | counter, user_presence); 273 | } else 274 | fprintf(stderr, "Authentication failed: %s\n", u2fs_strerror(rc)); 275 | } else if (rc != U2FS_OK) { 276 | fprintf(stderr, "error: u2fs_authentication_verify (%d): %s\n", rc, 277 | u2fs_strerror(rc)); 278 | exit(EXIT_FAILURE); 279 | } 280 | break; 281 | case action__NULL: 282 | default: 283 | fprintf(stderr, "error: unknown action.\n"); 284 | goto done; 285 | } 286 | if (rc != U2FS_OK) { 287 | fprintf(stderr, "error (%d): %s\n", rc, u2fs_strerror(rc)); 288 | goto done; 289 | } 290 | exit_code = EXIT_SUCCESS; 291 | done:u2fs_done(ctx); 292 | u2fs_global_done(); 293 | exit(exit_code); 294 | } 295 | -------------------------------------------------------------------------------- /tests/Makefile.am: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Yubico AB 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are 6 | # met: 7 | # 8 | # # Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # 11 | # # Redistributions in binary form must reproduce the above 12 | # copyright notice, this list of conditions and the following 13 | # disclaimer in the documentation and/or other materials provided 14 | # with the distribution. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | AM_CFLAGS = $(WARN_CFLAGS) 29 | AM_CFLAGS += -DMAKE_CHECK 30 | AM_CPPFLAGS=-I$(srcdir)/.. -I$(builddir)/.. 31 | AM_CPPFLAGS+=$(LIBSSL_CFLAGS) $(LIBCRYPTO_CFLAGS) $(LIBCHECK_CFLAGS) 32 | 33 | AM_LDFLAGS = -no-install 34 | 35 | if ENABLE_COV 36 | AM_CFLAGS += --coverage 37 | AM_LDFLAGS += --coverage 38 | endif 39 | 40 | LDADD = $(top_builddir)/u2f-server/libu2f-server.la 41 | LDADD += $(CHECK_LIBS) $(LIBSSL_LIBS) $(LIBCRYPTO_LIBS) $(LIBCHECK_LIBS) 42 | 43 | check_PROGRAMS = basic core openssl 44 | dist_check_SCRIPTS = u2f-server-test.sh 45 | TESTS = $(check_PROGRAMS) u2f-server-test.sh 46 | 47 | TEST_EXTENSIONS = .sh 48 | SH_LOG_COMPILER = $(SHELL) 49 | AM_SH_LOG_FLAGS = 50 | -------------------------------------------------------------------------------- /tests/basic.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Yubico AB 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above 13 | * copyright notice, this list of conditions and the following 14 | * disclaimer in the documentation and/or other materials provided 15 | * with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include 38 | 39 | #define INVALID_ERROR_CODE 1 40 | #define OOB_ERROR_CODE -100 41 | 42 | START_TEST(test_version) 43 | { 44 | ck_assert_msg(strcmp(U2FS_VERSION_STRING, u2fs_check_version(NULL)) == 0, 45 | "Was expecting version %s, but found version %s\n", 46 | U2FS_VERSION_STRING, u2fs_check_version(NULL)); 47 | 48 | ck_assert_msg(u2fs_check_version(U2FS_VERSION_STRING) != NULL, 49 | "Version NULL?\n"); 50 | 51 | ck_assert_msg(u2fs_check_version("99.99.99") == NULL, 52 | "Version not NULL?\n"); 53 | 54 | } 55 | 56 | END_TEST START_TEST(test_utils) 57 | { 58 | ck_assert_msg(u2fs_global_init(U2FS_DEBUG) == U2FS_OK, 59 | "u2fs_global_init rc %d\n, rc"); 60 | 61 | ck_assert_msg(u2fs_strerror(U2FS_OK) != NULL, "u2fs_strerror NULL\n"); 62 | 63 | ck_assert_msg(u2fs_strerror(INVALID_ERROR_CODE) != NULL, 64 | "u2fs_strerror NULL\n"); 65 | 66 | ck_assert_msg(u2fs_strerror(OOB_ERROR_CODE) != NULL, 67 | "u2fs_strerror NULL\n"); 68 | 69 | { 70 | const char *s; 71 | s = u2fs_strerror_name(U2FS_OK); 72 | ck_assert_msg(s != NULL 73 | && strcmp(s, "U2FS_OK") == 0, "u2fs_strerror_name %s\n", 74 | s); 75 | 76 | } 77 | } 78 | 79 | END_TEST START_TEST(test_init) 80 | { 81 | 82 | u2fs_ctx_t *ctx; 83 | 84 | ck_assert_int_eq(u2fs_init(&ctx), U2FS_OK); 85 | 86 | u2fs_done(ctx); 87 | u2fs_global_done(); 88 | 89 | } 90 | 91 | END_TEST Suite *basic_suite(void) 92 | { 93 | Suite *s; 94 | TCase *tc_basic; 95 | 96 | s = suite_create("u2fs_basic"); 97 | 98 | /* Basic test case */ 99 | tc_basic = tcase_create("Basic"); 100 | 101 | tcase_add_test(tc_basic, test_version); 102 | tcase_add_test(tc_basic, test_utils); 103 | tcase_add_test(tc_basic, test_init); 104 | 105 | suite_add_tcase(s, tc_basic); 106 | 107 | return s; 108 | } 109 | 110 | 111 | int main(void) 112 | { 113 | int number_failed; 114 | Suite *s; 115 | SRunner *sr; 116 | 117 | s = basic_suite(); 118 | sr = srunner_create(s); 119 | 120 | srunner_run_all(sr, CK_NORMAL); 121 | number_failed = srunner_ntests_failed(sr); 122 | srunner_free(sr); 123 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 124 | } 125 | -------------------------------------------------------------------------------- /tests/core.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Yubico AB 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above 13 | * copyright notice, this list of conditions and the following 14 | * disclaimer in the documentation and/or other materials provided 15 | * with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | START_TEST(test_create) 40 | { 41 | 42 | u2fs_ctx_t *ctx; 43 | 44 | ck_assert_int_eq(u2fs_global_init(U2FS_DEBUG), U2FS_OK); 45 | ck_assert_int_eq(u2fs_init(&ctx), U2FS_OK); 46 | 47 | u2fs_done(ctx); 48 | u2fs_global_done(); 49 | 50 | } 51 | 52 | END_TEST START_TEST(set_challenge) 53 | { 54 | 55 | u2fs_ctx_t *ctx; 56 | 57 | ck_assert_int_eq(u2fs_global_init(U2FS_DEBUG), U2FS_OK); 58 | ck_assert_int_eq(u2fs_init(&ctx), U2FS_OK); 59 | 60 | ck_assert_int_eq(u2fs_set_challenge(ctx, ""), U2FS_CHALLENGE_ERROR); 61 | ck_assert_int_eq(u2fs_set_challenge(ctx, NULL), U2FS_MEMORY_ERROR); 62 | ck_assert_int_eq(u2fs_set_challenge 63 | (ctx, "dDwRsjdFoPHZ5Qg2fHQsFba0NKl-F1hxjJ3uLLk5gbA"), 64 | U2FS_OK); 65 | ck_assert_str_eq(ctx->challenge, 66 | "dDwRsjdFoPHZ5Qg2fHQsFba0NKl-F1hxjJ3uLLk5gbA"); 67 | 68 | ck_assert_int_eq(strlen(ctx->challenge), U2FS_CHALLENGE_B64U_LEN); 69 | char *s = strdup(ctx->challenge); 70 | u2fs_done(ctx); 71 | ck_assert_int_eq(u2fs_init(&ctx), U2FS_OK); 72 | ck_assert_int_eq(strlen(ctx->challenge), 0); 73 | ck_assert_str_ne(ctx->challenge, s); 74 | 75 | free(s); 76 | s = NULL; 77 | 78 | u2fs_done(ctx); 79 | u2fs_global_done(); 80 | 81 | } 82 | 83 | END_TEST START_TEST(set_keyhandle) 84 | { 85 | 86 | u2fs_ctx_t *ctx; 87 | 88 | ck_assert_int_eq(u2fs_global_init(U2FS_DEBUG), U2FS_OK); 89 | ck_assert_int_eq(u2fs_init(&ctx), U2FS_OK); 90 | 91 | ck_assert_int_eq(u2fs_set_keyHandle(ctx, NULL), U2FS_MEMORY_ERROR); 92 | ck_assert_int_eq(u2fs_set_keyHandle 93 | (ctx, 94 | "kAbb2p57pxHg2mY8y_Kgcdc7jnnAoncJm8vOgqfigyWTvPGFlvxA04ULD9IJ-KpSyn733LRbJ-CG573N9jCY1g"), 95 | U2FS_OK); 96 | ck_assert_str_eq(ctx->keyHandle, 97 | "kAbb2p57pxHg2mY8y_Kgcdc7jnnAoncJm8vOgqfigyWTvPGFlvxA04ULD9IJ-KpSyn733LRbJ-CG573N9jCY1g"); 98 | 99 | u2fs_done(ctx); 100 | u2fs_global_done(); 101 | 102 | } 103 | 104 | END_TEST START_TEST(set_origin) 105 | { 106 | 107 | u2fs_ctx_t *ctx; 108 | 109 | ck_assert_int_eq(u2fs_global_init(U2FS_DEBUG), U2FS_OK); 110 | ck_assert_int_eq(u2fs_init(&ctx), U2FS_OK); 111 | 112 | ck_assert_int_eq(u2fs_set_origin(ctx, NULL), U2FS_MEMORY_ERROR); 113 | ck_assert_int_eq(u2fs_set_origin(ctx, "http://example.com"), U2FS_OK); 114 | ck_assert_str_eq(ctx->origin, "http://example.com"); 115 | ck_assert_int_eq(u2fs_set_origin(ctx, "https://test.org"), U2FS_OK); 116 | ck_assert_str_eq(ctx->origin, "https://test.org"); 117 | 118 | u2fs_done(ctx); 119 | u2fs_global_done(); 120 | 121 | } 122 | 123 | END_TEST START_TEST(set_appid) 124 | { 125 | 126 | u2fs_ctx_t *ctx; 127 | 128 | ck_assert_int_eq(u2fs_global_init(U2FS_DEBUG), U2FS_OK); 129 | ck_assert_int_eq(u2fs_init(&ctx), U2FS_OK); 130 | 131 | ck_assert_int_eq(u2fs_set_appid(ctx, NULL), U2FS_MEMORY_ERROR); 132 | ck_assert_int_eq(u2fs_set_appid(ctx, "http://example.com"), U2FS_OK); 133 | ck_assert_str_eq(ctx->appid, "http://example.com"); 134 | ck_assert_int_eq(u2fs_set_appid(ctx, "https://test.org"), U2FS_OK); 135 | ck_assert_str_eq(ctx->appid, "https://test.org"); 136 | 137 | u2fs_done(ctx); 138 | u2fs_global_done(); 139 | 140 | } 141 | 142 | END_TEST START_TEST(set_publicKey) 143 | { 144 | 145 | u2fs_ctx_t *ctx; 146 | unsigned char userkey_dat[] = { 147 | 0x04, 0x14, 0xc3, 0x2e, 0x41, 0x0b, 0x30, 0x9d, 0x6e, 0x93, 0x7f, 0x8b, 148 | 0x5d, 0x81, 0xf9, 0xe5, 0x64, 0xfd, 0x11, 0x2c, 0xe5, 0xfe, 0xf0, 0x10, 149 | 0x5e, 0xfb, 0xec, 0xd5, 0x55, 0x54, 0x52, 0x25, 0x25, 0xe4, 0x54, 0x29, 150 | 0x0f, 0xf4, 0x2e, 0xa1, 0xd8, 0x77, 0x19, 0x36, 0x12, 0xe3, 0x6e, 0x39, 151 | 0x17, 0x91, 0x24, 0xb5, 0x93, 0x8e, 0xe0, 0xfe, 0xf3, 0x69, 0xac, 0xb9, 152 | 0x4c, 0x37, 0x97, 0x83, 0xcb, 0x15, 0x40 153 | }; 154 | 155 | ck_assert_int_eq(u2fs_global_init(U2FS_DEBUG), U2FS_OK); 156 | ck_assert_int_eq(u2fs_init(&ctx), U2FS_OK); 157 | ck_assert_int_eq(u2fs_set_publicKey(ctx, userkey_dat), U2FS_OK); 158 | /* TODO: how to check it was imported ok? This test is now a bit silly. */ 159 | 160 | u2fs_done(ctx); 161 | u2fs_global_done(); 162 | 163 | } 164 | 165 | END_TEST START_TEST(registration_verify_ok) 166 | { 167 | 168 | u2fs_ctx_t *ctx; 169 | 170 | char *reg_response = 171 | "{ \"registrationData\": \"BQRcbdE4PHGRaJUTK9hY4GrX_jZa5eWgjJK6\ 172 | IfwezrndHvQi7QQtYA2qAg4NrebNkSCoOwJ0V1PzLlP1Wr_Oku_0QKfeNR0Ei4_\ 173 | I40GCo5xjm4Q7hnZwzXQ5f5vjtnx7xIqCZ-z7GOGExeouBXxaMgleYpX7xMR6Y9\ 174 | wa_qzLLTAr6IcwggIbMIIBBaADAgECAgR1o_Z1MAsGCSqGSIb3DQEBCzAuMSwwK\ 175 | gYDVQQDEyNZdWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0x\ 176 | NDA4MDEwMDAwMDBaGA8yMDUwMDkwNDAwMDAwMFowKjEoMCYGA1UEAwwfWXViaWN\ 177 | vIFUyRiBFRSBTZXJpYWwgMTk3MzY3OTczMzBZMBMGByqGSM49AgEGCCqGSM49Aw\ 178 | EHA0IABBmjfkNqa2mXzVh2ZxuES5coCvvENxDMDLmfd-0ACG0Fu7wR4ZTjKd9KA\ 179 | uidySpfona5csGmlM0Te_Zu35h_wwujEjAQMA4GCisGAQQBgsQKAQIEADALBgkq\ 180 | hkiG9w0BAQsDggEBAb0tuI0-CzSxBg4cAlyD6UyT4cKyJZGVhWdtPgj_mWepT3T\ 181 | u9jXtdgA5F3jfZtTc2eGxuS-PPvqRAkZd40AXgM8A0YaXPwlT4s0RUTY9Y8aAQz\ 182 | QZeAHuZk3lKKd_LUCg5077dzdt90lC5eVTEduj6cOnHEqnOr2Cv75FuiQXX7QkG\ 183 | QxtoD-otgvhZ2Fjk29o7Iy9ik7ewHGXOfoVw_ruGWi0YfXBTuqEJ6H666vvMN4B\ 184 | ZWHtzhC0k5ceQslB9Xdntky-GQgDqNkkBf32GKwAFT9JJrkO2BfsB-wfBrTiHr0\ 185 | AABYNTNKTceA5dtR3UVpI492VUWQbY3YmWUUfKTI7fM4wRQIhAN3c-VHubCCkUt\ 186 | ZXfWL1aiEXU1qWRiM_ayKmWLUafyFbAiARTwlVocoamd9S-cYBosRKso_XGAPzA\ 187 | edzpuE2tEjp1g==\", \"clientData\": \"eyAiY2hhbGxlbmdlIjogIllTMT\ 188 | ludV9ZWWpnczI5WndrU3dRb2JyNzhPaURXRnoxeXFZZW85WUpmQnciLCAib3JpZ\ 189 | 2luIjogImh0dHA6XC9cL2RlbW8ueXViaWNvLmNvbSIsICJ0eXAiOiAibmF2aWdh\ 190 | dG9yLmlkLmZpbmlzaEVucm9sbG1lbnQiIH0=\" }"; 191 | 192 | u2fs_reg_res_t *res = NULL;; 193 | const char *p; 194 | 195 | unsigned char userkey_dat[] = { 196 | 0x04, 0x5c, 0x6d, 0xd1, 0x38, 0x3c, 0x71, 0x91, 0x68, 0x95, 0x13, 0x2b, 197 | 0xd8, 0x58, 0xe0, 0x6a, 0xd7, 0xfe, 0x36, 0x5a, 0xe5, 0xe5, 0xa0, 198 | 0x8c, 0x92, 0xba, 0x21, 0xfc, 0x1e, 0xce, 0xb9, 0xdd, 0x1e, 0xf4, 199 | 0x22, 0xed, 0x04, 0x2d, 0x60, 0x0d, 0xaa, 0x02, 0x0e, 0x0d, 0xad, 200 | 0xe6, 0xcd, 0x91, 0x20, 0xa8, 0x3b, 0x02, 0x74, 0x57, 0x53, 0xf3, 201 | 0x2e, 0x53, 0xf5, 0x5a, 0xbf, 0xce, 0x92, 0xef, 0xf4 202 | }; 203 | 204 | ck_assert_int_eq(u2fs_global_init(U2FS_DEBUG), U2FS_OK); 205 | ck_assert_int_eq(u2fs_init(&ctx), U2FS_OK); 206 | ck_assert_int_eq(u2fs_set_appid(ctx, "http://demo.yubico.com"), U2FS_OK); 207 | ck_assert_int_eq(u2fs_set_origin(ctx, "http://demo.yubico.com"), 208 | U2FS_OK); 209 | ck_assert_int_eq(u2fs_set_challenge 210 | (ctx, "YS19nu_YYjgs29ZwkSwQobr78OiDWFz1yqYeo9YJfBw"), 211 | U2FS_OK); 212 | ck_assert_int_eq(u2fs_registration_verify(ctx, reg_response, &res), 213 | U2FS_OK); 214 | 215 | ck_assert_str_eq(u2fs_get_registration_keyHandle(res), 216 | "p941HQSLj8jjQYKjnGObhDuGdnDNdDl_m-O2fHvEioJn7PsY4YT" 217 | "F6i4FfFoyCV5ilfvExHpj3Br-rMstMCvohw"); 218 | 219 | p = u2fs_get_registration_publicKey(res); 220 | ck_assert_int_eq(memcmp(p, userkey_dat, U2FS_PUBLIC_KEY_LEN), 0); 221 | 222 | ck_assert_str_eq(u2fs_get_registration_attestation(res), 223 | "-----BEGIN CERTIFICATE-----\n" 224 | "MIICGzCCAQWgAwIBAgIEdaP2dTALBgkqhkiG9w0BAQswLjEsMCoGA1UEAxMjWXVi\n" 225 | "aWNvIFUyRiBSb290IENBIFNlcmlhbCA0NTcyMDA2MzEwIBcNMTQwODAxMDAwMDAw\n" 226 | "WhgPMjA1MDA5MDQwMDAwMDBaMCoxKDAmBgNVBAMMH1l1YmljbyBVMkYgRUUgU2Vy\n" 227 | "aWFsIDE5NzM2Nzk3MzMwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQZo35Damtp\n" 228 | "l81YdmcbhEuXKAr7xDcQzAy5n3ftAAhtBbu8EeGU4ynfSgLonckqX6J2uXLBppTN\n" 229 | "E3v2bt+Yf8MLoxIwEDAOBgorBgEEAYLECgECBAAwCwYJKoZIhvcNAQELA4IBAQG9\n" 230 | "LbiNPgs0sQYOHAJcg+lMk+HCsiWRlYVnbT4I/5lnqU907vY17XYAORd432bU3Nnh\n" 231 | "sbkvjz76kQJGXeNAF4DPANGGlz8JU+LNEVE2PWPGgEM0GXgB7mZN5Sinfy1AoOdO\n" 232 | "+3c3bfdJQuXlUxHbo+nDpxxKpzq9gr++RbokF1+0JBkMbaA/qLYL4WdhY5NvaOyM\n" 233 | "vYpO3sBxlzn6FcP67hlotGH1wU7qhCeh+uur7zDeAWVh7c4QtJOXHkLJQfV3Z7ZM\n" 234 | "vhkIA6jZJAX99hisABU/SSa5DtgX7AfsHwa04h69AAAWDUzSk3HgOXbUd1FaSOPd\n" 235 | "lVFkG2N2JllFHykyO3zO\n" 236 | "-----END CERTIFICATE-----\n"); 237 | 238 | u2fs_free_reg_res(res); 239 | u2fs_done(ctx); 240 | u2fs_global_done(); 241 | 242 | } 243 | 244 | END_TEST START_TEST(registration_challenge_error) 245 | { 246 | 247 | u2fs_ctx_t *ctx; 248 | 249 | char *reg_response = 250 | "{ \"registrationData\": \"BQRcbdE4PHGRaJUTK9hY4GrX_jZa5eWgjJK6\ 251 | IfwezrndHvQi7QQtYA2qAg4NrebNkSCoOwJ0V1PzLlP1Wr_Oku_0QKfeNR0Ei4_\ 252 | I40GCo5xjm4Q7hnZwzXQ5f5vjtnx7xIqCZ-z7GOGExeouBXxaMgleYpX7xMR6Y9\ 253 | wa_qzLLTAr6IcwggIbMIIBBaADAgECAgR1o_Z1MAsGCSqGSIb3DQEBCzAuMSwwK\ 254 | gYDVQQDEyNZdWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0x\ 255 | NDA4MDEwMDAwMDBaGA8yMDUwMDkwNDAwMDAwMFowKjEoMCYGA1UEAwwfWXViaWN\ 256 | vIFUyRiBFRSBTZXJpYWwgMTk3MzY3OTczMzBZMBMGByqGSM49AgEGCCqGSM49Aw\ 257 | EHA0IABBmjfkNqa2mXzVh2ZxuES5coCvvENxDMDLmfd-0ACG0Fu7wR4ZTjKd9KA\ 258 | uidySpfona5csGmlM0Te_Zu35h_wwujEjAQMA4GCisGAQQBgsQKAQIEADALBgkq\ 259 | hkiG9w0BAQsDggEBAb0tuI0-CzSxBg4cAlyD6UyT4cKyJZGVhWdtPgj_mWepT3T\ 260 | u9jXtdgA5F3jfZtTc2eGxuS-PPvqRAkZd40AXgM8A0YaXPwlT4s0RUTY9Y8aAQz\ 261 | QZeAHuZk3lKKd_LUCg5077dzdt90lC5eVTEduj6cOnHEqnOr2Cv75FuiQXX7QkG\ 262 | QxtoD-otgvhZ2Fjk29o7Iy9ik7ewHGXOfoVw_ruGWi0YfXBTuqEJ6H666vvMN4B\ 263 | ZWHtzhC0k5ceQslB9Xdntky-GQgDqNkkBf32GKwAFT9JJrkO2BfsB-wfBrTiHr0\ 264 | AABYNTNKTceA5dtR3UVpI492VUWQbY3YmWUUfKTI7fM4wRQIhAN3c-VHubCCkUt\ 265 | ZXfWL1aiEXU1qWRiM_ayKmWLUafyFbAiARTwlVocoamd9S-cYBosRKso_XGAPzA\ 266 | edzpuE2tEjp1g==\", \"clientData\": \"eyAiY2hhbGxlbmdlIjogIllTMT\ 267 | ludV9ZWWpnczI5WndrU3dRb2JyNzhPaURXRnoxeXFZZW85WUpmQnciLCAib3JpZ\ 268 | 2luIjogImh0dHA6XC9cL2RlbW8ueXViaWNvLmNvbSIsICJ0eXAiOiAibmF2aWdh\ 269 | dG9yLmlkLmZpbmlzaEVucm9sbG1lbnQiIH0=\" }"; 270 | 271 | u2fs_reg_res_t *res = NULL;; 272 | 273 | ck_assert_int_eq(u2fs_global_init(U2FS_DEBUG), U2FS_OK); 274 | ck_assert_int_eq(u2fs_init(&ctx), U2FS_OK); 275 | ck_assert_int_eq(u2fs_set_appid(ctx, "http://demo.yubico.com"), U2FS_OK); 276 | ck_assert_int_eq(u2fs_set_origin(ctx, "http://demo.yubico.com"), 277 | U2FS_OK); 278 | ck_assert_int_eq(u2fs_set_challenge 279 | (ctx, "0000000000000000000000000000000000000000000"), 280 | U2FS_OK); 281 | ck_assert_int_eq(u2fs_registration_verify(ctx, reg_response, &res), 282 | U2FS_CHALLENGE_ERROR); 283 | 284 | u2fs_free_reg_res(res); 285 | u2fs_done(ctx); 286 | u2fs_global_done(); 287 | 288 | } 289 | 290 | END_TEST START_TEST(registration_origin_error) 291 | { 292 | 293 | u2fs_ctx_t *ctx; 294 | 295 | char *reg_response = 296 | "{ \"registrationData\": \"BQRcbdE4PHGRaJUTK9hY4GrX_jZa5eWgjJK6\ 297 | IfwezrndHvQi7QQtYA2qAg4NrebNkSCoOwJ0V1PzLlP1Wr_Oku_0QKfeNR0Ei4_\ 298 | I40GCo5xjm4Q7hnZwzXQ5f5vjtnx7xIqCZ-z7GOGExeouBXxaMgleYpX7xMR6Y9\ 299 | wa_qzLLTAr6IcwggIbMIIBBaADAgECAgR1o_Z1MAsGCSqGSIb3DQEBCzAuMSwwK\ 300 | gYDVQQDEyNZdWJpY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0x\ 301 | NDA4MDEwMDAwMDBaGA8yMDUwMDkwNDAwMDAwMFowKjEoMCYGA1UEAwwfWXViaWN\ 302 | vIFUyRiBFRSBTZXJpYWwgMTk3MzY3OTczMzBZMBMGByqGSM49AgEGCCqGSM49Aw\ 303 | EHA0IABBmjfkNqa2mXzVh2ZxuES5coCvvENxDMDLmfd-0ACG0Fu7wR4ZTjKd9KA\ 304 | uidySpfona5csGmlM0Te_Zu35h_wwujEjAQMA4GCisGAQQBgsQKAQIEADALBgkq\ 305 | hkiG9w0BAQsDggEBAb0tuI0-CzSxBg4cAlyD6UyT4cKyJZGVhWdtPgj_mWepT3T\ 306 | u9jXtdgA5F3jfZtTc2eGxuS-PPvqRAkZd40AXgM8A0YaXPwlT4s0RUTY9Y8aAQz\ 307 | QZeAHuZk3lKKd_LUCg5077dzdt90lC5eVTEduj6cOnHEqnOr2Cv75FuiQXX7QkG\ 308 | QxtoD-otgvhZ2Fjk29o7Iy9ik7ewHGXOfoVw_ruGWi0YfXBTuqEJ6H666vvMN4B\ 309 | ZWHtzhC0k5ceQslB9Xdntky-GQgDqNkkBf32GKwAFT9JJrkO2BfsB-wfBrTiHr0\ 310 | AABYNTNKTceA5dtR3UVpI492VUWQbY3YmWUUfKTI7fM4wRQIhAN3c-VHubCCkUt\ 311 | ZXfWL1aiEXU1qWRiM_ayKmWLUafyFbAiARTwlVocoamd9S-cYBosRKso_XGAPzA\ 312 | edzpuE2tEjp1g==\", \"clientData\": \"eyAiY2hhbGxlbmdlIjogIllTMT\ 313 | ludV9ZWWpnczI5WndrU3dRb2JyNzhPaURXRnoxeXFZZW85WUpmQnciLCAib3JpZ\ 314 | 2luIjogImh0dHA6XC9cL2RlbW8ueXViaWNvLmNvbSIsICJ0eXAiOiAibmF2aWdh\ 315 | dG9yLmlkLmZpbmlzaEVucm9sbG1lbnQiIH0=\" }"; 316 | 317 | u2fs_reg_res_t *res = NULL;; 318 | 319 | ck_assert_int_eq(u2fs_global_init(U2FS_DEBUG), U2FS_OK); 320 | ck_assert_int_eq(u2fs_init(&ctx), U2FS_OK); 321 | ck_assert_int_eq(u2fs_set_appid(ctx, "http://demo.yubico.com"), U2FS_OK); 322 | ck_assert_int_eq(u2fs_set_origin(ctx, "http://example.com"), U2FS_OK); 323 | ck_assert_int_eq(u2fs_set_challenge 324 | (ctx, "YS19nu_YYjgs29ZwkSwQobr78OiDWFz1yqYeo9YJfBw"), 325 | U2FS_OK); 326 | ck_assert_int_eq(u2fs_registration_verify(ctx, reg_response, &res), 327 | U2FS_ORIGIN_ERROR); 328 | 329 | u2fs_free_reg_res(res); 330 | u2fs_done(ctx); 331 | u2fs_global_done(); 332 | 333 | } 334 | 335 | END_TEST START_TEST(authentication_verify_ok) 336 | { 337 | 338 | u2fs_ctx_t *ctx; 339 | 340 | char *auth_response = 341 | "{ \"signatureData\": \"AQAAACYwRAIgXUFB4phCuqcc0-a9obD8S_eMuM\ 342 | JbTC0_VrWizmwHadECIAXb_GaAEIuAJv806eUvMjc2Qi-ii5IMbNw2YU2t39Wp\ 343 | \", \"clientData\": \"eyAiY2hhbGxlbmdlIjogInYzMUlLQkZkTGtkTl9a\ 344 | OXRYZkF4eWR1cG9mQ2Y4OWs2QTRhN3RvME9qVG8iLCAib3JpZ2luIjogImh0dH\ 345 | A6XC9cL2RlbW8ueXViaWNvLmNvbSIsICJ0eXAiOiAibmF2aWdhdG9yLmlkLmdl\ 346 | dEFzc2VydGlvbiIgfQ==\", \"keyHandle\": \"kAbb2p57pxHg2mY8y_Kgc\ 347 | dc7jnnAoncJm8vOgqfigyWTvPGFlvxA04ULD9IJ-KpSyn733LRbJ-CG573N9jC\ 348 | Y1g\" }"; 349 | 350 | u2fs_auth_res_t *res = NULL;; 351 | 352 | unsigned char src_userkey_dat[] = { 353 | 0x04, 0x14, 0xc3, 0x2e, 0x41, 0x0b, 0x30, 0x9d, 0x6e, 0x93, 0x7f, 0x8b, 354 | 0x5d, 0x81, 0xf9, 0xe5, 0x64, 0xfd, 0x11, 0x2c, 0xe5, 0xfe, 0xf0, 0x10, 355 | 0x5e, 0xfb, 0xec, 0xd5, 0x55, 0x54, 0x52, 0x25, 0x25, 0xe4, 0x54, 0x29, 356 | 0x0f, 0xf4, 0x2e, 0xa1, 0xd8, 0x77, 0x19, 0x36, 0x12, 0xe3, 0x6e, 0x39, 357 | 0x17, 0x91, 0x24, 0xb5, 0x93, 0x8e, 0xe0, 0xfe, 0xf3, 0x69, 0xac, 0xb9, 358 | 0x4c, 0x37, 0x97, 0x83, 0xcb 359 | }; 360 | 361 | ck_assert_int_eq(u2fs_global_init(U2FS_DEBUG), U2FS_OK); 362 | ck_assert_int_eq(u2fs_init(&ctx), U2FS_OK); 363 | ck_assert_int_eq(u2fs_set_appid(ctx, "http://demo.yubico.com"), U2FS_OK); 364 | ck_assert_int_eq(u2fs_set_origin(ctx, "http://demo.yubico.com"), 365 | U2FS_OK); 366 | ck_assert_int_eq(u2fs_set_challenge 367 | (ctx, "v31IKBFdLkdN_Z9tXfAxydupofCf89k6A4a7to0OjTo"), 368 | U2FS_OK); 369 | ck_assert_int_eq(u2fs_set_publicKey(ctx, src_userkey_dat), U2FS_OK); 370 | ck_assert_int_eq(u2fs_authentication_verify(ctx, auth_response, &res), 371 | U2FS_OK); 372 | 373 | u2fs_free_auth_res(res); 374 | u2fs_done(ctx); 375 | u2fs_global_done(); 376 | } 377 | 378 | END_TEST START_TEST(authentication_verify_challenge_error) 379 | { 380 | 381 | u2fs_ctx_t *ctx; 382 | 383 | char *auth_response = 384 | "{ \"signatureData\": \"AQAAACYwRAIgXUFB4phCuqcc0-a9obD8S_eMuM\ 385 | JbTC0_VrWizmwHadECIAXb_GaAEIuAJv806eUvMjc2Qi-ii5IMbNw2YU2t39Wp\ 386 | \", \"clientData\": \"eyAiY2hhbGxlbmdlIjogInYzMUlLQkZkTGtkTl9a\ 387 | OXRYZkF4eWR1cG9mQ2Y4OWs2QTRhN3RvME9qVG8iLCAib3JpZ2luIjogImh0dH\ 388 | A6XC9cL2RlbW8ueXViaWNvLmNvbSIsICJ0eXAiOiAibmF2aWdhdG9yLmlkLmdl\ 389 | dEFzc2VydGlvbiIgfQ==\", \"keyHandle\": \"kAbb2p57pxHg2mY8y_Kgc\ 390 | dc7jnnAoncJm8vOgqfigyWTvPGFlvxA04ULD9IJ-KpSyn733LRbJ-CG573N9jC\ 391 | Y1g\" }"; 392 | 393 | u2fs_auth_res_t *res = NULL; 394 | 395 | unsigned char src_userkey_dat[] = { 396 | 0x04, 0x14, 0xc3, 0x2e, 0x41, 0x0b, 0x30, 0x9d, 0x6e, 0x93, 0x7f, 0x8b, 397 | 0x5d, 0x81, 0xf9, 0xe5, 0x64, 0xfd, 0x11, 0x2c, 0xe5, 0xfe, 0xf0, 0x10, 398 | 0x5e, 0xfb, 0xec, 0xd5, 0x55, 0x54, 0x52, 0x25, 0x25, 0xe4, 0x54, 0x29, 399 | 0x0f, 0xf4, 0x2e, 0xa1, 0xd8, 0x77, 0x19, 0x36, 0x12, 0xe3, 0x6e, 0x39, 400 | 0x17, 0x91, 0x24, 0xb5, 0x93, 0x8e, 0xe0, 0xfe, 0xf3, 0x69, 0xac, 0xb9, 401 | 0x4c, 0x37, 0x97, 0x83, 0xcb 402 | }; 403 | 404 | ck_assert_int_eq(u2fs_global_init(U2FS_DEBUG), U2FS_OK); 405 | ck_assert_int_eq(u2fs_init(&ctx), U2FS_OK); 406 | ck_assert_int_eq(u2fs_set_appid(ctx, "http://demo.yubico.com"), U2FS_OK); 407 | ck_assert_int_eq(u2fs_set_origin(ctx, "http://demo.yubico.com"), 408 | U2FS_OK); 409 | ck_assert_int_eq(u2fs_set_challenge 410 | (ctx, "0000000000000000000000000000000000000000000"), 411 | U2FS_OK); 412 | ck_assert_int_eq(u2fs_set_publicKey(ctx, src_userkey_dat), U2FS_OK); 413 | ck_assert_int_eq(u2fs_authentication_verify(ctx, auth_response, &res), 414 | U2FS_CHALLENGE_ERROR); 415 | 416 | u2fs_free_auth_res(res); 417 | u2fs_done(ctx); 418 | u2fs_global_done(); 419 | } 420 | 421 | END_TEST START_TEST(authentication_verify_signature_error) 422 | { 423 | 424 | u2fs_ctx_t *ctx; 425 | 426 | char *auth_response = 427 | "{ \"signatureData\": \"AQAAACYwRAIgXUFB4phCuqcc0-a9obD8S_eMuM\ 428 | JbTC0_VrWizmwHadECIAXb_GaAEIuAJv806eUvMjc2Qi-ii5IMbNw2YU2t39Wp\ 429 | \", \"clientData\": \"eyAiY2hhbGxlbmdlIjogInYzMUlLQkZkTGtkTl9a\ 430 | OXRYZkF4eWR1cG9mQ2Y4OWs2QTRhN3RvME9qVG8iLCAib3JpZ2luIjogImh0dH\ 431 | A6XC9cL2RlbW8ueXViaWNvLmNvbSIsICJ0eXAiOiAibmF2aWdhdG9yLmlkLmdl\ 432 | dEFzc2VydGlvbiIgfQ==\", \"keyHandle\": \"kAbb2p57pxHg2mY8y_Kgc\ 433 | dc7jnnAoncJm8vOgqfigyWTvPGFlvxA04ULD9IJ-KpSyn733LRbJ-CG573N9jC\ 434 | Y1g\" }"; 435 | 436 | u2fs_auth_res_t *res = NULL; 437 | 438 | unsigned char src_userkey_dat[] = { 439 | 0x04, 0xb4, 0x21, 0x96, 0x10, 0x25, 0xd3, 0x61, 0xe6, 0x3d, 0x3d, 0x68, 440 | 0x0d, 0x64, 0xd1, 0x40, 0x7c, 0xeb, 0x7b, 0x7b, 0x58, 0x28, 0x6b, 0x47, 441 | 0x77, 0xd4, 0x31, 0x97, 0x6a, 0xc6, 0xd4, 0xd3, 0x36, 0x86, 0xcf, 0xdb, 442 | 0x79, 0x33, 0x04, 0x78, 0x70, 0x11, 0xaa, 0x75, 0x16, 0xfb, 0xae, 0x18, 443 | 0xf5, 0x1d, 0xcd, 0x1e, 0x2c, 0x69, 0xab, 0xf3, 0x12, 0x75, 0xed, 0xed, 444 | 0xfc, 0x5f, 0x8c, 0xad, 0x54 445 | }; 446 | 447 | ck_assert_int_eq(u2fs_global_init(U2FS_DEBUG), U2FS_OK); 448 | ck_assert_int_eq(u2fs_init(&ctx), U2FS_OK); 449 | ck_assert_int_eq(u2fs_set_appid(ctx, "http://demo.yubico.com"), U2FS_OK); 450 | ck_assert_int_eq(u2fs_set_origin(ctx, "http://demo.yubico.com"), 451 | U2FS_OK); 452 | ck_assert_int_eq(u2fs_set_challenge 453 | (ctx, "v31IKBFdLkdN_Z9tXfAxydupofCf89k6A4a7to0OjTo"), 454 | U2FS_OK); 455 | ck_assert_int_eq(u2fs_set_publicKey(ctx, src_userkey_dat), U2FS_OK); 456 | ck_assert_int_eq(u2fs_authentication_verify(ctx, auth_response, &res), 457 | U2FS_SIGNATURE_ERROR); 458 | 459 | u2fs_free_auth_res(res); 460 | u2fs_done(ctx); 461 | u2fs_global_done(); 462 | } 463 | 464 | END_TEST Suite *u2fs_suite(void) 465 | { 466 | Suite *s; 467 | TCase *tc_core; 468 | 469 | s = suite_create("u2fs_core"); 470 | 471 | /* Core test case */ 472 | tc_core = tcase_create("Core"); 473 | 474 | tcase_add_test(tc_core, test_create); 475 | tcase_add_test(tc_core, set_challenge); 476 | tcase_add_test(tc_core, set_keyhandle); 477 | tcase_add_test(tc_core, set_publicKey); 478 | tcase_add_test(tc_core, set_origin); 479 | tcase_add_test(tc_core, set_appid); 480 | tcase_add_test(tc_core, registration_verify_ok); 481 | tcase_add_test(tc_core, registration_challenge_error); 482 | tcase_add_test(tc_core, registration_origin_error); 483 | tcase_add_test(tc_core, authentication_verify_ok); 484 | tcase_add_test(tc_core, authentication_verify_challenge_error); 485 | tcase_add_test(tc_core, authentication_verify_signature_error); 486 | suite_add_tcase(s, tc_core); 487 | 488 | return s; 489 | } 490 | 491 | 492 | int main(void) 493 | { 494 | int number_failed; 495 | Suite *s; 496 | SRunner *sr; 497 | 498 | s = u2fs_suite(); 499 | sr = srunner_create(s); 500 | 501 | srunner_run_all(sr, CK_NORMAL); 502 | number_failed = srunner_ntests_failed(sr); 503 | srunner_free(sr); 504 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 505 | } 506 | -------------------------------------------------------------------------------- /tests/openssl.c: -------------------------------------------------------------------------------- 1 | #include "../u2f-server/openssl.c" 2 | -------------------------------------------------------------------------------- /tests/u2f-server-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright (c) 2014 Yubico AB 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # # Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # 13 | # # Redistributions in binary form must reproduce the above 14 | # copyright notice, this list of conditions and the following 15 | # disclaimer in the documentation and/or other materials provided 16 | # with the distribution. 17 | # 18 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | EXIT_SUCCESS=0 31 | EXIT_FAILURE=99 32 | 33 | U2FSERVER=${U2FSERVER:-../src/u2f-server} 34 | 35 | ORIGIN="http://yubico.com" 36 | APPID="http://yubico.com" 37 | ORIGIN_WRONG="ftp://yubico.com" 38 | AUTH_PARAM="authenticate" 39 | REGISTER_PARAM="register" 40 | ERROR_PARAM="unknown" 41 | 42 | WRONG_CHALLENGE1="abcd" 43 | 44 | REG_CHALLENGE1="TVgGf_GfMfVf4L2KiNmLdyIoR59ez4qtmLwwdG4-lkI" 45 | REG_RESPONSE1="{ \"registrationData\": \"BQRjk4BrghuG1RR0nIzda230YhTj4\ 46 | iMpyFvZpRyZf37eKNAWPmcmPbsouRxw2NUj2Z0kPlbUIaFlAD88Ez_bGyzRQNaWpOywZ1-\ 47 | DkgpDiCnej_COzgsEwXO2Cgwyd2IZ_5wK7b4L85-T9DZHBtgNYnsxdYekFvDikKdd5TND-\ 48 | WVUn9cwggIbMIIBBaADAgECAgR1o_Z1MAsGCSqGSIb3DQEBCzAuMSwwKgYDVQQDEyNZdWJ\ 49 | pY28gVTJGIFJvb3QgQ0EgU2VyaWFsIDQ1NzIwMDYzMTAgFw0xNDA4MDEwMDAwMDBaGA8yM\ 50 | DUwMDkwNDAwMDAwMFowKjEoMCYGA1UEAwwfWXViaWNvIFUyRiBFRSBTZXJpYWwgMTk3MzY\ 51 | 3OTczMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABBmjfkNqa2mXzVh2ZxuES5coCvvEN\ 52 | xDMDLmfd-0ACG0Fu7wR4ZTjKd9KAuidySpfona5csGmlM0Te_Zu35h_wwujEjAQMA4GCis\ 53 | GAQQBgsQKAQIEADALBgkqhkiG9w0BAQsDggEBAb0tuI0-CzSxBg4cAlyD6UyT4cKyJZGVh\ 54 | WdtPgj_mWepT3Tu9jXtdgA5F3jfZtTc2eGxuS-PPvqRAkZd40AXgM8A0YaXPwlT4s0RUTY\ 55 | 9Y8aAQzQZeAHuZk3lKKd_LUCg5077dzdt90lC5eVTEduj6cOnHEqnOr2Cv75FuiQXX7QkG\ 56 | QxtoD-otgvhZ2Fjk29o7Iy9ik7ewHGXOfoVw_ruGWi0YfXBTuqEJ6H666vvMN4BZWHtzhC\ 57 | 0k5ceQslB9Xdntky-GQgDqNkkBf32GKwAFT9JJrkO2BfsB-wfBrTiHr0AABYNTNKTceA5d\ 58 | tR3UVpI492VUWQbY3YmWUUfKTI7fM4wRQIhAJnjtf2irhjgUbsdFUft-38T4d70e7Dhsyn\ 59 | VR_cy7Y2ZAiByN798SHtk61WmSsGcQ9e7hUW3OKxYGjgvKAwEMDHuKQ==\", \"clientD\ 60 | ata\": \"eyAiY2hhbGxlbmdlIjogIlRWZ0dmX0dmTWZWZjRMMktpTm1MZHlJb1I1OWV6N\ 61 | HF0bUx3d2RHNC1sa0kiLCAib3JpZ2luIjogImh0dHA6XC9cL3l1Ymljby5jb20iLCAidHl\ 62 | wIjogIm5hdmlnYXRvci5pZC5maW5pc2hFbnJvbGxtZW50IiB9\" }" 63 | 64 | REG_RESPONSE_ERROR="{ \"registrationData\": \"00000000000000000000000000000\ 65 | 0000000000000000000000000000000000000000000000000000000000000000000000\ 66 | 0000000000000000000000000000000000000000000000000000000000000000000000\ 67 | 0000000000000000000000000000000000000000000000000000000000000000000000\ 68 | 0000000000000000000000000000000000000000000000000000000000000000000000\ 69 | 0000000000000000000000000000000000000000000000000000000000000000000000\ 70 | 0000000000000000000000000000000000000000000000000000000000000000000000\ 71 | 0000000000000000000000000000000000000000000000000000000000000000000000\ 72 | 0000000000000000000000000000000000000000000000000000000000000000000000\ 73 | 0000000000000000000000000000000000000000000000000000000000000000000000\ 74 | 0000000000000000000000000000000000000000000000000000000000000000000000\ 75 | 0000000000000000000000000000000000000000000000000000000000000000000000\ 76 | 0000000000000000000000000000000000000000000000000000000000000000000000\ 77 | 0000000000000000000000000000000000000000000000000000000000000000000000\ 78 | 0000000000000000000000000000000000000000000000000000000==\", \"clientD\ 79 | ata\": \"0000000000000000000000000000000000000000000000000000000000000\ 80 | 0000000000000000000000000000000000000000000000000000000000000000000000\ 81 | 0000000000000000000000000000000000000000000000000\" }" 82 | 83 | AUTH_CHALLENGE1="uejb2GF9ICDgPYgJFDqz0C0X-TPLg6twVTf-SOdSyO8" 84 | AUTH_RESPONSE1="{ \"signatureData\": \"AQAAADMwRQIgJ_ugO8pZSxyUh1XX2kg\ 85 | KE00zC2Bnen8yhabO79IQmSsCIQChw_psGoSthXVw1drDmjW30fjABJxOZBouSzhsELrLR\ 86 | g==\", \"clientData\": \"eyAiY2hhbGxlbmdlIjogInVlamIyR0Y5SUNEZ1BZZ0pGR\ 87 | HF6MEMwWC1UUExnNnR3VlRmLVNPZFN5TzgiLCAib3JpZ2luIjogImh0dHA6XC9cL3l1Yml\ 88 | jby5jb20iLCAidHlwIjogIm5hdmlnYXRvci5pZC5nZXRBc3NlcnRpb24iIH0=\", \"key\ 89 | Handle\": \"1pak7LBnX4OSCkOIKd6P8I7OCwTBc7YKDDJ3Yhn_nArtvgvzn5P0NkcG2A\ 90 | 1iezF1h6QW8OKQp13lM0P5ZVSf1w\" }" 91 | AUTH_RESPONSE2="{ \"signatureData\": \"AQAAAFwwRQIgGcz53KRohZ51ZuL2A4R\ 92 | ygkV9P7KuT7k5K6MZRBBSFrMCIQDz4D-JmY_OS-0000RnBBtJG_m9McGK6lZuaCFBnbapC\ 93 | w==\", \"clientData\": \"eyAiY2hhbGxlbmdlIjogInVlamIyR0Y5SUNEZ1BZZ0pGR\ 94 | HF6MEMwWC1UUExnNnR3VlRmLVNPZFN5TzgiLCAib3JpZ2luIjogImh0dHA6XC9cL3l1Yml\ 95 | jby5jb20iLCAidHlwIjogIm5hdmlnYXRvci5pZC5nZXRBc3NlcnRpb24iIH0=\", \"key\ 96 | Handle\": \"1pak7LBnX4OSCkOIKd6P8I7OCwTBc7YKDDJ3Yhn_nArtvgvzn5P0NkcG2A\ 97 | 1iezF1h6QW8OKQp13lM0P5ZVSf1w\" }" 98 | 99 | $(${U2FSERVER} --${ERROR_PARAM}) 100 | RESULT=$? 101 | if [ $RESULT -ne 1 ]; then 102 | exit $EXIT_FAILURE 103 | fi 104 | 105 | $(${U2FSERVER} -a${REGISTER_PARAM}) 106 | RESULT=$? 107 | if [ $RESULT -ne 1 ]; then 108 | exit $EXIT_FAILURE 109 | fi 110 | 111 | $(${U2FSERVER} -a${REGISTER_PARAM} -o${ORIGIN}) 112 | RESULT=$? 113 | if [ $RESULT -ne 1 ]; then 114 | exit $EXIT_FAILURE 115 | fi 116 | 117 | $(echo ${REG_RESPONSE_ERROR} | ${U2FSERVER} -a${REGISTER_PARAM} -o${ORIGIN_WRONG} -i${APPID} >/dev/null) 118 | RESULT=$? 119 | if [ $RESULT -ne 1 ]; then 120 | exit $EXIT_FAILURE 121 | fi 122 | 123 | $(echo ${REG_RESPONSE_ERROR} | ${U2FSERVER} -a${REGISTER_PARAM} -o${ORIGIN} -i${APPID} >/dev/null) 124 | RESULT=$? 125 | if [ $RESULT -ne 1 ]; then 126 | exit $EXIT_FAILURE 127 | fi 128 | 129 | KEYFILE=$(mktemp) 130 | USERFILE=$(mktemp) 131 | 132 | $(echo ${REG_RESPONSE1} | ${U2FSERVER} -a${REGISTER_PARAM} -o${ORIGIN} -i${APPID} -c${WRONG_CHALLENGE1} -p${USERFILE} -k${KEYFILE} >/dev/null) 133 | RESULT=$? 134 | if [ $RESULT -ne 1 ]; then 135 | exit $EXIT_FAILURE 136 | fi 137 | 138 | $(echo ${REG_RESPONSE1} | ${U2FSERVER} -a${REGISTER_PARAM} -o${ORIGIN} -i${APPID} -c${REG_CHALLENGE1} >/dev/null) 139 | RESULT=$? 140 | if [ $RESULT -ne 0 ]; then 141 | exit $EXIT_FAILURE 142 | fi 143 | 144 | $(echo ${REG_RESPONSE1} | ${U2FSERVER} -a${REGISTER_PARAM} -o${ORIGIN} -i${APPID} -c${REG_CHALLENGE1} -p${USERFILE} -k${KEYFILE} >/dev/null) 145 | RESULT=$? 146 | if [ $RESULT -ne 0 ]; then 147 | exit $EXIT_FAILURE 148 | fi 149 | 150 | $(echo ${AUTH_RESPONSE1} | ${U2FSERVER} -a${AUTH_PARAM} -o${ORIGIN} -i${APPID} -c${AUTH_CHALLENGE1} >/dev/null) 151 | RESULT=$? 152 | if [ $RESULT -ne 1 ]; then 153 | exit $EXIT_FAILURE 154 | fi 155 | 156 | $(echo ${AUTH_RESPONSE1} | ${U2FSERVER} -a${AUTH_PARAM} -o${ORIGIN} -i${APPID} -c${AUTH_CHALLENGE1} -k${KEYFILE} >/dev/null) 157 | RESULT=$? 158 | if [ $RESULT -ne 1 ]; then 159 | exit $EXIT_FAILURE 160 | fi 161 | 162 | $(echo ${AUTH_RESPONSE2} | ${U2FSERVER} -a${AUTH_PARAM} -o${ORIGIN} -i${APPID} -c${AUTH_CHALLENGE1} -k${KEYFILE} >/dev/null) 163 | RESULT=$? 164 | if [ $RESULT -ne 1 ]; then 165 | exit $EXIT_FAILURE 166 | fi 167 | 168 | $(echo ${AUTH_RESPONSE1} | ${U2FSERVER} -a${AUTH_PARAM} -o${ORIGIN} -i${APPID} -c${AUTH_CHALLENGE1} -p${USERFILE} -k${KEYFILE} >/dev/null) 169 | RESULT=$? 170 | if [ $RESULT -ne 0 ]; then 171 | exit $EXIT_FAILURE 172 | fi 173 | 174 | rm -f ${KEYFILE} 175 | rm -f ${USERFILE} 176 | 177 | exit $EXIT_SUCCESS 178 | -------------------------------------------------------------------------------- /u2f-server/Makefile.am: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Yubico AB 2 | # All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions are 6 | # met: 7 | # 8 | # # Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # 11 | # # Redistributions in binary form must reproduce the above 12 | # copyright notice, this list of conditions and the following 13 | # disclaimer in the documentation and/or other materials provided 14 | # with the distribution. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | AM_CFLAGS = $(WARN_CFLAGS) 29 | AM_CPPFLAGS = -I$(srcdir)/.. -I$(builddir)/.. 30 | AM_CPPFLAGS += $(LIBJSON_CFLAGS) $(LIBSSL_CFLAGS) $(LIBCRYPTO_CFLAGS) 31 | 32 | lib_LTLIBRARIES = libu2f-server.la 33 | u2f_server_includedir = $(includedir)/u2f-server 34 | u2f_server_include_HEADERS = u2f-server.h u2f-server-version.h 35 | 36 | libu2f_server_la_SOURCES = u2f-server.h u2f-server-version.h 37 | libu2f_server_la_SOURCES += internal.h 38 | libu2f_server_la_SOURCES += u2f-server.pc.in u2f-server.map 39 | libu2f_server_la_SOURCES += global.c version.c error.c 40 | libu2f_server_la_SOURCES += core.c 41 | libu2f_server_la_SOURCES += sha256.h sha256.c 42 | libu2f_server_la_SOURCES += cencode.c cdecode.c b64/cencode.h b64/cdecode.h 43 | libu2f_server_la_SOURCES += crypto.h 44 | libu2f_server_la_SOURCES += openssl.c 45 | 46 | libu2f_server_la_LIBADD = $(HIDAPI_LIBS) $(LIBJSON_LIBS) $(LIBSSL_LIBS) $(LIBCRYPTO_LIBS) 47 | 48 | libu2f_server_la_LDFLAGS = -no-undefined \ 49 | -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) 50 | 51 | if HAVE_LD_VERSION_SCRIPT 52 | libu2f_server_la_LDFLAGS += -Wl,--version-script=$(srcdir)/u2f-server.map 53 | else 54 | libu2f_server_la_LDFLAGS += -export-symbols-regex '^u2fs_.*' 55 | endif 56 | 57 | if ENABLE_COV 58 | AM_CFLAGS += --coverage 59 | AM_LDFLAGS = --coverage 60 | endif 61 | 62 | pkgconfigdir = $(libdir)/pkgconfig 63 | pkgconfig_DATA = u2f-server.pc 64 | -------------------------------------------------------------------------------- /u2f-server/b64/cdecode.h: -------------------------------------------------------------------------------- 1 | /* 2 | cdecode.h - c header for a base64 decoding algorithm 3 | 4 | This is part of the libb64 project, and has been placed in the public domain. 5 | For details, see http://sourceforge.net/projects/libb64 6 | */ 7 | 8 | #ifndef BASE64_CDECODE_H 9 | #define BASE64_CDECODE_H 10 | 11 | typedef enum 12 | { 13 | step_a, step_b, step_c, step_d 14 | } base64_decodestep; 15 | 16 | typedef struct 17 | { 18 | base64_decodestep step; 19 | char plainchar; 20 | } base64_decodestate; 21 | 22 | void base64_init_decodestate(base64_decodestate* state_in); 23 | 24 | int base64_decode_value(char value_in); 25 | 26 | int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in); 27 | 28 | #endif /* BASE64_CDECODE_H */ 29 | 30 | -------------------------------------------------------------------------------- /u2f-server/b64/cencode.h: -------------------------------------------------------------------------------- 1 | /* 2 | cencode.h - c header for a base64 encoding algorithm 3 | 4 | This is part of the libb64 project, and has been placed in the public domain. 5 | For details, see http://sourceforge.net/projects/libb64 6 | */ 7 | 8 | #ifndef BASE64_CENCODE_H 9 | #define BASE64_CENCODE_H 10 | 11 | typedef enum 12 | { 13 | step_A, step_B, step_C 14 | } base64_encodestep; 15 | 16 | typedef struct 17 | { 18 | base64_encodestep step; 19 | char result; 20 | int stepcount; 21 | } base64_encodestate; 22 | 23 | void base64_init_encodestate(base64_encodestate* state_in); 24 | 25 | char base64_encode_value(char value_in); 26 | 27 | int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in); 28 | 29 | int base64_encode_blockend(char* code_out, base64_encodestate* state_in); 30 | 31 | #endif /* BASE64_CENCODE_H */ 32 | 33 | -------------------------------------------------------------------------------- /u2f-server/cdecode.c: -------------------------------------------------------------------------------- 1 | /* 2 | cdecoder.c - c source to a base64 decoding algorithm implementation 3 | 4 | This is part of the libb64 project, and has been placed in the public domain. 5 | For details, see http://sourceforge.net/projects/libb64 6 | */ 7 | 8 | #include 9 | 10 | int base64_decode_value(char value_in) 11 | { 12 | static const signed char decoding[] = 13 | { 62, -1, -1, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, 14 | -2, -1, 15 | -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 16 | 17, 18, 17 | 19, 20, 21, 22, 23, 24, 25, 18 | -1, -1, -1, -1, 63, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 19 | 37, 20 | 38, 39, 40, 41, 42, 43, 44, 21 | 45, 46, 47, 48, 49, 50, 51 22 | }; 23 | static const char decoding_size = sizeof(decoding); 24 | value_in -= 45; 25 | if (value_in < 0 || value_in >= decoding_size) 26 | return -1; 27 | return decoding[(int) value_in]; 28 | } 29 | 30 | void base64_init_decodestate(base64_decodestate * state_in) 31 | { 32 | state_in->step = step_a; 33 | state_in->plainchar = 0; 34 | } 35 | 36 | int 37 | base64_decode_block(const char *code_in, const int length_in, 38 | char *plaintext_out_, base64_decodestate * state_in) 39 | { 40 | const char *codechar = code_in; 41 | unsigned char *plaintext_out = (unsigned char *) plaintext_out_; 42 | unsigned char *plainchar = plaintext_out; 43 | int fragment; 44 | 45 | *plainchar = state_in->plainchar; 46 | 47 | switch (state_in->step) { 48 | while (1) { 49 | case step_a: 50 | do { 51 | if (codechar == code_in + length_in) { 52 | state_in->step = step_a; 53 | state_in->plainchar = *plainchar; 54 | return plainchar - plaintext_out; 55 | } 56 | fragment = base64_decode_value(*codechar++); 57 | } 58 | while (fragment < 0); 59 | *plainchar = (fragment & 0x03f) << 2; 60 | case step_b: 61 | do { 62 | if (codechar == code_in + length_in) { 63 | state_in->step = step_b; 64 | state_in->plainchar = *plainchar; 65 | return plainchar - plaintext_out; 66 | } 67 | fragment = base64_decode_value(*codechar++); 68 | } 69 | while (fragment < 0); 70 | *plainchar++ |= (fragment & 0x030) >> 4; 71 | *plainchar = (fragment & 0x00f) << 4; 72 | case step_c: 73 | do { 74 | if (codechar == code_in + length_in) { 75 | state_in->step = step_c; 76 | state_in->plainchar = *plainchar; 77 | return plainchar - plaintext_out; 78 | } 79 | fragment = base64_decode_value(*codechar++); 80 | } 81 | while (fragment < 0); 82 | *plainchar++ |= (fragment & 0x03c) >> 2; 83 | *plainchar = (fragment & 0x003) << 6; 84 | case step_d: 85 | do { 86 | if (codechar == code_in + length_in) { 87 | state_in->step = step_d; 88 | state_in->plainchar = *plainchar; 89 | return plainchar - plaintext_out; 90 | } 91 | fragment = base64_decode_value(*codechar++); 92 | } 93 | while (fragment < 0); 94 | *plainchar++ |= (fragment & 0x03f); 95 | } 96 | } 97 | /* control should not reach here */ 98 | return plainchar - plaintext_out; 99 | } 100 | -------------------------------------------------------------------------------- /u2f-server/cencode.c: -------------------------------------------------------------------------------- 1 | /* 2 | cencoder.c - c source to a base64 encoding algorithm implementation 3 | 4 | This is part of the libb64 project, and has been placed in the public domain. 5 | For details, see http://sourceforge.net/projects/libb64 6 | */ 7 | 8 | #include 9 | 10 | const int CHARS_PER_LINE = 72; 11 | 12 | void base64_init_encodestate(base64_encodestate * state_in) 13 | { 14 | state_in->step = step_A; 15 | state_in->result = 0; 16 | state_in->stepcount = 0; 17 | } 18 | 19 | char base64_encode_value(char value_in) 20 | { 21 | static const char *encoding = 22 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; 23 | if (value_in > 63) 24 | return '='; 25 | return encoding[(int) value_in]; 26 | } 27 | 28 | int 29 | base64_encode_block(const char *plaintext_in, int length_in, 30 | char *code_out, base64_encodestate * state_in) 31 | { 32 | const char *plainchar = plaintext_in; 33 | const char *const plaintextend = plaintext_in + length_in; 34 | char *codechar = code_out; 35 | char result; 36 | char fragment; 37 | 38 | result = state_in->result; 39 | 40 | switch (state_in->step) { 41 | while (1) { 42 | case step_A: 43 | if (plainchar == plaintextend) { 44 | state_in->result = result; 45 | state_in->step = step_A; 46 | return codechar - code_out; 47 | } 48 | fragment = *plainchar++; 49 | result = (fragment & 0x0fc) >> 2; 50 | *codechar++ = base64_encode_value(result); 51 | result = (fragment & 0x003) << 4; 52 | case step_B: 53 | if (plainchar == plaintextend) { 54 | state_in->result = result; 55 | state_in->step = step_B; 56 | return codechar - code_out; 57 | } 58 | fragment = *plainchar++; 59 | result |= (fragment & 0x0f0) >> 4; 60 | *codechar++ = base64_encode_value(result); 61 | result = (fragment & 0x00f) << 2; 62 | case step_C: 63 | if (plainchar == plaintextend) { 64 | state_in->result = result; 65 | state_in->step = step_C; 66 | return codechar - code_out; 67 | } 68 | fragment = *plainchar++; 69 | result |= (fragment & 0x0c0) >> 6; 70 | *codechar++ = base64_encode_value(result); 71 | result = (fragment & 0x03f) >> 0; 72 | *codechar++ = base64_encode_value(result); 73 | 74 | ++(state_in->stepcount); 75 | if (state_in->stepcount == CHARS_PER_LINE / 4) { 76 | /* *codechar++ = '\n'; */ 77 | state_in->stepcount = 0; 78 | } 79 | } 80 | } 81 | /* control should not reach here */ 82 | return codechar - code_out; 83 | } 84 | 85 | int base64_encode_blockend(char *code_out, base64_encodestate * state_in) 86 | { 87 | char *codechar = code_out; 88 | 89 | switch (state_in->step) { 90 | case step_B: 91 | *codechar++ = base64_encode_value(state_in->result); 92 | *codechar++ = '\0'; 93 | *codechar++ = '\0'; 94 | break; 95 | case step_C: 96 | *codechar++ = base64_encode_value(state_in->result); 97 | *codechar++ = '\0'; 98 | break; 99 | case step_A: 100 | break; 101 | } 102 | *codechar++ = '\0'; 103 | 104 | return codechar - code_out; 105 | } 106 | -------------------------------------------------------------------------------- /u2f-server/core.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Yubico AB 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above 13 | * copyright notice, this list of conditions and the following 14 | * disclaimer in the documentation and/or other materials provided 15 | * with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include "internal.h" 31 | 32 | #include 33 | #include 34 | #include 35 | #include "crypto.h" 36 | #include "b64/cencode.h" 37 | #include "b64/cdecode.h" 38 | #include "sha256.h" 39 | 40 | #ifdef HAVE_JSON_OBJECT_OBJECT_GET_EX 41 | #define u2fs_json_object_object_get(obj, key, value) json_object_object_get_ex(obj, key, &value) 42 | #else 43 | typedef int json_bool; 44 | #define u2fs_json_object_object_get(obj, key, value) (value = json_object_object_get(obj, key)) == NULL ? (json_bool)FALSE : (json_bool)TRUE 45 | #endif 46 | 47 | /* json-c 0.13.99 does not define TRUE/FALSE anymore 48 | * the json-c maintainers replaced them with pure 1/0 49 | * https://github.com/json-c/json-c/commit/0992aac61f8b 50 | */ 51 | #if defined JSON_C_VERSION_NUM && JSON_C_VERSION_NUM >= ((13 << 8) | 99) 52 | #ifndef FALSE 53 | #define FALSE 0 54 | #endif 55 | #ifndef TRUE 56 | #define TRUE 1 57 | #endif 58 | #endif 59 | 60 | static u2fs_rc encode_b64u(const char *data, size_t data_len, char *output) 61 | { 62 | base64_encodestate b64; 63 | int cnt; 64 | 65 | if ((data_len * 4) >= (_B64_BUFSIZE * 3) || output == NULL) //base64 is 75% efficient (4 characters encode 3 bytes) 66 | return U2FS_MEMORY_ERROR; 67 | 68 | base64_init_encodestate(&b64); 69 | cnt = base64_encode_block(data, data_len, output, &b64); 70 | cnt += base64_encode_blockend(output + cnt, &b64); 71 | 72 | output[cnt] = '\0'; 73 | 74 | return U2FS_OK; 75 | } 76 | 77 | static u2fs_rc gen_challenge(u2fs_ctx_t *ctx) 78 | { 79 | char buf[U2FS_CHALLENGE_RAW_LEN]; 80 | u2fs_rc rc; 81 | 82 | if (ctx->challenge[0] != '\0') 83 | return U2FS_OK; 84 | 85 | rc = set_random_bytes(buf, U2FS_CHALLENGE_RAW_LEN); 86 | if (rc != U2FS_OK) 87 | return rc; 88 | 89 | return encode_b64u(buf, U2FS_CHALLENGE_RAW_LEN, ctx->challenge); 90 | } 91 | 92 | /** 93 | * u2fs_init: 94 | * @ctx: pointer to output variable holding a context handle. 95 | * 96 | * Initialize the U2F server context handle. 97 | * 98 | * Returns: On success %U2FS_OK (integer 0) is returned, and on errors 99 | * an #u2fs_rc error code. 100 | */ 101 | u2fs_rc u2fs_init(u2fs_ctx_t ** ctx) 102 | { 103 | *ctx = calloc(1, sizeof(**ctx)); 104 | if (*ctx == NULL) 105 | return U2FS_MEMORY_ERROR; 106 | 107 | return U2FS_OK; 108 | } 109 | 110 | /** 111 | * u2fs_done: 112 | * @ctx: a context handle, from u2fs_init() 113 | * 114 | * Deallocate resources associated with context @ctx. 115 | */ 116 | void u2fs_done(u2fs_ctx_t * ctx) 117 | { 118 | if (ctx == NULL) 119 | return; 120 | 121 | free(ctx->keyHandle); 122 | ctx->keyHandle = NULL; 123 | free_key(ctx->key); 124 | ctx->key = NULL; 125 | free(ctx->origin); 126 | ctx->origin = NULL; 127 | free(ctx->appid); 128 | ctx->appid = NULL; 129 | free(ctx); 130 | } 131 | 132 | /** 133 | * u2fs_free_reg_res: 134 | * @result: a registration result as generated by u2fs_registration_verify() 135 | * 136 | * Deallocate resources associated with @result. 137 | */ 138 | void u2fs_free_reg_res(u2fs_reg_res_t * result) 139 | { 140 | if (result != NULL) { 141 | if (result->keyHandle) { 142 | free(result->keyHandle); 143 | result->keyHandle = NULL; 144 | } 145 | if (result->publicKey) { 146 | free(result->publicKey); 147 | result->publicKey = NULL; 148 | } 149 | if (result->attestation_certificate_PEM) { 150 | free(result->attestation_certificate_PEM); 151 | result->attestation_certificate_PEM = NULL; 152 | } 153 | if (result->user_public_key) { 154 | free_key(result->user_public_key); 155 | result->user_public_key = NULL; 156 | } 157 | if (result->attestation_certificate) { 158 | free_cert(result->attestation_certificate); 159 | result->attestation_certificate = NULL; 160 | } 161 | free(result); 162 | } 163 | } 164 | 165 | /** 166 | * u2fs_free_auth_res: 167 | * @result: an authentication result as generated by u2fs_authentication_verify() 168 | * 169 | * Deallocate resources associated with @result. 170 | */ 171 | void u2fs_free_auth_res(u2fs_auth_res_t * result) 172 | { 173 | if (result != NULL) { 174 | result->verified = -1; 175 | result->counter = 0; 176 | result->user_presence = 0; 177 | } 178 | free(result); 179 | } 180 | 181 | /** 182 | * u2fs_set_challenge: 183 | * @ctx: a context handle, from u2fs_init() 184 | * @challenge: a 43-byte long, websafe Base64 encoded challenge (viz RFC4648 Section 5) 185 | * 186 | * Stores a given @challenge within @ctx. If a value is already 187 | * present, it is cleared and the memory is released. 188 | * 189 | * Returns: On success %U2FS_OK (integer 0) is returned, and on errors 190 | * an #u2fs_rc error code. 191 | */ 192 | u2fs_rc u2fs_set_challenge(u2fs_ctx_t * ctx, const char *challenge) 193 | { 194 | if (ctx == NULL || challenge == NULL) 195 | return U2FS_MEMORY_ERROR; 196 | 197 | if (strlen(challenge) != U2FS_CHALLENGE_B64U_LEN) 198 | return U2FS_CHALLENGE_ERROR; 199 | 200 | strncpy(ctx->challenge, challenge, U2FS_CHALLENGE_B64U_LEN); 201 | 202 | return U2FS_OK; 203 | } 204 | 205 | /** 206 | * u2fs_set_keyHandle: 207 | * @ctx: a context handle, from u2fs_init() 208 | * @keyHandle: a registered key-handle in websafe Base64 form, to use for signing, as returned by the U2F registration. 209 | * 210 | * Stores a given @keyHandle within @ctx. If a value is already present, it is cleared and the memory is released. 211 | * 212 | * Returns: On success %U2FS_OK (integer 0) is returned, and on errors 213 | * an #u2fs_rc error code. 214 | */ 215 | u2fs_rc u2fs_set_keyHandle(u2fs_ctx_t * ctx, const char *keyHandle) 216 | { 217 | if (ctx == NULL || keyHandle == NULL) 218 | return U2FS_MEMORY_ERROR; 219 | 220 | if (ctx->keyHandle != NULL) { 221 | free(ctx->keyHandle); 222 | ctx->keyHandle = NULL; 223 | } 224 | 225 | ctx->keyHandle = strndup(keyHandle, strlen(keyHandle)); 226 | 227 | if (ctx->keyHandle == NULL) 228 | return U2FS_MEMORY_ERROR; 229 | 230 | return U2FS_OK; 231 | } 232 | 233 | /** 234 | * u2fs_set_publicKey: 235 | * @ctx: a context handle, from u2fs_init() 236 | * @publicKey: a 65-byte raw EC public key as returned from registration. 237 | * 238 | * Decode @publicKey and store within @ctx. If a value is already 239 | * present, it is cleared and the memory is released. 240 | * 241 | * Returns: On success %U2FS_OK (integer 0) is returned, and on errors 242 | * a #u2fs_rc error code. 243 | */ 244 | u2fs_rc 245 | u2fs_set_publicKey(u2fs_ctx_t * ctx, const unsigned char *publicKey) 246 | { 247 | u2fs_EC_KEY_t *user_key; 248 | u2fs_rc rc; 249 | 250 | if (ctx == NULL || publicKey == NULL) 251 | return U2FS_MEMORY_ERROR; 252 | 253 | rc = decode_user_key(publicKey, &user_key); 254 | if (rc != U2FS_OK) 255 | return rc; 256 | 257 | if (ctx->key != NULL) 258 | free_key(ctx->key); 259 | 260 | ctx->key = user_key; 261 | 262 | return U2FS_OK; 263 | } 264 | 265 | /** 266 | * u2fs_get_registration_keyHandle: 267 | * @result: a registration result obtained from u2fs_registration_verify() 268 | * 269 | * Get the Base64 keyHandle obtained during the U2F registration 270 | * operation. The memory is allocated by the library, and must not be 271 | * deallocated by the caller. 272 | * 273 | * Returns: On success the pointer to the buffer containing the keyHandle 274 | * is returned, and on errors NULL. 275 | */ 276 | const char *u2fs_get_registration_keyHandle(u2fs_reg_res_t * result) 277 | { 278 | if (result == NULL) 279 | return NULL; 280 | 281 | return result->keyHandle; 282 | } 283 | 284 | /** 285 | * u2fs_get_registration_publicKey: 286 | * @result: a registration result obtained from u2fs_registration_verify() 287 | * 288 | * Extract the raw user public key obtained during the U2F 289 | * registration operation. The memory is allocated by the library, 290 | * and must not be deallocated by the caller. The returned buffer 291 | * pointer holds %U2FS_PUBLIC_KEY_LEN bytes. 292 | * 293 | * Returns: On success the pointer to the buffer containing the user public key 294 | * is returned, and on errors NULL. 295 | */ 296 | const char *u2fs_get_registration_publicKey(u2fs_reg_res_t * result) 297 | { 298 | if (result == NULL) 299 | return NULL; 300 | 301 | return result->publicKey; 302 | } 303 | 304 | /** 305 | * u2fs_get_registration_attestation: 306 | * @result: a registration result obtained from u2fs_registration_verify() 307 | * 308 | * Extract the X509 attestation certificate (PEM format) obtained during the U2F 309 | * registration operation. The memory is allocated by the library, 310 | * and must not be deallocated by the caller. 311 | * 312 | * Returns: On success the pointer to the buffer containing the attestation 313 | * certificate is returned, and on errors NULL. 314 | */ 315 | const char *u2fs_get_registration_attestation(u2fs_reg_res_t * result) 316 | { 317 | if (result == NULL) 318 | return NULL; 319 | 320 | return (void*)result->attestation_certificate_PEM; 321 | } 322 | 323 | /** 324 | * u2fs_get_authentication_result: 325 | * @result: an authentication result obtained from u2fs_authentication_verify() 326 | * @verified: output parameter for the authentication result 327 | * @counter: output parameter for the counter value 328 | * @user_presence: output parameter for the user presence byte 329 | * 330 | * Unpack the authentication result obtained from a U2F authentication procedure 331 | * into its components. If any of the output parameters is set to NULL, that parameter 332 | * will be ignored. 333 | * 334 | * Returns: On success #U2FS_OK is returned, and on errors a #u2fs_rc error code. 335 | * The value @verified is set to #U2FS_OK on a successful authentication, and to 0 otherwise 336 | * @counter is filled with the value of the counter provided by the token. 337 | * A @user_presence value of 1 will determine the actual presence 338 | * of the user (yubikey touched) during the authentication. 339 | */ 340 | u2fs_rc u2fs_get_authentication_result(u2fs_auth_res_t * result, 341 | u2fs_rc *verified, 342 | uint32_t * counter, 343 | uint8_t * user_presence) 344 | { 345 | if (result == NULL) 346 | return U2FS_MEMORY_ERROR; 347 | 348 | if (verified) 349 | *verified = result->verified; 350 | 351 | if (counter) 352 | *counter = result->counter; 353 | 354 | if (user_presence) 355 | *user_presence = result->user_presence; 356 | 357 | return U2FS_OK; 358 | } 359 | 360 | /** 361 | * u2fs_set_origin: 362 | * @ctx: a context handle, from u2fs_init() 363 | * @origin: the origin of a registration request 364 | * 365 | * Stores @origin within @ctx. If a value is already present, it is cleared and the memory is released. 366 | * 367 | * Returns: On success %U2FS_OK (integer 0) is returned, and on errors 368 | * a #u2fs_rc error code. 369 | */ 370 | u2fs_rc u2fs_set_origin(u2fs_ctx_t * ctx, const char *origin) 371 | { 372 | if (ctx == NULL || origin == NULL) 373 | return U2FS_MEMORY_ERROR; 374 | 375 | if (ctx->origin != NULL) { 376 | free(ctx->origin); 377 | ctx->origin = NULL; 378 | } 379 | 380 | ctx->origin = strdup(origin); 381 | if (ctx->origin == NULL) 382 | return U2FS_MEMORY_ERROR; 383 | 384 | return U2FS_OK; 385 | } 386 | 387 | /** 388 | * u2fs_set_appid: 389 | * @ctx: a context handle, from u2fs_init() 390 | * @appid: the appid of a registration request 391 | * 392 | * Stores @appid within @ctx. If a value is already present, it is cleared and the memory is released. 393 | * 394 | * Returns: On success %U2FS_OK (integer 0) is returned, and on errors 395 | * a #u2fs_rc error code. 396 | */ 397 | u2fs_rc u2fs_set_appid(u2fs_ctx_t * ctx, const char *appid) 398 | { 399 | if (ctx == NULL || appid == NULL) 400 | return U2FS_MEMORY_ERROR; 401 | 402 | if (ctx->appid != NULL) { 403 | free(ctx->appid); 404 | ctx->appid = NULL; 405 | } 406 | 407 | ctx->appid = strdup(appid); 408 | if (ctx->appid == NULL) 409 | return U2FS_MEMORY_ERROR; 410 | 411 | return U2FS_OK; 412 | } 413 | 414 | static int registration_challenge_json(const char *challenge, 415 | const char *appid, char **output) 416 | { 417 | u2fs_rc rc = U2FS_JSON_ERROR; 418 | struct json_object *json_challenge = NULL; 419 | struct json_object *json_version = NULL; 420 | struct json_object *json_appid = NULL; 421 | struct json_object *json_output = NULL; 422 | const char *json_string = NULL; 423 | 424 | rc = U2FS_JSON_ERROR; 425 | 426 | json_challenge = json_object_new_string(challenge); 427 | if (json_challenge == NULL) 428 | goto done; 429 | json_version = json_object_new_string(U2F_VERSION); 430 | if (json_version == NULL) 431 | goto done; 432 | json_appid = json_object_new_string(appid); 433 | if (json_appid == NULL) 434 | goto done; 435 | 436 | json_output = json_object_new_object(); 437 | if (json_output == NULL) 438 | goto done; 439 | 440 | json_object_object_add(json_output, "challenge", json_object_get(json_challenge)); 441 | json_object_object_add(json_output, "version", json_object_get(json_version)); 442 | json_object_object_add(json_output, "appId", json_object_get(json_appid)); 443 | 444 | json_string = json_object_to_json_string(json_output); 445 | if (json_string == NULL) 446 | rc = U2FS_JSON_ERROR; 447 | else if ((*output = strdup(json_string)) == NULL) 448 | rc = U2FS_MEMORY_ERROR; 449 | else 450 | rc = U2FS_OK; 451 | 452 | done: 453 | json_object_put(json_output); 454 | json_object_put(json_challenge); 455 | json_object_put(json_version); 456 | json_object_put(json_appid); 457 | 458 | return rc; 459 | } 460 | 461 | /** 462 | * u2fs_registration_challenge: 463 | * @ctx: a context handle, from u2fs_init() 464 | * @output: pointer to output string with JSON data of RegistrationData. 465 | * 466 | * Get a U2F RegistrationData JSON structure, used as the challenge in 467 | * a U2F device registration. 468 | * 469 | * Returns: On success %U2FS_OK (integer 0) is returned, and on errors 470 | * an #u2fs_rc error code. 471 | */ 472 | u2fs_rc u2fs_registration_challenge(u2fs_ctx_t * ctx, char **output) 473 | { 474 | u2fs_rc rc = gen_challenge(ctx); 475 | if (rc != U2FS_OK) 476 | return rc; 477 | 478 | return registration_challenge_json(ctx->challenge, ctx->appid, output); 479 | } 480 | 481 | static u2fs_rc 482 | parse_clientData(const char *clientData, char **challenge, char **origin) 483 | { 484 | struct json_object *jo = json_tokener_parse(clientData); 485 | struct json_object *k; 486 | const char *p; 487 | 488 | if (clientData == NULL || challenge == NULL || origin == NULL) 489 | return U2FS_MEMORY_ERROR; 490 | 491 | if (jo == NULL) 492 | return U2FS_JSON_ERROR; 493 | 494 | if (u2fs_json_object_object_get(jo, "challenge", k) == FALSE) 495 | return U2FS_JSON_ERROR; 496 | 497 | p = json_object_get_string(k); 498 | if (p == NULL) 499 | return U2FS_JSON_ERROR; 500 | 501 | *challenge = strdup(p); 502 | if (*challenge == NULL) 503 | return U2FS_MEMORY_ERROR; 504 | 505 | if (u2fs_json_object_object_get(jo, "origin", k) == FALSE) 506 | return U2FS_JSON_ERROR; 507 | 508 | p = json_object_get_string(k); 509 | if (p == NULL) 510 | return U2FS_JSON_ERROR; 511 | 512 | *origin = strdup(p); 513 | if (*origin == NULL) 514 | return U2FS_JSON_ERROR; 515 | 516 | json_object_put(jo); 517 | 518 | return U2FS_OK; 519 | 520 | } 521 | 522 | /** 523 | * JSON decode 524 | */ 525 | static u2fs_rc 526 | parse_registration_response(const char *response, char **registrationData, 527 | char **clientData) 528 | { 529 | struct json_object *jo; 530 | struct json_object *k; 531 | const char *p; 532 | 533 | jo = json_tokener_parse(response); 534 | if (jo == NULL) 535 | return U2FS_JSON_ERROR; 536 | 537 | if (u2fs_json_object_object_get(jo, "registrationData", k) == FALSE) 538 | return U2FS_JSON_ERROR; 539 | p = json_object_get_string(k); 540 | if (p == NULL) 541 | return U2FS_JSON_ERROR; 542 | *registrationData = strdup(p); 543 | if (*registrationData == NULL) 544 | return U2FS_MEMORY_ERROR; 545 | 546 | if (u2fs_json_object_object_get(jo, "clientData", k) == FALSE) 547 | return U2FS_JSON_ERROR; 548 | p = json_object_get_string(k); 549 | if (p == NULL) 550 | return U2FS_JSON_ERROR; 551 | *clientData = strdup(p); 552 | if (*clientData == NULL) 553 | return U2FS_MEMORY_ERROR; 554 | 555 | json_object_put(jo); 556 | 557 | return U2FS_OK; 558 | } 559 | 560 | static void dumpHex(const unsigned char *data, int offs, int len) 561 | { 562 | int i; 563 | for (i = offs; i < len; i++) { 564 | if (i % 16 == 0) 565 | fprintf(stderr, "\n"); 566 | fprintf(stderr, "%02x ", data[i] & 0xFF); 567 | } 568 | fprintf(stderr, "\n"); 569 | } 570 | 571 | /** 572 | * Parse and validate the registration response. 573 | */ 574 | static u2fs_rc 575 | parse_registrationData2(const unsigned char *data, size_t len, 576 | unsigned char **user_public_key, 577 | size_t * keyHandle_len, char **keyHandle, 578 | u2fs_X509_t ** attestation_certificate, 579 | u2fs_ECDSA_t ** signature) 580 | { 581 | /* 582 | +-------------------------------------------------------------------+ 583 | | 1 | 65 | 1 | L | implied | 64 | 584 | +-------------------------------------------------------------------+ 585 | 0x05 586 | public key 587 | key handle length 588 | key handle 589 | attestation cert 590 | signature 591 | */ 592 | 593 | int offset = 0; 594 | size_t attestation_certificate_len; 595 | u2fs_rc rc; 596 | 597 | if (len <= 1 + 65 + 1 + 64) { 598 | if (debug) 599 | fprintf(stderr, "Length mismatch\n"); 600 | return U2FS_FORMAT_ERROR; 601 | } 602 | 603 | if (data[offset++] != 0x05) { 604 | if (debug) 605 | fprintf(stderr, "Reserved byte mismatch\n"); 606 | return U2FS_FORMAT_ERROR; 607 | } 608 | 609 | *user_public_key = calloc(sizeof(unsigned char), U2FS_PUBLIC_KEY_LEN); 610 | 611 | if (*user_public_key == NULL) { 612 | if (debug) 613 | fprintf(stderr, "Memory error\n"); 614 | return U2FS_MEMORY_ERROR; 615 | } 616 | 617 | memcpy(*user_public_key, data + offset, U2FS_PUBLIC_KEY_LEN); 618 | 619 | offset += U2FS_PUBLIC_KEY_LEN; 620 | 621 | *keyHandle_len = data[offset++]; 622 | 623 | *keyHandle = calloc(sizeof(char), *keyHandle_len); 624 | if (*keyHandle == NULL) 625 | return U2FS_MEMORY_ERROR; 626 | 627 | memcpy(*keyHandle, data + offset, *keyHandle_len); 628 | 629 | if (*keyHandle == NULL) { 630 | if (debug) 631 | fprintf(stderr, "Memory error\n"); 632 | 633 | free(*user_public_key); 634 | 635 | keyHandle_len = 0; 636 | *user_public_key = NULL; 637 | 638 | return U2FS_MEMORY_ERROR; 639 | } 640 | 641 | if (debug) 642 | fprintf(stderr, "Key handle length: %d\n", (int) *keyHandle_len); 643 | 644 | offset += *keyHandle_len; 645 | 646 | // Skip over offset and offset+1 (0x30, 0x82 respecitvely) 647 | // Length is big-endian encoded in offset+3 and offset+4 648 | attestation_certificate_len = 649 | (data[offset + 2] << 8) + data[offset + 3] + 4; 650 | 651 | rc = decode_X509(data + offset, attestation_certificate_len, 652 | attestation_certificate); 653 | 654 | if (rc != U2FS_OK) { 655 | return rc; 656 | } 657 | 658 | if (debug) 659 | dumpCert(*attestation_certificate); 660 | 661 | offset += attestation_certificate_len; 662 | 663 | size_t signature_len = len - offset; 664 | rc = decode_ECDSA(data + offset, signature_len, signature); 665 | 666 | if (rc != U2FS_OK) { 667 | free(*user_public_key); 668 | free(*keyHandle); 669 | 670 | *user_public_key = NULL; 671 | *keyHandle_len = 0; 672 | *keyHandle = NULL; 673 | 674 | if (debug) 675 | fprintf(stderr, "Unable to decode signature\n"); 676 | 677 | return rc; 678 | } 679 | 680 | return U2FS_OK; 681 | } 682 | 683 | static u2fs_rc parse_registrationData(const char *registrationData, 684 | unsigned char **user_public_key, 685 | size_t * keyHandle_len, 686 | char **keyHandle, 687 | u2fs_X509_t ** 688 | attestation_certificate, 689 | u2fs_ECDSA_t ** signature) 690 | { 691 | base64_decodestate b64; 692 | size_t registrationData_len = strlen(registrationData); 693 | unsigned char *data; 694 | int data_len; 695 | u2fs_rc rc; 696 | 697 | data = malloc(registrationData_len + 1); 698 | if (data == NULL) 699 | return U2FS_MEMORY_ERROR; 700 | 701 | data[registrationData_len] = '\0'; 702 | 703 | base64_init_decodestate(&b64); 704 | data_len = 705 | base64_decode_block(registrationData, registrationData_len, 706 | (char *) data, &b64); 707 | 708 | if (debug) { 709 | fprintf(stderr, "registrationData Hex: "); 710 | dumpHex((unsigned char *) data, 0, data_len); 711 | } 712 | 713 | rc = parse_registrationData2(data, data_len, 714 | user_public_key, keyHandle_len, keyHandle, 715 | attestation_certificate, signature); 716 | 717 | free(data); 718 | data = NULL; 719 | 720 | return rc; 721 | } 722 | 723 | static u2fs_rc decode_clientData(const char *clientData, char **output) 724 | { 725 | base64_decodestate b64; 726 | size_t clientData_len = strlen(clientData); 727 | char *data; 728 | u2fs_rc rc = 0; 729 | 730 | if (output == NULL) 731 | return U2FS_MEMORY_ERROR; 732 | 733 | data = calloc(sizeof(char), clientData_len); 734 | if (data == NULL) 735 | return U2FS_MEMORY_ERROR; 736 | 737 | base64_init_decodestate(&b64); 738 | base64_decode_block(clientData, clientData_len, data, &b64); 739 | 740 | if (debug) { 741 | fprintf(stderr, "clientData: %s\n", data); 742 | } 743 | 744 | *output = strndup(data, strlen(data)); 745 | 746 | free(data); 747 | data = NULL; 748 | 749 | if (*output == NULL) { 750 | fprintf(stderr, "Memory Error\n"); 751 | return U2FS_MEMORY_ERROR; 752 | } 753 | 754 | return rc; 755 | } 756 | 757 | /** 758 | * u2fs_registration_verify: 759 | * @ctx: a context handle, from u2fs_init(). 760 | * @response: a U2F registration response message Base64 encoded. 761 | * @output: pointer to output structure containing the relevant data for a well formed request. Memory should be free'd. 762 | * 763 | * Get a U2F registration response and check its validity. 764 | * 765 | * Returns: On success %U2FS_OK (integer 0) is returned and @output is filled up with the user public key, the key handle and the attestation certificate. On errors 766 | * a #u2fs_rc error code. 767 | */ 768 | u2fs_rc u2fs_registration_verify(u2fs_ctx_t * ctx, const char *response, 769 | u2fs_reg_res_t ** output) 770 | { 771 | char *registrationData; 772 | char *clientData; 773 | char *clientData_decoded; 774 | unsigned char *user_public_key; 775 | size_t keyHandle_len; 776 | char *keyHandle; 777 | char *origin; 778 | char *challenge; 779 | char buf[_B64_BUFSIZE]; 780 | unsigned char c = 0; 781 | u2fs_X509_t *attestation_certificate; 782 | u2fs_ECDSA_t *signature; 783 | u2fs_EC_KEY_t *key; 784 | u2fs_rc rc; 785 | 786 | if (ctx == NULL || response == NULL || output == NULL) 787 | return U2FS_MEMORY_ERROR; 788 | 789 | key = NULL; 790 | clientData_decoded = NULL; 791 | challenge = NULL; 792 | origin = NULL; 793 | attestation_certificate = NULL; 794 | user_public_key = NULL; 795 | signature = NULL; 796 | registrationData = NULL; 797 | clientData = NULL; 798 | keyHandle = NULL; 799 | *output = NULL; 800 | 801 | rc = parse_registration_response(response, ®istrationData, 802 | &clientData); 803 | if (rc != U2FS_OK) 804 | goto failure; 805 | 806 | if (debug) { 807 | fprintf(stderr, "registrationData: %s\n", registrationData); 808 | fprintf(stderr, "clientData: %s\n", clientData); 809 | } 810 | 811 | rc = parse_registrationData(registrationData, &user_public_key, 812 | &keyHandle_len, &keyHandle, 813 | &attestation_certificate, &signature); 814 | if (rc != U2FS_OK) 815 | goto failure; 816 | 817 | rc = extract_EC_KEY_from_X509(attestation_certificate, &key); 818 | 819 | if (rc != U2FS_OK) 820 | goto failure; 821 | 822 | //TODO Add certificate validation 823 | 824 | rc = decode_clientData(clientData, &clientData_decoded); 825 | 826 | if (rc != U2FS_OK) 827 | goto failure; 828 | 829 | rc = parse_clientData(clientData_decoded, &challenge, &origin); 830 | 831 | if (rc != U2FS_OK) 832 | goto failure; 833 | 834 | 835 | rc = gen_challenge(ctx); 836 | if (rc != U2FS_OK) 837 | goto failure; 838 | 839 | if (strcmp(ctx->challenge, challenge) != 0) { 840 | rc = U2FS_CHALLENGE_ERROR; 841 | goto failure; 842 | } 843 | 844 | if (strcmp(ctx->origin, origin) != 0) { 845 | rc = U2FS_ORIGIN_ERROR; 846 | goto failure; 847 | } 848 | 849 | struct sha256_state sha_ctx; 850 | char challenge_parameter[U2FS_HASH_LEN], 851 | application_parameter[U2FS_HASH_LEN]; 852 | 853 | sha256_init(&sha_ctx); 854 | sha256_process(&sha_ctx, (unsigned char *) ctx->appid, 855 | strlen(ctx->appid)); 856 | sha256_done(&sha_ctx, (unsigned char *) application_parameter); 857 | 858 | sha256_init(&sha_ctx); 859 | sha256_process(&sha_ctx, (unsigned char *) clientData_decoded, 860 | strlen(clientData_decoded)); 861 | sha256_done(&sha_ctx, (unsigned char *) challenge_parameter); 862 | 863 | unsigned char dgst[U2FS_HASH_LEN]; 864 | sha256_init(&sha_ctx); 865 | sha256_process(&sha_ctx, &c, 1); 866 | sha256_process(&sha_ctx, (unsigned char *) application_parameter, 867 | U2FS_HASH_LEN); 868 | sha256_process(&sha_ctx, (unsigned char *) challenge_parameter, 869 | U2FS_HASH_LEN); 870 | sha256_process(&sha_ctx, (unsigned char *) keyHandle, keyHandle_len); 871 | sha256_process(&sha_ctx, user_public_key, U2FS_PUBLIC_KEY_LEN); 872 | sha256_done(&sha_ctx, dgst); 873 | 874 | rc = verify_ECDSA(dgst, U2FS_HASH_LEN, signature, key); 875 | 876 | if (rc != U2FS_OK) 877 | goto failure; 878 | 879 | free_sig(signature); 880 | signature = NULL; 881 | 882 | *output = calloc(1, sizeof(**output)); 883 | if (*output == NULL) { 884 | rc = U2FS_MEMORY_ERROR; 885 | goto failure; 886 | } 887 | 888 | rc = encode_b64u(keyHandle, keyHandle_len, buf); 889 | if (rc != U2FS_OK) 890 | goto failure; 891 | 892 | u2fs_EC_KEY_t *key_ptr; 893 | (*output)->keyHandle = strndup(buf, strlen(buf)); 894 | 895 | rc = decode_user_key(user_public_key, &key_ptr); 896 | if (rc != U2FS_OK) 897 | goto failure; 898 | 899 | (*output)->attestation_certificate = dup_cert(attestation_certificate); 900 | 901 | rc = dump_user_key(key_ptr, &(*output)->publicKey); 902 | if (rc != U2FS_OK) 903 | goto failure; 904 | 905 | rc = dump_X509_cert(attestation_certificate, &(*output)->attestation_certificate_PEM); 906 | if (rc != U2FS_OK) 907 | goto failure; 908 | 909 | if ((*output)->keyHandle == NULL 910 | || (*output)->publicKey == NULL 911 | || (*output)->attestation_certificate == NULL) { 912 | rc = U2FS_MEMORY_ERROR; 913 | goto failure; 914 | } 915 | 916 | free_key(key); 917 | key = NULL; 918 | 919 | free_cert(attestation_certificate); 920 | attestation_certificate = NULL; 921 | 922 | free(clientData_decoded); 923 | clientData_decoded = NULL; 924 | 925 | free(challenge); 926 | challenge = NULL; 927 | 928 | free(origin); 929 | origin = NULL; 930 | 931 | free(user_public_key); 932 | user_public_key = NULL; 933 | 934 | free(registrationData); 935 | registrationData = NULL; 936 | 937 | free(clientData); 938 | clientData = NULL; 939 | 940 | free(keyHandle); 941 | keyHandle = NULL; 942 | 943 | return U2FS_OK; 944 | 945 | failure: 946 | if (key) { 947 | free_key(key); 948 | key = NULL; 949 | } 950 | 951 | if (clientData_decoded) { 952 | free(clientData_decoded); 953 | clientData_decoded = NULL; 954 | } 955 | 956 | if (challenge) { 957 | free(challenge); 958 | challenge = NULL; 959 | } 960 | 961 | if (origin) { 962 | free(origin); 963 | origin = NULL; 964 | } 965 | 966 | if (attestation_certificate) { 967 | free_cert(attestation_certificate); 968 | attestation_certificate = NULL; 969 | } 970 | 971 | if (user_public_key) { 972 | free(user_public_key); 973 | user_public_key = NULL; 974 | } 975 | 976 | if (signature) { 977 | free_sig(signature); 978 | signature = NULL; 979 | } 980 | 981 | if (registrationData) { 982 | free(registrationData); 983 | registrationData = NULL; 984 | } 985 | 986 | if (clientData) { 987 | free(clientData); 988 | clientData = NULL; 989 | } 990 | 991 | if (keyHandle) { 992 | free(keyHandle); 993 | keyHandle = NULL; 994 | } 995 | 996 | return rc; 997 | } 998 | 999 | static int authentication_challenge_json(const char *challenge, 1000 | const char *keyHandle, 1001 | const char *appid, char **output) 1002 | { 1003 | u2fs_rc rc = U2FS_JSON_ERROR; 1004 | struct json_object *json_challenge = NULL; 1005 | struct json_object *json_key = NULL; 1006 | struct json_object *json_version = NULL; 1007 | struct json_object *json_appid = NULL; 1008 | struct json_object *json_output = NULL; 1009 | const char *json_string = NULL; 1010 | 1011 | rc = U2FS_JSON_ERROR; 1012 | 1013 | json_key = json_object_new_string(keyHandle); 1014 | if (json_key == NULL) 1015 | goto done; 1016 | json_version = json_object_new_string(U2F_VERSION); 1017 | if (json_version == NULL) 1018 | goto done; 1019 | json_challenge = json_object_new_string(challenge); 1020 | if (json_challenge == NULL) 1021 | goto done; 1022 | json_appid = json_object_new_string(appid); 1023 | if (json_appid == NULL) 1024 | goto done; 1025 | 1026 | json_output = json_object_new_object(); 1027 | if (json_output == NULL) 1028 | goto done; 1029 | 1030 | json_object_object_add(json_output, "keyHandle", json_object_get(json_key)); 1031 | json_object_object_add(json_output, "version", json_object_get(json_version)); 1032 | json_object_object_add(json_output, "challenge", json_object_get(json_challenge)); 1033 | json_object_object_add(json_output, "appId", json_object_get(json_appid)); 1034 | 1035 | json_string = json_object_to_json_string(json_output); 1036 | 1037 | if (json_string == NULL) 1038 | rc = U2FS_JSON_ERROR; 1039 | else if ((*output = strdup(json_string)) == NULL) 1040 | rc = U2FS_MEMORY_ERROR; 1041 | else 1042 | rc = U2FS_OK; 1043 | 1044 | done: 1045 | json_object_put(json_output); 1046 | json_object_put(json_challenge); 1047 | json_object_put(json_key); 1048 | json_object_put(json_version); 1049 | json_object_put(json_appid); 1050 | 1051 | return rc; 1052 | } 1053 | 1054 | static u2fs_rc 1055 | parse_signatureData2(const unsigned char *data, size_t len, 1056 | uint8_t * user_presence, uint32_t * counter, 1057 | u2fs_ECDSA_t ** signature) 1058 | { 1059 | /* 1060 | +-----------------------------------+ 1061 | | 1 | 4 | implied | 1062 | +-----------------------------------+ 1063 | user presence 1064 | counter 1065 | signature 1066 | */ 1067 | 1068 | int offset = 0; 1069 | u2fs_rc rc; 1070 | 1071 | if (len <= 1 + U2FS_COUNTER_LEN) { 1072 | if (debug) 1073 | fprintf(stderr, "Length mismatch\n"); 1074 | return U2FS_FORMAT_ERROR; 1075 | } 1076 | 1077 | *user_presence = data[offset++] & 0x01; 1078 | 1079 | if (*user_presence == 0) { 1080 | if (debug) 1081 | fprintf(stderr, "User presence byte mismatch\n"); 1082 | return U2FS_FORMAT_ERROR; 1083 | } 1084 | 1085 | memcpy((char *) counter, data + offset, U2FS_COUNTER_LEN); 1086 | 1087 | offset += U2FS_COUNTER_LEN; 1088 | 1089 | size_t signature_len = len - offset; 1090 | rc = decode_ECDSA(data + offset, signature_len, signature); 1091 | 1092 | if (rc != U2FS_OK) { 1093 | return rc; 1094 | } 1095 | 1096 | return U2FS_OK; 1097 | } 1098 | 1099 | static u2fs_rc 1100 | parse_signatureData(const char *signatureData, uint8_t * user_presence, 1101 | uint32_t * counter, u2fs_ECDSA_t ** signature) 1102 | { 1103 | 1104 | base64_decodestate b64; 1105 | size_t signatureData_len = strlen(signatureData); 1106 | unsigned char *data; 1107 | int data_len; 1108 | u2fs_rc rc; 1109 | 1110 | data = malloc(signatureData_len + 1); 1111 | if (data == NULL) 1112 | return U2FS_MEMORY_ERROR; 1113 | 1114 | data[signatureData_len] = '\0'; 1115 | 1116 | base64_init_decodestate(&b64); 1117 | data_len = 1118 | base64_decode_block(signatureData, signatureData_len, (char *) data, 1119 | &b64); 1120 | 1121 | if (debug) { 1122 | fprintf(stderr, "signatureData Hex: "); 1123 | dumpHex((unsigned char *) data, 0, data_len); 1124 | } 1125 | 1126 | rc = parse_signatureData2(data, data_len, user_presence, counter, 1127 | signature); 1128 | 1129 | free(data); 1130 | data = NULL; 1131 | 1132 | return rc; 1133 | } 1134 | 1135 | static u2fs_rc 1136 | parse_authentication_response(const char *response, char **signatureData, 1137 | char **clientData, char **keyHandle) 1138 | { 1139 | struct json_object *jo; 1140 | struct json_object *k; 1141 | const char *p; 1142 | 1143 | jo = json_tokener_parse(response); 1144 | if (jo == NULL) 1145 | return U2FS_JSON_ERROR; 1146 | 1147 | if (u2fs_json_object_object_get(jo, "signatureData", k) == FALSE) 1148 | return U2FS_JSON_ERROR; 1149 | p = json_object_get_string(k); 1150 | if (p == NULL) 1151 | return U2FS_JSON_ERROR; 1152 | *signatureData = strdup(p); 1153 | if (*signatureData == NULL) 1154 | return U2FS_MEMORY_ERROR; 1155 | 1156 | if (u2fs_json_object_object_get(jo, "clientData", k) == FALSE) 1157 | return U2FS_JSON_ERROR; 1158 | p = json_object_get_string(k); 1159 | if (p == NULL) 1160 | return U2FS_JSON_ERROR; 1161 | *clientData = strdup(p); 1162 | if (*clientData == NULL) 1163 | return U2FS_MEMORY_ERROR; 1164 | 1165 | if (u2fs_json_object_object_get(jo, "keyHandle", k) == FALSE) 1166 | return U2FS_JSON_ERROR; 1167 | p = json_object_get_string(k); 1168 | if (p == NULL) 1169 | return U2FS_JSON_ERROR; 1170 | *keyHandle = strdup(p); 1171 | if (*keyHandle == NULL) 1172 | return U2FS_MEMORY_ERROR; 1173 | 1174 | json_object_put(jo); 1175 | 1176 | return U2FS_OK; 1177 | } 1178 | 1179 | /** 1180 | * u2fs_authentication_verify: 1181 | * @ctx: a context handle, from u2fs_init() 1182 | * @response: pointer to output string with JSON data. 1183 | * @output: pointer to output structure containing the relevant data for a well formed request. Memory should be free'd. 1184 | * 1185 | * Get a U2F authentication response and check its validity. 1186 | * 1187 | * Returns: On a successful verification %U2FS_OK (integer 0) is returned and @output is filled with the authentication result (same as the returned value), the counter received from the token and the user presence information. On errors 1188 | * a #u2fs_rc error code is returned. 1189 | */ 1190 | u2fs_rc u2fs_authentication_verify(u2fs_ctx_t * ctx, const char *response, 1191 | u2fs_auth_res_t ** output) 1192 | { 1193 | char *signatureData; 1194 | char *clientData; 1195 | char *clientData_decoded; 1196 | char *keyHandle; 1197 | char *challenge; 1198 | char *origin; 1199 | uint8_t user_presence; 1200 | uint32_t counter_num; 1201 | uint32_t counter; 1202 | u2fs_ECDSA_t *signature; 1203 | u2fs_rc rc; 1204 | 1205 | if (ctx == NULL || response == NULL || output == NULL) 1206 | return U2FS_MEMORY_ERROR; 1207 | 1208 | signatureData = NULL; 1209 | clientData = NULL; 1210 | clientData_decoded = NULL; 1211 | keyHandle = NULL; 1212 | challenge = NULL; 1213 | origin = NULL; 1214 | signature = NULL; 1215 | *output = NULL; 1216 | 1217 | rc = parse_authentication_response(response, &signatureData, 1218 | &clientData, &keyHandle); 1219 | if (rc != U2FS_OK) 1220 | goto failure; 1221 | 1222 | if (debug) { 1223 | fprintf(stderr, "signatureData: %s\n", signatureData); 1224 | fprintf(stderr, "clientData: %s\n", clientData); 1225 | fprintf(stderr, "keyHandle: %s\n", keyHandle); 1226 | } 1227 | 1228 | rc = parse_signatureData(signatureData, &user_presence, 1229 | &counter, &signature); 1230 | if (rc != U2FS_OK) 1231 | goto failure; 1232 | 1233 | rc = decode_clientData(clientData, &clientData_decoded); 1234 | 1235 | if (rc != U2FS_OK) 1236 | goto failure; 1237 | 1238 | rc = parse_clientData(clientData_decoded, &challenge, &origin); 1239 | 1240 | if (rc != U2FS_OK) 1241 | goto failure; 1242 | 1243 | if (strcmp(ctx->challenge, challenge) != 0) { 1244 | rc = U2FS_CHALLENGE_ERROR; 1245 | goto failure; 1246 | } 1247 | 1248 | if (strcmp(ctx->origin, origin) != 0) { 1249 | rc = U2FS_ORIGIN_ERROR; 1250 | goto failure; 1251 | } 1252 | 1253 | struct sha256_state sha_ctx; 1254 | char challenge_parameter[U2FS_HASH_LEN], 1255 | application_parameter[U2FS_HASH_LEN]; 1256 | 1257 | sha256_init(&sha_ctx); 1258 | sha256_process(&sha_ctx, (unsigned char *) ctx->appid, 1259 | strlen(ctx->appid)); 1260 | sha256_done(&sha_ctx, (unsigned char *) application_parameter); 1261 | 1262 | sha256_init(&sha_ctx); 1263 | sha256_process(&sha_ctx, (unsigned char *) clientData_decoded, 1264 | strlen(clientData_decoded)); 1265 | sha256_done(&sha_ctx, (unsigned char *) challenge_parameter); 1266 | 1267 | unsigned char dgst[U2FS_HASH_LEN]; 1268 | sha256_init(&sha_ctx); 1269 | sha256_process(&sha_ctx, (unsigned char *) application_parameter, 1270 | U2FS_HASH_LEN); 1271 | sha256_process(&sha_ctx, (unsigned char *) &user_presence, 1); 1272 | sha256_process(&sha_ctx, (unsigned char *) &counter, U2FS_COUNTER_LEN); 1273 | sha256_process(&sha_ctx, (unsigned char *) challenge_parameter, 1274 | U2FS_HASH_LEN); 1275 | sha256_done(&sha_ctx, dgst); 1276 | 1277 | rc = verify_ECDSA(dgst, U2FS_HASH_LEN, signature, ctx->key); 1278 | 1279 | if (rc != U2FS_OK) 1280 | goto failure; 1281 | 1282 | free_sig(signature); 1283 | signature = NULL; 1284 | 1285 | *output = calloc(1, sizeof(**output)); 1286 | if (*output == NULL) { 1287 | rc = U2FS_MEMORY_ERROR; 1288 | goto failure; 1289 | } 1290 | 1291 | counter_num = 0; 1292 | counter_num |= (counter & 0xFF000000) >> 24; 1293 | counter_num |= (counter & 0x00FF0000) >> 8; 1294 | counter_num |= (counter & 0x0000FF00) << 8; 1295 | counter_num |= (counter & 0x000000FF) << 24; 1296 | 1297 | (*output)->verified = U2FS_OK; 1298 | (*output)->user_presence = user_presence; 1299 | (*output)->counter = counter_num; 1300 | 1301 | free(origin); 1302 | origin = NULL; 1303 | 1304 | free(challenge); 1305 | challenge = NULL; 1306 | 1307 | free(keyHandle); 1308 | keyHandle = NULL; 1309 | 1310 | free(signatureData); 1311 | signatureData = NULL; 1312 | 1313 | free(clientData); 1314 | clientData = NULL; 1315 | 1316 | free(clientData_decoded); 1317 | clientData_decoded = NULL; 1318 | 1319 | return U2FS_OK; 1320 | 1321 | failure: 1322 | if (clientData_decoded) { 1323 | free(clientData_decoded); 1324 | clientData_decoded = NULL; 1325 | } 1326 | 1327 | if (challenge) { 1328 | free(challenge); 1329 | challenge = NULL; 1330 | } 1331 | 1332 | if (origin) { 1333 | free(origin); 1334 | origin = NULL; 1335 | } 1336 | 1337 | if (signature) { 1338 | free_sig(signature); 1339 | signature = NULL; 1340 | } 1341 | 1342 | if (signatureData) { 1343 | free(signatureData); 1344 | signatureData = NULL; 1345 | } 1346 | 1347 | if (clientData) { 1348 | free(clientData); 1349 | clientData = NULL; 1350 | } 1351 | 1352 | if (keyHandle) { 1353 | free(keyHandle); 1354 | keyHandle = NULL; 1355 | } 1356 | 1357 | return rc; 1358 | } 1359 | 1360 | /** 1361 | * u2fs_authentication_challenge: 1362 | * @ctx: a context handle, from u2fs_init() 1363 | * @output: pointer to output string with JSON data of AuthenticationData. 1364 | * 1365 | * Get a U2F AuthenticationData JSON structure, used as the challenge in 1366 | * a U2F authentication procedure. 1367 | * 1368 | * Returns: On success %U2FS_OK (integer 0) is returned, and on errors 1369 | * a #u2fs_rc error code. 1370 | */ 1371 | u2fs_rc u2fs_authentication_challenge(u2fs_ctx_t * ctx, char **output) 1372 | { 1373 | u2fs_rc rc; 1374 | 1375 | if (ctx->keyHandle == NULL) 1376 | return U2FS_MEMORY_ERROR; 1377 | 1378 | rc = gen_challenge(ctx); 1379 | if (rc != U2FS_OK) 1380 | return rc; 1381 | 1382 | return authentication_challenge_json(ctx->challenge, 1383 | ctx->keyHandle, ctx->appid, output); 1384 | } 1385 | -------------------------------------------------------------------------------- /u2f-server/crypto.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Yubico AB 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above 13 | * copyright notice, this list of conditions and the following 14 | * disclaimer in the documentation and/or other materials provided 15 | * with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef U2FS_CRYPTO_H 31 | #define U2FS_CRYPTO_H 32 | 33 | #include "internal.h" 34 | 35 | #ifdef MAKE_CHECK 36 | int debug = 1; 37 | #endif 38 | 39 | void dumpCert(const u2fs_X509_t * certificate); 40 | 41 | void crypto_init(void); 42 | void crypto_release(void); 43 | 44 | void free_key(u2fs_EC_KEY_t * key); 45 | void free_cert(u2fs_X509_t * cert); 46 | void free_sig(u2fs_ECDSA_t * sig); 47 | 48 | 49 | u2fs_rc set_random_bytes(char *data, size_t len); 50 | 51 | u2fs_rc decode_X509(const unsigned char *data, size_t len, 52 | u2fs_X509_t ** cert); 53 | u2fs_rc decode_ECDSA(const unsigned char *data, size_t len, 54 | u2fs_ECDSA_t ** sig); 55 | u2fs_rc decode_user_key(const unsigned char *data, u2fs_EC_KEY_t ** key); 56 | 57 | u2fs_rc verify_ECDSA(const unsigned char *dgst, int dgst_len, 58 | const u2fs_ECDSA_t * sig, u2fs_EC_KEY_t * eckey); 59 | 60 | u2fs_rc extract_EC_KEY_from_X509(const u2fs_X509_t * cert, 61 | u2fs_EC_KEY_t ** key); 62 | u2fs_EC_KEY_t *dup_key(const u2fs_EC_KEY_t * key); 63 | u2fs_X509_t *dup_cert(const u2fs_X509_t * cert); 64 | 65 | u2fs_rc dump_user_key(const u2fs_EC_KEY_t * key, char **output); 66 | u2fs_rc dump_X509_cert(const u2fs_X509_t * cert, char **output); 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /u2f-server/error.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Yubico AB 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above 13 | * copyright notice, this list of conditions and the following 14 | * disclaimer in the documentation and/or other materials provided 15 | * with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include 31 | 32 | #define ERR(name, desc) { name, #name, desc } 33 | 34 | typedef struct { 35 | int rc; 36 | const char *name; 37 | const char *description; 38 | } err_t; 39 | 40 | static const err_t errors[] = { 41 | ERR(U2FS_OK, "Successful return"), 42 | ERR(U2FS_MEMORY_ERROR, "Memory error (e.g., out of memory)"), 43 | ERR(U2FS_JSON_ERROR, "Error in JSON handling"), 44 | ERR(U2FS_BASE64_ERROR, "Base64 error"), 45 | ERR(U2FS_CRYPTO_ERROR, "Crypto error"), 46 | ERR(U2FS_ORIGIN_ERROR, "Origin mismatch"), 47 | ERR(U2FS_CHALLENGE_ERROR, "Challenge error"), 48 | ERR(U2FS_SIGNATURE_ERROR, "Unable to verify signature"), 49 | ERR(U2FS_FORMAT_ERROR, "Format mismatch") 50 | }; 51 | 52 | /** 53 | * u2fs_strerror: 54 | * @err: error code 55 | * 56 | * Convert return code to human readable string explanation of the 57 | * reason for the particular error code. 58 | * 59 | * This string can be used to output a diagnostic message to the user. 60 | * 61 | * This function is one of few in the library that can be used without 62 | * a successful call to u2fs_global_init(). 63 | * 64 | * Return value: Returns a pointer to a statically allocated string 65 | * containing an explanation of the error code @err. 66 | **/ 67 | const char *u2fs_strerror(int err) 68 | { 69 | static const char *unknown = "Unknown libu2f-server error"; 70 | const char *p; 71 | 72 | if (-err < 0 || -err >= (int) (sizeof(errors) / sizeof(errors[0]))) 73 | return unknown; 74 | 75 | p = errors[-err].description; 76 | if (!p) 77 | p = unknown; 78 | 79 | return p; 80 | } 81 | 82 | /** 83 | * u2fs_strerror_name: 84 | * @err: error code 85 | * 86 | * Convert return code to human readable string representing the error 87 | * code symbol itself. For example, u2fs_strerror_name(%U2FS_OK) 88 | * returns the string "U2FS_OK". 89 | * 90 | * This string can be used to output a diagnostic message to the user. 91 | * 92 | * This function is one of few in the library that can be used without 93 | * a successful call to u2fs_global_init(). 94 | * 95 | * Return value: Returns a pointer to a statically allocated string 96 | * containing a string version of the error code @err, or NULL if 97 | * the error code is not known. 98 | **/ 99 | const char *u2fs_strerror_name(int err) 100 | { 101 | if (-err < 0 || -err >= (int) (sizeof(errors) / sizeof(errors[0]))) 102 | return NULL; 103 | 104 | return errors[-err].name; 105 | } 106 | -------------------------------------------------------------------------------- /u2f-server/global.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Yubico AB 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above 13 | * copyright notice, this list of conditions and the following 14 | * disclaimer in the documentation and/or other materials provided 15 | * with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include "internal.h" 31 | #include "crypto.h" 32 | 33 | int debug; 34 | 35 | /** 36 | * u2fs_global_init: 37 | * @flags: initialization flags, ORed #u2fs_initflags. 38 | * 39 | * Initialize the library. This function is not guaranteed to be 40 | * thread safe and must be invoked on application startup. 41 | * 42 | * Returns: On success %U2FS_OK (integer 0) is returned, and on errors 43 | * an #u2fs_rc error code. 44 | */ 45 | u2fs_rc u2fs_global_init(u2fs_initflags flags) 46 | { 47 | if (flags & U2FS_DEBUG) 48 | debug = 1; 49 | 50 | crypto_init(); 51 | 52 | return U2FS_OK; 53 | } 54 | 55 | /** 56 | * u2fs_global_done: 57 | * 58 | * Release all resources from the library. Call this function when no 59 | * further use of the library is needed. 60 | */ 61 | void u2fs_global_done(void) 62 | { 63 | debug = 0; 64 | 65 | crypto_release(); 66 | } 67 | -------------------------------------------------------------------------------- /u2f-server/internal.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Yubico AB 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above 13 | * copyright notice, this list of conditions and the following 14 | * disclaimer in the documentation and/or other materials provided 15 | * with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef INTERNAL_H 31 | #define INTERNAL_H 32 | 33 | #include 34 | #include 35 | #include 36 | 37 | typedef void *u2fs_ECDSA_t; 38 | typedef void *u2fs_X509_t; 39 | typedef void *u2fs_EC_KEY_t; 40 | 41 | extern int debug; 42 | 43 | #define _SHA256_LEN 32 44 | #define _B64_BUFSIZE 2048 45 | 46 | #define U2F_VERSION "U2F_V2" 47 | #define U2FS_HASH_LEN _SHA256_LEN 48 | 49 | struct u2fs_reg_res { 50 | char *keyHandle; 51 | char *publicKey; 52 | u2fs_X509_t *attestation_certificate; 53 | 54 | char *attestation_certificate_PEM; 55 | u2fs_EC_KEY_t *user_public_key; 56 | }; 57 | 58 | struct u2fs_auth_res { 59 | int verified; 60 | uint32_t counter; 61 | uint8_t user_presence; 62 | }; 63 | 64 | struct u2fs_ctx { 65 | char challenge[U2FS_CHALLENGE_B64U_LEN + 1]; 66 | char *keyHandle; 67 | u2fs_EC_KEY_t *key; 68 | char *origin; 69 | char *appid; 70 | }; 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /u2f-server/openssl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Yubico AB 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above 13 | * copyright notice, this list of conditions and the following 14 | * disclaimer in the documentation and/or other materials provided 15 | * with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include "crypto.h" 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | void dumpCert(const u2fs_X509_t * certificate) 42 | { 43 | X509 *cert = (X509 *) certificate; 44 | BIO *out = BIO_new_fp(stderr, BIO_NOCLOSE); 45 | 46 | (void)X509_print_ex(out, cert, 0, 0); 47 | (void)PEM_write_bio_X509(out, cert); 48 | 49 | BIO_free(out); 50 | } 51 | 52 | void crypto_init(void) 53 | { 54 | /* Crypto init functions are deprecated in openssl-1.1.0 */ 55 | #if OPENSSL_VERSION_NUMBER < 0x10100000L 56 | SSL_load_error_strings(); 57 | #endif 58 | } 59 | 60 | void crypto_release(void) 61 | { 62 | /* Crypto deinit functions are deprecated in openssl-1.1.0. */ 63 | #if OPENSSL_VERSION_NUMBER < 0x10100000L 64 | RAND_cleanup(); 65 | ERR_free_strings(); 66 | #endif 67 | } 68 | 69 | u2fs_rc set_random_bytes(char *data, size_t len) 70 | { 71 | 72 | if (data == NULL) 73 | return U2FS_MEMORY_ERROR; 74 | 75 | if (RAND_status() != 1 || RAND_bytes((unsigned char *) data, len) != 1) 76 | return U2FS_CRYPTO_ERROR; 77 | 78 | return U2FS_OK; 79 | 80 | } 81 | 82 | u2fs_rc decode_X509(const unsigned char *data, size_t len, 83 | u2fs_X509_t ** cert) 84 | { 85 | 86 | const unsigned char *p; 87 | 88 | if (data == NULL || len == 0 || cert == NULL) 89 | return U2FS_MEMORY_ERROR; 90 | 91 | p = data; 92 | 93 | //Always set 1st param to NULL as per http://www.tedunangst.com/flak/post/analysis-of-d2i-X509-reuse 94 | *cert = (u2fs_X509_t *) d2i_X509(NULL, &p, len); 95 | if (*cert == NULL) { 96 | if (debug) { 97 | unsigned long err = 0; 98 | err = ERR_get_error(); 99 | fprintf(stderr, "Error: %s, %s, %s\n", 100 | ERR_lib_error_string(err), 101 | ERR_func_error_string(err), ERR_reason_error_string(err)); 102 | } 103 | return U2FS_CRYPTO_ERROR; 104 | } 105 | 106 | return U2FS_OK; 107 | } 108 | 109 | u2fs_rc decode_ECDSA(const unsigned char *data, size_t len, 110 | u2fs_ECDSA_t ** sig) 111 | { 112 | 113 | const unsigned char *p; 114 | 115 | if (data == NULL || len == 0 || sig == NULL) 116 | return U2FS_MEMORY_ERROR; 117 | 118 | p = data; 119 | 120 | *sig = (u2fs_ECDSA_t *) d2i_ECDSA_SIG(NULL, &p, len); 121 | 122 | if (*sig == NULL) { 123 | if (debug) { 124 | unsigned long err = 0; 125 | err = ERR_get_error(); 126 | fprintf(stderr, "Error: %s, %s, %s\n", 127 | ERR_lib_error_string(err), 128 | ERR_func_error_string(err), ERR_reason_error_string(err)); 129 | } 130 | return U2FS_CRYPTO_ERROR; 131 | } 132 | 133 | return U2FS_OK; 134 | } 135 | 136 | u2fs_rc decode_user_key(const unsigned char *data, u2fs_EC_KEY_t ** key) 137 | { 138 | 139 | if (key == NULL) 140 | return U2FS_MEMORY_ERROR; 141 | 142 | EC_GROUP *ecg = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); 143 | *key = (u2fs_EC_KEY_t *) EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); 144 | 145 | EC_POINT *point = EC_POINT_new(ecg); 146 | point_conversion_form_t pcf = POINT_CONVERSION_UNCOMPRESSED; 147 | EC_GROUP_set_point_conversion_form(ecg, pcf); 148 | 149 | if (EC_POINT_oct2point(ecg, point, data, U2FS_PUBLIC_KEY_LEN, NULL) == 0) { 150 | if (debug) { 151 | unsigned long err = 0; 152 | err = ERR_get_error(); 153 | fprintf(stderr, "Error: %s, %s, %s\n", 154 | ERR_lib_error_string(err), 155 | ERR_func_error_string(err), ERR_reason_error_string(err)); 156 | } 157 | *key = NULL; 158 | EC_GROUP_free(ecg); 159 | ecg = NULL; 160 | EC_POINT_free(point); 161 | point = NULL; 162 | return U2FS_CRYPTO_ERROR; 163 | } 164 | 165 | EC_GROUP_free(ecg); 166 | ecg = NULL; 167 | 168 | if (EC_KEY_set_public_key((EC_KEY *) * key, point) == 0) { 169 | if (debug) { 170 | unsigned long err = 0; 171 | err = ERR_get_error(); 172 | fprintf(stderr, "Error: %s, %s, %s\n", 173 | ERR_lib_error_string(err), 174 | ERR_func_error_string(err), ERR_reason_error_string(err)); 175 | } 176 | *key = NULL; 177 | EC_POINT_free(point); 178 | point = NULL; 179 | return U2FS_CRYPTO_ERROR; 180 | } 181 | 182 | EC_POINT_free(point); 183 | point = NULL; 184 | 185 | return U2FS_OK; 186 | 187 | } 188 | 189 | u2fs_rc verify_ECDSA(const unsigned char *dgst, int dgst_len, 190 | const u2fs_ECDSA_t * sig, u2fs_EC_KEY_t * eckey) 191 | { 192 | if (dgst == NULL || dgst_len == 0 || sig == NULL || eckey == NULL) 193 | return U2FS_MEMORY_ERROR; 194 | 195 | int rc = 196 | ECDSA_do_verify(dgst, dgst_len, (ECDSA_SIG *) sig, (EC_KEY *) eckey); 197 | 198 | if (rc != 1) { 199 | if (rc == -1) { 200 | if (debug) { 201 | unsigned long err = 0; 202 | err = ERR_get_error(); 203 | fprintf(stderr, "Error: %s, %s, %s\n", 204 | ERR_lib_error_string(err), 205 | ERR_func_error_string(err), ERR_reason_error_string(err)); 206 | } 207 | return U2FS_CRYPTO_ERROR; 208 | } else { 209 | return U2FS_SIGNATURE_ERROR; 210 | } 211 | } 212 | 213 | return U2FS_OK; 214 | } 215 | 216 | u2fs_rc extract_EC_KEY_from_X509(const u2fs_X509_t * cert, 217 | u2fs_EC_KEY_t ** key) 218 | { 219 | if (cert == NULL || key == NULL) 220 | return U2FS_MEMORY_ERROR; 221 | 222 | EVP_PKEY *pkey = X509_get_pubkey((X509 *) cert); 223 | 224 | if (pkey == NULL) { 225 | if (debug) { 226 | unsigned long err = 0; 227 | err = ERR_get_error(); 228 | fprintf(stderr, "Error: %s, %s, %s\n", 229 | ERR_lib_error_string(err), 230 | ERR_func_error_string(err), ERR_reason_error_string(err)); 231 | } 232 | return U2FS_CRYPTO_ERROR; 233 | } 234 | 235 | *key = (u2fs_EC_KEY_t *) EVP_PKEY_get1_EC_KEY(pkey); 236 | 237 | EVP_PKEY_free(pkey); 238 | pkey = NULL; 239 | 240 | if (*key == NULL) { 241 | if (debug) { 242 | unsigned long err = 0; 243 | err = ERR_get_error(); 244 | fprintf(stderr, "Error: %s, %s, %s\n", 245 | ERR_lib_error_string(err), 246 | ERR_func_error_string(err), ERR_reason_error_string(err)); 247 | } 248 | return U2FS_CRYPTO_ERROR; 249 | } 250 | 251 | EC_GROUP *ecg = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); 252 | 253 | EC_KEY_set_asn1_flag((EC_KEY *) * key, OPENSSL_EC_NAMED_CURVE); 254 | EC_KEY_set_group((EC_KEY *) * key, ecg); 255 | 256 | EC_GROUP_free(ecg); 257 | ecg = NULL; 258 | 259 | return U2FS_OK; 260 | } 261 | 262 | u2fs_EC_KEY_t *dup_key(const u2fs_EC_KEY_t * key) 263 | { 264 | return (u2fs_EC_KEY_t *) EC_KEY_dup((EC_KEY *) key); 265 | } 266 | 267 | void free_key(u2fs_EC_KEY_t * key) 268 | { 269 | EC_KEY_free((EC_KEY *) key); 270 | } 271 | 272 | u2fs_X509_t *dup_cert(const u2fs_X509_t * cert) 273 | { 274 | return (u2fs_X509_t *) X509_dup((X509 *) cert); 275 | } 276 | 277 | void free_cert(u2fs_X509_t * cert) 278 | { 279 | X509_free((X509 *) cert); 280 | } 281 | 282 | void free_sig(u2fs_ECDSA_t * sig) 283 | { 284 | ECDSA_SIG_free((ECDSA_SIG *) sig); 285 | } 286 | 287 | u2fs_rc dump_user_key(const u2fs_EC_KEY_t * key, char **output) 288 | { 289 | //TODO add PEM - current output is openssl octet string 290 | 291 | if (key == NULL || output == NULL) 292 | return U2FS_MEMORY_ERROR; 293 | 294 | EC_GROUP *ecg = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); 295 | point_conversion_form_t pcf = POINT_CONVERSION_UNCOMPRESSED; 296 | 297 | if (ecg == NULL) 298 | return U2FS_MEMORY_ERROR; 299 | 300 | const EC_POINT *point = EC_KEY_get0_public_key((EC_KEY *) key); 301 | 302 | *output = malloc(U2FS_PUBLIC_KEY_LEN); 303 | 304 | if (*output == NULL) { 305 | EC_GROUP_free(ecg); 306 | ecg = NULL; 307 | return U2FS_MEMORY_ERROR; 308 | } 309 | 310 | if (EC_POINT_point2oct 311 | (ecg, point, pcf, (unsigned char *) *output, U2FS_PUBLIC_KEY_LEN, 312 | NULL) != U2FS_PUBLIC_KEY_LEN) { 313 | free(ecg); 314 | ecg = NULL; 315 | free(*output); 316 | *output = NULL; 317 | return U2FS_CRYPTO_ERROR; 318 | } 319 | 320 | EC_GROUP_free(ecg); 321 | ecg = NULL; 322 | 323 | return U2FS_OK; 324 | 325 | } 326 | 327 | u2fs_rc dump_X509_cert(const u2fs_X509_t * cert, char **output) 328 | { 329 | //input: openssl X509 certificate 330 | //output: PEM-formatted char buffer 331 | 332 | if (cert == NULL || output == NULL) 333 | return U2FS_MEMORY_ERROR; 334 | 335 | *output = NULL; 336 | 337 | BIO *bio = BIO_new(BIO_s_mem()); 338 | if (bio == NULL) 339 | return U2FS_MEMORY_ERROR; 340 | 341 | if(!PEM_write_bio_X509(bio, (X509 *)cert)) { 342 | BIO_free(bio); 343 | return U2FS_CRYPTO_ERROR; 344 | } 345 | 346 | char *PEM_data; 347 | int length = BIO_get_mem_data(bio, &PEM_data); 348 | *output = malloc(length); 349 | if (*output == NULL) { 350 | BIO_free(bio); 351 | return U2FS_MEMORY_ERROR; 352 | } 353 | 354 | memcpy(*output, PEM_data, length); 355 | BIO_free(bio); 356 | 357 | return U2FS_OK; 358 | } 359 | 360 | #ifdef MAKE_CHECK 361 | #include 362 | 363 | START_TEST(test_errors) 364 | { 365 | 366 | u2fs_X509_t *cert = NULL; 367 | u2fs_ECDSA_t *sig = NULL; 368 | u2fs_EC_KEY_t *key = NULL; 369 | 370 | char *output; 371 | 372 | unsigned char some_data[] = { 373 | 0x0A, 0x0B, 0x0C, 0x0D, 0x0E 374 | }; 375 | 376 | unsigned char wrong_key[] = { 377 | 0x04, 0x5c, 0x6d, 0xd1, 0x38, 0x3c, 0x71, 0x91, 0x68, 0x95, 0x13, 0x2b, 378 | 0xd8, 0x58, 0xe0, 0x6a, 0xd7, 0xfe, 0x36, 0x5a, 0xe5, 0xe5, 0xa0, 379 | 0x8c, 0x92, 0xba, 0x21, 0xfc, 0x1e, 0xce, 0xb9, 0xdd, 0x1e, 0xf4, 380 | 0x22, 0xed, 0x04, 0x2d, 0x60, 0x0d, 0xaa, 0x02, 0x0e, 0x0d, 0xad, 381 | 0xe6, 0xcd, 0x91, 0x20, 0xa8, 0x3b, 0x02, 0x74, 0x57, 0x53, 0xf3, 382 | 0x2e, 0x53, 0xf5, 0x5a, 0xbf, 0xce, 0x92, 0xaa, 0xaa 383 | }; 384 | 385 | unsigned char userkey_dat[] = { 386 | 0x04, 0x5c, 0x6d, 0xd1, 0x38, 0x3c, 0x71, 0x91, 0x68, 0x95, 0x13, 0x2b, 387 | 0xd8, 0x58, 0xe0, 0x6a, 0xd7, 0xfe, 0x36, 0x5a, 0xe5, 0xe5, 0xa0, 388 | 0x8c, 0x92, 0xba, 0x21, 0xfc, 0x1e, 0xce, 0xb9, 0xdd, 0x1e, 0xf4, 389 | 0x22, 0xed, 0x04, 0x2d, 0x60, 0x0d, 0xaa, 0x02, 0x0e, 0x0d, 0xad, 390 | 0xe6, 0xcd, 0x91, 0x20, 0xa8, 0x3b, 0x02, 0x74, 0x57, 0x53, 0xf3, 391 | 0x2e, 0x53, 0xf5, 0x5a, 0xbf, 0xce, 0x92, 0xef, 0xf4 392 | }; 393 | 394 | cert = (u2fs_X509_t *) & output; 395 | key = (u2fs_EC_KEY_t *) & output; 396 | sig = (u2fs_ECDSA_t *) & output; 397 | 398 | ck_assert_int_eq(decode_X509(some_data, 5, &cert), U2FS_CRYPTO_ERROR); 399 | ck_assert_int_eq(decode_ECDSA(some_data, 5, &sig), U2FS_CRYPTO_ERROR); 400 | ck_assert_int_eq(decode_user_key(wrong_key, &key), U2FS_CRYPTO_ERROR); 401 | 402 | //ck_assert_int_eq(dump_user_key(key, &output), U2FS_CRYPTO_ERROR); 403 | ck_assert_int_eq(decode_user_key(userkey_dat, &key), U2FS_OK); 404 | //ck_assert_int_eq(extract_EC_KEY_from_X509(cert, &key), U2FS_CRYPTO_ERROR); 405 | 406 | } 407 | 408 | END_TEST START_TEST(test_dup_key) 409 | { 410 | 411 | u2fs_EC_KEY_t *key = NULL; 412 | u2fs_EC_KEY_t *key2 = NULL; 413 | 414 | unsigned char userkey_dat[] = { 415 | 0x04, 0x5c, 0x6d, 0xd1, 0x38, 0x3c, 0x71, 0x91, 0x68, 0x95, 0x13, 0x2b, 416 | 0xd8, 0x58, 0xe0, 0x6a, 0xd7, 0xfe, 0x36, 0x5a, 0xe5, 0xe5, 0xa0, 417 | 0x8c, 0x92, 0xba, 0x21, 0xfc, 0x1e, 0xce, 0xb9, 0xdd, 0x1e, 0xf4, 418 | 0x22, 0xed, 0x04, 0x2d, 0x60, 0x0d, 0xaa, 0x02, 0x0e, 0x0d, 0xad, 419 | 0xe6, 0xcd, 0x91, 0x20, 0xa8, 0x3b, 0x02, 0x74, 0x57, 0x53, 0xf3, 420 | 0x2e, 0x53, 0xf5, 0x5a, 0xbf, 0xce, 0x92, 0xef, 0xf4 421 | }; 422 | 423 | ck_assert_int_eq(decode_user_key(userkey_dat, &key), U2FS_OK); 424 | key2 = dup_key(key); 425 | ck_assert(key2 != NULL); 426 | //ck_assert(memcmp(key, key2, sizeof(key))); 427 | 428 | } 429 | 430 | END_TEST Suite *u2fs_crypto_suite(void) 431 | { 432 | Suite *s; 433 | TCase *tc_crypto; 434 | 435 | s = suite_create("u2fs_crypto"); 436 | 437 | /* Crypto test case */ 438 | tc_crypto = tcase_create("Crypto"); 439 | 440 | tcase_add_test(tc_crypto, test_errors); 441 | tcase_add_test(tc_crypto, test_dup_key); 442 | suite_add_tcase(s, tc_crypto); 443 | 444 | return s; 445 | } 446 | 447 | int main(void) 448 | { 449 | 450 | int number_failed; 451 | Suite *s; 452 | SRunner *sr; 453 | 454 | s = u2fs_crypto_suite(); 455 | sr = srunner_create(s); 456 | 457 | srunner_run_all(sr, CK_NORMAL); 458 | number_failed = srunner_ntests_failed(sr); 459 | srunner_free(sr); 460 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 461 | 462 | return 0; 463 | 464 | } 465 | #endif 466 | -------------------------------------------------------------------------------- /u2f-server/sha256.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Yubico AB 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above 13 | * copyright notice, this list of conditions and the following 14 | * disclaimer in the documentation and/or other materials provided 15 | * with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | /* The following file is adapted from the public domain SHA256 31 | implementation in LibTomCrypt see http://libtom.org/ 32 | and in particular the following files: 33 | https://raw.github.com/libtom/libtomcrypt/master/src/hashes/sha2/sha256.c 34 | https://raw.github.com/libtom/libtomcrypt/master/src/headers/tomcrypt_hash.h 35 | */ 36 | 37 | #include "sha256.h" 38 | 39 | #include 40 | 41 | /* the K array */ 42 | static const uint32_t K[64] = { 43 | 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL, 44 | 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL, 45 | 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 46 | 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, 47 | 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL, 48 | 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL, 49 | 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 50 | 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, 51 | 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL, 52 | 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL, 53 | 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 54 | 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, 55 | 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL 56 | }; 57 | 58 | /* Various logical functions */ 59 | #define RORc(x, y) ( ((((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)((y)&31)) | ((unsigned long)(x)<<(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL) 60 | #define MIN(x, y) ( ((x)<(y))?(x):(y) ) 61 | #define STORE32H(x, y) { (y)[0] = (unsigned char)(((x)>>24)&255); (y)[1] = (unsigned char)(((x)>>16)&255); \ 62 | (y)[2] = (unsigned char)(((x)>>8)&255); (y)[3] = (unsigned char)((x)&255); } 63 | 64 | #define LOAD32H(x, y) { x = ((unsigned long)((y)[0] & 255)<<24) | \ 65 | ((unsigned long)((y)[1] & 255)<<16) | \ 66 | ((unsigned long)((y)[2] & 255)<<8) | \ 67 | ((unsigned long)((y)[3] & 255)); } 68 | #define STORE64H(x, y) { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \ 69 | (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \ 70 | (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \ 71 | (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); } 72 | #define Ch(x,y,z) (z ^ (x & (y ^ z))) 73 | #define Maj(x,y,z) (((x | y) & z) | (x & y)) 74 | #define S(x, n) RORc((x),(n)) 75 | #define R(x, n) (((x)&0xFFFFFFFFUL)>>(n)) 76 | #define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22)) 77 | #define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25)) 78 | #define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3)) 79 | #define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10)) 80 | 81 | /* compress 512-bits */ 82 | static void sha256_compress(struct sha256_state *md, unsigned char *buf) 83 | { 84 | uint32_t S[8], W[64], t0, t1; 85 | uint32_t t; 86 | int i; 87 | 88 | /* copy state into S */ 89 | for (i = 0; i < 8; i++) { 90 | S[i] = md->state[i]; 91 | } 92 | 93 | /* copy the state into 512-bits into W[0..15] */ 94 | for (i = 0; i < 16; i++) { 95 | LOAD32H(W[i], buf + (4 * i)); 96 | } 97 | 98 | /* fill W[16..63] */ 99 | for (i = 16; i < 64; i++) { 100 | W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; 101 | } 102 | 103 | /* Compress */ 104 | #define RND(a,b,c,d,e,f,g,h,i) \ 105 | t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ 106 | t1 = Sigma0(a) + Maj(a, b, c); \ 107 | d += t0; \ 108 | h = t0 + t1; 109 | 110 | for (i = 0; i < 64; ++i) { 111 | RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i); 112 | t = S[7]; 113 | S[7] = S[6]; 114 | S[6] = S[5]; 115 | S[5] = S[4]; 116 | S[4] = S[3]; 117 | S[3] = S[2]; 118 | S[2] = S[1]; 119 | S[1] = S[0]; 120 | S[0] = t; 121 | } 122 | 123 | /* feedback */ 124 | for (i = 0; i < 8; i++) { 125 | md->state[i] = md->state[i] + S[i]; 126 | } 127 | } 128 | 129 | /** 130 | Initialize the hash state 131 | @param md The hash state you wish to initialize 132 | @return CRYPT_OK if successful 133 | */ 134 | void sha256_init(struct sha256_state *md) 135 | { 136 | md->curlen = 0; 137 | md->length = 0; 138 | md->state[0] = 0x6A09E667UL; 139 | md->state[1] = 0xBB67AE85UL; 140 | md->state[2] = 0x3C6EF372UL; 141 | md->state[3] = 0xA54FF53AUL; 142 | md->state[4] = 0x510E527FUL; 143 | md->state[5] = 0x9B05688CUL; 144 | md->state[6] = 0x1F83D9ABUL; 145 | md->state[7] = 0x5BE0CD19UL; 146 | } 147 | 148 | #define block_size 64 149 | void sha256_process(struct sha256_state *md, const unsigned char *in, 150 | unsigned long inlen) 151 | { 152 | unsigned long n; 153 | int err; 154 | 155 | while (inlen > 0) { 156 | if (md->curlen == 0 && inlen >= block_size) { 157 | sha256_compress(md, (unsigned char *) in); 158 | md->length += block_size * 8; 159 | in += block_size; 160 | inlen -= block_size; 161 | } else { 162 | n = MIN(inlen, (block_size - md->curlen)); 163 | memcpy(md->buf + md->curlen, in, (size_t) n); 164 | md->curlen += n; 165 | in += n; 166 | inlen -= n; 167 | if (md->curlen == block_size) { 168 | sha256_compress(md, md->buf); 169 | md->length += 8 * block_size; 170 | md->curlen = 0; 171 | } 172 | } 173 | } 174 | } 175 | 176 | /** 177 | Terminate the hash to get the digest 178 | @param md The hash state 179 | @param out [out] The destination of the hash (32 bytes) 180 | @return CRYPT_OK if successful 181 | */ 182 | void sha256_done(struct sha256_state *md, unsigned char *out) 183 | { 184 | int i; 185 | 186 | /* increase the length of the message */ 187 | md->length += md->curlen * 8; 188 | 189 | /* append the '1' bit */ 190 | md->buf[md->curlen++] = (unsigned char) 0x80; 191 | 192 | /* if the length is currently above 56 bytes we append zeros 193 | * then compress. Then we can fall back to padding zeros and length 194 | * encoding like normal. 195 | */ 196 | if (md->curlen > 56) { 197 | while (md->curlen < 64) { 198 | md->buf[md->curlen++] = (unsigned char) 0; 199 | } 200 | sha256_compress(md, md->buf); 201 | md->curlen = 0; 202 | } 203 | 204 | /* pad upto 56 bytes of zeroes */ 205 | while (md->curlen < 56) { 206 | md->buf[md->curlen++] = (unsigned char) 0; 207 | } 208 | 209 | /* store length */ 210 | STORE64H(md->length, md->buf + 56); 211 | sha256_compress(md, md->buf); 212 | 213 | /* copy output */ 214 | for (i = 0; i < 8; i++) { 215 | STORE32H(md->state[i], out + (4 * i)); 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /u2f-server/sha256.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Yubico AB 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above 13 | * copyright notice, this list of conditions and the following 14 | * disclaimer in the documentation and/or other materials provided 15 | * with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef SHA256_H 31 | #define SHA256_H 32 | 33 | #include 34 | 35 | struct sha256_state { 36 | uint64_t length; 37 | uint32_t state[8], curlen; 38 | unsigned char buf[64]; 39 | }; 40 | 41 | void sha256_init(struct sha256_state *md); 42 | void sha256_process(struct sha256_state *md, const unsigned char *in, 43 | unsigned long inlen); 44 | void sha256_done(struct sha256_state *md, unsigned char *out); 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /u2f-server/u2f-server-version.h.in: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Yubico AB 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above 13 | * copyright notice, this list of conditions and the following 14 | * disclaimer in the documentation and/or other materials provided 15 | * with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef U2F_SERVER_VERSION_H 31 | #define U2F_SERVER_VERSION_H 32 | 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | 37 | /** 38 | * U2FS_VERSION_STRING 39 | * 40 | * Pre-processor symbol with a string that describe the header file 41 | * version number. Used together with u2fs_check_version() to verify 42 | * header file and run-time library consistency. 43 | */ 44 | #define U2FS_VERSION_STRING "@VERSION@" 45 | 46 | /** 47 | * U2FS_VERSION_NUMBER 48 | * 49 | * Pre-processor symbol with a hexadecimal value describing the header 50 | * file version number. For example, when the header version is 1.2.3 51 | * this symbol will have the value 0x01020300. The last two digits 52 | * are only used between public releases, and will otherwise be 00. 53 | */ 54 | #define U2FS_VERSION_NUMBER @U2FS_VERSION_NUMBER@ 55 | 56 | /** 57 | * U2FS_VERSION_MAJOR 58 | * 59 | * Pre-processor symbol with a decimal value that describe the major 60 | * level of the header file version number. For example, when the 61 | * header version is 1.2.3 this symbol will be 1. 62 | */ 63 | #define U2FS_VERSION_MAJOR @U2FS_VERSION_MAJOR@ 64 | 65 | /** 66 | * U2FS_VERSION_MINOR 67 | * 68 | * Pre-processor symbol with a decimal value that describe the minor 69 | * level of the header file version number. For example, when the 70 | * header version is 1.2.3 this symbol will be 2. 71 | */ 72 | #define U2FS_VERSION_MINOR @U2FS_VERSION_MINOR@ 73 | 74 | /** 75 | * U2FS_VERSION_PATCH 76 | * 77 | * Pre-processor symbol with a decimal value that describe the patch 78 | * level of the header file version number. For example, when the 79 | * header version is 1.2.3 this symbol will be 3. 80 | */ 81 | #define U2FS_VERSION_PATCH @U2FS_VERSION_PATCH@ 82 | 83 | const char *u2fs_check_version(const char *req_version); 84 | 85 | #ifdef __cplusplus 86 | } 87 | #endif 88 | #endif 89 | -------------------------------------------------------------------------------- /u2f-server/u2f-server.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Yubico AB 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above 13 | * copyright notice, this list of conditions and the following 14 | * disclaimer in the documentation and/or other materials provided 15 | * with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef U2F_SERVER_H 31 | #define U2F_SERVER_H 32 | 33 | #include 34 | #include 35 | 36 | #include 37 | 38 | #ifdef __cplusplus 39 | extern "C" { 40 | #endif 41 | 42 | #define U2FS_CHALLENGE_RAW_LEN 32 43 | #define U2FS_CHALLENGE_B64U_LEN 43 44 | #define U2FS_PUBLIC_KEY_LEN 65 45 | #define U2FS_COUNTER_LEN 4 46 | 47 | /** 48 | * u2fs_rc: 49 | * @U2FS_OK: Success. 50 | * @U2FS_MEMORY_ERROR: Memory error. 51 | * @U2FS_JSON_ERROR: Json error. 52 | * @U2FS_BASE64_ERROR: Base64 error. 53 | * @U2FS_CRYPTO_ERROR: Cryptographic error. 54 | * @U2FS_ORIGIN_ERROR: Origin mismatch. 55 | * @U2FS_CHALLENGE_ERROR: Challenge error. 56 | * @U2FS_SIGNATURE_ERROR: Signature mismatch. 57 | * @U2FS_FORMAT_ERROR: Message format error. 58 | * 59 | * Error codes. 60 | */ 61 | typedef enum { 62 | U2FS_OK = 0, 63 | U2FS_MEMORY_ERROR = -1, 64 | U2FS_JSON_ERROR = -2, 65 | U2FS_BASE64_ERROR = -3, 66 | U2FS_CRYPTO_ERROR = -4, 67 | U2FS_ORIGIN_ERROR = -5, 68 | U2FS_CHALLENGE_ERROR = -6, 69 | U2FS_SIGNATURE_ERROR = -7, 70 | U2FS_FORMAT_ERROR = -8 71 | } u2fs_rc; 72 | 73 | /** 74 | * u2fs_initflags: 75 | * @U2FS_DEBUG: Print debug messages. 76 | * 77 | * Flags passed to u2fs_global_init(). 78 | */ 79 | typedef enum { 80 | U2FS_DEBUG = 1 81 | } u2fs_initflags; 82 | 83 | typedef struct u2fs_ctx u2fs_ctx_t; 84 | typedef struct u2fs_reg_res u2fs_reg_res_t; 85 | typedef struct u2fs_auth_res u2fs_auth_res_t; 86 | 87 | /* Must be called successfully before using any other functions. */ 88 | u2fs_rc u2fs_global_init(u2fs_initflags flags); 89 | void u2fs_global_done(void); 90 | 91 | /* Error handling */ 92 | const char *u2fs_strerror(int err); 93 | const char *u2fs_strerror_name(int err); 94 | 95 | /* Create context before registration/authentication calls. */ 96 | 97 | u2fs_rc u2fs_init(u2fs_ctx_t ** ctx); 98 | void u2fs_done(u2fs_ctx_t * ctx); 99 | u2fs_rc u2fs_set_origin(u2fs_ctx_t * ctx, const char *origin); 100 | u2fs_rc u2fs_set_appid(u2fs_ctx_t * ctx, const char *appid); 101 | u2fs_rc u2fs_set_challenge(u2fs_ctx_t * ctx, const char *challenge); 102 | u2fs_rc u2fs_set_keyHandle(u2fs_ctx_t * ctx, const char *keyHandle); 103 | u2fs_rc u2fs_set_publicKey(u2fs_ctx_t * ctx, 104 | const unsigned char *publicKey); 105 | 106 | /* U2F Registration functions */ 107 | 108 | u2fs_rc u2fs_registration_challenge(u2fs_ctx_t * ctx, char **output); 109 | u2fs_rc u2fs_registration_verify(u2fs_ctx_t * ctx, const char *response, 110 | u2fs_reg_res_t ** output); 111 | 112 | const char *u2fs_get_registration_keyHandle(u2fs_reg_res_t * result); 113 | const char *u2fs_get_registration_publicKey(u2fs_reg_res_t * result); 114 | const char *u2fs_get_registration_attestation(u2fs_reg_res_t * result); 115 | 116 | void u2fs_free_reg_res(u2fs_reg_res_t * result); 117 | 118 | /* U2F Authentication functions */ 119 | 120 | u2fs_rc u2fs_authentication_challenge(u2fs_ctx_t * ctx, char **output); 121 | u2fs_rc u2fs_authentication_verify(u2fs_ctx_t * ctx, 122 | const char *response, 123 | u2fs_auth_res_t ** output); 124 | 125 | u2fs_rc u2fs_get_authentication_result(u2fs_auth_res_t * result, 126 | u2fs_rc * verified, 127 | uint32_t * counter, 128 | uint8_t * user_presence); 129 | 130 | void u2fs_free_auth_res(u2fs_auth_res_t * result); 131 | 132 | #ifdef __cplusplus 133 | } 134 | #endif 135 | #endif 136 | -------------------------------------------------------------------------------- /u2f-server/u2f-server.map: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Yubico AB 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above 13 | * copyright notice, this list of conditions and the following 14 | * disclaimer in the documentation and/or other materials provided 15 | * with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | U2F_SERVER_0.0.0 31 | { 32 | global: 33 | u2fs_authentication_challenge; 34 | u2fs_authentication_verify; 35 | u2fs_check_version; 36 | u2fs_done; 37 | u2fs_free_auth_res; 38 | u2fs_free_reg_res; 39 | u2fs_get_authentication_result; 40 | u2fs_get_registration_result; 41 | u2fs_get_registration_keyHandle; 42 | u2fs_get_registration_publicKey; 43 | u2fs_get_registration_attestation; 44 | u2fs_global_done; 45 | u2fs_global_init; 46 | u2fs_init; 47 | u2fs_registration_challenge; 48 | u2fs_registration_verify; 49 | u2fs_set_appid; 50 | u2fs_set_challenge; 51 | u2fs_set_keyHandle; 52 | u2fs_set_publicKey; 53 | u2fs_set_origin; 54 | u2fs_set_user_key; 55 | u2fs_strerror; 56 | u2fs_strerror_name; 57 | 58 | local: 59 | *; 60 | }; 61 | -------------------------------------------------------------------------------- /u2f-server/u2f-server.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@ 5 | 6 | Name: Libu2f-server 7 | Description: Yubico Universal 2nd Factor (U2F) Server C Library 8 | URL: https://www.yubico.com/ 9 | Version: @VERSION@ 10 | Libs: -L${libdir} -lu2f-server 11 | Cflags: -I${includedir}/u2f-server 12 | -------------------------------------------------------------------------------- /u2f-server/version.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Yubico AB 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above 13 | * copyright notice, this list of conditions and the following 14 | * disclaimer in the documentation and/or other materials provided 15 | * with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include 31 | 32 | #include 33 | 34 | #include 35 | 36 | /* From http://article.gmane.org/gmane.os.freebsd.devel.hackers/23606 */ 37 | static int my_strverscmp(const char *s1, const char *s2) 38 | { 39 | static const char *digits = "0123456789"; 40 | int ret, lz1, lz2; 41 | size_t p1, p2; 42 | 43 | p1 = strcspn(s1, digits); 44 | p2 = strcspn(s2, digits); 45 | while (p1 == p2 && s1[p1] != '\0' && s2[p2] != '\0') { 46 | /* Different prefix */ 47 | if ((ret = strncmp(s1, s2, p1)) != 0) 48 | return ret; 49 | 50 | s1 += p1; 51 | s2 += p2; 52 | 53 | lz1 = lz2 = 0; 54 | if (*s1 == '0') 55 | lz1 = 1; 56 | if (*s2 == '0') 57 | lz2 = 1; 58 | 59 | if (lz1 > lz2) 60 | return -1; 61 | else if (lz1 < lz2) 62 | return 1; 63 | else if (lz1 == 1) { 64 | /* 65 | * If the common prefix for s1 and s2 consists only of zeros, then the 66 | * "longer" number has to compare less. Otherwise the comparison needs 67 | * to be numerical (just fallthrough). See 68 | * http://refspecs.freestandards.org/LSB_2.0.1/LSB-generic/ 69 | * LSB-generic/baselib-strverscmp.html 70 | */ 71 | while (*s1 == '0' && *s2 == '0') { 72 | ++s1; 73 | ++s2; 74 | } 75 | 76 | p1 = strspn(s1, digits); 77 | p2 = strspn(s2, digits); 78 | 79 | /* Catch empty strings */ 80 | if (p1 == 0 && p2 > 0) 81 | return 1; 82 | else if (p2 == 0 && p1 > 0) 83 | return -1; 84 | 85 | /* Prefixes are not same */ 86 | if (*s1 != *s2 && *s1 != '0' && *s2 != '0') { 87 | if (p1 < p2) 88 | return 1; 89 | else if (p1 > p2) 90 | return -1; 91 | } else { 92 | if (p1 < p2) 93 | ret = strncmp(s1, s2, p1); 94 | else if (p1 > p2) 95 | ret = strncmp(s1, s2, p2); 96 | if (ret != 0) 97 | return ret; 98 | } 99 | } 100 | 101 | p1 = strspn(s1, digits); 102 | p2 = strspn(s2, digits); 103 | 104 | if (p1 < p2) 105 | return -1; 106 | else if (p1 > p2) 107 | return 1; 108 | else if ((ret = strncmp(s1, s2, p1)) != 0) 109 | return ret; 110 | 111 | /* Numbers are equal or not present, try with next ones. */ 112 | s1 += p1; 113 | s2 += p2; 114 | p1 = strcspn(s1, digits); 115 | p2 = strcspn(s2, digits); 116 | } 117 | 118 | return strcmp(s1, s2); 119 | } 120 | 121 | /** 122 | * u2fs_check_version: 123 | * @req_version: Required version number, or NULL. 124 | * 125 | * Check that the version of the library is at minimum the requested 126 | * one and return the version string; return NULL if the condition is 127 | * not satisfied. If a NULL is passed to this function, no check is 128 | * done, but the version string is simply returned. 129 | * 130 | * See %U2FS_VERSION_STRING for a suitable @req_version string. 131 | * 132 | * Return value: Version string of run-time library, or NULL if the 133 | * run-time library does not meet the required version number. 134 | */ 135 | const char *u2fs_check_version(const char *req_version) 136 | { 137 | if (!req_version || my_strverscmp(req_version, U2FS_VERSION_STRING) <= 0) 138 | return U2FS_VERSION_STRING; 139 | 140 | return NULL; 141 | } 142 | --------------------------------------------------------------------------------