From 11f0a78c7d3443f0f0960525b5ede317cbf8536e Mon Sep 17 00:00:00 2001 From: Ben Collins Date: Sun, 5 Jan 2025 20:56:42 -0500 Subject: [PATCH] Initial docs Signed-off-by: Ben Collins --- .gitignore | 18 - AUTHORS | 1 - CMakeLists.txt | 108 + COPYING.LIB | 515 --- ChangeLog | 1 - HEAD/Info.plist | 25 + HEAD/LibJWT.svg | 588 ++++ HEAD/Makefile | 38 + HEAD/Nodes.xml | 1096 +++++++ HEAD/Tokens.xml | 2155 +++++++++++++ HEAD/annotated.html | 102 + HEAD/annotated_dup.js | 5 + HEAD/bc_s.png | Bin 0 -> 676 bytes HEAD/bc_sd.png | Bin 0 -> 635 bytes HEAD/classes.html | 102 + HEAD/closed.png | Bin 0 -> 132 bytes HEAD/cookie.js | 58 + .../dir_d44c64559bbebec7f509842c48db8b23.html | 97 + HEAD/doc.svg | 12 + HEAD/docd.svg | 12 + HEAD/doxygen-awesome-fragment-copy-button.js | 85 + HEAD/doxygen-awesome-interactive-toc.js | 91 + HEAD/doxygen-awesome-paragraph-link.js | 51 + HEAD/doxygen-awesome.css | 2681 ++++++++++++++++ HEAD/doxygen.css | 1850 +++++++++++ HEAD/doxygen.svg | 28 + HEAD/doxygen_crawl.html | 202 ++ HEAD/dynsections.js | 198 ++ HEAD/favicon.ico | Bin 0 -> 561229 bytes HEAD/folderclosed.svg | 11 + HEAD/folderclosedd.svg | 11 + HEAD/folderopen.svg | 17 + HEAD/folderopend.svg | 12 + HEAD/functions.html | 114 + HEAD/functions_vars.html | 114 + HEAD/graph_legend.html | 156 + HEAD/graph_legend.md5 | 1 + HEAD/graph_legend.svg | 167 + HEAD/group__jwks__core__grp.html | 542 ++++ HEAD/group__jwks__core__grp.js | 56 + HEAD/group__jwt__advanced__grp.html | 112 + HEAD/group__jwt__advanced__grp.js | 5 + HEAD/group__jwt__advanced__grp.map | 5 + HEAD/group__jwt__advanced__grp.md5 | 1 + HEAD/group__jwt__advanced__grp.svg | 77 + HEAD/group__jwt__advanced__grp_org.svg | 51 + HEAD/group__jwt__alg__grp.html | 243 ++ HEAD/group__jwt__alg__grp.js | 7 + HEAD/group__jwt__alg__grp.map | 4 + HEAD/group__jwt__alg__grp.md5 | 1 + HEAD/group__jwt__alg__grp.svg | 62 + HEAD/group__jwt__alg__grp_org.svg | 36 + HEAD/group__jwt__config__grp.html | 220 ++ HEAD/group__jwt__config__grp.js | 11 + HEAD/group__jwt__config__grp.map | 4 + HEAD/group__jwt__config__grp.md5 | 1 + HEAD/group__jwt__config__grp.svg | 62 + HEAD/group__jwt__config__grp_org.svg | 36 + HEAD/group__jwt__core__grp.html | 274 ++ HEAD/group__jwt__core__grp.js | 24 + HEAD/group__jwt__core__grp.map | 4 + HEAD/group__jwt__core__grp.md5 | 1 + HEAD/group__jwt__core__grp.svg | 62 + HEAD/group__jwt__core__grp_org.svg | 36 + HEAD/group__jwt__create__grp.html | 131 + HEAD/group__jwt__create__grp.js | 4 + HEAD/group__jwt__create__grp.map | 4 + HEAD/group__jwt__create__grp.md5 | 1 + HEAD/group__jwt__create__grp.svg | 62 + HEAD/group__jwt__create__grp_org.svg | 36 + HEAD/group__jwt__crypto__grp.html | 278 ++ HEAD/group__jwt__crypto__grp.js | 15 + HEAD/group__jwt__crypto__grp.map | 4 + HEAD/group__jwt__crypto__grp.md5 | 1 + HEAD/group__jwt__crypto__grp.svg | 62 + HEAD/group__jwt__crypto__grp_org.svg | 36 + HEAD/group__jwt__encode__grp.html | 304 ++ HEAD/group__jwt__encode__grp.js | 9 + HEAD/group__jwt__encode__grp.map | 4 + HEAD/group__jwt__encode__grp.md5 | 1 + HEAD/group__jwt__encode__grp.svg | 62 + HEAD/group__jwt__encode__grp_org.svg | 36 + HEAD/group__jwt__grant__grp.html | 431 +++ HEAD/group__jwt__grant__grp.js | 12 + HEAD/group__jwt__grant__grp.map | 4 + HEAD/group__jwt__grant__grp.md5 | 1 + HEAD/group__jwt__grant__grp.svg | 62 + HEAD/group__jwt__grant__grp_org.svg | 36 + HEAD/group__jwt__grp.html | 126 + HEAD/group__jwt__grp.js | 12 + HEAD/group__jwt__grp.map | 12 + HEAD/group__jwt__grp.md5 | 1 + HEAD/group__jwt__grp.svg | 182 ++ HEAD/group__jwt__grp_org.svg | 156 + HEAD/group__jwt__header__grp.html | 431 +++ HEAD/group__jwt__header__grp.js | 12 + HEAD/group__jwt__header__grp.map | 4 + HEAD/group__jwt__header__grp.md5 | 1 + HEAD/group__jwt__header__grp.svg | 62 + HEAD/group__jwt__header__grp_org.svg | 36 + HEAD/group__jwt__memory__grp.html | 250 ++ HEAD/group__jwt__memory__grp.js | 8 + HEAD/group__jwt__memory__grp.map | 4 + HEAD/group__jwt__memory__grp.md5 | 1 + HEAD/group__jwt__memory__grp.svg | 62 + HEAD/group__jwt__memory__grp_org.svg | 36 + HEAD/group__jwt__valid__grp.html | 856 +++++ HEAD/group__jwt__valid__grp.js | 36 + HEAD/group__jwt__valid__grp.map | 4 + HEAD/group__jwt__valid__grp.md5 | 1 + HEAD/group__jwt__valid__grp.svg | 62 + HEAD/group__jwt__valid__grp_org.svg | 36 + HEAD/group__jwt__verify__grp.html | 224 ++ HEAD/group__jwt__verify__grp.js | 5 + HEAD/group__jwt__verify__grp.map | 4 + HEAD/group__jwt__verify__grp.md5 | 1 + HEAD/group__jwt__verify__grp.svg | 62 + HEAD/group__jwt__verify__grp_org.svg | 36 + HEAD/index.html | 154 + HEAD/index.js | 15 + HEAD/index.qhp | 561 ++++ HEAD/jquery.js | 204 ++ HEAD/libjwt.css | 19 + HEAD/maclara-pill.svg | 1 + HEAD/minus.svg | 8 + HEAD/minusd.svg | 8 + HEAD/nav_f.png | Bin 0 -> 153 bytes HEAD/nav_fd.png | Bin 0 -> 169 bytes HEAD/nav_g.png | Bin 0 -> 95 bytes HEAD/nav_h.png | Bin 0 -> 98 bytes HEAD/nav_hd.png | Bin 0 -> 114 bytes HEAD/navtree.css | 149 + HEAD/navtree.js | 483 +++ HEAD/navtreedata.js | 48 + HEAD/navtreeindex0.js | 191 ++ HEAD/open.png | Bin 0 -> 123 bytes HEAD/pages.html | 101 + HEAD/plus.svg | 9 + HEAD/plusd.svg | 9 + HEAD/resize.js | 147 + HEAD/splitbar.png | Bin 0 -> 314 bytes HEAD/splitbard.png | Bin 0 -> 282 bytes HEAD/structjwk__item__t.html | 424 +++ HEAD/structjwk__item__t.js | 20 + HEAD/structjwt__config__t.html | 170 + HEAD/structjwt__config__t.js | 6 + HEAD/structjwt__config__t__coll__graph.map | 5 + HEAD/structjwt__config__t__coll__graph.md5 | 1 + HEAD/structjwt__config__t__coll__graph.svg | 66 + .../structjwt__config__t__coll__graph_org.svg | 40 + HEAD/svg.min.js | 46 + HEAD/sync_off.png | Bin 0 -> 853 bytes HEAD/sync_on.png | Bin 0 -> 845 bytes HEAD/tab_a.png | Bin 0 -> 142 bytes HEAD/tab_ad.png | Bin 0 -> 135 bytes HEAD/tab_b.png | Bin 0 -> 169 bytes HEAD/tab_bd.png | Bin 0 -> 173 bytes HEAD/tab_h.png | Bin 0 -> 177 bytes HEAD/tab_hd.png | Bin 0 -> 180 bytes HEAD/tab_s.png | Bin 0 -> 184 bytes HEAD/tab_sd.png | Bin 0 -> 188 bytes HEAD/tabs.css | 1 + HEAD/todo.html | 104 + HEAD/topics.html | 114 + HEAD/topics.js | 6 + INSTALL | 370 --- LICENSE | 373 +++ Makefile.am | 12 +- NEWS | 1 - README | 1 - README.cmake.md | 44 + README.md | 31 + annotated.html | 86 + annotated_dup.js | 4 + bc_s.png | Bin 0 -> 676 bytes bc_sd.png | Bin 0 -> 635 bytes classes.html | 87 + clipboard.js | 61 + closed.png | Bin 0 -> 132 bytes cmake/FindCheck.cmake | 56 + cmake/libjwtConfig.cmake.in | 4 + configure.ac | 97 +- cookie.js | 58 + dist/Makefile.am | 16 + dist/libjwt.spec.in | 82 + doc.svg | 12 + docd.svg | 12 + doxygen.cfg | 2829 +++++++++++++++++ doxygen.css | 2256 +++++++++++++ doxygen.mk | 188 ++ doxygen.svg | 28 + doxygen_crawl.html | 134 + dynsections.js | 205 ++ examples/.gitignore | 2 + examples/CMakeLists.txt | 36 + examples/Makefile.am | 16 + examples/main-auth.c | 166 + examples/main-gen.c | 164 + files.html | 86 + files_dup.js | 4 + folderclosed.svg | 11 + folderclosedd.svg | 11 + folderopen.svg | 17 + folderopend.svg | 12 + functions.html | 82 + functions_vars.html | 82 + globals.html | 180 ++ globals_defs.html | 93 + globals_enum.html | 81 + globals_eval.html | 96 + globals_func.html | 139 + globals_type.html | 87 + graph_legend.html | 141 + graph_legend.md5 | 1 + graph_legend.png | Bin 0 -> 22892 bytes group__jwt__alg.html | 240 ++ group__jwt__alg.js | 7 + group__jwt__crypto.html | 150 + group__jwt__crypto.js | 5 + group__jwt__encode.html | 311 ++ group__jwt__encode.js | 9 + group__jwt__grant.html | 450 +++ group__jwt__grant.js | 12 + group__jwt__header.html | 450 +++ group__jwt__header.js | 12 + group__jwt__memory.html | 179 ++ group__jwt__memory.js | 5 + group__jwt__new.html | 285 ++ group__jwt__new.js | 8 + group__jwt__validate.html | 830 +++++ group__jwt__validate.js | 23 + images/LibJWT.svg | 588 ++++ include/jwt.h | 1060 +++++- index.html | 82 + jquery.js | 204 ++ jwt_8h.html | 746 +++++ jwt_8h.js | 99 + jwt_8h__incl.map | 7 + jwt_8h__incl.md5 | 1 + jwt_8h__incl.png | Bin 0 -> 4272 bytes jwt_8h_source.html | 399 +++ libjwt/CMakeLists.txt | 117 + libjwt/Makefile.am | 29 +- libjwt/ccommon.h | 14 + libjwt/cdecode.c | 108 + libjwt/cdecode.h | 36 + libjwt/cencode.c | 177 ++ libjwt/cencode.h | 44 + libjwt/jwt-gnutls.c | 426 +++ libjwt/jwt-openssl.c | 540 ++++ libjwt/jwt-private.h | 67 + libjwt/jwt-wincrypt.c | 1015 ++++++ libjwt/jwt.c | 1787 ++++++++++- libjwt/libjwt.pc.in | 12 + m4/.keep | 0 m4/ax_code_coverage.m4 | 249 ++ m4/ax_valgrind_check.m4 | 236 ++ m4/doxygen.m4 | 321 ++ menu.js | 134 + menudata.js | 45 + minus.svg | 8 + minusd.svg | 8 + nav_f.png | Bin 0 -> 153 bytes nav_fd.png | Bin 0 -> 169 bytes nav_g.png | Bin 0 -> 95 bytes nav_h.png | Bin 0 -> 98 bytes nav_hd.png | Bin 0 -> 114 bytes navtree.css | 149 + navtree.js | 483 +++ navtreedata.js | 57 + navtreeindex0.js | 124 + open.png | Bin 0 -> 123 bytes plus.svg | 9 + plusd.svg | 9 + resize.js | 147 + splitbar.png | Bin 0 -> 314 bytes splitbard.png | Bin 0 -> 282 bytes structjwt__key__t.html | 137 + structjwt__key__t.js | 5 + sync_off.png | Bin 0 -> 853 bytes sync_on.png | Bin 0 -> 845 bytes tab_a.png | Bin 0 -> 142 bytes tab_ad.png | Bin 0 -> 135 bytes tab_b.png | Bin 0 -> 169 bytes tab_bd.png | Bin 0 -> 173 bytes tab_h.png | Bin 0 -> 177 bytes tab_hd.png | Bin 0 -> 180 bytes tab_s.png | Bin 0 -> 184 bytes tab_sd.png | Bin 0 -> 188 bytes tabs.css | 1 + tests/.gitignore | 17 + tests/CMakeLists.txt | 23 + tests/Makefile.am | 26 + tests/jwt_crypto.c | 51 + tests/jwt_dump.c | 386 +++ tests/jwt_ec.c | 241 ++ tests/jwt_eddsa.c | 90 + tests/jwt_encode.c | 396 +++ tests/jwt_es256k.c | 99 + tests/jwt_grant.c | 268 ++ tests/jwt_header.c | 268 ++ tests/jwt_new.c | 614 ++++ tests/jwt_rsa.c | 320 ++ tests/jwt_rsa_pss.c | 172 + tests/jwt_tests.h | 213 ++ tests/jwt_validate.c | 677 ++++ tests/keys/ec_key_invalid-pub.pem | 4 + tests/keys/ec_key_invalid.pem | 5 + tests/keys/ec_key_prime256v1-pub.pem | 4 + tests/keys/ec_key_prime256v1.pem | 5 + tests/keys/ec_key_secp256k1-pub.pem | 4 + tests/keys/ec_key_secp256k1.pem | 5 + tests/keys/ec_key_secp384r1-pub.pem | 5 + tests/keys/ec_key_secp384r1.pem | 6 + tests/keys/ec_key_secp521r1-pub.pem | 6 + tests/keys/ec_key_secp521r1.pem | 8 + tests/keys/eddsa_key_ed25519-pub.pem | 3 + tests/keys/eddsa_key_ed25519.pem | 3 + tests/keys/rsa-pss_key_2048-pub.pem | 9 + tests/keys/rsa-pss_key_2048.pem | 28 + tests/keys/rsa_key_2048-pub.pem | 9 + tests/keys/rsa_key_2048.pem | 28 + tests/keys/rsa_key_4096-pub.pem | 14 + tests/keys/rsa_key_4096.pem | 52 + tests/keys/rsa_key_8192-pub.pem | 25 + tests/keys/rsa_key_8192.pem | 100 + tests/keys/rsa_key_i37-pub.pem | 9 + tests/keys/rsa_key_invalid-pub.pem | 6 + tests/keys/rsa_key_invalid.pem | 17 + tests/misc/ruby-eddsa.rb | 22 + topics.html | 93 + topics.js | 11 + 332 files changed, 44697 insertions(+), 943 deletions(-) delete mode 100644 .gitignore delete mode 100644 AUTHORS create mode 100644 CMakeLists.txt delete mode 100644 COPYING.LIB delete mode 100644 ChangeLog create mode 100644 HEAD/Info.plist create mode 100644 HEAD/LibJWT.svg create mode 100644 HEAD/Makefile create mode 100644 HEAD/Nodes.xml create mode 100644 HEAD/Tokens.xml create mode 100644 HEAD/annotated.html create mode 100644 HEAD/annotated_dup.js create mode 100644 HEAD/bc_s.png create mode 100644 HEAD/bc_sd.png create mode 100644 HEAD/classes.html create mode 100644 HEAD/closed.png create mode 100644 HEAD/cookie.js create mode 100644 HEAD/dir_d44c64559bbebec7f509842c48db8b23.html create mode 100644 HEAD/doc.svg create mode 100644 HEAD/docd.svg create mode 100644 HEAD/doxygen-awesome-fragment-copy-button.js create mode 100644 HEAD/doxygen-awesome-interactive-toc.js create mode 100644 HEAD/doxygen-awesome-paragraph-link.js create mode 100644 HEAD/doxygen-awesome.css create mode 100644 HEAD/doxygen.css create mode 100644 HEAD/doxygen.svg create mode 100644 HEAD/doxygen_crawl.html create mode 100644 HEAD/dynsections.js create mode 100644 HEAD/favicon.ico create mode 100644 HEAD/folderclosed.svg create mode 100644 HEAD/folderclosedd.svg create mode 100644 HEAD/folderopen.svg create mode 100644 HEAD/folderopend.svg create mode 100644 HEAD/functions.html create mode 100644 HEAD/functions_vars.html create mode 100644 HEAD/graph_legend.html create mode 100644 HEAD/graph_legend.md5 create mode 100644 HEAD/graph_legend.svg create mode 100644 HEAD/group__jwks__core__grp.html create mode 100644 HEAD/group__jwks__core__grp.js create mode 100644 HEAD/group__jwt__advanced__grp.html create mode 100644 HEAD/group__jwt__advanced__grp.js create mode 100644 HEAD/group__jwt__advanced__grp.map create mode 100644 HEAD/group__jwt__advanced__grp.md5 create mode 100644 HEAD/group__jwt__advanced__grp.svg create mode 100644 HEAD/group__jwt__advanced__grp_org.svg create mode 100644 HEAD/group__jwt__alg__grp.html create mode 100644 HEAD/group__jwt__alg__grp.js create mode 100644 HEAD/group__jwt__alg__grp.map create mode 100644 HEAD/group__jwt__alg__grp.md5 create mode 100644 HEAD/group__jwt__alg__grp.svg create mode 100644 HEAD/group__jwt__alg__grp_org.svg create mode 100644 HEAD/group__jwt__config__grp.html create mode 100644 HEAD/group__jwt__config__grp.js create mode 100644 HEAD/group__jwt__config__grp.map create mode 100644 HEAD/group__jwt__config__grp.md5 create mode 100644 HEAD/group__jwt__config__grp.svg create mode 100644 HEAD/group__jwt__config__grp_org.svg create mode 100644 HEAD/group__jwt__core__grp.html create mode 100644 HEAD/group__jwt__core__grp.js create mode 100644 HEAD/group__jwt__core__grp.map create mode 100644 HEAD/group__jwt__core__grp.md5 create mode 100644 HEAD/group__jwt__core__grp.svg create mode 100644 HEAD/group__jwt__core__grp_org.svg create mode 100644 HEAD/group__jwt__create__grp.html create mode 100644 HEAD/group__jwt__create__grp.js create mode 100644 HEAD/group__jwt__create__grp.map create mode 100644 HEAD/group__jwt__create__grp.md5 create mode 100644 HEAD/group__jwt__create__grp.svg create mode 100644 HEAD/group__jwt__create__grp_org.svg create mode 100644 HEAD/group__jwt__crypto__grp.html create mode 100644 HEAD/group__jwt__crypto__grp.js create mode 100644 HEAD/group__jwt__crypto__grp.map create mode 100644 HEAD/group__jwt__crypto__grp.md5 create mode 100644 HEAD/group__jwt__crypto__grp.svg create mode 100644 HEAD/group__jwt__crypto__grp_org.svg create mode 100644 HEAD/group__jwt__encode__grp.html create mode 100644 HEAD/group__jwt__encode__grp.js create mode 100644 HEAD/group__jwt__encode__grp.map create mode 100644 HEAD/group__jwt__encode__grp.md5 create mode 100644 HEAD/group__jwt__encode__grp.svg create mode 100644 HEAD/group__jwt__encode__grp_org.svg create mode 100644 HEAD/group__jwt__grant__grp.html create mode 100644 HEAD/group__jwt__grant__grp.js create mode 100644 HEAD/group__jwt__grant__grp.map create mode 100644 HEAD/group__jwt__grant__grp.md5 create mode 100644 HEAD/group__jwt__grant__grp.svg create mode 100644 HEAD/group__jwt__grant__grp_org.svg create mode 100644 HEAD/group__jwt__grp.html create mode 100644 HEAD/group__jwt__grp.js create mode 100644 HEAD/group__jwt__grp.map create mode 100644 HEAD/group__jwt__grp.md5 create mode 100644 HEAD/group__jwt__grp.svg create mode 100644 HEAD/group__jwt__grp_org.svg create mode 100644 HEAD/group__jwt__header__grp.html create mode 100644 HEAD/group__jwt__header__grp.js create mode 100644 HEAD/group__jwt__header__grp.map create mode 100644 HEAD/group__jwt__header__grp.md5 create mode 100644 HEAD/group__jwt__header__grp.svg create mode 100644 HEAD/group__jwt__header__grp_org.svg create mode 100644 HEAD/group__jwt__memory__grp.html create mode 100644 HEAD/group__jwt__memory__grp.js create mode 100644 HEAD/group__jwt__memory__grp.map create mode 100644 HEAD/group__jwt__memory__grp.md5 create mode 100644 HEAD/group__jwt__memory__grp.svg create mode 100644 HEAD/group__jwt__memory__grp_org.svg create mode 100644 HEAD/group__jwt__valid__grp.html create mode 100644 HEAD/group__jwt__valid__grp.js create mode 100644 HEAD/group__jwt__valid__grp.map create mode 100644 HEAD/group__jwt__valid__grp.md5 create mode 100644 HEAD/group__jwt__valid__grp.svg create mode 100644 HEAD/group__jwt__valid__grp_org.svg create mode 100644 HEAD/group__jwt__verify__grp.html create mode 100644 HEAD/group__jwt__verify__grp.js create mode 100644 HEAD/group__jwt__verify__grp.map create mode 100644 HEAD/group__jwt__verify__grp.md5 create mode 100644 HEAD/group__jwt__verify__grp.svg create mode 100644 HEAD/group__jwt__verify__grp_org.svg create mode 100644 HEAD/index.html create mode 100644 HEAD/index.js create mode 100644 HEAD/index.qhp create mode 100644 HEAD/jquery.js create mode 100644 HEAD/libjwt.css create mode 100644 HEAD/maclara-pill.svg create mode 100644 HEAD/minus.svg create mode 100644 HEAD/minusd.svg create mode 100644 HEAD/nav_f.png create mode 100644 HEAD/nav_fd.png create mode 100644 HEAD/nav_g.png create mode 100644 HEAD/nav_h.png create mode 100644 HEAD/nav_hd.png create mode 100644 HEAD/navtree.css create mode 100644 HEAD/navtree.js create mode 100644 HEAD/navtreedata.js create mode 100644 HEAD/navtreeindex0.js create mode 100644 HEAD/open.png create mode 100644 HEAD/pages.html create mode 100644 HEAD/plus.svg create mode 100644 HEAD/plusd.svg create mode 100644 HEAD/resize.js create mode 100644 HEAD/splitbar.png create mode 100644 HEAD/splitbard.png create mode 100644 HEAD/structjwk__item__t.html create mode 100644 HEAD/structjwk__item__t.js create mode 100644 HEAD/structjwt__config__t.html create mode 100644 HEAD/structjwt__config__t.js create mode 100644 HEAD/structjwt__config__t__coll__graph.map create mode 100644 HEAD/structjwt__config__t__coll__graph.md5 create mode 100644 HEAD/structjwt__config__t__coll__graph.svg create mode 100644 HEAD/structjwt__config__t__coll__graph_org.svg create mode 100644 HEAD/svg.min.js create mode 100644 HEAD/sync_off.png create mode 100644 HEAD/sync_on.png create mode 100644 HEAD/tab_a.png create mode 100644 HEAD/tab_ad.png create mode 100644 HEAD/tab_b.png create mode 100644 HEAD/tab_bd.png create mode 100644 HEAD/tab_h.png create mode 100644 HEAD/tab_hd.png create mode 100644 HEAD/tab_s.png create mode 100644 HEAD/tab_sd.png create mode 100644 HEAD/tabs.css create mode 100644 HEAD/todo.html create mode 100644 HEAD/topics.html create mode 100644 HEAD/topics.js delete mode 100644 INSTALL create mode 100644 LICENSE delete mode 100644 NEWS delete mode 100644 README create mode 100644 README.cmake.md create mode 100644 README.md create mode 100644 annotated.html create mode 100644 annotated_dup.js create mode 100644 bc_s.png create mode 100644 bc_sd.png create mode 100644 classes.html create mode 100644 clipboard.js create mode 100644 closed.png create mode 100644 cmake/FindCheck.cmake create mode 100644 cmake/libjwtConfig.cmake.in create mode 100644 cookie.js create mode 100644 dist/Makefile.am create mode 100644 dist/libjwt.spec.in create mode 100644 doc.svg create mode 100644 docd.svg create mode 100644 doxygen.cfg create mode 100644 doxygen.css create mode 100644 doxygen.mk create mode 100644 doxygen.svg create mode 100644 doxygen_crawl.html create mode 100644 dynsections.js create mode 100644 examples/.gitignore create mode 100644 examples/CMakeLists.txt create mode 100644 examples/Makefile.am create mode 100644 examples/main-auth.c create mode 100644 examples/main-gen.c create mode 100644 files.html create mode 100644 files_dup.js create mode 100644 folderclosed.svg create mode 100644 folderclosedd.svg create mode 100644 folderopen.svg create mode 100644 folderopend.svg create mode 100644 functions.html create mode 100644 functions_vars.html create mode 100644 globals.html create mode 100644 globals_defs.html create mode 100644 globals_enum.html create mode 100644 globals_eval.html create mode 100644 globals_func.html create mode 100644 globals_type.html create mode 100644 graph_legend.html create mode 100644 graph_legend.md5 create mode 100644 graph_legend.png create mode 100644 group__jwt__alg.html create mode 100644 group__jwt__alg.js create mode 100644 group__jwt__crypto.html create mode 100644 group__jwt__crypto.js create mode 100644 group__jwt__encode.html create mode 100644 group__jwt__encode.js create mode 100644 group__jwt__grant.html create mode 100644 group__jwt__grant.js create mode 100644 group__jwt__header.html create mode 100644 group__jwt__header.js create mode 100644 group__jwt__memory.html create mode 100644 group__jwt__memory.js create mode 100644 group__jwt__new.html create mode 100644 group__jwt__new.js create mode 100644 group__jwt__validate.html create mode 100644 group__jwt__validate.js create mode 100644 images/LibJWT.svg create mode 100644 index.html create mode 100644 jquery.js create mode 100644 jwt_8h.html create mode 100644 jwt_8h.js create mode 100644 jwt_8h__incl.map create mode 100644 jwt_8h__incl.md5 create mode 100644 jwt_8h__incl.png create mode 100644 jwt_8h_source.html create mode 100644 libjwt/CMakeLists.txt create mode 100644 libjwt/ccommon.h create mode 100644 libjwt/cdecode.c create mode 100644 libjwt/cdecode.h create mode 100644 libjwt/cencode.c create mode 100644 libjwt/cencode.h create mode 100644 libjwt/jwt-gnutls.c create mode 100644 libjwt/jwt-openssl.c create mode 100644 libjwt/jwt-private.h create mode 100644 libjwt/jwt-wincrypt.c create mode 100644 libjwt/libjwt.pc.in delete mode 100644 m4/.keep create mode 100644 m4/ax_code_coverage.m4 create mode 100644 m4/ax_valgrind_check.m4 create mode 100644 m4/doxygen.m4 create mode 100644 menu.js create mode 100644 menudata.js create mode 100644 minus.svg create mode 100644 minusd.svg create mode 100644 nav_f.png create mode 100644 nav_fd.png create mode 100644 nav_g.png create mode 100644 nav_h.png create mode 100644 nav_hd.png create mode 100644 navtree.css create mode 100644 navtree.js create mode 100644 navtreedata.js create mode 100644 navtreeindex0.js create mode 100644 open.png create mode 100644 plus.svg create mode 100644 plusd.svg create mode 100644 resize.js create mode 100644 splitbar.png create mode 100644 splitbard.png create mode 100644 structjwt__key__t.html create mode 100644 structjwt__key__t.js create mode 100644 sync_off.png create mode 100644 sync_on.png create mode 100644 tab_a.png create mode 100644 tab_ad.png create mode 100644 tab_b.png create mode 100644 tab_bd.png create mode 100644 tab_h.png create mode 100644 tab_hd.png create mode 100644 tab_s.png create mode 100644 tab_sd.png create mode 100644 tabs.css create mode 100644 tests/.gitignore create mode 100644 tests/CMakeLists.txt create mode 100644 tests/Makefile.am create mode 100644 tests/jwt_crypto.c create mode 100644 tests/jwt_dump.c create mode 100644 tests/jwt_ec.c create mode 100644 tests/jwt_eddsa.c create mode 100644 tests/jwt_encode.c create mode 100644 tests/jwt_es256k.c create mode 100644 tests/jwt_grant.c create mode 100644 tests/jwt_header.c create mode 100644 tests/jwt_new.c create mode 100644 tests/jwt_rsa.c create mode 100644 tests/jwt_rsa_pss.c create mode 100644 tests/jwt_tests.h create mode 100644 tests/jwt_validate.c create mode 100644 tests/keys/ec_key_invalid-pub.pem create mode 100644 tests/keys/ec_key_invalid.pem create mode 100644 tests/keys/ec_key_prime256v1-pub.pem create mode 100644 tests/keys/ec_key_prime256v1.pem create mode 100644 tests/keys/ec_key_secp256k1-pub.pem create mode 100644 tests/keys/ec_key_secp256k1.pem create mode 100644 tests/keys/ec_key_secp384r1-pub.pem create mode 100644 tests/keys/ec_key_secp384r1.pem create mode 100644 tests/keys/ec_key_secp521r1-pub.pem create mode 100644 tests/keys/ec_key_secp521r1.pem create mode 100644 tests/keys/eddsa_key_ed25519-pub.pem create mode 100644 tests/keys/eddsa_key_ed25519.pem create mode 100644 tests/keys/rsa-pss_key_2048-pub.pem create mode 100644 tests/keys/rsa-pss_key_2048.pem create mode 100644 tests/keys/rsa_key_2048-pub.pem create mode 100644 tests/keys/rsa_key_2048.pem create mode 100644 tests/keys/rsa_key_4096-pub.pem create mode 100644 tests/keys/rsa_key_4096.pem create mode 100644 tests/keys/rsa_key_8192-pub.pem create mode 100644 tests/keys/rsa_key_8192.pem create mode 100644 tests/keys/rsa_key_i37-pub.pem create mode 100644 tests/keys/rsa_key_invalid-pub.pem create mode 100644 tests/keys/rsa_key_invalid.pem create mode 100755 tests/misc/ruby-eddsa.rb create mode 100644 topics.html create mode 100644 topics.js diff --git a/.gitignore b/.gitignore deleted file mode 100644 index d653d8e5..00000000 --- a/.gitignore +++ /dev/null @@ -1,18 +0,0 @@ -Makefile.in -Makefile -/autom4te.cache -/aclocal.m4 -/compile -/configure -/depcomp -/install-sh -/missing -/stamp-h1 -/m4/*.m4 -/config.guess -/config.sub -/config.log -/config.status -/ltmain.sh -/libtool -/libjwt/.deps/* diff --git a/AUTHORS b/AUTHORS deleted file mode 100644 index 29db78bd..00000000 --- a/AUTHORS +++ /dev/null @@ -1 +0,0 @@ -Ben Collins diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..94b4aab8 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,108 @@ +cmake_minimum_required (VERSION 3.7...3.15) + +set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +if (${CMAKE_VERSION} VERSION_LESS 3.14.7 AND ${CMAKE_GENERATOR} STREQUAL "Visual Studio 16 2019") + message(FATAL_ERROR "Please update your version of CMake to version 3.14.7 or greater in order to use Visual Studio 2019") +endif() + +if (MSVC) + set (CMAKE_SYSTEM_VERSION 8.1 CACHE TYPE INTERNAL FORCE) +endif () + +project (jwt) + +string(REPLACE "\\" "/" CMAKE_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}) + +option (ENABLE_DEBUG_INFO_IN_RELEASE "Enable adding debug info to release build." OFF) +option (ENABLE_LTO "Enable link time optimizations." OFF) +option (BUILD_EXAMPLES "Build example projects." ON) + +if (MSVC) + option (STATIC_RUNTIME "Link runtome library statically." OFF) +endif () + +if (UNIX) + option (BUILD_TESTS "Build test projects." OFF) +endif () + +if (APPLE) + add_definitions(-DNO_WEAK_ALIASES) +endif () +include_directories (include) + +if (MSVC) + set (CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /Ox") +endif () + +if (STATIC_RUNTIME) + set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /MTd") + set (CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MT") + set (CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL} /MT") + set (CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} /MT") +endif () + +if (ENABLE_DEBUG_INFO_IN_RELEASE) + if (MSVC) + set (CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /Zi") + set (CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /DEBUG /OPT:REF /OPT:ICF") + set (CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG /OPT:REF /OPT:ICF") + else () + set (CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -g") + endif () +endif () + +if (ENABLE_LTO) + if (MSVC) + set (CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /GL") + set (CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL} /GL") + set (CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} /GL") + + set (CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG") + set (CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "${CMAKE_EXE_LINKER_FLAGS_MINSIZEREL} /LTCG") + set (CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} /LTCG") + + set (CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /LTCG") + set (CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL "${CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL} /LTCG") + set (CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO} /LTCG") + endif () +endif () + +add_subdirectory (libjwt) + +if (${BUILD_EXAMPLES}) + add_subdirectory (examples) +endif() + +if (${BUILD_TESTS}) + add_subdirectory (tests) +endif () + + +# extract version from configure.ac +FILE(READ "${CMAKE_CURRENT_SOURCE_DIR}/configure.ac" configure_ac_contents) +STRING(REGEX REPLACE ";" "\\\\;" configure_ac_contents "${configure_ac_contents}") +STRING(REGEX REPLACE "\n" ";" configure_ac_contents "${configure_ac_contents}") +foreach(configure_ac_line ${configure_ac_contents}) + if("${configure_ac_line}" MATCHES "^[ ]*AC_INIT") + string(REGEX MATCH "[0-9]+\.[0-9]+\.[0-9]+" version_string "${configure_ac_line}") + message(STATUS "version from configure.ac: '${version_string}'") + break() + endif() +endforeach() + +set(INCLUDE_INSTALL_DIR include/) +set(LIB_INSTALL_DIR lib/) +set(LIBJWT_VERSION ${version_string}) +include(CMakePackageConfigHelpers) +configure_package_config_file(cmake/libjwtConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/libjwtConfig.cmake + INSTALL_DESTINATION ${LIB_INSTALL_DIR}/libjwt/cmake + PATH_VARS INCLUDE_INSTALL_DIR) +write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/libjwtConfigVersion.cmake + VERSION ${LIBJWT_VERSION} + COMPATIBILITY SameMajorVersion ) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libjwtConfig.cmake + ${CMAKE_CURRENT_BINARY_DIR}/libjwtConfigVersion.cmake + DESTINATION ${LIB_INSTALL_DIR}/libjwt/cmake ) diff --git a/COPYING.LIB b/COPYING.LIB deleted file mode 100644 index ba2be481..00000000 --- a/COPYING.LIB +++ /dev/null @@ -1,515 +0,0 @@ - - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations -below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. -^L - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it -becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. -^L - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control -compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. -^L - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. -^L - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. -^L - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. -^L - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply, and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License -may add an explicit geographical distribution limitation excluding those -countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. -^L - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS -^L - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms -of the ordinary General Public License). - - To apply these terms, attach the following notices to the library. -It is safest to attach them to the start of each source file to most -effectively convey the exclusion of warranty; and each file should -have at least the "copyright" line and a pointer to where the full -notice is found. - - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper -mail. - -You should also get your employer (if you work as a programmer) or -your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James -Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! - - diff --git a/ChangeLog b/ChangeLog deleted file mode 100644 index b7a3d4a2..00000000 --- a/ChangeLog +++ /dev/null @@ -1 +0,0 @@ -Please see http://github.com/benmcollins/jwt diff --git a/HEAD/Info.plist b/HEAD/Info.plist new file mode 100644 index 00000000..985ee133 --- /dev/null +++ b/HEAD/Info.plist @@ -0,0 +1,25 @@ + + + + + CFBundleName + LibJWT + CFBundleIdentifier + com.maclara-llc.LibJWT + CFBundleVersion + 2.9.99 + DocSetFeedName + Docs provided by maClara, LLC + DocSetFeedUrl + FeedUrl + DocSetPublisherIdentifier + com.maclara-llc.DocPublish + DocSetPublisherName + maClara, LLC + DashDocSetFamily + doxy + DocSetPlatformFamily + doxygen + + diff --git a/HEAD/LibJWT.svg b/HEAD/LibJWT.svg new file mode 100644 index 00000000..49a45995 --- /dev/null +++ b/HEAD/LibJWT.svg @@ -0,0 +1,588 @@ + +image/svg+xmlL +I +B +O +N +E +F +A +R +E + \ No newline at end of file diff --git a/HEAD/Makefile b/HEAD/Makefile new file mode 100644 index 00000000..660fb0b2 --- /dev/null +++ b/HEAD/Makefile @@ -0,0 +1,38 @@ +DOCSET_NAME=com.maclara-llc.LibJWT.docset +DOCSET_CONTENTS=$(DOCSET_NAME)/Contents +DOCSET_RESOURCES=$(DOCSET_CONTENTS)/Resources +DOCSET_DOCUMENTS=$(DOCSET_RESOURCES)/Documents +DESTDIR=~/Library/Developer/Shared/Documentation/DocSets +XCODE_INSTALL="$(shell xcode-select -print-path)" + +all: docset + +docset: + mkdir -p $(DOCSET_DOCUMENTS) + cp Nodes.xml $(DOCSET_RESOURCES) + cp Tokens.xml $(DOCSET_RESOURCES) + cp Info.plist $(DOCSET_CONTENTS) + tar --exclude $(DOCSET_NAME) \ + --exclude Nodes.xml \ + --exclude Tokens.xml \ + --exclude Info.plist \ + --exclude Makefile -c -f - . \ + | (cd $(DOCSET_DOCUMENTS); tar xvf -) + $(XCODE_INSTALL)/usr/bin/docsetutil index $(DOCSET_NAME) + rm -f $(DOCSET_DOCUMENTS)/Nodes.xml + rm -f $(DOCSET_DOCUMENTS)/Info.plist + rm -f $(DOCSET_DOCUMENTS)/Makefile + rm -f $(DOCSET_RESOURCES)/Nodes.xml + rm -f $(DOCSET_RESOURCES)/Tokens.xml + +clean: + rm -rf $(DOCSET_NAME) + +install: docset + mkdir -p $(DESTDIR) + cp -R $(DOCSET_NAME) $(DESTDIR) + +uninstall: + rm -rf $(DESTDIR)/$(DOCSET_NAME) + +always: diff --git a/HEAD/Nodes.xml b/HEAD/Nodes.xml new file mode 100644 index 00000000..0855be4c --- /dev/null +++ b/HEAD/Nodes.xml @@ -0,0 +1,1096 @@ + + + + + Root + index.html + + + Welcome to LibJWT + index.html + + + \u{1f4a1} Supported Standards + index.html + standards + + + \u{1f6a7} Build Prerequisites + index.html + building + + + Required + index.html + req + + + One or more of these + index.html + req_crypto + + + Optional + index.html + optional + + + + + \u{1f4d6} Docs and Source + index.html + docs + + + \u{1f4e6} Pre-built Packages + index.html + prebuilt + + + \u{1f528} Build Instructions + index.html + instructions + + + With CMake: + index.html + cmake + + + Extra Build Info + index.html + extra + + + + + + + Todo List + todo.html + + + Topics + topics.html + + + JSON Web Token + group__jwt__grp.html + + + Object Creation + group__jwt__core__grp.html + + + jwt_t + group__jwt__core__grp.html + gabf0acfb990d245214216af589c1d0947 + + + jwt_alg_t + group__jwt__core__grp.html + ga889db178d8714ab67bc994f1d8faad33 + + + JWT_ALG_NONE + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33a3249a71140a0bb4b3cb196fe15114b89 + + + JWT_ALG_HS256 + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33aaa9217f4c8f5266d5401402f0ec98604 + + + JWT_ALG_HS384 + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33af0adc69b9caa89fbc2eeaf744d7cf4f2 + + + JWT_ALG_HS512 + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33a596b0da85c01e17b43a6574f82159081 + + + JWT_ALG_RS256 + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33aa03ba981baa4b1cda3b9de1347ada853 + + + JWT_ALG_RS384 + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33a520814a2695a6df472bb408ba6feb206 + + + JWT_ALG_RS512 + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33ab4d732eef3f6bee21201d638dfd8ca44 + + + JWT_ALG_ES256 + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33a84f9bf1cb93f6ae622a9de57d9c67472 + + + JWT_ALG_ES384 + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33ae18e69e6b629aa0d3e6d16ec4a95c708 + + + JWT_ALG_ES512 + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33a87205e19f1dde7c3cee012424482e106 + + + JWT_ALG_PS256 + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33ad0216cbdf24f6b486d5082294b3cfc5b + + + JWT_ALG_PS384 + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33a3ad3c7e704886dd61dee0de5fa02072d + + + JWT_ALG_PS512 + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33a727c838b47ad911681eb029cc8b55744 + + + JWT_ALG_ES256K + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33a7f760cf9ed49b80ecb78580ed381ff01 + + + JWT_ALG_EDDSA + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33aab33738e67e3b23db72a3bc77043009b + + + JWT_ALG_INVAL + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33a199f5ce0e69c0160cc121cf6dbe9cb58 + + + + + jwt_dup + group__jwt__core__grp.html + gac11d4548c695e5781cae2a5148a7e240 + + + jwt_free + group__jwt__core__grp.html + gafd51b076050815be44081439d1cb33fc + + + + + Configuration Type + group__jwt__config__grp.html + + + jwt_config_t + structjwt__config__t.html + + + alg + structjwt__config__t.html + aa1f5e081bba22bd5e0e90962b55d3305 + + + ctx + structjwt__config__t.html + a5b20a944b428896447108820ca861117 + + + jw_key + structjwt__config__t.html + abc613eedbd07027e1aed03baaf7b7a93 + + + + + JWT_CONFIG_DECLARE + group__jwt__config__grp.html + ga0816f65962b32e875a2ed9b6f0166bee + + + jwt_callback_t + group__jwt__config__grp.html + ga44edddf2ce7034752c2b22283ad1010d + + + jwt_config_init + group__jwt__config__grp.html + ga08ab7f9e1917072c0910ee5335d3b04a + + + + + Create a JWT + group__jwt__create__grp.html + + + jwt_create + group__jwt__create__grp.html + ga7d5f4d5a83505c06faf9728bd7911908 + + + + + Token Verification + group__jwt__verify__grp.html + + + jwt_verify + group__jwt__verify__grp.html + gab0535d292e64c3688085cf0139aa8c8d + + + jwt_verify_wcb + group__jwt__verify__grp.html + gaddab273e09a8327e03f458be1cfb83b4 + + + + + Grant Management + group__jwt__grant__grp.html + + + jwt_add_grant + group__jwt__grant__grp.html + ga91d9bfe88d3e7b246ee67d2ed4609955 + + + jwt_add_grant_bool + group__jwt__grant__grp.html + gad528aabec09be8d051485a45c49781c3 + + + jwt_add_grant_int + group__jwt__grant__grp.html + gae4cdc69b259a0f2f98e3c07bf9038765 + + + jwt_add_grants_json + group__jwt__grant__grp.html + ga0509c745c45eebb41c278cebb0617a55 + + + jwt_del_grants + group__jwt__grant__grp.html + ga0d8da61021d73817fb71bf8ffb7791b7 + + + jwt_get_grant + group__jwt__grant__grp.html + ga2131ddd80d3c138766573b87310bdb6e + + + jwt_get_grant_bool + group__jwt__grant__grp.html + ga782d0fb87e79706e705ea5a989024abd + + + jwt_get_grant_int + group__jwt__grant__grp.html + gafac50d1449e4ea9560258a68a07cfdda + + + jwt_get_grants_json + group__jwt__grant__grp.html + ga5445b6af33ae868b763ae8d2139c089f + + + + + Header Hanagement + group__jwt__header__grp.html + + + jwt_add_header + group__jwt__header__grp.html + ga1c0093b5b221f78bbd4e102d30870a27 + + + jwt_add_header_bool + group__jwt__header__grp.html + ga337c85106951204af60f40288da0a7d8 + + + jwt_add_header_int + group__jwt__header__grp.html + ga250b312a2c57fa5664f3921950c88766 + + + jwt_add_headers_json + group__jwt__header__grp.html + ga283f3dc2d9686e4db73874ae9f446ac1 + + + jwt_del_headers + group__jwt__header__grp.html + gae5ebb283a76098e9a99f7fe13b18593f + + + jwt_get_header + group__jwt__header__grp.html + ga5563efb29cdd4bfe078a3e82fa6a873a + + + jwt_get_header_bool + group__jwt__header__grp.html + gacb7171946cdc613c4800c1c0b279af4b + + + jwt_get_header_int + group__jwt__header__grp.html + gaadd81a21b63c01106bd7943992687199 + + + jwt_get_headers_json + group__jwt__header__grp.html + ga7fffc3eb52315f54605dad87fbdb52cb + + + + + Encoding and Output + group__jwt__encode__grp.html + + + jwt_dump_fp + group__jwt__encode__grp.html + gacc77ae9e2214c9b07352a400758f1f4d + + + jwt_dump_grants_str + group__jwt__encode__grp.html + ga219c406420f7f92960735433c69a304e + + + jwt_dump_str + group__jwt__encode__grp.html + ga1bf8a1df0f944c027d40677404778290 + + + jwt_encode_fp + group__jwt__encode__grp.html + ga2782abdbb190b626a5bd4eb3943b7b8d + + + jwt_encode_str + group__jwt__encode__grp.html + ga124ccd21e6495dbbd1012f60d74202dd + + + jwt_free_str + group__jwt__encode__grp.html + ga1c6ff5f00e8722f5522fe7b36697ecdd + + + + + Algorithm Management + group__jwt__alg__grp.html + + + jwt_alg_str + group__jwt__alg__grp.html + gaa906a6a1c27a3f9f6500240f7b228123 + + + jwt_get_alg + group__jwt__alg__grp.html + ga3e9cb111d566f55017d86d22dc545225 + + + jwt_set_alg + group__jwt__alg__grp.html + ga4b2fe3a4afc420fa7fa9feecebf2c4d7 + + + jwt_str_alg + group__jwt__alg__grp.html + gaf15787af42ab0fc567a0a1fc5ef3edc6 + + + + + Validation Functions + group__jwt__valid__grp.html + + + jwt_valid_t + group__jwt__valid__grp.html + gac413377fbfc11671e8a8a66f45693e5d + + + jwt_valid_exception_t + group__jwt__valid__grp.html + ga47ee94f9c08ff581f78590bfe024f6e4 + + + JWT_VALIDATION_SUCCESS + group__jwt__valid__grp.html + gga47ee94f9c08ff581f78590bfe024f6e4a94bb9121f597e706e89322b0f9a7330b + + + JWT_VALIDATION_ERROR + group__jwt__valid__grp.html + gga47ee94f9c08ff581f78590bfe024f6e4ad2e58b1f8a8bc39518b1c973fa99ee6e + + + JWT_VALIDATION_ALG_MISMATCH + group__jwt__valid__grp.html + gga47ee94f9c08ff581f78590bfe024f6e4abf0dc5dd94b521a118485a9522cd8cea + + + JWT_VALIDATION_EXPIRED + group__jwt__valid__grp.html + gga47ee94f9c08ff581f78590bfe024f6e4a698be61a7f4bcf0dfd9894e1c6296e0b + + + JWT_VALIDATION_TOO_NEW + group__jwt__valid__grp.html + gga47ee94f9c08ff581f78590bfe024f6e4a685c5270c214bf2484b65376e4b7459b + + + JWT_VALIDATION_ISS_MISMATCH + group__jwt__valid__grp.html + gga47ee94f9c08ff581f78590bfe024f6e4a0dde2793247cd11fbf017212d8fd605f + + + JWT_VALIDATION_SUB_MISMATCH + group__jwt__valid__grp.html + gga47ee94f9c08ff581f78590bfe024f6e4ac8b2989c55deb3bdad631c61f1ed304d + + + JWT_VALIDATION_AUD_MISMATCH + group__jwt__valid__grp.html + gga47ee94f9c08ff581f78590bfe024f6e4acb9d53e3cc20a39c0827d77a071271e6 + + + JWT_VALIDATION_GRANT_MISSING + group__jwt__valid__grp.html + gga47ee94f9c08ff581f78590bfe024f6e4a94eb7551ec626004a55798ecbd563d38 + + + JWT_VALIDATION_GRANT_MISMATCH + group__jwt__valid__grp.html + gga47ee94f9c08ff581f78590bfe024f6e4a5b1db073c57082b557592b7115a8e363 + + + + + jwt_exception_str + group__jwt__valid__grp.html + ga8371a7299dab709fa28fba7271d03a14 + + + jwt_valid_add_grant + group__jwt__valid__grp.html + gab5e9e2dc5e0276ea2c21c050822c19e1 + + + jwt_valid_add_grant_bool + group__jwt__valid__grp.html + gafa57cedcecda005a47161c462b2f0c9f + + + jwt_valid_add_grant_int + group__jwt__valid__grp.html + ga50c04450cc57bcd18717c25f18aed479 + + + jwt_valid_add_grants_json + group__jwt__valid__grp.html + ga9361112f9372d8de2f11c115314c2d71 + + + jwt_valid_del_grants + group__jwt__valid__grp.html + ga929d87f2577cbb600de468b159d9f00d + + + jwt_valid_free + group__jwt__valid__grp.html + ga77db16a4a29ef6a607d6aff8012955b2 + + + jwt_valid_get_exp_leeway + group__jwt__valid__grp.html + ga0dc43278963ec6474d5d7263c960e69c + + + jwt_valid_get_grant + group__jwt__valid__grp.html + ga03b176d20c41a85a979c9671a9a02b6f + + + jwt_valid_get_grant_bool + group__jwt__valid__grp.html + gabe7ce52fca00463c2b70aa367037db98 + + + jwt_valid_get_grant_int + group__jwt__valid__grp.html + ga98a643ad3c8103539dee35c22a3adbb6 + + + jwt_valid_get_grants_json + group__jwt__valid__grp.html + gacafaf1f062cd358bda89601570d194ad + + + jwt_valid_get_nbf_leeway + group__jwt__valid__grp.html + gafa9c96838053831dd239824ed5149879 + + + jwt_valid_get_status + group__jwt__valid__grp.html + ga165d7857282f3cce9106b26369cf11ca + + + jwt_valid_new + group__jwt__valid__grp.html + gaede6b64d84fd8166ea16debe6efc826d + + + jwt_valid_set_exp_leeway + group__jwt__valid__grp.html + ga1ce0ec28e692a189d3c6819dc571f941 + + + jwt_valid_set_headers + group__jwt__valid__grp.html + gab29df6ee78d33bb846b13f95a1269e0d + + + jwt_valid_set_nbf_leeway + group__jwt__valid__grp.html + ga55f1862ab8bb6297ee0163f77fe7c386 + + + jwt_valid_set_now + group__jwt__valid__grp.html + gad6a97fc0bb3341ae7f19975095a8086a + + + jwt_validate + group__jwt__valid__grp.html + ga354a14a13a0c96dae2cc22c4fa790413 + + + + + + + JSON Web Key and Sets + group__jwks__core__grp.html + + + jwk_item_t + structjwk__item__t.html + + + alg + structjwk__item__t.html + aae5092f40a4bfbe9155ddde65f5b933f + + + bits + structjwk__item__t.html + a2a87bc6fcdbdbd234f1beee3ba63ea61 + + + curve + structjwk__item__t.html + a3840bd4c87181411e0f3012c020787b3 + + + error + structjwk__item__t.html + ae5aef5762e38fc66625cedc461a32ece + + + error_msg + structjwk__item__t.html + a2840975767dbdc020912682c06ebdb75 + + + is_private_key + structjwk__item__t.html + a58ab0832602b475e7c3a7009925db87e + + + json + structjwk__item__t.html + a3593e9e89e76e6e8b605f01a1a8a2367 + + + key + structjwk__item__t.html + ab4f6c1d6586f6b8a63349b2f014f1f7c + + + key_ops + structjwk__item__t.html + a44fe44eb2d9c9466c48f19cd5f590e21 + + + kid + structjwk__item__t.html + a874da82b485b8c541ecd0f3f8fd12a0f + + + kty + structjwk__item__t.html + ae2d7fed09d97d0ac3e6571e7c897d580 + + + len + structjwk__item__t.html + a379d62be0803bca022445adb82cd99e5 + + + oct + structjwk__item__t.html + ae03055df75f8a3940fb071d5bbcc2379 + + + pem + structjwk__item__t.html + afecd2ad6ce52bf49a202999c184a9b2e + + + provider + structjwk__item__t.html + a892ffdca6722b35e24d885f01edd6679 + + + provider_data + structjwk__item__t.html + a6cf519323148008f4549798d7df1d8f3 + + + use + structjwk__item__t.html + a8790b92f608e12757ba07dff49b931df + + + + + jwk_set_t + group__jwks__core__grp.html + ga380f2d92f1d0d306420729f9789f5e50 + + + jwk_key_op_t + group__jwks__core__grp.html + ga150285e933a3d456bcd8d87b906438a3 + + + JWK_KEY_OP_NONE + group__jwks__core__grp.html + gga150285e933a3d456bcd8d87b906438a3a9054426563814eff0f4ea23def0e00f9 + + + JWK_KEY_OP_SIGN + group__jwks__core__grp.html + gga150285e933a3d456bcd8d87b906438a3a893a71fda7261466815427686f349eef + + + JWK_KEY_OP_VERIFY + group__jwks__core__grp.html + gga150285e933a3d456bcd8d87b906438a3a76ea53a146dac84cffa46681bbc7fc2e + + + JWK_KEY_OP_ENCRYPT + group__jwks__core__grp.html + gga150285e933a3d456bcd8d87b906438a3a1101574110cba3dd17ff829d728ceb5b + + + JWK_KEY_OP_DECRYPT + group__jwks__core__grp.html + gga150285e933a3d456bcd8d87b906438a3a9a4c0f1119d858a8bda1f7cecfb98d79 + + + JWK_KEY_OP_WRAP + group__jwks__core__grp.html + gga150285e933a3d456bcd8d87b906438a3a451f04d391cf27e7938c6b29324272c8 + + + JWK_KEY_OP_UNWRAP + group__jwks__core__grp.html + gga150285e933a3d456bcd8d87b906438a3a56462a753babcb3a1593a5436d677667 + + + JWK_KEY_OP_DERIVE_KEY + group__jwks__core__grp.html + gga150285e933a3d456bcd8d87b906438a3adb8b6296fb02fd5e961d89310edf7ce6 + + + JWK_KEY_OP_DERIVE_BITS + group__jwks__core__grp.html + gga150285e933a3d456bcd8d87b906438a3a073d4ad92c0276dbc1ecdc874231ce39 + + + JWK_KEY_OP_INVALID + group__jwks__core__grp.html + gga150285e933a3d456bcd8d87b906438a3aeb336579eb3c3c3e6f563b2a36d1511f + + + + + jwk_key_type_t + group__jwks__core__grp.html + ga6ee53248054c67e9585aae9e937890f2 + + + JWK_KEY_TYPE_NONE + group__jwks__core__grp.html + gga6ee53248054c67e9585aae9e937890f2a91865197de901ef236c5570a7c6897d9 + + + JWK_KEY_TYPE_EC + group__jwks__core__grp.html + gga6ee53248054c67e9585aae9e937890f2ad7065b821b8699d3b42fe0c3263dd3b1 + + + JWK_KEY_TYPE_RSA + group__jwks__core__grp.html + gga6ee53248054c67e9585aae9e937890f2af1a4bf01f937df96d195572180f036d8 + + + JWK_KEY_TYPE_OKP + group__jwks__core__grp.html + gga6ee53248054c67e9585aae9e937890f2ab42a328b8fc31e10c3f5e38e953c684c + + + JWK_KEY_TYPE_OCT + group__jwks__core__grp.html + gga6ee53248054c67e9585aae9e937890f2af2312e76e2727031b0641a9f21405b3c + + + + + jwk_pub_key_use_t + group__jwks__core__grp.html + gab30049eeae2a6ecdc0c9ae849b835b6b + + + JWK_PUB_KEY_USE_NONE + group__jwks__core__grp.html + ggab30049eeae2a6ecdc0c9ae849b835b6baf3e36efeb7307384fa7f7dbee089573b + + + JWK_PUB_KEY_USE_SIG + group__jwks__core__grp.html + ggab30049eeae2a6ecdc0c9ae849b835b6ba5c6c707d72a666971135ad6bec551b99 + + + JWK_PUB_KEY_USE_ENC + group__jwks__core__grp.html + ggab30049eeae2a6ecdc0c9ae849b835b6baefac5505199173f9c52530be55a54408 + + + + + jwks_create + group__jwks__core__grp.html + ga61852f3afd280f50a02171fee05b5b8e + + + jwks_error + group__jwks__core__grp.html + ga2d50f634f1b9d73de31ee251fcf50210 + + + jwks_error_any + group__jwks__core__grp.html + gac3b6bdd5540c8aa1390705e09db6b655 + + + jwks_error_msg + group__jwks__core__grp.html + ga7500b0576723039ce30b09a2e1387d6a + + + jwks_free + group__jwks__core__grp.html + gadd20e30cdcd0c0d5b794547d41e8252f + + + jwks_item_add + group__jwks__core__grp.html + ga2524a7db039e09d7716cd63e86846f9f + + + jwks_item_free + group__jwks__core__grp.html + ga8a82553f9582e10d096a1b9ac73c21a5 + + + jwks_item_free_all + group__jwks__core__grp.html + ga8b5403fd3d2e07199bb8afb2c053aa2e + + + jwks_item_get + group__jwks__core__grp.html + gae00c8e06cbdf28d732452218940341a3 + + + + + Advanced Functionality + group__jwt__advanced__grp.html + + + Memory Handlers + group__jwt__memory__grp.html + + + jwt_free_t + group__jwt__memory__grp.html + gad3b3fc88482616b2883b786d0700c377 + + + jwt_malloc_t + group__jwt__memory__grp.html + gae64b1a91c7e44845d2422f5a6bfa7972 + + + jwt_realloc_t + group__jwt__memory__grp.html + gaee904814d9c9266c68d2abff08fbc5af + + + jwt_get_alloc + group__jwt__memory__grp.html + ga8d96db3450596f2e38b549bd8f0e4ec5 + + + jwt_set_alloc + group__jwt__memory__grp.html + ga1d0fb7527d55f7849d2195ff8fd28344 + + + + + Cryptographic Operations + group__jwt__crypto__grp.html + + + jwt_crypto_provider_t + group__jwt__crypto__grp.html + gaa1e2b1f241858d81b1bfb86018d3b3ac + + + JWT_CRYPTO_OPS_NONE + group__jwt__crypto__grp.html + ggaa1e2b1f241858d81b1bfb86018d3b3aca482aa19a4996aaddbfd0349356be92c2 + + + JWT_CRYPTO_OPS_OPENSSL + group__jwt__crypto__grp.html + ggaa1e2b1f241858d81b1bfb86018d3b3aca5ebef71958d3135b13ada7bb72a63b02 + + + JWT_CRYPTO_OPS_GNUTLS + group__jwt__crypto__grp.html + ggaa1e2b1f241858d81b1bfb86018d3b3acaee34d6e9a11bfaa47e01c56a66682750 + + + JWT_CRYPTO_OPS_MBEDTLS + group__jwt__crypto__grp.html + ggaa1e2b1f241858d81b1bfb86018d3b3aca4c5fd08d7061b5caba0f8698dc817306 + + + JWT_CRYPTO_OPS_ANY + group__jwt__crypto__grp.html + ggaa1e2b1f241858d81b1bfb86018d3b3aca80b50ab76725eb24ce2c16199865d50a + + + + + jwt_crypto_ops_supports_jwk + group__jwt__crypto__grp.html + gad03cf6e8291772a1ff1d37a81f18a177 + + + jwt_get_crypto_ops + group__jwt__crypto__grp.html + ga1ccd01c10c0f77ab1d247b1fff1f47b1 + + + jwt_get_crypto_ops_t + group__jwt__crypto__grp.html + ga3f15e0ad1c6f8e2afae34c1d5bb03fcd + + + jwt_set_crypto_ops + group__jwt__crypto__grp.html + ga6749e9262b985d2b0b1f99f316356aa3 + + + jwt_set_crypto_ops_t + group__jwt__crypto__grp.html + ga1a03f32ccbc62bd04b4ae3a795d0bb08 + + + + + + + + + Data Structures + annotated.html + + + Data Structures + annotated.html + + + jwk_item_t + structjwk__item__t.html + + + alg + structjwk__item__t.html + aae5092f40a4bfbe9155ddde65f5b933f + + + bits + structjwk__item__t.html + a2a87bc6fcdbdbd234f1beee3ba63ea61 + + + curve + structjwk__item__t.html + a3840bd4c87181411e0f3012c020787b3 + + + error + structjwk__item__t.html + ae5aef5762e38fc66625cedc461a32ece + + + error_msg + structjwk__item__t.html + a2840975767dbdc020912682c06ebdb75 + + + is_private_key + structjwk__item__t.html + a58ab0832602b475e7c3a7009925db87e + + + json + structjwk__item__t.html + a3593e9e89e76e6e8b605f01a1a8a2367 + + + key + structjwk__item__t.html + ab4f6c1d6586f6b8a63349b2f014f1f7c + + + key_ops + structjwk__item__t.html + a44fe44eb2d9c9466c48f19cd5f590e21 + + + kid + structjwk__item__t.html + a874da82b485b8c541ecd0f3f8fd12a0f + + + kty + structjwk__item__t.html + ae2d7fed09d97d0ac3e6571e7c897d580 + + + len + structjwk__item__t.html + a379d62be0803bca022445adb82cd99e5 + + + oct + structjwk__item__t.html + ae03055df75f8a3940fb071d5bbcc2379 + + + pem + structjwk__item__t.html + afecd2ad6ce52bf49a202999c184a9b2e + + + provider + structjwk__item__t.html + a892ffdca6722b35e24d885f01edd6679 + + + provider_data + structjwk__item__t.html + a6cf519323148008f4549798d7df1d8f3 + + + use + structjwk__item__t.html + a8790b92f608e12757ba07dff49b931df + + + + + jwt_config_t + structjwt__config__t.html + + + alg + structjwt__config__t.html + aa1f5e081bba22bd5e0e90962b55d3305 + + + ctx + structjwt__config__t.html + a5b20a944b428896447108820ca861117 + + + jw_key + structjwt__config__t.html + abc613eedbd07027e1aed03baaf7b7a93 + + + + + + + Data Structure Index + classes.html + + + Data Fields + functions.html + + + All + functions.html + + + Variables + functions_vars.html + + + + + + + + + diff --git a/HEAD/Tokens.xml b/HEAD/Tokens.xml new file mode 100644 index 00000000..ed7d4446 --- /dev/null +++ b/HEAD/Tokens.xml @@ -0,0 +1,2155 @@ + + + + + jwk_item_t + c + cl + jwk_item_t + + structjwk__item__t.html + jwt.h + + + + jwt_config_t + c + cl + jwt_config_t + + structjwt__config__t.html + jwt.h + + + + jwt_grp + c + + group__jwt__grp.html + + + + jwt_core_grp + c + + group__jwt__core__grp.html + + + + jwt_config_grp + c + + group__jwt__config__grp.html + + + + jwt_create_grp + c + + group__jwt__create__grp.html + + + + jwt_verify_grp + c + + group__jwt__verify__grp.html + + + + jwt_grant_grp + c + + group__jwt__grant__grp.html + + + + jwt_header_grp + c + + group__jwt__header__grp.html + + + + jwt_encode_grp + c + + group__jwt__encode__grp.html + + + + jwt_alg_grp + c + + group__jwt__alg__grp.html + + + + jwks_core_grp + c + + group__jwks__core__grp.html + + + + jwt_valid_grp + c + + group__jwt__valid__grp.html + + + + jwt_advanced_grp + c + + group__jwt__advanced__grp.html + + + + jwt_memory_grp + c + + group__jwt__memory__grp.html + + + + jwt_crypto_grp + c + + group__jwt__crypto__grp.html + + + + todo + unknown + + todo.html + + + + index + unknown + + index.html + + + + alg + cpp + data + jwk_item_t + + structjwk__item__t.html + aae5092f40a4bfbe9155ddde65f5b933f + + + + alg + cpp + data + jwt_config_t + + structjwt__config__t.html + aa1f5e081bba22bd5e0e90962b55d3305 + + + + bits + cpp + data + jwk_item_t + + structjwk__item__t.html + a2a87bc6fcdbdbd234f1beee3ba63ea61 + + + + ctx + cpp + data + jwt_config_t + + structjwt__config__t.html + a5b20a944b428896447108820ca861117 + + + + curve + cpp + data + jwk_item_t + + structjwk__item__t.html + a3840bd4c87181411e0f3012c020787b3 + + + + error + cpp + data + jwk_item_t + + structjwk__item__t.html + ae5aef5762e38fc66625cedc461a32ece + + + + error_msg + cpp + data + jwk_item_t + + structjwk__item__t.html + a2840975767dbdc020912682c06ebdb75 + + + + is_private_key + cpp + data + jwk_item_t + + structjwk__item__t.html + a58ab0832602b475e7c3a7009925db87e + + + + json + cpp + data + jwk_item_t + + structjwk__item__t.html + a3593e9e89e76e6e8b605f01a1a8a2367 + + + + jw_key + cpp + data + jwt_config_t + + structjwt__config__t.html + abc613eedbd07027e1aed03baaf7b7a93 + + + + key + cpp + data + jwk_item_t + + structjwk__item__t.html + ab4f6c1d6586f6b8a63349b2f014f1f7c + + + + key_ops + cpp + data + jwk_item_t + + structjwk__item__t.html + a44fe44eb2d9c9466c48f19cd5f590e21 + + + + kid + cpp + data + jwk_item_t + + structjwk__item__t.html + a874da82b485b8c541ecd0f3f8fd12a0f + + + + kty + cpp + data + jwk_item_t + + structjwk__item__t.html + ae2d7fed09d97d0ac3e6571e7c897d580 + + + + len + cpp + data + jwk_item_t + + structjwk__item__t.html + a379d62be0803bca022445adb82cd99e5 + + + + oct + cpp + data + jwk_item_t + + structjwk__item__t.html + ae03055df75f8a3940fb071d5bbcc2379 + + + + pem + cpp + data + jwk_item_t + + structjwk__item__t.html + afecd2ad6ce52bf49a202999c184a9b2e + + + + provider + cpp + data + jwk_item_t + + structjwk__item__t.html + a892ffdca6722b35e24d885f01edd6679 + + + + provider_data + cpp + data + jwk_item_t + + structjwk__item__t.html + a6cf519323148008f4549798d7df1d8f3 + + + + use + cpp + data + jwk_item_t + + structjwk__item__t.html + a8790b92f608e12757ba07dff49b931df + + + + JWK_KEY_OP_DECRYPT + c + econst + + group__jwks__core__grp.html + gga150285e933a3d456bcd8d87b906438a3a9a4c0f1119d858a8bda1f7cecfb98d79 + jwt.h + + + + JWK_KEY_OP_DERIVE_BITS + c + econst + + group__jwks__core__grp.html + gga150285e933a3d456bcd8d87b906438a3a073d4ad92c0276dbc1ecdc874231ce39 + jwt.h + + + + JWK_KEY_OP_DERIVE_KEY + c + econst + + group__jwks__core__grp.html + gga150285e933a3d456bcd8d87b906438a3adb8b6296fb02fd5e961d89310edf7ce6 + jwt.h + + + + JWK_KEY_OP_ENCRYPT + c + econst + + group__jwks__core__grp.html + gga150285e933a3d456bcd8d87b906438a3a1101574110cba3dd17ff829d728ceb5b + jwt.h + + + + JWK_KEY_OP_INVALID + c + econst + + group__jwks__core__grp.html + gga150285e933a3d456bcd8d87b906438a3aeb336579eb3c3c3e6f563b2a36d1511f + jwt.h + + + + JWK_KEY_OP_NONE + c + econst + + group__jwks__core__grp.html + gga150285e933a3d456bcd8d87b906438a3a9054426563814eff0f4ea23def0e00f9 + jwt.h + + + + JWK_KEY_OP_SIGN + c + econst + + group__jwks__core__grp.html + gga150285e933a3d456bcd8d87b906438a3a893a71fda7261466815427686f349eef + jwt.h + + + + jwk_key_op_t + c + enum + + group__jwks__core__grp.html + ga150285e933a3d456bcd8d87b906438a3 + jwt.h + + + + JWK_KEY_OP_NONE + c + econst + + group__jwks__core__grp.html + gga150285e933a3d456bcd8d87b906438a3a9054426563814eff0f4ea23def0e00f9 + jwt.h + + + + JWK_KEY_OP_SIGN + c + econst + + group__jwks__core__grp.html + gga150285e933a3d456bcd8d87b906438a3a893a71fda7261466815427686f349eef + jwt.h + + + + JWK_KEY_OP_VERIFY + c + econst + + group__jwks__core__grp.html + gga150285e933a3d456bcd8d87b906438a3a76ea53a146dac84cffa46681bbc7fc2e + jwt.h + + + + JWK_KEY_OP_ENCRYPT + c + econst + + group__jwks__core__grp.html + gga150285e933a3d456bcd8d87b906438a3a1101574110cba3dd17ff829d728ceb5b + jwt.h + + + + JWK_KEY_OP_DECRYPT + c + econst + + group__jwks__core__grp.html + gga150285e933a3d456bcd8d87b906438a3a9a4c0f1119d858a8bda1f7cecfb98d79 + jwt.h + + + + JWK_KEY_OP_WRAP + c + econst + + group__jwks__core__grp.html + gga150285e933a3d456bcd8d87b906438a3a451f04d391cf27e7938c6b29324272c8 + jwt.h + + + + JWK_KEY_OP_UNWRAP + c + econst + + group__jwks__core__grp.html + gga150285e933a3d456bcd8d87b906438a3a56462a753babcb3a1593a5436d677667 + jwt.h + + + + JWK_KEY_OP_DERIVE_KEY + c + econst + + group__jwks__core__grp.html + gga150285e933a3d456bcd8d87b906438a3adb8b6296fb02fd5e961d89310edf7ce6 + jwt.h + + + + JWK_KEY_OP_DERIVE_BITS + c + econst + + group__jwks__core__grp.html + gga150285e933a3d456bcd8d87b906438a3a073d4ad92c0276dbc1ecdc874231ce39 + jwt.h + + + + JWK_KEY_OP_INVALID + c + econst + + group__jwks__core__grp.html + gga150285e933a3d456bcd8d87b906438a3aeb336579eb3c3c3e6f563b2a36d1511f + jwt.h + + + + JWK_KEY_OP_UNWRAP + c + econst + + group__jwks__core__grp.html + gga150285e933a3d456bcd8d87b906438a3a56462a753babcb3a1593a5436d677667 + jwt.h + + + + JWK_KEY_OP_VERIFY + c + econst + + group__jwks__core__grp.html + gga150285e933a3d456bcd8d87b906438a3a76ea53a146dac84cffa46681bbc7fc2e + jwt.h + + + + JWK_KEY_OP_WRAP + c + econst + + group__jwks__core__grp.html + gga150285e933a3d456bcd8d87b906438a3a451f04d391cf27e7938c6b29324272c8 + jwt.h + + + + JWK_KEY_TYPE_EC + c + econst + + group__jwks__core__grp.html + gga6ee53248054c67e9585aae9e937890f2ad7065b821b8699d3b42fe0c3263dd3b1 + jwt.h + + + + JWK_KEY_TYPE_NONE + c + econst + + group__jwks__core__grp.html + gga6ee53248054c67e9585aae9e937890f2a91865197de901ef236c5570a7c6897d9 + jwt.h + + + + JWK_KEY_TYPE_OCT + c + econst + + group__jwks__core__grp.html + gga6ee53248054c67e9585aae9e937890f2af2312e76e2727031b0641a9f21405b3c + jwt.h + + + + JWK_KEY_TYPE_OKP + c + econst + + group__jwks__core__grp.html + gga6ee53248054c67e9585aae9e937890f2ab42a328b8fc31e10c3f5e38e953c684c + jwt.h + + + + JWK_KEY_TYPE_RSA + c + econst + + group__jwks__core__grp.html + gga6ee53248054c67e9585aae9e937890f2af1a4bf01f937df96d195572180f036d8 + jwt.h + + + + jwk_key_type_t + c + enum + + group__jwks__core__grp.html + ga6ee53248054c67e9585aae9e937890f2 + jwt.h + + + + JWK_KEY_TYPE_NONE + c + econst + + group__jwks__core__grp.html + gga6ee53248054c67e9585aae9e937890f2a91865197de901ef236c5570a7c6897d9 + jwt.h + + + + JWK_KEY_TYPE_EC + c + econst + + group__jwks__core__grp.html + gga6ee53248054c67e9585aae9e937890f2ad7065b821b8699d3b42fe0c3263dd3b1 + jwt.h + + + + JWK_KEY_TYPE_RSA + c + econst + + group__jwks__core__grp.html + gga6ee53248054c67e9585aae9e937890f2af1a4bf01f937df96d195572180f036d8 + jwt.h + + + + JWK_KEY_TYPE_OKP + c + econst + + group__jwks__core__grp.html + gga6ee53248054c67e9585aae9e937890f2ab42a328b8fc31e10c3f5e38e953c684c + jwt.h + + + + JWK_KEY_TYPE_OCT + c + econst + + group__jwks__core__grp.html + gga6ee53248054c67e9585aae9e937890f2af2312e76e2727031b0641a9f21405b3c + jwt.h + + + + JWK_PUB_KEY_USE_ENC + c + econst + + group__jwks__core__grp.html + ggab30049eeae2a6ecdc0c9ae849b835b6baefac5505199173f9c52530be55a54408 + jwt.h + + + + JWK_PUB_KEY_USE_NONE + c + econst + + group__jwks__core__grp.html + ggab30049eeae2a6ecdc0c9ae849b835b6baf3e36efeb7307384fa7f7dbee089573b + jwt.h + + + + JWK_PUB_KEY_USE_SIG + c + econst + + group__jwks__core__grp.html + ggab30049eeae2a6ecdc0c9ae849b835b6ba5c6c707d72a666971135ad6bec551b99 + jwt.h + + + + jwk_pub_key_use_t + c + enum + + group__jwks__core__grp.html + gab30049eeae2a6ecdc0c9ae849b835b6b + jwt.h + + + + JWK_PUB_KEY_USE_NONE + c + econst + + group__jwks__core__grp.html + ggab30049eeae2a6ecdc0c9ae849b835b6baf3e36efeb7307384fa7f7dbee089573b + jwt.h + + + + JWK_PUB_KEY_USE_SIG + c + econst + + group__jwks__core__grp.html + ggab30049eeae2a6ecdc0c9ae849b835b6ba5c6c707d72a666971135ad6bec551b99 + jwt.h + + + + JWK_PUB_KEY_USE_ENC + c + econst + + group__jwks__core__grp.html + ggab30049eeae2a6ecdc0c9ae849b835b6baefac5505199173f9c52530be55a54408 + jwt.h + + + + jwk_set_t + c + tdef + + group__jwks__core__grp.html + ga380f2d92f1d0d306420729f9789f5e50 + jwt.h + + + + jwks_create + c + func + + group__jwks__core__grp.html + ga61852f3afd280f50a02171fee05b5b8e + jwt.h + + + + jwks_error + c + func + + group__jwks__core__grp.html + ga2d50f634f1b9d73de31ee251fcf50210 + jwt.h + + + + jwks_error_any + c + func + + group__jwks__core__grp.html + gac3b6bdd5540c8aa1390705e09db6b655 + jwt.h + + + + jwks_error_msg + c + func + + group__jwks__core__grp.html + ga7500b0576723039ce30b09a2e1387d6a + jwt.h + + + + jwks_free + c + func + + group__jwks__core__grp.html + gadd20e30cdcd0c0d5b794547d41e8252f + jwt.h + + + + jwks_item_add + c + func + + group__jwks__core__grp.html + ga2524a7db039e09d7716cd63e86846f9f + jwt.h + + + + jwks_item_free + c + func + + group__jwks__core__grp.html + ga8a82553f9582e10d096a1b9ac73c21a5 + jwt.h + + + + jwks_item_free_all + c + func + + group__jwks__core__grp.html + ga8b5403fd3d2e07199bb8afb2c053aa2e + jwt.h + + + + jwks_item_get + c + func + + group__jwks__core__grp.html + gae00c8e06cbdf28d732452218940341a3 + jwt.h + + + + jwt_add_grant + c + func + + group__jwt__grant__grp.html + ga91d9bfe88d3e7b246ee67d2ed4609955 + jwt.h + + + + jwt_add_grant_bool + c + func + + group__jwt__grant__grp.html + gad528aabec09be8d051485a45c49781c3 + jwt.h + + + + jwt_add_grant_int + c + func + + group__jwt__grant__grp.html + gae4cdc69b259a0f2f98e3c07bf9038765 + jwt.h + + + + jwt_add_grants_json + c + func + + group__jwt__grant__grp.html + ga0509c745c45eebb41c278cebb0617a55 + jwt.h + + + + jwt_add_header + c + func + + group__jwt__header__grp.html + ga1c0093b5b221f78bbd4e102d30870a27 + jwt.h + + + + jwt_add_header_bool + c + func + + group__jwt__header__grp.html + ga337c85106951204af60f40288da0a7d8 + jwt.h + + + + jwt_add_header_int + c + func + + group__jwt__header__grp.html + ga250b312a2c57fa5664f3921950c88766 + jwt.h + + + + jwt_add_headers_json + c + func + + group__jwt__header__grp.html + ga283f3dc2d9686e4db73874ae9f446ac1 + jwt.h + + + + JWT_ALG_EDDSA + c + econst + + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33aab33738e67e3b23db72a3bc77043009b + jwt.h + + + + JWT_ALG_ES256 + c + econst + + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33a84f9bf1cb93f6ae622a9de57d9c67472 + jwt.h + + + + JWT_ALG_ES256K + c + econst + + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33a7f760cf9ed49b80ecb78580ed381ff01 + jwt.h + + + + JWT_ALG_ES384 + c + econst + + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33ae18e69e6b629aa0d3e6d16ec4a95c708 + jwt.h + + + + JWT_ALG_ES512 + c + econst + + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33a87205e19f1dde7c3cee012424482e106 + jwt.h + + + + JWT_ALG_HS256 + c + econst + + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33aaa9217f4c8f5266d5401402f0ec98604 + jwt.h + + + + JWT_ALG_HS384 + c + econst + + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33af0adc69b9caa89fbc2eeaf744d7cf4f2 + jwt.h + + + + JWT_ALG_HS512 + c + econst + + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33a596b0da85c01e17b43a6574f82159081 + jwt.h + + + + JWT_ALG_INVAL + c + econst + + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33a199f5ce0e69c0160cc121cf6dbe9cb58 + jwt.h + + + + JWT_ALG_NONE + c + econst + + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33a3249a71140a0bb4b3cb196fe15114b89 + jwt.h + + + + JWT_ALG_PS256 + c + econst + + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33ad0216cbdf24f6b486d5082294b3cfc5b + jwt.h + + + + JWT_ALG_PS384 + c + econst + + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33a3ad3c7e704886dd61dee0de5fa02072d + jwt.h + + + + JWT_ALG_PS512 + c + econst + + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33a727c838b47ad911681eb029cc8b55744 + jwt.h + + + + JWT_ALG_RS256 + c + econst + + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33aa03ba981baa4b1cda3b9de1347ada853 + jwt.h + + + + JWT_ALG_RS384 + c + econst + + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33a520814a2695a6df472bb408ba6feb206 + jwt.h + + + + JWT_ALG_RS512 + c + econst + + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33ab4d732eef3f6bee21201d638dfd8ca44 + jwt.h + + + + jwt_alg_str + c + func + + group__jwt__alg__grp.html + gaa906a6a1c27a3f9f6500240f7b228123 + jwt.h + + + + jwt_alg_t + c + enum + + group__jwt__core__grp.html + ga889db178d8714ab67bc994f1d8faad33 + jwt.h + + + + JWT_ALG_NONE + c + econst + + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33a3249a71140a0bb4b3cb196fe15114b89 + jwt.h + + + + JWT_ALG_HS256 + c + econst + + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33aaa9217f4c8f5266d5401402f0ec98604 + jwt.h + + + + JWT_ALG_HS384 + c + econst + + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33af0adc69b9caa89fbc2eeaf744d7cf4f2 + jwt.h + + + + JWT_ALG_HS512 + c + econst + + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33a596b0da85c01e17b43a6574f82159081 + jwt.h + + + + JWT_ALG_RS256 + c + econst + + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33aa03ba981baa4b1cda3b9de1347ada853 + jwt.h + + + + JWT_ALG_RS384 + c + econst + + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33a520814a2695a6df472bb408ba6feb206 + jwt.h + + + + JWT_ALG_RS512 + c + econst + + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33ab4d732eef3f6bee21201d638dfd8ca44 + jwt.h + + + + JWT_ALG_ES256 + c + econst + + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33a84f9bf1cb93f6ae622a9de57d9c67472 + jwt.h + + + + JWT_ALG_ES384 + c + econst + + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33ae18e69e6b629aa0d3e6d16ec4a95c708 + jwt.h + + + + JWT_ALG_ES512 + c + econst + + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33a87205e19f1dde7c3cee012424482e106 + jwt.h + + + + JWT_ALG_PS256 + c + econst + + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33ad0216cbdf24f6b486d5082294b3cfc5b + jwt.h + + + + JWT_ALG_PS384 + c + econst + + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33a3ad3c7e704886dd61dee0de5fa02072d + jwt.h + + + + JWT_ALG_PS512 + c + econst + + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33a727c838b47ad911681eb029cc8b55744 + jwt.h + + + + JWT_ALG_ES256K + c + econst + + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33a7f760cf9ed49b80ecb78580ed381ff01 + jwt.h + + + + JWT_ALG_EDDSA + c + econst + + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33aab33738e67e3b23db72a3bc77043009b + jwt.h + + + + JWT_ALG_INVAL + c + econst + + group__jwt__core__grp.html + gga889db178d8714ab67bc994f1d8faad33a199f5ce0e69c0160cc121cf6dbe9cb58 + jwt.h + + + + jwt_callback_t + c + tdef + + group__jwt__config__grp.html + ga44edddf2ce7034752c2b22283ad1010d + jwt.h + + + + JWT_CONFIG_DECLARE + unknown + macro + + group__jwt__config__grp.html + ga0816f65962b32e875a2ed9b6f0166bee + jwt.h + + + + jwt_config_init + c + func + + group__jwt__config__grp.html + ga08ab7f9e1917072c0910ee5335d3b04a + jwt.h + + + + jwt_create + c + func + + group__jwt__create__grp.html + ga7d5f4d5a83505c06faf9728bd7911908 + jwt.h + + + + JWT_CRYPTO_OPS_ANY + c + econst + + group__jwt__crypto__grp.html + ggaa1e2b1f241858d81b1bfb86018d3b3aca80b50ab76725eb24ce2c16199865d50a + jwt.h + + + + JWT_CRYPTO_OPS_GNUTLS + c + econst + + group__jwt__crypto__grp.html + ggaa1e2b1f241858d81b1bfb86018d3b3acaee34d6e9a11bfaa47e01c56a66682750 + jwt.h + + + + JWT_CRYPTO_OPS_MBEDTLS + c + econst + + group__jwt__crypto__grp.html + ggaa1e2b1f241858d81b1bfb86018d3b3aca4c5fd08d7061b5caba0f8698dc817306 + jwt.h + + + + JWT_CRYPTO_OPS_NONE + c + econst + + group__jwt__crypto__grp.html + ggaa1e2b1f241858d81b1bfb86018d3b3aca482aa19a4996aaddbfd0349356be92c2 + jwt.h + + + + JWT_CRYPTO_OPS_OPENSSL + c + econst + + group__jwt__crypto__grp.html + ggaa1e2b1f241858d81b1bfb86018d3b3aca5ebef71958d3135b13ada7bb72a63b02 + jwt.h + + + + jwt_crypto_ops_supports_jwk + c + func + + group__jwt__crypto__grp.html + gad03cf6e8291772a1ff1d37a81f18a177 + jwt.h + + + + jwt_crypto_provider_t + c + enum + + group__jwt__crypto__grp.html + gaa1e2b1f241858d81b1bfb86018d3b3ac + jwt.h + + + + JWT_CRYPTO_OPS_NONE + c + econst + + group__jwt__crypto__grp.html + ggaa1e2b1f241858d81b1bfb86018d3b3aca482aa19a4996aaddbfd0349356be92c2 + jwt.h + + + + JWT_CRYPTO_OPS_OPENSSL + c + econst + + group__jwt__crypto__grp.html + ggaa1e2b1f241858d81b1bfb86018d3b3aca5ebef71958d3135b13ada7bb72a63b02 + jwt.h + + + + JWT_CRYPTO_OPS_GNUTLS + c + econst + + group__jwt__crypto__grp.html + ggaa1e2b1f241858d81b1bfb86018d3b3acaee34d6e9a11bfaa47e01c56a66682750 + jwt.h + + + + JWT_CRYPTO_OPS_MBEDTLS + c + econst + + group__jwt__crypto__grp.html + ggaa1e2b1f241858d81b1bfb86018d3b3aca4c5fd08d7061b5caba0f8698dc817306 + jwt.h + + + + JWT_CRYPTO_OPS_ANY + c + econst + + group__jwt__crypto__grp.html + ggaa1e2b1f241858d81b1bfb86018d3b3aca80b50ab76725eb24ce2c16199865d50a + jwt.h + + + + jwt_del_grants + c + func + + group__jwt__grant__grp.html + ga0d8da61021d73817fb71bf8ffb7791b7 + jwt.h + + + + jwt_del_headers + c + func + + group__jwt__header__grp.html + gae5ebb283a76098e9a99f7fe13b18593f + jwt.h + + + + jwt_dump_fp + c + func + + group__jwt__encode__grp.html + gacc77ae9e2214c9b07352a400758f1f4d + jwt.h + + + + jwt_dump_grants_str + c + func + + group__jwt__encode__grp.html + ga219c406420f7f92960735433c69a304e + jwt.h + + + + jwt_dump_str + c + func + + group__jwt__encode__grp.html + ga1bf8a1df0f944c027d40677404778290 + jwt.h + + + + jwt_dup + c + func + + group__jwt__core__grp.html + gac11d4548c695e5781cae2a5148a7e240 + jwt.h + + + + jwt_encode_fp + c + func + + group__jwt__encode__grp.html + ga2782abdbb190b626a5bd4eb3943b7b8d + jwt.h + + + + jwt_encode_str + c + func + + group__jwt__encode__grp.html + ga124ccd21e6495dbbd1012f60d74202dd + jwt.h + + + + jwt_exception_str + c + func + + group__jwt__valid__grp.html + ga8371a7299dab709fa28fba7271d03a14 + jwt.h + + + + jwt_free + c + func + + group__jwt__core__grp.html + gafd51b076050815be44081439d1cb33fc + jwt.h + + + + jwt_free_str + c + func + + group__jwt__encode__grp.html + ga1c6ff5f00e8722f5522fe7b36697ecdd + jwt.h + + + + jwt_free_t + c + tdef + + group__jwt__memory__grp.html + gad3b3fc88482616b2883b786d0700c377 + jwt.h + + + + jwt_get_alg + c + func + + group__jwt__alg__grp.html + ga3e9cb111d566f55017d86d22dc545225 + jwt.h + + + + jwt_get_alloc + c + func + + group__jwt__memory__grp.html + ga8d96db3450596f2e38b549bd8f0e4ec5 + jwt.h + + + + jwt_get_crypto_ops + c + func + + group__jwt__crypto__grp.html + ga1ccd01c10c0f77ab1d247b1fff1f47b1 + jwt.h + + + + jwt_get_crypto_ops_t + c + func + + group__jwt__crypto__grp.html + ga3f15e0ad1c6f8e2afae34c1d5bb03fcd + jwt.h + + + + jwt_get_grant + c + func + + group__jwt__grant__grp.html + ga2131ddd80d3c138766573b87310bdb6e + jwt.h + + + + jwt_get_grant_bool + c + func + + group__jwt__grant__grp.html + ga782d0fb87e79706e705ea5a989024abd + jwt.h + + + + jwt_get_grant_int + c + func + + group__jwt__grant__grp.html + gafac50d1449e4ea9560258a68a07cfdda + jwt.h + + + + jwt_get_grants_json + c + func + + group__jwt__grant__grp.html + ga5445b6af33ae868b763ae8d2139c089f + jwt.h + + + + jwt_get_header + c + func + + group__jwt__header__grp.html + ga5563efb29cdd4bfe078a3e82fa6a873a + jwt.h + + + + jwt_get_header_bool + c + func + + group__jwt__header__grp.html + gacb7171946cdc613c4800c1c0b279af4b + jwt.h + + + + jwt_get_header_int + c + func + + group__jwt__header__grp.html + gaadd81a21b63c01106bd7943992687199 + jwt.h + + + + jwt_get_headers_json + c + func + + group__jwt__header__grp.html + ga7fffc3eb52315f54605dad87fbdb52cb + jwt.h + + + + jwt_malloc_t + c + tdef + + group__jwt__memory__grp.html + gae64b1a91c7e44845d2422f5a6bfa7972 + jwt.h + + + + jwt_realloc_t + c + tdef + + group__jwt__memory__grp.html + gaee904814d9c9266c68d2abff08fbc5af + jwt.h + + + + jwt_set_alg + c + func + + group__jwt__alg__grp.html + ga4b2fe3a4afc420fa7fa9feecebf2c4d7 + jwt.h + + + + jwt_set_alloc + c + func + + group__jwt__memory__grp.html + ga1d0fb7527d55f7849d2195ff8fd28344 + jwt.h + + + + jwt_set_crypto_ops + c + func + + group__jwt__crypto__grp.html + ga6749e9262b985d2b0b1f99f316356aa3 + jwt.h + + + + jwt_set_crypto_ops_t + c + func + + group__jwt__crypto__grp.html + ga1a03f32ccbc62bd04b4ae3a795d0bb08 + jwt.h + + + + jwt_str_alg + c + func + + group__jwt__alg__grp.html + gaf15787af42ab0fc567a0a1fc5ef3edc6 + jwt.h + + + + jwt_t + c + tdef + + group__jwt__core__grp.html + gabf0acfb990d245214216af589c1d0947 + jwt.h + + + + jwt_valid_add_grant + c + func + + group__jwt__valid__grp.html + gab5e9e2dc5e0276ea2c21c050822c19e1 + jwt.h + + + + jwt_valid_add_grant_bool + c + func + + group__jwt__valid__grp.html + gafa57cedcecda005a47161c462b2f0c9f + jwt.h + + + + jwt_valid_add_grant_int + c + func + + group__jwt__valid__grp.html + ga50c04450cc57bcd18717c25f18aed479 + jwt.h + + + + jwt_valid_add_grants_json + c + func + + group__jwt__valid__grp.html + ga9361112f9372d8de2f11c115314c2d71 + jwt.h + + + + jwt_valid_del_grants + c + func + + group__jwt__valid__grp.html + ga929d87f2577cbb600de468b159d9f00d + jwt.h + + + + jwt_valid_exception_t + c + enum + + group__jwt__valid__grp.html + ga47ee94f9c08ff581f78590bfe024f6e4 + jwt.h + + + + JWT_VALIDATION_SUCCESS + c + econst + + group__jwt__valid__grp.html + gga47ee94f9c08ff581f78590bfe024f6e4a94bb9121f597e706e89322b0f9a7330b + jwt.h + + + + JWT_VALIDATION_ERROR + c + econst + + group__jwt__valid__grp.html + gga47ee94f9c08ff581f78590bfe024f6e4ad2e58b1f8a8bc39518b1c973fa99ee6e + jwt.h + + + + JWT_VALIDATION_ALG_MISMATCH + c + econst + + group__jwt__valid__grp.html + gga47ee94f9c08ff581f78590bfe024f6e4abf0dc5dd94b521a118485a9522cd8cea + jwt.h + + + + JWT_VALIDATION_EXPIRED + c + econst + + group__jwt__valid__grp.html + gga47ee94f9c08ff581f78590bfe024f6e4a698be61a7f4bcf0dfd9894e1c6296e0b + jwt.h + + + + JWT_VALIDATION_TOO_NEW + c + econst + + group__jwt__valid__grp.html + gga47ee94f9c08ff581f78590bfe024f6e4a685c5270c214bf2484b65376e4b7459b + jwt.h + + + + JWT_VALIDATION_ISS_MISMATCH + c + econst + + group__jwt__valid__grp.html + gga47ee94f9c08ff581f78590bfe024f6e4a0dde2793247cd11fbf017212d8fd605f + jwt.h + + + + JWT_VALIDATION_SUB_MISMATCH + c + econst + + group__jwt__valid__grp.html + gga47ee94f9c08ff581f78590bfe024f6e4ac8b2989c55deb3bdad631c61f1ed304d + jwt.h + + + + JWT_VALIDATION_AUD_MISMATCH + c + econst + + group__jwt__valid__grp.html + gga47ee94f9c08ff581f78590bfe024f6e4acb9d53e3cc20a39c0827d77a071271e6 + jwt.h + + + + JWT_VALIDATION_GRANT_MISSING + c + econst + + group__jwt__valid__grp.html + gga47ee94f9c08ff581f78590bfe024f6e4a94eb7551ec626004a55798ecbd563d38 + jwt.h + + + + JWT_VALIDATION_GRANT_MISMATCH + c + econst + + group__jwt__valid__grp.html + gga47ee94f9c08ff581f78590bfe024f6e4a5b1db073c57082b557592b7115a8e363 + jwt.h + + + + jwt_valid_free + c + func + + group__jwt__valid__grp.html + ga77db16a4a29ef6a607d6aff8012955b2 + jwt.h + + + + jwt_valid_get_exp_leeway + c + func + + group__jwt__valid__grp.html + ga0dc43278963ec6474d5d7263c960e69c + jwt.h + + + + jwt_valid_get_grant + c + func + + group__jwt__valid__grp.html + ga03b176d20c41a85a979c9671a9a02b6f + jwt.h + + + + jwt_valid_get_grant_bool + c + func + + group__jwt__valid__grp.html + gabe7ce52fca00463c2b70aa367037db98 + jwt.h + + + + jwt_valid_get_grant_int + c + func + + group__jwt__valid__grp.html + ga98a643ad3c8103539dee35c22a3adbb6 + jwt.h + + + + jwt_valid_get_grants_json + c + func + + group__jwt__valid__grp.html + gacafaf1f062cd358bda89601570d194ad + jwt.h + + + + jwt_valid_get_nbf_leeway + c + func + + group__jwt__valid__grp.html + gafa9c96838053831dd239824ed5149879 + jwt.h + + + + jwt_valid_get_status + c + func + + group__jwt__valid__grp.html + ga165d7857282f3cce9106b26369cf11ca + jwt.h + + + + jwt_valid_new + c + func + + group__jwt__valid__grp.html + gaede6b64d84fd8166ea16debe6efc826d + jwt.h + + + + jwt_valid_set_exp_leeway + c + func + + group__jwt__valid__grp.html + ga1ce0ec28e692a189d3c6819dc571f941 + jwt.h + + + + jwt_valid_set_headers + c + func + + group__jwt__valid__grp.html + gab29df6ee78d33bb846b13f95a1269e0d + jwt.h + + + + jwt_valid_set_nbf_leeway + c + func + + group__jwt__valid__grp.html + ga55f1862ab8bb6297ee0163f77fe7c386 + jwt.h + + + + jwt_valid_set_now + c + func + + group__jwt__valid__grp.html + gad6a97fc0bb3341ae7f19975095a8086a + jwt.h + + + + jwt_valid_t + c + tdef + + group__jwt__valid__grp.html + gac413377fbfc11671e8a8a66f45693e5d + jwt.h + + + + jwt_validate + c + func + + group__jwt__valid__grp.html + ga354a14a13a0c96dae2cc22c4fa790413 + jwt.h + + + + JWT_VALIDATION_ALG_MISMATCH + c + econst + + group__jwt__valid__grp.html + gga47ee94f9c08ff581f78590bfe024f6e4abf0dc5dd94b521a118485a9522cd8cea + jwt.h + + + + JWT_VALIDATION_AUD_MISMATCH + c + econst + + group__jwt__valid__grp.html + gga47ee94f9c08ff581f78590bfe024f6e4acb9d53e3cc20a39c0827d77a071271e6 + jwt.h + + + + JWT_VALIDATION_ERROR + c + econst + + group__jwt__valid__grp.html + gga47ee94f9c08ff581f78590bfe024f6e4ad2e58b1f8a8bc39518b1c973fa99ee6e + jwt.h + + + + JWT_VALIDATION_EXPIRED + c + econst + + group__jwt__valid__grp.html + gga47ee94f9c08ff581f78590bfe024f6e4a698be61a7f4bcf0dfd9894e1c6296e0b + jwt.h + + + + JWT_VALIDATION_GRANT_MISMATCH + c + econst + + group__jwt__valid__grp.html + gga47ee94f9c08ff581f78590bfe024f6e4a5b1db073c57082b557592b7115a8e363 + jwt.h + + + + JWT_VALIDATION_GRANT_MISSING + c + econst + + group__jwt__valid__grp.html + gga47ee94f9c08ff581f78590bfe024f6e4a94eb7551ec626004a55798ecbd563d38 + jwt.h + + + + JWT_VALIDATION_ISS_MISMATCH + c + econst + + group__jwt__valid__grp.html + gga47ee94f9c08ff581f78590bfe024f6e4a0dde2793247cd11fbf017212d8fd605f + jwt.h + + + + JWT_VALIDATION_SUB_MISMATCH + c + econst + + group__jwt__valid__grp.html + gga47ee94f9c08ff581f78590bfe024f6e4ac8b2989c55deb3bdad631c61f1ed304d + jwt.h + + + + JWT_VALIDATION_SUCCESS + c + econst + + group__jwt__valid__grp.html + gga47ee94f9c08ff581f78590bfe024f6e4a94bb9121f597e706e89322b0f9a7330b + jwt.h + + + + JWT_VALIDATION_TOO_NEW + c + econst + + group__jwt__valid__grp.html + gga47ee94f9c08ff581f78590bfe024f6e4a685c5270c214bf2484b65376e4b7459b + jwt.h + + + + jwt_verify + c + func + + group__jwt__verify__grp.html + gab0535d292e64c3688085cf0139aa8c8d + jwt.h + + + + jwt_verify_wcb + c + func + + group__jwt__verify__grp.html + gaddab273e09a8327e03f458be1cfb83b4 + jwt.h + + diff --git a/HEAD/annotated.html b/HEAD/annotated.html new file mode 100644 index 00000000..5f1a9455 --- /dev/null +++ b/HEAD/annotated.html @@ -0,0 +1,102 @@ + + + + + + + + +LibJWT: Data Structures + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + +
+
LibJWT 2.9.99 +
+
The C JSON Web Token Library +JWK +JWKS
+
+
+ + + +
+
+ +
+
+
+ +
+
+
Data Structures
+
+
+
Here are the data structures with brief descriptions:
+ + + +
 Cjwk_item_tStructural representation of a JWK
 Cjwt_config_tStructure used to manage configuration state
+
+
+
+ + + + + diff --git a/HEAD/annotated_dup.js b/HEAD/annotated_dup.js new file mode 100644 index 00000000..2e53deda --- /dev/null +++ b/HEAD/annotated_dup.js @@ -0,0 +1,5 @@ +var annotated_dup = +[ + [ "jwk_item_t", "structjwk__item__t.html", "structjwk__item__t" ], + [ "jwt_config_t", "structjwt__config__t.html", "structjwt__config__t" ] +]; \ No newline at end of file diff --git a/HEAD/bc_s.png b/HEAD/bc_s.png new file mode 100644 index 0000000000000000000000000000000000000000..224b29aa9847d5a4b3902efd602b7ddf7d33e6c2 GIT binary patch literal 676 zcmV;V0$crwP)y__>=_9%My z{n931IS})GlGUF8K#6VIbs%684A^L3@%PlP2>_sk`UWPq@f;rU*V%rPy_ekbhXT&s z(GN{DxFv}*vZp`F>S!r||M`I*nOwwKX+BC~3P5N3-)Y{65c;ywYiAh-1*hZcToLHK ztpl1xomJ+Yb}K(cfbJr2=GNOnT!UFA7Vy~fBz8?J>XHsbZoDad^8PxfSa0GDgENZS zuLCEqzb*xWX2CG*b&5IiO#NzrW*;`VC9455M`o1NBh+(k8~`XCEEoC1Ybwf;vr4K3 zg|EB<07?SOqHp9DhLpS&bzgo70I+ghB_#)K7H%AMU3v}xuyQq9&Bm~++VYhF09a+U zl7>n7Jjm$K#b*FONz~fj;I->Bf;ule1prFN9FovcDGBkpg>)O*-}eLnC{6oZHZ$o% zXKW$;0_{8hxHQ>l;_*HATI(`7t#^{$(zLe}h*mqwOc*nRY9=?Sx4OOeVIfI|0V(V2 zBrW#G7Ss9wvzr@>H*`r>zE z+e8bOBgqIgldUJlG(YUDviMB`9+DH8n-s9SXRLyJHO1!=wY^79WYZMTa(wiZ!zP66 zA~!21vmF3H2{ngD;+`6j#~6j;$*f*G_2ZD1E;9(yaw7d-QnSCpK(cR1zU3qU0000< KMNUMnLSTYoA~SLT literal 0 HcmV?d00001 diff --git a/HEAD/bc_sd.png b/HEAD/bc_sd.png new file mode 100644 index 0000000000000000000000000000000000000000..31ca888dc71049713b35c351933a8d0f36180bf1 GIT binary patch literal 635 zcmV->0)+jEP)Jwi0r1~gdSq#w{Bu1q z`craw(p2!hu$4C_$Oc3X(sI6e=9QSTwPt{G) z=htT&^~&c~L2~e{r5_5SYe7#Is-$ln>~Kd%$F#tC65?{LvQ}8O`A~RBB0N~`2M+waajO;5>3B&-viHGJeEK2TQOiPRa zfDKyqwMc4wfaEh4jt>H`nW_Zidwk@Bowp`}(VUaj-pSI(-1L>FJVsX}Yl9~JsqgsZ zUD9(rMwf23Gez6KPa|wwInZodP-2}9@fK0Ga_9{8SOjU&4l`pH4@qlQp83>>HT$xW zER^U>)MyV%t(Lu=`d=Y?{k1@}&r7ZGkFQ%z%N+sE9BtYjovzxyxCPxN6&@wLK{soQ zSmkj$aLI}miuE^p@~4}mg9OjDfGEkgY4~^XzLRUBB*O{+&vq<3v(E%+k_i%=`~j%{ Vj14gnt9}3g002ovPDHLkV1n!oC4m3{ literal 0 HcmV?d00001 diff --git a/HEAD/classes.html b/HEAD/classes.html new file mode 100644 index 00000000..2314d390 --- /dev/null +++ b/HEAD/classes.html @@ -0,0 +1,102 @@ + + + + + + + + +LibJWT: Data Structure Index + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + +
+
LibJWT 2.9.99 +
+
The C JSON Web Token Library +JWK +JWKS
+
+
+ + + +
+
+ +
+
+
+ +
+
+
Data Structure Index
+
+ +
+ + + + + diff --git a/HEAD/closed.png b/HEAD/closed.png new file mode 100644 index 0000000000000000000000000000000000000000..98cc2c909da37a6df914fbf67780eebd99c597f5 GIT binary patch literal 132 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4>1|%O$WD@{V-kvUwAr*{o@8{^CZMh(5KoB^r_<4^zF@3)Cp&&t3hdujKf f*?bjBoY!V+E))@{xMcbjXe@)LtDnm{r-UW|*e5JT literal 0 HcmV?d00001 diff --git a/HEAD/cookie.js b/HEAD/cookie.js new file mode 100644 index 00000000..53ad21d9 --- /dev/null +++ b/HEAD/cookie.js @@ -0,0 +1,58 @@ +/*! + Cookie helper functions + Copyright (c) 2023 Dimitri van Heesch + Released under MIT license. +*/ +let Cookie = { + cookie_namespace: 'doxygen_', + + readSetting(cookie,defVal) { + if (window.chrome) { + const val = localStorage.getItem(this.cookie_namespace+cookie) || + sessionStorage.getItem(this.cookie_namespace+cookie); + if (val) return val; + } else { + let myCookie = this.cookie_namespace+cookie+"="; + if (document.cookie) { + const index = document.cookie.indexOf(myCookie); + if (index != -1) { + const valStart = index + myCookie.length; + let valEnd = document.cookie.indexOf(";", valStart); + if (valEnd == -1) { + valEnd = document.cookie.length; + } + return document.cookie.substring(valStart, valEnd); + } + } + } + return defVal; + }, + + writeSetting(cookie,val,days=10*365) { // default days='forever', 0=session cookie, -1=delete + if (window.chrome) { + if (days==0) { + sessionStorage.setItem(this.cookie_namespace+cookie,val); + } else { + localStorage.setItem(this.cookie_namespace+cookie,val); + } + } else { + let date = new Date(); + date.setTime(date.getTime()+(days*24*60*60*1000)); + const expiration = days!=0 ? "expires="+date.toGMTString()+";" : ""; + document.cookie = this.cookie_namespace + cookie + "=" + + val + "; SameSite=Lax;" + expiration + "path=/"; + } + }, + + eraseSetting(cookie) { + if (window.chrome) { + if (localStorage.getItem(this.cookie_namespace+cookie)) { + localStorage.removeItem(this.cookie_namespace+cookie); + } else if (sessionStorage.getItem(this.cookie_namespace+cookie)) { + sessionStorage.removeItem(this.cookie_namespace+cookie); + } + } else { + this.writeSetting(cookie,'',-1); + } + }, +} diff --git a/HEAD/dir_d44c64559bbebec7f509842c48db8b23.html b/HEAD/dir_d44c64559bbebec7f509842c48db8b23.html new file mode 100644 index 00000000..f6e68853 --- /dev/null +++ b/HEAD/dir_d44c64559bbebec7f509842c48db8b23.html @@ -0,0 +1,97 @@ + + + + + + + + +LibJWT: include Directory Reference + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + +
+
LibJWT 2.9.99 +
+
The C JSON Web Token Library +JWK +JWKS
+
+
+ + + +
+
+ +
+
+
+ +
+
+
include Directory Reference
+
+
+
+
+ + + + + diff --git a/HEAD/doc.svg b/HEAD/doc.svg new file mode 100644 index 00000000..0b928a53 --- /dev/null +++ b/HEAD/doc.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/HEAD/docd.svg b/HEAD/docd.svg new file mode 100644 index 00000000..ac18b275 --- /dev/null +++ b/HEAD/docd.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/HEAD/doxygen-awesome-fragment-copy-button.js b/HEAD/doxygen-awesome-fragment-copy-button.js new file mode 100644 index 00000000..86c16fd9 --- /dev/null +++ b/HEAD/doxygen-awesome-fragment-copy-button.js @@ -0,0 +1,85 @@ +/** + +Doxygen Awesome +https://github.com/jothepro/doxygen-awesome-css + +MIT License + +Copyright (c) 2022 - 2023 jothepro + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +class DoxygenAwesomeFragmentCopyButton extends HTMLElement { + constructor() { + super(); + this.onclick=this.copyContent + } + static title = "Copy to clipboard" + static copyIcon = `` + static successIcon = `` + static successDuration = 980 + static init() { + $(function() { + $(document).ready(function() { + if(navigator.clipboard) { + const fragments = document.getElementsByClassName("fragment") + for(const fragment of fragments) { + const fragmentWrapper = document.createElement("div") + fragmentWrapper.className = "doxygen-awesome-fragment-wrapper" + const fragmentCopyButton = document.createElement("doxygen-awesome-fragment-copy-button") + fragmentCopyButton.innerHTML = DoxygenAwesomeFragmentCopyButton.copyIcon + fragmentCopyButton.title = DoxygenAwesomeFragmentCopyButton.title + + fragment.parentNode.replaceChild(fragmentWrapper, fragment) + fragmentWrapper.appendChild(fragment) + fragmentWrapper.appendChild(fragmentCopyButton) + + } + } + }) + }) + } + + + copyContent() { + const content = this.previousSibling.cloneNode(true) + // filter out line number from file listings + content.querySelectorAll(".lineno, .ttc").forEach((node) => { + node.remove() + }) + let textContent = content.textContent + // remove trailing newlines that appear in file listings + let numberOfTrailingNewlines = 0 + while(textContent.charAt(textContent.length - (numberOfTrailingNewlines + 1)) == '\n') { + numberOfTrailingNewlines++; + } + textContent = textContent.substring(0, textContent.length - numberOfTrailingNewlines) + navigator.clipboard.writeText(textContent); + this.classList.add("success") + this.innerHTML = DoxygenAwesomeFragmentCopyButton.successIcon + window.setTimeout(() => { + this.classList.remove("success") + this.innerHTML = DoxygenAwesomeFragmentCopyButton.copyIcon + }, DoxygenAwesomeFragmentCopyButton.successDuration); + } +} + +customElements.define("doxygen-awesome-fragment-copy-button", DoxygenAwesomeFragmentCopyButton) diff --git a/HEAD/doxygen-awesome-interactive-toc.js b/HEAD/doxygen-awesome-interactive-toc.js new file mode 100644 index 00000000..f3c3e770 --- /dev/null +++ b/HEAD/doxygen-awesome-interactive-toc.js @@ -0,0 +1,91 @@ +/** + +Doxygen Awesome +https://github.com/jothepro/doxygen-awesome-css + +MIT License + +Copyright (c) 2022 - 2023 jothepro + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +class DoxygenAwesomeInteractiveToc { + static topOffset = 38 + static hideMobileMenu = true + static headers = [] + + static init() { + window.addEventListener("load", () => { + let toc = document.querySelector(".contents > .toc") + if(toc) { + toc.classList.add("interactive") + if(!DoxygenAwesomeInteractiveToc.hideMobileMenu) { + toc.classList.add("open") + } + document.querySelector(".contents > .toc > h3")?.addEventListener("click", () => { + if(toc.classList.contains("open")) { + toc.classList.remove("open") + } else { + toc.classList.add("open") + } + }) + + document.querySelectorAll(".contents > .toc > ul a").forEach((node) => { + let id = node.getAttribute("href").substring(1) + DoxygenAwesomeInteractiveToc.headers.push({ + node: node, + headerNode: document.getElementById(id) + }) + + document.getElementById("doc-content")?.addEventListener("scroll",this.throttle(DoxygenAwesomeInteractiveToc.update, 100)) + }) + DoxygenAwesomeInteractiveToc.update() + } + }) + } + + static update() { + let active = DoxygenAwesomeInteractiveToc.headers[0]?.node + DoxygenAwesomeInteractiveToc.headers.forEach((header) => { + let position = header.headerNode.getBoundingClientRect().top + header.node.classList.remove("active") + header.node.classList.remove("aboveActive") + if(position < DoxygenAwesomeInteractiveToc.topOffset) { + active = header.node + active?.classList.add("aboveActive") + } + }) + active?.classList.add("active") + active?.classList.remove("aboveActive") + } + + static throttle(func, delay) { + let lastCall = 0; + return function (...args) { + const now = new Date().getTime(); + if (now - lastCall < delay) { + return; + } + lastCall = now; + return setTimeout(() => {func(...args)}, delay); + }; + } +} diff --git a/HEAD/doxygen-awesome-paragraph-link.js b/HEAD/doxygen-awesome-paragraph-link.js new file mode 100644 index 00000000..e53d132c --- /dev/null +++ b/HEAD/doxygen-awesome-paragraph-link.js @@ -0,0 +1,51 @@ +/** + +Doxygen Awesome +https://github.com/jothepro/doxygen-awesome-css + +MIT License + +Copyright (c) 2022 - 2023 jothepro + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +class DoxygenAwesomeParagraphLink { + // Icon from https://fonts.google.com/icons + // Licensed under the Apache 2.0 license: + // https://www.apache.org/licenses/LICENSE-2.0.html + static icon = `` + static title = "Permanent Link" + static init() { + $(function() { + $(document).ready(function() { + document.querySelectorAll(".contents a.anchor[id], .contents .groupheader > a[id]").forEach((node) => { + let anchorlink = document.createElement("a") + anchorlink.setAttribute("href", `#${node.getAttribute("id")}`) + anchorlink.setAttribute("title", DoxygenAwesomeParagraphLink.title) + anchorlink.classList.add("anchorlink") + node.classList.add("anchor") + anchorlink.innerHTML = DoxygenAwesomeParagraphLink.icon + node.parentElement.appendChild(anchorlink) + }) + }) + }) + } +} diff --git a/HEAD/doxygen-awesome.css b/HEAD/doxygen-awesome.css new file mode 100644 index 00000000..c2f41142 --- /dev/null +++ b/HEAD/doxygen-awesome.css @@ -0,0 +1,2681 @@ +/** + +Doxygen Awesome +https://github.com/jothepro/doxygen-awesome-css + +MIT License + +Copyright (c) 2021 - 2023 jothepro + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +html { + /* primary theme color. This will affect the entire websites color scheme: links, arrows, labels, ... */ + --primary-color: #1779c4; + --primary-dark-color: #335c80; + --primary-light-color: #70b1e9; + + /* page base colors */ + --page-background-color: #ffffff; + --page-foreground-color: #2f4153; + --page-secondary-foreground-color: #6f7e8e; + + /* color for all separators on the website: hr, borders, ... */ + --separator-color: #dedede; + + /* border radius for all rounded components. Will affect many components, like dropdowns, memitems, codeblocks, ... */ + --border-radius-large: 8px; + --border-radius-small: 4px; + --border-radius-medium: 6px; + + /* default spacings. Most components reference these values for spacing, to provide uniform spacing on the page. */ + --spacing-small: 5px; + --spacing-medium: 10px; + --spacing-large: 16px; + + /* default box shadow used for raising an element above the normal content. Used in dropdowns, search result, ... */ + --box-shadow: 0 2px 8px 0 rgba(0,0,0,.075); + + --odd-color: rgba(0,0,0,.028); + + /* font-families. will affect all text on the website + * font-family: the normal font for text, headlines, menus + * font-family-monospace: used for preformatted text in memtitle, code, fragments + */ + --font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif; + --font-family-monospace: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace; + + /* font sizes */ + --page-font-size: 15.6px; + --navigation-font-size: 14.4px; + --toc-font-size: 13.4px; + --code-font-size: 14px; /* affects code, fragment */ + --title-font-size: 22px; + + /* content text properties. These only affect the page content, not the navigation or any other ui elements */ + --content-line-height: 27px; + /* The content is centered and constraint in it's width. To make the content fill the whole page, set the variable to auto.*/ + --content-maxwidth: 1050px; + --table-line-height: 24px; + --toc-sticky-top: var(--spacing-medium); + --toc-width: 200px; + --toc-max-height: calc(100vh - 2 * var(--spacing-medium) - 85px); + + /* colors for various content boxes: @warning, @note, @deprecated @bug */ + --warning-color: #faf3d8; + --warning-color-dark: #f3a600; + --warning-color-darker: #5f4204; + --note-color: #e4f3ff; + --note-color-dark: #1879C4; + --note-color-darker: #274a5c; + --todo-color: #e4dafd; + --todo-color-dark: #5b2bdd; + --todo-color-darker: #2a0d72; + --deprecated-color: #ecf0f3; + --deprecated-color-dark: #5b6269; + --deprecated-color-darker: #43454a; + --bug-color: #f8d1cc; + --bug-color-dark: #b61825; + --bug-color-darker: #75070f; + --invariant-color: #d8f1e3; + --invariant-color-dark: #44b86f; + --invariant-color-darker: #265532; + + /* blockquote colors */ + --blockquote-background: #f8f9fa; + --blockquote-foreground: #636568; + + /* table colors */ + --tablehead-background: #f1f1f1; + --tablehead-foreground: var(--page-foreground-color); + + /* menu-display: block | none + * Visibility of the top navigation on screens >= 768px. On smaller screen the menu is always visible. + * `GENERATE_TREEVIEW` MUST be enabled! + */ + --menu-display: block; + + --menu-focus-foreground: var(--page-background-color); + --menu-focus-background: var(--primary-color); + --menu-selected-background: rgba(0,0,0,.05); + + + --header-background: var(--page-background-color); + --header-foreground: var(--page-foreground-color); + + /* searchbar colors */ + --searchbar-background: var(--side-nav-background); + --searchbar-foreground: var(--page-foreground-color); + + /* searchbar size + * (`searchbar-width` is only applied on screens >= 768px. + * on smaller screens the searchbar will always fill the entire screen width) */ + --searchbar-height: 33px; + --searchbar-width: 210px; + --searchbar-border-radius: var(--searchbar-height); + + /* code block colors */ + --code-background: #f5f5f5; + --code-foreground: var(--page-foreground-color); + + /* fragment colors */ + --fragment-background: #F8F9FA; + --fragment-foreground: #37474F; + --fragment-keyword: #bb6bb2; + --fragment-keywordtype: #8258b3; + --fragment-keywordflow: #d67c3b; + --fragment-token: #438a59; + --fragment-comment: #969696; + --fragment-link: #5383d6; + --fragment-preprocessor: #46aaa5; + --fragment-linenumber-color: #797979; + --fragment-linenumber-background: #f4f4f5; + --fragment-linenumber-border: #e3e5e7; + --fragment-lineheight: 20px; + + /* sidebar navigation (treeview) colors */ + --side-nav-background: #fbfbfb; + --side-nav-foreground: var(--page-foreground-color); + --side-nav-arrow-opacity: 0; + --side-nav-arrow-hover-opacity: 0.9; + + --toc-background: var(--side-nav-background); + --toc-foreground: var(--side-nav-foreground); + + /* height of an item in any tree / collapsible table */ + --tree-item-height: 30px; + + --memname-font-size: var(--code-font-size); + --memtitle-font-size: 18px; + + --webkit-scrollbar-size: 7px; + --webkit-scrollbar-padding: 4px; + --webkit-scrollbar-color: var(--separator-color); + + --animation-duration: .12s +} + +@media screen and (max-width: 767px) { + html { + --page-font-size: 16px; + --navigation-font-size: 16px; + --toc-font-size: 15px; + --code-font-size: 15px; /* affects code, fragment */ + --title-font-size: 22px; + } +} + +@media (prefers-color-scheme: dark) { + html:not(.light-mode) { + color-scheme: dark; + + --primary-color: #1982d2; + --primary-dark-color: #86a9c4; + --primary-light-color: #4779ac; + + --box-shadow: 0 2px 8px 0 rgba(0,0,0,.35); + + --odd-color: rgba(100,100,100,.06); + + --menu-selected-background: rgba(0,0,0,.4); + + --page-background-color: #1C1D1F; + --page-foreground-color: #d2dbde; + --page-secondary-foreground-color: #859399; + --separator-color: #38393b; + --side-nav-background: #252628; + + --code-background: #2a2c2f; + + --tablehead-background: #2a2c2f; + + --blockquote-background: #222325; + --blockquote-foreground: #7e8c92; + + --warning-color: #3b2e04; + --warning-color-dark: #f1b602; + --warning-color-darker: #ceb670; + --note-color: #163750; + --note-color-dark: #1982D2; + --note-color-darker: #dcf0fa; + --todo-color: #2a2536; + --todo-color-dark: #7661b3; + --todo-color-darker: #ae9ed6; + --deprecated-color: #2e323b; + --deprecated-color-dark: #738396; + --deprecated-color-darker: #abb0bd; + --bug-color: #2e1917; + --bug-color-dark: #ad2617; + --bug-color-darker: #f5b1aa; + --invariant-color: #303a35; + --invariant-color-dark: #76ce96; + --invariant-color-darker: #cceed5; + + --fragment-background: #282c34; + --fragment-foreground: #dbe4eb; + --fragment-keyword: #cc99cd; + --fragment-keywordtype: #ab99cd; + --fragment-keywordflow: #e08000; + --fragment-token: #7ec699; + --fragment-comment: #999999; + --fragment-link: #98c0e3; + --fragment-preprocessor: #65cabe; + --fragment-linenumber-color: #cccccc; + --fragment-linenumber-background: #35393c; + --fragment-linenumber-border: #1f1f1f; + } +} + +/* dark mode variables are defined twice, to support both the dark-mode without and with doxygen-awesome-darkmode-toggle.js */ +html.dark-mode { + color-scheme: dark; + + --primary-color: #1982d2; + --primary-dark-color: #86a9c4; + --primary-light-color: #4779ac; + + --box-shadow: 0 2px 8px 0 rgba(0,0,0,.30); + + --odd-color: rgba(100,100,100,.06); + + --menu-selected-background: rgba(0,0,0,.4); + + --page-background-color: #1C1D1F; + --page-foreground-color: #d2dbde; + --page-secondary-foreground-color: #859399; + --separator-color: #38393b; + --side-nav-background: #252628; + + --code-background: #2a2c2f; + + --tablehead-background: #2a2c2f; + + --blockquote-background: #222325; + --blockquote-foreground: #7e8c92; + + --warning-color: #3b2e04; + --warning-color-dark: #f1b602; + --warning-color-darker: #ceb670; + --note-color: #163750; + --note-color-dark: #1982D2; + --note-color-darker: #dcf0fa; + --todo-color: #2a2536; + --todo-color-dark: #7661b3; + --todo-color-darker: #ae9ed6; + --deprecated-color: #2e323b; + --deprecated-color-dark: #738396; + --deprecated-color-darker: #abb0bd; + --bug-color: #2e1917; + --bug-color-dark: #ad2617; + --bug-color-darker: #f5b1aa; + --invariant-color: #303a35; + --invariant-color-dark: #76ce96; + --invariant-color-darker: #cceed5; + + --fragment-background: #282c34; + --fragment-foreground: #dbe4eb; + --fragment-keyword: #cc99cd; + --fragment-keywordtype: #ab99cd; + --fragment-keywordflow: #e08000; + --fragment-token: #7ec699; + --fragment-comment: #999999; + --fragment-link: #98c0e3; + --fragment-preprocessor: #65cabe; + --fragment-linenumber-color: #cccccc; + --fragment-linenumber-background: #35393c; + --fragment-linenumber-border: #1f1f1f; +} + +body { + color: var(--page-foreground-color); + background-color: var(--page-background-color); + font-size: var(--page-font-size); +} + +body, table, div, p, dl, #nav-tree .label, .title, +.sm-dox a, .sm-dox a:hover, .sm-dox a:focus, #projectname, +.SelectItem, #MSearchField, .navpath li.navelem a, +.navpath li.navelem a:hover, p.reference, p.definition, div.toc li, div.toc h3 { + font-family: var(--font-family); +} + +h1, h2, h3, h4, h5 { + margin-top: 1em; + font-weight: 600; + line-height: initial; +} + +p, div, table, dl, p.reference, p.definition { + font-size: var(--page-font-size); +} + +p.reference, p.definition { + color: var(--page-secondary-foreground-color); +} + +a:link, a:visited, a:hover, a:focus, a:active { + color: var(--primary-color) !important; + font-weight: 500; + background: none; +} + +a.anchor { + scroll-margin-top: var(--spacing-large); + display: block; +} + +/* + Title and top navigation + */ + +#top { + background: var(--header-background); + border-bottom: 1px solid var(--separator-color); +} + +@media screen and (min-width: 768px) { + #top { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + align-items: center; + } +} + +#main-nav { + flex-grow: 5; + padding: var(--spacing-small) var(--spacing-medium); +} + +#titlearea { + width: auto; + padding: var(--spacing-medium) var(--spacing-large); + background: none; + color: var(--header-foreground); + border-bottom: none; +} + +@media screen and (max-width: 767px) { + #titlearea { + padding-bottom: var(--spacing-small); + } +} + +#titlearea table tbody tr { + height: auto !important; +} + +#projectname { + font-size: var(--title-font-size); + font-weight: 600; +} + +#projectnumber { + font-family: inherit; + font-size: 60%; +} + +#projectbrief { + font-family: inherit; + font-size: 80%; +} + +#projectlogo { + vertical-align: middle; +} + +#projectlogo img { + max-height: calc(var(--title-font-size) * 2); + margin-right: var(--spacing-small); +} + +.sm-dox, .tabs, .tabs2, .tabs3 { + background: none; + padding: 0; +} + +.tabs, .tabs2, .tabs3 { + border-bottom: 1px solid var(--separator-color); + margin-bottom: -1px; +} + +.main-menu-btn-icon, .main-menu-btn-icon:before, .main-menu-btn-icon:after { + background: var(--page-secondary-foreground-color); +} + +@media screen and (max-width: 767px) { + .sm-dox a span.sub-arrow { + background: var(--code-background); + } + + #main-menu a.has-submenu span.sub-arrow { + color: var(--page-secondary-foreground-color); + border-radius: var(--border-radius-medium); + } + + #main-menu a.has-submenu:hover span.sub-arrow { + color: var(--page-foreground-color); + } +} + +@media screen and (min-width: 768px) { + .sm-dox li, .tablist li { + display: var(--menu-display); + } + + .sm-dox a span.sub-arrow { + border-color: var(--header-foreground) transparent transparent transparent; + } + + .sm-dox a:hover span.sub-arrow { + border-color: var(--menu-focus-foreground) transparent transparent transparent; + } + + .sm-dox ul a span.sub-arrow { + border-color: transparent transparent transparent var(--page-foreground-color); + } + + .sm-dox ul a:hover span.sub-arrow { + border-color: transparent transparent transparent var(--menu-focus-foreground); + } +} + +.sm-dox ul { + background: var(--page-background-color); + box-shadow: var(--box-shadow); + border: 1px solid var(--separator-color); + border-radius: var(--border-radius-medium) !important; + padding: var(--spacing-small); + animation: ease-out 150ms slideInMenu; +} + +@keyframes slideInMenu { + from { + opacity: 0; + transform: translate(0px, -2px); + } + + to { + opacity: 1; + transform: translate(0px, 0px); + } +} + +.sm-dox ul a { + color: var(--page-foreground-color) !important; + background: var(--page-background-color); + font-size: var(--navigation-font-size); +} + +.sm-dox>li>ul:after { + border-bottom-color: var(--page-background-color) !important; +} + +.sm-dox>li>ul:before { + border-bottom-color: var(--separator-color) !important; +} + +.sm-dox ul a:hover, .sm-dox ul a:active, .sm-dox ul a:focus { + font-size: var(--navigation-font-size) !important; + color: var(--menu-focus-foreground) !important; + text-shadow: none; + background-color: var(--menu-focus-background); + border-radius: var(--border-radius-small) !important; +} + +.sm-dox a, .sm-dox a:focus, .tablist li, .tablist li a, .tablist li.current a { + text-shadow: none; + background: transparent; + background-image: none !important; + color: var(--header-foreground) !important; + font-weight: normal; + font-size: var(--navigation-font-size); + border-radius: var(--border-radius-small) !important; +} + +.sm-dox a:focus { + outline: auto; +} + +.sm-dox a:hover, .sm-dox a:active, .tablist li a:hover { + text-shadow: none; + font-weight: normal; + background: var(--menu-focus-background); + color: var(--menu-focus-foreground) !important; + border-radius: var(--border-radius-small) !important; + font-size: var(--navigation-font-size); +} + +.tablist li.current { + border-radius: var(--border-radius-small); + background: var(--menu-selected-background); +} + +.tablist li { + margin: var(--spacing-small) 0 var(--spacing-small) var(--spacing-small); +} + +.tablist a { + padding: 0 var(--spacing-large); +} + + +/* + Search box + */ + +#MSearchBox { + height: var(--searchbar-height); + background: var(--searchbar-background); + border-radius: var(--searchbar-border-radius); + border: 1px solid var(--separator-color); + overflow: hidden; + width: var(--searchbar-width); + position: relative; + box-shadow: none; + display: block; + margin-top: 0; +} + +/* until Doxygen 1.9.4 */ +.left img#MSearchSelect { + left: 0; + user-select: none; + padding-left: 8px; +} + +/* Doxygen 1.9.5 */ +.left span#MSearchSelect { + left: 0; + user-select: none; + margin-left: 8px; + padding: 0; +} + +.left #MSearchSelect[src$=".png"] { + padding-left: 0 +} + +.SelectionMark { + user-select: none; +} + +.tabs .left #MSearchSelect { + padding-left: 0; +} + +.tabs #MSearchBox { + position: absolute; + right: var(--spacing-medium); +} + +@media screen and (max-width: 767px) { + .tabs #MSearchBox { + position: relative; + right: 0; + margin-left: var(--spacing-medium); + margin-top: 0; + } +} + +#MSearchSelectWindow, #MSearchResultsWindow { + z-index: 9999; +} + +#MSearchBox.MSearchBoxActive { + border-color: var(--primary-color); + box-shadow: inset 0 0 0 1px var(--primary-color); +} + +#main-menu > li:last-child { + margin-right: 0; +} + +@media screen and (max-width: 767px) { + #main-menu > li:last-child { + height: 50px; + } +} + +#MSearchField { + font-size: var(--navigation-font-size); + height: calc(var(--searchbar-height) - 2px); + background: transparent; + width: calc(var(--searchbar-width) - 64px); +} + +.MSearchBoxActive #MSearchField { + color: var(--searchbar-foreground); +} + +#MSearchSelect { + top: calc(calc(var(--searchbar-height) / 2) - 11px); +} + +#MSearchBox span.left, #MSearchBox span.right { + background: none; + background-image: none; +} + +#MSearchBox span.right { + padding-top: calc(calc(var(--searchbar-height) / 2) - 12px); + position: absolute; + right: var(--spacing-small); +} + +.tabs #MSearchBox span.right { + top: calc(calc(var(--searchbar-height) / 2) - 12px); +} + +@keyframes slideInSearchResults { + from { + opacity: 0; + transform: translate(0, 15px); + } + + to { + opacity: 1; + transform: translate(0, 20px); + } +} + +#MSearchResultsWindow { + left: auto !important; + right: var(--spacing-medium); + border-radius: var(--border-radius-large); + border: 1px solid var(--separator-color); + transform: translate(0, 20px); + box-shadow: var(--box-shadow); + animation: ease-out 280ms slideInSearchResults; + background: var(--page-background-color); +} + +iframe#MSearchResults { + margin: 4px; +} + +iframe { + color-scheme: normal; +} + +@media (prefers-color-scheme: dark) { + html:not(.light-mode) iframe#MSearchResults { + filter: invert() hue-rotate(180deg); + } +} + +html.dark-mode iframe#MSearchResults { + filter: invert() hue-rotate(180deg); +} + +#MSearchResults .SRPage { + background-color: transparent; +} + +#MSearchResults .SRPage .SREntry { + font-size: 10pt; + padding: var(--spacing-small) var(--spacing-medium); +} + +#MSearchSelectWindow { + border: 1px solid var(--separator-color); + border-radius: var(--border-radius-medium); + box-shadow: var(--box-shadow); + background: var(--page-background-color); + padding-top: var(--spacing-small); + padding-bottom: var(--spacing-small); +} + +#MSearchSelectWindow a.SelectItem { + font-size: var(--navigation-font-size); + line-height: var(--content-line-height); + margin: 0 var(--spacing-small); + border-radius: var(--border-radius-small); + color: var(--page-foreground-color) !important; + font-weight: normal; +} + +#MSearchSelectWindow a.SelectItem:hover { + background: var(--menu-focus-background); + color: var(--menu-focus-foreground) !important; +} + +@media screen and (max-width: 767px) { + #MSearchBox { + margin-top: var(--spacing-medium); + margin-bottom: var(--spacing-medium); + width: calc(100vw - 30px); + } + + #main-menu > li:last-child { + float: none !important; + } + + #MSearchField { + width: calc(100vw - 110px); + } + + @keyframes slideInSearchResultsMobile { + from { + opacity: 0; + transform: translate(0, 15px); + } + + to { + opacity: 1; + transform: translate(0, 20px); + } + } + + #MSearchResultsWindow { + left: var(--spacing-medium) !important; + right: var(--spacing-medium); + overflow: auto; + transform: translate(0, 20px); + animation: ease-out 280ms slideInSearchResultsMobile; + width: auto !important; + } + + /* + * Overwrites for fixing the searchbox on mobile in doxygen 1.9.2 + */ + label.main-menu-btn ~ #searchBoxPos1 { + top: 3px !important; + right: 6px !important; + left: 45px; + display: flex; + } + + label.main-menu-btn ~ #searchBoxPos1 > #MSearchBox { + margin-top: 0; + margin-bottom: 0; + flex-grow: 2; + float: left; + } +} + +/* + Tree view + */ + +#side-nav { + padding: 0 !important; + background: var(--side-nav-background); + min-width: 8px; + max-width: 50vw; +} + +@media screen and (max-width: 767px) { + #side-nav { + display: none; + } + + #doc-content { + margin-left: 0 !important; + } +} + +#nav-tree { + background: transparent; + margin-right: 1px; +} + +#nav-tree .label { + font-size: var(--navigation-font-size); +} + +#nav-tree .item { + height: var(--tree-item-height); + line-height: var(--tree-item-height); + overflow: hidden; + text-overflow: ellipsis; +} + +#nav-tree .item > a:focus { + outline: none; +} + +#nav-sync { + bottom: 12px; + right: 12px; + top: auto !important; + user-select: none; +} + +#nav-tree .selected { + text-shadow: none; + background-image: none; + background-color: transparent; + position: relative; + color: var(--primary-color) !important; + font-weight: 500; +} + +#nav-tree .selected::after { + content: ""; + position: absolute; + top: 1px; + bottom: 1px; + left: 0; + width: 4px; + border-radius: 0 var(--border-radius-small) var(--border-radius-small) 0; + background: var(--primary-color); +} + + +#nav-tree a { + color: var(--side-nav-foreground) !important; + font-weight: normal; +} + +#nav-tree a:focus { + outline-style: auto; +} + +#nav-tree .arrow { + opacity: var(--side-nav-arrow-opacity); + background: none; +} + +.arrow { + color: inherit; + cursor: pointer; + font-size: 45%; + vertical-align: middle; + margin-right: 2px; + font-family: serif; + height: auto; + text-align: right; +} + +#nav-tree div.item:hover .arrow, #nav-tree a:focus .arrow { + opacity: var(--side-nav-arrow-hover-opacity); +} + +#nav-tree .selected a { + color: var(--primary-color) !important; + font-weight: bolder; + font-weight: 600; +} + +.ui-resizable-e { + width: 4px; + background: transparent; + box-shadow: inset -1px 0 0 0 var(--separator-color); +} + +/* + Contents + */ + +div.header { + border-bottom: 1px solid var(--separator-color); + background-color: var(--page-background-color); + background-image: none; +} + +@media screen and (min-width: 1000px) { + #doc-content > div > div.contents, + .PageDoc > div.contents { + display: flex; + flex-direction: row-reverse; + flex-wrap: nowrap; + align-items: flex-start; + } + + div.contents .textblock { + min-width: 200px; + flex-grow: 1; + } +} + +div.contents, div.header .title, div.header .summary { + max-width: var(--content-maxwidth); +} + +div.contents, div.header .title { + line-height: initial; + margin: calc(var(--spacing-medium) + .2em) auto var(--spacing-medium) auto; +} + +div.header .summary { + margin: var(--spacing-medium) auto 0 auto; +} + +div.headertitle { + padding: 0; +} + +div.header .title { + font-weight: 600; + font-size: 225%; + padding: var(--spacing-medium) var(--spacing-large); + word-break: break-word; +} + +div.header .summary { + width: auto; + display: block; + float: none; + padding: 0 var(--spacing-large); +} + +td.memSeparator { + border-color: var(--separator-color); +} + +span.mlabel { + background: var(--primary-color); + border: none; + padding: 4px 9px; + border-radius: 12px; + margin-right: var(--spacing-medium); +} + +span.mlabel:last-of-type { + margin-right: 2px; +} + +div.contents { + padding: 0 var(--spacing-large); +} + +div.contents p, div.contents li { + line-height: var(--content-line-height); +} + +div.contents div.dyncontent { + margin: var(--spacing-medium) 0; +} + +@media (prefers-color-scheme: dark) { + html:not(.light-mode) div.contents div.dyncontent img, + html:not(.light-mode) div.contents center img, + html:not(.light-mode) div.contents > table img, + html:not(.light-mode) div.contents div.dyncontent iframe, + html:not(.light-mode) div.contents center iframe, + html:not(.light-mode) div.contents table iframe, + html:not(.light-mode) div.contents .dotgraph iframe { + filter: brightness(89%) hue-rotate(180deg) invert(); + } +} + +html.dark-mode div.contents div.dyncontent img, +html.dark-mode div.contents center img, +html.dark-mode div.contents > table img, +html.dark-mode div.contents div.dyncontent iframe, +html.dark-mode div.contents center iframe, +html.dark-mode div.contents table iframe, +html.dark-mode div.contents .dotgraph iframe + { + filter: brightness(89%) hue-rotate(180deg) invert(); +} + +h2.groupheader { + border-bottom: 0px; + color: var(--page-foreground-color); + box-shadow: + 100px 0 var(--page-background-color), + -100px 0 var(--page-background-color), + 100px 0.75px var(--separator-color), + -100px 0.75px var(--separator-color), + 500px 0 var(--page-background-color), + -500px 0 var(--page-background-color), + 500px 0.75px var(--separator-color), + -500px 0.75px var(--separator-color), + 900px 0 var(--page-background-color), + -900px 0 var(--page-background-color), + 900px 0.75px var(--separator-color), + -900px 0.75px var(--separator-color), + 1400px 0 var(--page-background-color), + -1400px 0 var(--page-background-color), + 1400px 0.75px var(--separator-color), + -1400px 0.75px var(--separator-color), + 1900px 0 var(--page-background-color), + -1900px 0 var(--page-background-color), + 1900px 0.75px var(--separator-color), + -1900px 0.75px var(--separator-color); +} + +blockquote { + margin: 0 var(--spacing-medium) 0 var(--spacing-medium); + padding: var(--spacing-small) var(--spacing-large); + background: var(--blockquote-background); + color: var(--blockquote-foreground); + border-left: 0; + overflow: visible; + border-radius: var(--border-radius-medium); + overflow: visible; + position: relative; +} + +blockquote::before, blockquote::after { + font-weight: bold; + font-family: serif; + font-size: 360%; + opacity: .15; + position: absolute; +} + +blockquote::before { + content: "“"; + left: -10px; + top: 4px; +} + +blockquote::after { + content: "”"; + right: -8px; + bottom: -25px; +} + +blockquote p { + margin: var(--spacing-small) 0 var(--spacing-medium) 0; +} +.paramname, .paramname em { + font-weight: 600; + color: var(--primary-dark-color); +} + +.paramname > code { + border: 0; +} + +table.params .paramname { + font-weight: 600; + font-family: var(--font-family-monospace); + font-size: var(--code-font-size); + padding-right: var(--spacing-small); + line-height: var(--table-line-height); +} + +h1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow { + text-shadow: 0 0 15px var(--primary-light-color); +} + +.alphachar a { + color: var(--page-foreground-color); +} + +.dotgraph { + max-width: 100%; + overflow-x: scroll; +} + +.dotgraph .caption { + position: sticky; + left: 0; +} + +/* Wrap Graphviz graphs with the `interactive_dotgraph` class if `INTERACTIVE_SVG = YES` */ +.interactive_dotgraph .dotgraph iframe { + max-width: 100%; +} + +/* + Table of Contents + */ + +div.contents .toc { + max-height: var(--toc-max-height); + min-width: var(--toc-width); + border: 0; + border-left: 1px solid var(--separator-color); + border-radius: 0; + background-color: var(--page-background-color); + box-shadow: none; + position: sticky; + top: var(--toc-sticky-top); + padding: 0 var(--spacing-large); + margin: var(--spacing-small) 0 var(--spacing-large) var(--spacing-large); +} + +div.toc h3 { + color: var(--toc-foreground); + font-size: var(--navigation-font-size); + margin: var(--spacing-large) 0 var(--spacing-medium) 0; +} + +div.toc li { + padding: 0; + background: none; + line-height: var(--toc-font-size); + margin: var(--toc-font-size) 0 0 0; +} + +div.toc li::before { + display: none; +} + +div.toc ul { + margin-top: 0 +} + +div.toc li a { + font-size: var(--toc-font-size); + color: var(--page-foreground-color) !important; + text-decoration: none; +} + +div.toc li a:hover, div.toc li a.active { + color: var(--primary-color) !important; +} + +div.toc li a.aboveActive { + color: var(--page-secondary-foreground-color) !important; +} + + +@media screen and (max-width: 999px) { + div.contents .toc { + max-height: 45vh; + float: none; + width: auto; + margin: 0 0 var(--spacing-medium) 0; + position: relative; + top: 0; + position: relative; + border: 1px solid var(--separator-color); + border-radius: var(--border-radius-medium); + background-color: var(--toc-background); + box-shadow: var(--box-shadow); + } + + div.contents .toc.interactive { + max-height: calc(var(--navigation-font-size) + 2 * var(--spacing-large)); + overflow: hidden; + } + + div.contents .toc > h3 { + -webkit-tap-highlight-color: transparent; + cursor: pointer; + position: sticky; + top: 0; + background-color: var(--toc-background); + margin: 0; + padding: var(--spacing-large) 0; + display: block; + } + + div.contents .toc.interactive > h3::before { + content: ""; + width: 0; + height: 0; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 5px solid var(--primary-color); + display: inline-block; + margin-right: var(--spacing-small); + margin-bottom: calc(var(--navigation-font-size) / 4); + transform: rotate(-90deg); + transition: transform var(--animation-duration) ease-out; + } + + div.contents .toc.interactive.open > h3::before { + transform: rotate(0deg); + } + + div.contents .toc.interactive.open { + max-height: 45vh; + overflow: auto; + transition: max-height 0.2s ease-in-out; + } + + div.contents .toc a, div.contents .toc a.active { + color: var(--primary-color) !important; + } + + div.contents .toc a:hover { + text-decoration: underline; + } +} + +/* + Code & Fragments + */ + +code, div.fragment, pre.fragment { + border-radius: var(--border-radius-small); + border: 1px solid var(--separator-color); + overflow: hidden; +} + +code { + display: inline; + background: var(--code-background); + color: var(--code-foreground); + padding: 2px 6px; +} + +div.fragment, pre.fragment { + margin: var(--spacing-medium) 0; + padding: calc(var(--spacing-large) - (var(--spacing-large) / 6)) var(--spacing-large); + background: var(--fragment-background); + color: var(--fragment-foreground); + overflow-x: auto; +} + +@media screen and (max-width: 767px) { + div.fragment, pre.fragment { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + border-right: 0; + } + + .contents > div.fragment, + .textblock > div.fragment, + .textblock > pre.fragment, + .textblock > .tabbed > ul > li > div.fragment, + .textblock > .tabbed > ul > li > pre.fragment, + .contents > .doxygen-awesome-fragment-wrapper > div.fragment, + .textblock > .doxygen-awesome-fragment-wrapper > div.fragment, + .textblock > .doxygen-awesome-fragment-wrapper > pre.fragment, + .textblock > .tabbed > ul > li > .doxygen-awesome-fragment-wrapper > div.fragment, + .textblock > .tabbed > ul > li > .doxygen-awesome-fragment-wrapper > pre.fragment { + margin: var(--spacing-medium) calc(0px - var(--spacing-large)); + border-radius: 0; + border-left: 0; + } + + .textblock li > .fragment, + .textblock li > .doxygen-awesome-fragment-wrapper > .fragment { + margin: var(--spacing-medium) calc(0px - var(--spacing-large)); + } + + .memdoc li > .fragment, + .memdoc li > .doxygen-awesome-fragment-wrapper > .fragment { + margin: var(--spacing-medium) calc(0px - var(--spacing-medium)); + } + + .textblock ul, .memdoc ul { + overflow: initial; + } + + .memdoc > div.fragment, + .memdoc > pre.fragment, + dl dd > div.fragment, + dl dd pre.fragment, + .memdoc > .doxygen-awesome-fragment-wrapper > div.fragment, + .memdoc > .doxygen-awesome-fragment-wrapper > pre.fragment, + dl dd > .doxygen-awesome-fragment-wrapper > div.fragment, + dl dd .doxygen-awesome-fragment-wrapper > pre.fragment { + margin: var(--spacing-medium) calc(0px - var(--spacing-medium)); + border-radius: 0; + border-left: 0; + } +} + +code, code a, pre.fragment, div.fragment, div.fragment .line, div.fragment span, div.fragment .line a, div.fragment .line span { + font-family: var(--font-family-monospace); + font-size: var(--code-font-size) !important; +} + +div.line:after { + margin-right: var(--spacing-medium); +} + +div.fragment .line, pre.fragment { + white-space: pre; + word-wrap: initial; + line-height: var(--fragment-lineheight); +} + +div.fragment span.keyword { + color: var(--fragment-keyword); +} + +div.fragment span.keywordtype { + color: var(--fragment-keywordtype); +} + +div.fragment span.keywordflow { + color: var(--fragment-keywordflow); +} + +div.fragment span.stringliteral { + color: var(--fragment-token) +} + +div.fragment span.comment { + color: var(--fragment-comment); +} + +div.fragment a.code { + color: var(--fragment-link) !important; +} + +div.fragment span.preprocessor { + color: var(--fragment-preprocessor); +} + +div.fragment span.lineno { + display: inline-block; + width: 27px; + border-right: none; + background: var(--fragment-linenumber-background); + color: var(--fragment-linenumber-color); +} + +div.fragment span.lineno a { + background: none; + color: var(--fragment-link) !important; +} + +div.fragment > .line:first-child .lineno { + box-shadow: -999999px 0px 0 999999px var(--fragment-linenumber-background), -999998px 0px 0 999999px var(--fragment-linenumber-border); + background-color: var(--fragment-linenumber-background) !important; +} + +div.line { + border-radius: var(--border-radius-small); +} + +div.line.glow { + background-color: var(--primary-light-color); + box-shadow: none; +} + +/* + dl warning, attention, note, deprecated, bug, ... + */ + +dl.bug dt a, dl.deprecated dt a, dl.todo dt a { + font-weight: bold !important; +} + +dl.warning, dl.attention, dl.note, dl.deprecated, dl.bug, dl.invariant, dl.pre, dl.post, dl.todo, dl.remark { + padding: var(--spacing-medium); + margin: var(--spacing-medium) 0; + color: var(--page-background-color); + overflow: hidden; + margin-left: 0; + border-radius: var(--border-radius-small); +} + +dl.section dd { + margin-bottom: 2px; +} + +dl.warning, dl.attention { + background: var(--warning-color); + border-left: 8px solid var(--warning-color-dark); + color: var(--warning-color-darker); +} + +dl.warning dt, dl.attention dt { + color: var(--warning-color-dark); +} + +dl.note, dl.remark { + background: var(--note-color); + border-left: 8px solid var(--note-color-dark); + color: var(--note-color-darker); +} + +dl.note dt, dl.remark dt { + color: var(--note-color-dark); +} + +dl.todo { + background: var(--todo-color); + border-left: 8px solid var(--todo-color-dark); + color: var(--todo-color-darker); +} + +dl.todo dt a { + color: var(--todo-color-dark) !important; +} + +dl.bug dt a { + color: var(--todo-color-dark) !important; +} + +dl.bug { + background: var(--bug-color); + border-left: 8px solid var(--bug-color-dark); + color: var(--bug-color-darker); +} + +dl.bug dt a { + color: var(--bug-color-dark) !important; +} + +dl.deprecated { + background: var(--deprecated-color); + border-left: 8px solid var(--deprecated-color-dark); + color: var(--deprecated-color-darker); +} + +dl.deprecated dt a { + color: var(--deprecated-color-dark) !important; +} + +dl.section dd, dl.bug dd, dl.deprecated dd, dl.todo dd { + margin-inline-start: 0px; +} + +dl.invariant, dl.pre, dl.post { + background: var(--invariant-color); + border-left: 8px solid var(--invariant-color-dark); + color: var(--invariant-color-darker); +} + +dl.invariant dt, dl.pre dt, dl.post dt { + color: var(--invariant-color-dark); +} + +/* + memitem + */ + +div.memdoc, div.memproto, h2.memtitle { + box-shadow: none; + background-image: none; + border: none; +} + +div.memdoc { + padding: 0 var(--spacing-medium); + background: var(--page-background-color); +} + +h2.memtitle, div.memitem { + border: 1px solid var(--separator-color); + box-shadow: var(--box-shadow); +} + +h2.memtitle { + box-shadow: 0px var(--spacing-medium) 0 -1px var(--fragment-background), var(--box-shadow); +} + +div.memitem { + transition: none; +} + +div.memproto, h2.memtitle { + background: var(--fragment-background); +} + +h2.memtitle { + font-weight: 500; + font-size: var(--memtitle-font-size); + font-family: var(--font-family-monospace); + border-bottom: none; + border-top-left-radius: var(--border-radius-medium); + border-top-right-radius: var(--border-radius-medium); + word-break: break-all; + position: relative; +} + +h2.memtitle:after { + content: ""; + display: block; + background: var(--fragment-background); + height: var(--spacing-medium); + bottom: calc(0px - var(--spacing-medium)); + left: 0; + right: -14px; + position: absolute; + border-top-right-radius: var(--border-radius-medium); +} + +h2.memtitle > span.permalink { + font-size: inherit; +} + +h2.memtitle > span.permalink > a { + text-decoration: none; + padding-left: 3px; + margin-right: -4px; + user-select: none; + display: inline-block; + margin-top: -6px; +} + +h2.memtitle > span.permalink > a:hover { + color: var(--primary-dark-color) !important; +} + +a:target + h2.memtitle, a:target + h2.memtitle + div.memitem { + border-color: var(--primary-light-color); +} + +div.memitem { + border-top-right-radius: var(--border-radius-medium); + border-bottom-right-radius: var(--border-radius-medium); + border-bottom-left-radius: var(--border-radius-medium); + overflow: hidden; + display: block !important; +} + +div.memdoc { + border-radius: 0; +} + +div.memproto { + border-radius: 0 var(--border-radius-small) 0 0; + overflow: auto; + border-bottom: 1px solid var(--separator-color); + padding: var(--spacing-medium); + margin-bottom: -1px; +} + +div.memtitle { + border-top-right-radius: var(--border-radius-medium); + border-top-left-radius: var(--border-radius-medium); +} + +div.memproto table.memname { + font-family: var(--font-family-monospace); + color: var(--page-foreground-color); + font-size: var(--memname-font-size); + text-shadow: none; +} + +div.memproto div.memtemplate { + font-family: var(--font-family-monospace); + color: var(--primary-dark-color); + font-size: var(--memname-font-size); + margin-left: 2px; + text-shadow: none; +} + +table.mlabels, table.mlabels > tbody { + display: block; +} + +td.mlabels-left { + width: auto; +} + +td.mlabels-right { + margin-top: 3px; + position: sticky; + left: 0; +} + +table.mlabels > tbody > tr:first-child { + display: flex; + justify-content: space-between; + flex-wrap: wrap; +} + +.memname, .memitem span.mlabels { + margin: 0 +} + +/* + reflist + */ + +dl.reflist { + box-shadow: var(--box-shadow); + border-radius: var(--border-radius-medium); + border: 1px solid var(--separator-color); + overflow: hidden; + padding: 0; +} + + +dl.reflist dt, dl.reflist dd { + box-shadow: none; + text-shadow: none; + background-image: none; + border: none; + padding: 12px; +} + + +dl.reflist dt { + font-weight: 500; + border-radius: 0; + background: var(--code-background); + border-bottom: 1px solid var(--separator-color); + color: var(--page-foreground-color) +} + + +dl.reflist dd { + background: none; +} + +/* + Table + */ + +.contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname), +.contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody { + display: inline-block; + max-width: 100%; +} + +.contents > table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname):not(.classindex) { + margin-left: calc(0px - var(--spacing-large)); + margin-right: calc(0px - var(--spacing-large)); + max-width: calc(100% + 2 * var(--spacing-large)); +} + +table.fieldtable, +table.markdownTable tbody, +table.doxtable tbody { + border: none; + margin: var(--spacing-medium) 0; + box-shadow: 0 0 0 1px var(--separator-color); + border-radius: var(--border-radius-small); +} + +table.markdownTable, table.doxtable, table.fieldtable { + padding: 1px; +} + +table.doxtable caption { + display: block; +} + +table.fieldtable { + border-collapse: collapse; + width: 100%; +} + +th.markdownTableHeadLeft, +th.markdownTableHeadRight, +th.markdownTableHeadCenter, +th.markdownTableHeadNone, +table.doxtable th { + background: var(--tablehead-background); + color: var(--tablehead-foreground); + font-weight: 600; + font-size: var(--page-font-size); +} + +th.markdownTableHeadLeft:first-child, +th.markdownTableHeadRight:first-child, +th.markdownTableHeadCenter:first-child, +th.markdownTableHeadNone:first-child, +table.doxtable tr th:first-child { + border-top-left-radius: var(--border-radius-small); +} + +th.markdownTableHeadLeft:last-child, +th.markdownTableHeadRight:last-child, +th.markdownTableHeadCenter:last-child, +th.markdownTableHeadNone:last-child, +table.doxtable tr th:last-child { + border-top-right-radius: var(--border-radius-small); +} + +table.markdownTable td, +table.markdownTable th, +table.fieldtable td, +table.fieldtable th, +table.doxtable td, +table.doxtable th { + border: 1px solid var(--separator-color); + padding: var(--spacing-small) var(--spacing-medium); +} + +table.markdownTable td:last-child, +table.markdownTable th:last-child, +table.fieldtable td:last-child, +table.fieldtable th:last-child, +table.doxtable td:last-child, +table.doxtable th:last-child { + border-right: none; +} + +table.markdownTable td:first-child, +table.markdownTable th:first-child, +table.fieldtable td:first-child, +table.fieldtable th:first-child, +table.doxtable td:first-child, +table.doxtable th:first-child { + border-left: none; +} + +table.markdownTable tr:first-child td, +table.markdownTable tr:first-child th, +table.fieldtable tr:first-child td, +table.fieldtable tr:first-child th, +table.doxtable tr:first-child td, +table.doxtable tr:first-child th { + border-top: none; +} + +table.markdownTable tr:last-child td, +table.markdownTable tr:last-child th, +table.fieldtable tr:last-child td, +table.fieldtable tr:last-child th, +table.doxtable tr:last-child td, +table.doxtable tr:last-child th { + border-bottom: none; +} + +table.markdownTable tr, table.doxtable tr { + border-bottom: 1px solid var(--separator-color); +} + +table.markdownTable tr:last-child, table.doxtable tr:last-child { + border-bottom: none; +} + +.full_width_table table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) { + display: block; +} + +.full_width_table table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody { + display: table; + width: 100%; +} + +table.fieldtable th { + font-size: var(--page-font-size); + font-weight: 600; + background-image: none; + background-color: var(--tablehead-background); + color: var(--tablehead-foreground); +} + +table.fieldtable td.fieldtype, .fieldtable td.fieldname, .fieldtable td.fieldinit, .fieldtable td.fielddoc, .fieldtable th { + border-bottom: 1px solid var(--separator-color); + border-right: 1px solid var(--separator-color); +} + +table.fieldtable tr:last-child td:first-child { + border-bottom-left-radius: var(--border-radius-small); +} + +table.fieldtable tr:last-child td:last-child { + border-bottom-right-radius: var(--border-radius-small); +} + +.memberdecls td.glow, .fieldtable tr.glow { + background-color: var(--primary-light-color); + box-shadow: none; +} + +table.memberdecls { + display: block; + -webkit-tap-highlight-color: transparent; +} + +table.memberdecls tr[class^='memitem'] { + font-family: var(--font-family-monospace); + font-size: var(--code-font-size); +} + +table.memberdecls tr[class^='memitem'] .memTemplParams { + font-family: var(--font-family-monospace); + font-size: var(--code-font-size); + color: var(--primary-dark-color); + white-space: normal; +} + +table.memberdecls .memItemLeft, +table.memberdecls .memItemRight, +table.memberdecls .memTemplItemLeft, +table.memberdecls .memTemplItemRight, +table.memberdecls .memTemplParams { + transition: none; + padding-top: var(--spacing-small); + padding-bottom: var(--spacing-small); + border-top: 1px solid var(--separator-color); + border-bottom: 1px solid var(--separator-color); + background-color: var(--fragment-background); +} + +table.memberdecls .memTemplItemLeft, +table.memberdecls .memTemplItemRight { + padding-top: 2px; +} + +table.memberdecls .memTemplParams { + border-bottom: 0; + border-left: 1px solid var(--separator-color); + border-right: 1px solid var(--separator-color); + border-radius: var(--border-radius-small) var(--border-radius-small) 0 0; + padding-bottom: var(--spacing-small); +} + +table.memberdecls .memTemplItemLeft { + border-radius: 0 0 0 var(--border-radius-small); + border-left: 1px solid var(--separator-color); + border-top: 0; +} + +table.memberdecls .memTemplItemRight { + border-radius: 0 0 var(--border-radius-small) 0; + border-right: 1px solid var(--separator-color); + padding-left: 0; + border-top: 0; +} + +table.memberdecls .memItemLeft { + border-radius: var(--border-radius-small) 0 0 var(--border-radius-small); + border-left: 1px solid var(--separator-color); + padding-left: var(--spacing-medium); + padding-right: 0; +} + +table.memberdecls .memItemRight { + border-radius: 0 var(--border-radius-small) var(--border-radius-small) 0; + border-right: 1px solid var(--separator-color); + padding-right: var(--spacing-medium); + padding-left: 0; + +} + +table.memberdecls .mdescLeft, table.memberdecls .mdescRight { + background: none; + color: var(--page-foreground-color); + padding: var(--spacing-small) 0; +} + +table.memberdecls .memItemLeft, +table.memberdecls .memTemplItemLeft { + padding-right: var(--spacing-medium); +} + +table.memberdecls .memSeparator { + background: var(--page-background-color); + height: var(--spacing-large); + border: 0; + transition: none; +} + +table.memberdecls .groupheader { + margin-bottom: var(--spacing-large); +} + +table.memberdecls .inherit_header td { + padding: 0 0 var(--spacing-medium) 0; + text-indent: -12px; + color: var(--page-secondary-foreground-color); +} + +table.memberdecls img[src="closed.png"], +table.memberdecls img[src="open.png"], +div.dynheader img[src="open.png"], +div.dynheader img[src="closed.png"] { + width: 0; + height: 0; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 5px solid var(--primary-color); + margin-top: 8px; + display: block; + float: left; + margin-left: -10px; + transition: transform var(--animation-duration) ease-out; +} + +table.memberdecls img { + margin-right: 10px; +} + +table.memberdecls img[src="closed.png"], +div.dynheader img[src="closed.png"] { + transform: rotate(-90deg); + +} + +.compoundTemplParams { + font-family: var(--font-family-monospace); + color: var(--primary-dark-color); + font-size: var(--code-font-size); +} + +@media screen and (max-width: 767px) { + + table.memberdecls .memItemLeft, + table.memberdecls .memItemRight, + table.memberdecls .mdescLeft, + table.memberdecls .mdescRight, + table.memberdecls .memTemplItemLeft, + table.memberdecls .memTemplItemRight, + table.memberdecls .memTemplParams { + display: block; + text-align: left; + padding-left: var(--spacing-large); + margin: 0 calc(0px - var(--spacing-large)) 0 calc(0px - var(--spacing-large)); + border-right: none; + border-left: none; + border-radius: 0; + white-space: normal; + } + + table.memberdecls .memItemLeft, + table.memberdecls .mdescLeft, + table.memberdecls .memTemplItemLeft { + border-bottom: 0; + padding-bottom: 0; + } + + table.memberdecls .memTemplItemLeft { + padding-top: 0; + } + + table.memberdecls .mdescLeft { + margin-bottom: calc(0px - var(--page-font-size)); + } + + table.memberdecls .memItemRight, + table.memberdecls .mdescRight, + table.memberdecls .memTemplItemRight { + border-top: 0; + padding-top: 0; + padding-right: var(--spacing-large); + overflow-x: auto; + } + + table.memberdecls tr[class^='memitem']:not(.inherit) { + display: block; + width: calc(100vw - 2 * var(--spacing-large)); + } + + table.memberdecls .mdescRight { + color: var(--page-foreground-color); + } + + table.memberdecls tr.inherit { + visibility: hidden; + } + + table.memberdecls tr[style="display: table-row;"] { + display: block !important; + visibility: visible; + width: calc(100vw - 2 * var(--spacing-large)); + animation: fade .5s; + } + + @keyframes fade { + 0% { + opacity: 0; + max-height: 0; + } + + 100% { + opacity: 1; + max-height: 200px; + } + } +} + + +/* + Horizontal Rule + */ + +hr { + margin-top: var(--spacing-large); + margin-bottom: var(--spacing-large); + height: 1px; + background-color: var(--separator-color); + border: 0; +} + +.contents hr { + box-shadow: 100px 0 var(--separator-color), + -100px 0 var(--separator-color), + 500px 0 var(--separator-color), + -500px 0 var(--separator-color), + 900px 0 var(--separator-color), + -900px 0 var(--separator-color), + 1400px 0 var(--separator-color), + -1400px 0 var(--separator-color), + 1900px 0 var(--separator-color), + -1900px 0 var(--separator-color); +} + +.contents img, .contents .center, .contents center, .contents div.image object { + max-width: 100%; + overflow: auto; +} + +@media screen and (max-width: 767px) { + .contents .dyncontent > .center, .contents > center { + margin-left: calc(0px - var(--spacing-large)); + margin-right: calc(0px - var(--spacing-large)); + max-width: calc(100% + 2 * var(--spacing-large)); + } +} + +/* + Directories + */ +div.directory { + border-top: 1px solid var(--separator-color); + border-bottom: 1px solid var(--separator-color); + width: auto; +} + +table.directory { + font-family: var(--font-family); + font-size: var(--page-font-size); + font-weight: normal; + width: 100%; +} + +table.directory td.entry, table.directory td.desc { + padding: calc(var(--spacing-small) / 2) var(--spacing-small); + line-height: var(--table-line-height); +} + +table.directory tr.even td:last-child { + border-radius: 0 var(--border-radius-small) var(--border-radius-small) 0; +} + +table.directory tr.even td:first-child { + border-radius: var(--border-radius-small) 0 0 var(--border-radius-small); +} + +table.directory tr.even:last-child td:last-child { + border-radius: 0 var(--border-radius-small) 0 0; +} + +table.directory tr.even:last-child td:first-child { + border-radius: var(--border-radius-small) 0 0 0; +} + +table.directory td.desc { + min-width: 250px; +} + +table.directory tr.even { + background-color: var(--odd-color); +} + +table.directory tr.odd { + background-color: transparent; +} + +.icona { + width: auto; + height: auto; + margin: 0 var(--spacing-small); +} + +.icon { + background: var(--primary-color); + border-radius: var(--border-radius-small); + font-size: var(--page-font-size); + padding: calc(var(--page-font-size) / 5); + line-height: var(--page-font-size); + transform: scale(0.8); + height: auto; + width: var(--page-font-size); + user-select: none; +} + +.iconfopen, .icondoc, .iconfclosed { + background-position: center; + margin-bottom: 0; + height: var(--table-line-height); +} + +.icondoc { + filter: saturate(0.2); +} + +@media screen and (max-width: 767px) { + div.directory { + margin-left: calc(0px - var(--spacing-large)); + margin-right: calc(0px - var(--spacing-large)); + } +} + +@media (prefers-color-scheme: dark) { + html:not(.light-mode) .iconfopen, html:not(.light-mode) .iconfclosed { + filter: hue-rotate(180deg) invert(); + } +} + +html.dark-mode .iconfopen, html.dark-mode .iconfclosed { + filter: hue-rotate(180deg) invert(); +} + +/* + Class list + */ + +.classindex dl.odd { + background: var(--odd-color); + border-radius: var(--border-radius-small); +} + +.classindex dl.even { + background-color: transparent; +} + +/* + Class Index Doxygen 1.8 +*/ + +table.classindex { + margin-left: 0; + margin-right: 0; + width: 100%; +} + +table.classindex table div.ah { + background-image: none; + background-color: initial; + border-color: var(--separator-color); + color: var(--page-foreground-color); + box-shadow: var(--box-shadow); + border-radius: var(--border-radius-large); + padding: var(--spacing-small); +} + +div.qindex { + background-color: var(--odd-color); + border-radius: var(--border-radius-small); + border: 1px solid var(--separator-color); + padding: var(--spacing-small) 0; +} + +/* + Footer and nav-path + */ + +#nav-path { + width: 100%; +} + +#nav-path ul { + background-image: none; + background: var(--page-background-color); + border: none; + border-top: 1px solid var(--separator-color); + border-bottom: 1px solid var(--separator-color); + border-bottom: 0; + box-shadow: 0 0.75px 0 var(--separator-color); + font-size: var(--navigation-font-size); +} + +img.footer { + width: 60px; +} + +.navpath li.footer { + color: var(--page-secondary-foreground-color); +} + +address.footer { + color: var(--page-secondary-foreground-color); + margin-bottom: var(--spacing-large); +} + +#nav-path li.navelem { + background-image: none; + display: flex; + align-items: center; +} + +.navpath li.navelem a { + text-shadow: none; + display: inline-block; + color: var(--primary-color) !important; +} + +.navpath li.navelem b { + color: var(--primary-dark-color); + font-weight: 500; +} + +li.navelem { + padding: 0; + margin-left: -8px; +} + +li.navelem:first-child { + margin-left: var(--spacing-large); +} + +li.navelem:first-child:before { + display: none; +} + +#nav-path li.navelem:after { + content: ''; + border: 5px solid var(--page-background-color); + border-bottom-color: transparent; + border-right-color: transparent; + border-top-color: transparent; + transform: translateY(-1px) scaleY(4.2); + z-index: 10; + margin-left: 6px; +} + +#nav-path li.navelem:before { + content: ''; + border: 5px solid var(--separator-color); + border-bottom-color: transparent; + border-right-color: transparent; + border-top-color: transparent; + transform: translateY(-1px) scaleY(3.2); + margin-right: var(--spacing-small); +} + +.navpath li.navelem a:hover { + color: var(--primary-color); +} + +/* + Scrollbars for Webkit +*/ + +#nav-tree::-webkit-scrollbar, +div.fragment::-webkit-scrollbar, +pre.fragment::-webkit-scrollbar, +div.memproto::-webkit-scrollbar, +.contents center::-webkit-scrollbar, +.contents .center::-webkit-scrollbar, +.contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody::-webkit-scrollbar, +div.contents .toc::-webkit-scrollbar, +.contents .dotgraph::-webkit-scrollbar, +.contents .tabs-overview-container::-webkit-scrollbar { + background: transparent; + width: calc(var(--webkit-scrollbar-size) + var(--webkit-scrollbar-padding) + var(--webkit-scrollbar-padding)); + height: calc(var(--webkit-scrollbar-size) + var(--webkit-scrollbar-padding) + var(--webkit-scrollbar-padding)); +} + +#nav-tree::-webkit-scrollbar-thumb, +div.fragment::-webkit-scrollbar-thumb, +pre.fragment::-webkit-scrollbar-thumb, +div.memproto::-webkit-scrollbar-thumb, +.contents center::-webkit-scrollbar-thumb, +.contents .center::-webkit-scrollbar-thumb, +.contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody::-webkit-scrollbar-thumb, +div.contents .toc::-webkit-scrollbar-thumb, +.contents .dotgraph::-webkit-scrollbar-thumb, +.contents .tabs-overview-container::-webkit-scrollbar-thumb { + background-color: transparent; + border: var(--webkit-scrollbar-padding) solid transparent; + border-radius: calc(var(--webkit-scrollbar-padding) + var(--webkit-scrollbar-padding)); + background-clip: padding-box; +} + +#nav-tree:hover::-webkit-scrollbar-thumb, +div.fragment:hover::-webkit-scrollbar-thumb, +pre.fragment:hover::-webkit-scrollbar-thumb, +div.memproto:hover::-webkit-scrollbar-thumb, +.contents center:hover::-webkit-scrollbar-thumb, +.contents .center:hover::-webkit-scrollbar-thumb, +.contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody:hover::-webkit-scrollbar-thumb, +div.contents .toc:hover::-webkit-scrollbar-thumb, +.contents .dotgraph:hover::-webkit-scrollbar-thumb, +.contents .tabs-overview-container:hover::-webkit-scrollbar-thumb { + background-color: var(--webkit-scrollbar-color); +} + +#nav-tree::-webkit-scrollbar-track, +div.fragment::-webkit-scrollbar-track, +pre.fragment::-webkit-scrollbar-track, +div.memproto::-webkit-scrollbar-track, +.contents center::-webkit-scrollbar-track, +.contents .center::-webkit-scrollbar-track, +.contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody::-webkit-scrollbar-track, +div.contents .toc::-webkit-scrollbar-track, +.contents .dotgraph::-webkit-scrollbar-track, +.contents .tabs-overview-container::-webkit-scrollbar-track { + background: transparent; +} + +#nav-tree::-webkit-scrollbar-corner { + background-color: var(--side-nav-background); +} + +#nav-tree, +div.fragment, +pre.fragment, +div.memproto, +.contents center, +.contents .center, +.contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody, +div.contents .toc { + overflow-x: auto; + overflow-x: overlay; +} + +#nav-tree { + overflow-x: auto; + overflow-y: auto; + overflow-y: overlay; +} + +/* + Scrollbars for Firefox +*/ + +#nav-tree, +div.fragment, +pre.fragment, +div.memproto, +.contents center, +.contents .center, +.contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody, +div.contents .toc, +.contents .dotgraph, +.contents .tabs-overview-container { + scrollbar-width: thin; +} + +/* + Optional Dark mode toggle button +*/ + +doxygen-awesome-dark-mode-toggle { + display: inline-block; + margin: 0 0 0 var(--spacing-small); + padding: 0; + width: var(--searchbar-height); + height: var(--searchbar-height); + background: none; + border: none; + border-radius: var(--searchbar-height); + vertical-align: middle; + text-align: center; + line-height: var(--searchbar-height); + font-size: 22px; + display: flex; + align-items: center; + justify-content: center; + user-select: none; + cursor: pointer; +} + +doxygen-awesome-dark-mode-toggle > svg { + transition: transform var(--animation-duration) ease-in-out; +} + +doxygen-awesome-dark-mode-toggle:active > svg { + transform: scale(.5); +} + +doxygen-awesome-dark-mode-toggle:hover { + background-color: rgba(0,0,0,.03); +} + +html.dark-mode doxygen-awesome-dark-mode-toggle:hover { + background-color: rgba(0,0,0,.18); +} + +/* + Optional fragment copy button +*/ +.doxygen-awesome-fragment-wrapper { + position: relative; +} + +doxygen-awesome-fragment-copy-button { + opacity: 0; + background: var(--fragment-background); + width: 28px; + height: 28px; + position: absolute; + right: calc(var(--spacing-large) - (var(--spacing-large) / 2.5)); + top: calc(var(--spacing-large) - (var(--spacing-large) / 2.5)); + border: 1px solid var(--fragment-foreground); + cursor: pointer; + border-radius: var(--border-radius-small); + display: flex; + justify-content: center; + align-items: center; +} + +.doxygen-awesome-fragment-wrapper:hover doxygen-awesome-fragment-copy-button, doxygen-awesome-fragment-copy-button.success { + opacity: .28; +} + +doxygen-awesome-fragment-copy-button:hover, doxygen-awesome-fragment-copy-button.success { + opacity: 1 !important; +} + +doxygen-awesome-fragment-copy-button:active:not([class~=success]) svg { + transform: scale(.91); +} + +doxygen-awesome-fragment-copy-button svg { + fill: var(--fragment-foreground); + width: 18px; + height: 18px; +} + +doxygen-awesome-fragment-copy-button.success svg { + fill: rgb(14, 168, 14); +} + +doxygen-awesome-fragment-copy-button.success { + border-color: rgb(14, 168, 14); +} + +@media screen and (max-width: 767px) { + .textblock > .doxygen-awesome-fragment-wrapper > doxygen-awesome-fragment-copy-button, + .textblock li > .doxygen-awesome-fragment-wrapper > doxygen-awesome-fragment-copy-button, + .memdoc li > .doxygen-awesome-fragment-wrapper > doxygen-awesome-fragment-copy-button, + .memdoc > .doxygen-awesome-fragment-wrapper > doxygen-awesome-fragment-copy-button, + dl dd > .doxygen-awesome-fragment-wrapper > doxygen-awesome-fragment-copy-button { + right: 0; + } +} + +/* + Optional paragraph link button +*/ + +a.anchorlink { + font-size: 90%; + margin-left: var(--spacing-small); + color: var(--page-foreground-color) !important; + text-decoration: none; + opacity: .15; + display: none; + transition: opacity var(--animation-duration) ease-in-out, color var(--animation-duration) ease-in-out; +} + +a.anchorlink svg { + fill: var(--page-foreground-color); +} + +h3 a.anchorlink svg, h4 a.anchorlink svg { + margin-bottom: -3px; + margin-top: -4px; +} + +a.anchorlink:hover { + opacity: .45; +} + +h2:hover a.anchorlink, h1:hover a.anchorlink, h3:hover a.anchorlink, h4:hover a.anchorlink { + display: inline-block; +} + +/* + Optional tab feature +*/ + +.tabbed > ul { + padding-inline-start: 0px; + margin: 0; + padding: var(--spacing-small) 0; +} + +.tabbed > ul > li { + display: none; +} + +.tabbed > ul > li.selected { + display: block; +} + +.tabs-overview-container { + overflow-x: auto; + display: block; + overflow-y: visible; +} + +.tabs-overview { + border-bottom: 1px solid var(--separator-color); + display: flex; + flex-direction: row; +} + +@media screen and (max-width: 767px) { + .tabs-overview-container { + margin: 0 calc(0px - var(--spacing-large)); + } + .tabs-overview { + padding: 0 var(--spacing-large) + } +} + +.tabs-overview button.tab-button { + color: var(--page-foreground-color); + margin: 0; + border: none; + background: transparent; + padding: calc(var(--spacing-large) / 2) 0; + display: inline-block; + font-size: var(--page-font-size); + cursor: pointer; + box-shadow: 0 1px 0 0 var(--separator-color); + position: relative; + + -webkit-tap-highlight-color: transparent; +} + +.tabs-overview button.tab-button .tab-title::before { + display: block; + content: attr(title); + font-weight: 600; + height: 0; + overflow: hidden; + visibility: hidden; +} + +.tabs-overview button.tab-button .tab-title { + float: left; + white-space: nowrap; + font-weight: normal; + padding: calc(var(--spacing-large) / 2) var(--spacing-large); + border-radius: var(--border-radius-medium); + transition: background-color var(--animation-duration) ease-in-out, font-weight var(--animation-duration) ease-in-out; +} + +.tabs-overview button.tab-button:not(:last-child) .tab-title { + box-shadow: 8px 0 0 -7px var(--separator-color); +} + +.tabs-overview button.tab-button:hover .tab-title { + background: var(--separator-color); + box-shadow: none; +} + +.tabs-overview button.tab-button.active .tab-title { + font-weight: 600; +} + +.tabs-overview button.tab-button::after { + content: ''; + display: block; + position: absolute; + left: 0; + bottom: 0; + right: 0; + height: 0; + width: 0%; + margin: 0 auto; + border-radius: var(--border-radius-small) var(--border-radius-small) 0 0; + background-color: var(--primary-color); + transition: width var(--animation-duration) ease-in-out, height var(--animation-duration) ease-in-out; +} + +.tabs-overview button.tab-button.active::after { + width: 100%; + box-sizing: border-box; + height: 3px; +} + + +/* + Navigation Buttons +*/ + +.section_buttons:not(:empty) { + margin-top: calc(var(--spacing-large) * 3); +} + +.section_buttons table.markdownTable { + display: block; + width: 100%; +} + +.section_buttons table.markdownTable tbody { + display: table !important; + width: 100%; + box-shadow: none; + border-spacing: 10px; +} + +.section_buttons table.markdownTable td { + padding: 0; +} + +.section_buttons table.markdownTable th { + display: none; +} + +.section_buttons table.markdownTable tr.markdownTableHead { + border: none; +} + +.section_buttons tr th, .section_buttons tr td { + background: none; + border: none; + padding: var(--spacing-large) 0 var(--spacing-small); +} + +.section_buttons a { + display: inline-block; + border: 1px solid var(--separator-color); + border-radius: var(--border-radius-medium); + color: var(--page-secondary-foreground-color) !important; + text-decoration: none; + transition: color var(--animation-duration) ease-in-out, background-color var(--animation-duration) ease-in-out; +} + +.section_buttons a:hover { + color: var(--page-foreground-color) !important; + background-color: var(--odd-color); +} + +.section_buttons tr td.markdownTableBodyLeft a { + padding: var(--spacing-medium) var(--spacing-large) var(--spacing-medium) calc(var(--spacing-large) / 2); +} + +.section_buttons tr td.markdownTableBodyRight a { + padding: var(--spacing-medium) calc(var(--spacing-large) / 2) var(--spacing-medium) var(--spacing-large); +} + +.section_buttons tr td.markdownTableBodyLeft a::before, +.section_buttons tr td.markdownTableBodyRight a::after { + color: var(--page-secondary-foreground-color) !important; + display: inline-block; + transition: color .08s ease-in-out, transform .09s ease-in-out; +} + +.section_buttons tr td.markdownTableBodyLeft a::before { + content: '〈'; + padding-right: var(--spacing-large); +} + + +.section_buttons tr td.markdownTableBodyRight a::after { + content: '〉'; + padding-left: var(--spacing-large); +} + + +.section_buttons tr td.markdownTableBodyLeft a:hover::before { + color: var(--page-foreground-color) !important; + transform: translateX(-3px); +} + +.section_buttons tr td.markdownTableBodyRight a:hover::after { + color: var(--page-foreground-color) !important; + transform: translateX(3px); +} + +@media screen and (max-width: 450px) { + .section_buttons a { + width: 100%; + box-sizing: border-box; + } + + .section_buttons tr td:nth-of-type(1).markdownTableBodyLeft a { + border-radius: var(--border-radius-medium) 0 0 var(--border-radius-medium); + border-right: none; + } + + .section_buttons tr td:nth-of-type(2).markdownTableBodyRight a { + border-radius: 0 var(--border-radius-medium) var(--border-radius-medium) 0; + } +} diff --git a/HEAD/doxygen.css b/HEAD/doxygen.css new file mode 100644 index 00000000..35513192 --- /dev/null +++ b/HEAD/doxygen.css @@ -0,0 +1,1850 @@ +/* The standard CSS for doxygen 1.13.0*/ + +body { + background-color: white; + color: black; +} + +body, table, div, p, dl { + font-weight: 400; + font-size: 14px; + font-family: Roboto,sans-serif; + line-height: 22px; +} + +/* @group Heading Levels */ + +.title { + font-family: Roboto,sans-serif; + line-height: 28px; + font-size: 150%; + font-weight: bold; + margin: 10px 2px; +} + +h1.groupheader { + font-size: 150%; +} + +h2.groupheader { + border-bottom: 1px solid #879ECB; + color: #354C7B; + font-size: 150%; + font-weight: normal; + margin-top: 1.75em; + padding-top: 8px; + padding-bottom: 4px; + width: 100%; +} + +h3.groupheader { + font-size: 100%; +} + +h1, h2, h3, h4, h5, h6 { + -webkit-transition: text-shadow 0.5s linear; + -moz-transition: text-shadow 0.5s linear; + -ms-transition: text-shadow 0.5s linear; + -o-transition: text-shadow 0.5s linear; + transition: text-shadow 0.5s linear; + margin-right: 15px; +} + +h1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow { + text-shadow: 0 0 15px cyan; +} + +dt { + font-weight: bold; +} + +p.startli, p.startdd { + margin-top: 2px; +} + +th p.starttd, th p.intertd, th p.endtd { + font-size: 100%; + font-weight: 700; +} + +p.starttd { + margin-top: 0px; +} + +p.endli { + margin-bottom: 0px; +} + +p.enddd { + margin-bottom: 4px; +} + +p.endtd { + margin-bottom: 2px; +} + +p.interli { +} + +p.interdd { +} + +p.intertd { +} + +/* @end */ + +caption { + font-weight: bold; +} + +span.legend { + font-size: 70%; + text-align: center; +} + +h3.version { + font-size: 90%; + text-align: center; +} + +div.navtab { + padding-right: 15px; + text-align: right; + line-height: 110%; +} + +div.navtab table { + border-spacing: 0; +} + +td.navtab { + padding-right: 6px; + padding-left: 6px; +} + +td.navtabHL { + background-image: url('tab_a.png'); + background-repeat:repeat-x; + padding-right: 6px; + padding-left: 6px; +} + +td.navtabHL a, td.navtabHL a:visited { + color: white; + text-shadow: 0px 1px 1px rgba(0, 0, 0, 1.0); +} + +a.navtab { + font-weight: bold; +} + +div.qindex{ + text-align: center; + width: 100%; + line-height: 140%; + font-size: 130%; + color: #A0A0A0; +} + +#main-menu a:focus { + outline: auto; + z-index: 10; + position: relative; +} + +dt.alphachar{ + font-size: 180%; + font-weight: bold; +} + +.alphachar a{ + color: black; +} + +.alphachar a:hover, .alphachar a:visited{ + text-decoration: none; +} + +.classindex dl { + padding: 25px; + column-count:1 +} + +.classindex dd { + display:inline-block; + margin-left: 50px; + width: 90%; + line-height: 1.15em; +} + +.classindex dl.even { + background-color: white; +} + +.classindex dl.odd { + background-color: #F8F9FC; +} + +@media(min-width: 1120px) { + .classindex dl { + column-count:2 + } +} + +@media(min-width: 1320px) { + .classindex dl { + column-count:3 + } +} + + +/* @group Link Styling */ + +a { + color: #3D578C; + font-weight: normal; + text-decoration: none; +} + +.contents a:visited { + color: #4665A2; +} + +a:hover { + text-decoration: none; + background: linear-gradient(to bottom, transparent 0,transparent calc(100% - 1px), currentColor 100%); +} + +a:hover > span.arrow { + text-decoration: none; + background : #F9FAFC; +} + +a.el { + font-weight: bold; +} + +a.elRef { +} + +a.code, a.code:visited, a.line, a.line:visited { + color: #4665A2; +} + +a.codeRef, a.codeRef:visited, a.lineRef, a.lineRef:visited { + color: #4665A2; +} + +a.code.hl_class { /* style for links to class names in code snippets */ } +a.code.hl_struct { /* style for links to struct names in code snippets */ } +a.code.hl_union { /* style for links to union names in code snippets */ } +a.code.hl_interface { /* style for links to interface names in code snippets */ } +a.code.hl_protocol { /* style for links to protocol names in code snippets */ } +a.code.hl_category { /* style for links to category names in code snippets */ } +a.code.hl_exception { /* style for links to exception names in code snippets */ } +a.code.hl_service { /* style for links to service names in code snippets */ } +a.code.hl_singleton { /* style for links to singleton names in code snippets */ } +a.code.hl_concept { /* style for links to concept names in code snippets */ } +a.code.hl_namespace { /* style for links to namespace names in code snippets */ } +a.code.hl_package { /* style for links to package names in code snippets */ } +a.code.hl_define { /* style for links to macro names in code snippets */ } +a.code.hl_function { /* style for links to function names in code snippets */ } +a.code.hl_variable { /* style for links to variable names in code snippets */ } +a.code.hl_typedef { /* style for links to typedef names in code snippets */ } +a.code.hl_enumvalue { /* style for links to enum value names in code snippets */ } +a.code.hl_enumeration { /* style for links to enumeration names in code snippets */ } +a.code.hl_signal { /* style for links to Qt signal names in code snippets */ } +a.code.hl_slot { /* style for links to Qt slot names in code snippets */ } +a.code.hl_friend { /* style for links to friend names in code snippets */ } +a.code.hl_dcop { /* style for links to KDE3 DCOP names in code snippets */ } +a.code.hl_property { /* style for links to property names in code snippets */ } +a.code.hl_event { /* style for links to event names in code snippets */ } +a.code.hl_sequence { /* style for links to sequence names in code snippets */ } +a.code.hl_dictionary { /* style for links to dictionary names in code snippets */ } + +/* @end */ + +dl.el { + margin-left: -1cm; +} + +ul.check { + list-style:none; + text-indent: -16px; + padding-left: 38px; +} +li.unchecked:before { + content: "\2610\A0"; +} +li.checked:before { + content: "\2611\A0"; +} + +ol { + text-indent: 0px; +} + +ul { + text-indent: 0px; + overflow: visible; +} + +ul.multicol { + -moz-column-gap: 1em; + -webkit-column-gap: 1em; + column-gap: 1em; + -moz-column-count: 3; + -webkit-column-count: 3; + column-count: 3; + list-style-type: none; +} + +#side-nav ul { + overflow: visible; /* reset ul rule for scroll bar in GENERATE_TREEVIEW window */ +} + +#main-nav ul { + overflow: visible; /* reset ul rule for the navigation bar drop down lists */ +} + +.fragment { + text-align: left; + direction: ltr; + overflow-x: auto; + overflow-y: hidden; + position: relative; + min-height: 12px; + margin: 10px 0px; + padding: 10px 10px; + border: 1px solid #C4CFE5; + border-radius: 4px; + background-color: #FBFCFD; + color: black; +} + +pre.fragment { + word-wrap: break-word; + font-size: 10pt; + line-height: 125%; + font-family: 'JetBrains Mono',Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace,fixed; +} + +.clipboard { + width: 24px; + height: 24px; + right: 5px; + top: 5px; + opacity: 0; + position: absolute; + display: inline; + overflow: auto; + fill: black; + justify-content: center; + align-items: center; + cursor: pointer; +} + +.clipboard.success { + border: 1px solid black; + border-radius: 4px; +} + +.fragment:hover .clipboard, .clipboard.success { + opacity: .28; +} + +.clipboard:hover, .clipboard.success { + opacity: 1 !important; +} + +.clipboard:active:not([class~=success]) svg { + transform: scale(.91); +} + +.clipboard.success svg { + fill: #2EC82E; +} + +.clipboard.success { + border-color: #2EC82E; +} + +div.line { + font-family: 'JetBrains Mono',Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace,fixed; + font-size: 13px; + min-height: 13px; + line-height: 1.2; + text-wrap: unrestricted; + white-space: -moz-pre-wrap; /* Moz */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + white-space: pre-wrap; /* CSS3 */ + word-wrap: break-word; /* IE 5.5+ */ + text-indent: -53px; + padding-left: 53px; + padding-bottom: 0px; + margin: 0px; + -webkit-transition-property: background-color, box-shadow; + -webkit-transition-duration: 0.5s; + -moz-transition-property: background-color, box-shadow; + -moz-transition-duration: 0.5s; + -ms-transition-property: background-color, box-shadow; + -ms-transition-duration: 0.5s; + -o-transition-property: background-color, box-shadow; + -o-transition-duration: 0.5s; + transition-property: background-color, box-shadow; + transition-duration: 0.5s; +} + +div.line:after { + content:"\000A"; + white-space: pre; +} + +div.line.glow { + background-color: cyan; + box-shadow: 0 0 10px cyan; +} + +span.fold { + margin-left: 5px; + margin-right: 1px; + margin-top: 0px; + margin-bottom: 0px; + padding: 0px; + display: inline-block; + width: 12px; + height: 12px; + background-repeat:no-repeat; + background-position:center; +} + +span.lineno { + padding-right: 4px; + margin-right: 9px; + text-align: right; + border-right: 2px solid #00FF00; + color: black; + background-color: #E8E8E8; + white-space: pre; +} +span.lineno a, span.lineno a:visited { + color: #4665A2; + background-color: #D8D8D8; +} + +span.lineno a:hover { + color: #4665A2; + background-color: #C8C8C8; +} + +.lineno { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +div.classindex ul { + list-style: none; + padding-left: 0; +} + +div.classindex span.ai { + display: inline-block; +} + +div.groupHeader { + margin-left: 16px; + margin-top: 12px; + font-weight: bold; +} + +div.groupText { + margin-left: 16px; + font-style: italic; +} + +body { + color: black; + margin: 0; +} + +div.contents { + margin-top: 10px; + margin-left: 12px; + margin-right: 8px; +} + +p.formulaDsp { + text-align: center; +} + +img.dark-mode-visible { + display: none; +} +img.light-mode-visible { + display: none; +} + +img.formulaInl, img.inline { + vertical-align: middle; +} + +div.center { + text-align: center; + margin-top: 0px; + margin-bottom: 0px; + padding: 0px; +} + +div.center img { + border: 0px; +} + +address.footer { + text-align: right; + padding-right: 12px; +} + +img.footer { + border: 0px; + vertical-align: middle; + width: 104px; +} + +.compoundTemplParams { + color: #4665A2; + font-size: 80%; + line-height: 120%; +} + +/* @group Code Colorization */ + +span.keyword { + color: #008000; +} + +span.keywordtype { + color: #604020; +} + +span.keywordflow { + color: #E08000; +} + +span.comment { + color: #800000; +} + +span.preprocessor { + color: #806020; +} + +span.stringliteral { + color: #002080; +} + +span.charliteral { + color: #008080; +} + +span.xmlcdata { + color: black; +} + +span.vhdldigit { + color: #FF00FF; +} + +span.vhdlchar { + color: #000000; +} + +span.vhdlkeyword { + color: #700070; +} + +span.vhdllogic { + color: #FF0000; +} + +blockquote { + background-color: #F7F8FB; + border-left: 2px solid #9CAFD4; + margin: 0 24px 0 4px; + padding: 0 12px 0 16px; +} + +/* @end */ + +td.tiny { + font-size: 75%; +} + +.dirtab { + padding: 4px; + border-collapse: collapse; + border: 1px solid #2D4068; +} + +th.dirtab { + background-color: #374F7F; + color: #FFFFFF; + font-weight: bold; +} + +hr { + height: 0px; + border: none; + border-top: 1px solid #4A6AAA; +} + +hr.footer { + height: 1px; +} + +/* @group Member Descriptions */ + +table.memberdecls { + border-spacing: 0px; + padding: 0px; +} + +.memberdecls td, .fieldtable tr { + -webkit-transition-property: background-color, box-shadow; + -webkit-transition-duration: 0.5s; + -moz-transition-property: background-color, box-shadow; + -moz-transition-duration: 0.5s; + -ms-transition-property: background-color, box-shadow; + -ms-transition-duration: 0.5s; + -o-transition-property: background-color, box-shadow; + -o-transition-duration: 0.5s; + transition-property: background-color, box-shadow; + transition-duration: 0.5s; +} + +.memberdecls td.glow, .fieldtable tr.glow { + background-color: cyan; + box-shadow: 0 0 15px cyan; +} + +.mdescLeft, .mdescRight, +.memItemLeft, .memItemRight, +.memTemplItemLeft, .memTemplItemRight, .memTemplParams { + background-color: #F9FAFC; + border: none; + margin: 4px; + padding: 1px 0 0 8px; +} + +.mdescLeft, .mdescRight { + padding: 0px 8px 4px 8px; + color: #555; +} + +.memSeparator { + border-bottom: 1px solid #DEE4F0; + line-height: 1px; + margin: 0px; + padding: 0px; +} + +.memItemLeft, .memTemplItemLeft { + white-space: nowrap; +} + +.memItemRight, .memTemplItemRight { + width: 100%; +} + +.memTemplParams { + color: #4665A2; + white-space: nowrap; + font-size: 80%; +} + +/* @end */ + +/* @group Member Details */ + +/* Styles for detailed member documentation */ + +.memtitle { + padding: 8px; + border-top: 1px solid #A8B8D9; + border-left: 1px solid #A8B8D9; + border-right: 1px solid #A8B8D9; + border-top-right-radius: 4px; + border-top-left-radius: 4px; + margin-bottom: -1px; + background-image: url('nav_f.png'); + background-repeat: repeat-x; + background-color: #E2E8F2; + line-height: 1.25; + font-weight: 300; + float:left; +} + +.permalink +{ + font-size: 65%; + display: inline-block; + vertical-align: middle; +} + +.memtemplate { + font-size: 80%; + color: #4665A2; + font-weight: normal; + margin-left: 9px; +} + +.mempage { + width: 100%; +} + +.memitem { + padding: 0; + margin-bottom: 10px; + margin-right: 5px; + -webkit-transition: box-shadow 0.5s linear; + -moz-transition: box-shadow 0.5s linear; + -ms-transition: box-shadow 0.5s linear; + -o-transition: box-shadow 0.5s linear; + transition: box-shadow 0.5s linear; + display: table !important; + width: 100%; +} + +.memitem.glow { + box-shadow: 0 0 15px cyan; +} + +.memname { + font-weight: 400; + margin-left: 6px; +} + +.memname td { + vertical-align: bottom; +} + +.memproto, dl.reflist dt { + border-top: 1px solid #A8B8D9; + border-left: 1px solid #A8B8D9; + border-right: 1px solid #A8B8D9; + padding: 6px 0px 6px 0px; + color: #253555; + font-weight: bold; + text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); + background-color: #DFE5F1; + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + border-top-right-radius: 4px; +} + +.overload { + font-family: 'JetBrains Mono',Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace,fixed; + font-size: 65%; +} + +.memdoc, dl.reflist dd { + border-bottom: 1px solid #A8B8D9; + border-left: 1px solid #A8B8D9; + border-right: 1px solid #A8B8D9; + padding: 6px 10px 2px 10px; + border-top-width: 0; + background-image:url('nav_g.png'); + background-repeat:repeat-x; + background-color: white; + /* opera specific markup */ + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + /* firefox specific markup */ + -moz-border-radius-bottomleft: 4px; + -moz-border-radius-bottomright: 4px; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + /* webkit specific markup */ + -webkit-border-bottom-left-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); +} + +dl.reflist dt { + padding: 5px; +} + +dl.reflist dd { + margin: 0px 0px 10px 0px; + padding: 5px; +} + +.paramkey { + text-align: right; +} + +.paramtype { + white-space: nowrap; + padding: 0px; + padding-bottom: 1px; +} + +.paramname { + white-space: nowrap; + padding: 0px; + padding-bottom: 1px; + margin-left: 2px; +} + +.paramname em { + color: #602020; + font-style: normal; + margin-right: 1px; +} + +.paramname .paramdefval { + font-family: 'JetBrains Mono',Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace,fixed; +} + +.params, .retval, .exception, .tparams { + margin-left: 0px; + padding-left: 0px; +} + +.params .paramname, .retval .paramname, .tparams .paramname, .exception .paramname { + font-weight: bold; + vertical-align: top; +} + +.params .paramtype, .tparams .paramtype { + font-style: italic; + vertical-align: top; +} + +.params .paramdir, .tparams .paramdir { + font-family: 'JetBrains Mono',Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace,fixed; + vertical-align: top; +} + +table.mlabels { + border-spacing: 0px; +} + +td.mlabels-left { + width: 100%; + padding: 0px; +} + +td.mlabels-right { + vertical-align: bottom; + padding: 0px; + white-space: nowrap; +} + +span.mlabels { + margin-left: 8px; +} + +span.mlabel { + background-color: #728DC1; + border-top:1px solid #5373B4; + border-left:1px solid #5373B4; + border-right:1px solid #C4CFE5; + border-bottom:1px solid #C4CFE5; + text-shadow: none; + color: white; + margin-right: 4px; + padding: 2px 3px; + border-radius: 3px; + font-size: 7pt; + white-space: nowrap; + vertical-align: middle; +} + + + +/* @end */ + +/* these are for tree view inside a (index) page */ + +div.directory { + margin: 10px 0px; + border-top: 1px solid #9CAFD4; + border-bottom: 1px solid #9CAFD4; + width: 100%; +} + +.directory table { + border-collapse:collapse; +} + +.directory td { + margin: 0px; + padding: 0px; + vertical-align: top; +} + +.directory td.entry { + white-space: nowrap; + padding-right: 6px; + padding-top: 3px; +} + +.directory td.entry a { + outline:none; +} + +.directory td.entry a img { + border: none; +} + +.directory td.desc { + width: 100%; + padding-left: 6px; + padding-right: 6px; + padding-top: 3px; + border-left: 1px solid rgba(0,0,0,0.05); +} + +.directory tr.odd { + padding-left: 6px; + background-color: #F8F9FC; +} + +.directory tr.even { + padding-left: 6px; + background-color: white; +} + +.directory img { + vertical-align: -30%; +} + +.directory .levels { + white-space: nowrap; + width: 100%; + text-align: right; + font-size: 9pt; +} + +.directory .levels span { + cursor: pointer; + padding-left: 2px; + padding-right: 2px; + color: #3D578C; +} + +.arrow { + color: #9CAFD4; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + cursor: pointer; + font-size: 80%; + display: inline-block; + width: 16px; + height: 22px; +} + +.icon { + font-family: Arial,Helvetica; + line-height: normal; + font-weight: bold; + font-size: 12px; + height: 14px; + width: 16px; + display: inline-block; + background-color: #728DC1; + color: white; + text-align: center; + border-radius: 4px; + margin-left: 2px; + margin-right: 2px; +} + +.icona { + width: 24px; + height: 22px; + display: inline-block; +} + +.iconfopen { + width: 24px; + height: 18px; + margin-bottom: 4px; + background-image:url('folderopen.svg'); + background-repeat: repeat-y; + vertical-align:top; + display: inline-block; +} + +.iconfclosed { + width: 24px; + height: 18px; + margin-bottom: 4px; + background-image:url('folderclosed.svg'); + background-repeat: repeat-y; + vertical-align:top; + display: inline-block; +} + +.icondoc { + width: 24px; + height: 18px; + margin-bottom: 4px; + background-image:url('doc.svg'); + background-position: 0px -4px; + background-repeat: repeat-y; + vertical-align:top; + display: inline-block; +} + +/* @end */ + +div.dynheader { + margin-top: 8px; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +address { + font-style: normal; + color: #2A3D61; +} + +table.doxtable caption { + caption-side: top; +} + +table.doxtable { + border-collapse:collapse; + margin-top: 4px; + margin-bottom: 4px; +} + +table.doxtable td, table.doxtable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; +} + +table.doxtable th { + background-color: #374F7F; + color: #FFFFFF; + font-size: 110%; + padding-bottom: 4px; + padding-top: 5px; +} + +table.fieldtable { + margin-bottom: 10px; + border: 1px solid #A8B8D9; + border-spacing: 0px; + border-radius: 4px; + box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); +} + +.fieldtable td, .fieldtable th { + padding: 3px 7px 2px; +} + +.fieldtable td.fieldtype, .fieldtable td.fieldname, .fieldtable td.fieldinit { + white-space: nowrap; + border-right: 1px solid #A8B8D9; + border-bottom: 1px solid #A8B8D9; + vertical-align: top; +} + +.fieldtable td.fieldname { + padding-top: 3px; +} + +.fieldtable td.fieldinit { + padding-top: 3px; + text-align: right; +} + + +.fieldtable td.fielddoc { + border-bottom: 1px solid #A8B8D9; +} + +.fieldtable td.fielddoc p:first-child { + margin-top: 0px; +} + +.fieldtable td.fielddoc p:last-child { + margin-bottom: 2px; +} + +.fieldtable tr:last-child td { + border-bottom: none; +} + +.fieldtable th { + background-image: url('nav_f.png'); + background-repeat:repeat-x; + background-color: #E2E8F2; + font-size: 90%; + color: #253555; + padding-bottom: 4px; + padding-top: 5px; + text-align:left; + font-weight: 400; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom: 1px solid #A8B8D9; +} + + +.tabsearch { + top: 0px; + left: 10px; + height: 36px; + background-image: url('tab_b.png'); + z-index: 101; + overflow: hidden; + font-size: 13px; +} + +.navpath ul +{ + font-size: 11px; + background-image: url('tab_b.png'); + background-repeat:repeat-x; + background-position: 0 -5px; + height:30px; + line-height:30px; + color:#283A5D; + border:solid 1px #C2CDE4; + overflow:hidden; + margin:0px; + padding:0px; +} + +.navpath li +{ + list-style-type:none; + float:left; + padding-left:10px; + padding-right:15px; + background-image:url('bc_s.png'); + background-repeat:no-repeat; + background-position:right; + color: #364D7C; +} + +.navpath li.navelem a +{ + height:32px; + display:block; + outline: none; + color: #283A5D; + font-family: 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; + text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); + text-decoration: none; +} + +.navpath li.navelem a:hover +{ + color: white; + text-shadow: 0px 1px 1px rgba(0, 0, 0, 1.0); +} + +.navpath li.footer +{ + list-style-type:none; + float:right; + padding-left:10px; + padding-right:15px; + background-image:none; + background-repeat:no-repeat; + background-position:right; + color: #2A3D61; + font-size: 8pt; +} + + +div.summary +{ + float: right; + font-size: 8pt; + padding-right: 5px; + width: 50%; + text-align: right; +} + +div.summary a +{ + white-space: nowrap; +} + +table.classindex +{ + margin: 10px; + white-space: nowrap; + margin-left: 3%; + margin-right: 3%; + width: 94%; + border: 0; + border-spacing: 0; + padding: 0; +} + +div.ingroups +{ + font-size: 8pt; + width: 50%; + text-align: left; +} + +div.ingroups a +{ + white-space: nowrap; +} + +div.header +{ + background-image: url('nav_h.png'); + background-repeat:repeat-x; + background-color: #F9FAFC; + margin: 0px; + border-bottom: 1px solid #C4CFE5; +} + +div.headertitle +{ + padding: 5px 5px 5px 10px; +} + +.PageDocRTL-title div.headertitle { + text-align: right; + direction: rtl; +} + +dl { + padding: 0 0 0 0; +} + +/* + +dl.section { + margin-left: 0px; + padding-left: 0px; +} + +dl.note { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #D0C000; +} + +dl.warning, dl.attention, dl.important { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #FF0000; +} + +dl.pre, dl.post, dl.invariant { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #00D000; +} + +dl.deprecated { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #505050; +} + +dl.todo { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #00C0E0; +} + +dl.test { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #3030E0; +} + +dl.bug { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #C08050; +} + +*/ + +dl.bug dt a, dl.deprecated dt a, dl.todo dt a, dl.test a { + font-weight: bold !important; +} + +dl.warning, dl.attention, dl.important, dl.note, dl.deprecated, dl.bug, +dl.invariant, dl.pre, dl.post, dl.todo, dl.test, dl.remark { + padding: 10px; + margin: 10px 0px; + overflow: hidden; + margin-left: 0; + border-radius: 4px; +} + +dl.section dd { + margin-bottom: 2px; +} + +dl.warning, dl.attention, dl.important { + background: #f8d1cc; + border-left: 8px solid #b61825; + color: #75070f; +} + +dl.warning dt, dl.attention dt, dl.important dt { + color: #b61825; +} + +dl.note, dl.remark { + background: #faf3d8; + border-left: 8px solid #f3a600; + color: #5f4204; +} + +dl.note dt, dl.remark dt { + color: #f3a600; +} + +dl.todo { + background: #e4f3ff; + border-left: 8px solid #1879C4; + color: #274a5c; +} + +dl.todo dt { + color: #1879C4; +} + +dl.test { + background: #e8e8ff; + border-left: 8px solid #3939C4; + color: #1a1a5c; +} + +dl.test dt { + color: #3939C4; +} + +dl.bug dt a { + color: #5b2bdd !important; +} + +dl.bug { + background: #e4dafd; + border-left: 8px solid #5b2bdd; + color: #2a0d72; +} + +dl.bug dt a { + color: #5b2bdd !important; +} + +dl.deprecated { + background: #ecf0f3; + border-left: 8px solid #5b6269; + color: #43454a; +} + +dl.deprecated dt a { + color: #5b6269 !important; +} + +dl.note dd, dl.warning dd, dl.pre dd, dl.post dd, +dl.remark dd, dl.attention dd, dl.important dd, dl.invariant dd, +dl.bug dd, dl.deprecated dd, dl.todo dd, dl.test dd { + margin-inline-start: 0px; +} + +dl.invariant, dl.pre, dl.post { + background: #d8f1e3; + border-left: 8px solid #44b86f; + color: #265532; +} + +dl.invariant dt, dl.pre dt, dl.post dt { + color: #44b86f; +} + + +#projectrow +{ + height: 56px; +} + +#projectlogo +{ + text-align: center; + vertical-align: bottom; + border-collapse: separate; +} + +#projectlogo img +{ + border: 0px none; +} + +#projectalign +{ + vertical-align: middle; + padding-left: 0.5em; +} + +#projectname +{ + font-size: 200%; + font-family: Tahoma,Arial,sans-serif; + margin: 0px; + padding: 2px 0px; +} + +#side-nav #projectname +{ + font-size: 130%; +} + +#projectbrief +{ + font-size: 90%; + font-family: Tahoma,Arial,sans-serif; + margin: 0px; + padding: 0px; +} + +#projectnumber +{ + font-size: 50%; + font-family: 50% Tahoma,Arial,sans-serif; + margin: 0px; + padding: 0px; +} + +#titlearea +{ + padding: 0px; + margin: 0px; + width: 100%; + border-bottom: 1px solid #5373B4; + background-color: white; +} + +.image +{ + text-align: center; +} + +.dotgraph +{ + text-align: center; +} + +.mscgraph +{ + text-align: center; +} + +.plantumlgraph +{ + text-align: center; +} + +.diagraph +{ + text-align: center; +} + +.caption +{ + font-weight: bold; +} + +dl.citelist { + margin-bottom:50px; +} + +dl.citelist dt { + color:#334975; + float:left; + font-weight:bold; + margin-right:10px; + padding:5px; + text-align:right; + width:52px; +} + +dl.citelist dd { + margin:2px 0 2px 72px; + padding:5px 0; +} + +div.toc { + padding: 14px 25px; + background-color: #F4F6FA; + border: 1px solid #D8DFEE; + border-radius: 7px 7px 7px 7px; + float: right; + height: auto; + margin: 0 8px 10px 10px; + width: 200px; +} + +div.toc li { + background: url("data:image/svg+xml;utf8,&%238595;") no-repeat scroll 0 5px transparent; + font: 10px/1.2 Verdana,'DejaVu Sans',Geneva,sans-serif; + margin-top: 5px; + padding-left: 10px; + padding-top: 2px; +} + +div.toc h3 { + font: bold 12px/1.2 Verdana,'DejaVu Sans',Geneva,sans-serif; + color: #4665A2; + border-bottom: 0 none; + margin: 0; +} + +div.toc ul { + list-style: none outside none; + border: medium none; + padding: 0px; +} + +div.toc li[class^='level'] { + margin-left: 15px; +} + +div.toc li.level1 { + margin-left: 0px; +} + +div.toc li.empty { + background-image: none; + margin-top: 0px; +} + +span.emoji { + /* font family used at the site: https://unicode.org/emoji/charts/full-emoji-list.html + * font-family: "Noto Color Emoji", "Apple Color Emoji", "Segoe UI Emoji", Times, Symbola, Aegyptus, Code2000, Code2001, Code2002, Musica, serif, LastResort; + */ +} + +span.obfuscator { + display: none; +} + +.inherit_header { + font-weight: bold; + color: gray; + cursor: pointer; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.inherit_header td { + padding: 6px 0px 2px 5px; +} + +.inherit { + display: none; +} + +tr.heading h2 { + margin-top: 12px; + margin-bottom: 4px; +} + +/* tooltip related style info */ + +.ttc { + position: absolute; + display: none; +} + +#powerTip { + cursor: default; + /*white-space: nowrap;*/ + color: black; + background-color: white; + border: 1px solid gray; + border-radius: 4px 4px 4px 4px; + box-shadow: 1px 1px 7px gray; + display: none; + font-size: smaller; + max-width: 80%; + opacity: 0.9; + padding: 1ex 1em 1em; + position: absolute; + z-index: 2147483647; +} + +#powerTip div.ttdoc { + color: grey; + font-style: italic; +} + +#powerTip div.ttname a { + font-weight: bold; +} + +#powerTip a { + color: #4665A2; +} + +#powerTip div.ttname { + font-weight: bold; +} + +#powerTip div.ttdeci { + color: #006318; +} + +#powerTip div { + margin: 0px; + padding: 0px; + font-size: 12px; + font-family: Roboto,sans-serif; + line-height: 16px; +} + +#powerTip:before, #powerTip:after { + content: ""; + position: absolute; + margin: 0px; +} + +#powerTip.n:after, #powerTip.n:before, +#powerTip.s:after, #powerTip.s:before, +#powerTip.w:after, #powerTip.w:before, +#powerTip.e:after, #powerTip.e:before, +#powerTip.ne:after, #powerTip.ne:before, +#powerTip.se:after, #powerTip.se:before, +#powerTip.nw:after, #powerTip.nw:before, +#powerTip.sw:after, #powerTip.sw:before { + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; +} + +#powerTip.n:after, #powerTip.s:after, +#powerTip.w:after, #powerTip.e:after, +#powerTip.nw:after, #powerTip.ne:after, +#powerTip.sw:after, #powerTip.se:after { + border-color: rgba(255, 255, 255, 0); +} + +#powerTip.n:before, #powerTip.s:before, +#powerTip.w:before, #powerTip.e:before, +#powerTip.nw:before, #powerTip.ne:before, +#powerTip.sw:before, #powerTip.se:before { + border-color: rgba(128, 128, 128, 0); +} + +#powerTip.n:after, #powerTip.n:before, +#powerTip.ne:after, #powerTip.ne:before, +#powerTip.nw:after, #powerTip.nw:before { + top: 100%; +} + +#powerTip.n:after, #powerTip.ne:after, #powerTip.nw:after { + border-top-color: white; + border-width: 10px; + margin: 0px -10px; +} +#powerTip.n:before, #powerTip.ne:before, #powerTip.nw:before { + border-top-color: gray; + border-width: 11px; + margin: 0px -11px; +} +#powerTip.n:after, #powerTip.n:before { + left: 50%; +} + +#powerTip.nw:after, #powerTip.nw:before { + right: 14px; +} + +#powerTip.ne:after, #powerTip.ne:before { + left: 14px; +} + +#powerTip.s:after, #powerTip.s:before, +#powerTip.se:after, #powerTip.se:before, +#powerTip.sw:after, #powerTip.sw:before { + bottom: 100%; +} + +#powerTip.s:after, #powerTip.se:after, #powerTip.sw:after { + border-bottom-color: white; + border-width: 10px; + margin: 0px -10px; +} + +#powerTip.s:before, #powerTip.se:before, #powerTip.sw:before { + border-bottom-color: gray; + border-width: 11px; + margin: 0px -11px; +} + +#powerTip.s:after, #powerTip.s:before { + left: 50%; +} + +#powerTip.sw:after, #powerTip.sw:before { + right: 14px; +} + +#powerTip.se:after, #powerTip.se:before { + left: 14px; +} + +#powerTip.e:after, #powerTip.e:before { + left: 100%; +} +#powerTip.e:after { + border-left-color: gray; + border-width: 10px; + top: 50%; + margin-top: -10px; +} +#powerTip.e:before { + border-left-color: gray; + border-width: 11px; + top: 50%; + margin-top: -11px; +} + +#powerTip.w:after, #powerTip.w:before { + right: 100%; +} +#powerTip.w:after { + border-right-color: gray; + border-width: 10px; + top: 50%; + margin-top: -10px; +} +#powerTip.w:before { + border-right-color: gray; + border-width: 11px; + top: 50%; + margin-top: -11px; +} + +@media print +{ + #top { display: none; } + #side-nav { display: none; } + #nav-path { display: none; } + body { overflow:visible; } + h1, h2, h3, h4, h5, h6 { page-break-after: avoid; } + .summary { display: none; } + .memitem { page-break-inside: avoid; } + #doc-content + { + margin-left:0 !important; + height:auto !important; + width:auto !important; + overflow:inherit; + display:inline; + } +} + +/* @group Markdown */ + +table.markdownTable { + border-collapse:collapse; + margin-top: 4px; + margin-bottom: 4px; +} + +table.markdownTable td, table.markdownTable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; +} + +table.markdownTable tr { +} + +th.markdownTableHeadLeft, th.markdownTableHeadRight, th.markdownTableHeadCenter, th.markdownTableHeadNone { + background-color: #374F7F; + color: #FFFFFF; + font-size: 110%; + padding-bottom: 4px; + padding-top: 5px; +} + +th.markdownTableHeadLeft, td.markdownTableBodyLeft { + text-align: left +} + +th.markdownTableHeadRight, td.markdownTableBodyRight { + text-align: right +} + +th.markdownTableHeadCenter, td.markdownTableBodyCenter { + text-align: center +} + +tt, code, kbd +{ + display: inline-block; +} +tt, code, kbd +{ + vertical-align: top; +} +/* @end */ + +u { + text-decoration: underline; +} + +details>summary { + list-style-type: none; +} + +details > summary::-webkit-details-marker { + display: none; +} + +details>summary::before { + content: "\25ba"; + padding-right:4px; + font-size: 80%; +} + +details[open]>summary::before { + content: "\25bc"; + padding-right:4px; + font-size: 80%; +} + diff --git a/HEAD/doxygen.svg b/HEAD/doxygen.svg new file mode 100644 index 00000000..79a76354 --- /dev/null +++ b/HEAD/doxygen.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HEAD/doxygen_crawl.html b/HEAD/doxygen_crawl.html new file mode 100644 index 00000000..46d0ab8e --- /dev/null +++ b/HEAD/doxygen_crawl.html @@ -0,0 +1,202 @@ + + + +Validator / crawler helper + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HEAD/dynsections.js b/HEAD/dynsections.js new file mode 100644 index 00000000..3cc426a6 --- /dev/null +++ b/HEAD/dynsections.js @@ -0,0 +1,198 @@ +/* + @licstart The following is the entire license notice for the JavaScript code in this file. + + The MIT License (MIT) + + Copyright (C) 1997-2020 by Dimitri van Heesch + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software + and associated documentation files (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, publish, distribute, + sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + @licend The above is the entire license notice for the JavaScript code in this file + */ + +function toggleVisibility(linkObj) { + return dynsection.toggleVisibility(linkObj); +} + +let dynsection = { + + // helper function + updateStripes : function() { + $('table.directory tr'). + removeClass('even').filter(':visible:even').addClass('even'); + $('table.directory tr'). + removeClass('odd').filter(':visible:odd').addClass('odd'); + }, + + toggleVisibility : function(linkObj) { + const base = $(linkObj).attr('id'); + const summary = $('#'+base+'-summary'); + const content = $('#'+base+'-content'); + const trigger = $('#'+base+'-trigger'); + const src=$(trigger).attr('src'); + if (content.is(':visible')===true) { + content.hide(); + summary.show(); + $(linkObj).addClass('closed').removeClass('opened'); + $(trigger).attr('src',src.substring(0,src.length-8)+'closed.png'); + } else { + content.show(); + summary.hide(); + $(linkObj).removeClass('closed').addClass('opened'); + $(trigger).attr('src',src.substring(0,src.length-10)+'open.png'); + } + return false; + }, + + toggleLevel : function(level) { + $('table.directory tr').each(function() { + const l = this.id.split('_').length-1; + const i = $('#img'+this.id.substring(3)); + const a = $('#arr'+this.id.substring(3)); + if (l'); + // add vertical lines to other rows + $('span[class=lineno]').not(':eq(0)').append(''); + // add toggle controls to lines with fold divs + $('div[class=foldopen]').each(function() { + // extract specific id to use + const id = $(this).attr('id').replace('foldopen',''); + // extract start and end foldable fragment attributes + const start = $(this).attr('data-start'); + const end = $(this).attr('data-end'); + // replace normal fold span with controls for the first line of a foldable fragment + $(this).find('span[class=fold]:first').replaceWith(''); + // append div for folded (closed) representation + $(this).after(''); + // extract the first line from the "open" section to represent closed content + const line = $(this).children().first().clone(); + // remove any glow that might still be active on the original line + $(line).removeClass('glow'); + if (start) { + // if line already ends with a start marker (e.g. trailing {), remove it + $(line).html($(line).html().replace(new RegExp('\\s*'+start+'\\s*$','g'),'')); + } + // replace minus with plus symbol + $(line).find('span[class=fold]').css('background-image',codefold.plusImg[relPath]); + // append ellipsis + $(line).append(' '+start+''+end); + // insert constructed line into closed div + $('#foldclosed'+id).html(line); + }); + }, +}; +/* @license-end */ diff --git a/HEAD/favicon.ico b/HEAD/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..b8bc61f943a9e98b42ade16abc418d5a0d0f8e72 GIT binary patch literal 561229 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%0D8>k&6@Qla(nVVsriM8)~DdFD6;*dky48t%zeX z`yTv-@PXlej3ysk)DA4D>udDjWw0vzKN(kuQ+4svT+K=#Iiy_PRY(E%)KGbhNeU^9 zpv3x3lvchbXQeJzf`%U}C|%t}Xe$kF{Zg6cX5TR*y8ocJjcOh#NbhuF5cLn)7NzDD zx-lIQ?3EnkC;t9{^N$Nltek-~Qwiwsql*J0DL=HO#-B&4n%TlWg#5R+Oc;9CxYW721xB zMo}zVAf8Vyw1L?m1!wVcHvxYX5SfXUoY;iFfl1 z#ZjKMAvz9Wimt)8(_P+JQY29782#H;U5LiN4a?pClA(X}*+(!EDh-j+B{wDb6v>L% ziEhGAxI%GMkvXHvJRf$;Zu|zVbDE50N3>4VLQpnark|ZCgVw{}XQx|3>z9vIPmMlF zec#$WH;$sMPYuR^2NK3|I_*P?&{!FstI$HtnpUP%J=wXJY^LR(FECYe-=+4ioM&mV zZnfD!HC!vkelMDGE`UKyFXz?ma~II-mD3qn=5zAyhWgHn(@(J(wkT5y4R-6-hC1Iq z9h|)=&cl*Qxbobeit`NZ$QUaqqCF$zQB-_+(OHJZdO#adn!h5?fIbLO;wX8EQt|HL zS0v2BI+OIpEwzltUPA=%E|5qMn%Am_OUuE!l6uG}>^FCVHzFwguJ^ZJ*NV{jvqftR zPyzycU&-5z`PU|Z?0^h#0f*F2tlm^slR-W!VM|zygFWZ2`C}25BlA`IX}m)?{viKF z$=l^?4@#%Zf!ljjC1L* zPq*pNYR$hK*?U$R1Q`_*EZxSF;<8K=&#s%WXr9dyWGn26p1yKgTs%~ci%I$bLp zM7ljKF0gjIzY)nBY_;emuoBrio6gf@$pzh7O+o!MLGoet7dAds&IHgbalOAw2IIuH z0RCDD_$?YYO19k^?pWS#ER22GG*7CN4J%HZNvPtl#+UwAoZ!w>Vdp#>BLKlKpFx6% z`i*96`j^o<)%S!kE%jM|P&qQ^VE=(2rL_83YWnU!oCQw_q$17+1@_;dD&pUA0 zp~IeuTUeE$4Bl8E?Unqrt-nSu$2d8BX)1DZm*~#@`Bcl)JFLK9x>wrh^?5%-OJGZ~ zpzMfZXv3SIV%y@e=;zF?uh{M=_zDXx$v+ORxq1&YXu)O$7~TD(cgRJMi)R`|1vUV^ zQvW!SZQlpUs<#k!xJ2W2JSQ}sEL3-bi?13gYlRdFSyo+QgBTO{bVP1S1s92eJ@$uybB7Yh4!UnOR@vsNhz?ZFQUkb1vE?aK^3 z!L)4^(no&Md#~6}uM$ps7X(u*Tl{{R^L;!WIdbm294rAGkaysnxRQvlDue$eU%v3+ zqY%E|d^rlz2_-JRxYcR?^B?8hmga_p?S9bT93(sLv2~YHA_8-|19?7{ZZ)S6B!nV& zo-_z4A~OF}kf2(-TVSilksN+Qz|#-?*|Dpemg5_h_Zclz(aszDBZ#V|kdaRoi?@3- zzY_2M0&)C{ZbXD8_>R*JF1{HiTc6{GDvvE7CsvP?dx*R-b236Af5gWi+mrB5U(6q- zygD_8Kt05p1AOkWTwVD!d|L44m=8KtRD{liB-i$Dk?rSxLZv0iWIqg5t8SS2#|yhW zd84fXnbn$v$t9}~H577Y&+dpDZ;*e3fpe-$?Y>?197X*;;_{x@e_p|pb;`+6%b(y5)A{IwSQiRCQz`Pk^4LS>pRAq)!4Wk39& zOeCy`>*R4jKlCXy9iJo`8*9siFo3Id<@u8^M3%21HODvnUBa*xBLRGK&!`6dOrH_8 zR$bu+Vuk|Z>WPscT?b)Q@9jHNaTDbd(~QB@NL3~{)zj1ZJf~ch2~!DE!y}2?Oz{~a zw5&%67$Jx<(#Ep+0Fc=THxTV#u-4#FyT6z2cq z8T`*xPCX5I@Kc_b9cX(UttgJt9qr}0YV!BYE9?!YoNy4mmXC(z_hBT^73VpJP^s#HkX zBeOnq97tdKnS9wTxAy9nxx#CUj#%EZQy(6(RYlfo5T#E`s9iw_O{Dnt@x5pnVQ zvoZr^Lm!9m4bZV5l_AVGj9%QjC(mVHS)!UA=AJ})@0G*7taYP9Bv57;*uL_mgj1)N zN6DigzI}kvLM)Li^|MGvyQ&UIihWu3a5F}hEViqf2l4pYC&Yl7zv%nDA$!kh9?>6}G z#NnpsdjI4d&1W=KXG`6@BMU8J{<-`SAovIg3azf%NeMUW;!y}cpqCW9E*sY z4zMAjZ&4QmUq^zA0zwT3XXt7KK+)>5y%x$gH!^rIGvwTCo_VzyhTR#9S!# zP4V%}TOj{e8uF@nbQGXcJUJx`Jd^Q6ocsQzYBmH!Palj*-3B3W*zwsQg$MUC$NdX` zLOi?5Ii4Fd=K~0=-WCE43?Kb~pJ~+rCQXEUj{FhMTd0{Ypexvv5axrh-WD0ipuwYp z=O9W9dd&W%f+l_!NqL>2?Hlb`+zxoJoa0a~zdX$2MC5d$t3cc!e>DdH!?krrDEC&gxo~%LTEV`p)9l2o{+_`lrFP^pTizIT~goLd4mIO%b#v7Adei-mj(P2F4#R7pHYr3cb-nj zxYm^fVy^1AnLn+)0z%?p@evVudLk#1gR<(P>a%yS$ZcwhUA*8dCE?T5TPpR2MoMDCz&n#HaJJzG^a?D z>>_}ns~Jh6wR}~f+N0oV+s}1Q&^QRP1k!=8-rUr-Kqc0R19_ZC>NE&=Xq5Hk9#LHE z_=d%~dNpG19t%7+(Op$tOD#kB-=pj5Yet4HP0%}}cSDW?PqHT-y(OHWDml(iM5T$C;1=ijQKS_NDo}$_Fv}ie{VaQdgbu~T!6{_tA#{l@=55hj z#^P-gV8X}_Q0HzCqkJZTyfEzw-oD$;`vmEJYXJOeecugfFXq17U2jli!L_`T(AfsN znbmsb*!hrWc?qTC6KTDo_+h&_wwlhb7a6+9iv@b6S+h@mQ0JP9As5mtdDu~uEE$>q z@BvopBV7U{bRZ3UO&u{~jd!WVOPeJM1^08>1FSq#qkcm@t=-+4x)GRmkAUJCrME#3 z8I1uOl$HEpv))D=UtebWeX2C{}LxUQl@Af>^7)+3M;R~#fHu_&L1agmM z|E;BYMg54zd6j#Q?Y|50sT_ZoYw&Mt1btRtgBhlilgKn1&ShGlJ>bt!#jxRlgY=W$uxU2%xRm<-HmvrMUL#h-ADTiY2$n7|ZS=cDF+vtNa4 zd)Xn&j3xw6STudZh6J)Dc_Oh^E@=g%fXD|vDEb)B>kT(A4lt&^nqDoNVSUqq4( ztdK%@l2Q1K2kN@kwl7`ODZ8=MXvJU~kZYpIBXIGL3@*6RbHnsKUlIvLR{Z1L@3?Hhi_-O1D|!pP9nA+LA(JfS64~*z_G~hP)wV!#I5uHEcC8@^R1|&&35T%|F zcpD-6SH|N2J{e#vyJ)>x(Rze%=R~Y`7P*!9g9U!T`GhXRxWnH*hS6Dn@xr3%`Hd~C zpXiz-v=%LPjM5}R;bz*4$rNhGQTsiVg`iyNSv63WiK4xfg1!8~d0(KaFc`L<4l{ux zr5V{k7|Shca_sw;2)o4feF0yrc}JWWCEt@XtAXUcOvJIYsoFkE8RS9sL|bm$+sT!Y zDT`ag(=t^dZrA$N{hB_@gfc~s{=e)l*6Kba_qnY!=-1Z>qF;^=l-{m=zLvt4`i^Bq z7b2Q6l|*bFaN!qUF95Nb;~u6h+torfFLKX0#C0#}`)oynk`Hx9ad z*42PRQ!mw`4>CUuNoI%RtWT*ACG-Zj?Cxqb-V%LH+TGgQo$f>dzP`+Q+4I!g%@<}{ z23HrvRl)^SRA2ipQJ&5P8_3jjM_ry%sHR402+M#GHG9?o3-TTlC!$4${5BV{oELgZ zlR=n-+|aR=&pnnd{Oi*~Qj)$zc#kQ2fw}n6MZzHKWZ_H`%^olVnF}Vv>YpmI(;9b+ zohcy~Kl9t+$MzSr)dAe^;XicQPEq>QIrt%eE~(2tDeVD~M{k6?QuFGUlZui<6Rh&q zSorAzs+mbp@ph`m2RL{64~jM90< zS|v|!iW2<$`lgg@^|srx>P-z{>3p@0)ZW7!fOa*Q%F#oCxjBz%(x-?a$^y(o^}v8e30YO!|&rc$Q1xuLPMY%oWgeD>SAS`6} zt?{y^@lIi(5LY_t<~8CE?TJpiKaI zCVUyGlcJnGzjV({9Oo_?Z041Hib^t$XRYZP#p4?Fox>O5OgR6IbQ&;OeA!=#(I&+W zIm~TJ0hNiW@zHDC#;X^|K6h@7UFk_ zF;*Ee5QwZVFz?;U1`~w+`{g|Fef-_3!wWUd%eiG-lkmjUAKZVsjYQu?n%d8zUjW`XY?h-HDz9t7-&EB1`M12kQ@iI??atMy_L9OXOb$`&W*4BBT9 zvBN@ArUEM}E*zl}_ygjH(>r$@{So2YGc;GJmyC#)-w3Qa8mE$1MM90P-C5HkJ$FbO!QVpyZo#%ulb|z6_)M zL~Hr1T;e|-&%wdVl2np-2qMF*;HQe!2{b_VOLW9||8rlTy9KqJi}^N-P{i#TCeB{B6zu zcR-v}-)HJ2nAMh%bnDH~3yBH(g-a&qmsG_E5-1<{Wf74SQ0s@(bo)E}j@C!hbMhZj z+1xpCZ}(>V99Ejk^d0ys)*j$U*sjU&lK-_Q2jlrxTiPj1 z4&!2G&u@t=)Sa0aHX*+GTGmC|Et?umTg|gZEKbFdDOL6{DH#U^=Y5$D#rlsZ3de!5 zdDTa9c-lu|`T?}U=Z0S+PAJ?4ca2r){r3{K*j3__B)^ZwIQhPrtBpQe|2xtuMD5`R z9r+M5MARE={fKDv_JRlmE(|fyqwHgSzI`6=vLA)F>@*@?+X7%4GYAJ~ySCfpoNx0K zm4U>4!v}^^sczrUWH~!`sY@{`d{3T9qv#cw%m$mhS7Lm1)6RuB08QZ@rl-FcR`ef= z6)`=-bKBDrde^-dBirI7K#+ki6}SoO_dFCLvox$mS`E0Az1x|1)EsaF5LG z!SK>Sp*cB9#Ehlz8!xhlSLhX3H>;WFAa_D3;*=dQk|10(pNxb}Nhn+^UuWvf(=zY= z%Q7aQz>}tLGOYsNet2l97&IRoVa%CuoG;RUa0~RvSj>SpO!;F~`q~Z8<;%BEMYM70 zl#$p2s$RqG#q)b)B7AZusX z2EhEq+r{X){6<~3QGxHqjs@!`KyVsJSX@q(f^+sa-Os+>bZC$|1sYAakvXJ0udsaO z>p=6RPCz(Ew+S;h;^-9+(C)h}-ON2O1CBHx!&;_l&sc^QlH%3uiK~Z^a&Oe88kvZ; zfy0Osf50H783LvO!)k0-Dg1jN3pxJ^Odz`%Ln-7)d45gN-`{o*ShGp~e1rpzu9G;u zP0q~sBJ6oi?rbY{$Bcmo1B0`)0ftPw{~vOqU%OEGe&`WhhG`#gw>iHKNUt2=%A79+| zX<`B1oR>T~8K(T(y3nl1^CgqRv7d4Z_f?458SKEhEI8=2_Q zTdW}EcHZt7Q@KJXdC@a))PUM6b3*6permv0Hi<5lNkj|rQ@>NK>_(>5QiWohYi4G< z2;@)P*#a-?^JW;D++B>ih(k1QiSYA%T@N_z#@%lszZQ&9e@Sd2rww z=#Fe{q+z9&=>J_GAj5qr%7FX(w;~lUJ|0`Y7o8_bZt?Oxh#LT5ECyNksgaNvI3$Hc z&e-7Ik(h)(@fUgE>#y_0tJb-v%D>p+v;F4C0$Y=YXsz|fxA|8#8&$CUk7gxo>Xe^? z;9jNKMmpaofW}yWExzGj&%fDf#E=xJb)3oI!T8Uf^9e&!|9gBnVk_H_$7#mfe4%50 zAFw~*biI18=(lP^f@CEfe!;0qKJB@A-3GX!-N1jw5@qFMZGKIp@L@@BR;S4>;3AgY*%mk716#aCJa$mBY-C}#R)8i;wl zz;;r7KWuul`Hj#rqZYro5iGHvuT+>vv1EB$Cn?WL&tVF%4$zmVK4<6&?=2vjit?9i zfXprPK%j4JL_vZgfOW^tf@p!ESTeX&lh*zPVCJ}~!hC`nxTCLRAL<{dz(hRzglTBC zIlaEjHO6?mALyGg)lMjGHpHYUT^u*NJqPz3`DwQWIn+NQwW=qh!reku=7S8cd?dkc z(ZHtmUgKfWf9*L5!=Fey;OUtCfdiDp=lzg9^ynSrRn9#%`T8@>9z+kl-)hQd8C7kc zFIS+7estC_>&sr~2f6AXVJn?7Ppo9p3Twy8cdScdj39etz;9!|_(rOWDB0BkzMl|l z9!>!QfkI>@#nh>()S|WWcT?T??nd#fS=evj&$ZF(sErp`+>TY`Va3~=g1h*-7jdyZ zs&;;w*$Z}am!O_&gfwdYR)@7pb{o-a*7jqZph$3n=>*_?SrzaSzP!IN0j_i~6R1Jw zj+wkklw1Kp9YCdIcU4`Wk(IG20#QjphyS8~E$}*_k=DWCqeM6xgk6P$oJ&mkbS10I zZB=Qz9PXDtLRz6gdZDe5Mw}EY=AZ%)sAI~yOBX$H1&O*&!op#1pvhgKD}*0wNq$Va zXkxOkk1lW@BtJ>R%z|E>p(=Op+d4(_QtmI#K;Z~ikIx4oqtBSaS099WpAg}gAX4Ju zEq}XG65%(l@z!%(3X7dU?48A2rz3IRkAD?<|K|*fTBWL60Mr&mW2)BuRkfWLR?Dx# z@I}aSg<>8gw0Z0Tr8Z~aM4T2awv(X;;*D19tP_aF%lNbyU?$a(g!hOVV%e>i-Cl$OIj zjD99)?gw##;{DsV$K#q)c%e-M8>8u%hFLcwQD^%j12;$*bw3E%vrt{7;Pv*O6y5DLVegCbALs5{*yVf*t1=H>NbF_e!MVkU>1VCWn zr`u{&2+C@ME_n~k>AITXpv0p8U>bBex+*zC#0fz@sAo$1`{4dPhe6IAmN@limrYJ$ zFc2y(&$l?y9^~iFe&37?f$lkHTP?;p!VP-G&A4E}|A0cJ^)Q+4~02$YZnM;&|(j9#}zV z`WK_lW*qe7cTCLP)>$L)H@S4a&)BA;z3r>Z8DkQ=9>9)4nu^hBI*1EU6zM$+0hS`e zZ@#@?S3yTsRZmKU>hZCjA~xL1wFlQfqR{=9RL@q4d4%}jQ-AN{4I4M!Oi7UBayUak zmcX9x^k2?`^c_4c8+&vJO>X!WLmq5D&Tb4fend-*8IT?))Cc8^AJXSO5pskh-ja6a z*gItkLmNN{%STWOwY-fpUNeS2)sV_{Z`5PD5G%XTM%-I*85g)KKQi4;_~A2pX54~`ceQ5uS@anOKA6PGajXs6fnU^`EJYt>Z1srt2 z%Z$M!GRe;BbFUNiI|w#Pj|v1|v5ktRP;^=|baveTAbQsIFX=q4X5K=(y&JUjzkcx2 z8NZamw)p^tZJ2+#3HdtYYuH^wGi&0Xn72z;`A) zJ7uZBEN;CCEj9F+{goCrX)xR@y${46ah1Y%KWdJeu=Gk~{+v$(GNh3}#Tcais5puT(}7bL5M1O9Z$gP*3&W-~nOF($F*%oq!0@VA54OX@AkA#|}|LoQCSIpJ} z2dql4w^2Dehq=~+hQdzvMaz9ezl;XTQNpb&=Mb__xI6*DDX~Jt z=5QgI7*w?4AZvefOX$V~1jOIPvSmg0mucW~MX)Cm{(=8y{#0;ONJO(~R~tg-6j*)) z?A%l2rE@dA6hWo6UHAi@A#VA4IsWWe_b;}?8;3!WP_{F2q26z4j4FHss@$d@`u@3~ zY752~;?u~U%O+ZF5PHsBuY7!#bq!6n$~m3~L#uZGO(+K}Za)XiO)P6^3p32)?O4wT z5i|drPyArcAx5#VS=Q!58gyqI4KiOuUZR-1z1EBFC^=ewq~a_3p@cZ_y|$iNK*f(O zoNNk{qw2PgfnL-l#+t-h1zEO1MK^dQ``1-Bc;$v$Q>!WtAbPr0AFo8U&TDz1H;q9> z0EjS48A|@dm3KxY+yn`7R272Jr}r!>Ud?y2Ab0uYnQg$99s73Ug!6CJosK-j6pGdP zr$qR{(qGuPB#Y8F2c+kT!QPWdgMO3GR)_KXF$gVaaiK^l+higdSD6hZc)$RwF)2X` zW*@Syz|7{0lz4G8G_I*OLTj0iWuCTKqAQaBDByLaz_nNbbnl#Z=jdJzFaCvk5uu=@sky`_5&GQz!GGI24kiemNO&c251 z$Ojr9Py@RQ>piyMBFBk_+y#%=pNpDdR2+C%p-y*%P86VJJxmeQerTM{hSlnlW=o=2 zmyl)&pp1{^LR2&U1r1?dwW2p7g8%ke6D``ZHdc6{#yCB?`FFW!oy>lX+Nae*m!>s( z7(!y>Kl^lY9((u9G8lY1Fj9ucdP+->fvmpU7K40PT9td|ZJGVH((MP7EFbpbUXd&| zMxU9tNoeXUq#m%B{n$DhTwnhxMaPXb1BV-ym@7ZQ8~^MKli1ffq%*G}pFaq4$B50G zXu`y?_0IamzJ&hq~zBuLER94DKKF++%YN2HwBR z*z%iv<~kQ~`Hn)z`At(~BOX*HL(82qZ(xC#r95nQ3ac9T?alZacuh_wMD0BAfLp}d zjVlXXA59!gbqIlXp_Dr#2JfA}4>qkGZMf$e45b)eLfjjapN&<-mbzS6gr%yi3MbN% z8|6ElClUzz&pj|OFfY^i1G{jy%virpKgMv`mV`m#-$(-JtKW2>H1QEP*+3`Xle1k$&R20+;N_BHxt-3^eoCn#!9A2^dVG6Q4 z?|vRHTGfwc2!9cUG8>o?swBj_za_LD`FZEgKK}Ufp8r?_p))3zOy9H;EtX^uHkJAe zlXc7OkXJtW0`;`ti>=d|4N5VNppOQtjtasAwtO=ul?VAiZGZF}$Y1GWD?xLX*Cvp6 zEUo5zdJspGf{fRvDvtaHkJ$YCI)J~2FjUadv#1Ujx%blJCe-z8WPuyxlMfaKs3TTP1xbSuSLaIF!^0X^Wd8lGn%_G32;o7@?dT0M zTt;D-Isr+#~+tRK&-rsOs!1tST!u~erZsl`4 zJ)iA6C9V@Ss+y@&h}B6r^h`wvOAK+~j&Ur;b8JXxM=FKmbkk2hHw*pr`+#wady@-; zc;6TCx81)gdmkDKlD`l-O=QHv4)x62=uuvKhzu6Bvvl1%`Jl`*9F?pI`9g|Ukncr` zN3p#RjcL=WFB@gdjvUAnWKyT%>jA<`Rm#jnd*vW9BW%bgVHIaw_wT&*%~0pA%E!HT z1a8i|Q2`A*KVaz4pcK7wJc;&XZd~$>0jc;feLoe44FCFrbtv<19c?itjhGOE(GoZ5 zk;lAI6@plH-bGsm+Aa7PDc?_l>_l^2g2kh;4X$X*Tt#S0AII}s#sK15PLECkAW!zE z9!K7$`|*jmg=@))gs11#3_I_l$uP>5D+%65n~MkfKtNI$6FG!(copCTe{SsXIDx-t z9=U)prW!?|z{h#<3{+~9D$eWI_mcbwV4K6~d2f$VwGK%i2LVO{Q3fQc*BKXh3vB3U zMx9c+&bAj9kU>xq;YX$g!cq9|2yfs4WB~dJaZL%JpOi@M*lUrB|I`XA*R7hVKSaxt z9Z>xa_gQ#RwJzf`9od5$^ObbAcOh=&&#>10^&Xl0B?g2pkhf4HuJee{M{bq!;d^7M z%&`HQ1YlrRxGuC`4@JLxQsNxcLVY&s->yVMWNY-XS7n8gcLtJ)Za^X$en;o4j;m#N z^b;8);*oSJ*T-_D3X}R_Ngxm5{Ole;W(p9G9sW7WF&Bu@ND9_RoD)3{K>~8DEkNd~ zpKo;^he3eFZV{-(8qg4JT^(%59Qh$LMUVl&t6i}2tVZ?lqiTjr-+VS# zx7$@QcM7>8e7;sbS0pe7V;@x%(wJJFUT5we=sHxZS0yyBrvRy-Y`ip8=5>`OxBZt? zWM=qhV3(QKk`OUL8+T)+qfI*ZMG;4Vbxw8`a6gzjp5ddr9E&GSNZj9*3KpTM1nbs} z`LG#TU&9u1mKHIOeh}&Xq2^$EvPJ79_~tGwAsE>E)fK?1m5M)BJjviUQXW~-LABrN zf*OAvmp^M!Vk4(Sssfw~NETZCunq(KRGJIEs&JXj9Q`$f=ur+&VoeH!0ulxJrpHK3 zA?6vU5?`bgCtC?jix;H+8sCki%yB~B10tC;AQCFl6;|PrQdNC}ZE7phZ3R7Ev((vm z->V;(U@v?TM5w(}ja`~NWy5CiuJ$_^=EdBe+Q4a!?C-KM-P3JsnIl34g#84ZCnwIo zHxiao8~q72cLu#gXF8CJS?Qi>Q9Ky1$GoWkRwvE^^#!g@;IeSn$8B*FKVvZYFhC|! z_qN@RmJn8rq0@`%qu@(nu9-3##E(X58Z->%;oV~~ekj>RL6m4@sDdn`zGWg=754*-!WcrL_%e!B1M=MpdcCQwGqDm_Lu zfl5W&O4+}NYjo?W1A!7NZE|amecp}j{l}jHvm1xNY=-dL>!`p3Mp%Rzk{}0pRDisE ziQ2dXv3&mLFk?>I1`;NDGZlt+clyVya4|Ry(-20ZQGrV=LmPI_-)MIHQj^HBzq-Z= z!Q&2eF~uA=IDlMNV-Uf_Skh7^&(>1#0896F5sMWGY4O+^(m|CCFqvt|0h@wx1}#1w zlR|1C?)$X(!>6L&4xu__gl>?e01m)eT8@@Bfj#6_y9Yu=o+N|R#PcL;b^uouk<$4N ztd|WNvk-7QyQ}S&@O8hHg1rC9abg77rz4B=u$ln^Q$N=m#6&Vzla<{s9E#q_EV%G5 z`bi4ZxR0ggC9jmhG=-v{(X5Q-bR-YoH!xYRk632J_nM@a?kKv{1+1W`cps4d1zNv; zVQHHfAurDaZ1+1)?B+<1K8Ndl0YN^YZ?tV@-~r`l%_b_FPBZag{CGhbL6p|a2J^8h zhzl@A9NB$l3$5nJFnVazDZ`Fs{;p9Qf65W!ZRP8yE8|4@pfA_&b10Cgf?i0~PhSM% zEr7It{K6Cd`*r~0+?c_Y7x#K`LstqH9{$nnj0iu~(5JVm*N7UzlpqgTC*(T_vC>E& z{tZ*h@9lEjg(?p&r;2LYFLx11svrM(YXFop`;deT%GvktoB?~X;c=qp`I+xh^)Fu< zB!aYA>cEAij1S-fgn#hq-Fc5vfbq+Pt>3RlvPM2*h=K))VQCddl-<|=N+dHW#E0sm ztVqs;k*<*2Jub)I5?DMmk<1cpnJrplVqblH zR%Y@J1WW8aat@x9s|?zBR!g*gEZqR$%PJGfnr5FQzmic`=0+|D0f-UYRbo}6zqX+V zwiNCy_N)EglGp%P{mdJQ5nov36mSrI25$9lH!XhA9(FG{zg_nGJzb7BQsc;We`B+2 zbL)u!sRM<(%sDx4GL14XBJ)Q4@jRk!X|zc~211lE3=kDrhD8B4PDJ?~4$ySyXgnFu z72cZKV!}USt0P;@w!h9ky#E7vWP^F!$?c;uIP*Y&yr4kA&xzxMeW&hEQSueeb~rB> zA)oB_d@S*Nu5iK~G`hahNIEx3phHy$`T{j&InxU(FwyCk8L%-|>eAHD7>ox02ZezB z9-bbG7IxyJ*HPN}FzrHTgI;b-gZWu`n%v{xiMbUuzi`WnyFC#m&2?W`BpF3NIvQkQ zVZArb07*Q+Zr1v~gf&22c7=mLwj-Xd*^@e$7Rb|I!5*Q|BxE(P;bYy^mRVzGf#-iD zopn@{-`B;5p-VcY8M;HdyGx{`Q5poKyF|LXq+3E#N;;*xk#0c{ko?{6`~J;hEtqGX zbMHB4@6TRR{dA;c`s{`nh+eU(+=2wZiNxi9=^ZP#)TcHVSdyUjv~`9XZ&70jx*E=0 z)LXOmI)d}Lo414}*jxZg>@<5lqZ0tA+q`tHPik`#T&0CojCh6R(>l3{omXa~!6jV4 zCk;0L=RG%-y?`lt^V^V&kF9~(GE$3Ntz6ov@iD2+Qz(tg)ddeGg!T(9ReGECk;H~d z=8J2ZCX=lTCyxDI|IN3cvlp~FBhH6f2Xm6KYVe6753*?~QQQ?1V}z_v4CyYsRz(H{n* zH;$fCG36~z@L+srkj`p{$EcG#d)r9$XD3-V*ckwk?(z`r0(L(TD9|F_mhTAUpsTDB zgE?v?Std83e4%+4mbD6JI(9MjVji(x|L+_4h(dH1UxNW$7;#W+qIOlq=d98AJZ$RU zh=(d4i}9RJ*en@!f0NRV3_CGTvMMW`0duXZCnh>*!x3}z=;bSww&5li%p!fmS_$?a zsR)EyUANQ7pz=K34-!4x;+S#klOzUjj5-C;7yTg65REN!>^gWTf`78zol1QO0^DM* znGcmIkv7C052H&Q`8*D;y}keV38U-3&mR7Kqj#^_C-hZeAEp#?9i;O>1HI|NP`n6u zRp8~4P`1BI4D4}Y$};J;RP_bM@boBiH60ixF;~irm;ChvF@b6UzW*!;mH!}fV$uoe z)^8#gEouFHt;>I5i^Jtc3JhOJcyQD(04cZf;k8{a&)CfVHI^Z-bKcydgK8>5bbFZ2 zmjsp!vAEgFXR=%TJeTItCGT?iKU&C086SyM{Y;7eGlBUl=%jxm3J@xo=7->vQGsHY zqM6kb4s{Kbkp>^?2hOl+u6GXAq3Bn$)m(*)1i8#eciY%WUhVM+o!msj%VcqH-7B_u zbN=hOxw4IaloTaLP@p2sMIyOt`;;b|q~B+|hRhn=wj!t13C5slAB)Ehqa13E20L)J z3*O*_!n6|_1!<@+0GoTDpy_p^!besk+Bs1iT102P0Vx@CfqeZ~hr=&bcwk)kEqz8W zprJzmTOzQ_Ty03p_+DV)%8R`kmsdRC#pV|W=WNs=qD`!30VL#8=#y28(I52!Zp*ZB8kW@iUyhZ8_hw1FCXs@;eo2W!9i>LlQjR#ceg9}M91~e-)?@?x zUW^0M-pbclXGr*$urGA>VJ^n%*3zSA+F?*lAu#M+CJq96EuO*8ZVAdn(jm(C-i zV^>Hu5j=EE4TgdluwM8sQwCxv&BUKxQqZ_nZ^ z!CMQHC zGC(p}cHkluw?lNA~v{oOm?X4PRkx z>Hcs$bZY@00$m9Fuk64a^L}$mP?#W`>_#+yYcSH;e9I29=-)_#RZb!FGwpFYc(Ok` zKE+zT-*LRyOczNP4hHE7|+)FL< zbYO@-W&rnzQHBiuN5KA;=AR(O1IG6e)BBZ|uo>hC{(TD8q^jC@FT5ETDNo?Z9XRY~yJs(o&Se^5|Ix2Gh z!!ysS+z%hOhu@F~5YTcEc8N_zTj)T842Ae#IM>@RH{)r2@376Lwy;SPlQot=UHNPA z(+>I%BcsNKRC=7ccEkBIu7}i*^$*x=PD^YY8JD_Ivu6y-W_Mo137Mt^bWTP+?==E^ zpK+e#)f@rB zcMQX<Xmo)H{ykmkZH_~s z_&hMifHl?oYw07u4$aW*D5|e#iCCdEV%QOw+t#E;i4@oH5l@Ut=z&r?z4~z)#Z8LY zYOk$OXfh69wE|C9F(kS== z?o%`flG4EQ+ts+5E=boiX;*+#@AschA*Dk|bx}1vNa@2q|G53=a6%iHW_kZW6et|G z4umrBr2IJ};yoG~+$hRZpE8!M$vu35aZ2#>?KN2R;}hwO*eq7gkk&X;MZ!v|CD^4; zpVu;hBbX2OAi|<2M^*~>)sF%`{0lW-{ zdf6p#xlen#;+XDL*UZsbC);nZ(s%%s*?N>4Vi(CgXXPu*vQ$^Ew*~<&7iKaAVvo)U zD?9(CX(h)fqsYHFLJ&v|%jNzY9@<`)tR{p4$k7MOH2)Z4)q5klT+d48%z&U%lTTyF z7I1)*FM+^J9o`(`!>yF^3l z0Gr9+tljgdPkTuK@-o!}jt+wr(l^QVdE`x|@W|W4ZL?>8j{F^8{SWwDuYdHA!J8hu zODB}Wl~l+o423)2Ta|7jD)9^g41j*BKI0gnAuu2Cq%do?EqL?FM&&D~4g)!5Z$_8n zT%TDK_CJt91Kdl~b(gL2i34k9u-8Ea`}gigj#>6$HbrbhKdn2lhu*%LQRhqkFfr`O zeuxBg{$SI_kOMiDY?-c3S3?^TfW-tzN30IA$wMHVeNvcCU`a~-<<|<_z-)Wb8!nAu zyh#ow43!WegjiWfkDMZixSM+U8F18pKF+a12Li!^muFLY0yFRl-4hDD0f{#C=%5^C zzd(?6mZw&N_uneqPeo@@h8qHceX71v#~$jXDvrF#9B`VxFk^y5CC|=(!^pgZQPRgG zEy@Ov!6jyYF%%Db?+=??eU)$vd~KO0>P@;R2!Y)&IVKK`_NCe_yBJomr~Lx7QxuGc zPdUQv*Aq^dXt%M>)z!Ok^AD;-4Po|p?)I8+G|-2s>W(9lR)1KK=|MRTvahw{lMmuzOVajX^9`6;a-r$9QbQ*)tsi%r#ss2(#qtD^#~Gt< zx#FGYT)96sFykS>16v}3{b%x1T|=9_HTSTf})~aaMh<9AdA013RFf}$Y3eE*SKu} z0wrs*Z~Ajw{}pdy)ChNuEznk_l*se*yLIjWdCM1|3L-9=^+>yU4lom@E_idH4)+k} z)S)nev9bh#mw3#b_@~yLK1~`Bei}lpIuv#Ufn~v7?{AV>>kyJYNGUjt;~T5A?Lbj9 z5`Pef4hh2YdLc4g{}QuZA6!q>jFaq7SY@&7byN^=6mQvU+RBlBND5RN3@fm0!!Z)* z7F~K!E6~z&<+nx6tCo#hfbr~PgT_cwod)pcHOQOZR|fvgW_F(sMfmkuX8;gQLS7NH zCrdPz_3{yyyv+O!vnu4u)1-bSHb_&mH?EQYK=4X^2+{Mr0T8KZaXy*re15OBkY~)z z`4O>I+C{N~FvgW#JR1#61kCr=RhA&()d`$0L*oG!)$;vRATj$b2R7K;+JEemnbofKIno0W| z);AwbBx_%KOir3p@D=A;Bpu(=&cq=-$^*dJTHVBrV z9~oqz_&GnR_`HwcEt_>Oh$Z+)5V#iT;3A^)a<`CgYBR~MGR?Kz4p6KtARxMFw-wrj z*hpT3Ik?5C`l2Pv?VzY=0YsDVDk1U4SoVQhWhMH_x1F074qvI0vOpLk!7bbvLatVU}%2^-= zzb0ihFZV3b6G0cNSe*Vc{8#7${Pl4ZmfiFbsVZVZi7;H$R#iKy_P@FU;0N<0wAgdN zLV+D&>GQwcvvLWd&ubR}wrz0+rD#B4}0FB-e8jXyX8qZjQ2yASSs0c{0ptbKoFO>WV?L0u>&@k#c|H zn3D_-Qh`9_SFSVi=uij2KAm}vUH)q_#R|AX`YYYw98hEk1Tw_;yqp8oFJE)5#t(uC zB!%_GEtc++P7tLFpZo{ckI$)01Rc`-U(*tqbDH-l>3X*)xTq@SYBhlc4|ty|d!uRAmm7NYdod&Q&b3d?zBM}<}|H;o4X>Ts!_ zsz>rt?)Om*8!V7C9P`{gYPVw5|^R8K#-VH0NQT)Cyd_lfb@$AjSPA7Tzj0T}NImfemEuR3A!C_x^Hm`e-0}~HZ0HBxl(Fj}dKuB8C3-VJm zv(iJ}%(lJ$!MJ1`u zuYHe;{+h6UIeiIwg6!9b{OQ>0ykEG)(QC62G)91fbceu+Be1K^6gW)KGd_$kNCa!2 zcJ{hUiSTIHJ^uc1-i!h3^&wjAD!H(mOs|cWh3j$(Nd(cQ@XVPMTP%^+RKY}j}H2YE6foNIGt0ky=dLw+V?We`?1f11>E9P`BLPGo?_;&DrwgAxQod5 zfRMIWB2ho9(ATt3pUZJM8?#O>PI~Z8V33rEJ0>Z#sye9u&mTnw&5>gP?W?dp!QcO< zvH7rD>w35csb#YrTR`J|-ANn@6bWE(HcQ|{1E4H!ybTqg*GB=f=izL7azW(lV;$z} zBn|ddS~yFX5JcVm`_k;<(bb3-C*MxP_UgVu(E<4)rg zD${*WY45H(iTn#mOuHS9?(R>@&8gYgs&lV(kV>)`6Z|Ii8HWr8nF)f*g5J?VrmGnb zpSpvyD%hD?r&mn5u%c2KgFWD$tq7i{#~EPFdZs7hi2*PPlV3(iNkln|7Lp##3+*A> zvb3_XFVVDkRNrklod!5>WFje7c;){N-~N4zG9Coma|h>ku?{b?927KXq;#S0 z_wUKMMMf60NCcu#2luH;ZegeY%7c#C6$$T~lz*#Tp}{PG zTwp7gUzO6nv&T$T>zoN2hj4s_iL;Oewr*4z%)V<2zzUQ=QrY7Q!=tY3&l2-*7MxP| zO5=+)30_|QPfx^6y*+0252NdyoL(*9bAw{pSnxvG0~GH;({BPg zE~lgnry(*FKjPoS7?@m7%ZG$yQ{rVdIV`^dg44~2C)?kPBh__N%b?$yw0rOiB?dn#xTuF#( zI?ed9CtnJh9SfiUm%dW;my`23B z2c(KR+{4()Qssmnx!fyvxKm;=h{1Rmzyny`HBLa0^Q7iSg@Af|@9A_!|2Me`^whZT zV=TSHan_dZX+^+__0BV4vL}}FM@R5KeCV%1r>ukvqpi|+<%^aFdcULqv7*%{p69Q~ z7NO3h(X_Lp19VT~5+x!v>JjWYdXJXmn8I%gj_7Rtvw4)#2?t#f`E$9BUVyg)fY=J- zXD)m-9`{;+Y=gNly&{}^zuoGi@?*kJ%1KFq}zp(Ht2FREo0NfXiR zkod_EvB8s)cSw_k)cQ@i?3e5>0NDLUsPRL!6DwW3kR%t9;T04h7+(F9g(x^Ne|pjUvjKtZ8&+ETYWt! z*2{$I6g`7LK9JcDNBe+U4qBLX^&<+%8X=!dyK@@bfE^EgsOz|XR=p_Alj`cD(SHs- zMGyRaA=YwUIJkdB{xH3L8=QRg?fU&qtBkEjwR;l{J;NeE`V6t6^pyymA}xNP*m(cxR=YfhVy$_Or`;dX|j%A8gBaNd~z6q#;OgoW0*=8{?gr=yz*Hqvr? zP0BOKXuc)s3b2^tD26?WWRS%Nd@Fx#N2kFBE)!k@8SVIpMqXi7;!W&9bC6xq zaBpUCK@Pr!tv82%A=Pq%HKV^^15PxHepFB&pk&iOeY<3&sF4UDxzB@ygCuab=&KkA zR}#bjqz73v+I~dz8@OV)aJJhWK$Iwdtx|5$$||fG3BqZ;2)A0p&!b(AzH@BFe4s1FYdCeiXuuG=T>K9 z*$8#11>($<#>1>2#zDKo7JdTAs7hc;KbV&PbDc0x($9{(yBfra73NKvSS0iM%t}t4 z)XS@VEPePJEc=Q2h$m31KxGi2D@IYwg$Gl5pw2EC2l2;1^?dMx-^2uWa`RI38_^5e zg8K-)=@(*^h3Rw-Pdc9xFFTe?gw)KN*#Id`)B@rkp4rsQH1O`sNDowU0V>le zVRC`7J7@15VnaaxQlHGMH*P0ztSRd~`yyRy+O<% z3q|G&LG=jJMu3$)0jZ%@g>5^ZU86dK7iwSovVk=&oSj}zk?5@GPF#l$^a zsNU-5T^|=Ch4^*J`OZ4!)$UjH5{mZ0J$LA=2EM;PEl-W44T@a`r%zrzVw=fm$$sPj zIk?45pMd(6U3yM)fS$>4bP{OBvENXkSg5QK7wU&*ye!lL5jn5&%a_K%$i8stbeSt` z;JoO4elqRkrZYnqfJnZTEpCqmC?6>`0%)w<-6K8R8?bk246F87b++|fB&92xF({$eL!V2}wike4tZ9@t}TCj1pX@MqGCW|0ADU;1uh z5Qo8)fr|glJJJic4#6t4bz+$mi0|%W5Jffmb`oe(oh*V1;(@WVzY2)1=C5)pf~0f= zpHU4KCVXJC^Hsz^i4JuDSYYEE&zBdM9QZ6JfF@E^&j=mhfitpjVs)tnBfEFxFO=y# z0^M)TWR^L$zjpzI&5hza>mQP?x4rZZQ1wW&e;g4Wi5*+A&zp3SUzzZZKfGa`}4aShb&l(-CMFZe@z8-3os?YTAFe@ zBTrChr7CeoptmsfllM{}!o@3fAxZDpg!lst>p_!oyt10~xrDlwid9%ocqp^ZckT?p zhE}3V$u}oHd8JUaRc=eor7>!G)(<3mOxnkZzQr=OzPuSTEt?H^d2gn~N3(TUj)+we z+D-54qby9hS8@;k3KA&s_jyo7t~f9n|C-zck z(?yRyDJGIVZkhTy829%+Htr7cImXKHWp0rB+P&TS{)T_w@f}VPpB`}7Ait%k!I-A_ z$GY{4*8hfz`b&C;9Wtlu^tx4apF0rWDdpKO+BFUl8RNfOIBVtStg8sWgOI+Lrk7J%cu+(+TCa&w zMGK?|SALMM^v{@lB+Ihk{7$}^Ui=Kd`>ay)AvLK8BYGbPW6n{rB@q~cs9@fGPNZhM zMiA7btT72Gab!&NY`miv1zt5Ajo156Z zB24N@9waEwLJs~@3N*2Tv9hxu-J2#YT-BW_N@He7;=cv`w7@Wi9PGk!)07@7S!5M} zrRU5>i7v^~{fY&Ly4?7T33*-srs@8_Qo>|cIggL>?bRDW^Wo5N_m-G(I0?kWOPq;k zYB3E?h?P;nN{Y+7J)}ueg)p0_{%v#Ot+{e!@pv0~KF|i%Sz9F62^tV^Z`4K0%Khyj z7N=}WH>O=pD!cCx`+FWG|H3{BaCQ;JqhG6L!Vc}S&&@%ee4=;2XxN*^p$9nU2z^6e~= zpj73srzy7p(*u=xWhCLlP!Bm*9;De(02F0a>XO<5@REj?5;K9(vf(4%8~xx8AYH3?XG8Qf?Rf+a)Od^<9zc5ATS+PeQ#K z5j*kG=lUEip$|7b#9#(ROd0_X{%&TZl8-^%u;Ao0w_{w+JR6smNNVS0Obs@HFS{>f z_A$mPa3(qeH;}2$UqNINAL4ZA-lo(L(WN8JRlT7^Cidx zT^u6j|Am&~phn0oDSWaMKz{L-Fj5~Xe{UAeZw14|lG~o8O6J5oBHba2?PyKmMgbxn zS9TB$TdNXlt~U6yt#c*E{wLt4mj|wZY))`x0i^zcEk?_~4F^=R5_bikaNH{7U&G-j zpUf((o;^>j2Oue?zPB-7^f;TTmi0UckJe7n*KchF0aupIK2}zO^=;Tl!62k228r03 zO5q$iWB4f{9 z_e+vluAtiCe$fFboiQuD%H_Ldt8K~9tT&6$&g&c}Wsu6$)zDMvWtI}vZ=ccR>WX_3(jq?QSzfM|zRM5}-DOi=%zLHVsO z^oF$ulj7)VNZxVZJ9>CTt}i{65|W=OtGtrkhQ=SeQsr$R6o9uBWyZt@(9zCnd8?VOoGr5+wWe*e;bB*1aIWz6uDM1uvee{6Y$`TzlDWWEt9~D%M$yNg1Oiv-_}Q4-c^? zSv`_*^81_p5#mPALO0llpod@fM7+`hKYm`bb%3z>j`-Kd+q04GEC`)O*o<%uTRN#&{9snZ0KBNpJm1^TTH*RbRIi zdFo1PJ9HHt3=zGXSdr9s?3`}Flu^gTLUy$<12{AM=9IUZLWc~H&TfKn9nnB-G+j$_ zJ)}`2beB3eG~2swYxfoh5q>kXf*!UYyUQ0kO(IpN2q+dmNIrR1&&fj};=QN?fl2g` zo9+^5BP|DeplyQYi9aVPO1dw@%eS1V+s3zQA@^4f9-f40_9jYsr%DYzV)GtLKAT&v zUrfHJl%~Evv(A+0KOJO9+H4i7h0JUoMJU zbj%Z~(qlz{mJKoN-4^`oFd!Zx^nf7u6!1Vce*Dqt`PsP+S)UVWGYdZ=`msh^0ddvn zf{GUS%7tg^*Bxt-N0mRS*?J}|4ovi0e3LS9xSluXcw?ukboSSM2vv@iq?2Qxalvg+#&UdEp(F+`~4T+s$FyTe{8dh3cwMQFsMREH@lBg3jH%6aFw!;eTfmk z{1NqILu`fHBv_HKzHx2LbeI04Da4TN!m=l(VzF6K2;1J zl?KTS5HjxT(|c5yODv#j_Tk%)st8;o2<64l)73Ve7UBxutHx!tbo?YQtNNOsxIgbS zkK^|kq>w+AYW=?l?vq@5Baq%x@M(WSa9!O4Z zUk|?Bf{HfAJ90-`ltEUhElHh^x<&w zH@{P;E{YPD@=>0-F>ulNEet+{eyS^YA>Lq00!Z#JM)S1qL=O*t^LN+Jm-FGhChTrRN^uw%aVe(uXCtco)af2Tu-Oo`C%D1 z0JvQW$xHn#h81Kx3cj0Qpb4f+TYB4pV%G-e(j@UcuTbYG3ra1wX_8x4H-n<_LAJc` z*QVDZ(h3@bME95;JGZm&s@qu%SJvr&pwY49vk&fi8d*@L zD&@X;Zn_Tki;CXqY-qL5PGUTv`-O}a^FsYDhOgNl`S-3nZK#8{Uivr9pSCTWHow56 zt7v#qQ?d_9x;f3zFt<+gr;T2EL!Mpe?pQB+fn>iHjj=DOP&cohkN_U~FS=HNF!pON zR9Ge)ZDQ0rLt>IrMTq0T%dyRHWPP~wGSYMst75u^Gz_Ptle@H-dy=^ZR*GLwnOV%p zxqRkdybUuN0ebpyCm^|JhAOaj-O{ZDEUqn_QN<#OUA0(_00i|)9 z3DfC^_s33c1xpd2Fj<1pi*?pz=VXb5GPR_$xdJR2OgECwBiYtw`tn zrhsRHvn6970~_iWWMrJ$6^z5~Ok`C^?3%}COxhIa16fqc`9P$f$lS78;AcB_npb(V z23D9sQmLZvug&+hrAw1DjmB>(G0NwaSTSbZZbnflRd#fm{fNW5?8o|KK0Vq{RDV%n zC*G7VoU<@~PSGD|7>lX@_ABHSmSj;LP6!EGi0RwaX|Ng9yV&_al8u5&>bY({lZOya zEQgk=s3h$Fq&|LvmpvJM%!}GZN{r0uH_MR%9r41fF&WNV-?C>-M5`XO6fbViz3%YH zqCTr`Iq-V)c;H=XjKqrsZxot-ZaihR4@08ItXoeH{-t_SQU1E+RD^A}1UqfKo8GC- zH`~0W2rbov;Yd^(Q#+2XGaP0uLtCxMS0z=Ubq5*%j-I(|>hSq3If=3aw#IKcy`?`( zPBg&G)5N3m4v|${LvIbOwv*h{Y(!+nlz$X9tJG?+jxqjubSeR!<)7aJ@rr_k2l~Z^ zu&3DLGmIkb3Aq-lD!Z{=~13=`L|e2!Ow zHunmN;OdBN-hFrx+E@^q%c41MSC&HJlX7+4AtA|xxPl!YS@$N#qAv(Zni(lz1u0*L zkJE_f7b0%u3Ipvltn#j!#o1@Le@^gaF`3UIab1wnKrCi3Z>oSA>@4Yb<)ScEBAS2R zBPh94e#{;_EsM;Gzh5s1%0&my0nrMP#ov;57}5Coo+v6GNI* zbj)4+GIY>IOlZQ{g0Q?Pf4427IoSt|)&sMq+!=?XC&STqY`FSYc#XI>8uK2|s?*w^ z?*`xF6#^FW*Rn!#(t7(W^*BYncZn3&V1 zOr_?0vx`<)JtfvWmCFGNUwj>#c9i|y_9B!NF2TIL%RS^UtVFGa?{}@vXnI5&%UQ&k z3AQj+CRdEFe@`%_f<0yTZ_4a1xJkmJ2P|{8@OP(;iOvUY3EBtW(aeVM{PJKDsgPtOCy$1?HAcDePKtmxcHHu!S@f+9$tn&Cz2`oj@JRXx{KVGCvFeSaruR#dxyq50vH(^iDY)SddBcB@=EDUbf;51+gSggk?tzM!v;@Vv|RXk7ee z#R>t%=4cjimBl_^vF%pi2utodSg6yxYu8Low{qyx*1ZIy%+F42p^z#xLF1|NmArYi1glW zPLO`G87`&o{^}*`=A{ydC6;>h)D~P@Mhhrml0$+BE(jx^{A(B$cwiAtFR4-R_Xbpf zyQ(KV$b%u{fgs&ofHH(9GY92HSM>ImnMvSI!Hr17t|}$y9!H6?XI*>3Z302j{_7q- zwg!n&OTMz=H0IV?w*~T&SY_$2j-gA==$0qmZ-#1{1CJ}E^Q4Od%~MC^6pJ^XGE3K6 zGX-Z9Xo>}1vw?=uC@y!n=K)bpw*%?AW5YMv!55LeW54@PS6sE+pNH)%1-OTlNCl{g z=s^M{vtfB=xvCJ3nd7QVqjeE`<&Dmowq%VcWNTFT1d{%d7hKu2qr91uQ-5OK=&a6) z`PDXabc;S};53n_~wf9gEpr)UnDPb+_E%F3)CocSQt&e+4k@uo>jL_B2$#XRHUiCOqn~QZI1e|ujx6N zW}9(RLBjE|8HcgPukyuyr$+}+Fu>_SKmO*}{&)TSMJ9Rs%cUnAZK`ak@F+#3zTX!^ zhk{mq>-MB*%##;olg=ckCTvGTi%^T=&cisO=g#;!s;wXwr$6d(aI4&)fpl_<6 zgQxs*kOcwy_%Hkw90JYKjg_K|f1*WN;ssJQ$JFIg5>>`|h=OyY@Va9M0Znw^KUBPb z7_a|&>okMnfw~uG#7}t&wU9Kt=(x{4I|VzlCWbc~IW1p*_!cu`4ra^JBWlG)Je3@2fs0$G_JNxV(W=v%R`_WzEi8{A5|15uOm zb>`=ebR^W(l^6wVnheGbpC#L01IZL`wr%3!;CoJ?< z4O~G7-okxcR2cv{JnC4AiFZR#m=ybxtl-k9ySqfth}$OV+1aB*+qLHiSx5uL+qA08 zLPY0LcqBZ!>#Eb1+El}2PNsvPVCfJ~o3Ug=zeHkvD*hvJ%7?+I3F*s}cXQo3{8B8r z=N+cc?*e8kjG0Z}k4nP`*!%Qq!BA&r<3_C{cjVh2XcB>+USn1ldeCb&s9F|;5%L4_ z+RvP;A6~wjNM}vox!gaP)oN0<+~h89^2~Vx_MiTRY?1!iv3dBq3pIrX&KMMtqG(mJ zc9c^JD@zNyahbxF_qBe|86-q1JJg@CZ<+Q3`~uA;|H4xFMC?!Q$_O!{2d!sif){5I z2b6@|N&P+?&GIeb#zzK4WtxuLj^k)ByMK3~)A8^%%>HIZ8fxBz73nM*!STBdT!K5W z3ESriN=Q$`4RT8DNvg14dbLA{-fw93BFw;1GKg@>HV5LR2SkT2bj@`HTxC});;bSG z#1XNgNGzmW?LJcIhe`Yk)K~o$zPyTl{wElICa*!(o&>s00H5L;5PS*!bn1;=xh4A( zH74exk{9_Wy=9M&`~oia5j3BQRVlyg>AN|u_T;2cV8q0OsF-)(W#d(DBTzMh2Ryb@ zL{XL4)T3Nu%S-Us#dGYR+CEroTBr1HCPl|rIZFm||U@1L8yqk`}T zZSWw0ERdI`LYoPnliU!3&uiny6=|oO$Aw8RAIKBbHY?wXNhplbaucQvlfMmd;Fjp) zb4$KFp#4iAu3=VWng#V|#E~7`5K*m_)TGxTG>m*p9AYXSsH|7HVz1_GLEU>AdHYZ^ zE|gwXEA=K31%GR}eP3~Q1xJ^|HzI20g!R*H`|($lmpXypos(EFWJRmRARehk%N16f znq1?&#Mq|oGGD7ItB;d>M*<~gu$97zTy}5i1#mpcE^{Zkx>g5`zW+d~R(L4fcV(mR zMvvmbLC9X9Q6rY=+G?!ipfOudrDo3>NAKp7$`xZ_=mpuCLCi0m06bR|x0P|aO2 z)CnU|n<|oLI5jQfHzcQW-x;2?;51!Bf2!&+61ldUTblL<3#00BL=wf2^GlU7Or6L% zy@s?##)RXSRL~EMBK4%RQBme2fZi4-Ow=&bY_1DEiO@A>&KR%$3vCTs;8JG3N{C#{ zKXD}CkGTRAbf6L{$5T`yYmC&f`&S-|YT4+N>#cjF#(c!h4N&^~8UtL&afRV+m2_5l zCagTCRSt>!z6Pf4k$J-1*n%^1%rQKd<=1Dw2Z&S4)sF?zSQ^#9818Wmwj_<8NB>W4 z7*M8egWKbs?>>`7f#%{gXHh@Yeg-2l?o^5KDNb>l{G}H-Omey7v#h+!INn@=JC(?e z%oe_>(s1DS=YHBPI?AZK`BmEy_s5A=I-|CG2asJorEL;DJ5M|bo8tRZ#Bm(nGdh!t z(C3igtJAKOg8wt=`Msf-a8m) zAR`+egc`jWvOOejGup3klp3wKXuFk4-G}CVal)uRf4j!$NG^Z=;J+fULOLFb zgSHNw8|VyJMOF!!y-JVpq4kA}jSN|S`b_`aT~A?6ej60Le~Fnx%EP6z^O-Xt;HRJ= zwG(SKDd6b#49QGvYpECZc95%T{J7ePm5Y>w{fQ2ftGfI#&_1ISBwCTA^@h4Mq)Rm4 zn`Idp`-I+jrf>Ai@FJFh5~5_o#E}-0+)}64tB`4uCR8;kYz>xUYd5-FLaKOk$OJku zS{H@@s%JLBrgZJY{x--SifzGggcpfJF#RKVaw$T=g8(<0B}P%@RjV3gX!%1srw4-R z+l41FbI11syp!&40W26E09T^v;u{;Kqc~_xOL0uUjW{d}M@G2o_B1iRLI1gI&^n@y zeHYO}VOMcA8G)A=(sfa&!{lG)-F;FO#dGx2ZJb)ozL-mp5^CUAC_XhCQ5a?9Xkvp*R*Fal;l@TT-g~inaAt&+D3I zepfO;+Eh-Kv_AYEv}fwa`=drH!%nE|^H&QgbvUo*T|&y_g~1vMm^1>@)v7=if?$b) zk)66y_a(H1%{DVcD|YlIK|Aifo@Nl;!N>8Te&gv( z@i!v2(D-mn!vQXD$TmUAkOLX~v;gynSMCFOl+SOh`F6j}HZz>GEN7;YdpE-f;Zet< z{GSBD*nizoR5`<__MNyjhPXBQxMdA2ae_HP`egk-v%{93ugUvf5eu0y@3ry?;kc7_ zi>cXSGgmp>TQ^9zyKsd>_j3%$V34XvaD}N&T6k}7g<%c8o5>H6lffehf#X8EJyBC{ zcbU@Em(Tvk41s8)ksf_TIXz24M!?0v$brp9DIf(RZL&e0Hw7sNM>kOg;5Qv00q4;- zCJ3C**)`o>+1!9J^wDGKlLMNHc6!C$fBPa)aHyJQgbpt<%}p|$9?Rn{+8{A$kb(@x zf~gk?b*jnV+7s0Y--M|R2^##w1(GNfLbSw4-J3DBADPGAxMr+03(mB_vIfq^k9dRz zq7m-D-_8G(Ff3OM4ciJ!J2=cLUtEwsl{dLJS zLM%KwZp-^?1yZWGObBbp->|sk)9A*#x!H8d~=gC%7$cJ?NGA zzzNyH-HRJv&Efo^U$(yO8R_O;NTv7KirkaEA4X}}F`MviZvgeB*xfAA9>*hs%Pyq@AtUI{M0ZViG z`G=N>nzYr|OKEs|wFhUBStFPxnN6=BdJ2uikuusk`+AZQ2hGC*%kgxY`~cqKwZ&dp zkONlLw9k>}ECLnezf3@uK|in<{)hV7&%0DO5m`)=tCq8$H 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

{t?C2^llY4Z&b-~i||LMz+ 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 D;Iqw3 literal 0 HcmV?d00001 diff --git a/HEAD/folderclosed.svg b/HEAD/folderclosed.svg new file mode 100644 index 00000000..b04bed2e --- /dev/null +++ b/HEAD/folderclosed.svg @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/HEAD/folderclosedd.svg b/HEAD/folderclosedd.svg new file mode 100644 index 00000000..52f0166a --- /dev/null +++ b/HEAD/folderclosedd.svg @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/HEAD/folderopen.svg b/HEAD/folderopen.svg new file mode 100644 index 00000000..f6896dd2 --- /dev/null +++ b/HEAD/folderopen.svg @@ -0,0 +1,17 @@ + + + + + + + + + + diff --git a/HEAD/folderopend.svg b/HEAD/folderopend.svg new file mode 100644 index 00000000..2d1f06e7 --- /dev/null +++ b/HEAD/folderopend.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/HEAD/functions.html b/HEAD/functions.html new file mode 100644 index 00000000..a216177b --- /dev/null +++ b/HEAD/functions.html @@ -0,0 +1,114 @@ + + + + + + + + +LibJWT: Data Fields + + + + + + + + + + + + + + + + + + + + + + + +

+
+ + + + + + + +
+
LibJWT 2.9.99 +
+
The C JSON Web Token Library +JWK +JWKS
+
+
+ + + +
+
+ +
+
+
+ +
+
+
Here is a list of all struct and union fields with links to the structures/unions they belong to:
+
+
+ + + + + diff --git a/HEAD/functions_vars.html b/HEAD/functions_vars.html new file mode 100644 index 00000000..b20996ad --- /dev/null +++ b/HEAD/functions_vars.html @@ -0,0 +1,114 @@ + + + + + + + + +LibJWT: Data Fields - Variables + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + +
+
LibJWT 2.9.99 +
+
The C JSON Web Token Library +JWK +JWKS
+
+
+ + + +
+
+ +
+
+
+ +
+
+
Here is a list of all variables with links to the structures/unions they belong to:
+
+
+ + + + + diff --git a/HEAD/graph_legend.html b/HEAD/graph_legend.html new file mode 100644 index 00000000..6ed6e310 --- /dev/null +++ b/HEAD/graph_legend.html @@ -0,0 +1,156 @@ + + + + + + + + +LibJWT: Graph Legend + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + +
+
LibJWT 2.9.99 +
+
The C JSON Web Token Library +JWK +JWKS
+
+
+ + + +
+
+ +
+
+
+ +
+
+
Graph Legend
+
+
+

This page explains how to interpret the graphs that are generated by doxygen.

+

Consider the following example:

/*! Invisible class because of truncation */
+
class Invisible { };
+
+
/*! Truncated class, inheritance relation is hidden */
+
class Truncated : public Invisible { };
+
+
/* Class not documented with doxygen comments */
+
class Undocumented { };
+
+
/*! Class that is inherited using public inheritance */
+
class PublicBase : public Truncated { };
+
+
/*! A template class */
+
template<class T> class Templ { };
+
+
/*! Class that is inherited using protected inheritance */
+
class ProtectedBase { };
+
+
/*! Class that is inherited using private inheritance */
+
class PrivateBase { };
+
+
/*! Class that is used by the Inherited class */
+
class Used { };
+
+
/*! Super class that inherits a number of other classes */
+
class Inherited : public PublicBase,
+
protected ProtectedBase,
+
private PrivateBase,
+
public Undocumented,
+
public Templ<int>
+
{
+
private:
+
Used *m_usedClass;
+
};
+

This will result in the following graph:

+

The boxes in the above graph have the following meaning:

+
    +
  • +A filled gray box represents the struct or class for which the graph is generated.
  • +
  • +A box with a black border denotes a documented struct or class.
  • +
  • +A box with a gray border denotes an undocumented struct or class.
  • +
  • +A box with a red border denotes a documented struct or class forwhich not all inheritance/containment relations are shown. A graph is truncated if it does not fit within the specified boundaries.
  • +
+

The arrows have the following meaning:

+
    +
  • +A blue arrow is used to visualize a public inheritance relation between two classes.
  • +
  • +A dark green arrow is used for protected inheritance.
  • +
  • +A dark red arrow is used for private inheritance.
  • +
  • +A purple dashed arrow is used if a class is contained or used by another class. The arrow is labeled with the variable(s) through which the pointed class or struct is accessible.
  • +
  • +A yellow dashed arrow denotes a relation between a template instance and the template class it was instantiated from. The arrow is labeled with the template parameters of the instance.
  • +
+
+
+ + + + + diff --git a/HEAD/graph_legend.md5 b/HEAD/graph_legend.md5 new file mode 100644 index 00000000..34a71d61 --- /dev/null +++ b/HEAD/graph_legend.md5 @@ -0,0 +1 @@ +238bc3d95adc1929b3259d0c39010ed6 \ No newline at end of file diff --git a/HEAD/graph_legend.svg b/HEAD/graph_legend.svg new file mode 100644 index 00000000..ccfbf29d --- /dev/null +++ b/HEAD/graph_legend.svg @@ -0,0 +1,167 @@ + + + + + + +Graph Legend + + +Node9 + + +Inherited + + + + + +Node10 + + +PublicBase + + + + + +Node10->Node9 + + + + + + + + +Node11 + + +Truncated + + + + + +Node11->Node10 + + + + + + + + +Node13 + + +ProtectedBase + + + + + +Node13->Node9 + + + + + + + + +Node14 + + +PrivateBase + + + + + +Node14->Node9 + + + + + + + + +Node15 + + +Undocumented + + + + + +Node15->Node9 + + + + + + + + +Node16 + + +Templ< int > + + + + + +Node16->Node9 + + + + + + + + +Node17 + + +Templ< T > + + + + + +Node17->Node16 + + + + + +< int > + + + +Node18 + + +Used + + + + + +Node18->Node9 + + + + + +m_usedClass + + + diff --git a/HEAD/group__jwks__core__grp.html b/HEAD/group__jwks__core__grp.html new file mode 100644 index 00000000..682ef7b7 --- /dev/null +++ b/HEAD/group__jwks__core__grp.html @@ -0,0 +1,542 @@ + + + + + + + + +LibJWT: JSON Web Key and Sets + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + +
+
LibJWT 2.9.99 +
+
The C JSON Web Token Library +JWK +JWKS
+
+
+ + + +
+
+ +
+
+
+ +
+
+ +
JSON Web Key and Sets
+
+
+ + + + + +

+Data Structures

struct  jwk_item_t
 Structural representation of a JWK. More...
 
+ + + + +

+Typedefs

typedef struct jwk_set jwk_set_t
 Opaque JWKS object.
 
+ + + + + + + + + + +

+Enumerations

enum  jwk_key_type_t {
+  JWK_KEY_TYPE_NONE = 0 +, JWK_KEY_TYPE_EC +, JWK_KEY_TYPE_RSA +, JWK_KEY_TYPE_OKP +,
+  JWK_KEY_TYPE_OCT +
+ }
 JWK Key Types. More...
 
enum  jwk_pub_key_use_t { JWK_PUB_KEY_USE_NONE = 0 +, JWK_PUB_KEY_USE_SIG +, JWK_PUB_KEY_USE_ENC + }
 Usage types for JWK public keys. More...
 
enum  jwk_key_op_t {
+  JWK_KEY_OP_NONE = 0x0000 +, JWK_KEY_OP_SIGN = 0x0001 +, JWK_KEY_OP_VERIFY = 0x0002 +, JWK_KEY_OP_ENCRYPT = 0x0004 +,
+  JWK_KEY_OP_DECRYPT = 0x0008 +, JWK_KEY_OP_WRAP = 0x0010 +, JWK_KEY_OP_UNWRAP = 0x0020 +, JWK_KEY_OP_DERIVE_KEY = 0x0040 +,
+  JWK_KEY_OP_DERIVE_BITS = 0x0080 +, JWK_KEY_OP_INVALID = 0xffff +
+ }
 Allowed key operations for JWK private keys. More...
 
+ + + + + + + + + + + + + + + + + + + +

+Functions

jwk_set_tjwks_create (const char *jwk_json_str)
 
int jwks_item_add (jwk_set_t *jwk_set, jwk_item_t *item)
 
int jwks_error (jwk_set_t *jwk_set)
 
int jwks_error_any (jwk_set_t *jwk_set)
 
const char * jwks_error_msg (jwk_set_t *jwk_set)
 
jwk_item_tjwks_item_get (jwk_set_t *jwk_set, size_t index)
 
void jwks_free (jwk_set_t *jwk_set)
 
int jwks_item_free (jwk_set_t *jwk_set, size_t index)
 
int jwks_item_free_all (jwk_set_t *jwk_set)
 
+

Detailed Description

+

Functions to handle JSON that represents JWK and JWKS for use in validating JWT objects.

+

Typedef Documentation

+ +

◆ jwk_set_t

+ +
+
+ + + + +
typedef struct jwk_set jwk_set_t
+
+ +

Opaque JWKS object.

+

Used for working with JSON Web Keys and JWK Sets (JWKS).

+
Remarks
All JWK operations require that you import your JWK into a jwk_set_t first. Internal, LibJWT creates a jwk_set_t even for single keys. This makes code pretty much the same whether working with one JWK or a set of them.
+ +
+
+

Enumeration Type Documentation

+ +

◆ jwk_key_op_t

+ +
+
+ + + + +
enum jwk_key_op_t
+
+ +

Allowed key operations for JWK private keys.

+

A JWK can support one or more of these bitwise flag operations. The names correspond with the RFC.

+
+
...
+
}
+
@ JWK_KEY_OP_ENCRYPT
Definition jwt.h:158
+
@ JWK_KEY_OP_SIGN
Definition jwt.h:156
+
Structural representation of a JWK.
Definition jwt.h:182
+
jwk_key_op_t key_ops
Definition jwt.h:199
+

📄 RFC-7517 Sec 4.3

+ + + + + + + + + + + +
Enumerator
JWK_KEY_OP_NONE 

No key_op set

+
JWK_KEY_OP_SIGN 

Signing

+
JWK_KEY_OP_VERIFY 

Signature verification

+
JWK_KEY_OP_ENCRYPT 

Used for encryption

+
JWK_KEY_OP_DECRYPT 

Used for decrypting

+
JWK_KEY_OP_WRAP 

For wrapping other keys

+
JWK_KEY_OP_UNWRAP 

For unwrappng other keys

+
JWK_KEY_OP_DERIVE_KEY 

Key derivation

+
JWK_KEY_OP_DERIVE_BITS 

Bits derivation

+
JWK_KEY_OP_INVALID 

Invalid key_ops in JWK

+
+ +
+
+ +

◆ jwk_key_type_t

+ +
+
+ + + + +
enum jwk_key_type_t
+
+ +

JWK Key Types.

+

Corresponds to the "kty" attribute of the JWK.

+

📄 RFC-7517 Sec 4.1 @rfc(7518,6.1}

+ + + + + + +
Enumerator
JWK_KEY_TYPE_NONE 

Unused on valid keys

+
JWK_KEY_TYPE_EC 

Eliptic Curve keys

+
JWK_KEY_TYPE_RSA 

RSA keys (RSA and RSA-PSS)

+
JWK_KEY_TYPE_OKP 

Octet Key Pair (e.g. EdDSA)

+
JWK_KEY_TYPE_OCT 

Octet sequence (e.g. HS256)

+
+ +
+
+ +

◆ jwk_pub_key_use_t

+ +
+
+ + + + +
enum jwk_pub_key_use_t
+
+ +

Usage types for JWK public keys.

+

Corresponds to the "use" attribute in a valid JWK.

+

📄 RFC-7517 Sec 4.2

+ + + + +
Enumerator
JWK_PUB_KEY_USE_NONE 

No usable attribute was set

+
JWK_PUB_KEY_USE_SIG 

Signature validation (JWS)

+
JWK_PUB_KEY_USE_ENC 

Decryption key (JWE)

+
+ +
+
+

Function Documentation

+ +

◆ jwks_create()

+ +
+
+ + + + + + + +
jwk_set_t * jwks_create (const char * jwk_json_str)
+
+

Create a new JWKS object for later use in validating JWTs.

+

This function expects a JSON string either as a single object for one JWK or as an array of objects under a key of "keys" (as defined in JWKS specifications).

+

If non-NULL is returned, you should then check to make sure there is no error with jwks_error(). There may be errors on individual JWK items in the set. You can check if there are any with jwks_error_any().

+
Parameters
+ + +
jwk_json_strJSON string representation of a single key or array of "keys". If NULL is passed, an empty jwk_set_t is created.
+
+
+
Returns
A valid jwt_set_t on success. On failure, either NULL or a jwt_set_t with error set. NULL generally means ENOMEM.
+ +
+
+ +

◆ jwks_error()

+ +
+
+ + + + + + + +
int jwks_error (jwk_set_t * jwk_set)
+
+

Check if there is an error within the jwk_set

+

To get a string describing the error, use jwks_error_str.

+
Parameters
+ + +
jwk_setAn existing jwk_set_t
+
+
+
Returns
0 if no error exists, 1 if it does exists.
+ +
+
+ +

◆ jwks_error_any()

+ +
+
+ + + + + + + +
int jwks_error_any (jwk_set_t * jwk_set)
+
+

Check if there is an error within the jwk_set and any of the jwk_item_t in the set.

+
Parameters
+ + +
jwk_setAn existing jwk_set_t
+
+
+
Returns
0 if no error exists, 1 if any exists.
+ +
+
+ +

◆ jwks_error_msg()

+ +
+
+ + + + + + + +
const char * jwks_error_msg (jwk_set_t * jwk_set)
+
+

Retrieve an error message from a jwk_set. Note, a zero length string is valid if jwos_error() returns non-zero.

+
Parameters
+ + +
jwk_setAn existing jwk_set_t
+
+
+
Returns
NULL on error, valid string otherwise
+ +
+
+ +

◆ jwks_free()

+ +
+
+ + + + + + + +
void jwks_free (jwk_set_t * jwk_set)
+
+

Free all memory associated with a jwt_set_t, including any jwk_item_t in the set

+
Parameters
+ + +
jwk_setAn existing jwk_set_t
+
+
+ +
+
+ +

◆ jwks_item_add()

+ +
+
+ + + + + + + + + + + +
int jwks_item_add (jwk_set_t * jwk_set,
jwk_item_t * item )
+
+

Add a jwk_item_t to an existing jwk_set_t

+
Parameters
+ + + +
jwk_setAn existing jwk_set_t
itemA JWK item to add to the set
+
+
+
Returns
0 on success, valid errno otherwise.
+ +
+
+ +

◆ jwks_item_free()

+ +
+
+ + + + + + + + + + + +
int jwks_item_free (jwk_set_t * jwk_set,
size_t index )
+
+

Free all memory associated with the nth jwk_item_t in a jwk_set

+
Parameters
+ + + +
jwk_setA JWKS object
indexthe position of the item in the index
+
+
+
Returns
0 if no item was was deleted (found), 1 if it was
+ +
+
+ +

◆ jwks_item_free_all()

+ +
+
+ + + + + + + +
int jwks_item_free_all (jwk_set_t * jwk_set)
+
+

Free all memory associated with all jwk_item_t in a jwk_set_t. The jwk_set_t becomes an empty set.

+
Parameters
+ + +
jwk_setA JWKS object
+
+
+
Returns
The numbner of items deleted
+ +
+
+ +

◆ jwks_item_get()

+ +
+
+ + + + + + + + + + + +
jwk_item_t * jwks_item_get (jwk_set_t * jwk_set,
size_t index )
+
+

Return the index'th jwk_item in the jwk_set

+

Allows you to obtain the raw jwk_item. NOTE, this is not a copy of the item, so any changes to it will be reflected to it in the jwk_set. This also means if the jwk_set is freed, then this data is freed and cannot be used.

+
Parameters
+ + + +
jwk_setAn existing jwk_set_t
indexIndex of the jwk_set
+
+
+
Returns
0 if no error exists, 1 if it does exists.
+
Remarks
It's also worth pointing out that the index of a specific jwk_item in a jwk_set can and will change if items are added or removed. from the jwk_set.
+ +
+
+
+
+ + + + + diff --git a/HEAD/group__jwks__core__grp.js b/HEAD/group__jwks__core__grp.js new file mode 100644 index 00000000..68d6ed85 --- /dev/null +++ b/HEAD/group__jwks__core__grp.js @@ -0,0 +1,56 @@ +var group__jwks__core__grp = +[ + [ "jwk_item_t", "structjwk__item__t.html", [ + [ "alg", "structjwk__item__t.html#aae5092f40a4bfbe9155ddde65f5b933f", null ], + [ "bits", "structjwk__item__t.html#a2a87bc6fcdbdbd234f1beee3ba63ea61", null ], + [ "curve", "structjwk__item__t.html#a3840bd4c87181411e0f3012c020787b3", null ], + [ "error", "structjwk__item__t.html#ae5aef5762e38fc66625cedc461a32ece", null ], + [ "error_msg", "structjwk__item__t.html#a2840975767dbdc020912682c06ebdb75", null ], + [ "is_private_key", "structjwk__item__t.html#a58ab0832602b475e7c3a7009925db87e", null ], + [ "json", "structjwk__item__t.html#a3593e9e89e76e6e8b605f01a1a8a2367", null ], + [ "key", "structjwk__item__t.html#ab4f6c1d6586f6b8a63349b2f014f1f7c", null ], + [ "key_ops", "structjwk__item__t.html#a44fe44eb2d9c9466c48f19cd5f590e21", null ], + [ "kid", "structjwk__item__t.html#a874da82b485b8c541ecd0f3f8fd12a0f", null ], + [ "kty", "structjwk__item__t.html#ae2d7fed09d97d0ac3e6571e7c897d580", null ], + [ "len", "structjwk__item__t.html#a379d62be0803bca022445adb82cd99e5", null ], + [ "oct", "structjwk__item__t.html#ae03055df75f8a3940fb071d5bbcc2379", null ], + [ "pem", "structjwk__item__t.html#afecd2ad6ce52bf49a202999c184a9b2e", null ], + [ "provider", "structjwk__item__t.html#a892ffdca6722b35e24d885f01edd6679", null ], + [ "provider_data", "structjwk__item__t.html#a6cf519323148008f4549798d7df1d8f3", null ], + [ "use", "structjwk__item__t.html#a8790b92f608e12757ba07dff49b931df", null ] + ] ], + [ "jwk_set_t", "group__jwks__core__grp.html#ga380f2d92f1d0d306420729f9789f5e50", null ], + [ "jwk_key_op_t", "group__jwks__core__grp.html#ga150285e933a3d456bcd8d87b906438a3", [ + [ "JWK_KEY_OP_NONE", "group__jwks__core__grp.html#gga150285e933a3d456bcd8d87b906438a3a9054426563814eff0f4ea23def0e00f9", null ], + [ "JWK_KEY_OP_SIGN", "group__jwks__core__grp.html#gga150285e933a3d456bcd8d87b906438a3a893a71fda7261466815427686f349eef", null ], + [ "JWK_KEY_OP_VERIFY", "group__jwks__core__grp.html#gga150285e933a3d456bcd8d87b906438a3a76ea53a146dac84cffa46681bbc7fc2e", null ], + [ "JWK_KEY_OP_ENCRYPT", "group__jwks__core__grp.html#gga150285e933a3d456bcd8d87b906438a3a1101574110cba3dd17ff829d728ceb5b", null ], + [ "JWK_KEY_OP_DECRYPT", "group__jwks__core__grp.html#gga150285e933a3d456bcd8d87b906438a3a9a4c0f1119d858a8bda1f7cecfb98d79", null ], + [ "JWK_KEY_OP_WRAP", "group__jwks__core__grp.html#gga150285e933a3d456bcd8d87b906438a3a451f04d391cf27e7938c6b29324272c8", null ], + [ "JWK_KEY_OP_UNWRAP", "group__jwks__core__grp.html#gga150285e933a3d456bcd8d87b906438a3a56462a753babcb3a1593a5436d677667", null ], + [ "JWK_KEY_OP_DERIVE_KEY", "group__jwks__core__grp.html#gga150285e933a3d456bcd8d87b906438a3adb8b6296fb02fd5e961d89310edf7ce6", null ], + [ "JWK_KEY_OP_DERIVE_BITS", "group__jwks__core__grp.html#gga150285e933a3d456bcd8d87b906438a3a073d4ad92c0276dbc1ecdc874231ce39", null ], + [ "JWK_KEY_OP_INVALID", "group__jwks__core__grp.html#gga150285e933a3d456bcd8d87b906438a3aeb336579eb3c3c3e6f563b2a36d1511f", null ] + ] ], + [ "jwk_key_type_t", "group__jwks__core__grp.html#ga6ee53248054c67e9585aae9e937890f2", [ + [ "JWK_KEY_TYPE_NONE", "group__jwks__core__grp.html#gga6ee53248054c67e9585aae9e937890f2a91865197de901ef236c5570a7c6897d9", null ], + [ "JWK_KEY_TYPE_EC", "group__jwks__core__grp.html#gga6ee53248054c67e9585aae9e937890f2ad7065b821b8699d3b42fe0c3263dd3b1", null ], + [ "JWK_KEY_TYPE_RSA", "group__jwks__core__grp.html#gga6ee53248054c67e9585aae9e937890f2af1a4bf01f937df96d195572180f036d8", null ], + [ "JWK_KEY_TYPE_OKP", "group__jwks__core__grp.html#gga6ee53248054c67e9585aae9e937890f2ab42a328b8fc31e10c3f5e38e953c684c", null ], + [ "JWK_KEY_TYPE_OCT", "group__jwks__core__grp.html#gga6ee53248054c67e9585aae9e937890f2af2312e76e2727031b0641a9f21405b3c", null ] + ] ], + [ "jwk_pub_key_use_t", "group__jwks__core__grp.html#gab30049eeae2a6ecdc0c9ae849b835b6b", [ + [ "JWK_PUB_KEY_USE_NONE", "group__jwks__core__grp.html#ggab30049eeae2a6ecdc0c9ae849b835b6baf3e36efeb7307384fa7f7dbee089573b", null ], + [ "JWK_PUB_KEY_USE_SIG", "group__jwks__core__grp.html#ggab30049eeae2a6ecdc0c9ae849b835b6ba5c6c707d72a666971135ad6bec551b99", null ], + [ "JWK_PUB_KEY_USE_ENC", "group__jwks__core__grp.html#ggab30049eeae2a6ecdc0c9ae849b835b6baefac5505199173f9c52530be55a54408", null ] + ] ], + [ "jwks_create", "group__jwks__core__grp.html#ga61852f3afd280f50a02171fee05b5b8e", null ], + [ "jwks_error", "group__jwks__core__grp.html#ga2d50f634f1b9d73de31ee251fcf50210", null ], + [ "jwks_error_any", "group__jwks__core__grp.html#gac3b6bdd5540c8aa1390705e09db6b655", null ], + [ "jwks_error_msg", "group__jwks__core__grp.html#ga7500b0576723039ce30b09a2e1387d6a", null ], + [ "jwks_free", "group__jwks__core__grp.html#gadd20e30cdcd0c0d5b794547d41e8252f", null ], + [ "jwks_item_add", "group__jwks__core__grp.html#ga2524a7db039e09d7716cd63e86846f9f", null ], + [ "jwks_item_free", "group__jwks__core__grp.html#ga8a82553f9582e10d096a1b9ac73c21a5", null ], + [ "jwks_item_free_all", "group__jwks__core__grp.html#ga8b5403fd3d2e07199bb8afb2c053aa2e", null ], + [ "jwks_item_get", "group__jwks__core__grp.html#gae00c8e06cbdf28d732452218940341a3", null ] +]; \ No newline at end of file diff --git a/HEAD/group__jwt__advanced__grp.html b/HEAD/group__jwt__advanced__grp.html new file mode 100644 index 00000000..1856e30c --- /dev/null +++ b/HEAD/group__jwt__advanced__grp.html @@ -0,0 +1,112 @@ + + + + + + + + +LibJWT: Advanced Functionality + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + +
+
LibJWT 2.9.99 +
+
The C JSON Web Token Library +JWK +JWKS
+
+
+ + + +
+
+ +
+
+
+ +
+
+ +
Advanced Functionality
+
+
+
+Collaboration diagram for Advanced Functionality:
+
+
+
+ + + + + + +

+Topics

 Memory Handlers
 
 Cryptographic Operations
 
+

Detailed Description

+
+
+ + + + + diff --git a/HEAD/group__jwt__advanced__grp.js b/HEAD/group__jwt__advanced__grp.js new file mode 100644 index 00000000..3b28aeba --- /dev/null +++ b/HEAD/group__jwt__advanced__grp.js @@ -0,0 +1,5 @@ +var group__jwt__advanced__grp = +[ + [ "Memory Handlers", "group__jwt__memory__grp.html", "group__jwt__memory__grp" ], + [ "Cryptographic Operations", "group__jwt__crypto__grp.html", "group__jwt__crypto__grp" ] +]; \ No newline at end of file diff --git a/HEAD/group__jwt__advanced__grp.map b/HEAD/group__jwt__advanced__grp.map new file mode 100644 index 00000000..90dc294c --- /dev/null +++ b/HEAD/group__jwt__advanced__grp.map @@ -0,0 +1,5 @@ + + + + + diff --git a/HEAD/group__jwt__advanced__grp.md5 b/HEAD/group__jwt__advanced__grp.md5 new file mode 100644 index 00000000..e5de1471 --- /dev/null +++ b/HEAD/group__jwt__advanced__grp.md5 @@ -0,0 +1 @@ +7a199238b5ca7140ff2b70b822cfd2ec \ No newline at end of file diff --git a/HEAD/group__jwt__advanced__grp.svg b/HEAD/group__jwt__advanced__grp.svg new file mode 100644 index 00000000..ed3ac536 --- /dev/null +++ b/HEAD/group__jwt__advanced__grp.svg @@ -0,0 +1,77 @@ + + + + + + + + + + + + +Advanced Functionality + + +Node1 + + +Advanced Functionality + + + + + +Node3 + + +Cryptographic Operations + + + + + +Node1->Node3 + + + + + +Node2 + + +Memory Handlers + + + + + +Node1->Node2 + + + + + + + + + + diff --git a/HEAD/group__jwt__advanced__grp_org.svg b/HEAD/group__jwt__advanced__grp_org.svg new file mode 100644 index 00000000..9f98d099 --- /dev/null +++ b/HEAD/group__jwt__advanced__grp_org.svg @@ -0,0 +1,51 @@ + + + + + + +Advanced Functionality + + +Node1 + + +Advanced Functionality + + + + + +Node3 + + +Cryptographic Operations + + + + + +Node1->Node3 + + + + + +Node2 + + +Memory Handlers + + + + + +Node1->Node2 + + + + + diff --git a/HEAD/group__jwt__alg__grp.html b/HEAD/group__jwt__alg__grp.html new file mode 100644 index 00000000..fc27356e --- /dev/null +++ b/HEAD/group__jwt__alg__grp.html @@ -0,0 +1,243 @@ + + + + + + + + +LibJWT: Algorithm Management + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + +
+
LibJWT 2.9.99 +
+
The C JSON Web Token Library +JWK +JWKS
+
+
+ + + +
+
+ +
+
+
+ +
+
+ +
Algorithm Management
+
+
+
+Collaboration diagram for Algorithm Management:
+
+
+
+ + + + + + + + + + +

+Functions

int jwt_set_alg (jwt_t *jwt, jwt_alg_t alg, const unsigned char *key, int len)
 
jwt_alg_t jwt_get_alg (const jwt_t *jwt)
 
const char * jwt_alg_str (jwt_alg_t alg)
 
jwt_alg_t jwt_str_alg (const char *alg)
 
+

Detailed Description

+

Set and check algorithms and algorithm specific values.

+

When working with functions that require a key, the underlying library takes care to scrub memory when the key is no longer used (e.g. when calling jwt_free() or when changing the algorithm, the old key, if it exists, is scrubbed).

+

Function Documentation

+ +

◆ jwt_alg_str()

+ +
+
+ + + + + + + +
const char * jwt_alg_str (jwt_alg_t alg)
+
+

Convert alg type to it's string representation.

+

Returns a string that matches the alg type provided.

+
Parameters
+ + +
algA valid jwt_alg_t specifier.
+
+
+
Returns
Returns a string (e.g. "RS256") matching the alg or NULL for invalid alg.
+ +
+
+ +

◆ jwt_get_alg()

+ +
+
+ + + + + + + +
jwt_alg_t jwt_get_alg (const jwt_t * jwt)
+
+

Get the jwt_alg_t set for this JWT object.

+

Returns the jwt_alg_t type for this JWT object.

+
Warning
This is the alg for the jwt_t object and NOT the one that may be set in the header. This is a programatic check to see what LibJWT will use to encode the object into a JWT. To see what is embedded in the header, use jwt_get_header(jwt, "alg") instead.
+
Parameters
+ + +
jwtPointer to a JWT object.
+
+
+
Returns
Returns a jwt_alg_t type for this object.
+ +
+
+ +

◆ jwt_set_alg()

+ +
+
+ + + + + + + + + + + + + + + + + + + + + +
int jwt_set_alg (jwt_t * jwt,
jwt_alg_t alg,
const unsigned char * key,
int len )
+
+

Set an algorithm for a jwt_t object.

+

Specifies an algorithm for a jwt_t object. If JWT_ALG_NONE is used, then key must be NULL and len must be 0. All other algorithms must have a valid pointer to key data, which may be specific to the algorithm (e.g RS256 expects a PEM formatted RSA key).

+
Parameters
+ + + + + +
jwtPointer to a jwt_t object.
algA valid jwt_alg_t specifier.
keyThe key data to use for the algorithm.
lenThe length of the key data.
+
+
+
Returns
Returns 0 on success, valid errno otherwise.
+ +
+
+ +

◆ jwt_str_alg()

+ +
+
+ + + + + + + +
jwt_alg_t jwt_str_alg (const char * alg)
+
+

Convert alg string to type.

+

Returns an alg type based on the string representation.

+

📄 RFC-7518 Sec 3.1

+
Parameters
+ + +
algA valid string for algorithm type (e.g. "RS256").
+
+
+
Returns
Returns a jwt_alg_t matching the string or JWT_ALG_INVAL if no matches were found.
+

Note, this only works for algorithms that LibJWT supports or knows about.

+ +
+
+
+
+ + + + + diff --git a/HEAD/group__jwt__alg__grp.js b/HEAD/group__jwt__alg__grp.js new file mode 100644 index 00000000..8215b81b --- /dev/null +++ b/HEAD/group__jwt__alg__grp.js @@ -0,0 +1,7 @@ +var group__jwt__alg__grp = +[ + [ "jwt_alg_str", "group__jwt__alg__grp.html#gaa906a6a1c27a3f9f6500240f7b228123", null ], + [ "jwt_get_alg", "group__jwt__alg__grp.html#ga3e9cb111d566f55017d86d22dc545225", null ], + [ "jwt_set_alg", "group__jwt__alg__grp.html#ga4b2fe3a4afc420fa7fa9feecebf2c4d7", null ], + [ "jwt_str_alg", "group__jwt__alg__grp.html#gaf15787af42ab0fc567a0a1fc5ef3edc6", null ] +]; \ No newline at end of file diff --git a/HEAD/group__jwt__alg__grp.map b/HEAD/group__jwt__alg__grp.map new file mode 100644 index 00000000..3d1ea642 --- /dev/null +++ b/HEAD/group__jwt__alg__grp.map @@ -0,0 +1,4 @@ + + + + diff --git a/HEAD/group__jwt__alg__grp.md5 b/HEAD/group__jwt__alg__grp.md5 new file mode 100644 index 00000000..18379915 --- /dev/null +++ b/HEAD/group__jwt__alg__grp.md5 @@ -0,0 +1 @@ +32b637643864fc886f9950363fcfc8c1 \ No newline at end of file diff --git a/HEAD/group__jwt__alg__grp.svg b/HEAD/group__jwt__alg__grp.svg new file mode 100644 index 00000000..9b07be55 --- /dev/null +++ b/HEAD/group__jwt__alg__grp.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + +Algorithm Management + + +Node1 + + +Algorithm Management + + + + + +Node2 + + +JSON Web Token + + + + + +Node2->Node1 + + + + + + + + + + diff --git a/HEAD/group__jwt__alg__grp_org.svg b/HEAD/group__jwt__alg__grp_org.svg new file mode 100644 index 00000000..4ad5ddc9 --- /dev/null +++ b/HEAD/group__jwt__alg__grp_org.svg @@ -0,0 +1,36 @@ + + + + + + +Algorithm Management + + +Node1 + + +Algorithm Management + + + + + +Node2 + + +JSON Web Token + + + + + +Node2->Node1 + + + + + diff --git a/HEAD/group__jwt__config__grp.html b/HEAD/group__jwt__config__grp.html new file mode 100644 index 00000000..46c295d4 --- /dev/null +++ b/HEAD/group__jwt__config__grp.html @@ -0,0 +1,220 @@ + + + + + + + + +LibJWT: Configuration Type + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + +
+
LibJWT 2.9.99 +
+
The C JSON Web Token Library +JWK +JWKS
+
+
+ + + +
+
+ +
+
+
+ +
+
+ +
Configuration Type
+
+
+
+Collaboration diagram for Configuration Type:
+
+
+
+ + + + + +

+Data Structures

struct  jwt_config_t
 Structure used to manage configuration state. More...
 
+ + + + +

+Macros

#define JWT_CONFIG_DECLARE(__name)
 Decleration of a jwt_config_t.
 
+ + + + +

+Typedefs

typedef int(* jwt_callback_t) (const jwt_t *, jwt_config_t *)
 Callback for operations involving verification of tokens.
 
+ + + + +

+Functions

void jwt_config_init (jwt_config_t *config)
 Intialize jwt_config_t to a clean state.
 
+

Detailed Description

+

The JWT configuration tools are setup to allow an agnostic way to handle state between different functions. The specific uses of the tools varies according to whether you are providing or consuming tokens. These aspects are documented in the other sections.

+

This section is a light intro of config types and common usage.

+
Remarks
LibJWT does not internally modify or set information in the jwt_config_t object. Certain values will determine how LibJWT handles various functions.
+

Macro Definition Documentation

+ +

◆ JWT_CONFIG_DECLARE

+ +
+
+ + + + + + + +
#define JWT_CONFIG_DECLARE( __name)
+
+Value:
jwt_config_t __name = { NULL, JWT_ALG_NONE, NULL}
+
@ JWT_ALG_NONE
Definition jwt.h:75
+
Structure used to manage configuration state.
Definition jwt.h:314
+
+

Decleration of a jwt_config_t.

+

This is useful for scoped usage to avoid declaring it and running the jwt_config_init function.

+
void some_function(const char *token)
+
{
+
JWT_CONFIG_DECLARE(my_config);
+
jwt_auto_t *my_jwt;
+
int ret;
+
+
// Setup my_config with key, alg type, etc
+
+
ret = jwt_verify(&my_jwt, token, &my_config);
+
if (ret)
+
return ret;
+
+
// Success
+
}
+
#define JWT_CONFIG_DECLARE(__name)
Decleration of a jwt_config_t.
Definition jwt.h:355
+
int jwt_verify(jwt_t **jwt, const char *token, jwt_config_t *config)
Decode and verify a JWT.
+
+
+
+

Typedef Documentation

+ +

◆ jwt_callback_t

+ +
+
+ + + + +
typedef int(* jwt_callback_t) (const jwt_t *, jwt_config_t *)
+
+ +

Callback for operations involving verification of tokens.

+

Further details can be found in Token Verification, specifically for jwt_verify_wcb

+ +
+
+

Function Documentation

+ +

◆ jwt_config_init()

+ +
+
+ + + + + + + +
void jwt_config_init (jwt_config_t * config)
+
+ +

Intialize jwt_config_t to a clean state.

+

To ensure a jwt_config_t is at a known state, this will clear values in the config. It will not free memory that might be associated with internal pointers.

+
Parameters
+ + +
configPointer to config to be cleared
+
+
+ +
+
+
+
+ + + + + diff --git a/HEAD/group__jwt__config__grp.js b/HEAD/group__jwt__config__grp.js new file mode 100644 index 00000000..60dcdb63 --- /dev/null +++ b/HEAD/group__jwt__config__grp.js @@ -0,0 +1,11 @@ +var group__jwt__config__grp = +[ + [ "jwt_config_t", "structjwt__config__t.html", [ + [ "alg", "structjwt__config__t.html#aa1f5e081bba22bd5e0e90962b55d3305", null ], + [ "ctx", "structjwt__config__t.html#a5b20a944b428896447108820ca861117", null ], + [ "jw_key", "structjwt__config__t.html#abc613eedbd07027e1aed03baaf7b7a93", null ] + ] ], + [ "JWT_CONFIG_DECLARE", "group__jwt__config__grp.html#ga0816f65962b32e875a2ed9b6f0166bee", null ], + [ "jwt_callback_t", "group__jwt__config__grp.html#ga44edddf2ce7034752c2b22283ad1010d", null ], + [ "jwt_config_init", "group__jwt__config__grp.html#ga08ab7f9e1917072c0910ee5335d3b04a", null ] +]; \ No newline at end of file diff --git a/HEAD/group__jwt__config__grp.map b/HEAD/group__jwt__config__grp.map new file mode 100644 index 00000000..b8fb1bd1 --- /dev/null +++ b/HEAD/group__jwt__config__grp.map @@ -0,0 +1,4 @@ + + + + diff --git a/HEAD/group__jwt__config__grp.md5 b/HEAD/group__jwt__config__grp.md5 new file mode 100644 index 00000000..e97f3b57 --- /dev/null +++ b/HEAD/group__jwt__config__grp.md5 @@ -0,0 +1 @@ +1343737bf3342f29951a9394505530c5 \ No newline at end of file diff --git a/HEAD/group__jwt__config__grp.svg b/HEAD/group__jwt__config__grp.svg new file mode 100644 index 00000000..d82cf389 --- /dev/null +++ b/HEAD/group__jwt__config__grp.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + +Configuration Type + + +Node1 + + +Configuration Type + + + + + +Node2 + + +JSON Web Token + + + + + +Node2->Node1 + + + + + + + + + + diff --git a/HEAD/group__jwt__config__grp_org.svg b/HEAD/group__jwt__config__grp_org.svg new file mode 100644 index 00000000..2d04c990 --- /dev/null +++ b/HEAD/group__jwt__config__grp_org.svg @@ -0,0 +1,36 @@ + + + + + + +Configuration Type + + +Node1 + + +Configuration Type + + + + + +Node2 + + +JSON Web Token + + + + + +Node2->Node1 + + + + + diff --git a/HEAD/group__jwt__core__grp.html b/HEAD/group__jwt__core__grp.html new file mode 100644 index 00000000..4a644d67 --- /dev/null +++ b/HEAD/group__jwt__core__grp.html @@ -0,0 +1,274 @@ + + + + + + + + +LibJWT: Object Creation + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + +
+
LibJWT 2.9.99 +
+
The C JSON Web Token Library +JWK +JWKS
+
+
+ + + +
+
+ +
+
+
+ +
+
+ +
Object Creation
+
+
+
+Collaboration diagram for Object Creation:
+
+
+
+ + + + + +

+Typedefs

typedef struct jwt jwt_t
 Opaque JWT object.
 
+ + + + +

+Enumerations

enum  jwt_alg_t {
+  JWT_ALG_NONE = 0 +, JWT_ALG_HS256 +, JWT_ALG_HS384 +, JWT_ALG_HS512 +,
+  JWT_ALG_RS256 +, JWT_ALG_RS384 +, JWT_ALG_RS512 +, JWT_ALG_ES256 +,
+  JWT_ALG_ES384 +, JWT_ALG_ES512 +, JWT_ALG_PS256 +, JWT_ALG_PS384 +,
+  JWT_ALG_PS512 +, JWT_ALG_ES256K +, JWT_ALG_EDDSA +, JWT_ALG_INVAL +
+ }
 JWT algorithm types. More...
 
+ + + + + +

+Functions

void jwt_free (jwt_t *jwt)
 
jwt_tjwt_dup (jwt_t *jwt)
 
+

Detailed Description

+

Functions used to create and destroy JWT objects.

+

Typedef Documentation

+ +

◆ jwt_t

+ +
+
+ + + + +
typedef struct jwt jwt_t
+
+ +

Opaque JWT object.

+

This object is used throughout the JWT functions.

+
Remarks
When creating a JWT object (encoding), this stores state until you call one of the encoding functions. When dedcoding a JSON Web Token this object is returned so you can inspect it further (e.g. retrieve grants).
+ +
+
+

Enumeration Type Documentation

+ +

◆ jwt_alg_t

+ +
+
+ + + + +
enum jwt_alg_t
+
+ +

JWT algorithm types.

+

These are the supported algorithm types for LibJWT.

+
Warning
You should not assume that this directly relates to what may be in the JWT header. The internal state of the jwt_t object and the JSON data are only guarateed to be in sync during encoding and decoding.
+
Note
For HMAC algorithms, the key can be any data, even binary. However, for all the other algorithms, the key is expected to be in a format that the underlying Cryptographic Operations can interpret. Generally, PEM is a safe bet.
+

📄 RFC-7518 Sec 3.1

+ + + + + + + + + + + + + + + + + +
Enumerator
JWT_ALG_NONE 

No signature

+
JWT_ALG_HS256 

HMAC using SHA-256

+
JWT_ALG_HS384 

HMAC using SHA-384

+
JWT_ALG_HS512 

HMAC using SHA-512

+
JWT_ALG_RS256 

RSASSA-PKCS1-v1_5 using SHA-256

+
JWT_ALG_RS384 

RSASSA-PKCS1-v1_5 using SHA-384

+
JWT_ALG_RS512 

RSASSA-PKCS1-v1_5 using SHA-512

+
JWT_ALG_ES256 

ECDSA using P-256 and SHA-256

+
JWT_ALG_ES384 

ECDSA using P-384 and SHA-384

+
JWT_ALG_ES512 

ECDSA using P-521 and SHA-512

+
JWT_ALG_PS256 

RSASSA-PSS using SHA-256 and MGF1 with SHA-256

+
JWT_ALG_PS384 

RSASSA-PSS using SHA-384 and MGF1 with SHA-384

+
JWT_ALG_PS512 

RSASSA-PSS using SHA-512 and MGF1 with SHA-512

+
JWT_ALG_ES256K 

ECDSA using secp256k1 and SHA-256

+
JWT_ALG_EDDSA 

EdDSA using Ed25519

+
JWT_ALG_INVAL 

An invalid algorithm from the caller or the token

+
+ +
+
+

Function Documentation

+ +

◆ jwt_dup()

+ +
+
+ + + + + + + +
jwt_t * jwt_dup (jwt_t * jwt)
+
+

Duplicate an existing JWT object.

+

Copies all grants and algorithm specific bits to a new JWT object.

+
Parameters
+ + +
jwtPointer to a JWT object.
+
+
+
Returns
A new object on success, NULL on error with errno set appropriately.
+ +
+
+ +

◆ jwt_free()

+ +
+
+ + + + + + + +
void jwt_free (jwt_t * jwt)
+
+

Free a JWT object and any other resources it is using.

+

After calling, the JWT object referenced will no longer be valid and its memory will be freed.

+
Parameters
+ + +
jwtPointer to a JWT object previously created object
+
+
+ +
+
+
+
+ + + + + diff --git a/HEAD/group__jwt__core__grp.js b/HEAD/group__jwt__core__grp.js new file mode 100644 index 00000000..b5a5325c --- /dev/null +++ b/HEAD/group__jwt__core__grp.js @@ -0,0 +1,24 @@ +var group__jwt__core__grp = +[ + [ "jwt_t", "group__jwt__core__grp.html#gabf0acfb990d245214216af589c1d0947", null ], + [ "jwt_alg_t", "group__jwt__core__grp.html#ga889db178d8714ab67bc994f1d8faad33", [ + [ "JWT_ALG_NONE", "group__jwt__core__grp.html#gga889db178d8714ab67bc994f1d8faad33a3249a71140a0bb4b3cb196fe15114b89", null ], + [ "JWT_ALG_HS256", "group__jwt__core__grp.html#gga889db178d8714ab67bc994f1d8faad33aaa9217f4c8f5266d5401402f0ec98604", null ], + [ "JWT_ALG_HS384", "group__jwt__core__grp.html#gga889db178d8714ab67bc994f1d8faad33af0adc69b9caa89fbc2eeaf744d7cf4f2", null ], + [ "JWT_ALG_HS512", "group__jwt__core__grp.html#gga889db178d8714ab67bc994f1d8faad33a596b0da85c01e17b43a6574f82159081", null ], + [ "JWT_ALG_RS256", "group__jwt__core__grp.html#gga889db178d8714ab67bc994f1d8faad33aa03ba981baa4b1cda3b9de1347ada853", null ], + [ "JWT_ALG_RS384", "group__jwt__core__grp.html#gga889db178d8714ab67bc994f1d8faad33a520814a2695a6df472bb408ba6feb206", null ], + [ "JWT_ALG_RS512", "group__jwt__core__grp.html#gga889db178d8714ab67bc994f1d8faad33ab4d732eef3f6bee21201d638dfd8ca44", null ], + [ "JWT_ALG_ES256", "group__jwt__core__grp.html#gga889db178d8714ab67bc994f1d8faad33a84f9bf1cb93f6ae622a9de57d9c67472", null ], + [ "JWT_ALG_ES384", "group__jwt__core__grp.html#gga889db178d8714ab67bc994f1d8faad33ae18e69e6b629aa0d3e6d16ec4a95c708", null ], + [ "JWT_ALG_ES512", "group__jwt__core__grp.html#gga889db178d8714ab67bc994f1d8faad33a87205e19f1dde7c3cee012424482e106", null ], + [ "JWT_ALG_PS256", "group__jwt__core__grp.html#gga889db178d8714ab67bc994f1d8faad33ad0216cbdf24f6b486d5082294b3cfc5b", null ], + [ "JWT_ALG_PS384", "group__jwt__core__grp.html#gga889db178d8714ab67bc994f1d8faad33a3ad3c7e704886dd61dee0de5fa02072d", null ], + [ "JWT_ALG_PS512", "group__jwt__core__grp.html#gga889db178d8714ab67bc994f1d8faad33a727c838b47ad911681eb029cc8b55744", null ], + [ "JWT_ALG_ES256K", "group__jwt__core__grp.html#gga889db178d8714ab67bc994f1d8faad33a7f760cf9ed49b80ecb78580ed381ff01", null ], + [ "JWT_ALG_EDDSA", "group__jwt__core__grp.html#gga889db178d8714ab67bc994f1d8faad33aab33738e67e3b23db72a3bc77043009b", null ], + [ "JWT_ALG_INVAL", "group__jwt__core__grp.html#gga889db178d8714ab67bc994f1d8faad33a199f5ce0e69c0160cc121cf6dbe9cb58", null ] + ] ], + [ "jwt_dup", "group__jwt__core__grp.html#gac11d4548c695e5781cae2a5148a7e240", null ], + [ "jwt_free", "group__jwt__core__grp.html#gafd51b076050815be44081439d1cb33fc", null ] +]; \ No newline at end of file diff --git a/HEAD/group__jwt__core__grp.map b/HEAD/group__jwt__core__grp.map new file mode 100644 index 00000000..64671441 --- /dev/null +++ b/HEAD/group__jwt__core__grp.map @@ -0,0 +1,4 @@ + + + + diff --git a/HEAD/group__jwt__core__grp.md5 b/HEAD/group__jwt__core__grp.md5 new file mode 100644 index 00000000..d317bd54 --- /dev/null +++ b/HEAD/group__jwt__core__grp.md5 @@ -0,0 +1 @@ +dba6150d476f7d745cdebcf4658242d9 \ No newline at end of file diff --git a/HEAD/group__jwt__core__grp.svg b/HEAD/group__jwt__core__grp.svg new file mode 100644 index 00000000..1f38e570 --- /dev/null +++ b/HEAD/group__jwt__core__grp.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + +Object Creation + + +Node1 + + +Object Creation + + + + + +Node2 + + +JSON Web Token + + + + + +Node2->Node1 + + + + + + + + + + diff --git a/HEAD/group__jwt__core__grp_org.svg b/HEAD/group__jwt__core__grp_org.svg new file mode 100644 index 00000000..0c091443 --- /dev/null +++ b/HEAD/group__jwt__core__grp_org.svg @@ -0,0 +1,36 @@ + + + + + + +Object Creation + + +Node1 + + +Object Creation + + + + + +Node2 + + +JSON Web Token + + + + + +Node2->Node1 + + + + + diff --git a/HEAD/group__jwt__create__grp.html b/HEAD/group__jwt__create__grp.html new file mode 100644 index 00000000..0b6a0c71 --- /dev/null +++ b/HEAD/group__jwt__create__grp.html @@ -0,0 +1,131 @@ + + + + + + + + +LibJWT: Create a JWT + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + +
+
LibJWT 2.9.99 +
+
The C JSON Web Token Library +JWK +JWKS
+
+
+ + + +
+
+ +
+
+
+ +
+
+ +
Create a JWT
+
+
+
+Collaboration diagram for Create a JWT:
+
+
+
+ + + + + +

+Functions

jwt_tjwt_create (jwt_config_t *config)
 Initial function to create a new JWT.
 
+

Detailed Description

+

Function Documentation

+ +

◆ jwt_create()

+ +
+
+ + + + + + + +
jwt_t * jwt_create (jwt_config_t * config)
+
+ +

Initial function to create a new JWT.

+ +
+
+
+
+ + + + + diff --git a/HEAD/group__jwt__create__grp.js b/HEAD/group__jwt__create__grp.js new file mode 100644 index 00000000..69093b75 --- /dev/null +++ b/HEAD/group__jwt__create__grp.js @@ -0,0 +1,4 @@ +var group__jwt__create__grp = +[ + [ "jwt_create", "group__jwt__create__grp.html#ga7d5f4d5a83505c06faf9728bd7911908", null ] +]; \ No newline at end of file diff --git a/HEAD/group__jwt__create__grp.map b/HEAD/group__jwt__create__grp.map new file mode 100644 index 00000000..ea10210c --- /dev/null +++ b/HEAD/group__jwt__create__grp.map @@ -0,0 +1,4 @@ + + + + diff --git a/HEAD/group__jwt__create__grp.md5 b/HEAD/group__jwt__create__grp.md5 new file mode 100644 index 00000000..70ed19b8 --- /dev/null +++ b/HEAD/group__jwt__create__grp.md5 @@ -0,0 +1 @@ +1db90b11060873e90e48cc78ca38edd3 \ No newline at end of file diff --git a/HEAD/group__jwt__create__grp.svg b/HEAD/group__jwt__create__grp.svg new file mode 100644 index 00000000..72c4fc0d --- /dev/null +++ b/HEAD/group__jwt__create__grp.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + +Create a JWT + + +Node1 + + +Create a JWT + + + + + +Node2 + + +JSON Web Token + + + + + +Node2->Node1 + + + + + + + + + + diff --git a/HEAD/group__jwt__create__grp_org.svg b/HEAD/group__jwt__create__grp_org.svg new file mode 100644 index 00000000..12b5d068 --- /dev/null +++ b/HEAD/group__jwt__create__grp_org.svg @@ -0,0 +1,36 @@ + + + + + + +Create a JWT + + +Node1 + + +Create a JWT + + + + + +Node2 + + +JSON Web Token + + + + + +Node2->Node1 + + + + + diff --git a/HEAD/group__jwt__crypto__grp.html b/HEAD/group__jwt__crypto__grp.html new file mode 100644 index 00000000..c1b2552e --- /dev/null +++ b/HEAD/group__jwt__crypto__grp.html @@ -0,0 +1,278 @@ + + + + + + + + +LibJWT: Cryptographic Operations + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + +
+
LibJWT 2.9.99 +
+
The C JSON Web Token Library +JWK +JWKS
+
+
+ + + +
+
+ +
+
+
+ +
+
+ +
Cryptographic Operations
+
+
+
+Collaboration diagram for Cryptographic Operations:
+
+
+
+ + + + + +

+Enumerations

enum  jwt_crypto_provider_t {
+  JWT_CRYPTO_OPS_NONE = 0 +, JWT_CRYPTO_OPS_OPENSSL +, JWT_CRYPTO_OPS_GNUTLS +, JWT_CRYPTO_OPS_MBEDTLS +,
+  JWT_CRYPTO_OPS_ANY +
+ }
 Different providers for crypto operations. More...
 
+ + + + + + + + + + + +

+Functions

const char * jwt_get_crypto_ops (void)
 
jwt_crypto_provider_t jwt_get_crypto_ops_t (void)
 
int jwt_set_crypto_ops (const char *opname)
 
int jwt_set_crypto_ops_t (jwt_crypto_provider_t opname)
 
int jwt_crypto_ops_supports_jwk (void)
 
+

Detailed Description

+

Functions used to set and get which crypto operations are used

+

LibJWT supports several crypto libraries, mainly "openssl" and "gnutls". By default, if enabled, "openssl" is used.

+
Warning
Changing the crypto operations is not thread safe. You must protect changing them with some sort of lock, including locking around usage of the operations themselves. Ideally, you should only perform this at the start of your application before using any of LibJWTs functions. Failing to follow this guide can lead to crashes in certain situations.
+
Remarks
ENVIRONMENT: You can set JWT_CRYPTO to the default operations you wish to use. If JWT_CRYPTO is invalid, an error message will be printed to the console when LibJWT is loaded by the application.
+

Enumeration Type Documentation

+ +

◆ jwt_crypto_provider_t

+ +
+
+ + + + +
enum jwt_crypto_provider_t
+
+ +

Different providers for crypto operations.

+

Used to set or test the underlying cryptographic library provider.

+
Remarks
These being present are not a guarantee that the JWT library has been compiled to support it. Also, certain functions of the library may not be supported by each. For example, not all of them support JWKS operations.
+ + + + + + +
Enumerator
JWT_CRYPTO_OPS_NONE 

Used for error handling

+
JWT_CRYPTO_OPS_OPENSSL 

OpenSSL Library

+
JWT_CRYPTO_OPS_GNUTLS 

GnuTLS Library

+
JWT_CRYPTO_OPS_MBEDTLS 

MBedTLS embedded library

+
JWT_CRYPTO_OPS_ANY 

Used internally for hmac keys

+
+ +
+
+

Function Documentation

+ +

◆ jwt_crypto_ops_supports_jwk()

+ +
+
+ + + + + + + +
int jwt_crypto_ops_supports_jwk (void )
+
+

Check if the current crypto operations support JWK usage

+
Returns
1 if it does, 0 if not
+ +
+
+ +

◆ jwt_get_crypto_ops()

+ +
+
+ + + + + + + +
const char * jwt_get_crypto_ops (void )
+
+

Retrieve the name of the current crypto operations being used.

+
Returns
name of the crypto operation set
+ +
+
+ +

◆ jwt_get_crypto_ops_t()

+ +
+
+ + + + + + + +
jwt_crypto_provider_t jwt_get_crypto_ops_t (void )
+
+

Retrieve the type of the current crypto operations being used.

+
Returns
jwt_crypto_provider_t of the crypto operation set
+ +
+
+ +

◆ jwt_set_crypto_ops()

+ +
+
+ + + + + + + +
int jwt_set_crypto_ops (const char * opname)
+
+

Set the crypto operations to the named set.

+

The opname is one of the available operators in the compiled version of LibJWT. Most times, this is either "openssl" or "gnutls".

+
Parameters
+ + +
opnamethe name of the crypto operation to set
+
+
+
Returns
0 on success, valid errno otherwise.
+ +
+
+ +

◆ jwt_set_crypto_ops_t()

+ +
+
+ + + + + + + +
int jwt_set_crypto_ops_t (jwt_crypto_provider_t opname)
+
+

Set the crypto operations to a jwt_crypto_provider_t type

+

The same as jwt_set_crypto_ops(), but uses the type as opname

+
Parameters
+ + +
opnameA valid jwt_crypto_provider_t type
+
+
+
Returns
0 on success, valid errno otherwise.
+ +
+
+
+
+ + + + + diff --git a/HEAD/group__jwt__crypto__grp.js b/HEAD/group__jwt__crypto__grp.js new file mode 100644 index 00000000..29f9ef9d --- /dev/null +++ b/HEAD/group__jwt__crypto__grp.js @@ -0,0 +1,15 @@ +var group__jwt__crypto__grp = +[ + [ "jwt_crypto_provider_t", "group__jwt__crypto__grp.html#gaa1e2b1f241858d81b1bfb86018d3b3ac", [ + [ "JWT_CRYPTO_OPS_NONE", "group__jwt__crypto__grp.html#ggaa1e2b1f241858d81b1bfb86018d3b3aca482aa19a4996aaddbfd0349356be92c2", null ], + [ "JWT_CRYPTO_OPS_OPENSSL", "group__jwt__crypto__grp.html#ggaa1e2b1f241858d81b1bfb86018d3b3aca5ebef71958d3135b13ada7bb72a63b02", null ], + [ "JWT_CRYPTO_OPS_GNUTLS", "group__jwt__crypto__grp.html#ggaa1e2b1f241858d81b1bfb86018d3b3acaee34d6e9a11bfaa47e01c56a66682750", null ], + [ "JWT_CRYPTO_OPS_MBEDTLS", "group__jwt__crypto__grp.html#ggaa1e2b1f241858d81b1bfb86018d3b3aca4c5fd08d7061b5caba0f8698dc817306", null ], + [ "JWT_CRYPTO_OPS_ANY", "group__jwt__crypto__grp.html#ggaa1e2b1f241858d81b1bfb86018d3b3aca80b50ab76725eb24ce2c16199865d50a", null ] + ] ], + [ "jwt_crypto_ops_supports_jwk", "group__jwt__crypto__grp.html#gad03cf6e8291772a1ff1d37a81f18a177", null ], + [ "jwt_get_crypto_ops", "group__jwt__crypto__grp.html#ga1ccd01c10c0f77ab1d247b1fff1f47b1", null ], + [ "jwt_get_crypto_ops_t", "group__jwt__crypto__grp.html#ga3f15e0ad1c6f8e2afae34c1d5bb03fcd", null ], + [ "jwt_set_crypto_ops", "group__jwt__crypto__grp.html#ga6749e9262b985d2b0b1f99f316356aa3", null ], + [ "jwt_set_crypto_ops_t", "group__jwt__crypto__grp.html#ga1a03f32ccbc62bd04b4ae3a795d0bb08", null ] +]; \ No newline at end of file diff --git a/HEAD/group__jwt__crypto__grp.map b/HEAD/group__jwt__crypto__grp.map new file mode 100644 index 00000000..c786037e --- /dev/null +++ b/HEAD/group__jwt__crypto__grp.map @@ -0,0 +1,4 @@ + + + + diff --git a/HEAD/group__jwt__crypto__grp.md5 b/HEAD/group__jwt__crypto__grp.md5 new file mode 100644 index 00000000..a3d57c76 --- /dev/null +++ b/HEAD/group__jwt__crypto__grp.md5 @@ -0,0 +1 @@ +9381b9af1619b2501e52a5686442ccde \ No newline at end of file diff --git a/HEAD/group__jwt__crypto__grp.svg b/HEAD/group__jwt__crypto__grp.svg new file mode 100644 index 00000000..49fa1ba4 --- /dev/null +++ b/HEAD/group__jwt__crypto__grp.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + +Cryptographic Operations + + +Node2 + + +Advanced Functionality + + + + + +Node1 + + +Cryptographic Operations + + + + + +Node2->Node1 + + + + + + + + + + diff --git a/HEAD/group__jwt__crypto__grp_org.svg b/HEAD/group__jwt__crypto__grp_org.svg new file mode 100644 index 00000000..1c16a2b0 --- /dev/null +++ b/HEAD/group__jwt__crypto__grp_org.svg @@ -0,0 +1,36 @@ + + + + + + +Cryptographic Operations + + +Node2 + + +Advanced Functionality + + + + + +Node1 + + +Cryptographic Operations + + + + + +Node2->Node1 + + + + + diff --git a/HEAD/group__jwt__encode__grp.html b/HEAD/group__jwt__encode__grp.html new file mode 100644 index 00000000..dba66751 --- /dev/null +++ b/HEAD/group__jwt__encode__grp.html @@ -0,0 +1,304 @@ + + + + + + + + +LibJWT: Encoding and Output + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + +
+
LibJWT 2.9.99 +
+
The C JSON Web Token Library +JWK +JWKS
+
+
+ + + +
+
+ +
+
+
+ +
+
+ +
Encoding and Output
+
+
+
+Collaboration diagram for Encoding and Output:
+
+
+
+ + + + + + + + + + + + + + +

+Functions

int jwt_dump_fp (jwt_t *jwt, FILE *fp, int pretty)
 
char * jwt_dump_str (jwt_t *jwt, int pretty)
 
char * jwt_dump_grants_str (jwt_t *jwt, int pretty)
 
int jwt_encode_fp (jwt_t *jwt, FILE *fp)
 
char * jwt_encode_str (jwt_t *jwt)
 
void jwt_free_str (char *str)
 
+

Detailed Description

+

Functions for encoding a valid JWT optionally (but preferably) using JWA operation such as sigining or encryption.

+

Function Documentation

+ +

◆ jwt_dump_fp()

+ +
+
+ + + + + + + + + + + + + + + + +
int jwt_dump_fp (jwt_t * jwt,
FILE * fp,
int pretty )
+
+

Output plain text representation to a FILE pointer.

+

This function will write a plain text representation of this JWT object without Base64 encoding. This only writes the header and body, and does not compute the signature or encryption (if such an algorithm were being used).

+
Remarks
This may change the content of JWT header if algorithm is set on the JWT object. If algorithm is set (jwt_set_alg was called on the jwt object) then dumping JWT attempts to append 'typ' header. If the 'typ' header already exists, then it is left untouched, otherwise it is added with default value of "JWT".
+
Parameters
+ + + + +
jwtPointer to a JWT object.
fpValid FILE pointer to write data to.
prettyEnables better visual formatting of output. Generally only used for debugging.
+
+
+
Returns
Returns 0 on success, valid errno otherwise.
+ +
+
+ +

◆ jwt_dump_grants_str()

+ +
+
+ + + + + + + + + + + +
char * jwt_dump_grants_str (jwt_t * jwt,
int pretty )
+
+

Return plain text representation of grants as a string.

+

Similar to jwt_dump_str() except that only a string containing the grants string is returned. The string must be freed by the caller.

+
Parameters
+ + + +
jwtPointer to a JWT object.
prettyEnables better visual formatting of output. Generally only used for debugging.
+
+
+
Returns
A nul terminated string on success, NULL on error with errno set appropriately.
+ +
+
+ +

◆ jwt_dump_str()

+ +
+
+ + + + + + + + + + + +
char * jwt_dump_str (jwt_t * jwt,
int pretty )
+
+

Return plain text representation as a string.

+

Similar to jwt_dump_fp() except that a string is returned. The string must be freed by the caller.

+
Remarks
This may change the content of JWT header if algorithm is set on the JWT object. If algorithm is set (jwt_set_alg was called on the jwt object) then dumping JWT attempts to append 'typ' header. If the 'typ' header already exists, then it is left untouched, otherwise it is added with default value of "JWT".
+
Parameters
+ + + +
jwtPointer to a JWT object.
prettyEnables better visual formatting of output. Generally only used for debugging.
+
+
+
Returns
A nul terminated string on success, NULL on error with errno set appropriately.
+ +
+
+ +

◆ jwt_encode_fp()

+ +
+
+ + + + + + + + + + + +
int jwt_encode_fp (jwt_t * jwt,
FILE * fp )
+
+

Fully encode a JWT object and write it to FILE.

+

This will create and write the complete JWT object to FILE. All parts will be Base64 encoded and signatures or encryption will be applied if the algorithm specified requires it.

+
Parameters
+ + + +
jwtPointer to a JWT object.
fpValid FILE pointer to write data to.
+
+
+
Returns
Returns 0 on success, valid errno otherwise.
+ +
+
+ +

◆ jwt_encode_str()

+ +
+
+ + + + + + + +
char * jwt_encode_str (jwt_t * jwt)
+
+

Fully encode a JWT object and return as a string.

+

Similar to jwt_encode_fp() except that a string is returned. The string must be freed by the caller. If you changed the allocation method using jwt_set_alloc, then you must use jwt_free_str() to free the memory.

+
Parameters
+ + +
jwtPointer to a JWT object.
+
+
+
Returns
A null terminated string on success, NULL on error with errno set appropriately.
+ +
+
+ +

◆ jwt_free_str()

+ +
+
+ + + + + + + +
void jwt_free_str (char * str)
+
+

Free a string returned from the library.

+
Parameters
+ + +
strPointer to a string previously created with jwt_encode_str().
+
+
+ +
+
+
+
+ + + + + diff --git a/HEAD/group__jwt__encode__grp.js b/HEAD/group__jwt__encode__grp.js new file mode 100644 index 00000000..15232cc9 --- /dev/null +++ b/HEAD/group__jwt__encode__grp.js @@ -0,0 +1,9 @@ +var group__jwt__encode__grp = +[ + [ "jwt_dump_fp", "group__jwt__encode__grp.html#gacc77ae9e2214c9b07352a400758f1f4d", null ], + [ "jwt_dump_grants_str", "group__jwt__encode__grp.html#ga219c406420f7f92960735433c69a304e", null ], + [ "jwt_dump_str", "group__jwt__encode__grp.html#ga1bf8a1df0f944c027d40677404778290", null ], + [ "jwt_encode_fp", "group__jwt__encode__grp.html#ga2782abdbb190b626a5bd4eb3943b7b8d", null ], + [ "jwt_encode_str", "group__jwt__encode__grp.html#ga124ccd21e6495dbbd1012f60d74202dd", null ], + [ "jwt_free_str", "group__jwt__encode__grp.html#ga1c6ff5f00e8722f5522fe7b36697ecdd", null ] +]; \ No newline at end of file diff --git a/HEAD/group__jwt__encode__grp.map b/HEAD/group__jwt__encode__grp.map new file mode 100644 index 00000000..69ceecd5 --- /dev/null +++ b/HEAD/group__jwt__encode__grp.map @@ -0,0 +1,4 @@ + + + + diff --git a/HEAD/group__jwt__encode__grp.md5 b/HEAD/group__jwt__encode__grp.md5 new file mode 100644 index 00000000..ce717b5c --- /dev/null +++ b/HEAD/group__jwt__encode__grp.md5 @@ -0,0 +1 @@ +0dcdbd84713d29eee8d3ba21af0082e6 \ No newline at end of file diff --git a/HEAD/group__jwt__encode__grp.svg b/HEAD/group__jwt__encode__grp.svg new file mode 100644 index 00000000..2946176a --- /dev/null +++ b/HEAD/group__jwt__encode__grp.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + +Encoding and Output + + +Node1 + + +Encoding and Output + + + + + +Node2 + + +JSON Web Token + + + + + +Node2->Node1 + + + + + + + + + + diff --git a/HEAD/group__jwt__encode__grp_org.svg b/HEAD/group__jwt__encode__grp_org.svg new file mode 100644 index 00000000..83aadae7 --- /dev/null +++ b/HEAD/group__jwt__encode__grp_org.svg @@ -0,0 +1,36 @@ + + + + + + +Encoding and Output + + +Node1 + + +Encoding and Output + + + + + +Node2 + + +JSON Web Token + + + + + +Node2->Node1 + + + + + diff --git a/HEAD/group__jwt__grant__grp.html b/HEAD/group__jwt__grant__grp.html new file mode 100644 index 00000000..561ec74b --- /dev/null +++ b/HEAD/group__jwt__grant__grp.html @@ -0,0 +1,431 @@ + + + + + + + + +LibJWT: Grant Management + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + +
+
LibJWT 2.9.99 +
+
The C JSON Web Token Library +JWK +JWKS
+
+
+ + + +
+
+ +
+
+
+ +
+
+ +
Grant Management
+
+
+
+Collaboration diagram for Grant Management:
+
+
+
+ + + + + + + + + + + + + + + + + + + + +

+Functions

const char * jwt_get_grant (const jwt_t *jwt, const char *grant)
 
long jwt_get_grant_int (const jwt_t *jwt, const char *grant)
 
int jwt_get_grant_bool (const jwt_t *jwt, const char *grant)
 
char * jwt_get_grants_json (const jwt_t *jwt, const char *grant)
 
int jwt_add_grant (jwt_t *jwt, const char *grant, const char *val)
 
int jwt_add_grant_int (jwt_t *jwt, const char *grant, long val)
 
int jwt_add_grant_bool (jwt_t *jwt, const char *grant, int val)
 
int jwt_add_grants_json (jwt_t *jwt, const char *json)
 
int jwt_del_grants (jwt_t *jwt, const char *grant)
 
+

Detailed Description

+

These functions allow you to add, remove and retrieve grants from a JWT object.

+

Function Documentation

+ +

◆ jwt_add_grant()

+ +
+
+ + + + + + + + + + + + + + + + +
int jwt_add_grant (jwt_t * jwt,
const char * grant,
const char * val )
+
+

Add a new string grant to this JWT object.

+

Creates a new grant for this object. The string for grant and val are copied internally, so do not require that the pointer or string remain valid for the lifetime of this object. It is an error if you try to add a grant that already exists.

+
Parameters
+ + + + +
jwtPointer to a JWT object.
grantString containing the name of the grant to add.
valString containing the value to be saved for grant. Can be an empty string, but cannot be NULL.
+
+
+
Returns
Returns 0 on success, valid errno otherwise.
+
Remarks
This only allows for string based grants. If you wish to add integer grants, then use jwt_add_grant_int(). If you wish to add more complex grants (e.g. an array), then use jwt_add_grants_json().
+ +
+
+ +

◆ jwt_add_grant_bool()

+ +
+
+ + + + + + + + + + + + + + + + +
int jwt_add_grant_bool (jwt_t * jwt,
const char * grant,
int val )
+
+

Add a new boolean grant to this JWT object.

+

Creates a new grant for this object. The string for grant is copied internally, so do not require that the pointer or string remain valid for the lifetime of this object. It is an error if you try to add a grant that already exists.

+
Parameters
+ + + + +
jwtPointer to a JWT object.
grantString containing the name of the grant to add.
valboolean containing the value to be saved for grant.
+
+
+
Returns
Returns 0 on success, valid errno otherwise.
+
Remarks
This only allows for boolean based grants. If you wish to add string grants, then use jwt_add_grant(). If you wish to add more complex grants (e.g. an array), then use jwt_add_grants_json().
+ +
+
+ +

◆ jwt_add_grant_int()

+ +
+
+ + + + + + + + + + + + + + + + +
int jwt_add_grant_int (jwt_t * jwt,
const char * grant,
long val )
+
+

Add a new integer grant to this JWT object.

+

Creates a new grant for this object. The string for grant is copied internally, so do not require that the pointer or string remain valid for the lifetime of this object. It is an error if you try to add a grant that already exists.

+
Parameters
+ + + + +
jwtPointer to a JWT object.
grantString containing the name of the grant to add.
valint containing the value to be saved for grant.
+
+
+
Returns
Returns 0 on success, valid errno otherwise.
+
Remarks
This only allows for integer based grants. If you wish to add string grants, then use jwt_add_grant(). If you wish to add more complex grants (e.g. an array), then use jwt_add_grants_json().
+ +
+
+ +

◆ jwt_add_grants_json()

+ +
+
+ + + + + + + + + + + +
int jwt_add_grants_json (jwt_t * jwt,
const char * json )
+
+

Add grants from a JSON encoded object string.

+

Loads a grant from an existing JSON encoded object string. Overwrites existing grant. If grant is NULL, then the JSON encoded string is assumed to be a JSON hash of all grants being added and will be merged into the grant listing.

+
Parameters
+ + + +
jwtPointer to a JWT object.
jsonString containing a JSON encoded object of grants.
+
+
+
Returns
Returns 0 on success, valid errno otherwise.
+ +
+
+ +

◆ jwt_del_grants()

+ +
+
+ + + + + + + + + + + +
int jwt_del_grants (jwt_t * jwt,
const char * grant )
+
+

Delete a grant from this JWT object.

+

Deletes the named grant from this object. It is not an error if there is no grant matching the passed name. If grant is NULL, then all grants are deleted from this JWT.

+
Parameters
+ + + +
jwtPointer to a JWT object.
grantString containing the name of the grant to delete. If this is NULL, then all grants are deleted.
+
+
+
Returns
Returns 0 on success, valid errno otherwise.
+ +
+
+ +

◆ jwt_get_grant()

+ +
+
+ + + + + + + + + + + +
const char * jwt_get_grant (const jwt_t * jwt,
const char * grant )
+
+

Return the value of a string grant.

+

Returns the string value for a grant (e.g. "iss"). If it does not exist, NULL will be returned.

+
Parameters
+ + + +
jwtPointer to a JWT object.
grantString containing the name of the grant to return a value for.
+
+
+
Returns
Returns a string for the value, or NULL when not found.
+
Remarks
This will only return grants with JSON string values. Use jwt_get_grants_json() to get the JSON representation of more complex values (e.g. arrays) or use jwt_get_grant_int() to get simple integer values.
+ +
+
+ +

◆ jwt_get_grant_bool()

+ +
+
+ + + + + + + + + + + +
int jwt_get_grant_bool (const jwt_t * jwt,
const char * grant )
+
+

Return the value of an boolean grant.

+

Returns the int value for a grant (e.g. "exp"). If it does not exist, 0 will be returned.

+
Parameters
+ + + +
jwtPointer to a JWT object.
grantString containing the name of the grant to return a value for.
+
+
+
Returns
Returns a boolean for the value. Sets errno to ENOENT when not found.
+
Remarks
This will only return grants with JSON boolean values. Use jwt_get_grants_json() to get the JSON representation of more complex values (e.g. arrays) or use jwt_get_grant() to get string values.
+ +
+
+ +

◆ jwt_get_grant_int()

+ +
+
+ + + + + + + + + + + +
long jwt_get_grant_int (const jwt_t * jwt,
const char * grant )
+
+

Return the value of an integer grant.

+

Returns the int value for a grant (e.g. "exp"). If it does not exist, 0 will be returned.

+
Parameters
+ + + +
jwtPointer to a JWT object.
grantString containing the name of the grant to return a value for.
+
+
+
Returns
Returns an int for the value. Sets errno to ENOENT when not found.
+
Remarks
This will only return grants with JSON integer values. Use jwt_get_grants_json() to get the JSON representation of more complex values (e.g. arrays) or use jwt_get_grant() to get string values.
+ +
+
+ +

◆ jwt_get_grants_json()

+ +
+
+ + + + + + + + + + + +
char * jwt_get_grants_json (const jwt_t * jwt,
const char * grant )
+
+

Return the value of a grant as JSON encoded object string.

+

Returns the JSON encoded string value for a grant (e.g. "iss"). If it does not exist, NULL will be returned.

+
Parameters
+ + + +
jwtPointer to a JWT object.
grantString containing the name of the grant to return a value for. If this is NULL, all grants will be returned as a JSON encoded hash.
+
+
+
Returns
Returns a string for the value, or NULL when not found. The returned string must be freed by the caller.
+ +
+
+
+
+ + + + + diff --git a/HEAD/group__jwt__grant__grp.js b/HEAD/group__jwt__grant__grp.js new file mode 100644 index 00000000..a683b956 --- /dev/null +++ b/HEAD/group__jwt__grant__grp.js @@ -0,0 +1,12 @@ +var group__jwt__grant__grp = +[ + [ "jwt_add_grant", "group__jwt__grant__grp.html#ga91d9bfe88d3e7b246ee67d2ed4609955", null ], + [ "jwt_add_grant_bool", "group__jwt__grant__grp.html#gad528aabec09be8d051485a45c49781c3", null ], + [ "jwt_add_grant_int", "group__jwt__grant__grp.html#gae4cdc69b259a0f2f98e3c07bf9038765", null ], + [ "jwt_add_grants_json", "group__jwt__grant__grp.html#ga0509c745c45eebb41c278cebb0617a55", null ], + [ "jwt_del_grants", "group__jwt__grant__grp.html#ga0d8da61021d73817fb71bf8ffb7791b7", null ], + [ "jwt_get_grant", "group__jwt__grant__grp.html#ga2131ddd80d3c138766573b87310bdb6e", null ], + [ "jwt_get_grant_bool", "group__jwt__grant__grp.html#ga782d0fb87e79706e705ea5a989024abd", null ], + [ "jwt_get_grant_int", "group__jwt__grant__grp.html#gafac50d1449e4ea9560258a68a07cfdda", null ], + [ "jwt_get_grants_json", "group__jwt__grant__grp.html#ga5445b6af33ae868b763ae8d2139c089f", null ] +]; \ No newline at end of file diff --git a/HEAD/group__jwt__grant__grp.map b/HEAD/group__jwt__grant__grp.map new file mode 100644 index 00000000..244aeb31 --- /dev/null +++ b/HEAD/group__jwt__grant__grp.map @@ -0,0 +1,4 @@ + + + + diff --git a/HEAD/group__jwt__grant__grp.md5 b/HEAD/group__jwt__grant__grp.md5 new file mode 100644 index 00000000..696a1952 --- /dev/null +++ b/HEAD/group__jwt__grant__grp.md5 @@ -0,0 +1 @@ +1899475d64bb96be913ce4da23961f5d \ No newline at end of file diff --git a/HEAD/group__jwt__grant__grp.svg b/HEAD/group__jwt__grant__grp.svg new file mode 100644 index 00000000..7df1341f --- /dev/null +++ b/HEAD/group__jwt__grant__grp.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + +Grant Management + + +Node1 + + +Grant Management + + + + + +Node2 + + +JSON Web Token + + + + + +Node2->Node1 + + + + + + + + + + diff --git a/HEAD/group__jwt__grant__grp_org.svg b/HEAD/group__jwt__grant__grp_org.svg new file mode 100644 index 00000000..78d6b649 --- /dev/null +++ b/HEAD/group__jwt__grant__grp_org.svg @@ -0,0 +1,36 @@ + + + + + + +Grant Management + + +Node1 + + +Grant Management + + + + + +Node2 + + +JSON Web Token + + + + + +Node2->Node1 + + + + + diff --git a/HEAD/group__jwt__grp.html b/HEAD/group__jwt__grp.html new file mode 100644 index 00000000..1c9cb1d6 --- /dev/null +++ b/HEAD/group__jwt__grp.html @@ -0,0 +1,126 @@ + + + + + + + + +LibJWT: JSON Web Token + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + +
+
LibJWT 2.9.99 +
+
The C JSON Web Token Library +JWK +JWKS
+
+
+ + + +
+
+ +
+
+
+ +
+
+ +
JSON Web Token
+
+
+
+Collaboration diagram for JSON Web Token:
+
+
+
+ + + + + + + + + + + + + + + + + + + + +

+Topics

 Object Creation
 
 Configuration Type
 
 Create a JWT
 
 Token Verification
 
 Grant Management
 
 Header Hanagement
 
 Encoding and Output
 
 Algorithm Management
 
 Validation Functions
 
+

Detailed Description

+
+
+ + + + + diff --git a/HEAD/group__jwt__grp.js b/HEAD/group__jwt__grp.js new file mode 100644 index 00000000..62b18aa1 --- /dev/null +++ b/HEAD/group__jwt__grp.js @@ -0,0 +1,12 @@ +var group__jwt__grp = +[ + [ "Object Creation", "group__jwt__core__grp.html", "group__jwt__core__grp" ], + [ "Configuration Type", "group__jwt__config__grp.html", "group__jwt__config__grp" ], + [ "Create a JWT", "group__jwt__create__grp.html", "group__jwt__create__grp" ], + [ "Token Verification", "group__jwt__verify__grp.html", "group__jwt__verify__grp" ], + [ "Grant Management", "group__jwt__grant__grp.html", "group__jwt__grant__grp" ], + [ "Header Hanagement", "group__jwt__header__grp.html", "group__jwt__header__grp" ], + [ "Encoding and Output", "group__jwt__encode__grp.html", "group__jwt__encode__grp" ], + [ "Algorithm Management", "group__jwt__alg__grp.html", "group__jwt__alg__grp" ], + [ "Validation Functions", "group__jwt__valid__grp.html", "group__jwt__valid__grp" ] +]; \ No newline at end of file diff --git a/HEAD/group__jwt__grp.map b/HEAD/group__jwt__grp.map new file mode 100644 index 00000000..5e8c2ec2 --- /dev/null +++ b/HEAD/group__jwt__grp.map @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/HEAD/group__jwt__grp.md5 b/HEAD/group__jwt__grp.md5 new file mode 100644 index 00000000..b1166044 --- /dev/null +++ b/HEAD/group__jwt__grp.md5 @@ -0,0 +1 @@ +943bd9980058277f06aae078cb5f888f \ No newline at end of file diff --git a/HEAD/group__jwt__grp.svg b/HEAD/group__jwt__grp.svg new file mode 100644 index 00000000..7dd198c1 --- /dev/null +++ b/HEAD/group__jwt__grp.svg @@ -0,0 +1,182 @@ + + + + + + + + + + + + +JSON Web Token + + +Node9 + + +Algorithm Management + + + + + +Node3 + + +Configuration Type + + + + + +Node2 + + +Object Creation + + + + + +Node4 + + +Create a JWT + + + + + +Node8 + + +Encoding and Output + + + + + +Node6 + + +Grant Management + + + + + +Node1 + + +JSON Web Token + + + + + +Node1->Node9 + + + + + +Node1->Node3 + + + + + +Node1->Node2 + + + + + +Node1->Node4 + + + + + +Node1->Node8 + + + + + +Node1->Node6 + + + + + +Node7 + + +Header Hanagement + + + + + +Node1->Node7 + + + + + +Node10 + + +Validation Functions + + + + + +Node1->Node10 + + + + + +Node5 + + +Token Verification + + + + + +Node1->Node5 + + + + + + + + + + diff --git a/HEAD/group__jwt__grp_org.svg b/HEAD/group__jwt__grp_org.svg new file mode 100644 index 00000000..fd120493 --- /dev/null +++ b/HEAD/group__jwt__grp_org.svg @@ -0,0 +1,156 @@ + + + + + + +JSON Web Token + + +Node9 + + +Algorithm Management + + + + + +Node3 + + +Configuration Type + + + + + +Node2 + + +Object Creation + + + + + +Node4 + + +Create a JWT + + + + + +Node8 + + +Encoding and Output + + + + + +Node6 + + +Grant Management + + + + + +Node1 + + +JSON Web Token + + + + + +Node1->Node9 + + + + + +Node1->Node3 + + + + + +Node1->Node2 + + + + + +Node1->Node4 + + + + + +Node1->Node8 + + + + + +Node1->Node6 + + + + + +Node7 + + +Header Hanagement + + + + + +Node1->Node7 + + + + + +Node10 + + +Validation Functions + + + + + +Node1->Node10 + + + + + +Node5 + + +Token Verification + + + + + +Node1->Node5 + + + + + diff --git a/HEAD/group__jwt__header__grp.html b/HEAD/group__jwt__header__grp.html new file mode 100644 index 00000000..877a15cb --- /dev/null +++ b/HEAD/group__jwt__header__grp.html @@ -0,0 +1,431 @@ + + + + + + + + +LibJWT: Header Hanagement + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + +
+
LibJWT 2.9.99 +
+
The C JSON Web Token Library +JWK +JWKS
+
+
+ + + +
+
+ +
+
+
+ +
+
+ +
Header Hanagement
+
+
+
+Collaboration diagram for Header Hanagement:
+
+
+
+ + + + + + + + + + + + + + + + + + + + +

+Functions

const char * jwt_get_header (const jwt_t *jwt, const char *header)
 
long jwt_get_header_int (const jwt_t *jwt, const char *header)
 
int jwt_get_header_bool (const jwt_t *jwt, const char *header)
 
char * jwt_get_headers_json (const jwt_t *jwt, const char *header)
 
int jwt_add_header (jwt_t *jwt, const char *header, const char *val)
 
int jwt_add_header_int (jwt_t *jwt, const char *header, long val)
 
int jwt_add_header_bool (jwt_t *jwt, const char *header, int val)
 
int jwt_add_headers_json (jwt_t *jwt, const char *json)
 
int jwt_del_headers (jwt_t *jwt, const char *header)
 
+

Detailed Description

+

These functions allow you to add, remove and retrieve headers from a JWT object.

+

Function Documentation

+ +

◆ jwt_add_header()

+ +
+
+ + + + + + + + + + + + + + + + +
int jwt_add_header (jwt_t * jwt,
const char * header,
const char * val )
+
+

Add a new string header to this JWT object.

+

Creates a new header for this object. The string for header and val are copied internally, so do not require that the pointer or string remain valid for the lifetime of this object. It is an error if you try to add a header that already exists.

+
Parameters
+ + + + +
jwtPointer to a JWT object.
headerString containing the name of the header to add.
valString containing the value to be saved for header. Can be an empty string, but cannot be NULL.
+
+
+
Returns
Returns 0 on success, valid errno otherwise.
+
Remarks
This only allows for string based headers. If you wish to add integer headers, then use jwt_add_header_int(). If you wish to add more complex headers (e.g. an array), then use jwt_add_headers_json().
+ +
+
+ +

◆ jwt_add_header_bool()

+ +
+
+ + + + + + + + + + + + + + + + +
int jwt_add_header_bool (jwt_t * jwt,
const char * header,
int val )
+
+

Add a new boolean header to this JWT object.

+

Creates a new header for this object. The string for header is copied internally, so do not require that the pointer or string remain valid for the lifetime of this object. It is an error if you try to add a header that already exists.

+
Parameters
+ + + + +
jwtPointer to a JWT object.
headerString containing the name of the header to add.
valboolean containing the value to be saved for header.
+
+
+
Returns
Returns 0 on success, valid errno otherwise.
+
Remarks
This only allows for boolean based headers. If you wish to add string headers, then use jwt_add_header(). If you wish to add more complex headers (e.g. an array), then use jwt_add_headers_json().
+ +
+
+ +

◆ jwt_add_header_int()

+ +
+
+ + + + + + + + + + + + + + + + +
int jwt_add_header_int (jwt_t * jwt,
const char * header,
long val )
+
+

Add a new integer header to this JWT object.

+

Creates a new header for this object. The string for header is copied internally, so do not require that the pointer or string remain valid for the lifetime of this object. It is an error if you try to add a header that already exists.

+
Parameters
+ + + + +
jwtPointer to a JWT object.
headerString containing the name of the header to add.
valint containing the value to be saved for header.
+
+
+
Returns
Returns 0 on success, valid errno otherwise.
+
Remarks
This only allows for integer based headers. If you wish to add string headers, then use jwt_add_header(). If you wish to add more complex headers (e.g. an array), then use jwt_add_headers_json().
+ +
+
+ +

◆ jwt_add_headers_json()

+ +
+
+ + + + + + + + + + + +
int jwt_add_headers_json (jwt_t * jwt,
const char * json )
+
+

Add headers from a JSON encoded object string.

+

Loads a header from an existing JSON encoded object string. Overwrites existing header. If header is NULL, then the JSON encoded string is assumed to be a JSON hash of all headers being added and will be merged into the header listing.

+
Parameters
+ + + +
jwtPointer to a JWT object.
jsonString containing a JSON encoded object of headers.
+
+
+
Returns
Returns 0 on success, valid errno otherwise.
+ +
+
+ +

◆ jwt_del_headers()

+ +
+
+ + + + + + + + + + + +
int jwt_del_headers (jwt_t * jwt,
const char * header )
+
+

Delete a header from this JWT object.

+

Deletes the named header from this object. It is not an error if there is no header matching the passed name. If header is NULL, then all headers are deleted from this JWT.

+
Parameters
+ + + +
jwtPointer to a JWT object.
headerString containing the name of the header to delete. If this is NULL, then all headers are deleted.
+
+
+
Returns
Returns 0 on success, valid errno otherwise.
+ +
+
+ +

◆ jwt_get_header()

+ +
+
+ + + + + + + + + + + +
const char * jwt_get_header (const jwt_t * jwt,
const char * header )
+
+

Return the value of a string header.

+

Returns the string value for a header (e.g. ""). If it does not exist, NULL will be returned.

+
Parameters
+ + + +
jwtPointer to a JWT object.
headerString containing the name of the header to return a value for.
+
+
+
Returns
Returns a string for the value, or NULL when not found.
+
Remarks
This will only return headers with JSON string values. Use jwt_get_header_json() to get the JSON representation of more complex values (e.g. arrays) or use jwt_get_header_int() to get simple integer values.
+ +
+
+ +

◆ jwt_get_header_bool()

+ +
+
+ + + + + + + + + + + +
int jwt_get_header_bool (const jwt_t * jwt,
const char * header )
+
+

Return the value of an boolean header.

+

Returns the int value for a header (e.g. ""). If it does not exist, 0 will be returned.

+
Parameters
+ + + +
jwtPointer to a JWT object.
headerString containing the name of the header to return a value for.
+
+
+
Returns
Returns a boolean for the value. Sets errno to ENOENT when not found.
+
Remarks
This will only return headers with JSON boolean values. Use jwt_get_header_json() to get the JSON representation of more complex values (e.g. arrays) or use jwt_get_header() to get string values.
+ +
+
+ +

◆ jwt_get_header_int()

+ +
+
+ + + + + + + + + + + +
long jwt_get_header_int (const jwt_t * jwt,
const char * header )
+
+

Return the value of an integer header.

+

Returns the int value for a header (e.g. ""). If it does not exist, 0 will be returned.

+
Parameters
+ + + +
jwtPointer to a JWT object.
headerString containing the name of the header to return a value for.
+
+
+
Returns
Returns an int for the value. Sets errno to ENOENT when not found.
+
Remarks
This will only return headers with JSON integer values. Use jwt_get_header_json() to get the JSON representation of more complex values (e.g. arrays) or use jwt_get_header() to get string values.
+ +
+
+ +

◆ jwt_get_headers_json()

+ +
+
+ + + + + + + + + + + +
char * jwt_get_headers_json (const jwt_t * jwt,
const char * header )
+
+

Return the value of a header as JSON encoded object string.

+

Returns the JSON encoded string value for a header (e.g. ""). If it does not exist, NULL will be returned.

+
Parameters
+ + + +
jwtPointer to a JWT object.
headerString containing the name of the header to return a value for. If this is NULL, all headers will be returned as a JSON encoded hash.
+
+
+
Returns
Returns a string for the value, or NULL when not found. The returned string must be freed by the caller.
+ +
+
+
+
+ + + + + diff --git a/HEAD/group__jwt__header__grp.js b/HEAD/group__jwt__header__grp.js new file mode 100644 index 00000000..56030154 --- /dev/null +++ b/HEAD/group__jwt__header__grp.js @@ -0,0 +1,12 @@ +var group__jwt__header__grp = +[ + [ "jwt_add_header", "group__jwt__header__grp.html#ga1c0093b5b221f78bbd4e102d30870a27", null ], + [ "jwt_add_header_bool", "group__jwt__header__grp.html#ga337c85106951204af60f40288da0a7d8", null ], + [ "jwt_add_header_int", "group__jwt__header__grp.html#ga250b312a2c57fa5664f3921950c88766", null ], + [ "jwt_add_headers_json", "group__jwt__header__grp.html#ga283f3dc2d9686e4db73874ae9f446ac1", null ], + [ "jwt_del_headers", "group__jwt__header__grp.html#gae5ebb283a76098e9a99f7fe13b18593f", null ], + [ "jwt_get_header", "group__jwt__header__grp.html#ga5563efb29cdd4bfe078a3e82fa6a873a", null ], + [ "jwt_get_header_bool", "group__jwt__header__grp.html#gacb7171946cdc613c4800c1c0b279af4b", null ], + [ "jwt_get_header_int", "group__jwt__header__grp.html#gaadd81a21b63c01106bd7943992687199", null ], + [ "jwt_get_headers_json", "group__jwt__header__grp.html#ga7fffc3eb52315f54605dad87fbdb52cb", null ] +]; \ No newline at end of file diff --git a/HEAD/group__jwt__header__grp.map b/HEAD/group__jwt__header__grp.map new file mode 100644 index 00000000..154e133f --- /dev/null +++ b/HEAD/group__jwt__header__grp.map @@ -0,0 +1,4 @@ + + + + diff --git a/HEAD/group__jwt__header__grp.md5 b/HEAD/group__jwt__header__grp.md5 new file mode 100644 index 00000000..25db7ca0 --- /dev/null +++ b/HEAD/group__jwt__header__grp.md5 @@ -0,0 +1 @@ +e2aace07ec16dc9b65f6f862bf9a3449 \ No newline at end of file diff --git a/HEAD/group__jwt__header__grp.svg b/HEAD/group__jwt__header__grp.svg new file mode 100644 index 00000000..a233c4eb --- /dev/null +++ b/HEAD/group__jwt__header__grp.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + +Header Hanagement + + +Node2 + + +JSON Web Token + + + + + +Node1 + + +Header Hanagement + + + + + +Node2->Node1 + + + + + + + + + + diff --git a/HEAD/group__jwt__header__grp_org.svg b/HEAD/group__jwt__header__grp_org.svg new file mode 100644 index 00000000..364a6eb4 --- /dev/null +++ b/HEAD/group__jwt__header__grp_org.svg @@ -0,0 +1,36 @@ + + + + + + +Header Hanagement + + +Node2 + + +JSON Web Token + + + + + +Node1 + + +Header Hanagement + + + + + +Node2->Node1 + + + + + diff --git a/HEAD/group__jwt__memory__grp.html b/HEAD/group__jwt__memory__grp.html new file mode 100644 index 00000000..b1015e00 --- /dev/null +++ b/HEAD/group__jwt__memory__grp.html @@ -0,0 +1,250 @@ + + + + + + + + +LibJWT: Memory Handlers + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + +
+
LibJWT 2.9.99 +
+
The C JSON Web Token Library +JWK +JWKS
+
+
+ + + +
+
+ +
+
+
+ +
+
+ +
Memory Handlers
+
+
+
+Collaboration diagram for Memory Handlers:
+
+
+
+ + + + + + + + + + + +

+Typedefs

typedef void *(* jwt_malloc_t) (size_t)
 Prototype for malloc(3)
 
typedef void *(* jwt_realloc_t) (void *, size_t)
 Prototype for realloc(3)
 
typedef void(* jwt_free_t) (void *)
 Prototype for free(3)
 
+ + + + + +

+Functions

int jwt_set_alloc (jwt_malloc_t pmalloc, jwt_realloc_t prealloc, jwt_free_t pfree)
 
void jwt_get_alloc (jwt_malloc_t *pmalloc, jwt_realloc_t *prealloc, jwt_free_t *pfree)
 
+

Detailed Description

+

These functions allow you to get or set memory allocation functions.

+

Typedef Documentation

+ +

◆ jwt_free_t

+ +
+
+ + + + +
typedef void(* jwt_free_t) (void *)
+
+ +

Prototype for free(3)

+ +
+
+ +

◆ jwt_malloc_t

+ +
+
+ + + + +
typedef void *(* jwt_malloc_t) (size_t)
+
+ +

Prototype for malloc(3)

+ +
+
+ +

◆ jwt_realloc_t

+ +
+
+ + + + +
typedef void *(* jwt_realloc_t) (void *, size_t)
+
+ +

Prototype for realloc(3)

+ +
+
+

Function Documentation

+ +

◆ jwt_get_alloc()

+ +
+
+ + + + + + + + + + + + + + + + +
void jwt_get_alloc (jwt_malloc_t * pmalloc,
jwt_realloc_t * prealloc,
jwt_free_t * pfree )
+
+

Get functions used for allocating and freeing memory.

+
Parameters
+ + + + +
pmallocPointer to malloc function output variable, or NULL
preallocPointer to realloc function output variable, or NULL
pfreePointer to free function output variable, or NULL
+
+
+ +
+
+ +

◆ jwt_set_alloc()

+ +
+
+ + + + + + + + + + + + + + + + +
int jwt_set_alloc (jwt_malloc_t pmalloc,
jwt_realloc_t prealloc,
jwt_free_t pfree )
+
+

Set functions to be used for allocating and freeing memory.

+

By default, LibJWT uses malloc, realloc, and free for memory management. This function allows the user of the library to specify its own memory management functions. This is especially useful on Windows where mismatches in runtimes across DLLs can cause problems.

+

The caller can specify either a valid function pointer for any of the parameters or NULL to use the corresponding default allocator function.

+

Note that this function will also set the memory allocator for the Jansson library.

+
Parameters
+ + + + +
pmallocThe function to use for allocating memory or NULL to use malloc
preallocThe function to use for reallocating memory or NULL to use realloc
pfreeThe function to use for freeing memory or NULL to use free
+
+
+
Returns
0 on success or errno otherwise.
+ +
+
+
+
+ + + + + diff --git a/HEAD/group__jwt__memory__grp.js b/HEAD/group__jwt__memory__grp.js new file mode 100644 index 00000000..0f057be2 --- /dev/null +++ b/HEAD/group__jwt__memory__grp.js @@ -0,0 +1,8 @@ +var group__jwt__memory__grp = +[ + [ "jwt_free_t", "group__jwt__memory__grp.html#gad3b3fc88482616b2883b786d0700c377", null ], + [ "jwt_malloc_t", "group__jwt__memory__grp.html#gae64b1a91c7e44845d2422f5a6bfa7972", null ], + [ "jwt_realloc_t", "group__jwt__memory__grp.html#gaee904814d9c9266c68d2abff08fbc5af", null ], + [ "jwt_get_alloc", "group__jwt__memory__grp.html#ga8d96db3450596f2e38b549bd8f0e4ec5", null ], + [ "jwt_set_alloc", "group__jwt__memory__grp.html#ga1d0fb7527d55f7849d2195ff8fd28344", null ] +]; \ No newline at end of file diff --git a/HEAD/group__jwt__memory__grp.map b/HEAD/group__jwt__memory__grp.map new file mode 100644 index 00000000..b56dc744 --- /dev/null +++ b/HEAD/group__jwt__memory__grp.map @@ -0,0 +1,4 @@ + + + + diff --git a/HEAD/group__jwt__memory__grp.md5 b/HEAD/group__jwt__memory__grp.md5 new file mode 100644 index 00000000..86431224 --- /dev/null +++ b/HEAD/group__jwt__memory__grp.md5 @@ -0,0 +1 @@ +e3b2fa2caa199f84087608fd7f220a52 \ No newline at end of file diff --git a/HEAD/group__jwt__memory__grp.svg b/HEAD/group__jwt__memory__grp.svg new file mode 100644 index 00000000..b8371639 --- /dev/null +++ b/HEAD/group__jwt__memory__grp.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + +Memory Handlers + + +Node2 + + +Advanced Functionality + + + + + +Node1 + + +Memory Handlers + + + + + +Node2->Node1 + + + + + + + + + + diff --git a/HEAD/group__jwt__memory__grp_org.svg b/HEAD/group__jwt__memory__grp_org.svg new file mode 100644 index 00000000..6632200d --- /dev/null +++ b/HEAD/group__jwt__memory__grp_org.svg @@ -0,0 +1,36 @@ + + + + + + +Memory Handlers + + +Node2 + + +Advanced Functionality + + + + + +Node1 + + +Memory Handlers + + + + + +Node2->Node1 + + + + + diff --git a/HEAD/group__jwt__valid__grp.html b/HEAD/group__jwt__valid__grp.html new file mode 100644 index 00000000..746132b0 --- /dev/null +++ b/HEAD/group__jwt__valid__grp.html @@ -0,0 +1,856 @@ + + + + + + + + +LibJWT: Validation Functions + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + +
+
LibJWT 2.9.99 +
+
The C JSON Web Token Library +JWK +JWKS
+
+
+ + + +
+
+ +
+
+
+ +
+
+ +
Validation Functions
+
+
+
+Collaboration diagram for Validation Functions:
+
+
+
+ + + + + +

+Typedefs

typedef struct jwt_valid jwt_valid_t
 Opaque JWT Validation object.
 
+ + + + +

+Enumerations

enum  jwt_valid_exception_t {
+  JWT_VALIDATION_SUCCESS = 0x0000 +, JWT_VALIDATION_ERROR = 0x0001 +, JWT_VALIDATION_ALG_MISMATCH = 0x0002 +, JWT_VALIDATION_EXPIRED = 0x0004 +,
+  JWT_VALIDATION_TOO_NEW = 0x0008 +, JWT_VALIDATION_ISS_MISMATCH = 0x0010 +, JWT_VALIDATION_SUB_MISMATCH = 0x0020 +, JWT_VALIDATION_AUD_MISMATCH = 0x0040 +,
+  JWT_VALIDATION_GRANT_MISSING = 0x0080 +, JWT_VALIDATION_GRANT_MISMATCH = 0x0100 +
+ }
 Validation exception types for jwt_t objects. More...
 
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+Functions

jwt_valid_exception_t jwt_validate (jwt_t *jwt, jwt_valid_t *jwt_valid)
 
int jwt_valid_new (jwt_valid_t **jwt_valid, jwt_alg_t alg)
 
void jwt_valid_free (jwt_valid_t *jwt_valid)
 
jwt_valid_exception_t jwt_valid_get_status (jwt_valid_t *jwt_valid)
 
time_t jwt_valid_get_nbf_leeway (jwt_valid_t *jwt_valid)
 
time_t jwt_valid_get_exp_leeway (jwt_valid_t *jwt_valid)
 
int jwt_valid_add_grant (jwt_valid_t *jwt_valid, const char *grant, const char *val)
 
const char * jwt_valid_get_grant (jwt_valid_t *jwt_valid, const char *grant)
 
int jwt_valid_add_grant_int (jwt_valid_t *jwt_valid, const char *grant, long val)
 
long jwt_valid_get_grant_int (jwt_valid_t *jwt_valid, const char *grant)
 
int jwt_valid_add_grant_bool (jwt_valid_t *jwt_valid, const char *grant, int val)
 
int jwt_valid_get_grant_bool (jwt_valid_t *jwt_valid, const char *grant)
 
int jwt_valid_add_grants_json (jwt_valid_t *jwt_valid, const char *json)
 
char * jwt_valid_get_grants_json (jwt_valid_t *jwt_valid, const char *grant)
 
int jwt_valid_del_grants (jwt_valid_t *jwt_valid, const char *grant)
 
int jwt_valid_set_now (jwt_valid_t *jwt_valid, const time_t now)
 
int jwt_valid_set_nbf_leeway (jwt_valid_t *jwt_valid, const time_t nbf_leeway)
 
int jwt_valid_set_exp_leeway (jwt_valid_t *jwt_valid, const time_t exp_leeway)
 
int jwt_valid_set_headers (jwt_valid_t *jwt_valid, int hdr)
 
char * jwt_exception_str (jwt_valid_exception_t exceptions)
 
+

Detailed Description

+

These functions allow you to define requirements for JWT validation.

+

The most basic validation is that the JWT uses the expected algorithm.

+

When replicating claims in header (usually for encrypted JWT), validation tests that they match claims in the body (iss, sub, aud).

+

Time-based claims can also be validated (nbf, exp).

+

Finally, validation can test that claims be present and have certain value.

+

Typedef Documentation

+ +

◆ jwt_valid_t

+ +
+
+ + + + +
typedef struct jwt_valid jwt_valid_t
+
+ +

Opaque JWT Validation object.

+

Used in the JWT validation functions.

+ +
+
+

Enumeration Type Documentation

+ +

◆ jwt_valid_exception_t

+ +
+
+ + + + +
enum jwt_valid_exception_t
+
+ +

Validation exception types for jwt_t objects.

+

These are bitwise values that allow you to check for exceptions when using the jwt_valid_t

+
Todo

📄 RFC-7519 Sec 4.1.6 "iat" Issued At

+

📄 RFC-7519 Sec 4.1.7 "jti" JWT ID

+
+ + + + + + + + + + + +
Enumerator
JWT_VALIDATION_SUCCESS 

Validation succeeded

+
JWT_VALIDATION_ERROR 

General failures

+
JWT_VALIDATION_ALG_MISMATCH 

📄 RFC-7518 Sec 3.1 "alg" Algorithm

+
JWT_VALIDATION_EXPIRED 

📄 RFC-7519 Sec 4.1.4 "exp" Expired

+
JWT_VALIDATION_TOO_NEW 

📄 RFC-7519 Sec 4.1.5 "nbf" Not Before

+
JWT_VALIDATION_ISS_MISMATCH 

📄 RFC-7519 Sec 4.1.1 "iss" Issuer

+
JWT_VALIDATION_SUB_MISMATCH 

📄 RFC-7519 Sec 4.1.2 "sub" Subject

+
JWT_VALIDATION_AUD_MISMATCH 

📄 RFC-7519 Sec 4.1.3 "aud" Audience

+
JWT_VALIDATION_GRANT_MISSING 

User-defined Grant missing

+
JWT_VALIDATION_GRANT_MISMATCH 

User-defined Grant mismatch

+
+ +
+
+

Function Documentation

+ +

◆ jwt_exception_str()

+ +
+
+ + + + + + + +
char * jwt_exception_str (jwt_valid_exception_t exceptions)
+
+

Parses exceptions and returns a comma delimited and human-readable string.

+

The returned string must be freed by the caller. If you changed the allocation method using jwt_set_alloc, then you must use jwt_free_str() to free the memory.

+
Remarks
This string is currently en-US ASCII only. Language support will come in the future.
+
Parameters
+ + +
exceptionsInteger containing the exception flags.
+
+
+
Returns
A null terminated string on success, NULL on error with errno set appropriately.
+ +
+
+ +

◆ jwt_valid_add_grant()

+ +
+
+ + + + + + + + + + + + + + + + +
int jwt_valid_add_grant (jwt_valid_t * jwt_valid,
const char * grant,
const char * val )
+
+

Add a new string grant requirement to this JWT validation object.

+
Parameters
+ + + + +
jwt_validPointer to a JWT validation object.
grantString containing the name of the grant to add.
valString containing the value to be saved for grant. Can be an empty string, but cannot be NULL.
+
+
+
Returns
Returns 0 on success, valid errno otherwise.
+

Note, this only allows for string based grants. If you wish to add integer grants, then use jwt_valid_add_grant_int(). If you wish to add more complex grants (e.g. an array), then use jwt_valid_add_grants_json().

+ +
+
+ +

◆ jwt_valid_add_grant_bool()

+ +
+
+ + + + + + + + + + + + + + + + +
int jwt_valid_add_grant_bool (jwt_valid_t * jwt_valid,
const char * grant,
int val )
+
+

Add a new boolean required grant to this JWT validation object.

+

Creates a new grant for this object. The string for grant is copied internally, so do not require that the pointer or string remain valid for the lifetime of this object. It is an error if you try to add a grant that already exists.

+
Parameters
+ + + + +
jwt_validPointer to a JWT validation object.
grantString containing the name of the grant to add.
valboolean containing the value to be saved for grant.
+
+
+
Returns
Returns 0 on success, valid errno otherwise.
+
Remarks
This only allows for boolean based grants. If you wish to add string grants, then use jwt_valid_add_grant(). If you wish to add more complex grants (e.g. an array), then use jwt_valid_add_grants_json().
+ +
+
+ +

◆ jwt_valid_add_grant_int()

+ +
+
+ + + + + + + + + + + + + + + + +
int jwt_valid_add_grant_int (jwt_valid_t * jwt_valid,
const char * grant,
long val )
+
+

Add a new integer grant requirement to this JWT validation object.

+
Parameters
+ + + + +
jwt_validPointer to a JWT validation object.
grantString containing the name of the grant to add.
valint containing the value to be saved for grant.
+
+
+
Returns
Returns 0 on success, valid errno otherwise.
+
Remarks
This only allows for integer based grants. If you wish to add string grants, then use jwt_valid_add_grant(). If you wish to add more complex grants (e.g. an array), then use jwt_valid_add_grants_json().
+ +
+
+ +

◆ jwt_valid_add_grants_json()

+ +
+
+ + + + + + + + + + + +
int jwt_valid_add_grants_json (jwt_valid_t * jwt_valid,
const char * json )
+
+

Add required grants from a JSON encoded object string.

+

Loads a grant from an existing JSON encoded object string. Overwrites existing grant.

+
Parameters
+ + + +
jwt_validPointer to a JWT validation object.
jsonString containing a JSON encoded object of grants.
+
+
+
Returns
Returns 0 on success, valid errno otherwise.
+ +
+
+ +

◆ jwt_valid_del_grants()

+ +
+
+ + + + + + + + + + + +
int jwt_valid_del_grants (jwt_valid_t * jwt_valid,
const char * grant )
+
+

Delete a grant from this JWT object.

+

Deletes the named grant from this object. It is not an error if there is no grant matching the passed name. If grant is NULL, then all grants are deleted from this JWT.

+
Parameters
+ + + +
jwt_validPointer to a JWT validation object.
grantString containing the name of the grant to delete. If this is NULL, then all grants are deleted.
+
+
+
Returns
Returns 0 on success, valid errno otherwise.
+ +
+
+ +

◆ jwt_valid_free()

+ +
+
+ + + + + + + +
void jwt_valid_free (jwt_valid_t * jwt_valid)
+
+

Free a JWT validation object and any other resources it is using.

+

After calling, the JWT validation object referenced will no longer be valid and its memory will be freed.

+
Parameters
+ + +
jwt_validPointer to a JWT validation object previously created with jwt_valid_new().
+
+
+ +
+
+ +

◆ jwt_valid_get_exp_leeway()

+ +
+
+ + + + + + + +
time_t jwt_valid_get_exp_leeway (jwt_valid_t * jwt_valid)
+
+

Return the exp_leeway value set.

+
Parameters
+ + +
jwt_validPointer to a JWT validation object.
+
+
+
Returns
Returns current exp_leeway value
+ +
+
+ +

◆ jwt_valid_get_grant()

+ +
+
+ + + + + + + + + + + +
const char * jwt_valid_get_grant (jwt_valid_t * jwt_valid,
const char * grant )
+
+

Return the value of a string required grant.

+

Returns the string value for a grant (e.g. "iss"). If it does not exist, NULL will be returned.

+
Parameters
+ + + +
jwt_validPointer to a JWT validation object.
grantString containing the name of the grant to return a value for.
+
+
+
Returns
Returns a string for the value, or NULL when not found.
+

Note, this will only return grants with JSON string values. Use jwt_valid_get_grants_json() to get the JSON representation of more complex values (e.g. arrays) or use jwt_valid_get_grant_int() to get simple integer values.

+ +
+
+ +

◆ jwt_valid_get_grant_bool()

+ +
+
+ + + + + + + + + + + +
int jwt_valid_get_grant_bool (jwt_valid_t * jwt_valid,
const char * grant )
+
+

Return the value of an boolean required grant.

+

Returns the int value for a grant (e.g. "exp"). If it does not exist, 0 will be returned.

+
Parameters
+ + + +
jwt_validPointer to a JWT validation object.
grantString containing the name of the grant to return a value for.
+
+
+
Returns
Returns a boolean for the value. Sets errno to ENOENT when not found.
+
Remarks
This will only return grants with JSON boolean values. Use jwt_valid_get_grants_json() to get the JSON representation of more complex values (e.g. arrays) or use jwt_valid_get_grant() to get string values.
+ +
+
+ +

◆ jwt_valid_get_grant_int()

+ +
+
+ + + + + + + + + + + +
long jwt_valid_get_grant_int (jwt_valid_t * jwt_valid,
const char * grant )
+
+

Return the value of an integer required grant.

+

Returns the int value for a grant (e.g. "exp"). If it does not exist, 0 will be returned.

+
Parameters
+ + + +
jwt_validPointer to a JWT validation object.
grantString containing the name of the grant to return a value for.
+
+
+
Returns
Returns an int for the value. Sets errno to ENOENT when not found.
+
Remarks
This will only return grants with JSON integer values. Use jwt_valid_get_grants_json() to get the JSON representation of more complex values (e.g. arrays) or use jwt_valid_get_grant() to get string values.
+ +
+
+ +

◆ jwt_valid_get_grants_json()

+ +
+
+ + + + + + + + + + + +
char * jwt_valid_get_grants_json (jwt_valid_t * jwt_valid,
const char * grant )
+
+

Return the value of a grant as JSON encoded object string.

+

Returns the JSON encoded string value for a grant (e.g. "iss"). If it does not exist, NULL will be returned.

+
Parameters
+ + + +
jwt_validPointer to a JWT validation object.
grantString containing the name of the grant to return a value for.
+
+
+
Returns
Returns a string for the value, or NULL when not found. The returned string must be freed by the caller.
+ +
+
+ +

◆ jwt_valid_get_nbf_leeway()

+ +
+
+ + + + + + + +
time_t jwt_valid_get_nbf_leeway (jwt_valid_t * jwt_valid)
+
+

Return the nbf_leeway value set.

+
Parameters
+ + +
jwt_validPointer to a JWT validation object.
+
+
+
Returns
Returns current nbf_leeway value
+ +
+
+ +

◆ jwt_valid_get_status()

+ +
+
+ + + + + + + +
jwt_valid_exception_t jwt_valid_get_status (jwt_valid_t * jwt_valid)
+
+

Return the status string for the validation object.

+

The status of validation object is primarily for describing the reason jwt_validate() failed.

+
Parameters
+ + +
jwt_validPointer to a JWT validation object.
+
+
+
Returns
Returns current validation status as a bitwise OR of possible errors, or 0 if validation is currently successful.
+ +
+
+ +

◆ jwt_valid_new()

+ +
+
+ + + + + + + + + + + +
int jwt_valid_new (jwt_valid_t ** jwt_valid,
jwt_alg_t alg )
+
+

Allocate a new, JWT validation object.

+

This is used to create a new object for a JWT validation. After you have finished with the object, use jwt_valid_free() to clean up the memory used by it.

+
Parameters
+ + + +
jwt_validPointer to a JWT validation object pointer. Will be allocated on success.
algA valid jwt_alg_t specifier.
+
+
+
Returns
0 on success, valid errno otherwise.
+ +
+
+ +

◆ jwt_valid_set_exp_leeway()

+ +
+
+ + + + + + + + + + + +
int jwt_valid_set_exp_leeway (jwt_valid_t * jwt_valid,
const time_t exp_leeway )
+
+

Set the exp_leeway value as defined in: https://www.rfc-editor.org/rfc/rfc7519#section-4.1.4.

+
Parameters
+ + + +
jwt_validPointer to a JWT validation object.
exp_leewayleeway for exp value.
+
+
+
Returns
Returns 0 on success, valid errno otherwise.
+ +
+
+ +

◆ jwt_valid_set_headers()

+ +
+
+ + + + + + + + + + + +
int jwt_valid_set_headers (jwt_valid_t * jwt_valid,
int hdr )
+
+

Set validation for replicated claims in headers.

+

When set, validation tests for presence of iss, sub, aud in jwt headers and tests match for same claims in body.

+
Parameters
+ + + +
jwt_validPointer to a JWT validation object.
hdrWhen true, test header claims
+
+
+
Returns
Returns 0 on success, valid errno otherwise.
+
Remarks
jwt_validate() will not fail if iss, sub, aud are not present in JWT header or body.
+ +
+
+ +

◆ jwt_valid_set_nbf_leeway()

+ +
+
+ + + + + + + + + + + +
int jwt_valid_set_nbf_leeway (jwt_valid_t * jwt_valid,
const time_t nbf_leeway )
+
+

Set the nbf_leeway value as defined in: https://www.rfc-editor.org/rfc/rfc7519#section-4.1.5.

+
Parameters
+ + + +
jwt_validPointer to a JWT validation object.
nbf_leewayleeway for nbf value.
+
+
+
Returns
Returns 0 on success, valid errno otherwise.
+ +
+
+ +

◆ jwt_valid_set_now()

+ +
+
+ + + + + + + + + + + +
int jwt_valid_set_now (jwt_valid_t * jwt_valid,
const time_t now )
+
+

Set the time for which expires and not-before claims should be evaluated.

+
Parameters
+ + + +
jwt_validPointer to a JWT validation object.
nowTime to use when considering nbf and exp claims.
+
+
+
Returns
Returns 0 on success, valid errno otherwise.
+
Remarks
jwt_validate() will not fail based on time if no expires or not-before claims exist in a JWT object.
+ +
+
+ +

◆ jwt_validate()

+ +
+
+ + + + + + + + + + + +
jwt_valid_exception_t jwt_validate (jwt_t * jwt,
jwt_valid_t * jwt_valid )
+
+

Validate a JWT object with a validation object.

+
Parameters
+ + + +
jwtPointer to a JWT object.
jwt_validPointer to a JWT validation object.
+
+
+
Returns
bitwide OR if possible validation errors or 0 on success
+ +
+
+
+
+ + + + + diff --git a/HEAD/group__jwt__valid__grp.js b/HEAD/group__jwt__valid__grp.js new file mode 100644 index 00000000..49f4a7d3 --- /dev/null +++ b/HEAD/group__jwt__valid__grp.js @@ -0,0 +1,36 @@ +var group__jwt__valid__grp = +[ + [ "jwt_valid_t", "group__jwt__valid__grp.html#gac413377fbfc11671e8a8a66f45693e5d", null ], + [ "jwt_valid_exception_t", "group__jwt__valid__grp.html#ga47ee94f9c08ff581f78590bfe024f6e4", [ + [ "JWT_VALIDATION_SUCCESS", "group__jwt__valid__grp.html#gga47ee94f9c08ff581f78590bfe024f6e4a94bb9121f597e706e89322b0f9a7330b", null ], + [ "JWT_VALIDATION_ERROR", "group__jwt__valid__grp.html#gga47ee94f9c08ff581f78590bfe024f6e4ad2e58b1f8a8bc39518b1c973fa99ee6e", null ], + [ "JWT_VALIDATION_ALG_MISMATCH", "group__jwt__valid__grp.html#gga47ee94f9c08ff581f78590bfe024f6e4abf0dc5dd94b521a118485a9522cd8cea", null ], + [ "JWT_VALIDATION_EXPIRED", "group__jwt__valid__grp.html#gga47ee94f9c08ff581f78590bfe024f6e4a698be61a7f4bcf0dfd9894e1c6296e0b", null ], + [ "JWT_VALIDATION_TOO_NEW", "group__jwt__valid__grp.html#gga47ee94f9c08ff581f78590bfe024f6e4a685c5270c214bf2484b65376e4b7459b", null ], + [ "JWT_VALIDATION_ISS_MISMATCH", "group__jwt__valid__grp.html#gga47ee94f9c08ff581f78590bfe024f6e4a0dde2793247cd11fbf017212d8fd605f", null ], + [ "JWT_VALIDATION_SUB_MISMATCH", "group__jwt__valid__grp.html#gga47ee94f9c08ff581f78590bfe024f6e4ac8b2989c55deb3bdad631c61f1ed304d", null ], + [ "JWT_VALIDATION_AUD_MISMATCH", "group__jwt__valid__grp.html#gga47ee94f9c08ff581f78590bfe024f6e4acb9d53e3cc20a39c0827d77a071271e6", null ], + [ "JWT_VALIDATION_GRANT_MISSING", "group__jwt__valid__grp.html#gga47ee94f9c08ff581f78590bfe024f6e4a94eb7551ec626004a55798ecbd563d38", null ], + [ "JWT_VALIDATION_GRANT_MISMATCH", "group__jwt__valid__grp.html#gga47ee94f9c08ff581f78590bfe024f6e4a5b1db073c57082b557592b7115a8e363", null ] + ] ], + [ "jwt_exception_str", "group__jwt__valid__grp.html#ga8371a7299dab709fa28fba7271d03a14", null ], + [ "jwt_valid_add_grant", "group__jwt__valid__grp.html#gab5e9e2dc5e0276ea2c21c050822c19e1", null ], + [ "jwt_valid_add_grant_bool", "group__jwt__valid__grp.html#gafa57cedcecda005a47161c462b2f0c9f", null ], + [ "jwt_valid_add_grant_int", "group__jwt__valid__grp.html#ga50c04450cc57bcd18717c25f18aed479", null ], + [ "jwt_valid_add_grants_json", "group__jwt__valid__grp.html#ga9361112f9372d8de2f11c115314c2d71", null ], + [ "jwt_valid_del_grants", "group__jwt__valid__grp.html#ga929d87f2577cbb600de468b159d9f00d", null ], + [ "jwt_valid_free", "group__jwt__valid__grp.html#ga77db16a4a29ef6a607d6aff8012955b2", null ], + [ "jwt_valid_get_exp_leeway", "group__jwt__valid__grp.html#ga0dc43278963ec6474d5d7263c960e69c", null ], + [ "jwt_valid_get_grant", "group__jwt__valid__grp.html#ga03b176d20c41a85a979c9671a9a02b6f", null ], + [ "jwt_valid_get_grant_bool", "group__jwt__valid__grp.html#gabe7ce52fca00463c2b70aa367037db98", null ], + [ "jwt_valid_get_grant_int", "group__jwt__valid__grp.html#ga98a643ad3c8103539dee35c22a3adbb6", null ], + [ "jwt_valid_get_grants_json", "group__jwt__valid__grp.html#gacafaf1f062cd358bda89601570d194ad", null ], + [ "jwt_valid_get_nbf_leeway", "group__jwt__valid__grp.html#gafa9c96838053831dd239824ed5149879", null ], + [ "jwt_valid_get_status", "group__jwt__valid__grp.html#ga165d7857282f3cce9106b26369cf11ca", null ], + [ "jwt_valid_new", "group__jwt__valid__grp.html#gaede6b64d84fd8166ea16debe6efc826d", null ], + [ "jwt_valid_set_exp_leeway", "group__jwt__valid__grp.html#ga1ce0ec28e692a189d3c6819dc571f941", null ], + [ "jwt_valid_set_headers", "group__jwt__valid__grp.html#gab29df6ee78d33bb846b13f95a1269e0d", null ], + [ "jwt_valid_set_nbf_leeway", "group__jwt__valid__grp.html#ga55f1862ab8bb6297ee0163f77fe7c386", null ], + [ "jwt_valid_set_now", "group__jwt__valid__grp.html#gad6a97fc0bb3341ae7f19975095a8086a", null ], + [ "jwt_validate", "group__jwt__valid__grp.html#ga354a14a13a0c96dae2cc22c4fa790413", null ] +]; \ No newline at end of file diff --git a/HEAD/group__jwt__valid__grp.map b/HEAD/group__jwt__valid__grp.map new file mode 100644 index 00000000..54d55a5c --- /dev/null +++ b/HEAD/group__jwt__valid__grp.map @@ -0,0 +1,4 @@ + + + + diff --git a/HEAD/group__jwt__valid__grp.md5 b/HEAD/group__jwt__valid__grp.md5 new file mode 100644 index 00000000..8a506c32 --- /dev/null +++ b/HEAD/group__jwt__valid__grp.md5 @@ -0,0 +1 @@ +07e0620123ce2069b38a3b8c0ed95291 \ No newline at end of file diff --git a/HEAD/group__jwt__valid__grp.svg b/HEAD/group__jwt__valid__grp.svg new file mode 100644 index 00000000..7dda828d --- /dev/null +++ b/HEAD/group__jwt__valid__grp.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + +Validation Functions + + +Node2 + + +JSON Web Token + + + + + +Node1 + + +Validation Functions + + + + + +Node2->Node1 + + + + + + + + + + diff --git a/HEAD/group__jwt__valid__grp_org.svg b/HEAD/group__jwt__valid__grp_org.svg new file mode 100644 index 00000000..db750fb8 --- /dev/null +++ b/HEAD/group__jwt__valid__grp_org.svg @@ -0,0 +1,36 @@ + + + + + + +Validation Functions + + +Node2 + + +JSON Web Token + + + + + +Node1 + + +Validation Functions + + + + + +Node2->Node1 + + + + + diff --git a/HEAD/group__jwt__verify__grp.html b/HEAD/group__jwt__verify__grp.html new file mode 100644 index 00000000..8068db42 --- /dev/null +++ b/HEAD/group__jwt__verify__grp.html @@ -0,0 +1,224 @@ + + + + + + + + +LibJWT: Token Verification + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + +
+
LibJWT 2.9.99 +
+
The C JSON Web Token Library +JWK +JWKS
+
+
+ + + +
+
+ +
+
+
+ +
+
+ +
Token Verification
+
+
+
+Collaboration diagram for Token Verification:
+
+
+
+ + + + + + + + +

+Functions

int jwt_verify (jwt_t **jwt, const char *token, jwt_config_t *config)
 Decode and verify a JWT.
 
int jwt_verify_wcb (jwt_t **jwt, const char *token, jwt_config_t *config, jwt_callback_t cb)
 Decode and verify a JWT, with user callback.
 
+

Detailed Description

+

Lots of cases to deal with.

+
    +
  1. If the caller passes a key/len pair:
      +
    • Then config.alg MUST NOT be none, and
    • +
    • The config.alg MUST match jwt.alg
    • +
    +
  2. +
  3. If the user passes a jw_key:
      +
    • config.alg is not used (jw_key.alg is used if available)
    • +
    • It's valid for jw_key.alg to be none (missing) (RFC-7517:4.4)
    • +
    • If jw_key.alg is not none, it MUST match the JWT
    • +
    • If jw_key.alg is none, then jwt.alg is compared against jwk.kty for compatibility. e.g:
        +
      • jwt.alg RS256, RS384, and RS512 are suitable with jwk.kty RSA
      • +
      • jwt.alg ES384 is not suitable with jwk.kty OKP
      • +
      +
    • +
    +
  4. +
  5. The user SHOULD NOT pass both types, but we allow it. However, checks for both keys MUST pass.
  6. +
  7. If the user did not pass a key of any kind:
      +
    • Then jwt.alg MUST be none, and
    • +
    • The sig_len MUST be zero
    • +
    • If the caller wishes to "peek" at a JWA token, (without verifying) then they MUST use a callback function to inspect it. Information from the callback can be passed between the cb and original caller with the jwt_config_t::ctx pointer.
    • +
    +
  8. +
  9. If jwt.alg is none then sig_len MUST be zero, regardless of (4)
  10. +
+

Function Documentation

+ +

◆ jwt_verify()

+ +
+
+ + + + + + + + + + + + + + + + +
int jwt_verify (jwt_t ** jwt,
const char * token,
jwt_config_t * config )
+
+ +

Decode and verify a JWT.

+
Parameters
+ + + + +
jwtPointer to a JWT object pointer
tokenPointer to a nil terminated JWT string
configPointer to a config structure to define how to verify the token. This can be NULL, in which case the token is simply decoded.
+
+
+
Returns
0 on success, or an errno. On success, jwt will be allocated
+ +
+
+ +

◆ jwt_verify_wcb()

+ +
+
+ + + + + + + + + + + + + + + + + + + + + +
int jwt_verify_wcb (jwt_t ** jwt,
const char * token,
jwt_config_t * config,
jwt_callback_t cb )
+
+ +

Decode and verify a JWT, with user callback.

+

This operates the same as jwt_verify, with the addition of calling a user defined callback function between the decode and verification step. This allows the user to perform some extra verification, and even provide a key after decoding (e.g. to match a "kid").

+

NOTE About NULL config and non-NULL cb and vice-a-versa

+
Parameters
+ + + + + +
jwtPointer to a JWT object pointer
tokenPointer to a nil terminated JWT string
configPointer to a config structure to define how to verify the token. This can be NULL, in which case the token is simply decoded.
cbPointer to a callback
+
+
+
Returns
0 on success, or an errno. On success, jwt will be allocated
+ +
+
+
+
+ + + + + diff --git a/HEAD/group__jwt__verify__grp.js b/HEAD/group__jwt__verify__grp.js new file mode 100644 index 00000000..3e54979e --- /dev/null +++ b/HEAD/group__jwt__verify__grp.js @@ -0,0 +1,5 @@ +var group__jwt__verify__grp = +[ + [ "jwt_verify", "group__jwt__verify__grp.html#gab0535d292e64c3688085cf0139aa8c8d", null ], + [ "jwt_verify_wcb", "group__jwt__verify__grp.html#gaddab273e09a8327e03f458be1cfb83b4", null ] +]; \ No newline at end of file diff --git a/HEAD/group__jwt__verify__grp.map b/HEAD/group__jwt__verify__grp.map new file mode 100644 index 00000000..a83dfc34 --- /dev/null +++ b/HEAD/group__jwt__verify__grp.map @@ -0,0 +1,4 @@ + + + + diff --git a/HEAD/group__jwt__verify__grp.md5 b/HEAD/group__jwt__verify__grp.md5 new file mode 100644 index 00000000..15ece3fe --- /dev/null +++ b/HEAD/group__jwt__verify__grp.md5 @@ -0,0 +1 @@ +9f5049bde9d113e51986d3b56e8937e4 \ No newline at end of file diff --git a/HEAD/group__jwt__verify__grp.svg b/HEAD/group__jwt__verify__grp.svg new file mode 100644 index 00000000..188b0adc --- /dev/null +++ b/HEAD/group__jwt__verify__grp.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + +Token Verification + + +Node2 + + +JSON Web Token + + + + + +Node1 + + +Token Verification + + + + + +Node2->Node1 + + + + + + + + + + diff --git a/HEAD/group__jwt__verify__grp_org.svg b/HEAD/group__jwt__verify__grp_org.svg new file mode 100644 index 00000000..95e3eb49 --- /dev/null +++ b/HEAD/group__jwt__verify__grp_org.svg @@ -0,0 +1,36 @@ + + + + + + +Token Verification + + +Node2 + + +JSON Web Token + + + + + +Node1 + + +Token Verification + + + + + +Node2->Node1 + + + + + diff --git a/HEAD/index.html b/HEAD/index.html new file mode 100644 index 00000000..ec9a60aa --- /dev/null +++ b/HEAD/index.html @@ -0,0 +1,154 @@ + + + + + + + + +LibJWT: Welcome to LibJWT + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + +
+
LibJWT 2.9.99 +
+
The C JSON Web Token Library +JWK +JWKS
+
+
+ + + +
+
+ +
+
+
+ +
+
+
Welcome to LibJWT
+
+
+

+💡 Supported Standards

+ + + + + + + + + + + +
Standard RFC Description
JWT 📄 RFC-7519 JSON Web Token
JWA 📄 RFC-7518 JSON Web Algorithms
JWS and JWE 📄 RFC-7518 Specific types of JWA
JWK and JWKS 📄 RFC-7517 JSON Web Keys and Sets
+
Note
Throughout this documentation you will see links such as the ones above to RFC documents. These are relevant to that particular part of the library and are helpful to understand some of the specific standards that shaped the development of LibJWT.
+

+🚧 Build Prerequisites

+

+Required

+
    +
  • JANSSON (>= 2.0)
  • +
  • CMake (>= 3.7)
  • +
+

+One or more of these

+
    +
  • OpenSSL (>= 1.1.0)
  • +
  • GnuTLS (>= 3.6.0)
  • +
+
Note
OpenSSL >= 3.0 is required for JWK and JWKS support
+

+Optional

+ +

+📖 Docs and Source

+

🔗 Release

+

🔗 Development

+

🔗 GitHub Repo

+

+📦 Pre-built Packages

+

LibJWT is available in most Linux distributions as well as through Homebrew for Linux, macOS, and Windows.

+

+🔨 Build Instructions

+

+With CMake:

+
$ mkdir build
+
$ cd build
+
$ cmake ..
+
$ make
+

+Extra Build Info

+

If you have libcheck installed you can compile the test suite which you can run using the check target.

+

CMake will auto detect OpenSSL and GnuTLS and use one or both. There are CMake options to force either one on or off.

+
+ +
+
+ + + + + diff --git a/HEAD/index.js b/HEAD/index.js new file mode 100644 index 00000000..8183a3b1 --- /dev/null +++ b/HEAD/index.js @@ -0,0 +1,15 @@ +var index = +[ + [ "\u{1f4a1} Supported Standards", "index.html#standards", null ], + [ "\u{1f6a7} Build Prerequisites", "index.html#building", [ + [ "Required", "index.html#req", null ], + [ "One or more of these", "index.html#req_crypto", null ], + [ "Optional", "index.html#optional", null ] + ] ], + [ "\u{1f4d6} Docs and Source", "index.html#docs", null ], + [ "\u{1f4e6} Pre-built Packages", "index.html#prebuilt", null ], + [ "\u{1f528} Build Instructions", "index.html#instructions", [ + [ "With CMake:", "index.html#cmake", null ], + [ "Extra Build Info", "index.html#extra", null ] + ] ] +]; \ No newline at end of file diff --git a/HEAD/index.qhp b/HEAD/index.qhp new file mode 100644 index 00000000..3833f566 --- /dev/null +++ b/HEAD/index.qhp @@ -0,0 +1,561 @@ + + +com.maclara-llc.LibJWT +doc + +doxygen + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +LibJWT.svg +annotated.html +bc_s.png +bc_sd.png +classes.html +closed.png +dir_d44c64559bbebec7f509842c48db8b23.html +doxygen-awesome-fragment-copy-button.js +doxygen-awesome-interactive-toc.js +doxygen-awesome-paragraph-link.js +doxygen-awesome.css +doxygen.css +doxygen.svg +doxygen_crawl.html +dynsections.js +favicon.ico +functions.html +functions_vars.html +graph_legend.html +graph_legend.svg +group__jwks__core__grp.html +group__jwt__advanced__grp.html +group__jwt__advanced__grp.svg +group__jwt__alg__grp.html +group__jwt__alg__grp.svg +group__jwt__config__grp.html +group__jwt__config__grp.svg +group__jwt__core__grp.html +group__jwt__core__grp.svg +group__jwt__create__grp.html +group__jwt__create__grp.svg +group__jwt__crypto__grp.html +group__jwt__crypto__grp.svg +group__jwt__encode__grp.html +group__jwt__encode__grp.svg +group__jwt__grant__grp.html +group__jwt__grant__grp.svg +group__jwt__grp.html +group__jwt__grp.svg +group__jwt__header__grp.html +group__jwt__header__grp.svg +group__jwt__memory__grp.html +group__jwt__memory__grp.svg +group__jwt__valid__grp.html +group__jwt__valid__grp.svg +group__jwt__verify__grp.html +group__jwt__verify__grp.svg +index.html +jquery.js +libjwt.css +maclara-pill.svg +minus.svg +minusd.svg +nav_f.png +nav_fd.png +nav_g.png +nav_h.png +nav_hd.png +navtree.css +open.png +pages.html +plus.svg +plusd.svg +resize.js +splitbar.png +splitbard.png +structjwk__item__t.html +structjwt__config__t.html +structjwt__config__t__coll__graph.pdf +structjwt__config__t__coll__graph.svg +svg.min.js +sync_off.png +sync_on.png +tab_a.png +tab_ad.png +tab_b.png +tab_bd.png +tab_h.png +tab_hd.png +tab_s.png +tab_sd.png +tabs.css +todo.html +topics.html + + + diff --git a/HEAD/jquery.js b/HEAD/jquery.js new file mode 100644 index 00000000..875ada73 --- /dev/null +++ b/HEAD/jquery.js @@ -0,0 +1,204 @@ +/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e} +var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp( +"^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"�":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType +}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c +)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){ +return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll( +":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id") +)&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push( +"\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test( +a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null, +null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne +).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for( +var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n; +return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0, +r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r] +,C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each( +function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r, +"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})} +),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each( +"blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0",options:{classes:{},disabled:!1,create:null},_createWidget:function(t,e){e=y(e||this.defaultElement||this)[0],this.element=y(e),this.uuid=i++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=y(),this.hoverable=y(),this.focusable=y(),this.classesElementLookup={},e!==this&&(y.data(e,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t +){t.target===e&&this.destroy()}}),this.document=y(e.style?e.ownerDocument:e.document||e),this.window=y(this.document[0].defaultView||this.document[0].parentWindow)),this.options=y.widget.extend({},this.options,this._getCreateOptions(),t),this._create(),this.options.disabled&&this._setOptionDisabled(this.options.disabled),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:function(){return{}},_getCreateEventData:y.noop,_create:y.noop,_init:y.noop,destroy:function(){var i=this;this._destroy(),y.each(this.classesElementLookup,function(t,e){i._removeClass(e,t)}),this.element.off(this.eventNamespace).removeData(this.widgetFullName),this.widget().off(this.eventNamespace).removeAttr("aria-disabled"),this.bindings.off(this.eventNamespace)},_destroy:y.noop,widget:function(){return this.element},option:function(t,e){var i,s,n,o=t;if(0===arguments.length)return y.widget.extend({},this.options);if("string"==typeof t)if(o={},t=(i=t.split(".")).shift(),i.length){for(s=o[t +]=y.widget.extend({},this.options[t]),n=0;n
"),i=e.children()[0];return y("body").append(e),t=i.offsetWidth,e.css("overflow","scroll"),t===(i=i.offsetWidth)&&(i=e[0].clientWidth),e.remove(),s=t-i}, +getScrollInfo:function(t){var e=t.isWindow||t.isDocument?"":t.element.css("overflow-x"),i=t.isWindow||t.isDocument?"":t.element.css("overflow-y"),e="scroll"===e||"auto"===e&&t.widthx(D(s),D(n))?o.important="horizontal":o.important="vertical",p.using.call(this,t,o)}),h.offset(y.extend(l,{using:t}))})},y.ui.position={fit:{left:function(t,e){var i=e.within, +s=i.isWindow?i.scrollLeft:i.offset.left,n=i.width,o=t.left-e.collisionPosition.marginLeft,h=s-o,a=o+e.collisionWidth-n-s;e.collisionWidth>n?0n?0=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}}),y.ui.plugin={add:function(t,e,i){var s,n=y.ui[t].prototype;for(s in i)n.plugins[s]=n.plugins[s]||[],n.plugins[s].push([e,i[s]])},call:function(t,e,i,s){var n,o=t.plugins[e];if(o&&(s||t.element[0].parentNode&&11!==t.element[0].parentNode.nodeType))for(n=0;n").css({overflow:"hidden",position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})), +this.element=this.element.parent().data("ui-resizable",this.element.resizable("instance")),this.elementIsWrapper=!0,t={marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom"),marginLeft:this.originalElement.css("marginLeft")},this.element.css(t),this.originalElement.css("margin",0),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css(t),this._proportionallyResize()),this._setupHandles(),e.autoHide&&y(this.element).on("mouseenter",function(){e.disabled||(i._removeClass("ui-resizable-autohide"),i._handles.show())}).on("mouseleave",function(){e.disabled||i.resizing||(i._addClass("ui-resizable-autohide"),i._handles.hide())}),this._mouseInit()},_destroy:function(){this._mouseDestroy(),this._addedHandles.remove();function t(t){y(t +).removeData("resizable").removeData("ui-resizable").off(".resizable")}var e;return this.elementIsWrapper&&(t(this.element),e=this.element,this.originalElement.css({position:e.css("position"),width:e.outerWidth(),height:e.outerHeight(),top:e.css("top"),left:e.css("left")}).insertAfter(e),e.remove()),this.originalElement.css("resize",this.originalResizeStyle),t(this.originalElement),this},_setOption:function(t,e){switch(this._super(t,e),t){case"handles":this._removeHandles(),this._setupHandles();break;case"aspectRatio":this._aspectRatio=!!e}},_setupHandles:function(){var t,e,i,s,n,o=this.options,h=this;if(this.handles=o.handles||(y(".ui-resizable-handle",this.element).length?{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"}:"e,s,se"),this._handles=y(),this._addedHandles=y(),this.handles.constructor===String)for("all"===this.handles&&(this.handles="n,e,s,w,se,sw,ne,nw"),i=this.handles.split( +","),this.handles={},e=0;e"),this._addClass(n,"ui-resizable-handle "+s),n.css({zIndex:o.zIndex}),this.handles[t]=".ui-resizable-"+t,this.element.children(this.handles[t]).length||(this.element.append(n),this._addedHandles=this._addedHandles.add(n));this._renderAxis=function(t){var e,i,s;for(e in t=t||this.element,this.handles)this.handles[e].constructor===String?this.handles[e]=this.element.children(this.handles[e]).first().show():(this.handles[e].jquery||this.handles[e].nodeType)&&(this.handles[e]=y(this.handles[e]),this._on(this.handles[e],{mousedown:h._mouseDown})),this.elementIsWrapper&&this.originalElement[0].nodeName.match(/^(textarea|input|select|button)$/i)&&(i=y(this.handles[e],this.element),s=/sw|ne|nw|se|n|s/.test(e)?i.outerHeight():i.outerWidth(),i=["padding",/ne|nw|n/.test(e)?"Top":/se|sw|s/.test(e)?"Bottom":/^e$/.test(e)?"Right":"Left"].join(""),t.css(i,s),this._proportionallyResize()),this._handles=this._handles.add( +this.handles[e])},this._renderAxis(this.element),this._handles=this._handles.add(this.element.find(".ui-resizable-handle")),this._handles.disableSelection(),this._handles.on("mouseover",function(){h.resizing||(this.className&&(n=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)),h.axis=n&&n[1]?n[1]:"se")}),o.autoHide&&(this._handles.hide(),this._addClass("ui-resizable-autohide"))},_removeHandles:function(){this._addedHandles.remove()},_mouseCapture:function(t){var e,i,s=!1;for(e in this.handles)(i=y(this.handles[e])[0])!==t.target&&!y.contains(i,t.target)||(s=!0);return!this.options.disabled&&s},_mouseStart:function(t){var e,i,s=this.options,n=this.element;return this.resizing=!0,this._renderProxy(),e=this._num(this.helper.css("left")),i=this._num(this.helper.css("top")),s.containment&&(e+=y(s.containment).scrollLeft()||0,i+=y(s.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:e,top:i},this.size=this._helper?{width:this.helper.width(),height:this.helper.height()}:{ +width:n.width(),height:n.height()},this.originalSize=this._helper?{width:n.outerWidth(),height:n.outerHeight()}:{width:n.width(),height:n.height()},this.sizeDiff={width:n.outerWidth()-n.width(),height:n.outerHeight()-n.height()},this.originalPosition={left:e,top:i},this.originalMousePosition={left:t.pageX,top:t.pageY},this.aspectRatio="number"==typeof s.aspectRatio?s.aspectRatio:this.originalSize.width/this.originalSize.height||1,s=y(".ui-resizable-"+this.axis).css("cursor"),y("body").css("cursor","auto"===s?this.axis+"-resize":s),this._addClass("ui-resizable-resizing"),this._propagate("start",t),!0},_mouseDrag:function(t){var e=this.originalMousePosition,i=this.axis,s=t.pageX-e.left||0,e=t.pageY-e.top||0,i=this._change[i];return this._updatePrevProperties(),i&&(e=i.apply(this,[t,s,e]),this._updateVirtualBoundaries(t.shiftKey),(this._aspectRatio||t.shiftKey)&&(e=this._updateRatio(e,t)),e=this._respectSize(e,t),this._updateCache(e),this._propagate("resize",t),e=this._applyChanges(), +!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),y.isEmptyObject(e)||(this._updatePrevProperties(),this._trigger("resize",t,this.ui()),this._applyChanges())),!1},_mouseStop:function(t){this.resizing=!1;var e,i,s,n=this.options,o=this;return this._helper&&(s=(e=(i=this._proportionallyResizeElements).length&&/textarea/i.test(i[0].nodeName))&&this._hasScroll(i[0],"left")?0:o.sizeDiff.height,i=e?0:o.sizeDiff.width,e={width:o.helper.width()-i,height:o.helper.height()-s},i=parseFloat(o.element.css("left"))+(o.position.left-o.originalPosition.left)||null,s=parseFloat(o.element.css("top"))+(o.position.top-o.originalPosition.top)||null,n.animate||this.element.css(y.extend(e,{top:s,left:i})),o.helper.height(o.size.height),o.helper.width(o.size.width),this._helper&&!n.animate&&this._proportionallyResize()),y("body").css("cursor","auto"),this._removeClass("ui-resizable-resizing"),this._propagate("stop",t),this._helper&&this.helper.remove(),!1},_updatePrevProperties:function(){ +this.prevPosition={top:this.position.top,left:this.position.left},this.prevSize={width:this.size.width,height:this.size.height}},_applyChanges:function(){var t={};return this.position.top!==this.prevPosition.top&&(t.top=this.position.top+"px"),this.position.left!==this.prevPosition.left&&(t.left=this.position.left+"px"),this.size.width!==this.prevSize.width&&(t.width=this.size.width+"px"),this.size.height!==this.prevSize.height&&(t.height=this.size.height+"px"),this.helper.css(t),t},_updateVirtualBoundaries:function(t){var e,i,s=this.options,n={minWidth:this._isNumber(s.minWidth)?s.minWidth:0,maxWidth:this._isNumber(s.maxWidth)?s.maxWidth:1/0,minHeight:this._isNumber(s.minHeight)?s.minHeight:0,maxHeight:this._isNumber(s.maxHeight)?s.maxHeight:1/0};(this._aspectRatio||t)&&(e=n.minHeight*this.aspectRatio,i=n.minWidth/this.aspectRatio,s=n.maxHeight*this.aspectRatio,t=n.maxWidth/this.aspectRatio,e>n.minWidth&&(n.minWidth=e),i>n.minHeight&&(n.minHeight=i),st.width,h=this._isNumber(t.height)&&e.minHeight&&e.minHeight>t.height,a=this.originalPosition.left+this.originalSize.width,r=this.originalPosition.top+this.originalSize.height +,l=/sw|nw|w/.test(i),i=/nw|ne|n/.test(i);return o&&(t.width=e.minWidth),h&&(t.height=e.minHeight),s&&(t.width=e.maxWidth),n&&(t.height=e.maxHeight),o&&l&&(t.left=a-e.minWidth),s&&l&&(t.left=a-e.maxWidth),h&&i&&(t.top=r-e.minHeight),n&&i&&(t.top=r-e.maxHeight),t.width||t.height||t.left||!t.top?t.width||t.height||t.top||!t.left||(t.left=null):t.top=null,t},_getPaddingPlusBorderDimensions:function(t){for(var e=0,i=[],s=[t.css("borderTopWidth"),t.css("borderRightWidth"),t.css("borderBottomWidth"),t.css("borderLeftWidth")],n=[t.css("paddingTop"),t.css("paddingRight"),t.css("paddingBottom"),t.css("paddingLeft")];e<4;e++)i[e]=parseFloat(s[e])||0,i[e]+=parseFloat(n[e])||0;return{height:i[0]+i[2],width:i[1]+i[3]}},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var t,e=0,i=this.helper||this.element;e").css({overflow:"hidden"}),this._addClass(this.helper,this._helper),this.helper.css({width:this.element.outerWidth(),height:this.element.outerHeight(),position:"absolute",left:this.elementOffset.left+"px",top:this.elementOffset.top+"px",zIndex:++e.zIndex}),this.helper.appendTo("body").disableSelection()):this.helper=this.element},_change:{e:function(t,e){return{width:this.originalSize.width+e}},w:function(t,e){var i=this.originalSize;return{left:this.originalPosition.left+e,width:i.width-e}},n:function(t,e,i){var s=this.originalSize;return{top:this.originalPosition.top+i,height:s.height-i}},s:function(t,e,i){return{height:this.originalSize.height+i}},se:function(t,e,i){return y.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[t,e,i]))},sw:function(t,e, +i){return y.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[t,e,i]))},ne:function(t,e,i){return y.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[t,e,i]))},nw:function(t,e,i){return y.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[t,e,i]))}},_propagate:function(t,e){y.ui.plugin.call(this,t,[e,this.ui()]),"resize"!==t&&this._trigger(t,e,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}),y.ui.plugin.add("resizable","animate",{stop:function(e){var i=y(this).resizable("instance"),t=i.options,s=i._proportionallyResizeElements,n=s.length&&/textarea/i.test(s[0].nodeName),o=n&&i._hasScroll(s[0],"left")?0:i.sizeDiff.height,h=n?0:i.sizeDiff.width,n={width:i.size.width-h,height:i.size.height-o},h=parseFloat(i.element.css("left"))+(i.position.left-i.originalPosition.left +)||null,o=parseFloat(i.element.css("top"))+(i.position.top-i.originalPosition.top)||null;i.element.animate(y.extend(n,o&&h?{top:o,left:h}:{}),{duration:t.animateDuration,easing:t.animateEasing,step:function(){var t={width:parseFloat(i.element.css("width")),height:parseFloat(i.element.css("height")),top:parseFloat(i.element.css("top")),left:parseFloat(i.element.css("left"))};s&&s.length&&y(s[0]).css({width:t.width,height:t.height}),i._updateCache(t),i._propagate("resize",e)}})}}),y.ui.plugin.add("resizable","containment",{start:function(){var i,s,n=y(this).resizable("instance"),t=n.options,e=n.element,o=t.containment,h=o instanceof y?o.get(0):/parent/.test(o)?e.parent().get(0):o;h&&(n.containerElement=y(h),/document/.test(o)||o===document?(n.containerOffset={left:0,top:0},n.containerPosition={left:0,top:0},n.parentData={element:y(document),left:0,top:0,width:y(document).width(),height:y(document).height()||document.body.parentNode.scrollHeight}):(i=y(h),s=[],y(["Top","Right","Left","Bottom"]).each(function(t,e +){s[t]=n._num(i.css("padding"+e))}),n.containerOffset=i.offset(),n.containerPosition=i.position(),n.containerSize={height:i.innerHeight()-s[3],width:i.innerWidth()-s[1]},t=n.containerOffset,e=n.containerSize.height,o=n.containerSize.width,o=n._hasScroll(h,"left")?h.scrollWidth:o,e=n._hasScroll(h)?h.scrollHeight:e,n.parentData={element:h,left:t.left,top:t.top,width:o,height:e}))},resize:function(t){var e=y(this).resizable("instance"),i=e.options,s=e.containerOffset,n=e.position,o=e._aspectRatio||t.shiftKey,h={top:0,left:0},a=e.containerElement,t=!0;a[0]!==document&&/static/.test(a.css("position"))&&(h=s),n.left<(e._helper?s.left:0)&&(e.size.width=e.size.width+(e._helper?e.position.left-s.left:e.position.left-h.left),o&&(e.size.height=e.size.width/e.aspectRatio,t=!1),e.position.left=i.helper?s.left:0),n.top<(e._helper?s.top:0)&&(e.size.height=e.size.height+(e._helper?e.position.top-s.top:e.position.top),o&&(e.size.width=e.size.height*e.aspectRatio,t=!1),e.position.top=e._helper?s.top:0), +i=e.containerElement.get(0)===e.element.parent().get(0),n=/relative|absolute/.test(e.containerElement.css("position")),i&&n?(e.offset.left=e.parentData.left+e.position.left,e.offset.top=e.parentData.top+e.position.top):(e.offset.left=e.element.offset().left,e.offset.top=e.element.offset().top),n=Math.abs(e.sizeDiff.width+(e._helper?e.offset.left-h.left:e.offset.left-s.left)),s=Math.abs(e.sizeDiff.height+(e._helper?e.offset.top-h.top:e.offset.top-s.top)),n+e.size.width>=e.parentData.width&&(e.size.width=e.parentData.width-n,o&&(e.size.height=e.size.width/e.aspectRatio,t=!1)),s+e.size.height>=e.parentData.height&&(e.size.height=e.parentData.height-s,o&&(e.size.width=e.size.height*e.aspectRatio,t=!1)),t||(e.position.left=e.prevPosition.left,e.position.top=e.prevPosition.top,e.size.width=e.prevSize.width,e.size.height=e.prevSize.height)},stop:function(){var t=y(this).resizable("instance"),e=t.options,i=t.containerOffset,s=t.containerPosition,n=t.containerElement,o=y(t.helper),h=o.offset(),a=o.outerWidth( +)-t.sizeDiff.width,o=o.outerHeight()-t.sizeDiff.height;t._helper&&!e.animate&&/relative/.test(n.css("position"))&&y(this).css({left:h.left-s.left-i.left,width:a,height:o}),t._helper&&!e.animate&&/static/.test(n.css("position"))&&y(this).css({left:h.left-s.left-i.left,width:a,height:o})}}),y.ui.plugin.add("resizable","alsoResize",{start:function(){var t=y(this).resizable("instance").options;y(t.alsoResize).each(function(){var t=y(this);t.data("ui-resizable-alsoresize",{width:parseFloat(t.width()),height:parseFloat(t.height()),left:parseFloat(t.css("left")),top:parseFloat(t.css("top"))})})},resize:function(t,i){var e=y(this).resizable("instance"),s=e.options,n=e.originalSize,o=e.originalPosition,h={height:e.size.height-n.height||0,width:e.size.width-n.width||0,top:e.position.top-o.top||0,left:e.position.left-o.left||0};y(s.alsoResize).each(function(){var t=y(this),s=y(this).data("ui-resizable-alsoresize"),n={},e=t.parents(i.originalElement[0]).length?["width","height"]:["width","height","top","left"];y.each(e, +function(t,e){var i=(s[e]||0)+(h[e]||0);i&&0<=i&&(n[e]=i||null)}),t.css(n)})},stop:function(){y(this).removeData("ui-resizable-alsoresize")}}),y.ui.plugin.add("resizable","ghost",{start:function(){var t=y(this).resizable("instance"),e=t.size;t.ghost=t.originalElement.clone(),t.ghost.css({opacity:.25,display:"block",position:"relative",height:e.height,width:e.width,margin:0,left:0,top:0}),t._addClass(t.ghost,"ui-resizable-ghost"),!1!==y.uiBackCompat&&"string"==typeof t.options.ghost&&t.ghost.addClass(this.options.ghost),t.ghost.appendTo(t.helper)},resize:function(){var t=y(this).resizable("instance");t.ghost&&t.ghost.css({position:"relative",height:t.size.height,width:t.size.width})},stop:function(){var t=y(this).resizable("instance");t.ghost&&t.helper&&t.helper.get(0).removeChild(t.ghost.get(0))}}),y.ui.plugin.add("resizable","grid",{resize:function(){var t,e=y(this).resizable("instance"),i=e.options,s=e.size,n=e.originalSize,o=e.originalPosition,h=e.axis,a="number"==typeof i.grid?[i.grid,i.grid]:i.grid,r=a[0 +]||1,l=a[1]||1,u=Math.round((s.width-n.width)/r)*r,p=Math.round((s.height-n.height)/l)*l,d=n.width+u,c=n.height+p,f=i.maxWidth&&i.maxWidthd,s=i.minHeight&&i.minHeight>c;i.grid=a,m&&(d+=r),s&&(c+=l),f&&(d-=r),g&&(c-=l),/^(se|s|e)$/.test(h)?(e.size.width=d,e.size.height=c):/^(ne)$/.test(h)?(e.size.width=d,e.size.height=c,e.position.top=o.top-p):/^(sw)$/.test(h)?(e.size.width=d,e.size.height=c,e.position.left=o.left-u):((c-l<=0||d-r<=0)&&(t=e._getPaddingPlusBorderDimensions(this)),0=f[g]?0:Math.min(f[g],n));!a&&1-1){ +targetElements.on(evt+EVENT_NAMESPACE,function elementToggle(event){$.powerTip.toggle(this,event)})}else{targetElements.on(evt+EVENT_NAMESPACE,function elementOpen(event){$.powerTip.show(this,event)})}});$.each(options.closeEvents,function(idx,evt){if($.inArray(evt,options.openEvents)<0){targetElements.on(evt+EVENT_NAMESPACE,function elementClose(event){$.powerTip.hide(this,!isMouseEvent(event))})}});targetElements.on("keydown"+EVENT_NAMESPACE,function elementKeyDown(event){if(event.keyCode===27){$.powerTip.hide(this,true)}})}return targetElements};$.fn.powerTip.defaults={fadeInTime:200,fadeOutTime:100,followMouse:false,popupId:"powerTip",popupClass:null,intentSensitivity:7,intentPollInterval:100,closeDelay:100,placement:"n",smartPlacement:false,offset:10,mouseOnToPopup:false,manual:false,openEvents:["mouseenter","focus"],closeEvents:["mouseleave","blur"]};$.fn.powerTip.smartPlacementLists={n:["n","ne","nw","s"],e:["e","ne","se","w","nw","sw","n","s","e"],s:["s","se","sw","n"],w:["w","nw","sw","e","ne","se", +"n","s","w"],nw:["nw","w","sw","n","s","se","nw"],ne:["ne","e","se","n","s","sw","ne"],sw:["sw","w","nw","s","n","ne","sw"],se:["se","e","ne","s","n","nw","se"],"nw-alt":["nw-alt","n","ne-alt","sw-alt","s","se-alt","w","e"],"ne-alt":["ne-alt","n","nw-alt","se-alt","s","sw-alt","e","w"],"sw-alt":["sw-alt","s","se-alt","nw-alt","n","ne-alt","w","e"],"se-alt":["se-alt","s","sw-alt","ne-alt","n","nw-alt","e","w"]};$.powerTip={show:function apiShowTip(element,event){if(isMouseEvent(event)){trackMouse(event);session.previousX=event.pageX;session.previousY=event.pageY;$(element).data(DATA_DISPLAYCONTROLLER).show()}else{$(element).first().data(DATA_DISPLAYCONTROLLER).show(true,true)}return element},reposition:function apiResetPosition(element){$(element).first().data(DATA_DISPLAYCONTROLLER).resetPosition();return element},hide:function apiCloseTip(element,immediate){var displayController;immediate=element?immediate:true;if(element){displayController=$(element).first().data(DATA_DISPLAYCONTROLLER)}else if( +session.activeHover){displayController=session.activeHover.data(DATA_DISPLAYCONTROLLER)}if(displayController){displayController.hide(immediate)}return element},toggle:function apiToggle(element,event){if(session.activeHover&&session.activeHover.is(element)){$.powerTip.hide(element,!isMouseEvent(event))}else{$.powerTip.show(element,event)}return element}};$.powerTip.showTip=$.powerTip.show;$.powerTip.closeTip=$.powerTip.hide;function CSSCoordinates(){var me=this;me.top="auto";me.left="auto";me.right="auto";me.bottom="auto";me.set=function(property,value){if($.isNumeric(value)){me[property]=Math.round(value)}}}function DisplayController(element,options,tipController){var hoverTimer=null,myCloseDelay=null;function openTooltip(immediate,forceOpen){cancelTimer();if(!element.data(DATA_HASACTIVEHOVER)){if(!immediate){session.tipOpenImminent=true;hoverTimer=setTimeout(function intentDelay(){hoverTimer=null;checkForIntent()},options.intentPollInterval)}else{if(forceOpen){element.data(DATA_FORCEDOPEN,true)} +closeAnyDelayed();tipController.showTip(element)}}else{cancelClose()}}function closeTooltip(disableDelay){if(myCloseDelay){myCloseDelay=session.closeDelayTimeout=clearTimeout(myCloseDelay);session.delayInProgress=false}cancelTimer();session.tipOpenImminent=false;if(element.data(DATA_HASACTIVEHOVER)){element.data(DATA_FORCEDOPEN,false);if(!disableDelay){session.delayInProgress=true;session.closeDelayTimeout=setTimeout(function closeDelay(){session.closeDelayTimeout=null;tipController.hideTip(element);session.delayInProgress=false;myCloseDelay=null},options.closeDelay);myCloseDelay=session.closeDelayTimeout}else{tipController.hideTip(element)}}}function checkForIntent(){var xDifference=Math.abs(session.previousX-session.currentX),yDifference=Math.abs(session.previousY-session.currentY),totalDifference=xDifference+yDifference;if(totalDifference",{id:options.popupId});if($body.length===0){$body=$("body")}$body.append(tipElement);session.tooltips=session.tooltips?session.tooltips.add(tipElement):tipElement}if(options.followMouse){if(!tipElement.data(DATA_HASMOUSEMOVE)){$document.on("mousemove"+EVENT_NAMESPACE,positionTipOnCursor);$window.on("scroll"+EVENT_NAMESPACE,positionTipOnCursor);tipElement.data(DATA_HASMOUSEMOVE,true)}}function beginShowTip(element){element.data(DATA_HASACTIVEHOVER,true);tipElement.queue(function queueTipInit(next){showTip(element);next()})}function showTip(element){var tipContent;if(!element.data(DATA_HASACTIVEHOVER)){return}if( +session.isTipOpen){if(!session.isClosing){hideTip(session.activeHover)}tipElement.delay(100).queue(function queueTipAgain(next){showTip(element);next()});return}element.trigger("powerTipPreRender");tipContent=getTooltipContent(element);if(tipContent){tipElement.empty().append(tipContent)}else{return}element.trigger("powerTipRender");session.activeHover=element;session.isTipOpen=true;tipElement.data(DATA_MOUSEONTOTIP,options.mouseOnToPopup);tipElement.addClass(options.popupClass);if(!options.followMouse||element.data(DATA_FORCEDOPEN)){positionTipOnElement(element);session.isFixedTipOpen=true}else{positionTipOnCursor()}if(!element.data(DATA_FORCEDOPEN)&&!options.followMouse){$document.on("click"+EVENT_NAMESPACE,function documentClick(event){var target=event.target;if(target!==element[0]){if(options.mouseOnToPopup){if(target!==tipElement[0]&&!$.contains(tipElement[0],target)){$.powerTip.hide()}}else{$.powerTip.hide()}}})}if(options.mouseOnToPopup&&!options.manual){tipElement.on("mouseenter"+EVENT_NAMESPACE, +function tipMouseEnter(){if(session.activeHover){session.activeHover.data(DATA_DISPLAYCONTROLLER).cancel()}});tipElement.on("mouseleave"+EVENT_NAMESPACE,function tipMouseLeave(){if(session.activeHover){session.activeHover.data(DATA_DISPLAYCONTROLLER).hide()}})}tipElement.fadeIn(options.fadeInTime,function fadeInCallback(){if(!session.desyncTimeout){session.desyncTimeout=setInterval(closeDesyncedTip,500)}element.trigger("powerTipOpen")})}function hideTip(element){session.isClosing=true;session.isTipOpen=false;session.desyncTimeout=clearInterval(session.desyncTimeout);element.data(DATA_HASACTIVEHOVER,false);element.data(DATA_FORCEDOPEN,false);$document.off("click"+EVENT_NAMESPACE);tipElement.off(EVENT_NAMESPACE);tipElement.fadeOut(options.fadeOutTime,function fadeOutCallback(){var coords=new CSSCoordinates;session.activeHover=null;session.isClosing=false;session.isFixedTipOpen=false;tipElement.removeClass();coords.set("top",session.currentY+options.offset);coords.set("left",session.currentX+options.offset); +tipElement.css(coords);element.trigger("powerTipClose")})}function positionTipOnCursor(){var tipWidth,tipHeight,coords,collisions,collisionCount;if(!session.isFixedTipOpen&&(session.isTipOpen||session.tipOpenImminent&&tipElement.data(DATA_HASMOUSEMOVE))){tipWidth=tipElement.outerWidth();tipHeight=tipElement.outerHeight();coords=new CSSCoordinates;coords.set("top",session.currentY+options.offset);coords.set("left",session.currentX+options.offset);collisions=getViewportCollisions(coords,tipWidth,tipHeight);if(collisions!==Collision.none){collisionCount=countFlags(collisions);if(collisionCount===1){if(collisions===Collision.right){coords.set("left",session.scrollLeft+session.windowWidth-tipWidth)}else if(collisions===Collision.bottom){coords.set("top",session.scrollTop+session.windowHeight-tipHeight)}}else{coords.set("left",session.currentX-tipWidth-options.offset);coords.set("top",session.currentY-tipHeight-options.offset)}}tipElement.css(coords)}}function positionTipOnElement(element){var priorityList, +finalPlacement;if(options.smartPlacement||options.followMouse&&element.data(DATA_FORCEDOPEN)){priorityList=$.fn.powerTip.smartPlacementLists[options.placement];$.each(priorityList,function(idx,pos){var collisions=getViewportCollisions(placeTooltip(element,pos),tipElement.outerWidth(),tipElement.outerHeight());finalPlacement=pos;return collisions!==Collision.none})}else{placeTooltip(element,options.placement);finalPlacement=options.placement}tipElement.removeClass("w nw sw e ne se n s w se-alt sw-alt ne-alt nw-alt");tipElement.addClass(finalPlacement)}function placeTooltip(element,placement){var iterationCount=0,tipWidth,tipHeight,coords=new CSSCoordinates;coords.set("top",0);coords.set("left",0);tipElement.css(coords);do{tipWidth=tipElement.outerWidth();tipHeight=tipElement.outerHeight();coords=placementCalculator.compute(element,placement,tipWidth,tipHeight,options.offset);tipElement.css(coords)}while(++iterationCount<=5&&(tipWidth!==tipElement.outerWidth()||tipHeight!==tipElement.outerHeight())); +return coords}function closeDesyncedTip(){var isDesynced=false,hasDesyncableCloseEvent=$.grep(["mouseleave","mouseout","blur","focusout"],function(eventType){return $.inArray(eventType,options.closeEvents)!==-1}).length>0;if(session.isTipOpen&&!session.isClosing&&!session.delayInProgress&&hasDesyncableCloseEvent){if(session.activeHover.data(DATA_HASACTIVEHOVER)===false||session.activeHover.is(":disabled")){isDesynced=true}else if(!isMouseOver(session.activeHover)&&!session.activeHover.is(":focus")&&!session.activeHover.data(DATA_FORCEDOPEN)){if(tipElement.data(DATA_MOUSEONTOTIP)){if(!isMouseOver(tipElement)){isDesynced=true}}else{isDesynced=true}}if(isDesynced){hideTip(session.activeHover)}}}this.showTip=beginShowTip;this.hideTip=hideTip;this.resetPosition=positionTipOnElement}function isSvgElement(element){return Boolean(window.SVGElement&&element[0]instanceof SVGElement)}function isMouseEvent(event){return Boolean(event&&$.inArray(event.type,MOUSE_EVENTS)>-1&&typeof event.pageX==="number")} +function initTracking(){if(!session.mouseTrackingActive){session.mouseTrackingActive=true;getViewportDimensions();$(getViewportDimensions);$document.on("mousemove"+EVENT_NAMESPACE,trackMouse);$window.on("resize"+EVENT_NAMESPACE,trackResize);$window.on("scroll"+EVENT_NAMESPACE,trackScroll)}}function getViewportDimensions(){session.scrollLeft=$window.scrollLeft();session.scrollTop=$window.scrollTop();session.windowWidth=$window.width();session.windowHeight=$window.height()}function trackResize(){session.windowWidth=$window.width();session.windowHeight=$window.height()}function trackScroll(){var x=$window.scrollLeft(),y=$window.scrollTop();if(x!==session.scrollLeft){session.currentX+=x-session.scrollLeft;session.scrollLeft=x}if(y!==session.scrollTop){session.currentY+=y-session.scrollTop;session.scrollTop=y}}function trackMouse(event){session.currentX=event.pageX;session.currentY=event.pageY}function isMouseOver(element){var elementPosition=element.offset(),elementBox=element[0].getBoundingClientRect(), +elementWidth=elementBox.right-elementBox.left,elementHeight=elementBox.bottom-elementBox.top;return session.currentX>=elementPosition.left&&session.currentX<=elementPosition.left+elementWidth&&session.currentY>=elementPosition.top&&session.currentY<=elementPosition.top+elementHeight}function getTooltipContent(element){var tipText=element.data(DATA_POWERTIP),tipObject=element.data(DATA_POWERTIPJQ),tipTarget=element.data(DATA_POWERTIPTARGET),targetElement,content;if(tipText){if($.isFunction(tipText)){tipText=tipText.call(element[0])}content=tipText}else if(tipObject){if($.isFunction(tipObject)){tipObject=tipObject.call(element[0])}if(tipObject.length>0){content=tipObject.clone(true,true)}}else if(tipTarget){targetElement=$("#"+tipTarget);if(targetElement.length>0){content=targetElement.html()}}return content}function getViewportCollisions(coords,elementWidth,elementHeight){var viewportTop=session.scrollTop,viewportLeft=session.scrollLeft,viewportBottom=viewportTop+session.windowHeight, +viewportRight=viewportLeft+session.windowWidth,collisions=Collision.none;if(coords.topviewportBottom||Math.abs(coords.bottom-session.windowHeight)>viewportBottom){collisions|=Collision.bottom}if(coords.leftviewportRight){collisions|=Collision.left}if(coords.left+elementWidth>viewportRight||coords.right1)){a.preventDefault();var c=a.originalEvent.changedTouches[0],d=document.createEvent("MouseEvents");d.initMouseEvent(b,!0,!0,window,1,c.screenX,c.screenY,c.clientX,c.clientY,!1,!1,!1,!1,0,null),a.target.dispatchEvent(d)}}if(a.support.touch="ontouchend"in document,a.support.touch){var e,b=a.ui.mouse.prototype,c=b._mouseInit,d=b._mouseDestroy;b._touchStart=function(a){var b=this;!e&&b._mouseCapture(a.originalEvent.changedTouches[0])&&(e=!0,b._touchMoved=!1,f(a,"mouseover"),f(a,"mousemove"),f(a,"mousedown"))},b._touchMove=function(a){e&&(this._touchMoved=!0,f(a,"mousemove"))},b._touchEnd=function(a){e&&(f(a,"mouseup"),f(a,"mouseout"),this._touchMoved||f(a,"click"),e=!1)},b._mouseInit=function(){var b=this;b.element.bind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),c.call(b)},b._mouseDestroy=function(){var b=this;b.element.unbind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b, +"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),d.call(b)}}}(jQuery); +/*! SmartMenus jQuery Plugin - v1.1.0 - September 17, 2017 + * http://www.smartmenus.org/ + * Copyright Vasil Dinkov, Vadikom Web Ltd. http://vadikom.com; Licensed MIT */(function(t){"function"==typeof define&&define.amd?define(["jquery"],t):"object"==typeof module&&"object"==typeof module.exports?module.exports=t(require("jquery")):t(jQuery)})(function($){function initMouseDetection(t){var e=".smartmenus_mouse";if(mouseDetectionEnabled||t)mouseDetectionEnabled&&t&&($(document).off(e),mouseDetectionEnabled=!1);else{var i=!0,s=null,o={mousemove:function(t){var e={x:t.pageX,y:t.pageY,timeStamp:(new Date).getTime()};if(s){var o=Math.abs(s.x-e.x),a=Math.abs(s.y-e.y);if((o>0||a>0)&&2>=o&&2>=a&&300>=e.timeStamp-s.timeStamp&&(mouse=!0,i)){var n=$(t.target).closest("a");n.is("a")&&$.each(menuTrees,function(){return $.contains(this.$root[0],n[0])?(this.itemEnter({currentTarget:n[0]}),!1):void 0}),i=!1}}s=e}};o[touchEvents?"touchstart":"pointerover pointermove pointerout MSPointerOver MSPointerMove MSPointerOut"]=function(t){isTouchEvent(t.originalEvent)&&(mouse=!1)},$(document).on(getEventsNS(o,e)), +mouseDetectionEnabled=!0}}function isTouchEvent(t){return!/^(4|mouse)$/.test(t.pointerType)}function getEventsNS(t,e){e||(e="");var i={};for(var s in t)i[s.split(" ").join(e+" ")+e]=t[s];return i}var menuTrees=[],mouse=!1,touchEvents="ontouchstart"in window,mouseDetectionEnabled=!1,requestAnimationFrame=window.requestAnimationFrame||function(t){return setTimeout(t,1e3/60)},cancelAnimationFrame=window.cancelAnimationFrame||function(t){clearTimeout(t)},canAnimate=!!$.fn.animate;return $.SmartMenus=function(t,e){this.$root=$(t),this.opts=e,this.rootId="",this.accessIdPrefix="",this.$subArrow=null,this.activatedItems=[],this.visibleSubMenus=[],this.showTimeout=0,this.hideTimeout=0,this.scrollTimeout=0,this.clickActivated=!1,this.focusActivated=!1,this.zIndexInc=0,this.idInc=0,this.$firstLink=null,this.$firstSub=null,this.disabled=!1,this.$disableOverlay=null,this.$touchScrollingSub=null,this.cssTransforms3d="perspective"in t.style||"webkitPerspective"in t.style,this.wasCollapsible=!1,this.init()},$.extend( +$.SmartMenus,{hideAll:function(){$.each(menuTrees,function(){this.menuHideAll()})},destroy:function(){for(;menuTrees.length;)menuTrees[0].destroy();initMouseDetection(!0)},prototype:{init:function(t){var e=this;if(!t){menuTrees.push(this),this.rootId=((new Date).getTime()+Math.random()+"").replace(/\D/g,""),this.accessIdPrefix="sm-"+this.rootId+"-",this.$root.hasClass("sm-rtl")&&(this.opts.rightToLeftSubMenus=!0);var i=".smartmenus";this.$root.data("smartmenus",this).attr("data-smartmenus-id",this.rootId).dataSM("level",1).on(getEventsNS({"mouseover focusin":$.proxy(this.rootOver,this),"mouseout focusout":$.proxy(this.rootOut,this),keydown:$.proxy(this.rootKeyDown,this)},i)).on(getEventsNS({mouseenter:$.proxy(this.itemEnter,this),mouseleave:$.proxy(this.itemLeave,this),mousedown:$.proxy(this.itemDown,this),focus:$.proxy(this.itemFocus,this),blur:$.proxy(this.itemBlur,this),click:$.proxy(this.itemClick,this)},i),"a"),i+=this.rootId,this.opts.hideOnClick&&$(document).on(getEventsNS({touchstart:$.proxy( +this.docTouchStart,this),touchmove:$.proxy(this.docTouchMove,this),touchend:$.proxy(this.docTouchEnd,this),click:$.proxy(this.docClick,this)},i)),$(window).on(getEventsNS({"resize orientationchange":$.proxy(this.winResize,this)},i)),this.opts.subIndicators&&(this.$subArrow=$("").addClass("sub-arrow"),this.opts.subIndicatorsText&&this.$subArrow.html(this.opts.subIndicatorsText)),initMouseDetection()}if(this.$firstSub=this.$root.find("ul").each(function(){e.menuInit($(this))}).eq(0),this.$firstLink=this.$root.find("a").eq(0),this.opts.markCurrentItem){var s=/(index|default)\.[^#\?\/]*/i,o=/#.*/,a=window.location.href.replace(s,""),n=a.replace(o,"");this.$root.find("a").each(function(){var t=this.href.replace(s,""),i=$(this);(t==a||t==n)&&(i.addClass("current"),e.opts.markCurrentTree&&i.parentsUntil("[data-smartmenus-id]","ul").each(function(){$(this).dataSM("parent-a").addClass("current")}))})}this.wasCollapsible=this.isCollapsible()},destroy:function(t){if(!t){var e=".smartmenus";this.$root.removeData( +"smartmenus").removeAttr("data-smartmenus-id").removeDataSM("level").off(e),e+=this.rootId,$(document).off(e),$(window).off(e),this.opts.subIndicators&&(this.$subArrow=null)}this.menuHideAll();var i=this;this.$root.find("ul").each(function(){var t=$(this);t.dataSM("scroll-arrows")&&t.dataSM("scroll-arrows").remove(),t.dataSM("shown-before")&&((i.opts.subMenusMinWidth||i.opts.subMenusMaxWidth)&&t.css({width:"",minWidth:"",maxWidth:""}).removeClass("sm-nowrap"),t.dataSM("scroll-arrows")&&t.dataSM("scroll-arrows").remove(),t.css({zIndex:"",top:"",left:"",marginLeft:"",marginTop:"",display:""})),0==(t.attr("id")||"").indexOf(i.accessIdPrefix)&&t.removeAttr("id")}).removeDataSM("in-mega").removeDataSM("shown-before").removeDataSM("scroll-arrows").removeDataSM("parent-a").removeDataSM("level").removeDataSM("beforefirstshowfired").removeAttr("role").removeAttr("aria-hidden").removeAttr("aria-labelledby").removeAttr("aria-expanded"),this.$root.find("a.has-submenu").each(function(){var t=$(this);0==t.attr("id" +).indexOf(i.accessIdPrefix)&&t.removeAttr("id")}).removeClass("has-submenu").removeDataSM("sub").removeAttr("aria-haspopup").removeAttr("aria-controls").removeAttr("aria-expanded").closest("li").removeDataSM("sub"),this.opts.subIndicators&&this.$root.find("span.sub-arrow").remove(),this.opts.markCurrentItem&&this.$root.find("a.current").removeClass("current"),t||(this.$root=null,this.$firstLink=null,this.$firstSub=null,this.$disableOverlay&&(this.$disableOverlay.remove(),this.$disableOverlay=null),menuTrees.splice($.inArray(this,menuTrees),1))},disable:function(t){if(!this.disabled){if(this.menuHideAll(),!t&&!this.opts.isPopup&&this.$root.is(":visible")){var e=this.$root.offset();this.$disableOverlay=$('
').css({position:"absolute",top:e.top,left:e.left,width:this.$root.outerWidth(),height:this.$root.outerHeight(),zIndex:this.getStartZIndex(!0),opacity:0}).appendTo(document.body)}this.disabled=!0}},docClick:function(t){return this.$touchScrollingSub?( +this.$touchScrollingSub=null,void 0):((this.visibleSubMenus.length&&!$.contains(this.$root[0],t.target)||$(t.target).closest("a").length)&&this.menuHideAll(),void 0)},docTouchEnd:function(){if(this.lastTouch){if(!(!this.visibleSubMenus.length||void 0!==this.lastTouch.x2&&this.lastTouch.x1!=this.lastTouch.x2||void 0!==this.lastTouch.y2&&this.lastTouch.y1!=this.lastTouch.y2||this.lastTouch.target&&$.contains(this.$root[0],this.lastTouch.target))){this.hideTimeout&&(clearTimeout(this.hideTimeout),this.hideTimeout=0);var t=this;this.hideTimeout=setTimeout(function(){t.menuHideAll()},350)}this.lastTouch=null}},docTouchMove:function(t){if(this.lastTouch){var e=t.originalEvent.touches[0];this.lastTouch.x2=e.pageX,this.lastTouch.y2=e.pageY}},docTouchStart:function(t){var e=t.originalEvent.touches[0];this.lastTouch={x1:e.pageX,y1:e.pageY,target:e.target}},enable:function(){this.disabled&&(this.$disableOverlay&&(this.$disableOverlay.remove(),this.$disableOverlay=null),this.disabled=!1)},getClosestMenu:function(t){for( +var e=$(t).closest("ul");e.dataSM("in-mega");)e=e.parent().closest("ul");return e[0]||null},getHeight:function(t){return this.getOffset(t,!0)},getOffset:function(t,e){var i;"none"==t.css("display")&&(i={position:t[0].style.position,visibility:t[0].style.visibility},t.css({position:"absolute",visibility:"hidden"}).show());var s=t[0].getBoundingClientRect&&t[0].getBoundingClientRect(),o=s&&(e?s.height||s.bottom-s.top:s.width||s.right-s.left);return o||0===o||(o=e?t[0].offsetHeight:t[0].offsetWidth),i&&t.hide().css(i),o},getStartZIndex:function(t){var e=parseInt(this[t?"$root":"$firstSub"].css("z-index"));return!t&&isNaN(e)&&(e=parseInt(this.$root.css("z-index"))),isNaN(e)?1:e},getTouchPoint:function(t){return t.touches&&t.touches[0]||t.changedTouches&&t.changedTouches[0]||t},getViewport:function(t){var e=t?"Height":"Width",i=document.documentElement["client"+e],s=window["inner"+e];return s&&(i=Math.min(i,s)),i},getViewportHeight:function(){return this.getViewport(!0)},getViewportWidth:function(){ +return this.getViewport()},getWidth:function(t){return this.getOffset(t)},handleEvents:function(){return!this.disabled&&this.isCSSOn()},handleItemEvents:function(t){return this.handleEvents()&&!this.isLinkInMegaMenu(t)},isCollapsible:function(){return"static"==this.$firstSub.css("position")},isCSSOn:function(){return"inline"!=this.$firstLink.css("display")},isFixed:function(){var t="fixed"==this.$root.css("position");return t||this.$root.parentsUntil("body").each(function(){return"fixed"==$(this).css("position")?(t=!0,!1):void 0}),t},isLinkInMegaMenu:function(t){return $(this.getClosestMenu(t[0])).hasClass("mega-menu")},isTouchMode:function(){return!mouse||this.opts.noMouseOver||this.isCollapsible()},itemActivate:function(t,e){var i=t.closest("ul"),s=i.dataSM("level");if(s>1&&(!this.activatedItems[s-2]||this.activatedItems[s-2][0]!=i.dataSM("parent-a")[0])){var o=this;$(i.parentsUntil("[data-smartmenus-id]","ul").get().reverse()).add(i).each(function(){o.itemActivate($(this).dataSM("parent-a"))})}if(( +!this.isCollapsible()||e)&&this.menuHideSubMenus(this.activatedItems[s-1]&&this.activatedItems[s-1][0]==t[0]?s:s-1),this.activatedItems[s-1]=t,this.$root.triggerHandler("activate.smapi",t[0])!==!1){var a=t.dataSM("sub");a&&(this.isTouchMode()||!this.opts.showOnClick||this.clickActivated)&&this.menuShow(a)}},itemBlur:function(t){var e=$(t.currentTarget);this.handleItemEvents(e)&&this.$root.triggerHandler("blur.smapi",e[0])},itemClick:function(t){var e=$(t.currentTarget);if(this.handleItemEvents(e)){if(this.$touchScrollingSub&&this.$touchScrollingSub[0]==e.closest("ul")[0])return this.$touchScrollingSub=null,t.stopPropagation(),!1;if(this.$root.triggerHandler("click.smapi",e[0])===!1)return!1;var i=$(t.target).is(".sub-arrow"),s=e.dataSM("sub"),o=s?2==s.dataSM("level"):!1,a=this.isCollapsible(),n=/toggle$/.test(this.opts.collapsibleBehavior),r=/link$/.test(this.opts.collapsibleBehavior),h=/^accordion/.test(this.opts.collapsibleBehavior);if(s&&!s.is(":visible")){if((!r||!a||i)&&(this.opts.showOnClick&&o&&( +this.clickActivated=!0),this.itemActivate(e,h),s.is(":visible")))return this.focusActivated=!0,!1}else if(a&&(n||i))return this.itemActivate(e,h),this.menuHide(s),n&&(this.focusActivated=!1),!1;return this.opts.showOnClick&&o||e.hasClass("disabled")||this.$root.triggerHandler("select.smapi",e[0])===!1?!1:void 0}},itemDown:function(t){var e=$(t.currentTarget);this.handleItemEvents(e)&&e.dataSM("mousedown",!0)},itemEnter:function(t){var e=$(t.currentTarget);if(this.handleItemEvents(e)){if(!this.isTouchMode()){this.showTimeout&&(clearTimeout(this.showTimeout),this.showTimeout=0);var i=this;this.showTimeout=setTimeout(function(){i.itemActivate(e)},this.opts.showOnClick&&1==e.closest("ul").dataSM("level")?1:this.opts.showTimeout)}this.$root.triggerHandler("mouseenter.smapi",e[0])}},itemFocus:function(t){var e=$(t.currentTarget);this.handleItemEvents(e)&&(!this.focusActivated||this.isTouchMode()&&e.dataSM("mousedown")||this.activatedItems.length&&this.activatedItems[this.activatedItems.length-1][0]==e[0 +]||this.itemActivate(e,!0),this.$root.triggerHandler("focus.smapi",e[0]))},itemLeave:function(t){var e=$(t.currentTarget);this.handleItemEvents(e)&&(this.isTouchMode()||(e[0].blur(),this.showTimeout&&(clearTimeout(this.showTimeout),this.showTimeout=0)),e.removeDataSM("mousedown"),this.$root.triggerHandler("mouseleave.smapi",e[0]))},menuHide:function(t){if(this.$root.triggerHandler("beforehide.smapi",t[0])!==!1&&(canAnimate&&t.stop(!0,!0),"none"!=t.css("display"))){var e=function(){t.css("z-index","")};this.isCollapsible()?canAnimate&&this.opts.collapsibleHideFunction?this.opts.collapsibleHideFunction.call(this,t,e):t.hide(this.opts.collapsibleHideDuration,e):canAnimate&&this.opts.hideFunction?this.opts.hideFunction.call(this,t,e):t.hide(this.opts.hideDuration,e),t.dataSM("scroll")&&(this.menuScrollStop(t),t.css({"touch-action":"","-ms-touch-action":"","-webkit-transform":"",transform:""}).off(".smartmenus_scroll").removeDataSM("scroll").dataSM("scroll-arrows").hide()),t.dataSM("parent-a").removeClass( +"highlighted").attr("aria-expanded","false"),t.attr({"aria-expanded":"false","aria-hidden":"true"});var i=t.dataSM("level");this.activatedItems.splice(i-1,1),this.visibleSubMenus.splice($.inArray(t,this.visibleSubMenus),1),this.$root.triggerHandler("hide.smapi",t[0])}},menuHideAll:function(){this.showTimeout&&(clearTimeout(this.showTimeout),this.showTimeout=0);for(var t=this.opts.isPopup?1:0,e=this.visibleSubMenus.length-1;e>=t;e--)this.menuHide(this.visibleSubMenus[e]);this.opts.isPopup&&(canAnimate&&this.$root.stop(!0,!0),this.$root.is(":visible")&&(canAnimate&&this.opts.hideFunction?this.opts.hideFunction.call(this,this.$root):this.$root.hide(this.opts.hideDuration))),this.activatedItems=[],this.visibleSubMenus=[],this.clickActivated=!1,this.focusActivated=!1,this.zIndexInc=0,this.$root.triggerHandler("hideAll.smapi")},menuHideSubMenus:function(t){for(var e=this.activatedItems.length-1;e>=t;e--){var i=this.activatedItems[e].dataSM("sub");i&&this.menuHide(i)}},menuInit:function(t){if(!t.dataSM("in-mega")){ +t.hasClass("mega-menu")&&t.find("ul").dataSM("in-mega",!0);for(var e=2,i=t[0];(i=i.parentNode.parentNode)!=this.$root[0];)e++;var s=t.prevAll("a").eq(-1);s.length||(s=t.prevAll().find("a").eq(-1)),s.addClass("has-submenu").dataSM("sub",t),t.dataSM("parent-a",s).dataSM("level",e).parent().dataSM("sub",t);var o=s.attr("id")||this.accessIdPrefix+ ++this.idInc,a=t.attr("id")||this.accessIdPrefix+ ++this.idInc;s.attr({id:o,"aria-haspopup":"true","aria-controls":a,"aria-expanded":"false"}),t.attr({id:a,role:"group","aria-hidden":"true","aria-labelledby":o,"aria-expanded":"false"}),this.opts.subIndicators&&s[this.opts.subIndicatorsPos](this.$subArrow.clone())}},menuPosition:function(t){var e,i,s=t.dataSM("parent-a"),o=s.closest("li"),a=o.parent(),n=t.dataSM("level"),r=this.getWidth(t),h=this.getHeight(t),u=s.offset(),l=u.left,c=u.top,d=this.getWidth(s),m=this.getHeight(s),p=$(window),f=p.scrollLeft(),v=p.scrollTop(),b=this.getViewportWidth(),S=this.getViewportHeight(),g=a.parent().is("[data-sm-horizontal-sub]" +)||2==n&&!a.hasClass("sm-vertical"),M=this.opts.rightToLeftSubMenus&&!o.is("[data-sm-reverse]")||!this.opts.rightToLeftSubMenus&&o.is("[data-sm-reverse]"),w=2==n?this.opts.mainMenuSubOffsetX:this.opts.subMenusSubOffsetX,T=2==n?this.opts.mainMenuSubOffsetY:this.opts.subMenusSubOffsetY;if(g?(e=M?d-r-w:w,i=this.opts.bottomToTopSubMenus?-h-T:m+T):(e=M?w-r:d-w,i=this.opts.bottomToTopSubMenus?m-T-h:T),this.opts.keepInViewport){var y=l+e,I=c+i;if(M&&f>y?e=g?f-y+e:d-w:!M&&y+r>f+b&&(e=g?f+b-r-y+e:w-r),g||(S>h&&I+h>v+S?i+=v+S-h-I:(h>=S||v>I)&&(i+=v-I)),g&&(I+h>v+S+.49||v>I)||!g&&h>S+.49){var x=this;t.dataSM("scroll-arrows")||t.dataSM("scroll-arrows",$([$('')[0],$('')[0]]).on({mouseenter:function(){t.dataSM("scroll").up=$(this).hasClass("scroll-up"),x.menuScroll(t)},mouseleave:function(e){x.menuScrollStop(t),x.menuScrollOut(t,e)},"mousewheel DOMMouseScroll":function(t){ +t.preventDefault()}}).insertAfter(t));var A=".smartmenus_scroll";if(t.dataSM("scroll",{y:this.cssTransforms3d?0:i-m,step:1,itemH:m,subH:h,arrowDownH:this.getHeight(t.dataSM("scroll-arrows").eq(1))}).on(getEventsNS({mouseover:function(e){x.menuScrollOver(t,e)},mouseout:function(e){x.menuScrollOut(t,e)},"mousewheel DOMMouseScroll":function(e){x.menuScrollMousewheel(t,e)}},A)).dataSM("scroll-arrows").css({top:"auto",left:"0",marginLeft:e+(parseInt(t.css("border-left-width"))||0),width:r-(parseInt(t.css("border-left-width"))||0)-(parseInt(t.css("border-right-width"))||0),zIndex:t.css("z-index")}).eq(g&&this.opts.bottomToTopSubMenus?0:1).show(),this.isFixed()){var C={};C[touchEvents?"touchstart touchmove touchend":"pointerdown pointermove pointerup MSPointerDown MSPointerMove MSPointerUp"]=function(e){x.menuScrollTouch(t,e)},t.css({"touch-action":"none","-ms-touch-action":"none"}).on(getEventsNS(C,A))}}}t.css({top:"auto",left:"0",marginLeft:e,marginTop:i-m})},menuScroll:function(t,e,i){var s,o=t.dataSM("scroll"), +a=t.dataSM("scroll-arrows"),n=o.up?o.upEnd:o.downEnd;if(!e&&o.momentum){if(o.momentum*=.92,s=o.momentum,.5>s)return this.menuScrollStop(t),void 0}else s=i||(e||!this.opts.scrollAccelerate?this.opts.scrollStep:Math.floor(o.step));var r=t.dataSM("level");if(this.activatedItems[r-1]&&this.activatedItems[r-1].dataSM("sub")&&this.activatedItems[r-1].dataSM("sub").is(":visible")&&this.menuHideSubMenus(r-1),o.y=o.up&&o.y>=n||!o.up&&n>=o.y?o.y:Math.abs(n-o.y)>s?o.y+(o.up?s:-s):n,t.css(this.cssTransforms3d?{"-webkit-transform":"translate3d(0, "+o.y+"px, 0)",transform:"translate3d(0, "+o.y+"px, 0)"}:{marginTop:o.y}),mouse&&(o.up&&o.y>o.downEnd||!o.up&&o.y0;t.dataSM("scroll-arrows").eq(i?0:1).is(":visible")&&(t.dataSM("scroll").up=i,this.menuScroll(t,!0))}e.preventDefault()},menuScrollOut:function(t,e){mouse&&(/^scroll-(up|down)/.test((e.relatedTarget||"").className)||(t[0]==e.relatedTarget||$.contains(t[0],e.relatedTarget))&&this.getClosestMenu(e.relatedTarget)==t[0]||t.dataSM("scroll-arrows").css("visibility","hidden"))},menuScrollOver:function(t,e){if(mouse&&!/^scroll-(up|down)/.test(e.target.className)&&this.getClosestMenu(e.target)==t[0]){this.menuScrollRefreshData(t);var i=t.dataSM("scroll"),s=$(window).scrollTop()-t.dataSM("parent-a").offset().top-i.itemH;t.dataSM("scroll-arrows").eq(0).css("margin-top",s).end().eq(1).css("margin-top",s+this.getViewportHeight()-i.arrowDownH).end().css("visibility","visible")}},menuScrollRefreshData:function(t){var e=t.dataSM("scroll"),i=$(window).scrollTop()-t.dataSM("parent-a").offset().top-e.itemH;this.cssTransforms3d&&(i=-(parseFloat(t.css("margin-top"))-i)),$.extend(e,{upEnd:i, +downEnd:i+this.getViewportHeight()-e.subH})},menuScrollStop:function(t){return this.scrollTimeout?(cancelAnimationFrame(this.scrollTimeout),this.scrollTimeout=0,t.dataSM("scroll").step=1,!0):void 0},menuScrollTouch:function(t,e){if(e=e.originalEvent,isTouchEvent(e)){var i=this.getTouchPoint(e);if(this.getClosestMenu(i.target)==t[0]){var s=t.dataSM("scroll");if(/(start|down)$/i.test(e.type))this.menuScrollStop(t)?(e.preventDefault(),this.$touchScrollingSub=t):this.$touchScrollingSub=null,this.menuScrollRefreshData(t),$.extend(s,{touchStartY:i.pageY,touchStartTime:e.timeStamp});else if(/move$/i.test(e.type)){var o=void 0!==s.touchY?s.touchY:s.touchStartY;if(void 0!==o&&o!=i.pageY){this.$touchScrollingSub=t;var a=i.pageY>o;void 0!==s.up&&s.up!=a&&$.extend(s,{touchStartY:i.pageY,touchStartTime:e.timeStamp}),$.extend(s,{up:a,touchY:i.pageY}),this.menuScroll(t,!0,Math.abs(i.pageY-o))}e.preventDefault()}else void 0!==s.touchY&&((s.momentum=15*Math.pow(Math.abs(i.pageY-s.touchStartY)/(e.timeStamp-s.touchStartTime),2) +)&&(this.menuScrollStop(t),this.menuScroll(t),e.preventDefault()),delete s.touchY)}}},menuShow:function(t){if((t.dataSM("beforefirstshowfired")||(t.dataSM("beforefirstshowfired",!0),this.$root.triggerHandler("beforefirstshow.smapi",t[0])!==!1))&&this.$root.triggerHandler("beforeshow.smapi",t[0])!==!1&&(t.dataSM("shown-before",!0),canAnimate&&t.stop(!0,!0),!t.is(":visible"))){var e=t.dataSM("parent-a"),i=this.isCollapsible();if((this.opts.keepHighlighted||i)&&e.addClass("highlighted"),i)t.removeClass("sm-nowrap").css({zIndex:"",width:"auto",minWidth:"",maxWidth:"",top:"",left:"",marginLeft:"",marginTop:""});else{if(t.css("z-index",this.zIndexInc=(this.zIndexInc||this.getStartZIndex())+1),(this.opts.subMenusMinWidth||this.opts.subMenusMaxWidth)&&(t.css({width:"auto",minWidth:"",maxWidth:""}).addClass("sm-nowrap"),this.opts.subMenusMinWidth&&t.css("min-width",this.opts.subMenusMinWidth),this.opts.subMenusMaxWidth)){var s=this.getWidth(t);t.css("max-width",this.opts.subMenusMaxWidth),s>this.getWidth(t +)&&t.removeClass("sm-nowrap").css("width",this.opts.subMenusMaxWidth)}this.menuPosition(t)}var o=function(){t.css("overflow","")};i?canAnimate&&this.opts.collapsibleShowFunction?this.opts.collapsibleShowFunction.call(this,t,o):t.show(this.opts.collapsibleShowDuration,o):canAnimate&&this.opts.showFunction?this.opts.showFunction.call(this,t,o):t.show(this.opts.showDuration,o),e.attr("aria-expanded","true"),t.attr({"aria-expanded":"true","aria-hidden":"false"}),this.visibleSubMenus.push(t),this.$root.triggerHandler("show.smapi",t[0])}},popupHide:function(t){this.hideTimeout&&(clearTimeout(this.hideTimeout),this.hideTimeout=0);var e=this;this.hideTimeout=setTimeout(function(){e.menuHideAll()},t?1:this.opts.hideTimeout)},popupShow:function(t,e){if(!this.opts.isPopup)return alert('SmartMenus jQuery Error:\n\nIf you want to show this menu via the "popupShow" method, set the isPopup:true option.'),void 0;if(this.hideTimeout&&(clearTimeout(this.hideTimeout),this.hideTimeout=0),this.$root.dataSM("shown-before",!0), +canAnimate&&this.$root.stop(!0,!0),!this.$root.is(":visible")){this.$root.css({left:t,top:e});var i=this,s=function(){i.$root.css("overflow","")};canAnimate&&this.opts.showFunction?this.opts.showFunction.call(this,this.$root,s):this.$root.show(this.opts.showDuration,s),this.visibleSubMenus[0]=this.$root}},refresh:function(){this.destroy(!0),this.init(!0)},rootKeyDown:function(t){if(this.handleEvents())switch(t.keyCode){case 27:var e=this.activatedItems[0];if(e){this.menuHideAll(),e[0].focus();var i=e.dataSM("sub");i&&this.menuHide(i)}break;case 32:var s=$(t.target);if(s.is("a")&&this.handleItemEvents(s)){var i=s.dataSM("sub");i&&!i.is(":visible")&&(this.itemClick({currentTarget:t.target}),t.preventDefault())}}},rootOut:function(t){if(this.handleEvents()&&!this.isTouchMode()&&t.target!=this.$root[0]&&(this.hideTimeout&&(clearTimeout(this.hideTimeout),this.hideTimeout=0),!this.opts.showOnClick||!this.opts.hideOnClick)){var e=this;this.hideTimeout=setTimeout(function(){e.menuHideAll()},this.opts.hideTimeout)}}, +rootOver:function(t){this.handleEvents()&&!this.isTouchMode()&&t.target!=this.$root[0]&&this.hideTimeout&&(clearTimeout(this.hideTimeout),this.hideTimeout=0)},winResize:function(t){if(this.handleEvents()){if(!("onorientationchange"in window)||"orientationchange"==t.type){var e=this.isCollapsible();this.wasCollapsible&&e||(this.activatedItems.length&&this.activatedItems[this.activatedItems.length-1][0].blur(),this.menuHideAll()),this.wasCollapsible=e}}else if(this.$disableOverlay){var i=this.$root.offset();this.$disableOverlay.css({top:i.top,left:i.left,width:this.$root.outerWidth(),height:this.$root.outerHeight()})}}}}),$.fn.dataSM=function(t,e){return e?this.data(t+"_smartmenus",e):this.data(t+"_smartmenus")},$.fn.removeDataSM=function(t){return this.removeData(t+"_smartmenus")},$.fn.smartmenus=function(options){if("string"==typeof options){var args=arguments,method=options;return Array.prototype.shift.call(args),this.each(function(){var t=$(this).data("smartmenus");t&&t[method]&&t[method].apply(t,args)})} +return this.each(function(){var dataOpts=$(this).data("sm-options")||null;if(dataOpts)try{dataOpts=eval("("+dataOpts+")")}catch(e){dataOpts=null,alert('ERROR\n\nSmartMenus jQuery init:\nInvalid "data-sm-options" attribute value syntax.')}new $.SmartMenus(this,$.extend({},$.fn.smartmenus.defaults,options,dataOpts))})},$.fn.smartmenus.defaults={isPopup:!1,mainMenuSubOffsetX:0,mainMenuSubOffsetY:0,subMenusSubOffsetX:0,subMenusSubOffsetY:0,subMenusMinWidth:"10em",subMenusMaxWidth:"20em",subIndicators:!0,subIndicatorsPos:"append",subIndicatorsText:"",scrollStep:30,scrollAccelerate:!0,showTimeout:250,hideTimeout:500,showDuration:0,showFunction:null,hideDuration:0,hideFunction:function(t,e){t.fadeOut(200,e)},collapsibleShowDuration:0,collapsibleShowFunction:function(t,e){t.slideDown(200,e)},collapsibleHideDuration:0,collapsibleHideFunction:function(t,e){t.slideUp(200,e)},showOnClick:!1,hideOnClick:!0,noMouseOver:!1,keepInViewport:!0,keepHighlighted:!0,markCurrentItem:!1,markCurrentTree:!0,rightToLeftSubMenus:!1, +bottomToTopSubMenus:!1,collapsibleBehavior:"default"},$}); diff --git a/HEAD/libjwt.css b/HEAD/libjwt.css new file mode 100644 index 00000000..3ed9915e --- /dev/null +++ b/HEAD/libjwt.css @@ -0,0 +1,19 @@ +a.button { + background-color: #EFF4FF; + border: none; + color: #2557D6; + padding: 10px 20px; + text-align: center; + text-decoration: none; + display: inline-block; + margin-right: 13px; + margin-left: -1px; + margin-bottom: 10px; + border-radius: 100px; + cursor: pointer; + transition: box-shadow 0.5s ease; +} + +a.button:hover { + box-shadow: inset 0em 0em 0em 10em #DCE6FF; +} diff --git a/HEAD/maclara-pill.svg b/HEAD/maclara-pill.svg new file mode 100644 index 00000000..07b7053f --- /dev/null +++ b/HEAD/maclara-pill.svg @@ -0,0 +1 @@ +Sponsored by: maClara, LLCSponsored bymaClara, LLC \ No newline at end of file diff --git a/HEAD/minus.svg b/HEAD/minus.svg new file mode 100644 index 00000000..f70d0c1a --- /dev/null +++ b/HEAD/minus.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/HEAD/minusd.svg b/HEAD/minusd.svg new file mode 100644 index 00000000..5f8e8796 --- /dev/null +++ b/HEAD/minusd.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/HEAD/nav_f.png b/HEAD/nav_f.png new file mode 100644 index 0000000000000000000000000000000000000000..72a58a529ed3a9ed6aa0c51a79cf207e026deee2 GIT binary patch literal 153 zcmeAS@N?(olHy`uVBq!ia0vp^j6iI`!2~2XGqLUlQVE_ejv*C{Z|{2ZH7M}7UYxc) zn!W8uqtnIQ>_z8U literal 0 HcmV?d00001 diff --git a/HEAD/nav_fd.png b/HEAD/nav_fd.png new file mode 100644 index 0000000000000000000000000000000000000000..032fbdd4c54f54fa9a2e6423b94ef4b2ebdfaceb GIT binary patch literal 169 zcmeAS@N?(olHy`uVBq!ia0vp^j6iI`!2~2XGqLUlQU#tajv*C{Z|C~*H7f|XvG1G8 zt7aS*L7xwMeS}!z6R#{C5tIw-s~AJ==F^i}x3XyJseHR@yF& zerFf(Zf;Dd{+(0lDIROL@Sj-Ju2JQ8&-n%4%q?>|^bShc&lR?}7HeMo@BDl5N(aHY Uj$gdr1MOz;boFyt=akR{0D!zeaR2}S literal 0 HcmV?d00001 diff --git a/HEAD/nav_g.png b/HEAD/nav_g.png new file mode 100644 index 0000000000000000000000000000000000000000..2093a237a94f6c83e19ec6e5fd42f7ddabdafa81 GIT binary patch literal 95 zcmeAS@N?(olHy`uVBq!ia0vp^j6lrB!3HFm1ilyoDK$?Q$B+ufw|5PB85lU25BhtE tr?otc=hd~V+ws&_A@j8Fiv!KF$B+ufw|5=67#uj90@pIL wZ=Q8~_Ju`#59=RjDrmm`tMD@M=!-l18IR?&vFVdQ&MBb@0HFXL6W-eg#Jd_@e6*DPn)w;=|1H}Zvm9l6xXXB%>yL=NQU;mg M>FVdQ&MBb@0Bdt1Qvd(} literal 0 HcmV?d00001 diff --git a/HEAD/navtree.css b/HEAD/navtree.css new file mode 100644 index 00000000..6b1e5e46 --- /dev/null +++ b/HEAD/navtree.css @@ -0,0 +1,149 @@ +#nav-tree .children_ul { + margin:0; + padding:4px; +} + +#nav-tree ul { + list-style:none outside none; + margin:0px; + padding:0px; +} + +#nav-tree li { + white-space:nowrap; + margin:0px; + padding:0px; +} + +#nav-tree .plus { + margin:0px; +} + +#nav-tree .selected { + background-image: url('tab_a.png'); + background-repeat:repeat-x; + color: white; + text-shadow: 0px 1px 1px rgba(0, 0, 0, 1.0); +} + +#nav-tree .selected .arrow { + color: #9CAFD4; + text-shadow: none; +} + +#nav-tree img { + margin:0px; + padding:0px; + border:0px; + vertical-align: middle; +} + +#nav-tree a { + text-decoration:none; + padding:0px; + margin:0px; +} + +#nav-tree .label { + margin:0px; + padding:0px; + font: 12px 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; +} + +#nav-tree .label a { + padding:2px; +} + +#nav-tree .selected a { + text-decoration:none; + color:white; +} + +#nav-tree .children_ul { + margin:0px; + padding:0px; +} + +#nav-tree .item { + margin:0px; + padding:0px; +} + +#nav-tree { + padding: 0px 0px; + font-size:14px; + overflow:auto; +} + +#doc-content { + overflow:auto; + display:block; + padding:0px; + margin:0px; + -webkit-overflow-scrolling : touch; /* iOS 5+ */ +} + +#side-nav { + padding:0 6px 0 0; + margin: 0px; + display:block; + position: absolute; + left: 0px; + width: $width; + overflow : hidden; +} + +.ui-resizable .ui-resizable-handle { + display:block; +} + +.ui-resizable-e { + background-image:url('splitbar.png'); + background-size:100%; + background-repeat:repeat-y; + background-attachment: scroll; + cursor:ew-resize; + height:100%; + right:0; + top:0; + width:6px; +} + +.ui-resizable-handle { + display:none; + font-size:0.1px; + position:absolute; + z-index:1; +} + +#nav-tree-contents { + margin: 6px 0px 0px 0px; +} + +#nav-tree { + background-repeat:repeat-x; + background-color: #F9FAFC; + -webkit-overflow-scrolling : touch; /* iOS 5+ */ +} + +#nav-sync { + position:absolute; + top:5px; + right:24px; + z-index:0; +} + +#nav-sync img { + opacity:0.3; +} + +#nav-sync img:hover { + opacity:0.9; +} + +@media print +{ + #nav-tree { display: none; } + div.ui-resizable-handle { display: none; position: relative; } +} + diff --git a/HEAD/navtree.js b/HEAD/navtree.js new file mode 100644 index 00000000..2d4fa84a --- /dev/null +++ b/HEAD/navtree.js @@ -0,0 +1,483 @@ +/* + @licstart The following is the entire license notice for the JavaScript code in this file. + + The MIT License (MIT) + + Copyright (C) 1997-2020 by Dimitri van Heesch + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software + and associated documentation files (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, publish, distribute, + sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + @licend The above is the entire license notice for the JavaScript code in this file + */ + +function initNavTree(toroot,relpath) { + let navTreeSubIndices = []; + const ARROW_DOWN = '▼'; + const ARROW_RIGHT = '►'; + const NAVPATH_COOKIE_NAME = ''+'navpath'; + + const getData = function(varName) { + const i = varName.lastIndexOf('/'); + const n = i>=0 ? varName.substring(i+1) : varName; + return eval(n.replace(/-/g,'_')); + } + + const stripPath = function(uri) { + return uri.substring(uri.lastIndexOf('/')+1); + } + + const stripPath2 = function(uri) { + const i = uri.lastIndexOf('/'); + const s = uri.substring(i+1); + const m = uri.substring(0,i+1).match(/\/d\w\/d\w\w\/$/); + return m ? uri.substring(i-6) : s; + } + + const hashValue = function() { + return $(location).attr('hash').substring(1).replace(/[^\w-]/g,''); + } + + const hashUrl = function() { + return '#'+hashValue(); + } + + const pathName = function() { + return $(location).attr('pathname').replace(/[^-A-Za-z0-9+&@#/%?=~_|!:,.;()]/g, ''); + } + + const storeLink = function(link) { + if (!$("#nav-sync").hasClass('sync')) { + Cookie.writeSetting(NAVPATH_COOKIE_NAME,link,0); + } + } + + const deleteLink = function() { + Cookie.eraseSetting(NAVPATH_COOKIE_NAME); + } + + const cachedLink = function() { + return Cookie.readSetting(NAVPATH_COOKIE_NAME,''); + } + + const getScript = function(scriptName,func) { + const head = document.getElementsByTagName("head")[0]; + const script = document.createElement('script'); + script.id = scriptName; + script.type = 'text/javascript'; + script.onload = func; + script.src = scriptName+'.js'; + head.appendChild(script); + } + + const createIndent = function(o,domNode,node) { + let level=-1; + let n = node; + while (n.parentNode) { level++; n=n.parentNode; } + if (node.childrenData) { + const imgNode = document.createElement("span"); + imgNode.className = 'arrow'; + imgNode.style.paddingLeft=(16*level).toString()+'px'; + imgNode.innerHTML=ARROW_RIGHT; + node.plus_img = imgNode; + node.expandToggle = document.createElement("a"); + node.expandToggle.href = "javascript:void(0)"; + node.expandToggle.onclick = function() { + if (node.expanded) { + $(node.getChildrenUL()).slideUp("fast"); + node.plus_img.innerHTML=ARROW_RIGHT; + node.expanded = false; + } else { + expandNode(o, node, false, true); + } + } + node.expandToggle.appendChild(imgNode); + domNode.appendChild(node.expandToggle); + } else { + let span = document.createElement("span"); + span.className = 'arrow'; + span.style.width = 16*(level+1)+'px'; + span.innerHTML = ' '; + domNode.appendChild(span); + } + } + + let animationInProgress = false; + + const gotoAnchor = function(anchor,aname) { + let pos, docContent = $('#doc-content'); + let ancParent = $(anchor.parent()); + if (ancParent.hasClass('memItemLeft') || ancParent.hasClass('memtitle') || + ancParent.hasClass('fieldname') || ancParent.hasClass('fieldtype') || + ancParent.is(':header')) { + pos = ancParent.offset().top; + } else if (anchor.position()) { + pos = anchor.offset().top; + } + if (pos) { + const dcOffset = docContent.offset().top; + const dcHeight = docContent.height(); + const dcScrHeight = docContent[0].scrollHeight + const dcScrTop = docContent.scrollTop(); + let dist = Math.abs(Math.min(pos-dcOffset,dcScrHeight-dcHeight-dcScrTop)); + animationInProgress = true; + docContent.animate({ + scrollTop: pos + dcScrTop - dcOffset + },Math.max(50,Math.min(500,dist)),function() { + animationInProgress=false; + if (anchor.parent().attr('class')=='memItemLeft') { + let rows = $('.memberdecls tr[class$="'+hashValue()+'"]'); + glowEffect(rows.children(),300); // member without details + } else if (anchor.parent().attr('class')=='fieldname') { + glowEffect(anchor.parent().parent(),1000); // enum value + } else if (anchor.parent().attr('class')=='fieldtype') { + glowEffect(anchor.parent().parent(),1000); // struct field + } else if (anchor.parent().is(":header")) { + glowEffect(anchor.parent(),1000); // section header + } else { + glowEffect(anchor.next(),1000); // normal member + } + }); + } + } + + const newNode = function(o, po, text, link, childrenData, lastNode) { + const node = { + children : [], + childrenData : childrenData, + depth : po.depth + 1, + relpath : po.relpath, + isLast : lastNode, + li : document.createElement("li"), + parentNode : po, + itemDiv : document.createElement("div"), + labelSpan : document.createElement("span"), + label : document.createTextNode(text), + expanded : false, + childrenUL : null, + getChildrenUL : function() { + if (!this.childrenUL) { + this.childrenUL = document.createElement("ul"); + this.childrenUL.className = "children_ul"; + this.childrenUL.style.display = "none"; + this.li.appendChild(node.childrenUL); + } + return node.childrenUL; + }, + }; + + node.itemDiv.className = "item"; + node.labelSpan.className = "label"; + createIndent(o,node.itemDiv,node); + node.itemDiv.appendChild(node.labelSpan); + node.li.appendChild(node.itemDiv); + + const a = document.createElement("a"); + node.labelSpan.appendChild(a); + po.getChildrenUL().appendChild(node.li); + a.appendChild(node.label); + if (link) { + let url; + if (link.substring(0,1)=='^') { + url = link.substring(1); + link = url; + } else { + url = node.relpath+link; + } + a.className = stripPath(link.replace('#',':')); + if (link.indexOf('#')!=-1) { + const aname = '#'+link.split('#')[1]; + const srcPage = stripPath(pathName()); + const targetPage = stripPath(link.split('#')[0]); + a.href = srcPage!=targetPage ? url : aname; + a.onclick = function() { + storeLink(link); + aPPar = $(a).parent().parent(); + if (!aPPar.hasClass('selected')) { + $('.item').removeClass('selected'); + $('.item').removeAttr('id'); + aPPar.addClass('selected'); + aPPar.attr('id','selected'); + } + const anchor = $(aname); + gotoAnchor(anchor,aname); + }; + } else { + a.href = url; + a.onclick = () => storeLink(link); + } + } else if (childrenData != null) { + a.className = "nolink"; + a.href = "javascript:void(0)"; + a.onclick = node.expandToggle.onclick; + } + return node; + } + + const showRoot = function() { + const headerHeight = $("#top").height(); + const footerHeight = $("#nav-path").height(); + const windowHeight = $(window).height() - headerHeight - footerHeight; + (function() { // retry until we can scroll to the selected item + try { + const navtree=$('#nav-tree'); + navtree.scrollTo('#selected',100,{offset:-windowHeight/2}); + } catch (err) { + setTimeout(arguments.callee, 0); + } + })(); + } + + const expandNode = function(o, node, imm, setFocus) { + if (node.childrenData && !node.expanded) { + if (typeof(node.childrenData)==='string') { + const varName = node.childrenData; + getScript(node.relpath+varName,function() { + node.childrenData = getData(varName); + expandNode(o, node, imm, setFocus); + }); + } else { + if (!node.childrenVisited) { + getNode(o, node); + } + $(node.getChildrenUL()).slideDown("fast"); + node.plus_img.innerHTML = ARROW_DOWN; + node.expanded = true; + if (setFocus) { + $(node.expandToggle).focus(); + } + } + } + } + + const glowEffect = function(n,duration) { + n.addClass('glow').delay(duration).queue(function(next) { + $(this).removeClass('glow');next(); + }); + } + + const highlightAnchor = function() { + const aname = hashUrl(); + const anchor = $(aname); + gotoAnchor(anchor,aname); + } + + const selectAndHighlight = function(hash,n) { + let a; + if (hash) { + const link=stripPath(pathName())+':'+hash.substring(1); + a=$('.item a[class$="'+link+'"]'); + } + if (a && a.length) { + a.parent().parent().addClass('selected'); + a.parent().parent().attr('id','selected'); + highlightAnchor(); + } else if (n) { + $(n.itemDiv).addClass('selected'); + $(n.itemDiv).attr('id','selected'); + } + let topOffset=5; + if ($('#nav-tree-contents .item:first').hasClass('selected')) { + topOffset+=25; + } + $('#nav-sync').css('top',topOffset+'px'); + showRoot(); + } + + const showNode = function(o, node, index, hash) { + if (node && node.childrenData) { + if (typeof(node.childrenData)==='string') { + const varName = node.childrenData; + getScript(node.relpath+varName,function() { + node.childrenData = getData(varName); + showNode(o,node,index,hash); + }); + } else { + if (!node.childrenVisited) { + getNode(o, node); + } + $(node.getChildrenUL()).css({'display':'block'}); + node.plus_img.innerHTML = ARROW_DOWN; + node.expanded = true; + const n = node.children[o.breadcrumbs[index]]; + if (index+11 ? '#'+parts[1].replace(/[^\w-]/g,'') : ''; + } + if (hash.match(/^#l\d+$/)) { + const anchor=$('a[name='+hash.substring(1)+']'); + glowEffect(anchor.parent(),1000); // line number + hash=''; // strip line number anchors + } + const url=root+hash; + let i=-1; + while (NAVTREEINDEX[i+1]<=url) i++; + if (i==-1) { i=0; root=NAVTREE[0][1]; } // fallback: show index + if (navTreeSubIndices[i]) { + gotoNode(o,i,root,hash,relpath) + } else { + getScript(relpath+'navtreeindex'+i,function() { + navTreeSubIndices[i] = eval('NAVTREEINDEX'+i); + if (navTreeSubIndices[i]) { + gotoNode(o,i,root,hash,relpath); + } + }); + } + } + + const showSyncOff = function(n,relpath) { + n.html(''); + } + + const showSyncOn = function(n,relpath) { + n.html(''); + } + + const o = { + toroot : toroot, + node : { + childrenData : NAVTREE, + children : [], + childrenUL : document.createElement("ul"), + getChildrenUL : function() { return this.childrenUL }, + li : document.getElementById("nav-tree-contents"), + depth : 0, + relpath : relpath, + expanded : false, + isLast : true, + plus_img : document.createElement("span"), + }, + }; + o.node.li.appendChild(o.node.childrenUL); + o.node.plus_img.className = 'arrow'; + o.node.plus_img.innerHTML = ARROW_RIGHT; + + const navSync = $('#nav-sync'); + if (cachedLink()) { + showSyncOff(navSync,relpath); + navSync.removeClass('sync'); + } else { + showSyncOn(navSync,relpath); + } + + navSync.click(() => { + const navSync = $('#nav-sync'); + if (navSync.hasClass('sync')) { + navSync.removeClass('sync'); + showSyncOff(navSync,relpath); + storeLink(stripPath2(pathName())+hashUrl()); + } else { + navSync.addClass('sync'); + showSyncOn(navSync,relpath); + deleteLink(); + } + }); + + navTo(o,toroot,hashUrl(),relpath); + showRoot(); + + $(window).bind('hashchange', () => { + if (!animationInProgress) { + if (window.location.hash && window.location.hash.length>1) { + let a; + if ($(location).attr('hash')) { + const clslink=stripPath(pathName())+':'+hashValue(); + a=$('.item a[class$="'+clslink.replace(/1|%O$WD@{VPM$7~Ar*{o?;hlAFyLXmaDC0y znK1_#cQqJWPES%4Uujug^TE?jMft$}Eq^WaR~)%f)vSNs&gek&x%A9X9sM + + + + + + + +LibJWT: Related Pages + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + +
+
LibJWT 2.9.99 +
+
The C JSON Web Token Library +JWK +JWKS
+
+
+ + + +
+
+ +
+
+
+ +
+
+
Related Pages
+
+
+
Here is a list of all related documentation pages:
+ + +
 Todo List
+
+
+
+ + + + + diff --git a/HEAD/plus.svg b/HEAD/plus.svg new file mode 100644 index 00000000..07520165 --- /dev/null +++ b/HEAD/plus.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/HEAD/plusd.svg b/HEAD/plusd.svg new file mode 100644 index 00000000..0c65bfe9 --- /dev/null +++ b/HEAD/plusd.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/HEAD/resize.js b/HEAD/resize.js new file mode 100644 index 00000000..178d03bc --- /dev/null +++ b/HEAD/resize.js @@ -0,0 +1,147 @@ +/* + @licstart The following is the entire license notice for the JavaScript code in this file. + + The MIT License (MIT) + + Copyright (C) 1997-2020 by Dimitri van Heesch + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software + and associated documentation files (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, publish, distribute, + sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + @licend The above is the entire license notice for the JavaScript code in this file + */ + +function initResizable(treeview) { + let sidenav,navtree,content,header,footer,barWidth=6; + const RESIZE_COOKIE_NAME = ''+'width'; + + function resizeWidth() { + const sidenavWidth = $(sidenav).outerWidth(); + content.css({marginLeft:parseInt(sidenavWidth)+"px"}); + if (typeof page_layout!=='undefined' && page_layout==1) { + footer.css({marginLeft:parseInt(sidenavWidth)+"px"}); + } + Cookie.writeSetting(RESIZE_COOKIE_NAME,sidenavWidth-barWidth); + } + + function restoreWidth(navWidth) { + content.css({marginLeft:parseInt(navWidth)+barWidth+"px"}); + if (typeof page_layout!=='undefined' && page_layout==1) { + footer.css({marginLeft:parseInt(navWidth)+barWidth+"px"}); + } + sidenav.css({width:navWidth + "px"}); + } + + function resizeHeight(treeview) { + const headerHeight = header.outerHeight(); + const windowHeight = $(window).height(); + let contentHeight; + if (treeview) + { + const footerHeight = footer.outerHeight(); + let navtreeHeight,sideNavHeight; + if (typeof page_layout==='undefined' || page_layout==0) { /* DISABLE_INDEX=NO */ + contentHeight = windowHeight - headerHeight - footerHeight; + navtreeHeight = contentHeight; + sideNavHeight = contentHeight; + } else if (page_layout==1) { /* DISABLE_INDEX=YES */ + contentHeight = windowHeight - footerHeight; + navtreeHeight = windowHeight - headerHeight; + sideNavHeight = windowHeight; + } + navtree.css({height:navtreeHeight + "px"}); + sidenav.css({height:sideNavHeight + "px"}); + } + else + { + contentHeight = windowHeight - headerHeight; + } + content.css({height:contentHeight + "px"}); + if (location.hash.slice(1)) { + (document.getElementById(location.hash.slice(1))||document.body).scrollIntoView(); + } + } + + function collapseExpand() { + let newWidth; + if (sidenav.width()>0) { + newWidth=0; + } else { + const width = Cookie.readSetting(RESIZE_COOKIE_NAME,250); + newWidth = (width>250 && width<$(window).width()) ? width : 250; + } + restoreWidth(newWidth); + const sidenavWidth = $(sidenav).outerWidth(); + Cookie.writeSetting(RESIZE_COOKIE_NAME,sidenavWidth-barWidth); + } + + header = $("#top"); + content = $("#doc-content"); + footer = $("#nav-path"); + sidenav = $("#side-nav"); + if (!treeview) { +// title = $("#titlearea"); +// titleH = $(title).height(); +// let animating = false; +// content.on("scroll", function() { +// slideOpts = { duration: 200, +// step: function() { +// contentHeight = $(window).height() - header.outerHeight(); +// content.css({ height : contentHeight + "px" }); +// }, +// done: function() { animating=false; } +// }; +// if (content.scrollTop()>titleH && title.css('display')!='none' && !animating) { +// title.slideUp(slideOpts); +// animating=true; +// } else if (content.scrollTop()<=titleH && title.css('display')=='none' && !animating) { +// title.slideDown(slideOpts); +// animating=true; +// } +// }); + } else { + navtree = $("#nav-tree"); + $(".side-nav-resizable").resizable({resize: function(e, ui) { resizeWidth(); } }); + $(sidenav).resizable({ minWidth: 0 }); + } + $(window).resize(function() { resizeHeight(treeview); }); + if (treeview) + { + const device = navigator.userAgent.toLowerCase(); + const touch_device = device.match(/(iphone|ipod|ipad|android)/); + if (touch_device) { /* wider split bar for touch only devices */ + $(sidenav).css({ paddingRight:'20px' }); + $('.ui-resizable-e').css({ width:'20px' }); + $('#nav-sync').css({ right:'34px' }); + barWidth=20; + } + const width = Cookie.readSetting(RESIZE_COOKIE_NAME,250); + if (width) { restoreWidth(width); } else { resizeWidth(); } + } + resizeHeight(treeview); + const url = location.href; + const i=url.indexOf("#"); + if (i>=0) window.location.hash=url.substr(i); + const _preventDefault = function(evt) { evt.preventDefault(); }; + if (treeview) + { + $("#splitbar").bind("dragstart", _preventDefault).bind("selectstart", _preventDefault); + $(".ui-resizable-handle").dblclick(collapseExpand); + // workaround for firefox + $("body").css({overflow: "hidden"}); + } + $(window).on('load',function() { resizeHeight(treeview); }); +} +/* @license-end */ diff --git a/HEAD/splitbar.png b/HEAD/splitbar.png new file mode 100644 index 0000000000000000000000000000000000000000..fe895f2c58179b471a22d8320b39a4bd7312ec8e GIT binary patch literal 314 zcmeAS@N?(olHy`uVBq!ia0vp^Yzz!63>-{AmhX=Jf(#6djGiuzAr*{o?=JLmPLyc> z_*`QK&+BH@jWrYJ7>r6%keRM@)Qyv8R=enp0jiI>aWlGyB58O zFVR20d+y`K7vDw(hJF3;>dD*3-?v=<8M)@x|EEGLnJsniYK!2U1 Y!`|5biEc?d1`HDhPgg&ebxsLQ02F6;9RL6T literal 0 HcmV?d00001 diff --git a/HEAD/splitbard.png b/HEAD/splitbard.png new file mode 100644 index 0000000000000000000000000000000000000000..8367416d757fd7b6dc4272b6432dc75a75abd068 GIT binary patch literal 282 zcmeAS@N?(olHy`uVBq!ia0vp^Yzz!63>-{AmhX=Jf@VhhFKy35^fiT zT~&lUj3=cDh^%3HDY9k5CEku}PHXNoNC(_$U3XPb&Q*ME25pT;2(*BOgAf<+R$lzakPG`kF31()Fx{L5Wrac|GQzjeE= zueY1`Ze{#x<8=S|`~MgGetGce)#vN&|J{Cd^tS%;tBYTo?+^d68<#n_Y_xx`J||4O V@QB{^CqU0Kc)I$ztaD0e0svEzbJzd? literal 0 HcmV?d00001 diff --git a/HEAD/structjwk__item__t.html b/HEAD/structjwk__item__t.html new file mode 100644 index 00000000..965b6999 --- /dev/null +++ b/HEAD/structjwk__item__t.html @@ -0,0 +1,424 @@ + + + + + + + + +LibJWT: jwk_item_t Struct Reference + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + +
+
LibJWT 2.9.99 +
+
The C JSON Web Token Library +JWK +JWKS
+
+
+ + + +
+
+ +
+
+
+ +
+
+ +
jwk_item_t Struct Reference
+
+
+ +

Structural representation of a JWK. + More...

+ +

#include <jwt.h>

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+Data Fields

char * pem
 
jwt_crypto_provider_t provider
 
union { 
 
   void *   provider_data 
 
   struct { 
 
      void *   key 
 
      size_t   len 
 
   }   oct 
 
};  
 
int is_private_key
 
char curve [256]
 
size_t bits
 
int error
 
char error_msg [256]
 
jwk_key_type_t kty
 
jwk_pub_key_use_t use
 
jwk_key_op_t key_ops
 
jwt_alg_t alg
 
char * kid
 
const char * json
 
+

Detailed Description

+

Structural representation of a JWK.

+

This data structure is produced by importing a JWK or JWKS into a jwk_set_t object. Generally, you would not change any values here and only use this to probe the internal parser and possibly to decide whether a key applies to certain jwt_t for verification or signing.

+
Remarks
If the jwk_item_t::pem field is not NULL, then it contains a nil terminated string of the key. The underlying crypto algorith may or may not support this. It's provided as a convenience.
+

Field Documentation

+ +

◆ [union]

+ +
+
+ + + + +
union { ... } jwk_item_t
+
+ +
+
+ +

◆ alg

+ +
+
+ + + + +
jwt_alg_t jwk_item_t::alg
+
+

📄 RFC-7517 Sec 4.4 JWA Algorithm supported

+ +
+
+ +

◆ bits

+ +
+
+ + + + +
size_t jwk_item_t::bits
+
+

The number of bits in the key (may be 0)

+ +
+
+ +

◆ curve

+ +
+
+ + + + +
char jwk_item_t::curve[256]
+
+

Curve name of an "EC" or "OKP" key

+ +
+
+ +

◆ error

+ +
+
+ + + + +
int jwk_item_t::error
+
+

There was an error parsing this key (unusable)

+ +
+
+ +

◆ error_msg

+ +
+
+ + + + +
char jwk_item_t::error_msg[256]
+
+

Descriptive message for jwk_item_t::error

+ +
+
+ +

◆ is_private_key

+ +
+
+ + + + +
int jwk_item_t::is_private_key
+
+

Whether this is a public or private key

+ +
+
+ +

◆ json

+ +
+
+ + + + +
const char* jwk_item_t::json
+
+

The entire JSON string for this key

+ +
+
+ +

◆ key

+ +
+
+ + + + +
void* jwk_item_t::key
+
+

Used for HMAC key material

+ +
+
+ +

◆ key_ops

+ +
+
+ + + + +
jwk_key_op_t jwk_item_t::key_ops
+
+

📄 RFC-7517 Sec 4.3 Key operations supported

+ +
+
+ +

◆ kid

+ +
+
+ + + + +
char* jwk_item_t::kid
+
+

📄 RFC-7517 Sec 4.5 Key ID

+ +
+
+ +

◆ kty

+ +
+
+ + + + +
jwk_key_type_t jwk_item_t::kty
+
+

📄 RFC-7517 Sec 4.1 The key type of this key

+ +
+
+ +

◆ len

+ +
+
+ + + + +
size_t jwk_item_t::len
+
+

Length of HMAC key material

+ +
+
+ +

◆ [struct]

+ +
+
+ + + + +
struct { ... } jwk_item_t::oct
+
+ +
+
+ +

◆ pem

+ +
+
+ + + + +
char* jwk_item_t::pem
+
+

If not NULL, contains PEM string of this key

+ +
+
+ +

◆ provider

+ +
+
+ + + + +
jwt_crypto_provider_t jwk_item_t::provider
+
+

Crypto provider that owns this key

+ +
+
+ +

◆ provider_data

+ +
+
+ + + + +
void* jwk_item_t::provider_data
+
+

Internal data used by the provider

+ +
+
+ +

◆ use

+ +
+
+ + + + +
jwk_pub_key_use_t jwk_item_t::use
+
+

📄 RFC-7517 Sec 4.2 How this key can be used

+ +
+
+
The documentation for this struct was generated from the following file:
    +
  • include/jwt.h
  • +
+
+
+ + + + + diff --git a/HEAD/structjwk__item__t.js b/HEAD/structjwk__item__t.js new file mode 100644 index 00000000..22f8bd2f --- /dev/null +++ b/HEAD/structjwk__item__t.js @@ -0,0 +1,20 @@ +var structjwk__item__t = +[ + [ "alg", "structjwk__item__t.html#aae5092f40a4bfbe9155ddde65f5b933f", null ], + [ "bits", "structjwk__item__t.html#a2a87bc6fcdbdbd234f1beee3ba63ea61", null ], + [ "curve", "structjwk__item__t.html#a3840bd4c87181411e0f3012c020787b3", null ], + [ "error", "structjwk__item__t.html#ae5aef5762e38fc66625cedc461a32ece", null ], + [ "error_msg", "structjwk__item__t.html#a2840975767dbdc020912682c06ebdb75", null ], + [ "is_private_key", "structjwk__item__t.html#a58ab0832602b475e7c3a7009925db87e", null ], + [ "json", "structjwk__item__t.html#a3593e9e89e76e6e8b605f01a1a8a2367", null ], + [ "key", "structjwk__item__t.html#ab4f6c1d6586f6b8a63349b2f014f1f7c", null ], + [ "key_ops", "structjwk__item__t.html#a44fe44eb2d9c9466c48f19cd5f590e21", null ], + [ "kid", "structjwk__item__t.html#a874da82b485b8c541ecd0f3f8fd12a0f", null ], + [ "kty", "structjwk__item__t.html#ae2d7fed09d97d0ac3e6571e7c897d580", null ], + [ "len", "structjwk__item__t.html#a379d62be0803bca022445adb82cd99e5", null ], + [ "oct", "structjwk__item__t.html#ae03055df75f8a3940fb071d5bbcc2379", null ], + [ "pem", "structjwk__item__t.html#afecd2ad6ce52bf49a202999c184a9b2e", null ], + [ "provider", "structjwk__item__t.html#a892ffdca6722b35e24d885f01edd6679", null ], + [ "provider_data", "structjwk__item__t.html#a6cf519323148008f4549798d7df1d8f3", null ], + [ "use", "structjwk__item__t.html#a8790b92f608e12757ba07dff49b931df", null ] +]; \ No newline at end of file diff --git a/HEAD/structjwt__config__t.html b/HEAD/structjwt__config__t.html new file mode 100644 index 00000000..d835312c --- /dev/null +++ b/HEAD/structjwt__config__t.html @@ -0,0 +1,170 @@ + + + + + + + + +LibJWT: jwt_config_t Struct Reference + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + +
+
LibJWT 2.9.99 +
+
The C JSON Web Token Library +JWK +JWKS
+
+
+ + + +
+
+ +
+
+
+ +
+
+ +
jwt_config_t Struct Reference
+
+
+ +

Structure used to manage configuration state. + More...

+ +

#include <jwt.h>

+
+Collaboration diagram for jwt_config_t:
+
+
+
[legend]
+ + + + + + + + +

+Data Fields

jwk_item_tjw_key
 
jwt_alg_t alg
 
void * ctx
 
+

Detailed Description

+

Structure used to manage configuration state.

+

Field Documentation

+ +

◆ alg

+ +
+
+ + + + +
jwt_alg_t jwt_config_t::alg
+
+

For algorithm matching

+ +
+
+ +

◆ ctx

+ +
+
+ + + + +
void* jwt_config_t::ctx
+
+

User controlled context

+ +
+
+ +

◆ jw_key

+ +
+
+ + + + +
jwk_item_t* jwt_config_t::jw_key
+
+

A JWK to use for key

+ +
+
+
The documentation for this struct was generated from the following file:
    +
  • include/jwt.h
  • +
+
+
+ + + + + diff --git a/HEAD/structjwt__config__t.js b/HEAD/structjwt__config__t.js new file mode 100644 index 00000000..0755df68 --- /dev/null +++ b/HEAD/structjwt__config__t.js @@ -0,0 +1,6 @@ +var structjwt__config__t = +[ + [ "alg", "structjwt__config__t.html#aa1f5e081bba22bd5e0e90962b55d3305", null ], + [ "ctx", "structjwt__config__t.html#a5b20a944b428896447108820ca861117", null ], + [ "jw_key", "structjwt__config__t.html#abc613eedbd07027e1aed03baaf7b7a93", null ] +]; \ No newline at end of file diff --git a/HEAD/structjwt__config__t__coll__graph.map b/HEAD/structjwt__config__t__coll__graph.map new file mode 100644 index 00000000..2cc2558f --- /dev/null +++ b/HEAD/structjwt__config__t__coll__graph.map @@ -0,0 +1,5 @@ + + + + + diff --git a/HEAD/structjwt__config__t__coll__graph.md5 b/HEAD/structjwt__config__t__coll__graph.md5 new file mode 100644 index 00000000..20116d99 --- /dev/null +++ b/HEAD/structjwt__config__t__coll__graph.md5 @@ -0,0 +1 @@ +03513a3a703f982cd53ad53865fef269 \ No newline at end of file diff --git a/HEAD/structjwt__config__t__coll__graph.svg b/HEAD/structjwt__config__t__coll__graph.svg new file mode 100644 index 00000000..e2289e4b --- /dev/null +++ b/HEAD/structjwt__config__t__coll__graph.svg @@ -0,0 +1,66 @@ + + + + + + + + + + + + +jwt_config_t + + +Node1 + + +jwt_config_t + + + + + +Node2 + + +jwk_item_t + + + + + +Node2->Node1 + + + + + + jw_key + + + + + + + + diff --git a/HEAD/structjwt__config__t__coll__graph_org.svg b/HEAD/structjwt__config__t__coll__graph_org.svg new file mode 100644 index 00000000..ce19c6f2 --- /dev/null +++ b/HEAD/structjwt__config__t__coll__graph_org.svg @@ -0,0 +1,40 @@ + + + + + + +jwt_config_t + + +Node1 + + +jwt_config_t + + + + + +Node2 + + +jwk_item_t + + + + + +Node2->Node1 + + + + + + jw_key + + + diff --git a/HEAD/svg.min.js b/HEAD/svg.min.js new file mode 100644 index 00000000..916732fe --- /dev/null +++ b/HEAD/svg.min.js @@ -0,0 +1,46 @@ +/*! +* @svgdotjs/svg.js - A lightweight library for manipulating and animating SVG. +* @version 3.1.2 +* https://svgjs.dev/ +* +* @copyright Wout Fierens +* @license MIT +* +* BUILT: Wed Jan 26 2022 23:19:07 GMT+0100 (Mitteleuropäische Normalzeit) +*/var SVG=function(){"use strict";const methods$1={};const names=[];function registerMethods(name,m){if(Array.isArray(name)){for(const _name of name){registerMethods(_name,m)}return}if(typeof name==="object"){for(const _name in name){registerMethods(_name,name[_name])}return}addMethodNames(Object.getOwnPropertyNames(m));methods$1[name]=Object.assign(methods$1[name]||{},m)}function getMethodsFor(name){return methods$1[name]||{}}function getMethodNames(){return[...new Set(names)]}function addMethodNames(_names){names.push(..._names)}function map(array,block){let i;const il=array.length;const result=[];for(i=0;i=0;i--){assignNewId(node.children[i])}if(node.id){node.id=eid(node.nodeName);return node}return node}function extend(modules,methods){let key,i;modules=Array.isArray(modules)?modules:[modules];for(i=modules.length-1;i>=0;i--){for(key in methods){modules[i].prototype[key]=methods[key]}}}function wrapWithAttrCheck(fn){return function(...args){const o=args[args.length-1];if(o&&o.constructor===Object&&!(o instanceof Array)){return fn.apply(this,args.slice(0,-1)).attr(o)}else{return fn.apply(this,args)}}}function siblings(){return this.parent().children()}function position(){return this.parent().index(this)}function next(){return this.siblings()[this.position()+1]}function prev(){return this.siblings()[this.position()-1]}function forward(){const i=this.position();const p=this.parent();p.add(this.remove(),i+1);return this}function backward(){const i=this.position();const p=this.parent();p.add(this.remove(),i?i-1:0);return this}function front(){const p=this.parent();p.add(this.remove());return this}function back(){const p=this.parent();p.add(this.remove(),0);return this}function before(element){element=makeInstance(element);element.remove();const i=this.position();this.parent().add(element,i);return this}function after(element){element=makeInstance(element);element.remove();const i=this.position();this.parent().add(element,i+1);return this}function insertBefore(element){element=makeInstance(element);element.before(this);return this}function insertAfter(element){element=makeInstance(element);element.after(this);return this}registerMethods("Dom",{siblings:siblings,position:position,next:next,prev:prev,forward:forward,backward:backward,front:front,back:back,before:before,after:after,insertBefore:insertBefore,insertAfter:insertAfter});const numberAndUnit=/^([+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?)([a-z%]*)$/i;const hex=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i;const rgb=/rgb\((\d+),(\d+),(\d+)\)/;const reference=/(#[a-z_][a-z0-9\-_]*)/i;const transforms=/\)\s*,?\s*/;const whitespace=/\s/g;const isHex=/^#[a-f0-9]{3}$|^#[a-f0-9]{6}$/i;const isRgb=/^rgb\(/;const isBlank=/^(\s+)?$/;const isNumber=/^[+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i;const isImage=/\.(jpg|jpeg|png|gif|svg)(\?[^=]+.*)?/i;const delimiter=/[\s,]+/;const isPathLetter=/[MLHVCSQTAZ]/i;var regex={__proto__:null,numberAndUnit:numberAndUnit,hex:hex,rgb:rgb,reference:reference,transforms:transforms,whitespace:whitespace,isHex:isHex,isRgb:isRgb,isBlank:isBlank,isNumber:isNumber,isImage:isImage,delimiter:delimiter,isPathLetter:isPathLetter};function classes(){const attr=this.attr("class");return attr==null?[]:attr.trim().split(delimiter)}function hasClass(name){return this.classes().indexOf(name)!==-1}function addClass(name){if(!this.hasClass(name)){const array=this.classes();array.push(name);this.attr("class",array.join(" "))}return this}function removeClass(name){if(this.hasClass(name)){this.attr("class",this.classes().filter(function(c){return c!==name}).join(" "))}return this}function toggleClass(name){return this.hasClass(name)?this.removeClass(name):this.addClass(name)}registerMethods("Dom",{classes:classes,hasClass:hasClass,addClass:addClass,removeClass:removeClass,toggleClass:toggleClass});function css(style,val){const ret={};if(arguments.length===0){this.node.style.cssText.split(/\s*;\s*/).filter(function(el){return!!el.length}).forEach(function(el){const t=el.split(/\s*:\s*/);ret[t[0]]=t[1]});return ret}if(arguments.length<2){if(Array.isArray(style)){for(const name of style){const cased=camelCase(name);ret[name]=this.node.style[cased]}return ret}if(typeof style==="string"){return this.node.style[camelCase(style)]}if(typeof style==="object"){for(const name in style){this.node.style[camelCase(name)]=style[name]==null||isBlank.test(style[name])?"":style[name]}}}if(arguments.length===2){this.node.style[camelCase(style)]=val==null||isBlank.test(val)?"":val}return this}function show(){return this.css("display","")}function hide(){return this.css("display","none")}function visible(){return this.css("display")!=="none"}registerMethods("Dom",{css:css,show:show,hide:hide,visible:visible});function data(a,v,r){if(a==null){return this.data(map(filter(this.node.attributes,el=>el.nodeName.indexOf("data-")===0),el=>el.nodeName.slice(5)))}else if(a instanceof Array){const data={};for(const key of a){data[key]=this.data(key)}return data}else if(typeof a==="object"){for(v in a){this.data(v,a[v])}}else if(arguments.length<2){try{return JSON.parse(this.attr("data-"+a))}catch(e){return this.attr("data-"+a)}}else{this.attr("data-"+a,v===null?null:r===true||typeof v==="string"||typeof v==="number"?v:JSON.stringify(v))}return this}registerMethods("Dom",{data:data});function remember(k,v){if(typeof arguments[0]==="object"){for(const key in k){this.remember(key,k[key])}}else if(arguments.length===1){return this.memory()[k]}else{this.memory()[k]=v}return this}function forget(){if(arguments.length===0){this._memory={}}else{for(let i=arguments.length-1;i>=0;i--){delete this.memory()[arguments[i]]}}return this}function memory(){return this._memory=this._memory||{}}registerMethods("Dom",{remember:remember,forget:forget,memory:memory});function sixDigitHex(hex){return hex.length===4?["#",hex.substring(1,2),hex.substring(1,2),hex.substring(2,3),hex.substring(2,3),hex.substring(3,4),hex.substring(3,4)].join(""):hex}function componentHex(component){const integer=Math.round(component);const bounded=Math.max(0,Math.min(255,integer));const hex=bounded.toString(16);return hex.length===1?"0"+hex:hex}function is(object,space){for(let i=space.length;i--;){if(object[space[i]]==null){return false}}return true}function getParameters(a,b){const params=is(a,"rgb")?{_a:a.r,_b:a.g,_c:a.b,_d:0,space:"rgb"}:is(a,"xyz")?{_a:a.x,_b:a.y,_c:a.z,_d:0,space:"xyz"}:is(a,"hsl")?{_a:a.h,_b:a.s,_c:a.l,_d:0,space:"hsl"}:is(a,"lab")?{_a:a.l,_b:a.a,_c:a.b,_d:0,space:"lab"}:is(a,"lch")?{_a:a.l,_b:a.c,_c:a.h,_d:0,space:"lch"}:is(a,"cmyk")?{_a:a.c,_b:a.m,_c:a.y,_d:a.k,space:"cmyk"}:{_a:0,_b:0,_c:0,space:"rgb"};params.space=b||params.space;return params}function cieSpace(space){if(space==="lab"||space==="xyz"||space==="lch"){return true}else{return false}}function hueToRgb(p,q,t){if(t<0)t+=1;if(t>1)t-=1;if(t<1/6)return p+(q-p)*6*t;if(t<1/2)return q;if(t<2/3)return p+(q-p)*(2/3-t)*6;return p}class Color{constructor(...inputs){this.init(...inputs)}static isColor(color){return color&&(color instanceof Color||this.isRgb(color)||this.test(color))}static isRgb(color){return color&&typeof color.r==="number"&&typeof color.g==="number"&&typeof color.b==="number"}static random(mode="vibrant",t,u){const{random,round,sin,PI:pi}=Math;if(mode==="vibrant"){const l=(81-57)*random()+57;const c=(83-45)*random()+45;const h=360*random();const color=new Color(l,c,h,"lch");return color}else if(mode==="sine"){t=t==null?random():t;const r=round(80*sin(2*pi*t/.5+.01)+150);const g=round(50*sin(2*pi*t/.5+4.6)+200);const b=round(100*sin(2*pi*t/.5+2.3)+150);const color=new Color(r,g,b);return color}else if(mode==="pastel"){const l=(94-86)*random()+86;const c=(26-9)*random()+9;const h=360*random();const color=new Color(l,c,h,"lch");return color}else if(mode==="dark"){const l=10+10*random();const c=(125-75)*random()+86;const h=360*random();const color=new Color(l,c,h,"lch");return color}else if(mode==="rgb"){const r=255*random();const g=255*random();const b=255*random();const color=new Color(r,g,b);return color}else if(mode==="lab"){const l=100*random();const a=256*random()-128;const b=256*random()-128;const color=new Color(l,a,b,"lab");return color}else if(mode==="grey"){const grey=255*random();const color=new Color(grey,grey,grey);return color}else{throw new Error("Unsupported random color mode")}}static test(color){return typeof color==="string"&&(isHex.test(color)||isRgb.test(color))}cmyk(){const{_a,_b,_c}=this.rgb();const[r,g,b]=[_a,_b,_c].map(v=>v/255);const k=Math.min(1-r,1-g,1-b);if(k===1){return new Color(0,0,0,1,"cmyk")}const c=(1-r-k)/(1-k);const m=(1-g-k)/(1-k);const y=(1-b-k)/(1-k);const color=new Color(c,m,y,k,"cmyk");return color}hsl(){const{_a,_b,_c}=this.rgb();const[r,g,b]=[_a,_b,_c].map(v=>v/255);const max=Math.max(r,g,b);const min=Math.min(r,g,b);const l=(max+min)/2;const isGrey=max===min;const delta=max-min;const s=isGrey?0:l>.5?delta/(2-max-min):delta/(max+min);const h=isGrey?0:max===r?((g-b)/delta+(gparseInt(v));Object.assign(this,{_a:_a,_b:_b,_c:_c,_d:0,space:"rgb"})}else if(isHex.test(a)){const hexParse=v=>parseInt(v,16);const[,_a,_b,_c]=hex.exec(sixDigitHex(a)).map(hexParse);Object.assign(this,{_a:_a,_b:_b,_c:_c,_d:0,space:"rgb"})}else throw Error("Unsupported string format, can't construct Color")}const{_a,_b,_c,_d}=this;const components=this.space==="rgb"?{r:_a,g:_b,b:_c}:this.space==="xyz"?{x:_a,y:_b,z:_c}:this.space==="hsl"?{h:_a,s:_b,l:_c}:this.space==="lab"?{l:_a,a:_b,b:_c}:this.space==="lch"?{l:_a,c:_b,h:_c}:this.space==="cmyk"?{c:_a,m:_b,y:_c,k:_d}:{};Object.assign(this,components)}lab(){const{x,y,z}=this.xyz();const l=116*y-16;const a=500*(x-y);const b=200*(y-z);const color=new Color(l,a,b,"lab");return color}lch(){const{l,a,b}=this.lab();const c=Math.sqrt(a**2+b**2);let h=180*Math.atan2(b,a)/Math.PI;if(h<0){h*=-1;h=360-h}const color=new Color(l,c,h,"lch");return color}rgb(){if(this.space==="rgb"){return this}else if(cieSpace(this.space)){let{x,y,z}=this;if(this.space==="lab"||this.space==="lch"){let{l,a,b}=this;if(this.space==="lch"){const{c,h}=this;const dToR=Math.PI/180;a=c*Math.cos(dToR*h);b=c*Math.sin(dToR*h)}const yL=(l+16)/116;const xL=a/500+yL;const zL=yL-b/200;const ct=16/116;const mx=.008856;const nm=7.787;x=.95047*(xL**3>mx?xL**3:(xL-ct)/nm);y=1*(yL**3>mx?yL**3:(yL-ct)/nm);z=1.08883*(zL**3>mx?zL**3:(zL-ct)/nm)}const rU=x*3.2406+y*-1.5372+z*-.4986;const gU=x*-.9689+y*1.8758+z*.0415;const bU=x*.0557+y*-.204+z*1.057;const pow=Math.pow;const bd=.0031308;const r=rU>bd?1.055*pow(rU,1/2.4)-.055:12.92*rU;const g=gU>bd?1.055*pow(gU,1/2.4)-.055:12.92*gU;const b=bU>bd?1.055*pow(bU,1/2.4)-.055:12.92*bU;const color=new Color(255*r,255*g,255*b);return color}else if(this.space==="hsl"){let{h,s,l}=this;h/=360;s/=100;l/=100;if(s===0){l*=255;const color=new Color(l,l,l);return color}const q=l<.5?l*(1+s):l+s-l*s;const p=2*l-q;const r=255*hueToRgb(p,q,h+1/3);const g=255*hueToRgb(p,q,h);const b=255*hueToRgb(p,q,h-1/3);const color=new Color(r,g,b);return color}else if(this.space==="cmyk"){const{c,m,y,k}=this;const r=255*(1-Math.min(1,c*(1-k)+k));const g=255*(1-Math.min(1,m*(1-k)+k));const b=255*(1-Math.min(1,y*(1-k)+k));const color=new Color(r,g,b);return color}else{return this}}toArray(){const{_a,_b,_c,_d,space}=this;return[_a,_b,_c,_d,space]}toHex(){const[r,g,b]=this._clamped().map(componentHex);return`#${r}${g}${b}`}toRgb(){const[rV,gV,bV]=this._clamped();const string=`rgb(${rV},${gV},${bV})`;return string}toString(){return this.toHex()}xyz(){const{_a:r255,_b:g255,_c:b255}=this.rgb();const[r,g,b]=[r255,g255,b255].map(v=>v/255);const rL=r>.04045?Math.pow((r+.055)/1.055,2.4):r/12.92;const gL=g>.04045?Math.pow((g+.055)/1.055,2.4):g/12.92;const bL=b>.04045?Math.pow((b+.055)/1.055,2.4):b/12.92;const xU=(rL*.4124+gL*.3576+bL*.1805)/.95047;const yU=(rL*.2126+gL*.7152+bL*.0722)/1;const zU=(rL*.0193+gL*.1192+bL*.9505)/1.08883;const x=xU>.008856?Math.pow(xU,1/3):7.787*xU+16/116;const y=yU>.008856?Math.pow(yU,1/3):7.787*yU+16/116;const z=zU>.008856?Math.pow(zU,1/3):7.787*zU+16/116;const color=new Color(x,y,z,"xyz");return color}_clamped(){const{_a,_b,_c}=this.rgb();const{max,min,round}=Math;const format=v=>max(0,min(round(v),255));return[_a,_b,_c].map(format)}}class Point{constructor(...args){this.init(...args)}clone(){return new Point(this)}init(x,y){const base={x:0,y:0};const source=Array.isArray(x)?{x:x[0],y:x[1]}:typeof x==="object"?{x:x.x,y:x.y}:{x:x,y:y};this.x=source.x==null?base.x:source.x;this.y=source.y==null?base.y:source.y;return this}toArray(){return[this.x,this.y]}transform(m){return this.clone().transformO(m)}transformO(m){if(!Matrix.isMatrixLike(m)){m=new Matrix(m)}const{x,y}=this;this.x=m.a*x+m.c*y+m.e;this.y=m.b*x+m.d*y+m.f;return this}}function point(x,y){return new Point(x,y).transform(this.screenCTM().inverse())}function closeEnough(a,b,threshold){return Math.abs(b-a)<(threshold||1e-6)}class Matrix{constructor(...args){this.init(...args)}static formatTransforms(o){const flipBoth=o.flip==="both"||o.flip===true;const flipX=o.flip&&(flipBoth||o.flip==="x")?-1:1;const flipY=o.flip&&(flipBoth||o.flip==="y")?-1:1;const skewX=o.skew&&o.skew.length?o.skew[0]:isFinite(o.skew)?o.skew:isFinite(o.skewX)?o.skewX:0;const skewY=o.skew&&o.skew.length?o.skew[1]:isFinite(o.skew)?o.skew:isFinite(o.skewY)?o.skewY:0;const scaleX=o.scale&&o.scale.length?o.scale[0]*flipX:isFinite(o.scale)?o.scale*flipX:isFinite(o.scaleX)?o.scaleX*flipX:flipX;const scaleY=o.scale&&o.scale.length?o.scale[1]*flipY:isFinite(o.scale)?o.scale*flipY:isFinite(o.scaleY)?o.scaleY*flipY:flipY;const shear=o.shear||0;const theta=o.rotate||o.theta||0;const origin=new Point(o.origin||o.around||o.ox||o.originX,o.oy||o.originY);const ox=origin.x;const oy=origin.y;const position=new Point(o.position||o.px||o.positionX||NaN,o.py||o.positionY||NaN);const px=position.x;const py=position.y;const translate=new Point(o.translate||o.tx||o.translateX,o.ty||o.translateY);const tx=translate.x;const ty=translate.y;const relative=new Point(o.relative||o.rx||o.relativeX,o.ry||o.relativeY);const rx=relative.x;const ry=relative.y;return{scaleX:scaleX,scaleY:scaleY,skewX:skewX,skewY:skewY,shear:shear,theta:theta,rx:rx,ry:ry,tx:tx,ty:ty,ox:ox,oy:oy,px:px,py:py}}static fromArray(a){return{a:a[0],b:a[1],c:a[2],d:a[3],e:a[4],f:a[5]}}static isMatrixLike(o){return o.a!=null||o.b!=null||o.c!=null||o.d!=null||o.e!=null||o.f!=null}static matrixMultiply(l,r,o){const a=l.a*r.a+l.c*r.b;const b=l.b*r.a+l.d*r.b;const c=l.a*r.c+l.c*r.d;const d=l.b*r.c+l.d*r.d;const e=l.e+l.a*r.e+l.c*r.f;const f=l.f+l.b*r.e+l.d*r.f;o.a=a;o.b=b;o.c=c;o.d=d;o.e=e;o.f=f;return o}around(cx,cy,matrix){return this.clone().aroundO(cx,cy,matrix)}aroundO(cx,cy,matrix){const dx=cx||0;const dy=cy||0;return this.translateO(-dx,-dy).lmultiplyO(matrix).translateO(dx,dy)}clone(){return new Matrix(this)}decompose(cx=0,cy=0){const a=this.a;const b=this.b;const c=this.c;const d=this.d;const e=this.e;const f=this.f;const determinant=a*d-b*c;const ccw=determinant>0?1:-1;const sx=ccw*Math.sqrt(a*a+b*b);const thetaRad=Math.atan2(ccw*b,ccw*a);const theta=180/Math.PI*thetaRad;const ct=Math.cos(thetaRad);const st=Math.sin(thetaRad);const lam=(a*c+b*d)/determinant;const sy=c*sx/(lam*a-b)||d*sx/(lam*b+a);const tx=e-cx+cx*ct*sx+cy*(lam*ct*sx-st*sy);const ty=f-cy+cx*st*sx+cy*(lam*st*sx+ct*sy);return{scaleX:sx,scaleY:sy,shear:lam,rotate:theta,translateX:tx,translateY:ty,originX:cx,originY:cy,a:this.a,b:this.b,c:this.c,d:this.d,e:this.e,f:this.f}}equals(other){if(other===this)return true;const comp=new Matrix(other);return closeEnough(this.a,comp.a)&&closeEnough(this.b,comp.b)&&closeEnough(this.c,comp.c)&&closeEnough(this.d,comp.d)&&closeEnough(this.e,comp.e)&&closeEnough(this.f,comp.f)}flip(axis,around){return this.clone().flipO(axis,around)}flipO(axis,around){return axis==="x"?this.scaleO(-1,1,around,0):axis==="y"?this.scaleO(1,-1,0,around):this.scaleO(-1,-1,axis,around||axis)}init(source){const base=Matrix.fromArray([1,0,0,1,0,0]);source=source instanceof Element?source.matrixify():typeof source==="string"?Matrix.fromArray(source.split(delimiter).map(parseFloat)):Array.isArray(source)?Matrix.fromArray(source):typeof source==="object"&&Matrix.isMatrixLike(source)?source:typeof source==="object"?(new Matrix).transform(source):arguments.length===6?Matrix.fromArray([].slice.call(arguments)):base;this.a=source.a!=null?source.a:base.a;this.b=source.b!=null?source.b:base.b;this.c=source.c!=null?source.c:base.c;this.d=source.d!=null?source.d:base.d;this.e=source.e!=null?source.e:base.e;this.f=source.f!=null?source.f:base.f;return this}inverse(){return this.clone().inverseO()}inverseO(){const a=this.a;const b=this.b;const c=this.c;const d=this.d;const e=this.e;const f=this.f;const det=a*d-b*c;if(!det)throw new Error("Cannot invert "+this);const na=d/det;const nb=-b/det;const nc=-c/det;const nd=a/det;const ne=-(na*e+nc*f);const nf=-(nb*e+nd*f);this.a=na;this.b=nb;this.c=nc;this.d=nd;this.e=ne;this.f=nf;return this}lmultiply(matrix){return this.clone().lmultiplyO(matrix)}lmultiplyO(matrix){const r=this;const l=matrix instanceof Matrix?matrix:new Matrix(matrix);return Matrix.matrixMultiply(l,r,this)}multiply(matrix){return this.clone().multiplyO(matrix)}multiplyO(matrix){const l=this;const r=matrix instanceof Matrix?matrix:new Matrix(matrix);return Matrix.matrixMultiply(l,r,this)}rotate(r,cx,cy){return this.clone().rotateO(r,cx,cy)}rotateO(r,cx=0,cy=0){r=radians(r);const cos=Math.cos(r);const sin=Math.sin(r);const{a,b,c,d,e,f}=this;this.a=a*cos-b*sin;this.b=b*cos+a*sin;this.c=c*cos-d*sin;this.d=d*cos+c*sin;this.e=e*cos-f*sin+cy*sin-cx*cos+cx;this.f=f*cos+e*sin-cx*sin-cy*cos+cy;return this}scale(x,y,cx,cy){return this.clone().scaleO(...arguments)}scaleO(x,y=x,cx=0,cy=0){if(arguments.length===3){cy=cx;cx=y;y=x}const{a,b,c,d,e,f}=this;this.a=a*x;this.b=b*y;this.c=c*x;this.d=d*y;this.e=e*x-cx*x+cx;this.f=f*y-cy*y+cy;return this}shear(a,cx,cy){return this.clone().shearO(a,cx,cy)}shearO(lx,cx=0,cy=0){const{a,b,c,d,e,f}=this;this.a=a+b*lx;this.c=c+d*lx;this.e=e+f*lx-cy*lx;return this}skew(x,y,cx,cy){return this.clone().skewO(...arguments)}skewO(x,y=x,cx=0,cy=0){if(arguments.length===3){cy=cx;cx=y;y=x}x=radians(x);y=radians(y);const lx=Math.tan(x);const ly=Math.tan(y);const{a,b,c,d,e,f}=this;this.a=a+b*lx;this.b=b+a*ly;this.c=c+d*lx;this.d=d+c*ly;this.e=e+f*lx-cy*lx;this.f=f+e*ly-cx*ly;return this}skewX(x,cx,cy){return this.skew(x,0,cx,cy)}skewY(y,cx,cy){return this.skew(0,y,cx,cy)}toArray(){return[this.a,this.b,this.c,this.d,this.e,this.f]}toString(){return"matrix("+this.a+","+this.b+","+this.c+","+this.d+","+this.e+","+this.f+")"}transform(o){if(Matrix.isMatrixLike(o)){const matrix=new Matrix(o);return matrix.multiplyO(this)}const t=Matrix.formatTransforms(o);const current=this;const{x:ox,y:oy}=new Point(t.ox,t.oy).transform(current);const transformer=(new Matrix).translateO(t.rx,t.ry).lmultiplyO(current).translateO(-ox,-oy).scaleO(t.scaleX,t.scaleY).skewO(t.skewX,t.skewY).shearO(t.shear).rotateO(t.theta).translateO(ox,oy);if(isFinite(t.px)||isFinite(t.py)){const origin=new Point(ox,oy).transform(transformer);const dx=isFinite(t.px)?t.px-origin.x:0;const dy=isFinite(t.py)?t.py-origin.y:0;transformer.translateO(dx,dy)}transformer.translateO(t.tx,t.ty);return transformer}translate(x,y){return this.clone().translateO(x,y)}translateO(x,y){this.e+=x||0;this.f+=y||0;return this}valueOf(){return{a:this.a,b:this.b,c:this.c,d:this.d,e:this.e,f:this.f}}}function ctm(){return new Matrix(this.node.getCTM())}function screenCTM(){if(typeof this.isRoot==="function"&&!this.isRoot()){const rect=this.rect(1,1);const m=rect.node.getScreenCTM();rect.remove();return new Matrix(m)}return new Matrix(this.node.getScreenCTM())}register(Matrix,"Matrix");function parser(){if(!parser.nodes){const svg=makeInstance().size(2,0);svg.node.style.cssText=["opacity: 0","position: absolute","left: -100%","top: -100%","overflow: hidden"].join(";");svg.attr("focusable","false");svg.attr("aria-hidden","true");const path=svg.path().node;parser.nodes={svg:svg,path:path}}if(!parser.nodes.svg.node.parentNode){const b=globals.document.body||globals.document.documentElement;parser.nodes.svg.addTo(b)}return parser.nodes}function isNulledBox(box){return!box.width&&!box.height&&!box.x&&!box.y}function domContains(node){return node===globals.document||(globals.document.documentElement.contains||function(node){while(node.parentNode){node=node.parentNode}return node===globals.document}).call(globals.document.documentElement,node)}class Box{constructor(...args){this.init(...args)}addOffset(){this.x+=globals.window.pageXOffset;this.y+=globals.window.pageYOffset;return new Box(this)}init(source){const base=[0,0,0,0];source=typeof source==="string"?source.split(delimiter).map(parseFloat):Array.isArray(source)?source:typeof source==="object"?[source.left!=null?source.left:source.x,source.top!=null?source.top:source.y,source.width,source.height]:arguments.length===4?[].slice.call(arguments):base;this.x=source[0]||0;this.y=source[1]||0;this.width=this.w=source[2]||0;this.height=this.h=source[3]||0;this.x2=this.x+this.w;this.y2=this.y+this.h;this.cx=this.x+this.w/2;this.cy=this.y+this.h/2;return this}isNulled(){return isNulledBox(this)}merge(box){const x=Math.min(this.x,box.x);const y=Math.min(this.y,box.y);const width=Math.max(this.x+this.width,box.x+box.width)-x;const height=Math.max(this.y+this.height,box.y+box.height)-y;return new Box(x,y,width,height)}toArray(){return[this.x,this.y,this.width,this.height]}toString(){return this.x+" "+this.y+" "+this.width+" "+this.height}transform(m){if(!(m instanceof Matrix)){m=new Matrix(m)}let xMin=Infinity;let xMax=-Infinity;let yMin=Infinity;let yMax=-Infinity;const pts=[new Point(this.x,this.y),new Point(this.x2,this.y),new Point(this.x,this.y2),new Point(this.x2,this.y2)];pts.forEach(function(p){p=p.transform(m);xMin=Math.min(xMin,p.x);xMax=Math.max(xMax,p.x);yMin=Math.min(yMin,p.y);yMax=Math.max(yMax,p.y)});return new Box(xMin,yMin,xMax-xMin,yMax-yMin)}}function getBox(el,getBBoxFn,retry){let box;try{box=getBBoxFn(el.node);if(isNulledBox(box)&&!domContains(el.node)){throw new Error("Element not in the dom")}}catch(e){box=retry(el)}return box}function bbox(){const getBBox=node=>node.getBBox();const retry=el=>{try{const clone=el.clone().addTo(parser().svg).show();const box=clone.node.getBBox();clone.remove();return box}catch(e){throw new Error(`Getting bbox of element "${el.node.nodeName}" is not possible: ${e.toString()}`)}};const box=getBox(this,getBBox,retry);const bbox=new Box(box);return bbox}function rbox(el){const getRBox=node=>node.getBoundingClientRect();const retry=el=>{throw new Error(`Getting rbox of element "${el.node.nodeName}" is not possible`)};const box=getBox(this,getRBox,retry);const rbox=new Box(box);if(el){return rbox.transform(el.screenCTM().inverseO())}return rbox.addOffset()}function inside(x,y){const box=this.bbox();return x>box.x&&y>box.y&&x{return fnOrMethodName.call(el,el,i,arr)})}else{return this.map(el=>{return el[fnOrMethodName](...args)})}},toArray(){return Array.prototype.concat.apply([],this)}});const reserved=["toArray","constructor","each"];List.extend=function(methods){methods=methods.reduce((obj,name)=>{if(reserved.includes(name))return obj;if(name[0]==="_")return obj;obj[name]=function(...attrs){return this.each(name,...attrs)};return obj},{});extend([List],methods)};function baseFind(query,parent){return new List(map((parent||globals.document).querySelectorAll(query),function(node){return adopt(node)}))}function find(query){return baseFind(query,this.node)}function findOne(query){return adopt(this.node.querySelector(query))}let listenerId=0;const windowEvents={};function getEvents(instance){let n=instance.getEventHolder();if(n===globals.window)n=windowEvents;if(!n.events)n.events={};return n.events}function getEventTarget(instance){return instance.getEventTarget()}function clearEvents(instance){let n=instance.getEventHolder();if(n===globals.window)n=windowEvents;if(n.events)n.events={}}function on(node,events,listener,binding,options){const l=listener.bind(binding||node);const instance=makeInstance(node);const bag=getEvents(instance);const n=getEventTarget(instance);events=Array.isArray(events)?events:events.split(delimiter);if(!listener._svgjsListenerId){listener._svgjsListenerId=++listenerId}events.forEach(function(event){const ev=event.split(".")[0];const ns=event.split(".")[1]||"*";bag[ev]=bag[ev]||{};bag[ev][ns]=bag[ev][ns]||{};bag[ev][ns][listener._svgjsListenerId]=l;n.addEventListener(ev,l,options||false)})}function off(node,events,listener,options){const instance=makeInstance(node);const bag=getEvents(instance);const n=getEventTarget(instance);if(typeof listener==="function"){listener=listener._svgjsListenerId;if(!listener)return}events=Array.isArray(events)?events:(events||"").split(delimiter);events.forEach(function(event){const ev=event&&event.split(".")[0];const ns=event&&event.split(".")[1];let namespace,l;if(listener){if(bag[ev]&&bag[ev][ns||"*"]){n.removeEventListener(ev,bag[ev][ns||"*"][listener],options||false);delete bag[ev][ns||"*"][listener]}}else if(ev&&ns){if(bag[ev]&&bag[ev][ns]){for(l in bag[ev][ns]){off(n,[ev,ns].join("."),l)}delete bag[ev][ns]}}else if(ns){for(event in bag){for(namespace in bag[event]){if(ns===namespace){off(n,[event,ns].join("."))}}}}else if(ev){if(bag[ev]){for(namespace in bag[ev]){off(n,[ev,namespace].join("."))}delete bag[ev]}}else{for(event in bag){off(n,event)}clearEvents(instance)}})}function dispatch(node,event,data,options){const n=getEventTarget(node);if(event instanceof globals.window.Event){n.dispatchEvent(event)}else{event=new globals.window.CustomEvent(event,{detail:data,cancelable:true,...options});n.dispatchEvent(event)}return event}class EventTarget extends Base{addEventListener(){}dispatch(event,data,options){return dispatch(this,event,data,options)}dispatchEvent(event){const bag=this.getEventHolder().events;if(!bag)return true;const events=bag[event.type];for(const i in events){for(const j in events[i]){events[i][j](event)}}return!event.defaultPrevented}fire(event,data,options){this.dispatch(event,data,options);return this}getEventHolder(){return this}getEventTarget(){return this}off(event,listener,options){off(this,event,listener,options);return this}on(event,listener,binding,options){on(this,event,listener,binding,options);return this}removeEventListener(){}}register(EventTarget,"EventTarget");function noop(){}const timeline={duration:400,ease:">",delay:0};const attrs={"fill-opacity":1,"stroke-opacity":1,"stroke-width":0,"stroke-linejoin":"miter","stroke-linecap":"butt",fill:"#000000",stroke:"#000000",opacity:1,x:0,y:0,cx:0,cy:0,width:0,height:0,r:0,rx:0,ry:0,offset:0,"stop-opacity":1,"stop-color":"#000000","text-anchor":"start"};var defaults={__proto__:null,noop:noop,timeline:timeline,attrs:attrs};class SVGArray extends Array{constructor(...args){super(...args);this.init(...args)}clone(){return new this.constructor(this)}init(arr){if(typeof arr==="number")return this;this.length=0;this.push(...this.parse(arr));return this}parse(array=[]){if(array instanceof Array)return array;return array.trim().split(delimiter).map(parseFloat)}toArray(){return Array.prototype.concat.apply([],this)}toSet(){return new Set(this)}toString(){return this.join(" ")}valueOf(){const ret=[];ret.push(...this);return ret}}class SVGNumber{constructor(...args){this.init(...args)}convert(unit){return new SVGNumber(this.value,unit)}divide(number){number=new SVGNumber(number);return new SVGNumber(this/number,this.unit||number.unit)}init(value,unit){unit=Array.isArray(value)?value[1]:unit;value=Array.isArray(value)?value[0]:value;this.value=0;this.unit=unit||"";if(typeof value==="number"){this.value=isNaN(value)?0:!isFinite(value)?value<0?-34e37:+34e37:value}else if(typeof value==="string"){unit=value.match(numberAndUnit);if(unit){this.value=parseFloat(unit[1]);if(unit[5]==="%"){this.value/=100}else if(unit[5]==="s"){this.value*=1e3}this.unit=unit[5]}}else{if(value instanceof SVGNumber){this.value=value.valueOf();this.unit=value.unit}}return this}minus(number){number=new SVGNumber(number);return new SVGNumber(this-number,this.unit||number.unit)}plus(number){number=new SVGNumber(number);return new SVGNumber(this+number,this.unit||number.unit)}times(number){number=new SVGNumber(number);return new SVGNumber(this*number,this.unit||number.unit)}toArray(){return[this.value,this.unit]}toJSON(){return this.toString()}toString(){return(this.unit==="%"?~~(this.value*1e8)/1e6:this.unit==="s"?this.value/1e3:this.value)+this.unit}valueOf(){return this.value}}const hooks=[];function registerAttrHook(fn){hooks.push(fn)}function attr(attr,val,ns){if(attr==null){attr={};val=this.node.attributes;for(const node of val){attr[node.nodeName]=isNumber.test(node.nodeValue)?parseFloat(node.nodeValue):node.nodeValue}return attr}else if(attr instanceof Array){return attr.reduce((last,curr)=>{last[curr]=this.attr(curr);return last},{})}else if(typeof attr==="object"&&attr.constructor===Object){for(val in attr)this.attr(val,attr[val])}else if(val===null){this.node.removeAttribute(attr)}else if(val==null){val=this.node.getAttribute(attr);return val==null?attrs[attr]:isNumber.test(val)?parseFloat(val):val}else{val=hooks.reduce((_val,hook)=>{return hook(attr,_val,this)},val);if(typeof val==="number"){val=new SVGNumber(val)}else if(Color.isColor(val)){val=new Color(val)}else if(val.constructor===Array){val=new SVGArray(val)}if(attr==="leading"){if(this.leading){this.leading(val)}}else{typeof ns==="string"?this.node.setAttributeNS(ns,attr,val.toString()):this.node.setAttribute(attr,val.toString())}if(this.rebuild&&(attr==="font-size"||attr==="x")){this.rebuild()}}return this}class Dom extends EventTarget{constructor(node,attrs){super();this.node=node;this.type=node.nodeName;if(attrs&&node!==attrs){this.attr(attrs)}}add(element,i){element=makeInstance(element);if(element.removeNamespace&&this.node instanceof globals.window.SVGElement){element.removeNamespace()}if(i==null){this.node.appendChild(element.node)}else if(element.node!==this.node.childNodes[i]){this.node.insertBefore(element.node,this.node.childNodes[i])}return this}addTo(parent,i){return makeInstance(parent).put(this,i)}children(){return new List(map(this.node.children,function(node){return adopt(node)}))}clear(){while(this.node.hasChildNodes()){this.node.removeChild(this.node.lastChild)}return this}clone(deep=true){this.writeDataToDom();return new this.constructor(assignNewId(this.node.cloneNode(deep)))}each(block,deep){const children=this.children();let i,il;for(i=0,il=children.length;i=0}html(htmlOrFn,outerHTML){return this.xml(htmlOrFn,outerHTML,html)}id(id){if(typeof id==="undefined"&&!this.node.id){this.node.id=eid(this.type)}return this.attr("id",id)}index(element){return[].slice.call(this.node.childNodes).indexOf(element.node)}last(){return adopt(this.node.lastChild)}matches(selector){const el=this.node;const matcher=el.matches||el.matchesSelector||el.msMatchesSelector||el.mozMatchesSelector||el.webkitMatchesSelector||el.oMatchesSelector||null;return matcher&&matcher.call(el,selector)}parent(type){let parent=this;if(!parent.node.parentNode)return null;parent=adopt(parent.node.parentNode);if(!type)return parent;do{if(typeof type==="string"?parent.matches(type):parent instanceof type)return parent}while(parent=adopt(parent.node.parentNode));return parent}put(element,i){element=makeInstance(element);this.add(element,i);return element}putIn(parent,i){return makeInstance(parent).add(this,i)}remove(){if(this.parent()){this.parent().removeElement(this)}return this}removeElement(element){this.node.removeChild(element.node);return this}replace(element){element=makeInstance(element);if(this.node.parentNode){this.node.parentNode.replaceChild(element.node,this.node)}return element}round(precision=2,map=null){const factor=10**precision;const attrs=this.attr(map);for(const i in attrs){if(typeof attrs[i]==="number"){attrs[i]=Math.round(attrs[i]*factor)/factor}}this.attr(attrs);return this}svg(svgOrFn,outerSVG){return this.xml(svgOrFn,outerSVG,svg)}toString(){return this.id()}words(text){this.node.textContent=text;return this}wrap(node){const parent=this.parent();if(!parent){return this.addTo(node)}const position=parent.index(this);return parent.put(node,position).put(this)}writeDataToDom(){this.each(function(){this.writeDataToDom()});return this}xml(xmlOrFn,outerXML,ns){if(typeof xmlOrFn==="boolean"){ns=outerXML;outerXML=xmlOrFn;xmlOrFn=null}if(xmlOrFn==null||typeof xmlOrFn==="function"){outerXML=outerXML==null?true:outerXML;this.writeDataToDom();let current=this;if(xmlOrFn!=null){current=adopt(current.node.cloneNode(true));if(outerXML){const result=xmlOrFn(current);current=result||current;if(result===false)return""}current.each(function(){const result=xmlOrFn(this);const _this=result||this;if(result===false){this.remove()}else if(result&&this!==_this){this.replace(_this)}},true)}return outerXML?current.node.outerHTML:current.node.innerHTML}outerXML=outerXML==null?false:outerXML;const well=create("wrapper",ns);const fragment=globals.document.createDocumentFragment();well.innerHTML=xmlOrFn;for(let len=well.children.length;len--;){fragment.appendChild(well.firstElementChild)}const parent=this.parent();return outerXML?this.replace(fragment)&&parent:this.add(fragment)}}extend(Dom,{attr:attr,find:find,findOne:findOne});register(Dom,"Dom");class Element extends Dom{constructor(node,attrs){super(node,attrs);this.dom={};this.node.instance=this;if(node.hasAttribute("svgjs:data")){this.setData(JSON.parse(node.getAttribute("svgjs:data"))||{})}}center(x,y){return this.cx(x).cy(y)}cx(x){return x==null?this.x()+this.width()/2:this.x(x-this.width()/2)}cy(y){return y==null?this.y()+this.height()/2:this.y(y-this.height()/2)}defs(){const root=this.root();return root&&root.defs()}dmove(x,y){return this.dx(x).dy(y)}dx(x=0){return this.x(new SVGNumber(x).plus(this.x()))}dy(y=0){return this.y(new SVGNumber(y).plus(this.y()))}getEventHolder(){return this}height(height){return this.attr("height",height)}move(x,y){return this.x(x).y(y)}parents(until=this.root()){const isSelector=typeof until==="string";if(!isSelector){until=makeInstance(until)}const parents=new List;let parent=this;while((parent=parent.parent())&&parent.node!==globals.document&&parent.nodeName!=="#document-fragment"){parents.push(parent);if(!isSelector&&parent.node===until.node){break}if(isSelector&&parent.matches(until)){break}if(parent.node===this.root().node){return null}}return parents}reference(attr){attr=this.attr(attr);if(!attr)return null;const m=(attr+"").match(reference);return m?makeInstance(m[1]):null}root(){const p=this.parent(getClass(root));return p&&p.root()}setData(o){this.dom=o;return this}size(width,height){const p=proportionalSize(this,width,height);return this.width(new SVGNumber(p.width)).height(new SVGNumber(p.height))}width(width){return this.attr("width",width)}writeDataToDom(){this.node.removeAttribute("svgjs:data");if(Object.keys(this.dom).length){this.node.setAttribute("svgjs:data",JSON.stringify(this.dom))}return super.writeDataToDom()}x(x){return this.attr("x",x)}y(y){return this.attr("y",y)}}extend(Element,{bbox:bbox,rbox:rbox,inside:inside,point:point,ctm:ctm,screenCTM:screenCTM});register(Element,"Element");const sugar={stroke:["color","width","opacity","linecap","linejoin","miterlimit","dasharray","dashoffset"],fill:["color","opacity","rule"],prefix:function(t,a){return a==="color"?t:t+"-"+a}};["fill","stroke"].forEach(function(m){const extension={};let i;extension[m]=function(o){if(typeof o==="undefined"){return this.attr(m)}if(typeof o==="string"||o instanceof Color||Color.isRgb(o)||o instanceof Element){this.attr(m,o)}else{for(i=sugar[m].length-1;i>=0;i--){if(o[sugar[m][i]]!=null){this.attr(sugar.prefix(m,sugar[m][i]),o[sugar[m][i]])}}}return this};registerMethods(["Element","Runner"],extension)});registerMethods(["Element","Runner"],{matrix:function(mat,b,c,d,e,f){if(mat==null){return new Matrix(this)}return this.attr("transform",new Matrix(mat,b,c,d,e,f))},rotate:function(angle,cx,cy){return this.transform({rotate:angle,ox:cx,oy:cy},true)},skew:function(x,y,cx,cy){return arguments.length===1||arguments.length===3?this.transform({skew:x,ox:y,oy:cx},true):this.transform({skew:[x,y],ox:cx,oy:cy},true)},shear:function(lam,cx,cy){return this.transform({shear:lam,ox:cx,oy:cy},true)},scale:function(x,y,cx,cy){return arguments.length===1||arguments.length===3?this.transform({scale:x,ox:y,oy:cx},true):this.transform({scale:[x,y],ox:cx,oy:cy},true)},translate:function(x,y){return this.transform({translate:[x,y]},true)},relative:function(x,y){return this.transform({relative:[x,y]},true)},flip:function(direction="both",origin="center"){if("xybothtrue".indexOf(direction)===-1){origin=direction;direction="both"}return this.transform({flip:direction,origin:origin},true)},opacity:function(value){return this.attr("opacity",value)}});registerMethods("radius",{radius:function(x,y=x){const type=(this._element||this).type;return type==="radialGradient"?this.attr("r",new SVGNumber(x)):this.rx(x).ry(y)}});registerMethods("Path",{length:function(){return this.node.getTotalLength()},pointAt:function(length){return new Point(this.node.getPointAtLength(length))}});registerMethods(["Element","Runner"],{font:function(a,v){if(typeof a==="object"){for(v in a)this.font(v,a[v]);return this}return a==="leading"?this.leading(v):a==="anchor"?this.attr("text-anchor",v):a==="size"||a==="family"||a==="weight"||a==="stretch"||a==="variant"||a==="style"?this.attr("font-"+a,v):this.attr(a,v)}});const methods=["click","dblclick","mousedown","mouseup","mouseover","mouseout","mousemove","mouseenter","mouseleave","touchstart","touchmove","touchleave","touchend","touchcancel"].reduce(function(last,event){const fn=function(f){if(f===null){this.off(event)}else{this.on(event,f)}return this};last[event]=fn;return last},{});registerMethods("Element",methods);function untransform(){return this.attr("transform",null)}function matrixify(){const matrix=(this.attr("transform")||"").split(transforms).slice(0,-1).map(function(str){const kv=str.trim().split("(");return[kv[0],kv[1].split(delimiter).map(function(str){return parseFloat(str)})]}).reverse().reduce(function(matrix,transform){if(transform[0]==="matrix"){return matrix.lmultiply(Matrix.fromArray(transform[1]))}return matrix[transform[0]].apply(matrix,transform[1])},new Matrix);return matrix}function toParent(parent,i){if(this===parent)return this;const ctm=this.screenCTM();const pCtm=parent.screenCTM().inverse();this.addTo(parent,i).untransform().transform(pCtm.multiply(ctm));return this}function toRoot(i){return this.toParent(this.root(),i)}function transform(o,relative){if(o==null||typeof o==="string"){const decomposed=new Matrix(this).decompose();return o==null?decomposed:decomposed[o]}if(!Matrix.isMatrixLike(o)){o={...o,origin:getOrigin(o,this)}}const cleanRelative=relative===true?this:relative||false;const result=new Matrix(cleanRelative).transform(o);return this.attr("transform",result)}registerMethods("Element",{untransform:untransform,matrixify:matrixify,toParent:toParent,toRoot:toRoot,transform:transform});class Container extends Element{flatten(parent=this,index){this.each(function(){if(this instanceof Container){return this.flatten().ungroup()}});return this}ungroup(parent=this.parent(),index=parent.index(this)){index=index===-1?parent.children().length:index;this.each(function(i,children){return children[children.length-i-1].toParent(parent,index)});return this.remove()}}register(Container,"Container");class Defs extends Container{constructor(node,attrs=node){super(nodeOrNew("defs",node),attrs)}flatten(){return this}ungroup(){return this}}register(Defs,"Defs");class Shape extends Element{}register(Shape,"Shape");function rx(rx){return this.attr("rx",rx)}function ry(ry){return this.attr("ry",ry)}function x$3(x){return x==null?this.cx()-this.rx():this.cx(x+this.rx())}function y$3(y){return y==null?this.cy()-this.ry():this.cy(y+this.ry())}function cx$1(x){return this.attr("cx",x)}function cy$1(y){return this.attr("cy",y)}function width$2(width){return width==null?this.rx()*2:this.rx(new SVGNumber(width).divide(2))}function height$2(height){return height==null?this.ry()*2:this.ry(new SVGNumber(height).divide(2))}var circled={__proto__:null,rx:rx,ry:ry,x:x$3,y:y$3,cx:cx$1,cy:cy$1,width:width$2,height:height$2};class Ellipse extends Shape{constructor(node,attrs=node){super(nodeOrNew("ellipse",node),attrs)}size(width,height){const p=proportionalSize(this,width,height);return this.rx(new SVGNumber(p.width).divide(2)).ry(new SVGNumber(p.height).divide(2))}}extend(Ellipse,circled);registerMethods("Container",{ellipse:wrapWithAttrCheck(function(width=0,height=width){return this.put(new Ellipse).size(width,height).move(0,0)})});register(Ellipse,"Ellipse");class Fragment extends Dom{constructor(node=globals.document.createDocumentFragment()){super(node)}xml(xmlOrFn,outerXML,ns){if(typeof xmlOrFn==="boolean"){ns=outerXML;outerXML=xmlOrFn;xmlOrFn=null}if(xmlOrFn==null||typeof xmlOrFn==="function"){const wrapper=new Dom(create("wrapper",ns));wrapper.add(this.node.cloneNode(true));return wrapper.xml(false,ns)}return super.xml(xmlOrFn,false,ns)}}register(Fragment,"Fragment");function from(x,y){return(this._element||this).type==="radialGradient"?this.attr({fx:new SVGNumber(x),fy:new SVGNumber(y)}):this.attr({x1:new SVGNumber(x),y1:new SVGNumber(y)})}function to(x,y){return(this._element||this).type==="radialGradient"?this.attr({cx:new SVGNumber(x),cy:new SVGNumber(y)}):this.attr({x2:new SVGNumber(x),y2:new SVGNumber(y)})}var gradiented={__proto__:null,from:from,to:to};class Gradient extends Container{constructor(type,attrs){super(nodeOrNew(type+"Gradient",typeof type==="string"?null:type),attrs)}attr(a,b,c){if(a==="transform")a="gradientTransform";return super.attr(a,b,c)}bbox(){return new Box}targets(){return baseFind('svg [fill*="'+this.id()+'"]')}toString(){return this.url()}update(block){this.clear();if(typeof block==="function"){block.call(this,this)}return this}url(){return'url("#'+this.id()+'")'}}extend(Gradient,gradiented);registerMethods({Container:{gradient(...args){return this.defs().gradient(...args)}},Defs:{gradient:wrapWithAttrCheck(function(type,block){return this.put(new Gradient(type)).update(block)})}});register(Gradient,"Gradient");class Pattern extends Container{constructor(node,attrs=node){super(nodeOrNew("pattern",node),attrs)}attr(a,b,c){if(a==="transform")a="patternTransform";return super.attr(a,b,c)}bbox(){return new Box}targets(){return baseFind('svg [fill*="'+this.id()+'"]')}toString(){return this.url()}update(block){this.clear();if(typeof block==="function"){block.call(this,this)}return this}url(){return'url("#'+this.id()+'")'}}registerMethods({Container:{pattern(...args){return this.defs().pattern(...args)}},Defs:{pattern:wrapWithAttrCheck(function(width,height,block){return this.put(new Pattern).update(block).attr({x:0,y:0,width:width,height:height,patternUnits:"userSpaceOnUse"})})}});register(Pattern,"Pattern");class Image extends Shape{constructor(node,attrs=node){super(nodeOrNew("image",node),attrs)}load(url,callback){if(!url)return this;const img=new globals.window.Image;on(img,"load",function(e){const p=this.parent(Pattern);if(this.width()===0&&this.height()===0){this.size(img.width,img.height)}if(p instanceof Pattern){if(p.width()===0&&p.height()===0){p.size(this.width(),this.height())}}if(typeof callback==="function"){callback.call(this,e)}},this);on(img,"load error",function(){off(img)});return this.attr("href",img.src=url,xlink)}}registerAttrHook(function(attr,val,_this){if(attr==="fill"||attr==="stroke"){if(isImage.test(val)){val=_this.root().defs().image(val)}}if(val instanceof Image){val=_this.root().defs().pattern(0,0,pattern=>{pattern.add(val)})}return val});registerMethods({Container:{image:wrapWithAttrCheck(function(source,callback){return this.put(new Image).size(0,0).load(source,callback)})}});register(Image,"Image");class PointArray extends SVGArray{bbox(){let maxX=-Infinity;let maxY=-Infinity;let minX=Infinity;let minY=Infinity;this.forEach(function(el){maxX=Math.max(el[0],maxX);maxY=Math.max(el[1],maxY);minX=Math.min(el[0],minX);minY=Math.min(el[1],minY)});return new Box(minX,minY,maxX-minX,maxY-minY)}move(x,y){const box=this.bbox();x-=box.x;y-=box.y;if(!isNaN(x)&&!isNaN(y)){for(let i=this.length-1;i>=0;i--){this[i]=[this[i][0]+x,this[i][1]+y]}}return this}parse(array=[0,0]){const points=[];if(array instanceof Array){array=Array.prototype.concat.apply([],array)}else{array=array.trim().split(delimiter).map(parseFloat)}if(array.length%2!==0)array.pop();for(let i=0,len=array.length;i=0;i--){if(box.width)this[i][0]=(this[i][0]-box.x)*width/box.width+box.x;if(box.height)this[i][1]=(this[i][1]-box.y)*height/box.height+box.y}return this}toLine(){return{x1:this[0][0],y1:this[0][1],x2:this[1][0],y2:this[1][1]}}toString(){const array=[];for(let i=0,il=this.length;i":function(pos){return-Math.cos(pos*Math.PI)/2+.5},">":function(pos){return Math.sin(pos*Math.PI/2)},"<":function(pos){return-Math.cos(pos*Math.PI/2)+1},bezier:function(x1,y1,x2,y2){return function(t){if(t<0){if(x1>0){return y1/x1*t}else if(x2>0){return y2/x2*t}else{return 0}}else if(t>1){if(x2<1){return(1-y2)/(1-x2)*t+(y2-x2)/(1-x2)}else if(x1<1){return(1-y1)/(1-x1)*t+(y1-x1)/(1-x1)}else{return 1}}else{return 3*t*(1-t)**2*y1+3*t**2*(1-t)*y2+t**3}}},steps:function(steps,stepPosition="end"){stepPosition=stepPosition.split("-").reverse()[0];let jumps=steps;if(stepPosition==="none"){--jumps}else if(stepPosition==="both"){++jumps}return(t,beforeFlag=false)=>{let step=Math.floor(t*steps);const jumping=t*step%1===0;if(stepPosition==="start"||stepPosition==="both"){++step}if(beforeFlag&&jumping){--step}if(t>=0&&step<0){step=0}if(t<=1&&step>jumps){step=jumps}return step/jumps}}};class Stepper{done(){return false}}class Ease extends Stepper{constructor(fn=timeline.ease){super();this.ease=easing[fn]||fn}step(from,to,pos){if(typeof from!=="number"){return pos<1?from:to}return from+(to-from)*this.ease(pos)}}class Controller extends Stepper{constructor(fn){super();this.stepper=fn}done(c){return c.done}step(current,target,dt,c){return this.stepper(current,target,dt,c)}}function recalculate(){const duration=(this._duration||500)/1e3;const overshoot=this._overshoot||0;const eps=1e-10;const pi=Math.PI;const os=Math.log(overshoot/100+eps);const zeta=-os/Math.sqrt(pi*pi+os*os);const wn=3.9/(zeta*duration);this.d=2*zeta*wn;this.k=wn*wn}class Spring extends Controller{constructor(duration=500,overshoot=0){super();this.duration(duration).overshoot(overshoot)}step(current,target,dt,c){if(typeof current==="string")return current;c.done=dt===Infinity;if(dt===Infinity)return target;if(dt===0)return current;if(dt>100)dt=16;dt/=1e3;const velocity=c.velocity||0;const acceleration=-this.d*velocity-this.k*(current-target);const newPosition=current+velocity*dt+acceleration*dt*dt/2;c.velocity=velocity+acceleration*dt;c.done=Math.abs(target-newPosition)+Math.abs(velocity)<.002;return c.done?target:newPosition}}extend(Spring,{duration:makeSetterGetter("_duration",recalculate),overshoot:makeSetterGetter("_overshoot",recalculate)});class PID extends Controller{constructor(p=.1,i=.01,d=0,windup=1e3){super();this.p(p).i(i).d(d).windup(windup)}step(current,target,dt,c){if(typeof current==="string")return current;c.done=dt===Infinity;if(dt===Infinity)return target;if(dt===0)return current;const p=target-current;let i=(c.integral||0)+p*dt;const d=(p-(c.error||0))/dt;const windup=this._windup;if(windup!==false){i=Math.max(-windup,Math.min(i,windup))}c.error=p;c.integral=i;c.done=Math.abs(p)<.001;return c.done?target:current+(this.P*p+this.I*i+this.D*d)}}extend(PID,{windup:makeSetterGetter("_windup"),p:makeSetterGetter("P"),i:makeSetterGetter("I"),d:makeSetterGetter("D")});const segmentParameters={M:2,L:2,H:1,V:1,C:6,S:4,Q:4,T:2,A:7,Z:0};const pathHandlers={M:function(c,p,p0){p.x=p0.x=c[0];p.y=p0.y=c[1];return["M",p.x,p.y]},L:function(c,p){p.x=c[0];p.y=c[1];return["L",c[0],c[1]]},H:function(c,p){p.x=c[0];return["H",c[0]]},V:function(c,p){p.y=c[0];return["V",c[0]]},C:function(c,p){p.x=c[4];p.y=c[5];return["C",c[0],c[1],c[2],c[3],c[4],c[5]]},S:function(c,p){p.x=c[2];p.y=c[3];return["S",c[0],c[1],c[2],c[3]]},Q:function(c,p){p.x=c[2];p.y=c[3];return["Q",c[0],c[1],c[2],c[3]]},T:function(c,p){p.x=c[0];p.y=c[1];return["T",c[0],c[1]]},Z:function(c,p,p0){p.x=p0.x;p.y=p0.y;return["Z"]},A:function(c,p){p.x=c[5];p.y=c[6];return["A",c[0],c[1],c[2],c[3],c[4],c[5],c[6]]}};const mlhvqtcsaz="mlhvqtcsaz".split("");for(let i=0,il=mlhvqtcsaz.length;i=0;i--){l=this[i][0];if(l==="M"||l==="L"||l==="T"){this[i][1]+=x;this[i][2]+=y}else if(l==="H"){this[i][1]+=x}else if(l==="V"){this[i][1]+=y}else if(l==="C"||l==="S"||l==="Q"){this[i][1]+=x;this[i][2]+=y;this[i][3]+=x;this[i][4]+=y;if(l==="C"){this[i][5]+=x;this[i][6]+=y}}else if(l==="A"){this[i][6]+=x;this[i][7]+=y}}}return this}parse(d="M0 0"){if(Array.isArray(d)){d=Array.prototype.concat.apply([],d).toString()}return pathParser(d)}size(width,height){const box=this.bbox();let i,l;box.width=box.width===0?1:box.width;box.height=box.height===0?1:box.height;for(i=this.length-1;i>=0;i--){l=this[i][0];if(l==="M"||l==="L"||l==="T"){this[i][1]=(this[i][1]-box.x)*width/box.width+box.x;this[i][2]=(this[i][2]-box.y)*height/box.height+box.y}else if(l==="H"){this[i][1]=(this[i][1]-box.x)*width/box.width+box.x}else if(l==="V"){this[i][1]=(this[i][1]-box.y)*height/box.height+box.y}else if(l==="C"||l==="S"||l==="Q"){this[i][1]=(this[i][1]-box.x)*width/box.width+box.x;this[i][2]=(this[i][2]-box.y)*height/box.height+box.y;this[i][3]=(this[i][3]-box.x)*width/box.width+box.x;this[i][4]=(this[i][4]-box.y)*height/box.height+box.y;if(l==="C"){this[i][5]=(this[i][5]-box.x)*width/box.width+box.x;this[i][6]=(this[i][6]-box.y)*height/box.height+box.y}}else if(l==="A"){this[i][1]=this[i][1]*width/box.width;this[i][2]=this[i][2]*height/box.height;this[i][6]=(this[i][6]-box.x)*width/box.width+box.x;this[i][7]=(this[i][7]-box.y)*height/box.height+box.y}}return this}toString(){return arrayToString(this)}}const getClassForType=value=>{const type=typeof value;if(type==="number"){return SVGNumber}else if(type==="string"){if(Color.isColor(value)){return Color}else if(delimiter.test(value)){return isPathLetter.test(value)?PathArray:SVGArray}else if(numberAndUnit.test(value)){return SVGNumber}else{return NonMorphable}}else if(morphableTypes.indexOf(value.constructor)>-1){return value.constructor}else if(Array.isArray(value)){return SVGArray}else if(type==="object"){return ObjectBag}else{return NonMorphable}};class Morphable{constructor(stepper){this._stepper=stepper||new Ease("-");this._from=null;this._to=null;this._type=null;this._context=null;this._morphObj=null}at(pos){return this._morphObj.morph(this._from,this._to,pos,this._stepper,this._context)}done(){const complete=this._context.map(this._stepper.done).reduce(function(last,curr){return last&&curr},true);return complete}from(val){if(val==null){return this._from}this._from=this._set(val);return this}stepper(stepper){if(stepper==null)return this._stepper;this._stepper=stepper;return this}to(val){if(val==null){return this._to}this._to=this._set(val);return this}type(type){if(type==null){return this._type}this._type=type;return this}_set(value){if(!this._type){this.type(getClassForType(value))}let result=new this._type(value);if(this._type===Color){result=this._to?result[this._to[4]]():this._from?result[this._from[4]]():result}if(this._type===ObjectBag){result=this._to?result.align(this._to):this._from?result.align(this._from):result}result=result.toConsumable();this._morphObj=this._morphObj||new this._type;this._context=this._context||Array.apply(null,Array(result.length)).map(Object).map(function(o){o.done=true;return o});return result}}class NonMorphable{constructor(...args){this.init(...args)}init(val){val=Array.isArray(val)?val[0]:val;this.value=val;return this}toArray(){return[this.value]}valueOf(){return this.value}}class TransformBag{constructor(...args){this.init(...args)}init(obj){if(Array.isArray(obj)){obj={scaleX:obj[0],scaleY:obj[1],shear:obj[2],rotate:obj[3],translateX:obj[4],translateY:obj[5],originX:obj[6],originY:obj[7]}}Object.assign(this,TransformBag.defaults,obj);return this}toArray(){const v=this;return[v.scaleX,v.scaleY,v.shear,v.rotate,v.translateX,v.translateY,v.originX,v.originY]}}TransformBag.defaults={scaleX:1,scaleY:1,shear:0,rotate:0,translateX:0,translateY:0,originX:0,originY:0};const sortByKey=(a,b)=>{return a[0]b[0]?1:0};class ObjectBag{constructor(...args){this.init(...args)}align(other){const values=this.values;for(let i=0,il=values.length;ilast.concat(curr),[]);return this}toArray(){return this.values}valueOf(){const obj={};const arr=this.values;while(arr.length){const key=arr.shift();const Type=arr.shift();const num=arr.shift();const values=arr.splice(0,num);obj[key]=new Type(values)}return obj}}const morphableTypes=[NonMorphable,TransformBag,ObjectBag];function registerMorphableType(type=[]){morphableTypes.push(...[].concat(type))}function makeMorphable(){extend(morphableTypes,{to(val){return(new Morphable).type(this.constructor).from(this.toArray()).to(val)},fromArray(arr){this.init(arr);return this},toConsumable(){return this.toArray()},morph(from,to,pos,stepper,context){const mapper=function(i,index){return stepper.step(i,to[index],pos,context[index],context)};return this.fromArray(from.map(mapper))}})}class Path extends Shape{constructor(node,attrs=node){super(nodeOrNew("path",node),attrs)}array(){return this._array||(this._array=new PathArray(this.attr("d")))}clear(){delete this._array;return this}height(height){return height==null?this.bbox().height:this.size(this.bbox().width,height)}move(x,y){return this.attr("d",this.array().move(x,y))}plot(d){return d==null?this.array():this.clear().attr("d",typeof d==="string"?d:this._array=new PathArray(d))}size(width,height){const p=proportionalSize(this,width,height);return this.attr("d",this.array().size(p.width,p.height))}width(width){return width==null?this.bbox().width:this.size(width,this.bbox().height)}x(x){return x==null?this.bbox().x:this.move(x,this.bbox().y)}y(y){return y==null?this.bbox().y:this.move(this.bbox().x,y)}}Path.prototype.MorphArray=PathArray;registerMethods({Container:{path:wrapWithAttrCheck(function(d){return this.put(new Path).plot(d||new PathArray)})}});register(Path,"Path");function array(){return this._array||(this._array=new PointArray(this.attr("points")))}function clear(){delete this._array;return this}function move$2(x,y){return this.attr("points",this.array().move(x,y))}function plot(p){return p==null?this.array():this.clear().attr("points",typeof p==="string"?p:this._array=new PointArray(p))}function size$1(width,height){const p=proportionalSize(this,width,height);return this.attr("points",this.array().size(p.width,p.height))}var poly={__proto__:null,array:array,clear:clear,move:move$2,plot:plot,size:size$1};class Polygon extends Shape{constructor(node,attrs=node){super(nodeOrNew("polygon",node),attrs)}}registerMethods({Container:{polygon:wrapWithAttrCheck(function(p){return this.put(new Polygon).plot(p||new PointArray)})}});extend(Polygon,pointed);extend(Polygon,poly);register(Polygon,"Polygon");class Polyline extends Shape{constructor(node,attrs=node){super(nodeOrNew("polyline",node),attrs)}}registerMethods({Container:{polyline:wrapWithAttrCheck(function(p){return this.put(new Polyline).plot(p||new PointArray)})}});extend(Polyline,pointed);extend(Polyline,poly);register(Polyline,"Polyline");class Rect extends Shape{constructor(node,attrs=node){super(nodeOrNew("rect",node),attrs)}}extend(Rect,{rx:rx,ry:ry});registerMethods({Container:{rect:wrapWithAttrCheck(function(width,height){return this.put(new Rect).size(width,height)})}});register(Rect,"Rect");class Queue{constructor(){this._first=null;this._last=null}first(){return this._first&&this._first.value}last(){return this._last&&this._last.value}push(value){const item=typeof value.next!=="undefined"?value:{value:value,next:null,prev:null};if(this._last){item.prev=this._last;this._last.next=item;this._last=item}else{this._last=item;this._first=item}return item}remove(item){if(item.prev)item.prev.next=item.next;if(item.next)item.next.prev=item.prev;if(item===this._last)this._last=item.prev;if(item===this._first)this._first=item.next;item.prev=null;item.next=null}shift(){const remove=this._first;if(!remove)return null;this._first=remove.next;if(this._first)this._first.prev=null;this._last=this._first?this._last:null;return remove.value}}const Animator={nextDraw:null,frames:new Queue,timeouts:new Queue,immediates:new Queue,timer:()=>globals.window.performance||globals.window.Date,transforms:[],frame(fn){const node=Animator.frames.push({run:fn});if(Animator.nextDraw===null){Animator.nextDraw=globals.window.requestAnimationFrame(Animator._draw)}return node},timeout(fn,delay){delay=delay||0;const time=Animator.timer().now()+delay;const node=Animator.timeouts.push({run:fn,time:time});if(Animator.nextDraw===null){Animator.nextDraw=globals.window.requestAnimationFrame(Animator._draw)}return node},immediate(fn){const node=Animator.immediates.push(fn);if(Animator.nextDraw===null){Animator.nextDraw=globals.window.requestAnimationFrame(Animator._draw)}return node},cancelFrame(node){node!=null&&Animator.frames.remove(node)},clearTimeout(node){node!=null&&Animator.timeouts.remove(node)},cancelImmediate(node){node!=null&&Animator.immediates.remove(node)},_draw(now){let nextTimeout=null;const lastTimeout=Animator.timeouts.last();while(nextTimeout=Animator.timeouts.shift()){if(now>=nextTimeout.time){nextTimeout.run()}else{Animator.timeouts.push(nextTimeout)}if(nextTimeout===lastTimeout)break}let nextFrame=null;const lastFrame=Animator.frames.last();while(nextFrame!==lastFrame&&(nextFrame=Animator.frames.shift())){nextFrame.run(now)}let nextImmediate=null;while(nextImmediate=Animator.immediates.shift()){nextImmediate()}Animator.nextDraw=Animator.timeouts.first()||Animator.frames.first()?globals.window.requestAnimationFrame(Animator._draw):null}};const makeSchedule=function(runnerInfo){const start=runnerInfo.start;const duration=runnerInfo.runner.duration();const end=start+duration;return{start:start,duration:duration,end:end,runner:runnerInfo.runner}};const defaultSource=function(){const w=globals.window;return(w.performance||w.Date).now()};class Timeline extends EventTarget{constructor(timeSource=defaultSource){super();this._timeSource=timeSource;this._startTime=0;this._speed=1;this._persist=0;this._nextFrame=null;this._paused=true;this._runners=[];this._runnerIds=[];this._lastRunnerId=-1;this._time=0;this._lastSourceTime=0;this._lastStepTime=0;this._step=this._stepFn.bind(this,false);this._stepImmediate=this._stepFn.bind(this,true)}active(){return!!this._nextFrame}finish(){this.time(this.getEndTimeOfTimeline()+1);return this.pause()}getEndTime(){const lastRunnerInfo=this.getLastRunnerInfo();const lastDuration=lastRunnerInfo?lastRunnerInfo.runner.duration():0;const lastStartTime=lastRunnerInfo?lastRunnerInfo.start:this._time;return lastStartTime+lastDuration}getEndTimeOfTimeline(){const endTimes=this._runners.map(i=>i.start+i.runner.duration());return Math.max(0,...endTimes)}getLastRunnerInfo(){return this.getRunnerInfoById(this._lastRunnerId)}getRunnerInfoById(id){return this._runners[this._runnerIds.indexOf(id)]||null}pause(){this._paused=true;return this._continue()}persist(dtOrForever){if(dtOrForever==null)return this._persist;this._persist=dtOrForever;return this}play(){this._paused=false;return this.updateTime()._continue()}reverse(yes){const currentSpeed=this.speed();if(yes==null)return this.speed(-currentSpeed);const positive=Math.abs(currentSpeed);return this.speed(yes?-positive:positive)}schedule(runner,delay,when){if(runner==null){return this._runners.map(makeSchedule)}let absoluteStartTime=0;const endTime=this.getEndTime();delay=delay||0;if(when==null||when==="last"||when==="after"){absoluteStartTime=endTime}else if(when==="absolute"||when==="start"){absoluteStartTime=delay;delay=0}else if(when==="now"){absoluteStartTime=this._time}else if(when==="relative"){const runnerInfo=this.getRunnerInfoById(runner.id);if(runnerInfo){absoluteStartTime=runnerInfo.start+delay;delay=0}}else if(when==="with-last"){const lastRunnerInfo=this.getLastRunnerInfo();const lastStartTime=lastRunnerInfo?lastRunnerInfo.start:this._time;absoluteStartTime=lastStartTime}else{throw new Error('Invalid value for the "when" parameter')}runner.unschedule();runner.timeline(this);const persist=runner.persist();const runnerInfo={persist:persist===null?this._persist:persist,start:absoluteStartTime+delay,runner:runner};this._lastRunnerId=runner.id;this._runners.push(runnerInfo);this._runners.sort((a,b)=>a.start-b.start);this._runnerIds=this._runners.map(info=>info.runner.id);this.updateTime()._continue();return this}seek(dt){return this.time(this._time+dt)}source(fn){if(fn==null)return this._timeSource;this._timeSource=fn;return this}speed(speed){if(speed==null)return this._speed;this._speed=speed;return this}stop(){this.time(0);return this.pause()}time(time){if(time==null)return this._time;this._time=time;return this._continue(true)}unschedule(runner){const index=this._runnerIds.indexOf(runner.id);if(index<0)return this;this._runners.splice(index,1);this._runnerIds.splice(index,1);runner.timeline(null);return this}updateTime(){if(!this.active()){this._lastSourceTime=this._timeSource()}return this}_continue(immediateStep=false){Animator.cancelFrame(this._nextFrame);this._nextFrame=null;if(immediateStep)return this._stepImmediate();if(this._paused)return this;this._nextFrame=Animator.frame(this._step);return this}_stepFn(immediateStep=false){const time=this._timeSource();let dtSource=time-this._lastSourceTime;if(immediateStep)dtSource=0;const dtTime=this._speed*dtSource+(this._time-this._lastStepTime);this._lastSourceTime=time;if(!immediateStep){this._time+=dtTime;this._time=this._time<0?0:this._time}this._lastStepTime=this._time;this.fire("time",this._time);for(let k=this._runners.length;k--;){const runnerInfo=this._runners[k];const runner=runnerInfo.runner;const dtToStart=this._time-runnerInfo.start;if(dtToStart<=0){runner.reset()}}let runnersLeft=false;for(let i=0,len=this._runners.length;i0){this._continue()}else{this.pause();this.fire("finished")}return this}}registerMethods({Element:{timeline:function(timeline){if(timeline==null){this._timeline=this._timeline||new Timeline;return this._timeline}else{this._timeline=timeline;return this}}}});class Runner extends EventTarget{constructor(options){super();this.id=Runner.id++;options=options==null?timeline.duration:options;options=typeof options==="function"?new Controller(options):options;this._element=null;this._timeline=null;this.done=false;this._queue=[];this._duration=typeof options==="number"&&options;this._isDeclarative=options instanceof Controller;this._stepper=this._isDeclarative?options:new Ease;this._history={};this.enabled=true;this._time=0;this._lastTime=0;this._reseted=true;this.transforms=new Matrix;this.transformId=1;this._haveReversed=false;this._reverse=false;this._loopsDone=0;this._swing=false;this._wait=0;this._times=1;this._frameId=null;this._persist=this._isDeclarative?true:null}static sanitise(duration,delay,when){let times=1;let swing=false;let wait=0;duration=duration||timeline.duration;delay=delay||timeline.delay;when=when||"last";if(typeof duration==="object"&&!(duration instanceof Stepper)){delay=duration.delay||delay;when=duration.when||when;swing=duration.swing||swing;times=duration.times||times;wait=duration.wait||wait;duration=duration.duration||timeline.duration}return{duration:duration,delay:delay,swing:swing,times:times,wait:wait,when:when}}active(enabled){if(enabled==null)return this.enabled;this.enabled=enabled;return this}addTransform(transform,index){this.transforms.lmultiplyO(transform);return this}after(fn){return this.on("finished",fn)}animate(duration,delay,when){const o=Runner.sanitise(duration,delay,when);const runner=new Runner(o.duration);if(this._timeline)runner.timeline(this._timeline);if(this._element)runner.element(this._element);return runner.loop(o).schedule(o.delay,o.when)}clearTransform(){this.transforms=new Matrix;return this}clearTransformsFromQueue(){if(!this.done||!this._timeline||!this._timeline._runnerIds.includes(this.id)){this._queue=this._queue.filter(item=>{return!item.isTransform})}}delay(delay){return this.animate(0,delay)}duration(){return this._times*(this._wait+this._duration)-this._wait}during(fn){return this.queue(null,fn)}ease(fn){this._stepper=new Ease(fn);return this}element(element){if(element==null)return this._element;this._element=element;element._prepareRunner();return this}finish(){return this.step(Infinity)}loop(times,swing,wait){if(typeof times==="object"){swing=times.swing;wait=times.wait;times=times.times}this._times=times||Infinity;this._swing=swing||false;this._wait=wait||0;if(this._times===true){this._times=Infinity}return this}loops(p){const loopDuration=this._duration+this._wait;if(p==null){const loopsDone=Math.floor(this._time/loopDuration);const relativeTime=this._time-loopsDone*loopDuration;const position=relativeTime/this._duration;return Math.min(loopsDone+position,this._times)}const whole=Math.floor(p);const partial=p%1;const time=loopDuration*whole+this._duration*partial;return this.time(time)}persist(dtOrForever){if(dtOrForever==null)return this._persist;this._persist=dtOrForever;return this}position(p){const x=this._time;const d=this._duration;const w=this._wait;const t=this._times;const s=this._swing;const r=this._reverse;let position;if(p==null){const f=function(x){const swinging=s*Math.floor(x%(2*(w+d))/(w+d));const backwards=swinging&&!r||!swinging&&r;const uncliped=Math.pow(-1,backwards)*(x%(w+d))/d+backwards;const clipped=Math.max(Math.min(uncliped,1),0);return clipped};const endTime=t*(w+d)-w;position=x<=0?Math.round(f(1e-5)):x=0;this._lastPosition=position;const duration=this.duration();const justStarted=this._lastTime<=0&&this._time>0;const justFinished=this._lastTime=duration;this._lastTime=this._time;if(justStarted){this.fire("start",this)}const declarative=this._isDeclarative;this.done=!declarative&&!justFinished&&this._time>=duration;this._reseted=false;let converged=false;if(running||declarative){this._initialise(running);this.transforms=new Matrix;converged=this._run(declarative?dt:position);this.fire("step",this)}this.done=this.done||converged&&declarative;if(justFinished){this.fire("finished",this)}return this}time(time){if(time==null){return this._time}const dt=time-this._time;this.step(dt);return this}timeline(timeline){if(typeof timeline==="undefined")return this._timeline;this._timeline=timeline;return this}unschedule(){const timeline=this.timeline();timeline&&timeline.unschedule(this);return this}_initialise(running){if(!running&&!this._isDeclarative)return;for(let i=0,len=this._queue.length;ilast.lmultiplyO(curr);const getRunnerTransform=runner=>runner.transforms;function mergeTransforms(){const runners=this._transformationRunners.runners;const netTransform=runners.map(getRunnerTransform).reduce(lmultiply,new Matrix);this.transform(netTransform);this._transformationRunners.merge();if(this._transformationRunners.length()===1){this._frameId=null}}class RunnerArray{constructor(){this.runners=[];this.ids=[]}add(runner){if(this.runners.includes(runner))return;const id=runner.id+1;this.runners.push(runner);this.ids.push(id);return this}clearBefore(id){const deleteCnt=this.ids.indexOf(id+1)||1;this.ids.splice(0,deleteCnt,0);this.runners.splice(0,deleteCnt,new FakeRunner).forEach(r=>r.clearTransformsFromQueue());return this}edit(id,newRunner){const index=this.ids.indexOf(id+1);this.ids.splice(index,1,id+1);this.runners.splice(index,1,newRunner);return this}getByID(id){return this.runners[this.ids.indexOf(id+1)]}length(){return this.ids.length}merge(){let lastRunner=null;for(let i=0;irunner.id<=current.id).map(getRunnerTransform).reduce(lmultiply,new Matrix)},_addRunner(runner){this._transformationRunners.add(runner);Animator.cancelImmediate(this._frameId);this._frameId=Animator.immediate(mergeTransforms.bind(this))},_prepareRunner(){if(this._frameId==null){this._transformationRunners=(new RunnerArray).add(new FakeRunner(new Matrix(this)))}}}});const difference=(a,b)=>a.filter(x=>!b.includes(x));extend(Runner,{attr(a,v){return this.styleAttr("attr",a,v)},css(s,v){return this.styleAttr("css",s,v)},styleAttr(type,nameOrAttrs,val){if(typeof nameOrAttrs==="string"){return this.styleAttr(type,{[nameOrAttrs]:val})}let attrs=nameOrAttrs;if(this._tryRetarget(type,attrs))return this;let morpher=new Morphable(this._stepper).to(attrs);let keys=Object.keys(attrs);this.queue(function(){morpher=morpher.from(this.element()[type](keys))},function(pos){this.element()[type](morpher.at(pos).valueOf());return morpher.done()},function(newToAttrs){const newKeys=Object.keys(newToAttrs);const differences=difference(newKeys,keys);if(differences.length){const addedFromAttrs=this.element()[type](differences);const oldFromAttrs=new ObjectBag(morpher.from()).valueOf();Object.assign(oldFromAttrs,addedFromAttrs);morpher.from(oldFromAttrs)}const oldToAttrs=new ObjectBag(morpher.to()).valueOf();Object.assign(oldToAttrs,newToAttrs);morpher.to(oldToAttrs);keys=newKeys;attrs=newToAttrs});this._rememberMorpher(type,morpher);return this},zoom(level,point){if(this._tryRetarget("zoom",level,point))return this;let morpher=new Morphable(this._stepper).to(new SVGNumber(level));this.queue(function(){morpher=morpher.from(this.element().zoom())},function(pos){this.element().zoom(morpher.at(pos),point);return morpher.done()},function(newLevel,newPoint){point=newPoint;morpher.to(newLevel)});this._rememberMorpher("zoom",morpher);return this},transform(transforms,relative,affine){relative=transforms.relative||relative;if(this._isDeclarative&&!relative&&this._tryRetarget("transform",transforms)){return this}const isMatrix=Matrix.isMatrixLike(transforms);affine=transforms.affine!=null?transforms.affine:affine!=null?affine:!isMatrix;const morpher=new Morphable(this._stepper).type(affine?TransformBag:Matrix);let origin;let element;let current;let currentAngle;let startTransform;function setup(){element=element||this.element();origin=origin||getOrigin(transforms,element);startTransform=new Matrix(relative?undefined:element);element._addRunner(this);if(!relative){element._clearTransformRunnersBefore(this)}}function run(pos){if(!relative)this.clearTransform();const{x,y}=new Point(origin).transform(element._currentTransform(this));let target=new Matrix({...transforms,origin:[x,y]});let start=this._isDeclarative&¤t?current:startTransform;if(affine){target=target.decompose(x,y);start=start.decompose(x,y);const rTarget=target.rotate;const rCurrent=start.rotate;const possibilities=[rTarget-360,rTarget,rTarget+360];const distances=possibilities.map(a=>Math.abs(a-rCurrent));const shortest=Math.min(...distances);const index=distances.indexOf(shortest);target.rotate=possibilities[index]}if(relative){if(!isMatrix){target.rotate=transforms.rotate||0}if(this._isDeclarative&¤tAngle){start.rotate=currentAngle}}morpher.from(start);morpher.to(target);const affineParameters=morpher.at(pos);currentAngle=affineParameters.rotate;current=new Matrix(affineParameters);this.addTransform(current);element._addRunner(this);return morpher.done()}function retarget(newTransforms){if((newTransforms.origin||"center").toString()!==(transforms.origin||"center").toString()){origin=getOrigin(newTransforms,element)}transforms={...newTransforms,origin:origin}}this.queue(setup,run,retarget,true);this._isDeclarative&&this._rememberMorpher("transform",morpher);return this},x(x,relative){return this._queueNumber("x",x)},y(y){return this._queueNumber("y",y)},dx(x=0){return this._queueNumberDelta("x",x)},dy(y=0){return this._queueNumberDelta("y",y)},dmove(x,y){return this.dx(x).dy(y)},_queueNumberDelta(method,to){to=new SVGNumber(to);if(this._tryRetarget(method,to))return this;const morpher=new Morphable(this._stepper).to(to);let from=null;this.queue(function(){from=this.element()[method]();morpher.from(from);morpher.to(from+to)},function(pos){this.element()[method](morpher.at(pos));return morpher.done()},function(newTo){morpher.to(from+new SVGNumber(newTo))});this._rememberMorpher(method,morpher);return this},_queueObject(method,to){if(this._tryRetarget(method,to))return this;const morpher=new Morphable(this._stepper).to(to);this.queue(function(){morpher.from(this.element()[method]())},function(pos){this.element()[method](morpher.at(pos));return morpher.done()});this._rememberMorpher(method,morpher);return this},_queueNumber(method,value){return this._queueObject(method,new SVGNumber(value))},cx(x){return this._queueNumber("cx",x)},cy(y){return this._queueNumber("cy",y)},move(x,y){return this.x(x).y(y)},center(x,y){return this.cx(x).cy(y)},size(width,height){let box;if(!width||!height){box=this._element.bbox()}if(!width){width=box.width/box.height*height}if(!height){height=box.height/box.width*width}return this.width(width).height(height)},width(width){return this._queueNumber("width",width)},height(height){return this._queueNumber("height",height)},plot(a,b,c,d){if(arguments.length===4){return this.plot([a,b,c,d])}if(this._tryRetarget("plot",a))return this;const morpher=new Morphable(this._stepper).type(this._element.MorphArray).to(a);this.queue(function(){morpher.from(this._element.array())},function(pos){this._element.plot(morpher.at(pos));return morpher.done()});this._rememberMorpher("plot",morpher);return this},leading(value){return this._queueNumber("leading",value)},viewbox(x,y,width,height){return this._queueObject("viewbox",new Box(x,y,width,height))},update(o){if(typeof o!=="object"){return this.update({offset:arguments[0],color:arguments[1],opacity:arguments[2]})}if(o.opacity!=null)this.attr("stop-opacity",o.opacity);if(o.color!=null)this.attr("stop-color",o.color);if(o.offset!=null)this.attr("offset",o.offset);return this}});extend(Runner,{rx:rx,ry:ry,from:from,to:to});register(Runner,"Runner");class Svg extends Container{constructor(node,attrs=node){super(nodeOrNew("svg",node),attrs);this.namespace()}defs(){if(!this.isRoot())return this.root().defs();return adopt(this.node.querySelector("defs"))||this.put(new Defs)}isRoot(){return!this.node.parentNode||!(this.node.parentNode instanceof globals.window.SVGElement)&&this.node.parentNode.nodeName!=="#document-fragment"}namespace(){if(!this.isRoot())return this.root().namespace();return this.attr({xmlns:svg,version:"1.1"}).attr("xmlns:xlink",xlink,xmlns).attr("xmlns:svgjs",svgjs,xmlns)}removeNamespace(){return this.attr({xmlns:null,version:null}).attr("xmlns:xlink",null,xmlns).attr("xmlns:svgjs",null,xmlns)}root(){if(this.isRoot())return this;return super.root()}}registerMethods({Container:{nested:wrapWithAttrCheck(function(){return this.put(new Svg)})}});register(Svg,"Svg",true);class Symbol extends Container{constructor(node,attrs=node){super(nodeOrNew("symbol",node),attrs)}}registerMethods({Container:{symbol:wrapWithAttrCheck(function(){return this.put(new Symbol)})}});register(Symbol,"Symbol");function plain(text){if(this._build===false){this.clear()}this.node.appendChild(globals.document.createTextNode(text));return this}function length(){return this.node.getComputedTextLength()}function x$1(x,box=this.bbox()){if(x==null){return box.x}return this.attr("x",this.attr("x")+x-box.x)}function y$1(y,box=this.bbox()){if(y==null){return box.y}return this.attr("y",this.attr("y")+y-box.y)}function move$1(x,y,box=this.bbox()){return this.x(x,box).y(y,box)}function cx(x,box=this.bbox()){if(x==null){return box.cx}return this.attr("x",this.attr("x")+x-box.cx)}function cy(y,box=this.bbox()){if(y==null){return box.cy}return this.attr("y",this.attr("y")+y-box.cy)}function center(x,y,box=this.bbox()){return this.cx(x,box).cy(y,box)}function ax(x){return this.attr("x",x)}function ay(y){return this.attr("y",y)}function amove(x,y){return this.ax(x).ay(y)}function build(build){this._build=!!build;return this}var textable={__proto__:null,plain:plain,length:length,x:x$1,y:y$1,move:move$1,cx:cx,cy:cy,center:center,ax:ax,ay:ay,amove:amove,build:build};class Text extends Shape{constructor(node,attrs=node){super(nodeOrNew("text",node),attrs);this.dom.leading=new SVGNumber(1.3);this._rebuild=true;this._build=false}leading(value){if(value==null){return this.dom.leading}this.dom.leading=new SVGNumber(value);return this.rebuild()}rebuild(rebuild){if(typeof rebuild==="boolean"){this._rebuild=rebuild}if(this._rebuild){const self=this;let blankLineOffset=0;const leading=this.dom.leading;this.each(function(i){const fontSize=globals.window.getComputedStyle(this.node).getPropertyValue("font-size");const dy=leading*new SVGNumber(fontSize);if(this.dom.newLined){this.attr("x",self.attr("x"));if(this.text()==="\n"){blankLineOffset+=dy}else{this.attr("dy",i?dy+blankLineOffset:0);blankLineOffset=0}}});this.fire("rebuild")}return this}setData(o){this.dom=o;this.dom.leading=new SVGNumber(o.leading||1.3);return this}text(text){if(text===undefined){const children=this.node.childNodes;let firstLine=0;text="";for(let i=0,len=children.length;i{let bbox;try{bbox=child.bbox()}catch(e){return}const m=new Matrix(child);const matrix=m.translate(dx,dy).transform(m.inverse());const p=new Point(bbox.x,bbox.y).transform(matrix);child.move(p.x,p.y)});return this}function dx(dx){return this.dmove(dx,0)}function dy(dy){return this.dmove(0,dy)}function height(height,box=this.bbox()){if(height==null)return box.height;return this.size(box.width,height,box)}function move(x=0,y=0,box=this.bbox()){const dx=x-box.x;const dy=y-box.y;return this.dmove(dx,dy)}function size(width,height,box=this.bbox()){const p=proportionalSize(this,width,height,box);const scaleX=p.width/box.width;const scaleY=p.height/box.height;this.children().forEach((child,i)=>{const o=new Point(box).transform(new Matrix(child).inverse());child.scale(scaleX,scaleY,o.x,o.y)});return this}function width(width,box=this.bbox()){if(width==null)return box.width;return this.size(width,box.height,box)}function x(x,box=this.bbox()){if(x==null)return box.x;return this.move(x,box.y,box)}function y(y,box=this.bbox()){if(y==null)return box.y;return this.move(box.x,y,box)}var containerGeometry={__proto__:null,dmove:dmove,dx:dx,dy:dy,height:height,move:move,size:size,width:width,x:x,y:y};class G extends Container{constructor(node,attrs=node){super(nodeOrNew("g",node),attrs)}}extend(G,containerGeometry);registerMethods({Container:{group:wrapWithAttrCheck(function(){return this.put(new G)})}});register(G,"G");class A extends Container{constructor(node,attrs=node){super(nodeOrNew("a",node),attrs)}target(target){return this.attr("target",target)}to(url){return this.attr("href",url,xlink)}}extend(A,containerGeometry);registerMethods({Container:{link:wrapWithAttrCheck(function(url){return this.put(new A).to(url)})},Element:{unlink(){const link=this.linker();if(!link)return this;const parent=link.parent();if(!parent){return this.remove()}const index=parent.index(link);parent.add(this,index);link.remove();return this},linkTo(url){let link=this.linker();if(!link){link=new A;this.wrap(link)}if(typeof url==="function"){url.call(link,link)}else{link.to(url)}return this},linker(){const link=this.parent();if(link&&link.node.nodeName.toLowerCase()==="a"){return link}return null}}});register(A,"A");class Mask extends Container{constructor(node,attrs=node){super(nodeOrNew("mask",node),attrs)}remove(){this.targets().forEach(function(el){el.unmask()});return super.remove()}targets(){return baseFind('svg [mask*="'+this.id()+'"]')}}registerMethods({Container:{mask:wrapWithAttrCheck(function(){return this.defs().put(new Mask)})},Element:{masker(){return this.reference("mask")},maskWith(element){const masker=element instanceof Mask?element:this.parent().mask().add(element);return this.attr("mask",'url("#'+masker.id()+'")')},unmask(){return this.attr("mask",null)}}});register(Mask,"Mask");class Stop extends Element{constructor(node,attrs=node){super(nodeOrNew("stop",node),attrs)}update(o){if(typeof o==="number"||o instanceof SVGNumber){o={offset:arguments[0],color:arguments[1],opacity:arguments[2]}}if(o.opacity!=null)this.attr("stop-opacity",o.opacity);if(o.color!=null)this.attr("stop-color",o.color);if(o.offset!=null)this.attr("offset",new SVGNumber(o.offset));return this}}registerMethods({Gradient:{stop:function(offset,color,opacity){return this.put(new Stop).update(offset,color,opacity)}}});register(Stop,"Stop");function cssRule(selector,rule){if(!selector)return"";if(!rule)return selector;let ret=selector+"{";for(const i in rule){ret+=unCamelCase(i)+":"+rule[i]+";"}ret+="}";return ret}class Style extends Element{constructor(node,attrs=node){super(nodeOrNew("style",node),attrs)}addText(w=""){this.node.textContent+=w;return this}font(name,src,params={}){return this.rule("@font-face",{fontFamily:name,src:src,...params})}rule(selector,obj){return this.addText(cssRule(selector,obj))}}registerMethods("Dom",{style(selector,obj){return this.put(new Style).rule(selector,obj)},fontface(name,src,params){return this.put(new Style).font(name,src,params)}});register(Style,"Style");class TextPath extends Text{constructor(node,attrs=node){super(nodeOrNew("textPath",node),attrs)}array(){const track=this.track();return track?track.array():null}plot(d){const track=this.track();let pathArray=null;if(track){pathArray=track.plot(d)}return d==null?pathArray:this}track(){return this.reference("href")}}registerMethods({Container:{textPath:wrapWithAttrCheck(function(text,path){if(!(text instanceof Text)){text=this.text(text)}return text.path(path)})},Text:{path:wrapWithAttrCheck(function(track,importNodes=true){const textPath=new TextPath;if(!(track instanceof Path)){track=this.defs().path(track)}textPath.attr("href","#"+track,xlink);let node;if(importNodes){while(node=this.node.firstChild){textPath.node.appendChild(node)}}return this.put(textPath)}),textPath(){return this.findOne("textPath")}},Path:{text:wrapWithAttrCheck(function(text){if(!(text instanceof Text)){text=(new Text).addTo(this.parent()).text(text)}return text.path(this)}),targets(){return baseFind("svg textPath").filter(node=>{return(node.attr("href")||"").includes(this.id())})}}});TextPath.prototype.MorphArray=PathArray;register(TextPath,"TextPath");class Use extends Shape{constructor(node,attrs=node){super(nodeOrNew("use",node),attrs)}use(element,file){return this.attr("href",(file||"")+"#"+element,xlink)}}registerMethods({Container:{use:wrapWithAttrCheck(function(element,file){return this.put(new Use).use(element,file)})}});register(Use,"Use");const SVG$1=makeInstance;extend([Svg,Symbol,Image,Pattern,Marker],getMethodsFor("viewbox"));extend([Line,Polyline,Polygon,Path],getMethodsFor("marker"));extend(Text,getMethodsFor("Text"));extend(Path,getMethodsFor("Path"));extend(Defs,getMethodsFor("Defs"));extend([Text,Tspan],getMethodsFor("Tspan"));extend([Rect,Ellipse,Gradient,Runner],getMethodsFor("radius"));extend(EventTarget,getMethodsFor("EventTarget"));extend(Dom,getMethodsFor("Dom"));extend(Element,getMethodsFor("Element"));extend(Shape,getMethodsFor("Shape"));extend([Container,Fragment],getMethodsFor("Container"));extend(Gradient,getMethodsFor("Gradient"));extend(Runner,getMethodsFor("Runner"));List.extend(getMethodNames());registerMorphableType([SVGNumber,Color,Box,Matrix,SVGArray,PointArray,PathArray,Point]);makeMorphable();var svgMembers={__proto__:null,Morphable:Morphable,registerMorphableType:registerMorphableType,makeMorphable:makeMorphable,TransformBag:TransformBag,ObjectBag:ObjectBag,NonMorphable:NonMorphable,defaults:defaults,utils:utils,namespaces:namespaces,regex:regex,SVG:SVG$1,parser:parser,find:baseFind,getWindow:getWindow,registerWindow:registerWindow,restoreWindow:restoreWindow,saveWindow:saveWindow,withWindow:withWindow,Animator:Animator,Controller:Controller,Ease:Ease,PID:PID,Spring:Spring,easing:easing,Queue:Queue,Runner:Runner,Timeline:Timeline,Array:SVGArray,Box:Box,Color:Color,EventTarget:EventTarget,Matrix:Matrix,Number:SVGNumber,PathArray:PathArray,Point:Point,PointArray:PointArray,List:List,Circle:Circle,ClipPath:ClipPath,Container:Container,Defs:Defs,Dom:Dom,Element:Element,Ellipse:Ellipse,ForeignObject:ForeignObject,Fragment:Fragment,Gradient:Gradient,G:G,A:A,Image:Image,Line:Line,Marker:Marker,Mask:Mask,Path:Path,Pattern:Pattern,Polygon:Polygon,Polyline:Polyline,Rect:Rect,Shape:Shape,Stop:Stop,Style:Style,Svg:Svg,Symbol:Symbol,Text:Text,TextPath:TextPath,Tspan:Tspan,Use:Use,windowEvents:windowEvents,getEvents:getEvents,getEventTarget:getEventTarget,clearEvents:clearEvents,on:on,off:off,dispatch:dispatch,root:root,create:create,makeInstance:makeInstance,nodeOrNew:nodeOrNew,adopt:adopt,mockAdopt:mockAdopt,register:register,getClass:getClass,eid:eid,assignNewId:assignNewId,extend:extend,wrapWithAttrCheck:wrapWithAttrCheck};function SVG(element,isHTML){return makeInstance(element,isHTML)}Object.assign(SVG,svgMembers);return SVG}(); +/*! + @licstart The following is the entire license notice for the JavaScript code in this file. + The code below is based on SVGPan Library 1.2 and was modified for doxygen + to support both zooming and panning via the mouse and via embedded buttons. + + This code is licensed under the following BSD license: + + Copyright 2009-2010 Andrea Leofreddi . All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are + permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list + of conditions and the following disclaimer in the documentation and/or other materials + provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY Andrea Leofreddi ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Andrea Leofreddi OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + The views and conclusions contained in the software and documentation are those of the + authors and should not be interpreted as representing official policies, either expressed + or implied, of Andrea Leofreddi. + + @licend The above is the entire license notice for the JavaScript code in this file + */ +var root=document.documentElement;var state="none";var stateOrigin;var stateTf=root.createSVGMatrix();var cursorGrab=' url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAMAAAAolt3jAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAlQTFRFAAAA////////c3ilYwAAAAN0Uk5T//8A18oNQQAAAD1JREFUeNp0zlEKACAIA9Bt9z90bZBZkQj29qFBEuBOzQHSnWTTyckEfqUuZgFvslH4ch3qLCO/Kr8cAgwATw4Ax6XRCcoAAAAASUVORK5CYII="), move';var zoomSteps=10;var zoomInFactor;var zoomOutFactor;var windowWidth;var windowHeight;var svgDoc;var minZoom;var maxZoom;if(!window)window=this;function show(){if(window.innerHeight){windowWidth=window.innerWidth;windowHeight=window.innerHeight}else if(document.documentElement.clientWidth){windowWidth=document.documentElement.clientWidth;windowHeight=document.documentElement.clientHeight}if(!windowWidth||!windowHeight){windowWidth=800;windowHeight=600}minZoom=Math.min(Math.min(viewHeight,windowHeight)/viewHeight,Math.min(viewWidth,windowWidth)/viewWidth);maxZoom=minZoom+1.5;zoomInFactor=Math.pow(maxZoom/minZoom,1/zoomSteps);zoomOutFactor=1/zoomInFactor;var g=svgDoc.getElementById("viewport");try{var bb=g.getBBox();var tx=(windowWidth-viewWidth*minZoom+8)/(2*minZoom);var ty=viewHeight+(windowHeight-viewHeight*minZoom)/(2*minZoom);var a="scale("+minZoom+") rotate(0) translate("+tx+" "+ty+")";g.setAttribute("transform",a)}catch(e){}}function init(evt){svgDoc=evt.target.ownerDocument;try{if(top.window&&top.window.registerShow){top.window.registerShow(sectionId,show)}}catch(e){}show();setAttributes(root,{onmousedown:"handleMouseDown(evt)",onmousemove:"handleMouseMove(evt)",onmouseup:"handleMouseUp(evt)"});if(window.addEventListener){if(navigator.userAgent.toLowerCase().indexOf("webkit")>=0||navigator.userAgent.toLowerCase().indexOf("opera")>=0||navigator.appVersion.indexOf("MSIE")!=-1){window.addEventListener("mousewheel",handleMouseWheel,false)}else{window.addEventListener("DOMMouseScroll",handleMouseWheel,false)}}}window.onresize=function(){if(svgDoc){show()}};function getEventPoint(evt){var p=root.createSVGPoint();p.x=evt.clientX;p.y=evt.clientY;return p}function setCTM(element,matrix){var s="matrix("+matrix.a+","+matrix.b+","+matrix.c+","+matrix.d+","+matrix.e+","+matrix.f+")";element.setAttribute("transform",s)}function setAttributes(element,attributes){for(i in attributes)element.setAttributeNS(null,i,attributes[i])}function doZoom(g,point,zoomFactor){var p=point.matrixTransform(g.getCTM().inverse());var k=root.createSVGMatrix().translate(p.x,p.y).scale(zoomFactor).translate(-p.x,-p.y);var n=g.getCTM().multiply(k);var s=Math.max(n.a,n.d);if(s>maxZoom)n=n.translate(p.x,p.y).scale(maxZoom/s).translate(-p.x,-p.y);else if(s');d.write("Print SVG");d.write('');d.write('
'+xs+"
");d.write("");d.write("");d.close()}catch(e){alert("Failed to open popup window needed for printing!\n"+e.message)}}function highlightEdges(){var elems=document.getElementsByTagName("g");if(elems){for(var i=0;i g");function findEnclosingG(domEl){let curEl=domEl;while(curEl.nodeName!="g"||curEl.id.substr(0,4)!="Node"){curEl=curEl.parentElement}return curEl}function onMouseOverElem(domEl){let e=SVG(findEnclosingG(domEl.target));walk(s,e=>{if(SVG(e)!=s)SVG(e).attr("data-mouse-over-selected","false")});walk(e,e=>SVG(e).attr("data-mouse-over-selected","true"));let{nodes,edges}=getEdgesAndDistance1Nodes(SVG(e),s);for(let node of nodes){walk(node,e=>SVG(e).attr("data-mouse-over-selected","true"))}for(let edge of edges){walk(edge,e=>SVG(e).attr("data-mouse-over-selected","true"))}}function onMouseOutElem(domEl){let e=SVG(findEnclosingG(domEl.target));walk(s,e=>e.attr("data-mouse-over-selected",null))}let gs=s.find("g[id^=Node]");for(let g of gs){g.on("mouseover",onMouseOverElem);g.on("mouseout",onMouseOutElem)}} diff --git a/HEAD/sync_off.png b/HEAD/sync_off.png new file mode 100644 index 0000000000000000000000000000000000000000..3b443fc62892114406e3d399421b2a881b897acc GIT binary patch literal 853 zcmV-b1FHOqP)oT|#XixUYy%lpuf3i8{fX!o zUyDD0jOrAiT^tq>fLSOOABs-#u{dV^F$b{L9&!2=9&RmV;;8s^x&UqB$PCj4FdKbh zoB1WTskPUPu05XzFbA}=KZ-GP1fPpAfSs>6AHb12UlR%-i&uOlTpFNS7{jm@mkU1V zh`nrXr~+^lsV-s1dkZOaI|kYyVj3WBpPCY{n~yd%u%e+d=f%`N0FItMPtdgBb@py; zq@v6NVArhyTC7)ULw-Jy8y42S1~4n(3LkrW8mW(F-4oXUP3E`e#g**YyqI7h-J2zK zK{m9##m4ri!7N>CqQqCcnI3hqo1I;Yh&QLNY4T`*ptiQGozK>FF$!$+84Z`xwmeMh zJ0WT+OH$WYFALEaGj2_l+#DC3t7_S`vHpSivNeFbP6+r50cO8iu)`7i%Z4BTPh@_m3Tk!nAm^)5Bqnr%Ov|Baunj#&RPtRuK& z4RGz|D5HNrW83-#ydk}tVKJrNmyYt-sTxLGlJY5nc&Re zU4SgHNPx8~Yxwr$bsju?4q&%T1874xxzq+_%?h8_ofw~(bld=o3iC)LUNR*BY%c0y zWd_jX{Y8`l%z+ol1$@Qa?Cy!(0CVIEeYpKZ`(9{z>3$CIe;pJDQk$m3p}$>xBm4lb zKo{4S)`wdU9Ba9jJbVJ0C=SOefZe%d$8=2r={nu<_^a3~>c#t_U6dye5)JrR(_a^E f@}b6j1K9lwFJq@>o)+Ry00000NkvXXu0mjfWa5j* literal 0 HcmV?d00001 diff --git a/HEAD/sync_on.png b/HEAD/sync_on.png new file mode 100644 index 0000000000000000000000000000000000000000..e08320fb64e6fa33b573005ed6d8fe294e19db76 GIT binary patch literal 845 zcmV-T1G4;yP)Y;xxyHF2B5Wzm| zOOGupOTn@c(JmBOl)e;XMNnZuiTJP>rM8<|Q`7I_))aP?*T)ow&n59{}X4$3Goat zgjs?*aasfbrokzG5cT4K=uG`E14xZl@z)F={P0Y^?$4t z>v!teRnNZym<6h{7sLyF1V0HsfEl+l6TrZpsfr1}luH~F7L}ktXu|*uVX^RG$L0`K zWs3j|0tIvVe(N%_?2{(iCPFGf#B6Hjy6o&}D$A%W%jfO8_W%ZO#-mh}EM$LMn7joJ z05dHr!5Y92g+31l<%i1(=L1a1pXX+OYnalY>31V4K}BjyRe3)9n#;-cCVRD_IG1fT zOKGeNY8q;TL@K{dj@D^scf&VCs*-Jb>8b>|`b*osv52-!A?BpbYtTQBns5EAU**$m zSnVSm(teh>tQi*S*A>#ySc=n;`BHz`DuG4&g4Kf8lLhca+zvZ7t7RflD6-i-mcK=M z!=^P$*u2)bkY5asG4gsss!Hn%u~>}kIW`vMs%lJLH+u*9<4PaV_c6U`KqWXQH%+Nu zTv41O(^ZVi@qhjQdG!fbZw&y+2o!iYymO^?ud3{P*HdoX83YV*Uu_HB=?U&W9%AU# z80}k1SS-CXTU7dcQlsm<^oYLxVSseqY6NO}dc`Nj?8vrhNuCdm@^{a3AQ_>6myOj+ z`1RsLUXF|dm|3k7s2jD(B{rzE>WI2scH8i1;=O5Cc9xB3^aJk%fQjqsu+kH#0=_5a z0nCE8@dbQa-|YIuUVvG0L_IwHMEhOj$Mj4Uq05 X8=0q~qBNan00000NkvXXu0mjfptF>5 literal 0 HcmV?d00001 diff --git a/HEAD/tab_a.png b/HEAD/tab_a.png new file mode 100644 index 0000000000000000000000000000000000000000..3b725c41c5a527a3a3e40097077d0e206a681247 GIT binary patch literal 142 zcmeAS@N?(olHy`uVBq!ia0vp^j6kfy!2~3aiye;!QlXwMjv*C{Z|8b*H5dputLHD# z=<0|*y7z(Vor?d;H&?EG&cXR}?!j-Lm&u1OOI7AIF5&c)RFE;&p0MYK>*Kl@eiymD r@|NpwKX@^z+;{u_Z~trSBfrMKa%3`zocFjEXaR$#tDnm{r-UW|TZ1%4 literal 0 HcmV?d00001 diff --git a/HEAD/tab_ad.png b/HEAD/tab_ad.png new file mode 100644 index 0000000000000000000000000000000000000000..e34850acfc24be58da6d2fd1ccc6b29cc84fe34d GIT binary patch literal 135 zcmeAS@N?(olHy`uVBq!ia0vp^j6kfy!2~3aiye;!QhuH;jv*C{Z|5d*H3V=pKi{In zd2jxLclDRPylmD}^l7{QOtL{vUjO{-WqItb5sQp2h-99b8^^Scr-=2mblCdZuUm?4 jzOJvgvt3{(cjKLW5(A@0qPS@<&}0TrS3j3^P6y&q2{!U5bk+Tso_B!YCpDh>v z{CM*1U8YvQRyBUHt^Ju0W_sq-?;9@_4equ-bavTs=gk796zopr0EBT&m;e9( literal 0 HcmV?d00001 diff --git a/HEAD/tab_s.png b/HEAD/tab_s.png new file mode 100644 index 0000000000000000000000000000000000000000..ab478c95b67371d700a20869f7de1ddd73522d50 GIT binary patch literal 184 zcmeAS@N?(olHy`uVBq!ia0vp^j6kfy!2~3aiye;!QuUrLjv*C{Z|^p8HaRdjTwH7) zC?wLlL}}I{)n%R&r+1}IGmDnq;&J#%V6)9VsYhS`O^BVBQlxOUep0c$RENLq#g8A$ z)z7%K_bI&n@J+X_=x}fJoEKed-$<>=ZI-;YrdjIl`U`uzuDWSP?o#Dmo{%SgM#oan kX~E1%D-|#H#QbHoIja2U-MgvsK&LQxy85}Sb4q9e0Efg%P5=M^ literal 0 HcmV?d00001 diff --git a/HEAD/tab_sd.png b/HEAD/tab_sd.png new file mode 100644 index 0000000000000000000000000000000000000000..757a565ced4730f85c833fb2547d8e199ae68f19 GIT binary patch literal 188 zcmeAS@N?(olHy`uVBq!ia0vp^j6kfy!2~3aiye;!Qq7(&jv*C{Z|_!fH5o7*c=%9% zcILh!EA=pAQKdx-Cdiev=v{eg{8Ht<{e8_NAN~b=)%W>-WDCE0PyDHGemi$BoXwcK z{>e9^za6*c1ilttWw&V+U;WCPlV9{LdC~Ey%_H(qj`xgfES(4Yz5jSTZfCt`4E$0YRsR*S^mTCR^;V&sxC8{l_Cp7w8-YPgg&ebxsLQ00$vXK>z>% literal 0 HcmV?d00001 diff --git a/HEAD/tabs.css b/HEAD/tabs.css new file mode 100644 index 00000000..edbb4241 --- /dev/null +++ b/HEAD/tabs.css @@ -0,0 +1 @@ +.sm{position:relative;z-index:9999}.sm,.sm ul,.sm li{display:block;list-style:none;margin:0;padding:0;line-height:normal;direction:ltr;text-align:left;-webkit-tap-highlight-color:rgba(0,0,0,0)}.sm-rtl,.sm-rtl ul,.sm-rtl li{direction:rtl;text-align:right}.sm>li>h1,.sm>li>h2,.sm>li>h3,.sm>li>h4,.sm>li>h5,.sm>li>h6{margin:0;padding:0}.sm ul{display:none}.sm li,.sm a{position:relative}.sm a{display:block}.sm a.disabled{cursor:not-allowed}.sm:after{content:"\00a0";display:block;height:0;font:0/0 serif;clear:both;visibility:hidden;overflow:hidden}.sm,.sm *,.sm *:before,.sm *:after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.main-menu-btn{position:relative;display:inline-block;width:36px;height:36px;text-indent:36px;margin-left:8px;white-space:nowrap;overflow:hidden;cursor:pointer;-webkit-tap-highlight-color:rgba(0,0,0,0)}.main-menu-btn-icon,.main-menu-btn-icon:before,.main-menu-btn-icon:after{position:absolute;top:50%;left:2px;height:2px;width:24px;background:#364D7C;-webkit-transition:all .25s;transition:all .25s}.main-menu-btn-icon:before{content:'';top:-7px;left:0}.main-menu-btn-icon:after{content:'';top:7px;left:0}#main-menu-state:checked ~ .main-menu-btn .main-menu-btn-icon{height:0}#main-menu-state:checked ~ .main-menu-btn .main-menu-btn-icon:before{top:0;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}#main-menu-state:checked ~ .main-menu-btn .main-menu-btn-icon:after{top:0;-webkit-transform:rotate(45deg);transform:rotate(45deg)}#main-menu-state{position:absolute;width:1px;height:1px;margin:-1px;border:0;padding:0;overflow:hidden;clip:rect(1px,1px,1px,1px)}#main-menu-state:not(:checked) ~ #main-menu{display:none}#main-menu-state:checked ~ #main-menu{display:block}@media(min-width:768px){.main-menu-btn{position:absolute;top:-99999px}#main-menu-state:not(:checked) ~ #main-menu{display:block}}.sm-dox{background-image:url('tab_b.png')}.sm-dox a,.sm-dox a:focus,.sm-dox a:hover,.sm-dox a:active{padding:0 12px;padding-right:43px;font-family:'Lucida Grande',Geneva,Helvetica,Arial,sans-serif;font-size:13px;font-weight:bold;line-height:36px;text-decoration:none;text-shadow:0px 1px 1px rgba(255, 255, 255, 0.9);color:#283A5D;outline:0}.sm-dox a:hover{background-image:url('tab_a.png');background-repeat:repeat-x;color:white;text-shadow:0px 1px 1px rgba(0, 0, 0, 1.0)}.sm-dox a.current{color:#d23600}.sm-dox a.disabled{color:#bbb}.sm-dox a span.sub-arrow{position:absolute;top:50%;margin-top:-14px;left:auto;right:3px;width:28px;height:28px;overflow:hidden;font:bold 12px/28px monospace !important;text-align:center;text-shadow:none;background:rgba(255, 255, 255, 0.5);-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.sm-dox a span.sub-arrow:before{display:block;content:'+'}.sm-dox a.highlighted span.sub-arrow:before{display:block;content:'-'}.sm-dox>li:first-child>a,.sm-dox>li:first-child>:not(ul) a{-moz-border-radius:5px 5px 0 0;-webkit-border-radius:5px;border-radius:5px 5px 0 0}.sm-dox>li:last-child>a,.sm-dox>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul{-moz-border-radius:0 0 5px 5px;-webkit-border-radius:0;border-radius:0 0 5px 5px}.sm-dox>li:last-child>a.highlighted,.sm-dox>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a.highlighted{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0}.sm-dox ul{background:white}.sm-dox ul a,.sm-dox ul a:focus,.sm-dox ul a:hover,.sm-dox ul a:active{font-size:12px;border-left:8px solid transparent;line-height:36px;text-shadow:none;background-color:white;background-image:none}.sm-dox ul a:hover{background-image:url('tab_a.png');background-repeat:repeat-x;color:white;text-shadow:0 1px 1px black}.sm-dox ul ul a,.sm-dox ul ul a:hover,.sm-dox ul ul a:focus,.sm-dox ul ul a:active{border-left:16px solid transparent}.sm-dox ul ul ul a,.sm-dox ul ul ul a:hover,.sm-dox ul ul ul a:focus,.sm-dox ul ul ul a:active{border-left:24px solid transparent}.sm-dox ul ul ul ul a,.sm-dox ul ul ul ul a:hover,.sm-dox ul ul ul ul a:focus,.sm-dox ul ul ul ul a:active{border-left:32px solid transparent}.sm-dox ul ul ul ul ul a,.sm-dox ul ul ul ul ul a:hover,.sm-dox ul ul ul ul ul a:focus,.sm-dox ul ul ul ul ul a:active{border-left:40px solid transparent}@media(min-width:768px){.sm-dox ul{position:absolute;width:12em}.sm-dox li{float:left}.sm-dox.sm-rtl li{float:right}.sm-dox ul li,.sm-dox.sm-rtl ul li,.sm-dox.sm-vertical li{float:none}.sm-dox a{white-space:nowrap}.sm-dox ul a,.sm-dox.sm-vertical a{white-space:normal}.sm-dox .sm-nowrap>li>a,.sm-dox .sm-nowrap>li>:not(ul) a{white-space:nowrap}.sm-dox{padding:0 10px;background-image:url('tab_b.png');line-height:36px}.sm-dox a span.sub-arrow{top:50%;margin-top:-2px;right:12px;width:0;height:0;border-width:4px;border-style:solid dashed dashed dashed;border-color:#283A5D transparent transparent transparent;background:transparent;-moz-border-radius:0;-webkit-border-radius:0;border-radius:0}.sm-dox a,.sm-dox a:focus,.sm-dox a:active,.sm-dox a:hover,.sm-dox a.highlighted{padding:0 12px;background-image:url('tab_s.png');background-repeat:no-repeat;background-position:right;-moz-border-radius:0 !important;-webkit-border-radius:0;border-radius:0 !important}.sm-dox a:hover{background-image:url('tab_a.png');background-repeat:repeat-x;color:white;text-shadow:0px 1px 1px rgba(0, 0, 0, 1.0)}.sm-dox a:hover span.sub-arrow{border-color:white transparent transparent transparent}.sm-dox a.has-submenu{padding-right:24px}.sm-dox li{border-top:0}.sm-dox>li>ul:before,.sm-dox>li>ul:after{content:'';position:absolute;top:-18px;left:30px;width:0;height:0;overflow:hidden;border-width:9px;border-style:dashed dashed solid dashed;border-color:transparent transparent #bbb transparent}.sm-dox>li>ul:after{top:-16px;left:31px;border-width:8px;border-color:transparent transparent white transparent}.sm-dox ul{border:1px solid #bbb;padding:5px 0;background:white;-moz-border-radius:5px !important;-webkit-border-radius:5px;border-radius:5px !important;-moz-box-shadow:0 5px 9px rgba(0,0,0,0.2);-webkit-box-shadow:0 5px 9px rgba(0,0,0,0.2);box-shadow:0 5px 9px rgba(0,0,0,0.2)}.sm-dox ul a span.sub-arrow{right:8px;top:50%;margin-top:-5px;border-width:5px;border-color:transparent transparent transparent #555555;border-style:dashed dashed dashed solid}.sm-dox ul a,.sm-dox ul a:hover,.sm-dox ul a:focus,.sm-dox ul a:active,.sm-dox ul a.highlighted{color:#555555;background-image:none;border:0 !important}.sm-dox ul a:hover{background-image:url('tab_a.png');background-repeat:repeat-x;color:white;text-shadow:0px 1px 1px rgba(0, 0, 0, 1.0)}.sm-dox ul a:hover span.sub-arrow{border-color:transparent transparent transparent white}.sm-dox span.scroll-up,.sm-dox span.scroll-down{position:absolute;display:none;visibility:hidden;overflow:hidden;background:white;height:36px}.sm-dox span.scroll-up:hover,.sm-dox span.scroll-down:hover{background:#eee}.sm-dox span.scroll-up:hover span.scroll-up-arrow,.sm-dox span.scroll-up:hover span.scroll-down-arrow{border-color:transparent transparent #d23600 transparent}.sm-dox span.scroll-down:hover span.scroll-down-arrow{border-color:#d23600 transparent transparent transparent}.sm-dox span.scroll-up-arrow,.sm-dox span.scroll-down-arrow{position:absolute;top:0;left:50%;margin-left:-6px;width:0;height:0;overflow:hidden;border-width:6px;border-style:dashed dashed solid dashed;border-color:transparent transparent #555555 transparent}.sm-dox span.scroll-down-arrow{top:8px;border-style:solid dashed dashed dashed;border-color:#555555 transparent transparent transparent}.sm-dox.sm-rtl a.has-submenu{padding-right:12px;padding-left:24px}.sm-dox.sm-rtl a span.sub-arrow{right:auto;left:12px}.sm-dox.sm-rtl.sm-vertical a.has-submenu{padding:10px 20px}.sm-dox.sm-rtl.sm-vertical a span.sub-arrow{right:auto;left:8px;border-style:dashed solid dashed dashed;border-color:transparent #555 transparent transparent}.sm-dox.sm-rtl>li>ul:before{left:auto;right:30px}.sm-dox.sm-rtl>li>ul:after{left:auto;right:31px}.sm-dox.sm-rtl ul a.has-submenu{padding:10px 20px !important}.sm-dox.sm-rtl ul a span.sub-arrow{right:auto;left:8px;border-style:dashed solid dashed dashed;border-color:transparent #555 transparent transparent}.sm-dox.sm-vertical{padding:10px 0;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.sm-dox.sm-vertical a{padding:10px 20px}.sm-dox.sm-vertical a:hover,.sm-dox.sm-vertical a:focus,.sm-dox.sm-vertical a:active,.sm-dox.sm-vertical a.highlighted{background:#fff}.sm-dox.sm-vertical a.disabled{background-image:url('tab_b.png')}.sm-dox.sm-vertical a span.sub-arrow{right:8px;top:50%;margin-top:-5px;border-width:5px;border-style:dashed dashed dashed solid;border-color:transparent transparent transparent #555}.sm-dox.sm-vertical>li>ul:before,.sm-dox.sm-vertical>li>ul:after{display:none}.sm-dox.sm-vertical ul a{padding:10px 20px}.sm-dox.sm-vertical ul a:hover,.sm-dox.sm-vertical ul a:focus,.sm-dox.sm-vertical ul a:active,.sm-dox.sm-vertical ul a.highlighted{background:#eee}.sm-dox.sm-vertical ul a.disabled{background:white}} diff --git a/HEAD/todo.html b/HEAD/todo.html new file mode 100644 index 00000000..a1abd761 --- /dev/null +++ b/HEAD/todo.html @@ -0,0 +1,104 @@ + + + + + + + + +LibJWT: Todo List + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + +
+
LibJWT 2.9.99 +
+
The C JSON Web Token Library +JWK +JWKS
+
+
+ + + +
+
+ +
+
+
+ +
+
+
Todo List
+
+
+
+
Global jwt_valid_exception_t
+

📄 RFC-7519 Sec 4.1.6 "iat" Issued At

+

+

📄 RFC-7519 Sec 4.1.7 "jti" JWT ID

+
+
+
+
+
+ + + + + diff --git a/HEAD/topics.html b/HEAD/topics.html new file mode 100644 index 00000000..2b099eef --- /dev/null +++ b/HEAD/topics.html @@ -0,0 +1,114 @@ + + + + + + + + +LibJWT: Topics + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + +
+
LibJWT 2.9.99 +
+
The C JSON Web Token Library +JWK +JWKS
+
+
+ + + +
+
+ +
+
+
+ + + + + + + diff --git a/HEAD/topics.js b/HEAD/topics.js new file mode 100644 index 00000000..bd01e9b0 --- /dev/null +++ b/HEAD/topics.js @@ -0,0 +1,6 @@ +var topics = +[ + [ "JSON Web Token", "group__jwt__grp.html", "group__jwt__grp" ], + [ "JSON Web Key and Sets", "group__jwks__core__grp.html", "group__jwks__core__grp" ], + [ "Advanced Functionality", "group__jwt__advanced__grp.html", "group__jwt__advanced__grp" ] +]; \ No newline at end of file diff --git a/INSTALL b/INSTALL deleted file mode 100644 index 20998407..00000000 --- a/INSTALL +++ /dev/null @@ -1,370 +0,0 @@ -Installation Instructions -************************* - -Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation, -Inc. - - Copying and distribution of this file, with or without modification, -are permitted in any medium without royalty provided the copyright -notice and this notice are preserved. This file is offered as-is, -without warranty of any kind. - -Basic Installation -================== - - Briefly, the shell command `./configure && make && make install' -should configure, build, and install this package. The following -more-detailed instructions are generic; see the `README' file for -instructions specific to this package. Some packages provide this -`INSTALL' file but do not implement all of the features documented -below. The lack of an optional feature in a given package is not -necessarily a bug. More recommendations for GNU packages can be found -in *note Makefile Conventions: (standards)Makefile Conventions. - - The `configure' shell script attempts to guess correct values for -various system-dependent variables used during compilation. It uses -those values to create a `Makefile' in each directory of the package. -It may also create one or more `.h' files containing system-dependent -definitions. Finally, it creates a shell script `config.status' that -you can run in the future to recreate the current configuration, and a -file `config.log' containing compiler output (useful mainly for -debugging `configure'). - - It can also use an optional file (typically called `config.cache' -and enabled with `--cache-file=config.cache' or simply `-C') that saves -the results of its tests to speed up reconfiguring. Caching is -disabled by default to prevent problems with accidental use of stale -cache files. - - If you need to do unusual things to compile the package, please try -to figure out how `configure' could check whether to do them, and mail -diffs or instructions to the address given in the `README' so they can -be considered for the next release. If you are using the cache, and at -some point `config.cache' contains results you don't want to keep, you -may remove or edit it. - - The file `configure.ac' (or `configure.in') is used to create -`configure' by a program called `autoconf'. You need `configure.ac' if -you want to change it or regenerate `configure' using a newer version -of `autoconf'. - - The simplest way to compile this package is: - - 1. `cd' to the directory containing the package's source code and type - `./configure' to configure the package for your system. - - Running `configure' might take a while. While running, it prints - some messages telling which features it is checking for. - - 2. Type `make' to compile the package. - - 3. Optionally, type `make check' to run any self-tests that come with - the package, generally using the just-built uninstalled binaries. - - 4. Type `make install' to install the programs and any data files and - documentation. When installing into a prefix owned by root, it is - recommended that the package be configured and built as a regular - user, and only the `make install' phase executed with root - privileges. - - 5. Optionally, type `make installcheck' to repeat any self-tests, but - this time using the binaries in their final installed location. - This target does not install anything. Running this target as a - regular user, particularly if the prior `make install' required - root privileges, verifies that the installation completed - correctly. - - 6. You can remove the program binaries and object files from the - source code directory by typing `make clean'. To also remove the - files that `configure' created (so you can compile the package for - a different kind of computer), type `make distclean'. There is - also a `make maintainer-clean' target, but that is intended mainly - for the package's developers. If you use it, you may have to get - all sorts of other programs in order to regenerate files that came - with the distribution. - - 7. Often, you can also type `make uninstall' to remove the installed - files again. In practice, not all packages have tested that - uninstallation works correctly, even though it is required by the - GNU Coding Standards. - - 8. Some packages, particularly those that use Automake, provide `make - distcheck', which can by used by developers to test that all other - targets like `make install' and `make uninstall' work correctly. - This target is generally not run by end users. - -Compilers and Options -===================== - - Some systems require unusual options for compilation or linking that -the `configure' script does not know about. Run `./configure --help' -for details on some of the pertinent environment variables. - - You can give `configure' initial values for configuration parameters -by setting variables in the command line or in the environment. Here -is an example: - - ./configure CC=c99 CFLAGS=-g LIBS=-lposix - - *Note Defining Variables::, for more details. - -Compiling For Multiple Architectures -==================================== - - You can compile the package for more than one kind of computer at the -same time, by placing the object files for each architecture in their -own directory. To do this, you can use GNU `make'. `cd' to the -directory where you want the object files and executables to go and run -the `configure' script. `configure' automatically checks for the -source code in the directory that `configure' is in and in `..'. This -is known as a "VPATH" build. - - With a non-GNU `make', it is safer to compile the package for one -architecture at a time in the source code directory. After you have -installed the package for one architecture, use `make distclean' before -reconfiguring for another architecture. - - On MacOS X 10.5 and later systems, you can create libraries and -executables that work on multiple system types--known as "fat" or -"universal" binaries--by specifying multiple `-arch' options to the -compiler but only a single `-arch' option to the preprocessor. Like -this: - - ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ - CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ - CPP="gcc -E" CXXCPP="g++ -E" - - This is not guaranteed to produce working output in all cases, you -may have to build one architecture at a time and combine the results -using the `lipo' tool if you have problems. - -Installation Names -================== - - By default, `make install' installs the package's commands under -`/usr/local/bin', include files under `/usr/local/include', etc. You -can specify an installation prefix other than `/usr/local' by giving -`configure' the option `--prefix=PREFIX', where PREFIX must be an -absolute file name. - - You can specify separate installation prefixes for -architecture-specific files and architecture-independent files. If you -pass the option `--exec-prefix=PREFIX' to `configure', the package uses -PREFIX as the prefix for installing programs and libraries. -Documentation and other data files still use the regular prefix. - - In addition, if you use an unusual directory layout you can give -options like `--bindir=DIR' to specify different values for particular -kinds of files. Run `configure --help' for a list of the directories -you can set and what kinds of files go in them. In general, the -default for these options is expressed in terms of `${prefix}', so that -specifying just `--prefix' will affect all of the other directory -specifications that were not explicitly provided. - - The most portable way to affect installation locations is to pass the -correct locations to `configure'; however, many packages provide one or -both of the following shortcuts of passing variable assignments to the -`make install' command line to change installation locations without -having to reconfigure or recompile. - - The first method involves providing an override variable for each -affected directory. For example, `make install -prefix=/alternate/directory' will choose an alternate location for all -directory configuration variables that were expressed in terms of -`${prefix}'. Any directories that were specified during `configure', -but not in terms of `${prefix}', must each be overridden at install -time for the entire installation to be relocated. The approach of -makefile variable overrides for each directory variable is required by -the GNU Coding Standards, and ideally causes no recompilation. -However, some platforms have known limitations with the semantics of -shared libraries that end up requiring recompilation when using this -method, particularly noticeable in packages that use GNU Libtool. - - The second method involves providing the `DESTDIR' variable. For -example, `make install DESTDIR=/alternate/directory' will prepend -`/alternate/directory' before all installation names. The approach of -`DESTDIR' overrides is not required by the GNU Coding Standards, and -does not work on platforms that have drive letters. On the other hand, -it does better at avoiding recompilation issues, and works well even -when some directory options were not specified in terms of `${prefix}' -at `configure' time. - -Optional Features -================= - - If the package supports it, you can cause programs to be installed -with an extra prefix or suffix on their names by giving `configure' the -option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. - - Some packages pay attention to `--enable-FEATURE' options to -`configure', where FEATURE indicates an optional part of the package. -They may also pay attention to `--with-PACKAGE' options, where PACKAGE -is something like `gnu-as' or `x' (for the X Window System). The -`README' should mention any `--enable-' and `--with-' options that the -package recognizes. - - For packages that use the X Window System, `configure' can usually -find the X include and library files automatically, but if it doesn't, -you can use the `configure' options `--x-includes=DIR' and -`--x-libraries=DIR' to specify their locations. - - Some packages offer the ability to configure how verbose the -execution of `make' will be. For these packages, running `./configure ---enable-silent-rules' sets the default to minimal output, which can be -overridden with `make V=1'; while running `./configure ---disable-silent-rules' sets the default to verbose, which can be -overridden with `make V=0'. - -Particular systems -================== - - On HP-UX, the default C compiler is not ANSI C compatible. If GNU -CC is not installed, it is recommended to use the following options in -order to use an ANSI C compiler: - - ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" - -and if that doesn't work, install pre-built binaries of GCC for HP-UX. - - HP-UX `make' updates targets which have the same time stamps as -their prerequisites, which makes it generally unusable when shipped -generated files such as `configure' are involved. Use GNU `make' -instead. - - On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot -parse its `' header file. The option `-nodtk' can be used as -a workaround. If GNU CC is not installed, it is therefore recommended -to try - - ./configure CC="cc" - -and if that doesn't work, try - - ./configure CC="cc -nodtk" - - On Solaris, don't put `/usr/ucb' early in your `PATH'. This -directory contains several dysfunctional programs; working variants of -these programs are available in `/usr/bin'. So, if you need `/usr/ucb' -in your `PATH', put it _after_ `/usr/bin'. - - On Haiku, software installed for all users goes in `/boot/common', -not `/usr/local'. It is recommended to use the following options: - - ./configure --prefix=/boot/common - -Specifying the System Type -========================== - - There may be some features `configure' cannot figure out -automatically, but needs to determine by the type of machine the package -will run on. Usually, assuming the package is built to be run on the -_same_ architectures, `configure' can figure that out, but if it prints -a message saying it cannot guess the machine type, give it the -`--build=TYPE' option. TYPE can either be a short name for the system -type, such as `sun4', or a canonical name which has the form: - - CPU-COMPANY-SYSTEM - -where SYSTEM can have one of these forms: - - OS - KERNEL-OS - - See the file `config.sub' for the possible values of each field. If -`config.sub' isn't included in this package, then this package doesn't -need to know the machine type. - - If you are _building_ compiler tools for cross-compiling, you should -use the option `--target=TYPE' to select the type of system they will -produce code for. - - If you want to _use_ a cross compiler, that generates code for a -platform different from the build platform, you should specify the -"host" platform (i.e., that on which the generated programs will -eventually be run) with `--host=TYPE'. - -Sharing Defaults -================ - - If you want to set default values for `configure' scripts to share, -you can create a site shell script called `config.site' that gives -default values for variables like `CC', `cache_file', and `prefix'. -`configure' looks for `PREFIX/share/config.site' if it exists, then -`PREFIX/etc/config.site' if it exists. Or, you can set the -`CONFIG_SITE' environment variable to the location of the site script. -A warning: not all `configure' scripts look for a site script. - -Defining Variables -================== - - Variables not defined in a site shell script can be set in the -environment passed to `configure'. However, some packages may run -configure again during the build, and the customized values of these -variables may be lost. In order to avoid this problem, you should set -them in the `configure' command line, using `VAR=value'. For example: - - ./configure CC=/usr/local2/bin/gcc - -causes the specified `gcc' to be used as the C compiler (unless it is -overridden in the site shell script). - -Unfortunately, this technique does not work for `CONFIG_SHELL' due to -an Autoconf limitation. Until the limitation is lifted, you can use -this workaround: - - CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash - -`configure' Invocation -====================== - - `configure' recognizes the following options to control how it -operates. - -`--help' -`-h' - Print a summary of all of the options to `configure', and exit. - -`--help=short' -`--help=recursive' - Print a summary of the options unique to this package's - `configure', and exit. The `short' variant lists options used - only in the top level, while the `recursive' variant lists options - also present in any nested packages. - -`--version' -`-V' - Print the version of Autoconf used to generate the `configure' - script, and exit. - -`--cache-file=FILE' - Enable the cache: use and save the results of the tests in FILE, - traditionally `config.cache'. FILE defaults to `/dev/null' to - disable caching. - -`--config-cache' -`-C' - Alias for `--cache-file=config.cache'. - -`--quiet' -`--silent' -`-q' - Do not print messages saying which checks are being made. To - suppress all normal output, redirect it to `/dev/null' (any error - messages will still be shown). - -`--srcdir=DIR' - Look for the package's source code in directory DIR. Usually - `configure' can determine that directory automatically. - -`--prefix=DIR' - Use DIR as the installation prefix. *note Installation Names:: - for more details, including other options available for fine-tuning - the installation locations. - -`--no-create' -`-n' - Run the configure checks, but stop before creating any output - files. - -`configure' also accepts some other, not widely useful, options. Run -`configure --help' for more details. diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..d0a1fa14 --- /dev/null +++ b/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at https://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/Makefile.am b/Makefile.am index ec8c4b96..e3420c1f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,3 +1,13 @@ ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = include libjwt +EXTRA_DIST = README.md README.cmake.md CMakeLists.txt cmake images + +SUBDIRS = include libjwt tests dist +if BUILD_EXAMPLES +SUBDIRS += examples +endif + +include $(top_srcdir)/doxygen.mk + +check-valgrind check-code-coverage: all + $(MAKE) $(AM_MAKEFLAGS) -C tests $@ diff --git a/NEWS b/NEWS deleted file mode 100644 index b7a3d4a2..00000000 --- a/NEWS +++ /dev/null @@ -1 +0,0 @@ -Please see http://github.com/benmcollins/jwt diff --git a/README b/README deleted file mode 100644 index 890d7c32..00000000 --- a/README +++ /dev/null @@ -1 +0,0 @@ -JWT C Library diff --git a/README.cmake.md b/README.cmake.md new file mode 100644 index 00000000..a2de3a12 --- /dev/null +++ b/README.cmake.md @@ -0,0 +1,44 @@ +Build Instructions for CMake +============================ + +1. Generate build tree. + + mkdir out + cd out + cmake -G "Ninja" -DBUILD_TESTS=1 .. + + NOTE: If you prefer, substitute "Unix Makefiles" or other cmake build generator. + +2. Build all. + + ninja + + NOTE: If using different build tool, `cmake --build .` will work. + +3. Run tests. + + ninja check + + NOTE: If using different build tool, `cmake --build . --target check` will work. + +Examples +-------- + +Two example utilities are provided to demonstrate generating JWT and for +authenticating JWT, jwtgen and jwtauth. + +1. Generate a token using RS256 signature with the sample private key. + + cd out/examples + ./jwtgen -k ../../tests/keys/rsa_key_2048.pem -a RS256 -c iss=example.com -c sub=user0 > user0.jwt + +2. Authenticate a token using RS256 signature with the sample public key. + + cd out/examples + ./jwtauth -k ../../tests/keys/rsa_key_2048-pub.pem --alg RS256 user0.jwt + +3. Authenticate a token using RS256 signature with the sample public key, verifying user is user1. + + cd out/examples + ./jwtauth -k ../../tests/keys/rsa_key_2048-pub.pem --alg RS256 -c sub=user1 user0.jwt + diff --git a/README.md b/README.md new file mode 100644 index 00000000..efef1107 --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +![LibJWT Logo](https://user-images.githubusercontent.com/320303/33439880-82406da4-d5bc-11e7-8959-6d53553c1984.png) + +# JWT C Library + +[![Build Status](https://app.travis-ci.com/benmcollins/libjwt.svg?branch=master)](https://app.travis-ci.com/github/benmcollins/libjwt) [![codecov](https://codecov.io/gh/benmcollins/libjwt/graph/badge.svg?token=MhCaZ8cpwQ)](https://codecov.io/gh/benmcollins/libjwt) + +[![View on JWT.IO](http://jwt.io/img/badge.svg)](https://jwt.io) + +## Build Requirements + +- https://github.com/akheron/jansson +- OpenSSL or GnuTLS + +## Documentation + +[GitHub Pages](https://benmcollins.github.io/libjwt/) + +## Pre-built Packages + +LibJWT is available in most Linux distributions as well as through [Homebrew](https://brew.sh/) +for macOS and Windows. + +## Build Instructions + +**With GNU Make:** Use ``autoreconf -i`` to create project files and run ``./configure``. +- ``make all``: build library. +- ``make check``: build and run test suite. +- See INSTALL file for more details on GNU Auto tools and GNU Make. +- By default, it will build with OpenSSL and GnuTLS support if found. You can + exclude or force either one by using the ``--with-`` and ``--without-`` flags + to ``./configure``. diff --git a/annotated.html b/annotated.html new file mode 100644 index 00000000..fe3d9c5d --- /dev/null +++ b/annotated.html @@ -0,0 +1,86 @@ + + + + + + + +libjwt-2.1.1: Data Structures + + + + + + + + + + + + +
+
+ + + + + + +
+
libjwt-2.1.1 +
+
+
+ + + + + + + +
+
+ +
+
+
+ +
+
+
Data Structures
+
+
+
Here are the data structures with brief descriptions:
+ + +
 Cjwt_key_tStructure used by key provider to return a key
+
+
+
+ + + + diff --git a/annotated_dup.js b/annotated_dup.js new file mode 100644 index 00000000..c22993f7 --- /dev/null +++ b/annotated_dup.js @@ -0,0 +1,4 @@ +var annotated_dup = +[ + [ "jwt_key_t", "structjwt__key__t.html", "structjwt__key__t" ] +]; \ No newline at end of file diff --git a/bc_s.png b/bc_s.png new file mode 100644 index 0000000000000000000000000000000000000000..224b29aa9847d5a4b3902efd602b7ddf7d33e6c2 GIT binary patch literal 676 zcmV;V0$crwP)y__>=_9%My z{n931IS})GlGUF8K#6VIbs%684A^L3@%PlP2>_sk`UWPq@f;rU*V%rPy_ekbhXT&s z(GN{DxFv}*vZp`F>S!r||M`I*nOwwKX+BC~3P5N3-)Y{65c;ywYiAh-1*hZcToLHK ztpl1xomJ+Yb}K(cfbJr2=GNOnT!UFA7Vy~fBz8?J>XHsbZoDad^8PxfSa0GDgENZS zuLCEqzb*xWX2CG*b&5IiO#NzrW*;`VC9455M`o1NBh+(k8~`XCEEoC1Ybwf;vr4K3 zg|EB<07?SOqHp9DhLpS&bzgo70I+ghB_#)K7H%AMU3v}xuyQq9&Bm~++VYhF09a+U zl7>n7Jjm$K#b*FONz~fj;I->Bf;ule1prFN9FovcDGBkpg>)O*-}eLnC{6oZHZ$o% zXKW$;0_{8hxHQ>l;_*HATI(`7t#^{$(zLe}h*mqwOc*nRY9=?Sx4OOeVIfI|0V(V2 zBrW#G7Ss9wvzr@>H*`r>zE z+e8bOBgqIgldUJlG(YUDviMB`9+DH8n-s9SXRLyJHO1!=wY^79WYZMTa(wiZ!zP66 zA~!21vmF3H2{ngD;+`6j#~6j;$*f*G_2ZD1E;9(yaw7d-QnSCpK(cR1zU3qU0000< KMNUMnLSTYoA~SLT literal 0 HcmV?d00001 diff --git a/bc_sd.png b/bc_sd.png new file mode 100644 index 0000000000000000000000000000000000000000..31ca888dc71049713b35c351933a8d0f36180bf1 GIT binary patch literal 635 zcmV->0)+jEP)Jwi0r1~gdSq#w{Bu1q z`craw(p2!hu$4C_$Oc3X(sI6e=9QSTwPt{G) z=htT&^~&c~L2~e{r5_5SYe7#Is-$ln>~Kd%$F#tC65?{LvQ}8O`A~RBB0N~`2M+waajO;5>3B&-viHGJeEK2TQOiPRa zfDKyqwMc4wfaEh4jt>H`nW_Zidwk@Bowp`}(VUaj-pSI(-1L>FJVsX}Yl9~JsqgsZ zUD9(rMwf23Gez6KPa|wwInZodP-2}9@fK0Ga_9{8SOjU&4l`pH4@qlQp83>>HT$xW zER^U>)MyV%t(Lu=`d=Y?{k1@}&r7ZGkFQ%z%N+sE9BtYjovzxyxCPxN6&@wLK{soQ zSmkj$aLI}miuE^p@~4}mg9OjDfGEkgY4~^XzLRUBB*O{+&vq<3v(E%+k_i%=`~j%{ Vj14gnt9}3g002ovPDHLkV1n!oC4m3{ literal 0 HcmV?d00001 diff --git a/classes.html b/classes.html new file mode 100644 index 00000000..c6db1005 --- /dev/null +++ b/classes.html @@ -0,0 +1,87 @@ + + + + + + + +libjwt-2.1.1: Data Structure Index + + + + + + + + + + + + +
+
+ + + + + + +
+
libjwt-2.1.1 +
+
+
+ + + + + + + +
+
+ +
+
+
+ +
+
+
Data Structure Index
+
+
+ +
+
+
J
+
jwt_key_t
+
+
+
+ + + + diff --git a/clipboard.js b/clipboard.js new file mode 100644 index 00000000..42c1fb0e --- /dev/null +++ b/clipboard.js @@ -0,0 +1,61 @@ +/** + +The code below is based on the Doxygen Awesome project, see +https://github.com/jothepro/doxygen-awesome-css + +MIT License + +Copyright (c) 2021 - 2022 jothepro + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +let clipboard_title = "Copy to clipboard" +let clipboard_icon = `` +let clipboard_successIcon = `` +let clipboard_successDuration = 1000 + +$(function() { + if(navigator.clipboard) { + const fragments = document.getElementsByClassName("fragment") + for(const fragment of fragments) { + const clipboard_div = document.createElement("div") + clipboard_div.classList.add("clipboard") + clipboard_div.innerHTML = clipboard_icon + clipboard_div.title = clipboard_title + $(clipboard_div).click(function() { + const content = this.parentNode.cloneNode(true) + // filter out line number and folded fragments from file listings + content.querySelectorAll(".lineno, .ttc, .foldclosed").forEach((node) => { node.remove() }) + let text = content.textContent + // remove trailing newlines and trailing spaces from empty lines + text = text.replace(/^\s*\n/gm,'\n').replace(/\n*$/,'') + navigator.clipboard.writeText(text); + this.classList.add("success") + this.innerHTML = clipboard_successIcon + window.setTimeout(() => { // switch back to normal icon after timeout + this.classList.remove("success") + this.innerHTML = clipboard_icon + }, clipboard_successDuration); + }) + fragment.insertBefore(clipboard_div, fragment.firstChild) + } + } +}) diff --git a/closed.png b/closed.png new file mode 100644 index 0000000000000000000000000000000000000000..98cc2c909da37a6df914fbf67780eebd99c597f5 GIT binary patch literal 132 zcmeAS@N?(olHy`uVBq!ia0vp^oFL4>1|%O$WD@{V-kvUwAr*{o@8{^CZMh(5KoB^r_<4^zF@3)Cp&&t3hdujKf f*?bjBoY!V+E))@{xMcbjXe@)LtDnm{r-UW|*e5JT literal 0 HcmV?d00001 diff --git a/cmake/FindCheck.cmake b/cmake/FindCheck.cmake new file mode 100644 index 00000000..36f9b487 --- /dev/null +++ b/cmake/FindCheck.cmake @@ -0,0 +1,56 @@ +# - Try to find the CHECK libraries +# Once done this will define +# +# CHECK_FOUND - system has check +# CHECK_INCLUDE_DIR - the check include directory +# CHECK_LIBRARIES - check library +# +# This configuration file for finding libcheck is originally from +# the opensync project. The originally was downloaded from here: +# opensync.org/browser/branches/3rd-party-cmake-modules/modules/FindCheck.cmake +# +# Copyright (c) 2007 Daniel Gollub +# Copyright (c) 2007 Bjoern Ricks +# +# Redistribution and use is allowed according to the terms of the New +# BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + + +INCLUDE( FindPkgConfig ) + +# Take care about check.pc settings +PKG_SEARCH_MODULE( CHECK check ) + +# Look for CHECK include dir and libraries +IF( NOT CHECK_FOUND ) + IF ( CHECK_INSTALL_DIR ) + MESSAGE ( STATUS "Using override CHECK_INSTALL_DIR to find check" ) + SET ( CHECK_INCLUDE_DIR "${CHECK_INSTALL_DIR}/include" ) + SET ( CHECK_INCLUDE_DIRS "${CHECK_INCLUDE_DIR}" ) + FIND_LIBRARY( CHECK_LIBRARY NAMES check PATHS "${CHECK_INSTALL_DIR}/lib" ) + FIND_LIBRARY( COMPAT_LIBRARY NAMES compat PATHS "${CHECK_INSTALL_DIR}/lib" ) + SET ( CHECK_LIBRARIES "${CHECK_LIBRARY}" "${COMPAT_LIBRARY}" ) + ELSE ( CHECK_INSTALL_DIR ) + FIND_PATH( CHECK_INCLUDE_DIR check.h ) + FIND_LIBRARY( CHECK_LIBRARIES NAMES check ) + ENDIF ( CHECK_INSTALL_DIR ) + + IF ( CHECK_INCLUDE_DIR AND CHECK_LIBRARIES ) + SET( CHECK_FOUND 1 ) + IF ( NOT Check_FIND_QUIETLY ) + MESSAGE ( STATUS "Found CHECK: ${CHECK_LIBRARIES}" ) + ENDIF ( NOT Check_FIND_QUIETLY ) + ELSE ( CHECK_INCLUDE_DIR AND CHECK_LIBRARIES ) + IF ( Check_FIND_REQUIRED ) + MESSAGE( FATAL_ERROR "Could NOT find CHECK" ) + ELSE ( Check_FIND_REQUIRED ) + IF ( NOT Check_FIND_QUIETLY ) + MESSAGE( STATUS "Could NOT find CHECK" ) + ENDIF ( NOT Check_FIND_QUIETLY ) + ENDIF ( Check_FIND_REQUIRED ) + ENDIF ( CHECK_INCLUDE_DIR AND CHECK_LIBRARIES ) +ENDIF( NOT CHECK_FOUND ) + +# Hide advanced variables from CMake GUIs +MARK_AS_ADVANCED( CHECK_INCLUDE_DIR CHECK_LIBRARIES ) diff --git a/cmake/libjwtConfig.cmake.in b/cmake/libjwtConfig.cmake.in new file mode 100644 index 00000000..4844ea9a --- /dev/null +++ b/cmake/libjwtConfig.cmake.in @@ -0,0 +1,4 @@ +set(LIBJWT_VERSION @LIBJWT_VERSION@) +@PACKAGE_INIT@ +set_and_check(LIBJWT_INCLUDE_DIR "@PACKAGE_INCLUDE_INSTALL_DIR@") +check_required_components(libjwt) diff --git a/configure.ac b/configure.ac index dd4b9fd1..006acb1f 100644 --- a/configure.ac +++ b/configure.ac @@ -1,10 +1,101 @@ AC_PREREQ([2.61]) -AC_INIT([libjwt], [1.0.0], [ben@cyphre.com]) -AM_INIT_AUTOMAKE +AC_INIT([libjwt],[2.1.1],[https://github.com/benmcollins/libjwt/issues]) +AC_LANG([C]) +AM_INIT_AUTOMAKE([foreign subdir-objects]) LT_PREREQ([2.2]) LT_INIT([]) AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_HEADERS([libjwt/config.h]) -AC_CONFIG_FILES([Makefile include/Makefile libjwt/Makefile]) +AC_SUBST([AM_CFLAGS], [-Wall]) + +AC_ARG_WITH([gnutls], + [AS_HELP_STRING([--with-gnutls], + [Support GnuTLS for crypto operations @<:@default=check@:>@])], + [], + [with_gnutls=check]) + +AC_ARG_WITH([openssl], + [AS_HELP_STRING([--with-openssl], + [Support OpenSSL for crypto operations @<:@default=check@:>@])], + [], + [with_openssl=check]) + + +AS_IF([test "x$with_gnutls" != xno], + [PKG_CHECK_MODULES([GNUTLS], [gnutls >= 3.6.0], + [AM_CONDITIONAL([HAVE_GNUTLS], [true])] + [AC_DEFINE([HAVE_GNUTLS], [1], [GnuTLS Support])] + [HAVE_CRYPTO=true], + AS_IF([test "x$with_gnutls" != xcheck], + AC_MSG_FAILURE([--with-gnutls was given but GnuTLS not found]) + ))], [AM_CONDITIONAL([HAVE_GNUTLS], [false])]) + +AS_IF([test "x$with_openssl" != xno], + [PKG_CHECK_MODULES([OPENSSL], [openssl >= 0.9.8], + [AM_CONDITIONAL([HAVE_OPENSSL], [true])] + [AC_DEFINE([HAVE_OPENSSL], [1], [OpenSSL Support])] + [HAVE_CRYPTO=true], + AS_IF([test "x$with_openssl" != xcheck], + AC_MSG_FAILURE([--with-openssl was given but OpenSSL not found]) + ))], [AM_CONDITIONAL([HAVE_OPENSSL], [false])]) + +AS_IF([test -z "$HAVE_CRYPTO"], + [AC_MSG_ERROR([no crypto library detected], [1])], []) + +AC_ARG_WITH([examples], + AS_HELP_STRING([--without-examples], [do not build example programs]), + [], + [with_examples=yes]) + +AM_CONDITIONAL([BUILD_EXAMPLES], [test "x$with_examples" != "xno"]) + +PKG_CHECK_MODULES([JANSSON], [jansson >= 2.0]) +PKG_CHECK_MODULES([CHECK], [check >= 0.9.4], [true], [true]) + +dnl XXX Gotta remove -lb64 if the test below this fails +AC_SEARCH_LIBS(base64_decode_value, b64, [TEST_B64=1], + AM_CONDITIONAL(HAVE_LIBB64, false)) + +AC_MSG_CHECKING(if b64 library supports chars_per_line) +AC_LINK_IFELSE([AC_LANG_SOURCE([[ + #include + int main(void) { + base64_encodestate state; + state.chars_per_line = 0; + return 0; + ]])], + [AM_CONDITIONAL(HAVE_LIBB64, true)] [AC_MSG_RESULT(yes)] + [AC_DEFINE([HAVE_LIBB64], [1], [Use system libb64])], + [AM_CONDITIONAL(HAVE_LIBB64, false)] [AC_MSG_RESULT(no)]) + +AX_VALGRIND_DFLT([memcheck], [on]) +AX_VALGRIND_DFLT([helgrind], [off]) +AX_VALGRIND_DFLT([drd], [off]) +AX_VALGRIND_DFLT([sgcheck], [off]) +AX_VALGRIND_CHECK + +AX_CODE_COVERAGE + +DX_MAN_FEATURE(ON) +DX_HTML_FEATURE(ON) +DX_CHM_FEATURE(OFF) +DX_CHI_FEATURE(OFF) +DX_RTF_FEATURE(OFF) +DX_XML_FEATURE(OFF) +DX_PDF_FEATURE(OFF) +DX_PS_FEATURE(OFF) +DX_INIT_DOXYGEN($PACKAGE_NAME, doxygen.cfg) + +AC_CONFIG_FILES([ + Makefile + include/Makefile + libjwt/Makefile + examples/Makefile + tests/Makefile + libjwt/libjwt.pc + dist/Makefile + dist/libjwt.spec +]) AC_OUTPUT diff --git a/cookie.js b/cookie.js new file mode 100644 index 00000000..53ad21d9 --- /dev/null +++ b/cookie.js @@ -0,0 +1,58 @@ +/*! + Cookie helper functions + Copyright (c) 2023 Dimitri van Heesch + Released under MIT license. +*/ +let Cookie = { + cookie_namespace: 'doxygen_', + + readSetting(cookie,defVal) { + if (window.chrome) { + const val = localStorage.getItem(this.cookie_namespace+cookie) || + sessionStorage.getItem(this.cookie_namespace+cookie); + if (val) return val; + } else { + let myCookie = this.cookie_namespace+cookie+"="; + if (document.cookie) { + const index = document.cookie.indexOf(myCookie); + if (index != -1) { + const valStart = index + myCookie.length; + let valEnd = document.cookie.indexOf(";", valStart); + if (valEnd == -1) { + valEnd = document.cookie.length; + } + return document.cookie.substring(valStart, valEnd); + } + } + } + return defVal; + }, + + writeSetting(cookie,val,days=10*365) { // default days='forever', 0=session cookie, -1=delete + if (window.chrome) { + if (days==0) { + sessionStorage.setItem(this.cookie_namespace+cookie,val); + } else { + localStorage.setItem(this.cookie_namespace+cookie,val); + } + } else { + let date = new Date(); + date.setTime(date.getTime()+(days*24*60*60*1000)); + const expiration = days!=0 ? "expires="+date.toGMTString()+";" : ""; + document.cookie = this.cookie_namespace + cookie + "=" + + val + "; SameSite=Lax;" + expiration + "path=/"; + } + }, + + eraseSetting(cookie) { + if (window.chrome) { + if (localStorage.getItem(this.cookie_namespace+cookie)) { + localStorage.removeItem(this.cookie_namespace+cookie); + } else if (sessionStorage.getItem(this.cookie_namespace+cookie)) { + sessionStorage.removeItem(this.cookie_namespace+cookie); + } + } else { + this.writeSetting(cookie,'',-1); + } + }, +} diff --git a/dist/Makefile.am b/dist/Makefile.am new file mode 100644 index 00000000..8d98b51d --- /dev/null +++ b/dist/Makefile.am @@ -0,0 +1,16 @@ +VERSION=@VERSION@ + +CLEANFILES=libjwt.spec + +all: + +rpmcheck: + rpmlint -i dist/libjwt.spec + +rpm-prep: + cd ${abs_top_srcdir} && mkdir -p ./rpmbuild + cd ${abs_top_srcdir}/rpmbuild && mkdir -p {SOURCES,SPECS,RPMS,SRPMS,BUILD} + cd ${abs_top_srcdir} && git archive --format=tar --prefix=libjwt-${VERSION}/ master | gzip > ./rpmbuild/SOURCES/v${VERSION}.tar.gz + +rpm: rpm-prep + rpmbuild -ba --define "_topdir ${abs_top_srcdir}/rpmbuild" libjwt.spec diff --git a/dist/libjwt.spec.in b/dist/libjwt.spec.in new file mode 100644 index 00000000..12fa1795 --- /dev/null +++ b/dist/libjwt.spec.in @@ -0,0 +1,82 @@ +Summary: JSON Web Token (C implementation) +Name: libjwt +Version: @VERSION@ +Release: 0%{?dist} +License: MPLv2.0 +Group: Development/Tools +Source: https://github.com/benmcollins/libjwt/archive/v%{version}.tar.gz +URL: https://github.com/benmcollins/libjwt +BuildRequires: make libtool automake +BuildRequires: gcc doxygen +BuildRequires: pkgconfig(openssl) >= 0.9.8 +BuildRequires: pkgconfig(jansson) +%if 0%{?rhel} >= 8 +BuildRequires: check +%endif + + +%description +JSON Web Tokens are an open, industry standard RFC 7519 method for representing +claims securely between two parties. This package is a C library implementing +JWT functionality. It supports HS256/384/512, RS256/384/512 and ES256/384/512 +digest algorithms. + + +%package devel +Summary: JSON Web Token (C implementation) development kit +Group: Development/Libraries +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description devel +Development files for the JWT C library. + + +%package devel-docs +Summary: JSON Web Token (C implementation) development kit documentation +Group: Development/Libraries + +%description devel-docs +Development documentation files for the JWT C library. + +%prep +%autosetup -n %{name}-%{version} + +%build +autoreconf -i +%configure +make all doxygen-doc + + +%install +make install DESTDIR=%{buildroot} +mkdir -p %{buildroot}%{_datadir}/%{name} +cp -r doxygen-doc/html %{buildroot}%{_datadir}/%{name} + +%if 0%{?rhel} >= 8 +%check +make check +%endif + +%post +/sbin/ldconfig + +%postun +/sbin/ldconfig + +%files +%{_libdir}/*.so.* +%{_bindir}/* + +%files devel +%{_libdir}/*.so +%{_libdir}/*.la +%{_libdir}/*.a +%{_libdir}/pkgconfig/*.pc +%{_includedir}/* + +%files devel-docs +%{_datadir}/* + +%changelog +* Tue Nov 12 2019 Julien Courtat - 1.11.0-0 +- Initial packaging diff --git a/doc.svg b/doc.svg new file mode 100644 index 00000000..0b928a53 --- /dev/null +++ b/doc.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/docd.svg b/docd.svg new file mode 100644 index 00000000..ac18b275 --- /dev/null +++ b/docd.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/doxygen.cfg b/doxygen.cfg new file mode 100644 index 00000000..259a209d --- /dev/null +++ b/doxygen.cfg @@ -0,0 +1,2829 @@ +# Doxyfile 1.12.0 + +# This file describes the settings to be used by the documentation system +# Doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). +# +# Note: +# +# Use Doxygen to compare the used configuration file with the template +# configuration file: +# doxygen -x [configFile] +# Use Doxygen to compare the used configuration file with the template +# configuration file without replacing the environment variables or CMake type +# replacement variables: +# doxygen -x_noenv [configFile] + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = $(PROJECT)-$(VERSION) + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# With the PROJECT_ICON tag one can specify an icon that is included in the tabs +# when the HTML document is shown. Doxygen will copy the logo to the output +# directory. + +PROJECT_ICON = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where Doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = $(DOCDIR) + +# If the CREATE_SUBDIRS tag is set to YES then Doxygen will create up to 4096 +# sub-directories (in 2 levels) under the output directory of each output format +# and will distribute the generated files over these directories. Enabling this +# option can be useful when feeding Doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to +# control the number of sub-directories. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# Controls the number of sub-directories that will be created when +# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every +# level increment doubles the number of directories, resulting in 4096 +# directories at level 8 which is the default and also the maximum value. The +# sub-directories are organized in 2 levels, the first level always has a fixed +# number of 16 directories. +# Minimum value: 0, maximum value: 8, default value: 8. +# This tag requires that the tag CREATE_SUBDIRS is set to YES. + +CREATE_SUBDIRS_LEVEL = 8 + +# If the ALLOW_UNICODE_NAMES tag is set to YES, Doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by Doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, +# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English +# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, +# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with +# English messages), Korean, Korean-en (Korean with English messages), Latvian, +# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, +# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, +# Swedish, Turkish, Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, Doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, Doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, Doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = YES + +# If the FULL_PATH_NAMES tag is set to YES, Doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which Doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where Doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = $(SRCDIR) + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, Doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = YES + +# If the JAVADOC_BANNER tag is set to YES then Doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by Doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# By default Python docstrings are displayed as preformatted text and Doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# Doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as Doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then Doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:^^" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". Note that you cannot put \n's in the value part of an alias +# to insert newlines (in the resulting output). You can put ^^ in the value part +# of an alias to insert a newline as if a physical newline was in the original +# file. When you need a literal { or } or , in the value part of an alias you +# have to escape them by means of a backslash (\), this can lead to conflicts +# with the commands \{ and \} for these it is advised to use the version @{ and +# @} or use a double escape (\\{ and \\}) + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by Doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, +# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files). For instance to make Doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by Doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then Doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See https://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by Doxygen, so you can +# mix Doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 6. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 6 + +# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to +# generate identifiers for the Markdown headings. Note: Every identifier is +# unique. +# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a +# sequence number starting at 0 and GITHUB use the lower case version of title +# with any whitespace replaced by '-' and punctuation characters removed. +# The default value is: DOXYGEN. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +MARKDOWN_ID_STYLE = DOXYGEN + +# When enabled Doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let Doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also makes the inheritance and +# collaboration diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# https://www.riverbankcomputing.com/software) sources only. Doxygen will parse +# them like normal C++ but will assume all classes use public instead of private +# inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# Doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then Doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, Doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# Doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run Doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +# The NUM_PROC_THREADS specifies the number of threads Doxygen is allowed to use +# during processing. When set to 0 Doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which effectively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 1 + +# If the TIMESTAMP tag is set different from NO then each generated page will +# contain the date or date and time when the page was generated. Setting this to +# NO can help when comparing the output of multiple runs. +# Possible values are: YES, NO, DATETIME and DATE. +# The default value is: NO. + +TIMESTAMP = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, Doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# will also hide undocumented C++ concepts if enabled. This option has no effect +# if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all friend +# declarations. If set to NO, these declarations will be included in the +# documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# With the correct setting of option CASE_SENSE_NAMES Doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and macOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. +# Possible values are: SYSTEM, NO and YES. +# The default value is: SYSTEM. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO then Doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then Doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class +# will show which file needs to be included to use the class. +# The default value is: YES. + +SHOW_HEADERFILE = YES + +# If the SHOW_INCLUDE_FILES tag is set to YES then Doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then Doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then Doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then Doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then Doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and Doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING Doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 0 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# Doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by Doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by Doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents Doxygen's defaults, run Doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. See also section "Changing the +# layout of pages" for information. +# +# Note that if you run Doxygen from a directory containing a file called +# DoxygenLayout.xml, Doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +# The EXTERNAL_TOOL_PATH tag can be used to extend the search path (PATH +# environment variable) so that external tools such as latex and gs can be +# found. +# Note: Directories specified with EXTERNAL_TOOL_PATH are added in front of the +# path already specified by the PATH variable, and are added in the order +# specified. +# Note: This option is particularly useful for macOS version 14 (Sonoma) and +# higher, when running Doxygen from Doxywizard, because in this case any user- +# defined changes to the PATH are ignored. A typical example on macOS is to set +# EXTERNAL_TOOL_PATH = /Library/TeX/texbin /usr/local/bin +# together with the standard path, the full search path used by doxygen when +# launching external tools will then become +# PATH=/Library/TeX/texbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin + +EXTERNAL_TOOL_PATH = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by Doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by Doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then Doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, Doxygen will generate warnings for +# potential errors in the documentation, such as documenting some parameters in +# a documented function twice, or documenting parameters that don't exist or +# using markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# If WARN_IF_INCOMPLETE_DOC is set to YES, Doxygen will warn about incomplete +# function parameter documentation. If set to NO, Doxygen will accept that some +# parameters have no documentation without warning. +# The default value is: YES. + +WARN_IF_INCOMPLETE_DOC = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, Doxygen will only warn about wrong parameter +# documentation, but not about the absence of documentation. If EXTRACT_ALL is +# set to YES then this flag will automatically be disabled. See also +# WARN_IF_INCOMPLETE_DOC +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, Doxygen will warn about +# undocumented enumeration values. If set to NO, Doxygen will accept +# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: NO. + +WARN_IF_UNDOC_ENUM_VAL = NO + +# If the WARN_AS_ERROR tag is set to YES then Doxygen will immediately stop when +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then Doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the Doxygen process Doxygen will return with a non-zero status. +# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then Doxygen behaves +# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined Doxygen will not +# write the warning messages in between other messages but write them at the end +# of a run, in case a WARN_LOGFILE is defined the warning messages will be +# besides being in the defined file also be shown at the end of a run, unless +# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case +# the behavior will remain as with the setting FAIL_ON_WARNINGS. +# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that Doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# See also: WARN_LINE_FORMAT +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# In the $text part of the WARN_FORMAT command it is possible that a reference +# to a more specific place is given. To make it easier to jump to this place +# (outside of Doxygen) the user can define a custom "cut" / "paste" string. +# Example: +# WARN_LINE_FORMAT = "'vi $file +$line'" +# See also: WARN_FORMAT +# The default value is: at line $line of file $file. + +WARN_LINE_FORMAT = "at line $line of file $file" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). In case the file specified cannot be opened for writing the +# warning and error messages are written to standard error. When as file - is +# specified the warning and error messages are written to standard output +# (stdout). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = $(SRCDIR) + +# This tag can be used to specify the character encoding of the source files +# that Doxygen parses. Internally Doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. +# See also: INPUT_FILE_ENCODING +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# This tag can be used to specify the character encoding of the source files +# that Doxygen parses The INPUT_FILE_ENCODING tag can be used to specify +# character encoding on a per file pattern basis. Doxygen will compare the file +# name with each pattern and apply the encoding instead of the default +# INPUT_ENCODING) if there is a match. The character encodings are a list of the +# form: pattern=encoding (like *.php=ISO-8859-1). +# See also: INPUT_ENCODING for further information on supported encodings. + +INPUT_FILE_ENCODING = + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by Doxygen. +# +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm, +# *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, +# *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d, +# *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to +# be provided as Doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which Doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# ANamespace::AClass, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that Doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that Doxygen will use the data processed and written to standard output +# for further processing, therefore nothing else, like debug statements or used +# commands (so in case of a Windows batch file always use @echo OFF), should be +# written to standard output. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by Doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by Doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the Doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +# The Fortran standard specifies that for fixed formatted Fortran code all +# characters from position 72 are to be considered as comment. A common +# extension is to allow longer lines before the automatic comment starts. The +# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can +# be processed before the automatic comment starts. +# Minimum value: 7, maximum value: 10000, default value: 72. + +FORTRAN_COMMENT_AFTER = 72 + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# multi-line macros, enums or list initialized variables directly into the +# documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct Doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# entity all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of Doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see https://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by Doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then Doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) +# that should be ignored while generating the index headers. The IGNORE_PREFIX +# tag works for classes, function and member names. The entity will be placed in +# the alphabetical list under the first letter of the entity name that remains +# after removing the prefix. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, Doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = $(GENERATE_HTML) + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank Doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that Doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that Doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of Doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank Doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that Doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank Doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that Doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by Doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# Note: Since the styling of scrollbars can currently not be overruled in +# Webkit/Chromium, the styling will be left out of the default doxygen.css if +# one or more extra stylesheets have been specified. So if scrollbar +# customization is desired it has to be added explicitly. For an example see the +# documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output +# should be rendered with a dark or light theme. +# Possible values are: LIGHT always generates light mode output, DARK always +# generates dark mode output, AUTO_LIGHT automatically sets the mode according +# to the user preference, uses light mode if no preference is set (the default), +# AUTO_DARK automatically sets the mode according to the user preference, uses +# dark mode if no preference is set and TOGGLE allows a user to switch between +# light and dark mode via a button. +# The default value is: AUTO_LIGHT. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE = AUTO_LIGHT + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a color-wheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use gray-scales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be +# dynamically folded and expanded in the generated HTML source code. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_CODE_FOLDING = YES + +# If the HTML_COPY_CLIPBOARD tag is set to YES then Doxygen will show an icon in +# the top right corner of code and text fragments that allows the user to copy +# its content to the clipboard. Note this only works if supported by the browser +# and the web page is served via a secure context (see: +# https://www.w3.org/TR/secure-contexts/), i.e. using the https: or file: +# protocol. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COPY_CLIPBOARD = YES + +# Doxygen stores a couple of settings persistently in the browser (via e.g. +# cookies). By default these settings apply to all HTML pages generated by +# Doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store +# the settings under a project specific key, such that the user preferences will +# be stored separately. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_PROJECT_COOKIE = + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, Doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag determines the URL of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDURL = + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then Doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# on Windows. In the beginning of 2021 Microsoft took the original page, with +# a.o. the download links, offline the HTML help workshop was already many years +# in maintenance mode). You can download the HTML help workshop from the web +# archives at Installation executable (see: +# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo +# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by Doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = $(GENERATE_HTMLHELP) + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = ../$(PROJECT).chm + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# Doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = $(HHC_PATH) + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the main .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = $(GENERATE_CHI) + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = YES + +# The SITEMAP_URL tag is used to specify the full URL of the place where the +# generated documentation will be placed on the server by the user during the +# deployment of the documentation. The generated sitemap is called sitemap.xml +# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL +# is specified no sitemap is generated. For information about the sitemap +# protocol see https://www.sitemaps.org +# This tag requires that the tag GENERATE_HTML is set to YES. + +SITEMAP_URL = + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty Doxygen will try to +# run qhelpgenerator on the generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine tune the look of the index (see "Fine-tuning the output"). As an +# example, the default style sheet generated by Doxygen has an example that +# shows how to put an image at the root of the tree instead of the PROJECT_NAME. +# Since the tree basically has the same information as the tab index, you could +# consider setting DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = YES + +# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the +# FULL_SIDEBAR option determines if the side bar is limited to only the treeview +# area (value NO) or if it should extend to the full height of the window (value +# YES). Setting this to YES gives a layout similar to +# https://docs.readthedocs.io with more room for contents, but less room for the +# project logo, title, and description. If either GENERATE_TREEVIEW or +# DISABLE_INDEX is set to NO, this option has no effect. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FULL_SIDEBAR = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# Doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# When the SHOW_ENUM_VALUES tag is set doxygen will show the specified +# enumeration values besides the enumeration mnemonics. +# The default value is: NO. + +SHOW_ENUM_VALUES = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, Doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# If the OBFUSCATE_EMAILS tag is set to YES, Doxygen will obfuscate email +# addresses. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +OBFUSCATE_EMAILS = YES + +# If the HTML_FORMULA_FORMAT option is set to svg, Doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# Doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# https://www.mathjax.org) which uses client side JavaScript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# With MATHJAX_VERSION it is possible to specify the MathJax version to be used. +# Note that the different versions of MathJax have different requirements with +# regards to the different settings, so it is possible that also other MathJax +# settings have to be changed when switching between the different MathJax +# versions. +# Possible values are: MathJax_2 and MathJax_3. +# The default value is: MathJax_2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_VERSION = MathJax_2 + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. For more details about the output format see MathJax +# version 2 (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 +# (see: +# http://docs.mathjax.org/en/latest/web/components/output.html). +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility. This is the name for Mathjax version 2, for MathJax version 3 +# this will be translated into chtml), NativeMML (i.e. MathML. Only supported +# for MathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This +# is the name for Mathjax version 3, for MathJax version 2 this will be +# translated into HTML-CSS) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from https://www.mathjax.org before deployment. The default value is: +# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 +# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# for MathJax version 2 (see +# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# For example for MathJax version 3 (see +# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): +# MATHJAX_EXTENSIONS = ams +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with JavaScript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled Doxygen will generate a search box for +# the HTML output. The underlying search engine uses JavaScript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the JavaScript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /
"),i=e.children()[0];return y("body").append(e),t=i.offsetWidth,e.css("overflow","scroll"),t===(i=i.offsetWidth)&&(i=e[0].clientWidth),e.remove(),s=t-i}, +getScrollInfo:function(t){var e=t.isWindow||t.isDocument?"":t.element.css("overflow-x"),i=t.isWindow||t.isDocument?"":t.element.css("overflow-y"),e="scroll"===e||"auto"===e&&t.widthx(D(s),D(n))?o.important="horizontal":o.important="vertical",p.using.call(this,t,o)}),h.offset(y.extend(l,{using:t}))})},y.ui.position={fit:{left:function(t,e){var i=e.within, +s=i.isWindow?i.scrollLeft:i.offset.left,n=i.width,o=t.left-e.collisionPosition.marginLeft,h=s-o,a=o+e.collisionWidth-n-s;e.collisionWidth>n?0n?0=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}}),y.ui.plugin={add:function(t,e,i){var s,n=y.ui[t].prototype;for(s in i)n.plugins[s]=n.plugins[s]||[],n.plugins[s].push([e,i[s]])},call:function(t,e,i,s){var n,o=t.plugins[e];if(o&&(s||t.element[0].parentNode&&11!==t.element[0].parentNode.nodeType))for(n=0;n").css({overflow:"hidden",position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})), +this.element=this.element.parent().data("ui-resizable",this.element.resizable("instance")),this.elementIsWrapper=!0,t={marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom"),marginLeft:this.originalElement.css("marginLeft")},this.element.css(t),this.originalElement.css("margin",0),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css(t),this._proportionallyResize()),this._setupHandles(),e.autoHide&&y(this.element).on("mouseenter",function(){e.disabled||(i._removeClass("ui-resizable-autohide"),i._handles.show())}).on("mouseleave",function(){e.disabled||i.resizing||(i._addClass("ui-resizable-autohide"),i._handles.hide())}),this._mouseInit()},_destroy:function(){this._mouseDestroy(),this._addedHandles.remove();function t(t){y(t +).removeData("resizable").removeData("ui-resizable").off(".resizable")}var e;return this.elementIsWrapper&&(t(this.element),e=this.element,this.originalElement.css({position:e.css("position"),width:e.outerWidth(),height:e.outerHeight(),top:e.css("top"),left:e.css("left")}).insertAfter(e),e.remove()),this.originalElement.css("resize",this.originalResizeStyle),t(this.originalElement),this},_setOption:function(t,e){switch(this._super(t,e),t){case"handles":this._removeHandles(),this._setupHandles();break;case"aspectRatio":this._aspectRatio=!!e}},_setupHandles:function(){var t,e,i,s,n,o=this.options,h=this;if(this.handles=o.handles||(y(".ui-resizable-handle",this.element).length?{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"}:"e,s,se"),this._handles=y(),this._addedHandles=y(),this.handles.constructor===String)for("all"===this.handles&&(this.handles="n,e,s,w,se,sw,ne,nw"),i=this.handles.split( +","),this.handles={},e=0;e"),this._addClass(n,"ui-resizable-handle "+s),n.css({zIndex:o.zIndex}),this.handles[t]=".ui-resizable-"+t,this.element.children(this.handles[t]).length||(this.element.append(n),this._addedHandles=this._addedHandles.add(n));this._renderAxis=function(t){var e,i,s;for(e in t=t||this.element,this.handles)this.handles[e].constructor===String?this.handles[e]=this.element.children(this.handles[e]).first().show():(this.handles[e].jquery||this.handles[e].nodeType)&&(this.handles[e]=y(this.handles[e]),this._on(this.handles[e],{mousedown:h._mouseDown})),this.elementIsWrapper&&this.originalElement[0].nodeName.match(/^(textarea|input|select|button)$/i)&&(i=y(this.handles[e],this.element),s=/sw|ne|nw|se|n|s/.test(e)?i.outerHeight():i.outerWidth(),i=["padding",/ne|nw|n/.test(e)?"Top":/se|sw|s/.test(e)?"Bottom":/^e$/.test(e)?"Right":"Left"].join(""),t.css(i,s),this._proportionallyResize()),this._handles=this._handles.add( +this.handles[e])},this._renderAxis(this.element),this._handles=this._handles.add(this.element.find(".ui-resizable-handle")),this._handles.disableSelection(),this._handles.on("mouseover",function(){h.resizing||(this.className&&(n=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)),h.axis=n&&n[1]?n[1]:"se")}),o.autoHide&&(this._handles.hide(),this._addClass("ui-resizable-autohide"))},_removeHandles:function(){this._addedHandles.remove()},_mouseCapture:function(t){var e,i,s=!1;for(e in this.handles)(i=y(this.handles[e])[0])!==t.target&&!y.contains(i,t.target)||(s=!0);return!this.options.disabled&&s},_mouseStart:function(t){var e,i,s=this.options,n=this.element;return this.resizing=!0,this._renderProxy(),e=this._num(this.helper.css("left")),i=this._num(this.helper.css("top")),s.containment&&(e+=y(s.containment).scrollLeft()||0,i+=y(s.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:e,top:i},this.size=this._helper?{width:this.helper.width(),height:this.helper.height()}:{ +width:n.width(),height:n.height()},this.originalSize=this._helper?{width:n.outerWidth(),height:n.outerHeight()}:{width:n.width(),height:n.height()},this.sizeDiff={width:n.outerWidth()-n.width(),height:n.outerHeight()-n.height()},this.originalPosition={left:e,top:i},this.originalMousePosition={left:t.pageX,top:t.pageY},this.aspectRatio="number"==typeof s.aspectRatio?s.aspectRatio:this.originalSize.width/this.originalSize.height||1,s=y(".ui-resizable-"+this.axis).css("cursor"),y("body").css("cursor","auto"===s?this.axis+"-resize":s),this._addClass("ui-resizable-resizing"),this._propagate("start",t),!0},_mouseDrag:function(t){var e=this.originalMousePosition,i=this.axis,s=t.pageX-e.left||0,e=t.pageY-e.top||0,i=this._change[i];return this._updatePrevProperties(),i&&(e=i.apply(this,[t,s,e]),this._updateVirtualBoundaries(t.shiftKey),(this._aspectRatio||t.shiftKey)&&(e=this._updateRatio(e,t)),e=this._respectSize(e,t),this._updateCache(e),this._propagate("resize",t),e=this._applyChanges(), +!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),y.isEmptyObject(e)||(this._updatePrevProperties(),this._trigger("resize",t,this.ui()),this._applyChanges())),!1},_mouseStop:function(t){this.resizing=!1;var e,i,s,n=this.options,o=this;return this._helper&&(s=(e=(i=this._proportionallyResizeElements).length&&/textarea/i.test(i[0].nodeName))&&this._hasScroll(i[0],"left")?0:o.sizeDiff.height,i=e?0:o.sizeDiff.width,e={width:o.helper.width()-i,height:o.helper.height()-s},i=parseFloat(o.element.css("left"))+(o.position.left-o.originalPosition.left)||null,s=parseFloat(o.element.css("top"))+(o.position.top-o.originalPosition.top)||null,n.animate||this.element.css(y.extend(e,{top:s,left:i})),o.helper.height(o.size.height),o.helper.width(o.size.width),this._helper&&!n.animate&&this._proportionallyResize()),y("body").css("cursor","auto"),this._removeClass("ui-resizable-resizing"),this._propagate("stop",t),this._helper&&this.helper.remove(),!1},_updatePrevProperties:function(){ +this.prevPosition={top:this.position.top,left:this.position.left},this.prevSize={width:this.size.width,height:this.size.height}},_applyChanges:function(){var t={};return this.position.top!==this.prevPosition.top&&(t.top=this.position.top+"px"),this.position.left!==this.prevPosition.left&&(t.left=this.position.left+"px"),this.size.width!==this.prevSize.width&&(t.width=this.size.width+"px"),this.size.height!==this.prevSize.height&&(t.height=this.size.height+"px"),this.helper.css(t),t},_updateVirtualBoundaries:function(t){var e,i,s=this.options,n={minWidth:this._isNumber(s.minWidth)?s.minWidth:0,maxWidth:this._isNumber(s.maxWidth)?s.maxWidth:1/0,minHeight:this._isNumber(s.minHeight)?s.minHeight:0,maxHeight:this._isNumber(s.maxHeight)?s.maxHeight:1/0};(this._aspectRatio||t)&&(e=n.minHeight*this.aspectRatio,i=n.minWidth/this.aspectRatio,s=n.maxHeight*this.aspectRatio,t=n.maxWidth/this.aspectRatio,e>n.minWidth&&(n.minWidth=e),i>n.minHeight&&(n.minHeight=i),st.width,h=this._isNumber(t.height)&&e.minHeight&&e.minHeight>t.height,a=this.originalPosition.left+this.originalSize.width,r=this.originalPosition.top+this.originalSize.height +,l=/sw|nw|w/.test(i),i=/nw|ne|n/.test(i);return o&&(t.width=e.minWidth),h&&(t.height=e.minHeight),s&&(t.width=e.maxWidth),n&&(t.height=e.maxHeight),o&&l&&(t.left=a-e.minWidth),s&&l&&(t.left=a-e.maxWidth),h&&i&&(t.top=r-e.minHeight),n&&i&&(t.top=r-e.maxHeight),t.width||t.height||t.left||!t.top?t.width||t.height||t.top||!t.left||(t.left=null):t.top=null,t},_getPaddingPlusBorderDimensions:function(t){for(var e=0,i=[],s=[t.css("borderTopWidth"),t.css("borderRightWidth"),t.css("borderBottomWidth"),t.css("borderLeftWidth")],n=[t.css("paddingTop"),t.css("paddingRight"),t.css("paddingBottom"),t.css("paddingLeft")];e<4;e++)i[e]=parseFloat(s[e])||0,i[e]+=parseFloat(n[e])||0;return{height:i[0]+i[2],width:i[1]+i[3]}},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var t,e=0,i=this.helper||this.element;e").css({overflow:"hidden"}),this._addClass(this.helper,this._helper),this.helper.css({width:this.element.outerWidth(),height:this.element.outerHeight(),position:"absolute",left:this.elementOffset.left+"px",top:this.elementOffset.top+"px",zIndex:++e.zIndex}),this.helper.appendTo("body").disableSelection()):this.helper=this.element},_change:{e:function(t,e){return{width:this.originalSize.width+e}},w:function(t,e){var i=this.originalSize;return{left:this.originalPosition.left+e,width:i.width-e}},n:function(t,e,i){var s=this.originalSize;return{top:this.originalPosition.top+i,height:s.height-i}},s:function(t,e,i){return{height:this.originalSize.height+i}},se:function(t,e,i){return y.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[t,e,i]))},sw:function(t,e, +i){return y.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[t,e,i]))},ne:function(t,e,i){return y.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[t,e,i]))},nw:function(t,e,i){return y.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[t,e,i]))}},_propagate:function(t,e){y.ui.plugin.call(this,t,[e,this.ui()]),"resize"!==t&&this._trigger(t,e,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}),y.ui.plugin.add("resizable","animate",{stop:function(e){var i=y(this).resizable("instance"),t=i.options,s=i._proportionallyResizeElements,n=s.length&&/textarea/i.test(s[0].nodeName),o=n&&i._hasScroll(s[0],"left")?0:i.sizeDiff.height,h=n?0:i.sizeDiff.width,n={width:i.size.width-h,height:i.size.height-o},h=parseFloat(i.element.css("left"))+(i.position.left-i.originalPosition.left +)||null,o=parseFloat(i.element.css("top"))+(i.position.top-i.originalPosition.top)||null;i.element.animate(y.extend(n,o&&h?{top:o,left:h}:{}),{duration:t.animateDuration,easing:t.animateEasing,step:function(){var t={width:parseFloat(i.element.css("width")),height:parseFloat(i.element.css("height")),top:parseFloat(i.element.css("top")),left:parseFloat(i.element.css("left"))};s&&s.length&&y(s[0]).css({width:t.width,height:t.height}),i._updateCache(t),i._propagate("resize",e)}})}}),y.ui.plugin.add("resizable","containment",{start:function(){var i,s,n=y(this).resizable("instance"),t=n.options,e=n.element,o=t.containment,h=o instanceof y?o.get(0):/parent/.test(o)?e.parent().get(0):o;h&&(n.containerElement=y(h),/document/.test(o)||o===document?(n.containerOffset={left:0,top:0},n.containerPosition={left:0,top:0},n.parentData={element:y(document),left:0,top:0,width:y(document).width(),height:y(document).height()||document.body.parentNode.scrollHeight}):(i=y(h),s=[],y(["Top","Right","Left","Bottom"]).each(function(t,e +){s[t]=n._num(i.css("padding"+e))}),n.containerOffset=i.offset(),n.containerPosition=i.position(),n.containerSize={height:i.innerHeight()-s[3],width:i.innerWidth()-s[1]},t=n.containerOffset,e=n.containerSize.height,o=n.containerSize.width,o=n._hasScroll(h,"left")?h.scrollWidth:o,e=n._hasScroll(h)?h.scrollHeight:e,n.parentData={element:h,left:t.left,top:t.top,width:o,height:e}))},resize:function(t){var e=y(this).resizable("instance"),i=e.options,s=e.containerOffset,n=e.position,o=e._aspectRatio||t.shiftKey,h={top:0,left:0},a=e.containerElement,t=!0;a[0]!==document&&/static/.test(a.css("position"))&&(h=s),n.left<(e._helper?s.left:0)&&(e.size.width=e.size.width+(e._helper?e.position.left-s.left:e.position.left-h.left),o&&(e.size.height=e.size.width/e.aspectRatio,t=!1),e.position.left=i.helper?s.left:0),n.top<(e._helper?s.top:0)&&(e.size.height=e.size.height+(e._helper?e.position.top-s.top:e.position.top),o&&(e.size.width=e.size.height*e.aspectRatio,t=!1),e.position.top=e._helper?s.top:0), +i=e.containerElement.get(0)===e.element.parent().get(0),n=/relative|absolute/.test(e.containerElement.css("position")),i&&n?(e.offset.left=e.parentData.left+e.position.left,e.offset.top=e.parentData.top+e.position.top):(e.offset.left=e.element.offset().left,e.offset.top=e.element.offset().top),n=Math.abs(e.sizeDiff.width+(e._helper?e.offset.left-h.left:e.offset.left-s.left)),s=Math.abs(e.sizeDiff.height+(e._helper?e.offset.top-h.top:e.offset.top-s.top)),n+e.size.width>=e.parentData.width&&(e.size.width=e.parentData.width-n,o&&(e.size.height=e.size.width/e.aspectRatio,t=!1)),s+e.size.height>=e.parentData.height&&(e.size.height=e.parentData.height-s,o&&(e.size.width=e.size.height*e.aspectRatio,t=!1)),t||(e.position.left=e.prevPosition.left,e.position.top=e.prevPosition.top,e.size.width=e.prevSize.width,e.size.height=e.prevSize.height)},stop:function(){var t=y(this).resizable("instance"),e=t.options,i=t.containerOffset,s=t.containerPosition,n=t.containerElement,o=y(t.helper),h=o.offset(),a=o.outerWidth( +)-t.sizeDiff.width,o=o.outerHeight()-t.sizeDiff.height;t._helper&&!e.animate&&/relative/.test(n.css("position"))&&y(this).css({left:h.left-s.left-i.left,width:a,height:o}),t._helper&&!e.animate&&/static/.test(n.css("position"))&&y(this).css({left:h.left-s.left-i.left,width:a,height:o})}}),y.ui.plugin.add("resizable","alsoResize",{start:function(){var t=y(this).resizable("instance").options;y(t.alsoResize).each(function(){var t=y(this);t.data("ui-resizable-alsoresize",{width:parseFloat(t.width()),height:parseFloat(t.height()),left:parseFloat(t.css("left")),top:parseFloat(t.css("top"))})})},resize:function(t,i){var e=y(this).resizable("instance"),s=e.options,n=e.originalSize,o=e.originalPosition,h={height:e.size.height-n.height||0,width:e.size.width-n.width||0,top:e.position.top-o.top||0,left:e.position.left-o.left||0};y(s.alsoResize).each(function(){var t=y(this),s=y(this).data("ui-resizable-alsoresize"),n={},e=t.parents(i.originalElement[0]).length?["width","height"]:["width","height","top","left"];y.each(e, +function(t,e){var i=(s[e]||0)+(h[e]||0);i&&0<=i&&(n[e]=i||null)}),t.css(n)})},stop:function(){y(this).removeData("ui-resizable-alsoresize")}}),y.ui.plugin.add("resizable","ghost",{start:function(){var t=y(this).resizable("instance"),e=t.size;t.ghost=t.originalElement.clone(),t.ghost.css({opacity:.25,display:"block",position:"relative",height:e.height,width:e.width,margin:0,left:0,top:0}),t._addClass(t.ghost,"ui-resizable-ghost"),!1!==y.uiBackCompat&&"string"==typeof t.options.ghost&&t.ghost.addClass(this.options.ghost),t.ghost.appendTo(t.helper)},resize:function(){var t=y(this).resizable("instance");t.ghost&&t.ghost.css({position:"relative",height:t.size.height,width:t.size.width})},stop:function(){var t=y(this).resizable("instance");t.ghost&&t.helper&&t.helper.get(0).removeChild(t.ghost.get(0))}}),y.ui.plugin.add("resizable","grid",{resize:function(){var t,e=y(this).resizable("instance"),i=e.options,s=e.size,n=e.originalSize,o=e.originalPosition,h=e.axis,a="number"==typeof i.grid?[i.grid,i.grid]:i.grid,r=a[0 +]||1,l=a[1]||1,u=Math.round((s.width-n.width)/r)*r,p=Math.round((s.height-n.height)/l)*l,d=n.width+u,c=n.height+p,f=i.maxWidth&&i.maxWidthd,s=i.minHeight&&i.minHeight>c;i.grid=a,m&&(d+=r),s&&(c+=l),f&&(d-=r),g&&(c-=l),/^(se|s|e)$/.test(h)?(e.size.width=d,e.size.height=c):/^(ne)$/.test(h)?(e.size.width=d,e.size.height=c,e.position.top=o.top-p):/^(sw)$/.test(h)?(e.size.width=d,e.size.height=c,e.position.left=o.left-u):((c-l<=0||d-r<=0)&&(t=e._getPaddingPlusBorderDimensions(this)),0=f[g]?0:Math.min(f[g],n));!a&&1-1){ +targetElements.on(evt+EVENT_NAMESPACE,function elementToggle(event){$.powerTip.toggle(this,event)})}else{targetElements.on(evt+EVENT_NAMESPACE,function elementOpen(event){$.powerTip.show(this,event)})}});$.each(options.closeEvents,function(idx,evt){if($.inArray(evt,options.openEvents)<0){targetElements.on(evt+EVENT_NAMESPACE,function elementClose(event){$.powerTip.hide(this,!isMouseEvent(event))})}});targetElements.on("keydown"+EVENT_NAMESPACE,function elementKeyDown(event){if(event.keyCode===27){$.powerTip.hide(this,true)}})}return targetElements};$.fn.powerTip.defaults={fadeInTime:200,fadeOutTime:100,followMouse:false,popupId:"powerTip",popupClass:null,intentSensitivity:7,intentPollInterval:100,closeDelay:100,placement:"n",smartPlacement:false,offset:10,mouseOnToPopup:false,manual:false,openEvents:["mouseenter","focus"],closeEvents:["mouseleave","blur"]};$.fn.powerTip.smartPlacementLists={n:["n","ne","nw","s"],e:["e","ne","se","w","nw","sw","n","s","e"],s:["s","se","sw","n"],w:["w","nw","sw","e","ne","se", +"n","s","w"],nw:["nw","w","sw","n","s","se","nw"],ne:["ne","e","se","n","s","sw","ne"],sw:["sw","w","nw","s","n","ne","sw"],se:["se","e","ne","s","n","nw","se"],"nw-alt":["nw-alt","n","ne-alt","sw-alt","s","se-alt","w","e"],"ne-alt":["ne-alt","n","nw-alt","se-alt","s","sw-alt","e","w"],"sw-alt":["sw-alt","s","se-alt","nw-alt","n","ne-alt","w","e"],"se-alt":["se-alt","s","sw-alt","ne-alt","n","nw-alt","e","w"]};$.powerTip={show:function apiShowTip(element,event){if(isMouseEvent(event)){trackMouse(event);session.previousX=event.pageX;session.previousY=event.pageY;$(element).data(DATA_DISPLAYCONTROLLER).show()}else{$(element).first().data(DATA_DISPLAYCONTROLLER).show(true,true)}return element},reposition:function apiResetPosition(element){$(element).first().data(DATA_DISPLAYCONTROLLER).resetPosition();return element},hide:function apiCloseTip(element,immediate){var displayController;immediate=element?immediate:true;if(element){displayController=$(element).first().data(DATA_DISPLAYCONTROLLER)}else if( +session.activeHover){displayController=session.activeHover.data(DATA_DISPLAYCONTROLLER)}if(displayController){displayController.hide(immediate)}return element},toggle:function apiToggle(element,event){if(session.activeHover&&session.activeHover.is(element)){$.powerTip.hide(element,!isMouseEvent(event))}else{$.powerTip.show(element,event)}return element}};$.powerTip.showTip=$.powerTip.show;$.powerTip.closeTip=$.powerTip.hide;function CSSCoordinates(){var me=this;me.top="auto";me.left="auto";me.right="auto";me.bottom="auto";me.set=function(property,value){if($.isNumeric(value)){me[property]=Math.round(value)}}}function DisplayController(element,options,tipController){var hoverTimer=null,myCloseDelay=null;function openTooltip(immediate,forceOpen){cancelTimer();if(!element.data(DATA_HASACTIVEHOVER)){if(!immediate){session.tipOpenImminent=true;hoverTimer=setTimeout(function intentDelay(){hoverTimer=null;checkForIntent()},options.intentPollInterval)}else{if(forceOpen){element.data(DATA_FORCEDOPEN,true)} +closeAnyDelayed();tipController.showTip(element)}}else{cancelClose()}}function closeTooltip(disableDelay){if(myCloseDelay){myCloseDelay=session.closeDelayTimeout=clearTimeout(myCloseDelay);session.delayInProgress=false}cancelTimer();session.tipOpenImminent=false;if(element.data(DATA_HASACTIVEHOVER)){element.data(DATA_FORCEDOPEN,false);if(!disableDelay){session.delayInProgress=true;session.closeDelayTimeout=setTimeout(function closeDelay(){session.closeDelayTimeout=null;tipController.hideTip(element);session.delayInProgress=false;myCloseDelay=null},options.closeDelay);myCloseDelay=session.closeDelayTimeout}else{tipController.hideTip(element)}}}function checkForIntent(){var xDifference=Math.abs(session.previousX-session.currentX),yDifference=Math.abs(session.previousY-session.currentY),totalDifference=xDifference+yDifference;if(totalDifference",{id:options.popupId});if($body.length===0){$body=$("body")}$body.append(tipElement);session.tooltips=session.tooltips?session.tooltips.add(tipElement):tipElement}if(options.followMouse){if(!tipElement.data(DATA_HASMOUSEMOVE)){$document.on("mousemove"+EVENT_NAMESPACE,positionTipOnCursor);$window.on("scroll"+EVENT_NAMESPACE,positionTipOnCursor);tipElement.data(DATA_HASMOUSEMOVE,true)}}function beginShowTip(element){element.data(DATA_HASACTIVEHOVER,true);tipElement.queue(function queueTipInit(next){showTip(element);next()})}function showTip(element){var tipContent;if(!element.data(DATA_HASACTIVEHOVER)){return}if( +session.isTipOpen){if(!session.isClosing){hideTip(session.activeHover)}tipElement.delay(100).queue(function queueTipAgain(next){showTip(element);next()});return}element.trigger("powerTipPreRender");tipContent=getTooltipContent(element);if(tipContent){tipElement.empty().append(tipContent)}else{return}element.trigger("powerTipRender");session.activeHover=element;session.isTipOpen=true;tipElement.data(DATA_MOUSEONTOTIP,options.mouseOnToPopup);tipElement.addClass(options.popupClass);if(!options.followMouse||element.data(DATA_FORCEDOPEN)){positionTipOnElement(element);session.isFixedTipOpen=true}else{positionTipOnCursor()}if(!element.data(DATA_FORCEDOPEN)&&!options.followMouse){$document.on("click"+EVENT_NAMESPACE,function documentClick(event){var target=event.target;if(target!==element[0]){if(options.mouseOnToPopup){if(target!==tipElement[0]&&!$.contains(tipElement[0],target)){$.powerTip.hide()}}else{$.powerTip.hide()}}})}if(options.mouseOnToPopup&&!options.manual){tipElement.on("mouseenter"+EVENT_NAMESPACE, +function tipMouseEnter(){if(session.activeHover){session.activeHover.data(DATA_DISPLAYCONTROLLER).cancel()}});tipElement.on("mouseleave"+EVENT_NAMESPACE,function tipMouseLeave(){if(session.activeHover){session.activeHover.data(DATA_DISPLAYCONTROLLER).hide()}})}tipElement.fadeIn(options.fadeInTime,function fadeInCallback(){if(!session.desyncTimeout){session.desyncTimeout=setInterval(closeDesyncedTip,500)}element.trigger("powerTipOpen")})}function hideTip(element){session.isClosing=true;session.isTipOpen=false;session.desyncTimeout=clearInterval(session.desyncTimeout);element.data(DATA_HASACTIVEHOVER,false);element.data(DATA_FORCEDOPEN,false);$document.off("click"+EVENT_NAMESPACE);tipElement.off(EVENT_NAMESPACE);tipElement.fadeOut(options.fadeOutTime,function fadeOutCallback(){var coords=new CSSCoordinates;session.activeHover=null;session.isClosing=false;session.isFixedTipOpen=false;tipElement.removeClass();coords.set("top",session.currentY+options.offset);coords.set("left",session.currentX+options.offset); +tipElement.css(coords);element.trigger("powerTipClose")})}function positionTipOnCursor(){var tipWidth,tipHeight,coords,collisions,collisionCount;if(!session.isFixedTipOpen&&(session.isTipOpen||session.tipOpenImminent&&tipElement.data(DATA_HASMOUSEMOVE))){tipWidth=tipElement.outerWidth();tipHeight=tipElement.outerHeight();coords=new CSSCoordinates;coords.set("top",session.currentY+options.offset);coords.set("left",session.currentX+options.offset);collisions=getViewportCollisions(coords,tipWidth,tipHeight);if(collisions!==Collision.none){collisionCount=countFlags(collisions);if(collisionCount===1){if(collisions===Collision.right){coords.set("left",session.scrollLeft+session.windowWidth-tipWidth)}else if(collisions===Collision.bottom){coords.set("top",session.scrollTop+session.windowHeight-tipHeight)}}else{coords.set("left",session.currentX-tipWidth-options.offset);coords.set("top",session.currentY-tipHeight-options.offset)}}tipElement.css(coords)}}function positionTipOnElement(element){var priorityList, +finalPlacement;if(options.smartPlacement||options.followMouse&&element.data(DATA_FORCEDOPEN)){priorityList=$.fn.powerTip.smartPlacementLists[options.placement];$.each(priorityList,function(idx,pos){var collisions=getViewportCollisions(placeTooltip(element,pos),tipElement.outerWidth(),tipElement.outerHeight());finalPlacement=pos;return collisions!==Collision.none})}else{placeTooltip(element,options.placement);finalPlacement=options.placement}tipElement.removeClass("w nw sw e ne se n s w se-alt sw-alt ne-alt nw-alt");tipElement.addClass(finalPlacement)}function placeTooltip(element,placement){var iterationCount=0,tipWidth,tipHeight,coords=new CSSCoordinates;coords.set("top",0);coords.set("left",0);tipElement.css(coords);do{tipWidth=tipElement.outerWidth();tipHeight=tipElement.outerHeight();coords=placementCalculator.compute(element,placement,tipWidth,tipHeight,options.offset);tipElement.css(coords)}while(++iterationCount<=5&&(tipWidth!==tipElement.outerWidth()||tipHeight!==tipElement.outerHeight())); +return coords}function closeDesyncedTip(){var isDesynced=false,hasDesyncableCloseEvent=$.grep(["mouseleave","mouseout","blur","focusout"],function(eventType){return $.inArray(eventType,options.closeEvents)!==-1}).length>0;if(session.isTipOpen&&!session.isClosing&&!session.delayInProgress&&hasDesyncableCloseEvent){if(session.activeHover.data(DATA_HASACTIVEHOVER)===false||session.activeHover.is(":disabled")){isDesynced=true}else if(!isMouseOver(session.activeHover)&&!session.activeHover.is(":focus")&&!session.activeHover.data(DATA_FORCEDOPEN)){if(tipElement.data(DATA_MOUSEONTOTIP)){if(!isMouseOver(tipElement)){isDesynced=true}}else{isDesynced=true}}if(isDesynced){hideTip(session.activeHover)}}}this.showTip=beginShowTip;this.hideTip=hideTip;this.resetPosition=positionTipOnElement}function isSvgElement(element){return Boolean(window.SVGElement&&element[0]instanceof SVGElement)}function isMouseEvent(event){return Boolean(event&&$.inArray(event.type,MOUSE_EVENTS)>-1&&typeof event.pageX==="number")} +function initTracking(){if(!session.mouseTrackingActive){session.mouseTrackingActive=true;getViewportDimensions();$(getViewportDimensions);$document.on("mousemove"+EVENT_NAMESPACE,trackMouse);$window.on("resize"+EVENT_NAMESPACE,trackResize);$window.on("scroll"+EVENT_NAMESPACE,trackScroll)}}function getViewportDimensions(){session.scrollLeft=$window.scrollLeft();session.scrollTop=$window.scrollTop();session.windowWidth=$window.width();session.windowHeight=$window.height()}function trackResize(){session.windowWidth=$window.width();session.windowHeight=$window.height()}function trackScroll(){var x=$window.scrollLeft(),y=$window.scrollTop();if(x!==session.scrollLeft){session.currentX+=x-session.scrollLeft;session.scrollLeft=x}if(y!==session.scrollTop){session.currentY+=y-session.scrollTop;session.scrollTop=y}}function trackMouse(event){session.currentX=event.pageX;session.currentY=event.pageY}function isMouseOver(element){var elementPosition=element.offset(),elementBox=element[0].getBoundingClientRect(), +elementWidth=elementBox.right-elementBox.left,elementHeight=elementBox.bottom-elementBox.top;return session.currentX>=elementPosition.left&&session.currentX<=elementPosition.left+elementWidth&&session.currentY>=elementPosition.top&&session.currentY<=elementPosition.top+elementHeight}function getTooltipContent(element){var tipText=element.data(DATA_POWERTIP),tipObject=element.data(DATA_POWERTIPJQ),tipTarget=element.data(DATA_POWERTIPTARGET),targetElement,content;if(tipText){if($.isFunction(tipText)){tipText=tipText.call(element[0])}content=tipText}else if(tipObject){if($.isFunction(tipObject)){tipObject=tipObject.call(element[0])}if(tipObject.length>0){content=tipObject.clone(true,true)}}else if(tipTarget){targetElement=$("#"+tipTarget);if(targetElement.length>0){content=targetElement.html()}}return content}function getViewportCollisions(coords,elementWidth,elementHeight){var viewportTop=session.scrollTop,viewportLeft=session.scrollLeft,viewportBottom=viewportTop+session.windowHeight, +viewportRight=viewportLeft+session.windowWidth,collisions=Collision.none;if(coords.topviewportBottom||Math.abs(coords.bottom-session.windowHeight)>viewportBottom){collisions|=Collision.bottom}if(coords.leftviewportRight){collisions|=Collision.left}if(coords.left+elementWidth>viewportRight||coords.right1)){a.preventDefault();var c=a.originalEvent.changedTouches[0],d=document.createEvent("MouseEvents");d.initMouseEvent(b,!0,!0,window,1,c.screenX,c.screenY,c.clientX,c.clientY,!1,!1,!1,!1,0,null),a.target.dispatchEvent(d)}}if(a.support.touch="ontouchend"in document,a.support.touch){var e,b=a.ui.mouse.prototype,c=b._mouseInit,d=b._mouseDestroy;b._touchStart=function(a){var b=this;!e&&b._mouseCapture(a.originalEvent.changedTouches[0])&&(e=!0,b._touchMoved=!1,f(a,"mouseover"),f(a,"mousemove"),f(a,"mousedown"))},b._touchMove=function(a){e&&(this._touchMoved=!0,f(a,"mousemove"))},b._touchEnd=function(a){e&&(f(a,"mouseup"),f(a,"mouseout"),this._touchMoved||f(a,"click"),e=!1)},b._mouseInit=function(){var b=this;b.element.bind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),c.call(b)},b._mouseDestroy=function(){var b=this;b.element.unbind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b, +"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),d.call(b)}}}(jQuery); +/*! SmartMenus jQuery Plugin - v1.1.0 - September 17, 2017 + * http://www.smartmenus.org/ + * Copyright Vasil Dinkov, Vadikom Web Ltd. http://vadikom.com; Licensed MIT */(function(t){"function"==typeof define&&define.amd?define(["jquery"],t):"object"==typeof module&&"object"==typeof module.exports?module.exports=t(require("jquery")):t(jQuery)})(function($){function initMouseDetection(t){var e=".smartmenus_mouse";if(mouseDetectionEnabled||t)mouseDetectionEnabled&&t&&($(document).off(e),mouseDetectionEnabled=!1);else{var i=!0,s=null,o={mousemove:function(t){var e={x:t.pageX,y:t.pageY,timeStamp:(new Date).getTime()};if(s){var o=Math.abs(s.x-e.x),a=Math.abs(s.y-e.y);if((o>0||a>0)&&2>=o&&2>=a&&300>=e.timeStamp-s.timeStamp&&(mouse=!0,i)){var n=$(t.target).closest("a");n.is("a")&&$.each(menuTrees,function(){return $.contains(this.$root[0],n[0])?(this.itemEnter({currentTarget:n[0]}),!1):void 0}),i=!1}}s=e}};o[touchEvents?"touchstart":"pointerover pointermove pointerout MSPointerOver MSPointerMove MSPointerOut"]=function(t){isTouchEvent(t.originalEvent)&&(mouse=!1)},$(document).on(getEventsNS(o,e)), +mouseDetectionEnabled=!0}}function isTouchEvent(t){return!/^(4|mouse)$/.test(t.pointerType)}function getEventsNS(t,e){e||(e="");var i={};for(var s in t)i[s.split(" ").join(e+" ")+e]=t[s];return i}var menuTrees=[],mouse=!1,touchEvents="ontouchstart"in window,mouseDetectionEnabled=!1,requestAnimationFrame=window.requestAnimationFrame||function(t){return setTimeout(t,1e3/60)},cancelAnimationFrame=window.cancelAnimationFrame||function(t){clearTimeout(t)},canAnimate=!!$.fn.animate;return $.SmartMenus=function(t,e){this.$root=$(t),this.opts=e,this.rootId="",this.accessIdPrefix="",this.$subArrow=null,this.activatedItems=[],this.visibleSubMenus=[],this.showTimeout=0,this.hideTimeout=0,this.scrollTimeout=0,this.clickActivated=!1,this.focusActivated=!1,this.zIndexInc=0,this.idInc=0,this.$firstLink=null,this.$firstSub=null,this.disabled=!1,this.$disableOverlay=null,this.$touchScrollingSub=null,this.cssTransforms3d="perspective"in t.style||"webkitPerspective"in t.style,this.wasCollapsible=!1,this.init()},$.extend( +$.SmartMenus,{hideAll:function(){$.each(menuTrees,function(){this.menuHideAll()})},destroy:function(){for(;menuTrees.length;)menuTrees[0].destroy();initMouseDetection(!0)},prototype:{init:function(t){var e=this;if(!t){menuTrees.push(this),this.rootId=((new Date).getTime()+Math.random()+"").replace(/\D/g,""),this.accessIdPrefix="sm-"+this.rootId+"-",this.$root.hasClass("sm-rtl")&&(this.opts.rightToLeftSubMenus=!0);var i=".smartmenus";this.$root.data("smartmenus",this).attr("data-smartmenus-id",this.rootId).dataSM("level",1).on(getEventsNS({"mouseover focusin":$.proxy(this.rootOver,this),"mouseout focusout":$.proxy(this.rootOut,this),keydown:$.proxy(this.rootKeyDown,this)},i)).on(getEventsNS({mouseenter:$.proxy(this.itemEnter,this),mouseleave:$.proxy(this.itemLeave,this),mousedown:$.proxy(this.itemDown,this),focus:$.proxy(this.itemFocus,this),blur:$.proxy(this.itemBlur,this),click:$.proxy(this.itemClick,this)},i),"a"),i+=this.rootId,this.opts.hideOnClick&&$(document).on(getEventsNS({touchstart:$.proxy( +this.docTouchStart,this),touchmove:$.proxy(this.docTouchMove,this),touchend:$.proxy(this.docTouchEnd,this),click:$.proxy(this.docClick,this)},i)),$(window).on(getEventsNS({"resize orientationchange":$.proxy(this.winResize,this)},i)),this.opts.subIndicators&&(this.$subArrow=$("").addClass("sub-arrow"),this.opts.subIndicatorsText&&this.$subArrow.html(this.opts.subIndicatorsText)),initMouseDetection()}if(this.$firstSub=this.$root.find("ul").each(function(){e.menuInit($(this))}).eq(0),this.$firstLink=this.$root.find("a").eq(0),this.opts.markCurrentItem){var s=/(index|default)\.[^#\?\/]*/i,o=/#.*/,a=window.location.href.replace(s,""),n=a.replace(o,"");this.$root.find("a").each(function(){var t=this.href.replace(s,""),i=$(this);(t==a||t==n)&&(i.addClass("current"),e.opts.markCurrentTree&&i.parentsUntil("[data-smartmenus-id]","ul").each(function(){$(this).dataSM("parent-a").addClass("current")}))})}this.wasCollapsible=this.isCollapsible()},destroy:function(t){if(!t){var e=".smartmenus";this.$root.removeData( +"smartmenus").removeAttr("data-smartmenus-id").removeDataSM("level").off(e),e+=this.rootId,$(document).off(e),$(window).off(e),this.opts.subIndicators&&(this.$subArrow=null)}this.menuHideAll();var i=this;this.$root.find("ul").each(function(){var t=$(this);t.dataSM("scroll-arrows")&&t.dataSM("scroll-arrows").remove(),t.dataSM("shown-before")&&((i.opts.subMenusMinWidth||i.opts.subMenusMaxWidth)&&t.css({width:"",minWidth:"",maxWidth:""}).removeClass("sm-nowrap"),t.dataSM("scroll-arrows")&&t.dataSM("scroll-arrows").remove(),t.css({zIndex:"",top:"",left:"",marginLeft:"",marginTop:"",display:""})),0==(t.attr("id")||"").indexOf(i.accessIdPrefix)&&t.removeAttr("id")}).removeDataSM("in-mega").removeDataSM("shown-before").removeDataSM("scroll-arrows").removeDataSM("parent-a").removeDataSM("level").removeDataSM("beforefirstshowfired").removeAttr("role").removeAttr("aria-hidden").removeAttr("aria-labelledby").removeAttr("aria-expanded"),this.$root.find("a.has-submenu").each(function(){var t=$(this);0==t.attr("id" +).indexOf(i.accessIdPrefix)&&t.removeAttr("id")}).removeClass("has-submenu").removeDataSM("sub").removeAttr("aria-haspopup").removeAttr("aria-controls").removeAttr("aria-expanded").closest("li").removeDataSM("sub"),this.opts.subIndicators&&this.$root.find("span.sub-arrow").remove(),this.opts.markCurrentItem&&this.$root.find("a.current").removeClass("current"),t||(this.$root=null,this.$firstLink=null,this.$firstSub=null,this.$disableOverlay&&(this.$disableOverlay.remove(),this.$disableOverlay=null),menuTrees.splice($.inArray(this,menuTrees),1))},disable:function(t){if(!this.disabled){if(this.menuHideAll(),!t&&!this.opts.isPopup&&this.$root.is(":visible")){var e=this.$root.offset();this.$disableOverlay=$('
').css({position:"absolute",top:e.top,left:e.left,width:this.$root.outerWidth(),height:this.$root.outerHeight(),zIndex:this.getStartZIndex(!0),opacity:0}).appendTo(document.body)}this.disabled=!0}},docClick:function(t){return this.$touchScrollingSub?( +this.$touchScrollingSub=null,void 0):((this.visibleSubMenus.length&&!$.contains(this.$root[0],t.target)||$(t.target).closest("a").length)&&this.menuHideAll(),void 0)},docTouchEnd:function(){if(this.lastTouch){if(!(!this.visibleSubMenus.length||void 0!==this.lastTouch.x2&&this.lastTouch.x1!=this.lastTouch.x2||void 0!==this.lastTouch.y2&&this.lastTouch.y1!=this.lastTouch.y2||this.lastTouch.target&&$.contains(this.$root[0],this.lastTouch.target))){this.hideTimeout&&(clearTimeout(this.hideTimeout),this.hideTimeout=0);var t=this;this.hideTimeout=setTimeout(function(){t.menuHideAll()},350)}this.lastTouch=null}},docTouchMove:function(t){if(this.lastTouch){var e=t.originalEvent.touches[0];this.lastTouch.x2=e.pageX,this.lastTouch.y2=e.pageY}},docTouchStart:function(t){var e=t.originalEvent.touches[0];this.lastTouch={x1:e.pageX,y1:e.pageY,target:e.target}},enable:function(){this.disabled&&(this.$disableOverlay&&(this.$disableOverlay.remove(),this.$disableOverlay=null),this.disabled=!1)},getClosestMenu:function(t){for( +var e=$(t).closest("ul");e.dataSM("in-mega");)e=e.parent().closest("ul");return e[0]||null},getHeight:function(t){return this.getOffset(t,!0)},getOffset:function(t,e){var i;"none"==t.css("display")&&(i={position:t[0].style.position,visibility:t[0].style.visibility},t.css({position:"absolute",visibility:"hidden"}).show());var s=t[0].getBoundingClientRect&&t[0].getBoundingClientRect(),o=s&&(e?s.height||s.bottom-s.top:s.width||s.right-s.left);return o||0===o||(o=e?t[0].offsetHeight:t[0].offsetWidth),i&&t.hide().css(i),o},getStartZIndex:function(t){var e=parseInt(this[t?"$root":"$firstSub"].css("z-index"));return!t&&isNaN(e)&&(e=parseInt(this.$root.css("z-index"))),isNaN(e)?1:e},getTouchPoint:function(t){return t.touches&&t.touches[0]||t.changedTouches&&t.changedTouches[0]||t},getViewport:function(t){var e=t?"Height":"Width",i=document.documentElement["client"+e],s=window["inner"+e];return s&&(i=Math.min(i,s)),i},getViewportHeight:function(){return this.getViewport(!0)},getViewportWidth:function(){ +return this.getViewport()},getWidth:function(t){return this.getOffset(t)},handleEvents:function(){return!this.disabled&&this.isCSSOn()},handleItemEvents:function(t){return this.handleEvents()&&!this.isLinkInMegaMenu(t)},isCollapsible:function(){return"static"==this.$firstSub.css("position")},isCSSOn:function(){return"inline"!=this.$firstLink.css("display")},isFixed:function(){var t="fixed"==this.$root.css("position");return t||this.$root.parentsUntil("body").each(function(){return"fixed"==$(this).css("position")?(t=!0,!1):void 0}),t},isLinkInMegaMenu:function(t){return $(this.getClosestMenu(t[0])).hasClass("mega-menu")},isTouchMode:function(){return!mouse||this.opts.noMouseOver||this.isCollapsible()},itemActivate:function(t,e){var i=t.closest("ul"),s=i.dataSM("level");if(s>1&&(!this.activatedItems[s-2]||this.activatedItems[s-2][0]!=i.dataSM("parent-a")[0])){var o=this;$(i.parentsUntil("[data-smartmenus-id]","ul").get().reverse()).add(i).each(function(){o.itemActivate($(this).dataSM("parent-a"))})}if(( +!this.isCollapsible()||e)&&this.menuHideSubMenus(this.activatedItems[s-1]&&this.activatedItems[s-1][0]==t[0]?s:s-1),this.activatedItems[s-1]=t,this.$root.triggerHandler("activate.smapi",t[0])!==!1){var a=t.dataSM("sub");a&&(this.isTouchMode()||!this.opts.showOnClick||this.clickActivated)&&this.menuShow(a)}},itemBlur:function(t){var e=$(t.currentTarget);this.handleItemEvents(e)&&this.$root.triggerHandler("blur.smapi",e[0])},itemClick:function(t){var e=$(t.currentTarget);if(this.handleItemEvents(e)){if(this.$touchScrollingSub&&this.$touchScrollingSub[0]==e.closest("ul")[0])return this.$touchScrollingSub=null,t.stopPropagation(),!1;if(this.$root.triggerHandler("click.smapi",e[0])===!1)return!1;var i=$(t.target).is(".sub-arrow"),s=e.dataSM("sub"),o=s?2==s.dataSM("level"):!1,a=this.isCollapsible(),n=/toggle$/.test(this.opts.collapsibleBehavior),r=/link$/.test(this.opts.collapsibleBehavior),h=/^accordion/.test(this.opts.collapsibleBehavior);if(s&&!s.is(":visible")){if((!r||!a||i)&&(this.opts.showOnClick&&o&&( +this.clickActivated=!0),this.itemActivate(e,h),s.is(":visible")))return this.focusActivated=!0,!1}else if(a&&(n||i))return this.itemActivate(e,h),this.menuHide(s),n&&(this.focusActivated=!1),!1;return this.opts.showOnClick&&o||e.hasClass("disabled")||this.$root.triggerHandler("select.smapi",e[0])===!1?!1:void 0}},itemDown:function(t){var e=$(t.currentTarget);this.handleItemEvents(e)&&e.dataSM("mousedown",!0)},itemEnter:function(t){var e=$(t.currentTarget);if(this.handleItemEvents(e)){if(!this.isTouchMode()){this.showTimeout&&(clearTimeout(this.showTimeout),this.showTimeout=0);var i=this;this.showTimeout=setTimeout(function(){i.itemActivate(e)},this.opts.showOnClick&&1==e.closest("ul").dataSM("level")?1:this.opts.showTimeout)}this.$root.triggerHandler("mouseenter.smapi",e[0])}},itemFocus:function(t){var e=$(t.currentTarget);this.handleItemEvents(e)&&(!this.focusActivated||this.isTouchMode()&&e.dataSM("mousedown")||this.activatedItems.length&&this.activatedItems[this.activatedItems.length-1][0]==e[0 +]||this.itemActivate(e,!0),this.$root.triggerHandler("focus.smapi",e[0]))},itemLeave:function(t){var e=$(t.currentTarget);this.handleItemEvents(e)&&(this.isTouchMode()||(e[0].blur(),this.showTimeout&&(clearTimeout(this.showTimeout),this.showTimeout=0)),e.removeDataSM("mousedown"),this.$root.triggerHandler("mouseleave.smapi",e[0]))},menuHide:function(t){if(this.$root.triggerHandler("beforehide.smapi",t[0])!==!1&&(canAnimate&&t.stop(!0,!0),"none"!=t.css("display"))){var e=function(){t.css("z-index","")};this.isCollapsible()?canAnimate&&this.opts.collapsibleHideFunction?this.opts.collapsibleHideFunction.call(this,t,e):t.hide(this.opts.collapsibleHideDuration,e):canAnimate&&this.opts.hideFunction?this.opts.hideFunction.call(this,t,e):t.hide(this.opts.hideDuration,e),t.dataSM("scroll")&&(this.menuScrollStop(t),t.css({"touch-action":"","-ms-touch-action":"","-webkit-transform":"",transform:""}).off(".smartmenus_scroll").removeDataSM("scroll").dataSM("scroll-arrows").hide()),t.dataSM("parent-a").removeClass( +"highlighted").attr("aria-expanded","false"),t.attr({"aria-expanded":"false","aria-hidden":"true"});var i=t.dataSM("level");this.activatedItems.splice(i-1,1),this.visibleSubMenus.splice($.inArray(t,this.visibleSubMenus),1),this.$root.triggerHandler("hide.smapi",t[0])}},menuHideAll:function(){this.showTimeout&&(clearTimeout(this.showTimeout),this.showTimeout=0);for(var t=this.opts.isPopup?1:0,e=this.visibleSubMenus.length-1;e>=t;e--)this.menuHide(this.visibleSubMenus[e]);this.opts.isPopup&&(canAnimate&&this.$root.stop(!0,!0),this.$root.is(":visible")&&(canAnimate&&this.opts.hideFunction?this.opts.hideFunction.call(this,this.$root):this.$root.hide(this.opts.hideDuration))),this.activatedItems=[],this.visibleSubMenus=[],this.clickActivated=!1,this.focusActivated=!1,this.zIndexInc=0,this.$root.triggerHandler("hideAll.smapi")},menuHideSubMenus:function(t){for(var e=this.activatedItems.length-1;e>=t;e--){var i=this.activatedItems[e].dataSM("sub");i&&this.menuHide(i)}},menuInit:function(t){if(!t.dataSM("in-mega")){ +t.hasClass("mega-menu")&&t.find("ul").dataSM("in-mega",!0);for(var e=2,i=t[0];(i=i.parentNode.parentNode)!=this.$root[0];)e++;var s=t.prevAll("a").eq(-1);s.length||(s=t.prevAll().find("a").eq(-1)),s.addClass("has-submenu").dataSM("sub",t),t.dataSM("parent-a",s).dataSM("level",e).parent().dataSM("sub",t);var o=s.attr("id")||this.accessIdPrefix+ ++this.idInc,a=t.attr("id")||this.accessIdPrefix+ ++this.idInc;s.attr({id:o,"aria-haspopup":"true","aria-controls":a,"aria-expanded":"false"}),t.attr({id:a,role:"group","aria-hidden":"true","aria-labelledby":o,"aria-expanded":"false"}),this.opts.subIndicators&&s[this.opts.subIndicatorsPos](this.$subArrow.clone())}},menuPosition:function(t){var e,i,s=t.dataSM("parent-a"),o=s.closest("li"),a=o.parent(),n=t.dataSM("level"),r=this.getWidth(t),h=this.getHeight(t),u=s.offset(),l=u.left,c=u.top,d=this.getWidth(s),m=this.getHeight(s),p=$(window),f=p.scrollLeft(),v=p.scrollTop(),b=this.getViewportWidth(),S=this.getViewportHeight(),g=a.parent().is("[data-sm-horizontal-sub]" +)||2==n&&!a.hasClass("sm-vertical"),M=this.opts.rightToLeftSubMenus&&!o.is("[data-sm-reverse]")||!this.opts.rightToLeftSubMenus&&o.is("[data-sm-reverse]"),w=2==n?this.opts.mainMenuSubOffsetX:this.opts.subMenusSubOffsetX,T=2==n?this.opts.mainMenuSubOffsetY:this.opts.subMenusSubOffsetY;if(g?(e=M?d-r-w:w,i=this.opts.bottomToTopSubMenus?-h-T:m+T):(e=M?w-r:d-w,i=this.opts.bottomToTopSubMenus?m-T-h:T),this.opts.keepInViewport){var y=l+e,I=c+i;if(M&&f>y?e=g?f-y+e:d-w:!M&&y+r>f+b&&(e=g?f+b-r-y+e:w-r),g||(S>h&&I+h>v+S?i+=v+S-h-I:(h>=S||v>I)&&(i+=v-I)),g&&(I+h>v+S+.49||v>I)||!g&&h>S+.49){var x=this;t.dataSM("scroll-arrows")||t.dataSM("scroll-arrows",$([$('')[0],$('')[0]]).on({mouseenter:function(){t.dataSM("scroll").up=$(this).hasClass("scroll-up"),x.menuScroll(t)},mouseleave:function(e){x.menuScrollStop(t),x.menuScrollOut(t,e)},"mousewheel DOMMouseScroll":function(t){ +t.preventDefault()}}).insertAfter(t));var A=".smartmenus_scroll";if(t.dataSM("scroll",{y:this.cssTransforms3d?0:i-m,step:1,itemH:m,subH:h,arrowDownH:this.getHeight(t.dataSM("scroll-arrows").eq(1))}).on(getEventsNS({mouseover:function(e){x.menuScrollOver(t,e)},mouseout:function(e){x.menuScrollOut(t,e)},"mousewheel DOMMouseScroll":function(e){x.menuScrollMousewheel(t,e)}},A)).dataSM("scroll-arrows").css({top:"auto",left:"0",marginLeft:e+(parseInt(t.css("border-left-width"))||0),width:r-(parseInt(t.css("border-left-width"))||0)-(parseInt(t.css("border-right-width"))||0),zIndex:t.css("z-index")}).eq(g&&this.opts.bottomToTopSubMenus?0:1).show(),this.isFixed()){var C={};C[touchEvents?"touchstart touchmove touchend":"pointerdown pointermove pointerup MSPointerDown MSPointerMove MSPointerUp"]=function(e){x.menuScrollTouch(t,e)},t.css({"touch-action":"none","-ms-touch-action":"none"}).on(getEventsNS(C,A))}}}t.css({top:"auto",left:"0",marginLeft:e,marginTop:i-m})},menuScroll:function(t,e,i){var s,o=t.dataSM("scroll"), +a=t.dataSM("scroll-arrows"),n=o.up?o.upEnd:o.downEnd;if(!e&&o.momentum){if(o.momentum*=.92,s=o.momentum,.5>s)return this.menuScrollStop(t),void 0}else s=i||(e||!this.opts.scrollAccelerate?this.opts.scrollStep:Math.floor(o.step));var r=t.dataSM("level");if(this.activatedItems[r-1]&&this.activatedItems[r-1].dataSM("sub")&&this.activatedItems[r-1].dataSM("sub").is(":visible")&&this.menuHideSubMenus(r-1),o.y=o.up&&o.y>=n||!o.up&&n>=o.y?o.y:Math.abs(n-o.y)>s?o.y+(o.up?s:-s):n,t.css(this.cssTransforms3d?{"-webkit-transform":"translate3d(0, "+o.y+"px, 0)",transform:"translate3d(0, "+o.y+"px, 0)"}:{marginTop:o.y}),mouse&&(o.up&&o.y>o.downEnd||!o.up&&o.y0;t.dataSM("scroll-arrows").eq(i?0:1).is(":visible")&&(t.dataSM("scroll").up=i,this.menuScroll(t,!0))}e.preventDefault()},menuScrollOut:function(t,e){mouse&&(/^scroll-(up|down)/.test((e.relatedTarget||"").className)||(t[0]==e.relatedTarget||$.contains(t[0],e.relatedTarget))&&this.getClosestMenu(e.relatedTarget)==t[0]||t.dataSM("scroll-arrows").css("visibility","hidden"))},menuScrollOver:function(t,e){if(mouse&&!/^scroll-(up|down)/.test(e.target.className)&&this.getClosestMenu(e.target)==t[0]){this.menuScrollRefreshData(t);var i=t.dataSM("scroll"),s=$(window).scrollTop()-t.dataSM("parent-a").offset().top-i.itemH;t.dataSM("scroll-arrows").eq(0).css("margin-top",s).end().eq(1).css("margin-top",s+this.getViewportHeight()-i.arrowDownH).end().css("visibility","visible")}},menuScrollRefreshData:function(t){var e=t.dataSM("scroll"),i=$(window).scrollTop()-t.dataSM("parent-a").offset().top-e.itemH;this.cssTransforms3d&&(i=-(parseFloat(t.css("margin-top"))-i)),$.extend(e,{upEnd:i, +downEnd:i+this.getViewportHeight()-e.subH})},menuScrollStop:function(t){return this.scrollTimeout?(cancelAnimationFrame(this.scrollTimeout),this.scrollTimeout=0,t.dataSM("scroll").step=1,!0):void 0},menuScrollTouch:function(t,e){if(e=e.originalEvent,isTouchEvent(e)){var i=this.getTouchPoint(e);if(this.getClosestMenu(i.target)==t[0]){var s=t.dataSM("scroll");if(/(start|down)$/i.test(e.type))this.menuScrollStop(t)?(e.preventDefault(),this.$touchScrollingSub=t):this.$touchScrollingSub=null,this.menuScrollRefreshData(t),$.extend(s,{touchStartY:i.pageY,touchStartTime:e.timeStamp});else if(/move$/i.test(e.type)){var o=void 0!==s.touchY?s.touchY:s.touchStartY;if(void 0!==o&&o!=i.pageY){this.$touchScrollingSub=t;var a=i.pageY>o;void 0!==s.up&&s.up!=a&&$.extend(s,{touchStartY:i.pageY,touchStartTime:e.timeStamp}),$.extend(s,{up:a,touchY:i.pageY}),this.menuScroll(t,!0,Math.abs(i.pageY-o))}e.preventDefault()}else void 0!==s.touchY&&((s.momentum=15*Math.pow(Math.abs(i.pageY-s.touchStartY)/(e.timeStamp-s.touchStartTime),2) +)&&(this.menuScrollStop(t),this.menuScroll(t),e.preventDefault()),delete s.touchY)}}},menuShow:function(t){if((t.dataSM("beforefirstshowfired")||(t.dataSM("beforefirstshowfired",!0),this.$root.triggerHandler("beforefirstshow.smapi",t[0])!==!1))&&this.$root.triggerHandler("beforeshow.smapi",t[0])!==!1&&(t.dataSM("shown-before",!0),canAnimate&&t.stop(!0,!0),!t.is(":visible"))){var e=t.dataSM("parent-a"),i=this.isCollapsible();if((this.opts.keepHighlighted||i)&&e.addClass("highlighted"),i)t.removeClass("sm-nowrap").css({zIndex:"",width:"auto",minWidth:"",maxWidth:"",top:"",left:"",marginLeft:"",marginTop:""});else{if(t.css("z-index",this.zIndexInc=(this.zIndexInc||this.getStartZIndex())+1),(this.opts.subMenusMinWidth||this.opts.subMenusMaxWidth)&&(t.css({width:"auto",minWidth:"",maxWidth:""}).addClass("sm-nowrap"),this.opts.subMenusMinWidth&&t.css("min-width",this.opts.subMenusMinWidth),this.opts.subMenusMaxWidth)){var s=this.getWidth(t);t.css("max-width",this.opts.subMenusMaxWidth),s>this.getWidth(t +)&&t.removeClass("sm-nowrap").css("width",this.opts.subMenusMaxWidth)}this.menuPosition(t)}var o=function(){t.css("overflow","")};i?canAnimate&&this.opts.collapsibleShowFunction?this.opts.collapsibleShowFunction.call(this,t,o):t.show(this.opts.collapsibleShowDuration,o):canAnimate&&this.opts.showFunction?this.opts.showFunction.call(this,t,o):t.show(this.opts.showDuration,o),e.attr("aria-expanded","true"),t.attr({"aria-expanded":"true","aria-hidden":"false"}),this.visibleSubMenus.push(t),this.$root.triggerHandler("show.smapi",t[0])}},popupHide:function(t){this.hideTimeout&&(clearTimeout(this.hideTimeout),this.hideTimeout=0);var e=this;this.hideTimeout=setTimeout(function(){e.menuHideAll()},t?1:this.opts.hideTimeout)},popupShow:function(t,e){if(!this.opts.isPopup)return alert('SmartMenus jQuery Error:\n\nIf you want to show this menu via the "popupShow" method, set the isPopup:true option.'),void 0;if(this.hideTimeout&&(clearTimeout(this.hideTimeout),this.hideTimeout=0),this.$root.dataSM("shown-before",!0), +canAnimate&&this.$root.stop(!0,!0),!this.$root.is(":visible")){this.$root.css({left:t,top:e});var i=this,s=function(){i.$root.css("overflow","")};canAnimate&&this.opts.showFunction?this.opts.showFunction.call(this,this.$root,s):this.$root.show(this.opts.showDuration,s),this.visibleSubMenus[0]=this.$root}},refresh:function(){this.destroy(!0),this.init(!0)},rootKeyDown:function(t){if(this.handleEvents())switch(t.keyCode){case 27:var e=this.activatedItems[0];if(e){this.menuHideAll(),e[0].focus();var i=e.dataSM("sub");i&&this.menuHide(i)}break;case 32:var s=$(t.target);if(s.is("a")&&this.handleItemEvents(s)){var i=s.dataSM("sub");i&&!i.is(":visible")&&(this.itemClick({currentTarget:t.target}),t.preventDefault())}}},rootOut:function(t){if(this.handleEvents()&&!this.isTouchMode()&&t.target!=this.$root[0]&&(this.hideTimeout&&(clearTimeout(this.hideTimeout),this.hideTimeout=0),!this.opts.showOnClick||!this.opts.hideOnClick)){var e=this;this.hideTimeout=setTimeout(function(){e.menuHideAll()},this.opts.hideTimeout)}}, +rootOver:function(t){this.handleEvents()&&!this.isTouchMode()&&t.target!=this.$root[0]&&this.hideTimeout&&(clearTimeout(this.hideTimeout),this.hideTimeout=0)},winResize:function(t){if(this.handleEvents()){if(!("onorientationchange"in window)||"orientationchange"==t.type){var e=this.isCollapsible();this.wasCollapsible&&e||(this.activatedItems.length&&this.activatedItems[this.activatedItems.length-1][0].blur(),this.menuHideAll()),this.wasCollapsible=e}}else if(this.$disableOverlay){var i=this.$root.offset();this.$disableOverlay.css({top:i.top,left:i.left,width:this.$root.outerWidth(),height:this.$root.outerHeight()})}}}}),$.fn.dataSM=function(t,e){return e?this.data(t+"_smartmenus",e):this.data(t+"_smartmenus")},$.fn.removeDataSM=function(t){return this.removeData(t+"_smartmenus")},$.fn.smartmenus=function(options){if("string"==typeof options){var args=arguments,method=options;return Array.prototype.shift.call(args),this.each(function(){var t=$(this).data("smartmenus");t&&t[method]&&t[method].apply(t,args)})} +return this.each(function(){var dataOpts=$(this).data("sm-options")||null;if(dataOpts)try{dataOpts=eval("("+dataOpts+")")}catch(e){dataOpts=null,alert('ERROR\n\nSmartMenus jQuery init:\nInvalid "data-sm-options" attribute value syntax.')}new $.SmartMenus(this,$.extend({},$.fn.smartmenus.defaults,options,dataOpts))})},$.fn.smartmenus.defaults={isPopup:!1,mainMenuSubOffsetX:0,mainMenuSubOffsetY:0,subMenusSubOffsetX:0,subMenusSubOffsetY:0,subMenusMinWidth:"10em",subMenusMaxWidth:"20em",subIndicators:!0,subIndicatorsPos:"append",subIndicatorsText:"",scrollStep:30,scrollAccelerate:!0,showTimeout:250,hideTimeout:500,showDuration:0,showFunction:null,hideDuration:0,hideFunction:function(t,e){t.fadeOut(200,e)},collapsibleShowDuration:0,collapsibleShowFunction:function(t,e){t.slideDown(200,e)},collapsibleHideDuration:0,collapsibleHideFunction:function(t,e){t.slideUp(200,e)},showOnClick:!1,hideOnClick:!0,noMouseOver:!1,keepInViewport:!0,keepHighlighted:!0,markCurrentItem:!1,markCurrentTree:!0,rightToLeftSubMenus:!1, +bottomToTopSubMenus:!1,collapsibleBehavior:"default"},$}); diff --git a/jwt_8h.html b/jwt_8h.html new file mode 100644 index 00000000..96734564 --- /dev/null +++ b/jwt_8h.html @@ -0,0 +1,746 @@ + + + + + + + +libjwt-2.1.1: jwt.h File Reference + + + + + + + + + + + + +
+
+ + + + + + +
+
libjwt-2.1.1 +
+
+
+ + + + + + + +
+
+ +
+
+
+ +
+
+ +
jwt.h File Reference
+
+
+ +

JWT C Library. +More...

+
#include <stdio.h>
+#include <time.h>
+
+Include dependency graph for jwt.h:
+
+
+ + + + + + + +
+
+

Go to the source code of this file.

+ + + + + +

+Data Structures

struct  jwt_key_t
 Structure used by key provider to return a key. More...
 
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+Macros

#define DEPRECATED(func)
 
#define JWT_EXPORT
 
#define JWT_ALG_INVAL
 
#define JWT_VALIDATION_SUCCESS
 JWT Validation exception types.
 
#define JWT_VALIDATION_ERROR
 
#define JWT_VALIDATION_ALG_MISMATCH
 
#define JWT_VALIDATION_EXPIRED
 
#define JWT_VALIDATION_TOO_NEW
 
#define JWT_VALIDATION_ISS_MISMATCH
 
#define JWT_VALIDATION_SUB_MISMATCH
 
#define JWT_VALIDATION_AUD_MISMATCH
 
#define JWT_VALIDATION_GRANT_MISSING
 
#define JWT_VALIDATION_GRANT_MISMATCH
 
+ + + + + + + + + + + + + + + + + + + + +

+Typedefs

typedef struct jwt jwt_t
 Opaque JWT object.
 
typedef struct jwt_valid jwt_valid_t
 Opaque JWT validation object.
 
typedef enum jwt_alg jwt_alg_t
 JWT algorithm types.
 
typedef void *(* jwt_malloc_t) (size_t)
 JWT Memory allocation overrides.
 
typedef void *(* jwt_realloc_t) (void *, size_t)
 
typedef void(* jwt_free_t) (void *)
 
typedef int(* jwt_key_p_t) (const jwt_t *, jwt_key_t *)
 Key provider - inspects the JWT to obtain the key used to verify the signature.
 
+ + + + +

+Enumerations

enum  jwt_alg {
+  JWT_ALG_NONE +, JWT_ALG_HS256 +, JWT_ALG_HS384 +, JWT_ALG_HS512 +,
+  JWT_ALG_RS256 +, JWT_ALG_RS384 +, JWT_ALG_RS512 +, JWT_ALG_ES256 +,
+  JWT_ALG_ES384 +, JWT_ALG_ES512 +, JWT_ALG_PS256 +, JWT_ALG_PS384 +,
+  JWT_ALG_PS512 +, JWT_ALG_ES256K +, JWT_ALG_EDDSA +, JWT_ALG_TERM +
+ }
 JWT algorithm types. More...
 
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+Functions

const char * jwt_get_crypto_ops (void)
 Retrieve the name of the current crypto operations being used.
 
int jwt_set_crypto_ops (const char *opname)
 Set the crypto operations to the named set.
 
int jwt_new (jwt_t **jwt)
 Allocate a new, empty, JWT object.
 
int jwt_decode (jwt_t **jwt, const char *token, const unsigned char *key, int key_len)
 Decode an existing JWT and allocate a new JWT object from it.
 
int jwt_decode_2 (jwt_t **jwt, const char *token, jwt_key_p_t key_provider)
 Like jwt_decode(), but the key will be obtained via the key provider.
 
void jwt_free (jwt_t *jwt)
 Free a JWT object and any other resources it is using.
 
jwt_tjwt_dup (jwt_t *jwt)
 Duplicate an existing JWT object.
 
const char * jwt_get_grant (jwt_t *jwt, const char *grant)
 Return the value of a string grant.
 
long jwt_get_grant_int (jwt_t *jwt, const char *grant)
 Return the value of an integer grant.
 
int jwt_get_grant_bool (jwt_t *jwt, const char *grant)
 Return the value of an boolean grant.
 
char * jwt_get_grants_json (jwt_t *jwt, const char *grant)
 Return the value of a grant as JSON encoded object string.
 
int jwt_add_grant (jwt_t *jwt, const char *grant, const char *val)
 Add a new string grant to this JWT object.
 
int jwt_add_grant_int (jwt_t *jwt, const char *grant, long val)
 Add a new integer grant to this JWT object.
 
int jwt_add_grant_bool (jwt_t *jwt, const char *grant, int val)
 Add a new boolean grant to this JWT object.
 
int jwt_add_grants_json (jwt_t *jwt, const char *json)
 Add grants from a JSON encoded object string.
 
int jwt_del_grants (jwt_t *jwt, const char *grant)
 Delete a grant from this JWT object.
 
const char * jwt_get_header (jwt_t *jwt, const char *header)
 Return the value of a string header.
 
long jwt_get_header_int (jwt_t *jwt, const char *header)
 Return the value of an integer header.
 
int jwt_get_header_bool (jwt_t *jwt, const char *header)
 Return the value of an boolean header.
 
char * jwt_get_headers_json (jwt_t *jwt, const char *header)
 Return the value of a header as JSON encoded object string.
 
int jwt_add_header (jwt_t *jwt, const char *header, const char *val)
 Add a new string header to this JWT object.
 
int jwt_add_header_int (jwt_t *jwt, const char *header, long val)
 Add a new integer header to this JWT object.
 
int jwt_add_header_bool (jwt_t *jwt, const char *header, int val)
 Add a new boolean header to this JWT object.
 
int jwt_add_headers_json (jwt_t *jwt, const char *json)
 Add headers from a JSON encoded object string.
 
int jwt_del_headers (jwt_t *jwt, const char *header)
 Delete a header from this JWT object.
 
int jwt_dump_fp (jwt_t *jwt, FILE *fp, int pretty)
 Output plain text representation to a FILE pointer.
 
char * jwt_dump_str (jwt_t *jwt, int pretty)
 Return plain text representation as a string.
 
char * jwt_dump_grants_str (jwt_t *jwt, int pretty)
 Return plain text representation of grants as a string.
 
int jwt_encode_fp (jwt_t *jwt, FILE *fp)
 Fully encode a JWT object and write it to FILE.
 
char * jwt_encode_str (jwt_t *jwt)
 Fully encode a JWT object and return as a string.
 
void jwt_free_str (char *str)
 Free a string returned from the library.
 
int jwt_set_alg (jwt_t *jwt, jwt_alg_t alg, const unsigned char *key, int len)
 Set an algorithm from jwt_alg_t for this JWT object.
 
jwt_alg_t jwt_get_alg (const jwt_t *jwt)
 Get the jwt_alg_t set for this JWT object.
 
const char * jwt_alg_str (jwt_alg_t alg)
 Convert alg type to it's string representation.
 
jwt_alg_t jwt_str_alg (const char *alg)
 Convert alg string to type.
 
int jwt_set_alloc (jwt_malloc_t pmalloc, jwt_realloc_t prealloc, jwt_free_t pfree)
 Set functions to be used for allocating and freeing memory.
 
void jwt_get_alloc (jwt_malloc_t *pmalloc, jwt_realloc_t *prealloc, jwt_free_t *pfree)
 Get functions used for allocating and freeing memory.
 
unsigned int jwt_validate (jwt_t *jwt, jwt_valid_t *jwt_valid)
 Validate a JWT object with a validation object.
 
int jwt_valid_new (jwt_valid_t **jwt_valid, jwt_alg_t alg)
 Allocate a new, JWT validation object.
 
void jwt_valid_free (jwt_valid_t *jwt_valid)
 Free a JWT validation object and any other resources it is using.
 
unsigned int jwt_valid_get_status (jwt_valid_t *jwt_valid)
 Return the status string for the validation object.
 
time_t jwt_valid_get_nbf_leeway (jwt_valid_t *jwt_valid)
 Return the nbf_leeway value set.
 
time_t jwt_valid_get_exp_leeway (jwt_valid_t *jwt_valid)
 Return the exp_leeway value set.
 
int jwt_valid_add_grant (jwt_valid_t *jwt_valid, const char *grant, const char *val)
 Add a new string grant requirement to this JWT validation object.
 
const char * jwt_valid_get_grant (jwt_valid_t *jwt_valid, const char *grant)
 Return the value of a string required grant.
 
int jwt_valid_add_grant_int (jwt_valid_t *jwt_valid, const char *grant, long val)
 Add a new integer grant requirement to this JWT validation object.
 
long jwt_valid_get_grant_int (jwt_valid_t *jwt_valid, const char *grant)
 Return the value of an integer required grant.
 
int jwt_valid_add_grant_bool (jwt_valid_t *jwt_valid, const char *grant, int val)
 Add a new boolean required grant to this JWT validation object.
 
int jwt_valid_get_grant_bool (jwt_valid_t *jwt_valid, const char *grant)
 Return the value of an boolean required grant.
 
int jwt_valid_add_grants_json (jwt_valid_t *jwt_valid, const char *json)
 Add required grants from a JSON encoded object string.
 
char * jwt_valid_get_grants_json (jwt_valid_t *jwt_valid, const char *grant)
 Return the value of a grant as JSON encoded object string.
 
int jwt_valid_del_grants (jwt_valid_t *jwt_valid, const char *grant)
 Delete a grant from this JWT object.
 
int jwt_valid_set_now (jwt_valid_t *jwt_valid, const time_t now)
 Set the time for which expires and not-before claims should be evaluated.
 
int jwt_valid_set_nbf_leeway (jwt_valid_t *jwt_valid, const time_t nbf_leeway)
 Set the nbf_leeway value as defined in: https://www.rfc-editor.org/rfc/rfc7519#section-4.1.5.
 
int jwt_valid_set_exp_leeway (jwt_valid_t *jwt_valid, const time_t exp_leeway)
 Set the exp_leeway value as defined in: https://www.rfc-editor.org/rfc/rfc7519#section-4.1.4.
 
int jwt_valid_set_headers (jwt_valid_t *jwt_valid, int hdr)
 Set validation for replicated claims in headers.
 
char * jwt_exception_str (unsigned int exceptions)
 Parses exceptions and returns a comma delimited and human-readable string.
 
+

Detailed Description

+

JWT C Library.

+ +

Definition in file jwt.h.

+

Macro Definition Documentation

+ +

◆ DEPRECATED

+ +
+
+ + + + + + + +
#define DEPRECATED( func)
+
+ +

Definition at line 40 of file jwt.h.

+ +
+
+ +

◆ JWT_ALG_INVAL

+ +
+
+ + + + +
#define JWT_ALG_INVAL
+
+ +

Definition at line 75 of file jwt.h.

+ +
+
+ +

◆ JWT_EXPORT

+ + + +

◆ JWT_VALIDATION_ALG_MISMATCH

+ +
+
+ + + + +
#define JWT_VALIDATION_ALG_MISMATCH
+
+ +

Definition at line 80 of file jwt.h.

+ +
+
+ +

◆ JWT_VALIDATION_AUD_MISMATCH

+ +
+
+ + + + +
#define JWT_VALIDATION_AUD_MISMATCH
+
+ +

Definition at line 85 of file jwt.h.

+ +
+
+ +

◆ JWT_VALIDATION_ERROR

+ +
+
+ + + + +
#define JWT_VALIDATION_ERROR
+
+ +

Definition at line 79 of file jwt.h.

+ +
+
+ +

◆ JWT_VALIDATION_EXPIRED

+ +
+
+ + + + +
#define JWT_VALIDATION_EXPIRED
+
+ +

Definition at line 81 of file jwt.h.

+ +
+
+ +

◆ JWT_VALIDATION_GRANT_MISMATCH

+ +
+
+ + + + +
#define JWT_VALIDATION_GRANT_MISMATCH
+
+ +

Definition at line 87 of file jwt.h.

+ +
+
+ +

◆ JWT_VALIDATION_GRANT_MISSING

+ +
+
+ + + + +
#define JWT_VALIDATION_GRANT_MISSING
+
+ +

Definition at line 86 of file jwt.h.

+ +
+
+ +

◆ JWT_VALIDATION_ISS_MISMATCH

+ +
+
+ + + + +
#define JWT_VALIDATION_ISS_MISMATCH
+
+ +

Definition at line 83 of file jwt.h.

+ +
+
+ +

◆ JWT_VALIDATION_SUB_MISMATCH

+ +
+
+ + + + +
#define JWT_VALIDATION_SUB_MISMATCH
+
+ +

Definition at line 84 of file jwt.h.

+ +
+
+ +

◆ JWT_VALIDATION_SUCCESS

+ +
+
+ + + + +
#define JWT_VALIDATION_SUCCESS
+
+ +

JWT Validation exception types.

+

These are bit values.

+ +

Definition at line 78 of file jwt.h.

+ +
+
+ +

◆ JWT_VALIDATION_TOO_NEW

+ +
+
+ + + + +
#define JWT_VALIDATION_TOO_NEW
+
+ +

Definition at line 82 of file jwt.h.

+ +
+
+

Typedef Documentation

+ +

◆ jwt_alg_t

+ +
+
+ + + + +
typedef enum jwt_alg jwt_alg_t
+
+ +

JWT algorithm types.

+ +
+
+ +

◆ jwt_free_t

+ +
+
+ + + + +
typedef void(* jwt_free_t) (void *)
+
+ +

Definition at line 92 of file jwt.h.

+ +
+
+ +

◆ jwt_key_p_t

+ +
+
+ + + + +
typedef int(* jwt_key_p_t) (const jwt_t *, jwt_key_t *)
+
+ +

Key provider - inspects the JWT to obtain the key used to verify the signature.

+ +

Definition at line 101 of file jwt.h.

+ +
+
+ +

◆ jwt_malloc_t

+ +
+
+ + + + +
typedef void *(* jwt_malloc_t) (size_t)
+
+ +

JWT Memory allocation overrides.

+ +

Definition at line 90 of file jwt.h.

+ +
+
+ +

◆ jwt_realloc_t

+ +
+
+ + + + +
typedef void *(* jwt_realloc_t) (void *, size_t)
+
+ +

Definition at line 91 of file jwt.h.

+ +
+
+ +

◆ jwt_t

+ +
+
+ + + + +
typedef struct jwt jwt_t
+
+ +

Opaque JWT object.

+ +

Definition at line 50 of file jwt.h.

+ +
+
+ +

◆ jwt_valid_t

+ +
+
+ + + + +
typedef struct jwt_valid jwt_valid_t
+
+ +

Opaque JWT validation object.

+ +

Definition at line 53 of file jwt.h.

+ +
+
+

Enumeration Type Documentation

+ +

◆ jwt_alg

+ +
+
+ + + + +
enum jwt_alg
+
+ +

JWT algorithm types.

+ + + + + + + + + + + + + + + + + +
Enumerator
JWT_ALG_NONE 
JWT_ALG_HS256 
JWT_ALG_HS384 
JWT_ALG_HS512 
JWT_ALG_RS256 
JWT_ALG_RS384 
JWT_ALG_RS512 
JWT_ALG_ES256 
JWT_ALG_ES384 
JWT_ALG_ES512 
JWT_ALG_PS256 
JWT_ALG_PS384 
JWT_ALG_PS512 
JWT_ALG_ES256K 
JWT_ALG_EDDSA 
JWT_ALG_TERM 
+ +

Definition at line 56 of file jwt.h.

+ +
+
+
+
+ + + + diff --git a/jwt_8h.js b/jwt_8h.js new file mode 100644 index 00000000..1dbfb21c --- /dev/null +++ b/jwt_8h.js @@ -0,0 +1,99 @@ +var jwt_8h = +[ + [ "jwt_key_t", "structjwt__key__t.html", "structjwt__key__t" ], + [ "DEPRECATED", "jwt_8h.html#aaf225a2b96038de3a42d61a1fa63654b", null ], + [ "JWT_ALG_INVAL", "jwt_8h.html#a3541fa2951819d49dea3cac216d1bccb", null ], + [ "JWT_EXPORT", "jwt_8h.html#a2b5e67463c9576aa2e7bcceb9f5e6ac1", null ], + [ "JWT_VALIDATION_ALG_MISMATCH", "jwt_8h.html#a8e9d81fe105e2048a666101c00fa312d", null ], + [ "JWT_VALIDATION_AUD_MISMATCH", "jwt_8h.html#ac9fe4bfe03b722d417edfbd407abda28", null ], + [ "JWT_VALIDATION_ERROR", "jwt_8h.html#a78733e4e4caeb3969078f8b189686da6", null ], + [ "JWT_VALIDATION_EXPIRED", "jwt_8h.html#ab8dddabec9cb37a37d6dd9c932ea85ed", null ], + [ "JWT_VALIDATION_GRANT_MISMATCH", "jwt_8h.html#a6bdcdf4d7f00e5a86a47ff7b9da416a6", null ], + [ "JWT_VALIDATION_GRANT_MISSING", "jwt_8h.html#a809618ca6f67874e9d1afcaccf72a5da", null ], + [ "JWT_VALIDATION_ISS_MISMATCH", "jwt_8h.html#ac7b655bdd310cf4354f10b418ac5ebe0", null ], + [ "JWT_VALIDATION_SUB_MISMATCH", "jwt_8h.html#af37f2cce1c494f1c594fba518b3e3cc1", null ], + [ "JWT_VALIDATION_SUCCESS", "jwt_8h.html#a1705f2ed0fa77f6359da00bd44619892", null ], + [ "JWT_VALIDATION_TOO_NEW", "jwt_8h.html#af6ce9844414cbb96e037322cd2a68bb2", null ], + [ "jwt_alg_t", "jwt_8h.html#adf2388fe6579bd140eb773f3cc8f60d3", null ], + [ "jwt_free_t", "jwt_8h.html#ad3b3fc88482616b2883b786d0700c377", null ], + [ "jwt_key_p_t", "jwt_8h.html#a614ae0eb913524303fbab3623d810975", null ], + [ "jwt_malloc_t", "jwt_8h.html#ae64b1a91c7e44845d2422f5a6bfa7972", null ], + [ "jwt_realloc_t", "jwt_8h.html#aee904814d9c9266c68d2abff08fbc5af", null ], + [ "jwt_t", "jwt_8h.html#abf0acfb990d245214216af589c1d0947", null ], + [ "jwt_valid_t", "jwt_8h.html#ac413377fbfc11671e8a8a66f45693e5d", null ], + [ "jwt_alg", "jwt_8h.html#a3f05228338020adcde0d799e814e1851", [ + [ "JWT_ALG_NONE", "jwt_8h.html#a3f05228338020adcde0d799e814e1851a3249a71140a0bb4b3cb196fe15114b89", null ], + [ "JWT_ALG_HS256", "jwt_8h.html#a3f05228338020adcde0d799e814e1851aaa9217f4c8f5266d5401402f0ec98604", null ], + [ "JWT_ALG_HS384", "jwt_8h.html#a3f05228338020adcde0d799e814e1851af0adc69b9caa89fbc2eeaf744d7cf4f2", null ], + [ "JWT_ALG_HS512", "jwt_8h.html#a3f05228338020adcde0d799e814e1851a596b0da85c01e17b43a6574f82159081", null ], + [ "JWT_ALG_RS256", "jwt_8h.html#a3f05228338020adcde0d799e814e1851aa03ba981baa4b1cda3b9de1347ada853", null ], + [ "JWT_ALG_RS384", "jwt_8h.html#a3f05228338020adcde0d799e814e1851a520814a2695a6df472bb408ba6feb206", null ], + [ "JWT_ALG_RS512", "jwt_8h.html#a3f05228338020adcde0d799e814e1851ab4d732eef3f6bee21201d638dfd8ca44", null ], + [ "JWT_ALG_ES256", "jwt_8h.html#a3f05228338020adcde0d799e814e1851a84f9bf1cb93f6ae622a9de57d9c67472", null ], + [ "JWT_ALG_ES384", "jwt_8h.html#a3f05228338020adcde0d799e814e1851ae18e69e6b629aa0d3e6d16ec4a95c708", null ], + [ "JWT_ALG_ES512", "jwt_8h.html#a3f05228338020adcde0d799e814e1851a87205e19f1dde7c3cee012424482e106", null ], + [ "JWT_ALG_PS256", "jwt_8h.html#a3f05228338020adcde0d799e814e1851ad0216cbdf24f6b486d5082294b3cfc5b", null ], + [ "JWT_ALG_PS384", "jwt_8h.html#a3f05228338020adcde0d799e814e1851a3ad3c7e704886dd61dee0de5fa02072d", null ], + [ "JWT_ALG_PS512", "jwt_8h.html#a3f05228338020adcde0d799e814e1851a727c838b47ad911681eb029cc8b55744", null ], + [ "JWT_ALG_ES256K", "jwt_8h.html#a3f05228338020adcde0d799e814e1851a7f760cf9ed49b80ecb78580ed381ff01", null ], + [ "JWT_ALG_EDDSA", "jwt_8h.html#a3f05228338020adcde0d799e814e1851aab33738e67e3b23db72a3bc77043009b", null ], + [ "JWT_ALG_TERM", "jwt_8h.html#a3f05228338020adcde0d799e814e1851a8e90b1d1ed4944f97adabf2d38b1a949", null ] + ] ], + [ "jwt_add_grant", "group__jwt__grant.html#ga91d9bfe88d3e7b246ee67d2ed4609955", null ], + [ "jwt_add_grant_bool", "group__jwt__grant.html#gad528aabec09be8d051485a45c49781c3", null ], + [ "jwt_add_grant_int", "group__jwt__grant.html#gae4cdc69b259a0f2f98e3c07bf9038765", null ], + [ "jwt_add_grants_json", "group__jwt__grant.html#ga0509c745c45eebb41c278cebb0617a55", null ], + [ "jwt_add_header", "group__jwt__header.html#ga1c0093b5b221f78bbd4e102d30870a27", null ], + [ "jwt_add_header_bool", "group__jwt__header.html#ga337c85106951204af60f40288da0a7d8", null ], + [ "jwt_add_header_int", "group__jwt__header.html#ga250b312a2c57fa5664f3921950c88766", null ], + [ "jwt_add_headers_json", "group__jwt__header.html#ga283f3dc2d9686e4db73874ae9f446ac1", null ], + [ "jwt_alg_str", "group__jwt__alg.html#gaa906a6a1c27a3f9f6500240f7b228123", null ], + [ "jwt_decode", "group__jwt__new.html#ga4d498ffdcf1c95d67b81c224e2a447e5", null ], + [ "jwt_decode_2", "group__jwt__new.html#ga77b1f5283227b0a39228426265cf426e", null ], + [ "jwt_del_grants", "group__jwt__grant.html#ga0d8da61021d73817fb71bf8ffb7791b7", null ], + [ "jwt_del_headers", "group__jwt__header.html#gae5ebb283a76098e9a99f7fe13b18593f", null ], + [ "jwt_dump_fp", "group__jwt__encode.html#gacc77ae9e2214c9b07352a400758f1f4d", null ], + [ "jwt_dump_grants_str", "group__jwt__encode.html#ga219c406420f7f92960735433c69a304e", null ], + [ "jwt_dump_str", "group__jwt__encode.html#ga1bf8a1df0f944c027d40677404778290", null ], + [ "jwt_dup", "group__jwt__new.html#gac11d4548c695e5781cae2a5148a7e240", null ], + [ "jwt_encode_fp", "group__jwt__encode.html#ga2782abdbb190b626a5bd4eb3943b7b8d", null ], + [ "jwt_encode_str", "group__jwt__encode.html#ga124ccd21e6495dbbd1012f60d74202dd", null ], + [ "jwt_exception_str", "group__jwt__validate.html#ga9c29423f490268df9ec101a83ed96e5f", null ], + [ "jwt_free", "group__jwt__new.html#gafd51b076050815be44081439d1cb33fc", null ], + [ "jwt_free_str", "group__jwt__encode.html#ga1c6ff5f00e8722f5522fe7b36697ecdd", null ], + [ "jwt_get_alg", "group__jwt__alg.html#ga3e9cb111d566f55017d86d22dc545225", null ], + [ "jwt_get_alloc", "group__jwt__memory.html#ga8d96db3450596f2e38b549bd8f0e4ec5", null ], + [ "jwt_get_crypto_ops", "group__jwt__crypto.html#ga1ccd01c10c0f77ab1d247b1fff1f47b1", null ], + [ "jwt_get_grant", "group__jwt__grant.html#ga371b128c850eef1c364fa199ab79304b", null ], + [ "jwt_get_grant_bool", "group__jwt__grant.html#ga439c70c683ebf8b44eb6cdcc7a88ae2f", null ], + [ "jwt_get_grant_int", "group__jwt__grant.html#gae48541a059b47945181102d6478bd2bb", null ], + [ "jwt_get_grants_json", "group__jwt__grant.html#gad17b899468a54b1270a55f9ff5ae1c6f", null ], + [ "jwt_get_header", "group__jwt__header.html#ga721e3aff05b50f3859d4a725fd700710", null ], + [ "jwt_get_header_bool", "group__jwt__header.html#ga33a937ff813f648aef0ed1a5179aa75a", null ], + [ "jwt_get_header_int", "group__jwt__header.html#gae245c9c95917566c0b135b39edd398fc", null ], + [ "jwt_get_headers_json", "group__jwt__header.html#ga42eca709925d5bfcb9d0c5cfa11039f8", null ], + [ "jwt_new", "group__jwt__new.html#ga9ce4e8fb6c2eb66e41b07a7a57d8a68a", null ], + [ "jwt_set_alg", "group__jwt__alg.html#ga4b2fe3a4afc420fa7fa9feecebf2c4d7", null ], + [ "jwt_set_alloc", "group__jwt__memory.html#ga1d0fb7527d55f7849d2195ff8fd28344", null ], + [ "jwt_set_crypto_ops", "group__jwt__crypto.html#ga6749e9262b985d2b0b1f99f316356aa3", null ], + [ "jwt_str_alg", "group__jwt__alg.html#gaf15787af42ab0fc567a0a1fc5ef3edc6", null ], + [ "jwt_valid_add_grant", "group__jwt__validate.html#gab5e9e2dc5e0276ea2c21c050822c19e1", null ], + [ "jwt_valid_add_grant_bool", "group__jwt__validate.html#gafa57cedcecda005a47161c462b2f0c9f", null ], + [ "jwt_valid_add_grant_int", "group__jwt__validate.html#ga50c04450cc57bcd18717c25f18aed479", null ], + [ "jwt_valid_add_grants_json", "group__jwt__validate.html#ga9361112f9372d8de2f11c115314c2d71", null ], + [ "jwt_valid_del_grants", "group__jwt__validate.html#ga929d87f2577cbb600de468b159d9f00d", null ], + [ "jwt_valid_free", "group__jwt__validate.html#ga77db16a4a29ef6a607d6aff8012955b2", null ], + [ "jwt_valid_get_exp_leeway", "group__jwt__validate.html#ga0dc43278963ec6474d5d7263c960e69c", null ], + [ "jwt_valid_get_grant", "group__jwt__validate.html#ga03b176d20c41a85a979c9671a9a02b6f", null ], + [ "jwt_valid_get_grant_bool", "group__jwt__validate.html#gabe7ce52fca00463c2b70aa367037db98", null ], + [ "jwt_valid_get_grant_int", "group__jwt__validate.html#ga98a643ad3c8103539dee35c22a3adbb6", null ], + [ "jwt_valid_get_grants_json", "group__jwt__validate.html#gacafaf1f062cd358bda89601570d194ad", null ], + [ "jwt_valid_get_nbf_leeway", "group__jwt__validate.html#gafa9c96838053831dd239824ed5149879", null ], + [ "jwt_valid_get_status", "group__jwt__validate.html#ga800a8033a89f9b3e456e9206391ffe2e", null ], + [ "jwt_valid_new", "group__jwt__validate.html#gaede6b64d84fd8166ea16debe6efc826d", null ], + [ "jwt_valid_set_exp_leeway", "group__jwt__validate.html#ga1ce0ec28e692a189d3c6819dc571f941", null ], + [ "jwt_valid_set_headers", "group__jwt__validate.html#gab29df6ee78d33bb846b13f95a1269e0d", null ], + [ "jwt_valid_set_nbf_leeway", "group__jwt__validate.html#ga55f1862ab8bb6297ee0163f77fe7c386", null ], + [ "jwt_valid_set_now", "group__jwt__validate.html#gad6a97fc0bb3341ae7f19975095a8086a", null ], + [ "jwt_validate", "group__jwt__validate.html#gaee37cb2b275c2467f2d80a50722522f1", null ] +]; \ No newline at end of file diff --git a/jwt_8h__incl.map b/jwt_8h__incl.map new file mode 100644 index 00000000..3420f941 --- /dev/null +++ b/jwt_8h__incl.map @@ -0,0 +1,7 @@ + + + + + + + diff --git a/jwt_8h__incl.md5 b/jwt_8h__incl.md5 new file mode 100644 index 00000000..910c2075 --- /dev/null +++ b/jwt_8h__incl.md5 @@ -0,0 +1 @@ +8e6823f4fc3152e251e6554d674cd2a9 \ No newline at end of file diff --git a/jwt_8h__incl.png b/jwt_8h__incl.png new file mode 100644 index 0000000000000000000000000000000000000000..7e24c43ed53a771548df6c4fb69d1752b780ca21 GIT binary patch literal 4272 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 + + + + + + +libjwt-2.1.1: jwt.h Source File + + + + + + + + + + + + +
+
+ + + + + + +
+
libjwt-2.1.1 +
+
+
+ + + + + + + +
+
+ +
+
+
+ +
+
+
jwt.h
+
+
+Go to the documentation of this file.
1/* Copyright (C) 2015-2024 Ben Collins <bcollins@maclara-llc.com>
+
2 This file is part of the JWT C Library
+
3
+
4 SPDX-License-Identifier: MPL-2.0
+
5 This Source Code Form is subject to the terms of the Mozilla Public
+
6 License, v. 2.0. If a copy of the MPL was not distributed with this
+
7 file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
8
+
13
+
14#ifndef JWT_H
+
15#define JWT_H
+
16
+
17#include <stdio.h>
+
18#include <time.h>
+
19
+
20#ifdef _MSC_VER
+
21
+
22 #define DEPRECATED(func) __declspec(deprecated) func
+
23
+
24 #define alloca _alloca
+
25 #define strcasecmp _stricmp
+
26 #define strdup _strdup
+
27
+
28 #ifdef JWT_DLL_CONFIG
+
29 #ifdef JWT_BUILD_SHARED_LIBRARY
+
30 #define JWT_EXPORT __declspec(dllexport)
+
31 #else
+
32 #define JWT_EXPORT __declspec(dllimport)
+
33 #endif
+
34 #else
+
35 #define JWT_EXPORT
+
36 #endif
+
37
+
38#else
+
39
+
40 #define DEPRECATED(func) func __attribute__ ((deprecated))
+
41 #define JWT_EXPORT
+
42
+
43#endif
+
44
+
45#ifdef __cplusplus
+
46extern "C" {
+
47#endif
+
48
+
50typedef struct jwt jwt_t;
+
51
+
53typedef struct jwt_valid jwt_valid_t;
+
54
+ +
74
+
75#define JWT_ALG_INVAL JWT_ALG_TERM
+
76
+
78#define JWT_VALIDATION_SUCCESS 0x0000
+
79#define JWT_VALIDATION_ERROR 0x0001 /* General failures */
+
80#define JWT_VALIDATION_ALG_MISMATCH 0x0002
+
81#define JWT_VALIDATION_EXPIRED 0x0004
+
82#define JWT_VALIDATION_TOO_NEW 0x0008
+
83#define JWT_VALIDATION_ISS_MISMATCH 0x0010
+
84#define JWT_VALIDATION_SUB_MISMATCH 0x0020
+
85#define JWT_VALIDATION_AUD_MISMATCH 0x0040
+
86#define JWT_VALIDATION_GRANT_MISSING 0x0080
+
87#define JWT_VALIDATION_GRANT_MISMATCH 0x0100
+
88
+
90typedef void *(*jwt_malloc_t)(size_t);
+
91typedef void *(*jwt_realloc_t)(void *, size_t);
+
92typedef void (*jwt_free_t)(void *);
+
93
+
+
95typedef struct {
+
96 const unsigned char *jwt_key;
+ +
98} jwt_key_t;
+
+
99
+
101typedef int (*jwt_key_p_t)(const jwt_t *, jwt_key_t *);
+
102
+
103
+
120
+
126const char *jwt_get_crypto_ops(void);
+
127
+
137int jwt_set_crypto_ops(const char *opname);
+
138
+
140
+
156
+ +
168
+
196JWT_EXPORT int jwt_decode(jwt_t **jwt, const char *token,
+
197 const unsigned char *key, int key_len);
+
198
+
215JWT_EXPORT int jwt_decode_2(jwt_t **jwt, const char *token, jwt_key_p_t key_provider);
+
216
+ +
227
+ +
238
+
240
+
247
+
264JWT_EXPORT const char *jwt_get_grant(jwt_t *jwt, const char *grant);
+
265
+
282JWT_EXPORT long jwt_get_grant_int(jwt_t *jwt, const char *grant);
+
283
+
300JWT_EXPORT int jwt_get_grant_bool(jwt_t *jwt, const char *grant);
+
301
+
315JWT_EXPORT char *jwt_get_grants_json(jwt_t *jwt, const char *grant);
+
316
+
335JWT_EXPORT int jwt_add_grant(jwt_t *jwt, const char *grant, const char *val);
+
336
+
354JWT_EXPORT int jwt_add_grant_int(jwt_t *jwt, const char *grant, long val);
+
355
+
373JWT_EXPORT int jwt_add_grant_bool(jwt_t *jwt, const char *grant, int val);
+
374
+
387JWT_EXPORT int jwt_add_grants_json(jwt_t *jwt, const char *json);
+
388
+
401JWT_EXPORT int jwt_del_grants(jwt_t *jwt, const char *grant);
+
402
+
404
+
411
+
428JWT_EXPORT const char *jwt_get_header(jwt_t *jwt, const char *header);
+
429
+
446JWT_EXPORT long jwt_get_header_int(jwt_t *jwt, const char *header);
+
447
+
464JWT_EXPORT int jwt_get_header_bool(jwt_t *jwt, const char *header);
+
465
+
479JWT_EXPORT char *jwt_get_headers_json(jwt_t *jwt, const char *header);
+
480
+
499JWT_EXPORT int jwt_add_header(jwt_t *jwt, const char *header, const char *val);
+
500
+
518JWT_EXPORT int jwt_add_header_int(jwt_t *jwt, const char *header, long val);
+
519
+
537JWT_EXPORT int jwt_add_header_bool(jwt_t *jwt, const char *header, int val);
+
538
+
551JWT_EXPORT int jwt_add_headers_json(jwt_t *jwt, const char *json);
+
552
+
565JWT_EXPORT int jwt_del_headers(jwt_t *jwt, const char *header);
+
566
+
568
+
575
+
596JWT_EXPORT int jwt_dump_fp(jwt_t *jwt, FILE *fp, int pretty);
+
597
+
616JWT_EXPORT char *jwt_dump_str(jwt_t *jwt, int pretty);
+
617
+
630JWT_EXPORT char *jwt_dump_grants_str(jwt_t *jwt, int pretty);
+
631
+
643JWT_EXPORT int jwt_encode_fp(jwt_t *jwt, FILE *fp);
+
644
+ +
657
+
664JWT_EXPORT void jwt_free_str(char *str);
+
665
+
667
+
678
+
693JWT_EXPORT int jwt_set_alg(jwt_t *jwt, jwt_alg_t alg, const unsigned char *key, int len);
+
694
+ +
704
+ +
715
+ +
728
+
730
+
736
+ +
762
+ +
771
+
773
+
789
+
798JWT_EXPORT unsigned int jwt_validate(jwt_t *jwt, jwt_valid_t *jwt_valid);
+
799
+ +
813
+ +
824
+ +
836
+ +
844
+ +
852
+
866JWT_EXPORT int jwt_valid_add_grant(jwt_valid_t *jwt_valid, const char *grant, const char *val);
+
867
+
884JWT_EXPORT const char *jwt_valid_get_grant(jwt_valid_t *jwt_valid, const char *grant);
+
885
+
898JWT_EXPORT int jwt_valid_add_grant_int(jwt_valid_t *jwt_valid, const char *grant, long val);
+
899
+
916JWT_EXPORT long jwt_valid_get_grant_int(jwt_valid_t *jwt_valid, const char *grant);
+
917
+
935JWT_EXPORT int jwt_valid_add_grant_bool(jwt_valid_t *jwt_valid, const char *grant, int val);
+
936
+
953JWT_EXPORT int jwt_valid_get_grant_bool(jwt_valid_t *jwt_valid, const char *grant);
+
954
+
965JWT_EXPORT int jwt_valid_add_grants_json(jwt_valid_t *jwt_valid, const char *json);
+
966
+
979JWT_EXPORT char* jwt_valid_get_grants_json(jwt_valid_t *jwt_valid, const char *grant);
+
980
+
993JWT_EXPORT int jwt_valid_del_grants(jwt_valid_t *jwt_valid, const char *grant);
+
994
+
1005JWT_EXPORT int jwt_valid_set_now(jwt_valid_t *jwt_valid, const time_t now);
+
1006
+
1015JWT_EXPORT int jwt_valid_set_nbf_leeway(jwt_valid_t *jwt_valid, const time_t nbf_leeway);
+
1016
+
1025JWT_EXPORT int jwt_valid_set_exp_leeway(jwt_valid_t *jwt_valid, const time_t exp_leeway);
+
1026
+ +
1041
+
1055JWT_EXPORT char *jwt_exception_str(unsigned int exceptions);
+
1056
+
1058
+
1059#ifdef __cplusplus
+
1060}
+
1061#endif
+
1062
+
1063#endif /* JWT_H */
+
jwt_alg_t jwt_get_alg(const jwt_t *jwt)
Get the jwt_alg_t set for this JWT object.
+
int jwt_set_alg(jwt_t *jwt, jwt_alg_t alg, const unsigned char *key, int len)
Set an algorithm from jwt_alg_t for this JWT object.
+
const char * jwt_alg_str(jwt_alg_t alg)
Convert alg type to it's string representation.
+
jwt_alg_t jwt_str_alg(const char *alg)
Convert alg string to type.
+
const char * jwt_get_crypto_ops(void)
Retrieve the name of the current crypto operations being used.
+
int jwt_set_crypto_ops(const char *opname)
Set the crypto operations to the named set.
+
char * jwt_encode_str(jwt_t *jwt)
Fully encode a JWT object and return as a string.
+
char * jwt_dump_str(jwt_t *jwt, int pretty)
Return plain text representation as a string.
+
void jwt_free_str(char *str)
Free a string returned from the library.
+
char * jwt_dump_grants_str(jwt_t *jwt, int pretty)
Return plain text representation of grants as a string.
+
int jwt_encode_fp(jwt_t *jwt, FILE *fp)
Fully encode a JWT object and write it to FILE.
+
int jwt_dump_fp(jwt_t *jwt, FILE *fp, int pretty)
Output plain text representation to a FILE pointer.
+
int jwt_add_grants_json(jwt_t *jwt, const char *json)
Add grants from a JSON encoded object string.
+
int jwt_del_grants(jwt_t *jwt, const char *grant)
Delete a grant from this JWT object.
+
const char * jwt_get_grant(jwt_t *jwt, const char *grant)
Return the value of a string grant.
+
int jwt_get_grant_bool(jwt_t *jwt, const char *grant)
Return the value of an boolean grant.
+
int jwt_add_grant(jwt_t *jwt, const char *grant, const char *val)
Add a new string grant to this JWT object.
+
char * jwt_get_grants_json(jwt_t *jwt, const char *grant)
Return the value of a grant as JSON encoded object string.
+
int jwt_add_grant_bool(jwt_t *jwt, const char *grant, int val)
Add a new boolean grant to this JWT object.
+
long jwt_get_grant_int(jwt_t *jwt, const char *grant)
Return the value of an integer grant.
+
int jwt_add_grant_int(jwt_t *jwt, const char *grant, long val)
Add a new integer grant to this JWT object.
+
int jwt_add_header(jwt_t *jwt, const char *header, const char *val)
Add a new string header to this JWT object.
+
int jwt_add_header_int(jwt_t *jwt, const char *header, long val)
Add a new integer header to this JWT object.
+
int jwt_add_headers_json(jwt_t *jwt, const char *json)
Add headers from a JSON encoded object string.
+
int jwt_add_header_bool(jwt_t *jwt, const char *header, int val)
Add a new boolean header to this JWT object.
+
int jwt_get_header_bool(jwt_t *jwt, const char *header)
Return the value of an boolean header.
+
char * jwt_get_headers_json(jwt_t *jwt, const char *header)
Return the value of a header as JSON encoded object string.
+
const char * jwt_get_header(jwt_t *jwt, const char *header)
Return the value of a string header.
+
long jwt_get_header_int(jwt_t *jwt, const char *header)
Return the value of an integer header.
+
int jwt_del_headers(jwt_t *jwt, const char *header)
Delete a header from this JWT object.
+
int jwt_set_alloc(jwt_malloc_t pmalloc, jwt_realloc_t prealloc, jwt_free_t pfree)
Set functions to be used for allocating and freeing memory.
+
void jwt_get_alloc(jwt_malloc_t *pmalloc, jwt_realloc_t *prealloc, jwt_free_t *pfree)
Get functions used for allocating and freeing memory.
+
int jwt_decode(jwt_t **jwt, const char *token, const unsigned char *key, int key_len)
Decode an existing JWT and allocate a new JWT object from it.
+
int jwt_decode_2(jwt_t **jwt, const char *token, jwt_key_p_t key_provider)
Like jwt_decode(), but the key will be obtained via the key provider.
+
int jwt_new(jwt_t **jwt)
Allocate a new, empty, JWT object.
+
jwt_t * jwt_dup(jwt_t *jwt)
Duplicate an existing JWT object.
+
void jwt_free(jwt_t *jwt)
Free a JWT object and any other resources it is using.
+
const char * jwt_valid_get_grant(jwt_valid_t *jwt_valid, const char *grant)
Return the value of a string required grant.
+
time_t jwt_valid_get_exp_leeway(jwt_valid_t *jwt_valid)
Return the exp_leeway value set.
+
int jwt_valid_set_exp_leeway(jwt_valid_t *jwt_valid, const time_t exp_leeway)
Set the exp_leeway value as defined in: https://www.rfc-editor.org/rfc/rfc7519#section-4....
+
int jwt_valid_add_grant_int(jwt_valid_t *jwt_valid, const char *grant, long val)
Add a new integer grant requirement to this JWT validation object.
+
int jwt_valid_set_nbf_leeway(jwt_valid_t *jwt_valid, const time_t nbf_leeway)
Set the nbf_leeway value as defined in: https://www.rfc-editor.org/rfc/rfc7519#section-4....
+
void jwt_valid_free(jwt_valid_t *jwt_valid)
Free a JWT validation object and any other resources it is using.
+
unsigned int jwt_valid_get_status(jwt_valid_t *jwt_valid)
Return the status string for the validation object.
+
int jwt_valid_del_grants(jwt_valid_t *jwt_valid, const char *grant)
Delete a grant from this JWT object.
+
int jwt_valid_add_grants_json(jwt_valid_t *jwt_valid, const char *json)
Add required grants from a JSON encoded object string.
+
long jwt_valid_get_grant_int(jwt_valid_t *jwt_valid, const char *grant)
Return the value of an integer required grant.
+
char * jwt_exception_str(unsigned int exceptions)
Parses exceptions and returns a comma delimited and human-readable string.
+
int jwt_valid_set_headers(jwt_valid_t *jwt_valid, int hdr)
Set validation for replicated claims in headers.
+
int jwt_valid_add_grant(jwt_valid_t *jwt_valid, const char *grant, const char *val)
Add a new string grant requirement to this JWT validation object.
+
int jwt_valid_get_grant_bool(jwt_valid_t *jwt_valid, const char *grant)
Return the value of an boolean required grant.
+
char * jwt_valid_get_grants_json(jwt_valid_t *jwt_valid, const char *grant)
Return the value of a grant as JSON encoded object string.
+
int jwt_valid_set_now(jwt_valid_t *jwt_valid, const time_t now)
Set the time for which expires and not-before claims should be evaluated.
+
int jwt_valid_new(jwt_valid_t **jwt_valid, jwt_alg_t alg)
Allocate a new, JWT validation object.
+
unsigned int jwt_validate(jwt_t *jwt, jwt_valid_t *jwt_valid)
Validate a JWT object with a validation object.
+
int jwt_valid_add_grant_bool(jwt_valid_t *jwt_valid, const char *grant, int val)
Add a new boolean required grant to this JWT validation object.
+
time_t jwt_valid_get_nbf_leeway(jwt_valid_t *jwt_valid)
Return the nbf_leeway value set.
+
#define JWT_EXPORT
Definition jwt.h:41
+
jwt_alg
JWT algorithm types.
Definition jwt.h:56
+
@ JWT_ALG_NONE
Definition jwt.h:57
+
@ JWT_ALG_PS384
Definition jwt.h:68
+
@ JWT_ALG_RS384
Definition jwt.h:62
+
@ JWT_ALG_HS512
Definition jwt.h:60
+
@ JWT_ALG_PS512
Definition jwt.h:69
+
@ JWT_ALG_ES256K
Definition jwt.h:70
+
@ JWT_ALG_ES256
Definition jwt.h:64
+
@ JWT_ALG_ES512
Definition jwt.h:66
+
@ JWT_ALG_TERM
Definition jwt.h:72
+
@ JWT_ALG_RS256
Definition jwt.h:61
+
@ JWT_ALG_HS256
Definition jwt.h:58
+
@ JWT_ALG_EDDSA
Definition jwt.h:71
+
@ JWT_ALG_RS512
Definition jwt.h:63
+
@ JWT_ALG_PS256
Definition jwt.h:67
+
@ JWT_ALG_ES384
Definition jwt.h:65
+
@ JWT_ALG_HS384
Definition jwt.h:59
+
int(* jwt_key_p_t)(const jwt_t *, jwt_key_t *)
Key provider - inspects the JWT to obtain the key used to verify the signature.
Definition jwt.h:101
+
struct jwt jwt_t
Opaque JWT object.
Definition jwt.h:50
+
struct jwt_valid jwt_valid_t
Opaque JWT validation object.
Definition jwt.h:53
+
void(* jwt_free_t)(void *)
Definition jwt.h:92
+
enum jwt_alg jwt_alg_t
JWT algorithm types.
+
void *(* jwt_malloc_t)(size_t)
JWT Memory allocation overrides.
Definition jwt.h:90
+
void *(* jwt_realloc_t)(void *, size_t)
Definition jwt.h:91
+
Structure used by key provider to return a key.
Definition jwt.h:95
+
const unsigned char * jwt_key
Definition jwt.h:96
+
int jwt_key_len
Definition jwt.h:97
+
+
+ + + + diff --git a/libjwt/CMakeLists.txt b/libjwt/CMakeLists.txt new file mode 100644 index 00000000..a2dbf55d --- /dev/null +++ b/libjwt/CMakeLists.txt @@ -0,0 +1,117 @@ +include(FetchContent) +include(FindPkgConfig) + +set (TARGET_NAME ${PROJECT_NAME}) + +option (BUILD_SHARED_LIBS "Build libjwt as shared library instead as static one." OFF) +option (WITHOUT_OPENSSL "Use GnuTLS for encryption instead of OpenSSL" OFF) +option (USE_WINSSL "Use Windows crypto API for encryption instead of OpenSSL" OFF) +option (USE_INSTALLED_JANSSON "Use pre-installed jansson library" ON) + +if (UNIX) + option (ENABLE_PIC "Use position independent code in static library build." OFF) +endif () + +if (BUILD_SHARED_LIBS) + set (LIBRARY_TYPE SHARED) +else () + set (LIBRARY_TYPE STATIC) + if (MSVC) + set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Z7") + set (CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} /Z7") + if (${ENABLE_DEBUG_INFO_IN_RELEASE}) + set (CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /Z7") + endif () + endif () +endif () + +if (USE_WINSSL) + set (FILES_TO_REMOVE ${CMAKE_CURRENT_SOURCE_DIR}/jwt-openssl.c ${CMAKE_CURRENT_SOURCE_DIR}/jwt-gnutls.c) + set (SSL_LIBRARY_INCLUDE_DIR ) + set (SSL_LIBRARIES_DEBUG crypt32.lib ncrypt.lib bcrypt.lib) + set (SSL_LIBRARIES_OPTIMIZED crypt32.lib ncrypt.lib bcrypt.lib) +elseif (WITHOUT_OPENSSL) + find_package (GnuTLS REQUIRED) + set (FILES_TO_REMOVE ${CMAKE_CURRENT_SOURCE_DIR}/jwt-openssl.c ${CMAKE_CURRENT_SOURCE_DIR}/jwt-wincrypt.c) + set (SSL_LIBRARY_INCLUDE_DIR ${GNUTLS_INCLUDE_DIR}) + set (SSL_LIBRARIES_DEBUG ${GNUTLS_LIBRARY}) + set (SSL_LIBRARIES_OPTIMIZED ${GNUTLS_LIBRARY}) +else () + if (MSVC AND STATIC_RUNTIME) + set (OPENSSL_MSVC_STATIC_RT TRUE) + endif () + find_package (OpenSSL REQUIRED) + set (FILES_TO_REMOVE ${CMAKE_CURRENT_SOURCE_DIR}/jwt-gnutls.c ${CMAKE_CURRENT_SOURCE_DIR}/jwt-wincrypt.c) + set (SSL_LIBRARY_INCLUDE_DIR ${OPENSSL_INCLUDE_DIR}) + set (SSL_LIBRARIES_DEBUG ${OPENSSL_LIBRARIES}) + set (SSL_LIBRARIES_OPTIMIZED ${OPENSSL_LIBRARIES}) +endif () + +if (USE_INSTALLED_JANSSON) + PKG_SEARCH_MODULE( JANSSON jansson ) +else() + FetchContent_Declare( + jansson + GIT_REPOSITORY https://github.com/akheron/jansson.git + GIT_TAG v2.14 + ) + + FetchContent_MakeAvailable(jansson) +endif() + +write_file(${CMAKE_CURRENT_BINARY_DIR}/config.h "") + +file (GLOB SOURCE_FILES "../include/*.h" "*.h" "*.c") +list (REMOVE_ITEM SOURCE_FILES ${FILES_TO_REMOVE}) + +add_library (${TARGET_NAME} ${LIBRARY_TYPE} ${SOURCE_FILES}) + +target_include_directories (${TARGET_NAME} PRIVATE + ${SSL_LIBRARY_INCLUDE_DIR} + ${jansson_SOURCE_DIR}/src + ${jansson_SOURCE_DIR}/android + ${CMAKE_CURRENT_BINARY_DIR} + ) + +# Need for using the project by add_subdirectory +target_include_directories (${TARGET_NAME} INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/../include + ) + +if (UNIX) + target_compile_definitions (${TARGET_NAME} PUBLIC _GNU_SOURCE) +endif () + +if (UNIX AND ENABLE_LTO) + set_property(TARGET ${TARGET_NAME} PROPERTY INTERPROCEDURAL_OPTIMIZATION True) +endif () + +if (MSVC) + target_compile_definitions (${TARGET_NAME} PRIVATE _CRT_SECURE_NO_WARNINGS) + if (BUILD_SHARED_LIBS) + target_compile_definitions (${TARGET_NAME} + PUBLIC JWT_DLL_CONFIG + PRIVATE JWT_BUILD_SHARED_LIBRARY + ) + endif () +endif () + +if (NOT BUILD_SHARED_LIBS AND ENABLE_PIC) + target_compile_options (${TARGET_NAME} PRIVATE -fPIC) +endif () + +target_link_libraries (${TARGET_NAME} + debug ${SSL_LIBRARIES_DEBUG} optimized ${SSL_LIBRARIES_OPTIMIZED} + jansson + ) + +install (TARGETS ${TARGET_NAME} + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib + ) + +install (FILES + ${PROJECT_SOURCE_DIR}/include/jwt.h + DESTINATION include/ + ) diff --git a/libjwt/Makefile.am b/libjwt/Makefile.am index 4ed1000d..6da4f2e5 100644 --- a/libjwt/Makefile.am +++ b/libjwt/Makefile.am @@ -1,5 +1,30 @@ +EXTRA_DIST = jwt-private.h CMakeLists.txt cencode.h cdecode.h ccommon.h + lib_LTLIBRARIES = libjwt.la + libjwt_la_SOURCES = jwt.c + +# SONAME History +# v1.12.0 => v1.12.1 0 => 1 +# v1.14.0 => v1.15.0 1 => 2 # http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html -libjwt_la_LDFLAGS = -version-info 0:0:0 -libjwt_la_CPPFLAGS = -I$(top_srcdir)/include + +libjwt_la_LDFLAGS = -version-info 13:2:11 $(OPENSSL_LDFLAGS) $(GNUTLS_LDFLAGS) $(JANSSON_LDFLAGS) -no-undefined +libjwt_la_CPPFLAGS = -I$(top_srcdir)/include $(OPENSSL_INCLUDES) $(GNUTLS_INCLUDES) $(CODE_COVERAGE_CPPFLAGS) -Wall +libjwt_la_CFLAGS = $(JANSSON_CFLAGS) $(OPENSSL_CFLAGS) $(GNUTLS_CFLAGS) $(CODE_COVERAGE_CFLAGS) -D_GNU_SOURCE +libjwt_la_LIBADD = $(JANSSON_LIBS) $(OPENSSL_LIBS) $(GNUTLS_LIBS) $(CODE_COVERAGE_LDFLAGS) + +if HAVE_OPENSSL +libjwt_la_SOURCES += jwt-openssl.c +endif + +if HAVE_GNUTLS +libjwt_la_SOURCES += jwt-gnutls.c +endif + +if !HAVE_LIBB64 +libjwt_la_SOURCES += cdecode.c cencode.c +endif + +pkgconfiglibdir = $(libdir)/pkgconfig +pkgconfiglib_DATA = libjwt.pc diff --git a/libjwt/ccommon.h b/libjwt/ccommon.h new file mode 100644 index 00000000..74896b61 --- /dev/null +++ b/libjwt/ccommon.h @@ -0,0 +1,14 @@ +/* +ccommon.h - common header for encoding and decoding algorithm + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#ifndef BASE64_CCOMMON_H +#define BASE64_CCOMMON_H + +#define BASE64_VER_MAJOR 2 +#define BASE64_VER_MINOR 0 + +#endif /* BASE64_CCOMMON_H */ diff --git a/libjwt/cdecode.c b/libjwt/cdecode.c new file mode 100644 index 00000000..e3a94029 --- /dev/null +++ b/libjwt/cdecode.c @@ -0,0 +1,108 @@ +/* +cdecoder.c - c source to a base64 decoding algorithm implementation + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#include "cdecode.h" + +/* + * Calculate a maximum required buffer length for decoded output based on input size. + * + * Return the value as size_t + */ + +__attribute__ ((visibility ("hidden"))) +size_t base64_decode_maxlength(size_t encode_len) +{ + return encode_len / 4 * 3 + 2; +} + +__attribute__ ((visibility ("hidden"))) +int base64_decode_value(signed char value_in) +{ + static const signed char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51}; + + if (value_in < (signed char) '+') + return -1; + + value_in -= (signed char) '+'; + + if (value_in >= (signed char) (sizeof(decoding) / sizeof(*decoding)) ) + return -1; + + return decoding[value_in]; +} + +__attribute__ ((visibility ("hidden"))) +void base64_init_decodestate(base64_decodestate* state_in) +{ + state_in->step = step_a; + state_in->plainchar = 0; +} + +__attribute__ ((visibility ("hidden"))) +size_t base64_decode_block(const char* code_in, const size_t length_in, void* plaintext_out, base64_decodestate* state_in) +{ + const char* codechar = code_in; + char* plainchar = plaintext_out; + int fragment; + + *plainchar = state_in->plainchar; + + switch (state_in->step) + { + for(;;) + { + case step_a: + do { + if (codechar == code_in+length_in) + { + state_in->step = step_a; + state_in->plainchar = *plainchar; + return (size_t)(plainchar - (char *) plaintext_out); + } + fragment = base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar = (fragment & 0x03f) << 2; + case step_b: + do { + if (codechar == code_in+length_in) + { + state_in->step = step_b; + state_in->plainchar = *plainchar; + return (size_t)(plainchar - (char *) plaintext_out); + } + fragment = base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar++ |= (fragment & 0x030) >> 4; + *plainchar = (fragment & 0x00f) << 4; + case step_c: + do { + if (codechar == code_in+length_in) + { + state_in->step = step_c; + state_in->plainchar = *plainchar; + return (size_t)(plainchar - (char *) plaintext_out); + } + fragment = base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar++ |= (fragment & 0x03c) >> 2; + *plainchar = (fragment & 0x003) << 6; + case step_d: + do { + if (codechar == code_in+length_in) + { + state_in->step = step_d; + state_in->plainchar = *plainchar; + return (size_t)(plainchar - (char *) plaintext_out); + } + fragment = base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar++ |= (fragment & 0x03f); + } + } + /* control should not reach here */ + return (size_t) (plainchar - (char *) plaintext_out); +} diff --git a/libjwt/cdecode.h b/libjwt/cdecode.h new file mode 100644 index 00000000..d9f00e3c --- /dev/null +++ b/libjwt/cdecode.h @@ -0,0 +1,36 @@ +/* +cdecode.h - c header for a base64 decoding algorithm + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#ifndef BASE64_CDECODE_H +#define BASE64_CDECODE_H + +#include "ccommon.h" + +#include + +#define BASE64_CDEC_VER_MAJOR 2 +#define BASE64_CDEC_VER_MINOR 0 + +typedef enum +{ + step_a, step_b, step_c, step_d +} base64_decodestep; + +typedef struct +{ + base64_decodestep step; + char plainchar; +} base64_decodestate; + +extern void base64_init_decodestate(base64_decodestate* state_in); + +extern size_t base64_decode_maxlength(size_t encode_len); + +extern int base64_decode_value(signed char value_in); +extern size_t base64_decode_block(const char* code_in, const size_t length_in, void* plaintext_out, base64_decodestate* state_in); + +#endif /* BASE64_CDECODE_H */ diff --git a/libjwt/cencode.c b/libjwt/cencode.c new file mode 100644 index 00000000..43b45e5e --- /dev/null +++ b/libjwt/cencode.c @@ -0,0 +1,177 @@ +/* +cencoder.c - c source to a base64 encoding algorithm implementation + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#include "cencode.h" + +__attribute__ ((visibility ("hidden"))) +void base64_init_encodestate(base64_encodestate* state_in) +{ + state_in->step = step_A; + state_in->result = 0; + state_in->stepcount = 0; + state_in->chars_per_line = BASE64_CENC_DEFCPL; +} + +/* + * Calculate required length of buffer for encoded string when using encoder state structure state_in for encoding + * and when having input data of size plain_len + * + * Return encoded length, or 0 if encoded length + one additional null byte would exceed range of size_t + */ +__attribute__ ((visibility ("hidden"))) +size_t base64_encode_length(size_t plain_len, base64_encodestate* state_in) +{ + size_t retmax = 0, retval; + size_t cpl = state_in->chars_per_line; + + /* + * Check for integer overflow. Takes into consideration that gcc may optimise out + * naive integer overflow checks based on the C standard saying the result of overflow is undefined. + */ + retmax = ~retmax; + retval = (plain_len + 2) / 3; + if(retval > retmax / 4) + return 0; + + retval *= 4; + + /* Calculate number of linebreaks */ + if(retval && cpl) + { + size_t numbreaks; + + numbreaks = (retval - 1) / cpl + 1; + + retmax -= retval; + + /* Make sure there's always room for one additional 0 byte on top of our return value */ + if(numbreaks < retmax) + return numbreaks + retval; + else + return 0; + } + else + return retval; +} + +__attribute__ ((visibility ("hidden"))) +char base64_encode_value(signed char value_in) +{ + static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + if (value_in > 63) return '='; + return encoding[(int)value_in]; +} + +#define CHECK_BREAK() \ +do \ +{ \ + if (cpl && state_in->stepcount >= cpl) \ + { \ + *codechar++ = '\n'; \ + state_in->stepcount = 0; \ + } \ + state_in->stepcount++; \ +} while(0); + +__attribute__ ((visibility ("hidden"))) +size_t base64_encode_block(const void* plaintext_in, const size_t length_in, char* code_out, base64_encodestate* state_in) +{ + const char* plainchar = plaintext_in; + const char* const plaintextend = plainchar + length_in; + char* codechar = code_out; + size_t cpl = state_in->chars_per_line; + char result; + char fragment; + + result = state_in->result; + + switch (state_in->step) + { + for(;;) + { + case step_A: + if (plainchar == plaintextend) + { + state_in->result = result; + state_in->step = step_A; + return (size_t)(codechar - code_out); + } + + CHECK_BREAK(); + + fragment = *plainchar++; + result = (fragment & 0x0fc) >> 2; + *codechar++ = base64_encode_value(result); + result = (fragment & 0x003) << 4; + case step_B: + if (plainchar == plaintextend) + { + state_in->result = result; + state_in->step = step_B; + return (size_t)(codechar - code_out); + } + + CHECK_BREAK(); + + fragment = *plainchar++; + result |= (fragment & 0x0f0) >> 4; + *codechar++ = base64_encode_value(result); + result = (fragment & 0x00f) << 2; + case step_C: + if (plainchar == plaintextend) + { + state_in->result = result; + state_in->step = step_C; + return (size_t)(codechar - code_out); + } + + CHECK_BREAK(); + + fragment = *plainchar++; + result |= (fragment & 0x0c0) >> 6; + *codechar++ = base64_encode_value(result); + + CHECK_BREAK(); + + result = (fragment & 0x03f) >> 0; + *codechar++ = base64_encode_value(result); + } + } + /* control should not reach here */ + return (size_t) (codechar - code_out); +} + +__attribute__ ((visibility ("hidden"))) +size_t base64_encode_blockend(char* code_out, base64_encodestate* state_in) +{ + char* codechar = code_out; + size_t cpl = state_in->chars_per_line; + + switch (state_in->step) + { + case step_B: + CHECK_BREAK(); + *codechar++ = base64_encode_value(state_in->result); + CHECK_BREAK(); + *codechar++ = '='; + CHECK_BREAK(); + *codechar++ = '='; + break; + case step_C: + CHECK_BREAK(); + *codechar++ = base64_encode_value(state_in->result); + CHECK_BREAK(); + *codechar++ = '='; + break; + case step_A: + break; + } + if(state_in->chars_per_line) + *codechar++ = '\n'; + + return (size_t) (codechar - code_out); +} diff --git a/libjwt/cencode.h b/libjwt/cencode.h new file mode 100644 index 00000000..2edf00c2 --- /dev/null +++ b/libjwt/cencode.h @@ -0,0 +1,44 @@ +/* +cencode.h - c header for a base64 encoding algorithm + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#ifndef BASE64_CENCODE_H +#define BASE64_CENCODE_H + +#include "ccommon.h" + +#include + +#define BASE64_CENC_VER_MAJOR 2 +#define BASE64_CENC_VER_MINOR 0 + +typedef enum +{ + step_A, step_B, step_C +} base64_encodestep; + +#define BASE64_CENC_DEFCPL 0 + +typedef struct +{ + size_t stepcount; + size_t chars_per_line; + + base64_encodestep step; + + int cflags; + char result; +} base64_encodestate; + +extern void base64_init_encodestate(base64_encodestate* state_in); + +extern size_t base64_encode_length(size_t plain_len, base64_encodestate* state_in); + +extern char base64_encode_value(signed char value_in); +extern size_t base64_encode_block(const void* plaintext_in, const size_t length_in, char* code_out, base64_encodestate* state_in); +extern size_t base64_encode_blockend(char* code_out, base64_encodestate* state_in); + +#endif /* BASE64_CENCODE_H */ diff --git a/libjwt/jwt-gnutls.c b/libjwt/jwt-gnutls.c new file mode 100644 index 00000000..3dbdd01e --- /dev/null +++ b/libjwt/jwt-gnutls.c @@ -0,0 +1,426 @@ +/* Copyright (C) 2017 Nicolas Mora + This file is part of the JWT C Library + + SPDX-License-Identifier: MPL-2.0 + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "jwt-private.h" + +/* Workaround to use GnuTLS 3.5 EC signature encode/decode functions that + * are not public yet. */ +#if GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR == 5 +extern int _gnutls_encode_ber_rs_raw(gnutls_datum_t *sig_value, + const gnutls_datum_t *r, + const gnutls_datum_t *s); +extern int _gnutls_decode_ber_rs_raw(const gnutls_datum_t *sig_value, + gnutls_datum_t *r, gnutls_datum_t *s); + +static int gnutls_encode_rs_value(gnutls_datum_t *sig_value, + const gnutls_datum_t *r, + const gnutls_datum_t *s) +{ + return _gnutls_encode_ber_rs_raw(sig_value, r, s); +} + +static int gnutls_decode_rs_value(const gnutls_datum_t *sig_value, + gnutls_datum_t *r, gnutls_datum_t *s) +{ + return _gnutls_decode_ber_rs_raw(sig_value, r, s); +} +#endif /* End of pre-3.6 work-arounds. */ + +/** + * libjwt encryption/decryption function definitions + */ +static int gnutls_sign_sha_hmac(jwt_t *jwt, char **out, unsigned int *len, + const char *str, unsigned int str_len) +{ + int alg; + + switch (jwt->alg) { + case JWT_ALG_HS256: + alg = GNUTLS_DIG_SHA256; + break; + case JWT_ALG_HS384: + alg = GNUTLS_DIG_SHA384; + break; + case JWT_ALG_HS512: + alg = GNUTLS_DIG_SHA512; + break; + default: + return EINVAL; + } + + *len = gnutls_hmac_get_len(alg); + *out = jwt_malloc(*len); + if (*out == NULL) + return ENOMEM; + + if (gnutls_hmac_fast(alg, jwt->key, jwt->key_len, str, str_len, *out)) { + jwt_freemem(*out); + *out = NULL; + return EINVAL; + } + + return 0; +} + +static int gnutls_verify_sha_hmac(jwt_t *jwt, const char *head, + unsigned int head_len, const char *sig) +{ + char *sig_check, *buf = NULL; + unsigned int len; + int ret; + + if (gnutls_sign_sha_hmac(jwt, &sig_check, &len, head, head_len)) + return EINVAL; + + ret = jwt_base64uri_encode(&buf, sig_check, len); + if (ret <= 0 || buf == NULL) + return -ret; + + ret = jwt_strcmp(sig, buf) ? EINVAL : 0; + + jwt_freemem(buf); + jwt_freemem(sig_check); + + return ret; +} + +static int gnutls_sign_sha_pem(jwt_t *jwt, char **out, unsigned int *len, + const char *str, unsigned int str_len) +{ + /* For EC handling. */ + int r_padding = 0, s_padding = 0, r_out_padding = 0, + s_out_padding = 0; + size_t out_size; + + gnutls_x509_privkey_t key; + gnutls_privkey_t privkey; + gnutls_datum_t key_dat = { + jwt->key, + jwt->key_len + }; + gnutls_datum_t body_dat = { + (unsigned char *)str, + str_len + }; + gnutls_datum_t sig_dat, r, s; + int ret = 0, pk_alg; + int alg, adj; + + /* Initialize for checking later. */ + *out = NULL; + + switch (jwt->alg) { + /* RSA */ + case JWT_ALG_RS256: + alg = GNUTLS_DIG_SHA256; + pk_alg = GNUTLS_PK_RSA; + break; + case JWT_ALG_RS384: + alg = GNUTLS_DIG_SHA384; + pk_alg = GNUTLS_PK_RSA; + break; + case JWT_ALG_RS512: + alg = GNUTLS_DIG_SHA512; + pk_alg = GNUTLS_PK_RSA; + break; + + /* RSA-PSS */ + case JWT_ALG_PS256: + alg = GNUTLS_DIG_SHA256; + pk_alg = GNUTLS_PK_RSA_PSS; + break; + case JWT_ALG_PS384: + alg = GNUTLS_DIG_SHA384; + pk_alg = GNUTLS_PK_RSA_PSS; + break; + case JWT_ALG_PS512: + alg = GNUTLS_DIG_SHA512; + pk_alg = GNUTLS_PK_RSA_PSS; + break; + + /* EC */ + case JWT_ALG_ES256: + case JWT_ALG_ES256K: + alg = GNUTLS_DIG_SHA256; + pk_alg = GNUTLS_PK_EC; + break; + case JWT_ALG_ES384: + alg = GNUTLS_DIG_SHA384; + pk_alg = GNUTLS_PK_EC; + break; + case JWT_ALG_ES512: + alg = GNUTLS_DIG_SHA512; + pk_alg = GNUTLS_PK_EC; + break; + + /* EdDSA */ + case JWT_ALG_EDDSA: + alg = GNUTLS_DIG_SHA512; + pk_alg = GNUTLS_PK_EDDSA_ED25519; + break; + + default: + return EINVAL; + } + + /* Initialize signature process data */ + if (gnutls_x509_privkey_init(&key)) + return ENOMEM; + + if (gnutls_x509_privkey_import(key, &key_dat, GNUTLS_X509_FMT_PEM)) { + ret = EINVAL; + goto sign_clean_key; + } + + if (gnutls_privkey_init(&privkey)) { + ret = ENOMEM; + goto sign_clean_key; + } + + if (gnutls_privkey_import_x509(privkey, key, 0)) { + ret = EINVAL; + goto sign_clean_privkey; + } + + if (pk_alg != gnutls_privkey_get_pk_algorithm(privkey, NULL)) { + ret = EINVAL; + goto sign_clean_privkey; + } + + /* XXX Get curve name for ES256K case and make sure it's secp256k1 */ + + /* XXX Get EC curve bits and make sure it matches ES* alg type */ + + /* Sign data */ + if (gnutls_privkey_sign_data(privkey, alg, 0, &body_dat, &sig_dat)) { + ret = EINVAL; + goto sign_clean_privkey; + } + + if (pk_alg == GNUTLS_PK_EC) { + /* Start EC handling. */ + if ((ret = gnutls_decode_rs_value(&sig_dat, &r, &s))) { + ret = EINVAL; + goto sign_clean_privkey; + } + + /* Check r and s size */ + if (jwt->alg == JWT_ALG_ES256 || jwt->alg == JWT_ALG_ES256K) + adj = 32; + if (jwt->alg == JWT_ALG_ES384) + adj = 48; + if (jwt->alg == JWT_ALG_ES512) + adj = 66; + + if (r.size > adj) + r_padding = r.size - adj; + else if (r.size < adj) + r_out_padding = adj - r.size; + + if (s.size > adj) + s_padding = s.size - adj; + else if (s.size < adj) + s_out_padding = adj - s.size; + + out_size = adj << 1; + + *out = jwt_malloc(out_size); + if (*out == NULL) { + ret = ENOMEM; + goto sign_clean_privkey; + } + memset(*out, 0, out_size); + + memcpy(*out + r_out_padding, r.data + r_padding, r.size - r_padding); + memcpy(*out + (r.size - r_padding + r_out_padding) + s_out_padding, + s.data + s_padding, (s.size - s_padding)); + + *len = (r.size - r_padding + r_out_padding) + + (s.size - s_padding + s_out_padding); + gnutls_free(r.data); + gnutls_free(s.data); + } else { + /* All others that aren't EC */ + *out = jwt_malloc(sig_dat.size); + if (*out == NULL) { + ret = ENOMEM; + goto sign_clean_privkey; + } + + /* Copy signature to out */ + memcpy(*out, sig_dat.data, sig_dat.size); + *len = sig_dat.size; + } + + /* Clean and exit */ + gnutls_free(sig_dat.data); + +sign_clean_privkey: + gnutls_privkey_deinit(privkey); + +sign_clean_key: + gnutls_x509_privkey_deinit(key); + + if (ret && *out) { + jwt_freemem(*out); + *out = NULL; + } + + return ret; +} + +static int gnutls_verify_sha_pem(jwt_t *jwt, const char *head, + unsigned int head_len, const char *sig_b64) +{ + gnutls_datum_t r, s; + gnutls_datum_t cert_dat = { + jwt->key, + jwt->key_len + }; + gnutls_datum_t data = { + (unsigned char *)head, + head_len + }; + gnutls_datum_t sig_dat = { NULL, 0 }; + gnutls_pubkey_t pubkey; + int alg, ret = 0, sig_len; + unsigned char *sig = NULL; + + switch (jwt->alg) { + /* RSA */ + case JWT_ALG_RS256: + alg = GNUTLS_SIGN_RSA_SHA256; + break; + case JWT_ALG_RS384: + alg = GNUTLS_SIGN_RSA_SHA384; + break; + case JWT_ALG_RS512: + alg = GNUTLS_SIGN_RSA_SHA512; + break; + + /* RSA-PSS */ + case JWT_ALG_PS256: + alg = GNUTLS_SIGN_RSA_PSS_SHA256; + break; + case JWT_ALG_PS384: + alg = GNUTLS_SIGN_RSA_PSS_SHA384; + break; + case JWT_ALG_PS512: + alg = GNUTLS_SIGN_RSA_PSS_SHA512; + break; + + /* EC */ + case JWT_ALG_ES256: + case JWT_ALG_ES256K: + alg = GNUTLS_SIGN_ECDSA_SHA256; + break; + case JWT_ALG_ES384: + alg = GNUTLS_SIGN_ECDSA_SHA384; + break; + case JWT_ALG_ES512: + alg = GNUTLS_SIGN_ECDSA_SHA512; + break; + + /* EdDSA */ + case JWT_ALG_EDDSA: + alg = GNUTLS_SIGN_EDDSA_ED25519; + break; + + default: + return EINVAL; + } + + sig = (unsigned char *)jwt_base64uri_decode(sig_b64, &sig_len); + + if (sig == NULL) + return EINVAL; + + if (gnutls_pubkey_init(&pubkey)) { + ret = EINVAL; + goto verify_clean_sig; + } + + if ((ret = gnutls_pubkey_import(pubkey, &cert_dat, GNUTLS_X509_FMT_PEM))) { + ret = EINVAL; + goto verify_clean_pubkey; + } + + /* Rebuild signature using r and s extracted from sig when jwt->alg + * is ESxxx. */ + switch (jwt->alg) { + case JWT_ALG_ES256: + case JWT_ALG_ES256K: + case JWT_ALG_ES384: + case JWT_ALG_ES512: + /* XXX Gotta be a better way. */ + if (sig_len == 64) { + r.size = 32; + r.data = sig; + s.size = 32; + s.data = sig + 32; + } else if (sig_len == 96) { + r.size = 48; + r.data = sig; + s.size = 48; + s.data = sig + 48; + } else if (sig_len == 132) { + r.size = 66; + r.data = sig; + s.size = 66; + s.data = sig + 66; + } else { + ret = EINVAL; + goto verify_clean_pubkey; + } + + if (gnutls_encode_rs_value(&sig_dat, &r, &s) || + gnutls_pubkey_verify_data2(pubkey, alg, 0, &data, &sig_dat)) + ret = EINVAL; + + if (sig_dat.data != NULL) + gnutls_free(sig_dat.data); + + break; + + default: + /* Use simple signature verification. */ + sig_dat.size = sig_len; + sig_dat.data = sig; + + if (gnutls_pubkey_verify_data2(pubkey, alg, 0, &data, &sig_dat)) + ret = EINVAL; + } + +verify_clean_pubkey: + gnutls_pubkey_deinit(pubkey); + +verify_clean_sig: + jwt_freemem(sig); + + return ret; +} + +/* Export our ops */ +struct jwt_crypto_ops jwt_gnutls_ops = { + .name = "gnutls", + .sign_sha_hmac = gnutls_sign_sha_hmac, + .verify_sha_hmac = gnutls_verify_sha_hmac, + .sign_sha_pem = gnutls_sign_sha_pem, + .verify_sha_pem = gnutls_verify_sha_pem, +}; diff --git a/libjwt/jwt-openssl.c b/libjwt/jwt-openssl.c new file mode 100644 index 00000000..c067670a --- /dev/null +++ b/libjwt/jwt-openssl.c @@ -0,0 +1,540 @@ +/* Copyright (C) 2015-2024 Ben Collins + This file is part of the JWT C Library + + SPDX-License-Identifier: MPL-2.0 + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "jwt-private.h" + +/* Routines to support crypto in LibJWT using OpenSSL. */ + +/* Functions to make libjwt backward compatible with OpenSSL version < 1.1.0 + * See https://wiki.openssl.org/index.php/1.1_API_Changes + */ +#if OPENSSL_VERSION_NUMBER < 0x10100000L + +static void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) +{ + if (pr != NULL) + *pr = sig->r; + if (ps != NULL) + *ps = sig->s; +} + +static int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s) +{ + if (r == NULL || s == NULL) + return 0; + + BN_clear_free(sig->r); + BN_clear_free(sig->s); + sig->r = r; + sig->s = s; + + return 1; +} + +#endif + +static int openssl_sign_sha_hmac(jwt_t *jwt, char **out, unsigned int *len, + const char *str, unsigned int str_len) +{ + const EVP_MD *alg; + + switch (jwt->alg) { + /* HMAC */ + case JWT_ALG_HS256: + alg = EVP_sha256(); + break; + case JWT_ALG_HS384: + alg = EVP_sha384(); + break; + case JWT_ALG_HS512: + alg = EVP_sha512(); + break; + default: + return EINVAL; + } + + *out = jwt_malloc(EVP_MAX_MD_SIZE); + if (*out == NULL) + return ENOMEM; + + HMAC(alg, jwt->key, jwt->key_len, + (const unsigned char *)str, str_len, (unsigned char *)*out, + len); + + return 0; +} + +static int openssl_verify_sha_hmac(jwt_t *jwt, const char *head, + unsigned int head_len, const char *sig) +{ + unsigned char res[EVP_MAX_MD_SIZE]; + unsigned int res_len; + const EVP_MD *alg; + char *buf = NULL; + int ret; + + switch (jwt->alg) { + case JWT_ALG_HS256: + alg = EVP_sha256(); + break; + case JWT_ALG_HS384: + alg = EVP_sha384(); + break; + case JWT_ALG_HS512: + alg = EVP_sha512(); + break; + default: + return EINVAL; + } + + HMAC(alg, jwt->key, jwt->key_len, + (const unsigned char *)head, head_len, res, &res_len); + + ret = jwt_base64uri_encode(&buf, (char *)res, res_len); + if (ret <= 0) + return -ret; + + ret = jwt_strcmp(buf, sig) ? EINVAL : 0; + jwt_freemem(buf); + + /* And now... */ + return ret; +} + +#define EC_ERROR(__err) { return -(__err); } + +static size_t __degree_and_check(EVP_PKEY *pkey, jwt_t *jwt) +{ + size_t degree; + int curve_nid; + +#if OPENSSL_VERSION_NUMBER < 0x30000000L + const EC_GROUP *group; + const EC_KEY *ec_key; + + ec_key = EVP_PKEY_get0_EC_KEY(pkey); + if (ec_key == NULL) + EC_ERROR(EINVAL); + + group = EC_KEY_get0_group(ec_key); + if (group == NULL) + EC_ERROR(EINVAL); + + curve_nid = EC_GROUP_get_curve_name(group); + degree = EC_GROUP_get_degree(group); +#else + EC_GROUP *group; + char curve_name[80]; + size_t len; + + if (!EVP_PKEY_get_group_name(pkey, curve_name, sizeof(curve_name), &len)) + EC_ERROR(EINVAL); + curve_name[len] = '\0'; + + curve_nid = OBJ_txt2nid(curve_name); + if (curve_nid == NID_undef) + EC_ERROR(EINVAL); + group = EC_GROUP_new_by_curve_name(curve_nid); + if (group == NULL) + EC_ERROR(EINVAL); + + degree = EC_GROUP_get_degree(group); + EC_GROUP_free(group); +#endif + + /* We only perform this check for ES256K. All others we just check + * the degree (bits). */ + if (jwt->alg == JWT_ALG_ES256K && curve_nid != NID_secp256k1) + EC_ERROR(EINVAL); + + return degree; +} + +static int jwt_degree_for_key(EVP_PKEY *pkey, jwt_t *jwt) +{ + size_t degree = __degree_and_check(pkey, jwt); + + /* Final check for matching degree */ + switch (jwt->alg) { + case JWT_ALG_ES256: + case JWT_ALG_ES256K: + if (degree != 256) + return -EINVAL; + break; + case JWT_ALG_ES384: + if (degree != 384) + return -EINVAL; + break; + case JWT_ALG_ES512: + /* This is not a typo. ES512 uses secp521r1 */ + if (degree != 521) + return -EINVAL; + break; + default: + return -EINVAL; + } + + return degree; +} + +#define SIGN_ERROR(__err) { ret = __err; goto jwt_sign_sha_pem_done; } + +static int openssl_sign_sha_pem(jwt_t *jwt, char **out, unsigned int *len, + const char *str, unsigned int str_len) +{ + EVP_MD_CTX *mdctx = NULL; + EVP_PKEY_CTX *pkey_ctx = NULL; + ECDSA_SIG *ec_sig = NULL; + const BIGNUM *ec_sig_r = NULL; + const BIGNUM *ec_sig_s = NULL; + BIO *bufkey = NULL; + const EVP_MD *alg; + int type; + EVP_PKEY *pkey = NULL; + unsigned char *sig; + int ret = 0; + size_t slen; + + switch (jwt->alg) { + /* RSA */ + case JWT_ALG_RS256: + alg = EVP_sha256(); + type = EVP_PKEY_RSA; + break; + case JWT_ALG_RS384: + alg = EVP_sha384(); + type = EVP_PKEY_RSA; + break; + case JWT_ALG_RS512: + alg = EVP_sha512(); + type = EVP_PKEY_RSA; + break; + + /* RSA-PSS */ + case JWT_ALG_PS256: + alg = EVP_sha256(); + type = EVP_PKEY_RSA_PSS; + break; + case JWT_ALG_PS384: + alg = EVP_sha384(); + type = EVP_PKEY_RSA_PSS; + break; + case JWT_ALG_PS512: + alg = EVP_sha512(); + type = EVP_PKEY_RSA_PSS; + break; + + /* ECC */ + case JWT_ALG_ES256: + case JWT_ALG_ES256K: + alg = EVP_sha256(); + type = EVP_PKEY_EC; + break; + case JWT_ALG_ES384: + alg = EVP_sha384(); + type = EVP_PKEY_EC; + break; + case JWT_ALG_ES512: + alg = EVP_sha512(); + type = EVP_PKEY_EC; + break; + + /* EdDSA */ + case JWT_ALG_EDDSA: + alg = NULL; + type = EVP_PKEY_ED25519; + break; + + default: + return EINVAL; + } + + bufkey = BIO_new_mem_buf(jwt->key, jwt->key_len); + if (bufkey == NULL) + SIGN_ERROR(ENOMEM); + + /* This uses OpenSSL's default passphrase callback if needed. The + * library caller can override this in many ways, all of which are + * outside of the scope of LibJWT and this is documented in jwt.h. */ + pkey = PEM_read_bio_PrivateKey(bufkey, NULL, NULL, NULL); + if (pkey == NULL) + SIGN_ERROR(EINVAL); + + if (type != EVP_PKEY_id(pkey)) + SIGN_ERROR(EINVAL); + + mdctx = EVP_MD_CTX_create(); + if (mdctx == NULL) + SIGN_ERROR(ENOMEM); + + /* Initialize the DigestSign operation using alg */ + if (EVP_DigestSignInit(mdctx, &pkey_ctx, alg, NULL, pkey) != 1) + SIGN_ERROR(EINVAL); + + if (type == EVP_PKEY_RSA_PSS) { + if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) < 0) + SIGN_ERROR(EINVAL); + if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, RSA_PSS_SALTLEN_DIGEST) < 0) + SIGN_ERROR(EINVAL); + } + + /* Get the size of sig first */ + if (EVP_DigestSign(mdctx, NULL, &slen, (const unsigned char *)str, str_len) != 1) + SIGN_ERROR(EINVAL); + + /* Allocate memory for signature based on returned size */ + sig = alloca(slen); + if (sig == NULL) + SIGN_ERROR(ENOMEM); + + /* Actual signing */ + if (EVP_DigestSign(mdctx, sig, &slen, (const unsigned char *)str, str_len) != 1) + SIGN_ERROR(EINVAL); + + if (type != EVP_PKEY_EC) { + *out = jwt_malloc(slen); + if (*out == NULL) + SIGN_ERROR(ENOMEM); + memcpy(*out, sig, slen); + *len = slen; + } else { + unsigned int bn_len, r_len, s_len, buf_len; + unsigned char *raw_buf; + + /* For EC we need to convert to a raw format of R/S. */ + int degree = jwt_degree_for_key(pkey, jwt); + if (degree < 0) + SIGN_ERROR(-degree); + + /* Get the sig from the DER encoded version. */ + ec_sig = d2i_ECDSA_SIG(NULL, (const unsigned char **)&sig, slen); + if (ec_sig == NULL) + SIGN_ERROR(ENOMEM); + + ECDSA_SIG_get0(ec_sig, &ec_sig_r, &ec_sig_s); + r_len = BN_num_bytes(ec_sig_r); + s_len = BN_num_bytes(ec_sig_s); + bn_len = (degree + 7) / 8; + if ((r_len > bn_len) || (s_len > bn_len)) + SIGN_ERROR(EINVAL); + + buf_len = 2 * bn_len; + raw_buf = alloca(buf_len); + if (raw_buf == NULL) + SIGN_ERROR(ENOMEM); + + /* Pad the bignums with leading zeroes. */ + memset(raw_buf, 0, buf_len); + BN_bn2bin(ec_sig_r, raw_buf + bn_len - r_len); + BN_bn2bin(ec_sig_s, raw_buf + buf_len - s_len); + + *out = jwt_malloc(buf_len); + if (*out == NULL) + SIGN_ERROR(ENOMEM); + memcpy(*out, raw_buf, buf_len); + *len = buf_len; + } + +jwt_sign_sha_pem_done: + if (bufkey) + BIO_free(bufkey); + if (pkey) + EVP_PKEY_free(pkey); + if (mdctx) + EVP_MD_CTX_destroy(mdctx); + if (ec_sig) + ECDSA_SIG_free(ec_sig); + + return ret; +} + +#define VERIFY_ERROR(__err) { ret = __err; goto jwt_verify_sha_pem_done; } + +static int openssl_verify_sha_pem(jwt_t *jwt, const char *head, + unsigned int head_len, const char *sig_b64) +{ + unsigned char *sig = NULL; + EVP_MD_CTX *mdctx = NULL; + EVP_PKEY_CTX *pkey_ctx = NULL; + ECDSA_SIG *ec_sig = NULL; + BIGNUM *ec_sig_r = NULL; + BIGNUM *ec_sig_s = NULL; + EVP_PKEY *pkey = NULL; + const EVP_MD *alg; + int type; + BIO *bufkey = NULL; + int ret = 0; + int slen; + + switch (jwt->alg) { + /* RSA */ + case JWT_ALG_RS256: + alg = EVP_sha256(); + type = EVP_PKEY_RSA; + break; + case JWT_ALG_RS384: + alg = EVP_sha384(); + type = EVP_PKEY_RSA; + break; + case JWT_ALG_RS512: + alg = EVP_sha512(); + type = EVP_PKEY_RSA; + break; + + /* RSA-PSS */ + case JWT_ALG_PS256: + alg = EVP_sha256(); + type = EVP_PKEY_RSA_PSS; + break; + case JWT_ALG_PS384: + alg = EVP_sha384(); + type = EVP_PKEY_RSA_PSS; + break; + case JWT_ALG_PS512: + alg = EVP_sha512(); + type = EVP_PKEY_RSA_PSS; + break; + + /* ECC */ + case JWT_ALG_ES256: + case JWT_ALG_ES256K: + alg = EVP_sha256(); + type = EVP_PKEY_EC; + break; + case JWT_ALG_ES384: + alg = EVP_sha384(); + type = EVP_PKEY_EC; + break; + case JWT_ALG_ES512: + alg = EVP_sha512(); + type = EVP_PKEY_EC; + break; + + /* EdDSA */ + case JWT_ALG_EDDSA: + alg = NULL; + type = EVP_PKEY_ED25519; + break; + + default: + return EINVAL; + } + + sig = jwt_base64uri_decode(sig_b64, &slen); + if (sig == NULL) + VERIFY_ERROR(EINVAL); + + bufkey = BIO_new_mem_buf(jwt->key, jwt->key_len); + if (bufkey == NULL) + VERIFY_ERROR(ENOMEM); + + /* This uses OpenSSL's default passphrase callback if needed. The + * library caller can override this in many ways, all of which are + * outside of the scope of LibJWT and this is documented in jwt.h. */ + pkey = PEM_read_bio_PUBKEY(bufkey, NULL, NULL, NULL); + if (pkey == NULL) + VERIFY_ERROR(EINVAL); + + if (type != EVP_PKEY_id(pkey)) + VERIFY_ERROR(EINVAL); + + /* Convert EC sigs back to ASN1. */ + if (type == EVP_PKEY_EC) { + unsigned int bn_len; + int degree; + unsigned char *p; + + ec_sig = ECDSA_SIG_new(); + if (ec_sig == NULL) + VERIFY_ERROR(ENOMEM); + + degree = jwt_degree_for_key(pkey, jwt); + if (degree < 0) + VERIFY_ERROR(-degree); + + bn_len = (degree + 7) / 8; + if ((bn_len * 2) != slen) + VERIFY_ERROR(EINVAL); + + ec_sig_r = BN_bin2bn(sig, bn_len, NULL); + ec_sig_s = BN_bin2bn(sig + bn_len, bn_len, NULL); + if (ec_sig_r == NULL || ec_sig_s == NULL) + VERIFY_ERROR(EINVAL); + + ECDSA_SIG_set0(ec_sig, ec_sig_r, ec_sig_s); + jwt_freemem(sig); + + slen = i2d_ECDSA_SIG(ec_sig, NULL); + sig = jwt_malloc(slen); + if (sig == NULL) + VERIFY_ERROR(ENOMEM); + + p = sig; + slen = i2d_ECDSA_SIG(ec_sig, &p); + + if (slen == 0) + VERIFY_ERROR(EINVAL); + } + + mdctx = EVP_MD_CTX_create(); + if (mdctx == NULL) + VERIFY_ERROR(ENOMEM); + + /* Initialize the DigestVerify operation using alg */ + if (EVP_DigestVerifyInit(mdctx, &pkey_ctx, alg, NULL, pkey) != 1) + VERIFY_ERROR(EINVAL); + + if (type == EVP_PKEY_RSA_PSS) { + if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) < 0) + VERIFY_ERROR(EINVAL); + if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, RSA_PSS_SALTLEN_DIGEST) < 0) + VERIFY_ERROR(EINVAL); + } + + /* One-shot update and verify */ + if (EVP_DigestVerify(mdctx, sig, slen, (const unsigned char *)head, head_len) != 1) + VERIFY_ERROR(EINVAL); + +jwt_verify_sha_pem_done: + if (bufkey) + BIO_free(bufkey); + if (pkey) + EVP_PKEY_free(pkey); + if (mdctx) + EVP_MD_CTX_destroy(mdctx); + if (sig) + jwt_freemem(sig); + if (ec_sig) + ECDSA_SIG_free(ec_sig); + + return ret; +} + +/* Export our ops */ +struct jwt_crypto_ops jwt_openssl_ops = { + .name = "openssl", + .sign_sha_hmac = openssl_sign_sha_hmac, + .verify_sha_hmac = openssl_verify_sha_hmac, + .sign_sha_pem = openssl_sign_sha_pem, + .verify_sha_pem = openssl_verify_sha_pem, +}; diff --git a/libjwt/jwt-private.h b/libjwt/jwt-private.h new file mode 100644 index 00000000..73f6e199 --- /dev/null +++ b/libjwt/jwt-private.h @@ -0,0 +1,67 @@ +/* Copyright (C) 2015-2024 Ben Collins + This file is part of the JWT C Library + + SPDX-License-Identifier: MPL-2.0 + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef JWT_PRIVATE_H +#define JWT_PRIVATE_H + +#include "config.h" + +#include +#include + +struct jwt { + jwt_alg_t alg; + unsigned char *key; + int key_len; + json_t *grants; + json_t *headers; +}; + +struct jwt_valid { + jwt_alg_t alg; + time_t now; + time_t nbf_leeway; + time_t exp_leeway; + int hdr; + json_t *req_grants; + unsigned int status; +}; + +/* Crypto operations */ +struct jwt_crypto_ops { + const char *name; + int (*sign_sha_hmac)(jwt_t *jwt, char **out, unsigned int *len, + const char *str, unsigned int str_len); + int (*verify_sha_hmac)(jwt_t *jwt, const char *head, + unsigned int head_len, const char *sig); + int (*sign_sha_pem)(jwt_t *jwt, char **out, unsigned int *len, + const char *str, unsigned int str_len); + int (*verify_sha_pem)(jwt_t *jwt, const char *head, + unsigned int head_len, const char *sig_b64); +}; + +#ifdef HAVE_OPENSSL +extern struct jwt_crypto_ops jwt_openssl_ops; +#endif +#ifdef HAVE_GNUTLS +extern struct jwt_crypto_ops jwt_gnutls_ops; +#endif + +/* Memory allocators. */ +void *jwt_malloc(size_t size); +void jwt_freemem(void *ptr); + +/* Helper routines to handle base64url encoding without percent padding + * as defined in RFC-4648. */ +int jwt_base64uri_encode(char **_dst, const char *plain, int plain_len); +void *jwt_base64uri_decode(const char *src, int *ret_len); + +/* A time-safe strcmp function */ +int jwt_strcmp(const char *str1, const char *str2); + +#endif /* JWT_PRIVATE_H */ diff --git a/libjwt/jwt-wincrypt.c b/libjwt/jwt-wincrypt.c new file mode 100644 index 00000000..2f3ac37e --- /dev/null +++ b/libjwt/jwt-wincrypt.c @@ -0,0 +1,1015 @@ +/* Copyright (C) 2015-2024 Ben Collins + This file is part of the JWT C Library + + SPDX-License-Identifier: MPL-2.0 + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include + +#include +#include +#include + +#include + +#include "jwt-private.h" + +/* Routines to support crypto in LibJWT using Windows crypto APIs. */ + +#define CERT_THUMBPRINT_STR_LEN 40 +#define CERT_THUMBPRINT_HASH_LEN 20 +#define MY_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING) + +#define PEM_PUBLIC_KEY_HEADER "-----BEGIN PUBLIC KEY-----" + +#define CERT_STORE_PREFIX "cert:\\" + +static int +get_cert_location( + char *uri, + int uri_len, + DWORD *store_name, + wchar_t **store_path, + CRYPT_HASH_BLOB *thumbprint) +{ + char *path; + int path_len; + char *sep; + size_t store_name_len; + size_t remainder_len; + size_t thumbprint_len; + char* thumbprint_str; + char* store_path_mbs = NULL; + size_t wchar_count; + wchar_t* wchar_buf = NULL; + errno_t ret; + + /* Certificate store path starts with cert:\ */ + if (strncmp(uri, CERT_STORE_PREFIX, strlen(CERT_STORE_PREFIX))) + return EINVAL; + + path = uri + strlen(CERT_STORE_PREFIX); + path_len = uri_len - (int)strlen(CERT_STORE_PREFIX); + sep = memchr(path, '\\', path_len); + if (!sep) + { + ret = EINVAL; + goto get_cert_location_done; + } + + store_name_len = sep - path; + + if (!strncmp(path, "CurrentUser", store_name_len)) + *store_name = CERT_SYSTEM_STORE_CURRENT_USER; + else if (!strncmp(path, "LocalMachine", store_name_len)) + *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE; + else if (!strncmp(path, "CurrentService", store_name_len)) + *store_name = CERT_SYSTEM_STORE_CURRENT_SERVICE; + else if (!strncmp(path, "Services", store_name_len)) + *store_name = CERT_SYSTEM_STORE_SERVICES; + else if (!strncmp(path, "Users", store_name_len)) + *store_name = CERT_SYSTEM_STORE_USERS; + else if (!strncmp(path, "CurrentUserGroupPolicy", store_name_len)) + *store_name = CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY; + else if (!strncmp(path, "LocalMachineGroupPolicy", store_name_len)) + *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY; + else if (!strncmp(path, "LocalMachineEnterprise", store_name_len)) + *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE; + else + { + ret = EINVAL; + goto get_cert_location_done; + } + + remainder_len = path_len - (store_name_len + 1); + store_path_mbs = (char*)jwt_malloc(remainder_len); + if (!store_path_mbs) + { + ret = ENOMEM; + goto get_cert_location_done; + } + + if (memcpy_s(store_path_mbs, remainder_len, sep + 1, remainder_len)) + { + ret = EINVAL; + goto get_cert_location_done; + } + + sep = memchr(store_path_mbs, '\\', remainder_len); + if (!sep) + { + ret = EINVAL; + goto get_cert_location_done; + } + + *sep = 0; + + thumbprint_str = sep + 1; + thumbprint_len = remainder_len - (thumbprint_str - store_path_mbs); + if (thumbprint_len != CERT_THUMBPRINT_STR_LEN) + { + ret = EINVAL; + goto get_cert_location_done; + } + + if (!CryptStringToBinaryA( + thumbprint_str, + CERT_THUMBPRINT_STR_LEN, + CRYPT_STRING_HEX, + thumbprint->pbData, + &thumbprint->cbData, + NULL, + NULL)) + { + ret = EINVAL; + goto get_cert_location_done; + } + + /* Convert store_name to wide chars, which cert API requires. */ + if (mbstowcs_s(&wchar_count, NULL, 0, store_path_mbs, 0)) + { + ret = EINVAL; + goto get_cert_location_done; + } + + wchar_buf = (wchar_t*)jwt_malloc(wchar_count * sizeof(wchar_t)); + if (!wchar_buf) + { + ret = ENOMEM; + goto get_cert_location_done; + } + + if (mbstowcs_s(NULL, wchar_buf, wchar_count, store_path_mbs, _TRUNCATE)) + { + ret = EINVAL; + goto get_cert_location_done; + } + + *store_path = wchar_buf; + ret = 0; + +get_cert_location_done: + if (store_path_mbs) + jwt_freemem(store_path_mbs); + if (ret && wchar_buf) + jwt_freemem(wchar_buf); + + return ret; +} + +#define OPEN_PRIVATE_STORE_ERROR(__err) { ret = __err; goto open_private_key_from_store_done; } + +static int open_private_key_from_store( + unsigned char* key, + int key_len, + HCERTSTORE* hCertStore_out, + PCCERT_CONTEXT* pSignerCert_out, + NCRYPT_PROV_HANDLE* hStorageProv_out, + NCRYPT_KEY_HANDLE* hKey_out) +{ + int ret = EINVAL; + DWORD store_name; + wchar_t* store_path = NULL; + CRYPT_HASH_BLOB thumbprint; + BYTE thumbprint_buf[CERT_THUMBPRINT_HASH_LEN]; + DWORD keyProvInfoSize; + HCERTSTORE hCertStore = NULL; + PCCERT_CONTEXT pSignerCert = NULL; + CRYPT_KEY_PROV_INFO *pKeyProvInfo = NULL; + DWORD dwKeyFlags; + NCRYPT_PROV_HANDLE hStorageProv = (NCRYPT_PROV_HANDLE)NULL; + NCRYPT_KEY_HANDLE hKey = (NCRYPT_KEY_HANDLE)NULL; + + *hCertStore_out = NULL; + *pSignerCert_out = NULL; + *hStorageProv_out = (NCRYPT_PROV_HANDLE)NULL; + *hKey_out = (NCRYPT_KEY_HANDLE)NULL; + + /* Break down cert path. */ + thumbprint.pbData = thumbprint_buf; + thumbprint.cbData = CERT_THUMBPRINT_HASH_LEN; + ret = get_cert_location(key, key_len, &store_name, &store_path, &thumbprint); + if (ret) + goto open_private_key_from_store_done; + + /* Open the certificate store. */ + if (!(hCertStore = CertOpenStore( + CERT_STORE_PROV_SYSTEM, + 0, + (HCRYPTPROV_LEGACY)NULL, + store_name | CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG, + store_path))) + OPEN_PRIVATE_STORE_ERROR(ENOENT); + + /* + * Get a pointer to the signer's certificate. + * This certificate must have access to the signer's private key. + */ + if (!(pSignerCert = CertFindCertificateInStore( + hCertStore, + MY_ENCODING_TYPE, + 0, + CERT_FIND_HASH, + &thumbprint, + NULL))) + OPEN_PRIVATE_STORE_ERROR(ENOENT); + + /* Get private key provider info. */ + if (!(CertGetCertificateContextProperty( + pSignerCert, + CERT_KEY_PROV_INFO_PROP_ID, + NULL, + &keyProvInfoSize))) + OPEN_PRIVATE_STORE_ERROR(ENOENT); + + if (!(pKeyProvInfo = (CRYPT_KEY_PROV_INFO *)jwt_malloc(keyProvInfoSize))) + OPEN_PRIVATE_STORE_ERROR(ENOMEM); + + if (!CertGetCertificateContextProperty( + pSignerCert, + CERT_KEY_PROV_INFO_PROP_ID, + pKeyProvInfo, + &keyProvInfoSize)) + OPEN_PRIVATE_STORE_ERROR(ENOENT); + + /* Open key using NCrypt. */ + if (NCryptOpenStorageProvider( + &hStorageProv, + pKeyProvInfo->pwszProvName, + 0) != ERROR_SUCCESS) + OPEN_PRIVATE_STORE_ERROR(ENOENT); + + dwKeyFlags = NCRYPT_SILENT_FLAG | + ((store_name == CERT_SYSTEM_STORE_LOCAL_MACHINE) ? NCRYPT_MACHINE_KEY_FLAG : 0); + if (NCryptOpenKey( + hStorageProv, + &hKey, + pKeyProvInfo->pwszContainerName, + 0, + dwKeyFlags) != ERROR_SUCCESS) + OPEN_PRIVATE_STORE_ERROR(ENOENT); + + ret = 0; + +open_private_key_from_store_done: + if (store_path) + jwt_freemem(store_path); + + if (pKeyProvInfo) + jwt_freemem(pKeyProvInfo); + + if (ret) + { + if (pSignerCert) + CertFreeCertificateContext(pSignerCert); + + if (hCertStore) + CertCloseStore(hCertStore, CERT_CLOSE_STORE_CHECK_FLAG); + + if (hKey) + NCryptFreeObject(hKey); + + if (hStorageProv) + NCryptFreeObject(hStorageProv); + } + else + { + *pSignerCert_out = pSignerCert; + *hCertStore_out = hCertStore; + *hKey_out = hKey; + *hStorageProv_out = hStorageProv; + } + + return ret; +} + +#define OPEN_PUBLIC_STORE_ERROR(__err) { ret = __err; goto open_public_key_from_store_done; } + +static int open_public_key_from_store( + unsigned char* key, + int key_len, + HCERTSTORE* hCertStore_out, + PCCERT_CONTEXT* pSignerCert_out, + BCRYPT_KEY_HANDLE* hKey_out) +{ + int ret = EINVAL; + DWORD store_name; + wchar_t* store_path = NULL; + CRYPT_HASH_BLOB thumbprint; + BYTE thumbprint_buf[CERT_THUMBPRINT_HASH_LEN]; + HCERTSTORE hCertStore = NULL; + PCCERT_CONTEXT pSignerCert = NULL; + BCRYPT_KEY_HANDLE hKey = (BCRYPT_KEY_HANDLE)NULL; + + *hCertStore_out = NULL; + *pSignerCert_out = NULL; + *hKey_out = (BCRYPT_KEY_HANDLE)NULL; + + /* Break down cert path. */ + thumbprint.pbData = thumbprint_buf; + thumbprint.cbData = CERT_THUMBPRINT_HASH_LEN; + ret = get_cert_location(key, key_len, &store_name, &store_path, &thumbprint); + if (ret) + goto open_public_key_from_store_done; + + /* Open the certificate store. */ + if (!(hCertStore = CertOpenStore( + CERT_STORE_PROV_SYSTEM, + 0, + (HCRYPTPROV_LEGACY)NULL, + store_name | CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG, + store_path))) + OPEN_PUBLIC_STORE_ERROR(ENOENT); + + /* + * Get a pointer to the signer's certificate. + * This certificate must have access to the signer's private key. + */ + if (!(pSignerCert = CertFindCertificateInStore( + hCertStore, + MY_ENCODING_TYPE, + 0, + CERT_FIND_HASH, + &thumbprint, + NULL))) + OPEN_PUBLIC_STORE_ERROR(ENOENT); + + if (!CryptImportPublicKeyInfoEx2(X509_ASN_ENCODING, &pSignerCert->pCertInfo->SubjectPublicKeyInfo, 0, NULL, &hKey)) + OPEN_PUBLIC_STORE_ERROR(EINVAL); + + ret = 0; + +open_public_key_from_store_done: + if (store_path) + jwt_freemem(store_path); + + if (ret) + { + if (pSignerCert) + CertFreeCertificateContext(pSignerCert); + + if (hCertStore) + CertCloseStore(hCertStore, CERT_CLOSE_STORE_CHECK_FLAG); + + if (hKey) + BCryptDestroyKey(hKey); + } + else + { + *pSignerCert_out = pSignerCert; + *hCertStore_out = hCertStore; + *hKey_out = hKey; + } + + return ret; +} + +#define OPEN_PUBLIC_PEM_ERROR(__err) { ret = __err; goto open_public_key_from_pem_done; } + +static int open_public_key_from_pem( + unsigned char* key, + int key_len, + BCRYPT_KEY_HANDLE* hKey_out) +{ + int ret = EINVAL; + BYTE* pbPublicKeyDer = NULL; + DWORD cbPublicKeyDer; + PCERT_PUBLIC_KEY_INFO pbPublicKeyInfo = NULL; + DWORD cbPublicKeyInfo; + BCRYPT_KEY_HANDLE hKey = (BCRYPT_KEY_HANDLE)NULL; + + /* Convert public key from PEM to DER. */ + if (!CryptStringToBinaryA( + key, + key_len, + CRYPT_STRING_BASE64HEADER, + NULL, + &cbPublicKeyDer, + NULL, + NULL)) + OPEN_PUBLIC_PEM_ERROR(EINVAL); + + if (!(pbPublicKeyDer = (BYTE *)jwt_malloc(cbPublicKeyDer))) + OPEN_PUBLIC_PEM_ERROR(ENOMEM); + + if (!CryptStringToBinaryA( + key, + key_len, + CRYPT_STRING_BASE64HEADER, + pbPublicKeyDer, + &cbPublicKeyDer, + NULL, + NULL)) + OPEN_PUBLIC_PEM_ERROR(EINVAL); + + /* Create public key info. */ + if (!CryptDecodeObjectEx( + X509_ASN_ENCODING, + X509_PUBLIC_KEY_INFO, + pbPublicKeyDer, + cbPublicKeyDer, + 0, + NULL, + NULL, + &cbPublicKeyInfo)) + OPEN_PUBLIC_PEM_ERROR(EINVAL); + + if (!(pbPublicKeyInfo = (PCERT_PUBLIC_KEY_INFO)jwt_malloc(cbPublicKeyInfo))) + OPEN_PUBLIC_PEM_ERROR(ENOMEM); + + if (!CryptDecodeObjectEx( + X509_ASN_ENCODING, + X509_PUBLIC_KEY_INFO, + pbPublicKeyDer, + cbPublicKeyDer, + 0, + NULL, + pbPublicKeyInfo, + &cbPublicKeyInfo)) + OPEN_PUBLIC_PEM_ERROR(EINVAL); + + /* Import public key into CNG. */ + if (!CryptImportPublicKeyInfoEx2( + X509_ASN_ENCODING, + pbPublicKeyInfo, + 0, + NULL, + &hKey)) + OPEN_PUBLIC_PEM_ERROR(EINVAL); + + *hKey_out = hKey; + ret = 0; + +open_public_key_from_pem_done: + if (pbPublicKeyDer) + jwt_freemem(pbPublicKeyDer); + + if (pbPublicKeyInfo) + jwt_freemem(pbPublicKeyInfo); + + if (ret) + { + if (hKey) + BCryptDestroyKey(hKey); + } + else + { + *hKey_out = hKey; + } + + return ret; +} + +static int is_public_key_pem(unsigned char* key, int key_len) +{ + if (key_len < strlen(PEM_PUBLIC_KEY_HEADER)) + return 0; + + return !memcmp(key, PEM_PUBLIC_KEY_HEADER, strlen(PEM_PUBLIC_KEY_HEADER)); +} + +#define SIGN_HMAC_ERROR(__err) { ret = __err; goto jwt_sign_sha_hmac_done; } + +int jwt_sign_sha_hmac(jwt_t *jwt, char **out, unsigned int *len, + const char *str) +{ + int ret = EINVAL; + LPCWSTR alg; + BCRYPT_ALG_HANDLE hAlg = (BCRYPT_ALG_HANDLE)NULL; + BCRYPT_HASH_HANDLE hHash = (BCRYPT_HASH_HANDLE)NULL; + BYTE* pbHashObject = NULL; + DWORD cbHashObject; + BYTE* pbHashValue = NULL; + DWORD cbHashValue; + DWORD cbDummy; + + /* Lookup algorithm type. */ + switch (jwt->alg) { + /* HMAC */ + case JWT_ALG_HS256: + alg = BCRYPT_SHA256_ALGORITHM; + break; + case JWT_ALG_HS384: + alg = BCRYPT_SHA384_ALGORITHM; + break; + case JWT_ALG_HS512: + alg = BCRYPT_SHA512_ALGORITHM; + break; + default: + /* For now, do not support ECC on Windows */ + SIGN_HMAC_ERROR(EINVAL); + } + + /* Hash the JWT using the selected algorithm, and with MAC. */ + if (BCryptOpenAlgorithmProvider( + &hAlg, + alg, + NULL, + BCRYPT_ALG_HANDLE_HMAC_FLAG) != ERROR_SUCCESS) + SIGN_HMAC_ERROR(EINVAL); + + if (BCryptGetProperty( + hAlg, + BCRYPT_OBJECT_LENGTH, + (PBYTE)&cbHashObject, + sizeof(DWORD), + &cbDummy, + 0) != ERROR_SUCCESS) + SIGN_HMAC_ERROR(EINVAL); + + if (!(pbHashObject = (BYTE*)jwt_malloc(cbHashObject))) + SIGN_HMAC_ERROR(ENOMEM); + + if (BCryptCreateHash( + hAlg, + &hHash, + pbHashObject, + cbHashObject, + jwt->key, + jwt->key_len, + 0) != ERROR_SUCCESS) + SIGN_HMAC_ERROR(EINVAL); + + if (BCryptGetProperty( + hAlg, + BCRYPT_HASH_LENGTH, + (PBYTE)&cbHashValue, + sizeof(DWORD), + &cbDummy, + 0) != ERROR_SUCCESS) + SIGN_HMAC_ERROR(EINVAL); + + if (!(pbHashValue = (BYTE*)jwt_malloc(cbHashValue))) + SIGN_HMAC_ERROR(ENOMEM); + + if (BCryptHashData( + hHash, + (PUCHAR)str, + (ULONG)strlen(str), + 0) != ERROR_SUCCESS) + SIGN_HMAC_ERROR(EINVAL); + + if (BCryptFinishHash( + hHash, + pbHashValue, + cbHashValue, + 0) != ERROR_SUCCESS) + SIGN_HMAC_ERROR(EINVAL); + + /* Done! */ + *out = pbHashValue; + *len = cbHashValue; + ret = 0; + +jwt_sign_sha_hmac_done: + if (hHash) + BCryptDestroyHash(hHash); + + if (hAlg) + BCryptCloseAlgorithmProvider(hAlg, 0); + + if (pbHashObject) + jwt_freemem(pbHashObject); + + /* Only free result string if function failed. */ + if (ret && pbHashValue) + jwt_freemem(pbHashValue); + + return ret; +} + +#define VERIFY_HMAC_ERROR(__err) { ret = __err; goto jwt_verify_hmac_done; } + +int jwt_verify_sha_hmac(jwt_t *jwt, const char *head, const char *sig) +{ + int ret; + char* pbHash = NULL; + unsigned int cbHash; + char* pbB64 = NULL; + DWORD cbB64; + + /* Compute the HMAC on the "head" string. */ + ret = jwt_sign_sha_hmac(jwt, &pbHash, &cbHash, head); + if (ret) + goto jwt_verify_hmac_done; + + /* Encode as Base64. */ + if (!CryptBinaryToStringA( + pbHash, + cbHash, + CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, + NULL, + &cbB64)) + VERIFY_HMAC_ERROR(EINVAL); + + /* Null terminator is already included in base64Size. */ + pbB64 = (char*)jwt_malloc(cbB64); + if (!pbB64) + VERIFY_HMAC_ERROR(ENOMEM); + + /* Get the actual value. */ + if (!CryptBinaryToStringA( + pbHash, + cbHash, + CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, + pbB64, + &cbB64)) + VERIFY_HMAC_ERROR(EINVAL); + + /* URI encode. */ + jwt_base64uri_encode(pbB64); + + /* And now... */ + ret = jwt_strcmp(pbB64, sig) ? EINVAL : 0; + +jwt_verify_hmac_done: + if (pbHash) + jwt_freemem(pbHash); + + if (pbB64) + jwt_freemem(pbB64); + + return ret; +} + +#define SIGN_PEM_ERROR(__err) { ret = __err; goto jwt_sign_sha_pem_done; } + +int jwt_sign_sha_pem(jwt_t *jwt, char **out, unsigned int *len, + const char *str) +{ + int ret = EINVAL; + LPCWSTR alg; + int isECDSA = 0; + HCERTSTORE hCertStore = NULL; + PCCERT_CONTEXT pSignerCert = NULL; + NCRYPT_PROV_HANDLE hStorageProv = (NCRYPT_PROV_HANDLE)NULL; + NCRYPT_KEY_HANDLE hStoreKey = (NCRYPT_KEY_HANDLE)NULL; + BCRYPT_ALG_HANDLE hAlg = (BCRYPT_ALG_HANDLE)NULL; + BCRYPT_HASH_HANDLE hHash = (BCRYPT_HASH_HANDLE)NULL; + BYTE* pbHashObject = NULL; + DWORD cbHashObject; + BYTE* pbHashValue = NULL; + DWORD cbHashValue; + DWORD cbDummy; + BCRYPT_PKCS1_PADDING_INFO paddingInfo; + BYTE* pbSignature = NULL; + DWORD cbSignature; + DWORD ncryptSignFlags; + + /* Lookup algorithm type. */ + switch (jwt->alg) { + /* RSA */ + case JWT_ALG_RS256: + alg = BCRYPT_SHA256_ALGORITHM; + isECDSA = 0; + break; + case JWT_ALG_RS384: + alg = BCRYPT_SHA384_ALGORITHM; + isECDSA = 0; + break; + case JWT_ALG_RS512: + alg = BCRYPT_SHA512_ALGORITHM; + isECDSA = 0; + break; + + /* ECC */ + case JWT_ALG_ES256: + case JWT_ALG_ES256K: + alg = BCRYPT_SHA256_ALGORITHM; + isECDSA = 1; + break; + case JWT_ALG_ES384: + alg = BCRYPT_SHA384_ALGORITHM; + isECDSA = 1; + break; + case JWT_ALG_ES512: + alg = BCRYPT_SHA512_ALGORITHM; + isECDSA = 1; + break; + + default: + SIGN_PEM_ERROR(EINVAL); + } + + /* + * Open private key in certificate store. + * + * Note that in Windows crypto APIs, there are no obvious ways to + * import a generic private key from a PEM encoded string, nor is + * it desirable on Windows platforms to have private keys outside + * of the Windows certificate store. Therefore, only support keys + * in the certificate store. + */ + ret = open_private_key_from_store( + jwt->key, + jwt->key_len, + &hCertStore, + &pSignerCert, + &hStorageProv, + &hStoreKey); + if (ret) + goto jwt_sign_sha_pem_done; + + /* Hash the JWT using the selected algorithm. */ + if (BCryptOpenAlgorithmProvider( + &hAlg, + alg, + NULL, + 0) != ERROR_SUCCESS) + SIGN_PEM_ERROR(EINVAL); + + if (BCryptGetProperty( + hAlg, + BCRYPT_OBJECT_LENGTH, + (PBYTE)&cbHashObject, + sizeof(DWORD), + &cbDummy, + 0) != ERROR_SUCCESS) + SIGN_PEM_ERROR(EINVAL); + + if (!(pbHashObject = (BYTE*)jwt_malloc(cbHashObject))) + SIGN_PEM_ERROR(ENOMEM); + + if (BCryptCreateHash( + hAlg, + &hHash, + pbHashObject, + cbHashObject, + NULL, + 0, + 0) != ERROR_SUCCESS) + SIGN_PEM_ERROR(EINVAL); + + if (BCryptGetProperty( + hAlg, + BCRYPT_HASH_LENGTH, + (PBYTE)&cbHashValue, + sizeof(DWORD), + &cbDummy, + 0) != ERROR_SUCCESS) + SIGN_PEM_ERROR(EINVAL); + + if (!(pbHashValue = (BYTE*)jwt_malloc(cbHashValue))) + SIGN_PEM_ERROR(ENOMEM); + + if (BCryptHashData( + hHash, + (PUCHAR)str, + (ULONG)strlen(str), + 0) != ERROR_SUCCESS) + SIGN_PEM_ERROR(EINVAL); + + if (BCryptFinishHash( + hHash, + pbHashValue, + cbHashValue, + 0) != ERROR_SUCCESS) + SIGN_PEM_ERROR(EINVAL); + + /* Sign the hash using the private key. */ + ncryptSignFlags = NCRYPT_SILENT_FLAG | (isECDSA ? 0 : NCRYPT_PAD_PKCS1_FLAG); + paddingInfo.pszAlgId = alg; + if (NCryptSignHash( + hStoreKey, + (isECDSA ? NULL : &paddingInfo), + pbHashValue, + cbHashValue, + NULL, + 0, + &cbSignature, + ncryptSignFlags) != ERROR_SUCCESS) + SIGN_PEM_ERROR(EINVAL); + + if (!(pbSignature = (BYTE*)jwt_malloc(cbSignature))) + SIGN_PEM_ERROR(ENOMEM); + + paddingInfo.pszAlgId = alg; + if (NCryptSignHash( + hStoreKey, + (isECDSA ? NULL : &paddingInfo), + pbHashValue, + cbHashValue, + pbSignature, + cbSignature, + &cbDummy, + ncryptSignFlags) != ERROR_SUCCESS) + SIGN_PEM_ERROR(EINVAL); + + /* Done! */ + *out = pbSignature; + *len = cbSignature; + ret = 0; + +jwt_sign_sha_pem_done: + if (hHash) + BCryptDestroyHash(hHash); + + if (hAlg) + BCryptCloseAlgorithmProvider(hAlg, 0); + + if (hStoreKey) + NCryptFreeObject(hStoreKey); + + if (hStorageProv) + NCryptFreeObject(hStorageProv); + + if (pSignerCert) + CertFreeCertificateContext(pSignerCert); + + if (hCertStore) + CertCloseStore(hCertStore, CERT_CLOSE_STORE_CHECK_FLAG); + + if (pbHashObject) + jwt_freemem(pbHashObject); + + if (pbHashValue) + jwt_freemem(pbHashValue); + + /* Only free result string if function failed. */ + if (ret && pbSignature) + jwt_freemem(pbSignature); + + return ret; +} + +#define VERIFY_PEM_ERROR(__err) { ret = __err; goto jwt_verify_sha_pem_done; } + +int jwt_verify_sha_pem(jwt_t *jwt, const char *head, const char *sig_b64) +{ + int ret = EINVAL; + LPCWSTR alg; + int isECDSA = 0; + BYTE* pbSignature = NULL; + DWORD cbSignature; + HCERTSTORE hCertStore = NULL; + PCCERT_CONTEXT pSignerCert = NULL; + BCRYPT_ALG_HANDLE hHashAlg = (BCRYPT_ALG_HANDLE)NULL; + BCRYPT_KEY_HANDLE hKey = (BCRYPT_KEY_HANDLE)NULL; + BCRYPT_HASH_HANDLE hHash = (BCRYPT_HASH_HANDLE)NULL; + BYTE* pbHashObject = NULL; + DWORD cbHashObject; + BYTE* pbHashValue = NULL; + DWORD cbHashValue; + DWORD cbDummy; + BCRYPT_PKCS1_PADDING_INFO paddingInfo; + + /* Lookup algorithm type. */ + switch (jwt->alg) { + /* RSA */ + case JWT_ALG_RS256: + alg = BCRYPT_SHA256_ALGORITHM; + isECDSA = 0; + break; + case JWT_ALG_RS384: + alg = BCRYPT_SHA384_ALGORITHM; + isECDSA = 0; + break; + case JWT_ALG_RS512: + alg = BCRYPT_SHA512_ALGORITHM; + isECDSA = 0; + break; + + /* ECC */ + case JWT_ALG_ES256: + case JWT_ALG_ES256K: + alg = BCRYPT_SHA256_ALGORITHM; + isECDSA = 1; + break; + case JWT_ALG_ES384: + alg = BCRYPT_SHA384_ALGORITHM; + isECDSA = 1; + break; + case JWT_ALG_ES512: + alg = BCRYPT_SHA512_ALGORITHM; + isECDSA = 1; + break; + + default: + VERIFY_PEM_ERROR(EINVAL); + } + + /* Decode signature. */ + if (!(pbSignature = jwt_b64_decode(sig_b64, &cbSignature))) + VERIFY_PEM_ERROR(EINVAL); + + /* Open handle to public key. */ + if (is_public_key_pem(jwt->key, jwt->key_len)) + { + ret = open_public_key_from_pem( + jwt->key, + jwt->key_len, + &hKey); + } + else + { + ret = open_public_key_from_store( + jwt->key, + jwt->key_len, + &hCertStore, + &pSignerCert, + &hKey); + } + if (ret) + goto jwt_verify_sha_pem_done; + + /* Hash data. */ + if (BCryptOpenAlgorithmProvider( + &hHashAlg, + alg, + NULL, + 0) != ERROR_SUCCESS) + VERIFY_PEM_ERROR(EINVAL); + + if (BCryptGetProperty( + hHashAlg, + BCRYPT_OBJECT_LENGTH, + (PBYTE)&cbHashObject, + sizeof(DWORD), + &cbDummy, + 0) != ERROR_SUCCESS) + VERIFY_PEM_ERROR(EINVAL); + + if (!(pbHashObject = (BYTE*)jwt_malloc(cbHashObject))) + VERIFY_PEM_ERROR(ENOMEM); + + if (BCryptCreateHash( + hHashAlg, + &hHash, + pbHashObject, + cbHashObject, + NULL, + 0, + 0) != ERROR_SUCCESS) + VERIFY_PEM_ERROR(EINVAL); + + if (BCryptGetProperty( + hHashAlg, + BCRYPT_HASH_LENGTH, + (PBYTE)&cbHashValue, + sizeof(DWORD), + &cbDummy, + 0) != ERROR_SUCCESS) + VERIFY_PEM_ERROR(EINVAL); + + if (!(pbHashValue = (BYTE*)jwt_malloc(cbHashValue))) + VERIFY_PEM_ERROR(ENOMEM); + + if (BCryptHashData( + hHash, + (PUCHAR)head, + (ULONG)strlen(head), + 0) != ERROR_SUCCESS) + VERIFY_PEM_ERROR(EINVAL); + + if (BCryptFinishHash( + hHash, + pbHashValue, + cbHashValue, + 0) != ERROR_SUCCESS) + VERIFY_PEM_ERROR(EINVAL); + + /* Verify hash against signature. */ + paddingInfo.pszAlgId = alg; + if (BCryptVerifySignature( + hKey, + (isECDSA ? NULL : &paddingInfo), + pbHashValue, + cbHashValue, + pbSignature, + cbSignature, + (isECDSA ? 0 : BCRYPT_PAD_PKCS1)) != ERROR_SUCCESS) + VERIFY_PEM_ERROR(EINVAL); + + /* Signature valid! */ + ret = 0; + +jwt_verify_sha_pem_done: + if (pbSignature) + jwt_freemem(pbSignature); + + if (pbHashObject) + jwt_freemem(pbHashObject); + + if (pbHashValue) + jwt_freemem(pbHashValue); + + if (hHash) + BCryptDestroyHash(hHash); + + if (hKey) + BCryptDestroyKey(hKey); + + if (hHashAlg) + BCryptCloseAlgorithmProvider(hHashAlg, 0); + + if (pSignerCert) + CertFreeCertificateContext(pSignerCert); + + if (hCertStore) + CertCloseStore(hCertStore, CERT_CLOSE_STORE_CHECK_FLAG); + + return ret; +} diff --git a/libjwt/jwt.c b/libjwt/jwt.c index bdebbb1a..03c5ba29 100644 --- a/libjwt/jwt.c +++ b/libjwt/jwt.c @@ -1,24 +1,1779 @@ -/* Copyright (C) 2015 Ben Collins +/* Copyright (C) 2015-2024 Ben Collins This file is part of the JWT C Library - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + SPDX-License-Identifier: MPL-2.0 + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. +#include +#include +#include +#include + +#include + +#ifdef HAVE_LIBB64 +#include +#include +#else +#include "cencode.h" +#include "cdecode.h" +#endif + +#include "jwt-private.h" + +/* Library init functionality */ +static struct jwt_crypto_ops *jwt_ops_available[] = { +#ifdef HAVE_OPENSSL + &jwt_openssl_ops, +#endif +#ifdef HAVE_GNUTLS + &jwt_gnutls_ops, +#endif + NULL, +}; + +#ifdef HAVE_OPENSSL +static struct jwt_crypto_ops *jwt_ops = &jwt_openssl_ops; +#else +static struct jwt_crypto_ops *jwt_ops = &jwt_gnutls_ops; +#endif + +const char *jwt_get_crypto_ops(void) +{ + if (jwt_ops == NULL) + return "(unknown)"; + + return jwt_ops->name; +} + +int jwt_set_crypto_ops(const char *opname) +{ + int i; + + /* The user asked for something, let's give it a try */ + for (i = 0; jwt_ops_available[i] != NULL; i++) { + if (strcmp(jwt_ops_available[i]->name, opname)) + continue; + + jwt_ops = jwt_ops_available[i]; + return 0; + } + + return EINVAL; +} + +__attribute__((constructor)) void jwt_init() +{ + const char *opname = getenv("JWT_CRYPTO"); + + /* By default, we choose the top spot */ + if (opname == NULL || opname[0] == '\0') { + jwt_ops = jwt_ops_available[0]; + return; + } + + /* Attempt to set ops */ + if (jwt_set_crypto_ops(opname)) { + jwt_ops = jwt_ops_available[0]; + fprintf(stderr, "LibJWT: No such crypto ops [%s], falling back to [%s]\n", + opname, jwt_ops->name); + } +} + +/* Number of elements in an array */ +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +static jwt_malloc_t pfn_malloc = NULL; +static jwt_realloc_t pfn_realloc = NULL; +static jwt_free_t pfn_free = NULL; + +void *jwt_malloc(size_t size) +{ + if (pfn_malloc) + return pfn_malloc(size); + + return malloc(size); +} + +static void *jwt_realloc(void *ptr, size_t size) +{ + if (pfn_realloc) + return pfn_realloc(ptr, size); + + return realloc(ptr, size); +} + +void jwt_freemem(void *ptr) +{ + if (pfn_free) + pfn_free(ptr); + else + free(ptr); +} + +static char *jwt_strdup(const char *str) +{ + size_t len; + char *result; + + len = strlen(str); + result = (char *)jwt_malloc(len + 1); + if (!result) + return NULL; + + memcpy(result, str, len); + result[len] = '\0'; + return result; +} + +/* A time-safe strcmp function */ +int jwt_strcmp(const char *str1, const char *str2) +{ + /* Get the LONGEST length */ + int len1 = strlen(str1); + int len2 = strlen(str2); + int len_max = len1 >= len2 ? len1 : len2; + + int i, ret = 0; + + /* Iterate the entire longest string no matter what. Only testing + * the shortest string would still allow attacks for + * "a" == "aKJSDHkjashaaHJASJ", adding a character each time one + * is found. */ + for (i = 0; i < len_max; i++) { + char c1, c2; + + c1 = (i < len1) ? str1[i] : 0; + c2 = (i < len2) ? str2[i] : 0; + + ret |= c1 ^ c2; + } + + /* Don't forget to check length */ + ret |= len1 ^ len2; + + return ret; +} + +static void *jwt_calloc(size_t nmemb, size_t size) +{ + size_t total_size; + void *ptr; + + total_size = nmemb * size; + if (!total_size) + return NULL; + + ptr = jwt_malloc(total_size); + if (ptr) + memset(ptr, 0, total_size); + + return ptr; +} + +const char *jwt_alg_str(jwt_alg_t alg) +{ + switch (alg) { + case JWT_ALG_NONE: + return "none"; + case JWT_ALG_HS256: + return "HS256"; + case JWT_ALG_HS384: + return "HS384"; + case JWT_ALG_HS512: + return "HS512"; + case JWT_ALG_RS256: + return "RS256"; + case JWT_ALG_RS384: + return "RS384"; + case JWT_ALG_RS512: + return "RS512"; + case JWT_ALG_ES256: + return "ES256"; + case JWT_ALG_ES256K: + return "ES256K"; + case JWT_ALG_ES384: + return "ES384"; + case JWT_ALG_ES512: + return "ES512"; + case JWT_ALG_PS256: + return "PS256"; + case JWT_ALG_PS384: + return "PS384"; + case JWT_ALG_PS512: + return "PS512"; + case JWT_ALG_EDDSA: + return "EDDSA"; + default: + return NULL; + } +} + +jwt_alg_t jwt_str_alg(const char *alg) +{ + if (alg == NULL) + return JWT_ALG_INVAL; + + if (!strcmp(alg, "none")) + return JWT_ALG_NONE; + else if (!strcmp(alg, "HS256")) + return JWT_ALG_HS256; + else if (!strcmp(alg, "HS384")) + return JWT_ALG_HS384; + else if (!strcmp(alg, "HS512")) + return JWT_ALG_HS512; + else if (!strcmp(alg, "RS256")) + return JWT_ALG_RS256; + else if (!strcmp(alg, "RS384")) + return JWT_ALG_RS384; + else if (!strcmp(alg, "RS512")) + return JWT_ALG_RS512; + else if (!strcmp(alg, "ES256")) + return JWT_ALG_ES256; + else if (!strcmp(alg, "ES256K")) + return JWT_ALG_ES256K; + else if (!strcmp(alg, "ES384")) + return JWT_ALG_ES384; + else if (!strcmp(alg, "ES512")) + return JWT_ALG_ES512; + else if (!strcmp(alg, "PS256")) + return JWT_ALG_PS256; + else if (!strcmp(alg, "PS384")) + return JWT_ALG_PS384; + else if (!strcmp(alg, "PS512")) + return JWT_ALG_PS512; + else if (!strcmp(alg, "EDDSA")) + return JWT_ALG_EDDSA; + + return JWT_ALG_INVAL; +} + +static void jwt_scrub_key(jwt_t *jwt) +{ + if (jwt->key) { + /* Overwrite it so it's gone from memory. */ + memset(jwt->key, 0, jwt->key_len); + + jwt_freemem(jwt->key); + jwt->key = NULL; + } + + jwt->key_len = 0; + jwt->alg = JWT_ALG_NONE; +} + +int jwt_set_alg(jwt_t *jwt, jwt_alg_t alg, const unsigned char *key, int len) +{ + /* No matter what happens here, we do this. */ + jwt_scrub_key(jwt); + + if (alg < JWT_ALG_NONE || alg >= JWT_ALG_INVAL) + return EINVAL; + + switch (alg) { + case JWT_ALG_NONE: + if (key || len) + return EINVAL; + break; + + default: + if (!key || len <= 0) + return EINVAL; + + jwt->key = jwt_malloc(len); + if (!jwt->key) + return ENOMEM; + + memcpy(jwt->key, key, len); + } + + jwt->alg = alg; + jwt->key_len = len; + + return 0; +} + +jwt_alg_t jwt_get_alg(const jwt_t *jwt) +{ + if (jwt == NULL) + return JWT_ALG_INVAL; + + return jwt->alg; +} + +int jwt_new(jwt_t **jwt) +{ + if (!jwt) + return EINVAL; + + *jwt = jwt_malloc(sizeof(jwt_t)); + if (!*jwt) + return ENOMEM; + + memset(*jwt, 0, sizeof(jwt_t)); + + (*jwt)->grants = json_object(); + if (!(*jwt)->grants) { + jwt_freemem(*jwt); + *jwt = NULL; + return ENOMEM; + } + + (*jwt)->headers = json_object(); + if (!(*jwt)->headers) { + json_decref((*jwt)->grants); + jwt_freemem(*jwt); + *jwt = NULL; + return ENOMEM; + } + + return 0; +} + +void jwt_free(jwt_t *jwt) +{ + if (!jwt) + return; + + jwt_scrub_key(jwt); + + json_decref(jwt->grants); + json_decref(jwt->headers); + + jwt_freemem(jwt); +} + +jwt_t *jwt_dup(jwt_t *jwt) +{ + jwt_t *new = NULL; + + if (!jwt) { + errno = EINVAL; + goto dup_fail; + } + + errno = 0; + + new = jwt_malloc(sizeof(jwt_t)); + if (!new) { + errno = ENOMEM; + return NULL; + } + + memset(new, 0, sizeof(jwt_t)); + + if (jwt->key_len) { + new->alg = jwt->alg; + new->key = jwt_malloc(jwt->key_len); + if (!new->key) { + errno = ENOMEM; + goto dup_fail; + } + memcpy(new->key, jwt->key, jwt->key_len); + new->key_len = jwt->key_len; + } + + new->grants = json_deep_copy(jwt->grants); + if (!new->grants) + errno = ENOMEM; + + new->headers = json_deep_copy(jwt->headers); + if (!new->headers) + errno = ENOMEM; + +dup_fail: + if (errno) { + jwt_free(new); + new = NULL; + } + + return new; +} + +static const char *get_js_string(json_t *js, const char *key) +{ + const char *val = NULL; + json_t *js_val; + + js_val = json_object_get(js, key); + if (js_val) { + if (json_typeof(js_val) == JSON_STRING) + val = json_string_value(js_val); + else + errno = EINVAL; + } else { + errno = ENOENT; + } + + return val; +} + +static long get_js_int(json_t *js, const char *key) +{ + long val = -1; + json_t *js_val; + + js_val = json_object_get(js, key); + if (js_val) { + if (json_typeof(js_val) == JSON_INTEGER) + val = (long)json_integer_value(js_val); + else + errno = EINVAL; + } else { + errno = ENOENT; + } + + return val; +} + +static int get_js_bool(json_t *js, const char *key) +{ + int val = -1; + json_t *js_val; + + js_val = json_object_get(js, key); + if (js_val) { + switch (json_typeof(js_val)) { + case JSON_TRUE: + val = 1; + break; + case JSON_FALSE: + val = 0; + break; + default: + errno = EINVAL; + } + } else { + errno = ENOENT; + } + return val; +} + +void *jwt_base64uri_decode(const char *src, int *ret_len) +{ + base64_decodestate state; + void *buf; + char *new; + int len, i, z; + + /* Decode based on RFC-4648 URI safe encoding. */ + len = (int)strlen(src); + /* When reversing the URI cleanse, we can possibly add up + * to 3 '=' characters to replace the missing padding. */ + new = alloca(len + 4); + if (!new) + return NULL; + + for (i = 0; i < len; i++) { + switch (src[i]) { + case '-': + new[i] = '+'; + break; + case '_': + new[i] = '/'; + break; + default: + new[i] = src[i]; + } + } + z = 4 - (i % 4); + if (z < 4) { + while (z--) + new[i++] = '='; + } + new[i] = '\0'; + + /* Now we have a standard base64 encoded string. */ + buf = jwt_malloc(base64_decode_maxlength(len) + 1); + if (buf == NULL) + return NULL; + + base64_init_decodestate(&state); + *ret_len = base64_decode_block(new, strlen(new), buf, &state); + + if (*ret_len == 0) { + jwt_freemem(buf); + buf = NULL; + } + + return buf; +} + + +static json_t *jwt_base64uri_decode_to_json(char *src) +{ + json_t *js; + char *buf; + int len; + + buf = jwt_base64uri_decode(src, &len); + + if (buf == NULL) + return NULL; + + buf[len] = '\0'; + + js = json_loads(buf, 0, NULL); + + jwt_freemem(buf); + + return js; +} + +int jwt_base64uri_encode(char **_dst, const char *plain, int plain_len) +{ + base64_encodestate state; + int len, i; + char *dst; + + base64_init_encodestate(&state); + /* Ensure no newlines are emitted into the string */ + state.chars_per_line = 0; + + len = base64_encode_length(plain_len, &state); + *_dst = jwt_malloc(len + 1); + if (*_dst == NULL) + return -ENOMEM; + dst = *_dst; + + /* First, a normal base64 encoding */ + len = base64_encode_block(plain, plain_len, dst, &state); + len += base64_encode_blockend(dst + len, &state); + dst[len] = 0; + + /* Now for the URI encoding */ + for (i = 0; i < len; i++) { + switch (dst[i]) { + case '+': + dst[i] = '-'; + break; + case '/': + dst[i] = '_'; + break; + case '=': + dst[i] = '\0'; + break; + } + } + + dst[i] = '\0'; + + return i; +} + +static int jwt_sign(jwt_t *jwt, char **out, unsigned int *len, const char *str, unsigned int str_len) +{ + switch (jwt->alg) { + /* HMAC */ + case JWT_ALG_HS256: + case JWT_ALG_HS384: + case JWT_ALG_HS512: + return jwt_ops->sign_sha_hmac(jwt, out, len, str, str_len); + + /* RSA */ + case JWT_ALG_RS256: + case JWT_ALG_RS384: + case JWT_ALG_RS512: + + /* RSA-PSS */ + case JWT_ALG_PS256: + case JWT_ALG_PS384: + case JWT_ALG_PS512: + + /* ECC */ + case JWT_ALG_ES256: + case JWT_ALG_ES256K: + case JWT_ALG_ES384: + case JWT_ALG_ES512: + + /* EdDSA */ + case JWT_ALG_EDDSA: + return jwt_ops->sign_sha_pem(jwt, out, len, str, str_len); + + /* You wut, mate? */ + default: + return EINVAL; + } +} + +static int jwt_verify(jwt_t *jwt, const char *head, unsigned int head_len, const char *sig) +{ + switch (jwt->alg) { + /* HMAC */ + case JWT_ALG_HS256: + case JWT_ALG_HS384: + case JWT_ALG_HS512: + return jwt_ops->verify_sha_hmac(jwt, head, head_len, sig); + + /* RSA */ + case JWT_ALG_RS256: + case JWT_ALG_RS384: + case JWT_ALG_RS512: + + /* RSA-PSS */ + case JWT_ALG_PS256: + case JWT_ALG_PS384: + case JWT_ALG_PS512: + + /* ECC */ + case JWT_ALG_ES256: + case JWT_ALG_ES256K: + case JWT_ALG_ES384: + case JWT_ALG_ES512: + + /* EdDSA */ + case JWT_ALG_EDDSA: + return jwt_ops->verify_sha_pem(jwt, head, head_len, sig); + + /* You wut, mate? */ + default: + return EINVAL; + } +} + +static int jwt_parse_body(jwt_t *jwt, char *body) +{ + if (jwt->grants) { + json_decref(jwt->grants); + jwt->grants = NULL; + } + + jwt->grants = jwt_base64uri_decode_to_json(body); + if (!jwt->grants) + return EINVAL; + + return 0; +} + +static int jwt_parse_head(jwt_t *jwt, char *head) +{ + const char *alg; + + if (jwt->headers) { + json_decref(jwt->headers); + jwt->headers = NULL; + } + + jwt->headers = jwt_base64uri_decode_to_json(head); + if (!jwt->headers) + return EINVAL; + + alg = get_js_string(jwt->headers, "alg"); + jwt->alg = jwt_str_alg(alg); + if (jwt->alg == JWT_ALG_INVAL) + return EINVAL; + + return 0; +} + +/** + * @brief Smoke test to save the user from themselves. + */ +static int jwt_verify_alg(jwt_t *jwt) +{ + int ret = 0; + + if (jwt->alg == JWT_ALG_NONE) { + /* If the user gave us a key but the JWT has alg = none, + * then we shouldn't even proceed. */ + if (jwt->key || jwt->key_len) + ret = EINVAL; + } else if (!(jwt->key && (jwt->key_len > 0))) { + /* If alg != none, then we should have a key to use */ + ret = EINVAL; + } + + /* Releive ourselves of the burden of this secret. */ + if (ret) + jwt_scrub_key(jwt); + + return ret; +} + +static int jwt_parse(jwt_t **jwt, const char *token, unsigned int *len) +{ + char *head = NULL; + jwt_t *new = NULL; + char *body, *sig; + int ret = EINVAL; + + if (!jwt) + return EINVAL; + + *jwt = NULL; + head = jwt_strdup(token); + + if (!head) + return ENOMEM; + + /* Find the components. */ + for (body = head; body[0] != '.'; body++) { + if (body[0] == '\0') + goto parse_done; + } + + body[0] = '\0'; + body++; + + for (sig = body; sig[0] != '.'; sig++) { + if (sig[0] == '\0') + goto parse_done; + } + + sig[0] = '\0'; + + /* Now that we have everything split up, let's check out the + * header. */ + ret = jwt_new(&new); + if (ret) + goto parse_done; + + if ((ret = jwt_parse_head(new, head))) + goto parse_done; + + ret = jwt_parse_body(new, body); +parse_done: + if (ret) { + jwt_free(new); + *jwt = NULL; + } else { + *jwt = new; + *len = sig - head; + } + + jwt_freemem(head); + + return ret; +} + +static int jwt_copy_key(jwt_t *jwt, const unsigned char *key, int key_len) +{ + int ret = 0; + + if (!key_len) + return 0; + + /* Always allocate one extra byte. For PEM, it ensures against + * not having a nil at the end (although all crypto backends + * should honor length), and for binary keys, it wont hurt + * because we use key_len for those operations. */ + jwt->key = jwt_malloc(key_len + 1); + if (jwt->key == NULL) + return ENOMEM; + + jwt->key[key_len] = '\0'; + + memcpy(jwt->key, key, key_len); + jwt->key_len = key_len; + + return ret; +} + +static int jwt_decode_complete(jwt_t **jwt, const unsigned char *key, int key_len, + const char *token, unsigned int payload_len) +{ + int ret = EINVAL; + jwt_t *new = *jwt; + + /* Copy the key over for verify_alg. */ + ret = jwt_copy_key(new, key, key_len); + if (ret) + goto decode_done; + + ret = jwt_verify_alg(new); + if (ret) + goto decode_done; + + /* Check the signature, if needed. */ + if (new->alg != JWT_ALG_NONE) { + const char *sig = token + (payload_len + 1); + ret = jwt_verify(new, token, payload_len, sig); + } + +decode_done: + if (ret) { + jwt_free(new); + *jwt = NULL; + } + + return ret; +} + +int jwt_decode(jwt_t **jwt, const char *token, const unsigned char *key, + int key_len) +{ + int ret; + unsigned int payload_len; + + if (jwt == NULL) + return EINVAL; + + if ((ret = jwt_parse(jwt, token, &payload_len))) + return ret; + + return jwt_decode_complete(jwt, key, key_len, token, payload_len); +} + +int jwt_decode_2(jwt_t **jwt, const char *token, jwt_key_p_t key_provider) +{ + int ret; + unsigned int payload_len; + jwt_key_t key = { NULL, 0 }; + jwt_t *new = NULL; + + if (jwt == NULL) + return EINVAL; + *jwt = NULL; + + ret = jwt_parse(&new, token, &payload_len); + if (ret) + return ret; + + if (key_provider) { + /* The previous code trusted the JWT alg too much. If it was + * NONE, then it wouldn't even bother calling the cb. + * + * We also had some test cases that called this func with no + * key_provider and exptected it to work. True, this code + * allowed for that. My gut tells me that should never have + * been the case. + * + * For one, the previous code didn't check for NULL, so if + * you got a key that wasn't alg == none, instant SEGV. + * + * However, since this func is getting deprecated, we'll + * just let that case be like calling jwt_decode() + */ + ret = key_provider(new, &key); + } + + if (ret) { + jwt_free(new); + return ret; + } + + *jwt = new; + + return jwt_decode_complete(jwt, key.jwt_key, key.jwt_key_len, token, payload_len); +} + +const char *jwt_get_grant(jwt_t *jwt, const char *grant) +{ + if (!jwt || !grant || !strlen(grant)) { + errno = EINVAL; + return NULL; + } + + errno = 0; + + return get_js_string(jwt->grants, grant); +} + +long jwt_get_grant_int(jwt_t *jwt, const char *grant) +{ + if (!jwt || !grant || !strlen(grant)) { + errno = EINVAL; + return 0; + } + + errno = 0; + + return get_js_int(jwt->grants, grant); +} + +int jwt_get_grant_bool(jwt_t *jwt, const char *grant) +{ + if (!jwt || !grant || !strlen(grant)) { + errno = EINVAL; + return 0; + } + + errno = 0; + + return get_js_bool(jwt->grants, grant); +} + +char *jwt_get_grants_json(jwt_t *jwt, const char *grant) +{ + json_t *js_val = NULL; + + if (!jwt) { + errno = EINVAL; + return NULL; + } + + if (grant && strlen(grant)) + js_val = json_object_get(jwt->grants, grant); + else + js_val = jwt->grants; + + if (js_val == NULL) { + errno = ENOENT; + return NULL; + } + + errno = 0; + + return json_dumps(js_val, JSON_SORT_KEYS | JSON_COMPACT | JSON_ENCODE_ANY); +} + +int jwt_add_grant(jwt_t *jwt, const char *grant, const char *val) +{ + if (!jwt || !grant || !strlen(grant) || !val) + return EINVAL; + + if (get_js_string(jwt->grants, grant) != NULL) + return EEXIST; + + if (json_object_set_new(jwt->grants, grant, json_string(val))) + return EINVAL; + + return 0; +} + +int jwt_add_grant_int(jwt_t *jwt, const char *grant, long val) +{ + if (!jwt || !grant || !strlen(grant)) + return EINVAL; + + if (get_js_int(jwt->grants, grant) != -1) + return EEXIST; + + if (json_object_set_new(jwt->grants, grant, json_integer((json_int_t)val))) + return EINVAL; + + return 0; +} + +int jwt_add_grant_bool(jwt_t *jwt, const char *grant, int val) +{ + if (!jwt || !grant || !strlen(grant)) + return EINVAL; + + if (get_js_int(jwt->grants, grant) != -1) + return EEXIST; + + if (json_object_set_new(jwt->grants, grant, json_boolean(val))) + return EINVAL; + + return 0; +} + +int jwt_add_grants_json(jwt_t *jwt, const char *json) +{ + json_t *js_val; + int ret = -1; + + if (!jwt) + return EINVAL; + + js_val = json_loads(json, JSON_REJECT_DUPLICATES, NULL); + + if (json_is_object(js_val)) + ret = json_object_update(jwt->grants, js_val); + + json_decref(js_val); + + return ret ? EINVAL : 0; +} + +int jwt_del_grants(jwt_t *jwt, const char *grant) +{ + if (!jwt) + return EINVAL; + + if (grant == NULL || !strlen(grant)) + json_object_clear(jwt->grants); + else + json_object_del(jwt->grants, grant); + + return 0; +} + +const char *jwt_get_header(jwt_t *jwt, const char *header) +{ + if (!jwt || !header || !strlen(header)) { + errno = EINVAL; + return NULL; + } + + errno = 0; + + return get_js_string(jwt->headers, header); +} + +long jwt_get_header_int(jwt_t *jwt, const char *header) +{ + if (!jwt || !header || !strlen(header)) { + errno = EINVAL; + return 0; + } + + errno = 0; + + return get_js_int(jwt->headers, header); +} + +int jwt_get_header_bool(jwt_t *jwt, const char *header) +{ + if (!jwt || !header || !strlen(header)) { + errno = EINVAL; + return 0; + } + + errno = 0; + + return get_js_bool(jwt->headers, header); +} + +char *jwt_get_headers_json(jwt_t *jwt, const char *header) +{ + json_t *js_val = NULL; + + errno = EINVAL; + + if (!jwt) + return NULL; + + if (header && strlen(header)) + js_val = json_object_get(jwt->headers, header); + else + js_val = jwt->headers; + + if (js_val == NULL) + return NULL; + + errno = 0; + + return json_dumps(js_val, JSON_SORT_KEYS | JSON_COMPACT | JSON_ENCODE_ANY); +} - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, see - . */ +int jwt_add_header(jwt_t *jwt, const char *header, const char *val) +{ + if (!jwt || !header || !strlen(header) || !val) + return EINVAL; -#include -#include + if (get_js_string(jwt->headers, header) != NULL) + return EEXIST; + + if (json_object_set_new(jwt->headers, header, json_string(val))) + return EINVAL; + + return 0; +} + +int jwt_add_header_int(jwt_t *jwt, const char *header, long val) +{ + if (!jwt || !header || !strlen(header)) + return EINVAL; + + if (get_js_int(jwt->headers, header) != -1) + return EEXIST; + + if (json_object_set_new(jwt->headers, header, json_integer((json_int_t)val))) + return EINVAL; + + return 0; +} + +int jwt_add_header_bool(jwt_t *jwt, const char *header, int val) +{ + if (!jwt || !header || !strlen(header)) + return EINVAL; + + if (get_js_int(jwt->headers, header) != -1) + return EEXIST; + + if (json_object_set_new(jwt->headers, header, json_boolean(val))) + return EINVAL; + + return 0; +} + +int jwt_add_headers_json(jwt_t *jwt, const char *json) +{ + json_t *js_val; + int ret = -1; + + if (!jwt) + return EINVAL; + + js_val = json_loads(json, JSON_REJECT_DUPLICATES, NULL); + + if (json_is_object(js_val)) + ret = json_object_update(jwt->headers, js_val); + + json_decref(js_val); + + return ret ? EINVAL : 0; +} + +int jwt_del_headers(jwt_t *jwt, const char *header) +{ + if (!jwt) + return EINVAL; + + if (header == NULL || !strlen(header)) + json_object_clear(jwt->headers); + else + json_object_del(jwt->headers, header); + + return 0; +} + +static int __append_str(char **buf, const char *str) +{ + char *new; + + if (*buf == NULL) + new = jwt_calloc(1, strlen(str) + 1); + else + new = jwt_realloc(*buf, strlen(*buf) + strlen(str) + 1); + + if (new == NULL) + return ENOMEM; + + strcat(new, str); + + *buf = new; + + return 0; +} + +#define APPEND_STR(__buf, __str) do { \ + int ret = __append_str(__buf, __str); \ + if (ret) \ + return ret; \ +} while (0) + +static int write_js(const json_t *js, char **buf, int pretty) +{ + /* Sort keys for repeatability */ + size_t flags = JSON_SORT_KEYS; + char *serial; + + if (pretty) { + APPEND_STR(buf, "\n"); + flags |= JSON_INDENT(4); + } else { + flags |= JSON_COMPACT; + } + + serial = json_dumps(js, flags); + + APPEND_STR(buf, serial); + + jwt_freemem(serial); + + if (pretty) + APPEND_STR(buf, "\n"); + + return 0; +} + +static int jwt_write_head(jwt_t *jwt, char **buf, int pretty) +{ + int ret = 0; + + if (jwt->alg != JWT_ALG_NONE) { + + /* Only add default 'typ' header if it has not been defined, + * allowing for any value of it. This allows for signaling + * of application specific extensions to JWT, such as PASSporT, + * RFC 8225. */ + if ((ret = jwt_add_header(jwt, "typ", "JWT"))) { + if (ret != EEXIST) + return ret; + } + } + + if ((ret = jwt_del_headers(jwt, "alg"))) + return ret; + + if ((ret = jwt_add_header(jwt, "alg", jwt_alg_str(jwt->alg)))) + return ret; + + return write_js(jwt->headers, buf, pretty); +} + +static int jwt_write_body(jwt_t *jwt, char **buf, int pretty) +{ + return write_js(jwt->grants, buf, pretty); +} + +static int jwt_dump(jwt_t *jwt, char **buf, int pretty) +{ + int ret; + + ret = jwt_write_head(jwt, buf, pretty); + + if (ret == 0) + ret = __append_str(buf, "."); + + if (ret == 0) + ret = jwt_write_body(jwt, buf, pretty); + + return ret; +} + +char *jwt_dump_grants_str(jwt_t *jwt, int pretty) +{ + char *out = NULL; + int err; + + errno = 0; + + err = jwt_write_body(jwt, &out, pretty); + + if (err) { + errno = err; + if (out) + jwt_freemem(out); + out = NULL; + } + + return out; +} + +int jwt_dump_fp(jwt_t *jwt, FILE *fp, int pretty) +{ + char *out = NULL; + int ret = 0; + + ret = jwt_dump(jwt, &out, pretty); + + if (ret == 0) + fputs(out, fp); + + if (out) + jwt_freemem(out); + + return ret; +} + +char *jwt_dump_str(jwt_t *jwt, int pretty) +{ + char *out = NULL; + int err; + + err = jwt_dump(jwt, &out, pretty); + + if (err) { + errno = err; + if (out) + jwt_freemem(out); + out = NULL; + } else { + errno = 0; + } + + return out; +} + +static int jwt_encode(jwt_t *jwt, char **out) +{ + char *buf = NULL, *head = NULL, *body = NULL, *sig; + int ret, head_len, body_len; + unsigned int sig_len; + + /* First the header. */ + ret = jwt_write_head(jwt, &buf, 0); + if (ret) { + if (buf) + jwt_freemem(buf); + return ret; + } + + head = alloca(strlen(buf) * 2); + if (head == NULL) { + jwt_freemem(buf); + return ENOMEM; + } + head_len = jwt_base64uri_encode(&head, buf, (int)strlen(buf)); + jwt_freemem(buf); + buf = NULL; + + if (head_len <= 0) + return -head_len; + + /* Now the body. */ + ret = jwt_write_body(jwt, &buf, 0); + if (ret) { + if (buf) + jwt_freemem(buf); + return ret; + } + + body_len = jwt_base64uri_encode(&body, buf, (int)strlen(buf)); + jwt_freemem(buf); + buf = NULL; + + if (body_len <= 0) { + jwt_freemem(head); + return -body_len; + } + + /* Allocate enough to reuse as b64 buffer. */ + buf = jwt_malloc(head_len + body_len + 2); + if (buf == NULL) { + jwt_freemem(head); + jwt_freemem(body); + return ENOMEM; + } + + strcpy(buf, head); + strcat(buf, "."); + strcat(buf, body); + + jwt_freemem(head); + jwt_freemem(body); + + ret = __append_str(out, buf); + if (ret == 0) + ret = __append_str(out, "."); + if (ret) { + if (buf) + jwt_freemem(buf); + return ret; + } + + if (jwt->alg == JWT_ALG_NONE) { + jwt_freemem(buf); + return 0; + } + + /* Now the signature. */ + ret = jwt_sign(jwt, &sig, &sig_len, buf, strlen(buf)); + jwt_freemem(buf); + buf = NULL; + + if (ret) + return ret; + + ret = jwt_base64uri_encode(&buf, sig, sig_len); + jwt_freemem(sig); + if (ret <= 0) + return -ret; + + ret = __append_str(out, buf); + jwt_freemem(buf); + + return ret; +} + +int jwt_encode_fp(jwt_t *jwt, FILE *fp) +{ + char *str = NULL; + int ret; + + ret = jwt_encode(jwt, &str); + if (ret) { + if (str) + jwt_freemem(str); + return ret; + } + + fputs(str, fp); + jwt_freemem(str); + + return 0; +} + +char *jwt_encode_str(jwt_t *jwt) +{ + char *str = NULL; + + errno = jwt_encode(jwt, &str); + if (errno) { + if (str) + jwt_freemem(str); + str = NULL; + } + + return str; +} + +void jwt_free_str(char *str) +{ + if (str) + jwt_freemem(str); +} + +int jwt_set_alloc(jwt_malloc_t pmalloc, jwt_realloc_t prealloc, jwt_free_t pfree) +{ + /* Set allocator functions for LibJWT. */ + pfn_malloc = pmalloc; + pfn_realloc = prealloc; + pfn_free = pfree; + + /* Set same allocator functions for Jansson. */ + json_set_alloc_funcs(jwt_malloc, jwt_freemem); + + return 0; +} + +void jwt_get_alloc(jwt_malloc_t *pmalloc, jwt_realloc_t *prealloc, jwt_free_t *pfree) +{ + if (pmalloc) + *pmalloc = pfn_malloc; + + if (prealloc) + *prealloc = pfn_realloc; -void jwt(void) + if (pfree) + *pfree = pfn_free; +} + +int jwt_valid_new(jwt_valid_t **jwt_valid, jwt_alg_t alg) +{ + if (!jwt_valid) + return EINVAL; + + *jwt_valid = jwt_malloc(sizeof(jwt_valid_t)); + if (!*jwt_valid) + return ENOMEM; + + memset(*jwt_valid, 0, sizeof(jwt_valid_t)); + (*jwt_valid)->alg = alg; + + (*jwt_valid)->status = JWT_VALIDATION_ERROR; + + (*jwt_valid)->nbf_leeway = 0; + (*jwt_valid)->exp_leeway = 0; + + (*jwt_valid)->req_grants = json_object(); + if (!(*jwt_valid)->req_grants) { + jwt_freemem(*jwt_valid); + *jwt_valid = NULL; + return ENOMEM; + } + + return 0; +} + +void jwt_valid_free(jwt_valid_t *jwt_valid) +{ + if (!jwt_valid) + return; + + json_decref(jwt_valid->req_grants); + + jwt_freemem(jwt_valid); +} + +unsigned int jwt_valid_get_status(jwt_valid_t *jwt_valid) +{ + if (!jwt_valid) + return JWT_VALIDATION_ERROR; + + return jwt_valid->status; +} + +time_t jwt_valid_get_nbf_leeway(jwt_valid_t *jwt_valid) +{ + if (!jwt_valid) + return EINVAL; + + return jwt_valid->nbf_leeway; +} + +time_t jwt_valid_get_exp_leeway(jwt_valid_t *jwt_valid) +{ + if (!jwt_valid) + return EINVAL; + + return jwt_valid->exp_leeway; +} + +int jwt_valid_add_grant(jwt_valid_t *jwt_valid, const char *grant, const char *val) +{ + if (!jwt_valid || !grant || !strlen(grant) || !val) + return EINVAL; + + if (get_js_string(jwt_valid->req_grants, grant) != NULL) + return EEXIST; + + if (json_object_set_new(jwt_valid->req_grants, grant, json_string(val))) + return EINVAL; + + return 0; +} + +int jwt_valid_add_grant_int(jwt_valid_t *jwt_valid, const char *grant, long val) +{ + if (!jwt_valid || !grant || !strlen(grant)) + return EINVAL; + + if (get_js_int(jwt_valid->req_grants, grant) != -1) + return EEXIST; + + if (json_object_set_new(jwt_valid->req_grants, grant, json_integer((json_int_t)val))) + return EINVAL; + + return 0; +} + +int jwt_valid_add_grant_bool(jwt_valid_t *jwt_valid, const char *grant, int val) +{ + if (!jwt_valid || !grant || !strlen(grant)) + return EINVAL; + + if (get_js_bool(jwt_valid->req_grants, grant) != -1) + return EEXIST; + + if (json_object_set_new(jwt_valid->req_grants, grant, json_boolean(val))) + return EINVAL; + + return 0; +} + +int jwt_valid_add_grants_json(jwt_valid_t *jwt_valid, const char *json) +{ + json_t *js_val; + int ret = -1; + + if (!jwt_valid) + return EINVAL; + + js_val = json_loads(json, JSON_REJECT_DUPLICATES, NULL); + + if (json_is_object(js_val)) + ret = json_object_update(jwt_valid->req_grants, js_val); + + json_decref(js_val); + + return ret ? EINVAL : 0; +} + +char *jwt_valid_get_grants_json(jwt_valid_t *jwt_valid, const char *grant) +{ + json_t *js_val = NULL; + + errno = EINVAL; + + if (!jwt_valid) + return NULL; + + if (grant && strlen(grant)) + js_val = json_object_get(jwt_valid->req_grants, grant); + else + js_val = jwt_valid->req_grants; + + if (js_val == NULL) + return NULL; + + errno = 0; + + return json_dumps(js_val, JSON_SORT_KEYS | JSON_COMPACT | JSON_ENCODE_ANY); +} + +const char *jwt_valid_get_grant(jwt_valid_t *jwt_valid, const char *grant) +{ + if (!jwt_valid || !grant || !strlen(grant)) { + errno = EINVAL; + return NULL; + } + + errno = 0; + + return get_js_string(jwt_valid->req_grants, grant); +} + +long jwt_valid_get_grant_int(jwt_valid_t *jwt_valid, const char *grant) +{ + if (!jwt_valid || !grant || !strlen(grant)) { + errno = EINVAL; + return 0; + } + + errno = 0; + + return get_js_int(jwt_valid->req_grants, grant); +} + +int jwt_valid_get_grant_bool(jwt_valid_t *jwt_valid, const char *grant) +{ + if (!jwt_valid || !grant || !strlen(grant)) { + errno = EINVAL; + return 0; + } + + errno = 0; + + return get_js_bool(jwt_valid->req_grants, grant); +} + +int jwt_valid_set_now(jwt_valid_t *jwt_valid, const time_t now) +{ + if (!jwt_valid) + return EINVAL; + + jwt_valid->now = now; + + return 0; +} + +int jwt_valid_set_nbf_leeway(jwt_valid_t *jwt_valid, const time_t nbf_leeway) +{ + if (!jwt_valid) + return EINVAL; + + jwt_valid->nbf_leeway = nbf_leeway; + + return 0; +} + +int jwt_valid_set_exp_leeway(jwt_valid_t *jwt_valid, const time_t exp_leeway) +{ + if (!jwt_valid) + return EINVAL; + + jwt_valid->exp_leeway = exp_leeway; + + return 0; +} + +int jwt_valid_set_headers(jwt_valid_t *jwt_valid, int hdr) +{ + if (!jwt_valid) + return EINVAL; + + jwt_valid->hdr = hdr; + + return 0; +} + +int jwt_valid_del_grants(jwt_valid_t *jwt_valid, const char *grant) +{ + if (!jwt_valid) + return EINVAL; + + if (grant == NULL || !strlen(grant)) + json_object_clear(jwt_valid->req_grants); + else + json_object_del(jwt_valid->req_grants, grant); + + return 0; +} + +#define _SET_AND_RET(__v, __e) do { \ + __v->status |= __e; \ + return __v->status; \ +} while (0) + +unsigned int jwt_validate(jwt_t *jwt, jwt_valid_t *jwt_valid) +{ + const char *jwt_hdr_str, *jwt_body_str, *req_grant; + json_t *js_val_1, *js_val_2; + time_t t; + + if (!jwt_valid) + return JWT_VALIDATION_ERROR; + + if (!jwt) { + jwt_valid->status = JWT_VALIDATION_ERROR; + return jwt_valid->status; + } + + jwt_valid->status = JWT_VALIDATION_SUCCESS; + + /* Validate algorithm */ + if (jwt_valid->alg != jwt_get_alg(jwt)) + jwt_valid->status |= JWT_VALIDATION_ALG_MISMATCH; + + /* Validate expires */ + t = get_js_int(jwt->grants, "exp"); + if (jwt_valid->now && t != -1 && jwt_valid->now - jwt_valid->exp_leeway >= t) + jwt_valid->status |= JWT_VALIDATION_EXPIRED; + + /* Validate not-before */ + t = get_js_int(jwt->grants, "nbf"); + if (jwt_valid->now && t != -1 && jwt_valid->now + jwt_valid->nbf_leeway < t) + jwt_valid->status |= JWT_VALIDATION_TOO_NEW; + + /* Validate replicated issuer */ + jwt_hdr_str = get_js_string(jwt->headers, "iss"); + jwt_body_str = get_js_string(jwt->grants, "iss"); + if (jwt_hdr_str && jwt_body_str && strcmp(jwt_hdr_str, jwt_body_str)) + jwt_valid->status |= JWT_VALIDATION_ISS_MISMATCH; + + /* Validate replicated subject */ + jwt_hdr_str = get_js_string(jwt->headers, "sub"); + jwt_body_str = get_js_string(jwt->grants, "sub"); + if (jwt_hdr_str && jwt_body_str && strcmp(jwt_hdr_str, jwt_body_str)) + jwt_valid->status |= JWT_VALIDATION_SUB_MISMATCH; + + /* Validate replicated audience (might be array or string) */ + js_val_1 = json_object_get(jwt->headers, "aud"); + js_val_2 = json_object_get(jwt->grants, "aud"); + if (js_val_1 && js_val_2 && !json_equal(js_val_1, js_val_2)) + jwt_valid->status |= JWT_VALIDATION_AUD_MISMATCH; + + /* Validate required grants */ + json_object_foreach(jwt_valid->req_grants, req_grant, js_val_1) { + json_t *act_js_val = json_object_get(jwt->grants, req_grant); + + if (act_js_val && json_equal(js_val_1, act_js_val)) + continue; + + if (act_js_val) + jwt_valid->status |= JWT_VALIDATION_GRANT_MISMATCH; + else + jwt_valid->status |= JWT_VALIDATION_GRANT_MISSING; + } + + return jwt_valid->status; +} + +typedef struct { + int error; + char *str; +} jwt_exception_dict_t; + +static jwt_exception_dict_t jwt_exceptions[] = { + /* { JWT_VALIDATION_SUCCESS, "SUCCESS" }, */ + { JWT_VALIDATION_ERROR, "general failures" }, + { JWT_VALIDATION_ALG_MISMATCH, "algorithm mismatch" }, + { JWT_VALIDATION_EXPIRED, "token expired" }, + { JWT_VALIDATION_TOO_NEW, "token future dated" }, + { JWT_VALIDATION_ISS_MISMATCH, "issuer mismatch" }, + { JWT_VALIDATION_SUB_MISMATCH, "subject mismatch" }, + { JWT_VALIDATION_AUD_MISMATCH, "audience mismatch" }, + { JWT_VALIDATION_GRANT_MISSING, "grant missing" }, + { JWT_VALIDATION_GRANT_MISMATCH, "grant mismatch" }, +}; + +char *jwt_exception_str(unsigned int exceptions) { - fprintf(stderr, "Hello World!\n"); + int rc, i; + char *str = NULL; + + if (exceptions == JWT_VALIDATION_SUCCESS) { + if ((rc = __append_str(&str, "success"))) + goto fail; + return str; + } + + for (i = 0; i < ARRAY_SIZE(jwt_exceptions); i++) { + if (!(jwt_exceptions[i].error & exceptions)) + continue; + + if (str && (rc = __append_str(&str, ", "))) + goto fail; + + if ((rc = __append_str(&str, jwt_exceptions[i].str))) + goto fail; + } + + /* check if none of the exceptions matched? */ + if (!str && (rc = __append_str(&str, "unknown exceptions"))) + goto fail; + + return str; +fail: + errno = rc; + jwt_freemem(str); + return NULL; } diff --git a/libjwt/libjwt.pc.in b/libjwt/libjwt.pc.in new file mode 100644 index 00000000..0c81d365 --- /dev/null +++ b/libjwt/libjwt.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: @PACKAGE_NAME@ +Description: JWT C Library +Version: @PACKAGE_VERSION@ + +Cflags: -I${includedir} +Libs: -L${libdir} -ljwt +Libs.private: @JANSSON_LIBS@ @OPENSSL_LIBS@ diff --git a/m4/.keep b/m4/.keep deleted file mode 100644 index e69de29b..00000000 diff --git a/m4/ax_code_coverage.m4 b/m4/ax_code_coverage.m4 new file mode 100644 index 00000000..6da9699f --- /dev/null +++ b/m4/ax_code_coverage.m4 @@ -0,0 +1,249 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_code_coverage.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CODE_COVERAGE() +# +# DESCRIPTION +# +# Defines CODE_COVERAGE_CPPFLAGS, CODE_COVERAGE_CFLAGS, +# CODE_COVERAGE_CXXFLAGS and CODE_COVERAGE_LDFLAGS which should be +# included in the CPPFLAGS, CFLAGS CXXFLAGS and LIBS/LDFLAGS variables of +# every build target (program or library) which should be built with code +# coverage support. Also defines CODE_COVERAGE_RULES which should be +# substituted in your Makefile; and $enable_code_coverage which can be +# used in subsequent configure output. CODE_COVERAGE_ENABLED is defined +# and substituted, and corresponds to the value of the +# --enable-code-coverage option, which defaults to being disabled. +# +# Test also for gcov program and create GCOV variable that could be +# substituted. +# +# Note that all optimisation flags in CFLAGS must be disabled when code +# coverage is enabled. +# +# Usage example: +# +# configure.ac: +# +# AX_CODE_COVERAGE +# +# Makefile.am: +# +# @CODE_COVERAGE_RULES@ +# my_program_LIBS = ... $(CODE_COVERAGE_LDFLAGS) ... +# my_program_CPPFLAGS = ... $(CODE_COVERAGE_CPPFLAGS) ... +# my_program_CFLAGS = ... $(CODE_COVERAGE_CFLAGS) ... +# my_program_CXXFLAGS = ... $(CODE_COVERAGE_CXXFLAGS) ... +# +# This results in a "check-code-coverage" rule being added to any +# Makefile.am which includes "@CODE_COVERAGE_RULES@" (assuming the module +# has been configured with --enable-code-coverage). Running `make +# check-code-coverage` in that directory will run the module's test suite +# (`make check`) and build a code coverage report detailing the code which +# was touched, then print the URI for the report. +# +# This code was derived from Makefile.decl in GLib, originally licenced +# under LGPLv2.1+. +# +# LICENSE +# +# Copyright (c) 2012, 2016 Philip Withnall +# Copyright (c) 2012 Xan Lopez +# Copyright (c) 2012 Christian Persch +# Copyright (c) 2012 Paolo Borelli +# Copyright (c) 2012 Dan Winship +# Copyright (c) 2015 Bastien ROUCARIES +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or (at +# your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +#serial 16 + +AC_DEFUN([AX_CODE_COVERAGE],[ + dnl Check for --enable-code-coverage + AC_REQUIRE([AC_PROG_SED]) + + # allow to override gcov location + AC_ARG_WITH([gcov], + [AS_HELP_STRING([--with-gcov[=GCOV]], [use given GCOV for coverage (GCOV=gcov).])], + [_AX_CODE_COVERAGE_GCOV_PROG_WITH=$with_gcov], + [_AX_CODE_COVERAGE_GCOV_PROG_WITH=gcov]) + + AC_MSG_CHECKING([whether to build with code coverage support]) + AC_ARG_ENABLE([code-coverage], + AS_HELP_STRING([--enable-code-coverage], + [Whether to enable code coverage support]),, + enable_code_coverage=no) + + AM_CONDITIONAL([CODE_COVERAGE_ENABLED], [test x$enable_code_coverage = xyes]) + AC_SUBST([CODE_COVERAGE_ENABLED], [$enable_code_coverage]) + AC_MSG_RESULT($enable_code_coverage) + + AS_IF([ test "$enable_code_coverage" = "yes" ], [ + # check for gcov + AC_CHECK_TOOL([GCOV], + [$_AX_CODE_COVERAGE_GCOV_PROG_WITH], + [:]) + AS_IF([test "X$GCOV" = "X:"], + [AC_MSG_ERROR([gcov is needed to do coverage])]) + AC_SUBST([GCOV]) + + dnl Check if gcc is being used + AS_IF([ test "$GCC" = "no" ], [ + AC_MSG_ERROR([not compiling with gcc, which is required for gcov code coverage]) + ]) + + AC_CHECK_PROG([LCOV], [lcov], [lcov]) + AC_CHECK_PROG([GENHTML], [genhtml], [genhtml]) + + AS_IF([ test -z "$GENHTML" ], [ + AC_MSG_ERROR([Could not find genhtml from the lcov package]) + ]) + + dnl Build the code coverage flags + CODE_COVERAGE_CPPFLAGS="-DNDEBUG" + CODE_COVERAGE_CFLAGS="-O0 -g -fprofile-arcs -ftest-coverage" + CODE_COVERAGE_CXXFLAGS="-O0 -g -fprofile-arcs -ftest-coverage" + dnl Using -lgcov explicitly seems to be unneeded -- BenC : Aug 13, 2017 + CODE_COVERAGE_LDFLAGS="" + + AC_SUBST([CODE_COVERAGE_CPPFLAGS]) + AC_SUBST([CODE_COVERAGE_CFLAGS]) + AC_SUBST([CODE_COVERAGE_CXXFLAGS]) + AC_SUBST([CODE_COVERAGE_LDFLAGS]) + ]) + +[CODE_COVERAGE_RULES=' +# Code coverage +# +# Optional: +# - CODE_COVERAGE_DIRECTORY: Top-level directory for code coverage reporting. +# Multiple directories may be specified, separated by whitespace. +# (Default: $(top_builddir)) +# - CODE_COVERAGE_OUTPUT_FILE: Filename and path for the .info file generated +# by lcov for code coverage. (Default: +# $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info) +# - CODE_COVERAGE_OUTPUT_DIRECTORY: Directory for generated code coverage +# reports to be created. (Default: +# $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage) +# - CODE_COVERAGE_BRANCH_COVERAGE: Set to 1 to enforce branch coverage, +# set to 0 to disable it and leave empty to stay with the default. +# (Default: empty) +# - CODE_COVERAGE_LCOV_SHOPTS_DEFAULT: Extra options shared between both lcov +# instances. (Default: based on $CODE_COVERAGE_BRANCH_COVERAGE) +# - CODE_COVERAGE_LCOV_SHOPTS: Extra options to shared between both lcov +# instances. (Default: $CODE_COVERAGE_LCOV_SHOPTS_DEFAULT) +# - CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH: --gcov-tool pathtogcov +# - CODE_COVERAGE_LCOV_OPTIONS_DEFAULT: Extra options to pass to the +# collecting lcov instance. (Default: $CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH) +# - CODE_COVERAGE_LCOV_OPTIONS: Extra options to pass to the collecting lcov +# instance. (Default: $CODE_COVERAGE_LCOV_OPTIONS_DEFAULT) +# - CODE_COVERAGE_LCOV_RMOPTS_DEFAULT: Extra options to pass to the filtering +# lcov instance. (Default: empty) +# - CODE_COVERAGE_LCOV_RMOPTS: Extra options to pass to the filtering lcov +# instance. (Default: $CODE_COVERAGE_LCOV_RMOPTS_DEFAULT) +# - CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT: Extra options to pass to the +# genhtml instance. (Default: based on $CODE_COVERAGE_BRANCH_COVERAGE) +# - CODE_COVERAGE_GENHTML_OPTIONS: Extra options to pass to the genhtml +# instance. (Default: $CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT) +# - CODE_COVERAGE_IGNORE_PATTERN: Extra glob pattern of files to ignore +# +# The generated report will be titled using the $(PACKAGE_NAME) and +# $(PACKAGE_VERSION). In order to add the current git hash to the title, +# use the git-version-gen script, available online. + +# Optional variables +CODE_COVERAGE_DIRECTORY ?= $(top_builddir) +CODE_COVERAGE_OUTPUT_FILE ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info +CODE_COVERAGE_OUTPUT_DIRECTORY ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage +CODE_COVERAGE_BRANCH_COVERAGE ?= +CODE_COVERAGE_LCOV_SHOPTS_DEFAULT ?= $(if $(CODE_COVERAGE_BRANCH_COVERAGE),\ +--rc lcov_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE)) +CODE_COVERAGE_LCOV_SHOPTS ?= $(CODE_COVERAGE_LCOV_SHOPTS_DEFAULT) +CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH ?= --gcov-tool "$(GCOV)" +CODE_COVERAGE_LCOV_OPTIONS_DEFAULT ?= $(CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH) +CODE_COVERAGE_LCOV_OPTIONS ?= $(CODE_COVERAGE_LCOV_OPTIONS_DEFAULT) +CODE_COVERAGE_LCOV_RMOPTS_DEFAULT ?= +CODE_COVERAGE_LCOV_RMOPTS ?= $(CODE_COVERAGE_LCOV_RMOPTS_DEFAULT) +CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT ?=\ +$(if $(CODE_COVERAGE_BRANCH_COVERAGE),\ +--rc genhtml_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE)) +CODE_COVERAGE_GENHTML_OPTIONS ?= $(CODE_COVERAGE_GENHTML_OPTIONS_DEFAULTS) +CODE_COVERAGE_IGNORE_PATTERN ?= + +code_coverage_v_lcov_cap = $(code_coverage_v_lcov_cap_$(V)) +code_coverage_v_lcov_cap_ = $(code_coverage_v_lcov_cap_$(AM_DEFAULT_VERBOSITY)) +code_coverage_v_lcov_cap_0 = @echo " LCOV --capture"\ + $(CODE_COVERAGE_OUTPUT_FILE); +code_coverage_v_lcov_ign = $(code_coverage_v_lcov_ign_$(V)) +code_coverage_v_lcov_ign_ = $(code_coverage_v_lcov_ign_$(AM_DEFAULT_VERBOSITY)) +code_coverage_v_lcov_ign_0 = @echo " LCOV --remove /tmp/*"\ + $(CODE_COVERAGE_IGNORE_PATTERN); +code_coverage_v_genhtml = $(code_coverage_v_genhtml_$(V)) +code_coverage_v_genhtml_ = $(code_coverage_v_genhtml_$(AM_DEFAULT_VERBOSITY)) +code_coverage_v_genhtml_0 = @echo " GEN " $(CODE_COVERAGE_OUTPUT_DIRECTORY); +code_coverage_quiet = $(code_coverage_quiet_$(V)) +code_coverage_quiet_ = $(code_coverage_quiet_$(AM_DEFAULT_VERBOSITY)) +code_coverage_quiet_0 = --quiet + +# sanitizes the test-name: replaces with underscores: dashes and dots +code_coverage_sanitize = $(subst -,_,$(subst .,_,$(1))) + +# Use recursive makes in order to ignore errors during check +check-code-coverage: +ifeq ($(CODE_COVERAGE_ENABLED),yes) + -$(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) -k check + $(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) code-coverage-capture +else + @echo "Need to reconfigure with --enable-code-coverage" +endif + +# Capture code coverage data +code-coverage-capture: code-coverage-capture-hook +ifeq ($(CODE_COVERAGE_ENABLED),yes) + $(code_coverage_v_lcov_cap)$(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --capture --output-file "$(CODE_COVERAGE_OUTPUT_FILE).tmp" --test-name "$(call code_coverage_sanitize,$(PACKAGE_NAME)-$(PACKAGE_VERSION))" --no-checksum --compat-libtool $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_OPTIONS) + $(code_coverage_v_lcov_ign)$(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --remove "$(CODE_COVERAGE_OUTPUT_FILE).tmp" --ignore-errors unused "/tmp/*" $(CODE_COVERAGE_IGNORE_PATTERN) --output-file "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_RMOPTS) + -@rm -f $(CODE_COVERAGE_OUTPUT_FILE).tmp + $(code_coverage_v_genhtml)LANG=C $(GENHTML) $(code_coverage_quiet) $(addprefix --prefix ,$(CODE_COVERAGE_DIRECTORY)) --output-directory "$(CODE_COVERAGE_OUTPUT_DIRECTORY)" --title "$(PACKAGE_NAME)-$(PACKAGE_VERSION) Code Coverage" --legend --show-details "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_GENHTML_OPTIONS) + @echo "file://$(abs_builddir)/$(CODE_COVERAGE_OUTPUT_DIRECTORY)/index.html" +else + @echo "Need to reconfigure with --enable-code-coverage" +endif + +# Hook rule executed before code-coverage-capture, overridable by the user +code-coverage-capture-hook: + +ifeq ($(CODE_COVERAGE_ENABLED),yes) +clean: code-coverage-clean +distclean: code-coverage-clean +code-coverage-clean: + -$(LCOV) --directory $(top_builddir) -z + -rm -rf $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_FILE).tmp $(CODE_COVERAGE_OUTPUT_DIRECTORY) + -find . \( -name "*.gcda" -o -name "*.gcno" -o -name "*.gcov" \) -delete +endif + +GITIGNOREFILES ?= +GITIGNOREFILES += $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_DIRECTORY) + +A''M_DISTCHECK_CONFIGURE_FLAGS ?= +A''M_DISTCHECK_CONFIGURE_FLAGS += --disable-code-coverage + +.PHONY: check-code-coverage code-coverage-capture code-coverage-capture-hook code-coverage-clean +'] + + AC_SUBST([CODE_COVERAGE_RULES]) + m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([CODE_COVERAGE_RULES])]) +]) diff --git a/m4/ax_valgrind_check.m4 b/m4/ax_valgrind_check.m4 new file mode 100644 index 00000000..b18babe6 --- /dev/null +++ b/m4/ax_valgrind_check.m4 @@ -0,0 +1,236 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_valgrind_check.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_VALGRIND_DFLT(memcheck|helgrind|drd|sgcheck, on|off) +# AX_VALGRIND_CHECK() +# +# DESCRIPTION +# +# AX_VALGRIND_CHECK checks whether Valgrind is present and, if so, allows +# running `make check` under a variety of Valgrind tools to check for +# memory and threading errors. +# +# Defines VALGRIND_CHECK_RULES which should be substituted in your +# Makefile; and $enable_valgrind which can be used in subsequent configure +# output. VALGRIND_ENABLED is defined and substituted, and corresponds to +# the value of the --enable-valgrind option, which defaults to being +# enabled if Valgrind is installed and disabled otherwise. Individual +# Valgrind tools can be disabled via --disable-valgrind-, the +# default is configurable via the AX_VALGRIND_DFLT command or is to use +# all commands not disabled via AX_VALGRIND_DFLT. All AX_VALGRIND_DFLT +# calls must be made before the call to AX_VALGRIND_CHECK. +# +# If unit tests are written using a shell script and automake's +# LOG_COMPILER system, the $(VALGRIND) variable can be used within the +# shell scripts to enable Valgrind, as described here: +# +# https://www.gnu.org/software/gnulib/manual/html_node/Running-self_002dtests-under-valgrind.html +# +# Usage example: +# +# configure.ac: +# +# AX_VALGRIND_DFLT([sgcheck], [off]) +# AX_VALGRIND_CHECK +# +# Makefile.am: +# +# @VALGRIND_CHECK_RULES@ +# VALGRIND_SUPPRESSIONS_FILES = my-project.supp +# EXTRA_DIST = my-project.supp +# +# This results in a "check-valgrind" rule being added to any Makefile.am +# which includes "@VALGRIND_CHECK_RULES@" (assuming the module has been +# configured with --enable-valgrind). Running `make check-valgrind` in +# that directory will run the module's test suite (`make check`) once for +# each of the available Valgrind tools (out of memcheck, helgrind and drd) +# while the sgcheck will be skipped unless enabled again on the +# commandline with --enable-valgrind-sgcheck. The results for each check +# will be output to test-suite-$toolname.log. The target will succeed if +# there are zero errors and fail otherwise. +# +# Alternatively, a "check-valgrind-$TOOL" rule will be added, for $TOOL in +# memcheck, helgrind, drd and sgcheck. These are useful because often only +# some of those tools can be ran cleanly on a codebase. +# +# The macro supports running with and without libtool. +# +# LICENSE +# +# Copyright (c) 2014, 2015, 2016 Philip Withnall +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 13 + +dnl Configured tools +m4_define([valgrind_tool_list], [[memcheck], [helgrind], [drd], [sgcheck]]) +m4_set_add_all([valgrind_exp_tool_set], [sgcheck]) +m4_foreach([vgtool], [valgrind_tool_list], + [m4_define([en_dflt_valgrind_]vgtool, [on])]) + +AC_DEFUN([AX_VALGRIND_DFLT],[ + m4_define([en_dflt_valgrind_$1], [$2]) +])dnl + +AC_DEFUN([AX_VALGRIND_CHECK],[ + dnl Check for --enable-valgrind + AC_ARG_ENABLE([valgrind], + [AS_HELP_STRING([--enable-valgrind], [Whether to enable Valgrind on the unit tests])], + [enable_valgrind=$enableval],[enable_valgrind=]) + + AS_IF([test "$enable_valgrind" != "no"],[ + # Check for Valgrind. + AC_CHECK_PROG([VALGRIND],[valgrind],[valgrind]) + AS_IF([test "$VALGRIND" = ""],[ + AS_IF([test "$enable_valgrind" = "yes"],[ + AC_MSG_ERROR([Could not find valgrind; either install it or reconfigure with --disable-valgrind]) + ],[ + enable_valgrind=no + ]) + ],[ + enable_valgrind=yes + ]) + ]) + + AM_CONDITIONAL([VALGRIND_ENABLED],[test "$enable_valgrind" = "yes"]) + AC_SUBST([VALGRIND_ENABLED],[$enable_valgrind]) + + # Check for Valgrind tools we care about. + [valgrind_enabled_tools=] + m4_foreach([vgtool],[valgrind_tool_list],[ + AC_ARG_ENABLE([valgrind-]vgtool, + m4_if(m4_defn([en_dflt_valgrind_]vgtool),[off],dnl +[AS_HELP_STRING([--enable-valgrind-]vgtool, [Whether to use ]vgtool[ during the Valgrind tests])],dnl +[AS_HELP_STRING([--disable-valgrind-]vgtool, [Whether to skip ]vgtool[ during the Valgrind tests])]), + [enable_valgrind_]vgtool[=$enableval], + [enable_valgrind_]vgtool[=]) + AS_IF([test "$enable_valgrind" = "no"],[ + enable_valgrind_]vgtool[=no], + [test "$enable_valgrind_]vgtool[" ]dnl +m4_if(m4_defn([en_dflt_valgrind_]vgtool), [off], [= "yes"], [!= "no"]),[ + AC_CACHE_CHECK([for Valgrind tool ]vgtool, + [ax_cv_valgrind_tool_]vgtool,[ + ax_cv_valgrind_tool_]vgtool[=no + m4_set_contains([valgrind_exp_tool_set],vgtool, + [m4_define([vgtoolx],[exp-]vgtool)], + [m4_define([vgtoolx],vgtool)]) + AS_IF([`$VALGRIND --tool=]vgtoolx[ --help >/dev/null 2>&1`],[ + ax_cv_valgrind_tool_]vgtool[=yes + ]) + ]) + AS_IF([test "$ax_cv_valgrind_tool_]vgtool[" = "no"],[ + AS_IF([test "$enable_valgrind_]vgtool[" = "yes"],[ + AC_MSG_ERROR([Valgrind does not support ]vgtool[; reconfigure with --disable-valgrind-]vgtool) + ],[ + enable_valgrind_]vgtool[=no + ]) + ],[ + enable_valgrind_]vgtool[=yes + ]) + ]) + AS_IF([test "$enable_valgrind_]vgtool[" = "yes"],[ + valgrind_enabled_tools="$valgrind_enabled_tools ]m4_bpatsubst(vgtool,[^exp-])[" + ]) + AC_SUBST([ENABLE_VALGRIND_]vgtool,[$enable_valgrind_]vgtool) + ]) + AC_SUBST([valgrind_tools],["]m4_join([ ], valgrind_tool_list)["]) + AC_SUBST([valgrind_enabled_tools],[$valgrind_enabled_tools]) + +[VALGRIND_CHECK_RULES=' +# Valgrind check +# +# Optional: +# - VALGRIND_SUPPRESSIONS_FILES: Space-separated list of Valgrind suppressions +# files to load. (Default: empty) +# - VALGRIND_FLAGS: General flags to pass to all Valgrind tools. +# (Default: --num-callers=30) +# - VALGRIND_$toolname_FLAGS: Flags to pass to Valgrind $toolname (one of: +# memcheck, helgrind, drd, sgcheck). (Default: various) + +# Optional variables +VALGRIND_SUPPRESSIONS ?= $(addprefix --suppressions=,$(VALGRIND_SUPPRESSIONS_FILES)) +VALGRIND_FLAGS ?= --num-callers=30 +VALGRIND_memcheck_FLAGS ?= --leak-check=full --show-reachable=no +VALGRIND_helgrind_FLAGS ?= --history-level=approx +VALGRIND_drd_FLAGS ?= +VALGRIND_sgcheck_FLAGS ?= + +# Internal use +valgrind_log_files = $(addprefix test-suite-,$(addsuffix .log,$(valgrind_tools))) + +valgrind_memcheck_flags = --tool=memcheck $(VALGRIND_memcheck_FLAGS) +valgrind_helgrind_flags = --tool=helgrind $(VALGRIND_helgrind_FLAGS) +valgrind_drd_flags = --tool=drd $(VALGRIND_drd_FLAGS) +valgrind_sgcheck_flags = --tool=exp-sgcheck $(VALGRIND_sgcheck_FLAGS) + +valgrind_quiet = $(valgrind_quiet_$(V)) +valgrind_quiet_ = $(valgrind_quiet_$(AM_DEFAULT_VERBOSITY)) +valgrind_quiet_0 = --quiet +valgrind_v_use = $(valgrind_v_use_$(V)) +valgrind_v_use_ = $(valgrind_v_use_$(AM_DEFAULT_VERBOSITY)) +valgrind_v_use_0 = @echo " USE " $(patsubst check-valgrind-%,%,$''@):; + +# Support running with and without libtool. +ifneq ($(LIBTOOL),) +valgrind_lt = $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=execute +else +valgrind_lt = +endif + +# Use recursive makes in order to ignore errors during check +check-valgrind: +ifeq ($(VALGRIND_ENABLED),yes) + -$(A''M_V_at)$(foreach tool,$(valgrind_enabled_tools), \ + $(MAKE) $(AM_MAKEFLAGS) -k check-valgrind-$(tool); \ + ) +else + @echo "Need to reconfigure with --enable-valgrind" +endif + +# Valgrind running +VALGRIND_TESTS_ENVIRONMENT = \ + $(TESTS_ENVIRONMENT) \ + env VALGRIND=$(VALGRIND) \ + G_SLICE=always-malloc,debug-blocks \ + G_DEBUG=fatal-warnings,fatal-criticals,gc-friendly + +VALGRIND_LOG_COMPILER = \ + $(valgrind_lt) \ + $(VALGRIND) $(VALGRIND_SUPPRESSIONS) --error-exitcode=1 $(VALGRIND_FLAGS) + +define valgrind_tool_rule = +check-valgrind-$(1): +ifeq ($$(VALGRIND_ENABLED)-$$(ENABLE_VALGRIND_$(1)),yes-yes) + $$(valgrind_v_use)$$(MAKE) check-TESTS \ + TESTS_ENVIRONMENT="$$(VALGRIND_TESTS_ENVIRONMENT)" \ + LOG_COMPILER="$$(VALGRIND_LOG_COMPILER)" \ + LOG_FLAGS="$$(valgrind_$(1)_flags)" \ + TEST_SUITE_LOG=test-suite-$(1).log +else ifeq ($$(VALGRIND_ENABLED),yes) + @echo "Need to reconfigure with --enable-valgrind-$(1)" +else + @echo "Need to reconfigure with --enable-valgrind" +endif +endef + +$(foreach tool,$(valgrind_tools),$(eval $(call valgrind_tool_rule,$(tool)))) + +A''M_DISTCHECK_CONFIGURE_FLAGS ?= +A''M_DISTCHECK_CONFIGURE_FLAGS += --disable-valgrind + +MOSTLYCLEANFILES ?= +MOSTLYCLEANFILES += $(valgrind_log_files) + +.PHONY: check-valgrind $(add-prefix check-valgrind-,$(valgrind_tools)) +'] + + AC_SUBST([VALGRIND_CHECK_RULES]) + m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([VALGRIND_CHECK_RULES])]) +]) diff --git a/m4/doxygen.m4 b/m4/doxygen.m4 new file mode 100644 index 00000000..f3f32c2d --- /dev/null +++ b/m4/doxygen.m4 @@ -0,0 +1,321 @@ +# This file is part of Autoconf. -*- Autoconf -*- + +# Copyright (C) 2004 Oren Ben-Kiki +# This file is distributed under the same terms as the Autoconf macro files. + +########## CHANGELOG ################## +# 2009-01-14 Martin Mann +# * DX_ARG_ABLE : new variable 'DX_FLAG_DX_CURRENT_FEATURE' +# * DX_CLEAR_DEPEND : use of explicit variable 'DX_FLAG_DX_CURRENT_FEATURE' +# in AC_SUBST instead of 'DX_FLAG[]DX_CURRENT_FEATURE' which is rejected by +# newer autotools + +# Generate automatic documentation using Doxygen. Works in concert with the +# aminclude.m4 file and a compatible doxygen configuration file. Defines the +# following public macros: +# +# DX_???_FEATURE(ON|OFF) - control the default setting fo a Doxygen feature. +# Supported features are 'DOXYGEN' itself, 'DOT' for generating graphics, +# 'HTML' for plain HTML, 'CHM' for compressed HTML help (for MS users), 'CHI' +# for generating a seperate .chi file by the .chm file, and 'MAN', 'RTF', +# 'XML', 'PDF' and 'PS' for the appropriate output formats. The environment +# variable DOXYGEN_PAPER_SIZE may be specified to override the default 'a4wide' +# paper size. +# +# By default, HTML, PDF and PS documentation is generated as this seems to be +# the most popular and portable combination. MAN pages created by Doxygen are +# usually problematic, though by picking an appropriate subset and doing some +# massaging they might be better than nothing. CHM and RTF are specific for MS +# (note that you can't generate both HTML and CHM at the same time). The XML is +# rather useless unless you apply specialized post-processing to it. +# +# The macro mainly controls the default state of the feature. The use can +# override the default by specifying --enable or --disable. The macros ensure +# that contradictory flags are not given (e.g., --enable-doxygen-html and +# --enable-doxygen-chm, --enable-doxygen-anything with --disable-doxygen, etc.) +# Finally, each feature will be automatically disabled (with a warning) if the +# required programs are missing. +# +# Once all the feature defaults have been specified, call DX_INIT_DOXYGEN with +# the following parameters: a one-word name for the project for use as a +# filename base etc., an optional configuration file name (the default is +# 'Doxyfile', the same as Doxygen's default), and an optional output directory +# name (the default is 'doxygen-doc'). + +## ----------## +## Defaults. ## +## ----------## + +DX_ENV="" +AC_DEFUN([DX_FEATURE_doc], ON) +AC_DEFUN([DX_FEATURE_dot], ON) +AC_DEFUN([DX_FEATURE_man], OFF) +AC_DEFUN([DX_FEATURE_html], ON) +AC_DEFUN([DX_FEATURE_chm], OFF) +AC_DEFUN([DX_FEATURE_chi], OFF) +AC_DEFUN([DX_FEATURE_rtf], OFF) +AC_DEFUN([DX_FEATURE_xml], OFF) +AC_DEFUN([DX_FEATURE_pdf], ON) +AC_DEFUN([DX_FEATURE_ps], ON) + +## --------------- ## +## Private macros. ## +## --------------- ## + +# DX_ENV_APPEND(VARIABLE, VALUE) +# ------------------------------ +# Append VARIABLE="VALUE" to DX_ENV for invoking doxygen. +AC_DEFUN([DX_ENV_APPEND], [AC_SUBST([DX_ENV], ["$DX_ENV $1='$2'"])]) + +# DX_DIRNAME_EXPR +# --------------- +# Expand into a shell expression prints the directory part of a path. +AC_DEFUN([DX_DIRNAME_EXPR], + [[expr ".$1" : '\(\.\)[^/]*$' \| "x$1" : 'x\(.*\)/[^/]*$']]) + +# DX_IF_FEATURE(FEATURE, IF-ON, IF-OFF) +# ------------------------------------- +# Expands according to the M4 (static) status of the feature. +AC_DEFUN([DX_IF_FEATURE], [ifelse(DX_FEATURE_$1, ON, [$2], [$3])]) + +# DX_REQUIRE_PROG(VARIABLE, PROGRAM) +# ---------------------------------- +# Require the specified program to be found for the DX_CURRENT_FEATURE to work. +AC_DEFUN([DX_REQUIRE_PROG], [ +AC_PATH_TOOL([$1], [$2]) +if test "$DX_FLAG_DX_CURRENT_FEATURE$$1" = 1; then + AC_MSG_WARN([$2 not found - will not DX_CURRENT_DESCRIPTION]) + AC_SUBST([DX_FLAG_DX_CURRENT_FEATURE], 0) +fi +]) + +# DX_TEST_FEATURE(FEATURE) +# ------------------------ +# Expand to a shell expression testing whether the feature is active. +AC_DEFUN([DX_TEST_FEATURE], [test "$DX_FLAG_$1" = 1]) + +# DX_CHECK_DEPEND(REQUIRED_FEATURE, REQUIRED_STATE) +# ------------------------------------------------- +# Verify that a required features has the right state before trying to turn on +# the DX_CURRENT_FEATURE. +AC_DEFUN([DX_CHECK_DEPEND], [ +test "$DX_FLAG_$1" = "$2" \ +|| AC_MSG_ERROR([doxygen-DX_CURRENT_FEATURE ifelse([$2], 1, + requires, contradicts) doxygen-DX_CURRENT_FEATURE]) +]) + +# DX_CLEAR_DEPEND(FEATURE, REQUIRED_FEATURE, REQUIRED_STATE) +# ---------------------------------------------------------- +# Turn off the DX_CURRENT_FEATURE if the required feature is off. +AC_DEFUN([DX_CLEAR_DEPEND], [ +test "$DX_FLAG_$1" = "$2" || AC_SUBST([DX_FLAG_DX_CURRENT_FEATURE], 0) +]) + + +# DX_FEATURE_ARG(FEATURE, DESCRIPTION, +# CHECK_DEPEND, CLEAR_DEPEND, +# REQUIRE, DO-IF-ON, DO-IF-OFF) +# -------------------------------------------- +# Parse the command-line option controlling a feature. CHECK_DEPEND is called +# if the user explicitly turns the feature on (and invokes DX_CHECK_DEPEND), +# otherwise CLEAR_DEPEND is called to turn off the default state if a required +# feature is disabled (using DX_CLEAR_DEPEND). REQUIRE performs additional +# requirement tests (DX_REQUIRE_PROG). Finally, an automake flag is set and +# DO-IF-ON or DO-IF-OFF are called according to the final state of the feature. +AC_DEFUN([DX_ARG_ABLE], [ + AC_DEFUN([DX_CURRENT_FEATURE], [$1]) + AC_DEFUN([DX_FLAG_DX_CURRENT_FEATURE], [DX_FLAG_$1]) + AC_DEFUN([DX_CURRENT_DESCRIPTION], [$2]) + AC_ARG_ENABLE(doxygen-$1, + [AS_HELP_STRING(DX_IF_FEATURE([$1], [--disable-doxygen-$1], + [--enable-doxygen-$1]), + DX_IF_FEATURE([$1], [don't $2], [$2]))], + [ +case "$enableval" in +#( +y|Y|yes|Yes|YES) + AC_SUBST([DX_FLAG_$1], 1) + $3 +;; #( +n|N|no|No|NO) + AC_SUBST([DX_FLAG_$1], 0) +;; #( +*) + AC_MSG_ERROR([invalid value '$enableval' given to doxygen-$1]) +;; +esac +], [ +AC_SUBST([DX_FLAG_$1], [DX_IF_FEATURE([$1], 1, 0)]) +$4 +]) +if DX_TEST_FEATURE([$1]); then + $5 + : +fi +if DX_TEST_FEATURE([$1]); then + AM_CONDITIONAL(DX_COND_$1, :) + $6 + : +else + AM_CONDITIONAL(DX_COND_$1, false) + $7 + : +fi +]) + +## -------------- ## +## Public macros. ## +## -------------- ## + +# DX_XXX_FEATURE(DEFAULT_STATE) +# ----------------------------- +AC_DEFUN([DX_DOXYGEN_FEATURE], [AC_DEFUN([DX_FEATURE_doc], [$1])]) +AC_DEFUN([DX_MAN_FEATURE], [AC_DEFUN([DX_FEATURE_man], [$1])]) +AC_DEFUN([DX_HTML_FEATURE], [AC_DEFUN([DX_FEATURE_html], [$1])]) +AC_DEFUN([DX_CHM_FEATURE], [AC_DEFUN([DX_FEATURE_chm], [$1])]) +AC_DEFUN([DX_CHI_FEATURE], [AC_DEFUN([DX_FEATURE_chi], [$1])]) +AC_DEFUN([DX_RTF_FEATURE], [AC_DEFUN([DX_FEATURE_rtf], [$1])]) +AC_DEFUN([DX_XML_FEATURE], [AC_DEFUN([DX_FEATURE_xml], [$1])]) +AC_DEFUN([DX_XML_FEATURE], [AC_DEFUN([DX_FEATURE_xml], [$1])]) +AC_DEFUN([DX_PDF_FEATURE], [AC_DEFUN([DX_FEATURE_pdf], [$1])]) +AC_DEFUN([DX_PS_FEATURE], [AC_DEFUN([DX_FEATURE_ps], [$1])]) + +# DX_INIT_DOXYGEN(PROJECT, [CONFIG-FILE], [OUTPUT-DOC-DIR]) +# --------------------------------------------------------- +# PROJECT also serves as the base name for the documentation files. +# The default CONFIG-FILE is "Doxyfile" and OUTPUT-DOC-DIR is "doxygen-doc". +AC_DEFUN([DX_INIT_DOXYGEN], [ + +# Files: +AC_SUBST([DX_PROJECT], [$1]) +AC_SUBST([DX_CONFIG], [ifelse([$2], [], Doxyfile, [$2])]) +AC_SUBST([DX_DOCDIR], [ifelse([$3], [], doxygen-doc, [$3])]) + +# Environment variables used inside doxygen.cfg: +DX_ENV_APPEND(SRCDIR, $srcdir/include) +DX_ENV_APPEND(PROJECT, $DX_PROJECT) +DX_ENV_APPEND(DOCDIR, $DX_DOCDIR) +DX_ENV_APPEND(VERSION, $PACKAGE_VERSION) + +# Doxygen itself: +DX_ARG_ABLE(doc, [generate any doxygen documentation], + [], + [], + [DX_REQUIRE_PROG([DX_DOXYGEN], doxygen) + DX_REQUIRE_PROG([DX_PERL], perl)], + [DX_ENV_APPEND(PERL_PATH, $DX_PERL)]) + +# Dot for graphics: +DX_ARG_ABLE(dot, [generate graphics for doxygen documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [DX_REQUIRE_PROG([DX_DOT], dot)], + [DX_ENV_APPEND(HAVE_DOT, YES) + DX_ENV_APPEND(DOT_PATH, [`DX_DIRNAME_EXPR($DX_DOT)`])], + [DX_ENV_APPEND(HAVE_DOT, NO)]) + +# Man pages generation: +DX_ARG_ABLE(man, [generate doxygen manual pages], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [], + [DX_ENV_APPEND(GENERATE_MAN, YES)], + [DX_ENV_APPEND(GENERATE_MAN, NO)]) + +# RTF file generation: +DX_ARG_ABLE(rtf, [generate doxygen RTF documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [], + [DX_ENV_APPEND(GENERATE_RTF, YES)], + [DX_ENV_APPEND(GENERATE_RTF, NO)]) + +# XML file generation: +DX_ARG_ABLE(xml, [generate doxygen XML documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [], + [DX_ENV_APPEND(GENERATE_XML, YES)], + [DX_ENV_APPEND(GENERATE_XML, NO)]) + +# (Compressed) HTML help generation: +DX_ARG_ABLE(chm, [generate doxygen compressed HTML help documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [DX_REQUIRE_PROG([DX_HHC], hhc)], + [DX_ENV_APPEND(HHC_PATH, $DX_HHC) + DX_ENV_APPEND(GENERATE_HTML, YES) + DX_ENV_APPEND(GENERATE_HTMLHELP, YES)], + [DX_ENV_APPEND(GENERATE_HTMLHELP, NO)]) + +# Seperate CHI file generation. +DX_ARG_ABLE(chi, [generate doxygen seperate compressed HTML help index file], + [DX_CHECK_DEPEND(chm, 1)], + [DX_CLEAR_DEPEND(chm, 1)], + [], + [DX_ENV_APPEND(GENERATE_CHI, YES)], + [DX_ENV_APPEND(GENERATE_CHI, NO)]) + +# Plain HTML pages generation: +DX_ARG_ABLE(html, [generate doxygen plain HTML documentation], + [DX_CHECK_DEPEND(doc, 1) DX_CHECK_DEPEND(chm, 0)], + [DX_CLEAR_DEPEND(doc, 1) DX_CLEAR_DEPEND(chm, 0)], + [], + [DX_ENV_APPEND(GENERATE_HTML, YES)], + [DX_TEST_FEATURE(chm) || DX_ENV_APPEND(GENERATE_HTML, NO)]) + +# PostScript file generation: +DX_ARG_ABLE(ps, [generate doxygen PostScript documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [DX_REQUIRE_PROG([DX_LATEX], latex) + DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex) + DX_REQUIRE_PROG([DX_DVIPS], dvips) + DX_REQUIRE_PROG([DX_EGREP], egrep)]) + +# PDF file generation: +DX_ARG_ABLE(pdf, [generate doxygen PDF documentation], + [DX_CHECK_DEPEND(doc, 1)], + [DX_CLEAR_DEPEND(doc, 1)], + [DX_REQUIRE_PROG([DX_PDFLATEX], pdflatex) + DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex) + DX_REQUIRE_PROG([DX_EGREP], egrep)]) + +# LaTeX generation for PS and/or PDF: +if DX_TEST_FEATURE(ps) || DX_TEST_FEATURE(pdf); then + AM_CONDITIONAL(DX_COND_latex, :) + DX_ENV_APPEND(GENERATE_LATEX, YES) +else + AM_CONDITIONAL(DX_COND_latex, false) + DX_ENV_APPEND(GENERATE_LATEX, NO) +fi + +# Paper size for PS and/or PDF: +AC_ARG_VAR(DOXYGEN_PAPER_SIZE, + [a4wide (default), a4, letter, legal or executive]) +case "$DOXYGEN_PAPER_SIZE" in +#( +"") + AC_SUBST(DOXYGEN_PAPER_SIZE, "") +;; #( +a4wide|a4|letter|legal|executive) + DX_ENV_APPEND(PAPER_SIZE, $DOXYGEN_PAPER_SIZE) +;; #( +*) + AC_MSG_ERROR([unknown DOXYGEN_PAPER_SIZE='$DOXYGEN_PAPER_SIZE']) +;; +esac + +#For debugging: +#echo DX_FLAG_doc=$DX_FLAG_doc +#echo DX_FLAG_dot=$DX_FLAG_dot +#echo DX_FLAG_man=$DX_FLAG_man +#echo DX_FLAG_html=$DX_FLAG_html +#echo DX_FLAG_chm=$DX_FLAG_chm +#echo DX_FLAG_chi=$DX_FLAG_chi +#echo DX_FLAG_rtf=$DX_FLAG_rtf +#echo DX_FLAG_xml=$DX_FLAG_xml +#echo DX_FLAG_pdf=$DX_FLAG_pdf +#echo DX_FLAG_ps=$DX_FLAG_ps +#echo DX_ENV=$DX_ENV +]) diff --git a/menu.js b/menu.js new file mode 100644 index 00000000..0fd1e990 --- /dev/null +++ b/menu.js @@ -0,0 +1,134 @@ +/* + @licstart The following is the entire license notice for the JavaScript code in this file. + + The MIT License (MIT) + + Copyright (C) 1997-2020 by Dimitri van Heesch + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software + and associated documentation files (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, publish, distribute, + sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + @licend The above is the entire license notice for the JavaScript code in this file + */ +function initMenu(relPath,searchEnabled,serverSide,searchPage,search,treeview) { + function makeTree(data,relPath) { + let result=''; + if ('children' in data) { + result+='
    '; + for (let i in data.children) { + let url; + const link = data.children[i].url; + if (link.substring(0,1)=='^') { + url = link.substring(1); + } else { + url = relPath+link; + } + result+='
  • '+ + data.children[i].text+''+ + makeTree(data.children[i],relPath)+'
  • '; + } + result+='
'; + } + return result; + } + let searchBoxHtml; + if (searchEnabled) { + if (serverSide) { + searchBoxHtml='
'+ + '
'+ + '
 '+ + ''+ + '
'+ + '
'+ + '
'+ + '
'; + } else { + searchBoxHtml='
'+ + ''+ + ' '+ + ''+ + ''+ + ''+ + ''+ + ''+ + '
'; + } + } + + $('#main-nav').before('
'+ + ''+ + ''+ + '
'); + $('#main-nav').append(makeTree(menudata,relPath)); + $('#main-nav').children(':first').addClass('sm sm-dox').attr('id','main-menu'); + if (searchBoxHtml) { + $('#main-menu').append('
  • '); + } + const $mainMenuState = $('#main-menu-state'); + let prevWidth = 0; + if ($mainMenuState.length) { + const initResizableIfExists = function() { + if (typeof initResizable==='function') initResizable(treeview); + } + // animate mobile menu + $mainMenuState.change(function() { + const $menu = $('#main-menu'); + let options = { duration: 250, step: initResizableIfExists }; + if (this.checked) { + options['complete'] = () => $menu.css('display', 'block'); + $menu.hide().slideDown(options); + } else { + options['complete'] = () => $menu.css('display', 'none'); + $menu.show().slideUp(options); + } + }); + // set default menu visibility + const resetState = function() { + const $menu = $('#main-menu'); + const newWidth = $(window).outerWidth(); + if (newWidth!=prevWidth) { + if ($(window).outerWidth()<768) { + $mainMenuState.prop('checked',false); $menu.hide(); + $('#searchBoxPos1').html(searchBoxHtml); + $('#searchBoxPos2').hide(); + } else { + $menu.show(); + $('#searchBoxPos1').empty(); + $('#searchBoxPos2').html(searchBoxHtml); + $('#searchBoxPos2').show(); + } + if (typeof searchBox!=='undefined') { + searchBox.CloseResultsWindow(); + } + prevWidth = newWidth; + } + } + $(window).ready(function() { resetState(); initResizableIfExists(); }); + $(window).resize(resetState); + } + $('#main-menu').smartmenus(); +} +/* @license-end */ diff --git a/menudata.js b/menudata.js new file mode 100644 index 00000000..94b096f9 --- /dev/null +++ b/menudata.js @@ -0,0 +1,45 @@ +/* + @licstart The following is the entire license notice for the JavaScript code in this file. + + The MIT License (MIT) + + Copyright (C) 1997-2020 by Dimitri van Heesch + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software + and associated documentation files (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, publish, distribute, + sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + @licend The above is the entire license notice for the JavaScript code in this file +*/ +var menudata={children:[ +{text:"Main Page",url:"index.html"}, +{text:"Topics",url:"topics.html"}, +{text:"Data Structures",url:"annotated.html",children:[ +{text:"Data Structures",url:"annotated.html"}, +{text:"Data Structure Index",url:"classes.html"}, +{text:"Data Fields",url:"functions.html",children:[ +{text:"All",url:"functions.html"}, +{text:"Variables",url:"functions_vars.html"}]}]}, +{text:"Files",url:"files.html",children:[ +{text:"File List",url:"files.html"}, +{text:"Globals",url:"globals.html",children:[ +{text:"All",url:"globals.html",children:[ +{text:"d",url:"globals.html#index_d"}, +{text:"j",url:"globals.html#index_j"}]}, +{text:"Functions",url:"globals_func.html",children:[ +{text:"j",url:"globals_func.html#index_j"}]}, +{text:"Typedefs",url:"globals_type.html"}, +{text:"Enumerations",url:"globals_enum.html"}, +{text:"Enumerator",url:"globals_eval.html"}, +{text:"Macros",url:"globals_defs.html"}]}]}]} diff --git a/minus.svg b/minus.svg new file mode 100644 index 00000000..f70d0c1a --- /dev/null +++ b/minus.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/minusd.svg b/minusd.svg new file mode 100644 index 00000000..5f8e8796 --- /dev/null +++ b/minusd.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/nav_f.png b/nav_f.png new file mode 100644 index 0000000000000000000000000000000000000000..72a58a529ed3a9ed6aa0c51a79cf207e026deee2 GIT binary patch literal 153 zcmeAS@N?(olHy`uVBq!ia0vp^j6iI`!2~2XGqLUlQVE_ejv*C{Z|{2ZH7M}7UYxc) zn!W8uqtnIQ>_z8U literal 0 HcmV?d00001 diff --git a/nav_fd.png b/nav_fd.png new file mode 100644 index 0000000000000000000000000000000000000000..032fbdd4c54f54fa9a2e6423b94ef4b2ebdfaceb GIT binary patch literal 169 zcmeAS@N?(olHy`uVBq!ia0vp^j6iI`!2~2XGqLUlQU#tajv*C{Z|C~*H7f|XvG1G8 zt7aS*L7xwMeS}!z6R#{C5tIw-s~AJ==F^i}x3XyJseHR@yF& zerFf(Zf;Dd{+(0lDIROL@Sj-Ju2JQ8&-n%4%q?>|^bShc&lR?}7HeMo@BDl5N(aHY Uj$gdr1MOz;boFyt=akR{0D!zeaR2}S literal 0 HcmV?d00001 diff --git a/nav_g.png b/nav_g.png new file mode 100644 index 0000000000000000000000000000000000000000..2093a237a94f6c83e19ec6e5fd42f7ddabdafa81 GIT binary patch literal 95 zcmeAS@N?(olHy`uVBq!ia0vp^j6lrB!3HFm1ilyoDK$?Q$B+ufw|5PB85lU25BhtE tr?otc=hd~V+ws&_A@j8Fiv!KF$B+ufw|5=67#uj90@pIL wZ=Q8~_Ju`#59=RjDrmm`tMD@M=!-l18IR?&vFVdQ&MBb@0HFXL6W-eg#Jd_@e6*DPn)w;=|1H}Zvm9l6xXXB%>yL=NQU;mg M>FVdQ&MBb@0Bdt1Qvd(} literal 0 HcmV?d00001 diff --git a/navtree.css b/navtree.css new file mode 100644 index 00000000..69211d4a --- /dev/null +++ b/navtree.css @@ -0,0 +1,149 @@ +#nav-tree .children_ul { + margin:0; + padding:4px; +} + +#nav-tree ul { + list-style:none outside none; + margin:0px; + padding:0px; +} + +#nav-tree li { + white-space:nowrap; + margin:0px; + padding:0px; +} + +#nav-tree .plus { + margin:0px; +} + +#nav-tree .selected { + background-image: url('tab_a.png'); + background-repeat:repeat-x; + color: var(--nav-text-active-color); + text-shadow: var(--nav-text-active-shadow); +} + +#nav-tree .selected .arrow { + color: var(--nav-arrow-selected-color); + text-shadow: none; +} + +#nav-tree img { + margin:0px; + padding:0px; + border:0px; + vertical-align: middle; +} + +#nav-tree a { + text-decoration:none; + padding:0px; + margin:0px; +} + +#nav-tree .label { + margin:0px; + padding:0px; + font: 12px var(--font-family-nav); +} + +#nav-tree .label a { + padding:2px; +} + +#nav-tree .selected a { + text-decoration:none; + color:var(--nav-text-active-color); +} + +#nav-tree .children_ul { + margin:0px; + padding:0px; +} + +#nav-tree .item { + margin:0px; + padding:0px; +} + +#nav-tree { + padding: 0px 0px; + font-size:14px; + overflow:auto; +} + +#doc-content { + overflow:auto; + display:block; + padding:0px; + margin:0px; + -webkit-overflow-scrolling : touch; /* iOS 5+ */ +} + +#side-nav { + padding:0 6px 0 0; + margin: 0px; + display:block; + position: absolute; + left: 0px; + width: $width; + overflow : hidden; +} + +.ui-resizable .ui-resizable-handle { + display:block; +} + +.ui-resizable-e { + background-image:var(--nav-splitbar-image); + background-size:100%; + background-repeat:repeat-y; + background-attachment: scroll; + cursor:ew-resize; + height:100%; + right:0; + top:0; + width:6px; +} + +.ui-resizable-handle { + display:none; + font-size:0.1px; + position:absolute; + z-index:1; +} + +#nav-tree-contents { + margin: 6px 0px 0px 0px; +} + +#nav-tree { + background-repeat:repeat-x; + background-color: var(--nav-background-color); + -webkit-overflow-scrolling : touch; /* iOS 5+ */ +} + +#nav-sync { + position:absolute; + top:5px; + right:24px; + z-index:0; +} + +#nav-sync img { + opacity:0.3; +} + +#nav-sync img:hover { + opacity:0.9; +} + +@media print +{ + #nav-tree { display: none; } + div.ui-resizable-handle { display: none; position: relative; } +} + diff --git a/navtree.js b/navtree.js new file mode 100644 index 00000000..2d4fa84a --- /dev/null +++ b/navtree.js @@ -0,0 +1,483 @@ +/* + @licstart The following is the entire license notice for the JavaScript code in this file. + + The MIT License (MIT) + + Copyright (C) 1997-2020 by Dimitri van Heesch + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software + and associated documentation files (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, publish, distribute, + sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + @licend The above is the entire license notice for the JavaScript code in this file + */ + +function initNavTree(toroot,relpath) { + let navTreeSubIndices = []; + const ARROW_DOWN = '▼'; + const ARROW_RIGHT = '►'; + const NAVPATH_COOKIE_NAME = ''+'navpath'; + + const getData = function(varName) { + const i = varName.lastIndexOf('/'); + const n = i>=0 ? varName.substring(i+1) : varName; + return eval(n.replace(/-/g,'_')); + } + + const stripPath = function(uri) { + return uri.substring(uri.lastIndexOf('/')+1); + } + + const stripPath2 = function(uri) { + const i = uri.lastIndexOf('/'); + const s = uri.substring(i+1); + const m = uri.substring(0,i+1).match(/\/d\w\/d\w\w\/$/); + return m ? uri.substring(i-6) : s; + } + + const hashValue = function() { + return $(location).attr('hash').substring(1).replace(/[^\w-]/g,''); + } + + const hashUrl = function() { + return '#'+hashValue(); + } + + const pathName = function() { + return $(location).attr('pathname').replace(/[^-A-Za-z0-9+&@#/%?=~_|!:,.;()]/g, ''); + } + + const storeLink = function(link) { + if (!$("#nav-sync").hasClass('sync')) { + Cookie.writeSetting(NAVPATH_COOKIE_NAME,link,0); + } + } + + const deleteLink = function() { + Cookie.eraseSetting(NAVPATH_COOKIE_NAME); + } + + const cachedLink = function() { + return Cookie.readSetting(NAVPATH_COOKIE_NAME,''); + } + + const getScript = function(scriptName,func) { + const head = document.getElementsByTagName("head")[0]; + const script = document.createElement('script'); + script.id = scriptName; + script.type = 'text/javascript'; + script.onload = func; + script.src = scriptName+'.js'; + head.appendChild(script); + } + + const createIndent = function(o,domNode,node) { + let level=-1; + let n = node; + while (n.parentNode) { level++; n=n.parentNode; } + if (node.childrenData) { + const imgNode = document.createElement("span"); + imgNode.className = 'arrow'; + imgNode.style.paddingLeft=(16*level).toString()+'px'; + imgNode.innerHTML=ARROW_RIGHT; + node.plus_img = imgNode; + node.expandToggle = document.createElement("a"); + node.expandToggle.href = "javascript:void(0)"; + node.expandToggle.onclick = function() { + if (node.expanded) { + $(node.getChildrenUL()).slideUp("fast"); + node.plus_img.innerHTML=ARROW_RIGHT; + node.expanded = false; + } else { + expandNode(o, node, false, true); + } + } + node.expandToggle.appendChild(imgNode); + domNode.appendChild(node.expandToggle); + } else { + let span = document.createElement("span"); + span.className = 'arrow'; + span.style.width = 16*(level+1)+'px'; + span.innerHTML = ' '; + domNode.appendChild(span); + } + } + + let animationInProgress = false; + + const gotoAnchor = function(anchor,aname) { + let pos, docContent = $('#doc-content'); + let ancParent = $(anchor.parent()); + if (ancParent.hasClass('memItemLeft') || ancParent.hasClass('memtitle') || + ancParent.hasClass('fieldname') || ancParent.hasClass('fieldtype') || + ancParent.is(':header')) { + pos = ancParent.offset().top; + } else if (anchor.position()) { + pos = anchor.offset().top; + } + if (pos) { + const dcOffset = docContent.offset().top; + const dcHeight = docContent.height(); + const dcScrHeight = docContent[0].scrollHeight + const dcScrTop = docContent.scrollTop(); + let dist = Math.abs(Math.min(pos-dcOffset,dcScrHeight-dcHeight-dcScrTop)); + animationInProgress = true; + docContent.animate({ + scrollTop: pos + dcScrTop - dcOffset + },Math.max(50,Math.min(500,dist)),function() { + animationInProgress=false; + if (anchor.parent().attr('class')=='memItemLeft') { + let rows = $('.memberdecls tr[class$="'+hashValue()+'"]'); + glowEffect(rows.children(),300); // member without details + } else if (anchor.parent().attr('class')=='fieldname') { + glowEffect(anchor.parent().parent(),1000); // enum value + } else if (anchor.parent().attr('class')=='fieldtype') { + glowEffect(anchor.parent().parent(),1000); // struct field + } else if (anchor.parent().is(":header")) { + glowEffect(anchor.parent(),1000); // section header + } else { + glowEffect(anchor.next(),1000); // normal member + } + }); + } + } + + const newNode = function(o, po, text, link, childrenData, lastNode) { + const node = { + children : [], + childrenData : childrenData, + depth : po.depth + 1, + relpath : po.relpath, + isLast : lastNode, + li : document.createElement("li"), + parentNode : po, + itemDiv : document.createElement("div"), + labelSpan : document.createElement("span"), + label : document.createTextNode(text), + expanded : false, + childrenUL : null, + getChildrenUL : function() { + if (!this.childrenUL) { + this.childrenUL = document.createElement("ul"); + this.childrenUL.className = "children_ul"; + this.childrenUL.style.display = "none"; + this.li.appendChild(node.childrenUL); + } + return node.childrenUL; + }, + }; + + node.itemDiv.className = "item"; + node.labelSpan.className = "label"; + createIndent(o,node.itemDiv,node); + node.itemDiv.appendChild(node.labelSpan); + node.li.appendChild(node.itemDiv); + + const a = document.createElement("a"); + node.labelSpan.appendChild(a); + po.getChildrenUL().appendChild(node.li); + a.appendChild(node.label); + if (link) { + let url; + if (link.substring(0,1)=='^') { + url = link.substring(1); + link = url; + } else { + url = node.relpath+link; + } + a.className = stripPath(link.replace('#',':')); + if (link.indexOf('#')!=-1) { + const aname = '#'+link.split('#')[1]; + const srcPage = stripPath(pathName()); + const targetPage = stripPath(link.split('#')[0]); + a.href = srcPage!=targetPage ? url : aname; + a.onclick = function() { + storeLink(link); + aPPar = $(a).parent().parent(); + if (!aPPar.hasClass('selected')) { + $('.item').removeClass('selected'); + $('.item').removeAttr('id'); + aPPar.addClass('selected'); + aPPar.attr('id','selected'); + } + const anchor = $(aname); + gotoAnchor(anchor,aname); + }; + } else { + a.href = url; + a.onclick = () => storeLink(link); + } + } else if (childrenData != null) { + a.className = "nolink"; + a.href = "javascript:void(0)"; + a.onclick = node.expandToggle.onclick; + } + return node; + } + + const showRoot = function() { + const headerHeight = $("#top").height(); + const footerHeight = $("#nav-path").height(); + const windowHeight = $(window).height() - headerHeight - footerHeight; + (function() { // retry until we can scroll to the selected item + try { + const navtree=$('#nav-tree'); + navtree.scrollTo('#selected',100,{offset:-windowHeight/2}); + } catch (err) { + setTimeout(arguments.callee, 0); + } + })(); + } + + const expandNode = function(o, node, imm, setFocus) { + if (node.childrenData && !node.expanded) { + if (typeof(node.childrenData)==='string') { + const varName = node.childrenData; + getScript(node.relpath+varName,function() { + node.childrenData = getData(varName); + expandNode(o, node, imm, setFocus); + }); + } else { + if (!node.childrenVisited) { + getNode(o, node); + } + $(node.getChildrenUL()).slideDown("fast"); + node.plus_img.innerHTML = ARROW_DOWN; + node.expanded = true; + if (setFocus) { + $(node.expandToggle).focus(); + } + } + } + } + + const glowEffect = function(n,duration) { + n.addClass('glow').delay(duration).queue(function(next) { + $(this).removeClass('glow');next(); + }); + } + + const highlightAnchor = function() { + const aname = hashUrl(); + const anchor = $(aname); + gotoAnchor(anchor,aname); + } + + const selectAndHighlight = function(hash,n) { + let a; + if (hash) { + const link=stripPath(pathName())+':'+hash.substring(1); + a=$('.item a[class$="'+link+'"]'); + } + if (a && a.length) { + a.parent().parent().addClass('selected'); + a.parent().parent().attr('id','selected'); + highlightAnchor(); + } else if (n) { + $(n.itemDiv).addClass('selected'); + $(n.itemDiv).attr('id','selected'); + } + let topOffset=5; + if ($('#nav-tree-contents .item:first').hasClass('selected')) { + topOffset+=25; + } + $('#nav-sync').css('top',topOffset+'px'); + showRoot(); + } + + const showNode = function(o, node, index, hash) { + if (node && node.childrenData) { + if (typeof(node.childrenData)==='string') { + const varName = node.childrenData; + getScript(node.relpath+varName,function() { + node.childrenData = getData(varName); + showNode(o,node,index,hash); + }); + } else { + if (!node.childrenVisited) { + getNode(o, node); + } + $(node.getChildrenUL()).css({'display':'block'}); + node.plus_img.innerHTML = ARROW_DOWN; + node.expanded = true; + const n = node.children[o.breadcrumbs[index]]; + if (index+11 ? '#'+parts[1].replace(/[^\w-]/g,'') : ''; + } + if (hash.match(/^#l\d+$/)) { + const anchor=$('a[name='+hash.substring(1)+']'); + glowEffect(anchor.parent(),1000); // line number + hash=''; // strip line number anchors + } + const url=root+hash; + let i=-1; + while (NAVTREEINDEX[i+1]<=url) i++; + if (i==-1) { i=0; root=NAVTREE[0][1]; } // fallback: show index + if (navTreeSubIndices[i]) { + gotoNode(o,i,root,hash,relpath) + } else { + getScript(relpath+'navtreeindex'+i,function() { + navTreeSubIndices[i] = eval('NAVTREEINDEX'+i); + if (navTreeSubIndices[i]) { + gotoNode(o,i,root,hash,relpath); + } + }); + } + } + + const showSyncOff = function(n,relpath) { + n.html(''); + } + + const showSyncOn = function(n,relpath) { + n.html(''); + } + + const o = { + toroot : toroot, + node : { + childrenData : NAVTREE, + children : [], + childrenUL : document.createElement("ul"), + getChildrenUL : function() { return this.childrenUL }, + li : document.getElementById("nav-tree-contents"), + depth : 0, + relpath : relpath, + expanded : false, + isLast : true, + plus_img : document.createElement("span"), + }, + }; + o.node.li.appendChild(o.node.childrenUL); + o.node.plus_img.className = 'arrow'; + o.node.plus_img.innerHTML = ARROW_RIGHT; + + const navSync = $('#nav-sync'); + if (cachedLink()) { + showSyncOff(navSync,relpath); + navSync.removeClass('sync'); + } else { + showSyncOn(navSync,relpath); + } + + navSync.click(() => { + const navSync = $('#nav-sync'); + if (navSync.hasClass('sync')) { + navSync.removeClass('sync'); + showSyncOff(navSync,relpath); + storeLink(stripPath2(pathName())+hashUrl()); + } else { + navSync.addClass('sync'); + showSyncOn(navSync,relpath); + deleteLink(); + } + }); + + navTo(o,toroot,hashUrl(),relpath); + showRoot(); + + $(window).bind('hashchange', () => { + if (!animationInProgress) { + if (window.location.hash && window.location.hash.length>1) { + let a; + if ($(location).attr('hash')) { + const clslink=stripPath(pathName())+':'+hashValue(); + a=$('.item a[class$="'+clslink.replace(/1|%O$WD@{VPM$7~Ar*{o?;hlAFyLXmaDC0y znK1_#cQqJWPES%4Uujug^TE?jMft$}Eq^WaR~)%f)vSNs&gek&x%A9X9sM + + + + + + + + diff --git a/plusd.svg b/plusd.svg new file mode 100644 index 00000000..0c65bfe9 --- /dev/null +++ b/plusd.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resize.js b/resize.js new file mode 100644 index 00000000..178d03bc --- /dev/null +++ b/resize.js @@ -0,0 +1,147 @@ +/* + @licstart The following is the entire license notice for the JavaScript code in this file. + + The MIT License (MIT) + + Copyright (C) 1997-2020 by Dimitri van Heesch + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software + and associated documentation files (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, publish, distribute, + sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + @licend The above is the entire license notice for the JavaScript code in this file + */ + +function initResizable(treeview) { + let sidenav,navtree,content,header,footer,barWidth=6; + const RESIZE_COOKIE_NAME = ''+'width'; + + function resizeWidth() { + const sidenavWidth = $(sidenav).outerWidth(); + content.css({marginLeft:parseInt(sidenavWidth)+"px"}); + if (typeof page_layout!=='undefined' && page_layout==1) { + footer.css({marginLeft:parseInt(sidenavWidth)+"px"}); + } + Cookie.writeSetting(RESIZE_COOKIE_NAME,sidenavWidth-barWidth); + } + + function restoreWidth(navWidth) { + content.css({marginLeft:parseInt(navWidth)+barWidth+"px"}); + if (typeof page_layout!=='undefined' && page_layout==1) { + footer.css({marginLeft:parseInt(navWidth)+barWidth+"px"}); + } + sidenav.css({width:navWidth + "px"}); + } + + function resizeHeight(treeview) { + const headerHeight = header.outerHeight(); + const windowHeight = $(window).height(); + let contentHeight; + if (treeview) + { + const footerHeight = footer.outerHeight(); + let navtreeHeight,sideNavHeight; + if (typeof page_layout==='undefined' || page_layout==0) { /* DISABLE_INDEX=NO */ + contentHeight = windowHeight - headerHeight - footerHeight; + navtreeHeight = contentHeight; + sideNavHeight = contentHeight; + } else if (page_layout==1) { /* DISABLE_INDEX=YES */ + contentHeight = windowHeight - footerHeight; + navtreeHeight = windowHeight - headerHeight; + sideNavHeight = windowHeight; + } + navtree.css({height:navtreeHeight + "px"}); + sidenav.css({height:sideNavHeight + "px"}); + } + else + { + contentHeight = windowHeight - headerHeight; + } + content.css({height:contentHeight + "px"}); + if (location.hash.slice(1)) { + (document.getElementById(location.hash.slice(1))||document.body).scrollIntoView(); + } + } + + function collapseExpand() { + let newWidth; + if (sidenav.width()>0) { + newWidth=0; + } else { + const width = Cookie.readSetting(RESIZE_COOKIE_NAME,250); + newWidth = (width>250 && width<$(window).width()) ? width : 250; + } + restoreWidth(newWidth); + const sidenavWidth = $(sidenav).outerWidth(); + Cookie.writeSetting(RESIZE_COOKIE_NAME,sidenavWidth-barWidth); + } + + header = $("#top"); + content = $("#doc-content"); + footer = $("#nav-path"); + sidenav = $("#side-nav"); + if (!treeview) { +// title = $("#titlearea"); +// titleH = $(title).height(); +// let animating = false; +// content.on("scroll", function() { +// slideOpts = { duration: 200, +// step: function() { +// contentHeight = $(window).height() - header.outerHeight(); +// content.css({ height : contentHeight + "px" }); +// }, +// done: function() { animating=false; } +// }; +// if (content.scrollTop()>titleH && title.css('display')!='none' && !animating) { +// title.slideUp(slideOpts); +// animating=true; +// } else if (content.scrollTop()<=titleH && title.css('display')=='none' && !animating) { +// title.slideDown(slideOpts); +// animating=true; +// } +// }); + } else { + navtree = $("#nav-tree"); + $(".side-nav-resizable").resizable({resize: function(e, ui) { resizeWidth(); } }); + $(sidenav).resizable({ minWidth: 0 }); + } + $(window).resize(function() { resizeHeight(treeview); }); + if (treeview) + { + const device = navigator.userAgent.toLowerCase(); + const touch_device = device.match(/(iphone|ipod|ipad|android)/); + if (touch_device) { /* wider split bar for touch only devices */ + $(sidenav).css({ paddingRight:'20px' }); + $('.ui-resizable-e').css({ width:'20px' }); + $('#nav-sync').css({ right:'34px' }); + barWidth=20; + } + const width = Cookie.readSetting(RESIZE_COOKIE_NAME,250); + if (width) { restoreWidth(width); } else { resizeWidth(); } + } + resizeHeight(treeview); + const url = location.href; + const i=url.indexOf("#"); + if (i>=0) window.location.hash=url.substr(i); + const _preventDefault = function(evt) { evt.preventDefault(); }; + if (treeview) + { + $("#splitbar").bind("dragstart", _preventDefault).bind("selectstart", _preventDefault); + $(".ui-resizable-handle").dblclick(collapseExpand); + // workaround for firefox + $("body").css({overflow: "hidden"}); + } + $(window).on('load',function() { resizeHeight(treeview); }); +} +/* @license-end */ diff --git a/splitbar.png b/splitbar.png new file mode 100644 index 0000000000000000000000000000000000000000..fe895f2c58179b471a22d8320b39a4bd7312ec8e GIT binary patch literal 314 zcmeAS@N?(olHy`uVBq!ia0vp^Yzz!63>-{AmhX=Jf(#6djGiuzAr*{o?=JLmPLyc> z_*`QK&+BH@jWrYJ7>r6%keRM@)Qyv8R=enp0jiI>aWlGyB58O zFVR20d+y`K7vDw(hJF3;>dD*3-?v=<8M)@x|EEGLnJsniYK!2U1 Y!`|5biEc?d1`HDhPgg&ebxsLQ02F6;9RL6T literal 0 HcmV?d00001 diff --git a/splitbard.png b/splitbard.png new file mode 100644 index 0000000000000000000000000000000000000000..8367416d757fd7b6dc4272b6432dc75a75abd068 GIT binary patch literal 282 zcmeAS@N?(olHy`uVBq!ia0vp^Yzz!63>-{AmhX=Jf@VhhFKy35^fiT zT~&lUj3=cDh^%3HDY9k5CEku}PHXNoNC(_$U3XPb&Q*ME25pT;2(*BOgAf<+R$lzakPG`kF31()Fx{L5Wrac|GQzjeE= zueY1`Ze{#x<8=S|`~MgGetGce)#vN&|J{Cd^tS%;tBYTo?+^d68<#n_Y_xx`J||4O V@QB{^CqU0Kc)I$ztaD0e0svEzbJzd? literal 0 HcmV?d00001 diff --git a/structjwt__key__t.html b/structjwt__key__t.html new file mode 100644 index 00000000..0d2901be --- /dev/null +++ b/structjwt__key__t.html @@ -0,0 +1,137 @@ + + + + + + + +libjwt-2.1.1: jwt_key_t Struct Reference + + + + + + + + + + + + +
    +
    + + + + + + +
    +
    libjwt-2.1.1 +
    +
    +
    + + + + + + + +
    +
    + +
    +
    +
    + +
    +
    + +
    jwt_key_t Struct Reference
    +
    +
    + +

    Structure used by key provider to return a key. + More...

    + +

    #include <jwt.h>

    + + + + + + +

    +Data Fields

    const unsigned char * jwt_key
     
    int jwt_key_len
     
    +

    Detailed Description

    +

    Structure used by key provider to return a key.

    + +

    Definition at line 95 of file jwt.h.

    +

    Field Documentation

    + +

    ◆ jwt_key

    + +
    +
    + + + + +
    const unsigned char* jwt_key_t::jwt_key
    +
    + +

    Definition at line 96 of file jwt.h.

    + +
    +
    + +

    ◆ jwt_key_len

    + +
    +
    + + + + +
    int jwt_key_t::jwt_key_len
    +
    + +

    Definition at line 97 of file jwt.h.

    + +
    +
    +
    The documentation for this struct was generated from the following file: +
    +
    + + + + diff --git a/structjwt__key__t.js b/structjwt__key__t.js new file mode 100644 index 00000000..df3efebb --- /dev/null +++ b/structjwt__key__t.js @@ -0,0 +1,5 @@ +var structjwt__key__t = +[ + [ "jwt_key", "structjwt__key__t.html#a886e24bcf6336a1cb046c750df17da75", null ], + [ "jwt_key_len", "structjwt__key__t.html#aa315a71039529b20f3a855092fc7c914", null ] +]; \ No newline at end of file diff --git a/sync_off.png b/sync_off.png new file mode 100644 index 0000000000000000000000000000000000000000..3b443fc62892114406e3d399421b2a881b897acc GIT binary patch literal 853 zcmV-b1FHOqP)oT|#XixUYy%lpuf3i8{fX!o zUyDD0jOrAiT^tq>fLSOOABs-#u{dV^F$b{L9&!2=9&RmV;;8s^x&UqB$PCj4FdKbh zoB1WTskPUPu05XzFbA}=KZ-GP1fPpAfSs>6AHb12UlR%-i&uOlTpFNS7{jm@mkU1V zh`nrXr~+^lsV-s1dkZOaI|kYyVj3WBpPCY{n~yd%u%e+d=f%`N0FItMPtdgBb@py; zq@v6NVArhyTC7)ULw-Jy8y42S1~4n(3LkrW8mW(F-4oXUP3E`e#g**YyqI7h-J2zK zK{m9##m4ri!7N>CqQqCcnI3hqo1I;Yh&QLNY4T`*ptiQGozK>FF$!$+84Z`xwmeMh zJ0WT+OH$WYFALEaGj2_l+#DC3t7_S`vHpSivNeFbP6+r50cO8iu)`7i%Z4BTPh@_m3Tk!nAm^)5Bqnr%Ov|Baunj#&RPtRuK& z4RGz|D5HNrW83-#ydk}tVKJrNmyYt-sTxLGlJY5nc&Re zU4SgHNPx8~Yxwr$bsju?4q&%T1874xxzq+_%?h8_ofw~(bld=o3iC)LUNR*BY%c0y zWd_jX{Y8`l%z+ol1$@Qa?Cy!(0CVIEeYpKZ`(9{z>3$CIe;pJDQk$m3p}$>xBm4lb zKo{4S)`wdU9Ba9jJbVJ0C=SOefZe%d$8=2r={nu<_^a3~>c#t_U6dye5)JrR(_a^E f@}b6j1K9lwFJq@>o)+Ry00000NkvXXu0mjfWa5j* literal 0 HcmV?d00001 diff --git a/sync_on.png b/sync_on.png new file mode 100644 index 0000000000000000000000000000000000000000..e08320fb64e6fa33b573005ed6d8fe294e19db76 GIT binary patch literal 845 zcmV-T1G4;yP)Y;xxyHF2B5Wzm| zOOGupOTn@c(JmBOl)e;XMNnZuiTJP>rM8<|Q`7I_))aP?*T)ow&n59{}X4$3Goat zgjs?*aasfbrokzG5cT4K=uG`E14xZl@z)F={P0Y^?$4t z>v!teRnNZym<6h{7sLyF1V0HsfEl+l6TrZpsfr1}luH~F7L}ktXu|*uVX^RG$L0`K zWs3j|0tIvVe(N%_?2{(iCPFGf#B6Hjy6o&}D$A%W%jfO8_W%ZO#-mh}EM$LMn7joJ z05dHr!5Y92g+31l<%i1(=L1a1pXX+OYnalY>31V4K}BjyRe3)9n#;-cCVRD_IG1fT zOKGeNY8q;TL@K{dj@D^scf&VCs*-Jb>8b>|`b*osv52-!A?BpbYtTQBns5EAU**$m zSnVSm(teh>tQi*S*A>#ySc=n;`BHz`DuG4&g4Kf8lLhca+zvZ7t7RflD6-i-mcK=M z!=^P$*u2)bkY5asG4gsss!Hn%u~>}kIW`vMs%lJLH+u*9<4PaV_c6U`KqWXQH%+Nu zTv41O(^ZVi@qhjQdG!fbZw&y+2o!iYymO^?ud3{P*HdoX83YV*Uu_HB=?U&W9%AU# z80}k1SS-CXTU7dcQlsm<^oYLxVSseqY6NO}dc`Nj?8vrhNuCdm@^{a3AQ_>6myOj+ z`1RsLUXF|dm|3k7s2jD(B{rzE>WI2scH8i1;=O5Cc9xB3^aJk%fQjqsu+kH#0=_5a z0nCE8@dbQa-|YIuUVvG0L_IwHMEhOj$Mj4Uq05 X8=0q~qBNan00000NkvXXu0mjfptF>5 literal 0 HcmV?d00001 diff --git a/tab_a.png b/tab_a.png new file mode 100644 index 0000000000000000000000000000000000000000..3b725c41c5a527a3a3e40097077d0e206a681247 GIT binary patch literal 142 zcmeAS@N?(olHy`uVBq!ia0vp^j6kfy!2~3aiye;!QlXwMjv*C{Z|8b*H5dputLHD# z=<0|*y7z(Vor?d;H&?EG&cXR}?!j-Lm&u1OOI7AIF5&c)RFE;&p0MYK>*Kl@eiymD r@|NpwKX@^z+;{u_Z~trSBfrMKa%3`zocFjEXaR$#tDnm{r-UW|TZ1%4 literal 0 HcmV?d00001 diff --git a/tab_ad.png b/tab_ad.png new file mode 100644 index 0000000000000000000000000000000000000000..e34850acfc24be58da6d2fd1ccc6b29cc84fe34d GIT binary patch literal 135 zcmeAS@N?(olHy`uVBq!ia0vp^j6kfy!2~3aiye;!QhuH;jv*C{Z|5d*H3V=pKi{In zd2jxLclDRPylmD}^l7{QOtL{vUjO{-WqItb5sQp2h-99b8^^Scr-=2mblCdZuUm?4 jzOJvgvt3{(cjKLW5(A@0qPS@<&}0TrS3j3^P6y&q2{!U5bk+Tso_B!YCpDh>v z{CM*1U8YvQRyBUHt^Ju0W_sq-?;9@_4equ-bavTs=gk796zopr0EBT&m;e9( literal 0 HcmV?d00001 diff --git a/tab_s.png b/tab_s.png new file mode 100644 index 0000000000000000000000000000000000000000..ab478c95b67371d700a20869f7de1ddd73522d50 GIT binary patch literal 184 zcmeAS@N?(olHy`uVBq!ia0vp^j6kfy!2~3aiye;!QuUrLjv*C{Z|^p8HaRdjTwH7) zC?wLlL}}I{)n%R&r+1}IGmDnq;&J#%V6)9VsYhS`O^BVBQlxOUep0c$RENLq#g8A$ z)z7%K_bI&n@J+X_=x}fJoEKed-$<>=ZI-;YrdjIl`U`uzuDWSP?o#Dmo{%SgM#oan kX~E1%D-|#H#QbHoIja2U-MgvsK&LQxy85}Sb4q9e0Efg%P5=M^ literal 0 HcmV?d00001 diff --git a/tab_sd.png b/tab_sd.png new file mode 100644 index 0000000000000000000000000000000000000000..757a565ced4730f85c833fb2547d8e199ae68f19 GIT binary patch literal 188 zcmeAS@N?(olHy`uVBq!ia0vp^j6kfy!2~3aiye;!Qq7(&jv*C{Z|_!fH5o7*c=%9% zcILh!EA=pAQKdx-Cdiev=v{eg{8Ht<{e8_NAN~b=)%W>-WDCE0PyDHGemi$BoXwcK z{>e9^za6*c1ilttWw&V+U;WCPlV9{LdC~Ey%_H(qj`xgfES(4Yz5jSTZfCt`4E$0YRsR*S^mTCR^;V&sxC8{l_Cp7w8-YPgg&ebxsLQ00$vXK>z>% literal 0 HcmV?d00001 diff --git a/tabs.css b/tabs.css new file mode 100644 index 00000000..7fa4268a --- /dev/null +++ b/tabs.css @@ -0,0 +1 @@ +.sm{position:relative;z-index:9999}.sm,.sm ul,.sm li{display:block;list-style:none;margin:0;padding:0;line-height:normal;direction:ltr;text-align:left;-webkit-tap-highlight-color:rgba(0,0,0,0)}.sm-rtl,.sm-rtl ul,.sm-rtl li{direction:rtl;text-align:right}.sm>li>h1,.sm>li>h2,.sm>li>h3,.sm>li>h4,.sm>li>h5,.sm>li>h6{margin:0;padding:0}.sm ul{display:none}.sm li,.sm a{position:relative}.sm a{display:block}.sm a.disabled{cursor:not-allowed}.sm:after{content:"\00a0";display:block;height:0;font:0/0 serif;clear:both;visibility:hidden;overflow:hidden}.sm,.sm *,.sm *:before,.sm *:after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.main-menu-btn{position:relative;display:inline-block;width:36px;height:36px;text-indent:36px;margin-left:8px;white-space:nowrap;overflow:hidden;cursor:pointer;-webkit-tap-highlight-color:rgba(0,0,0,0)}.main-menu-btn-icon,.main-menu-btn-icon:before,.main-menu-btn-icon:after{position:absolute;top:50%;left:2px;height:2px;width:24px;background:var(--nav-menu-button-color);-webkit-transition:all .25s;transition:all .25s}.main-menu-btn-icon:before{content:'';top:-7px;left:0}.main-menu-btn-icon:after{content:'';top:7px;left:0}#main-menu-state:checked ~ .main-menu-btn .main-menu-btn-icon{height:0}#main-menu-state:checked ~ .main-menu-btn .main-menu-btn-icon:before{top:0;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}#main-menu-state:checked ~ .main-menu-btn .main-menu-btn-icon:after{top:0;-webkit-transform:rotate(45deg);transform:rotate(45deg)}#main-menu-state{position:absolute;width:1px;height:1px;margin:-1px;border:0;padding:0;overflow:hidden;clip:rect(1px,1px,1px,1px)}#main-menu-state:not(:checked) ~ #main-menu{display:none}#main-menu-state:checked ~ #main-menu{display:block}@media(min-width:768px){.main-menu-btn{position:absolute;top:-99999px}#main-menu-state:not(:checked) ~ #main-menu{display:block}}.sm-dox{background-image:var(--nav-gradient-image)}.sm-dox a,.sm-dox a:focus,.sm-dox a:hover,.sm-dox a:active{padding:0 12px;padding-right:43px;font-family:var(--font-family-nav);font-size:13px;font-weight:bold;line-height:36px;text-decoration:none;text-shadow:var(--nav-text-normal-shadow);color:var(--nav-text-normal-color);outline:0}.sm-dox a:hover{background-image:var(--nav-gradient-active-image);background-repeat:repeat-x;color:var(--nav-text-hover-color);text-shadow:var(--nav-text-hover-shadow)}.sm-dox a.current{color:#d23600}.sm-dox a.disabled{color:#bbb}.sm-dox a span.sub-arrow{position:absolute;top:50%;margin-top:-14px;left:auto;right:3px;width:28px;height:28px;overflow:hidden;font:bold 12px/28px monospace !important;text-align:center;text-shadow:none;background:var(--nav-menu-toggle-color);-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.sm-dox a span.sub-arrow:before{display:block;content:'+'}.sm-dox a.highlighted span.sub-arrow:before{display:block;content:'-'}.sm-dox>li:first-child>a,.sm-dox>li:first-child>:not(ul) a{-moz-border-radius:5px 5px 0 0;-webkit-border-radius:5px;border-radius:5px 5px 0 0}.sm-dox>li:last-child>a,.sm-dox>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul{-moz-border-radius:0 0 5px 5px;-webkit-border-radius:0;border-radius:0 0 5px 5px}.sm-dox>li:last-child>a.highlighted,.sm-dox>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a.highlighted{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0}.sm-dox ul{background:var(--nav-menu-background-color)}.sm-dox ul a,.sm-dox ul a:focus,.sm-dox ul a:hover,.sm-dox ul a:active{font-size:12px;border-left:8px solid transparent;line-height:36px;text-shadow:none;background-color:var(--nav-menu-background-color);background-image:none}.sm-dox ul a:hover{background-image:var(--nav-gradient-active-image);background-repeat:repeat-x;color:var(--nav-text-hover-color);text-shadow:0 1px 1px black}.sm-dox ul ul a,.sm-dox ul ul a:hover,.sm-dox ul ul a:focus,.sm-dox ul ul a:active{border-left:16px solid transparent}.sm-dox ul ul ul a,.sm-dox ul ul ul a:hover,.sm-dox ul ul ul a:focus,.sm-dox ul ul ul a:active{border-left:24px solid transparent}.sm-dox ul ul ul ul a,.sm-dox ul ul ul ul a:hover,.sm-dox ul ul ul ul a:focus,.sm-dox ul ul ul ul a:active{border-left:32px solid transparent}.sm-dox ul ul ul ul ul a,.sm-dox ul ul ul ul ul a:hover,.sm-dox ul ul ul ul ul a:focus,.sm-dox ul ul ul ul ul a:active{border-left:40px solid transparent}@media(min-width:768px){.sm-dox ul{position:absolute;width:12em}.sm-dox li{float:left}.sm-dox.sm-rtl li{float:right}.sm-dox ul li,.sm-dox.sm-rtl ul li,.sm-dox.sm-vertical li{float:none}.sm-dox a{white-space:nowrap}.sm-dox ul a,.sm-dox.sm-vertical a{white-space:normal}.sm-dox .sm-nowrap>li>a,.sm-dox .sm-nowrap>li>:not(ul) a{white-space:nowrap}.sm-dox{padding:0 10px;background-image:var(--nav-gradient-image);line-height:36px}.sm-dox a span.sub-arrow{top:50%;margin-top:-2px;right:12px;width:0;height:0;border-width:4px;border-style:solid dashed dashed dashed;border-color:var(--nav-text-normal-color) transparent transparent transparent;background:transparent;-moz-border-radius:0;-webkit-border-radius:0;border-radius:0}.sm-dox a,.sm-dox a:focus,.sm-dox a:active,.sm-dox a:hover,.sm-dox a.highlighted{padding:0 12px;background-image:var(--nav-separator-image);background-repeat:no-repeat;background-position:right;-moz-border-radius:0 !important;-webkit-border-radius:0;border-radius:0 !important}.sm-dox a:hover{background-image:var(--nav-gradient-active-image);background-repeat:repeat-x;color:var(--nav-text-hover-color);text-shadow:var(--nav-text-hover-shadow)}.sm-dox a:hover span.sub-arrow{border-color:var(--nav-text-hover-color) transparent transparent transparent}.sm-dox a.has-submenu{padding-right:24px}.sm-dox li{border-top:0}.sm-dox>li>ul:before,.sm-dox>li>ul:after{content:'';position:absolute;top:-18px;left:30px;width:0;height:0;overflow:hidden;border-width:9px;border-style:dashed dashed solid dashed;border-color:transparent transparent #bbb transparent}.sm-dox>li>ul:after{top:-16px;left:31px;border-width:8px;border-color:transparent transparent var(--nav-menu-background-color) transparent}.sm-dox ul{border:1px solid #bbb;padding:5px 0;background:var(--nav-menu-background-color);-moz-border-radius:5px !important;-webkit-border-radius:5px;border-radius:5px !important;-moz-box-shadow:0 5px 9px rgba(0,0,0,0.2);-webkit-box-shadow:0 5px 9px rgba(0,0,0,0.2);box-shadow:0 5px 9px rgba(0,0,0,0.2)}.sm-dox ul a span.sub-arrow{right:8px;top:50%;margin-top:-5px;border-width:5px;border-color:transparent transparent transparent var(--nav-menu-foreground-color);border-style:dashed dashed dashed solid}.sm-dox ul a,.sm-dox ul a:hover,.sm-dox ul a:focus,.sm-dox ul a:active,.sm-dox ul a.highlighted{color:var(--nav-menu-foreground-color);background-image:none;border:0 !important}.sm-dox ul a:hover{background-image:var(--nav-gradient-active-image);background-repeat:repeat-x;color:var(--nav-text-hover-color);text-shadow:var(--nav-text-hover-shadow)}.sm-dox ul a:hover span.sub-arrow{border-color:transparent transparent transparent var(--nav-text-hover-color)}.sm-dox span.scroll-up,.sm-dox span.scroll-down{position:absolute;display:none;visibility:hidden;overflow:hidden;background:var(--nav-menu-background-color);height:36px}.sm-dox span.scroll-up:hover,.sm-dox span.scroll-down:hover{background:#eee}.sm-dox span.scroll-up:hover span.scroll-up-arrow,.sm-dox span.scroll-up:hover span.scroll-down-arrow{border-color:transparent transparent #d23600 transparent}.sm-dox span.scroll-down:hover span.scroll-down-arrow{border-color:#d23600 transparent transparent transparent}.sm-dox span.scroll-up-arrow,.sm-dox span.scroll-down-arrow{position:absolute;top:0;left:50%;margin-left:-6px;width:0;height:0;overflow:hidden;border-width:6px;border-style:dashed dashed solid dashed;border-color:transparent transparent var(--nav-menu-foreground-color) transparent}.sm-dox span.scroll-down-arrow{top:8px;border-style:solid dashed dashed dashed;border-color:var(--nav-menu-foreground-color) transparent transparent transparent}.sm-dox.sm-rtl a.has-submenu{padding-right:12px;padding-left:24px}.sm-dox.sm-rtl a span.sub-arrow{right:auto;left:12px}.sm-dox.sm-rtl.sm-vertical a.has-submenu{padding:10px 20px}.sm-dox.sm-rtl.sm-vertical a span.sub-arrow{right:auto;left:8px;border-style:dashed solid dashed dashed;border-color:transparent #555 transparent transparent}.sm-dox.sm-rtl>li>ul:before{left:auto;right:30px}.sm-dox.sm-rtl>li>ul:after{left:auto;right:31px}.sm-dox.sm-rtl ul a.has-submenu{padding:10px 20px !important}.sm-dox.sm-rtl ul a span.sub-arrow{right:auto;left:8px;border-style:dashed solid dashed dashed;border-color:transparent #555 transparent transparent}.sm-dox.sm-vertical{padding:10px 0;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.sm-dox.sm-vertical a{padding:10px 20px}.sm-dox.sm-vertical a:hover,.sm-dox.sm-vertical a:focus,.sm-dox.sm-vertical a:active,.sm-dox.sm-vertical a.highlighted{background:#fff}.sm-dox.sm-vertical a.disabled{background-image:var(--nav-gradient-image)}.sm-dox.sm-vertical a span.sub-arrow{right:8px;top:50%;margin-top:-5px;border-width:5px;border-style:dashed dashed dashed solid;border-color:transparent transparent transparent #555}.sm-dox.sm-vertical>li>ul:before,.sm-dox.sm-vertical>li>ul:after{display:none}.sm-dox.sm-vertical ul a{padding:10px 20px}.sm-dox.sm-vertical ul a:hover,.sm-dox.sm-vertical ul a:focus,.sm-dox.sm-vertical ul a:active,.sm-dox.sm-vertical ul a.highlighted{background:#eee}.sm-dox.sm-vertical ul a.disabled{background:var(--nav-menu-background-color)}} diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 00000000..3496efdf --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,17 @@ +jwt_grant +jwt_new +jwt_dump +jwt_encode +jwt_rsa +jwt_rsa_pss +jwt_ec +jwt_header +jwt_validate +jwt_eddsa +jwt_crypto +jwt_es256k +*.log +*.trs +libjwt-*-coverage.info +libjwt-*-coverage +libjwt-*-coverage/* diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 00000000..daef45d5 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,23 @@ +find_package (Check REQUIRED) + +enable_testing () + +# Add the check target to behave like automake +add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND}) + +set (TARGET_NAMES jwt_dump jwt_ec jwt_encode jwt_grant jwt_header jwt_new jwt_rsa jwt_validate) + +if (UNIX) + set (PLATFORM_LIBRARIES pthread) +endif () + +foreach (TARGET_NAME ${TARGET_NAMES}) + add_executable (${TARGET_NAME} ${TARGET_NAME}.c) + target_include_directories (${TARGET_NAME} PRIVATE ${CHECK_INCLUDE_DIR}) + if (UNIX AND ENABLE_LTO) + set_property(TARGET ${TARGET_NAME} PROPERTY INTERPROCEDURAL_OPTIMIZATION True) + endif () + target_compile_definitions (${TARGET_NAME} PRIVATE KEYDIR=\"${CMAKE_CURRENT_SOURCE_DIR}/keys\" _GNU_SOURCE) + target_link_libraries (${TARGET_NAME} jwt ${CHECK_LIBRARIES} ${PLATFORM_LIBRARIES}) + add_test (${TARGET_NAME} ${TARGET_NAME}) +endforeach () diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 00000000..eae439b3 --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,26 @@ +EXTRA_DIST = CMakeLists.txt keys jwt_tests.h + +TESTS = \ + jwt_new \ + jwt_grant \ + jwt_header \ + jwt_dump \ + jwt_encode \ + jwt_rsa \ + jwt_ec \ + jwt_validate \ + jwt_rsa_pss \ + jwt_eddsa \ + jwt_crypto \ + jwt_es256k + +check_PROGRAMS = $(TESTS) + +AM_CPPFLAGS = -I$(top_srcdir)/include +AM_CFLAGS = -Wall $(CHECK_CFLAGS) -DKEYDIR="\"$(srcdir)/keys\"" -D_GNU_SOURCE +AM_LDFLAGS = -L$(top_builddir)/libjwt +LDADD = -ljwt $(CHECK_LIBS) + +CODE_COVERAGE_IGNORE_PATTERN = 'cencode.*' 'cdecode.*' +@CODE_COVERAGE_RULES@ +@VALGRIND_CHECK_RULES@ diff --git a/tests/jwt_crypto.c b/tests/jwt_crypto.c new file mode 100644 index 00000000..8d04188e --- /dev/null +++ b/tests/jwt_crypto.c @@ -0,0 +1,51 @@ +/* Public domain, no copyright. Use at your own risk. */ + +#include +#include +#include +#include +#include + +#include "jwt_tests.h" + +START_TEST(test_jwt_ops) +{ + int i; + + for (i = 0; jwt_test_ops[i] != NULL; i++) { + const char *name = jwt_test_ops[i]; + const char *test; + + ck_assert(!jwt_set_crypto_ops(name)); + + test = jwt_get_crypto_ops(); + ck_assert_str_eq(test, name); + } + + /* Assert that this fails */ + ck_assert(jwt_set_crypto_ops("ALWAYS FAIL")); +} +END_TEST + +static Suite *libjwt_suite(const char *title) +{ + Suite *s; + TCase *tc_core; + + s = suite_create(title); + + tc_core = tcase_create("jwt_crypto"); + + tcase_add_test(tc_core, test_jwt_ops); + + tcase_set_timeout(tc_core, 30); + + suite_add_tcase(s, tc_core); + + return s; +} + +int main(int argc, char *argv[]) +{ + JWT_TEST_MAIN("LibJWT Crypto Operations"); +} diff --git a/tests/jwt_dump.c b/tests/jwt_dump.c new file mode 100644 index 00000000..4eeac893 --- /dev/null +++ b/tests/jwt_dump.c @@ -0,0 +1,386 @@ +/* Public domain, no copyright. Use at your own risk. */ + +#include +#include +#include +#include +#include + +#include "jwt_tests.h" + +static void *test_malloc(size_t size) +{ + return malloc(size); +} + +static void test_free(void *ptr) +{ + free(ptr); +} + +static void *test_realloc(void *ptr, size_t size) +{ + return realloc(ptr, size); +} + +static int test_set_alloc(void) +{ + return jwt_set_alloc(test_malloc, test_realloc, test_free); +} + +START_TEST(test_alloc_funcs) +{ + jwt_malloc_t m = NULL; + jwt_realloc_t r = NULL; + jwt_free_t f = NULL; + int ret; + + SET_OPS(); + + jwt_get_alloc(&m, &r, &f); + ck_assert_ptr_null(m); + ck_assert_ptr_null(r); + ck_assert_ptr_null(f); + + ret = test_set_alloc(); + ck_assert_int_eq(ret, 0); + + jwt_get_alloc(&m, &r, &f); + ck_assert(m == test_malloc); + ck_assert(r == test_realloc); + ck_assert(f == test_free); +} +END_TEST + +START_TEST(test_jwt_dump_fp) +{ + FILE *out; + jwt_t *jwt = NULL; + int ret = 0; + + SET_OPS(); + + ret = test_set_alloc(); + ck_assert_int_eq(ret, 0); + + ret = jwt_new(&jwt); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + ret = jwt_add_grant(jwt, "iss", "files.maclara-llc.com"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "sub", "user0"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant_int(jwt, "iat", (long)time(NULL)); + ck_assert_int_eq(ret, 0); + +#ifdef _WIN32 + out = fopen("nul", "w"); +#else + out = fopen("/dev/null", "w"); +#endif + ck_assert_ptr_ne(out, NULL); + + ret = jwt_dump_fp(jwt, out, 1); + ck_assert_int_eq(ret, 0); + + ret = jwt_dump_fp(jwt, out, 0); + ck_assert_int_eq(ret, 0); + + fclose(out); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_dump_str) +{ + jwt_t *jwt = NULL; + int ret = 0; + char *out; + const char *val = NULL; + + SET_OPS(); + + ret = test_set_alloc(); + ck_assert_int_eq(ret, 0); + + ret = jwt_new(&jwt); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + ret = jwt_add_grant(jwt, "iss", "files.maclara-llc.com"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "sub", "user0"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant_int(jwt, "iat", (long)time(NULL)); + ck_assert_int_eq(ret, 0); + + /* Test 'typ' header: should not be present, cause 'alg' is JWT_ALG_NONE. */ + val = jwt_get_header(jwt, "typ"); + ck_assert_ptr_null(val); + + out = jwt_dump_str(jwt, 1); + ck_assert_ptr_nonnull(out); + + /* Test 'typ' header: should not be present, cause 'alg' is JWT_ALG_NONE. */ + val = jwt_get_header(jwt, "typ"); + ck_assert_ptr_null(val); + + jwt_free_str(out); + + out = jwt_dump_str(jwt, 0); + ck_assert_ptr_nonnull(out); + + /* Test 'typ' header: should not be present, cause 'alg' is JWT_ALG_NONE. */ + val = jwt_get_header(jwt, "typ"); + ck_assert_ptr_null(val); + + jwt_free_str(out); + + jwt_free(jwt); +} +END_TEST + +#define JSON_GRANTS_PRETTY "\n{\n" \ + " \"%s\": %ld,\n" \ + " \"%s\": \"%s\",\n" \ + " \"%s\": \"%s\",\n" \ + " \"%s\": \"%s\"\n" \ + "}\n" + +#define JSON_GRANTS_COMPACT "{\"%s\":%ld,\"%s\":\"%s\"," \ + "\"%s\":\"%s\",\"%s\":\"%s\"}" + + +START_TEST(test_jwt_dump_grants_str) +{ + jwt_t *jwt = NULL; + int ret = 0; + char *out; + long timestamp = (long)time(NULL); + char buf[1024]; + + SET_OPS(); + + ret = test_set_alloc(); + ck_assert_int_eq(ret, 0); + + ret = jwt_new(&jwt); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + ret = jwt_add_grant(jwt, "iss", "files.maclara-llc.com"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "sub", "user0"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant_int(jwt, "iat", timestamp); + ck_assert_int_eq(ret, 0); + + out = jwt_dump_grants_str(jwt, 1); + ck_assert_ptr_nonnull(out); + + /* Sorted Keys are expected */ + snprintf(buf, sizeof(buf), JSON_GRANTS_PRETTY, + "iat", timestamp, + "iss", "files.maclara-llc.com", + "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC", + "sub", "user0"); + ck_assert_str_eq(out, buf); + + jwt_free_str(out); + + out = jwt_dump_grants_str(jwt, 0); + ck_assert_ptr_nonnull(out); + + /* Sorted Keys are expected */ + snprintf(buf, sizeof(buf), JSON_GRANTS_COMPACT, + "iat", timestamp, + "iss", "files.maclara-llc.com", + "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC", + "sub", "user0"); + ck_assert_str_eq(out, buf); + + jwt_free_str(out); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_dump_str_alg_default_typ_header) +{ + jwt_t *jwt = NULL; + const char key[] = "My Passphrase"; + int ret = 0; + char *out; + const char *val = NULL; + + SET_OPS(); + + ret = test_set_alloc(); + ck_assert_int_eq(ret, 0); + + ret = jwt_new(&jwt); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + ret = jwt_add_grant(jwt, "iss", "files.maclara-llc.com"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "sub", "user0"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant_int(jwt, "iat", (long)time(NULL)); + ck_assert_int_eq(ret, 0); + + ret = jwt_set_alg(jwt, JWT_ALG_HS256, (unsigned char *)key, + strlen(key)); + ck_assert_int_eq(ret, 0); + + /* Test 'typ' header: should not be present, cause jwt's header has not been touched yet + * by jwt_write_head, this is only called as a result of calling jwt_dump* methods. */ + val = jwt_get_header(jwt, "typ"); + ck_assert_ptr_null(val); + + out = jwt_dump_str(jwt, 1); + ck_assert_ptr_nonnull(out); + + /* Test 'typ' header: should be added with default value of 'JWT', cause 'alg' is set explicitly + * and jwt's header has been processed by jwt_write_head. */ + val = jwt_get_header(jwt, "typ"); + ck_assert_ptr_nonnull(val); + ck_assert_str_eq(val, "JWT"); + + jwt_free_str(out); + + out = jwt_dump_str(jwt, 0); + ck_assert_ptr_nonnull(out); + + /* Test 'typ' header: should be added with default value of 'JWT', cause 'alg' is set explicitly + * and jwt's header has been processed by jwt_write_head. */ + val = jwt_get_header(jwt, "typ"); + ck_assert_ptr_nonnull(val); + ck_assert_str_eq(val, "JWT"); + + jwt_free_str(out); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_dump_str_alg_custom_typ_header) +{ + jwt_t *jwt = NULL; + const char key[] = "My Passphrase"; + int ret = 0; + char *out; + const char *val = NULL; + + SET_OPS(); + + ret = test_set_alloc(); + ck_assert_int_eq(ret, 0); + + ret = jwt_new(&jwt); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + ret = jwt_add_grant(jwt, "iss", "files.maclara-llc.com"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "sub", "user0"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant_int(jwt, "iat", (long)time(NULL)); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_header(jwt, "typ", "favourite"); + ck_assert_int_eq(ret, 0); + + /* Test that 'typ' header has been added. */ + val = jwt_get_header(jwt, "typ"); + ck_assert_ptr_nonnull(val); + ck_assert_str_eq(val, "favourite"); + + ret = jwt_set_alg(jwt, JWT_ALG_HS256, (unsigned char *)key, + strlen(key)); + ck_assert_int_eq(ret, 0); + + /* Test 'typ' header: should be left untouched. */ + val = jwt_get_header(jwt, "typ"); + ck_assert_ptr_nonnull(val); + ck_assert_str_eq(val, "favourite"); + + out = jwt_dump_str(jwt, 1); + ck_assert_ptr_nonnull(out); + + /* Test 'typ' header: should be left untouched. */ + val = jwt_get_header(jwt, "typ"); + ck_assert_ptr_nonnull(val); + ck_assert_str_eq(val, "favourite"); + + jwt_free_str(out); + + out = jwt_dump_str(jwt, 0); + ck_assert_ptr_nonnull(out); + + /* Test 'typ' header: should be left untouched. */ + val = jwt_get_header(jwt, "typ"); + ck_assert_ptr_nonnull(val); + ck_assert_str_eq(val, "favourite"); + + jwt_free_str(out); + + jwt_free(jwt); +} +END_TEST + +static Suite *libjwt_suite(const char *title) +{ + Suite *s; + TCase *tc_core; + int i = ARRAY_SIZE(jwt_test_ops) - 1; + + s = suite_create(title); + + tc_core = tcase_create("jwt_dump"); + + tcase_add_loop_test(tc_core, test_alloc_funcs, 0, i); + tcase_add_loop_test(tc_core, test_jwt_dump_fp, 0, i); + tcase_add_loop_test(tc_core, test_jwt_dump_str, 0, i); + tcase_add_loop_test(tc_core, test_jwt_dump_grants_str, 0, i); + tcase_add_loop_test(tc_core, test_jwt_dump_str_alg_default_typ_header, 0, i); + tcase_add_loop_test(tc_core, test_jwt_dump_str_alg_custom_typ_header, 0, i); + + tcase_set_timeout(tc_core, 30); + + suite_add_tcase(s, tc_core); + + return s; +} + +int main(int argc, char *argv[]) +{ + JWT_TEST_MAIN("LibJWT Dump"); +} diff --git a/tests/jwt_ec.c b/tests/jwt_ec.c new file mode 100644 index 00000000..cf62a4c9 --- /dev/null +++ b/tests/jwt_ec.c @@ -0,0 +1,241 @@ +/* Public domain, no copyright. Use at your own risk. */ + +#include +#include +#include +#include +#include + +#include "jwt_tests.h" + +/* NOTE: ES signing will generate a different signature every time, so can't + * be simply string compared for verification like we do with RS. */ + +static const char jwt_es256[] = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQ" + "iOjE0NzU5ODA1NDUsImlzcyI6ImZpbGVzLm1hY2xhcmEtbGxjLmNvbSIsInJlZiI6Ilh" + "YWFgtWVlZWS1aWlpaLUFBQUEtQ0NDQyIsInN1YiI6InVzZXIwIn0.IONoUPo6QhHwcx1" + "N1TD4DnrjvmB-9lSX6qrn_WPrh3DBum-qKP66MIF9tgymy7hCoU6dvUW8zKK0AyVH3iD" + "1uA"; + +static const char jwt_es384[] = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzM4NCJ9.eyJpYXQ" + "iOjE0NzU5ODA1NDUsImlzcyI6ImZpbGVzLmN5cGhyZS5jb20iLCJyZWYiOiJYWFhYLVl" + "ZWVktWlpaWi1BQUFBLUNDQ0MiLCJzdWIiOiJ1c2VyMCJ9.p6McjolhuIqel0DWaI2OrD" + "oRYcxgSMnGFirdKT5jXpe9L801HBkouKBJSae8F7LLFUKiE2VVX_514WzkuExLQs2eB1" + "L2Qahid5VFOK3hc7HcBL-rcCXa8d2tf_MudyrM"; + +static const char jwt_es512[] = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzUxMiJ9.eyJpYXQ" + "iOjE0NzU5ODA1NDUsImlzcyI6ImZpbGVzLmN5cGhyZS5jb20iLCJyZWYiOiJYWFhYLVl" + "ZWVktWlpaWi1BQUFBLUNDQ0MiLCJzdWIiOiJ1c2VyMCJ9.Abs-SriTqd9NAO-bJb-B3U" + "zF1W8JmoutfHQpMqJnkPHyasVVuKN-I-6RibSv-qxgTxuzlo0u5dCt4mOw7w8mgEnMAS" + "zsjm-NlOPUBjIUD9T592lse9OOF6TjPOQbijqeMc6qFZ8q5YhxvxBXHO6PuImkJpEWj4" + "Zda8lNTxqHol7vorg9"; + +static const char jwt_es_invalid[] = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJpYXQ" + "iOjE0NzU5ODA1IAmCornholio6ImZpbGVzLmN5cGhyZS5jb20iLCJyZWYiOiJYWFhYLVl" + "PN9G9tV75ylfWvcwkF20bQA9m1vDbUIl8PIK8Q"; + +START_TEST(test_jwt_encode_es256) +{ + SET_OPS(); + __test_alg_key(JWT_ALG_ES256, "ec_key_prime256v1.pem", "ec_key_prime256v1-pub.pem"); +} +END_TEST + +START_TEST(test_jwt_verify_es256) +{ + SET_OPS(); + __verify_jwt(jwt_es256, JWT_ALG_ES256, "ec_key_prime256v1-pub.pem"); +} +END_TEST + +START_TEST(test_jwt_encode_es384) +{ + SET_OPS(); + __test_alg_key(JWT_ALG_ES384, "ec_key_secp384r1.pem", "ec_key_secp384r1-pub.pem"); +} +END_TEST + +START_TEST(test_jwt_verify_es384) +{ + SET_OPS(); + __verify_jwt(jwt_es384, JWT_ALG_ES384, "ec_key_secp384r1-pub.pem"); +} +END_TEST + +START_TEST(test_jwt_encode_es512) +{ + SET_OPS(); + __test_alg_key(JWT_ALG_ES512, "ec_key_secp521r1.pem", "ec_key_secp521r1-pub.pem"); +} +END_TEST + +START_TEST(test_jwt_verify_es512) +{ + SET_OPS(); + __verify_jwt(jwt_es512, JWT_ALG_ES512, "ec_key_secp521r1-pub.pem"); +} +END_TEST + +START_TEST(test_jwt_encode_ec_with_rsa) +{ + jwt_t *jwt = NULL; + int ret = 0; + char *out; + + SET_OPS(); + + ALLOC_JWT(&jwt); + + read_key("rsa_key_4096.pem"); + + ret = jwt_add_grant(jwt, "iss", "files.maclara-llc.com"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "sub", "user0"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant_int(jwt, "iat", TS_CONST); + ck_assert_int_eq(ret, 0); + + ret = jwt_set_alg(jwt, JWT_ALG_ES384, key, key_len); + ck_assert_int_eq(ret, 0); + + out = jwt_encode_str(jwt); + ck_assert_ptr_eq(out, NULL); + ck_assert_int_eq(errno, EINVAL); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_verify_invalid_token) +{ + jwt_t *jwt = NULL; + int ret = 0; + + SET_OPS(); + + read_key("ec_key_secp384r1.pem"); + + ret = jwt_decode(&jwt, jwt_es_invalid, key, JWT_ALG_ES256); + ck_assert_int_ne(ret, 0); + ck_assert_ptr_eq(jwt, NULL); +} +END_TEST + +START_TEST(test_jwt_verify_invalid_alg) +{ + jwt_t *jwt = NULL; + int ret = 0; + + SET_OPS(); + + read_key("ec_key_secp384r1.pem"); + + ret = jwt_decode(&jwt, jwt_es256, key, JWT_ALG_ES512); + ck_assert_int_ne(ret, 0); + ck_assert_ptr_eq(jwt, NULL); +} +END_TEST + +START_TEST(test_jwt_verify_invalid_cert) +{ + jwt_t *jwt = NULL; + int ret = 0; + + SET_OPS(); + + read_key("ec_key_secp521r1-pub.pem"); + + ret = jwt_decode(&jwt, jwt_es256, key, JWT_ALG_ES256); + ck_assert_int_ne(ret, 0); + ck_assert_ptr_eq(jwt, NULL); +} +END_TEST + +START_TEST(test_jwt_verify_invalid_cert_file) +{ + jwt_t *jwt = NULL; + int ret = 0; + + SET_OPS(); + + read_key("ec_key_invalid-pub.pem"); + + ret = jwt_decode(&jwt, jwt_es256, key, JWT_ALG_ES256); + ck_assert_int_ne(ret, 0); + ck_assert_ptr_eq(jwt, NULL); +} +END_TEST + +START_TEST(test_jwt_encode_invalid_key) +{ + jwt_t *jwt = NULL; + int ret = 0; + char *out = NULL; + + SET_OPS(); + + ALLOC_JWT(&jwt); + + read_key("ec_key_invalid.pem"); + + ret = jwt_add_grant(jwt, "iss", "files.maclara-llc.com"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "sub", "user0"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant_int(jwt, "iat", TS_CONST); + ck_assert_int_eq(ret, 0); + + ret = jwt_set_alg(jwt, JWT_ALG_ES512, key, key_len); + ck_assert_int_eq(ret, 0); + + out = jwt_encode_str(jwt); + ck_assert_ptr_eq(out, NULL); + + jwt_free(jwt); +} +END_TEST + +static Suite *libjwt_suite(const char *title) +{ + Suite *s; + TCase *tc_core; + int i = ARRAY_SIZE(jwt_test_ops) - 1; + + s = suite_create(title); + + tc_core = tcase_create("jwt_ec"); + + tcase_add_loop_test(tc_core, test_jwt_encode_es256, 0, i); + tcase_add_loop_test(tc_core, test_jwt_verify_es256, 0, i); + tcase_add_loop_test(tc_core, test_jwt_encode_es384, 0, i); + tcase_add_loop_test(tc_core, test_jwt_verify_es384, 0, i); + tcase_add_loop_test(tc_core, test_jwt_encode_es512, 0, i); + tcase_add_loop_test(tc_core, test_jwt_verify_es512, 0, i); + tcase_add_loop_test(tc_core, test_jwt_encode_ec_with_rsa, 0, i); + tcase_add_loop_test(tc_core, test_jwt_verify_invalid_token, 0, i); + tcase_add_loop_test(tc_core, test_jwt_verify_invalid_alg, 0, i); + tcase_add_loop_test(tc_core, test_jwt_verify_invalid_cert, 0, i); + tcase_add_loop_test(tc_core, test_jwt_verify_invalid_cert_file, 0, i); + tcase_add_loop_test(tc_core, test_jwt_encode_invalid_key, 0, i); + + tcase_set_timeout(tc_core, 30); + + suite_add_tcase(s, tc_core); + + return s; +} + +int main(int argc, char *argv[]) +{ + JWT_TEST_MAIN("LibJWT EC Sign/Verify"); +} diff --git a/tests/jwt_eddsa.c b/tests/jwt_eddsa.c new file mode 100644 index 00000000..d9c1eec4 --- /dev/null +++ b/tests/jwt_eddsa.c @@ -0,0 +1,90 @@ +/* Public domain, no copyright. Use at your own risk. */ + +#include +#include +#include +#include +#include + +#include "jwt_tests.h" + +static const char jwt_eddsa[] = "eyJhbGciOiJFRERTQSIsInR5cCI6IkpXVCJ9.eyJpYX" + "QiOjE0NzU5ODA1NDUsImlzcyI6ImZpbGVzLm1hY2xhcmEtbGxjLmNvbSIsInJlZiI6I" + "lhYWFgtWVlZWS1aWlpaLUFBQUEtQ0NDQyIsInN1YiI6InVzZXIwIn0.19ip2DFFjaZ_" + "UFVCo0OtdwuzSmOYModleJVeFcAjb_4hrAAf0pZSf8O78pivbXLJenEIsaZ9REFOauB" + "eDxbTBw"; + +START_TEST(test_jwt_encode_eddsa) +{ + SET_OPS(); + __test_alg_key(JWT_ALG_EDDSA, "eddsa_key_ed25519.pem", "eddsa_key_ed25519-pub.pem"); +} +END_TEST + +START_TEST(test_jwt_verify_eddsa) +{ + SET_OPS(); + __verify_jwt(jwt_eddsa, JWT_ALG_EDDSA, "eddsa_key_ed25519-pub.pem"); +} +END_TEST + +START_TEST(test_jwt_encode_eddsa_with_rsa) +{ + jwt_t *jwt = NULL; + int ret = 0; + char *out; + + SET_OPS(); + + ALLOC_JWT(&jwt); + + read_key("rsa_key_4096.pem"); + + ret = jwt_add_grant(jwt, "iss", "files.maclara-llc.com"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "sub", "user0"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant_int(jwt, "iat", TS_CONST); + ck_assert_int_eq(ret, 0); + + ret = jwt_set_alg(jwt, JWT_ALG_EDDSA, key, key_len); + ck_assert_int_eq(ret, 0); + + out = jwt_encode_str(jwt); + ck_assert_ptr_eq(out, NULL); + ck_assert_int_eq(errno, EINVAL); + + jwt_free(jwt); +} +END_TEST + +static Suite *libjwt_suite(const char *title) +{ + Suite *s; + TCase *tc_core; + int i = ARRAY_SIZE(jwt_test_ops) - 1; + + s = suite_create(title); + + tc_core = tcase_create("jwt_eddsa"); + + tcase_add_loop_test(tc_core, test_jwt_encode_eddsa, 0, i); + tcase_add_loop_test(tc_core, test_jwt_verify_eddsa, 0, i); + tcase_add_loop_test(tc_core, test_jwt_encode_eddsa_with_rsa, 0, i); + + tcase_set_timeout(tc_core, 30); + + suite_add_tcase(s, tc_core); + + return s; +} + +int main(int argc, char *argv[]) +{ + JWT_TEST_MAIN("LibJWT EdDSA Sign/Verify"); +} diff --git a/tests/jwt_encode.c b/tests/jwt_encode.c new file mode 100644 index 00000000..40f60224 --- /dev/null +++ b/tests/jwt_encode.c @@ -0,0 +1,396 @@ +/* Public domain, no copyright. Use at your own risk. */ + +#include +#include +#include +#include +#include + +#include "jwt_tests.h" + +START_TEST(test_jwt_encode_fp) +{ + FILE *out; + jwt_t *jwt = NULL; + int ret = 0; + + SET_OPS(); + + ALLOC_JWT(&jwt); + + ret = jwt_add_grant(jwt, "iss", "files.maclara-llc.com"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "sub", "user0"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant_int(jwt, "iat", TS_CONST); + ck_assert_int_eq(ret, 0); + + /* TODO Write to actual file and read back to validate output. */ +#ifdef _WIN32 + out = fopen("nul", "w"); +#else + out = fopen("/dev/null", "w"); +#endif + ck_assert_ptr_ne(out, NULL); + + ret = jwt_encode_fp(jwt, out); + ck_assert_int_eq(ret, 0); + + fclose(out); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_encode_str) +{ + const char res[] = "eyJhbGciOiJub25lIn0.eyJpYXQiOjE0NzU5ODA1NDUsIml" + "zcyI6ImZpbGVzLm1hY2xhcmEtbGxjLmNvbSIsInJlZiI6IlhYWFgtWVlZW" + "S1aWlpaLUFBQUEtQ0NDQyIsInN1YiI6InVzZXIwIn0."; + jwt_t *jwt = NULL; + int ret = 0; + char *out; + + SET_OPS(); + + ALLOC_JWT(&jwt); + + ret = jwt_add_grant(jwt, "iss", "files.maclara-llc.com"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "sub", "user0"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant_int(jwt, "iat", TS_CONST); + ck_assert_int_eq(ret, 0); + + out = jwt_encode_str(jwt); + ck_assert_ptr_ne(out, NULL); + + ck_assert_str_eq(out, res); + + jwt_free_str(out); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_encode_alg_none) +{ + const char res[] = "eyJhbGciOiJub25lIn0.eyJhdWQiOiJ3d3cucGx1Z2dlcnM" + "ubmwiLCJleHAiOjE0Nzc1MTQ4MTIsInN1YiI6IlBsdWdnZXJzIFNvZnR3YXJlIn0."; + jwt_t *jwt = NULL; + int ret = 0; + char *out; + + SET_OPS(); + + ALLOC_JWT(&jwt); + + ret = jwt_add_grant(jwt, "aud", "www.pluggers.nl"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant_int(jwt, "exp", 1477514812); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "sub", "Pluggers Software"); + ck_assert_int_eq(ret, 0); + + ret = jwt_set_alg(jwt, JWT_ALG_NONE, NULL, 0); + ck_assert_int_eq(ret, 0); + + out = jwt_encode_str(jwt); + ck_assert_ptr_ne(out, NULL); + + ck_assert_str_eq(out, res); + + jwt_free_str(out); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_encode_hs256) +{ + const char res[] = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOj" + "E0NzU5ODA1NDUsImlzcyI6ImZpbGVzLm1hY2xhcmEtbGxjLmNvbSIsInJl" + "ZiI6IlhYWFgtWVlZWS1aWlpaLUFBQUEtQ0NDQyIsInN1YiI6InVzZXIwIn" + "0.ezbB-vH0KoNU5aA6apcJ1JNorI2HnD8s0yp7zdXt42M"; + unsigned char key256[32] = "012345678901234567890123456789XY"; + jwt_t *jwt = NULL; + int ret = 0; + char *out; + + SET_OPS(); + + ALLOC_JWT(&jwt); + + ret = jwt_add_grant(jwt, "iss", "files.maclara-llc.com"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "sub", "user0"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant_int(jwt, "iat", TS_CONST); + ck_assert_int_eq(ret, 0); + + ret = jwt_set_alg(jwt, JWT_ALG_HS256, key256, sizeof(key256)); + ck_assert_int_eq(ret, 0); + + out = jwt_encode_str(jwt); + ck_assert_ptr_ne(out, NULL); + + ck_assert_str_eq(out, res); + + jwt_free_str(out); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_encode_hs384) +{ + const char res[] = "eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.eyJpYXQiOj" + "E0NzU5ODA1NDUsImlzcyI6ImZpbGVzLm1hY2xhcmEtbGxjLmNvbSIsInJl" + "ZiI6IlhYWFgtWVlZWS1aWlpaLUFBQUEtQ0NDQyIsInN1YiI6InVzZXIwIn" + "0.jiOTkn_HXFz1eff2rx4bh2USfCVFh_YigoJP77Baf2d9wtQn6TBxqXmo" + "kdQtTDQm"; + unsigned char key384[48] = "aaaabbbbccccddddeeeeffffgggghhhh" + "iiiijjjjkkkkllll"; + jwt_t *jwt = NULL; + int ret = 0; + char *out; + + SET_OPS(); + + ALLOC_JWT(&jwt); + + ret = jwt_add_grant(jwt, "iss", "files.maclara-llc.com"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "sub", "user0"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant_int(jwt, "iat", TS_CONST); + ck_assert_int_eq(ret, 0); + + ret = jwt_set_alg(jwt, JWT_ALG_HS384, key384, sizeof(key384)); + ck_assert_int_eq(ret, 0); + + out = jwt_encode_str(jwt); + ck_assert_ptr_ne(out, NULL); + + ck_assert_str_eq(out, res); + + jwt_free_str(out); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_encode_hs512) +{ + const char res[] = "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJpYXQiOj" + "E0NzU5ODA1NDUsImlzcyI6ImZpbGVzLm1hY2xhcmEtbGxjLmNvbSIsInJl" + "ZiI6IlhYWFgtWVlZWS1aWlpaLUFBQUEtQ0NDQyIsInN1YiI6InVzZXIwIn" + "0.OgEFcuV7zY3A-_3YgBzZqdMXW09CGDznMuqoHh5Ln2i18UJ3pf6WdMec" + "zlBniP2LzuS_fWJRpHMOuofeMHgU3Q"; + unsigned char key512[64] = "012345678901234567890123456789XY" + "012345678901234567890123456789XY"; + jwt_t *jwt = NULL; + int ret = 0; + char *out; + + SET_OPS(); + + ALLOC_JWT(&jwt); + + ret = jwt_add_grant(jwt, "iss", "files.maclara-llc.com"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "sub", "user0"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant_int(jwt, "iat", TS_CONST); + ck_assert_int_eq(ret, 0); + + ret = jwt_set_alg(jwt, JWT_ALG_HS512, key512, sizeof(key512)); + ck_assert_int_eq(ret, 0); + + out = jwt_encode_str(jwt); + ck_assert_ptr_ne(out, NULL); + + ck_assert_str_eq(out, res); + + jwt_free_str(out); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_encode_change_alg) +{ + const char res[] = "eyJhbGciOiJub25lIn0.eyJpYXQiOjE0NzU5ODA1NDUsIml" + "zcyI6ImZpbGVzLm1hY2xhcmEtbGxjLmNvbSIsInJlZiI6IlhYWFgtWVlZW" + "S1aWlpaLUFBQUEtQ0NDQyIsInN1YiI6InVzZXIwIn0."; + unsigned char key512[64] = "012345678901234567890123456789XY" + "012345678901234567890123456789XY"; + jwt_t *jwt = NULL; + int ret = 0; + char *out; + + SET_OPS(); + + ALLOC_JWT(&jwt); + + ret = jwt_add_grant(jwt, "iss", "files.maclara-llc.com"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "sub", "user0"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant_int(jwt, "iat", TS_CONST); + ck_assert_int_eq(ret, 0); + + ret = jwt_set_alg(jwt, JWT_ALG_HS512, key512, sizeof(key512)); + ck_assert_int_eq(ret, 0); + + ret = jwt_set_alg(jwt, JWT_ALG_NONE, NULL, 0); + ck_assert_int_eq(ret, 0); + + out = jwt_encode_str(jwt); + ck_assert_ptr_ne(out, NULL); + + ck_assert_str_eq(out, res); + + jwt_free_str(out); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_encode_invalid) +{ + unsigned char key512[64] = "012345678901234567890123456789XY" + "012345678901234567890123456789XY"; + jwt_t *jwt = NULL; + int ret = 0; + + SET_OPS(); + + ALLOC_JWT(&jwt); + + ret = jwt_add_grant(jwt, "iss", "files.maclara-llc.com"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "sub", "user0"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant_int(jwt, "iat", TS_CONST); + ck_assert_int_eq(ret, 0); + + ret = jwt_set_alg(jwt, JWT_ALG_HS512, NULL, 0); + ck_assert_int_eq(ret, EINVAL); + + ret = jwt_set_alg(jwt, JWT_ALG_HS512, NULL, sizeof(key512)); + ck_assert_int_eq(ret, EINVAL); + + ret = jwt_set_alg(jwt, JWT_ALG_HS512, key512, 0); + ck_assert_int_eq(ret, EINVAL); + + ret = jwt_set_alg(jwt, JWT_ALG_NONE, key512, sizeof(key512)); + ck_assert_int_eq(ret, EINVAL); + + ret = jwt_set_alg(jwt, JWT_ALG_NONE, key512, 0); + ck_assert_int_eq(ret, EINVAL); + + ret = jwt_set_alg(jwt, JWT_ALG_NONE, NULL, sizeof(key512)); + ck_assert_int_eq(ret, EINVAL); + + /* Set a value that will never happen. */ + ret = jwt_set_alg(jwt, 999, NULL, 0); + ck_assert_int_eq(ret, EINVAL); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_encode_decode) +{ + const char *key = "somestring"; + jwt_t *mytoken, *ymtoken; + char *encoded; + int rc; + + SET_OPS(); + + jwt_new(&mytoken); + jwt_add_grant(mytoken, "sub", "user0"); + jwt_add_grant_int(mytoken, "iat", 1619130517); + jwt_add_grant_int(mytoken, "exp", 1619216917); + jwt_set_alg(mytoken, JWT_ALG_HS256, (unsigned char *)key, strlen(key)); + + encoded = jwt_encode_str(mytoken); + rc = jwt_decode(&ymtoken, encoded, (unsigned char *)key, strlen(key)); + ck_assert_int_eq(rc, 0); + + jwt_free(mytoken); + jwt_free(ymtoken); + free(encoded); +} +END_TEST + +static Suite *libjwt_suite(const char *title) +{ + Suite *s; + TCase *tc_core; + int i = ARRAY_SIZE(jwt_test_ops) - 1; + + s = suite_create(title); + + tc_core = tcase_create("jwt_encode"); + + tcase_add_loop_test(tc_core, test_jwt_encode_fp, 0, i); + tcase_add_loop_test(tc_core, test_jwt_encode_str, 0, i); + tcase_add_loop_test(tc_core, test_jwt_encode_alg_none, 0, i); + tcase_add_loop_test(tc_core, test_jwt_encode_hs256, 0, i); + tcase_add_loop_test(tc_core, test_jwt_encode_hs384, 0, i); + tcase_add_loop_test(tc_core, test_jwt_encode_hs512, 0, i); + tcase_add_loop_test(tc_core, test_jwt_encode_change_alg, 0, i); + tcase_add_loop_test(tc_core, test_jwt_encode_invalid, 0, i); + tcase_add_loop_test(tc_core, test_jwt_encode_decode, 0, i); + + tcase_set_timeout(tc_core, 30); + + suite_add_tcase(s, tc_core); + + return s; +} + +int main(int argc, char *argv[]) +{ + JWT_TEST_MAIN("LibJWT Encode"); +} diff --git a/tests/jwt_es256k.c b/tests/jwt_es256k.c new file mode 100644 index 00000000..f1780d6c --- /dev/null +++ b/tests/jwt_es256k.c @@ -0,0 +1,99 @@ +/* Public domain, no copyright. Use at your own risk. */ + +#include +#include +#include +#include +#include + +#include "jwt_tests.h" + +static const char jwt_es256k[] = "eyJhbGciOiJFUzI1NksiLCJ0eXAiOiJKV1QifQ.e" + "yJpYXQiOjE0NzU5ODA1NDUsImlzcyI6ImZpbGVzLm1hY2xhcmEtbGxjLmNvbSIsIn" + "JlZiI6IlhYWFgtWVlZWS1aWlpaLUFBQUEtQ0NDQyIsInN1YiI6InVzZXIwIn0.u_E" + "sClxS3Z8AFYude9vRupmOZ35646zAgc1xgTf_g1ImJV_1B6kqrg0IS1ckHimgUjd4" + "-DBR1UMibSCdByZngw"; + +#define SKIP_IF(opname) ({ \ + if (!strcmp(opname, jwt_get_crypto_ops())) \ + return; \ +}) + +START_TEST(test_jwt_encode_es256k) +{ + SET_OPS(); + SKIP_IF("gnutls"); + __test_alg_key(JWT_ALG_ES256K, "ec_key_secp256k1.pem", "ec_key_secp256k1-pub.pem"); +} +END_TEST + +START_TEST(test_jwt_verify_es256k) +{ + SET_OPS(); + SKIP_IF("gnutls"); + __verify_jwt(jwt_es256k, JWT_ALG_ES256K, "ec_key_secp256k1-pub.pem"); +} +END_TEST + +START_TEST(test_jwt_encode_es256k_with_ec) +{ + SKIP_IF("gnutls"); + jwt_t *jwt = NULL; + int ret = 0; + char *out; + + SET_OPS(); + SKIP_IF("gnutls"); + + ALLOC_JWT(&jwt); + + read_key("ec_key_prime256v1.pem"); + + ret = jwt_add_grant(jwt, "iss", "files.maclara-llc.com"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "sub", "user0"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant_int(jwt, "iat", TS_CONST); + ck_assert_int_eq(ret, 0); + + ret = jwt_set_alg(jwt, JWT_ALG_ES256K, key, key_len); + ck_assert_int_eq(ret, 0); + + out = jwt_encode_str(jwt); + ck_assert_ptr_eq(out, NULL); + ck_assert_int_eq(errno, EINVAL); + + jwt_free(jwt); +} +END_TEST + +static Suite *libjwt_suite(const char *title) +{ + Suite *s; + TCase *tc_core; + int i = ARRAY_SIZE(jwt_test_ops) - 1; + + s = suite_create(title); + + tc_core = tcase_create("jwt_es256k"); + + tcase_add_loop_test(tc_core, test_jwt_encode_es256k, 0, i); + tcase_add_loop_test(tc_core, test_jwt_verify_es256k, 0, i); + tcase_add_loop_test(tc_core, test_jwt_encode_es256k_with_ec, 0, i); + + tcase_set_timeout(tc_core, 30); + + suite_add_tcase(s, tc_core); + + return s; +} + +int main(int argc, char *argv[]) +{ + JWT_TEST_MAIN("LibJWT ES256K Sign/Verify"); +} diff --git a/tests/jwt_grant.c b/tests/jwt_grant.c new file mode 100644 index 00000000..ce6f51a3 --- /dev/null +++ b/tests/jwt_grant.c @@ -0,0 +1,268 @@ +/* Public domain, no copyright. Use at your own risk. */ + +#include +#include +#include +#include +#include + +#include "jwt_tests.h" + +START_TEST(test_jwt_add_grant) +{ + jwt_t *jwt = NULL; + int ret = 0; + + SET_OPS(); + + ret = jwt_new(&jwt); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + ret = jwt_add_grant(jwt, "iss", "test"); + ck_assert_int_eq(ret, 0); + + /* No duplicates */ + ret = jwt_add_grant(jwt, "iss", "other"); + ck_assert_int_eq(ret, EEXIST); + + /* No duplicates for int */ + ret = jwt_add_grant_int(jwt, "iat", (long)time(NULL)); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant_int(jwt, "iat", (long)time(NULL)); + ck_assert_int_eq(ret, EEXIST); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_get_grant) +{ + jwt_t *jwt = NULL; + const char *val; + const char testval[] = "testing"; + int ret = 0; + + SET_OPS(); + + ret = jwt_new(&jwt); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + ret = jwt_add_grant(jwt, "iss", testval); + ck_assert_int_eq(ret, 0); + + val = jwt_get_grant(jwt, "iss"); + ck_assert_ptr_nonnull(val); + ck_assert_str_eq(val, testval); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_add_grant_int) +{ + jwt_t *jwt = NULL; + long val; + int ret = 0; + + SET_OPS(); + + ret = jwt_new(&jwt); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + ret = jwt_add_grant_int(jwt, "int", 1); + ck_assert_int_eq(ret, 0); + + val = jwt_get_grant_int(jwt, "int"); + ck_assert(val); + + val = jwt_get_grant_int(jwt, "not found"); + ck_assert_int_eq(errno, ENOENT); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_add_grant_bool) +{ + jwt_t *jwt = NULL; + int val; + int ret = 0; + + SET_OPS(); + + ret = jwt_new(&jwt); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + ret = jwt_add_grant_bool(jwt, "admin", 1); + ck_assert_int_eq(ret, 0); + + val = jwt_get_grant_bool(jwt, "admin"); + ck_assert(val); + + ret = jwt_add_grant_bool(jwt, "test", 0); + ck_assert_int_eq(ret, 0); + + val = jwt_get_grant_bool(jwt, "test"); + ck_assert(!val); + + val = jwt_get_grant_bool(jwt, "not found"); + ck_assert_int_eq(errno, ENOENT); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_del_grants) +{ + jwt_t *jwt = NULL; + const char *val; + const char testval[] = "testing"; + int ret = 0; + + SET_OPS(); + + ret = jwt_new(&jwt); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + ret = jwt_add_grant(jwt, "iss", testval); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "other", testval); + ck_assert_int_eq(ret, 0); + + ret = jwt_del_grants(jwt, "iss"); + ck_assert_int_eq(ret, 0); + + val = jwt_get_grant(jwt, "iss"); + ck_assert_ptr_null(val); + + /* Delete non existent. */ + ret = jwt_del_grants(jwt, "iss"); + ck_assert_int_eq(ret, 0); + + /* Delete all grants. */ + ret = jwt_del_grants(jwt, NULL); + ck_assert_int_eq(ret, 0); + + val = jwt_get_grant(jwt, "other"); + ck_assert_ptr_null(val); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_grant_invalid) +{ + jwt_t *jwt = NULL; + const char *val; + long valint = 0; + int valbool = 0; + int ret = 0; + + SET_OPS(); + + ret = jwt_new(&jwt); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + ret = jwt_add_grant(jwt, "iss", NULL); + ck_assert_int_eq(ret, EINVAL); + + ret = jwt_add_grant_int(jwt, "", (long)time(NULL)); + ck_assert_int_eq(ret, EINVAL); + + val = jwt_get_grant(jwt, NULL); + ck_assert_int_eq(errno, EINVAL); + ck_assert_ptr_null(val); + + valint = jwt_get_grant_int(jwt, NULL); + ck_assert_int_eq(errno, EINVAL); + ck_assert(valint == 0); + + valbool = jwt_get_grant_bool(jwt, NULL); + ck_assert_int_eq(errno, EINVAL); + ck_assert(valbool == 0); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_grants_json) +{ + const char *json = "{\"id\":\"FVvGYTr3FhiURCFebsBOpBqTbzHdX/DvImiA2yheXr8=\"," + "\"iss\":\"localhost\",\"other\":[\"foo\",\"bar\"]," + "\"ref\":\"385d6518-fb73-45fc-b649-0527d8576130\"," + "\"scopes\":\"storage\",\"sub\":\"user0\"}"; + jwt_t *jwt = NULL; + const char *val; + char *json_val; + int ret = 0; + + SET_OPS(); + + ret = jwt_new(&jwt); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + ret = jwt_add_grants_json(jwt, json); + ck_assert_int_eq(ret, 0); + + val = jwt_get_grant(jwt, "ref"); + ck_assert_ptr_nonnull(val); + ck_assert_str_eq(val, "385d6518-fb73-45fc-b649-0527d8576130"); + + json_val = jwt_get_grants_json(NULL, "other"); + ck_assert_ptr_null(json_val); + ck_assert_int_eq(errno, EINVAL); + + json_val = jwt_get_grants_json(jwt, "other"); + ck_assert_ptr_nonnull(json_val); + ck_assert_str_eq(json_val, "[\"foo\",\"bar\"]"); + + jwt_free_str(json_val); + + json_val = jwt_get_grants_json(jwt, NULL); + ck_assert_ptr_nonnull(json_val); + ck_assert_str_eq(json_val, json); + + jwt_free_str(json_val); + + jwt_free(jwt); +} +END_TEST + +static Suite *libjwt_suite(const char *title) +{ + Suite *s; + TCase *tc_core; + int i = ARRAY_SIZE(jwt_test_ops) - 1; + + s = suite_create(title); + + tc_core = tcase_create("jwt_grant"); + + tcase_add_loop_test(tc_core, test_jwt_add_grant, 0, i); + tcase_add_loop_test(tc_core, test_jwt_add_grant_int, 0, i); + tcase_add_loop_test(tc_core, test_jwt_add_grant_bool, 0, i); + tcase_add_loop_test(tc_core, test_jwt_get_grant, 0, i); + tcase_add_loop_test(tc_core, test_jwt_del_grants, 0, i); + tcase_add_loop_test(tc_core, test_jwt_grant_invalid, 0, i); + tcase_add_loop_test(tc_core, test_jwt_grants_json, 0, i); + + tcase_set_timeout(tc_core, 30); + + suite_add_tcase(s, tc_core); + + return s; +} + +int main(int argc, char *argv[]) +{ + JWT_TEST_MAIN("LibJWT Grant"); +} diff --git a/tests/jwt_header.c b/tests/jwt_header.c new file mode 100644 index 00000000..0ee4da36 --- /dev/null +++ b/tests/jwt_header.c @@ -0,0 +1,268 @@ +/* Public domain, no copyright. Use at your own risk. */ + +#include +#include +#include +#include +#include + +#include "jwt_tests.h" + +START_TEST(test_jwt_add_header) +{ + jwt_t *jwt = NULL; + int ret = 0; + + SET_OPS(); + + ret = jwt_new(&jwt); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + ret = jwt_add_header(jwt, "iss", "test"); + ck_assert_int_eq(ret, 0); + + /* No duplicates */ + ret = jwt_add_header(jwt, "iss", "other"); + ck_assert_int_eq(ret, EEXIST); + + /* No duplicates for int */ + ret = jwt_add_header_int(jwt, "iat", (long)time(NULL)); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_header_int(jwt, "iat", (long)time(NULL)); + ck_assert_int_eq(ret, EEXIST); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_get_header) +{ + jwt_t *jwt = NULL; + const char *val; + const char testval[] = "testing"; + int ret = 0; + + SET_OPS(); + + ret = jwt_new(&jwt); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + ret = jwt_add_header(jwt, "iss", testval); + ck_assert_int_eq(ret, 0); + + val = jwt_get_header(jwt, "iss"); + ck_assert_ptr_nonnull(val); + ck_assert_str_eq(val, testval); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_add_header_int) +{ + jwt_t *jwt = NULL; + long val; + int ret = 0; + + SET_OPS(); + + ret = jwt_new(&jwt); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + ret = jwt_add_header_int(jwt, "int", 1); + ck_assert_int_eq(ret, 0); + + val = jwt_get_header_int(jwt, "int"); + ck_assert(val); + + val = jwt_get_header_int(jwt, "not found"); + ck_assert_int_eq(errno, ENOENT); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_add_header_bool) +{ + jwt_t *jwt = NULL; + int val; + int ret = 0; + + SET_OPS(); + + ret = jwt_new(&jwt); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + ret = jwt_add_header_bool(jwt, "admin", 1); + ck_assert_int_eq(ret, 0); + + val = jwt_get_header_bool(jwt, "admin"); + ck_assert(val); + + ret = jwt_add_header_bool(jwt, "test", 0); + ck_assert_int_eq(ret, 0); + + val = jwt_get_header_bool(jwt, "test"); + ck_assert(!val); + + val = jwt_get_header_bool(jwt, "not found"); + ck_assert_int_eq(errno, ENOENT); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_del_headers) +{ + jwt_t *jwt = NULL; + const char *val; + const char testval[] = "testing"; + int ret = 0; + + SET_OPS(); + + ret = jwt_new(&jwt); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + ret = jwt_add_header(jwt, "iss", testval); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_header(jwt, "other", testval); + ck_assert_int_eq(ret, 0); + + ret = jwt_del_headers(jwt, "iss"); + ck_assert_int_eq(ret, 0); + + val = jwt_get_header(jwt, "iss"); + ck_assert_ptr_null(val); + + /* Delete non existent. */ + ret = jwt_del_headers(jwt, "iss"); + ck_assert_int_eq(ret, 0); + + /* Delete all headers. */ + ret = jwt_del_headers(jwt, NULL); + ck_assert_int_eq(ret, 0); + + val = jwt_get_header(jwt, "other"); + ck_assert_ptr_null(val); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_header_invalid) +{ + jwt_t *jwt = NULL; + const char *val; + long valint = 0; + int valbool = 0; + int ret = 0; + + SET_OPS(); + + ret = jwt_new(&jwt); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + ret = jwt_add_header(jwt, "iss", NULL); + ck_assert_int_eq(ret, EINVAL); + + ret = jwt_add_header_int(jwt, "", (long)time(NULL)); + ck_assert_int_eq(ret, EINVAL); + + val = jwt_get_header(jwt, NULL); + ck_assert_int_eq(errno, EINVAL); + ck_assert_ptr_null(val); + + valint = jwt_get_header_int(jwt, NULL); + ck_assert_int_eq(errno, EINVAL); + ck_assert(valint == 0); + + valbool = jwt_get_header_bool(jwt, NULL); + ck_assert_int_eq(errno, EINVAL); + ck_assert(valbool == 0); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_headers_json) +{ + const char *json = "{\"id\":\"FVvGYTr3FhiURCFebsBOpBqTbzHdX/DvImiA2yheXr8=\"," + "\"iss\":\"localhost\",\"other\":[\"foo\",\"bar\"]," + "\"ref\":\"385d6518-fb73-45fc-b649-0527d8576130\"," + "\"scopes\":\"storage\",\"sub\":\"user0\"}"; + jwt_t *jwt = NULL; + const char *val; + char *json_val; + int ret = 0; + + SET_OPS(); + + ret = jwt_new(&jwt); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + ret = jwt_add_headers_json(jwt, json); + ck_assert_int_eq(ret, 0); + + val = jwt_get_header(jwt, "ref"); + ck_assert_ptr_nonnull(val); + ck_assert_str_eq(val, "385d6518-fb73-45fc-b649-0527d8576130"); + + json_val = jwt_get_headers_json(NULL, "other"); + ck_assert_ptr_null(json_val); + ck_assert_int_eq(errno, EINVAL); + + json_val = jwt_get_headers_json(jwt, "other"); + ck_assert_ptr_nonnull(json_val); + ck_assert_str_eq(json_val, "[\"foo\",\"bar\"]"); + + jwt_free_str(json_val); + + json_val = jwt_get_headers_json(jwt, NULL); + ck_assert_ptr_nonnull(json_val); + ck_assert_str_eq(json_val, json); + + jwt_free_str(json_val); + + jwt_free(jwt); +} +END_TEST + +static Suite *libjwt_suite(const char *title) +{ + Suite *s; + TCase *tc_core; + int i = ARRAY_SIZE(jwt_test_ops) - 1; + + s = suite_create(title); + + tc_core = tcase_create("jwt_header"); + + tcase_add_loop_test(tc_core, test_jwt_add_header, 0, i); + tcase_add_loop_test(tc_core, test_jwt_add_header_int, 0, i); + tcase_add_loop_test(tc_core, test_jwt_add_header_bool, 0, i); + tcase_add_loop_test(tc_core, test_jwt_get_header, 0, i); + tcase_add_loop_test(tc_core, test_jwt_del_headers, 0, i); + tcase_add_loop_test(tc_core, test_jwt_header_invalid, 0, i); + tcase_add_loop_test(tc_core, test_jwt_headers_json, 0, i); + + tcase_set_timeout(tc_core, 30); + + suite_add_tcase(s, tc_core); + + return s; +} + +int main(int argc, char *argv[]) +{ + JWT_TEST_MAIN("LibJWT Header"); +} diff --git a/tests/jwt_new.c b/tests/jwt_new.c new file mode 100644 index 00000000..ecc562ef --- /dev/null +++ b/tests/jwt_new.c @@ -0,0 +1,614 @@ +/* Public domain, no copyright. Use at your own risk. */ + +#include +#include +#include +#include +#include + +#include "jwt_tests.h" + +START_TEST(test_jwt_new) +{ + jwt_t *jwt = NULL; + int ret = 0; + + SET_OPS(); + + ret = jwt_new(NULL); + ck_assert_int_eq(ret, EINVAL); + + ret = jwt_new(&jwt); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_dup) +{ + jwt_t *jwt = NULL, *new = NULL; + int ret = 0; + const char *val = NULL; + time_t now; + long valint; + + SET_OPS(); + + new = jwt_dup(NULL); + ck_assert_ptr_null(new); + + ret = jwt_new(&jwt); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + ret = jwt_add_grant(jwt, "iss", "test"); + ck_assert_int_eq(ret, 0); + + new = jwt_dup(jwt); + ck_assert_ptr_nonnull(new); + + val = jwt_get_grant(new, "iss"); + ck_assert_ptr_nonnull(val); + ck_assert_str_eq(val, "test"); + + ck_assert_int_eq(jwt_get_alg(new), JWT_ALG_NONE); + + now = time(NULL); + ret = jwt_add_grant_int(jwt, "iat", (long)now); + ck_assert_int_eq(ret, 0); + + valint = jwt_get_grant_int(jwt, "iat"); + ck_assert(((long)now) == valint); + + jwt_free(new); + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_dup_signed) +{ + unsigned char key256[32] = "012345678901234567890123456789XY"; + jwt_t *jwt = NULL, *new = NULL; + int ret = 0; + const char *val = NULL; + + SET_OPS(); + + ret = jwt_new(&jwt); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + ret = jwt_add_grant(jwt, "iss", "test"); + ck_assert_int_eq(ret, 0); + + ret = jwt_set_alg(jwt, JWT_ALG_HS256, key256, sizeof(key256)); + ck_assert_int_eq(ret, 0); + + new = jwt_dup(jwt); + ck_assert_ptr_nonnull(new); + + val = jwt_get_grant(new, "iss"); + ck_assert_ptr_nonnull(val); + ck_assert_str_eq(val, "test"); + + ck_assert_int_eq(jwt_get_alg(new), JWT_ALG_HS256); + + jwt_free(new); + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_decode) +{ + const char token[] = "eyJhbGciOiJub25lIn0.eyJpc3MiOiJmaWxlcy5jeXBo" + "cmUuY29tIiwic3ViIjoidXNlcjAifQ."; + jwt_alg_t alg; + jwt_t *jwt; + int ret; + + SET_OPS(); + + ret = jwt_decode(&jwt, token, NULL, 0); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + alg = jwt_get_alg(jwt); + ck_assert(alg == JWT_ALG_NONE); + + jwt_free(jwt); +} +END_TEST + +static int jwt_decode_2_cb_done; + +static int test_jwt_decode_2_alg_none_cb(const jwt_t *jwt, jwt_key_t *key) +{ + jwt_alg_t alg = jwt_get_alg(jwt); + + jwt_decode_2_cb_done = 1; + + return (alg == JWT_ALG_NONE) ? 0 : -1; +} + +START_TEST(test_jwt_decode_2_alg_none) +{ + const char token[] = "eyJhbGciOiJub25lIn0.eyJpc3MiOiJmaWxlcy5jeXBo" + "cmUuY29tIiwic3ViIjoidXNlcjAifQ."; + jwt_alg_t alg; + jwt_t *jwt; + int ret; + + SET_OPS(); + + jwt_decode_2_cb_done = 0; + + ret = jwt_decode_2(&jwt, token, test_jwt_decode_2_alg_none_cb); + ck_assert_int_ne(jwt_decode_2_cb_done, 0); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + alg = jwt_get_alg(jwt); + ck_assert_int_eq(alg, JWT_ALG_NONE); + + jwt_free(jwt); +} + +START_TEST(test_jwt_decode_2_compat) +{ + const char token[] = "eyJhbGciOiJub25lIn0.eyJpc3MiOiJmaWxlcy5jeXBo" + "cmUuY29tIiwic3ViIjoidXNlcjAifQ."; + jwt_alg_t alg; + jwt_t *jwt; + int ret; + + SET_OPS(); + + ret = jwt_decode_2(&jwt, token, NULL); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + alg = jwt_get_alg(jwt); + ck_assert_int_eq(alg, JWT_ALG_NONE); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_decode_invalid_final_dot) +{ + const char token[] = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzM4NCJ9." + "eyJpc3MiOiJmaWxlcy5jeXBocmUuY29tIiwic" + "3ViIjoidXNlcjAifQ"; + jwt_t *jwt; + int ret; + + SET_OPS(); + + ret = jwt_decode(&jwt, token, NULL, 0); + ck_assert_int_eq(ret, EINVAL); + ck_assert_ptr_null(jwt); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_decode_invalid_alg) +{ + const char token[] = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIQUhBSCJ9." + "eyJpc3MiOiJmaWxlcy5jeXBocmUuY29tIiwic" + "3ViIjoidXNlcjAifQ."; + jwt_t *jwt; + int ret; + + SET_OPS(); + + ret = jwt_decode(&jwt, token, NULL, 0); + ck_assert_int_eq(ret, EINVAL); + ck_assert_ptr_null(jwt); + + jwt_free(jwt); +} +END_TEST + +/* { "typ": "JWT", "alg": "none" } . . */ +START_TEST(test_jwt_decode_dot_dot) +{ + char token[] = "eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.."; + jwt_t *jwt; + int ret; + + SET_OPS(); + + /* Two dots */ + ret = jwt_decode(&jwt, token, NULL, 0); + ck_assert_int_ne(ret, 0); + ck_assert_ptr_null(jwt); + + /* One dot */ + ret = strlen(token); + token[ret - 1] = '\0'; + + ret = jwt_decode(&jwt, token, NULL, 0); + ck_assert_int_ne(ret, 0); + ck_assert_ptr_null(jwt); + + /* No dot */ + ret = strlen(token); + token[ret - 1] = '\0'; + + ret = jwt_decode(&jwt, token, NULL, 0); + ck_assert_int_ne(ret, 0); + ck_assert_ptr_null(jwt); +} + +/* { "typ": "JWT", "alg": "none" } . {} . */ +START_TEST(test_jwt_decode_empty_body) +{ + const char token[] = "eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.e30."; + jwt_t *jwt; + int ret; + + SET_OPS(); + + ret = jwt_decode(&jwt, token, NULL, 0); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + jwt_free(jwt); +} + +/* { "typ": "JWT", "alg": "HS256" } . { "test": 1 } . */ +START_TEST(test_jwt_decode_nokey_alg_hs256) +{ + const char token[] = "eyJ0eXAiOiJBTEwiLCJhbGciOiJOT05FIn0.eyJ0ZXN0IjoxfQ."; + jwt_t *jwt; + int ret; + + SET_OPS(); + + ret = jwt_decode(&jwt, token, NULL, 0); + ck_assert_int_ne(ret, 0); + ck_assert_ptr_null(jwt); +} +END_TEST + +/* { "typ": "ALL", "alg": "none" } . { "test": 1 } */ +START_TEST(test_jwt_decode_ignore_typ) +{ + const char token[] = "eyJ0eXAiOiJBTEwiLCJhbGciOiJub25lIn0.eyJ0ZXN0IjoxfQ."; + jwt_t *jwt; + int ret; + + SET_OPS(); + + ret = jwt_decode(&jwt, token, NULL, 0); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_decode_invalid_head) +{ + const char token[] = "yJ0eXAiOiJKV1QiLCJhbGciOiJIUzM4NCJ9." + "eyJpc3MiOiJmaWxlcy5jeXBocmUuY29tIiwic" + "3ViIjoidXNlcjAifQ."; + jwt_t *jwt; + int ret; + + SET_OPS(); + + ret = jwt_decode(&jwt, token, NULL, 0); + ck_assert_int_eq(ret, EINVAL); + ck_assert_ptr_null(jwt); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_decode_alg_none_with_key) +{ + const char token[] = "eyJhbGciOiJub25lIn0." + "eyJpc3MiOiJmaWxlcy5jeXBocmUuY29tIiwic" + "3ViIjoidXNlcjAifQ."; + jwt_t *jwt; + int ret; + + SET_OPS(); + + ret = jwt_decode(&jwt, token, (const unsigned char *)"key", 3); + ck_assert_int_eq(ret, EINVAL); + ck_assert_ptr_null(jwt); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_decode_invalid_body) +{ + const char token[] = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzM4NCJ9." + "eyJpc3MiOiJmaWxlcy5jeBocmUuY29tIiwic" + "3ViIjoidXNlcjAifQ."; + jwt_t *jwt; + int ret; + + SET_OPS(); + + ret = jwt_decode(&jwt, token, NULL, 0); + ck_assert_int_eq(ret, EINVAL); + ck_assert_ptr_null(jwt); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_decode_hs256) +{ + const char token[] = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3Mi" + "OiJmaWxlcy5jeXBocmUuY29tIiwic3ViIjoidXNlcjAif" + "Q.dLFbrHVViu1e3VD1yeCd9aaLNed-bfXhSsF0Gh56fBg"; + unsigned char key256[32] = "012345678901234567890123456789XY"; + jwt_t *jwt; + int ret; + + SET_OPS(); + + ret = jwt_decode(&jwt, token, key256, sizeof(key256)); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + jwt_free(jwt); +} +END_TEST + +/* Fron issue #201. Adding tests for alg checks. */ +/* { "typ": "JWT", "alg": "HS256" } . { ... } . sig */ +START_TEST(test_jwt_decode_hs256_no_key_alg) +{ + const char token[] = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3Mi" + "OiJmaWxlcy5jeXBocmUuY29tIiwic3ViIjoidXNlcjAif" + "Q.dLFbrHVViu1e3VD1yeCd9aaLNed-bfXhSsF0Gh56fBg"; + jwt_t *jwt; + int ret; + + SET_OPS(); + + ret = jwt_decode(&jwt, token, NULL, 0); + ck_assert_int_ne(ret, 0); + ck_assert_ptr_null(jwt); +} +END_TEST + +START_TEST(test_jwt_decode_hs256_issue_1) +{ + const char token[] = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIi" + "OiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE" + "5hbWUiOiJ3b3JsZCIsInJvbGVzIjpbInRoaXMiLCJ0aGF0IiwidGhlb3Ro" + "ZXIiXSwiaXNzIjoiaXNzdWVyIiwicGVyc29uSWQiOiI3NWJiM2NjNy1iOT" + "MzLTQ0ZjAtOTNjNi0xNDdiMDgyZmFkYjUiLCJleHAiOjE5MDg4MzUyMDAs" + "ImlhdCI6MTQ4ODgxOTYwMCwidXNlcm5hbWUiOiJoZWxsby53b3JsZCJ9.t" + "JoAl_pvq95hK7GKqsp5TU462pLTbmSYZc1fAHzcqWM"; + const unsigned char key256[] = "\x00\x11\x22\x33\x44\x55\x66\x77\x88" + "\x99\xAA\xBB\xCC\xDD\xEE\xFF\x00\x11\x22\x33\x44\x55\x66" + "\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF"; + jwt_t *jwt; + int ret; + + SET_OPS(); + + ret = jwt_decode(&jwt, token, key256, sizeof(key256)); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_decode_hs256_issue_2) +{ + const char token[] = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIi" + "OiJzb21lLWxvbmctdXVpZCIsImZpcnN0TmFtZSI6ImhlbGxvIiwibGFzdE" + "5hbWUiOiJ3b3JsZCIsInJvbGVzIjpbInRoaXMiLCJ0aGF0IiwidGhlb3Ro" + "ZXIiXSwiaXNzIjoiaXNzdWVyIiwicGVyc29uSWQiOiI3NWJiM2NjNy1iOT" + "MzLTQ0ZjAtOTNjNi0xNDdiMDgyZmFkYjUiLCJleHAiOjE5MDg4MzUyMDAs" + "ImlhdCI6MTQ4ODgxOTYwMCwidXNlcm5hbWUiOiJoZWxsby53b3JsZCJ9.G" + "pCRdGxE4uClX6Vg7eAPwG-37ZvNBQXyfcldKzDG_QI"; + const char key256[] = "00112233445566778899AABBCCDDEEFF001122334455" + "66778899AABBCCDDEEFF"; + jwt_t *jwt; + int ret; + + SET_OPS(); + + ret = jwt_decode(&jwt, token, (const unsigned char *)key256, strlen(key256)); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_decode_hs384) +{ + const char token[] = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzM4NCJ9." + "eyJpc3MiOiJmaWxlcy5jeXBocmUuY29tIiwic" + "3ViIjoidXNlcjAifQ.xqea3OVgPEMxsCgyikr" + "R3gGv4H2yqMyXMm7xhOlQWpA-NpT6n2a1d7TD" + "GgU6LOe4"; + const unsigned char key384[48] = "aaaabbbbccccddddeeeeffffg" + "ggghhhhiiiijjjjkkkkllll"; + jwt_t *jwt; + int ret; + + SET_OPS(); + + ret = jwt_decode(&jwt, token, key384, sizeof(key384)); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_decode_hs512) +{ + const char token[] = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpc3Mi" + "OiJmaWxlcy5jeXBocmUuY29tIiwic3ViIjoidXNlcjAif" + "Q.u-4XQB1xlYV8SgAnKBof8fOWOtfyNtc1ytTlc_vHo0U" + "lh5uGT238te6kSacnVzBbC6qwzVMT1806oa1Y8_8EOg"; + unsigned char key512[64] = "012345678901234567890123456789XY" + "012345678901234567890123456789XY"; + jwt_t *jwt; + int ret; + + SET_OPS(); + + ret = jwt_decode(&jwt, token, key512, sizeof(key512)); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + jwt_free(jwt); +} +END_TEST + +unsigned char __key512[64] = "012345678901234567890123456789XY" + "012345678901234567890123456789XY"; + +static int test_jwt_decode_2_hs512_kp(const jwt_t *jwt, jwt_key_t *key) +{ + if (jwt_get_alg(jwt) == JWT_ALG_HS512) { + key->jwt_key = __key512; + key->jwt_key_len = sizeof(__key512); + return 0; + } + return EINVAL; +} + +START_TEST(test_jwt_decode_2_hs512) +{ + const char token[] = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpc3Mi" + "OiJmaWxlcy5jeXBocmUuY29tIiwic3ViIjoidXNlcjAif" + "Q.u-4XQB1xlYV8SgAnKBof8fOWOtfyNtc1ytTlc_vHo0U" + "lh5uGT238te6kSacnVzBbC6qwzVMT1806oa1Y8_8EOg"; + + jwt_t *jwt; + int ret; + + SET_OPS(); + + ret = jwt_decode_2(&jwt, token, &test_jwt_decode_2_hs512_kp); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_decode_2_invalid) +{ + const char token[] = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzM4NCJ9." + "eyJpc3MiOiJmaWxlcy5jeXBocmUuY29tIiwic" + "3ViIjoidXNlcjAifQ.xqea3OVgPEMxsCgyikr" + "R3gGv4H2yqMyXMm7xhOlQWpA-NpT6n2a1d7TD" + "GgU6LOe4"; + + jwt_t *jwt; + int ret; + + SET_OPS(); + + ret = jwt_decode_2(&jwt, token, &test_jwt_decode_2_hs512_kp); + ck_assert_int_eq(ret, EINVAL); + ck_assert_ptr_null(jwt); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_decode_2_invalid_body) +{ + const char token[] = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzM4NCJ9." + "eyJpc3MiOiJmaWxlcy5jeBocmUuY29tIiwic" + "3ViIjoidXNlcjAifQ."; + jwt_t *jwt; + int ret; + + SET_OPS(); + + ret = jwt_decode_2(&jwt, token, &test_jwt_decode_2_hs512_kp); + ck_assert_int_eq(ret, EINVAL); + ck_assert_ptr_null(jwt); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_decode_invalid_base64) +{ + const char token[] = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3Mi" + "OiJmaWxlcy5jeXBocmUuY29tIiwic3ViIjoidXNlcjAif" + "Q.dLFbrHVViu1e3VD1yeCd9aaLNed-bfXhSsF0Gh56fBga"; + unsigned char key256[32] = "012345678901234567890123456789XY"; + jwt_t *jwt; + int ret; + + SET_OPS(); + + ret = jwt_decode(&jwt, token, key256, sizeof(key256)); + ck_assert_int_ne(ret, 0); + ck_assert_ptr_null(jwt); + + jwt_free(jwt); +} +END_TEST + +static Suite *libjwt_suite(const char *title) +{ + Suite *s; + TCase *tc_core; + int i = ARRAY_SIZE(jwt_test_ops) - 1; + + s = suite_create(title); + + tc_core = tcase_create("jwt_new"); + tcase_add_loop_test(tc_core, test_jwt_new, 0, i); + tcase_add_loop_test(tc_core, test_jwt_dup, 0, i); + tcase_add_loop_test(tc_core, test_jwt_dup_signed, 0, i); + tcase_add_loop_test(tc_core, test_jwt_decode, 0, i); + tcase_add_loop_test(tc_core, test_jwt_decode_2_compat, 0, i); + tcase_add_loop_test(tc_core, test_jwt_decode_2_alg_none, 0, i); + tcase_add_loop_test(tc_core, test_jwt_decode_invalid_alg, 0, i); + tcase_add_loop_test(tc_core, test_jwt_decode_ignore_typ, 0, i); + tcase_add_loop_test(tc_core, test_jwt_decode_dot_dot, 0, i); + tcase_add_loop_test(tc_core, test_jwt_decode_empty_body, 0, i); + tcase_add_loop_test(tc_core, test_jwt_decode_nokey_alg_hs256, 0, i); + tcase_add_loop_test(tc_core, test_jwt_decode_invalid_head, 0, i); + tcase_add_loop_test(tc_core, test_jwt_decode_alg_none_with_key, 0, i); + tcase_add_loop_test(tc_core, test_jwt_decode_invalid_body, 0, i); + tcase_add_loop_test(tc_core, test_jwt_decode_2_invalid_body, 0, i); + tcase_add_loop_test(tc_core, test_jwt_decode_invalid_final_dot, 0, i); + tcase_add_loop_test(tc_core, test_jwt_decode_hs256, 0, i); + tcase_add_loop_test(tc_core, test_jwt_decode_hs256_no_key_alg, 0, i); + tcase_add_loop_test(tc_core, test_jwt_decode_hs384, 0, i); + tcase_add_loop_test(tc_core, test_jwt_decode_hs512, 0, i); + tcase_add_loop_test(tc_core, test_jwt_decode_2_hs512, 0, i); + tcase_add_loop_test(tc_core, test_jwt_decode_2_invalid, 0, i); + tcase_add_loop_test(tc_core, test_jwt_decode_invalid_base64, 0, i); + + tcase_add_loop_test(tc_core, test_jwt_decode_hs256_issue_1, 0, i); + tcase_add_loop_test(tc_core, test_jwt_decode_hs256_issue_2, 0, i); + + tcase_set_timeout(tc_core, 30); + + suite_add_tcase(s, tc_core); + + return s; +} + +int main(int argc, char *argv[]) +{ + JWT_TEST_MAIN("LibJWT New"); +} diff --git a/tests/jwt_rsa.c b/tests/jwt_rsa.c new file mode 100644 index 00000000..1692f331 --- /dev/null +++ b/tests/jwt_rsa.c @@ -0,0 +1,320 @@ +/* Public domain, no copyright. Use at your own risk. */ + +#include +#include +#include +#include +#include + +#include "jwt_tests.h" + +static const char jwt_rs256_2048[] = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.ey" + "JpYXQiOjE0NzU5ODA1NDUsImlzcyI6ImZpbGVzLm1hY2xhcmEtbGxjLmNvbSIsInJlZi" + "I6IlhYWFgtWVlZWS1aWlpaLUFBQUEtQ0NDQyIsInN1YiI6InVzZXIwIn0.RlJPQst_lp" + "MJUsbnzlT2Mf3xzlHyUlVaQM_PJ1_vpBf1gHkhv-0hm3pa1_HRvpqg5UdDF3iOMLT0GU" + "j3W8JveaSvXKFeZdRpQGqmC7MZ7NzaYtyaDT7asniIVDf0JomD8Cfq8IdOn2ZREpbuJ6" + "moPwwvJ2zwL3vY-7w5A7ZQ3fxUedPuzn9n6tbEnuXcbDMyWQjen5poYmmvoIrDbzK0Zb" + "KbAJ5VrJwME_fZnPHS4c3b8rZGdBJCPI8oT2On6a9LrVqY3riqqHeiSqewfjDsox4tL2" + "G5KUpqK0oJmnZPGTnNY774PGabpcPBNbfMJqi8o8r0a7pa7sy6B59P7slUdw"; + +static const char jwt_rs384_4096[] = "eyJhbGciOiJSUzM4NCIsInR5cCI6IkpXVCJ9.ey" + "JpYXQiOjE0NzU5ODA1NDUsImlzcyI6ImZpbGVzLm1hY2xhcmEtbGxjLmNvbSIsInJlZi" + "I6IlhYWFgtWVlZWS1aWlpaLUFBQUEtQ0NDQyIsInN1YiI6InVzZXIwIn0.E9e6Chv39H" + "FQqhfUFcCxUzyS6yHQvBex_OaQNIPqeoPR_FDquiLhQUJj45IGd8_aVT8DvfzTFSHOP1" + "cP_UbzCYtZ8VC0o-idxyjTnlnqMJy75xNBzPUy6OSpTeX9yVQCtIkT6kIom4j35ABibk" + "_MdCVqnWG2fijDEeTlLD7uDRcCTGckqQhVOI3t4iIytiUVPnboFiaPLfei_mPcJ6CiOI" + "QYF4VOWlDllgUFrQ0M0nKm4Pq6bVaBIMzF0hJrPn_7GCV3XmVLthcObljfydaNm_CcIY" + "g7y_8OT_8yAvDlbKBe5jVeq-7_lLCinarkGUZ5ryA2lbC6yPBgtaAU3g6XxP60n6To7z" + "akV_5dgcPJFDlTkoBI6pPH3Zf50UYsf-wR4D2J3fP4rcYco4HGtxZ89tfoNYCB8z5-GA" + "ImSJRbVSsdadwSKKgldlG9XR13Cq-Ox8Hc7qCd1tTC-NeY31XMuWxd9981bQMeGhBkyJ" + "fFnIksh8xyyO0uOPvgmchOtG8bSImfZZaeBI6TcPJ89Oo3iD6NmPdO5AUqv6NB7Y46zH" + "GUXCeTuumVk7I_PJ9laICW4-cx1zHDvPY3TphVVTudSqWdUDMjwhrRI23569DByMJE9J" + "XpTg7HV_17EPTDWExc6TQVkmmY4QbS0pVbKyJTwwKmnu2F9o2fl1N0NOw1SOM"; + +static const char jwt_rs512_8192[] = "eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.ey" + "JpYXQiOjE0NzU5ODA1NDUsImlzcyI6ImZpbGVzLm1hY2xhcmEtbGxjLmNvbSIsInJlZi" + "I6IlhYWFgtWVlZWS1aWlpaLUFBQUEtQ0NDQyIsInN1YiI6InVzZXIwIn0.F3_fF6RGJy" + "B_apyq_ljxLP7luYC78wT23pPKAyRSMiDi0g-7Yfohv5_p0rBOCsT1H-1_rImWiIcsdJ" + "87oYfQw95G_pHcK56ag7_a1i-jQCV7ZyRAuesDcM1YRealKvdnof08Jw392h685XNK2l" + "mqvZ436Hz4oCcQimwuKeR0ndAgm38y-_FswOlb3POwHBJcDtDR3UTvCONyqgcCD5hl5J" + "edjA8GA0Mvlp4JV2-ctsNYaDOMQRw3S8hJOXvKySQsbckEj8pC_bCrcbSR8BUJBrjM5Q" + "SLslcYGJopyCYAApGsq3t4-uY3Dx-QjWBMgSIgD9BONjv4VXvsfrllWUQmK88qJ4WQFO" + "L0PUSJy57lZjpjXReafJcJgdrGI-tFHgqkcb1VxmqnnIFRqadgjJ1BkfFvdExJiOgnvN" + "SEQ16AYy2FlxZeaabqyOjc8IlGi7Z9hYZgImL_qn0REJXgvxNtabA9-A4cEMWVDtYkCn" + "F_VyYeMOMYKMuoFW5ubLfOhYhDnd_yvkrLzZ76BGXpHtg7cfpnN8dNWp8irwjSByBH4H" + "lB0F9NFZydNB8wylFsKesNhmkQYcLnchhA4dBZV888NKVfqIBcM-GWXExtmON0SXg_HX" + "YmbO6zTgQ4tGAk4HglRWJYlfgORdcBBtnlwUHXm5L7_0J8KHoyNUGbA2XU1krVhBN-A7" + "sxtLof6MZP8c7p65Oc6DH8FulpmnBt8yVID2GnXPfcLs-RgM8QnlSGbNhn9WT2kmBTIV" + "eniVfp_IJ40Kd4SROGa_XbXMTufympqyYSSxtqHblGgtolVGdpN05FDvInG0J0dojYxT" + "_puZ-fvBAMXRPeoC_t_1ScfER4CjsUedbReQLZmE-9nJKhCZKqiba5qCbq8riZYiROGJ" + "rtVryTywLzw7XX4D-s9oJsEk6ELSRI-buXuCqyCmbdRpFz-i2-VPmNQIalk_0Pq_dOZN" + "y0GCvcezkhx1quGKPDmFskJnvKZmC70er0DvOQl1A909kFivxIXfTzlu8jUJt8PPi0gU" + "-nnOGSYxC8tD16vwHvAP3KPYvUzmC2N0r6_yM2_Y-JH5Vypeecbuh66cx18Bqk1nQYfg" + "BLjuJwQSIKRNNBLtgU7mcyI0Lj4-TWbE-22dYYvKcPMxSmfwAmJUm7ZFUAq_Ok-46AmV" + "RYg-h7bZZlfutOiWuoBrmnqQ6dEDGjXiEgWhtAx5HG3qn1_vmA3JQxJAWEfuhHa3IWac" + "MDRrJehImeyDE0H0rpOsxSXOjnDqiFBsf9d0-zJNFvo9tWlK_-d-N40BIy5eZm37FKG7" + "g2rFmXtuicUs6jiwu0_tHSi1fPKO7YN2ezQc9HAoBvvrur1z_XGbDSmFTQNTv0Cg"; + +static const char jwt_rs256_invalid[] = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9" + ".eyJpYXQiOjE0NzU5ODA1NDUsImlzcyI6ImZpbGVzLmN5cGhyZS5jb20iLCJyZWYiOiJ" + "YWFhYLVlZWVktWlpaWi1BQUFBLUNDQ0MiLCJzdWIiOiJ1c2VyMCJ9.IAmCornholio"; + +START_TEST(test_jwt_encode_rs256) +{ + SET_OPS(); + __compare_alg_key("rsa_key_2048.pem", jwt_rs256_2048, JWT_ALG_RS256); +} +END_TEST + +START_TEST(test_jwt_verify_rs256) +{ + SET_OPS(); + __verify_alg_key("rsa_key_2048-pub.pem", jwt_rs256_2048, JWT_ALG_RS256); +} +END_TEST + +START_TEST(test_jwt_validate_rs256) +{ + jwt_t *jwt = NULL; + jwt_valid_t *jwt_valid = NULL; + int ret = 0; + + SET_OPS(); + + read_key("rsa_key_2048-pub.pem"); + + ret = jwt_decode(&jwt, jwt_rs256_2048, key, key_len); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + jwt_valid_new(&jwt_valid, JWT_ALG_RS256); + ck_assert_ptr_nonnull(jwt_valid); + + ret = jwt_valid_add_grant(jwt_valid, "iss", "files.maclara-llc.com"); + ck_assert_int_eq(ret, 0); + + ret = jwt_valid_add_grant_int(jwt_valid, "iat", TS_CONST); + ck_assert_int_eq(ret, 0); + + ck_assert_int_eq(JWT_VALIDATION_SUCCESS, jwt_validate(jwt, jwt_valid)); + + jwt_valid_free(jwt_valid); + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_encode_rs384) +{ + SET_OPS(); + __compare_alg_key("rsa_key_4096.pem", jwt_rs384_4096, JWT_ALG_RS384); +} +END_TEST + +START_TEST(test_jwt_verify_rs384) +{ + SET_OPS(); + __verify_alg_key("rsa_key_4096-pub.pem", jwt_rs384_4096, JWT_ALG_RS384); +} +END_TEST + +START_TEST(test_jwt_encode_rs512) +{ + SET_OPS(); + __compare_alg_key("rsa_key_8192.pem", jwt_rs512_8192, JWT_ALG_RS512); +} +END_TEST + +START_TEST(test_jwt_verify_rs512) +{ + SET_OPS(); + __verify_alg_key("rsa_key_8192-pub.pem", jwt_rs512_8192, JWT_ALG_RS512); +} +END_TEST + +static const char jwt_rsa_i37[] = "eyJraWQiOiJkWUoxTDVnbWd0eDlWVU9xbVpyd2F6cW" + "NhK3B5c1lHNUl3N3RSUXB6a3Z3PSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiJhMDQyZj" + "Y4My0xODNiLTQ1ZWUtOTZiYy1lNDdlYjhiMzc2MTYiLCJ0b2tlbl91c2UiOiJhY2Nlc3" + "MiLCJzY29wZSI6ImF3cy5jb2duaXRvLnNpZ25pbi51c2VyLmFkbWluIiwiaXNzIjoiaH" + "R0cHM6XC9cL2NvZ25pdG8taWRwLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tXC91cy1lYX" + "N0LTFfUWJvMXlMZ0ZIIiwiZXhwIjoxNDg1ODgyNDg5LCJpYXQiOjE0ODU4Nzg4ODksIm" + "p0aSI6Ijg1MTBlMGVkLWU3N2UtNDJmZS1hMmI2LTgyMjAzMDcxZWQyOCIsImNsaWVudF" + "9pZCI6IjdicTVhanV0czM1anVmamVnMGYwcmhzNnRpIiwidXNlcm5hbWUiOiJhZG1pbj" + "MifQ.IZqzZEuwKCVT0acHk3p5DnzPSNxg1tLISt8wZCMAHJAnLSdtbtVibrCTZkTLP5z" + "PD16MgzgsID_CFF2wZXPGBihhyihu1B5W8GimY4eQOKrt4qiLJgK-D8tG6MSZ2K_9DC3" + "RwhMjrNL4lpu2YoSOgugRdKpJWy4zadtHKptFkKrkI8qjnDoDSkF0kt4I6S1xOcEPuVh" + "EOrGsfKr5Bm1N3wX9OVQhcTiVugKrpU8x0Mv1AJYdaxKASOQ6fFlNquwfohgLDwy3By3" + "xU6RoY6ZWhKm5dcGW7H9gqmr9X4aBmHDmYG5KQtodwf0LOYtprPAXCs9X7Ja-7ddJvko" + "8mDObTA"; + +START_TEST(test_jwt_verify_rsa_i37) +{ + SET_OPS(); + __verify_alg_key("rsa_key_i37-pub.pem", jwt_rsa_i37, JWT_ALG_RS256); +} +END_TEST + +START_TEST(test_jwt_encode_rsa_with_ec) +{ + jwt_t *jwt = NULL; + int ret = 0; + char *out; + + SET_OPS(); + + ALLOC_JWT(&jwt); + + read_key("ec_key_secp384r1.pem"); + + ret = jwt_add_grant(jwt, "iss", "files.maclara-llc.com"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "sub", "user0"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant_int(jwt, "iat", TS_CONST); + ck_assert_int_eq(ret, 0); + + ret = jwt_set_alg(jwt, JWT_ALG_RS384, key, key_len); + ck_assert_int_eq(ret, 0); + + out = jwt_encode_str(jwt); + ck_assert_ptr_eq(out, NULL); + ck_assert_int_eq(errno, EINVAL); + + jwt_free(jwt); +} +END_TEST + +START_TEST(test_jwt_verify_invalid_token) +{ + jwt_t *jwt = NULL; + int ret = 0; + + SET_OPS(); + + read_key("rsa_key_2048.pem"); + + ret = jwt_decode(&jwt, jwt_rs256_invalid, key, JWT_ALG_RS512); + ck_assert_int_ne(ret, 0); + ck_assert_ptr_eq(jwt, NULL); +} +END_TEST + +START_TEST(test_jwt_verify_invalid_alg) +{ + jwt_t *jwt = NULL; + int ret = 0; + + SET_OPS(); + + read_key("rsa_key_2048.pem"); + + ret = jwt_decode(&jwt, jwt_rs256_2048, key, JWT_ALG_RS512); + ck_assert_int_ne(ret, 0); + ck_assert_ptr_eq(jwt, NULL); +} +END_TEST + +START_TEST(test_jwt_verify_invalid_cert) +{ + jwt_t *jwt = NULL; + int ret = 0; + + SET_OPS(); + + read_key("rsa_key_8192-pub.pem"); + + ret = jwt_decode(&jwt, jwt_rs256_2048, key, JWT_ALG_RS256); + ck_assert_int_ne(ret, 0); + ck_assert_ptr_eq(jwt, NULL); +} +END_TEST + +START_TEST(test_jwt_verify_invalid_cert_file) +{ + jwt_t *jwt = NULL; + int ret = 0; + + SET_OPS(); + + read_key("rsa_key_invalid-pub.pem"); + + ret = jwt_decode(&jwt, jwt_rs256_2048, key, JWT_ALG_RS256); + ck_assert_int_ne(ret, 0); + ck_assert_ptr_eq(jwt, NULL); +} +END_TEST + +START_TEST(test_jwt_encode_invalid_key) +{ + jwt_t *jwt = NULL; + int ret = 0; + char *out = NULL; + + SET_OPS(); + + ALLOC_JWT(&jwt); + + read_key("rsa_key_invalid.pem"); + + ret = jwt_add_grant(jwt, "iss", "files.maclara-llc.com"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "sub", "user0"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant_int(jwt, "iat", TS_CONST); + ck_assert_int_eq(ret, 0); + + ret = jwt_set_alg(jwt, JWT_ALG_RS512, key, key_len); + ck_assert_int_eq(ret, 0); + + out = jwt_encode_str(jwt); + ck_assert_ptr_eq(out, NULL); + + jwt_free(jwt); +} +END_TEST + +static Suite *libjwt_suite(const char *title) +{ + Suite *s; + TCase *tc_core; + int i = ARRAY_SIZE(jwt_test_ops) - 1; + + s = suite_create(title); + + tc_core = tcase_create("jwt_rsa"); + + tcase_add_loop_test(tc_core, test_jwt_encode_rs256, 0, i); + tcase_add_loop_test(tc_core, test_jwt_verify_rs256, 0, i); + tcase_add_loop_test(tc_core, test_jwt_validate_rs256, 0, i); + tcase_add_loop_test(tc_core, test_jwt_encode_rs384, 0, i); + tcase_add_loop_test(tc_core, test_jwt_verify_rs384, 0, i); + tcase_add_loop_test(tc_core, test_jwt_encode_rs512, 0, i); + tcase_add_loop_test(tc_core, test_jwt_verify_rs512, 0, i); + tcase_add_loop_test(tc_core, test_jwt_verify_rsa_i37, 0, i); + tcase_add_loop_test(tc_core, test_jwt_encode_rsa_with_ec, 0, i); + tcase_add_loop_test(tc_core, test_jwt_verify_invalid_token, 0, i); + tcase_add_loop_test(tc_core, test_jwt_verify_invalid_alg, 0, i); + tcase_add_loop_test(tc_core, test_jwt_verify_invalid_cert, 0, i); + tcase_add_loop_test(tc_core, test_jwt_verify_invalid_cert_file, 0, i); + tcase_add_loop_test(tc_core, test_jwt_encode_invalid_key, 0, i); + + tcase_set_timeout(tc_core, 120); + + suite_add_tcase(s, tc_core); + + return s; +} + +int main(int argc, char *argv[]) +{ + JWT_TEST_MAIN("LibJWT RSA Sign/Verify"); +} diff --git a/tests/jwt_rsa_pss.c b/tests/jwt_rsa_pss.c new file mode 100644 index 00000000..36e92b0d --- /dev/null +++ b/tests/jwt_rsa_pss.c @@ -0,0 +1,172 @@ +/* Public domain, no copyright. Use at your own risk. */ + +#include +#include +#include +#include +#include + +#include "jwt_tests.h" + +static const char jwt_ps256_2048[] = "eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.ey" + "JpYXQiOjE0NzU5ODA1NDUsImlzcyI6ImZpbGVzLm1hY2xhcmEtbGxjLmNvbSIsInJlZi" + "I6IlhYWFgtWVlZWS1aWlpaLUFBQUEtQ0NDQyIsInN1YiI6InVzZXIwIn0.B9gxqtbZae" + "9PyGkjQaBMyBITOieALP39yCDSqmynmvnE2L8JJzNxOKjm5dy_ORhYjagghE18ti90v2" + "whAwRFFvA7MlQC2rQm-4pXrHqAyhT7Dl1_lSeL98WGToZgJ646WLjr-SwbMNjp3RWwZz" + "F-IwnB1D1f-RoA9yUoaNEFHUYVuL4okVj4ImnUE07pW-l2eal3bxUg6lzqGWSctbT46t" + "y8qFlsOyrifev3y_z6-eKPHUruYEbWb1zw3-snBtcPfGMWAQ91PVoNkPLTO6G56I8FAF" + "IufXyyp6k9VuKQ_WRzRQhwO8zBOto4RsTUjYbDJEY2FSFYVZUdPctwojNlCw"; + +static const char jwt_ps384_2048[] = "eyJhbGciOiJQUzM4NCIsInR5cCI6IkpXVCJ9.ey" + "JpYXQiOjE0NzU5ODA1NDUsImlzcyI6ImZpbGVzLm1hY2xhcmEtbGxjLmNvbSIsInJlZi" + "I6IlhYWFgtWVlZWS1aWlpaLUFBQUEtQ0NDQyIsInN1YiI6InVzZXIwIn0.GY8aZobXTy" + "6DzooRUt6vwgBbWwWvTchFtDCVMto_NM68aqT_OI8_X1MAHwE7ppS1S-yxg1aEeGzZEG" + "VEAdeIzswd7ilCpQrUQ2Qcym6SuK3NAKLtr6NyUZwdaEPTeEx3GWQbmvY66hVs7g2o4c" + "luSfp3I4McgLCm-HS5Dl_xHoyV_1ympz_n3n7YDoe5l0EoHaX3-XPMtUvL4kxeMV5pLh" + "72Yj2qNM5Dbbe9F_WSxoeQsyktg8MmPb22LWAAW7uafazr7TinJvPtBhPqT7hc2sUFbA" + "Jui_TSM60Kjfqg15QQELifywNvgW0ZO6xKEI5GKgaIi2S9F2iqQehBBkjMrg"; + +static const char jwt_ps512_2048[] = "eyJhbGciOiJQUzUxMiIsInR5cCI6IkpXVCJ9.ey" + "JpYXQiOjE0NzU5ODA1NDUsImlzcyI6ImZpbGVzLm1hY2xhcmEtbGxjLmNvbSIsInJlZi" + "I6IlhYWFgtWVlZWS1aWlpaLUFBQUEtQ0NDQyIsInN1YiI6InVzZXIwIn0.OxnjxVNAEC" + "xEnNVg6S6sx-JIxOq3sJimEefq4OONsYomWz1TAM8_42bmAnvda0bhC8LTmIogQwnYj3" + "qIYrjef3s7nrs5USS3_ffqeMuog_Xp7cH1YhVwvkXEWzfeT-SLZiEdxGBrPvEASxwzv0" + "CitQrfDGvFe20UXkhAvOKIc_1K5Fzv9IQiaKaPR2Jg8Ub0qQ6qZq1whnwDbjutWCFlW3" + "62UOQbhA2WtE72Q60OFXMr2J0PYrScGgTRRrL6V2G7cNRend14FzDFG586dGUCwp9iKF" + "nCrshFefpaFsOJYHG70Ka6CNIDG4LDiLatjjz1UCtAgbnHfy9qyJEpcJYPWg"; + +static const char jwt_ps256_2048_invalid[] = "eyJhbGciOiJQUzI1NiIsInR5cCI6Ikp" + "XVCJ9.eyJpYXQiOjE0NzU5ODA1NDUsImlzcyI6ImZpbGVzLm1hY2xhcmEtbGxjLmNvbS" + "IsInJlZiI6IlhYWFgtWVlZWS1aWlpaLUFBQUEtQ0NDQyIsInN1YiI6InVzZXIwIn0.WX" + "41yYTKxf6lDg7toDAAnwuLKCUSdEWUsJ-5neEbOPE4l09EEIDW2cjK4NZkAgySgCZHCa" + "NUSn8XaOouoLoEMVua5f0g6U-_-c380KRfmiqFGe39vjHCqiw8j-WkdxHisi7eXw3fvL" + "kp0VoyeWA6Fnp2x-shfHU5Br67Wagp7OgCk-SvVL08xyfvgZr6fzEqc486zdNhQE71Pv" + "in5dRQ75Lg3rr1W8Xmx2zRrFKZALsEwGMhRL7e-x46mt6KF1UlwTYAW6FYoKTrrW62sH" + "OgpgvsIwhE93RfCmJ_xvZNkKrqnB6RxfpHEbZYTS8iAI3va2S8IBEL_pH-2etsr1fqAg"; + +#define RSA_PSS_KEY_PRE "rsa-pss_key_2048" +#define PS_KEY_PRIV RSA_PSS_KEY_PRE ".pem" +#define PS_KEY_PUB RSA_PSS_KEY_PRE "-pub.pem" + +static void __test_rsa_pss_encode(const char *priv_key_file, + const char *pub_key_file, + const jwt_alg_t alg) +{ + jwt_t *jwt; + int ret; + char *out; + + ALLOC_JWT(&jwt); + + read_key(priv_key_file); + + ret = jwt_add_grant(jwt, "iss", "files.maclara-llc.com"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "sub", "user0"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant_int(jwt, "iat", TS_CONST); + ck_assert_int_eq(ret, 0); + + ret = jwt_set_alg(jwt, alg, key, key_len); + ck_assert_int_eq(ret, 0); + + out = jwt_encode_str(jwt); + ck_assert_ptr_ne(out, NULL); + + __verify_alg_key(pub_key_file, out, alg); + + jwt_free_str(out); + jwt_free(jwt); +} + +START_TEST(test_jwt_encode_ps256) +{ + SET_OPS(); + __test_alg_key(JWT_ALG_PS256, PS_KEY_PRIV, PS_KEY_PUB); +} +END_TEST + +START_TEST(test_jwt_encode_ps384) +{ + SET_OPS(); + __test_rsa_pss_encode(PS_KEY_PRIV, PS_KEY_PUB, JWT_ALG_PS384); +} +END_TEST + +START_TEST(test_jwt_encode_ps512) +{ + SET_OPS(); + __test_rsa_pss_encode(PS_KEY_PRIV, PS_KEY_PUB, JWT_ALG_PS512); +} +END_TEST + +START_TEST(test_jwt_verify_ps256) +{ + SET_OPS(); + __verify_alg_key(PS_KEY_PUB, jwt_ps256_2048, JWT_ALG_PS256); +} +END_TEST + +START_TEST(test_jwt_verify_ps384) +{ + SET_OPS(); + __verify_alg_key(PS_KEY_PUB, jwt_ps384_2048, JWT_ALG_PS384); +} +END_TEST + +START_TEST(test_jwt_verify_ps512) +{ + SET_OPS(); + __verify_alg_key(PS_KEY_PUB, jwt_ps512_2048, JWT_ALG_PS512); +} +END_TEST + +START_TEST(test_jwt_verify_invalid_rsa_pss) +{ + jwt_t *jwt = NULL; + int ret = 0; + + SET_OPS(); + + read_key(PS_KEY_PUB); + + ret = jwt_decode(&jwt, jwt_ps256_2048_invalid, key, key_len); + ck_assert_int_ne(ret, 0); + ck_assert_ptr_eq(jwt, NULL); +} +END_TEST + +static Suite *libjwt_suite(const char *title) +{ + Suite *s; + TCase *tc_core; + int i = ARRAY_SIZE(jwt_test_ops) - 1; + + s = suite_create(title); + + tc_core = tcase_create("jwt_rsa_pss"); + + tcase_add_loop_test(tc_core, test_jwt_encode_ps256, 0, i); + tcase_add_loop_test(tc_core, test_jwt_encode_ps384, 0, i); + tcase_add_loop_test(tc_core, test_jwt_encode_ps512, 0, i); + tcase_add_loop_test(tc_core, test_jwt_verify_ps256, 0, i); + tcase_add_loop_test(tc_core, test_jwt_verify_ps384, 0, i); + tcase_add_loop_test(tc_core, test_jwt_verify_ps512, 0, i); + tcase_add_loop_test(tc_core, test_jwt_verify_invalid_rsa_pss, 0, i); + + tcase_set_timeout(tc_core, 120); + + suite_add_tcase(s, tc_core); + + return s; +} + +int main(int argc, char *argv[]) +{ + JWT_TEST_MAIN("LibJWT RSA-PSS Sign/Verify"); +} diff --git a/tests/jwt_tests.h b/tests/jwt_tests.h new file mode 100644 index 00000000..20e24272 --- /dev/null +++ b/tests/jwt_tests.h @@ -0,0 +1,213 @@ +/* Copyright (C) 2024 Ben Collins + This file is part of the JWT C Library + + SPDX-License-Identifier: MPL-2.0 + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef JWT_TESTS_H +#define JWT_TESTS_H + +#include +#include + +#include "config.h" + +/* Compatibility with older libCheck versions */ +/* Older check doesn't have this. */ +#ifndef ck_assert_ptr_ne +#define ck_assert_ptr_ne(X, Y) ck_assert(X != Y) +#define ck_assert_ptr_eq(X, Y) ck_assert(X == Y) +#endif + +#ifndef ck_assert_int_gt +#define ck_assert_int_gt(X, Y) ck_assert(X > Y) +#endif + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) +#endif + +/* Constant time to make tests consistent. */ +#define TS_CONST 1475980545L + +__attribute__((unused)) +static const char *jwt_test_ops[] = { +#ifdef HAVE_OPENSSL + "openssl", +#endif +#ifdef HAVE_GNUTLS + "gnutls", +#endif + NULL +}; + +/* Macro to allocate a new JWT with checks. */ +#define ALLOC_JWT(__jwt) do { \ + int __ret = jwt_new(__jwt); \ + ck_assert_int_eq(__ret, 0); \ + ck_assert_ptr_ne(__jwt, NULL); \ +} while(0) + +#define JWT_TEST_MAIN(__title) ({ \ + int number_failed = 0; \ + SRunner *sr; \ + Suite *s; \ + \ + s = libjwt_suite(__title); \ + sr = srunner_create(s); \ + \ + srunner_run_all(sr, CK_VERBOSE); \ + number_failed += srunner_ntests_failed(sr); \ + srunner_free(sr); \ + \ + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; \ +}) + +#define SET_OPS() ({ \ + ck_assert_int_eq(jwt_set_crypto_ops(jwt_test_ops[_i]), 0); \ + const char *ops = jwt_get_crypto_ops(); \ + ck_assert_str_eq(ops, jwt_test_ops[_i]); \ +}) + +__attribute__((unused)) static unsigned char key[16384]; +__attribute__((unused)) static size_t key_len; + +__attribute__((unused)) +static void read_key(const char *key_file) +{ + FILE *fp = fopen(key_file, "r"); + char *key_path; + int ret = 0; + + ret = asprintf(&key_path, KEYDIR "/%s", key_file); + ck_assert_int_gt(ret, 0); + + fp = fopen(key_path, "r"); + ck_assert_ptr_ne(fp, NULL); + + jwt_free_str(key_path); + + key_len = fread(key, 1, sizeof(key), fp); + ck_assert_int_ne(key_len, 0); + + ck_assert_int_eq(ferror(fp), 0); + + fclose(fp); + + key[key_len] = '\0'; +} + +__attribute__((unused)) +static void __verify_jwt(const char *jwt_str, const jwt_alg_t alg, const char *file) +{ + jwt_t *jwt = NULL; + int ret = 0; + + read_key(file); + + ret = jwt_decode(&jwt, jwt_str, key, key_len); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_ne(jwt, NULL); + + ck_assert(jwt_get_alg(jwt) == alg); + + jwt_free(jwt); +} + +__attribute__((unused)) +static void __test_alg_key(const jwt_alg_t alg, const char *file, const char *pub) +{ + jwt_t *jwt = NULL; + int ret = 0; + char *out; + + ALLOC_JWT(&jwt); + + read_key(file); + + ret = jwt_add_grant(jwt, "iss", "files.maclara-llc.com"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "sub", "user0"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant_int(jwt, "iat", TS_CONST); + ck_assert_int_eq(ret, 0); + + ret = jwt_set_alg(jwt, alg, key, key_len); + ck_assert_int_eq(ret, 0); + + out = jwt_encode_str(jwt); + ck_assert_ptr_ne(out, NULL); + + __verify_jwt(out, alg, pub); + + jwt_free_str(out); + jwt_free(jwt); +} + +__attribute__((unused)) +static void __verify_alg_key(const char *key_file, const char *jwt_str, + const jwt_alg_t alg) +{ + jwt_valid_t *jwt_valid = NULL; + jwt_t *jwt = NULL; + int ret = 0; + + read_key(key_file); + + ret = jwt_decode(&jwt, jwt_str, key, key_len); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt); + + jwt_valid_new(&jwt_valid, alg); + + ret = jwt_validate(jwt, jwt_valid); + ck_assert_int_eq(JWT_VALIDATION_SUCCESS, ret); + + jwt_valid_free(jwt_valid); + jwt_free(jwt); +} + +__attribute__((unused)) +static void __compare_alg_key(const char *key_file, const char *jwt_str, + const jwt_alg_t alg) +{ + jwt_t *jwt = NULL; + int ret = 0; + char *out; + + ALLOC_JWT(&jwt); + + read_key(key_file); + + ret = jwt_add_grant(jwt, "iss", "files.maclara-llc.com"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "sub", "user0"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant(jwt, "ref", "XXXX-YYYY-ZZZZ-AAAA-CCCC"); + ck_assert_int_eq(ret, 0); + + ret = jwt_add_grant_int(jwt, "iat", TS_CONST); + ck_assert_int_eq(ret, 0); + + ret = jwt_set_alg(jwt, alg, key, key_len); + ck_assert_int_eq(ret, 0); + + out = jwt_encode_str(jwt); + ck_assert_ptr_ne(out, NULL); + + ck_assert_str_eq(out, jwt_str); + + jwt_free_str(out); + jwt_free(jwt); +} + +#endif /* JWT_TESTS_H */ diff --git a/tests/jwt_validate.c b/tests/jwt_validate.c new file mode 100644 index 00000000..b5806b39 --- /dev/null +++ b/tests/jwt_validate.c @@ -0,0 +1,677 @@ +/* Public domain, no copyright. Use at your own risk. */ + +#include +#include +#include +#include +#include + +#include "jwt_tests.h" + +static jwt_t *jwt; + +static const time_t iat = TS_CONST; +static const time_t not_before = TS_CONST + 60L; +static const time_t expires = TS_CONST + 600L; + +static void __setup_jwt() +{ + jwt_new(&jwt); + jwt_add_grant(jwt, "iss", "test"); + jwt_add_grant(jwt, "sub", "user0"); + jwt_add_grants_json(jwt, "{\"aud\": [\"svc1\",\"svc2\"]}"); + jwt_add_grant_int(jwt, "iat", iat); + jwt_add_grant_bool(jwt, "admin", 1); + jwt_set_alg(jwt, JWT_ALG_NONE, NULL, 0); +} + +static void __teardown_jwt() +{ + jwt_free(jwt); + jwt = NULL; +} + +#define __VAL_EQ(__v, __e, __str) do { \ + unsigned int __r = jwt_validate(jwt, __v); \ + char *__s; \ + ck_assert_int_eq(__r, __e); \ + __r = jwt_valid_get_status(__v); \ + ck_assert_int_eq(__e, __r); \ + __s = jwt_exception_str(__r); \ + ck_assert_str_eq(__str, __s); \ + jwt_free_str(__s); \ +} while(0); + +START_TEST(test_jwt_validate_errno) +{ + jwt_valid_t *jwt_valid = NULL; + unsigned int ret = 0; + char *exc; + + SET_OPS(); + + ck_assert_ptr_nonnull(jwt); + + ret = jwt_valid_new(&jwt_valid, JWT_ALG_NONE); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt_valid); + + /* Validate fails with NULL jwt */ + ret = jwt_validate(NULL, jwt_valid); + ck_assert_int_eq(ret, JWT_VALIDATION_ERROR); + ret = jwt_valid_get_status(jwt_valid); + ck_assert_int_eq(ret, JWT_VALIDATION_ERROR); + exc = jwt_exception_str(ret); + ck_assert_str_eq(exc, "general failures"); + jwt_free_str(exc); + + + /* Validate fails with NULL jwt_valid */ + ret = jwt_validate(jwt, NULL); + ck_assert_int_eq(ret, JWT_VALIDATION_ERROR); + + /* Get status fails with NULL jwt_valid */ + ck_assert_int_eq(JWT_VALIDATION_ERROR, jwt_valid_get_status(NULL)); + + jwt_valid_free(jwt_valid); +} +END_TEST + +START_TEST(test_jwt_valid_algorithm) +{ + jwt_valid_t *jwt_valid = NULL; + unsigned int ret = 0; + + SET_OPS(); + + /* Matching algorithm is valid */ + ret = jwt_valid_new(&jwt_valid, JWT_ALG_NONE); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt_valid); + + __VAL_EQ(jwt_valid, JWT_VALIDATION_SUCCESS, "success"); + + jwt_valid_free(jwt_valid); + + /* Wrong algorithm is not valid */ + ret = jwt_valid_new(&jwt_valid, JWT_ALG_HS256); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt_valid); + + /* Starts with invalid */ + ck_assert_int_eq(JWT_VALIDATION_ERROR, jwt_valid_get_status(jwt_valid)); + + __VAL_EQ(jwt_valid, JWT_VALIDATION_ALG_MISMATCH, "algorithm mismatch"); + + jwt_valid_free(jwt_valid); +} +END_TEST + +START_TEST(test_jwt_valid_require_grant) +{ + jwt_valid_t *jwt_valid = NULL; + unsigned int ret = 0; + const char *valstr = NULL; + int valnum = 0; + + SET_OPS(); + + /* Valid when alg matches and all required grants match */ + ret = jwt_valid_new(&jwt_valid, JWT_ALG_NONE); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt_valid); + + ret = jwt_valid_add_grant(jwt_valid, "iss", "test"); + ck_assert_int_eq(ret, 0); + + /* No duplicates */ + ret = jwt_valid_add_grant(jwt_valid, "iss", "other"); + ck_assert_int_eq(ret, EEXIST); + + /* Grant has expected value */ + valstr = jwt_valid_get_grant(jwt_valid, "iss"); + ck_assert_ptr_nonnull(valstr); + ck_assert_str_eq(valstr, "test"); + + ret = jwt_valid_add_grant_int(jwt_valid, "iat", (long)iat); + ck_assert_int_eq(ret, 0); + + /* No duplicates for int */ + ret = jwt_valid_add_grant_int(jwt_valid, "iat", (long)time(NULL)); + ck_assert_int_eq(ret, EEXIST); + + /* Grant has expected value */ + valnum = jwt_valid_get_grant_int(jwt_valid, "iat"); + ck_assert_int_eq(valnum, (long)iat); + + ret = jwt_valid_add_grant_bool(jwt_valid, "admin", 1); + ck_assert_int_eq(ret, 0); + + /* No duplicates for bool */ + ret = jwt_valid_add_grant_bool(jwt_valid, "admin", 0); + ck_assert_int_eq(ret, EEXIST); + + /* Grant has expected value */ + valnum = jwt_valid_get_grant_bool(jwt_valid, "admin"); + ck_assert_int_eq(valnum, 1); + + ret = jwt_valid_add_grants_json(jwt_valid, "{\"aud\": [\"svc1\",\"svc2\"]}"); + ck_assert_int_eq(ret, 0); + + __VAL_EQ(jwt_valid, JWT_VALIDATION_SUCCESS, "success"); + + jwt_valid_free(jwt_valid); +} +END_TEST + +START_TEST(test_jwt_valid_nonmatch_grant) +{ + jwt_valid_t *jwt_valid = NULL; + unsigned int ret = 0; + + SET_OPS(); + + ret = jwt_valid_new(&jwt_valid, JWT_ALG_NONE); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt_valid); + + /* Invalid when required grants don't match */ + ret = jwt_valid_add_grant(jwt_valid, "iss", "wrong"); + ck_assert_int_eq(ret, 0); + + __VAL_EQ(jwt_valid, JWT_VALIDATION_GRANT_MISMATCH, "grant mismatch"); + + jwt_valid_del_grants(jwt_valid, NULL); + + /* Invalid when required grants don't match (int) */ + ret = jwt_valid_add_grant_int(jwt_valid, "iat", (long)time(NULL) + 1); + ck_assert_int_eq(ret, 0); + + __VAL_EQ(jwt_valid, JWT_VALIDATION_GRANT_MISMATCH, "grant mismatch"); + + jwt_valid_del_grants(jwt_valid, NULL); + + /* Invalid when required grants don't match (bool) */ + ret = jwt_valid_add_grant_bool(jwt_valid, "admin", 0); + ck_assert_int_eq(ret, 0); + + __VAL_EQ(jwt_valid, JWT_VALIDATION_GRANT_MISMATCH, "grant mismatch"); + + jwt_valid_del_grants(jwt_valid, NULL); + + /* Invalid when required grants don't match (json) */ + ret = jwt_valid_add_grants_json(jwt_valid, "{\"aud\": [\"svc3\",\"svc4\"]}"); + ck_assert_int_eq(ret, 0); + + __VAL_EQ(jwt_valid, JWT_VALIDATION_GRANT_MISMATCH, "grant mismatch"); + + jwt_valid_free(jwt_valid); +} +END_TEST + +START_TEST(test_jwt_valid_grant_bool) +{ + jwt_valid_t *jwt_valid = NULL; + int val; + unsigned int ret = 0; + + SET_OPS(); + + ret = jwt_valid_new(&jwt_valid, JWT_ALG_NONE); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt_valid); + + ret = jwt_valid_add_grant_bool(jwt_valid, "admin", 1); + ck_assert_int_eq(ret, 0); + + val = jwt_valid_get_grant_bool(jwt_valid, "admin"); + ck_assert(val); + + ret = jwt_valid_add_grant_bool(jwt_valid, "test", 0); + ck_assert_int_eq(ret, 0); + + val = jwt_valid_get_grant_bool(jwt_valid, "test"); + ck_assert(!val); + + val = jwt_valid_get_grant_bool(jwt_valid, "not found"); + ck_assert_int_eq(errno, ENOENT); + + jwt_valid_free(jwt_valid); +} +END_TEST + +START_TEST(test_jwt_valid_del_grants) +{ + jwt_valid_t *jwt_valid = NULL; + const char *val; + const char testval[] = "testing"; + unsigned int ret = 0; + + SET_OPS(); + + ret = jwt_valid_new(&jwt_valid, JWT_ALG_NONE); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt_valid); + + ret = jwt_valid_add_grant(jwt_valid, "iss", testval); + ck_assert_int_eq(ret, 0); + + ret = jwt_valid_add_grant(jwt_valid, "other", testval); + ck_assert_int_eq(ret, 0); + + ret = jwt_valid_del_grants(jwt_valid, "iss"); + ck_assert_int_eq(ret, 0); + + val = jwt_valid_get_grant(jwt_valid, "iss"); + ck_assert_ptr_null(val); + + /* Delete non existent. */ + ret = jwt_valid_del_grants(jwt_valid, "iss"); + ck_assert_int_eq(ret, 0); + + /* Delete all grants. */ + ret = jwt_valid_del_grants(jwt_valid, NULL); + ck_assert_int_eq(ret, 0); + + val = jwt_valid_get_grant(jwt_valid, "other"); + ck_assert_ptr_null(val); + + jwt_valid_free(jwt_valid); +} +END_TEST + +START_TEST(test_jwt_valid_invalid_grant) +{ + jwt_valid_t *jwt_valid = NULL; + const char *val; + long valint = 0; + long valbool = 0; + unsigned int ret = 0; + + SET_OPS(); + + ret = jwt_valid_new(&jwt_valid, JWT_ALG_NONE); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt_valid); + + ret = jwt_valid_add_grant(jwt_valid, "iss", NULL); + ck_assert_int_eq(ret, EINVAL); + + ret = jwt_valid_add_grant_int(jwt_valid, "", (long)time(NULL)); + ck_assert_int_eq(ret, EINVAL); + + val = jwt_valid_get_grant(jwt_valid, NULL); + ck_assert_int_eq(errno, EINVAL); + ck_assert_ptr_null(val); + + valint = jwt_valid_get_grant_int(jwt_valid, NULL); + ck_assert_int_eq(errno, EINVAL); + ck_assert(valint == 0); + + valbool = jwt_valid_get_grant_bool(jwt_valid, NULL); + ck_assert_int_eq(errno, EINVAL); + ck_assert(valbool == 0); + + jwt_valid_free(jwt_valid); +} +END_TEST + +START_TEST(test_jwt_valid_missing_grant) +{ + jwt_valid_t *jwt_valid = NULL; + unsigned int ret = 0; + + SET_OPS(); + + ret = jwt_valid_new(&jwt_valid, JWT_ALG_NONE); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt_valid); + + /* JWT is invalid when required grants are not present */ + ret = jwt_valid_add_grant(jwt_valid, "np-str", "test"); + ck_assert_int_eq(ret, 0); + __VAL_EQ(jwt_valid, JWT_VALIDATION_GRANT_MISSING, "grant missing"); + + jwt_valid_del_grants(jwt_valid, NULL); + + /* JWT is invalid when required grants are not present (int) */ + ret = jwt_valid_add_grant_int(jwt_valid, "np-int", 7); + ck_assert_int_eq(ret, 0); + __VAL_EQ(jwt_valid, JWT_VALIDATION_GRANT_MISSING, "grant missing"); + + jwt_valid_del_grants(jwt_valid, NULL); + + /* JWT is invalid when required grants are not present (bool) */ + ret = jwt_valid_add_grant_int(jwt_valid, "np-bool", 1); + ck_assert_int_eq(ret, 0); + __VAL_EQ(jwt_valid, JWT_VALIDATION_GRANT_MISSING, "grant missing"); + + jwt_valid_del_grants(jwt_valid, NULL); + + /* JWT is invalid when required grants are not present (json) */ + ret = jwt_valid_add_grants_json(jwt_valid, "{\"np-other\": [\"foo\",\"bar\"]}"); + ck_assert_int_eq(ret, 0); + __VAL_EQ(jwt_valid, JWT_VALIDATION_GRANT_MISSING, "grant missing"); + + jwt_valid_free(jwt_valid); +} +END_TEST + +START_TEST(test_jwt_valid_not_before) +{ + jwt_valid_t *jwt_valid = NULL; + unsigned int ret = 0; + + SET_OPS(); + + jwt_add_grant_int(jwt, "nbf", not_before); + + ret = jwt_valid_new(&jwt_valid, JWT_ALG_NONE); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt_valid); + + /* JWT is invalid when now < not-before */ + ret = jwt_valid_set_now(jwt_valid, not_before - 1); + ck_assert_int_eq(ret, 0); + + __VAL_EQ(jwt_valid, JWT_VALIDATION_TOO_NEW, "token future dated"); + + /* JWT is valid when now >= not-before */ + ret = jwt_valid_set_now(jwt_valid, not_before); + ck_assert_int_eq(ret, 0); + + __VAL_EQ(jwt_valid, JWT_VALIDATION_SUCCESS, "success"); + + jwt_valid_free(jwt_valid); +} +END_TEST + +START_TEST(test_jwt_valid_set_nbf_leeway) +{ + jwt_valid_t *jwt_valid = NULL; + unsigned int ret = 0; + + SET_OPS(); + + ret = jwt_valid_new(&jwt_valid, JWT_ALG_NONE); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt_valid); + + /* 0 by default */ + time_t init_nbf_leeway = jwt_valid_get_nbf_leeway(jwt_valid); + ck_assert_int_eq(init_nbf_leeway, 0); + + /* Setting nbf_leeway */ + ret = jwt_valid_set_nbf_leeway(jwt_valid, 1); + ck_assert_int_eq(ret, 0); + + time_t set_nbf_leeway = jwt_valid_get_nbf_leeway(jwt_valid); + ck_assert_int_eq(set_nbf_leeway, 1); + + jwt_valid_free(jwt_valid); +} +END_TEST + +START_TEST(test_jwt_valid_not_before_leeway) +{ + jwt_valid_t *jwt_valid = NULL; + unsigned int ret = 0; + + SET_OPS(); + + jwt_add_grant_int(jwt, "nbf", not_before); + + ret = jwt_valid_new(&jwt_valid, JWT_ALG_NONE); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt_valid); + + /* Setting nbf_leeway */ + ret = jwt_valid_set_nbf_leeway(jwt_valid, 10); + ck_assert_int_eq(ret, 0); + + /* JWT is invalid when now < not-before - nbf_leeway */ + ret = jwt_valid_set_now(jwt_valid, (long)not_before - 15); + ck_assert_int_eq(ret, 0); + + __VAL_EQ(jwt_valid, JWT_VALIDATION_TOO_NEW, "token future dated"); + + /* JWT is valid when now >= not-before - nbf_leeway */ + ret = jwt_valid_set_now(jwt_valid, (long)not_before - 5); + ck_assert_int_eq(ret, 0); + + __VAL_EQ(jwt_valid, JWT_VALIDATION_SUCCESS, "success"); + + jwt_valid_free(jwt_valid); +} +END_TEST + +START_TEST(test_jwt_valid_expires) +{ + jwt_valid_t *jwt_valid = NULL; + unsigned int ret = 0; + + SET_OPS(); + + jwt_add_grant_int(jwt, "exp", expires); + + ret = jwt_valid_new(&jwt_valid, JWT_ALG_NONE); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt_valid); + + /* JWT is valid when now < expires */ + ret = jwt_valid_set_now(jwt_valid, (long)expires - 1); + ck_assert_int_eq(ret, 0); + + __VAL_EQ(jwt_valid, JWT_VALIDATION_SUCCESS, "success"); + + /* JWT is invalid when now >= expires */ + ret = jwt_valid_set_now(jwt_valid, (long)expires); + ck_assert_int_eq(ret, 0); + + __VAL_EQ(jwt_valid, JWT_VALIDATION_EXPIRED, "token expired"); + + jwt_valid_free(jwt_valid); +} +END_TEST + +START_TEST(test_jwt_valid_set_exp_leeway) +{ + jwt_valid_t *jwt_valid = NULL; + unsigned int ret = 0; + + SET_OPS(); + + ret = jwt_valid_new(&jwt_valid, JWT_ALG_NONE); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt_valid); + + /* 0 by default */ + time_t init_exp_leeway = jwt_valid_get_exp_leeway(jwt_valid); + ck_assert_int_eq(init_exp_leeway, 0); + + /* Setting exp_leeway */ + ret = jwt_valid_set_exp_leeway(jwt_valid, 1); + ck_assert_int_eq(ret, 0); + + time_t set_exp_leeway = jwt_valid_get_exp_leeway(jwt_valid); + ck_assert_int_eq(set_exp_leeway, 1); + + jwt_valid_free(jwt_valid); +} +END_TEST + +START_TEST(test_jwt_valid_expires_leeway) +{ + jwt_valid_t *jwt_valid = NULL; + unsigned int ret = 0; + + SET_OPS(); + + jwt_add_grant_int(jwt, "exp", expires); + + ret = jwt_valid_new(&jwt_valid, JWT_ALG_NONE); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt_valid); + + /* Setting exp_leeway */ + ret = jwt_valid_set_exp_leeway(jwt_valid, 10); + ck_assert_int_eq(ret, 0); + + /* JWT is valid when now < expires + exp_leeway */ + ret = jwt_valid_set_now(jwt_valid, (long)expires + 5); + ck_assert_int_eq(ret, 0); + + __VAL_EQ(jwt_valid, JWT_VALIDATION_SUCCESS, "success"); + + /* JWT is invalid when now >= expires + exp_leeway */ + ret = jwt_valid_set_now(jwt_valid, (long)expires + 15); + ck_assert_int_eq(ret, 0); + + __VAL_EQ(jwt_valid, JWT_VALIDATION_EXPIRED, "token expired"); + + jwt_valid_free(jwt_valid); +} +END_TEST + +START_TEST(test_jwt_valid_headers) +{ + jwt_valid_t *jwt_valid = NULL; + unsigned int ret = 0; + + SET_OPS(); + + ret = jwt_valid_new(&jwt_valid, JWT_ALG_NONE); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt_valid); + + ret = jwt_valid_set_headers(jwt_valid, 1); + ck_assert_int_eq(ret, 0); + + /* JWT is valid when iss in hdr matches iss in body */ + jwt_add_header(jwt, "iss", "test"); + __VAL_EQ(jwt_valid, JWT_VALIDATION_SUCCESS, "success"); + + /* JWT is invalid when iss in hdr does not match iss in body */ + jwt_del_headers(jwt, "iss"); + jwt_add_header(jwt, "iss", "wrong"); + __VAL_EQ(jwt_valid, JWT_VALIDATION_ISS_MISMATCH, "issuer mismatch"); + + /* JWT is valid when checking hdr and iss not replicated */ + jwt_del_headers(jwt, "iss"); + __VAL_EQ(jwt_valid, JWT_VALIDATION_SUCCESS, "success"); + + /* JWT is valid when iss in hdr matches iss in body */ + jwt_add_header(jwt, "sub", "user0"); + __VAL_EQ(jwt_valid, JWT_VALIDATION_SUCCESS, "success"); + + /* JWT is invalid when iss in hdr does not match iss in body */ + jwt_del_headers(jwt, "sub"); + jwt_add_header(jwt, "sub", "wrong"); + __VAL_EQ(jwt_valid, JWT_VALIDATION_SUB_MISMATCH, "subject mismatch"); + + /* JWT is valid when checking hdr and sub not replicated */ + jwt_del_headers(jwt, "sub"); + __VAL_EQ(jwt_valid, JWT_VALIDATION_SUCCESS, "success"); + + /* JWT is valid when checking hdr and aud matches */ + jwt_add_headers_json(jwt, "{\"aud\": [\"svc1\",\"svc2\"]}"); + __VAL_EQ(jwt_valid, JWT_VALIDATION_SUCCESS, "success"); + + /* JWT is invalid when checking hdr and aud does not match */ + jwt_del_headers(jwt, "aud"); + jwt_add_headers_json(jwt, "{\"aud\": [\"svc1\",\"svc2\",\"svc3\"]}"); + __VAL_EQ(jwt_valid, JWT_VALIDATION_AUD_MISMATCH, "audience mismatch"); + + /* JWT is invalid when checking hdr and aud does not match */ + jwt_del_headers(jwt, "aud"); + __VAL_EQ(jwt_valid, JWT_VALIDATION_SUCCESS, "success"); + + jwt_valid_free(jwt_valid); +} +END_TEST + +START_TEST(test_jwt_valid_grants_json) +{ + const char *json = "{\"id\":\"FVvGYTr3FhiURCFebsBOpBqTbzHdX/DvImiA2yheXr8=\"," + "\"iss\":\"localhost\",\"other\":[\"foo\",\"bar\"]," + "\"ref\":\"385d6518-fb73-45fc-b649-0527d8576130\"," + "\"scopes\":\"storage\",\"sub\":\"user0\"}"; + jwt_valid_t *jwt_valid = NULL; + const char *val; + char *json_val; + unsigned int ret = 0; + + SET_OPS(); + + ret = jwt_valid_new(&jwt_valid, JWT_ALG_NONE); + ck_assert_int_eq(ret, 0); + ck_assert_ptr_nonnull(jwt_valid); + + ret = jwt_valid_add_grants_json(jwt_valid, json); + ck_assert_int_eq(ret, 0); + + val = jwt_valid_get_grant(jwt_valid, "ref"); + ck_assert_ptr_nonnull(val); + ck_assert_str_eq(val, "385d6518-fb73-45fc-b649-0527d8576130"); + + json_val = jwt_valid_get_grants_json(NULL, "other"); + ck_assert_ptr_null(json_val); + ck_assert_int_eq(errno, EINVAL); + + json_val = jwt_valid_get_grants_json(jwt_valid, "other"); + ck_assert_ptr_nonnull(json_val); + ck_assert_str_eq(json_val, "[\"foo\",\"bar\"]"); + + jwt_free_str(json_val); + + json_val = jwt_valid_get_grants_json(jwt_valid, NULL); + ck_assert_ptr_nonnull(json_val); + ck_assert_str_eq(json_val, json); + + jwt_free_str(json_val); + + jwt_valid_free(jwt_valid); +} +END_TEST + +static Suite *libjwt_suite(const char *title) +{ + Suite *s; + TCase *tc_core; + int i = ARRAY_SIZE(jwt_test_ops) - 1; + + s = suite_create(title); + + tc_core = tcase_create("jwt_validate"); + + /* Run before and after each unit test. */ + tcase_add_checked_fixture(tc_core, __setup_jwt, __teardown_jwt); + + tcase_add_loop_test(tc_core, test_jwt_validate_errno, 0, i); + tcase_add_loop_test(tc_core, test_jwt_valid_algorithm, 0, i); + tcase_add_loop_test(tc_core, test_jwt_valid_require_grant, 0, i); + tcase_add_loop_test(tc_core, test_jwt_valid_nonmatch_grant, 0, i); + tcase_add_loop_test(tc_core, test_jwt_valid_invalid_grant, 0, i); + tcase_add_loop_test(tc_core, test_jwt_valid_missing_grant, 0, i); + tcase_add_loop_test(tc_core, test_jwt_valid_grant_bool, 0, i); + tcase_add_loop_test(tc_core, test_jwt_valid_grants_json, 0, i); + tcase_add_loop_test(tc_core, test_jwt_valid_del_grants, 0, i); + tcase_add_loop_test(tc_core, test_jwt_valid_not_before, 0, i); + tcase_add_loop_test(tc_core, test_jwt_valid_set_nbf_leeway, 0, i); + tcase_add_loop_test(tc_core, test_jwt_valid_not_before_leeway, 0, i); + tcase_add_loop_test(tc_core, test_jwt_valid_expires, 0, i); + tcase_add_loop_test(tc_core, test_jwt_valid_set_exp_leeway, 0, i); + tcase_add_loop_test(tc_core, test_jwt_valid_expires_leeway, 0, i); + tcase_add_loop_test(tc_core, test_jwt_valid_headers, 0, i); + + tcase_set_timeout(tc_core, 30); + + suite_add_tcase(s, tc_core); + + return s; +} + +int main(int argc, char *argv[]) +{ + JWT_TEST_MAIN("LibJWT Validate"); +} diff --git a/tests/keys/ec_key_invalid-pub.pem b/tests/keys/ec_key_invalid-pub.pem new file mode 100644 index 00000000..9ab326f2 --- /dev/null +++ b/tests/keys/ec_key_invalid-pub.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MHYwEAYIAmCornholioFK4EEACIDYgAEomxC9ycc8AkXSwWQpu1kN5Fmgy/sD/KJ +0eJg79PvXuLiVUH98cBL/o8sDVB/sGzz +-----END PUBLIC KEY----- diff --git a/tests/keys/ec_key_invalid.pem b/tests/keys/ec_key_invalid.pem new file mode 100644 index 00000000..cd3382ac --- /dev/null +++ b/tests/keys/ec_key_invalid.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDBeLCgapjZmvTatMHaYX3A02+0Ys3Tr8kda+E9DFnmCSiCOEig519fT +13edeU8YdDugBwIAmCornholioNiAASibEL3JxzwCRdLBZCm7WQ3kWaDL+wP8omo +4mDv0+9e4uJVQf3xwEv+jywNUH+wbPM= +-----END EC PRIVATE KEY----- diff --git a/tests/keys/ec_key_prime256v1-pub.pem b/tests/keys/ec_key_prime256v1-pub.pem new file mode 100644 index 00000000..189ca422 --- /dev/null +++ b/tests/keys/ec_key_prime256v1-pub.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEY++DdSpCZ5oF3j//h+SdNJIwvB5a +I4AXzpRErGUjWrP9tJMJeUN5T6llls4oNRUsA2ylIh64pl9Mzyu1cUb9FQ== +-----END PUBLIC KEY----- diff --git a/tests/keys/ec_key_prime256v1.pem b/tests/keys/ec_key_prime256v1.pem new file mode 100644 index 00000000..023c0657 --- /dev/null +++ b/tests/keys/ec_key_prime256v1.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgwDvSCv8kxkqIVTYP +pgEm4Efs6lKzf+95W8Qej4F1yEOhRANCAARj74N1KkJnmgXeP/+H5J00kjC8Hloj +gBfOlESsZSNas/20kwl5Q3lPqWWWzig1FSwDbKUiHrimX0zPK7VxRv0V +-----END PRIVATE KEY----- diff --git a/tests/keys/ec_key_secp256k1-pub.pem b/tests/keys/ec_key_secp256k1-pub.pem new file mode 100644 index 00000000..93791c1a --- /dev/null +++ b/tests/keys/ec_key_secp256k1-pub.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE7xdE1j+vQLoWBnhP7Mn70yNhPDCO6pyt +CqMJTeJ7/93v3lmjazz3WX0TslvnuD0IS9FUaT8snsfLSLHMZxXxQQ== +-----END PUBLIC KEY----- diff --git a/tests/keys/ec_key_secp256k1.pem b/tests/keys/ec_key_secp256k1.pem new file mode 100644 index 00000000..899af267 --- /dev/null +++ b/tests/keys/ec_key_secp256k1.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgzTih0G/1ZdZupZhb7SgO +yqsoe2dudSWNPtM4Vt2OYJmhRANCAATvF0TWP69AuhYGeE/syfvTI2E8MI7qnK0K +owlN4nv/3e/eWaNrPPdZfROyW+e4PQhL0VRpPyyex8tIscxnFfFB +-----END PRIVATE KEY----- diff --git a/tests/keys/ec_key_secp384r1-pub.pem b/tests/keys/ec_key_secp384r1-pub.pem new file mode 100644 index 00000000..13e9199e --- /dev/null +++ b/tests/keys/ec_key_secp384r1-pub.pem @@ -0,0 +1,5 @@ +-----BEGIN PUBLIC KEY----- +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEomxC9ycc8AkXSwWQpu1kN5Fmgy/sD/KJ +qN3tlSZmUEZ3w3c6KYJfK97PMOSZQaUdeydBoq/IOglQQOj8zLqubq5IpaaUiDQ5 +0eJg79PvXuLiVUH98cBL/o8sDVB/sGzz +-----END PUBLIC KEY----- diff --git a/tests/keys/ec_key_secp384r1.pem b/tests/keys/ec_key_secp384r1.pem new file mode 100644 index 00000000..e3b3424d --- /dev/null +++ b/tests/keys/ec_key_secp384r1.pem @@ -0,0 +1,6 @@ +-----BEGIN PRIVATE KEY----- +MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDBeLCgapjZmvTatMHaY +X3A02+0Ys3Tr8kda+E9DFnmCSiCOEig519fT13edeU8YdDuhZANiAASibEL3Jxzw +CRdLBZCm7WQ3kWaDL+wP8omo3e2VJmZQRnfDdzopgl8r3s8w5JlBpR17J0Gir8g6 +CVBA6PzMuq5urkilppSINDnR4mDv0+9e4uJVQf3xwEv+jywNUH+wbPM= +-----END PRIVATE KEY----- diff --git a/tests/keys/ec_key_secp521r1-pub.pem b/tests/keys/ec_key_secp521r1-pub.pem new file mode 100644 index 00000000..51c28eb1 --- /dev/null +++ b/tests/keys/ec_key_secp521r1-pub.pem @@ -0,0 +1,6 @@ +-----BEGIN PUBLIC KEY----- +MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQA/axE26pXWXesAjcTP/2Tfe4EcF4A +3LuqgpIFzrftiztViq0+5deUvfcxuPIFk+ANVinlAOzgZWpFS0kheI7KJAYA3fOH +n5ZTU08AAjau0CoZe9GSPUC4cnSy1nqetiKBW0YpBvhaY5FXnngvfHUHdmkFSVLC +S6N+LXoi/dm0Fbo6snE= +-----END PUBLIC KEY----- diff --git a/tests/keys/ec_key_secp521r1.pem b/tests/keys/ec_key_secp521r1.pem new file mode 100644 index 00000000..62f86599 --- /dev/null +++ b/tests/keys/ec_key_secp521r1.pem @@ -0,0 +1,8 @@ +-----BEGIN PRIVATE KEY----- +MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIAp6rxb2PoAISjCCTx +pTQOxv5arJ/N6Xibr0eyOAnlWcVk34m1W5323/6TcPGTtFQgEX9TWjNcp9W8HIuI +yRdLnsKhgYkDgYYABAD9rETbqldZd6wCNxM//ZN97gRwXgDcu6qCkgXOt+2LO1WK +rT7l15S99zG48gWT4A1WKeUA7OBlakVLSSF4jsokBgDd84efllNTTwACNq7QKhl7 +0ZI9QLhydLLWep62IoFbRikG+FpjkVeeeC98dQd2aQVJUsJLo34teiL92bQVujqy +cQ== +-----END PRIVATE KEY----- diff --git a/tests/keys/eddsa_key_ed25519-pub.pem b/tests/keys/eddsa_key_ed25519-pub.pem new file mode 100644 index 00000000..e99b0224 --- /dev/null +++ b/tests/keys/eddsa_key_ed25519-pub.pem @@ -0,0 +1,3 @@ +-----BEGIN PUBLIC KEY----- +MCowBQYDK2VwAyEAHUj+14kN6N4i5qNVkfEhwKiCf+tSrvRHstQdtV8a5QM= +-----END PUBLIC KEY----- diff --git a/tests/keys/eddsa_key_ed25519.pem b/tests/keys/eddsa_key_ed25519.pem new file mode 100644 index 00000000..e6f9cf18 --- /dev/null +++ b/tests/keys/eddsa_key_ed25519.pem @@ -0,0 +1,3 @@ +-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEIF2OaFGahllWYV+ydPYRvq0ZwCl4sNYP2gYfgXOdimf2 +-----END PRIVATE KEY----- diff --git a/tests/keys/rsa-pss_key_2048-pub.pem b/tests/keys/rsa-pss_key_2048-pub.pem new file mode 100644 index 00000000..d4627db0 --- /dev/null +++ b/tests/keys/rsa-pss_key_2048-pub.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIDALBgkqhkiG9w0BAQoDggEPADCCAQoCggEBAJNh/p3OqXB3jxM+5YgscuLG +FwW65LN44Jk8kKN58H6RCnnGz7vKq2l4OToI5vS83yGqZNyZv3hbWPsxIaoDkw4q +XP2l0YU2YO9QB4fhoBexLWraw8F7NArOjPnYRdDGaYKq1LdkYJIzA9wn4M7XyVS4 +uijnhHMH2e8247rxBnO6b0K4rzZLL6cZTlVQ2LRdKP5BFjRiQeHY6+YiXpNmTjty +I94j8N2wcf5wTuKdjV2+4/JhXJ288gATTxsK/eEsl5lkzYbTGFK32KY1nAae/R+h +cVx0btCWWo2AiHveUa8UNty9cwSGcIQ9VSYGFRnYTHwfZn26OxZsQ8GGgJg6DWEC +AwEAAQ== +-----END PUBLIC KEY----- diff --git a/tests/keys/rsa-pss_key_2048.pem b/tests/keys/rsa-pss_key_2048.pem new file mode 100644 index 00000000..f568856b --- /dev/null +++ b/tests/keys/rsa-pss_key_2048.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEugIBADALBgkqhkiG9w0BAQoEggSmMIIEogIBAAKCAQEAk2H+nc6pcHePEz7l +iCxy4sYXBbrks3jgmTyQo3nwfpEKecbPu8qraXg5Ogjm9LzfIapk3Jm/eFtY+zEh +qgOTDipc/aXRhTZg71AHh+GgF7EtatrDwXs0Cs6M+dhF0MZpgqrUt2RgkjMD3Cfg +ztfJVLi6KOeEcwfZ7zbjuvEGc7pvQrivNksvpxlOVVDYtF0o/kEWNGJB4djr5iJe +k2ZOO3Ij3iPw3bBx/nBO4p2NXb7j8mFcnbzyABNPGwr94SyXmWTNhtMYUrfYpjWc +Bp79H6FxXHRu0JZajYCIe95RrxQ23L1zBIZwhD1VJgYVGdhMfB9mfbo7FmxDwYaA +mDoNYQIDAQABAoIBAAx46zobYZwHbVlRf5ovbMXp9P8CpG6kImZ4liLt44WXRgqT +lRCm1sYbqvgeBb5QKq/a9bzBDbXxQQ2TSFTuoLS+4aGDgibvhaXq7cafHodMWQ2x +/evzPntScUkf3ZsMsQqIsdMW3W2x5h5s74NObP0y9TbkWzSWEjVCV2S1sStjSppc +PpT0W9+tJ+x+ShUlxAjVgbCPa5Cbj7zvx5hGKLYgKLacZQtzfXGJhXPhvluiR03B +ewkdg2TmrhNzCOYyaWvqsy1yigqYU6vj5G3jNI8IF3IkNdUWgt/SlcuGKrlg06bY +pW+C4BkAjA6lSHluVZwQZobSbwCYT6PxOUSvjAsCgYEAy6sKp6asFY+kv8hFEh/M +Px+eTmZqr+g+CDEq7xgap9fI4LFGvemJv3isQcYJjPAFWreB0PF3uz6XlcK6x1Cw +jqWlNecQYUmDWq0kyJSvQ7Gn2txsIF6An5lkhSG/AQ+qLWay/WPmyjPXsxaF9aFM ++QjTSW12wWcIKGCP0KSrhsMCgYEAuUCYSXKi+4h0Z43Ol/jqy1mCD+yaICt4CtJe +ZTMbC7CI9qVPUaNHni3fUBxTDgOdH6xJGMZ1XuUFn68fjPB/mPmKUimYCrqlBzhR +h7axT6tdD4WQc8rpEHvaucGjp2dBXrCDve/RPyMI8HYRS5kKGmvqhVpnwT0iVq5K +RCERgQsCgYAcUTOyC6JxfP9TCu1Pj8w+Go9QJz0+J/Q3/14Q0DEklQQA+aT0Urws +OwL4Qiihx912t3z9KBWVmyZIVELT44woyS8nR3EnlakFun0axlF4AANukqcV+Q25 +IwKu3/pGk379wQGx6Wn1QzACXmdRt5OULH3z/5w7V6/yrTpRsBOJMQKBgG5hD5wL +wxLOjlU5SFSCyxuY3Icyee8bF26TRA1WcxH3ANS9UNKa0ExulrV7+vzJqHA7zl4F +ud9t5Hjt9oWastxqK0GjaOVTqorvwpxzfZizyUI7WgO0bkQVGBFAUIupWWjbEdQ1 +NV9CN8xW/R0nNb7ieB027cVU8MXnGXcYoP5bAoGARhNgmhA5ZNiHU0DoDoMzeaXV +pwGxUZ/QkAmC1mc11SROp0khJmM+XfQBW5x6f8sYmX0zAj9EUh01dk1cKrg0dPX4 +2c5nYItgo6ynFSJoSUkl/nu4tJysoCSh+3HtUtstooogDPQKUf4A9UAzgf1vrl95 +SRl+oL8MaVXlHm/o1KQ= +-----END PRIVATE KEY----- diff --git a/tests/keys/rsa_key_2048-pub.pem b/tests/keys/rsa_key_2048-pub.pem new file mode 100644 index 00000000..01f59bf4 --- /dev/null +++ b/tests/keys/rsa_key_2048-pub.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwtpMAM4l1H995oqlqdMh +uqNuffp4+4aUCwuFE9B5s9MJr63gyf8jW0oDr7Mb1Xb8y9iGkWfhouZqNJbMFry+ +iBs+z2TtJF06vbHQZzajDsdux3XVfXv9v6dDIImyU24MsGNkpNt0GISaaiqv51NM +ZQX0miOXXWdkQvWTZFXhmsFCmJLE67oQFSar4hzfAaCulaMD+b3Mcsjlh0yvSq7g +6swiIasEU3qNLKaJAZEzfywroVYr3BwM1IiVbQeKgIkyPS/85M4Y6Ss/T+OWi1Oe +K49NdYBvFP+hNVEoeZzJz5K/nd6C35IX0t2bN5CVXchUFmaUMYk2iPdhXdsC720t +BwIDAQAB +-----END PUBLIC KEY----- diff --git a/tests/keys/rsa_key_2048.pem b/tests/keys/rsa_key_2048.pem new file mode 100644 index 00000000..0f58120a --- /dev/null +++ b/tests/keys/rsa_key_2048.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDC2kwAziXUf33m +iqWp0yG6o259+nj7hpQLC4UT0Hmz0wmvreDJ/yNbSgOvsxvVdvzL2IaRZ+Gi5mo0 +lswWvL6IGz7PZO0kXTq9sdBnNqMOx27HddV9e/2/p0MgibJTbgywY2Sk23QYhJpq +Kq/nU0xlBfSaI5ddZ2RC9ZNkVeGawUKYksTruhAVJqviHN8BoK6VowP5vcxyyOWH +TK9KruDqzCIhqwRTeo0spokBkTN/LCuhVivcHAzUiJVtB4qAiTI9L/zkzhjpKz9P +45aLU54rj011gG8U/6E1USh5nMnPkr+d3oLfkhfS3Zs3kJVdyFQWZpQxiTaI92Fd +2wLvbS0HAgMBAAECggEAD8dTnkETSSjlzhRuI9loAtAXM3Zj86JLPLW7GgaoxEoT +n7lJ2bGicFMHB2ROnbOb9vnas82gtOtJsGaBslmoaCckp/C5T1eJWTEb+i+vdpPp +wZcmKZovyyRFSE4+NYlU17fEv6DRvuaGBpDcW7QgHJIl45F8QWEM+msee2KE+V4G +z/9vAQ+sOlvsb4mJP1tJIBx9Lb5loVREwCRy2Ha9tnWdDNar8EYkOn8si4snPT+E +3ZCy8mlcZyUkZeiS/HdtydxZfoiwrSRYamd1diQpPhWCeRteQ802a7ds0Y2YzgfF +UaYjNuRQm7zA//hwbXS7ELPyNMU15N00bajlG0tUOQKBgQDnLy01l20OneW6A2cI +DIDyYhy5O7uulsaEtJReUlcjEDMkin8b767q2VZHb//3ZH+ipnRYByUUyYUhdOs2 +DYRGGeAebnH8wpTT4FCYxUsIUpDfB7RwfdBONgaKewTJz/FPswy1Ye0b5H2c6vVi +m2FZ33HQcoZ3wvFFqyGVnMzpOwKBgQDXxL95yoxUGKa8vMzcE3Cn01szh0dFq0sq +cFpM+HWLVr84CItuG9H6L0KaStEEIOiJsxOVpcXfFFhsJvOGhMA4DQTwH4WuXmXp +1PoVMDlV65PYqvhzwL4+QhvZO2bsrEunITXOmU7CI6kilnAN3LuP4HbqZgoX9lqP +I31VYzLupQKBgGEYck9w0s/xxxtR9ILv5XRnepLdoJzaHHR991aKFKjYU/KD7JDK +INfoAhGs23+HCQhCCtkx3wQVA0Ii/erM0II0ueluD5fODX3TV2ZibnoHW2sgrEsW +vFcs36BnvIIaQMptc+f2QgSV+Z/fGsKYadG6Q+39O7au/HB7SHayzWkjAoGBAMgt +Fzslp9TpXd9iBWjzfCOnGUiP65Z+GWkQ/SXFqD+SRir0+m43zzGdoNvGJ23+Hd6K +TdQbDJ0uoe4MoQeepzoZEgi4JeykVUZ/uVfo+nh06yArVf8FxTm7WVzLGGzgV/uA ++wtl/cRtEyAsk1649yW/KHPEIP8kJdYAJeoO8xSlAoGAERMrkFR7KGYZG1eFNRdV +mJMq+Ibxyw8ks/CbiI+n3yUyk1U8962ol2Q0T4qjBmb26L5rrhNQhneM4e8mo9FX +LlQapYkPvkdrqW0Bp72A/UNAvcGTmN7z5OCJGMUutx2hmEAlrYmpLKS8pM/p9zpK +tEOtzsP5GMDYVlEp1jYSjzQ= +-----END PRIVATE KEY----- diff --git a/tests/keys/rsa_key_4096-pub.pem b/tests/keys/rsa_key_4096-pub.pem new file mode 100644 index 00000000..78b1d31f --- /dev/null +++ b/tests/keys/rsa_key_4096-pub.pem @@ -0,0 +1,14 @@ +-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAybi/3guc70NLoGecAdyA +o0dJYLRXCYFdx7Iaq20REXb5SsyqSjGV/KEr8sdyFhr/PasKb1XbV7G8/gdtxU+S +l5dGI9h8mymIQ7VzbyEhwQqjzdyCKoz2levKPgjsTDN93IQiNfSOfc4qUux5viWQ +aPWt3bzpvOZXHcMUbzEz/jahKZPU/hMq/N3DroE6xFYB91fb74qHOC5RGMPjskUY +KOjCtj+v+sZqznuckf2x0a2r5X++gsqGZT8dHaX9smyrpBbV9Y/g1VZ+CO8XFdRi +HWth98FnlbEeHsrXbho9CE2GEACWvRRApMn1uSpghpENbkefpbYCIN7tq6X4lKvS +YQZARiLkD1ELHcmWCX+cpIlfxihrKpVnRR4iLJNl33VtOBIPaMvtifojkLxsi3dt +yJkcEFQI/kXbKW8Kdw5Fd+UOcsQf8PKk7k9LgFaH8GZRFhbwT0bLjjy0UIV5NDHc +SozydV2uQh+ON6BsygCSvME8V4lu9z74Rq3T7eP25pC+Q0mN+1n7QGQL4zKshkKN +h6Rx+QCP0hVGNosL6MoLzseluapTqnHrQEHTbG1Jm+WL/ces3thH7PySyWKJcUQ/ +iWBD0FFkya0KTvTfWvBg/WPdqBHLIQh6ie/7lOFyE56FbRYuRU0cF8+NykA/6HN2 +47pFtw1PSgQ68Dqtv9bZMFUCAwEAAQ== +-----END PUBLIC KEY----- diff --git a/tests/keys/rsa_key_4096.pem b/tests/keys/rsa_key_4096.pem new file mode 100644 index 00000000..12994932 --- /dev/null +++ b/tests/keys/rsa_key_4096.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +nfictCHvTZXlDWYlp5Uf8IDXrd4= +-----END PRIVATE KEY----- diff --git a/tests/keys/rsa_key_8192-pub.pem b/tests/keys/rsa_key_8192-pub.pem new file mode 100644 index 00000000..f9162df0 --- /dev/null +++ b/tests/keys/rsa_key_8192-pub.pem @@ -0,0 +1,25 @@ +-----BEGIN PUBLIC KEY----- +MIIEIjANBgkqhkiG9w0BAQEFAAOCBA8AMIIECgKCBAEAs0XMckvY06KW80N/Fz5r +BJUN3BlsUoViZYjpZy5UbhNAt96tdnoMMXJw6vd9+/DWwBEgpq7cRUsWq1qRkqSL +z8dxTQ8OGsxauFVTWRTX3ZMQ/YtZJDGa1+snkxhsLxnvpXx/KPgYU1WTqB46GV+i +twsZ69S0nq+cNMjIFiz6TJMNVNwI5yEdYUildxb0YmGu/ZBQMihcrYesAJSQezNu +10YvioSIQUASHjfVcFMMLtZRYvi7idIfejb7iW2rPlO7sF4ZNhTaSoszB03BvnNZ +jikORBnDz71AgMKqW/Itlq/uMuMb0XlPCiLZgS83oLfSvMQQspeOks3yv5w3Hv+s +EFXrSZyyt3SPxHuc3gggSqLcfzCV4PG/p2H7y5Q6i9VHPaA89ZQXlKWQC46sripP +OtkSAkyJU9NJ/WkYNnnXzQiKA3kfN2mLCOzpv62SGoUrEssycTuVLUKTzM76LaYQ +/lGbpbSrTGvZEmm3OJjjFr5r2JSPmzGw+5dOUpJ+i3r9+um+iUKKpOkTISYyCXY1 +7w9yf13W4Gms7l5XJU+pRIlBlb3H2QFN2u9wp7mEXYOJzGNGpA3oHFKUmn40jlz5 +09dNpGaG4SXzJG8Uku+mWrjWWsnh9VcZQhgLKVobJsmmSIkqcpJCityl21LAk7Do +UB0GeMCFOjzPhZ0ncD7RVBwq762A6rcowuuY8Lwbrp9auNgzzhHHr+c01aFvqZbU +T4oaxSFInyrocsyCqJYQEG3sDK6NMxNIPs9NIyC+IWPbqvxESOGJK6rLmh2WCaD0 +irCvboW5yxfxEaCuu3WWUFFEecR0KvHRQig/dFb2Jyo4fypzqdSRtAu0R7/qOn6p +LsEO9XseQh7uRzdagwb9CjdBB8nKB5zVQ79gswBDY2+gDl1UphywtT+ISr4kLUm3 +tvOFQZjj1SNaZdLTDQbxO9GWjwaijIQbAdMU0f9kE7FHIR3vPEMLfwNgpo6hEyuk +TBfBTT1cq8eHQt0WbDLDYFcuA1fFa9XGrZPmmGCoV8AFKQaUNRcIbfRWnykPs9oJ +Ki60gKbhoIStIq/DkV/B8OPTMRTbFranSJDaO2P6O0A5Y7Rezhjnqyb0oiJsKg74 ++/TcgL/tZB+YB3AG3gdElVnu9cWuR2FpY35L1WAk9zA46Z4dxQyL7nNpQDg5pX4Q +8sXK2x3Ov1VmYy1TmUxpv1eLTMxts0yXYDQFGcDS4VuDfZZNkIXMOMEo4y5fQw11 +OOcElpvyVMUkYl0LgJvIqbUCzCulkrruKKPPdHrtJvhImysgT46lbjKPGDT4D/6q +It4UqV9B969PNbqfqFORnmgaH3oF3xxbAQfcasfUxCPxChYpOPs25ldtAASAAqDO +RwIDAQAB +-----END PUBLIC KEY----- diff --git a/tests/keys/rsa_key_8192.pem b/tests/keys/rsa_key_8192.pem new file mode 100644 index 00000000..9e72c836 --- /dev/null +++ b/tests/keys/rsa_key_8192.pem @@ -0,0 +1,100 @@ +-----BEGIN PRIVATE KEY----- +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 +EtoiU3WKndSOrpIVOqOQTQU54puQoOE= +-----END PRIVATE KEY----- diff --git a/tests/keys/rsa_key_i37-pub.pem b/tests/keys/rsa_key_i37-pub.pem new file mode 100644 index 00000000..7b6d7284 --- /dev/null +++ b/tests/keys/rsa_key_i37-pub.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn/7RTQX1xlllgo+aqo5z +d/+wqaRZwFWp3JJIUXryJ4d153J1gEYdz4RkjFO0X8kpv8qb5hdzWhhZHSItD+07 +LaQXP4lSUuiK1lJAT/YW51D37nCkWm839gAqEGdsWsYQCvqJKSJr4pWCZTEx2MEf +ikmmnaXPR/VOVgZSj9kIoKo+kFwlw9LGVBHYAeR/W+l5DJQK6Yha8Igi36hnvZqq +HcQ4gkGUqEc//Tq2nyYNwvAN3ieZvtvL71rFW26FnuA4OwDER+TYSkAr8Z4wH2Ed +Gno1GZQAegIm+yWblleCaBVOnlk8VcDO9PecTiAFjjWAbQiaFKlkf/plD7KE4tAa +mQIDAQAB +-----END PUBLIC KEY----- diff --git a/tests/keys/rsa_key_invalid-pub.pem b/tests/keys/rsa_key_invalid-pub.pem new file mode 100644 index 00000000..dbd571b9 --- /dev/null +++ b/tests/keys/rsa_key_invalid-pub.pem @@ -0,0 +1,6 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwtpMAM4l1H995oqlqdMh +uqNuffp4+4aUCwuFE9B5s9MJrIAmCornholio7Mb1Xb8y9iGkWfhouZqNJbMFry+ +iBs+z2TtJF06vbHQZzajDsdux3XVfXv9v6dDIImyU24MsGNkpNt0GISaaiqv51NM +BwIDAQAB +-----END PUBLIC KEY----- diff --git a/tests/keys/rsa_key_invalid.pem b/tests/keys/rsa_key_invalid.pem new file mode 100644 index 00000000..f7edbe63 --- /dev/null +++ b/tests/keys/rsa_key_invalid.pem @@ -0,0 +1,17 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDC2kwAziXUf33m +iqWp0yG6o259+nj7hpQLC4UT0Hmz0wmvreDJ/yNbSgOvsxvVdvzL2IaRZ+Gi5mo0 +lswWvL6IGz7PZO0kXTq9sdBnNqMOx27HddV9e/2/p0MgibJTbgywY2Sk23QYhJpq +Kq/nU0xlBfSaI5ddZ2RC9ZNkVeGawUKYksTruhAVJqviHN8BoK6VowP5vcxyyOWH +TK9KruDqzCIhqwRTeo0spokBkTN/LCuhVivcHAzUiJVtB4qAiTI9L/zkzhjpKz9P +45aLU54rj011gG8U/6E1UShIAmCornholiofkhfS3Zs3kJVdyFQWZpQxiTaI92Fd +2wLvbS0HAgMBAAECggEAD8dTnkETSSjlzhRuI9loAtAXM3Zj86JLPLW7GgaoxEoT +n7lJ2bGicFMHB2ROnbOb9vnas82gtOtJsGaBslmoaCckp/C5T1eJWTEb+i+vdpPp +wZcmKZovyyRFSE4+NYlU17fEv6DRvuaGBpDcW7QgHJIl45F8QWEM+msee2KE+V4G +Fzslp9TpXd9iBWjzfCOnGUiP65Z+GWkQ/SXFqD+SRir0+m43zzGdoNvGJ23+Hd6K +TdQbDJ0uoe4MoQeepzoZEgi4JeykVUZ/uVfo+nh06yArVf8FxTm7WVzLGGzgV/uA ++wtl/cRtEyAsk1649yW/KHPEIP8kJdYAJeoO8xSlAoGAERMrkFR7KGYZG1eFNRdV +mJMq+Ibxyw8ks/CbiI+n3yUyk1U8962ol2Q0T4qjBmb26L5rrhNQhneM4e8mo9FX +LlQapYkPvkdrqW0Bp72A/UNAvcGTmN7z5OCJGMUutx2hmEAlrYmpLKS8pM/p9zpK +tEOtzsP5GMDYVlEp1jYSjzQ= +-----END PRIVATE KEY----- diff --git a/tests/misc/ruby-eddsa.rb b/tests/misc/ruby-eddsa.rb new file mode 100755 index 00000000..246b79a4 --- /dev/null +++ b/tests/misc/ruby-eddsa.rb @@ -0,0 +1,22 @@ +#!/usr/bin/env ruby + +# gem install jwt-eddsa +require "jwt/eddsa" + +# EDDSA Token generated by LibJWT +# Key generated by OpenSSL (keys/eddsa_key_ed25519-pub.pem) +# The pub key was converted to 32-bytes with some openssl and CLI magic + +token = "eyJhbGciOiJFRERTQSIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0NzU5ODA1NDUsImlzc" + + "yI6ImZpbGVzLm1hY2xhcmEtbGxjLmNvbSIsInJlZiI6IlhYWFgtWVlZWS1aWlpaLUF" + + "BQUEtQ0NDQyIsInN1YiI6InVzZXIwIn0.19ip2DFFjaZ_UFVCo0OtdwuzSmOYModle" + + "JVeFcAjb_4hrAAf0pZSf8O78pivbXLJenEIsaZ9REFOauBeDxbTBw" + +pub_key = "\x1d\x48\xfe\xd7\x89\x0d\xe8\xde\x22\xe6\xa3\x55\x91\xf1\x21\xc0" + + "\xa8\x82\x7f\xeb\x52\xae\xf4\x47\xb2\xd4\x1d\xb5\x5f\x1a\xe5\x03" + +verify_key = Ed25519::VerifyKey.new(pub_key) + +payload, header = JWT.decode(token, verify_key, true, algorithm: "EdDSA") +puts payload +puts header diff --git a/topics.html b/topics.html new file mode 100644 index 00000000..27464500 --- /dev/null +++ b/topics.html @@ -0,0 +1,93 @@ + + + + + + + +libjwt-2.1.1: Topics + + + + + + + + + + + + +
    +
    + + + + + + +
    +
    libjwt-2.1.1 +
    +
    +
    + + + + + + + +
    +
    + +
    +
    +
    + +
    +
    +
    Topics
    +
    +
    +
    Here is a list of all topics with brief descriptions:
    + + + + + + + + + +
     JWT Crypto OperationsFunctions used to set and get which crypto operations are used
     JWT Object CreationFunctions used to create and destroy JWT objects
     JWT Grant ManipulationThese functions allow you to add, remove and retrieve grants from a JWT object
     JWT Header ManipulationThese functions allow you to add, remove and retrieve headers from a JWT object
     JWT Output FunctionsFunctions that enable seeing the plain text or fully encoded version of a JWT object
     JWT Algorithm FunctionsSet and check algorithms and algorithm specific values
     JWT memory functionsThese functions allow you to get or set memory allocation functions
     JWT validation functionsThese functions allow you to define requirements for JWT validation
    +
    +
    +
    + + + + diff --git a/topics.js b/topics.js new file mode 100644 index 00000000..f45cc348 --- /dev/null +++ b/topics.js @@ -0,0 +1,11 @@ +var topics = +[ + [ "JWT Crypto Operations", "group__jwt__crypto.html", "group__jwt__crypto" ], + [ "JWT Object Creation", "group__jwt__new.html", "group__jwt__new" ], + [ "JWT Grant Manipulation", "group__jwt__grant.html", "group__jwt__grant" ], + [ "JWT Header Manipulation", "group__jwt__header.html", "group__jwt__header" ], + [ "JWT Output Functions", "group__jwt__encode.html", "group__jwt__encode" ], + [ "JWT Algorithm Functions", "group__jwt__alg.html", "group__jwt__alg" ], + [ "JWT memory functions", "group__jwt__memory.html", "group__jwt__memory" ], + [ "JWT validation functions", "group__jwt__validate.html", "group__jwt__validate" ] +]; \ No newline at end of file