summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornicoo <nicoo@debian.org>2020-02-12 13:43:18 +0100
committerNicolas Braud-Santoni <nicolas@braud-santoni.eu>2020-02-12 13:43:18 +0100
commit88a8bdd35ca7fb0c1ce70abdd8262d958fedafc1 (patch)
treece42d9d46d371c05eed82d8bb1e8aa7b2522a769
parent4e06e4554b69e678110563b1cf00a258a202dd7b (diff)
parentc79050aa44b8836d836c5dd22a383a073c28b74b (diff)
Merge upstream release 1.3.0 into debian/sid
-rw-r--r--CMakeLists.txt383
-rw-r--r--LICENSE24
-rw-r--r--NEWS73
-rw-r--r--README.adoc88
-rw-r--r--docker/bionic/Dockerfile14
-rw-r--r--examples/CMakeLists.txt44
-rw-r--r--examples/README.adoc81
-rw-r--r--examples/assert.c329
-rw-r--r--examples/cred.c303
-rw-r--r--examples/extern.h32
-rw-r--r--examples/info.c216
-rw-r--r--examples/manifest.c45
-rw-r--r--examples/reset.c64
-rw-r--r--examples/retries.c52
-rw-r--r--examples/setpin.c59
-rw-r--r--examples/util.c415
-rw-r--r--fuzz/CMakeLists.txt44
-rw-r--r--fuzz/README157
-rw-r--r--fuzz/corpus.tgzbin0 -> 1131788 bytes
-rw-r--r--fuzz/functions.txt564
-rw-r--r--fuzz/fuzz_assert.c664
-rw-r--r--fuzz/fuzz_bio.c755
-rw-r--r--fuzz/fuzz_cred.c925
-rw-r--r--fuzz/fuzz_credman.c667
-rw-r--r--fuzz/fuzz_mgmt.c529
-rwxr-xr-xfuzz/harnesses/assert32
-rwxr-xr-xfuzz/harnesses/assert-rsa-h-p33
-rwxr-xr-xfuzz/harnesses/assert-u2f32
-rwxr-xr-xfuzz/harnesses/cred31
-rwxr-xr-xfuzz/harnesses/cred-rsa-h-p32
-rwxr-xr-xfuzz/harnesses/cred-u2f31
-rwxr-xr-xfuzz/harnesses/cred-u2f-exclude33
-rwxr-xr-xfuzz/harnesses/fido2-assert-G31
-rwxr-xr-xfuzz/harnesses/fido2-assert-V32
-rwxr-xr-xfuzz/harnesses/fido2-cred-M31
-rwxr-xr-xfuzz/harnesses/fido2-cred-V31
-rwxr-xr-xfuzz/harnesses/fuzz_assert29
-rwxr-xr-xfuzz/harnesses/fuzz_bio29
-rwxr-xr-xfuzz/harnesses/fuzz_cred28
-rwxr-xr-xfuzz/harnesses/fuzz_credman28
-rwxr-xr-xfuzz/harnesses/fuzz_mgmt29
-rw-r--r--fuzz/mutator_aux.c314
-rw-r--r--fuzz/mutator_aux.h65
-rw-r--r--fuzz/preload-fuzz.c104
-rw-r--r--fuzz/preload-snoop.c217
-rwxr-xr-xfuzz/report80
-rw-r--r--fuzz/summary.txt39
-rw-r--r--fuzz/uniform_random.c56
-rw-r--r--fuzz/wrap.c419
-rw-r--r--fuzz/wrapped.sym47
-rw-r--r--man/CMakeLists.txt314
-rw-r--r--man/NOTES4
-rw-r--r--man/dyc.css14
-rw-r--r--man/eddsa_pk_new.3122
-rw-r--r--man/es256_pk_new.3122
-rw-r--r--man/fido2-assert.1220
-rw-r--r--man/fido2-cred.1238
-rw-r--r--man/fido2-token.1158
-rw-r--r--man/fido_assert_allow_cred.347
-rw-r--r--man/fido_assert_new.3190
-rw-r--r--man/fido_assert_set_authdata.3194
-rw-r--r--man/fido_assert_verify.379
-rw-r--r--man/fido_bio_dev_get_info.3120
-rw-r--r--man/fido_bio_enroll_new.395
-rw-r--r--man/fido_bio_info_new.381
-rw-r--r--man/fido_bio_template.3169
-rw-r--r--man/fido_cbor_info_new.3153
-rw-r--r--man/fido_cred_exclude.360
-rw-r--r--man/fido_cred_new.3157
-rw-r--r--man/fido_cred_set_authdata.3240
-rw-r--r--man/fido_cred_verify.364
-rw-r--r--man/fido_credman_metadata_new.3299
-rw-r--r--man/fido_dev_get_assert.376
-rw-r--r--man/fido_dev_info_manifest.3143
-rw-r--r--man/fido_dev_make_cred.377
-rw-r--r--man/fido_dev_open.3159
-rw-r--r--man/fido_dev_set_io_functions.395
-rw-r--r--man/fido_dev_set_pin.388
-rw-r--r--man/fido_init.340
-rw-r--r--man/fido_strerr.327
-rw-r--r--man/rs256_pk_new.3122
-rw-r--r--man/style.css24
-rw-r--r--openbsd-compat/bsd-getline.c115
-rw-r--r--openbsd-compat/bsd-getpagesize.c27
-rwxr-xr-xopenbsd-compat/diff.sh24
-rw-r--r--openbsd-compat/err.h85
-rw-r--r--openbsd-compat/explicit_bzero.c57
-rw-r--r--openbsd-compat/explicit_bzero_win32.c19
-rw-r--r--openbsd-compat/getopt.h74
-rw-r--r--openbsd-compat/getopt_long.c523
-rw-r--r--openbsd-compat/openbsd-compat.h87
-rw-r--r--openbsd-compat/posix_win.c61
-rw-r--r--openbsd-compat/posix_win.h47
-rw-r--r--openbsd-compat/readpassphrase.c214
-rw-r--r--openbsd-compat/readpassphrase.h42
-rw-r--r--openbsd-compat/readpassphrase_win32.c131
-rw-r--r--openbsd-compat/recallocarray.c91
-rw-r--r--openbsd-compat/strlcat.c63
-rw-r--r--openbsd-compat/strlcpy.c59
-rw-r--r--openbsd-compat/timingsafe_bcmp.c35
-rw-r--r--openbsd-compat/types.h71
-rw-r--r--regress/CMakeLists.txt18
-rw-r--r--regress/assert.c511
-rw-r--r--regress/cred.c816
-rw-r--r--regress/dev.c77
-rw-r--r--src/CMakeLists.txt104
-rw-r--r--src/aes256.c98
-rw-r--r--src/assert.c1090
-rw-r--r--src/authkey.c98
-rw-r--r--src/bio.c844
-rw-r--r--src/blob.c102
-rw-r--r--src/blob.h28
-rw-r--r--src/buf.c34
-rw-r--r--src/cbor.c1520
-rw-r--r--src/cred.c1034
-rw-r--r--src/credman.c736
-rw-r--r--src/dev.c284
-rwxr-xr-xsrc/diff_exports.sh23
-rw-r--r--src/ecdh.c121
-rw-r--r--src/eddsa.c169
-rw-r--r--src/err.c122
-rw-r--r--src/es256.c434
-rw-r--r--src/export.gnu183
-rw-r--r--src/export.llvm178
-rw-r--r--src/export.msvc179
-rw-r--r--src/extern.h132
-rw-r--r--src/fido.h194
-rw-r--r--src/fido/bio.h95
-rw-r--r--src/fido/credman.h74
-rw-r--r--src/fido/eddsa.h40
-rw-r--r--src/fido/err.h69
-rw-r--r--src/fido/es256.h34
-rw-r--r--src/fido/param.h84
-rw-r--r--src/fido/rs256.h22
-rw-r--r--src/hid.c70
-rw-r--r--src/hid_linux.c344
-rw-r--r--src/hid_openbsd.c277
-rw-r--r--src/hid_osx.c410
-rw-r--r--src/hid_win.c324
-rw-r--r--src/info.c410
-rw-r--r--src/io.c256
-rw-r--r--src/iso7816.c70
-rw-r--r--src/iso7816.h38
-rw-r--r--src/libfido2.pc.in12
-rw-r--r--src/log.c63
-rw-r--r--src/packed.h22
-rw-r--r--src/pin.c428
-rw-r--r--src/reset.c40
-rw-r--r--src/rs256.c204
-rw-r--r--src/types.h171
-rw-r--r--src/u2f.c758
-rw-r--r--tools/CMakeLists.txt65
-rw-r--r--tools/assert_get.c233
-rw-r--r--tools/assert_verify.c201
-rw-r--r--tools/base64.c135
-rw-r--r--tools/bio.c270
-rw-r--r--tools/cred_make.c221
-rw-r--r--tools/cred_verify.c177
-rw-r--r--tools/credman.c237
-rw-r--r--tools/extern.h64
-rw-r--r--tools/fido2-assert.c54
-rw-r--r--tools/fido2-cred.c52
-rw-r--r--tools/fido2-token.c95
-rw-r--r--tools/pin.c146
-rw-r--r--tools/sk-libfido2.c784
-rwxr-xr-xtools/test.sh96
-rw-r--r--tools/token.c364
-rw-r--r--tools/util.c364
-rw-r--r--udev/70-u2f.rules72
-rw-r--r--udev/CMakeLists.txt7
-rw-r--r--windows/build.ps1204
-rw-r--r--windows/libressl.gpgbin0 -> 16425 bytes
172 files changed, 31461 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..c7c5991
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,383 @@
1# Copyright (c) 2018 Yubico AB. All rights reserved.
2# Use of this source code is governed by a BSD-style
3# license that can be found in the LICENSE file.
4
5# detect AppleClang; needs to come before project()
6cmake_policy(SET CMP0025 NEW)
7
8project(libfido2 C)
9cmake_minimum_required(VERSION 3.0)
10
11include(CheckCCompilerFlag)
12include(CheckFunctionExists)
13include(CheckIncludeFiles)
14include(CheckTypeSize)
15include(GNUInstallDirs)
16
17set(CMAKE_COLOR_MAKEFILE off)
18set(CMAKE_VERBOSE_MAKEFILE on)
19set(CMAKE_POSITION_INDEPENDENT_CODE ON)
20
21set(FIDO_MAJOR "1")
22set(FIDO_MINOR "3")
23set(FIDO_PATCH "0")
24set(FIDO_VERSION ${FIDO_MAJOR}.${FIDO_MINOR}.${FIDO_PATCH})
25
26add_definitions(-D_FIDO_MAJOR=${FIDO_MAJOR})
27add_definitions(-D_FIDO_MINOR=${FIDO_MINOR})
28add_definitions(-D_FIDO_PATCH=${FIDO_PATCH})
29
30if(WIN32)
31 add_definitions(-DWIN32_LEAN_AND_MEAN)
32endif()
33
34if(APPLE)
35 set(CMAKE_INSTALL_NAME_DIR
36 "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
37endif()
38
39# /dev/urandom
40if(UNIX)
41 add_definitions(-DHAS_DEV_URANDOM)
42endif()
43
44# Observe OpenBSD's library versioning scheme.
45if(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
46 set(LIB_VERSION ${FIDO_MAJOR}.${FIDO_MINOR})
47 set(LIB_SOVERSION ${LIB_VERSION})
48else()
49 set(LIB_VERSION ${FIDO_VERSION})
50 set(LIB_SOVERSION ${FIDO_MAJOR})
51endif()
52
53if(MSVC)
54 if((NOT CBOR_INCLUDE_DIRS) OR (NOT CBOR_LIBRARY_DIRS) OR
55 (NOT CRYPTO_INCLUDE_DIRS) OR (NOT CRYPTO_LIBRARY_DIRS))
56 message(FATAL_ERROR "please provide definitions for "
57 "{CBOR,CRYPTO}_{INCLUDE,LIBRARY}_DIRS when building "
58 "under msvc")
59 endif()
60 set(CBOR_LIBRARIES cbor)
61 set(CRYPTO_LIBRARIES crypto-45)
62 set(MSVC_DISABLED_WARNINGS_LIST
63 "C4200" # nonstandard extension used: zero-sized array in
64 # struct/union;
65 "C4204" # nonstandard extension used: non-constant aggregate
66 # initializer;
67 "C4706" # assignment within conditional expression;
68 "C4996" # The POSIX name for this item is deprecated. Instead,
69 # use the ISO C and C++ conformant name
70 )
71 # The construction in the following 3 lines was taken from LibreSSL's
72 # CMakeLists.txt.
73 string(REPLACE "C" " -wd" MSVC_DISABLED_WARNINGS_STR
74 ${MSVC_DISABLED_WARNINGS_LIST})
75 string(REGEX REPLACE "[/-]W[1234][ ]?" "" CMAKE_C_FLAGS ${CMAKE_C_FLAGS})
76 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -MP -W4 ${MSVC_DISABLED_WARNINGS_STR}")
77 set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Z7")
78 set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /Zi")
79else()
80 include(FindPkgConfig)
81 pkg_search_module(CBOR libcbor)
82 pkg_search_module(CRYPTO libcrypto REQUIRED)
83
84 # XXX workaround libcbor's missing .pc file
85 if(NOT CBOR_FOUND)
86 check_include_files(cbor.h HAVE_CBOR_H)
87 if(NOT HAVE_CBOR_H)
88 message(FATAL_ERROR "could not find cbor header files")
89 endif()
90 set(CBOR_LIBRARIES "cbor")
91 endif()
92
93 if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
94 pkg_search_module(UDEV libudev REQUIRED)
95 set(UDEV_NAME "udev")
96 # Define be32toh().
97 add_definitions(-D_GNU_SOURCE)
98 elseif(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
99 set(BASE_LIBRARIES usbhid)
100 endif()
101
102 if(MINGW)
103 # MinGW is stuck with a flavour of C89.
104 add_definitions(-DFIDO_NO_DIAGNOSTIC)
105 add_definitions(-DWC_ERR_INVALID_CHARS=0x80)
106 endif()
107
108 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
109 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wextra")
110 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror")
111 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wshadow")
112 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wwrite-strings")
113 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wmissing-prototypes")
114 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wbad-function-cast")
115 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic")
116 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic-errors")
117 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector-all")
118 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99")
119
120 set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g2")
121 set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fno-omit-frame-pointer")
122
123 if(FUZZ)
124 if(LIBFUZZER)
125 set(FUZZ_LDFLAGS "-fsanitize=fuzzer")
126 endif()
127 add_definitions(-DFIDO_FUZZ)
128 endif()
129
130 if(ASAN)
131 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address,leak")
132 endif()
133
134 if(MSAN)
135 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=memory")
136 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-memory-track-origins")
137 endif()
138
139 if(UBSAN)
140 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined")
141 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize-trap=undefined")
142 endif()
143
144 if(COVERAGE)
145 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-instr-generate -fcoverage-mapping")
146 endif()
147endif()
148
149# Use -Wshorten-64-to-32 if available.
150check_c_compiler_flag("-Wshorten-64-to-32" HAVE_SHORTEN_64_TO_32)
151if(HAVE_SHORTEN_64_TO_32)
152 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wshorten-64-to-32")
153endif()
154
155# Avoid https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66425
156if(CMAKE_COMPILER_IS_GNUCC)
157 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-result")
158endif()
159
160# Decide which keyword to use for thread-local storage.
161if(CMAKE_COMPILER_IS_GNUCC OR
162 CMAKE_C_COMPILER_ID STREQUAL "Clang" OR
163 CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
164 set(TLS "__thread")
165elseif(WIN32)
166 set(TLS "__declspec(thread)")
167endif()
168
169add_definitions(-DTLS=${TLS})
170
171# endian.h
172check_include_files(endian.h HAVE_ENDIAN_H)
173if(HAVE_ENDIAN_H)
174 add_definitions(-DHAVE_ENDIAN_H)
175endif()
176
177# err.h
178check_include_files(err.h HAVE_ERR_H)
179if(HAVE_ERR_H)
180 add_definitions(-DHAVE_ERR_H)
181endif()
182
183# unistd.h
184check_include_files(unistd.h HAVE_UNISTD_H)
185if(HAVE_UNISTD_H)
186 add_definitions(-DHAVE_UNISTD_H)
187endif()
188
189# signal.h
190check_include_files(signal.h HAVE_SIGNAL_H)
191if(HAVE_SIGNAL_H)
192 add_definitions(-DHAVE_SIGNAL_H)
193endif()
194
195# strlcpy
196check_function_exists(strlcpy HAVE_STRLCPY)
197if(HAVE_STRLCPY)
198 add_definitions(-DHAVE_STRLCPY)
199endif()
200
201# strlcat
202check_function_exists(strlcpy HAVE_STRLCAT)
203if(HAVE_STRLCAT)
204 add_definitions(-DHAVE_STRLCAT)
205endif()
206
207# recallocarray
208check_function_exists(recallocarray HAVE_RECALLOCARRAY)
209if(HAVE_RECALLOCARRAY)
210 add_definitions(-DHAVE_RECALLOCARRAY)
211endif()
212
213# XXX getpagesize is incorrectly detected when cross-compiling
214# with mingw on Linux. Avoid.
215if(NOT WIN32)
216 check_function_exists(getpagesize HAVE_GETPAGESIZE)
217endif()
218if(HAVE_GETPAGESIZE)
219 add_definitions(-DHAVE_GETPAGESIZE)
220endif()
221
222# sysconf
223check_function_exists(sysconf HAVE_SYSCONF)
224if(HAVE_SYSCONF)
225 add_definitions(-DHAVE_SYSCONF)
226endif()
227
228# memset_s
229if(APPLE)
230 add_definitions(-D__STDC_WANT_LIB_EXT1__=1)
231endif()
232check_function_exists(memset_s HAVE_MEMSET_S)
233if(HAVE_MEMSET_S)
234 add_definitions(-DHAVE_MEMSET_S)
235endif()
236
237# explicit_bzero
238if(NOT LIBFUZZER)
239 check_function_exists(explicit_bzero HAVE_EXPLICIT_BZERO)
240 if(HAVE_EXPLICIT_BZERO)
241 add_definitions(-DHAVE_EXPLICIT_BZERO)
242 endif()
243endif()
244
245# timingsafe_bcmp
246check_function_exists(timingsafe_bcmp HAVE_TIMINGSAFE_BCMP)
247if(HAVE_TIMINGSAFE_BCMP)
248 add_definitions(-DHAVE_TIMINGSAFE_BCMP)
249endif()
250
251# readpassphrase
252check_function_exists(readpassphrase HAVE_READPASSPHRASE)
253if(HAVE_READPASSPHRASE)
254 add_definitions(-DHAVE_READPASSPHRASE)
255endif()
256
257# getline
258check_function_exists(getline HAVE_GETLINE)
259if(HAVE_GETLINE)
260 add_definitions(-DHAVE_GETLINE)
261endif()
262
263# getopt
264check_function_exists(getopt HAVE_GETOPT)
265if(HAVE_GETOPT)
266 add_definitions(-DHAVE_GETOPT)
267 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wcast-qual")
268else()
269 if(CMAKE_COMPILER_IS_GNUCC)
270 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-discarded-qualifiers")
271 endif()
272 if(CMAKE_C_COMPILER_ID STREQUAL "Clang")
273 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-incompatible-pointer-types-discards-qualifiers")
274 endif()
275endif()
276
277# usable sigaction
278set(CMAKE_EXTRA_INCLUDE_FILES signal.h)
279check_function_exists(sigaction HAVE_SIGACTION)
280check_type_size("sig_atomic_t" HAVE_SIG_ATOMIC_T)
281if(HAVE_SIGACTION AND (NOT HAVE_SIG_ATOMIC_T STREQUAL ""))
282 add_definitions(-DSIGNAL_EXAMPLE)
283endif()
284set(CMAKE_EXTRA_INCLUDE_FILES)
285
286# arc4random_buf
287check_function_exists(arc4random_buf HAVE_ARC4RANDOM_BUF)
288if(HAVE_ARC4RANDOM_BUF)
289 add_definitions(-DHAVE_ARC4RANDOM_BUF)
290endif()
291
292# getentropy
293check_function_exists(getentropy HAVE_GETENTROPY)
294if(HAVE_GETENTROPY)
295 add_definitions(-DHAVE_GETENTROPY)
296endif()
297
298# export list
299if(CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
300 # clang + lld
301 string(CONCAT CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS}
302 " -exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/src/export.llvm")
303elseif(NOT MSVC)
304 # clang/gcc + gnu ld
305 string(CONCAT CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS}
306 " -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/src/export.gnu")
307 if(NOT WIN32)
308 string(CONCAT CMAKE_SHARED_LINKER_FLAGS
309 ${CMAKE_SHARED_LINKER_FLAGS}
310 " -Wl,-z,noexecstack -Wl,-z,relro,-z,now")
311 string(CONCAT CMAKE_EXE_LINKER_FLAGS
312 ${CMAKE_EXE_LINKER_FLAGS}
313 " -Wl,-z,noexecstack -Wl,-z,relro,-z,now")
314 if(FUZZ)
315 file(STRINGS fuzz/wrapped.sym WRAPPED_SYMBOLS)
316 foreach(s ${WRAPPED_SYMBOLS})
317 string(CONCAT CMAKE_SHARED_LINKER_FLAGS
318 ${CMAKE_SHARED_LINKER_FLAGS}
319 " -Wl,--wrap=${s}")
320 endforeach()
321 endif()
322 endif()
323else()
324 string(CONCAT CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS}
325 " /def:${CMAKE_CURRENT_SOURCE_DIR}/src/export.msvc")
326endif()
327
328include_directories(${CMAKE_SOURCE_DIR}/src)
329include_directories(${CBOR_INCLUDE_DIRS})
330include_directories(${CRYPTO_INCLUDE_DIRS})
331
332link_directories(${CBOR_LIBRARY_DIRS})
333link_directories(${CRYPTO_LIBRARY_DIRS})
334
335message(STATUS "CMAKE_C_COMPILER: ${CMAKE_C_COMPILER}")
336message(STATUS "CMAKE_C_COMPILER_ID: ${CMAKE_C_COMPILER_ID}")
337message(STATUS "CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}")
338message(STATUS "CMAKE_INSTALL_LIBDIR: ${CMAKE_INSTALL_LIBDIR}")
339message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
340message(STATUS "CBOR_INCLUDE_DIRS: ${CBOR_INCLUDE_DIRS}")
341message(STATUS "CBOR_LIBRARY_DIRS: ${CBOR_LIBRARY_DIRS}")
342message(STATUS "CBOR_LIBRARIES: ${CBOR_LIBRARIES}")
343message(STATUS "CRYPTO_INCLUDE_DIRS: ${CRYPTO_INCLUDE_DIRS}")
344message(STATUS "CRYPTO_LIBRARY_DIRS: ${CRYPTO_LIBRARY_DIRS}")
345message(STATUS "CRYPTO_LIBRARIES: ${CRYPTO_LIBRARIES}")
346message(STATUS "BASE_LIBRARIES: ${BASE_LIBRARIES}")
347message(STATUS "VERSION: ${FIDO_VERSION}")
348message(STATUS "LIB_VERSION: ${LIB_VERSION}")
349message(STATUS "LIB_SOVERSION: ${LIB_SOVERSION}")
350message(STATUS "FUZZ: ${FUZZ}")
351message(STATUS "AFL: ${AFL}")
352message(STATUS "LIBFUZZER: ${LIBFUZZER}")
353message(STATUS "ASAN: ${ASAN}")
354message(STATUS "MSAN: ${MSAN}")
355message(STATUS "COVERAGE: ${COVERAGE}")
356message(STATUS "TLS: ${TLS}")
357
358if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
359 message(STATUS "UDEV_INCLUDE_DIRS: ${UDEV_INCLUDE_DIRS}")
360 message(STATUS "UDEV_LIBRARY_DIRS: ${UDEV_LIBRARY_DIRS}")
361 message(STATUS "UDEV_LIBRARIES: ${UDEV_LIBRARIES}")
362 message(STATUS "UDEV_RULES_DIR: ${UDEV_RULES_DIR}")
363endif()
364
365subdirs(src)
366subdirs(examples)
367subdirs(tools)
368subdirs(man)
369
370if(NOT WIN32)
371 if(CMAKE_BUILD_TYPE STREQUAL "Debug")
372 if(NOT MSAN AND NOT LIBFUZZER)
373 subdirs(regress)
374 endif()
375 endif()
376 if(FUZZ)
377 subdirs(fuzz)
378 endif()
379
380 if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
381 subdirs(udev)
382 endif()
383endif()
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..340cc35
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,24 @@
1Copyright (c) 2018 Yubico AB. All rights reserved.
2
3Redistribution and use in source and binary forms, with or without
4modification, are permitted provided that the following conditions are
5met:
6
7 1. Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 2. Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in
11 the documentation and/or other materials provided with the
12 distribution.
13
14THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
18HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..1b78c7c
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,73 @@
1* Version 1.3.0 (2019-11-28)
2 ** assert/hmac: encode public key as per spec, gh#60.
3 ** fido2-cred: fix creation of resident keys.
4 ** fido2-{assert,cred}: support for hmac-secret extension.
5 ** hid_osx: detect device removal, gh#56.
6 ** hid_osx: fix device detection in MacOS Catalina.
7 ** New API calls:
8 - fido_assert_set_authdata_raw;
9 - fido_assert_sigcount;
10 - fido_cred_set_authdata_raw;
11 - fido_dev_cancel.
12 ** Middleware library for use by OpenSSH.
13 ** Support for biometric enrollment.
14 ** Support for OpenBSD.
15 ** Support for self-attestation.
16
17* Version 1.2.0 (released 2019-07-26)
18 ** Credential management support.
19 ** New API reflecting FIDO's 3-state booleans (true, false, absent):
20 - fido_assert_set_up;
21 - fido_assert_set_uv;
22 - fido_cred_set_rk;
23 - fido_cred_set_uv.
24 ** Command-line tools for Windows.
25 ** Documentation and reliability fixes.
26 ** fido_{assert,cred}_set_options() are now marked as deprecated.
27
28* Version 1.1.0 (released 2019-05-08)
29 ** MacOS: fix IOKit crash on HID read.
30 ** Windows: fix contents of release file.
31 ** EdDSA (Ed25519) support.
32 ** fido_dev_make_cred: fix order of CBOR map keys.
33 ** fido_dev_get_assert: plug memory leak when operating on U2F devices.
34
35* Version 1.0.0 (released 2019-03-21)
36 ** Native HID support on Linux, MacOS, and Windows.
37 ** fido2-{assert,cred}: new -u option to force U2F on dual authenticators.
38 ** fido2-assert: support for multiple resident keys with the same RP.
39 ** Strict checks for CTAP2 compliance on received CBOR payloads.
40 ** Better fuzzing harnesses.
41 ** Documentation and reliability fixes.
42
43* Version 0.4.0 (released 2019-01-07)
44 ** fido2-assert: print the user id for resident credentials.
45 ** Fix encoding of COSE algorithms when making a credential.
46 ** Rework purpose of fido_cred_set_type; no ABI change.
47 ** Minor documentation and code fixes.
48
49* Version 0.3.0 (released 2018-09-11)
50 ** Various reliability fixes.
51 ** Merged fuzzing instrumentation.
52 ** Added regress tests.
53 ** Added support for FIDO 2's hmac-secret extension.
54 ** New API calls:
55 - fido_assert_hmac_secret_len;
56 - fido_assert_hmac_secret_ptr;
57 - fido_assert_set_extensions;
58 - fido_assert_set_hmac_salt;
59 - fido_cred_set_extensions;
60 - fido_dev_force_fido2.
61 ** Support for native builds with Microsoft Visual Studio 17.
62
63* Version 0.2.0 (released 2018-06-20)
64 ** Added command-line tools.
65 ** Added a couple of missing get functions.
66
67* Version 0.1.1 (released 2018-06-05)
68 ** Added documentation.
69 ** Added OpenSSL 1.0 support.
70 ** Minor fixes.
71
72* Version 0.1.0 (released 2018-05-18)
73 ** First beta release.
diff --git a/README.adoc b/README.adoc
new file mode 100644
index 0000000..8693417
--- /dev/null
+++ b/README.adoc
@@ -0,0 +1,88 @@
1== libfido2
2
3image:https://api.travis-ci.org/Yubico/libfido2.svg?branch=master["Build Status (Travis)", link="https://travis-ci.org/Yubico/libfido2"]
4image:https://github.com/yubico/libfido2/workflows/windows/badge.svg["windows build status (github actions)", link="https://github.com/Yubico/libfido2/actions"]
5image:https://img.shields.io/badge/license-BSD-blue.svg["License", link="https://raw.githubusercontent.com/Yubico/libfido2/master/LICENSE"]
6
7*libfido2* provides library functionality and command-line tools to
8communicate with a FIDO device over USB, and to verify attestation and
9assertion signatures.
10
11*libfido2* supports the FIDO U2F (CTAP 1) and FIDO 2.0 (CTAP 2) protocols.
12
13For usage, see the `examples/` directory.
14
15=== License
16
17*libfido2* is licensed under the BSD 2-clause license. See the _LICENSE_
18file for the full license text.
19
20=== Supported Platforms
21
22*libfido2* is known to work on Linux, MacOS, Windows, and OpenBSD.
23
24=== Documentation
25
26Documentation is available in troff and HTML formats. An
27https://developers.yubico.com/libfido2/Manuals/[online mirror of *libfido2*'s documentation]
28is also available.
29
30=== Installation
31
32==== Releases
33
34The current release of *libfido2* is 1.3.0. Please consult Yubico's
35https://developers.yubico.com/libfido2/Releases[release page] for source
36and binary releases.
37
38==== Ubuntu
39
40 $ sudo apt-add-repository ppa:yubico/stable
41 $ sudo apt update
42 $ sudo apt install libfido2-dev
43
44Or from source, on UNIX-like systems:
45
46 $ (rm -rf build && mkdir build && cd build && cmake ..)
47 $ make -C build
48 $ sudo make -C build install
49
50Depending on the platform, the PKG_CONFIG_PATH environment variable may need to
51be set.
52
53*libfido2* depends on https://github.com/pjk/libcbor[libcbor] and
54https://github.com/libressl-portable/portable[LibreSSL] (alternatively,
55https://www.openssl.org[OpenSSL] may be used). On Linux, libudev (part of
56https://www.freedesktop.org/wiki/Software/systemd[systemd]) is also required.
57
58For complete, OS-specific installation instructions, please refer to the
59`.travis/` (Linux, MacOS) and `windows/` directories.
60
61On Linux, you will need to add a udev rule to be able to access the FIDO
62device, or run as root. For example, the udev rule may contain the following:
63
64----
65#udev rule for allowing HID access to Yubico devices for FIDO support.
66
67KERNEL=="hidraw*", SUBSYSTEM=="hidraw", \
68 MODE="0664", GROUP="plugdev", ATTRS{idVendor}=="1050"
69----
70
71On Windows 1903 and newer versions, access to FIDO devices has been restricted
72to applications using the operating system's native API. Use of *libfido2*
73is still possible in privileged applications.
74
75=== OpenSSH Integration
76
77*libfido2* includes middleware allowing https://www.openssh.com[OpenSSH] to
78talk to U2F/FIDO2 devices. Note that server support is required for
79authentication. In a nutshell:
80
81==== Key Generation
82
83 $ ssh-keygen -t [ecdsa-sk|ed25519-sk] -w /path/to/libsk-libfido2.so
84
85==== Authentication
86
87 $ ssh-agent -P /path/to/libsk-libfido2.so
88 $ ssh-add -S /path/to/libsk-libfido2.so
diff --git a/docker/bionic/Dockerfile b/docker/bionic/Dockerfile
new file mode 100644
index 0000000..9225ed8
--- /dev/null
+++ b/docker/bionic/Dockerfile
@@ -0,0 +1,14 @@
1# unlock-yk
2# docker run --rm --volume=/home/pedro/projects/libfido2:/workdir \
3# --volume=$(gpgconf --list-dirs socketdir):/root/.gnupg \
4# --volume=$(gpgconf --list-dirs homedir)/pubring.kbx:/root/.gnupg/pubring.kbx \
5# -it libfido2-staging --install-deps --ppa martelletto/ppa \
6# --key pedro@yubico.com
7FROM ubuntu:bionic
8ENV DEBIAN_FRONTEND noninteractive
9RUN apt-get -qq update && apt-get -qq upgrade
10RUN apt-get install -qq packaging-dev debian-keyring devscripts equivs gnupg python sudo
11ADD https://raw.githubusercontent.com/dainnilsson/scripts/master/make-ppa /make-ppa
12RUN chmod +x /make-ppa
13WORKDIR /workdir
14ENTRYPOINT ["/make-ppa"]
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
new file mode 100644
index 0000000..957311e
--- /dev/null
+++ b/examples/CMakeLists.txt
@@ -0,0 +1,44 @@
1# Copyright (c) 2018 Yubico AB. All rights reserved.
2# Use of this source code is governed by a BSD-style
3# license that can be found in the LICENSE file.
4
5list(APPEND COMPAT_SOURCES
6 ../openbsd-compat/getopt_long.c
7 ../openbsd-compat/strlcat.c
8 ../openbsd-compat/strlcpy.c
9)
10
11if(WIN32)
12 list(APPEND COMPAT_SOURCES ../openbsd-compat/posix_win.c)
13endif()
14
15# drop -rdynamic
16set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
17
18# manifest
19add_executable(manifest manifest.c ${COMPAT_SOURCES})
20target_link_libraries(manifest fido2)
21
22# info
23add_executable(info info.c ${COMPAT_SOURCES})
24target_link_libraries(info fido2)
25
26# reset
27add_executable(reset reset.c util.c ${COMPAT_SOURCES})
28target_link_libraries(reset fido2)
29
30# cred
31add_executable(cred cred.c util.c ${COMPAT_SOURCES})
32target_link_libraries(cred fido2)
33
34# assert
35add_executable(assert assert.c util.c ${COMPAT_SOURCES})
36target_link_libraries(assert fido2)
37
38# setpin
39add_executable(setpin setpin.c ${COMPAT_SOURCES})
40target_link_libraries(setpin fido2)
41
42# retries
43add_executable(retries retries.c ${COMPAT_SOURCES})
44target_link_libraries(retries fido2)
diff --git a/examples/README.adoc b/examples/README.adoc
new file mode 100644
index 0000000..091c6bc
--- /dev/null
+++ b/examples/README.adoc
@@ -0,0 +1,81 @@
1= Examples
2
3=== Definitions
4
5The following definitions are used in the description below:
6
7- <device>
8
9 The file system path or subsystem-specific identification string of a
10 FIDO device.
11
12- <pin>, [oldpin]
13
14 Strings passed directly in the executed command's argument vector.
15
16- <cred_id>
17
18 The file system path of a file containing a FIDO credential ID in
19 binary representation.
20
21- <pubkey>
22
23 The file system path of a file containing a NIST P-256 public key in
24 PEM format.
25
26=== Description
27
28The following examples are provided:
29
30- manifest
31
32 Prints a list of configured FIDO devices.
33
34- info <device>
35
36 Prints information about <device>.
37
38- reset <device>
39
40 Performs a factory reset on <device>.
41
42- setpin <pin> [oldpin] <device>
43
44 Configures <pin> as the new PIN of <device>. If [oldpin] is provided,
45 the device's PIN is changed from [oldpin] to <pin>.
46
47- cred [-t ecdsa|rsa|eddsa] [-k pubkey] [-ei cred_id] [-P pin] [-T seconds]
48 [-hruv] <device>
49
50 Creates a new credential on <device> and verify that the credential
51 was signed by the authenticator. The device's attestation certificate
52 is not verified. If option -k is specified, the credential's public
53 key is stored in <pubkey>. If option -i is specified, the credential
54 ID is stored in <cred_id>. The -e option may be used to add <cred_id>
55 to the list of excluded credentials. If option -h is specified,
56 the hmac-secret FIDO2 extension is enabled on the generated
57 credential. If option -r is specified, the generated credential
58 will involve a resident key. User verification may be requested
59 through the -v option. If option -u is specified, the credential
60 is generated using U2F (CTAP1) instead of FIDO2 (CTAP2) commands.
61 The -T option may be used to enforce a timeout of <seconds>.
62
63- assert [-t ecdsa|rsa|eddsa] [-a cred_id] [-h hmac_secret] [-s hmac_salt]
64 [-P pin] [-T seconds] [-puv] <pubkey> <device>
65
66 Asks <device> for a FIDO2 assertion corresponding to [cred_id],
67 which may be omitted for resident keys. The obtained assertion
68 is verified using <pubkey>. The -p option requests that the user
69 be present. User verification may be requested through the -v
70 option. If option -u is specified, the assertion is generated using
71 U2F (CTAP1) instead of FIDO2 (CTAP2) commands. If option -s is
72 specified, a FIDO2 hmac-secret is requested from the authenticator,
73 and the contents of <hmac_salt> are used as the salt. If option -h
74 is specified, the resulting hmac-secret is stored in <hmac_secret>.
75 The -T option may be used to enforce a timeout of <seconds>.
76
77- retries <device>
78 Get the number of PIN attempts left on <device> before lockout.
79
80Debugging is possible through the use of the FIDO_DEBUG environment variable.
81If set, libfido2 will produce a log of its transactions with the authenticator.
diff --git a/examples/assert.c b/examples/assert.c
new file mode 100644
index 0000000..a421a51
--- /dev/null
+++ b/examples/assert.c
@@ -0,0 +1,329 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <openssl/ec.h>
8
9#include <stdbool.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#ifdef HAVE_UNISTD_H
14#include <unistd.h>
15#endif
16
17#include "../openbsd-compat/openbsd-compat.h"
18
19#include "fido.h"
20#include "fido/es256.h"
21#include "fido/rs256.h"
22#include "fido/eddsa.h"
23#include "extern.h"
24
25#ifdef SIGNAL_EXAMPLE
26extern volatile sig_atomic_t got_signal;
27#endif
28
29static const unsigned char cdh[32] = {
30 0xec, 0x8d, 0x8f, 0x78, 0x42, 0x4a, 0x2b, 0xb7,
31 0x82, 0x34, 0xaa, 0xca, 0x07, 0xa1, 0xf6, 0x56,
32 0x42, 0x1c, 0xb6, 0xf6, 0xb3, 0x00, 0x86, 0x52,
33 0x35, 0x2d, 0xa2, 0x62, 0x4a, 0xbe, 0x89, 0x76,
34};
35
36static void
37usage(void)
38{
39 fprintf(stderr, "usage: assert [-t ecdsa|rsa|eddsa] [-a cred_id] "
40 "[-h hmac_secret] [-s hmac_salt] [-P pin] [-T seconds] [-puv] "
41 "<pubkey> <device>\n");
42 exit(EXIT_FAILURE);
43}
44
45static void
46verify_assert(int type, const unsigned char *authdata_ptr, size_t authdata_len,
47 const unsigned char *sig_ptr, size_t sig_len, bool up, bool uv, int ext,
48 const char *key)
49{
50 fido_assert_t *assert = NULL;
51 EC_KEY *ec = NULL;
52 RSA *rsa = NULL;
53 EVP_PKEY *eddsa = NULL;
54 es256_pk_t *es256_pk = NULL;
55 rs256_pk_t *rs256_pk = NULL;
56 eddsa_pk_t *eddsa_pk = NULL;
57 void *pk;
58 int r;
59
60 /* credential pubkey */
61 switch (type) {
62 case COSE_ES256:
63 if ((ec = read_ec_pubkey(key)) == NULL)
64 errx(1, "read_ec_pubkey");
65
66 if ((es256_pk = es256_pk_new()) == NULL)
67 errx(1, "es256_pk_new");
68
69 if (es256_pk_from_EC_KEY(es256_pk, ec) != FIDO_OK)
70 errx(1, "es256_pk_from_EC_KEY");
71
72 pk = es256_pk;
73 EC_KEY_free(ec);
74 ec = NULL;
75
76 break;
77 case COSE_RS256:
78 if ((rsa = read_rsa_pubkey(key)) == NULL)
79 errx(1, "read_rsa_pubkey");
80
81 if ((rs256_pk = rs256_pk_new()) == NULL)
82 errx(1, "rs256_pk_new");
83
84 if (rs256_pk_from_RSA(rs256_pk, rsa) != FIDO_OK)
85 errx(1, "rs256_pk_from_RSA");
86
87 pk = rs256_pk;
88 RSA_free(rsa);
89 rsa = NULL;
90
91 break;
92 case COSE_EDDSA:
93 if ((eddsa = read_eddsa_pubkey(key)) == NULL)
94 errx(1, "read_eddsa_pubkey");
95
96 if ((eddsa_pk = eddsa_pk_new()) == NULL)
97 errx(1, "eddsa_pk_new");
98
99 if (eddsa_pk_from_EVP_PKEY(eddsa_pk, eddsa) != FIDO_OK)
100 errx(1, "eddsa_pk_from_EVP_PKEY");
101
102 pk = eddsa_pk;
103 EVP_PKEY_free(eddsa);
104 eddsa = NULL;
105
106 break;
107 default:
108 errx(1, "unknown credential type %d", type);
109 }
110
111 if ((assert = fido_assert_new()) == NULL)
112 errx(1, "fido_assert_new");
113
114 /* client data hash */
115 r = fido_assert_set_clientdata_hash(assert, cdh, sizeof(cdh));
116 if (r != FIDO_OK)
117 errx(1, "fido_assert_set_clientdata_hash: %s (0x%x)",
118 fido_strerr(r), r);
119
120 /* relying party */
121 r = fido_assert_set_rp(assert, "localhost");
122 if (r != FIDO_OK)
123 errx(1, "fido_assert_set_rp: %s (0x%x)", fido_strerr(r), r);
124
125 /* authdata */
126 r = fido_assert_set_count(assert, 1);
127 if (r != FIDO_OK)
128 errx(1, "fido_assert_set_count: %s (0x%x)", fido_strerr(r), r);
129 r = fido_assert_set_authdata(assert, 0, authdata_ptr, authdata_len);
130 if (r != FIDO_OK)
131 errx(1, "fido_assert_set_authdata: %s (0x%x)", fido_strerr(r), r);
132
133 /* extension */
134 r = fido_assert_set_extensions(assert, ext);
135 if (r != FIDO_OK)
136 errx(1, "fido_assert_set_extensions: %s (0x%x)", fido_strerr(r),
137 r);
138
139 /* user presence */
140 if (up && (r = fido_assert_set_up(assert, FIDO_OPT_TRUE)) != FIDO_OK)
141 errx(1, "fido_assert_set_up: %s (0x%x)", fido_strerr(r), r);
142
143 /* user verification */
144 if (uv && (r = fido_assert_set_uv(assert, FIDO_OPT_TRUE)) != FIDO_OK)
145 errx(1, "fido_assert_set_uv: %s (0x%x)", fido_strerr(r), r);
146
147 /* sig */
148 r = fido_assert_set_sig(assert, 0, sig_ptr, sig_len);
149 if (r != FIDO_OK)
150 errx(1, "fido_assert_set_sig: %s (0x%x)", fido_strerr(r), r);
151
152 r = fido_assert_verify(assert, 0, type, pk);
153 if (r != FIDO_OK)
154 errx(1, "fido_assert_verify: %s (0x%x)", fido_strerr(r), r);
155
156 es256_pk_free(&es256_pk);
157 rs256_pk_free(&rs256_pk);
158 eddsa_pk_free(&eddsa_pk);
159
160 fido_assert_free(&assert);
161}
162
163int
164main(int argc, char **argv)
165{
166 bool up = false;
167 bool uv = false;
168 bool u2f = false;
169 fido_dev_t *dev = NULL;
170 fido_assert_t *assert = NULL;
171 const char *pin = NULL;
172 const char *hmac_out = NULL;
173 unsigned char *body = NULL;
174 long long seconds = 0;
175 size_t len;
176 int type = COSE_ES256;
177 int ext = 0;
178 int ch;
179 int r;
180
181 if ((assert = fido_assert_new()) == NULL)
182 errx(1, "fido_assert_new");
183
184 while ((ch = getopt(argc, argv, "P:T:a:h:ps:t:uv")) != -1) {
185 switch (ch) {
186 case 'P':
187 pin = optarg;
188 break;
189 case 'T':
190#ifndef SIGNAL_EXAMPLE
191 errx(1, "-T not supported");
192#endif
193 if (base10(optarg, &seconds) < 0)
194 errx(1, "base10: %s", optarg);
195 if (seconds <= 0 || seconds > 30)
196 errx(1, "-T: %s must be in (0,30]", optarg);
197 break;
198 case 'a':
199 if (read_blob(optarg, &body, &len) < 0)
200 errx(1, "read_blob: %s", optarg);
201 if ((r = fido_assert_allow_cred(assert, body,
202 len)) != FIDO_OK)
203 errx(1, "fido_assert_allow_cred: %s (0x%x)",
204 fido_strerr(r), r);
205 free(body);
206 body = NULL;
207 break;
208 case 'h':
209 hmac_out = optarg;
210 break;
211 case 'p':
212 up = true;
213 break;
214 case 's':
215 ext = FIDO_EXT_HMAC_SECRET;
216 if (read_blob(optarg, &body, &len) < 0)
217 errx(1, "read_blob: %s", optarg);
218 if ((r = fido_assert_set_hmac_salt(assert, body,
219 len)) != FIDO_OK)
220 errx(1, "fido_assert_set_hmac_salt: %s (0x%x)",
221 fido_strerr(r), r);
222 free(body);
223 body = NULL;
224 break;
225 case 't':
226 if (strcmp(optarg, "ecdsa") == 0)
227 type = COSE_ES256;
228 else if (strcmp(optarg, "rsa") == 0)
229 type = COSE_RS256;
230 else if (strcmp(optarg, "eddsa") == 0)
231 type = COSE_EDDSA;
232 else
233 errx(1, "unknown type %s", optarg);
234 break;
235 case 'u':
236 u2f = true;
237 break;
238 case 'v':
239 uv = true;
240 break;
241 default:
242 usage();
243 }
244 }
245
246 argc -= optind;
247 argv += optind;
248
249 if (argc != 2)
250 usage();
251
252 fido_init(0);
253
254 if ((dev = fido_dev_new()) == NULL)
255 errx(1, "fido_dev_new");
256
257 r = fido_dev_open(dev, argv[1]);
258 if (r != FIDO_OK)
259 errx(1, "fido_dev_open: %s (0x%x)", fido_strerr(r), r);
260 if (u2f)
261 fido_dev_force_u2f(dev);
262
263 /* client data hash */
264 r = fido_assert_set_clientdata_hash(assert, cdh, sizeof(cdh));
265 if (r != FIDO_OK)
266 errx(1, "fido_assert_set_clientdata_hash: %s (0x%x)",
267 fido_strerr(r), r);
268
269 /* relying party */
270 r = fido_assert_set_rp(assert, "localhost");
271 if (r != FIDO_OK)
272 errx(1, "fido_assert_set_rp: %s (0x%x)", fido_strerr(r), r);
273
274 /* extensions */
275 r = fido_assert_set_extensions(assert, ext);
276 if (r != FIDO_OK)
277 errx(1, "fido_assert_set_extensions: %s (0x%x)", fido_strerr(r),
278 r);
279
280 /* user presence */
281 if (up && (r = fido_assert_set_up(assert, FIDO_OPT_TRUE)) != FIDO_OK)
282 errx(1, "fido_assert_set_up: %s (0x%x)", fido_strerr(r), r);
283
284 /* user verification */
285 if (uv && (r = fido_assert_set_uv(assert, FIDO_OPT_TRUE)) != FIDO_OK)
286 errx(1, "fido_assert_set_uv: %s (0x%x)", fido_strerr(r), r);
287
288#ifdef SIGNAL_EXAMPLE
289 prepare_signal_handler(SIGINT);
290 if (seconds) {
291 prepare_signal_handler(SIGALRM);
292 alarm((unsigned)seconds);
293 }
294#endif
295
296 r = fido_dev_get_assert(dev, assert, pin);
297 if (r != FIDO_OK) {
298#ifdef SIGNAL_EXAMPLE
299 if (got_signal)
300 fido_dev_cancel(dev);
301#endif
302 errx(1, "fido_dev_get_assert: %s (0x%x)", fido_strerr(r), r);
303 }
304
305 r = fido_dev_close(dev);
306 if (r != FIDO_OK)
307 errx(1, "fido_dev_close: %s (0x%x)", fido_strerr(r), r);
308
309 fido_dev_free(&dev);
310
311 if (fido_assert_count(assert) != 1)
312 errx(1, "fido_assert_count: %d signatures returned",
313 (int)fido_assert_count(assert));
314
315 verify_assert(type, fido_assert_authdata_ptr(assert, 0),
316 fido_assert_authdata_len(assert, 0), fido_assert_sig_ptr(assert, 0),
317 fido_assert_sig_len(assert, 0), up, uv, ext, argv[0]);
318
319 if (hmac_out != NULL) {
320 /* extract the hmac secret */
321 if (write_blob(hmac_out, fido_assert_hmac_secret_ptr(assert, 0),
322 fido_assert_hmac_secret_len(assert, 0)) < 0)
323 errx(1, "write_blob");
324 }
325
326 fido_assert_free(&assert);
327
328 exit(0);
329}
diff --git a/examples/cred.c b/examples/cred.c
new file mode 100644
index 0000000..e471f7e
--- /dev/null
+++ b/examples/cred.c
@@ -0,0 +1,303 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <openssl/ec.h>
8#include <openssl/pem.h>
9
10#include <errno.h>
11#include <stdbool.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#ifdef HAVE_UNISTD_H
16#include <unistd.h>
17#endif
18
19#include "../openbsd-compat/openbsd-compat.h"
20
21#include "fido.h"
22#include "extern.h"
23
24#ifdef SIGNAL_EXAMPLE
25extern volatile sig_atomic_t got_signal;
26#endif
27
28static const unsigned char cdh[32] = {
29 0xf9, 0x64, 0x57, 0xe7, 0x2d, 0x97, 0xf6, 0xbb,
30 0xdd, 0xd7, 0xfb, 0x06, 0x37, 0x62, 0xea, 0x26,
31 0x20, 0x44, 0x8e, 0x69, 0x7c, 0x03, 0xf2, 0x31,
32 0x2f, 0x99, 0xdc, 0xaf, 0x3e, 0x8a, 0x91, 0x6b,
33};
34
35static const unsigned char user_id[32] = {
36 0x78, 0x1c, 0x78, 0x60, 0xad, 0x88, 0xd2, 0x63,
37 0x32, 0x62, 0x2a, 0xf1, 0x74, 0x5d, 0xed, 0xb2,
38 0xe7, 0xa4, 0x2b, 0x44, 0x89, 0x29, 0x39, 0xc5,
39 0x56, 0x64, 0x01, 0x27, 0x0d, 0xbb, 0xc4, 0x49,
40};
41
42static void
43usage(void)
44{
45 fprintf(stderr, "usage: cred [-t ecdsa|rsa|eddsa] [-k pubkey] "
46 "[-ei cred_id] [-P pin] [-T seconds] [-hruv] <device>\n");
47 exit(EXIT_FAILURE);
48}
49
50static void
51verify_cred(int type, const char *fmt, const unsigned char *authdata_ptr,
52 size_t authdata_len, const unsigned char *x509_ptr, size_t x509_len,
53 const unsigned char *sig_ptr, size_t sig_len, bool rk, bool uv, int ext,
54 const char *key_out, const char *id_out)
55{
56 fido_cred_t *cred;
57 int r;
58
59 if ((cred = fido_cred_new()) == NULL)
60 errx(1, "fido_cred_new");
61
62 /* type */
63 r = fido_cred_set_type(cred, type);
64 if (r != FIDO_OK)
65 errx(1, "fido_cred_set_type: %s (0x%x)", fido_strerr(r), r);
66
67 /* client data hash */
68 r = fido_cred_set_clientdata_hash(cred, cdh, sizeof(cdh));
69 if (r != FIDO_OK)
70 errx(1, "fido_cred_set_clientdata_hash: %s (0x%x)",
71 fido_strerr(r), r);
72
73 /* relying party */
74 r = fido_cred_set_rp(cred, "localhost", "sweet home localhost");
75 if (r != FIDO_OK)
76 errx(1, "fido_cred_set_rp: %s (0x%x)", fido_strerr(r), r);
77
78 /* authdata */
79 r = fido_cred_set_authdata(cred, authdata_ptr, authdata_len);
80 if (r != FIDO_OK)
81 errx(1, "fido_cred_set_authdata: %s (0x%x)", fido_strerr(r), r);
82
83 /* extensions */
84 r = fido_cred_set_extensions(cred, ext);
85 if (r != FIDO_OK)
86 errx(1, "fido_cred_set_extensions: %s (0x%x)", fido_strerr(r), r);
87
88 /* resident key */
89 if (rk && (r = fido_cred_set_rk(cred, FIDO_OPT_TRUE)) != FIDO_OK)
90 errx(1, "fido_cred_set_rk: %s (0x%x)", fido_strerr(r), r);
91
92 /* user verification */
93 if (uv && (r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK)
94 errx(1, "fido_cred_set_uv: %s (0x%x)", fido_strerr(r), r);
95
96 /* x509 */
97 r = fido_cred_set_x509(cred, x509_ptr, x509_len);
98 if (r != FIDO_OK)
99 errx(1, "fido_cred_set_x509: %s (0x%x)", fido_strerr(r), r);
100
101 /* sig */
102 r = fido_cred_set_sig(cred, sig_ptr, sig_len);
103 if (r != FIDO_OK)
104 errx(1, "fido_cred_set_sig: %s (0x%x)", fido_strerr(r), r);
105
106 /* fmt */
107 r = fido_cred_set_fmt(cred, fmt);
108 if (r != FIDO_OK)
109 errx(1, "fido_cred_set_fmt: %s (0x%x)", fido_strerr(r), r);
110
111 r = fido_cred_verify(cred);
112 if (r != FIDO_OK)
113 errx(1, "fido_cred_verify: %s (0x%x)", fido_strerr(r), r);
114
115 if (key_out != NULL) {
116 /* extract the credential pubkey */
117 if (type == COSE_ES256) {
118 if (write_ec_pubkey(key_out, fido_cred_pubkey_ptr(cred),
119 fido_cred_pubkey_len(cred)) < 0)
120 errx(1, "write_ec_pubkey");
121 } else if (type == COSE_RS256) {
122 if (write_rsa_pubkey(key_out, fido_cred_pubkey_ptr(cred),
123 fido_cred_pubkey_len(cred)) < 0)
124 errx(1, "write_rsa_pubkey");
125 } else if (type == COSE_EDDSA) {
126 if (write_eddsa_pubkey(key_out, fido_cred_pubkey_ptr(cred),
127 fido_cred_pubkey_len(cred)) < 0)
128 errx(1, "write_eddsa_pubkey");
129 }
130 }
131
132 if (id_out != NULL) {
133 /* extract the credential id */
134 if (write_blob(id_out, fido_cred_id_ptr(cred),
135 fido_cred_id_len(cred)) < 0)
136 errx(1, "write_blob");
137 }
138
139 fido_cred_free(&cred);
140}
141
142int
143main(int argc, char **argv)
144{
145 bool rk = false;
146 bool uv = false;
147 bool u2f = false;
148 fido_dev_t *dev;
149 fido_cred_t *cred = NULL;
150 const char *pin = NULL;
151 const char *key_out = NULL;
152 const char *id_out = NULL;
153 unsigned char *body = NULL;
154 long long seconds = 0;
155 size_t len;
156 int type = COSE_ES256;
157 int ext = 0;
158 int ch;
159 int r;
160
161 if ((cred = fido_cred_new()) == NULL)
162 errx(1, "fido_cred_new");
163
164 while ((ch = getopt(argc, argv, "P:T:e:hi:k:rt:uv")) != -1) {
165 switch (ch) {
166 case 'P':
167 pin = optarg;
168 break;
169 case 'T':
170#ifndef SIGNAL_EXAMPLE
171 errx(1, "-T not supported");
172#endif
173 if (base10(optarg, &seconds) < 0)
174 errx(1, "base10: %s", optarg);
175 if (seconds <= 0 || seconds > 30)
176 errx(1, "-T: %s must be in (0,30]", optarg);
177 break;
178 case 'e':
179 if (read_blob(optarg, &body, &len) < 0)
180 errx(1, "read_blob: %s", optarg);
181 r = fido_cred_exclude(cred, body, len);
182 if (r != FIDO_OK)
183 errx(1, "fido_cred_exclude: %s (0x%x)",
184 fido_strerr(r), r);
185 free(body);
186 body = NULL;
187 break;
188 case 'h':
189 ext = FIDO_EXT_HMAC_SECRET;
190 break;
191 case 'i':
192 id_out = optarg;
193 break;
194 case 'k':
195 key_out = optarg;
196 break;
197 case 'r':
198 rk = true;
199 break;
200 case 't':
201 if (strcmp(optarg, "ecdsa") == 0)
202 type = COSE_ES256;
203 else if (strcmp(optarg, "rsa") == 0)
204 type = COSE_RS256;
205 else if (strcmp(optarg, "eddsa") == 0)
206 type = COSE_EDDSA;
207 else
208 errx(1, "unknown type %s", optarg);
209 break;
210 case 'u':
211 u2f = true;
212 break;
213 case 'v':
214 uv = true;
215 break;
216 default:
217 usage();
218 }
219 }
220
221 argc -= optind;
222 argv += optind;
223
224 if (argc != 1)
225 usage();
226
227 fido_init(0);
228
229 if ((dev = fido_dev_new()) == NULL)
230 errx(1, "fido_dev_new");
231
232 if ((r = fido_dev_open(dev, argv[0])) != FIDO_OK)
233 errx(1, "fido_dev_open: %s (0x%x)", fido_strerr(r), r);
234 if (u2f)
235 fido_dev_force_u2f(dev);
236
237 /* type */
238 r = fido_cred_set_type(cred, type);
239 if (r != FIDO_OK)
240 errx(1, "fido_cred_set_type: %s (0x%x)", fido_strerr(r), r);
241
242 /* client data hash */
243 r = fido_cred_set_clientdata_hash(cred, cdh, sizeof(cdh));
244 if (r != FIDO_OK)
245 errx(1, "fido_cred_set_clientdata_hash: %s (0x%x)",
246 fido_strerr(r), r);
247
248 /* relying party */
249 r = fido_cred_set_rp(cred, "localhost", "sweet home localhost");
250 if (r != FIDO_OK)
251 errx(1, "fido_cred_set_rp: %s (0x%x)", fido_strerr(r), r);
252
253 /* user */
254 r = fido_cred_set_user(cred, user_id, sizeof(user_id), "john smith",
255 "jsmith", NULL);
256 if (r != FIDO_OK)
257 errx(1, "fido_cred_set_user: %s (0x%x)", fido_strerr(r), r);
258
259 /* extensions */
260 r = fido_cred_set_extensions(cred, ext);
261 if (r != FIDO_OK)
262 errx(1, "fido_cred_set_extensions: %s (0x%x)", fido_strerr(r), r);
263
264 /* resident key */
265 if (rk && (r = fido_cred_set_rk(cred, FIDO_OPT_TRUE)) != FIDO_OK)
266 errx(1, "fido_cred_set_rk: %s (0x%x)", fido_strerr(r), r);
267
268 /* user verification */
269 if (uv && (r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK)
270 errx(1, "fido_cred_set_uv: %s (0x%x)", fido_strerr(r), r);
271
272#ifdef SIGNAL_EXAMPLE
273 prepare_signal_handler(SIGINT);
274 if (seconds) {
275 prepare_signal_handler(SIGALRM);
276 alarm((unsigned)seconds);
277 }
278#endif
279
280 r = fido_dev_make_cred(dev, cred, pin);
281 if (r != FIDO_OK) {
282#ifdef SIGNAL_EXAMPLE
283 if (got_signal)
284 fido_dev_cancel(dev);
285#endif
286 errx(1, "fido_makecred: %s (0x%x)", fido_strerr(r), r);
287 }
288
289 r = fido_dev_close(dev);
290 if (r != FIDO_OK)
291 errx(1, "fido_dev_close: %s (0x%x)", fido_strerr(r), r);
292
293 fido_dev_free(&dev);
294
295 verify_cred(type, fido_cred_fmt(cred), fido_cred_authdata_ptr(cred),
296 fido_cred_authdata_len(cred), fido_cred_x5c_ptr(cred),
297 fido_cred_x5c_len(cred), fido_cred_sig_ptr(cred),
298 fido_cred_sig_len(cred), rk, uv, ext, key_out, id_out);
299
300 fido_cred_free(&cred);
301
302 exit(0);
303}
diff --git a/examples/extern.h b/examples/extern.h
new file mode 100644
index 0000000..578b8c4
--- /dev/null
+++ b/examples/extern.h
@@ -0,0 +1,32 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#ifndef _EXTERN_H_
8#define _EXTERN_H_
9
10#include <openssl/ec.h>
11#include <openssl/evp.h>
12#include <openssl/rsa.h>
13
14#ifdef HAVE_SIGNAL_H
15#include <signal.h>
16#endif
17
18/* util.c */
19EC_KEY *read_ec_pubkey(const char *);
20RSA *read_rsa_pubkey(const char *);
21EVP_PKEY *read_eddsa_pubkey(const char *);
22int base10(const char *, long long *);
23int read_blob(const char *, unsigned char **, size_t *);
24int write_blob(const char *, const unsigned char *, size_t);
25int write_ec_pubkey(const char *, const void *, size_t);
26int write_rsa_pubkey(const char *, const void *, size_t);
27int write_eddsa_pubkey(const char *, const void *, size_t);
28#ifdef SIGNAL_EXAMPLE
29void prepare_signal_handler(int);
30#endif
31
32#endif /* _EXTERN_H_ */
diff --git a/examples/info.c b/examples/info.c
new file mode 100644
index 0000000..e79729c
--- /dev/null
+++ b/examples/info.c
@@ -0,0 +1,216 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <openssl/ec.h>
8
9#include <stdbool.h>
10#include <stdint.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14
15#include "../openbsd-compat/openbsd-compat.h"
16
17#include "fido.h"
18
19/*
20 * Pretty-print a device's capabilities flags and return the result.
21 */
22static void
23format_flags(char *ret, size_t retlen, uint8_t flags)
24{
25 memset(ret, 0, retlen);
26
27 if (flags & FIDO_CAP_WINK) {
28 if (strlcat(ret, "wink,", retlen) >= retlen)
29 goto toolong;
30 } else {
31 if (strlcat(ret, "nowink,", retlen) >= retlen)
32 goto toolong;
33 }
34
35 if (flags & FIDO_CAP_CBOR) {
36 if (strlcat(ret, " cbor,", retlen) >= retlen)
37 goto toolong;
38 } else {
39 if (strlcat(ret, " nocbor,", retlen) >= retlen)
40 goto toolong;
41 }
42
43 if (flags & FIDO_CAP_NMSG) {
44 if (strlcat(ret, " nomsg", retlen) >= retlen)
45 goto toolong;
46 } else {
47 if (strlcat(ret, " msg", retlen) >= retlen)
48 goto toolong;
49 }
50
51 return;
52toolong:
53 strlcpy(ret, "toolong", retlen);
54}
55
56/*
57 * Print a FIDO device's attributes on stdout.
58 */
59static void
60print_attr(const fido_dev_t *dev)
61{
62 char flags_txt[128];
63
64 printf("proto: 0x%02x\n", fido_dev_protocol(dev));
65 printf("major: 0x%02x\n", fido_dev_major(dev));
66 printf("minor: 0x%02x\n", fido_dev_minor(dev));
67 printf("build: 0x%02x\n", fido_dev_build(dev));
68
69 format_flags(flags_txt, sizeof(flags_txt), fido_dev_flags(dev));
70 printf("caps: 0x%02x (%s)\n", fido_dev_flags(dev), flags_txt);
71}
72
73/*
74 * Auxiliary function to print an array of strings on stdout.
75 */
76static void
77print_str_array(const char *label, char * const *sa, size_t len)
78{
79 if (len == 0)
80 return;
81
82 printf("%s strings: ", label);
83
84 for (size_t i = 0; i < len; i++)
85 printf("%s%s", i > 0 ? ", " : "", sa[i]);
86
87 printf("\n");
88}
89
90/*
91 * Auxiliary function to print (char *, bool) pairs on stdout.
92 */
93static void
94print_opt_array(const char *label, char * const *name, const bool *value,
95 size_t len)
96{
97 if (len == 0)
98 return;
99
100 printf("%s: ", label);
101
102 for (size_t i = 0; i < len; i++)
103 printf("%s%s%s", i > 0 ? ", " : "",
104 value[i] ? "" : "no", name[i]);
105
106 printf("\n");
107}
108
109/*
110 * Auxiliary function to print an authenticator's AAGUID on stdout.
111 */
112static void
113print_aaguid(const unsigned char *buf, size_t buflen)
114{
115 printf("aaguid: ");
116
117 while (buflen--)
118 printf("%02x", *buf++);
119
120 printf("\n");
121}
122
123/*
124 * Auxiliary function to print an authenticator's maximum message size on
125 * stdout.
126 */
127static void
128print_maxmsgsiz(uint64_t maxmsgsiz)
129{
130 printf("maxmsgsiz: %d\n", (int)maxmsgsiz);
131}
132
133/*
134 * Auxiliary function to print an array of bytes on stdout.
135 */
136static void
137print_byte_array(const char *label, const uint8_t *ba, size_t len)
138{
139 if (len == 0)
140 return;
141
142 printf("%s: ", label);
143
144 for (size_t i = 0; i < len; i++)
145 printf("%s%u", i > 0 ? ", " : "", (unsigned)ba[i]);
146
147 printf("\n");
148}
149
150static void
151getinfo(const char *path)
152{
153 fido_dev_t *dev;
154 fido_cbor_info_t *ci;
155 int r;
156
157 fido_init(0);
158
159 if ((dev = fido_dev_new()) == NULL)
160 errx(1, "fido_dev_new");
161 if ((r = fido_dev_open(dev, path)) != FIDO_OK)
162 errx(1, "fido_dev_open: %s (0x%x)", fido_strerr(r), r);
163
164 print_attr(dev);
165
166 if (fido_dev_is_fido2(dev) == false)
167 goto end;
168 if ((ci = fido_cbor_info_new()) == NULL)
169 errx(1, "fido_cbor_info_new");
170 if ((r = fido_dev_get_cbor_info(dev, ci)) != FIDO_OK)
171 errx(1, "fido_dev_get_cbor_info: %s (0x%x)", fido_strerr(r), r);
172
173 /* print supported protocol versions */
174 print_str_array("version", fido_cbor_info_versions_ptr(ci),
175 fido_cbor_info_versions_len(ci));
176
177 /* print supported extensions */
178 print_str_array("extension", fido_cbor_info_extensions_ptr(ci),
179 fido_cbor_info_extensions_len(ci));
180
181 /* print aaguid */
182 print_aaguid(fido_cbor_info_aaguid_ptr(ci),
183 fido_cbor_info_aaguid_len(ci));
184
185 /* print supported options */
186 print_opt_array("options", fido_cbor_info_options_name_ptr(ci),
187 fido_cbor_info_options_value_ptr(ci),
188 fido_cbor_info_options_len(ci));
189
190 /* print maximum message size */
191 print_maxmsgsiz(fido_cbor_info_maxmsgsiz(ci));
192
193 /* print supported pin protocols */
194 print_byte_array("pin protocols", fido_cbor_info_protocols_ptr(ci),
195 fido_cbor_info_protocols_len(ci));
196
197 fido_cbor_info_free(&ci);
198end:
199 if ((r = fido_dev_close(dev)) != FIDO_OK)
200 errx(1, "fido_dev_close: %s (0x%x)", fido_strerr(r), r);
201
202 fido_dev_free(&dev);
203}
204
205int
206main(int argc, char **argv)
207{
208 if (argc != 2) {
209 fprintf(stderr, "usage: info <device>\n");
210 exit(EXIT_FAILURE);
211 }
212
213 getinfo(argv[1]);
214
215 exit(0);
216}
diff --git a/examples/manifest.c b/examples/manifest.c
new file mode 100644
index 0000000..895447a
--- /dev/null
+++ b/examples/manifest.c
@@ -0,0 +1,45 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <openssl/ec.h>
8
9#include <stdbool.h>
10#include <stdio.h>
11#include <stdlib.h>
12
13#include "../openbsd-compat/openbsd-compat.h"
14
15#include "fido.h"
16
17int
18main(void)
19{
20 fido_dev_info_t *devlist;
21 size_t ndevs;
22 int r;
23
24 fido_init(0);
25
26 if ((devlist = fido_dev_info_new(64)) == NULL)
27 errx(1, "fido_dev_info_new");
28
29 if ((r = fido_dev_info_manifest(devlist, 64, &ndevs)) != FIDO_OK)
30 errx(1, "fido_dev_info_manifest: %s (0x%x)", fido_strerr(r), r);
31
32 for (size_t i = 0; i < ndevs; i++) {
33 const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i);
34 printf("%s: vendor=0x%04x, product=0x%04x (%s %s)\n",
35 fido_dev_info_path(di),
36 (uint16_t)fido_dev_info_vendor(di),
37 (uint16_t)fido_dev_info_product(di),
38 fido_dev_info_manufacturer_string(di),
39 fido_dev_info_product_string(di));
40 }
41
42 fido_dev_info_free(&devlist, ndevs);
43
44 exit(0);
45}
diff --git a/examples/reset.c b/examples/reset.c
new file mode 100644
index 0000000..36a7de2
--- /dev/null
+++ b/examples/reset.c
@@ -0,0 +1,64 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7/*
8 * Perform a factory reset on a given authenticator.
9 */
10
11#include <openssl/ec.h>
12
13#include <stdbool.h>
14#include <stdint.h>
15#include <stdio.h>
16#include <stdlib.h>
17
18#include "../openbsd-compat/openbsd-compat.h"
19
20#include "fido.h"
21#include "extern.h"
22
23#ifdef SIGNAL_EXAMPLE
24extern volatile sig_atomic_t got_signal;
25#endif
26
27int
28main(int argc, char **argv)
29{
30 fido_dev_t *dev;
31 int r;
32
33 if (argc != 2) {
34 fprintf(stderr, "usage: reset <device>\n");
35 exit(EXIT_FAILURE);
36 }
37
38 fido_init(0);
39
40 if ((dev = fido_dev_new()) == NULL)
41 errx(1, "fido_dev_new");
42
43 if ((r = fido_dev_open(dev, argv[1])) != FIDO_OK)
44 errx(1, "fido_dev_open: %s (0x%x)", fido_strerr(r), r);
45
46#ifdef SIGNAL_EXAMPLE
47 prepare_signal_handler(SIGINT);
48#endif
49
50 if ((r = fido_dev_reset(dev)) != FIDO_OK) {
51#ifdef SIGNAL_EXAMPLE
52 if (got_signal)
53 fido_dev_cancel(dev);
54#endif
55 errx(1, "fido_reset: %s (0x%x)", fido_strerr(r), r);
56 }
57
58 if ((r = fido_dev_close(dev)) != FIDO_OK)
59 errx(1, "fido_dev_close: %s (0x%x)", fido_strerr(r), r);
60
61 fido_dev_free(&dev);
62
63 exit(0);
64}
diff --git a/examples/retries.c b/examples/retries.c
new file mode 100644
index 0000000..3ed7558
--- /dev/null
+++ b/examples/retries.c
@@ -0,0 +1,52 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7/*
8 * Get an authenticator's number of PIN attempts left.
9 */
10
11#include <openssl/ec.h>
12
13#include <stdbool.h>
14#include <stdio.h>
15#include <stdlib.h>
16
17#include "../openbsd-compat/openbsd-compat.h"
18
19#include "fido.h"
20
21int
22main(int argc, char **argv)
23{
24 fido_dev_t *dev;
25 int n;
26 int r;
27
28 if (argc != 2) {
29 fprintf(stderr, "usage: retries <device>\n");
30 exit(EXIT_FAILURE);
31 }
32
33 fido_init(0);
34
35 if ((dev = fido_dev_new()) == NULL)
36 errx(1, "fido_dev_new");
37
38 if ((r = fido_dev_open(dev, argv[1])) != FIDO_OK)
39 errx(1, "fido_open: %s (0x%x)", fido_strerr(r), r);
40
41 if ((r = fido_dev_get_retry_count(dev, &n)) != FIDO_OK)
42 errx(1, "fido_get_retries: %s (0x%x)", fido_strerr(r), r);
43
44 if ((r = fido_dev_close(dev)) != FIDO_OK)
45 errx(1, "fido_close: %s (0x%x)", fido_strerr(r), r);
46
47 fido_dev_free(&dev);
48
49 printf("%d\n", n);
50
51 exit(0);
52}
diff --git a/examples/setpin.c b/examples/setpin.c
new file mode 100644
index 0000000..75d3d4a
--- /dev/null
+++ b/examples/setpin.c
@@ -0,0 +1,59 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7/*
8 * Configure a PIN on a given authenticator.
9 */
10
11#include <openssl/ec.h>
12
13#include <stdbool.h>
14#include <stdint.h>
15#include <stdio.h>
16#include <stdlib.h>
17
18#include "../openbsd-compat/openbsd-compat.h"
19
20#include "fido.h"
21
22static void
23setpin(const char *path, const char *pin, const char *oldpin)
24{
25 fido_dev_t *dev;
26 int r;
27
28 fido_init(0);
29
30 if ((dev = fido_dev_new()) == NULL)
31 errx(1, "fido_dev_new");
32
33 if ((r = fido_dev_open(dev, path)) != FIDO_OK)
34 errx(1, "fido_dev_open: %s (0x%x)", fido_strerr(r), r);
35
36 if ((r = fido_dev_set_pin(dev, pin, oldpin)) != FIDO_OK)
37 errx(1, "fido_setpin: %s (0x%x)", fido_strerr(r), r);
38
39 if ((r = fido_dev_close(dev)) != FIDO_OK)
40 errx(1, "fido_dev_close: %s (0x%x)", fido_strerr(r), r);
41
42 fido_dev_free(&dev);
43}
44
45int
46main(int argc, char **argv)
47{
48 if (argc < 3 || argc > 4) {
49 fprintf(stderr, "usage: setpin <pin> [oldpin] <device>\n");
50 exit(EXIT_FAILURE);
51 }
52
53 if (argc == 3)
54 setpin(argv[2], argv[1], NULL);
55 else
56 setpin(argv[3], argv[1], argv[2]);
57
58 exit(0);
59}
diff --git a/examples/util.c b/examples/util.c
new file mode 100644
index 0000000..2f6a845
--- /dev/null
+++ b/examples/util.c
@@ -0,0 +1,415 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <sys/types.h>
8#include <sys/stat.h>
9
10#include <openssl/ec.h>
11#include <openssl/evp.h>
12#include <openssl/pem.h>
13
14#include <errno.h>
15#include <fcntl.h>
16#include <limits.h>
17#include <stdbool.h>
18#include <stdlib.h>
19#include <string.h>
20#ifdef HAVE_SIGNAL_H
21#include <signal.h>
22#endif
23#ifdef HAVE_UNISTD_H
24#include <unistd.h>
25#endif
26#ifdef _MSC_VER
27#include "../openbsd-compat/posix_win.h"
28#endif
29
30#include "../openbsd-compat/openbsd-compat.h"
31
32#include "fido.h"
33#include "fido/es256.h"
34#include "fido/rs256.h"
35#include "fido/eddsa.h"
36#include "extern.h"
37
38#ifdef SIGNAL_EXAMPLE
39volatile sig_atomic_t got_signal = 0;
40
41static void
42signal_handler(int signo)
43{
44 (void)signo;
45 got_signal = 1;
46}
47
48void
49prepare_signal_handler(int signo)
50{
51 struct sigaction sa;
52
53 memset(&sa, 0, sizeof(sa));
54
55 sigemptyset(&sa.sa_mask);
56 sa.sa_handler = signal_handler;
57
58 if (sigaction(signo, &sa, NULL) < 0)
59 err(1, "sigaction");
60}
61#endif
62
63int
64base10(const char *str, long long *ll)
65{
66 char *ep;
67
68 *ll = strtoll(str, &ep, 10);
69 if (str == ep || *ep != '\0')
70 return (-1);
71 else if (*ll == LLONG_MIN && errno == ERANGE)
72 return (-1);
73 else if (*ll == LLONG_MAX && errno == ERANGE)
74 return (-1);
75
76 return (0);
77}
78
79int
80write_blob(const char *path, const unsigned char *ptr, size_t len)
81{
82 int fd, ok = -1;
83 ssize_t n;
84
85 if ((fd = open(path, O_WRONLY | O_CREAT, 0644)) < 0) {
86 warn("open %s", path);
87 goto fail;
88 }
89
90 if ((n = write(fd, ptr, len)) < 0) {
91 warn("write");
92 goto fail;
93 }
94 if ((size_t)n != len) {
95 warnx("write");
96 goto fail;
97 }
98
99 ok = 0;
100fail:
101 if (fd != -1) {
102 close(fd);
103 }
104
105 return (ok);
106}
107
108int
109read_blob(const char *path, unsigned char **ptr, size_t *len)
110{
111 int fd, ok = -1;
112 struct stat st;
113 ssize_t n;
114
115 *ptr = NULL;
116 *len = 0;
117
118 if ((fd = open(path, O_RDONLY)) < 0) {
119 warn("open %s", path);
120 goto fail;
121 }
122 if (fstat(fd, &st) < 0) {
123 warn("stat %s", path);
124 goto fail;
125 }
126 if (st.st_size < 0) {
127 warnx("stat %s: invalid size", path);
128 goto fail;
129 }
130 *len = (size_t)st.st_size;
131 if ((*ptr = malloc(*len)) == NULL) {
132 warn("malloc");
133 goto fail;
134 }
135 if ((n = read(fd, *ptr, *len)) < 0) {
136 warn("read");
137 goto fail;
138 }
139 if ((size_t)n != *len) {
140 warnx("read");
141 goto fail;
142 }
143
144 ok = 0;
145fail:
146 if (fd != -1) {
147 close(fd);
148 }
149 if (ok < 0) {
150 free(*ptr);
151 *ptr = NULL;
152 *len = 0;
153 }
154
155 return (ok);
156}
157
158EC_KEY *
159read_ec_pubkey(const char *path)
160{
161 FILE *fp = NULL;
162 EVP_PKEY *pkey = NULL;
163 EC_KEY *ec = NULL;
164
165 if ((fp = fopen(path, "r")) == NULL) {
166 warn("fopen");
167 goto fail;
168 }
169
170 if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
171 warnx("PEM_read_PUBKEY");
172 goto fail;
173 }
174 if ((ec = EVP_PKEY_get1_EC_KEY(pkey)) == NULL) {
175 warnx("EVP_PKEY_get1_EC_KEY");
176 goto fail;
177 }
178
179fail:
180 if (fp != NULL) {
181 fclose(fp);
182 }
183 if (pkey != NULL) {
184 EVP_PKEY_free(pkey);
185 }
186
187 return (ec);
188}
189
190int
191write_ec_pubkey(const char *path, const void *ptr, size_t len)
192{
193 FILE *fp = NULL;
194 EVP_PKEY *pkey = NULL;
195 es256_pk_t *pk = NULL;
196 int fd = -1;
197 int ok = -1;
198
199 if ((pk = es256_pk_new()) == NULL) {
200 warnx("es256_pk_new");
201 goto fail;
202 }
203
204 if (es256_pk_from_ptr(pk, ptr, len) != FIDO_OK) {
205 warnx("es256_pk_from_ptr");
206 goto fail;
207 }
208
209 if ((fd = open(path, O_WRONLY | O_CREAT, 0644)) < 0) {
210 warn("open %s", path);
211 goto fail;
212 }
213
214 if ((fp = fdopen(fd, "w")) == NULL) {
215 warn("fdopen");
216 goto fail;
217 }
218 fd = -1; /* owned by fp now */
219
220 if ((pkey = es256_pk_to_EVP_PKEY(pk)) == NULL) {
221 warnx("es256_pk_to_EVP_PKEY");
222 goto fail;
223 }
224
225 if (PEM_write_PUBKEY(fp, pkey) == 0) {
226 warnx("PEM_write_PUBKEY");
227 goto fail;
228 }
229
230 ok = 0;
231fail:
232 es256_pk_free(&pk);
233
234 if (fp != NULL) {
235 fclose(fp);
236 }
237 if (fd != -1) {
238 close(fd);
239 }
240 if (pkey != NULL) {
241 EVP_PKEY_free(pkey);
242 }
243
244 return (ok);
245}
246
247RSA *
248read_rsa_pubkey(const char *path)
249{
250 FILE *fp = NULL;
251 EVP_PKEY *pkey = NULL;
252 RSA *rsa = NULL;
253
254 if ((fp = fopen(path, "r")) == NULL) {
255 warn("fopen");
256 goto fail;
257 }
258
259 if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
260 warnx("PEM_read_PUBKEY");
261 goto fail;
262 }
263 if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) {
264 warnx("EVP_PKEY_get1_RSA");
265 goto fail;
266 }
267
268fail:
269 if (fp != NULL) {
270 fclose(fp);
271 }
272 if (pkey != NULL) {
273 EVP_PKEY_free(pkey);
274 }
275
276 return (rsa);
277}
278
279int
280write_rsa_pubkey(const char *path, const void *ptr, size_t len)
281{
282 FILE *fp = NULL;
283 EVP_PKEY *pkey = NULL;
284 rs256_pk_t *pk = NULL;
285 int fd = -1;
286 int ok = -1;
287
288 if ((pk = rs256_pk_new()) == NULL) {
289 warnx("rs256_pk_new");
290 goto fail;
291 }
292
293 if (rs256_pk_from_ptr(pk, ptr, len) != FIDO_OK) {
294 warnx("rs256_pk_from_ptr");
295 goto fail;
296 }
297
298 if ((fd = open(path, O_WRONLY | O_CREAT, 0644)) < 0) {
299 warn("open %s", path);
300 goto fail;
301 }
302
303 if ((fp = fdopen(fd, "w")) == NULL) {
304 warn("fdopen");
305 goto fail;
306 }
307 fd = -1; /* owned by fp now */
308
309 if ((pkey = rs256_pk_to_EVP_PKEY(pk)) == NULL) {
310 warnx("rs256_pk_to_EVP_PKEY");
311 goto fail;
312 }
313
314 if (PEM_write_PUBKEY(fp, pkey) == 0) {
315 warnx("PEM_write_PUBKEY");
316 goto fail;
317 }
318
319 ok = 0;
320fail:
321 rs256_pk_free(&pk);
322
323 if (fp != NULL) {
324 fclose(fp);
325 }
326 if (fd != -1) {
327 close(fd);
328 }
329 if (pkey != NULL) {
330 EVP_PKEY_free(pkey);
331 }
332
333 return (ok);
334}
335
336EVP_PKEY *
337read_eddsa_pubkey(const char *path)
338{
339 FILE *fp = NULL;
340 EVP_PKEY *pkey = NULL;
341
342 if ((fp = fopen(path, "r")) == NULL) {
343 warn("fopen");
344 goto fail;
345 }
346
347 if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
348 warnx("PEM_read_PUBKEY");
349 goto fail;
350 }
351
352fail:
353 if (fp) {
354 fclose(fp);
355 }
356
357 return (pkey);
358}
359
360int
361write_eddsa_pubkey(const char *path, const void *ptr, size_t len)
362{
363 FILE *fp = NULL;
364 EVP_PKEY *pkey = NULL;
365 eddsa_pk_t *pk = NULL;
366 int fd = -1;
367 int ok = -1;
368
369 if ((pk = eddsa_pk_new()) == NULL) {
370 warnx("eddsa_pk_new");
371 goto fail;
372 }
373
374 if (eddsa_pk_from_ptr(pk, ptr, len) != FIDO_OK) {
375 warnx("eddsa_pk_from_ptr");
376 goto fail;
377 }
378
379 if ((fd = open(path, O_WRONLY | O_CREAT, 0644)) < 0) {
380 warn("open %s", path);
381 goto fail;
382 }
383
384 if ((fp = fdopen(fd, "w")) == NULL) {
385 warn("fdopen");
386 goto fail;
387 }
388 fd = -1; /* owned by fp now */
389
390 if ((pkey = eddsa_pk_to_EVP_PKEY(pk)) == NULL) {
391 warnx("eddsa_pk_to_EVP_PKEY");
392 goto fail;
393 }
394
395 if (PEM_write_PUBKEY(fp, pkey) == 0) {
396 warnx("PEM_write_PUBKEY");
397 goto fail;
398 }
399
400 ok = 0;
401fail:
402 eddsa_pk_free(&pk);
403
404 if (fp != NULL) {
405 fclose(fp);
406 }
407 if (fd != -1) {
408 close(fd);
409 }
410 if (pkey != NULL) {
411 EVP_PKEY_free(pkey);
412 }
413
414 return (ok);
415}
diff --git a/fuzz/CMakeLists.txt b/fuzz/CMakeLists.txt
new file mode 100644
index 0000000..ad30aa3
--- /dev/null
+++ b/fuzz/CMakeLists.txt
@@ -0,0 +1,44 @@
1# Copyright (c) 2019 Yubico AB. All rights reserved.
2# Use of this source code is governed by a BSD-style
3# license that can be found in the LICENSE file.
4
5list(APPEND COMPAT_SOURCES
6 ../openbsd-compat/strlcpy.c
7 ../openbsd-compat/strlcat.c
8)
9
10list(APPEND COMMON_SOURCES
11 mutator_aux.c
12 uniform_random.c
13)
14
15
16# fuzz_cred
17add_executable(fuzz_cred fuzz_cred.c ${COMMON_SOURCES} ${COMPAT_SOURCES})
18target_compile_options(fuzz_cred PRIVATE ${FUZZ_LDFLAGS})
19set_target_properties(fuzz_cred PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS})
20target_link_libraries(fuzz_cred fido2_shared)
21
22# fuzz_assert
23add_executable(fuzz_assert fuzz_assert.c ${COMMON_SOURCES} ${COMPAT_SOURCES})
24target_compile_options(fuzz_assert PRIVATE ${FUZZ_LDFLAGS})
25set_target_properties(fuzz_assert PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS})
26target_link_libraries(fuzz_assert fido2_shared)
27
28# fuzz_mgmt
29add_executable(fuzz_mgmt fuzz_mgmt.c ${COMMON_SOURCES} ${COMPAT_SOURCES})
30target_compile_options(fuzz_mgmt PRIVATE ${FUZZ_LDFLAGS})
31set_target_properties(fuzz_mgmt PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS})
32target_link_libraries(fuzz_mgmt fido2_shared)
33
34# fuzz_credman
35add_executable(fuzz_credman fuzz_credman.c ${COMMON_SOURCES} ${COMPAT_SOURCES})
36target_compile_options(fuzz_credman PRIVATE ${FUZZ_LDFLAGS})
37set_target_properties(fuzz_credman PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS})
38target_link_libraries(fuzz_credman fido2_shared)
39
40# fuzz_bio
41add_executable(fuzz_bio fuzz_bio.c ${COMMON_SOURCES} ${COMPAT_SOURCES})
42target_compile_options(fuzz_bio PRIVATE ${FUZZ_LDFLAGS})
43set_target_properties(fuzz_bio PROPERTIES LINK_FLAGS ${FUZZ_LDFLAGS})
44target_link_libraries(fuzz_bio fido2_shared)
diff --git a/fuzz/README b/fuzz/README
new file mode 100644
index 0000000..ecb02bb
--- /dev/null
+++ b/fuzz/README
@@ -0,0 +1,157 @@
1libfido2 can be fuzzed using AFL or libFuzzer, with or without
2ASAN/MSAN/UBSAN.
3
4AFL is more convenient when fuzzing the path from the authenticator to
5libfido2 in an existing application. To do so, use preload-snoop.c with a real
6authenticator to obtain an initial corpus, rebuild libfido2 with -DFUZZ=1
7-DAFL=1, and use preload-fuzz.c to read device data from stdin. Examples of
8this approach can be found in the harnesses under fuzz/harnesses/ that fuzz
9the standalone examples and tools bundled with libfido2.
10
11libFuzzer is better suited for bespoke fuzzers; see fuzz_cred.c, fuzz_credman.c,
12fuzz_assert.c, and fuzz_mgmt.c for examples. To build these harnesses,
13use -DFUZZ=1 -DLIBFUZZER=1.
14
15To run under ASAN/MSAN/UBSAN, libfido2 needs to be linked against flavours of
16libcbor and OpenSSL built with the respective sanitiser. In order to keep
17memory utilisation at a manageable level, you can either enforce limits at
18the OS level (e.g. cgroups on Linux) or, alternatively, patch libcbor with
19the diff at the bottom of this file.
20
211. Using ASAN + UBSAN
22
23- Make sure you have libcbor built with -fsanitize=address;
24- Make sure you have OpenSSL built with -fsanitize=address;
25- Rebuild libfido2 with -DASAN=1 -DUBSAN=1.
26
271.1 Decide where your workspace will live
28
29$ export FAKEROOT=/home/pedro/fakeroot
30$ mkdir -p ${FAKEROOT}/src
31
321.2 Building libcbor with ASAN
33
34$ git clone https://github.com/pjk/libcbor ${FAKEROOT}/src/libcbor
35$ cd ${FAKEROOT}/src/libcbor
36
37Assuming libfido2 is under ${FAKEROOT}/src/libfido2:
38
39$ patch -p0 < ${FAKEROOT}/src/libfido2/fuzz/README
40$ mkdir build
41$ cd build
42$ cmake -DCMAKE_C_FLAGS_DEBUG="-g2 -fno-omit-frame-pointer" \
43 -DCMAKE_C_COMPILER=clang -DCMAKE_BUILD_TYPE=Debug \
44 -DCMAKE_INSTALL_PREFIX=${FAKEROOT} -DSANITIZE=ON \
45 -DCMAKE_INSTALL_LIBDIR=lib ..
46$ make
47$ make install
48
491.3 Building OpenSSL with ASAN
50
51$ git clone https://github.com/openssl/openssl ${FAKEROOT}/src/openssl
52$ cd ${FAKEROOT}/src/openssl
53$ ./Configure linux-x86_64-clang enable-asan --prefix=${FAKEROOT} \
54 --openssldir=${FAKEROOT}/openssl
55$ make clean
56$ make
57$ make install_sw
58
591.4 Building libfido2 with libFuzzer and ASAN + UBSAN
60
61$ cd ${FAKEROOT}/src/libfido2
62$ mkdir build
63$ cd build
64$ cmake -DFUZZ=1 -DLIBFUZZER=1 -DASAN=1 -DUBSAN=1 -DCMAKE_C_COMPILER=clang \
65 -DCRYPTO_INCLUDE_DIRS=${FAKEROOT}/include \
66 -DCRYPTO_LIBRARY_DIRS=${FAKEROOT}/lib \
67 -DCBOR_INCLUDE_DIRS=${FAKEROOT}/include \
68 -DCBOR_LIBRARY_DIRS=${FAKEROOT}/lib \
69 -DCMAKE_BUILD_TYPE=Debug ..
70$ make
71
722. Using MSAN + UBSAN
73
74- Make sure you have libcbor built with -fsanitize=memory;
75- Make sure you have OpenSSL built with -fsanitize=memory;
76- Rebuild libfido2 with -DMSAN=1 -DUBSAN=1.
77
782.1 Decide where your workspace will live
79
80$ export FAKEROOT=/home/pedro/fakeroot
81$ mkdir -p ${FAKEROOT}/src
82
832.2 Building libcbor with MSAN
84
85$ git clone https://github.com/pjk/libcbor ${FAKEROOT}/src/libcbor
86$ cd ${FAKEROOT}/src/libcbor
87
88Assuming libfido2 is under ${FAKEROOT}/src/libfido2:
89
90$ patch -p0 < ${FAKEROOT}/src/libfido2/fuzz/README
91$ mkdir build
92$ cd build
93$ cmake -DCMAKE_C_FLAGS_DEBUG="-fsanitize=memory,undefined -g2 -fno-omit-frame-pointer" \
94 -DCMAKE_C_COMPILER=clang -DCMAKE_BUILD_TYPE=Debug \
95 -DCMAKE_INSTALL_PREFIX=${FAKEROOT} -DSANITIZE=OFF \
96 -DCMAKE_INSTALL_LIBDIR=lib ..
97$ make
98$ make install
99
1002.2 Building OpenSSL with MSAN
101
102$ mkdir -p ${FAKEROOT}/src
103$ git clone https://github.com/openssl/openssl ${FAKEROOT}/src/openssl
104$ cd ${FAKEROOT}/src/openssl
105$ ./Configure linux-x86_64-clang enable-msan --prefix=${FAKEROOT} \
106 --openssldir=${FAKEROOT}/openssl
107$ make clean
108$ make
109$ make install_sw
110
1112.3 Building libfido2 with libFuzzer and MSAN + UBSAN
112
113$ cd ${FAKEROOT}/src/libfido2
114$ mkdir build
115$ cd build
116$ cmake -DFUZZ=1 -DLIBFUZZER=1 -DMSAN=1 -DUBSAN=1 -DCMAKE_C_COMPILER=clang \
117 -DCRYPTO_INCLUDE_DIRS=${FAKEROOT}/include \
118 -DCRYPTO_LIBRARY_DIRS=${FAKEROOT}/lib \
119 -DCBOR_INCLUDE_DIRS=${FAKEROOT}/include \
120 -DCBOR_LIBRARY_DIRS=${FAKEROOT}/lib \
121 -DCMAKE_BUILD_TYPE=Debug ..
122$ make
123
1243. Running the libFuzzer harnesses
125
126When running under ASAN, you may want to set ASAN_OPTIONS to
127'allocator_may_return_null=1:detect_stack_use_after_return=1'.
128
129The recommended way to run the harnesses is:
130
131$ fuzz_{assert,cred,credman,mgmt} -use_value_profile=1 -reload=30 \
132 -print_pcs=1 -print_funcs=30 -timeout=10 -max_len=17408 CORPUS_DIR
133
134You may want to use -jobs or -workers depending on the number of logical
135cores available for fuzzing.
136
1374. Auxiliary scripts
138
139A set of harnesses and auxiliary scripts can be found under harnesses/. To
140compile coverage reports, adjust the harnesses to your setup and run 'report'.
141
142diff --git src/cbor/internal/memory_utils.c src/cbor/internal/memory_utils.c
143index aa049a2..e294b38 100644
144--- src/cbor/internal/memory_utils.c
145+++ src/cbor/internal/memory_utils.c
146@@ -28,7 +28,10 @@ bool _cbor_safe_to_multiply(size_t a, size_t b) {
147
148 void* _cbor_alloc_multiple(size_t item_size, size_t item_count) {
149 if (_cbor_safe_to_multiply(item_size, item_count)) {
150- return _CBOR_MALLOC(item_size * item_count);
151+ if (item_count > 1000) {
152+ return NULL;
153+ } else
154+ return _CBOR_MALLOC(item_size * item_count);
155 } else {
156 return NULL;
157 }
diff --git a/fuzz/corpus.tgz b/fuzz/corpus.tgz
new file mode 100644
index 0000000..9da3099
--- /dev/null
+++ b/fuzz/corpus.tgz
Binary files differ
diff --git a/fuzz/functions.txt b/fuzz/functions.txt
new file mode 100644
index 0000000..cd652f2
--- /dev/null
+++ b/fuzz/functions.txt
@@ -0,0 +1,564 @@
1File '/home/pedro/projects/libfido2/src/aes256.c':
2Name Regions Miss Cover Lines Miss Cover
3-----------------------------------------------------------------------------
4aes256_cbc_enc 28 0 100.00% 41 0 100.00%
5aes256_cbc_dec 28 0 100.00% 41 0 100.00%
6-----------------------------------------------------------------------------
7TOTAL 56 0 100.00% 82 0 100.00%
8
9File '/home/pedro/projects/libfido2/src/assert.c':
10Name Regions Miss Cover Lines Miss Cover
11---------------------------------------------------------------------------------------
12fido_dev_get_assert 35 3 91.43% 38 4 89.47%
13fido_check_flags 13 0 100.00% 18 0 100.00%
14fido_verify_sig_es256 17 2 88.24% 31 7 77.42%
15fido_verify_sig_rs256 17 2 88.24% 31 7 77.42%
16fido_verify_sig_eddsa 23 2 91.30% 43 7 83.72%
17fido_assert_verify 48 4 91.67% 79 4 94.94%
18fido_assert_set_clientdata_hash 6 0 100.00% 6 0 100.00%
19fido_assert_set_hmac_salt 10 0 100.00% 7 0 100.00%
20fido_assert_set_rp 12 1 91.67% 14 3 78.57%
21fido_assert_allow_cred 13 2 84.62% 29 3 89.66%
22fido_assert_set_extensions 9 0 100.00% 8 0 100.00%
23fido_assert_set_options 6 6 0.00% 6 6 0.00%
24fido_assert_set_up 2 0 100.00% 5 0 100.00%
25fido_assert_set_uv 2 0 100.00% 5 0 100.00%
26fido_assert_clientdata_hash_ptr 1 0 100.00% 3 0 100.00%
27fido_assert_clientdata_hash_len 1 0 100.00% 3 0 100.00%
28fido_assert_new 1 0 100.00% 3 0 100.00%
29fido_assert_reset_tx 1 0 100.00% 15 0 100.00%
30fido_assert_reset_rx 6 0 100.00% 24 0 100.00%
31fido_assert_free 6 0 100.00% 13 0 100.00%
32fido_assert_count 1 0 100.00% 3 0 100.00%
33fido_assert_rp_id 1 0 100.00% 3 0 100.00%
34fido_assert_flags 4 0 100.00% 6 0 100.00%
35fido_assert_sigcount 4 0 100.00% 6 0 100.00%
36fido_assert_authdata_ptr 4 0 100.00% 6 0 100.00%
37fido_assert_authdata_len 4 0 100.00% 6 0 100.00%
38fido_assert_sig_ptr 4 0 100.00% 6 0 100.00%
39fido_assert_sig_len 4 0 100.00% 6 0 100.00%
40fido_assert_id_ptr 4 0 100.00% 6 0 100.00%
41fido_assert_id_len 4 0 100.00% 6 0 100.00%
42fido_assert_user_id_ptr 4 0 100.00% 6 0 100.00%
43fido_assert_user_id_len 4 0 100.00% 6 0 100.00%
44fido_assert_user_icon 4 0 100.00% 6 0 100.00%
45fido_assert_user_name 4 0 100.00% 6 0 100.00%
46fido_assert_user_display_name 4 0 100.00% 6 0 100.00%
47fido_assert_hmac_secret_ptr 4 0 100.00% 6 0 100.00%
48fido_assert_hmac_secret_len 4 0 100.00% 6 0 100.00%
49fido_assert_set_authdata 24 0 100.00% 35 0 100.00%
50fido_assert_set_authdata_raw 24 0 100.00% 34 0 100.00%
51fido_assert_set_sig 14 0 100.00% 17 0 100.00%
52fido_assert_set_count 10 0 100.00% 21 0 100.00%
53assert.c:fido_dev_get_assert_wait 21 0 100.00% 16 0 100.00%
54assert.c:fido_dev_get_assert_tx 58 4 93.10% 84 11 86.90%
55assert.c:fido_dev_get_assert_rx 20 0 100.00% 38 0 100.00%
56assert.c:adjust_assert_count 24 0 100.00% 33 0 100.00%
57assert.c:parse_assert_reply 11 0 100.00% 25 0 100.00%
58assert.c:fido_get_next_assert_tx 9 0 100.00% 11 0 100.00%
59assert.c:fido_get_next_assert_rx 16 2 87.50% 26 4 84.62%
60assert.c:decrypt_hmac_secrets 9 1 88.89% 15 4 73.33%
61assert.c:check_extensions 4 0 100.00% 9 0 100.00%
62assert.c:get_signed_hash 32 0 100.00% 46 0 100.00%
63assert.c:fido_assert_clean_authdata 1 0 100.00% 9 0 100.00%
64assert.c:fido_assert_clean_sig 1 0 100.00% 5 0 100.00%
65---------------------------------------------------------------------------------------
66TOTAL 569 29 94.90% 901 60 93.34%
67
68File '/home/pedro/projects/libfido2/src/authkey.c':
69Name Regions Miss Cover Lines Miss Cover
70---------------------------------------------------------------------------------------
71fido_dev_authkey 1 0 100.00% 3 0 100.00%
72authkey.c:fido_dev_authkey_wait 10 0 100.00% 9 0 100.00%
73authkey.c:fido_dev_authkey_tx 19 0 100.00% 33 0 100.00%
74authkey.c:fido_dev_authkey_rx 7 0 100.00% 18 0 100.00%
75authkey.c:parse_authkey 8 0 100.00% 12 0 100.00%
76---------------------------------------------------------------------------------------
77TOTAL 45 0 100.00% 75 0 100.00%
78
79File '/home/pedro/projects/libfido2/src/bio.c':
80Name Regions Miss Cover Lines Miss Cover
81---------------------------------------------------------------------------------------
82fido_bio_dev_get_template_array 5 2 60.00% 6 0 100.00%
83fido_bio_dev_set_template_name 7 0 100.00% 6 0 100.00%
84fido_bio_dev_enroll_begin 24 2 91.67% 36 0 100.00%
85fido_bio_dev_enroll_continue 5 2 60.00% 6 0 100.00%
86fido_bio_dev_enroll_cancel 1 1 0.00% 3 3 0.00%
87fido_bio_dev_enroll_remove 1 0 100.00% 3 0 100.00%
88fido_bio_dev_get_info 1 0 100.00% 3 0 100.00%
89fido_bio_template_name 1 0 100.00% 3 0 100.00%
90fido_bio_template_id_ptr 1 0 100.00% 3 0 100.00%
91fido_bio_template_id_len 1 0 100.00% 3 0 100.00%
92fido_bio_template_array_count 1 0 100.00% 3 0 100.00%
93fido_bio_template_array_new 1 0 100.00% 3 0 100.00%
94fido_bio_template_new 1 0 100.00% 3 0 100.00%
95fido_bio_template_array_free 6 0 100.00% 10 0 100.00%
96fido_bio_template_free 6 0 100.00% 10 0 100.00%
97fido_bio_template_set_name 8 0 100.00% 9 0 100.00%
98fido_bio_template_set_id 8 0 100.00% 10 0 100.00%
99fido_bio_template 4 0 100.00% 6 0 100.00%
100fido_bio_enroll_new 1 0 100.00% 3 0 100.00%
101fido_bio_info_new 1 0 100.00% 3 0 100.00%
102fido_bio_info_type 1 0 100.00% 3 0 100.00%
103fido_bio_info_max_samples 1 0 100.00% 3 0 100.00%
104fido_bio_enroll_free 6 0 100.00% 11 0 100.00%
105fido_bio_info_free 6 0 100.00% 9 0 100.00%
106fido_bio_enroll_remaining_samples 1 0 100.00% 3 0 100.00%
107fido_bio_enroll_last_status 1 0 100.00% 3 0 100.00%
108bio.c:bio_get_template_array_wait 11 0 100.00% 9 0 100.00%
109bio.c:bio_tx 43 0 100.00% 65 0 100.00%
110bio.c:bio_prepare_hmac 18 0 100.00% 36 0 100.00%
111bio.c:bio_rx_template_array 12 0 100.00% 21 0 100.00%
112bio.c:bio_parse_template_array 26 1 96.15% 34 4 88.24%
113bio.c:decode_template_array 12 1 91.67% 23 3 86.96%
114bio.c:decode_template 9 0 100.00% 18 0 100.00%
115bio.c:bio_set_template_name_wait 19 0 100.00% 24 0 100.00%
116bio.c:bio_enroll_begin_wait 17 1 94.12% 24 3 87.50%
117bio.c:bio_rx_enroll_begin 16 0 100.00% 29 0 100.00%
118bio.c:bio_parse_enroll_status 20 0 100.00% 31 0 100.00%
119bio.c:bio_parse_template_id 8 0 100.00% 12 0 100.00%
120bio.c:bio_enroll_continue_wait 19 0 100.00% 25 0 100.00%
121bio.c:bio_rx_enroll_continue 12 0 100.00% 22 0 100.00%
122bio.c:bio_enroll_cancel_wait 11 11 0.00% 12 12 0.00%
123bio.c:bio_enroll_remove_wait 17 0 100.00% 24 0 100.00%
124bio.c:bio_get_info_wait 11 0 100.00% 11 0 100.00%
125bio.c:bio_rx_info 12 0 100.00% 21 0 100.00%
126bio.c:bio_reset_info 1 0 100.00% 4 0 100.00%
127bio.c:bio_parse_info 20 0 100.00% 31 0 100.00%
128bio.c:bio_reset_template_array 4 0 100.00% 8 0 100.00%
129bio.c:bio_reset_template 1 0 100.00% 6 0 100.00%
130bio.c:bio_reset_enroll 3 0 100.00% 7 0 100.00%
131---------------------------------------------------------------------------------------
132TOTAL 422 21 95.02% 661 25 96.22%
133
134File '/home/pedro/projects/libfido2/src/blob.c':
135Name Regions Miss Cover Lines Miss Cover
136---------------------------------------------------------------------------------------
137fido_blob_new 1 0 100.00% 3 0 100.00%
138fido_blob_set 11 1 90.91% 25 4 84.00%
139fido_blob_free 8 0 100.00% 16 0 100.00%
140fido_free_blob_array 9 0 100.00% 17 0 100.00%
141fido_blob_encode 6 0 100.00% 6 0 100.00%
142fido_blob_decode 1 0 100.00% 3 0 100.00%
143fido_blob_is_empty 3 0 100.00% 3 0 100.00%
144---------------------------------------------------------------------------------------
145TOTAL 39 1 97.44% 73 4 94.52%
146
147File '/home/pedro/projects/libfido2/src/buf.c':
148Name Regions Miss Cover Lines Miss Cover
149---------------------------------------------------------------------------------------
150fido_buf_read 4 0 100.00% 10 0 100.00%
151fido_buf_write 4 1 75.00% 10 1 90.00%
152---------------------------------------------------------------------------------------
153TOTAL 8 1 87.50% 20 1 95.00%
154
155File '/home/pedro/projects/libfido2/src/cbor.c':
156Name Regions Miss Cover Lines Miss Cover
157---------------------------------------------------------------------------------------
158cbor_map_iter 20 1 95.00% 30 4 86.67%
159cbor_array_iter 12 0 100.00% 20 0 100.00%
160cbor_parse_reply 27 0 100.00% 43 0 100.00%
161cbor_vector_free 6 0 100.00% 5 0 100.00%
162cbor_bytestring_copy 14 0 100.00% 22 0 100.00%
163cbor_string_copy 14 0 100.00% 23 0 100.00%
164cbor_add_bytestring 14 0 100.00% 26 0 100.00%
165cbor_add_string 14 0 100.00% 26 0 100.00%
166cbor_add_bool 14 0 100.00% 26 0 100.00%
167cbor_flatten_vector 14 1 92.86% 21 1 95.24%
168cbor_build_frame 15 0 100.00% 32 0 100.00%
169cbor_encode_rp_entity 13 0 100.00% 14 0 100.00%
170cbor_encode_user_entity 21 0 100.00% 18 0 100.00%
171cbor_encode_pubkey_param 36 0 100.00% 48 0 100.00%
172cbor_encode_pubkey 10 0 100.00% 13 0 100.00%
173cbor_encode_pubkey_list 18 2 88.89% 23 0 100.00%
174cbor_encode_extensions 13 1 92.31% 16 0 100.00%
175cbor_encode_options 13 0 100.00% 14 0 100.00%
176cbor_encode_assert_options 13 0 100.00% 14 0 100.00%
177cbor_encode_pin_auth 8 0 100.00% 12 0 100.00%
178cbor_encode_pin_opt 1 0 100.00% 3 0 100.00%
179cbor_encode_pin_enc 4 0 100.00% 12 0 100.00%
180cbor_encode_change_pin_auth 44 1 97.73% 69 3 95.65%
181cbor_encode_set_pin_auth 17 0 100.00% 28 0 100.00%
182cbor_encode_pin_hash_enc 15 0 100.00% 27 0 100.00%
183cbor_encode_hmac_secret_param 41 1 97.56% 66 4 93.94%
184cbor_decode_fmt 9 0 100.00% 18 0 100.00%
185cbor_decode_pubkey 21 1 95.24% 32 2 93.75%
186cbor_decode_cred_authdata 31 0 100.00% 46 0 100.00%
187cbor_decode_assert_authdata 23 0 100.00% 44 0 100.00%
188cbor_decode_attstmt 8 0 100.00% 10 0 100.00%
189cbor_decode_uint64 4 0 100.00% 10 0 100.00%
190cbor_decode_cred_id 8 0 100.00% 10 0 100.00%
191cbor_decode_user 8 0 100.00% 10 0 100.00%
192cbor_decode_rp_entity 8 0 100.00% 10 0 100.00%
193cbor.c:ctap_check_cbor 28 0 100.00% 32 0 100.00%
194cbor.c:check_key_type 8 0 100.00% 9 0 100.00%
195cbor.c:cbor_add_arg 13 0 100.00% 28 0 100.00%
196cbor.c:sha256 7 0 100.00% 15 0 100.00%
197cbor.c:get_cose_alg 36 0 100.00% 48 0 100.00%
198cbor.c:find_cose_alg 35 0 100.00% 40 0 100.00%
199cbor.c:decode_attcred 25 0 100.00% 58 0 100.00%
200cbor.c:decode_extensions 16 4 75.00% 34 6 82.35%
201cbor.c:decode_extension 19 19 0.00% 27 27 0.00%
202cbor.c:decode_hmac_secret 16 0 100.00% 32 0 100.00%
203cbor.c:decode_hmac_secret_aux 7 0 100.00% 17 0 100.00%
204cbor.c:decode_attstmt_entry 29 0 100.00% 39 0 100.00%
205cbor.c:decode_x5c 4 0 100.00% 8 0 100.00%
206cbor.c:decode_cred_id_entry 10 0 100.00% 23 0 100.00%
207cbor.c:decode_user_entry 25 0 100.00% 39 0 100.00%
208cbor.c:decode_rp_entity_entry 15 0 100.00% 29 0 100.00%
209---------------------------------------------------------------------------------------
210TOTAL 844 31 96.33% 1319 47 96.44%
211
212File '/home/pedro/projects/libfido2/src/cred.c':
213Name Regions Miss Cover Lines Miss Cover
214---------------------------------------------------------------------------------------
215fido_dev_make_cred 12 0 100.00% 9 0 100.00%
216fido_check_rp_id 4 0 100.00% 14 0 100.00%
217fido_cred_verify 45 0 100.00% 71 0 100.00%
218fido_cred_verify_self 54 10 81.48% 90 14 84.44%
219fido_cred_new 1 0 100.00% 3 0 100.00%
220fido_cred_reset_tx 1 0 100.00% 20 0 100.00%
221fido_cred_reset_rx 1 0 100.00% 8 0 100.00%
222fido_cred_free 6 1 83.33% 13 0 100.00%
223fido_cred_set_authdata 22 0 100.00% 36 0 100.00%
224fido_cred_set_authdata_raw 22 2 90.91% 35 4 88.57%
225fido_cred_set_x509 12 0 100.00% 16 0 100.00%
226fido_cred_set_sig 12 0 100.00% 16 0 100.00%
227fido_cred_exclude 14 2 85.71% 25 3 88.00%
228fido_cred_set_clientdata_hash 6 0 100.00% 6 0 100.00%
229fido_cred_set_rp 18 2 88.89% 26 6 76.92%
230fido_cred_set_user 33 4 87.88% 50 13 74.00%
231fido_cred_set_extensions 9 0 100.00% 8 0 100.00%
232fido_cred_set_options 6 6 0.00% 6 6 0.00%
233fido_cred_set_rk 2 0 100.00% 5 0 100.00%
234fido_cred_set_uv 2 0 100.00% 5 0 100.00%
235fido_cred_set_fmt 16 4 75.00% 15 1 93.33%
236fido_cred_set_type 17 2 88.24% 9 1 88.89%
237fido_cred_type 1 0 100.00% 3 0 100.00%
238fido_cred_flags 1 0 100.00% 3 0 100.00%
239fido_cred_clientdata_hash_ptr 1 0 100.00% 3 0 100.00%
240fido_cred_clientdata_hash_len 1 0 100.00% 3 0 100.00%
241fido_cred_x5c_ptr 1 0 100.00% 3 0 100.00%
242fido_cred_x5c_len 1 0 100.00% 3 0 100.00%
243fido_cred_sig_ptr 1 0 100.00% 3 0 100.00%
244fido_cred_sig_len 1 0 100.00% 3 0 100.00%
245fido_cred_authdata_ptr 1 0 100.00% 3 0 100.00%
246fido_cred_authdata_len 1 0 100.00% 3 0 100.00%
247fido_cred_pubkey_ptr 9 0 100.00% 20 0 100.00%
248fido_cred_pubkey_len 9 0 100.00% 20 0 100.00%
249fido_cred_id_ptr 1 0 100.00% 3 0 100.00%
250fido_cred_id_len 1 0 100.00% 3 0 100.00%
251fido_cred_fmt 1 0 100.00% 3 0 100.00%
252fido_cred_rp_id 1 0 100.00% 3 0 100.00%
253fido_cred_rp_name 1 0 100.00% 3 0 100.00%
254fido_cred_user_name 1 0 100.00% 3 0 100.00%
255fido_cred_display_name 1 0 100.00% 3 0 100.00%
256fido_cred_user_id_ptr 1 0 100.00% 3 0 100.00%
257fido_cred_user_id_len 1 0 100.00% 3 0 100.00%
258cred.c:fido_dev_make_cred_wait 10 0 100.00% 9 0 100.00%
259cred.c:fido_dev_make_cred_tx 59 0 100.00% 81 0 100.00%
260cred.c:fido_dev_make_cred_rx 22 0 100.00% 28 0 100.00%
261cred.c:parse_makecred_reply 10 0 100.00% 23 0 100.00%
262cred.c:check_extensions 4 0 100.00% 9 0 100.00%
263cred.c:get_signed_hash_packed 23 1 95.65% 38 3 92.11%
264cred.c:get_signed_hash_u2f 22 0 100.00% 20 0 100.00%
265cred.c:verify_sig 27 1 96.30% 40 4 90.00%
266cred.c:fido_cred_clean_authdata 1 0 100.00% 9 0 100.00%
267cred.c:fido_cred_clean_x509 1 0 100.00% 5 0 100.00%
268cred.c:fido_cred_clean_sig 1 0 100.00% 5 0 100.00%
269---------------------------------------------------------------------------------------
270TOTAL 532 35 93.42% 850 55 93.53%
271
272File '/home/pedro/projects/libfido2/src/credman.c':
273Name Regions Miss Cover Lines Miss Cover
274---------------------------------------------------------------------------------------
275fido_credman_get_dev_metadata 9 2 77.78% 8 0 100.00%
276fido_credman_get_dev_rk 9 2 77.78% 8 0 100.00%
277fido_credman_del_dev_rk 9 2 77.78% 8 0 100.00%
278fido_credman_get_dev_rp 9 2 77.78% 8 0 100.00%
279fido_credman_rk_new 1 0 100.00% 3 0 100.00%
280fido_credman_rk_free 6 1 83.33% 10 0 100.00%
281fido_credman_rk_count 1 0 100.00% 3 0 100.00%
282fido_credman_rk 4 0 100.00% 6 0 100.00%
283fido_credman_metadata_new 1 0 100.00% 3 0 100.00%
284fido_credman_metadata_free 6 1 83.33% 9 0 100.00%
285fido_credman_rk_existing 1 0 100.00% 3 0 100.00%
286fido_credman_rk_remaining 1 0 100.00% 3 0 100.00%
287fido_credman_rp_new 1 0 100.00% 3 0 100.00%
288fido_credman_rp_free 6 1 83.33% 10 0 100.00%
289fido_credman_rp_count 1 0 100.00% 3 0 100.00%
290fido_credman_rp_id 4 0 100.00% 6 0 100.00%
291fido_credman_rp_name 4 0 100.00% 6 0 100.00%
292fido_credman_rp_id_hash_len 4 0 100.00% 6 0 100.00%
293fido_credman_rp_id_hash_ptr 4 0 100.00% 6 0 100.00%
294credman.c:credman_get_metadata_wait 11 0 100.00% 9 0 100.00%
295credman.c:credman_tx 30 0 100.00% 53 0 100.00%
296credman.c:credman_prepare_hmac 21 1 95.24% 43 2 95.35%
297credman.c:credman_rx_metadata 12 0 100.00% 21 0 100.00%
298credman.c:credman_parse_metadata 9 0 100.00% 19 0 100.00%
299credman.c:credman_get_rk_wait 27 0 100.00% 26 0 100.00%
300credman.c:credman_rx_rk 20 0 100.00% 36 0 100.00%
301credman.c:credman_parse_rk_count 16 0 100.00% 25 0 100.00%
302credman.c:credman_grow_array 17 2 88.24% 28 5 82.14%
303credman.c:credman_parse_rk 13 0 100.00% 25 0 100.00%
304credman.c:credman_rx_next_rk 16 2 87.50% 26 4 84.62%
305credman.c:credman_del_rk_wait 16 0 100.00% 19 0 100.00%
306credman.c:credman_get_rp_wait 23 0 100.00% 16 0 100.00%
307credman.c:credman_rx_rp 20 0 100.00% 36 0 100.00%
308credman.c:credman_parse_rp_count 16 0 100.00% 25 0 100.00%
309credman.c:credman_parse_rp 9 0 100.00% 19 0 100.00%
310credman.c:credman_rx_next_rp 16 2 87.50% 26 4 84.62%
311credman.c:credman_reset_rk 4 0 100.00% 10 0 100.00%
312credman.c:credman_reset_rp 4 0 100.00% 15 0 100.00%
313---------------------------------------------------------------------------------------
314TOTAL 381 18 95.28% 589 15 97.45%
315
316File '/home/pedro/projects/libfido2/src/dev.c':
317Name Regions Miss Cover Lines Miss Cover
318---------------------------------------------------------------------------------------
319fido_dev_open 1 0 100.00% 3 0 100.00%
320fido_dev_close 8 2 75.00% 9 0 100.00%
321fido_dev_cancel 8 2 75.00% 6 3 50.00%
322fido_dev_set_io_functions 18 4 77.78% 19 6 68.42%
323fido_init 7 1 85.71% 4 0 100.00%
324fido_dev_new 9 1 88.89% 22 4 81.82%
325fido_dev_free 6 0 100.00% 10 0 100.00%
326fido_dev_protocol 1 0 100.00% 3 0 100.00%
327fido_dev_major 1 0 100.00% 3 0 100.00%
328fido_dev_minor 1 0 100.00% 3 0 100.00%
329fido_dev_build 1 0 100.00% 3 0 100.00%
330fido_dev_flags 1 0 100.00% 3 0 100.00%
331fido_dev_is_fido2 2 0 100.00% 3 0 100.00%
332fido_dev_force_u2f 2 0 100.00% 3 0 100.00%
333fido_dev_force_fido2 2 2 0.00% 3 3 0.00%
334dev.c:fido_dev_open_wait 10 0 100.00% 9 0 100.00%
335dev.c:fido_dev_open_tx 26 8 69.23% 32 12 62.50%
336dev.c:obtain_nonce 13 2 84.62% 18 2 88.89%
337dev.c:fido_dev_open_rx 14 0 100.00% 27 0 100.00%
338---------------------------------------------------------------------------------------
339TOTAL 131 22 83.21% 183 30 83.61%
340
341File '/home/pedro/projects/libfido2/src/ecdh.c':
342Name Regions Miss Cover Lines Miss Cover
343---------------------------------------------------------------------------------------
344fido_do_ecdh 29 0 100.00% 44 0 100.00%
345ecdh.c:do_ecdh 39 0 100.00% 60 0 100.00%
346---------------------------------------------------------------------------------------
347TOTAL 68 0 100.00% 104 0 100.00%
348
349File '/home/pedro/projects/libfido2/src/eddsa.c':
350Name Regions Miss Cover Lines Miss Cover
351---------------------------------------------------------------------------------------
352eddsa_pk_decode 8 0 100.00% 10 0 100.00%
353eddsa_pk_new 1 0 100.00% 3 0 100.00%
354eddsa_pk_free 6 0 100.00% 11 0 100.00%
355eddsa_pk_from_ptr 6 0 100.00% 8 0 100.00%
356eddsa_pk_to_EVP_PKEY 3 0 100.00% 9 0 100.00%
357eddsa_pk_from_EVP_PKEY 14 4 71.43% 12 2 83.33%
358eddsa.c:decode_pubkey_point 8 0 100.00% 14 0 100.00%
359eddsa.c:decode_coord 8 0 100.00% 12 0 100.00%
360---------------------------------------------------------------------------------------
361TOTAL 54 4 92.59% 79 2 97.47%
362
363File '/home/pedro/projects/libfido2/src/err.c':
364Name Regions Miss Cover Lines Miss Cover
365---------------------------------------------------------------------------------------
366fido_strerr 108 108 0.00% 112 112 0.00%
367---------------------------------------------------------------------------------------
368TOTAL 108 108 0.00% 112 112 0.00%
369
370File '/home/pedro/projects/libfido2/src/es256.c':
371Name Regions Miss Cover Lines Miss Cover
372---------------------------------------------------------------------------------------
373es256_pk_decode 8 0 100.00% 10 0 100.00%
374es256_pk_encode 56 0 100.00% 70 0 100.00%
375es256_sk_new 1 0 100.00% 3 0 100.00%
376es256_sk_free 6 0 100.00% 11 0 100.00%
377es256_pk_new 1 0 100.00% 3 0 100.00%
378es256_pk_free 6 0 100.00% 11 0 100.00%
379es256_pk_from_ptr 6 0 100.00% 8 0 100.00%
380es256_pk_set_x 1 0 100.00% 5 0 100.00%
381es256_pk_set_y 1 0 100.00% 5 0 100.00%
382es256_sk_create 39 2 94.87% 46 6 86.96%
383es256_pk_to_EVP_PKEY 41 0 100.00% 58 0 100.00%
384es256_pk_from_EC_KEY 38 2 94.74% 39 7 82.05%
385es256_sk_to_EVP_PKEY 27 0 100.00% 41 0 100.00%
386es256_derive_pk 25 0 100.00% 34 0 100.00%
387es256.c:decode_pubkey_point 9 0 100.00% 16 0 100.00%
388es256.c:decode_coord 8 0 100.00% 12 0 100.00%
389---------------------------------------------------------------------------------------
390TOTAL 273 4 98.53% 372 13 96.51%
391
392File '/home/pedro/projects/libfido2/src/extern.h':
393Name Regions Miss Cover Lines Miss Cover
394---------------------------------------------------------------------------------------
395
396File '/home/pedro/projects/libfido2/src/fido.h':
397Name Regions Miss Cover Lines Miss Cover
398---------------------------------------------------------------------------------------
399
400File '/home/pedro/projects/libfido2/src/hid.c':
401Name Regions Miss Cover Lines Miss Cover
402---------------------------------------------------------------------------------------
403fido_dev_info_new 1 1 0.00% 3 3 0.00%
404fido_dev_info_free 9 9 0.00% 17 17 0.00%
405fido_dev_info_ptr 1 1 0.00% 3 3 0.00%
406fido_dev_info_path 1 1 0.00% 3 3 0.00%
407fido_dev_info_vendor 1 1 0.00% 3 3 0.00%
408fido_dev_info_product 1 1 0.00% 3 3 0.00%
409fido_dev_info_manufacturer_string 1 1 0.00% 3 3 0.00%
410fido_dev_info_product_string 1 1 0.00% 3 3 0.00%
411---------------------------------------------------------------------------------------
412TOTAL 16 16 0.00% 38 38 0.00%
413
414File '/home/pedro/projects/libfido2/src/hid_linux.c':
415Name Regions Miss Cover Lines Miss Cover
416---------------------------------------------------------------------------------------
417fido_dev_info_manifest 33 33 0.00% 40 40 0.00%
418fido_hid_open 6 6 0.00% 11 11 0.00%
419fido_hid_close 1 1 0.00% 6 6 0.00%
420fido_hid_read 12 12 0.00% 16 16 0.00%
421fido_hid_write 12 12 0.00% 16 16 0.00%
422hid_linux.c:copy_info 35 35 0.00% 56 56 0.00%
423hid_linux.c:is_fido 6 6 0.00% 14 14 0.00%
424hid_linux.c:get_report_descriptor 17 17 0.00% 31 31 0.00%
425hid_linux.c:get_usage_info 16 16 0.00% 33 33 0.00%
426hid_linux.c:get_key_len 6 6 0.00% 14 14 0.00%
427hid_linux.c:get_key_val 6 6 0.00% 20 20 0.00%
428hid_linux.c:parse_uevent 16 16 0.00% 30 30 0.00%
429---------------------------------------------------------------------------------------
430TOTAL 166 166 0.00% 287 287 0.00%
431
432File '/home/pedro/projects/libfido2/src/info.c':
433Name Regions Miss Cover Lines Miss Cover
434---------------------------------------------------------------------------------------
435fido_dev_get_cbor_info 1 0 100.00% 3 0 100.00%
436fido_cbor_info_new 1 0 100.00% 3 0 100.00%
437fido_cbor_info_free 6 1 83.33% 14 0 100.00%
438fido_cbor_info_versions_ptr 1 0 100.00% 3 0 100.00%
439fido_cbor_info_versions_len 1 0 100.00% 3 0 100.00%
440fido_cbor_info_extensions_ptr 1 0 100.00% 3 0 100.00%
441fido_cbor_info_extensions_len 1 0 100.00% 3 0 100.00%
442fido_cbor_info_aaguid_ptr 1 0 100.00% 3 0 100.00%
443fido_cbor_info_aaguid_len 1 0 100.00% 3 0 100.00%
444fido_cbor_info_options_name_ptr 1 0 100.00% 3 0 100.00%
445fido_cbor_info_options_value_ptr 1 0 100.00% 3 0 100.00%
446fido_cbor_info_options_len 1 0 100.00% 3 0 100.00%
447fido_cbor_info_maxmsgsiz 1 0 100.00% 3 0 100.00%
448fido_cbor_info_protocols_ptr 1 0 100.00% 3 0 100.00%
449fido_cbor_info_protocols_len 1 0 100.00% 3 0 100.00%
450info.c:fido_dev_get_cbor_info_wait 10 0 100.00% 9 0 100.00%
451info.c:fido_dev_get_cbor_info_tx 9 0 100.00% 13 0 100.00%
452info.c:fido_dev_get_cbor_info_rx 7 0 100.00% 18 0 100.00%
453info.c:parse_reply_element 13 0 100.00% 27 0 100.00%
454info.c:decode_versions 12 0 100.00% 21 0 100.00%
455info.c:decode_version 4 0 100.00% 14 0 100.00%
456info.c:decode_extensions 12 0 100.00% 21 0 100.00%
457info.c:decode_extension 4 0 100.00% 14 0 100.00%
458info.c:decode_aaguid 8 0 100.00% 12 0 100.00%
459info.c:decode_options 11 0 100.00% 18 0 100.00%
460info.c:decode_option 11 0 100.00% 22 0 100.00%
461info.c:decode_protocols 12 0 100.00% 21 0 100.00%
462info.c:decode_protocol 6 0 100.00% 16 0 100.00%
463info.c:free_str_array 4 0 100.00% 8 0 100.00%
464info.c:free_opt_array 4 0 100.00% 9 0 100.00%
465info.c:free_byte_array 1 0 100.00% 6 0 100.00%
466---------------------------------------------------------------------------------------
467TOTAL 148 1 99.32% 305 0 100.00%
468
469File '/home/pedro/projects/libfido2/src/io.c':
470Name Regions Miss Cover Lines Miss Cover
471---------------------------------------------------------------------------------------
472fido_tx 18 0 100.00% 35 0 100.00%
473fido_rx 34 3 91.18% 84 12 85.71%
474fido_rx_cbor_status 9 0 100.00% 13 0 100.00%
475io.c:tx_preamble 16 1 93.75% 24 1 95.83%
476io.c:tx_frame 16 1 93.75% 21 0 100.00%
477io.c:rx_preamble 11 0 100.00% 12 0 100.00%
478io.c:rx_frame 9 1 88.89% 12 0 100.00%
479---------------------------------------------------------------------------------------
480TOTAL 113 6 94.69% 201 13 93.53%
481
482File '/home/pedro/projects/libfido2/src/iso7816.c':
483Name Regions Miss Cover Lines Miss Cover
484---------------------------------------------------------------------------------------
485iso7816_new 4 0 100.00% 19 0 100.00%
486iso7816_free 6 0 100.00% 11 0 100.00%
487iso7816_add 6 1 83.33% 10 0 100.00%
488iso7816_ptr 1 0 100.00% 3 0 100.00%
489iso7816_len 1 0 100.00% 4 0 100.00%
490---------------------------------------------------------------------------------------
491TOTAL 18 1 94.44% 47 0 100.00%
492
493File '/home/pedro/projects/libfido2/src/log.c':
494Name Regions Miss Cover Lines Miss Cover
495---------------------------------------------------------------------------------------
496fido_log_init 1 1 0.00% 3 3 0.00%
497fido_log_xxd 11 8 27.27% 18 12 33.33%
498fido_log_debug 4 1 75.00% 13 8 38.46%
499---------------------------------------------------------------------------------------
500TOTAL 16 10 37.50% 34 23 32.35%
501
502File '/home/pedro/projects/libfido2/src/pin.c':
503Name Regions Miss Cover Lines Miss Cover
504---------------------------------------------------------------------------------------
505fido_dev_get_pin_token 1 0 100.00% 3 0 100.00%
506fido_dev_set_pin 1 0 100.00% 3 0 100.00%
507fido_dev_get_retry_count 1 0 100.00% 3 0 100.00%
508cbor_add_pin_params 17 0 100.00% 27 0 100.00%
509pin.c:fido_dev_get_pin_token_wait 10 0 100.00% 9 0 100.00%
510pin.c:fido_dev_get_pin_token_tx 29 0 100.00% 40 0 100.00%
511pin.c:fido_dev_get_pin_token_rx 21 0 100.00% 36 0 100.00%
512pin.c:parse_pintoken 8 0 100.00% 12 0 100.00%
513pin.c:fido_dev_set_pin_wait 16 0 100.00% 22 0 100.00%
514pin.c:fido_dev_change_pin_tx 41 0 100.00% 59 0 100.00%
515pin.c:pad64 18 0 100.00% 24 0 100.00%
516pin.c:fido_dev_set_pin_tx 33 0 100.00% 48 0 100.00%
517pin.c:fido_dev_get_retry_count_wait 10 0 100.00% 9 0 100.00%
518pin.c:fido_dev_get_retry_count_tx 19 0 100.00% 28 0 100.00%
519pin.c:fido_dev_get_retry_count_rx 12 0 100.00% 21 0 100.00%
520pin.c:parse_retry_count 13 0 100.00% 20 0 100.00%
521---------------------------------------------------------------------------------------
522TOTAL 250 0 100.00% 364 0 100.00%
523
524File '/home/pedro/projects/libfido2/src/reset.c':
525Name Regions Miss Cover Lines Miss Cover
526---------------------------------------------------------------------------------------
527fido_dev_reset 1 0 100.00% 3 0 100.00%
528reset.c:fido_dev_reset_wait 10 0 100.00% 9 0 100.00%
529reset.c:fido_dev_reset_tx 9 0 100.00% 11 0 100.00%
530---------------------------------------------------------------------------------------
531TOTAL 20 0 100.00% 23 0 100.00%
532
533File '/home/pedro/projects/libfido2/src/rs256.c':
534Name Regions Miss Cover Lines Miss Cover
535---------------------------------------------------------------------------------------
536rs256_pk_decode 8 0 100.00% 10 0 100.00%
537rs256_pk_new 1 0 100.00% 3 0 100.00%
538rs256_pk_free 6 0 100.00% 11 0 100.00%
539rs256_pk_from_ptr 6 0 100.00% 8 0 100.00%
540rs256_pk_to_EVP_PKEY 32 0 100.00% 48 0 100.00%
541rs256_pk_from_RSA 32 6 81.25% 32 9 71.88%
542rs256.c:decode_rsa_pubkey 9 0 100.00% 16 0 100.00%
543rs256.c:decode_bignum 8 0 100.00% 12 0 100.00%
544---------------------------------------------------------------------------------------
545TOTAL 102 6 94.12% 140 9 93.57%
546
547File '/home/pedro/projects/libfido2/src/u2f.c':
548Name Regions Miss Cover Lines Miss Cover
549---------------------------------------------------------------------------------------
550u2f_register 70 1 98.57% 89 0 100.00%
551u2f_authenticate 27 0 100.00% 33 0 100.00%
552u2f.c:key_lookup 44 0 100.00% 69 0 100.00%
553u2f.c:send_dummy_register 31 1 96.77% 50 0 100.00%
554u2f.c:parse_register_reply 57 0 100.00% 83 0 100.00%
555u2f.c:x5c_get 21 1 95.24% 37 3 91.89%
556u2f.c:sig_get 8 1 87.50% 16 6 62.50%
557u2f.c:encode_cred_authdata 37 2 94.59% 82 6 92.68%
558u2f.c:cbor_blob_from_ec_point 22 0 100.00% 39 0 100.00%
559u2f.c:u2f_authenticate_single 34 2 94.12% 53 4 92.45%
560u2f.c:do_auth 50 1 98.00% 72 0 100.00%
561u2f.c:parse_auth_reply 23 2 91.30% 29 3 89.66%
562u2f.c:authdata_fake 12 0 100.00% 34 0 100.00%
563---------------------------------------------------------------------------------------
564TOTAL 436 11 97.48% 686 22 96.79%
diff --git a/fuzz/fuzz_assert.c b/fuzz/fuzz_assert.c
new file mode 100644
index 0000000..0395345
--- /dev/null
+++ b/fuzz/fuzz_assert.c
@@ -0,0 +1,664 @@
1/*
2 * Copyright (c) 2019 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <assert.h>
8#include <stdbool.h>
9#include <stdint.h>
10#include <stdlib.h>
11#include <string.h>
12#include <stdio.h>
13
14#include "mutator_aux.h"
15#include "fido.h"
16#include "fido/es256.h"
17#include "fido/rs256.h"
18#include "fido/eddsa.h"
19
20#include "../openbsd-compat/openbsd-compat.h"
21
22#define TAG_U2F 0x01
23#define TAG_TYPE 0x02
24#define TAG_CDH 0x03
25#define TAG_RP_ID 0x04
26#define TAG_EXT 0x05
27#define TAG_SEED 0x06
28#define TAG_UP 0x07
29#define TAG_UV 0x08
30#define TAG_WIRE_DATA 0x09
31#define TAG_CRED_COUNT 0x0a
32#define TAG_CRED 0x0b
33#define TAG_ES256 0x0c
34#define TAG_RS256 0x0d
35#define TAG_PIN 0x0e
36#define TAG_EDDSA 0x0f
37
38/* Parameter set defining a FIDO2 get assertion operation. */
39struct param {
40 char pin[MAXSTR];
41 char rp_id[MAXSTR];
42 int ext;
43 int seed;
44 struct blob cdh;
45 struct blob cred;
46 struct blob es256;
47 struct blob rs256;
48 struct blob eddsa;
49 struct blob wire_data;
50 uint8_t cred_count;
51 uint8_t type;
52 uint8_t u2f;
53 uint8_t up;
54 uint8_t uv;
55};
56
57/* Example parameters. */
58static const char dummy_rp_id[] = "localhost";
59static const char dummy_pin[] = "9}4gT:8d=A37Dh}U";
60
61static const uint8_t dummy_cdh[] = {
62 0xec, 0x8d, 0x8f, 0x78, 0x42, 0x4a, 0x2b, 0xb7,
63 0x82, 0x34, 0xaa, 0xca, 0x07, 0xa1, 0xf6, 0x56,
64 0x42, 0x1c, 0xb6, 0xf6, 0xb3, 0x00, 0x86, 0x52,
65 0x35, 0x2d, 0xa2, 0x62, 0x4a, 0xbe, 0x89, 0x76,
66};
67
68static const uint8_t dummy_es256[] = {
69 0xcc, 0x1b, 0x50, 0xac, 0xc4, 0x19, 0xf8, 0x3a,
70 0xee, 0x0a, 0x77, 0xd6, 0xf3, 0x53, 0xdb, 0xef,
71 0xf2, 0xb9, 0x5c, 0x2d, 0x8b, 0x1e, 0x52, 0x58,
72 0x88, 0xf4, 0x0b, 0x85, 0x1f, 0x40, 0x6d, 0x18,
73 0x15, 0xb3, 0xcc, 0x25, 0x7c, 0x38, 0x3d, 0xec,
74 0xdf, 0xad, 0xbd, 0x46, 0x91, 0xc3, 0xac, 0x30,
75 0x94, 0x2a, 0xf7, 0x78, 0x35, 0x70, 0x59, 0x6f,
76 0x28, 0xcb, 0x8e, 0x07, 0x85, 0xb5, 0x91, 0x96,
77};
78
79static const uint8_t dummy_rs256[] = {
80 0xd2, 0xa8, 0xc0, 0x11, 0x82, 0x9e, 0x57, 0x2e,
81 0x60, 0xae, 0x8c, 0xb0, 0x09, 0xe1, 0x58, 0x2b,
82 0x99, 0xec, 0xc3, 0x11, 0x1b, 0xef, 0x81, 0x49,
83 0x34, 0x53, 0x6a, 0x01, 0x65, 0x2c, 0x24, 0x09,
84 0x30, 0x87, 0x98, 0x51, 0x6e, 0x30, 0x4f, 0x60,
85 0xbd, 0x54, 0xd2, 0x54, 0xbd, 0x94, 0x42, 0xdd,
86 0x63, 0xe5, 0x2c, 0xc6, 0x04, 0x32, 0xc0, 0x8f,
87 0x72, 0xd5, 0xb4, 0xf0, 0x4f, 0x42, 0xe5, 0xb0,
88 0xa2, 0x95, 0x11, 0xfe, 0xd8, 0xb0, 0x65, 0x34,
89 0xff, 0xfb, 0x44, 0x97, 0x52, 0xfc, 0x67, 0x23,
90 0x0b, 0xad, 0xf3, 0x3a, 0x82, 0xd4, 0x96, 0x10,
91 0x87, 0x6b, 0xfa, 0xd6, 0x51, 0x60, 0x3e, 0x1c,
92 0xae, 0x19, 0xb8, 0xce, 0x08, 0xae, 0x9a, 0xee,
93 0x78, 0x16, 0x22, 0xcc, 0x92, 0xcb, 0xa8, 0x95,
94 0x34, 0xe5, 0xb9, 0x42, 0x6a, 0xf0, 0x2e, 0x82,
95 0x1f, 0x4c, 0x7d, 0x84, 0x94, 0x68, 0x7b, 0x97,
96 0x2b, 0xf7, 0x7d, 0x67, 0x83, 0xbb, 0xc7, 0x8a,
97 0x31, 0x5a, 0xf3, 0x2a, 0x95, 0xdf, 0x63, 0xe7,
98 0x4e, 0xee, 0x26, 0xda, 0x87, 0x00, 0xe2, 0x23,
99 0x4a, 0x33, 0x9a, 0xa0, 0x1b, 0xce, 0x60, 0x1f,
100 0x98, 0xa1, 0xb0, 0xdb, 0xbf, 0x20, 0x59, 0x27,
101 0xf2, 0x06, 0xd9, 0xbe, 0x37, 0xa4, 0x03, 0x6b,
102 0x6a, 0x4e, 0xaf, 0x22, 0x68, 0xf3, 0xff, 0x28,
103 0x59, 0x05, 0xc9, 0xf1, 0x28, 0xf4, 0xbb, 0x35,
104 0xe0, 0xc2, 0x68, 0xc2, 0xaa, 0x54, 0xac, 0x8c,
105 0xc1, 0x69, 0x9e, 0x4b, 0x32, 0xfc, 0x53, 0x58,
106 0x85, 0x7d, 0x3f, 0x51, 0xd1, 0xc9, 0x03, 0x02,
107 0x13, 0x61, 0x62, 0xda, 0xf8, 0xfe, 0x3e, 0xc8,
108 0x95, 0x12, 0xfb, 0x0c, 0xdf, 0x06, 0x65, 0x6f,
109 0x23, 0xc7, 0x83, 0x7c, 0x50, 0x2d, 0x27, 0x25,
110 0x4d, 0xbf, 0x94, 0xf0, 0x89, 0x04, 0xb9, 0x2d,
111 0xc4, 0xa5, 0x32, 0xa9, 0x25, 0x0a, 0x99, 0x59,
112 0x01, 0x00, 0x01,
113};
114
115static const uint8_t dummy_eddsa[] = {
116 0xfe, 0x8b, 0x61, 0x50, 0x31, 0x7a, 0xe6, 0xdf,
117 0xb1, 0x04, 0x9d, 0x4d, 0xb5, 0x7a, 0x5e, 0x96,
118 0x4c, 0xb2, 0xf9, 0x5f, 0x72, 0x47, 0xb5, 0x18,
119 0xe2, 0x39, 0xdf, 0x2f, 0x87, 0x19, 0xb3, 0x02,
120};
121
122/*
123 * Collection of HID reports from an authenticator issued with a FIDO2
124 * get assertion using the example parameters above.
125 */
126static const uint8_t dummy_wire_data_fido[] = {
127 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0xf7,
128 0x6f, 0xda, 0x52, 0xfd, 0xcb, 0xb6, 0x24, 0x00,
129 0x92, 0x00, 0x0e, 0x02, 0x05, 0x00, 0x02, 0x05,
130 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
131 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
132 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
133 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
134 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
135 0x00, 0x92, 0x00, 0x0e, 0x90, 0x00, 0x51, 0x00,
136 0xa1, 0x01, 0xa5, 0x01, 0x02, 0x03, 0x38, 0x18,
137 0x20, 0x01, 0x21, 0x58, 0x20, 0xe9, 0x1d, 0x9b,
138 0xac, 0x14, 0x25, 0x5f, 0xda, 0x1e, 0x11, 0xdb,
139 0xae, 0xc2, 0x90, 0x22, 0xca, 0x32, 0xec, 0x32,
140 0xe6, 0x05, 0x15, 0x44, 0xe5, 0xe8, 0xbc, 0x4f,
141 0x0a, 0xb6, 0x1a, 0xeb, 0x11, 0x22, 0x58, 0x20,
142 0xcc, 0x72, 0xf0, 0x22, 0xe8, 0x28, 0x82, 0xc5,
143 0x00, 0x92, 0x00, 0x0e, 0x00, 0xa6, 0x65, 0x6e,
144 0xff, 0x1e, 0xe3, 0x7f, 0x27, 0x44, 0x2d, 0xfb,
145 0x8d, 0x41, 0xfa, 0x85, 0x0e, 0xcb, 0xda, 0x95,
146 0x64, 0x64, 0x9b, 0x1f, 0x34, 0x00, 0x00, 0x00,
147 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
148 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
149 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
150 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
151 0x00, 0x92, 0x00, 0x0e, 0x90, 0x00, 0x14, 0x00,
152 0xa1, 0x02, 0x50, 0xee, 0x40, 0x4c, 0x85, 0xd7,
153 0xa1, 0x2f, 0x56, 0xc4, 0x4e, 0xc5, 0x93, 0x41,
154 0xd0, 0x3b, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00,
155 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
156 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
157 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
158 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
159 0x00, 0x92, 0x00, 0x0e, 0x90, 0x00, 0xcb, 0x00,
160 0xa3, 0x01, 0xa2, 0x62, 0x69, 0x64, 0x58, 0x40,
161 0x4a, 0x4c, 0x9e, 0xcc, 0x81, 0x7d, 0x42, 0x03,
162 0x2b, 0x41, 0xd1, 0x38, 0xd3, 0x49, 0xb4, 0xfc,
163 0xfb, 0xe4, 0x4e, 0xe4, 0xff, 0x76, 0x34, 0x16,
164 0x68, 0x06, 0x9d, 0xa6, 0x01, 0x32, 0xb9, 0xff,
165 0xc2, 0x35, 0x0d, 0x89, 0x43, 0x66, 0x12, 0xf8,
166 0x8e, 0x5b, 0xde, 0xf4, 0xcc, 0xec, 0x9d, 0x03,
167 0x00, 0x92, 0x00, 0x0e, 0x00, 0x85, 0xc2, 0xf5,
168 0xe6, 0x8e, 0xeb, 0x3f, 0x3a, 0xec, 0xc3, 0x1d,
169 0x04, 0x6e, 0xf3, 0x5b, 0x88, 0x64, 0x74, 0x79,
170 0x70, 0x65, 0x6a, 0x70, 0x75, 0x62, 0x6c, 0x69,
171 0x63, 0x2d, 0x6b, 0x65, 0x79, 0x02, 0x58, 0x25,
172 0x49, 0x96, 0x0d, 0xe5, 0x88, 0x0e, 0x8c, 0x68,
173 0x74, 0x34, 0x17, 0x0f, 0x64, 0x76, 0x60, 0x5b,
174 0x8f, 0xe4, 0xae, 0xb9, 0xa2, 0x86, 0x32, 0xc7,
175 0x00, 0x92, 0x00, 0x0e, 0x01, 0x99, 0x5c, 0xf3,
176 0xba, 0x83, 0x1d, 0x97, 0x63, 0x04, 0x00, 0x00,
177 0x00, 0x09, 0x03, 0x58, 0x47, 0x30, 0x45, 0x02,
178 0x21, 0x00, 0xcf, 0x3f, 0x36, 0x0e, 0x1f, 0x6f,
179 0xd6, 0xa0, 0x9d, 0x13, 0xcf, 0x55, 0xf7, 0x49,
180 0x8f, 0xc8, 0xc9, 0x03, 0x12, 0x76, 0x41, 0x75,
181 0x7b, 0xb5, 0x0a, 0x90, 0xa5, 0x82, 0x26, 0xf1,
182 0x6b, 0x80, 0x02, 0x20, 0x34, 0x9b, 0x7a, 0x82,
183 0x00, 0x92, 0x00, 0x0e, 0x02, 0xd3, 0xe1, 0x79,
184 0x49, 0x55, 0x41, 0x9f, 0xa4, 0x06, 0x06, 0xbd,
185 0xc8, 0xb9, 0x2b, 0x5f, 0xe1, 0xa7, 0x99, 0x1c,
186 0xa1, 0xfc, 0x7e, 0x3e, 0xd5, 0x85, 0x2e, 0x11,
187 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
188 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
189 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
190 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
191};
192
193/*
194 * Collection of HID reports from an authenticator issued with a U2F
195 * authentication using the example parameters above.
196 */
197static const uint8_t dummy_wire_data_u2f[] = {
198 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0x0f,
199 0x26, 0x9c, 0xd3, 0x87, 0x0d, 0x7b, 0xf6, 0x00,
200 0x00, 0x99, 0x01, 0x02, 0x01, 0x01, 0x00, 0x01,
201 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
202 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
203 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
204 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
205 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
206 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x02, 0x69,
207 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
208 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
209 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
210 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
211 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
212 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
213 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
214 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x02, 0x69,
215 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
216 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
217 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
218 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
219 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
220 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
221 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
222 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x02, 0x69,
223 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
224 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
225 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
226 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
227 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
228 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
229 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
230 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x02, 0x69,
231 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
232 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
233 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
234 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
235 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
236 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
237 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
238 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x02, 0x69,
239 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
240 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
241 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
242 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
243 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
244 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
245 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
246 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x02, 0x69,
247 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
248 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
249 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
250 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
251 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
252 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
253 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
254 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x02, 0x69,
255 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
256 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
257 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
258 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
259 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
260 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
261 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
262 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x02, 0x69,
263 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
264 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
265 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
266 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
267 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
268 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
269 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
270 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x02, 0x69,
271 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
272 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
273 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
274 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
275 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
276 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
277 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
278 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x4e, 0x01,
279 0x00, 0x00, 0x00, 0x2c, 0x30, 0x45, 0x02, 0x20,
280 0x1c, 0xf5, 0x7c, 0xf6, 0xde, 0xbe, 0xe9, 0x86,
281 0xee, 0x97, 0xb7, 0x64, 0xa3, 0x4e, 0x7a, 0x70,
282 0x85, 0xd0, 0x66, 0xf9, 0xf0, 0xcd, 0x04, 0x5d,
283 0x97, 0xf2, 0x3c, 0x22, 0xe3, 0x0e, 0x61, 0xc8,
284 0x02, 0x21, 0x00, 0x97, 0xef, 0xae, 0x36, 0xe6,
285 0x17, 0x9f, 0x5e, 0x2d, 0xd7, 0x8c, 0x34, 0xa7,
286 0x00, 0x00, 0x99, 0x01, 0x00, 0xa1, 0xe9, 0xfb,
287 0x8f, 0x86, 0x8c, 0xe3, 0x1e, 0xde, 0x3f, 0x4e,
288 0x1b, 0xe1, 0x2f, 0x8f, 0x2f, 0xca, 0x42, 0x26,
289 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
290 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
291 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
292 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
293 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
294};
295
296int LLVMFuzzerTestOneInput(const uint8_t *, size_t);
297size_t LLVMFuzzerCustomMutator(uint8_t *, size_t, size_t, unsigned int);
298
299static int
300unpack(const uint8_t *ptr, size_t len, struct param *p) NO_MSAN
301{
302 uint8_t **pp = (void *)&ptr;
303
304 if (unpack_byte(TAG_UV, pp, &len, &p->uv) < 0 ||
305 unpack_byte(TAG_UP, pp, &len, &p->up) < 0 ||
306 unpack_byte(TAG_U2F, pp, &len, &p->u2f) < 0 ||
307 unpack_byte(TAG_TYPE, pp, &len, &p->type) < 0 ||
308 unpack_byte(TAG_CRED_COUNT, pp, &len, &p->cred_count) < 0 ||
309 unpack_int(TAG_EXT, pp, &len, &p->ext) < 0 ||
310 unpack_int(TAG_SEED, pp, &len, &p->seed) < 0 ||
311 unpack_string(TAG_RP_ID, pp, &len, p->rp_id) < 0 ||
312 unpack_string(TAG_PIN, pp, &len, p->pin) < 0 ||
313 unpack_blob(TAG_WIRE_DATA, pp, &len, &p->wire_data) < 0 ||
314 unpack_blob(TAG_RS256, pp, &len, &p->rs256) < 0 ||
315 unpack_blob(TAG_ES256, pp, &len, &p->es256) < 0 ||
316 unpack_blob(TAG_EDDSA, pp, &len, &p->eddsa) < 0 ||
317 unpack_blob(TAG_CRED, pp, &len, &p->cred) < 0 ||
318 unpack_blob(TAG_CDH, pp, &len, &p->cdh) < 0)
319 return (-1);
320
321 return (0);
322}
323
324static size_t
325pack(uint8_t *ptr, size_t len, const struct param *p)
326{
327 const size_t max = len;
328
329 if (pack_byte(TAG_UV, &ptr, &len, p->uv) < 0 ||
330 pack_byte(TAG_UP, &ptr, &len, p->up) < 0 ||
331 pack_byte(TAG_U2F, &ptr, &len, p->u2f) < 0 ||
332 pack_byte(TAG_TYPE, &ptr, &len, p->type) < 0 ||
333 pack_byte(TAG_CRED_COUNT, &ptr, &len, p->cred_count) < 0 ||
334 pack_int(TAG_EXT, &ptr, &len, p->ext) < 0 ||
335 pack_int(TAG_SEED, &ptr, &len, p->seed) < 0 ||
336 pack_string(TAG_RP_ID, &ptr, &len, p->rp_id) < 0 ||
337 pack_string(TAG_PIN, &ptr, &len, p->pin) < 0 ||
338 pack_blob(TAG_WIRE_DATA, &ptr, &len, &p->wire_data) < 0 ||
339 pack_blob(TAG_RS256, &ptr, &len, &p->rs256) < 0 ||
340 pack_blob(TAG_ES256, &ptr, &len, &p->es256) < 0 ||
341 pack_blob(TAG_EDDSA, &ptr, &len, &p->eddsa) < 0 ||
342 pack_blob(TAG_CRED, &ptr, &len, &p->cred) < 0 ||
343 pack_blob(TAG_CDH, &ptr, &len, &p->cdh) < 0)
344 return (0);
345
346 return (max - len);
347}
348
349static void
350get_assert(fido_assert_t *assert, uint8_t u2f, const struct blob *cdh,
351 const char *rp_id, int ext, uint8_t up, uint8_t uv, const char *pin,
352 uint8_t cred_count, struct blob *cred)
353{
354 fido_dev_t *dev;
355 fido_dev_io_t io;
356
357 io.open = dev_open;
358 io.close = dev_close;
359 io.read = dev_read;
360 io.write = dev_write;
361
362 if ((dev = fido_dev_new()) == NULL || fido_dev_set_io_functions(dev,
363 &io) != FIDO_OK || fido_dev_open(dev, "nodev") != FIDO_OK) {
364 fido_dev_free(&dev);
365 return;
366 }
367
368 if (u2f & 1)
369 fido_dev_force_u2f(dev);
370
371 for (uint8_t i = 0; i < cred_count; i++)
372 fido_assert_allow_cred(assert, cred->body, cred->len);
373
374 fido_assert_set_clientdata_hash(assert, cdh->body, cdh->len);
375 fido_assert_set_rp(assert, rp_id);
376 if (ext & 1)
377 fido_assert_set_extensions(assert, FIDO_EXT_HMAC_SECRET);
378 if (up & 1)
379 fido_assert_set_up(assert, FIDO_OPT_TRUE);
380 if (uv & 1)
381 fido_assert_set_uv(assert, FIDO_OPT_TRUE);
382 /* XXX reuse cred as hmac salt to keep struct param small */
383 fido_assert_set_hmac_salt(assert, cred->body, cred->len);
384
385 fido_dev_get_assert(dev, assert, u2f & 1 ? NULL : pin);
386
387 fido_dev_cancel(dev);
388 fido_dev_close(dev);
389 fido_dev_free(&dev);
390}
391
392static void
393verify_assert(int type, const unsigned char *cdh_ptr, size_t cdh_len,
394 const char *rp_id, const unsigned char *authdata_ptr, size_t authdata_len,
395 const unsigned char *sig_ptr, size_t sig_len, uint8_t up, uint8_t uv,
396 int ext, void *pk)
397{
398 fido_assert_t *assert = NULL;
399
400 if ((assert = fido_assert_new()) == NULL)
401 return;
402
403 fido_assert_set_clientdata_hash(assert, cdh_ptr, cdh_len);
404 fido_assert_set_rp(assert, rp_id);
405 fido_assert_set_count(assert, 1);
406 if (fido_assert_set_authdata(assert, 0, authdata_ptr,
407 authdata_len) != FIDO_OK) {
408 fido_assert_set_authdata_raw(assert, 0, authdata_ptr,
409 authdata_len);
410 }
411 fido_assert_set_extensions(assert, ext);
412 if (up & 1) fido_assert_set_up(assert, FIDO_OPT_TRUE);
413 if (uv & 1) fido_assert_set_uv(assert, FIDO_OPT_TRUE);
414 fido_assert_set_sig(assert, 0, sig_ptr, sig_len);
415 fido_assert_verify(assert, 0, type, pk);
416
417 fido_assert_free(&assert);
418}
419
420/*
421 * Do a dummy conversion to exercise rs256_pk_from_RSA().
422 */
423static void
424rs256_convert(const rs256_pk_t *k)
425{
426 EVP_PKEY *pkey = NULL;
427 rs256_pk_t *pk = NULL;
428 RSA *rsa = NULL;
429 volatile int r;
430
431 if ((pkey = rs256_pk_to_EVP_PKEY(k)) == NULL ||
432 (pk = rs256_pk_new()) == NULL ||
433 (rsa = EVP_PKEY_get0_RSA(pkey)) == NULL)
434 goto out;
435
436 r = rs256_pk_from_RSA(pk, rsa);
437out:
438 if (pk)
439 rs256_pk_free(&pk);
440 if (pkey)
441 EVP_PKEY_free(pkey);
442}
443
444/*
445 * Do a dummy conversion to exercise eddsa_pk_from_EVP_PKEY().
446 */
447static void
448eddsa_convert(const eddsa_pk_t *k)
449{
450 EVP_PKEY *pkey = NULL;
451 eddsa_pk_t *pk = NULL;
452 volatile int r;
453
454 if ((pkey = eddsa_pk_to_EVP_PKEY(k)) == NULL ||
455 (pk = eddsa_pk_new()) == NULL)
456 goto out;
457
458 r = eddsa_pk_from_EVP_PKEY(pk, pkey);
459out:
460 if (pk)
461 eddsa_pk_free(&pk);
462 if (pkey)
463 EVP_PKEY_free(pkey);
464}
465
466int
467LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
468{
469 struct param p;
470 fido_assert_t *assert = NULL;
471 es256_pk_t *es256_pk = NULL;
472 rs256_pk_t *rs256_pk = NULL;
473 eddsa_pk_t *eddsa_pk = NULL;
474 uint8_t flags;
475 uint32_t sigcount;
476 int cose_alg = 0;
477 void *pk;
478
479 memset(&p, 0, sizeof(p));
480
481 if (unpack(data, size, &p) < 0)
482 return (0);
483
484 srandom((unsigned int)p.seed);
485
486 fido_init(0);
487
488 switch (p.type & 3) {
489 case 0:
490 cose_alg = COSE_ES256;
491
492 if ((es256_pk = es256_pk_new()) == NULL)
493 return (0);
494
495 es256_pk_from_ptr(es256_pk, p.es256.body, p.es256.len);
496 pk = es256_pk;
497
498 break;
499 case 1:
500 cose_alg = COSE_RS256;
501
502 if ((rs256_pk = rs256_pk_new()) == NULL)
503 return (0);
504
505 rs256_pk_from_ptr(rs256_pk, p.rs256.body, p.rs256.len);
506 pk = rs256_pk;
507
508 rs256_convert(pk);
509
510 break;
511 default:
512 cose_alg = COSE_EDDSA;
513
514 if ((eddsa_pk = eddsa_pk_new()) == NULL)
515 return (0);
516
517 eddsa_pk_from_ptr(eddsa_pk, p.eddsa.body, p.eddsa.len);
518 pk = eddsa_pk;
519
520 eddsa_convert(pk);
521
522 break;
523 }
524
525 if ((assert = fido_assert_new()) == NULL)
526 goto out;
527
528 set_wire_data(p.wire_data.body, p.wire_data.len);
529
530 get_assert(assert, p.u2f, &p.cdh, p.rp_id, p.ext, p.up, p.uv, p.pin,
531 p.cred_count, &p.cred);
532
533 /* XXX +1 on purpose */
534 for (size_t i = 0; i <= fido_assert_count(assert); i++) {
535 verify_assert(cose_alg,
536 fido_assert_clientdata_hash_ptr(assert),
537 fido_assert_clientdata_hash_len(assert),
538 fido_assert_rp_id(assert),
539 fido_assert_authdata_ptr(assert, i),
540 fido_assert_authdata_len(assert, i),
541 fido_assert_sig_ptr(assert, i),
542 fido_assert_sig_len(assert, i), p.up, p.uv, p.ext, pk);
543 consume(fido_assert_id_ptr(assert, i),
544 fido_assert_id_len(assert, i));
545 consume(fido_assert_user_id_ptr(assert, i),
546 fido_assert_user_id_len(assert, i));
547 consume(fido_assert_hmac_secret_ptr(assert, i),
548 fido_assert_hmac_secret_len(assert, i));
549 consume(fido_assert_user_icon(assert, i),
550 xstrlen(fido_assert_user_icon(assert, i)));
551 consume(fido_assert_user_name(assert, i),
552 xstrlen(fido_assert_user_name(assert, i)));
553 consume(fido_assert_user_display_name(assert, i),
554 xstrlen(fido_assert_user_display_name(assert, i)));
555 flags = fido_assert_flags(assert, i);
556 consume(&flags, sizeof(flags));
557 sigcount = fido_assert_sigcount(assert, i);
558 consume(&sigcount, sizeof(sigcount));
559 }
560
561out:
562 es256_pk_free(&es256_pk);
563 rs256_pk_free(&rs256_pk);
564 eddsa_pk_free(&eddsa_pk);
565
566 fido_assert_free(&assert);
567
568 return (0);
569}
570
571static size_t
572pack_dummy(uint8_t *ptr, size_t len)
573{
574 struct param dummy;
575 uint8_t blob[16384];
576 size_t blob_len;
577
578 memset(&dummy, 0, sizeof(dummy));
579
580 dummy.type = 1;
581 dummy.ext = FIDO_EXT_HMAC_SECRET;
582
583 strlcpy(dummy.pin, dummy_pin, sizeof(dummy.pin));
584 strlcpy(dummy.rp_id, dummy_rp_id, sizeof(dummy.rp_id));
585
586 dummy.cdh.len = sizeof(dummy_cdh);
587 dummy.es256.len = sizeof(dummy_es256);
588 dummy.rs256.len = sizeof(dummy_rs256);
589 dummy.eddsa.len = sizeof(dummy_eddsa);
590 dummy.wire_data.len = sizeof(dummy_wire_data_fido);
591
592 memcpy(&dummy.cdh.body, &dummy_cdh, dummy.cdh.len);
593 memcpy(&dummy.wire_data.body, &dummy_wire_data_fido,
594 dummy.wire_data.len);
595 memcpy(&dummy.es256.body, &dummy_es256, dummy.es256.len);
596 memcpy(&dummy.rs256.body, &dummy_rs256, dummy.rs256.len);
597 memcpy(&dummy.eddsa.body, &dummy_eddsa, dummy.eddsa.len);
598
599 blob_len = pack(blob, sizeof(blob), &dummy);
600 assert(blob_len != 0);
601
602 if (blob_len > len) {
603 memcpy(ptr, blob, len);
604 return (len);
605 }
606
607 memcpy(ptr, blob, blob_len);
608
609 return (blob_len);
610}
611
612size_t
613LLVMFuzzerCustomMutator(uint8_t *data, size_t size, size_t maxsize,
614 unsigned int seed) NO_MSAN
615{
616 struct param p;
617 uint8_t blob[16384];
618 size_t blob_len;
619
620 (void)seed;
621
622 memset(&p, 0, sizeof(p));
623
624 if (unpack(data, size, &p) < 0)
625 return (pack_dummy(data, maxsize));
626
627 mutate_byte(&p.uv);
628 mutate_byte(&p.up);
629 mutate_byte(&p.u2f);
630 mutate_byte(&p.type);
631 mutate_byte(&p.cred_count);
632
633 mutate_int(&p.ext);
634 p.seed = (int)seed;
635
636 if (p.u2f & 1) {
637 p.wire_data.len = sizeof(dummy_wire_data_u2f);
638 memcpy(&p.wire_data.body, &dummy_wire_data_u2f,
639 p.wire_data.len);
640 } else {
641 p.wire_data.len = sizeof(dummy_wire_data_fido);
642 memcpy(&p.wire_data.body, &dummy_wire_data_fido,
643 p.wire_data.len);
644 }
645
646 mutate_blob(&p.wire_data);
647 mutate_blob(&p.rs256);
648 mutate_blob(&p.es256);
649 mutate_blob(&p.eddsa);
650 mutate_blob(&p.cred);
651 mutate_blob(&p.cdh);
652
653 mutate_string(p.rp_id);
654 mutate_string(p.pin);
655
656 blob_len = pack(blob, sizeof(blob), &p);
657
658 if (blob_len == 0 || blob_len > maxsize)
659 return (0);
660
661 memcpy(data, blob, blob_len);
662
663 return (blob_len);
664}
diff --git a/fuzz/fuzz_bio.c b/fuzz/fuzz_bio.c
new file mode 100644
index 0000000..f1596a7
--- /dev/null
+++ b/fuzz/fuzz_bio.c
@@ -0,0 +1,755 @@
1/*
2 * Copyright (c) 2019 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <assert.h>
8#include <stdint.h>
9#include <stdlib.h>
10#include <string.h>
11#include <stdio.h>
12
13#include "mutator_aux.h"
14#include "fido.h"
15#include "fido/bio.h"
16
17#include "../openbsd-compat/openbsd-compat.h"
18
19#define TAG_PIN 0x01
20#define TAG_NAME 0x02
21#define TAG_SEED 0x03
22#define TAG_ID 0x04
23#define TAG_INFO_WIRE_DATA 0x05
24#define TAG_ENROLL_WIRE_DATA 0x06
25#define TAG_LIST_WIRE_DATA 0x07
26#define TAG_SET_NAME_WIRE_DATA 0x08
27#define TAG_REMOVE_WIRE_DATA 0x09
28
29/* Parameter set defining a FIDO2 credential management operation. */
30struct param {
31 char pin[MAXSTR];
32 char name[MAXSTR];
33 int seed;
34 struct blob id;
35 struct blob info_wire_data;
36 struct blob enroll_wire_data;
37 struct blob list_wire_data;
38 struct blob set_name_wire_data;
39 struct blob remove_wire_data;
40};
41
42/* Example parameters. */
43static const uint8_t dummy_id[] = { 0x5e, 0xd2, };
44static const char dummy_pin[] = "3Q;I){TAx";
45static const char dummy_name[] = "finger1";
46
47/*
48 * Collection of HID reports from an authenticator issued with a FIDO2
49 * 'getFingerprintSensorInfo' bio enrollment command.
50 */
51static const uint8_t dummy_info_wire_data[] = {
52 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0xf0,
53 0x08, 0xc1, 0x8f, 0x76, 0x4b, 0x8f, 0xa9, 0x00,
54 0x10, 0x00, 0x04, 0x02, 0x00, 0x04, 0x06, 0x05,
55 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
56 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
57 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
58 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
59 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
60 0x00, 0x10, 0x00, 0x04, 0x90, 0x00, 0x06, 0x00,
61 0xa2, 0x02, 0x01, 0x03, 0x04, 0x00, 0x00, 0x00,
62 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
63 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
64 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
65 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
66 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
67 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
68};
69
70/*
71 * Collection of HID reports from an authenticator issued with FIDO2
72 * 'enrollBegin' + 'enrollCaptureNextSample' bio enrollment commands.
73 */
74static const uint8_t dummy_enroll_wire_data[] = {
75 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0x06,
76 0xb4, 0xba, 0x2e, 0xb3, 0x88, 0x24, 0x38, 0x00,
77 0x0a, 0x00, 0x05, 0x02, 0x00, 0x04, 0x06, 0x05,
78 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
79 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
80 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
81 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
82 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
83 0x00, 0x0a, 0x00, 0x05, 0x90, 0x00, 0x51, 0x00,
84 0xa1, 0x01, 0xa5, 0x01, 0x02, 0x03, 0x38, 0x18,
85 0x20, 0x01, 0x21, 0x58, 0x20, 0xc9, 0x12, 0x01,
86 0xab, 0x88, 0xd7, 0x0a, 0x24, 0xdd, 0xdc, 0xde,
87 0x16, 0x27, 0x50, 0x77, 0x37, 0x06, 0xd3, 0x48,
88 0xe6, 0xf9, 0xdb, 0xaa, 0x10, 0x83, 0x81, 0xac,
89 0x13, 0x3c, 0xf9, 0x77, 0x2d, 0x22, 0x58, 0x20,
90 0xda, 0x20, 0x71, 0x03, 0x01, 0x40, 0xac, 0xd0,
91 0x00, 0x0a, 0x00, 0x05, 0x00, 0xb8, 0xdf, 0x2a,
92 0x95, 0xd3, 0x88, 0x1c, 0x06, 0x34, 0x30, 0xf1,
93 0xf3, 0xcd, 0x27, 0x40, 0x90, 0x5c, 0xc6, 0x74,
94 0x66, 0xff, 0x10, 0xde, 0xb6, 0x00, 0x00, 0x00,
95 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
96 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
97 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
98 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
99 0x00, 0x0a, 0x00, 0x05, 0x90, 0x00, 0x14, 0x00,
100 0xa1, 0x02, 0x50, 0x18, 0x81, 0xff, 0xf2, 0xf5,
101 0xde, 0x74, 0x43, 0xd5, 0xe0, 0x77, 0x37, 0x6b,
102 0x6c, 0x18, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00,
103 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
104 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
105 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
106 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
107 0x00, 0x0a, 0x00, 0x05, 0xbb, 0x00, 0x01, 0x02,
108 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
109 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
110 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
111 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
112 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
113 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
114 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
115 0x00, 0x0a, 0x00, 0x05, 0xbb, 0x00, 0x01, 0x02,
116 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
117 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
118 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
119 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
120 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
121 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
122 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
123 0x00, 0x0a, 0x00, 0x05, 0xbb, 0x00, 0x01, 0x02,
124 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
125 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
126 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
127 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
128 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
129 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
130 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
131 0x00, 0x0a, 0x00, 0x05, 0xbb, 0x00, 0x01, 0x02,
132 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
133 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
134 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
135 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
136 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
137 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
138 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
139 0x00, 0x0a, 0x00, 0x05, 0xbb, 0x00, 0x01, 0x02,
140 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
141 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
142 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
143 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
144 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
145 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
146 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
147 0x00, 0x0a, 0x00, 0x05, 0xbb, 0x00, 0x01, 0x02,
148 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
149 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
150 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
151 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
152 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
153 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
154 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
155 0x00, 0x0a, 0x00, 0x05, 0xbb, 0x00, 0x01, 0x02,
156 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
157 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
158 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
159 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
160 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
161 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
162 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
163 0x00, 0x0a, 0x00, 0x05, 0xbb, 0x00, 0x01, 0x02,
164 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
165 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
166 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
167 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
168 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
169 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
170 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
171 0x00, 0x0a, 0x00, 0x05, 0xbb, 0x00, 0x01, 0x02,
172 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
173 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
174 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
175 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
176 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
177 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
178 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
179 0x00, 0x0a, 0x00, 0x05, 0xbb, 0x00, 0x01, 0x02,
180 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
181 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
182 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
183 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
184 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
185 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
186 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
187 0x00, 0x0a, 0x00, 0x05, 0x90, 0x00, 0x0a, 0x00,
188 0xa3, 0x04, 0x42, 0x68, 0x96, 0x05, 0x00, 0x06,
189 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
190 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
191 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
192 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
193 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
194 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
195 0x00, 0x0a, 0x00, 0x05, 0xbb, 0x00, 0x01, 0x02,
196 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
197 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
198 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
199 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
200 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
201 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
202 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
203 0x00, 0x0a, 0x00, 0x05, 0xbb, 0x00, 0x01, 0x02,
204 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
205 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
206 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
207 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
208 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
209 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
210 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
211 0x00, 0x0a, 0x00, 0x05, 0xbb, 0x00, 0x01, 0x02,
212 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
213 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
214 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
215 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
216 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
217 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
218 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
219 0x00, 0x0a, 0x00, 0x05, 0xbb, 0x00, 0x01, 0x02,
220 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
221 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
222 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
223 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
224 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
225 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
226 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
227 0x00, 0x0a, 0x00, 0x05, 0xbb, 0x00, 0x01, 0x02,
228 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
229 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
230 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
231 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
232 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
233 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
234 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
235 0x00, 0x0a, 0x00, 0x05, 0x90, 0x00, 0x06, 0x00,
236 0xa2, 0x05, 0x00, 0x06, 0x01, 0x00, 0x00, 0x00,
237 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
238 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
239 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
240 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
241 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
242 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
243 0x00, 0x0a, 0x00, 0x05, 0xbb, 0x00, 0x01, 0x02,
244 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
245 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
246 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
247 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
248 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
249 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
250 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
251 0x00, 0x0a, 0x00, 0x05, 0xbb, 0x00, 0x01, 0x02,
252 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
253 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
254 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
255 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
256 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
257 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
258 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
259 0x00, 0x0a, 0x00, 0x05, 0xbb, 0x00, 0x01, 0x02,
260 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
261 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
262 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
263 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
264 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
265 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
266 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
267 0x00, 0x0a, 0x00, 0x05, 0xbb, 0x00, 0x01, 0x02,
268 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
269 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
270 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
271 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
272 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
273 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
274 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
275 0x00, 0x0a, 0x00, 0x05, 0xbb, 0x00, 0x01, 0x02,
276 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
277 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
278 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
279 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
280 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
281 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
282 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
283 0x00, 0x0a, 0x00, 0x05, 0x90, 0x00, 0x06, 0x00,
284 0xa2, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00,
285 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
286 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
287 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
288 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
289 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
290};
291
292/*
293 * Collection of HID reports from an authenticator issued with a FIDO2
294 * 'enumerateEnrollments' bio enrollment command.
295 */
296static const uint8_t dummy_list_wire_data[] = {
297 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0xae,
298 0x21, 0x88, 0x51, 0x09, 0x6f, 0xd7, 0xbb, 0x00,
299 0x10, 0x00, 0x0f, 0x02, 0x00, 0x04, 0x06, 0x05,
300 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
301 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
302 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
303 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
304 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
305 0x00, 0x10, 0x00, 0x0f, 0x90, 0x00, 0x51, 0x00,
306 0xa1, 0x01, 0xa5, 0x01, 0x02, 0x03, 0x38, 0x18,
307 0x20, 0x01, 0x21, 0x58, 0x20, 0x5a, 0x70, 0x63,
308 0x11, 0x5b, 0xa6, 0xe1, 0x8e, 0x4a, 0xb0, 0x75,
309 0xe7, 0xfd, 0x39, 0x26, 0x29, 0xed, 0x69, 0xb0,
310 0xc1, 0x1f, 0xa5, 0x7d, 0xcb, 0x64, 0x1e, 0x7c,
311 0x9f, 0x60, 0x5e, 0xb2, 0xf8, 0x22, 0x58, 0x20,
312 0xec, 0xe9, 0x1b, 0x11, 0xac, 0x2a, 0x0d, 0xd5,
313 0x00, 0x10, 0x00, 0x0f, 0x00, 0x3b, 0x9f, 0xba,
314 0x0f, 0x25, 0xd5, 0x24, 0x33, 0x4c, 0x5d, 0x0f,
315 0x63, 0xbf, 0xf1, 0xf3, 0x64, 0x55, 0x78, 0x1a,
316 0x59, 0x6e, 0x65, 0x59, 0xfc, 0x00, 0x00, 0x00,
317 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
318 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
319 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
320 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
321 0x00, 0x10, 0x00, 0x0f, 0x90, 0x00, 0x14, 0x00,
322 0xa1, 0x02, 0x50, 0xb9, 0x31, 0x34, 0xe2, 0x71,
323 0x6a, 0x8e, 0xa3, 0x60, 0xec, 0x5e, 0xd2, 0x13,
324 0x2e, 0x19, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
325 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
326 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
327 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
328 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
329 0x00, 0x10, 0x00, 0x0f, 0x90, 0x00, 0x2e, 0x00,
330 0xa1, 0x07, 0x83, 0xa2, 0x01, 0x42, 0xce, 0xa3,
331 0x02, 0x67, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72,
332 0x31, 0xa2, 0x01, 0x42, 0xbf, 0x5e, 0x02, 0x67,
333 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x32, 0xa2,
334 0x01, 0x42, 0x5e, 0xd2, 0x02, 0x67, 0x66, 0x69,
335 0x6e, 0x67, 0x65, 0x72, 0x33, 0x00, 0x00, 0x00,
336 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
337};
338
339/*
340 * Collection of HID reports from an authenticator issued with a FIDO2
341 * 'setFriendlyName' bio enrollment command.
342 */
343static const uint8_t dummy_set_name_wire_data[] = {
344 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0xac,
345 0x48, 0xfd, 0xbd, 0xdd, 0x36, 0x24, 0x4d, 0x00,
346 0x10, 0x00, 0x10, 0x02, 0x00, 0x04, 0x06, 0x05,
347 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
348 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
349 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
350 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
351 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
352 0x00, 0x10, 0x00, 0x10, 0x90, 0x00, 0x51, 0x00,
353 0xa1, 0x01, 0xa5, 0x01, 0x02, 0x03, 0x38, 0x18,
354 0x20, 0x01, 0x21, 0x58, 0x20, 0x5a, 0x70, 0x63,
355 0x11, 0x5b, 0xa6, 0xe1, 0x8e, 0x4a, 0xb0, 0x75,
356 0xe7, 0xfd, 0x39, 0x26, 0x29, 0xed, 0x69, 0xb0,
357 0xc1, 0x1f, 0xa5, 0x7d, 0xcb, 0x64, 0x1e, 0x7c,
358 0x9f, 0x60, 0x5e, 0xb2, 0xf8, 0x22, 0x58, 0x20,
359 0xec, 0xe9, 0x1b, 0x11, 0xac, 0x2a, 0x0d, 0xd5,
360 0x00, 0x10, 0x00, 0x10, 0x00, 0x3b, 0x9f, 0xba,
361 0x0f, 0x25, 0xd5, 0x24, 0x33, 0x4c, 0x5d, 0x0f,
362 0x63, 0xbf, 0xf1, 0xf3, 0x64, 0x55, 0x78, 0x1a,
363 0x59, 0x6e, 0x65, 0x59, 0xfc, 0x00, 0x00, 0x00,
364 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
365 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
366 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
367 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
368 0x00, 0x10, 0x00, 0x10, 0x90, 0x00, 0x14, 0x00,
369 0xa1, 0x02, 0x50, 0x40, 0x95, 0xf3, 0xcb, 0xae,
370 0xf2, 0x8d, 0xd9, 0xe0, 0xe0, 0x8a, 0xbd, 0xc3,
371 0x03, 0x58, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00,
372 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
373 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
374 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
375 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
376 0x00, 0x10, 0x00, 0x10, 0x90, 0x00, 0x01, 0x00,
377 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
378 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
379 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
380 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
381 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
382 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
383 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
384};
385
386/*
387 * Collection of HID reports from an authenticator issued with a FIDO2
388 * 'removeEnrollment' bio enrollment command.
389 */
390static const uint8_t dummy_remove_wire_data[] = {
391 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0x4b,
392 0x24, 0xde, 0xd9, 0x06, 0x57, 0x1a, 0xbd, 0x00,
393 0x10, 0x00, 0x15, 0x02, 0x00, 0x04, 0x06, 0x05,
394 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
395 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
396 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
397 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
398 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
399 0x00, 0x10, 0x00, 0x15, 0x90, 0x00, 0x51, 0x00,
400 0xa1, 0x01, 0xa5, 0x01, 0x02, 0x03, 0x38, 0x18,
401 0x20, 0x01, 0x21, 0x58, 0x20, 0x5a, 0x70, 0x63,
402 0x11, 0x5b, 0xa6, 0xe1, 0x8e, 0x4a, 0xb0, 0x75,
403 0xe7, 0xfd, 0x39, 0x26, 0x29, 0xed, 0x69, 0xb0,
404 0xc1, 0x1f, 0xa5, 0x7d, 0xcb, 0x64, 0x1e, 0x7c,
405 0x9f, 0x60, 0x5e, 0xb2, 0xf8, 0x22, 0x58, 0x20,
406 0xec, 0xe9, 0x1b, 0x11, 0xac, 0x2a, 0x0d, 0xd5,
407 0x00, 0x10, 0x00, 0x15, 0x00, 0x3b, 0x9f, 0xba,
408 0x0f, 0x25, 0xd5, 0x24, 0x33, 0x4c, 0x5d, 0x0f,
409 0x63, 0xbf, 0xf1, 0xf3, 0x64, 0x55, 0x78, 0x1a,
410 0x59, 0x6e, 0x65, 0x59, 0xfc, 0x00, 0x00, 0x00,
411 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
412 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
413 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
414 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
415 0x00, 0x10, 0x00, 0x15, 0x90, 0x00, 0x14, 0x00,
416 0xa1, 0x02, 0x50, 0xb0, 0xd0, 0x71, 0x2f, 0xa7,
417 0x8b, 0x89, 0xbd, 0xca, 0xa4, 0x1e, 0x6c, 0x43,
418 0xa1, 0x71, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00,
419 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
420 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
421 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
422 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
423 0x00, 0x10, 0x00, 0x15, 0x90, 0x00, 0x01, 0x00,
424 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
425 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
426 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
427 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
428 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
429 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
430 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
431};
432
433int LLVMFuzzerTestOneInput(const uint8_t *, size_t);
434size_t LLVMFuzzerCustomMutator(uint8_t *, size_t, size_t, unsigned int);
435
436static int
437unpack(const uint8_t *ptr, size_t len, struct param *p) NO_MSAN
438{
439 uint8_t **pp = (void *)&ptr;
440
441 if (unpack_string(TAG_PIN, pp, &len, p->pin) < 0 ||
442 unpack_string(TAG_NAME, pp, &len, p->name) < 0 ||
443 unpack_int(TAG_SEED, pp, &len, &p->seed) < 0 ||
444 unpack_blob(TAG_ID, pp, &len, &p->id) < 0 ||
445 unpack_blob(TAG_INFO_WIRE_DATA, pp, &len, &p->info_wire_data) < 0 ||
446 unpack_blob(TAG_ENROLL_WIRE_DATA, pp, &len, &p->enroll_wire_data) < 0 ||
447 unpack_blob(TAG_LIST_WIRE_DATA, pp, &len, &p->list_wire_data) < 0 ||
448 unpack_blob(TAG_SET_NAME_WIRE_DATA, pp, &len, &p->set_name_wire_data) < 0 ||
449 unpack_blob(TAG_REMOVE_WIRE_DATA, pp, &len, &p->remove_wire_data) < 0)
450 return (-1);
451
452 return (0);
453}
454
455static size_t
456pack(uint8_t *ptr, size_t len, const struct param *p)
457{
458 const size_t max = len;
459
460 if (pack_string(TAG_PIN, &ptr, &len, p->pin) < 0 ||
461 pack_string(TAG_NAME, &ptr, &len, p->name) < 0 ||
462 pack_int(TAG_SEED, &ptr, &len, p->seed) < 0 ||
463 pack_blob(TAG_ID, &ptr, &len, &p->id) < 0 ||
464 pack_blob(TAG_INFO_WIRE_DATA, &ptr, &len, &p->info_wire_data) < 0 ||
465 pack_blob(TAG_ENROLL_WIRE_DATA, &ptr, &len, &p->enroll_wire_data) < 0 ||
466 pack_blob(TAG_LIST_WIRE_DATA, &ptr, &len, &p->list_wire_data) < 0 ||
467 pack_blob(TAG_SET_NAME_WIRE_DATA, &ptr, &len, &p->set_name_wire_data) < 0 ||
468 pack_blob(TAG_REMOVE_WIRE_DATA, &ptr, &len, &p->remove_wire_data) < 0)
469 return (0);
470
471 return (max - len);
472}
473
474static fido_dev_t *
475prepare_dev()
476{
477 fido_dev_t *dev;
478 fido_dev_io_t io;
479
480 io.open = dev_open;
481 io.close = dev_close;
482 io.read = dev_read;
483 io.write = dev_write;
484
485 if ((dev = fido_dev_new()) == NULL || fido_dev_set_io_functions(dev,
486 &io) != FIDO_OK || fido_dev_open(dev, "nodev") != FIDO_OK) {
487 fido_dev_free(&dev);
488 return (NULL);
489 }
490
491 return (dev);
492}
493
494static void
495get_info(struct param *p)
496{
497 fido_dev_t *dev = NULL;
498 fido_bio_info_t *i = NULL;
499 uint8_t type;
500 uint8_t max_samples;
501
502 set_wire_data(p->info_wire_data.body, p->info_wire_data.len);
503
504 if ((dev = prepare_dev()) == NULL || (i = fido_bio_info_new()) == NULL)
505 goto done;
506
507 fido_bio_dev_get_info(dev, i);
508
509 type = fido_bio_info_type(i);
510 max_samples = fido_bio_info_max_samples(i);
511 consume(&type, sizeof(type));
512 consume(&max_samples, sizeof(max_samples));
513
514done:
515 if (dev)
516 fido_dev_close(dev);
517
518 fido_dev_free(&dev);
519 fido_bio_info_free(&i);
520}
521
522static void
523consume_template(const fido_bio_template_t *t)
524{
525 consume(fido_bio_template_name(t), xstrlen(fido_bio_template_name(t)));
526 consume(fido_bio_template_id_ptr(t), fido_bio_template_id_len(t));
527}
528
529static void
530consume_enroll(fido_bio_enroll_t *e)
531{
532 uint8_t last_status;
533 uint8_t remaining_samples;
534
535 last_status = fido_bio_enroll_last_status(e);
536 remaining_samples = fido_bio_enroll_remaining_samples(e);
537 consume(&last_status, sizeof(last_status));
538 consume(&remaining_samples, sizeof(remaining_samples));
539}
540
541static void
542enroll(struct param *p)
543{
544 fido_dev_t *dev = NULL;
545 fido_bio_template_t *t = NULL;
546 fido_bio_enroll_t *e = NULL;
547 size_t cnt = 0;
548
549 set_wire_data(p->enroll_wire_data.body, p->enroll_wire_data.len);
550
551 if ((dev = prepare_dev()) == NULL ||
552 (t = fido_bio_template_new()) == NULL ||
553 (e = fido_bio_enroll_new()) == NULL)
554 goto done;
555
556 fido_bio_dev_enroll_begin(dev, t, e, p->seed, p->pin);
557
558 consume_template(t);
559 consume_enroll(e);
560
561 while (fido_bio_enroll_remaining_samples(e) > 0 && cnt++ < 5) {
562 fido_bio_dev_enroll_continue(dev, t, e, p->seed);
563 consume_template(t);
564 consume_enroll(e);
565 }
566
567done:
568 if (dev)
569 fido_dev_close(dev);
570
571 fido_dev_free(&dev);
572 fido_bio_template_free(&t);
573 fido_bio_enroll_free(&e);
574}
575
576static void
577list(struct param *p)
578{
579 fido_dev_t *dev = NULL;
580 fido_bio_template_array_t *ta = NULL;
581 const fido_bio_template_t *t = NULL;
582
583 set_wire_data(p->list_wire_data.body, p->list_wire_data.len);
584
585 if ((dev = prepare_dev()) == NULL ||
586 (ta = fido_bio_template_array_new()) == NULL)
587 goto done;
588
589 fido_bio_dev_get_template_array(dev, ta, p->pin);
590
591 /* +1 on purpose */
592 for (size_t i = 0; i < fido_bio_template_array_count(ta) + 1; i++)
593 if ((t = fido_bio_template(ta, i)) != NULL)
594 consume_template(t);
595
596done:
597 if (dev)
598 fido_dev_close(dev);
599
600 fido_dev_free(&dev);
601 fido_bio_template_array_free(&ta);
602}
603
604static void
605set_name(struct param *p)
606{
607 fido_dev_t *dev = NULL;
608 fido_bio_template_t *t = NULL;
609
610 set_wire_data(p->set_name_wire_data.body, p->set_name_wire_data.len);
611
612 if ((dev = prepare_dev()) == NULL ||
613 (t = fido_bio_template_new()) == NULL)
614 goto done;
615
616 fido_bio_template_set_name(t, p->name);
617 fido_bio_template_set_id(t, p->id.body, p->id.len);
618 consume_template(t);
619
620 fido_bio_dev_set_template_name(dev, t, p->pin);
621
622done:
623 if (dev)
624 fido_dev_close(dev);
625
626 fido_dev_free(&dev);
627 fido_bio_template_free(&t);
628}
629
630static void
631del(struct param *p)
632{
633 fido_dev_t *dev = NULL;
634 fido_bio_template_t *t = NULL;
635
636 set_wire_data(p->remove_wire_data.body, p->remove_wire_data.len);
637
638 if ((dev = prepare_dev()) == NULL ||
639 (t = fido_bio_template_new()) == NULL)
640 goto done;
641
642 fido_bio_template_set_id(t, p->id.body, p->id.len);
643 consume_template(t);
644
645 fido_bio_dev_enroll_remove(dev, t, p->pin);
646
647done:
648 if (dev)
649 fido_dev_close(dev);
650
651 fido_dev_free(&dev);
652 fido_bio_template_free(&t);
653}
654
655int
656LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
657{
658 struct param p;
659
660 memset(&p, 0, sizeof(p));
661
662 if (unpack(data, size, &p) < 0)
663 return (0);
664
665 srandom((unsigned int)p.seed);
666
667 fido_init(0);
668
669 get_info(&p);
670 enroll(&p);
671 list(&p);
672 set_name(&p);
673 del(&p);
674
675 return (0);
676}
677
678static size_t
679pack_dummy(uint8_t *ptr, size_t len)
680{
681 struct param dummy;
682 uint8_t blob[32768];
683 size_t blob_len;
684
685 memset(&dummy, 0, sizeof(dummy));
686
687 strlcpy(dummy.pin, dummy_pin, sizeof(dummy.pin));
688 strlcpy(dummy.name, dummy_name, sizeof(dummy.name));
689
690 dummy.info_wire_data.len = sizeof(dummy_info_wire_data);
691 dummy.enroll_wire_data.len = sizeof(dummy_enroll_wire_data);
692 dummy.list_wire_data.len = sizeof(dummy_list_wire_data);
693 dummy.set_name_wire_data.len = sizeof(dummy_set_name_wire_data);
694 dummy.remove_wire_data.len = sizeof(dummy_remove_wire_data);
695 dummy.id.len = sizeof(dummy_id);
696
697 memcpy(&dummy.info_wire_data.body, &dummy_info_wire_data,
698 dummy.info_wire_data.len);
699 memcpy(&dummy.enroll_wire_data.body, &dummy_enroll_wire_data,
700 dummy.enroll_wire_data.len);
701 memcpy(&dummy.list_wire_data.body, &dummy_list_wire_data,
702 dummy.list_wire_data.len);
703 memcpy(&dummy.set_name_wire_data.body, &dummy_set_name_wire_data,
704 dummy.set_name_wire_data.len);
705 memcpy(&dummy.remove_wire_data.body, &dummy_remove_wire_data,
706 dummy.remove_wire_data.len);
707 memcpy(&dummy.id.body, &dummy_id, dummy.id.len);
708
709 blob_len = pack(blob, sizeof(blob), &dummy);
710 assert(blob_len != 0);
711
712 if (blob_len > len) {
713 memcpy(ptr, blob, len);
714 return (len);
715 }
716
717 memcpy(ptr, blob, blob_len);
718
719 return (blob_len);
720}
721
722size_t
723LLVMFuzzerCustomMutator(uint8_t *data, size_t size, size_t maxsize,
724 unsigned int seed) NO_MSAN
725{
726 struct param p;
727 uint8_t blob[16384];
728 size_t blob_len;
729
730 memset(&p, 0, sizeof(p));
731
732 if (unpack(data, size, &p) < 0)
733 return (pack_dummy(data, maxsize));
734
735 p.seed = (int)seed;
736
737 mutate_blob(&p.id);
738 mutate_blob(&p.info_wire_data);
739 mutate_blob(&p.enroll_wire_data);
740 mutate_blob(&p.list_wire_data);
741 mutate_blob(&p.set_name_wire_data);
742 mutate_blob(&p.remove_wire_data);
743
744 mutate_string(p.pin);
745 mutate_string(p.name);
746
747 blob_len = pack(blob, sizeof(blob), &p);
748
749 if (blob_len == 0 || blob_len > maxsize)
750 return (0);
751
752 memcpy(data, blob, blob_len);
753
754 return (blob_len);
755}
diff --git a/fuzz/fuzz_cred.c b/fuzz/fuzz_cred.c
new file mode 100644
index 0000000..7bd1d3c
--- /dev/null
+++ b/fuzz/fuzz_cred.c
@@ -0,0 +1,925 @@
1/*
2 * Copyright (c) 2019 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <assert.h>
8#include <stdint.h>
9#include <stdlib.h>
10#include <string.h>
11#include <stdio.h>
12
13#include "mutator_aux.h"
14#include "fido.h"
15
16#include "../openbsd-compat/openbsd-compat.h"
17
18#define TAG_U2F 0x01
19#define TAG_TYPE 0x02
20#define TAG_CDH 0x03
21#define TAG_RP_ID 0x04
22#define TAG_RP_NAME 0x05
23#define TAG_USER_ID 0x06
24#define TAG_USER_NAME 0x07
25#define TAG_USER_NICK 0x08
26#define TAG_USER_ICON 0x09
27#define TAG_EXT 0x0a
28#define TAG_SEED 0x0b
29#define TAG_RK 0x0c
30#define TAG_UV 0x0d
31#define TAG_PIN 0x0e
32#define TAG_WIRE_DATA 0x0f
33#define TAG_EXCL_COUNT 0x10
34#define TAG_EXCL_CRED 0x11
35
36/* Parameter set defining a FIDO2 make credential operation. */
37struct param {
38 char pin[MAXSTR];
39 char rp_id[MAXSTR];
40 char rp_name[MAXSTR];
41 char user_icon[MAXSTR];
42 char user_name[MAXSTR];
43 char user_nick[MAXSTR];
44 int ext;
45 int seed;
46 struct blob cdh;
47 struct blob excl_cred;
48 struct blob user_id;
49 struct blob wire_data;
50 uint8_t excl_count;
51 uint8_t rk;
52 uint8_t type;
53 uint8_t u2f;
54 uint8_t uv;
55};
56
57/* Example parameters. */
58static const char dummy_rp_id[] = "localhost";
59static const char dummy_rp_name[] = "sweet home localhost";
60static const char dummy_pin[] = "9}4gT:8d=A37Dh}U";
61static const char dummy_user_icon[] = "an icon";
62static const char dummy_user_name[] = "john smith";
63static const char dummy_user_nick[] = "jsmith";
64
65static const uint8_t dummy_cdh[] = {
66 0xf9, 0x64, 0x57, 0xe7, 0x2d, 0x97, 0xf6, 0xbb,
67 0xdd, 0xd7, 0xfb, 0x06, 0x37, 0x62, 0xea, 0x26,
68 0x20, 0x44, 0x8e, 0x69, 0x7c, 0x03, 0xf2, 0x31,
69 0x2f, 0x99, 0xdc, 0xaf, 0x3e, 0x8a, 0x91, 0x6b,
70};
71
72static const uint8_t dummy_user_id[] = {
73 0x78, 0x1c, 0x78, 0x60, 0xad, 0x88, 0xd2, 0x63,
74 0x32, 0x62, 0x2a, 0xf1, 0x74, 0x5d, 0xed, 0xb2,
75 0xe7, 0xa4, 0x2b, 0x44, 0x89, 0x29, 0x39, 0xc5,
76 0x56, 0x64, 0x01, 0x27, 0x0d, 0xbb, 0xc4, 0x49,
77};
78
79/*
80 * Collection of HID reports from an authenticator issued with a FIDO2
81 * make credential using the example parameters above.
82 */
83static const uint8_t dummy_wire_data_fido[] = {
84 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0xb0,
85 0x84, 0xeb, 0xec, 0x4d, 0x97, 0x72, 0x09, 0x00,
86 0x91, 0x00, 0x03, 0x02, 0x05, 0x00, 0x02, 0x05,
87 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
88 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
89 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
90 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
91 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
92 0x00, 0x91, 0x00, 0x03, 0x90, 0x00, 0x51, 0x00,
93 0xa1, 0x01, 0xa5, 0x01, 0x02, 0x03, 0x38, 0x18,
94 0x20, 0x01, 0x21, 0x58, 0x20, 0x69, 0xf2, 0x7d,
95 0x37, 0x57, 0xda, 0x11, 0xba, 0x42, 0xde, 0x79,
96 0xe4, 0xab, 0x8d, 0x73, 0x63, 0xee, 0x66, 0x9e,
97 0x8a, 0x70, 0xa9, 0xb5, 0xf6, 0x38, 0x4f, 0x5b,
98 0xdf, 0xe1, 0xa0, 0xa4, 0xff, 0x22, 0x58, 0x20,
99 0x8a, 0xcb, 0x23, 0x2e, 0x93, 0xdb, 0xe0, 0xa4,
100 0x00, 0x91, 0x00, 0x03, 0x00, 0xbb, 0xb5, 0x60,
101 0x19, 0x18, 0x8b, 0x4d, 0xb8, 0x88, 0x6e, 0x13,
102 0x75, 0xac, 0x00, 0x19, 0x27, 0x80, 0xcc, 0x63,
103 0xc4, 0xbf, 0xfe, 0x4b, 0x4a, 0x00, 0x00, 0x00,
104 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
105 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
106 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
107 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
108 0x00, 0x91, 0x00, 0x03, 0x90, 0x00, 0x14, 0x00,
109 0xa1, 0x02, 0x50, 0x10, 0x89, 0x77, 0x43, 0x3a,
110 0x58, 0xa2, 0xc9, 0x98, 0x18, 0x1a, 0xb1, 0xcc,
111 0x09, 0x6b, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00,
112 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
113 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
114 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
115 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
116 0x00, 0x91, 0x00, 0x03, 0xbb, 0x00, 0x01, 0x02,
117 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
118 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
119 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
120 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
121 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
122 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
123 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
124 0x00, 0x91, 0x00, 0x03, 0xbb, 0x00, 0x01, 0x02,
125 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
126 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
127 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
128 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
129 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
130 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
131 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
132 0x00, 0x91, 0x00, 0x03, 0xbb, 0x00, 0x01, 0x02,
133 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
134 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
135 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
136 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
137 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
138 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
139 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
140 0x00, 0x91, 0x00, 0x03, 0xbb, 0x00, 0x01, 0x02,
141 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
142 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
143 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
144 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
145 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
146 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
147 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
148 0x00, 0x91, 0x00, 0x03, 0xbb, 0x00, 0x01, 0x02,
149 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
150 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
151 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
152 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
153 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
154 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
155 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
156 0x00, 0x91, 0x00, 0x03, 0xbb, 0x00, 0x01, 0x02,
157 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
158 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
159 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
160 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
161 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
162 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
163 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
164 0x00, 0x91, 0x00, 0x03, 0xbb, 0x00, 0x01, 0x02,
165 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
166 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
167 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
168 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
169 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
170 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
171 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
172 0x00, 0x91, 0x00, 0x03, 0xbb, 0x00, 0x01, 0x02,
173 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
174 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
175 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
176 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
177 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
178 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
179 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
180 0x00, 0x91, 0x00, 0x03, 0xbb, 0x00, 0x01, 0x02,
181 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
182 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
183 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
184 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
185 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
186 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
187 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
188 0x00, 0x91, 0x00, 0x03, 0xbb, 0x00, 0x01, 0x02,
189 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
190 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
191 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
192 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
193 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
194 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
195 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
196 0x00, 0x91, 0x00, 0x03, 0x90, 0x03, 0xe1, 0x00,
197 0xa3, 0x01, 0x66, 0x70, 0x61, 0x63, 0x6b, 0x65,
198 0x64, 0x02, 0x58, 0xc4, 0x49, 0x96, 0x0d, 0xe5,
199 0x88, 0x0e, 0x8c, 0x68, 0x74, 0x34, 0x17, 0x0f,
200 0x64, 0x76, 0x60, 0x5b, 0x8f, 0xe4, 0xae, 0xb9,
201 0xa2, 0x86, 0x32, 0xc7, 0x99, 0x5c, 0xf3, 0xba,
202 0x83, 0x1d, 0x97, 0x63, 0x45, 0x00, 0x00, 0x00,
203 0x00, 0xf8, 0xa0, 0x11, 0xf3, 0x8c, 0x0a, 0x4d,
204 0x00, 0x91, 0x00, 0x03, 0x00, 0x15, 0x80, 0x06,
205 0x17, 0x11, 0x1f, 0x9e, 0xdc, 0x7d, 0x00, 0x40,
206 0xed, 0x88, 0x48, 0xa1, 0xdb, 0x56, 0x4d, 0x0f,
207 0x0d, 0xc8, 0x8f, 0x0f, 0xe9, 0x16, 0xb1, 0x78,
208 0xa9, 0x40, 0x98, 0x71, 0xa0, 0xb3, 0xf2, 0xcf,
209 0x05, 0x73, 0x6c, 0x12, 0xbf, 0x00, 0x96, 0xf3,
210 0x7b, 0x93, 0xba, 0x49, 0xee, 0x23, 0xb4, 0x78,
211 0x2e, 0xfb, 0xce, 0x27, 0xa8, 0xc2, 0x26, 0x78,
212 0x00, 0x91, 0x00, 0x03, 0x01, 0xcc, 0x95, 0x2d,
213 0x40, 0xdb, 0xd1, 0x40, 0x3d, 0x2b, 0xa3, 0x31,
214 0xa0, 0x75, 0x82, 0x63, 0xf0, 0xa5, 0x01, 0x02,
215 0x03, 0x26, 0x20, 0x01, 0x21, 0x58, 0x20, 0x9d,
216 0x95, 0xa1, 0xb5, 0xd6, 0x11, 0xbf, 0xe2, 0x28,
217 0xa0, 0x7f, 0xca, 0x1e, 0xd9, 0x09, 0x0f, 0x0d,
218 0xe7, 0x8e, 0x29, 0xe8, 0x2e, 0x11, 0xdb, 0x55,
219 0x62, 0x13, 0xd7, 0x26, 0xc2, 0x7e, 0x2b, 0x22,
220 0x00, 0x91, 0x00, 0x03, 0x02, 0x58, 0x20, 0xbe,
221 0x74, 0x2a, 0xac, 0xde, 0x11, 0x40, 0x76, 0x31,
222 0x0b, 0xed, 0x55, 0xde, 0xf3, 0x03, 0xe4, 0x1c,
223 0xac, 0x42, 0x63, 0x8f, 0xe8, 0x30, 0x63, 0xb7,
224 0x07, 0x4e, 0x5d, 0xfb, 0x17, 0x5e, 0x9b, 0x03,
225 0xa3, 0x63, 0x61, 0x6c, 0x67, 0x26, 0x63, 0x73,
226 0x69, 0x67, 0x58, 0x48, 0x30, 0x46, 0x02, 0x21,
227 0x00, 0xfb, 0xd1, 0x26, 0x76, 0x34, 0x74, 0xac,
228 0x00, 0x91, 0x00, 0x03, 0x03, 0xf6, 0xd8, 0x5c,
229 0x5d, 0xbc, 0xda, 0xe0, 0x43, 0xe0, 0xa5, 0x42,
230 0x9f, 0xc7, 0xe2, 0x18, 0x3e, 0xe2, 0x2c, 0x94,
231 0x78, 0xbf, 0x9c, 0xeb, 0x3e, 0x9d, 0x02, 0x21,
232 0x00, 0xab, 0x21, 0x1b, 0xc4, 0x30, 0x69, 0xee,
233 0x7f, 0x09, 0xe6, 0x6b, 0x99, 0x98, 0x34, 0x07,
234 0x7b, 0x9a, 0x58, 0xb2, 0xe8, 0x77, 0xe0, 0xba,
235 0x7d, 0xab, 0x65, 0xf8, 0xba, 0x2a, 0xcb, 0x9a,
236 0x00, 0x91, 0x00, 0x03, 0x04, 0x41, 0x63, 0x78,
237 0x35, 0x63, 0x81, 0x59, 0x02, 0xb3, 0x30, 0x82,
238 0x02, 0xaf, 0x30, 0x82, 0x01, 0x97, 0xa0, 0x03,
239 0x02, 0x01, 0x02, 0x02, 0x04, 0x48, 0x5b, 0x3d,
240 0xb6, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
241 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00,
242 0x30, 0x21, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03,
243 0x55, 0x04, 0x03, 0x0c, 0x16, 0x59, 0x75, 0x62,
244 0x00, 0x91, 0x00, 0x03, 0x05, 0x69, 0x63, 0x6f,
245 0x20, 0x46, 0x49, 0x44, 0x4f, 0x20, 0x50, 0x72,
246 0x65, 0x76, 0x69, 0x65, 0x77, 0x20, 0x43, 0x41,
247 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x38, 0x30, 0x34,
248 0x31, 0x32, 0x31, 0x30, 0x35, 0x37, 0x31, 0x30,
249 0x5a, 0x17, 0x0d, 0x31, 0x38, 0x31, 0x32, 0x33,
250 0x31, 0x31, 0x30, 0x35, 0x37, 0x31, 0x30, 0x5a,
251 0x30, 0x6f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
252 0x00, 0x91, 0x00, 0x03, 0x06, 0x55, 0x04, 0x06,
253 0x13, 0x02, 0x53, 0x45, 0x31, 0x12, 0x30, 0x10,
254 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x09, 0x59,
255 0x75, 0x62, 0x69, 0x63, 0x6f, 0x20, 0x41, 0x42,
256 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04,
257 0x0b, 0x0c, 0x19, 0x41, 0x75, 0x74, 0x68, 0x65,
258 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x6f, 0x72,
259 0x20, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61,
260 0x00, 0x91, 0x00, 0x03, 0x07, 0x74, 0x69, 0x6f,
261 0x6e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55,
262 0x04, 0x03, 0x0c, 0x1f, 0x59, 0x75, 0x62, 0x69,
263 0x63, 0x6f, 0x20, 0x55, 0x32, 0x46, 0x20, 0x45,
264 0x45, 0x20, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c,
265 0x20, 0x31, 0x32, 0x31, 0x33, 0x39, 0x33, 0x39,
266 0x31, 0x32, 0x36, 0x30, 0x59, 0x30, 0x13, 0x06,
267 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01,
268 0x00, 0x91, 0x00, 0x03, 0x08, 0x06, 0x08, 0x2a,
269 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03,
270 0x42, 0x00, 0x04, 0xfb, 0x2c, 0xdd, 0x30, 0x43,
271 0x28, 0xc5, 0x72, 0x4a, 0x50, 0xcc, 0xe6, 0xf6,
272 0x0b, 0xad, 0x7d, 0x27, 0xa9, 0x1b, 0x59, 0xe1,
273 0xe6, 0x6f, 0x29, 0x7b, 0x89, 0xc9, 0xd4, 0x3d,
274 0xc2, 0xb2, 0xc7, 0x78, 0x89, 0xb4, 0xf0, 0xff,
275 0x9d, 0x02, 0x28, 0xcb, 0x94, 0x6d, 0xfc, 0xe0,
276 0x00, 0x91, 0x00, 0x03, 0x09, 0x1b, 0x19, 0x58,
277 0x9b, 0x67, 0x80, 0x4a, 0xac, 0x97, 0x7f, 0x28,
278 0x18, 0x9c, 0xcd, 0xb3, 0x25, 0x74, 0xca, 0x28,
279 0xa3, 0x6c, 0x30, 0x6a, 0x30, 0x22, 0x06, 0x09,
280 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xc4, 0x0a,
281 0x02, 0x04, 0x15, 0x31, 0x2e, 0x33, 0x2e, 0x36,
282 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34,
283 0x31, 0x34, 0x38, 0x32, 0x2e, 0x31, 0x2e, 0x36,
284 0x00, 0x91, 0x00, 0x03, 0x0a, 0x30, 0x13, 0x06,
285 0x0b, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xe5,
286 0x1c, 0x02, 0x01, 0x01, 0x04, 0x04, 0x03, 0x02,
287 0x04, 0x30, 0x30, 0x21, 0x06, 0x0b, 0x2b, 0x06,
288 0x01, 0x04, 0x01, 0x82, 0xe5, 0x1c, 0x01, 0x01,
289 0x04, 0x04, 0x12, 0x04, 0x10, 0xf8, 0xa0, 0x11,
290 0xf3, 0x8c, 0x0a, 0x4d, 0x15, 0x80, 0x06, 0x17,
291 0x11, 0x1f, 0x9e, 0xdc, 0x7d, 0x30, 0x0c, 0x06,
292 0x00, 0x91, 0x00, 0x03, 0x0b, 0x03, 0x55, 0x1d,
293 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00,
294 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
295 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03,
296 0x82, 0x01, 0x01, 0x00, 0x32, 0xf3, 0xe4, 0xbd,
297 0x58, 0xd7, 0x42, 0x2b, 0xaf, 0x49, 0x99, 0x86,
298 0x08, 0x1f, 0x0d, 0xa9, 0x3b, 0xc6, 0xaa, 0x1c,
299 0x72, 0x11, 0xf9, 0x28, 0x53, 0xeb, 0xf3, 0xeb,
300 0x00, 0x91, 0x00, 0x03, 0x0c, 0x73, 0xda, 0x69,
301 0x3b, 0x06, 0xde, 0x31, 0x33, 0x8e, 0x5d, 0x02,
302 0xec, 0xf6, 0x76, 0xe9, 0x5c, 0x42, 0xbe, 0xa5,
303 0x8f, 0x25, 0xd3, 0x37, 0x3f, 0x77, 0xbb, 0x2a,
304 0x9d, 0x7c, 0xb2, 0x3e, 0x11, 0x8c, 0x41, 0xd4,
305 0x9a, 0x4c, 0x9a, 0xd8, 0xf3, 0xe2, 0xa4, 0xec,
306 0x01, 0x77, 0x7a, 0x74, 0xa8, 0xc4, 0x12, 0x43,
307 0xc3, 0x1e, 0xce, 0x20, 0x8f, 0x2d, 0x0f, 0x6e,
308 0x00, 0x91, 0x00, 0x03, 0x0d, 0xbc, 0x61, 0x9b,
309 0xe1, 0x84, 0xa1, 0x72, 0xf6, 0xa9, 0xac, 0xcb,
310 0xf8, 0x73, 0x6d, 0x5b, 0xe2, 0x98, 0xb3, 0x6b,
311 0xec, 0xe7, 0x1e, 0x77, 0x8d, 0x0a, 0x69, 0xaa,
312 0xf9, 0x94, 0xb8, 0x63, 0x6d, 0xe8, 0xfa, 0xf6,
313 0x2f, 0xd3, 0xce, 0x7f, 0x04, 0x4c, 0x32, 0x2c,
314 0xf7, 0x26, 0x3e, 0x34, 0x99, 0xe6, 0xa5, 0xb2,
315 0xb0, 0x2a, 0xbb, 0xad, 0x5b, 0xd9, 0xec, 0xe5,
316 0x00, 0x91, 0x00, 0x03, 0x0e, 0xb0, 0x71, 0x4d,
317 0x73, 0xbb, 0x94, 0x61, 0x49, 0x9c, 0x94, 0x2a,
318 0x5f, 0x1d, 0xcc, 0xaf, 0x65, 0x03, 0x3b, 0x39,
319 0x39, 0xd4, 0x47, 0xd9, 0xfc, 0xc4, 0x7b, 0x0b,
320 0x16, 0xd8, 0xe9, 0x01, 0xfc, 0xec, 0x3f, 0x8c,
321 0x1b, 0xc0, 0xc6, 0xac, 0x0b, 0x5d, 0x74, 0xc7,
322 0xbb, 0x03, 0x05, 0x69, 0x17, 0xe9, 0x98, 0x1a,
323 0x19, 0xb9, 0x09, 0x5c, 0xa1, 0xf4, 0xab, 0x9f,
324 0x00, 0x91, 0x00, 0x03, 0x0f, 0x02, 0x7c, 0x28,
325 0x0f, 0x8a, 0xf9, 0xed, 0x1d, 0x29, 0x3c, 0xf6,
326 0xcc, 0x2f, 0x04, 0x6d, 0x9a, 0xd6, 0x62, 0xb4,
327 0xa9, 0x6e, 0xb1, 0xca, 0xca, 0xac, 0x5e, 0x05,
328 0x3e, 0x83, 0x91, 0x47, 0x7c, 0x1f, 0x8b, 0x60,
329 0x01, 0xde, 0x65, 0x3a, 0xbf, 0xf2, 0xaa, 0xbb,
330 0x55, 0x98, 0x86, 0x91, 0x7e, 0xad, 0x3b, 0x36,
331 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
332};
333
334/*
335 * Collection of HID reports from an authenticator issued with a U2F
336 * registration using the example parameters above.
337 */
338static const uint8_t dummy_wire_data_u2f[] = {
339 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0x8e,
340 0x80, 0xd0, 0xe2, 0x3b, 0x24, 0x93, 0xea, 0x00,
341 0x00, 0x99, 0x01, 0x02, 0x01, 0x01, 0x00, 0x01,
342 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
343 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
344 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
345 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
346 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
347 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x02, 0x69,
348 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
349 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
350 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
351 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
352 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
353 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
354 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
355 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x02, 0x69,
356 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
357 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
358 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
359 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
360 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
361 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
362 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
363 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x02, 0x69,
364 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
365 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
366 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
367 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
368 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
369 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
370 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
371 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x02, 0x69,
372 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
373 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
374 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
375 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
376 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
377 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
378 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
379 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x02, 0x69,
380 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
381 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
382 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
383 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
384 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
385 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
386 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
387 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x02, 0x69,
388 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
389 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
390 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
391 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
392 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
393 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
394 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
395 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x02, 0x69,
396 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
397 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
398 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
399 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
400 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
401 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
402 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
403 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x02, 0x69,
404 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
405 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
406 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
407 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
408 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
409 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
410 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
411 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x02, 0x69,
412 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
413 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
414 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
415 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
416 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
417 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
418 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
419 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x02, 0x69,
420 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
421 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
422 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
423 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
424 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
425 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
426 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
427 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x02, 0x69,
428 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
429 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
430 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
431 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
432 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
433 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
434 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
435 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x02, 0x69,
436 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
437 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
438 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
439 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
440 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
441 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
442 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
443 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x02, 0x69,
444 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
445 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
446 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
447 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
448 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
449 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
450 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
451 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x02, 0x69,
452 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
453 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
454 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
455 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
456 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
457 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
458 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
459 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x02, 0x69,
460 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
461 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
462 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
463 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
464 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
465 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
466 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
467 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x02, 0x69,
468 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
469 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
470 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
471 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
472 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
473 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
474 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
475 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x02, 0x69,
476 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
477 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
478 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
479 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
480 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
481 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
482 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
483 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x02, 0x69,
484 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
485 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
486 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
487 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
488 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
489 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
490 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
491 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x02, 0x69,
492 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
493 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
494 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
495 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
496 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
497 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
498 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
499 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x02, 0x69,
500 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
501 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
502 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
503 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
504 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
505 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
506 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
507 0x00, 0x00, 0x99, 0x01, 0x83, 0x00, 0x02, 0x69,
508 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
509 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
510 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
511 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
512 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
513 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
514 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
515 0x00, 0x00, 0x99, 0x01, 0x83, 0x03, 0x1e, 0x05,
516 0x04, 0x9f, 0xa0, 0xf9, 0x0d, 0x4c, 0xf4, 0xae,
517 0x96, 0x3c, 0xb7, 0x46, 0xb7, 0x5c, 0x9d, 0x8b,
518 0x48, 0x19, 0xdf, 0xc4, 0xad, 0xea, 0xb2, 0x70,
519 0x58, 0x72, 0xd9, 0xce, 0x75, 0xf5, 0xe6, 0x8e,
520 0x0f, 0x9c, 0x0e, 0x2e, 0x62, 0x3e, 0x91, 0xd3,
521 0x7b, 0x97, 0x46, 0x60, 0xb9, 0x57, 0x13, 0x97,
522 0x26, 0xae, 0x0f, 0xb3, 0x8f, 0x2e, 0x9b, 0x3f,
523 0x00, 0x00, 0x99, 0x01, 0x00, 0xa5, 0x55, 0xec,
524 0x8c, 0x25, 0x7c, 0x65, 0xb7, 0x09, 0x40, 0x48,
525 0xae, 0xa8, 0xcb, 0xa1, 0x91, 0xac, 0x40, 0x24,
526 0xf2, 0x34, 0x6e, 0x3a, 0x8f, 0xa5, 0xb7, 0x48,
527 0x54, 0x6e, 0xfb, 0xf4, 0x37, 0x88, 0x69, 0x79,
528 0x6f, 0x12, 0xc1, 0x32, 0xdf, 0x15, 0x5d, 0x6e,
529 0x82, 0x54, 0xc0, 0x6e, 0x56, 0x4f, 0x3a, 0x9c,
530 0xc3, 0x96, 0x7a, 0xde, 0xa5, 0xfe, 0xec, 0xd1,
531 0x00, 0x00, 0x99, 0x01, 0x01, 0x5a, 0x21, 0x85,
532 0x0e, 0x25, 0x7b, 0x8d, 0x6e, 0x1d, 0x32, 0x29,
533 0xdb, 0x21, 0xb0, 0xa3, 0x30, 0x82, 0x02, 0x4f,
534 0x30, 0x82, 0x01, 0x37, 0xa0, 0x03, 0x02, 0x01,
535 0x02, 0x02, 0x04, 0x2a, 0xd9, 0x6a, 0xf3, 0x30,
536 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
537 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x2e,
538 0x31, 0x2c, 0x30, 0x2a, 0x06, 0x03, 0x55, 0x04,
539 0x00, 0x00, 0x99, 0x01, 0x02, 0x03, 0x13, 0x23,
540 0x59, 0x75, 0x62, 0x69, 0x63, 0x6f, 0x20, 0x55,
541 0x32, 0x46, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20,
542 0x43, 0x41, 0x20, 0x53, 0x65, 0x72, 0x69, 0x61,
543 0x6c, 0x20, 0x34, 0x35, 0x37, 0x32, 0x30, 0x30,
544 0x36, 0x33, 0x31, 0x30, 0x20, 0x17, 0x0d, 0x31,
545 0x34, 0x30, 0x38, 0x30, 0x31, 0x30, 0x30, 0x30,
546 0x30, 0x30, 0x30, 0x5a, 0x18, 0x0f, 0x32, 0x30,
547 0x00, 0x00, 0x99, 0x01, 0x03, 0x35, 0x30, 0x30,
548 0x39, 0x30, 0x34, 0x30, 0x30, 0x30, 0x30, 0x30,
549 0x30, 0x5a, 0x30, 0x31, 0x31, 0x2f, 0x30, 0x2d,
550 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x26, 0x59,
551 0x75, 0x62, 0x69, 0x63, 0x6f, 0x20, 0x55, 0x32,
552 0x46, 0x20, 0x45, 0x45, 0x20, 0x53, 0x65, 0x72,
553 0x69, 0x61, 0x6c, 0x20, 0x32, 0x33, 0x39, 0x32,
554 0x35, 0x37, 0x33, 0x34, 0x35, 0x31, 0x36, 0x35,
555 0x00, 0x00, 0x99, 0x01, 0x04, 0x35, 0x30, 0x33,
556 0x38, 0x37, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07,
557 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06,
558 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01,
559 0x07, 0x03, 0x42, 0x00, 0x04, 0x2f, 0xe1, 0xa2,
560 0x3e, 0xbf, 0xa5, 0x5b, 0x3e, 0x46, 0x1d, 0x59,
561 0xa4, 0x35, 0x22, 0xd7, 0x97, 0x48, 0x98, 0x1c,
562 0xba, 0x6d, 0x28, 0x9a, 0x98, 0xf1, 0xbd, 0x7d,
563 0x00, 0x00, 0x99, 0x01, 0x05, 0xff, 0x65, 0x66,
564 0x80, 0xdb, 0xbb, 0xed, 0xbc, 0x2b, 0xae, 0x60,
565 0x7e, 0x6e, 0xf7, 0x72, 0xf5, 0x76, 0xb0, 0x4d,
566 0x54, 0xc4, 0xe5, 0xf3, 0x2f, 0x59, 0x6f, 0x26,
567 0xe6, 0x11, 0x15, 0xc7, 0x27, 0x2c, 0xf6, 0xca,
568 0x75, 0x94, 0xa3, 0x3b, 0x30, 0x39, 0x30, 0x22,
569 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82,
570 0xc4, 0x0a, 0x02, 0x04, 0x15, 0x31, 0x2e, 0x33,
571 0x00, 0x00, 0x99, 0x01, 0x06, 0x2e, 0x36, 0x2e,
572 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x31,
573 0x34, 0x38, 0x32, 0x2e, 0x31, 0x2e, 0x32, 0x30,
574 0x13, 0x06, 0x0b, 0x2b, 0x06, 0x01, 0x04, 0x01,
575 0x82, 0xe5, 0x1c, 0x02, 0x01, 0x01, 0x04, 0x04,
576 0x03, 0x02, 0x04, 0x30, 0x30, 0x0d, 0x06, 0x09,
577 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
578 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00,
579 0x00, 0x00, 0x99, 0x01, 0x07, 0x85, 0x6a, 0xfa,
580 0x8b, 0xcf, 0x4f, 0x3f, 0x62, 0x5f, 0x29, 0x1b,
581 0xc1, 0x15, 0x8e, 0x3c, 0x7e, 0xbd, 0x25, 0x52,
582 0xbc, 0xf7, 0x57, 0x07, 0x53, 0xf5, 0x12, 0x1d,
583 0xa6, 0xa5, 0x4d, 0x24, 0xcc, 0xcf, 0xae, 0x27,
584 0xce, 0xd6, 0xab, 0x31, 0x12, 0x8c, 0x29, 0x7e,
585 0x5b, 0x5b, 0x89, 0x05, 0xdd, 0xa0, 0x20, 0x17,
586 0x93, 0x1f, 0x1f, 0x5f, 0x59, 0x25, 0x93, 0x59,
587 0x00, 0x00, 0x99, 0x01, 0x08, 0x51, 0xfc, 0x00,
588 0x4b, 0xcb, 0xe2, 0x0a, 0xdd, 0x7d, 0x8d, 0x05,
589 0x2f, 0x95, 0x43, 0xb3, 0x49, 0x6c, 0x15, 0xb8,
590 0x31, 0x0e, 0x10, 0xcb, 0xd9, 0xbb, 0x05, 0x38,
591 0x27, 0x4f, 0x58, 0x3e, 0xad, 0x1f, 0x45, 0x12,
592 0x88, 0xc3, 0xea, 0x76, 0xd0, 0x70, 0xad, 0x44,
593 0xe5, 0x3a, 0xfe, 0xa8, 0xf2, 0x2d, 0x1f, 0x73,
594 0x62, 0x5f, 0xf2, 0xd5, 0x89, 0xfe, 0x30, 0xdf,
595 0x00, 0x00, 0x99, 0x01, 0x09, 0x26, 0x62, 0xcb,
596 0x7c, 0xbb, 0x7c, 0x99, 0x61, 0x80, 0xad, 0xcf,
597 0xa9, 0x8a, 0x4d, 0x01, 0x2c, 0xf3, 0x13, 0x46,
598 0xcd, 0x11, 0x74, 0x6a, 0x58, 0x48, 0xe8, 0xbe,
599 0xed, 0xf3, 0xe3, 0x0c, 0xcb, 0xd9, 0xc1, 0xdd,
600 0x22, 0x16, 0x71, 0xb2, 0x83, 0x88, 0x61, 0xf6,
601 0x5a, 0x45, 0x36, 0x23, 0xb5, 0x18, 0xd5, 0x56,
602 0x7f, 0xa8, 0xf0, 0xa3, 0xce, 0x10, 0x5d, 0xf4,
603 0x00, 0x00, 0x99, 0x01, 0x0a, 0xf1, 0x39, 0x53,
604 0xe1, 0x14, 0xea, 0x59, 0xe0, 0xa7, 0xf2, 0xfe,
605 0x66, 0x88, 0x67, 0x43, 0x2e, 0x52, 0xfd, 0x6a,
606 0x2f, 0x64, 0xf7, 0x3c, 0x48, 0xcd, 0x9b, 0x38,
607 0xf2, 0xdf, 0xba, 0x2c, 0x7a, 0x4b, 0x3b, 0x11,
608 0x28, 0xdf, 0x26, 0xd6, 0x6a, 0x24, 0xf8, 0x95,
609 0xdd, 0xa0, 0xb6, 0x11, 0x80, 0xf4, 0x14, 0x4f,
610 0x6b, 0x70, 0x75, 0xc3, 0x18, 0xa4, 0x9a, 0xe0,
611 0x00, 0x00, 0x99, 0x01, 0x0b, 0x8b, 0x58, 0xd3,
612 0x6a, 0xdb, 0x1e, 0x30, 0x53, 0x67, 0x2b, 0x17,
613 0xc5, 0xa1, 0x9f, 0x7f, 0x0a, 0x22, 0xf1, 0x0e,
614 0x94, 0x30, 0x44, 0x02, 0x20, 0x07, 0x5c, 0x4f,
615 0xd2, 0x83, 0xb6, 0x9f, 0x0a, 0x4a, 0x4d, 0x4b,
616 0x08, 0x35, 0xeb, 0xc0, 0x7e, 0x4a, 0x14, 0x2e,
617 0xc7, 0x8c, 0xd6, 0x64, 0x2f, 0xd3, 0x1e, 0xcc,
618 0xb5, 0xe8, 0x42, 0xea, 0xf6, 0x02, 0x20, 0x6b,
619 0x00, 0x00, 0x99, 0x01, 0x0c, 0x5a, 0xba, 0x4a,
620 0xc8, 0xd7, 0x89, 0xcc, 0x77, 0xe6, 0xb9, 0xa3,
621 0x34, 0xea, 0x06, 0x85, 0x72, 0xc6, 0x28, 0xa8,
622 0x7a, 0xaa, 0x19, 0x88, 0x34, 0xbb, 0xdc, 0x64,
623 0x90, 0x0a, 0xdb, 0x39, 0x90, 0x00, 0x00, 0x00,
624 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
625 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
626 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
627};
628
629int LLVMFuzzerTestOneInput(const uint8_t *, size_t);
630size_t LLVMFuzzerCustomMutator(uint8_t *, size_t, size_t, unsigned int);
631
632static int
633unpack(const uint8_t *ptr, size_t len, struct param *p) NO_MSAN
634{
635 uint8_t **pp = (void *)&ptr;
636
637 if (unpack_byte(TAG_RK, pp, &len, &p->rk) < 0 ||
638 unpack_byte(TAG_TYPE, pp, &len, &p->type) < 0 ||
639 unpack_byte(TAG_U2F, pp, &len, &p->u2f) < 0 ||
640 unpack_byte(TAG_UV, pp, &len, &p->uv) < 0 ||
641 unpack_byte(TAG_EXCL_COUNT, pp, &len, &p->excl_count) < 0 ||
642 unpack_string(TAG_PIN, pp, &len, p->pin) < 0 ||
643 unpack_string(TAG_RP_ID, pp, &len, p->rp_id) < 0 ||
644 unpack_string(TAG_RP_NAME, pp, &len, p->rp_name) < 0 ||
645 unpack_string(TAG_USER_ICON, pp, &len, p->user_icon) < 0 ||
646 unpack_string(TAG_USER_NAME, pp, &len, p->user_name) < 0 ||
647 unpack_string(TAG_USER_NICK, pp, &len, p->user_nick) < 0 ||
648 unpack_int(TAG_EXT, pp, &len, &p->ext) < 0 ||
649 unpack_int(TAG_SEED, pp, &len, &p->seed) < 0 ||
650 unpack_blob(TAG_CDH, pp, &len, &p->cdh) < 0 ||
651 unpack_blob(TAG_USER_ID, pp, &len, &p->user_id) < 0 ||
652 unpack_blob(TAG_WIRE_DATA, pp, &len, &p->wire_data) < 0 ||
653 unpack_blob(TAG_EXCL_CRED, pp, &len, &p->excl_cred) < 0)
654 return (-1);
655
656 return (0);
657}
658
659static size_t
660pack(uint8_t *ptr, size_t len, const struct param *p)
661{
662 const size_t max = len;
663
664 if (pack_byte(TAG_RK, &ptr, &len, p->rk) < 0 ||
665 pack_byte(TAG_TYPE, &ptr, &len, p->type) < 0 ||
666 pack_byte(TAG_U2F, &ptr, &len, p->u2f) < 0 ||
667 pack_byte(TAG_UV, &ptr, &len, p->uv) < 0 ||
668 pack_byte(TAG_EXCL_COUNT, &ptr, &len, p->excl_count) < 0 ||
669 pack_string(TAG_PIN, &ptr, &len, p->pin) < 0 ||
670 pack_string(TAG_RP_ID, &ptr, &len, p->rp_id) < 0 ||
671 pack_string(TAG_RP_NAME, &ptr, &len, p->rp_name) < 0 ||
672 pack_string(TAG_USER_ICON, &ptr, &len, p->user_icon) < 0 ||
673 pack_string(TAG_USER_NAME, &ptr, &len, p->user_name) < 0 ||
674 pack_string(TAG_USER_NICK, &ptr, &len, p->user_nick) < 0 ||
675 pack_int(TAG_EXT, &ptr, &len, p->ext) < 0 ||
676 pack_int(TAG_SEED, &ptr, &len, p->seed) < 0 ||
677 pack_blob(TAG_CDH, &ptr, &len, &p->cdh) < 0 ||
678 pack_blob(TAG_USER_ID, &ptr, &len, &p->user_id) < 0 ||
679 pack_blob(TAG_WIRE_DATA, &ptr, &len, &p->wire_data) < 0 ||
680 pack_blob(TAG_EXCL_CRED, &ptr, &len, &p->excl_cred) < 0)
681 return (0);
682
683 return (max - len);
684}
685
686static void
687make_cred(fido_cred_t *cred, uint8_t u2f, int type, const struct blob *cdh,
688 const char *rp_id, const char *rp_name, struct blob *user_id,
689 const char *user_name, const char *user_nick, const char *user_icon,
690 int ext, uint8_t rk, uint8_t uv, const char *pin, uint8_t excl_count,
691 struct blob *excl_cred)
692{
693 fido_dev_t *dev;
694 fido_dev_io_t io;
695
696 io.open = dev_open;
697 io.close = dev_close;
698 io.read = dev_read;
699 io.write = dev_write;
700
701 if ((dev = fido_dev_new()) == NULL || fido_dev_set_io_functions(dev,
702 &io) != FIDO_OK || fido_dev_open(dev, "nodev") != FIDO_OK) {
703 fido_dev_free(&dev);
704 return;
705 }
706
707 if (u2f & 1)
708 fido_dev_force_u2f(dev);
709
710 for (uint8_t i = 0; i < excl_count; i++)
711 fido_cred_exclude(cred, excl_cred->body, excl_cred->len);
712
713 fido_cred_set_type(cred, type);
714 fido_cred_set_clientdata_hash(cred, cdh->body, cdh->len);
715 fido_cred_set_rp(cred, rp_id, rp_name);
716 fido_cred_set_user(cred, user_id->body, user_id->len, user_name,
717 user_nick, user_icon);
718 fido_cred_set_extensions(cred, ext);
719 if (rk & 1)
720 fido_cred_set_rk(cred, FIDO_OPT_TRUE);
721 if (uv & 1)
722 fido_cred_set_uv(cred, FIDO_OPT_TRUE);
723
724 fido_dev_make_cred(dev, cred, u2f & 1 ? NULL : pin);
725
726 fido_dev_cancel(dev);
727 fido_dev_close(dev);
728 fido_dev_free(&dev);
729}
730
731static void
732verify_cred(int type, const unsigned char *cdh_ptr, size_t cdh_len,
733 const char *rp_id, const char *rp_name, const unsigned char *authdata_ptr,
734 size_t authdata_len, int ext, uint8_t rk, uint8_t uv,
735 const unsigned char *x5c_ptr, size_t x5c_len, const unsigned char *sig_ptr,
736 size_t sig_len, const char *fmt)
737{
738 fido_cred_t *cred;
739 uint8_t flags;
740
741 if ((cred = fido_cred_new()) == NULL) {
742 warnx("%s: fido_cred_new", __func__);
743 return;
744 }
745
746 fido_cred_set_type(cred, type);
747 fido_cred_set_clientdata_hash(cred, cdh_ptr, cdh_len);
748 fido_cred_set_rp(cred, rp_id, rp_name);
749 if (fido_cred_set_authdata(cred, authdata_ptr, authdata_len) != FIDO_OK)
750 fido_cred_set_authdata_raw(cred, authdata_ptr, authdata_len);
751 fido_cred_set_extensions(cred, ext);
752 fido_cred_set_x509(cred, x5c_ptr, x5c_len);
753 fido_cred_set_sig(cred, sig_ptr, sig_len);
754
755 if (rk & 1)
756 fido_cred_set_rk(cred, FIDO_OPT_TRUE);
757 if (uv & 1)
758 fido_cred_set_uv(cred, FIDO_OPT_TRUE);
759 if (fmt)
760 fido_cred_set_fmt(cred, fmt);
761
762 fido_cred_verify(cred);
763 fido_cred_verify_self(cred);
764
765 consume(fido_cred_pubkey_ptr(cred), fido_cred_pubkey_len(cred));
766 consume(fido_cred_id_ptr(cred), fido_cred_id_len(cred));
767 consume(fido_cred_user_id_ptr(cred), fido_cred_user_id_len(cred));
768 consume(fido_cred_user_name(cred), xstrlen(fido_cred_user_name(cred)));
769 consume(fido_cred_display_name(cred),
770 xstrlen(fido_cred_display_name(cred)));
771
772 flags = fido_cred_flags(cred);
773 consume(&flags, sizeof(flags));
774 type = fido_cred_type(cred);
775 consume(&type, sizeof(type));
776
777 fido_cred_free(&cred);
778}
779
780int
781LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
782{
783 struct param p;
784 fido_cred_t *cred = NULL;
785 int cose_alg = 0;
786
787 memset(&p, 0, sizeof(p));
788
789 if (unpack(data, size, &p) < 0)
790 return (0);
791
792 srandom((unsigned int)p.seed);
793
794 fido_init(0);
795
796 if ((cred = fido_cred_new()) == NULL)
797 return (0);
798
799 set_wire_data(p.wire_data.body, p.wire_data.len);
800
801 switch (p.type & 3) {
802 case 0:
803 cose_alg = COSE_ES256;
804 break;
805 case 1:
806 cose_alg = COSE_RS256;
807 break;
808 default:
809 cose_alg = COSE_EDDSA;
810 break;
811 }
812
813 make_cred(cred, p.u2f, cose_alg, &p.cdh, p.rp_id, p.rp_name,
814 &p.user_id, p.user_name, p.user_nick, p.user_icon, p.ext, p.rk,
815 p.uv, p.pin, p.excl_count, &p.excl_cred);
816
817 verify_cred(cose_alg,
818 fido_cred_clientdata_hash_ptr(cred),
819 fido_cred_clientdata_hash_len(cred), fido_cred_rp_id(cred),
820 fido_cred_rp_name(cred), fido_cred_authdata_ptr(cred),
821 fido_cred_authdata_len(cred), p.ext, p.rk, p.uv,
822 fido_cred_x5c_ptr(cred), fido_cred_x5c_len(cred),
823 fido_cred_sig_ptr(cred), fido_cred_sig_len(cred),
824 fido_cred_fmt(cred));
825
826 fido_cred_free(&cred);
827
828 return (0);
829}
830
831static size_t
832pack_dummy(uint8_t *ptr, size_t len)
833{
834 struct param dummy;
835 uint8_t blob[16384];
836 size_t blob_len;
837
838 memset(&dummy, 0, sizeof(dummy));
839
840 dummy.type = 1;
841 dummy.ext = FIDO_EXT_HMAC_SECRET;
842
843 strlcpy(dummy.pin, dummy_pin, sizeof(dummy.pin));
844 strlcpy(dummy.rp_id, dummy_rp_id, sizeof(dummy.rp_id));
845 strlcpy(dummy.rp_name, dummy_rp_name, sizeof(dummy.rp_name));
846 strlcpy(dummy.user_icon, dummy_user_icon, sizeof(dummy.user_icon));
847 strlcpy(dummy.user_name, dummy_user_name, sizeof(dummy.user_name));
848 strlcpy(dummy.user_nick, dummy_user_nick, sizeof(dummy.user_nick));
849
850 dummy.cdh.len = sizeof(dummy_cdh);
851 dummy.user_id.len = sizeof(dummy_user_id);
852 dummy.wire_data.len = sizeof(dummy_wire_data_fido);
853
854 memcpy(&dummy.cdh.body, &dummy_cdh, dummy.cdh.len);
855 memcpy(&dummy.user_id.body, &dummy_user_id, dummy.user_id.len);
856 memcpy(&dummy.wire_data.body, &dummy_wire_data_fido,
857 dummy.wire_data.len);
858
859 blob_len = pack(blob, sizeof(blob), &dummy);
860 assert(blob_len != 0);
861
862 if (blob_len > len) {
863 memcpy(ptr, blob, len);
864 return (len);
865 }
866
867 memcpy(ptr, blob, blob_len);
868
869 return (blob_len);
870}
871
872size_t
873LLVMFuzzerCustomMutator(uint8_t *data, size_t size, size_t maxsize,
874 unsigned int seed) NO_MSAN
875{
876 struct param p;
877 uint8_t blob[16384];
878 size_t blob_len;
879
880 memset(&p, 0, sizeof(p));
881
882 if (unpack(data, size, &p) < 0)
883 return (pack_dummy(data, maxsize));
884
885 mutate_byte(&p.rk);
886 mutate_byte(&p.type);
887 mutate_byte(&p.u2f);
888 mutate_byte(&p.uv);
889 mutate_byte(&p.excl_count);
890
891 mutate_int(&p.ext);
892 p.seed = (int)seed;
893
894 mutate_blob(&p.cdh);
895 mutate_blob(&p.user_id);
896
897 if (p.u2f & 1) {
898 p.wire_data.len = sizeof(dummy_wire_data_u2f);
899 memcpy(&p.wire_data.body, &dummy_wire_data_u2f,
900 p.wire_data.len);
901 } else {
902 p.wire_data.len = sizeof(dummy_wire_data_fido);
903 memcpy(&p.wire_data.body, &dummy_wire_data_fido,
904 p.wire_data.len);
905 }
906
907 mutate_blob(&p.wire_data);
908 mutate_blob(&p.excl_cred);
909
910 mutate_string(p.pin);
911 mutate_string(p.user_icon);
912 mutate_string(p.user_name);
913 mutate_string(p.user_nick);
914 mutate_string(p.rp_id);
915 mutate_string(p.rp_name);
916
917 blob_len = pack(blob, sizeof(blob), &p);
918
919 if (blob_len == 0 || blob_len > maxsize)
920 return (0);
921
922 memcpy(data, blob, blob_len);
923
924 return (blob_len);
925}
diff --git a/fuzz/fuzz_credman.c b/fuzz/fuzz_credman.c
new file mode 100644
index 0000000..4359938
--- /dev/null
+++ b/fuzz/fuzz_credman.c
@@ -0,0 +1,667 @@
1/*
2 * Copyright (c) 2019 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <assert.h>
8#include <stdint.h>
9#include <stdlib.h>
10#include <string.h>
11#include <stdio.h>
12
13#include "mutator_aux.h"
14#include "fido.h"
15#include "fido/credman.h"
16
17#include "../openbsd-compat/openbsd-compat.h"
18
19#define TAG_META_WIRE_DATA 0x01
20#define TAG_RP_WIRE_DATA 0x02
21#define TAG_RK_WIRE_DATA 0x03
22#define TAG_DEL_WIRE_DATA 0x04
23#define TAG_CRED_ID 0x05
24#define TAG_PIN 0x06
25#define TAG_RP_ID 0x07
26#define TAG_SEED 0x08
27
28/* Parameter set defining a FIDO2 credential management operation. */
29struct param {
30 char pin[MAXSTR];
31 char rp_id[MAXSTR];
32 int seed;
33 struct blob cred_id;
34 struct blob del_wire_data;
35 struct blob meta_wire_data;
36 struct blob rk_wire_data;
37 struct blob rp_wire_data;
38};
39
40/* Example parameters. */
41static const uint8_t dummy_cred_id[] = {
42 0x4f, 0x72, 0x98, 0x42, 0x4a, 0xe1, 0x17, 0xa5,
43 0x85, 0xa0, 0xef, 0x3b, 0x11, 0x24, 0x4a, 0x3d,
44};
45static const char dummy_pin[] = "[n#899:~m";
46static const char dummy_rp_id[] = "yubico.com";
47
48/*
49 * Collection of HID reports from an authenticator issued with a FIDO2
50 * 'getCredsMetadata' credential management command.
51 */
52static const uint8_t dummy_meta_wire_data[] = {
53 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0xc5,
54 0xb7, 0x89, 0xba, 0x8d, 0x5f, 0x94, 0x1b, 0x00,
55 0x12, 0x00, 0x04, 0x02, 0x00, 0x04, 0x05, 0x05,
56 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
57 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
58 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
59 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
60 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
61 0x00, 0x12, 0x00, 0x04, 0x90, 0x00, 0x51, 0x00,
62 0xa1, 0x01, 0xa5, 0x01, 0x02, 0x03, 0x38, 0x18,
63 0x20, 0x01, 0x21, 0x58, 0x20, 0x93, 0xc5, 0x64,
64 0x71, 0xe9, 0xd1, 0xb8, 0xed, 0xf6, 0xd5, 0xf3,
65 0xa7, 0xd5, 0x96, 0x70, 0xbb, 0xd5, 0x20, 0xa1,
66 0xa3, 0xd3, 0x93, 0x4c, 0x5c, 0x20, 0x5c, 0x22,
67 0xeb, 0xb0, 0x6a, 0x27, 0x59, 0x22, 0x58, 0x20,
68 0x63, 0x02, 0x33, 0xa8, 0xed, 0x3c, 0xbc, 0xe9,
69 0x00, 0x12, 0x00, 0x04, 0x00, 0xda, 0x44, 0xf5,
70 0xed, 0xda, 0xe6, 0xa4, 0xad, 0x3f, 0x9e, 0xf8,
71 0x50, 0x8d, 0x01, 0x47, 0x6c, 0x4e, 0x72, 0xa4,
72 0x04, 0x13, 0xa8, 0x65, 0x97, 0x00, 0x00, 0x00,
73 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
74 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
75 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
76 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
77 0x00, 0x12, 0x00, 0x04, 0x90, 0x00, 0x14, 0x00,
78 0xa1, 0x02, 0x50, 0x6f, 0x11, 0x96, 0x21, 0x92,
79 0x52, 0xf1, 0x6b, 0xd4, 0x2c, 0xe3, 0xf8, 0xc9,
80 0x8c, 0x47, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
81 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
82 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
83 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
84 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
85 0x00, 0x12, 0x00, 0x04, 0x90, 0x00, 0x07, 0x00,
86 0xa2, 0x01, 0x00, 0x02, 0x18, 0x19, 0x00, 0x00,
87 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
88 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
89 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
90 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
91 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
92 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
93};
94
95/*
96 * Collection of HID reports from an authenticator issued with a FIDO2
97 * 'enumerateRPsBegin' credential management command.
98 */
99static const uint8_t dummy_rp_wire_data[] = {
100 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0x87,
101 0xbf, 0xc6, 0x7f, 0x36, 0xf5, 0xe2, 0x49, 0x00,
102 0x15, 0x00, 0x02, 0x02, 0x00, 0x04, 0x05, 0x05,
103 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
104 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
105 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
106 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
107 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
108 0x00, 0x15, 0x00, 0x02, 0x90, 0x00, 0x51, 0x00,
109 0xa1, 0x01, 0xa5, 0x01, 0x02, 0x03, 0x38, 0x18,
110 0x20, 0x01, 0x21, 0x58, 0x20, 0x12, 0xc1, 0x81,
111 0x6b, 0x92, 0x6a, 0x56, 0x05, 0xfe, 0xdb, 0xab,
112 0x90, 0x2f, 0x57, 0x0b, 0x3d, 0x85, 0x3e, 0x3f,
113 0xbc, 0xe5, 0xd3, 0xb6, 0x86, 0xdf, 0x10, 0x43,
114 0xc2, 0xaf, 0x87, 0x34, 0x0e, 0x22, 0x58, 0x20,
115 0xd3, 0x0f, 0x7e, 0x5d, 0x10, 0x33, 0x57, 0x24,
116 0x00, 0x15, 0x00, 0x02, 0x00, 0x6e, 0x90, 0x58,
117 0x61, 0x2a, 0xd2, 0xc2, 0x1e, 0x08, 0xea, 0x91,
118 0xcb, 0x44, 0x66, 0x73, 0x29, 0x92, 0x29, 0x59,
119 0x91, 0xa3, 0x4d, 0x2c, 0xbb, 0x00, 0x00, 0x00,
120 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
121 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
122 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
123 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
124 0x00, 0x15, 0x00, 0x02, 0x90, 0x00, 0x14, 0x00,
125 0xa1, 0x02, 0x50, 0x6d, 0x95, 0x0e, 0x73, 0x78,
126 0x46, 0x13, 0x2e, 0x07, 0xbf, 0xeb, 0x61, 0x31,
127 0x37, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
128 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
129 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
130 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
131 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
132 0x00, 0x15, 0x00, 0x02, 0x90, 0x00, 0x37, 0x00,
133 0xa3, 0x03, 0xa1, 0x62, 0x69, 0x64, 0x6a, 0x79,
134 0x75, 0x62, 0x69, 0x63, 0x6f, 0x2e, 0x63, 0x6f,
135 0x6d, 0x04, 0x58, 0x20, 0x37, 0x82, 0x09, 0xb7,
136 0x2d, 0xef, 0xcb, 0xa9, 0x1d, 0xcb, 0xf8, 0x54,
137 0xed, 0xb4, 0xda, 0xa6, 0x48, 0x82, 0x8a, 0x2c,
138 0xbd, 0x18, 0x0a, 0xfc, 0x77, 0xa7, 0x44, 0x34,
139 0x65, 0x5a, 0x1c, 0x7d, 0x05, 0x03, 0x00, 0x00,
140 0x00, 0x15, 0x00, 0x02, 0x90, 0x00, 0x36, 0x00,
141 0xa2, 0x03, 0xa1, 0x62, 0x69, 0x64, 0x6b, 0x79,
142 0x75, 0x62, 0x69, 0x6b, 0x65, 0x79, 0x2e, 0x6f,
143 0x72, 0x67, 0x04, 0x58, 0x20, 0x12, 0x6b, 0xba,
144 0x6a, 0x2d, 0x7a, 0x81, 0x84, 0x25, 0x7b, 0x74,
145 0xdd, 0x1d, 0xdd, 0x46, 0xb6, 0x2a, 0x8c, 0xa2,
146 0xa7, 0x83, 0xfe, 0xdb, 0x5b, 0x19, 0x48, 0x73,
147 0x55, 0xb7, 0xe3, 0x46, 0x09, 0x00, 0x00, 0x00,
148 0x00, 0x15, 0x00, 0x02, 0x90, 0x00, 0x37, 0x00,
149 0xa2, 0x03, 0xa1, 0x62, 0x69, 0x64, 0x6c, 0x77,
150 0x65, 0x62, 0x61, 0x75, 0x74, 0x68, 0x6e, 0x2e,
151 0x64, 0x65, 0x76, 0x04, 0x58, 0x20, 0xd6, 0x32,
152 0x7d, 0x8c, 0x6a, 0x5d, 0xe6, 0xae, 0x0e, 0x33,
153 0xd0, 0xa3, 0x31, 0xfb, 0x67, 0x77, 0xb9, 0x4e,
154 0xf4, 0x73, 0x19, 0xfe, 0x7e, 0xfd, 0xfa, 0x82,
155 0x70, 0x8e, 0x1f, 0xbb, 0xa2, 0x55, 0x00, 0x00,
156};
157
158/*
159 * Collection of HID reports from an authenticator issued with a FIDO2
160 * 'enumerateCredentialsBegin' credential management command.
161 */
162static const uint8_t dummy_rk_wire_data[] = {
163 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0x35,
164 0x3b, 0x34, 0xb9, 0xcb, 0xeb, 0x40, 0x55, 0x00,
165 0x15, 0x00, 0x04, 0x02, 0x00, 0x04, 0x05, 0x05,
166 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
167 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
168 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
169 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
170 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
171 0x00, 0x15, 0x00, 0x04, 0x90, 0x00, 0x51, 0x00,
172 0xa1, 0x01, 0xa5, 0x01, 0x02, 0x03, 0x38, 0x18,
173 0x20, 0x01, 0x21, 0x58, 0x20, 0x12, 0xc1, 0x81,
174 0x6b, 0x92, 0x6a, 0x56, 0x05, 0xfe, 0xdb, 0xab,
175 0x90, 0x2f, 0x57, 0x0b, 0x3d, 0x85, 0x3e, 0x3f,
176 0xbc, 0xe5, 0xd3, 0xb6, 0x86, 0xdf, 0x10, 0x43,
177 0xc2, 0xaf, 0x87, 0x34, 0x0e, 0x22, 0x58, 0x20,
178 0xd3, 0x0f, 0x7e, 0x5d, 0x10, 0x33, 0x57, 0x24,
179 0x00, 0x15, 0x00, 0x04, 0x00, 0x6e, 0x90, 0x58,
180 0x61, 0x2a, 0xd2, 0xc2, 0x1e, 0x08, 0xea, 0x91,
181 0xcb, 0x44, 0x66, 0x73, 0x29, 0x92, 0x29, 0x59,
182 0x91, 0xa3, 0x4d, 0x2c, 0xbb, 0x00, 0x00, 0x00,
183 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
184 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
185 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
186 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
187 0x00, 0x15, 0x00, 0x04, 0x90, 0x00, 0x14, 0x00,
188 0xa1, 0x02, 0x50, 0x1b, 0xf0, 0x01, 0x0d, 0x32,
189 0xee, 0x28, 0xa4, 0x5a, 0x7f, 0x56, 0x5b, 0x28,
190 0xfd, 0x1f, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00,
191 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
192 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
193 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
194 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
195 0x00, 0x15, 0x00, 0x04, 0x90, 0x00, 0xc5, 0x00,
196 0xa5, 0x06, 0xa3, 0x62, 0x69, 0x64, 0x58, 0x20,
197 0xe4, 0xe1, 0x06, 0x31, 0xde, 0x00, 0x0f, 0x4f,
198 0x12, 0x6e, 0xc9, 0x68, 0x2d, 0x43, 0x3f, 0xf1,
199 0x02, 0x2c, 0x6e, 0xe6, 0x96, 0x10, 0xbf, 0x73,
200 0x35, 0xc9, 0x20, 0x27, 0x06, 0xba, 0x39, 0x09,
201 0x64, 0x6e, 0x61, 0x6d, 0x65, 0x6a, 0x62, 0x6f,
202 0x62, 0x20, 0x62, 0x61, 0x6e, 0x61, 0x6e, 0x61,
203 0x00, 0x15, 0x00, 0x04, 0x00, 0x6b, 0x64, 0x69,
204 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d,
205 0x65, 0x67, 0x62, 0x62, 0x61, 0x6e, 0x61, 0x6e,
206 0x61, 0x07, 0xa2, 0x62, 0x69, 0x64, 0x50, 0x19,
207 0xf7, 0x78, 0x0c, 0xa0, 0xbc, 0xb9, 0xa6, 0xd5,
208 0x1e, 0xd7, 0x87, 0xfb, 0x6c, 0x80, 0x03, 0x64,
209 0x74, 0x79, 0x70, 0x65, 0x6a, 0x70, 0x75, 0x62,
210 0x6c, 0x69, 0x63, 0x2d, 0x6b, 0x65, 0x79, 0x08,
211 0x00, 0x15, 0x00, 0x04, 0x01, 0xa5, 0x01, 0x02,
212 0x03, 0x26, 0x20, 0x01, 0x21, 0x58, 0x20, 0x81,
213 0x6c, 0xdd, 0x8c, 0x8f, 0x8c, 0xc8, 0x43, 0xa7,
214 0xbb, 0x79, 0x51, 0x09, 0xb1, 0xdf, 0xbe, 0xc4,
215 0xa5, 0x54, 0x16, 0x9e, 0x58, 0x56, 0xb3, 0x0b,
216 0x34, 0x4f, 0xa5, 0x6c, 0x05, 0xa2, 0x21, 0x22,
217 0x58, 0x20, 0xcd, 0xc2, 0x0c, 0x99, 0x83, 0x5a,
218 0x61, 0x73, 0xd8, 0xe0, 0x74, 0x23, 0x46, 0x64,
219 0x00, 0x15, 0x00, 0x04, 0x02, 0x39, 0x4c, 0xb0,
220 0xf4, 0x6c, 0x0a, 0x37, 0x72, 0xaa, 0xa8, 0xea,
221 0x58, 0xd3, 0xd4, 0xe0, 0x51, 0xb2, 0x28, 0x09,
222 0x05, 0x0a, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
223 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
224 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
225 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
226 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
227 0x00, 0x15, 0x00, 0x04, 0x90, 0x00, 0xa0, 0x00,
228 0xa4, 0x06, 0xa3, 0x62, 0x69, 0x64, 0x58, 0x20,
229 0x56, 0xa1, 0x3c, 0x06, 0x2b, 0xad, 0xa2, 0x21,
230 0x7d, 0xcd, 0x91, 0x08, 0x47, 0xa8, 0x8a, 0x06,
231 0x06, 0xf6, 0x66, 0x91, 0xf6, 0xeb, 0x89, 0xe4,
232 0xdf, 0x26, 0xbc, 0x46, 0x59, 0xc3, 0x7d, 0xc0,
233 0x64, 0x6e, 0x61, 0x6d, 0x65, 0x6a, 0x62, 0x6f,
234 0x62, 0x20, 0x62, 0x61, 0x6e, 0x61, 0x6e, 0x61,
235 0x00, 0x15, 0x00, 0x04, 0x00, 0x6b, 0x64, 0x69,
236 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d,
237 0x65, 0x67, 0x62, 0x62, 0x61, 0x6e, 0x61, 0x6e,
238 0x61, 0x07, 0xa2, 0x62, 0x69, 0x64, 0x50, 0xd8,
239 0x27, 0x4b, 0x25, 0xed, 0x19, 0xef, 0x11, 0xaf,
240 0xa6, 0x89, 0x7b, 0x84, 0x50, 0xe7, 0x62, 0x64,
241 0x74, 0x79, 0x70, 0x65, 0x6a, 0x70, 0x75, 0x62,
242 0x6c, 0x69, 0x63, 0x2d, 0x6b, 0x65, 0x79, 0x08,
243 0x00, 0x15, 0x00, 0x04, 0x01, 0xa4, 0x01, 0x01,
244 0x03, 0x27, 0x20, 0x06, 0x21, 0x58, 0x20, 0x8d,
245 0xfe, 0x45, 0xd5, 0x7d, 0xb6, 0x17, 0xab, 0x86,
246 0x2d, 0x32, 0xf6, 0x85, 0xf0, 0x92, 0x76, 0xb7,
247 0xce, 0x73, 0xca, 0x4e, 0x0e, 0xfd, 0xd5, 0xdb,
248 0x2a, 0x1d, 0x55, 0x90, 0x96, 0x52, 0xc2, 0x0a,
249 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
250 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
251 0x00, 0x15, 0x00, 0x04, 0x90, 0x00, 0xa0, 0x00,
252 0xa4, 0x06, 0xa3, 0x62, 0x69, 0x64, 0x58, 0x20,
253 0x04, 0x0e, 0x0f, 0xa0, 0xcd, 0x60, 0x35, 0x9a,
254 0xba, 0x47, 0x0c, 0x10, 0xb6, 0x82, 0x6e, 0x2f,
255 0x66, 0xb9, 0xa7, 0xcf, 0xd8, 0x47, 0xb4, 0x3d,
256 0xfd, 0x77, 0x1a, 0x38, 0x22, 0xa1, 0xda, 0xa5,
257 0x64, 0x6e, 0x61, 0x6d, 0x65, 0x6a, 0x62, 0x6f,
258 0x62, 0x20, 0x62, 0x61, 0x6e, 0x61, 0x6e, 0x61,
259 0x00, 0x15, 0x00, 0x04, 0x00, 0x6b, 0x64, 0x69,
260 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d,
261 0x65, 0x67, 0x62, 0x62, 0x61, 0x6e, 0x61, 0x6e,
262 0x61, 0x07, 0xa2, 0x62, 0x69, 0x64, 0x50, 0x00,
263 0x5d, 0xdf, 0xef, 0xe2, 0xf3, 0x06, 0xb2, 0xa5,
264 0x46, 0x4d, 0x98, 0xbc, 0x14, 0x65, 0xc1, 0x64,
265 0x74, 0x79, 0x70, 0x65, 0x6a, 0x70, 0x75, 0x62,
266 0x6c, 0x69, 0x63, 0x2d, 0x6b, 0x65, 0x79, 0x08,
267 0x00, 0x15, 0x00, 0x04, 0x01, 0xa4, 0x01, 0x01,
268 0x03, 0x27, 0x20, 0x06, 0x21, 0x58, 0x20, 0x72,
269 0x79, 0x14, 0x69, 0xdf, 0xcb, 0x64, 0x75, 0xee,
270 0xd4, 0x45, 0x94, 0xbc, 0x48, 0x4d, 0x2a, 0x9f,
271 0xc9, 0xf4, 0xb5, 0x1b, 0x05, 0xa6, 0x5b, 0x54,
272 0x9a, 0xac, 0x6c, 0x2e, 0xc6, 0x90, 0x62, 0x0a,
273 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
274 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
275 0x00, 0x15, 0x00, 0x04, 0x90, 0x00, 0xc3, 0x00,
276 0xa4, 0x06, 0xa3, 0x62, 0x69, 0x64, 0x58, 0x20,
277 0xce, 0x32, 0xd8, 0x79, 0xdd, 0x86, 0xa2, 0x42,
278 0x7c, 0xc3, 0xe1, 0x95, 0x12, 0x93, 0x1a, 0x03,
279 0xe6, 0x70, 0xb8, 0xff, 0xcd, 0xa5, 0xdf, 0x15,
280 0xfc, 0x88, 0x2a, 0xf5, 0x44, 0xf1, 0x33, 0x9c,
281 0x64, 0x6e, 0x61, 0x6d, 0x65, 0x6a, 0x62, 0x6f,
282 0x62, 0x20, 0x62, 0x61, 0x6e, 0x61, 0x6e, 0x61,
283 0x00, 0x15, 0x00, 0x04, 0x00, 0x6b, 0x64, 0x69,
284 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d,
285 0x65, 0x67, 0x62, 0x62, 0x61, 0x6e, 0x61, 0x6e,
286 0x61, 0x07, 0xa2, 0x62, 0x69, 0x64, 0x50, 0x0a,
287 0x26, 0x5b, 0x7e, 0x1a, 0x2a, 0xba, 0x70, 0x5f,
288 0x18, 0x26, 0x14, 0xb2, 0x71, 0xca, 0x98, 0x64,
289 0x74, 0x79, 0x70, 0x65, 0x6a, 0x70, 0x75, 0x62,
290 0x6c, 0x69, 0x63, 0x2d, 0x6b, 0x65, 0x79, 0x08,
291 0x00, 0x15, 0x00, 0x04, 0x01, 0xa5, 0x01, 0x02,
292 0x03, 0x26, 0x20, 0x01, 0x21, 0x58, 0x20, 0x8b,
293 0x48, 0xf0, 0x69, 0xfb, 0x22, 0xfb, 0xf3, 0x86,
294 0x57, 0x7c, 0xdd, 0x82, 0x2c, 0x1c, 0x0c, 0xdc,
295 0x27, 0xe2, 0x6a, 0x4c, 0x1a, 0x10, 0x04, 0x27,
296 0x51, 0x3e, 0x2a, 0x9d, 0x3a, 0xb6, 0xb5, 0x22,
297 0x58, 0x20, 0x70, 0xfe, 0x91, 0x67, 0x64, 0x53,
298 0x63, 0x83, 0x72, 0x31, 0xe9, 0xe5, 0x20, 0xb7,
299 0x00, 0x15, 0x00, 0x04, 0x02, 0xee, 0xc9, 0xfb,
300 0x63, 0xd7, 0xe4, 0x76, 0x39, 0x80, 0x82, 0x74,
301 0xb8, 0xfa, 0x67, 0xf5, 0x1b, 0x8f, 0xe0, 0x0a,
302 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
303 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
304 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
305 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
306 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
307 0x00, 0x15, 0x00, 0x04, 0x90, 0x00, 0xc3, 0x00,
308 0xa4, 0x06, 0xa3, 0x62, 0x69, 0x64, 0x58, 0x20,
309 0xf9, 0xa3, 0x67, 0xbf, 0x5e, 0x80, 0x95, 0xdb,
310 0x4c, 0xc5, 0x8f, 0x65, 0x36, 0xc5, 0xaf, 0xdd,
311 0x90, 0x2e, 0x62, 0x68, 0x67, 0x9c, 0xa2, 0x26,
312 0x2f, 0x2a, 0xf9, 0x3a, 0xda, 0x15, 0xf2, 0x27,
313 0x64, 0x6e, 0x61, 0x6d, 0x65, 0x6a, 0x62, 0x6f,
314 0x62, 0x20, 0x62, 0x61, 0x6e, 0x61, 0x6e, 0x61,
315 0x00, 0x15, 0x00, 0x04, 0x00, 0x6b, 0x64, 0x69,
316 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d,
317 0x65, 0x67, 0x62, 0x62, 0x61, 0x6e, 0x61, 0x6e,
318 0x61, 0x07, 0xa2, 0x62, 0x69, 0x64, 0x50, 0xfb,
319 0xa6, 0xbe, 0xc1, 0x01, 0xf6, 0x7a, 0x81, 0xf9,
320 0xcd, 0x6d, 0x20, 0x41, 0x7a, 0x1c, 0x40, 0x64,
321 0x74, 0x79, 0x70, 0x65, 0x6a, 0x70, 0x75, 0x62,
322 0x6c, 0x69, 0x63, 0x2d, 0x6b, 0x65, 0x79, 0x08,
323 0x00, 0x15, 0x00, 0x04, 0x01, 0xa5, 0x01, 0x02,
324 0x03, 0x26, 0x20, 0x01, 0x21, 0x58, 0x20, 0xda,
325 0x2b, 0x53, 0xc3, 0xbe, 0x48, 0xf8, 0xab, 0xbd,
326 0x06, 0x28, 0x46, 0xfa, 0x35, 0xab, 0xf9, 0xc5,
327 0x2e, 0xfd, 0x3c, 0x38, 0x88, 0xb3, 0xe1, 0xa7,
328 0xc5, 0xc6, 0xed, 0x72, 0x54, 0x37, 0x93, 0x22,
329 0x58, 0x20, 0x12, 0x82, 0x32, 0x2d, 0xab, 0xbc,
330 0x64, 0xb3, 0xed, 0xcc, 0xd5, 0x22, 0xec, 0x79,
331 0x00, 0x15, 0x00, 0x04, 0x02, 0x4b, 0xe2, 0x4d,
332 0x0c, 0x4b, 0x8d, 0x31, 0x4c, 0xb4, 0x0f, 0xd4,
333 0xa9, 0xbe, 0x0c, 0xab, 0x9e, 0x0a, 0xc9, 0x0a,
334 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
335 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
336 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
337 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
338 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
339};
340
341/*
342 * Collection of HID reports from an authenticator issued with a FIDO2
343 * 'deleteCredential' credential management command.
344 */
345static const uint8_t dummy_del_wire_data[] = {
346 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0x8b,
347 0xe1, 0xf0, 0x3a, 0x18, 0xa5, 0xda, 0x59, 0x00,
348 0x15, 0x00, 0x05, 0x02, 0x00, 0x04, 0x05, 0x05,
349 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
350 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
351 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
352 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
353 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
354 0x00, 0x15, 0x00, 0x05, 0x90, 0x00, 0x51, 0x00,
355 0xa1, 0x01, 0xa5, 0x01, 0x02, 0x03, 0x38, 0x18,
356 0x20, 0x01, 0x21, 0x58, 0x20, 0x12, 0xc1, 0x81,
357 0x6b, 0x92, 0x6a, 0x56, 0x05, 0xfe, 0xdb, 0xab,
358 0x90, 0x2f, 0x57, 0x0b, 0x3d, 0x85, 0x3e, 0x3f,
359 0xbc, 0xe5, 0xd3, 0xb6, 0x86, 0xdf, 0x10, 0x43,
360 0xc2, 0xaf, 0x87, 0x34, 0x0e, 0x22, 0x58, 0x20,
361 0xd3, 0x0f, 0x7e, 0x5d, 0x10, 0x33, 0x57, 0x24,
362 0x00, 0x15, 0x00, 0x05, 0x00, 0x6e, 0x90, 0x58,
363 0x61, 0x2a, 0xd2, 0xc2, 0x1e, 0x08, 0xea, 0x91,
364 0xcb, 0x44, 0x66, 0x73, 0x29, 0x92, 0x29, 0x59,
365 0x91, 0xa3, 0x4d, 0x2c, 0xbb, 0x00, 0x00, 0x00,
366 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
367 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
368 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
369 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
370 0x00, 0x15, 0x00, 0x05, 0x90, 0x00, 0x14, 0x00,
371 0xa1, 0x02, 0x50, 0x33, 0xf1, 0x3b, 0xde, 0x1e,
372 0xa5, 0xd1, 0xbf, 0xf6, 0x5d, 0x63, 0xb6, 0xfc,
373 0xd2, 0x24, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00,
374 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
375 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
376 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
377 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
378 0x00, 0x15, 0x00, 0x05, 0x90, 0x00, 0x01, 0x00,
379 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
380 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
381 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
382 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
383 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
384 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
385 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
386};
387
388int LLVMFuzzerTestOneInput(const uint8_t *, size_t);
389size_t LLVMFuzzerCustomMutator(uint8_t *, size_t, size_t, unsigned int);
390
391static int
392unpack(const uint8_t *ptr, size_t len, struct param *p) NO_MSAN
393{
394 uint8_t **pp = (void *)&ptr;
395
396 if (unpack_string(TAG_PIN, pp, &len, p->pin) < 0 ||
397 unpack_string(TAG_RP_ID, pp, &len, p->rp_id) < 0 ||
398 unpack_blob(TAG_CRED_ID, pp, &len, &p->cred_id) < 0 ||
399 unpack_blob(TAG_META_WIRE_DATA, pp, &len, &p->meta_wire_data) < 0 ||
400 unpack_blob(TAG_RP_WIRE_DATA, pp, &len, &p->rp_wire_data) < 0 ||
401 unpack_blob(TAG_RK_WIRE_DATA, pp, &len, &p->rk_wire_data) < 0 ||
402 unpack_blob(TAG_DEL_WIRE_DATA, pp, &len, &p->del_wire_data) < 0 ||
403 unpack_int(TAG_SEED, pp, &len, &p->seed) < 0)
404 return (-1);
405
406 return (0);
407}
408
409static size_t
410pack(uint8_t *ptr, size_t len, const struct param *p)
411{
412 const size_t max = len;
413
414 if (pack_string(TAG_PIN, &ptr, &len, p->pin) < 0 ||
415 pack_string(TAG_RP_ID, &ptr, &len, p->rp_id) < 0 ||
416 pack_blob(TAG_CRED_ID, &ptr, &len, &p->cred_id) < 0 ||
417 pack_blob(TAG_META_WIRE_DATA, &ptr, &len, &p->meta_wire_data) < 0 ||
418 pack_blob(TAG_RP_WIRE_DATA, &ptr, &len, &p->rp_wire_data) < 0 ||
419 pack_blob(TAG_RK_WIRE_DATA, &ptr, &len, &p->rk_wire_data) < 0 ||
420 pack_blob(TAG_DEL_WIRE_DATA, &ptr, &len, &p->del_wire_data) < 0 ||
421 pack_int(TAG_SEED, &ptr, &len, p->seed) < 0)
422 return (0);
423
424 return (max - len);
425}
426
427static fido_dev_t *
428prepare_dev()
429{
430 fido_dev_t *dev;
431 fido_dev_io_t io;
432
433 io.open = dev_open;
434 io.close = dev_close;
435 io.read = dev_read;
436 io.write = dev_write;
437
438 if ((dev = fido_dev_new()) == NULL || fido_dev_set_io_functions(dev,
439 &io) != FIDO_OK || fido_dev_open(dev, "nodev") != FIDO_OK) {
440 fido_dev_free(&dev);
441 return (NULL);
442 }
443
444 return (dev);
445}
446
447static void
448get_metadata(struct param *p)
449{
450 fido_dev_t *dev;
451 fido_credman_metadata_t *metadata;
452 uint64_t existing;
453 uint64_t remaining;
454
455 set_wire_data(p->meta_wire_data.body, p->meta_wire_data.len);
456
457 if ((dev = prepare_dev()) == NULL) {
458 return;
459 }
460 if ((metadata = fido_credman_metadata_new()) == NULL) {
461 fido_dev_close(dev);
462 fido_dev_free(&dev);
463 return;
464 }
465
466 fido_credman_get_dev_metadata(dev, metadata, p->pin);
467
468 existing = fido_credman_rk_existing(metadata);
469 remaining = fido_credman_rk_remaining(metadata);
470 consume(&existing, sizeof(existing));
471 consume(&remaining, sizeof(remaining));
472
473 fido_credman_metadata_free(&metadata);
474 fido_dev_close(dev);
475 fido_dev_free(&dev);
476}
477
478static void
479get_rp_list(struct param *p)
480{
481 fido_dev_t *dev;
482 fido_credman_rp_t *rp;
483
484 set_wire_data(p->rp_wire_data.body, p->rp_wire_data.len);
485
486 if ((dev = prepare_dev()) == NULL) {
487 return;
488 }
489 if ((rp = fido_credman_rp_new()) == NULL) {
490 fido_dev_close(dev);
491 fido_dev_free(&dev);
492 return;
493 }
494
495 fido_credman_get_dev_rp(dev, rp, p->pin);
496
497 /* +1 on purpose */
498 for (size_t i = 0; i < fido_credman_rp_count(rp) + 1; i++) {
499 consume(fido_credman_rp_id_hash_ptr(rp, i),
500 fido_credman_rp_id_hash_len(rp, i));
501 consume(fido_credman_rp_id(rp, i),
502 xstrlen(fido_credman_rp_id(rp, i)));
503 consume(fido_credman_rp_name(rp, i),
504 xstrlen(fido_credman_rp_name(rp, i)));
505 }
506
507 fido_credman_rp_free(&rp);
508 fido_dev_close(dev);
509 fido_dev_free(&dev);
510}
511
512static void
513get_rk_list(struct param *p)
514{
515 fido_dev_t *dev;
516 fido_credman_rk_t *rk;
517 const fido_cred_t *cred;
518 int type;
519
520 set_wire_data(p->rk_wire_data.body, p->rk_wire_data.len);
521
522 if ((dev = prepare_dev()) == NULL) {
523 return;
524 }
525 if ((rk = fido_credman_rk_new()) == NULL) {
526 fido_dev_close(dev);
527 fido_dev_free(&dev);
528 return;
529 }
530
531 fido_credman_get_dev_rk(dev, p->rp_id, rk, p->pin);
532
533 /* +1 on purpose */
534 for (size_t i = 0; i < fido_credman_rk_count(rk) + 1; i++) {
535 if ((cred = fido_credman_rk(rk, i)) == NULL) {
536 assert(i >= fido_credman_rk_count(rk));
537 continue;
538 }
539 type = fido_cred_type(cred);
540 consume(&type, sizeof(type));
541 consume(fido_cred_id_ptr(cred), fido_cred_id_len(cred));
542 consume(fido_cred_pubkey_ptr(cred), fido_cred_pubkey_len(cred));
543 consume(fido_cred_user_id_ptr(cred),
544 fido_cred_user_id_len(cred));
545 consume(fido_cred_user_name(cred),
546 xstrlen(fido_cred_user_name(cred)));
547 consume(fido_cred_display_name(cred),
548 xstrlen(fido_cred_display_name(cred)));
549 }
550
551 fido_credman_rk_free(&rk);
552 fido_dev_close(dev);
553 fido_dev_free(&dev);
554}
555
556static void
557del_rk(struct param *p)
558{
559 fido_dev_t *dev;
560
561 set_wire_data(p->del_wire_data.body, p->del_wire_data.len);
562
563 if ((dev = prepare_dev()) == NULL) {
564 return;
565 }
566
567 fido_credman_del_dev_rk(dev, p->cred_id.body, p->cred_id.len, p->pin);
568 fido_dev_close(dev);
569 fido_dev_free(&dev);
570}
571
572int
573LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
574{
575 struct param p;
576
577 memset(&p, 0, sizeof(p));
578
579 if (unpack(data, size, &p) < 0)
580 return (0);
581
582 srandom((unsigned int)p.seed);
583
584 fido_init(0);
585
586 get_metadata(&p);
587 get_rp_list(&p);
588 get_rk_list(&p);
589 del_rk(&p);
590
591 return (0);
592}
593
594static size_t
595pack_dummy(uint8_t *ptr, size_t len)
596{
597 struct param dummy;
598 uint8_t blob[32768];
599 size_t blob_len;
600
601 memset(&dummy, 0, sizeof(dummy));
602
603 strlcpy(dummy.pin, dummy_pin, sizeof(dummy.pin));
604 strlcpy(dummy.rp_id, dummy_rp_id, sizeof(dummy.rp_id));
605
606 dummy.meta_wire_data.len = sizeof(dummy_meta_wire_data);
607 dummy.rp_wire_data.len = sizeof(dummy_rp_wire_data);
608 dummy.rk_wire_data.len = sizeof(dummy_rk_wire_data);
609 dummy.del_wire_data.len = sizeof(dummy_del_wire_data);
610 dummy.cred_id.len = sizeof(dummy_cred_id);
611
612 memcpy(&dummy.meta_wire_data.body, &dummy_meta_wire_data,
613 dummy.meta_wire_data.len);
614 memcpy(&dummy.rp_wire_data.body, &dummy_rp_wire_data,
615 dummy.rp_wire_data.len);
616 memcpy(&dummy.rk_wire_data.body, &dummy_rk_wire_data,
617 dummy.rk_wire_data.len);
618 memcpy(&dummy.del_wire_data.body, &dummy_del_wire_data,
619 dummy.del_wire_data.len);
620 memcpy(&dummy.cred_id.body, &dummy_cred_id, dummy.cred_id.len);
621
622 blob_len = pack(blob, sizeof(blob), &dummy);
623 assert(blob_len != 0);
624
625 if (blob_len > len) {
626 memcpy(ptr, blob, len);
627 return (len);
628 }
629
630 memcpy(ptr, blob, blob_len);
631
632 return (blob_len);
633}
634
635size_t
636LLVMFuzzerCustomMutator(uint8_t *data, size_t size, size_t maxsize,
637 unsigned int seed) NO_MSAN
638{
639 struct param p;
640 uint8_t blob[16384];
641 size_t blob_len;
642
643 memset(&p, 0, sizeof(p));
644
645 if (unpack(data, size, &p) < 0)
646 return (pack_dummy(data, maxsize));
647
648 p.seed = (int)seed;
649
650 mutate_blob(&p.cred_id);
651 mutate_blob(&p.meta_wire_data);
652 mutate_blob(&p.rp_wire_data);
653 mutate_blob(&p.rk_wire_data);
654 mutate_blob(&p.del_wire_data);
655
656 mutate_string(p.pin);
657 mutate_string(p.rp_id);
658
659 blob_len = pack(blob, sizeof(blob), &p);
660
661 if (blob_len == 0 || blob_len > maxsize)
662 return (0);
663
664 memcpy(data, blob, blob_len);
665
666 return (blob_len);
667}
diff --git a/fuzz/fuzz_mgmt.c b/fuzz/fuzz_mgmt.c
new file mode 100644
index 0000000..741b375
--- /dev/null
+++ b/fuzz/fuzz_mgmt.c
@@ -0,0 +1,529 @@
1/*
2 * Copyright (c) 2019 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <assert.h>
8#include <stdint.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12
13#include "mutator_aux.h"
14#include "fido.h"
15
16#include "../openbsd-compat/openbsd-compat.h"
17
18#define TAG_PIN1 0x01
19#define TAG_PIN2 0x02
20#define TAG_RESET_WIRE_DATA 0x03
21#define TAG_INFO_WIRE_DATA 0x04
22#define TAG_SET_PIN_WIRE_DATA 0x05
23#define TAG_CHANGE_PIN_WIRE_DATA 0x06
24#define TAG_RETRY_WIRE_DATA 0x07
25#define TAG_SEED 0x08
26
27struct param {
28 char pin1[MAXSTR];
29 char pin2[MAXSTR];
30 struct blob reset_wire_data;
31 struct blob info_wire_data;
32 struct blob set_pin_wire_data;
33 struct blob change_pin_wire_data;
34 struct blob retry_wire_data;
35 int seed;
36};
37
38/* Example parameters. */
39static const char dummy_pin1[] = "skepp cg0u3;Y..";
40static const char dummy_pin2[] = "bastilha 6rJrfQZI.";
41
42static const uint8_t dummy_reset_wire_data[] = {
43 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0x91,
44 0xef, 0xbe, 0x74, 0x39, 0x1a, 0x1c, 0x4a, 0x00,
45 0x22, 0x00, 0x01, 0x02, 0x05, 0x02, 0x01, 0x05,
46 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
47 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
48 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
49 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
50 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
51 0x00, 0x22, 0x00, 0x01, 0xbb, 0x00, 0x01, 0x02,
52 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
53 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
54 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
55 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
56 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
57 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
58 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
59 0x00, 0x22, 0x00, 0x01, 0xbb, 0x00, 0x01, 0x02,
60 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
61 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
62 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
63 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
64 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
65 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
66 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
67 0x00, 0x22, 0x00, 0x01, 0xbb, 0x00, 0x01, 0x02,
68 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
69 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
70 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
71 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
72 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
73 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
74 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
75 0x00, 0x22, 0x00, 0x01, 0xbb, 0x00, 0x01, 0x02,
76 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
77 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
78 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
79 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
80 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
81 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
82 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
83 0x00, 0x22, 0x00, 0x01, 0xbb, 0x00, 0x01, 0x02,
84 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
85 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
86 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
87 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
88 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
89 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
90 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
91 0x00, 0x22, 0x00, 0x01, 0xbb, 0x00, 0x01, 0x01,
92 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
93 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
94 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
95 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
96 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
97 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
98 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
99 0x00, 0x22, 0x00, 0x01, 0x90, 0x00, 0x01, 0x00,
100 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
101 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
102 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
103 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
104 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
105 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
106 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
107};
108
109static const uint8_t dummy_info_wire_data[] = {
110 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0x80,
111 0x43, 0x56, 0x40, 0xb1, 0x4e, 0xd9, 0x2d, 0x00,
112 0x22, 0x00, 0x02, 0x02, 0x05, 0x02, 0x01, 0x05,
113 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
114 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
115 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
116 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
117 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
118 0x00, 0x22, 0x00, 0x02, 0x90, 0x00, 0xb9, 0x00,
119 0xa9, 0x01, 0x83, 0x66, 0x55, 0x32, 0x46, 0x5f,
120 0x56, 0x32, 0x68, 0x46, 0x49, 0x44, 0x4f, 0x5f,
121 0x32, 0x5f, 0x30, 0x6c, 0x46, 0x49, 0x44, 0x4f,
122 0x5f, 0x32, 0x5f, 0x31, 0x5f, 0x50, 0x52, 0x45,
123 0x02, 0x82, 0x6b, 0x63, 0x72, 0x65, 0x64, 0x50,
124 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x6b, 0x68,
125 0x6d, 0x61, 0x63, 0x2d, 0x73, 0x65, 0x63, 0x72,
126 0x00, 0x22, 0x00, 0x02, 0x00, 0x65, 0x74, 0x03,
127 0x50, 0x19, 0x56, 0xe5, 0xbd, 0xa3, 0x74, 0x45,
128 0xf1, 0xa8, 0x14, 0x35, 0x64, 0x03, 0xfd, 0xbc,
129 0x18, 0x04, 0xa5, 0x62, 0x72, 0x6b, 0xf5, 0x62,
130 0x75, 0x70, 0xf5, 0x64, 0x70, 0x6c, 0x61, 0x74,
131 0xf4, 0x69, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74,
132 0x50, 0x69, 0x6e, 0xf4, 0x75, 0x63, 0x72, 0x65,
133 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4d,
134 0x00, 0x22, 0x00, 0x02, 0x01, 0x67, 0x6d, 0x74,
135 0x50, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0xf5,
136 0x05, 0x19, 0x04, 0xb0, 0x06, 0x81, 0x01, 0x07,
137 0x08, 0x08, 0x18, 0x80, 0x0a, 0x82, 0xa2, 0x63,
138 0x61, 0x6c, 0x67, 0x26, 0x64, 0x74, 0x79, 0x70,
139 0x65, 0x6a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63,
140 0x2d, 0x6b, 0x65, 0x79, 0xa2, 0x63, 0x61, 0x6c,
141 0x67, 0x27, 0x64, 0x74, 0x79, 0x70, 0x65, 0x6a,
142 0x00, 0x22, 0x00, 0x02, 0x02, 0x70, 0x75, 0x62,
143 0x6c, 0x69, 0x63, 0x2d, 0x6b, 0x65, 0x79, 0x00,
144 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
145 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
146 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
147 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
148 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
149 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
150};
151
152static const uint8_t dummy_set_pin_wire_data[] = {
153 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0x59,
154 0x50, 0x8c, 0x27, 0x14, 0x83, 0x43, 0xd5, 0x00,
155 0x22, 0x00, 0x03, 0x02, 0x05, 0x02, 0x01, 0x05,
156 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
157 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
158 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
159 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
160 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
161 0x00, 0x22, 0x00, 0x03, 0x90, 0x00, 0x51, 0x00,
162 0xa1, 0x01, 0xa5, 0x01, 0x02, 0x03, 0x38, 0x18,
163 0x20, 0x01, 0x21, 0x58, 0x20, 0x2a, 0xb8, 0x2d,
164 0x36, 0x69, 0xab, 0x30, 0x9d, 0xe3, 0x5e, 0x9b,
165 0xfb, 0x94, 0xfc, 0x1d, 0x92, 0x95, 0xaf, 0x01,
166 0x47, 0xfe, 0x4b, 0x87, 0xe5, 0xcf, 0x3f, 0x05,
167 0x0b, 0x39, 0xda, 0x17, 0x49, 0x22, 0x58, 0x20,
168 0x15, 0x1b, 0xbe, 0x08, 0x78, 0x60, 0x4d, 0x3c,
169 0x00, 0x22, 0x00, 0x03, 0x00, 0x3f, 0xf1, 0x60,
170 0xa6, 0xd8, 0xf8, 0xed, 0xce, 0x4a, 0x30, 0x5d,
171 0x1a, 0xaf, 0x80, 0xc4, 0x0a, 0xd2, 0x6f, 0x77,
172 0x38, 0x12, 0x97, 0xaa, 0xbd, 0x00, 0x00, 0x00,
173 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
174 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
175 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
176 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
177 0x00, 0x22, 0x00, 0x03, 0x90, 0x00, 0x01, 0x00,
178 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
179 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
180 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
181 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
182 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
183 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
184 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
185};
186
187static const uint8_t dummy_change_pin_wire_data[] = {
188 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0x48,
189 0xfd, 0xf9, 0xde, 0x28, 0x21, 0x99, 0xd5, 0x00,
190 0x22, 0x00, 0x04, 0x02, 0x05, 0x02, 0x01, 0x05,
191 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
192 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
193 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
194 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
195 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
196 0x00, 0x22, 0x00, 0x04, 0x90, 0x00, 0x51, 0x00,
197 0xa1, 0x01, 0xa5, 0x01, 0x02, 0x03, 0x38, 0x18,
198 0x20, 0x01, 0x21, 0x58, 0x20, 0x2a, 0xb8, 0x2d,
199 0x36, 0x69, 0xab, 0x30, 0x9d, 0xe3, 0x5e, 0x9b,
200 0xfb, 0x94, 0xfc, 0x1d, 0x92, 0x95, 0xaf, 0x01,
201 0x47, 0xfe, 0x4b, 0x87, 0xe5, 0xcf, 0x3f, 0x05,
202 0x0b, 0x39, 0xda, 0x17, 0x49, 0x22, 0x58, 0x20,
203 0x15, 0x1b, 0xbe, 0x08, 0x78, 0x60, 0x4d, 0x3c,
204 0x00, 0x22, 0x00, 0x04, 0x00, 0x3f, 0xf1, 0x60,
205 0xa6, 0xd8, 0xf8, 0xed, 0xce, 0x4a, 0x30, 0x5d,
206 0x1a, 0xaf, 0x80, 0xc4, 0x0a, 0xd2, 0x6f, 0x77,
207 0x38, 0x12, 0x97, 0xaa, 0xbd, 0x00, 0x00, 0x00,
208 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
209 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
210 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
211 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
212 0x00, 0x22, 0x00, 0x04, 0x90, 0x00, 0x01, 0x00,
213 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
214 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
215 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
216 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
217 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
218 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
219 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
220};
221
222static const uint8_t dummy_retry_wire_data[] = {
223 0xff, 0xff, 0xff, 0xff, 0x86, 0x00, 0x11, 0x7f,
224 0xaa, 0x73, 0x3e, 0x95, 0x98, 0xa8, 0x60, 0x00,
225 0x22, 0x00, 0x05, 0x02, 0x05, 0x02, 0x01, 0x05,
226 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
227 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
228 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
229 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
230 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
231 0x00, 0x22, 0x00, 0x05, 0x90, 0x00, 0x04, 0x00,
232 0xa1, 0x03, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
233 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
234 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
235 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
236 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
237 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
238 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
239};
240
241int LLVMFuzzerTestOneInput(const uint8_t *, size_t);
242size_t LLVMFuzzerCustomMutator(uint8_t *, size_t, size_t, unsigned int);
243
244static int
245unpack(const uint8_t *ptr, size_t len, struct param *p) NO_MSAN
246{
247 uint8_t **pp = (void *)&ptr;
248
249 if (unpack_string(TAG_PIN1, pp, &len, p->pin1) < 0 ||
250 unpack_string(TAG_PIN2, pp, &len, p->pin2) < 0 ||
251 unpack_blob(TAG_RESET_WIRE_DATA, pp, &len, &p->reset_wire_data) < 0 ||
252 unpack_blob(TAG_INFO_WIRE_DATA, pp, &len, &p->info_wire_data) < 0 ||
253 unpack_blob(TAG_SET_PIN_WIRE_DATA, pp, &len, &p->set_pin_wire_data) < 0 ||
254 unpack_blob(TAG_CHANGE_PIN_WIRE_DATA, pp, &len, &p->change_pin_wire_data) < 0 ||
255 unpack_blob(TAG_RETRY_WIRE_DATA, pp, &len, &p->retry_wire_data) < 0 ||
256 unpack_int(TAG_SEED, pp, &len, &p->seed) < 0)
257 return (-1);
258
259 return (0);
260}
261
262static size_t
263pack(uint8_t *ptr, size_t len, const struct param *p)
264{
265 const size_t max = len;
266
267 if (pack_string(TAG_PIN1, &ptr, &len, p->pin1) < 0 ||
268 pack_string(TAG_PIN2, &ptr, &len, p->pin2) < 0 ||
269 pack_blob(TAG_RESET_WIRE_DATA, &ptr, &len, &p->reset_wire_data) < 0 ||
270 pack_blob(TAG_INFO_WIRE_DATA, &ptr, &len, &p->info_wire_data) < 0 ||
271 pack_blob(TAG_SET_PIN_WIRE_DATA, &ptr, &len, &p->set_pin_wire_data) < 0 ||
272 pack_blob(TAG_CHANGE_PIN_WIRE_DATA, &ptr, &len, &p->change_pin_wire_data) < 0 ||
273 pack_blob(TAG_RETRY_WIRE_DATA, &ptr, &len, &p->retry_wire_data) < 0 ||
274 pack_int(TAG_SEED, &ptr, &len, p->seed) < 0)
275 return (0);
276
277 return (max - len);
278}
279
280static fido_dev_t *
281prepare_dev()
282{
283 fido_dev_t *dev;
284 fido_dev_io_t io;
285
286 io.open = dev_open;
287 io.close = dev_close;
288 io.read = dev_read;
289 io.write = dev_write;
290
291 if ((dev = fido_dev_new()) == NULL || fido_dev_set_io_functions(dev,
292 &io) != FIDO_OK || fido_dev_open(dev, "nodev") != FIDO_OK) {
293 fido_dev_free(&dev);
294 return (NULL);
295 }
296
297 return (dev);
298}
299
300static void
301dev_reset(struct param *p)
302{
303 fido_dev_t *dev;
304
305 set_wire_data(p->reset_wire_data.body, p->reset_wire_data.len);
306
307 if ((dev = prepare_dev()) == NULL) {
308 return;
309 }
310
311 fido_dev_reset(dev);
312 fido_dev_close(dev);
313 fido_dev_free(&dev);
314}
315
316static void
317dev_get_cbor_info(struct param *p)
318{
319 fido_dev_t *dev;
320 fido_cbor_info_t *ci;
321 uint64_t n;
322 uint8_t proto;
323 uint8_t major;
324 uint8_t minor;
325 uint8_t build;
326 uint8_t flags;
327
328 set_wire_data(p->info_wire_data.body, p->info_wire_data.len);
329
330 if ((dev = prepare_dev()) == NULL) {
331 return;
332 }
333
334 proto = fido_dev_protocol(dev);
335 major = fido_dev_major(dev);
336 minor = fido_dev_minor(dev);
337 build = fido_dev_build(dev);
338 flags = fido_dev_flags(dev);
339
340 consume(&proto, sizeof(proto));
341 consume(&major, sizeof(major));
342 consume(&minor, sizeof(minor));
343 consume(&build, sizeof(build));
344 consume(&flags, sizeof(flags));
345
346 if ((ci = fido_cbor_info_new()) == NULL) {
347 fido_dev_close(dev);
348 fido_dev_free(&dev);
349 return;
350 }
351
352 fido_dev_get_cbor_info(dev, ci);
353 fido_dev_close(dev);
354 fido_dev_free(&dev);
355
356 for (size_t i = 0; i < fido_cbor_info_versions_len(ci); i++) {
357 char * const *sa = fido_cbor_info_versions_ptr(ci);
358 consume(sa[i], strlen(sa[i]));
359 }
360 for (size_t i = 0; i < fido_cbor_info_extensions_len(ci); i++) {
361 char * const *sa = fido_cbor_info_extensions_ptr(ci);
362 consume(sa[i], strlen(sa[i]));
363 }
364
365 for (size_t i = 0; i < fido_cbor_info_options_len(ci); i++) {
366 char * const *sa = fido_cbor_info_options_name_ptr(ci);
367 const bool *va = fido_cbor_info_options_value_ptr(ci);
368 consume(sa[i], strlen(sa[i]));
369 consume(&va[i], sizeof(va[i]));
370 }
371
372 n = fido_cbor_info_maxmsgsiz(ci);
373 consume(&n, sizeof(n));
374
375 consume(fido_cbor_info_aaguid_ptr(ci), fido_cbor_info_aaguid_len(ci));
376 consume(fido_cbor_info_protocols_ptr(ci),
377 fido_cbor_info_protocols_len(ci));
378
379 fido_cbor_info_free(&ci);
380}
381
382static void
383dev_set_pin(struct param *p)
384{
385 fido_dev_t *dev;
386
387 set_wire_data(p->set_pin_wire_data.body, p->set_pin_wire_data.len);
388
389 if ((dev = prepare_dev()) == NULL) {
390 return;
391 }
392
393 fido_dev_set_pin(dev, p->pin1, NULL);
394 fido_dev_close(dev);
395 fido_dev_free(&dev);
396}
397
398static void
399dev_change_pin(struct param *p)
400{
401 fido_dev_t *dev;
402
403 set_wire_data(p->change_pin_wire_data.body, p->change_pin_wire_data.len);
404
405 if ((dev = prepare_dev()) == NULL) {
406 return;
407 }
408
409 fido_dev_set_pin(dev, p->pin2, p->pin1);
410 fido_dev_close(dev);
411 fido_dev_free(&dev);
412}
413
414static void
415dev_get_retry_count(struct param *p)
416{
417 fido_dev_t *dev;
418 int n;
419
420 set_wire_data(p->retry_wire_data.body, p->retry_wire_data.len);
421
422 if ((dev = prepare_dev()) == NULL) {
423 return;
424 }
425
426 fido_dev_get_retry_count(dev, &n);
427 consume(&n, sizeof(n));
428 fido_dev_close(dev);
429 fido_dev_free(&dev);
430}
431
432int
433LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
434{
435 struct param p;
436
437 memset(&p, 0, sizeof(p));
438
439 if (unpack(data, size, &p) < 0)
440 return (0);
441
442 srandom((unsigned int)p.seed);
443
444 fido_init(0);
445
446 dev_reset(&p);
447 dev_get_cbor_info(&p);
448 dev_set_pin(&p);
449 dev_change_pin(&p);
450 dev_get_retry_count(&p);
451
452 return (0);
453}
454
455static size_t
456pack_dummy(uint8_t *ptr, size_t len)
457{
458 struct param dummy;
459 uint8_t blob[16384];
460 size_t blob_len;
461
462 memset(&dummy, 0, sizeof(dummy));
463
464 strlcpy(dummy.pin1, dummy_pin1, sizeof(dummy.pin1));
465 strlcpy(dummy.pin2, dummy_pin2, sizeof(dummy.pin2));
466
467 dummy.reset_wire_data.len = sizeof(dummy_reset_wire_data);
468 dummy.info_wire_data.len = sizeof(dummy_info_wire_data);
469 dummy.set_pin_wire_data.len = sizeof(dummy_set_pin_wire_data);
470 dummy.change_pin_wire_data.len = sizeof(dummy_change_pin_wire_data);
471 dummy.retry_wire_data.len = sizeof(dummy_retry_wire_data);
472
473 memcpy(&dummy.reset_wire_data.body, &dummy_reset_wire_data,
474 dummy.reset_wire_data.len);
475 memcpy(&dummy.info_wire_data.body, &dummy_info_wire_data,
476 dummy.info_wire_data.len);
477 memcpy(&dummy.set_pin_wire_data.body, &dummy_set_pin_wire_data,
478 dummy.set_pin_wire_data.len);
479 memcpy(&dummy.change_pin_wire_data.body, &dummy_change_pin_wire_data,
480 dummy.change_pin_wire_data.len);
481 memcpy(&dummy.retry_wire_data.body, &dummy_retry_wire_data,
482 dummy.retry_wire_data.len);
483
484 blob_len = pack(blob, sizeof(blob), &dummy);
485 assert(blob_len != 0);
486
487 if (blob_len > len) {
488 memcpy(ptr, blob, len);
489 return (len);
490 }
491
492 memcpy(ptr, blob, blob_len);
493
494 return (blob_len);
495}
496
497size_t
498LLVMFuzzerCustomMutator(uint8_t *data, size_t size, size_t maxsize,
499 unsigned int seed)
500{
501 struct param p;
502 uint8_t blob[16384];
503 size_t blob_len;
504
505 memset(&p, 0, sizeof(p));
506
507 if (unpack(data, size, &p) < 0)
508 return (pack_dummy(data, maxsize));
509
510 p.seed = (int)seed;
511
512 mutate_string(p.pin1);
513 mutate_string(p.pin2);
514
515 mutate_blob(&p.reset_wire_data);
516 mutate_blob(&p.info_wire_data);
517 mutate_blob(&p.set_pin_wire_data);
518 mutate_blob(&p.change_pin_wire_data);
519 mutate_blob(&p.retry_wire_data);
520
521 blob_len = pack(blob, sizeof(blob), &p);
522
523 if (blob_len == 0 || blob_len > maxsize)
524 return (0);
525
526 memcpy(data, blob, blob_len);
527
528 return (blob_len);
529}
diff --git a/fuzz/harnesses/assert b/fuzz/harnesses/assert
new file mode 100755
index 0000000..55cd889
--- /dev/null
+++ b/fuzz/harnesses/assert
@@ -0,0 +1,32 @@
1#!/bin/bash -u
2# Copyright (c) 2019 Yubico AB. All rights reserved.
3# Use of this source code is governed by a BSD-style
4# license that can be found in the LICENSE file.
5
6T=assert
7
8sync() {
9 mkdir ${T}
10 ssh "${REMOTE}" "cd ${T}/afl-out && tar -cf- queue ../pubkey" | \
11 tar -C ${T} -xf-
12}
13
14run() {
15 find ${T}/queue -type f | while read f; do
16 cat "${f}" | LD_PRELOAD=${PRELOAD} build/examples/assert \
17 ${T}/pubkey nodev 2>/dev/null 1>&2
18 done
19}
20
21case "$1" in
22sync)
23 sync
24 ;;
25run)
26 run
27 exit 0
28 ;;
29*)
30 echo unknown command "$1"
31 exit 1
32esac
diff --git a/fuzz/harnesses/assert-rsa-h-p b/fuzz/harnesses/assert-rsa-h-p
new file mode 100755
index 0000000..8eb9ea6
--- /dev/null
+++ b/fuzz/harnesses/assert-rsa-h-p
@@ -0,0 +1,33 @@
1#!/bin/bash -u
2# Copyright (c) 2019 Yubico AB. All rights reserved.
3# Use of this source code is governed by a BSD-style
4# license that can be found in the LICENSE file.
5
6T=assert-rsa-h-p
7
8sync() {
9 mkdir ${T}
10 ssh "${REMOTE}" "cd ${T}/afl-out && tar -cf- queue ../pubkey ../hmac-salt" | \
11 tar -C ${T} -xf-
12}
13
14run() {
15 find ${T}/queue -type f | while read f; do
16 cat "${f}" | LD_PRELOAD=${PRELOAD} build/examples/assert \
17 -t rsa -h ${T}/hmac-out -s ${T}/hmac-salt \
18 -p ${T}/pubkey nodev 2>/dev/null 1>&2
19 done
20}
21
22case "$1" in
23sync)
24 sync
25 ;;
26run)
27 run
28 exit 0
29 ;;
30*)
31 echo unknown command "$1"
32 exit 1
33esac
diff --git a/fuzz/harnesses/assert-u2f b/fuzz/harnesses/assert-u2f
new file mode 100755
index 0000000..257d1d6
--- /dev/null
+++ b/fuzz/harnesses/assert-u2f
@@ -0,0 +1,32 @@
1#!/bin/bash -u
2# Copyright (c) 2019 Yubico AB. All rights reserved.
3# Use of this source code is governed by a BSD-style
4# license that can be found in the LICENSE file.
5
6T=assert-u2f
7
8sync() {
9 mkdir ${T}
10 ssh "${REMOTE}" "cd ${T}/afl-out && tar -cf- queue ../cred_id ../pubkey" | \
11 tar -C ${T} -xf-
12}
13
14run() {
15 find ${T}/queue -type f | while read f; do
16 cat "${f}" | LD_PRELOAD=${PRELOAD} build/examples/assert -up \
17 -a ${T}/cred_id ${T}/pubkey nodev 2>/dev/null 1>&2
18 done
19}
20
21case "$1" in
22sync)
23 sync
24 ;;
25run)
26 run
27 exit 0
28 ;;
29*)
30 echo unknown command "$1"
31 exit 1
32esac
diff --git a/fuzz/harnesses/cred b/fuzz/harnesses/cred
new file mode 100755
index 0000000..71ee845
--- /dev/null
+++ b/fuzz/harnesses/cred
@@ -0,0 +1,31 @@
1#!/bin/bash -u
2# Copyright (c) 2019 Yubico AB. All rights reserved.
3# Use of this source code is governed by a BSD-style
4# license that can be found in the LICENSE file.
5
6T=cred
7
8sync() {
9 mkdir ${T}
10 ssh "${REMOTE}" "cd ${T}/afl-out && tar -cf- queue" | tar -C ${T} -xf-
11}
12
13run() {
14 find ${T}/queue -type f | while read f; do
15 cat "${f}" | LD_PRELOAD=${PRELOAD} build/examples/cred \
16 -k ${T}/pubkey -i ${T}/cred_id nodev 2>/dev/null 1>&2
17 done
18}
19
20case "$1" in
21sync)
22 sync
23 ;;
24run)
25 run
26 exit 0
27 ;;
28*)
29 echo unknown command "$1"
30 exit 1
31esac
diff --git a/fuzz/harnesses/cred-rsa-h-p b/fuzz/harnesses/cred-rsa-h-p
new file mode 100755
index 0000000..bb14c23
--- /dev/null
+++ b/fuzz/harnesses/cred-rsa-h-p
@@ -0,0 +1,32 @@
1#!/bin/bash -u
2# Copyright (c) 2019 Yubico AB. All rights reserved.
3# Use of this source code is governed by a BSD-style
4# license that can be found in the LICENSE file.
5
6T=cred-rsa-h-p
7
8sync() {
9 mkdir ${T}
10 ssh "${REMOTE}" "cd ${T}/afl-out && tar -cf- queue" | tar -C ${T} -xf-
11}
12
13run() {
14 find ${T}/queue -type f | while read f; do
15 cat "${f}" | LD_PRELOAD=${PRELOAD} build/examples/cred \
16 -t rsa -r -k ${T}/pubkey -i ${T}/cred_id -h nodev \
17 2>/dev/null 1>&2
18 done
19}
20
21case "$1" in
22sync)
23 sync
24 ;;
25run)
26 run
27 exit 0
28 ;;
29*)
30 echo unknown command "$1"
31 exit 1
32esac
diff --git a/fuzz/harnesses/cred-u2f b/fuzz/harnesses/cred-u2f
new file mode 100755
index 0000000..3af4393
--- /dev/null
+++ b/fuzz/harnesses/cred-u2f
@@ -0,0 +1,31 @@
1#!/bin/bash -u
2# Copyright (c) 2019 Yubico AB. All rights reserved.
3# Use of this source code is governed by a BSD-style
4# license that can be found in the LICENSE file.
5
6T=cred-u2f
7
8sync() {
9 mkdir ${T}
10 ssh "${REMOTE}" "cd ${T}/afl-out && tar -cf- queue" | tar -C ${T} -xf-
11}
12
13run() {
14 find ${T}/queue -type f | while read f; do
15 cat "${f}" | LD_PRELOAD=${PRELOAD} build/examples/cred \
16 -k ${T}/pubkey -i ${T}/cred_id -u nodev 2>/dev/null 1>&2
17 done
18}
19
20case "$1" in
21sync)
22 sync
23 ;;
24run)
25 run
26 exit 0
27 ;;
28*)
29 echo unknown command "$1"
30 exit 1
31esac
diff --git a/fuzz/harnesses/cred-u2f-exclude b/fuzz/harnesses/cred-u2f-exclude
new file mode 100755
index 0000000..3777134
--- /dev/null
+++ b/fuzz/harnesses/cred-u2f-exclude
@@ -0,0 +1,33 @@
1#!/bin/bash -u
2# Copyright (c) 2019 Yubico AB. All rights reserved.
3# Use of this source code is governed by a BSD-style
4# license that can be found in the LICENSE file.
5
6T=cred-u2f-exclude
7
8sync() {
9 mkdir ${T}
10 ssh "${REMOTE}" "cd ${T}/afl-out && tar -cf- queue ../excl_id" | \
11 tar -C ${T} -xf-
12}
13
14run() {
15 find ${T}/queue -type f | while read f; do
16 cat "${f}" | LD_PRELOAD=${PRELOAD} build/examples/cred \
17 -k ${T}/pubkey -i ${T}/cred_id -e ${T}/excl_id \
18 -u nodev 2>/dev/null 1>&2
19 done
20}
21
22case "$1" in
23sync)
24 sync
25 ;;
26run)
27 run
28 exit 0
29 ;;
30*)
31 echo unknown command "$1"
32 exit 1
33esac
diff --git a/fuzz/harnesses/fido2-assert-G b/fuzz/harnesses/fido2-assert-G
new file mode 100755
index 0000000..6671449
--- /dev/null
+++ b/fuzz/harnesses/fido2-assert-G
@@ -0,0 +1,31 @@
1#!/bin/bash -u
2# Copyright (c) 2019 Yubico AB. All rights reserved.
3# Use of this source code is governed by a BSD-style
4# license that can be found in the LICENSE file.
5
6T=fido2-assert-G
7
8sync() {
9 mkdir ${T}
10 ssh "${REMOTE}" "cd ${T}/afl-out && tar -cf- queue" | tar -C ${T} -xf-
11}
12
13run() {
14 find ${T}/queue -type f | while read f; do
15 cat "${f}" | LD_PRELOAD=${PRELOAD} build/tools/fido2-assert \
16 -G -i - nodev 2>/dev/null 1>&2
17 done
18}
19
20case "$1" in
21sync)
22 sync
23 ;;
24run)
25 run
26 exit 0
27 ;;
28*)
29 echo unknown command "$1"
30 exit 1
31esac
diff --git a/fuzz/harnesses/fido2-assert-V b/fuzz/harnesses/fido2-assert-V
new file mode 100755
index 0000000..898cb0f
--- /dev/null
+++ b/fuzz/harnesses/fido2-assert-V
@@ -0,0 +1,32 @@
1#!/bin/bash -u
2# Copyright (c) 2019 Yubico AB. All rights reserved.
3# Use of this source code is governed by a BSD-style
4# license that can be found in the LICENSE file.
5
6T=fido2-assert-V
7
8sync() {
9 mkdir ${T}
10 ssh "${REMOTE}" "cd ${T}/afl-out && tar -cf- queue ../pubkey" | \
11 tar -C ${T} -xf-
12}
13
14run() {
15 find ${T}/queue -type f | while read f; do
16 cat "${f}" | LD_PRELOAD=${PRELOAD} build/tools/fido2-assert -V \
17 pubkey es256 2>/dev/null 1>&2
18 done
19}
20
21case "$1" in
22sync)
23 sync
24 ;;
25run)
26 run
27 exit 0
28 ;;
29*)
30 echo unknown command "$1"
31 exit 1
32esac
diff --git a/fuzz/harnesses/fido2-cred-M b/fuzz/harnesses/fido2-cred-M
new file mode 100755
index 0000000..f82fbf7
--- /dev/null
+++ b/fuzz/harnesses/fido2-cred-M
@@ -0,0 +1,31 @@
1#!/bin/bash -u
2# Copyright (c) 2019 Yubico AB. All rights reserved.
3# Use of this source code is governed by a BSD-style
4# license that can be found in the LICENSE file.
5
6T=fido2-cred-M
7
8sync() {
9 mkdir ${T}
10 ssh "${REMOTE}" "cd ${T}/afl-out && tar -cf- queue" | tar -C ${T} -xf-
11}
12
13run() {
14 find ${T}/queue -type f | while read f; do
15 cat "${f}" | LD_PRELOAD=${PRELOAD} build/tools/fido2-cred -M \
16 -q -i - nodev 2>/dev/null 1>&2
17 done
18}
19
20case "$1" in
21sync)
22 sync
23 ;;
24run)
25 run
26 exit 0
27 ;;
28*)
29 echo unknown command "$1"
30 exit 1
31esac
diff --git a/fuzz/harnesses/fido2-cred-V b/fuzz/harnesses/fido2-cred-V
new file mode 100755
index 0000000..13a648f
--- /dev/null
+++ b/fuzz/harnesses/fido2-cred-V
@@ -0,0 +1,31 @@
1#!/bin/bash -u
2# Copyright (c) 2019 Yubico AB. All rights reserved.
3# Use of this source code is governed by a BSD-style
4# license that can be found in the LICENSE file.
5
6T=fido2-cred-V
7
8sync() {
9 mkdir ${T}
10 ssh "${REMOTE}" "cd ${T}/afl-out && tar -cf- queue" | tar -C ${T} -xf-
11}
12
13run() {
14 find ${T}/queue -type f | while read f; do
15 cat "${f}" | LD_PRELOAD=${PRELOAD} build/tools/fido2-cred -V \
16 -o cred 2>/dev/null 1>&2
17 done
18}
19
20case "$1" in
21sync)
22 sync
23 ;;
24run)
25 run
26 exit 0
27 ;;
28*)
29 echo unknown command "$1"
30 exit 1
31esac
diff --git a/fuzz/harnesses/fuzz_assert b/fuzz/harnesses/fuzz_assert
new file mode 100755
index 0000000..6a0baa9
--- /dev/null
+++ b/fuzz/harnesses/fuzz_assert
@@ -0,0 +1,29 @@
1#!/bin/bash -u
2# Copyright (c) 2019 Yubico AB. All rights reserved.
3# Use of this source code is governed by a BSD-style
4# license that can be found in the LICENSE file.
5
6T=fuzz_assert
7
8sync() {
9 mkdir ${T}
10 ssh "${REMOTE}" "cd ${T} && tar -cf- corpus" | tar -C ${T} -xf-
11}
12
13run() {
14 build/fuzz/fuzz_assert -max_len=17408 -runs=1 ${T}/corpus \
15 2>/dev/null 1>&2
16}
17
18case "$1" in
19sync)
20 sync
21 ;;
22run)
23 run
24 exit 0
25 ;;
26*)
27 echo unknown command "$1"
28 exit 1
29esac
diff --git a/fuzz/harnesses/fuzz_bio b/fuzz/harnesses/fuzz_bio
new file mode 100755
index 0000000..f4bab19
--- /dev/null
+++ b/fuzz/harnesses/fuzz_bio
@@ -0,0 +1,29 @@
1#!/bin/bash -u
2# Copyright (c) 2019 Yubico AB. All rights reserved.
3# Use of this source code is governed by a BSD-style
4# license that can be found in the LICENSE file.
5
6T=fuzz_bio
7
8sync() {
9 mkdir ${T}
10 ssh "${REMOTE}" "cd ${T} && tar -cf- corpus" | tar -C ${T} -xf-
11}
12
13run() {
14 build/fuzz/fuzz_bio -max_len=17408 -runs=1 ${T}/corpus \
15 2>/dev/null 1>&2
16}
17
18case "$1" in
19sync)
20 sync
21 ;;
22run)
23 run
24 exit 0
25 ;;
26*)
27 echo unknown command "$1"
28 exit 1
29esac
diff --git a/fuzz/harnesses/fuzz_cred b/fuzz/harnesses/fuzz_cred
new file mode 100755
index 0000000..8dfb168
--- /dev/null
+++ b/fuzz/harnesses/fuzz_cred
@@ -0,0 +1,28 @@
1#!/bin/bash -u
2# Copyright (c) 2019 Yubico AB. All rights reserved.
3# Use of this source code is governed by a BSD-style
4# license that can be found in the LICENSE file.
5
6T=fuzz_cred
7
8sync() {
9 mkdir ${T}
10 ssh "${REMOTE}" "cd ${T} && tar -cf- corpus" | tar -C ${T} -xf-
11}
12
13run() {
14 build/fuzz/fuzz_cred -max_len=17408 -runs=1 ${T}/corpus 2>/dev/null 1>&2
15}
16
17case "$1" in
18sync)
19 sync
20 ;;
21run)
22 run
23 exit 0
24 ;;
25*)
26 echo unknown command "$1"
27 exit 1
28esac
diff --git a/fuzz/harnesses/fuzz_credman b/fuzz/harnesses/fuzz_credman
new file mode 100755
index 0000000..7721a58
--- /dev/null
+++ b/fuzz/harnesses/fuzz_credman
@@ -0,0 +1,28 @@
1#!/bin/bash -u
2# Copyright (c) 2019 Yubico AB. All rights reserved.
3# Use of this source code is governed by a BSD-style
4# license that can be found in the LICENSE file.
5
6T=fuzz_credman
7
8sync() {
9 mkdir ${T}
10 ssh "${REMOTE}" "cd ${T} && tar -cf- corpus" | tar -C ${T} -xf-
11}
12
13run() {
14 build/fuzz/fuzz_credman -max_len=17408 -runs=1 ${T}/corpus 2>/dev/null 1>&2
15}
16
17case "$1" in
18sync)
19 sync
20 ;;
21run)
22 run
23 exit 0
24 ;;
25*)
26 echo unknown command "$1"
27 exit 1
28esac
diff --git a/fuzz/harnesses/fuzz_mgmt b/fuzz/harnesses/fuzz_mgmt
new file mode 100755
index 0000000..9224eea
--- /dev/null
+++ b/fuzz/harnesses/fuzz_mgmt
@@ -0,0 +1,29 @@
1#!/bin/bash -u
2# Copyright (c) 2019 Yubico AB. All rights reserved.
3# Use of this source code is governed by a BSD-style
4# license that can be found in the LICENSE file.
5
6T=fuzz_mgmt
7
8sync() {
9 mkdir ${T}
10 ssh "${REMOTE}" "cd ${T} && tar -cf- corpus" | tar -C ${T} -xf-
11}
12
13run() {
14 build/fuzz/fuzz_mgmt -max_len=17408 -runs=1 ${T}/corpus \
15 2>/dev/null 1>&2
16}
17
18case "$1" in
19sync)
20 sync
21 ;;
22run)
23 run
24 exit 0
25 ;;
26*)
27 echo unknown command "$1"
28 exit 1
29esac
diff --git a/fuzz/mutator_aux.c b/fuzz/mutator_aux.c
new file mode 100644
index 0000000..24aa716
--- /dev/null
+++ b/fuzz/mutator_aux.c
@@ -0,0 +1,314 @@
1/*
2 * Copyright (c) 2019 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <assert.h>
8#include <stddef.h>
9#include <stdint.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13
14#include "mutator_aux.h"
15
16size_t LLVMFuzzerMutate(uint8_t *, size_t, size_t);
17
18static uint8_t *wire_data_ptr = NULL;
19static size_t wire_data_len = 0;
20
21size_t
22xstrlen(const char *s)
23{
24 if (s == NULL)
25 return (0);
26
27 return (strlen(s));
28}
29
30void
31consume(const void *body, size_t len)
32{
33 const volatile uint8_t *ptr = body;
34 volatile uint8_t x = 0;
35
36 while (len--)
37 x ^= *ptr++;
38}
39
40int
41unpack_int(uint8_t t, uint8_t **ptr, size_t *len, int *v) NO_MSAN
42{
43 size_t l;
44
45 if (*len < sizeof(t) || **ptr != t)
46 return (-1);
47
48 *ptr += sizeof(t);
49 *len -= sizeof(t);
50
51 if (*len < sizeof(l))
52 return (-1);
53
54 memcpy(&l, *ptr, sizeof(l));
55 *ptr += sizeof(l);
56 *len -= sizeof(l);
57
58 if (l != sizeof(*v) || *len < l)
59 return (-1);
60
61 memcpy(v, *ptr, sizeof(*v));
62 *ptr += sizeof(*v);
63 *len -= sizeof(*v);
64
65 return (0);
66}
67
68int
69unpack_string(uint8_t t, uint8_t **ptr, size_t *len, char *v) NO_MSAN
70{
71 size_t l;
72
73 if (*len < sizeof(t) || **ptr != t)
74 return (-1);
75
76 *ptr += sizeof(t);
77 *len -= sizeof(t);
78
79 if (*len < sizeof(l))
80 return (-1);
81
82 memcpy(&l, *ptr, sizeof(l));
83 *ptr += sizeof(l);
84 *len -= sizeof(l);
85
86 if (*len < l || l >= MAXSTR)
87 return (-1);
88
89 memcpy(v, *ptr, l);
90 v[l] = '\0';
91
92 *ptr += l;
93 *len -= l;
94
95 return (0);
96}
97
98int
99unpack_byte(uint8_t t, uint8_t **ptr, size_t *len, uint8_t *v) NO_MSAN
100{
101 size_t l;
102
103 if (*len < sizeof(t) || **ptr != t)
104 return (-1);
105
106 *ptr += sizeof(t);
107 *len -= sizeof(t);
108
109 if (*len < sizeof(l))
110 return (-1);
111
112 memcpy(&l, *ptr, sizeof(l));
113 *ptr += sizeof(l);
114 *len -= sizeof(l);
115
116 if (l != sizeof(*v) || *len < l)
117 return (-1);
118
119 memcpy(v, *ptr, sizeof(*v));
120 *ptr += sizeof(*v);
121 *len -= sizeof(*v);
122
123 return (0);
124}
125
126int
127unpack_blob(uint8_t t, uint8_t **ptr, size_t *len, struct blob *v) NO_MSAN
128{
129 size_t l;
130
131 v->len = 0;
132
133 if (*len < sizeof(t) || **ptr != t)
134 return (-1);
135
136 *ptr += sizeof(t);
137 *len -= sizeof(t);
138
139 if (*len < sizeof(l))
140 return (-1);
141
142 memcpy(&l, *ptr, sizeof(l));
143 *ptr += sizeof(l);
144 *len -= sizeof(l);
145
146 if (*len < l || l > sizeof(v->body))
147 return (-1);
148
149 memcpy(v->body, *ptr, l);
150 *ptr += l;
151 *len -= l;
152
153 v->len = l;
154
155 return (0);
156}
157
158int
159pack_int(uint8_t t, uint8_t **ptr, size_t *len, int v) NO_MSAN
160{
161 const size_t l = sizeof(v);
162
163 if (*len < sizeof(t) + sizeof(l) + l)
164 return (-1);
165
166 (*ptr)[0] = t;
167 memcpy(&(*ptr)[sizeof(t)], &l, sizeof(l));
168 memcpy(&(*ptr)[sizeof(t) + sizeof(l)], &v, l);
169
170 *ptr += sizeof(t) + sizeof(l) + l;
171 *len -= sizeof(t) + sizeof(l) + l;
172
173 return (0);
174}
175
176int
177pack_string(uint8_t t, uint8_t **ptr, size_t *len, const char *v) NO_MSAN
178{
179 const size_t l = strlen(v);
180
181 if (*len < sizeof(t) + sizeof(l) + l)
182 return (-1);
183
184 (*ptr)[0] = t;
185 memcpy(&(*ptr)[sizeof(t)], &l, sizeof(l));
186 memcpy(&(*ptr)[sizeof(t) + sizeof(l)], v, l);
187
188 *ptr += sizeof(t) + sizeof(l) + l;
189 *len -= sizeof(t) + sizeof(l) + l;
190
191 return (0);
192}
193
194int
195pack_byte(uint8_t t, uint8_t **ptr, size_t *len, uint8_t v) NO_MSAN
196{
197 const size_t l = sizeof(v);
198
199 if (*len < sizeof(t) + sizeof(l) + l)
200 return (-1);
201
202 (*ptr)[0] = t;
203 memcpy(&(*ptr)[sizeof(t)], &l, sizeof(l));
204 memcpy(&(*ptr)[sizeof(t) + sizeof(l)], &v, l);
205
206 *ptr += sizeof(t) + sizeof(l) + l;
207 *len -= sizeof(t) + sizeof(l) + l;
208
209 return (0);
210}
211
212int
213pack_blob(uint8_t t, uint8_t **ptr, size_t *len, const struct blob *v) NO_MSAN
214{
215 const size_t l = v->len;
216
217 if (*len < sizeof(t) + sizeof(l) + l)
218 return (-1);
219
220 (*ptr)[0] = t;
221 memcpy(&(*ptr)[sizeof(t)], &l, sizeof(l));
222 memcpy(&(*ptr)[sizeof(t) + sizeof(l)], v->body, l);
223
224 *ptr += sizeof(t) + sizeof(l) + l;
225 *len -= sizeof(t) + sizeof(l) + l;
226
227 return (0);
228}
229
230void
231mutate_byte(uint8_t *b)
232{
233 LLVMFuzzerMutate(b, sizeof(*b), sizeof(*b));
234}
235
236void
237mutate_int(int *i)
238{
239 LLVMFuzzerMutate((uint8_t *)i, sizeof(*i), sizeof(*i));
240}
241
242void
243mutate_blob(struct blob *blob)
244{
245 blob->len = LLVMFuzzerMutate((uint8_t *)blob->body, blob->len,
246 sizeof(blob->body));
247}
248
249void
250mutate_string(char *s)
251{
252 size_t n;
253
254 n = LLVMFuzzerMutate((uint8_t *)s, strlen(s), MAXSTR - 1);
255 s[n] = '\0';
256}
257
258void *
259dev_open(const char *path)
260{
261 (void)path;
262
263 return ((void *)0xdeadbeef);
264}
265
266void
267dev_close(void *handle)
268{
269 assert(handle == (void *)0xdeadbeef);
270}
271
272int
273dev_read(void *handle, unsigned char *ptr, size_t len, int ms)
274{
275 size_t n;
276
277 (void)ms;
278
279 assert(handle == (void *)0xdeadbeef);
280 assert(len == 64);
281
282 if (wire_data_len < len)
283 n = wire_data_len;
284 else
285 n = len;
286
287 memcpy(ptr, wire_data_ptr, n);
288
289 wire_data_ptr += n;
290 wire_data_len -= n;
291
292 return ((int)n);
293}
294
295int
296dev_write(void *handle, const unsigned char *ptr, size_t len)
297{
298 assert(handle == (void *)0xdeadbeef);
299 assert(len == 64 + 1);
300
301 consume(ptr, len);
302
303 if (uniform_random(400) < 1)
304 return (-1);
305
306 return ((int)len);
307}
308
309void
310set_wire_data(uint8_t *ptr, size_t len)
311{
312 wire_data_ptr = ptr;
313 wire_data_len = len;
314}
diff --git a/fuzz/mutator_aux.h b/fuzz/mutator_aux.h
new file mode 100644
index 0000000..687f130
--- /dev/null
+++ b/fuzz/mutator_aux.h
@@ -0,0 +1,65 @@
1/*
2 * Copyright (c) 2019 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#ifndef _MUTATOR_AUX_H
8#define _MUTATOR_AUX_H
9
10/*
11 * As of LLVM 7.0.1, MSAN support in libFuzzer was still experimental.
12 * We therefore have to be careful when using our custom mutator, or
13 * MSAN will flag uninitialised reads on memory populated by libFuzzer.
14 * Since there is no way to suppress MSAN without regenerating object
15 * code (in which case you might as well rebuild libFuzzer with MSAN),
16 * we adjust our mutator to make it less accurate while allowing
17 * fuzzing to proceed.
18 */
19
20#if defined(__has_feature)
21# if __has_feature(memory_sanitizer)
22# define NO_MSAN __attribute__((no_sanitize("memory")))
23# define WITH_MSAN 1
24# endif
25#endif
26
27#if !defined(WITH_MSAN)
28# define NO_MSAN
29#endif
30
31#define MAXSTR 1024
32#define MAXBLOB 3072
33
34struct blob {
35 uint8_t body[MAXBLOB];
36 size_t len;
37};
38
39size_t xstrlen(const char *);
40void consume(const void *, size_t);
41
42int unpack_blob(uint8_t, uint8_t **, size_t *, struct blob *);
43int unpack_byte(uint8_t, uint8_t **, size_t *, uint8_t *);
44int unpack_int(uint8_t, uint8_t **, size_t *, int *);
45int unpack_string(uint8_t, uint8_t **, size_t *, char *);
46
47int pack_blob(uint8_t, uint8_t **, size_t *, const struct blob *);
48int pack_byte(uint8_t, uint8_t **, size_t *, uint8_t);
49int pack_int(uint8_t, uint8_t **, size_t *, int);
50int pack_string(uint8_t, uint8_t **, size_t *, const char *);
51
52void mutate_byte(uint8_t *);
53void mutate_int(int *);
54void mutate_blob(struct blob *);
55void mutate_string(char *);
56
57void * dev_open(const char *);
58void dev_close(void *);
59void set_wire_data(uint8_t *, size_t);
60int dev_read(void *, unsigned char *, size_t, int);
61int dev_write(void *, const unsigned char *, size_t);
62
63uint32_t uniform_random(uint32_t);
64
65#endif /* !_MUTATOR_AUX_H */
diff --git a/fuzz/preload-fuzz.c b/fuzz/preload-fuzz.c
new file mode 100644
index 0000000..efcb8c6
--- /dev/null
+++ b/fuzz/preload-fuzz.c
@@ -0,0 +1,104 @@
1/*
2 * Copyright (c) 2019 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7/*
8 * cc -fPIC -D_GNU_SOURCE -shared -o preload-fuzz.so preload-fuzz.c
9 * LD_PRELOAD=$(realpath preload-fuzz.so)
10 */
11
12#include <sys/types.h>
13#include <sys/stat.h>
14
15#include <dlfcn.h>
16#include <err.h>
17#include <errno.h>
18#include <fcntl.h>
19#include <limits.h>
20#include <stdarg.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <unistd.h>
25
26#define FUZZ_DEV_PREFIX "nodev"
27
28static int fd_fuzz = -1;
29static int (*open_f)(const char *, int, mode_t);
30static int (*close_f)(int);
31static ssize_t (*write_f)(int, const void *, size_t);
32
33int
34open(const char *path, int flags, ...)
35{
36 va_list ap;
37 mode_t mode;
38
39 va_start(ap, flags);
40 mode = va_arg(ap, mode_t);
41 va_end(ap);
42
43 if (open_f == NULL) {
44 open_f = dlsym(RTLD_NEXT, "open");
45 if (open_f == NULL) {
46 warnx("%s: dlsym", __func__);
47 errno = EACCES;
48 return (-1);
49 }
50 }
51
52 if (strncmp(path, FUZZ_DEV_PREFIX, strlen(FUZZ_DEV_PREFIX)) != 0)
53 return (open_f(path, flags, mode));
54
55 if (fd_fuzz != -1) {
56 warnx("%s: fd_fuzz != -1", __func__);
57 errno = EACCES;
58 return (-1);
59 }
60
61 if ((fd_fuzz = dup(STDIN_FILENO)) < 0) {
62 warn("%s: dup", __func__);
63 errno = EACCES;
64 return (-1);
65 }
66
67 return (fd_fuzz);
68}
69
70int
71close(int fd)
72{
73 if (close_f == NULL) {
74 close_f = dlsym(RTLD_NEXT, "close");
75 if (close_f == NULL) {
76 warnx("%s: dlsym", __func__);
77 errno = EACCES;
78 return (-1);
79 }
80 }
81
82 if (fd == fd_fuzz)
83 fd_fuzz = -1;
84
85 return (close_f(fd));
86}
87
88ssize_t
89write(int fd, const void *buf, size_t nbytes)
90{
91 if (write_f == NULL) {
92 write_f = dlsym(RTLD_NEXT, "write");
93 if (write_f == NULL) {
94 warnx("%s: dlsym", __func__);
95 errno = EBADF;
96 return (-1);
97 }
98 }
99
100 if (fd != fd_fuzz)
101 return (write_f(fd, buf, nbytes));
102
103 return (nbytes);
104}
diff --git a/fuzz/preload-snoop.c b/fuzz/preload-snoop.c
new file mode 100644
index 0000000..373acc5
--- /dev/null
+++ b/fuzz/preload-snoop.c
@@ -0,0 +1,217 @@
1/*
2 * Copyright (c) 2019 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7/*
8 * cc -fPIC -D_GNU_SOURCE -shared -o preload-snoop.so preload-snoop.c
9 * LD_PRELOAD=$(realpath preload-snoop.so)
10 */
11
12#include <sys/types.h>
13#include <sys/stat.h>
14
15#include <dlfcn.h>
16#include <err.h>
17#include <errno.h>
18#include <fcntl.h>
19#include <limits.h>
20#include <stdarg.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <unistd.h>
25
26#define SNOOP_DEV_PREFIX "/dev/hidraw"
27
28struct fd_tuple {
29 int snoop_in;
30 int snoop_out;
31 int real_dev;
32};
33
34static struct fd_tuple *fd_tuple;
35static int (*open_f)(const char *, int, mode_t);
36static int (*close_f)(int);
37static ssize_t (*read_f)(int, void *, size_t);
38static ssize_t (*write_f)(int, const void *, size_t);
39
40static int
41get_fd(const char *hid_path, const char *suffix)
42{
43 char *s = NULL;
44 char path[PATH_MAX];
45 int fd;
46 int r;
47
48 if ((s = strdup(hid_path)) == NULL) {
49 warnx("%s: strdup", __func__);
50 return (-1);
51 }
52
53 for (size_t i = 0; i < strlen(s); i++)
54 if (s[i] == '/')
55 s[i] = '_';
56
57 if ((r = snprintf(path, sizeof(path), "%s-%s", s, suffix)) < 0 ||
58 (size_t)r >= sizeof(path)) {
59 warnx("%s: snprintf", __func__);
60 free(s);
61 return (-1);
62 }
63
64 free(s);
65 s = NULL;
66
67 if ((fd = open_f(path, O_CREAT | O_WRONLY, 0644)) < 0) {
68 warn("%s: open", __func__);
69 return (-1);
70 }
71
72 return (fd);
73}
74
75int
76open(const char *path, int flags, ...)
77{
78 va_list ap;
79 mode_t mode;
80
81 va_start(ap, flags);
82 mode = va_arg(ap, mode_t);
83 va_end(ap);
84
85 if (open_f == NULL) {
86 open_f = dlsym(RTLD_NEXT, "open");
87 if (open_f == NULL) {
88 warnx("%s: dlsym", __func__);
89 errno = EACCES;
90 return (-1);
91 }
92 }
93
94 if (strncmp(path, SNOOP_DEV_PREFIX, strlen(SNOOP_DEV_PREFIX)) != 0)
95 return (open_f(path, flags, mode));
96
97 if (fd_tuple != NULL) {
98 warnx("%s: fd_tuple != NULL", __func__);
99 errno = EACCES;
100 return (-1);
101 }
102
103 if ((fd_tuple = calloc(1, sizeof(*fd_tuple))) == NULL) {
104 warn("%s: calloc", __func__);
105 errno = ENOMEM;
106 return (-1);
107 }
108
109 fd_tuple->snoop_in = -1;
110 fd_tuple->snoop_out = -1;
111 fd_tuple->real_dev = -1;
112
113 if ((fd_tuple->snoop_in = get_fd(path, "in")) < 0 ||
114 (fd_tuple->snoop_out = get_fd(path, "out")) < 0 ||
115 (fd_tuple->real_dev = open_f(path, flags, mode)) < 0) {
116 warn("%s: get_fd/open", __func__);
117 goto fail;
118 }
119
120 return (fd_tuple->real_dev);
121fail:
122 if (fd_tuple->snoop_in != -1)
123 close(fd_tuple->snoop_in);
124 if (fd_tuple->snoop_out != -1)
125 close(fd_tuple->snoop_out);
126 if (fd_tuple->real_dev != -1)
127 close(fd_tuple->real_dev);
128
129 free(fd_tuple);
130 fd_tuple = NULL;
131
132 errno = EACCES;
133
134 return (-1);
135}
136
137int
138close(int fd)
139{
140 if (close_f == NULL) {
141 close_f = dlsym(RTLD_NEXT, "close");
142 if (close_f == NULL) {
143 warnx("%s: dlsym", __func__);
144 errno = EBADF;
145 return (-1);
146 }
147 }
148
149 if (fd_tuple == NULL || fd_tuple->real_dev != fd)
150 return (close_f(fd));
151
152 close_f(fd_tuple->snoop_in);
153 close_f(fd_tuple->snoop_out);
154 close_f(fd_tuple->real_dev);
155
156 free(fd_tuple);
157 fd_tuple = NULL;
158
159 return (0);
160}
161
162ssize_t
163read(int fd, void *buf, size_t nbytes)
164{
165 ssize_t n;
166
167 if (read_f == NULL) {
168 read_f = dlsym(RTLD_NEXT, "read");
169 if (read_f == NULL) {
170 warnx("%s: dlsym", __func__);
171 errno = EBADF;
172 return (-1);
173 }
174 }
175
176 if (write_f == NULL) {
177 write_f = dlsym(RTLD_NEXT, "write");
178 if (write_f == NULL) {
179 warnx("%s: dlsym", __func__);
180 errno = EBADF;
181 return (-1);
182 }
183 }
184
185 if (fd_tuple == NULL || fd_tuple->real_dev != fd)
186 return (read_f(fd, buf, nbytes));
187
188 if ((n = read_f(fd, buf, nbytes)) < 0 ||
189 write_f(fd_tuple->snoop_in, buf, n) != n)
190 return (-1);
191
192 return (n);
193}
194
195ssize_t
196write(int fd, const void *buf, size_t nbytes)
197{
198 ssize_t n;
199
200 if (write_f == NULL) {
201 write_f = dlsym(RTLD_NEXT, "write");
202 if (write_f == NULL) {
203 warnx("%s: dlsym", __func__);
204 errno = EBADF;
205 return (-1);
206 }
207 }
208
209 if (fd_tuple == NULL || fd_tuple->real_dev != fd)
210 return (write_f(fd, buf, nbytes));
211
212 if ((n = write_f(fd, buf, nbytes)) < 0 ||
213 write_f(fd_tuple->snoop_out, buf, n) != n)
214 return (-1);
215
216 return (n);
217}
diff --git a/fuzz/report b/fuzz/report
new file mode 100755
index 0000000..bebb0ca
--- /dev/null
+++ b/fuzz/report
@@ -0,0 +1,80 @@
1#!/bin/bash -e
2# Copyright (c) 2019 Yubico AB. All rights reserved.
3# Use of this source code is governed by a BSD-style
4# license that can be found in the LICENSE file.
5#
6# XXX This should really be a Makefile.
7
8T=""
9#T+=" harnesses/assert"
10#T+=" harnesses/assert-rsa-h-p"
11#T+=" harnesses/assert-u2f"
12#T+=" harnesses/cred"
13#T+=" harnesses/cred-rsa-h-p"
14#T+=" harnesses/cred-u2f"
15#T+=" harnesses/cred-u2f-exclude"
16#T+=" harnesses/fido2-assert-G"
17#T+=" harnesses/fido2-assert-V"
18#T+=" harnesses/fido2-cred-M"
19#T+=" harnesses/fido2-cred-V"
20T+=" harnesses/fuzz_assert"
21T+=" harnesses/fuzz_bio"
22T+=" harnesses/fuzz_cred"
23T+=" harnesses/fuzz_credman"
24T+=" harnesses/fuzz_mgmt"
25
26clean() {
27 echo cleaning
28 rm -rf obj
29 mkdir obj
30}
31
32build() {
33 echo building
34 mkdir obj/build
35 (cd obj/build && cmake -DFUZZ=1 -DLIBFUZZER=1 -DCMAKE_C_COMPILER=clang \
36 -DCOVERAGE=1 -DCMAKE_BUILD_TYPE=Debug ../../..) 2>/dev/null 1>&2
37 make -C obj/build 2>/dev/null 1>&2
38 cc -fPIC -D_GNU_SOURCE -shared -o obj/preload-fuzz.so preload-fuzz.c
39}
40
41sync() {
42 if [ -n "${REMOTE}" ]; then
43 for t in ${T}; do
44 echo syncing ${t}
45 (cd obj && REMOTE="${REMOTE}" ../${t} sync)
46 done
47 else
48 tar -C obj -zxf corpus.tgz
49 fi
50}
51
52run() {
53 export LLVM_PROFILE_FILE="profraw/%h-%p.profraw"
54 export PRELOAD=$(realpath obj/preload-fuzz.so)
55
56 for t in ${T}; do
57 echo running ${t}
58 (cd obj && ../${t} run)
59 done
60}
61
62merge() {
63 echo merging
64 (cd obj && \
65 llvm-profdata merge -sparse profraw/*.profraw \
66 -o libfido2.profdata &&
67 llvm-cov show -format=html -tab-size=8 build/src/libfido2.so \
68 -instr-profile=libfido2.profdata > report.html &&
69 llvm-cov report -use-color=false build/src/libfido2.so \
70 -instr-profile=libfido2.profdata > summary.txt &&
71 llvm-cov report -use-color=false -show-functions \
72 -instr-profile=libfido2.profdata build/src/libfido2.so \
73 ../../src/*.[ch] > functions.txt)
74}
75
76clean
77build
78sync
79run
80merge
diff --git a/fuzz/summary.txt b/fuzz/summary.txt
new file mode 100644
index 0000000..e494865
--- /dev/null
+++ b/fuzz/summary.txt
@@ -0,0 +1,39 @@
1Filename Regions Missed Regions Cover Functions Missed Functions Executed Lines Missed Lines Cover
2--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3fuzz/uniform_random.c 7 1 85.71% 1 0 100.00% 23 1 95.65%
4fuzz/wrap.c 4 0 100.00% 1 0 100.00% 7 0 100.00%
5openbsd-compat/explicit_bzero.c 4 0 100.00% 1 0 100.00% 12 0 100.00%
6openbsd-compat/recallocarray.c 41 7 82.93% 1 0 100.00% 49 7 85.71%
7openbsd-compat/timingsafe_bcmp.c 4 0 100.00% 1 0 100.00% 8 0 100.00%
8src/aes256.c 56 0 100.00% 2 0 100.00% 82 0 100.00%
9src/assert.c 569 29 94.90% 53 1 98.11% 901 60 93.34%
10src/authkey.c 45 0 100.00% 5 0 100.00% 75 0 100.00%
11src/bio.c 422 21 95.02% 49 2 95.92% 661 25 96.22%
12src/blob.c 39 1 97.44% 7 0 100.00% 73 4 94.52%
13src/buf.c 8 1 87.50% 2 0 100.00% 20 1 95.00%
14src/cbor.c 844 31 96.33% 51 1 98.04% 1319 47 96.44%
15src/cred.c 532 35 93.42% 54 1 98.15% 850 55 93.53%
16src/credman.c 381 18 95.28% 38 0 100.00% 589 15 97.45%
17src/dev.c 131 22 83.21% 19 1 94.74% 183 30 83.61%
18src/ecdh.c 68 0 100.00% 2 0 100.00% 104 0 100.00%
19src/eddsa.c 54 4 92.59% 8 0 100.00% 79 2 97.47%
20src/err.c 108 108 0.00% 1 1 0.00% 112 112 0.00%
21src/es256.c 273 4 98.53% 16 0 100.00% 372 13 96.51%
22src/hid.c 16 16 0.00% 8 8 0.00% 38 38 0.00%
23src/hid_linux.c 166 166 0.00% 12 12 0.00% 287 287 0.00%
24src/info.c 148 1 99.32% 31 0 100.00% 305 0 100.00%
25src/io.c 113 6 94.69% 7 0 100.00% 201 13 93.53%
26src/iso7816.c 18 1 94.44% 5 0 100.00% 47 0 100.00%
27src/log.c 16 10 37.50% 3 1 66.67% 34 23 32.35%
28src/pin.c 250 0 100.00% 16 0 100.00% 364 0 100.00%
29src/reset.c 20 0 100.00% 3 0 100.00% 23 0 100.00%
30src/rs256.c 102 6 94.12% 8 0 100.00% 140 9 93.57%
31src/u2f.c 436 11 97.48% 13 0 100.00% 686 22 96.79%
32
33Files which contain no functions:
34src/extern.h 0 0 - 0 0 - 0 0 -
35src/fido.h 0 0 - 0 0 - 0 0 -
36src/fido/err.h 0 0 - 0 0 - 0 0 -
37src/fido/param.h 0 0 - 0 0 - 0 0 -
38--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
39TOTAL 4875 499 89.76% 418 28 93.30% 7644 764 90.01%
diff --git a/fuzz/uniform_random.c b/fuzz/uniform_random.c
new file mode 100644
index 0000000..7711d69
--- /dev/null
+++ b/fuzz/uniform_random.c
@@ -0,0 +1,56 @@
1/*
2 * Copyright (c) 2008, Damien Miller <djm@openbsd.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include <stdint.h>
18#include <stdlib.h>
19
20uint32_t uniform_random(uint32_t);
21
22/*
23 * Calculate a uniformly distributed random number less than upper_bound
24 * avoiding "modulo bias".
25 *
26 * Uniformity is achieved by generating new random numbers until the one
27 * returned is outside the range [0, 2**32 % upper_bound). This
28 * guarantees the selected random number will be inside
29 * [2**32 % upper_bound, 2**32) which maps back to [0, upper_bound)
30 * after reduction modulo upper_bound.
31 */
32uint32_t
33uniform_random(uint32_t upper_bound)
34{
35 uint32_t r, min;
36
37 if (upper_bound < 2)
38 return 0;
39
40 /* 2**32 % x == (2**32 - x) % x */
41 min = -upper_bound % upper_bound;
42
43 /*
44 * This could theoretically loop forever but each retry has
45 * p > 0.5 (worst case, usually far better) of selecting a
46 * number inside the range we need, so it should rarely need
47 * to re-roll.
48 */
49 for (;;) {
50 r = (uint32_t)random();
51 if (r >= min)
52 break;
53 }
54
55 return r % upper_bound;
56}
diff --git a/fuzz/wrap.c b/fuzz/wrap.c
new file mode 100644
index 0000000..8ff7ee7
--- /dev/null
+++ b/fuzz/wrap.c
@@ -0,0 +1,419 @@
1/*
2 * Copyright (c) 2019 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <openssl/bn.h>
8#include <openssl/evp.h>
9#include <openssl/sha.h>
10
11#include <cbor.h>
12#include <fido.h>
13
14#include <stdbool.h>
15#include <stdint.h>
16#include <stdio.h>
17#include <stdlib.h>
18
19#include "mutator_aux.h"
20
21/*
22 * Build wrappers around functions of interest, and have them fail
23 * in a pseudo-random manner.
24 */
25
26#define WRAP(type, name, args, retval, param, prob) \
27extern type __wrap_##name args; \
28extern type __real_##name args; \
29type __wrap_##name args { \
30 if (uniform_random(400) < (prob)) { \
31 return (retval); \
32 } \
33 \
34 return (__real_##name param); \
35}
36
37WRAP(void *,
38 malloc,
39 (size_t size),
40 NULL,
41 (size),
42 1
43)
44
45WRAP(void *,
46 calloc,
47 (size_t nmemb, size_t size),
48 NULL,
49 (nmemb, size),
50 1
51)
52
53WRAP(char *,
54 strdup,
55 (const char *s),
56 NULL,
57 (s),
58 1
59)
60
61WRAP(EVP_CIPHER_CTX *,
62 EVP_CIPHER_CTX_new,
63 (void),
64 NULL,
65 (),
66 1
67)
68
69WRAP(int, EVP_EncryptInit_ex,
70 (EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, ENGINE *impl,
71 const unsigned char *key, const unsigned char *iv),
72 0,
73 (ctx, type, impl, key, iv),
74 1
75)
76
77WRAP(int,
78 EVP_CIPHER_CTX_set_padding,
79 (EVP_CIPHER_CTX *x, int padding),
80 0,
81 (x, padding),
82 1
83)
84
85WRAP(int,
86 EVP_EncryptUpdate,
87 (EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl,
88 const unsigned char *in, int inl),
89 0,
90 (ctx, out, outl, in, inl),
91 1
92)
93
94WRAP(int,
95 EVP_DecryptInit_ex,
96 (EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, ENGINE *impl,
97 const unsigned char *key, const unsigned char *iv),
98 0,
99 (ctx, type, impl, key, iv),
100 1
101)
102
103WRAP(int,
104 EVP_DecryptUpdate,
105 (EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl,
106 const unsigned char *in, int inl),
107 0,
108 (ctx, out, outl, in, inl),
109 1
110)
111
112WRAP(int,
113 SHA256_Init,
114 (SHA256_CTX *c),
115 0,
116 (c),
117 1
118)
119
120WRAP(int,
121 SHA256_Update,
122 (SHA256_CTX *c, const void *data, size_t len),
123 0,
124 (c, data, len),
125 1
126)
127
128WRAP(int,
129 SHA256_Final,
130 (unsigned char *md, SHA256_CTX *c),
131 0,
132 (md, c),
133 1
134)
135
136WRAP(RSA *,
137 EVP_PKEY_get0_RSA,
138 (EVP_PKEY *pkey),
139 NULL,
140 (pkey),
141 1
142)
143
144WRAP(EVP_MD_CTX *,
145 EVP_MD_CTX_new,
146 (void),
147 NULL,
148 (),
149 1
150)
151
152WRAP(int,
153 EVP_DigestVerifyInit,
154 (EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx, const EVP_MD *type, ENGINE *e,
155 EVP_PKEY *pkey),
156 0,
157 (ctx, pctx, type, e, pkey),
158 1
159)
160
161WRAP(BIGNUM *,
162 BN_bin2bn,
163 (const unsigned char *s, int len, BIGNUM *ret),
164 NULL,
165 (s, len, ret),
166 1
167)
168
169WRAP(BIGNUM *,
170 BN_CTX_get,
171 (BN_CTX *ctx),
172 NULL,
173 (ctx),
174 1
175)
176
177WRAP(BN_CTX *,
178 BN_CTX_new,
179 (void),
180 NULL,
181 (),
182 1
183)
184
185WRAP(BIGNUM *,
186 BN_new,
187 (void),
188 NULL,
189 (),
190 1
191)
192
193WRAP(int,
194 RSA_set0_key,
195 (RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d),
196 0,
197 (r, n, e, d),
198 1
199)
200
201WRAP(EC_KEY *,
202 EC_KEY_new_by_curve_name,
203 (int nid),
204 NULL,
205 (nid),
206 1
207)
208
209WRAP(const EC_GROUP *,
210 EC_KEY_get0_group,
211 (const EC_KEY *key),
212 NULL,
213 (key),
214 1
215)
216
217WRAP(EC_POINT *,
218 EC_POINT_new,
219 (const EC_GROUP *group),
220 NULL,
221 (group),
222 1
223)
224
225WRAP(EVP_PKEY *,
226 EVP_PKEY_new,
227 (void),
228 NULL,
229 (),
230 1
231)
232
233WRAP(int,
234 EVP_PKEY_assign,
235 (EVP_PKEY *pkey, int type, void *key),
236 0,
237 (pkey, type, key),
238 1
239)
240
241WRAP(EVP_PKEY *,
242 EVP_PKEY_new_raw_public_key,
243 (int type, ENGINE *e, const unsigned char *key, size_t keylen),
244 NULL,
245 (type, e, key, keylen),
246 1
247)
248
249WRAP(EVP_PKEY_CTX *,
250 EVP_PKEY_CTX_new,
251 (EVP_PKEY *pkey, ENGINE *e),
252 NULL,
253 (pkey, e),
254 1
255)
256
257WRAP(int,
258 EVP_PKEY_derive_init,
259 (EVP_PKEY_CTX *ctx),
260 0,
261 (ctx),
262 1
263)
264
265WRAP(int,
266 EVP_PKEY_derive_set_peer,
267 (EVP_PKEY_CTX *ctx, EVP_PKEY *peer),
268 0,
269 (ctx, peer),
270 1
271)
272
273WRAP(const EVP_MD *,
274 EVP_sha256,
275 (void),
276 NULL,
277 (),
278 1
279)
280
281WRAP(unsigned char *,
282 HMAC,
283 (const EVP_MD *evp_md, const void *key, int key_len,
284 const unsigned char *d, int n, unsigned char *md,
285 unsigned int *md_len),
286 NULL,
287 (evp_md, key, key_len, d, n, md, md_len),
288 1
289)
290
291WRAP(HMAC_CTX *,
292 HMAC_CTX_new,
293 (void),
294 NULL,
295 (),
296 1
297)
298
299WRAP(int,
300 HMAC_Init_ex,
301 (HMAC_CTX *ctx, const void *key, int key_len, const EVP_MD *md,
302 ENGINE *impl),
303 0,
304 (ctx, key, key_len, md, impl),
305 1
306)
307
308WRAP(int,
309 HMAC_Update,
310 (HMAC_CTX *ctx, const unsigned char *data, int len),
311 0,
312 (ctx, data, len),
313 1
314)
315
316WRAP(int,
317 HMAC_Final,
318 (HMAC_CTX *ctx, unsigned char *md, unsigned int *len),
319 0,
320 (ctx, md, len),
321 1
322)
323
324WRAP(unsigned char *,
325 SHA256,
326 (const unsigned char *d, size_t n, unsigned char *md),
327 NULL,
328 (d, n, md),
329 1
330)
331
332WRAP(cbor_item_t *,
333 cbor_build_string,
334 (const char *val),
335 NULL,
336 (val),
337 1
338)
339
340WRAP(cbor_item_t *,
341 cbor_build_bytestring,
342 (cbor_data handle, size_t length),
343 NULL,
344 (handle, length),
345 1
346)
347
348WRAP(cbor_item_t *,
349 cbor_load,
350 (cbor_data source, size_t source_size, struct cbor_load_result *result),
351 NULL,
352 (source, source_size, result),
353 1
354)
355
356WRAP(cbor_item_t *,
357 cbor_build_uint8,
358 (uint8_t value),
359 NULL,
360 (value),
361 1
362)
363
364WRAP(struct cbor_pair *,
365 cbor_map_handle,
366 (const cbor_item_t *item),
367 NULL,
368 (item),
369 1
370)
371
372WRAP(cbor_item_t **,
373 cbor_array_handle,
374 (const cbor_item_t *item),
375 NULL,
376 (item),
377 1
378)
379
380WRAP(bool,
381 cbor_map_add,
382 (cbor_item_t *item, struct cbor_pair pair),
383 false,
384 (item, pair),
385 1
386)
387
388WRAP(cbor_item_t *,
389 cbor_new_definite_map,
390 (size_t size),
391 NULL,
392 (size),
393 1
394)
395
396WRAP(size_t,
397 cbor_serialize_alloc,
398 (const cbor_item_t *item, cbor_mutable_data *buffer,
399 size_t *buffer_size),
400 0,
401 (item, buffer, buffer_size),
402 1
403)
404
405WRAP(int,
406 fido_tx,
407 (fido_dev_t *d, uint8_t cmd, const void *buf, size_t count),
408 -1,
409 (d, cmd, buf, count),
410 1
411)
412
413WRAP(int,
414 usleep,
415 (unsigned int usec),
416 -1,
417 (usec),
418 1
419)
diff --git a/fuzz/wrapped.sym b/fuzz/wrapped.sym
new file mode 100644
index 0000000..3679f91
--- /dev/null
+++ b/fuzz/wrapped.sym
@@ -0,0 +1,47 @@
1BN_bin2bn
2BN_CTX_get
3BN_CTX_new
4BN_new
5calloc
6cbor_array_handle
7cbor_build_bytestring
8cbor_build_string
9cbor_build_uint8
10cbor_load
11cbor_map_add
12cbor_map_handle
13cbor_new_definite_map
14cbor_serialize_alloc
15EC_KEY_get0_group
16EC_KEY_new_by_curve_name
17EC_POINT_new
18EVP_CIPHER_CTX_new
19EVP_CIPHER_CTX_set_padding
20EVP_DecryptInit_ex
21EVP_DecryptUpdate
22EVP_DigestVerifyInit
23EVP_EncryptInit_ex
24EVP_EncryptUpdate
25EVP_MD_CTX_new
26EVP_PKEY_assign
27EVP_PKEY_CTX_new
28EVP_PKEY_derive_init
29EVP_PKEY_derive_set_peer
30EVP_PKEY_get0_RSA
31EVP_PKEY_new
32EVP_PKEY_new_raw_public_key
33EVP_sha256
34fido_tx
35HMAC
36HMAC_CTX_new
37HMAC_Final
38HMAC_Init_ex
39HMAC_Update
40malloc
41RSA_set0_key
42SHA256
43SHA256_Final
44SHA256_Init
45SHA256_Update
46strdup
47usleep
diff --git a/man/CMakeLists.txt b/man/CMakeLists.txt
new file mode 100644
index 0000000..c903ab8
--- /dev/null
+++ b/man/CMakeLists.txt
@@ -0,0 +1,314 @@
1# Copyright (c) 2018 Yubico AB. All rights reserved.
2# Use of this source code is governed by a BSD-style
3# license that can be found in the LICENSE file.
4
5find_program(MANDOC_PATH mandoc)
6message(STATUS "MANDOC_PATH: ${MANDOC_PATH}")
7
8if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
9 find_program(GZIP_PATH gzip)
10 message(STATUS "GZIP_PATH: ${GZIP_PATH}")
11endif()
12
13list(APPEND MAN_SOURCES
14 eddsa_pk_new.3
15 es256_pk_new.3
16 fido2-assert.1
17 fido2-cred.1
18 fido2-token.1
19 fido_init.3
20 fido_assert_new.3
21 fido_assert_allow_cred.3
22 fido_assert_set_authdata.3
23 fido_assert_verify.3
24 fido_bio_dev_get_info.3
25 fido_bio_enroll_new.3
26 fido_bio_info_new.3
27 fido_bio_template.3
28 fido_cbor_info_new.3
29 fido_cred_new.3
30 fido_cred_exclude.3
31 fido_credman_metadata_new.3
32 fido_cred_set_authdata.3
33 fido_cred_verify.3
34 fido_dev_get_assert.3
35 fido_dev_info_manifest.3
36 fido_dev_make_cred.3
37 fido_dev_open.3
38 fido_dev_set_io_functions.3
39 fido_dev_set_pin.3
40 fido_strerr.3
41 rs256_pk_new.3
42)
43
44list(APPEND MAN_ALIAS
45 eddsa_pk_new eddsa_pk_free
46 eddsa_pk_new eddsa_pk_from_ptr
47 eddsa_pk_new eddsa_pk_to_EVP_PKEY
48 es256_pk_new es256_pk_free
49 es256_pk_new es256_pk_from_EC_KEY
50 es256_pk_new es256_pk_from_ptr
51 es256_pk_new es256_pk_to_EVP_PKEY
52 fido_assert_new fido_assert_authdata_len
53 fido_assert_new fido_assert_authdata_ptr
54 fido_assert_new fido_assert_clientdata_hash_len
55 fido_assert_new fido_assert_clientdata_hash_ptr
56 fido_assert_new fido_assert_count
57 fido_assert_new fido_assert_free
58 fido_assert_new fido_assert_hmac_secret_len
59 fido_assert_new fido_assert_hmac_secret_ptr
60 fido_assert_new fido_assert_sigcount
61 fido_assert_new fido_assert_sig_len
62 fido_assert_new fido_assert_sig_ptr
63 fido_assert_new fido_assert_user_display_name
64 fido_assert_new fido_assert_user_icon
65 fido_assert_new fido_assert_user_id_len
66 fido_assert_new fido_assert_user_id_ptr
67 fido_assert_new fido_assert_user_name
68 fido_assert_set_authdata fido_assert_set_clientdata_hash
69 fido_assert_set_authdata fido_assert_set_count
70 fido_assert_set_authdata fido_assert_set_extensions
71 fido_assert_set_authdata fido_assert_set_hmac_salt
72 fido_assert_set_authdata fido_assert_set_rp
73 fido_assert_set_authdata fido_assert_set_sig
74 fido_assert_set_authdata fido_assert_set_up
75 fido_assert_set_authdata fido_assert_set_uv
76 fido_bio_dev_get_info fido_bio_dev_enroll_begin
77 fido_bio_dev_get_info fido_bio_dev_enroll_cancel
78 fido_bio_dev_get_info fido_bio_dev_enroll_continue
79 fido_bio_dev_get_info fido_bio_dev_enroll_remove
80 fido_bio_dev_get_info fido_bio_dev_get_template_array
81 fido_bio_dev_get_info fido_bio_dev_set_template_name
82 fido_bio_enroll_new fido_bio_enroll_free
83 fido_bio_enroll_new fido_bio_enroll_last_status
84 fido_bio_enroll_new fido_bio_enroll_remaining_samples
85 fido_bio_info_new fido_bio_info_free
86 fido_bio_info_new fido_bio_info_max_samples
87 fido_bio_info_new fido_bio_info_type
88 fido_bio_template fido_bio_template_array_count
89 fido_bio_template fido_bio_template_array_free
90 fido_bio_template fido_bio_template_array_new
91 fido_bio_template fido_bio_template_free
92 fido_bio_template fido_bio_template_id_len
93 fido_bio_template fido_bio_template_id_ptr
94 fido_bio_template fido_bio_template_name
95 fido_bio_template fido_bio_template_new
96 fido_bio_template fido_bio_template_set_id
97 fido_bio_template fido_bio_template_set_name
98 fido_cbor_info_new fido_cbor_info_aaguid_len
99 fido_cbor_info_new fido_cbor_info_aaguid_ptr
100 fido_cbor_info_new fido_cbor_info_extensions_len
101 fido_cbor_info_new fido_cbor_info_extensions_ptr
102 fido_cbor_info_new fido_cbor_info_free
103 fido_cbor_info_new fido_cbor_info_maxmsgsiz
104 fido_cbor_info_new fido_cbor_info_options_len
105 fido_cbor_info_new fido_cbor_info_options_name_ptr
106 fido_cbor_info_new fido_cbor_info_options_value_ptr
107 fido_cbor_info_new fido_cbor_info_protocols_len
108 fido_cbor_info_new fido_cbor_info_protocols_ptr
109 fido_cbor_info_new fido_cbor_info_versions_len
110 fido_cbor_info_new fido_cbor_info_versions_ptr
111 fido_cbor_info_new fido_dev_get_cbor_info
112 fido_cred_new fido_cred_authdata_len
113 fido_cred_new fido_cred_authdata_ptr
114 fido_cred_new fido_cred_clientdata_hash_len
115 fido_cred_new fido_cred_clientdata_hash_ptr
116 fido_cred_new fido_cred_fmt
117 fido_cred_new fido_cred_free
118 fido_cred_new fido_cred_id_len
119 fido_cred_new fido_cred_id_ptr
120 fido_cred_new fido_cred_pubkey_len
121 fido_cred_new fido_cred_pubkey_ptr
122 fido_cred_new fido_cred_sig_len
123 fido_cred_new fido_cred_sig_ptr
124 fido_cred_new fido_cred_x5c_len
125 fido_cred_new fido_cred_x5c_ptr
126 fido_credman_metadata_new fido_credman_del_dev_rk
127 fido_credman_metadata_new fido_credman_get_dev_metadata
128 fido_credman_metadata_new fido_credman_get_dev_rk
129 fido_credman_metadata_new fido_credman_get_dev_rp
130 fido_credman_metadata_new fido_credman_metadata_free
131 fido_credman_metadata_new fido_credman_rk
132 fido_credman_metadata_new fido_credman_rk_count
133 fido_credman_metadata_new fido_credman_rk_existing
134 fido_credman_metadata_new fido_credman_rk_free
135 fido_credman_metadata_new fido_credman_rk_new
136 fido_credman_metadata_new fido_credman_rk_remaining
137 fido_credman_metadata_new fido_credman_rp_count
138 fido_credman_metadata_new fido_credman_rp_free
139 fido_credman_metadata_new fido_credman_rp_id
140 fido_credman_metadata_new fido_credman_rp_id_hash_len
141 fido_credman_metadata_new fido_credman_rp_id_hash_ptr
142 fido_credman_metadata_new fido_credman_rp_name
143 fido_credman_metadata_new fido_credman_rp_new
144 fido_cred_set_authdata fido_cred_set_authdata_raw
145 fido_cred_set_authdata fido_cred_set_clientdata_hash
146 fido_cred_set_authdata fido_cred_set_extensions
147 fido_cred_set_authdata fido_cred_set_fmt
148 fido_cred_set_authdata fido_cred_set_rk
149 fido_cred_set_authdata fido_cred_set_rp
150 fido_cred_set_authdata fido_cred_set_sig
151 fido_cred_set_authdata fido_cred_set_type
152 fido_cred_set_authdata fido_cred_set_user
153 fido_cred_set_authdata fido_cred_set_uv
154 fido_cred_set_authdata fido_cred_set_x509
155 fido_dev_info_manifest fido_dev_info_free
156 fido_dev_info_manifest fido_dev_info_manufacturer_string
157 fido_dev_info_manifest fido_dev_info_new
158 fido_dev_info_manifest fido_dev_info_path
159 fido_dev_info_manifest fido_dev_info_product
160 fido_dev_info_manifest fido_dev_info_product_string
161 fido_dev_info_manifest fido_dev_info_ptr
162 fido_dev_info_manifest fido_dev_info_vendor
163 fido_dev_open fido_dev_build
164 fido_dev_open fido_dev_cancel
165 fido_dev_open fido_dev_close
166 fido_dev_open fido_dev_flags
167 fido_dev_open fido_dev_force_fido2
168 fido_dev_open fido_dev_force_u2f
169 fido_dev_open fido_dev_free
170 fido_dev_open fido_dev_is_fido2
171 fido_dev_open fido_dev_major
172 fido_dev_open fido_dev_minor
173 fido_dev_open fido_dev_new
174 fido_dev_open fido_dev_protocol
175 fido_dev_set_pin fido_dev_get_retry_count
176 fido_dev_set_pin fido_dev_reset
177 rs256_pk_new rs256_pk_free
178 rs256_pk_new rs256_pk_from_ptr
179 rs256_pk_new rs256_pk_from_RSA
180 rs256_pk_new rs256_pk_to_EVP_PKEY
181)
182
183list(LENGTH MAN_ALIAS MAN_ALIAS_LEN)
184math(EXPR MAN_ALIAS_MAX "${MAN_ALIAS_LEN} - 2")
185
186# man_copy
187foreach(f ${MAN_SOURCES})
188 add_custom_command(OUTPUT ${f}
189 COMMAND cp -f ${CMAKE_SOURCE_DIR}/man/${f} .
190 DEPENDS ${f})
191 list(APPEND COPY_FILES ${f})
192endforeach()
193
194# man_lint
195foreach(f ${MAN_SOURCES})
196 add_custom_command(OUTPUT ${f}.lint
197 COMMAND mandoc -T lint -W warning ${f} > ${f}.lint
198 DEPENDS ${f})
199 list(APPEND LINT_FILES ${f}.lint)
200endforeach()
201
202# man_html
203foreach(f ${MAN_SOURCES})
204 string(REGEX REPLACE ".[13]" "" g ${f})
205 add_custom_command(OUTPUT ${g}.html
206 COMMAND mandoc -T html -O man="%N.html",style=style.css -I os="Yubico AB" ${f} > ${g}.html
207 DEPENDS ${f})
208 list(APPEND HTML_FILES ${g}.html)
209endforeach()
210
211# man_html_partial
212foreach(f ${MAN_SOURCES})
213 string(REGEX REPLACE ".[13]" "" g ${f})
214 add_custom_command(OUTPUT ${g}.partial
215 COMMAND cat ${CMAKE_SOURCE_DIR}/man/dyc.css > ${g}.partial
216 COMMAND mandoc -T html -O man="%N.html",fragment ${f} >> ${g}.partial
217 DEPENDS ${f})
218 list(APPEND HTML_PARTIAL_FILES ${g}.partial)
219endforeach()
220
221# man_gzip
222foreach(f ${MAN_SOURCES})
223 add_custom_command(OUTPUT ${f}.gz
224 COMMAND gzip -c ${f} > ${f}.gz
225 DEPENDS ${f})
226 list(APPEND GZ_FILES ${f}.gz)
227endforeach()
228
229macro(define_symlink_target NAME EXT)
230 foreach(i RANGE 0 ${MAN_ALIAS_MAX} 2)
231 math(EXPR j "${i} + 1")
232 list(GET MAN_ALIAS ${i} SRC)
233 list(GET MAN_ALIAS ${j} DST)
234 add_custom_command(OUTPUT ${DST}.${EXT}
235 COMMAND ln -sf ${SRC}.${EXT} ${DST}.${EXT})
236 list(APPEND ${NAME}_LINK_FILES ${DST}.${EXT})
237 endforeach()
238 add_custom_target(${NAME} DEPENDS ${${NAME}_LINK_FILES})
239endmacro()
240
241add_custom_target(man_copy DEPENDS ${COPY_FILES})
242add_custom_target(man_lint DEPENDS ${LINT_FILES})
243add_custom_target(man_html DEPENDS ${HTML_FILES})
244add_custom_target(man_html_partial DEPENDS ${HTML_PARTIAL_FILES})
245add_custom_target(man_gzip DEPENDS ${GZ_FILES})
246
247define_symlink_target(man_symlink 3)
248define_symlink_target(man_symlink_html html)
249define_symlink_target(man_symlink_html_partial partial)
250define_symlink_target(man_symlink_gzip 3.gz)
251
252add_dependencies(man_symlink man_copy)
253add_dependencies(man_lint man_symlink)
254add_dependencies(man_html man_lint)
255add_dependencies(man_symlink_html man_html)
256add_dependencies(man_html_partial man_lint)
257add_dependencies(man_symlink_html_partial man_html_partial)
258add_custom_target(man ALL)
259
260if(MANDOC_PATH)
261 add_dependencies(man man_symlink_html)
262 add_dependencies(man_gzip man_lint)
263 install(FILES ${CMAKE_SOURCE_DIR}/man/style.css
264 DESTINATION ${CMAKE_INSTALL_PREFIX}/share/doc/libfido2)
265 foreach(f ${MAN_SOURCES})
266 string(REGEX REPLACE ".[13]" "" f ${f})
267 install(FILES ${CMAKE_BINARY_DIR}/man/${f}.html
268 DESTINATION ${CMAKE_INSTALL_PREFIX}/share/doc/libfido2)
269 endforeach()
270 foreach(i RANGE 0 ${MAN_ALIAS_MAX} 2)
271 math(EXPR j "${i} + 1")
272 list(GET MAN_ALIAS ${j} DST)
273 install(FILES ${CMAKE_BINARY_DIR}/man/${DST}.html
274 DESTINATION ${CMAKE_INSTALL_PREFIX}/share/doc/libfido2)
275 endforeach()
276endif()
277
278if(GZIP_PATH)
279 add_dependencies(man_gzip man_copy)
280 add_dependencies(man_symlink_gzip man_gzip)
281 add_dependencies(man man_symlink_gzip)
282 foreach(f ${MAN_SOURCES})
283 if (${f} MATCHES ".1$")
284 install(FILES ${CMAKE_BINARY_DIR}/man/${f}.gz
285 DESTINATION "${CMAKE_INSTALL_PREFIX}/share/man/man1")
286 elseif(${f} MATCHES ".3$")
287 install(FILES ${CMAKE_BINARY_DIR}/man/${f}.gz
288 DESTINATION "${CMAKE_INSTALL_PREFIX}/share/man/man3")
289 endif()
290 endforeach()
291 foreach(i RANGE 0 ${MAN_ALIAS_MAX} 2)
292 math(EXPR j "${i} + 1")
293 list(GET MAN_ALIAS ${j} DST)
294 install(FILES ${CMAKE_BINARY_DIR}/man/${DST}.3.gz
295 DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man3)
296 endforeach()
297elseif(NOT MSVC)
298 add_dependencies(man man_symlink)
299 foreach(f ${MAN_SOURCES})
300 if (${f} MATCHES ".1$")
301 install(FILES ${CMAKE_BINARY_DIR}/man/${f}
302 DESTINATION "${CMAKE_INSTALL_PREFIX}/man/man1")
303 elseif(${f} MATCHES ".3$")
304 install(FILES ${CMAKE_BINARY_DIR}/man/${f}
305 DESTINATION "${CMAKE_INSTALL_PREFIX}/man/man3")
306 endif()
307 endforeach()
308 foreach(i RANGE 0 ${MAN_ALIAS_MAX} 2)
309 math(EXPR j "${i} + 1")
310 list(GET MAN_ALIAS ${j} DST)
311 install(FILES ${CMAKE_BINARY_DIR}/man/${DST}.3
312 DESTINATION ${CMAKE_INSTALL_PREFIX}/man/man3)
313 endforeach()
314endif()
diff --git a/man/NOTES b/man/NOTES
new file mode 100644
index 0000000..4a461ff
--- /dev/null
+++ b/man/NOTES
@@ -0,0 +1,4 @@
1To generate .partial files for https://developers.yubico.com/:
2
3$ make -C build man_symlink_html_partial
4$ (cd build/man && pax -p p -r -w *.partial /tmp/partial)
diff --git a/man/dyc.css b/man/dyc.css
new file mode 100644
index 0000000..1ff5b59
--- /dev/null
+++ b/man/dyc.css
@@ -0,0 +1,14 @@
1<style>
2 table.head, table.foot { width: 100%; }
3 td.head-rtitle, td.foot-os { text-align: right; }
4 td.head-vol { text-align: center; }
5 div.Pp { margin: 1ex 0ex; }
6 div.Nd, div.Bf, div.Op { display: inline; }
7 span.Pa, span.Ad { font-style: italic; }
8 span.Ms { font-weight: bold; }
9 dl.Bl-diag > dt { font-weight: bold; }
10 code.Nm, code.Fl, code.Cm, code.Ic, code.In, code.Fd, code.Fn,
11 code.Cd { font-weight: bold; font-family: monospace; }
12 var { font-family: monospace; }
13 .Sh { font-size: 1.5em; padding-top: 1em; padding-bottom: 1em; }
14</style>
diff --git a/man/eddsa_pk_new.3 b/man/eddsa_pk_new.3
new file mode 100644
index 0000000..65bf9a9
--- /dev/null
+++ b/man/eddsa_pk_new.3
@@ -0,0 +1,122 @@
1.\" Copyright (c) 2019 Yubico AB. All rights reserved.
2.\" Use of this source code is governed by a BSD-style
3.\" license that can be found in the LICENSE file.
4.\"
5.Dd $Mdocdate: May 15 2019 $
6.Dt EDDSA_PK_NEW 3
7.Os
8.Sh NAME
9.Nm eddsa_pk_new ,
10.Nm eddsa_pk_free ,
11.Nm eddsa_pk_from_EVP_PKEY ,
12.Nm eddsa_pk_from_ptr ,
13.Nm eddsa_pk_to_EVP_PKEY
14.Nd FIDO 2 COSE EDDSA API
15.Sh SYNOPSIS
16.In openssl/evp.h
17.In fido/eddsa.h
18.Ft eddsa_pk_t *
19.Fn eddsa_pk_new "void"
20.Ft void
21.Fn eddsa_pk_free "eddsa_pk_t **pkp"
22.Ft int
23.Fn eddsa_pk_from_EVP_PKEY "eddsa_pk_t *pk" "const EVP_PKEY *pkey"
24.Ft int
25.Fn eddsa_pk_from_ptr "eddsa_pk_t *pk" "const void *ptr" "size_t len"
26.Ft EVP_PKEY *
27.Fn eddsa_pk_to_EVP_PKEY "const eddsa_pk_t *pk"
28.Sh DESCRIPTION
29EDDSA is the name given in the CBOR Object Signing and Encryption
30(COSE) RFC to EDDSA over Curve25519 with SHA-512.
31The COSE EDDSA API of
32.Em libfido2
33is an auxiliary API with routines to convert between the different
34EDDSA public key types used in
35.Em libfido2
36and
37.Em OpenSSL .
38.Pp
39In
40.Em libfido2 ,
41EDDSA public keys are abstracted by the
42.Vt eddsa_pk_t
43type.
44.Pp
45The
46.Fn eddsa_pk_new
47function returns a pointer to a newly allocated, empty
48.Vt eddsa_pk_t
49type.
50If memory cannot be allocated, NULL is returned.
51.Pp
52The
53.Fn eddsa_pk_free
54function releases the memory backing
55.Fa *pkp ,
56where
57.Fa *pkp
58must have been previously allocated by
59.Fn eddsa_pk_new .
60On return,
61.Fa *pkp
62is set to NULL.
63Either
64.Fa pkp
65or
66.Fa *pkp
67may be NULL, in which case
68.Fn eddsa_pk_free
69is a NOP.
70.Pp
71The
72.Fn eddsa_pk_from_EVP_PKEY
73function fills
74.Fa pk
75with the contents of
76.Fa pkey .
77No references to
78.Fa pkey
79are kept.
80.Pp
81The
82.Fn eddsa_pk_from_ptr
83function fills
84.Fa pk
85with the contents of
86.Fa ptr ,
87where
88.Fa ptr
89points to
90.Fa len
91bytes.
92No references to
93.Fa ptr
94are kept.
95.Pp
96The
97.Fn eddsa_pk_to_EVP_PKEY
98function converts
99.Fa pk
100to a newly allocated
101.Fa EVP_PKEY
102type with a reference count of 1.
103No internal references to the returned pointer are kept.
104If an error occurs,
105.Fn eddsa_pk_to_EVP_PKEY
106returns NULL.
107.Sh RETURN VALUES
108The
109.Fn eddsa_pk_from_EC_KEY
110and
111.Fn eddsa_pk_from_ptr
112functions return
113.Dv FIDO_OK
114on success.
115On error, a different error code defined in
116.In fido/err.h
117is returned.
118.Sh SEE ALSO
119.Xr es256_pk_new 3 ,
120.Xr fido_assert_verify 3 ,
121.Xr fido_cred_pubkey_ptr 3 ,
122.Xr rs256_pk_new 3
diff --git a/man/es256_pk_new.3 b/man/es256_pk_new.3
new file mode 100644
index 0000000..48eda0b
--- /dev/null
+++ b/man/es256_pk_new.3
@@ -0,0 +1,122 @@
1.\" Copyright (c) 2018 Yubico AB. All rights reserved.
2.\" Use of this source code is governed by a BSD-style
3.\" license that can be found in the LICENSE file.
4.\"
5.Dd $Mdocdate: May 24 2018 $
6.Dt ES256_PK_NEW 3
7.Os
8.Sh NAME
9.Nm es256_pk_new ,
10.Nm es256_pk_free ,
11.Nm es256_pk_from_EC_KEY ,
12.Nm es256_pk_from_ptr ,
13.Nm es256_pk_to_EVP_PKEY
14.Nd FIDO 2 COSE ES256 API
15.Sh SYNOPSIS
16.In openssl/ec.h
17.In fido/es256.h
18.Ft es256_pk_t *
19.Fn es256_pk_new "void"
20.Ft void
21.Fn es256_pk_free "es256_pk_t **pkp"
22.Ft int
23.Fn es256_pk_from_EC_KEY "es256_pk_t *pk" "const EC_KEY *ec"
24.Ft int
25.Fn es256_pk_from_ptr "es256_pk_t *pk" "const void *ptr" "size_t len"
26.Ft EVP_PKEY *
27.Fn es256_pk_to_EVP_PKEY "const es256_pk_t *pk"
28.Sh DESCRIPTION
29ES256 is the name given in the CBOR Object Signing and Encryption
30(COSE) RFC to ECDSA over P-256 with SHA-256.
31The COSE ES256 API of
32.Em libfido2
33is an auxiliary API with routines to convert between the different
34ECDSA public key types used in
35.Em libfido2
36and
37.Em OpenSSL .
38.Pp
39In
40.Em libfido2 ,
41ES256 public keys are abstracted by the
42.Vt es256_pk_t
43type.
44.Pp
45The
46.Fn es256_pk_new
47function returns a pointer to a newly allocated, empty
48.Vt es256_pk_t
49type.
50If memory cannot be allocated, NULL is returned.
51.Pp
52The
53.Fn es256_pk_free
54function releases the memory backing
55.Fa *pkp ,
56where
57.Fa *pkp
58must have been previously allocated by
59.Fn es256_pk_new .
60On return,
61.Fa *pkp
62is set to NULL.
63Either
64.Fa pkp
65or
66.Fa *pkp
67may be NULL, in which case
68.Fn es256_pk_free
69is a NOP.
70.Pp
71The
72.Fn es256_pk_from_EC_KEY
73function fills
74.Fa pk
75with the contents of
76.Fa ec .
77No references to
78.Fa ec
79are kept.
80.Pp
81The
82.Fn es256_pk_from_ptr
83function fills
84.Fa pk
85with the contents of
86.Fa ptr ,
87where
88.Fa ptr
89points to
90.Fa len
91bytes.
92No references to
93.Fa ptr
94are kept.
95.Pp
96The
97.Fn es256_pk_to_EVP_PKEY
98function converts
99.Fa pk
100to a newly allocated
101.Fa EVP_PKEY
102type with a reference count of 1.
103No internal references to the returned pointer are kept.
104If an error occurs,
105.Fn es256_pk_to_EVP_PKEY
106returns NULL.
107.Sh RETURN VALUES
108The
109.Fn es256_pk_from_EC_KEY
110and
111.Fn es256_pk_from_ptr
112functions return
113.Dv FIDO_OK
114on success.
115On error, a different error code defined in
116.In fido/err.h
117is returned.
118.Sh SEE ALSO
119.Xr eddsa_pk_new 3 ,
120.Xr fido_assert_verify 3 ,
121.Xr fido_cred_pubkey_ptr 3 ,
122.Xr rs256_pk_new 3
diff --git a/man/fido2-assert.1 b/man/fido2-assert.1
new file mode 100644
index 0000000..67883e2
--- /dev/null
+++ b/man/fido2-assert.1
@@ -0,0 +1,220 @@
1.\" Copyright (c) 2018 Yubico AB. All rights reserved.
2.\" Use of this source code is governed by a BSD-style
3.\" license that can be found in the LICENSE file.
4.\"
5.Dd $Mdocdate: November 5 2019 $
6.Dt FIDO2-ASSERT 1
7.Os
8.Sh NAME
9.Nm fido2-assert
10.Nd get/verify a FIDO 2 assertion
11.Sh SYNOPSIS
12.Nm
13.Fl G
14.Op Fl dhpruv
15.Op Fl i Ar input_file
16.Op Fl o Ar output_file
17.Ar device
18.Nm
19.Fl V
20.Op Fl dhpv
21.Op Fl i Ar input_file
22.Ar key_file
23.Op Ar type
24.Sh DESCRIPTION
25.Nm
26gets or verifies a FIDO 2 assertion.
27.Pp
28The input of
29.Nm
30is defined by the parameters of the assertion to be obtained/verified.
31See the
32.Sx INPUT FORMAT
33section for details.
34.Pp
35The output of
36.Nm
37is defined by the result of the selected operation.
38See the
39.Sx OUTPUT FORMAT
40section for details.
41.Pp
42If an assertion is successfully obtained or verified,
43.Nm
44exits 0.
45Otherwise,
46.Nm
47exits 1.
48.Pp
49The options are as follows:
50.Bl -tag -width Ds
51.It Fl G
52Tells
53.Nm
54to obtain a new assertion from
55.Ar device .
56.It Fl V
57Tells
58.Nm
59to verify an assertion using the PEM-encoded public key in
60.Ar key_file
61of type
62.Ar type ,
63where
64.Ar type
65may be
66.Em es256
67(denoting ECDSA over NIST P-256 with SHA-256),
68.Em rs256
69(denoting 2048-bit RSA with PKCS#1.5 padding and SHA-256), or
70.Em eddsa
71(denoting EDDSA over Curve25519 with SHA-512).
72If
73.Ar type
74is not specified,
75.Em es256
76is assumed.
77.It Fl h
78If obtaining an assertion, enable the FIDO2 hmac-secret
79extension.
80If verifying an assertion, check whether the extension data bit was
81signed by the authenticator.
82.It Fl d
83Causes
84.Nm
85to emit debugging output on
86.Em stderr .
87.It Fl i Ar input_file
88Tells
89.Nm
90to read the parameters of the assertion from
91.Ar input_file
92instead of
93.Em stdin .
94.It Fl o Ar output_file
95Tells
96.Nm
97to write output on
98.Ar output_file
99instead of
100.Em stdout .
101.It Fl p
102If obtaining an assertion, request user presence.
103If verifying an assertion, check whether the user presence bit was
104signed by the authenticator.
105.It Fl r
106Obtain an assertion using a resident credential.
107If
108.Fl r
109is specified,
110.Nm
111will not expect a credential id in its input, and may output
112multiple assertions.
113.It Fl u
114Obtain an assertion using U2F.
115By default,
116.Nm
117will use FIDO2 if supported by the authenticator, and fallback to
118U2F otherwise.
119.It Fl v
120If obtaining an assertion, prompt the user for a PIN and request
121user verification from the authenticator.
122If a
123.Em tty
124is available,
125.Nm
126will use it to obtain the PIN.
127Otherwise,
128.Em stdin
129is used.
130If verifying an assertion, check whether the user verification bit
131was signed by the authenticator.
132.El
133.Sh INPUT FORMAT
134The input of
135.Nm
136consists of base64 blobs and UTF-8 strings separated
137by newline characters ('\\n').
138.Pp
139When obtaining an assertion,
140.Nm
141expects its input to consist of:
142.Pp
143.Bl -enum -offset indent -compact
144.It
145client data hash (base64 blob);
146.It
147relying party id (UTF-8 string);
148.It
149credential id, if credential not resident (base64 blob);
150.It
151hmac salt, if the FIDO2 hmac-secret extension is enabled
152(base64 blob);
153.El
154.Pp
155When verifying an assertion,
156.Nm
157expects its input to consist of:
158.Pp
159.Bl -enum -offset indent -compact
160.It
161client data hash (base64 blob);
162.It
163relying party id (UTF-8 string);
164.It
165authenticator data (base64 blob);
166.It
167assertion signature (base64 blob);
168.El
169.Pp
170UTF-8 strings passed to
171.Nm
172must not contain embedded newline or NUL characters.
173.Sh OUTPUT FORMAT
174The output of
175.Nm
176consists of base64 blobs and UTF-8 strings separated
177by newline characters ('\\n').
178.Pp
179For each generated assertion,
180.Nm
181outputs:
182.Pp
183.Bl -enum -offset indent -compact
184.It
185client data hash (base64 blob);
186.It
187relying party id (UTF-8 string);
188.It
189authenticator data (base64 blob);
190.It
191assertion signature (base64 blob);
192.It
193user id, if credential resident (base64 blob);
194.It
195hmac secret, if the FIDO2 hmac-secret extension is enabled
196(base64 blob);
197.El
198.Pp
199When verifying an assertion,
200.Nm
201produces no output.
202.Sh EXAMPLES
203Assuming
204.Pa cred
205contains a
206.Em es256
207credential created according to the steps outlined in
208.Xr fido2-cred 1 ,
209obtain an assertion from an authenticator at
210.Pa /dev/hidraw5
211and verify it:
212.Pp
213.Dl $ echo assertion challenge | openssl sha256 -binary | base64 > assert_param
214.Dl $ echo relying party >> assert_param
215.Dl $ head -1 cred >> assert_param
216.Dl $ tail -n +2 cred > pubkey
217.Dl $ fido2-assert -G -i assert_param /dev/hidraw5 | fido2-assert -V pubkey es256
218.Sh SEE ALSO
219.Xr fido2-cred 1 ,
220.Xr fido2-token 1
diff --git a/man/fido2-cred.1 b/man/fido2-cred.1
new file mode 100644
index 0000000..d9bf7d2
--- /dev/null
+++ b/man/fido2-cred.1
@@ -0,0 +1,238 @@
1.\" Copyright (c) 2018 Yubico AB. All rights reserved.
2.\" Use of this source code is governed by a BSD-style
3.\" license that can be found in the LICENSE file.
4.\"
5.Dd $Mdocdate: November 5 2019 $
6.Dt FIDO2-CRED 1
7.Os
8.Sh NAME
9.Nm fido2-cred
10.Nd make/verify a FIDO 2 credential
11.Sh SYNOPSIS
12.Nm
13.Fl M
14.Op Fl dhqruv
15.Op Fl i Ar input_file
16.Op Fl o Ar output_file
17.Ar device
18.Op Ar type
19.Nm
20.Fl V
21.Op Fl dhv
22.Op Fl i Ar input_file
23.Op Fl o Ar output_file
24.Op Ar type
25.Sh DESCRIPTION
26.Nm
27makes or verifies a FIDO 2 credential.
28.Pp
29A credential
30.Ar type
31may be
32.Em es256
33(denoting ECDSA over NIST P-256 with SHA-256),
34.Em rs256
35(denoting 2048-bit RSA with PKCS#1.5 padding and SHA-256), or
36.Em eddsa
37(denoting EDDSA over Curve25519 with SHA-512).
38If
39.Ar type
40is not specified,
41.Em es256
42is assumed.
43.Pp
44When making a credential, the authenticator may require the user
45to authenticate with a PIN.
46If the
47.Fl q
48option is not specified,
49.Nm
50will prompt the user for the PIN.
51If a
52.Em tty
53is available,
54.Nm
55will use it to obtain the PIN.
56Otherwise,
57.Em stdin
58is used.
59.Pp
60The input of
61.Nm
62is defined by the parameters of the credential to be made/verified.
63See the
64.Sx INPUT FORMAT
65section for details.
66.Pp
67The output of
68.Nm
69is defined by the result of the selected operation.
70See the
71.Sx OUTPUT FORMAT
72section for details.
73.Pp
74If a credential is successfully created or verified,
75.Nm
76exits 0.
77Otherwise,
78.Nm
79exits 1.
80.Pp
81The options are as follows:
82.Bl -tag -width Ds
83.It Fl M
84Tells
85.Nm
86to make a new credential on
87.Ar device .
88.It Fl V
89Tells
90.Nm
91to verify a credential.
92.It Fl d
93Causes
94.Nm
95to emit debugging output on
96.Em stderr .
97.It Fl h
98If making a credential, enable the FIDO2 hmac-secret extension.
99If verifying a credential, check whether the extension data bit was
100signed by the authenticator.
101.It Fl i Ar input_file
102Tells
103.Nm
104to read the parameters of the credential from
105.Ar input_file
106instead of
107.Em stdin .
108.It Fl o Ar output_file
109Tells
110.Nm
111to write output on
112.Ar output_file
113instead of
114.Em stdout .
115.It Fl q
116Tells
117.Nm
118to be quiet.
119If a PIN is required and
120.Fl q
121is specified,
122.Nm
123will fail.
124.It Fl r
125Create a resident credential.
126.It Fl u
127Create a U2F credential.
128By default,
129.Nm
130will use FIDO2 if supported by the authenticator, and fallback to
131U2F otherwise.
132.It Fl v
133If making a credential, request user verification.
134If verifying a credential, check whether the user verification bit
135was signed by the authenticator.
136.El
137.Sh INPUT FORMAT
138The input of
139.Nm
140consists of base64 blobs and UTF-8 strings separated
141by newline characters ('\\n').
142.Pp
143When making a credential,
144.Nm
145expects its input to consist of:
146.Pp
147.Bl -enum -offset indent -compact
148.It
149client data hash (base64 blob);
150.It
151relying party id (UTF-8 string);
152.It
153user name (UTF-8 string);
154.It
155user id (base64 blob).
156.El
157.Pp
158When verifying a credential,
159.Nm
160expects its input to consist of:
161.Pp
162.Bl -enum -offset indent -compact
163.It
164client data hash (base64 blob);
165.It
166relying party id (UTF-8 string);
167.It
168credential format (UTF-8 string);
169.It
170authenticator data (base64 blob);
171.It
172credential id (base64 blob);
173.It
174attestation signature (base64 blob);
175.It
176attestation certificate (optional, base64 blob).
177.El
178.Pp
179UTF-8 strings passed to
180.Nm
181must not contain embedded newline or NUL characters.
182.Sh OUTPUT FORMAT
183The output of
184.Nm
185consists of base64 blobs, UTF-8 strings, and PEM-encoded public
186keys separated by newline characters ('\\n').
187.Pp
188Upon the successful generation of a credential,
189.Nm
190outputs:
191.Pp
192.Bl -enum -offset indent -compact
193.It
194client data hash (base64 blob);
195.It
196relying party id (UTF-8 string);
197.It
198credential format (UTF-8 string);
199.It
200authenticator data (base64 blob);
201.It
202credential id (base64 blob);
203.It
204attestation signature (base64 blob);
205.It
206attestation certificate, if present (base64 blob).
207.El
208.Pp
209Upon the successful verification of a credential,
210.Nm
211outputs:
212.Pp
213.Bl -enum -offset indent -compact
214.It
215credential id (base64 blob);
216.It
217PEM-encoded credential key.
218.El
219.Sh EXAMPLES
220Create a new
221.Em es256
222credential on
223.Pa /dev/hidraw5 ,
224verify it, and save the id and the public key of the credential in
225.Em cred :
226.Pp
227.Dl $ echo credential challenge | openssl sha256 -binary | base64 > cred_param
228.Dl $ echo relying party >> cred_param
229.Dl $ echo user name >> cred_param
230.Dl $ dd if=/dev/urandom bs=1 count=32 | base64 >> cred_param
231.Dl $ fido2-cred -M -i cred_param /dev/hidraw5 | fido2-cred -V -o cred
232.Sh SEE ALSO
233.Xr fido2-assert 1 ,
234.Xr fido2-token 1
235.Sh CAVEATS
236Please note that
237.Nm
238handles Basic Attestation and Self Attestation transparently.
diff --git a/man/fido2-token.1 b/man/fido2-token.1
new file mode 100644
index 0000000..d5a5734
--- /dev/null
+++ b/man/fido2-token.1
@@ -0,0 +1,158 @@
1.\" Copyright (c) 2018 Yubico AB. All rights reserved.
2.\" Use of this source code is governed by a BSD-style
3.\" license that can be found in the LICENSE file.
4.\"
5.Dd $Mdocdate: September 13 2019 $
6.Dt FIDO2-TOKEN 1
7.Os
8.Sh NAME
9.Nm fido2-token
10.Nd find and manage a FIDO 2 authenticator
11.Sh SYNOPSIS
12.Nm
13.Op Fl CR
14.Op Fl d
15.Ar device
16.Nm
17.Fl D
18.Op Fl de
19.Fl i
20.Ar id
21.Ar device
22.Nm
23.Fl I
24.Op Fl cd
25.Op Fl k Ar rp_id Fl i Ar cred_id
26.Ar device
27.Nm
28.Fl L
29.Op Fl der
30.Op Fl k Ar rp_id
31.Op device
32.Nm
33.Fl S
34.Op Fl de
35.Op Fl i Ar template_id Fl n Ar template_name
36.Ar device
37.Nm
38.Fl V
39.Sh DESCRIPTION
40.Nm
41manages a FIDO 2 authenticator.
42.Pp
43The options are as follows:
44.Bl -tag -width Ds
45.It Fl C Ar device
46Changes the PIN of
47.Ar device .
48The user will be prompted for the current and new PINs.
49.It Fl D Fl i Ar id Ar device
50Deletes the resident credential specified by
51.Ar id
52from
53.Ar device ,
54where
55.Ar id
56is the credential's base64-encoded id.
57The user will be prompted for the PIN.
58.It Fl D Fl e Fl i Ar id Ar device
59Deletes the biometric enrollment specified by
60.Ar id
61from
62.Ar device ,
63where
64.Ar id
65is the enrollment's template base64-encoded id.
66The user will be prompted for the PIN.
67.It Fl I Ar device
68Retrieves information on
69.Ar device .
70.It Fl I Fl c Ar device
71Retrieves resident credential metadata from
72.Ar device .
73The user will be prompted for the PIN.
74.It Fl I Fl k Ar rp_id Fl i Ar cred_id Ar device
75Prints the credential id (base64-encoded) and public key
76(PEM encoded) of the resident credential specified by
77.Ar rp_id
78and
79.Ar cred_id ,
80where
81.Ar rp_id
82is a UTF-8 relying party id, and
83.Ar cred_id
84is a base64-encoded credential id.
85The user will be prompted for the PIN.
86.It Fl L
87Produces a list of authenticators found by the operating system.
88.It Fl L Fl e Ar device
89Produces a list of biometric enrollments on
90.Ar device .
91The user will be prompted for the PIN.
92.It Fl L Fl r Ar device
93Produces a list of relying parties with resident credentials on
94.Ar device .
95The user will be prompted for the PIN.
96.It Fl L Fl k Ar rp_id Ar device
97Produces a list of resident credentials corresponding to
98relying party
99.Ar rp_id
100on
101.Ar device .
102The user will be prompted for the PIN.
103.It Fl R
104Performs a reset on
105.Ar device .
106.Nm
107will NOT prompt for confirmation.
108.It Fl S
109Sets the PIN of
110.Ar device .
111The user will be prompted for the PIN.
112.It Fl S Fl e Ar device
113Performs a new biometric enrollment on
114.Ar device .
115The user will be prompted for the PIN.
116.It Fl S Fl e Fl i Ar template_id Fl n Ar template_name Ar device
117Sets the friendly name of the biometric enrollment specified by
118.Ar template_id
119to
120.Ar template_name
121on
122.Ar device ,
123where
124.Ar template_id
125is base64-encoded and
126.Ar template_name
127is a UTF-8 string.
128The user will be prompted for the PIN.
129.It Fl V
130Prints version information.
131.It Fl d
132Causes
133.Nm
134to emit debugging output on
135.Em stderr .
136.El
137.Pp
138If a
139.Em tty
140is available,
141.Nm
142will use it to prompt for PINs.
143Otherwise,
144.Em stdin
145is used.
146.Pp
147.Nm
148exits 0 on success and 1 on error.
149.Sh SEE ALSO
150.Xr fido2-assert 1 ,
151.Xr fido2-cred 1
152.Sh CAVEATS
153The actual user-flow to perform a reset is outside the scope of the
154FIDO2 specification, and may therefore vary depending on the
155authenticator.
156Yubico authenticators do not allow resets after 5 seconds from
157power-up, and expect a reset to be confirmed by the user through
158touch within 30 seconds.
diff --git a/man/fido_assert_allow_cred.3 b/man/fido_assert_allow_cred.3
new file mode 100644
index 0000000..bbe6e4d
--- /dev/null
+++ b/man/fido_assert_allow_cred.3
@@ -0,0 +1,47 @@
1.\" Copyright (c) 2018 Yubico AB. All rights reserved.
2.\" Use of this source code is governed by a BSD-style
3.\" license that can be found in the LICENSE file.
4.\"
5.Dd $Mdocdate: May 23 2018 $
6.Dt FIDO_ASSERT_ALLOW_CRED 3
7.Os
8.Sh NAME
9.Nm fido_assert_allow_cred
10.Nd appends a credential ID to the list of credentials allowed in an assertion
11.Sh SYNOPSIS
12.In fido.h
13.Ft int
14.Fn fido_assert_allow_cred "fido_assert_t *assert" "const unsigned char *ptr" "size_t len"
15.Sh DESCRIPTION
16The
17.Fn fido_assert_allow_cred
18function adds
19.Fa ptr
20to the list of credentials allowed in
21.Fa assert ,
22where
23.Fa ptr
24points to a credential ID of
25.Fa len
26bytes.
27A copy of
28.Fa ptr
29is made, and no references to the passed pointer are kept.
30If
31.Fn fido_assert_allow_cred
32fails, the existing list of allowed credentials is preserved.
33.Pp
34For the format of a FIDO 2 credential ID, please refer to the
35Web Authentication (webauthn) standard.
36.Sh RETURN VALUES
37The error codes returned by
38.Fn fido_assert_allow_cred
39are defined in
40.In fido/err.h .
41On success,
42.Dv FIDO_OK
43is returned.
44.Sh SEE ALSO
45.Xr fido_assert_new 3 ,
46.Xr fido_assert_set_authdata 3 ,
47.Xr fido_dev_get_assert 3
diff --git a/man/fido_assert_new.3 b/man/fido_assert_new.3
new file mode 100644
index 0000000..0c2f92f
--- /dev/null
+++ b/man/fido_assert_new.3
@@ -0,0 +1,190 @@
1.\" Copyright (c) 2018 Yubico AB. All rights reserved.
2.\" Use of this source code is governed by a BSD-style
3.\" license that can be found in the LICENSE file.
4.\"
5.Dd $Mdocdate: October 22 2019 $
6.Dt FIDO_ASSERT_NEW 3
7.Os
8.Sh NAME
9.Nm fido_assert_new ,
10.Nm fido_assert_free ,
11.Nm fido_assert_count ,
12.Nm fido_assert_user_display_name ,
13.Nm fido_assert_user_icon ,
14.Nm fido_assert_user_name ,
15.Nm fido_assert_authdata_ptr ,
16.Nm fido_assert_clientdata_hash_ptr ,
17.Nm fido_assert_hmac_secret_ptr ,
18.Nm fido_assert_user_id_ptr ,
19.Nm fido_assert_sig_ptr ,
20.Nm fido_assert_authdata_len ,
21.Nm fido_assert_clientdata_hash_len ,
22.Nm fido_assert_hmac_secret_len ,
23.Nm fido_assert_user_id_len ,
24.Nm fido_assert_sig_len ,
25.Nm fido_assert_sigcount
26.Nd FIDO 2 assertion API
27.Sh SYNOPSIS
28.In fido.h
29.Ft fido_assert_t *
30.Fn fido_assert_new "void"
31.Ft void
32.Fn fido_assert_free "fido_assert_t **assert_p"
33.Ft size_t
34.Fn fido_assert_count "const fido_assert_t *assert"
35.Ft const char *
36.Fn fido_assert_user_display_name "const fido_assert_t *assert" "size_t idx"
37.Ft const char *
38.Fn fido_assert_user_icon "const fido_assert_t *assert" "size_t idx"
39.Ft const char *
40.Fn fido_assert_user_name "const fido_assert_t *assert" "size_t idx"
41.Ft const unsigned char *
42.Fn fido_assert_authdata_ptr "const fido_assert_t *assert" "size_t idx"
43.Ft const unsigned char *
44.Fn fido_assert_clientdata_hash_ptr "const fido_assert_t *assert"
45.Ft const unsigned char *
46.Fn fido_assert_hmac_secret_ptr "const fido_assert_t *assert" "size_t idx"
47.Ft const unsigned char *
48.Fn fido_assert_user_id_ptr "const fido_assert_t *assert" "size_t idx"
49.Ft const unsigned char *
50.Fn fido_assert_sig_ptr "const fido_assert_t *assert" "size_t idx"
51.Ft size_t
52.Fn fido_assert_authdata_len "const fido_assert_t *assert" "size_t idx"
53.Ft size_t
54.Fn fido_assert_clientdata_hash_len "const fido_assert_t *assert"
55.Ft size_t
56.Fn fido_assert_hmac_secret_len "const fido_assert_t *assert" "size_t idx"
57.Ft size_t
58.Fn fido_assert_user_id_len "const fido_assert_t *assert" "size_t idx"
59.Ft size_t
60.Fn fido_assert_sig_len "const fido_assert_t *assert" "size_t idx"
61.Ft uint32_t
62.Fn fido_assert_sigcount "const fido_assert_t *assert" "size_t idx"
63.Sh DESCRIPTION
64FIDO 2 assertions are abstracted in
65.Em libfido2
66by the
67.Vt fido_assert_t
68type.
69The functions described in this page allow a
70.Vt fido_assert_t
71type to be allocated, deallocated, and inspected.
72For other operations on
73.Vt fido_assert_t ,
74please refer to
75.Xr fido_assert_set_authdata 3 ,
76.Xr fido_assert_allow_cred 3 ,
77.Xr fido_assert_verify 3 ,
78and
79.Xr fido_dev_get_assert 3 .
80.Pp
81The
82.Fn fido_assert_new
83function returns a pointer to a newly allocated, empty
84.Vt fido_assert_t
85type.
86If memory cannot be allocated, NULL is returned.
87.Pp
88The
89.Fn fido_assert_free
90function releases the memory backing
91.Fa *assert_p ,
92where
93.Fa *assert_p
94must have been previously allocated by
95.Fn fido_assert_new .
96On return,
97.Fa *assert_p
98is set to NULL.
99Either
100.Fa assert_p
101or
102.Fa *assert_p
103may be NULL, in which case
104.Fn fido_assert_free
105is a NOP.
106.Pp
107The
108.Fn fido_assert_count
109function returns the number of statements in
110.Fa assert .
111.Pp
112The
113.Fn fido_assert_user_display_name ,
114.Fn fido_assert_user_icon ,
115and
116.Fn fido_assert_user_name ,
117functions return pointers to the user display name, icon, and
118name attributes of statement
119.Fa idx
120in
121.Fa assert .
122If not NULL, the values returned by these functions point to
123NUL-terminated UTF-8 strings.
124.Pp
125The
126.Fn fido_assert_user_id_ptr ,
127.Fn fido_assert_authdata_ptr ,
128.Fn fido_assert_hmac_secret_ptr ,
129and
130.Fn fido_assert_sig_ptr
131functions return pointers to the user ID, authenticator data,
132hmac-secret, and signature attributes of statement
133.Fa idx
134in
135.Fa assert .
136The
137.Fn fido_assert_user_id_len ,
138.Fn fido_assert_authdata_len ,
139.Fn fido_assert_hmac_secret_len ,
140and
141.Fn fido_assert_sig_len
142functions can be used to retrieve the corresponding length of a
143specific attribute.
144.Pp
145The
146.Fn fido_assert_sigcount
147function can be used to obtain the signature counter of statement
148.Fa idx
149in
150.Fa assert .
151.Pp
152Please note that the first statement in
153.Fa assert
154has an
155.Fa idx
156(index) value of 0.
157.Pp
158The authenticator data and signature parts of an assertion
159statement are typically passed to a FIDO 2 server for verification.
160.Pp
161The
162.Fn fido_assert_clientdata_hash_ptr
163function returns a pointer to the client data hash of
164.Fa assert .
165The corresponding length can be obtained by
166.Fn fido_assert_clientdata_hash_len .
167.Sh RETURN VALUES
168The
169.Fn fido_assert_user_display_name ,
170.Fn fido_assert_user_icon ,
171.Fn fido_assert_user_name ,
172.Fn fido_assert_authdata_ptr ,
173.Fn fido_assert_clientdata_hash_ptr ,
174.Fn fido_assert_user_id_ptr ,
175and
176.Fn fido_assert_sig_ptr
177functions return NULL if the respective field in
178.Fa assert
179is not set.
180If not NULL, returned pointers are guaranteed to exist until any API
181function that takes
182.Fa assert
183without the
184.Em const
185qualifier is invoked.
186.Sh SEE ALSO
187.Xr fido_assert_allow_cred 3 ,
188.Xr fido_assert_set_authdata 3 ,
189.Xr fido_assert_verify 3 ,
190.Xr fido_dev_get_assert 3
diff --git a/man/fido_assert_set_authdata.3 b/man/fido_assert_set_authdata.3
new file mode 100644
index 0000000..29a86c1
--- /dev/null
+++ b/man/fido_assert_set_authdata.3
@@ -0,0 +1,194 @@
1.\" Copyright (c) 2018 Yubico AB. All rights reserved.
2.\" Use of this source code is governed by a BSD-style
3.\" license that can be found in the LICENSE file.
4.\"
5.Dd $Mdocdate: May 23 2018 $
6.Dt FIDO_ASSERT_SET_AUTHDATA 3
7.Os
8.Sh NAME
9.Nm fido_assert_set_authdata ,
10.Nm fido_assert_set_authdata_raw ,
11.Nm fido_assert_set_clientdata_hash ,
12.Nm fido_assert_set_count ,
13.Nm fido_assert_set_extensions ,
14.Nm fido_assert_set_hmac_salt ,
15.Nm fido_assert_set_up ,
16.Nm fido_assert_set_uv ,
17.Nm fido_assert_set_rp ,
18.Nm fido_assert_set_sig
19.Nd set parameters of a FIDO 2 assertion
20.Sh SYNOPSIS
21.In fido.h
22.Bd -literal
23typedef enum {
24 FIDO_OPT_OMIT = 0, /* use authenticator's default */
25 FIDO_OPT_FALSE, /* explicitly set option to false */
26 FIDO_OPT_TRUE, /* explicitly set option to true */
27} fido_opt_t;
28.Ed
29.Ft int
30.Fn fido_assert_set_authdata "fido_assert_t *assert" " size_t idx" "const unsigned char *ptr" "size_t len"
31.Ft int
32.Fn fido_assert_set_authdata_raw "fido_assert_t *assert" " size_t idx" "const unsigned char *ptr" "size_t len"
33.Ft int
34.Fn fido_assert_set_clientdata_hash "fido_assert_t *assert" "const unsigned char *ptr" "size_t len"
35.Ft int
36.Fn fido_assert_set_count "fido_assert_t *assert" "size_t n"
37.Ft int
38.Fn fido_assert_set_extensions "fido_assert_t *assert" "int flags"
39.Ft int
40.Fn fido_assert_set_hmac_salt "fido_assert_t *assert" "const unsigned char *ptr" "size_t len"
41.Ft int
42.Fn fido_assert_set_up "fido_assert_t *assert" "fido_opt_t up"
43.Ft int
44.Fn fido_assert_set_uv "fido_assert_t *assert" "fido_opt_t uv"
45.Ft int
46.Fn fido_assert_set_rp "fido_assert_t *assert" "const char *id"
47.Ft int
48.Fn fido_assert_set_sig "fido_assert_t *assert" "size_t idx" "const unsigned char *ptr" "size_t len"
49.Sh DESCRIPTION
50The
51.Nm
52set of functions define the various parameters of a FIDO 2
53assertion, allowing a
54.Fa fido_assert_t
55type to be prepared for a subsequent call to
56.Xr fido_dev_get_assert 3
57or
58.Xr fido_assert_verify 3 .
59For the complete specification of a FIDO 2 assertion and the format
60of its constituent parts, please refer to the Web Authentication
61(webauthn) standard.
62.Pp
63The
64.Fn fido_assert_set_count
65function sets the number of assertion statements in
66.Fa assert
67to
68.Fa n .
69.Pp
70The
71.Fn fido_assert_set_authdata
72and
73.Fn fido_assert_set_sig
74functions set the authenticator data and signature parts of the
75statement with index
76.Fa idx
77of
78.Fa assert
79to
80.Fa ptr ,
81where
82.Fa ptr
83points to
84.Fa len
85bytes.
86A copy of
87.Fa ptr
88is made, and no references to the passed pointer are kept.
89Please note that the first assertion statement of
90.Fa assert
91has an
92.Fa idx
93of
94.Em 0 .
95The authenticator data passed to
96.Fn fido_assert_set_authdata
97must be a CBOR-encoded byte string, as obtained from
98.Fn fido_assert_authdata_ptr .
99Alternatively, a raw binary blob may be passed to
100.Fn fido_assert_set_authdata_raw .
101.Pp
102The
103.Fn fido_assert_set_clientdata_hash
104and
105.Fn fido_assert_set_hmac_salt
106functions set the client data hash and hmac-salt parts of
107.Fa assert
108to
109.Fa ptr ,
110where
111.Fa ptr
112points to
113.Fa len
114bytes.
115A copy of
116.Fa ptr
117is made, and no references to the passed pointer are kept.
118.Pp
119The
120.Fn fido_assert_set_rp
121function sets the relying party
122.Fa id
123of
124.Fa assert ,
125where
126.Fa id
127is a NUL-terminated UTF-8 string.
128The content of
129.Fa id
130is copied, and no references to the passed pointer are kept.
131.Pp
132The
133.Fn fido_assert_set_extensions
134function sets the extensions of
135.Fa assert
136to the bitmask
137.Fa flags .
138At the moment, only the
139.Dv FIDO_EXT_HMAC_SECRET
140extension is supported.
141If
142.Fa flags
143is zero, the extensions of
144.Fa assert
145are cleared.
146.Pp
147The
148.Fn fido_assert_set_up
149and
150.Fn fido_assert_set_uv
151functions set the
152.Fa up
153(user presence) and
154.Fa uv
155(user verification)
156attributes of
157.Fa assert .
158Both are
159.Dv FIDO_OPT_OMIT
160by default, allowing the authenticator to use its default settings.
161.Pp
162Use of the
163.Nm
164set of functions may happen in two distinct situations:
165when asking a FIDO device to produce a series of assertion
166statements, prior to
167.Xr fido_dev_get_assert 3
168(i.e, in the context of a FIDO client), or when verifying assertion
169statements using
170.Xr fido_assert_verify 3
171(i.e, in the context of a FIDO server).
172.Pp
173For a complete description of the generation of a FIDO 2 assertion
174and its verification, please refer to the FIDO 2 specification.
175An example of how to use the
176.Nm
177set of functions can be found in the
178.Pa examples/assert.c
179file shipped with
180.Em libfido2 .
181.Sh RETURN VALUES
182The
183.Nm
184functions return
185.Dv FIDO_OK
186on success.
187The error codes returned by the
188.Nm
189set of functions are defined in
190.In fido/err.h .
191.Sh SEE ALSO
192.Xr fido_assert_allow_cred 3 ,
193.Xr fido_assert_verify 3 ,
194.Xr fido_dev_get_assert 3
diff --git a/man/fido_assert_verify.3 b/man/fido_assert_verify.3
new file mode 100644
index 0000000..82e64e1
--- /dev/null
+++ b/man/fido_assert_verify.3
@@ -0,0 +1,79 @@
1.\" Copyright (c) 2018 Yubico AB. All rights reserved.
2.\" Use of this source code is governed by a BSD-style
3.\" license that can be found in the LICENSE file.
4.\"
5.Dd $Mdocdate: May 24 2018 $
6.Dt FIDO_ASSERT_VERIFY 3
7.Os
8.Sh NAME
9.Nm fido_assert_verify
10.Nd verifies the signature of a FIDO 2 assertion statement
11.Sh SYNOPSIS
12.In fido.h
13.Ft int
14.Fn fido_assert_verify "fido_assert_t *assert" "size_t idx" "int cose_alg" "const void *pk"
15.Sh DESCRIPTION
16The
17.Fn fido_assert_verify
18function verifies whether the signature contained in statement index
19.Fa idx
20of
21.Fa assert
22matches the parameters of the assertion.
23Before using
24.Fn fido_assert_verify
25in a sensitive context, the reader is strongly encouraged to make
26herself familiar with the FIDO 2 assertion statement process
27as defined in the Web Authentication (webauthn) standard.
28.Pp
29A brief description follows:
30.Pp
31The
32.Fn fido_assert_verify
33function verifies whether the client data hash, relying party ID,
34user presence and user verification attributes of
35.Fa assert
36have been attested by the holder of the private counterpart of
37the public key
38.Fa pk
39of COSE type
40.Fa cose_alg ,
41where
42.Fa cose_alg
43is
44.Dv COSE_ES256 ,
45.Dv COSE_RS256 ,
46or
47.Dv COSE_EDDSA ,
48and
49.Fa pk
50points to a
51.Vt es256_pk_t ,
52.Vt rs256_pk_t ,
53or
54.Vt eddsa_pk_t
55type accordingly.
56.Pp
57Please note that the first statement in
58.Fa assert
59has an
60.Fa idx
61of 0.
62.Sh RETURN VALUES
63The error codes returned by
64.Fn fido_assert_verify
65are defined in
66.In fido/err.h .
67If
68statement
69.Fa idx
70of
71.Fa assert
72passes verification with
73.Fa pk ,
74then
75.Dv FIDO_OK
76is returned.
77.Sh SEE ALSO
78.Xr fido_assert_new 3 ,
79.Xr fido_assert_set_authdata 3
diff --git a/man/fido_bio_dev_get_info.3 b/man/fido_bio_dev_get_info.3
new file mode 100644
index 0000000..bde1396
--- /dev/null
+++ b/man/fido_bio_dev_get_info.3
@@ -0,0 +1,120 @@
1.\" Copyright (c) 2019 Yubico AB. All rights reserved.
2.\" Use of this source code is governed by a BSD-style
3.\" license that can be found in the LICENSE file.
4.\"
5.Dd $Mdocdate: September 13 2019 $
6.Dt FIDO_BIO_DEV_GET_INFO 3
7.Os
8.Sh NAME
9.Nm fido_bio_dev_get_info ,
10.Nm fido_bio_dev_enroll_begin ,
11.Nm fido_bio_dev_enroll_continue ,
12.Nm fido_bio_dev_enroll_cancel ,
13.Nm fido_bio_dev_enroll_remove ,
14.Nm fido_bio_dev_get_template_array ,
15.Nm fido_bio_dev_set_template_name
16.Nd FIDO 2 biometric authenticator API
17.Sh SYNOPSIS
18.In fido.h
19.In fido/bio.h
20.Ft int
21.Fn fido_bio_dev_get_info "fido_dev_t *dev" "fido_bio_info_t *info"
22.Ft int
23.Fn fido_bio_dev_enroll_begin "fido_dev_t *dev" "fido_bio_template_t *template" "fido_bio_enroll_t *enroll" "uint32_t timeout_ms" "const char *pin"
24.Ft int
25.Fn fido_bio_dev_enroll_continue "fido_dev_t *dev" "const fido_bio_template_t *template" "fido_bio_enroll_t *enroll" "uint32_t timeout_ms"
26.Ft int
27.Fn fido_bio_dev_enroll_cancel "fido_dev_t *dev"
28.Ft int
29.Fn fido_bio_dev_enroll_remove "fido_dev_t *dev" "const fido_bio_template_t *template" "const char *pin"
30.Ft int
31.Fn fido_bio_dev_get_template_array "fido_dev_t *dev" "fido_bio_template_array_t *template_array" "const char *pin"
32.Ft int
33.Fn fido_bio_dev_set_template_name "fido_dev_t *dev" "const fido_bio_template_t *template" "const char *pin"
34.Sh DESCRIPTION
35The functions described in this page allow biometric
36templates on a FIDO2 authenticator to be listed, created,
37removed, and customised.
38For a description of the types involved, please refer to
39.Xr fido_bio_info_new 3 ,
40.Xr fido_bio_enroll_new 3 ,
41and
42.Xr fido_bio_template 3 .
43.Pp
44The
45.Fn fido_bio_dev_get_info
46function populates
47.Fa info
48with sensor information from
49.Fa dev .
50.Pp
51The
52.Fn fido_bio_dev_enroll_begin
53function initiates a biometric enrollment on
54.Fa dev ,
55instructing the authenticator to wait
56.Fa timeout_ms
57milliseconds.
58On success,
59.Fa template
60and
61.Fa enroll
62will be populated with the newly created template's
63information and enrollment status, respectively.
64.Pp
65The
66.Fn fido_bio_dev_enroll_continue
67function continues an ongoing enrollment on
68.Fa dev ,
69instructing the authenticator to wait
70.Fa timeout_ms
71milliseconds.
72On success,
73.Fa enroll
74will be updated to reflect the status of the biometric
75enrollment.
76.Pp
77The
78.Fn fido_bio_dev_enroll_cancel
79function cancels an ongoing enrollment on
80.Fa dev .
81.Pp
82The
83.Fn fido_bio_dev_enroll_remove
84function removes
85.Fa template
86from
87.Fa dev .
88.Pp
89The
90.Fn fido_bio_dev_get_template_array
91function populates
92.Fa template_array
93with the templates currently enrolled on
94.Fa dev .
95.Pp
96The
97.Fn fido_bio_dev_set_template_name
98function sets the friendly name of
99.Fa template
100on
101.Fa dev .
102.Pp
103The error codes returned by
104.Fn fido_bio_dev_get_info ,
105.Fn fido_bio_dev_enroll_begin ,
106.Fn fido_bio_dev_enroll_continue ,
107.Fn fido_bio_dev_enroll_cancel ,
108.Fn fido_bio_dev_enroll_remove ,
109.Fn fido_bio_dev_get_template_array ,
110and
111.Fn fido_bio_dev_set_template_name
112are defined in
113.In fido/err.h .
114On success,
115.Dv FIDO_OK
116is returned.
117.Sh SEE ALSO
118.Xr fido_bio_enroll_new 3 ,
119.Xr fido_bio_info_new 3 ,
120.Xr fido_bio_template 3
diff --git a/man/fido_bio_enroll_new.3 b/man/fido_bio_enroll_new.3
new file mode 100644
index 0000000..3db3e7a
--- /dev/null
+++ b/man/fido_bio_enroll_new.3
@@ -0,0 +1,95 @@
1.\" Copyright (c) 2019 Yubico AB. All rights reserved.
2.\" Use of this source code is governed by a BSD-style
3.\" license that can be found in the LICENSE file.
4.\"
5.Dd $Mdocdate: September 13 2019 $
6.Dt FIDO_BIO_ENROLL_NEW 3
7.Os
8.Sh NAME
9.Nm fido_bio_enroll_new ,
10.Nm fido_bio_enroll_free ,
11.Nm fido_bio_enroll_last_status ,
12.Nm fido_bio_enroll_remaining_samples
13.Nd FIDO 2 biometric enrollment API
14.Sh SYNOPSIS
15.In fido.h
16.In fido/bio.h
17.Bd -literal
18#define FIDO_BIO_ENROLL_FP_GOOD 0x00
19#define FIDO_BIO_ENROLL_FP_TOO_HIGH 0x01
20#define FIDO_BIO_ENROLL_FP_TOO_LOW 0x02
21#define FIDO_BIO_ENROLL_FP_TOO_LEFT 0x03
22#define FIDO_BIO_ENROLL_FP_TOO_RIGHT 0x04
23#define FIDO_BIO_ENROLL_FP_TOO_FAST 0x05
24#define FIDO_BIO_ENROLL_FP_TOO_SLOW 0x06
25#define FIDO_BIO_ENROLL_FP_POOR_QUALITY 0x07
26#define FIDO_BIO_ENROLL_FP_TOO_SKEWED 0x08
27#define FIDO_BIO_ENROLL_FP_TOO_SHORT 0x09
28#define FIDO_BIO_ENROLL_FP_MERGE_FAILURE 0x0a
29#define FIDO_BIO_ENROLL_FP_EXISTS 0x0b
30#define FIDO_BIO_ENROLL_FP_DATABASE_FULL 0x0c
31#define FIDO_BIO_ENROLL_NO_USER_ACTIVITY 0x0d
32#define FIDO_BIO_ENROLL_NO_USER_PRESENCE_TRANSITION 0x0e
33.Ed
34.Ft fido_bio_enroll_t *
35.Fn fido_bio_enroll_new "void"
36.Ft void
37.Fn fido_bio_enroll_free "fido_bio_enroll_t **enroll_p"
38.Ft uint8_t
39.Fn fido_bio_enroll_last_status "const fido_bio_enroll_t *enroll"
40.Ft uint8_t
41.Fn fido_bio_enroll_remaining_samples "const fido_bio_enroll_t *enroll"
42.Sh DESCRIPTION
43Ongoing FIDO 2 biometric enrollments are abstracted in
44.Em libfido2
45by the
46.Vt fido_bio_enroll_t
47type.
48.Pp
49The functions described in this page allow a
50.Vt fido_bio_enroll_t
51type to be allocated, deallocated, and inspected.
52For device operations on
53.Vt fido_bio_enroll_t ,
54please refer to
55.Xr fido_bio_dev_get_info 3 .
56.Pp
57The
58.Fn fido_bio_enroll_new
59function returns a pointer to a newly allocated, empty
60.Vt fido_bio_enroll_t
61type.
62If memory cannot be allocated, NULL is returned.
63.Pp
64The
65.Fn fido_bio_enroll_free
66function releases the memory backing
67.Fa *enroll_p ,
68where
69.Fa *enroll_p
70must have been previously allocated by
71.Fn fido_bio_enroll_new .
72On return,
73.Fa *enroll_p
74is set to NULL.
75Either
76.Fa enroll_p
77or
78.Fa *enroll_p
79may be NULL, in which case
80.Fn fido_bio_enroll_free
81is a NOP.
82.Pp
83The
84.Fn fido_bio_enroll_last_status
85function returns the enrollment status of
86.Fa enroll .
87.Pp
88The
89.Fn fido_bio_enroll_remaining_samples
90function returns the number of samples left for
91.Fa enroll
92to complete.
93.Sh SEE ALSO
94.Xr fido_bio_dev_get_info 3 ,
95.Xr fido_bio_template 3
diff --git a/man/fido_bio_info_new.3 b/man/fido_bio_info_new.3
new file mode 100644
index 0000000..c827333
--- /dev/null
+++ b/man/fido_bio_info_new.3
@@ -0,0 +1,81 @@
1.\" Copyright (c) 2019 Yubico AB. All rights reserved.
2.\" Use of this source code is governed by a BSD-style
3.\" license that can be found in the LICENSE file.
4.\"
5.Dd $Mdocdate: September 13 2019 $
6.Dt FIDO_BIO_INFO_NEW 3
7.Os
8.Sh NAME
9.Nm fido_bio_info_new ,
10.Nm fido_bio_info_free ,
11.Nm fido_bio_info_type ,
12.Nm fido_bio_info_max_samples
13.Nd FIDO 2 biometric sensor information API
14.Sh SYNOPSIS
15.In fido.h
16.In fido/bio.h
17.Ft fido_bio_info_t *
18.Fn fido_bio_info_new "void"
19.Ft void
20.Fn fido_bio_info_free "fido_bio_info_t **info_p"
21.Ft uint8_t
22.Fn fido_bio_info_type "const fido_bio_info_t *info"
23.Ft uint8_t
24.Fn fido_bio_info_max_samples "const fido_bio_info_t *info"
25.Sh DESCRIPTION
26Biometric sensor metadata is abstracted in
27.Em libfido2
28by the
29.Vt fido_bio_info_t
30type.
31.Pp
32The functions described in this page allow a
33.Vt fido_bio_info_t
34type to be allocated, deallocated, and inspected.
35For device operations on
36.Vt fido_bio_info_t ,
37please refer to
38.Xr fido_bio_dev_get_info 3 .
39.Pp
40The
41.Fn fido_bio_info_new
42function returns a pointer to a newly allocated, empty
43.Vt fido_bio_info_t
44type.
45If memory cannot be allocated, NULL is returned.
46.Pp
47The
48.Fn fido_bio_info_free
49function releases the memory backing
50.Fa *info_p ,
51where
52.Fa *info_p
53must have been previously allocated by
54.Fn fido_bio_info_new .
55On return,
56.Fa *info_p
57is set to NULL.
58Either
59.Fa info_p
60or
61.Fa *info_p
62may be NULL, in which case
63.Fn fido_bio_info_free
64is a NOP.
65.Pp
66The
67.Fn fido_bio_info_type
68function returns the fingerprint sensor type, which is
69.Dv 1
70for touch sensors, and
71.Dv 2
72for swipe sensors.
73.Pp
74The
75.Fn fido_bio_info_max_samples
76function returns the maximum number of successful samples
77required for enrollment.
78.Sh SEE ALSO
79.Xr fido_bio_dev_get_info 3 ,
80.Xr fido_bio_enroll_new 3 ,
81.Xr fido_bio_template 3
diff --git a/man/fido_bio_template.3 b/man/fido_bio_template.3
new file mode 100644
index 0000000..6140926
--- /dev/null
+++ b/man/fido_bio_template.3
@@ -0,0 +1,169 @@
1.\" Copyright (c) 2019 Yubico AB. All rights reserved.
2.\" Use of this source code is governed by a BSD-style
3.\" license that can be found in the LICENSE file.
4.\"
5.Dd $Mdocdate: September 13 2019 $
6.Dt FIDO_BIO_TEMPLATE 3
7.Os
8.Sh NAME
9.Nm fido_bio_template ,
10.Nm fido_bio_template_array_count ,
11.Nm fido_bio_template_array_free ,
12.Nm fido_bio_template_array_new ,
13.Nm fido_bio_template_free ,
14.Nm fido_bio_template_id_len ,
15.Nm fido_bio_template_id_ptr ,
16.Nm fido_bio_template_name ,
17.Nm fido_bio_template_new ,
18.Nm fido_bio_template_set_id ,
19.Nm fido_bio_template_set_name
20.Nd FIDO 2 biometric template API
21.Sh SYNOPSIS
22.In fido.h
23.In fido/bio.h
24.Ft fido_bio_template_t *
25.Fn fido_bio_template_new "void"
26.Ft void
27.Fn fido_bio_template_free "fido_bio_template_t **template_p"
28.Ft const char *
29.Fn fido_bio_template_name "const fido_bio_template_t *template"
30.Ft const unsigned char *
31.Fn fido_bio_template_id_ptr "const fido_bio_template_t *template"
32.Ft size_t
33.Fn fido_bio_template_id_len "const fido_bio_template_t *template"
34.Ft int
35.Fn fido_bio_template_set_id "fido_bio_template_t *template" "const unsigned char *ptr" "size_t len"
36.Ft int
37.Fn fido_bio_template_set_name "fido_bio_template_t *template" "const char *name"
38.Ft fido_bio_template_array_t *
39.Fn fido_bio_template_array_new "void"
40.Ft void
41.Fn fido_bio_template_array_free "fido_bio_template_array_t **template_array_p"
42.Ft size_t
43.Fn fido_bio_template_array_count "const fido_bio_template_array_t *template_array"
44.Ft const fido_bio_template_t *
45.Fn fido_bio_template "const fido_bio_template_array_t *template_array" "size_t idx"
46.Sh DESCRIPTION
47Existing FIDO 2 biometric enrollments are abstracted in
48.Em libfido2
49by the
50.Vt fido_bio_template_t
51and
52.Vt fido_bio_template_array_t
53types.
54.Pp
55The functions described in this page allow a
56.Vt fido_bio_template_t
57type to be allocated, deallocated, changed, and inspected,
58and a
59.Vt fido_bio_template_array_t
60type to be allocated, deallocated, and inspected.
61For device operations on
62.Vt fido_bio_template_t
63and
64.Vt fido_bio_template_array_t ,
65please refer to
66.Xr fido_bio_dev_get_info 3 .
67.Pp
68The
69.Fn fido_bio_template_new
70function returns a pointer to a newly allocated, empty
71.Vt fido_bio_template_t
72type.
73If memory cannot be allocated, NULL is returned.
74.Pp
75The
76.Fn fido_bio_template_free
77function releases the memory backing
78.Fa *template_p ,
79where
80.Fa *template_p
81must have been previously allocated by
82.Fn fido_bio_template_new .
83On return,
84.Fa *template_p
85is set to NULL.
86Either
87.Fa template_p
88or
89.Fa *template_p
90may be NULL, in which case
91.Fn fido_bio_template_free
92is a NOP.
93.Pp
94The
95.Fn fido_bio_template_name
96function returns a pointer to a NUL-terminated string containing
97the friendly name of
98.Fa template ,
99or NULL if
100.Fa template
101does not have a friendly name set.
102.Pp
103The
104.Fn fido_bio_template_id_ptr
105function returns a pointer to the template id of
106.Fa template ,
107or NULL if
108.Fa template
109does not have an id.
110The corresponding length can be obtained by
111.Fn fido_bio_template_id_len .
112.Pp
113The
114.Fn fido_bio_template_set_name
115function sets the friendly name of
116.Fa template
117to
118.Fa name .
119If
120.Fa name
121is NULL, the friendly name of
122.Fa template
123is unset.
124.Pp
125The
126.Fn fido_bio_template_array_new
127function returns a pointer to a newly allocated, empty
128.Vt fido_bio_template_array_t
129type.
130If memory cannot be allocated, NULL is returned.
131.Pp
132The
133.Fn fido_bio_template_array_free
134function releases the memory backing
135.Fa *template_array_p ,
136where
137.Fa *template_array_p
138must have been previously allocated by
139.Fn fido_bio_template_array_new .
140On return,
141.Fa *template_array_p
142is set to NULL.
143Either
144.Fa template_array_p
145or
146.Fa *template_array_p
147may be NULL, in which case
148.Fn fido_bio_template_array_free
149is a NOP.
150.Pp
151The
152.Fn fido_bio_template_array_count
153function returns the number of templates in
154.Fa template_array .
155.Pp
156The
157.Fn fido_bio_template
158function returns a pointer to the template at index
159.Fa idx
160in
161.Fa template_array .
162Please note that the first template in
163.Fa template_array
164has an
165.Fa idx
166(index) value of 0.
167.Sh SEE ALSO
168.Xr fido_bio_dev_get_info 3 ,
169.Xr fido_bio_enroll_new 3
diff --git a/man/fido_cbor_info_new.3 b/man/fido_cbor_info_new.3
new file mode 100644
index 0000000..d1e4c41
--- /dev/null
+++ b/man/fido_cbor_info_new.3
@@ -0,0 +1,153 @@
1.\" Copyright (c) 2018 Yubico AB. All rights reserved.
2.\" Use of this source code is governed by a BSD-style
3.\" license that can be found in the LICENSE file.
4.\"
5.Dd $Mdocdate: May 24 2018 $
6.Dt FIDO_CBOR_INFO_NEW 3
7.Os
8.Sh NAME
9.Nm fido_cbor_info_new ,
10.Nm fido_cbor_info_free ,
11.Nm fido_dev_get_cbor_info ,
12.Nm fido_cbor_info_aaguid_ptr ,
13.Nm fido_cbor_info_extensions_ptr ,
14.Nm fido_cbor_info_protocols_ptr ,
15.Nm fido_cbor_info_versions_ptr ,
16.Nm fido_cbor_info_options_name_ptr ,
17.Nm fido_cbor_info_options_value_ptr ,
18.Nm fido_cbor_info_aaguid_len ,
19.Nm fido_cbor_info_extensions_len ,
20.Nm fido_cbor_info_protocols_len ,
21.Nm fido_cbor_info_versions_len ,
22.Nm fido_cbor_info_options_len ,
23.Nm fido_cbor_info_maxmsgsiz
24.Nd FIDO 2 CBOR Info API
25.Sh SYNOPSIS
26.In fido.h
27.Ft fido_cbor_info_t *
28.Fn fido_cbor_info_new "void"
29.Ft void
30.Fn fido_cbor_info_free "fido_cbor_info_t **ci_p"
31.Ft int
32.Fn fido_dev_get_cbor_info "fido_dev_t *dev" "fido_cbor_info_t *ci"
33.Ft const unsigned char *
34.Fn fido_cbor_info_aaguid_ptr "const fido_cbor_info_t *ci"
35.Ft char **
36.Fn fido_cbor_info_extensions_ptr "const fido_cbor_info_t *ci"
37.Ft const uint8_t *
38.Fn fido_cbor_info_protocols_ptr "const fido_cbor_info_t *ci"
39.Ft char **
40.Fn fido_cbor_info_versions_ptr "const fido_cbor_info_t *ci"
41.Ft char **
42.Fn fido_cbor_info_options_name_ptr "const fido_cbor_info_t *ci"
43.Ft const bool *
44.Fn fido_cbor_info_options_value_ptr "const fido_cbor_info_t *ci"
45.Ft size_t
46.Fn fido_cbor_info_aaguid_len "const fido_cbor_info_t *ci"
47.Ft size_t
48.Fn fido_cbor_info_extensions_len "const fido_cbor_info_t *ci"
49.Ft size_t
50.Fn fido_cbor_info_protocols_len "const fido_cbor_info_t *ci"
51.Ft size_t
52.Fn fido_cbor_info_versions_len "const fido_cbor_info_t *ci"
53.Ft size_t
54.Fn fido_cbor_info_options_len "const fido_cbor_info_t *ci"
55.Ft uint64_t
56.Fn fido_cbor_info_maxmsgsiz "const fido_cbor_info_t *ci"
57.Sh DESCRIPTION
58The
59.Fn fido_cbor_info_new
60function returns a pointer to a newly allocated, empty
61.Vt fido_cbor_info_t
62type.
63If memory cannot be allocated, NULL is returned.
64.Pp
65The
66.Fn fido_cbor_info_free
67function releases the memory backing
68.Fa *ci_p ,
69where
70.Fa *ci_p
71must have been previously allocated by
72.Fn fido_cbor_info_new .
73On return,
74.Fa *ci_p
75is set to NULL.
76Either
77.Fa ci_p
78or
79.Fa *ci_p
80may be NULL, in which case
81.Fn fido_cbor_info_free
82is a NOP.
83.Pp
84The
85.Fn fido_dev_get_cbor_info
86function transmits a
87.Dv CTAP_CBOR_GETINFO
88command to
89.Fa dev
90and fills
91.Fa ci
92with attributes retrieved from the command's response.
93The
94.Fn fido_dev_get_cbor_info
95function may block.
96.Pp
97The
98.Fn fido_cbor_info_aaguid_ptr ,
99.Fn fido_cbor_info_extensions_ptr ,
100.Fn fido_cbor_info_protocols_ptr ,
101and
102.Fn fido_cbor_info_versions_ptr
103functions return pointers to the AAGUID, supported extensions,
104PIN protocol and CTAP version strings of
105.Fa ci .
106The corresponding length of a given attribute can be
107obtained by
108.Fn fido_cbor_info_aaguid_len ,
109.Fn fido_cbor_info_extensions_len ,
110.Fn fido_cbor_info_protocols_len ,
111or
112.Fn fido_cbor_info_versions_len .
113.Pp
114The
115.Fn fido_cbor_info_options_name_ptr
116and
117.Fn fido_cbor_info_options_value_ptr
118functions return pointers to the array of option names and their
119respective values
120in
121.Fa ci .
122The length of the options array is returned by
123.Fn fido_cbor_info_options_len .
124.Pp
125The
126.Fn fido_cbor_info_maxmsgsiz
127function returns the maximum message size of
128.Fa ci .
129.Pp
130A complete example of how to use these functions can be found in the
131.Pa example/info.c
132file shipped with
133.Em libfido2 .
134.Sh RETURN VALUES
135The
136.Fn fido_cbor_info_aaguid_ptr ,
137.Fn fido_cbor_info_extensions_ptr ,
138.Fn fido_cbor_info_protocols_ptr ,
139.Fn fido_cbor_info_versions_ptr ,
140.Fn fido_cbor_info_options_name_ptr ,
141and
142.Fn fido_cbor_info_options_value_ptr
143functions return NULL if the respective field in
144.Fa ci
145is absent.
146If not NULL, returned pointers are guaranteed to exist until any
147API function that takes
148.Fa ci
149without the
150.Em const
151qualifier is invoked.
152.Sh SEE ALSO
153.Xr fido_dev_open 3
diff --git a/man/fido_cred_exclude.3 b/man/fido_cred_exclude.3
new file mode 100644
index 0000000..700d6af
--- /dev/null
+++ b/man/fido_cred_exclude.3
@@ -0,0 +1,60 @@
1.\" Copyright (c) 2018 Yubico AB. All rights reserved.
2.\" Use of this source code is governed by a BSD-style
3.\" license that can be found in the LICENSE file.
4.\"
5.Dd $Mdocdate: May 23 2018 $
6.Dt FIDO_CRED_EXCLUDE 3
7.Os
8.Sh NAME
9.Nm fido_cred_exclude
10.Nd appends a credential ID to a credential's list of excluded credentials
11.Sh SYNOPSIS
12.In fido.h
13.Ft int
14.Fn fido_cred_exclude "fido_cred_t *cred" "const unsigned char *ptr" "size_t len"
15.Sh DESCRIPTION
16The
17.Fn fido_cred_exclude
18function adds
19.Fa ptr
20to the list of credentials excluded by
21.Fa cred ,
22where
23.Fa ptr
24points to a credential ID of
25.Fa len
26bytes.
27A copy of
28.Fa ptr
29is made, and no references to the passed pointer are kept.
30If
31.Fn fido_cred_exclude
32fails, the existing list of excluded credentials is preserved.
33.Pp
34If
35.Nm
36returns success and
37.Fa cred
38is later passed to
39.Xr fido_dev_make_cred 3
40on a device that contains the credential
41denoted by
42.Fa ptr ,
43then
44.Xr fido_dev_make_cred 3
45will fail.
46.Pp
47For the format of a FIDO 2 credential ID, please refer to the
48Web Authentication (webauthn) standard.
49.Sh RETURN VALUES
50The error codes returned by
51.Fn fido_cred_exclude
52are defined in
53.In fido/err.h .
54On success,
55.Dv FIDO_OK
56is returned.
57.Sh SEE ALSO
58.Xr fido_cred_new 3 ,
59.Xr fido_cred_set_authdata 3 ,
60.Xr fido_dev_make_cred 3
diff --git a/man/fido_cred_new.3 b/man/fido_cred_new.3
new file mode 100644
index 0000000..47eeba8
--- /dev/null
+++ b/man/fido_cred_new.3
@@ -0,0 +1,157 @@
1.\" Copyright (c) 2018 Yubico AB. All rights reserved.
2.\" Use of this source code is governed by a BSD-style
3.\" license that can be found in the LICENSE file.
4.\"
5.Dd $Mdocdate: May 23 2018 $
6.Dt FIDO_CRED_NEW 3
7.Os
8.Sh NAME
9.Nm fido_cred_new ,
10.Nm fido_cred_free ,
11.Nm fido_cred_fmt ,
12.Nm fido_cred_authdata_ptr ,
13.Nm fido_cred_clientdata_hash_ptr ,
14.Nm fido_cred_id_ptr ,
15.Nm fido_cred_pubkey_ptr ,
16.Nm fido_cred_sig_ptr ,
17.Nm fido_cred_x5c_ptr ,
18.Nm fido_cred_authdata_len ,
19.Nm fido_cred_clientdata_hash_len ,
20.Nm fido_cred_id_len ,
21.Nm fido_cred_pubkey_len ,
22.Nm fido_cred_sig_len ,
23.Nm fido_cred_x5c_len
24.Nd FIDO 2 credential API
25.Sh SYNOPSIS
26.In fido.h
27.Ft fido_cred_t *
28.Fn fido_cred_new "void"
29.Ft void
30.Fn fido_cred_free "fido_cred_t **cred_p"
31.Ft const char *
32.Fn fido_cred_fmt "const fido_cred_t *cred"
33.Ft const unsigned char *
34.Fn fido_cred_authdata_ptr "const fido_cred_t *cred"
35.Ft const unsigned char *
36.Fn fido_cred_clientdata_hash_ptr "const fido_cred_t *cred"
37.Ft const unsigned char *
38.Fn fido_cred_id_ptr "const fido_cred_t *cred"
39.Ft const unsigned char *
40.Fn fido_cred_pubkey_ptr "const fido_cred_t *cred"
41.Ft const unsigned char *
42.Fn fido_cred_sig_ptr "const fido_cred_t *cred"
43.Ft const unsigned char *
44.Fn fido_cred_x5c_ptr "const fido_cred_t *cred"
45.Ft size_t
46.Fn fido_cred_authdata_len "const fido_cred_t *cred"
47.Ft size_t
48.Fn fido_cred_clientdata_hash_len "const fido_cred_t *cred"
49.Ft size_t
50.Fn fido_cred_id_len "const fido_cred_t *cred"
51.Ft size_t
52.Fn fido_cred_pubkey_len "const fido_cred_t *cred"
53.Ft size_t
54.Fn fido_cred_sig_len "const fido_cred_t *cred"
55.Ft size_t
56.Fn fido_cred_x5c_len "const fido_cred_t *cred"
57.Sh DESCRIPTION
58FIDO 2 credentials are abstracted in
59.Em libfido2
60by the
61.Vt fido_cred_t
62type.
63The functions described in this page allow a
64.Vt fido_cred_t
65type to be allocated, deallocated, and inspected.
66For other operations on
67.Vt fido_cred_t ,
68please refer to
69.Xr fido_cred_set_authdata 3 ,
70.Xr fido_cred_exclude 3 ,
71.Xr fido_cred_verify 3 ,
72and
73.Xr fido_dev_make_cred 3 .
74.Pp
75The
76.Fn fido_cred_new
77function returns a pointer to a newly allocated, empty
78.Vt fido_cred_t
79type.
80If memory cannot be allocated, NULL is returned.
81.Pp
82The
83.Fn fido_cred_free
84function releases the memory backing
85.Fa *cred_p ,
86where
87.Fa *cred_p
88must have been previously allocated by
89.Fn fido_cred_new .
90On return,
91.Fa *cred_p
92is set to NULL.
93Either
94.Fa cred_p
95or
96.Fa *cred_p
97may be NULL, in which case
98.Fn fido_cred_free
99is a NOP.
100.Pp
101The
102.Fn fido_cred_fmt
103function returns a pointer to a NUL-terminated string containing
104the format of
105.Fa cred ,
106or NULL if
107.Fa cred
108does not have a format set.
109.Pp
110The
111.Fn fido_cred_authdata_ptr ,
112.Fn fido_cred_clientdata_hash_ptr ,
113.Fn fido_cred_id_ptr ,
114.Fn fido_cred_pubkey_ptr ,
115.Fn fido_cred_sig_ptr ,
116and
117.Fn fido_cred_x5c_ptr
118functions return pointers to the authenticator data, client data
119hash, ID, public key, signature and x509 certificate parts of
120.Fa cred ,
121or NULL if the respective entry is not set.
122.Pp
123The corresponding length can be obtained by
124.Fn fido_cred_authdata_len ,
125.Fn fido_cred_clientdata_hash_len ,
126.Fn fido_cred_id_len ,
127.Fn fido_cred_pubkey_len ,
128and
129.Fn fido_cred_sig_len .
130.Pp
131The authenticator data, x509 certificate, and signature parts of a
132credential are typically passed to a FIDO 2 server for verification.
133.Sh RETURN VALUES
134The authenticator data returned by
135.Fn fido_cred_authdata_ptr
136is a CBOR-encoded byte string, as obtained from the authenticator.
137.Pp
138If not NULL, pointers returned by
139.Fn fido_cred_fmt ,
140.Fn fido_cred_authdata_ptr ,
141.Fn fido_cred_clientdata_hash_ptr ,
142.Fn fido_cred_id_ptr ,
143.Fn fido_cred_pubkey_ptr ,
144.Fn fido_cred_sig_ptr ,
145and
146.Fn fido_cred_x5c_ptr
147are guaranteed to exist until any API function that takes
148.Fa cred
149without the
150.Em const
151qualifier is invoked.
152.Sh SEE ALSO
153.Xr fido_cred_exclude 3 ,
154.Xr fido_cred_set_authdata 3 ,
155.Xr fido_cred_verify 3 ,
156.Xr fido_credman_metadata_new 3 ,
157.Xr fido_dev_make_cred 3
diff --git a/man/fido_cred_set_authdata.3 b/man/fido_cred_set_authdata.3
new file mode 100644
index 0000000..8b087fa
--- /dev/null
+++ b/man/fido_cred_set_authdata.3
@@ -0,0 +1,240 @@
1.\" Copyright (c) 2018 Yubico AB. All rights reserved.
2.\" Use of this source code is governed by a BSD-style
3.\" license that can be found in the LICENSE file.
4.\"
5.Dd $Mdocdate: May 23 2018 $
6.Dt FIDO_CRED_SET_AUTHDATA 3
7.Os
8.Sh NAME
9.Nm fido_cred_set_authdata ,
10.Nm fido_cred_set_authdata_raw ,
11.Nm fido_cred_set_x509 ,
12.Nm fido_cred_set_sig ,
13.Nm fido_cred_set_clientdata_hash ,
14.Nm fido_cred_set_rp ,
15.Nm fido_cred_set_user ,
16.Nm fido_cred_set_extensions ,
17.Nm fido_cred_set_rk ,
18.Nm fido_cred_set_uv ,
19.Nm fido_cred_set_fmt ,
20.Nm fido_cred_set_type
21.Nd set parameters of a FIDO 2 credential
22.Sh SYNOPSIS
23.In fido.h
24.Bd -literal
25typedef enum {
26 FIDO_OPT_OMIT = 0, /* use authenticator's default */
27 FIDO_OPT_FALSE, /* explicitly set option to false */
28 FIDO_OPT_TRUE, /* explicitly set option to true */
29} fido_opt_t;
30.Ed
31.Ft int
32.Fn fido_cred_set_authdata "fido_cred_t *cred" "const unsigned char *ptr" "size_t len"
33.Ft int
34.Fn fido_cred_set_authdata_raw "fido_cred_t *cred" "const unsigned char *ptr" "size_t len"
35.Ft int
36.Fn fido_cred_set_x509 "fido_cred_t *cred" "const unsigned char *ptr" "size_t len"
37.Ft int
38.Fn fido_cred_set_sig "fido_cred_t *cred" "const unsigned char *ptr" "size_t len"
39.Ft int
40.Fn fido_cred_set_clientdata_hash "fido_cred_t *cred" "const unsigned char *ptr" "size_t len"
41.Ft int
42.Fn fido_cred_set_rp "fido_cred_t *cred" "const char *id" "const char *name"
43.Ft int
44.Fn fido_cred_set_user "fido_cred_t *cred" "const unsigned char *user_id" "size_t user_id_len" "const char *name" "const char *display_name" "const char *icon"
45.Ft int
46.Fn fido_cred_set_extensions "fido_cred_t *cred" "int flags"
47.Ft int
48.Fn fido_cred_set_rk "fido_cred_t *cred" "fido_opt_t rk"
49.Ft int
50.Fn fido_cred_set_uv "fido_cred_t *cred" "fido_opt_t uv"
51.Ft int
52.Fn fido_cred_set_fmt "fido_cred_t *cred" "const char *ptr"
53.Ft int
54.Fn fido_cred_set_type "fido_cred_t *cred" "int cose_alg"
55.Sh DESCRIPTION
56The
57.Nm
58set of functions define the various parameters of a FIDO 2
59credential, allowing a
60.Fa fido_cred_t
61type to be prepared for a subsequent call to
62.Xr fido_dev_make_cred 3
63or
64.Xr fido_cred_verify 3 .
65For the complete specification of a FIDO 2 credential and the format
66of its constituent parts, please refer to the Web Authentication
67(webauthn) standard.
68.Pp
69The
70.Fn fido_cred_set_authdata ,
71.Fn fido_cred_set_x509 ,
72.Fn fido_cred_set_sig ,
73and
74.Fn fido_cred_set_clientdata_hash
75functions set the authenticator data, attestation certificate,
76signature and client data hash parts of
77.Fa cred
78to
79.Fa ptr ,
80where
81.Fa ptr
82points to
83.Fa len
84bytes.
85A copy of
86.Fa ptr
87is made, and no references to the passed pointer are kept.
88The authenticator data passed to
89.Fn fido_cred_set_authdata
90must be a CBOR-encoded byte string, as obtained from
91.Fn fido_cred_authdata_ptr .
92Alternatively, a raw binary blob may be passed to
93.Fn fido_cred_set_authdata_raw .
94.Pp
95The
96.Fn fido_cred_set_rp
97function sets the relying party
98.Fa id
99and
100.Fa name
101parameters of
102.Fa cred ,
103where
104.Fa id
105and
106.Fa name
107are NUL-terminated UTF-8 strings.
108The contents of
109.Fa id
110and
111.Fa name
112are copied, and no references to the passed pointers are kept.
113.Pp
114The
115.Fn fido_cred_set_user
116function sets the user attributes of
117.Fa cred ,
118where
119.Fa user_id
120points to
121.Fa user_id_len
122bytes and
123.Fa name ,
124.Fa display_name ,
125and
126.Fa icon
127are NUL-terminated UTF-8 strings.
128The contents of
129.Fa user_id ,
130.Fa name ,
131.Fa display_name ,
132and
133.Fa icon
134are copied, and no references to the passed pointers are kept.
135Previously set user attributes are flushed.
136The
137.Fa user_id ,
138.Fa name ,
139.Fa display_name ,
140and
141.Fa icon
142parameters may be NULL.
143.Pp
144The
145.Fn fido_cred_set_extensions
146function sets the extensions of
147.Fa cred
148to the bitmask
149.Fa flags .
150At the moment, only the
151.Dv FIDO_EXT_HMAC_SECRET
152extension is supported.
153If
154.Fa flags
155is zero, the extensions of
156.Fa cred
157are cleared.
158.Pp
159The
160.Fn fido_cred_set_rk
161and
162.Fn fido_cred_set_uv
163functions set the
164.Em rk
165(resident key)
166and
167.Em uv
168(user verification)
169attributes of
170.Fa cred .
171Both are
172.Dv FIDO_OPT_OMIT
173by default, allowing the authenticator to use its default settings.
174.Pp
175The
176.Fn fido_cred_set_fmt
177function sets the format of
178.Fa cred
179to
180.Fa fmt ,
181where
182.Fa fmt
183must be either
184.Vt "packed"
185(the format used in FIDO 2)
186or
187.Vt "fido-u2f"
188(the format used by U2F).
189A copy of
190.Fa fmt
191is made, and no references to the passed pointer are kept.
192Note that not all authenticators support FIDO2 and therefore may not
193be able to generate
194.Vt "packed" .
195.Pp
196The
197.Fn fido_cred_set_type
198function sets the type of
199.Fa cred to
200.Fa cose_alg ,
201where
202.Fa cose_alg
203is
204.Dv COSE_ES256 ,
205.Dv COSE_RS256 ,
206or
207.Dv COSE_EDDSA .
208The type of a credential may only be set once.
209Note that not all authenticators support COSE_RS256 or COSE_EDDSA.
210.Pp
211Use of the
212.Nm
213set of functions may happen in two distinct situations:
214when generating a new credential on a FIDO device, prior to
215.Xr fido_dev_make_cred 3
216(i.e, in the context of a FIDO client), or when validating
217a generated credential using
218.Xr fido_cred_verify 3
219(i.e, in the context of a FIDO server).
220.Pp
221For a complete description of the generation of a FIDO 2 credential
222and its verification, please refer to the FIDO 2 specification.
223A concrete utilisation example of the
224.Nm
225set of functions can be found in the
226.Pa cred.c
227example shipped with
228.Em libfido2 .
229.Sh RETURN VALUES
230The error codes returned by the
231.Nm
232set of functions are defined in
233.In fido/err.h .
234On success,
235.Dv FIDO_OK
236is returned.
237.Sh SEE ALSO
238.Xr fido_cred_exclude 3 ,
239.Xr fido_cred_verify 3 ,
240.Xr fido_dev_make_cred 3
diff --git a/man/fido_cred_verify.3 b/man/fido_cred_verify.3
new file mode 100644
index 0000000..c75b9a1
--- /dev/null
+++ b/man/fido_cred_verify.3
@@ -0,0 +1,64 @@
1.\" Copyright (c) 2018 Yubico AB. All rights reserved.
2.\" Use of this source code is governed by a BSD-style
3.\" license that can be found in the LICENSE file.
4.\"
5.Dd $Mdocdate: May 23 2018 $
6.Dt FIDO_CRED_VERIFY 3
7.Os
8.Sh NAME
9.Nm fido_cred_verify
10.Nd verifies the signature of a FIDO 2 credential
11.Sh SYNOPSIS
12.In fido.h
13.Ft int
14.Fn fido_cred_verify "const fido_cred_t *cred"
15.Sh DESCRIPTION
16The
17.Fn fido_cred_verify
18function verifies whether the signature contained in
19.Fa cred
20matches the attributes of the credential.
21Before using
22.Fn fido_cred_verify
23in a sensitive context, the reader is strongly encouraged to make
24herself familiar with the FIDO 2 credential attestation process
25as defined in the Web Authentication (webauthn) standard.
26.Pp
27A brief description follows:
28.Pp
29The
30.Fn fido_cred_verify
31function verifies whether the client data hash, relying party ID,
32credential ID, type, and resident key and user verification
33attributes of
34.Fa cred
35have been attested by the holder of the private counterpart of
36the public key contained in the credential's x509 certificate.
37.Pp
38Please note that the x509 certificate itself is not verified.
39.Pp
40The attestation statement formats supported by
41.Fn fido_cred_verify
42are
43.Em packed
44and
45.Em fido-u2f .
46The attestation type implemented by
47.Fn fido_cred_verify
48is
49.Em Basic Attestation .
50The attestation key pair is assumed to be of the type ES256.
51Other attestation formats and types are not supported.
52.Sh RETURN VALUES
53The error codes returned by
54.Fn fido_cred_verify
55are defined in
56.In fido/err.h .
57If
58.Fa cred
59passes verification, then
60.Dv FIDO_OK
61is returned.
62.Sh SEE ALSO
63.Xr fido_cred_new 3 ,
64.Xr fido_cred_set_authdata 3
diff --git a/man/fido_credman_metadata_new.3 b/man/fido_credman_metadata_new.3
new file mode 100644
index 0000000..16f0192
--- /dev/null
+++ b/man/fido_credman_metadata_new.3
@@ -0,0 +1,299 @@
1.\" Copyright (c) 2019 Yubico AB. All rights reserved.
2.\" Use of this source code is governed by a BSD-style
3.\" license that can be found in the LICENSE file.
4.\"
5.Dd $Mdocdate: June 28 2019 $
6.Dt FIDO_CREDMAN_METADATA_NEW 3
7.Os
8.Sh NAME
9.Nm fido_credman_metadata_new ,
10.Nm fido_credman_rk_new ,
11.Nm fido_credman_rp_new ,
12.Nm fido_credman_metadata_free ,
13.Nm fido_credman_rk_free ,
14.Nm fido_credman_rp_free ,
15.Nm fido_credman_rk_existing ,
16.Nm fido_credman_rk_remaining ,
17.Nm fido_credman_rk ,
18.Nm fido_credman_rk_count ,
19.Nm fido_credman_rp_id ,
20.Nm fido_credman_rp_name ,
21.Nm fido_credman_rp_count ,
22.Nm fido_credman_rp_id_hash_ptr ,
23.Nm fido_credman_rp_id_hash_len ,
24.Nm fido_credman_get_dev_metadata ,
25.Nm fido_credman_get_dev_rk ,
26.Nm fido_credman_del_dev_rk ,
27.Nm fido_credman_get_dev_rp
28.Nd FIDO 2 credential management API
29.Sh SYNOPSIS
30.In fido.h
31.In fido/credman.h
32.Ft fido_credman_metadata_t *
33.Fn fido_credman_metadata_new "void"
34.Ft fido_credman_rk_t *
35.Fn fido_credman_rk_new "void"
36.Ft fido_credman_rp_t *
37.Fn fido_credman_rp_new "void"
38.Ft void
39.Fn fido_credman_metadata_free "fido_credman_metadata_t **metadata_p"
40.Ft void
41.Fn fido_credman_rk_free "fido_credman_rk_t **rk_p"
42.Ft void
43.Fn fido_credman_rp_free "fido_credman_rp_t **rp_p"
44.Ft uint64_t
45.Fn fido_credman_rk_existing "const fido_credman_metadata_t *metadata"
46.Ft uint64_t
47.Fn fido_credman_rk_remaining "const fido_credman_metadata_t *metadata"
48.Ft const fido_cred_t *
49.Fn fido_credman_rk "const fido_credman_rk_t *rk" "size_t idx"
50.Ft size_t
51.Fn fido_credman_rk_count "const fido_credman_rk_t *rk"
52.Ft const char *
53.Fn fido_credman_rp_id "const fido_credman_rp_t *rp" "size_t idx"
54.Ft const char *
55.Fn fido_credman_rp_name "const fido_credman_rp_t *rp" "size_t idx"
56.Ft size_t
57.Fn fido_credman_rp_count "const fido_credman_rp_t *rp"
58.Ft const unsigned char *
59.Fn fido_credman_rp_id_hash_ptr "const fido_credman_rp_t *rp" "size_t idx"
60.Ft size_t
61.Fn fido_credman_rp_id_hash_len "const fido_credman_rp_t *" "size_t idx"
62.Ft int
63.Fn fido_credman_get_dev_metadata "fido_dev_t *dev" "fido_credman_metadata_t *metadata" "const char *pin"
64.Ft int
65.Fn fido_credman_get_dev_rk "fido_dev_t *dev" "const char *rp_id" "fido_credman_rk_t *rk" "const char *pin"
66.Ft int
67.Fn fido_credman_del_dev_rk "fido_dev_t *dev" const unsigned char *cred_id" "size_t cred_id_len" "const char *pin"
68.Ft int
69.Fn fido_credman_get_dev_rp "fido_dev_t *dev" "fido_credman_rp_t *rp" "const char *pin"
70.Sh DESCRIPTION
71The credential management API of
72.Em libfido2
73allows resident credentials on a FIDO2 authenticator to be listed,
74inspected, and removed.
75Please note that not all authenticators support credential management.
76To obtain information on what an authenticator supports, please
77refer to
78.Xr fido_cbor_info_new 3 .
79.Pp
80The
81.Vt fido_credman_metadata_t
82type abstracts credential management metadata.
83.Pp
84The
85.Fn fido_credman_metadata_new
86function returns a pointer to a newly allocated, empty
87.Vt fido_credman_metadata_t
88type.
89If memory cannot be allocated, NULL is returned.
90.Pp
91The
92.Fn fido_credman_metadata_free
93function releases the memory backing
94.Fa *metadata_p ,
95where
96.Fa *metadata_p
97must have been previously allocated by
98.Fn fido_credman_metadata_new .
99On return,
100.Fa *metadata_p
101is set to NULL.
102Either
103.Fa metadata_p
104or
105.Fa *metadata_p
106may be NULL, in which case
107.Fn fido_credman_metadata_free
108is a NOP.
109.Pp
110The
111.Fn fido_credman_get_dev_metadata
112function populates
113.Fa metadata
114with information retrieved from
115.Fa dev .
116A valid
117.Fa pin
118must be provided.
119.Pp
120The
121.Fn fido_credman_rk_existing
122function inspects
123.Fa metadata
124and returns the number of resident credentials on the
125authenticator.
126The
127.Fn fido_credman_rk_remaining
128function inspects
129.Fa metadata
130and returns the estimated number of resident credentials that can
131be created on the authenticator.
132.Pp
133The
134.Vt fido_credman_rk_t
135type abstracts the set of resident credentials belonging to a
136given relying party.
137.Pp
138The
139.Fn fido_credman_rk_new
140function returns a pointer to a newly allocated, empty
141.Vt fido_credman_rk_t
142type.
143If memory cannot be allocated, NULL is returned.
144.Pp
145The
146.Fn fido_credman_rk_free
147function releases the memory backing
148.Fa *rk_p ,
149where
150.Fa *rk_p
151must have been previously allocated by
152.Fn fido_credman_rk_new .
153On return,
154.Fa *rk_p
155is set to NULL.
156Either
157.Fa rk_p
158or
159.Fa *rk_p
160may be NULL, in which case
161.Fn fido_credman_rk_free
162is a NOP.
163.Pp
164The
165.Fn fido_credman_get_dev_rk
166function populates
167.Fa rk
168with the set of resident credentials belonging to
169.Fa rp_id
170in
171.Fa dev .
172A valid
173.Fa pin
174must be provided.
175.Pp
176The
177.Fn fido_credman_rk_count
178function returns the number of resident credentials in
179.Fa rk .
180The
181.Fn fido_credman_rk
182function returns a pointer to the credential at index
183.Fa idx
184in
185.Fa rk .
186Please note that the first credential in
187.Fa rk
188has an
189.Fa idx
190(index) value of 0.
191.Pp
192The
193.Fn fido_credman_del_dev_rk
194function deletes the resident credential identified by
195.Fa cred_id
196from
197.Fa dev ,
198where
199.Fa cred_id
200points to
201.Fa cred_id_len
202bytes.
203A valid
204.Fa pin
205must be provided.
206.Pp
207The
208.Vt fido_credman_rp_t
209type abstracts information about a relying party.
210.Pp
211The
212.Fn fido_credman_rp_new
213function returns a pointer to a newly allocated, empty
214.Vt fido_credman_rp_t
215type.
216If memory cannot be allocated, NULL is returned.
217.Pp
218The
219.Fn fido_credman_rp_free
220function releases the memory backing
221.Fa *rp_p ,
222where
223.Fa *rp_p
224must have been previously allocated by
225.Fn fido_credman_rp_new .
226On return,
227.Fa *rp_p
228is set to NULL.
229Either
230.Fa rp_p
231or
232.Fa *rp_p
233may be NULL, in which case
234.Fn fido_credman_rp_free
235is a NOP.
236.Pp
237The
238.Fn fido_credman_get_dev_rp
239function populates
240.Fa rp
241with information about relying parties with resident credentials
242in
243.Fa dev .
244A valid
245.Fa pin
246must be provided.
247.Pp
248The
249.Fn fido_credman_rp_count
250function returns the number of relying parties in
251.Fa rp .
252.Pp
253The
254.Fn fido_credman_rp_id
255and
256.Fn fido_credman_rp_name
257functions return pointers to the id and name of relying party
258.Fa idx
259in
260.Fa rp .
261If not NULL, the values returned by these functions point to
262NUL-terminated UTF-8 strings.
263Please note that the first relying party in
264.Fa rp
265has an
266.Fa idx
267(index) value of 0.
268.Pp
269The
270.Fn fido_credman_rp_id_hash_ptr
271function returns a pointer to the hashed id of relying party
272.Fa idx
273in
274.Fa rp .
275The corresponding length can be obtained by
276.Fn fido_credman_rp_id_hash_len .
277Please note that the first relying party in
278.Fa rp
279has an
280.Fa idx
281(index) value of 0.
282.Sh RETURN VALUES
283The
284.Fn fido_credman_get_dev_metadata ,
285.Fn fido_credman_get_dev_rk ,
286.Fn fido_credman_del_dev_rk ,
287and
288.Fn fido_credman_get_dev_rp
289functions return
290.Dv FIDO_OK
291on success.
292On error, a different error code defined in
293.In fido/err.h
294is returned.
295Functions returning pointers are not guaranteed to succeed, and
296should have their return values checked for NULL.
297.Sh SEE ALSO
298.Xr fido_cbor_info_new 3 ,
299.Xr fido_cred_new 3
diff --git a/man/fido_dev_get_assert.3 b/man/fido_dev_get_assert.3
new file mode 100644
index 0000000..2e33fc5
--- /dev/null
+++ b/man/fido_dev_get_assert.3
@@ -0,0 +1,76 @@
1.\" Copyright (c) 2018 Yubico AB. All rights reserved.
2.\" Use of this source code is governed by a BSD-style
3.\" license that can be found in the LICENSE file.
4.\"
5.Dd $Mdocdate: May 24 2018 $
6.Dt FIDO_DEV_GET_ASSERT 3
7.Os
8.Sh NAME
9.Nm fido_dev_get_assert
10.Nd obtains an assertion from a FIDO device
11.Sh SYNOPSIS
12.In fido.h
13.Ft int
14.Fn fido_dev_get_assert "fido_dev_t *dev" " fido_assert_t *assert" "const char *pin"
15.Sh DESCRIPTION
16The
17.Fn fido_dev_get_assert
18function asks the FIDO device represented by
19.Fa dev
20for an assertion according to the following parameters defined in
21.Fa assert :
22.Pp
23.Bl -dash -compact
24.It
25.Nm relying party ID ;
26.It
27.Nm client data hash ;
28.It
29.Nm list of allowed credential IDs ;
30.It
31.Nm user presence and user verification attributes .
32.El
33.Pp
34See
35.Xr fido_assert_set_authdata 3
36for information on how these values are set.
37.Pp
38If a PIN is not needed to authenticate the request against
39.Fa dev ,
40then
41.Fa pin
42may be NULL.
43Otherwise
44.Fa pin
45must point to a NUL-terminated UTF-8 string.
46.Pp
47After a successful call to
48.Fn fido_dev_get_assert ,
49the
50.Xr fido_assert_count 3 ,
51.Xr fido_assert_user_display_name 3 ,
52.Xr fido_assert_user_icon 3 ,
53.Xr fido_assert_user_name 3 ,
54.Xr fido_assert_authdata_ptr 3 ,
55.Xr fido_assert_user_id_ptr 3 ,
56.Xr fido_assert_sig_ptr 3 ,
57and
58.Xr fido_assert_sigcount 3
59functions may be invoked on
60.Fa assert
61to retrieve the various attributes of the generated assertion.
62.Pp
63Please note that
64.Fn fido_dev_get_assert
65is synchronous and will block if necessary.
66.Sh RETURN VALUES
67The error codes returned by
68.Fn fido_dev_get_assert
69are defined in
70.In fido/err.h .
71On success,
72.Dv FIDO_OK
73is returned.
74.Sh SEE ALSO
75.Xr fido_assert_new 3 ,
76.Xr fido_assert_set_authdata 3
diff --git a/man/fido_dev_info_manifest.3 b/man/fido_dev_info_manifest.3
new file mode 100644
index 0000000..22519e2
--- /dev/null
+++ b/man/fido_dev_info_manifest.3
@@ -0,0 +1,143 @@
1.\" Copyright (c) 2018 Yubico AB. All rights reserved.
2.\" Use of this source code is governed by a BSD-style
3.\" license that can be found in the LICENSE file.
4.\"
5.Dd $Mdocdate: May 25 2018 $
6.Dt FIDO_DEV_INFO_MANIFEST 3
7.Os
8.Sh NAME
9.Nm fido_dev_info_manifest ,
10.Nm fido_dev_info_new ,
11.Nm fido_dev_info_free ,
12.Nm fido_dev_info_ptr ,
13.Nm fido_dev_info_path ,
14.Nm fido_dev_info_product ,
15.Nm fido_dev_info_vendor ,
16.Nm fido_dev_info_manufacturer_string ,
17.Nm fido_dev_info_product_string
18.Nd FIDO 2 device discovery functions
19.Sh SYNOPSIS
20.In fido.h
21.Ft int
22.Fn fido_dev_info_manifest "fido_dev_info_t *devlist" "size_t ilen" "size_t *olen"
23.Ft fido_dev_info_t *
24.Fn fido_dev_info_new "size_t n"
25.Ft void
26.Fn fido_dev_info_free "fido_dev_info_t **devlist_p" "size_t n"
27.Ft const fido_dev_info_t *
28.Fn fido_dev_info_ptr "const fido_dev_info_t *devlist" "size_t i"
29.Ft const char *
30.Fn fido_dev_info_path "const fido_dev_info_t *di"
31.Ft int16_t
32.Fn fido_dev_info_product "const fido_dev_info_t *di"
33.Ft int16_t
34.Fn fido_dev_info_vendor "const fido_dev_info_t *di"
35.Ft const char *
36.Fn fido_dev_info_manufacturer_string "const fido_dev_info_t *di"
37.Ft const char *
38.Fn fido_dev_info_product_string "const fido_dev_info_t *di"
39.Sh DESCRIPTION
40The
41.Fn fido_dev_info_manifest
42function fills
43.Fa devlist
44with up to
45.Fa ilen
46FIDO devices found by the underlying operating system.
47Currently only USB HID devices are supported.
48The number of discovered devices is returned in
49.Fa olen ,
50where
51.Fa olen
52is an addressable pointer.
53.Pp
54The
55.Fn fido_dev_info_new
56function returns a pointer to a newly allocated, empty device list
57with
58.Fa n
59available slots.
60If memory is not available, NULL is returned.
61.Pp
62The
63.Fn fido_dev_info_free
64function releases the memory backing
65.Fa *devlist_p ,
66where
67.Fa *devlist_p
68must have been previously allocated by
69.Fn fido_dev_info_new .
70The number
71.Fa n
72of allocated slots must also be provided.
73On return,
74.Fa *devlist_p
75is set to NULL.
76Either
77.Fa devlist_p
78or
79.Fa *devlist_p
80may be NULL, in which case
81.Fn fido_dev_info_free
82is a NOP.
83.Pp
84The
85.Fn fido_dev_info_ptr
86function returns a pointer to slot number
87.Fa i
88of
89.Fa devlist .
90It is the caller's responsibility to ensure that
91.Fa i
92is bounded.
93Please note that the first slot has index 0.
94.Pp
95The
96.Fn fido_dev_info_path
97returns the filesystem path or subsystem-specific identification
98string of
99.Fa di .
100.Pp
101The
102.Fn fido_dev_info_product
103function returns the product ID of
104.Fa di .
105.Pp
106The
107.Fn fido_dev_info_vendor
108function returns the vendor ID of
109.Fa di .
110.Pp
111The
112.Fn fido_dev_info_manufacturer_string
113function returns the manufacturer string of
114.Fa di .
115.Pp
116The
117.Fn fido_dev_info_product_string
118function returns the product string of
119.Fa di .
120.Pp
121An example of how to use the functions described in this document
122can be found in the
123.Pa examples/manifest.c
124file shipped with
125.Em libfido2 .
126.Sh RETURN VALUES
127The
128.Fn fido_dev_info_manifest
129function always returns
130.Dv FIDO_OK .
131If a discovery error occurs, the
132.Fa olen
133pointer is set to 0.
134.Pp
135The pointers returned by
136.Fn fido_dev_info_ptr ,
137.Fn fido_dev_info_path ,
138.Fn fido_dev_info_manufacturer_string ,
139and
140.Fn fido_dev_info_product_string
141are guaranteed to exist until
142.Fn fido_dev_info_free
143is called on the corresponding device list.
diff --git a/man/fido_dev_make_cred.3 b/man/fido_dev_make_cred.3
new file mode 100644
index 0000000..ccaf788
--- /dev/null
+++ b/man/fido_dev_make_cred.3
@@ -0,0 +1,77 @@
1.\" Copyright (c) 2018 Yubico AB. All rights reserved.
2.\" Use of this source code is governed by a BSD-style
3.\" license that can be found in the LICENSE file.
4.\"
5.Dd $Mdocdate: May 23 2018 $
6.Dt FIDO_DEV_MAKE_CRED 3
7.Os
8.Sh NAME
9.Nm fido_dev_make_cred
10.Nd generates a new credential on a FIDO device
11.Sh SYNOPSIS
12.In fido.h
13.Ft int
14.Fn fido_dev_make_cred "fido_dev_t *dev" " fido_cred_t *cred" "const char *pin"
15.Sh DESCRIPTION
16The
17.Fn fido_dev_make_cred
18function asks the FIDO device represented by
19.Fa dev
20to generate a new credential according to the following parameters
21defined in
22.Fa cred :
23.Pp
24.Bl -dash -compact
25.It
26.Nm type ;
27.It
28.Nm client data hash ;
29.It
30.Nm relying party ;
31.It
32.Nm user attributes ;
33.It
34.Nm list of excluded credential IDs ;
35.It
36.Nm resident key and user verification attributes .
37.El
38.Pp
39See
40.Xr fido_cred_set_authdata 3
41for information on how these values are set.
42.Pp
43If a PIN is not needed to authenticate the request against
44.Fa dev ,
45then
46.Fa pin
47may be NULL.
48Otherwise
49.Fa pin
50must point to a NUL-terminated UTF-8 string.
51.Pp
52After a successful call to
53.Fn fido_dev_make_cred ,
54the
55.Xr fido_cred_authdata_ptr 3 ,
56.Xr fido_cred_pubkey_ptr 3 ,
57.Xr fido_cred_x5c_ptr 3 ,
58and
59.Xr fido_cred_sig_ptr 3
60functions may be invoked on
61.Fa cred
62to retrieve the various parts of the generated credential.
63.Pp
64Please note that
65.Fn fido_dev_make_cred
66is synchronous and will block if necessary.
67.Sh RETURN VALUES
68The error codes returned by
69.Fn fido_dev_make_cred
70are defined in
71.In fido/err.h .
72On success,
73.Dv FIDO_OK
74is returned.
75.Sh SEE ALSO
76.Xr fido_cred_new 3 ,
77.Xr fido_cred_set_authdata 3
diff --git a/man/fido_dev_open.3 b/man/fido_dev_open.3
new file mode 100644
index 0000000..53e3a12
--- /dev/null
+++ b/man/fido_dev_open.3
@@ -0,0 +1,159 @@
1.\" Copyright (c) 2018 Yubico AB. All rights reserved.
2.\" Use of this source code is governed by a BSD-style
3.\" license that can be found in the LICENSE file.
4.\"
5.Dd $Mdocdate: May 25 2018 $
6.Dt FIDO_DEV_OPEN 3
7.Os
8.Sh NAME
9.Nm fido_dev_open ,
10.Nm fido_dev_close ,
11.Nm fido_dev_cancel ,
12.Nm fido_dev_new ,
13.Nm fido_dev_free ,
14.Nm fido_dev_force_fido2 ,
15.Nm fido_dev_force_u2f ,
16.Nm fido_dev_is_fido2 ,
17.Nm fido_dev_protocol ,
18.Nm fido_dev_build ,
19.Nm fido_dev_flags ,
20.Nm fido_dev_major ,
21.Nm fido_dev_minor
22.Nd FIDO 2 device open/close and related functions
23.Sh SYNOPSIS
24.In fido.h
25.Ft int
26.Fn fido_dev_open "fido_dev_t *dev" "const char *path"
27.Ft int
28.Fn fido_dev_close "fido_dev_t *dev"
29.Ft int
30.Fn fido_dev_cancel "fido_dev_t *dev"
31.Ft fido_dev_t *
32.Fn fido_dev_new "void"
33.Ft void
34.Fn fido_dev_free "fido_dev_t **dev_p"
35.Ft void
36.Fn fido_dev_force_fido2 "fido_dev_t *dev"
37.Ft void
38.Fn fido_dev_force_u2f "fido_dev_t *dev"
39.Ft bool
40.Fn fido_dev_is_fido2 "const fido_dev_t *dev"
41.Ft uint8_t
42.Fn fido_dev_protocol "const fido_dev_t *dev"
43.Ft uint8_t
44.Fn fido_dev_build "const fido_dev_t *dev"
45.Ft uint8_t
46.Fn fido_dev_flags "const fido_dev_t *dev"
47.Ft uint8_t
48.Fn fido_dev_major "const fido_dev_t *dev"
49.Ft uint8_t
50.Fn fido_dev_minor "const fido_dev_t *dev"
51.Sh DESCRIPTION
52The
53.Fn fido_dev_open
54function opens the device pointed to by
55.Fa path ,
56where
57.Fa dev
58is a freshly allocated or otherwise closed
59.Vt fido_dev_t .
60.Pp
61The
62.Fn fido_dev_close
63function closes the device represented by
64.Fa dev .
65If
66.Fa dev
67is already closed,
68.Fn fido_dev_close
69is a NOP.
70.Pp
71The
72.Fn fido_dev_cancel
73function cancels any pending requests on
74.Fa dev .
75.Pp
76The
77.Fn fido_dev_new
78function returns a pointer to a newly allocated, empty
79.Vt fido_dev_t .
80If memory cannot be allocated, NULL is returned.
81.Pp
82The
83.Fn fido_dev_free
84function releases the memory backing
85.Fa *dev_p ,
86where
87.Fa *dev_p
88must have been previously allocated by
89.Fn fido_dev_new .
90On return,
91.Fa *dev_p
92is set to NULL.
93Either
94.Fa dev_p
95or
96.Fa *dev_p
97may be NULL, in which case
98.Fn fido_dev_free
99is a NOP.
100.Pp
101The
102.Fn fido_dev_force_fido2
103function can be used to force CTAP2 communication with
104.Fa dev .
105.Pp
106The
107.Fn fido_dev_force_u2f
108function can be used to force CTAP1 (U2F) communication with
109.Fa dev .
110.Pp
111The
112.Fn fido_dev_is_fido2
113function returns
114.Dv true
115if
116.Fa dev
117is a FIDO 2 device.
118.Pp
119The
120.Fn fido_dev_protocol
121function returns the CTAPHID protocol version identifier of
122.Fa dev .
123.Pp
124The
125.Fn fido_dev_build
126function returns the CTAPHID build version number of
127.Fa dev .
128.Pp
129The
130.Fn fido_dev_flags
131function returns the CTAPHID capabilities flags of
132.Fa dev .
133.Pp
134The
135.Fn fido_dev_major
136function returns the CTAPHID major version number of
137.Fa dev .
138.Pp
139The
140.Fn fido_dev_minor
141function returns the CTAPHID minor version number of
142.Fa dev .
143.Pp
144For the format and meaning of the CTAPHID parameters returned by
145functions above, please refer to the FIDO Client to Authenticator
146Protocol (CTAP) specification.
147.Sh RETURN VALUES
148On success,
149.Fn fido_dev_open
150and
151.Fn fido_dev_close
152return
153.Dv FIDO_OK .
154On error, a different error code defined in
155.In fido/err.h
156is returned.
157.Sh SEE ALSO
158.Xr fido_dev_info_manifest 3 ,
159.Xr fido_dev_set_io_functions 3
diff --git a/man/fido_dev_set_io_functions.3 b/man/fido_dev_set_io_functions.3
new file mode 100644
index 0000000..adc4a9e
--- /dev/null
+++ b/man/fido_dev_set_io_functions.3
@@ -0,0 +1,95 @@
1.\" Copyright (c) 2018 Yubico AB. All rights reserved.
2.\" Use of this source code is governed by a BSD-style
3.\" license that can be found in the LICENSE file.
4.\"
5.Dd $Mdocdate: May 25 2018 $
6.Dt FIDO_DEV_SET_IO_FUNCTIONS 3
7.Os
8.Sh NAME
9.Nm fido_dev_set_io_functions
10.Nd FIDO 2 device I/O interface
11.Sh SYNOPSIS
12.In fido.h
13.Bd -literal
14typedef void *fido_dev_io_open_t(const char *);
15typedef void fido_dev_io_close_t(void *);
16typedef int fido_dev_io_read_t(void *, unsigned char *, size_t, int);
17typedef int fido_dev_io_write_t(void *, const unsigned char *, size_t);
18
19typedef struct fido_dev_io {
20 fido_dev_io_open_t *open;
21 fido_dev_io_close_t *close;
22 fido_dev_io_read_t *read;
23 fido_dev_io_write_t *write;
24} fido_dev_io_t;
25.Ed
26.Ft int
27.Fn fido_dev_set_io_functions "fido_dev_t *dev" "const fido_dev_io_t *io"
28.Sh DESCRIPTION
29The
30.Nm
31interface defines the I/O handlers used to talk to
32.Fa dev .
33Its usage is optional.
34By default,
35.Em libfido2
36will use the operating system's native HID interface to talk to
37a FIDO device.
38.Pp
39A
40.Vt fido_dev_io_open_t
41function is expected to return a non-NULL opaque pointer on success,
42and NULL on error.
43The returned opaque pointer is never dereferenced by
44.Em libfido2 .
45.Pp
46A
47.Vt fido_dev_io_close_t
48function receives the opaque handle obtained from
49.Vt fido_dev_io_open_t .
50It is not expected to be idempotent.
51.Pp
52A
53.Vt fido_dev_io_read_t
54function reads from
55.Fa dev .
56The first parameter taken is the opaque handle obtained from
57.Vt fido_dev_io_open_t .
58The read buffer is pointed to by the second parameter, and the
59third parameter holds its size.
60Finally, the last argument passed to
61.Vt fido_dev_io_read_t
62is the number of milliseconds the caller is willing to sleep,
63should the call need to block.
64If this value holds -1,
65.Vt fido_dev_io_read_t
66may block indefinitely.
67The number of bytes read is returned.
68On error, -1 is returned.
69.Pp
70Conversely, a
71.Vt fido_dev_io_write_t
72function writes to
73.Fa dev .
74The first parameter taken is the opaque handle returned by
75.Vt fido_dev_io_open_t .
76The write buffer is pointed to by the second parameter, and the
77third parameter holds its size.
78A
79.Vt fido_dev_io_write_t
80function may block.
81The number of bytes written is returned.
82On error, -1 is returned.
83.Pp
84No references to
85.Fa io
86are held by
87.Fn fido_dev_set_io_functions .
88.Sh RETURN VALUES
89On success,
90.Fn fido_dev_set_io_functions
91returns
92.Dv FIDO_OK .
93On error, a different error code defined in
94.In fido/err.h
95is returned.
diff --git a/man/fido_dev_set_pin.3 b/man/fido_dev_set_pin.3
new file mode 100644
index 0000000..94f841b
--- /dev/null
+++ b/man/fido_dev_set_pin.3
@@ -0,0 +1,88 @@
1.\" Copyright (c) 2018 Yubico AB. All rights reserved.
2.\" Use of this source code is governed by a BSD-style
3.\" license that can be found in the LICENSE file.
4.\"
5.Dd $Mdocdate: May 25 2018 $
6.Dt FIDO_DEV_SET_PIN 3
7.Os
8.Sh NAME
9.Nm fido_dev_set_pin ,
10.Nm fido_dev_get_retry_count ,
11.Nm fido_dev_reset
12.Nd FIDO 2 device management functions
13.Sh SYNOPSIS
14.In fido.h
15.Ft int
16.Fn fido_dev_set_pin "fido_dev_t *dev" "const char *pin" "const char *oldpin"
17.Ft int
18.Fn fido_dev_get_retry_count "fido_dev_t *dev" "int *retries"
19.Ft int
20.Fn fido_dev_reset "fido_dev_t *dev"
21.Sh DESCRIPTION
22The
23.Fn fido_dev_set_pin
24function sets the PIN of device
25.Fa dev
26to
27.Fa pin ,
28where
29.Fa pin
30is a NUL-terminated UTF-8 string.
31If
32.Fa oldpin
33is not NULL, the device's PIN is changed from
34.Fa oldpin
35to
36.Fa pin ,
37where
38.Fa pin
39and
40.Fa oldpin
41are NUL-terminated UTF-8 strings.
42.Pp
43The
44.Fn fido_dev_get_retry_count
45function fills
46.Fa retries
47with the number of PIN retries left in
48.Fa dev
49before lock-out, where
50.Fa retries
51is an addressable pointer.
52.Pp
53The
54.Fn fido_dev_reset
55function performs a reset on
56.Fa dev ,
57resetting the device's PIN and erasing credentials stored on the
58device.
59.Pp
60Please note that
61.Fn fido_dev_set_pin ,
62.Fn fido_dev_get_retry_count ,
63and
64.Fn fido_dev_reset
65are synchronous and will block if necessary.
66.Sh RETURN VALUES
67The error codes returned by
68.Fn fido_dev_set_pin ,
69.Fn fido_dev_get_retry_count ,
70and
71.Fn fido_dev_reset
72are defined in
73.In fido/err.h .
74On success,
75.Dv FIDO_OK
76is returned.
77.Sh CAVEATS
78Regarding
79.Fn fido_dev_reset ,
80the actual user-flow to perform a reset is outside the scope of the
81FIDO2 specification, and may therefore vary depending on the
82authenticator.
83Yubico authenticators will return
84.Dv FIDO_ERR_NOT_ALLOWED
85if a reset is issued later than 5 seconds after power-up, and
86.Dv FIDO_ERR_ACTION_TIMEOUT
87if the user fails to confirm the reset by touching the key
88within 30 seconds.
diff --git a/man/fido_init.3 b/man/fido_init.3
new file mode 100644
index 0000000..7f38948
--- /dev/null
+++ b/man/fido_init.3
@@ -0,0 +1,40 @@
1.\" Copyright (c) 2018 Yubico AB. All rights reserved.
2.\" Use of this source code is governed by a BSD-style
3.\" license that can be found in the LICENSE file.
4.\"
5.Dd $Mdocdate: May 25 2018 $
6.Dt FIDO_INIT 3
7.Os
8.Sh NAME
9.Nm fido_init
10.Nd initialise the FIDO 2 library
11.Sh SYNOPSIS
12.In fido.h
13.Ft void
14.Fn fido_init "int flags"
15.Sh DESCRIPTION
16The
17.Fn fido_init
18function initialises the
19.Em libfido2
20library.
21Its invocation must precede that of any other
22.Em libfido2
23function.
24If
25.Dv FIDO_DEBUG
26is set in
27.Fa flags ,
28then
29debug output will be emitted by
30.Em libfido2
31on
32.Em stderr .
33Alternatively, the
34.Ev FIDO_DEBUG
35environment variable may be set.
36.Sh SEE ALSO
37.Xr fido_assert_new 3 ,
38.Xr fido_cred_new 3 ,
39.Xr fido_dev_info_manifest 3 ,
40.Xr fido_dev_open 3
diff --git a/man/fido_strerr.3 b/man/fido_strerr.3
new file mode 100644
index 0000000..05c86b9
--- /dev/null
+++ b/man/fido_strerr.3
@@ -0,0 +1,27 @@
1.\" Copyright (c) 2018 Yubico AB. All rights reserved.
2.\" Use of this source code is governed by a BSD-style
3.\" license that can be found in the LICENSE file.
4.\"
5.Dd $Mdocdate: May 25 2018 $
6.Dt FIDO_STRERR 3
7.Os
8.Sh NAME
9.Nm fido_strerr
10.Nd FIDO 2 error codes
11.Sh SYNOPSIS
12.In fido.h
13.Ft const char *
14.Fn fido_strerr "int n"
15.Sh DESCRIPTION
16The
17.Fn fido_strerr
18function translates the error code
19.Fa n
20into a readable string,
21where
22.Fa n
23is an error code defined in
24.In fido/err.h .
25.Fn fido_strerr
26never returns NULL.
27Returned pointers point to static strings.
diff --git a/man/rs256_pk_new.3 b/man/rs256_pk_new.3
new file mode 100644
index 0000000..4ad0ebe
--- /dev/null
+++ b/man/rs256_pk_new.3
@@ -0,0 +1,122 @@
1.\" Copyright (c) 2018 Yubico AB. All rights reserved.
2.\" Use of this source code is governed by a BSD-style
3.\" license that can be found in the LICENSE file.
4.\"
5.Dd $Mdocdate: May 24 2018 $
6.Dt RS256_PK_NEW 3
7.Os
8.Sh NAME
9.Nm rs256_pk_new ,
10.Nm rs256_pk_free ,
11.Nm rs256_pk_from_RSA ,
12.Nm rs256_pk_from_ptr ,
13.Nm rs256_pk_to_EVP_PKEY
14.Nd FIDO 2 COSE RS256 API
15.Sh SYNOPSIS
16.In openssl/rsa.h
17.In fido/rs256.h
18.Ft rs256_pk_t *
19.Fn rs256_pk_new "void"
20.Ft void
21.Fn rs256_pk_free "rs256_pk_t **pkp"
22.Ft int
23.Fn rs256_pk_from_RSA "rs256_pk_t *pk" "const RSA *rsa"
24.Ft int
25.Fn rs256_pk_from_ptr "rs256_pk_t *pk" "const void *ptr" "size_t len"
26.Ft EVP_PKEY *
27.Fn rs256_pk_to_EVP_PKEY "const rs256_pk_t *pk"
28.Sh DESCRIPTION
29RS256 is the name given in the CBOR Object Signing and Encryption
30(COSE) RFC to PKCS#1.5 2048-bit RSA with SHA-256.
31The COSE RS256 API of
32.Em libfido2
33is an auxiliary API with routines to convert between the different
34RSA public key types used in
35.Em libfido2
36and
37.Em OpenSSL .
38.Pp
39In
40.Em libfido2 ,
41RS256 public keys are abstracted by the
42.Vt rs256_pk_t
43type.
44.Pp
45The
46.Fn rs256_pk_new
47function returns a pointer to a newly allocated, empty
48.Vt rs256_pk_t
49type.
50If memory cannot be allocated, NULL is returned.
51.Pp
52The
53.Fn rs256_pk_free
54function releases the memory backing
55.Fa *pkp ,
56where
57.Fa *pkp
58must have been previously allocated by
59.Fn rs256_pk_new .
60On return,
61.Fa *pkp
62is set to NULL.
63Either
64.Fa pkp
65or
66.Fa *pkp
67may be NULL, in which case
68.Fn rs256_pk_free
69is a NOP.
70.Pp
71The
72.Fn rs256_pk_from_RSA
73function fills
74.Fa pk
75with the contents of
76.Fa rsa .
77No references to
78.Fa rsa
79are kept.
80.Pp
81The
82.Fn rs256_pk_from_ptr
83function fills
84.Fa pk
85with the contents of
86.Fa ptr ,
87where
88.Fa ptr
89points to
90.Fa len
91bytes.
92No references to
93.Fa ptr
94are kept.
95.Pp
96The
97.Fn rs256_pk_to_EVP_PKEY
98function converts
99.Fa pk
100to a newly allocated
101.Fa EVP_PKEY
102type with a reference count of 1.
103No internal references to the returned pointer are kept.
104If an error occurs,
105.Fn rs256_pk_to_EVP_PKEY
106returns NULL.
107.Sh RETURN VALUES
108The
109.Fn rs256_pk_from_RSA
110and
111.Fn rs256_pk_from_ptr
112functions return
113.Dv FIDO_OK
114on success.
115On error, a different error code defined in
116.In fido/err.h
117is returned.
118.Sh SEE ALSO
119.Xr eddsa_pk_new 3 ,
120.Xr es256_pk_new 3 ,
121.Xr fido_assert_verify 3 ,
122.Xr fido_cred_pubkey_ptr 3
diff --git a/man/style.css b/man/style.css
new file mode 100644
index 0000000..8c223fa
--- /dev/null
+++ b/man/style.css
@@ -0,0 +1,24 @@
1* { margin: 0; padding: 0; }
2
3body {
4 font-family: monospace;
5 font-size: 1em;
6 margin: 2% auto;
7 max-width: 54em;
8}
9
10ul { margin-left: 1em; }
11a { color: #009900; }
12.Sh { font-size: 1em; padding-top: 1em; padding-bottom: 1em; }
13.foot { padding-top: 1em; }
14
15table.head, table.foot { width: 100%; }
16td.head-rtitle, td.foot-os { text-align: right; }
17td.head-vol { text-align: center; }
18div.Pp { margin: 1ex 0ex; }
19div.Nd, div.Bf, div.Op { display: inline; }
20span.Pa, span.Ad { font-style: italic; }
21span.Ms { font-weight: bold; }
22dl.Bl-diag > dt { font-weight: bold; }
23code.Nm, code.Fl, code.Cm, code.Ic, code.In, code.Fd, code.Fn,
24code.Cd { font-weight: bold; font-family: inherit; }
diff --git a/openbsd-compat/bsd-getline.c b/openbsd-compat/bsd-getline.c
new file mode 100644
index 0000000..52b44f7
--- /dev/null
+++ b/openbsd-compat/bsd-getline.c
@@ -0,0 +1,115 @@
1/* $NetBSD: getline.c,v 1.1.1.6 2015/01/02 20:34:27 christos Exp $ */
2
3/* NetBSD: getline.c,v 1.2 2014/09/16 17:23:50 christos Exp */
4
5/*-
6 * Copyright (c) 2011 The NetBSD Foundation, Inc.
7 * All rights reserved.
8 *
9 * This code is derived from software contributed to The NetBSD Foundation
10 * by Christos Zoulas.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34/* NETBSD ORIGINAL: external/bsd/file/dist/src/getline.c */
35
36#include "openbsd-compat.h"
37
38#if 0
39#include "file.h"
40#endif
41
42#if !HAVE_GETLINE
43#include <stdlib.h>
44#include <stdio.h>
45#ifdef HAVE_UNISTD_H
46#include <unistd.h>
47#endif
48#include <errno.h>
49#include <string.h>
50
51static ssize_t
52getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp)
53{
54 char *ptr, *eptr;
55
56
57 if (*buf == NULL || *bufsiz == 0) {
58 if ((*buf = malloc(BUFSIZ)) == NULL)
59 return -1;
60 *bufsiz = BUFSIZ;
61 }
62
63 for (ptr = *buf, eptr = *buf + *bufsiz;;) {
64 int c = fgetc(fp);
65 if (c == -1) {
66 if (feof(fp)) {
67 ssize_t diff = (ssize_t)(ptr - *buf);
68 if (diff != 0) {
69 *ptr = '\0';
70 return diff;
71 }
72 }
73 return -1;
74 }
75 *ptr++ = (char)c;
76 if (c == delimiter) {
77 *ptr = '\0';
78 return ptr - *buf;
79 }
80 if (ptr + 2 >= eptr) {
81 char *nbuf;
82 size_t nbufsiz = *bufsiz * 2;
83 ssize_t d = ptr - *buf;
84 if ((nbuf = realloc(*buf, nbufsiz)) == NULL)
85 return -1;
86 *buf = nbuf;
87 *bufsiz = nbufsiz;
88 eptr = nbuf + nbufsiz;
89 ptr = nbuf + d;
90 }
91 }
92}
93
94ssize_t
95getline(char **buf, size_t *bufsiz, FILE *fp)
96{
97 return getdelim(buf, bufsiz, '\n', fp);
98}
99
100#endif
101
102#ifdef TEST
103int
104main(int argc, char *argv[])
105{
106 char *p = NULL;
107 ssize_t len;
108 size_t n = 0;
109
110 while ((len = getline(&p, &n, stdin)) != -1)
111 (void)printf("%" SIZE_T_FORMAT "d %s", len, p);
112 free(p);
113 return 0;
114}
115#endif
diff --git a/openbsd-compat/bsd-getpagesize.c b/openbsd-compat/bsd-getpagesize.c
new file mode 100644
index 0000000..903bfc3
--- /dev/null
+++ b/openbsd-compat/bsd-getpagesize.c
@@ -0,0 +1,27 @@
1/* Placed in the public domain */
2
3#include "openbsd-compat.h"
4
5#if !defined(HAVE_GETPAGESIZE)
6
7#ifdef HAVE_UNISTD_H
8#include <unistd.h>
9#endif
10#include <limits.h>
11
12int
13getpagesize(void)
14{
15#if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
16 long r = sysconf(_SC_PAGESIZE);
17 if (r > 0 && r < INT_MAX)
18 return (int)r;
19#endif
20 /*
21 * This is at the lower end of common values and appropriate for
22 * our current use of getpagesize() in recallocarray().
23 */
24 return 4096;
25}
26
27#endif /* !defined(HAVE_GETPAGESIZE) */
diff --git a/openbsd-compat/diff.sh b/openbsd-compat/diff.sh
new file mode 100755
index 0000000..f21e7d8
--- /dev/null
+++ b/openbsd-compat/diff.sh
@@ -0,0 +1,24 @@
1#!/bin/bash -u
2
3# Copyright (c) 2019 Yubico AB. All rights reserved.
4# Use of this source code is governed by a BSD-style
5# license that can be found in the LICENSE file.
6
7OPENSSH=$(realpath ../../openssh)
8LIBRESSL=$(realpath ../../libressl-2.8.3)
9[[ ! -d "${OPENSSH}" || ! -d "${LIBRESSL}" ]] && exit 1
10
11diff -pu bsd-getpagesize.c ${OPENSSH}/openbsd-compat/bsd-getpagesize.c
12diff -pu err.h ${LIBRESSL}/include/compat/err.h
13diff -pu explicit_bzero.c ${OPENSSH}/openbsd-compat/explicit_bzero.c
14diff -pu explicit_bzero_win32.c ${LIBRESSL}/crypto/compat/explicit_bzero_win.c
15diff -pu getopt.h ${OPENSSH}/openbsd-compat/getopt.h
16diff -pu getopt_long.c ${OPENSSH}/openbsd-compat/getopt_long.c
17diff -pu posix_win.c ${LIBRESSL}/crypto/compat/posix_win.c
18diff -pu readpassphrase.c ${OPENSSH}/openbsd-compat/readpassphrase.c
19diff -pu readpassphrase.h ${OPENSSH}/openbsd-compat/readpassphrase.h
20diff -pu recallocarray.c ${OPENSSH}/openbsd-compat/recallocarray.c
21diff -pu strlcat.c ${OPENSSH}/openbsd-compat/strlcat.c
22diff -pu strlcpy.c ${OPENSSH}/openbsd-compat/strlcpy.c
23diff -pu timingsafe_bcmp.c ${OPENSSH}/openbsd-compat/timingsafe_bcmp.c
24diff -pu types.h ${LIBRESSL}/include/compat/sys/types.h
diff --git a/openbsd-compat/err.h b/openbsd-compat/err.h
new file mode 100644
index 0000000..394c7bb
--- /dev/null
+++ b/openbsd-compat/err.h
@@ -0,0 +1,85 @@
1/*
2 * Public domain
3 * err.h compatibility shim
4 */
5
6#ifndef _COMPAT_ERR_H
7#define _COMPAT_ERR_H
8
9#if !defined(HAVE_ERR_H)
10
11#include <errno.h>
12#include <stdarg.h>
13#include <stdlib.h>
14#include <stdio.h>
15#include <string.h>
16
17#if defined(_MSC_VER)
18__declspec(noreturn)
19#else
20__attribute__((noreturn))
21#endif
22static inline void
23err(int eval, const char *fmt, ...)
24{
25 int sverrno = errno;
26 va_list ap;
27
28 va_start(ap, fmt);
29 if (fmt != NULL) {
30 vfprintf(stderr, fmt, ap);
31 fprintf(stderr, ": ");
32 }
33 va_end(ap);
34 fprintf(stderr, "%s\n", strerror(sverrno));
35 exit(eval);
36}
37
38#if defined(_MSC_VER)
39__declspec(noreturn)
40#else
41__attribute__((noreturn))
42#endif
43static inline void
44errx(int eval, const char *fmt, ...)
45{
46 va_list ap;
47
48 va_start(ap, fmt);
49 if (fmt != NULL)
50 vfprintf(stderr, fmt, ap);
51 va_end(ap);
52 fprintf(stderr, "\n");
53 exit(eval);
54}
55
56static inline void
57warn(const char *fmt, ...)
58{
59 int sverrno = errno;
60 va_list ap;
61
62 va_start(ap, fmt);
63 if (fmt != NULL) {
64 vfprintf(stderr, fmt, ap);
65 fprintf(stderr, ": ");
66 }
67 va_end(ap);
68 fprintf(stderr, "%s\n", strerror(sverrno));
69}
70
71static inline void
72warnx(const char *fmt, ...)
73{
74 va_list ap;
75
76 va_start(ap, fmt);
77 if (fmt != NULL)
78 vfprintf(stderr, fmt, ap);
79 va_end(ap);
80 fprintf(stderr, "\n");
81}
82
83#endif /* !defined(HAVE_ERR_H) */
84
85#endif /* _COMPAT_ERR_H */
diff --git a/openbsd-compat/explicit_bzero.c b/openbsd-compat/explicit_bzero.c
new file mode 100644
index 0000000..ac64e69
--- /dev/null
+++ b/openbsd-compat/explicit_bzero.c
@@ -0,0 +1,57 @@
1/* OPENBSD ORIGINAL: lib/libc/string/explicit_bzero.c */
2/* $OpenBSD: explicit_bzero.c,v 1.1 2014/01/22 21:06:45 tedu Exp $ */
3/*
4 * Public domain.
5 * Written by Ted Unangst
6 */
7
8#include "openbsd-compat.h"
9
10#if !defined(HAVE_EXPLICIT_BZERO) && !defined(_WIN32)
11
12#include <string.h>
13
14/*
15 * explicit_bzero - don't let the compiler optimize away bzero
16 */
17
18#ifdef HAVE_MEMSET_S
19
20void
21explicit_bzero(void *p, size_t n)
22{
23 if (n == 0)
24 return;
25 (void)memset_s(p, n, 0, n);
26}
27
28#else /* HAVE_MEMSET_S */
29
30/*
31 * Indirect bzero through a volatile pointer to hopefully avoid
32 * dead-store optimisation eliminating the call.
33 */
34static void (* volatile ssh_bzero)(void *, size_t) = bzero;
35
36void
37explicit_bzero(void *p, size_t n)
38{
39 if (n == 0)
40 return;
41 /*
42 * clang -fsanitize=memory needs to intercept memset-like functions
43 * to correctly detect memory initialisation. Make sure one is called
44 * directly since our indirection trick above successfully confuses it.
45 */
46#if defined(__has_feature)
47# if __has_feature(memory_sanitizer)
48 memset(p, 0, n);
49# endif
50#endif
51
52 ssh_bzero(p, n);
53}
54
55#endif /* HAVE_MEMSET_S */
56
57#endif /* !defined(HAVE_EXPLICIT_BZERO) && !defined(_WIN32) */
diff --git a/openbsd-compat/explicit_bzero_win32.c b/openbsd-compat/explicit_bzero_win32.c
new file mode 100644
index 0000000..8017aff
--- /dev/null
+++ b/openbsd-compat/explicit_bzero_win32.c
@@ -0,0 +1,19 @@
1/*
2 * Public domain.
3 * Win32 explicit_bzero compatibility shim.
4 */
5
6#include "openbsd-compat.h"
7
8#if !defined(HAVE_EXPLICIT_BZERO) && defined(_WIN32)
9
10#include <windows.h>
11#include <string.h>
12
13void
14explicit_bzero(void *buf, size_t len)
15{
16 SecureZeroMemory(buf, len);
17}
18
19#endif /* !defined(HAVE_EXPLICIT_BZERO) && defined(_WIN32) */
diff --git a/openbsd-compat/getopt.h b/openbsd-compat/getopt.h
new file mode 100644
index 0000000..8eb1244
--- /dev/null
+++ b/openbsd-compat/getopt.h
@@ -0,0 +1,74 @@
1/* $OpenBSD: getopt.h,v 1.2 2008/06/26 05:42:04 ray Exp $ */
2/* $NetBSD: getopt.h,v 1.4 2000/07/07 10:43:54 ad Exp $ */
3
4/*-
5 * Copyright (c) 2000 The NetBSD Foundation, Inc.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by Dieter Baron and Thomas Klausner.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#ifndef _GETOPT_H_
34#define _GETOPT_H_
35
36/*
37 * GNU-like getopt_long() and 4.4BSD getsubopt()/optreset extensions
38 */
39#define no_argument 0
40#define required_argument 1
41#define optional_argument 2
42
43struct option {
44 /* name of long option */
45 const char *name;
46 /*
47 * one of no_argument, required_argument, and optional_argument:
48 * whether option takes an argument
49 */
50 int has_arg;
51 /* if not NULL, set *flag to val when option found */
52 int *flag;
53 /* if flag not NULL, value to set *flag to; else return value */
54 int val;
55};
56
57int getopt_long(int, char * const *, const char *,
58 const struct option *, int *);
59int getopt_long_only(int, char * const *, const char *,
60 const struct option *, int *);
61#ifndef _GETOPT_DEFINED_
62#define _GETOPT_DEFINED_
63int getopt(int, char * const *, const char *);
64int getsubopt(char **, char * const *, char **);
65
66extern char *optarg; /* getopt(3) external variables */
67extern int opterr;
68extern int optind;
69extern int optopt;
70extern int optreset;
71extern char *suboptarg; /* getsubopt(3) external variable */
72#endif
73
74#endif /* !_GETOPT_H_ */
diff --git a/openbsd-compat/getopt_long.c b/openbsd-compat/getopt_long.c
new file mode 100644
index 0000000..dabbb46
--- /dev/null
+++ b/openbsd-compat/getopt_long.c
@@ -0,0 +1,523 @@
1/* $OpenBSD: getopt_long.c,v 1.25 2011/03/05 22:10:11 guenther Exp $ */
2/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */
3
4/*
5 * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 *
19 * Sponsored in part by the Defense Advanced Research Projects
20 * Agency (DARPA) and Air Force Research Laboratory, Air Force
21 * Materiel Command, USAF, under agreement number F39502-99-1-0512.
22 */
23/*-
24 * Copyright (c) 2000 The NetBSD Foundation, Inc.
25 * All rights reserved.
26 *
27 * This code is derived from software contributed to The NetBSD Foundation
28 * by Dieter Baron and Thomas Klausner.
29 *
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions
32 * are met:
33 * 1. Redistributions of source code must retain the above copyright
34 * notice, this list of conditions and the following disclaimer.
35 * 2. Redistributions in binary form must reproduce the above copyright
36 * notice, this list of conditions and the following disclaimer in the
37 * documentation and/or other materials provided with the distribution.
38 *
39 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
40 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
41 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
42 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
43 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
44 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
45 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
46 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
47 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
48 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
49 * POSSIBILITY OF SUCH DAMAGE.
50 */
51
52/* OPENBSD ORIGINAL: lib/libc/stdlib/getopt_long.c */
53#include "openbsd-compat.h"
54
55#if !defined(HAVE_GETOPT)
56
57#if 0
58#include <err.h>
59#include <getopt.h>
60#endif
61#include <errno.h>
62#include <stdlib.h>
63#include <string.h>
64#include <stdarg.h>
65
66int opterr = 1; /* if error message should be printed */
67int optind = 1; /* index into parent argv vector */
68int optopt = '?'; /* character checked for validity */
69int optreset; /* reset getopt */
70char *optarg; /* argument associated with option */
71
72#define PRINT_ERROR ((opterr) && (*options != ':'))
73
74#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */
75#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */
76#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
77
78/* return values */
79#define BADCH (int)'?'
80#define BADARG ((*options == ':') ? (int)':' : (int)'?')
81#define INORDER (int)1
82
83#define EMSG ""
84
85static int getopt_internal(int, char * const *, const char *,
86 const struct option *, int *, int);
87static int parse_long_options(char * const *, const char *,
88 const struct option *, int *, int);
89static int gcd(int, int);
90static void permute_args(int, int, int, char * const *);
91
92static char *place = EMSG; /* option letter processing */
93
94/* XXX: set optreset to 1 rather than these two */
95static int nonopt_start = -1; /* first non option argument (for permute) */
96static int nonopt_end = -1; /* first option after non options (for permute) */
97
98/* Error messages */
99static const char recargchar[] = "option requires an argument -- %c";
100static const char recargstring[] = "option requires an argument -- %s";
101static const char ambig[] = "ambiguous option -- %.*s";
102static const char noarg[] = "option doesn't take an argument -- %.*s";
103static const char illoptchar[] = "unknown option -- %c";
104static const char illoptstring[] = "unknown option -- %s";
105
106/*
107 * Compute the greatest common divisor of a and b.
108 */
109static int
110gcd(int a, int b)
111{
112 int c;
113
114 c = a % b;
115 while (c != 0) {
116 a = b;
117 b = c;
118 c = a % b;
119 }
120
121 return (b);
122}
123
124/*
125 * Exchange the block from nonopt_start to nonopt_end with the block
126 * from nonopt_end to opt_end (keeping the same order of arguments
127 * in each block).
128 */
129static void
130permute_args(int panonopt_start, int panonopt_end, int opt_end,
131 char * const *nargv)
132{
133 int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
134 char *swap;
135
136 /*
137 * compute lengths of blocks and number and size of cycles
138 */
139 nnonopts = panonopt_end - panonopt_start;
140 nopts = opt_end - panonopt_end;
141 ncycle = gcd(nnonopts, nopts);
142 cyclelen = (opt_end - panonopt_start) / ncycle;
143
144 for (i = 0; i < ncycle; i++) {
145 cstart = panonopt_end+i;
146 pos = cstart;
147 for (j = 0; j < cyclelen; j++) {
148 if (pos >= panonopt_end)
149 pos -= nnonopts;
150 else
151 pos += nopts;
152 swap = nargv[pos];
153 /* LINTED const cast */
154 ((char **) nargv)[pos] = nargv[cstart];
155 /* LINTED const cast */
156 ((char **)nargv)[cstart] = swap;
157 }
158 }
159}
160
161/*
162 * parse_long_options --
163 * Parse long options in argc/argv argument vector.
164 * Returns -1 if short_too is set and the option does not match long_options.
165 */
166static int
167parse_long_options(char * const *nargv, const char *options,
168 const struct option *long_options, int *idx, int short_too)
169{
170 char *current_argv, *has_equal;
171 size_t current_argv_len;
172 int i, match;
173
174 current_argv = place;
175 match = -1;
176
177 optind++;
178
179 if ((has_equal = strchr(current_argv, '=')) != NULL) {
180 /* argument found (--option=arg) */
181 current_argv_len = has_equal - current_argv;
182 has_equal++;
183 } else
184 current_argv_len = strlen(current_argv);
185
186 for (i = 0; long_options[i].name; i++) {
187 /* find matching long option */
188 if (strncmp(current_argv, long_options[i].name,
189 current_argv_len))
190 continue;
191
192 if (strlen(long_options[i].name) == current_argv_len) {
193 /* exact match */
194 match = i;
195 break;
196 }
197 /*
198 * If this is a known short option, don't allow
199 * a partial match of a single character.
200 */
201 if (short_too && current_argv_len == 1)
202 continue;
203
204 if (match == -1) /* partial match */
205 match = i;
206 else {
207 /* ambiguous abbreviation */
208 if (PRINT_ERROR)
209 warnx(ambig, (int)current_argv_len,
210 current_argv);
211 optopt = 0;
212 return (BADCH);
213 }
214 }
215 if (match != -1) { /* option found */
216 if (long_options[match].has_arg == no_argument
217 && has_equal) {
218 if (PRINT_ERROR)
219 warnx(noarg, (int)current_argv_len,
220 current_argv);
221 /*
222 * XXX: GNU sets optopt to val regardless of flag
223 */
224 if (long_options[match].flag == NULL)
225 optopt = long_options[match].val;
226 else
227 optopt = 0;
228 return (BADARG);
229 }
230 if (long_options[match].has_arg == required_argument ||
231 long_options[match].has_arg == optional_argument) {
232 if (has_equal)
233 optarg = has_equal;
234 else if (long_options[match].has_arg ==
235 required_argument) {
236 /*
237 * optional argument doesn't use next nargv
238 */
239 optarg = nargv[optind++];
240 }
241 }
242 if ((long_options[match].has_arg == required_argument)
243 && (optarg == NULL)) {
244 /*
245 * Missing argument; leading ':' indicates no error
246 * should be generated.
247 */
248 if (PRINT_ERROR)
249 warnx(recargstring,
250 current_argv);
251 /*
252 * XXX: GNU sets optopt to val regardless of flag
253 */
254 if (long_options[match].flag == NULL)
255 optopt = long_options[match].val;
256 else
257 optopt = 0;
258 --optind;
259 return (BADARG);
260 }
261 } else { /* unknown option */
262 if (short_too) {
263 --optind;
264 return (-1);
265 }
266 if (PRINT_ERROR)
267 warnx(illoptstring, current_argv);
268 optopt = 0;
269 return (BADCH);
270 }
271 if (idx)
272 *idx = match;
273 if (long_options[match].flag) {
274 *long_options[match].flag = long_options[match].val;
275 return (0);
276 } else
277 return (long_options[match].val);
278}
279
280/*
281 * getopt_internal --
282 * Parse argc/argv argument vector. Called by user level routines.
283 */
284static int
285getopt_internal(int nargc, char * const *nargv, const char *options,
286 const struct option *long_options, int *idx, int flags)
287{
288 char *oli; /* option letter list index */
289 int optchar, short_too;
290 static int posixly_correct = -1;
291
292 if (options == NULL)
293 return (-1);
294
295 /*
296 * XXX Some GNU programs (like cvs) set optind to 0 instead of
297 * XXX using optreset. Work around this braindamage.
298 */
299 if (optind == 0)
300 optind = optreset = 1;
301
302 /*
303 * Disable GNU extensions if POSIXLY_CORRECT is set or options
304 * string begins with a '+'.
305 */
306 if (posixly_correct == -1 || optreset)
307 posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
308 if (*options == '-')
309 flags |= FLAG_ALLARGS;
310 else if (posixly_correct || *options == '+')
311 flags &= ~FLAG_PERMUTE;
312 if (*options == '+' || *options == '-')
313 options++;
314
315 optarg = NULL;
316 if (optreset)
317 nonopt_start = nonopt_end = -1;
318start:
319 if (optreset || !*place) { /* update scanning pointer */
320 optreset = 0;
321 if (optind >= nargc) { /* end of argument vector */
322 place = EMSG;
323 if (nonopt_end != -1) {
324 /* do permutation, if we have to */
325 permute_args(nonopt_start, nonopt_end,
326 optind, nargv);
327 optind -= nonopt_end - nonopt_start;
328 }
329 else if (nonopt_start != -1) {
330 /*
331 * If we skipped non-options, set optind
332 * to the first of them.
333 */
334 optind = nonopt_start;
335 }
336 nonopt_start = nonopt_end = -1;
337 return (-1);
338 }
339 if (*(place = nargv[optind]) != '-' ||
340 (place[1] == '\0' && strchr(options, '-') == NULL)) {
341 place = EMSG; /* found non-option */
342 if (flags & FLAG_ALLARGS) {
343 /*
344 * GNU extension:
345 * return non-option as argument to option 1
346 */
347 optarg = nargv[optind++];
348 return (INORDER);
349 }
350 if (!(flags & FLAG_PERMUTE)) {
351 /*
352 * If no permutation wanted, stop parsing
353 * at first non-option.
354 */
355 return (-1);
356 }
357 /* do permutation */
358 if (nonopt_start == -1)
359 nonopt_start = optind;
360 else if (nonopt_end != -1) {
361 permute_args(nonopt_start, nonopt_end,
362 optind, nargv);
363 nonopt_start = optind -
364 (nonopt_end - nonopt_start);
365 nonopt_end = -1;
366 }
367 optind++;
368 /* process next argument */
369 goto start;
370 }
371 if (nonopt_start != -1 && nonopt_end == -1)
372 nonopt_end = optind;
373
374 /*
375 * If we have "-" do nothing, if "--" we are done.
376 */
377 if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
378 optind++;
379 place = EMSG;
380 /*
381 * We found an option (--), so if we skipped
382 * non-options, we have to permute.
383 */
384 if (nonopt_end != -1) {
385 permute_args(nonopt_start, nonopt_end,
386 optind, nargv);
387 optind -= nonopt_end - nonopt_start;
388 }
389 nonopt_start = nonopt_end = -1;
390 return (-1);
391 }
392 }
393
394 /*
395 * Check long options if:
396 * 1) we were passed some
397 * 2) the arg is not just "-"
398 * 3) either the arg starts with -- we are getopt_long_only()
399 */
400 if (long_options != NULL && place != nargv[optind] &&
401 (*place == '-' || (flags & FLAG_LONGONLY))) {
402 short_too = 0;
403 if (*place == '-')
404 place++; /* --foo long option */
405 else if (*place != ':' && strchr(options, *place) != NULL)
406 short_too = 1; /* could be short option too */
407
408 optchar = parse_long_options(nargv, options, long_options,
409 idx, short_too);
410 if (optchar != -1) {
411 place = EMSG;
412 return (optchar);
413 }
414 }
415
416 if ((optchar = (int)*place++) == (int)':' ||
417 (optchar == (int)'-' && *place != '\0') ||
418 (oli = strchr(options, optchar)) == NULL) {
419 /*
420 * If the user specified "-" and '-' isn't listed in
421 * options, return -1 (non-option) as per POSIX.
422 * Otherwise, it is an unknown option character (or ':').
423 */
424 if (optchar == (int)'-' && *place == '\0')
425 return (-1);
426 if (!*place)
427 ++optind;
428 if (PRINT_ERROR)
429 warnx(illoptchar, optchar);
430 optopt = optchar;
431 return (BADCH);
432 }
433 if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
434 /* -W long-option */
435 if (*place) /* no space */
436 /* NOTHING */;
437 else if (++optind >= nargc) { /* no arg */
438 place = EMSG;
439 if (PRINT_ERROR)
440 warnx(recargchar, optchar);
441 optopt = optchar;
442 return (BADARG);
443 } else /* white space */
444 place = nargv[optind];
445 optchar = parse_long_options(nargv, options, long_options,
446 idx, 0);
447 place = EMSG;
448 return (optchar);
449 }
450 if (*++oli != ':') { /* doesn't take argument */
451 if (!*place)
452 ++optind;
453 } else { /* takes (optional) argument */
454 optarg = NULL;
455 if (*place) /* no white space */
456 optarg = place;
457 else if (oli[1] != ':') { /* arg not optional */
458 if (++optind >= nargc) { /* no arg */
459 place = EMSG;
460 if (PRINT_ERROR)
461 warnx(recargchar, optchar);
462 optopt = optchar;
463 return (BADARG);
464 } else
465 optarg = nargv[optind];
466 }
467 place = EMSG;
468 ++optind;
469 }
470 /* dump back option letter */
471 return (optchar);
472}
473
474/*
475 * getopt --
476 * Parse argc/argv argument vector.
477 *
478 * [eventually this will replace the BSD getopt]
479 */
480int
481getopt(int nargc, char * const *nargv, const char *options)
482{
483
484 /*
485 * We don't pass FLAG_PERMUTE to getopt_internal() since
486 * the BSD getopt(3) (unlike GNU) has never done this.
487 *
488 * Furthermore, since many privileged programs call getopt()
489 * before dropping privileges it makes sense to keep things
490 * as simple (and bug-free) as possible.
491 */
492 return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
493}
494
495#if 0
496/*
497 * getopt_long --
498 * Parse argc/argv argument vector.
499 */
500int
501getopt_long(int nargc, char * const *nargv, const char *options,
502 const struct option *long_options, int *idx)
503{
504
505 return (getopt_internal(nargc, nargv, options, long_options, idx,
506 FLAG_PERMUTE));
507}
508
509/*
510 * getopt_long_only --
511 * Parse argc/argv argument vector.
512 */
513int
514getopt_long_only(int nargc, char * const *nargv, const char *options,
515 const struct option *long_options, int *idx)
516{
517
518 return (getopt_internal(nargc, nargv, options, long_options, idx,
519 FLAG_PERMUTE|FLAG_LONGONLY));
520}
521#endif
522
523#endif /* !defined(HAVE_GETOPT) */
diff --git a/openbsd-compat/openbsd-compat.h b/openbsd-compat/openbsd-compat.h
new file mode 100644
index 0000000..d1d8652
--- /dev/null
+++ b/openbsd-compat/openbsd-compat.h
@@ -0,0 +1,87 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#ifndef _OPENBSD_COMPAT_H
8#define _OPENBSD_COMPAT_H
9
10#if defined(_MSC_VER)
11#include "types.h"
12#endif
13
14#if defined(HAVE_ENDIAN_H)
15#include <endian.h>
16#endif
17
18#if defined(__APPLE__) && !defined(HAVE_ENDIAN_H)
19#include <libkern/OSByteOrder.h>
20#define be16toh(x) OSSwapBigToHostInt16((x))
21#define be32toh(x) OSSwapBigToHostInt32((x))
22#endif /* __APPLE__ && !HAVE_ENDIAN_H */
23
24#if defined(_WIN32) && !defined(HAVE_ENDIAN_H)
25#include <winsock2.h>
26#if !defined(_MSC_VER)
27#include <sys/param.h>
28#endif
29#define be16toh(x) ntohs((x))
30#define be32toh(x) ntohl((x))
31#endif /* _WIN32 && !HAVE_ENDIAN_H */
32
33#include <stdlib.h>
34
35#if !defined(HAVE_STRLCAT)
36size_t strlcat(char *, const char *, size_t);
37#endif
38
39#if !defined(HAVE_STRLCPY)
40size_t strlcpy(char *, const char *, size_t);
41#endif
42
43#if !defined(HAVE_RECALLOCARRAY)
44void *recallocarray(void *, size_t, size_t, size_t);
45#endif
46
47#if !defined(HAVE_EXPLICIT_BZERO)
48void explicit_bzero(void *, size_t);
49#endif
50
51#if !defined(HAVE_GETPAGESIZE)
52int getpagesize(void);
53#endif
54
55#if !defined(HAVE_TIMINGSAFE_BCMP)
56int timingsafe_bcmp(const void *, const void *, size_t);
57#endif
58
59#if !defined(HAVE_READPASSPHRASE)
60#include "readpassphrase.h"
61#else
62#include <readpassphrase.h>
63#endif
64
65#if OPENSSL_VERSION_NUMBER < 0x10100000L
66#define EVP_PKEY_get0_EC_KEY(x) ((x)->pkey.ec)
67#define EVP_PKEY_get0_RSA(x) ((x)->pkey.rsa)
68#endif
69
70#if !defined(HAVE_ERR_H)
71#include "err.h"
72#else
73#include <err.h>
74#endif
75
76#if !defined(HAVE_GETOPT)
77#include "getopt.h"
78#else
79#include <unistd.h>
80#endif
81
82#if !defined(HAVE_GETLINE)
83#include <stdio.h>
84ssize_t getline(char **, size_t *, FILE *);
85#endif
86
87#endif /* !_OPENBSD_COMPAT_H */
diff --git a/openbsd-compat/posix_win.c b/openbsd-compat/posix_win.c
new file mode 100644
index 0000000..eac67c2
--- /dev/null
+++ b/openbsd-compat/posix_win.c
@@ -0,0 +1,61 @@
1/*
2 * Public domain
3 *
4 * File IO compatibility shims
5 * Brent Cook <bcook@openbsd.org>
6 */
7
8#define NO_REDEF_POSIX_FUNCTIONS
9
10#include <windows.h>
11
12#include <errno.h>
13#include <io.h>
14
15#include "posix_win.h"
16
17int
18posix_open(const char *path, ...)
19{
20 va_list ap;
21 int mode = 0;
22 int flags;
23
24 va_start(ap, path);
25 flags = va_arg(ap, int);
26 if (flags & O_CREAT)
27 mode = va_arg(ap, int);
28 va_end(ap);
29
30 flags |= O_BINARY | O_NOINHERIT;
31
32 return (open(path, flags, mode));
33}
34
35int
36posix_close(int fd)
37{
38 return (close(fd));
39}
40
41ssize_t
42posix_read(int fd, void *buf, size_t count)
43{
44 if (count > INT_MAX) {
45 errno = EINVAL;
46 return (-1);
47 }
48
49 return (read(fd, buf, (unsigned int)count));
50}
51
52ssize_t
53posix_write(int fd, const void *buf, size_t count)
54{
55 if (count > INT_MAX) {
56 errno = EINVAL;
57 return (-1);
58 }
59
60 return (write(fd, buf, (unsigned int)count));
61}
diff --git a/openbsd-compat/posix_win.h b/openbsd-compat/posix_win.h
new file mode 100644
index 0000000..a1e0888
--- /dev/null
+++ b/openbsd-compat/posix_win.h
@@ -0,0 +1,47 @@
1/*
2 * Public domain
3 *
4 * BSD socket emulation code for Winsock2
5 * Brent Cook <bcook@openbsd.org>
6 */
7
8#ifndef _COMPAT_POSIX_WIN_H
9#define _COMPAT_POSIX_WIN_H
10
11#ifdef _WIN32
12
13#include <windows.h>
14
15#include <errno.h>
16#include <stdarg.h>
17#include <stdint.h>
18#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21
22#if _MSC_VER >= 1900
23#include <../ucrt/fcntl.h>
24#else
25#include <../include/fcntl.h>
26#endif
27
28#include "types.h"
29
30int posix_open(const char *path, ...);
31
32int posix_close(int fd);
33
34ssize_t posix_read(int fd, void *buf, size_t count);
35
36ssize_t posix_write(int fd, const void *buf, size_t count);
37
38#ifndef NO_REDEF_POSIX_FUNCTIONS
39#define open(path, ...) posix_open(path, __VA_ARGS__)
40#define close(fd) posix_close(fd)
41#define read(fd, buf, count) posix_read(fd, buf, count)
42#define write(fd, buf, count) posix_write(fd, buf, count)
43#endif
44
45#endif /* _WIN32 */
46
47#endif /* !_COMPAT_POSIX_WIN_H */
diff --git a/openbsd-compat/readpassphrase.c b/openbsd-compat/readpassphrase.c
new file mode 100644
index 0000000..dfb3065
--- /dev/null
+++ b/openbsd-compat/readpassphrase.c
@@ -0,0 +1,214 @@
1/* $OpenBSD: readpassphrase.c,v 1.26 2016/10/18 12:47:18 millert Exp $ */
2
3/*
4 * Copyright (c) 2000-2002, 2007, 2010
5 * Todd C. Miller <Todd.Miller@courtesan.com>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 *
19 * Sponsored in part by the Defense Advanced Research Projects
20 * Agency (DARPA) and Air Force Research Laboratory, Air Force
21 * Materiel Command, USAF, under agreement number F39502-99-1-0512.
22 */
23
24/* OPENBSD ORIGINAL: lib/libc/gen/readpassphrase.c */
25
26#include "openbsd-compat.h"
27
28#ifndef HAVE_READPASSPHRASE
29
30#include <termios.h>
31#include <signal.h>
32#include <ctype.h>
33#include <fcntl.h>
34#include <errno.h>
35#include <string.h>
36#ifdef HAVE_UNISTD_H
37#include <unistd.h>
38#endif
39#include <paths.h>
40
41#ifndef _PATH_TTY
42# define _PATH_TTY "/dev/tty"
43#endif
44
45#ifndef TCSASOFT
46/* If we don't have TCSASOFT define it so that ORing it it below is a no-op. */
47# define TCSASOFT 0
48#endif
49
50/* SunOS 4.x which lacks _POSIX_VDISABLE, but has VDISABLE */
51#if !defined(_POSIX_VDISABLE) && defined(VDISABLE)
52# define _POSIX_VDISABLE VDISABLE
53#endif
54
55static volatile sig_atomic_t signo[_NSIG];
56
57static void handler(int);
58
59char *
60readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags)
61{
62 ssize_t nr;
63 int input, output, save_errno, i, need_restart;
64 char ch, *p, *end;
65 struct termios term, oterm;
66 struct sigaction sa, savealrm, saveint, savehup, savequit, saveterm;
67 struct sigaction savetstp, savettin, savettou, savepipe;
68
69 /* I suppose we could alloc on demand in this case (XXX). */
70 if (bufsiz == 0) {
71 errno = EINVAL;
72 return(NULL);
73 }
74
75restart:
76 for (i = 0; i < _NSIG; i++)
77 signo[i] = 0;
78 need_restart = 0;
79 /*
80 * Read and write to /dev/tty if available. If not, read from
81 * stdin and write to stderr unless a tty is required.
82 */
83 if ((flags & RPP_STDIN) ||
84 (input = output = open(_PATH_TTY, O_RDWR)) == -1) {
85 if (flags & RPP_REQUIRE_TTY) {
86 errno = ENOTTY;
87 return(NULL);
88 }
89 input = STDIN_FILENO;
90 output = STDERR_FILENO;
91 }
92
93 /*
94 * Turn off echo if possible.
95 * If we are using a tty but are not the foreground pgrp this will
96 * generate SIGTTOU, so do it *before* installing the signal handlers.
97 */
98 if (input != STDIN_FILENO && tcgetattr(input, &oterm) == 0) {
99 memcpy(&term, &oterm, sizeof(term));
100 if (!(flags & RPP_ECHO_ON))
101 term.c_lflag &= ~(ECHO | ECHONL);
102#ifdef VSTATUS
103 if (term.c_cc[VSTATUS] != _POSIX_VDISABLE)
104 term.c_cc[VSTATUS] = _POSIX_VDISABLE;
105#endif
106 (void)tcsetattr(input, TCSAFLUSH|TCSASOFT, &term);
107 } else {
108 memset(&term, 0, sizeof(term));
109 term.c_lflag |= ECHO;
110 memset(&oterm, 0, sizeof(oterm));
111 oterm.c_lflag |= ECHO;
112 }
113
114 /*
115 * Catch signals that would otherwise cause the user to end
116 * up with echo turned off in the shell. Don't worry about
117 * things like SIGXCPU and SIGVTALRM for now.
118 */
119 sigemptyset(&sa.sa_mask);
120 sa.sa_flags = 0; /* don't restart system calls */
121 sa.sa_handler = handler;
122 (void)sigaction(SIGALRM, &sa, &savealrm);
123 (void)sigaction(SIGHUP, &sa, &savehup);
124 (void)sigaction(SIGINT, &sa, &saveint);
125 (void)sigaction(SIGPIPE, &sa, &savepipe);
126 (void)sigaction(SIGQUIT, &sa, &savequit);
127 (void)sigaction(SIGTERM, &sa, &saveterm);
128 (void)sigaction(SIGTSTP, &sa, &savetstp);
129 (void)sigaction(SIGTTIN, &sa, &savettin);
130 (void)sigaction(SIGTTOU, &sa, &savettou);
131
132 if (!(flags & RPP_STDIN))
133 (void)write(output, prompt, strlen(prompt));
134 end = buf + bufsiz - 1;
135 p = buf;
136 while ((nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') {
137 if (p < end) {
138 if ((flags & RPP_SEVENBIT))
139 ch &= 0x7f;
140 if (isalpha((unsigned char)ch)) {
141 if ((flags & RPP_FORCELOWER))
142 ch = (char)tolower((unsigned char)ch);
143 if ((flags & RPP_FORCEUPPER))
144 ch = (char)toupper((unsigned char)ch);
145 }
146 *p++ = ch;
147 }
148 }
149 *p = '\0';
150 save_errno = errno;
151 if (!(term.c_lflag & ECHO))
152 (void)write(output, "\n", 1);
153
154 /* Restore old terminal settings and signals. */
155 if (memcmp(&term, &oterm, sizeof(term)) != 0) {
156 const int sigttou = signo[SIGTTOU];
157
158 /* Ignore SIGTTOU generated when we are not the fg pgrp. */
159 while (tcsetattr(input, TCSAFLUSH|TCSASOFT, &oterm) == -1 &&
160 errno == EINTR && !signo[SIGTTOU])
161 continue;
162 signo[SIGTTOU] = sigttou;
163 }
164 (void)sigaction(SIGALRM, &savealrm, NULL);
165 (void)sigaction(SIGHUP, &savehup, NULL);
166 (void)sigaction(SIGINT, &saveint, NULL);
167 (void)sigaction(SIGQUIT, &savequit, NULL);
168 (void)sigaction(SIGPIPE, &savepipe, NULL);
169 (void)sigaction(SIGTERM, &saveterm, NULL);
170 (void)sigaction(SIGTSTP, &savetstp, NULL);
171 (void)sigaction(SIGTTIN, &savettin, NULL);
172 (void)sigaction(SIGTTOU, &savettou, NULL);
173 if (input != STDIN_FILENO)
174 (void)close(input);
175
176 /*
177 * If we were interrupted by a signal, resend it to ourselves
178 * now that we have restored the signal handlers.
179 */
180 for (i = 0; i < _NSIG; i++) {
181 if (signo[i]) {
182 kill(getpid(), i);
183 switch (i) {
184 case SIGTSTP:
185 case SIGTTIN:
186 case SIGTTOU:
187 need_restart = 1;
188 }
189 }
190 }
191 if (need_restart)
192 goto restart;
193
194 if (save_errno)
195 errno = save_errno;
196 return(nr == -1 ? NULL : buf);
197}
198
199#if 0
200char *
201getpass(const char *prompt)
202{
203 static char buf[_PASSWORD_LEN + 1];
204
205 return(readpassphrase(prompt, buf, sizeof(buf), RPP_ECHO_OFF));
206}
207#endif
208
209static void handler(int s)
210{
211
212 signo[s] = 1;
213}
214#endif /* HAVE_READPASSPHRASE */
diff --git a/openbsd-compat/readpassphrase.h b/openbsd-compat/readpassphrase.h
new file mode 100644
index 0000000..0c4a59e
--- /dev/null
+++ b/openbsd-compat/readpassphrase.h
@@ -0,0 +1,42 @@
1/* $OpenBSD: readpassphrase.h,v 1.5 2003/06/17 21:56:23 millert Exp $ */
2
3/*
4 * Copyright (c) 2000, 2002 Todd C. Miller <Todd.Miller@courtesan.com>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 * Sponsored in part by the Defense Advanced Research Projects
19 * Agency (DARPA) and Air Force Research Laboratory, Air Force
20 * Materiel Command, USAF, under agreement number F39502-99-1-0512.
21 */
22
23/* OPENBSD ORIGINAL: include/readpassphrase.h */
24
25#ifndef _READPASSPHRASE_H_
26#define _READPASSPHRASE_H_
27
28#ifndef HAVE_READPASSPHRASE
29
30#define RPP_ECHO_OFF 0x00 /* Turn off echo (default). */
31#define RPP_ECHO_ON 0x01 /* Leave echo on. */
32#define RPP_REQUIRE_TTY 0x02 /* Fail if there is no tty. */
33#define RPP_FORCELOWER 0x04 /* Force input to lower case. */
34#define RPP_FORCEUPPER 0x08 /* Force input to upper case. */
35#define RPP_SEVENBIT 0x10 /* Strip the high bit from input. */
36#define RPP_STDIN 0x20 /* Read from stdin, not /dev/tty */
37
38char * readpassphrase(const char *, char *, size_t, int);
39
40#endif /* HAVE_READPASSPHRASE */
41
42#endif /* !_READPASSPHRASE_H_ */
diff --git a/openbsd-compat/readpassphrase_win32.c b/openbsd-compat/readpassphrase_win32.c
new file mode 100644
index 0000000..f3079e4
--- /dev/null
+++ b/openbsd-compat/readpassphrase_win32.c
@@ -0,0 +1,131 @@
1/*
2* Author: Manoj Ampalam <manoj.ampalam@microsoft.com>
3*
4* Author: Bryan Berns <berns@uwalumni.com>
5* Modified group detection use s4u token information
6*
7* Copyright(c) 2016 Microsoft Corp.
8* All rights reserved
9*
10* Misc Unix POSIX routine implementations for Windows
11*
12* Redistribution and use in source and binary forms, with or without
13* modification, are permitted provided that the following conditions
14* are met :
15*
16* 1. Redistributions of source code must retain the above copyright
17* notice, this list of conditions and the following disclaimer.
18* 2. Redistributions in binary form must reproduce the above copyright
19* notice, this list of conditions and the following disclaimer in the
20* documentation and / or other materials provided with the distribution.
21*
22* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT
27* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32*/
33
34#define UMDF_USING_NTSTATUS
35#define SECURITY_WIN32
36#include <windows.h>
37#include <stdio.h>
38#include <time.h>
39#include <shlwapi.h>
40#include <conio.h>
41#include <lm.h>
42#include <sddl.h>
43#include <aclapi.h>
44#include <ntsecapi.h>
45#include <security.h>
46#include <ntstatus.h>
47#include <wchar.h>
48
49#include "openbsd-compat.h"
50
51#ifndef HAVE_READPASSPHRASE
52
53/*on error returns NULL and sets errno*/
54static wchar_t *
55utf8_to_utf16(const char *utf8)
56{
57 int needed = 0;
58 wchar_t* utf16 = NULL;
59 if ((needed = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0)) == 0 ||
60 (utf16 = malloc(needed * sizeof(wchar_t))) == NULL ||
61 MultiByteToWideChar(CP_UTF8, 0, utf8, -1, utf16, needed) == 0) {
62 /* debug3("failed to convert utf8 payload:%s error:%d", utf8, GetLastError()); */
63 errno = ENOMEM;
64 return NULL;
65 }
66
67 return utf16;
68}
69
70char *
71readpassphrase(const char *prompt, char *outBuf, size_t outBufLen, int flags)
72{
73 size_t current_index = 0;
74 char ch;
75 wchar_t* wtmp = NULL;
76
77 if (outBufLen == 0) {
78 errno = EINVAL;
79 return NULL;
80 }
81
82 while (_kbhit()) _getch();
83
84 wtmp = utf8_to_utf16(prompt);
85 if (wtmp == NULL)
86 errx(1, "unable to alloc memory");
87
88 _cputws(wtmp);
89 free(wtmp);
90
91 while (current_index < outBufLen - 1) {
92 ch = (char)_getch();
93
94 if (ch == '\r') {
95 if (_kbhit()) _getch(); /* read linefeed if its there */
96 break;
97 } else if (ch == '\n') {
98 break;
99 } else if (ch == '\b') { /* backspace */
100 if (current_index > 0) {
101 if (flags & RPP_ECHO_ON)
102 printf_s("%c \b", ch);
103
104 current_index--; /* overwrite last character */
105 }
106 } else if (ch == '\003') { /* exit on Ctrl+C */
107 errx(1, "");
108 } else {
109 if (flags & RPP_SEVENBIT)
110 ch &= 0x7f;
111
112 if (isalpha((unsigned char)ch)) {
113 if(flags & RPP_FORCELOWER)
114 ch = (char)tolower((unsigned char)ch);
115 if(flags & RPP_FORCEUPPER)
116 ch = (char)toupper((unsigned char)ch);
117 }
118
119 outBuf[current_index++] = ch;
120 if(flags & RPP_ECHO_ON)
121 printf_s("%c", ch);
122 }
123 }
124
125 outBuf[current_index] = '\0';
126 _cputs("\n");
127
128 return outBuf;
129}
130
131#endif /* HAVE_READPASSPHRASE */
diff --git a/openbsd-compat/recallocarray.c b/openbsd-compat/recallocarray.c
new file mode 100644
index 0000000..5d2f8d9
--- /dev/null
+++ b/openbsd-compat/recallocarray.c
@@ -0,0 +1,91 @@
1/* $OpenBSD: recallocarray.c,v 1.1 2017/03/06 18:44:21 otto Exp $ */
2/*
3 * Copyright (c) 2008, 2017 Otto Moerbeek <otto@drijf.net>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18/* OPENBSD ORIGINAL: lib/libc/stdlib/recallocarray.c */
19
20#include "openbsd-compat.h"
21
22#if !defined(HAVE_RECALLOCARRAY)
23
24#include <errno.h>
25#include <stdlib.h>
26#include <stdint.h>
27#include <string.h>
28#ifdef HAVE_UNISTD_H
29#include <unistd.h>
30#endif
31
32/*
33 * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
34 * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
35 */
36#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
37
38void *
39recallocarray(void *ptr, size_t oldnmemb, size_t newnmemb, size_t size)
40{
41 size_t oldsize, newsize;
42 void *newptr;
43
44 if (ptr == NULL)
45 return calloc(newnmemb, size);
46
47 if ((newnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
48 newnmemb > 0 && SIZE_MAX / newnmemb < size) {
49 errno = ENOMEM;
50 return NULL;
51 }
52 newsize = newnmemb * size;
53
54 if ((oldnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
55 oldnmemb > 0 && SIZE_MAX / oldnmemb < size) {
56 errno = EINVAL;
57 return NULL;
58 }
59 oldsize = oldnmemb * size;
60
61 /*
62 * Don't bother too much if we're shrinking just a bit,
63 * we do not shrink for series of small steps, oh well.
64 */
65 if (newsize <= oldsize) {
66 size_t d = oldsize - newsize;
67
68 if (d < oldsize / 2 && d < (size_t)getpagesize()) {
69 memset((char *)ptr + newsize, 0, d);
70 return ptr;
71 }
72 }
73
74 newptr = malloc(newsize);
75 if (newptr == NULL)
76 return NULL;
77
78 if (newsize > oldsize) {
79 memcpy(newptr, ptr, oldsize);
80 memset((char *)newptr + oldsize, 0, newsize - oldsize);
81 } else
82 memcpy(newptr, ptr, newsize);
83
84 explicit_bzero(ptr, oldsize);
85 free(ptr);
86
87 return newptr;
88}
89/* DEF_WEAK(recallocarray); */
90
91#endif /* !defined(HAVE_RECALLOCARRAY) */
diff --git a/openbsd-compat/strlcat.c b/openbsd-compat/strlcat.c
new file mode 100644
index 0000000..44470de
--- /dev/null
+++ b/openbsd-compat/strlcat.c
@@ -0,0 +1,63 @@
1/* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */
2
3/*
4 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19/* OPENBSD ORIGINAL: lib/libc/string/strlcat.c */
20
21#include "openbsd-compat.h"
22
23#if !defined(HAVE_STRLCAT)
24
25#include <sys/types.h>
26#include <string.h>
27
28/*
29 * Appends src to string dst of size siz (unlike strncat, siz is the
30 * full size of dst, not space left). At most siz-1 characters
31 * will be copied. Always NUL terminates (unless siz <= strlen(dst)).
32 * Returns strlen(src) + MIN(siz, strlen(initial dst)).
33 * If retval >= siz, truncation occurred.
34 */
35size_t
36strlcat(char *dst, const char *src, size_t siz)
37{
38 char *d = dst;
39 const char *s = src;
40 size_t n = siz;
41 size_t dlen;
42
43 /* Find the end of dst and adjust bytes left but don't go past end */
44 while (n-- != 0 && *d != '\0')
45 d++;
46 dlen = d - dst;
47 n = siz - dlen;
48
49 if (n == 0)
50 return(dlen + strlen(s));
51 while (*s != '\0') {
52 if (n != 1) {
53 *d++ = *s;
54 n--;
55 }
56 s++;
57 }
58 *d = '\0';
59
60 return(dlen + (s - src)); /* count does not include NUL */
61}
62
63#endif /* !defined(HAVE_STRLCAT) */
diff --git a/openbsd-compat/strlcpy.c b/openbsd-compat/strlcpy.c
new file mode 100644
index 0000000..a8b18ea
--- /dev/null
+++ b/openbsd-compat/strlcpy.c
@@ -0,0 +1,59 @@
1/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */
2
3/*
4 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19/* OPENBSD ORIGINAL: lib/libc/string/strlcpy.c */
20
21#include "openbsd-compat.h"
22
23#if !defined(HAVE_STRLCPY)
24
25#include <sys/types.h>
26#include <string.h>
27
28/*
29 * Copy src to string dst of size siz. At most siz-1 characters
30 * will be copied. Always NUL terminates (unless siz == 0).
31 * Returns strlen(src); if retval >= siz, truncation occurred.
32 */
33size_t
34strlcpy(char *dst, const char *src, size_t siz)
35{
36 char *d = dst;
37 const char *s = src;
38 size_t n = siz;
39
40 /* Copy as many bytes as will fit */
41 if (n != 0) {
42 while (--n != 0) {
43 if ((*d++ = *s++) == '\0')
44 break;
45 }
46 }
47
48 /* Not enough room in dst, add NUL and traverse rest of src */
49 if (n == 0) {
50 if (siz != 0)
51 *d = '\0'; /* NUL-terminate dst */
52 while (*s++)
53 ;
54 }
55
56 return(s - src - 1); /* count does not include NUL */
57}
58
59#endif /* !defined(HAVE_STRLCPY) */
diff --git a/openbsd-compat/timingsafe_bcmp.c b/openbsd-compat/timingsafe_bcmp.c
new file mode 100644
index 0000000..3f7b9e5
--- /dev/null
+++ b/openbsd-compat/timingsafe_bcmp.c
@@ -0,0 +1,35 @@
1/* $OpenBSD: timingsafe_bcmp.c,v 1.1 2010/09/24 13:33:00 matthew Exp $ */
2/*
3 * Copyright (c) 2010 Damien Miller. All rights reserved.
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18/* OPENBSD ORIGINAL: lib/libc/string/timingsafe_bcmp.c */
19
20#include "openbsd-compat.h"
21
22#if !defined(HAVE_TIMINGSAFE_BCMP)
23
24int
25timingsafe_bcmp(const void *b1, const void *b2, size_t n)
26{
27 const unsigned char *p1 = b1, *p2 = b2;
28 int ret = 0;
29
30 for (; n > 0; n--)
31 ret |= *p1++ ^ *p2++;
32 return (ret != 0);
33}
34
35#endif /* !defined(HAVE_TIMINGSAFE_BCMP) */
diff --git a/openbsd-compat/types.h b/openbsd-compat/types.h
new file mode 100644
index 0000000..cc1da66
--- /dev/null
+++ b/openbsd-compat/types.h
@@ -0,0 +1,71 @@
1/*
2 * Public domain
3 * sys/types.h compatibility shim
4 */
5
6#ifdef _MSC_VER
7#if _MSC_VER >= 1900
8#include <../ucrt/sys/types.h>
9#else
10#include <../include/sys/types.h>
11#endif
12#endif
13
14#ifndef _COMPAT_TYPES_H
15#define _COMPAT_TYPES_H
16
17#include <stdint.h>
18
19#ifdef __MINGW32__
20#include <_bsd_types.h>
21typedef uint32_t in_addr_t;
22typedef uint32_t uid_t;
23#endif
24
25#ifdef _MSC_VER
26typedef unsigned char u_char;
27typedef unsigned short u_short;
28typedef unsigned int u_int;
29typedef uint32_t in_addr_t;
30typedef uint32_t mode_t;
31typedef uint32_t uid_t;
32
33#include <basetsd.h>
34typedef SSIZE_T ssize_t;
35
36#ifndef SSIZE_MAX
37#ifdef _WIN64
38#define SSIZE_MAX _I64_MAX
39#else
40#define SSIZE_MAX INT_MAX
41#endif
42#endif
43
44#endif
45
46#if !defined(HAVE_ATTRIBUTE__BOUNDED__) && !defined(__bounded__)
47# define __bounded__(x, y, z)
48#endif
49
50#ifdef _WIN32
51#define __warn_references(sym,msg)
52#else
53
54#ifndef __warn_references
55
56#ifndef __STRING
57#define __STRING(x) #x
58#endif
59
60#if defined(__GNUC__) && defined (HAS_GNU_WARNING_LONG)
61#define __warn_references(sym,msg) \
62 __asm__(".section .gnu.warning." __STRING(sym) \
63 "\n\t.ascii \"" msg "\"\n\t.text");
64#else
65#define __warn_references(sym,msg)
66#endif
67
68#endif /* __warn_references */
69#endif /* _WIN32 */
70
71#endif /* !_COMPAT_TYPES_H */
diff --git a/regress/CMakeLists.txt b/regress/CMakeLists.txt
new file mode 100644
index 0000000..b8fea64
--- /dev/null
+++ b/regress/CMakeLists.txt
@@ -0,0 +1,18 @@
1# Copyright (c) 2018 Yubico AB. All rights reserved.
2# Use of this source code is governed by a BSD-style
3# license that can be found in the LICENSE file.
4
5# cred
6add_executable(regress_cred cred.c)
7target_link_libraries(regress_cred fido2_shared)
8add_custom_command(TARGET regress_cred POST_BUILD COMMAND regress_cred)
9
10# assert
11add_executable(regress_assert assert.c)
12target_link_libraries(regress_assert fido2_shared)
13add_custom_command(TARGET regress_assert POST_BUILD COMMAND regress_assert)
14
15# dev
16add_executable(regress_dev dev.c)
17target_link_libraries(regress_dev fido2_shared)
18add_custom_command(TARGET regress_dev POST_BUILD COMMAND regress_dev)
diff --git a/regress/assert.c b/regress/assert.c
new file mode 100644
index 0000000..ebf0652
--- /dev/null
+++ b/regress/assert.c
@@ -0,0 +1,511 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <assert.h>
8#include <fido.h>
9#include <fido/es256.h>
10#include <fido/rs256.h>
11#include <string.h>
12
13#define FAKE_DEV_HANDLE ((void *)0xdeadbeef)
14
15static const unsigned char es256_pk[64] = {
16 0x34, 0xeb, 0x99, 0x77, 0x02, 0x9c, 0x36, 0x38,
17 0xbb, 0xc2, 0xae, 0xa0, 0xa0, 0x18, 0xc6, 0x64,
18 0xfc, 0xe8, 0x49, 0x92, 0xd7, 0x74, 0x9e, 0x0c,
19 0x46, 0x8c, 0x9d, 0xa6, 0xdf, 0x46, 0xf7, 0x84,
20 0x60, 0x1e, 0x0f, 0x8b, 0x23, 0x85, 0x4a, 0x9a,
21 0xec, 0xc1, 0x08, 0x9f, 0x30, 0xd0, 0x0d, 0xd7,
22 0x76, 0x7b, 0x55, 0x48, 0x91, 0x7c, 0x4f, 0x0f,
23 0x64, 0x1a, 0x1d, 0xf8, 0xbe, 0x14, 0x90, 0x8a,
24};
25
26static const unsigned char cdh[32] = {
27 0xec, 0x8d, 0x8f, 0x78, 0x42, 0x4a, 0x2b, 0xb7,
28 0x82, 0x34, 0xaa, 0xca, 0x07, 0xa1, 0xf6, 0x56,
29 0x42, 0x1c, 0xb6, 0xf6, 0xb3, 0x00, 0x86, 0x52,
30 0x35, 0x2d, 0xa2, 0x62, 0x4a, 0xbe, 0x89, 0x76,
31};
32
33static const unsigned char authdata[39] = {
34 0x58, 0x25, 0x49, 0x96, 0x0d, 0xe5, 0x88, 0x0e,
35 0x8c, 0x68, 0x74, 0x34, 0x17, 0x0f, 0x64, 0x76,
36 0x60, 0x5b, 0x8f, 0xe4, 0xae, 0xb9, 0xa2, 0x86,
37 0x32, 0xc7, 0x99, 0x5c, 0xf3, 0xba, 0x83, 0x1d,
38 0x97, 0x63, 0x00, 0x00, 0x00, 0x00, 0x03,
39};
40
41static const unsigned char sig[72] = {
42 0x30, 0x46, 0x02, 0x21, 0x00, 0xf6, 0xd1, 0xa3,
43 0xd5, 0x24, 0x2b, 0xde, 0xee, 0xa0, 0x90, 0x89,
44 0xcd, 0xf8, 0x9e, 0xbd, 0x6b, 0x4d, 0x55, 0x79,
45 0xe4, 0xc1, 0x42, 0x27, 0xb7, 0x9b, 0x9b, 0xa4,
46 0x0a, 0xe2, 0x47, 0x64, 0x0e, 0x02, 0x21, 0x00,
47 0xe5, 0xc9, 0xc2, 0x83, 0x47, 0x31, 0xc7, 0x26,
48 0xe5, 0x25, 0xb2, 0xb4, 0x39, 0xa7, 0xfc, 0x3d,
49 0x70, 0xbe, 0xe9, 0x81, 0x0d, 0x4a, 0x62, 0xa9,
50 0xab, 0x4a, 0x91, 0xc0, 0x7d, 0x2d, 0x23, 0x1e,
51};
52
53static void *
54dummy_open(const char *path)
55{
56 (void)path;
57
58 return (FAKE_DEV_HANDLE);
59}
60
61static void
62dummy_close(void *handle)
63{
64 assert(handle == FAKE_DEV_HANDLE);
65}
66
67static int
68dummy_read(void *handle, unsigned char *buf, size_t len, int ms)
69{
70 (void)handle;
71 (void)buf;
72 (void)len;
73 (void)ms;
74
75 abort();
76 /* NOTREACHED */
77}
78
79static int
80dummy_write(void *handle, const unsigned char *buf, size_t len)
81{
82 (void)handle;
83 (void)buf;
84 (void)len;
85
86 abort();
87 /* NOTREACHED */
88}
89
90static fido_assert_t *
91alloc_assert(void)
92{
93 fido_assert_t *a;
94
95 a = fido_assert_new();
96 assert(a != NULL);
97
98 return (a);
99}
100
101static void
102free_assert(fido_assert_t *a)
103{
104 fido_assert_free(&a);
105 assert(a == NULL);
106}
107
108static fido_dev_t *
109alloc_dev(void)
110{
111 fido_dev_t *d;
112
113 d = fido_dev_new();
114 assert(d != NULL);
115
116 return (d);
117}
118
119static void
120free_dev(fido_dev_t *d)
121{
122 fido_dev_free(&d);
123 assert(d == NULL);
124}
125
126static es256_pk_t *
127alloc_es256_pk(void)
128{
129 es256_pk_t *pk;
130
131 pk = es256_pk_new();
132 assert(pk != NULL);
133
134 return (pk);
135}
136
137static void
138free_es256_pk(es256_pk_t *pk)
139{
140 es256_pk_free(&pk);
141 assert(pk == NULL);
142}
143
144static rs256_pk_t *
145alloc_rs256_pk(void)
146{
147 rs256_pk_t *pk;
148
149 pk = rs256_pk_new();
150 assert(pk != NULL);
151
152 return (pk);
153}
154
155static void
156free_rs256_pk(rs256_pk_t *pk)
157{
158 rs256_pk_free(&pk);
159 assert(pk == NULL);
160}
161
162static void
163empty_assert(fido_dev_t *d, fido_assert_t *a, int idx)
164{
165 es256_pk_t *es256;
166 rs256_pk_t *rs256;
167
168 assert(fido_assert_flags(a, idx) == 0);
169 assert(fido_assert_authdata_len(a, idx) == 0);
170 assert(fido_assert_authdata_ptr(a, idx) == NULL);
171 assert(fido_assert_clientdata_hash_len(a) == 0);
172 assert(fido_assert_clientdata_hash_ptr(a) == NULL);
173 assert(fido_assert_id_len(a, idx) == 0);
174 assert(fido_assert_id_ptr(a, idx) == NULL);
175 assert(fido_assert_rp_id(a) == NULL);
176 assert(fido_assert_sig_len(a, idx) == 0);
177 assert(fido_assert_sig_ptr(a, idx) == NULL);
178 assert(fido_assert_user_display_name(a, idx) == NULL);
179 assert(fido_assert_user_icon(a, idx) == NULL);
180 assert(fido_assert_user_id_len(a, idx) == 0);
181 assert(fido_assert_user_id_ptr(a, idx) == NULL);
182 assert(fido_assert_user_name(a, idx) == NULL);
183
184 es256 = alloc_es256_pk();
185 rs256 = alloc_rs256_pk();
186
187 fido_dev_force_u2f(d);
188 assert(fido_dev_get_assert(d, a, NULL) == FIDO_ERR_INVALID_ARGUMENT);
189 assert(fido_dev_get_assert(d, a, "") == FIDO_ERR_INVALID_ARGUMENT);
190 assert(fido_assert_verify(a, COSE_ES256, idx,
191 NULL) == FIDO_ERR_INVALID_ARGUMENT);
192 assert(fido_assert_verify(a, COSE_ES256, idx,
193 es256) == FIDO_ERR_INVALID_ARGUMENT);
194 assert(fido_assert_verify(a, COSE_RS256, idx,
195 rs256) == FIDO_ERR_INVALID_ARGUMENT);
196
197 fido_dev_force_fido2(d);
198 assert(fido_dev_get_assert(d, a, NULL) == FIDO_ERR_INVALID_ARGUMENT);
199 assert(fido_dev_get_assert(d, a, "") == FIDO_ERR_INVALID_ARGUMENT);
200 assert(fido_assert_verify(a, COSE_ES256, idx,
201 NULL) == FIDO_ERR_INVALID_ARGUMENT);
202 assert(fido_assert_verify(a, COSE_ES256, idx,
203 es256) == FIDO_ERR_INVALID_ARGUMENT);
204 assert(fido_assert_verify(a, COSE_RS256, idx,
205 rs256) == FIDO_ERR_INVALID_ARGUMENT);
206
207 free_es256_pk(es256);
208 free_rs256_pk(rs256);
209}
210
211static void
212empty_assert_tests(void)
213{
214 fido_assert_t *a;
215 fido_dev_t *d;
216 fido_dev_io_t io_f;
217 int i;
218
219 a = alloc_assert();
220 d = alloc_dev();
221 io_f.open = dummy_open;
222 io_f.close = dummy_close;
223 io_f.read = dummy_read;
224 io_f.write = dummy_write;
225 assert(fido_dev_set_io_functions(d, &io_f) == FIDO_OK);
226
227 empty_assert(d, a, 0);
228 assert(fido_assert_count(a) == 0);
229 assert(fido_assert_set_count(a, 4) == FIDO_OK);
230 assert(fido_assert_count(a) == 4);
231 for (i = 0; i < 4; i++) {
232 empty_assert(d, a, i);
233 }
234 empty_assert(d, a, 10);
235 free_assert(a);
236 free_dev(d);
237}
238
239static void
240valid_assert(void)
241{
242 fido_assert_t *a;
243 es256_pk_t *pk;
244
245 a = alloc_assert();
246 pk = alloc_es256_pk();
247 assert(es256_pk_from_ptr(pk, es256_pk, sizeof(es256_pk)) == FIDO_OK);
248 assert(fido_assert_set_clientdata_hash(a, cdh, sizeof(cdh)) == FIDO_OK);
249 assert(fido_assert_set_rp(a, "localhost") == FIDO_OK);
250 assert(fido_assert_set_count(a, 1) == FIDO_OK);
251 assert(fido_assert_set_authdata(a, 0, authdata,
252 sizeof(authdata)) == FIDO_OK);
253 assert(fido_assert_set_up(a, FIDO_OPT_FALSE) == FIDO_OK);
254 assert(fido_assert_set_uv(a, FIDO_OPT_FALSE) == FIDO_OK);
255 assert(fido_assert_set_sig(a, 0, sig, sizeof(sig)) == FIDO_OK);
256 assert(fido_assert_verify(a, 0, COSE_ES256, pk) == FIDO_OK);
257 free_assert(a);
258 free_es256_pk(pk);
259}
260
261static void
262no_cdh(void)
263{
264 fido_assert_t *a;
265 es256_pk_t *pk;
266
267 a = alloc_assert();
268 pk = alloc_es256_pk();
269 assert(es256_pk_from_ptr(pk, es256_pk, sizeof(es256_pk)) == FIDO_OK);
270 assert(fido_assert_set_rp(a, "localhost") == FIDO_OK);
271 assert(fido_assert_set_count(a, 1) == FIDO_OK);
272 assert(fido_assert_set_authdata(a, 0, authdata,
273 sizeof(authdata)) == FIDO_OK);
274 assert(fido_assert_set_up(a, FIDO_OPT_FALSE) == FIDO_OK);
275 assert(fido_assert_set_uv(a, FIDO_OPT_FALSE) == FIDO_OK);
276 assert(fido_assert_set_sig(a, 0, sig, sizeof(sig)) == FIDO_OK);
277 assert(fido_assert_verify(a, 0, COSE_ES256,
278 pk) == FIDO_ERR_INVALID_ARGUMENT);
279 free_assert(a);
280 free_es256_pk(pk);
281}
282
283static void
284no_rp(void)
285{
286 fido_assert_t *a;
287 es256_pk_t *pk;
288
289 a = alloc_assert();
290 pk = alloc_es256_pk();
291 assert(es256_pk_from_ptr(pk, es256_pk, sizeof(es256_pk)) == FIDO_OK);
292 assert(fido_assert_set_clientdata_hash(a, cdh, sizeof(cdh)) == FIDO_OK);
293 assert(fido_assert_set_count(a, 1) == FIDO_OK);
294 assert(fido_assert_set_authdata(a, 0, authdata,
295 sizeof(authdata)) == FIDO_OK);
296 assert(fido_assert_set_up(a, FIDO_OPT_FALSE) == FIDO_OK);
297 assert(fido_assert_set_uv(a, FIDO_OPT_FALSE) == FIDO_OK);
298 assert(fido_assert_set_sig(a, 0, sig, sizeof(sig)) == FIDO_OK);
299 assert(fido_assert_verify(a, 0, COSE_ES256,
300 pk) == FIDO_ERR_INVALID_ARGUMENT);
301 free_assert(a);
302 free_es256_pk(pk);
303}
304
305static void
306no_authdata(void)
307{
308 fido_assert_t *a;
309 es256_pk_t *pk;
310
311 a = alloc_assert();
312 pk = alloc_es256_pk();
313 assert(es256_pk_from_ptr(pk, es256_pk, sizeof(es256_pk)) == FIDO_OK);
314 assert(fido_assert_set_clientdata_hash(a, cdh, sizeof(cdh)) == FIDO_OK);
315 assert(fido_assert_set_rp(a, "localhost") == FIDO_OK);
316 assert(fido_assert_set_count(a, 1) == FIDO_OK);
317 assert(fido_assert_set_up(a, FIDO_OPT_FALSE) == FIDO_OK);
318 assert(fido_assert_set_uv(a, FIDO_OPT_FALSE) == FIDO_OK);
319 assert(fido_assert_set_sig(a, 0, sig, sizeof(sig)) == FIDO_OK);
320 assert(fido_assert_verify(a, 0, COSE_ES256,
321 pk) == FIDO_ERR_INVALID_ARGUMENT);
322 free_assert(a);
323 free_es256_pk(pk);
324}
325
326static void
327no_sig(void)
328{
329 fido_assert_t *a;
330 es256_pk_t *pk;
331
332 a = alloc_assert();
333 pk = alloc_es256_pk();
334 assert(es256_pk_from_ptr(pk, es256_pk, sizeof(es256_pk)) == FIDO_OK);
335 assert(fido_assert_set_clientdata_hash(a, cdh, sizeof(cdh)) == FIDO_OK);
336 assert(fido_assert_set_rp(a, "localhost") == FIDO_OK);
337 assert(fido_assert_set_count(a, 1) == FIDO_OK);
338 assert(fido_assert_set_authdata(a, 0, authdata,
339 sizeof(authdata)) == FIDO_OK);
340 assert(fido_assert_set_up(a, FIDO_OPT_FALSE) == FIDO_OK);
341 assert(fido_assert_set_uv(a, FIDO_OPT_FALSE) == FIDO_OK);
342 assert(fido_assert_verify(a, 0, COSE_ES256,
343 pk) == FIDO_ERR_INVALID_ARGUMENT);
344 free_assert(a);
345 free_es256_pk(pk);
346}
347
348static void
349junk_cdh(void)
350{
351 fido_assert_t *a;
352 es256_pk_t *pk;
353 unsigned char *junk;
354
355 junk = malloc(sizeof(cdh));
356 assert(junk != NULL);
357 memcpy(junk, cdh, sizeof(cdh));
358 junk[0] = ~junk[0];
359
360 a = alloc_assert();
361 pk = alloc_es256_pk();
362 assert(es256_pk_from_ptr(pk, es256_pk, sizeof(es256_pk)) == FIDO_OK);
363 assert(fido_assert_set_clientdata_hash(a, junk, sizeof(cdh)) == FIDO_OK);
364 assert(fido_assert_set_rp(a, "localhost") == FIDO_OK);
365 assert(fido_assert_set_count(a, 1) == FIDO_OK);
366 assert(fido_assert_set_authdata(a, 0, authdata,
367 sizeof(authdata)) == FIDO_OK);
368 assert(fido_assert_set_up(a, FIDO_OPT_FALSE) == FIDO_OK);
369 assert(fido_assert_set_uv(a, FIDO_OPT_FALSE) == FIDO_OK);
370 assert(fido_assert_set_sig(a, 0, sig, sizeof(sig)) == FIDO_OK);
371 assert(fido_assert_verify(a, 0, COSE_ES256, pk) == FIDO_ERR_INVALID_SIG);
372 free_assert(a);
373 free_es256_pk(pk);
374 free(junk);
375}
376
377static void
378junk_rp(void)
379{
380 fido_assert_t *a;
381 es256_pk_t *pk;
382
383 a = alloc_assert();
384 pk = alloc_es256_pk();
385 assert(es256_pk_from_ptr(pk, es256_pk, sizeof(es256_pk)) == FIDO_OK);
386 assert(fido_assert_set_clientdata_hash(a, cdh, sizeof(cdh)) == FIDO_OK);
387 assert(fido_assert_set_rp(a, "potato") == FIDO_OK);
388 assert(fido_assert_set_count(a, 1) == FIDO_OK);
389 assert(fido_assert_set_authdata(a, 0, authdata,
390 sizeof(authdata)) == FIDO_OK);
391 assert(fido_assert_set_up(a, FIDO_OPT_FALSE) == FIDO_OK);
392 assert(fido_assert_set_uv(a, FIDO_OPT_FALSE) == FIDO_OK);
393 assert(fido_assert_set_sig(a, 0, sig, sizeof(sig)) == FIDO_OK);
394 assert(fido_assert_verify(a, 0, COSE_ES256,
395 pk) == FIDO_ERR_INVALID_PARAM);
396 free_assert(a);
397 free_es256_pk(pk);
398}
399
400static void
401junk_authdata(void)
402{
403 fido_assert_t *a;
404 unsigned char *junk;
405
406 junk = malloc(sizeof(authdata));
407 assert(junk != NULL);
408 memcpy(junk, authdata, sizeof(authdata));
409 junk[0] = ~junk[0];
410
411 a = alloc_assert();
412 assert(fido_assert_set_count(a, 1) == FIDO_OK);
413 assert(fido_assert_set_authdata(a, 0, junk,
414 sizeof(authdata)) == FIDO_ERR_INVALID_ARGUMENT);
415 free_assert(a);
416 free(junk);
417}
418
419static void
420junk_sig(void)
421{
422 fido_assert_t *a;
423 es256_pk_t *pk;
424 unsigned char *junk;
425
426 junk = malloc(sizeof(sig));
427 assert(junk != NULL);
428 memcpy(junk, sig, sizeof(sig));
429 junk[0] = ~junk[0];
430
431 a = alloc_assert();
432 pk = alloc_es256_pk();
433 assert(es256_pk_from_ptr(pk, es256_pk, sizeof(es256_pk)) == FIDO_OK);
434 assert(fido_assert_set_clientdata_hash(a, cdh, sizeof(cdh)) == FIDO_OK);
435 assert(fido_assert_set_rp(a, "localhost") == FIDO_OK);
436 assert(fido_assert_set_count(a, 1) == FIDO_OK);
437 assert(fido_assert_set_authdata(a, 0, authdata,
438 sizeof(authdata)) == FIDO_OK);
439 assert(fido_assert_set_up(a, FIDO_OPT_FALSE) == FIDO_OK);
440 assert(fido_assert_set_uv(a, FIDO_OPT_FALSE) == FIDO_OK);
441 assert(fido_assert_set_sig(a, 0, junk, sizeof(sig)) == FIDO_OK);
442 assert(fido_assert_verify(a, 0, COSE_ES256, pk) == FIDO_ERR_INVALID_SIG);
443 free_assert(a);
444 free_es256_pk(pk);
445 free(junk);
446}
447
448static void
449wrong_options(void)
450{
451 fido_assert_t *a;
452 es256_pk_t *pk;
453
454 a = alloc_assert();
455 pk = alloc_es256_pk();
456 assert(es256_pk_from_ptr(pk, es256_pk, sizeof(es256_pk)) == FIDO_OK);
457 assert(fido_assert_set_clientdata_hash(a, cdh, sizeof(cdh)) == FIDO_OK);
458 assert(fido_assert_set_rp(a, "localhost") == FIDO_OK);
459 assert(fido_assert_set_count(a, 1) == FIDO_OK);
460 assert(fido_assert_set_authdata(a, 0, authdata,
461 sizeof(authdata)) == FIDO_OK);
462 assert(fido_assert_set_up(a, FIDO_OPT_TRUE) == FIDO_OK);
463 assert(fido_assert_set_uv(a, FIDO_OPT_FALSE) == FIDO_OK);
464 assert(fido_assert_set_sig(a, 0, sig, sizeof(sig)) == FIDO_OK);
465 assert(fido_assert_verify(a, 0, COSE_ES256,
466 pk) == FIDO_ERR_INVALID_PARAM);
467 assert(fido_assert_set_up(a, FIDO_OPT_FALSE) == FIDO_OK);
468 assert(fido_assert_set_uv(a, FIDO_OPT_TRUE) == FIDO_OK);
469 assert(fido_assert_verify(a, 0, COSE_ES256,
470 pk) == FIDO_ERR_INVALID_PARAM);
471 assert(fido_assert_set_up(a, FIDO_OPT_FALSE) == FIDO_OK);
472 assert(fido_assert_set_uv(a, FIDO_OPT_FALSE) == FIDO_OK);
473 assert(fido_assert_verify(a, 0, COSE_ES256, pk) == FIDO_OK);
474 free_assert(a);
475 free_es256_pk(pk);
476}
477
478/* cbor_serialize_alloc misuse */
479static void
480bad_cbor_serialize(void)
481{
482 fido_assert_t *a;
483
484 a = alloc_assert();
485 assert(fido_assert_set_count(a, 1) == FIDO_OK);
486 assert(fido_assert_set_authdata(a, 0, authdata,
487 sizeof(authdata)) == FIDO_OK);
488 assert(fido_assert_authdata_len(a, 0) == sizeof(authdata));
489 free_assert(a);
490}
491
492int
493main(void)
494{
495 fido_init(0);
496
497 empty_assert_tests();
498 valid_assert();
499 no_cdh();
500 no_rp();
501 no_authdata();
502 no_sig();
503 junk_cdh();
504 junk_rp();
505 junk_authdata();
506 junk_sig();
507 wrong_options();
508 bad_cbor_serialize();
509
510 exit(0);
511}
diff --git a/regress/cred.c b/regress/cred.c
new file mode 100644
index 0000000..4998649
--- /dev/null
+++ b/regress/cred.c
@@ -0,0 +1,816 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <assert.h>
8#include <fido.h>
9#include <string.h>
10
11#define FAKE_DEV_HANDLE ((void *)0xdeadbeef)
12
13static const unsigned char cdh[32] = {
14 0xf9, 0x64, 0x57, 0xe7, 0x2d, 0x97, 0xf6, 0xbb,
15 0xdd, 0xd7, 0xfb, 0x06, 0x37, 0x62, 0xea, 0x26,
16 0x20, 0x44, 0x8e, 0x69, 0x7c, 0x03, 0xf2, 0x31,
17 0x2f, 0x99, 0xdc, 0xaf, 0x3e, 0x8a, 0x91, 0x6b,
18};
19
20static const unsigned char authdata[198] = {
21 0x58, 0xc4, 0x49, 0x96, 0x0d, 0xe5, 0x88, 0x0e,
22 0x8c, 0x68, 0x74, 0x34, 0x17, 0x0f, 0x64, 0x76,
23 0x60, 0x5b, 0x8f, 0xe4, 0xae, 0xb9, 0xa2, 0x86,
24 0x32, 0xc7, 0x99, 0x5c, 0xf3, 0xba, 0x83, 0x1d,
25 0x97, 0x63, 0x41, 0x00, 0x00, 0x00, 0x00, 0xf8,
26 0xa0, 0x11, 0xf3, 0x8c, 0x0a, 0x4d, 0x15, 0x80,
27 0x06, 0x17, 0x11, 0x1f, 0x9e, 0xdc, 0x7d, 0x00,
28 0x40, 0x53, 0xfb, 0xdf, 0xaa, 0xce, 0x63, 0xde,
29 0xc5, 0xfe, 0x47, 0xe6, 0x52, 0xeb, 0xf3, 0x5d,
30 0x53, 0xa8, 0xbf, 0x9d, 0xd6, 0x09, 0x6b, 0x5e,
31 0x7f, 0xe0, 0x0d, 0x51, 0x30, 0x85, 0x6a, 0xda,
32 0x68, 0x70, 0x85, 0xb0, 0xdb, 0x08, 0x0b, 0x83,
33 0x2c, 0xef, 0x44, 0xe2, 0x36, 0x88, 0xee, 0x76,
34 0x90, 0x6e, 0x7b, 0x50, 0x3e, 0x9a, 0xa0, 0xd6,
35 0x3c, 0x34, 0xe3, 0x83, 0xe7, 0xd1, 0xbd, 0x9f,
36 0x25, 0xa5, 0x01, 0x02, 0x03, 0x26, 0x20, 0x01,
37 0x21, 0x58, 0x20, 0x17, 0x5b, 0x27, 0xa6, 0x56,
38 0xb2, 0x26, 0x0c, 0x26, 0x0c, 0x55, 0x42, 0x78,
39 0x17, 0x5d, 0x4c, 0xf8, 0xa2, 0xfd, 0x1b, 0xb9,
40 0x54, 0xdf, 0xd5, 0xeb, 0xbf, 0x22, 0x64, 0xf5,
41 0x21, 0x9a, 0xc6, 0x22, 0x58, 0x20, 0x87, 0x5f,
42 0x90, 0xe6, 0xfd, 0x71, 0x27, 0x9f, 0xeb, 0xe3,
43 0x03, 0x44, 0xbc, 0x8d, 0x49, 0xc6, 0x1c, 0x31,
44 0x3b, 0x72, 0xae, 0xd4, 0x53, 0xb1, 0xfe, 0x5d,
45 0xe1, 0x30, 0xfc, 0x2b, 0x1e, 0xd2,
46};
47
48static const unsigned char authdata_dupkeys[200] = {
49 0x58, 0xc6, 0x49, 0x96, 0x0d, 0xe5, 0x88, 0x0e,
50 0x8c, 0x68, 0x74, 0x34, 0x17, 0x0f, 0x64, 0x76,
51 0x60, 0x5b, 0x8f, 0xe4, 0xae, 0xb9, 0xa2, 0x86,
52 0x32, 0xc7, 0x99, 0x5c, 0xf3, 0xba, 0x83, 0x1d,
53 0x97, 0x63, 0x41, 0x00, 0x00, 0x00, 0x00, 0xf8,
54 0xa0, 0x11, 0xf3, 0x8c, 0x0a, 0x4d, 0x15, 0x80,
55 0x06, 0x17, 0x11, 0x1f, 0x9e, 0xdc, 0x7d, 0x00,
56 0x40, 0x53, 0xfb, 0xdf, 0xaa, 0xce, 0x63, 0xde,
57 0xc5, 0xfe, 0x47, 0xe6, 0x52, 0xeb, 0xf3, 0x5d,
58 0x53, 0xa8, 0xbf, 0x9d, 0xd6, 0x09, 0x6b, 0x5e,
59 0x7f, 0xe0, 0x0d, 0x51, 0x30, 0x85, 0x6a, 0xda,
60 0x68, 0x70, 0x85, 0xb0, 0xdb, 0x08, 0x0b, 0x83,
61 0x2c, 0xef, 0x44, 0xe2, 0x36, 0x88, 0xee, 0x76,
62 0x90, 0x6e, 0x7b, 0x50, 0x3e, 0x9a, 0xa0, 0xd6,
63 0x3c, 0x34, 0xe3, 0x83, 0xe7, 0xd1, 0xbd, 0x9f,
64 0x25, 0xa6, 0x01, 0x02, 0x01, 0x02, 0x03, 0x26,
65 0x20, 0x01, 0x21, 0x58, 0x20, 0x17, 0x5b, 0x27,
66 0xa6, 0x56, 0xb2, 0x26, 0x0c, 0x26, 0x0c, 0x55,
67 0x42, 0x78, 0x17, 0x5d, 0x4c, 0xf8, 0xa2, 0xfd,
68 0x1b, 0xb9, 0x54, 0xdf, 0xd5, 0xeb, 0xbf, 0x22,
69 0x64, 0xf5, 0x21, 0x9a, 0xc6, 0x22, 0x58, 0x20,
70 0x87, 0x5f, 0x90, 0xe6, 0xfd, 0x71, 0x27, 0x9f,
71 0xeb, 0xe3, 0x03, 0x44, 0xbc, 0x8d, 0x49, 0xc6,
72 0x1c, 0x31, 0x3b, 0x72, 0xae, 0xd4, 0x53, 0xb1,
73 0xfe, 0x5d, 0xe1, 0x30, 0xfc, 0x2b, 0x1e, 0xd2,
74};
75
76static const unsigned char authdata_unsorted_keys[198] = {
77 0x58, 0xc4, 0x49, 0x96, 0x0d, 0xe5, 0x88, 0x0e,
78 0x8c, 0x68, 0x74, 0x34, 0x17, 0x0f, 0x64, 0x76,
79 0x60, 0x5b, 0x8f, 0xe4, 0xae, 0xb9, 0xa2, 0x86,
80 0x32, 0xc7, 0x99, 0x5c, 0xf3, 0xba, 0x83, 0x1d,
81 0x97, 0x63, 0x41, 0x00, 0x00, 0x00, 0x00, 0xf8,
82 0xa0, 0x11, 0xf3, 0x8c, 0x0a, 0x4d, 0x15, 0x80,
83 0x06, 0x17, 0x11, 0x1f, 0x9e, 0xdc, 0x7d, 0x00,
84 0x40, 0x53, 0xfb, 0xdf, 0xaa, 0xce, 0x63, 0xde,
85 0xc5, 0xfe, 0x47, 0xe6, 0x52, 0xeb, 0xf3, 0x5d,
86 0x53, 0xa8, 0xbf, 0x9d, 0xd6, 0x09, 0x6b, 0x5e,
87 0x7f, 0xe0, 0x0d, 0x51, 0x30, 0x85, 0x6a, 0xda,
88 0x68, 0x70, 0x85, 0xb0, 0xdb, 0x08, 0x0b, 0x83,
89 0x2c, 0xef, 0x44, 0xe2, 0x36, 0x88, 0xee, 0x76,
90 0x90, 0x6e, 0x7b, 0x50, 0x3e, 0x9a, 0xa0, 0xd6,
91 0x3c, 0x34, 0xe3, 0x83, 0xe7, 0xd1, 0xbd, 0x9f,
92 0x25, 0xa5, 0x03, 0x26, 0x01, 0x02, 0x20, 0x01,
93 0x21, 0x58, 0x20, 0x17, 0x5b, 0x27, 0xa6, 0x56,
94 0xb2, 0x26, 0x0c, 0x26, 0x0c, 0x55, 0x42, 0x78,
95 0x17, 0x5d, 0x4c, 0xf8, 0xa2, 0xfd, 0x1b, 0xb9,
96 0x54, 0xdf, 0xd5, 0xeb, 0xbf, 0x22, 0x64, 0xf5,
97 0x21, 0x9a, 0xc6, 0x22, 0x58, 0x20, 0x87, 0x5f,
98 0x90, 0xe6, 0xfd, 0x71, 0x27, 0x9f, 0xeb, 0xe3,
99 0x03, 0x44, 0xbc, 0x8d, 0x49, 0xc6, 0x1c, 0x31,
100 0x3b, 0x72, 0xae, 0xd4, 0x53, 0xb1, 0xfe, 0x5d,
101 0xe1, 0x30, 0xfc, 0x2b, 0x1e, 0xd2,
102};
103
104static const unsigned char x509[742] = {
105 0x30, 0x82, 0x02, 0xe2, 0x30, 0x81, 0xcb, 0x02,
106 0x01, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
107 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05,
108 0x00, 0x30, 0x1d, 0x31, 0x1b, 0x30, 0x19, 0x06,
109 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x59, 0x75,
110 0x62, 0x69, 0x63, 0x6f, 0x20, 0x55, 0x32, 0x46,
111 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x41,
112 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x30, 0x35,
113 0x31, 0x35, 0x31, 0x32, 0x35, 0x38, 0x35, 0x34,
114 0x5a, 0x17, 0x0d, 0x31, 0x34, 0x30, 0x36, 0x31,
115 0x34, 0x31, 0x32, 0x35, 0x38, 0x35, 0x34, 0x5a,
116 0x30, 0x1d, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03,
117 0x55, 0x04, 0x03, 0x13, 0x12, 0x59, 0x75, 0x62,
118 0x69, 0x63, 0x6f, 0x20, 0x55, 0x32, 0x46, 0x20,
119 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x45, 0x30,
120 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48,
121 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86,
122 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42,
123 0x00, 0x04, 0xdb, 0x0a, 0xdb, 0xf5, 0x21, 0xc7,
124 0x5c, 0xce, 0x63, 0xdc, 0xa6, 0xe1, 0xe8, 0x25,
125 0x06, 0x0d, 0x94, 0xe6, 0x27, 0x54, 0x19, 0x4f,
126 0x9d, 0x24, 0xaf, 0x26, 0x1a, 0xbe, 0xad, 0x99,
127 0x44, 0x1f, 0x95, 0xa3, 0x71, 0x91, 0x0a, 0x3a,
128 0x20, 0xe7, 0x3e, 0x91, 0x5e, 0x13, 0xe8, 0xbe,
129 0x38, 0x05, 0x7a, 0xd5, 0x7a, 0xa3, 0x7e, 0x76,
130 0x90, 0x8f, 0xaf, 0xe2, 0x8a, 0x94, 0xb6, 0x30,
131 0xeb, 0x9d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
132 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05,
133 0x00, 0x03, 0x82, 0x02, 0x01, 0x00, 0x95, 0x40,
134 0x6b, 0x50, 0x61, 0x7d, 0xad, 0x84, 0xa3, 0xb4,
135 0xeb, 0x88, 0x0f, 0xe3, 0x30, 0x0f, 0x2d, 0xa2,
136 0x0a, 0x00, 0xd9, 0x25, 0x04, 0xee, 0x72, 0xfa,
137 0x67, 0xdf, 0x58, 0x51, 0x0f, 0x0b, 0x47, 0x02,
138 0x9c, 0x3e, 0x41, 0x29, 0x4a, 0x93, 0xac, 0x29,
139 0x85, 0x89, 0x2d, 0xa4, 0x7a, 0x81, 0x32, 0x28,
140 0x57, 0x71, 0x01, 0xef, 0xa8, 0x42, 0x88, 0x16,
141 0x96, 0x37, 0x91, 0xd5, 0xdf, 0xe0, 0x8f, 0xc9,
142 0x3c, 0x8d, 0xb0, 0xcd, 0x89, 0x70, 0x82, 0xec,
143 0x79, 0xd3, 0xc6, 0x78, 0x73, 0x29, 0x32, 0xe5,
144 0xab, 0x6c, 0xbd, 0x56, 0x9f, 0xd5, 0x45, 0x91,
145 0xce, 0xc1, 0xdd, 0x8d, 0x64, 0xdc, 0xe9, 0x9c,
146 0x1f, 0x5e, 0x3c, 0xd2, 0xaf, 0x51, 0xa5, 0x82,
147 0x18, 0xaf, 0xe0, 0x37, 0xe7, 0x32, 0x9e, 0x76,
148 0x05, 0x77, 0x02, 0x7b, 0xe6, 0x24, 0xa0, 0x31,
149 0x56, 0x1b, 0xfd, 0x19, 0xc5, 0x71, 0xd3, 0xf0,
150 0x9e, 0xc0, 0x73, 0x05, 0x4e, 0xbc, 0x85, 0xb8,
151 0x53, 0x9e, 0xef, 0xc5, 0xbc, 0x9c, 0x56, 0xa3,
152 0xba, 0xd9, 0x27, 0x6a, 0xbb, 0xa9, 0x7a, 0x40,
153 0xd7, 0x47, 0x8b, 0x55, 0x72, 0x6b, 0xe3, 0xfe,
154 0x28, 0x49, 0x71, 0x24, 0xf4, 0x8f, 0xf4, 0x20,
155 0x81, 0xea, 0x38, 0xff, 0x7c, 0x0a, 0x4f, 0xdf,
156 0x02, 0x82, 0x39, 0x81, 0x82, 0x3b, 0xca, 0x09,
157 0xdd, 0xca, 0xaa, 0x0f, 0x27, 0xf5, 0xa4, 0x83,
158 0x55, 0x6c, 0x9a, 0x39, 0x9b, 0x15, 0x3a, 0x16,
159 0x63, 0xdc, 0x5b, 0xf9, 0xac, 0x5b, 0xbc, 0xf7,
160 0x9f, 0xbe, 0x0f, 0x8a, 0xa2, 0x3c, 0x31, 0x13,
161 0xa3, 0x32, 0x48, 0xca, 0x58, 0x87, 0xf8, 0x7b,
162 0xa0, 0xa1, 0x0a, 0x6a, 0x60, 0x96, 0x93, 0x5f,
163 0x5d, 0x26, 0x9e, 0x63, 0x1d, 0x09, 0xae, 0x9a,
164 0x41, 0xe5, 0xbd, 0x08, 0x47, 0xfe, 0xe5, 0x09,
165 0x9b, 0x20, 0xfd, 0x12, 0xe2, 0xe6, 0x40, 0x7f,
166 0xba, 0x4a, 0x61, 0x33, 0x66, 0x0d, 0x0e, 0x73,
167 0xdb, 0xb0, 0xd5, 0xa2, 0x9a, 0x9a, 0x17, 0x0d,
168 0x34, 0x30, 0x85, 0x6a, 0x42, 0x46, 0x9e, 0xff,
169 0x34, 0x8f, 0x5f, 0x87, 0x6c, 0x35, 0xe7, 0xa8,
170 0x4d, 0x35, 0xeb, 0xc1, 0x41, 0xaa, 0x8a, 0xd2,
171 0xda, 0x19, 0xaa, 0x79, 0xa2, 0x5f, 0x35, 0x2c,
172 0xa0, 0xfd, 0x25, 0xd3, 0xf7, 0x9d, 0x25, 0x18,
173 0x2d, 0xfa, 0xb4, 0xbc, 0xbb, 0x07, 0x34, 0x3c,
174 0x8d, 0x81, 0xbd, 0xf4, 0xe9, 0x37, 0xdb, 0x39,
175 0xe9, 0xd1, 0x45, 0x5b, 0x20, 0x41, 0x2f, 0x2d,
176 0x27, 0x22, 0xdc, 0x92, 0x74, 0x8a, 0x92, 0xd5,
177 0x83, 0xfd, 0x09, 0xfb, 0x13, 0x9b, 0xe3, 0x39,
178 0x7a, 0x6b, 0x5c, 0xfa, 0xe6, 0x76, 0x9e, 0xe0,
179 0xe4, 0xe3, 0xef, 0xad, 0xbc, 0xfd, 0x42, 0x45,
180 0x9a, 0xd4, 0x94, 0xd1, 0x7e, 0x8d, 0xa7, 0xd8,
181 0x05, 0xd5, 0xd3, 0x62, 0xcf, 0x15, 0xcf, 0x94,
182 0x7d, 0x1f, 0x5b, 0x58, 0x20, 0x44, 0x20, 0x90,
183 0x71, 0xbe, 0x66, 0xe9, 0x9a, 0xab, 0x74, 0x32,
184 0x70, 0x53, 0x1d, 0x69, 0xed, 0x87, 0x66, 0xf4,
185 0x09, 0x4f, 0xca, 0x25, 0x30, 0xc2, 0x63, 0x79,
186 0x00, 0x3c, 0xb1, 0x9b, 0x39, 0x3f, 0x00, 0xe0,
187 0xa8, 0x88, 0xef, 0x7a, 0x51, 0x5b, 0xe7, 0xbd,
188 0x49, 0x64, 0xda, 0x41, 0x7b, 0x24, 0xc3, 0x71,
189 0x22, 0xfd, 0xd1, 0xd1, 0x20, 0xb3, 0x3f, 0x97,
190 0xd3, 0x97, 0xb2, 0xaa, 0x18, 0x1c, 0x9e, 0x03,
191 0x77, 0x7b, 0x5b, 0x7e, 0xf9, 0xa3, 0xa0, 0xd6,
192 0x20, 0x81, 0x2c, 0x38, 0x8f, 0x9d, 0x25, 0xde,
193 0xe9, 0xc8, 0xf5, 0xdd, 0x6a, 0x47, 0x9c, 0x65,
194 0x04, 0x5a, 0x56, 0xe6, 0xc2, 0xeb, 0xf2, 0x02,
195 0x97, 0xe1, 0xb9, 0xd8, 0xe1, 0x24, 0x76, 0x9f,
196 0x23, 0x62, 0x39, 0x03, 0x4b, 0xc8, 0xf7, 0x34,
197 0x07, 0x49, 0xd6, 0xe7, 0x4d, 0x9a,
198};
199
200const unsigned char sig[70] = {
201 0x30, 0x44, 0x02, 0x20, 0x54, 0x92, 0x28, 0x3b,
202 0x83, 0x33, 0x47, 0x56, 0x68, 0x79, 0xb2, 0x0c,
203 0x84, 0x80, 0xcc, 0x67, 0x27, 0x8b, 0xfa, 0x48,
204 0x43, 0x0d, 0x3c, 0xb4, 0x02, 0x36, 0x87, 0x97,
205 0x3e, 0xdf, 0x2f, 0x65, 0x02, 0x20, 0x1b, 0x56,
206 0x17, 0x06, 0xe2, 0x26, 0x0f, 0x6a, 0xe9, 0xa9,
207 0x70, 0x99, 0x62, 0xeb, 0x3a, 0x04, 0x1a, 0xc4,
208 0xa7, 0x03, 0x28, 0x56, 0x7c, 0xed, 0x47, 0x08,
209 0x68, 0x73, 0x6a, 0xb6, 0x89, 0x0d,
210};
211
212const unsigned char pubkey[64] = {
213 0x17, 0x5b, 0x27, 0xa6, 0x56, 0xb2, 0x26, 0x0c,
214 0x26, 0x0c, 0x55, 0x42, 0x78, 0x17, 0x5d, 0x4c,
215 0xf8, 0xa2, 0xfd, 0x1b, 0xb9, 0x54, 0xdf, 0xd5,
216 0xeb, 0xbf, 0x22, 0x64, 0xf5, 0x21, 0x9a, 0xc6,
217 0x87, 0x5f, 0x90, 0xe6, 0xfd, 0x71, 0x27, 0x9f,
218 0xeb, 0xe3, 0x03, 0x44, 0xbc, 0x8d, 0x49, 0xc6,
219 0x1c, 0x31, 0x3b, 0x72, 0xae, 0xd4, 0x53, 0xb1,
220 0xfe, 0x5d, 0xe1, 0x30, 0xfc, 0x2b, 0x1e, 0xd2,
221};
222
223const unsigned char id[64] = {
224 0x53, 0xfb, 0xdf, 0xaa, 0xce, 0x63, 0xde, 0xc5,
225 0xfe, 0x47, 0xe6, 0x52, 0xeb, 0xf3, 0x5d, 0x53,
226 0xa8, 0xbf, 0x9d, 0xd6, 0x09, 0x6b, 0x5e, 0x7f,
227 0xe0, 0x0d, 0x51, 0x30, 0x85, 0x6a, 0xda, 0x68,
228 0x70, 0x85, 0xb0, 0xdb, 0x08, 0x0b, 0x83, 0x2c,
229 0xef, 0x44, 0xe2, 0x36, 0x88, 0xee, 0x76, 0x90,
230 0x6e, 0x7b, 0x50, 0x3e, 0x9a, 0xa0, 0xd6, 0x3c,
231 0x34, 0xe3, 0x83, 0xe7, 0xd1, 0xbd, 0x9f, 0x25,
232};
233
234const char rp_id[] = "localhost";
235const char rp_name[] = "sweet home localhost";
236
237static void *
238dummy_open(const char *path)
239{
240 (void)path;
241
242 return (FAKE_DEV_HANDLE);
243}
244
245static void
246dummy_close(void *handle)
247{
248 assert(handle == FAKE_DEV_HANDLE);
249}
250
251static int
252dummy_read(void *handle, unsigned char *buf, size_t len, int ms)
253{
254 (void)handle;
255 (void)buf;
256 (void)len;
257 (void)ms;
258
259 abort();
260 /* NOTREACHED */
261}
262
263static int
264dummy_write(void *handle, const unsigned char *buf, size_t len)
265{
266 (void)handle;
267 (void)buf;
268 (void)len;
269
270 abort();
271 /* NOTREACHED */
272}
273
274static fido_cred_t *
275alloc_cred(void)
276{
277 fido_cred_t *c;
278
279 c = fido_cred_new();
280 assert(c != NULL);
281
282 return (c);
283}
284
285static void
286free_cred(fido_cred_t *c)
287{
288 fido_cred_free(&c);
289 assert(c == NULL);
290}
291
292static fido_dev_t *
293alloc_dev(void)
294{
295 fido_dev_t *d;
296
297 d = fido_dev_new();
298 assert(d != NULL);
299
300 return (d);
301}
302
303static void
304free_dev(fido_dev_t *d)
305{
306 fido_dev_free(&d);
307 assert(d == NULL);
308}
309
310static void
311empty_cred(void)
312{
313 fido_cred_t *c;
314 fido_dev_t *d;
315 fido_dev_io_t io_f;
316
317 c = alloc_cred();
318 assert(fido_cred_authdata_len(c) == 0);
319 assert(fido_cred_authdata_ptr(c) == NULL);
320 assert(fido_cred_clientdata_hash_len(c) == 0);
321 assert(fido_cred_clientdata_hash_ptr(c) == NULL);
322 assert(fido_cred_flags(c) == 0);
323 assert(fido_cred_fmt(c) == NULL);
324 assert(fido_cred_id_len(c) == 0);
325 assert(fido_cred_id_ptr(c) == NULL);
326 assert(fido_cred_pubkey_len(c) == 0);
327 assert(fido_cred_pubkey_ptr(c) == NULL);
328 assert(fido_cred_rp_id(c) == NULL);
329 assert(fido_cred_rp_name(c) == NULL);
330 assert(fido_cred_sig_len(c) == 0);
331 assert(fido_cred_sig_ptr(c) == NULL);
332 assert(fido_cred_x5c_len(c) == 0);
333 assert(fido_cred_x5c_ptr(c) == NULL);
334 assert(fido_cred_verify(c) == FIDO_ERR_INVALID_ARGUMENT);
335
336 io_f.open = dummy_open;
337 io_f.close = dummy_close;
338 io_f.read = dummy_read;
339 io_f.write = dummy_write;
340 d = alloc_dev();
341
342 fido_dev_force_u2f(d);
343 assert(fido_dev_set_io_functions(d, &io_f) == FIDO_OK);
344 assert(fido_dev_make_cred(d, c, NULL) == FIDO_ERR_INVALID_ARGUMENT);
345 assert(fido_dev_make_cred(d, c, "") == FIDO_ERR_UNSUPPORTED_OPTION);
346 assert(fido_cred_verify(c) == FIDO_ERR_INVALID_ARGUMENT);
347
348 fido_dev_force_fido2(d);
349 assert(fido_dev_set_io_functions(d, &io_f) == FIDO_OK);
350 assert(fido_dev_make_cred(d, c, NULL) == FIDO_ERR_INVALID_ARGUMENT);
351 assert(fido_dev_make_cred(d, c, "") == FIDO_ERR_INVALID_ARGUMENT);
352 assert(fido_cred_verify(c) == FIDO_ERR_INVALID_ARGUMENT);
353
354 free_cred(c);
355 free_dev(d);
356}
357
358static void
359valid_cred(void)
360{
361 fido_cred_t *c;
362
363 c = alloc_cred();
364 assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK);
365 assert(fido_cred_set_clientdata_hash(c, cdh, sizeof(cdh)) == FIDO_OK);
366 assert(fido_cred_set_rp(c, rp_id, rp_name) == FIDO_OK);
367 assert(fido_cred_set_authdata(c, authdata, sizeof(authdata)) == FIDO_OK);
368 assert(fido_cred_set_rk(c, FIDO_OPT_FALSE) == FIDO_OK);
369 assert(fido_cred_set_uv(c, FIDO_OPT_FALSE) == FIDO_OK);
370 assert(fido_cred_set_x509(c, x509, sizeof(x509)) == FIDO_OK);
371 assert(fido_cred_set_sig(c, sig, sizeof(sig)) == FIDO_OK);
372 assert(fido_cred_set_fmt(c, "packed") == FIDO_OK);
373 assert(fido_cred_verify(c) == FIDO_OK);
374 assert(fido_cred_pubkey_len(c) == sizeof(pubkey));
375 assert(memcmp(fido_cred_pubkey_ptr(c), pubkey, sizeof(pubkey)) == 0);
376 assert(fido_cred_id_len(c) == sizeof(id));
377 assert(memcmp(fido_cred_id_ptr(c), id, sizeof(id)) == 0);
378 free_cred(c);
379}
380
381static void
382no_cdh(void)
383{
384 fido_cred_t *c;
385
386 c = alloc_cred();
387 assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK);
388 assert(fido_cred_set_rp(c, rp_id, rp_name) == FIDO_OK);
389 assert(fido_cred_set_authdata(c, authdata, sizeof(authdata)) == FIDO_OK);
390 assert(fido_cred_set_rk(c, FIDO_OPT_FALSE) == FIDO_OK);
391 assert(fido_cred_set_uv(c, FIDO_OPT_FALSE) == FIDO_OK);
392 assert(fido_cred_set_x509(c, x509, sizeof(x509)) == FIDO_OK);
393 assert(fido_cred_set_sig(c, sig, sizeof(sig)) == FIDO_OK);
394 assert(fido_cred_set_fmt(c, "packed") == FIDO_OK);
395 assert(fido_cred_verify(c) == FIDO_ERR_INVALID_ARGUMENT);
396 assert(fido_cred_pubkey_len(c) == sizeof(pubkey));
397 assert(memcmp(fido_cred_pubkey_ptr(c), pubkey, sizeof(pubkey)) == 0);
398 assert(fido_cred_id_len(c) == sizeof(id));
399 assert(memcmp(fido_cred_id_ptr(c), id, sizeof(id)) == 0);
400 free_cred(c);
401}
402
403static void
404no_rp_id(void)
405{
406 fido_cred_t *c;
407
408 c = alloc_cred();
409 assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK);
410 assert(fido_cred_set_clientdata_hash(c, cdh, sizeof(cdh)) == FIDO_OK);
411 assert(fido_cred_set_authdata(c, authdata, sizeof(authdata)) == FIDO_OK);
412 assert(fido_cred_set_rk(c, FIDO_OPT_FALSE) == FIDO_OK);
413 assert(fido_cred_set_uv(c, FIDO_OPT_FALSE) == FIDO_OK);
414 assert(fido_cred_set_x509(c, x509, sizeof(x509)) == FIDO_OK);
415 assert(fido_cred_set_sig(c, sig, sizeof(sig)) == FIDO_OK);
416 assert(fido_cred_set_fmt(c, "packed") == FIDO_OK);
417 assert(fido_cred_verify(c) == FIDO_ERR_INVALID_ARGUMENT);
418 assert(fido_cred_pubkey_len(c) == sizeof(pubkey));
419 assert(memcmp(fido_cred_pubkey_ptr(c), pubkey, sizeof(pubkey)) == 0);
420 assert(fido_cred_id_len(c) == sizeof(id));
421 assert(memcmp(fido_cred_id_ptr(c), id, sizeof(id)) == 0);
422 free_cred(c);
423}
424
425static void
426no_rp_name(void)
427{
428 fido_cred_t *c;
429
430 c = alloc_cred();
431 assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK);
432 assert(fido_cred_set_rp(c, rp_id, NULL) == FIDO_OK);
433 assert(fido_cred_set_clientdata_hash(c, cdh, sizeof(cdh)) == FIDO_OK);
434 assert(fido_cred_set_authdata(c, authdata, sizeof(authdata)) == FIDO_OK);
435 assert(fido_cred_set_rk(c, FIDO_OPT_FALSE) == FIDO_OK);
436 assert(fido_cred_set_uv(c, FIDO_OPT_FALSE) == FIDO_OK);
437 assert(fido_cred_set_x509(c, x509, sizeof(x509)) == FIDO_OK);
438 assert(fido_cred_set_sig(c, sig, sizeof(sig)) == FIDO_OK);
439 assert(fido_cred_set_fmt(c, "packed") == FIDO_OK);
440 assert(fido_cred_verify(c) == FIDO_OK);
441 assert(fido_cred_pubkey_len(c) == sizeof(pubkey));
442 assert(memcmp(fido_cred_pubkey_ptr(c), pubkey, sizeof(pubkey)) == 0);
443 assert(fido_cred_id_len(c) == sizeof(id));
444 assert(memcmp(fido_cred_id_ptr(c), id, sizeof(id)) == 0);
445 free_cred(c);
446}
447
448static void
449no_authdata(void)
450{
451 fido_cred_t *c;
452
453 c = alloc_cred();
454 assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK);
455 assert(fido_cred_set_clientdata_hash(c, cdh, sizeof(cdh)) == FIDO_OK);
456 assert(fido_cred_set_rp(c, rp_id, rp_name) == FIDO_OK);
457 assert(fido_cred_set_rk(c, FIDO_OPT_FALSE) == FIDO_OK);
458 assert(fido_cred_set_uv(c, FIDO_OPT_FALSE) == FIDO_OK);
459 assert(fido_cred_set_x509(c, x509, sizeof(x509)) == FIDO_OK);
460 assert(fido_cred_set_sig(c, sig, sizeof(sig)) == FIDO_OK);
461 assert(fido_cred_set_fmt(c, "packed") == FIDO_OK);
462 assert(fido_cred_verify(c) == FIDO_ERR_INVALID_ARGUMENT);
463 assert(fido_cred_pubkey_len(c) == 0);
464 assert(fido_cred_pubkey_ptr(c) == NULL);
465 assert(fido_cred_id_len(c) == 0);
466 assert(fido_cred_id_ptr(c) == NULL);
467 free_cred(c);
468}
469
470static void
471no_x509(void)
472{
473 fido_cred_t *c;
474
475 c = alloc_cred();
476 assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK);
477 assert(fido_cred_set_clientdata_hash(c, cdh, sizeof(cdh)) == FIDO_OK);
478 assert(fido_cred_set_rp(c, rp_id, rp_name) == FIDO_OK);
479 assert(fido_cred_set_authdata(c, authdata, sizeof(authdata)) == FIDO_OK);
480 assert(fido_cred_set_rk(c, FIDO_OPT_FALSE) == FIDO_OK);
481 assert(fido_cred_set_uv(c, FIDO_OPT_FALSE) == FIDO_OK);
482 assert(fido_cred_set_sig(c, sig, sizeof(sig)) == FIDO_OK);
483 assert(fido_cred_set_fmt(c, "packed") == FIDO_OK);
484 assert(fido_cred_verify(c) == FIDO_ERR_INVALID_ARGUMENT);
485 assert(fido_cred_pubkey_len(c) == sizeof(pubkey));
486 assert(memcmp(fido_cred_pubkey_ptr(c), pubkey, sizeof(pubkey)) == 0);
487 assert(fido_cred_id_len(c) == sizeof(id));
488 assert(memcmp(fido_cred_id_ptr(c), id, sizeof(id)) == 0);
489 free_cred(c);
490}
491
492static void
493no_sig(void)
494{
495 fido_cred_t *c;
496
497 c = alloc_cred();
498 assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK);
499 assert(fido_cred_set_clientdata_hash(c, cdh, sizeof(cdh)) == FIDO_OK);
500 assert(fido_cred_set_rp(c, rp_id, rp_name) == FIDO_OK);
501 assert(fido_cred_set_authdata(c, authdata, sizeof(authdata)) == FIDO_OK);
502 assert(fido_cred_set_rk(c, FIDO_OPT_FALSE) == FIDO_OK);
503 assert(fido_cred_set_uv(c, FIDO_OPT_FALSE) == FIDO_OK);
504 assert(fido_cred_set_x509(c, x509, sizeof(x509)) == FIDO_OK);
505 assert(fido_cred_set_fmt(c, "packed") == FIDO_OK);
506 assert(fido_cred_verify(c) == FIDO_ERR_INVALID_ARGUMENT);
507 assert(fido_cred_pubkey_len(c) == sizeof(pubkey));
508 assert(memcmp(fido_cred_pubkey_ptr(c), pubkey, sizeof(pubkey)) == 0);
509 assert(fido_cred_id_len(c) == sizeof(id));
510 assert(memcmp(fido_cred_id_ptr(c), id, sizeof(id)) == 0);
511 free_cred(c);
512}
513
514static void
515no_fmt(void)
516{
517 fido_cred_t *c;
518
519 c = alloc_cred();
520 assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK);
521 assert(fido_cred_set_clientdata_hash(c, cdh, sizeof(cdh)) == FIDO_OK);
522 assert(fido_cred_set_rp(c, rp_id, rp_name) == FIDO_OK);
523 assert(fido_cred_set_authdata(c, authdata, sizeof(authdata)) == FIDO_OK);
524 assert(fido_cred_set_rk(c, FIDO_OPT_FALSE) == FIDO_OK);
525 assert(fido_cred_set_uv(c, FIDO_OPT_FALSE) == FIDO_OK);
526 assert(fido_cred_set_x509(c, x509, sizeof(x509)) == FIDO_OK);
527 assert(fido_cred_set_sig(c, sig, sizeof(sig)) == FIDO_OK);
528 assert(fido_cred_verify(c) == FIDO_ERR_INVALID_ARGUMENT);
529 assert(fido_cred_pubkey_len(c) == sizeof(pubkey));
530 assert(memcmp(fido_cred_pubkey_ptr(c), pubkey, sizeof(pubkey)) == 0);
531 assert(fido_cred_id_len(c) == sizeof(id));
532 assert(memcmp(fido_cred_id_ptr(c), id, sizeof(id)) == 0);
533 free_cred(c);
534}
535
536static void
537wrong_options(void)
538{
539 fido_cred_t *c;
540
541 c = alloc_cred();
542 assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK);
543 assert(fido_cred_set_clientdata_hash(c, cdh, sizeof(cdh)) == FIDO_OK);
544 assert(fido_cred_set_rp(c, rp_id, rp_name) == FIDO_OK);
545 assert(fido_cred_set_authdata(c, authdata, sizeof(authdata)) == FIDO_OK);
546 assert(fido_cred_set_rk(c, FIDO_OPT_FALSE) == FIDO_OK);
547 assert(fido_cred_set_uv(c, FIDO_OPT_TRUE) == FIDO_OK);
548 assert(fido_cred_set_x509(c, x509, sizeof(x509)) == FIDO_OK);
549 assert(fido_cred_set_sig(c, sig, sizeof(sig)) == FIDO_OK);
550 assert(fido_cred_set_fmt(c, "packed") == FIDO_OK);
551 assert(fido_cred_verify(c) == FIDO_ERR_INVALID_PARAM);
552 assert(fido_cred_pubkey_len(c) == sizeof(pubkey));
553 assert(memcmp(fido_cred_pubkey_ptr(c), pubkey, sizeof(pubkey)) == 0);
554 assert(fido_cred_id_len(c) == sizeof(id));
555 assert(memcmp(fido_cred_id_ptr(c), id, sizeof(id)) == 0);
556 free_cred(c);
557}
558
559static void
560junk_cdh(void)
561{
562 fido_cred_t *c;
563 unsigned char *junk;
564
565 junk = malloc(sizeof(cdh));
566 assert(junk != NULL);
567 memcpy(junk, cdh, sizeof(cdh));
568 junk[0] = ~junk[0];
569
570 c = alloc_cred();
571 assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK);
572 assert(fido_cred_set_clientdata_hash(c, junk, sizeof(cdh)) == FIDO_OK);
573 assert(fido_cred_set_rp(c, rp_id, rp_name) == FIDO_OK);
574 assert(fido_cred_set_authdata(c, authdata, sizeof(authdata)) == FIDO_OK);
575 assert(fido_cred_set_rk(c, FIDO_OPT_FALSE) == FIDO_OK);
576 assert(fido_cred_set_uv(c, FIDO_OPT_FALSE) == FIDO_OK);
577 assert(fido_cred_set_x509(c, x509, sizeof(x509)) == FIDO_OK);
578 assert(fido_cred_set_sig(c, sig, sizeof(sig)) == FIDO_OK);
579 assert(fido_cred_set_fmt(c, "packed") == FIDO_OK);
580 assert(fido_cred_verify(c) == FIDO_ERR_INVALID_SIG);
581 assert(fido_cred_pubkey_len(c) == sizeof(pubkey));
582 assert(memcmp(fido_cred_pubkey_ptr(c), pubkey, sizeof(pubkey)) == 0);
583 assert(fido_cred_id_len(c) == sizeof(id));
584 assert(memcmp(fido_cred_id_ptr(c), id, sizeof(id)) == 0);
585 free_cred(c);
586 free(junk);
587}
588
589static void
590junk_rp_id(void)
591{
592 fido_cred_t *c;
593
594 c = alloc_cred();
595 assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK);
596 assert(fido_cred_set_clientdata_hash(c, cdh, sizeof(cdh)) == FIDO_OK);
597 assert(fido_cred_set_rp(c, "potato", rp_name) == FIDO_OK);
598 assert(fido_cred_set_authdata(c, authdata, sizeof(authdata)) == FIDO_OK);
599 assert(fido_cred_set_rk(c, FIDO_OPT_FALSE) == FIDO_OK);
600 assert(fido_cred_set_uv(c, FIDO_OPT_FALSE) == FIDO_OK);
601 assert(fido_cred_set_x509(c, x509, sizeof(x509)) == FIDO_OK);
602 assert(fido_cred_set_sig(c, sig, sizeof(sig)) == FIDO_OK);
603 assert(fido_cred_set_fmt(c, "packed") == FIDO_OK);
604 assert(fido_cred_verify(c) == FIDO_ERR_INVALID_PARAM);
605 assert(fido_cred_pubkey_len(c) == sizeof(pubkey));
606 assert(memcmp(fido_cred_pubkey_ptr(c), pubkey, sizeof(pubkey)) == 0);
607 assert(fido_cred_id_len(c) == sizeof(id));
608 assert(memcmp(fido_cred_id_ptr(c), id, sizeof(id)) == 0);
609 free_cred(c);
610}
611
612static void
613junk_rp_name(void)
614{
615 fido_cred_t *c;
616
617 c = alloc_cred();
618 assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK);
619 assert(fido_cred_set_clientdata_hash(c, cdh, sizeof(cdh)) == FIDO_OK);
620 assert(fido_cred_set_rp(c, rp_id, "potato") == FIDO_OK);
621 assert(fido_cred_set_authdata(c, authdata, sizeof(authdata)) == FIDO_OK);
622 assert(fido_cred_set_rk(c, FIDO_OPT_FALSE) == FIDO_OK);
623 assert(fido_cred_set_uv(c, FIDO_OPT_FALSE) == FIDO_OK);
624 assert(fido_cred_set_x509(c, x509, sizeof(x509)) == FIDO_OK);
625 assert(fido_cred_set_sig(c, sig, sizeof(sig)) == FIDO_OK);
626 assert(fido_cred_set_fmt(c, "packed") == FIDO_OK);
627 assert(fido_cred_verify(c) == FIDO_OK);
628 assert(fido_cred_pubkey_len(c) == sizeof(pubkey));
629 assert(memcmp(fido_cred_pubkey_ptr(c), pubkey, sizeof(pubkey)) == 0);
630 assert(fido_cred_id_len(c) == sizeof(id));
631 assert(memcmp(fido_cred_id_ptr(c), id, sizeof(id)) == 0);
632 free_cred(c);
633}
634
635static void
636junk_authdata(void)
637{
638 fido_cred_t *c;
639 unsigned char *junk;
640
641 junk = malloc(sizeof(authdata));
642 assert(junk != NULL);
643 memcpy(junk, authdata, sizeof(authdata));
644 junk[0] = ~junk[0];
645
646 c = alloc_cred();
647 assert(fido_cred_set_authdata(c, junk,
648 sizeof(authdata)) == FIDO_ERR_INVALID_ARGUMENT);
649 assert(fido_cred_authdata_len(c) == 0);
650 assert(fido_cred_authdata_ptr(c) == NULL);
651 assert(fido_cred_flags(c) == 0);
652 assert(fido_cred_fmt(c) == NULL);
653 assert(fido_cred_id_len(c) == 0);
654 assert(fido_cred_id_ptr(c) == NULL);
655 assert(fido_cred_pubkey_len(c) == 0);
656 assert(fido_cred_pubkey_ptr(c) == NULL);
657 assert(fido_cred_rp_id(c) == NULL);
658 assert(fido_cred_rp_name(c) == NULL);
659 assert(fido_cred_sig_len(c) == 0);
660 assert(fido_cred_sig_ptr(c) == NULL);
661 assert(fido_cred_x5c_len(c) == 0);
662 assert(fido_cred_x5c_ptr(c) == NULL);
663 assert(fido_cred_verify(c) == FIDO_ERR_INVALID_ARGUMENT);
664 free_cred(c);
665 free(junk);
666}
667
668static void
669junk_sig(void)
670{
671 fido_cred_t *c;
672 unsigned char *junk;
673
674 junk = malloc(sizeof(sig));
675 assert(junk != NULL);
676 memcpy(junk, sig, sizeof(sig));
677 junk[0] = ~junk[0];
678
679 c = alloc_cred();
680 assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK);
681 assert(fido_cred_set_clientdata_hash(c, cdh, sizeof(cdh)) == FIDO_OK);
682 assert(fido_cred_set_rp(c, rp_id, rp_name) == FIDO_OK);
683 assert(fido_cred_set_authdata(c, authdata, sizeof(authdata)) == FIDO_OK);
684 assert(fido_cred_set_rk(c, FIDO_OPT_FALSE) == FIDO_OK);
685 assert(fido_cred_set_uv(c, FIDO_OPT_FALSE) == FIDO_OK);
686 assert(fido_cred_set_x509(c, x509, sizeof(x509)) == FIDO_OK);
687 assert(fido_cred_set_sig(c, junk, sizeof(sig)) == FIDO_OK);
688 assert(fido_cred_set_fmt(c, "packed") == FIDO_OK);
689 assert(fido_cred_verify(c) == FIDO_ERR_INVALID_SIG);
690 assert(fido_cred_pubkey_len(c) == sizeof(pubkey));
691 assert(memcmp(fido_cred_pubkey_ptr(c), pubkey, sizeof(pubkey)) == 0);
692 assert(fido_cred_id_len(c) == sizeof(id));
693 assert(memcmp(fido_cred_id_ptr(c), id, sizeof(id)) == 0);
694 free_cred(c);
695 free(junk);
696}
697
698static void
699junk_x509(void)
700{
701 fido_cred_t *c;
702 unsigned char *junk;
703
704 junk = malloc(sizeof(x509));
705 assert(junk != NULL);
706 memcpy(junk, x509, sizeof(x509));
707 junk[0] = ~junk[0];
708
709 c = alloc_cred();
710 assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK);
711 assert(fido_cred_set_clientdata_hash(c, cdh, sizeof(cdh)) == FIDO_OK);
712 assert(fido_cred_set_rp(c, rp_id, rp_name) == FIDO_OK);
713 assert(fido_cred_set_authdata(c, authdata, sizeof(authdata)) == FIDO_OK);
714 assert(fido_cred_set_rk(c, FIDO_OPT_FALSE) == FIDO_OK);
715 assert(fido_cred_set_uv(c, FIDO_OPT_FALSE) == FIDO_OK);
716 assert(fido_cred_set_x509(c, junk, sizeof(x509)) == FIDO_OK);
717 assert(fido_cred_set_sig(c, sig, sizeof(sig)) == FIDO_OK);
718 assert(fido_cred_set_fmt(c, "packed") == FIDO_OK);
719 assert(fido_cred_verify(c) == FIDO_ERR_INVALID_SIG);
720 assert(fido_cred_pubkey_len(c) == sizeof(pubkey));
721 assert(memcmp(fido_cred_pubkey_ptr(c), pubkey, sizeof(pubkey)) == 0);
722 assert(fido_cred_id_len(c) == sizeof(id));
723 assert(memcmp(fido_cred_id_ptr(c), id, sizeof(id)) == 0);
724 free_cred(c);
725 free(junk);
726}
727
728/* github issue #6 */
729static void
730invalid_type(void)
731{
732 fido_cred_t *c;
733
734 c = alloc_cred();
735 assert(fido_cred_set_type(c, COSE_RS256) == FIDO_OK);
736 assert(fido_cred_set_clientdata_hash(c, cdh, sizeof(cdh)) == FIDO_OK);
737 assert(fido_cred_set_rp(c, rp_id, rp_name) == FIDO_OK);
738 assert(fido_cred_set_authdata(c, authdata, sizeof(authdata)) == FIDO_ERR_INVALID_ARGUMENT);
739 assert(fido_cred_set_rk(c, FIDO_OPT_FALSE) == FIDO_OK);
740 assert(fido_cred_set_uv(c, FIDO_OPT_FALSE) == FIDO_OK);
741 assert(fido_cred_set_x509(c, x509, sizeof(x509)) == FIDO_OK);
742 assert(fido_cred_set_sig(c, sig, sizeof(sig)) == FIDO_OK);
743 assert(fido_cred_set_fmt(c, "packed") == FIDO_OK);
744 assert(fido_cred_verify(c) == FIDO_ERR_INVALID_ARGUMENT);
745 assert(fido_cred_pubkey_len(c) == 0);
746 assert(fido_cred_pubkey_ptr(c) == NULL);
747 assert(fido_cred_id_len(c) == 0);
748 assert(fido_cred_id_ptr(c) == NULL);
749 free_cred(c);
750}
751
752/* cbor_serialize_alloc misuse */
753static void
754bad_cbor_serialize(void)
755{
756 fido_cred_t *c;
757
758 c = alloc_cred();
759 assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK);
760 assert(fido_cred_set_authdata(c, authdata, sizeof(authdata)) == FIDO_OK);
761 assert(fido_cred_authdata_len(c) == sizeof(authdata));
762 free_cred(c);
763}
764
765static void
766duplicate_keys(void)
767{
768 fido_cred_t *c;
769
770 c = alloc_cred();
771 assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK);
772 assert(fido_cred_set_authdata(c, authdata_dupkeys,
773 sizeof(authdata_dupkeys)) == FIDO_ERR_INVALID_ARGUMENT);
774 free_cred(c);
775}
776
777static void
778unsorted_keys(void)
779{
780 fido_cred_t *c;
781
782 c = alloc_cred();
783 assert(fido_cred_set_type(c, COSE_ES256) == FIDO_OK);
784 assert(fido_cred_set_authdata(c, authdata_unsorted_keys,
785 sizeof(authdata_unsorted_keys)) == FIDO_ERR_INVALID_ARGUMENT);
786 free_cred(c);
787}
788
789int
790main(void)
791{
792 fido_init(0);
793
794 empty_cred();
795 valid_cred();
796 no_cdh();
797 no_rp_id();
798 no_rp_name();
799 no_authdata();
800 no_x509();
801 no_sig();
802 no_fmt();
803 junk_cdh();
804 junk_rp_id();
805 junk_rp_name();
806 junk_authdata();
807 junk_x509();
808 junk_sig();
809 wrong_options();
810 invalid_type();
811 bad_cbor_serialize();
812 duplicate_keys();
813 unsorted_keys();
814
815 exit(0);
816}
diff --git a/regress/dev.c b/regress/dev.c
new file mode 100644
index 0000000..39b3584
--- /dev/null
+++ b/regress/dev.c
@@ -0,0 +1,77 @@
1/*
2 * Copyright (c) 2019 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <assert.h>
8#include <fido.h>
9
10#define FAKE_DEV_HANDLE ((void *)0xdeadbeef)
11#define REPORT_LEN (64 + 1)
12
13static void *
14dummy_open(const char *path)
15{
16 (void)path;
17
18 return (FAKE_DEV_HANDLE);
19}
20
21static void
22dummy_close(void *handle)
23{
24 assert(handle == FAKE_DEV_HANDLE);
25}
26
27static int
28dummy_read(void *handle, unsigned char *ptr, size_t len, int ms)
29{
30 (void)ptr;
31 (void)len;
32 (void)ms;
33
34 assert(handle == FAKE_DEV_HANDLE);
35
36 return (-1);
37}
38
39static int
40dummy_write(void *handle, const unsigned char *ptr, size_t len)
41{
42 assert(handle == FAKE_DEV_HANDLE);
43 assert(ptr != NULL);
44 assert(len == REPORT_LEN);
45
46 return ((int)len);
47}
48
49/* gh#56 */
50static void
51open_iff_ok(void)
52{
53 fido_dev_t *dev = NULL;
54 fido_dev_io_t io;
55
56 io.open = dummy_open;
57 io.close = dummy_close;
58 io.read = dummy_read;
59 io.write = dummy_write;
60
61 assert((dev = fido_dev_new()) != NULL);
62 assert(fido_dev_set_io_functions(dev, &io) == FIDO_OK);
63 assert(fido_dev_open(dev, "dummy") == FIDO_ERR_RX);
64 assert(fido_dev_close(dev) == FIDO_ERR_INVALID_ARGUMENT);
65
66 fido_dev_free(&dev);
67}
68
69int
70main(void)
71{
72 fido_init(0);
73
74 open_iff_ok();
75
76 exit(0);
77}
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..926e7f2
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,104 @@
1# Copyright (c) 2018 Yubico AB. All rights reserved.
2# Use of this source code is governed by a BSD-style
3# license that can be found in the LICENSE file.
4
5add_definitions(-D_FIDO_INTERNAL)
6
7list(APPEND FIDO_SOURCES
8 aes256.c
9 assert.c
10 authkey.c
11 bio.c
12 blob.c
13 buf.c
14 cbor.c
15 cred.c
16 credman.c
17 dev.c
18 ecdh.c
19 eddsa.c
20 err.c
21 es256.c
22 hid.c
23 info.c
24 io.c
25 iso7816.c
26 log.c
27 pin.c
28 reset.c
29 rs256.c
30 u2f.c
31)
32
33if(FUZZ)
34 list(APPEND FIDO_SOURCES ../fuzz/uniform_random.c)
35 list(APPEND FIDO_SOURCES ../fuzz/wrap.c)
36endif()
37
38if(WIN32)
39 list(APPEND COMPAT_SOURCES hid_win.c)
40elseif(APPLE)
41 list(APPEND COMPAT_SOURCES hid_osx.c)
42elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
43 list(APPEND COMPAT_SOURCES hid_linux.c)
44elseif(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
45 list(APPEND COMPAT_SOURCES hid_openbsd.c)
46endif()
47
48list(APPEND COMPAT_SOURCES
49 ../openbsd-compat/bsd-getpagesize.c
50 ../openbsd-compat/explicit_bzero.c
51 ../openbsd-compat/explicit_bzero_win32.c
52 ../openbsd-compat/recallocarray.c
53 ../openbsd-compat/timingsafe_bcmp.c
54)
55
56# static library
57add_library(fido2 STATIC ${FIDO_SOURCES} ${COMPAT_SOURCES})
58target_link_libraries(fido2 ${CBOR_LIBRARIES} ${CRYPTO_LIBRARIES}
59 ${UDEV_LIBRARIES} ${BASE_LIBRARIES})
60if(WIN32)
61 if (MINGW)
62 target_link_libraries(fido2 wsock32 ws2_32 bcrypt setupapi hid)
63 else()
64 target_link_libraries(fido2 wsock32 ws2_32 bcrypt SetupAPI hid)
65 set_target_properties(fido2 PROPERTIES OUTPUT_NAME fido2_static)
66 endif()
67elseif(APPLE)
68 target_link_libraries(fido2 "-framework CoreFoundation"
69 "-framework IOKit")
70endif()
71install(TARGETS fido2 ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
72 LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
73
74# dynamic library
75add_library(fido2_shared SHARED ${FIDO_SOURCES} ${COMPAT_SOURCES})
76target_link_libraries(fido2_shared ${CBOR_LIBRARIES} ${CRYPTO_LIBRARIES}
77 ${UDEV_LIBRARIES} ${BASE_LIBRARIES})
78if(WIN32)
79 if (MINGW)
80 target_link_libraries(fido2_shared wsock32 ws2_32 bcrypt
81 setupapi hid)
82 else()
83 target_link_libraries(fido2_shared wsock32 ws2_32 bcrypt
84 SetupAPI hid)
85 endif()
86elseif(APPLE)
87 target_link_libraries(fido2_shared "-framework CoreFoundation"
88 "-framework IOKit")
89endif()
90set_target_properties(fido2_shared PROPERTIES OUTPUT_NAME fido2
91 VERSION ${LIB_VERSION} SOVERSION ${LIB_SOVERSION})
92install(TARGETS fido2_shared
93 ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
94 LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
95 RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR})
96
97install(FILES fido.h DESTINATION include)
98install(DIRECTORY fido DESTINATION include)
99
100if(NOT WIN32)
101 configure_file(libfido2.pc.in libfido2.pc @ONLY)
102 install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libfido2.pc"
103 DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
104endif()
diff --git a/src/aes256.c b/src/aes256.c
new file mode 100644
index 0000000..767cdb2
--- /dev/null
+++ b/src/aes256.c
@@ -0,0 +1,98 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <openssl/evp.h>
8#include <string.h>
9
10#include "fido.h"
11
12int
13aes256_cbc_enc(const fido_blob_t *key, const fido_blob_t *in, fido_blob_t *out)
14{
15 EVP_CIPHER_CTX *ctx = NULL;
16 unsigned char iv[32];
17 int len;
18 int ok = -1;
19
20 memset(iv, 0, sizeof(iv));
21 out->ptr = NULL;
22 out->len = 0;
23
24 /* sanity check */
25 if (in->len > INT_MAX || (in->len % 16) != 0 ||
26 (out->ptr = calloc(1, in->len)) == NULL) {
27 fido_log_debug("%s: in->len=%zu", __func__, in->len);
28 goto fail;
29 }
30
31 if ((ctx = EVP_CIPHER_CTX_new()) == NULL || key->len != 32 ||
32 !EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key->ptr, iv) ||
33 !EVP_CIPHER_CTX_set_padding(ctx, 0) ||
34 !EVP_EncryptUpdate(ctx, out->ptr, &len, in->ptr, (int)in->len) ||
35 len < 0 || (size_t)len != in->len) {
36 fido_log_debug("%s: EVP_Encrypt", __func__);
37 goto fail;
38 }
39
40 out->len = (size_t)len;
41
42 ok = 0;
43fail:
44 if (ctx != NULL)
45 EVP_CIPHER_CTX_free(ctx);
46
47 if (ok < 0) {
48 free(out->ptr);
49 out->ptr = NULL;
50 out->len = 0;
51 }
52
53 return (ok);
54}
55
56int
57aes256_cbc_dec(const fido_blob_t *key, const fido_blob_t *in, fido_blob_t *out)
58{
59 EVP_CIPHER_CTX *ctx = NULL;
60 unsigned char iv[32];
61 int len;
62 int ok = -1;
63
64 memset(iv, 0, sizeof(iv));
65 out->ptr = NULL;
66 out->len = 0;
67
68 /* sanity check */
69 if (in->len > INT_MAX || (in->len % 16) != 0 ||
70 (out->ptr = calloc(1, in->len)) == NULL) {
71 fido_log_debug("%s: in->len=%zu", __func__, in->len);
72 goto fail;
73 }
74
75 if ((ctx = EVP_CIPHER_CTX_new()) == NULL || key->len != 32 ||
76 !EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key->ptr, iv) ||
77 !EVP_CIPHER_CTX_set_padding(ctx, 0) ||
78 !EVP_DecryptUpdate(ctx, out->ptr, &len, in->ptr, (int)in->len) ||
79 len < 0 || (size_t)len > in->len + 32) {
80 fido_log_debug("%s: EVP_Decrypt", __func__);
81 goto fail;
82 }
83
84 out->len = (size_t)len;
85
86 ok = 0;
87fail:
88 if (ctx != NULL)
89 EVP_CIPHER_CTX_free(ctx);
90
91 if (ok < 0) {
92 free(out->ptr);
93 out->ptr = NULL;
94 out->len = 0;
95 }
96
97 return (ok);
98}
diff --git a/src/assert.c b/src/assert.c
new file mode 100644
index 0000000..a21b308
--- /dev/null
+++ b/src/assert.c
@@ -0,0 +1,1090 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <openssl/ec.h>
8#include <openssl/ecdsa.h>
9#include <openssl/evp.h>
10#include <openssl/sha.h>
11
12#include <string.h>
13#include "fido.h"
14#include "fido/es256.h"
15#include "fido/rs256.h"
16#include "fido/eddsa.h"
17
18static int
19adjust_assert_count(const cbor_item_t *key, const cbor_item_t *val, void *arg)
20{
21 fido_assert_t *assert = arg;
22 uint64_t n;
23
24 /* numberOfCredentials; see section 6.2 */
25 if (cbor_isa_uint(key) == false ||
26 cbor_int_get_width(key) != CBOR_INT_8 ||
27 cbor_get_uint8(key) != 5) {
28 fido_log_debug("%s: cbor_type", __func__);
29 return (0); /* ignore */
30 }
31
32 if (cbor_decode_uint64(val, &n) < 0 || n > SIZE_MAX) {
33 fido_log_debug("%s: cbor_decode_uint64", __func__);
34 return (-1);
35 }
36
37 if (assert->stmt_len != 0 || assert->stmt_cnt != 1 ||
38 (size_t)n < assert->stmt_cnt) {
39 fido_log_debug("%s: stmt_len=%zu, stmt_cnt=%zu, n=%zu",
40 __func__, assert->stmt_len, assert->stmt_cnt, (size_t)n);
41 return (-1);
42 }
43
44 if (fido_assert_set_count(assert, (size_t)n) != FIDO_OK) {
45 fido_log_debug("%s: fido_assert_set_count", __func__);
46 return (-1);
47 }
48
49 assert->stmt_len = 0; /* XXX */
50
51 return (0);
52}
53
54static int
55parse_assert_reply(const cbor_item_t *key, const cbor_item_t *val, void *arg)
56{
57 fido_assert_stmt *stmt = arg;
58
59 if (cbor_isa_uint(key) == false ||
60 cbor_int_get_width(key) != CBOR_INT_8) {
61 fido_log_debug("%s: cbor type", __func__);
62 return (0); /* ignore */
63 }
64
65 switch (cbor_get_uint8(key)) {
66 case 1: /* credential id */
67 return (cbor_decode_cred_id(val, &stmt->id));
68 case 2: /* authdata */
69 return (cbor_decode_assert_authdata(val, &stmt->authdata_cbor,
70 &stmt->authdata, &stmt->authdata_ext,
71 &stmt->hmac_secret_enc));
72 case 3: /* signature */
73 return (fido_blob_decode(val, &stmt->sig));
74 case 4: /* user attributes */
75 return (cbor_decode_user(val, &stmt->user));
76 default: /* ignore */
77 fido_log_debug("%s: cbor type", __func__);
78 return (0);
79 }
80}
81
82static int
83fido_dev_get_assert_tx(fido_dev_t *dev, fido_assert_t *assert,
84 const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin)
85{
86 fido_blob_t f;
87 cbor_item_t *argv[7];
88 int r;
89
90 memset(argv, 0, sizeof(argv));
91 memset(&f, 0, sizeof(f));
92
93 /* do we have everything we need? */
94 if (assert->rp_id == NULL || assert->cdh.ptr == NULL) {
95 fido_log_debug("%s: rp_id=%p, cdh.ptr=%p", __func__,
96 (void *)assert->rp_id, (void *)assert->cdh.ptr);
97 r = FIDO_ERR_INVALID_ARGUMENT;
98 goto fail;
99 }
100
101 if ((argv[0] = cbor_build_string(assert->rp_id)) == NULL ||
102 (argv[1] = fido_blob_encode(&assert->cdh)) == NULL) {
103 fido_log_debug("%s: cbor encode", __func__);
104 r = FIDO_ERR_INTERNAL;
105 goto fail;
106 }
107
108 /* allowed credentials */
109 if (assert->allow_list.len) {
110 const fido_blob_array_t *cl = &assert->allow_list;
111 if ((argv[2] = cbor_encode_pubkey_list(cl)) == NULL) {
112 fido_log_debug("%s: cbor_encode_pubkey_list", __func__);
113 r = FIDO_ERR_INTERNAL;
114 goto fail;
115 }
116 }
117
118 /* hmac-secret extension */
119 if (assert->ext & FIDO_EXT_HMAC_SECRET)
120 if ((argv[3] = cbor_encode_hmac_secret_param(ecdh, pk,
121 &assert->hmac_salt)) == NULL) {
122 fido_log_debug("%s: cbor_encode_hmac_secret_param",
123 __func__);
124 r = FIDO_ERR_INTERNAL;
125 goto fail;
126 }
127
128 /* options */
129 if (assert->up != FIDO_OPT_OMIT || assert->uv != FIDO_OPT_OMIT)
130 if ((argv[4] = cbor_encode_assert_options(assert->up,
131 assert->uv)) == NULL) {
132 fido_log_debug("%s: cbor_encode_assert_options",
133 __func__);
134 r = FIDO_ERR_INTERNAL;
135 goto fail;
136 }
137
138 /* pin authentication */
139 if (pin) {
140 if (pk == NULL || ecdh == NULL) {
141 fido_log_debug("%s: pin=%p, pk=%p, ecdh=%p", __func__,
142 (const void *)pin, (const void *)pk,
143 (const void *)ecdh);
144 r = FIDO_ERR_INVALID_ARGUMENT;
145 goto fail;
146 }
147 if ((r = cbor_add_pin_params(dev, &assert->cdh, pk, ecdh, pin,
148 &argv[5], &argv[6])) != FIDO_OK) {
149 fido_log_debug("%s: cbor_add_pin_params", __func__);
150 goto fail;
151 }
152 }
153
154 /* frame and transmit */
155 if (cbor_build_frame(CTAP_CBOR_ASSERT, argv, 7, &f) < 0 ||
156 fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
157 fido_log_debug("%s: fido_tx", __func__);
158 r = FIDO_ERR_TX;
159 goto fail;
160 }
161
162 r = FIDO_OK;
163fail:
164 cbor_vector_free(argv, nitems(argv));
165 free(f.ptr);
166
167 return (r);
168}
169
170static int
171fido_dev_get_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int ms)
172{
173 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
174 unsigned char reply[2048];
175 int reply_len;
176 int r;
177
178 fido_assert_reset_rx(assert);
179
180 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
181 fido_log_debug("%s: fido_rx", __func__);
182 return (FIDO_ERR_RX);
183 }
184
185 /* start with room for a single assertion */
186 if ((assert->stmt = calloc(1, sizeof(fido_assert_stmt))) == NULL)
187 return (FIDO_ERR_INTERNAL);
188
189 assert->stmt_len = 0;
190 assert->stmt_cnt = 1;
191
192 /* adjust as needed */
193 if ((r = cbor_parse_reply(reply, (size_t)reply_len, assert,
194 adjust_assert_count)) != FIDO_OK) {
195 fido_log_debug("%s: adjust_assert_count", __func__);
196 return (r);
197 }
198
199 /* parse the first assertion */
200 if ((r = cbor_parse_reply(reply, (size_t)reply_len,
201 &assert->stmt[assert->stmt_len], parse_assert_reply)) != FIDO_OK) {
202 fido_log_debug("%s: parse_assert_reply", __func__);
203 return (r);
204 }
205
206 assert->stmt_len++;
207
208 return (FIDO_OK);
209}
210
211static int
212fido_get_next_assert_tx(fido_dev_t *dev)
213{
214 const unsigned char cbor[] = { CTAP_CBOR_NEXT_ASSERT };
215 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
216
217 if (fido_tx(dev, cmd, cbor, sizeof(cbor)) < 0) {
218 fido_log_debug("%s: fido_tx", __func__);
219 return (FIDO_ERR_TX);
220 }
221
222 return (FIDO_OK);
223}
224
225static int
226fido_get_next_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int ms)
227{
228 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
229 unsigned char reply[2048];
230 int reply_len;
231 int r;
232
233 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
234 fido_log_debug("%s: fido_rx", __func__);
235 return (FIDO_ERR_RX);
236 }
237
238 /* sanity check */
239 if (assert->stmt_len >= assert->stmt_cnt) {
240 fido_log_debug("%s: stmt_len=%zu, stmt_cnt=%zu", __func__,
241 assert->stmt_len, assert->stmt_cnt);
242 return (FIDO_ERR_INTERNAL);
243 }
244
245 if ((r = cbor_parse_reply(reply, (size_t)reply_len,
246 &assert->stmt[assert->stmt_len], parse_assert_reply)) != FIDO_OK) {
247 fido_log_debug("%s: parse_assert_reply", __func__);
248 return (r);
249 }
250
251 return (FIDO_OK);
252}
253
254static int
255fido_dev_get_assert_wait(fido_dev_t *dev, fido_assert_t *assert,
256 const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin, int ms)
257{
258 int r;
259
260 if ((r = fido_dev_get_assert_tx(dev, assert, pk, ecdh, pin)) != FIDO_OK ||
261 (r = fido_dev_get_assert_rx(dev, assert, ms)) != FIDO_OK)
262 return (r);
263
264 while (assert->stmt_len < assert->stmt_cnt) {
265 if ((r = fido_get_next_assert_tx(dev)) != FIDO_OK ||
266 (r = fido_get_next_assert_rx(dev, assert, ms)) != FIDO_OK)
267 return (r);
268 assert->stmt_len++;
269 }
270
271 return (FIDO_OK);
272}
273
274static int
275decrypt_hmac_secrets(fido_assert_t *assert, const fido_blob_t *key)
276{
277 for (size_t i = 0; i < assert->stmt_cnt; i++) {
278 fido_assert_stmt *stmt = &assert->stmt[i];
279 if (stmt->hmac_secret_enc.ptr != NULL) {
280 if (aes256_cbc_dec(key, &stmt->hmac_secret_enc,
281 &stmt->hmac_secret) < 0) {
282 fido_log_debug("%s: aes256_cbc_dec %zu",
283 __func__, i);
284 return (-1);
285 }
286 }
287 }
288
289 return (0);
290}
291
292int
293fido_dev_get_assert(fido_dev_t *dev, fido_assert_t *assert, const char *pin)
294{
295 fido_blob_t *ecdh = NULL;
296 es256_pk_t *pk = NULL;
297 int r;
298
299 if (assert->rp_id == NULL || assert->cdh.ptr == NULL) {
300 fido_log_debug("%s: rp_id=%p, cdh.ptr=%p", __func__,
301 (void *)assert->rp_id, (void *)assert->cdh.ptr);
302 return (FIDO_ERR_INVALID_ARGUMENT);
303 }
304
305 if (fido_dev_is_fido2(dev) == false) {
306 if (pin != NULL || assert->ext != 0)
307 return (FIDO_ERR_UNSUPPORTED_OPTION);
308 return (u2f_authenticate(dev, assert, -1));
309 }
310
311 if (pin != NULL || assert->ext != 0) {
312 if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
313 fido_log_debug("%s: fido_do_ecdh", __func__);
314 goto fail;
315 }
316 }
317
318 r = fido_dev_get_assert_wait(dev, assert, pk, ecdh, pin, -1);
319 if (r == FIDO_OK && assert->ext & FIDO_EXT_HMAC_SECRET)
320 if (decrypt_hmac_secrets(assert, ecdh) < 0) {
321 fido_log_debug("%s: decrypt_hmac_secrets", __func__);
322 r = FIDO_ERR_INTERNAL;
323 goto fail;
324 }
325
326fail:
327 es256_pk_free(&pk);
328 fido_blob_free(&ecdh);
329
330 return (r);
331}
332
333int
334fido_check_flags(uint8_t flags, fido_opt_t up, fido_opt_t uv)
335{
336 fido_log_debug("%s: flags=%02x", __func__, flags);
337 fido_log_debug("%s: up=%d, uv=%d", __func__, up, uv);
338
339 if (up == FIDO_OPT_TRUE &&
340 (flags & CTAP_AUTHDATA_USER_PRESENT) == 0) {
341 fido_log_debug("%s: CTAP_AUTHDATA_USER_PRESENT", __func__);
342 return (-1); /* user not present */
343 }
344
345 if (uv == FIDO_OPT_TRUE &&
346 (flags & CTAP_AUTHDATA_USER_VERIFIED) == 0) {
347 fido_log_debug("%s: CTAP_AUTHDATA_USER_VERIFIED", __func__);
348 return (-1); /* user not verified */
349 }
350
351 return (0);
352}
353
354static int
355check_extensions(int authdata_ext, int ext)
356{
357 if (authdata_ext != ext) {
358 fido_log_debug("%s: authdata_ext=0x%x != ext=0x%x", __func__,
359 authdata_ext, ext);
360 return (-1);
361 }
362
363 return (0);
364}
365
366static int
367get_signed_hash(int cose_alg, fido_blob_t *dgst, const fido_blob_t *clientdata,
368 const fido_blob_t *authdata_cbor)
369{
370 cbor_item_t *item = NULL;
371 unsigned char *authdata_ptr = NULL;
372 size_t authdata_len;
373 struct cbor_load_result cbor;
374 SHA256_CTX ctx;
375 int ok = -1;
376
377 if ((item = cbor_load(authdata_cbor->ptr, authdata_cbor->len,
378 &cbor)) == NULL || cbor_isa_bytestring(item) == false ||
379 cbor_bytestring_is_definite(item) == false) {
380 fido_log_debug("%s: authdata", __func__);
381 goto fail;
382 }
383
384 authdata_ptr = cbor_bytestring_handle(item);
385 authdata_len = cbor_bytestring_length(item);
386
387 if (cose_alg != COSE_EDDSA) {
388 if (dgst->len < SHA256_DIGEST_LENGTH || SHA256_Init(&ctx) == 0 ||
389 SHA256_Update(&ctx, authdata_ptr, authdata_len) == 0 ||
390 SHA256_Update(&ctx, clientdata->ptr, clientdata->len) == 0 ||
391 SHA256_Final(dgst->ptr, &ctx) == 0) {
392 fido_log_debug("%s: sha256", __func__);
393 goto fail;
394 }
395 dgst->len = SHA256_DIGEST_LENGTH;
396 } else {
397 if (SIZE_MAX - authdata_len < clientdata->len ||
398 dgst->len < authdata_len + clientdata->len) {
399 fido_log_debug("%s: memcpy", __func__);
400 goto fail;
401 }
402 memcpy(dgst->ptr, authdata_ptr, authdata_len);
403 memcpy(dgst->ptr + authdata_len, clientdata->ptr,
404 clientdata->len);
405 dgst->len = authdata_len + clientdata->len;
406 }
407
408 ok = 0;
409fail:
410 if (item != NULL)
411 cbor_decref(&item);
412
413 return (ok);
414}
415
416int
417fido_verify_sig_es256(const fido_blob_t *dgst, const es256_pk_t *pk,
418 const fido_blob_t *sig)
419{
420 EVP_PKEY *pkey = NULL;
421 EC_KEY *ec = NULL;
422 int ok = -1;
423
424 /* ECDSA_verify needs ints */
425 if (dgst->len > INT_MAX || sig->len > INT_MAX) {
426 fido_log_debug("%s: dgst->len=%zu, sig->len=%zu", __func__,
427 dgst->len, sig->len);
428 return (-1);
429 }
430
431 if ((pkey = es256_pk_to_EVP_PKEY(pk)) == NULL ||
432 (ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL) {
433 fido_log_debug("%s: pk -> ec", __func__);
434 goto fail;
435 }
436
437 if (ECDSA_verify(0, dgst->ptr, (int)dgst->len, sig->ptr,
438 (int)sig->len, ec) != 1) {
439 fido_log_debug("%s: ECDSA_verify", __func__);
440 goto fail;
441 }
442
443 ok = 0;
444fail:
445 if (pkey != NULL)
446 EVP_PKEY_free(pkey);
447
448 return (ok);
449}
450
451int
452fido_verify_sig_rs256(const fido_blob_t *dgst, const rs256_pk_t *pk,
453 const fido_blob_t *sig)
454{
455 EVP_PKEY *pkey = NULL;
456 RSA *rsa = NULL;
457 int ok = -1;
458
459 /* RSA_verify needs unsigned ints */
460 if (dgst->len > UINT_MAX || sig->len > UINT_MAX) {
461 fido_log_debug("%s: dgst->len=%zu, sig->len=%zu", __func__,
462 dgst->len, sig->len);
463 return (-1);
464 }
465
466 if ((pkey = rs256_pk_to_EVP_PKEY(pk)) == NULL ||
467 (rsa = EVP_PKEY_get0_RSA(pkey)) == NULL) {
468 fido_log_debug("%s: pk -> ec", __func__);
469 goto fail;
470 }
471
472 if (RSA_verify(NID_sha256, dgst->ptr, (unsigned int)dgst->len, sig->ptr,
473 (unsigned int)sig->len, rsa) != 1) {
474 fido_log_debug("%s: RSA_verify", __func__);
475 goto fail;
476 }
477
478 ok = 0;
479fail:
480 if (pkey != NULL)
481 EVP_PKEY_free(pkey);
482
483 return (ok);
484}
485
486int
487fido_verify_sig_eddsa(const fido_blob_t *dgst, const eddsa_pk_t *pk,
488 const fido_blob_t *sig)
489{
490 EVP_PKEY *pkey = NULL;
491 EVP_MD_CTX *mdctx = NULL;
492 int ok = -1;
493
494 /* EVP_DigestVerify needs ints */
495 if (dgst->len > INT_MAX || sig->len > INT_MAX) {
496 fido_log_debug("%s: dgst->len=%zu, sig->len=%zu", __func__,
497 dgst->len, sig->len);
498 return (-1);
499 }
500
501 if ((pkey = eddsa_pk_to_EVP_PKEY(pk)) == NULL) {
502 fido_log_debug("%s: pk -> pkey", __func__);
503 goto fail;
504 }
505
506 if ((mdctx = EVP_MD_CTX_new()) == NULL) {
507 fido_log_debug("%s: EVP_MD_CTX_new", __func__);
508 goto fail;
509 }
510
511 if (EVP_DigestVerifyInit(mdctx, NULL, NULL, NULL, pkey) != 1) {
512 fido_log_debug("%s: EVP_DigestVerifyInit", __func__);
513 goto fail;
514 }
515
516 if (EVP_DigestVerify(mdctx, sig->ptr, sig->len, dgst->ptr,
517 dgst->len) != 1) {
518 fido_log_debug("%s: EVP_DigestVerify", __func__);
519 goto fail;
520 }
521
522 ok = 0;
523fail:
524 if (mdctx != NULL)
525 EVP_MD_CTX_free(mdctx);
526
527 if (pkey != NULL)
528 EVP_PKEY_free(pkey);
529
530 return (ok);
531}
532
533int
534fido_assert_verify(const fido_assert_t *assert, size_t idx, int cose_alg,
535 const void *pk)
536{
537 unsigned char buf[1024];
538 fido_blob_t dgst;
539 const fido_assert_stmt *stmt = NULL;
540 int ok = -1;
541 int r;
542
543 dgst.ptr = buf;
544 dgst.len = sizeof(buf);
545
546 if (idx >= assert->stmt_len || pk == NULL) {
547 r = FIDO_ERR_INVALID_ARGUMENT;
548 goto out;
549 }
550
551 stmt = &assert->stmt[idx];
552
553 /* do we have everything we need? */
554 if (assert->cdh.ptr == NULL || assert->rp_id == NULL ||
555 stmt->authdata_cbor.ptr == NULL || stmt->sig.ptr == NULL) {
556 fido_log_debug("%s: cdh=%p, rp_id=%s, authdata=%p, sig=%p",
557 __func__, (void *)assert->cdh.ptr, assert->rp_id,
558 (void *)stmt->authdata_cbor.ptr, (void *)stmt->sig.ptr);
559 r = FIDO_ERR_INVALID_ARGUMENT;
560 goto out;
561 }
562
563 if (fido_check_flags(stmt->authdata.flags, assert->up,
564 assert->uv) < 0) {
565 fido_log_debug("%s: fido_check_flags", __func__);
566 r = FIDO_ERR_INVALID_PARAM;
567 goto out;
568 }
569
570 if (check_extensions(stmt->authdata_ext, assert->ext) < 0) {
571 fido_log_debug("%s: check_extensions", __func__);
572 r = FIDO_ERR_INVALID_PARAM;
573 goto out;
574 }
575
576 if (fido_check_rp_id(assert->rp_id, stmt->authdata.rp_id_hash) != 0) {
577 fido_log_debug("%s: fido_check_rp_id", __func__);
578 r = FIDO_ERR_INVALID_PARAM;
579 goto out;
580 }
581
582 if (get_signed_hash(cose_alg, &dgst, &assert->cdh,
583 &stmt->authdata_cbor) < 0) {
584 fido_log_debug("%s: get_signed_hash", __func__);
585 r = FIDO_ERR_INTERNAL;
586 goto out;
587 }
588
589 switch (cose_alg) {
590 case COSE_ES256:
591 ok = fido_verify_sig_es256(&dgst, pk, &stmt->sig);
592 break;
593 case COSE_RS256:
594 ok = fido_verify_sig_rs256(&dgst, pk, &stmt->sig);
595 break;
596 case COSE_EDDSA:
597 ok = fido_verify_sig_eddsa(&dgst, pk, &stmt->sig);
598 break;
599 default:
600 fido_log_debug("%s: unsupported cose_alg %d", __func__,
601 cose_alg);
602 r = FIDO_ERR_UNSUPPORTED_OPTION;
603 goto out;
604 }
605
606 if (ok < 0)
607 r = FIDO_ERR_INVALID_SIG;
608 else
609 r = FIDO_OK;
610out:
611 explicit_bzero(buf, sizeof(buf));
612
613 return (r);
614}
615
616int
617fido_assert_set_clientdata_hash(fido_assert_t *assert,
618 const unsigned char *hash, size_t hash_len)
619{
620 if (fido_blob_set(&assert->cdh, hash, hash_len) < 0)
621 return (FIDO_ERR_INVALID_ARGUMENT);
622
623 return (FIDO_OK);
624}
625
626int
627fido_assert_set_hmac_salt(fido_assert_t *assert, const unsigned char *salt,
628 size_t salt_len)
629{
630 if ((salt_len != 32 && salt_len != 64) ||
631 fido_blob_set(&assert->hmac_salt, salt, salt_len) < 0)
632 return (FIDO_ERR_INVALID_ARGUMENT);
633
634 return (FIDO_OK);
635}
636
637int
638fido_assert_set_rp(fido_assert_t *assert, const char *id)
639{
640 if (assert->rp_id != NULL) {
641 free(assert->rp_id);
642 assert->rp_id = NULL;
643 }
644
645 if (id == NULL)
646 return (FIDO_ERR_INVALID_ARGUMENT);
647
648 if ((assert->rp_id = strdup(id)) == NULL)
649 return (FIDO_ERR_INTERNAL);
650
651 return (FIDO_OK);
652}
653
654int
655fido_assert_allow_cred(fido_assert_t *assert, const unsigned char *ptr,
656 size_t len)
657{
658 fido_blob_t id;
659 fido_blob_t *list_ptr;
660 int r;
661
662 memset(&id, 0, sizeof(id));
663
664 if (assert->allow_list.len == SIZE_MAX) {
665 r = FIDO_ERR_INVALID_ARGUMENT;
666 goto fail;
667 }
668
669 if (fido_blob_set(&id, ptr, len) < 0 || (list_ptr =
670 recallocarray(assert->allow_list.ptr, assert->allow_list.len,
671 assert->allow_list.len + 1, sizeof(fido_blob_t))) == NULL) {
672 r = FIDO_ERR_INVALID_ARGUMENT;
673 goto fail;
674 }
675
676 list_ptr[assert->allow_list.len++] = id;
677 assert->allow_list.ptr = list_ptr;
678
679 return (FIDO_OK);
680fail:
681 free(id.ptr);
682
683 return (r);
684
685}
686
687int
688fido_assert_set_extensions(fido_assert_t *assert, int ext)
689{
690 if (ext != 0 && ext != FIDO_EXT_HMAC_SECRET)
691 return (FIDO_ERR_INVALID_ARGUMENT);
692
693 assert->ext = ext;
694
695 return (FIDO_OK);
696}
697
698int
699fido_assert_set_options(fido_assert_t *assert, bool up, bool uv)
700{
701 assert->up = up ? FIDO_OPT_TRUE : FIDO_OPT_FALSE;
702 assert->uv = uv ? FIDO_OPT_TRUE : FIDO_OPT_FALSE;
703
704 return (FIDO_OK);
705}
706
707int
708fido_assert_set_up(fido_assert_t *assert, fido_opt_t up)
709{
710 assert->up = up;
711
712 return (FIDO_OK);
713}
714
715int
716fido_assert_set_uv(fido_assert_t *assert, fido_opt_t uv)
717{
718 assert->uv = uv;
719
720 return (FIDO_OK);
721}
722
723const unsigned char *
724fido_assert_clientdata_hash_ptr(const fido_assert_t *assert)
725{
726 return (assert->cdh.ptr);
727}
728
729size_t
730fido_assert_clientdata_hash_len(const fido_assert_t *assert)
731{
732 return (assert->cdh.len);
733}
734
735fido_assert_t *
736fido_assert_new(void)
737{
738 return (calloc(1, sizeof(fido_assert_t)));
739}
740
741void
742fido_assert_reset_tx(fido_assert_t *assert)
743{
744 free(assert->rp_id);
745 free(assert->cdh.ptr);
746 free(assert->hmac_salt.ptr);
747 fido_free_blob_array(&assert->allow_list);
748
749 memset(&assert->cdh, 0, sizeof(assert->cdh));
750 memset(&assert->hmac_salt, 0, sizeof(assert->hmac_salt));
751 memset(&assert->allow_list, 0, sizeof(assert->allow_list));
752
753 assert->rp_id = NULL;
754 assert->up = FIDO_OPT_OMIT;
755 assert->uv = FIDO_OPT_OMIT;
756 assert->ext = 0;
757}
758
759void
760fido_assert_reset_rx(fido_assert_t *assert)
761{
762 for (size_t i = 0; i < assert->stmt_cnt; i++) {
763 free(assert->stmt[i].user.id.ptr);
764 free(assert->stmt[i].user.icon);
765 free(assert->stmt[i].user.name);
766 free(assert->stmt[i].user.display_name);
767 free(assert->stmt[i].id.ptr);
768 if (assert->stmt[i].hmac_secret.ptr != NULL) {
769 explicit_bzero(assert->stmt[i].hmac_secret.ptr,
770 assert->stmt[i].hmac_secret.len);
771 }
772 free(assert->stmt[i].hmac_secret.ptr);
773 free(assert->stmt[i].hmac_secret_enc.ptr);
774 free(assert->stmt[i].authdata_cbor.ptr);
775 free(assert->stmt[i].sig.ptr);
776 memset(&assert->stmt[i], 0, sizeof(assert->stmt[i]));
777 }
778
779 free(assert->stmt);
780
781 assert->stmt = NULL;
782 assert->stmt_len = 0;
783 assert->stmt_cnt = 0;
784}
785
786void
787fido_assert_free(fido_assert_t **assert_p)
788{
789 fido_assert_t *assert;
790
791 if (assert_p == NULL || (assert = *assert_p) == NULL)
792 return;
793
794 fido_assert_reset_tx(assert);
795 fido_assert_reset_rx(assert);
796
797 free(assert);
798
799 *assert_p = NULL;
800}
801
802size_t
803fido_assert_count(const fido_assert_t *assert)
804{
805 return (assert->stmt_len);
806}
807
808const char *
809fido_assert_rp_id(const fido_assert_t *assert)
810{
811 return (assert->rp_id);
812}
813
814uint8_t
815fido_assert_flags(const fido_assert_t *assert, size_t idx)
816{
817 if (idx >= assert->stmt_len)
818 return (0);
819
820 return (assert->stmt[idx].authdata.flags);
821}
822
823uint32_t
824fido_assert_sigcount(const fido_assert_t *assert, size_t idx)
825{
826 if (idx >= assert->stmt_len)
827 return (0);
828
829 return (assert->stmt[idx].authdata.sigcount);
830}
831
832const unsigned char *
833fido_assert_authdata_ptr(const fido_assert_t *assert, size_t idx)
834{
835 if (idx >= assert->stmt_len)
836 return (NULL);
837
838 return (assert->stmt[idx].authdata_cbor.ptr);
839}
840
841size_t
842fido_assert_authdata_len(const fido_assert_t *assert, size_t idx)
843{
844 if (idx >= assert->stmt_len)
845 return (0);
846
847 return (assert->stmt[idx].authdata_cbor.len);
848}
849
850const unsigned char *
851fido_assert_sig_ptr(const fido_assert_t *assert, size_t idx)
852{
853 if (idx >= assert->stmt_len)
854 return (NULL);
855
856 return (assert->stmt[idx].sig.ptr);
857}
858
859size_t
860fido_assert_sig_len(const fido_assert_t *assert, size_t idx)
861{
862 if (idx >= assert->stmt_len)
863 return (0);
864
865 return (assert->stmt[idx].sig.len);
866}
867
868const unsigned char *
869fido_assert_id_ptr(const fido_assert_t *assert, size_t idx)
870{
871 if (idx >= assert->stmt_len)
872 return (NULL);
873
874 return (assert->stmt[idx].id.ptr);
875}
876
877size_t
878fido_assert_id_len(const fido_assert_t *assert, size_t idx)
879{
880 if (idx >= assert->stmt_len)
881 return (0);
882
883 return (assert->stmt[idx].id.len);
884}
885
886const unsigned char *
887fido_assert_user_id_ptr(const fido_assert_t *assert, size_t idx)
888{
889 if (idx >= assert->stmt_len)
890 return (NULL);
891
892 return (assert->stmt[idx].user.id.ptr);
893}
894
895size_t
896fido_assert_user_id_len(const fido_assert_t *assert, size_t idx)
897{
898 if (idx >= assert->stmt_len)
899 return (0);
900
901 return (assert->stmt[idx].user.id.len);
902}
903
904const char *
905fido_assert_user_icon(const fido_assert_t *assert, size_t idx)
906{
907 if (idx >= assert->stmt_len)
908 return (NULL);
909
910 return (assert->stmt[idx].user.icon);
911}
912
913const char *
914fido_assert_user_name(const fido_assert_t *assert, size_t idx)
915{
916 if (idx >= assert->stmt_len)
917 return (NULL);
918
919 return (assert->stmt[idx].user.name);
920}
921
922const char *
923fido_assert_user_display_name(const fido_assert_t *assert, size_t idx)
924{
925 if (idx >= assert->stmt_len)
926 return (NULL);
927
928 return (assert->stmt[idx].user.display_name);
929}
930
931const unsigned char *
932fido_assert_hmac_secret_ptr(const fido_assert_t *assert, size_t idx)
933{
934 if (idx >= assert->stmt_len)
935 return (NULL);
936
937 return (assert->stmt[idx].hmac_secret.ptr);
938}
939
940size_t
941fido_assert_hmac_secret_len(const fido_assert_t *assert, size_t idx)
942{
943 if (idx >= assert->stmt_len)
944 return (0);
945
946 return (assert->stmt[idx].hmac_secret.len);
947}
948
949static void
950fido_assert_clean_authdata(fido_assert_stmt *as)
951{
952 free(as->authdata_cbor.ptr);
953 free(as->hmac_secret_enc.ptr);
954
955 memset(&as->authdata_ext, 0, sizeof(as->authdata_ext));
956 memset(&as->authdata_cbor, 0, sizeof(as->authdata_cbor));
957 memset(&as->authdata, 0, sizeof(as->authdata));
958 memset(&as->hmac_secret_enc, 0, sizeof(as->hmac_secret_enc));
959}
960
961int
962fido_assert_set_authdata(fido_assert_t *assert, size_t idx,
963 const unsigned char *ptr, size_t len)
964{
965 cbor_item_t *item = NULL;
966 fido_assert_stmt *stmt = NULL;
967 struct cbor_load_result cbor;
968 int r;
969
970 if (idx >= assert->stmt_len || ptr == NULL || len == 0)
971 return (FIDO_ERR_INVALID_ARGUMENT);
972
973 stmt = &assert->stmt[idx];
974 fido_assert_clean_authdata(stmt);
975
976 if ((item = cbor_load(ptr, len, &cbor)) == NULL) {
977 fido_log_debug("%s: cbor_load", __func__);
978 r = FIDO_ERR_INVALID_ARGUMENT;
979 goto fail;
980 }
981
982 if (cbor_decode_assert_authdata(item, &stmt->authdata_cbor,
983 &stmt->authdata, &stmt->authdata_ext, &stmt->hmac_secret_enc) < 0) {
984 fido_log_debug("%s: cbor_decode_assert_authdata", __func__);
985 r = FIDO_ERR_INVALID_ARGUMENT;
986 goto fail;
987 }
988
989 r = FIDO_OK;
990fail:
991 if (item != NULL)
992 cbor_decref(&item);
993
994 if (r != FIDO_OK)
995 fido_assert_clean_authdata(stmt);
996
997 return (r);
998}
999
1000int
1001fido_assert_set_authdata_raw(fido_assert_t *assert, size_t idx,
1002 const unsigned char *ptr, size_t len)
1003{
1004 cbor_item_t *item = NULL;
1005 fido_assert_stmt *stmt = NULL;
1006 int r;
1007
1008 if (idx >= assert->stmt_len || ptr == NULL || len == 0)
1009 return (FIDO_ERR_INVALID_ARGUMENT);
1010
1011 stmt = &assert->stmt[idx];
1012 fido_assert_clean_authdata(stmt);
1013
1014 if ((item = cbor_build_bytestring(ptr, len)) == NULL) {
1015 fido_log_debug("%s: cbor_build_bytestring", __func__);
1016 r = FIDO_ERR_INTERNAL;
1017 goto fail;
1018 }
1019
1020 if (cbor_decode_assert_authdata(item, &stmt->authdata_cbor,
1021 &stmt->authdata, &stmt->authdata_ext, &stmt->hmac_secret_enc) < 0) {
1022 fido_log_debug("%s: cbor_decode_assert_authdata", __func__);
1023 r = FIDO_ERR_INVALID_ARGUMENT;
1024 goto fail;
1025 }
1026
1027 r = FIDO_OK;
1028fail:
1029 if (item != NULL)
1030 cbor_decref(&item);
1031
1032 if (r != FIDO_OK)
1033 fido_assert_clean_authdata(stmt);
1034
1035 return (r);
1036}
1037
1038static void
1039fido_assert_clean_sig(fido_assert_stmt *as)
1040{
1041 free(as->sig.ptr);
1042 as->sig.ptr = NULL;
1043 as->sig.len = 0;
1044}
1045
1046int
1047fido_assert_set_sig(fido_assert_t *a, size_t idx, const unsigned char *ptr,
1048 size_t len)
1049{
1050 unsigned char *sig;
1051
1052 if (idx >= a->stmt_len || ptr == NULL || len == 0)
1053 return (FIDO_ERR_INVALID_ARGUMENT);
1054
1055 fido_assert_clean_sig(&a->stmt[idx]);
1056
1057 if ((sig = malloc(len)) == NULL)
1058 return (FIDO_ERR_INTERNAL);
1059
1060 memcpy(sig, ptr, len);
1061 a->stmt[idx].sig.ptr = sig;
1062 a->stmt[idx].sig.len = len;
1063
1064 return (FIDO_OK);
1065}
1066
1067/* XXX shrinking leaks memory; fortunately that shouldn't happen */
1068int
1069fido_assert_set_count(fido_assert_t *assert, size_t n)
1070{
1071 void *new_stmt;
1072
1073#ifdef FIDO_FUZZ
1074 if (n > UINT8_MAX) {
1075 fido_log_debug("%s: n > UINT8_MAX", __func__);
1076 return (FIDO_ERR_INTERNAL);
1077 }
1078#endif
1079
1080 new_stmt = recallocarray(assert->stmt, assert->stmt_cnt, n,
1081 sizeof(fido_assert_stmt));
1082 if (new_stmt == NULL)
1083 return (FIDO_ERR_INTERNAL);
1084
1085 assert->stmt = new_stmt;
1086 assert->stmt_cnt = n;
1087 assert->stmt_len = n;
1088
1089 return (FIDO_OK);
1090}
diff --git a/src/authkey.c b/src/authkey.c
new file mode 100644
index 0000000..9de37f1
--- /dev/null
+++ b/src/authkey.c
@@ -0,0 +1,98 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <string.h>
8#include "fido.h"
9
10static int
11parse_authkey(const cbor_item_t *key, const cbor_item_t *val, void *arg)
12{
13 es256_pk_t *authkey = arg;
14
15 if (cbor_isa_uint(key) == false ||
16 cbor_int_get_width(key) != CBOR_INT_8 ||
17 cbor_get_uint8(key) != 1) {
18 fido_log_debug("%s: cbor type", __func__);
19 return (0); /* ignore */
20 }
21
22 return (es256_pk_decode(val, authkey));
23}
24
25static int
26fido_dev_authkey_tx(fido_dev_t *dev)
27{
28 fido_blob_t f;
29 cbor_item_t *argv[2];
30 int r;
31
32 fido_log_debug("%s: dev=%p", __func__, (void *)dev);
33
34 memset(&f, 0, sizeof(f));
35 memset(argv, 0, sizeof(argv));
36
37 /* add command parameters */
38 if ((argv[0] = cbor_build_uint8(1)) == NULL ||
39 (argv[1] = cbor_build_uint8(2)) == NULL) {
40 fido_log_debug("%s: cbor_build", __func__);
41 r = FIDO_ERR_INTERNAL;
42 goto fail;
43 }
44
45 /* frame and transmit */
46 if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, 2, &f) < 0 ||
47 fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
48 fido_log_debug("%s: fido_tx", __func__);
49 r = FIDO_ERR_TX;
50 goto fail;
51 }
52
53 r = FIDO_OK;
54fail:
55 cbor_vector_free(argv, nitems(argv));
56 free(f.ptr);
57
58 return (r);
59}
60
61static int
62fido_dev_authkey_rx(fido_dev_t *dev, es256_pk_t *authkey, int ms)
63{
64 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
65 unsigned char reply[2048];
66 int reply_len;
67
68 fido_log_debug("%s: dev=%p, authkey=%p, ms=%d", __func__, (void *)dev,
69 (void *)authkey, ms);
70
71 memset(authkey, 0, sizeof(*authkey));
72
73 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
74 fido_log_debug("%s: fido_rx", __func__);
75 return (FIDO_ERR_RX);
76 }
77
78 return (cbor_parse_reply(reply, (size_t)reply_len, authkey,
79 parse_authkey));
80}
81
82static int
83fido_dev_authkey_wait(fido_dev_t *dev, es256_pk_t *authkey, int ms)
84{
85 int r;
86
87 if ((r = fido_dev_authkey_tx(dev)) != FIDO_OK ||
88 (r = fido_dev_authkey_rx(dev, authkey, ms)) != FIDO_OK)
89 return (r);
90
91 return (FIDO_OK);
92}
93
94int
95fido_dev_authkey(fido_dev_t *dev, es256_pk_t *authkey)
96{
97 return (fido_dev_authkey_wait(dev, authkey, -1));
98}
diff --git a/src/bio.c b/src/bio.c
new file mode 100644
index 0000000..74814b9
--- /dev/null
+++ b/src/bio.c
@@ -0,0 +1,844 @@
1/*
2 * Copyright (c) 2019 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <string.h>
8
9#include "fido.h"
10#include "fido/bio.h"
11#include "fido/es256.h"
12
13#define CMD_ENROLL_BEGIN 0x01
14#define CMD_ENROLL_NEXT 0x02
15#define CMD_ENROLL_CANCEL 0x03
16#define CMD_ENUM 0x04
17#define CMD_SET_NAME 0x05
18#define CMD_ENROLL_REMOVE 0x06
19#define CMD_GET_INFO 0x07
20
21static int
22bio_prepare_hmac(uint8_t cmd, cbor_item_t **argv, size_t argc,
23 cbor_item_t **param, fido_blob_t *hmac_data)
24{
25 const uint8_t prefix[2] = { 0x01 /* modality */, cmd };
26 int ok = -1;
27 size_t cbor_alloc_len;
28 size_t cbor_len;
29 unsigned char *cbor = NULL;
30
31 if (argv == NULL || param == NULL)
32 return (fido_blob_set(hmac_data, prefix, sizeof(prefix)));
33
34 if ((*param = cbor_flatten_vector(argv, argc)) == NULL) {
35 fido_log_debug("%s: cbor_flatten_vector", __func__);
36 goto fail;
37 }
38
39 if ((cbor_len = cbor_serialize_alloc(*param, &cbor,
40 &cbor_alloc_len)) == 0 || cbor_len > SIZE_MAX - sizeof(prefix)) {
41 fido_log_debug("%s: cbor_serialize_alloc", __func__);
42 goto fail;
43 }
44
45 if ((hmac_data->ptr = malloc(cbor_len + sizeof(prefix))) == NULL) {
46 fido_log_debug("%s: malloc", __func__);
47 goto fail;
48 }
49
50 memcpy(hmac_data->ptr, prefix, sizeof(prefix));
51 memcpy(hmac_data->ptr + sizeof(prefix), cbor, cbor_len);
52 hmac_data->len = cbor_len + sizeof(prefix);
53
54 ok = 0;
55fail:
56 free(cbor);
57
58 return (ok);
59}
60
61static int
62bio_tx(fido_dev_t *dev, uint8_t cmd, cbor_item_t **sub_argv, size_t sub_argc,
63 const char *pin, const fido_blob_t *token)
64{
65 cbor_item_t *argv[5];
66 es256_pk_t *pk = NULL;
67 fido_blob_t *ecdh = NULL;
68 fido_blob_t f;
69 fido_blob_t hmac;
70 int r = FIDO_ERR_INTERNAL;
71
72 memset(&f, 0, sizeof(f));
73 memset(&hmac, 0, sizeof(hmac));
74 memset(&argv, 0, sizeof(argv));
75
76 /* modality, subCommand */
77 if ((argv[0] = cbor_build_uint8(1)) == NULL ||
78 (argv[1] = cbor_build_uint8(cmd)) == NULL) {
79 fido_log_debug("%s: cbor encode", __func__);
80 goto fail;
81 }
82
83 /* subParams */
84 if (pin || token) {
85 if (bio_prepare_hmac(cmd, sub_argv, sub_argc, &argv[2],
86 &hmac) < 0) {
87 fido_log_debug("%s: bio_prepare_hmac", __func__);
88 goto fail;
89 }
90 }
91
92 /* pinProtocol, pinAuth */
93 if (pin) {
94 if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
95 fido_log_debug("%s: fido_do_ecdh", __func__);
96 goto fail;
97 }
98 if ((r = cbor_add_pin_params(dev, &hmac, pk, ecdh, pin,
99 &argv[4], &argv[3])) != FIDO_OK) {
100 fido_log_debug("%s: cbor_add_pin_params", __func__);
101 goto fail;
102 }
103 } else if (token) {
104 if ((argv[3] = cbor_encode_pin_opt()) == NULL ||
105 (argv[4] = cbor_encode_pin_auth(token, &hmac)) == NULL) {
106 fido_log_debug("%s: encode pin", __func__);
107 goto fail;
108 }
109 }
110
111 /* framing and transmission */
112 if (cbor_build_frame(CTAP_CBOR_BIO_ENROLL_PRE, argv, 5, &f) < 0 ||
113 fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
114 fido_log_debug("%s: fido_tx", __func__);
115 r = FIDO_ERR_TX;
116 goto fail;
117 }
118
119 r = FIDO_OK;
120fail:
121 cbor_vector_free(argv, nitems(argv));
122 es256_pk_free(&pk);
123 fido_blob_free(&ecdh);
124 free(f.ptr);
125 free(hmac.ptr);
126
127 return (r);
128}
129
130static void
131bio_reset_template(fido_bio_template_t *t)
132{
133 free(t->name);
134 free(t->id.ptr);
135 t->name = NULL;
136 memset(&t->id, 0, sizeof(t->id));
137}
138
139static void
140bio_reset_template_array(fido_bio_template_array_t *ta)
141{
142 for (size_t i = 0; i < ta->n_alloc; i++)
143 bio_reset_template(&ta->ptr[i]);
144
145 free(ta->ptr);
146 ta->ptr = NULL;
147 memset(ta, 0, sizeof(*ta));
148}
149
150static int
151decode_template(const cbor_item_t *key, const cbor_item_t *val, void *arg)
152{
153 fido_bio_template_t *t = arg;
154
155 if (cbor_isa_uint(key) == false ||
156 cbor_int_get_width(key) != CBOR_INT_8) {
157 fido_log_debug("%s: cbor type", __func__);
158 return (0); /* ignore */
159 }
160
161 switch (cbor_get_uint8(key)) {
162 case 1: /* id */
163 return (fido_blob_decode(val, &t->id));
164 case 2: /* name */
165 return (cbor_string_copy(val, &t->name));
166 }
167
168 return (0); /* ignore */
169}
170
171static int
172decode_template_array(const cbor_item_t *item, void *arg)
173{
174 fido_bio_template_array_t *ta = arg;
175
176 if (cbor_isa_map(item) == false ||
177 cbor_map_is_definite(item) == false) {
178 fido_log_debug("%s: cbor type", __func__);
179 return (-1);
180 }
181
182 if (ta->n_rx >= ta->n_alloc) {
183 fido_log_debug("%s: n_rx >= n_alloc", __func__);
184 return (-1);
185 }
186
187 if (cbor_map_iter(item, &ta->ptr[ta->n_rx], decode_template) < 0) {
188 fido_log_debug("%s: decode_template", __func__);
189 return (-1);
190 }
191
192 ta->n_rx++;
193
194 return (0);
195}
196
197static int
198bio_parse_template_array(const cbor_item_t *key, const cbor_item_t *val,
199 void *arg)
200{
201 fido_bio_template_array_t *ta = arg;
202
203 if (cbor_isa_uint(key) == false ||
204 cbor_int_get_width(key) != CBOR_INT_8 ||
205 cbor_get_uint8(key) != 7) {
206 fido_log_debug("%s: cbor type", __func__);
207 return (0); /* ignore */
208 }
209
210 if (cbor_isa_array(val) == false ||
211 cbor_array_is_definite(val) == false) {
212 fido_log_debug("%s: cbor type", __func__);
213 return (-1);
214 }
215
216 if (ta->ptr != NULL || ta->n_alloc != 0 || ta->n_rx != 0) {
217 fido_log_debug("%s: ptr != NULL || n_alloc != 0 || n_rx != 0",
218 __func__);
219 return (-1);
220 }
221
222 if ((ta->ptr = calloc(cbor_array_size(val), sizeof(*ta->ptr))) == NULL)
223 return (-1);
224
225 ta->n_alloc = cbor_array_size(val);
226
227 if (cbor_array_iter(val, ta, decode_template_array) < 0) {
228 fido_log_debug("%s: decode_template_array", __func__);
229 return (-1);
230 }
231
232 return (0);
233}
234
235static int
236bio_rx_template_array(fido_dev_t *dev, fido_bio_template_array_t *ta, int ms)
237{
238 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
239 unsigned char reply[2048];
240 int reply_len;
241 int r;
242
243 bio_reset_template_array(ta);
244
245 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
246 fido_log_debug("%s: fido_rx", __func__);
247 return (FIDO_ERR_RX);
248 }
249
250 if ((r = cbor_parse_reply(reply, (size_t)reply_len, ta,
251 bio_parse_template_array)) != FIDO_OK) {
252 fido_log_debug("%s: bio_parse_template_array" , __func__);
253 return (r);
254 }
255
256 return (FIDO_OK);
257}
258
259static int
260bio_get_template_array_wait(fido_dev_t *dev, fido_bio_template_array_t *ta,
261 const char *pin, int ms)
262{
263 int r;
264
265 if ((r = bio_tx(dev, CMD_ENUM, NULL, 0, pin, NULL)) != FIDO_OK ||
266 (r = bio_rx_template_array(dev, ta, ms)) != FIDO_OK)
267 return (r);
268
269 return (FIDO_OK);
270}
271
272int
273fido_bio_dev_get_template_array(fido_dev_t *dev, fido_bio_template_array_t *ta,
274 const char *pin)
275{
276 if (pin == NULL)
277 return (FIDO_ERR_INVALID_ARGUMENT);
278
279 return (bio_get_template_array_wait(dev, ta, pin, -1));
280}
281
282static int
283bio_set_template_name_wait(fido_dev_t *dev, const fido_bio_template_t *t,
284 const char *pin, int ms)
285{
286 cbor_item_t *argv[2];
287 int r = FIDO_ERR_INTERNAL;
288
289 memset(&argv, 0, sizeof(argv));
290
291 if ((argv[0] = fido_blob_encode(&t->id)) == NULL ||
292 (argv[1] = cbor_build_string(t->name)) == NULL) {
293 fido_log_debug("%s: cbor encode", __func__);
294 goto fail;
295 }
296
297 if ((r = bio_tx(dev, CMD_SET_NAME, argv, 2, pin, NULL)) != FIDO_OK ||
298 (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
299 fido_log_debug("%s: tx/rx", __func__);
300 goto fail;
301 }
302
303 r = FIDO_OK;
304fail:
305 cbor_vector_free(argv, nitems(argv));
306
307 return (r);
308}
309
310int
311fido_bio_dev_set_template_name(fido_dev_t *dev, const fido_bio_template_t *t,
312 const char *pin)
313{
314 if (pin == NULL || t->name == NULL)
315 return (FIDO_ERR_INVALID_ARGUMENT);
316
317 return (bio_set_template_name_wait(dev, t, pin, -1));
318}
319
320static void
321bio_reset_enroll(fido_bio_enroll_t *e)
322{
323 e->remaining_samples = 0;
324 e->last_status = 0;
325
326 if (e->token)
327 fido_blob_free(&e->token);
328}
329
330static int
331bio_parse_enroll_status(const cbor_item_t *key, const cbor_item_t *val,
332 void *arg)
333{
334 fido_bio_enroll_t *e = arg;
335 uint64_t x;
336
337 if (cbor_isa_uint(key) == false ||
338 cbor_int_get_width(key) != CBOR_INT_8) {
339 fido_log_debug("%s: cbor type", __func__);
340 return (0); /* ignore */
341 }
342
343 switch (cbor_get_uint8(key)) {
344 case 5:
345 if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) {
346 fido_log_debug("%s: cbor_decode_uint64", __func__);
347 return (-1);
348 }
349 e->last_status = (uint8_t)x;
350 break;
351 case 6:
352 if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) {
353 fido_log_debug("%s: cbor_decode_uint64", __func__);
354 return (-1);
355 }
356 e->remaining_samples = (uint8_t)x;
357 break;
358 default:
359 return (0); /* ignore */
360 }
361
362 return (0);
363}
364
365static int
366bio_parse_template_id(const cbor_item_t *key, const cbor_item_t *val,
367 void *arg)
368{
369 fido_blob_t *id = arg;
370
371 if (cbor_isa_uint(key) == false ||
372 cbor_int_get_width(key) != CBOR_INT_8 ||
373 cbor_get_uint8(key) != 4) {
374 fido_log_debug("%s: cbor type", __func__);
375 return (0); /* ignore */
376 }
377
378 return (fido_blob_decode(val, id));
379}
380
381static int
382bio_rx_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t,
383 fido_bio_enroll_t *e, int ms)
384{
385 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
386 unsigned char reply[2048];
387 int reply_len;
388 int r;
389
390 bio_reset_template(t);
391
392 e->remaining_samples = 0;
393 e->last_status = 0;
394
395 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
396 fido_log_debug("%s: fido_rx", __func__);
397 return (FIDO_ERR_RX);
398 }
399
400 if ((r = cbor_parse_reply(reply, (size_t)reply_len, e,
401 bio_parse_enroll_status)) != FIDO_OK) {
402 fido_log_debug("%s: bio_parse_enroll_status", __func__);
403 return (r);
404 }
405 if ((r = cbor_parse_reply(reply, (size_t)reply_len, &t->id,
406 bio_parse_template_id)) != FIDO_OK) {
407 fido_log_debug("%s: bio_parse_template_id", __func__);
408 return (r);
409 }
410
411 return (FIDO_OK);
412}
413
414static int
415bio_enroll_begin_wait(fido_dev_t *dev, fido_bio_template_t *t,
416 fido_bio_enroll_t *e, uint32_t timo_ms, int ms)
417{
418 cbor_item_t *argv[3];
419 const uint8_t cmd = CMD_ENROLL_BEGIN;
420 int r = FIDO_ERR_INTERNAL;
421
422 memset(&argv, 0, sizeof(argv));
423
424 if ((argv[2] = cbor_build_uint32(timo_ms)) == NULL) {
425 fido_log_debug("%s: cbor encode", __func__);
426 goto fail;
427 }
428
429 if ((r = bio_tx(dev, cmd, argv, 3, NULL, e->token)) != FIDO_OK ||
430 (r = bio_rx_enroll_begin(dev, t, e, ms)) != FIDO_OK) {
431 fido_log_debug("%s: tx/rx", __func__);
432 goto fail;
433 }
434
435 r = FIDO_OK;
436fail:
437 cbor_vector_free(argv, nitems(argv));
438
439 return (r);
440}
441
442int
443fido_bio_dev_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t,
444 fido_bio_enroll_t *e, uint32_t timo_ms, const char *pin)
445{
446 es256_pk_t *pk = NULL;
447 fido_blob_t *ecdh = NULL;
448 fido_blob_t *token = NULL;
449 int r;
450
451 if (pin == NULL || e->token != NULL)
452 return (FIDO_ERR_INVALID_ARGUMENT);
453
454 if ((token = fido_blob_new()) == NULL) {
455 r = FIDO_ERR_INTERNAL;
456 goto fail;
457 }
458
459 if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
460 fido_log_debug("%s: fido_do_ecdh", __func__);
461 goto fail;
462 }
463
464 if ((r = fido_dev_get_pin_token(dev, pin, ecdh, pk, token)) != FIDO_OK) {
465 fido_log_debug("%s: fido_dev_get_pin_token", __func__);
466 goto fail;
467 }
468
469 e->token = token;
470 token = NULL;
471fail:
472 es256_pk_free(&pk);
473 fido_blob_free(&ecdh);
474 fido_blob_free(&token);
475
476 if (r != FIDO_OK)
477 return (r);
478
479 return (bio_enroll_begin_wait(dev, t, e, timo_ms, -1));
480}
481
482static int
483bio_rx_enroll_continue(fido_dev_t *dev, fido_bio_enroll_t *e, int ms)
484{
485 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
486 unsigned char reply[2048];
487 int reply_len;
488 int r;
489
490 e->remaining_samples = 0;
491 e->last_status = 0;
492
493 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
494 fido_log_debug("%s: fido_rx", __func__);
495 return (FIDO_ERR_RX);
496 }
497
498 if ((r = cbor_parse_reply(reply, (size_t)reply_len, e,
499 bio_parse_enroll_status)) != FIDO_OK) {
500 fido_log_debug("%s: bio_parse_enroll_status", __func__);
501 return (r);
502 }
503
504 return (FIDO_OK);
505}
506
507static int
508bio_enroll_continue_wait(fido_dev_t *dev, const fido_bio_template_t *t,
509 fido_bio_enroll_t *e, uint32_t timo_ms, int ms)
510{
511 cbor_item_t *argv[3];
512 const uint8_t cmd = CMD_ENROLL_NEXT;
513 int r = FIDO_ERR_INTERNAL;
514
515 memset(&argv, 0, sizeof(argv));
516
517 if ((argv[0] = fido_blob_encode(&t->id)) == NULL ||
518 (argv[2] = cbor_build_uint32(timo_ms)) == NULL) {
519 fido_log_debug("%s: cbor encode", __func__);
520 goto fail;
521 }
522
523 if ((r = bio_tx(dev, cmd, argv, 3, NULL, e->token)) != FIDO_OK ||
524 (r = bio_rx_enroll_continue(dev, e, ms)) != FIDO_OK) {
525 fido_log_debug("%s: tx/rx", __func__);
526 goto fail;
527 }
528
529 r = FIDO_OK;
530fail:
531 cbor_vector_free(argv, nitems(argv));
532
533 return (r);
534}
535
536int
537fido_bio_dev_enroll_continue(fido_dev_t *dev, const fido_bio_template_t *t,
538 fido_bio_enroll_t *e, uint32_t timo_ms)
539{
540 if (e->token == NULL)
541 return (FIDO_ERR_INVALID_ARGUMENT);
542
543 return (bio_enroll_continue_wait(dev, t, e, timo_ms, -1));
544}
545
546static int
547bio_enroll_cancel_wait(fido_dev_t *dev, int ms)
548{
549 const uint8_t cmd = CMD_ENROLL_CANCEL;
550 int r;
551
552 if ((r = bio_tx(dev, cmd, NULL, 0, NULL, NULL)) != FIDO_OK ||
553 (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
554 fido_log_debug("%s: tx/rx", __func__);
555 return (r);
556 }
557
558 return (FIDO_OK);
559}
560
561int
562fido_bio_dev_enroll_cancel(fido_dev_t *dev)
563{
564 return (bio_enroll_cancel_wait(dev, -1));
565}
566
567static int
568bio_enroll_remove_wait(fido_dev_t *dev, const fido_bio_template_t *t,
569 const char *pin, int ms)
570{
571 cbor_item_t *argv[1];
572 const uint8_t cmd = CMD_ENROLL_REMOVE;
573 int r = FIDO_ERR_INTERNAL;
574
575 memset(&argv, 0, sizeof(argv));
576
577 if ((argv[0] = fido_blob_encode(&t->id)) == NULL) {
578 fido_log_debug("%s: cbor encode", __func__);
579 goto fail;
580 }
581
582 if ((r = bio_tx(dev, cmd, argv, 1, pin, NULL)) != FIDO_OK ||
583 (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
584 fido_log_debug("%s: tx/rx", __func__);
585 goto fail;
586 }
587
588 r = FIDO_OK;
589fail:
590 cbor_vector_free(argv, nitems(argv));
591
592 return (r);
593}
594
595int
596fido_bio_dev_enroll_remove(fido_dev_t *dev, const fido_bio_template_t *t,
597 const char *pin)
598{
599 return (bio_enroll_remove_wait(dev, t, pin, -1));
600}
601
602static void
603bio_reset_info(fido_bio_info_t *i)
604{
605 i->type = 0;
606 i->max_samples = 0;
607}
608
609static int
610bio_parse_info(const cbor_item_t *key, const cbor_item_t *val, void *arg)
611{
612 fido_bio_info_t *i = arg;
613 uint64_t x;
614
615 if (cbor_isa_uint(key) == false ||
616 cbor_int_get_width(key) != CBOR_INT_8) {
617 fido_log_debug("%s: cbor type", __func__);
618 return (0); /* ignore */
619 }
620
621 switch (cbor_get_uint8(key)) {
622 case 2:
623 if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) {
624 fido_log_debug("%s: cbor_decode_uint64", __func__);
625 return (-1);
626 }
627 i->type = (uint8_t)x;
628 break;
629 case 3:
630 if (cbor_decode_uint64(val, &x) < 0 || x > UINT8_MAX) {
631 fido_log_debug("%s: cbor_decode_uint64", __func__);
632 return (-1);
633 }
634 i->max_samples = (uint8_t)x;
635 break;
636 default:
637 return (0); /* ignore */
638 }
639
640 return (0);
641}
642
643static int
644bio_rx_info(fido_dev_t *dev, fido_bio_info_t *i, int ms)
645{
646 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
647 unsigned char reply[2048];
648 int reply_len;
649 int r;
650
651 bio_reset_info(i);
652
653 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
654 fido_log_debug("%s: fido_rx", __func__);
655 return (FIDO_ERR_RX);
656 }
657
658 if ((r = cbor_parse_reply(reply, (size_t)reply_len, i,
659 bio_parse_info)) != FIDO_OK) {
660 fido_log_debug("%s: bio_parse_info" , __func__);
661 return (r);
662 }
663
664 return (FIDO_OK);
665}
666
667static int
668bio_get_info_wait(fido_dev_t *dev, fido_bio_info_t *i, int ms)
669{
670 int r;
671
672 if ((r = bio_tx(dev, CMD_GET_INFO, NULL, 0, NULL, NULL)) != FIDO_OK ||
673 (r = bio_rx_info(dev, i, ms)) != FIDO_OK) {
674 fido_log_debug("%s: tx/rx", __func__);
675 return (r);
676 }
677
678 return (FIDO_OK);
679}
680
681int
682fido_bio_dev_get_info(fido_dev_t *dev, fido_bio_info_t *i)
683{
684 return (bio_get_info_wait(dev, i, -1));
685}
686
687const char *
688fido_bio_template_name(const fido_bio_template_t *t)
689{
690 return (t->name);
691}
692
693const unsigned char *
694fido_bio_template_id_ptr(const fido_bio_template_t *t)
695{
696 return (t->id.ptr);
697}
698
699size_t
700fido_bio_template_id_len(const fido_bio_template_t *t)
701{
702 return (t->id.len);
703}
704
705size_t
706fido_bio_template_array_count(const fido_bio_template_array_t *ta)
707{
708 return (ta->n_rx);
709}
710
711fido_bio_template_array_t *
712fido_bio_template_array_new(void)
713{
714 return (calloc(1, sizeof(fido_bio_template_array_t)));
715}
716
717fido_bio_template_t *
718fido_bio_template_new(void)
719{
720 return (calloc(1, sizeof(fido_bio_template_t)));
721}
722
723void
724fido_bio_template_array_free(fido_bio_template_array_t **tap)
725{
726 fido_bio_template_array_t *ta;
727
728 if (tap == NULL || (ta = *tap) == NULL)
729 return;
730
731 bio_reset_template_array(ta);
732 free(ta);
733 *tap = NULL;
734}
735
736void
737fido_bio_template_free(fido_bio_template_t **tp)
738{
739 fido_bio_template_t *t;
740
741 if (tp == NULL || (t = *tp) == NULL)
742 return;
743
744 bio_reset_template(t);
745 free(t);
746 *tp = NULL;
747}
748
749int
750fido_bio_template_set_name(fido_bio_template_t *t, const char *name)
751{
752 free(t->name);
753 t->name = NULL;
754
755 if (name && (t->name = strdup(name)) == NULL)
756 return (FIDO_ERR_INTERNAL);
757
758 return (FIDO_OK);
759}
760
761int
762fido_bio_template_set_id(fido_bio_template_t *t, const unsigned char *ptr,
763 size_t len)
764{
765 free(t->id.ptr);
766 t->id.ptr = NULL;
767 t->id.len = 0;
768
769 if (ptr && fido_blob_set(&t->id, ptr, len) < 0)
770 return (FIDO_ERR_INTERNAL);
771
772 return (FIDO_OK);
773}
774
775const fido_bio_template_t *
776fido_bio_template(const fido_bio_template_array_t *ta, size_t idx)
777{
778 if (idx >= ta->n_alloc)
779 return (NULL);
780
781 return (&ta->ptr[idx]);
782}
783
784fido_bio_enroll_t *
785fido_bio_enroll_new(void)
786{
787 return (calloc(1, sizeof(fido_bio_enroll_t)));
788}
789
790fido_bio_info_t *
791fido_bio_info_new(void)
792{
793 return (calloc(1, sizeof(fido_bio_info_t)));
794}
795
796uint8_t
797fido_bio_info_type(const fido_bio_info_t *i)
798{
799 return (i->type);
800}
801
802uint8_t
803fido_bio_info_max_samples(const fido_bio_info_t *i)
804{
805 return (i->max_samples);
806}
807
808void
809fido_bio_enroll_free(fido_bio_enroll_t **ep)
810{
811 fido_bio_enroll_t *e;
812
813 if (ep == NULL || (e = *ep) == NULL)
814 return;
815
816 bio_reset_enroll(e);
817
818 free(e);
819 *ep = NULL;
820}
821
822void
823fido_bio_info_free(fido_bio_info_t **ip)
824{
825 fido_bio_info_t *i;
826
827 if (ip == NULL || (i = *ip) == NULL)
828 return;
829
830 free(i);
831 *ip = NULL;
832}
833
834uint8_t
835fido_bio_enroll_remaining_samples(const fido_bio_enroll_t *e)
836{
837 return (e->remaining_samples);
838}
839
840uint8_t
841fido_bio_enroll_last_status(const fido_bio_enroll_t *e)
842{
843 return (e->last_status);
844}
diff --git a/src/blob.c b/src/blob.c
new file mode 100644
index 0000000..d4eae70
--- /dev/null
+++ b/src/blob.c
@@ -0,0 +1,102 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <string.h>
8#include "fido.h"
9
10fido_blob_t *
11fido_blob_new(void)
12{
13 return (calloc(1, sizeof(fido_blob_t)));
14}
15
16int
17fido_blob_set(fido_blob_t *b, const unsigned char *ptr, size_t len)
18{
19 if (b->ptr != NULL) {
20 explicit_bzero(b->ptr, b->len);
21 free(b->ptr);
22 b->ptr = NULL;
23 }
24
25 b->len = 0;
26
27 if (ptr == NULL || len == 0) {
28 fido_log_debug("%s: ptr=%p, len=%zu", __func__,
29 (const void *)ptr, len);
30 return (-1);
31 }
32
33 if ((b->ptr = malloc(len)) == NULL) {
34 fido_log_debug("%s: malloc", __func__);
35 return (-1);
36 }
37
38 memcpy(b->ptr, ptr, len);
39 b->len = len;
40
41 return (0);
42}
43
44void
45fido_blob_free(fido_blob_t **bp)
46{
47 fido_blob_t *b;
48
49 if (bp == NULL || (b = *bp) == NULL)
50 return;
51
52 if (b->ptr) {
53 explicit_bzero(b->ptr, b->len);
54 free(b->ptr);
55 }
56
57 explicit_bzero(b, sizeof(*b));
58 free(b);
59
60 *bp = NULL;
61}
62
63void
64fido_free_blob_array(fido_blob_array_t *array)
65{
66 if (array->ptr == NULL)
67 return;
68
69 for (size_t i = 0; i < array->len; i++) {
70 fido_blob_t *b = &array->ptr[i];
71 if (b->ptr != NULL) {
72 explicit_bzero(b->ptr, b->len);
73 free(b->ptr);
74 b->ptr = NULL;
75 }
76 }
77
78 free(array->ptr);
79 array->ptr = NULL;
80 array->len = 0;
81}
82
83cbor_item_t *
84fido_blob_encode(const fido_blob_t *b)
85{
86 if (b == NULL || b->ptr == NULL)
87 return (NULL);
88
89 return (cbor_build_bytestring(b->ptr, b->len));
90}
91
92int
93fido_blob_decode(const cbor_item_t *item, fido_blob_t *b)
94{
95 return (cbor_bytestring_copy(item, &b->ptr, &b->len));
96}
97
98int
99fido_blob_is_empty(const fido_blob_t *b)
100{
101 return (b->ptr == NULL || b->len == 0);
102}
diff --git a/src/blob.h b/src/blob.h
new file mode 100644
index 0000000..24fdc23
--- /dev/null
+++ b/src/blob.h
@@ -0,0 +1,28 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#ifndef _BLOB_H
8#define _BLOB_H
9
10typedef struct fido_blob {
11 unsigned char *ptr;
12 size_t len;
13} fido_blob_t;
14
15typedef struct fido_blob_array {
16 fido_blob_t *ptr;
17 size_t len;
18} fido_blob_array_t;
19
20cbor_item_t *fido_blob_encode(const fido_blob_t *);
21fido_blob_t *fido_blob_new(void);
22int fido_blob_decode(const cbor_item_t *, fido_blob_t *);
23int fido_blob_is_empty(const fido_blob_t *);
24int fido_blob_set(fido_blob_t *, const unsigned char *, size_t);
25void fido_blob_free(fido_blob_t **);
26void fido_free_blob_array(fido_blob_array_t *);
27
28#endif /* !_BLOB_H */
diff --git a/src/buf.c b/src/buf.c
new file mode 100644
index 0000000..4646476
--- /dev/null
+++ b/src/buf.c
@@ -0,0 +1,34 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <string.h>
8#include "fido.h"
9
10int
11fido_buf_read(const unsigned char **buf, size_t *len, void *dst, size_t count)
12{
13 if (count > *len)
14 return (-1);
15
16 memcpy(dst, *buf, count);
17 *buf += count;
18 *len -= count;
19
20 return (0);
21}
22
23int
24fido_buf_write(unsigned char **buf, size_t *len, const void *src, size_t count)
25{
26 if (count > *len)
27 return (-1);
28
29 memcpy(*buf, src, count);
30 *buf += count;
31 *len -= count;
32
33 return (0);
34}
diff --git a/src/cbor.c b/src/cbor.c
new file mode 100644
index 0000000..3e03592
--- /dev/null
+++ b/src/cbor.c
@@ -0,0 +1,1520 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <openssl/evp.h>
8#include <openssl/hmac.h>
9#include <openssl/sha.h>
10
11#include <string.h>
12#include "fido.h"
13
14static int
15check_key_type(cbor_item_t *item)
16{
17 if (item->type == CBOR_TYPE_UINT || item->type == CBOR_TYPE_NEGINT ||
18 item->type == CBOR_TYPE_STRING)
19 return (0);
20
21 fido_log_debug("%s: invalid type: %d", __func__, item->type);
22
23 return (-1);
24}
25
26/*
27 * Validate CTAP2 canonical CBOR encoding rules for maps.
28 */
29static int
30ctap_check_cbor(cbor_item_t *prev, cbor_item_t *curr)
31{
32 size_t curr_len;
33 size_t prev_len;
34
35 if (check_key_type(prev) < 0 || check_key_type(curr) < 0)
36 return (-1);
37
38 if (prev->type != curr->type) {
39 if (prev->type < curr->type)
40 return (0);
41 fido_log_debug("%s: unsorted types", __func__);
42 return (-1);
43 }
44
45 if (curr->type == CBOR_TYPE_UINT || curr->type == CBOR_TYPE_NEGINT) {
46 if (cbor_int_get_width(curr) >= cbor_int_get_width(prev) &&
47 cbor_get_int(curr) > cbor_get_int(prev))
48 return (0);
49 } else {
50 curr_len = cbor_string_length(curr);
51 prev_len = cbor_string_length(prev);
52
53 if (curr_len > prev_len || (curr_len == prev_len &&
54 memcmp(cbor_string_handle(prev), cbor_string_handle(curr),
55 curr_len) < 0))
56 return (0);
57 }
58
59 fido_log_debug("%s: invalid cbor", __func__);
60
61 return (-1);
62}
63
64int
65cbor_map_iter(const cbor_item_t *item, void *arg, int(*f)(const cbor_item_t *,
66 const cbor_item_t *, void *))
67{
68 struct cbor_pair *v;
69 size_t n;
70
71 if ((v = cbor_map_handle(item)) == NULL) {
72 fido_log_debug("%s: cbor_map_handle", __func__);
73 return (-1);
74 }
75
76 n = cbor_map_size(item);
77
78 for (size_t i = 0; i < n; i++) {
79 if (v[i].key == NULL || v[i].value == NULL) {
80 fido_log_debug("%s: key=%p, value=%p for i=%zu",
81 __func__, (void *)v[i].key, (void *)v[i].value, i);
82 return (-1);
83 }
84 if (i && ctap_check_cbor(v[i - 1].key, v[i].key) < 0) {
85 fido_log_debug("%s: ctap_check_cbor", __func__);
86 return (-1);
87 }
88 if (f(v[i].key, v[i].value, arg) < 0) {
89 fido_log_debug("%s: iterator < 0 on i=%zu", __func__,
90 i);
91 return (-1);
92 }
93 }
94
95 return (0);
96}
97
98int
99cbor_array_iter(const cbor_item_t *item, void *arg, int(*f)(const cbor_item_t *,
100 void *))
101{
102 cbor_item_t **v;
103 size_t n;
104
105 if ((v = cbor_array_handle(item)) == NULL) {
106 fido_log_debug("%s: cbor_array_handle", __func__);
107 return (-1);
108 }
109
110 n = cbor_array_size(item);
111
112 for (size_t i = 0; i < n; i++)
113 if (v[i] == NULL || f(v[i], arg) < 0) {
114 fido_log_debug("%s: iterator < 0 on i=%zu,%p",
115 __func__, i, (void *)v[i]);
116 return (-1);
117 }
118
119 return (0);
120}
121
122int
123cbor_parse_reply(const unsigned char *blob, size_t blob_len, void *arg,
124 int(*parser)(const cbor_item_t *, const cbor_item_t *, void *))
125{
126 cbor_item_t *item = NULL;
127 struct cbor_load_result cbor;
128 int r;
129
130 if (blob_len < 1) {
131 fido_log_debug("%s: blob_len=%zu", __func__, blob_len);
132 r = FIDO_ERR_RX;
133 goto fail;
134 }
135
136 if (blob[0] != FIDO_OK) {
137 fido_log_debug("%s: blob[0]=0x%02x", __func__, blob[0]);
138 r = blob[0];
139 goto fail;
140 }
141
142 if ((item = cbor_load(blob + 1, blob_len - 1, &cbor)) == NULL) {
143 fido_log_debug("%s: cbor_load", __func__);
144 r = FIDO_ERR_RX_NOT_CBOR;
145 goto fail;
146 }
147
148 if (cbor_isa_map(item) == false ||
149 cbor_map_is_definite(item) == false) {
150 fido_log_debug("%s: cbor type", __func__);
151 r = FIDO_ERR_RX_INVALID_CBOR;
152 goto fail;
153 }
154
155 if (cbor_map_iter(item, arg, parser) < 0) {
156 fido_log_debug("%s: cbor_map_iter", __func__);
157 r = FIDO_ERR_RX_INVALID_CBOR;
158 goto fail;
159 }
160
161 r = FIDO_OK;
162fail:
163 if (item != NULL)
164 cbor_decref(&item);
165
166 return (r);
167}
168
169void
170cbor_vector_free(cbor_item_t **item, size_t len)
171{
172 for (size_t i = 0; i < len; i++)
173 if (item[i] != NULL)
174 cbor_decref(&item[i]);
175}
176
177int
178cbor_bytestring_copy(const cbor_item_t *item, unsigned char **buf, size_t *len)
179{
180 if (*buf != NULL || *len != 0) {
181 fido_log_debug("%s: dup", __func__);
182 return (-1);
183 }
184
185 if (cbor_isa_bytestring(item) == false ||
186 cbor_bytestring_is_definite(item) == false) {
187 fido_log_debug("%s: cbor type", __func__);
188 return (-1);
189 }
190
191 *len = cbor_bytestring_length(item);
192 if ((*buf = malloc(*len)) == NULL) {
193 *len = 0;
194 return (-1);
195 }
196
197 memcpy(*buf, cbor_bytestring_handle(item), *len);
198
199 return (0);
200}
201
202int
203cbor_string_copy(const cbor_item_t *item, char **str)
204{
205 size_t len;
206
207 if (*str != NULL) {
208 fido_log_debug("%s: dup", __func__);
209 return (-1);
210 }
211
212 if (cbor_isa_string(item) == false ||
213 cbor_string_is_definite(item) == false) {
214 fido_log_debug("%s: cbor type", __func__);
215 return (-1);
216 }
217
218 if ((len = cbor_string_length(item)) == SIZE_MAX ||
219 (*str = malloc(len + 1)) == NULL)
220 return (-1);
221
222 memcpy(*str, cbor_string_handle(item), len);
223 (*str)[len] = '\0';
224
225 return (0);
226}
227
228int
229cbor_add_bytestring(cbor_item_t *item, const char *key,
230 const unsigned char *value, size_t value_len)
231{
232 struct cbor_pair pair;
233 int ok = -1;
234
235 memset(&pair, 0, sizeof(pair));
236
237 if ((pair.key = cbor_build_string(key)) == NULL ||
238 (pair.value = cbor_build_bytestring(value, value_len)) == NULL) {
239 fido_log_debug("%s: cbor_build", __func__);
240 goto fail;
241 }
242
243 if (!cbor_map_add(item, pair)) {
244 fido_log_debug("%s: cbor_map_add", __func__);
245 goto fail;
246 }
247
248 ok = 0;
249fail:
250 if (pair.key)
251 cbor_decref(&pair.key);
252 if (pair.value)
253 cbor_decref(&pair.value);
254
255 return (ok);
256}
257
258int
259cbor_add_string(cbor_item_t *item, const char *key, const char *value)
260{
261 struct cbor_pair pair;
262 int ok = -1;
263
264 memset(&pair, 0, sizeof(pair));
265
266 if ((pair.key = cbor_build_string(key)) == NULL ||
267 (pair.value = cbor_build_string(value)) == NULL) {
268 fido_log_debug("%s: cbor_build", __func__);
269 goto fail;
270 }
271
272 if (!cbor_map_add(item, pair)) {
273 fido_log_debug("%s: cbor_map_add", __func__);
274 goto fail;
275 }
276
277 ok = 0;
278fail:
279 if (pair.key)
280 cbor_decref(&pair.key);
281 if (pair.value)
282 cbor_decref(&pair.value);
283
284 return (ok);
285}
286
287int
288cbor_add_bool(cbor_item_t *item, const char *key, fido_opt_t value)
289{
290 struct cbor_pair pair;
291 int ok = -1;
292
293 memset(&pair, 0, sizeof(pair));
294
295 if ((pair.key = cbor_build_string(key)) == NULL ||
296 (pair.value = cbor_build_bool(value == FIDO_OPT_TRUE)) == NULL) {
297 fido_log_debug("%s: cbor_build", __func__);
298 goto fail;
299 }
300
301 if (!cbor_map_add(item, pair)) {
302 fido_log_debug("%s: cbor_map_add", __func__);
303 goto fail;
304 }
305
306 ok = 0;
307fail:
308 if (pair.key)
309 cbor_decref(&pair.key);
310 if (pair.value)
311 cbor_decref(&pair.value);
312
313 return (ok);
314}
315
316static int
317cbor_add_arg(cbor_item_t *item, uint8_t n, cbor_item_t *arg)
318{
319 struct cbor_pair pair;
320 int ok = -1;
321
322 memset(&pair, 0, sizeof(pair));
323
324 if (arg == NULL)
325 return (0); /* empty argument */
326
327 if ((pair.key = cbor_build_uint8(n)) == NULL) {
328 fido_log_debug("%s: cbor_build", __func__);
329 goto fail;
330 }
331
332 pair.value = arg;
333
334 if (!cbor_map_add(item, pair)) {
335 fido_log_debug("%s: cbor_map_add", __func__);
336 goto fail;
337 }
338
339 ok = 0;
340fail:
341 if (pair.key)
342 cbor_decref(&pair.key);
343
344 return (ok);
345}
346
347cbor_item_t *
348cbor_flatten_vector(cbor_item_t *argv[], size_t argc)
349{
350 cbor_item_t *map;
351 uint8_t i;
352
353 if (argc > UINT8_MAX - 1)
354 return (NULL);
355
356 if ((map = cbor_new_definite_map(argc)) == NULL)
357 return (NULL);
358
359 for (i = 0; i < argc; i++)
360 if (cbor_add_arg(map, i + 1, argv[i]) < 0)
361 break;
362
363 if (i != argc) {
364 cbor_decref(&map);
365 map = NULL;
366 }
367
368 return (map);
369}
370
371int
372cbor_build_frame(uint8_t cmd, cbor_item_t *argv[], size_t argc, fido_blob_t *f)
373{
374 cbor_item_t *flat = NULL;
375 unsigned char *cbor = NULL;
376 size_t cbor_len;
377 size_t cbor_alloc_len;
378 int ok = -1;
379
380 if ((flat = cbor_flatten_vector(argv, argc)) == NULL)
381 goto fail;
382
383 cbor_len = cbor_serialize_alloc(flat, &cbor, &cbor_alloc_len);
384 if (cbor_len == 0 || cbor_len == SIZE_MAX) {
385 fido_log_debug("%s: cbor_len=%zu", __func__, cbor_len);
386 goto fail;
387 }
388
389 if ((f->ptr = malloc(cbor_len + 1)) == NULL)
390 goto fail;
391
392 f->len = cbor_len + 1;
393 f->ptr[0] = cmd;
394 memcpy(f->ptr + 1, cbor, f->len - 1);
395
396 ok = 0;
397fail:
398 if (flat != NULL)
399 cbor_decref(&flat);
400
401 free(cbor);
402
403 return (ok);
404}
405
406cbor_item_t *
407cbor_encode_rp_entity(const fido_rp_t *rp)
408{
409 cbor_item_t *item = NULL;
410
411 if ((item = cbor_new_definite_map(2)) == NULL)
412 return (NULL);
413
414 if ((rp->id && cbor_add_string(item, "id", rp->id) < 0) ||
415 (rp->name && cbor_add_string(item, "name", rp->name) < 0)) {
416 cbor_decref(&item);
417 return (NULL);
418 }
419
420 return (item);
421}
422
423cbor_item_t *
424cbor_encode_user_entity(const fido_user_t *user)
425{
426 cbor_item_t *item = NULL;
427 const fido_blob_t *id = &user->id;
428 const char *display = user->display_name;
429
430 if ((item = cbor_new_definite_map(4)) == NULL)
431 return (NULL);
432
433 if ((id->ptr && cbor_add_bytestring(item, "id", id->ptr, id->len) < 0) ||
434 (user->icon && cbor_add_string(item, "icon", user->icon) < 0) ||
435 (user->name && cbor_add_string(item, "name", user->name) < 0) ||
436 (display && cbor_add_string(item, "displayName", display) < 0)) {
437 cbor_decref(&item);
438 return (NULL);
439 }
440
441 return (item);
442}
443
444cbor_item_t *
445cbor_encode_pubkey_param(int cose_alg)
446{
447 cbor_item_t *item = NULL;
448 cbor_item_t *body = NULL;
449 struct cbor_pair alg;
450 int ok = -1;
451
452 memset(&alg, 0, sizeof(alg));
453
454 if ((item = cbor_new_definite_array(1)) == NULL ||
455 (body = cbor_new_definite_map(2)) == NULL ||
456 cose_alg > -1 || cose_alg < INT16_MIN)
457 goto fail;
458
459 alg.key = cbor_build_string("alg");
460
461 if (-cose_alg - 1 > UINT8_MAX)
462 alg.value = cbor_build_negint16((uint16_t)(-cose_alg - 1));
463 else
464 alg.value = cbor_build_negint8((uint8_t)(-cose_alg - 1));
465
466 if (alg.key == NULL || alg.value == NULL) {
467 fido_log_debug("%s: cbor_build", __func__);
468 goto fail;
469 }
470
471 if (cbor_map_add(body, alg) == false ||
472 cbor_add_string(body, "type", "public-key") < 0 ||
473 cbor_array_push(item, body) == false)
474 goto fail;
475
476 ok = 0;
477fail:
478 if (ok < 0) {
479 if (item != NULL) {
480 cbor_decref(&item);
481 item = NULL;
482 }
483 }
484
485 if (body != NULL)
486 cbor_decref(&body);
487 if (alg.key != NULL)
488 cbor_decref(&alg.key);
489 if (alg.value != NULL)
490 cbor_decref(&alg.value);
491
492 return (item);
493}
494
495cbor_item_t *
496cbor_encode_pubkey(const fido_blob_t *pubkey)
497{
498 cbor_item_t *cbor_key = NULL;
499
500 if ((cbor_key = cbor_new_definite_map(2)) == NULL ||
501 cbor_add_bytestring(cbor_key, "id", pubkey->ptr, pubkey->len) < 0 ||
502 cbor_add_string(cbor_key, "type", "public-key") < 0) {
503 if (cbor_key)
504 cbor_decref(&cbor_key);
505 return (NULL);
506 }
507
508 return (cbor_key);
509}
510
511cbor_item_t *
512cbor_encode_pubkey_list(const fido_blob_array_t *list)
513{
514 cbor_item_t *array = NULL;
515 cbor_item_t *key = NULL;
516
517 if ((array = cbor_new_definite_array(list->len)) == NULL)
518 goto fail;
519
520 for (size_t i = 0; i < list->len; i++) {
521 if ((key = cbor_encode_pubkey(&list->ptr[i])) == NULL ||
522 cbor_array_push(array, key) == false)
523 goto fail;
524 cbor_decref(&key);
525 }
526
527 return (array);
528fail:
529 if (key != NULL)
530 cbor_decref(&key);
531 if (array != NULL)
532 cbor_decref(&array);
533
534 return (NULL);
535}
536
537cbor_item_t *
538cbor_encode_extensions(int ext)
539{
540 cbor_item_t *item = NULL;
541
542 if (ext == 0 || ext != FIDO_EXT_HMAC_SECRET)
543 return (NULL);
544
545 if ((item = cbor_new_definite_map(1)) == NULL)
546 return (NULL);
547
548 if (cbor_add_bool(item, "hmac-secret", FIDO_OPT_TRUE) < 0) {
549 cbor_decref(&item);
550 return (NULL);
551 }
552
553 return (item);
554}
555
556cbor_item_t *
557cbor_encode_options(fido_opt_t rk, fido_opt_t uv)
558{
559 cbor_item_t *item = NULL;
560
561 if ((item = cbor_new_definite_map(2)) == NULL)
562 return (NULL);
563
564 if ((rk != FIDO_OPT_OMIT && cbor_add_bool(item, "rk", rk) < 0) ||
565 (uv != FIDO_OPT_OMIT && cbor_add_bool(item, "uv", uv) < 0)) {
566 cbor_decref(&item);
567 return (NULL);
568 }
569
570 return (item);
571}
572
573cbor_item_t *
574cbor_encode_assert_options(fido_opt_t up, fido_opt_t uv)
575{
576 cbor_item_t *item = NULL;
577
578 if ((item = cbor_new_definite_map(2)) == NULL)
579 return (NULL);
580
581 if ((up != FIDO_OPT_OMIT && cbor_add_bool(item, "up", up) < 0) ||
582 (uv != FIDO_OPT_OMIT && cbor_add_bool(item, "uv", uv) < 0)) {
583 cbor_decref(&item);
584 return (NULL);
585 }
586
587 return (item);
588}
589
590cbor_item_t *
591cbor_encode_pin_auth(const fido_blob_t *hmac_key, const fido_blob_t *data)
592{
593 const EVP_MD *md = NULL;
594 unsigned char dgst[SHA256_DIGEST_LENGTH];
595 unsigned int dgst_len;
596
597 if ((md = EVP_sha256()) == NULL || HMAC(md, hmac_key->ptr,
598 (int)hmac_key->len, data->ptr, (int)data->len, dgst,
599 &dgst_len) == NULL || dgst_len != SHA256_DIGEST_LENGTH)
600 return (NULL);
601
602 return (cbor_build_bytestring(dgst, 16));
603}
604
605cbor_item_t *
606cbor_encode_pin_opt(void)
607{
608 return (cbor_build_uint8(1));
609}
610
611cbor_item_t *
612cbor_encode_pin_enc(const fido_blob_t *key, const fido_blob_t *pin)
613{
614 fido_blob_t pe;
615 cbor_item_t *item = NULL;
616
617 if (aes256_cbc_enc(key, pin, &pe) < 0)
618 return (NULL);
619
620 item = cbor_build_bytestring(pe.ptr, pe.len);
621 free(pe.ptr);
622
623 return (item);
624}
625
626static int
627sha256(const unsigned char *data, size_t data_len, fido_blob_t *digest)
628{
629 if ((digest->ptr = calloc(1, SHA256_DIGEST_LENGTH)) == NULL)
630 return (-1);
631
632 digest->len = SHA256_DIGEST_LENGTH;
633
634 if (SHA256(data, data_len, digest->ptr) != digest->ptr) {
635 free(digest->ptr);
636 digest->ptr = NULL;
637 digest->len = 0;
638 return (-1);
639 }
640
641 return (0);
642}
643
644cbor_item_t *
645cbor_encode_change_pin_auth(const fido_blob_t *key, const fido_blob_t *new_pin,
646 const fido_blob_t *pin)
647{
648 unsigned char dgst[SHA256_DIGEST_LENGTH];
649 unsigned int dgst_len;
650 cbor_item_t *item = NULL;
651 const EVP_MD *md = NULL;
652#if OPENSSL_VERSION_NUMBER < 0x10100000L
653 HMAC_CTX ctx;
654#else
655 HMAC_CTX *ctx = NULL;
656#endif
657 fido_blob_t *npe = NULL; /* new pin, encrypted */
658 fido_blob_t *ph = NULL; /* pin hash */
659 fido_blob_t *phe = NULL; /* pin hash, encrypted */
660 int ok = -1;
661
662 if ((npe = fido_blob_new()) == NULL ||
663 (ph = fido_blob_new()) == NULL ||
664 (phe = fido_blob_new()) == NULL)
665 goto fail;
666
667 if (aes256_cbc_enc(key, new_pin, npe) < 0) {
668 fido_log_debug("%s: aes256_cbc_enc 1", __func__);
669 goto fail;
670 }
671
672 if (sha256(pin->ptr, pin->len, ph) < 0 || ph->len < 16) {
673 fido_log_debug("%s: sha256", __func__);
674 goto fail;
675 }
676
677 ph->len = 16; /* first 16 bytes */
678
679 if (aes256_cbc_enc(key, ph, phe) < 0) {
680 fido_log_debug("%s: aes256_cbc_enc 2", __func__);
681 goto fail;
682 }
683
684#if OPENSSL_VERSION_NUMBER < 0x10100000L
685 HMAC_CTX_init(&ctx);
686
687 if ((md = EVP_sha256()) == NULL ||
688 HMAC_Init_ex(&ctx, key->ptr, (int)key->len, md, NULL) == 0 ||
689 HMAC_Update(&ctx, npe->ptr, (int)npe->len) == 0 ||
690 HMAC_Update(&ctx, phe->ptr, (int)phe->len) == 0 ||
691 HMAC_Final(&ctx, dgst, &dgst_len) == 0 || dgst_len != 32) {
692 fido_log_debug("%s: HMAC", __func__);
693 goto fail;
694 }
695#else
696 if ((ctx = HMAC_CTX_new()) == NULL ||
697 (md = EVP_sha256()) == NULL ||
698 HMAC_Init_ex(ctx, key->ptr, (int)key->len, md, NULL) == 0 ||
699 HMAC_Update(ctx, npe->ptr, (int)npe->len) == 0 ||
700 HMAC_Update(ctx, phe->ptr, (int)phe->len) == 0 ||
701 HMAC_Final(ctx, dgst, &dgst_len) == 0 || dgst_len != 32) {
702 fido_log_debug("%s: HMAC", __func__);
703 goto fail;
704 }
705#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
706
707 if ((item = cbor_build_bytestring(dgst, 16)) == NULL) {
708 fido_log_debug("%s: cbor_build_bytestring", __func__);
709 goto fail;
710 }
711
712 ok = 0;
713fail:
714 fido_blob_free(&npe);
715 fido_blob_free(&ph);
716 fido_blob_free(&phe);
717
718#if OPENSSL_VERSION_NUMBER >= 0x10100000L
719 if (ctx != NULL)
720 HMAC_CTX_free(ctx);
721#endif
722
723 if (ok < 0) {
724 if (item != NULL) {
725 cbor_decref(&item);
726 item = NULL;
727 }
728 }
729
730 return (item);
731}
732
733cbor_item_t *
734cbor_encode_set_pin_auth(const fido_blob_t *key, const fido_blob_t *pin)
735{
736 const EVP_MD *md = NULL;
737 unsigned char dgst[SHA256_DIGEST_LENGTH];
738 unsigned int dgst_len;
739 cbor_item_t *item = NULL;
740 fido_blob_t *pe = NULL;
741
742 if ((pe = fido_blob_new()) == NULL)
743 goto fail;
744
745 if (aes256_cbc_enc(key, pin, pe) < 0) {
746 fido_log_debug("%s: aes256_cbc_enc", __func__);
747 goto fail;
748 }
749
750 if ((md = EVP_sha256()) == NULL || key->len != 32 || HMAC(md, key->ptr,
751 (int)key->len, pe->ptr, (int)pe->len, dgst, &dgst_len) == NULL ||
752 dgst_len != SHA256_DIGEST_LENGTH) {
753 fido_log_debug("%s: HMAC", __func__);
754 goto fail;
755 }
756
757 item = cbor_build_bytestring(dgst, 16);
758fail:
759 fido_blob_free(&pe);
760
761 return (item);
762}
763
764cbor_item_t *
765cbor_encode_pin_hash_enc(const fido_blob_t *shared, const fido_blob_t *pin)
766{
767 cbor_item_t *item = NULL;
768 fido_blob_t *ph = NULL;
769 fido_blob_t *phe = NULL;
770
771 if ((ph = fido_blob_new()) == NULL || (phe = fido_blob_new()) == NULL)
772 goto fail;
773
774 if (sha256(pin->ptr, pin->len, ph) < 0 || ph->len < 16) {
775 fido_log_debug("%s: SHA256", __func__);
776 goto fail;
777 }
778
779 ph->len = 16; /* first 16 bytes */
780
781 if (aes256_cbc_enc(shared, ph, phe) < 0) {
782 fido_log_debug("%s: aes256_cbc_enc", __func__);
783 goto fail;
784 }
785
786 item = cbor_build_bytestring(phe->ptr, phe->len);
787fail:
788 fido_blob_free(&ph);
789 fido_blob_free(&phe);
790
791 return (item);
792}
793
794cbor_item_t *
795cbor_encode_hmac_secret_param(const fido_blob_t *ecdh, const es256_pk_t *pk,
796 const fido_blob_t *hmac_salt)
797{
798 cbor_item_t *item = NULL;
799 cbor_item_t *param = NULL;
800 cbor_item_t *argv[3];
801 struct cbor_pair pair;
802
803 memset(argv, 0, sizeof(argv));
804 memset(&pair, 0, sizeof(pair));
805
806 if (ecdh == NULL || pk == NULL || hmac_salt->ptr == NULL) {
807 fido_log_debug("%s: ecdh=%p, pk=%p, hmac_salt->ptr=%p",
808 __func__, (const void *)ecdh, (const void *)pk,
809 (const void *)hmac_salt->ptr);
810 goto fail;
811 }
812
813 if (hmac_salt->len != 32 && hmac_salt->len != 64) {
814 fido_log_debug("%s: hmac_salt->len=%zu", __func__,
815 hmac_salt->len);
816 goto fail;
817 }
818
819 /* XXX not pin, but salt */
820 if ((argv[0] = es256_pk_encode(pk, 1)) == NULL ||
821 (argv[1] = cbor_encode_pin_enc(ecdh, hmac_salt)) == NULL ||
822 (argv[2] = cbor_encode_set_pin_auth(ecdh, hmac_salt)) == NULL) {
823 fido_log_debug("%s: cbor encode", __func__);
824 goto fail;
825 }
826
827 if ((param = cbor_flatten_vector(argv, 3)) == NULL) {
828 fido_log_debug("%s: cbor_flatten_vector", __func__);
829 goto fail;
830 }
831
832 if ((item = cbor_new_definite_map(1)) == NULL) {
833 fido_log_debug("%s: cbor_new_definite_map", __func__);
834 goto fail;
835 }
836
837 if ((pair.key = cbor_build_string("hmac-secret")) == NULL) {
838 fido_log_debug("%s: cbor_build", __func__);
839 goto fail;
840 }
841
842 pair.value = param;
843
844 if (!cbor_map_add(item, pair)) {
845 fido_log_debug("%s: cbor_map_add", __func__);
846 cbor_decref(&item);
847 item = NULL;
848 goto fail;
849 }
850
851fail:
852 for (size_t i = 0; i < 3; i++)
853 if (argv[i] != NULL)
854 cbor_decref(&argv[i]);
855
856 if (param != NULL)
857 cbor_decref(&param);
858 if (pair.key != NULL)
859 cbor_decref(&pair.key);
860
861 return (item);
862}
863
864int
865cbor_decode_fmt(const cbor_item_t *item, char **fmt)
866{
867 char *type = NULL;
868
869 if (cbor_string_copy(item, &type) < 0) {
870 fido_log_debug("%s: cbor_string_copy", __func__);
871 return (-1);
872 }
873
874 if (strcmp(type, "packed") && strcmp(type, "fido-u2f")) {
875 fido_log_debug("%s: type=%s", __func__, type);
876 free(type);
877 return (-1);
878 }
879
880 *fmt = type;
881
882 return (0);
883}
884
885struct cose_key {
886 int kty;
887 int alg;
888 int crv;
889};
890
891static int
892find_cose_alg(const cbor_item_t *key, const cbor_item_t *val, void *arg)
893{
894 struct cose_key *cose_key = arg;
895
896 if (cbor_isa_uint(key) == true &&
897 cbor_int_get_width(key) == CBOR_INT_8) {
898 switch (cbor_get_uint8(key)) {
899 case 1:
900 if (cbor_isa_uint(val) == false ||
901 cbor_get_int(val) > INT_MAX || cose_key->kty != 0) {
902 fido_log_debug("%s: kty", __func__);
903 return (-1);
904 }
905
906 cose_key->kty = (int)cbor_get_int(val);
907
908 break;
909 case 3:
910 if (cbor_isa_negint(val) == false ||
911 cbor_get_int(val) > INT_MAX || cose_key->alg != 0) {
912 fido_log_debug("%s: alg", __func__);
913 return (-1);
914 }
915
916 cose_key->alg = -(int)cbor_get_int(val) - 1;
917
918 break;
919 }
920 } else if (cbor_isa_negint(key) == true &&
921 cbor_int_get_width(key) == CBOR_INT_8) {
922 if (cbor_get_uint8(key) == 0) {
923 /* get crv if not rsa, otherwise ignore */
924 if (cbor_isa_uint(val) == true &&
925 cbor_get_int(val) <= INT_MAX &&
926 cose_key->crv == 0)
927 cose_key->crv = (int)cbor_get_int(val);
928 }
929 }
930
931 return (0);
932}
933
934static int
935get_cose_alg(const cbor_item_t *item, int *cose_alg)
936{
937 struct cose_key cose_key;
938
939 memset(&cose_key, 0, sizeof(cose_key));
940
941 *cose_alg = 0;
942
943 if (cbor_isa_map(item) == false ||
944 cbor_map_is_definite(item) == false ||
945 cbor_map_iter(item, &cose_key, find_cose_alg) < 0) {
946 fido_log_debug("%s: cbor type", __func__);
947 return (-1);
948 }
949
950 switch (cose_key.alg) {
951 case COSE_ES256:
952 if (cose_key.kty != COSE_KTY_EC2 ||
953 cose_key.crv != COSE_P256) {
954 fido_log_debug("%s: invalid kty/crv", __func__);
955 return (-1);
956 }
957
958 break;
959 case COSE_EDDSA:
960 if (cose_key.kty != COSE_KTY_OKP ||
961 cose_key.crv != COSE_ED25519) {
962 fido_log_debug("%s: invalid kty/crv", __func__);
963 return (-1);
964 }
965
966 break;
967 case COSE_RS256:
968 if (cose_key.kty != COSE_KTY_RSA) {
969 fido_log_debug("%s: invalid kty/crv", __func__);
970 return (-1);
971 }
972
973 break;
974 default:
975 fido_log_debug("%s: unknown alg %d", __func__, cose_key.alg);
976
977 return (-1);
978 }
979
980 *cose_alg = cose_key.alg;
981
982 return (0);
983}
984
985int
986cbor_decode_pubkey(const cbor_item_t *item, int *type, void *key)
987{
988 if (get_cose_alg(item, type) < 0) {
989 fido_log_debug("%s: get_cose_alg", __func__);
990 return (-1);
991 }
992
993 switch (*type) {
994 case COSE_ES256:
995 if (es256_pk_decode(item, key) < 0) {
996 fido_log_debug("%s: es256_pk_decode", __func__);
997 return (-1);
998 }
999 break;
1000 case COSE_RS256:
1001 if (rs256_pk_decode(item, key) < 0) {
1002 fido_log_debug("%s: rs256_pk_decode", __func__);
1003 return (-1);
1004 }
1005 break;
1006 case COSE_EDDSA:
1007 if (eddsa_pk_decode(item, key) < 0) {
1008 fido_log_debug("%s: eddsa_pk_decode", __func__);
1009 return (-1);
1010 }
1011 break;
1012 default:
1013 fido_log_debug("%s: invalid cose_alg %d", __func__, *type);
1014 return (-1);
1015 }
1016
1017 return (0);
1018}
1019
1020static int
1021decode_attcred(const unsigned char **buf, size_t *len, int cose_alg,
1022 fido_attcred_t *attcred)
1023{
1024 cbor_item_t *item = NULL;
1025 struct cbor_load_result cbor;
1026 uint16_t id_len;
1027 int ok = -1;
1028
1029 fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)*buf,
1030 *len);
1031
1032 if (fido_buf_read(buf, len, &attcred->aaguid,
1033 sizeof(attcred->aaguid)) < 0) {
1034 fido_log_debug("%s: fido_buf_read aaguid", __func__);
1035 return (-1);
1036 }
1037
1038 if (fido_buf_read(buf, len, &id_len, sizeof(id_len)) < 0) {
1039 fido_log_debug("%s: fido_buf_read id_len", __func__);
1040 return (-1);
1041 }
1042
1043 attcred->id.len = (size_t)be16toh(id_len);
1044 if ((attcred->id.ptr = malloc(attcred->id.len)) == NULL)
1045 return (-1);
1046
1047 fido_log_debug("%s: attcred->id.len=%zu", __func__, attcred->id.len);
1048
1049 if (fido_buf_read(buf, len, attcred->id.ptr, attcred->id.len) < 0) {
1050 fido_log_debug("%s: fido_buf_read id", __func__);
1051 return (-1);
1052 }
1053
1054 if ((item = cbor_load(*buf, *len, &cbor)) == NULL) {
1055 fido_log_debug("%s: cbor_load", __func__);
1056 fido_log_xxd(*buf, *len);
1057 goto fail;
1058 }
1059
1060 if (cbor_decode_pubkey(item, &attcred->type, &attcred->pubkey) < 0) {
1061 fido_log_debug("%s: cbor_decode_pubkey", __func__);
1062 goto fail;
1063 }
1064
1065 if (attcred->type != cose_alg) {
1066 fido_log_debug("%s: cose_alg mismatch (%d != %d)", __func__,
1067 attcred->type, cose_alg);
1068 goto fail;
1069 }
1070
1071 *buf += cbor.read;
1072 *len -= cbor.read;
1073
1074 ok = 0;
1075fail:
1076 if (item != NULL)
1077 cbor_decref(&item);
1078
1079 return (ok);
1080}
1081
1082static int
1083decode_extension(const cbor_item_t *key, const cbor_item_t *val, void *arg)
1084{
1085 int *authdata_ext = arg;
1086 char *type = NULL;
1087 int ok = -1;
1088
1089 if (cbor_string_copy(key, &type) < 0 || strcmp(type, "hmac-secret")) {
1090 fido_log_debug("%s: cbor type", __func__);
1091 ok = 0; /* ignore */
1092 goto out;
1093 }
1094
1095 if (cbor_isa_float_ctrl(val) == false ||
1096 cbor_float_get_width(val) != CBOR_FLOAT_0 ||
1097 cbor_is_bool(val) == false || *authdata_ext != 0) {
1098 fido_log_debug("%s: cbor type", __func__);
1099 goto out;
1100 }
1101
1102 if (cbor_ctrl_value(val) == CBOR_CTRL_TRUE)
1103 *authdata_ext |= FIDO_EXT_HMAC_SECRET;
1104
1105 ok = 0;
1106out:
1107 free(type);
1108
1109 return (ok);
1110}
1111
1112static int
1113decode_extensions(const unsigned char **buf, size_t *len, int *authdata_ext)
1114{
1115 cbor_item_t *item = NULL;
1116 struct cbor_load_result cbor;
1117 int ok = -1;
1118
1119 fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)*buf,
1120 *len);
1121
1122 *authdata_ext = 0;
1123
1124 if ((item = cbor_load(*buf, *len, &cbor)) == NULL) {
1125 fido_log_debug("%s: cbor_load", __func__);
1126 fido_log_xxd(*buf, *len);
1127 goto fail;
1128 }
1129
1130 if (cbor_isa_map(item) == false ||
1131 cbor_map_is_definite(item) == false ||
1132 cbor_map_size(item) != 1 ||
1133 cbor_map_iter(item, authdata_ext, decode_extension) < 0) {
1134 fido_log_debug("%s: cbor type", __func__);
1135 goto fail;
1136 }
1137
1138 *buf += cbor.read;
1139 *len -= cbor.read;
1140
1141 ok = 0;
1142fail:
1143 if (item != NULL)
1144 cbor_decref(&item);
1145
1146 return (ok);
1147}
1148
1149static int
1150decode_hmac_secret_aux(const cbor_item_t *key, const cbor_item_t *val, void *arg)
1151{
1152 fido_blob_t *out = arg;
1153 char *type = NULL;
1154 int ok = -1;
1155
1156 if (cbor_string_copy(key, &type) < 0 || strcmp(type, "hmac-secret")) {
1157 fido_log_debug("%s: cbor type", __func__);
1158 ok = 0; /* ignore */
1159 goto out;
1160 }
1161
1162 ok = cbor_bytestring_copy(val, &out->ptr, &out->len);
1163out:
1164 free(type);
1165
1166 return (ok);
1167}
1168
1169static int
1170decode_hmac_secret(const unsigned char **buf, size_t *len, fido_blob_t *out)
1171{
1172 cbor_item_t *item = NULL;
1173 struct cbor_load_result cbor;
1174 int ok = -1;
1175
1176 fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)*buf,
1177 *len);
1178
1179 if ((item = cbor_load(*buf, *len, &cbor)) == NULL) {
1180 fido_log_debug("%s: cbor_load", __func__);
1181 fido_log_xxd(*buf, *len);
1182 goto fail;
1183 }
1184
1185 if (cbor_isa_map(item) == false ||
1186 cbor_map_is_definite(item) == false ||
1187 cbor_map_size(item) != 1 ||
1188 cbor_map_iter(item, out, decode_hmac_secret_aux) < 0) {
1189 fido_log_debug("%s: cbor type", __func__);
1190 goto fail;
1191 }
1192
1193 *buf += cbor.read;
1194 *len -= cbor.read;
1195
1196 ok = 0;
1197fail:
1198 if (item != NULL)
1199 cbor_decref(&item);
1200
1201 return (ok);
1202}
1203
1204int
1205cbor_decode_cred_authdata(const cbor_item_t *item, int cose_alg,
1206 fido_blob_t *authdata_cbor, fido_authdata_t *authdata,
1207 fido_attcred_t *attcred, int *authdata_ext)
1208{
1209 const unsigned char *buf = NULL;
1210 size_t len;
1211 size_t alloc_len;
1212
1213 if (cbor_isa_bytestring(item) == false ||
1214 cbor_bytestring_is_definite(item) == false) {
1215 fido_log_debug("%s: cbor type", __func__);
1216 return (-1);
1217 }
1218
1219 if (authdata_cbor->ptr != NULL ||
1220 (authdata_cbor->len = cbor_serialize_alloc(item,
1221 &authdata_cbor->ptr, &alloc_len)) == 0) {
1222 fido_log_debug("%s: cbor_serialize_alloc", __func__);
1223 return (-1);
1224 }
1225
1226 buf = cbor_bytestring_handle(item);
1227 len = cbor_bytestring_length(item);
1228
1229 fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)buf, len);
1230
1231 if (fido_buf_read(&buf, &len, authdata, sizeof(*authdata)) < 0) {
1232 fido_log_debug("%s: fido_buf_read", __func__);
1233 return (-1);
1234 }
1235
1236 authdata->sigcount = be32toh(authdata->sigcount);
1237
1238 if (attcred != NULL) {
1239 if ((authdata->flags & CTAP_AUTHDATA_ATT_CRED) == 0 ||
1240 decode_attcred(&buf, &len, cose_alg, attcred) < 0)
1241 return (-1);
1242 }
1243
1244 if (authdata_ext != NULL) {
1245 if ((authdata->flags & CTAP_AUTHDATA_EXT_DATA) != 0 &&
1246 decode_extensions(&buf, &len, authdata_ext) < 0)
1247 return (-1);
1248 }
1249
1250 /* XXX we should probably ensure that len == 0 at this point */
1251
1252 return (FIDO_OK);
1253}
1254
1255int
1256cbor_decode_assert_authdata(const cbor_item_t *item, fido_blob_t *authdata_cbor,
1257 fido_authdata_t *authdata, int *authdata_ext, fido_blob_t *hmac_secret_enc)
1258{
1259 const unsigned char *buf = NULL;
1260 size_t len;
1261 size_t alloc_len;
1262
1263 if (cbor_isa_bytestring(item) == false ||
1264 cbor_bytestring_is_definite(item) == false) {
1265 fido_log_debug("%s: cbor type", __func__);
1266 return (-1);
1267 }
1268
1269 if (authdata_cbor->ptr != NULL ||
1270 (authdata_cbor->len = cbor_serialize_alloc(item,
1271 &authdata_cbor->ptr, &alloc_len)) == 0) {
1272 fido_log_debug("%s: cbor_serialize_alloc", __func__);
1273 return (-1);
1274 }
1275
1276 buf = cbor_bytestring_handle(item);
1277 len = cbor_bytestring_length(item);
1278
1279 fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)buf, len);
1280
1281 if (fido_buf_read(&buf, &len, authdata, sizeof(*authdata)) < 0) {
1282 fido_log_debug("%s: fido_buf_read", __func__);
1283 return (-1);
1284 }
1285
1286 authdata->sigcount = be32toh(authdata->sigcount);
1287
1288 *authdata_ext = 0;
1289 if ((authdata->flags & CTAP_AUTHDATA_EXT_DATA) != 0) {
1290 /* XXX semantic leap: extensions -> hmac_secret */
1291 if (decode_hmac_secret(&buf, &len, hmac_secret_enc) < 0) {
1292 fido_log_debug("%s: decode_hmac_secret", __func__);
1293 return (-1);
1294 }
1295 *authdata_ext = FIDO_EXT_HMAC_SECRET;
1296 }
1297
1298 /* XXX we should probably ensure that len == 0 at this point */
1299
1300 return (FIDO_OK);
1301}
1302
1303static int
1304decode_x5c(const cbor_item_t *item, void *arg)
1305{
1306 fido_blob_t *x5c = arg;
1307
1308 if (x5c->len)
1309 return (0); /* ignore */
1310
1311 return (cbor_bytestring_copy(item, &x5c->ptr, &x5c->len));
1312}
1313
1314static int
1315decode_attstmt_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg)
1316{
1317 fido_attstmt_t *attstmt = arg;
1318 char *name = NULL;
1319 int ok = -1;
1320
1321 if (cbor_string_copy(key, &name) < 0) {
1322 fido_log_debug("%s: cbor type", __func__);
1323 ok = 0; /* ignore */
1324 goto out;
1325 }
1326
1327 if (!strcmp(name, "alg")) {
1328 if (cbor_isa_negint(val) == false ||
1329 cbor_int_get_width(val) != CBOR_INT_8 ||
1330 cbor_get_uint8(val) != -COSE_ES256 - 1) {
1331 fido_log_debug("%s: alg", __func__);
1332 goto out;
1333 }
1334 } else if (!strcmp(name, "sig")) {
1335 if (cbor_bytestring_copy(val, &attstmt->sig.ptr,
1336 &attstmt->sig.len) < 0) {
1337 fido_log_debug("%s: sig", __func__);
1338 goto out;
1339 }
1340 } else if (!strcmp(name, "x5c")) {
1341 if (cbor_isa_array(val) == false ||
1342 cbor_array_is_definite(val) == false ||
1343 cbor_array_iter(val, &attstmt->x5c, decode_x5c) < 0) {
1344 fido_log_debug("%s: x5c", __func__);
1345 goto out;
1346 }
1347 }
1348
1349 ok = 0;
1350out:
1351 free(name);
1352
1353 return (ok);
1354}
1355
1356int
1357cbor_decode_attstmt(const cbor_item_t *item, fido_attstmt_t *attstmt)
1358{
1359 if (cbor_isa_map(item) == false ||
1360 cbor_map_is_definite(item) == false ||
1361 cbor_map_iter(item, attstmt, decode_attstmt_entry) < 0) {
1362 fido_log_debug("%s: cbor type", __func__);
1363 return (-1);
1364 }
1365
1366 return (0);
1367}
1368
1369int
1370cbor_decode_uint64(const cbor_item_t *item, uint64_t *n)
1371{
1372 if (cbor_isa_uint(item) == false) {
1373 fido_log_debug("%s: cbor type", __func__);
1374 return (-1);
1375 }
1376
1377 *n = cbor_get_int(item);
1378
1379 return (0);
1380}
1381
1382static int
1383decode_cred_id_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg)
1384{
1385 fido_blob_t *id = arg;
1386 char *name = NULL;
1387 int ok = -1;
1388
1389 if (cbor_string_copy(key, &name) < 0) {
1390 fido_log_debug("%s: cbor type", __func__);
1391 ok = 0; /* ignore */
1392 goto out;
1393 }
1394
1395 if (!strcmp(name, "id"))
1396 if (cbor_bytestring_copy(val, &id->ptr, &id->len) < 0) {
1397 fido_log_debug("%s: cbor_bytestring_copy", __func__);
1398 goto out;
1399 }
1400
1401 ok = 0;
1402out:
1403 free(name);
1404
1405 return (ok);
1406}
1407
1408int
1409cbor_decode_cred_id(const cbor_item_t *item, fido_blob_t *id)
1410{
1411 if (cbor_isa_map(item) == false ||
1412 cbor_map_is_definite(item) == false ||
1413 cbor_map_iter(item, id, decode_cred_id_entry) < 0) {
1414 fido_log_debug("%s: cbor type", __func__);
1415 return (-1);
1416 }
1417
1418 return (0);
1419}
1420
1421static int
1422decode_user_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg)
1423{
1424 fido_user_t *user = arg;
1425 char *name = NULL;
1426 int ok = -1;
1427
1428 if (cbor_string_copy(key, &name) < 0) {
1429 fido_log_debug("%s: cbor type", __func__);
1430 ok = 0; /* ignore */
1431 goto out;
1432 }
1433
1434 if (!strcmp(name, "icon")) {
1435 if (cbor_string_copy(val, &user->icon) < 0) {
1436 fido_log_debug("%s: icon", __func__);
1437 goto out;
1438 }
1439 } else if (!strcmp(name, "name")) {
1440 if (cbor_string_copy(val, &user->name) < 0) {
1441 fido_log_debug("%s: name", __func__);
1442 goto out;
1443 }
1444 } else if (!strcmp(name, "displayName")) {
1445 if (cbor_string_copy(val, &user->display_name) < 0) {
1446 fido_log_debug("%s: display_name", __func__);
1447 goto out;
1448 }
1449 } else if (!strcmp(name, "id")) {
1450 if (cbor_bytestring_copy(val, &user->id.ptr, &user->id.len) < 0) {
1451 fido_log_debug("%s: id", __func__);
1452 goto out;
1453 }
1454 }
1455
1456 ok = 0;
1457out:
1458 free(name);
1459
1460 return (ok);
1461}
1462
1463int
1464cbor_decode_user(const cbor_item_t *item, fido_user_t *user)
1465{
1466 if (cbor_isa_map(item) == false ||
1467 cbor_map_is_definite(item) == false ||
1468 cbor_map_iter(item, user, decode_user_entry) < 0) {
1469 fido_log_debug("%s: cbor type", __func__);
1470 return (-1);
1471 }
1472
1473 return (0);
1474}
1475
1476static int
1477decode_rp_entity_entry(const cbor_item_t *key, const cbor_item_t *val,
1478 void *arg)
1479{
1480 fido_rp_t *rp = arg;
1481 char *name = NULL;
1482 int ok = -1;
1483
1484 if (cbor_string_copy(key, &name) < 0) {
1485 fido_log_debug("%s: cbor type", __func__);
1486 ok = 0; /* ignore */
1487 goto out;
1488 }
1489
1490 if (!strcmp(name, "id")) {
1491 if (cbor_string_copy(val, &rp->id) < 0) {
1492 fido_log_debug("%s: id", __func__);
1493 goto out;
1494 }
1495 } else if (!strcmp(name, "name")) {
1496 if (cbor_string_copy(val, &rp->name) < 0) {
1497 fido_log_debug("%s: name", __func__);
1498 goto out;
1499 }
1500 }
1501
1502 ok = 0;
1503out:
1504 free(name);
1505
1506 return (ok);
1507}
1508
1509int
1510cbor_decode_rp_entity(const cbor_item_t *item, fido_rp_t *rp)
1511{
1512 if (cbor_isa_map(item) == false ||
1513 cbor_map_is_definite(item) == false ||
1514 cbor_map_iter(item, rp, decode_rp_entity_entry) < 0) {
1515 fido_log_debug("%s: cbor type", __func__);
1516 return (-1);
1517 }
1518
1519 return (0);
1520}
diff --git a/src/cred.c b/src/cred.c
new file mode 100644
index 0000000..c4e1edb
--- /dev/null
+++ b/src/cred.c
@@ -0,0 +1,1034 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <openssl/ec.h>
8#include <openssl/evp.h>
9#include <openssl/sha.h>
10#include <openssl/x509.h>
11
12#include <string.h>
13#include "fido.h"
14#include "fido/es256.h"
15
16static int
17parse_makecred_reply(const cbor_item_t *key, const cbor_item_t *val, void *arg)
18{
19 fido_cred_t *cred = arg;
20
21 if (cbor_isa_uint(key) == false ||
22 cbor_int_get_width(key) != CBOR_INT_8) {
23 fido_log_debug("%s: cbor type", __func__);
24 return (0); /* ignore */
25 }
26
27 switch (cbor_get_uint8(key)) {
28 case 1: /* fmt */
29 return (cbor_decode_fmt(val, &cred->fmt));
30 case 2: /* authdata */
31 return (cbor_decode_cred_authdata(val, cred->type,
32 &cred->authdata_cbor, &cred->authdata, &cred->attcred,
33 &cred->authdata_ext));
34 case 3: /* attestation statement */
35 return (cbor_decode_attstmt(val, &cred->attstmt));
36 default: /* ignore */
37 fido_log_debug("%s: cbor type", __func__);
38 return (0);
39 }
40}
41
42static int
43fido_dev_make_cred_tx(fido_dev_t *dev, fido_cred_t *cred, const char *pin)
44{
45 fido_blob_t f;
46 fido_blob_t *ecdh = NULL;
47 es256_pk_t *pk = NULL;
48 cbor_item_t *argv[9];
49 int r;
50
51 memset(&f, 0, sizeof(f));
52 memset(argv, 0, sizeof(argv));
53
54 if (cred->cdh.ptr == NULL || cred->type == 0) {
55 fido_log_debug("%s: cdh=%p, type=%d", __func__,
56 (void *)cred->cdh.ptr, cred->type);
57 r = FIDO_ERR_INVALID_ARGUMENT;
58 goto fail;
59 }
60
61 if ((argv[0] = fido_blob_encode(&cred->cdh)) == NULL ||
62 (argv[1] = cbor_encode_rp_entity(&cred->rp)) == NULL ||
63 (argv[2] = cbor_encode_user_entity(&cred->user)) == NULL ||
64 (argv[3] = cbor_encode_pubkey_param(cred->type)) == NULL) {
65 fido_log_debug("%s: cbor encode", __func__);
66 r = FIDO_ERR_INTERNAL;
67 goto fail;
68 }
69
70 /* excluded credentials */
71 if (cred->excl.len)
72 if ((argv[4] = cbor_encode_pubkey_list(&cred->excl)) == NULL) {
73 fido_log_debug("%s: cbor_encode_pubkey_list", __func__);
74 r = FIDO_ERR_INTERNAL;
75 goto fail;
76 }
77
78 /* extensions */
79 if (cred->ext)
80 if ((argv[5] = cbor_encode_extensions(cred->ext)) == NULL) {
81 fido_log_debug("%s: cbor_encode_extensions", __func__);
82 r = FIDO_ERR_INTERNAL;
83 goto fail;
84 }
85
86 /* options */
87 if (cred->rk != FIDO_OPT_OMIT || cred->uv != FIDO_OPT_OMIT)
88 if ((argv[6] = cbor_encode_options(cred->rk,
89 cred->uv)) == NULL) {
90 fido_log_debug("%s: cbor_encode_options", __func__);
91 r = FIDO_ERR_INTERNAL;
92 goto fail;
93 }
94
95 /* pin authentication */
96 if (pin) {
97 if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
98 fido_log_debug("%s: fido_do_ecdh", __func__);
99 goto fail;
100 }
101 if ((r = cbor_add_pin_params(dev, &cred->cdh, pk, ecdh, pin,
102 &argv[7], &argv[8])) != FIDO_OK) {
103 fido_log_debug("%s: cbor_add_pin_params", __func__);
104 goto fail;
105 }
106 }
107
108 /* framing and transmission */
109 if (cbor_build_frame(CTAP_CBOR_MAKECRED, argv, 9, &f) < 0 ||
110 fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
111 fido_log_debug("%s: fido_tx", __func__);
112 r = FIDO_ERR_TX;
113 goto fail;
114 }
115
116 r = FIDO_OK;
117fail:
118 es256_pk_free(&pk);
119 fido_blob_free(&ecdh);
120 cbor_vector_free(argv, nitems(argv));
121 free(f.ptr);
122
123 return (r);
124}
125
126static int
127fido_dev_make_cred_rx(fido_dev_t *dev, fido_cred_t *cred, int ms)
128{
129 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
130 unsigned char reply[2048];
131 int reply_len;
132 int r;
133
134 fido_cred_reset_rx(cred);
135
136 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
137 fido_log_debug("%s: fido_rx", __func__);
138 return (FIDO_ERR_RX);
139 }
140
141 if ((r = cbor_parse_reply(reply, (size_t)reply_len, cred,
142 parse_makecred_reply)) != FIDO_OK) {
143 fido_log_debug("%s: parse_makecred_reply", __func__);
144 return (r);
145 }
146
147 if (cred->fmt == NULL || fido_blob_is_empty(&cred->authdata_cbor) ||
148 fido_blob_is_empty(&cred->attcred.id) ||
149 fido_blob_is_empty(&cred->attstmt.sig)) {
150 fido_cred_reset_rx(cred);
151 return (FIDO_ERR_INVALID_CBOR);
152 }
153
154 return (FIDO_OK);
155}
156
157static int
158fido_dev_make_cred_wait(fido_dev_t *dev, fido_cred_t *cred, const char *pin, int ms)
159{
160 int r;
161
162 if ((r = fido_dev_make_cred_tx(dev, cred, pin)) != FIDO_OK ||
163 (r = fido_dev_make_cred_rx(dev, cred, ms)) != FIDO_OK)
164 return (r);
165
166 return (FIDO_OK);
167}
168
169int
170fido_dev_make_cred(fido_dev_t *dev, fido_cred_t *cred, const char *pin)
171{
172 if (fido_dev_is_fido2(dev) == false) {
173 if (pin != NULL || cred->rk == FIDO_OPT_TRUE || cred->ext != 0)
174 return (FIDO_ERR_UNSUPPORTED_OPTION);
175 return (u2f_register(dev, cred, -1));
176 }
177
178 return (fido_dev_make_cred_wait(dev, cred, pin, -1));
179}
180
181static int
182check_extensions(int authdata_ext, int ext)
183{
184 if (authdata_ext != ext) {
185 fido_log_debug("%s: authdata_ext=0x%x != ext=0x%x", __func__,
186 authdata_ext, ext);
187 return (-1);
188 }
189
190 return (0);
191}
192
193int
194fido_check_rp_id(const char *id, const unsigned char *obtained_hash)
195{
196 unsigned char expected_hash[SHA256_DIGEST_LENGTH];
197
198 explicit_bzero(expected_hash, sizeof(expected_hash));
199
200 if (SHA256((const unsigned char *)id, strlen(id),
201 expected_hash) != expected_hash) {
202 fido_log_debug("%s: sha256", __func__);
203 return (-1);
204 }
205
206 return (timingsafe_bcmp(expected_hash, obtained_hash,
207 SHA256_DIGEST_LENGTH));
208}
209
210static int
211get_signed_hash_packed(fido_blob_t *dgst, const fido_blob_t *clientdata,
212 const fido_blob_t *authdata_cbor)
213{
214 cbor_item_t *item = NULL;
215 unsigned char *authdata_ptr = NULL;
216 size_t authdata_len;
217 struct cbor_load_result cbor;
218 SHA256_CTX ctx;
219 int ok = -1;
220
221 if ((item = cbor_load(authdata_cbor->ptr, authdata_cbor->len,
222 &cbor)) == NULL) {
223 fido_log_debug("%s: cbor_load", __func__);
224 goto fail;
225 }
226
227 if (cbor_isa_bytestring(item) == false ||
228 cbor_bytestring_is_definite(item) == false) {
229 fido_log_debug("%s: cbor type", __func__);
230 goto fail;
231 }
232
233 authdata_ptr = cbor_bytestring_handle(item);
234 authdata_len = cbor_bytestring_length(item);
235
236 if (dgst->len != SHA256_DIGEST_LENGTH || SHA256_Init(&ctx) == 0 ||
237 SHA256_Update(&ctx, authdata_ptr, authdata_len) == 0 ||
238 SHA256_Update(&ctx, clientdata->ptr, clientdata->len) == 0 ||
239 SHA256_Final(dgst->ptr, &ctx) == 0) {
240 fido_log_debug("%s: sha256", __func__);
241 goto fail;
242 }
243
244 ok = 0;
245fail:
246 if (item != NULL)
247 cbor_decref(&item);
248
249 return (ok);
250}
251
252static int
253get_signed_hash_u2f(fido_blob_t *dgst, const unsigned char *rp_id,
254 size_t rp_id_len, const fido_blob_t *clientdata, const fido_blob_t *id,
255 const es256_pk_t *pk)
256{
257 const uint8_t zero = 0;
258 const uint8_t four = 4; /* uncompressed point */
259 SHA256_CTX ctx;
260
261 if (dgst->len != SHA256_DIGEST_LENGTH || SHA256_Init(&ctx) == 0 ||
262 SHA256_Update(&ctx, &zero, sizeof(zero)) == 0 ||
263 SHA256_Update(&ctx, rp_id, rp_id_len) == 0 ||
264 SHA256_Update(&ctx, clientdata->ptr, clientdata->len) == 0 ||
265 SHA256_Update(&ctx, id->ptr, id->len) == 0 ||
266 SHA256_Update(&ctx, &four, sizeof(four)) == 0 ||
267 SHA256_Update(&ctx, pk->x, sizeof(pk->x)) == 0 ||
268 SHA256_Update(&ctx, pk->y, sizeof(pk->y)) == 0 ||
269 SHA256_Final(dgst->ptr, &ctx) == 0) {
270 fido_log_debug("%s: sha256", __func__);
271 return (-1);
272 }
273
274 return (0);
275}
276
277static int
278verify_sig(const fido_blob_t *dgst, const fido_blob_t *x5c,
279 const fido_blob_t *sig)
280{
281 BIO *rawcert = NULL;
282 X509 *cert = NULL;
283 EVP_PKEY *pkey = NULL;
284 EC_KEY *ec;
285 int ok = -1;
286
287 /* openssl needs ints */
288 if (dgst->len > INT_MAX || x5c->len > INT_MAX || sig->len > INT_MAX) {
289 fido_log_debug("%s: dgst->len=%zu, x5c->len=%zu, sig->len=%zu",
290 __func__, dgst->len, x5c->len, sig->len);
291 return (-1);
292 }
293
294 /* fetch key from x509 */
295 if ((rawcert = BIO_new_mem_buf(x5c->ptr, (int)x5c->len)) == NULL ||
296 (cert = d2i_X509_bio(rawcert, NULL)) == NULL ||
297 (pkey = X509_get_pubkey(cert)) == NULL ||
298 (ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL) {
299 fido_log_debug("%s: x509 key", __func__);
300 goto fail;
301 }
302
303 if (ECDSA_verify(0, dgst->ptr, (int)dgst->len, sig->ptr,
304 (int)sig->len, ec) != 1) {
305 fido_log_debug("%s: ECDSA_verify", __func__);
306 goto fail;
307 }
308
309 ok = 0;
310fail:
311 if (rawcert != NULL)
312 BIO_free(rawcert);
313 if (cert != NULL)
314 X509_free(cert);
315 if (pkey != NULL)
316 EVP_PKEY_free(pkey);
317
318 return (ok);
319}
320
321int
322fido_cred_verify(const fido_cred_t *cred)
323{
324 unsigned char buf[SHA256_DIGEST_LENGTH];
325 fido_blob_t dgst;
326 int r;
327
328 dgst.ptr = buf;
329 dgst.len = sizeof(buf);
330
331 /* do we have everything we need? */
332 if (cred->cdh.ptr == NULL || cred->authdata_cbor.ptr == NULL ||
333 cred->attstmt.x5c.ptr == NULL || cred->attstmt.sig.ptr == NULL ||
334 cred->fmt == NULL || cred->attcred.id.ptr == NULL ||
335 cred->rp.id == NULL) {
336 fido_log_debug("%s: cdh=%p, authdata=%p, x5c=%p, sig=%p, "
337 "fmt=%p id=%p, rp.id=%s", __func__, (void *)cred->cdh.ptr,
338 (void *)cred->authdata_cbor.ptr,
339 (void *)cred->attstmt.x5c.ptr,
340 (void *)cred->attstmt.sig.ptr, (void *)cred->fmt,
341 (void *)cred->attcred.id.ptr, cred->rp.id);
342 r = FIDO_ERR_INVALID_ARGUMENT;
343 goto out;
344 }
345
346 if (fido_check_rp_id(cred->rp.id, cred->authdata.rp_id_hash) != 0) {
347 fido_log_debug("%s: fido_check_rp_id", __func__);
348 r = FIDO_ERR_INVALID_PARAM;
349 goto out;
350 }
351
352 if (fido_check_flags(cred->authdata.flags, FIDO_OPT_TRUE,
353 cred->uv) < 0) {
354 fido_log_debug("%s: fido_check_flags", __func__);
355 r = FIDO_ERR_INVALID_PARAM;
356 goto out;
357 }
358
359 if (check_extensions(cred->authdata_ext, cred->ext) < 0) {
360 fido_log_debug("%s: check_extensions", __func__);
361 r = FIDO_ERR_INVALID_PARAM;
362 goto out;
363 }
364
365 if (!strcmp(cred->fmt, "packed")) {
366 if (get_signed_hash_packed(&dgst, &cred->cdh,
367 &cred->authdata_cbor) < 0) {
368 fido_log_debug("%s: get_signed_hash_packed", __func__);
369 r = FIDO_ERR_INTERNAL;
370 goto out;
371 }
372 } else {
373 if (get_signed_hash_u2f(&dgst, cred->authdata.rp_id_hash,
374 sizeof(cred->authdata.rp_id_hash), &cred->cdh,
375 &cred->attcred.id, &cred->attcred.pubkey.es256) < 0) {
376 fido_log_debug("%s: get_signed_hash_u2f", __func__);
377 r = FIDO_ERR_INTERNAL;
378 goto out;
379 }
380 }
381
382 if (verify_sig(&dgst, &cred->attstmt.x5c, &cred->attstmt.sig) < 0) {
383 fido_log_debug("%s: verify_sig", __func__);
384 r = FIDO_ERR_INVALID_SIG;
385 goto out;
386 }
387
388 r = FIDO_OK;
389out:
390 explicit_bzero(buf, sizeof(buf));
391
392 return (r);
393}
394
395int
396fido_cred_verify_self(const fido_cred_t *cred)
397{
398 unsigned char buf[SHA256_DIGEST_LENGTH];
399 fido_blob_t dgst;
400 int ok = -1;
401 int r;
402
403 dgst.ptr = buf;
404 dgst.len = sizeof(buf);
405
406 /* do we have everything we need? */
407 if (cred->cdh.ptr == NULL || cred->authdata_cbor.ptr == NULL ||
408 cred->attstmt.x5c.ptr != NULL || cred->attstmt.sig.ptr == NULL ||
409 cred->fmt == NULL || cred->attcred.id.ptr == NULL ||
410 cred->rp.id == NULL) {
411 fido_log_debug("%s: cdh=%p, authdata=%p, x5c=%p, sig=%p, "
412 "fmt=%p id=%p, rp.id=%s", __func__, (void *)cred->cdh.ptr,
413 (void *)cred->authdata_cbor.ptr,
414 (void *)cred->attstmt.x5c.ptr,
415 (void *)cred->attstmt.sig.ptr, (void *)cred->fmt,
416 (void *)cred->attcred.id.ptr, cred->rp.id);
417 r = FIDO_ERR_INVALID_ARGUMENT;
418 goto out;
419 }
420
421 if (fido_check_rp_id(cred->rp.id, cred->authdata.rp_id_hash) != 0) {
422 fido_log_debug("%s: fido_check_rp_id", __func__);
423 r = FIDO_ERR_INVALID_PARAM;
424 goto out;
425 }
426
427 if (fido_check_flags(cred->authdata.flags, FIDO_OPT_TRUE,
428 cred->uv) < 0) {
429 fido_log_debug("%s: fido_check_flags", __func__);
430 r = FIDO_ERR_INVALID_PARAM;
431 goto out;
432 }
433
434 if (check_extensions(cred->authdata_ext, cred->ext) < 0) {
435 fido_log_debug("%s: check_extensions", __func__);
436 r = FIDO_ERR_INVALID_PARAM;
437 goto out;
438 }
439
440 if (!strcmp(cred->fmt, "packed")) {
441 if (get_signed_hash_packed(&dgst, &cred->cdh,
442 &cred->authdata_cbor) < 0) {
443 fido_log_debug("%s: get_signed_hash_packed", __func__);
444 r = FIDO_ERR_INTERNAL;
445 goto out;
446 }
447 } else {
448 if (get_signed_hash_u2f(&dgst, cred->authdata.rp_id_hash,
449 sizeof(cred->authdata.rp_id_hash), &cred->cdh,
450 &cred->attcred.id, &cred->attcred.pubkey.es256) < 0) {
451 fido_log_debug("%s: get_signed_hash_u2f", __func__);
452 r = FIDO_ERR_INTERNAL;
453 goto out;
454 }
455 }
456
457 switch (cred->attcred.type) {
458 case COSE_ES256:
459 ok = fido_verify_sig_es256(&dgst, &cred->attcred.pubkey.es256,
460 &cred->attstmt.sig);
461 break;
462 case COSE_RS256:
463 ok = fido_verify_sig_rs256(&dgst, &cred->attcred.pubkey.rs256,
464 &cred->attstmt.sig);
465 break;
466 case COSE_EDDSA:
467 ok = fido_verify_sig_eddsa(&dgst, &cred->attcred.pubkey.eddsa,
468 &cred->attstmt.sig);
469 break;
470 default:
471 fido_log_debug("%s: unsupported cose_alg %d", __func__,
472 cred->attcred.type);
473 r = FIDO_ERR_UNSUPPORTED_OPTION;
474 goto out;
475 }
476
477 if (ok < 0)
478 r = FIDO_ERR_INVALID_SIG;
479 else
480 r = FIDO_OK;
481
482out:
483 explicit_bzero(buf, sizeof(buf));
484
485 return (r);
486}
487
488fido_cred_t *
489fido_cred_new(void)
490{
491 return (calloc(1, sizeof(fido_cred_t)));
492}
493
494static void
495fido_cred_clean_authdata(fido_cred_t *cred)
496{
497 free(cred->authdata_cbor.ptr);
498 free(cred->attcred.id.ptr);
499
500 memset(&cred->authdata_ext, 0, sizeof(cred->authdata_ext));
501 memset(&cred->authdata_cbor, 0, sizeof(cred->authdata_cbor));
502 memset(&cred->authdata, 0, sizeof(cred->authdata));
503 memset(&cred->attcred, 0, sizeof(cred->attcred));
504}
505
506void
507fido_cred_reset_tx(fido_cred_t *cred)
508{
509 free(cred->cdh.ptr);
510 free(cred->rp.id);
511 free(cred->rp.name);
512 free(cred->user.id.ptr);
513 free(cred->user.icon);
514 free(cred->user.name);
515 free(cred->user.display_name);
516 fido_free_blob_array(&cred->excl);
517
518 memset(&cred->cdh, 0, sizeof(cred->cdh));
519 memset(&cred->rp, 0, sizeof(cred->rp));
520 memset(&cred->user, 0, sizeof(cred->user));
521 memset(&cred->excl, 0, sizeof(cred->excl));
522
523 cred->type = 0;
524 cred->ext = 0;
525 cred->rk = FIDO_OPT_OMIT;
526 cred->uv = FIDO_OPT_OMIT;
527}
528
529static void
530fido_cred_clean_x509(fido_cred_t *cred)
531{
532 free(cred->attstmt.x5c.ptr);
533 cred->attstmt.x5c.ptr = NULL;
534 cred->attstmt.x5c.len = 0;
535}
536
537static void
538fido_cred_clean_sig(fido_cred_t *cred)
539{
540 free(cred->attstmt.sig.ptr);
541 cred->attstmt.sig.ptr = NULL;
542 cred->attstmt.sig.len = 0;
543}
544
545void
546fido_cred_reset_rx(fido_cred_t *cred)
547{
548 free(cred->fmt);
549 cred->fmt = NULL;
550
551 fido_cred_clean_authdata(cred);
552 fido_cred_clean_x509(cred);
553 fido_cred_clean_sig(cred);
554}
555
556void
557fido_cred_free(fido_cred_t **cred_p)
558{
559 fido_cred_t *cred;
560
561 if (cred_p == NULL || (cred = *cred_p) == NULL)
562 return;
563
564 fido_cred_reset_tx(cred);
565 fido_cred_reset_rx(cred);
566
567 free(cred);
568
569 *cred_p = NULL;
570}
571
572int
573fido_cred_set_authdata(fido_cred_t *cred, const unsigned char *ptr, size_t len)
574{
575 cbor_item_t *item = NULL;
576 struct cbor_load_result cbor;
577 int r;
578
579 fido_cred_clean_authdata(cred);
580
581 if (ptr == NULL || len == 0) {
582 r = FIDO_ERR_INVALID_ARGUMENT;
583 goto fail;
584 }
585
586 if ((item = cbor_load(ptr, len, &cbor)) == NULL) {
587 fido_log_debug("%s: cbor_load", __func__);
588 r = FIDO_ERR_INVALID_ARGUMENT;
589 goto fail;
590 }
591
592 if (cbor_decode_cred_authdata(item, cred->type, &cred->authdata_cbor,
593 &cred->authdata, &cred->attcred, &cred->authdata_ext) < 0) {
594 fido_log_debug("%s: cbor_decode_cred_authdata", __func__);
595 r = FIDO_ERR_INVALID_ARGUMENT;
596 goto fail;
597 }
598
599 r = FIDO_OK;
600fail:
601 if (item != NULL)
602 cbor_decref(&item);
603
604 if (r != FIDO_OK)
605 fido_cred_clean_authdata(cred);
606
607 return (r);
608
609}
610
611int
612fido_cred_set_authdata_raw(fido_cred_t *cred, const unsigned char *ptr,
613 size_t len)
614{
615 cbor_item_t *item = NULL;
616 int r;
617
618 fido_cred_clean_authdata(cred);
619
620 if (ptr == NULL || len == 0) {
621 r = FIDO_ERR_INVALID_ARGUMENT;
622 goto fail;
623 }
624
625 if ((item = cbor_build_bytestring(ptr, len)) == NULL) {
626 fido_log_debug("%s: cbor_build_bytestring", __func__);
627 r = FIDO_ERR_INTERNAL;
628 goto fail;
629 }
630
631 if (cbor_decode_cred_authdata(item, cred->type, &cred->authdata_cbor,
632 &cred->authdata, &cred->attcred, &cred->authdata_ext) < 0) {
633 fido_log_debug("%s: cbor_decode_cred_authdata", __func__);
634 r = FIDO_ERR_INVALID_ARGUMENT;
635 goto fail;
636 }
637
638 r = FIDO_OK;
639fail:
640 if (item != NULL)
641 cbor_decref(&item);
642
643 if (r != FIDO_OK)
644 fido_cred_clean_authdata(cred);
645
646 return (r);
647
648}
649
650int
651fido_cred_set_x509(fido_cred_t *cred, const unsigned char *ptr, size_t len)
652{
653 unsigned char *x509;
654
655 fido_cred_clean_x509(cred);
656
657 if (ptr == NULL || len == 0)
658 return (FIDO_ERR_INVALID_ARGUMENT);
659 if ((x509 = malloc(len)) == NULL)
660 return (FIDO_ERR_INTERNAL);
661
662 memcpy(x509, ptr, len);
663 cred->attstmt.x5c.ptr = x509;
664 cred->attstmt.x5c.len = len;
665
666 return (FIDO_OK);
667}
668
669int
670fido_cred_set_sig(fido_cred_t *cred, const unsigned char *ptr, size_t len)
671{
672 unsigned char *sig;
673
674 fido_cred_clean_sig(cred);
675
676 if (ptr == NULL || len == 0)
677 return (FIDO_ERR_INVALID_ARGUMENT);
678 if ((sig = malloc(len)) == NULL)
679 return (FIDO_ERR_INTERNAL);
680
681 memcpy(sig, ptr, len);
682 cred->attstmt.sig.ptr = sig;
683 cred->attstmt.sig.len = len;
684
685 return (FIDO_OK);
686}
687
688int
689fido_cred_exclude(fido_cred_t *cred, const unsigned char *id_ptr, size_t id_len)
690{
691 fido_blob_t id_blob;
692 fido_blob_t *list_ptr;
693
694 memset(&id_blob, 0, sizeof(id_blob));
695
696 if (fido_blob_set(&id_blob, id_ptr, id_len) < 0)
697 return (FIDO_ERR_INVALID_ARGUMENT);
698
699 if (cred->excl.len == SIZE_MAX) {
700 free(id_blob.ptr);
701 return (FIDO_ERR_INVALID_ARGUMENT);
702 }
703
704 if ((list_ptr = recallocarray(cred->excl.ptr, cred->excl.len,
705 cred->excl.len + 1, sizeof(fido_blob_t))) == NULL) {
706 free(id_blob.ptr);
707 return (FIDO_ERR_INTERNAL);
708 }
709
710 list_ptr[cred->excl.len++] = id_blob;
711 cred->excl.ptr = list_ptr;
712
713 return (FIDO_OK);
714}
715
716int
717fido_cred_set_clientdata_hash(fido_cred_t *cred, const unsigned char *hash,
718 size_t hash_len)
719{
720 if (fido_blob_set(&cred->cdh, hash, hash_len) < 0)
721 return (FIDO_ERR_INVALID_ARGUMENT);
722
723 return (FIDO_OK);
724}
725
726int
727fido_cred_set_rp(fido_cred_t *cred, const char *id, const char *name)
728{
729 fido_rp_t *rp = &cred->rp;
730
731 if (rp->id != NULL) {
732 free(rp->id);
733 rp->id = NULL;
734 }
735 if (rp->name != NULL) {
736 free(rp->name);
737 rp->name = NULL;
738 }
739
740 if (id != NULL && (rp->id = strdup(id)) == NULL)
741 goto fail;
742 if (name != NULL && (rp->name = strdup(name)) == NULL)
743 goto fail;
744
745 return (FIDO_OK);
746fail:
747 free(rp->id);
748 free(rp->name);
749 rp->id = NULL;
750 rp->name = NULL;
751
752 return (FIDO_ERR_INTERNAL);
753}
754
755int
756fido_cred_set_user(fido_cred_t *cred, const unsigned char *user_id,
757 size_t user_id_len, const char *name, const char *display_name,
758 const char *icon)
759{
760 fido_user_t *up = &cred->user;
761
762 if (up->id.ptr != NULL) {
763 free(up->id.ptr);
764 up->id.ptr = NULL;
765 up->id.len = 0;
766 }
767 if (up->name != NULL) {
768 free(up->name);
769 up->name = NULL;
770 }
771 if (up->display_name != NULL) {
772 free(up->display_name);
773 up->display_name = NULL;
774 }
775 if (up->icon != NULL) {
776 free(up->icon);
777 up->icon = NULL;
778 }
779
780 if (user_id != NULL) {
781 if ((up->id.ptr = malloc(user_id_len)) == NULL)
782 goto fail;
783 memcpy(up->id.ptr, user_id, user_id_len);
784 up->id.len = user_id_len;
785 }
786 if (name != NULL && (up->name = strdup(name)) == NULL)
787 goto fail;
788 if (display_name != NULL &&
789 (up->display_name = strdup(display_name)) == NULL)
790 goto fail;
791 if (icon != NULL && (up->icon = strdup(icon)) == NULL)
792 goto fail;
793
794 return (FIDO_OK);
795fail:
796 free(up->id.ptr);
797 free(up->name);
798 free(up->display_name);
799 free(up->icon);
800
801 up->id.ptr = NULL;
802 up->id.len = 0;
803 up->name = NULL;
804 up->display_name = NULL;
805 up->icon = NULL;
806
807 return (FIDO_ERR_INTERNAL);
808}
809
810int
811fido_cred_set_extensions(fido_cred_t *cred, int ext)
812{
813 if (ext != 0 && ext != FIDO_EXT_HMAC_SECRET)
814 return (FIDO_ERR_INVALID_ARGUMENT);
815
816 cred->ext = ext;
817
818 return (FIDO_OK);
819}
820
821int
822fido_cred_set_options(fido_cred_t *cred, bool rk, bool uv)
823{
824 cred->rk = rk ? FIDO_OPT_TRUE : FIDO_OPT_FALSE;
825 cred->uv = uv ? FIDO_OPT_TRUE : FIDO_OPT_FALSE;
826
827 return (FIDO_OK);
828}
829
830int
831fido_cred_set_rk(fido_cred_t *cred, fido_opt_t rk)
832{
833 cred->rk = rk;
834
835 return (FIDO_OK);
836}
837
838int
839fido_cred_set_uv(fido_cred_t *cred, fido_opt_t uv)
840{
841 cred->uv = uv;
842
843 return (FIDO_OK);
844}
845
846int
847fido_cred_set_fmt(fido_cred_t *cred, const char *fmt)
848{
849 free(cred->fmt);
850 cred->fmt = NULL;
851
852 if (fmt == NULL)
853 return (FIDO_ERR_INVALID_ARGUMENT);
854
855 if (strcmp(fmt, "packed") && strcmp(fmt, "fido-u2f"))
856 return (FIDO_ERR_INVALID_ARGUMENT);
857
858 if ((cred->fmt = strdup(fmt)) == NULL)
859 return (FIDO_ERR_INTERNAL);
860
861 return (FIDO_OK);
862}
863
864int
865fido_cred_set_type(fido_cred_t *cred, int cose_alg)
866{
867 if ((cose_alg != COSE_ES256 && cose_alg != COSE_RS256 &&
868 cose_alg != COSE_EDDSA) || cred->type != 0)
869 return (FIDO_ERR_INVALID_ARGUMENT);
870
871 cred->type = cose_alg;
872
873 return (FIDO_OK);
874}
875
876int
877fido_cred_type(const fido_cred_t *cred)
878{
879 return (cred->type);
880}
881
882uint8_t
883fido_cred_flags(const fido_cred_t *cred)
884{
885 return (cred->authdata.flags);
886}
887
888const unsigned char *
889fido_cred_clientdata_hash_ptr(const fido_cred_t *cred)
890{
891 return (cred->cdh.ptr);
892}
893
894size_t
895fido_cred_clientdata_hash_len(const fido_cred_t *cred)
896{
897 return (cred->cdh.len);
898}
899
900const unsigned char *
901fido_cred_x5c_ptr(const fido_cred_t *cred)
902{
903 return (cred->attstmt.x5c.ptr);
904}
905
906size_t
907fido_cred_x5c_len(const fido_cred_t *cred)
908{
909 return (cred->attstmt.x5c.len);
910}
911
912const unsigned char *
913fido_cred_sig_ptr(const fido_cred_t *cred)
914{
915 return (cred->attstmt.sig.ptr);
916}
917
918size_t
919fido_cred_sig_len(const fido_cred_t *cred)
920{
921 return (cred->attstmt.sig.len);
922}
923
924const unsigned char *
925fido_cred_authdata_ptr(const fido_cred_t *cred)
926{
927 return (cred->authdata_cbor.ptr);
928}
929
930size_t
931fido_cred_authdata_len(const fido_cred_t *cred)
932{
933 return (cred->authdata_cbor.len);
934}
935
936const unsigned char *
937fido_cred_pubkey_ptr(const fido_cred_t *cred)
938{
939 const void *ptr;
940
941 switch (cred->attcred.type) {
942 case COSE_ES256:
943 ptr = &cred->attcred.pubkey.es256;
944 break;
945 case COSE_RS256:
946 ptr = &cred->attcred.pubkey.rs256;
947 break;
948 case COSE_EDDSA:
949 ptr = &cred->attcred.pubkey.eddsa;
950 break;
951 default:
952 ptr = NULL;
953 break;
954 }
955
956 return (ptr);
957}
958
959size_t
960fido_cred_pubkey_len(const fido_cred_t *cred)
961{
962 size_t len;
963
964 switch (cred->attcred.type) {
965 case COSE_ES256:
966 len = sizeof(cred->attcred.pubkey.es256);
967 break;
968 case COSE_RS256:
969 len = sizeof(cred->attcred.pubkey.rs256);
970 break;
971 case COSE_EDDSA:
972 len = sizeof(cred->attcred.pubkey.eddsa);
973 break;
974 default:
975 len = 0;
976 break;
977 }
978
979 return (len);
980}
981
982const unsigned char *
983fido_cred_id_ptr(const fido_cred_t *cred)
984{
985 return (cred->attcred.id.ptr);
986}
987
988size_t
989fido_cred_id_len(const fido_cred_t *cred)
990{
991 return (cred->attcred.id.len);
992}
993
994const char *
995fido_cred_fmt(const fido_cred_t *cred)
996{
997 return (cred->fmt);
998}
999
1000const char *
1001fido_cred_rp_id(const fido_cred_t *cred)
1002{
1003 return (cred->rp.id);
1004}
1005
1006const char *
1007fido_cred_rp_name(const fido_cred_t *cred)
1008{
1009 return (cred->rp.name);
1010}
1011
1012const char *
1013fido_cred_user_name(const fido_cred_t *cred)
1014{
1015 return (cred->user.name);
1016}
1017
1018const char *
1019fido_cred_display_name(const fido_cred_t *cred)
1020{
1021 return (cred->user.display_name);
1022}
1023
1024const unsigned char *
1025fido_cred_user_id_ptr(const fido_cred_t *cred)
1026{
1027 return (cred->user.id.ptr);
1028}
1029
1030size_t
1031fido_cred_user_id_len(const fido_cred_t *cred)
1032{
1033 return (cred->user.id.len);
1034}
diff --git a/src/credman.c b/src/credman.c
new file mode 100644
index 0000000..76327e5
--- /dev/null
+++ b/src/credman.c
@@ -0,0 +1,736 @@
1/*
2 * Copyright (c) 2019 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <openssl/sha.h>
8
9#include <string.h>
10
11#include "fido.h"
12#include "fido/credman.h"
13#include "fido/es256.h"
14
15#define CMD_CRED_METADATA 0x01
16#define CMD_RP_BEGIN 0x02
17#define CMD_RP_NEXT 0x03
18#define CMD_RK_BEGIN 0x04
19#define CMD_RK_NEXT 0x05
20#define CMD_DELETE_CRED 0x06
21
22static int
23credman_grow_array(void **ptr, size_t *n_alloc, size_t *n_rx, size_t n,
24 size_t size)
25{
26 void *new_ptr;
27
28#ifdef FIDO_FUZZ
29 if (n > UINT8_MAX) {
30 fido_log_debug("%s: n > UINT8_MAX", __func__);
31 return (-1);
32 }
33#endif
34
35 if (n < *n_alloc)
36 return (0);
37
38 /* sanity check */
39 if (*n_rx > 0 || *n_rx > *n_alloc || n < *n_alloc) {
40 fido_log_debug("%s: n=%zu, n_rx=%zu, n_alloc=%zu", __func__, n,
41 *n_rx, *n_alloc);
42 return (-1);
43 }
44
45 if ((new_ptr = recallocarray(*ptr, *n_alloc, n, size)) == NULL)
46 return (-1);
47
48 *ptr = new_ptr;
49 *n_alloc = n;
50
51 return (0);
52}
53
54static int
55credman_prepare_hmac(uint8_t cmd, const fido_blob_t *body, cbor_item_t **param,
56 fido_blob_t *hmac_data)
57{
58 cbor_item_t *param_cbor[2];
59 size_t n;
60 int ok = -1;
61
62 memset(&param_cbor, 0, sizeof(param_cbor));
63
64 if (body == NULL)
65 return (fido_blob_set(hmac_data, &cmd, sizeof(cmd)));
66
67 switch (cmd) {
68 case CMD_RK_BEGIN:
69 n = 1;
70 param_cbor[n - 1] = fido_blob_encode(body);
71 break;
72 case CMD_DELETE_CRED:
73 n = 2;
74 param_cbor[n - 1] = cbor_encode_pubkey(body);
75 break;
76 default:
77 fido_log_debug("%s: unknown cmd=0x%02x", __func__, cmd);
78 return (-1);
79 }
80
81 if (param_cbor[n - 1] == NULL) {
82 fido_log_debug("%s: cbor encode", __func__);
83 return (-1);
84 }
85 if ((*param = cbor_flatten_vector(param_cbor, n)) == NULL) {
86 fido_log_debug("%s: cbor_flatten_vector", __func__);
87 goto fail;
88 }
89 if (cbor_build_frame(cmd, param_cbor, n, hmac_data) < 0) {
90 fido_log_debug("%s: cbor_build_frame", __func__);
91 goto fail;
92 }
93
94 ok = 0;
95fail:
96 cbor_vector_free(param_cbor, nitems(param_cbor));
97
98 return (ok);
99}
100
101static int
102credman_tx(fido_dev_t *dev, uint8_t cmd, const fido_blob_t *param,
103 const char *pin)
104{
105 fido_blob_t f;
106 fido_blob_t *ecdh = NULL;
107 fido_blob_t hmac;
108 es256_pk_t *pk = NULL;
109 cbor_item_t *argv[4];
110 int r = FIDO_ERR_INTERNAL;
111
112 memset(&f, 0, sizeof(f));
113 memset(&hmac, 0, sizeof(hmac));
114 memset(&argv, 0, sizeof(argv));
115
116 /* subCommand */
117 if ((argv[0] = cbor_build_uint8(cmd)) == NULL) {
118 fido_log_debug("%s: cbor encode", __func__);
119 goto fail;
120 }
121
122 /* pinProtocol, pinAuth */
123 if (pin != NULL) {
124 if (credman_prepare_hmac(cmd, param, &argv[1], &hmac) < 0) {
125 fido_log_debug("%s: credman_prepare_hmac", __func__);
126 goto fail;
127 }
128 if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
129 fido_log_debug("%s: fido_do_ecdh", __func__);
130 goto fail;
131 }
132 if ((r = cbor_add_pin_params(dev, &hmac, pk, ecdh, pin,
133 &argv[3], &argv[2])) != FIDO_OK) {
134 fido_log_debug("%s: cbor_add_pin_params", __func__);
135 goto fail;
136 }
137 }
138
139 /* framing and transmission */
140 if (cbor_build_frame(CTAP_CBOR_CRED_MGMT_PRE, argv, 4, &f) < 0 ||
141 fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
142 fido_log_debug("%s: fido_tx", __func__);
143 r = FIDO_ERR_TX;
144 goto fail;
145 }
146
147 r = FIDO_OK;
148fail:
149 es256_pk_free(&pk);
150 fido_blob_free(&ecdh);
151 cbor_vector_free(argv, nitems(argv));
152 free(f.ptr);
153 free(hmac.ptr);
154
155 return (r);
156}
157
158static int
159credman_parse_metadata(const cbor_item_t *key, const cbor_item_t *val,
160 void *arg)
161{
162 fido_credman_metadata_t *metadata = arg;
163
164 if (cbor_isa_uint(key) == false ||
165 cbor_int_get_width(key) != CBOR_INT_8) {
166 fido_log_debug("%s: cbor type", __func__);
167 return (0); /* ignore */
168 }
169
170 switch (cbor_get_uint8(key)) {
171 case 1:
172 return (cbor_decode_uint64(val, &metadata->rk_existing));
173 case 2:
174 return (cbor_decode_uint64(val, &metadata->rk_remaining));
175 default:
176 fido_log_debug("%s: cbor type", __func__);
177 return (0); /* ignore */
178 }
179}
180
181static int
182credman_rx_metadata(fido_dev_t *dev, fido_credman_metadata_t *metadata, int ms)
183{
184 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
185 unsigned char reply[512];
186 int reply_len;
187 int r;
188
189 memset(metadata, 0, sizeof(*metadata));
190
191 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
192 fido_log_debug("%s: fido_rx", __func__);
193 return (FIDO_ERR_RX);
194 }
195
196 if ((r = cbor_parse_reply(reply, (size_t)reply_len, metadata,
197 credman_parse_metadata)) != FIDO_OK) {
198 fido_log_debug("%s: credman_parse_metadata", __func__);
199 return (r);
200 }
201
202 return (FIDO_OK);
203}
204
205static int
206credman_get_metadata_wait(fido_dev_t *dev, fido_credman_metadata_t *metadata,
207 const char *pin, int ms)
208{
209 int r;
210
211 if ((r = credman_tx(dev, CMD_CRED_METADATA, NULL, pin)) != FIDO_OK ||
212 (r = credman_rx_metadata(dev, metadata, ms)) != FIDO_OK)
213 return (r);
214
215 return (FIDO_OK);
216}
217
218int
219fido_credman_get_dev_metadata(fido_dev_t *dev, fido_credman_metadata_t *metadata,
220 const char *pin)
221{
222 if (fido_dev_is_fido2(dev) == false)
223 return (FIDO_ERR_INVALID_COMMAND);
224 if (pin == NULL)
225 return (FIDO_ERR_INVALID_ARGUMENT);
226
227 return (credman_get_metadata_wait(dev, metadata, pin, -1));
228}
229
230static int
231credman_parse_rk(const cbor_item_t *key, const cbor_item_t *val, void *arg)
232{
233 fido_cred_t *cred = arg;
234
235 if (cbor_isa_uint(key) == false ||
236 cbor_int_get_width(key) != CBOR_INT_8) {
237 fido_log_debug("%s: cbor type", __func__);
238 return (0); /* ignore */
239 }
240
241 switch (cbor_get_uint8(key)) {
242 case 6: /* user entity */
243 return (cbor_decode_user(val, &cred->user));
244 case 7:
245 return (cbor_decode_cred_id(val, &cred->attcred.id));
246 case 8:
247 if (cbor_decode_pubkey(val, &cred->attcred.type,
248 &cred->attcred.pubkey) < 0)
249 return (-1);
250 cred->type = cred->attcred.type; /* XXX */
251 return (0);
252 default:
253 fido_log_debug("%s: cbor type", __func__);
254 return (0); /* ignore */
255 }
256}
257
258static void
259credman_reset_rk(fido_credman_rk_t *rk)
260{
261 for (size_t i = 0; i < rk->n_alloc; i++) {
262 fido_cred_reset_tx(&rk->ptr[i]);
263 fido_cred_reset_rx(&rk->ptr[i]);
264 }
265
266 free(rk->ptr);
267 rk->ptr = NULL;
268 memset(rk, 0, sizeof(*rk));
269}
270
271static int
272credman_parse_rk_count(const cbor_item_t *key, const cbor_item_t *val,
273 void *arg)
274{
275 fido_credman_rk_t *rk = arg;
276 uint64_t n;
277
278 /* totalCredentials */
279 if (cbor_isa_uint(key) == false ||
280 cbor_int_get_width(key) != CBOR_INT_8 ||
281 cbor_get_uint8(key) != 9) {
282 fido_log_debug("%s: cbor_type", __func__);
283 return (0); /* ignore */
284 }
285
286 if (cbor_decode_uint64(val, &n) < 0 || n > SIZE_MAX) {
287 fido_log_debug("%s: cbor_decode_uint64", __func__);
288 return (-1);
289 }
290
291 if (credman_grow_array((void **)&rk->ptr, &rk->n_alloc, &rk->n_rx,
292 (size_t)n, sizeof(*rk->ptr)) < 0) {
293 fido_log_debug("%s: credman_grow_array", __func__);
294 return (-1);
295 }
296
297 return (0);
298}
299
300static int
301credman_rx_rk(fido_dev_t *dev, fido_credman_rk_t *rk, int ms)
302{
303 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
304 unsigned char reply[2048];
305 int reply_len;
306 int r;
307
308 credman_reset_rk(rk);
309
310 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
311 fido_log_debug("%s: fido_rx", __func__);
312 return (FIDO_ERR_RX);
313 }
314
315 /* adjust as needed */
316 if ((r = cbor_parse_reply(reply, (size_t)reply_len, rk,
317 credman_parse_rk_count)) != FIDO_OK) {
318 fido_log_debug("%s: credman_parse_rk_count", __func__);
319 return (r);
320 }
321
322 if (rk->n_alloc == 0) {
323 fido_log_debug("%s: n_alloc=0", __func__);
324 return (FIDO_OK);
325 }
326
327 /* parse the first rk */
328 if ((r = cbor_parse_reply(reply, (size_t)reply_len, &rk->ptr[0],
329 credman_parse_rk)) != FIDO_OK) {
330 fido_log_debug("%s: credman_parse_rk", __func__);
331 return (r);
332 }
333
334 rk->n_rx++;
335
336 return (FIDO_OK);
337}
338
339static int
340credman_rx_next_rk(fido_dev_t *dev, fido_credman_rk_t *rk, int ms)
341{
342 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
343 unsigned char reply[2048];
344 int reply_len;
345 int r;
346
347 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
348 fido_log_debug("%s: fido_rx", __func__);
349 return (FIDO_ERR_RX);
350 }
351
352 /* sanity check */
353 if (rk->n_rx >= rk->n_alloc) {
354 fido_log_debug("%s: n_rx=%zu, n_alloc=%zu", __func__, rk->n_rx,
355 rk->n_alloc);
356 return (FIDO_ERR_INTERNAL);
357 }
358
359 if ((r = cbor_parse_reply(reply, (size_t)reply_len, &rk->ptr[rk->n_rx],
360 credman_parse_rk)) != FIDO_OK) {
361 fido_log_debug("%s: credman_parse_rk", __func__);
362 return (r);
363 }
364
365 return (FIDO_OK);
366}
367
368static int
369credman_get_rk_wait(fido_dev_t *dev, const char *rp_id, fido_credman_rk_t *rk,
370 const char *pin, int ms)
371{
372 fido_blob_t rp_dgst;
373 uint8_t dgst[SHA256_DIGEST_LENGTH];
374 int r;
375
376 if (SHA256((const unsigned char *)rp_id, strlen(rp_id), dgst) != dgst) {
377 fido_log_debug("%s: sha256", __func__);
378 return (FIDO_ERR_INTERNAL);
379 }
380
381 rp_dgst.ptr = dgst;
382 rp_dgst.len = sizeof(dgst);
383
384 if ((r = credman_tx(dev, CMD_RK_BEGIN, &rp_dgst, pin)) != FIDO_OK ||
385 (r = credman_rx_rk(dev, rk, ms)) != FIDO_OK)
386 return (r);
387
388 while (rk->n_rx < rk->n_alloc) {
389 if ((r = credman_tx(dev, CMD_RK_NEXT, NULL, NULL)) != FIDO_OK ||
390 (r = credman_rx_next_rk(dev, rk, ms)) != FIDO_OK)
391 return (r);
392 rk->n_rx++;
393 }
394
395 return (FIDO_OK);
396}
397
398int
399fido_credman_get_dev_rk(fido_dev_t *dev, const char *rp_id,
400 fido_credman_rk_t *rk, const char *pin)
401{
402 if (fido_dev_is_fido2(dev) == false)
403 return (FIDO_ERR_INVALID_COMMAND);
404 if (pin == NULL)
405 return (FIDO_ERR_INVALID_ARGUMENT);
406
407 return (credman_get_rk_wait(dev, rp_id, rk, pin, -1));
408}
409
410static int
411credman_del_rk_wait(fido_dev_t *dev, const unsigned char *cred_id,
412 size_t cred_id_len, const char *pin, int ms)
413{
414 fido_blob_t cred;
415 int r;
416
417 memset(&cred, 0, sizeof(cred));
418
419 if (fido_blob_set(&cred, cred_id, cred_id_len) < 0)
420 return (FIDO_ERR_INVALID_ARGUMENT);
421
422 if ((r = credman_tx(dev, CMD_DELETE_CRED, &cred, pin)) != FIDO_OK ||
423 (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK)
424 goto fail;
425
426 r = FIDO_OK;
427fail:
428 free(cred.ptr);
429
430 return (r);
431}
432
433int
434fido_credman_del_dev_rk(fido_dev_t *dev, const unsigned char *cred_id,
435 size_t cred_id_len, const char *pin)
436{
437 if (fido_dev_is_fido2(dev) == false)
438 return (FIDO_ERR_INVALID_COMMAND);
439 if (pin == NULL)
440 return (FIDO_ERR_INVALID_ARGUMENT);
441
442 return (credman_del_rk_wait(dev, cred_id, cred_id_len, pin, -1));
443}
444
445static int
446credman_parse_rp(const cbor_item_t *key, const cbor_item_t *val, void *arg)
447{
448 struct fido_credman_single_rp *rp = arg;
449
450 if (cbor_isa_uint(key) == false ||
451 cbor_int_get_width(key) != CBOR_INT_8) {
452 fido_log_debug("%s: cbor type", __func__);
453 return (0); /* ignore */
454 }
455
456 switch (cbor_get_uint8(key)) {
457 case 3:
458 return (cbor_decode_rp_entity(val, &rp->rp_entity));
459 case 4:
460 return (fido_blob_decode(val, &rp->rp_id_hash));
461 default:
462 fido_log_debug("%s: cbor type", __func__);
463 return (0); /* ignore */
464 }
465}
466
467static void
468credman_reset_rp(fido_credman_rp_t *rp)
469{
470 for (size_t i = 0; i < rp->n_alloc; i++) {
471 free(rp->ptr[i].rp_entity.id);
472 free(rp->ptr[i].rp_entity.name);
473 rp->ptr[i].rp_entity.id = NULL;
474 rp->ptr[i].rp_entity.name = NULL;
475 free(rp->ptr[i].rp_id_hash.ptr);
476 memset(&rp->ptr[i].rp_id_hash, 0,
477 sizeof(rp->ptr[i].rp_id_hash));
478 }
479
480 free(rp->ptr);
481 rp->ptr = NULL;
482 memset(rp, 0, sizeof(*rp));
483}
484
485static int
486credman_parse_rp_count(const cbor_item_t *key, const cbor_item_t *val,
487 void *arg)
488{
489 fido_credman_rp_t *rp = arg;
490 uint64_t n;
491
492 /* totalRPs */
493 if (cbor_isa_uint(key) == false ||
494 cbor_int_get_width(key) != CBOR_INT_8 ||
495 cbor_get_uint8(key) != 5) {
496 fido_log_debug("%s: cbor_type", __func__);
497 return (0); /* ignore */
498 }
499
500 if (cbor_decode_uint64(val, &n) < 0 || n > SIZE_MAX) {
501 fido_log_debug("%s: cbor_decode_uint64", __func__);
502 return (-1);
503 }
504
505 if (credman_grow_array((void **)&rp->ptr, &rp->n_alloc, &rp->n_rx,
506 (size_t)n, sizeof(*rp->ptr)) < 0) {
507 fido_log_debug("%s: credman_grow_array", __func__);
508 return (-1);
509 }
510
511 return (0);
512}
513
514static int
515credman_rx_rp(fido_dev_t *dev, fido_credman_rp_t *rp, int ms)
516{
517 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
518 unsigned char reply[2048];
519 int reply_len;
520 int r;
521
522 credman_reset_rp(rp);
523
524 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
525 fido_log_debug("%s: fido_rx", __func__);
526 return (FIDO_ERR_RX);
527 }
528
529 /* adjust as needed */
530 if ((r = cbor_parse_reply(reply, (size_t)reply_len, rp,
531 credman_parse_rp_count)) != FIDO_OK) {
532 fido_log_debug("%s: credman_parse_rp_count", __func__);
533 return (r);
534 }
535
536 if (rp->n_alloc == 0) {
537 fido_log_debug("%s: n_alloc=0", __func__);
538 return (FIDO_OK);
539 }
540
541 /* parse the first rp */
542 if ((r = cbor_parse_reply(reply, (size_t)reply_len, &rp->ptr[0],
543 credman_parse_rp)) != FIDO_OK) {
544 fido_log_debug("%s: credman_parse_rp", __func__);
545 return (r);
546 }
547
548 rp->n_rx++;
549
550 return (FIDO_OK);
551}
552
553static int
554credman_rx_next_rp(fido_dev_t *dev, fido_credman_rp_t *rp, int ms)
555{
556 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
557 unsigned char reply[2048];
558 int reply_len;
559 int r;
560
561 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
562 fido_log_debug("%s: fido_rx", __func__);
563 return (FIDO_ERR_RX);
564 }
565
566 /* sanity check */
567 if (rp->n_rx >= rp->n_alloc) {
568 fido_log_debug("%s: n_rx=%zu, n_alloc=%zu", __func__, rp->n_rx,
569 rp->n_alloc);
570 return (FIDO_ERR_INTERNAL);
571 }
572
573 if ((r = cbor_parse_reply(reply, (size_t)reply_len, &rp->ptr[rp->n_rx],
574 credman_parse_rp)) != FIDO_OK) {
575 fido_log_debug("%s: credman_parse_rp", __func__);
576 return (r);
577 }
578
579 return (FIDO_OK);
580}
581
582static int
583credman_get_rp_wait(fido_dev_t *dev, fido_credman_rp_t *rp, const char *pin,
584 int ms)
585{
586 int r;
587
588 if ((r = credman_tx(dev, CMD_RP_BEGIN, NULL, pin)) != FIDO_OK ||
589 (r = credman_rx_rp(dev, rp, ms)) != FIDO_OK)
590 return (r);
591
592 while (rp->n_rx < rp->n_alloc) {
593 if ((r = credman_tx(dev, CMD_RP_NEXT, NULL, NULL)) != FIDO_OK ||
594 (r = credman_rx_next_rp(dev, rp, ms)) != FIDO_OK)
595 return (r);
596 rp->n_rx++;
597 }
598
599 return (FIDO_OK);
600}
601
602int
603fido_credman_get_dev_rp(fido_dev_t *dev, fido_credman_rp_t *rp, const char *pin)
604{
605 if (fido_dev_is_fido2(dev) == false)
606 return (FIDO_ERR_INVALID_COMMAND);
607 if (pin == NULL)
608 return (FIDO_ERR_INVALID_ARGUMENT);
609
610 return (credman_get_rp_wait(dev, rp, pin, -1));
611}
612
613fido_credman_rk_t *
614fido_credman_rk_new(void)
615{
616 return (calloc(1, sizeof(fido_credman_rk_t)));
617}
618
619void
620fido_credman_rk_free(fido_credman_rk_t **rk_p)
621{
622 fido_credman_rk_t *rk;
623
624 if (rk_p == NULL || (rk = *rk_p) == NULL)
625 return;
626
627 credman_reset_rk(rk);
628 free(rk);
629 *rk_p = NULL;
630}
631
632size_t
633fido_credman_rk_count(const fido_credman_rk_t *rk)
634{
635 return (rk->n_rx);
636}
637
638const fido_cred_t *
639fido_credman_rk(const fido_credman_rk_t *rk, size_t idx)
640{
641 if (idx >= rk->n_alloc)
642 return (NULL);
643
644 return (&rk->ptr[idx]);
645}
646
647fido_credman_metadata_t *
648fido_credman_metadata_new(void)
649{
650 return (calloc(1, sizeof(fido_credman_metadata_t)));
651}
652
653void
654fido_credman_metadata_free(fido_credman_metadata_t **metadata_p)
655{
656 fido_credman_metadata_t *metadata;
657
658 if (metadata_p == NULL || (metadata = *metadata_p) == NULL)
659 return;
660
661 free(metadata);
662 *metadata_p = NULL;
663}
664
665uint64_t
666fido_credman_rk_existing(const fido_credman_metadata_t *metadata)
667{
668 return (metadata->rk_existing);
669}
670
671uint64_t
672fido_credman_rk_remaining(const fido_credman_metadata_t *metadata)
673{
674 return (metadata->rk_remaining);
675}
676
677fido_credman_rp_t *
678fido_credman_rp_new(void)
679{
680 return (calloc(1, sizeof(fido_credman_rp_t)));
681}
682
683void
684fido_credman_rp_free(fido_credman_rp_t **rp_p)
685{
686 fido_credman_rp_t *rp;
687
688 if (rp_p == NULL || (rp = *rp_p) == NULL)
689 return;
690
691 credman_reset_rp(rp);
692 free(rp);
693 *rp_p = NULL;
694}
695
696size_t
697fido_credman_rp_count(const fido_credman_rp_t *rp)
698{
699 return (rp->n_rx);
700}
701
702const char *
703fido_credman_rp_id(const fido_credman_rp_t *rp, size_t idx)
704{
705 if (idx >= rp->n_alloc)
706 return (NULL);
707
708 return (rp->ptr[idx].rp_entity.id);
709}
710
711const char *
712fido_credman_rp_name(const fido_credman_rp_t *rp, size_t idx)
713{
714 if (idx >= rp->n_alloc)
715 return (NULL);
716
717 return (rp->ptr[idx].rp_entity.name);
718}
719
720size_t
721fido_credman_rp_id_hash_len(const fido_credman_rp_t *rp, size_t idx)
722{
723 if (idx >= rp->n_alloc)
724 return (0);
725
726 return (rp->ptr[idx].rp_id_hash.len);
727}
728
729const unsigned char *
730fido_credman_rp_id_hash_ptr(const fido_credman_rp_t *rp, size_t idx)
731{
732 if (idx >= rp->n_alloc)
733 return (NULL);
734
735 return (rp->ptr[idx].rp_id_hash.ptr);
736}
diff --git a/src/dev.c b/src/dev.c
new file mode 100644
index 0000000..d0efac7
--- /dev/null
+++ b/src/dev.c
@@ -0,0 +1,284 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <sys/types.h>
8#include <sys/stat.h>
9
10#include <fcntl.h>
11#include <stdint.h>
12#include <stdlib.h>
13#include <string.h>
14#ifdef HAVE_UNISTD_H
15#include <unistd.h>
16#endif
17
18#include "fido.h"
19
20#if defined(_WIN32)
21#include <windows.h>
22
23#include <winternl.h>
24#include <winerror.h>
25#include <stdio.h>
26#include <bcrypt.h>
27#include <sal.h>
28
29static int
30obtain_nonce(uint64_t *nonce)
31{
32 NTSTATUS status;
33
34 status = BCryptGenRandom(NULL, (unsigned char *)nonce, sizeof(*nonce),
35 BCRYPT_USE_SYSTEM_PREFERRED_RNG);
36
37 if (!NT_SUCCESS(status))
38 return (-1);
39
40 return (0);
41}
42#elif defined(HAS_DEV_URANDOM)
43static int
44obtain_nonce(uint64_t *nonce)
45{
46 int fd = -1;
47 int ok = -1;
48 ssize_t r;
49
50 if ((fd = open(FIDO_RANDOM_DEV, O_RDONLY)) < 0)
51 goto fail;
52 if ((r = read(fd, nonce, sizeof(*nonce))) < 0 ||
53 (size_t)r != sizeof(*nonce))
54 goto fail;
55
56 ok = 0;
57fail:
58 if (fd != -1)
59 close(fd);
60
61 return (ok);
62}
63#else
64#error "please provide an implementation of obtain_nonce() for your platform"
65#endif /* _WIN32 */
66
67static int
68fido_dev_open_tx(fido_dev_t *dev, const char *path)
69{
70 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_INIT;
71
72 if (dev->io_handle != NULL) {
73 fido_log_debug("%s: handle=%p", __func__, dev->io_handle);
74 return (FIDO_ERR_INVALID_ARGUMENT);
75 }
76
77 if (dev->io.open == NULL || dev->io.close == NULL) {
78 fido_log_debug("%s: NULL open/close", __func__);
79 return (FIDO_ERR_INVALID_ARGUMENT);
80 }
81
82 if (obtain_nonce(&dev->nonce) < 0) {
83 fido_log_debug("%s: obtain_nonce", __func__);
84 return (FIDO_ERR_INTERNAL);
85 }
86
87 if ((dev->io_handle = dev->io.open(path)) == NULL) {
88 fido_log_debug("%s: dev->io.open", __func__);
89 return (FIDO_ERR_INTERNAL);
90 }
91
92 if (fido_tx(dev, cmd, &dev->nonce, sizeof(dev->nonce)) < 0) {
93 fido_log_debug("%s: fido_tx", __func__);
94 dev->io.close(dev->io_handle);
95 dev->io_handle = NULL;
96 return (FIDO_ERR_TX);
97 }
98
99 return (FIDO_OK);
100}
101
102static int
103fido_dev_open_rx(fido_dev_t *dev, int ms)
104{
105 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_INIT;
106 int n;
107
108 if ((n = fido_rx(dev, cmd, &dev->attr, sizeof(dev->attr), ms)) < 0) {
109 fido_log_debug("%s: fido_rx", __func__);
110 goto fail;
111 }
112
113#ifdef FIDO_FUZZ
114 dev->attr.nonce = dev->nonce;
115#endif
116
117 if ((size_t)n != sizeof(dev->attr) || dev->attr.nonce != dev->nonce) {
118 fido_log_debug("%s: invalid nonce", __func__);
119 goto fail;
120 }
121
122 dev->cid = dev->attr.cid;
123
124 return (FIDO_OK);
125fail:
126 dev->io.close(dev->io_handle);
127 dev->io_handle = NULL;
128
129 return (FIDO_ERR_RX);
130}
131
132static int
133fido_dev_open_wait(fido_dev_t *dev, const char *path, int ms)
134{
135 int r;
136
137 if ((r = fido_dev_open_tx(dev, path)) != FIDO_OK ||
138 (r = fido_dev_open_rx(dev, ms)) != FIDO_OK)
139 return (r);
140
141 return (FIDO_OK);
142}
143
144int
145fido_dev_open(fido_dev_t *dev, const char *path)
146{
147 return (fido_dev_open_wait(dev, path, -1));
148}
149
150int
151fido_dev_close(fido_dev_t *dev)
152{
153 if (dev->io_handle == NULL || dev->io.close == NULL)
154 return (FIDO_ERR_INVALID_ARGUMENT);
155
156 dev->io.close(dev->io_handle);
157 dev->io_handle = NULL;
158
159 return (FIDO_OK);
160}
161
162int
163fido_dev_cancel(fido_dev_t *dev)
164{
165 if (fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CANCEL, NULL, 0) < 0)
166 return (FIDO_ERR_TX);
167
168 return (FIDO_OK);
169}
170
171int
172fido_dev_set_io_functions(fido_dev_t *dev, const fido_dev_io_t *io)
173{
174 if (dev->io_handle != NULL) {
175 fido_log_debug("%s: NULL handle", __func__);
176 return (FIDO_ERR_INVALID_ARGUMENT);
177 }
178
179 if (io == NULL || io->open == NULL || io->close == NULL ||
180 io->read == NULL || io->write == NULL) {
181 fido_log_debug("%s: NULL function", __func__);
182 return (FIDO_ERR_INVALID_ARGUMENT);
183 }
184
185 dev->io.open = io->open;
186 dev->io.close = io->close;
187 dev->io.read = io->read;
188 dev->io.write = io->write;
189
190 return (FIDO_OK);
191}
192
193void
194fido_init(int flags)
195{
196 if (flags & FIDO_DEBUG || getenv("FIDO_DEBUG") != NULL)
197 fido_log_init();
198}
199
200fido_dev_t *
201fido_dev_new(void)
202{
203 fido_dev_t *dev;
204 fido_dev_io_t io;
205
206 if ((dev = calloc(1, sizeof(*dev))) == NULL)
207 return (NULL);
208
209 dev->cid = CTAP_CID_BROADCAST;
210
211 io.open = fido_hid_open;
212 io.close = fido_hid_close;
213 io.read = fido_hid_read;
214 io.write = fido_hid_write;
215
216 if (fido_dev_set_io_functions(dev, &io) != FIDO_OK) {
217 fido_log_debug("%s: fido_dev_set_io_functions", __func__);
218 fido_dev_free(&dev);
219 return (NULL);
220 }
221
222 return (dev);
223}
224
225void
226fido_dev_free(fido_dev_t **dev_p)
227{
228 fido_dev_t *dev;
229
230 if (dev_p == NULL || (dev = *dev_p) == NULL)
231 return;
232
233 free(dev);
234
235 *dev_p = NULL;
236}
237
238uint8_t
239fido_dev_protocol(const fido_dev_t *dev)
240{
241 return (dev->attr.protocol);
242}
243
244uint8_t
245fido_dev_major(const fido_dev_t *dev)
246{
247 return (dev->attr.major);
248}
249
250uint8_t
251fido_dev_minor(const fido_dev_t *dev)
252{
253 return (dev->attr.minor);
254}
255
256uint8_t
257fido_dev_build(const fido_dev_t *dev)
258{
259 return (dev->attr.build);
260}
261
262uint8_t
263fido_dev_flags(const fido_dev_t *dev)
264{
265 return (dev->attr.flags);
266}
267
268bool
269fido_dev_is_fido2(const fido_dev_t *dev)
270{
271 return (dev->attr.flags & FIDO_CAP_CBOR);
272}
273
274void
275fido_dev_force_u2f(fido_dev_t *dev)
276{
277 dev->attr.flags &= ~FIDO_CAP_CBOR;
278}
279
280void
281fido_dev_force_fido2(fido_dev_t *dev)
282{
283 dev->attr.flags |= FIDO_CAP_CBOR;
284}
diff --git a/src/diff_exports.sh b/src/diff_exports.sh
new file mode 100755
index 0000000..7920f47
--- /dev/null
+++ b/src/diff_exports.sh
@@ -0,0 +1,23 @@
1#!/bin/bash -u
2
3# Copyright (c) 2018 Yubico AB. All rights reserved.
4# Use of this source code is governed by a BSD-style
5# license that can be found in the LICENSE file.
6
7[[ ! -f export.gnu || ! -f export.llvm || ! -f export.msvc ]] && exit 1
8
9TMPDIR=$(mktemp -d)
10GNU=${TMPDIR}/gnu
11LLVM=${TMPDIR}/llvm
12MSVC=${TMPDIR}/msvc
13
14egrep -o $'([^*{}\t]+);$' export.gnu | tr -d ';' | sort > ${GNU}
15sed 's/^_//g' export.llvm | sort > ${LLVM}
16egrep -v "^EXPORTS$" export.msvc | sort > ${MSVC}
17diff -u ${GNU} ${LLVM} && diff -u ${MSVC} ${LLVM}
18ERROR=$?
19
20rm ${GNU} ${LLVM} ${MSVC}
21rmdir ${TMPDIR}
22
23exit ${ERROR}
diff --git a/src/ecdh.c b/src/ecdh.c
new file mode 100644
index 0000000..7f25c7b
--- /dev/null
+++ b/src/ecdh.c
@@ -0,0 +1,121 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <openssl/evp.h>
8#include <openssl/sha.h>
9
10#include "fido.h"
11#include "fido/es256.h"
12
13static int
14do_ecdh(const es256_sk_t *sk, const es256_pk_t *pk, fido_blob_t **ecdh)
15{
16 EVP_PKEY *pk_evp = NULL;
17 EVP_PKEY *sk_evp = NULL;
18 EVP_PKEY_CTX *ctx = NULL;
19 fido_blob_t *secret = NULL;
20 int ok = -1;
21
22 *ecdh = NULL;
23
24 /* allocate blobs for secret & ecdh */
25 if ((secret = fido_blob_new()) == NULL ||
26 (*ecdh = fido_blob_new()) == NULL)
27 goto fail;
28
29 /* wrap the keys as openssl objects */
30 if ((pk_evp = es256_pk_to_EVP_PKEY(pk)) == NULL ||
31 (sk_evp = es256_sk_to_EVP_PKEY(sk)) == NULL) {
32 fido_log_debug("%s: es256_to_EVP_PKEY", __func__);
33 goto fail;
34 }
35
36 /* set ecdh parameters */
37 if ((ctx = EVP_PKEY_CTX_new(sk_evp, NULL)) == NULL ||
38 EVP_PKEY_derive_init(ctx) <= 0 ||
39 EVP_PKEY_derive_set_peer(ctx, pk_evp) <= 0) {
40 fido_log_debug("%s: EVP_PKEY_derive_init", __func__);
41 goto fail;
42 }
43
44 /* perform ecdh */
45 if (EVP_PKEY_derive(ctx, NULL, &secret->len) <= 0 ||
46 (secret->ptr = calloc(1, secret->len)) == NULL ||
47 EVP_PKEY_derive(ctx, secret->ptr, &secret->len) <= 0) {
48 fido_log_debug("%s: EVP_PKEY_derive", __func__);
49 goto fail;
50 }
51
52 /* use sha256 as a kdf on the resulting secret */
53 (*ecdh)->len = SHA256_DIGEST_LENGTH;
54 if (((*ecdh)->ptr = calloc(1, (*ecdh)->len)) == NULL ||
55 SHA256(secret->ptr, secret->len, (*ecdh)->ptr) != (*ecdh)->ptr) {
56 fido_log_debug("%s: sha256", __func__);
57 goto fail;
58 }
59
60 ok = 0;
61fail:
62 if (pk_evp != NULL)
63 EVP_PKEY_free(pk_evp);
64 if (sk_evp != NULL)
65 EVP_PKEY_free(sk_evp);
66 if (ctx != NULL)
67 EVP_PKEY_CTX_free(ctx);
68 if (ok < 0)
69 fido_blob_free(ecdh);
70
71 fido_blob_free(&secret);
72
73 return (ok);
74}
75
76int
77fido_do_ecdh(fido_dev_t *dev, es256_pk_t **pk, fido_blob_t **ecdh)
78{
79 es256_sk_t *sk = NULL; /* our private key */
80 es256_pk_t *ak = NULL; /* authenticator's public key */
81 int r;
82
83 *pk = NULL; /* our public key; returned */
84 *ecdh = NULL; /* shared ecdh secret; returned */
85
86 if ((sk = es256_sk_new()) == NULL || (*pk = es256_pk_new()) == NULL) {
87 r = FIDO_ERR_INTERNAL;
88 goto fail;
89 }
90
91 if (es256_sk_create(sk) < 0 || es256_derive_pk(sk, *pk) < 0) {
92 fido_log_debug("%s: es256_derive_pk", __func__);
93 r = FIDO_ERR_INTERNAL;
94 goto fail;
95 }
96
97 if ((ak = es256_pk_new()) == NULL ||
98 fido_dev_authkey(dev, ak) != FIDO_OK) {
99 fido_log_debug("%s: fido_dev_authkey", __func__);
100 r = FIDO_ERR_INTERNAL;
101 goto fail;
102 }
103
104 if (do_ecdh(sk, ak, ecdh) < 0) {
105 fido_log_debug("%s: do_ecdh", __func__);
106 r = FIDO_ERR_INTERNAL;
107 goto fail;
108 }
109
110 r = FIDO_OK;
111fail:
112 es256_sk_free(&sk);
113 es256_pk_free(&ak);
114
115 if (r != FIDO_OK) {
116 es256_pk_free(pk);
117 fido_blob_free(ecdh);
118 }
119
120 return (r);
121}
diff --git a/src/eddsa.c b/src/eddsa.c
new file mode 100644
index 0000000..92a0222
--- /dev/null
+++ b/src/eddsa.c
@@ -0,0 +1,169 @@
1/*
2 * Copyright (c) 2019 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <openssl/bn.h>
8#include <openssl/ec.h>
9#include <openssl/evp.h>
10#include <openssl/obj_mac.h>
11
12#include <string.h>
13#include "fido.h"
14#include "fido/eddsa.h"
15
16#if defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10101000L
17EVP_PKEY *
18EVP_PKEY_new_raw_public_key(int type, ENGINE *e, const unsigned char *key,
19 size_t keylen)
20{
21 (void)type;
22 (void)e;
23 (void)key;
24 (void)keylen;
25
26 return (NULL);
27}
28
29int
30EVP_PKEY_get_raw_public_key(const EVP_PKEY *pkey, unsigned char *pub,
31 size_t *len)
32{
33 (void)pkey;
34 (void)pub;
35 (void)len;
36
37 return (0);
38}
39
40int
41EVP_DigestVerify(EVP_MD_CTX *ctx, const unsigned char *sigret, size_t siglen,
42 const unsigned char *tbs, size_t tbslen)
43{
44 (void)ctx;
45 (void)sigret;
46 (void)siglen;
47 (void)tbs;
48 (void)tbslen;
49
50 return (0);
51}
52#endif /* LIBRESSL_VERSION_NUMBER || OPENSSL_VERSION_NUMBER < 0x10101000L */
53
54#if OPENSSL_VERSION_NUMBER < 0x10100000L
55EVP_MD_CTX *
56EVP_MD_CTX_new(void)
57{
58 return (NULL);
59}
60
61void
62EVP_MD_CTX_free(EVP_MD_CTX *ctx)
63{
64 (void)ctx;
65}
66#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
67
68static int
69decode_coord(const cbor_item_t *item, void *xy, size_t xy_len)
70{
71 if (cbor_isa_bytestring(item) == false ||
72 cbor_bytestring_is_definite(item) == false ||
73 cbor_bytestring_length(item) != xy_len) {
74 fido_log_debug("%s: cbor type", __func__);
75 return (-1);
76 }
77
78 memcpy(xy, cbor_bytestring_handle(item), xy_len);
79
80 return (0);
81}
82
83static int
84decode_pubkey_point(const cbor_item_t *key, const cbor_item_t *val, void *arg)
85{
86 eddsa_pk_t *k = arg;
87
88 if (cbor_isa_negint(key) == false ||
89 cbor_int_get_width(key) != CBOR_INT_8)
90 return (0); /* ignore */
91
92 switch (cbor_get_uint8(key)) {
93 case 1: /* x coordinate */
94 return (decode_coord(val, &k->x, sizeof(k->x)));
95 }
96
97 return (0); /* ignore */
98}
99
100int
101eddsa_pk_decode(const cbor_item_t *item, eddsa_pk_t *k)
102{
103 if (cbor_isa_map(item) == false ||
104 cbor_map_is_definite(item) == false ||
105 cbor_map_iter(item, k, decode_pubkey_point) < 0) {
106 fido_log_debug("%s: cbor type", __func__);
107 return (-1);
108 }
109
110 return (0);
111}
112
113eddsa_pk_t *
114eddsa_pk_new(void)
115{
116 return (calloc(1, sizeof(eddsa_pk_t)));
117}
118
119void
120eddsa_pk_free(eddsa_pk_t **pkp)
121{
122 eddsa_pk_t *pk;
123
124 if (pkp == NULL || (pk = *pkp) == NULL)
125 return;
126
127 explicit_bzero(pk, sizeof(*pk));
128 free(pk);
129
130 *pkp = NULL;
131}
132
133int
134eddsa_pk_from_ptr(eddsa_pk_t *pk, const void *ptr, size_t len)
135{
136 if (len < sizeof(*pk))
137 return (FIDO_ERR_INVALID_ARGUMENT);
138
139 memcpy(pk, ptr, sizeof(*pk));
140
141 return (FIDO_OK);
142}
143
144EVP_PKEY *
145eddsa_pk_to_EVP_PKEY(const eddsa_pk_t *k)
146{
147 EVP_PKEY *pkey = NULL;
148
149 if ((pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL, k->x,
150 sizeof(k->x))) == NULL)
151 fido_log_debug("%s: EVP_PKEY_new_raw_public_key", __func__);
152
153 return (pkey);
154}
155
156int
157eddsa_pk_from_EVP_PKEY(eddsa_pk_t *pk, const EVP_PKEY *pkey)
158{
159 size_t len = 0;
160
161 if (EVP_PKEY_get_raw_public_key(pkey, NULL, &len) != 1 ||
162 len != sizeof(pk->x))
163 return (FIDO_ERR_INTERNAL);
164 if (EVP_PKEY_get_raw_public_key(pkey, pk->x, &len) != 1 ||
165 len != sizeof(pk->x))
166 return (FIDO_ERR_INTERNAL);
167
168 return (FIDO_OK);
169}
diff --git a/src/err.c b/src/err.c
new file mode 100644
index 0000000..5d3efd4
--- /dev/null
+++ b/src/err.c
@@ -0,0 +1,122 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include "fido/err.h"
8
9const char *
10fido_strerr(int n)
11{
12 switch (n) {
13 case FIDO_ERR_SUCCESS:
14 return "FIDO_ERR_SUCCESS";
15 case FIDO_ERR_INVALID_COMMAND:
16 return "FIDO_ERR_INVALID_COMMAND";
17 case FIDO_ERR_INVALID_PARAMETER:
18 return "FIDO_ERR_INVALID_PARAMETER";
19 case FIDO_ERR_INVALID_LENGTH:
20 return "FIDO_ERR_INVALID_LENGTH";
21 case FIDO_ERR_INVALID_SEQ:
22 return "FIDO_ERR_INVALID_SEQ";
23 case FIDO_ERR_TIMEOUT:
24 return "FIDO_ERR_TIMEOUT";
25 case FIDO_ERR_CHANNEL_BUSY:
26 return "FIDO_ERR_CHANNEL_BUSY";
27 case FIDO_ERR_LOCK_REQUIRED:
28 return "FIDO_ERR_LOCK_REQUIRED";
29 case FIDO_ERR_INVALID_CHANNEL:
30 return "FIDO_ERR_INVALID_CHANNEL";
31 case FIDO_ERR_CBOR_UNEXPECTED_TYPE:
32 return "FIDO_ERR_UNEXPECTED_TYPE";
33 case FIDO_ERR_INVALID_CBOR:
34 return "FIDO_ERR_INVALID_CBOR";
35 case FIDO_ERR_MISSING_PARAMETER:
36 return "FIDO_ERR_MISSING_PARAMETER";
37 case FIDO_ERR_LIMIT_EXCEEDED:
38 return "FIDO_ERR_LIMIT_EXCEEDED";
39 case FIDO_ERR_UNSUPPORTED_EXTENSION:
40 return "FIDO_ERR_UNSUPPORTED_EXTENSION";
41 case FIDO_ERR_CREDENTIAL_EXCLUDED:
42 return "FIDO_ERR_CREDENTIAL_EXCLUDED";
43 case FIDO_ERR_PROCESSING:
44 return "FIDO_ERR_PROCESSING";
45 case FIDO_ERR_INVALID_CREDENTIAL:
46 return "FIDO_ERR_INVALID_CREDENTIAL";
47 case FIDO_ERR_USER_ACTION_PENDING:
48 return "FIDO_ERR_ACTION_PENDING";
49 case FIDO_ERR_OPERATION_PENDING:
50 return "FIDO_ERR_OPERATION_PENDING";
51 case FIDO_ERR_NO_OPERATIONS:
52 return "FIDO_ERR_NO_OPERATIONS";
53 case FIDO_ERR_UNSUPPORTED_ALGORITHM:
54 return "FIDO_ERR_UNSUPPORTED_ALGORITHM";
55 case FIDO_ERR_OPERATION_DENIED:
56 return "FIDO_ERR_OPERATION_DENIED";
57 case FIDO_ERR_KEY_STORE_FULL:
58 return "FIDO_ERR_STORE_FULL";
59 case FIDO_ERR_NOT_BUSY:
60 return "FIDO_ERR_NOT_BUSY";
61 case FIDO_ERR_NO_OPERATION_PENDING:
62 return "FIDO_ERR_OPERATION_PENDING";
63 case FIDO_ERR_UNSUPPORTED_OPTION:
64 return "FIDO_ERR_UNSUPPORTED_OPTION";
65 case FIDO_ERR_INVALID_OPTION:
66 return "FIDO_ERR_INVALID_OPTION";
67 case FIDO_ERR_KEEPALIVE_CANCEL:
68 return "FIDO_ERR_KEEPALIVE_CANCEL";
69 case FIDO_ERR_NO_CREDENTIALS:
70 return "FIDO_ERR_NO_CREDENTIALS";
71 case FIDO_ERR_USER_ACTION_TIMEOUT:
72 return "FIDO_ERR_ACTION_TIMEOUT";
73 case FIDO_ERR_NOT_ALLOWED:
74 return "FIDO_ERR_NOT_ALLOWED";
75 case FIDO_ERR_PIN_INVALID:
76 return "FIDO_ERR_PIN_INVALID";
77 case FIDO_ERR_PIN_BLOCKED:
78 return "FIDO_ERR_PIN_BLOCKED";
79 case FIDO_ERR_PIN_AUTH_INVALID:
80 return "FIDO_ERR_AUTH_INVALID";
81 case FIDO_ERR_PIN_AUTH_BLOCKED:
82 return "FIDO_ERR_AUTH_BLOCKED";
83 case FIDO_ERR_PIN_NOT_SET:
84 return "FIDO_ERR_NOT_SET";
85 case FIDO_ERR_PIN_REQUIRED:
86 return "FIDO_ERR_PIN_REQUIRED";
87 case FIDO_ERR_PIN_POLICY_VIOLATION:
88 return "FIDO_ERR_POLICY_VIOLATION";
89 case FIDO_ERR_PIN_TOKEN_EXPIRED:
90 return "FIDO_ERR_TOKEN_EXPIRED";
91 case FIDO_ERR_REQUEST_TOO_LARGE:
92 return "FIDO_ERR_TOO_LARGE";
93 case FIDO_ERR_ACTION_TIMEOUT:
94 return "FIDO_ERR_ACTION_TIMEOUT";
95 case FIDO_ERR_UP_REQUIRED:
96 return "FIDO_ERR_UP_REQUIRED";
97 case FIDO_ERR_ERR_OTHER:
98 return "FIDO_ERR_OTHER";
99 case FIDO_ERR_SPEC_LAST:
100 return "FIDO_ERR_SPEC_LAST";
101 case FIDO_ERR_TX:
102 return "FIDO_ERR_TX";
103 case FIDO_ERR_RX:
104 return "FIDO_ERR_RX";
105 case FIDO_ERR_RX_NOT_CBOR:
106 return "FIDO_ERR_RX_NOT_CBOR";
107 case FIDO_ERR_RX_INVALID_CBOR:
108 return "FIDO_ERR_RX_INVALID_CBOR";
109 case FIDO_ERR_INVALID_PARAM:
110 return "FIDO_ERR_INVALID_PARAM";
111 case FIDO_ERR_INVALID_SIG:
112 return "FIDO_ERR_INVALID_SIG";
113 case FIDO_ERR_INVALID_ARGUMENT:
114 return "FIDO_ERR_INVALID_ARGUMENT";
115 case FIDO_ERR_USER_PRESENCE_REQUIRED:
116 return "FIDO_ERR_USER_PRESENCE_REQUIRED";
117 case FIDO_ERR_INTERNAL:
118 return "FIDO_ERR_INTERNAL";
119 default:
120 return "FIDO_ERR_UNKNOWN";
121 }
122}
diff --git a/src/es256.c b/src/es256.c
new file mode 100644
index 0000000..c8fd9f4
--- /dev/null
+++ b/src/es256.c
@@ -0,0 +1,434 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <openssl/bn.h>
8#include <openssl/ec.h>
9#include <openssl/evp.h>
10#include <openssl/obj_mac.h>
11
12#include <string.h>
13#include "fido.h"
14#include "fido/es256.h"
15
16static int
17decode_coord(const cbor_item_t *item, void *xy, size_t xy_len)
18{
19 if (cbor_isa_bytestring(item) == false ||
20 cbor_bytestring_is_definite(item) == false ||
21 cbor_bytestring_length(item) != xy_len) {
22 fido_log_debug("%s: cbor type", __func__);
23 return (-1);
24 }
25
26 memcpy(xy, cbor_bytestring_handle(item), xy_len);
27
28 return (0);
29}
30
31static int
32decode_pubkey_point(const cbor_item_t *key, const cbor_item_t *val, void *arg)
33{
34 es256_pk_t *k = arg;
35
36 if (cbor_isa_negint(key) == false ||
37 cbor_int_get_width(key) != CBOR_INT_8)
38 return (0); /* ignore */
39
40 switch (cbor_get_uint8(key)) {
41 case 1: /* x coordinate */
42 return (decode_coord(val, &k->x, sizeof(k->x)));
43 case 2: /* y coordinate */
44 return (decode_coord(val, &k->y, sizeof(k->y)));
45 }
46
47 return (0); /* ignore */
48}
49
50int
51es256_pk_decode(const cbor_item_t *item, es256_pk_t *k)
52{
53 if (cbor_isa_map(item) == false ||
54 cbor_map_is_definite(item) == false ||
55 cbor_map_iter(item, k, decode_pubkey_point) < 0) {
56 fido_log_debug("%s: cbor type", __func__);
57 return (-1);
58 }
59
60 return (0);
61}
62
63cbor_item_t *
64es256_pk_encode(const es256_pk_t *pk, int ecdh)
65{
66 cbor_item_t *item = NULL;
67 struct cbor_pair argv[5];
68 int alg;
69 int ok = -1;
70
71 memset(argv, 0, sizeof(argv));
72
73 if ((item = cbor_new_definite_map(5)) == NULL)
74 goto fail;
75
76 /* kty */
77 if ((argv[0].key = cbor_build_uint8(1)) == NULL ||
78 (argv[0].value = cbor_build_uint8(2)) == NULL ||
79 !cbor_map_add(item, argv[0]))
80 goto fail;
81
82 /*
83 * "The COSEAlgorithmIdentifier used is -25 (ECDH-ES +
84 * HKDF-256) although this is NOT the algorithm actually
85 * used. Setting this to a different value may result in
86 * compatibility issues."
87 */
88 if (ecdh)
89 alg = COSE_ECDH_ES256;
90 else
91 alg = COSE_ES256;
92
93 /* alg */
94 if ((argv[1].key = cbor_build_uint8(3)) == NULL ||
95 (argv[1].value = cbor_build_negint8(-alg - 1)) == NULL ||
96 !cbor_map_add(item, argv[1]))
97 goto fail;
98
99 /* crv */
100 if ((argv[2].key = cbor_build_negint8(0)) == NULL ||
101 (argv[2].value = cbor_build_uint8(1)) == NULL ||
102 !cbor_map_add(item, argv[2]))
103 goto fail;
104
105 /* x */
106 if ((argv[3].key = cbor_build_negint8(1)) == NULL ||
107 (argv[3].value = cbor_build_bytestring(pk->x,
108 sizeof(pk->x))) == NULL || !cbor_map_add(item, argv[3]))
109 goto fail;
110
111 /* y */
112 if ((argv[4].key = cbor_build_negint8(2)) == NULL ||
113 (argv[4].value = cbor_build_bytestring(pk->y,
114 sizeof(pk->y))) == NULL || !cbor_map_add(item, argv[4]))
115 goto fail;
116
117 ok = 0;
118fail:
119 if (ok < 0) {
120 if (item != NULL) {
121 cbor_decref(&item);
122 item = NULL;
123 }
124 }
125
126 for (size_t i = 0; i < 5; i++) {
127 if (argv[i].key)
128 cbor_decref(&argv[i].key);
129 if (argv[i].value)
130 cbor_decref(&argv[i].value);
131 }
132
133 return (item);
134}
135
136es256_sk_t *
137es256_sk_new(void)
138{
139 return (calloc(1, sizeof(es256_sk_t)));
140}
141
142void
143es256_sk_free(es256_sk_t **skp)
144{
145 es256_sk_t *sk;
146
147 if (skp == NULL || (sk = *skp) == NULL)
148 return;
149
150 explicit_bzero(sk, sizeof(*sk));
151 free(sk);
152
153 *skp = NULL;
154}
155
156es256_pk_t *
157es256_pk_new(void)
158{
159 return (calloc(1, sizeof(es256_pk_t)));
160}
161
162void
163es256_pk_free(es256_pk_t **pkp)
164{
165 es256_pk_t *pk;
166
167 if (pkp == NULL || (pk = *pkp) == NULL)
168 return;
169
170 explicit_bzero(pk, sizeof(*pk));
171 free(pk);
172
173 *pkp = NULL;
174}
175
176int
177es256_pk_from_ptr(es256_pk_t *pk, const void *ptr, size_t len)
178{
179 if (len < sizeof(*pk))
180 return (FIDO_ERR_INVALID_ARGUMENT);
181
182 memcpy(pk, ptr, sizeof(*pk));
183
184 return (FIDO_OK);
185}
186
187int
188es256_pk_set_x(es256_pk_t *pk, const unsigned char *x)
189{
190 memcpy(pk->x, x, sizeof(pk->x));
191
192 return (0);
193}
194
195int
196es256_pk_set_y(es256_pk_t *pk, const unsigned char *y)
197{
198 memcpy(pk->y, y, sizeof(pk->y));
199
200 return (0);
201}
202
203int
204es256_sk_create(es256_sk_t *key)
205{
206 EVP_PKEY_CTX *pctx = NULL;
207 EVP_PKEY_CTX *kctx = NULL;
208 EVP_PKEY *p = NULL;
209 EVP_PKEY *k = NULL;
210 const EC_KEY *ec;
211 const BIGNUM *d;
212 const int nid = NID_X9_62_prime256v1;
213 int n;
214 int ok = -1;
215
216 if ((pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL)) == NULL ||
217 EVP_PKEY_paramgen_init(pctx) <= 0 ||
218 EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, nid) <= 0 ||
219 EVP_PKEY_paramgen(pctx, &p) <= 0) {
220 fido_log_debug("%s: EVP_PKEY_paramgen", __func__);
221 goto fail;
222 }
223
224 if ((kctx = EVP_PKEY_CTX_new(p, NULL)) == NULL ||
225 EVP_PKEY_keygen_init(kctx) <= 0 || EVP_PKEY_keygen(kctx, &k) <= 0) {
226 fido_log_debug("%s: EVP_PKEY_keygen", __func__);
227 goto fail;
228 }
229
230 if ((ec = EVP_PKEY_get0_EC_KEY(k)) == NULL ||
231 (d = EC_KEY_get0_private_key(ec)) == NULL ||
232 (n = BN_num_bytes(d)) < 0 || (size_t)n > sizeof(key->d) ||
233 (n = BN_bn2bin(d, key->d)) < 0 || (size_t)n > sizeof(key->d)) {
234 fido_log_debug("%s: EC_KEY_get0_private_key", __func__);
235 goto fail;
236 }
237
238 ok = 0;
239fail:
240 if (p != NULL)
241 EVP_PKEY_free(p);
242 if (k != NULL)
243 EVP_PKEY_free(k);
244 if (pctx != NULL)
245 EVP_PKEY_CTX_free(pctx);
246 if (kctx != NULL)
247 EVP_PKEY_CTX_free(kctx);
248
249 return (ok);
250}
251
252EVP_PKEY *
253es256_pk_to_EVP_PKEY(const es256_pk_t *k)
254{
255 BN_CTX *bnctx = NULL;
256 EC_KEY *ec = NULL;
257 EC_POINT *q = NULL;
258 EVP_PKEY *pkey = NULL;
259 BIGNUM *x = NULL;
260 BIGNUM *y = NULL;
261 const EC_GROUP *g = NULL;
262 const int nid = NID_X9_62_prime256v1;
263 int ok = -1;
264
265 if ((bnctx = BN_CTX_new()) == NULL ||
266 (x = BN_CTX_get(bnctx)) == NULL ||
267 (y = BN_CTX_get(bnctx)) == NULL)
268 goto fail;
269
270 if (BN_bin2bn(k->x, sizeof(k->x), x) == NULL ||
271 BN_bin2bn(k->y, sizeof(k->y), y) == NULL) {
272 fido_log_debug("%s: BN_bin2bn", __func__);
273 goto fail;
274 }
275
276 if ((ec = EC_KEY_new_by_curve_name(nid)) == NULL ||
277 (g = EC_KEY_get0_group(ec)) == NULL) {
278 fido_log_debug("%s: EC_KEY init", __func__);
279 goto fail;
280 }
281
282 if ((q = EC_POINT_new(g)) == NULL ||
283 EC_POINT_set_affine_coordinates_GFp(g, q, x, y, bnctx) == 0 ||
284 EC_KEY_set_public_key(ec, q) == 0) {
285 fido_log_debug("%s: EC_KEY_set_public_key", __func__);
286 goto fail;
287 }
288
289 if ((pkey = EVP_PKEY_new()) == NULL ||
290 EVP_PKEY_assign_EC_KEY(pkey, ec) == 0) {
291 fido_log_debug("%s: EVP_PKEY_assign_EC_KEY", __func__);
292 goto fail;
293 }
294
295 ec = NULL; /* at this point, ec belongs to evp */
296
297 ok = 0;
298fail:
299 if (bnctx != NULL)
300 BN_CTX_free(bnctx);
301 if (ec != NULL)
302 EC_KEY_free(ec);
303 if (q != NULL)
304 EC_POINT_free(q);
305 if (ok < 0 && pkey != NULL) {
306 EVP_PKEY_free(pkey);
307 pkey = NULL;
308 }
309
310 return (pkey);
311}
312
313int
314es256_pk_from_EC_KEY(es256_pk_t *pk, const EC_KEY *ec)
315{
316 BN_CTX *ctx = NULL;
317 BIGNUM *x = NULL;
318 BIGNUM *y = NULL;
319 const EC_POINT *q = NULL;
320 const EC_GROUP *g = NULL;
321 int ok = FIDO_ERR_INTERNAL;
322 int n;
323
324 if ((q = EC_KEY_get0_public_key(ec)) == NULL ||
325 (g = EC_KEY_get0_group(ec)) == NULL)
326 goto fail;
327
328 if ((ctx = BN_CTX_new()) == NULL ||
329 (x = BN_CTX_get(ctx)) == NULL ||
330 (y = BN_CTX_get(ctx)) == NULL)
331 goto fail;
332
333 if (EC_POINT_get_affine_coordinates_GFp(g, q, x, y, ctx) == 0 ||
334 (n = BN_num_bytes(x)) < 0 || (size_t)n > sizeof(pk->x) ||
335 (n = BN_num_bytes(y)) < 0 || (size_t)n > sizeof(pk->y)) {
336 fido_log_debug("%s: EC_POINT_get_affine_coordinates_GFp",
337 __func__);
338 goto fail;
339 }
340
341 if ((n = BN_bn2bin(x, pk->x)) < 0 || (size_t)n > sizeof(pk->x) ||
342 (n = BN_bn2bin(y, pk->y)) < 0 || (size_t)n > sizeof(pk->y)) {
343 fido_log_debug("%s: BN_bn2bin", __func__);
344 goto fail;
345 }
346
347 ok = FIDO_OK;
348fail:
349 if (ctx != NULL)
350 BN_CTX_free(ctx);
351
352 return (ok);
353}
354
355EVP_PKEY *
356es256_sk_to_EVP_PKEY(const es256_sk_t *k)
357{
358 BN_CTX *bnctx = NULL;
359 EC_KEY *ec = NULL;
360 EVP_PKEY *pkey = NULL;
361 BIGNUM *d = NULL;
362 const int nid = NID_X9_62_prime256v1;
363 int ok = -1;
364
365 if ((bnctx = BN_CTX_new()) == NULL || (d = BN_CTX_get(bnctx)) == NULL ||
366 BN_bin2bn(k->d, sizeof(k->d), d) == NULL) {
367 fido_log_debug("%s: BN_bin2bn", __func__);
368 goto fail;
369 }
370
371 if ((ec = EC_KEY_new_by_curve_name(nid)) == NULL ||
372 EC_KEY_set_private_key(ec, d) == 0) {
373 fido_log_debug("%s: EC_KEY_set_private_key", __func__);
374 goto fail;
375 }
376
377 if ((pkey = EVP_PKEY_new()) == NULL ||
378 EVP_PKEY_assign_EC_KEY(pkey, ec) == 0) {
379 fido_log_debug("%s: EVP_PKEY_assign_EC_KEY", __func__);
380 goto fail;
381 }
382
383 ec = NULL; /* at this point, ec belongs to evp */
384
385 ok = 0;
386fail:
387 if (bnctx != NULL)
388 BN_CTX_free(bnctx);
389 if (ec != NULL)
390 EC_KEY_free(ec);
391 if (ok < 0 && pkey != NULL) {
392 EVP_PKEY_free(pkey);
393 pkey = NULL;
394 }
395
396 return (pkey);
397}
398
399int
400es256_derive_pk(const es256_sk_t *sk, es256_pk_t *pk)
401{
402 BIGNUM *d = NULL;
403 EC_KEY *ec = NULL;
404 EC_POINT *q = NULL;
405 const EC_GROUP *g = NULL;
406 const int nid = NID_X9_62_prime256v1;
407 int ok = -1;
408
409 if ((d = BN_bin2bn(sk->d, (int)sizeof(sk->d), NULL)) == NULL ||
410 (ec = EC_KEY_new_by_curve_name(nid)) == NULL ||
411 (g = EC_KEY_get0_group(ec)) == NULL ||
412 (q = EC_POINT_new(g)) == NULL) {
413 fido_log_debug("%s: get", __func__);
414 goto fail;
415 }
416
417 if (EC_POINT_mul(g, q, d, NULL, NULL, NULL) == 0 ||
418 EC_KEY_set_public_key(ec, q) == 0 ||
419 es256_pk_from_EC_KEY(pk, ec) != FIDO_OK) {
420 fido_log_debug("%s: set", __func__);
421 goto fail;
422 }
423
424 ok = 0;
425fail:
426 if (d != NULL)
427 BN_clear_free(d);
428 if (q != NULL)
429 EC_POINT_free(q);
430 if (ec != NULL)
431 EC_KEY_free(ec);
432
433 return (ok);
434}
diff --git a/src/export.gnu b/src/export.gnu
new file mode 100644
index 0000000..f111e33
--- /dev/null
+++ b/src/export.gnu
@@ -0,0 +1,183 @@
1{
2 global:
3 eddsa_pk_free;
4 eddsa_pk_from_EVP_PKEY;
5 eddsa_pk_from_ptr;
6 eddsa_pk_new;
7 eddsa_pk_to_EVP_PKEY;
8 es256_pk_free;
9 es256_pk_from_EC_KEY;
10 es256_pk_from_ptr;
11 es256_pk_new;
12 es256_pk_to_EVP_PKEY;
13 fido_assert_allow_cred;
14 fido_assert_authdata_len;
15 fido_assert_authdata_ptr;
16 fido_assert_clientdata_hash_len;
17 fido_assert_clientdata_hash_ptr;
18 fido_assert_count;
19 fido_assert_flags;
20 fido_assert_free;
21 fido_assert_hmac_secret_len;
22 fido_assert_hmac_secret_ptr;
23 fido_assert_id_len;
24 fido_assert_id_ptr;
25 fido_assert_new;
26 fido_assert_rp_id;
27 fido_assert_set_authdata;
28 fido_assert_set_authdata_raw;
29 fido_assert_set_clientdata_hash;
30 fido_assert_set_count;
31 fido_assert_set_extensions;
32 fido_assert_set_hmac_salt;
33 fido_assert_set_options;
34 fido_assert_set_rp;
35 fido_assert_set_sig;
36 fido_assert_set_up;
37 fido_assert_set_uv;
38 fido_assert_sigcount;
39 fido_assert_sig_len;
40 fido_assert_sig_ptr;
41 fido_assert_user_display_name;
42 fido_assert_user_icon;
43 fido_assert_user_id_len;
44 fido_assert_user_id_ptr;
45 fido_assert_user_name;
46 fido_assert_verify;
47 fido_bio_dev_enroll_begin;
48 fido_bio_dev_enroll_cancel;
49 fido_bio_dev_enroll_continue;
50 fido_bio_dev_enroll_remove;
51 fido_bio_dev_get_info;
52 fido_bio_dev_get_template_array;
53 fido_bio_dev_set_template_name;
54 fido_bio_enroll_free;
55 fido_bio_enroll_last_status;
56 fido_bio_enroll_new;
57 fido_bio_enroll_remaining_samples;
58 fido_bio_info_free;
59 fido_bio_info_max_samples;
60 fido_bio_info_new;
61 fido_bio_info_type;
62 fido_bio_template;
63 fido_bio_template_array_count;
64 fido_bio_template_array_free;
65 fido_bio_template_array_new;
66 fido_bio_template_free;
67 fido_bio_template_id_len;
68 fido_bio_template_id_ptr;
69 fido_bio_template_name;
70 fido_bio_template_new;
71 fido_bio_template_set_id;
72 fido_bio_template_set_name;
73 fido_cbor_info_aaguid_len;
74 fido_cbor_info_aaguid_ptr;
75 fido_cbor_info_extensions_len;
76 fido_cbor_info_extensions_ptr;
77 fido_cbor_info_free;
78 fido_cbor_info_maxmsgsiz;
79 fido_cbor_info_new;
80 fido_cbor_info_options_len;
81 fido_cbor_info_options_name_ptr;
82 fido_cbor_info_options_value_ptr;
83 fido_cbor_info_protocols_len;
84 fido_cbor_info_protocols_ptr;
85 fido_cbor_info_versions_len;
86 fido_cbor_info_versions_ptr;
87 fido_cred_authdata_len;
88 fido_cred_authdata_ptr;
89 fido_cred_clientdata_hash_len;
90 fido_cred_clientdata_hash_ptr;
91 fido_cred_display_name;
92 fido_cred_exclude;
93 fido_cred_flags;
94 fido_cred_fmt;
95 fido_cred_free;
96 fido_cred_id_len;
97 fido_cred_id_ptr;
98 fido_credman_del_dev_rk;
99 fido_credman_get_dev_metadata;
100 fido_credman_get_dev_rk;
101 fido_credman_get_dev_rp;
102 fido_credman_metadata_free;
103 fido_credman_metadata_new;
104 fido_credman_rk;
105 fido_credman_rk_count;
106 fido_credman_rk_existing;
107 fido_credman_rk_free;
108 fido_credman_rk_new;
109 fido_credman_rk_remaining;
110 fido_credman_rp_count;
111 fido_credman_rp_free;
112 fido_credman_rp_id;
113 fido_credman_rp_id_hash_len;
114 fido_credman_rp_id_hash_ptr;
115 fido_credman_rp_name;
116 fido_credman_rp_new;
117 fido_cred_new;
118 fido_cred_pubkey_len;
119 fido_cred_pubkey_ptr;
120 fido_cred_rp_id;
121 fido_cred_rp_name;
122 fido_cred_set_authdata;
123 fido_cred_set_authdata_raw;
124 fido_cred_set_clientdata_hash;
125 fido_cred_set_extensions;
126 fido_cred_set_fmt;
127 fido_cred_set_options;
128 fido_cred_set_rk;
129 fido_cred_set_rp;
130 fido_cred_set_sig;
131 fido_cred_set_type;
132 fido_cred_set_user;
133 fido_cred_set_uv;
134 fido_cred_set_x509;
135 fido_cred_sig_len;
136 fido_cred_sig_ptr;
137 fido_cred_type;
138 fido_cred_user_id_len;
139 fido_cred_user_id_ptr;
140 fido_cred_user_name;
141 fido_cred_verify;
142 fido_cred_verify_self;
143 fido_cred_x5c_len;
144 fido_cred_x5c_ptr;
145 fido_dev_build;
146 fido_dev_cancel;
147 fido_dev_close;
148 fido_dev_flags;
149 fido_dev_force_fido2;
150 fido_dev_force_u2f;
151 fido_dev_free;
152 fido_dev_get_assert;
153 fido_dev_get_cbor_info;
154 fido_dev_get_retry_count;
155 fido_dev_info_free;
156 fido_dev_info_manifest;
157 fido_dev_info_manufacturer_string;
158 fido_dev_info_new;
159 fido_dev_info_path;
160 fido_dev_info_product;
161 fido_dev_info_product_string;
162 fido_dev_info_ptr;
163 fido_dev_info_vendor;
164 fido_dev_is_fido2;
165 fido_dev_major;
166 fido_dev_make_cred;
167 fido_dev_minor;
168 fido_dev_new;
169 fido_dev_open;
170 fido_dev_protocol;
171 fido_dev_reset;
172 fido_dev_set_io_functions;
173 fido_dev_set_pin;
174 fido_init;
175 fido_strerr;
176 rs256_pk_free;
177 rs256_pk_from_ptr;
178 rs256_pk_from_RSA;
179 rs256_pk_new;
180 rs256_pk_to_EVP_PKEY;
181 local:
182 *;
183};
diff --git a/src/export.llvm b/src/export.llvm
new file mode 100644
index 0000000..ef99a26
--- /dev/null
+++ b/src/export.llvm
@@ -0,0 +1,178 @@
1_eddsa_pk_free
2_eddsa_pk_from_EVP_PKEY
3_eddsa_pk_from_ptr
4_eddsa_pk_new
5_eddsa_pk_to_EVP_PKEY
6_es256_pk_free
7_es256_pk_from_EC_KEY
8_es256_pk_from_ptr
9_es256_pk_new
10_es256_pk_to_EVP_PKEY
11_fido_assert_allow_cred
12_fido_assert_authdata_len
13_fido_assert_authdata_ptr
14_fido_assert_clientdata_hash_len
15_fido_assert_clientdata_hash_ptr
16_fido_assert_count
17_fido_assert_flags
18_fido_assert_free
19_fido_assert_hmac_secret_len
20_fido_assert_hmac_secret_ptr
21_fido_assert_id_len
22_fido_assert_id_ptr
23_fido_assert_new
24_fido_assert_rp_id
25_fido_assert_set_authdata
26_fido_assert_set_authdata_raw
27_fido_assert_set_clientdata_hash
28_fido_assert_set_count
29_fido_assert_set_extensions
30_fido_assert_set_hmac_salt
31_fido_assert_set_options
32_fido_assert_set_rp
33_fido_assert_set_sig
34_fido_assert_set_up
35_fido_assert_set_uv
36_fido_assert_sigcount
37_fido_assert_sig_len
38_fido_assert_sig_ptr
39_fido_assert_user_display_name
40_fido_assert_user_icon
41_fido_assert_user_id_len
42_fido_assert_user_id_ptr
43_fido_assert_user_name
44_fido_assert_verify
45_fido_bio_dev_enroll_begin
46_fido_bio_dev_enroll_cancel
47_fido_bio_dev_enroll_continue
48_fido_bio_dev_enroll_remove
49_fido_bio_dev_get_info
50_fido_bio_dev_get_template_array
51_fido_bio_dev_set_template_name
52_fido_bio_enroll_free
53_fido_bio_enroll_last_status
54_fido_bio_enroll_new
55_fido_bio_enroll_remaining_samples
56_fido_bio_info_free
57_fido_bio_info_max_samples
58_fido_bio_info_new
59_fido_bio_info_type
60_fido_bio_template
61_fido_bio_template_array_count
62_fido_bio_template_array_free
63_fido_bio_template_array_new
64_fido_bio_template_free
65_fido_bio_template_id_len
66_fido_bio_template_id_ptr
67_fido_bio_template_name
68_fido_bio_template_new
69_fido_bio_template_set_id
70_fido_bio_template_set_name
71_fido_cbor_info_aaguid_len
72_fido_cbor_info_aaguid_ptr
73_fido_cbor_info_extensions_len
74_fido_cbor_info_extensions_ptr
75_fido_cbor_info_free
76_fido_cbor_info_maxmsgsiz
77_fido_cbor_info_new
78_fido_cbor_info_options_len
79_fido_cbor_info_options_name_ptr
80_fido_cbor_info_options_value_ptr
81_fido_cbor_info_protocols_len
82_fido_cbor_info_protocols_ptr
83_fido_cbor_info_versions_len
84_fido_cbor_info_versions_ptr
85_fido_cred_authdata_len
86_fido_cred_authdata_ptr
87_fido_cred_clientdata_hash_len
88_fido_cred_clientdata_hash_ptr
89_fido_cred_display_name
90_fido_cred_exclude
91_fido_cred_flags
92_fido_cred_fmt
93_fido_cred_free
94_fido_cred_id_len
95_fido_cred_id_ptr
96_fido_credman_del_dev_rk
97_fido_credman_get_dev_metadata
98_fido_credman_get_dev_rk
99_fido_credman_get_dev_rp
100_fido_credman_metadata_free
101_fido_credman_metadata_new
102_fido_credman_rk
103_fido_credman_rk_count
104_fido_credman_rk_existing
105_fido_credman_rk_free
106_fido_credman_rk_new
107_fido_credman_rk_remaining
108_fido_credman_rp_count
109_fido_credman_rp_free
110_fido_credman_rp_id
111_fido_credman_rp_id_hash_len
112_fido_credman_rp_id_hash_ptr
113_fido_credman_rp_name
114_fido_credman_rp_new
115_fido_cred_new
116_fido_cred_pubkey_len
117_fido_cred_pubkey_ptr
118_fido_cred_rp_id
119_fido_cred_rp_name
120_fido_cred_set_authdata
121_fido_cred_set_authdata_raw
122_fido_cred_set_clientdata_hash
123_fido_cred_set_extensions
124_fido_cred_set_fmt
125_fido_cred_set_options
126_fido_cred_set_rk
127_fido_cred_set_rp
128_fido_cred_set_sig
129_fido_cred_set_type
130_fido_cred_set_user
131_fido_cred_set_uv
132_fido_cred_set_x509
133_fido_cred_sig_len
134_fido_cred_sig_ptr
135_fido_cred_type
136_fido_cred_user_id_len
137_fido_cred_user_id_ptr
138_fido_cred_user_name
139_fido_cred_verify
140_fido_cred_verify_self
141_fido_cred_x5c_len
142_fido_cred_x5c_ptr
143_fido_dev_build
144_fido_dev_cancel
145_fido_dev_close
146_fido_dev_flags
147_fido_dev_force_fido2
148_fido_dev_force_u2f
149_fido_dev_free
150_fido_dev_get_assert
151_fido_dev_get_cbor_info
152_fido_dev_get_retry_count
153_fido_dev_info_free
154_fido_dev_info_manifest
155_fido_dev_info_manufacturer_string
156_fido_dev_info_new
157_fido_dev_info_path
158_fido_dev_info_product
159_fido_dev_info_product_string
160_fido_dev_info_ptr
161_fido_dev_info_vendor
162_fido_dev_is_fido2
163_fido_dev_major
164_fido_dev_make_cred
165_fido_dev_minor
166_fido_dev_new
167_fido_dev_open
168_fido_dev_protocol
169_fido_dev_reset
170_fido_dev_set_io_functions
171_fido_dev_set_pin
172_fido_init
173_fido_strerr
174_rs256_pk_free
175_rs256_pk_from_ptr
176_rs256_pk_from_RSA
177_rs256_pk_new
178_rs256_pk_to_EVP_PKEY
diff --git a/src/export.msvc b/src/export.msvc
new file mode 100644
index 0000000..ff5425a
--- /dev/null
+++ b/src/export.msvc
@@ -0,0 +1,179 @@
1EXPORTS
2eddsa_pk_free
3eddsa_pk_from_EVP_PKEY
4eddsa_pk_from_ptr
5eddsa_pk_new
6eddsa_pk_to_EVP_PKEY
7es256_pk_free
8es256_pk_from_EC_KEY
9es256_pk_from_ptr
10es256_pk_new
11es256_pk_to_EVP_PKEY
12fido_assert_allow_cred
13fido_assert_authdata_len
14fido_assert_authdata_ptr
15fido_assert_clientdata_hash_len
16fido_assert_clientdata_hash_ptr
17fido_assert_count
18fido_assert_flags
19fido_assert_free
20fido_assert_hmac_secret_len
21fido_assert_hmac_secret_ptr
22fido_assert_id_len
23fido_assert_id_ptr
24fido_assert_new
25fido_assert_rp_id
26fido_assert_set_authdata
27fido_assert_set_authdata_raw
28fido_assert_set_clientdata_hash
29fido_assert_set_count
30fido_assert_set_extensions
31fido_assert_set_hmac_salt
32fido_assert_set_options
33fido_assert_set_rp
34fido_assert_set_sig
35fido_assert_set_up
36fido_assert_set_uv
37fido_assert_sigcount
38fido_assert_sig_len
39fido_assert_sig_ptr
40fido_assert_user_display_name
41fido_assert_user_icon
42fido_assert_user_id_len
43fido_assert_user_id_ptr
44fido_assert_user_name
45fido_assert_verify
46fido_bio_dev_enroll_begin
47fido_bio_dev_enroll_cancel
48fido_bio_dev_enroll_continue
49fido_bio_dev_enroll_remove
50fido_bio_dev_get_info
51fido_bio_dev_get_template_array
52fido_bio_dev_set_template_name
53fido_bio_enroll_free
54fido_bio_enroll_last_status
55fido_bio_enroll_new
56fido_bio_enroll_remaining_samples
57fido_bio_info_free
58fido_bio_info_max_samples
59fido_bio_info_new
60fido_bio_info_type
61fido_bio_template
62fido_bio_template_array_count
63fido_bio_template_array_free
64fido_bio_template_array_new
65fido_bio_template_free
66fido_bio_template_id_len
67fido_bio_template_id_ptr
68fido_bio_template_name
69fido_bio_template_new
70fido_bio_template_set_id
71fido_bio_template_set_name
72fido_cbor_info_aaguid_len
73fido_cbor_info_aaguid_ptr
74fido_cbor_info_extensions_len
75fido_cbor_info_extensions_ptr
76fido_cbor_info_free
77fido_cbor_info_maxmsgsiz
78fido_cbor_info_new
79fido_cbor_info_options_len
80fido_cbor_info_options_name_ptr
81fido_cbor_info_options_value_ptr
82fido_cbor_info_protocols_len
83fido_cbor_info_protocols_ptr
84fido_cbor_info_versions_len
85fido_cbor_info_versions_ptr
86fido_cred_authdata_len
87fido_cred_authdata_ptr
88fido_cred_clientdata_hash_len
89fido_cred_clientdata_hash_ptr
90fido_cred_display_name
91fido_cred_exclude
92fido_cred_flags
93fido_cred_fmt
94fido_cred_free
95fido_cred_id_len
96fido_cred_id_ptr
97fido_credman_del_dev_rk
98fido_credman_get_dev_metadata
99fido_credman_get_dev_rk
100fido_credman_get_dev_rp
101fido_credman_metadata_free
102fido_credman_metadata_new
103fido_credman_rk
104fido_credman_rk_count
105fido_credman_rk_existing
106fido_credman_rk_free
107fido_credman_rk_new
108fido_credman_rk_remaining
109fido_credman_rp_count
110fido_credman_rp_free
111fido_credman_rp_id
112fido_credman_rp_id_hash_len
113fido_credman_rp_id_hash_ptr
114fido_credman_rp_name
115fido_credman_rp_new
116fido_cred_new
117fido_cred_pubkey_len
118fido_cred_pubkey_ptr
119fido_cred_rp_id
120fido_cred_rp_name
121fido_cred_set_authdata
122fido_cred_set_authdata_raw
123fido_cred_set_clientdata_hash
124fido_cred_set_extensions
125fido_cred_set_fmt
126fido_cred_set_options
127fido_cred_set_rk
128fido_cred_set_rp
129fido_cred_set_sig
130fido_cred_set_type
131fido_cred_set_user
132fido_cred_set_uv
133fido_cred_set_x509
134fido_cred_sig_len
135fido_cred_sig_ptr
136fido_cred_type
137fido_cred_user_id_len
138fido_cred_user_id_ptr
139fido_cred_user_name
140fido_cred_verify
141fido_cred_verify_self
142fido_cred_x5c_len
143fido_cred_x5c_ptr
144fido_dev_build
145fido_dev_cancel
146fido_dev_close
147fido_dev_flags
148fido_dev_force_fido2
149fido_dev_force_u2f
150fido_dev_free
151fido_dev_get_assert
152fido_dev_get_cbor_info
153fido_dev_get_retry_count
154fido_dev_info_free
155fido_dev_info_manifest
156fido_dev_info_manufacturer_string
157fido_dev_info_new
158fido_dev_info_path
159fido_dev_info_product
160fido_dev_info_product_string
161fido_dev_info_ptr
162fido_dev_info_vendor
163fido_dev_is_fido2
164fido_dev_major
165fido_dev_make_cred
166fido_dev_minor
167fido_dev_new
168fido_dev_open
169fido_dev_protocol
170fido_dev_reset
171fido_dev_set_io_functions
172fido_dev_set_pin
173fido_init
174fido_strerr
175rs256_pk_free
176rs256_pk_from_ptr
177rs256_pk_from_RSA
178rs256_pk_new
179rs256_pk_to_EVP_PKEY
diff --git a/src/extern.h b/src/extern.h
new file mode 100644
index 0000000..c35af58
--- /dev/null
+++ b/src/extern.h
@@ -0,0 +1,132 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#ifndef _EXTERN_H
8#define _EXTERN_H
9
10/* aes256 */
11int aes256_cbc_dec(const fido_blob_t *, const fido_blob_t *, fido_blob_t *);
12int aes256_cbc_enc(const fido_blob_t *, const fido_blob_t *, fido_blob_t *);
13
14/* cbor encoding functions */
15cbor_item_t *cbor_flatten_vector(cbor_item_t **, size_t);
16cbor_item_t *cbor_encode_assert_options(fido_opt_t, fido_opt_t);
17cbor_item_t *cbor_encode_change_pin_auth(const fido_blob_t *,
18 const fido_blob_t *, const fido_blob_t *);
19cbor_item_t *cbor_encode_extensions(int);
20cbor_item_t *cbor_encode_hmac_secret_param(const fido_blob_t *,
21 const es256_pk_t *, const fido_blob_t *);
22cbor_item_t *cbor_encode_options(fido_opt_t, fido_opt_t);
23cbor_item_t *cbor_encode_pin_auth(const fido_blob_t *, const fido_blob_t *);
24cbor_item_t *cbor_encode_pin_enc(const fido_blob_t *, const fido_blob_t *);
25cbor_item_t *cbor_encode_pin_hash_enc(const fido_blob_t *, const fido_blob_t *);
26cbor_item_t *cbor_encode_pin_opt(void);
27cbor_item_t *cbor_encode_pubkey(const fido_blob_t *);
28cbor_item_t *cbor_encode_pubkey_list(const fido_blob_array_t *);
29cbor_item_t *cbor_encode_pubkey_param(int);
30cbor_item_t *cbor_encode_rp_entity(const fido_rp_t *);
31cbor_item_t *cbor_encode_set_pin_auth(const fido_blob_t *, const fido_blob_t *);
32cbor_item_t *cbor_encode_user_entity(const fido_user_t *);
33cbor_item_t *es256_pk_encode(const es256_pk_t *, int);
34
35/* cbor decoding functions */
36int cbor_decode_attstmt(const cbor_item_t *, fido_attstmt_t *);
37int cbor_decode_cred_authdata(const cbor_item_t *, int, fido_blob_t *,
38 fido_authdata_t *, fido_attcred_t *, int *);
39int cbor_decode_assert_authdata(const cbor_item_t *, fido_blob_t *,
40 fido_authdata_t *, int *, fido_blob_t *);
41int cbor_decode_cred_id(const cbor_item_t *, fido_blob_t *);
42int cbor_decode_fmt(const cbor_item_t *, char **);
43int cbor_decode_pubkey(const cbor_item_t *, int *, void *);
44int cbor_decode_rp_entity(const cbor_item_t *, fido_rp_t *);
45int cbor_decode_uint64(const cbor_item_t *, uint64_t *);
46int cbor_decode_user(const cbor_item_t *, fido_user_t *);
47int es256_pk_decode(const cbor_item_t *, es256_pk_t *);
48int rs256_pk_decode(const cbor_item_t *, rs256_pk_t *);
49int eddsa_pk_decode(const cbor_item_t *, eddsa_pk_t *);
50
51/* auxiliary cbor routines */
52int cbor_add_bool(cbor_item_t *, const char *, fido_opt_t);
53int cbor_add_bytestring(cbor_item_t *, const char *, const unsigned char *,
54 size_t);
55int cbor_add_string(cbor_item_t *, const char *, const char *);
56int cbor_array_iter(const cbor_item_t *, void *, int(*)(const cbor_item_t *,
57 void *));
58int cbor_build_frame(uint8_t, cbor_item_t *[], size_t, fido_blob_t *);
59int cbor_bytestring_copy(const cbor_item_t *, unsigned char **, size_t *);
60int cbor_map_iter(const cbor_item_t *, void *, int(*)(const cbor_item_t *,
61 const cbor_item_t *, void *));
62int cbor_string_copy(const cbor_item_t *, char **);
63int cbor_parse_reply(const unsigned char *, size_t, void *,
64 int(*)(const cbor_item_t *, const cbor_item_t *, void *));
65int cbor_add_pin_params(fido_dev_t *, const fido_blob_t *, const es256_pk_t *,
66 const fido_blob_t *,const char *, cbor_item_t **, cbor_item_t **);
67void cbor_vector_free(cbor_item_t **, size_t);
68
69#ifndef nitems
70#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
71#endif
72
73/* buf */
74int fido_buf_read(const unsigned char **, size_t *, void *, size_t);
75int fido_buf_write(unsigned char **, size_t *, const void *, size_t);
76
77/* hid i/o */
78void *fido_hid_open(const char *);
79void fido_hid_close(void *);
80int fido_hid_read(void *, unsigned char *, size_t, int);
81int fido_hid_write(void *, const unsigned char *, size_t);
82
83/* generic i/o */
84int fido_rx_cbor_status(fido_dev_t *, int);
85int fido_rx(fido_dev_t *, uint8_t, void *, size_t, int);
86int fido_tx(fido_dev_t *, uint8_t, const void *, size_t);
87
88/* log */
89#ifdef FIDO_NO_DIAGNOSTIC
90#define fido_log_init(...) do { /* nothing */ } while (0)
91#define fido_log_debug(...) do { /* nothing */ } while (0)
92#define fido_log_xxd(...) do { /* nothing */ } while (0)
93#else
94#ifdef __GNUC__
95void fido_log_init(void);
96void fido_log_debug(const char *, ...)
97 __attribute__((__format__ (printf, 1, 2)));
98void fido_log_xxd(const void *, size_t);
99#else
100void fido_log_init(void);
101void fido_log_debug(const char *, ...);
102void fido_log_xxd(const void *, size_t);
103#endif /* __GNUC__ */
104#endif /* FIDO_NO_DIAGNOSTIC */
105
106/* u2f */
107int u2f_register(fido_dev_t *, fido_cred_t *, int);
108int u2f_authenticate(fido_dev_t *, fido_assert_t *, int);
109
110/* unexposed fido ops */
111int fido_dev_authkey(fido_dev_t *, es256_pk_t *);
112int fido_dev_get_pin_token(fido_dev_t *, const char *, const fido_blob_t *,
113 const es256_pk_t *, fido_blob_t *);
114int fido_do_ecdh(fido_dev_t *, es256_pk_t **, fido_blob_t **);
115
116/* misc */
117void fido_assert_reset_rx(fido_assert_t *);
118void fido_assert_reset_tx(fido_assert_t *);
119void fido_cred_reset_rx(fido_cred_t *);
120void fido_cred_reset_tx(fido_cred_t *);
121int fido_check_rp_id(const char *, const unsigned char *);
122int fido_check_flags(uint8_t, fido_opt_t, fido_opt_t);
123
124/* crypto */
125int fido_verify_sig_es256(const fido_blob_t *, const es256_pk_t *,
126 const fido_blob_t *);
127int fido_verify_sig_rs256(const fido_blob_t *, const rs256_pk_t *,
128 const fido_blob_t *);
129int fido_verify_sig_eddsa(const fido_blob_t *, const eddsa_pk_t *,
130 const fido_blob_t *);
131
132#endif /* !_EXTERN_H */
diff --git a/src/fido.h b/src/fido.h
new file mode 100644
index 0000000..f85a41a
--- /dev/null
+++ b/src/fido.h
@@ -0,0 +1,194 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#ifndef _FIDO_H
8#define _FIDO_H
9
10#include <openssl/ec.h>
11#include <openssl/evp.h>
12
13#include <stdbool.h>
14#include <stdint.h>
15#include <stdlib.h>
16
17typedef void *fido_dev_io_open_t(const char *);
18typedef void fido_dev_io_close_t(void *);
19typedef int fido_dev_io_read_t(void *, unsigned char *, size_t, int);
20typedef int fido_dev_io_write_t(void *, const unsigned char *, size_t);
21
22typedef struct fido_dev_io {
23 fido_dev_io_open_t *open;
24 fido_dev_io_close_t *close;
25 fido_dev_io_read_t *read;
26 fido_dev_io_write_t *write;
27} fido_dev_io_t;
28
29typedef enum {
30 FIDO_OPT_OMIT = 0, /* use authenticator's default */
31 FIDO_OPT_FALSE, /* explicitly set option to false */
32 FIDO_OPT_TRUE, /* explicitly set option to true */
33} fido_opt_t;
34
35#ifdef _FIDO_INTERNAL
36#include <cbor.h>
37#include <limits.h>
38
39#include "blob.h"
40#include "../openbsd-compat/openbsd-compat.h"
41#include "iso7816.h"
42#include "types.h"
43#include "extern.h"
44#endif
45
46#include "fido/err.h"
47#include "fido/param.h"
48
49#ifndef _FIDO_INTERNAL
50typedef struct fido_assert fido_assert_t;
51typedef struct fido_cbor_info fido_cbor_info_t;
52typedef struct fido_cred fido_cred_t;
53typedef struct fido_dev fido_dev_t;
54typedef struct fido_dev_info fido_dev_info_t;
55typedef struct es256_pk es256_pk_t;
56typedef struct es256_sk es256_sk_t;
57typedef struct rs256_pk rs256_pk_t;
58typedef struct eddsa_pk eddsa_pk_t;
59#endif
60
61fido_assert_t *fido_assert_new(void);
62fido_cred_t *fido_cred_new(void);
63fido_dev_t *fido_dev_new(void);
64fido_dev_info_t *fido_dev_info_new(size_t);
65fido_cbor_info_t *fido_cbor_info_new(void);
66
67void fido_assert_free(fido_assert_t **);
68void fido_cbor_info_free(fido_cbor_info_t **);
69void fido_cred_free(fido_cred_t **);
70void fido_dev_force_fido2(fido_dev_t *);
71void fido_dev_force_u2f(fido_dev_t *);
72void fido_dev_free(fido_dev_t **);
73void fido_dev_info_free(fido_dev_info_t **, size_t);
74
75/* fido_init() flags. */
76#define FIDO_DEBUG 0x01
77
78void fido_init(int);
79
80const unsigned char *fido_assert_authdata_ptr(const fido_assert_t *, size_t);
81const unsigned char *fido_assert_clientdata_hash_ptr(const fido_assert_t *);
82const unsigned char *fido_assert_hmac_secret_ptr(const fido_assert_t *, size_t);
83const unsigned char *fido_assert_id_ptr(const fido_assert_t *, size_t);
84const unsigned char *fido_assert_sig_ptr(const fido_assert_t *, size_t);
85const unsigned char *fido_assert_user_id_ptr(const fido_assert_t *, size_t);
86
87char **fido_cbor_info_extensions_ptr(const fido_cbor_info_t *);
88char **fido_cbor_info_options_name_ptr(const fido_cbor_info_t *);
89char **fido_cbor_info_versions_ptr(const fido_cbor_info_t *);
90const bool *fido_cbor_info_options_value_ptr(const fido_cbor_info_t *);
91const char *fido_assert_rp_id(const fido_assert_t *);
92const char *fido_assert_user_display_name(const fido_assert_t *, size_t);
93const char *fido_assert_user_icon(const fido_assert_t *, size_t);
94const char *fido_assert_user_name(const fido_assert_t *, size_t);
95const char *fido_cred_display_name(const fido_cred_t *);
96const char *fido_cred_fmt(const fido_cred_t *);
97const char *fido_cred_rp_id(const fido_cred_t *);
98const char *fido_cred_rp_name(const fido_cred_t *);
99const char *fido_cred_user_name(const fido_cred_t *);
100const char *fido_dev_info_manufacturer_string(const fido_dev_info_t *);
101const char *fido_dev_info_path(const fido_dev_info_t *);
102const char *fido_dev_info_product_string(const fido_dev_info_t *);
103const fido_dev_info_t *fido_dev_info_ptr(const fido_dev_info_t *, size_t);
104const uint8_t *fido_cbor_info_protocols_ptr(const fido_cbor_info_t *);
105const unsigned char *fido_cbor_info_aaguid_ptr(const fido_cbor_info_t *);
106const unsigned char *fido_cred_authdata_ptr(const fido_cred_t *);
107const unsigned char *fido_cred_clientdata_hash_ptr(const fido_cred_t *);
108const unsigned char *fido_cred_id_ptr(const fido_cred_t *);
109const unsigned char *fido_cred_user_id_ptr(const fido_cred_t *);
110const unsigned char *fido_cred_pubkey_ptr(const fido_cred_t *);
111const unsigned char *fido_cred_sig_ptr(const fido_cred_t *);
112const unsigned char *fido_cred_x5c_ptr(const fido_cred_t *);
113
114int fido_assert_allow_cred(fido_assert_t *, const unsigned char *, size_t);
115int fido_assert_set_authdata(fido_assert_t *, size_t, const unsigned char *,
116 size_t);
117int fido_assert_set_authdata_raw(fido_assert_t *, size_t, const unsigned char *,
118 size_t);
119int fido_assert_set_clientdata_hash(fido_assert_t *, const unsigned char *,
120 size_t);
121int fido_assert_set_count(fido_assert_t *, size_t);
122int fido_assert_set_extensions(fido_assert_t *, int);
123int fido_assert_set_hmac_salt(fido_assert_t *, const unsigned char *, size_t);
124int fido_assert_set_options(fido_assert_t *, bool, bool) __attribute__((__deprecated__));
125int fido_assert_set_rp(fido_assert_t *, const char *);
126int fido_assert_set_up(fido_assert_t *, fido_opt_t);
127int fido_assert_set_uv(fido_assert_t *, fido_opt_t);
128int fido_assert_set_sig(fido_assert_t *, size_t, const unsigned char *, size_t);
129int fido_assert_verify(const fido_assert_t *, size_t, int, const void *);
130int fido_cred_exclude(fido_cred_t *, const unsigned char *, size_t);
131int fido_cred_set_authdata(fido_cred_t *, const unsigned char *, size_t);
132int fido_cred_set_authdata_raw(fido_cred_t *, const unsigned char *, size_t);
133int fido_cred_set_clientdata_hash(fido_cred_t *, const unsigned char *, size_t);
134int fido_cred_set_extensions(fido_cred_t *, int);
135int fido_cred_set_fmt(fido_cred_t *, const char *);
136int fido_cred_set_options(fido_cred_t *, bool, bool) __attribute__((__deprecated__));
137int fido_cred_set_rk(fido_cred_t *, fido_opt_t);
138int fido_cred_set_rp(fido_cred_t *, const char *, const char *);
139int fido_cred_set_sig(fido_cred_t *, const unsigned char *, size_t);
140int fido_cred_set_type(fido_cred_t *, int);
141int fido_cred_set_uv(fido_cred_t *, fido_opt_t);
142int fido_cred_type(const fido_cred_t *);
143int fido_cred_set_user(fido_cred_t *, const unsigned char *, size_t,
144 const char *, const char *, const char *);
145int fido_cred_set_x509(fido_cred_t *, const unsigned char *, size_t);
146int fido_cred_verify(const fido_cred_t *);
147int fido_cred_verify_self(const fido_cred_t *);
148int fido_dev_cancel(fido_dev_t *);
149int fido_dev_close(fido_dev_t *);
150int fido_dev_get_assert(fido_dev_t *, fido_assert_t *, const char *);
151int fido_dev_get_cbor_info(fido_dev_t *, fido_cbor_info_t *);
152int fido_dev_get_retry_count(fido_dev_t *, int *);
153int fido_dev_info_manifest(fido_dev_info_t *, size_t, size_t *);
154int fido_dev_make_cred(fido_dev_t *, fido_cred_t *, const char *);
155int fido_dev_open(fido_dev_t *, const char *);
156int fido_dev_reset(fido_dev_t *);
157int fido_dev_set_io_functions(fido_dev_t *, const fido_dev_io_t *);
158int fido_dev_set_pin(fido_dev_t *, const char *, const char *);
159
160size_t fido_assert_authdata_len(const fido_assert_t *, size_t);
161size_t fido_assert_clientdata_hash_len(const fido_assert_t *);
162size_t fido_assert_count(const fido_assert_t *);
163size_t fido_assert_hmac_secret_len(const fido_assert_t *, size_t);
164size_t fido_assert_id_len(const fido_assert_t *, size_t);
165size_t fido_assert_sig_len(const fido_assert_t *, size_t);
166size_t fido_assert_user_id_len(const fido_assert_t *, size_t);
167size_t fido_cbor_info_aaguid_len(const fido_cbor_info_t *);
168size_t fido_cbor_info_extensions_len(const fido_cbor_info_t *);
169size_t fido_cbor_info_options_len(const fido_cbor_info_t *);
170size_t fido_cbor_info_protocols_len(const fido_cbor_info_t *);
171size_t fido_cbor_info_versions_len(const fido_cbor_info_t *);
172size_t fido_cred_authdata_len(const fido_cred_t *);
173size_t fido_cred_clientdata_hash_len(const fido_cred_t *);
174size_t fido_cred_id_len(const fido_cred_t *);
175size_t fido_cred_user_id_len(const fido_cred_t *);
176size_t fido_cred_pubkey_len(const fido_cred_t *);
177size_t fido_cred_sig_len(const fido_cred_t *);
178size_t fido_cred_x5c_len(const fido_cred_t *);
179
180uint8_t fido_assert_flags(const fido_assert_t *, size_t);
181uint32_t fido_assert_sigcount(const fido_assert_t *, size_t);
182uint8_t fido_cred_flags(const fido_cred_t *);
183uint8_t fido_dev_protocol(const fido_dev_t *);
184uint8_t fido_dev_major(const fido_dev_t *);
185uint8_t fido_dev_minor(const fido_dev_t *);
186uint8_t fido_dev_build(const fido_dev_t *);
187uint8_t fido_dev_flags(const fido_dev_t *);
188int16_t fido_dev_info_vendor(const fido_dev_info_t *);
189int16_t fido_dev_info_product(const fido_dev_info_t *);
190uint64_t fido_cbor_info_maxmsgsiz(const fido_cbor_info_t *);
191
192bool fido_dev_is_fido2(const fido_dev_t *);
193
194#endif /* !_FIDO_H */
diff --git a/src/fido/bio.h b/src/fido/bio.h
new file mode 100644
index 0000000..31dffe4
--- /dev/null
+++ b/src/fido/bio.h
@@ -0,0 +1,95 @@
1/*
2 * Copyright (c) 2019 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#ifndef _FIDO_BIO_H
8#define _FIDO_BIO_H
9
10#include <stdint.h>
11#include <stdlib.h>
12
13#include "fido/err.h"
14#include "fido/param.h"
15
16#ifdef _FIDO_INTERNAL
17struct fido_bio_template {
18 fido_blob_t id;
19 char *name;
20};
21
22struct fido_bio_template_array {
23 struct fido_bio_template *ptr;
24 size_t n_alloc; /* number of allocated entries */
25 size_t n_rx; /* number of populated entries */
26};
27
28struct fido_bio_enroll {
29 uint8_t remaining_samples;
30 uint8_t last_status;
31 fido_blob_t *token;
32};
33
34struct fido_bio_info {
35 uint8_t type;
36 uint8_t max_samples;
37};
38#endif
39
40typedef struct fido_bio_template fido_bio_template_t;
41typedef struct fido_bio_template_array fido_bio_template_array_t;
42typedef struct fido_bio_enroll fido_bio_enroll_t;
43typedef struct fido_bio_info fido_bio_info_t;
44
45#define FIDO_BIO_ENROLL_FP_GOOD 0x00
46#define FIDO_BIO_ENROLL_FP_TOO_HIGH 0x01
47#define FIDO_BIO_ENROLL_FP_TOO_LOW 0x02
48#define FIDO_BIO_ENROLL_FP_TOO_LEFT 0x03
49#define FIDO_BIO_ENROLL_FP_TOO_RIGHT 0x04
50#define FIDO_BIO_ENROLL_FP_TOO_FAST 0x05
51#define FIDO_BIO_ENROLL_FP_TOO_SLOW 0x06
52#define FIDO_BIO_ENROLL_FP_POOR_QUALITY 0x07
53#define FIDO_BIO_ENROLL_FP_TOO_SKEWED 0x08
54#define FIDO_BIO_ENROLL_FP_TOO_SHORT 0x09
55#define FIDO_BIO_ENROLL_FP_MERGE_FAILURE 0x0a
56#define FIDO_BIO_ENROLL_FP_EXISTS 0x0b
57#define FIDO_BIO_ENROLL_FP_DATABASE_FULL 0x0c
58#define FIDO_BIO_ENROLL_NO_USER_ACTIVITY 0x0d
59#define FIDO_BIO_ENROLL_NO_USER_PRESENCE_TRANSITION 0x0e
60
61const char *fido_bio_template_name(const fido_bio_template_t *);
62const fido_bio_template_t *fido_bio_template(const fido_bio_template_array_t *,
63 size_t);
64const unsigned char *fido_bio_template_id_ptr(const fido_bio_template_t *);
65fido_bio_enroll_t *fido_bio_enroll_new(void);
66fido_bio_info_t *fido_bio_info_new(void);
67fido_bio_template_array_t *fido_bio_template_array_new(void);
68fido_bio_template_t *fido_bio_template_new(void);
69int fido_bio_dev_enroll_begin(fido_dev_t *, fido_bio_template_t *,
70 fido_bio_enroll_t *, uint32_t, const char *);
71int fido_bio_dev_enroll_cancel(fido_dev_t *);
72int fido_bio_dev_enroll_continue(fido_dev_t *, const fido_bio_template_t *,
73 fido_bio_enroll_t *, uint32_t);
74int fido_bio_dev_enroll_remove(fido_dev_t *, const fido_bio_template_t *,
75 const char *);
76int fido_bio_dev_get_info(fido_dev_t *, fido_bio_info_t *);
77int fido_bio_dev_get_template_array(fido_dev_t *, fido_bio_template_array_t *,
78 const char *);
79int fido_bio_dev_set_template_name(fido_dev_t *, const fido_bio_template_t *,
80 const char *);
81int fido_bio_template_set_id(fido_bio_template_t *, const unsigned char *,
82 size_t);
83int fido_bio_template_set_name(fido_bio_template_t *, const char *);
84size_t fido_bio_template_array_count(const fido_bio_template_array_t *);
85size_t fido_bio_template_id_len(const fido_bio_template_t *);
86uint8_t fido_bio_enroll_last_status(const fido_bio_enroll_t *);
87uint8_t fido_bio_enroll_remaining_samples(const fido_bio_enroll_t *);
88uint8_t fido_bio_info_max_samples(const fido_bio_info_t *);
89uint8_t fido_bio_info_type(const fido_bio_info_t *);
90void fido_bio_enroll_free(fido_bio_enroll_t **);
91void fido_bio_info_free(fido_bio_info_t **);
92void fido_bio_template_array_free(fido_bio_template_array_t **);
93void fido_bio_template_free(fido_bio_template_t **);
94
95#endif /* !_FIDO_BIO_H */
diff --git a/src/fido/credman.h b/src/fido/credman.h
new file mode 100644
index 0000000..1c7cafe
--- /dev/null
+++ b/src/fido/credman.h
@@ -0,0 +1,74 @@
1/*
2 * Copyright (c) 2019 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#ifndef _FIDO_CREDMAN_H
8#define _FIDO_CREDMAN_H
9
10#include <stdint.h>
11#include <stdlib.h>
12
13#include "fido/err.h"
14#include "fido/param.h"
15
16#ifdef _FIDO_INTERNAL
17struct fido_credman_metadata {
18 uint64_t rk_existing;
19 uint64_t rk_remaining;
20};
21
22struct fido_credman_single_rp {
23 fido_rp_t rp_entity;
24 fido_blob_t rp_id_hash;
25};
26
27struct fido_credman_rp {
28 struct fido_credman_single_rp *ptr;
29 size_t n_alloc; /* number of allocated entries */
30 size_t n_rx; /* number of populated entries */
31};
32
33struct fido_credman_rk {
34 fido_cred_t *ptr;
35 size_t n_alloc; /* number of allocated entries */
36 size_t n_rx; /* number of populated entries */
37};
38#endif
39
40typedef struct fido_credman_metadata fido_credman_metadata_t;
41typedef struct fido_credman_rk fido_credman_rk_t;
42typedef struct fido_credman_rp fido_credman_rp_t;
43
44const char *fido_credman_rp_id(const fido_credman_rp_t *, size_t);
45const char *fido_credman_rp_name(const fido_credman_rp_t *, size_t);
46
47const fido_cred_t *fido_credman_rk(const fido_credman_rk_t *, size_t);
48const unsigned char *fido_credman_rp_id_hash_ptr(const fido_credman_rp_t *,
49 size_t);
50
51fido_credman_metadata_t *fido_credman_metadata_new(void);
52fido_credman_rk_t *fido_credman_rk_new(void);
53fido_credman_rp_t *fido_credman_rp_new(void);
54
55int fido_credman_del_dev_rk(fido_dev_t *, const unsigned char *, size_t,
56 const char *);
57int fido_credman_get_dev_metadata(fido_dev_t *, fido_credman_metadata_t *,
58 const char *);
59int fido_credman_get_dev_rk(fido_dev_t *, const char *, fido_credman_rk_t *,
60 const char *);
61int fido_credman_get_dev_rp(fido_dev_t *, fido_credman_rp_t *, const char *);
62
63size_t fido_credman_rk_count(const fido_credman_rk_t *);
64size_t fido_credman_rp_count(const fido_credman_rp_t *);
65size_t fido_credman_rp_id_hash_len(const fido_credman_rp_t *, size_t);
66
67uint64_t fido_credman_rk_existing(const fido_credman_metadata_t *);
68uint64_t fido_credman_rk_remaining(const fido_credman_metadata_t *);
69
70void fido_credman_metadata_free(fido_credman_metadata_t **);
71void fido_credman_rk_free(fido_credman_rk_t **);
72void fido_credman_rp_free(fido_credman_rp_t **);
73
74#endif /* !_FIDO_CREDMAN_H */
diff --git a/src/fido/eddsa.h b/src/fido/eddsa.h
new file mode 100644
index 0000000..9de272d
--- /dev/null
+++ b/src/fido/eddsa.h
@@ -0,0 +1,40 @@
1/*
2 * Copyright (c) 2019 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#ifndef _FIDO_EDDSA_H
8#define _FIDO_EDDSA_H
9
10#include <openssl/ec.h>
11
12#include <stdint.h>
13#include <stdlib.h>
14
15eddsa_pk_t *eddsa_pk_new(void);
16void eddsa_pk_free(eddsa_pk_t **);
17EVP_PKEY *eddsa_pk_to_EVP_PKEY(const eddsa_pk_t *);
18
19int eddsa_pk_from_EVP_PKEY(eddsa_pk_t *, const EVP_PKEY *);
20int eddsa_pk_from_ptr(eddsa_pk_t *, const void *, size_t);
21
22#ifdef _FIDO_INTERNAL
23
24#if defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10101000L
25#define EVP_PKEY_ED25519 EVP_PKEY_NONE
26int EVP_PKEY_get_raw_public_key(const EVP_PKEY *, unsigned char *, size_t *);
27EVP_PKEY *EVP_PKEY_new_raw_public_key(int, ENGINE *, const unsigned char *,
28 size_t);
29int EVP_DigestVerify(EVP_MD_CTX *, const unsigned char *, size_t,
30 const unsigned char *, size_t);
31#endif /* LIBRESSL_VERSION_NUMBER || OPENSSL_VERSION_NUMBER < 0x10101000L */
32
33#if OPENSSL_VERSION_NUMBER < 0x10100000L
34EVP_MD_CTX *EVP_MD_CTX_new(void);
35void EVP_MD_CTX_free(EVP_MD_CTX *);
36#endif
37
38#endif /* _FIDO_INTERNAL */
39
40#endif /* !_FIDO_EDDSA_H */
diff --git a/src/fido/err.h b/src/fido/err.h
new file mode 100644
index 0000000..11f52bc
--- /dev/null
+++ b/src/fido/err.h
@@ -0,0 +1,69 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#ifndef _FIDO_ERR_H
8#define _FIDO_ERR_H
9
10#define FIDO_ERR_SUCCESS 0x00
11#define FIDO_ERR_INVALID_COMMAND 0x01
12#define FIDO_ERR_INVALID_PARAMETER 0x02
13#define FIDO_ERR_INVALID_LENGTH 0x03
14#define FIDO_ERR_INVALID_SEQ 0x04
15#define FIDO_ERR_TIMEOUT 0x05
16#define FIDO_ERR_CHANNEL_BUSY 0x06
17#define FIDO_ERR_LOCK_REQUIRED 0x0a
18#define FIDO_ERR_INVALID_CHANNEL 0x0b
19#define FIDO_ERR_CBOR_UNEXPECTED_TYPE 0x11
20#define FIDO_ERR_INVALID_CBOR 0x12
21#define FIDO_ERR_MISSING_PARAMETER 0x14
22#define FIDO_ERR_LIMIT_EXCEEDED 0x15
23#define FIDO_ERR_UNSUPPORTED_EXTENSION 0x16
24#define FIDO_ERR_CREDENTIAL_EXCLUDED 0x19
25#define FIDO_ERR_PROCESSING 0x21
26#define FIDO_ERR_INVALID_CREDENTIAL 0x22
27#define FIDO_ERR_USER_ACTION_PENDING 0x23
28#define FIDO_ERR_OPERATION_PENDING 0x24
29#define FIDO_ERR_NO_OPERATIONS 0x25
30#define FIDO_ERR_UNSUPPORTED_ALGORITHM 0x26
31#define FIDO_ERR_OPERATION_DENIED 0x27
32#define FIDO_ERR_KEY_STORE_FULL 0x28
33#define FIDO_ERR_NOT_BUSY 0x29
34#define FIDO_ERR_NO_OPERATION_PENDING 0x2a
35#define FIDO_ERR_UNSUPPORTED_OPTION 0x2b
36#define FIDO_ERR_INVALID_OPTION 0x2c
37#define FIDO_ERR_KEEPALIVE_CANCEL 0x2d
38#define FIDO_ERR_NO_CREDENTIALS 0x2e
39#define FIDO_ERR_USER_ACTION_TIMEOUT 0x2f
40#define FIDO_ERR_NOT_ALLOWED 0x30
41#define FIDO_ERR_PIN_INVALID 0x31
42#define FIDO_ERR_PIN_BLOCKED 0x32
43#define FIDO_ERR_PIN_AUTH_INVALID 0x33
44#define FIDO_ERR_PIN_AUTH_BLOCKED 0x34
45#define FIDO_ERR_PIN_NOT_SET 0x35
46#define FIDO_ERR_PIN_REQUIRED 0x36
47#define FIDO_ERR_PIN_POLICY_VIOLATION 0x37
48#define FIDO_ERR_PIN_TOKEN_EXPIRED 0x38
49#define FIDO_ERR_REQUEST_TOO_LARGE 0x39
50#define FIDO_ERR_ACTION_TIMEOUT 0x3a
51#define FIDO_ERR_UP_REQUIRED 0x3b
52#define FIDO_ERR_ERR_OTHER 0x7f
53#define FIDO_ERR_SPEC_LAST 0xdf
54
55/* defined internally */
56#define FIDO_OK FIDO_ERR_SUCCESS
57#define FIDO_ERR_TX -1
58#define FIDO_ERR_RX -2
59#define FIDO_ERR_RX_NOT_CBOR -3
60#define FIDO_ERR_RX_INVALID_CBOR -4
61#define FIDO_ERR_INVALID_PARAM -5
62#define FIDO_ERR_INVALID_SIG -6
63#define FIDO_ERR_INVALID_ARGUMENT -7
64#define FIDO_ERR_USER_PRESENCE_REQUIRED -8
65#define FIDO_ERR_INTERNAL -9
66
67const char *fido_strerr(int);
68
69#endif /* _FIDO_ERR_H */
diff --git a/src/fido/es256.h b/src/fido/es256.h
new file mode 100644
index 0000000..d3d13dd
--- /dev/null
+++ b/src/fido/es256.h
@@ -0,0 +1,34 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#ifndef _FIDO_ES256_H
8#define _FIDO_ES256_H
9
10#include <openssl/ec.h>
11
12#include <stdint.h>
13#include <stdlib.h>
14
15es256_pk_t *es256_pk_new(void);
16void es256_pk_free(es256_pk_t **);
17EVP_PKEY *es256_pk_to_EVP_PKEY(const es256_pk_t *);
18
19int es256_pk_from_EC_KEY(es256_pk_t *, const EC_KEY *);
20int es256_pk_from_ptr(es256_pk_t *, const void *, size_t);
21
22#ifdef _FIDO_INTERNAL
23es256_sk_t *es256_sk_new(void);
24void es256_sk_free(es256_sk_t **);
25EVP_PKEY *es256_sk_to_EVP_PKEY(const es256_sk_t *);
26
27int es256_derive_pk(const es256_sk_t *, es256_pk_t *);
28int es256_sk_create(es256_sk_t *);
29
30int es256_pk_set_x(es256_pk_t *, const unsigned char *);
31int es256_pk_set_y(es256_pk_t *, const unsigned char *);
32#endif
33
34#endif /* !_FIDO_ES256_H */
diff --git a/src/fido/param.h b/src/fido/param.h
new file mode 100644
index 0000000..9e12ac6
--- /dev/null
+++ b/src/fido/param.h
@@ -0,0 +1,84 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#ifndef _FIDO_PARAM_H
8#define _FIDO_PARAM_H
9
10/* Authentication data flags. */
11#define CTAP_AUTHDATA_USER_PRESENT 0x01
12#define CTAP_AUTHDATA_USER_VERIFIED 0x04
13#define CTAP_AUTHDATA_ATT_CRED 0x40
14#define CTAP_AUTHDATA_EXT_DATA 0x80
15
16/* CTAPHID command opcodes. */
17#define CTAP_CMD_PING 0x01
18#define CTAP_CMD_MSG 0x03
19#define CTAP_CMD_LOCK 0x04
20#define CTAP_CMD_INIT 0x06
21#define CTAP_CMD_WINK 0x08
22#define CTAP_CMD_CBOR 0x10
23#define CTAP_CMD_CANCEL 0x11
24#define CTAP_KEEPALIVE 0x3b
25#define CTAP_FRAME_INIT 0x80
26
27/* CTAPHID CBOR command opcodes. */
28#define CTAP_CBOR_MAKECRED 0x01
29#define CTAP_CBOR_ASSERT 0x02
30#define CTAP_CBOR_GETINFO 0x04
31#define CTAP_CBOR_CLIENT_PIN 0x06
32#define CTAP_CBOR_RESET 0x07
33#define CTAP_CBOR_NEXT_ASSERT 0x08
34#define CTAP_CBOR_BIO_ENROLL_PRE 0x40
35#define CTAP_CBOR_CRED_MGMT_PRE 0x41
36
37/* U2F command opcodes. */
38#define U2F_CMD_REGISTER 0x01
39#define U2F_CMD_AUTH 0x02
40
41/* U2F command flags. */
42#define U2F_AUTH_SIGN 0x03
43#define U2F_AUTH_CHECK 0x07
44
45/* ISO7816-4 status words. */
46#define SW_CONDITIONS_NOT_SATISFIED 0x6985
47#define SW_WRONG_DATA 0x6a80
48#define SW_NO_ERROR 0x9000
49
50/* HID Broadcast channel ID. */
51#define CTAP_CID_BROADCAST 0xffffffff
52
53/* Expected size of a HID report in bytes. */
54#define CTAP_RPT_SIZE 64
55
56/* Randomness device on UNIX-like platforms. */
57#ifndef FIDO_RANDOM_DEV
58#define FIDO_RANDOM_DEV "/dev/urandom"
59#endif
60
61/* CTAP capability bits. */
62#define FIDO_CAP_WINK 0x01 /* if set, device supports CTAP_CMD_WINK */
63#define FIDO_CAP_CBOR 0x04 /* if set, device supports CTAP_CMD_CBOR */
64#define FIDO_CAP_NMSG 0x08 /* if set, device doesn't support CTAP_CMD_MSG */
65
66/* Supported COSE algorithms. */
67#define COSE_ES256 -7
68#define COSE_EDDSA -8
69#define COSE_ECDH_ES256 -25
70#define COSE_RS256 -257
71
72/* Supported COSE types. */
73#define COSE_KTY_OKP 1
74#define COSE_KTY_EC2 2
75#define COSE_KTY_RSA 3
76
77/* Supported curves. */
78#define COSE_P256 1
79#define COSE_ED25519 6
80
81/* Supported extensions. */
82#define FIDO_EXT_HMAC_SECRET 0x01
83
84#endif /* !_FIDO_PARAM_H */
diff --git a/src/fido/rs256.h b/src/fido/rs256.h
new file mode 100644
index 0000000..d2fa162
--- /dev/null
+++ b/src/fido/rs256.h
@@ -0,0 +1,22 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#ifndef _FIDO_RS256_H
8#define _FIDO_RS256_H
9
10#include <openssl/rsa.h>
11
12#include <stdint.h>
13#include <stdlib.h>
14
15rs256_pk_t *rs256_pk_new(void);
16void rs256_pk_free(rs256_pk_t **);
17EVP_PKEY *rs256_pk_to_EVP_PKEY(const rs256_pk_t *);
18
19int rs256_pk_from_RSA(rs256_pk_t *, const RSA *);
20int rs256_pk_from_ptr(rs256_pk_t *, const void *, size_t);
21
22#endif /* !_FIDO_RS256_H */
diff --git a/src/hid.c b/src/hid.c
new file mode 100644
index 0000000..783d22c
--- /dev/null
+++ b/src/hid.c
@@ -0,0 +1,70 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <string.h>
8#include "fido.h"
9
10fido_dev_info_t *
11fido_dev_info_new(size_t n)
12{
13 return (calloc(n, sizeof(fido_dev_info_t)));
14}
15
16void
17fido_dev_info_free(fido_dev_info_t **devlist_p, size_t n)
18{
19 fido_dev_info_t *devlist;
20
21 if (devlist_p == NULL || (devlist = *devlist_p) == NULL)
22 return;
23
24 for (size_t i = 0; i < n; i++) {
25 const fido_dev_info_t *di = &devlist[i];
26 free(di->path);
27 free(di->manufacturer);
28 free(di->product);
29 }
30
31 free(devlist);
32
33 *devlist_p = NULL;
34}
35
36const fido_dev_info_t *
37fido_dev_info_ptr(const fido_dev_info_t *devlist, size_t i)
38{
39 return (&devlist[i]);
40}
41
42const char *
43fido_dev_info_path(const fido_dev_info_t *di)
44{
45 return (di->path);
46}
47
48int16_t
49fido_dev_info_vendor(const fido_dev_info_t *di)
50{
51 return (di->vendor_id);
52}
53
54int16_t
55fido_dev_info_product(const fido_dev_info_t *di)
56{
57 return (di->product_id);
58}
59
60const char *
61fido_dev_info_manufacturer_string(const fido_dev_info_t *di)
62{
63 return (di->manufacturer);
64}
65
66const char *
67fido_dev_info_product_string(const fido_dev_info_t *di)
68{
69 return (di->product);
70}
diff --git a/src/hid_linux.c b/src/hid_linux.c
new file mode 100644
index 0000000..c7cabc9
--- /dev/null
+++ b/src/hid_linux.c
@@ -0,0 +1,344 @@
1/*
2 * Copyright (c) 2019 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <sys/types.h>
8
9#include <sys/ioctl.h>
10#include <linux/hidraw.h>
11
12#include <fcntl.h>
13#include <libudev.h>
14#include <string.h>
15#include <unistd.h>
16
17#include "fido.h"
18
19#define REPORT_LEN 65
20
21static int
22get_key_len(uint8_t tag, uint8_t *key, size_t *key_len)
23{
24 *key = tag & 0xfc;
25 if ((*key & 0xf0) == 0xf0) {
26 fido_log_debug("%s: *key=0x%02x", __func__, *key);
27 return (-1);
28 }
29
30 *key_len = tag & 0x3;
31 if (*key_len == 3) {
32 *key_len = 4;
33 }
34
35 return (0);
36}
37
38static int
39get_key_val(const void *body, size_t key_len, uint32_t *val)
40{
41 const uint8_t *ptr = body;
42
43 switch (key_len) {
44 case 0:
45 *val = 0;
46 break;
47 case 1:
48 *val = ptr[0];
49 break;
50 case 2:
51 *val = (uint32_t)((ptr[1] << 8) | ptr[0]);
52 break;
53 default:
54 fido_log_debug("%s: key_len=%zu", __func__, key_len);
55 return (-1);
56 }
57
58 return (0);
59}
60
61static int
62get_usage_info(const struct hidraw_report_descriptor *hrd, uint32_t *usage_page,
63 uint32_t *usage)
64{
65 const uint8_t *ptr;
66 size_t len;
67
68 ptr = hrd->value;
69 len = hrd->size;
70
71 while (len > 0) {
72 const uint8_t tag = ptr[0];
73 ptr++;
74 len--;
75
76 uint8_t key;
77 size_t key_len;
78 uint32_t key_val;
79
80 if (get_key_len(tag, &key, &key_len) < 0 || key_len > len ||
81 get_key_val(ptr, key_len, &key_val) < 0) {
82 return (-1);
83 }
84
85 if (key == 0x4) {
86 *usage_page = key_val;
87 } else if (key == 0x8) {
88 *usage = key_val;
89 }
90
91 ptr += key_len;
92 len -= key_len;
93 }
94
95 return (0);
96}
97
98static int
99get_report_descriptor(const char *path, struct hidraw_report_descriptor *hrd)
100{
101 int r;
102 int s = -1;
103 int fd;
104 int ok = -1;
105
106 if ((fd = open(path, O_RDONLY)) < 0) {
107 fido_log_debug("%s: open", __func__);
108 return (-1);
109 }
110
111 if ((r = ioctl(fd, HIDIOCGRDESCSIZE, &s)) < 0 || s < 0 ||
112 (unsigned)s > HID_MAX_DESCRIPTOR_SIZE) {
113 fido_log_debug("%s: ioctl HIDIOCGRDESCSIZE", __func__);
114 goto fail;
115 }
116
117 hrd->size = s;
118
119 if ((r = ioctl(fd, HIDIOCGRDESC, hrd)) < 0) {
120 fido_log_debug("%s: ioctl HIDIOCGRDESC", __func__);
121 goto fail;
122 }
123
124 ok = 0;
125fail:
126 if (fd != -1)
127 close(fd);
128
129 return (ok);
130}
131
132static bool
133is_fido(const char *path)
134{
135 uint32_t usage = 0;
136 uint32_t usage_page = 0;
137 struct hidraw_report_descriptor hrd;
138
139 memset(&hrd, 0, sizeof(hrd));
140
141 if (get_report_descriptor(path, &hrd) < 0 ||
142 get_usage_info(&hrd, &usage_page, &usage) < 0) {
143 return (false);
144 }
145
146 return (usage_page == 0xf1d0);
147}
148
149static int
150parse_uevent(struct udev_device *dev, int16_t *vendor_id, int16_t *product_id)
151{
152 const char *uevent;
153 char *cp;
154 char *p;
155 char *s;
156 int ok = -1;
157 short unsigned int x;
158 short unsigned int y;
159
160 if ((uevent = udev_device_get_sysattr_value(dev, "uevent")) == NULL)
161 return (-1);
162
163 if ((s = cp = strdup(uevent)) == NULL)
164 return (-1);
165
166 for ((p = strsep(&cp, "\n")); p && *p != '\0'; (p = strsep(&cp, "\n"))) {
167 if (strncmp(p, "HID_ID=", 7) == 0) {
168 if (sscanf(p + 7, "%*x:%hx:%hx", &x, &y) == 2) {
169 *vendor_id = (int16_t)x;
170 *product_id = (int16_t)y;
171 ok = 0;
172 }
173 break;
174 }
175 }
176
177 free(s);
178
179 return (ok);
180}
181
182static int
183copy_info(fido_dev_info_t *di, struct udev *udev,
184 struct udev_list_entry *udev_entry)
185{
186 const char *name;
187 const char *path;
188 const char *manufacturer;
189 const char *product;
190 struct udev_device *dev = NULL;
191 struct udev_device *hid_parent;
192 struct udev_device *usb_parent;
193 int ok = -1;
194
195 memset(di, 0, sizeof(*di));
196
197 if ((name = udev_list_entry_get_name(udev_entry)) == NULL ||
198 (dev = udev_device_new_from_syspath(udev, name)) == NULL ||
199 (path = udev_device_get_devnode(dev)) == NULL ||
200 is_fido(path) == 0)
201 goto fail;
202
203 if ((hid_parent = udev_device_get_parent_with_subsystem_devtype(dev,
204 "hid", NULL)) == NULL)
205 goto fail;
206
207 if ((usb_parent = udev_device_get_parent_with_subsystem_devtype(dev,
208 "usb", "usb_device")) == NULL)
209 goto fail;
210
211 if (parse_uevent(hid_parent, &di->vendor_id, &di->product_id) < 0 ||
212 (manufacturer = udev_device_get_sysattr_value(usb_parent,
213 "manufacturer")) == NULL ||
214 (product = udev_device_get_sysattr_value(usb_parent,
215 "product")) == NULL)
216 goto fail;
217
218 di->path = strdup(path);
219 di->manufacturer = strdup(manufacturer);
220 di->product = strdup(product);
221
222 if (di->path == NULL ||
223 di->manufacturer == NULL ||
224 di->product == NULL)
225 goto fail;
226
227 ok = 0;
228fail:
229 if (dev != NULL)
230 udev_device_unref(dev);
231
232 if (ok < 0) {
233 free(di->path);
234 free(di->manufacturer);
235 free(di->product);
236 explicit_bzero(di, sizeof(*di));
237 }
238
239 return (ok);
240}
241
242int
243fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
244{
245 struct udev *udev = NULL;
246 struct udev_enumerate *udev_enum = NULL;
247 struct udev_list_entry *udev_list;
248 struct udev_list_entry *udev_entry;
249 int r = FIDO_ERR_INTERNAL;
250
251 *olen = 0;
252
253 if (ilen == 0)
254 return (FIDO_OK); /* nothing to do */
255
256 if (devlist == NULL)
257 return (FIDO_ERR_INVALID_ARGUMENT);
258
259 if ((udev = udev_new()) == NULL ||
260 (udev_enum = udev_enumerate_new(udev)) == NULL)
261 goto fail;
262
263 if (udev_enumerate_add_match_subsystem(udev_enum, "hidraw") < 0 ||
264 udev_enumerate_scan_devices(udev_enum) < 0 ||
265 (udev_list = udev_enumerate_get_list_entry(udev_enum)) == NULL)
266 goto fail;
267
268 udev_list_entry_foreach(udev_entry, udev_list) {
269 if (copy_info(&devlist[*olen], udev, udev_entry) == 0) {
270 if (++(*olen) == ilen)
271 break;
272 }
273 }
274
275 r = FIDO_OK;
276fail:
277 if (udev_enum != NULL)
278 udev_enumerate_unref(udev_enum);
279 if (udev != NULL)
280 udev_unref(udev);
281
282 return (r);
283}
284
285void *
286fido_hid_open(const char *path)
287{
288 int *fd;
289
290 if ((fd = malloc(sizeof(*fd))) == NULL ||
291 (*fd = open(path, O_RDWR)) < 0) {
292 free(fd);
293 return (NULL);
294 }
295
296 return (fd);
297}
298
299void
300fido_hid_close(void *handle)
301{
302 int *fd = handle;
303
304 close(*fd);
305 free(fd);
306}
307
308int
309fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
310{
311 int *fd = handle;
312 ssize_t r;
313
314 (void)ms; /* XXX */
315
316 if (len != REPORT_LEN - 1) {
317 fido_log_debug("%s: invalid len", __func__);
318 return (-1);
319 }
320
321 if ((r = read(*fd, buf, len)) < 0 || r != REPORT_LEN - 1)
322 return (-1);
323
324 return (REPORT_LEN - 1);
325}
326
327int
328fido_hid_write(void *handle, const unsigned char *buf, size_t len)
329{
330 int *fd = handle;
331 ssize_t r;
332
333 if (len != REPORT_LEN) {
334 fido_log_debug("%s: invalid len", __func__);
335 return (-1);
336 }
337
338 if ((r = write(*fd, buf, len)) < 0 || r != REPORT_LEN) {
339 fido_log_debug("%s: write", __func__);
340 return (-1);
341 }
342
343 return (REPORT_LEN);
344}
diff --git a/src/hid_openbsd.c b/src/hid_openbsd.c
new file mode 100644
index 0000000..8b92bd6
--- /dev/null
+++ b/src/hid_openbsd.c
@@ -0,0 +1,277 @@
1/*
2 * Copyright (c) 2019 Google LLC. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <sys/types.h>
8
9#include <sys/ioctl.h>
10#include <dev/usb/usb.h>
11#include <dev/usb/usbhid.h>
12
13#include <errno.h>
14#include <fcntl.h>
15#include <string.h>
16#include <unistd.h>
17#include <usbhid.h>
18#include <poll.h>
19
20#include "fido.h"
21
22#define MAX_UHID 64
23#define MAX_REPORT_LEN (sizeof(((struct usb_ctl_report *)(NULL))->ucr_data))
24
25struct hid_openbsd {
26 int fd;
27 size_t report_in_len;
28 size_t report_out_len;
29};
30
31int
32fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
33{
34 size_t i;
35 char path[64];
36 int is_fido, fd;
37 struct usb_device_info udi;
38 report_desc_t rdesc = NULL;
39 hid_data_t hdata = NULL;
40 hid_item_t hitem;
41 fido_dev_info_t *di;
42
43 if (ilen == 0)
44 return (FIDO_OK); /* nothing to do */
45
46 if (devlist == NULL || olen == NULL)
47 return (FIDO_ERR_INVALID_ARGUMENT);
48
49 for (i = *olen = 0; i < MAX_UHID && *olen < ilen; i++) {
50 snprintf(path, sizeof(path), "/dev/uhid%zu", i);
51 if ((fd = open(path, O_RDWR)) == -1) {
52 if (errno != ENOENT && errno != ENXIO) {
53 fido_log_debug("%s: open %s: %s", __func__,
54 path, strerror(errno));
55 }
56 continue;
57 }
58 memset(&udi, 0, sizeof(udi));
59 if (ioctl(fd, USB_GET_DEVICEINFO, &udi) != 0) {
60 fido_log_debug("%s: get device info %s: %s", __func__,
61 path, strerror(errno));
62 close(fd);
63 continue;
64 }
65 if ((rdesc = hid_get_report_desc(fd)) == NULL) {
66 fido_log_debug("%s: failed to get report descriptor: %s",
67 __func__, path);
68 close(fd);
69 continue;
70 }
71 if ((hdata = hid_start_parse(rdesc,
72 1<<hid_collection, -1)) == NULL) {
73 fido_log_debug("%s: failed to parse report descriptor: %s",
74 __func__, path);
75 hid_dispose_report_desc(rdesc);
76 close(fd);
77 continue;
78 }
79 is_fido = 0;
80 for (is_fido = 0; !is_fido;) {
81 memset(&hitem, 0, sizeof(hitem));
82 if (hid_get_item(hdata, &hitem) <= 0)
83 break;
84 if ((hitem._usage_page & 0xFFFF0000) == 0xf1d00000)
85 is_fido = 1;
86 }
87 hid_end_parse(hdata);
88 hid_dispose_report_desc(rdesc);
89 close(fd);
90
91 if (!is_fido)
92 continue;
93
94 fido_log_debug("%s: %s: bus = 0x%02x, addr = 0x%02x",
95 __func__, path, udi.udi_bus, udi.udi_addr);
96 fido_log_debug("%s: %s: vendor = \"%s\", product = \"%s\"",
97 __func__, path, udi.udi_vendor, udi.udi_product);
98 fido_log_debug("%s: %s: productNo = 0x%04x, vendorNo = 0x%04x, "
99 "releaseNo = 0x%04x", __func__, path, udi.udi_productNo,
100 udi.udi_vendorNo, udi.udi_releaseNo);
101
102 di = &devlist[*olen];
103 memset(di, 0, sizeof(*di));
104 if ((di->path = strdup(path)) == NULL ||
105 (di->manufacturer = strdup(udi.udi_vendor)) == NULL ||
106 (di->product = strdup(udi.udi_product)) == NULL) {
107 free(di->path);
108 free(di->manufacturer);
109 free(di->product);
110 explicit_bzero(di, sizeof(*di));
111 return FIDO_ERR_INTERNAL;
112 }
113 di->vendor_id = udi.udi_vendorNo;
114 di->product_id = udi.udi_productNo;
115 (*olen)++;
116 }
117
118 return FIDO_OK;
119}
120
121/*
122 * Workaround for OpenBSD <=6.6-current (as of 201910) bug that loses
123 * sync of DATA0/DATA1 sequence bit across uhid open/close.
124 * Send pings until we get a response - early pings with incorrect
125 * sequence bits will be ignored as duplicate packets by the device.
126 */
127static int
128terrible_ping_kludge(struct hid_openbsd *ctx)
129{
130 u_char data[256];
131 int i, n;
132 struct pollfd pfd;
133
134 if (sizeof(data) < ctx->report_out_len + 1)
135 return -1;
136 for (i = 0; i < 4; i++) {
137 memset(data, 0, sizeof(data));
138 /* broadcast channel ID */
139 data[1] = 0xff;
140 data[2] = 0xff;
141 data[3] = 0xff;
142 data[4] = 0xff;
143 /* Ping command */
144 data[5] = 0x81;
145 /* One byte ping only, Vasili */
146 data[6] = 0;
147 data[7] = 1;
148 fido_log_debug("%s: send ping %d", __func__, i);
149 if (fido_hid_write(ctx, data, ctx->report_out_len + 1) == -1)
150 return -1;
151 fido_log_debug("%s: wait reply", __func__);
152 memset(&pfd, 0, sizeof(pfd));
153 pfd.fd = ctx->fd;
154 pfd.events = POLLIN;
155 if ((n = poll(&pfd, 1, 100)) == -1) {
156 fido_log_debug("%s: poll: %s", __func__, strerror(errno));
157 return -1;
158 } else if (n == 0) {
159 fido_log_debug("%s: timed out", __func__);
160 continue;
161 }
162 if (fido_hid_read(ctx, data, ctx->report_out_len, 250) == -1)
163 return -1;
164 /*
165 * Ping isn't always supported on the broadcast channel,
166 * so we might get an error, but we don't care - we're
167 * synched now.
168 */
169 fido_log_debug("%s: got reply", __func__);
170 fido_log_xxd(data, ctx->report_out_len);
171 return 0;
172 }
173 fido_log_debug("%s: no response", __func__);
174 return -1;
175}
176
177void *
178fido_hid_open(const char *path)
179{
180 struct hid_openbsd *ret = NULL;
181 report_desc_t rdesc = NULL;
182 int len, usb_report_id = 0;
183
184 if ((ret = calloc(1, sizeof(*ret))) == NULL ||
185 (ret->fd = open(path, O_RDWR)) < 0) {
186 free(ret);
187 return (NULL);
188 }
189 if (ioctl(ret->fd, USB_GET_REPORT_ID, &usb_report_id) != 0) {
190 fido_log_debug("%s: failed to get report ID: %s", __func__,
191 strerror(errno));
192 goto fail;
193 }
194 if ((rdesc = hid_get_report_desc(ret->fd)) == NULL) {
195 fido_log_debug("%s: failed to get report descriptor", __func__);
196 goto fail;
197 }
198 if ((len = hid_report_size(rdesc, hid_input, usb_report_id)) <= 0 ||
199 (size_t)len > MAX_REPORT_LEN) {
200 fido_log_debug("%s: bad input report size %d", __func__, len);
201 goto fail;
202 }
203 ret->report_in_len = (size_t)len;
204 if ((len = hid_report_size(rdesc, hid_output, usb_report_id)) <= 0 ||
205 (size_t)len > MAX_REPORT_LEN) {
206 fido_log_debug("%s: bad output report size %d", __func__, len);
207 fail:
208 hid_dispose_report_desc(rdesc);
209 close(ret->fd);
210 free(ret);
211 return NULL;
212 }
213 ret->report_out_len = (size_t)len;
214 hid_dispose_report_desc(rdesc);
215 fido_log_debug("%s: USB report ID %d, inlen = %zu outlen = %zu",
216 __func__, usb_report_id, ret->report_in_len, ret->report_out_len);
217
218 /*
219 * OpenBSD (as of 201910) has a bug that causes it to lose
220 * track of the DATA0/DATA1 sequence toggle across uhid device
221 * open and close. This is a terrible hack to work around it.
222 */
223 if (terrible_ping_kludge(ret) != 0) {
224 fido_hid_close(ret);
225 return NULL;
226 }
227
228 return (ret);
229}
230
231void
232fido_hid_close(void *handle)
233{
234 struct hid_openbsd *ctx = (struct hid_openbsd *)handle;
235
236 close(ctx->fd);
237 free(ctx);
238}
239
240int
241fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
242{
243 struct hid_openbsd *ctx = (struct hid_openbsd *)handle;
244 ssize_t r;
245
246 (void)ms; /* XXX */
247
248 if (len != ctx->report_in_len) {
249 fido_log_debug("%s: invalid len: got %zu, want %zu", __func__,
250 len, ctx->report_in_len);
251 return (-1);
252 }
253 if ((r = read(ctx->fd, buf, len)) == -1 || (size_t)r != len) {
254 fido_log_debug("%s: read: %s", __func__, strerror(errno));
255 return (-1);
256 }
257 return ((int)len);
258}
259
260int
261fido_hid_write(void *handle, const unsigned char *buf, size_t len)
262{
263 struct hid_openbsd *ctx = (struct hid_openbsd *)handle;
264 ssize_t r;
265
266 if (len != ctx->report_out_len + 1) {
267 fido_log_debug("%s: invalid len: got %zu, want %zu", __func__,
268 len, ctx->report_out_len);
269 return (-1);
270 }
271 if ((r = write(ctx->fd, buf + 1, len - 1)) == -1 ||
272 (size_t)r != len - 1) {
273 fido_log_debug("%s: write: %s", __func__, strerror(errno));
274 return (-1);
275 }
276 return ((int)len);
277}
diff --git a/src/hid_osx.c b/src/hid_osx.c
new file mode 100644
index 0000000..b705b43
--- /dev/null
+++ b/src/hid_osx.c
@@ -0,0 +1,410 @@
1/*
2 * Copyright (c) 2019 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <sys/types.h>
8
9#include <fcntl.h>
10#include <string.h>
11#ifdef HAVE_UNISTD_H
12#include <unistd.h>
13#endif
14
15#include <CoreFoundation/CoreFoundation.h>
16#include <IOKit/IOKitLib.h>
17#include <IOKit/hid/IOHIDKeys.h>
18#include <IOKit/hid/IOHIDManager.h>
19
20#include "fido.h"
21
22#define REPORT_LEN 65
23
24struct dev {
25 IOHIDDeviceRef ref;
26 CFStringRef loop_id;
27};
28
29static int
30get_int32(IOHIDDeviceRef dev, CFStringRef key, int32_t *v)
31{
32 CFTypeRef ref;
33
34 if ((ref = IOHIDDeviceGetProperty(dev, key)) == NULL ||
35 CFGetTypeID(ref) != CFNumberGetTypeID()) {
36 fido_log_debug("%s: IOHIDDeviceGetProperty", __func__);
37 return (-1);
38 }
39
40 if (CFNumberGetType(ref) != kCFNumberSInt32Type &&
41 CFNumberGetType(ref) != kCFNumberSInt64Type) {
42 fido_log_debug("%s: CFNumberGetType", __func__);
43 return (-1);
44 }
45
46 if (CFNumberGetValue(ref, kCFNumberSInt32Type, v) == false) {
47 fido_log_debug("%s: CFNumberGetValue", __func__);
48 return (-1);
49 }
50
51 return (0);
52}
53
54static int
55get_utf8(IOHIDDeviceRef dev, CFStringRef key, void *buf, size_t len)
56{
57 CFTypeRef ref;
58
59 memset(buf, 0, len);
60
61 if ((ref = IOHIDDeviceGetProperty(dev, key)) == NULL ||
62 CFGetTypeID(ref) != CFStringGetTypeID()) {
63 fido_log_debug("%s: IOHIDDeviceGetProperty", __func__);
64 return (-1);
65 }
66
67 if (CFStringGetCString(ref, buf, len, kCFStringEncodingUTF8) == false) {
68 fido_log_debug("%s: CFStringGetCString", __func__);
69 return (-1);
70 }
71
72 return (0);
73}
74
75static bool
76is_fido(IOHIDDeviceRef dev)
77{
78 uint32_t usage_page;
79 int32_t report_len;
80
81 if (get_int32(dev, CFSTR(kIOHIDPrimaryUsagePageKey),
82 (int32_t *)&usage_page) != 0 || usage_page != 0xf1d0)
83 return (false);
84
85 if (get_int32(dev, CFSTR(kIOHIDMaxInputReportSizeKey),
86 &report_len) < 0 || report_len != REPORT_LEN - 1) {
87 fido_log_debug("%s: unsupported report len", __func__);
88 return (false);
89 }
90
91 return (true);
92}
93
94static int
95get_id(IOHIDDeviceRef dev, int16_t *vendor_id, int16_t *product_id)
96{
97 int32_t vendor;
98 int32_t product;
99
100 if (get_int32(dev, CFSTR(kIOHIDVendorIDKey), &vendor) < 0 ||
101 vendor > UINT16_MAX) {
102 fido_log_debug("%s: get_int32 vendor", __func__);
103 return (-1);
104 }
105
106 if (get_int32(dev, CFSTR(kIOHIDProductIDKey), &product) < 0 ||
107 product > UINT16_MAX) {
108 fido_log_debug("%s: get_int32 product", __func__);
109 return (-1);
110 }
111
112 *vendor_id = (int16_t)vendor;
113 *product_id = (int16_t)product;
114
115 return (0);
116}
117
118static int
119get_str(IOHIDDeviceRef dev, char **manufacturer, char **product)
120{
121 char buf[512];
122 int ok = -1;
123
124 *manufacturer = NULL;
125 *product = NULL;
126
127 if (get_utf8(dev, CFSTR(kIOHIDManufacturerKey), buf, sizeof(buf)) < 0) {
128 fido_log_debug("%s: get_utf8 manufacturer", __func__);
129 goto fail;
130 }
131
132 if ((*manufacturer = strdup(buf)) == NULL) {
133 fido_log_debug("%s: strdup manufacturer", __func__);
134 goto fail;
135 }
136
137 if (get_utf8(dev, CFSTR(kIOHIDProductKey), buf, sizeof(buf)) < 0) {
138 fido_log_debug("%s: get_utf8 product", __func__);
139 goto fail;
140 }
141
142 if ((*product = strdup(buf)) == NULL) {
143 fido_log_debug("%s: strdup product", __func__);
144 goto fail;
145 }
146
147 ok = 0;
148fail:
149 if (ok < 0) {
150 free(*manufacturer);
151 free(*product);
152 *manufacturer = NULL;
153 *product = NULL;
154 }
155
156 return (ok);
157}
158
159static char *
160get_path(IOHIDDeviceRef dev)
161{
162 io_service_t s;
163 io_string_t path;
164
165 if ((s = IOHIDDeviceGetService(dev)) == MACH_PORT_NULL) {
166 fido_log_debug("%s: IOHIDDeviceGetService", __func__);
167 return (NULL);
168 }
169
170 if (IORegistryEntryGetPath(s, kIOServicePlane, path) != KERN_SUCCESS) {
171 fido_log_debug("%s: IORegistryEntryGetPath", __func__);
172 return (NULL);
173 }
174
175 return (strdup(path));
176}
177
178static int
179copy_info(fido_dev_info_t *di, IOHIDDeviceRef dev)
180{
181 memset(di, 0, sizeof(*di));
182
183 if (is_fido(dev) == false)
184 return (-1);
185
186 if (get_id(dev, &di->vendor_id, &di->product_id) < 0 ||
187 get_str(dev, &di->manufacturer, &di->product) < 0 ||
188 (di->path = get_path(dev)) == NULL) {
189 free(di->path);
190 free(di->manufacturer);
191 free(di->product);
192 explicit_bzero(di, sizeof(*di));
193 return (-1);
194 }
195
196 return (0);
197}
198
199int
200fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
201{
202 IOHIDManagerRef manager = NULL;
203 CFSetRef devset = NULL;
204 CFIndex devcnt;
205 IOHIDDeviceRef *devs = NULL;
206 int r = FIDO_ERR_INTERNAL;
207
208 *olen = 0;
209
210 if (ilen == 0)
211 return (FIDO_OK); /* nothing to do */
212
213 if (devlist == NULL)
214 return (FIDO_ERR_INVALID_ARGUMENT);
215
216 if ((manager = IOHIDManagerCreate(kCFAllocatorDefault,
217 kIOHIDManagerOptionNone)) == NULL) {
218 fido_log_debug("%s: IOHIDManagerCreate", __func__);
219 goto fail;
220 }
221
222 IOHIDManagerSetDeviceMatching(manager, NULL);
223
224 if ((devset = IOHIDManagerCopyDevices(manager)) == NULL) {
225 fido_log_debug("%s: IOHIDManagerCopyDevices", __func__);
226 goto fail;
227 }
228
229 if ((devcnt = CFSetGetCount(devset)) < 0) {
230 fido_log_debug("%s: CFSetGetCount", __func__);
231 goto fail;
232 }
233
234 if ((devs = calloc(devcnt, sizeof(*devs))) == NULL) {
235 fido_log_debug("%s: calloc", __func__);
236 goto fail;
237 }
238
239 CFSetGetValues(devset, (void *)devs);
240
241 for (CFIndex i = 0; i < devcnt; i++) {
242 if (copy_info(&devlist[*olen], devs[i]) == 0) {
243 if (++(*olen) == ilen)
244 break;
245 }
246 }
247
248 r = FIDO_OK;
249fail:
250 if (manager != NULL)
251 CFRelease(manager);
252 if (devset != NULL)
253 CFRelease(devset);
254
255 free(devs);
256
257 return (r);
258}
259
260void *
261fido_hid_open(const char *path)
262{
263 io_registry_entry_t entry = MACH_PORT_NULL;
264 struct dev *dev = NULL;
265 int ok = -1;
266 int r;
267 char loop_id[32];
268
269 if ((dev = calloc(1, sizeof(*dev))) == NULL) {
270 fido_log_debug("%s: calloc", __func__);
271 goto fail;
272 }
273
274 if ((entry = IORegistryEntryFromPath(kIOMasterPortDefault,
275 path)) == MACH_PORT_NULL) {
276 fido_log_debug("%s: IORegistryEntryFromPath", __func__);
277 goto fail;
278 }
279
280 if ((dev->ref = IOHIDDeviceCreate(kCFAllocatorDefault,
281 entry)) == NULL) {
282 fido_log_debug("%s: IOHIDDeviceCreate", __func__);
283 goto fail;
284 }
285
286 if (IOHIDDeviceOpen(dev->ref,
287 kIOHIDOptionsTypeSeizeDevice) != kIOReturnSuccess) {
288 fido_log_debug("%s: IOHIDDeviceOpen", __func__);
289 goto fail;
290 }
291
292 if ((r = snprintf(loop_id, sizeof(loop_id), "fido2-%p",
293 (void *)dev->ref)) < 0 || (size_t)r >= sizeof(loop_id)) {
294 fido_log_debug("%s: snprintf", __func__);
295 goto fail;
296 }
297
298 if ((dev->loop_id = CFStringCreateWithCString(NULL, loop_id,
299 kCFStringEncodingASCII)) == NULL) {
300 fido_log_debug("%s: CFStringCreateWithCString", __func__);
301 goto fail;
302 }
303
304 ok = 0;
305fail:
306 if (entry != MACH_PORT_NULL)
307 IOObjectRelease(entry);
308
309 if (ok < 0 && dev != NULL) {
310 if (dev->ref != NULL)
311 CFRelease(dev->ref);
312 if (dev->loop_id != NULL)
313 CFRelease(dev->loop_id);
314 free(dev);
315 dev = NULL;
316 }
317
318 return (dev);
319}
320
321void
322fido_hid_close(void *handle)
323{
324 struct dev *dev = handle;
325
326 if (IOHIDDeviceClose(dev->ref,
327 kIOHIDOptionsTypeSeizeDevice) != kIOReturnSuccess)
328 fido_log_debug("%s: IOHIDDeviceClose", __func__);
329
330 CFRelease(dev->ref);
331 CFRelease(dev->loop_id);
332
333 free(dev);
334}
335
336static void
337read_callback(void *context, IOReturn result, void *dev, IOHIDReportType type,
338 uint32_t report_id, uint8_t *report, CFIndex report_len)
339{
340 (void)context;
341 (void)dev;
342 (void)report;
343
344 if (result != kIOReturnSuccess || type != kIOHIDReportTypeInput ||
345 report_id != 0 || report_len != REPORT_LEN - 1) {
346 fido_log_debug("%s: io error", __func__);
347 }
348}
349
350static void
351removal_callback(void *context, IOReturn result, void *sender)
352{
353 (void)context;
354 (void)result;
355 (void)sender;
356
357 CFRunLoopStop(CFRunLoopGetCurrent());
358}
359
360int
361fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
362{
363 struct dev *dev = handle;
364 CFRunLoopRunResult r;
365
366 (void)ms; /* XXX */
367
368 if (len != REPORT_LEN - 1) {
369 fido_log_debug("%s: invalid len", __func__);
370 return (-1);
371 }
372
373 explicit_bzero(buf, len);
374
375 IOHIDDeviceRegisterInputReportCallback(dev->ref, buf, len,
376 &read_callback, NULL);
377 IOHIDDeviceRegisterRemovalCallback(dev->ref, &removal_callback, dev);
378 IOHIDDeviceScheduleWithRunLoop(dev->ref, CFRunLoopGetCurrent(),
379 dev->loop_id);
380
381 do
382 r = CFRunLoopRunInMode(dev->loop_id, 0.003, true);
383 while (r != kCFRunLoopRunHandledSource);
384
385 IOHIDDeviceRegisterInputReportCallback(dev->ref, buf, len, NULL, NULL);
386 IOHIDDeviceRegisterRemovalCallback(dev->ref, NULL, NULL);
387 IOHIDDeviceUnscheduleFromRunLoop(dev->ref, CFRunLoopGetCurrent(),
388 dev->loop_id);
389
390 return (REPORT_LEN - 1);
391}
392
393int
394fido_hid_write(void *handle, const unsigned char *buf, size_t len)
395{
396 struct dev *dev = handle;
397
398 if (len != REPORT_LEN) {
399 fido_log_debug("%s: invalid len", __func__);
400 return (-1);
401 }
402
403 if (IOHIDDeviceSetReport(dev->ref, kIOHIDReportTypeOutput, 0, buf + 1,
404 len - 1) != kIOReturnSuccess) {
405 fido_log_debug("%s: IOHIDDeviceSetReport", __func__);
406 return (-1);
407 }
408
409 return (REPORT_LEN);
410}
diff --git a/src/hid_win.c b/src/hid_win.c
new file mode 100644
index 0000000..6d93778
--- /dev/null
+++ b/src/hid_win.c
@@ -0,0 +1,324 @@
1/*
2 * Copyright (c) 2019 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <sys/types.h>
8
9#include <fcntl.h>
10#include <string.h>
11#ifdef HAVE_UNISTD_H
12#include <unistd.h>
13#endif
14#include <windows.h>
15#include <setupapi.h>
16#include <initguid.h>
17#include <hidclass.h>
18#include <hidsdi.h>
19
20#include "fido.h"
21
22#define REPORT_LEN 65
23
24static bool
25is_fido(HANDLE dev)
26{
27 PHIDP_PREPARSED_DATA data = NULL;
28 HIDP_CAPS caps;
29 uint16_t usage_page = 0;
30
31 if (HidD_GetPreparsedData(dev, &data) == false) {
32 fido_log_debug("%s: HidD_GetPreparsedData", __func__);
33 goto fail;
34 }
35
36 if (HidP_GetCaps(data, &caps) != HIDP_STATUS_SUCCESS) {
37 fido_log_debug("%s: HidP_GetCaps", __func__);
38 goto fail;
39 }
40
41 if (caps.OutputReportByteLength != REPORT_LEN ||
42 caps.InputReportByteLength != REPORT_LEN) {
43 fido_log_debug("%s: unsupported report len", __func__);
44 goto fail;
45 }
46
47 usage_page = caps.UsagePage;
48fail:
49 if (data != NULL)
50 HidD_FreePreparsedData(data);
51
52 return (usage_page == 0xf1d0);
53}
54
55static int
56get_int(HANDLE dev, int16_t *vendor_id, int16_t *product_id)
57{
58 HIDD_ATTRIBUTES attr;
59
60 attr.Size = sizeof(attr);
61
62 if (HidD_GetAttributes(dev, &attr) == false) {
63 fido_log_debug("%s: HidD_GetAttributes", __func__);
64 return (-1);
65 }
66
67 *vendor_id = attr.VendorID;
68 *product_id = attr.ProductID;
69
70 return (0);
71}
72
73static int
74get_str(HANDLE dev, char **manufacturer, char **product)
75{
76 wchar_t buf[512];
77 int utf8_len;
78 int ok = -1;
79
80 *manufacturer = NULL;
81 *product = NULL;
82
83 if (HidD_GetManufacturerString(dev, &buf, sizeof(buf)) == false) {
84 fido_log_debug("%s: HidD_GetManufacturerString", __func__);
85 goto fail;
86 }
87
88 if ((utf8_len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf,
89 -1, NULL, 0, NULL, NULL)) <= 0 || utf8_len > 128) {
90 fido_log_debug("%s: WideCharToMultiByte", __func__);
91 goto fail;
92 }
93
94 if ((*manufacturer = malloc(utf8_len)) == NULL) {
95 fido_log_debug("%s: malloc", __func__);
96 goto fail;
97 }
98
99 if (WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf, -1,
100 *manufacturer, utf8_len, NULL, NULL) != utf8_len) {
101 fido_log_debug("%s: WideCharToMultiByte", __func__);
102 goto fail;
103 }
104
105 if (HidD_GetProductString(dev, &buf, sizeof(buf)) == false) {
106 fido_log_debug("%s: HidD_GetProductString", __func__);
107 goto fail;
108 }
109
110 if ((utf8_len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf,
111 -1, NULL, 0, NULL, NULL)) <= 0 || utf8_len > 128) {
112 fido_log_debug("%s: WideCharToMultiByte", __func__);
113 goto fail;
114 }
115
116 if ((*product = malloc(utf8_len)) == NULL) {
117 fido_log_debug("%s: malloc", __func__);
118 goto fail;
119 }
120
121 if (WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, buf, -1,
122 *product, utf8_len, NULL, NULL) != utf8_len) {
123 fido_log_debug("%s: WideCharToMultiByte", __func__);
124 goto fail;
125 }
126
127 ok = 0;
128fail:
129 if (ok < 0) {
130 free(*manufacturer);
131 free(*product);
132 *manufacturer = NULL;
133 *product = NULL;
134 }
135
136 return (ok);
137}
138
139static int
140copy_info(fido_dev_info_t *di, const char *path)
141{
142 HANDLE dev = INVALID_HANDLE_VALUE;
143 int ok = -1;
144
145 memset(di, 0, sizeof(*di));
146
147 dev = CreateFileA(path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
148 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
149 if (dev == INVALID_HANDLE_VALUE || is_fido(dev) == 0)
150 goto fail;
151
152 if (get_int(dev, &di->vendor_id, &di->product_id) < 0 ||
153 get_str(dev, &di->manufacturer, &di->product) < 0)
154 goto fail;
155
156 if ((di->path = strdup(path)) == NULL)
157 goto fail;
158
159 ok = 0;
160fail:
161 if (dev != INVALID_HANDLE_VALUE)
162 CloseHandle(dev);
163
164 if (ok < 0) {
165 free(di->path);
166 free(di->manufacturer);
167 free(di->product);
168 explicit_bzero(di, sizeof(*di));
169 }
170
171 return (ok);
172}
173
174int
175fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
176{
177 GUID hid_guid = GUID_DEVINTERFACE_HID;
178 HDEVINFO devinfo = INVALID_HANDLE_VALUE;
179 SP_DEVICE_INTERFACE_DATA ifdata;
180 SP_DEVICE_INTERFACE_DETAIL_DATA_A *ifdetail = NULL;
181 DWORD len = 0;
182 DWORD idx = 0;
183 int r = FIDO_ERR_INTERNAL;
184
185 *olen = 0;
186
187 if (ilen == 0)
188 return (FIDO_OK); /* nothing to do */
189
190 if (devlist == NULL)
191 return (FIDO_ERR_INVALID_ARGUMENT);
192
193 devinfo = SetupDiGetClassDevsA(&hid_guid, NULL, NULL,
194 DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
195 if (devinfo == INVALID_HANDLE_VALUE) {
196 fido_log_debug("%s: SetupDiGetClassDevsA", __func__);
197 goto fail;
198 }
199
200 ifdata.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
201
202 while (SetupDiEnumDeviceInterfaces(devinfo, NULL, &hid_guid, idx++,
203 &ifdata) == true) {
204 /*
205 * "Get the required buffer size. Call
206 * SetupDiGetDeviceInterfaceDetail with a NULL
207 * DeviceInterfaceDetailData pointer, a
208 * DeviceInterfaceDetailDataSize of zero, and a valid
209 * RequiredSize variable. In response to such a call, this
210 * function returns the required buffer size at RequiredSize
211 * and fails with GetLastError returning
212 * ERROR_INSUFFICIENT_BUFFER."
213 */
214 if (SetupDiGetDeviceInterfaceDetailA(devinfo, &ifdata, NULL, 0,
215 &len, NULL) != false ||
216 GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
217 fido_log_debug("%s: SetupDiGetDeviceInterfaceDetailA 1",
218 __func__);
219 goto fail;
220 }
221
222 if ((ifdetail = malloc(len)) == NULL) {
223 fido_log_debug("%s: malloc", __func__);
224 goto fail;
225 }
226
227 ifdetail->cbSize = sizeof(*ifdetail);
228
229 if (SetupDiGetDeviceInterfaceDetailA(devinfo, &ifdata, ifdetail,
230 len, NULL, NULL) == false) {
231 fido_log_debug("%s: SetupDiGetDeviceInterfaceDetailA 2",
232 __func__);
233 goto fail;
234 }
235
236 if (copy_info(&devlist[*olen], ifdetail->DevicePath) == 0) {
237 if (++(*olen) == ilen)
238 break;
239 }
240
241 free(ifdetail);
242 ifdetail = NULL;
243 }
244
245 r = FIDO_OK;
246fail:
247 if (devinfo != INVALID_HANDLE_VALUE)
248 SetupDiDestroyDeviceInfoList(devinfo);
249
250 free(ifdetail);
251
252 return (r);
253}
254
255void *
256fido_hid_open(const char *path)
257{
258 HANDLE dev;
259
260 dev = CreateFileA(path, GENERIC_READ | GENERIC_WRITE,
261 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
262 FILE_ATTRIBUTE_NORMAL, NULL);
263
264 if (dev == INVALID_HANDLE_VALUE)
265 return (NULL);
266
267 return (dev);
268}
269
270void
271fido_hid_close(void *handle)
272{
273 CloseHandle(handle);
274}
275
276int
277fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
278{
279 DWORD n;
280 int r = -1;
281 uint8_t report[REPORT_LEN];
282
283 (void)ms; /* XXX */
284
285 memset(report, 0, sizeof(report));
286
287 if (len != sizeof(report) - 1) {
288 fido_log_debug("%s: invalid len", __func__);
289 return (-1);
290 }
291
292 if (ReadFile(handle, report, sizeof(report), &n, NULL) == false ||
293 n != sizeof(report)) {
294 fido_log_debug("%s: ReadFile", __func__);
295 goto fail;
296 }
297
298 r = sizeof(report) - 1;
299 memcpy(buf, report + 1, len);
300
301fail:
302 explicit_bzero(report, sizeof(report));
303
304 return (r);
305}
306
307int
308fido_hid_write(void *handle, const unsigned char *buf, size_t len)
309{
310 DWORD n;
311
312 if (len != REPORT_LEN) {
313 fido_log_debug("%s: invalid len", __func__);
314 return (-1);
315 }
316
317 if (WriteFile(handle, buf, (DWORD)len, &n, NULL) == false ||
318 n != REPORT_LEN) {
319 fido_log_debug("%s: WriteFile", __func__);
320 return (-1);
321 }
322
323 return (REPORT_LEN);
324}
diff --git a/src/info.c b/src/info.c
new file mode 100644
index 0000000..e896503
--- /dev/null
+++ b/src/info.c
@@ -0,0 +1,410 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <string.h>
8#include "fido.h"
9
10static int
11decode_version(const cbor_item_t *item, void *arg)
12{
13 fido_str_array_t *v = arg;
14 const size_t i = v->len;
15
16 /* keep ptr[x] and len consistent */
17 if (cbor_string_copy(item, &v->ptr[i]) < 0) {
18 fido_log_debug("%s: cbor_string_copy", __func__);
19 return (-1);
20 }
21
22 v->len++;
23
24 return (0);
25}
26
27static int
28decode_versions(const cbor_item_t *item, fido_str_array_t *v)
29{
30 v->ptr = NULL;
31 v->len = 0;
32
33 if (cbor_isa_array(item) == false ||
34 cbor_array_is_definite(item) == false) {
35 fido_log_debug("%s: cbor type", __func__);
36 return (-1);
37 }
38
39 v->ptr = calloc(cbor_array_size(item), sizeof(char *));
40 if (v->ptr == NULL)
41 return (-1);
42
43 if (cbor_array_iter(item, v, decode_version) < 0) {
44 fido_log_debug("%s: decode_version", __func__);
45 return (-1);
46 }
47
48 return (0);
49}
50
51static int
52decode_extension(const cbor_item_t *item, void *arg)
53{
54 fido_str_array_t *e = arg;
55 const size_t i = e->len;
56
57 /* keep ptr[x] and len consistent */
58 if (cbor_string_copy(item, &e->ptr[i]) < 0) {
59 fido_log_debug("%s: cbor_string_copy", __func__);
60 return (-1);
61 }
62
63 e->len++;
64
65 return (0);
66}
67
68static int
69decode_extensions(const cbor_item_t *item, fido_str_array_t *e)
70{
71 e->ptr = NULL;
72 e->len = 0;
73
74 if (cbor_isa_array(item) == false ||
75 cbor_array_is_definite(item) == false) {
76 fido_log_debug("%s: cbor type", __func__);
77 return (-1);
78 }
79
80 e->ptr = calloc(cbor_array_size(item), sizeof(char *));
81 if (e->ptr == NULL)
82 return (-1);
83
84 if (cbor_array_iter(item, e, decode_extension) < 0) {
85 fido_log_debug("%s: decode_extension", __func__);
86 return (-1);
87 }
88
89 return (0);
90}
91
92static int
93decode_aaguid(const cbor_item_t *item, unsigned char *aaguid, size_t aaguid_len)
94{
95 if (cbor_isa_bytestring(item) == false ||
96 cbor_bytestring_is_definite(item) == false ||
97 cbor_bytestring_length(item) != aaguid_len) {
98 fido_log_debug("%s: cbor type", __func__);
99 return (-1);
100 }
101
102 memcpy(aaguid, cbor_bytestring_handle(item), aaguid_len);
103
104 return (0);
105}
106
107static int
108decode_option(const cbor_item_t *key, const cbor_item_t *val, void *arg)
109{
110 fido_opt_array_t *o = arg;
111 const size_t i = o->len;
112
113 if (cbor_isa_float_ctrl(val) == false ||
114 cbor_float_get_width(val) != CBOR_FLOAT_0 ||
115 cbor_is_bool(val) == false) {
116 fido_log_debug("%s: cbor type", __func__);
117 return (0); /* ignore */
118 }
119
120 if (cbor_string_copy(key, &o->name[i]) < 0) {
121 fido_log_debug("%s: cbor_string_copy", __func__);
122 return (0); /* ignore */
123 }
124
125 /* keep name/value and len consistent */
126 o->value[i] = cbor_ctrl_value(val) == CBOR_CTRL_TRUE;
127 o->len++;
128
129 return (0);
130}
131
132static int
133decode_options(const cbor_item_t *item, fido_opt_array_t *o)
134{
135 o->name = NULL;
136 o->value = NULL;
137 o->len = 0;
138
139 if (cbor_isa_map(item) == false ||
140 cbor_map_is_definite(item) == false) {
141 fido_log_debug("%s: cbor type", __func__);
142 return (-1);
143 }
144
145 o->name = calloc(cbor_map_size(item), sizeof(char *));
146 o->value = calloc(cbor_map_size(item), sizeof(bool));
147 if (o->name == NULL || o->value == NULL)
148 return (-1);
149
150 return (cbor_map_iter(item, o, decode_option));
151}
152
153static int
154decode_protocol(const cbor_item_t *item, void *arg)
155{
156 fido_byte_array_t *p = arg;
157 const size_t i = p->len;
158
159 if (cbor_isa_uint(item) == false ||
160 cbor_int_get_width(item) != CBOR_INT_8) {
161 fido_log_debug("%s: cbor type", __func__);
162 return (-1);
163 }
164
165 /* keep ptr[x] and len consistent */
166 p->ptr[i] = cbor_get_uint8(item);
167 p->len++;
168
169 return (0);
170}
171
172static int
173decode_protocols(const cbor_item_t *item, fido_byte_array_t *p)
174{
175 p->ptr = NULL;
176 p->len = 0;
177
178 if (cbor_isa_array(item) == false ||
179 cbor_array_is_definite(item) == false) {
180 fido_log_debug("%s: cbor type", __func__);
181 return (-1);
182 }
183
184 p->ptr = calloc(cbor_array_size(item), sizeof(uint8_t));
185 if (p->ptr == NULL)
186 return (-1);
187
188 if (cbor_array_iter(item, p, decode_protocol) < 0) {
189 fido_log_debug("%s: decode_protocol", __func__);
190 return (-1);
191 }
192
193 return (0);
194}
195
196static int
197parse_reply_element(const cbor_item_t *key, const cbor_item_t *val, void *arg)
198{
199 fido_cbor_info_t *ci = arg;
200
201 if (cbor_isa_uint(key) == false ||
202 cbor_int_get_width(key) != CBOR_INT_8) {
203 fido_log_debug("%s: cbor type", __func__);
204 return (0); /* ignore */
205 }
206
207 switch (cbor_get_uint8(key)) {
208 case 1: /* versions */
209 return (decode_versions(val, &ci->versions));
210 case 2: /* extensions */
211 return (decode_extensions(val, &ci->extensions));
212 case 3: /* aaguid */
213 return (decode_aaguid(val, ci->aaguid, sizeof(ci->aaguid)));
214 case 4: /* options */
215 return (decode_options(val, &ci->options));
216 case 5: /* maxMsgSize */
217 return (cbor_decode_uint64(val, &ci->maxmsgsiz));
218 case 6: /* pinProtocols */
219 return (decode_protocols(val, &ci->protocols));
220 default: /* ignore */
221 fido_log_debug("%s: cbor type", __func__);
222 return (0);
223 }
224}
225
226static int
227fido_dev_get_cbor_info_tx(fido_dev_t *dev)
228{
229 const unsigned char cbor[] = { CTAP_CBOR_GETINFO };
230 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
231
232 fido_log_debug("%s: dev=%p", __func__, (void *)dev);
233
234 if (fido_tx(dev, cmd, cbor, sizeof(cbor)) < 0) {
235 fido_log_debug("%s: fido_tx", __func__);
236 return (FIDO_ERR_TX);
237 }
238
239 return (FIDO_OK);
240}
241
242static int
243fido_dev_get_cbor_info_rx(fido_dev_t *dev, fido_cbor_info_t *ci, int ms)
244{
245 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
246 unsigned char reply[512];
247 int reply_len;
248
249 fido_log_debug("%s: dev=%p, ci=%p, ms=%d", __func__, (void *)dev,
250 (void *)ci, ms);
251
252 memset(ci, 0, sizeof(*ci));
253
254 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
255 fido_log_debug("%s: fido_rx", __func__);
256 return (FIDO_ERR_RX);
257 }
258
259 return (cbor_parse_reply(reply, (size_t)reply_len, ci,
260 parse_reply_element));
261}
262
263static int
264fido_dev_get_cbor_info_wait(fido_dev_t *dev, fido_cbor_info_t *ci, int ms)
265{
266 int r;
267
268 if ((r = fido_dev_get_cbor_info_tx(dev)) != FIDO_OK ||
269 (r = fido_dev_get_cbor_info_rx(dev, ci, ms)) != FIDO_OK)
270 return (r);
271
272 return (FIDO_OK);
273}
274
275int
276fido_dev_get_cbor_info(fido_dev_t *dev, fido_cbor_info_t *ci)
277{
278 return (fido_dev_get_cbor_info_wait(dev, ci, -1));
279}
280
281/*
282 * get/set functions for fido_cbor_info_t; always at the end of the file
283 */
284
285fido_cbor_info_t *
286fido_cbor_info_new(void)
287{
288 return (calloc(1, sizeof(fido_cbor_info_t)));
289}
290
291static void
292free_str_array(fido_str_array_t *sa)
293{
294 for (size_t i = 0; i < sa->len; i++)
295 free(sa->ptr[i]);
296
297 free(sa->ptr);
298 sa->ptr = NULL;
299 sa->len = 0;
300}
301
302static void
303free_opt_array(fido_opt_array_t *oa)
304{
305 for (size_t i = 0; i < oa->len; i++)
306 free(oa->name[i]);
307
308 free(oa->name);
309 free(oa->value);
310 oa->name = NULL;
311 oa->value = NULL;
312}
313
314static void
315free_byte_array(fido_byte_array_t *ba)
316{
317 free(ba->ptr);
318
319 ba->ptr = NULL;
320 ba->len = 0;
321}
322
323void
324fido_cbor_info_free(fido_cbor_info_t **ci_p)
325{
326 fido_cbor_info_t *ci;
327
328 if (ci_p == NULL || (ci = *ci_p) == NULL)
329 return;
330
331 free_str_array(&ci->versions);
332 free_str_array(&ci->extensions);
333 free_opt_array(&ci->options);
334 free_byte_array(&ci->protocols);
335 free(ci);
336
337 *ci_p = NULL;
338}
339
340char **
341fido_cbor_info_versions_ptr(const fido_cbor_info_t *ci)
342{
343 return (ci->versions.ptr);
344}
345
346size_t
347fido_cbor_info_versions_len(const fido_cbor_info_t *ci)
348{
349 return (ci->versions.len);
350}
351
352char **
353fido_cbor_info_extensions_ptr(const fido_cbor_info_t *ci)
354{
355 return (ci->extensions.ptr);
356}
357
358size_t
359fido_cbor_info_extensions_len(const fido_cbor_info_t *ci)
360{
361 return (ci->extensions.len);
362}
363
364const unsigned char *
365fido_cbor_info_aaguid_ptr(const fido_cbor_info_t *ci)
366{
367 return (ci->aaguid);
368}
369
370size_t
371fido_cbor_info_aaguid_len(const fido_cbor_info_t *ci)
372{
373 return (sizeof(ci->aaguid));
374}
375
376char **
377fido_cbor_info_options_name_ptr(const fido_cbor_info_t *ci)
378{
379 return (ci->options.name);
380}
381
382const bool *
383fido_cbor_info_options_value_ptr(const fido_cbor_info_t *ci)
384{
385 return (ci->options.value);
386}
387
388size_t
389fido_cbor_info_options_len(const fido_cbor_info_t *ci)
390{
391 return (ci->options.len);
392}
393
394uint64_t
395fido_cbor_info_maxmsgsiz(const fido_cbor_info_t *ci)
396{
397 return (ci->maxmsgsiz);
398}
399
400const uint8_t *
401fido_cbor_info_protocols_ptr(const fido_cbor_info_t *ci)
402{
403 return (ci->protocols.ptr);
404}
405
406size_t
407fido_cbor_info_protocols_len(const fido_cbor_info_t *ci)
408{
409 return (ci->protocols.len);
410}
diff --git a/src/io.c b/src/io.c
new file mode 100644
index 0000000..aa88720
--- /dev/null
+++ b/src/io.c
@@ -0,0 +1,256 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <stdint.h>
8#include <stdio.h>
9#include <string.h>
10
11#include "fido.h"
12#include "packed.h"
13
14PACKED_TYPE(frame_t,
15struct frame {
16 uint32_t cid; /* channel id */
17 union {
18 uint8_t type;
19 struct {
20 uint8_t cmd;
21 uint8_t bcnth;
22 uint8_t bcntl;
23 uint8_t data[CTAP_RPT_SIZE - 7];
24 } init;
25 struct {
26 uint8_t seq;
27 uint8_t data[CTAP_RPT_SIZE - 5];
28 } cont;
29 } body;
30})
31
32#ifndef MIN
33#define MIN(x, y) ((x) > (y) ? (y) : (x))
34#endif
35
36static size_t
37tx_preamble(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count)
38{
39 struct frame *fp;
40 unsigned char pkt[sizeof(*fp) + 1];
41 int n;
42
43 if (d->io.write == NULL || (cmd & 0x80) == 0)
44 return (0);
45
46 memset(&pkt, 0, sizeof(pkt));
47 fp = (struct frame *)(pkt + 1);
48 fp->cid = d->cid;
49 fp->body.init.cmd = 0x80 | cmd;
50 fp->body.init.bcnth = (count >> 8) & 0xff;
51 fp->body.init.bcntl = count & 0xff;
52 count = MIN(count, sizeof(fp->body.init.data));
53 if (count)
54 memcpy(&fp->body.init.data, buf, count);
55
56 n = d->io.write(d->io_handle, pkt, sizeof(pkt));
57 if (n < 0 || (size_t)n != sizeof(pkt))
58 return (0);
59
60 return (count);
61}
62
63static size_t
64tx_frame(fido_dev_t *d, int seq, const void *buf, size_t count)
65{
66 struct frame *fp;
67 unsigned char pkt[sizeof(*fp) + 1];
68 int n;
69
70 if (d->io.write == NULL || seq < 0 || seq > UINT8_MAX)
71 return (0);
72
73 memset(&pkt, 0, sizeof(pkt));
74 fp = (struct frame *)(pkt + 1);
75 fp->cid = d->cid;
76 fp->body.cont.seq = (uint8_t)seq;
77 count = MIN(count, sizeof(fp->body.cont.data));
78 memcpy(&fp->body.cont.data, buf, count);
79
80 n = d->io.write(d->io_handle, pkt, sizeof(pkt));
81 if (n < 0 || (size_t)n != sizeof(pkt))
82 return (0);
83
84 return (count);
85}
86
87int
88fido_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count)
89{
90 int seq = 0;
91 size_t sent;
92
93 fido_log_debug("%s: d=%p, cmd=0x%02x, buf=%p, count=%zu", __func__,
94 (void *)d, cmd, buf, count);
95 fido_log_xxd(buf, count);
96
97 if (d->io_handle == NULL || count > UINT16_MAX) {
98 fido_log_debug("%s: invalid argument (%p, %zu)", __func__,
99 d->io_handle, count);
100 return (-1);
101 }
102
103 if ((sent = tx_preamble(d, cmd, buf, count)) == 0) {
104 fido_log_debug("%s: tx_preamble", __func__);
105 return (-1);
106 }
107
108 while (sent < count) {
109 if (seq & 0x80) {
110 fido_log_debug("%s: seq & 0x80", __func__);
111 return (-1);
112 }
113 const uint8_t *p = (const uint8_t *)buf + sent;
114 size_t n = tx_frame(d, seq++, p, count - sent);
115 if (n == 0) {
116 fido_log_debug("%s: tx_frame", __func__);
117 return (-1);
118 }
119 sent += n;
120 }
121
122 return (0);
123}
124
125static int
126rx_frame(fido_dev_t *d, struct frame *fp, int ms)
127{
128 int n;
129
130 if (d->io.read == NULL)
131 return (-1);
132
133 n = d->io.read(d->io_handle, (unsigned char *)fp, sizeof(*fp), ms);
134 if (n < 0 || (size_t)n != sizeof(*fp))
135 return (-1);
136
137 return (0);
138}
139
140static int
141rx_preamble(fido_dev_t *d, struct frame *fp, int ms)
142{
143 do {
144 if (rx_frame(d, fp, ms) < 0)
145 return (-1);
146#ifdef FIDO_FUZZ
147 fp->cid = d->cid;
148#endif
149 } while (fp->cid == d->cid &&
150 fp->body.init.cmd == (CTAP_FRAME_INIT | CTAP_KEEPALIVE));
151
152 return (0);
153}
154
155int
156fido_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int ms)
157{
158 struct frame f;
159 uint16_t r;
160 uint16_t flen;
161 int seq;
162
163 if (d->io_handle == NULL || (cmd & 0x80) == 0) {
164 fido_log_debug("%s: invalid argument (%p, 0x%02x)", __func__,
165 d->io_handle, cmd);
166 return (-1);
167 }
168
169 if (rx_preamble(d, &f, ms) < 0) {
170 fido_log_debug("%s: rx_preamble", __func__);
171 return (-1);
172 }
173
174 fido_log_debug("%s: initiation frame at %p, len %zu", __func__,
175 (void *)&f, sizeof(f));
176 fido_log_xxd(&f, sizeof(f));
177
178#ifdef FIDO_FUZZ
179 f.cid = d->cid;
180 f.body.init.cmd = cmd;
181#endif
182
183 if (f.cid != d->cid || f.body.init.cmd != cmd) {
184 fido_log_debug("%s: cid (0x%x, 0x%x), cmd (0x%02x, 0x%02x)",
185 __func__, f.cid, d->cid, f.body.init.cmd, cmd);
186 return (-1);
187 }
188
189 flen = (f.body.init.bcnth << 8) | f.body.init.bcntl;
190 if (count < (size_t)flen) {
191 fido_log_debug("%s: count < flen (%zu, %zu)", __func__, count,
192 (size_t)flen);
193 return (-1);
194 }
195 if (flen < sizeof(f.body.init.data)) {
196 memcpy(buf, f.body.init.data, flen);
197 return (flen);
198 }
199
200 memcpy(buf, f.body.init.data, sizeof(f.body.init.data));
201 r = sizeof(f.body.init.data);
202 seq = 0;
203
204 while ((size_t)r < flen) {
205 if (rx_frame(d, &f, ms) < 0) {
206 fido_log_debug("%s: rx_frame", __func__);
207 return (-1);
208 }
209
210 fido_log_debug("%s: continuation frame at %p, len %zu",
211 __func__, (void *)&f, sizeof(f));
212 fido_log_xxd(&f, sizeof(f));
213
214#ifdef FIDO_FUZZ
215 f.cid = d->cid;
216 f.body.cont.seq = seq;
217#endif
218
219 if (f.cid != d->cid || f.body.cont.seq != seq++) {
220 fido_log_debug("%s: cid (0x%x, 0x%x), seq (%d, %d)",
221 __func__, f.cid, d->cid, f.body.cont.seq, seq);
222 return (-1);
223 }
224
225 uint8_t *p = (uint8_t *)buf + r;
226
227 if ((size_t)(flen - r) > sizeof(f.body.cont.data)) {
228 memcpy(p, f.body.cont.data, sizeof(f.body.cont.data));
229 r += sizeof(f.body.cont.data);
230 } else {
231 memcpy(p, f.body.cont.data, flen - r);
232 r += (flen - r); /* break */
233 }
234 }
235
236 fido_log_debug("%s: payload at %p, len %zu", __func__, buf, (size_t)r);
237 fido_log_xxd(buf, r);
238
239 return (r);
240}
241
242int
243fido_rx_cbor_status(fido_dev_t *d, int ms)
244{
245 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
246 unsigned char reply[2048];
247 int reply_len;
248
249 if ((reply_len = fido_rx(d, cmd, &reply, sizeof(reply), ms)) < 0 ||
250 (size_t)reply_len < 1) {
251 fido_log_debug("%s: fido_rx", __func__);
252 return (FIDO_ERR_RX);
253 }
254
255 return (reply[0]);
256}
diff --git a/src/iso7816.c b/src/iso7816.c
new file mode 100644
index 0000000..e2ea281
--- /dev/null
+++ b/src/iso7816.c
@@ -0,0 +1,70 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <string.h>
8#include "fido.h"
9
10iso7816_apdu_t *
11iso7816_new(uint8_t ins, uint8_t p1, uint16_t payload_len)
12{
13 iso7816_apdu_t *apdu;
14 size_t alloc_len;
15
16 alloc_len = sizeof(iso7816_apdu_t) + payload_len;
17
18 if ((apdu = calloc(1, alloc_len)) == NULL)
19 return (NULL);
20
21 apdu->alloc_len = alloc_len;
22 apdu->payload_len = payload_len;
23 apdu->payload_ptr = apdu->payload;
24 apdu->header.ins = ins;
25 apdu->header.p1 = p1;
26 apdu->header.lc2 = (payload_len >> 8) & 0xff;
27 apdu->header.lc3 = payload_len & 0xff;
28
29 return (apdu);
30}
31
32void
33iso7816_free(iso7816_apdu_t **apdu_p)
34{
35 iso7816_apdu_t *apdu;
36
37 if (apdu_p == NULL || (apdu = *apdu_p) == NULL)
38 return;
39
40 explicit_bzero(apdu, apdu->alloc_len);
41 free(apdu);
42
43 *apdu_p = NULL;
44}
45
46int
47iso7816_add(iso7816_apdu_t *apdu, const void *buf, size_t cnt)
48{
49 if (cnt > apdu->payload_len || cnt > UINT16_MAX)
50 return (-1);
51
52 memcpy(apdu->payload_ptr, buf, cnt);
53 apdu->payload_ptr += cnt;
54 apdu->payload_len -= (uint16_t)cnt;
55
56 return (0);
57}
58
59const unsigned char *
60iso7816_ptr(const iso7816_apdu_t *apdu)
61{
62 return ((const unsigned char *)&apdu->header);
63}
64
65size_t
66iso7816_len(const iso7816_apdu_t *apdu)
67{
68 return (apdu->alloc_len - sizeof(apdu->alloc_len) -
69 sizeof(apdu->payload_len) - sizeof(apdu->payload_ptr));
70}
diff --git a/src/iso7816.h b/src/iso7816.h
new file mode 100644
index 0000000..426cd97
--- /dev/null
+++ b/src/iso7816.h
@@ -0,0 +1,38 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#ifndef _ISO7816_H
8#define _ISO7816_H
9
10#include "packed.h"
11
12PACKED_TYPE(iso7816_header_t,
13struct iso7816_header {
14 uint8_t cla;
15 uint8_t ins;
16 uint8_t p1;
17 uint8_t p2;
18 uint8_t lc1;
19 uint8_t lc2;
20 uint8_t lc3;
21})
22
23PACKED_TYPE(iso7816_apdu_t,
24struct iso7816_apdu {
25 size_t alloc_len;
26 uint16_t payload_len;
27 uint8_t *payload_ptr;
28 iso7816_header_t header;
29 uint8_t payload[];
30})
31
32const unsigned char *iso7816_ptr(const iso7816_apdu_t *);
33int iso7816_add(iso7816_apdu_t *, const void *, size_t);
34iso7816_apdu_t *iso7816_new(uint8_t, uint8_t, uint16_t);
35size_t iso7816_len(const iso7816_apdu_t *);
36void iso7816_free(iso7816_apdu_t **);
37
38#endif /* !_ISO7816_H */
diff --git a/src/libfido2.pc.in b/src/libfido2.pc.in
new file mode 100644
index 0000000..03d0606
--- /dev/null
+++ b/src/libfido2.pc.in
@@ -0,0 +1,12 @@
1prefix=@CMAKE_INSTALL_PREFIX@
2exec_prefix=${prefix}
3libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
4includedir=${prefix}/include
5
6Name: @PROJECT_NAME@
7Description: A FIDO2 library
8URL: https://github.com/yubico/libfido2
9Version: @FIDO_VERSION@
10Requires: libcrypto
11Libs: -L${libdir} -lfido2
12Cflags: -I${includedir}
diff --git a/src/log.c b/src/log.c
new file mode 100644
index 0000000..982bdb7
--- /dev/null
+++ b/src/log.c
@@ -0,0 +1,63 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <stdarg.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include "fido.h"
11
12#ifndef FIDO_NO_DIAGNOSTIC
13
14#ifndef TLS
15#define TLS
16#endif
17
18static TLS int logging;
19
20void
21fido_log_init(void)
22{
23 logging = 1;
24}
25
26void
27fido_log_xxd(const void *buf, size_t count)
28{
29 const uint8_t *ptr = buf;
30 size_t i;
31
32 if (!logging)
33 return;
34
35 fprintf(stderr, " ");
36
37 for (i = 0; i < count; i++) {
38 fprintf(stderr, "%02x ", *ptr++);
39 if ((i + 1) % 16 == 0 && i + 1 < count)
40 fprintf(stderr, "\n ");
41 }
42
43 fprintf(stderr, "\n");
44 fflush(stderr);
45}
46
47void
48fido_log_debug(const char *fmt, ...)
49{
50 va_list ap;
51
52 if (!logging)
53 return;
54
55 va_start(ap, fmt);
56 vfprintf(stderr, fmt, ap);
57 va_end(ap);
58
59 fprintf(stderr, "\n");
60 fflush(stderr);
61}
62
63#endif /* !FIDO_NO_DIAGNOSTIC */
diff --git a/src/packed.h b/src/packed.h
new file mode 100644
index 0000000..3857c22
--- /dev/null
+++ b/src/packed.h
@@ -0,0 +1,22 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#ifndef _PACKED_H
8#define _PACKED_H
9
10#if defined(__GNUC__)
11#define PACKED_TYPE(type, def) \
12 typedef def __attribute__ ((__packed__)) type;
13#elif defined(_MSC_VER)
14#define PACKED_TYPE(type, def) \
15 __pragma(pack(push, 1)) \
16 typedef def type; \
17 __pragma(pack(pop))
18#else
19#error "please provide a way to define packed types on your platform"
20#endif
21
22#endif /* !_PACKED_H */
diff --git a/src/pin.c b/src/pin.c
new file mode 100644
index 0000000..1ed555c
--- /dev/null
+++ b/src/pin.c
@@ -0,0 +1,428 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <string.h>
8#include "fido.h"
9#include "fido/es256.h"
10
11static int
12parse_pintoken(const cbor_item_t *key, const cbor_item_t *val, void *arg)
13{
14 fido_blob_t *token = arg;
15
16 if (cbor_isa_uint(key) == false ||
17 cbor_int_get_width(key) != CBOR_INT_8 ||
18 cbor_get_uint8(key) != 2) {
19 fido_log_debug("%s: cbor type", __func__);
20 return (0); /* ignore */
21 }
22
23 return (fido_blob_decode(val, token));
24}
25
26static int
27fido_dev_get_pin_token_tx(fido_dev_t *dev, const char *pin,
28 const fido_blob_t *ecdh, const es256_pk_t *pk)
29{
30 fido_blob_t f;
31 fido_blob_t *p = NULL;
32 cbor_item_t *argv[6];
33 int r;
34
35 memset(&f, 0, sizeof(f));
36 memset(argv, 0, sizeof(argv));
37
38 if ((p = fido_blob_new()) == NULL || fido_blob_set(p,
39 (const unsigned char *)pin, strlen(pin)) < 0) {
40 fido_log_debug("%s: fido_blob_set", __func__);
41 r = FIDO_ERR_INVALID_ARGUMENT;
42 goto fail;
43 }
44
45 if ((argv[0] = cbor_build_uint8(1)) == NULL ||
46 (argv[1] = cbor_build_uint8(5)) == NULL ||
47 (argv[2] = es256_pk_encode(pk, 0)) == NULL ||
48 (argv[5] = cbor_encode_pin_hash_enc(ecdh, p)) == NULL) {
49 fido_log_debug("%s: cbor encode", __func__);
50 r = FIDO_ERR_INTERNAL;
51 goto fail;
52 }
53
54 if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, 6, &f) < 0 ||
55 fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
56 fido_log_debug("%s: fido_tx", __func__);
57 r = FIDO_ERR_TX;
58 goto fail;
59 }
60
61 r = FIDO_OK;
62fail:
63 cbor_vector_free(argv, nitems(argv));
64 fido_blob_free(&p);
65 free(f.ptr);
66
67 return (r);
68}
69
70static int
71fido_dev_get_pin_token_rx(fido_dev_t *dev, const fido_blob_t *ecdh,
72 fido_blob_t *token, int ms)
73{
74 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
75 fido_blob_t *aes_token = NULL;
76 unsigned char reply[2048];
77 int reply_len;
78 int r;
79
80 if ((aes_token = fido_blob_new()) == NULL) {
81 r = FIDO_ERR_INTERNAL;
82 goto fail;
83 }
84
85 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
86 fido_log_debug("%s: fido_rx", __func__);
87 r = FIDO_ERR_RX;
88 goto fail;
89 }
90
91 if ((r = cbor_parse_reply(reply, (size_t)reply_len, aes_token,
92 parse_pintoken)) != FIDO_OK) {
93 fido_log_debug("%s: parse_pintoken", __func__);
94 goto fail;
95 }
96
97 if (aes256_cbc_dec(ecdh, aes_token, token) < 0) {
98 fido_log_debug("%s: aes256_cbc_dec", __func__);
99 r = FIDO_ERR_RX;
100 goto fail;
101 }
102
103 r = FIDO_OK;
104fail:
105 fido_blob_free(&aes_token);
106
107 return (r);
108}
109
110static int
111fido_dev_get_pin_token_wait(fido_dev_t *dev, const char *pin,
112 const fido_blob_t *ecdh, const es256_pk_t *pk, fido_blob_t *token, int ms)
113{
114 int r;
115
116 if ((r = fido_dev_get_pin_token_tx(dev, pin, ecdh, pk)) != FIDO_OK ||
117 (r = fido_dev_get_pin_token_rx(dev, ecdh, token, ms)) != FIDO_OK)
118 return (r);
119
120 return (FIDO_OK);
121}
122
123int
124fido_dev_get_pin_token(fido_dev_t *dev, const char *pin,
125 const fido_blob_t *ecdh, const es256_pk_t *pk, fido_blob_t *token)
126{
127 return (fido_dev_get_pin_token_wait(dev, pin, ecdh, pk, token, -1));
128}
129
130static int
131pad64(const char *pin, fido_blob_t **ppin)
132{
133 size_t pin_len;
134 size_t ppin_len;
135
136 pin_len = strlen(pin);
137 if (pin_len < 4 || pin_len > 255) {
138 fido_log_debug("%s: invalid pin length", __func__);
139 return (FIDO_ERR_PIN_POLICY_VIOLATION);
140 }
141
142 if ((*ppin = fido_blob_new()) == NULL)
143 return (FIDO_ERR_INTERNAL);
144
145 ppin_len = (pin_len + 63) & ~63;
146 if (ppin_len < pin_len || ((*ppin)->ptr = calloc(1, ppin_len)) == NULL) {
147 fido_blob_free(ppin);
148 return (FIDO_ERR_INTERNAL);
149 }
150
151 memcpy((*ppin)->ptr, pin, pin_len);
152 (*ppin)->len = ppin_len;
153
154 return (FIDO_OK);
155}
156
157static int
158fido_dev_change_pin_tx(fido_dev_t *dev, const char *pin, const char *oldpin)
159{
160 fido_blob_t f;
161 fido_blob_t *ppin = NULL;
162 fido_blob_t *ecdh = NULL;
163 fido_blob_t *opin = NULL;
164 cbor_item_t *argv[6];
165 es256_pk_t *pk = NULL;
166 int r;
167
168 memset(&f, 0, sizeof(f));
169 memset(argv, 0, sizeof(argv));
170
171 if ((opin = fido_blob_new()) == NULL || fido_blob_set(opin,
172 (const unsigned char *)oldpin, strlen(oldpin)) < 0) {
173 fido_log_debug("%s: fido_blob_set", __func__);
174 r = FIDO_ERR_INVALID_ARGUMENT;
175 goto fail;
176 }
177
178 if ((r = pad64(pin, &ppin)) != FIDO_OK) {
179 fido_log_debug("%s: pad64", __func__);
180 goto fail;
181 }
182
183 if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
184 fido_log_debug("%s: fido_do_ecdh", __func__);
185 goto fail;
186 }
187
188 if ((argv[0] = cbor_build_uint8(1)) == NULL ||
189 (argv[1] = cbor_build_uint8(4)) == NULL ||
190 (argv[2] = es256_pk_encode(pk, 0)) == NULL ||
191 (argv[3] = cbor_encode_change_pin_auth(ecdh, ppin, opin)) == NULL ||
192 (argv[4] = cbor_encode_pin_enc(ecdh, ppin)) == NULL ||
193 (argv[5] = cbor_encode_pin_hash_enc(ecdh, opin)) == NULL) {
194 fido_log_debug("%s: cbor encode", __func__);
195 r = FIDO_ERR_INTERNAL;
196 goto fail;
197 }
198
199 if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, 6, &f) < 0 ||
200 fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
201 fido_log_debug("%s: fido_tx", __func__);
202 r = FIDO_ERR_TX;
203 goto fail;
204 }
205
206 r = FIDO_OK;
207fail:
208 cbor_vector_free(argv, nitems(argv));
209 es256_pk_free(&pk);
210 fido_blob_free(&ppin);
211 fido_blob_free(&ecdh);
212 fido_blob_free(&opin);
213 free(f.ptr);
214
215 return (r);
216
217}
218
219static int
220fido_dev_set_pin_tx(fido_dev_t *dev, const char *pin)
221{
222 fido_blob_t f;
223 fido_blob_t *ppin = NULL;
224 fido_blob_t *ecdh = NULL;
225 cbor_item_t *argv[5];
226 es256_pk_t *pk = NULL;
227 int r;
228
229 memset(&f, 0, sizeof(f));
230 memset(argv, 0, sizeof(argv));
231
232 if ((r = pad64(pin, &ppin)) != FIDO_OK) {
233 fido_log_debug("%s: pad64", __func__);
234 goto fail;
235 }
236
237 if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
238 fido_log_debug("%s: fido_do_ecdh", __func__);
239 goto fail;
240 }
241
242 if ((argv[0] = cbor_build_uint8(1)) == NULL ||
243 (argv[1] = cbor_build_uint8(3)) == NULL ||
244 (argv[2] = es256_pk_encode(pk, 0)) == NULL ||
245 (argv[3] = cbor_encode_set_pin_auth(ecdh, ppin)) == NULL ||
246 (argv[4] = cbor_encode_pin_enc(ecdh, ppin)) == NULL) {
247 fido_log_debug("%s: cbor encode", __func__);
248 r = FIDO_ERR_INTERNAL;
249 goto fail;
250 }
251
252 if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, 5, &f) < 0 ||
253 fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
254 fido_log_debug("%s: fido_tx", __func__);
255 r = FIDO_ERR_TX;
256 goto fail;
257 }
258
259 r = FIDO_OK;
260fail:
261 cbor_vector_free(argv, nitems(argv));
262 es256_pk_free(&pk);
263 fido_blob_free(&ppin);
264 fido_blob_free(&ecdh);
265 free(f.ptr);
266
267 return (r);
268}
269
270static int
271fido_dev_set_pin_wait(fido_dev_t *dev, const char *pin, const char *oldpin,
272 int ms)
273{
274 int r;
275
276 if (oldpin != NULL) {
277 if ((r = fido_dev_change_pin_tx(dev, pin, oldpin)) != FIDO_OK) {
278 fido_log_debug("%s: fido_dev_change_pin_tx", __func__);
279 return (r);
280 }
281 } else {
282 if ((r = fido_dev_set_pin_tx(dev, pin)) != FIDO_OK) {
283 fido_log_debug("%s: fido_dev_set_pin_tx", __func__);
284 return (r);
285 }
286 }
287
288 if ((r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
289 fido_log_debug("%s: fido_rx_cbor_status", __func__);
290 return (r);
291 }
292
293 return (FIDO_OK);
294}
295
296int
297fido_dev_set_pin(fido_dev_t *dev, const char *pin, const char *oldpin)
298{
299 return (fido_dev_set_pin_wait(dev, pin, oldpin, -1));
300}
301
302static int
303parse_retry_count(const cbor_item_t *key, const cbor_item_t *val, void *arg)
304{
305 int *retries = arg;
306 uint64_t n;
307
308 if (cbor_isa_uint(key) == false ||
309 cbor_int_get_width(key) != CBOR_INT_8 ||
310 cbor_get_uint8(key) != 3) {
311 fido_log_debug("%s: cbor type", __func__);
312 return (0); /* ignore */
313 }
314
315 if (cbor_decode_uint64(val, &n) < 0 || n > INT_MAX) {
316 fido_log_debug("%s: cbor_decode_uint64", __func__);
317 return (-1);
318 }
319
320 *retries = (int)n;
321
322 return (0);
323}
324
325static int
326fido_dev_get_retry_count_tx(fido_dev_t *dev)
327{
328 fido_blob_t f;
329 cbor_item_t *argv[2];
330 int r;
331
332 memset(&f, 0, sizeof(f));
333 memset(argv, 0, sizeof(argv));
334
335 if ((argv[0] = cbor_build_uint8(1)) == NULL ||
336 (argv[1] = cbor_build_uint8(1)) == NULL) {
337 r = FIDO_ERR_INTERNAL;
338 goto fail;
339 }
340
341 if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, 2, &f) < 0 ||
342 fido_tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
343 fido_log_debug("%s: fido_tx", __func__);
344 r = FIDO_ERR_TX;
345 goto fail;
346 }
347
348 r = FIDO_OK;
349fail:
350 cbor_vector_free(argv, nitems(argv));
351 free(f.ptr);
352
353 return (r);
354}
355
356static int
357fido_dev_get_retry_count_rx(fido_dev_t *dev, int *retries, int ms)
358{
359 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
360 unsigned char reply[512];
361 int reply_len;
362 int r;
363
364 *retries = 0;
365
366 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
367 fido_log_debug("%s: fido_rx", __func__);
368 return (FIDO_ERR_RX);
369 }
370
371 if ((r = cbor_parse_reply(reply, (size_t)reply_len, retries,
372 parse_retry_count)) != FIDO_OK) {
373 fido_log_debug("%s: parse_retry_count", __func__);
374 return (r);
375 }
376
377 return (FIDO_OK);
378}
379
380static int
381fido_dev_get_retry_count_wait(fido_dev_t *dev, int *retries, int ms)
382{
383 int r;
384
385 if ((r = fido_dev_get_retry_count_tx(dev)) != FIDO_OK ||
386 (r = fido_dev_get_retry_count_rx(dev, retries, ms)) != FIDO_OK)
387 return (r);
388
389 return (FIDO_OK);
390}
391
392int
393fido_dev_get_retry_count(fido_dev_t *dev, int *retries)
394{
395 return (fido_dev_get_retry_count_wait(dev, retries, -1));
396}
397
398int
399cbor_add_pin_params(fido_dev_t *dev, const fido_blob_t *hmac_data,
400 const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin,
401 cbor_item_t **auth, cbor_item_t **opt)
402{
403 fido_blob_t *token = NULL;
404 int r;
405
406 if ((token = fido_blob_new()) == NULL) {
407 r = FIDO_ERR_INTERNAL;
408 goto fail;
409 }
410
411 if ((r = fido_dev_get_pin_token(dev, pin, ecdh, pk, token)) != FIDO_OK) {
412 fido_log_debug("%s: fido_dev_get_pin_token", __func__);
413 goto fail;
414 }
415
416 if ((*auth = cbor_encode_pin_auth(token, hmac_data)) == NULL ||
417 (*opt = cbor_encode_pin_opt()) == NULL) {
418 fido_log_debug("%s: cbor encode", __func__);
419 r = FIDO_ERR_INTERNAL;
420 goto fail;
421 }
422
423 r = FIDO_OK;
424fail:
425 fido_blob_free(&token);
426
427 return (r);
428}
diff --git a/src/reset.c b/src/reset.c
new file mode 100644
index 0000000..4b2c88a
--- /dev/null
+++ b/src/reset.c
@@ -0,0 +1,40 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <stdlib.h>
8#include "fido.h"
9
10static int
11fido_dev_reset_tx(fido_dev_t *dev)
12{
13 const unsigned char cbor[] = { CTAP_CBOR_RESET };
14 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
15
16 if (fido_tx(dev, cmd, cbor, sizeof(cbor)) < 0) {
17 fido_log_debug("%s: fido_tx", __func__);
18 return (FIDO_ERR_TX);
19 }
20
21 return (FIDO_OK);
22}
23
24static int
25fido_dev_reset_wait(fido_dev_t *dev, int ms)
26{
27 int r;
28
29 if ((r = fido_dev_reset_tx(dev)) != FIDO_OK ||
30 (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK)
31 return (r);
32
33 return (FIDO_OK);
34}
35
36int
37fido_dev_reset(fido_dev_t *dev)
38{
39 return (fido_dev_reset_wait(dev, -1));
40}
diff --git a/src/rs256.c b/src/rs256.c
new file mode 100644
index 0000000..9f30163
--- /dev/null
+++ b/src/rs256.c
@@ -0,0 +1,204 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <openssl/bn.h>
8#include <openssl/rsa.h>
9#include <openssl/evp.h>
10#include <openssl/obj_mac.h>
11
12#include <string.h>
13#include "fido.h"
14#include "fido/rs256.h"
15
16#if OPENSSL_VERSION_NUMBER < 0x10100000L
17static int
18RSA_bits(const RSA *r)
19{
20 return (BN_num_bits(r->n));
21}
22
23static int
24RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d)
25{
26 r->n = n;
27 r->e = e;
28 r->d = d;
29
30 return (1);
31}
32
33static void
34RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
35{
36 *n = r->n;
37 *e = r->e;
38 *d = r->d;
39}
40#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
41
42static int
43decode_bignum(const cbor_item_t *item, void *ptr, size_t len)
44{
45 if (cbor_isa_bytestring(item) == false ||
46 cbor_bytestring_is_definite(item) == false ||
47 cbor_bytestring_length(item) != len) {
48 fido_log_debug("%s: cbor type", __func__);
49 return (-1);
50 }
51
52 memcpy(ptr, cbor_bytestring_handle(item), len);
53
54 return (0);
55}
56
57static int
58decode_rsa_pubkey(const cbor_item_t *key, const cbor_item_t *val, void *arg)
59{
60 rs256_pk_t *k = arg;
61
62 if (cbor_isa_negint(key) == false ||
63 cbor_int_get_width(key) != CBOR_INT_8)
64 return (0); /* ignore */
65
66 switch (cbor_get_uint8(key)) {
67 case 0: /* modulus */
68 return (decode_bignum(val, &k->n, sizeof(k->n)));
69 case 1: /* public exponent */
70 return (decode_bignum(val, &k->e, sizeof(k->e)));
71 }
72
73 return (0); /* ignore */
74}
75
76int
77rs256_pk_decode(const cbor_item_t *item, rs256_pk_t *k)
78{
79 if (cbor_isa_map(item) == false ||
80 cbor_map_is_definite(item) == false ||
81 cbor_map_iter(item, k, decode_rsa_pubkey) < 0) {
82 fido_log_debug("%s: cbor type", __func__);
83 return (-1);
84 }
85
86 return (0);
87}
88
89rs256_pk_t *
90rs256_pk_new(void)
91{
92 return (calloc(1, sizeof(rs256_pk_t)));
93}
94
95void
96rs256_pk_free(rs256_pk_t **pkp)
97{
98 rs256_pk_t *pk;
99
100 if (pkp == NULL || (pk = *pkp) == NULL)
101 return;
102
103 explicit_bzero(pk, sizeof(*pk));
104 free(pk);
105
106 *pkp = NULL;
107}
108
109int
110rs256_pk_from_ptr(rs256_pk_t *pk, const void *ptr, size_t len)
111{
112 if (len < sizeof(*pk))
113 return (FIDO_ERR_INVALID_ARGUMENT);
114
115 memcpy(pk, ptr, sizeof(*pk));
116
117 return (FIDO_OK);
118}
119
120EVP_PKEY *
121rs256_pk_to_EVP_PKEY(const rs256_pk_t *k)
122{
123 RSA *rsa = NULL;
124 EVP_PKEY *pkey = NULL;
125 BIGNUM *n = NULL;
126 BIGNUM *e = NULL;
127 int ok = -1;
128
129 if ((n = BN_new()) == NULL || (e = BN_new()) == NULL)
130 goto fail;
131
132 if (BN_bin2bn(k->n, sizeof(k->n), n) == NULL ||
133 BN_bin2bn(k->e, sizeof(k->e), e) == NULL) {
134 fido_log_debug("%s: BN_bin2bn", __func__);
135 goto fail;
136 }
137
138 if ((rsa = RSA_new()) == NULL || RSA_set0_key(rsa, n, e, NULL) == 0) {
139 fido_log_debug("%s: RSA_set0_key", __func__);
140 goto fail;
141 }
142
143 /* at this point, n and e belong to rsa */
144 n = NULL;
145 e = NULL;
146
147 if ((pkey = EVP_PKEY_new()) == NULL ||
148 EVP_PKEY_assign_RSA(pkey, rsa) == 0) {
149 fido_log_debug("%s: EVP_PKEY_assign_RSA", __func__);
150 goto fail;
151 }
152
153 rsa = NULL; /* at this point, rsa belongs to evp */
154
155 ok = 0;
156fail:
157 if (n != NULL)
158 BN_free(n);
159 if (e != NULL)
160 BN_free(e);
161 if (rsa != NULL)
162 RSA_free(rsa);
163 if (ok < 0 && pkey != NULL) {
164 EVP_PKEY_free(pkey);
165 pkey = NULL;
166 }
167
168 return (pkey);
169}
170
171int
172rs256_pk_from_RSA(rs256_pk_t *pk, const RSA *rsa)
173{
174 const BIGNUM *n = NULL;
175 const BIGNUM *e = NULL;
176 const BIGNUM *d = NULL;
177 int k;
178
179 if (RSA_bits(rsa) != 2048) {
180 fido_log_debug("%s: invalid key length", __func__);
181 return (FIDO_ERR_INVALID_ARGUMENT);
182 }
183
184 RSA_get0_key(rsa, &n, &e, &d);
185
186 if (n == NULL || e == NULL) {
187 fido_log_debug("%s: RSA_get0_key", __func__);
188 return (FIDO_ERR_INTERNAL);
189 }
190
191 if ((k = BN_num_bytes(n)) < 0 || (size_t)k > sizeof(pk->n) ||
192 (k = BN_num_bytes(e)) < 0 || (size_t)k > sizeof(pk->e)) {
193 fido_log_debug("%s: invalid key", __func__);
194 return (FIDO_ERR_INTERNAL);
195 }
196
197 if ((k = BN_bn2bin(n, pk->n)) < 0 || (size_t)k > sizeof(pk->n) ||
198 (k = BN_bn2bin(e, pk->e)) < 0 || (size_t)k > sizeof(pk->e)) {
199 fido_log_debug("%s: BN_bn2bin", __func__);
200 return (FIDO_ERR_INTERNAL);
201 }
202
203 return (FIDO_OK);
204}
diff --git a/src/types.h b/src/types.h
new file mode 100644
index 0000000..42ed1b7
--- /dev/null
+++ b/src/types.h
@@ -0,0 +1,171 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#ifndef _TYPES_H
8#define _TYPES_H
9
10#include "packed.h"
11
12/* COSE ES256 (ECDSA over P-256 with SHA-256) public key */
13typedef struct es256_pk {
14 unsigned char x[32];
15 unsigned char y[32];
16} es256_pk_t;
17
18/* COSE ES256 (ECDSA over P-256 with SHA-256) (secret) key */
19typedef struct es256_sk {
20 unsigned char d[32];
21} es256_sk_t;
22
23/* COSE RS256 (2048-bit RSA with PKCS1 padding and SHA-256) public key */
24typedef struct rs256_pk {
25 unsigned char n[256];
26 unsigned char e[3];
27} rs256_pk_t;
28
29/* COSE EDDSA (ED25519) */
30typedef struct eddsa_pk {
31 unsigned char x[32];
32} eddsa_pk_t;
33
34PACKED_TYPE(fido_authdata_t,
35struct fido_authdata {
36 unsigned char rp_id_hash[32]; /* sha256 of fido_rp.id */
37 uint8_t flags; /* user present/verified */
38 uint32_t sigcount; /* signature counter */
39 /* actually longer */
40})
41
42PACKED_TYPE(fido_attcred_raw_t,
43struct fido_attcred_raw {
44 unsigned char aaguid[16]; /* credential's aaguid */
45 uint16_t id_len; /* credential id length */
46 uint8_t body[]; /* credential id + pubkey */
47})
48
49typedef struct fido_attcred {
50 unsigned char aaguid[16]; /* credential's aaguid */
51 fido_blob_t id; /* credential id */
52 int type; /* credential's cose algorithm */
53 union { /* credential's public key */
54 es256_pk_t es256;
55 rs256_pk_t rs256;
56 eddsa_pk_t eddsa;
57 } pubkey;
58} fido_attcred_t;
59
60typedef struct fido_attstmt {
61 fido_blob_t x5c; /* attestation certificate */
62 fido_blob_t sig; /* attestation signature */
63} fido_attstmt_t;
64
65typedef struct fido_rp {
66 char *id; /* relying party id */
67 char *name; /* relying party name */
68} fido_rp_t;
69
70typedef struct fido_user {
71 fido_blob_t id; /* required */
72 char *icon; /* optional */
73 char *name; /* optional */
74 char *display_name; /* required */
75} fido_user_t;
76
77typedef struct fido_cred {
78 fido_blob_t cdh; /* client data hash */
79 fido_rp_t rp; /* relying party */
80 fido_user_t user; /* user entity */
81 fido_blob_array_t excl; /* list of credential ids to exclude */
82 fido_opt_t rk; /* resident key */
83 fido_opt_t uv; /* user verification */
84 int ext; /* enabled extensions */
85 int type; /* cose algorithm */
86 char *fmt; /* credential format */
87 int authdata_ext; /* decoded extensions */
88 fido_blob_t authdata_cbor; /* raw cbor payload */
89 fido_authdata_t authdata; /* decoded authdata payload */
90 fido_attcred_t attcred; /* returned credential (key + id) */
91 fido_attstmt_t attstmt; /* attestation statement (x509 + sig) */
92} fido_cred_t;
93
94typedef struct _fido_assert_stmt {
95 fido_blob_t id; /* credential id */
96 fido_user_t user; /* user attributes */
97 fido_blob_t hmac_secret_enc; /* hmac secret, encrypted */
98 fido_blob_t hmac_secret; /* hmac secret */
99 int authdata_ext; /* decoded extensions */
100 fido_blob_t authdata_cbor; /* raw cbor payload */
101 fido_authdata_t authdata; /* decoded authdata payload */
102 fido_blob_t sig; /* signature of cdh + authdata */
103} fido_assert_stmt;
104
105typedef struct fido_assert {
106 char *rp_id; /* relying party id */
107 fido_blob_t cdh; /* client data hash */
108 fido_blob_t hmac_salt; /* optional hmac-secret salt */
109 fido_blob_array_t allow_list; /* list of allowed credentials */
110 fido_opt_t up; /* user presence */
111 fido_opt_t uv; /* user verification */
112 int ext; /* enabled extensions */
113 fido_assert_stmt *stmt; /* array of expected assertions */
114 size_t stmt_cnt; /* number of allocated assertions */
115 size_t stmt_len; /* number of received assertions */
116} fido_assert_t;
117
118typedef struct fido_opt_array {
119 char **name;
120 bool *value;
121 size_t len;
122} fido_opt_array_t;
123
124typedef struct fido_str_array {
125 char **ptr;
126 size_t len;
127} fido_str_array_t;
128
129typedef struct fido_byte_array {
130 uint8_t *ptr;
131 size_t len;
132} fido_byte_array_t;
133
134typedef struct fido_cbor_info {
135 fido_str_array_t versions; /* supported versions: fido2|u2f */
136 fido_str_array_t extensions; /* list of supported extensions */
137 unsigned char aaguid[16]; /* aaguid */
138 fido_opt_array_t options; /* list of supported options */
139 uint64_t maxmsgsiz; /* maximum message size */
140 fido_byte_array_t protocols; /* supported pin protocols */
141} fido_cbor_info_t;
142
143typedef struct fido_dev_info {
144 char *path; /* device path */
145 int16_t vendor_id; /* 2-byte vendor id */
146 int16_t product_id; /* 2-byte product id */
147 char *manufacturer; /* manufacturer string */
148 char *product; /* product string */
149} fido_dev_info_t;
150
151PACKED_TYPE(fido_ctap_info_t,
152/* defined in section 8.1.9.1.3 (CTAPHID_INIT) of the fido2 ctap spec */
153struct fido_ctap_info {
154 uint64_t nonce; /* echoed nonce */
155 uint32_t cid; /* channel id */
156 uint8_t protocol; /* ctaphid protocol id */
157 uint8_t major; /* major version number */
158 uint8_t minor; /* minor version number */
159 uint8_t build; /* build version number */
160 uint8_t flags; /* capabilities flags; see FIDO_CAP_* */
161})
162
163typedef struct fido_dev {
164 uint64_t nonce; /* issued nonce */
165 fido_ctap_info_t attr; /* device attributes */
166 uint32_t cid; /* assigned channel id */
167 void *io_handle; /* abstract i/o handle */
168 fido_dev_io_t io; /* i/o functions & data */
169} fido_dev_t;
170
171#endif /* !_TYPES_H */
diff --git a/src/u2f.c b/src/u2f.c
new file mode 100644
index 0000000..3f2d9aa
--- /dev/null
+++ b/src/u2f.c
@@ -0,0 +1,758 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <openssl/sha.h>
8#include <openssl/x509.h>
9
10#include <string.h>
11#ifdef HAVE_UNISTD_H
12#include <unistd.h>
13#endif
14
15#include "fido.h"
16#include "fido/es256.h"
17
18#if defined(_MSC_VER)
19static int
20usleep(unsigned int usec)
21{
22 Sleep(usec / 1000);
23
24 return (0);
25}
26#endif
27
28static int
29sig_get(fido_blob_t *sig, const unsigned char **buf, size_t *len)
30{
31 sig->len = *len; /* consume the whole buffer */
32 if ((sig->ptr = calloc(1, sig->len)) == NULL ||
33 fido_buf_read(buf, len, sig->ptr, sig->len) < 0) {
34 fido_log_debug("%s: fido_buf_read", __func__);
35 if (sig->ptr != NULL) {
36 explicit_bzero(sig->ptr, sig->len);
37 free(sig->ptr);
38 sig->ptr = NULL;
39 sig->len = 0;
40 return (-1);
41 }
42 }
43
44 return (0);
45}
46
47static int
48x5c_get(fido_blob_t *x5c, const unsigned char **buf, size_t *len)
49{
50 X509 *cert = NULL;
51 int ok = -1;
52
53 if (*len > LONG_MAX) {
54 fido_log_debug("%s: invalid len %zu", __func__, *len);
55 goto fail;
56 }
57
58 /* find out the certificate's length */
59 const unsigned char *end = *buf;
60 if ((cert = d2i_X509(NULL, &end, (long)*len)) == NULL || end <= *buf ||
61 (x5c->len = (size_t)(end - *buf)) >= *len) {
62 fido_log_debug("%s: d2i_X509", __func__);
63 goto fail;
64 }
65
66 /* read accordingly */
67 if ((x5c->ptr = calloc(1, x5c->len)) == NULL ||
68 fido_buf_read(buf, len, x5c->ptr, x5c->len) < 0) {
69 fido_log_debug("%s: fido_buf_read", __func__);
70 goto fail;
71 }
72
73 ok = 0;
74fail:
75 if (cert != NULL)
76 X509_free(cert);
77
78 if (ok < 0) {
79 free(x5c->ptr);
80 x5c->ptr = NULL;
81 x5c->len = 0;
82 }
83
84 return (ok);
85}
86
87static int
88authdata_fake(const char *rp_id, uint8_t flags, uint32_t sigcount,
89 fido_blob_t *fake_cbor_ad)
90{
91 fido_authdata_t ad;
92 cbor_item_t *item = NULL;
93 size_t alloc_len;
94
95 memset(&ad, 0, sizeof(ad));
96
97 if (SHA256((const void *)rp_id, strlen(rp_id),
98 ad.rp_id_hash) != ad.rp_id_hash) {
99 fido_log_debug("%s: sha256", __func__);
100 return (-1);
101 }
102
103 ad.flags = flags; /* XXX translate? */
104 ad.sigcount = sigcount;
105
106 if ((item = cbor_build_bytestring((const unsigned char *)&ad,
107 sizeof(ad))) == NULL) {
108 fido_log_debug("%s: cbor_build_bytestring", __func__);
109 return (-1);
110 }
111
112 if (fake_cbor_ad->ptr != NULL ||
113 (fake_cbor_ad->len = cbor_serialize_alloc(item, &fake_cbor_ad->ptr,
114 &alloc_len)) == 0) {
115 fido_log_debug("%s: cbor_serialize_alloc", __func__);
116 cbor_decref(&item);
117 return (-1);
118 }
119
120 cbor_decref(&item);
121
122 return (0);
123}
124
125static int
126send_dummy_register(fido_dev_t *dev, int ms)
127{
128 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_MSG;
129 iso7816_apdu_t *apdu = NULL;
130 unsigned char challenge[SHA256_DIGEST_LENGTH];
131 unsigned char application[SHA256_DIGEST_LENGTH];
132 unsigned char reply[2048];
133 int r;
134
135#ifdef FIDO_FUZZ
136 ms = 0; /* XXX */
137#endif
138
139 /* dummy challenge & application */
140 memset(&challenge, 0xff, sizeof(challenge));
141 memset(&application, 0xff, sizeof(application));
142
143 if ((apdu = iso7816_new(U2F_CMD_REGISTER, 0, 2 *
144 SHA256_DIGEST_LENGTH)) == NULL ||
145 iso7816_add(apdu, &challenge, sizeof(challenge)) < 0 ||
146 iso7816_add(apdu, &application, sizeof(application)) < 0) {
147 fido_log_debug("%s: iso7816", __func__);
148 r = FIDO_ERR_INTERNAL;
149 goto fail;
150 }
151
152 do {
153 if (fido_tx(dev, cmd, iso7816_ptr(apdu),
154 iso7816_len(apdu)) < 0) {
155 fido_log_debug("%s: fido_tx", __func__);
156 r = FIDO_ERR_TX;
157 goto fail;
158 }
159 if (fido_rx(dev, cmd, &reply, sizeof(reply), ms) < 2) {
160 fido_log_debug("%s: fido_rx", __func__);
161 r = FIDO_ERR_RX;
162 goto fail;
163 }
164 if (usleep((ms == -1 ? 100 : ms) * 1000) < 0) {
165 fido_log_debug("%s: usleep", __func__);
166 r = FIDO_ERR_RX;
167 goto fail;
168 }
169 } while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED);
170
171 r = FIDO_OK;
172fail:
173 iso7816_free(&apdu);
174
175 return (r);
176}
177
178static int
179key_lookup(fido_dev_t *dev, const char *rp_id, const fido_blob_t *key_id,
180 int *found, int ms)
181{
182 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_MSG;
183 iso7816_apdu_t *apdu = NULL;
184 unsigned char challenge[SHA256_DIGEST_LENGTH];
185 unsigned char rp_id_hash[SHA256_DIGEST_LENGTH];
186 unsigned char reply[8];
187 uint8_t key_id_len;
188 int r;
189
190 if (key_id->len > UINT8_MAX || rp_id == NULL) {
191 fido_log_debug("%s: key_id->len=%zu, rp_id=%p", __func__,
192 key_id->len, (const void *)rp_id);
193 r = FIDO_ERR_INVALID_ARGUMENT;
194 goto fail;
195 }
196
197 memset(&challenge, 0xff, sizeof(challenge));
198 memset(&rp_id_hash, 0, sizeof(rp_id_hash));
199
200 if (SHA256((const void *)rp_id, strlen(rp_id),
201 rp_id_hash) != rp_id_hash) {
202 fido_log_debug("%s: sha256", __func__);
203 r = FIDO_ERR_INTERNAL;
204 goto fail;
205 }
206
207 key_id_len = (uint8_t)key_id->len;
208
209 if ((apdu = iso7816_new(U2F_CMD_AUTH, U2F_AUTH_CHECK, 2 *
210 SHA256_DIGEST_LENGTH + sizeof(key_id_len) + key_id_len)) == NULL ||
211 iso7816_add(apdu, &challenge, sizeof(challenge)) < 0 ||
212 iso7816_add(apdu, &rp_id_hash, sizeof(rp_id_hash)) < 0 ||
213 iso7816_add(apdu, &key_id_len, sizeof(key_id_len)) < 0 ||
214 iso7816_add(apdu, key_id->ptr, key_id_len) < 0) {
215 fido_log_debug("%s: iso7816", __func__);
216 r = FIDO_ERR_INTERNAL;
217 goto fail;
218 }
219
220 if (fido_tx(dev, cmd, iso7816_ptr(apdu), iso7816_len(apdu)) < 0) {
221 fido_log_debug("%s: fido_tx", __func__);
222 r = FIDO_ERR_TX;
223 goto fail;
224 }
225 if (fido_rx(dev, cmd, &reply, sizeof(reply), ms) != 2) {
226 fido_log_debug("%s: fido_rx", __func__);
227 r = FIDO_ERR_RX;
228 goto fail;
229 }
230
231 switch ((reply[0] << 8) | reply[1]) {
232 case SW_CONDITIONS_NOT_SATISFIED:
233 *found = 1; /* key exists */
234 break;
235 case SW_WRONG_DATA:
236 *found = 0; /* key does not exist */
237 break;
238 default:
239 /* unexpected sw */
240 r = FIDO_ERR_INTERNAL;
241 goto fail;
242 }
243
244 r = FIDO_OK;
245fail:
246 iso7816_free(&apdu);
247
248 return (r);
249}
250
251static int
252parse_auth_reply(fido_blob_t *sig, fido_blob_t *ad, const char *rp_id,
253 const unsigned char *reply, size_t len)
254{
255 uint8_t flags;
256 uint32_t sigcount;
257
258 if (len < 2 || ((reply[len - 2] << 8) | reply[len - 1]) != SW_NO_ERROR) {
259 fido_log_debug("%s: unexpected sw", __func__);
260 return (FIDO_ERR_RX);
261 }
262
263 len -= 2;
264
265 if (fido_buf_read(&reply, &len, &flags, sizeof(flags)) < 0 ||
266 fido_buf_read(&reply, &len, &sigcount, sizeof(sigcount)) < 0) {
267 fido_log_debug("%s: fido_buf_read", __func__);
268 return (FIDO_ERR_RX);
269 }
270
271 if (sig_get(sig, &reply, &len) < 0) {
272 fido_log_debug("%s: sig_get", __func__);
273 return (FIDO_ERR_RX);
274 }
275
276 if (authdata_fake(rp_id, flags, sigcount, ad) < 0) {
277 fido_log_debug("%s; authdata_fake", __func__);
278 return (FIDO_ERR_RX);
279 }
280
281 return (FIDO_OK);
282}
283
284static int
285do_auth(fido_dev_t *dev, const fido_blob_t *cdh, const char *rp_id,
286 const fido_blob_t *key_id, fido_blob_t *sig, fido_blob_t *ad, int ms)
287{
288 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_MSG;
289 iso7816_apdu_t *apdu = NULL;
290 unsigned char rp_id_hash[SHA256_DIGEST_LENGTH];
291 unsigned char reply[128];
292 int reply_len;
293 uint8_t key_id_len;
294 int r;
295
296#ifdef FIDO_FUZZ
297 ms = 0; /* XXX */
298#endif
299
300 if (cdh->len != SHA256_DIGEST_LENGTH || key_id->len > UINT8_MAX ||
301 rp_id == NULL) {
302 r = FIDO_ERR_INVALID_ARGUMENT;
303 goto fail;
304 }
305
306 memset(&rp_id_hash, 0, sizeof(rp_id_hash));
307
308 if (SHA256((const void *)rp_id, strlen(rp_id),
309 rp_id_hash) != rp_id_hash) {
310 fido_log_debug("%s: sha256", __func__);
311 r = FIDO_ERR_INTERNAL;
312 goto fail;
313 }
314
315 key_id_len = (uint8_t)key_id->len;
316
317 if ((apdu = iso7816_new(U2F_CMD_AUTH, U2F_AUTH_SIGN, 2 *
318 SHA256_DIGEST_LENGTH + sizeof(key_id_len) + key_id_len)) == NULL ||
319 iso7816_add(apdu, cdh->ptr, cdh->len) < 0 ||
320 iso7816_add(apdu, &rp_id_hash, sizeof(rp_id_hash)) < 0 ||
321 iso7816_add(apdu, &key_id_len, sizeof(key_id_len)) < 0 ||
322 iso7816_add(apdu, key_id->ptr, key_id_len) < 0) {
323 fido_log_debug("%s: iso7816", __func__);
324 r = FIDO_ERR_INTERNAL;
325 goto fail;
326 }
327
328 do {
329 if (fido_tx(dev, cmd, iso7816_ptr(apdu),
330 iso7816_len(apdu)) < 0) {
331 fido_log_debug("%s: fido_tx", __func__);
332 r = FIDO_ERR_TX;
333 goto fail;
334 }
335 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply),
336 ms)) < 2) {
337 fido_log_debug("%s: fido_rx", __func__);
338 r = FIDO_ERR_RX;
339 goto fail;
340 }
341 if (usleep((ms == -1 ? 100 : ms) * 1000) < 0) {
342 fido_log_debug("%s: usleep", __func__);
343 r = FIDO_ERR_RX;
344 goto fail;
345 }
346 } while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED);
347
348 if ((r = parse_auth_reply(sig, ad, rp_id, reply,
349 (size_t)reply_len)) != FIDO_OK) {
350 fido_log_debug("%s: parse_auth_reply", __func__);
351 goto fail;
352 }
353
354fail:
355 iso7816_free(&apdu);
356
357 return (r);
358}
359
360static int
361cbor_blob_from_ec_point(const uint8_t *ec_point, size_t ec_point_len,
362 fido_blob_t *cbor_blob)
363{
364 es256_pk_t *pk = NULL;
365 cbor_item_t *pk_cbor = NULL;
366 size_t alloc_len;
367 int ok = -1;
368
369 /* only handle uncompressed points */
370 if (ec_point_len != 65 || ec_point[0] != 0x04) {
371 fido_log_debug("%s: unexpected format", __func__);
372 goto fail;
373 }
374
375 if ((pk = es256_pk_new()) == NULL ||
376 es256_pk_set_x(pk, &ec_point[1]) < 0 ||
377 es256_pk_set_y(pk, &ec_point[33]) < 0) {
378 fido_log_debug("%s: es256_pk_set", __func__);
379 goto fail;
380 }
381
382 if ((pk_cbor = es256_pk_encode(pk, 0)) == NULL) {
383 fido_log_debug("%s: es256_pk_encode", __func__);
384 goto fail;
385 }
386
387 if ((cbor_blob->len = cbor_serialize_alloc(pk_cbor, &cbor_blob->ptr,
388 &alloc_len)) != 77) {
389 fido_log_debug("%s: cbor_serialize_alloc", __func__);
390 goto fail;
391 }
392
393 ok = 0;
394fail:
395 es256_pk_free(&pk);
396
397 if (pk_cbor)
398 cbor_decref(&pk_cbor);
399
400 return (ok);
401}
402
403static int
404encode_cred_authdata(const char *rp_id, const uint8_t *kh, uint8_t kh_len,
405 const uint8_t *pubkey, size_t pubkey_len, fido_blob_t *out)
406{
407 fido_authdata_t authdata;
408 fido_attcred_raw_t attcred_raw;
409 fido_blob_t pk_blob;
410 fido_blob_t authdata_blob;
411 cbor_item_t *authdata_cbor = NULL;
412 unsigned char *ptr;
413 size_t len;
414 size_t alloc_len;
415 int ok = -1;
416
417 memset(&pk_blob, 0, sizeof(pk_blob));
418 memset(&authdata, 0, sizeof(authdata));
419 memset(&authdata_blob, 0, sizeof(authdata_blob));
420 memset(out, 0, sizeof(*out));
421
422 if (rp_id == NULL) {
423 fido_log_debug("%s: NULL rp_id", __func__);
424 goto fail;
425 }
426
427 if (cbor_blob_from_ec_point(pubkey, pubkey_len, &pk_blob) < 0) {
428 fido_log_debug("%s: cbor_blob_from_ec_point", __func__);
429 goto fail;
430 }
431
432 if (SHA256((const void *)rp_id, strlen(rp_id),
433 authdata.rp_id_hash) != authdata.rp_id_hash) {
434 fido_log_debug("%s: sha256", __func__);
435 goto fail;
436 }
437
438 authdata.flags = (CTAP_AUTHDATA_ATT_CRED | CTAP_AUTHDATA_USER_PRESENT);
439 authdata.sigcount = 0;
440
441 memset(&attcred_raw.aaguid, 0, sizeof(attcred_raw.aaguid));
442 attcred_raw.id_len = (uint16_t)(kh_len << 8); /* XXX */
443
444 len = authdata_blob.len = sizeof(authdata) + sizeof(attcred_raw) +
445 kh_len + pk_blob.len;
446 ptr = authdata_blob.ptr = calloc(1, authdata_blob.len);
447
448 fido_log_debug("%s: ptr=%p, len=%zu", __func__, (void *)ptr, len);
449
450 if (authdata_blob.ptr == NULL)
451 goto fail;
452
453 if (fido_buf_write(&ptr, &len, &authdata, sizeof(authdata)) < 0 ||
454 fido_buf_write(&ptr, &len, &attcred_raw, sizeof(attcred_raw)) < 0 ||
455 fido_buf_write(&ptr, &len, kh, kh_len) < 0 ||
456 fido_buf_write(&ptr, &len, pk_blob.ptr, pk_blob.len) < 0) {
457 fido_log_debug("%s: fido_buf_write", __func__);
458 goto fail;
459 }
460
461 if ((authdata_cbor = fido_blob_encode(&authdata_blob)) == NULL) {
462 fido_log_debug("%s: fido_blob_encode", __func__);
463 goto fail;
464 }
465
466 if ((out->len = cbor_serialize_alloc(authdata_cbor, &out->ptr,
467 &alloc_len)) == 0) {
468 fido_log_debug("%s: cbor_serialize_alloc", __func__);
469 goto fail;
470 }
471
472 ok = 0;
473fail:
474 if (authdata_cbor)
475 cbor_decref(&authdata_cbor);
476
477 if (pk_blob.ptr) {
478 explicit_bzero(pk_blob.ptr, pk_blob.len);
479 free(pk_blob.ptr);
480 }
481 if (authdata_blob.ptr) {
482 explicit_bzero(authdata_blob.ptr, authdata_blob.len);
483 free(authdata_blob.ptr);
484 }
485
486 return (ok);
487}
488
489static int
490parse_register_reply(fido_cred_t *cred, const unsigned char *reply, size_t len)
491{
492 fido_blob_t x5c;
493 fido_blob_t sig;
494 fido_blob_t ad;
495 uint8_t dummy;
496 uint8_t pubkey[65];
497 uint8_t kh_len = 0;
498 uint8_t *kh = NULL;
499 int r;
500
501 memset(&x5c, 0, sizeof(x5c));
502 memset(&sig, 0, sizeof(sig));
503 memset(&ad, 0, sizeof(ad));
504 r = FIDO_ERR_RX;
505
506 /* status word */
507 if (len < 2 || ((reply[len - 2] << 8) | reply[len - 1]) != SW_NO_ERROR) {
508 fido_log_debug("%s: unexpected sw", __func__);
509 goto fail;
510 }
511
512 len -= 2;
513
514 /* reserved byte */
515 if (fido_buf_read(&reply, &len, &dummy, sizeof(dummy)) < 0 ||
516 dummy != 0x05) {
517 fido_log_debug("%s: reserved byte", __func__);
518 goto fail;
519 }
520
521 /* pubkey + key handle */
522 if (fido_buf_read(&reply, &len, &pubkey, sizeof(pubkey)) < 0 ||
523 fido_buf_read(&reply, &len, &kh_len, sizeof(kh_len)) < 0 ||
524 (kh = calloc(1, kh_len)) == NULL ||
525 fido_buf_read(&reply, &len, kh, kh_len) < 0) {
526 fido_log_debug("%s: fido_buf_read", __func__);
527 goto fail;
528 }
529
530 /* x5c + sig */
531 if (x5c_get(&x5c, &reply, &len) < 0 ||
532 sig_get(&sig, &reply, &len) < 0) {
533 fido_log_debug("%s: x5c || sig", __func__);
534 goto fail;
535 }
536
537 /* authdata */
538 if (encode_cred_authdata(cred->rp.id, kh, kh_len, pubkey,
539 sizeof(pubkey), &ad) < 0) {
540 fido_log_debug("%s: encode_cred_authdata", __func__);
541 goto fail;
542 }
543
544 if (fido_cred_set_fmt(cred, "fido-u2f") != FIDO_OK ||
545 fido_cred_set_authdata(cred, ad.ptr, ad.len) != FIDO_OK ||
546 fido_cred_set_x509(cred, x5c.ptr, x5c.len) != FIDO_OK ||
547 fido_cred_set_sig(cred, sig.ptr, sig.len) != FIDO_OK) {
548 fido_log_debug("%s: fido_cred_set", __func__);
549 r = FIDO_ERR_INTERNAL;
550 goto fail;
551 }
552
553 r = FIDO_OK;
554fail:
555 if (kh) {
556 explicit_bzero(kh, kh_len);
557 free(kh);
558 }
559 if (x5c.ptr) {
560 explicit_bzero(x5c.ptr, x5c.len);
561 free(x5c.ptr);
562 }
563 if (sig.ptr) {
564 explicit_bzero(sig.ptr, sig.len);
565 free(sig.ptr);
566 }
567 if (ad.ptr) {
568 explicit_bzero(ad.ptr, ad.len);
569 free(ad.ptr);
570 }
571
572 return (r);
573}
574
575int
576u2f_register(fido_dev_t *dev, fido_cred_t *cred, int ms)
577{
578 const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_MSG;
579 iso7816_apdu_t *apdu = NULL;
580 unsigned char rp_id_hash[SHA256_DIGEST_LENGTH];
581 unsigned char reply[2048];
582 int reply_len;
583 int found;
584 int r;
585
586#ifdef FIDO_FUZZ
587 ms = 0; /* XXX */
588#endif
589
590 if (cred->rk == FIDO_OPT_TRUE || cred->uv == FIDO_OPT_TRUE) {
591 fido_log_debug("%s: rk=%d, uv=%d", __func__, cred->rk,
592 cred->uv);
593 return (FIDO_ERR_UNSUPPORTED_OPTION);
594 }
595
596 if (cred->type != COSE_ES256 || cred->cdh.ptr == NULL ||
597 cred->rp.id == NULL || cred->cdh.len != SHA256_DIGEST_LENGTH) {
598 fido_log_debug("%s: type=%d, cdh=(%p,%zu)" , __func__,
599 cred->type, (void *)cred->cdh.ptr, cred->cdh.len);
600 return (FIDO_ERR_INVALID_ARGUMENT);
601 }
602
603 for (size_t i = 0; i < cred->excl.len; i++) {
604 if ((r = key_lookup(dev, cred->rp.id, &cred->excl.ptr[i],
605 &found, ms)) != FIDO_OK) {
606 fido_log_debug("%s: key_lookup", __func__);
607 return (r);
608 }
609 if (found) {
610 if ((r = send_dummy_register(dev, ms)) != FIDO_OK) {
611 fido_log_debug("%s: send_dummy_register",
612 __func__);
613 return (r);
614 }
615 return (FIDO_ERR_CREDENTIAL_EXCLUDED);
616 }
617 }
618
619 memset(&rp_id_hash, 0, sizeof(rp_id_hash));
620
621 if (SHA256((const void *)cred->rp.id, strlen(cred->rp.id),
622 rp_id_hash) != rp_id_hash) {
623 fido_log_debug("%s: sha256", __func__);
624 return (FIDO_ERR_INTERNAL);
625 }
626
627 if ((apdu = iso7816_new(U2F_CMD_REGISTER, 0, 2 *
628 SHA256_DIGEST_LENGTH)) == NULL ||
629 iso7816_add(apdu, cred->cdh.ptr, cred->cdh.len) < 0 ||
630 iso7816_add(apdu, rp_id_hash, sizeof(rp_id_hash)) < 0) {
631 fido_log_debug("%s: iso7816", __func__);
632 r = FIDO_ERR_INTERNAL;
633 goto fail;
634 }
635
636 do {
637 if (fido_tx(dev, cmd, iso7816_ptr(apdu),
638 iso7816_len(apdu)) < 0) {
639 fido_log_debug("%s: fido_tx", __func__);
640 r = FIDO_ERR_TX;
641 goto fail;
642 }
643 if ((reply_len = fido_rx(dev, cmd, &reply, sizeof(reply),
644 ms)) < 2) {
645 fido_log_debug("%s: fido_rx", __func__);
646 r = FIDO_ERR_RX;
647 goto fail;
648 }
649 if (usleep((ms == -1 ? 100 : ms) * 1000) < 0) {
650 fido_log_debug("%s: usleep", __func__);
651 r = FIDO_ERR_RX;
652 goto fail;
653 }
654 } while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED);
655
656 if ((r = parse_register_reply(cred, reply,
657 (size_t)reply_len)) != FIDO_OK) {
658 fido_log_debug("%s: parse_register_reply", __func__);
659 goto fail;
660 }
661fail:
662 iso7816_free(&apdu);
663
664 return (r);
665}
666
667static int
668u2f_authenticate_single(fido_dev_t *dev, const fido_blob_t *key_id,
669 fido_assert_t *fa, size_t idx, int ms)
670{
671 fido_blob_t sig;
672 fido_blob_t ad;
673 int found;
674 int r;
675
676 memset(&sig, 0, sizeof(sig));
677 memset(&ad, 0, sizeof(ad));
678
679 if ((r = key_lookup(dev, fa->rp_id, key_id, &found, ms)) != FIDO_OK) {
680 fido_log_debug("%s: key_lookup", __func__);
681 goto fail;
682 }
683
684 if (!found) {
685 fido_log_debug("%s: not found", __func__);
686 r = FIDO_ERR_CREDENTIAL_EXCLUDED;
687 goto fail;
688 }
689
690 if (fa->up == FIDO_OPT_FALSE) {
691 fido_log_debug("%s: checking for key existence only", __func__);
692 r = FIDO_ERR_USER_PRESENCE_REQUIRED;
693 goto fail;
694 }
695
696 if ((r = do_auth(dev, &fa->cdh, fa->rp_id, key_id, &sig, &ad,
697 ms)) != FIDO_OK) {
698 fido_log_debug("%s: do_auth", __func__);
699 goto fail;
700 }
701
702 if (fido_blob_set(&fa->stmt[idx].id, key_id->ptr, key_id->len) < 0 ||
703 fido_assert_set_authdata(fa, idx, ad.ptr, ad.len) != FIDO_OK ||
704 fido_assert_set_sig(fa, idx, sig.ptr, sig.len) != FIDO_OK) {
705 fido_log_debug("%s: fido_assert_set", __func__);
706 r = FIDO_ERR_INTERNAL;
707 goto fail;
708 }
709
710 r = FIDO_OK;
711fail:
712 if (sig.ptr) {
713 explicit_bzero(sig.ptr, sig.len);
714 free(sig.ptr);
715 }
716 if (ad.ptr) {
717 explicit_bzero(ad.ptr, ad.len);
718 free(ad.ptr);
719 }
720
721 return (r);
722}
723
724int
725u2f_authenticate(fido_dev_t *dev, fido_assert_t *fa, int ms)
726{
727 int nauth_ok = 0;
728 int r;
729
730 if (fa->uv == FIDO_OPT_TRUE || fa->allow_list.ptr == NULL) {
731 fido_log_debug("%s: uv=%d, allow_list=%p", __func__, fa->uv,
732 (void *)fa->allow_list.ptr);
733 return (FIDO_ERR_UNSUPPORTED_OPTION);
734 }
735
736 if ((r = fido_assert_set_count(fa, fa->allow_list.len)) != FIDO_OK) {
737 fido_log_debug("%s: fido_assert_set_count", __func__);
738 return (r);
739 }
740
741 for (size_t i = 0; i < fa->allow_list.len; i++) {
742 if ((r = u2f_authenticate_single(dev, &fa->allow_list.ptr[i],
743 fa, nauth_ok, ms)) == FIDO_OK) {
744 nauth_ok++;
745 } else if (r != FIDO_ERR_CREDENTIAL_EXCLUDED) {
746 fido_log_debug("%s: u2f_authenticate_single", __func__);
747 return (r);
748 }
749 /* ignore credentials that don't exist */
750 }
751
752 fa->stmt_len = nauth_ok;
753
754 if (nauth_ok == 0)
755 return (FIDO_ERR_NO_CREDENTIALS);
756
757 return (FIDO_OK);
758}
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
new file mode 100644
index 0000000..5f27e88
--- /dev/null
+++ b/tools/CMakeLists.txt
@@ -0,0 +1,65 @@
1# Copyright (c) 2018 Yubico AB. All rights reserved.
2# Use of this source code is governed by a BSD-style
3# license that can be found in the LICENSE file.
4
5list(APPEND COMPAT_SOURCES
6 ../openbsd-compat/explicit_bzero.c
7 ../openbsd-compat/strlcpy.c
8 ../openbsd-compat/strlcat.c
9)
10
11if(WIN32)
12 list(APPEND COMPAT_SOURCES
13 ../openbsd-compat/bsd-getline.c
14 ../openbsd-compat/explicit_bzero_win32.c
15 ../openbsd-compat/getopt_long.c
16 ../openbsd-compat/posix_win.c
17 ../openbsd-compat/readpassphrase_win32.c
18 )
19else()
20 list(APPEND COMPAT_SOURCES ../openbsd-compat/readpassphrase.c)
21endif()
22
23add_executable(fido2-cred
24 fido2-cred.c
25 cred_make.c
26 cred_verify.c
27 base64.c
28 util.c
29 ${COMPAT_SOURCES}
30)
31
32add_executable(fido2-assert
33 fido2-assert.c
34 assert_get.c
35 assert_verify.c
36 base64.c
37 util.c
38 ${COMPAT_SOURCES}
39)
40
41add_executable(fido2-token
42 fido2-token.c
43 base64.c
44 bio.c
45 credman.c
46 pin.c
47 token.c
48 util.c
49 ${COMPAT_SOURCES}
50)
51
52add_library(sk-libfido2 MODULE sk-libfido2.c)
53set_target_properties(sk-libfido2 PROPERTIES
54 COMPILE_FLAGS "-DSK_STANDALONE -DWITH_OPENSSL"
55 OUTPUT_NAME sk-libfido2
56)
57
58target_link_libraries(fido2-cred ${CRYPTO_LIBRARIES} fido2_shared)
59target_link_libraries(fido2-assert ${CRYPTO_LIBRARIES} fido2_shared)
60target_link_libraries(fido2-token ${CRYPTO_LIBRARIES} fido2_shared)
61target_link_libraries(sk-libfido2 ${CRYPTO_LIBRARIES} fido2_shared)
62
63install(TARGETS fido2-cred fido2-assert fido2-token
64 DESTINATION ${CMAKE_INSTALL_BINDIR})
65install(TARGETS sk-libfido2 DESTINATION ${CMAKE_INSTALL_LIBDIR})
diff --git a/tools/assert_get.c b/tools/assert_get.c
new file mode 100644
index 0000000..5e209cd
--- /dev/null
+++ b/tools/assert_get.c
@@ -0,0 +1,233 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <fido.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#ifdef HAVE_UNISTD_H
12#include <unistd.h>
13#endif
14
15#include "../openbsd-compat/openbsd-compat.h"
16#include "extern.h"
17
18static fido_assert_t *
19prepare_assert(FILE *in_f, int flags)
20{
21 fido_assert_t *assert = NULL;
22 struct blob cdh;
23 struct blob id;
24 struct blob hmac_salt;
25 char *rpid = NULL;
26 int r;
27
28 memset(&cdh, 0, sizeof(cdh));
29 memset(&id, 0, sizeof(id));
30 memset(&hmac_salt, 0, sizeof(hmac_salt));
31
32 r = base64_read(in_f, &cdh);
33 r |= string_read(in_f, &rpid);
34 if ((flags & FLAG_RK) == 0)
35 r |= base64_read(in_f, &id);
36 if (flags & FLAG_HMAC)
37 r |= base64_read(in_f, &hmac_salt);
38 if (r < 0)
39 errx(1, "input error");
40
41 if (flags & FLAG_DEBUG) {
42 fprintf(stderr, "client data hash:\n");
43 xxd(cdh.ptr, cdh.len);
44 fprintf(stderr, "relying party id: %s\n", rpid);
45 if ((flags & FLAG_RK) == 0) {
46 fprintf(stderr, "credential id:\n");
47 xxd(id.ptr, id.len);
48 }
49 }
50
51 if ((assert = fido_assert_new()) == NULL)
52 errx(1, "fido_assert_new");
53
54 if ((r = fido_assert_set_clientdata_hash(assert, cdh.ptr,
55 cdh.len)) != FIDO_OK ||
56 (r = fido_assert_set_rp(assert, rpid)) != FIDO_OK)
57 errx(1, "fido_assert_set: %s", fido_strerr(r));
58
59 if (flags & FLAG_UP) {
60 if ((r = fido_assert_set_up(assert, FIDO_OPT_TRUE)) != FIDO_OK)
61 errx(1, "fido_assert_set_up: %s", fido_strerr(r));
62 }
63 if (flags & FLAG_UV) {
64 if ((r = fido_assert_set_uv(assert, FIDO_OPT_TRUE)) != FIDO_OK)
65 errx(1, "fido_assert_set_uv: %s", fido_strerr(r));
66 }
67 if (flags & FLAG_HMAC) {
68 if ((r = fido_assert_set_extensions(assert,
69 FIDO_EXT_HMAC_SECRET)) != FIDO_OK)
70 errx(1, "fido_assert_set_extensions: %s",
71 fido_strerr(r));
72 if ((r = fido_assert_set_hmac_salt(assert, hmac_salt.ptr,
73 hmac_salt.len)) != FIDO_OK)
74 errx(1, "fido_assert_set_hmac_salt: %s",
75 fido_strerr(r));
76 }
77 if ((flags & FLAG_RK) == 0) {
78 if ((r = fido_assert_allow_cred(assert, id.ptr,
79 id.len)) != FIDO_OK)
80 errx(1, "fido_assert_allow_cred: %s", fido_strerr(r));
81 }
82
83 free(hmac_salt.ptr);
84 free(cdh.ptr);
85 free(id.ptr);
86 free(rpid);
87
88 return (assert);
89}
90
91static void
92print_assert(FILE *out_f, const fido_assert_t *assert, size_t idx, int flags)
93{
94 char *cdh = NULL;
95 char *authdata = NULL;
96 char *sig = NULL;
97 char *user_id = NULL;
98 char *hmac_secret = NULL;
99 int r;
100
101 r = base64_encode(fido_assert_clientdata_hash_ptr(assert),
102 fido_assert_clientdata_hash_len(assert), &cdh);
103 r |= base64_encode(fido_assert_authdata_ptr(assert, idx),
104 fido_assert_authdata_len(assert, 0), &authdata);
105 r |= base64_encode(fido_assert_sig_ptr(assert, idx),
106 fido_assert_sig_len(assert, idx), &sig);
107 if (flags & FLAG_RK)
108 r |= base64_encode(fido_assert_user_id_ptr(assert, idx),
109 fido_assert_user_id_len(assert, idx), &user_id);
110 if (flags & FLAG_HMAC)
111 r |= base64_encode(fido_assert_hmac_secret_ptr(assert, idx),
112 fido_assert_hmac_secret_len(assert, idx), &hmac_secret);
113 if (r < 0)
114 errx(1, "output error");
115
116 fprintf(out_f, "%s\n", cdh);
117 fprintf(out_f, "%s\n", fido_assert_rp_id(assert));
118 fprintf(out_f, "%s\n", authdata);
119 fprintf(out_f, "%s\n", sig);
120 if (flags & FLAG_RK)
121 fprintf(out_f, "%s\n", user_id);
122 if (hmac_secret) {
123 fprintf(out_f, "%s\n", hmac_secret);
124 explicit_bzero(hmac_secret, strlen(hmac_secret));
125 }
126
127 free(hmac_secret);
128 free(cdh);
129 free(authdata);
130 free(sig);
131 free(user_id);
132}
133
134int
135assert_get(int argc, char **argv)
136{
137 fido_dev_t *dev = NULL;
138 fido_assert_t *assert = NULL;
139 char pin[1024];
140 char prompt[1024];
141 char *in_path = NULL;
142 char *out_path = NULL;
143 FILE *in_f = NULL;
144 FILE *out_f = NULL;
145 int flags = 0;
146 int ch;
147 int r;
148
149 while ((ch = getopt(argc, argv, "dhi:o:pruv")) != -1) {
150 switch (ch) {
151 case 'd':
152 flags |= FLAG_DEBUG;
153 break;
154 case 'h':
155 flags |= FLAG_HMAC;
156 break;
157 case 'i':
158 in_path = optarg;
159 break;
160 case 'o':
161 out_path = optarg;
162 break;
163 case 'p':
164 flags |= FLAG_UP;
165 break;
166 case 'r':
167 flags |= FLAG_RK;
168 break;
169 case 'u':
170 flags |= FLAG_U2F;
171 break;
172 case 'v':
173 flags |= FLAG_UV;
174 break;
175 default:
176 usage();
177 }
178 }
179
180 argc -= optind;
181 argv += optind;
182
183 if (argc < 1)
184 usage();
185
186 in_f = open_read(in_path);
187 out_f = open_write(out_path);
188
189 fido_init((flags & FLAG_DEBUG) ? FIDO_DEBUG : 0);
190
191 assert = prepare_assert(in_f, flags);
192
193 dev = open_dev(argv[0]);
194 if (flags & FLAG_U2F)
195 fido_dev_force_u2f(dev);
196
197 if (flags & FLAG_UV) {
198 r = snprintf(prompt, sizeof(prompt), "Enter PIN for %s: ",
199 argv[0]);
200 if (r < 0 || (size_t)r >= sizeof(prompt))
201 errx(1, "snprintf");
202 if (!readpassphrase(prompt, pin, sizeof(pin), RPP_ECHO_OFF))
203 errx(1, "readpassphrase");
204 r = fido_dev_get_assert(dev, assert, pin);
205 } else
206 r = fido_dev_get_assert(dev, assert, NULL);
207
208 explicit_bzero(pin, sizeof(pin));
209
210 if (r != FIDO_OK)
211 errx(1, "fido_dev_get_assert: %s", fido_strerr(r));
212
213 if (flags & FLAG_RK) {
214 for (size_t idx = 0; idx < fido_assert_count(assert); idx++)
215 print_assert(out_f, assert, idx, flags);
216 } else {
217 if (fido_assert_count(assert) != 1)
218 errx(1, "fido_assert_count: %zu",
219 fido_assert_count(assert));
220 print_assert(out_f, assert, 0, flags);
221 }
222
223 fido_dev_close(dev);
224 fido_dev_free(&dev);
225 fido_assert_free(&assert);
226
227 fclose(in_f);
228 fclose(out_f);
229 in_f = NULL;
230 out_f = NULL;
231
232 exit(0);
233}
diff --git a/tools/assert_verify.c b/tools/assert_verify.c
new file mode 100644
index 0000000..ccff57a
--- /dev/null
+++ b/tools/assert_verify.c
@@ -0,0 +1,201 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <fido.h>
8#include <fido/es256.h>
9#include <fido/rs256.h>
10#include <fido/eddsa.h>
11
12#include <stdbool.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#ifdef HAVE_UNISTD_H
17#include <unistd.h>
18#endif
19
20#include "../openbsd-compat/openbsd-compat.h"
21#include "extern.h"
22
23static fido_assert_t *
24prepare_assert(FILE *in_f, int flags)
25{
26 fido_assert_t *assert = NULL;
27 struct blob cdh;
28 struct blob authdata;
29 struct blob sig;
30 char *rpid = NULL;
31 int r;
32
33 memset(&cdh, 0, sizeof(cdh));
34 memset(&authdata, 0, sizeof(authdata));
35 memset(&sig, 0, sizeof(sig));
36
37 r = base64_read(in_f, &cdh);
38 r |= string_read(in_f, &rpid);
39 r |= base64_read(in_f, &authdata);
40 r |= base64_read(in_f, &sig);
41 if (r < 0)
42 errx(1, "input error");
43
44 if (flags & FLAG_DEBUG) {
45 fprintf(stderr, "client data hash:\n");
46 xxd(cdh.ptr, cdh.len);
47 fprintf(stderr, "relying party id: %s\n", rpid);
48 fprintf(stderr, "authenticator data:\n");
49 xxd(authdata.ptr, authdata.len);
50 fprintf(stderr, "signature:\n");
51 xxd(sig.ptr, sig.len);
52 }
53
54 if ((assert = fido_assert_new()) == NULL)
55 errx(1, "fido_assert_new");
56 if ((r = fido_assert_set_count(assert, 1)) != FIDO_OK)
57 errx(1, "fido_assert_count: %s", fido_strerr(r));
58
59 if ((r = fido_assert_set_clientdata_hash(assert, cdh.ptr,
60 cdh.len)) != FIDO_OK ||
61 (r = fido_assert_set_rp(assert, rpid)) != FIDO_OK ||
62 (r = fido_assert_set_authdata(assert, 0, authdata.ptr,
63 authdata.len)) != FIDO_OK ||
64 (r = fido_assert_set_sig(assert, 0, sig.ptr, sig.len)) != FIDO_OK)
65 errx(1, "fido_assert_set: %s", fido_strerr(r));
66
67 if (flags & FLAG_UP) {
68 if ((r = fido_assert_set_up(assert, FIDO_OPT_TRUE)) != FIDO_OK)
69 errx(1, "fido_assert_set_up: %s", fido_strerr(r));
70 }
71 if (flags & FLAG_UV) {
72 if ((r = fido_assert_set_uv(assert, FIDO_OPT_TRUE)) != FIDO_OK)
73 errx(1, "fido_assert_set_uv: %s", fido_strerr(r));
74 }
75 if (flags & FLAG_HMAC) {
76 if ((r = fido_assert_set_extensions(assert,
77 FIDO_EXT_HMAC_SECRET)) != FIDO_OK)
78 errx(1, "fido_assert_set_extensions: %s",
79 fido_strerr(r));
80 }
81
82 free(cdh.ptr);
83 free(authdata.ptr);
84 free(sig.ptr);
85 free(rpid);
86
87 return (assert);
88}
89
90static void *
91load_pubkey(int type, const char *file)
92{
93 EC_KEY *ec = NULL;
94 RSA *rsa = NULL;
95 EVP_PKEY *eddsa = NULL;
96 es256_pk_t *es256_pk = NULL;
97 rs256_pk_t *rs256_pk = NULL;
98 eddsa_pk_t *eddsa_pk = NULL;
99 void *pk = NULL;
100
101 if (type == COSE_ES256) {
102 if ((ec = read_ec_pubkey(file)) == NULL)
103 errx(1, "read_ec_pubkey");
104 if ((es256_pk = es256_pk_new()) == NULL)
105 errx(1, "es256_pk_new");
106 if (es256_pk_from_EC_KEY(es256_pk, ec) != FIDO_OK)
107 errx(1, "es256_pk_from_EC_KEY");
108
109 pk = es256_pk;
110 EC_KEY_free(ec);
111 } else if (type == COSE_RS256) {
112 if ((rsa = read_rsa_pubkey(file)) == NULL)
113 errx(1, "read_rsa_pubkey");
114 if ((rs256_pk = rs256_pk_new()) == NULL)
115 errx(1, "rs256_pk_new");
116 if (rs256_pk_from_RSA(rs256_pk, rsa) != FIDO_OK)
117 errx(1, "rs256_pk_from_RSA");
118
119 pk = rs256_pk;
120 RSA_free(rsa);
121 } else if (type == COSE_EDDSA) {
122 if ((eddsa = read_eddsa_pubkey(file)) == NULL)
123 errx(1, "read_eddsa_pubkey");
124 if ((eddsa_pk = eddsa_pk_new()) == NULL)
125 errx(1, "eddsa_pk_new");
126 if (eddsa_pk_from_EVP_PKEY(eddsa_pk, eddsa) != FIDO_OK)
127 errx(1, "eddsa_pk_from_EVP_PKEY");
128
129 pk = eddsa_pk;
130 EVP_PKEY_free(eddsa);
131 }
132
133 return (pk);
134}
135
136int
137assert_verify(int argc, char **argv)
138{
139 fido_assert_t *assert = NULL;
140 void *pk = NULL;
141 char *in_path = NULL;
142 FILE *in_f = NULL;
143 int type = COSE_ES256;
144 int flags = 0;
145 int ch;
146 int r;
147
148 while ((ch = getopt(argc, argv, "dhi:pv")) != -1) {
149 switch (ch) {
150 case 'd':
151 flags |= FLAG_DEBUG;
152 break;
153 case 'h':
154 flags |= FLAG_HMAC;
155 break;
156 case 'i':
157 in_path = optarg;
158 break;
159 case 'p':
160 flags |= FLAG_UP;
161 break;
162 case 'v':
163 flags |= FLAG_UV;
164 break;
165 default:
166 usage();
167 }
168 }
169
170 argc -= optind;
171 argv += optind;
172
173 if (argc < 1 || argc > 2)
174 usage();
175
176 in_f = open_read(in_path);
177
178 if (argc > 1) {
179 if (strcmp(argv[1], "es256") == 0)
180 type = COSE_ES256;
181 else if (strcmp(argv[1], "rs256") == 0)
182 type = COSE_RS256;
183 else if (strcmp(argv[1], "eddsa") == 0)
184 type = COSE_EDDSA;
185 else
186 errx(1, "unknown type %s", argv[1]);
187 }
188
189 fido_init((flags & FLAG_DEBUG) ? FIDO_DEBUG : 0);
190
191 pk = load_pubkey(type, argv[0]);
192 assert = prepare_assert(in_f, flags);
193 if ((r = fido_assert_verify(assert, 0, type, pk)) != FIDO_OK)
194 errx(1, "fido_assert_verify: %s", fido_strerr(r));
195 fido_assert_free(&assert);
196
197 fclose(in_f);
198 in_f = NULL;
199
200 exit(0);
201}
diff --git a/tools/base64.c b/tools/base64.c
new file mode 100644
index 0000000..9f31def
--- /dev/null
+++ b/tools/base64.c
@@ -0,0 +1,135 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <openssl/bio.h>
8#include <openssl/evp.h>
9
10#include <fido.h>
11#include <limits.h>
12#include <stdint.h>
13#include <string.h>
14
15#include "../openbsd-compat/openbsd-compat.h"
16#include "extern.h"
17
18int
19base64_encode(const void *ptr, size_t len, char **out)
20{
21 BIO *bio_b64 = NULL;
22 BIO *bio_mem = NULL;
23 char *b64_ptr = NULL;
24 long b64_len;
25 int n;
26 int ok = -1;
27
28 if (ptr == NULL || out == NULL || len > INT_MAX)
29 return (-1);
30
31 *out = NULL;
32
33 if ((bio_b64 = BIO_new(BIO_f_base64())) == NULL)
34 goto fail;
35 if ((bio_mem = BIO_new(BIO_s_mem())) == NULL)
36 goto fail;
37
38 BIO_set_flags(bio_b64, BIO_FLAGS_BASE64_NO_NL);
39 BIO_push(bio_b64, bio_mem);
40
41 n = BIO_write(bio_b64, ptr, (int)len);
42 if (n < 0 || (size_t)n != len)
43 goto fail;
44
45 if (BIO_flush(bio_b64) < 0)
46 goto fail;
47
48 b64_len = BIO_get_mem_data(bio_b64, &b64_ptr);
49 if (b64_len < 0 || (size_t)b64_len == SIZE_MAX || b64_ptr == NULL)
50 goto fail;
51 if ((*out = calloc(1, (size_t)b64_len + 1)) == NULL)
52 goto fail;
53
54 memcpy(*out, b64_ptr, (size_t)b64_len);
55 ok = 0;
56
57fail:
58 BIO_free(bio_b64);
59 BIO_free(bio_mem);
60
61 return (ok);
62}
63
64int
65base64_decode(char *in, void **ptr, size_t *len)
66{
67 BIO *bio_mem = NULL;
68 BIO *bio_b64 = NULL;
69 size_t alloc_len;
70 int n;
71 int ok = -1;
72
73 if (in == NULL || ptr == NULL || len == NULL || strlen(in) > INT_MAX)
74 return (-1);
75
76 *ptr = NULL;
77 *len = 0;
78
79 if ((bio_b64 = BIO_new(BIO_f_base64())) == NULL)
80 goto fail;
81 if ((bio_mem = BIO_new_mem_buf((void *)in, -1)) == NULL)
82 goto fail;
83
84 BIO_set_flags(bio_b64, BIO_FLAGS_BASE64_NO_NL);
85 BIO_push(bio_b64, bio_mem);
86
87 alloc_len = strlen(in);
88 if ((*ptr = calloc(1, alloc_len)) == NULL)
89 goto fail;
90
91 n = BIO_read(bio_b64, *ptr, (int)alloc_len);
92 if (n <= 0 || BIO_eof(bio_b64) == 0)
93 goto fail;
94
95 *len = (size_t)n;
96 ok = 0;
97
98fail:
99 BIO_free(bio_b64);
100 BIO_free(bio_mem);
101
102 if (ok < 0) {
103 free(*ptr);
104 *ptr = NULL;
105 *len = 0;
106 }
107
108 return (ok);
109}
110
111int
112base64_read(FILE *f, struct blob *out)
113{
114 char *line = NULL;
115 size_t linesize = 0;
116 ssize_t n;
117
118 out->ptr = NULL;
119 out->len = 0;
120
121 if ((n = getline(&line, &linesize, f)) <= 0 ||
122 (size_t)n != strlen(line)) {
123 free(line); /* XXX should be free'd _even_ if getline() fails */
124 return (-1);
125 }
126
127 if (base64_decode(line, (void **)&out->ptr, &out->len) < 0) {
128 free(line);
129 return (-1);
130 }
131
132 free(line);
133
134 return (0);
135}
diff --git a/tools/bio.c b/tools/bio.c
new file mode 100644
index 0000000..b8f9b38
--- /dev/null
+++ b/tools/bio.c
@@ -0,0 +1,270 @@
1/*
2 * Copyright (c) 2019 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <fido.h>
8#include <fido/bio.h>
9
10#include <stdbool.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#ifdef HAVE_UNISTD_H
15#include <unistd.h>
16#endif
17
18#include "../openbsd-compat/openbsd-compat.h"
19#include "extern.h"
20
21static void
22print_template(const fido_bio_template_array_t *ta, size_t idx)
23{
24 char *id = NULL;
25 const fido_bio_template_t *t = NULL;
26
27 if ((t = fido_bio_template(ta, idx)) == NULL)
28 errx(1, "fido_bio_template");
29
30 if (base64_encode(fido_bio_template_id_ptr(t),
31 fido_bio_template_id_len(t), &id) < 0)
32 errx(1, "output error");
33
34 printf("%02u: %s %s\n", (unsigned)idx, id, fido_bio_template_name(t));
35
36 free(id);
37}
38
39int
40bio_list(char *path)
41{
42 char pin[1024];
43 fido_bio_template_array_t *ta = NULL;
44 fido_dev_t *dev = NULL;
45 int r;
46
47 if (path == NULL)
48 usage();
49 if ((ta = fido_bio_template_array_new()) == NULL)
50 errx(1, "fido_bio_template_array_new");
51
52 dev = open_dev(path);
53 read_pin(path, pin, sizeof(pin));
54 r = fido_bio_dev_get_template_array(dev, ta, pin);
55 explicit_bzero(pin, sizeof(pin));
56
57 if (r != FIDO_OK)
58 errx(1, "fido_bio_dev_get_template_array: %s", fido_strerr(r));
59 for (size_t i = 0; i < fido_bio_template_array_count(ta); i++)
60 print_template(ta, i);
61
62 fido_bio_template_array_free(&ta);
63 fido_dev_close(dev);
64 fido_dev_free(&dev);
65
66 exit(0);
67}
68
69int
70bio_set_name(char *path, char *id, char *name)
71{
72 char pin[1024];
73 fido_bio_template_t *t = NULL;
74 fido_dev_t *dev = NULL;
75 int r;
76 size_t id_blob_len = 0;
77 void *id_blob_ptr = NULL;
78
79 if (path == NULL)
80 usage();
81 if ((t = fido_bio_template_new()) == NULL)
82 errx(1, "fido_bio_template_new");
83
84 if (base64_decode(id, &id_blob_ptr, &id_blob_len) < 0)
85 errx(1, "base64_decode");
86
87 if ((r = fido_bio_template_set_name(t, name)) != FIDO_OK)
88 errx(1, "fido_bio_template_set_name: %s", fido_strerr(r));
89 if ((r = fido_bio_template_set_id(t, id_blob_ptr,
90 id_blob_len)) != FIDO_OK)
91 errx(1, "fido_bio_template_set_id: %s", fido_strerr(r));
92
93 dev = open_dev(path);
94 read_pin(path, pin, sizeof(pin));
95 r = fido_bio_dev_set_template_name(dev, t, pin);
96 explicit_bzero(pin, sizeof(pin));
97
98 if (r != FIDO_OK)
99 errx(1, "fido_bio_dev_set_template_name: %s", fido_strerr(r));
100
101 free(id_blob_ptr);
102 fido_bio_template_free(&t);
103 fido_dev_close(dev);
104 fido_dev_free(&dev);
105
106 exit(0);
107}
108
109static const char *
110plural(uint8_t n)
111{
112 if (n == 1)
113 return "";
114 return "s";
115}
116
117static const char *
118enroll_strerr(uint8_t n)
119{
120 switch (n) {
121 case FIDO_BIO_ENROLL_FP_GOOD:
122 return "Sample ok";
123 case FIDO_BIO_ENROLL_FP_TOO_HIGH:
124 return "Sample too high";
125 case FIDO_BIO_ENROLL_FP_TOO_LOW:
126 return "Sample too low";
127 case FIDO_BIO_ENROLL_FP_TOO_LEFT:
128 return "Sample too left";
129 case FIDO_BIO_ENROLL_FP_TOO_RIGHT:
130 return "Sample too right";
131 case FIDO_BIO_ENROLL_FP_TOO_FAST:
132 return "Sample too fast";
133 case FIDO_BIO_ENROLL_FP_TOO_SLOW:
134 return "Sample too slow";
135 case FIDO_BIO_ENROLL_FP_POOR_QUALITY:
136 return "Poor quality sample";
137 case FIDO_BIO_ENROLL_FP_TOO_SKEWED:
138 return "Sample too skewed";
139 case FIDO_BIO_ENROLL_FP_TOO_SHORT:
140 return "Sample too short";
141 case FIDO_BIO_ENROLL_FP_MERGE_FAILURE:
142 return "Sample merge failure";
143 case FIDO_BIO_ENROLL_FP_EXISTS:
144 return "Sample exists";
145 case FIDO_BIO_ENROLL_FP_DATABASE_FULL:
146 return "Fingerprint database full";
147 case FIDO_BIO_ENROLL_NO_USER_ACTIVITY:
148 return "No user activity";
149 case FIDO_BIO_ENROLL_NO_USER_PRESENCE_TRANSITION:
150 return "No user presence transition";
151 default:
152 return "Unknown error";
153 }
154}
155
156int
157bio_enroll(char *path)
158{
159 char pin[1024];
160 fido_bio_enroll_t *e = NULL;
161 fido_bio_template_t *t = NULL;
162 fido_dev_t *dev = NULL;
163 int r;
164
165 if (path == NULL)
166 usage();
167 if ((t = fido_bio_template_new()) == NULL)
168 errx(1, "fido_bio_template_new");
169 if ((e = fido_bio_enroll_new()) == NULL)
170 errx(1, "fido_bio_enroll_new");
171
172 dev = open_dev(path);
173 read_pin(path, pin, sizeof(pin));
174
175 printf("Touch your security key.\n");
176
177 r = fido_bio_dev_enroll_begin(dev, t, e, 10000, pin);
178 explicit_bzero(pin, sizeof(pin));
179 if (r != FIDO_OK)
180 errx(1, "fido_bio_dev_enroll_begin: %s", fido_strerr(r));
181
182 printf("%s.\n", enroll_strerr(fido_bio_enroll_last_status(e)));
183
184 while (fido_bio_enroll_remaining_samples(e) > 0) {
185 printf("Touch your security key (%u sample%s left).\n",
186 (unsigned)fido_bio_enroll_remaining_samples(e),
187 plural(fido_bio_enroll_remaining_samples(e)));
188 if ((r = fido_bio_dev_enroll_continue(dev, t, e,
189 10000)) != FIDO_OK) {
190 errx(1, "fido_bio_dev_enroll_continue: %s",
191 fido_strerr(r));
192 }
193 printf("%s.\n", enroll_strerr(fido_bio_enroll_last_status(e)));
194 }
195
196 fido_bio_template_free(&t);
197 fido_bio_enroll_free(&e);
198 fido_dev_close(dev);
199 fido_dev_free(&dev);
200
201 exit(0);
202}
203
204int
205bio_delete(fido_dev_t *dev, char *path, char *id)
206{
207 char pin[1024];
208 fido_bio_template_t *t = NULL;
209 int r;
210 size_t id_blob_len = 0;
211 void *id_blob_ptr = NULL;
212
213 if (path == NULL)
214 usage();
215 if ((t = fido_bio_template_new()) == NULL)
216 errx(1, "fido_bio_template_new");
217
218 if (base64_decode(id, &id_blob_ptr, &id_blob_len) < 0)
219 errx(1, "base64_decode");
220 if ((r = fido_bio_template_set_id(t, id_blob_ptr,
221 id_blob_len)) != FIDO_OK)
222 errx(1, "fido_bio_template_set_id: %s", fido_strerr(r));
223
224 read_pin(path, pin, sizeof(pin));
225 r = fido_bio_dev_enroll_remove(dev, t, pin);
226 explicit_bzero(pin, sizeof(pin));
227
228 if (r != FIDO_OK)
229 errx(1, "fido_bio_dev_enroll_remove: %s", fido_strerr(r));
230
231 free(id_blob_ptr);
232 fido_bio_template_free(&t);
233 fido_dev_close(dev);
234 fido_dev_free(&dev);
235
236 exit(0);
237}
238
239static const char *
240type_str(uint8_t t)
241{
242 switch (t) {
243 case 1:
244 return "touch";
245 case 2:
246 return "swipe";
247 default:
248 return "unknown";
249 }
250}
251
252void
253bio_info(fido_dev_t *dev)
254{
255 fido_bio_info_t *i = NULL;
256 int r;
257
258 if ((i = fido_bio_info_new()) == NULL)
259 errx(1, "fido_bio_info_new");
260 if ((r = fido_bio_dev_get_info(dev, i)) != FIDO_OK) {
261 fido_bio_info_free(&i);
262 return;
263 }
264
265 printf("sensor type: %u (%s)\n", (unsigned)fido_bio_info_type(i),
266 type_str(fido_bio_info_type(i)));
267 printf("max samples: %u\n", (unsigned)fido_bio_info_max_samples(i));
268
269 fido_bio_info_free(&i);
270}
diff --git a/tools/cred_make.c b/tools/cred_make.c
new file mode 100644
index 0000000..380c67a
--- /dev/null
+++ b/tools/cred_make.c
@@ -0,0 +1,221 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <fido.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#ifdef HAVE_UNISTD_H
12#include <unistd.h>
13#endif
14
15#include "../openbsd-compat/openbsd-compat.h"
16#include "extern.h"
17
18static fido_cred_t *
19prepare_cred(FILE *in_f, int type, int flags)
20{
21 fido_cred_t *cred = NULL;
22 struct blob cdh;
23 struct blob uid;
24 char *rpid = NULL;
25 char *uname = NULL;
26 int r;
27
28 memset(&cdh, 0, sizeof(cdh));
29 memset(&uid, 0, sizeof(uid));
30
31 r = base64_read(in_f, &cdh);
32 r |= string_read(in_f, &rpid);
33 r |= string_read(in_f, &uname);
34 r |= base64_read(in_f, &uid);
35 if (r < 0)
36 errx(1, "input error");
37
38 if (flags & FLAG_DEBUG) {
39 fprintf(stderr, "client data hash:\n");
40 xxd(cdh.ptr, cdh.len);
41 fprintf(stderr, "relying party id: %s\n", rpid);
42 fprintf(stderr, "user name: %s\n", uname);
43 fprintf(stderr, "user id:\n");
44 xxd(uid.ptr, uid.len);
45 }
46
47 if ((cred = fido_cred_new()) == NULL)
48 errx(1, "fido_cred_new");
49
50 if ((r = fido_cred_set_type(cred, type)) != FIDO_OK ||
51 (r = fido_cred_set_clientdata_hash(cred, cdh.ptr,
52 cdh.len)) != FIDO_OK ||
53 (r = fido_cred_set_rp(cred, rpid, NULL)) != FIDO_OK ||
54 (r = fido_cred_set_user(cred, uid.ptr, uid.len, uname, NULL,
55 NULL)) != FIDO_OK)
56 errx(1, "fido_cred_set: %s", fido_strerr(r));
57
58 if (flags & FLAG_RK) {
59 if ((r = fido_cred_set_rk(cred, FIDO_OPT_TRUE)) != FIDO_OK)
60 errx(1, "fido_cred_set_rk: %s", fido_strerr(r));
61 }
62 if (flags & FLAG_UV) {
63 if ((r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK)
64 errx(1, "fido_cred_set_uv: %s", fido_strerr(r));
65 }
66 if (flags & FLAG_HMAC) {
67 if ((r = fido_cred_set_extensions(cred,
68 FIDO_EXT_HMAC_SECRET)) != FIDO_OK)
69 errx(1, "fido_cred_set_extensions: %s", fido_strerr(r));
70 }
71
72 free(cdh.ptr);
73 free(uid.ptr);
74 free(rpid);
75 free(uname);
76
77 return (cred);
78}
79
80static void
81print_attcred(FILE *out_f, const fido_cred_t *cred)
82{
83 char *cdh = NULL;
84 char *authdata = NULL;
85 char *id = NULL;
86 char *sig = NULL;
87 char *x5c = NULL;
88 int r;
89
90 r = base64_encode(fido_cred_clientdata_hash_ptr(cred),
91 fido_cred_clientdata_hash_len(cred), &cdh);
92 r |= base64_encode(fido_cred_authdata_ptr(cred),
93 fido_cred_authdata_len(cred), &authdata);
94 r |= base64_encode(fido_cred_id_ptr(cred), fido_cred_id_len(cred),
95 &id);
96 r |= base64_encode(fido_cred_sig_ptr(cred), fido_cred_sig_len(cred),
97 &sig);
98 if (fido_cred_x5c_ptr(cred) != NULL)
99 r |= base64_encode(fido_cred_x5c_ptr(cred),
100 fido_cred_x5c_len(cred), &x5c);
101 if (r < 0)
102 errx(1, "output error");
103
104 fprintf(out_f, "%s\n", cdh);
105 fprintf(out_f, "%s\n", fido_cred_rp_id(cred));
106 fprintf(out_f, "%s\n", fido_cred_fmt(cred));
107 fprintf(out_f, "%s\n", authdata);
108 fprintf(out_f, "%s\n", id);
109 fprintf(out_f, "%s\n", sig);
110 if (x5c != NULL)
111 fprintf(out_f, "%s\n", x5c);
112
113 free(cdh);
114 free(authdata);
115 free(id);
116 free(sig);
117 free(x5c);
118}
119
120int
121cred_make(int argc, char **argv)
122{
123 fido_dev_t *dev = NULL;
124 fido_cred_t *cred = NULL;
125 char prompt[1024];
126 char pin[1024];
127 char *in_path = NULL;
128 char *out_path = NULL;
129 FILE *in_f = NULL;
130 FILE *out_f = NULL;
131 int type = COSE_ES256;
132 int flags = 0;
133 int ch;
134 int r;
135
136 while ((ch = getopt(argc, argv, "dhi:o:qruv")) != -1) {
137 switch (ch) {
138 case 'd':
139 flags |= FLAG_DEBUG;
140 break;
141 case 'h':
142 flags |= FLAG_HMAC;
143 break;
144 case 'i':
145 in_path = optarg;
146 break;
147 case 'o':
148 out_path = optarg;
149 break;
150 case 'q':
151 flags |= FLAG_QUIET;
152 break;
153 case 'r':
154 flags |= FLAG_RK;
155 break;
156 case 'u':
157 flags |= FLAG_U2F;
158 break;
159 case 'v':
160 flags |= FLAG_UV;
161 break;
162 default:
163 usage();
164 }
165 }
166
167 argc -= optind;
168 argv += optind;
169
170 if (argc < 1 || argc > 2)
171 usage();
172
173 in_f = open_read(in_path);
174 out_f = open_write(out_path);
175
176 if (argc > 1) {
177 if (strcmp(argv[1], "es256") == 0)
178 type = COSE_ES256;
179 else if (strcmp(argv[1], "rs256") == 0)
180 type = COSE_RS256;
181 else if (strcmp(argv[1], "eddsa") == 0)
182 type = COSE_EDDSA;
183 else
184 errx(1, "unknown type %s", argv[1]);
185 }
186
187 fido_init((flags & FLAG_DEBUG) ? FIDO_DEBUG : 0);
188
189 cred = prepare_cred(in_f, type, flags);
190
191 dev = open_dev(argv[0]);
192 if (flags & FLAG_U2F)
193 fido_dev_force_u2f(dev);
194
195 r = fido_dev_make_cred(dev, cred, NULL);
196 if (r == FIDO_ERR_PIN_REQUIRED && !(flags & FLAG_QUIET)) {
197 r = snprintf(prompt, sizeof(prompt), "Enter PIN for %s: ",
198 argv[0]);
199 if (r < 0 || (size_t)r >= sizeof(prompt))
200 errx(1, "snprintf");
201 if (!readpassphrase(prompt, pin, sizeof(pin), RPP_ECHO_OFF))
202 errx(1, "readpassphrase");
203 r = fido_dev_make_cred(dev, cred, pin);
204 }
205
206 explicit_bzero(pin, sizeof(pin));
207 if (r != FIDO_OK)
208 errx(1, "fido_dev_make_cred: %s", fido_strerr(r));
209 print_attcred(out_f, cred);
210
211 fido_dev_close(dev);
212 fido_dev_free(&dev);
213 fido_cred_free(&cred);
214
215 fclose(in_f);
216 fclose(out_f);
217 in_f = NULL;
218 out_f = NULL;
219
220 exit(0);
221}
diff --git a/tools/cred_verify.c b/tools/cred_verify.c
new file mode 100644
index 0000000..3f7a400
--- /dev/null
+++ b/tools/cred_verify.c
@@ -0,0 +1,177 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <fido.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#ifdef HAVE_UNISTD_H
12#include <unistd.h>
13#endif
14
15#include "../openbsd-compat/openbsd-compat.h"
16#include "extern.h"
17
18static fido_cred_t *
19prepare_cred(FILE *in_f, int type, int flags)
20{
21 fido_cred_t *cred = NULL;
22 struct blob cdh;
23 struct blob authdata;
24 struct blob id;
25 struct blob sig;
26 struct blob x5c;
27 char *rpid = NULL;
28 char *fmt = NULL;
29 int r;
30
31 memset(&cdh, 0, sizeof(cdh));
32 memset(&authdata, 0, sizeof(authdata));
33 memset(&id, 0, sizeof(id));
34 memset(&sig, 0, sizeof(sig));
35 memset(&x5c, 0, sizeof(x5c));
36
37 r = base64_read(in_f, &cdh);
38 r |= string_read(in_f, &rpid);
39 r |= string_read(in_f, &fmt);
40 r |= base64_read(in_f, &authdata);
41 r |= base64_read(in_f, &id);
42 r |= base64_read(in_f, &sig);
43 if (r < 0)
44 errx(1, "input error");
45
46 (void)base64_read(in_f, &x5c);
47
48 if (flags & FLAG_DEBUG) {
49 fprintf(stderr, "client data hash:\n");
50 xxd(cdh.ptr, cdh.len);
51 fprintf(stderr, "relying party id: %s\n", rpid);
52 fprintf(stderr, "format: %s\n", fmt);
53 fprintf(stderr, "authenticator data:\n");
54 xxd(authdata.ptr, authdata.len);
55 fprintf(stderr, "credential id:\n");
56 xxd(id.ptr, id.len);
57 fprintf(stderr, "signature:\n");
58 xxd(sig.ptr, sig.len);
59 fprintf(stderr, "x509:\n");
60 xxd(x5c.ptr, x5c.len);
61 }
62
63 if ((cred = fido_cred_new()) == NULL)
64 errx(1, "fido_cred_new");
65
66 if ((r = fido_cred_set_type(cred, type)) != FIDO_OK ||
67 (r = fido_cred_set_clientdata_hash(cred, cdh.ptr,
68 cdh.len)) != FIDO_OK ||
69 (r = fido_cred_set_rp(cred, rpid, NULL)) != FIDO_OK ||
70 (r = fido_cred_set_authdata(cred, authdata.ptr,
71 authdata.len)) != FIDO_OK ||
72 (r = fido_cred_set_sig(cred, sig.ptr, sig.len)) != FIDO_OK ||
73 (r = fido_cred_set_fmt(cred, fmt)) != FIDO_OK)
74 errx(1, "fido_cred_set: %s", fido_strerr(r));
75
76 if (x5c.ptr != NULL) {
77 if ((r = fido_cred_set_x509(cred, x5c.ptr, x5c.len)) != FIDO_OK)
78 errx(1, "fido_cred_set_x509: %s", fido_strerr(r));
79 }
80
81 if (flags & FLAG_UV) {
82 if ((r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK)
83 errx(1, "fido_cred_set_uv: %s", fido_strerr(r));
84 }
85 if (flags & FLAG_HMAC) {
86 if ((r = fido_cred_set_extensions(cred,
87 FIDO_EXT_HMAC_SECRET)) != FIDO_OK)
88 errx(1, "fido_cred_set_extensions: %s", fido_strerr(r));
89 }
90
91 free(cdh.ptr);
92 free(authdata.ptr);
93 free(id.ptr);
94 free(sig.ptr);
95 free(x5c.ptr);
96 free(rpid);
97 free(fmt);
98
99 return (cred);
100}
101
102int
103cred_verify(int argc, char **argv)
104{
105 fido_cred_t *cred = NULL;
106 char *in_path = NULL;
107 char *out_path = NULL;
108 FILE *in_f = NULL;
109 FILE *out_f = NULL;
110 int type = COSE_ES256;
111 int flags = 0;
112 int ch;
113 int r;
114
115 while ((ch = getopt(argc, argv, "dhi:o:v")) != -1) {
116 switch (ch) {
117 case 'd':
118 flags |= FLAG_DEBUG;
119 break;
120 case 'h':
121 flags |= FLAG_HMAC;
122 break;
123 case 'i':
124 in_path = optarg;
125 break;
126 case 'o':
127 out_path = optarg;
128 break;
129 case 'v':
130 flags |= FLAG_UV;
131 break;
132 default:
133 usage();
134 }
135 }
136
137 argc -= optind;
138 argv += optind;
139
140 if (argc > 1)
141 usage();
142
143 in_f = open_read(in_path);
144 out_f = open_write(out_path);
145
146 if (argc > 0) {
147 if (strcmp(argv[0], "es256") == 0)
148 type = COSE_ES256;
149 else if (strcmp(argv[0], "rs256") == 0)
150 type = COSE_RS256;
151 else if (strcmp(argv[0], "eddsa") == 0)
152 type = COSE_EDDSA;
153 else
154 errx(1, "unknown type %s", argv[0]);
155 }
156
157 fido_init((flags & FLAG_DEBUG) ? FIDO_DEBUG : 0);
158 cred = prepare_cred(in_f, type, flags);
159
160 if (fido_cred_x5c_ptr(cred) == NULL) {
161 if ((r = fido_cred_verify_self(cred)) != FIDO_OK)
162 errx(1, "fido_cred_verify_self: %s", fido_strerr(r));
163 } else {
164 if ((r = fido_cred_verify(cred)) != FIDO_OK)
165 errx(1, "fido_cred_verify: %s", fido_strerr(r));
166 }
167
168 print_cred(out_f, type, cred);
169 fido_cred_free(&cred);
170
171 fclose(in_f);
172 fclose(out_f);
173 in_f = NULL;
174 out_f = NULL;
175
176 exit(0);
177}
diff --git a/tools/credman.c b/tools/credman.c
new file mode 100644
index 0000000..08c9eb8
--- /dev/null
+++ b/tools/credman.c
@@ -0,0 +1,237 @@
1/*
2 * Copyright (c) 2019 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <fido.h>
8#include <fido/credman.h>
9
10#include <stdbool.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#ifdef HAVE_UNISTD_H
15#include <unistd.h>
16#endif
17
18#include "../openbsd-compat/openbsd-compat.h"
19#include "extern.h"
20
21int
22credman_get_metadata(fido_dev_t *dev, const char *path)
23{
24 fido_credman_metadata_t *metadata = NULL;
25 char pin[1024];
26 int r;
27
28 if ((metadata = fido_credman_metadata_new()) == NULL)
29 errx(1, "fido_credman_metadata_new");
30
31 read_pin(path, pin, sizeof(pin));
32 r = fido_credman_get_dev_metadata(dev, metadata, pin);
33 explicit_bzero(pin, sizeof(pin));
34
35 if (r != FIDO_OK)
36 errx(1, "fido_credman_get_dev_metadata: %s", fido_strerr(r));
37
38 printf("existing rk(s): %u\n",
39 (unsigned)fido_credman_rk_existing(metadata));
40 printf("possible rk(s): %u\n",
41 (unsigned)fido_credman_rk_remaining(metadata));
42
43 fido_credman_metadata_free(&metadata);
44 fido_dev_close(dev);
45 fido_dev_free(&dev);
46
47 exit(0);
48}
49
50static void
51print_rp(fido_credman_rp_t *rp, size_t idx)
52{
53 char *rp_id_hash = NULL;
54
55 if (base64_encode(fido_credman_rp_id_hash_ptr(rp, idx),
56 fido_credman_rp_id_hash_len(rp, idx), &rp_id_hash) < 0)
57 errx(1, "output error");
58
59 printf("%02u: %s %s\n", (unsigned)idx, rp_id_hash,
60 fido_credman_rp_id(rp, idx));
61
62 free(rp_id_hash);
63 rp_id_hash = NULL;
64}
65
66int
67credman_list_rp(char *path)
68{
69 fido_dev_t *dev = NULL;
70 fido_credman_rp_t *rp = NULL;
71 char pin[1024];
72 int r;
73
74 if (path == NULL)
75 usage();
76 if ((rp = fido_credman_rp_new()) == NULL)
77 errx(1, "fido_credman_rp_new");
78
79 dev = open_dev(path);
80 read_pin(path, pin, sizeof(pin));
81 r = fido_credman_get_dev_rp(dev, rp, pin);
82 explicit_bzero(pin, sizeof(pin));
83
84 if (r != FIDO_OK)
85 errx(1, "fido_credman_get_dev_rp: %s", fido_strerr(r));
86
87 for (size_t i = 0; i < fido_credman_rp_count(rp); i++)
88 print_rp(rp, i);
89
90 fido_credman_rp_free(&rp);
91 fido_dev_close(dev);
92 fido_dev_free(&dev);
93
94 exit(0);
95}
96
97static void
98print_rk(const fido_credman_rk_t *rk, size_t idx)
99{
100 const fido_cred_t *cred;
101 char *id = NULL;
102 char *user_id = NULL;
103 const char *type;
104
105 if ((cred = fido_credman_rk(rk, idx)) == NULL)
106 errx(1, "fido_credman_rk");
107 if (base64_encode(fido_cred_id_ptr(cred), fido_cred_id_len(cred),
108 &id) < 0 || base64_encode(fido_cred_user_id_ptr(cred),
109 fido_cred_user_id_len(cred), &user_id) < 0)
110 errx(1, "output error");
111
112 switch (fido_cred_type(cred)) {
113 case COSE_EDDSA:
114 type = "eddsa";
115 break;
116 case COSE_ES256:
117 type = "es256";
118 break;
119 case COSE_RS256:
120 type = "rs256";
121 break;
122 default:
123 type = "unknown";
124 break;
125 }
126
127 printf("%02u: %s %s (%s) %s\n", (unsigned)idx, id,
128 fido_cred_display_name(cred), user_id, type);
129
130 free(user_id);
131 free(id);
132 user_id = NULL;
133 id = NULL;
134}
135
136int
137credman_list_rk(char *path, const char *rp_id)
138{
139 fido_dev_t *dev = NULL;
140 fido_credman_rk_t *rk = NULL;
141 char pin[1024];
142 int r;
143
144 if (path == NULL)
145 usage();
146 if ((rk = fido_credman_rk_new()) == NULL)
147 errx(1, "fido_credman_rk_new");
148
149 dev = open_dev(path);
150 read_pin(path, pin, sizeof(pin));
151 r = fido_credman_get_dev_rk(dev, rp_id, rk, pin);
152 explicit_bzero(pin, sizeof(pin));
153
154 if (r != FIDO_OK)
155 errx(1, "fido_credman_get_dev_rk: %s", fido_strerr(r));
156 for (size_t i = 0; i < fido_credman_rk_count(rk); i++)
157 print_rk(rk, i);
158
159 fido_credman_rk_free(&rk);
160 fido_dev_close(dev);
161 fido_dev_free(&dev);
162
163 exit(0);
164}
165
166int
167credman_print_rk(fido_dev_t *dev, const char *path, char *rp_id, char *cred_id)
168{
169 const fido_cred_t *cred = NULL;
170 fido_credman_rk_t *rk = NULL;
171 char pin[1024];
172 void *cred_id_ptr = NULL;
173 size_t cred_id_len = 0;
174 int r;
175
176 if ((rk = fido_credman_rk_new()) == NULL)
177 errx(1, "fido_credman_rk_new");
178 if (base64_decode(cred_id, &cred_id_ptr, &cred_id_len) < 0)
179 errx(1, "base64_decode");
180
181 read_pin(path, pin, sizeof(pin));
182 r = fido_credman_get_dev_rk(dev, rp_id, rk, pin);
183 explicit_bzero(pin, sizeof(pin));
184
185 if (r != FIDO_OK)
186 errx(1, "fido_credman_get_dev_rk: %s", fido_strerr(r));
187
188 for (size_t i = 0; i < fido_credman_rk_count(rk); i++) {
189 if ((cred = fido_credman_rk(rk, i)) == NULL ||
190 fido_cred_id_ptr(cred) == NULL)
191 errx(1, "output error");
192 if (cred_id_len != fido_cred_id_len(cred) ||
193 memcmp(cred_id_ptr, fido_cred_id_ptr(cred), cred_id_len))
194 continue;
195 print_cred(stdout, fido_cred_type(cred), cred);
196 goto out;
197 }
198
199 errx(1, "credential not found");
200
201out:
202 free(cred_id_ptr);
203 cred_id_ptr = NULL;
204
205 fido_credman_rk_free(&rk);
206 fido_dev_close(dev);
207 fido_dev_free(&dev);
208
209 exit(0);
210}
211
212int
213credman_delete_rk(fido_dev_t *dev, const char *path, char *id)
214{
215 char pin[1024];
216 void *id_ptr = NULL;
217 size_t id_len = 0;
218 int r;
219
220 if (base64_decode(id, &id_ptr, &id_len) < 0)
221 errx(1, "base64_decode");
222
223 read_pin(path, pin, sizeof(pin));
224 r = fido_credman_del_dev_rk(dev, id_ptr, id_len, pin);
225 explicit_bzero(pin, sizeof(pin));
226
227 if (r != FIDO_OK)
228 errx(1, "fido_credman_del_dev_rk: %s", fido_strerr(r));
229
230 free(id_ptr);
231 id_ptr = NULL;
232
233 fido_dev_close(dev);
234 fido_dev_free(&dev);
235
236 exit(0);
237}
diff --git a/tools/extern.h b/tools/extern.h
new file mode 100644
index 0000000..e79e6f0
--- /dev/null
+++ b/tools/extern.h
@@ -0,0 +1,64 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#ifndef _EXTERN_H_
8#define _EXTERN_H_
9
10struct blob {
11 unsigned char *ptr;
12 size_t len;
13};
14
15#define TOKEN_OPT "CDILPRSVbcdei:k:n:r"
16
17#define FLAG_DEBUG 0x01
18#define FLAG_QUIET 0x02
19#define FLAG_RK 0x04
20#define FLAG_UV 0x08
21#define FLAG_U2F 0x10
22#define FLAG_HMAC 0x20
23#define FLAG_UP 0x40
24
25EC_KEY *read_ec_pubkey(const char *);
26fido_dev_t *open_dev(const char *);
27FILE *open_read(const char *);
28FILE *open_write(const char *);
29int assert_get(int, char **);
30int assert_verify(int, char **);
31int base64_decode(char *, void **, size_t *);
32int base64_encode(const void *, size_t, char **);
33int base64_read(FILE *, struct blob *);
34int bio_delete(fido_dev_t *, char *, char *);
35int bio_enroll(char *);
36void bio_info(fido_dev_t *);
37int bio_list(char *);
38int bio_set_name(char *, char *, char *);
39int cred_make(int, char **);
40int cred_verify(int, char **);
41int credman_delete_rk(fido_dev_t *, const char *, char *);
42int credman_get_metadata(fido_dev_t *, const char *);
43int credman_list_rk(char *, const char *);
44int credman_list_rp(char *);
45int credman_print_rk(fido_dev_t *, const char *, char *, char *);
46int pin_change(char *);
47int pin_set(char *);
48int string_read(FILE *, char **);
49int token_delete(int, char **, char *);
50int token_info(int, char **, char *);
51int token_list(int, char **, char *);
52int token_reset(char *);
53int token_set(int, char **, char *);
54int write_ec_pubkey(FILE *, const void *, size_t);
55int write_rsa_pubkey(FILE *, const void *, size_t);
56RSA *read_rsa_pubkey(const char *);
57EVP_PKEY *read_eddsa_pubkey(const char *);
58int write_eddsa_pubkey(FILE *, const void *, size_t);
59void print_cred(FILE *, int, const fido_cred_t *);
60void read_pin(const char *, char *, size_t);
61void usage(void);
62void xxd(const void *, size_t);
63
64#endif /* _EXTERN_H_ */
diff --git a/tools/fido2-assert.c b/tools/fido2-assert.c
new file mode 100644
index 0000000..9ce537a
--- /dev/null
+++ b/tools/fido2-assert.c
@@ -0,0 +1,54 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7/*
8 * Example usage:
9 *
10 * $ echo assertion challenge | openssl sha256 -binary | base64 > assert_param
11 * $ echo relying party >> assert_param
12 * $ head -1 cred >> assert_param # credential id
13 * $ tail -n +2 cred > pubkey # credential pubkey
14 * $ fido2-assert -G -i assert_param /dev/hidraw5 | fido2-assert -V pubkey rs256
15 *
16 * See blurb in fido2-cred.c on how to obtain cred.
17 */
18
19#include <fido.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23
24#include "../openbsd-compat/openbsd-compat.h"
25#include "extern.h"
26
27void
28usage(void)
29{
30 fprintf(stderr,
31"usage: fido2-assert -G [-dhpruv] [-i input_file] [-o output_file] device\n"
32" fido2-assert -V [-dhpv] [-i input_file] key_file [type]\n"
33 );
34
35 exit(1);
36}
37
38int
39main(int argc, char **argv)
40{
41 if (argc < 2 || strlen(argv[1]) != 2 || argv[1][0] != '-')
42 usage();
43
44 switch (argv[1][1]) {
45 case 'G':
46 return (assert_get(--argc, ++argv));
47 case 'V':
48 return (assert_verify(--argc, ++argv));
49 }
50
51 usage();
52
53 /* NOTREACHED */
54}
diff --git a/tools/fido2-cred.c b/tools/fido2-cred.c
new file mode 100644
index 0000000..45efca0
--- /dev/null
+++ b/tools/fido2-cred.c
@@ -0,0 +1,52 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7/*
8 * Example usage:
9 *
10 * $ echo credential challenge | openssl sha256 -binary | base64 > cred_param
11 * $ echo relying party >> cred_param
12 * $ echo user name >> cred_param
13 * $ dd if=/dev/urandom bs=1 count=32 | base64 >> cred_param
14 * $ fido2-cred -M -i cred_param /dev/hidraw5 | fido2-cred -V -o cred
15 */
16
17#include <fido.h>
18#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21
22#include "../openbsd-compat/openbsd-compat.h"
23#include "extern.h"
24
25void
26usage(void)
27{
28 fprintf(stderr,
29"usage: fido2-cred -M [-dhqruv] [-i input_file] [-o output_file] device [type]\n"
30" fido2-cred -V [-dhv] [-i input_file] [-o output_file] [type]\n"
31 );
32
33 exit(1);
34}
35
36int
37main(int argc, char **argv)
38{
39 if (argc < 2 || strlen(argv[1]) != 2 || argv[1][0] != '-')
40 usage();
41
42 switch (argv[1][1]) {
43 case 'M':
44 return (cred_make(--argc, ++argv));
45 case 'V':
46 return (cred_verify(--argc, ++argv));
47 }
48
49 usage();
50
51 /* NOTREACHED */
52}
diff --git a/tools/fido2-token.c b/tools/fido2-token.c
new file mode 100644
index 0000000..0b02fea
--- /dev/null
+++ b/tools/fido2-token.c
@@ -0,0 +1,95 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <fido.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11
12#include "../openbsd-compat/openbsd-compat.h"
13#include "extern.h"
14
15static int action;
16
17void
18usage(void)
19{
20 fprintf(stderr,
21"usage: fido2-token [-CR] [-d] device\n"
22" fido2-token -D [-de] -i id device\n"
23" fido2-token -I [-cd] [-k rp_id -i cred_id] device\n"
24" fido2-token -L [-der] [-k rp_id] [device]\n"
25" fido2-token -S [-de] [-i template_id -n template_name] device\n"
26" fido2-token -V\n"
27 );
28
29 exit(1);
30}
31
32static void
33setaction(int ch)
34{
35 if (action)
36 usage();
37 action = ch;
38}
39
40int
41main(int argc, char **argv)
42{
43 int ch;
44 int flags = 0;
45 char *device;
46
47 while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
48 switch (ch) {
49 case 'b':
50 case 'c':
51 case 'e':
52 case 'i':
53 case 'k':
54 case 'n':
55 case 'r':
56 break; /* ignore */
57 case 'd':
58 flags = FIDO_DEBUG;
59 break;
60 default:
61 setaction(ch);
62 break;
63 }
64 }
65
66 if (argc - optind == 1)
67 device = argv[optind];
68 else
69 device = NULL;
70
71 fido_init(flags);
72
73 switch (action) {
74 case 'C':
75 return (pin_change(device));
76 case 'D':
77 return (token_delete(argc, argv, device));
78 case 'I':
79 return (token_info(argc, argv, device));
80 case 'L':
81 return (token_list(argc, argv, device));
82 case 'R':
83 return (token_reset(device));
84 case 'S':
85 return (token_set(argc, argv, device));
86 case 'V':
87 fprintf(stderr, "%d.%d.%d\n", _FIDO_MAJOR, _FIDO_MINOR,
88 _FIDO_PATCH);
89 exit(0);
90 }
91
92 usage();
93
94 /* NOTREACHED */
95}
diff --git a/tools/pin.c b/tools/pin.c
new file mode 100644
index 0000000..eab178a
--- /dev/null
+++ b/tools/pin.c
@@ -0,0 +1,146 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <fido.h>
8#include <stdbool.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#ifdef HAVE_UNISTD_H
13#include <unistd.h>
14#endif
15
16#include "../openbsd-compat/openbsd-compat.h"
17#include "extern.h"
18
19int
20pin_set(char *path)
21{
22 fido_dev_t *dev = NULL;
23 char prompt[1024];
24 char pin1[1024];
25 char pin2[1024];
26 int r;
27 int status = 1;
28
29 if (path == NULL)
30 usage();
31
32 dev = open_dev(path);
33
34 r = snprintf(prompt, sizeof(prompt), "Enter new PIN for %s: ", path);
35 if (r < 0 || (size_t)r >= sizeof(prompt)) {
36 warnx("snprintf");
37 goto out;
38 }
39
40 if (!readpassphrase(prompt, pin1, sizeof(pin1), RPP_ECHO_OFF)) {
41 warnx("readpassphrase");
42 goto out;
43 }
44
45 r = snprintf(prompt, sizeof(prompt), "Enter the same PIN again: ");
46 if (r < 0 || (size_t)r >= sizeof(prompt)) {
47 warnx("snprintf");
48 goto out;
49 }
50
51 if (!readpassphrase(prompt, pin2, sizeof(pin2), RPP_ECHO_OFF)) {
52 warnx("readpassphrase");
53 goto out;
54 }
55
56 if (strcmp(pin1, pin2) != 0) {
57 fprintf(stderr, "PINs do not match. Try again.\n");
58 goto out;
59 }
60
61 if ((r = fido_dev_set_pin(dev, pin1, NULL)) != FIDO_OK) {
62 warnx("fido_dev_set_pin: %s", fido_strerr(r));
63 goto out;
64 }
65
66 fido_dev_close(dev);
67 fido_dev_free(&dev);
68
69 status = 0;
70out:
71 explicit_bzero(pin1, sizeof(pin1));
72 explicit_bzero(pin2, sizeof(pin2));
73
74 exit(status);
75}
76
77int
78pin_change(char *path)
79{
80 fido_dev_t *dev = NULL;
81 char prompt[1024];
82 char pin0[1024];
83 char pin1[1024];
84 char pin2[1024];
85 int r;
86 int status = 1;
87
88 if (path == NULL)
89 usage();
90
91 dev = open_dev(path);
92
93 r = snprintf(prompt, sizeof(prompt), "Enter current PIN for %s: ", path);
94 if (r < 0 || (size_t)r >= sizeof(prompt)) {
95 warnx("snprintf");
96 goto out;
97 }
98
99 if (!readpassphrase(prompt, pin0, sizeof(pin0), RPP_ECHO_OFF)) {
100 warnx("readpassphrase");
101 goto out;
102 }
103
104 r = snprintf(prompt, sizeof(prompt), "Enter new PIN for %s: ", path);
105 if (r < 0 || (size_t)r >= sizeof(prompt)) {
106 warnx("snprintf");
107 goto out;
108 }
109
110 if (!readpassphrase(prompt, pin1, sizeof(pin1), RPP_ECHO_OFF)) {
111 warnx("readpassphrase");
112 goto out;
113 }
114
115 r = snprintf(prompt, sizeof(prompt), "Enter the same PIN again: ");
116 if (r < 0 || (size_t)r >= sizeof(prompt)) {
117 warnx("snprintf");
118 goto out;
119 }
120
121 if (!readpassphrase(prompt, pin2, sizeof(pin2), RPP_ECHO_OFF)) {
122 warnx("readpassphrase");
123 goto out;
124 }
125
126 if (strcmp(pin1, pin2) != 0) {
127 fprintf(stderr, "PINs do not match. Try again.\n");
128 goto out;
129 }
130
131 if ((r = fido_dev_set_pin(dev, pin1, pin0)) != FIDO_OK) {
132 warnx("fido_dev_set_pin: %s", fido_strerr(r));
133 goto out;
134 }
135
136 fido_dev_close(dev);
137 fido_dev_free(&dev);
138
139 status = 0;
140out:
141 explicit_bzero(pin0, sizeof(pin0));
142 explicit_bzero(pin1, sizeof(pin1));
143 explicit_bzero(pin2, sizeof(pin2));
144
145 exit(status);
146}
diff --git a/tools/sk-libfido2.c b/tools/sk-libfido2.c
new file mode 100644
index 0000000..15aa813
--- /dev/null
+++ b/tools/sk-libfido2.c
@@ -0,0 +1,784 @@
1/*
2 * Copyright (c) 2019 Markus Friedl
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include <fcntl.h>
18#include <stdint.h>
19#include <stdlib.h>
20#include <string.h>
21#include <stdio.h>
22#include <stddef.h>
23#include <stdarg.h>
24#ifdef HAVE_UNISTD_H
25#include <unistd.h>
26#endif
27
28#ifdef WITH_OPENSSL
29#include <openssl/opensslv.h>
30#include <openssl/crypto.h>
31#include <openssl/bn.h>
32#include <openssl/ec.h>
33#include <openssl/ecdsa.h>
34#endif /* WITH_OPENSSL */
35
36#include <fido.h>
37
38#ifndef SK_STANDALONE
39#include "log.h"
40#include "xmalloc.h"
41#endif
42
43/* #define SK_DEBUG 1 */
44
45#if defined(_WIN32)
46#include <windows.h>
47#include <winternl.h>
48#include <winerror.h>
49#include <bcrypt.h>
50#include <sal.h>
51#endif
52
53#define MAX_FIDO_DEVICES 256
54
55/* Compatibility with OpenSSL 1.0.x */
56#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
57#define ECDSA_SIG_get0(sig, pr, ps) \
58 do { \
59 (*pr) = sig->r; \
60 (*ps) = sig->s; \
61 } while (0)
62#endif
63
64#define SK_VERSION_MAJOR 0x00020000 /* current API version */
65
66/* Flags */
67#define SK_USER_PRESENCE_REQD 0x01
68
69/* Algs */
70#define SK_ECDSA 0x00
71#define SK_ED25519 0x01
72
73struct sk_enroll_response {
74 uint8_t *public_key;
75 size_t public_key_len;
76 uint8_t *key_handle;
77 size_t key_handle_len;
78 uint8_t *signature;
79 size_t signature_len;
80 uint8_t *attestation_cert;
81 size_t attestation_cert_len;
82};
83
84struct sk_sign_response {
85 uint8_t flags;
86 uint32_t counter;
87 uint8_t *sig_r;
88 size_t sig_r_len;
89 uint8_t *sig_s;
90 size_t sig_s_len;
91};
92
93/* If building as part of OpenSSH, then rename exported functions */
94#if !defined(SK_STANDALONE)
95#define sk_api_version ssh_sk_api_version
96#define sk_enroll ssh_sk_enroll
97#define sk_sign ssh_sk_sign
98#endif
99
100/* Return the version of the middleware API */
101uint32_t sk_api_version(void);
102
103/* Enroll a U2F key (private key generation) */
104int sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len,
105 const char *application, uint8_t flags,
106 struct sk_enroll_response **enroll_response);
107
108/* Sign a challenge */
109int sk_sign(int alg, const uint8_t *message, size_t message_len,
110 const char *application, const uint8_t *key_handle, size_t key_handle_len,
111 uint8_t flags, struct sk_sign_response **sign_response);
112
113#ifdef SK_DEBUG
114static void skdebug(const char *func, const char *fmt, ...)
115 __attribute__((__format__ (printf, 2, 3)));
116
117static void
118skdebug(const char *func, const char *fmt, ...)
119{
120#if !defined(SK_STANDALONE)
121 char *msg;
122 va_list ap;
123
124 va_start(ap, fmt);
125 xvasprintf(&msg, fmt, ap);
126 va_end(ap);
127 debug("%s: %s", func, msg);
128 free(msg);
129#else
130 va_list ap;
131
132 va_start(ap, fmt);
133 fprintf(stderr, "%s: ", func);
134 vfprintf(stderr, fmt, ap);
135 fputc('\n', stderr);
136 va_end(ap);
137#endif /* !SK_STANDALONE */
138}
139#else
140#define skdebug(...) do { /* nothing */ } while (0)
141#endif /* SK_DEBUG */
142
143uint32_t
144sk_api_version(void)
145{
146 return SK_VERSION_MAJOR;
147}
148
149/* Select the first identified FIDO device attached to the system */
150static char *
151pick_first_device(void)
152{
153 char *ret = NULL;
154 fido_dev_info_t *devlist = NULL;
155 size_t olen = 0;
156 int r;
157 const fido_dev_info_t *di;
158
159 if ((devlist = fido_dev_info_new(1)) == NULL) {
160 skdebug(__func__, "fido_dev_info_new failed");
161 goto out;
162 }
163 if ((r = fido_dev_info_manifest(devlist, 1, &olen)) != FIDO_OK) {
164 skdebug(__func__, "fido_dev_info_manifest failed: %s",
165 fido_strerr(r));
166 goto out;
167 }
168 if (olen != 1) {
169 skdebug(__func__, "fido_dev_info_manifest bad len %zu", olen);
170 goto out;
171 }
172 di = fido_dev_info_ptr(devlist, 0);
173 if ((ret = strdup(fido_dev_info_path(di))) == NULL) {
174 skdebug(__func__, "fido_dev_info_path failed");
175 goto out;
176 }
177 out:
178 fido_dev_info_free(&devlist, 1);
179 return ret;
180}
181
182#if defined(HAVE_ARC4RANDOM_BUF)
183static int
184get_random_challenge(uint8_t *ptr, size_t len)
185{
186 arc4random_buf(ptr, len);
187
188 return 0;
189}
190#elif defined(HAVE_GETENTROPY)
191static int
192get_random_challenge(uint8_t *ptr, size_t len)
193{
194 if (getentropy(ptr, len) == -1) {
195 skdebug(__func__, "getentropy failed");
196 return -1;
197 }
198
199 return 0;
200}
201#elif defined(HAS_DEV_URANDOM)
202static int
203get_random_challenge(uint8_t *ptr, size_t len)
204{
205 int fd;
206 ssize_t n;
207
208 if ((fd = open(FIDO_RANDOM_DEV, O_RDONLY)) < 0) {
209 skdebug(__func__, "open %s failed", FIDO_RANDOM_DEV);
210 return -1;
211 }
212
213 n = read(fd, ptr, len);
214 close(fd);
215
216 if (n < 0 || (size_t)n != len) {
217 skdebug(__func__, "read from %s failed", FIDO_RANDOM_DEV);
218 return -1;
219 }
220
221 return 0;
222}
223#elif defined(_WIN32)
224static int
225get_random_challenge(uint8_t *ptr, size_t len)
226{
227 NTSTATUS status;
228
229 status = BCryptGenRandom(NULL, ptr, len,
230 BCRYPT_USE_SYSTEM_PREFERRED_RNG);
231 if (!NT_SUCCESS(status))
232 return -1;
233
234 return 0;
235}
236#else
237#error "please provide an implementation of get_random_challenge() for your platform"
238#endif
239
240/* Check if the specified key handle exists on a given device. */
241static int
242try_device(fido_dev_t *dev, const char *application,
243 const uint8_t *key_handle, size_t key_handle_len)
244{
245 fido_assert_t *assert = NULL;
246 uint8_t challenge[32];
247 int r = FIDO_ERR_INTERNAL;
248
249 if (get_random_challenge(challenge, sizeof(challenge)) == -1) {
250 skdebug(__func__, "get_random_challenge failed");
251 goto out;
252 }
253
254 if ((assert = fido_assert_new()) == NULL) {
255 skdebug(__func__, "fido_assert_new failed");
256 goto out;
257 }
258 if ((r = fido_assert_set_clientdata_hash(assert, challenge,
259 sizeof(challenge))) != FIDO_OK) {
260 skdebug(__func__, "fido_assert_set_clientdata_hash: %s",
261 fido_strerr(r));
262 goto out;
263 }
264 if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
265 skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
266 goto out;
267 }
268 if ((r = fido_assert_allow_cred(assert, key_handle,
269 key_handle_len)) != FIDO_OK) {
270 skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
271 goto out;
272 }
273 if ((r = fido_assert_set_up(assert, FIDO_OPT_FALSE)) != FIDO_OK) {
274 skdebug(__func__, "fido_assert_up: %s", fido_strerr(r));
275 goto out;
276 }
277 r = fido_dev_get_assert(dev, assert, NULL);
278 skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
279 if (r == FIDO_ERR_USER_PRESENCE_REQUIRED) {
280 /* U2F tokens may return this */
281 r = FIDO_OK;
282 }
283 out:
284 fido_assert_free(&assert);
285
286 return r != FIDO_OK ? -1 : 0;
287}
288
289/* Iterate over configured devices looking for a specific key handle */
290static fido_dev_t *
291find_device(const char *application, const uint8_t *key_handle,
292 size_t key_handle_len)
293{
294 fido_dev_info_t *devlist = NULL;
295 fido_dev_t *dev = NULL;
296 size_t devlist_len = 0, i;
297 const char *path;
298 int r;
299
300 if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) {
301 skdebug(__func__, "fido_dev_info_new failed");
302 goto out;
303 }
304 if ((r = fido_dev_info_manifest(devlist, MAX_FIDO_DEVICES,
305 &devlist_len)) != FIDO_OK) {
306 skdebug(__func__, "fido_dev_info_manifest: %s", fido_strerr(r));
307 goto out;
308 }
309
310 skdebug(__func__, "found %zu device(s)", devlist_len);
311
312 for (i = 0; i < devlist_len; i++) {
313 const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i);
314
315 if (di == NULL) {
316 skdebug(__func__, "fido_dev_info_ptr %zu failed", i);
317 continue;
318 }
319 if ((path = fido_dev_info_path(di)) == NULL) {
320 skdebug(__func__, "fido_dev_info_path %zu failed", i);
321 continue;
322 }
323 skdebug(__func__, "trying device %zu: %s", i, path);
324 if ((dev = fido_dev_new()) == NULL) {
325 skdebug(__func__, "fido_dev_new failed");
326 continue;
327 }
328 if ((r = fido_dev_open(dev, path)) != FIDO_OK) {
329 skdebug(__func__, "fido_dev_open failed");
330 fido_dev_free(&dev);
331 continue;
332 }
333 if (try_device(dev, application, key_handle,
334 key_handle_len) == 0) {
335 skdebug(__func__, "found key");
336 break;
337 }
338 fido_dev_close(dev);
339 fido_dev_free(&dev);
340 }
341
342 out:
343 if (devlist != NULL)
344 fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
345
346 return dev;
347}
348
349#ifdef WITH_OPENSSL
350/*
351 * The key returned via fido_cred_pubkey_ptr() is in affine coordinates,
352 * but the API expects a SEC1 octet string.
353 */
354static int
355pack_public_key_ecdsa(fido_cred_t *cred, struct sk_enroll_response *response)
356{
357 const uint8_t *ptr;
358 BIGNUM *x = NULL, *y = NULL;
359 EC_POINT *q = NULL;
360 EC_GROUP *g = NULL;
361 int ret = -1;
362
363 response->public_key = NULL;
364 response->public_key_len = 0;
365
366 if ((x = BN_new()) == NULL ||
367 (y = BN_new()) == NULL ||
368 (g = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)) == NULL ||
369 (q = EC_POINT_new(g)) == NULL) {
370 skdebug(__func__, "libcrypto setup failed");
371 goto out;
372 }
373 if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) {
374 skdebug(__func__, "fido_cred_pubkey_ptr failed");
375 goto out;
376 }
377 if (fido_cred_pubkey_len(cred) != 64) {
378 skdebug(__func__, "bad fido_cred_pubkey_len %zu",
379 fido_cred_pubkey_len(cred));
380 goto out;
381 }
382
383 if (BN_bin2bn(ptr, 32, x) == NULL ||
384 BN_bin2bn(ptr + 32, 32, y) == NULL) {
385 skdebug(__func__, "BN_bin2bn failed");
386 goto out;
387 }
388 if (EC_POINT_set_affine_coordinates_GFp(g, q, x, y, NULL) != 1) {
389 skdebug(__func__, "EC_POINT_set_affine_coordinates_GFp failed");
390 goto out;
391 }
392 response->public_key_len = EC_POINT_point2oct(g, q,
393 POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL);
394 if (response->public_key_len == 0 || response->public_key_len > 2048) {
395 skdebug(__func__, "bad pubkey length %zu",
396 response->public_key_len);
397 goto out;
398 }
399 if ((response->public_key = malloc(response->public_key_len)) == NULL) {
400 skdebug(__func__, "malloc pubkey failed");
401 goto out;
402 }
403 if (EC_POINT_point2oct(g, q, POINT_CONVERSION_UNCOMPRESSED,
404 response->public_key, response->public_key_len, NULL) == 0) {
405 skdebug(__func__, "EC_POINT_point2oct failed");
406 goto out;
407 }
408 /* success */
409 ret = 0;
410 out:
411 if (ret != 0 && response->public_key != NULL) {
412 memset(response->public_key, 0, response->public_key_len);
413 free(response->public_key);
414 response->public_key = NULL;
415 }
416 EC_POINT_free(q);
417 EC_GROUP_free(g);
418 BN_clear_free(x);
419 BN_clear_free(y);
420 return ret;
421}
422#endif /* WITH_OPENSSL */
423
424static int
425pack_public_key_ed25519(fido_cred_t *cred, struct sk_enroll_response *response)
426{
427 const uint8_t *ptr;
428 size_t len;
429 int ret = -1;
430
431 response->public_key = NULL;
432 response->public_key_len = 0;
433
434 if ((len = fido_cred_pubkey_len(cred)) != 32) {
435 skdebug(__func__, "bad fido_cred_pubkey_len len %zu", len);
436 goto out;
437 }
438 if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) {
439 skdebug(__func__, "fido_cred_pubkey_ptr failed");
440 goto out;
441 }
442 response->public_key_len = len;
443 if ((response->public_key = malloc(response->public_key_len)) == NULL) {
444 skdebug(__func__, "malloc pubkey failed");
445 goto out;
446 }
447 memcpy(response->public_key, ptr, len);
448 ret = 0;
449 out:
450 if (ret != 0)
451 free(response->public_key);
452 return ret;
453}
454
455static int
456pack_public_key(int alg, fido_cred_t *cred, struct sk_enroll_response *response)
457{
458 switch(alg) {
459#ifdef WITH_OPENSSL
460 case SK_ECDSA:
461 return pack_public_key_ecdsa(cred, response);
462#endif /* WITH_OPENSSL */
463 case SK_ED25519:
464 return pack_public_key_ed25519(cred, response);
465 default:
466 return -1;
467 }
468}
469
470int
471sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len,
472 const char *application, uint8_t flags,
473 struct sk_enroll_response **enroll_response)
474{
475 fido_cred_t *cred = NULL;
476 fido_dev_t *dev = NULL;
477 const uint8_t *ptr;
478 uint8_t user_id[32];
479 struct sk_enroll_response *response = NULL;
480 size_t len;
481 int cose_alg;
482 int ret = -1;
483 int r;
484 char *device = NULL;
485
486 (void)flags; /* XXX; unused */
487#ifdef SK_DEBUG
488 fido_init(FIDO_DEBUG);
489#endif
490 if (enroll_response == NULL) {
491 skdebug(__func__, "enroll_response == NULL");
492 goto out;
493 }
494 *enroll_response = NULL;
495 switch(alg) {
496#ifdef WITH_OPENSSL
497 case SK_ECDSA:
498 cose_alg = COSE_ES256;
499 break;
500#endif /* WITH_OPENSSL */
501 case SK_ED25519:
502 cose_alg = COSE_EDDSA;
503 break;
504 default:
505 skdebug(__func__, "unsupported key type %d", alg);
506 goto out;
507 }
508 if ((device = pick_first_device()) == NULL) {
509 skdebug(__func__, "pick_first_device failed");
510 goto out;
511 }
512 skdebug(__func__, "using device %s", device);
513 if ((cred = fido_cred_new()) == NULL) {
514 skdebug(__func__, "fido_cred_new failed");
515 goto out;
516 }
517 memset(user_id, 0, sizeof(user_id));
518 if ((r = fido_cred_set_type(cred, cose_alg)) != FIDO_OK) {
519 skdebug(__func__, "fido_cred_set_type: %s", fido_strerr(r));
520 goto out;
521 }
522 if ((r = fido_cred_set_clientdata_hash(cred, challenge,
523 challenge_len)) != FIDO_OK) {
524 skdebug(__func__, "fido_cred_set_clientdata_hash: %s",
525 fido_strerr(r));
526 goto out;
527 }
528 if ((r = fido_cred_set_user(cred, user_id, sizeof(user_id),
529 "openssh", "openssh", NULL)) != FIDO_OK) {
530 skdebug(__func__, "fido_cred_set_user: %s", fido_strerr(r));
531 goto out;
532 }
533 if ((r = fido_cred_set_rp(cred, application, NULL)) != FIDO_OK) {
534 skdebug(__func__, "fido_cred_set_rp: %s", fido_strerr(r));
535 goto out;
536 }
537 if ((dev = fido_dev_new()) == NULL) {
538 skdebug(__func__, "fido_dev_new failed");
539 goto out;
540 }
541 if ((r = fido_dev_open(dev, device)) != FIDO_OK) {
542 skdebug(__func__, "fido_dev_open: %s", fido_strerr(r));
543 goto out;
544 }
545 if ((r = fido_dev_make_cred(dev, cred, NULL)) != FIDO_OK) {
546 skdebug(__func__, "fido_dev_make_cred: %s", fido_strerr(r));
547 goto out;
548 }
549 if (fido_cred_x5c_ptr(cred) != NULL) {
550 if ((r = fido_cred_verify(cred)) != FIDO_OK) {
551 skdebug(__func__, "fido_cred_verify: %s",
552 fido_strerr(r));
553 goto out;
554 }
555 } else {
556 skdebug(__func__, "self-attested credential");
557 if ((r = fido_cred_verify_self(cred)) != FIDO_OK) {
558 skdebug(__func__, "fido_cred_verify_self: %s",
559 fido_strerr(r));
560 goto out;
561 }
562 }
563 if ((response = calloc(1, sizeof(*response))) == NULL) {
564 skdebug(__func__, "calloc response failed");
565 goto out;
566 }
567 if (pack_public_key(alg, cred, response) != 0) {
568 skdebug(__func__, "pack_public_key failed");
569 goto out;
570 }
571 if ((ptr = fido_cred_id_ptr(cred)) != NULL) {
572 len = fido_cred_id_len(cred);
573 if ((response->key_handle = calloc(1, len)) == NULL) {
574 skdebug(__func__, "calloc key handle failed");
575 goto out;
576 }
577 memcpy(response->key_handle, ptr, len);
578 response->key_handle_len = len;
579 }
580 if ((ptr = fido_cred_sig_ptr(cred)) != NULL) {
581 len = fido_cred_sig_len(cred);
582 if ((response->signature = calloc(1, len)) == NULL) {
583 skdebug(__func__, "calloc signature failed");
584 goto out;
585 }
586 memcpy(response->signature, ptr, len);
587 response->signature_len = len;
588 }
589 if ((ptr = fido_cred_x5c_ptr(cred)) != NULL) {
590 len = fido_cred_x5c_len(cred);
591 if ((response->attestation_cert = calloc(1, len)) == NULL) {
592 skdebug(__func__, "calloc attestation cert failed");
593 goto out;
594 }
595 memcpy(response->attestation_cert, ptr, len);
596 response->attestation_cert_len = len;
597 }
598 *enroll_response = response;
599 response = NULL;
600 ret = 0;
601 out:
602 free(device);
603 if (response != NULL) {
604 free(response->public_key);
605 free(response->key_handle);
606 free(response->signature);
607 free(response->attestation_cert);
608 free(response);
609 }
610 if (dev != NULL) {
611 fido_dev_close(dev);
612 fido_dev_free(&dev);
613 }
614 if (cred != NULL) {
615 fido_cred_free(&cred);
616 }
617 return ret;
618}
619
620#ifdef WITH_OPENSSL
621static int
622pack_sig_ecdsa(fido_assert_t *assert, struct sk_sign_response *response)
623{
624 ECDSA_SIG *sig = NULL;
625 const BIGNUM *sig_r, *sig_s;
626 const unsigned char *cp;
627 size_t sig_len;
628 int ret = -1;
629
630 cp = fido_assert_sig_ptr(assert, 0);
631 sig_len = fido_assert_sig_len(assert, 0);
632 if ((sig = d2i_ECDSA_SIG(NULL, &cp, sig_len)) == NULL) {
633 skdebug(__func__, "d2i_ECDSA_SIG failed");
634 goto out;
635 }
636 ECDSA_SIG_get0(sig, &sig_r, &sig_s);
637 response->sig_r_len = BN_num_bytes(sig_r);
638 response->sig_s_len = BN_num_bytes(sig_s);
639 if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL ||
640 (response->sig_s = calloc(1, response->sig_s_len)) == NULL) {
641 skdebug(__func__, "calloc signature failed");
642 goto out;
643 }
644 BN_bn2bin(sig_r, response->sig_r);
645 BN_bn2bin(sig_s, response->sig_s);
646 ret = 0;
647 out:
648 ECDSA_SIG_free(sig);
649 if (ret != 0) {
650 free(response->sig_r);
651 free(response->sig_s);
652 response->sig_r = NULL;
653 response->sig_s = NULL;
654 }
655 return ret;
656}
657#endif /* WITH_OPENSSL */
658
659static int
660pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response)
661{
662 const unsigned char *ptr;
663 size_t len;
664 int ret = -1;
665
666 ptr = fido_assert_sig_ptr(assert, 0);
667 len = fido_assert_sig_len(assert, 0);
668 if (len != 64) {
669 skdebug(__func__, "bad length %zu", len);
670 goto out;
671 }
672 response->sig_r_len = len;
673 if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) {
674 skdebug(__func__, "calloc signature failed");
675 goto out;
676 }
677 memcpy(response->sig_r, ptr, len);
678 ret = 0;
679 out:
680 if (ret != 0) {
681 free(response->sig_r);
682 response->sig_r = NULL;
683 }
684 return ret;
685}
686
687static int
688pack_sig(int alg, fido_assert_t *assert, struct sk_sign_response *response)
689{
690 switch(alg) {
691#ifdef WITH_OPENSSL
692 case SK_ECDSA:
693 return pack_sig_ecdsa(assert, response);
694#endif /* WITH_OPENSSL */
695 case SK_ED25519:
696 return pack_sig_ed25519(assert, response);
697 default:
698 return -1;
699 }
700}
701
702int
703sk_sign(int alg, const uint8_t *message, size_t message_len,
704 const char *application,
705 const uint8_t *key_handle, size_t key_handle_len,
706 uint8_t flags, struct sk_sign_response **sign_response)
707{
708 fido_assert_t *assert = NULL;
709 fido_dev_t *dev = NULL;
710 struct sk_sign_response *response = NULL;
711 int ret = -1;
712 int r;
713
714#ifdef SK_DEBUG
715 fido_init(FIDO_DEBUG);
716#endif
717
718 if (sign_response == NULL) {
719 skdebug(__func__, "sign_response == NULL");
720 goto out;
721 }
722 *sign_response = NULL;
723 if ((dev = find_device(application, key_handle,
724 key_handle_len)) == NULL) {
725 skdebug(__func__, "couldn't find device for key handle");
726 goto out;
727 }
728 if ((assert = fido_assert_new()) == NULL) {
729 skdebug(__func__, "fido_assert_new failed");
730 goto out;
731 }
732 if ((r = fido_assert_set_clientdata_hash(assert, message,
733 message_len)) != FIDO_OK) {
734 skdebug(__func__, "fido_assert_set_clientdata_hash: %s",
735 fido_strerr(r));
736 goto out;
737 }
738 if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
739 skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
740 goto out;
741 }
742 if ((r = fido_assert_allow_cred(assert, key_handle,
743 key_handle_len)) != FIDO_OK) {
744 skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
745 goto out;
746 }
747 if ((r = fido_assert_set_up(assert,
748 (flags & SK_USER_PRESENCE_REQD) ?
749 FIDO_OPT_TRUE : FIDO_OPT_FALSE)) != FIDO_OK) {
750 skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r));
751 goto out;
752 }
753 if ((r = fido_dev_get_assert(dev, assert, NULL)) != FIDO_OK) {
754 skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
755 goto out;
756 }
757 if ((response = calloc(1, sizeof(*response))) == NULL) {
758 skdebug(__func__, "calloc response failed");
759 goto out;
760 }
761 response->flags = fido_assert_flags(assert, 0);
762 response->counter = fido_assert_sigcount(assert, 0);
763 if (pack_sig(alg, assert, response) != 0) {
764 skdebug(__func__, "pack_sig failed");
765 goto out;
766 }
767 *sign_response = response;
768 response = NULL;
769 ret = 0;
770 out:
771 if (response != NULL) {
772 free(response->sig_r);
773 free(response->sig_s);
774 free(response);
775 }
776 if (dev != NULL) {
777 fido_dev_close(dev);
778 fido_dev_free(&dev);
779 }
780 if (assert != NULL) {
781 fido_assert_free(&assert);
782 }
783 return ret;
784}
diff --git a/tools/test.sh b/tools/test.sh
new file mode 100755
index 0000000..8159a44
--- /dev/null
+++ b/tools/test.sh
@@ -0,0 +1,96 @@
1#!/bin/bash -e
2#
3# Copyright (c) 2018 Yubico AB. All rights reserved.
4# Use of this source code is governed by a BSD-style
5# license that can be found in the LICENSE file.
6
7if [[ "$#" -ne 1 ]]; then
8 echo "usage: test.sh device" 1>&2
9 exit 1
10fi
11
12read -p "This script will reset the authenticator at $1, permanently erasing "\
13"its credentials. Are you *SURE* you want to proceed (yes/no)? "
14if [[ "${REPLY}" != "yes" ]]; then
15 exit 1
16fi
17
18echo "Resetting authenticator... (tap to continue!)"
19fido2-token -R $1
20
21CRED_PARAM="$(mktemp /tmp/cred_param.XXXXXXXX)"
22ASSERT_PARAM="$(mktemp /tmp/assert_param.XXXXXXXX)"
23ASSERT_PUBKEY="$(mktemp /tmp/assert_pubkey.XXXXXXXX)"
24ES256_CRED="$(mktemp /tmp/es256_cred.XXXXXXX)"
25ES256_CRED_R="$(mktemp /tmp/es256_cred_r.XXXXXXXX)"
26
27cleanup() {
28 echo "Cleaning up..."
29 [[ "${CRED_PARAM}" != "" ]] && rm "${CRED_PARAM}"
30 [[ "${ASSERT_PARAM}" != "" ]] && rm "${ASSERT_PARAM}"
31 [[ "${ASSERT_PUBKEY}" != "" ]] && rm "${ASSERT_PUBKEY}"
32 [[ "${ES256_CRED}" != "" ]] && rm "${ES256_CRED}"
33 [[ "${ES256_CRED_R}" != "" ]] && rm "${ES256_CRED_R}"
34}
35
36trap cleanup EXIT
37
38dd if=/dev/urandom bs=1 count=32 2>/dev/null | base64 > "${CRED_PARAM}"
39echo "Boring Relying Party" >> "${CRED_PARAM}"
40echo "Boring User Name" >> "${CRED_PARAM}"
41dd if=/dev/urandom bs=1 count=32 2>/dev/null | base64 >> "${CRED_PARAM}"
42echo "Credential parameters:"
43cat "${CRED_PARAM}"
44
45echo "Generating non-resident ES256 credential... (tap to continue!)"
46fido2-cred -M -i "${CRED_PARAM}" $1 | fido2-cred -V | tee "${ES256_CRED}"
47echo "Generating resident ES256 credential... (tap to continue!)"
48fido2-cred -M -r -i "${CRED_PARAM}" $1 | fido2-cred -V | tee "${ES256_CRED_R}"
49
50PIN1="$(dd if=/dev/urandom | tr -cd '[:print:]' | fold -w50 | head -1)"
51PIN2="$(dd if=/dev/urandom | tr -cd '[:print:]' | fold -w50 | head -1)"
52
53echo "Setting ${PIN1} as the PIN..."
54echo -e "${PIN1}\n${PIN1}" | setsid -w fido2-token -S $1
55echo "Changing PIN from ${PIN1} to ${PIN2}..."
56echo -e "${PIN1}\n${PIN2}\n${PIN2}" | setsid -w fido2-token -C $1
57echo ""
58
59echo "Testing non-resident ES256 credential..."
60echo "Getting assertion without user presence verification..."
61dd if=/dev/urandom bs=1 count=32 2>/dev/null | base64 > "${ASSERT_PARAM}"
62echo "Boring Relying Party" >> "${ASSERT_PARAM}"
63head -1 "${ES256_CRED}" >> "${ASSERT_PARAM}"
64tail -n +2 "${ES256_CRED}" > "${ASSERT_PUBKEY}"
65echo "Assertion parameters:"
66cat "${ASSERT_PARAM}"
67fido2-assert -G -i "${ASSERT_PARAM}" $1 | fido2-assert -V "${ASSERT_PUBKEY}"
68echo "Checking that the user presence bit is observed..."
69! fido2-assert -G -i "${ASSERT_PARAM}" $1 | fido2-assert -V -p "${ASSERT_PUBKEY}"
70echo "Checking that the user verification bit is observed..."
71! fido2-assert -G -i "${ASSERT_PARAM}" $1 | fido2-assert -V -v "${ASSERT_PUBKEY}"
72echo "Getting assertion _with_ user presence verification... (tap to continue!)"
73fido2-assert -G -p -i "${ASSERT_PARAM}" $1 | fido2-assert -V -p "${ASSERT_PUBKEY}"
74echo "Getting assertion _with_ user verification..."
75echo -e "${PIN2}\n" | setsid -w fido2-assert -G -v -i "${ASSERT_PARAM}" $1 | \
76 fido2-assert -V -v "${ASSERT_PUBKEY}"
77echo ""
78
79echo "Testing resident ES256 credential..."
80echo "Getting assertion without user presence verification..."
81dd if=/dev/urandom bs=1 count=32 2>/dev/null | base64 > "${ASSERT_PARAM}"
82echo "Boring Relying Party" >> "${ASSERT_PARAM}"
83tail -n +2 "${ES256_CRED_R}" > "${ASSERT_PUBKEY}"
84echo "Assertion parameters:"
85cat "${ASSERT_PARAM}"
86fido2-assert -G -r -i "${ASSERT_PARAM}" $1 | fido2-assert -V "${ASSERT_PUBKEY}"
87echo "Checking that the user presence bit is observed..."
88! fido2-assert -G -r -i "${ASSERT_PARAM}" $1 | fido2-assert -V -p "${ASSERT_PUBKEY}"
89echo "Checking that the user verification bit is observed..."
90! fido2-assert -G -r -i "${ASSERT_PARAM}" $1 | fido2-assert -V -v "${ASSERT_PUBKEY}"
91echo "Getting assertion _with_ user presence verification... (tap to continue!)"
92fido2-assert -G -r -p -i "${ASSERT_PARAM}" $1 | fido2-assert -V -p "${ASSERT_PUBKEY}"
93echo "Getting assertion _with_ user verification..."
94echo -e "${PIN2}\n" | setsid -w fido2-assert -G -v -r -i "${ASSERT_PARAM}" $1 | \
95 fido2-assert -V -v "${ASSERT_PUBKEY}"
96echo ""
diff --git a/tools/token.c b/tools/token.c
new file mode 100644
index 0000000..b149208
--- /dev/null
+++ b/tools/token.c
@@ -0,0 +1,364 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <fido.h>
8#include <stdbool.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#ifdef HAVE_UNISTD_H
13#include <unistd.h>
14#endif
15
16#include "../openbsd-compat/openbsd-compat.h"
17#include "extern.h"
18
19static void
20format_flags(char *ret, size_t retlen, uint8_t flags)
21{
22 memset(ret, 0, retlen);
23
24 if (flags & FIDO_CAP_WINK) {
25 if (strlcat(ret, "wink,", retlen) >= retlen)
26 goto toolong;
27 } else {
28 if (strlcat(ret, "nowink,", retlen) >= retlen)
29 goto toolong;
30 }
31
32 if (flags & FIDO_CAP_CBOR) {
33 if (strlcat(ret, " cbor,", retlen) >= retlen)
34 goto toolong;
35 } else {
36 if (strlcat(ret, " nocbor,", retlen) >= retlen)
37 goto toolong;
38 }
39
40 if (flags & FIDO_CAP_NMSG) {
41 if (strlcat(ret, " nomsg", retlen) >= retlen)
42 goto toolong;
43 } else {
44 if (strlcat(ret, " msg", retlen) >= retlen)
45 goto toolong;
46 }
47
48 return;
49toolong:
50 strlcpy(ret, "toolong", retlen);
51}
52
53static void
54print_attr(const fido_dev_t *dev)
55{
56 char flags_txt[128];
57
58 printf("proto: 0x%02x\n", fido_dev_protocol(dev));
59 printf("major: 0x%02x\n", fido_dev_major(dev));
60 printf("minor: 0x%02x\n", fido_dev_minor(dev));
61 printf("build: 0x%02x\n", fido_dev_build(dev));
62
63 format_flags(flags_txt, sizeof(flags_txt), fido_dev_flags(dev));
64 printf("caps: 0x%02x (%s)\n", fido_dev_flags(dev), flags_txt);
65}
66
67static void
68print_str_array(const char *label, char * const *sa, size_t len)
69{
70 if (len == 0)
71 return;
72
73 printf("%s strings: ", label);
74
75 for (size_t i = 0; i < len; i++)
76 printf("%s%s", i > 0 ? ", " : "", sa[i]);
77
78 printf("\n");
79}
80
81static void
82print_opt_array(const char *label, char * const *name, const bool *value,
83 size_t len)
84{
85 if (len == 0)
86 return;
87
88 printf("%s: ", label);
89
90 for (size_t i = 0; i < len; i++)
91 printf("%s%s%s", i > 0 ? ", " : "",
92 value[i] ? "" : "no", name[i]);
93
94 printf("\n");
95}
96
97static void
98print_aaguid(const unsigned char *buf, size_t buflen)
99{
100 printf("aaguid: ");
101
102 while (buflen--)
103 printf("%02x", *buf++);
104
105 printf("\n");
106}
107
108static void
109print_maxmsgsiz(uint64_t maxmsgsiz)
110{
111 printf("maxmsgsiz: %d\n", (int)maxmsgsiz);
112}
113
114static void
115print_byte_array(const char *label, const uint8_t *ba, size_t len)
116{
117 if (len == 0)
118 return;
119
120 printf("%s: ", label);
121
122 for (size_t i = 0; i < len; i++)
123 printf("%s%u", i > 0 ? ", " : "", (unsigned)ba[i]);
124
125 printf("\n");
126}
127
128int
129token_info(int argc, char **argv, char *path)
130{
131 char *cred_id = NULL;
132 char *rp_id = NULL;
133 fido_cbor_info_t *ci = NULL;
134 fido_dev_t *dev = NULL;
135 int ch;
136 int credman = 0;
137 int r;
138 int retrycnt;
139
140 optind = 1;
141
142 while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
143 switch (ch) {
144 case 'c':
145 credman = 1;
146 break;
147 case 'i':
148 cred_id = optarg;
149 break;
150 case 'k':
151 rp_id = optarg;
152 break;
153 default:
154 break; /* ignore */
155 }
156 }
157
158 if (path == NULL || (credman && (cred_id != NULL || rp_id != NULL)))
159 usage();
160
161 dev = open_dev(path);
162
163 if (credman)
164 return (credman_get_metadata(dev, path));
165 if (cred_id && rp_id)
166 return (credman_print_rk(dev, path, rp_id, cred_id));
167 if (cred_id || rp_id)
168 usage();
169
170 print_attr(dev);
171
172 if (fido_dev_is_fido2(dev) == false)
173 goto end;
174 if ((ci = fido_cbor_info_new()) == NULL)
175 errx(1, "fido_cbor_info_new");
176 if ((r = fido_dev_get_cbor_info(dev, ci)) != FIDO_OK)
177 errx(1, "fido_dev_get_cbor_info: %s (0x%x)", fido_strerr(r), r);
178
179 /* print supported protocol versions */
180 print_str_array("version", fido_cbor_info_versions_ptr(ci),
181 fido_cbor_info_versions_len(ci));
182
183 /* print supported extensions */
184 print_str_array("extension", fido_cbor_info_extensions_ptr(ci),
185 fido_cbor_info_extensions_len(ci));
186
187 /* print aaguid */
188 print_aaguid(fido_cbor_info_aaguid_ptr(ci),
189 fido_cbor_info_aaguid_len(ci));
190
191 /* print supported options */
192 print_opt_array("options", fido_cbor_info_options_name_ptr(ci),
193 fido_cbor_info_options_value_ptr(ci),
194 fido_cbor_info_options_len(ci));
195
196 /* print maximum message size */
197 print_maxmsgsiz(fido_cbor_info_maxmsgsiz(ci));
198
199 /* print supported pin protocols */
200 print_byte_array("pin protocols", fido_cbor_info_protocols_ptr(ci),
201 fido_cbor_info_protocols_len(ci));
202
203 if ((r = fido_dev_get_retry_count(dev, &retrycnt)) != FIDO_OK)
204 printf("pin retries: undefined\n");
205 else
206 printf("pin retries: %d\n", retrycnt);
207
208 bio_info(dev);
209
210 fido_cbor_info_free(&ci);
211end:
212 fido_dev_close(dev);
213 fido_dev_free(&dev);
214
215 exit(0);
216}
217
218int
219token_reset(char *path)
220{
221 fido_dev_t *dev = NULL;
222 int r;
223
224 if (path == NULL)
225 usage();
226
227 dev = open_dev(path);
228 if ((r = fido_dev_reset(dev)) != FIDO_OK)
229 errx(1, "fido_dev_reset: %s", fido_strerr(r));
230
231 fido_dev_close(dev);
232 fido_dev_free(&dev);
233
234 exit(0);
235}
236
237int
238token_set(int argc, char **argv, char *path)
239{
240 char *id = NULL;
241 char *name = NULL;
242 int ch;
243 int enroll = 0;
244
245 optind = 1;
246
247 while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
248 switch (ch) {
249 case 'e':
250 enroll = 1;
251 break;
252 case 'i':
253 id = optarg;
254 break;
255 case 'n':
256 name = optarg;
257 break;
258 default:
259 break; /* ignore */
260 }
261 }
262
263 if (enroll) {
264 if (id && name)
265 return (bio_set_name(path, id, name));
266 if (!id && !name)
267 return (bio_enroll(path));
268 usage();
269 }
270
271 return (pin_set(path));
272}
273
274int
275token_list(int argc, char **argv, char *path)
276{
277 fido_dev_info_t *devlist;
278 size_t ndevs;
279 const char *rp_id = NULL;
280 int enrolls = 0;
281 int keys = 0;
282 int rplist = 0;
283 int ch;
284 int r;
285
286 optind = 1;
287
288 while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
289 switch (ch) {
290 case 'e':
291 enrolls = 1;
292 break;
293 case 'k':
294 keys = 1;
295 rp_id = optarg;
296 break;
297 case 'r':
298 rplist = 1;
299 break;
300 default:
301 break; /* ignore */
302 }
303 }
304
305 if (enrolls)
306 return (bio_list(path));
307 if (keys)
308 return (credman_list_rk(path, rp_id));
309 if (rplist)
310 return (credman_list_rp(path));
311
312 if ((devlist = fido_dev_info_new(64)) == NULL)
313 errx(1, "fido_dev_info_new");
314 if ((r = fido_dev_info_manifest(devlist, 64, &ndevs)) != FIDO_OK)
315 errx(1, "fido_dev_info_manifest: %s (0x%x)", fido_strerr(r), r);
316
317 for (size_t i = 0; i < ndevs; i++) {
318 const fido_dev_info_t *di = fido_dev_info_ptr(devlist, i);
319 printf("%s: vendor=0x%04x, product=0x%04x (%s %s)\n",
320 fido_dev_info_path(di),
321 (uint16_t)fido_dev_info_vendor(di),
322 (uint16_t)fido_dev_info_product(di),
323 fido_dev_info_manufacturer_string(di),
324 fido_dev_info_product_string(di));
325 }
326
327 fido_dev_info_free(&devlist, ndevs);
328
329 exit(0);
330}
331
332int
333token_delete(int argc, char **argv, char *path)
334{
335 char *id = NULL;
336 fido_dev_t *dev = NULL;
337 int ch;
338 int enroll = 0;
339
340 optind = 1;
341
342 while ((ch = getopt(argc, argv, TOKEN_OPT)) != -1) {
343 switch (ch) {
344 case 'e':
345 enroll = 1;
346 break;
347 case 'i':
348 id = optarg;
349 break;
350 default:
351 break; /* ignore */
352 }
353 }
354
355 if (path == NULL || id == NULL)
356 usage();
357
358 dev = open_dev(path);
359
360 if (id && !enroll)
361 return (credman_delete_rk(dev, path, id));
362
363 return (bio_delete(dev, path, id));
364}
diff --git a/tools/util.c b/tools/util.c
new file mode 100644
index 0000000..de70388
--- /dev/null
+++ b/tools/util.c
@@ -0,0 +1,364 @@
1/*
2 * Copyright (c) 2018 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 */
6
7#include <sys/types.h>
8#include <sys/stat.h>
9
10#include <openssl/ec.h>
11#include <openssl/evp.h>
12#include <openssl/pem.h>
13
14#include <fido.h>
15#include <fido/es256.h>
16#include <fido/rs256.h>
17#include <fido/eddsa.h>
18
19#include <fcntl.h>
20#include <stdint.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24
25#include "../openbsd-compat/openbsd-compat.h"
26#ifdef _MSC_VER
27#include "../openbsd-compat/posix_win.h"
28#endif
29
30#include "extern.h"
31
32void
33read_pin(const char *path, char *buf, size_t len)
34{
35 char prompt[1024];
36 int r;
37
38 r = snprintf(prompt, sizeof(prompt), "Enter PIN for %s: ", path);
39 if (r < 0 || (size_t)r >= sizeof(prompt))
40 errx(1, "snprintf");
41 if (!readpassphrase(prompt, buf, len, RPP_ECHO_OFF))
42 errx(1, "readpassphrase");
43}
44
45FILE *
46open_write(const char *file)
47{
48 int fd;
49 FILE *f;
50
51 if (file == NULL || strcmp(file, "-") == 0)
52 return (stdout);
53 if ((fd = open(file, O_WRONLY | O_CREAT, 0600)) < 0)
54 err(1, "open %s", file);
55 if ((f = fdopen(fd, "w")) == NULL)
56 err(1, "fdopen %s", file);
57
58 return (f);
59}
60
61FILE *
62open_read(const char *file)
63{
64 int fd;
65 FILE *f;
66
67 if (file == NULL || strcmp(file, "-") == 0) {
68#ifdef FIDO_FUZZ
69 setvbuf(stdin, NULL, _IONBF, 0);
70#endif
71 return (stdin);
72 }
73 if ((fd = open(file, O_RDONLY)) < 0)
74 err(1, "open %s", file);
75 if ((f = fdopen(fd, "r")) == NULL)
76 err(1, "fdopen %s", file);
77
78 return (f);
79}
80
81void
82xxd(const void *buf, size_t count)
83{
84 const uint8_t *ptr = buf;
85 size_t i;
86
87 fprintf(stderr, " ");
88
89 for (i = 0; i < count; i++) {
90 fprintf(stderr, "%02x ", *ptr++);
91 if ((i + 1) % 16 == 0 && i + 1 < count)
92 fprintf(stderr, "\n ");
93 }
94
95 fprintf(stderr, "\n");
96 fflush(stderr);
97}
98
99int
100string_read(FILE *f, char **out)
101{
102 char *line = NULL;
103 size_t linesize = 0;
104 ssize_t n;
105
106 *out = NULL;
107
108 if ((n = getline(&line, &linesize, f)) <= 0 ||
109 (size_t)n != strlen(line)) {
110 free(line);
111 return (-1);
112 }
113
114 line[n - 1] = '\0'; /* trim \n */
115 *out = line;
116
117 return (0);
118}
119
120fido_dev_t *
121open_dev(const char *path)
122{
123 fido_dev_t *dev;
124 int r;
125
126 if ((dev = fido_dev_new()) == NULL)
127 errx(1, "fido_dev_new");
128
129 r = fido_dev_open(dev, path);
130 if (r != FIDO_OK)
131 errx(1, "fido_dev_open %s: %s", path, fido_strerr(r));
132
133 return (dev);
134}
135
136EC_KEY *
137read_ec_pubkey(const char *path)
138{
139 FILE *fp = NULL;
140 EVP_PKEY *pkey = NULL;
141 EC_KEY *ec = NULL;
142
143 if ((fp = fopen(path, "r")) == NULL) {
144 warn("fopen");
145 goto fail;
146 }
147
148 if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
149 warnx("PEM_read_PUBKEY");
150 goto fail;
151 }
152 if ((ec = EVP_PKEY_get1_EC_KEY(pkey)) == NULL) {
153 warnx("EVP_PKEY_get1_EC_KEY");
154 goto fail;
155 }
156
157fail:
158 if (fp) {
159 fclose(fp);
160 }
161 if (pkey) {
162 EVP_PKEY_free(pkey);
163 }
164
165 return (ec);
166}
167
168int
169write_ec_pubkey(FILE *f, const void *ptr, size_t len)
170{
171 EVP_PKEY *pkey = NULL;
172 es256_pk_t *pk = NULL;
173 int ok = -1;
174
175 if ((pk = es256_pk_new()) == NULL) {
176 warnx("es256_pk_new");
177 goto fail;
178 }
179
180 if (es256_pk_from_ptr(pk, ptr, len) != FIDO_OK) {
181 warnx("es256_pk_from_ptr");
182 goto fail;
183 }
184
185 if ((pkey = es256_pk_to_EVP_PKEY(pk)) == NULL) {
186 warnx("es256_pk_to_EVP_PKEY");
187 goto fail;
188 }
189
190 if (PEM_write_PUBKEY(f, pkey) == 0) {
191 warnx("PEM_write_PUBKEY");
192 goto fail;
193 }
194
195 ok = 0;
196fail:
197 es256_pk_free(&pk);
198
199 if (pkey != NULL) {
200 EVP_PKEY_free(pkey);
201 }
202
203 return (ok);
204}
205
206RSA *
207read_rsa_pubkey(const char *path)
208{
209 FILE *fp = NULL;
210 EVP_PKEY *pkey = NULL;
211 RSA *rsa = NULL;
212
213 if ((fp = fopen(path, "r")) == NULL) {
214 warn("fopen");
215 goto fail;
216 }
217
218 if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
219 warnx("PEM_read_PUBKEY");
220 goto fail;
221 }
222 if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) {
223 warnx("EVP_PKEY_get1_RSA");
224 goto fail;
225 }
226
227fail:
228 if (fp) {
229 fclose(fp);
230 }
231 if (pkey) {
232 EVP_PKEY_free(pkey);
233 }
234
235 return (rsa);
236}
237
238int
239write_rsa_pubkey(FILE *f, const void *ptr, size_t len)
240{
241 EVP_PKEY *pkey = NULL;
242 rs256_pk_t *pk = NULL;
243 int ok = -1;
244
245 if ((pk = rs256_pk_new()) == NULL) {
246 warnx("rs256_pk_new");
247 goto fail;
248 }
249
250 if (rs256_pk_from_ptr(pk, ptr, len) != FIDO_OK) {
251 warnx("rs256_pk_from_ptr");
252 goto fail;
253 }
254
255 if ((pkey = rs256_pk_to_EVP_PKEY(pk)) == NULL) {
256 warnx("rs256_pk_to_EVP_PKEY");
257 goto fail;
258 }
259
260 if (PEM_write_PUBKEY(f, pkey) == 0) {
261 warnx("PEM_write_PUBKEY");
262 goto fail;
263 }
264
265 ok = 0;
266fail:
267 rs256_pk_free(&pk);
268
269 if (pkey != NULL) {
270 EVP_PKEY_free(pkey);
271 }
272
273 return (ok);
274}
275
276EVP_PKEY *
277read_eddsa_pubkey(const char *path)
278{
279 FILE *fp = NULL;
280 EVP_PKEY *pkey = NULL;
281
282 if ((fp = fopen(path, "r")) == NULL) {
283 warn("fopen");
284 goto fail;
285 }
286
287 if ((pkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
288 warnx("PEM_read_PUBKEY");
289 goto fail;
290 }
291
292fail:
293 if (fp) {
294 fclose(fp);
295 }
296
297 return (pkey);
298}
299
300int
301write_eddsa_pubkey(FILE *f, const void *ptr, size_t len)
302{
303 EVP_PKEY *pkey = NULL;
304 eddsa_pk_t *pk = NULL;
305 int ok = -1;
306
307 if ((pk = eddsa_pk_new()) == NULL) {
308 warnx("eddsa_pk_new");
309 goto fail;
310 }
311
312 if (eddsa_pk_from_ptr(pk, ptr, len) != FIDO_OK) {
313 warnx("eddsa_pk_from_ptr");
314 goto fail;
315 }
316
317 if ((pkey = eddsa_pk_to_EVP_PKEY(pk)) == NULL) {
318 warnx("eddsa_pk_to_EVP_PKEY");
319 goto fail;
320 }
321
322 if (PEM_write_PUBKEY(f, pkey) == 0) {
323 warnx("PEM_write_PUBKEY");
324 goto fail;
325 }
326
327 ok = 0;
328fail:
329 eddsa_pk_free(&pk);
330
331 if (pkey != NULL) {
332 EVP_PKEY_free(pkey);
333 }
334
335 return (ok);
336}
337
338void
339print_cred(FILE *out_f, int type, const fido_cred_t *cred)
340{
341 char *id;
342 int r;
343
344 r = base64_encode(fido_cred_id_ptr(cred), fido_cred_id_len(cred), &id);
345 if (r < 0)
346 errx(1, "output error");
347
348 fprintf(out_f, "%s\n", id);
349
350 if (type == COSE_ES256) {
351 write_ec_pubkey(out_f, fido_cred_pubkey_ptr(cred),
352 fido_cred_pubkey_len(cred));
353 } else if (type == COSE_RS256) {
354 write_rsa_pubkey(out_f, fido_cred_pubkey_ptr(cred),
355 fido_cred_pubkey_len(cred));
356 } else if (type == COSE_EDDSA) {
357 write_eddsa_pubkey(out_f, fido_cred_pubkey_ptr(cred),
358 fido_cred_pubkey_len(cred));
359 } else {
360 errx(1, "print_cred: unknown type");
361 }
362
363 free(id);
364}
diff --git a/udev/70-u2f.rules b/udev/70-u2f.rules
new file mode 100644
index 0000000..8dc20a1
--- /dev/null
+++ b/udev/70-u2f.rules
@@ -0,0 +1,72 @@
1# Copyright (c) 2018 Yubico AB. All rights reserved.
2# Use of this source code is governed by a BSD-style
3# license that can be found in the LICENSE file.
4
5# this udev file should be used with udev 188 and newer
6ACTION!="add|change", GOTO="u2f_end"
7
8# Yubico YubiKey
9KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0113|0114|0115|0116|0120|0121|0200|0402|0403|0406|0407|0410", TAG+="uaccess", GROUP="plugdev", MODE="0660"
10
11# Happlink (formerly Plug-Up) Security KEY
12KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2581", ATTRS{idProduct}=="f1d0", TAG+="uaccess", GROUP="plugdev", MODE="0660"
13
14# Neowave Keydo and Keydo AES
15KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1e0d", ATTRS{idProduct}=="f1d0|f1ae", TAG+="uaccess", GROUP="plugdev", MODE="0660"
16
17# HyperSecu HyperFIDO
18KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="096e|2ccf", ATTRS{idProduct}=="0880", TAG+="uaccess", GROUP="plugdev", MODE="0660"
19
20# Feitian ePass FIDO, BioPass FIDO2
21KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="096e", ATTRS{idProduct}=="0850|0852|0853|0854|0856|0858|085a|085b|085d", TAG+="uaccess", GROUP="plugdev", MODE="0660"
22
23# JaCarta U2F
24KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="24dc", ATTRS{idProduct}=="0101|0501", TAG+="uaccess", GROUP="plugdev", MODE="0660"
25
26# U2F Zero
27KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="8acf", TAG+="uaccess", GROUP="plugdev", MODE="0660"
28
29# VASCO SecureClick
30KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1a44", ATTRS{idProduct}=="00bb", TAG+="uaccess", GROUP="plugdev", MODE="0660"
31
32# Bluink Key
33KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2abe", ATTRS{idProduct}=="1002", TAG+="uaccess", GROUP="plugdev", MODE="0660"
34
35# Thetis Key
36KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1ea8", ATTRS{idProduct}=="f025", TAG+="uaccess", GROUP="plugdev", MODE="0660"
37
38# Nitrokey FIDO U2F
39KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="4287", TAG+="uaccess", GROUP="plugdev", MODE="0660"
40
41# Google Titan U2F
42KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="18d1", ATTRS{idProduct}=="5026", TAG+="uaccess", GROUP="plugdev", MODE="0660"
43
44# Tomu board + chopstx U2F + SoloKeys
45KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="cdab|a2ca", TAG+="uaccess", GROUP="plugdev", MODE="0660"
46
47# SoloKeys
48KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="5070|50b0", TAG+="uaccess", GROUP="plugdev", MODE="0660"
49
50# Trezor
51KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="534c", ATTRS{idProduct}=="0001", TAG+="uaccess", GROUP="plugdev", MODE="0660"
52KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="53c1", TAG+="uaccess", GROUP="plugdev", MODE="0660"
53
54# Infineon FIDO
55KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="058b", ATTRS{idProduct}=="022d", TAG+="uaccess", GROUP="plugdev", MODE="0660"
56
57# Ledger Nano S and Nano X
58KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2c97", ATTRS{idProduct}=="0001|0004", TAG+="uaccess", GROUP="plugdev", MODE="0660"
59
60# Kensington VeriMark
61KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="06cb", ATTRS{idProduct}=="0088", TAG+="uaccess", GROUP="plugdev", MODE="0660"
62
63# Longmai mFIDO
64KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="4c4d", ATTRS{idProduct}=="f703", TAG+="uaccess", GROUP="plugdev", MODE="0660"
65
66# eWBM FIDO2 - Goldengate 310, 320, 500, 450
67KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="311f", ATTRS{idProduct}=="4a1a|4c2a|5c2f|f47c", TAG+="uaccess", GROUP="plugdev", MODE="0660"
68
69# OnlyKey (FIDO2 / U2F)
70KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="60fc", TAG+="uaccess", GROUP="plugdev", MODE="0660"
71
72LABEL="u2f_end"
diff --git a/udev/CMakeLists.txt b/udev/CMakeLists.txt
new file mode 100644
index 0000000..29a9d41
--- /dev/null
+++ b/udev/CMakeLists.txt
@@ -0,0 +1,7 @@
1# Copyright (c) 2018 Yubico AB. All rights reserved.
2# Use of this source code is governed by a BSD-style
3# license that can be found in the LICENSE file.
4
5if(UDEV_RULES_DIR)
6 install(FILES 70-u2f.rules DESTINATION ${UDEV_RULES_DIR})
7endif()
diff --git a/windows/build.ps1 b/windows/build.ps1
new file mode 100644
index 0000000..aaa848d
--- /dev/null
+++ b/windows/build.ps1
@@ -0,0 +1,204 @@
1param(
2 [string]$CMakePath = "C:\Program Files\CMake\bin\cmake.exe",
3 [string]$GitPath = "C:\Program Files\Git\bin\git.exe",
4 [string]$SevenZPath = "C:\Program Files\7-Zip\7z.exe",
5 [string]$GPGPath = "C:\Program Files (x86)\GnuPG\bin\gpg.exe"
6)
7
8$ErrorActionPreference = "Continue"
9
10[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
11
12# LibreSSL coordinates.
13New-Variable -Name 'LIBRESSL_URL' `
14 -Value 'https://ftp.openbsd.org/pub/OpenBSD/LibreSSL' -Option Constant
15New-Variable -Name 'LIBRESSL' -Value 'libressl-3.0.2' -Option Constant
16
17# libcbor coordinates.
18New-Variable -Name 'LIBCBOR' -Value 'libcbor-0.5.0' -Option Constant
19New-Variable -Name 'LIBCBOR_BRANCH' -Value 'v0.5.0' -Option Constant
20New-Variable -Name 'LIBCBOR_GIT' -Value 'https://github.com/pjk/libcbor' `
21 -Option Constant
22
23# Work directories.
24New-Variable -Name 'BUILD' -Value "$PSScriptRoot\..\build" -Option Constant
25New-Variable -Name 'OUTPUT' -Value "$PSScriptRoot\..\output" -Option Constant
26
27# Find CMake.
28$CMake = $(Get-Command cmake -ErrorAction Ignore | Select-Object -ExpandProperty Source)
29if([string]::IsNullOrEmpty($CMake)) {
30 $CMake = $CMakePath
31}
32
33# Find Git.
34$Git = $(Get-Command git -ErrorAction Ignore | Select-Object -ExpandProperty Source)
35if([string]::IsNullOrEmpty($Git)) {
36 $Git = $GitPath
37}
38
39# Find 7z.
40$SevenZ = $(Get-Command 7z -ErrorAction Ignore | Select-Object -ExpandProperty Source)
41if([string]::IsNullOrEmpty($SevenZ)) {
42 $SevenZ = $SevenZPath
43}
44
45# Find GPG.
46$GPG = $(Get-Command gpg -ErrorAction Ignore | Select-Object -ExpandProperty Source)
47if([string]::IsNullOrEmpty($GPG)) {
48 $GPG = $GPGPath
49}
50
51if(-Not (Test-Path $CMake)) {
52 throw "Unable to find CMake at $CMake"
53}
54
55if(-Not (Test-Path $Git)) {
56 throw "Unable to find Git at $Git"
57}
58
59if(-Not (Test-Path $SevenZ)) {
60 throw "Unable to find 7z at $SevenZ"
61}
62
63if(-Not (Test-Path $GPG)) {
64 throw "Unable to find GPG at $GPG"
65}
66
67Write-Host "Git: $Git"
68Write-Host "CMake: $CMake"
69Write-Host "7z: $SevenZ"
70Write-Host "GPG: $GPG"
71
72New-Item -Type Directory ${BUILD}
73New-Item -Type Directory ${BUILD}\32
74New-Item -Type Directory ${BUILD}\64
75New-Item -Type Directory ${OUTPUT}
76New-Item -Type Directory ${OUTPUT}\pkg\Win64\Release\v142\dynamic
77New-Item -Type Directory ${OUTPUT}\pkg\Win32\Release\v142\dynamic
78
79Push-Location ${BUILD}
80
81try {
82 if (Test-Path .\${LIBRESSL}) {
83 Remove-Item .\${LIBRESSL} -Recurse -ErrorAction Stop
84 }
85
86 if(-Not (Test-Path .\${LIBRESSL}.tar.gz -PathType leaf)) {
87 Invoke-WebRequest ${LIBRESSL_URL}/${LIBRESSL}.tar.gz `
88 -OutFile .\${LIBRESSL}.tar.gz
89 }
90 if(-Not (Test-Path .\${LIBRESSL}.tar.gz.asc -PathType leaf)) {
91 Invoke-WebRequest ${LIBRESSL_URL}/${LIBRESSL}.tar.gz.asc `
92 -OutFile .\${LIBRESSL}.tar.gz.asc
93 }
94
95 Copy-Item "$PSScriptRoot\libressl.gpg" -Destination "${BUILD}"
96 & $GPG --list-keys
97 & $GPG -v --no-default-keyring --keyring ./libressl.gpg `
98 --verify .\${LIBRESSL}.tar.gz.asc .\${LIBRESSL}.tar.gz
99 if ($LastExitCode -ne 0) {
100 throw "GPG signature verification failed"
101 }
102
103 & $SevenZ e .\${LIBRESSL}.tar.gz
104 & $SevenZ x .\${LIBRESSL}.tar
105 Remove-Item -Force .\${LIBRESSL}.tar
106
107 if(-Not (Test-Path .\${LIBCBOR})) {
108 Write-Host "Cloning ${LIBCBOR}..."
109 & $Git clone --branch ${LIBCBOR_BRANCH} ${LIBCBOR_GIT} `
110 .\${LIBCBOR}
111 }
112} catch {
113 throw "Failed to fetch and verify dependencies"
114} finally {
115 Pop-Location
116}
117
118Function Build(${OUTPUT}, ${GENERATOR}, ${ARCH}) {
119 if(-Not (Test-Path .\${LIBRESSL})) {
120 New-Item -Type Directory .\${LIBRESSL} -ErrorAction Stop
121 }
122
123 Push-Location .\${LIBRESSL}
124 & $CMake ..\..\${LIBRESSL} -G "${GENERATOR}" -A "${ARCH}" `
125 -DCMAKE_C_FLAGS_RELEASE="/Zi" `
126 -DCMAKE_INSTALL_PREFIX="${OUTPUT}" -DBUILD_SHARED_LIBS=ON `
127 -DLIBRESSL_TESTS=OFF
128 & $CMake --build . --config Release
129 & $CMake --build . --config Release --target install
130 Pop-Location
131
132 if(-Not (Test-Path .\${LIBCBOR})) {
133 New-Item -Type Directory .\${LIBCBOR} -ErrorAction Stop
134 }
135
136 Push-Location .\${LIBCBOR}
137 & $CMake ..\..\${LIBCBOR} -G "${GENERATOR}" -A "${ARCH}" `
138 -DCMAKE_C_FLAGS_RELEASE="/Zi" `
139 -DCMAKE_INSTALL_PREFIX="${OUTPUT}"
140 & $CMake --build . --config Release
141 & $CMake --build . --config Release --target install
142 Pop-Location
143
144 & $CMake ..\.. -G "${GENERATOR}" -A "${ARCH}" `
145 -DCBOR_INCLUDE_DIRS="${OUTPUT}\include" `
146 -DCBOR_LIBRARY_DIRS="${OUTPUT}\lib" `
147 -DCRYPTO_INCLUDE_DIRS="${OUTPUT}\include" `
148 -DCRYPTO_LIBRARY_DIRS="${OUTPUT}\lib" `
149 -DCMAKE_INSTALL_PREFIX="${OUTPUT}"
150 & $CMake --build . --config Release
151 & $CMake --build . --config Release --target install
152 "cbor.dll", "crypto-45.dll" | %{ Copy-Item "${OUTPUT}\bin\$_" `
153 -Destination "examples\Release" }
154}
155
156Function Package-Headers() {
157 Copy-Item "${OUTPUT}\64\include" -Destination "${OUTPUT}\pkg" `
158 -Recurse -ErrorAction Stop
159}
160
161Function Package-Libraries(${SRC}, ${DEST}) {
162 Copy-Item "${SRC}\bin\cbor.dll" "${DEST}" -ErrorAction Stop
163 Copy-Item "${SRC}\lib\cbor.lib" "${DEST}" -ErrorAction Stop
164 Copy-Item "${SRC}\bin\crypto-45.dll" "${DEST}" -ErrorAction Stop
165 Copy-Item "${SRC}\lib\crypto-45.lib" "${DEST}" -ErrorAction Stop
166 Copy-Item "${SRC}\lib\fido2.dll" "${DEST}" -ErrorAction Stop
167 Copy-Item "${SRC}\lib\fido2.lib" "${DEST}" -ErrorAction Stop
168}
169
170Function Package-PDBs(${SRC}, ${DEST}) {
171 Copy-Item "${SRC}\${LIBRESSL}\crypto\crypto.dir\Release\vc142.pdb" `
172 "${DEST}\crypto-45.pdb" -ErrorAction Stop
173 Copy-Item "${SRC}\${LIBCBOR}\src\cbor_shared.dir\Release\vc142.pdb" `
174 "${DEST}\cbor.pdb" -ErrorAction Stop
175 Copy-Item "${SRC}\src\fido2_shared.dir\Release\vc142.pdb" `
176 "${DEST}\fido2.pdb" -ErrorAction Stop
177}
178
179Function Package-Tools(${SRC}, ${DEST}) {
180 Copy-Item "${SRC}\tools\Release\fido2-assert.exe" `
181 "${DEST}\fido2-assert.exe" -ErrorAction stop
182 Copy-Item "${SRC}\tools\Release\fido2-cred.exe" `
183 "${DEST}\fido2-cred.exe" -ErrorAction stop
184 Copy-Item "${SRC}\tools\Release\fido2-token.exe" `
185 "${DEST}\fido2-token.exe" -ErrorAction stop
186}
187
188Push-Location ${BUILD}\64
189Build ${OUTPUT}\64 "Visual Studio 16 2019" "x64"
190Pop-Location
191
192Push-Location ${BUILD}\32
193Build ${OUTPUT}\32 "Visual Studio 16 2019" "Win32"
194Pop-Location
195
196Package-Headers
197
198Package-Libraries ${OUTPUT}\64 ${OUTPUT}\pkg\Win64\Release\v142\dynamic
199Package-PDBs ${BUILD}\64 ${OUTPUT}\pkg\Win64\Release\v142\dynamic
200Package-Tools ${BUILD}\64 ${OUTPUT}\pkg\Win64\Release\v142\dynamic
201
202Package-Libraries ${OUTPUT}\32 ${OUTPUT}\pkg\Win32\Release\v142\dynamic
203Package-PDBs ${BUILD}\32 ${OUTPUT}\pkg\Win32\Release\v142\dynamic
204Package-Tools ${BUILD}\32 ${OUTPUT}\pkg\Win32\Release\v142\dynamic
diff --git a/windows/libressl.gpg b/windows/libressl.gpg
new file mode 100644
index 0000000..87d5dad
--- /dev/null
+++ b/windows/libressl.gpg
Binary files differ