├── .github └── workflows │ ├── linux_build.yml │ └── scan.yml ├── .gitignore ├── .travis.yml ├── AUTHORS ├── BLURB ├── COPYING ├── Makefile.am ├── NEWS ├── README ├── README.adoc ├── build-and-test.sh ├── cdecode.c ├── cdecode.h ├── cencode.c ├── cencode.h ├── configure.ac ├── hmac.c ├── libykclient.map ├── m4 ├── ld-version-script.m4 ├── manywarnings.m4 ├── pkg.m4 ├── valgrind-tests.m4 └── warnings.m4 ├── sha-private.h ├── sha.h ├── sha1.c ├── sha224-256.c ├── sha384-512.c ├── tests ├── Makefile.am └── selftest.c ├── tool.c ├── usha.c ├── ykclient.c ├── ykclient.h ├── ykclient_errors.h ├── ykclient_server_response.c ├── ykclient_server_response.h ├── ykclient_version.c └── ykclient_version.h.in /.github/workflows/linux_build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | os: [ubuntu-20.04] 12 | cc: [clang-10] 13 | steps: 14 | - uses: actions/checkout@v1 15 | - name: Dependencies 16 | env: 17 | CC: ${{ matrix.cc }} 18 | run: | 19 | sudo apt -q update 20 | sudo apt install -q -y autoconf automake libtool pkg-config \ 21 | libcurl4-openssl-dev 22 | sudo apt install -q -y ${CC%-*}-tools-${CC#clang-} 23 | - name: Build & Run tests 24 | env: 25 | CC: ${{ matrix.cc }} 26 | run: | 27 | autoreconf --install 28 | ./configure CC=${CC} CFLAGS="-fsanitize=address,leak,undefined" \ 29 | --disable-documentation 30 | make 31 | cd tests && make selftest && ./selftest 32 | -------------------------------------------------------------------------------- /.github/workflows/scan.yml: -------------------------------------------------------------------------------- 1 | name: static code analysis 2 | 3 | on: 4 | push: 5 | schedule: 6 | - cron: '0 0 * * 1' 7 | 8 | env: 9 | SCAN_IMG: 10 | yubico-yes-docker-local.jfrog.io/static-code-analysis/c:v1 11 | COMPILE_DEPS: "libcurl4-openssl-dev" 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@master 19 | 20 | - name: Prep scan 21 | run: | 22 | docker login yubico-yes-docker-local.jfrog.io/ \ 23 | -u svc-static-code-analysis-reader \ 24 | -p ${{ secrets.ARTIFACTORY_READER_TOKEN }} 25 | docker pull ${SCAN_IMG} 26 | 27 | - name: Scan and fail if warnings 28 | run: | 29 | docker run -v${PWD}:/k -e COMPILE_DEPS="${COMPILE_DEPS}" \ 30 | -e PROJECT_NAME=${GITHUB_REPOSITORY#Yubico/} -t ${SCAN_IMG} 31 | 32 | - uses: actions/upload-artifact@master 33 | if: failure() 34 | with: 35 | name: suppression_files 36 | path: suppression_files 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.cache 2 | *.lo 3 | *.o 4 | *.swp 5 | *~ 6 | .*~ 7 | .deps/ 8 | .libs/ 9 | ChangeLog 10 | INSTALL 11 | Makefile 12 | Makefile.in 13 | \#* 14 | aclocal.m4 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 | libtool 28 | libykclient.la 29 | m4/libtool.m4 30 | m4/ltoptions.m4 31 | m4/ltsugar.m4 32 | m4/ltversion.m4 33 | m4/lt~obsolete.m4 34 | tests/.deps/ 35 | tests/Makefile 36 | tests/selftest 37 | tests/selftest.log 38 | tests/selftest.trs 39 | tests/test-suite.log 40 | ykclient 41 | ykclient-*.tar.gz 42 | ykclient-*.tar.gz.sig 43 | ykclient.1 44 | ykclient_version.h 45 | cdecode.gcno 46 | cencode.gcno 47 | coverage/ 48 | hmac.gcno 49 | sha1.gcno 50 | sha224-256.gcno 51 | sha384-512.gcno 52 | tool.gcda 53 | tool.gcno 54 | usha.gcno 55 | ykclient.gcno 56 | ykclient_server_response.gcno 57 | ykclient_version.gcno 58 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | compiler: 3 | - gcc 4 | - clang 5 | before_install: 6 | - sudo apt-get update -qq 7 | - sudo apt-get install -qq -y help2man $EXTRA 8 | script: ./build-and-test.sh 9 | matrix: 10 | include: 11 | - compiler: gcc 12 | env: COVERAGE="--enable-coverage" EXTRA="lcov" 13 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Author 2 | ------ 3 | Simon Josefsson 4 | 5 | For technical feedback, please consider using our forum: 6 | http://forum.yubico.com/ 7 | 8 | 9 | Patches 10 | ------- 11 | Remi Mollon 12 | Supply server urls and add nonce to requests. 13 | 14 | Sebastien Martini and qistoph@gmail.com 15 | Patches for Protocol 2 support. 16 | 17 | Arran Cudbard-Bell 18 | Added support for persistent curl handles and cleanup of 19 | error conditions in the library. 20 | -------------------------------------------------------------------------------- /BLURB: -------------------------------------------------------------------------------- 1 | Author: Yubico 2 | Basename: yubico-c-client 3 | Homepage: https://developers.yubico.com/yubico-c-client/ 4 | License: BSD-2-Clause 5 | Name: yubico-c-client 6 | Project: yubico-c-client 7 | Summary: Yubico C client library 8 | Travis: https://travis-ci.org/Yubico/yubico-c-client 9 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2006-2013 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 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | # Written by Simon Josefsson . 2 | # Copyright (c) 2008-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 | SUBDIRS = . tests 30 | 31 | AM_CPPFLAGS = $(LIBCURL_CFLAGS) 32 | ACLOCAL_AMFLAGS = -I m4 33 | AM_CFLAGS = $(WARN_CFLAGS) 34 | 35 | # The library. 36 | 37 | lib_LTLIBRARIES = libykclient.la 38 | include_HEADERS = ykclient_server_response.h ykclient.h \ 39 | ykclient_errors.h ykclient_version.h 40 | 41 | libykclient_la_SOURCES = ykclient_server_response.h \ 42 | ykclient_server_response.c 43 | libykclient_la_SOURCES += ykclient.h ykclient_errors.h \ 44 | ykclient_version.h 45 | libykclient_la_SOURCES += ykclient.c ykclient_version.c 46 | libykclient_la_SOURCES += sha.h sha-private.h hmac.c sha1.c usha.c \ 47 | sha224-256.c sha384-512.c 48 | libykclient_la_SOURCES += cencode.h cencode.c cdecode.h cdecode.c 49 | libykclient_la_SOURCES += libykclient.map 50 | libykclient_la_LIBADD = $(LIBCURL_LIBS) 51 | libykclient_la_LDFLAGS = -no-undefined \ 52 | -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) 53 | 54 | if HAVE_LD_VERSION_SCRIPT 55 | libykclient_la_LDFLAGS += -Wl,--version-script=$(srcdir)/libykclient.map 56 | else 57 | libykclient_la_LDFLAGS += -export-symbols-regex '^ykclient_.*' 58 | endif 59 | 60 | # The command line tools. 61 | 62 | bin_PROGRAMS = ykclient 63 | 64 | ykclient_SOURCES = tool.c 65 | ykclient_LDADD = ./libykclient.la 66 | 67 | # Man page 68 | if ENABLE_DOC 69 | dist_man_MANS = ykclient.1 70 | DISTCLEANFILES = $(dist_man_MANS) 71 | 72 | ykclient.1: $(ykclient_SOURCES) $(srcdir)/configure.ac | $(builddir)/ykclient$(EXEEXT) 73 | $(HELP2MAN) \ 74 | --output=$@ $(builddir)/ykclient$(EXEEXT) \ 75 | --name="YubiCloud One-Time-Password Validation Client" \ 76 | --no-info 77 | endif 78 | 79 | if ENABLE_COV 80 | AM_CFLAGS += --coverage 81 | AM_LDFLAGS = --coverage 82 | 83 | cov-reset: 84 | rm -fr coverage 85 | find . -name "*.gcda" -exec rm {} \; 86 | lcov --directory . --zerocounters 87 | 88 | cov-report: 89 | mkdir -p coverage 90 | lcov --compat-libtool --directory . --capture --output-file coverage/app.info 91 | lcov --extract coverage/app.info '*.c' --output-file coverage/app2.info 92 | genhtml -o coverage/ coverage/app2.info 93 | 94 | cov: 95 | make cov-report 96 | 97 | clean-local: 98 | make cov-reset 99 | 100 | check: 101 | make cov 102 | endif 103 | 104 | # Release 105 | 106 | indent: 107 | indent *.c *.h *.h.in 108 | indent *.c *.h *.h.in 109 | 110 | ChangeLog: 111 | cd $(srcdir) && git2cl > ChangeLog 112 | 113 | PROJECT = yubico-c-client 114 | 115 | release: 116 | @if test -z "$(KEYID)"; then \ 117 | echo "Try this instead:"; \ 118 | echo " make release KEYID=[PGPKEYID]"; \ 119 | echo "For example:"; \ 120 | echo " make release KEYID=2117364A"; \ 121 | exit 1; \ 122 | fi 123 | @if test ! -d "$(YUBICO_WWW_REPO)"; then \ 124 | echo "WWW repo not found!"; \ 125 | echo "Make sure that YUBICO_WWW_REPO is set"; \ 126 | exit 1; \ 127 | fi 128 | @head -3 $(srcdir)/NEWS | grep -q "Version $(VERSION) .released `date -I`" || \ 129 | (echo 'error: You need to update date/version in $(srcdir)/NEWS'; exit 1) 130 | rm -f $(srcdir)/ChangeLog 131 | make ChangeLog distcheck 132 | gpg --detach-sign --default-key $(KEYID) $(PACKAGE)-$(VERSION).tar.gz 133 | gpg --verify $(PACKAGE)-$(VERSION).tar.gz.sig 134 | cd $(srcdir) && git push 135 | cd $(srcdir) && git tag -u $(KEYID) -m $(VERSION) $(PACKAGE)-$(VERSION) 136 | cd $(srcdir) && git push --tags 137 | $(YUBICO_WWW_REPO)/publish $(PROJECT) $(VERSION) $(PACKAGE)-$(VERSION).tar.gz* 138 | $(HELP2ADOC) -e ./ykclient -n "YubiCloud One-Time-Password Validation Client" > ykclient.1.txt 139 | $(YUBICO_WWW_REPO)/save-mans $(PROJECT) ykclient.1.txt 140 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | Yubikey-c-client NEWS -- History of user-visible changes. -*- outline -*- 2 | 3 | * Version 2.16 (unreleased) 4 | 5 | * Version 2.15 (released 2015-11-12) 6 | 7 | ** Add ykclient_get_server_response() to the library. 8 | 9 | ** Show more information from the commandline on debug. 10 | 11 | ** Add proxy support via Curl. 12 | 13 | * Version 2.14 (released 2015-03-05) 14 | 15 | ** Switch default templates to https. 16 | 17 | ** Fixup call to curl_easy_escape() to use a easy handle. 18 | 19 | * Version 2.13 (released 2014-09-12) 20 | 21 | ** libykclient: Skip responses where the curl status isn't CURLE_OK. 22 | 23 | ** libykclient: Add forgotten prototype for ykclient_set_ca_info. 24 | 25 | * Version 2.12 (released 2013-10-18) 26 | 27 | ** Use pkg-config to find curl, instead of libcurl.m4. 28 | 29 | ** ykclient: Added --cai parameter to specify GnuTLS-compatible CA Info. 30 | 31 | ** libykclient: Added ykclient_set_ca_info function. 32 | Used when curl is linked with GnuTLS, used to set CA Info. 33 | 34 | ** libykclient: Added ykclient_set_url_bases function. 35 | Uses a more reasonable/extensible URL string syntax. The old 36 | ykclient_set_url_templates is hereby deprecated. 37 | 38 | ** Added shared library versioning script. 39 | 40 | ** Valgrind is used for selftests. 41 | 42 | * Version 2.11 (released 2013-07-24) 43 | 44 | ** Fix breakage with latest automake. 45 | 46 | * Version 2.10 (released 2013-05-15) 47 | 48 | ** Add ykclient_global_init and ykclient_global_done. 49 | 50 | ** Add ykclient_version.h header file with versioning information. 51 | New symbols are YKCLIENT_VERSION_STRING, YKCLIENT_VERSION_NUMBER, 52 | YKCLIENT_VERSION_MAJOR, YKCLIENT_VERSION_MINOR, 53 | YKCLIENT_VERSION_PATCH. New function ykclient_check_version. 54 | 55 | ** Modified API to use 'ykclient_rc' enum as return type instead of 'int'. 56 | ** Enum also moved to separate new header file ykclient_errors.h. 57 | This should be backwards compatible. It makes the return type 58 | clearer. From Arran Cudbard-Bell . 59 | 60 | ** Improve curl multi usage. 61 | From Arran Cudbard-Bell . 62 | 63 | ** ykclient: Cleanup command line tool a bit to make it more useful. 64 | Added --help, --version and --debug. Defaults to silent output. Exit 65 | codes are documented and more useful. Added manpage. 66 | 67 | * Version 2.9 (released 2012-08-07) 68 | 69 | ** Compability with curl versions before 7.20. 70 | 71 | ** Fix signature checking on ARM (at least). 72 | 73 | * Version 2.8 (released 2012-06-15) 74 | 75 | ** ykclient: Add C++ namespace protection. 76 | 77 | ** Add multi-server support with curl_multi. 78 | Enabled by default for YubiCloud servers. 79 | Settable with the new library function set_template_urls() or 80 | the urls parameter to ykclient_verify_otp_v2(). 81 | 82 | ** Remove extra % in ykclient help. 83 | 84 | ** Add ca path option to ykclient, --ca. 85 | Patch from Jay Kline . 86 | 87 | ** Make the nonce unique for consecutive calls to the same ykclient handle. 88 | 89 | ** Do url encoding of OTP before sending. 90 | 91 | ** Fix segfault on curl error. 92 | Patch from Lee Hinman 93 | 94 | * Version 2.7 (released 2011-11-23) 95 | 96 | ** Verify server signatures per default when a key is provided. The old 97 | behavior can be restored with ykclient_set_verify_signature (ykc, 0); 98 | Reported by Dominic Rutherford . 99 | 100 | ** Return YKCLIENT_BAD_SERVER_SIGNATURE on missing signature. 101 | 102 | * Version 2.6 (released 2011-06-06) 103 | 104 | ** Use fprintf+exit instead of glibc-ism errx. 105 | Reported by Jussi Sallinen . 106 | 107 | ** Indent code and replace // comments with /* */ comments (for C89). 108 | 109 | ** Fix dates of releases for 2.4 and 2.5 below (2011 not 2010). 110 | 111 | * Version 2.5 (released 2011-05-31) 112 | 113 | ** Implement almost all of Validation protocol 2.0. 114 | The part still known to be missing is parallell querys to all validation 115 | servers using curl-multi. Includes significant patches by 116 | Sebastien Martini and qistoph@gmail.com. 117 | 118 | * Version 2.4 (released 2011-03-02) 119 | 120 | ** Add possibility to supply validation service URL. 121 | Patch by Remi Mollon . 122 | 123 | ** Make Validation protocol 2.0 servers not reject our requests. 124 | They would reject our requests because they did not have a nonce 125 | parameter. Patch by Remi Mollon . 126 | 127 | ** Add new API ykclient_set_ca_path to set a trusted CA certificates path. 128 | 129 | ** Fix issue with insecure rpath in the ykclient executable. 130 | 131 | ** Documentation converted to asciidoc after move to Github. 132 | 133 | * Version 2.3 (released 2009-05-11) 134 | 135 | ** Add new API ykclient_set_client_b64 to set client key in b64 format. 136 | 137 | ** Fix memory leaks. 138 | 139 | ** Fix usage when sending multiple requests. 140 | 141 | * Version 2.2 (released 2009-03-31) 142 | 143 | ** Add new API ykclient_set_client_hex to set client key in hex format. 144 | 145 | ** Change API ykclient_verify_otp to take a hex key rather than a binary one. 146 | 147 | ** Improved instructions in README on how to build the package. 148 | 149 | * Version 2.1 (released 2009-03-25) 150 | 151 | ** Add new API ykclient_get_last_url, mostly for debugging purposes. 152 | 153 | ** Create signatures on requests. 154 | 155 | * Version 2.0 (released 2009-03-25) 156 | 157 | ** Major rewrite of library. 158 | The library changed name from libyubikey-client.* to libykclient.* and 159 | the header file changed from libykclient.h to ykclient.h. All 160 | interfaces were also renamed, see ykclient.h. 161 | 162 | * Version 1.5 (released 2009-01-13) 163 | 164 | ** Add libtool -export-symbols-regex to restrict exported symbols. 165 | 166 | ** Avoid use of asprintf, to make it work under Solaris too. 167 | 168 | ** Fix memory leaks. 169 | 170 | * Version 1.4 (released 2008-09-15) 171 | 172 | ** Added include paths to make aclocal find libcurl.m4. 173 | Thanks to Oden Eriksson . 174 | 175 | * Version 1.3 (released 2008-09-15) 176 | 177 | ** ykclient: Add new API yubikey_client_set_url_template. 178 | Allows setting a private server to query. 179 | 180 | * Version 1.2 (released 2008-07-24) 181 | 182 | ** ykclient: Set exit code as per OTP verification result. 183 | Inspired by discussion in 184 | . 185 | 186 | ** Install libykclient.h. 187 | 188 | * Version 1.1 (released 2008-06-25) 189 | 190 | ** Install libykclient.h in $prefix. 191 | 192 | * Version 1.0 (released 2008-06-17) 193 | 194 | ** Initial release, code from yubico-pam. 195 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | == YubiKey C Client Library: libykclient == 2 | 3 | NOTE: This project is deprecated and is no longer being maintained. For more information and guidance on how to implement Yubico OTP support in applications, see https://status.yubico.com/2021/04/15/one-api-yubico-com-one-http-get/. 4 | 5 | YubiKey C Client Library (libykclient) is a C library used to validate 6 | an Yubikey OTP against Yubico's servers. See 7 | https://www.yubico.com[the Yubico website] 8 | for more information about Yubico and the YubiKey. 9 | 10 | === Building === 11 | 12 | After downloading and unpacking the package tarball, you build it as 13 | follows. 14 | 15 | ./configure 16 | make check 17 | sudo make install 18 | 19 | === Building from version controlled sources === 20 | 21 | Warning! This is only for developers and if you don't understand what 22 | you are doing, you should download the release files instead. 23 | 24 | You may check out the sources using Git with the following command: 25 | 26 | git clone https://github.com/Yubico/yubico-c-client.git 27 | 28 | This will create a directory 'yubico-c-client'. Enter the directory: 29 | 30 | cd yubico-c-client 31 | 32 | Recent versions of autoconf, automake and libtool must be installed. 33 | Help2man is used to generate the manpages. Libcurl development files 34 | (headers and *.so) must also be installed. 35 | 36 | Generate the build system using: 37 | 38 | autoreconf --install 39 | 40 | Then refer to the user sections above regarding building. 41 | 42 | === Command-line tools === 43 | 44 | [CAUTION] 45 | The ykclient command line tool is for testing/debugging purposes only. 46 | Secret data, such as the client secret, should generally not be passed 47 | as a command line argument in production. Doing so may expose the 48 | value to other parts of the system. 49 | 50 | There is one command line tool, ykclient, to validate a particular 51 | OTP. It needs a client id, which you can generate 52 | https://upgrade.yubico.com/getapikey[here]. 53 | 54 | Example usage: 55 | 56 | [source, sh] 57 | ----------- 58 | jas@mocca:~/src/yubico-c-client$ ./ykclient 59 | Usage: ./ykclient 60 | CLIENT_ID: your client id integer 61 | YUBIKEY_OTP: One-time password generated by yubikey 62 | jas@mocca:~/src/yubico-c-client$ ./ykclient --debug 16 ccccccbchvthlivuitriujjifivbvtrjkjfirllluurj 63 | Input: 64 | client id: 16 65 | token: ccccccbchvthlivuitriujjifivbvtrjkjfirllluurj 66 | Verification output (2): Yubikey OTP was replayed (REPLAYED_OTP) 67 | jas@mocca:~/src/yubico-c-client$ 68 | ----------- 69 | 70 | === License === 71 | 72 | The project is licensed under a BSD license. See the file COPYING for 73 | exact wording. For any copyright year range specified as YYYY-ZZZZ in 74 | this package note that the range specifies every single year in that 75 | closed interval. 76 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | README -------------------------------------------------------------------------------- /build-and-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | set -x 5 | 6 | autoreconf -i 7 | ./configure $COVERAGE 8 | make check 9 | if [ "x$COVERAGE" != "x" ]; then 10 | gem install coveralls-lcov 11 | set +x 12 | coveralls-lcov coverage/app2.info 13 | fi 14 | -------------------------------------------------------------------------------- /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 11 | base64_decode_value (char value_in) 12 | { 13 | static const char decoding[] = 14 | { 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, 15 | -2, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 16 | 17, 18, 19, 20, 21, 22, 23, 17 | 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 18 | 36, 37, 38, 39, 40, 41, 42, 19 | 43, 44, 45, 46, 47, 48, 49, 50, 51 20 | }; 21 | static const char decoding_size = sizeof (decoding); 22 | value_in -= 43; 23 | if (value_in < 0 || value_in >= decoding_size) 24 | return -1; 25 | return decoding[(int) value_in]; 26 | } 27 | 28 | void 29 | base64_init_decodestate (base64_decodestate * state_in) 30 | { 31 | state_in->step = step_a; 32 | state_in->plainchar = 0; 33 | } 34 | 35 | int 36 | base64_decode_block (const char *code_in, const int length_in, 37 | char *plaintext_out, base64_decodestate * state_in) 38 | { 39 | const char *codechar = code_in; 40 | char *plainchar = plaintext_out; 41 | signed char fragment; 42 | 43 | *plainchar = state_in->plainchar; 44 | 45 | switch (state_in->step) 46 | { 47 | while (1) 48 | { 49 | case step_a: 50 | do 51 | { 52 | if (codechar == code_in + length_in) 53 | { 54 | state_in->step = step_a; 55 | state_in->plainchar = *plainchar; 56 | return plainchar - plaintext_out; 57 | } 58 | fragment = (char) base64_decode_value (*codechar++); 59 | } 60 | while (fragment < 0); 61 | *plainchar = (fragment & 0x03f) << 2; 62 | case step_b: 63 | do 64 | { 65 | if (codechar == code_in + length_in) 66 | { 67 | state_in->step = step_b; 68 | state_in->plainchar = *plainchar; 69 | return plainchar - plaintext_out; 70 | } 71 | fragment = (char) base64_decode_value (*codechar++); 72 | } 73 | while (fragment < 0); 74 | *plainchar++ |= (fragment & 0x030) >> 4; 75 | *plainchar = (fragment & 0x00f) << 4; 76 | case step_c: 77 | do 78 | { 79 | if (codechar == code_in + length_in) 80 | { 81 | state_in->step = step_c; 82 | state_in->plainchar = *plainchar; 83 | return plainchar - plaintext_out; 84 | } 85 | fragment = (char) base64_decode_value (*codechar++); 86 | } 87 | while (fragment < 0); 88 | *plainchar++ |= (fragment & 0x03c) >> 2; 89 | *plainchar = (fragment & 0x003) << 6; 90 | case step_d: 91 | do 92 | { 93 | if (codechar == code_in + length_in) 94 | { 95 | state_in->step = step_d; 96 | state_in->plainchar = *plainchar; 97 | return plainchar - plaintext_out; 98 | } 99 | fragment = (char) base64_decode_value (*codechar++); 100 | } 101 | while (fragment < 0); 102 | *plainchar++ |= (fragment & 0x03f); 103 | } 104 | } 105 | /* control should not reach here */ 106 | return plainchar - plaintext_out; 107 | } 108 | -------------------------------------------------------------------------------- /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 | typedef enum 11 | { step_a, step_b, step_c, step_d 12 | } base64_decodestep; 13 | typedef struct 14 | { 15 | base64_decodestep step; 16 | char plainchar; 17 | } base64_decodestate; 18 | void base64_init_decodestate (base64_decodestate * state_in); 19 | int base64_decode_value (char value_in); 20 | int base64_decode_block (const char *code_in, const int length_in, 21 | char *plaintext_out, 22 | base64_decodestate * state_in); 23 | 24 | #endif /* BASE64_CDECODE_H */ 25 | -------------------------------------------------------------------------------- /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 13 | base64_init_encodestate (base64_encodestate * state_in) 14 | { 15 | state_in->step = step_A; 16 | state_in->result = 0; 17 | state_in->stepcount = 0; 18 | } 19 | 20 | char 21 | base64_encode_value (char value_in) 22 | { 23 | static const char *encoding = 24 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 25 | if (value_in > 63) 26 | return '='; 27 | return encoding[(int) value_in]; 28 | } 29 | 30 | int 31 | base64_encode_block (const char *plaintext_in, int length_in, char *code_out, 32 | base64_encodestate * state_in) 33 | { 34 | const char *plainchar = plaintext_in; 35 | const char *const plaintextend = plaintext_in + length_in; 36 | char *codechar = code_out; 37 | char result; 38 | signed char fragment; 39 | 40 | result = state_in->result; 41 | 42 | switch (state_in->step) 43 | { 44 | while (1) 45 | { 46 | case step_A: 47 | if (plainchar == plaintextend) 48 | { 49 | state_in->result = result; 50 | state_in->step = step_A; 51 | return codechar - code_out; 52 | } 53 | fragment = *plainchar++; 54 | result = (fragment & 0x0fc) >> 2; 55 | *codechar++ = base64_encode_value (result); 56 | result = (fragment & 0x003) << 4; 57 | case step_B: 58 | if (plainchar == plaintextend) 59 | { 60 | state_in->result = result; 61 | state_in->step = step_B; 62 | return codechar - code_out; 63 | } 64 | fragment = *plainchar++; 65 | result |= (fragment & 0x0f0) >> 4; 66 | *codechar++ = base64_encode_value (result); 67 | result = (fragment & 0x00f) << 2; 68 | case step_C: 69 | if (plainchar == plaintextend) 70 | { 71 | state_in->result = result; 72 | state_in->step = step_C; 73 | return codechar - code_out; 74 | } 75 | fragment = *plainchar++; 76 | result |= (fragment & 0x0c0) >> 6; 77 | *codechar++ = base64_encode_value (result); 78 | result = (fragment & 0x03f) >> 0; 79 | *codechar++ = base64_encode_value (result); 80 | 81 | ++(state_in->stepcount); 82 | if (state_in->stepcount == CHARS_PER_LINE / 4) 83 | { 84 | *codechar++ = '\n'; 85 | state_in->stepcount = 0; 86 | } 87 | } 88 | } 89 | /* control should not reach here */ 90 | return codechar - code_out; 91 | } 92 | 93 | int 94 | base64_encode_blockend (char *code_out, base64_encodestate * state_in) 95 | { 96 | char *codechar = code_out; 97 | 98 | switch (state_in->step) 99 | { 100 | case step_B: 101 | *codechar++ = base64_encode_value (state_in->result); 102 | *codechar++ = '='; 103 | *codechar++ = '='; 104 | break; 105 | case step_C: 106 | *codechar++ = base64_encode_value (state_in->result); 107 | *codechar++ = '='; 108 | break; 109 | case step_A: 110 | break; 111 | } 112 | *codechar++ = '\n'; 113 | 114 | return codechar - code_out; 115 | } 116 | -------------------------------------------------------------------------------- /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 | typedef enum 11 | { step_A, step_B, step_C 12 | } base64_encodestep; 13 | typedef struct 14 | { 15 | base64_encodestep step; 16 | char result; 17 | int stepcount; 18 | } base64_encodestate; 19 | void base64_init_encodestate (base64_encodestate * state_in); 20 | char base64_encode_value (char value_in); 21 | int base64_encode_block (const char *plaintext_in, int length_in, 22 | char *code_out, base64_encodestate * state_in); 23 | int base64_encode_blockend (char *code_out, base64_encodestate * state_in); 24 | 25 | #endif /* BASE64_CENCODE_H */ 26 | 27 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # Written by Simon Josefsson . 2 | # Copyright (c) 2008-2013 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 | AC_INIT([ykclient], [2.16], [yubico-devel@googlegroups.com]) 30 | AC_CONFIG_AUX_DIR([build-aux]) 31 | AC_CONFIG_MACRO_DIR([m4]) 32 | 33 | # Library code modified: REVISION++ 34 | # Interfaces changed/added/removed: CURRENT++ REVISION=0 35 | # Interfaces added: AGE++ 36 | # Interfaces removed: AGE=0 37 | AC_SUBST(LT_CURRENT, 10) 38 | AC_SUBST(LT_REVISION, 0) 39 | AC_SUBST(LT_AGE, 7) 40 | 41 | AM_INIT_AUTOMAKE([1.11 -Wall -Werror]) 42 | AM_SILENT_RULES([yes]) 43 | AC_PROG_CC 44 | 45 | m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) 46 | 47 | AM_MISSING_PROG(HELP2MAN, help2man, $missing_dir) 48 | AM_MISSING_PROG(HELP2ADOC, help2adoc, $missing_dir) 49 | AC_LIBTOOL_WIN32_DLL 50 | AC_PROG_LIBTOOL 51 | 52 | gl_LD_VERSION_SCRIPT 53 | gl_VALGRIND_TESTS 54 | 55 | PKG_CHECK_MODULES([LIBCURL], [libcurl]) 56 | 57 | # --disable-documentation 58 | AC_ARG_ENABLE([documentation], 59 | [AS_HELP_STRING([--disable-documentation], 60 | [do not build documentation])], 61 | [enable_doc="${enableval}" ], 62 | [enable_doc="yes"]) 63 | AM_CONDITIONAL(ENABLE_DOC, test "$enable_doc" != "no") 64 | 65 | # --enable-coverage 66 | AC_ARG_ENABLE([coverage], 67 | [AS_HELP_STRING([--enable-coverage], 68 | [use Gcov to test the test suite])], 69 | [], 70 | [enable_cov=no]) 71 | AM_CONDITIONAL([ENABLE_COV],[test '!' "$enable_cov" = no]) 72 | 73 | AC_ARG_ENABLE([gcc-warnings], 74 | [AS_HELP_STRING([--enable-gcc-warnings], 75 | [turn on lots of GCC warnings (for developers)])], 76 | [case $enableval in 77 | yes|no) ;; 78 | *) AC_MSG_ERROR([bad value $enableval for gcc-warnings option]) ;; 79 | esac 80 | gl_gcc_warnings=$enableval], 81 | [gl_gcc_warnings=no] 82 | ) 83 | 84 | if test "$gl_gcc_warnings" = yes; then 85 | nw="$nw -Wsystem-headers" # Don't let system headers trigger warnings 86 | nw="$nw -Wpadded" # Struct's arenot padded 87 | nw="$nw -Wc++-compat" # We don't care strongly about C++ compilers 88 | nw="$nw -Wtraditional" # Warns on #elif which we use often 89 | nw="$nw -Wtraditional-conversion" # Too many warnings for now 90 | nw="$nw -Wconversion" # Too many warnings for now 91 | nw="$nw -Wsuggest-attribute=pure" # Is it worth using attributes? 92 | 93 | gl_MANYWARN_ALL_GCC([ws]) 94 | gl_MANYWARN_COMPLEMENT(ws, [$ws], [$nw]) 95 | for w in $ws; do 96 | gl_WARN_ADD([$w]) 97 | done 98 | 99 | gl_WARN_ADD([-fdiagnostics-show-option]) 100 | fi 101 | 102 | AC_SUBST(YKCLIENT_VERSION_MAJOR, `echo $PACKAGE_VERSION | sed 's/\(.*\)\..*/\1/g'`) 103 | AC_SUBST(YKCLIENT_VERSION_MINOR, `echo $PACKAGE_VERSION | sed 's/.*\.\(.*\)/\1/g'`) 104 | AC_SUBST(YKCLIENT_VERSION_PATCH, 0) 105 | AC_SUBST(YKCLIENT_VERSION_NUMBER, `printf "0x%02x%02x%02x" $YKCLIENT_VERSION_MAJOR $YKCLIENT_VERSION_MINOR $YKCLIENT_VERSION_PATCH`) 106 | 107 | AC_CONFIG_FILES([ 108 | Makefile 109 | ykclient_version.h 110 | tests/Makefile 111 | ]) 112 | AC_OUTPUT 113 | 114 | AC_MSG_NOTICE([summary of build options: 115 | 116 | Version: ${VERSION} 117 | Library: current $LT_CURRENT revision $LT_REVISION age $LT_AGE 118 | Header: major $YKCLIENT_VERSION_MAJOR minor $YKCLIENT_VERSION_MINOR patch $YKCLIENT_VERSION_PATCH number $YKCLIENT_VERSION_NUMBER 119 | Host type: ${host} 120 | Install prefix: ${prefix} 121 | Compiler: ${CC} 122 | Compiler CFLAGS: ${CFLAGS} 123 | Warning flags: ${WARN_CFLAGS} 124 | Library types: Shared=${enable_shared}, Static=${enable_static} 125 | Curl CFLAGS: ${LIBCURL_CFLAGS} 126 | LDFLAGS: ${LIBCURL_LIBS} 127 | Valgrind: ${VALGRIND} 128 | Version script: $have_ld_version_script 129 | ]) 130 | -------------------------------------------------------------------------------- /hmac.c: -------------------------------------------------------------------------------- 1 | /**************************** hmac.c ****************************/ 2 | /******************** See RFC 4634 for details ******************/ 3 | /* 4 | * Description: 5 | * This file implements the HMAC algorithm (Keyed-Hashing for 6 | * Message Authentication, RFC2104), expressed in terms of the 7 | * various SHA algorithms. 8 | */ 9 | 10 | #include "sha.h" 11 | 12 | /* 13 | * hmac 14 | * 15 | * Description: 16 | * This function will compute an HMAC message digest. 17 | * 18 | * Parameters: 19 | * whichSha: [in] 20 | * One of SHA1, SHA224, SHA256, SHA384, SHA512 21 | * key: [in] 22 | * The secret shared key. 23 | * key_len: [in] 24 | * The length of the secret shared key. 25 | * message_array: [in] 26 | * An array of characters representing the message. 27 | * length: [in] 28 | * The length of the message in message_array 29 | * digest: [out] 30 | * Where the digest is returned. 31 | * NOTE: The length of the digest is determined by 32 | * the value of whichSha. 33 | * 34 | * Returns: 35 | * sha Error Code. 36 | * 37 | */ 38 | int 39 | hmac (SHAversion whichSha, const unsigned char *text, int text_len, 40 | const unsigned char *key, int key_len, uint8_t digest[USHAMaxHashSize]) 41 | { 42 | HMACContext ctx; 43 | return hmacReset (&ctx, whichSha, key, key_len) || 44 | hmacInput (&ctx, text, text_len) || hmacResult (&ctx, digest); 45 | } 46 | 47 | /* 48 | * hmacReset 49 | * 50 | * Description: 51 | * This function will initialize the hmacContext in preparation 52 | * for computing a new HMAC message digest. 53 | * 54 | * Parameters: 55 | * context: [in/out] 56 | * The context to reset. 57 | * whichSha: [in] 58 | * One of SHA1, SHA224, SHA256, SHA384, SHA512 59 | * key: [in] 60 | * The secret shared key. 61 | * key_len: [in] 62 | * The length of the secret shared key. 63 | * 64 | * Returns: 65 | * sha Error Code. 66 | * 67 | */ 68 | int 69 | hmacReset (HMACContext * ctx, enum SHAversion whichSha, 70 | const unsigned char *key, int key_len) 71 | { 72 | int i, blocksize, hashsize; 73 | 74 | /* inner padding - key XORd with ipad */ 75 | unsigned char k_ipad[USHA_Max_Message_Block_Size]; 76 | 77 | /* temporary buffer when keylen > blocksize */ 78 | unsigned char tempkey[USHAMaxHashSize]; 79 | 80 | if (!ctx) 81 | return shaNull; 82 | 83 | blocksize = ctx->blockSize = USHABlockSize (whichSha); 84 | hashsize = ctx->hashSize = USHAHashSize (whichSha); 85 | 86 | ctx->whichSha = whichSha; 87 | 88 | /* 89 | * If key is longer than the hash blocksize, 90 | * reset it to key = HASH(key). 91 | */ 92 | if (key_len > blocksize) 93 | { 94 | USHAContext tctx; 95 | int err = USHAReset (&tctx, whichSha) || 96 | USHAInput (&tctx, key, key_len) || USHAResult (&tctx, tempkey); 97 | if (err != shaSuccess) 98 | return err; 99 | 100 | key = tempkey; 101 | key_len = hashsize; 102 | } 103 | 104 | /* 105 | * The HMAC transform looks like: 106 | * 107 | * SHA(K XOR opad, SHA(K XOR ipad, text)) 108 | * 109 | * where K is an n byte key. 110 | * ipad is the byte 0x36 repeated blocksize times 111 | * opad is the byte 0x5c repeated blocksize times 112 | * and text is the data being protected. 113 | */ 114 | 115 | /* store key into the pads, XOR'd with ipad and opad values */ 116 | for (i = 0; i < key_len; i++) 117 | { 118 | k_ipad[i] = key[i] ^ 0x36; 119 | ctx->k_opad[i] = key[i] ^ 0x5c; 120 | } 121 | /* remaining pad bytes are '\0' XOR'd with ipad and opad values */ 122 | for (; i < blocksize; i++) 123 | { 124 | k_ipad[i] = 0x36; 125 | ctx->k_opad[i] = 0x5c; 126 | } 127 | 128 | /* perform inner hash */ 129 | /* init context for 1st pass */ 130 | return USHAReset (&ctx->shaContext, whichSha) || 131 | /* and start with inner pad */ 132 | USHAInput (&ctx->shaContext, k_ipad, blocksize); 133 | } 134 | 135 | /* 136 | * hmacInput 137 | * 138 | * Description: 139 | * This function accepts an array of octets as the next portion 140 | * of the message. 141 | * 142 | * Parameters: 143 | * context: [in/out] 144 | * The HMAC context to update 145 | * message_array: [in] 146 | * An array of characters representing the next portion of 147 | * the message. 148 | * length: [in] 149 | * The length of the message in message_array 150 | * 151 | * Returns: 152 | * sha Error Code. 153 | * 154 | */ 155 | int 156 | hmacInput (HMACContext * ctx, const unsigned char *text, int text_len) 157 | { 158 | if (!ctx) 159 | return shaNull; 160 | /* then text of datagram */ 161 | return USHAInput (&ctx->shaContext, text, text_len); 162 | } 163 | 164 | /* 165 | * HMACFinalBits 166 | * 167 | * Description: 168 | * This function will add in any final bits of the message. 169 | * 170 | * Parameters: 171 | * context: [in/out] 172 | * The HMAC context to update 173 | * message_bits: [in] 174 | * The final bits of the message, in the upper portion of the 175 | * byte. (Use 0b###00000 instead of 0b00000### to input the 176 | * three bits ###.) 177 | * length: [in] 178 | * The number of bits in message_bits, between 1 and 7. 179 | * 180 | * Returns: 181 | * sha Error Code. 182 | */ 183 | int 184 | hmacFinalBits (HMACContext * ctx, const uint8_t bits, unsigned int bitcount) 185 | { 186 | if (!ctx) 187 | return shaNull; 188 | /* then final bits of datagram */ 189 | return USHAFinalBits (&ctx->shaContext, bits, bitcount); 190 | } 191 | 192 | /* 193 | * HMACResult 194 | * 195 | * Description: 196 | * This function will return the N-byte message digest into the 197 | * Message_Digest array provided by the caller. 198 | * NOTE: The first octet of hash is stored in the 0th element, 199 | * the last octet of hash in the Nth element. 200 | * 201 | * Parameters: 202 | * context: [in/out] 203 | * The context to use to calculate the HMAC hash. 204 | * digest: [out] 205 | * Where the digest is returned. 206 | * NOTE 2: The length of the hash is determined by the value of 207 | * whichSha that was passed to hmacReset(). 208 | * 209 | * Returns: 210 | * sha Error Code. 211 | * 212 | */ 213 | int 214 | hmacResult (HMACContext * ctx, uint8_t * digest) 215 | { 216 | if (!ctx) 217 | return shaNull; 218 | 219 | /* finish up 1st pass */ 220 | /* (Use digest here as a temporary buffer.) */ 221 | return USHAResult (&ctx->shaContext, digest) || 222 | /* perform outer SHA */ 223 | /* init context for 2nd pass */ 224 | USHAReset (&ctx->shaContext, ctx->whichSha) || 225 | /* start with outer pad */ 226 | USHAInput (&ctx->shaContext, ctx->k_opad, ctx->blockSize) || 227 | /* then results of 1st hash */ 228 | USHAInput (&ctx->shaContext, digest, ctx->hashSize) || 229 | /* finish up 2nd pass */ 230 | USHAResult (&ctx->shaContext, digest); 231 | } 232 | -------------------------------------------------------------------------------- /libykclient.map: -------------------------------------------------------------------------------- 1 | # Written by Simon Josefsson . 2 | # Copyright (c) 2013 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 | Base { 30 | global: 31 | ykclient_done; 32 | ykclient_get_last_url; 33 | ykclient_init; 34 | ykclient_request; 35 | ykclient_server_response_free; 36 | ykclient_server_response_get; 37 | ykclient_server_response_init; 38 | ykclient_server_response_parse; 39 | ykclient_server_response_verify_signature; 40 | ykclient_set_ca_path; 41 | ykclient_set_client; 42 | ykclient_set_client_b64; 43 | ykclient_set_client_hex; 44 | ykclient_set_nonce; 45 | ykclient_set_url_template; 46 | ykclient_set_url_templates; 47 | ykclient_set_verify_signature; 48 | ykclient_strerror; 49 | ykclient_verify_otp; 50 | ykclient_verify_otp_v2; 51 | ykclient_check_version; 52 | ykclient_global_done; 53 | ykclient_global_init; 54 | ykclient_handle_cleanup; 55 | ykclient_handle_done; 56 | ykclient_handle_init; 57 | ykclient_request_process; 58 | 59 | local: 60 | *; 61 | }; 62 | 63 | YKCLIENT_2.12 { 64 | global: 65 | ykclient_set_ca_info; 66 | ykclient_set_url_bases; 67 | } Base; 68 | 69 | YKCLIENT_2.15 { 70 | global: 71 | ykclient_get_server_response; 72 | ykclient_set_proxy; 73 | } YKCLIENT_2.12; 74 | 75 | YKCLIENT_2.16 { 76 | global: 77 | ykclient_set_max_retries; 78 | } YKCLIENT_2.15; 79 | -------------------------------------------------------------------------------- /m4/ld-version-script.m4: -------------------------------------------------------------------------------- 1 | # ld-version-script.m4 serial 3 2 | dnl Copyright (C) 2008-2012 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 <. 5 | # 6 | # This program is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, but 12 | # WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | # General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program; if not, write to the Free Software 18 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 19 | # 20 | # As a special exception to the GNU General Public License, if you 21 | # distribute this file as part of a program that contains a 22 | # configuration script generated by Autoconf, you may include it under 23 | # the same distribution terms that you use for the rest of that program. 24 | 25 | # PKG_PROG_PKG_CONFIG([MIN-VERSION]) 26 | # ---------------------------------- 27 | AC_DEFUN([PKG_PROG_PKG_CONFIG], 28 | [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) 29 | m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) 30 | m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) 31 | AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) 32 | AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) 33 | AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) 34 | 35 | if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then 36 | AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) 37 | fi 38 | if test -n "$PKG_CONFIG"; then 39 | _pkg_min_version=m4_default([$1], [0.9.0]) 40 | AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) 41 | if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then 42 | AC_MSG_RESULT([yes]) 43 | else 44 | AC_MSG_RESULT([no]) 45 | PKG_CONFIG="" 46 | fi 47 | fi[]dnl 48 | ])# PKG_PROG_PKG_CONFIG 49 | 50 | # PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) 51 | # 52 | # Check to see whether a particular set of modules exists. Similar 53 | # to PKG_CHECK_MODULES(), but does not set variables or print errors. 54 | # 55 | # Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) 56 | # only at the first occurence in configure.ac, so if the first place 57 | # it's called might be skipped (such as if it is within an "if", you 58 | # have to call PKG_CHECK_EXISTS manually 59 | # -------------------------------------------------------------- 60 | AC_DEFUN([PKG_CHECK_EXISTS], 61 | [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl 62 | if test -n "$PKG_CONFIG" && \ 63 | AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then 64 | m4_default([$2], [:]) 65 | m4_ifvaln([$3], [else 66 | $3])dnl 67 | fi]) 68 | 69 | # _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) 70 | # --------------------------------------------- 71 | m4_define([_PKG_CONFIG], 72 | [if test -n "$$1"; then 73 | pkg_cv_[]$1="$$1" 74 | elif test -n "$PKG_CONFIG"; then 75 | PKG_CHECK_EXISTS([$3], 76 | [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` 77 | test "x$?" != "x0" && pkg_failed=yes ], 78 | [pkg_failed=yes]) 79 | else 80 | pkg_failed=untried 81 | fi[]dnl 82 | ])# _PKG_CONFIG 83 | 84 | # _PKG_SHORT_ERRORS_SUPPORTED 85 | # ----------------------------- 86 | AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], 87 | [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) 88 | if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then 89 | _pkg_short_errors_supported=yes 90 | else 91 | _pkg_short_errors_supported=no 92 | fi[]dnl 93 | ])# _PKG_SHORT_ERRORS_SUPPORTED 94 | 95 | 96 | # PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], 97 | # [ACTION-IF-NOT-FOUND]) 98 | # 99 | # 100 | # Note that if there is a possibility the first call to 101 | # PKG_CHECK_MODULES might not happen, you should be sure to include an 102 | # explicit call to PKG_PROG_PKG_CONFIG in your configure.ac 103 | # 104 | # 105 | # -------------------------------------------------------------- 106 | AC_DEFUN([PKG_CHECK_MODULES], 107 | [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl 108 | AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl 109 | AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl 110 | 111 | pkg_failed=no 112 | AC_MSG_CHECKING([for $1]) 113 | 114 | _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) 115 | _PKG_CONFIG([$1][_LIBS], [libs], [$2]) 116 | 117 | m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS 118 | and $1[]_LIBS to avoid the need to call pkg-config. 119 | See the pkg-config man page for more details.]) 120 | 121 | if test $pkg_failed = yes; then 122 | AC_MSG_RESULT([no]) 123 | _PKG_SHORT_ERRORS_SUPPORTED 124 | if test $_pkg_short_errors_supported = yes; then 125 | $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` 126 | else 127 | $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` 128 | fi 129 | # Put the nasty error message in config.log where it belongs 130 | echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD 131 | 132 | m4_default([$4], [AC_MSG_ERROR( 133 | [Package requirements ($2) were not met: 134 | 135 | $$1_PKG_ERRORS 136 | 137 | Consider adjusting the PKG_CONFIG_PATH environment variable if you 138 | installed software in a non-standard prefix. 139 | 140 | _PKG_TEXT])[]dnl 141 | ]) 142 | elif test $pkg_failed = untried; then 143 | AC_MSG_RESULT([no]) 144 | m4_default([$4], [AC_MSG_FAILURE( 145 | [The pkg-config script could not be found or is too old. Make sure it 146 | is in your PATH or set the PKG_CONFIG environment variable to the full 147 | path to pkg-config. 148 | 149 | _PKG_TEXT 150 | 151 | To get pkg-config, see .])[]dnl 152 | ]) 153 | else 154 | $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS 155 | $1[]_LIBS=$pkg_cv_[]$1[]_LIBS 156 | AC_MSG_RESULT([yes]) 157 | $3 158 | fi[]dnl 159 | ])# PKG_CHECK_MODULES 160 | -------------------------------------------------------------------------------- /m4/valgrind-tests.m4: -------------------------------------------------------------------------------- 1 | # valgrind-tests.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 | # gl_VALGRIND_TESTS() 10 | # ------------------- 11 | # Check if valgrind is available, and set VALGRIND to it if available. 12 | AC_DEFUN([gl_VALGRIND_TESTS], 13 | [ 14 | AC_ARG_ENABLE(valgrind-tests, 15 | AS_HELP_STRING([--enable-valgrind-tests], 16 | [run self tests under valgrind]), 17 | [opt_valgrind_tests=$enableval], [opt_valgrind_tests=yes]) 18 | 19 | # Run self-tests under valgrind? 20 | if test "$opt_valgrind_tests" = "yes" && test "$cross_compiling" = no; then 21 | AC_CHECK_PROGS(VALGRIND, valgrind) 22 | fi 23 | 24 | OPTS="-q --error-exitcode=1 --leak-check=full" 25 | 26 | if test -n "$VALGRIND" \ 27 | && $VALGRIND $OPTS $SHELL -c 'exit 0' > /dev/null 2>&1; then 28 | opt_valgrind_tests=yes 29 | VALGRIND="$VALGRIND $OPTS" 30 | else 31 | opt_valgrind_tests=no 32 | VALGRIND= 33 | fi 34 | 35 | AC_MSG_CHECKING([whether self tests are run under valgrind]) 36 | AC_MSG_RESULT($opt_valgrind_tests) 37 | ]) 38 | -------------------------------------------------------------------------------- /m4/warnings.m4: -------------------------------------------------------------------------------- 1 | # warnings.m4 serial 7 2 | dnl Copyright (C) 2008-2012 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]), [" $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 | 42 | # gl_WARN_ADD(OPTION, [VARIABLE = WARN_CFLAGS], 43 | # [PROGRAM = AC_LANG_PROGRAM()]) 44 | # --------------------------------------------- 45 | # Adds parameter to WARN_CFLAGS if the compiler supports it when 46 | # compiling PROGRAM. For example, gl_WARN_ADD([-Wparentheses]). 47 | # 48 | # If VARIABLE is a variable name, AC_SUBST it. 49 | AC_DEFUN([gl_WARN_ADD], 50 | [gl_COMPILER_OPTION_IF([$1], 51 | [gl_AS_VAR_APPEND(m4_if([$2], [], [[WARN_CFLAGS]], [[$2]]), [" $1"])], 52 | [], 53 | [$3]) 54 | m4_ifval([$2], 55 | [AS_LITERAL_IF([$2], [AC_SUBST([$2])])], 56 | [AC_SUBST([WARN_CFLAGS])])dnl 57 | ]) 58 | 59 | # Local Variables: 60 | # mode: autoconf 61 | # End: 62 | -------------------------------------------------------------------------------- /sha-private.h: -------------------------------------------------------------------------------- 1 | /*************************** sha-private.h ***************************/ 2 | /********************** See RFC 4634 for details *********************/ 3 | #ifndef _SHA_PRIVATE__H 4 | #define _SHA_PRIVATE__H 5 | /* 6 | * These definitions are defined in FIPS-180-2, section 4.1. 7 | * Ch() and Maj() are defined identically in sections 4.1.1, 8 | * 4.1.2 and 4.1.3. 9 | * 10 | * The definitions used in FIPS-180-2 are as follows: 11 | */ 12 | 13 | #ifndef USE_MODIFIED_MACROS 14 | #define SHA_Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z))) 15 | #define SHA_Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) 16 | 17 | #else /* USE_MODIFIED_MACROS */ 18 | /* 19 | * The following definitions are equivalent and potentially faster. 20 | */ 21 | 22 | #define SHA_Ch(x, y, z) (((x) & ((y) ^ (z))) ^ (z)) 23 | #define SHA_Maj(x, y, z) (((x) & ((y) | (z))) | ((y) & (z))) 24 | #endif /* USE_MODIFIED_MACROS */ 25 | 26 | #define SHA_Parity(x, y, z) ((x) ^ (y) ^ (z)) 27 | 28 | #endif /* _SHA_PRIVATE__H */ 29 | -------------------------------------------------------------------------------- /sha.h: -------------------------------------------------------------------------------- 1 | /**************************** sha.h ****************************/ 2 | /******************* See RFC 4634 for details ******************/ 3 | #ifndef _SHA_H_ 4 | #define _SHA_H_ 5 | 6 | /* 7 | * Description: 8 | * This file implements the Secure Hash Signature Standard 9 | * algorithms as defined in the National Institute of Standards 10 | * and Technology Federal Information Processing Standards 11 | * Publication (FIPS PUB) 180-1 published on April 17, 1995, 180-2 12 | * published on August 1, 2002, and the FIPS PUB 180-2 Change 13 | * Notice published on February 28, 2004. 14 | * 15 | * A combined document showing all algorithms is available at 16 | * http://csrc.nist.gov/publications/fips/ 17 | * fips180-2/fips180-2withchangenotice.pdf 18 | * 19 | * The five hashes are defined in these sizes: 20 | * SHA-1 20 byte / 160 bit 21 | * SHA-224 28 byte / 224 bit 22 | * SHA-256 32 byte / 256 bit 23 | * SHA-384 48 byte / 384 bit 24 | * SHA-512 64 byte / 512 bit 25 | */ 26 | 27 | #include 28 | /* 29 | * If you do not have the ISO standard stdint.h header file, then you 30 | * must typedef the following: 31 | * name meaning 32 | * uint64_t unsigned 64 bit integer 33 | * uint32_t unsigned 32 bit integer 34 | * uint8_t unsigned 8 bit integer (i.e., unsigned char) 35 | * int_least16_t integer of >= 16 bits 36 | * 37 | */ 38 | 39 | #ifndef _SHA_enum_ 40 | #define _SHA_enum_ 41 | /* 42 | * All SHA functions return one of these values. 43 | */ 44 | enum 45 | { 46 | shaSuccess = 0, 47 | shaNull, /* Null pointer parameter */ 48 | shaInputTooLong, /* input data too long */ 49 | shaStateError, /* called Input after FinalBits or Result */ 50 | shaBadParam /* passed a bad parameter */ 51 | }; 52 | #endif /* _SHA_enum_ */ 53 | 54 | /* 55 | * These constants hold size information for each of the SHA 56 | * hashing operations 57 | */ 58 | enum 59 | { 60 | SHA1_Message_Block_Size = 64, SHA224_Message_Block_Size = 64, 61 | SHA256_Message_Block_Size = 64, SHA384_Message_Block_Size = 128, 62 | SHA512_Message_Block_Size = 128, 63 | USHA_Max_Message_Block_Size = SHA512_Message_Block_Size, 64 | 65 | SHA1HashSize = 20, SHA224HashSize = 28, SHA256HashSize = 32, 66 | SHA384HashSize = 48, SHA512HashSize = 64, 67 | USHAMaxHashSize = SHA512HashSize, 68 | 69 | SHA1HashSizeBits = 160, SHA224HashSizeBits = 224, 70 | SHA256HashSizeBits = 256, SHA384HashSizeBits = 384, 71 | SHA512HashSizeBits = 512, USHAMaxHashSizeBits = SHA512HashSizeBits 72 | }; 73 | 74 | /* 75 | * These constants are used in the USHA (unified sha) functions. 76 | */ 77 | typedef enum SHAversion 78 | { 79 | SHA1, SHA224, SHA256, SHA384, SHA512 80 | } SHAversion; 81 | 82 | /* 83 | * This structure will hold context information for the SHA-1 84 | * hashing operation. 85 | */ 86 | typedef struct SHA1Context 87 | { 88 | uint32_t Intermediate_Hash[SHA1HashSize / 4]; /* Message Digest */ 89 | 90 | uint32_t Length_Low; /* Message length in bits */ 91 | uint32_t Length_High; /* Message length in bits */ 92 | 93 | int_least16_t Message_Block_Index; /* Message_Block array index */ 94 | /* 512-bit message blocks */ 95 | uint8_t Message_Block[SHA1_Message_Block_Size]; 96 | 97 | int Computed; /* Is the digest computed? */ 98 | int Corrupted; /* Is the digest corrupted? */ 99 | } SHA1Context; 100 | 101 | /* 102 | * This structure will hold context information for the SHA-256 103 | * hashing operation. 104 | */ 105 | typedef struct SHA256Context 106 | { 107 | uint32_t Intermediate_Hash[SHA256HashSize / 4]; /* Message Digest */ 108 | 109 | uint32_t Length_Low; /* Message length in bits */ 110 | uint32_t Length_High; /* Message length in bits */ 111 | 112 | int_least16_t Message_Block_Index; /* Message_Block array index */ 113 | /* 512-bit message blocks */ 114 | uint8_t Message_Block[SHA256_Message_Block_Size]; 115 | 116 | int Computed; /* Is the digest computed? */ 117 | int Corrupted; /* Is the digest corrupted? */ 118 | } SHA256Context; 119 | 120 | /* 121 | * This structure will hold context information for the SHA-512 122 | * hashing operation. 123 | */ 124 | typedef struct SHA512Context 125 | { 126 | #ifdef USE_32BIT_ONLY 127 | uint32_t Intermediate_Hash[SHA512HashSize / 4]; /* Message Digest */ 128 | uint32_t Length[4]; /* Message length in bits */ 129 | #else /* !USE_32BIT_ONLY */ 130 | uint64_t Intermediate_Hash[SHA512HashSize / 8]; /* Message Digest */ 131 | uint64_t Length_Low, Length_High; /* Message length in bits */ 132 | #endif /* USE_32BIT_ONLY */ 133 | int_least16_t Message_Block_Index; /* Message_Block array index */ 134 | /* 1024-bit message blocks */ 135 | uint8_t Message_Block[SHA512_Message_Block_Size]; 136 | 137 | int Computed; /* Is the digest computed? */ 138 | int Corrupted; /* Is the digest corrupted? */ 139 | } SHA512Context; 140 | 141 | /* 142 | * This structure will hold context information for the SHA-224 143 | * hashing operation. It uses the SHA-256 structure for computation. 144 | */ 145 | typedef struct SHA256Context SHA224Context; 146 | 147 | /* 148 | * This structure will hold context information for the SHA-384 149 | * hashing operation. It uses the SHA-512 structure for computation. 150 | */ 151 | typedef struct SHA512Context SHA384Context; 152 | 153 | /* 154 | * This structure holds context information for all SHA 155 | * hashing operations. 156 | */ 157 | typedef struct USHAContext 158 | { 159 | int whichSha; /* which SHA is being used */ 160 | union 161 | { 162 | SHA1Context sha1Context; 163 | SHA224Context sha224Context; 164 | SHA256Context sha256Context; 165 | SHA384Context sha384Context; 166 | SHA512Context sha512Context; 167 | } ctx; 168 | } USHAContext; 169 | 170 | /* 171 | * This structure will hold context information for the HMAC 172 | * keyed hashing operation. 173 | */ 174 | typedef struct HMACContext 175 | { 176 | int whichSha; /* which SHA is being used */ 177 | int hashSize; /* hash size of SHA being used */ 178 | int blockSize; /* block size of SHA being used */ 179 | USHAContext shaContext; /* SHA context */ 180 | unsigned char k_opad[USHA_Max_Message_Block_Size]; 181 | /* outer padding - key XORd with opad */ 182 | } HMACContext; 183 | 184 | /* 185 | * Function Prototypes 186 | */ 187 | 188 | /* SHA-1 */ 189 | extern int SHA1Reset (SHA1Context *); 190 | extern int SHA1Input (SHA1Context *, const uint8_t * bytes, 191 | unsigned int bytecount); 192 | extern int SHA1FinalBits (SHA1Context *, const uint8_t bits, 193 | unsigned int bitcount); 194 | extern int SHA1Result (SHA1Context *, uint8_t Message_Digest[SHA1HashSize]); 195 | 196 | /* SHA-224 */ 197 | extern int SHA224Reset (SHA224Context *); 198 | extern int SHA224Input (SHA224Context *, const uint8_t * bytes, 199 | unsigned int bytecount); 200 | extern int SHA224FinalBits (SHA224Context *, const uint8_t bits, 201 | unsigned int bitcount); 202 | extern int SHA224Result (SHA224Context *, 203 | uint8_t Message_Digest[SHA224HashSize]); 204 | 205 | /* SHA-256 */ 206 | extern int SHA256Reset (SHA256Context *); 207 | extern int SHA256Input (SHA256Context *, const uint8_t * bytes, 208 | unsigned int bytecount); 209 | extern int SHA256FinalBits (SHA256Context *, const uint8_t bits, 210 | unsigned int bitcount); 211 | extern int SHA256Result (SHA256Context *, 212 | uint8_t Message_Digest[SHA256HashSize]); 213 | 214 | /* SHA-384 */ 215 | extern int SHA384Reset (SHA384Context *); 216 | extern int SHA384Input (SHA384Context *, const uint8_t * bytes, 217 | unsigned int bytecount); 218 | extern int SHA384FinalBits (SHA384Context *, const uint8_t bits, 219 | unsigned int bitcount); 220 | extern int SHA384Result (SHA384Context *, 221 | uint8_t Message_Digest[SHA384HashSize]); 222 | 223 | /* SHA-512 */ 224 | extern int SHA512Reset (SHA512Context *); 225 | extern int SHA512Input (SHA512Context *, const uint8_t * bytes, 226 | unsigned int bytecount); 227 | extern int SHA512FinalBits (SHA512Context *, const uint8_t bits, 228 | unsigned int bitcount); 229 | extern int SHA512Result (SHA512Context *, 230 | uint8_t Message_Digest[SHA512HashSize]); 231 | 232 | /* Unified SHA functions, chosen by whichSha */ 233 | extern int USHAReset (USHAContext *, SHAversion whichSha); 234 | extern int USHAInput (USHAContext *, 235 | const uint8_t * bytes, unsigned int bytecount); 236 | extern int USHAFinalBits (USHAContext *, 237 | const uint8_t bits, unsigned int bitcount); 238 | extern int USHAResult (USHAContext *, 239 | uint8_t Message_Digest[USHAMaxHashSize]); 240 | extern int USHABlockSize (enum SHAversion whichSha); 241 | extern int USHAHashSize (enum SHAversion whichSha); 242 | extern int USHAHashSizeBits (enum SHAversion whichSha); 243 | 244 | /* 245 | * HMAC Keyed-Hashing for Message Authentication, RFC2104, 246 | * for all SHAs. 247 | * This interface allows a fixed-length text input to be used. 248 | */ 249 | extern int hmac (SHAversion whichSha, /* which SHA algorithm to use */ 250 | const unsigned char *text, /* pointer to data stream */ 251 | int text_len, /* length of data stream */ 252 | const unsigned char *key, /* pointer to authentication key */ 253 | int key_len, /* length of authentication key */ 254 | uint8_t digest[USHAMaxHashSize]); /* caller digest to fill in */ 255 | 256 | /* 257 | * HMAC Keyed-Hashing for Message Authentication, RFC2104, 258 | * for all SHAs. 259 | * This interface allows any length of text input to be used. 260 | */ 261 | extern int hmacReset (HMACContext * ctx, enum SHAversion whichSha, 262 | const unsigned char *key, int key_len); 263 | extern int hmacInput (HMACContext * ctx, const unsigned char *text, 264 | int text_len); 265 | 266 | extern int hmacFinalBits (HMACContext * ctx, const uint8_t bits, 267 | unsigned int bitcount); 268 | extern int hmacResult (HMACContext * ctx, uint8_t digest[USHAMaxHashSize]); 269 | 270 | #endif /* _SHA_H_ */ 271 | -------------------------------------------------------------------------------- /sha1.c: -------------------------------------------------------------------------------- 1 | /**************************** sha1.c ****************************/ 2 | /******************** See RFC 4634 for details ******************/ 3 | /* 4 | * Description: 5 | * This file implements the Secure Hash Signature Standard 6 | * algorithms as defined in the National Institute of Standards 7 | * and Technology Federal Information Processing Standards 8 | * Publication (FIPS PUB) 180-1 published on April 17, 1995, 180-2 9 | * published on August 1, 2002, and the FIPS PUB 180-2 Change 10 | * Notice published on February 28, 2004. 11 | * 12 | * A combined document showing all algorithms is available at 13 | * http://csrc.nist.gov/publications/fips/ 14 | * fips180-2/fips180-2withchangenotice.pdf 15 | * 16 | * The SHA-1 algorithm produces a 160-bit message digest for a 17 | * given data stream. It should take about 2**n steps to find a 18 | * message with the same digest as a given message and 19 | * 2**(n/2) to find any two messages with the same digest, 20 | * when n is the digest size in bits. Therefore, this 21 | * algorithm can serve as a means of providing a 22 | * "fingerprint" for a message. 23 | * 24 | * Portability Issues: 25 | * SHA-1 is defined in terms of 32-bit "words". This code 26 | * uses (included via "sha.h") to define 32 and 8 27 | * bit unsigned integer types. If your C compiler does not 28 | * support 32 bit unsigned integers, this code is not 29 | * appropriate. 30 | * 31 | * Caveats: 32 | * SHA-1 is designed to work with messages less than 2^64 bits 33 | * long. This implementation uses SHA1Input() to hash the bits 34 | * that are a multiple of the size of an 8-bit character, and then 35 | * uses SHA1FinalBits() to hash the final few bits of the input. 36 | */ 37 | 38 | #include "sha.h" 39 | #include "sha-private.h" 40 | 41 | /* 42 | * Define the SHA1 circular left shift macro 43 | */ 44 | #define SHA1_ROTL(bits,word) \ 45 | (((word) << (bits)) | ((word) >> (32-(bits)))) 46 | 47 | /* 48 | * add "length" to the length 49 | */ 50 | static uint32_t addTemp; 51 | #define SHA1AddLength(context, length) \ 52 | (addTemp = (context)->Length_Low, \ 53 | (context)->Corrupted = \ 54 | (((context)->Length_Low += (length)) < addTemp) && \ 55 | (++(context)->Length_High == 0) ? 1 : 0) 56 | 57 | /* Local Function Prototypes */ 58 | static void SHA1Finalize (SHA1Context * context, uint8_t Pad_Byte); 59 | static void SHA1PadMessage (SHA1Context *, uint8_t Pad_Byte); 60 | static void SHA1ProcessMessageBlock (SHA1Context *); 61 | 62 | /* 63 | * SHA1Reset 64 | * 65 | * Description: 66 | * This function will initialize the SHA1Context in preparation 67 | * for computing a new SHA1 message digest. 68 | * 69 | * Parameters: 70 | * context: [in/out] 71 | * The context to reset. 72 | * 73 | * Returns: 74 | * sha Error Code. 75 | * 76 | */ 77 | int 78 | SHA1Reset (SHA1Context * context) 79 | { 80 | if (!context) 81 | return shaNull; 82 | 83 | context->Length_Low = 0; 84 | context->Length_High = 0; 85 | context->Message_Block_Index = 0; 86 | 87 | /* Initial Hash Values: FIPS-180-2 section 5.3.1 */ 88 | context->Intermediate_Hash[0] = 0x67452301; 89 | context->Intermediate_Hash[1] = 0xEFCDAB89; 90 | context->Intermediate_Hash[2] = 0x98BADCFE; 91 | context->Intermediate_Hash[3] = 0x10325476; 92 | context->Intermediate_Hash[4] = 0xC3D2E1F0; 93 | 94 | context->Computed = 0; 95 | context->Corrupted = 0; 96 | 97 | return shaSuccess; 98 | } 99 | 100 | /* 101 | * SHA1Input 102 | * 103 | * Description: 104 | * This function accepts an array of octets as the next portion 105 | * of the message. 106 | * 107 | * Parameters: 108 | * context: [in/out] 109 | * The SHA context to update 110 | * message_array: [in] 111 | * An array of characters representing the next portion of 112 | * the message. 113 | * length: [in] 114 | * The length of the message in message_array 115 | * 116 | * Returns: 117 | * sha Error Code. 118 | * 119 | */ 120 | int 121 | SHA1Input (SHA1Context * context, 122 | const uint8_t * message_array, unsigned length) 123 | { 124 | if (!length) 125 | return shaSuccess; 126 | 127 | if (!context || !message_array) 128 | return shaNull; 129 | 130 | if (context->Computed) 131 | { 132 | context->Corrupted = shaStateError; 133 | return shaStateError; 134 | } 135 | 136 | if (context->Corrupted) 137 | return context->Corrupted; 138 | 139 | while (length-- && !context->Corrupted) 140 | { 141 | context->Message_Block[context->Message_Block_Index++] = 142 | (*message_array & 0xFF); 143 | 144 | if (!SHA1AddLength (context, 8) && 145 | (context->Message_Block_Index == SHA1_Message_Block_Size)) 146 | SHA1ProcessMessageBlock (context); 147 | 148 | message_array++; 149 | } 150 | 151 | return shaSuccess; 152 | } 153 | 154 | /* 155 | * SHA1FinalBits 156 | * 157 | * Description: 158 | * This function will add in any final bits of the message. 159 | * 160 | * Parameters: 161 | * context: [in/out] 162 | * The SHA context to update 163 | * message_bits: [in] 164 | * The final bits of the message, in the upper portion of the 165 | * byte. (Use 0b###00000 instead of 0b00000### to input the 166 | * three bits ###.) 167 | * length: [in] 168 | * The number of bits in message_bits, between 1 and 7. 169 | * 170 | * Returns: 171 | * sha Error Code. 172 | */ 173 | int 174 | SHA1FinalBits (SHA1Context * context, const uint8_t message_bits, 175 | unsigned int length) 176 | { 177 | uint8_t masks[8] = { 178 | /* 0 0b00000000 */ 0x00, /* 1 0b10000000 */ 0x80, 179 | /* 2 0b11000000 */ 0xC0, /* 3 0b11100000 */ 0xE0, 180 | /* 4 0b11110000 */ 0xF0, /* 5 0b11111000 */ 0xF8, 181 | /* 6 0b11111100 */ 0xFC, /* 7 0b11111110 */ 0xFE 182 | }; 183 | uint8_t markbit[8] = { 184 | /* 0 0b10000000 */ 0x80, /* 1 0b01000000 */ 0x40, 185 | /* 2 0b00100000 */ 0x20, /* 3 0b00010000 */ 0x10, 186 | /* 4 0b00001000 */ 0x08, /* 5 0b00000100 */ 0x04, 187 | /* 6 0b00000010 */ 0x02, /* 7 0b00000001 */ 0x01 188 | }; 189 | 190 | if (!length) 191 | return shaSuccess; 192 | 193 | if (!context) 194 | return shaNull; 195 | 196 | if (context->Computed || (length >= 8) || (length == 0)) 197 | { 198 | context->Corrupted = shaStateError; 199 | return shaStateError; 200 | } 201 | 202 | if (context->Corrupted) 203 | return context->Corrupted; 204 | 205 | SHA1AddLength (context, length); 206 | SHA1Finalize (context, 207 | (uint8_t) ((message_bits & masks[length]) | markbit[length])); 208 | 209 | return shaSuccess; 210 | } 211 | 212 | /* 213 | * SHA1Result 214 | * 215 | * Description: 216 | * This function will return the 160-bit message digest into the 217 | * Message_Digest array provided by the caller. 218 | * NOTE: The first octet of hash is stored in the 0th element, 219 | * the last octet of hash in the 19th element. 220 | * 221 | * Parameters: 222 | * context: [in/out] 223 | * The context to use to calculate the SHA-1 hash. 224 | * Message_Digest: [out] 225 | * Where the digest is returned. 226 | * 227 | * Returns: 228 | * sha Error Code. 229 | * 230 | */ 231 | int 232 | SHA1Result (SHA1Context * context, uint8_t Message_Digest[SHA1HashSize]) 233 | { 234 | int i; 235 | 236 | if (!context || !Message_Digest) 237 | return shaNull; 238 | 239 | if (context->Corrupted) 240 | return context->Corrupted; 241 | 242 | if (!context->Computed) 243 | SHA1Finalize (context, 0x80); 244 | 245 | for (i = 0; i < SHA1HashSize; ++i) 246 | Message_Digest[i] = (uint8_t) (context->Intermediate_Hash[i >> 2] 247 | >> 8 * (3 - (i & 0x03))); 248 | 249 | return shaSuccess; 250 | } 251 | 252 | /* 253 | * SHA1Finalize 254 | * 255 | * Description: 256 | * This helper function finishes off the digest calculations. 257 | * 258 | * Parameters: 259 | * context: [in/out] 260 | * The SHA context to update 261 | * Pad_Byte: [in] 262 | * The last byte to add to the digest before the 0-padding 263 | * and length. This will contain the last bits of the message 264 | * followed by another single bit. If the message was an 265 | * exact multiple of 8-bits long, Pad_Byte will be 0x80. 266 | * 267 | * Returns: 268 | * sha Error Code. 269 | * 270 | */ 271 | static void 272 | SHA1Finalize (SHA1Context * context, uint8_t Pad_Byte) 273 | { 274 | int i; 275 | SHA1PadMessage (context, Pad_Byte); 276 | /* message may be sensitive, clear it out */ 277 | for (i = 0; i < SHA1_Message_Block_Size; ++i) 278 | context->Message_Block[i] = 0; 279 | context->Length_Low = 0; /* and clear length */ 280 | context->Length_High = 0; 281 | context->Computed = 1; 282 | } 283 | 284 | /* 285 | * SHA1PadMessage 286 | * 287 | * Description: 288 | * According to the standard, the message must be padded to an 289 | * even 512 bits. The first padding bit must be a '1'. The last 290 | * 64 bits represent the length of the original message. All bits 291 | * in between should be 0. This helper function will pad the 292 | * message according to those rules by filling the Message_Block 293 | * array accordingly. When it returns, it can be assumed that the 294 | * message digest has been computed. 295 | * 296 | * Parameters: 297 | * context: [in/out] 298 | * The context to pad 299 | * Pad_Byte: [in] 300 | * The last byte to add to the digest before the 0-padding 301 | * and length. This will contain the last bits of the message 302 | * followed by another single bit. If the message was an 303 | * exact multiple of 8-bits long, Pad_Byte will be 0x80. 304 | * 305 | * Returns: 306 | * Nothing. 307 | */ 308 | static void 309 | SHA1PadMessage (SHA1Context * context, uint8_t Pad_Byte) 310 | { 311 | /* 312 | * Check to see if the current message block is too small to hold 313 | * the initial padding bits and length. If so, we will pad the 314 | * block, process it, and then continue padding into a second 315 | * block. 316 | */ 317 | if (context->Message_Block_Index >= (SHA1_Message_Block_Size - 8)) 318 | { 319 | context->Message_Block[context->Message_Block_Index++] = Pad_Byte; 320 | while (context->Message_Block_Index < SHA1_Message_Block_Size) 321 | context->Message_Block[context->Message_Block_Index++] = 0; 322 | 323 | SHA1ProcessMessageBlock (context); 324 | } 325 | else 326 | context->Message_Block[context->Message_Block_Index++] = Pad_Byte; 327 | 328 | while (context->Message_Block_Index < (SHA1_Message_Block_Size - 8)) 329 | context->Message_Block[context->Message_Block_Index++] = 0; 330 | 331 | /* 332 | * Store the message length as the last 8 octets 333 | */ 334 | context->Message_Block[56] = (uint8_t) (context->Length_High >> 24); 335 | context->Message_Block[57] = (uint8_t) (context->Length_High >> 16); 336 | context->Message_Block[58] = (uint8_t) (context->Length_High >> 8); 337 | context->Message_Block[59] = (uint8_t) (context->Length_High); 338 | context->Message_Block[60] = (uint8_t) (context->Length_Low >> 24); 339 | context->Message_Block[61] = (uint8_t) (context->Length_Low >> 16); 340 | context->Message_Block[62] = (uint8_t) (context->Length_Low >> 8); 341 | context->Message_Block[63] = (uint8_t) (context->Length_Low); 342 | 343 | SHA1ProcessMessageBlock (context); 344 | } 345 | 346 | /* 347 | * SHA1ProcessMessageBlock 348 | * 349 | * Description: 350 | * This helper function will process the next 512 bits of the 351 | * message stored in the Message_Block array. 352 | * 353 | * Parameters: 354 | * None. 355 | * 356 | * Returns: 357 | * Nothing. 358 | * 359 | * Comments: 360 | * Many of the variable names in this code, especially the 361 | * single character names, were used because those were the 362 | * names used in the publication. 363 | */ 364 | static void 365 | SHA1ProcessMessageBlock (SHA1Context * context) 366 | { 367 | /* Constants defined in FIPS-180-2, section 4.2.1 */ 368 | const uint32_t K[4] = { 369 | 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6 370 | }; 371 | int t; /* Loop counter */ 372 | uint32_t temp; /* Temporary word value */ 373 | uint32_t W[80]; /* Word sequence */ 374 | uint32_t A, B, C, D, E; /* Word buffers */ 375 | 376 | /* 377 | * Initialize the first 16 words in the array W 378 | */ 379 | for (t = 0; t < 16; t++) 380 | { 381 | W[t] = ((uint32_t) context->Message_Block[t * 4]) << 24; 382 | W[t] |= ((uint32_t) context->Message_Block[t * 4 + 1]) << 16; 383 | W[t] |= ((uint32_t) context->Message_Block[t * 4 + 2]) << 8; 384 | W[t] |= ((uint32_t) context->Message_Block[t * 4 + 3]); 385 | } 386 | for (t = 16; t < 80; t++) 387 | W[t] = SHA1_ROTL (1, W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16]); 388 | 389 | A = context->Intermediate_Hash[0]; 390 | B = context->Intermediate_Hash[1]; 391 | C = context->Intermediate_Hash[2]; 392 | D = context->Intermediate_Hash[3]; 393 | E = context->Intermediate_Hash[4]; 394 | 395 | for (t = 0; t < 20; t++) 396 | { 397 | temp = SHA1_ROTL (5, A) + SHA_Ch (B, C, D) + E + W[t] + K[0]; 398 | E = D; 399 | D = C; 400 | C = SHA1_ROTL (30, B); 401 | B = A; 402 | A = temp; 403 | } 404 | 405 | for (t = 20; t < 40; t++) 406 | { 407 | temp = SHA1_ROTL (5, A) + SHA_Parity (B, C, D) + E + W[t] + K[1]; 408 | E = D; 409 | D = C; 410 | C = SHA1_ROTL (30, B); 411 | B = A; 412 | A = temp; 413 | } 414 | 415 | for (t = 40; t < 60; t++) 416 | { 417 | temp = SHA1_ROTL (5, A) + SHA_Maj (B, C, D) + E + W[t] + K[2]; 418 | E = D; 419 | D = C; 420 | C = SHA1_ROTL (30, B); 421 | B = A; 422 | A = temp; 423 | } 424 | 425 | for (t = 60; t < 80; t++) 426 | { 427 | temp = SHA1_ROTL (5, A) + SHA_Parity (B, C, D) + E + W[t] + K[3]; 428 | E = D; 429 | D = C; 430 | C = SHA1_ROTL (30, B); 431 | B = A; 432 | A = temp; 433 | } 434 | 435 | context->Intermediate_Hash[0] += A; 436 | context->Intermediate_Hash[1] += B; 437 | context->Intermediate_Hash[2] += C; 438 | context->Intermediate_Hash[3] += D; 439 | context->Intermediate_Hash[4] += E; 440 | 441 | context->Message_Block_Index = 0; 442 | } 443 | -------------------------------------------------------------------------------- /sha224-256.c: -------------------------------------------------------------------------------- 1 | /*************************** sha224-256.c ***************************/ 2 | /********************* See RFC 4634 for details *********************/ 3 | /* 4 | * Description: 5 | * This file implements the Secure Hash Signature Standard 6 | * algorithms as defined in the National Institute of Standards 7 | * and Technology Federal Information Processing Standards 8 | * Publication (FIPS PUB) 180-1 published on April 17, 1995, 180-2 9 | * published on August 1, 2002, and the FIPS PUB 180-2 Change 10 | * Notice published on February 28, 2004. 11 | * 12 | * A combined document showing all algorithms is available at 13 | * http://csrc.nist.gov/publications/fips/ 14 | * fips180-2/fips180-2withchangenotice.pdf 15 | * 16 | * The SHA-224 and SHA-256 algorithms produce 224-bit and 256-bit 17 | * message digests for a given data stream. It should take about 18 | * 2**n steps to find a message with the same digest as a given 19 | * message and 2**(n/2) to find any two messages with the same 20 | * digest, when n is the digest size in bits. Therefore, this 21 | * algorithm can serve as a means of providing a 22 | * "fingerprint" for a message. 23 | * 24 | * Portability Issues: 25 | * SHA-224 and SHA-256 are defined in terms of 32-bit "words". 26 | * This code uses (included via "sha.h") to define 32 27 | * and 8 bit unsigned integer types. If your C compiler does not 28 | * support 32 bit unsigned integers, this code is not 29 | * appropriate. 30 | * 31 | * Caveats: 32 | * SHA-224 and SHA-256 are designed to work with messages less 33 | * than 2^64 bits long. This implementation uses SHA224/256Input() 34 | * to hash the bits that are a multiple of the size of an 8-bit 35 | * character, and then uses SHA224/256FinalBits() to hash the 36 | * final few bits of the input. 37 | */ 38 | 39 | #include "sha.h" 40 | #include "sha-private.h" 41 | /* Define the SHA shift, rotate left and rotate right macro */ 42 | #define SHA256_SHR(bits,word) ((word) >> (bits)) 43 | #define SHA256_ROTL(bits,word) \ 44 | (((word) << (bits)) | ((word) >> (32-(bits)))) 45 | #define SHA256_ROTR(bits,word) \ 46 | (((word) >> (bits)) | ((word) << (32-(bits)))) 47 | 48 | /* Define the SHA SIGMA and sigma macros */ 49 | #define SHA256_SIGMA0(word) \ 50 | (SHA256_ROTR( 2,word) ^ SHA256_ROTR(13,word) ^ SHA256_ROTR(22,word)) 51 | #define SHA256_SIGMA1(word) \ 52 | (SHA256_ROTR( 6,word) ^ SHA256_ROTR(11,word) ^ SHA256_ROTR(25,word)) 53 | #define SHA256_sigma0(word) \ 54 | (SHA256_ROTR( 7,word) ^ SHA256_ROTR(18,word) ^ SHA256_SHR( 3,word)) 55 | #define SHA256_sigma1(word) \ 56 | (SHA256_ROTR(17,word) ^ SHA256_ROTR(19,word) ^ SHA256_SHR(10,word)) 57 | 58 | /* 59 | * add "length" to the length 60 | */ 61 | static uint32_t addTemp; 62 | #define SHA224_256AddLength(context, length) \ 63 | (addTemp = (context)->Length_Low, (context)->Corrupted = \ 64 | (((context)->Length_Low += (length)) < addTemp) && \ 65 | (++(context)->Length_High == 0) ? 1 : 0) 66 | 67 | /* Local Function Prototypes */ 68 | static void SHA224_256Finalize (SHA256Context * context, uint8_t Pad_Byte); 69 | static void SHA224_256PadMessage (SHA256Context * context, uint8_t Pad_Byte); 70 | static void SHA224_256ProcessMessageBlock (SHA256Context * context); 71 | static int SHA224_256Reset (SHA256Context * context, uint32_t * H0); 72 | static int SHA224_256ResultN (SHA256Context * context, 73 | uint8_t Message_Digest[], int HashSize); 74 | 75 | /* Initial Hash Values: FIPS-180-2 Change Notice 1 */ 76 | static uint32_t SHA224_H0[SHA256HashSize / 4] = { 77 | 0xC1059ED8, 0x367CD507, 0x3070DD17, 0xF70E5939, 78 | 0xFFC00B31, 0x68581511, 0x64F98FA7, 0xBEFA4FA4 79 | }; 80 | 81 | /* Initial Hash Values: FIPS-180-2 section 5.3.2 */ 82 | static uint32_t SHA256_H0[SHA256HashSize / 4] = { 83 | 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 84 | 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 85 | }; 86 | 87 | /* 88 | * SHA224Reset 89 | * 90 | * Description: 91 | * This function will initialize the SHA384Context in preparation 92 | * for computing a new SHA224 message digest. 93 | * 94 | * Parameters: 95 | * context: [in/out] 96 | * The context to reset. 97 | * 98 | * Returns: 99 | * sha Error Code. 100 | */ 101 | int 102 | SHA224Reset (SHA224Context * context) 103 | { 104 | return SHA224_256Reset (context, SHA224_H0); 105 | } 106 | 107 | /* 108 | * SHA224Input 109 | * 110 | * Description: 111 | * This function accepts an array of octets as the next portion 112 | * of the message. 113 | * 114 | * Parameters: 115 | * context: [in/out] 116 | * The SHA context to update 117 | * message_array: [in] 118 | * An array of characters representing the next portion of 119 | * the message. 120 | * length: [in] 121 | * The length of the message in message_array 122 | * 123 | * Returns: 124 | * sha Error Code. 125 | * 126 | */ 127 | int 128 | SHA224Input (SHA224Context * context, const uint8_t * message_array, 129 | unsigned int length) 130 | { 131 | return SHA256Input (context, message_array, length); 132 | } 133 | 134 | /* 135 | * SHA224FinalBits 136 | * 137 | * Description: 138 | * This function will add in any final bits of the message. 139 | * 140 | * Parameters: 141 | * context: [in/out] 142 | * The SHA context to update 143 | * message_bits: [in] 144 | * The final bits of the message, in the upper portion of the 145 | * byte. (Use 0b###00000 instead of 0b00000### to input the 146 | * three bits ###.) 147 | * length: [in] 148 | * The number of bits in message_bits, between 1 and 7. 149 | * 150 | * Returns: 151 | * sha Error Code. 152 | */ 153 | int 154 | SHA224FinalBits (SHA224Context * context, 155 | const uint8_t message_bits, unsigned int length) 156 | { 157 | return SHA256FinalBits (context, message_bits, length); 158 | } 159 | 160 | /* 161 | * SHA224Result 162 | * 163 | * Description: 164 | * This function will return the 224-bit message 165 | * digest into the Message_Digest array provided by the caller. 166 | * NOTE: The first octet of hash is stored in the 0th element, 167 | * the last octet of hash in the 28th element. 168 | * 169 | * Parameters: 170 | * context: [in/out] 171 | * The context to use to calculate the SHA hash. 172 | * Message_Digest: [out] 173 | * Where the digest is returned. 174 | * 175 | * Returns: 176 | * sha Error Code. 177 | */ 178 | int 179 | SHA224Result (SHA224Context * context, uint8_t Message_Digest[SHA224HashSize]) 180 | { 181 | return SHA224_256ResultN (context, Message_Digest, SHA224HashSize); 182 | } 183 | 184 | /* 185 | * SHA256Reset 186 | * 187 | * Description: 188 | * This function will initialize the SHA256Context in preparation 189 | * for computing a new SHA256 message digest. 190 | * 191 | * Parameters: 192 | * context: [in/out] 193 | * The context to reset. 194 | * 195 | * Returns: 196 | * sha Error Code. 197 | */ 198 | int 199 | SHA256Reset (SHA256Context * context) 200 | { 201 | return SHA224_256Reset (context, SHA256_H0); 202 | } 203 | 204 | /* 205 | * SHA256Input 206 | * 207 | * Description: 208 | * This function accepts an array of octets as the next portion 209 | * of the message. 210 | * 211 | * Parameters: 212 | * context: [in/out] 213 | * The SHA context to update 214 | * message_array: [in] 215 | * An array of characters representing the next portion of 216 | * the message. 217 | * length: [in] 218 | * The length of the message in message_array 219 | * 220 | * Returns: 221 | * sha Error Code. 222 | */ 223 | int 224 | SHA256Input (SHA256Context * context, const uint8_t * message_array, 225 | unsigned int length) 226 | { 227 | if (!length) 228 | return shaSuccess; 229 | 230 | if (!context || !message_array) 231 | return shaNull; 232 | 233 | if (context->Computed) 234 | { 235 | context->Corrupted = shaStateError; 236 | return shaStateError; 237 | } 238 | 239 | if (context->Corrupted) 240 | return context->Corrupted; 241 | 242 | while (length-- && !context->Corrupted) 243 | { 244 | context->Message_Block[context->Message_Block_Index++] = 245 | (*message_array & 0xFF); 246 | 247 | if (!SHA224_256AddLength (context, 8) && 248 | (context->Message_Block_Index == SHA256_Message_Block_Size)) 249 | SHA224_256ProcessMessageBlock (context); 250 | 251 | message_array++; 252 | } 253 | 254 | return shaSuccess; 255 | 256 | } 257 | 258 | /* 259 | * SHA256FinalBits 260 | * 261 | * Description: 262 | * This function will add in any final bits of the message. 263 | * 264 | * Parameters: 265 | * context: [in/out] 266 | * The SHA context to update 267 | * message_bits: [in] 268 | * The final bits of the message, in the upper portion of the 269 | * byte. (Use 0b###00000 instead of 0b00000### to input the 270 | * three bits ###.) 271 | * length: [in] 272 | * The number of bits in message_bits, between 1 and 7. 273 | * 274 | * Returns: 275 | * sha Error Code. 276 | */ 277 | int 278 | SHA256FinalBits (SHA256Context * context, 279 | const uint8_t message_bits, unsigned int length) 280 | { 281 | uint8_t masks[8] = { 282 | /* 0 0b00000000 */ 0x00, /* 1 0b10000000 */ 0x80, 283 | /* 2 0b11000000 */ 0xC0, /* 3 0b11100000 */ 0xE0, 284 | /* 4 0b11110000 */ 0xF0, /* 5 0b11111000 */ 0xF8, 285 | /* 6 0b11111100 */ 0xFC, /* 7 0b11111110 */ 0xFE 286 | }; 287 | uint8_t markbit[8] = { 288 | /* 0 0b10000000 */ 0x80, /* 1 0b01000000 */ 0x40, 289 | /* 2 0b00100000 */ 0x20, /* 3 0b00010000 */ 0x10, 290 | /* 4 0b00001000 */ 0x08, /* 5 0b00000100 */ 0x04, 291 | /* 6 0b00000010 */ 0x02, /* 7 0b00000001 */ 0x01 292 | }; 293 | 294 | if (!length) 295 | return shaSuccess; 296 | 297 | if (!context) 298 | return shaNull; 299 | 300 | if ((context->Computed) || (length >= 8) || (length == 0)) 301 | { 302 | context->Corrupted = shaStateError; 303 | return shaStateError; 304 | } 305 | 306 | if (context->Corrupted) 307 | return context->Corrupted; 308 | 309 | SHA224_256AddLength (context, length); 310 | SHA224_256Finalize (context, (uint8_t) 311 | ((message_bits & masks[length]) | markbit[length])); 312 | 313 | return shaSuccess; 314 | } 315 | 316 | /* 317 | * SHA256Result 318 | * 319 | * Description: 320 | * This function will return the 256-bit message 321 | * digest into the Message_Digest array provided by the caller. 322 | * NOTE: The first octet of hash is stored in the 0th element, 323 | * the last octet of hash in the 32nd element. 324 | * 325 | * Parameters: 326 | * context: [in/out] 327 | * The context to use to calculate the SHA hash. 328 | * Message_Digest: [out] 329 | * Where the digest is returned. 330 | * 331 | * Returns: 332 | * sha Error Code. 333 | */ 334 | int 335 | SHA256Result (SHA256Context * context, uint8_t Message_Digest[]) 336 | { 337 | return SHA224_256ResultN (context, Message_Digest, SHA256HashSize); 338 | } 339 | 340 | /* 341 | * SHA224_256Finalize 342 | * 343 | * Description: 344 | * This helper function finishes off the digest calculations. 345 | * 346 | * Parameters: 347 | * context: [in/out] 348 | * The SHA context to update 349 | * Pad_Byte: [in] 350 | * The last byte to add to the digest before the 0-padding 351 | * and length. This will contain the last bits of the message 352 | * followed by another single bit. If the message was an 353 | * exact multiple of 8-bits long, Pad_Byte will be 0x80. 354 | * 355 | * Returns: 356 | * sha Error Code. 357 | */ 358 | static void 359 | SHA224_256Finalize (SHA256Context * context, uint8_t Pad_Byte) 360 | { 361 | int i; 362 | SHA224_256PadMessage (context, Pad_Byte); 363 | /* message may be sensitive, so clear it out */ 364 | for (i = 0; i < SHA256_Message_Block_Size; ++i) 365 | context->Message_Block[i] = 0; 366 | context->Length_Low = 0; /* and clear length */ 367 | context->Length_High = 0; 368 | context->Computed = 1; 369 | } 370 | 371 | /* 372 | * SHA224_256PadMessage 373 | * 374 | * Description: 375 | * According to the standard, the message must be padded to an 376 | * even 512 bits. The first padding bit must be a '1'. The 377 | * last 64 bits represent the length of the original message. 378 | * All bits in between should be 0. This helper function will pad 379 | * the message according to those rules by filling the 380 | * Message_Block array accordingly. When it returns, it can be 381 | * assumed that the message digest has been computed. 382 | * 383 | * Parameters: 384 | * context: [in/out] 385 | * The context to pad 386 | * Pad_Byte: [in] 387 | * The last byte to add to the digest before the 0-padding 388 | * and length. This will contain the last bits of the message 389 | * followed by another single bit. If the message was an 390 | * exact multiple of 8-bits long, Pad_Byte will be 0x80. 391 | * 392 | * Returns: 393 | * Nothing. 394 | */ 395 | static void 396 | SHA224_256PadMessage (SHA256Context * context, uint8_t Pad_Byte) 397 | { 398 | /* 399 | * Check to see if the current message block is too small to hold 400 | * the initial padding bits and length. If so, we will pad the 401 | * block, process it, and then continue padding into a second 402 | * block. 403 | */ 404 | if (context->Message_Block_Index >= (SHA256_Message_Block_Size - 8)) 405 | { 406 | context->Message_Block[context->Message_Block_Index++] = Pad_Byte; 407 | while (context->Message_Block_Index < SHA256_Message_Block_Size) 408 | context->Message_Block[context->Message_Block_Index++] = 0; 409 | SHA224_256ProcessMessageBlock (context); 410 | } 411 | else 412 | context->Message_Block[context->Message_Block_Index++] = Pad_Byte; 413 | 414 | while (context->Message_Block_Index < (SHA256_Message_Block_Size - 8)) 415 | context->Message_Block[context->Message_Block_Index++] = 0; 416 | 417 | /* 418 | * Store the message length as the last 8 octets 419 | */ 420 | context->Message_Block[56] = (uint8_t) (context->Length_High >> 24); 421 | context->Message_Block[57] = (uint8_t) (context->Length_High >> 16); 422 | context->Message_Block[58] = (uint8_t) (context->Length_High >> 8); 423 | context->Message_Block[59] = (uint8_t) (context->Length_High); 424 | context->Message_Block[60] = (uint8_t) (context->Length_Low >> 24); 425 | context->Message_Block[61] = (uint8_t) (context->Length_Low >> 16); 426 | context->Message_Block[62] = (uint8_t) (context->Length_Low >> 8); 427 | context->Message_Block[63] = (uint8_t) (context->Length_Low); 428 | 429 | SHA224_256ProcessMessageBlock (context); 430 | } 431 | 432 | /* 433 | * SHA224_256ProcessMessageBlock 434 | * 435 | * Description: 436 | * This function will process the next 512 bits of the message 437 | * stored in the Message_Block array. 438 | * 439 | * Parameters: 440 | * context: [in/out] 441 | * The SHA context to update 442 | * 443 | * Returns: 444 | * Nothing. 445 | * 446 | * Comments: 447 | * Many of the variable names in this code, especially the 448 | * single character names, were used because those were the 449 | * names used in the publication. 450 | */ 451 | static void 452 | SHA224_256ProcessMessageBlock (SHA256Context * context) 453 | { 454 | /* Constants defined in FIPS-180-2, section 4.2.2 */ 455 | static const uint32_t K[64] = { 456 | 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 457 | 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 458 | 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 459 | 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 460 | 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 461 | 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 462 | 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 463 | 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 464 | 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 465 | 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 466 | 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 467 | 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 468 | 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 469 | }; 470 | int t, t4; /* Loop counter */ 471 | uint32_t temp1, temp2; /* Temporary word value */ 472 | uint32_t W[64]; /* Word sequence */ 473 | uint32_t A, B, C, D, E, F, G, H; /* Word buffers */ 474 | 475 | /* 476 | * Initialize the first 16 words in the array W 477 | */ 478 | for (t = t4 = 0; t < 16; t++, t4 += 4) 479 | W[t] = (((uint32_t) context->Message_Block[t4]) << 24) | 480 | (((uint32_t) context->Message_Block[t4 + 1]) << 16) | 481 | (((uint32_t) context->Message_Block[t4 + 2]) << 8) | 482 | (((uint32_t) context->Message_Block[t4 + 3])); 483 | 484 | for (t = 16; t < 64; t++) 485 | W[t] = SHA256_sigma1 (W[t - 2]) + W[t - 7] + 486 | SHA256_sigma0 (W[t - 15]) + W[t - 16]; 487 | 488 | A = context->Intermediate_Hash[0]; 489 | B = context->Intermediate_Hash[1]; 490 | C = context->Intermediate_Hash[2]; 491 | D = context->Intermediate_Hash[3]; 492 | E = context->Intermediate_Hash[4]; 493 | F = context->Intermediate_Hash[5]; 494 | G = context->Intermediate_Hash[6]; 495 | H = context->Intermediate_Hash[7]; 496 | 497 | for (t = 0; t < 64; t++) 498 | { 499 | temp1 = H + SHA256_SIGMA1 (E) + SHA_Ch (E, F, G) + K[t] + W[t]; 500 | temp2 = SHA256_SIGMA0 (A) + SHA_Maj (A, B, C); 501 | H = G; 502 | G = F; 503 | F = E; 504 | E = D + temp1; 505 | D = C; 506 | C = B; 507 | B = A; 508 | A = temp1 + temp2; 509 | } 510 | 511 | context->Intermediate_Hash[0] += A; 512 | context->Intermediate_Hash[1] += B; 513 | context->Intermediate_Hash[2] += C; 514 | context->Intermediate_Hash[3] += D; 515 | context->Intermediate_Hash[4] += E; 516 | context->Intermediate_Hash[5] += F; 517 | context->Intermediate_Hash[6] += G; 518 | context->Intermediate_Hash[7] += H; 519 | 520 | context->Message_Block_Index = 0; 521 | } 522 | 523 | /* 524 | * SHA224_256Reset 525 | * 526 | * Description: 527 | * This helper function will initialize the SHA256Context in 528 | * preparation for computing a new SHA256 message digest. 529 | * 530 | * Parameters: 531 | * context: [in/out] 532 | * The context to reset. 533 | * H0 534 | * The initial hash value to use. 535 | * 536 | * Returns: 537 | * sha Error Code. 538 | */ 539 | static int 540 | SHA224_256Reset (SHA256Context * context, uint32_t * H0) 541 | { 542 | if (!context) 543 | return shaNull; 544 | 545 | context->Length_Low = 0; 546 | context->Length_High = 0; 547 | context->Message_Block_Index = 0; 548 | 549 | context->Intermediate_Hash[0] = H0[0]; 550 | context->Intermediate_Hash[1] = H0[1]; 551 | context->Intermediate_Hash[2] = H0[2]; 552 | context->Intermediate_Hash[3] = H0[3]; 553 | context->Intermediate_Hash[4] = H0[4]; 554 | context->Intermediate_Hash[5] = H0[5]; 555 | context->Intermediate_Hash[6] = H0[6]; 556 | context->Intermediate_Hash[7] = H0[7]; 557 | 558 | context->Computed = 0; 559 | context->Corrupted = 0; 560 | 561 | return shaSuccess; 562 | } 563 | 564 | /* 565 | * SHA224_256ResultN 566 | * 567 | * Description: 568 | * This helper function will return the 224-bit or 256-bit message 569 | * digest into the Message_Digest array provided by the caller. 570 | * NOTE: The first octet of hash is stored in the 0th element, 571 | * the last octet of hash in the 28th/32nd element. 572 | * 573 | * Parameters: 574 | * context: [in/out] 575 | * The context to use to calculate the SHA hash. 576 | * Message_Digest: [out] 577 | * Where the digest is returned. 578 | * HashSize: [in] 579 | * The size of the hash, either 28 or 32. 580 | * 581 | * Returns: 582 | * sha Error Code. 583 | */ 584 | static int 585 | SHA224_256ResultN (SHA256Context * context, 586 | uint8_t Message_Digest[], int HashSize) 587 | { 588 | int i; 589 | 590 | if (!context || !Message_Digest) 591 | return shaNull; 592 | 593 | if (context->Corrupted) 594 | return context->Corrupted; 595 | 596 | if (!context->Computed) 597 | SHA224_256Finalize (context, 0x80); 598 | 599 | for (i = 0; i < HashSize; ++i) 600 | Message_Digest[i] = (uint8_t) 601 | (context->Intermediate_Hash[i >> 2] >> 8 * (3 - (i & 0x03))); 602 | 603 | return shaSuccess; 604 | } 605 | -------------------------------------------------------------------------------- /tests/Makefile.am: -------------------------------------------------------------------------------- 1 | # Written by Simon Josefsson . 2 | # Copyright (c) 2011-2013 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 | # Self tests. 30 | 31 | AUTOMAKE_OPTIONS = subdir-objects 32 | AM_LDFLAGS = -no-install 33 | AM_CFLAGS=-I$(srcdir)/.. $(WARN_CFLAGS) 34 | LDADD = ../libykclient.la 35 | selftest_SOURCES = selftest.c 36 | selftest_SOURCES+= ../hmac.c ../cencode.c ../cdecode.c ../usha.c \ 37 | ../sha384-512.c ../sha224-256.c ../sha1.c 38 | 39 | check_PROGRAMS = selftest 40 | TESTS = $(check_PROGRAMS) 41 | 42 | TESTS_ENVIRONMENT = $(VALGRIND) 43 | -------------------------------------------------------------------------------- /tests/selftest.c: -------------------------------------------------------------------------------- 1 | /* selftest.c --- Self-tests for Yubico client library. 2 | * 3 | * Written by Simon Josefsson . 4 | * Copyright (c) 2006-2013 Yubico AB 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are 9 | * met: 10 | * 11 | * * Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * * Redistributions in binary form must reproduce the above 15 | * copyright notice, this list of conditions and the following 16 | * disclaimer in the documentation and/or other materials provided 17 | * with the distribution. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | * 31 | */ 32 | 33 | #include "ykclient.h" 34 | #include "sha.h" 35 | #include "cencode.h" 36 | #include "cdecode.h" 37 | 38 | #include 39 | #include 40 | 41 | #define TEST(xX) printf ("\nTest %s:%d (%s): ", __FILE__, __LINE__, __FUNCTION__); \ 42 | printf xX; \ 43 | printf ("\n") 44 | 45 | void 46 | test_base64 (void) 47 | { 48 | base64_encodestate encode; 49 | base64_decodestate decode; 50 | char b64dig[64]; 51 | char buf[64]; 52 | int size1, size2; 53 | int ret; 54 | 55 | TEST(("test base64 encoding")); 56 | base64_init_encodestate(&encode); 57 | size1 = base64_encode_block("foo", 3, b64dig, &encode); 58 | size2 = base64_encode_blockend(&b64dig[size1], &encode); 59 | b64dig[size1 + size2 - 1] = '\0'; 60 | 61 | printf("b64 encode: %s, expected: Zm9v\n", b64dig); 62 | ret = strcmp(b64dig, "Zm9v"); 63 | assert(ret == 0); 64 | 65 | TEST(("test base64 decoding")); 66 | base64_init_decodestate(&decode); 67 | base64_decode_block ("YmxhaG9uZ2E=", 12, buf, &decode); 68 | 69 | printf("b64 decode: %s, expexted: blahonga\n", buf); 70 | ret = strcmp(buf, "blahonga"); 71 | assert(ret == 0); 72 | } 73 | 74 | /* test cases for HMAC-SHA1 from rfc 2202 */ 75 | void 76 | test_hmac (void) 77 | { 78 | int res; 79 | uint8_t result[USHAMaxHashSize]; 80 | 81 | unsigned char text1[] = "Hi There"; 82 | unsigned char key1[20]; 83 | memset(key1, 0x0b, 20); 84 | uint8_t expected1[] = {0xb6, 0x17, 0x31, 0x86, 0x55, 0x05, 0x72, 0x64, 0xe2, 85 | 0x8b, 0xc0, 0xb6, 0xfb, 0x37, 0x8c, 0x8e, 0xf1, 0x46, 86 | 0xbe, 0x00}; 87 | uint8_t expected1_sha224[] = {0x89, 0x6f, 0xb1, 0x12, 0x8a, 0xbb, 0xdf, 0x19, 88 | 0x68, 0x32, 0x10, 0x7c, 0xd4, 0x9d, 0xf3, 0x3f, 89 | 0x47, 0xb4, 0xb1, 0x16, 0x99, 0x12, 0xba, 0x4f, 90 | 0x53, 0x68, 0x4b, 0x22}; 91 | uint8_t expected1_sha256[] = {0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 92 | 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0x0b, 0xf1, 0x2b, 93 | 0x88, 0x1d, 0xc2, 0x00, 0xc9, 0x83, 0x3d, 0xa7, 94 | 0x26, 0xe9, 0x37, 0x6c, 0x2e, 0x32, 0xcf, 0xf7}; 95 | uint8_t expected1_sha384[] = {0xaf, 0xd0, 0x39, 0x44, 0xd8, 0x48, 0x95, 0x62, 96 | 0x6b, 0x08, 0x25, 0xf4, 0xab, 0x46, 0x90, 0x7f, 97 | 0x15, 0xf9, 0xda, 0xdb, 0xe4, 0x10, 0x1e, 0xc6, 98 | 0x82, 0xaa, 0x03, 0x4c, 0x7c, 0xeb, 0xc5, 0x9c, 99 | 0xfa, 0xea, 0x9e, 0xa9, 0x07, 0x6e, 0xde, 0x7f, 100 | 0x4a, 0xf1, 0x52, 0xe8, 0xb2, 0xfa, 0x9c, 0xb6}; 101 | uint8_t expected1_sha512[] = {0x87, 0xaa, 0x7c, 0xde, 0xa5, 0xef, 0x61, 0x9d, 102 | 0x4f, 0xf0, 0xb4, 0x24, 0x1a, 0x1d, 0x6c, 0xb0, 103 | 0x23, 0x79, 0xf4, 0xe2, 0xce, 0x4e, 0xc2, 0x78, 104 | 0x7a, 0xd0, 0xb3, 0x05, 0x45, 0xe1, 0x7c, 0xde, 105 | 0xda, 0xa8, 0x33, 0xb7, 0xd6, 0xb8, 0xa7, 0x02, 106 | 0x03, 0x8b, 0x27, 0x4e, 0xae, 0xa3, 0xf4, 0xe4, 107 | 0xbe, 0x9d, 0x91, 0x4e, 0xeb, 0x61, 0xf1, 0x70, 108 | 0x2e, 0x69, 0x6c, 0x20, 0x3a, 0x12, 0x68, 0x54}; 109 | 110 | unsigned char text2[] = "what do ya want for nothing?"; 111 | unsigned char key2[] = "Jefe"; 112 | uint8_t expected2[] = {0xef, 0xfc, 0xdf, 0x6a, 0xe5, 0xeb, 0x2f, 0xa2, 0xd2, 113 | 0x74, 0x16, 0xd5, 0xf1, 0x84, 0xdf, 0x9c, 0x25, 0x9a, 114 | 0x7c, 0x79}; 115 | uint8_t expected2_sha224[] = {0xa3, 0x0e, 0x01, 0x09, 0x8b, 0xc6, 0xdb, 0xbf, 116 | 0x45, 0x69, 0x0f, 0x3a, 0x7e, 0x9e, 0x6d, 0x0f, 117 | 0x8b, 0xbe, 0xa2, 0xa3, 0x9e, 0x61, 0x48, 0x00, 118 | 0x8f, 0xd0, 0x5e, 0x44}; 119 | uint8_t expected2_sha256[] = {0x5b, 0xdc, 0xc1, 0x46, 0xbf, 0x60, 0x75, 0x4e, 120 | 0x6a, 0x04, 0x24, 0x26, 0x08, 0x95, 0x75, 0xc7, 121 | 0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27, 0x39, 0x83, 122 | 0x9d, 0xec, 0x58, 0xb9, 0x64, 0xec, 0x38, 0x43}; 123 | uint8_t expected2_sha384[] = {0xaf, 0x45, 0xd2, 0xe3, 0x76, 0x48, 0x40, 0x31, 124 | 0x61, 0x7f, 0x78, 0xd2, 0xb5, 0x8a, 0x6b, 0x1b, 125 | 0x9c, 0x7e, 0xf4, 0x64, 0xf5, 0xa0, 0x1b, 0x47, 126 | 0xe4, 0x2e, 0xc3, 0x73, 0x63, 0x22, 0x44, 0x5e, 127 | 0x8e, 0x22, 0x40, 0xca, 0x5e, 0x69, 0xe2, 0xc7, 128 | 0x8b, 0x32, 0x39, 0xec, 0xfa, 0xb2, 0x16, 0x49}; 129 | uint8_t expected2_sha512[] = {0x16, 0x4b, 0x7a, 0x7b, 0xfc, 0xf8, 0x19, 0xe2, 130 | 0xe3, 0x95, 0xfb, 0xe7, 0x3b, 0x56, 0xe0, 0xa3, 131 | 0x87, 0xbd, 0x64, 0x22, 0x2e, 0x83, 0x1f, 0xd6, 132 | 0x10, 0x27, 0x0c, 0xd7, 0xea, 0x25, 0x05, 0x54, 133 | 0x97, 0x58, 0xbf, 0x75, 0xc0, 0x5a, 0x99, 0x4a, 134 | 0x6d, 0x03, 0x4f, 0x65, 0xf8, 0xf0, 0xe6, 0xfd, 135 | 0xca, 0xea, 0xb1, 0xa3, 0x4d, 0x4a, 0x6b, 0x4b, 136 | 0x63, 0x6e, 0x07, 0x0a, 0x38, 0xbc, 0xe7, 0x37}; 137 | 138 | unsigned char text3[50]; 139 | memset(text3, 0xdd, 50); 140 | unsigned char key3[20]; 141 | memset(key3, 0xaa, 20); 142 | uint8_t expected3[] = {0x12, 0x5d, 0x73, 0x42, 0xb9, 0xac, 0x11, 0xcd, 0x91, 143 | 0xa3, 0x9a, 0xf4, 0x8a, 0xa1, 0x7b, 0x4f, 0x63, 0xf1, 144 | 0x75, 0xd3}; 145 | uint8_t expected3_sha224[] = {0x7f, 0xb3, 0xcb, 0x35, 0x88, 0xc6, 0xc1, 0xf6, 146 | 0xff, 0xa9, 0x69, 0x4d, 0x7d, 0x6a, 0xd2, 0x64, 147 | 0x93, 0x65, 0xb0, 0xc1, 0xf6, 0x5d, 0x69, 0xd1, 148 | 0xec, 0x83, 0x33, 0xea}; 149 | uint8_t expected3_sha256[] = {0x77, 0x3e, 0xa9, 0x1e, 0x36, 0x80, 0x0e, 0x46, 150 | 0x85, 0x4d, 0xb8, 0xeb, 0xd0, 0x91, 0x81, 0xa7, 151 | 0x29, 0x59, 0x09, 0x8b, 0x3e, 0xf8, 0xc1, 0x22, 152 | 0xd9, 0x63, 0x55, 0x14, 0xce, 0xd5, 0x65, 0xfe}; 153 | uint8_t expected3_sha384[] = {0x88, 0x06, 0x26, 0x08, 0xd3, 0xe6, 0xad, 0x8a, 154 | 0x0a, 0xa2, 0xac, 0xe0, 0x14, 0xc8, 0xa8, 0x6f, 155 | 0x0a, 0xa6, 0x35, 0xd9, 0x47, 0xac, 0x9f, 0xeb, 156 | 0xe8, 0x3e, 0xf4, 0xe5, 0x59, 0x66, 0x14, 0x4b, 157 | 0x2a, 0x5a, 0xb3, 0x9d, 0xc1, 0x38, 0x14, 0xb9, 158 | 0x4e, 0x3a, 0xb6, 0xe1, 0x01, 0xa3, 0x4f, 0x27}; 159 | uint8_t expected3_sha512[] = {0xfa, 0x73, 0xb0, 0x08, 0x9d, 0x56, 0xa2, 0x84, 160 | 0xef, 0xb0, 0xf0, 0x75, 0x6c, 0x89, 0x0b, 0xe9, 161 | 0xb1, 0xb5, 0xdb, 0xdd, 0x8e, 0xe8, 0x1a, 0x36, 162 | 0x55, 0xf8, 0x3e, 0x33, 0xb2, 0x27, 0x9d, 0x39, 163 | 0xbf, 0x3e, 0x84, 0x82, 0x79, 0xa7, 0x22, 0xc8, 164 | 0x06, 0xb4, 0x85, 0xa4, 0x7e, 0x67, 0xc8, 0x07, 165 | 0xb9, 0x46, 0xa3, 0x37, 0xbe, 0xe8, 0x94, 0x26, 166 | 0x74, 0x27, 0x88, 0x59, 0xe1, 0x32, 0x92, 0xfb}; 167 | 168 | unsigned char text4[50]; 169 | memset(text4, 0xcd, 50); 170 | unsigned char key4[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 171 | 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 172 | 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19}; 173 | uint8_t expected4[] = {0x4c, 0x90, 0x07, 0xf4, 0x02, 0x62, 0x50, 0xc6, 0xbc, 174 | 0x84, 0x14, 0xf9, 0xbf, 0x50, 0xc8, 0x6c, 0x2d, 0x72, 175 | 0x35, 0xda}; 176 | uint8_t expected4_sha224[] = {0x6c, 0x11, 0x50, 0x68, 0x74, 0x01, 0x3c, 0xac, 177 | 0x6a, 0x2a, 0xbc, 0x1b, 0xb3, 0x82, 0x62, 0x7c, 178 | 0xec, 0x6a, 0x90, 0xd8, 0x6e, 0xfc, 0x01, 0x2d, 179 | 0xe7, 0xaf, 0xec, 0x5a}; 180 | uint8_t expected4_sha256[] = {0x82, 0x55, 0x8a, 0x38, 0x9a, 0x44, 0x3c, 0x0e, 181 | 0xa4, 0xcc, 0x81, 0x98, 0x99, 0xf2, 0x08, 0x3a, 182 | 0x85, 0xf0, 0xfa, 0xa3, 0xe5, 0x78, 0xf8, 0x07, 183 | 0x7a, 0x2e, 0x3f, 0xf4, 0x67, 0x29, 0x66, 0x5b}; 184 | uint8_t expected4_sha384[] = {0x3e, 0x8a, 0x69, 0xb7, 0x78, 0x3c, 0x25, 0x85, 185 | 0x19, 0x33, 0xab, 0x62, 0x90, 0xaf, 0x6c, 0xa7, 186 | 0x7a, 0x99, 0x81, 0x48, 0x08, 0x50, 0x00, 0x9c, 187 | 0xc5, 0x57, 0x7c, 0x6e, 0x1f, 0x57, 0x3b, 0x4e, 188 | 0x68, 0x01, 0xdd, 0x23, 0xc4, 0xa7, 0xd6, 0x79, 189 | 0xcc, 0xf8, 0xa3, 0x86, 0xc6, 0x74, 0xcf, 0xfb}; 190 | uint8_t expected4_sha512[] = {0xb0, 0xba, 0x46, 0x56, 0x37, 0x45, 0x8c, 0x69, 191 | 0x90, 0xe5, 0xa8, 0xc5, 0xf6, 0x1d, 0x4a, 0xf7, 192 | 0xe5, 0x76, 0xd9, 0x7f, 0xf9, 0x4b, 0x87, 0x2d, 193 | 0xe7, 0x6f, 0x80, 0x50, 0x36, 0x1e, 0xe3, 0xdb, 194 | 0xa9, 0x1c, 0xa5, 0xc1, 0x1a, 0xa2, 0x5e, 0xb4, 195 | 0xd6, 0x79, 0x27, 0x5c, 0xc5, 0x78, 0x80, 0x63, 196 | 0xa5, 0xf1, 0x97, 0x41, 0x12, 0x0c, 0x4f, 0x2d, 197 | 0xe2, 0xad, 0xeb, 0xeb, 0x10, 0xa2, 0x98, 0xdd}; 198 | 199 | unsigned char text5[] = "Test With Truncation"; 200 | unsigned char key5[20]; 201 | memset(key5, 0x0c, 20); 202 | uint8_t expected5[] = {0x4c, 0x1a, 0x03, 0x42, 0x4b, 0x55, 0xe0, 0x7f, 0xe7, 203 | 0xf2, 0x7b, 0xe1, 0xd5, 0x8b, 0xb9, 0x32, 0x4a, 0x9a, 204 | 0x5a, 0x04}; 205 | uint8_t expected5_sha224[] = {0x0e, 0x2a, 0xea, 0x68, 0xa9, 0x0c, 0x8d, 0x37, 206 | 0xc9, 0x88, 0xbc, 0xdb, 0x9f, 0xca, 0x6f, 0xa8}; 207 | uint8_t expected5_sha256[] = {0xa3, 0xb6, 0x16, 0x74, 0x73, 0x10, 0x0e, 0xe0, 208 | 0x6e, 0x0c, 0x79, 0x6c, 0x29, 0x55, 0x55, 0x2b}; 209 | uint8_t expected5_sha384[] = {0x3a, 0xbf, 0x34, 0xc3, 0x50, 0x3b, 0x2a, 0x23, 210 | 0xa4, 0x6e, 0xfc, 0x61, 0x9b, 0xae, 0xf8, 0x97}; 211 | uint8_t expected5_sha512[] = {0x41, 0x5f, 0xad, 0x62, 0x71, 0x58, 0x0a, 0x53, 212 | 0x1d, 0x41, 0x79, 0xbc, 0x89, 0x1d, 0x87, 0xa6}; 213 | 214 | /* RFC 2202 only requires 80 but RFC 4231 needs 131 so we make the key buffer 131 bytes */ 215 | unsigned char text6[] = "Test Using Larger Than Block-Size Key - Hash Key First"; 216 | unsigned char key6[131]; 217 | memset(key6, 0xaa, 131); 218 | uint8_t expected6[] = {0xaa, 0x4a, 0xe5, 0xe1, 0x52, 0x72, 0xd0, 0x0e, 0x95, 219 | 0x70, 0x56, 0x37, 0xce, 0x8a, 0x3b, 0x55, 0xed, 0x40, 220 | 0x21, 0x12}; 221 | uint8_t expected6_sha224[] = {0x95, 0xe9, 0xa0, 0xdb, 0x96, 0x20, 0x95, 0xad, 222 | 0xae, 0xbe, 0x9b, 0x2d, 0x6f, 0x0d, 0xbc, 0xe2, 223 | 0xd4, 0x99, 0xf1, 0x12, 0xf2, 0xd2, 0xb7, 0x27, 224 | 0x3f, 0xa6, 0x87, 0x0e}; 225 | uint8_t expected6_sha256[] = {0x60, 0xe4, 0x31, 0x59, 0x1e, 0xe0, 0xb6, 0x7f, 226 | 0x0d, 0x8a, 0x26, 0xaa, 0xcb, 0xf5, 0xb7, 0x7f, 227 | 0x8e, 0x0b, 0xc6, 0x21, 0x37, 0x28, 0xc5, 0x14, 228 | 0x05, 0x46, 0x04, 0x0f, 0x0e, 0xe3, 0x7f, 0x54}; 229 | uint8_t expected6_sha384[] = {0x4e, 0xce, 0x08, 0x44, 0x85, 0x81, 0x3e, 0x90, 230 | 0x88, 0xd2, 0xc6, 0x3a, 0x04, 0x1b, 0xc5, 0xb4, 231 | 0x4f, 0x9e, 0xf1, 0x01, 0x2a, 0x2b, 0x58, 0x8f, 232 | 0x3c, 0xd1, 0x1f, 0x05, 0x03, 0x3a, 0xc4, 0xc6, 233 | 0x0c, 0x2e, 0xf6, 0xab, 0x40, 0x30, 0xfe, 0x82, 234 | 0x96, 0x24, 0x8d, 0xf1, 0x63, 0xf4, 0x49, 0x52}; 235 | uint8_t expected6_sha512[] = {0x80, 0xb2, 0x42, 0x63, 0xc7, 0xc1, 0xa3, 0xeb, 236 | 0xb7, 0x14, 0x93, 0xc1, 0xdd, 0x7b, 0xe8, 0xb4, 237 | 0x9b, 0x46, 0xd1, 0xf4, 0x1b, 0x4a, 0xee, 0xc1, 238 | 0x12, 0x1b, 0x01, 0x37, 0x83, 0xf8, 0xf3, 0x52, 239 | 0x6b, 0x56, 0xd0, 0x37, 0xe0, 0x5f, 0x25, 0x98, 240 | 0xbd, 0x0f, 0xd2, 0x21, 0x5d, 0x6a, 0x1e, 0x52, 241 | 0x95, 0xe6, 0x4f, 0x73, 0xf6, 0x3f, 0x0a, 0xec, 242 | 0x8b, 0x91, 0x5a, 0x98, 0x5d, 0x78, 0x65, 0x98}; 243 | 244 | /* RFC 2202 test case 7 differs quite a lot from the rfc 4231 testcase 7 so they 245 | * will be kept separate 246 | */ 247 | unsigned char text7[] = "Test Using Larger Than Block-Size Key and Larger " 248 | "Than One Block-Size Data"; 249 | unsigned char key7[80]; 250 | memset(key7, 0xaa, 80); 251 | uint8_t expected7[] = {0xe8, 0xe9, 0x9d, 0x0f, 0x45, 0x23, 0x7d, 0x78, 0x6d, 252 | 0x6b, 0xba, 0xa7, 0x96, 0x5c, 0x78, 0x08, 0xbb, 0xff, 253 | 0x1a, 0x91}; 254 | 255 | unsigned char text7_rfc4231[] = "This is a test using a larger than " 256 | "block-size key and a larger than block-size data. The key needs to be " 257 | "hashed before being used by the HMAC algorithm."; 258 | unsigned char key7_rfc4231[131]; 259 | memset(key7_rfc4231, 0xaa, 131); 260 | 261 | uint8_t expected7_sha224[] = {0x3a, 0x85, 0x41, 0x66, 0xac, 0x5d, 0x9f, 0x02, 262 | 0x3f, 0x54, 0xd5, 0x17, 0xd0, 0xb3, 0x9d, 0xbd, 263 | 0x94, 0x67, 0x70, 0xdb, 0x9c, 0x2b, 0x95, 0xc9, 264 | 0xf6, 0xf5, 0x65, 0xd1}; 265 | uint8_t expected7_sha256[] = {0x9b, 0x09, 0xff, 0xa7, 0x1b, 0x94, 0x2f, 0xcb, 266 | 0x27, 0x63, 0x5f, 0xbc, 0xd5, 0xb0, 0xe9, 0x44, 267 | 0xbf, 0xdc, 0x63, 0x64, 0x4f, 0x07, 0x13, 0x93, 268 | 0x8a, 0x7f, 0x51, 0x53, 0x5c, 0x3a, 0x35, 0xe2}; 269 | uint8_t expected7_sha384[] = {0x66, 0x17, 0x17, 0x8e, 0x94, 0x1f, 0x02, 0x0d, 270 | 0x35, 0x1e, 0x2f, 0x25, 0x4e, 0x8f, 0xd3, 0x2c, 271 | 0x60, 0x24, 0x20, 0xfe, 0xb0, 0xb8, 0xfb, 0x9a, 272 | 0xdc, 0xce, 0xbb, 0x82, 0x46, 0x1e, 0x99, 0xc5, 273 | 0xa6, 0x78, 0xcc, 0x31, 0xe7, 0x99, 0x17, 0x6d, 274 | 0x38, 0x60, 0xe6, 0x11, 0x0c, 0x46, 0x52, 0x3e}; 275 | uint8_t expected7_sha512[] = {0xe3, 0x7b, 0x6a, 0x77, 0x5d, 0xc8, 0x7d, 0xba, 276 | 0xa4, 0xdf, 0xa9, 0xf9, 0x6e, 0x5e, 0x3f, 0xfd, 277 | 0xde, 0xbd, 0x71, 0xf8, 0x86, 0x72, 0x89, 0x86, 278 | 0x5d, 0xf5, 0xa3, 0x2d, 0x20, 0xcd, 0xc9, 0x44, 279 | 0xb6, 0x02, 0x2c, 0xac, 0x3c, 0x49, 0x82, 0xb1, 280 | 0x0d, 0x5e, 0xeb, 0x55, 0xc3, 0xe4, 0xde, 0x15, 281 | 0x13, 0x46, 0x76, 0xfb, 0x6d, 0xe0, 0x44, 0x60, 282 | 0x65, 0xc9, 0x74, 0x40, 0xfa, 0x8c, 0x6a, 0x58}; 283 | 284 | TEST(("HMAC-SHA1 case 1")); 285 | res = hmac (SHA1, text1, 8, key1, 20, result); 286 | assert(res == 0); 287 | assert(memcmp(result, expected1, 20) == 0); 288 | 289 | TEST(("HMAC-SHA-224 case 1")); 290 | res = hmac (SHA224, text1, 8, key1, 20, result); 291 | assert(res == 0); 292 | assert(memcmp(result, expected1_sha224, sizeof(expected1_sha224)) == 0); 293 | 294 | TEST(("HMAC-SHA-256 case 1")); 295 | res = hmac (SHA256, text1, 8, key1, 20, result); 296 | assert(res == 0); 297 | assert(memcmp(result, expected1_sha256, sizeof(expected1_sha256)) == 0); 298 | 299 | TEST(("HMAC-SHA-384 case 1")); 300 | res = hmac (SHA384, text1, 8, key1, 20, result); 301 | assert(res == 0); 302 | assert(memcmp(result, expected1_sha384, sizeof(expected1_sha384)) == 0); 303 | 304 | TEST(("HMAC-SHA-512 case 1")); 305 | res = hmac (SHA512, text1, 8, key1, 20, result); 306 | assert(res == 0); 307 | assert(memcmp(result, expected1_sha512, sizeof(expected1_sha512)) == 0); 308 | 309 | TEST(("HMAC-SHA1 case 2")); 310 | res = hmac (SHA1, text2, 28, key2, 4, result); 311 | assert(res == 0); 312 | assert(memcmp(result, expected2, 20) == 0); 313 | 314 | TEST(("HMAC-SHA-224 case 2")); 315 | res = hmac (SHA224, text2, 28, key2, 4, result); 316 | assert(res == 0); 317 | assert(memcmp(result, expected2_sha224, sizeof(expected2_sha224)) == 0); 318 | 319 | TEST(("HMAC-SHA-256 case 2")); 320 | res = hmac (SHA256, text2, 28, key2, 4, result); 321 | assert(res == 0); 322 | assert(memcmp(result, expected2_sha256, sizeof(expected2_sha256)) == 0); 323 | 324 | TEST(("HMAC-SHA-384 case 2")); 325 | res = hmac (SHA384, text2, 28, key2, 4, result); 326 | assert(res == 0); 327 | assert(memcmp(result, expected2_sha384, sizeof(expected2_sha384)) == 0); 328 | 329 | TEST(("HMAC-SHA-512 case 2")); 330 | res = hmac (SHA512, text2, 28, key2, 4, result); 331 | assert(res == 0); 332 | assert(memcmp(result, expected2_sha512, sizeof(expected1_sha512)) == 0); 333 | 334 | TEST(("HMAC-SHA1 case 3")); 335 | res = hmac (SHA1, text3, 50, key3, 20, result); 336 | assert(res == 0); 337 | assert(memcmp(result, expected3, 20) == 0); 338 | 339 | TEST(("HMAC-SHA-224 case 3")); 340 | res = hmac (SHA224, text3, 50, key3, 20, result); 341 | assert(res == 0); 342 | assert(memcmp(result, expected3_sha224, sizeof(expected3_sha224)) == 0); 343 | 344 | TEST(("HMAC-SHA-256 case 3")); 345 | res = hmac (SHA256, text3, 50, key3, 20, result); 346 | assert(res == 0); 347 | assert(memcmp(result, expected3_sha256, sizeof(expected3_sha256)) == 0); 348 | 349 | TEST(("HMAC-SHA-384 case 3")); 350 | res = hmac (SHA384, text3, 50, key3, 20, result); 351 | assert(res == 0); 352 | assert(memcmp(result, expected3_sha384, sizeof(expected3_sha384)) == 0); 353 | 354 | TEST(("HMAC-SHA-512 case 3")); 355 | res = hmac (SHA512, text3, 50, key3, 20, result); 356 | assert(res == 0); 357 | assert(memcmp(result, expected3_sha512, sizeof(expected3_sha512)) == 0); 358 | 359 | TEST(("HMAC-SHA1 case 4")); 360 | res = hmac (SHA1, text4, 50, key4, 25, result); 361 | assert(res == 0); 362 | assert(memcmp(result, expected4, 20) == 0); 363 | 364 | TEST(("HMAC-SHA-224 case 4")); 365 | res = hmac (SHA224, text4, 50, key4, 25, result); 366 | assert(res == 0); 367 | assert(memcmp(result, expected4_sha224, sizeof(expected4_sha224)) == 0); 368 | 369 | TEST(("HMAC-SHA-256 case 4")); 370 | res = hmac (SHA256, text4, 50, key4, 25, result); 371 | assert(res == 0); 372 | assert(memcmp(result, expected4_sha256, sizeof(expected4_sha256)) == 0); 373 | 374 | TEST(("HMAC-SHA-384 case 4")); 375 | res = hmac (SHA384, text4, 50, key4, 25, result); 376 | assert(res == 0); 377 | assert(memcmp(result, expected4_sha384, sizeof(expected4_sha384)) == 0); 378 | 379 | TEST(("HMAC-SHA-512 case 4")); 380 | res = hmac (SHA512, text4, 50, key4, 25, result); 381 | assert(res == 0); 382 | assert(memcmp(result, expected4_sha512, sizeof(expected4_sha512)) == 0); 383 | 384 | TEST(("HMAC-SHA1 case 5")); 385 | res = hmac (SHA1, text5, 20, key5, 20, result); 386 | assert(res == 0); 387 | assert(memcmp(result, expected5, 20) == 0); 388 | 389 | TEST(("HMAC-SHA-224 case 5")); 390 | res = hmac (SHA224, text5, 20, key5, 20, result); 391 | assert(res == 0); 392 | assert(memcmp(result, expected5_sha224, sizeof(expected5_sha224)) == 0); 393 | 394 | TEST(("HMAC-SHA-256 case 5")); 395 | res = hmac (SHA256, text5, 20, key5, 20, result); 396 | assert(res == 0); 397 | assert(memcmp(result, expected5_sha256, sizeof(expected5_sha256)) == 0); 398 | 399 | TEST(("HMAC-SHA-384 case 5")); 400 | res = hmac (SHA384, text5, 20, key5, 20, result); 401 | assert(res == 0); 402 | assert(memcmp(result, expected5_sha384, sizeof(expected5_sha384)) == 0); 403 | 404 | TEST(("HMAC-SHA-512 case 5")); 405 | res = hmac (SHA512, text5, 20, key5, 20, result); 406 | assert(res == 0); 407 | assert(memcmp(result, expected5_sha512, sizeof(expected5_sha512)) == 0); 408 | 409 | /* the SHA1 test from rfc 2202 uses 80 bytes keysize */ 410 | TEST(("HMAC-SHA1 case 6")); 411 | res = hmac (SHA1, text6, 54, key6, 80, result); 412 | assert(res == 0); 413 | assert(memcmp(result, expected6, 20) == 0); 414 | 415 | /* The tests from RFC 4231 uses a 131 bytes key (a key larger 416 | * than 128 bytes (= block-size of SHA-384 and SHA-512). 417 | */ 418 | TEST(("HMAC-SHA-224 case 6")); 419 | res = hmac (SHA224, text6, 54, key6, 131, result); 420 | assert(res == 0); 421 | assert(memcmp(result, expected6_sha224, sizeof(expected6_sha224)) == 0); 422 | 423 | TEST(("HMAC-SHA-256 case 6")); 424 | res = hmac (SHA256, text6, 54, key6, 131, result); 425 | assert(res == 0); 426 | assert(memcmp(result, expected6_sha256, sizeof(expected6_sha256)) == 0); 427 | 428 | TEST(("HMAC-SHA-384 case 6")); 429 | res = hmac (SHA384, text6, 54, key6, 131, result); 430 | assert(res == 0); 431 | assert(memcmp(result, expected6_sha384, sizeof(expected6_sha384)) == 0); 432 | 433 | TEST(("HMAC-SHA-512 case 6")); 434 | res = hmac (SHA512, text6, 54, key6, 131, result); 435 | assert(res == 0); 436 | assert(memcmp(result, expected6_sha512, sizeof(expected6_sha512)) == 0); 437 | 438 | TEST(("HMAC-SHA1 case 7")); 439 | res = hmac (SHA1, text7, 73, key7, 80, result); 440 | assert(res == 0); 441 | assert(memcmp(result, expected7, 20) == 0); 442 | 443 | TEST(("HMAC-SHA-224 case 7")); 444 | res = hmac (SHA224, text7_rfc4231, strlen(text7_rfc4231), key7_rfc4231, 445 | sizeof(key7_rfc4231), result); 446 | assert(res == 0); 447 | assert(memcmp(result, expected7_sha224, sizeof(expected7_sha224)) == 0); 448 | 449 | TEST(("HMAC-SHA-256 case 7")); 450 | res = hmac (SHA256, text7_rfc4231, strlen(text7_rfc4231), key7_rfc4231, 451 | sizeof(key7_rfc4231), result); 452 | assert(res == 0); 453 | assert(memcmp(result, expected7_sha256, sizeof(expected7_sha256)) == 0); 454 | 455 | TEST(("HMAC-SHA-384 case 7")); 456 | res = hmac (SHA384, text7_rfc4231, strlen(text7_rfc4231), key7_rfc4231, 457 | sizeof(key7_rfc4231), result); 458 | assert(res == 0); 459 | assert(memcmp(result, expected7_sha384, sizeof(expected7_sha384)) == 0); 460 | 461 | TEST(("HMAC-SHA-512 case 7")); 462 | res = hmac (SHA512, text7_rfc4231, strlen(text7_rfc4231), key7_rfc4231, 463 | sizeof(key7_rfc4231), result); 464 | assert(res == 0); 465 | assert(memcmp(result, expected7_sha512, sizeof(expected7_sha512)) == 0); 466 | } 467 | 468 | int 469 | main (void) 470 | { 471 | int client_id = 1851; 472 | char client_key[] = { 473 | 0xa0, 0x15, 0x5b, 0x36, 0xde, 0xc8, 0x65, 0xe8, 0x59, 0x19, 474 | 0x1f, 0x7d, 0xae, 0xfa, 0xbc, 0x77, 0xa4, 0x59, 0xd4, 0x33 475 | }; 476 | char *client_hexkey = "a0155b36dec865e859191f7daefabc77a459d433"; 477 | char *client_b64key = "oBVbNt7IZehZGR99rvq8d6RZ1DM="; 478 | ykclient_t *ykc; 479 | int ret; 480 | 481 | if (strcmp (YKCLIENT_VERSION_STRING, ykclient_check_version (NULL)) != 0) 482 | { 483 | printf ("version mismatch %s != %s\n",YKCLIENT_VERSION_STRING, 484 | ykclient_check_version (NULL)); 485 | return 1; 486 | } 487 | 488 | if (ykclient_check_version (YKCLIENT_VERSION_STRING) == NULL) 489 | { 490 | printf ("version NULL?\n"); 491 | return 1; 492 | } 493 | 494 | if (ykclient_check_version ("99.99.99") != NULL) 495 | { 496 | printf ("version not NULL?\n"); 497 | return 1; 498 | } 499 | 500 | printf ("ykclient version: header %s library %s\n", 501 | YKCLIENT_VERSION_STRING, ykclient_check_version(NULL)); 502 | 503 | ret = ykclient_global_init (); 504 | assert (ret == YKCLIENT_OK); 505 | 506 | TEST(("init self")); 507 | ret = ykclient_init (&ykc); 508 | printf ("ykclient_init (%d): %s\n", ret, ykclient_strerror (ret)); 509 | assert(ret == YKCLIENT_OK); 510 | 511 | TEST(("null client_id, expect REPLAYED_OTP")); 512 | ykclient_set_verify_signature(ykc, 0); 513 | ykclient_set_client (ykc, client_id, 0, NULL); 514 | 515 | #ifndef TEST_WITHOUT_INTERNET 516 | ret = ykclient_request (ykc, "ccccccbchvthlivuitriujjifivbvtrjkjfirllluurj"); 517 | printf ("ykclient_request (%d): %s\n", ret, ykclient_strerror (ret)); 518 | printf ("used url: %s\n", ykclient_get_last_url (ykc)); 519 | assert(ret == YKCLIENT_REPLAYED_OTP); 520 | #else 521 | printf ("Test SKIPPED\n"); 522 | #endif 523 | 524 | TEST(("client_id set(20), correct client_key, expect REPLAYED_OTP")); 525 | ykclient_set_client (ykc, client_id, 20, client_key); 526 | 527 | #ifndef TEST_WITHOUT_INTERNET 528 | ret = ykclient_request (ykc, "ccccccbchvthlivuitriujjifivbvtrjkjfirllluurj"); 529 | printf ("ykclient_request (%d): %s\n", ret, ykclient_strerror (ret)); 530 | printf ("used url: %s\n", ykclient_get_last_url (ykc)); 531 | assert (ret == YKCLIENT_REPLAYED_OTP); 532 | #else 533 | printf ("Test SKIPPED\n"); 534 | #endif 535 | 536 | TEST(("wrong client_id set(10), correct client_key, expect BAD_SIGNATURE")); 537 | ykclient_set_client (ykc, client_id, 10, client_key); 538 | 539 | #ifndef TEST_WITHOUT_INTERNET 540 | ret = ykclient_request (ykc, "ccccccbchvthlivuitriujjifivbvtrjkjfirllluurj"); 541 | printf ("ykclient_request (%d): %s\n", ret, ykclient_strerror (ret)); 542 | printf ("used url: %s\n", ykclient_get_last_url (ykc)); 543 | assert (ret == YKCLIENT_BAD_SIGNATURE); 544 | #else 545 | printf ("Test SKIPPED\n"); 546 | #endif 547 | 548 | TEST(("invalid client_id set(a), correct client_key, expect HEX_DECODE_ERROR")); 549 | ret = ykclient_set_client_hex (ykc, client_id, "a"); 550 | printf ("ykclient_set_client_hex (%d): %s\n", ret, ykclient_strerror (ret)); 551 | assert (ret == YKCLIENT_HEX_DECODE_ERROR); 552 | 553 | TEST(("invalid client_id set(xx), correct client_key, expect HEX_DECODE_ERROR")); 554 | ret = ykclient_set_client_hex (ykc, client_id, "xx"); 555 | printf ("ykclient_set_client_hex (%d): %s\n", ret, ykclient_strerror (ret)); 556 | assert (ret == YKCLIENT_HEX_DECODE_ERROR); 557 | 558 | TEST(("hex client_id set, correct client_key, expect OK")); 559 | ret = ykclient_set_client_hex (ykc, client_id, client_hexkey); 560 | printf ("ykclient_set_client_hex (%d): %s\n", ret, ykclient_strerror (ret)); 561 | assert (ret == YKCLIENT_OK); 562 | 563 | #ifndef TEST_WITHOUT_INTERNET 564 | TEST(("validation request, expect REPLAYED_OTP")); 565 | ret = ykclient_request (ykc, "ccccccbchvthlivuitriujjifivbvtrjkjfirllluurj"); 566 | printf ("ykclient_request (%d): %s\n", ret, ykclient_strerror (ret)); 567 | printf ("used url: %s\n", ykclient_get_last_url (ykc)); 568 | assert (ret == YKCLIENT_REPLAYED_OTP); 569 | #else 570 | printf ("Test SKIPPED\n"); 571 | #endif 572 | 573 | TEST(("set deadbeef client_id, expect OK")); 574 | ret = ykclient_set_client_hex (ykc, client_id, "deadbeef"); 575 | printf ("ykclient_set_client_hex (%d): %s\n", ret, ykclient_strerror (ret)); 576 | assert (ret == YKCLIENT_OK); 577 | 578 | #ifndef TEST_WITHOUT_INTERNET 579 | TEST(("validation request, expect BAD_SIGNATURE")); 580 | ret = ykclient_request (ykc, "ccccccbchvthlivuitriujjifivbvtrjkjfirllluurj"); 581 | printf ("ykclient_request (%d): %s\n", ret, ykclient_strerror (ret)); 582 | printf ("used url: %s\n", ykclient_get_last_url (ykc)); 583 | assert (ret == YKCLIENT_BAD_SIGNATURE); 584 | #else 585 | printf ("Test SKIPPED\n"); 586 | #endif 587 | 588 | TEST(("b64 set deadbeef client_id, expect OK")); 589 | ret = ykclient_set_client_b64 (ykc, client_id, "deadbeef"); 590 | printf ("ykclient_set_client_b64 (%d): %s\n", ret, ykclient_strerror (ret)); 591 | assert (ret == YKCLIENT_OK); 592 | 593 | #ifndef TEST_WITHOUT_INTERNET 594 | /* When the server dislikes our signature, it will sign the response with a 595 | NULL key, so the API call will fail with BAD_SERVER_SIGNATURE even though 596 | the server returned status=BAD_SIGNATURE. 597 | */ 598 | TEST(("validation request, expect BAD_SERVER_SIGNATURE")); 599 | ret = ykclient_request (ykc, "ccccccbchvthlivuitriujjifivbvtrjkjfirllluurj"); 600 | printf ("ykclient_request (%d): %s\n", ret, ykclient_strerror (ret)); 601 | printf ("used url: %s\n", ykclient_get_last_url (ykc)); 602 | assert (ret == YKCLIENT_BAD_SERVER_SIGNATURE); 603 | #else 604 | printf ("Test SKIPPED\n"); 605 | #endif 606 | 607 | #ifndef TEST_WITHOUT_INTERNET 608 | /* Now, disable our checking of the servers signature to get the error 609 | the server returned (server will use 00000 as key when signing this 610 | error response). 611 | */ 612 | TEST(("validation request, expect BAD_SIGNATURE")); 613 | ykclient_set_verify_signature (ykc, 0); 614 | ret = ykclient_request (ykc, "ccccccbchvthlivuitriujjifivbvtrjkjfirllluurj"); 615 | printf ("ykclient_request (%d): %s\n", ret, ykclient_strerror (ret)); 616 | printf ("used url: %s\n", ykclient_get_last_url (ykc)); 617 | assert (ret == YKCLIENT_BAD_SIGNATURE); 618 | #else 619 | printf ("Test SKIPPED\n"); 620 | #endif 621 | 622 | TEST(("b64 set client_b64key, expect OK")); 623 | ret = ykclient_set_client_b64 (ykc, client_id, client_b64key); 624 | printf ("ykclient_set_client_b64 (%d): %s\n", ret, ykclient_strerror (ret)); 625 | assert (ret == YKCLIENT_OK); 626 | 627 | #ifndef TEST_WITHOUT_INTERNET 628 | TEST(("validation request, expect REPLAYED_OTP")); 629 | ret = ykclient_request (ykc, "ccccccbchvthlivuitriujjifivbvtrjkjfirllluurj"); 630 | printf ("ykclient_request (%d): %s\n", ret, ykclient_strerror (ret)); 631 | printf ("used url: %s\n", ykclient_get_last_url (ykc)); 632 | assert (ret == YKCLIENT_REPLAYED_OTP); 633 | #else 634 | printf ("Test SKIPPED\n"); 635 | #endif 636 | 637 | TEST(("set WS 2.0 URL template")); 638 | /* Set one URL and run tests with that. */ 639 | ykclient_set_url_template 640 | (ykc, "https://api.yubico.com/wsapi/2.0/verify?id=%d&otp=%s"); 641 | 642 | #ifndef TEST_WITHOUT_INTERNET 643 | TEST(("validation request, expect REPLAYED_OTP")); 644 | ret = ykclient_request (ykc, "ccccccbchvthlivuitriujjifivbvtrjkjfirllluurj"); 645 | printf ("yubikey_request (%d): %s\n", ret, ykclient_strerror (ret)); 646 | printf ("used url: %s\n", ykclient_get_last_url (ykc)); 647 | assert (ret == YKCLIENT_REPLAYED_OTP); 648 | #else 649 | printf ("Test SKIPPED\n"); 650 | #endif 651 | 652 | ykclient_set_verify_signature(ykc, 1); 653 | 654 | TEST(("validation request with valid signature, expect REPLAYED_OTP")); 655 | // Check a genuine signature. 656 | ykclient_set_client (ykc, client_id, 20, client_key); 657 | #ifndef TEST_WITHOUT_INTERNET 658 | ret = ykclient_request (ykc, "ccccccbchvthlivuitriujjifivbvtrjkjfirllluurj"); 659 | printf ("ykclient_request (%d): %s\n", ret, ykclient_strerror (ret)); 660 | printf ("used url: %s\n", ykclient_get_last_url (ykc)); 661 | assert (ret == YKCLIENT_REPLAYED_OTP); 662 | #else 663 | printf ("Test SKIPPED\n"); 664 | #endif 665 | 666 | TEST(("validation request with bad key, expect YKCLIENT_BAD_SERVER_SIGNATURE")); 667 | // Check a genuine signature with a truncated key. 668 | ykclient_set_client (ykc, client_id, 10, client_key); 669 | #ifndef TEST_WITHOUT_INTERNET 670 | ret = ykclient_request (ykc, "ccccccbchvthlivuitriujjifivbvtrjkjfirllluurj"); 671 | printf ("ykclient_request (%d): %s\n", ret, ykclient_strerror (ret)); 672 | printf ("used url: %s\n", ykclient_get_last_url (ykc)); 673 | assert (ret == YKCLIENT_BAD_SERVER_SIGNATURE); 674 | #else 675 | printf ("Test SKIPPED\n"); 676 | #endif 677 | 678 | TEST(("Set and use OLD V2.0 URL")); 679 | const char *templates[] = { 680 | "https://api.yubico.com/wsapi/2.0/verify?id=%d&otp=%s", 681 | }; 682 | ykclient_set_url_templates(ykc, 1, templates); 683 | ykclient_set_client (ykc, client_id, 20, client_key); 684 | #ifndef TEST_WITHOUT_INTERNET 685 | ret = ykclient_request (ykc, "ccccccbchvthlivuitriujjifivbvtrjkjfirllluurj"); 686 | printf ("ykclient_request (%d): %s\n", ret, ykclient_strerror (ret)); 687 | printf ("used url: %s\n", ykclient_get_last_url (ykc)); 688 | assert (ret == YKCLIENT_REPLAYED_OTP); 689 | #else 690 | printf ("Test SKIPPED\n"); 691 | #endif 692 | 693 | TEST(("Set and use NEW V2.0 URL")); 694 | const char *bases[] = { 695 | "https://api.yubico.com/wsapi/2.0/verify", 696 | }; 697 | ykclient_set_url_bases(ykc, 1, bases); 698 | ykclient_set_client (ykc, client_id, 20, client_key); 699 | #ifndef TEST_WITHOUT_INTERNET 700 | ret = ykclient_request (ykc, "ccccccbchvthlivuitriujjifivbvtrjkjfirllluurj"); 701 | printf ("ykclient_request (%d): %s\n", ret, ykclient_strerror (ret)); 702 | printf ("used url: %s\n", ykclient_get_last_url (ykc)); 703 | assert (ret == YKCLIENT_REPLAYED_OTP); 704 | #else 705 | printf ("Test SKIPPED\n"); 706 | #endif 707 | 708 | TEST(("Set a mix of bad and good URLs")); 709 | const char *bad_bases[] = { 710 | "https://api2.example.com/wsapi/2.0/verify", 711 | "https://api3.example.com/wsapi/2.0/verify", 712 | "https://api4.example.com/wsapi/2.0/verify", 713 | "https://api5.example.com/wsapi/2.0/verify", 714 | "https://api.yubico.com/wsapi/2.0/verify", 715 | }; 716 | ykclient_set_url_bases(ykc, 5, bad_bases); 717 | ykclient_set_client (ykc, client_id, 20, client_key); 718 | #ifndef TEST_WITHOUT_INTERNET 719 | ret = ykclient_request (ykc, "ccccccbchvthlivuitriujjifivbvtrjkjfirllluurj"); 720 | printf ("ykclient_request (%d): %s\n", ret, ykclient_strerror (ret)); 721 | printf ("used url: %s\n", ykclient_get_last_url (ykc)); 722 | assert (ret == YKCLIENT_REPLAYED_OTP); 723 | #else 724 | printf ("Test SKIPPED\n"); 725 | #endif 726 | 727 | ykclient_done (&ykc); 728 | 729 | TEST(("strerror 0")); 730 | printf ("strerror(0): %s\n", ykclient_strerror (0)); 731 | ret = strcmp(ykclient_strerror (0), "Success"); assert (ret == 0); 732 | 733 | TEST(("strerror BAD_OTP")); 734 | printf ("strerror(BAD_OTP): %s\n", ykclient_strerror (YKCLIENT_BAD_OTP)); 735 | ret = strcmp(ykclient_strerror (YKCLIENT_BAD_OTP), "Yubikey OTP was bad (BAD_OTP)"); assert (ret == 0); 736 | 737 | test_base64(); 738 | 739 | test_hmac(); 740 | 741 | printf ("All tests passed\n"); 742 | 743 | ykclient_global_done(); 744 | 745 | return 0; 746 | } 747 | -------------------------------------------------------------------------------- /tool.c: -------------------------------------------------------------------------------- 1 | /* tool.c --- Command line interface to libykclient. 2 | * 3 | * Copyright (c) 2006-2013 Yubico AB 4 | * Copyright (c) 2012 Secure Mission Solutions 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are 9 | * met: 10 | * 11 | * * Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * * Redistributions in binary form must reproduce the above 15 | * copyright notice, this list of conditions and the following 16 | * disclaimer in the documentation and/or other materials provided 17 | * with the distribution. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | * 31 | */ 32 | 33 | #include "ykclient.h" 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | const char *usage = 44 | "Usage: ykclient [OPTION]... CLIENTID YUBIKEYOTP\n" 45 | "Validate the YUBIKEYOTP one-time-password against the YubiCloud\n" 46 | "using CLIENTID as the client identifier.\n" 47 | "\n" 48 | "Mandatory arguments to long options are mandatory for short options too.\n" 49 | " --help Display this help screen\n" 50 | " --version Display version information\n" 51 | "\n" 52 | " --debug Print debugging information\n" 53 | " --url URL Validation service URL, for example,\n" 54 | " \"https://api.yubico.com/wsapi/2.0/verify\"\n" 55 | " --ca CADIR Path to directory containing Certificate Authoritity,\n" 56 | " e.g., \"/usr/local/etc/CERTS\"\n" 57 | " --cai CAFILE Path to a file holding one or more certificated to\n" 58 | " verify the peer with\n" 59 | " --apikey Key API key for HMAC validation of request/response\n" 60 | " --proxy ip:port Connect to validation service through a proxy,\n" 61 | " e.g., \"socks5h://user:pass@127.0.0.1:1080\"\n" 62 | " --retries MaxRetries Max number of retries in event of transient\n" 63 | " network error\n" 64 | "\n" 65 | "Exit status is 0 on success, 1 if there is a hard failure, 2 if the\n" 66 | "OTP was replayed, 3 for other soft OTP-related failures.\n" 67 | "\n" "Report bugs at .\n"; 68 | 69 | static struct option long_options[] = { 70 | {"url", 1, 0, 'u'}, 71 | {"ca", 1, 0, 'c'}, 72 | {"cai", 1, 0, 'i'}, 73 | {"apikey", 1, 0, 'a'}, 74 | {"proxy", 1, 0, 'p'}, 75 | {"retries", 1, 0, 'r'}, 76 | {"debug", 0, 0, 'd'}, 77 | {"help", 0, 0, 'h'}, 78 | {"version", 0, 0, 'V'}, 79 | {0, 0, 0, 0} 80 | }; 81 | 82 | #define MAX_URLS 10 83 | 84 | /* Parse command line parameters. */ 85 | static void 86 | parse_args (int argc, char *argv[], 87 | unsigned int *client_id, char **token, const char **urls, 88 | int *n_urls, char **ca, char **cai, char **api_key, char **proxy, 89 | int *max_retries, int *debug) 90 | { 91 | char *endptr; 92 | unsigned long input; 93 | 94 | while (1) 95 | { 96 | int option_index = 0; 97 | 98 | int c = getopt_long (argc, argv, "", 99 | long_options, &option_index); 100 | if (c == -1) 101 | break; 102 | 103 | switch (c) 104 | { 105 | case 'a': 106 | if (strlen (optarg) < 16) 107 | { 108 | fprintf (stderr, 109 | "error: API key must be at least 16 characters"); 110 | exit (EXIT_FAILURE); 111 | } 112 | *api_key = optarg; 113 | break; 114 | 115 | case 'd': 116 | *debug = 1; 117 | break; 118 | 119 | case 'u': 120 | if (strncmp ("http://", optarg, 7) != 0 121 | && strncmp ("https://", optarg, 8) != 0) 122 | { 123 | fprintf (stderr, "error: validation url must be http or https"); 124 | exit (EXIT_FAILURE); 125 | } 126 | if (*n_urls >= MAX_URLS) 127 | { 128 | fprintf (stderr, 129 | "error: maximum %d urls can be processed on commandline", 130 | MAX_URLS); 131 | exit (EXIT_FAILURE); 132 | } 133 | urls[*n_urls] = optarg; 134 | *n_urls += 1; 135 | break; 136 | 137 | case 'c': 138 | if (strlen (optarg) < 1) 139 | { 140 | fprintf (stderr, 141 | "error: must give a valid directory containing CAs"); 142 | exit (EXIT_FAILURE); 143 | } 144 | *ca = optarg; 145 | break; 146 | 147 | case 'i': 148 | if (strlen (optarg) < 1) 149 | { 150 | fprintf (stderr, 151 | "error: must give a valid filename with one or more certificates"); 152 | exit (EXIT_FAILURE); 153 | } 154 | *cai = optarg; 155 | break; 156 | 157 | case 'p': 158 | if (strlen(optarg) < 1) 159 | { 160 | fprintf (stderr, "error: must give a valid proxy [scheme]://ip:port"); 161 | exit (EXIT_FAILURE); 162 | } 163 | *proxy = optarg; 164 | break; 165 | 166 | case 'r': 167 | if (strlen (optarg) < 1) 168 | { 169 | fprintf (stderr, 170 | "error: must give number of retries"); 171 | exit (EXIT_FAILURE); 172 | } 173 | input = strtoul (optarg, &endptr, 10); 174 | if (endptr == optarg || *endptr != '\0' || input > INT_MAX) 175 | { 176 | fprintf (stderr, 177 | "error: number of retries must be an integer between 0 and %d.", INT_MAX); 178 | exit (EXIT_FAILURE); 179 | } 180 | *max_retries = input; 181 | break; 182 | 183 | case 'h': 184 | printf ("%s", usage); 185 | exit (EXIT_SUCCESS); 186 | break; 187 | 188 | case 'V': 189 | printf ("%s\n", VERSION); 190 | exit (EXIT_SUCCESS); 191 | break; 192 | } 193 | } 194 | 195 | if (argc - optind != 2) 196 | { 197 | printf ("%s", usage); 198 | exit (EXIT_SUCCESS); 199 | } 200 | 201 | /* Now get mandatory numeric client_id */ 202 | input = strtol (argv[optind++], &endptr, 10); 203 | if (endptr == optarg || *endptr != '\0' || input > INT_MAX || input == 0) 204 | { 205 | fprintf (stderr, "error: client identity must be a non-zero integer."); 206 | exit (EXIT_FAILURE); 207 | } 208 | 209 | *client_id = input; 210 | 211 | /* Likewise mandatory OTP token */ 212 | *token = argv[optind++]; 213 | if (strlen (*token) < 32) 214 | { 215 | fprintf (stderr, 216 | "error: modhex encoded token must be at least 32 characters"); 217 | exit (EXIT_FAILURE); 218 | } 219 | } 220 | 221 | int 222 | main (int argc, char *argv[]) 223 | { 224 | unsigned int client_id; 225 | char *token, *ca = NULL, *api_key = NULL, *cai = NULL, *proxy = NULL; 226 | const char *urls[MAX_URLS]; 227 | int n_urls = 0; 228 | int max_retries = DEFAULT_MAX_RETRIES; 229 | int debug = 0; 230 | ykclient_rc ret; 231 | ykclient_t *ykc = NULL; 232 | 233 | parse_args (argc, argv, &client_id, &token, urls, &n_urls, &ca, &cai, 234 | &api_key, &proxy, &max_retries, &debug); 235 | 236 | ret = ykclient_init (&ykc); 237 | if (ret != YKCLIENT_OK) 238 | return EXIT_FAILURE; 239 | 240 | if (ca) 241 | { 242 | ykclient_set_ca_path (ykc, ca); 243 | } 244 | 245 | if (cai) 246 | { 247 | ykclient_set_ca_info (ykc, cai); 248 | } 249 | if (proxy) 250 | { 251 | ykclient_set_proxy (ykc, proxy); 252 | } 253 | if (max_retries != DEFAULT_MAX_RETRIES) 254 | { 255 | ykclient_set_max_retries (ykc, max_retries); 256 | } 257 | 258 | if (debug) 259 | { 260 | fprintf (stderr, "Input:\n"); 261 | if (n_urls) 262 | { 263 | int i; 264 | for (i = 0; i < n_urls; i++) 265 | { 266 | fprintf (stderr, " validation URL %d: %s\n", i + 1, urls[i]); 267 | } 268 | } 269 | if (ca) 270 | fprintf (stderr, " CA Path: %s\n", ca); 271 | if (cai) 272 | fprintf (stderr, " CA Info: %s\n", cai); 273 | fprintf (stderr, " client id: %u\n", client_id); 274 | fprintf (stderr, " token: %s\n", token); 275 | if (api_key != NULL) 276 | fprintf (stderr, " api key: %s\n", api_key); 277 | if (proxy != NULL) 278 | fprintf (stderr, "Using proxy: %s\n", proxy); 279 | } 280 | 281 | ret = ykclient_verify_otp_v2 (ykc, token, client_id, NULL, n_urls, 282 | urls, api_key); 283 | 284 | if (debug) 285 | { 286 | const ykclient_server_response_t *srv_response = ykclient_get_server_response (ykc); 287 | printf ("Response from: %s\n", ykclient_get_last_url (ykc)); 288 | printf ("Verification output (%d): %s\n", ret, ykclient_strerror (ret)); 289 | printf (" otp: %s\n", ykclient_server_response_get (srv_response, "otp")); 290 | printf (" nonce: %s\n", ykclient_server_response_get (srv_response, "nonce")); 291 | printf (" t: %s\n", ykclient_server_response_get (srv_response, "t")); 292 | printf (" timestamp: %s\n", ykclient_server_response_get (srv_response, "timestamp")); 293 | printf (" sessioncounter: %s\n", ykclient_server_response_get (srv_response, "sessioncounter")); 294 | printf (" sessionuse: %s\n", ykclient_server_response_get (srv_response, "sessionuse")); 295 | printf (" sl: %s\n", ykclient_server_response_get (srv_response, "sl")); 296 | printf (" status: %s\n", ykclient_server_response_get (srv_response, "status")); 297 | } 298 | 299 | ykclient_done(&ykc); 300 | 301 | if (ret == YKCLIENT_REPLAYED_OTP) 302 | return 2; 303 | else if (ret != YKCLIENT_OK) 304 | return 3; 305 | 306 | return EXIT_SUCCESS; 307 | } 308 | -------------------------------------------------------------------------------- /usha.c: -------------------------------------------------------------------------------- 1 | /**************************** usha.c ****************************/ 2 | /******************** See RFC 4634 for details ******************/ 3 | /* 4 | * Description: 5 | * This file implements a unified interface to the SHA algorithms. 6 | */ 7 | 8 | #include "sha.h" 9 | 10 | /* 11 | * USHAReset 12 | * 13 | * Description: 14 | * This function will initialize the SHA Context in preparation 15 | * for computing a new SHA message digest. 16 | * 17 | * Parameters: 18 | * context: [in/out] 19 | * The context to reset. 20 | * whichSha: [in] 21 | * Selects which SHA reset to call 22 | * 23 | * Returns: 24 | * sha Error Code. 25 | * 26 | */ 27 | int 28 | USHAReset (USHAContext * ctx, enum SHAversion whichSha) 29 | { 30 | if (ctx) 31 | { 32 | ctx->whichSha = whichSha; 33 | switch (whichSha) 34 | { 35 | case SHA1: 36 | return SHA1Reset ((SHA1Context *) & ctx->ctx); 37 | case SHA224: 38 | return SHA224Reset ((SHA224Context *) & ctx->ctx); 39 | case SHA256: 40 | return SHA256Reset ((SHA256Context *) & ctx->ctx); 41 | case SHA384: 42 | return SHA384Reset ((SHA384Context *) & ctx->ctx); 43 | case SHA512: 44 | return SHA512Reset ((SHA512Context *) & ctx->ctx); 45 | default: 46 | return shaBadParam; 47 | } 48 | } 49 | else 50 | { 51 | return shaNull; 52 | } 53 | } 54 | 55 | /* 56 | * USHAInput 57 | * 58 | * Description: 59 | * This function accepts an array of octets as the next portion 60 | * of the message. 61 | * 62 | * Parameters: 63 | * context: [in/out] 64 | * The SHA context to update 65 | * message_array: [in] 66 | * An array of characters representing the next portion of 67 | * the message. 68 | * length: [in] 69 | * The length of the message in message_array 70 | * 71 | * Returns: 72 | * sha Error Code. 73 | * 74 | */ 75 | int 76 | USHAInput (USHAContext * ctx, const uint8_t * bytes, unsigned int bytecount) 77 | { 78 | if (ctx) 79 | { 80 | switch (ctx->whichSha) 81 | { 82 | case SHA1: 83 | return SHA1Input ((SHA1Context *) & ctx->ctx, bytes, bytecount); 84 | case SHA224: 85 | return SHA224Input ((SHA224Context *) & ctx->ctx, bytes, bytecount); 86 | case SHA256: 87 | return SHA256Input ((SHA256Context *) & ctx->ctx, bytes, bytecount); 88 | case SHA384: 89 | return SHA384Input ((SHA384Context *) & ctx->ctx, bytes, bytecount); 90 | case SHA512: 91 | return SHA512Input ((SHA512Context *) & ctx->ctx, bytes, bytecount); 92 | default: 93 | return shaBadParam; 94 | } 95 | } 96 | else 97 | { 98 | return shaNull; 99 | } 100 | } 101 | 102 | /* 103 | * USHAFinalBits 104 | * 105 | * Description: 106 | * This function will add in any final bits of the message. 107 | * 108 | * Parameters: 109 | * context: [in/out] 110 | * The SHA context to update 111 | * message_bits: [in] 112 | * The final bits of the message, in the upper portion of the 113 | * byte. (Use 0b###00000 instead of 0b00000### to input the 114 | * three bits ###.) 115 | * length: [in] 116 | * The number of bits in message_bits, between 1 and 7. 117 | * 118 | * Returns: 119 | * sha Error Code. 120 | */ 121 | int 122 | USHAFinalBits (USHAContext * ctx, const uint8_t bits, unsigned int bitcount) 123 | { 124 | if (ctx) 125 | { 126 | switch (ctx->whichSha) 127 | { 128 | case SHA1: 129 | return SHA1FinalBits ((SHA1Context *) & ctx->ctx, bits, bitcount); 130 | case SHA224: 131 | return SHA224FinalBits ((SHA224Context *) & ctx->ctx, bits, 132 | bitcount); 133 | case SHA256: 134 | return SHA256FinalBits ((SHA256Context *) & ctx->ctx, bits, 135 | bitcount); 136 | case SHA384: 137 | return SHA384FinalBits ((SHA384Context *) & ctx->ctx, bits, 138 | bitcount); 139 | case SHA512: 140 | return SHA512FinalBits ((SHA512Context *) & ctx->ctx, bits, 141 | bitcount); 142 | default: 143 | return shaBadParam; 144 | } 145 | } 146 | else 147 | { 148 | return shaNull; 149 | } 150 | } 151 | 152 | /* 153 | * USHAResult 154 | * 155 | * Description: 156 | * This function will return the 160-bit message digest into the 157 | * Message_Digest array provided by the caller. 158 | * NOTE: The first octet of hash is stored in the 0th element, 159 | * the last octet of hash in the 19th element. 160 | * 161 | * Parameters: 162 | * context: [in/out] 163 | * The context to use to calculate the SHA-1 hash. 164 | * Message_Digest: [out] 165 | * Where the digest is returned. 166 | * 167 | * Returns: 168 | * sha Error Code. 169 | * 170 | */ 171 | int 172 | USHAResult (USHAContext * ctx, uint8_t Message_Digest[USHAMaxHashSize]) 173 | { 174 | if (ctx) 175 | { 176 | switch (ctx->whichSha) 177 | { 178 | case SHA1: 179 | return SHA1Result ((SHA1Context *) & ctx->ctx, Message_Digest); 180 | case SHA224: 181 | return SHA224Result ((SHA224Context *) & ctx->ctx, Message_Digest); 182 | case SHA256: 183 | return SHA256Result ((SHA256Context *) & ctx->ctx, Message_Digest); 184 | case SHA384: 185 | return SHA384Result ((SHA384Context *) & ctx->ctx, Message_Digest); 186 | case SHA512: 187 | return SHA512Result ((SHA512Context *) & ctx->ctx, Message_Digest); 188 | default: 189 | return shaBadParam; 190 | } 191 | } 192 | else 193 | { 194 | return shaNull; 195 | } 196 | } 197 | 198 | /* 199 | * USHABlockSize 200 | * 201 | * Description: 202 | * This function will return the blocksize for the given SHA 203 | * algorithm. 204 | * 205 | * Parameters: 206 | * whichSha: 207 | * which SHA algorithm to query 208 | * 209 | * Returns: 210 | * block size 211 | * 212 | */ 213 | int 214 | USHABlockSize (enum SHAversion whichSha) 215 | { 216 | switch (whichSha) 217 | { 218 | case SHA1: 219 | return SHA1_Message_Block_Size; 220 | case SHA224: 221 | return SHA224_Message_Block_Size; 222 | case SHA256: 223 | return SHA256_Message_Block_Size; 224 | case SHA384: 225 | return SHA384_Message_Block_Size; 226 | default: 227 | case SHA512: 228 | return SHA512_Message_Block_Size; 229 | } 230 | } 231 | 232 | /* 233 | * USHAHashSize 234 | * 235 | * Description: 236 | * This function will return the hashsize for the given SHA 237 | * algorithm. 238 | * 239 | * Parameters: 240 | * whichSha: 241 | * which SHA algorithm to query 242 | * 243 | * Returns: 244 | * hash size 245 | * 246 | */ 247 | int 248 | USHAHashSize (enum SHAversion whichSha) 249 | { 250 | switch (whichSha) 251 | { 252 | case SHA1: 253 | return SHA1HashSize; 254 | case SHA224: 255 | return SHA224HashSize; 256 | case SHA256: 257 | return SHA256HashSize; 258 | case SHA384: 259 | return SHA384HashSize; 260 | default: 261 | case SHA512: 262 | return SHA512HashSize; 263 | } 264 | } 265 | 266 | /* 267 | * USHAHashSizeBits 268 | * 269 | * Description: 270 | * This function will return the hashsize for the given SHA 271 | * algorithm, expressed in bits. 272 | * 273 | * Parameters: 274 | * whichSha: 275 | * which SHA algorithm to query 276 | * 277 | * Returns: 278 | * hash size in bits 279 | * 280 | */ 281 | int 282 | USHAHashSizeBits (enum SHAversion whichSha) 283 | { 284 | switch (whichSha) 285 | { 286 | case SHA1: 287 | return SHA1HashSizeBits; 288 | case SHA224: 289 | return SHA224HashSizeBits; 290 | case SHA256: 291 | return SHA256HashSizeBits; 292 | case SHA384: 293 | return SHA384HashSizeBits; 294 | default: 295 | case SHA512: 296 | return SHA512HashSizeBits; 297 | } 298 | } 299 | -------------------------------------------------------------------------------- /ykclient.h: -------------------------------------------------------------------------------- 1 | /* ykclient.h --- Prototypes for Yubikey OTP validation client library. 2 | * 3 | * Written by Simon Josefsson . 4 | * Copyright (c) 2006-2013 Yubico AB 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are 9 | * met: 10 | * 11 | * * Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * * Redistributions in binary form must reproduce the above 15 | * copyright notice, this list of conditions and the following 16 | * disclaimer in the documentation and/or other materials provided 17 | * with the distribution. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | * 31 | */ 32 | 33 | #ifndef YKCLIENT_H 34 | #define YKCLIENT_H 35 | 36 | #include 37 | #include 38 | 39 | #include 40 | #include 41 | #include 42 | 43 | #define DEFAULT_MAX_RETRIES 3 44 | 45 | #ifdef __cplusplus 46 | extern "C" 47 | { 48 | #endif 49 | 50 | /* Must be called successfully before using any other functions. */ 51 | extern ykclient_rc ykclient_global_init (void); 52 | extern void ykclient_global_done (void); 53 | 54 | typedef struct ykclient_st ykclient_t; 55 | 56 | typedef struct ykclient_handle_st ykclient_handle_t; 57 | 58 | extern ykclient_rc ykclient_init (ykclient_t ** ykc); 59 | 60 | extern void ykclient_done (ykclient_t ** ykc); 61 | 62 | extern ykclient_rc ykclient_handle_init (ykclient_t * ykc, 63 | ykclient_handle_t ** ykh); 64 | 65 | extern void ykclient_handle_cleanup (ykclient_handle_t * ykh); 66 | 67 | extern void ykclient_handle_done (ykclient_handle_t ** ykh); 68 | 69 | /* If value is 0 the authenticity of the signature returned by the 70 | server in response to the request won't be verified. */ 71 | extern void ykclient_set_verify_signature (ykclient_t * ykc, int value); 72 | 73 | extern const char *ykclient_strerror (ykclient_rc ret); 74 | 75 | extern void ykclient_set_client (ykclient_t * ykc, 76 | unsigned int client_id, 77 | size_t keylen, const char *key); 78 | 79 | extern ykclient_rc ykclient_set_client_hex (ykclient_t * ykc, 80 | unsigned int client_id, 81 | const char *key); 82 | 83 | extern ykclient_rc ykclient_set_client_b64 (ykclient_t * ykc, 84 | unsigned int client_id, 85 | const char *key); 86 | 87 | extern ykclient_rc ykclient_set_url_template (ykclient_t * ykc, 88 | const char *url_template); 89 | 90 | extern ykclient_rc ykclient_set_url_templates (ykclient_t * ykc, 91 | size_t num_templates, 92 | const char **url_templates); 93 | 94 | extern ykclient_rc ykclient_set_url_bases (ykclient_t * ykc, 95 | size_t num_templates, 96 | const char **url_templates); 97 | 98 | extern void ykclient_set_ca_path (ykclient_t * ykc, const char *ca_path); 99 | 100 | extern void ykclient_set_ca_info (ykclient_t * ykc, const char *ca_info); 101 | 102 | extern void ykclient_set_proxy (ykclient_t * ykc, const char *proxy); 103 | 104 | /* 105 | * Set the nonce. A default nonce is generated in ykclient_init(), but 106 | * if you either want to specify your own nonce, or want to remove the 107 | * nonce (needed to send signed requests to v1 validation servers), 108 | * you must call this function. Set nonce to NULL to disable it. 109 | */ 110 | extern void ykclient_set_nonce (ykclient_t * ykc, char *nonce); 111 | 112 | extern void ykclient_set_max_retries (ykclient_t * ykc, int retries); 113 | 114 | 115 | extern const char *ykclient_get_last_url (ykclient_t * ykc); 116 | 117 | extern ykclient_rc ykclient_request_process (ykclient_t * ykc, 118 | ykclient_handle_t * ykh, 119 | const char *yubikey); 120 | 121 | extern ykclient_rc ykclient_request (ykclient_t * ykc, 122 | const char *yubikey_otp); 123 | 124 | /* One call interface for validation protocol 1.x, with default URL. */ 125 | extern ykclient_rc ykclient_verify_otp (const char *yubikey_otp, 126 | unsigned int client_id, 127 | const char *hexkey); 128 | 129 | /* One call interface for validation protocol 2.0 and/or non-default URL. */ 130 | extern ykclient_rc ykclient_verify_otp_v2 (ykclient_t * ykc_in, 131 | const char *yubikey_otp, 132 | unsigned int client_id, 133 | const char *hexkey, 134 | size_t urlcount, 135 | const char **urls, 136 | const char *api_key); 137 | 138 | /* Fetch out the server response form the last query */ 139 | extern const ykclient_server_response_t *ykclient_get_server_response(ykclient_t *ykc); 140 | #ifdef __cplusplus 141 | } 142 | #endif 143 | 144 | #endif 145 | -------------------------------------------------------------------------------- /ykclient_errors.h: -------------------------------------------------------------------------------- 1 | /* ykclient_errors.h --- Error codes used by ykclient. 2 | * 3 | * Written by Simon Josefsson . 4 | * Copyright (c) 2006-2013 Yubico AB 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are 9 | * met: 10 | * 11 | * * Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * * Redistributions in binary form must reproduce the above 15 | * copyright notice, this list of conditions and the following 16 | * disclaimer in the documentation and/or other materials provided 17 | * with the distribution. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | * 31 | */ 32 | 33 | #ifndef YKCLIENT_ERRORS_H 34 | #define YKCLIENT_ERRORS_H 35 | 36 | typedef enum 37 | { 38 | /* Official yubikey client API errors. */ 39 | YKCLIENT_OK = 0, 40 | YKCLIENT_BAD_OTP, 41 | YKCLIENT_REPLAYED_OTP, 42 | YKCLIENT_BAD_SIGNATURE, 43 | YKCLIENT_MISSING_PARAMETER, 44 | YKCLIENT_NO_SUCH_CLIENT, 45 | YKCLIENT_OPERATION_NOT_ALLOWED, 46 | YKCLIENT_BACKEND_ERROR, 47 | YKCLIENT_NOT_ENOUGH_ANSWERS, 48 | YKCLIENT_REPLAYED_REQUEST, 49 | /* Other implementation specific errors. */ 50 | YKCLIENT_OUT_OF_MEMORY = 100, 51 | YKCLIENT_PARSE_ERROR, 52 | YKCLIENT_FORMAT_ERROR, 53 | YKCLIENT_CURL_INIT_ERROR, 54 | YKCLIENT_HMAC_ERROR, 55 | YKCLIENT_HEX_DECODE_ERROR, 56 | YKCLIENT_BASE64_DECODE_ERROR, 57 | YKCLIENT_BAD_SERVER_SIGNATURE, 58 | YKCLIENT_NOT_IMPLEMENTED, 59 | YKCLIENT_CURL_PERFORM_ERROR, 60 | YKCLIENT_BAD_INPUT, 61 | YKCLIENT_HANDLE_NOT_REINIT 62 | } ykclient_rc; 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /ykclient_server_response.c: -------------------------------------------------------------------------------- 1 | /* ykclient_server_response.c --- Server response parsing and validation. 2 | * 3 | * Written by Sebastien Martini . 4 | * Copyright (c) 2011-2013 Yubico AB 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are 9 | * met: 10 | * 11 | * * Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * * Redistributions in binary form must reproduce the above 15 | * copyright notice, this list of conditions and the following 16 | * disclaimer in the documentation and/or other materials provided 17 | * with the distribution. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | * 31 | */ 32 | 33 | #include "ykclient_server_response.h" 34 | 35 | #include "ykclient.h" 36 | 37 | #include 38 | #include 39 | #include 40 | 41 | #include "sha.h" 42 | #include "cdecode.h" 43 | 44 | 45 | /* Parameters' manipulation functions */ 46 | 47 | static void 48 | parameter_free (ykclient_parameter_t * param) 49 | { 50 | if (param == NULL) 51 | return; 52 | 53 | if (param->key) 54 | free (param->key); 55 | if (param->value) 56 | free (param->value); 57 | 58 | free (param); 59 | } 60 | 61 | /* Inserts elem in front of params. */ 62 | static void 63 | list_parameter_insert_front (ykclient_parameters_t ** params, 64 | ykclient_parameter_t * elem) 65 | { 66 | if (params == NULL || elem == NULL) 67 | return; 68 | 69 | ykclient_parameters_t *new_node = malloc (sizeof (ykclient_parameters_t)); 70 | if (new_node == NULL) 71 | return; 72 | memset (new_node, 0, (sizeof (ykclient_parameters_t))); 73 | 74 | new_node->parameter = elem; 75 | new_node->next = NULL; 76 | 77 | if (*params == NULL) 78 | { 79 | *params = new_node; 80 | return; 81 | } 82 | 83 | new_node->next = *params; 84 | *params = new_node; 85 | } 86 | 87 | /* Keys comparison function. It compares two keys, and returns 1 if 88 | the first precedes the second. */ 89 | static int 90 | alphanum_less_than (const ykclient_parameter_t * rhs, 91 | const ykclient_parameter_t * lhs) 92 | { 93 | if (rhs == NULL || lhs == NULL) 94 | return -1; 95 | 96 | if (strcmp (rhs->key, lhs->key) < 0) 97 | return 1; 98 | return 0; 99 | } 100 | 101 | /* Inserts elem into params. The position where elem must inserted is 102 | determined by cmp_func. cmp_func must be a strict weak ordering binary 103 | predicate. */ 104 | static void 105 | list_parameter_insert_ord (ykclient_parameters_t ** params, 106 | ykclient_parameter_t * elem, 107 | int (*cmp_func) (const ykclient_parameter_t * rhs, 108 | const ykclient_parameter_t * lhs)) 109 | { 110 | if (elem == NULL) 111 | return; 112 | 113 | ykclient_parameters_t *iter = *params; 114 | ykclient_parameters_t *prev = NULL; 115 | 116 | for (; iter != NULL; iter = iter->next) 117 | { 118 | const int result = cmp_func (elem, iter->parameter); 119 | if (result == -1) 120 | return; /* error */ 121 | if (result == 1) 122 | break; 123 | prev = iter; 124 | } 125 | 126 | list_parameter_insert_front (&iter, elem); 127 | if (prev != NULL) 128 | prev->next = iter; 129 | else 130 | *params = iter; 131 | } 132 | 133 | static void 134 | list_parameter_free (ykclient_parameters_t * params) 135 | { 136 | ykclient_parameters_t *iter = params; 137 | while (iter != NULL) 138 | { 139 | parameter_free (iter->parameter); 140 | ykclient_parameters_t *current = iter; 141 | iter = iter->next; 142 | free (current); 143 | } 144 | } 145 | 146 | 147 | /* Server's response functions */ 148 | 149 | ykclient_server_response_t * 150 | ykclient_server_response_init (void) 151 | { 152 | ykclient_server_response_t *serv_response = 153 | malloc (sizeof (ykclient_server_response_t)); 154 | if (serv_response == NULL) 155 | return NULL; 156 | memset (serv_response, 0, (sizeof (ykclient_server_response_t))); 157 | serv_response->signature = NULL; 158 | serv_response->parameters = NULL; 159 | return serv_response; 160 | } 161 | 162 | void 163 | ykclient_server_response_free (ykclient_server_response_t * response) 164 | { 165 | if (response == NULL) 166 | return; 167 | list_parameter_free (response->parameters); 168 | parameter_free (response->signature); 169 | free (response); 170 | } 171 | 172 | 173 | /* Server's response parsing functions */ 174 | 175 | /* Returns 1 if c is a whitespace or a line break character, 0 otherwise. */ 176 | static int 177 | is_ws_or_lb (char c) 178 | { 179 | switch (c) 180 | { 181 | /* Line breaks */ 182 | case '\n': 183 | case '\r': 184 | /* Spaces */ 185 | case ' ': 186 | case '\t': 187 | return 1; 188 | default: 189 | return 0; 190 | } 191 | return 0; 192 | } 193 | 194 | /* Trims leading whitespaces and line breaks. */ 195 | static void 196 | trim_ws_and_lb (char **s) 197 | { 198 | if (s == NULL || *s == NULL) 199 | return; 200 | 201 | char *pos = *s; 202 | while (*pos != '\0' && is_ws_or_lb (*pos)) 203 | ++pos; 204 | *s = pos; 205 | } 206 | 207 | /* Parses and builds the next parameter param from s, moves response's pointer 208 | to the immediate right character. Returns 0 if it succeeds. */ 209 | static ykclient_rc 210 | parse_next_parameter (char **s, ykclient_parameter_t * param) 211 | { 212 | if (s == NULL || *s == NULL || param == NULL) 213 | return YKCLIENT_PARSE_ERROR; 214 | char *pos = *s; 215 | int index = 0; 216 | 217 | /* key parsing */ 218 | while (*(pos + index) != '\0' && *(pos + index) != '=') 219 | ++index; 220 | if (*(pos + index) == '\0') 221 | return YKCLIENT_PARSE_ERROR; 222 | 223 | param->key = malloc (index + 1); 224 | if (param->key == NULL) 225 | { 226 | return YKCLIENT_OUT_OF_MEMORY; 227 | } 228 | strncpy (param->key, pos, index); 229 | param->key[index] = '\0'; 230 | 231 | /* value parsing */ 232 | pos += index + 1; 233 | index = 0; 234 | while (*(pos + index) != '\0' && !is_ws_or_lb (*(pos + index))) 235 | ++index; 236 | if (*(pos + index) == '\0') 237 | { 238 | parameter_free (param); 239 | return YKCLIENT_PARSE_ERROR; 240 | } 241 | 242 | param->value = malloc (index + 1); 243 | if (param->value == NULL) 244 | { 245 | parameter_free (param); 246 | return YKCLIENT_OUT_OF_MEMORY; 247 | } 248 | strncpy (param->value, pos, index); 249 | param->value[index] = '\0'; 250 | 251 | pos += index; 252 | *s = pos; 253 | return 0; 254 | } 255 | 256 | ykclient_rc 257 | ykclient_server_response_parse (char *response, 258 | ykclient_server_response_t * serv_response) 259 | { 260 | if (response == NULL || serv_response == NULL) 261 | return YKCLIENT_PARSE_ERROR; 262 | 263 | trim_ws_and_lb (&response); 264 | while (*response != '\0') 265 | { 266 | ykclient_parameter_t *param = malloc (sizeof (ykclient_parameter_t)); 267 | if (param == NULL) 268 | return YKCLIENT_OUT_OF_MEMORY; 269 | memset (param, 0, (sizeof (ykclient_parameter_t))); 270 | int ret = parse_next_parameter (&response, param); 271 | if (ret) 272 | return ret; 273 | 274 | if (!strcmp (param->key, "h")) 275 | serv_response->signature = param; 276 | else 277 | /* Parameters are alphanumerically ordered. */ 278 | list_parameter_insert_ord (&serv_response->parameters, param, 279 | alphanum_less_than); 280 | trim_ws_and_lb (&response); 281 | } 282 | 283 | /* We expect at least one parameter along its mandatory signature. */ 284 | if (serv_response->signature == NULL) 285 | return YKCLIENT_BAD_SERVER_SIGNATURE; 286 | if (serv_response->parameters == NULL) 287 | return YKCLIENT_PARSE_ERROR; 288 | return 0; 289 | } 290 | 291 | int 292 | ykclient_server_response_verify_signature (const ykclient_server_response_t * 293 | serv_response, const char *key, 294 | int key_length) 295 | { 296 | if (serv_response == NULL || key == NULL || key_length < 0) 297 | return 1; 298 | 299 | HMACContext ctx; 300 | if (hmacReset (&ctx, SHA1, (const unsigned char *) key, key_length)) 301 | return 1; 302 | 303 | /* Iterate over parameters and feed the hmac. */ 304 | ykclient_parameters_t *iter = serv_response->parameters; 305 | for (; iter != NULL; iter = iter->next) 306 | { 307 | if (hmacInput (&ctx, (unsigned char *) iter->parameter->key, 308 | strlen (iter->parameter->key))) 309 | return 1; 310 | if (hmacInput (&ctx, (const unsigned char *) "=", 1)) 311 | return 1; 312 | if (hmacInput (&ctx, (unsigned char *) iter->parameter->value, 313 | strlen (iter->parameter->value))) 314 | return 1; 315 | if (iter->next != NULL 316 | && hmacInput (&ctx, (const unsigned char *) "&", 1)) 317 | return 1; 318 | } 319 | 320 | uint8_t digest[SHA1HashSize + 1]; 321 | if (hmacResult (&ctx, digest)) 322 | return 1; 323 | 324 | if (serv_response->signature == NULL || 325 | serv_response->signature->value == NULL) 326 | return 1; 327 | 328 | char server_digest[SHA1HashSize + 1]; 329 | base64_decodestate b64; 330 | base64_init_decodestate (&b64); 331 | if (base64_decode_block (serv_response->signature->value, 332 | strlen (serv_response->signature->value), 333 | server_digest, &b64) != SHA1HashSize) 334 | return 1; 335 | 336 | if (memcmp (server_digest, digest, SHA1HashSize) != 0) 337 | return 1; 338 | return 0; 339 | } 340 | 341 | char * 342 | ykclient_server_response_get (const ykclient_server_response_t * 343 | serv_response, const char *key) 344 | { 345 | if (serv_response == NULL || key == NULL) 346 | return NULL; 347 | 348 | ykclient_parameters_t *iter = serv_response->parameters; 349 | for (; iter != NULL; iter = iter->next) 350 | if (!strcmp (iter->parameter->key, key)) 351 | return iter->parameter->value; 352 | return NULL; 353 | } 354 | -------------------------------------------------------------------------------- /ykclient_server_response.h: -------------------------------------------------------------------------------- 1 | /* ykclient_server_response.h --- Server response parsing and validation. 2 | * 3 | * Written by Sebastien Martini . 4 | * Copyright (c) 2011-2013 Yubico AB 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions are 9 | * met: 10 | * 11 | * * Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * * Redistributions in binary form must reproduce the above 15 | * copyright notice, this list of conditions and the following 16 | * disclaimer in the documentation and/or other materials provided 17 | * with the distribution. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | * 31 | */ 32 | 33 | #ifndef YKCLIENT_SERVER_RESPONSE_H 34 | #define YKCLIENT_SERVER_RESPONSE_H 35 | 36 | #include 37 | 38 | /* Example: 39 | key: status 40 | value: OK 41 | */ 42 | typedef struct ykclient_parameter_st 43 | { 44 | char *key; 45 | char *value; 46 | } ykclient_parameter_t; 47 | 48 | typedef struct ykclient_parameters_st 49 | { 50 | ykclient_parameter_t *parameter; 51 | struct ykclient_parameters_st *next; 52 | } ykclient_parameters_t; 53 | 54 | typedef struct ykclient_server_response_st 55 | { 56 | ykclient_parameter_t *signature; 57 | ykclient_parameters_t *parameters; 58 | } ykclient_server_response_t; 59 | 60 | 61 | /* Returns NULL if it fails. */ 62 | extern ykclient_server_response_t *ykclient_server_response_init (void); 63 | 64 | /* Frees allocated data structures. */ 65 | extern void ykclient_server_response_free (ykclient_server_response_t 66 | * response); 67 | 68 | /* Parses server's response and builds a list of parameters and isolates 69 | the corresponding signature parameter. Returns 0 if it succeeds. */ 70 | extern ykclient_rc 71 | ykclient_server_response_parse (char *response, 72 | ykclient_server_response_t * serv_response); 73 | 74 | /* Iterates the parameters buils a HMAC-SHA1 and checks it matches the 75 | signature returned by the server. This function returns 0 if the signature 76 | is valid, 1 otherwise. */ 77 | extern int 78 | ykclient_server_response_verify_signature (const ykclient_server_response_t 79 | * serv_response, 80 | const char *key, int key_length); 81 | 82 | /* Returns value associated to key or NULL if unmatched. The caller doesn't 83 | take ownership of the returned value. */ 84 | extern char *ykclient_server_response_get (const ykclient_server_response_t * 85 | serv_response, const char *key); 86 | 87 | #endif /* YKCLIENT_SERVER_RESPONSE_H */ 88 | -------------------------------------------------------------------------------- /ykclient_version.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2013 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 | 33 | #define _GNU_SOURCE 34 | #include 35 | 36 | /* From http://article.gmane.org/gmane.os.freebsd.devel.hackers/23606 */ 37 | int 38 | my_strverscmp (const char *s1, const char *s2) 39 | { 40 | static const char *digits = "0123456789"; 41 | int ret, lz1, lz2; 42 | size_t p1, p2; 43 | 44 | p1 = strcspn (s1, digits); 45 | p2 = strcspn (s2, digits); 46 | while (p1 == p2 && s1[p1] != '\0' && s2[p2] != '\0') 47 | { 48 | /* Different prefix */ 49 | if ((ret = strncmp (s1, s2, p1)) != 0) 50 | return ret; 51 | 52 | s1 += p1; 53 | s2 += p2; 54 | 55 | lz1 = lz2 = 0; 56 | if (*s1 == '0') 57 | lz1 = 1; 58 | if (*s2 == '0') 59 | lz2 = 1; 60 | 61 | if (lz1 > lz2) 62 | return -1; 63 | else if (lz1 < lz2) 64 | return 1; 65 | else if (lz1 == 1) 66 | { 67 | /* 68 | * If the common prefix for s1 and s2 consists only of zeros, then the 69 | * "longer" number has to compare less. Otherwise the comparison needs 70 | * to be numerical (just fallthrough). See 71 | * http://refspecs.freestandards.org/LSB_2.0.1/LSB-generic/ 72 | * LSB-generic/baselib-strverscmp.html 73 | */ 74 | while (*s1 == '0' && *s2 == '0') 75 | { 76 | ++s1; 77 | ++s2; 78 | } 79 | 80 | p1 = strspn (s1, digits); 81 | p2 = strspn (s2, digits); 82 | 83 | /* Catch empty strings */ 84 | if (p1 == 0 && p2 > 0) 85 | return 1; 86 | else if (p2 == 0 && p1 > 0) 87 | return -1; 88 | 89 | /* Prefixes are not same */ 90 | if (*s1 != *s2 && *s1 != '0' && *s2 != '0') 91 | { 92 | if (p1 < p2) 93 | return 1; 94 | else if (p1 > p2) 95 | return -1; 96 | } 97 | else 98 | { 99 | if (p1 < p2) 100 | ret = strncmp (s1, s2, p1); 101 | else if (p1 > p2) 102 | ret = strncmp (s1, s2, p2); 103 | if (ret != 0) 104 | return ret; 105 | } 106 | } 107 | 108 | p1 = strspn (s1, digits); 109 | p2 = strspn (s2, digits); 110 | 111 | if (p1 < p2) 112 | return -1; 113 | else if (p1 > p2) 114 | return 1; 115 | else if ((ret = strncmp (s1, s2, p1)) != 0) 116 | return ret; 117 | 118 | /* Numbers are equal or not present, try with next ones. */ 119 | s1 += p1; 120 | s2 += p2; 121 | p1 = strcspn (s1, digits); 122 | p2 = strcspn (s2, digits); 123 | } 124 | 125 | return strcmp (s1, s2); 126 | } 127 | 128 | const char * 129 | ykclient_check_version (const char *req_version) 130 | { 131 | if (!req_version 132 | || my_strverscmp (req_version, YKCLIENT_VERSION_STRING) <= 0) 133 | return YKCLIENT_VERSION_STRING; 134 | 135 | return NULL; 136 | } 137 | -------------------------------------------------------------------------------- /ykclient_version.h.in: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 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 YKCLIENT_VERSION_H 31 | #define YKCLIENT_VERSION_H 32 | 33 | #ifdef __cplusplus 34 | extern "C" 35 | { 36 | #endif 37 | 38 | /** 39 | * YKCLIENT_VERSION_STRING 40 | * 41 | * Pre-processor symbol with a string that describe the header file 42 | * version number. Used together with ykclient_check_version() to 43 | * verify header file and run-time library consistency. 44 | */ 45 | #define YKCLIENT_VERSION_STRING "@VERSION@" 46 | 47 | /** 48 | * YKCLIENT_VERSION_NUMBER 49 | * 50 | * Pre-processor symbol with a hexadecimal value describing the header 51 | * file version number. For example, when the header version is 1.2.3 52 | * this symbol will have the value 0x01020300. The last two digits 53 | * are only used between public releases, and will otherwise be 00. 54 | */ 55 | #define YKCLIENT_VERSION_NUMBER @YKCLIENT_VERSION_NUMBER@ 56 | 57 | /** 58 | * YKCLIENT_VERSION_MAJOR 59 | * 60 | * Pre-processor symbol with a decimal value that describe the major 61 | * level of the header file version number. For example, when the 62 | * header version is 1.2.3 this symbol will be 1. 63 | */ 64 | #define YKCLIENT_VERSION_MAJOR @YKCLIENT_VERSION_MAJOR@ 65 | 66 | /** 67 | * YKCLIENT_VERSION_MINOR 68 | * 69 | * Pre-processor symbol with a decimal value that describe the minor 70 | * level of the header file version number. For example, when the 71 | * header version is 1.2.3 this symbol will be 2. 72 | */ 73 | #define YKCLIENT_VERSION_MINOR @YKCLIENT_VERSION_MINOR@ 74 | 75 | /** 76 | * YKCLIENT_VERSION_PATCH 77 | * 78 | * Pre-processor symbol with a decimal value that describe the patch 79 | * level of the header file version number. For example, when the 80 | * header version is 1.2.3 this symbol will be 3. 81 | */ 82 | #define YKCLIENT_VERSION_PATCH @YKCLIENT_VERSION_PATCH@ 83 | 84 | const char *ykclient_check_version (const char *req_version); 85 | 86 | #ifdef __cplusplus 87 | } 88 | #endif 89 | 90 | #endif /* YKCLIENT_VERSION_H */ 91 | --------------------------------------------------------------------------------